diff --git a/infrabox/local-dev/docker-compose.yml b/infrabox/local-dev/docker-compose.yml index 28600e820..8ed70d46b 100644 --- a/infrabox/local-dev/docker-compose.yml +++ b/infrabox/local-dev/docker-compose.yml @@ -16,7 +16,7 @@ services: - "5432:5432" minio: - image: minio/minio + image: quay.io/minio/minio command: server /data environment: - MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE diff --git a/infrabox/test/api/docker-compose.yml b/infrabox/test/api/docker-compose.yml index bf63e5239..4bfdafbda 100644 --- a/infrabox/test/api/docker-compose.yml +++ b/infrabox/test/api/docker-compose.yml @@ -7,7 +7,7 @@ services: dockerfile: ./src/postgres/Dockerfile minio: - image: minio/minio + image: quay.io/minio/minio command: server /data environment: - MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE diff --git a/infrabox/utils/storage/docker-compose.yml b/infrabox/utils/storage/docker-compose.yml index 5d0d72f6d..280a1833c 100644 --- a/infrabox/utils/storage/docker-compose.yml +++ b/infrabox/utils/storage/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.2" services: minio: - image: minio/minio + image: quay.io/minio/minio command: server /data environment: - MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE diff --git a/src/services/gcp/README.md b/src/services/gcp/README.md index 9698dea28..ceb92e1dd 100644 --- a/src/services/gcp/README.md +++ b/src/services/gcp/README.md @@ -84,6 +84,69 @@ or using export KUBECONFIG="/var/run/infrabox.net/services/$SERVICE_NAME/kubeconfig" ``` +## Spec Reference + +| Field | Type | Default | Description | +|---|---|---|---| +| `zone` | string | — | GCP zone (required) | +| `machineType` | string | — | GKE node machine type | +| `numNodes` | int | — | Number of nodes | +| `diskSize` | int | — | Boot disk size in GB | +| `clusterVersion` | string | — | Kubernetes version (e.g. `1.37`) | +| `preemptible` | bool | false | Use preemptible nodes | +| `enableAutoscaling` | bool | false | Enable cluster autoscaling | +| `minNodes` | int | — | Minimum nodes (requires `enableAutoscaling`) | +| `maxNodes` | int | — | Maximum nodes (requires `enableAutoscaling`) | +| `enableNetworkPolicy` | bool | false | Enable Kubernetes network policy | +| `disableLegacyAuthorization` | bool | false | Disable legacy ABAC authorization | +| `enablePodSecurityPolicy` | bool | false | Enable Pod Security Policy | +| `stackType` | string | — | Set to `ipv4-ipv6` for dual-stack networking | +| `enableManagedPrometheus` | bool | false | Enable Google Managed Prometheus | +| `serviceCidr` | string | `/18` | Services IPv4 CIDR | +| `clusterCidr` | string | `/18` | Pods IPv4 CIDR | +| `keepAlive` | bool | false | Keep cluster running after job ends (see below) | + +## Long-Running Clusters (keepAlive) + +By default, GKE clusters are deleted as soon as the InfraBox job finishes. Set `keepAlive: true` to keep the cluster running after the job ends — useful for debugging or extended testing scenarios. + +```json +"services": [{ + "apiVersion": "gcp.service.infrabox.net/v1alpha1", + "kind": "GKECluster", + "metadata": { + "name": "my-cluster" + }, + "spec": { + "zone": "us-east1-b", + "machineType": "n1-standard-1", + "numNodes": 1, + "diskSize": 100, + "clusterVersion": "1.37", + "keepAlive": true + } +}] +``` + +When `keepAlive: true`: +- The cluster is tagged with the GCP label `infrabox-keep-alive=true` at creation time +- InfraBox removes its CRD record when the job ends but does **not** delete the GKE cluster +- The cluster is excluded from InfraBox's GC loop + +**Finding keepAlive clusters:** +```bash +gcloud container clusters list \ + --filter="labels.infrabox-keep-alive=true" \ + --format="table(name,zone,status,createTime)" +``` + +**Deleting a keepAlive cluster:** +```bash +gcloud container clusters delete --zone +``` + +> **Note:** keepAlive clusters are not managed by InfraBox after the job ends. You are responsible for deleting them to avoid ongoing GCP costs. + ## Install To install the service in your Kubernetes cluster you have to first create a GCP Service Account with `Kubernetes Engine Admin` and `Service Account User` roles. Download the service account json file and save it as `service_account.json`. Then create a secret for it: diff --git a/src/services/gcp/infrabox-service-gcp/templates/crd.yaml b/src/services/gcp/infrabox-service-gcp/templates/crd.yaml index 5c33afbfa..9591f2b0d 100644 --- a/src/services/gcp/infrabox-service-gcp/templates/crd.yaml +++ b/src/services/gcp/infrabox-service-gcp/templates/crd.yaml @@ -54,6 +54,8 @@ spec: type: boolean stackType: type: string + keepAlive: + type: boolean status: x-kubernetes-preserve-unknown-fields: true names: diff --git a/src/services/gcp/pkg/apis/gcp/v1alpha1/types.go b/src/services/gcp/pkg/apis/gcp/v1alpha1/types.go index e514d1f84..1b14a1d6b 100644 --- a/src/services/gcp/pkg/apis/gcp/v1alpha1/types.go +++ b/src/services/gcp/pkg/apis/gcp/v1alpha1/types.go @@ -32,6 +32,7 @@ type GKEClusterSpec struct { ServiceCidr string `json:"serviceCidr,omitempty"` ClusterCidr string `json:"clusterCidr,omitempty"` EnableManagedPrometheus bool `json:"enableManagedPrometheus,omitempty"` + KeepAlive bool `json:"keepAlive,omitempty"` } type GKEClusterStatus struct { diff --git a/src/services/gcp/pkg/stub/handler.go b/src/services/gcp/pkg/stub/handler.go index fc110311b..ed35c3953 100644 --- a/src/services/gcp/pkg/stub/handler.go +++ b/src/services/gcp/pkg/stub/handler.go @@ -240,6 +240,9 @@ func createCluster(cr *v1alpha1.GKECluster, log *logrus.Entry) (*v1alpha1.GKEClu if !cr.Spec.EnableManagedPrometheus { args = append(args, "--no-enable-managed-prometheus") } + if cr.Spec.KeepAlive { + args = append(args, "--labels", "infrabox-keep-alive=true") + } master_authorized_networks := os.Getenv("ALLOW_IPS") if master_authorized_networks == "" { master_authorized_networks = "0.0.0.0/0" @@ -546,6 +549,15 @@ func checkTimeout(cr *v1alpha1.GKECluster, log *logrus.Entry) error { } func deleteGKECluster(cr *v1alpha1.GKECluster, log *logrus.Entry) error { + if cr.Spec.KeepAlive { + if err := cleanUpCrd(cr, log); err != nil { + log.Errorf("Failed to remove GKECluster CRD: %v", err) + return err + } + log.Infof("GKE cluster %s kept alive (keepAlive=true)", cr.Status.ClusterName) + return nil + } + // Get the GKE Cluster gkecluster, err := getRemoteCluster(cr.Status.ClusterName, log) if err != nil && !errors.IsNotFound(err) { @@ -895,7 +907,7 @@ func cleanUpClusters(maxAge string, log *logrus.Entry) { func getOutdatedClusters(maxAge string, log *logrus.Entry) ([]RemoteCluster, error) { cmd := exec.Command("gcloud", "container", "clusters", "list", - "--filter", fmt.Sprintf("createTime<-P%s AND name:ib-*", maxAge), + "--filter", fmt.Sprintf("createTime<-P%s AND name:ib-* AND NOT labels.infrabox-keep-alive=true", maxAge), "--format", "json") out, err := cmd.Output()