Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Enhance Cloud Run deploy command with advanced configuration options #40

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: Add access and traffic configuration for Cloud Run deployments
- Introduce new flags for authentication and ingress settings
- Add support for controlling service access and traffic routing
- Update deployment options to configure unauthenticated access
- Implement ingress traffic policy configuration (all, internal, internal-and-cloud-load-balancing)
- Enhance service definition with new metadata annotations for access control
  • Loading branch information
dijarvrella committed Feb 27, 2025
commit a6ef041863bdc3aa355813c6779e9ee7d41e56ac
14 changes: 13 additions & 1 deletion cmd/cloud-run/deploy.go
Original file line number Diff line number Diff line change
@@ -52,6 +52,11 @@ Secrets:
- --update-secrets KEY=VALUE Update or add new secrets
- --clear-secrets Remove all secrets

Access and Traffic Configuration:
- --allow-unauthenticated Allow unauthenticated access to the service
- --no-allow-unauthenticated Require authentication for access to the service
- --ingress TYPE Set the ingress traffic settings (all, internal, internal-and-cloud-load-balancing)

Examples:
# Deploy from container image
cloud-run deploy --project-id=my-project --region=us-central1 --service=myapp --image=gcr.io/myproject/myapp:v1
@@ -66,7 +71,10 @@ Examples:
cloud-run deploy --service=myapp --set-secrets=/secrets/api/key=mysecret:latest

# Set a secret as an environment variable
cloud-run deploy --service=myapp --set-secrets=API_KEY=mysecret:1`,
cloud-run deploy --service=myapp --set-secrets=API_KEY=mysecret:1

# Deploy internal service requiring authentication
cloud-run deploy --service=myapp --image=gcr.io/myproject/myapp:v1 --no-allow-unauthenticated --ingress internal`,
RunE: deployService,
}
deployCmd.Flags().StringVar(&opts.Image, "image", "", "The container image to deploy (e.g., gcr.io/project/image:tag)")
@@ -84,6 +92,10 @@ Examples:
deployCmd.Flags().StringToStringVar(&opts.UpdateSecrets, "update-secrets", nil, "List of key-value pairs to update or add as secrets")
deployCmd.Flags().BoolVar(&opts.ClearSecrets, "clear-secrets", false, "Remove all secrets")

// Add access and traffic configuration flags
deployCmd.Flags().BoolVar(&opts.AllowUnauthenticated, "allow-unauthenticated", true, "Allow unauthenticated access to the service")
deployCmd.Flags().StringVar(&opts.Ingress, "ingress", "all", "Set the ingress traffic settings (all, internal, internal-and-cloud-load-balancing)")

