Skip to content

Instantly share code, notes, and snippets.

@DazWilkin
Created August 13, 2025 20:59
Show Gist options
  • Save DazWilkin/76add445b6c883f1a0ff2dcca81edcca to your computer and use it in GitHub Desktop.
Save DazWilkin/76add445b6c883f1a0ff2dcca81edcca to your computer and use it in GitHub Desktop.
Stackoverflow: 79734079
package main
import (
"fmt"
"log/slog"
"math/rand/v2"
"os"
"cloud.google.com/go/run/apiv2/runpb"
)
type config struct {
project string
region string
service string
revisions map[string]string
}
func new() config {
return config{
project: os.Getenv("PROJECT"),
region: os.Getenv("REGION"),
service: os.Getenv("SERVICE"),
revisions: map[string]string{
"blue": os.Getenv("BLUE"),
"green": os.Getenv("GREEN"),
"purple": os.Getenv("PURPLE"),
},
}
}
func (c config) name() string {
name := fmt.Sprintf("projects/%s/locations/%s/services/%s",
c.project,
c.region,
c.service,
)
return name
}
// traffic is a method that returns a Cloud RUn Service's TrafficTarget
// It uses a random number, initially 100%, that reduces for each target
// The last target must always consume the remaining percentage
// So that the total of all the revisions is 100%
func (c config) traffic() []*runpb.TrafficTarget {
// Create TrafficTargets for each config revision
num := len(c.revisions)
targets := make([]*runpb.TrafficTarget, num)
// Generate random traffic percent to avoid collisions
// Start with 1..100
var remaining int32 = 100
var percent int32
// Iterator
i := 0
for tag, revision := range c.revisions {
// The last branch must consume the remaining percent so that the branches total 100%
if i == num-1 {
percent = remaining
} else {
percent = rand.N(int32(remaining + 1))
}
slog.Info("Generating traffic",
"tag", tag,
"revision", revision,
"percent", percent,
)
targets[i] = &runpb.TrafficTarget{
Type: runpb.TrafficTargetAllocationType_TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION,
Revision: revision,
Percent: percent,
Tag: tag,
}
// Increment the iterator
i = i + 1
remaining = remaining - percent
}
return targets
}
module github.com/DazWilkin/stackoverflow/79734079
go 1.25.0
require (
cloud.google.com/go/run v1.12.0
google.golang.org/protobuf v1.36.7
)
require (
cloud.google.com/go v0.120.0 // indirect
cloud.google.com/go/auth v0.16.3 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.7.0 // indirect
cloud.google.com/go/iam v1.5.2 // indirect
cloud.google.com/go/longrunning v0.6.7 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
golang.org/x/time v0.12.0 // indirect
google.golang.org/api v0.246.0 // indirect
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b // indirect
google.golang.org/grpc v1.74.2 // indirect
)
package main
import (
"context"
"log/slog"
"os"
run "cloud.google.com/go/run/apiv2"
"cloud.google.com/go/run/apiv2/runpb"
"google.golang.org/protobuf/types/known/fieldmaskpb"
)
func main() {
ctx := context.Background()
client, err := run.NewServicesClient(ctx)
if err != nil {
panic(err)
}
defer func() {
if err := client.Close(); err != nil {
slog.Error("Failed to close client", "err", err.Error())
os.Exit(1)
}
}()
c := new()
// Get the Service
var resp *runpb.Service
{
rqst := &runpb.GetServiceRequest{
Name: c.name(),
}
var err error
resp, err = client.GetService(ctx, rqst)
if err != nil {
panic(err)
}
slog.Info("Response", "resp", resp)
}
// Update the Service
{
// Update the Service's traffic
resp.Traffic = c.traffic()
rqst := &runpb.UpdateServiceRequest{
UpdateMask: &fieldmaskpb.FieldMask{
Paths: []string{
"traffic",
},
},
Service: resp,
}
resp, err := client.UpdateService(ctx, rqst)
if err != nil {
panic(err)
}
slog.Info("Response", "resp", resp)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment