Skip to content

Commit 1ed9bcd

Browse files
feat(cloudrun): allow adding new readiness probe to cloud run (#5387)
`cloudrunci` updates to support readiness probe additions in #5368 * feat(cloudrun): allow adding new readiness probe to cloud run * fix(cloudrun): add comment to Readiness field --------- Co-authored-by: Brian Dorsey <[email protected]>
1 parent c65aa22 commit 1ed9bcd

File tree

2 files changed

+107
-8
lines changed

2 files changed

+107
-8
lines changed

internal/cloudrunci/cloudrunci.go

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,28 @@ const (
4949
defaultRegistryName = "cloudrunci"
5050
)
5151

52+
// HTTPGetProbe describes a probe definition using HTTP Get.
53+
type HTTPGetProbe struct {
54+
Path string
55+
Port int
56+
}
57+
58+
// GRPCProbe describes a probe definition using gRPC.
59+
type GRPCProbe struct {
60+
Port int
61+
Service string
62+
}
63+
64+
// ReadinessProbe describes the readiness probe for a Cloud Run service.
65+
type ReadinessProbe struct {
66+
TimeoutSeconds int
67+
PeriodSeconds int
68+
SuccessThreshold int
69+
FailureThreshold int
70+
HttpGet *HTTPGetProbe
71+
GRPC *GRPCProbe
72+
}
73+
5274
// Service describes a Cloud Run service
5375
type Service struct {
5476
// Name is an ID, used for logging and to generate a unique version to this run.
@@ -85,6 +107,9 @@ type Service struct {
85107

86108
// Location to deploy the Service, and related artifacts
87109
Location string
110+
111+
// ReadinessProbe description
112+
Readiness *ReadinessProbe
88113
}
89114

90115
// runID is an identifier that changes between runs.
@@ -272,7 +297,7 @@ func (s *Service) validate() error {
272297

273298
// revision returns the revision that the service will be deployed to.
274299
// NOTE: Until traffic splitting is available, this will be used as the service name.
275-
func (s *Service) version() string {
300+
func (s *Service) Version() string {
276301
return s.Name + "-" + runID
277302
}
278303

@@ -293,7 +318,7 @@ func (s *Service) Deploy() error {
293318
}
294319

295320
if _, err := gcloud(s.operationLabel(labelOperationDeploy), s.deployCmd()); err != nil {
296-
return fmt.Errorf("gcloud: %s: %q", s.version(), err)
321+
return fmt.Errorf("gcloud: %s: %q", s.Version(), err)
297322
}
298323

299324
s.deployed = true
@@ -339,15 +364,15 @@ func (s *Service) Clean() error {
339364
}
340365

341366
if _, err := gcloud(s.operationLabel(labelOperationDeleteService), s.deleteServiceCmd()); err != nil {
342-
return fmt.Errorf("gcloud: %v: %q", s.version(), err)
367+
return fmt.Errorf("gcloud: %v: %q", s.Version(), err)
343368
}
344369
s.deployed = false
345370

346371
// If s.built is false no image was created or is not managed by cloudrun-ci.
347372
if s.built {
348373
_, err := gcloud(s.operationLabel("delete container image"), s.deleteImageCmd())
349374
if err != nil {
350-
return fmt.Errorf("gcloud: %v: %q", s.version(), err)
375+
return fmt.Errorf("gcloud: %v: %q", s.Version(), err)
351376
}
352377
s.built = false
353378
}
@@ -365,7 +390,7 @@ func (s *Service) deployCmd() *exec.Cmd {
365390
"alpha", // TODO until --use-http2 goes GA
366391
"run",
367392
"deploy",
368-
s.version(),
393+
s.Version(),
369394
"--project",
370395
s.ProjectID,
371396
"--image",
@@ -384,6 +409,40 @@ func (s *Service) deployCmd() *exec.Cmd {
384409
args = append(args, "--use-http2")
385410
}
386411

412+
if s.Readiness != nil {
413+
var readinessProbeParts []string
414+
if s.Readiness.TimeoutSeconds > 0 {
415+
readinessProbeParts = append(readinessProbeParts, fmt.Sprintf("timeoutSeconds=%d", s.Readiness.TimeoutSeconds))
416+
}
417+
if s.Readiness.PeriodSeconds > 0 {
418+
readinessProbeParts = append(readinessProbeParts, fmt.Sprintf("periodSeconds=%d", s.Readiness.PeriodSeconds))
419+
}
420+
if s.Readiness.SuccessThreshold > 0 {
421+
readinessProbeParts = append(readinessProbeParts, fmt.Sprintf("successThreshold=%d", s.Readiness.SuccessThreshold))
422+
}
423+
if s.Readiness.FailureThreshold > 0 {
424+
readinessProbeParts = append(readinessProbeParts, fmt.Sprintf("failureThreshold=%d", s.Readiness.FailureThreshold))
425+
}
426+
if s.Readiness.HttpGet != nil {
427+
if s.Readiness.HttpGet.Path != "" {
428+
readinessProbeParts = append(readinessProbeParts, fmt.Sprintf("httpGet.path=%s", s.Readiness.HttpGet.Path))
429+
}
430+
if s.Readiness.HttpGet.Port > 0 {
431+
readinessProbeParts = append(readinessProbeParts, fmt.Sprintf("httpGet.port=%d", s.Readiness.HttpGet.Port))
432+
}
433+
} else if s.Readiness.GRPC != nil {
434+
if s.Readiness.GRPC.Service != "" {
435+
readinessProbeParts = append(readinessProbeParts, fmt.Sprintf("grpc.service=%s", s.Readiness.GRPC.Service))
436+
}
437+
if s.Readiness.GRPC.Port > 0 {
438+
readinessProbeParts = append(readinessProbeParts, fmt.Sprintf("grpc.port=%d", s.Readiness.GRPC.Port))
439+
}
440+
}
441+
if len(readinessProbeParts) > 0 {
442+
args = append(args, "--readiness-probe="+strings.Join(readinessProbeParts, ","))
443+
}
444+
}
445+
387446
// NOTE: if the "beta" component is not available, and this is run in parallel,
388447
// gcloud will attempt to install those components multiple
389448
// times and will eventually fail on IO.
@@ -439,7 +498,7 @@ func (s *Service) deleteServiceCmd() *exec.Cmd {
439498
"run",
440499
"services",
441500
"delete",
442-
s.version(),
501+
s.Version(),
443502
"--project",
444503
s.ProjectID,
445504
}, s.Platform.CommandFlags()...)
@@ -458,7 +517,7 @@ func (s *Service) urlCmd() *exec.Cmd {
458517
"run",
459518
"services",
460519
"describe",
461-
s.version(),
520+
s.Version(),
462521
"--project",
463522
s.ProjectID,
464523
"--format",
@@ -481,7 +540,7 @@ func (s *Service) LogEntries(filter string, find string, maxAttempts int) (bool,
481540
}
482541
defer client.Close()
483542

484-
preparedFilter := fmt.Sprintf(`resource.type="cloud_run_revision" resource.labels.service_name="%s" %s`, s.version(), filter)
543+
preparedFilter := fmt.Sprintf(`resource.type="cloud_run_revision" resource.labels.service_name="%s" %s`, s.Version(), filter)
485544
log.Printf("Using log filter: %s\n", preparedFilter)
486545

487546
log.Println("Waiting for logs...")

internal/cloudrunci/cloudrunci_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,46 @@ func TestDeployArgs(t *testing.T) {
111111
}
112112
}
113113

114+
func TestDeployArgsReadinessProbe(t *testing.T) {
115+
tests := []struct {
116+
name string
117+
readiness *ReadinessProbe
118+
wantArgs []string // Expected arguments in the gcloud command
119+
}{
120+
{
121+
name: "HTTPGet probe",
122+
readiness: &ReadinessProbe{
123+
TimeoutSeconds: 10, PeriodSeconds: 5, SuccessThreshold: 1, FailureThreshold: 3,
124+
HttpGet: &HTTPGetProbe{Path: "/healthz", Port: 8080},
125+
},
126+
wantArgs: []string{"--readiness-probe=timeoutSeconds=10,periodSeconds=5,successThreshold=1,failureThreshold=3,httpGet.path=/healthz,httpGet.port=8080"},
127+
},
128+
{
129+
name: "GRPC probe",
130+
readiness: &ReadinessProbe{
131+
TimeoutSeconds: 10, PeriodSeconds: 5, SuccessThreshold: 1, FailureThreshold: 3,
132+
GRPC: &GRPCProbe{Port: 50051, Service: "myservice"},
133+
},
134+
wantArgs: []string{"--readiness-probe=timeoutSeconds=10,periodSeconds=5,successThreshold=1,failureThreshold=3,grpc.service=myservice,grpc.port=50051"},
135+
},
136+
}
137+
138+
for _, test := range tests {
139+
t.Run(test.name, func(t *testing.T) {
140+
service := NewService("my-service", "my-project")
141+
service.Image = "gcr.io/my-project/my-service"
142+
service.Readiness = test.readiness
143+
144+
cmd := service.deployCmd()
145+
for _, wantArg := range test.wantArgs {
146+
if !contains(cmd.Args, wantArg) {
147+
t.Errorf("deployCmd() args missing expected readiness probe arg: %s, got: %v", wantArg, cmd.Args)
148+
}
149+
}
150+
})
151+
}
152+
}
153+
114154
// contains searches for a string value in a string slice.
115155
func contains(haystack []string, needle string) bool {
116156
for _, i := range haystack {

0 commit comments

Comments
 (0)