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

package example import ( // Importing necessary packages for the code "context" // For managing request contexts "encoding/json" // For JSON encoding and decoding "fmt" // For formatted I/O "log" // For logging messages "strings" // For string manipulation "time" // For time management "cloud.google.com/go/deploy/apiv1/deploypb" // Google Cloud Deploy library for managing deployment processes "cloud.google.com/go/pubsub" // Pub/Sub for messaging in Google Cloud "github.com/GoogleCloudPlatform/functions-framework-go/functions" // Framework for creating Cloud Run Functions "github.com/cloudevents/sdk-go/v2/event" // CloudEvents SDK for event handling "github.com/codingconcepts/env" // For managing environment variables in Go "google.golang.org/api/option" ) // Struct for storing configuration data pulled from environment variables type config struct { // Environment variables for project ID, location, and topic ID, all required ProjectId string `env:"PROJECTID" required:"true"` Location string `env:"LOCATION" required:"true"` SendTopicID string `env:"SENDTOPICID" required:"true"` } // Struct representing a Pub/Sub message payload type PubsubMessage struct { // Data contains the core message payload Data []byte `json:"data"` // Attributes store optional metadata as an ApprovalsData object Attributes ApprovalsData `json:"attributes"` // ID generated by server for tracking MessageID string `json:"messageId"` // PublishTime indicates when the message was published PublishTime time.Time `json:"publishTime"` // OrderingKey helps determine the order of message processing OrderingKey string `json:"orderingKey"` } // Struct representing a complete Pub/Sub message type Message struct { Message PubsubMessage `json:"message"` } // Struct defining the expected structure of message attributes type ApprovalsData struct { Action string `json:"Action"` // Approval action (e.g., "Required") Rollout string `json:"Rollout"` // Rollout ID ReleaseId string `json:"ReleaseId"` // Release ID RolloutId string `json:"RolloutId"` // Rollout ID TargetId string `json:"TargetId"` // Target ID for deployment Location string `json:"Location"` // Location for deployment ProjectNumber string `json:"ProjectNumber"` // Project number ManualApproval string `json:"manualApproval"` // Manual approval flag ("true"/"false") } // Struct for creating command messages for Pub/Sub type CommandMessage struct { Commmand string `json:"command"` // Command type, e.g., "ApproveRollout" ApproveRollout deploypb.ApproveRolloutRequest `json:"approveRolloutRequest"` // Request details for approving rollout } // Global config variable for storing loaded environment variables var c config // Initialization function to register the Cloud Event and load environment variables func init() { functions.CloudEvent("cloudDeployApprovals", cloudDeployApprovals) // Registers the function as a CloudEvent handler // Load environment variables into the config struct if err := env.Set(&c); err != nil { _ = fmt.Errorf("error getting env: %s", err) // Logs error if environment variables are missing } } // Cloud Run Function to process deployment approval requests func cloudDeployApprovals(ctx context.Context, e event.Event) error { log.Printf("Deploy Approvals function invoked") // Logs function invocation var msg Message // Struct to hold the decoded event data err := json.Unmarshal(e.Data(), &msg) // Decodes the JSON event data into msg if err != nil { // If data is malformed, log the error and continue to avoid reprocessing _ = fmt.Errorf("errored unmarshalling data: %v", err) return nil } // Wait for 3 seconds as a demo delay before proceeding log.Printf("Waiting 3 seconds to approve for demo") time.Sleep(3 * time.Second) // Extract the message attributes for easier access var a = msg.Message.Attributes log.Printf("A is: %v", a) // Logs the extracted attributes // Check conditions: Action required, a valid Rollout ID, and manual approval if a.Action == "Required" && a.Rollout != "" && strings.ToLower(a.ManualApproval) == "true" { log.Printf("Creating Rollout and sending to pubsub") // Logs the action // Constructs a command message to approve the rollout var command = CommandMessage{ Commmand: "ApproveRollout", ApproveRollout: deploypb.ApproveRolloutRequest{ Name: a.Rollout, // Rollout name as per received message Approved: true, // Approves the rollout }, } // Sends the command to Pub/Sub err = sendCommandPubSub(ctx, &command) if err != nil { _ = fmt.Errorf("failed to send pubsub command: %v", err) // Logs error if Pub/Sub message fails to send return nil // Returns nil to acknowledge message even if there's an error } log.Printf("Deployment triggered successfully") // Logs success } return nil // Acknowledges successful message processing } // Function to publish messages to Pub/Sub func sendCommandPubSub(ctx context.Context, m *CommandMessage) error { // Initializes Pub/Sub client using the project ID from config 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) // Returns error if client creation fails } defer client.Close() // Ensures client is closed after message is sent // Defines the target topic using config data t := client.Topic(c.SendTopicID) // Encodes the CommandMessage as JSON jsonData, err := json.Marshal(m) if err != nil { return fmt.Errorf("json.Marshal: %v", err) // Returns error if JSON encoding fails } log.Printf("Sending message to PubSub") // Logs before publishing // Publishes the message to the topic result := t.Publish(ctx, &pubsub.Message{ Data: jsonData, // Publishes JSON data as message }) // Waits for the publish result, logging ID or error if present id, err := result.Get(ctx) log.Printf("ID: %s, err: %v", id, err) if err != nil { fmt.Printf("Get: %v", err) // Logs any error on publish return nil } log.Printf("Published a message; msg ID: %v\n", id) // Logs message ID if successful return nil }