reference-architectures/cloud_deploy_flow/CloudFunctions/cloudDeployOperations/main.go (107 lines of code) (raw):

package example import ( "context" "encoding/json" "fmt" "log" "time" "cloud.google.com/go/deploy/apiv1/deploypb" "cloud.google.com/go/pubsub" "github.com/GoogleCloudPlatform/functions-framework-go/functions" "github.com/cloudevents/sdk-go/v2/event" "github.com/codingconcepts/env" "google.golang.org/api/option" ) // Config struct for environment variables; contains details necessary for deployment. type config struct { // ProjectId and Location might be auto-detectable in some cases but are set here as required environment variables. ProjectId string `env:"PROJECTID" required:"true"` Location string `env:"LOCATION" required:"true"` SendTopicID string `env:"SENDTOPICID" required:"true"` } // PubsubMessage represents the structure for a Pub/Sub message. type PubsubMessage struct { Data []byte `json:"data"` // Payload of the message. Attributes OperationsData `json:"attributes"` // Metadata attributes for the message. MessageID string `json:"messageId"` // Server-generated message ID. PublishTime time.Time `json:"publishTime"` // Timestamp for when the message was published. OrderingKey string `json:"orderingKey"` // Ordering key for message ordering. } // Message wraps the PubsubMessage structure in the JSON object expected by the Cloud Run Function. type Message struct { Message PubsubMessage `json:"message"` } // OperationsData holds metadata about the deployment operation, such as action and resource type. type OperationsData struct { Action string `json:"Action"` Resource string `json:"Resource"` ResourceType string `json:"ResourceType"` Location string `json:"Location"` DeliveryPipelineId string `json:"DeliveryPipelineId"` ProjectNumber string `json:"ProjectNumber"` ReleaseId string `json:"ReleaseId"` RolloutId string `json:"RolloutId"` } // CommandMessage struct defines commands that are sent to Pub/Sub, including deployment actions. type CommandMessage struct { Commmand string `json:"command"` CreateRollout deploypb.CreateRolloutRequest `json:"createRolloutRequest"` } var c config // init initializes the function and loads environment variables into the config struct. func init() { functions.CloudEvent("cloudDeployOperations", cloudDeployOperations) // Load environment variables into the config struct using the env package. if err := env.Set(&c); err != nil { _ = fmt.Errorf("error getting env: %s", err) } } // cloudDeployOperations is triggered by a CloudEvent to process deployment events and initiate rollouts. func cloudDeployOperations(ctx context.Context, e event.Event) error { log.Printf("Deploy Operations function invoked") // Parse event data into Message struct var msg Message err := json.Unmarshal(e.Data(), &msg) if err != nil { // Acknowledge the message by returning nil, even if it’s bad, to prevent reprocessing. _ = fmt.Errorf("errored unmarshalling data: %v", err) return nil } // Extract attributes from the message for validation and processing. var a = msg.Message.Attributes // Check if the message indicates a successful release event for further processing. if a.ResourceType == "Release" && a.Action == "Succeed" { log.Printf("Creating Rollout and sending to pubsub") // Define a rollout command message with details from OperationsData. var command = CommandMessage{ Commmand: "CreateRollout", CreateRollout: deploypb.CreateRolloutRequest{ Parent: a.Resource, // The deployment resource to associate with this rollout RolloutId: a.ReleaseId, // The ID of the release Rollout: &deploypb.Rollout{ // TODO: TargetId should ideally come from the Pub/Sub message rather than hardcoded. TargetId: "random-date-service", }, }, } // Send the command to Pub/Sub and log any errors that occur. err = sendCommandPubSub(ctx, &command) if err != nil { _ = fmt.Errorf("failed to send pubsub command: %v", err) // Acknowledge the message even if there's a failure to prevent repeated processing. return nil } log.Printf("Deployment triggered successfully") } return nil } // sendCommandPubSub publishes a CommandMessage to a specified Pub/Sub topic to trigger further actions. func sendCommandPubSub(ctx context.Context, m *CommandMessage) error { // Create a new Pub/Sub client to publish messages. client, err := pubsub.NewClient(ctx, c.ProjectId, option.WithUserAgent("cloud-solutions/platform-engineering-cloud-deploy-pipeline-code-v1"), ) if err != nil { return fmt.Errorf("pubsub.NewClient: %v", err) } defer client.Close() // Ensure client is closed when done // Define the topic for message publication based on environment configuration. t := client.Topic(c.SendTopicID) // Marshal the CommandMessage into JSON format for the Pub/Sub message data payload. jsonData, err := json.Marshal(m) if err != nil { return fmt.Errorf("json.Marshal: %v", err) } log.Printf("Sending message to PubSub") // Publish the JSON data as a Pub/Sub message and wait for the message ID. result := t.Publish(ctx, &pubsub.Message{ Data: jsonData, // Serialized JSON command message }) // Block until the result returns the server-generated ID for the published message. id, err := result.Get(ctx) log.Printf("ID: %s, err: %v", id, err) if err != nil { fmt.Printf("Get: %v", err) return nil } log.Printf("Published a message; msg ID: %v\n", id) return nil }