From 1513b2413a14646441dac30da9b2487094d7ba08 Mon Sep 17 00:00:00 2001 From: Damian Czaja Date: Wed, 28 Aug 2024 18:17:26 +0200 Subject: [PATCH 1/9] add e2e tests skeleton --- .../templates/deployment.yaml | 18 ++++ .../templates/gcp-credentials-secret.yaml | 11 +++ charts/cast-cloud-proxy/values.yaml | 6 ++ cmd/proxy/main.go | 92 ++++++++----------- e2e/Dockerfile | 4 + e2e/chart/.helmignore | 23 +++++ e2e/chart/Chart.yaml | 24 +++++ e2e/chart/templates/_helpers.tpl | 62 +++++++++++++ e2e/chart/templates/job.yaml | 21 +++++ e2e/chart/templates/service.yaml | 15 +++ e2e/chart/values.yaml | 5 + e2e/main.go | 15 +++ e2e/run.sh | 63 +++++++++++++ go.mod | 19 ++++ go.sum | 57 +++++++++++- internal/castai/dummy/mock_cast.go | 1 - internal/config/config.go | 57 ++++++++++++ internal/proxy/proxy_client.go | 6 +- 18 files changed, 439 insertions(+), 60 deletions(-) create mode 100644 charts/cast-cloud-proxy/templates/gcp-credentials-secret.yaml create mode 100644 e2e/Dockerfile create mode 100644 e2e/chart/.helmignore create mode 100644 e2e/chart/Chart.yaml create mode 100644 e2e/chart/templates/_helpers.tpl create mode 100644 e2e/chart/templates/job.yaml create mode 100644 e2e/chart/templates/service.yaml create mode 100644 e2e/chart/values.yaml create mode 100644 e2e/main.go create mode 100755 e2e/run.sh create mode 100644 internal/config/config.go diff --git a/charts/cast-cloud-proxy/templates/deployment.yaml b/charts/cast-cloud-proxy/templates/deployment.yaml index bc0694b..b4019fd 100644 --- a/charts/cast-cloud-proxy/templates/deployment.yaml +++ b/charts/cast-cloud-proxy/templates/deployment.yaml @@ -33,12 +33,25 @@ spec: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + - name: GRPC_ENDPOINT + value: {{ .Values.config.grpc.endpoint | quote }} + - name: GRPC_KEY + value: {{ .Values.config.grpc.key | quote }} + {{- if .Values.config.gcpCredentials }} + - name: GOOGLE_APPLICATION_CREDENTIALS2 + value: dasd + {{- end }} livenessProbe: {{- toYaml .Values.livenessProbe | nindent 12 }} readinessProbe: {{- toYaml .Values.readinessProbe | nindent 12 }} resources: {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: gcp-creds + mountPath: "/root/.config/gcloud" + readOnly: true {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} @@ -51,3 +64,8 @@ spec: tolerations: {{- toYaml . | nindent 8 }} {{- end }} + volumes: + - name: gcp-creds + secret: + secretName: {{ include "cast-cloud-proxy.fullname" . }}-gcp-creds + optional: true diff --git a/charts/cast-cloud-proxy/templates/gcp-credentials-secret.yaml b/charts/cast-cloud-proxy/templates/gcp-credentials-secret.yaml new file mode 100644 index 0000000..9fafc99 --- /dev/null +++ b/charts/cast-cloud-proxy/templates/gcp-credentials-secret.yaml @@ -0,0 +1,11 @@ +{{- if .Values.config.gcpCredentials }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "cast-cloud-proxy.fullname" . }}-gcp-creds + labels: + {{- include "cast-cloud-proxy.labels" . | nindent 4 }} +stringData: + application_default_credentials.json: | {{ .Values.config.gcpCredentials | nindent 4 }} +{{- end }} + diff --git a/charts/cast-cloud-proxy/values.yaml b/charts/cast-cloud-proxy/values.yaml index 71d1aac..bedf6b2 100644 --- a/charts/cast-cloud-proxy/values.yaml +++ b/charts/cast-cloud-proxy/values.yaml @@ -2,6 +2,12 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. +config: + gcpCredentials: "" + grpc: + endpoint: "" + key: "" + replicaCount: 1 image: diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index f1a1e50..5ef54c6 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -1,76 +1,60 @@ package main import ( - "flag" - "log" + "fmt" "net/http" - "os" - "time" + "path" + "runtime" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "github.com/castai/cloud-proxy/internal/castai/dummy" + "github.com/castai/cloud-proxy/internal/config" "github.com/castai/cloud-proxy/internal/gcpauth" - "github.com/castai/cloud-proxy/internal/localtest" "github.com/castai/cloud-proxy/internal/proxy" -) - -const ( - // TODO: Change accordingly for local testing - - projectID = "engineering-test-353509" - location = "europe-north1-a" - testCluster = "lachezar-2708" + "github.com/sirupsen/logrus" ) var ( - runSanityTests = flag.Bool("sanity-checks", false, "run sanity checks that validate auth loading and basic executor function") - runMockCastTest = flag.Bool("mockcast", true, "run a test using a mock Cast.AI server") + GitCommit = "undefined" + GitRef = "no-ref" + Version = "local" ) func main() { - flag.Parse() - - if runSanityTests != nil && *runSanityTests { - log.Println("run sanity tests is true, starting") - go func() { - localtest.RunBasicTests(projectID, location, testCluster) - localtest.RunProxyTest(projectID, location, testCluster) - }() + cfg := config.Get() + + logger := logrus.New() + logger.SetLevel(logrus.Level(cfg.Log.Level)) + logger.SetReportCaller(true) + logger.Formatter = &logrus.TextFormatter{ + CallerPrettyfier: func(f *runtime.Frame) (function string, file string) { + filename := path.Base(f.File) + return fmt.Sprintf("%s()", f.Function), fmt.Sprintf("%s:%d", filename, f.Line) + }, } - if runMockCastTest != nil && *runMockCastTest { - log.Println("run mockcast tests is true, starting") - go func() { - log.Println("Starting mock cast instance") - mockCast := &dummy.MockCast{} - if err := mockCast.Run(); err != nil { - log.Panicln("Error running mock Cast:", err) - } - }() + logger.WithFields(logrus.Fields{ + "GitCommit": GitCommit, + "GitRef": GitRef, + "Version": Version, + }).Println("Starting cloud-proxy") - go func() { - loggerClientProxy := log.New(os.Stderr, "[CLUSTER PROXY] ", log.LstdFlags) - loggerClientProxy.Println("Starting proxy client") - conn, err := grpc.NewClient("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - loggerClientProxy.Panicf("Failed to connect to server: %v", err) - } - defer func(conn *grpc.ClientConn) { - err := conn.Close() - if err != nil { - loggerClientProxy.Panicf("Failed to close gRPC connection: %v", err) - } - }(conn) - - src := gcpauth.GCPCredentialsSource{} - executor := proxy.NewExecutor(src, http.DefaultClient) - client := proxy.NewClient(executor, loggerClientProxy) - client.Run(conn) - }() + conn, err := grpc.NewClient(cfg.GRPC.Endpoint, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + logger.Panicf("Failed to connect to server: %v", err) } - log.Println("Sleeping for 1h, feel free to kill me") - time.Sleep(1 * time.Hour) + defer func(conn *grpc.ClientConn) { + err := conn.Close() + if err != nil { + logger.Panicf("Failed to close gRPC connection: %v", err) + } + }(conn) + + src := gcpauth.GCPCredentialsSource{} + + executor := proxy.NewExecutor(src, http.DefaultClient) + client := proxy.NewClient(executor, logger) + client.Run(conn) } diff --git a/e2e/Dockerfile b/e2e/Dockerfile new file mode 100644 index 0000000..fa98e3e --- /dev/null +++ b/e2e/Dockerfile @@ -0,0 +1,4 @@ +FROM gcr.io/distroless/static-debian11 +COPY bin/cloud-proxy-e2e /usr/local/bin/cloud-proxy-e2e +CMD ["cloud-proxy-e2e"] + diff --git a/e2e/chart/.helmignore b/e2e/chart/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/e2e/chart/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/e2e/chart/Chart.yaml b/e2e/chart/Chart.yaml new file mode 100644 index 0000000..973b09a --- /dev/null +++ b/e2e/chart/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: cloud-proxy-e2e +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/e2e/chart/templates/_helpers.tpl b/e2e/chart/templates/_helpers.tpl new file mode 100644 index 0000000..60d16d1 --- /dev/null +++ b/e2e/chart/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "e2e.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "e2e.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "e2e.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "e2e.labels" -}} +helm.sh/chart: {{ include "e2e.chart" . }} +{{ include "e2e.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "e2e.selectorLabels" -}} +app.kubernetes.io/name: {{ include "e2e.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "e2e.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "e2e.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/e2e/chart/templates/job.yaml b/e2e/chart/templates/job.yaml new file mode 100644 index 0000000..9926711 --- /dev/null +++ b/e2e/chart/templates/job.yaml @@ -0,0 +1,21 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "e2e.fullname" . }} + labels: + {{- include "e2e.labels" . | nindent 4}} +spec: + template: + metadata: + labels: + {{- include "e2e.selectorLabels" . | nindent 8 }} + spec: + restartPolicy: Never + containers: + - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} + imagePullPolicy: Never + name: {{ .Chart.Name }} + ports: + - containerPort: 50051 + name: grpc + diff --git a/e2e/chart/templates/service.yaml b/e2e/chart/templates/service.yaml new file mode 100644 index 0000000..dc82ef8 --- /dev/null +++ b/e2e/chart/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "e2e.fullname" . }} + labels: + {{- include "e2e.labels" . | nindent 4 }} +spec: + type: ClusterIP + ports: + - port: 50051 + targetPort: grpc + protocol: TCP + name: grpc + selector: + {{- include "e2e.selectorLabels" . | nindent 4 }} diff --git a/e2e/chart/values.yaml b/e2e/chart/values.yaml new file mode 100644 index 0000000..beb389b --- /dev/null +++ b/e2e/chart/values.yaml @@ -0,0 +1,5 @@ +image: + repository: cloud-proxy-e2e + pullPolicy: IfNotPresent + tag: "" + diff --git a/e2e/main.go b/e2e/main.go new file mode 100644 index 0000000..9d55b5b --- /dev/null +++ b/e2e/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "log" + + "github.com/castai/cloud-proxy/internal/castai/dummy" +) + +func main() { + log.Println("Starting mock cast instance") + mockCast := &dummy.MockCast{} + if err := mockCast.Run(); err != nil { + log.Panicln("Error running mock Cast:", err) + } +} diff --git a/e2e/run.sh b/e2e/run.sh new file mode 100755 index 0000000..b77eb5b --- /dev/null +++ b/e2e/run.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +set -eEu + +CLUSTER_NAME=cloud-proxy-e2e +IMAGE_TAG=$RANDOM + +kind::ensure() { + if ! kind export kubeconfig --name "${CLUSTER_NAME}"; then + kind create cluster --name "${CLUSTER_NAME}" + fi +} + +kind::load_images() { + kind load docker-image --name "${CLUSTER_NAME}" cloud-proxy:$IMAGE_TAG + kind load docker-image --name "${CLUSTER_NAME}" cloud-proxy-e2e:$IMAGE_TAG + +} + +cloud_proxy::build_image() { + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o bin/castai-cloud-proxy-amd64 ./cmd/proxy + docker build -t cloud-proxy:$IMAGE_TAG . +} + +cloud_proxy::helm_install() { + helm upgrade --install --wait \ + --set image.repository=cloud-proxy \ + --set image.tag=$IMAGE_TAG \ + --set config.grpc.endpoint=cloud-proxy-e2e:50051 \ + --set config.grpc.key=test \ + --set-file config.gcpCredentials="${GCP_CREDENTIALS}" \ + cast-cloud-proxy ./charts/cast-cloud-proxy +} + +e2e::build_image() { + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o bin/cloud-proxy-e2e ./e2e + docker build -t cloud-proxy-e2e:$IMAGE_TAG -f ./e2e/Dockerfile . +} + +e2e::helm_install() { + helm upgrade --install --wait \ + --set image.tag=$IMAGE_TAG \ + cloud-proxy-e2e ./e2e/chart +} + +e2e::helm_uninstall() { + helm delete cloud-proxy-e2e +} + +main() { + [[ -z "${GCP_CREDENTIALS:-}" ]] && echo "Missing GCP_CREDENTIALS" && exit 1 + + kind::ensure + cloud_proxy::build_image + e2e::build_image + kind::load_images + cloud_proxy::helm_install + e2e::helm_install + + #e2e::helm_uninstall +} + +main $@ diff --git a/go.mod b/go.mod index 6dcdbd4..55a1ef5 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ require ( cloud.google.com/go/container v1.39.0 github.com/google/uuid v1.6.0 github.com/googleapis/gax-go/v2 v2.13.0 + github.com/sirupsen/logrus v1.9.3 + github.com/spf13/viper v1.19.0 golang.org/x/oauth2 v0.22.0 golang.org/x/sync v0.8.0 google.golang.org/api v0.193.0 @@ -18,22 +20,39 @@ require ( cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect cloud.google.com/go/compute/metadata v0.5.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/s2a-go v0.1.8 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect go.opentelemetry.io/otel v1.28.0 // indirect go.opentelemetry.io/otel/metric v1.28.0 // indirect go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect golang.org/x/crypto v0.26.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/sys v0.24.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.6.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 43555c7..0edce92 100644 --- a/go.sum +++ b/go.sum @@ -14,14 +14,19 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -60,17 +65,54 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfF github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 h1:vS1Ao/R55RNV4O7TA2Qopok8yN+X0LIP6RVWLFkprck= @@ -85,11 +127,17 @@ go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBq go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -113,6 +161,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -157,6 +206,10 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/castai/dummy/mock_cast.go b/internal/castai/dummy/mock_cast.go index 2c8b93c..bc65111 100644 --- a/internal/castai/dummy/mock_cast.go +++ b/internal/castai/dummy/mock_cast.go @@ -19,7 +19,6 @@ import ( // - "dispatcher" that uses proxy to send requests // - "client" that does GCP cloud calls type MockCast struct { - proxyServer *MockCastServer } func (mc *MockCast) Run() error { diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..1f4f688 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,57 @@ +package config + +import ( + "fmt" + + "github.com/sirupsen/logrus" + "github.com/spf13/viper" +) + +type Config struct { + GRPC GRPC + Log Log +} + +type GRPC struct { + Endpoint string + Key string +} + +type Log struct { + Level int +} + +var cfg *Config = nil + +func Get() Config { + if cfg != nil { + return *cfg + } + + _ = viper.BindEnv("grpc.endpoint", "GRPC_ENDPOINT") + _ = viper.BindEnv("grpc.key", "GRPC_KEY") + _ = viper.BindEnv("log.level", "LOG_LEVEL") + + cfg = &Config{} + if err := viper.Unmarshal(cfg); err != nil { + panic(fmt.Errorf("while parsing config: %w", err)) + } + + if cfg.GRPC.Endpoint == "" { + required("GRPC_ENDPOINT") + } + + if cfg.GRPC.Key == "" { + required("GRPC_KEY") + } + + if cfg.Log.Level == 0 { + cfg.Log.Level = int(logrus.InfoLevel) + } + + return *cfg +} + +func required(variable string) { + panic(fmt.Errorf("variable %s is required", variable)) +} diff --git a/internal/proxy/proxy_client.go b/internal/proxy/proxy_client.go index 07b8630..f553728 100644 --- a/internal/proxy/proxy_client.go +++ b/internal/proxy/proxy_client.go @@ -3,21 +3,21 @@ package proxy import ( "context" "io" - "log" "time" "google.golang.org/grpc" "github.com/castai/cloud-proxy/internal/castai/proto" + "github.com/sirupsen/logrus" ) type Client struct { executor *Executor - logger *log.Logger + logger *logrus.Logger } -func NewClient(executor *Executor, logger *log.Logger) *Client { +func NewClient(executor *Executor, logger *logrus.Logger) *Client { return &Client{executor: executor, logger: logger} } From f5dc785da75d632469af6404ab4a930e1ff11f89 Mon Sep 17 00:00:00 2001 From: Damian Czaja Date: Wed, 28 Aug 2024 18:19:11 +0200 Subject: [PATCH 2/9] cleanup --- charts/cast-cloud-proxy/templates/deployment.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/charts/cast-cloud-proxy/templates/deployment.yaml b/charts/cast-cloud-proxy/templates/deployment.yaml index b4019fd..31ad562 100644 --- a/charts/cast-cloud-proxy/templates/deployment.yaml +++ b/charts/cast-cloud-proxy/templates/deployment.yaml @@ -38,10 +38,6 @@ spec: value: {{ .Values.config.grpc.endpoint | quote }} - name: GRPC_KEY value: {{ .Values.config.grpc.key | quote }} - {{- if .Values.config.gcpCredentials }} - - name: GOOGLE_APPLICATION_CREDENTIALS2 - value: dasd - {{- end }} livenessProbe: {{- toYaml .Values.livenessProbe | nindent 12 }} readinessProbe: From f370f6a1129245578c0b388e1c4e1aee5d11ff83 Mon Sep 17 00:00:00 2001 From: Damian Czaja Date: Fri, 30 Aug 2024 12:51:56 +0200 Subject: [PATCH 3/9] add working example --- .github/workflows/build.yaml | 28 ++-- Makefile | 3 +- e2e/chart/templates/job.yaml | 3 +- e2e/main.go | 66 +++++++- e2e/run.sh | 34 +++- e2e/setup.go | 146 ++++++++++++++++++ go.mod | 2 + go.sum | 4 + .../castai/dummy/external_provisioner_mock.go | 9 +- internal/castai/dummy/mock_cast.go | 3 +- .../{castai/dummy => e2etest}/dispatcher.go | 2 +- .../{castai/dummy => e2etest}/roundtripper.go | 2 +- internal/proxy/executor.go | 1 + 13 files changed, 275 insertions(+), 28 deletions(-) create mode 100644 e2e/setup.go rename internal/{castai/dummy => e2etest}/dispatcher.go (99%) rename internal/{castai/dummy => e2etest}/roundtripper.go (99%) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 431d9e1..c043d15 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -57,7 +57,23 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 + - name: Create k8s Kind Cluster + uses: helm/kind-action@v1 + with: + cluster_name: cloud-proxy-e2e + + - uses: azure/setup-helm@v4.2.0 + + - name: Run e2e tests + run: | + echo "$GCP_CREDENTIALS_JSON" > "$GCP_CREDENTIALS" + ./e2e/run.sh + env: + GCP_CREDENTIALS_JSON: ${{ secrets.TEST_GCP_CREDENTIALS }} + GCP_CREDENTIALS: gcp-credentials.json + - name: Login to Google Artifact Registry + if: github.event_name == 'release' uses: docker/login-action@v3 with: registry: us-docker.pkg.dev @@ -85,15 +101,3 @@ jobs: us-docker.pkg.dev/castai-hub/library/cloud-proxy:${{ env.RELEASE_TAG }} us-docker.pkg.dev/castai-hub/library/cloud-proxy:latest - #- name: Docker pull for fossa main - # if: github.event_name == 'release' - # run: docker pull us-docker.pkg.dev/castai-hub/library/cloud-proxy:${{ env.RELEASE_TAG }} - # - #- name: FOSSA scan docker image - # if: github.event_name == 'release' - # continue-on-error: true - # uses: fossas/fossa-action@v1 - # with: - # api-key: ${{ secrets.FOSSA_API_KEY }} - # container: us-docker.pkg.dev/castai-hub/library/cloud-proxy:${{ env.RELEASE_TAG }} - diff --git a/Makefile b/Makefile index 19effdf..5569227 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,11 @@ VERSION ?= poc2 -REPO ?= lachezarcast/cloud-proxy +REPO ?= trojan295/cloud-proxy build: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o bin/castai-cloud-proxy-amd64 ./cmd/proxy #docker build -t us-docker.pkg.dev/castai-hub/library/svc:$(VERSION) . docker build -t $(REPO):$(VERSION) --platform linux/amd64 . +.PHONY: build push: docker push $(REPO):$(VERSION) diff --git a/e2e/chart/templates/job.yaml b/e2e/chart/templates/job.yaml index 9926711..54abbbf 100644 --- a/e2e/chart/templates/job.yaml +++ b/e2e/chart/templates/job.yaml @@ -5,6 +5,7 @@ metadata: labels: {{- include "e2e.labels" . | nindent 4}} spec: + backoffLimit: 0 template: metadata: labels: @@ -13,7 +14,7 @@ spec: restartPolicy: Never containers: - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} - imagePullPolicy: Never + imagePullPolicy: {{ .Values.image.pullPolicy }} name: {{ .Chart.Name }} ports: - containerPort: 50051 diff --git a/e2e/main.go b/e2e/main.go index 9d55b5b..cfbc1f1 100644 --- a/e2e/main.go +++ b/e2e/main.go @@ -1,15 +1,71 @@ package main import ( + "context" + "fmt" "log" + "net/http" + "os" - "github.com/castai/cloud-proxy/internal/castai/dummy" + "cloud.google.com/go/compute/apiv1/computepb" + "github.com/castai/cloud-proxy/internal/castai/proto" + "google.golang.org/api/iterator" + "google.golang.org/grpc" ) +func TestBasic(ctx context.Context, server Server, rt http.RoundTripper) error { + if err := server.StartServer(); err != nil { + return err + } + defer server.StopServer() + + zonesClient, err := GetZonesClient(ctx, rt) + if err != nil { + return err + } + + it := zonesClient.List(ctx, &computepb.ListZonesRequest{ + Project: "engineering-test-353509", + }) + + zones := make([]*computepb.Zone, 0) + for { + zone, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + return err + } + + zones = append(zones, zone) + } + + if len(zones) < 1 { + return fmt.Errorf("no zones") + } + + return nil +} + func main() { - log.Println("Starting mock cast instance") - mockCast := &dummy.MockCast{} - if err := mockCast.Run(); err != nil { - log.Panicln("Error running mock Cast:", err) + ctx := context.Background() + + logger := log.New(os.Stderr, "[CLOUD-PROXY-E2E] ", log.LstdFlags) + + grpcServer := grpc.NewServer() + setup := NewTestSetup(grpcServer, logger) + proto.RegisterGCPProxyServerServer(grpcServer, setup) + + tt := map[string]func(ctx context.Context, server Server, rt http.RoundTripper) error{ + "basic test": TestBasic, + } + + for name, testFunc := range tt { + setup.ExecuteTest(ctx, name, testFunc) + } + + if !setup.result { + os.Exit(1) } } diff --git a/e2e/run.sh b/e2e/run.sh index b77eb5b..0256063 100755 --- a/e2e/run.sh +++ b/e2e/run.sh @@ -1,10 +1,15 @@ -#!/bin/bash +#!/usr/bin/env bash set -eEu CLUSTER_NAME=cloud-proxy-e2e IMAGE_TAG=$RANDOM +shout() { + echo "============= $(date) ===============" + echo "= $1" + echo "===========================================================" +} kind::ensure() { if ! kind export kubeconfig --name "${CLUSTER_NAME}"; then kind create cluster --name "${CLUSTER_NAME}" @@ -47,6 +52,14 @@ e2e::helm_uninstall() { helm delete cloud-proxy-e2e } +e2e::failure() { + shout "e2e logs" + kubectl logs jobs/cloud-proxy-e2e + + shout "cloud-proxy logs" + kubectl logs deployment/cast-cloud-proxy +} + main() { [[ -z "${GCP_CREDENTIALS:-}" ]] && echo "Missing GCP_CREDENTIALS" && exit 1 @@ -57,7 +70,24 @@ main() { cloud_proxy::helm_install e2e::helm_install - #e2e::helm_uninstall + kubectl wait jobs cloud-proxy-e2e --for condition=Complete --timeout=120s & + local -r complete_pid="$!" + + kubectl wait jobs cloud-proxy-e2e --for condition=Failed --timeout=120s && exit 1 & + local -r failed_pid="$!" + + if wait -n "$complete_pid" "$failed_pid"; then + local -r exit_code=0 + shout "TESTS PASSED!" + else + local -r exit_code=1 + shout "TESTS FAILED!" + e2e::failure + fi + + e2e::helm_uninstall + + exit $exit_code } main $@ diff --git a/e2e/setup.go b/e2e/setup.go new file mode 100644 index 0000000..b43293f --- /dev/null +++ b/e2e/setup.go @@ -0,0 +1,146 @@ +package main + +import ( + "context" + "fmt" + "io" + "log" + "net" + "net/http" + + compute "cloud.google.com/go/compute/apiv1" + container "cloud.google.com/go/container/apiv1" + "github.com/castai/cloud-proxy/internal/castai/proto" + "github.com/castai/cloud-proxy/internal/e2etest" + "golang.org/x/sync/errgroup" + "google.golang.org/api/option" + "google.golang.org/grpc" +) + +type Server interface { + StartServer() error + StopServer() + GracefulStopServer() +} + +type TestSetup struct { + result bool + + proto.UnimplementedGCPProxyServerServer + + grpcServer *grpc.Server + dispatcher *e2etest.Dispatcher + roundTripper *e2etest.HttpOverGrpcRoundTripper + + requestChan chan *proto.HttpRequest + responseChan chan *proto.HttpResponse + + logger *log.Logger +} + +func NewTestSetup(grpcSrv *grpc.Server, logger *log.Logger) *TestSetup { + requestChan, respChan := make(chan *proto.HttpRequest), make(chan *proto.HttpResponse) + dispatcher := e2etest.NewDispatcher(requestChan, respChan, logger) + roundTrip := e2etest.NewHttpOverGrpcRoundTripper(dispatcher, logger) + + dispatcher.Run() + + return &TestSetup{ + result: true, + grpcServer: grpcSrv, + dispatcher: dispatcher, + roundTripper: roundTrip, + requestChan: requestChan, + responseChan: respChan, + logger: logger, + } +} + +func (srv *TestSetup) StartServer() error { + list, err := net.Listen("tcp", "0.0.0.0:50051") + if err != nil { + return fmt.Errorf("listening: %w", err) + } + + go func() { + if err := srv.grpcServer.Serve(list); err != nil { + srv.logger.Printf("when serving grpc: %v", err) + } + }() + + return nil +} + +func (srv *TestSetup) StopServer() { + srv.grpcServer.Stop() +} + +func (srv *TestSetup) GracefulStopServer() { + srv.grpcServer.Stop() +} + +func (srv *TestSetup) Proxy(stream proto.GCPProxyServer_ProxyServer) error { + srv.logger.Println("Received a proxy connection from client") + + var eg errgroup.Group + + eg.Go(func() error { + srv.logger.Println("Starting request sender") + + for req := range srv.requestChan { + srv.logger.Println("Sending request to cluster proxy client") + + if err := stream.Send(req); err != nil { + srv.logger.Printf("Error sending request: %v\n", err) + } + } + return nil + }) + + eg.Go(func() error { + srv.logger.Println("Starting response receiver") + + for { + in, err := stream.Recv() + if err == io.EOF { + fmt.Println("stream was closed by client") + return err + } + if err != nil { + srv.logger.Printf("Error in response receiver: %v\n", err) + return err + } + + srv.logger.Printf("Got a response from client: %v, %v\n", in.RequestID, in.Status) + srv.responseChan <- in + } + }) + + return eg.Wait() +} + +func (srv *TestSetup) ExecuteTest(ctx context.Context, name string, f func(ctx context.Context, server Server, rt http.RoundTripper) error) { + if err := f(ctx, srv, srv.roundTripper); err != nil { + srv.logger.Printf("TEST FAILED: %s", name) + srv.logger.Printf("error: %v", err) + srv.result = false + } else { + srv.logger.Printf("TEST PASSED: %s", name) + } +} + +func GetContainerClient(ctx context.Context, rt http.RoundTripper) (*container.ClusterManagerClient, error) { + httpClient := &http.Client{} + httpClient.Transport = rt + return container.NewClusterManagerRESTClient(ctx, + option.WithoutAuthentication(), + option.WithHTTPClient(httpClient)) +} + +func GetZonesClient(ctx context.Context, rt http.RoundTripper) (*compute.ZonesClient, error) { + httpClient := &http.Client{} + httpClient.Transport = rt + return compute.NewZonesRESTClient(ctx, + option.WithoutAuthentication(), + option.WithHTTPClient(httpClient)) +} diff --git a/go.mod b/go.mod index 55a1ef5..ee720b8 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/castai/cloud-proxy go 1.22 require ( + cloud.google.com/go/compute v1.27.4 cloud.google.com/go/container v1.39.0 github.com/google/uuid v1.6.0 github.com/googleapis/gax-go/v2 v2.13.0 @@ -51,6 +52,7 @@ require ( golang.org/x/sys v0.24.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.6.0 // indirect + google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 0edce92..bb165ed 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ cloud.google.com/go/auth v0.9.0 h1:cYhKl1JUhynmxjXfrk4qdPc6Amw7i+GC9VLflgT0p5M= cloud.google.com/go/auth v0.9.0/go.mod h1:2HsApZBr9zGZhC9QAXsYVYaWk8kNUt37uny+XVKi7wM= cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= +cloud.google.com/go/compute v1.27.4 h1:XM8ulx6crjdl09XBfji7viFgZOEQuIxBwKmjRH9Rtmc= +cloud.google.com/go/compute v1.27.4/go.mod h1:7JZS+h21ERAGHOy5qb7+EPyXlQwzshzrx1x6L9JhTqU= cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/container v1.39.0 h1:Q1oW01ENxkkG3uf1oYoTmHPdvP+yhFCIuCJ4mk2RwkQ= @@ -183,6 +185,8 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142 h1:oLiyxGgE+rt22duwci1+TG7bg2/L1LQsXwfjPlmuJA0= +google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142/go.mod h1:G11eXq53iI5Q+kyNOmCvnzBaxEA2Q/Ik5Tj7nqBE8j4= google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= diff --git a/internal/castai/dummy/external_provisioner_mock.go b/internal/castai/dummy/external_provisioner_mock.go index 9b71dcf..2e4cf05 100644 --- a/internal/castai/dummy/external_provisioner_mock.go +++ b/internal/castai/dummy/external_provisioner_mock.go @@ -9,14 +9,15 @@ import ( container "cloud.google.com/go/container/apiv1" "cloud.google.com/go/container/apiv1/containerpb" + "github.com/castai/cloud-proxy/internal/e2etest" "google.golang.org/api/option" htransport "google.golang.org/api/transport/http" ) const ( projectID = "engineering-test-353509" - location = "europe-north1-a" - testCluster = "lachezar-2708" + location = "europe-central2" + testCluster = "damianc" ) type mockEP struct { @@ -24,12 +25,12 @@ type mockEP struct { logger *log.Logger } -func newMockEP(dispatcher *Dispatcher, logger *log.Logger) (*mockEP, error) { +func newMockEP(dispatcher *e2etest.Dispatcher, logger *log.Logger) (*mockEP, error) { httpClient, _, err := htransport.NewClient(context.Background(), option.WithoutAuthentication()) if err != nil { return nil, err } - httpClient.Transport = NewHttpOverGrpcRoundTripper(dispatcher, logger) + httpClient.Transport = e2etest.NewHttpOverGrpcRoundTripper(dispatcher, logger) gkeProxiedClient, err := container.NewClusterManagerRESTClient( context.Background(), option.WithoutAuthentication(), diff --git a/internal/castai/dummy/mock_cast.go b/internal/castai/dummy/mock_cast.go index bc65111..cf0ac59 100644 --- a/internal/castai/dummy/mock_cast.go +++ b/internal/castai/dummy/mock_cast.go @@ -12,6 +12,7 @@ import ( "google.golang.org/grpc" "github.com/castai/cloud-proxy/internal/castai/proto" + "github.com/castai/cloud-proxy/internal/e2etest" ) // MockCast simulates what cast would do but runs it in the same process: @@ -35,7 +36,7 @@ func (mc *MockCast) Run() error { grpcServer := grpc.NewServer() proto.RegisterGCPProxyServerServer(grpcServer, NewMockCastServer(requestChan, respChan, logger)) - dispatcher := NewDispatcher(requestChan, respChan, logger) + dispatcher := e2etest.NewDispatcher(requestChan, respChan, logger) epMock, err := newMockEP(dispatcher, logger) if err != nil { diff --git a/internal/castai/dummy/dispatcher.go b/internal/e2etest/dispatcher.go similarity index 99% rename from internal/castai/dummy/dispatcher.go rename to internal/e2etest/dispatcher.go index 12bc8c2..e51ab84 100644 --- a/internal/castai/dummy/dispatcher.go +++ b/internal/e2etest/dispatcher.go @@ -1,4 +1,4 @@ -package dummy +package e2etest import ( "log" diff --git a/internal/castai/dummy/roundtripper.go b/internal/e2etest/roundtripper.go similarity index 99% rename from internal/castai/dummy/roundtripper.go rename to internal/e2etest/roundtripper.go index 71eb8a0..ef164cd 100644 --- a/internal/castai/dummy/roundtripper.go +++ b/internal/e2etest/roundtripper.go @@ -1,4 +1,4 @@ -package dummy +package e2etest import ( "bytes" diff --git a/internal/proxy/executor.go b/internal/proxy/executor.go index 2cfd557..922a9bd 100644 --- a/internal/proxy/executor.go +++ b/internal/proxy/executor.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "io" + "log" "net/http" "strings" From 90db6ecde5395967451ed1871c98a0906b7f3e49 Mon Sep 17 00:00:00 2001 From: Damian Czaja Date: Fri, 30 Aug 2024 13:00:02 +0200 Subject: [PATCH 4/9] fix --- internal/proxy/executor.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/proxy/executor.go b/internal/proxy/executor.go index 922a9bd..2cfd557 100644 --- a/internal/proxy/executor.go +++ b/internal/proxy/executor.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "io" - "log" "net/http" "strings" From bc811b857076f9d51d7d1a1465e1b31f14e84eb8 Mon Sep 17 00:00:00 2001 From: Damian Czaja Date: Fri, 30 Aug 2024 14:58:01 +0200 Subject: [PATCH 5/9] add scope --- internal/gcpauth/auth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/gcpauth/auth.go b/internal/gcpauth/auth.go index dd3dd67..421496c 100644 --- a/internal/gcpauth/auth.go +++ b/internal/gcpauth/auth.go @@ -13,7 +13,7 @@ type GCPCredentialsSource struct { // TODO: check if we should be doing it constantly; cache them; cache the token or something else func (src *GCPCredentialsSource) GetDefaultCredentials() (*google.Credentials, error) { - defaultCreds, err := google.FindDefaultCredentials(context.Background()) + defaultCreds, err := google.FindDefaultCredentials(context.Background(), "https://www.googleapis.com/auth/cloud-platform") if err != nil { return nil, fmt.Errorf("could not load default credentials: %w", err) } From 581318a8ddb5cb588261c60ceb24f772135df009 Mon Sep 17 00:00:00 2001 From: Damian Czaja Date: Fri, 30 Aug 2024 15:38:07 +0200 Subject: [PATCH 6/9] fix --- .github/workflows/build.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c043d15..220f954 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -73,7 +73,6 @@ jobs: GCP_CREDENTIALS: gcp-credentials.json - name: Login to Google Artifact Registry - if: github.event_name == 'release' uses: docker/login-action@v3 with: registry: us-docker.pkg.dev From 330b6b328e38575f4007059a0522a599690adbc6 Mon Sep 17 00:00:00 2001 From: Damian Czaja Date: Tue, 3 Sep 2024 14:42:25 +0200 Subject: [PATCH 7/9] add Terraform to deploy GKE cluster with castai-cloud-proxy --- .gitignore | 7 +- Dockerfile | 2 +- Makefile | 10 +- README.md | 19 + charts/cast-cloud-proxy/.helmignore | 23 - charts/cast-cloud-proxy/Chart.yaml | 7 - .../cast-cloud-proxy/templates/_helpers.tpl | 62 --- .../templates/deployment.yaml | 67 --- .../templates/gcp-credentials-secret.yaml | 11 - .../templates/serviceaccount.yaml | 13 - charts/cast-cloud-proxy/values.yaml | 73 --- cmd/proxy/main.go | 28 +- dummy_deploy.yaml | 10 +- e2e/main.go | 64 ++- e2e/run.sh | 7 +- e2e/setup.go | 46 +- go.mod | 2 +- hack/terraform/.terraform.lock.hcl | 141 ++++++ hack/terraform/castai.tf | 67 +++ hack/terraform/cloud-proxy.tf | 40 ++ hack/terraform/gke.tf | 33 ++ hack/terraform/terraform.tf | 17 + hack/terraform/vars.tf | 39 ++ hack/terraform/vpc.tf | 33 ++ internal/castai/dummy/mock_cast.go | 16 +- internal/castai/proto/proxy.pb.go | 416 ++++++++++++++---- internal/castai/proto/proxy.proto | 44 +- internal/castai/proto/proxy_grpc.pb.go | 82 ++-- internal/config/config.go | 6 + internal/e2etest/dispatcher.go | 22 +- internal/e2etest/roundtripper.go | 50 ++- internal/localtest/roundtripper.go | 72 --- internal/localtest/run.go | 124 ------ internal/proxy/executor.go | 59 ++- internal/proxy/proxy_client.go | 10 +- 35 files changed, 979 insertions(+), 743 deletions(-) delete mode 100644 charts/cast-cloud-proxy/.helmignore delete mode 100644 charts/cast-cloud-proxy/Chart.yaml delete mode 100644 charts/cast-cloud-proxy/templates/_helpers.tpl delete mode 100644 charts/cast-cloud-proxy/templates/deployment.yaml delete mode 100644 charts/cast-cloud-proxy/templates/gcp-credentials-secret.yaml delete mode 100644 charts/cast-cloud-proxy/templates/serviceaccount.yaml delete mode 100644 charts/cast-cloud-proxy/values.yaml create mode 100644 hack/terraform/.terraform.lock.hcl create mode 100644 hack/terraform/castai.tf create mode 100644 hack/terraform/cloud-proxy.tf create mode 100644 hack/terraform/gke.tf create mode 100644 hack/terraform/terraform.tf create mode 100644 hack/terraform/vars.tf create mode 100644 hack/terraform/vpc.tf delete mode 100644 internal/localtest/roundtripper.go delete mode 100644 internal/localtest/run.go diff --git a/.gitignore b/.gitignore index 0823176..8694b17 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ -# If you prefer the allow list template instead of the deny list, see community template: -# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore -# +.terraform +.terraform/*.json +terraform.tfstate* + # Binaries for programs and plugins *.exe *.exe~ diff --git a/Dockerfile b/Dockerfile index e3f0295..d0770d3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/distroless/static-debian11 +FROM gcr.io/distroless/static-debian11:nonroot ARG TARGETARCH="amd64" diff --git a/Makefile b/Makefile index 5569227..764bd52 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -VERSION ?= poc2 -REPO ?= trojan295/cloud-proxy +VERSION ?= latest +REPO ?= us-docker.pkg.dev/castai-hub/library/cloud-proxy build: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o bin/castai-cloud-proxy-amd64 ./cmd/proxy @@ -9,8 +9,10 @@ build: push: docker push $(REPO):$(VERSION) +.PHONY: push release: build push +.PHONY: release deploy: build push # Get the latest digest because it doesn't work for some f. reason and put it in the yaml @@ -18,7 +20,9 @@ deploy: build push sed "s/{{IMAGE_DIGEST}}/$${DIGEST}/g" dummy_deploy.yaml > tmp.yaml kubectl apply -f tmp.yaml rm tmp.yaml - +.PHONY: deploy generate-grpc: protoc --go_out=./internal/castai/proto --go-grpc_out=./internal/castai/proto ./internal/castai/proto/proxy.proto +.PHONY: generate-grpc + diff --git a/README.md b/README.md index 35c910c..4b4182a 100644 --- a/README.md +++ b/README.md @@ -50,3 +50,22 @@ gcloud projects add-iam-policy-binding projects/ \ --condition=None ``` +## Dev cloud-proxy deployment + +You can use this [Terraform module](./hack/terraform/) to create a GKE cluster, onboard to CAST AI and install the castai-cloud-proxy Helm chart. + +You might need to tweak the values in the [helm_release](./hack/terraform/cloud-proxy.tf) resource for the castai-cloud-proxy, to use the proper image and Helm chart. For now the Helm chart is not published, to you have to clone the [helm-charts repo](https://github.com/castai/helm-charts) and provide a local path in the `helm_release`. + +To deploy the Terraform module execute: + +```bash +export TF_VAR_castai_api_url=https://api-...localenv.cast.ai +export TF_VAR_castai_api_token= +export TF_VAR_castai_grpc_url=grpc-...localenv.cast.ai:443 +export TF_VAR_cluster_name= + +terraform -chdir=hack/terraform apply +``` + +The castai-cloud-proxy will try to connect the GRPC server provided in the variables. + diff --git a/charts/cast-cloud-proxy/.helmignore b/charts/cast-cloud-proxy/.helmignore deleted file mode 100644 index 0e8a0eb..0000000 --- a/charts/cast-cloud-proxy/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/charts/cast-cloud-proxy/Chart.yaml b/charts/cast-cloud-proxy/Chart.yaml deleted file mode 100644 index 3b11c9e..0000000 --- a/charts/cast-cloud-proxy/Chart.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# TODO: move to https://github.com/castai/helm-charts -apiVersion: v2 -name: cloud-proxy -description: CAST AI cloud-proxy chart -type: application -version: 0.1.0 -appVersion: "v0.0.1" diff --git a/charts/cast-cloud-proxy/templates/_helpers.tpl b/charts/cast-cloud-proxy/templates/_helpers.tpl deleted file mode 100644 index 1936ca2..0000000 --- a/charts/cast-cloud-proxy/templates/_helpers.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "cast-cloud-proxy.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "cast-cloud-proxy.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "cast-cloud-proxy.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "cast-cloud-proxy.labels" -}} -helm.sh/chart: {{ include "cast-cloud-proxy.chart" . }} -{{ include "cast-cloud-proxy.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "cast-cloud-proxy.selectorLabels" -}} -app.kubernetes.io/name: {{ include "cast-cloud-proxy.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "cast-cloud-proxy.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "cast-cloud-proxy.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/charts/cast-cloud-proxy/templates/deployment.yaml b/charts/cast-cloud-proxy/templates/deployment.yaml deleted file mode 100644 index 31ad562..0000000 --- a/charts/cast-cloud-proxy/templates/deployment.yaml +++ /dev/null @@ -1,67 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "cast-cloud-proxy.fullname" . }} - labels: - {{- include "cast-cloud-proxy.labels" . | nindent 4 }} -spec: - selector: - matchLabels: - {{- include "cast-cloud-proxy.selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "cast-cloud-proxy.labels" . | nindent 8 }} - {{- with .Values.podLabels }} - {{- toYaml . | nindent 8 }} - {{- end }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "cast-cloud-proxy.serviceAccountName" . }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - env: - - name: GRPC_ENDPOINT - value: {{ .Values.config.grpc.endpoint | quote }} - - name: GRPC_KEY - value: {{ .Values.config.grpc.key | quote }} - livenessProbe: - {{- toYaml .Values.livenessProbe | nindent 12 }} - readinessProbe: - {{- toYaml .Values.readinessProbe | nindent 12 }} - resources: - {{- toYaml .Values.resources | nindent 12 }} - volumeMounts: - - name: gcp-creds - mountPath: "/root/.config/gcloud" - readOnly: true - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} - volumes: - - name: gcp-creds - secret: - secretName: {{ include "cast-cloud-proxy.fullname" . }}-gcp-creds - optional: true diff --git a/charts/cast-cloud-proxy/templates/gcp-credentials-secret.yaml b/charts/cast-cloud-proxy/templates/gcp-credentials-secret.yaml deleted file mode 100644 index 9fafc99..0000000 --- a/charts/cast-cloud-proxy/templates/gcp-credentials-secret.yaml +++ /dev/null @@ -1,11 +0,0 @@ -{{- if .Values.config.gcpCredentials }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "cast-cloud-proxy.fullname" . }}-gcp-creds - labels: - {{- include "cast-cloud-proxy.labels" . | nindent 4 }} -stringData: - application_default_credentials.json: | {{ .Values.config.gcpCredentials | nindent 4 }} -{{- end }} - diff --git a/charts/cast-cloud-proxy/templates/serviceaccount.yaml b/charts/cast-cloud-proxy/templates/serviceaccount.yaml deleted file mode 100644 index 7c28e67..0000000 --- a/charts/cast-cloud-proxy/templates/serviceaccount.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "cast-cloud-proxy.serviceAccountName" . }} - labels: - {{- include "cast-cloud-proxy.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -automountServiceAccountToken: {{ .Values.serviceAccount.automount }} -{{- end }} diff --git a/charts/cast-cloud-proxy/values.yaml b/charts/cast-cloud-proxy/values.yaml deleted file mode 100644 index bedf6b2..0000000 --- a/charts/cast-cloud-proxy/values.yaml +++ /dev/null @@ -1,73 +0,0 @@ -# Default values for cast-cloud-proxy. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -config: - gcpCredentials: "" - grpc: - endpoint: "" - key: "" - -replicaCount: 1 - -image: - repository: us-docker.pkg.dev/castai-hub/library/cloud-proxy - pullPolicy: IfNotPresent - tag: "" - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # Automatically mount a ServiceAccount's API credentials? - automount: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - -podAnnotations: {} -podLabels: {} - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -# TODO: add probes -#livenessProbe: -# httpGet: -# path: / -# port: http -#readinessProbe: -# httpGet: -# path: / -# port: http - -nodeSelector: {} - -tolerations: [] - -affinity: {} diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index 5ef54c6..42bdddd 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -1,13 +1,18 @@ package main import ( + "context" "fmt" "net/http" "path" "runtime" + "time" "google.golang.org/grpc" + "google.golang.org/grpc/backoff" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/metadata" "github.com/castai/cloud-proxy/internal/config" "github.com/castai/cloud-proxy/internal/gcpauth" @@ -40,7 +45,22 @@ func main() { "Version": Version, }).Println("Starting cloud-proxy") - conn, err := grpc.NewClient(cfg.GRPC.Endpoint, grpc.WithTransportCredentials(insecure.NewCredentials())) + dialOpts := make([]grpc.DialOption, 0) + dialOpts = append(dialOpts, grpc.WithConnectParams(grpc.ConnectParams{ + Backoff: backoff.Config{ + BaseDelay: 2 * time.Second, + Jitter: 0.1, + MaxDelay: 5 * time.Second, + Multiplier: 1.2, + }, + })) + if cfg.GRPC.TLS.Enabled { + dialOpts = append(dialOpts, grpc.WithTransportCredentials(credentials.NewTLS(nil))) + } else { + dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) + } + + conn, err := grpc.NewClient(cfg.GRPC.Endpoint, dialOpts...) if err != nil { logger.Panicf("Failed to connect to server: %v", err) } @@ -52,9 +72,13 @@ func main() { } }(conn) + ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs( + "authorization", fmt.Sprintf("Token %s", cfg.GRPC.Key), + )) + src := gcpauth.GCPCredentialsSource{} executor := proxy.NewExecutor(src, http.DefaultClient) client := proxy.NewClient(executor, logger) - client.Run(conn) + client.Run(ctx, conn) } diff --git a/dummy_deploy.yaml b/dummy_deploy.yaml index bff636a..5dd2294 100644 --- a/dummy_deploy.yaml +++ b/dummy_deploy.yaml @@ -1,18 +1,18 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: cloud-proxy-deployment + name: castai-cloud-proxy-deployment labels: - app: cloud-proxy + app: castai-cloud-proxy spec: replicas: 1 selector: matchLabels: - app: cloud-proxy + app: castai-cloud-proxy template: metadata: labels: - app: cloud-proxy + app: castai-cloud-proxy spec: serviceAccountName: castai-cloud-proxy containers: @@ -35,4 +35,4 @@ apiVersion: v1 kind: ServiceAccount metadata: name: castai-cloud-proxy - namespace: default \ No newline at end of file + namespace: default diff --git a/e2e/main.go b/e2e/main.go index cfbc1f1..b68aa9c 100644 --- a/e2e/main.go +++ b/e2e/main.go @@ -7,45 +7,37 @@ import ( "net/http" "os" - "cloud.google.com/go/compute/apiv1/computepb" - "github.com/castai/cloud-proxy/internal/castai/proto" - "google.golang.org/api/iterator" - "google.golang.org/grpc" + "cloud.google.com/go/container/apiv1/containerpb" + "golang.org/x/sync/errgroup" ) -func TestBasic(ctx context.Context, server Server, rt http.RoundTripper) error { - if err := server.StartServer(); err != nil { - return err - } - defer server.StopServer() +const projectID = "engineering-test-353509" - zonesClient, err := GetZonesClient(ctx, rt) - if err != nil { +func TestParallelCalls(ctx context.Context, server Server, rt http.RoundTripper) error { + if err := server.StartServer(); err != nil { return err } - - it := zonesClient.List(ctx, &computepb.ListZonesRequest{ - Project: "engineering-test-353509", - }) - - zones := make([]*computepb.Zone, 0) - for { - zone, err := it.Next() - if err == iterator.Done { - break - } - if err != nil { - return err - } - - zones = append(zones, zone) - } - - if len(zones) < 1 { - return fmt.Errorf("no zones") + defer server.GracefulStopServer() + + eg := &errgroup.Group{} + for i := 0; i < 3; i++ { + eg.Go(func() error { + contClient, err := GetContainerClient(ctx, rt) + if err != nil { + return err + } + + _, err = contClient.ListClusters(ctx, &containerpb.ListClustersRequest{ + Parent: fmt.Sprintf("projects/%s/locations/-", projectID), + }) + if err != nil { + return err + } + + return nil + }) } - - return nil + return eg.Wait() } func main() { @@ -53,12 +45,10 @@ func main() { logger := log.New(os.Stderr, "[CLOUD-PROXY-E2E] ", log.LstdFlags) - grpcServer := grpc.NewServer() - setup := NewTestSetup(grpcServer, logger) - proto.RegisterGCPProxyServerServer(grpcServer, setup) + setup := NewTestSetup(logger) tt := map[string]func(ctx context.Context, server Server, rt http.RoundTripper) error{ - "basic test": TestBasic, + "basic test": TestParallelCalls, } for name, testFunc := range tt { diff --git a/e2e/run.sh b/e2e/run.sh index 0256063..4e4892c 100755 --- a/e2e/run.sh +++ b/e2e/run.sh @@ -32,9 +32,10 @@ cloud_proxy::helm_install() { --set image.repository=cloud-proxy \ --set image.tag=$IMAGE_TAG \ --set config.grpc.endpoint=cloud-proxy-e2e:50051 \ - --set config.grpc.key=test \ + --set config.grpc.key=dummytoken \ + --set config.grpc.tls.enabled=false \ --set-file config.gcpCredentials="${GCP_CREDENTIALS}" \ - cast-cloud-proxy ./charts/cast-cloud-proxy + castai-cloud-proxy ../github-helm-charts/charts/castai-cloud-proxy } e2e::build_image() { @@ -57,7 +58,7 @@ e2e::failure() { kubectl logs jobs/cloud-proxy-e2e shout "cloud-proxy logs" - kubectl logs deployment/cast-cloud-proxy + kubectl logs deployment/castai-cloud-proxy } main() { diff --git a/e2e/setup.go b/e2e/setup.go index b43293f..ed84a11 100644 --- a/e2e/setup.go +++ b/e2e/setup.go @@ -24,22 +24,22 @@ type Server interface { } type TestSetup struct { - result bool + proto.UnimplementedCloudProxyAPIServer - proto.UnimplementedGCPProxyServerServer + result bool grpcServer *grpc.Server dispatcher *e2etest.Dispatcher roundTripper *e2etest.HttpOverGrpcRoundTripper - requestChan chan *proto.HttpRequest - responseChan chan *proto.HttpResponse + requestChan chan *proto.StreamCloudProxyResponse + responseChan chan *proto.StreamCloudProxyRequest logger *log.Logger } -func NewTestSetup(grpcSrv *grpc.Server, logger *log.Logger) *TestSetup { - requestChan, respChan := make(chan *proto.HttpRequest), make(chan *proto.HttpResponse) +func NewTestSetup(logger *log.Logger) *TestSetup { + requestChan, respChan := make(chan *proto.StreamCloudProxyResponse), make(chan *proto.StreamCloudProxyRequest) dispatcher := e2etest.NewDispatcher(requestChan, respChan, logger) roundTrip := e2etest.NewHttpOverGrpcRoundTripper(dispatcher, logger) @@ -47,7 +47,6 @@ func NewTestSetup(grpcSrv *grpc.Server, logger *log.Logger) *TestSetup { return &TestSetup{ result: true, - grpcServer: grpcSrv, dispatcher: dispatcher, roundTripper: roundTrip, requestChan: requestChan, @@ -62,6 +61,9 @@ func (srv *TestSetup) StartServer() error { return fmt.Errorf("listening: %w", err) } + srv.grpcServer = grpc.NewServer() + proto.RegisterCloudProxyAPIServer(srv.grpcServer, srv) + go func() { if err := srv.grpcServer.Serve(list); err != nil { srv.logger.Printf("when serving grpc: %v", err) @@ -79,22 +81,36 @@ func (srv *TestSetup) GracefulStopServer() { srv.grpcServer.Stop() } -func (srv *TestSetup) Proxy(stream proto.GCPProxyServer_ProxyServer) error { +func (srv *TestSetup) StreamCloudProxy(stream proto.CloudProxyAPI_StreamCloudProxyServer) error { srv.logger.Println("Received a proxy connection from client") + //md, ok := metadata.FromIncomingContext(stream.Context()) + //if !ok { + // return fmt.Errorf("missing metadata") + //} + //if token := md.Get("authorization"); token[0] != "Token dummytoken" { + // fmt.Println(token) + // return fmt.Errorf("wrong authentication token") + //} + var eg errgroup.Group eg.Go(func() error { srv.logger.Println("Starting request sender") - for req := range srv.requestChan { - srv.logger.Println("Sending request to cluster proxy client") - - if err := stream.Send(req); err != nil { - srv.logger.Printf("Error sending request: %v\n", err) + for { + select { + case req := <-srv.requestChan: + srv.logger.Println("Sending request to cluster proxy client") + + if err := stream.Send(req); err != nil { + srv.logger.Printf("Error sending request: %v\n", err) + } + case <-stream.Context().Done(): + srv.logger.Printf("stream closed, stopping sending responses") + return nil } } - return nil }) eg.Go(func() error { @@ -111,7 +127,7 @@ func (srv *TestSetup) Proxy(stream proto.GCPProxyServer_ProxyServer) error { return err } - srv.logger.Printf("Got a response from client: %v, %v\n", in.RequestID, in.Status) + srv.logger.Printf("Got a response from client: %v, %v\n", in.MessageId, in.HttpResponse.Status) srv.responseChan <- in } }) diff --git a/go.mod b/go.mod index ee720b8..0f453ac 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( cloud.google.com/go/compute v1.27.4 cloud.google.com/go/container v1.39.0 github.com/google/uuid v1.6.0 - github.com/googleapis/gax-go/v2 v2.13.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/viper v1.19.0 golang.org/x/oauth2 v0.22.0 @@ -27,6 +26,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/s2a-go v0.1.8 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.13.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect diff --git a/hack/terraform/.terraform.lock.hcl b/hack/terraform/.terraform.lock.hcl new file mode 100644 index 0000000..b65040e --- /dev/null +++ b/hack/terraform/.terraform.lock.hcl @@ -0,0 +1,141 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/castai/castai" { + version = "7.13.2" + constraints = ">= 5.1.0, ~> 7.11" + hashes = [ + "h1:HZJN4r+j4HgDrhYFn0Sp9PYhsOBag8kS/B9bsYP7SVo=", + "zh:09fcf4cafa6e1ab30607c76f8959dffb1586bc7a7c2513863c9dfce42f0e6675", + "zh:1920643c1cfb1decb53d8370f33580e93382a0cc599809a11df9137fb392648d", + "zh:1f9ebba7bd6204ecdc5383cad8fd7711cc48a6dd632cce2bbf05db783940b518", + "zh:34b1d3a0e78b0e1661bcfef681f880204157c4d54c60e57a5081b1b9607fd9f1", + "zh:3ab0c47e65ad49cfe16fc787abf250362b4069fd49594c66f96fd1cccb06d7a9", + "zh:421214c2d9cf6a65e4c036b88c0a2570fa86eb10d73f26a2d2e7d6165a5c5ffb", + "zh:51bb557b9fcce75a829be1351892fd6469456eaec5686c31aafab77042249e18", + "zh:8c0fdd2282cf789cfb52c8e2ebc90189de58d971a563764074bd976f815a33c1", + "zh:9b9f66592b7605ac888cf3176baf12b90ef0344367a139fe85a562cb6d4d8432", + "zh:aa5f3563a5ab2953e15c5ca459f228195dfc2c83e99d8a9746c4eade2c14561b", + "zh:cfa1bf6e3cfcef5103cb578b24ba104026fb84ea9a01e8b0bab337f346a2f1fc", + "zh:d18b6a73ac7c00c76c8f5d139fe6742d6386291ef979066cacec927321c1c4f2", + "zh:d22f0cab874893293afc6d5154e7beca7c1299071c7e77c5a2fbb02d34bc783f", + ] +} + +provider "registry.terraform.io/hashicorp/google" { + version = "4.85.0" + constraints = ">= 2.15.0, >= 2.49.0, >= 3.33.0, >= 3.45.0, >= 3.83.0, >= 4.36.0, < 5.0.0" + hashes = [ + "h1:ZVDZuhYSIWhCkSuDkwFeSIJjn0/DcCxak2W/cHW4OQQ=", + "zh:17d60a6a6c1741cf1e09ac6731433a30950285eac88236e623ab4cbf23832ca3", + "zh:1c70254c016439dbb75cab646b4beace6ceeff117c75d81f2cc27d41c312f752", + "zh:35e2aa2cc7ac84ce55e05bb4de7b461b169d3582e56d3262e249ff09d64fe008", + "zh:417afb08d7b2744429f6b76806f4134d62b0354acf98e8a6c00de3c24f2bb6ad", + "zh:622165d09d21d9a922c86f1fc7177a400507f2a8c4a4513114407ae04da2dd29", + "zh:7cdb8e39a8ea0939558d87d2cb6caceded9e21f21003d9e9f9ce648d5db0bc3a", + "zh:851e737dc551d6004a860a8907fda65118fc2c7ede9fa828f7be704a2a39e68f", + "zh:a331ad289a02a2c4473572a573dc389be0a604cdd9e03dd8dbc10297fb14f14d", + "zh:b67fd531251380decd8dd1f849460d60f329f89df3d15f5815849a1dd001f430", + "zh:be8785957acca4f97aa3e800b313b57d1fca07788761c8867c9bc701fbe0bdb5", + "zh:cb6579a259fe020e1f88217d8f6937b2d5ace15b6406370977a1966eb31b1ca5", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} + +provider "registry.terraform.io/hashicorp/google-beta" { + version = "4.85.0" + constraints = ">= 3.45.0, < 5.0.0" + hashes = [ + "h1:CSE8cBR85sVefrE2Kg6KT5vOb7855t158oMo+iDfL0M=", + "zh:40e9c7ec46955b4d79065a14185043a4ad6af8d0246715853fc5c99208b66980", + "zh:5950a9ba2f96420ea5335b543e315b1a47a705f9a9abfc53c6fec52d084eddcb", + "zh:5dfa98d32246a5d97e018f2b91b0e921cc6f061bc8591884f3b144f0d62f1c20", + "zh:628d0ca35c6d4c35077859bb0a5534c1de44f23a91e190f9c3f06f2358172e75", + "zh:6e78d54fd4de4151968149b4c3521f563a8b5c55aad423dba5968a9114b65ae4", + "zh:91c3bc443188638353285bd35b06d3a3b39b42b3b4cc0637599a430438fba2f7", + "zh:9e91b03363ebf39eea5ec0fbe7675f6979883aa9ad9a36664357d8513a007cf3", + "zh:db9a8d6bfe075fb38c260986ab557d40e8d18e5698c62956a6da8120fae01d59", + "zh:e41169c49f3bb53217905509e2ba8bb4680c373e1f54db7fac1b7f72943a1004", + "zh:f32f55a8af605afbc940814e17493ac83d9d66cd6da9bbc247e0a833a0aa37ec", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + "zh:f6561a6badc3af842f9ad5bb926104954047f07cb90fadcca1357441cc67d91d", + ] +} + +provider "registry.terraform.io/hashicorp/helm" { + version = "2.15.0" + constraints = ">= 2.0.0" + hashes = [ + "h1:VymvscRkDy0+zN2uKpKYY6njXPY8JROARuaL3VPsEos=", + "zh:18b94c7c83c30ad166722a61a412e3de6a67935772960e79aaa24c15f8ea0d0f", + "zh:4f07c929a71e8169f7471b7600bfcca36dfb295787e975e82ac0455a3ab68b47", + "zh:776b804a14c3c4ae6075b12176f81c1f1987214ee1cae4a542599389591cde11", + "zh:7c11e3adbe9bd26e88484dcdbd28c473ce3a5c58950a3e3c4f0a2caee225b845", + "zh:821e1a53415df0ae4ed523f098360d367a95d6ce3872ba841f22adfdd2f97664", + "zh:94c06e483f75a11c3f139c41b3f64b51a96d1d1485e7d1fd3c0f795e2e750945", + "zh:aa2040de0b8150ef40222a965445ec40e3df2997ffde1fb062ab4c226689115e", + "zh:ad73eebeffe20228656567963477d034b9ed3d1bd2075c1c81150def4927d810", + "zh:b77450a36807f3ad1d3ae736d1d165a94fa26f476504a280e9fb2ccb89f648d0", + "zh:d2ebd3c34c50c92106ce2df25d5598f47127dc7c60172b9e2fe56ac73dc863a8", + "zh:e565995e2614df5ddde75a743a674129288fb91669596a7b0b2580fa7ed49979", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} + +provider "registry.terraform.io/hashicorp/kubernetes" { + version = "2.32.0" + constraints = "~> 2.10" + hashes = [ + "h1:3j4XBR5UWQA7xXaiEnzZp0bHbcwOhWetHYKTWIrUTI0=", + "zh:0e715d7fb13a8ad569a5fdc937b488590633f6942e986196fdb17cd7b8f7720e", + "zh:495fc23acfe508ed981e60af9a3758218b0967993065e10a297fdbc210874974", + "zh:4b930a8619910ef528bc90dae739cb4236b9b76ce41367281e3bc3cf586101c7", + "zh:5344405fde7b1febf0734052052268ee24e7220818155702907d9ece1c0697c7", + "zh:92ee11e8c23bbac3536df7b124456407f35c6c2468bc0dbab15c3fc9f414bd0e", + "zh:a45488fe8d5bb59c49380f398da5d109a4ac02ebc10824567dabb87f6102fda8", + "zh:a4a0b57cf719a4c91f642436882b7bea24d659c08a5b6f4214ce4fe6a0204caa", + "zh:b7a27a6d11ba956a2d7b0f7389a46ec857ebe46ae3aeee537250e66cac15bf03", + "zh:bf94ce389028b686bfa70a90f536e81bb776c5c20ab70138bbe5c3d0a04c4253", + "zh:d965b2608da0212e26a65a0b3f33c5baae46cbe839196be15d93f70061516908", + "zh:f441fc793d03057a17af8bdca8b26d54916645bc5c148f54e22a54ed39089e83", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} + +provider "registry.terraform.io/hashicorp/null" { + version = "3.2.2" + hashes = [ + "h1:IMVAUHKoydFrlPrl9OzasDnw/8ntZFerCC9iXw1rXQY=", + "zh:3248aae6a2198f3ec8394218d05bd5e42be59f43a3a7c0b71c66ec0df08b69e7", + "zh:32b1aaa1c3013d33c245493f4a65465eab9436b454d250102729321a44c8ab9a", + "zh:38eff7e470acb48f66380a73a5c7cdd76cc9b9c9ba9a7249c7991488abe22fe3", + "zh:4c2f1faee67af104f5f9e711c4574ff4d298afaa8a420680b0cb55d7bbc65606", + "zh:544b33b757c0b954dbb87db83a5ad921edd61f02f1dc86c6186a5ea86465b546", + "zh:696cf785090e1e8cf1587499516b0494f47413b43cb99877ad97f5d0de3dc539", + "zh:6e301f34757b5d265ae44467d95306d61bef5e41930be1365f5a8dcf80f59452", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:913a929070c819e59e94bb37a2a253c228f83921136ff4a7aa1a178c7cce5422", + "zh:aa9015926cd152425dbf86d1abdbc74bfe0e1ba3d26b3db35051d7b9ca9f72ae", + "zh:bb04798b016e1e1d49bcc76d62c53b56c88c63d6f2dfe38821afef17c416a0e1", + "zh:c23084e1b23577de22603cff752e59128d83cfecc2e6819edadd8cf7a10af11e", + ] +} + +provider "registry.terraform.io/hashicorp/random" { + version = "3.6.2" + hashes = [ + "h1:VavG5unYCa3SYISMKF9pzc3718M0bhPlcbUZZGl7wuo=", + "zh:0ef01a4f81147b32c1bea3429974d4d104bbc4be2ba3cfa667031a8183ef88ec", + "zh:1bcd2d8161e89e39886119965ef0f37fcce2da9c1aca34263dd3002ba05fcb53", + "zh:37c75d15e9514556a5f4ed02e1548aaa95c0ecd6ff9af1119ac905144c70c114", + "zh:4210550a767226976bc7e57d988b9ce48f4411fa8a60cd74a6b246baf7589dad", + "zh:562007382520cd4baa7320f35e1370ffe84e46ed4e2071fdc7e4b1a9b1f8ae9b", + "zh:5efb9da90f665e43f22c2e13e0ce48e86cae2d960aaf1abf721b497f32025916", + "zh:6f71257a6b1218d02a573fc9bff0657410404fb2ef23bc66ae8cd968f98d5ff6", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:9647e18f221380a85f2f0ab387c68fdafd58af6193a932417299cdcae4710150", + "zh:bb6297ce412c3c2fa9fec726114e5e0508dd2638cad6a0cb433194930c97a544", + "zh:f83e925ed73ff8a5ef6e3608ad9225baa5376446349572c2449c0c0b3cf184b7", + "zh:fbef0781cb64de76b1df1ca11078aecba7800d82fd4a956302734999cfd9a4af", + ] +} diff --git a/hack/terraform/castai.tf b/hack/terraform/castai.tf new file mode 100644 index 0000000..0f25da4 --- /dev/null +++ b/hack/terraform/castai.tf @@ -0,0 +1,67 @@ +data "google_client_config" "default" {} + +provider "castai" { + api_url = var.castai_api_url + api_token = var.castai_api_token +} + +provider "helm" { + kubernetes { + host = "https://${module.gke.endpoint}" + token = data.google_client_config.default.access_token + cluster_ca_certificate = base64decode(module.gke.ca_certificate) + } +} + +# Configure GKE cluster connection using CAST AI gke-cluster module. +module "castai-gke-iam" { + source = "castai/gke-iam/castai" + + project_id = var.project_id + gke_cluster_name = var.cluster_name +} + +module "castai-gke-cluster" { + source = "castai/gke-cluster/castai" + + api_url = var.castai_api_url + castai_api_token = var.castai_api_token + grpc_url = var.castai_grpc_url + wait_for_cluster_ready = true + + project_id = var.project_id + gke_cluster_name = var.cluster_name + gke_cluster_location = module.gke.location + + gke_credentials = module.castai-gke-iam.private_key + default_node_configuration_name = "default" + + node_configurations = { + default = { + disk_cpu_ratio = 25 + subnets = [module.vpc.subnets_ids[0]] + } + } + + node_templates = { + default_by_castai = { + name = "default-by-castai" + configuration_name = "default" + is_default = true + is_enabled = true + should_taint = false + + constraints = { + on_demand = true + spot = true + use_spot_fallbacks = true + + enable_spot_diversity = false + spot_diversity_price_increase_limit_percent = 20 + } + } + } + + depends_on = [module.gke, module.castai-gke-iam] +} + diff --git a/hack/terraform/cloud-proxy.tf b/hack/terraform/cloud-proxy.tf new file mode 100644 index 0000000..112689e --- /dev/null +++ b/hack/terraform/cloud-proxy.tf @@ -0,0 +1,40 @@ +resource "google_project_iam_binding" "cloud_proxy_workload_identity" { + project = var.project_id + role = "roles/container.clusterViewer" + members = [ + "serviceAccount:${var.project_id}.svc.id.goog[castai-agent/castai-cloud-proxy]" + ] +} + +resource "helm_release" "castai_cloud_proxy" { + name = "castai-cloud-proxy" + //repository = "https://castai.github.io/helm-charts" + //chart = "castai-cloud-proxy" + chart = "../../../github-helm-charts/charts/castai-cloud-proxy" + namespace = "castai-agent" + create_namespace = true + cleanup_on_fail = true + wait = true + + set { + name = "image.tag" + value = "latest" + } + + set { + name = "image.pullPolicy" + value = "Always" + } + + set { + name = "config.grpc.endpoint" + value = var.castai_grpc_url + } + + set_sensitive { + name = "config.grpc.key" + value = var.castai_api_token + } + + depends_on = [module.castai-gke-cluster] +} diff --git a/hack/terraform/gke.tf b/hack/terraform/gke.tf new file mode 100644 index 0000000..2d6c088 --- /dev/null +++ b/hack/terraform/gke.tf @@ -0,0 +1,33 @@ +module "gke" { + source = "terraform-google-modules/kubernetes-engine/google" + version = "24.1.0" + project_id = var.project_id + name = var.cluster_name + region = var.cluster_region + //zones = var.cluster_zones + network = module.vpc.network_name + subnetwork = module.vpc.subnets_names[0] + ip_range_pods = local.ip_range_pods + ip_range_services = local.ip_range_services + http_load_balancing = false + network_policy = false + horizontal_pod_autoscaling = true + filestore_csi_driver = false + + node_pools = [ + { + name = "default-node-pool" + machine_type = "e2-standard-2" + min_count = 2 + max_count = 10 + local_ssd_count = 0 + disk_size_gb = 100 + disk_type = "pd-standard" + image_type = "COS_CONTAINERD" + auto_repair = true + auto_upgrade = true + preemptible = false + initial_node_count = 2 # has to be >=2 to successfully deploy CAST AI controller + } + ] +} diff --git a/hack/terraform/terraform.tf b/hack/terraform/terraform.tf new file mode 100644 index 0000000..502f0c5 --- /dev/null +++ b/hack/terraform/terraform.tf @@ -0,0 +1,17 @@ +terraform { + required_providers { + castai = { + source = "castai/castai" + } + kubernetes = { + source = "hashicorp/kubernetes" + } + google = { + source = "hashicorp/google" + } + google-beta = { + source = "hashicorp/google-beta" + } + } + required_version = ">= 0.13" +} diff --git a/hack/terraform/vars.tf b/hack/terraform/vars.tf new file mode 100644 index 0000000..c706224 --- /dev/null +++ b/hack/terraform/vars.tf @@ -0,0 +1,39 @@ +# GKE module variables. +variable "cluster_name" { + type = string + description = "GKE cluster name in GCP project." +} + +variable "cluster_region" { + type = string + default = "europe-central2" + description = "The region to create the cluster." +} + +variable "project_id" { + type = string + default = "engineering-test-353509" + description = "GCP project ID in which GKE cluster would be created." +} + +variable "castai_api_url" { + type = string + description = "URL of alternative CAST AI API to be used during development or testing" +} + +# Variables required for connecting EKS cluster to CAST AI +variable "castai_api_token" { + type = string + description = "CAST AI API token created in console.cast.ai API Access keys section." +} + +variable "castai_grpc_url" { + type = string + description = "CAST AI gRPC URL" +} + +variable "tags" { + type = map(any) + description = "Optional tags for new cluster nodes. This parameter applies only to new nodes - tags for old nodes are not reconciled." + default = {} +} diff --git a/hack/terraform/vpc.tf b/hack/terraform/vpc.tf new file mode 100644 index 0000000..5fa6a7f --- /dev/null +++ b/hack/terraform/vpc.tf @@ -0,0 +1,33 @@ +locals { + ip_range_pods = "${var.cluster_name}-ip-range-pods" + ip_range_services = "${var.cluster_name}-ip-range-services" + ip_range_nodes = "${var.cluster_name}-ip-range-nodes" +} + +module "vpc" { + source = "terraform-google-modules/network/google" + version = "6.0.0" + project_id = var.project_id + network_name = var.cluster_name + subnets = [ + { + subnet_name = local.ip_range_nodes + subnet_ip = "10.0.0.0/16" + subnet_region = var.cluster_region + subnet_private_access = "true" + }, + ] + + secondary_ranges = { + (local.ip_range_nodes) = [ + { + range_name = local.ip_range_pods + ip_cidr_range = "10.20.0.0/16" + }, + { + range_name = local.ip_range_services + ip_cidr_range = "10.30.0.0/24" + } + ] + } +} diff --git a/internal/castai/dummy/mock_cast.go b/internal/castai/dummy/mock_cast.go index cf0ac59..ec529a0 100644 --- a/internal/castai/dummy/mock_cast.go +++ b/internal/castai/dummy/mock_cast.go @@ -25,7 +25,7 @@ type MockCast struct { func (mc *MockCast) Run() error { logger := log.New(os.Stderr, "[CAST-MOCK] ", log.LstdFlags) - requestChan, respChan := make(chan *proto.HttpRequest), make(chan *proto.HttpResponse) + requestChan, respChan := make(chan *proto.StreamCloudProxyResponse), make(chan *proto.StreamCloudProxyRequest) // Start the mock server listener, err := net.Listen("tcp", ":50051") @@ -34,7 +34,7 @@ func (mc *MockCast) Run() error { } grpcServer := grpc.NewServer() - proto.RegisterGCPProxyServerServer(grpcServer, NewMockCastServer(requestChan, respChan, logger)) + proto.RegisterCloudProxyAPIServer(grpcServer, NewMockCastServer(requestChan, respChan, logger)) dispatcher := e2etest.NewDispatcher(requestChan, respChan, logger) @@ -68,15 +68,15 @@ func (mc *MockCast) Run() error { } type MockCastServer struct { - proto.UnimplementedGCPProxyServerServer + proto.UnimplementedCloudProxyAPIServer - requestChan <-chan *proto.HttpRequest - responseChan chan<- *proto.HttpResponse + requestChan <-chan *proto.StreamCloudProxyResponse + responseChan chan<- *proto.StreamCloudProxyRequest logger *log.Logger } -func NewMockCastServer(requestChan <-chan *proto.HttpRequest, responseChan chan<- *proto.HttpResponse, logger *log.Logger) *MockCastServer { +func NewMockCastServer(requestChan <-chan *proto.StreamCloudProxyResponse, responseChan chan<- *proto.StreamCloudProxyRequest, logger *log.Logger) *MockCastServer { return &MockCastServer{ requestChan: requestChan, responseChan: responseChan, @@ -84,7 +84,7 @@ func NewMockCastServer(requestChan <-chan *proto.HttpRequest, responseChan chan< } } -func (msrv *MockCastServer) Proxy(stream proto.GCPProxyServer_ProxyServer) error { +func (msrv *MockCastServer) Proxy(stream proto.CloudProxyAPI_StreamCloudProxyServer) error { msrv.logger.Println("Received a proxy connection from client") var eg errgroup.Group @@ -117,7 +117,7 @@ func (msrv *MockCastServer) Proxy(stream proto.GCPProxyServer_ProxyServer) error return err } - msrv.logger.Printf("Got a response from client: %v, %v\n", in.RequestID, in.Status) + msrv.logger.Printf("Got a response from client: %v, %v\n", in.MessageId, in.HttpResponse.Status) msrv.responseChan <- in } }) diff --git a/internal/castai/proto/proxy.pb.go b/internal/castai/proto/proxy.pb.go index 702291a..8139698 100644 --- a/internal/castai/proto/proxy.pb.go +++ b/internal/castai/proto/proxy.pb.go @@ -20,19 +20,17 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type HttpResponse struct { +type StreamCloudProxyRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RequestID string `protobuf:"bytes,1,opt,name=requestID,proto3" json:"requestID,omitempty"` - Status int32 `protobuf:"varint,2,opt,name=status,proto3" json:"status,omitempty"` - Headers map[string]string `protobuf:"bytes,3,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` + MessageId string `protobuf:"bytes,1,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` + HttpResponse *HTTPResponse `protobuf:"bytes,2,opt,name=http_response,json=httpResponse,proto3,oneof" json:"http_response,omitempty"` } -func (x *HttpResponse) Reset() { - *x = HttpResponse{} +func (x *StreamCloudProxyRequest) Reset() { + *x = StreamCloudProxyRequest{} if protoimpl.UnsafeEnabled { mi := &file_internal_castai_proto_proxy_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -40,13 +38,13 @@ func (x *HttpResponse) Reset() { } } -func (x *HttpResponse) String() string { +func (x *StreamCloudProxyRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*HttpResponse) ProtoMessage() {} +func (*StreamCloudProxyRequest) ProtoMessage() {} -func (x *HttpResponse) ProtoReflect() protoreflect.Message { +func (x *StreamCloudProxyRequest) ProtoReflect() protoreflect.Message { mi := &file_internal_castai_proto_proxy_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -58,68 +56,108 @@ func (x *HttpResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use HttpResponse.ProtoReflect.Descriptor instead. -func (*HttpResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use StreamCloudProxyRequest.ProtoReflect.Descriptor instead. +func (*StreamCloudProxyRequest) Descriptor() ([]byte, []int) { return file_internal_castai_proto_proxy_proto_rawDescGZIP(), []int{0} } -func (x *HttpResponse) GetRequestID() string { +func (x *StreamCloudProxyRequest) GetMessageId() string { if x != nil { - return x.RequestID + return x.MessageId } return "" } -func (x *HttpResponse) GetStatus() int32 { +func (x *StreamCloudProxyRequest) GetHttpResponse() *HTTPResponse { if x != nil { - return x.Status + return x.HttpResponse + } + return nil +} + +type StreamCloudProxyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MessageId string `protobuf:"bytes,1,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` + HttpRequest *HTTPRequest `protobuf:"bytes,2,opt,name=http_request,json=httpRequest,proto3,oneof" json:"http_request,omitempty"` +} + +func (x *StreamCloudProxyResponse) Reset() { + *x = StreamCloudProxyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_castai_proto_proxy_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return 0 } -func (x *HttpResponse) GetHeaders() map[string]string { +func (x *StreamCloudProxyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamCloudProxyResponse) ProtoMessage() {} + +func (x *StreamCloudProxyResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_castai_proto_proxy_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamCloudProxyResponse.ProtoReflect.Descriptor instead. +func (*StreamCloudProxyResponse) Descriptor() ([]byte, []int) { + return file_internal_castai_proto_proxy_proto_rawDescGZIP(), []int{1} +} + +func (x *StreamCloudProxyResponse) GetMessageId() string { if x != nil { - return x.Headers + return x.MessageId } - return nil + return "" } -func (x *HttpResponse) GetBody() []byte { +func (x *StreamCloudProxyResponse) GetHttpRequest() *HTTPRequest { if x != nil { - return x.Body + return x.HttpRequest } return nil } -type HttpRequest struct { +type HTTPRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RequestID string `protobuf:"bytes,1,opt,name=requestID,proto3" json:"requestID,omitempty"` - Method string `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"` - Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"` - Headers map[string]string `protobuf:"bytes,4,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Body []byte `protobuf:"bytes,5,opt,name=body,proto3" json:"body,omitempty"` + Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Body []byte `protobuf:"bytes,3,opt,name=body,proto3,oneof" json:"body,omitempty"` + Headers map[string]*HeaderValue `protobuf:"bytes,4,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -func (x *HttpRequest) Reset() { - *x = HttpRequest{} +func (x *HTTPRequest) Reset() { + *x = HTTPRequest{} if protoimpl.UnsafeEnabled { - mi := &file_internal_castai_proto_proxy_proto_msgTypes[1] + mi := &file_internal_castai_proto_proxy_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *HttpRequest) String() string { +func (x *HTTPRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*HttpRequest) ProtoMessage() {} +func (*HTTPRequest) ProtoMessage() {} -func (x *HttpRequest) ProtoReflect() protoreflect.Message { - mi := &file_internal_castai_proto_proxy_proto_msgTypes[1] +func (x *HTTPRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_castai_proto_proxy_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -130,86 +168,223 @@ func (x *HttpRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use HttpRequest.ProtoReflect.Descriptor instead. -func (*HttpRequest) Descriptor() ([]byte, []int) { - return file_internal_castai_proto_proxy_proto_rawDescGZIP(), []int{1} +// Deprecated: Use HTTPRequest.ProtoReflect.Descriptor instead. +func (*HTTPRequest) Descriptor() ([]byte, []int) { + return file_internal_castai_proto_proxy_proto_rawDescGZIP(), []int{2} } -func (x *HttpRequest) GetRequestID() string { +func (x *HTTPRequest) GetMethod() string { if x != nil { - return x.RequestID + return x.Method } return "" } -func (x *HttpRequest) GetMethod() string { +func (x *HTTPRequest) GetPath() string { if x != nil { - return x.Method + return x.Path } return "" } -func (x *HttpRequest) GetUrl() string { +func (x *HTTPRequest) GetBody() []byte { if x != nil { - return x.Url + return x.Body } - return "" + return nil } -func (x *HttpRequest) GetHeaders() map[string]string { +func (x *HTTPRequest) GetHeaders() map[string]*HeaderValue { if x != nil { return x.Headers } return nil } -func (x *HttpRequest) GetBody() []byte { +type HTTPResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Body []byte `protobuf:"bytes,1,opt,name=body,proto3,oneof" json:"body,omitempty"` + Error *string `protobuf:"bytes,2,opt,name=error,proto3,oneof" json:"error,omitempty"` + Status int32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"` + Headers map[string]*HeaderValue `protobuf:"bytes,4,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *HTTPResponse) Reset() { + *x = HTTPResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_castai_proto_proxy_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HTTPResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HTTPResponse) ProtoMessage() {} + +func (x *HTTPResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_castai_proto_proxy_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HTTPResponse.ProtoReflect.Descriptor instead. +func (*HTTPResponse) Descriptor() ([]byte, []int) { + return file_internal_castai_proto_proxy_proto_rawDescGZIP(), []int{3} +} + +func (x *HTTPResponse) GetBody() []byte { if x != nil { return x.Body } return nil } +func (x *HTTPResponse) GetError() string { + if x != nil && x.Error != nil { + return *x.Error + } + return "" +} + +func (x *HTTPResponse) GetStatus() int32 { + if x != nil { + return x.Status + } + return 0 +} + +func (x *HTTPResponse) GetHeaders() map[string]*HeaderValue { + if x != nil { + return x.Headers + } + return nil +} + +type HeaderValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Value []string `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"` +} + +func (x *HeaderValue) Reset() { + *x = HeaderValue{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_castai_proto_proxy_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HeaderValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HeaderValue) ProtoMessage() {} + +func (x *HeaderValue) ProtoReflect() protoreflect.Message { + mi := &file_internal_castai_proto_proxy_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HeaderValue.ProtoReflect.Descriptor instead. +func (*HeaderValue) Descriptor() ([]byte, []int) { + return file_internal_castai_proto_proxy_proto_rawDescGZIP(), []int{4} +} + +func (x *HeaderValue) GetValue() []string { + if x != nil { + return x.Value + } + return nil +} + var File_internal_castai_proto_proxy_proto protoreflect.FileDescriptor var file_internal_castai_proto_proxy_proto_rawDesc = []byte{ 0x0a, 0x21, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x61, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x67, 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x22, 0xd3, 0x01, - 0x0a, 0x0c, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, - 0x0a, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, + 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x67, 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x22, 0x8c, 0x01, + 0x0a, 0x17, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, 0x6f, + 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x0d, 0x68, 0x74, 0x74, 0x70, + 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x67, 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x68, 0x74, 0x74, 0x70, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x68, + 0x74, 0x74, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x89, 0x01, 0x0a, + 0x18, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, 0x6f, 0x78, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, 0x3d, 0x0a, 0x0c, 0x68, 0x74, 0x74, 0x70, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x67, 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x68, 0x74, 0x74, 0x70, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xec, 0x01, 0x0a, 0x0b, 0x48, 0x54, 0x54, + 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x70, 0x61, 0x74, 0x68, 0x12, 0x17, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x48, 0x00, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x88, 0x01, 0x01, 0x12, 0x3c, 0x0a, + 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x67, 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x51, 0x0a, 0x0c, 0x48, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, + 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x07, + 0x0a, 0x05, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xff, 0x01, 0x0a, 0x0c, 0x48, 0x54, 0x54, 0x50, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x88, 0x01, + 0x01, 0x12, 0x19, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x01, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3d, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x67, 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x2e, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x65, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x67, 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x1a, 0x3a, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0xe3, 0x01, 0x0a, 0x0b, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x44, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, - 0x44, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x3c, 0x0a, 0x07, 0x68, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, - 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, - 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x1a, 0x3a, 0x0a, - 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0x4c, 0x0a, 0x0e, 0x47, 0x43, 0x50, - 0x50, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x05, 0x50, - 0x72, 0x6f, 0x78, 0x79, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, - 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x15, 0x2e, 0x67, - 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x72, 0x73, 0x1a, 0x51, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x42, + 0x08, 0x0a, 0x06, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x23, 0x0a, 0x0b, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x32, 0x70, + 0x0a, 0x0d, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x41, 0x50, 0x49, 0x12, + 0x5f, 0x0a, 0x10, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, + 0x6f, 0x78, 0x79, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x50, 0x72, 0x6f, + 0x78, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, + 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -224,23 +399,30 @@ func file_internal_castai_proto_proxy_proto_rawDescGZIP() []byte { return file_internal_castai_proto_proxy_proto_rawDescData } -var file_internal_castai_proto_proxy_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_internal_castai_proto_proxy_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_internal_castai_proto_proxy_proto_goTypes = []any{ - (*HttpResponse)(nil), // 0: gcpproxy.HttpResponse - (*HttpRequest)(nil), // 1: gcpproxy.HttpRequest - nil, // 2: gcpproxy.HttpResponse.HeadersEntry - nil, // 3: gcpproxy.HttpRequest.HeadersEntry + (*StreamCloudProxyRequest)(nil), // 0: gcpproxy.StreamCloudProxyRequest + (*StreamCloudProxyResponse)(nil), // 1: gcpproxy.StreamCloudProxyResponse + (*HTTPRequest)(nil), // 2: gcpproxy.HTTPRequest + (*HTTPResponse)(nil), // 3: gcpproxy.HTTPResponse + (*HeaderValue)(nil), // 4: gcpproxy.HeaderValue + nil, // 5: gcpproxy.HTTPRequest.HeadersEntry + nil, // 6: gcpproxy.HTTPResponse.HeadersEntry } var file_internal_castai_proto_proxy_proto_depIdxs = []int32{ - 2, // 0: gcpproxy.HttpResponse.headers:type_name -> gcpproxy.HttpResponse.HeadersEntry - 3, // 1: gcpproxy.HttpRequest.headers:type_name -> gcpproxy.HttpRequest.HeadersEntry - 0, // 2: gcpproxy.GCPProxyServer.Proxy:input_type -> gcpproxy.HttpResponse - 1, // 3: gcpproxy.GCPProxyServer.Proxy:output_type -> gcpproxy.HttpRequest - 3, // [3:4] is the sub-list for method output_type - 2, // [2:3] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 3, // 0: gcpproxy.StreamCloudProxyRequest.http_response:type_name -> gcpproxy.HTTPResponse + 2, // 1: gcpproxy.StreamCloudProxyResponse.http_request:type_name -> gcpproxy.HTTPRequest + 5, // 2: gcpproxy.HTTPRequest.headers:type_name -> gcpproxy.HTTPRequest.HeadersEntry + 6, // 3: gcpproxy.HTTPResponse.headers:type_name -> gcpproxy.HTTPResponse.HeadersEntry + 4, // 4: gcpproxy.HTTPRequest.HeadersEntry.value:type_name -> gcpproxy.HeaderValue + 4, // 5: gcpproxy.HTTPResponse.HeadersEntry.value:type_name -> gcpproxy.HeaderValue + 0, // 6: gcpproxy.CloudProxyAPI.StreamCloudProxy:input_type -> gcpproxy.StreamCloudProxyRequest + 1, // 7: gcpproxy.CloudProxyAPI.StreamCloudProxy:output_type -> gcpproxy.StreamCloudProxyResponse + 7, // [7:8] is the sub-list for method output_type + 6, // [6:7] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_internal_castai_proto_proxy_proto_init() } @@ -250,7 +432,7 @@ func file_internal_castai_proto_proxy_proto_init() { } if !protoimpl.UnsafeEnabled { file_internal_castai_proto_proxy_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*HttpResponse); i { + switch v := v.(*StreamCloudProxyRequest); i { case 0: return &v.state case 1: @@ -262,7 +444,43 @@ func file_internal_castai_proto_proxy_proto_init() { } } file_internal_castai_proto_proxy_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*HttpRequest); i { + switch v := v.(*StreamCloudProxyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_castai_proto_proxy_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*HTTPRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_castai_proto_proxy_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*HTTPResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_castai_proto_proxy_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*HeaderValue); i { case 0: return &v.state case 1: @@ -274,13 +492,17 @@ func file_internal_castai_proto_proxy_proto_init() { } } } + file_internal_castai_proto_proxy_proto_msgTypes[0].OneofWrappers = []any{} + file_internal_castai_proto_proxy_proto_msgTypes[1].OneofWrappers = []any{} + file_internal_castai_proto_proxy_proto_msgTypes[2].OneofWrappers = []any{} + file_internal_castai_proto_proxy_proto_msgTypes[3].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_internal_castai_proto_proxy_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 7, NumExtensions: 0, NumServices: 1, }, diff --git a/internal/castai/proto/proxy.proto b/internal/castai/proto/proxy.proto index bee1289..0e3b6c4 100644 --- a/internal/castai/proto/proxy.proto +++ b/internal/castai/proto/proxy.proto @@ -4,25 +4,41 @@ package gcpproxy; option go_package = ".;proto"; -// TODO: Keep-alives +// CloudProxyAPI provides the API for proxying cloud requests for CAST AI External Provisioner. +service CloudProxyAPI { + // option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_tag) = { + // description: "Provides the API for CAST AI Cloud-proxy." + // }; + + // Stream motherboard with cluster. + rpc StreamCloudProxy(stream StreamCloudProxyRequest) returns (stream StreamCloudProxyResponse) {} +} + +message StreamCloudProxyRequest { + string message_id = 1; + optional HTTPResponse http_response = 2; +} +message StreamCloudProxyResponse { + string message_id = 1; + optional HTTPRequest http_request = 2; +} -service GCPProxyServer { - rpc Proxy(stream HttpResponse) returns (stream HttpRequest); +message HTTPRequest { + string method = 1; + string path = 2; + optional bytes body = 3; + map headers = 4; } -message HttpResponse { - string requestID = 1; - int32 status = 2; - map headers = 3; - bytes body = 4; +message HTTPResponse { + optional bytes body = 1; + optional string error = 2; + int32 status = 3; + map headers = 4; } -message HttpRequest { - string requestID = 1; - string method = 2; - string url = 3; - map headers = 4; - bytes body = 5; +message HeaderValue { + repeated string value = 1; } diff --git a/internal/castai/proto/proxy_grpc.pb.go b/internal/castai/proto/proxy_grpc.pb.go index 9bc4260..a5bf385 100644 --- a/internal/castai/proto/proxy_grpc.pb.go +++ b/internal/castai/proto/proxy_grpc.pb.go @@ -19,94 +19,100 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - GCPProxyServer_Proxy_FullMethodName = "/gcpproxy.GCPProxyServer/Proxy" + CloudProxyAPI_StreamCloudProxy_FullMethodName = "/gcpproxy.CloudProxyAPI/StreamCloudProxy" ) -// GCPProxyServerClient is the client API for GCPProxyServer service. +// CloudProxyAPIClient is the client API for CloudProxyAPI service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type GCPProxyServerClient interface { - Proxy(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[HttpResponse, HttpRequest], error) +// +// CloudProxyAPI provides the API for proxying cloud requests for CAST AI External Provisioner. +type CloudProxyAPIClient interface { + // Stream motherboard with cluster. + StreamCloudProxy(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[StreamCloudProxyRequest, StreamCloudProxyResponse], error) } -type gCPProxyServerClient struct { +type cloudProxyAPIClient struct { cc grpc.ClientConnInterface } -func NewGCPProxyServerClient(cc grpc.ClientConnInterface) GCPProxyServerClient { - return &gCPProxyServerClient{cc} +func NewCloudProxyAPIClient(cc grpc.ClientConnInterface) CloudProxyAPIClient { + return &cloudProxyAPIClient{cc} } -func (c *gCPProxyServerClient) Proxy(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[HttpResponse, HttpRequest], error) { +func (c *cloudProxyAPIClient) StreamCloudProxy(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[StreamCloudProxyRequest, StreamCloudProxyResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &GCPProxyServer_ServiceDesc.Streams[0], GCPProxyServer_Proxy_FullMethodName, cOpts...) + stream, err := c.cc.NewStream(ctx, &CloudProxyAPI_ServiceDesc.Streams[0], CloudProxyAPI_StreamCloudProxy_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &grpc.GenericClientStream[HttpResponse, HttpRequest]{ClientStream: stream} + x := &grpc.GenericClientStream[StreamCloudProxyRequest, StreamCloudProxyResponse]{ClientStream: stream} return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type GCPProxyServer_ProxyClient = grpc.BidiStreamingClient[HttpResponse, HttpRequest] +type CloudProxyAPI_StreamCloudProxyClient = grpc.BidiStreamingClient[StreamCloudProxyRequest, StreamCloudProxyResponse] -// GCPProxyServerServer is the server API for GCPProxyServer service. -// All implementations must embed UnimplementedGCPProxyServerServer +// CloudProxyAPIServer is the server API for CloudProxyAPI service. +// All implementations must embed UnimplementedCloudProxyAPIServer // for forward compatibility. -type GCPProxyServerServer interface { - Proxy(grpc.BidiStreamingServer[HttpResponse, HttpRequest]) error - mustEmbedUnimplementedGCPProxyServerServer() +// +// CloudProxyAPI provides the API for proxying cloud requests for CAST AI External Provisioner. +type CloudProxyAPIServer interface { + // Stream motherboard with cluster. + StreamCloudProxy(grpc.BidiStreamingServer[StreamCloudProxyRequest, StreamCloudProxyResponse]) error + mustEmbedUnimplementedCloudProxyAPIServer() } -// UnimplementedGCPProxyServerServer must be embedded to have +// UnimplementedCloudProxyAPIServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. -type UnimplementedGCPProxyServerServer struct{} +type UnimplementedCloudProxyAPIServer struct{} -func (UnimplementedGCPProxyServerServer) Proxy(grpc.BidiStreamingServer[HttpResponse, HttpRequest]) error { - return status.Errorf(codes.Unimplemented, "method Proxy not implemented") +func (UnimplementedCloudProxyAPIServer) StreamCloudProxy(grpc.BidiStreamingServer[StreamCloudProxyRequest, StreamCloudProxyResponse]) error { + return status.Errorf(codes.Unimplemented, "method StreamCloudProxy not implemented") } -func (UnimplementedGCPProxyServerServer) mustEmbedUnimplementedGCPProxyServerServer() {} -func (UnimplementedGCPProxyServerServer) testEmbeddedByValue() {} +func (UnimplementedCloudProxyAPIServer) mustEmbedUnimplementedCloudProxyAPIServer() {} +func (UnimplementedCloudProxyAPIServer) testEmbeddedByValue() {} -// UnsafeGCPProxyServerServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to GCPProxyServerServer will +// UnsafeCloudProxyAPIServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to CloudProxyAPIServer will // result in compilation errors. -type UnsafeGCPProxyServerServer interface { - mustEmbedUnimplementedGCPProxyServerServer() +type UnsafeCloudProxyAPIServer interface { + mustEmbedUnimplementedCloudProxyAPIServer() } -func RegisterGCPProxyServerServer(s grpc.ServiceRegistrar, srv GCPProxyServerServer) { - // If the following call pancis, it indicates UnimplementedGCPProxyServerServer was +func RegisterCloudProxyAPIServer(s grpc.ServiceRegistrar, srv CloudProxyAPIServer) { + // If the following call pancis, it indicates UnimplementedCloudProxyAPIServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } - s.RegisterService(&GCPProxyServer_ServiceDesc, srv) + s.RegisterService(&CloudProxyAPI_ServiceDesc, srv) } -func _GCPProxyServer_Proxy_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(GCPProxyServerServer).Proxy(&grpc.GenericServerStream[HttpResponse, HttpRequest]{ServerStream: stream}) +func _CloudProxyAPI_StreamCloudProxy_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(CloudProxyAPIServer).StreamCloudProxy(&grpc.GenericServerStream[StreamCloudProxyRequest, StreamCloudProxyResponse]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type GCPProxyServer_ProxyServer = grpc.BidiStreamingServer[HttpResponse, HttpRequest] +type CloudProxyAPI_StreamCloudProxyServer = grpc.BidiStreamingServer[StreamCloudProxyRequest, StreamCloudProxyResponse] -// GCPProxyServer_ServiceDesc is the grpc.ServiceDesc for GCPProxyServer service. +// CloudProxyAPI_ServiceDesc is the grpc.ServiceDesc for CloudProxyAPI service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) -var GCPProxyServer_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "gcpproxy.GCPProxyServer", - HandlerType: (*GCPProxyServerServer)(nil), +var CloudProxyAPI_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "gcpproxy.CloudProxyAPI", + HandlerType: (*CloudProxyAPIServer)(nil), Methods: []grpc.MethodDesc{}, Streams: []grpc.StreamDesc{ { - StreamName: "Proxy", - Handler: _GCPProxyServer_Proxy_Handler, + StreamName: "StreamCloudProxy", + Handler: _CloudProxyAPI_StreamCloudProxy_Handler, ServerStreams: true, ClientStreams: true, }, diff --git a/internal/config/config.go b/internal/config/config.go index 1f4f688..fdf2af6 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -15,6 +15,11 @@ type Config struct { type GRPC struct { Endpoint string Key string + TLS TLSConfig +} + +type TLSConfig struct { + Enabled bool } type Log struct { @@ -30,6 +35,7 @@ func Get() Config { _ = viper.BindEnv("grpc.endpoint", "GRPC_ENDPOINT") _ = viper.BindEnv("grpc.key", "GRPC_KEY") + _ = viper.BindEnv("grpc.tls.enabled", "GRPC_TLS_ENABLED") _ = viper.BindEnv("log.level", "LOG_LEVEL") cfg = &Config{} diff --git a/internal/e2etest/dispatcher.go b/internal/e2etest/dispatcher.go index e51ab84..ecd7850 100644 --- a/internal/e2etest/dispatcher.go +++ b/internal/e2etest/dispatcher.go @@ -8,18 +8,18 @@ import ( ) type Dispatcher struct { - pendingRequests map[string]chan *proto.HttpResponse + pendingRequests map[string]chan *proto.StreamCloudProxyRequest locker sync.Mutex - proxyRequestChan chan<- *proto.HttpRequest - proxyResponseChan <-chan *proto.HttpResponse + proxyRequestChan chan<- *proto.StreamCloudProxyResponse + proxyResponseChan <-chan *proto.StreamCloudProxyRequest logger *log.Logger } -func NewDispatcher(requestChan chan<- *proto.HttpRequest, responseChan <-chan *proto.HttpResponse, logger *log.Logger) *Dispatcher { +func NewDispatcher(requestChan chan<- *proto.StreamCloudProxyResponse, responseChan <-chan *proto.StreamCloudProxyRequest, logger *log.Logger) *Dispatcher { return &Dispatcher{ - pendingRequests: make(map[string]chan *proto.HttpResponse), + pendingRequests: make(map[string]chan *proto.StreamCloudProxyRequest), locker: sync.Mutex{}, proxyRequestChan: requestChan, proxyResponseChan: responseChan, @@ -32,7 +32,7 @@ func (d *Dispatcher) Run() { d.logger.Println("starting response returning loop") for { for resp := range d.proxyResponseChan { - waiter := d.findWaiterForResponse(resp.RequestID) + waiter := d.findWaiterForResponse(resp.MessageId) waiter <- resp d.logger.Println("Sent a response back to caller") } @@ -40,21 +40,21 @@ func (d *Dispatcher) Run() { }() } -func (d *Dispatcher) SendRequest(req *proto.HttpRequest) (<-chan *proto.HttpResponse, error) { - waiter := d.addRequestToWaitingList(req.RequestID) +func (d *Dispatcher) SendRequest(req *proto.StreamCloudProxyResponse) (<-chan *proto.StreamCloudProxyRequest, error) { + waiter := d.addRequestToWaitingList(req.MessageId) d.proxyRequestChan <- req return waiter, nil } -func (d *Dispatcher) addRequestToWaitingList(requestID string) <-chan *proto.HttpResponse { - waiter := make(chan *proto.HttpResponse, 1) +func (d *Dispatcher) addRequestToWaitingList(requestID string) <-chan *proto.StreamCloudProxyRequest { + waiter := make(chan *proto.StreamCloudProxyRequest, 1) d.locker.Lock() d.pendingRequests[requestID] = waiter d.locker.Unlock() return waiter } -func (d *Dispatcher) findWaiterForResponse(requestID string) chan *proto.HttpResponse { +func (d *Dispatcher) findWaiterForResponse(requestID string) chan *proto.StreamCloudProxyRequest { d.locker.Lock() val, ok := d.pendingRequests[requestID] if !ok { diff --git a/internal/e2etest/roundtripper.go b/internal/e2etest/roundtripper.go index ef164cd..883837b 100644 --- a/internal/e2etest/roundtripper.go +++ b/internal/e2etest/roundtripper.go @@ -6,7 +6,6 @@ import ( "io" "log" "net/http" - "strings" "github.com/google/uuid" @@ -26,26 +25,28 @@ func NewHttpOverGrpcRoundTripper(dispatcher *Dispatcher, logger *log.Logger) *Ht func (p *HttpOverGrpcRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { requestID := uuid.New().String() - headers := make(map[string]string) + headers := make(map[string]*proto.HeaderValue) for h, v := range request.Header { - headers[h] = strings.Join(v, ",") + headers[h] = &proto.HeaderValue{Value: v} } - protoReq := &proto.HttpRequest{ - RequestID: requestID, - Method: request.Method, - Url: request.URL.String(), - Headers: headers, - Body: func() []byte { - if request.Body == nil { - return []byte{} - } - body, err := io.ReadAll(request.Body) - if err != nil { - panic(fmt.Sprintf("Failed to read body: %v", err)) - } - return body - }(), + protoReq := &proto.StreamCloudProxyResponse{ + MessageId: requestID, + HttpRequest: &proto.HTTPRequest{ + Method: request.Method, + Path: request.URL.String(), + Headers: headers, + Body: func() []byte { + if request.Body == nil { + return []byte{} + } + body, err := io.ReadAll(request.Body) + if err != nil { + panic(fmt.Sprintf("Failed to read body: %v", err)) + } + return body + }(), + }, } waiter, err := p.dispatcher.SendRequest(protoReq) if err != nil { @@ -57,17 +58,18 @@ func (p *HttpOverGrpcRoundTripper) RoundTrip(request *http.Request) (*http.Respo // Convert to response resp := &http.Response{ - Status: http.StatusText(int(response.Status)), - StatusCode: int(response.Status), + StatusCode: int(response.HttpResponse.Status), Header: func() http.Header { headers := make(http.Header) - for key, value := range response.Headers { - headers[key] = strings.Split(value, ",") + for key, value := range response.HttpResponse.Headers { + for _, v := range value.Value { + headers.Add(key, v) + } } return headers }(), - Body: io.NopCloser(bytes.NewReader(response.Body)), - ContentLength: int64(len(response.Body)), + Body: io.NopCloser(bytes.NewReader(response.HttpResponse.Body)), + ContentLength: int64(len(response.HttpResponse.Body)), Request: request, } diff --git a/internal/localtest/roundtripper.go b/internal/localtest/roundtripper.go deleted file mode 100644 index 6c996a8..0000000 --- a/internal/localtest/roundtripper.go +++ /dev/null @@ -1,72 +0,0 @@ -package localtest - -import ( - "bytes" - "fmt" - "io" - "net/http" - "strings" - - "github.com/google/uuid" - - "github.com/castai/cloud-proxy/internal/castai/proto" - "github.com/castai/cloud-proxy/internal/proxy" -) - -// RoundTripper does proxying via Executor instance directly in-process. -// Useful to test without a grpc connection or Cast at all; just to isolate if a http request can be modified successfully. -type RoundTripper struct { - executor *proxy.Executor -} - -func NewProxyRoundTripper(executor *proxy.Executor) *RoundTripper { - return &RoundTripper{executor: executor} -} - -func (p *RoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { - fmt.Println("Sending request to dispatcher") - requestID := uuid.New().String() - - headers := make(map[string]string) - for h, v := range request.Header { - headers[h] = strings.Join(v, ",") - } - protoReq := &proto.HttpRequest{ - RequestID: requestID, - Method: request.Method, - Url: request.URL.String(), - Headers: headers, - Body: func() []byte { - if request.Body == nil { - return []byte{} - } - body, err := io.ReadAll(request.Body) - if err != nil { - panic(fmt.Sprintf("Failed to read body: %v", err)) - } - return body - }(), - } - response, err := p.executor.DoRequest(protoReq) - if err != nil { - return nil, fmt.Errorf("error sending request: %v", err) - } - - // Convert to http response - resp := &http.Response{ - Status: http.StatusText(int(response.Status)), - StatusCode: int(response.Status), - Header: func() http.Header { - headers := make(http.Header) - for key, value := range response.Headers { - headers[key] = strings.Split(value, ",") - } - return headers - }(), - Body: io.NopCloser(bytes.NewReader(response.Body)), - ContentLength: int64(len(response.Body)), - Request: request, - } - - return resp, nil -} diff --git a/internal/localtest/run.go b/internal/localtest/run.go deleted file mode 100644 index db33171..0000000 --- a/internal/localtest/run.go +++ /dev/null @@ -1,124 +0,0 @@ -package localtest - -import ( - "context" - "errors" - "fmt" - "log" - "net/http" - "time" - - container "cloud.google.com/go/container/apiv1" - "cloud.google.com/go/container/apiv1/containerpb" - "github.com/googleapis/gax-go/v2/apierror" - "google.golang.org/api/option" - htransport "google.golang.org/api/transport/http" - - "github.com/castai/cloud-proxy/internal/gcpauth" - "github.com/castai/cloud-proxy/internal/proxy" -) - -// RunBasicTests verifies that: -// -// - We can get a local auth token -// - We can make a call to GKE GetCluster API successfully with that token -// -// Sample usage: verify that the workload's identity has access to GKE without any modifications; proxies; etc -func RunBasicTests(projectID, location, testCluster string) { - src := gcpauth.GCPCredentialsSource{} - log.Println(src) - defaultCreds, err := src.GetDefaultCredentials() - if err != nil { - panic(err) - } - - if defaultCreds.ProjectID != projectID { - log.Panicf("expected project ID %q got %q", projectID, defaultCreds.ProjectID) - } - - log.Printf("Found default credentials: [project: %v, typeof(tokensrc): %T, tokensrc: %+v, jsonCreds: %v\n", - defaultCreds.ProjectID, defaultCreds.TokenSource, defaultCreds.TokenSource, string(defaultCreds.JSON)) - - token, err := defaultCreds.TokenSource.Token() - if err != nil { - panic(err) - } - log.Printf("Managed to successfully get a token: %+v\n", token) - log.Printf("token expiration: %+v\n", token.Expiry) - - // Test GKE method - gkeClient, err := container.NewClusterManagerRESTClient( - context.Background(), - option.WithCredentials(defaultCreds)) - if err != nil { - log.Panicf("creating gke cluster client: %v", err) - } - - getClusterResponse, err := gkeClient.GetCluster(context.Background(), &containerpb.GetClusterRequest{ - Name: fmt.Sprintf("projects/%v/locations/%v/clusters/%v", projectID, location, testCluster), - }) - if err != nil { - log.Panicf("getting cluster: %v", err) - } - log.Printf("Got successful response for cluster: %v: %v\n", getClusterResponse.Name, getClusterResponse.GetStatus()) -} - -// RunProxyTest verifies that: -// -// - Making a call to GKE without credentials fails with 401 (to ensure we don't auto-detect credentials somehow). -// - Making the call via proxy.Executor works as expected (i.e. the modifications proxy.Executor does make the call succeed with proper auth) -// -// This func doesn't return; it loops forever -func RunProxyTest(projectID, location, testCluster string) { - // Ensure that we get 401 when we don't use the "proxy" - gkeClient, err := container.NewClusterManagerRESTClient( - context.Background(), - option.WithoutAuthentication()) - if err != nil { - log.Panicf("creating gke cluster client: %v", err) - } - - _, err = gkeClient.GetCluster(context.Background(), &containerpb.GetClusterRequest{ - Name: fmt.Sprintf("projects/%v/locations/%v/clusters/%v", projectID, location, testCluster), - }) - if err == nil { - log.Panic("expected unauthorized error when not passing credentials") - } - - // TODO: This feels ugly, isn't there a type-safe way? - out := &apierror.APIError{} - if ok := errors.As(err, &out); ok && out.Reason() == "CREDENTIALS_MISSING" { - log.Println("Test successful; with no auth we get 401") - } else { - log.Panicf("expected error when not passing credentials: %v", err) - } - - log.Println("Setting up custom GCP client with our roundtripper") - - src := gcpauth.GCPCredentialsSource{} - executor := proxy.NewExecutor(src, http.DefaultClient) - customRoundTripper := NewProxyRoundTripper(executor) - customClientForGCP, _, err := htransport.NewClient(context.Background()) - if err != nil { - log.Panicf("failed creating http client: %v", err) - } - customClientForGCP.Transport = customRoundTripper - gkeProxiedClient, err := container.NewClusterManagerRESTClient( - context.Background(), - option.WithoutAuthentication(), - option.WithHTTPClient(customClientForGCP)) - if err != nil { - log.Panicf("failed creating gke cluster client with proxy: %v", err) - } - log.Println("Simulating cast requests forever...") - for { - getClusterResponse, err := gkeProxiedClient.GetCluster(context.Background(), &containerpb.GetClusterRequest{ - Name: fmt.Sprintf("projects/%v/locations/%v/clusters/%v", projectID, location, testCluster), - }) - if err != nil { - log.Panicf("getting cluster through proxy: %v", err) - } - log.Printf("Got successful response for cluster via local proxy: %v: %v\n", getClusterResponse.Name, getClusterResponse.GetStatus()) - time.Sleep(10 * time.Second) - } -} diff --git a/internal/proxy/executor.go b/internal/proxy/executor.go index 2cfd557..3a34071 100644 --- a/internal/proxy/executor.go +++ b/internal/proxy/executor.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "net/http" - "strings" "github.com/castai/cloud-proxy/internal/castai/proto" "github.com/castai/cloud-proxy/internal/gcpauth" @@ -20,7 +19,7 @@ func NewExecutor(credentialsSrc gcpauth.GCPCredentialsSource, client *http.Clien return &Executor{credentialsSrc: credentialsSrc, client: client} } -func (e *Executor) DoRequest(request *proto.HttpRequest) (*proto.HttpResponse, error) { +func (e *Executor) DoRequest(request *proto.StreamCloudProxyResponse) (*proto.StreamCloudProxyRequest, error) { credentials, err := e.credentialsSrc.GetDefaultCredentials() if err != nil { return nil, fmt.Errorf("cannot load GCP credentials: %w", err) @@ -31,41 +30,53 @@ func (e *Executor) DoRequest(request *proto.HttpRequest) (*proto.HttpResponse, e return nil, fmt.Errorf("cannot get access token from src (%T): %w", credentials.TokenSource, err) } - httpReq, err := http.NewRequest(request.Method, request.Url, bytes.NewReader(request.Body)) + httpReq := request.HttpRequest + + req, err := http.NewRequest(request.HttpRequest.Method, httpReq.Path, bytes.NewReader(httpReq.Body)) if err != nil { return nil, fmt.Errorf("cannot create http request: %w", err) } - for header, val := range request.Headers { - httpReq.Header.Add(header, val) + for header, values := range httpReq.Headers { + for _, value := range values.Value { + req.Header.Add(header, value) + } } // Set the authorize header manually since we can't rely on mothership auth - httpReq.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.AccessToken)) + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.AccessToken)) //log.Println("Sending request:", httpReq) //log.Println("Auth header:", httpReq.Header.Get("Authorization")) - httpResponse, err := e.client.Do(httpReq) + resp, err := e.client.Do(req) if err != nil { return nil, fmt.Errorf("unexpected err for %+v: %w", request, err) } + defer resp.Body.Close() - response := &proto.HttpResponse{ - RequestID: request.RequestID, - Status: int32(httpResponse.StatusCode), - Headers: make(map[string]string), - Body: func() []byte { - if httpResponse.Body == nil { - return []byte{} - } - body, err := io.ReadAll(httpResponse.Body) - if err != nil { - panic(fmt.Errorf("failed to serialize body from request: %w", err)) - } - return body - }(), - } - for header, val := range httpResponse.Header { - response.Headers[header] = strings.Join(val, ",") + response := &proto.StreamCloudProxyRequest{ + MessageId: request.MessageId, + HttpResponse: &proto.HTTPResponse{ + Status: int32(resp.StatusCode), + Headers: func() map[string]*proto.HeaderValue { + headers := make(map[string]*proto.HeaderValue, len(resp.Header)) + for h, v := range resp.Header { + headers[h] = &proto.HeaderValue{ + Value: v, + } + } + return headers + }(), + Body: func() []byte { + if resp.Body == nil { + return []byte{} + } + body, err := io.ReadAll(resp.Body) + if err != nil { + panic(fmt.Errorf("failed to serialize body from request: %w", err)) + } + return body + }(), + }, } return response, nil diff --git a/internal/proxy/proxy_client.go b/internal/proxy/proxy_client.go index f553728..89b1c21 100644 --- a/internal/proxy/proxy_client.go +++ b/internal/proxy/proxy_client.go @@ -21,15 +21,15 @@ func NewClient(executor *Executor, logger *logrus.Logger) *Client { return &Client{executor: executor, logger: logger} } -func (client *Client) Run(grpcConn *grpc.ClientConn) { - grpcClient := proto.NewGCPProxyServerClient(grpcConn) +func (client *Client) Run(ctx context.Context, grpcConn *grpc.ClientConn) { + grpcClient := proto.NewCloudProxyAPIClient(grpcConn) // Outer loop is a dumb re-connect version for { time.Sleep(1 * time.Second) client.logger.Println("Connecting to castai") - stream, err := grpcClient.Proxy(context.Background()) + stream, err := grpcClient.StreamCloudProxy(ctx) if err != nil { client.logger.Printf("error connecting to castai: %v\n", err) continue @@ -53,14 +53,14 @@ func (client *Client) Run(grpcConn *grpc.ClientConn) { } go func() { - client.logger.Println("Received message from server for proxying:", in.RequestID) + client.logger.Println("Received message from server for proxying:", in.MessageId) resp, err := client.executor.DoRequest(in) if err != nil { client.logger.Println("error executing request", err) // TODO: Sent error as metadata to cast return } - client.logger.Println("got response for request:", resp.RequestID) + client.logger.Println("got response for request:", resp.MessageId) err = stream.Send(resp) if err != nil { From 06bee2a04d36599b0520a00fbbfdd2407e7c2610 Mon Sep 17 00:00:00 2001 From: Damian Czaja Date: Tue, 3 Sep 2024 16:02:18 +0200 Subject: [PATCH 8/9] add docker build on pr --- .github/workflows/build.yaml | 54 +++++++++++++++++++++++------------ hack/terraform/cloud-proxy.tf | 7 ++--- 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 220f954..cdb6235 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -57,20 +57,21 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - - name: Create k8s Kind Cluster - uses: helm/kind-action@v1 - with: - cluster_name: cloud-proxy-e2e - - - uses: azure/setup-helm@v4.2.0 - - - name: Run e2e tests - run: | - echo "$GCP_CREDENTIALS_JSON" > "$GCP_CREDENTIALS" - ./e2e/run.sh - env: - GCP_CREDENTIALS_JSON: ${{ secrets.TEST_GCP_CREDENTIALS }} - GCP_CREDENTIALS: gcp-credentials.json + # TODO: uncomment after Helm chart is released + # - name: Create k8s Kind Cluster + # uses: helm/kind-action@v1 + # with: + # cluster_name: cloud-proxy-e2e + + # - uses: azure/setup-helm@v4.2.0 + + # - name: Run e2e tests + # run: | + # echo "$GCP_CREDENTIALS_JSON" > "$GCP_CREDENTIALS" + # ./e2e/run.sh + # env: + # GCP_CREDENTIALS_JSON: ${{ secrets.TEST_GCP_CREDENTIALS }} + # GCP_CREDENTIALS: gcp-credentials.json - name: Login to Google Artifact Registry uses: docker/login-action@v3 @@ -79,23 +80,40 @@ jobs: username: _json_key password: ${{ secrets.ARTIFACT_BUILDER_JSON_KEY }} + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + if: ${{ github.event_name == 'pull_request' }} + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push pr + if: github.event_name == 'pull_request' + uses: docker/build-push-action@v4 + with: + context: . + platforms: linux/arm64,linux/amd64 + push: ${{ github.event_name == 'pull_request' }} + tags: ghcr.io/castai/cloud-proxy:${{ github.sha }} + - name: Build and push main if: github.event_name != 'release' - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . - push: true platforms: linux/arm64,linux/amd64 + push: ${{ github.event_name != 'pull_request' }} tags: | us-docker.pkg.dev/castai-hub/library/cloud-proxy:${{ github.sha }} - name: Build and push release if: github.event_name == 'release' - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . - push: true platforms: linux/arm64,linux/amd64 + push: true tags: | us-docker.pkg.dev/castai-hub/library/cloud-proxy:${{ env.RELEASE_TAG }} us-docker.pkg.dev/castai-hub/library/cloud-proxy:latest diff --git a/hack/terraform/cloud-proxy.tf b/hack/terraform/cloud-proxy.tf index 112689e..ed26e25 100644 --- a/hack/terraform/cloud-proxy.tf +++ b/hack/terraform/cloud-proxy.tf @@ -7,10 +7,9 @@ resource "google_project_iam_binding" "cloud_proxy_workload_identity" { } resource "helm_release" "castai_cloud_proxy" { - name = "castai-cloud-proxy" - //repository = "https://castai.github.io/helm-charts" - //chart = "castai-cloud-proxy" - chart = "../../../github-helm-charts/charts/castai-cloud-proxy" + name = "castai-cloud-proxy" + repository = "https://castai.github.io/helm-charts" + chart = "castai-cloud-proxy" namespace = "castai-agent" create_namespace = true cleanup_on_fail = true From 0d068ace26337ec853ba9a2bff9161b57592b401 Mon Sep 17 00:00:00 2001 From: Damian Czaja Date: Tue, 3 Sep 2024 16:16:42 +0200 Subject: [PATCH 9/9] remove terraform --- hack/terraform/.terraform.lock.hcl | 141 ----------------------------- hack/terraform/castai.tf | 67 -------------- hack/terraform/cloud-proxy.tf | 39 -------- hack/terraform/gke.tf | 33 ------- hack/terraform/terraform.tf | 17 ---- hack/terraform/vars.tf | 39 -------- hack/terraform/vpc.tf | 33 ------- 7 files changed, 369 deletions(-) delete mode 100644 hack/terraform/.terraform.lock.hcl delete mode 100644 hack/terraform/castai.tf delete mode 100644 hack/terraform/cloud-proxy.tf delete mode 100644 hack/terraform/gke.tf delete mode 100644 hack/terraform/terraform.tf delete mode 100644 hack/terraform/vars.tf delete mode 100644 hack/terraform/vpc.tf diff --git a/hack/terraform/.terraform.lock.hcl b/hack/terraform/.terraform.lock.hcl deleted file mode 100644 index b65040e..0000000 --- a/hack/terraform/.terraform.lock.hcl +++ /dev/null @@ -1,141 +0,0 @@ -# This file is maintained automatically by "terraform init". -# Manual edits may be lost in future updates. - -provider "registry.terraform.io/castai/castai" { - version = "7.13.2" - constraints = ">= 5.1.0, ~> 7.11" - hashes = [ - "h1:HZJN4r+j4HgDrhYFn0Sp9PYhsOBag8kS/B9bsYP7SVo=", - "zh:09fcf4cafa6e1ab30607c76f8959dffb1586bc7a7c2513863c9dfce42f0e6675", - "zh:1920643c1cfb1decb53d8370f33580e93382a0cc599809a11df9137fb392648d", - "zh:1f9ebba7bd6204ecdc5383cad8fd7711cc48a6dd632cce2bbf05db783940b518", - "zh:34b1d3a0e78b0e1661bcfef681f880204157c4d54c60e57a5081b1b9607fd9f1", - "zh:3ab0c47e65ad49cfe16fc787abf250362b4069fd49594c66f96fd1cccb06d7a9", - "zh:421214c2d9cf6a65e4c036b88c0a2570fa86eb10d73f26a2d2e7d6165a5c5ffb", - "zh:51bb557b9fcce75a829be1351892fd6469456eaec5686c31aafab77042249e18", - "zh:8c0fdd2282cf789cfb52c8e2ebc90189de58d971a563764074bd976f815a33c1", - "zh:9b9f66592b7605ac888cf3176baf12b90ef0344367a139fe85a562cb6d4d8432", - "zh:aa5f3563a5ab2953e15c5ca459f228195dfc2c83e99d8a9746c4eade2c14561b", - "zh:cfa1bf6e3cfcef5103cb578b24ba104026fb84ea9a01e8b0bab337f346a2f1fc", - "zh:d18b6a73ac7c00c76c8f5d139fe6742d6386291ef979066cacec927321c1c4f2", - "zh:d22f0cab874893293afc6d5154e7beca7c1299071c7e77c5a2fbb02d34bc783f", - ] -} - -provider "registry.terraform.io/hashicorp/google" { - version = "4.85.0" - constraints = ">= 2.15.0, >= 2.49.0, >= 3.33.0, >= 3.45.0, >= 3.83.0, >= 4.36.0, < 5.0.0" - hashes = [ - "h1:ZVDZuhYSIWhCkSuDkwFeSIJjn0/DcCxak2W/cHW4OQQ=", - "zh:17d60a6a6c1741cf1e09ac6731433a30950285eac88236e623ab4cbf23832ca3", - "zh:1c70254c016439dbb75cab646b4beace6ceeff117c75d81f2cc27d41c312f752", - "zh:35e2aa2cc7ac84ce55e05bb4de7b461b169d3582e56d3262e249ff09d64fe008", - "zh:417afb08d7b2744429f6b76806f4134d62b0354acf98e8a6c00de3c24f2bb6ad", - "zh:622165d09d21d9a922c86f1fc7177a400507f2a8c4a4513114407ae04da2dd29", - "zh:7cdb8e39a8ea0939558d87d2cb6caceded9e21f21003d9e9f9ce648d5db0bc3a", - "zh:851e737dc551d6004a860a8907fda65118fc2c7ede9fa828f7be704a2a39e68f", - "zh:a331ad289a02a2c4473572a573dc389be0a604cdd9e03dd8dbc10297fb14f14d", - "zh:b67fd531251380decd8dd1f849460d60f329f89df3d15f5815849a1dd001f430", - "zh:be8785957acca4f97aa3e800b313b57d1fca07788761c8867c9bc701fbe0bdb5", - "zh:cb6579a259fe020e1f88217d8f6937b2d5ace15b6406370977a1966eb31b1ca5", - "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", - ] -} - -provider "registry.terraform.io/hashicorp/google-beta" { - version = "4.85.0" - constraints = ">= 3.45.0, < 5.0.0" - hashes = [ - "h1:CSE8cBR85sVefrE2Kg6KT5vOb7855t158oMo+iDfL0M=", - "zh:40e9c7ec46955b4d79065a14185043a4ad6af8d0246715853fc5c99208b66980", - "zh:5950a9ba2f96420ea5335b543e315b1a47a705f9a9abfc53c6fec52d084eddcb", - "zh:5dfa98d32246a5d97e018f2b91b0e921cc6f061bc8591884f3b144f0d62f1c20", - "zh:628d0ca35c6d4c35077859bb0a5534c1de44f23a91e190f9c3f06f2358172e75", - "zh:6e78d54fd4de4151968149b4c3521f563a8b5c55aad423dba5968a9114b65ae4", - "zh:91c3bc443188638353285bd35b06d3a3b39b42b3b4cc0637599a430438fba2f7", - "zh:9e91b03363ebf39eea5ec0fbe7675f6979883aa9ad9a36664357d8513a007cf3", - "zh:db9a8d6bfe075fb38c260986ab557d40e8d18e5698c62956a6da8120fae01d59", - "zh:e41169c49f3bb53217905509e2ba8bb4680c373e1f54db7fac1b7f72943a1004", - "zh:f32f55a8af605afbc940814e17493ac83d9d66cd6da9bbc247e0a833a0aa37ec", - "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", - "zh:f6561a6badc3af842f9ad5bb926104954047f07cb90fadcca1357441cc67d91d", - ] -} - -provider "registry.terraform.io/hashicorp/helm" { - version = "2.15.0" - constraints = ">= 2.0.0" - hashes = [ - "h1:VymvscRkDy0+zN2uKpKYY6njXPY8JROARuaL3VPsEos=", - "zh:18b94c7c83c30ad166722a61a412e3de6a67935772960e79aaa24c15f8ea0d0f", - "zh:4f07c929a71e8169f7471b7600bfcca36dfb295787e975e82ac0455a3ab68b47", - "zh:776b804a14c3c4ae6075b12176f81c1f1987214ee1cae4a542599389591cde11", - "zh:7c11e3adbe9bd26e88484dcdbd28c473ce3a5c58950a3e3c4f0a2caee225b845", - "zh:821e1a53415df0ae4ed523f098360d367a95d6ce3872ba841f22adfdd2f97664", - "zh:94c06e483f75a11c3f139c41b3f64b51a96d1d1485e7d1fd3c0f795e2e750945", - "zh:aa2040de0b8150ef40222a965445ec40e3df2997ffde1fb062ab4c226689115e", - "zh:ad73eebeffe20228656567963477d034b9ed3d1bd2075c1c81150def4927d810", - "zh:b77450a36807f3ad1d3ae736d1d165a94fa26f476504a280e9fb2ccb89f648d0", - "zh:d2ebd3c34c50c92106ce2df25d5598f47127dc7c60172b9e2fe56ac73dc863a8", - "zh:e565995e2614df5ddde75a743a674129288fb91669596a7b0b2580fa7ed49979", - "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", - ] -} - -provider "registry.terraform.io/hashicorp/kubernetes" { - version = "2.32.0" - constraints = "~> 2.10" - hashes = [ - "h1:3j4XBR5UWQA7xXaiEnzZp0bHbcwOhWetHYKTWIrUTI0=", - "zh:0e715d7fb13a8ad569a5fdc937b488590633f6942e986196fdb17cd7b8f7720e", - "zh:495fc23acfe508ed981e60af9a3758218b0967993065e10a297fdbc210874974", - "zh:4b930a8619910ef528bc90dae739cb4236b9b76ce41367281e3bc3cf586101c7", - "zh:5344405fde7b1febf0734052052268ee24e7220818155702907d9ece1c0697c7", - "zh:92ee11e8c23bbac3536df7b124456407f35c6c2468bc0dbab15c3fc9f414bd0e", - "zh:a45488fe8d5bb59c49380f398da5d109a4ac02ebc10824567dabb87f6102fda8", - "zh:a4a0b57cf719a4c91f642436882b7bea24d659c08a5b6f4214ce4fe6a0204caa", - "zh:b7a27a6d11ba956a2d7b0f7389a46ec857ebe46ae3aeee537250e66cac15bf03", - "zh:bf94ce389028b686bfa70a90f536e81bb776c5c20ab70138bbe5c3d0a04c4253", - "zh:d965b2608da0212e26a65a0b3f33c5baae46cbe839196be15d93f70061516908", - "zh:f441fc793d03057a17af8bdca8b26d54916645bc5c148f54e22a54ed39089e83", - "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", - ] -} - -provider "registry.terraform.io/hashicorp/null" { - version = "3.2.2" - hashes = [ - "h1:IMVAUHKoydFrlPrl9OzasDnw/8ntZFerCC9iXw1rXQY=", - "zh:3248aae6a2198f3ec8394218d05bd5e42be59f43a3a7c0b71c66ec0df08b69e7", - "zh:32b1aaa1c3013d33c245493f4a65465eab9436b454d250102729321a44c8ab9a", - "zh:38eff7e470acb48f66380a73a5c7cdd76cc9b9c9ba9a7249c7991488abe22fe3", - "zh:4c2f1faee67af104f5f9e711c4574ff4d298afaa8a420680b0cb55d7bbc65606", - "zh:544b33b757c0b954dbb87db83a5ad921edd61f02f1dc86c6186a5ea86465b546", - "zh:696cf785090e1e8cf1587499516b0494f47413b43cb99877ad97f5d0de3dc539", - "zh:6e301f34757b5d265ae44467d95306d61bef5e41930be1365f5a8dcf80f59452", - "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", - "zh:913a929070c819e59e94bb37a2a253c228f83921136ff4a7aa1a178c7cce5422", - "zh:aa9015926cd152425dbf86d1abdbc74bfe0e1ba3d26b3db35051d7b9ca9f72ae", - "zh:bb04798b016e1e1d49bcc76d62c53b56c88c63d6f2dfe38821afef17c416a0e1", - "zh:c23084e1b23577de22603cff752e59128d83cfecc2e6819edadd8cf7a10af11e", - ] -} - -provider "registry.terraform.io/hashicorp/random" { - version = "3.6.2" - hashes = [ - "h1:VavG5unYCa3SYISMKF9pzc3718M0bhPlcbUZZGl7wuo=", - "zh:0ef01a4f81147b32c1bea3429974d4d104bbc4be2ba3cfa667031a8183ef88ec", - "zh:1bcd2d8161e89e39886119965ef0f37fcce2da9c1aca34263dd3002ba05fcb53", - "zh:37c75d15e9514556a5f4ed02e1548aaa95c0ecd6ff9af1119ac905144c70c114", - "zh:4210550a767226976bc7e57d988b9ce48f4411fa8a60cd74a6b246baf7589dad", - "zh:562007382520cd4baa7320f35e1370ffe84e46ed4e2071fdc7e4b1a9b1f8ae9b", - "zh:5efb9da90f665e43f22c2e13e0ce48e86cae2d960aaf1abf721b497f32025916", - "zh:6f71257a6b1218d02a573fc9bff0657410404fb2ef23bc66ae8cd968f98d5ff6", - "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", - "zh:9647e18f221380a85f2f0ab387c68fdafd58af6193a932417299cdcae4710150", - "zh:bb6297ce412c3c2fa9fec726114e5e0508dd2638cad6a0cb433194930c97a544", - "zh:f83e925ed73ff8a5ef6e3608ad9225baa5376446349572c2449c0c0b3cf184b7", - "zh:fbef0781cb64de76b1df1ca11078aecba7800d82fd4a956302734999cfd9a4af", - ] -} diff --git a/hack/terraform/castai.tf b/hack/terraform/castai.tf deleted file mode 100644 index 0f25da4..0000000 --- a/hack/terraform/castai.tf +++ /dev/null @@ -1,67 +0,0 @@ -data "google_client_config" "default" {} - -provider "castai" { - api_url = var.castai_api_url - api_token = var.castai_api_token -} - -provider "helm" { - kubernetes { - host = "https://${module.gke.endpoint}" - token = data.google_client_config.default.access_token - cluster_ca_certificate = base64decode(module.gke.ca_certificate) - } -} - -# Configure GKE cluster connection using CAST AI gke-cluster module. -module "castai-gke-iam" { - source = "castai/gke-iam/castai" - - project_id = var.project_id - gke_cluster_name = var.cluster_name -} - -module "castai-gke-cluster" { - source = "castai/gke-cluster/castai" - - api_url = var.castai_api_url - castai_api_token = var.castai_api_token - grpc_url = var.castai_grpc_url - wait_for_cluster_ready = true - - project_id = var.project_id - gke_cluster_name = var.cluster_name - gke_cluster_location = module.gke.location - - gke_credentials = module.castai-gke-iam.private_key - default_node_configuration_name = "default" - - node_configurations = { - default = { - disk_cpu_ratio = 25 - subnets = [module.vpc.subnets_ids[0]] - } - } - - node_templates = { - default_by_castai = { - name = "default-by-castai" - configuration_name = "default" - is_default = true - is_enabled = true - should_taint = false - - constraints = { - on_demand = true - spot = true - use_spot_fallbacks = true - - enable_spot_diversity = false - spot_diversity_price_increase_limit_percent = 20 - } - } - } - - depends_on = [module.gke, module.castai-gke-iam] -} - diff --git a/hack/terraform/cloud-proxy.tf b/hack/terraform/cloud-proxy.tf deleted file mode 100644 index ed26e25..0000000 --- a/hack/terraform/cloud-proxy.tf +++ /dev/null @@ -1,39 +0,0 @@ -resource "google_project_iam_binding" "cloud_proxy_workload_identity" { - project = var.project_id - role = "roles/container.clusterViewer" - members = [ - "serviceAccount:${var.project_id}.svc.id.goog[castai-agent/castai-cloud-proxy]" - ] -} - -resource "helm_release" "castai_cloud_proxy" { - name = "castai-cloud-proxy" - repository = "https://castai.github.io/helm-charts" - chart = "castai-cloud-proxy" - namespace = "castai-agent" - create_namespace = true - cleanup_on_fail = true - wait = true - - set { - name = "image.tag" - value = "latest" - } - - set { - name = "image.pullPolicy" - value = "Always" - } - - set { - name = "config.grpc.endpoint" - value = var.castai_grpc_url - } - - set_sensitive { - name = "config.grpc.key" - value = var.castai_api_token - } - - depends_on = [module.castai-gke-cluster] -} diff --git a/hack/terraform/gke.tf b/hack/terraform/gke.tf deleted file mode 100644 index 2d6c088..0000000 --- a/hack/terraform/gke.tf +++ /dev/null @@ -1,33 +0,0 @@ -module "gke" { - source = "terraform-google-modules/kubernetes-engine/google" - version = "24.1.0" - project_id = var.project_id - name = var.cluster_name - region = var.cluster_region - //zones = var.cluster_zones - network = module.vpc.network_name - subnetwork = module.vpc.subnets_names[0] - ip_range_pods = local.ip_range_pods - ip_range_services = local.ip_range_services - http_load_balancing = false - network_policy = false - horizontal_pod_autoscaling = true - filestore_csi_driver = false - - node_pools = [ - { - name = "default-node-pool" - machine_type = "e2-standard-2" - min_count = 2 - max_count = 10 - local_ssd_count = 0 - disk_size_gb = 100 - disk_type = "pd-standard" - image_type = "COS_CONTAINERD" - auto_repair = true - auto_upgrade = true - preemptible = false - initial_node_count = 2 # has to be >=2 to successfully deploy CAST AI controller - } - ] -} diff --git a/hack/terraform/terraform.tf b/hack/terraform/terraform.tf deleted file mode 100644 index 502f0c5..0000000 --- a/hack/terraform/terraform.tf +++ /dev/null @@ -1,17 +0,0 @@ -terraform { - required_providers { - castai = { - source = "castai/castai" - } - kubernetes = { - source = "hashicorp/kubernetes" - } - google = { - source = "hashicorp/google" - } - google-beta = { - source = "hashicorp/google-beta" - } - } - required_version = ">= 0.13" -} diff --git a/hack/terraform/vars.tf b/hack/terraform/vars.tf deleted file mode 100644 index c706224..0000000 --- a/hack/terraform/vars.tf +++ /dev/null @@ -1,39 +0,0 @@ -# GKE module variables. -variable "cluster_name" { - type = string - description = "GKE cluster name in GCP project." -} - -variable "cluster_region" { - type = string - default = "europe-central2" - description = "The region to create the cluster." -} - -variable "project_id" { - type = string - default = "engineering-test-353509" - description = "GCP project ID in which GKE cluster would be created." -} - -variable "castai_api_url" { - type = string - description = "URL of alternative CAST AI API to be used during development or testing" -} - -# Variables required for connecting EKS cluster to CAST AI -variable "castai_api_token" { - type = string - description = "CAST AI API token created in console.cast.ai API Access keys section." -} - -variable "castai_grpc_url" { - type = string - description = "CAST AI gRPC URL" -} - -variable "tags" { - type = map(any) - description = "Optional tags for new cluster nodes. This parameter applies only to new nodes - tags for old nodes are not reconciled." - default = {} -} diff --git a/hack/terraform/vpc.tf b/hack/terraform/vpc.tf deleted file mode 100644 index 5fa6a7f..0000000 --- a/hack/terraform/vpc.tf +++ /dev/null @@ -1,33 +0,0 @@ -locals { - ip_range_pods = "${var.cluster_name}-ip-range-pods" - ip_range_services = "${var.cluster_name}-ip-range-services" - ip_range_nodes = "${var.cluster_name}-ip-range-nodes" -} - -module "vpc" { - source = "terraform-google-modules/network/google" - version = "6.0.0" - project_id = var.project_id - network_name = var.cluster_name - subnets = [ - { - subnet_name = local.ip_range_nodes - subnet_ip = "10.0.0.0/16" - subnet_region = var.cluster_region - subnet_private_access = "true" - }, - ] - - secondary_ranges = { - (local.ip_range_nodes) = [ - { - range_name = local.ip_range_pods - ip_cidr_range = "10.20.0.0/16" - }, - { - range_name = local.ip_range_services - ip_cidr_range = "10.30.0.0/24" - } - ] - } -}