From cda258d4656a7c6c57c8e86d1b572d55349445f7 Mon Sep 17 00:00:00 2001 From: Yuwen Ma Date: Fri, 14 Jun 2024 23:50:49 +0000 Subject: [PATCH 1/2] feat: CBWP update with ETag --- apis/cloudbuild/v1alpha1/conversion.go | 2 ++ apis/cloudbuild/v1alpha1/workerpool_types.go | 2 ++ apis/cloudbuild/v1alpha1/zz_generated.deepcopy.go | 5 +++++ ...oudbuildworkerpools.cloudbuild.cnrm.cloud.google.com.yaml | 2 ++ mockgcp/mockcloudbuild/workerpool.go | 4 +++- .../apis/cloudbuild/v1alpha1/cloudbuildworkerpool_types.go | 3 +++ .../apis/cloudbuild/v1alpha1/zz_generated.deepcopy.go | 5 +++++ pkg/controller/direct/cloudbuild/workerpool_controller.go | 1 + .../_generated_object_cloudbuildworkerpool.golden.yaml | 1 + .../basic/cloudbuild/v1alpha1/cloudbuildworkerpool/_http.log | 2 ++ 10 files changed, 26 insertions(+), 1 deletion(-) diff --git a/apis/cloudbuild/v1alpha1/conversion.go b/apis/cloudbuild/v1alpha1/conversion.go index 768b96cbb3..11bc325119 100644 --- a/apis/cloudbuild/v1alpha1/conversion.go +++ b/apis/cloudbuild/v1alpha1/conversion.go @@ -25,6 +25,8 @@ func Convert_WorkerPool_API_v1_To_KRM_status(in *cloudbuildpb.WorkerPool, out *C return nil } out.ObservedState = &CloudBuildWorkerPoolObservedState{} + + out.ObservedState.ETag = &in.Etag if err := Convert_PrivatePoolV1Config_API_v1_To_KRM(in.GetPrivatePoolV1Config(), out.ObservedState); err != nil { return err } diff --git a/apis/cloudbuild/v1alpha1/workerpool_types.go b/apis/cloudbuild/v1alpha1/workerpool_types.go index 6397bfdf3a..16d79d1e4d 100644 --- a/apis/cloudbuild/v1alpha1/workerpool_types.go +++ b/apis/cloudbuild/v1alpha1/workerpool_types.go @@ -86,6 +86,8 @@ type CloudBuildWorkerPoolObservedState struct { // +optional WorkerConfig *WorkerConfig `json:"workerConfig,omitempty"` NetworkConfig *NetworkConfigState `json:"networkConfig,omitempty"` + + ETag *string `json:"eTag,omitempty"` } type NetworkConfigState struct { diff --git a/apis/cloudbuild/v1alpha1/zz_generated.deepcopy.go b/apis/cloudbuild/v1alpha1/zz_generated.deepcopy.go index 2e9aed9c88..e4bbc1fbe6 100644 --- a/apis/cloudbuild/v1alpha1/zz_generated.deepcopy.go +++ b/apis/cloudbuild/v1alpha1/zz_generated.deepcopy.go @@ -92,6 +92,11 @@ func (in *CloudBuildWorkerPoolObservedState) DeepCopyInto(out *CloudBuildWorkerP *out = new(NetworkConfigState) **out = **in } + if in.ETag != nil { + in, out := &in.ETag, &out.ETag + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudBuildWorkerPoolObservedState. diff --git a/config/crds/resources/apiextensions.k8s.io_v1_customresourcedefinition_cloudbuildworkerpools.cloudbuild.cnrm.cloud.google.com.yaml b/config/crds/resources/apiextensions.k8s.io_v1_customresourcedefinition_cloudbuildworkerpools.cloudbuild.cnrm.cloud.google.com.yaml index f072f3b0bb..423e5b9718 100644 --- a/config/crds/resources/apiextensions.k8s.io_v1_customresourcedefinition_cloudbuildworkerpools.cloudbuild.cnrm.cloud.google.com.yaml +++ b/config/crds/resources/apiextensions.k8s.io_v1_customresourcedefinition_cloudbuildworkerpools.cloudbuild.cnrm.cloud.google.com.yaml @@ -180,6 +180,8 @@ spec: description: The creation timestamp of the workerpool. format: date-time type: string + eTag: + type: string networkConfig: properties: egressOption: diff --git a/mockgcp/mockcloudbuild/workerpool.go b/mockgcp/mockcloudbuild/workerpool.go index 393a1c9fda..859b93c0d5 100644 --- a/mockgcp/mockcloudbuild/workerpool.go +++ b/mockgcp/mockcloudbuild/workerpool.go @@ -77,6 +77,7 @@ func (s *CloudBuildV1) CreateWorkerPool(ctx context.Context, req *pb.CreateWorke result.CreateTime = now result.UpdateTime = now result.State = pb.WorkerPool_RUNNING + result.Etag = "W/11111111111111111111111111111111" return result, nil }) } @@ -99,7 +100,7 @@ func (s *CloudBuildV1) UpdateWorkerPool(ctx context.Context, req *pb.UpdateWorke f := target.FieldByName(path) if f.IsValid() && f.CanSet() { switch f.Kind() { - case reflect.Int: + case reflect.Int, reflect.Int64: intVal := source.FieldByName(path).Int() f.SetInt(intVal) case reflect.String: @@ -122,6 +123,7 @@ func (s *CloudBuildV1) UpdateWorkerPool(ctx context.Context, req *pb.UpdateWorke result := proto.Clone(obj).(*pb.WorkerPool) result.UpdateTime = now result.State = pb.WorkerPool_RUNNING + result.Etag = "W/22222222222222222222222222222222" return result, nil }) } diff --git a/pkg/clients/generated/apis/cloudbuild/v1alpha1/cloudbuildworkerpool_types.go b/pkg/clients/generated/apis/cloudbuild/v1alpha1/cloudbuildworkerpool_types.go index 73d62f9f33..e556535409 100644 --- a/pkg/clients/generated/apis/cloudbuild/v1alpha1/cloudbuildworkerpool_types.go +++ b/pkg/clients/generated/apis/cloudbuild/v1alpha1/cloudbuildworkerpool_types.go @@ -94,6 +94,9 @@ type WorkerpoolObservedStateStatus struct { // +optional CreateTime *string `json:"createTime,omitempty"` + // +optional + ETag *string `json:"eTag,omitempty"` + // +optional NetworkConfig *WorkerpoolNetworkConfigStatus `json:"networkConfig,omitempty"` diff --git a/pkg/clients/generated/apis/cloudbuild/v1alpha1/zz_generated.deepcopy.go b/pkg/clients/generated/apis/cloudbuild/v1alpha1/zz_generated.deepcopy.go index 12492f78c1..b5556ca0c6 100644 --- a/pkg/clients/generated/apis/cloudbuild/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/clients/generated/apis/cloudbuild/v1alpha1/zz_generated.deepcopy.go @@ -220,6 +220,11 @@ func (in *WorkerpoolObservedStateStatus) DeepCopyInto(out *WorkerpoolObservedSta *out = new(string) **out = **in } + if in.ETag != nil { + in, out := &in.ETag, &out.ETag + *out = new(string) + **out = **in + } if in.NetworkConfig != nil { in, out := &in.NetworkConfig, &out.NetworkConfig *out = new(WorkerpoolNetworkConfigStatus) diff --git a/pkg/controller/direct/cloudbuild/workerpool_controller.go b/pkg/controller/direct/cloudbuild/workerpool_controller.go index 41e998dace..760b76eca3 100644 --- a/pkg/controller/direct/cloudbuild/workerpool_controller.go +++ b/pkg/controller/direct/cloudbuild/workerpool_controller.go @@ -259,6 +259,7 @@ func (a *Adapter) Update(ctx context.Context, u *unstructured.Unstructured) erro wp := &cloudbuildpb.WorkerPool{ Name: a.fullyQualifiedName(), + Etag: a.actual.Etag, } desired := a.desired.DeepCopy() err := krm.Convert_WorkerPool_KRM_To_API_v1(desired, wp) diff --git a/pkg/test/resourcefixture/testdata/basic/cloudbuild/v1alpha1/cloudbuildworkerpool/_generated_object_cloudbuildworkerpool.golden.yaml b/pkg/test/resourcefixture/testdata/basic/cloudbuild/v1alpha1/cloudbuildworkerpool/_generated_object_cloudbuildworkerpool.golden.yaml index 3acbb4ad51..d287f5b7cd 100644 --- a/pkg/test/resourcefixture/testdata/basic/cloudbuild/v1alpha1/cloudbuildworkerpool/_generated_object_cloudbuildworkerpool.golden.yaml +++ b/pkg/test/resourcefixture/testdata/basic/cloudbuild/v1alpha1/cloudbuildworkerpool/_generated_object_cloudbuildworkerpool.golden.yaml @@ -33,6 +33,7 @@ status: observedGeneration: 2 observedState: createTime: "1970-01-01T00:00:00Z" + eTag: W/22222222222222222222222222222222 networkConfig: egressOption: NO_PUBLIC_EGRESS peeredNetwork: projects/${projectId}/global/networks/computenetwork-${uniqueId} diff --git a/pkg/test/resourcefixture/testdata/basic/cloudbuild/v1alpha1/cloudbuildworkerpool/_http.log b/pkg/test/resourcefixture/testdata/basic/cloudbuild/v1alpha1/cloudbuildworkerpool/_http.log index d38bcce2cf..69b98084c3 100644 --- a/pkg/test/resourcefixture/testdata/basic/cloudbuild/v1alpha1/cloudbuildworkerpool/_http.log +++ b/pkg/test/resourcefixture/testdata/basic/cloudbuild/v1alpha1/cloudbuildworkerpool/_http.log @@ -593,6 +593,7 @@ X-Xss-Protection: 0 "@type": "type.googleapis.com/google.devtools.cloudbuild.v1.WorkerPool", "createTime": "2024-04-01T12:34:56.123456Z", "displayName": "New CloudBuild WorkerPool", + "etag": "abcdef0123A=", "name": "projects/${projectId}/locations/us-central1/workerPools/cloudbuildworkerpool-${uniqueId}", "privatePoolV1Config": { "networkConfig": { @@ -715,6 +716,7 @@ X-Xss-Protection: 0 "response": { "@type": "type.googleapis.com/google.devtools.cloudbuild.v1.WorkerPool", "displayName": "New CloudBuild WorkerPool", + "etag": "abcdef0123A=", "name": "projects/${projectId}/locations/us-central1/workerPools/cloudbuildworkerpool-${uniqueId}", "privatePoolV1Config": { "networkConfig": { From ed008f339b8c754eace2d058c8f1ca03283fb347 Mon Sep 17 00:00:00 2001 From: Yuwen Ma Date: Mon, 17 Jun 2024 21:13:46 +0000 Subject: [PATCH 2/2] add common computeEtag function --- apis/cloudbuild/v1alpha1/workerpool_types.go | 4 +- ...ools.cloudbuild.cnrm.cloud.google.com.yaml | 3 +- mockgcp/common/fields/etag.go | 71 +++++++++++++++++++ mockgcp/mockcloudbuild/workerpool.go | 5 +- .../v1alpha1/cloudbuildworkerpool_types.go | 3 +- .../v1alpha1/zz_generated.deepcopy.go | 4 +- ...ed_object_cloudbuildworkerpool.golden.yaml | 2 +- 7 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 mockgcp/common/fields/etag.go diff --git a/apis/cloudbuild/v1alpha1/workerpool_types.go b/apis/cloudbuild/v1alpha1/workerpool_types.go index 16d79d1e4d..b2fabdce91 100644 --- a/apis/cloudbuild/v1alpha1/workerpool_types.go +++ b/apis/cloudbuild/v1alpha1/workerpool_types.go @@ -87,7 +87,9 @@ type CloudBuildWorkerPoolObservedState struct { WorkerConfig *WorkerConfig `json:"workerConfig,omitempty"` NetworkConfig *NetworkConfigState `json:"networkConfig,omitempty"` - ETag *string `json:"eTag,omitempty"` + /* The Checksum computed by the server, using weak indicator.*/ + // +optional + ETag *string `json:"etag,omitempty"` } type NetworkConfigState struct { diff --git a/config/crds/resources/apiextensions.k8s.io_v1_customresourcedefinition_cloudbuildworkerpools.cloudbuild.cnrm.cloud.google.com.yaml b/config/crds/resources/apiextensions.k8s.io_v1_customresourcedefinition_cloudbuildworkerpools.cloudbuild.cnrm.cloud.google.com.yaml index 423e5b9718..6581240c36 100644 --- a/config/crds/resources/apiextensions.k8s.io_v1_customresourcedefinition_cloudbuildworkerpools.cloudbuild.cnrm.cloud.google.com.yaml +++ b/config/crds/resources/apiextensions.k8s.io_v1_customresourcedefinition_cloudbuildworkerpools.cloudbuild.cnrm.cloud.google.com.yaml @@ -180,7 +180,8 @@ spec: description: The creation timestamp of the workerpool. format: date-time type: string - eTag: + etag: + description: The Checksum computed by the server, using weak indicator. type: string networkConfig: properties: diff --git a/mockgcp/common/fields/etag.go b/mockgcp/common/fields/etag.go new file mode 100644 index 0000000000..8f90639c1e --- /dev/null +++ b/mockgcp/common/fields/etag.go @@ -0,0 +1,71 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fields + +import ( + "crypto/sha256" + "encoding/base64" + "fmt" + + "google.golang.org/protobuf/encoding/prototext" + "google.golang.org/protobuf/proto" +) + +var mustFields = []string{ + "displayName", + "state", +} + +// ComputeEtag computes the etag of the proto object with weak indicator. +func ComputeEtag(obj proto.Message) string { + pb := proto.Clone(obj) + + // ignore dynamic fields like timestampe or uniqueId. + descriptor := pb.ProtoReflect().Descriptor() + fieldDescs := descriptor.Fields() + for i := 0; i < fieldDescs.Len(); i++ { + fieldDesc := fieldDescs.Get(i) + must := false + for _, mustField := range mustFields { + if fieldDesc.JSONName() == mustField { + must = true + } + } + if !must { + pb.ProtoReflect().Clear(fieldDesc) + } + } + + m, err := prototext.Marshal(pb) + if err != nil { + panic(fmt.Sprintf("converting to prototext: %v", err)) + } + h := sha256.Sum256([]byte(m)) + str := base64.StdEncoding.EncodeToString(h[:]) + strong := fmt.Sprintf(`"%s"`, str) // ETag must be quoted. + return "W/" + strong +} + +func ComputeEtagBytes(obj proto.Message) []byte { + return []byte(ComputeEtag(obj)) +} + +func ComputeEtagPtr(obj proto.Message) *string { + return ptrTo(ComputeEtag(obj)) +} + +func ptrTo[T any](t T) *T { + return &t +} diff --git a/mockgcp/mockcloudbuild/workerpool.go b/mockgcp/mockcloudbuild/workerpool.go index 859b93c0d5..674e2950da 100644 --- a/mockgcp/mockcloudbuild/workerpool.go +++ b/mockgcp/mockcloudbuild/workerpool.go @@ -20,6 +20,7 @@ import ( "strings" "cloud.google.com/go/longrunning/autogen/longrunningpb" + "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/fields" "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/projects" pb "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/generated/mockgcp/devtools/cloudbuild/v1" "google.golang.org/grpc/codes" @@ -77,7 +78,7 @@ func (s *CloudBuildV1) CreateWorkerPool(ctx context.Context, req *pb.CreateWorke result.CreateTime = now result.UpdateTime = now result.State = pb.WorkerPool_RUNNING - result.Etag = "W/11111111111111111111111111111111" + result.Etag = fields.ComputeEtag(result) return result, nil }) } @@ -123,7 +124,7 @@ func (s *CloudBuildV1) UpdateWorkerPool(ctx context.Context, req *pb.UpdateWorke result := proto.Clone(obj).(*pb.WorkerPool) result.UpdateTime = now result.State = pb.WorkerPool_RUNNING - result.Etag = "W/22222222222222222222222222222222" + result.Etag = fields.ComputeEtag(result) return result, nil }) } diff --git a/pkg/clients/generated/apis/cloudbuild/v1alpha1/cloudbuildworkerpool_types.go b/pkg/clients/generated/apis/cloudbuild/v1alpha1/cloudbuildworkerpool_types.go index e556535409..38bfd72fb3 100644 --- a/pkg/clients/generated/apis/cloudbuild/v1alpha1/cloudbuildworkerpool_types.go +++ b/pkg/clients/generated/apis/cloudbuild/v1alpha1/cloudbuildworkerpool_types.go @@ -94,8 +94,9 @@ type WorkerpoolObservedStateStatus struct { // +optional CreateTime *string `json:"createTime,omitempty"` + /* The Checksum computed by the server, using weak indicator. */ // +optional - ETag *string `json:"eTag,omitempty"` + Etag *string `json:"etag,omitempty"` // +optional NetworkConfig *WorkerpoolNetworkConfigStatus `json:"networkConfig,omitempty"` diff --git a/pkg/clients/generated/apis/cloudbuild/v1alpha1/zz_generated.deepcopy.go b/pkg/clients/generated/apis/cloudbuild/v1alpha1/zz_generated.deepcopy.go index b5556ca0c6..8eaf16910c 100644 --- a/pkg/clients/generated/apis/cloudbuild/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/clients/generated/apis/cloudbuild/v1alpha1/zz_generated.deepcopy.go @@ -220,8 +220,8 @@ func (in *WorkerpoolObservedStateStatus) DeepCopyInto(out *WorkerpoolObservedSta *out = new(string) **out = **in } - if in.ETag != nil { - in, out := &in.ETag, &out.ETag + if in.Etag != nil { + in, out := &in.Etag, &out.Etag *out = new(string) **out = **in } diff --git a/pkg/test/resourcefixture/testdata/basic/cloudbuild/v1alpha1/cloudbuildworkerpool/_generated_object_cloudbuildworkerpool.golden.yaml b/pkg/test/resourcefixture/testdata/basic/cloudbuild/v1alpha1/cloudbuildworkerpool/_generated_object_cloudbuildworkerpool.golden.yaml index d287f5b7cd..88dcb35386 100644 --- a/pkg/test/resourcefixture/testdata/basic/cloudbuild/v1alpha1/cloudbuildworkerpool/_generated_object_cloudbuildworkerpool.golden.yaml +++ b/pkg/test/resourcefixture/testdata/basic/cloudbuild/v1alpha1/cloudbuildworkerpool/_generated_object_cloudbuildworkerpool.golden.yaml @@ -33,7 +33,7 @@ status: observedGeneration: 2 observedState: createTime: "1970-01-01T00:00:00Z" - eTag: W/22222222222222222222222222222222 + etag: W/"pwzCjvRptOAz64ZilGUmgNZjYVh0LzD3Oh+zEbMPULw=" networkConfig: egressOption: NO_PUBLIC_EGRESS peeredNetwork: projects/${projectId}/global/networks/computenetwork-${uniqueId}