diff --git a/.gitignore b/.gitignore index 6f72f89..0823176 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.dll *.so *.dylib +bin/ # Test binary, built with `go test -c` *.test @@ -23,3 +24,6 @@ go.work.sum # env file .env + +# goland +.idea diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4bdab84 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM gcr.io/distroless/static-debian11 + +#FROM ubuntu:latest +# TODO: Multi-arch build + +COPY bin/castai-cloud-proxy-amd64 /usr/local/bin/castai-cloud-proxy +CMD ["castai-cloud-proxy"] diff --git a/Makefile b/Makefile index e69de29..19effdf 100644 --- a/Makefile +++ b/Makefile @@ -0,0 +1,23 @@ +VERSION ?= poc2 +REPO ?= lachezarcast/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 . + +push: + docker push $(REPO):$(VERSION) + +release: build push + +deploy: build push + # Get the latest digest because it doesn't work for some f. reason and put it in the yaml + @DIGEST=$$(docker inspect --format='{{index .RepoDigests 0}}' $(REPO):$(VERSION) | awk -F@ '{print $$2}'); \ + sed "s/{{IMAGE_DIGEST}}/$${DIGEST}/g" dummy_deploy.yaml > tmp.yaml + kubectl apply -f tmp.yaml + rm tmp.yaml + + +generate-grpc: + protoc --go_out=./internal/castai/proto --go-grpc_out=./internal/castai/proto ./internal/castai/proto/proxy.proto diff --git a/README.md b/README.md index 7b6ce86..35c910c 100644 --- a/README.md +++ b/README.md @@ -1 +1,52 @@ -# cloud-proxy \ No newline at end of file +# cloud-proxy + + +## Running the PoC + +PoC has two modes - a "local-only" test and a "mocked CAST" test so far. They are controlled via command-line args, see `cmd/proxy/main.go`. + +The local-only test is good to debug issues with the "replace-credentials" part or workload identity (e.g. does my identity have access?). + +The "mocked CAST" test is useful to simulate the flow running real GRPC connection, albeit local. + +`make deploy` can be used to build, push an image and deploy the test proxy in the cluster kubectx points to currently. +You will have to provide the REPO and VERSION variables as defaults are specific to one developer right now :) + +Easiest way to change modes is to edit dummy_deploy.yaml and redeploy. + +## Enabling workload identity + +The [GCP guide](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) is OK. + +Essential steps are: +- Create a GKE cluster and enable workload identity in settings +- If using existing cluster, update the cluster to use workload identity AND node pools to have GKE metadata server (restarts the nodes) +- Create a SA in k8s (one is provided in dummy_deploy.yaml) +- Grant the SA IAM permissions (or test without them first to see that they are required) + +To grant IAM permissions to the service account: + +Use the following to get the current project number: +```bash +gcloud projects list \ + --filter="$(gcloud config get-value project)" \ + --format="value(PROJECT_NUMBER)" +``` + +```bash +PROJECT_ID=XXX PROJECT_NUMBER=YYYY gcloud projects add-iam-policy-binding projects/$PROJECT_ID \ + --role=roles/container.clusterViewer \ + --member=principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/$PROJECT_ID.svc.id.goog/subject/ns/default/sa/castai-cloud-proxy \ + --condition=None +``` + +**For some reason, above can fail with 404; no time to find out why now.** +Just replace the values in this command manually and it will work: + +```bash +gcloud projects add-iam-policy-binding projects/ \ + --role=roles/container.clusterViewer \ + --member=principal://iam.googleapis.com/projects//locations/global/workloadIdentityPools/.svc.id.goog/subject/ns/default/sa/castai-cloud-proxy \ + --condition=None +``` + diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index 943b369..d0dec73 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -1 +1,76 @@ -package proxy +package main + +import ( + "flag" + "log" + "net/http" + "os" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + "github.com/castai/cloud-proxy/internal/castai/dummy" + "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" +) + +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") +) + +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) + }() + } + + 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) + } + }() + + go func() { + loggerClientProxy := log.New(os.Stderr, "[CLUSTER PROXY] ", log.LstdFlags) + loggerClientProxy.Println("Starting proxy client") + conn, err := grpc.Dial("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) + }() + } + + log.Println("Sleeping for 1h, feel free to kill me") + time.Sleep(1 * time.Hour) +} diff --git a/dummy_deploy.yaml b/dummy_deploy.yaml new file mode 100644 index 0000000..bff636a --- /dev/null +++ b/dummy_deploy.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cloud-proxy-deployment + labels: + app: cloud-proxy +spec: + replicas: 1 + selector: + matchLabels: + app: cloud-proxy + template: + metadata: + labels: + app: cloud-proxy + spec: + serviceAccountName: castai-cloud-proxy + containers: + - name: cloud-proxy-container + image: docker.io/lachezarcast/cloud-proxy@{{IMAGE_DIGEST}} + resources: + requests: + memory: "10Mi" + cpu: "50m" + limits: + memory: "10Mi" + imagePullPolicy: Always + command: + - "castai-cloud-proxy" + args: + - "-sanity-checks=false" + - "-mockcast=true" +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: castai-cloud-proxy + namespace: default \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..74ed9ea --- /dev/null +++ b/go.mod @@ -0,0 +1,39 @@ +module github.com/castai/cloud-proxy + +go 1.22 + +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 + golang.org/x/oauth2 v0.22.0 + google.golang.org/api v0.193.0 + google.golang.org/grpc v1.65.0 + google.golang.org/protobuf v1.34.2 +) + +require ( + cloud.google.com/go/auth v0.9.0 // indirect + 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/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 + 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 + golang.org/x/crypto v0.26.0 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/sync v0.8.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 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..43555c7 --- /dev/null +++ b/go.sum @@ -0,0 +1,164 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ= +cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= +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/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= +cloud.google.com/go/container v1.39.0/go.mod h1:gNgnvs1cRHXjYxrotVm+0nxDfZkqzBbXCffh5WtqieI= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +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/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/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= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +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/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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +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= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0/go.mod h1:BMsdeOxN04K0L5FNUBfjFdvwWGNe/rkmSwH4Aelu/X0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +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= +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/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= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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.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= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.193.0 h1:eOGDoJFsLU+HpCBaDJex2fWiYujAw9KbXgpOAMePoUs= +google.golang.org/api v0.193.0/go.mod h1:Po3YMV1XZx+mTku3cfJrlIYR03wiGrCOsdpC67hjZvw= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +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/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= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +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/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= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/castai/dummy/dispatcher.go b/internal/castai/dummy/dispatcher.go new file mode 100644 index 0000000..12bc8c2 --- /dev/null +++ b/internal/castai/dummy/dispatcher.go @@ -0,0 +1,67 @@ +package dummy + +import ( + "log" + "sync" + + "github.com/castai/cloud-proxy/internal/castai/proto" +) + +type Dispatcher struct { + pendingRequests map[string]chan *proto.HttpResponse + locker sync.Mutex + + proxyRequestChan chan<- *proto.HttpRequest + proxyResponseChan <-chan *proto.HttpResponse + + logger *log.Logger +} + +func NewDispatcher(requestChan chan<- *proto.HttpRequest, responseChan <-chan *proto.HttpResponse, logger *log.Logger) *Dispatcher { + return &Dispatcher{ + pendingRequests: make(map[string]chan *proto.HttpResponse), + locker: sync.Mutex{}, + proxyRequestChan: requestChan, + proxyResponseChan: responseChan, + logger: logger, + } +} + +func (d *Dispatcher) Run() { + go func() { + d.logger.Println("starting response returning loop") + for { + for resp := range d.proxyResponseChan { + waiter := d.findWaiterForResponse(resp.RequestID) + waiter <- resp + d.logger.Println("Sent a response back to caller") + } + } + }() +} + +func (d *Dispatcher) SendRequest(req *proto.HttpRequest) (<-chan *proto.HttpResponse, error) { + waiter := d.addRequestToWaitingList(req.RequestID) + d.proxyRequestChan <- req + return waiter, nil +} + +func (d *Dispatcher) addRequestToWaitingList(requestID string) <-chan *proto.HttpResponse { + waiter := make(chan *proto.HttpResponse, 1) + d.locker.Lock() + d.pendingRequests[requestID] = waiter + d.locker.Unlock() + return waiter +} + +func (d *Dispatcher) findWaiterForResponse(requestID string) chan *proto.HttpResponse { + d.locker.Lock() + val, ok := d.pendingRequests[requestID] + if !ok { + d.logger.Panicln("Trying to send a response for non-existent request", requestID) + } + delete(d.pendingRequests, requestID) + d.locker.Unlock() + + return val +} diff --git a/internal/castai/dummy/external_provisioner_mock.go b/internal/castai/dummy/external_provisioner_mock.go new file mode 100644 index 0000000..9b71dcf --- /dev/null +++ b/internal/castai/dummy/external_provisioner_mock.go @@ -0,0 +1,55 @@ +package dummy + +import ( + "context" + "fmt" + "log" + "math/rand/v2" + "time" + + container "cloud.google.com/go/container/apiv1" + "cloud.google.com/go/container/apiv1/containerpb" + "google.golang.org/api/option" + htransport "google.golang.org/api/transport/http" +) + +const ( + projectID = "engineering-test-353509" + location = "europe-north1-a" + testCluster = "lachezar-2708" +) + +type mockEP struct { + gkeClient *container.ClusterManagerClient + logger *log.Logger +} + +func newMockEP(dispatcher *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) + gkeProxiedClient, err := container.NewClusterManagerRESTClient( + context.Background(), + option.WithoutAuthentication(), + option.WithHTTPClient(httpClient)) + if err != nil { + return nil, err + } + return &mockEP{gkeClient: gkeProxiedClient, logger: logger}, nil +} + +func (m *mockEP) simulateActivity() { + m.logger.Println("Simulating External provisioner call forever...") + for { + getClusterResponse, err := m.gkeClient.GetCluster(context.Background(), &containerpb.GetClusterRequest{ + Name: fmt.Sprintf("projects/%v/locations/%v/clusters/%v", projectID, location, testCluster), + }) + if err != nil { + m.logger.Panicf("%v\n", fmt.Errorf("getting cluster through proxy: %w", err)) + } + m.logger.Printf("Got successful response for cluster via proxy: %v: %v\n", getClusterResponse.Name, getClusterResponse.GetStatus()) + time.Sleep(time.Duration(rand.IntN(5)) * time.Second) + } +} diff --git a/internal/castai/dummy/mock_cast.go b/internal/castai/dummy/mock_cast.go new file mode 100644 index 0000000..2c8b93c --- /dev/null +++ b/internal/castai/dummy/mock_cast.go @@ -0,0 +1,130 @@ +package dummy + +import ( + "fmt" + "io" + "log" + "net" + "os" + "sync" + + "golang.org/x/sync/errgroup" + "google.golang.org/grpc" + + "github.com/castai/cloud-proxy/internal/castai/proto" +) + +// MockCast simulates what cast would do but runs it in the same process: +// - server side of proxy +// - "dispatcher" that uses proxy to send requests +// - "client" that does GCP cloud calls +type MockCast struct { + proxyServer *MockCastServer +} + +func (mc *MockCast) Run() error { + logger := log.New(os.Stderr, "[CAST-MOCK] ", log.LstdFlags) + + requestChan, respChan := make(chan *proto.HttpRequest), make(chan *proto.HttpResponse) + + // Start the mock server + listener, err := net.Listen("tcp", ":50051") + if err != nil { + logger.Panicf("Failed to listen: %v", err) + } + + grpcServer := grpc.NewServer() + proto.RegisterGCPProxyServerServer(grpcServer, NewMockCastServer(requestChan, respChan, logger)) + + dispatcher := NewDispatcher(requestChan, respChan, logger) + + epMock, err := newMockEP(dispatcher, logger) + if err != nil { + logger.Panicf("Failed to create ep mock: %v", err) + } + + var wg sync.WaitGroup + wg.Add(2) + + // Start the "sender" simulation + go func() { + epMock.simulateActivity() + wg.Done() + }() + + // Start the server simulation + go func() { + defer wg.Done() + dispatcher.Run() + + if err := grpcServer.Serve(listener); err != nil { + logger.Panicf("Failed to serve mock cast: %v", err) + } + }() + + wg.Wait() + + return nil +} + +type MockCastServer struct { + proto.UnimplementedGCPProxyServerServer + + requestChan <-chan *proto.HttpRequest + responseChan chan<- *proto.HttpResponse + + logger *log.Logger +} + +func NewMockCastServer(requestChan <-chan *proto.HttpRequest, responseChan chan<- *proto.HttpResponse, logger *log.Logger) *MockCastServer { + return &MockCastServer{ + requestChan: requestChan, + responseChan: responseChan, + logger: logger, + } +} + +func (msrv *MockCastServer) Proxy(stream proto.GCPProxyServer_ProxyServer) error { + msrv.logger.Println("Received a proxy connection from client") + + var eg errgroup.Group + + // TODO: errs + eg.Go(func() error { + msrv.logger.Println("Starting request sender") + + for req := range msrv.requestChan { + msrv.logger.Println("Sending request to cluster proxy client") + + if err := stream.Send(req); err != nil { + msrv.logger.Printf("Error sending request: %v\n", err) + } + } + return nil + }) + + eg.Go(func() error { + msrv.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 { + msrv.logger.Printf("Error in response receiver: %v\n", err) + return err + } + + msrv.logger.Printf("Got a response from client: %v, %v\n", in.RequestID, in.Status) + msrv.responseChan <- in + } + }) + + if err := eg.Wait(); err != nil { + return err + } + msrv.logger.Println("Closing proxy connection") + return nil +} diff --git a/internal/castai/dummy/roundtripper.go b/internal/castai/dummy/roundtripper.go new file mode 100644 index 0000000..93801c6 --- /dev/null +++ b/internal/castai/dummy/roundtripper.go @@ -0,0 +1,74 @@ +package dummy + +import ( + "bytes" + "fmt" + "io" + "log" + "net/http" + "strings" + + "github.com/google/uuid" + + "github.com/castai/cloud-proxy/internal/castai/proto" +) + +type HttpOverGrpcRoundTripper struct { + dispatcher *Dispatcher + + logger *log.Logger +} + +func NewHttpOverGrpcRoundTripper(dispatcher *Dispatcher, logger *log.Logger) *HttpOverGrpcRoundTripper { + return &HttpOverGrpcRoundTripper{dispatcher: dispatcher, logger: logger} +} + +func (p *HttpOverGrpcRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { + 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 + }(), + } + waiter, err := p.dispatcher.SendRequest(protoReq) + if err != nil { + return nil, fmt.Errorf("error sending request: %v", err) + } + + response := <-waiter + p.logger.Println("Received a response back from dispatcher", requestID) + + // Convert to 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/castai/proto/proxy.pb.go b/internal/castai/proto/proxy.pb.go index f8c6cc1..702291a 100644 --- a/internal/castai/proto/proxy.pb.go +++ b/internal/castai/proto/proxy.pb.go @@ -2,7 +2,7 @@ // versions: // protoc-gen-go v1.34.2 // protoc v5.27.3 -// source: proto/proxy.proto +// source: internal/castai/proto/proxy.proto package proto @@ -20,53 +20,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type Ping struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Hello string `protobuf:"bytes,1,opt,name=hello,proto3" json:"hello,omitempty"` -} - -func (x *Ping) Reset() { - *x = Ping{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_proxy_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Ping) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Ping) ProtoMessage() {} - -func (x *Ping) ProtoReflect() protoreflect.Message { - mi := &file_proto_proxy_proto_msgTypes[0] - 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 Ping.ProtoReflect.Descriptor instead. -func (*Ping) Descriptor() ([]byte, []int) { - return file_proto_proxy_proto_rawDescGZIP(), []int{0} -} - -func (x *Ping) GetHello() string { - if x != nil { - return x.Hello - } - return "" -} - type HttpResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -81,7 +34,7 @@ type HttpResponse struct { func (x *HttpResponse) Reset() { *x = HttpResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_proxy_proto_msgTypes[1] + mi := &file_internal_castai_proto_proxy_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -94,7 +47,7 @@ func (x *HttpResponse) String() string { func (*HttpResponse) ProtoMessage() {} func (x *HttpResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_proxy_proto_msgTypes[1] + mi := &file_internal_castai_proto_proxy_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -107,7 +60,7 @@ func (x *HttpResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use HttpResponse.ProtoReflect.Descriptor instead. func (*HttpResponse) Descriptor() ([]byte, []int) { - return file_proto_proxy_proto_rawDescGZIP(), []int{1} + return file_internal_castai_proto_proxy_proto_rawDescGZIP(), []int{0} } func (x *HttpResponse) GetRequestID() string { @@ -153,7 +106,7 @@ type HttpRequest struct { func (x *HttpRequest) Reset() { *x = HttpRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_proxy_proto_msgTypes[2] + mi := &file_internal_castai_proto_proxy_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -166,7 +119,7 @@ func (x *HttpRequest) String() string { func (*HttpRequest) ProtoMessage() {} func (x *HttpRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_proxy_proto_msgTypes[2] + 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 { @@ -179,7 +132,7 @@ func (x *HttpRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use HttpRequest.ProtoReflect.Descriptor instead. func (*HttpRequest) Descriptor() ([]byte, []int) { - return file_proto_proxy_proto_rawDescGZIP(), []int{2} + return file_internal_castai_proto_proxy_proto_rawDescGZIP(), []int{1} } func (x *HttpRequest) GetRequestID() string { @@ -217,105 +170,86 @@ func (x *HttpRequest) GetBody() []byte { return nil } -var File_proto_proxy_proto protoreflect.FileDescriptor - -var file_proto_proxy_proto_rawDesc = []byte{ - 0x0a, 0x11, 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, 0x1c, 0x0a, - 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 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, 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, 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, 0x79, 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, 0x12, 0x2b, 0x0a, 0x09, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x50, 0x69, - 0x6e, 0x67, 0x12, 0x0e, 0x2e, 0x67, 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x50, 0x69, - 0x6e, 0x67, 0x1a, 0x0e, 0x2e, 0x67, 0x63, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x50, 0x69, - 0x6e, 0x67, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, +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, + 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, + 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, } var ( - file_proto_proxy_proto_rawDescOnce sync.Once - file_proto_proxy_proto_rawDescData = file_proto_proxy_proto_rawDesc + file_internal_castai_proto_proxy_proto_rawDescOnce sync.Once + file_internal_castai_proto_proxy_proto_rawDescData = file_internal_castai_proto_proxy_proto_rawDesc ) -func file_proto_proxy_proto_rawDescGZIP() []byte { - file_proto_proxy_proto_rawDescOnce.Do(func() { - file_proto_proxy_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_proxy_proto_rawDescData) +func file_internal_castai_proto_proxy_proto_rawDescGZIP() []byte { + file_internal_castai_proto_proxy_proto_rawDescOnce.Do(func() { + file_internal_castai_proto_proxy_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_castai_proto_proxy_proto_rawDescData) }) - return file_proto_proxy_proto_rawDescData + return file_internal_castai_proto_proxy_proto_rawDescData } -var file_proto_proxy_proto_msgTypes = make([]protoimpl.MessageInfo, 5) -var file_proto_proxy_proto_goTypes = []any{ - (*Ping)(nil), // 0: gcpproxy.Ping - (*HttpResponse)(nil), // 1: gcpproxy.HttpResponse - (*HttpRequest)(nil), // 2: gcpproxy.HttpRequest - nil, // 3: gcpproxy.HttpResponse.HeadersEntry - nil, // 4: gcpproxy.HttpRequest.HeadersEntry +var file_internal_castai_proto_proxy_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +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 } -var file_proto_proxy_proto_depIdxs = []int32{ - 3, // 0: gcpproxy.HttpResponse.headers:type_name -> gcpproxy.HttpResponse.HeadersEntry - 4, // 1: gcpproxy.HttpRequest.headers:type_name -> gcpproxy.HttpRequest.HeadersEntry - 1, // 2: gcpproxy.GCPProxyServer.Proxy:input_type -> gcpproxy.HttpResponse - 0, // 3: gcpproxy.GCPProxyServer.HelloPing:input_type -> gcpproxy.Ping - 2, // 4: gcpproxy.GCPProxyServer.Proxy:output_type -> gcpproxy.HttpRequest - 0, // 5: gcpproxy.GCPProxyServer.HelloPing:output_type -> gcpproxy.Ping - 4, // [4:6] is the sub-list for method output_type - 2, // [2:4] is the sub-list for method input_type +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 } -func init() { file_proto_proxy_proto_init() } -func file_proto_proxy_proto_init() { - if File_proto_proxy_proto != nil { +func init() { file_internal_castai_proto_proxy_proto_init() } +func file_internal_castai_proto_proxy_proto_init() { + if File_internal_castai_proto_proxy_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_proto_proxy_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*Ping); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_proto_proxy_proto_msgTypes[1].Exporter = func(v any, i int) any { + file_internal_castai_proto_proxy_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*HttpResponse); i { case 0: return &v.state @@ -327,7 +261,7 @@ func file_proto_proxy_proto_init() { return nil } } - file_proto_proxy_proto_msgTypes[2].Exporter = func(v any, i int) any { + file_internal_castai_proto_proxy_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*HttpRequest); i { case 0: return &v.state @@ -344,18 +278,18 @@ func file_proto_proxy_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_proto_proxy_proto_rawDesc, + RawDescriptor: file_internal_castai_proto_proxy_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 4, NumExtensions: 0, NumServices: 1, }, - GoTypes: file_proto_proxy_proto_goTypes, - DependencyIndexes: file_proto_proxy_proto_depIdxs, - MessageInfos: file_proto_proxy_proto_msgTypes, + GoTypes: file_internal_castai_proto_proxy_proto_goTypes, + DependencyIndexes: file_internal_castai_proto_proxy_proto_depIdxs, + MessageInfos: file_internal_castai_proto_proxy_proto_msgTypes, }.Build() - File_proto_proxy_proto = out.File - file_proto_proxy_proto_rawDesc = nil - file_proto_proxy_proto_goTypes = nil - file_proto_proxy_proto_depIdxs = nil + File_internal_castai_proto_proxy_proto = out.File + file_internal_castai_proto_proxy_proto_rawDesc = nil + file_internal_castai_proto_proxy_proto_goTypes = nil + file_internal_castai_proto_proxy_proto_depIdxs = nil } diff --git a/internal/castai/proto/proxy.proto b/internal/castai/proto/proxy.proto index 42db836..bee1289 100644 --- a/internal/castai/proto/proxy.proto +++ b/internal/castai/proto/proxy.proto @@ -4,14 +4,11 @@ package gcpproxy; option go_package = ".;proto"; -service GCPProxyServer { - rpc Proxy(stream HttpResponse) returns (stream HttpRequest); - rpc HelloPing(Ping) returns (Ping); -} +// TODO: Keep-alives -message Ping { - string hello = 1; +service GCPProxyServer { + rpc Proxy(stream HttpResponse) returns (stream HttpRequest); } message HttpResponse { diff --git a/internal/castai/proto/proxy_grpc.pb.go b/internal/castai/proto/proxy_grpc.pb.go index cd443d8..9bc4260 100644 --- a/internal/castai/proto/proxy_grpc.pb.go +++ b/internal/castai/proto/proxy_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.5.1 // - protoc v5.27.3 -// source: proto/proxy.proto +// source: internal/castai/proto/proxy.proto package proto @@ -19,8 +19,7 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - GCPProxyServer_Proxy_FullMethodName = "/gcpproxy.GCPProxyServer/Proxy" - GCPProxyServer_HelloPing_FullMethodName = "/gcpproxy.GCPProxyServer/HelloPing" + GCPProxyServer_Proxy_FullMethodName = "/gcpproxy.GCPProxyServer/Proxy" ) // GCPProxyServerClient is the client API for GCPProxyServer service. @@ -28,7 +27,6 @@ const ( // 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) - HelloPing(ctx context.Context, in *Ping, opts ...grpc.CallOption) (*Ping, error) } type gCPProxyServerClient struct { @@ -52,22 +50,11 @@ func (c *gCPProxyServerClient) Proxy(ctx context.Context, opts ...grpc.CallOptio // 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] -func (c *gCPProxyServerClient) HelloPing(ctx context.Context, in *Ping, opts ...grpc.CallOption) (*Ping, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(Ping) - err := c.cc.Invoke(ctx, GCPProxyServer_HelloPing_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - // GCPProxyServerServer is the server API for GCPProxyServer service. // All implementations must embed UnimplementedGCPProxyServerServer // for forward compatibility. type GCPProxyServerServer interface { Proxy(grpc.BidiStreamingServer[HttpResponse, HttpRequest]) error - HelloPing(context.Context, *Ping) (*Ping, error) mustEmbedUnimplementedGCPProxyServerServer() } @@ -81,9 +68,6 @@ type UnimplementedGCPProxyServerServer struct{} func (UnimplementedGCPProxyServerServer) Proxy(grpc.BidiStreamingServer[HttpResponse, HttpRequest]) error { return status.Errorf(codes.Unimplemented, "method Proxy not implemented") } -func (UnimplementedGCPProxyServerServer) HelloPing(context.Context, *Ping) (*Ping, error) { - return nil, status.Errorf(codes.Unimplemented, "method HelloPing not implemented") -} func (UnimplementedGCPProxyServerServer) mustEmbedUnimplementedGCPProxyServerServer() {} func (UnimplementedGCPProxyServerServer) testEmbeddedByValue() {} @@ -112,36 +96,13 @@ func _GCPProxyServer_Proxy_Handler(srv interface{}, stream grpc.ServerStream) er // 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] -func _GCPProxyServer_HelloPing_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Ping) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(GCPProxyServerServer).HelloPing(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: GCPProxyServer_HelloPing_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(GCPProxyServerServer).HelloPing(ctx, req.(*Ping)) - } - return interceptor(ctx, in, info, handler) -} - // GCPProxyServer_ServiceDesc is the grpc.ServiceDesc for GCPProxyServer 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), - Methods: []grpc.MethodDesc{ - { - MethodName: "HelloPing", - Handler: _GCPProxyServer_HelloPing_Handler, - }, - }, + Methods: []grpc.MethodDesc{}, Streams: []grpc.StreamDesc{ { StreamName: "Proxy", @@ -150,5 +111,5 @@ var GCPProxyServer_ServiceDesc = grpc.ServiceDesc{ ClientStreams: true, }, }, - Metadata: "proto/proxy.proto", + Metadata: "internal/castai/proto/proxy.proto", } diff --git a/internal/castai/proxy_client.go b/internal/castai/proxy_client.go deleted file mode 100644 index 9d30295..0000000 --- a/internal/castai/proxy_client.go +++ /dev/null @@ -1 +0,0 @@ -package castai diff --git a/internal/gcpauth/auth.go b/internal/gcpauth/auth.go index c1bc255..dd3dd67 100644 --- a/internal/gcpauth/auth.go +++ b/internal/gcpauth/auth.go @@ -1 +1,21 @@ package gcpauth + +import ( + "context" + "fmt" + + "golang.org/x/oauth2/google" +) + +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()) + if err != nil { + return nil, fmt.Errorf("could not load default credentials: %w", err) + } + return defaultCreds, nil +} diff --git a/internal/localtest/roundtripper.go b/internal/localtest/roundtripper.go new file mode 100644 index 0000000..6c996a8 --- /dev/null +++ b/internal/localtest/roundtripper.go @@ -0,0 +1,72 @@ +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 new file mode 100644 index 0000000..db33171 --- /dev/null +++ b/internal/localtest/run.go @@ -0,0 +1,124 @@ +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 new file mode 100644 index 0000000..2cfd557 --- /dev/null +++ b/internal/proxy/executor.go @@ -0,0 +1,72 @@ +package proxy + +import ( + "bytes" + "fmt" + "io" + "net/http" + "strings" + + "github.com/castai/cloud-proxy/internal/castai/proto" + "github.com/castai/cloud-proxy/internal/gcpauth" +) + +type Executor struct { + credentialsSrc gcpauth.GCPCredentialsSource + client *http.Client +} + +func NewExecutor(credentialsSrc gcpauth.GCPCredentialsSource, client *http.Client) *Executor { + return &Executor{credentialsSrc: credentialsSrc, client: client} +} + +func (e *Executor) DoRequest(request *proto.HttpRequest) (*proto.HttpResponse, error) { + credentials, err := e.credentialsSrc.GetDefaultCredentials() + if err != nil { + return nil, fmt.Errorf("cannot load GCP credentials: %w", err) + } + + token, err := credentials.TokenSource.Token() + if err != nil { + 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)) + if err != nil { + return nil, fmt.Errorf("cannot create http request: %w", err) + } + for header, val := range request.Headers { + httpReq.Header.Add(header, val) + } + // Set the authorize header manually since we can't rely on mothership auth + httpReq.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) + if err != nil { + return nil, fmt.Errorf("unexpected err for %+v: %w", request, err) + } + + 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, ",") + } + + return response, nil +} diff --git a/internal/proxy/proxy_client.go b/internal/proxy/proxy_client.go new file mode 100644 index 0000000..07b8630 --- /dev/null +++ b/internal/proxy/proxy_client.go @@ -0,0 +1,73 @@ +package proxy + +import ( + "context" + "io" + "log" + "time" + + "google.golang.org/grpc" + + "github.com/castai/cloud-proxy/internal/castai/proto" +) + +type Client struct { + executor *Executor + + logger *log.Logger +} + +func NewClient(executor *Executor, logger *log.Logger) *Client { + return &Client{executor: executor, logger: logger} +} + +func (client *Client) Run(grpcConn *grpc.ClientConn) { + grpcClient := proto.NewGCPProxyServerClient(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()) + if err != nil { + client.logger.Printf("error connecting to castai: %v\n", err) + continue + } + + // Inner loop handles "per-message" execution + for { + in, err := stream.Recv() + if err == io.EOF { + client.logger.Println("Reconnecting") + break + } + if err != nil { + client.logger.Printf("error receiving from castai: %v; closing stream\n", err) + err = stream.CloseSend() + if err != nil { + client.logger.Println("error closing stream", err) + } + // Reconnect by stopping inner loop + break + } + + go func() { + client.logger.Println("Received message from server for proxying:", in.RequestID) + 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) + + err = stream.Send(resp) + if err != nil { + client.logger.Println("error sending response to CAST", err) + return + } + }() + } + } +}