-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
148 lines (131 loc) · 5.46 KB
/
Copy pathmain.go
File metadata and controls
148 lines (131 loc) · 5.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// cloudrun-mvp is a minimal CLI that demonstrates the core CLOUDRUN_SYNC deployment flow:
// load service.yaml → compute revision name → mutate manifest → call GCP Cloud Run API → poll readiness.
//
// This is the same logic that will live inside ExecuteStage() for the CLOUDRUN_SYNC stage
// in the PipeCD v1 Cloud Run plugin (pkg/app/pipedv1/plugin/cloudrun/deployment/).
//
// Usage:
//
// go run . --project=<gcp-project> --region=<region> [--service-yaml=service.yaml] \
// [--credentials-file=key.json] [--commit-hash=<hash>]
//
// Authentication:
//
// If --credentials-file is omitted, Application Default Credentials are used.
// Run `gcloud auth application-default login` to set up ADC locally.
package main
import (
"context"
"flag"
"fmt"
"log"
"math/rand"
"os"
"time"
)
// PipeCD tracking labels — same constants used in v0 platformprovider/cloudrun/cloudrun.go.
const (
labelManagedBy = "pipecd-dev-managed-by"
labelCommitHash = "pipecd-dev-commit-hash"
labelRevisionName = "pipecd-dev-revision-name"
managedByPiped = "piped"
)
func main() {
var (
project = flag.String("project", "", "GCP project ID (required)")
region = flag.String("region", "us-central1", "Cloud Run region")
serviceYAML = flag.String("service-yaml", "service.yaml", "Path to service.yaml manifest")
credentialsFile = flag.String("credentials-file", "", "Path to GCP service account JSON key (uses ADC if omitted)")
commitHash = flag.String("commit-hash", "", "Commit hash for deterministic revision naming (uses random 7-char hash if omitted)")
)
flag.Parse()
if *project == "" {
fmt.Fprintln(os.Stderr, "error: --project is required")
flag.Usage()
os.Exit(1)
}
if *commitHash == "" {
*commitHash = randomHash()
}
log.SetFlags(log.Ltime)
logf := func(format string, args ...interface{}) {
log.Printf(format, args...)
}
ctx := context.Background()
// ── Step 1: Load the service manifest ──────────────────────────────────────
logf("[CLOUDRUN_SYNC] Loading service manifest: %s", *serviceYAML)
sm, err := LoadServiceManifest(*serviceYAML)
if err != nil {
logf("[CLOUDRUN_SYNC] ERROR: %v", err)
os.Exit(1)
}
logf("[CLOUDRUN_SYNC] Loaded manifest for service: %q", sm.Name)
// ── Step 2: Compute deterministic revision name ─────────────────────────────
// Format: "<service>-<tag>-<short-commit>" e.g. "hello-cloudrun-latest-abc1234"
revisionName, err := DecideRevisionName(sm, *commitHash)
if err != nil {
logf("[CLOUDRUN_SYNC] ERROR computing revision name: %v", err)
os.Exit(1)
}
logf("[CLOUDRUN_SYNC] Computed revision name: %s", revisionName)
// ── Step 3: Mutate the manifest ─────────────────────────────────────────────
logf("[CLOUDRUN_SYNC] Setting revision name in manifest")
if err := sm.SetRevision(revisionName); err != nil {
logf("[CLOUDRUN_SYNC] ERROR: %v", err)
os.Exit(1)
}
logf("[CLOUDRUN_SYNC] Setting traffic: [%s → 100%%]", revisionName)
if err := sm.UpdateTraffic([]RevisionTraffic{
{RevisionName: revisionName, Percent: 100},
}); err != nil {
logf("[CLOUDRUN_SYNC] ERROR: %v", err)
os.Exit(1)
}
labels := map[string]string{
labelManagedBy: managedByPiped,
labelCommitHash: *commitHash,
labelRevisionName: revisionName,
}
sm.AddLabels(labels)
if err := sm.AddRevisionLabels(labels); err != nil {
logf("[CLOUDRUN_SYNC] ERROR adding revision labels: %v", err)
os.Exit(1)
}
logf("[CLOUDRUN_SYNC] PipeCD tracking labels applied")
// ── Step 4: Create the GCP client ──────────────────────────────────────────
logf("[CLOUDRUN_SYNC] Initializing Cloud Run client (project=%s, region=%s)", *project, *region)
client, err := NewClient(ctx, *project, *region, *credentialsFile)
if err != nil {
logf("[CLOUDRUN_SYNC] ERROR: %v", err)
os.Exit(1)
}
// ── Step 5: Apply the manifest (Create or Update) ──────────────────────────
logf("[CLOUDRUN_SYNC] Applying service manifest to Cloud Run API...")
created, err := client.CreateOrUpdate(ctx, sm)
if err != nil {
logf("[CLOUDRUN_SYNC] ERROR applying manifest: %v", err)
os.Exit(1)
}
if created {
logf("[CLOUDRUN_SYNC] Service %q created (first deployment)", sm.Name)
} else {
logf("[CLOUDRUN_SYNC] Service %q updated", sm.Name)
}
// ── Step 6: Poll until revision is ready ───────────────────────────────────
logf("[CLOUDRUN_SYNC] Polling revision %s for readiness (timeout: 2m, interval: 10s)...", revisionName)
if err := client.WaitRevisionReady(ctx, revisionName, logf); err != nil {
logf("[CLOUDRUN_SYNC] ERROR: %v", err)
os.Exit(1)
}
// ── Done ───────────────────────────────────────────────────────────────────
logf("[CLOUDRUN_SYNC] SUCCESS — service %q is running revision %s with 100%% traffic", sm.Name, revisionName)
}
func randomHash() string {
const chars = "abcdef0123456789"
r := rand.New(rand.NewSource(time.Now().UnixNano()))
b := make([]byte, 7)
for i := range b {
b[i] = chars[r.Intn(len(chars))]
}
return string(b)
}