1 // Copyright 2023 The Sigstore Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package pubsub 16 17 import ( 18 "context" 19 "fmt" 20 "strings" 21 22 "github.com/sigstore/rekor/pkg/events" 23 24 "golang.org/x/exp/maps" 25 "golang.org/x/exp/slices" 26 ) 27 28 // Publisher provides methods for publishing events to a Pub/Sub topic. 29 type Publisher interface { 30 // Publish publishes a CloudEvent to the configured Pub/Sub topic serialized 31 // using the specified encoding type. 32 Publish(ctx context.Context, event *events.Event, encoding events.EventContentType) error 33 // Close safely closes any active connections. 34 Close() error 35 } 36 37 // ProviderNotFoundError indicates that no matching PubSub provider was found. 38 type ProviderNotFoundError struct { 39 ref string 40 } 41 42 func (e *ProviderNotFoundError) Error() string { 43 return fmt.Sprintf("no pubsub provider found for key reference: %s", e.ref) 44 } 45 46 // ProviderInit is a function that initializes provider-specific Publisher. 47 type ProviderInit func(ctx context.Context, topicResourceID string) (Publisher, error) 48 49 // AddProvider adds the provider implementation into the local cache 50 func AddProvider(uri string, init ProviderInit) { 51 providersMap[uri] = init 52 } 53 54 var providersMap = map[string]ProviderInit{} 55 56 // Get returns a Publisher for the given resource string and hash function. 57 // If no matching provider is found, Get returns a ProviderNotFoundError. It 58 // also returns an error if initializing the Publisher fails. If no resource 59 // is supplied, it returns a nil Publisher and no error. 60 func Get(ctx context.Context, topicResourceID string) (Publisher, error) { 61 for ref, pi := range providersMap { 62 if strings.HasPrefix(topicResourceID, ref) { 63 return pi(ctx, topicResourceID) 64 } 65 } 66 return nil, &ProviderNotFoundError{ref: topicResourceID} 67 } 68 69 // SupportedProviders returns list of initialized providers 70 func SupportedProviders() []string { 71 names := maps.Keys(providersMap) 72 slices.Sort(names) 73 return names 74 } 75