diff --git a/.github/workflows/release-please-updates.yaml b/.github/workflows/release-please-updates.yaml index 57ef40a0..449d4104 100644 --- a/.github/workflows/release-please-updates.yaml +++ b/.github/workflows/release-please-updates.yaml @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -name: Release PRs +name: Release PR on: pull_request: types: [opened, synchronize, reopened, labeled] @@ -20,11 +20,8 @@ jobs: build: name: "Code Generation" runs-on: ubuntu-latest + if: "${{ github.actor == 'release-please[bot]' }}" steps: - - name: Print Actor - uses: actions/github-script@v6 - with: - script: console.log("The github actor is ${{github.actor}}"); - name: Setup Go uses: actions/setup-go@v3 with: @@ -35,5 +32,4 @@ jobs: ref: ${{ github.event.pull_request.head.ref }} repository: ${{ github.event.pull_request.head.repo.full_name }} - name: Generate code and commit differences - if: "${{ github.actor == 'release-please[bot]' }}" run: tools/release-pr-generate.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 87c5af30..665349c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## [0.1.0](https://github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/compare/v0.0.3...v0.1.0) (2022-12-13) + + +### Features + +* add user agent to proxy invocation ([#122](https://github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/issues/122)) ([803446d](https://github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/commit/803446d4766fe556cb149725100f7e955bd8c8d0)) + + +### Bug Fixes + +* change memory resource to match recommendations Cloud SQL Proxy ([#139](https://github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/issues/139)) ([a475dd9](https://github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/commit/a475dd934a59469e9ef38fd9934593d7d7c3b0e6)) +* remove unsupported CRD fields and associated code from the project. ([#141](https://github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/issues/141)) ([3867621](https://github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/commit/386762120f386a459c57c6e3e090e6795f53886f)) + ## [0.0.3](https://github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/compare/v0.0.1...v0.0.3) (2022-12-07) diff --git a/Makefile b/Makefile index f675bfbf..6f7eb5d3 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ # Global settings ## RELEASE_TAG is the public image tag for the operator -RELEASE_TAG_PATH=cloud-sql-connectors/cloud-sql-operator-dev/cloud-sql-proxy-operator:$(VERSION) +RELEASE_TAG_PATH=cloud-sql-connectors/cloud-sql-operator/cloud-sql-proxy-operator:$(VERSION) RELEASE_TAG=gcr.io/$(RELEASE_TAG_PATH) # When the environment variable IS_RELEASE_BUILD is set, the IMG will be set @@ -85,7 +85,7 @@ help: ## Display this help. install_tools: remove_tools all_tools ## Installs all development tools .PHONY: generate -generate: ctrl_generate ctrl_manifests go_lint tf_lint installer reset_image add_copyright_header go_fmt yaml_fmt ## Runs code generation, format, and validation tools +generate: ctrl_generate ctrl_manifests go_lint tf_lint installer reset_image add_copyright_header update_version_in_docs go_fmt yaml_fmt ## Runs code generation, format, and validation tools .PHONY: build build: generate build_push_docker ## Builds and pushes the docker image to tag defined in envvar IMG @@ -125,6 +125,12 @@ yaml_fmt: # Automatically formats all yaml files add_copyright_header: # Add the copyright header go run github.com/google/addlicense@latest * +.PHONY: update_version_in_docs +update_version_in_docs: # Fix version numbers that appear in the markdown documentation + # Update links to the install script + find . -name '*.md' | xargs sed -i.bak -E 's|storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy-operator/[^/]+/install.sh|storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy-operator/v$(VERSION)/install.sh|g' && \ + find . -name '*.md.bak' | xargs rm -f + .PHONY: build_push_docker build_push_docker: # Build docker image with the operator. set IMG env var before running: `IMG=example.com/img:1.0 make build` @test -n "$(IMG)" || ( echo "IMG environment variable must be set to the public repo where you want to push the image" ; exit 1) @@ -216,7 +222,9 @@ E2E_KUBECTL = $(E2E_KUBECTL_ENV) $(KUBECTL) # This is the file where Terraform will write the URL to the e2e container registry E2E_DOCKER_URL_FILE :=$(PWD)/bin/gcloud-docker-repo.url E2E_DOCKER_URL=$(shell cat $(E2E_DOCKER_URL_FILE) | tr -d '\n') -E2E_PROXY_URL ?= "gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.0.0-preview.2" + +# Default value in the Makefile blank. When blank tests will use workload.DefaultProxyImage +E2E_PROXY_URL ?= "" E2E_WORK_DIR=$(PWD)/bin/e2e $(E2E_WORK_DIR): diff --git a/README.md b/README.md index 8f543f47..86bcb4cb 100644 --- a/README.md +++ b/README.md @@ -9,34 +9,78 @@ which specifies the Cloud SQL Auth Proxy configuration for a workload. The opera reads this resource and adds a properly configured Cloud SQL Auth Proxy container to the matching workload pods. -## Setting up the initial project -These commands will be run to initialize the kubebuilder project +## Installation +Check for the latest version on the [releases page][releases] and use the +following instructions. -``` -# Get the kubebuilder binary -mkdir -p .bin -curl -L -o .bin/kubebuilder "https://github.com/kubernetes-sigs/kubebuilder/releases/download/v3.6.0/kubebuilder_$(go env GOOS)_$(go env GOARCH)" -chmod a+x .bin/kubebuilder +Confirm that kubectl can connect to your kubernetes cluster. -# Clean up the root dir for kubebuilder -rm -rf Makefile main.go go.mod go.sum cover.out +```shell +kubectl cluster-info +``` -mkdir -p .bin/tmp/ -mv docs .bin/tmp/ -mv version.txt .bin/tmp/ +Run the following command to install the cloud sql proxy operator into +your kubernetes cluster: -rm -rf bin -.bin/kubebuilder init --owner "Google LLC" --project-name "cloud-sql-proxy-operator" --domain cloud.google.com --repo github.com/GoogleCloudPlatform/cloud-sql-proxy-operator +```shell +curl https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy-operator/v0.1.0/install.sh | bash +``` -mv .bin/tmp/* . +Confirm that the operator is installed and running by listing its pods: +```shell +kubectl get pods -n cloud-sql-proxy-operator-system ``` -Then, to create the CRD for Workload -``` -.bin/kubebuilder create api --group cloudsql --version v1alpha1 --kind AuthProxyWorkload --controller --resource --force -.bin/kubebuilder create webhook --group cloudsql --version v1alpha1 --kind AuthProxyWorkload --defaulting --programmatic-validation -``` +## Usage + +See the [Quick Start Guide](docs/quick-start.md) for a description of basic usage. +Additional usage may be found in the [Examples](docs/examples/). + +## Frequently Asked Questions + +### Why would I use the Cloud SQL Auth Proxy Operator? + +The Cloud SQL Auth Proxy Operator gives you an easy way to add a proxy container +to your kubernetes workloads, configured correctly for production use. + +Writing the kubernetes configuration for a proxy to the production level requires +a great deal of deep kubernetes and proxy knowledge. The Cloud SQL Proxy team has +worked to encapsulate that knowledge in this operator. This saves you from having +to know all the details to configure your proxy. + +## Reference Documentation +- [Quick Start Guide](docs/quick-start.md) +- [Cloud SQL Proxy](/GoogleCloudPlatform/cloud-sql-proxy) +- [Developer Getting Started](docs/dev.md) +- [Developing End-to-End tests](docs/e2e-tests.md) +- [Contributing](docs/contributing.md) +- [Code of Conduct](docs/code-of-conduct.md) +- [Examples](docs/examples/) + +## Support policy + +### Major version lifecycle + +This project uses [semantic versioning](https://semver.org/), and uses the +following lifecycle regarding support for a major version: + +**Active** - Active versions get all new features and security fixes (that +wouldn’t otherwise introduce a breaking change). New major versions are +guaranteed to be "active" for a minimum of 1 year. +**Deprecated** - Deprecated versions continue to receive security and critical +bug fixes, but do not receive new features. Deprecated versions will be publicly +supported for 1 year. +**Unsupported** - Any major version that has been deprecated for >=1 year is +considered publicly unsupported. + +## Contributing + +Contributions are welcome. Please, see the [CONTRIBUTING][contributing] document +for details. +Please note that this project is released with a Contributor Code of Conduct. +By participating in this project you agree to abide by its terms. See +[Contributor Code of Conduct][code-of-conduct] for more information. diff --git a/config/crd/bases/cloudsql.cloud.google.com_authproxyworkloads.yaml b/config/crd/bases/cloudsql.cloud.google.com_authproxyworkloads.yaml index 233ca84a..9262663f 100644 --- a/config/crd/bases/cloudsql.cloud.google.com_authproxyworkloads.yaml +++ b/config/crd/bases/cloudsql.cloud.google.com_authproxyworkloads.yaml @@ -829,12 +829,6 @@ spec: required: - name type: object - fuseDir: - description: FUSEDir is the path where the FUSE volume will be mounted. This sets the proxy container's CLI argument `--fuse` and will mount the FUSE volume at this path on all containers in the workload. - type: string - fuseTempDir: - description: FUSETempDir is the path for the temp dir for Unix sockets created with FUSE. This sets the proxy container's CLI argument `--fuse-tmp-dir` and will mount the FUSE temp volume at this path on all containers in the workload. - type: string image: description: Image is the URL to the proxy image. Optional, by default the operator will use the latest known compatible proxy image. type: string @@ -871,51 +865,6 @@ spec: sqlAdminAPIEndpoint: description: SQLAdminAPIEndpoint is a debugging parameter that when specified will change the Google Cloud api endpoint used by the proxy. type: string - telemetry: - description: Telemetry specifies how the proxy should expose telemetry. Optional, by default - properties: - disableMetrics: - description: DisableMetrics disables Cloud Monitoring testintegration (used with telemetryProject) This sets the proxy container's CLI argument `--disable-metrics` - type: boolean - disableTraces: - description: DisableTraces disables Cloud Trace testintegration (used with telemetryProject) This sets the proxy container's CLI argument `--disable-traces` - type: boolean - httpPort: - description: HTTPPort the port for Prometheus and health check server. This sets the proxy container's CLI argument `--http-port` - format: int32 - type: integer - prometheus: - description: Prometheus Enables Prometheus HTTP endpoint /metrics on localhost This sets the proxy container's CLI argument `--prometheus` - type: boolean - prometheusNamespace: - description: PrometheusNamespace is used the provided Prometheus namespace for metrics This sets the proxy container's CLI argument `--prometheus-namespace` - type: string - quotaProject: - description: QuotaProject Specifies the project to use for Cloud SQL Admin API quota tracking. The IAM principal must have the "serviceusage.services.use" permission for the given project. See https://cloud.google.com/service-usage/docs/overview and https://cloud.google.com/storage/docs/requester-pays This sets the proxy container's CLI argument `--quota-project` - type: string - telemetryPrefix: - description: TelemetryPrefix is the prefix for Cloud Monitoring metrics. This sets the proxy container's CLI argument `--telemetry-prefix` - type: string - telemetryProject: - description: TelemetryProject enables Cloud Monitoring and Cloud Trace with the provided project ID. This sets the proxy container's CLI argument `--telemetry-project` - type: string - telemetrySampleRate: - description: TelemetrySampleRate is the Cloud Trace sample rate. A smaller number means more traces. This sets the proxy container's CLI argument `--telemetry-sample-rate` - type: integer - type: object - type: object - authentication: - description: Authentication describes how to authenticate the Auth Proxy container to Google Cloud - properties: - credentialsFileKey: - description: CredentialsFileKey The key within the kubernetes secret containing the credentials file. This sets the Cloud SQL Proxy container's CLI argument `--credentials-file` - type: string - credentialsFileSecret: - description: CredentialsFileSecret the "name" or "namespace/name" for the secret. This sets the Cloud SQL Proxy container's CLI argument `--credentials-file` - type: string - gcloudAuth: - description: GCloudAuth true when we should use the Google Cloud metadata server to authenticate. This sets the Cloud SQL Proxy container's CLI argument `--gcloud-auth` - type: boolean type: object instances: description: Instances lists the Cloud SQL instances to connect @@ -941,18 +890,6 @@ spec: privateIP: description: PrivateIP Enable connection to the Cloud SQL instance's private ip for this instance. Optional, default false. type: boolean - socketType: - description: 'SocketType declares what type of socket to create for this database. Allowed values: "tcp" or "unix"' - enum: - - tcp - - unix - type: string - unixSocketPath: - description: UnixSocketPath is the directory to mount the unix socket for this instance. When set, this directory will be mounted on all containers in the workload. - type: string - unixSocketPathEnvName: - description: UnixSocketPathEnvName the name of the environment variable containing the unix socket path Optional, when set this environment variable will be added to all containers in the workload. - type: string type: object minItems: 1 type: array diff --git a/docs/dev.md b/docs/dev.md new file mode 100644 index 00000000..7a5b362f --- /dev/null +++ b/docs/dev.md @@ -0,0 +1,33 @@ +# Development + +# How this project was created +This project was initially scaffolded by `kubebuilder` 3.6.0. These are the +commands initially used to set up the project. + +``` +# Get the kubebuilder binary +mkdir -p .bin +curl -L -o .bin/kubebuilder "https://github.com/kubernetes-sigs/kubebuilder/releases/download/v3.6.0/kubebuilder_$(go env GOOS)_$(go env GOARCH)" +chmod a+x .bin/kubebuilder + +# Clean up the root dir for kubebuilder +rm -rf Makefile main.go go.mod go.sum cover.out + +mkdir -p .bin/tmp/ +mv docs .bin/tmp/ +mv version.txt .bin/tmp/ + +rm -rf bin +.bin/kubebuilder init --owner "Google LLC" --project-name "cloud-sql-proxy-operator" --domain cloud.google.com --repo github.com/GoogleCloudPlatform/cloud-sql-proxy-operator + +mv .bin/tmp/* . + +``` + +Then, to create the CRD for Workload +``` +.bin/kubebuilder create api --group cloudsql --version v1alpha1 --kind AuthProxyWorkload --controller --resource --force +.bin/kubebuilder create webhook --group cloudsql --version v1alpha1 --kind AuthProxyWorkload --defaulting --programmatic-validation +``` + + diff --git a/docs/examples/deployment-postgres-tcp.yaml b/docs/examples/deployment-postgres-tcp.yaml new file mode 100644 index 00000000..3c86a0d7 --- /dev/null +++ b/docs/examples/deployment-postgres-tcp.yaml @@ -0,0 +1,115 @@ +# Copyright 2022 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +### +# This example demonstrates how to use environment variables set by the +# Cloud SQL Proxy Operator to connect to your database. + +## +# Create an AuthProxyWorkload to hold the configuration for your +# Cloud SQL Proxy containers. + +apiVersion: cloudsql.cloud.google.com/v1alpha1 +kind: AuthProxyWorkload +metadata: + name: authproxyworkload-sample +spec: + workloadSelector: + kind: "Deployment" # Applies to a "Deployment" + name: "gke-cloud-sql-app" # named 'gke-cloud-sql-app' + instances: + - connectionString: "my-project:us-central1:instance" # from your Cloud SQL Database instance + portEnvName: "DB_PORT" # Will set an env var named 'DB_PORT' to the database port + hostEnvName: "DB_HOST" # Will set an env var named 'DB_HOST' to the proxy's host, 127.0.0.1 +--- +## +# Put the database name, username, and password into a kubernetes secret +# Update the values below as needed for your environment +# +# WARNING: Do not store passwords in a source code file. It is a bad +# way to keep your secrets safe. +# +# Instead, use kubectl to create the secret using an interactive command +# so that your password is not stored in your source code. +# +# kubectl create secret generic gke-cloud-sql-operator-demo \ +# --from-literal=DB_NAME=your_db_name \ +# --from-literal=DB_USER=your_db_user \ +# --from-literal=DB_PASS=your_db_password +# +apiVersion: v1 +kind: Secret +metadata: + name: gke-cloud-sql-operator-demo +type: Opaque +data: + DB_PASS: cGFzc3dvcmQ= # "password" + DB_NAME: cG9zdGdyZXM= # "postgres" + DB_USER: dGVzdHVzZXI= # "testuser" +--- +## +# Create a deployment for your application that uses environment variables +# set by the proxy to connect to the database. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gke-cloud-sql-app +spec: + selector: + matchLabels: + app: gke-cloud-sql-app + template: + metadata: + labels: + app: gke-cloud-sql-app + spec: + containers: + - name: gke-cloud-sql-app + image: postgres + livenessProbe: + initialDelaySeconds: 60 + periodSeconds: 30 + failureThreshold: 3 + exec: + command: ["/bin/sh", "-c", "psql --host=$DB_HOST --port=$DB_PORT --username=$DB_USER '--command=select 1' --echo-queries --dbname=$DB_NAME"] + command: + - "/bin/sh" + - "-e" + - "-c" + - "sleep 10 ; psql --host=$DB_HOST --port=$DB_PORT --username=$DB_USER '--command=select 1' --echo-queries --dbname=$DB_NAME ; sleep 3600" + env: + - name: DB_HOST + value: "set-by-operator" + - name: DB_PORT + value: "set-by-operator" + - name: DB_USER + valueFrom: + secretKeyRef: + name: gke-cloud-sql-operator-demo + key: DB_USER + - name: DB_USER + valueFrom: + secretKeyRef: + name: gke-cloud-sql-operator-demo + key: DB_USER + - name: PGPASSWORD # The env name PGPASSWORD is specific to the psql command. + valueFrom: + secretKeyRef: + name: gke-cloud-sql-operator-demo + key: DB_PASS + - name: DB_NAME + valueFrom: + secretKeyRef: + name: gke-cloud-sql-operator-demo + key: DB_NAME diff --git a/docs/quick-start.md b/docs/quick-start.md new file mode 100644 index 00000000..ed92f820 --- /dev/null +++ b/docs/quick-start.md @@ -0,0 +1,135 @@ +# Quick Start + +Follow the instructions in the Quick Start Guide for Cloud SQL: +[Connect to Cloud SQL for PostgreSQL from Google Kubernetes Engine]( +https://cloud.google.com/sql/docs/postgres/connect-instance-kubernetes) +through the end of the step named [Build the Sample App]( +https://cloud.google.com/sql/docs/postgres/connect-instance-kubernetes#build_the_sample_app). + +Then, continue following these instructions: + +## Install the Cloud SQL Proxy Operator + +Confirm that kubectl can connect to the cluster. + +```shell +kubectl cluster-info +``` + +Run the following command to install the cloud sql proxy operator into +your kuberentes cluster: + +```shell +curl https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy-operator/v0.1.0/install.sh | bash +``` + +Confirm that the operator is installed and running by listing its pods: + +```shell +kubectl get pods -n cloud-sql-proxy-operator-system +``` + +## Configure Cloud SQL Proxy for the quick start app + +Get the Cloud SQL instance connection name by running the gcloud sql instances describe command: + +```shell +gcloud sql instances describe quickstart-instance --format='value(connectionName)' +``` + +Create a new file named `authproxyworkload.yaml` containing the following: + +```yaml +apiVersion: cloudsql.cloud.google.com/v1alpha1 +kind: AuthProxyWorkload +metadata: + name: authproxyworkload-sample + -spec: + workloadSelector: + kind: "Deployment" + name: "gke-cloud-sql-quickstart" + instances: + - connectionString: "" + portEnvName: "DB_PORT" + hostEnvName: "INSTANCE_HOST" +``` + +Update with the Cloud SQL instance connection name +retrieved from the gcloud command on the previous step. The format is +project_id:region:instance_name. The instance connection name is also visible +in the Cloud SQL instance Overview page. + +Apply the proxy configuration to to kubernetes: + +```shell +kubectl apply -f authproxyworkload.yaml +``` + +### Deploy the sample app + +Proceed with the quickstart guide step [Deploy the sample app]( +https://cloud.google.com/sql/docs/postgres/connect-instance-kubernetes#deploy_the_sample_app). +In step 2, use this YAML as your template. + +Note that this template has only one container for the application. In the published +quickstart guide, there are two containers, one for the application, and one for the +proxy. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gke-cloud-sql-quickstart + -spec: + selector: + matchLabels: + app: gke-cloud-sql-app + template: + metadata: + labels: + app: gke-cloud-sql-app + spec: + serviceAccountName: + containers: + - name: gke-cloud-sql-app + # Replace with your Artifact Registry location (e.g., us-central1). + # Replace with your project ID. + image: -docker.pkg.dev//gke-cloud-sql-repo/gke-sql:latest + # This app listens on port 8080 for web traffic by default. + ports: + - containerPort: 8080 + env: + - name: PORT + value: "8080" + - name: INSTANCE_HOST + value: "set-by-proxy" + - name: DB_PORT + value: "set-by-proxy" + - name: DB_USER + valueFrom: + secretKeyRef: + name: + key: username + - name: DB_PASS + valueFrom: + secretKeyRef: + name: + key: password + - name: DB_NAME + valueFrom: + secretKeyRef: + name: + key: database +``` + +### Inspect the container managed by the proxy operator +Finally, after completing the steps in the quickstart guide, inspect the pods +for the application to see the proxy container. + +```shell +kubectl describe pods -l app=gke-cloud-sql-app +``` + +Note that there are now two containers in the pods, while there is only one +container in the deployment. The operator adds a second proxy container configured +using the settings in the `AuthProxyWorkload` resource. diff --git a/installer/cloud-sql-proxy-operator.yaml b/installer/cloud-sql-proxy-operator.yaml index 34887752..13da76a2 100644 --- a/installer/cloud-sql-proxy-operator.yaml +++ b/installer/cloud-sql-proxy-operator.yaml @@ -847,12 +847,6 @@ spec: required: - name type: object - fuseDir: - description: FUSEDir is the path where the FUSE volume will be mounted. This sets the proxy container's CLI argument `--fuse` and will mount the FUSE volume at this path on all containers in the workload. - type: string - fuseTempDir: - description: FUSETempDir is the path for the temp dir for Unix sockets created with FUSE. This sets the proxy container's CLI argument `--fuse-tmp-dir` and will mount the FUSE temp volume at this path on all containers in the workload. - type: string image: description: Image is the URL to the proxy image. Optional, by default the operator will use the latest known compatible proxy image. type: string @@ -889,51 +883,6 @@ spec: sqlAdminAPIEndpoint: description: SQLAdminAPIEndpoint is a debugging parameter that when specified will change the Google Cloud api endpoint used by the proxy. type: string - telemetry: - description: Telemetry specifies how the proxy should expose telemetry. Optional, by default - properties: - disableMetrics: - description: DisableMetrics disables Cloud Monitoring testintegration (used with telemetryProject) This sets the proxy container's CLI argument `--disable-metrics` - type: boolean - disableTraces: - description: DisableTraces disables Cloud Trace testintegration (used with telemetryProject) This sets the proxy container's CLI argument `--disable-traces` - type: boolean - httpPort: - description: HTTPPort the port for Prometheus and health check server. This sets the proxy container's CLI argument `--http-port` - format: int32 - type: integer - prometheus: - description: Prometheus Enables Prometheus HTTP endpoint /metrics on localhost This sets the proxy container's CLI argument `--prometheus` - type: boolean - prometheusNamespace: - description: PrometheusNamespace is used the provided Prometheus namespace for metrics This sets the proxy container's CLI argument `--prometheus-namespace` - type: string - quotaProject: - description: QuotaProject Specifies the project to use for Cloud SQL Admin API quota tracking. The IAM principal must have the "serviceusage.services.use" permission for the given project. See https://cloud.google.com/service-usage/docs/overview and https://cloud.google.com/storage/docs/requester-pays This sets the proxy container's CLI argument `--quota-project` - type: string - telemetryPrefix: - description: TelemetryPrefix is the prefix for Cloud Monitoring metrics. This sets the proxy container's CLI argument `--telemetry-prefix` - type: string - telemetryProject: - description: TelemetryProject enables Cloud Monitoring and Cloud Trace with the provided project ID. This sets the proxy container's CLI argument `--telemetry-project` - type: string - telemetrySampleRate: - description: TelemetrySampleRate is the Cloud Trace sample rate. A smaller number means more traces. This sets the proxy container's CLI argument `--telemetry-sample-rate` - type: integer - type: object - type: object - authentication: - description: Authentication describes how to authenticate the Auth Proxy container to Google Cloud - properties: - credentialsFileKey: - description: CredentialsFileKey The key within the kubernetes secret containing the credentials file. This sets the Cloud SQL Proxy container's CLI argument `--credentials-file` - type: string - credentialsFileSecret: - description: CredentialsFileSecret the "name" or "namespace/name" for the secret. This sets the Cloud SQL Proxy container's CLI argument `--credentials-file` - type: string - gcloudAuth: - description: GCloudAuth true when we should use the Google Cloud metadata server to authenticate. This sets the Cloud SQL Proxy container's CLI argument `--gcloud-auth` - type: boolean type: object instances: description: Instances lists the Cloud SQL instances to connect @@ -959,18 +908,6 @@ spec: privateIP: description: PrivateIP Enable connection to the Cloud SQL instance's private ip for this instance. Optional, default false. type: boolean - socketType: - description: 'SocketType declares what type of socket to create for this database. Allowed values: "tcp" or "unix"' - enum: - - tcp - - unix - type: string - unixSocketPath: - description: UnixSocketPath is the directory to mount the unix socket for this instance. When set, this directory will be mounted on all containers in the workload. - type: string - unixSocketPathEnvName: - description: UnixSocketPathEnvName the name of the environment variable containing the unix socket path Optional, when set this environment variable will be added to all containers in the workload. - type: string type: object minItems: 1 type: array @@ -1427,7 +1364,7 @@ spec: - --leader-elect command: - /manager - image: gcr.io/cloud-sql-connectors/cloud-sql-operator-dev/cloud-sql-proxy-operator:0.0.3 + image: gcr.io/cloud-sql-connectors/cloud-sql-operator/cloud-sql-proxy-operator:0.1.0 livenessProbe: httpGet: path: /healthz diff --git a/installer/install.sh b/installer/install.sh index 71a837b5..a8439efe 100644 --- a/installer/install.sh +++ b/installer/install.sh @@ -16,7 +16,7 @@ set -euxo # exit 1 from the script when command fails -VERSION="v0.0.3" +VERSION="v0.1.0" CERT_MANAGER_VERSION="v1.9.1" if ! which kubectl ; then diff --git a/internal/api/v1alpha1/authproxyworkload_types.go b/internal/api/v1alpha1/authproxyworkload_types.go index d8616857..01b427ff 100644 --- a/internal/api/v1alpha1/authproxyworkload_types.go +++ b/internal/api/v1alpha1/authproxyworkload_types.go @@ -28,10 +28,6 @@ const ( // ErrorCodeEnvConflict occurs when an the environment code does not work. ErrorCodeEnvConflict = "EnvVarConflict" - // ErrorCodeFUSENotSupported occurs when any FUSE configuration is set, - // because fuse is not yet supported. - ErrorCodeFUSENotSupported = "FUSENotSupported" - // AnnotationPrefix is used as the prefix for all annotations added to a domain object. // to hold metadata related to this operator. AnnotationPrefix = "cloudsql.cloud.google.com" @@ -57,10 +53,6 @@ const ( // has properly processed the latest generation of an AuthProxyInstance ConditionWorkloadUpToDate = "WorkloadUpToDate" - // ReasonNeedsUpdate relates to condition WorkloadUpToDate, this reason is set - // when there are no workloads related to this AuthProxyWorkload resource. - ReasonNeedsUpdate = "NeedsUpdate" - // ReasonUpToDate relates to condition WorkloadUpToDate, this reason is set // when there are no workloads related to this AuthProxyWorkload resource. ReasonUpToDate = "UpToDate" @@ -72,10 +64,6 @@ type AuthProxyWorkloadSpec struct { //+kubebuilder:validation:Required Workload WorkloadSelectorSpec `json:"workloadSelector"` - // Authentication describes how to authenticate the Auth Proxy container to Google Cloud - //+kubebuilder:validation:Optional - Authentication *AuthenticationSpec `json:"authentication,omitempty"` - // AuthProxyContainer describes the resources and config for the Auth Proxy container //+kubebuilder:validation:Optional AuthProxyContainer *AuthProxyContainerSpec `json:"authProxyContainer,omitempty"` @@ -124,32 +112,6 @@ func (s *WorkloadSelectorSpec) LabelsSelector() (labels.Selector, error) { return metav1.LabelSelectorAsSelector(s.Selector) } -// AuthenticationSpec describes how the proxy should get its Google Cloud identity -// to authenticate to the Google Cloud api. The proxy can get its Google Cloud -// identity in one of two ways: -// -// 1. Using the Google Cloud metadata server, in which case the AuthenticationSpec -// would set the GCloudAuth field to true. e.g. `{gcloudAuth:true}` -// 2. Using a IAM credential key file stored in a kubernetes secret, in which -// case the AuthenticationSpec would set CredentialFileSecret and CredentialFileKey. -// e.g. `{credentialFileSecret: "default/gcloud-cred", credentialFileKey="gcloud.json"}` -type AuthenticationSpec struct { - // CredentialsFileSecret the "name" or "namespace/name" for the secret. - // This sets the Cloud SQL Proxy container's CLI argument `--credentials-file` - //+kubebuilder:validation:Optional - CredentialsFileSecret string `json:"credentialsFileSecret,omitempty"` - - // CredentialsFileKey The key within the kubernetes secret containing the credentials file. - // This sets the Cloud SQL Proxy container's CLI argument `--credentials-file` - //+kubebuilder:validation:Optional - CredentialsFileKey string `json:"credentialsFileKey,omitempty"` - - // GCloudAuth true when we should use the Google Cloud metadata server to authenticate. - // This sets the Cloud SQL Proxy container's CLI argument `--gcloud-auth` - //+kubebuilder:validation:Optional - GCloudAuth bool `json:"gcloudAuth,omitempty"` -} - // AuthProxyContainerSpec specifies configuration for the proxy container. type AuthProxyContainerSpec struct { @@ -162,11 +124,6 @@ type AuthProxyContainerSpec struct { //+kubebuilder:validation:Optional Resources *v1.ResourceRequirements `json:"resources,omitempty"` - // Telemetry specifies how the proxy should expose telemetry. - // Optional, by default - //+kubebuilder:validation:Optional - Telemetry *TelemetrySpec `json:"telemetry,omitempty"` - // MaxConnections limits the number of connections. Default value is no limit. // This sets the proxy container's CLI argument `--max-connections` //+kubebuilder:validation:Optional @@ -178,77 +135,15 @@ type AuthProxyContainerSpec struct { //+kubebuilder:validation:Optional MaxSigtermDelay *int64 `json:"maxSigtermDelay,omitempty"` - // FUSEDir is the path where the FUSE volume will be mounted. - // This sets the proxy container's CLI argument `--fuse` and - // will mount the FUSE volume at this path on all containers in the workload. - //+kubebuilder:validation:Optional - FUSEDir string `json:"fuseDir,omitempty"` - - // FUSETempDir is the path for the temp dir for Unix sockets created with FUSE. - // This sets the proxy container's CLI argument `--fuse-tmp-dir` and - // will mount the FUSE temp volume at this path on all containers in the workload. - //+kubebuilder:validation:Optional - FUSETempDir string `json:"fuseTempDir,omitempty"` - // Image is the URL to the proxy image. Optional, by default the operator - // will use the latest known compatible proxy image. - //+kubebuilder:validation:Optional - Image string `json:"image,omitempty"` - // SQLAdminAPIEndpoint is a debugging parameter that when specified will // change the Google Cloud api endpoint used by the proxy. //+kubebuilder:validation:Optional SQLAdminAPIEndpoint string `json:"sqlAdminAPIEndpoint,omitempty"` -} - -// TelemetrySpec specifies how the proxy container will expose telemetry. -type TelemetrySpec struct { - // QuotaProject Specifies the project to use for Cloud SQL Admin API quota tracking. - // The IAM principal must have the "serviceusage.services.use" permission - // for the given project. See https://cloud.google.com/service-usage/docs/overview and - // https://cloud.google.com/storage/docs/requester-pays - // This sets the proxy container's CLI argument `--quota-project` - //+kubebuilder:validation:Optional - QuotaProject *string `json:"quotaProject,omitempty"` - - // Prometheus Enables Prometheus HTTP endpoint /metrics on localhost - // This sets the proxy container's CLI argument `--prometheus` - //+kubebuilder:validation:Optional - Prometheus *bool `json:"prometheus,omitempty"` - - // PrometheusNamespace is used the provided Prometheus namespace for metrics - // This sets the proxy container's CLI argument `--prometheus-namespace` - //+kubebuilder:validation:Optional - PrometheusNamespace *string `json:"prometheusNamespace,omitempty"` - - // TelemetryProject enables Cloud Monitoring and Cloud Trace with the provided project ID. - // This sets the proxy container's CLI argument `--telemetry-project` - //+kubebuilder:validation:Optional - TelemetryProject *string `json:"telemetryProject,omitempty"` - - // TelemetryPrefix is the prefix for Cloud Monitoring metrics. - // This sets the proxy container's CLI argument `--telemetry-prefix` - //+kubebuilder:validation:Optional - TelemetryPrefix *string `json:"telemetryPrefix,omitempty"` - - // TelemetrySampleRate is the Cloud Trace sample rate. A smaller number means more traces. - // This sets the proxy container's CLI argument `--telemetry-sample-rate` - //+kubebuilder:validation:Optional - TelemetrySampleRate *int `json:"telemetrySampleRate,omitempty"` - - // HTTPPort the port for Prometheus and health check server. - // This sets the proxy container's CLI argument `--http-port` - //+kubebuilder:validation:Optional - HTTPPort *int32 `json:"httpPort,omitempty"` - // DisableTraces disables Cloud Trace testintegration (used with telemetryProject) - // This sets the proxy container's CLI argument `--disable-traces` - //+kubebuilder:validation:Optional - DisableTraces *bool `json:"disableTraces,omitempty"` - - // DisableMetrics disables Cloud Monitoring testintegration (used with telemetryProject) - // This sets the proxy container's CLI argument `--disable-metrics` + // Image is the URL to the proxy image. Optional, by default the operator + // will use the latest known compatible proxy image. //+kubebuilder:validation:Optional - DisableMetrics *bool `json:"disableMetrics,omitempty"` + Image string `json:"image,omitempty"` } // InstanceSpec describes the configuration for how the proxy should expose @@ -293,12 +188,6 @@ type InstanceSpec struct { //+kubebuilder:validation:Required ConnectionString string `json:"connectionString,omitempty"` - // SocketType declares what type of socket to create for this database. Allowed - // values: "tcp" or "unix" - //+kubebuilder:validation:Enum=tcp;unix - //+kubebuilder:validation:Optional - SocketType string `json:"socketType,omitempty"` - // Port sets the tcp port for this instance. Optional, if not set, a value will // be automatically assigned by the operator and set as an environment variable // on all containers in the workload named according to PortEnvName. The operator will choose @@ -306,11 +195,6 @@ type InstanceSpec struct { //+kubebuilder:validation:Optional Port *int32 `json:"port,omitempty"` - // UnixSocketPath is the directory to mount the unix socket for this instance. - // When set, this directory will be mounted on all containers in the workload. - //+kubebuilder:validation:Optional - UnixSocketPath string `json:"unixSocketPath,omitempty"` - // AutoIAMAuthN Enables IAM Authentication for this instance. Optional, default // false. //+kubebuilder:validation:Optional @@ -330,11 +214,6 @@ type InstanceSpec struct { // Optional, when set this environment variable will be added to all containers in the workload. //+kubebuilder:validation:Optional HostEnvName string `json:"hostEnvName,omitempty"` - - // UnixSocketPathEnvName the name of the environment variable containing the unix socket path - // Optional, when set this environment variable will be added to all containers in the workload. - //+kubebuilder:validation:Optional - UnixSocketPathEnvName string `json:"unixSocketPathEnvName,omitempty"` } // AuthProxyWorkloadStatus presents the observed state of AuthProxyWorkload using diff --git a/internal/api/v1alpha1/zz_generated.deepcopy.go b/internal/api/v1alpha1/zz_generated.deepcopy.go index 209777a7..e0340fe6 100644 --- a/internal/api/v1alpha1/zz_generated.deepcopy.go +++ b/internal/api/v1alpha1/zz_generated.deepcopy.go @@ -38,11 +38,6 @@ func (in *AuthProxyContainerSpec) DeepCopyInto(out *AuthProxyContainerSpec) { *out = new(corev1.ResourceRequirements) (*in).DeepCopyInto(*out) } - if in.Telemetry != nil { - in, out := &in.Telemetry, &out.Telemetry - *out = new(TelemetrySpec) - (*in).DeepCopyInto(*out) - } if in.MaxConnections != nil { in, out := &in.MaxConnections, &out.MaxConnections *out = new(int64) @@ -128,11 +123,6 @@ func (in *AuthProxyWorkloadList) DeepCopyObject() runtime.Object { func (in *AuthProxyWorkloadSpec) DeepCopyInto(out *AuthProxyWorkloadSpec) { *out = *in in.Workload.DeepCopyInto(&out.Workload) - if in.Authentication != nil { - in, out := &in.Authentication, &out.Authentication - *out = new(AuthenticationSpec) - **out = **in - } if in.AuthProxyContainer != nil { in, out := &in.AuthProxyContainer, &out.AuthProxyContainer *out = new(AuthProxyContainerSpec) @@ -194,21 +184,6 @@ func (in *AuthProxyWorkloadStatus) DeepCopy() *AuthProxyWorkloadStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AuthenticationSpec) DeepCopyInto(out *AuthenticationSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthenticationSpec. -func (in *AuthenticationSpec) DeepCopy() *AuthenticationSpec { - if in == nil { - return nil - } - out := new(AuthenticationSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstanceSpec) DeepCopyInto(out *InstanceSpec) { *out = *in @@ -239,66 +214,6 @@ func (in *InstanceSpec) DeepCopy() *InstanceSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TelemetrySpec) DeepCopyInto(out *TelemetrySpec) { - *out = *in - if in.QuotaProject != nil { - in, out := &in.QuotaProject, &out.QuotaProject - *out = new(string) - **out = **in - } - if in.Prometheus != nil { - in, out := &in.Prometheus, &out.Prometheus - *out = new(bool) - **out = **in - } - if in.PrometheusNamespace != nil { - in, out := &in.PrometheusNamespace, &out.PrometheusNamespace - *out = new(string) - **out = **in - } - if in.TelemetryProject != nil { - in, out := &in.TelemetryProject, &out.TelemetryProject - *out = new(string) - **out = **in - } - if in.TelemetryPrefix != nil { - in, out := &in.TelemetryPrefix, &out.TelemetryPrefix - *out = new(string) - **out = **in - } - if in.TelemetrySampleRate != nil { - in, out := &in.TelemetrySampleRate, &out.TelemetrySampleRate - *out = new(int) - **out = **in - } - if in.HTTPPort != nil { - in, out := &in.HTTPPort, &out.HTTPPort - *out = new(int32) - **out = **in - } - if in.DisableTraces != nil { - in, out := &in.DisableTraces, &out.DisableTraces - *out = new(bool) - **out = **in - } - if in.DisableMetrics != nil { - in, out := &in.DisableMetrics, &out.DisableMetrics - *out = new(bool) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TelemetrySpec. -func (in *TelemetrySpec) DeepCopy() *TelemetrySpec { - if in == nil { - return nil - } - out := new(TelemetrySpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkloadSelectorSpec) DeepCopyInto(out *WorkloadSelectorSpec) { *out = *in diff --git a/internal/controller/authproxyworkload_controller_test.go b/internal/controller/authproxyworkload_controller_test.go index a8920e8d..7e852cb8 100644 --- a/internal/controller/authproxyworkload_controller_test.go +++ b/internal/controller/authproxyworkload_controller_test.go @@ -256,7 +256,7 @@ func reconciler(p *v1alpha1.AuthProxyWorkload, cb client.Client) (*AuthProxyWork r := &AuthProxyWorkloadReconciler{ Client: cb, recentlyDeleted: &recentlyDeletedCache{}, - updater: workload.NewUpdater(), + updater: workload.NewUpdater("cloud-sql-proxy-operator/dev"), } req := ctrl.Request{ NamespacedName: types.NamespacedName{ diff --git a/internal/controller/setup.go b/internal/controller/setup.go index 1b8b1640..5b378d88 100644 --- a/internal/controller/setup.go +++ b/internal/controller/setup.go @@ -38,8 +38,8 @@ func InitScheme(scheme *runtime.Scheme) { // SetupManagers was moved out of ../main.go to here so that it can be invoked // from the testintegration tests AND from the actual operator. -func SetupManagers(mgr manager.Manager) error { - u := workload.NewUpdater() +func SetupManagers(mgr manager.Manager, userAgent string) error { + u := workload.NewUpdater(userAgent) setupLog.Info("Configuring reconcilers...") var err error diff --git a/internal/testintegration/setup.go b/internal/testintegration/setup.go index 40af0892..a684026d 100644 --- a/internal/testintegration/setup.go +++ b/internal/testintegration/setup.go @@ -143,7 +143,7 @@ func EnvTestSetup() (func(), error) { return teardownFunc, fmt.Errorf("unable to start kuberenetes envtest %v", err) } - err = controller.SetupManagers(mgr) + err = controller.SetupManagers(mgr, "cloud-sql-proxy-operator/dev") if err != nil { return teardownFunc, fmt.Errorf("unable to start kuberenetes envtest %v", err) } diff --git a/internal/workload/podspec_updates.go b/internal/workload/podspec_updates.go index 08ee1a7d..b4f1118e 100644 --- a/internal/workload/podspec_updates.go +++ b/internal/workload/podspec_updates.go @@ -34,7 +34,7 @@ import ( // package and documented here so that they appear in the godoc. These also // need to be documented in the CRD const ( - DefaultProxyImage = "gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.0.0-preview.2" + DefaultProxyImage = "gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.0.0-preview.4" // DefaultFirstPort is the first port number chose for an instance listener by the // proxy. @@ -49,12 +49,14 @@ var l = logf.Log.WithName("internal.workload") // Updater holds global state used while reconciling workloads. type Updater struct { + // userAgent is the userAgent of the operator + userAgent string } // NewUpdater creates a new instance of Updater with a supplier // that loads the default proxy impage from the public docker registry -func NewUpdater() *Updater { - return &Updater{} +func NewUpdater(userAgent string) *Updater { + return &Updater{userAgent: userAgent} } // ConfigError is an error with extra details about why an AuthProxyWorkload @@ -121,7 +123,7 @@ func (e *ConfigErrorDetail) Error() string { var defaultContainerResources = corev1.ResourceRequirements{ Requests: corev1.ResourceList{ "cpu": resource.MustParse("1.0"), - "memory": resource.MustParse("1Gi"), + "memory": resource.MustParse("2Gi"), }, } @@ -237,23 +239,6 @@ type workloadMods struct { Ports []*managedPort `json:"ports"` } -func (s *updateState) addVolumeMount(p *cloudsqlapi.AuthProxyWorkload, is *cloudsqlapi.InstanceSpec, m corev1.VolumeMount, v corev1.Volume) { - key := dbInst(p.Namespace, p.Name, is.ConnectionString) - vol := &managedVolume{ - Instance: key, - Volume: v, - VolumeMount: m, - } - - for i, mount := range s.mods.VolumeMounts { - if mount.Instance == key { - s.mods.VolumeMounts[i] = vol - return - } - } - s.mods.VolumeMounts = append(s.mods.VolumeMounts, vol) -} - func (s *updateState) addInUsePort(p int32, containerName string) { s.addPort(p, containerName, types.NamespacedName{}, "") } @@ -417,12 +402,6 @@ func (s *updateState) update(wl *PodWorkload, matches []*cloudsqlapi.AuthProxyWo for i := range podSpec.Containers { c := &podSpec.Containers[i] s.updateContainerEnv(c) - for _, mount := range s.mods.VolumeMounts { - c.VolumeMounts = append(c.VolumeMounts, mount.VolumeMount) - } - } - for _, mount := range s.mods.VolumeMounts { - podSpec.Volumes = append(podSpec.Volumes, mount.Volume) } // only return ConfigError if there were reported @@ -451,19 +430,15 @@ func (s *updateState) updateContainer(p *cloudsqlapi.AuthProxyWorkload, wl Workl var cliArgs []string // always enable http port healthchecks on 0.0.0.0 and structured logs - healthcheckPort := s.addHealthCheck(p, c) - cliArgs = append(cliArgs, - fmt.Sprintf("--http-port=%d", healthcheckPort), - "--http-address=0.0.0.0", - "--health-check", - "--structured-logs") + cliArgs = s.addHealthCheck(p, c, cliArgs) + + // add the user agent + cliArgs = append(cliArgs, fmt.Sprintf("--user-agent=%v", s.updater.userAgent)) c.Name = ContainerName(p) c.ImagePullPolicy = "IfNotPresent" cliArgs = s.applyContainerSpec(p, c, cliArgs) - cliArgs = s.applyTelemetrySpec(p, cliArgs) - cliArgs = s.applyAuthenticationSpec(p, c, cliArgs) // Instances for i := range p.Spec.Instances { @@ -471,43 +446,20 @@ func (s *updateState) updateContainer(p *cloudsqlapi.AuthProxyWorkload, wl Workl params := map[string]string{} // if it is a TCP socket - if inst.SocketType == "tcp" || - (inst.SocketType == "" && inst.UnixSocketPath == "") { - port := s.useInstancePort(p, inst) - params["port"] = fmt.Sprint(port) - if inst.HostEnvName != "" { - s.addWorkloadEnvVar(p, inst, corev1.EnvVar{ - Name: inst.HostEnvName, - Value: "127.0.0.1", - }) - } - if inst.PortEnvName != "" { - s.addWorkloadEnvVar(p, inst, corev1.EnvVar{ - Name: inst.PortEnvName, - Value: fmt.Sprint(port), - }) - } - } else { - // else if it is a unix socket - params["unix-socket"] = inst.UnixSocketPath - mountName := VolumeName(p, inst, "unix") - s.addVolumeMount(p, inst, - corev1.VolumeMount{ - Name: mountName, - ReadOnly: false, - MountPath: inst.UnixSocketPath, - }, - corev1.Volume{ - Name: mountName, - VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}, - }) - if inst.UnixSocketPathEnvName != "" { - s.addWorkloadEnvVar(p, inst, corev1.EnvVar{ - Name: inst.UnixSocketPathEnvName, - Value: inst.UnixSocketPath, - }) - } + port := s.useInstancePort(p, inst) + params["port"] = fmt.Sprint(port) + if inst.HostEnvName != "" { + s.addWorkloadEnvVar(p, inst, corev1.EnvVar{ + Name: inst.HostEnvName, + Value: "127.0.0.1", + }) + } + if inst.PortEnvName != "" { + s.addWorkloadEnvVar(p, inst, corev1.EnvVar{ + Name: inst.PortEnvName, + Value: fmt.Sprint(port), + }) } if inst.AutoIAMAuthN != nil { @@ -554,12 +506,6 @@ func (s *updateState) applyContainerSpec(p *cloudsqlapi.AuthProxyWorkload, c *co return cliArgs } - // Fuse - if p.Spec.AuthProxyContainer.FUSEDir != "" || p.Spec.AuthProxyContainer.FUSETempDir != "" { - s.addError(cloudsqlapi.ErrorCodeFUSENotSupported, "the FUSE filesystem is not yet supported", p) - // if FUSE is used, we need to use the 'buster' or 'alpine' image. - } - if p.Spec.AuthProxyContainer.Image != "" { c.Image = p.Spec.AuthProxyContainer.Image } @@ -583,42 +529,6 @@ func (s *updateState) applyContainerSpec(p *cloudsqlapi.AuthProxyWorkload, c *co return cliArgs } -// applyTelemetrySpec applies settings from cloudsqlapi.TelemetrySpec -// to the container -func (s *updateState) applyTelemetrySpec(p *cloudsqlapi.AuthProxyWorkload, cliArgs []string) []string { - if p.Spec.AuthProxyContainer == nil || p.Spec.AuthProxyContainer.Telemetry == nil { - return cliArgs - } - tel := p.Spec.AuthProxyContainer.Telemetry - - if tel.TelemetrySampleRate != nil { - cliArgs = append(cliArgs, fmt.Sprintf("--telemetry-sample-rate=%d", *tel.TelemetrySampleRate)) - } - if tel.DisableTraces != nil && *tel.DisableTraces { - cliArgs = append(cliArgs, "--disable-traces") - } - if tel.DisableMetrics != nil && *tel.DisableMetrics { - cliArgs = append(cliArgs, "--disable-metrics") - } - if tel.PrometheusNamespace != nil || (tel.Prometheus != nil && *tel.Prometheus) { - cliArgs = append(cliArgs, "--prometheus") - } - if tel.PrometheusNamespace != nil { - cliArgs = append(cliArgs, fmt.Sprintf("--prometheus-namespace=%s", *tel.PrometheusNamespace)) - } - if tel.TelemetryProject != nil { - cliArgs = append(cliArgs, fmt.Sprintf("--telemetry-project=%s", *tel.TelemetryProject)) - } - if tel.TelemetryPrefix != nil { - cliArgs = append(cliArgs, fmt.Sprintf("--telemetry-prefix=%s", *tel.TelemetryPrefix)) - } - if tel.QuotaProject != nil { - cliArgs = append(cliArgs, fmt.Sprintf("--quota-project=%s", *tel.QuotaProject)) - } - - return cliArgs -} - // updateContainerEnv applies global container state to all containers func (s *updateState) updateContainerEnv(c *corev1.Container) { for i := 0; i < len(s.mods.EnvVars); i++ { @@ -639,22 +549,10 @@ func (s *updateState) updateContainerEnv(c *corev1.Container) { } // addHealthCheck adds the health check declaration to this workload. -func (s *updateState) addHealthCheck(p *cloudsqlapi.AuthProxyWorkload, c *corev1.Container) int32 { - var port int32 - - cs := p.Spec.AuthProxyContainer - // if the TelemetrySpec.HTTPPort is explicitly set - if cs != nil && cs.Telemetry != nil && cs.Telemetry.HTTPPort != nil { - port = *cs.Telemetry.HTTPPort - if s.isPortInUse(port) { - s.addError(cloudsqlapi.ErrorCodePortConflict, - fmt.Sprintf("telemetry httpPort %d is already in use", port), p) - } - } else { - port = DefaultHealthCheckPort - for s.isPortInUse(port) { - port++ - } +func (s *updateState) addHealthCheck(_ *cloudsqlapi.AuthProxyWorkload, c *corev1.Container, cliArgs []string) []string { + port := DefaultHealthCheckPort + for s.isPortInUse(port) { + port++ } c.StartupProbe = &corev1.Probe{ @@ -678,23 +576,18 @@ func (s *updateState) addHealthCheck(p *cloudsqlapi.AuthProxyWorkload, c *corev1 }}, PeriodSeconds: 30, } - return port + cliArgs = append(cliArgs, + fmt.Sprintf("--http-port=%d", port), + "--http-address=0.0.0.0", + "--health-check", + "--structured-logs") + return cliArgs } func (s *updateState) addError(errorCode, description string, p *cloudsqlapi.AuthProxyWorkload) { s.err.add(errorCode, description, p) } -func (s *updateState) applyAuthenticationSpec(proxy *cloudsqlapi.AuthProxyWorkload, _ *corev1.Container, args []string) []string { - if proxy.Spec.Authentication == nil { - return args - } - // Authentication needs end-to-end test in place before we can check - // that it is implemented correctly. - // --credentials-file - return args -} - func (s *updateState) defaultProxyImage() string { return DefaultProxyImage } diff --git a/internal/workload/podspec_updates_test.go b/internal/workload/podspec_updates_test.go index 175b3d5a..813fc06f 100644 --- a/internal/workload/podspec_updates_test.go +++ b/internal/workload/podspec_updates_test.go @@ -23,6 +23,7 @@ import ( "github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/internal/api/v1alpha1" "github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/internal/workload" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" ) @@ -137,7 +138,7 @@ func TestUpdatePodWorkload(t *testing.T) { wantContainerName = "csql-default-" + wantsName wantsInstanceName = "project:server:db" wantsInstanceArg = fmt.Sprintf("%s?port=%d", wantsInstanceName, wantsPort) - u = workload.NewUpdater() + u = workload.NewUpdater("cloud-sql-proxy-operator/dev") ) var err error @@ -192,7 +193,7 @@ func TestUpdateWorkloadFixedPort(t *testing.T) { "DB_HOST": "127.0.0.1", "DB_PORT": strconv.Itoa(int(wantsPort)), } - u = workload.NewUpdater() + u = workload.NewUpdater("cloud-sql-proxy-operator/dev") ) // Create a pod @@ -260,7 +261,7 @@ func TestWorkloadNoPortSet(t *testing.T) { "DB_PORT": strconv.Itoa(int(wantsPort)), } ) - u := workload.NewUpdater() + u := workload.NewUpdater("cloud-sql-proxy-operator/dev") // Create a pod wl := podWorkload() @@ -314,92 +315,11 @@ func TestWorkloadNoPortSet(t *testing.T) { } -func TestWorkloadUnixVolume(t *testing.T) { - var ( - wantsInstanceName = "project:server:db" - wantsUnixDir = "/mnt/db" - wantContainerArgs = []string{ - fmt.Sprintf("%s?unix-socket=%s", wantsInstanceName, wantsUnixDir), - } - wantWorkloadEnv = map[string]string{ - "DB_SOCKET_PATH": wantsUnixDir, - } - u = workload.NewUpdater() - ) - - // Create a pod - wl := podWorkload() - wl.Pod.Spec.Containers[0].Ports = - []corev1.ContainerPort{{Name: "http", ContainerPort: 8080}} - - // Create a AuthProxyWorkload that matches the deployment - csqls := []*v1alpha1.AuthProxyWorkload{ - authProxyWorkload("instance1", []v1alpha1.InstanceSpec{{ - ConnectionString: wantsInstanceName, - UnixSocketPath: wantsUnixDir, - UnixSocketPathEnvName: "DB_SOCKET_PATH", - }}), - } - - // update the containers - err := configureProxies(u, wl, csqls) - if err != nil { - t.Fatal(err) - } - - // ensure that the new container exists - if len(wl.Pod.Spec.Containers) != 2 { - t.Fatalf("got %v, wants 1. deployment containers length", len(wl.Pod.Spec.Containers)) - } - - // test that the instancename matches the new expected instance name. - csqlContainer, err := findContainer(wl, fmt.Sprintf("csql-default-%s", csqls[0].GetName())) - if err != nil { - t.Fatal(err) - } - - // test that port cli args are set correctly - assertContainerArgsContains(t, csqlContainer.Args, wantContainerArgs) - - // Test that workload has the right env vars - for wantKey, wantValue := range wantWorkloadEnv { - gotEnvVar, err := findEnvVar(wl, "busybox", wantKey) - if err != nil { - t.Error(err) - logPodSpec(t, wl) - } else if gotEnvVar.Value != wantValue { - t.Errorf("got %v, wants %v workload env var %v", gotEnvVar, wantValue, wantKey) - - } - } - - // test that Volume exists - if want, got := 1, len(wl.Pod.Spec.Volumes); want != got { - t.Fatalf("got %v, wants %v. PodSpec.Volumes", got, want) - } - - // test that Volume mount exists on busybox - busyboxContainer, err := findContainer(wl, "busybox") - if err != nil { - t.Fatal(err) - } - if want, got := 1, len(busyboxContainer.VolumeMounts); want != got { - t.Fatalf("got %v, wants %v. Busybox Container.VolumeMounts", got, want) - } - if want, got := wantsUnixDir, busyboxContainer.VolumeMounts[0].MountPath; want != got { - t.Fatalf("got %v, wants %v. Busybox Container.VolumeMounts.MountPath", got, want) - } - if want, got := wl.Pod.Spec.Volumes[0].Name, busyboxContainer.VolumeMounts[0].Name; want != got { - t.Fatalf("got %v, wants %v. Busybox Container.VolumeMounts.MountPath", got, want) - } - -} - func TestContainerImageChanged(t *testing.T) { var ( wantsInstanceName = "project:server:db" wantImage = "custom-image:latest" - u = workload.NewUpdater() + u = workload.NewUpdater("cloud-sql-proxy-operator/dev") ) // Create a pod @@ -441,7 +361,7 @@ func TestContainerImageEmpty(t *testing.T) { var ( wantsInstanceName = "project:server:db" wantImage = workload.DefaultProxyImage - u = workload.NewUpdater() + u = workload.NewUpdater("cloud-sql-proxy-operator/dev") ) // Create a AuthProxyWorkload that matches the deployment @@ -500,7 +420,7 @@ func TestContainerReplaced(t *testing.T) { wantContainer = &corev1.Container{ Name: "sample", Image: "debian:latest", Command: []string{"/bin/bash"}, } - u = workload.NewUpdater() + u = workload.NewUpdater("cloud-sql-proxy-operator/dev") ) // Create a pod @@ -544,6 +464,52 @@ func ptr[T int | int32 | int64 | string](i T) *T { return &i } +func TestResourcesFromSpec(t *testing.T) { + var ( + wantsInstanceName = "project:server:db" + wantResources = &corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "cpu": resource.MustParse("4.0"), + "memory": resource.MustParse("4Gi"), + }, + } + + u = workload.NewUpdater("cloud-sql-proxy-operator/dev") + ) + + // Create a pod + wl := podWorkload() + wl.Pod.Spec.Containers[0].Ports = + []corev1.ContainerPort{{Name: "http", ContainerPort: 8080}} + + // Create a AuthProxyWorkload that matches the deployment + csqls := []*v1alpha1.AuthProxyWorkload{simpleAuthProxy("instance1", wantsInstanceName)} + csqls[0].Spec.AuthProxyContainer = &v1alpha1.AuthProxyContainerSpec{Resources: wantResources} + + // update the containers + err := configureProxies(u, wl, csqls) + if err != nil { + t.Fatal(err) + } + + // ensure that the new container exists + if len(wl.Pod.Spec.Containers) != 2 { + t.Fatalf("got %v, wants 1. deployment containers length", len(wl.Pod.Spec.Containers)) + } + + // test that the instancename matches the new expected instance name. + csqlContainer, err := findContainer(wl, fmt.Sprintf("csql-default-%s", csqls[0].GetName())) + if err != nil { + t.Fatal(err) + } + + // test that resources was set + if !reflect.DeepEqual(csqlContainer.Resources.Requests, wantResources.Requests) { + t.Errorf("got %v, want %v for proxy container command", csqlContainer.Resources.Requests, wantResources.Requests) + } + +} + func TestProxyCLIArgs(t *testing.T) { type testParam struct { desc string @@ -570,6 +536,8 @@ func TestProxyCLIArgs(t *testing.T) { "--structured-logs", "--health-check", fmt.Sprintf("--http-port=%d", workload.DefaultHealthCheckPort), + "--http-address=0.0.0.0", + "--user-agent=cloud-sql-proxy-operator/dev", }, }, { @@ -583,19 +551,6 @@ func TestProxyCLIArgs(t *testing.T) { }, wantProxyArgContains: []string{"hello:world:db?port=5000"}, }, - { - desc: "fuse not supported error", - proxySpec: v1alpha1.AuthProxyWorkloadSpec{ - Instances: []v1alpha1.InstanceSpec{{ - ConnectionString: "hello:world:db", - }}, - AuthProxyContainer: &v1alpha1.AuthProxyContainerSpec{ - FUSEDir: "/fuse/db", - }, - }, - wantProxyArgContains: []string{"hello:world:db?port=5000"}, - wantErrorCodes: []string{v1alpha1.ErrorCodeFUSENotSupported}, - }, { desc: "port implicitly set and increments", proxySpec: v1alpha1.AuthProxyWorkloadSpec{ @@ -666,23 +621,12 @@ func TestProxyCLIArgs(t *testing.T) { fmt.Sprintf("hello:world:two?port=%d&private-ip=false", workload.DefaultFirstPort+1)}, }, { - desc: "telemetry flags", + desc: "global flags", proxySpec: v1alpha1.AuthProxyWorkloadSpec{ AuthProxyContainer: &v1alpha1.AuthProxyContainerSpec{ SQLAdminAPIEndpoint: "https://example.com", - Telemetry: &v1alpha1.TelemetrySpec{ - PrometheusNamespace: ptr("hello"), - TelemetryPrefix: ptr("telprefix"), - TelemetryProject: ptr("telproject"), - TelemetrySampleRate: ptr(200), - HTTPPort: ptr(int32(9091)), - DisableTraces: &wantTrue, - DisableMetrics: &wantTrue, - Prometheus: &wantTrue, - QuotaProject: ptr("qp"), - }, - MaxConnections: ptr(int64(10)), - MaxSigtermDelay: ptr(int64(20)), + MaxConnections: ptr(int64(10)), + MaxSigtermDelay: ptr(int64(20)), }, Instances: []v1alpha1.InstanceSpec{{ ConnectionString: "hello:world:one", @@ -691,16 +635,6 @@ func TestProxyCLIArgs(t *testing.T) { wantProxyArgContains: []string{ fmt.Sprintf("hello:world:one?port=%d", workload.DefaultFirstPort), "--sqladmin-api-endpoint=https://example.com", - "--telemetry-sample-rate=200", - "--prometheus-namespace=hello", - "--telemetry-project=telproject", - "--telemetry-prefix=telprefix", - "--http-port=9091", - "--health-check", - "--disable-traces", - "--disable-metrics", - "--prometheus", - "--quota-project=qp", "--max-connections=10", "--max-sigterm-delay=20", }, @@ -742,7 +676,7 @@ func TestProxyCLIArgs(t *testing.T) { for i := 0; i < len(testcases); i++ { tc := &testcases[i] t.Run(tc.desc, func(t *testing.T) { - u := workload.NewUpdater() + u := workload.NewUpdater("cloud-sql-proxy-operator/dev") // Create a pod wl := &workload.PodWorkload{Pod: &corev1.Pod{ diff --git a/main.go b/main.go index 81716cb2..ffefb148 100644 --- a/main.go +++ b/main.go @@ -32,10 +32,11 @@ import ( ) var ( - scheme = k8sruntime.NewScheme() - setupLog = ctrl.Log.WithName("setup") - version = "unknown" - buildID = "unknown" + scheme = k8sruntime.NewScheme() + setupLog = ctrl.Log.WithName("setup") + version = "unknown" + buildID = "unknown" + userAgent = "cloud-sql-proxy-operator/" + version ) func init() { @@ -86,7 +87,7 @@ func main() { os.Exit(1) } - err = controller.SetupManagers(mgr) + err = controller.SetupManagers(mgr, userAgent) if err != nil { setupLog.Error(err, "unable to set up the controllers") os.Exit(1) diff --git a/tests/setup_test.go b/tests/setup_test.go index c6854b2c..ed43a87e 100644 --- a/tests/setup_test.go +++ b/tests/setup_test.go @@ -26,6 +26,7 @@ import ( "github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/internal/controller" "github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/internal/testhelpers" + "github.com/GoogleCloudPlatform/cloud-sql-proxy-operator/internal/workload" "github.com/go-logr/logr" "go.uber.org/zap/zapcore" appsv1 "k8s.io/api/apps/v1" @@ -96,7 +97,7 @@ func setupTests() (func(), error) { } // Read e2e test configuration - proxyImageURL = loadValue("PROXY_IMAGE_URL", "../bin/last-proxy-image-url.txt", "gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.0.0-preview.2") + proxyImageURL = loadValue("PROXY_IMAGE_URL", "../bin/last-proxy-image-url.txt", workload.DefaultProxyImage) operatorURL = loadValue("OPERATOR_IMAGE_URL", "../bin/last-gcloud-operator-url.txt", "operator:latest") testInfraPath := loadValue("TEST_INFRA_JSON", "", "../bin/testinfra.json") ti, err := loadTestInfra(testInfraPath) diff --git a/tools/publish-installer.sh b/tools/publish-installer.sh index d63a62bc..7e7f2b69 100755 --- a/tools/publish-installer.sh +++ b/tools/publish-installer.sh @@ -13,15 +13,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -RELEASE_PROJECT_ID="cloud-sql-connectors" -BUCKET_PATH="gs://cloud-sql-connectors/cloud-sql-proxy-operator-dev" - set -euxo SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) PROJECT_DIR=$( dirname "$SCRIPT_DIR") cd "$PROJECT_DIR" + +RELEASE_PROJECT_ID="cloud-sql-connectors" +if [[ -n "${IS_RELEASE_BUILD:-}" ]] ; then + BUCKET_PATH="gs://cloud-sql-connectors/cloud-sql-proxy-operator" +else + BUCKET_PATH="gs://cloud-sql-connectors/cloud-sql-proxy-operator-dev" +fi + ## # Release Process if [[ -n ${RELEASE_TEST_BUILD_ID:-} ]] ; then diff --git a/version.txt b/version.txt index bcab45af..6e8bf73a 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.0.3 +0.1.0