// Flag validations
// Only one of these env var flags can be used at a time, but all are optional
if deployCmd.Flags().Lookup("set-env-vars").Changed ||
4 changes: 4 additions & 0 deletions cmd/cloud-run/pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -31,4 +31,8 @@ type DeployOptions struct {
RemoveSecrets []string // Secrets to remove
UpdateSecrets map[string]string // Secrets to update
ClearSecrets bool // Whether to clear all secrets

// Access and traffic configuration
AllowUnauthenticated bool // Whether to allow unauthenticated access (if false, --no-allow-unauthenticated)
Ingress string // Ingress setting: all, internal, or internal-and-cloud-load-balancing
}
108 changes: 87 additions & 21 deletions cmd/cloud-run/pkg/deploy/deploy.go
Original file line number Diff line number Diff line change
@@ -274,18 +274,21 @@ func updateWithOptions(service *run.Service, opts config.DeployOptions) {
if secretName != "" {
log.Printf("Adding secret env var %s with secret %s and version %s", k, secretName, version)

// Create a properly initialized SecretKeySelector with all required fields
secretKeyRef := &run.SecretKeySelector{
Key: version,
Name: secretName,
LocalObjectReference: &run.LocalObjectReference{
Name: secretName,
},
}

// Use direct struct initialization to avoid null JSON fields
envVar := &run.EnvVar{
Name: k,
// Ensure Value is explicitly empty string, not null
Name: k,
Value: "",
ValueFrom: &run.EnvVarSource{
SecretKeyRef: &run.SecretKeySelector{
Key: version,
LocalObjectReference: &run.LocalObjectReference{
Name: secretName,
},
},
SecretKeyRef: secretKeyRef,
},
}

@@ -431,14 +434,16 @@ func updateWithOptions(service *run.Service, opts config.DeployOptions) {
env.Value = ""
env.ValueFrom = &run.EnvVarSource{
SecretKeyRef: &run.SecretKeySelector{
Key: version,
Key: version,
Name: secretName,
LocalObjectReference: &run.LocalObjectReference{
Name: secretName,
},
},
}
} else if env.ValueFrom.SecretKeyRef != nil {
env.ValueFrom.SecretKeyRef.Key = version
env.ValueFrom.SecretKeyRef.Name = secretName
env.ValueFrom.SecretKeyRef.LocalObjectReference.Name = secretName
}
found = true
@@ -455,7 +460,8 @@ func updateWithOptions(service *run.Service, opts config.DeployOptions) {
Name: k,
ValueFrom: &run.EnvVarSource{
SecretKeyRef: &run.SecretKeySelector{
Key: version,
Key: version,
Name: secretName,
LocalObjectReference: &run.LocalObjectReference{
Name: secretName,
},
@@ -468,6 +474,38 @@ func updateWithOptions(service *run.Service, opts config.DeployOptions) {
}
}
}

// Set ingress traffic policy if specified
if opts.Ingress != "" {
// Make sure metadata annotations exist
if service.Metadata.Annotations == nil {
service.Metadata.Annotations = make(map[string]string)
}

// Set the ingress value
switch opts.Ingress {
case "internal":
service.Metadata.Annotations["run.googleapis.com/ingress"] = "internal"
case "internal-and-cloud-load-balancing":
service.Metadata.Annotations["run.googleapis.com/ingress"] = "internal-and-cloud-load-balancing"
default: // "all" is the default
service.Metadata.Annotations["run.googleapis.com/ingress"] = "all"
}
log.Printf("Setting ingress to: %s", opts.Ingress)
}

// Set authentication policy
if service.Metadata.Annotations == nil {
service.Metadata.Annotations = make(map[string]string)
}

if opts.AllowUnauthenticated {
service.Metadata.Annotations["run.googleapis.com/ingress-status"] = "all"
log.Println("Allowing unauthenticated access")
} else {
service.Metadata.Annotations["run.googleapis.com/ingress-status"] = "internal-and-cloud-load-balancing"
log.Println("Requiring authentication for access")
}
}

// buildServiceDefinition creates a new Cloud Run Service object based on the
@@ -531,25 +569,27 @@ func buildServiceDefinition(projectID string, opts config.DeployOptions) run.Ser
if secretName != "" {
log.Printf("Building service: Adding secret env var %s with secret %s and version %s", k, secretName, version)

// Create a properly initialized SecretKeySelector with all required fields
secretKeyRef := &run.SecretKeySelector{
Key: version,
Name: secretName,
LocalObjectReference: &run.LocalObjectReference{
Name: secretName,
},
}

// Use explicit field initialization to avoid null values in JSON
container.Env = append(container.Env, &run.EnvVar{
Name: k,
Name: k,
Value: "", // Ensure value is empty string, not null
ValueFrom: &run.EnvVarSource{
SecretKeyRef: &run.SecretKeySelector{
Key: version,
LocalObjectReference: &run.LocalObjectReference{
Name: secretName,
},
},
SecretKeyRef: secretKeyRef,
},
})

// Log the resulting structure to verify it's set correctly
log.Printf("Secret reference set for new service: Name=%s, Key=%s",
secretName, version)

// Additional debug to see exactly what we're sending
container.Env[len(container.Env)-1].Value = "" // Ensure value is empty string, not null
}
}
}
@@ -559,7 +599,11 @@ func buildServiceDefinition(projectID string, opts config.DeployOptions) run.Ser
rService := run.Service{
ApiVersion: "serving.knative.dev/v1",
Kind: "Service",
Metadata: &run.ObjectMeta{Namespace: projectID, Name: opts.Service},
Metadata: &run.ObjectMeta{
Namespace: projectID,
Name: opts.Service,
Annotations: make(map[string]string),
},
Spec: &run.ServiceSpec{
Template: &run.RevisionTemplate{
Spec: &run.RevisionSpec{
@@ -571,5 +615,27 @@ func buildServiceDefinition(projectID string, opts config.DeployOptions) run.Ser
},
}

// Set ingress traffic policy
switch opts.Ingress {
case "internal":
rService.Metadata.Annotations["run.googleapis.com/ingress"] = "internal"
log.Printf("Setting ingress to: internal")
case "internal-and-cloud-load-balancing":
rService.Metadata.Annotations["run.googleapis.com/ingress"] = "internal-and-cloud-load-balancing"
log.Printf("Setting ingress to: internal-and-cloud-load-balancing")
default: // "all" is the default
rService.Metadata.Annotations["run.googleapis.com/ingress"] = "all"
log.Printf("Setting ingress to: all")
}

// Set authentication policy
if opts.AllowUnauthenticated {
rService.Metadata.Annotations["run.googleapis.com/ingress-status"] = "all"
log.Println("Allowing unauthenticated access")
} else {
rService.Metadata.Annotations["run.googleapis.com/ingress-status"] = "internal-and-cloud-load-balancing"
log.Println("Requiring authentication for access")
}

return rService
}