From f5b80a3dc4018e136475f42be5d5f92825a4d2e9 Mon Sep 17 00:00:00 2001 From: rkurduka Date: Fri, 1 Mar 2024 07:52:24 +0000 Subject: [PATCH 1/6] regenerated code again --- apis/v1alpha1/ack-generate-metadata.yaml | 10 +- apis/v1alpha1/generator.yaml | 9 + cmd/controller/main.go | 16 ++ config/controller/deployment.yaml | 12 + config/controller/kustomization.yaml | 2 +- .../services.k8s.aws_adoptedresources.yaml | 206 ++++++++---------- .../bases/services.k8s.aws_fieldexports.yaml | 54 ++--- generator.yaml | 9 + helm/Chart.yaml | 4 +- .../services.k8s.aws_adoptedresources.yaml | 4 +- helm/templates/NOTES.txt | 2 +- helm/templates/deployment.yaml | 12 + helm/values.yaml | 2 +- pkg/resource/model/hooks.go | 120 ++++++++++ pkg/resource/model/sdk.go | 97 ++++++++- .../model/sdk_read_one_post_set_output.go.tpl | 19 ++ .../sdk_update_post_build_request.go.tpl | 3 + .../model/sdk_update_pre_build_request.go.tpl | 5 + test/e2e/tests/test_model.py | 21 ++ 19 files changed, 444 insertions(+), 163 deletions(-) create mode 100644 pkg/resource/model/hooks.go create mode 100644 templates/model/sdk_read_one_post_set_output.go.tpl create mode 100644 templates/model/sdk_update_post_build_request.go.tpl create mode 100644 templates/model/sdk_update_pre_build_request.go.tpl diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index 759548a7..f54f476b 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,13 +1,13 @@ ack_generate_info: - build_date: "2024-02-24T00:04:37Z" - build_hash: f429bd95efc1c7286e7cc973dc174a30490a5521 - go_version: go1.22.0 - version: v0.30.0-3-gf429bd9 + build_date: "2024-03-01T07:39:37Z" + build_hash: c2165b65565ab3a094cfb474c4396f4d14c7ef1e + go_version: go1.21.6 + version: v0.27.1-55-gc2165b6 api_directory_checksum: 731faf4c5d6d6f5140b4e0786127df447f773217 api_version: v1alpha1 aws_sdk_go_version: v1.50.15 generator_config_info: - file_checksum: 0d728ab3662c7e538aff6727f087b54c5969fdcf + file_checksum: 0da0e2290f259a2cf64e436dd8090247fcb92ef4 original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index 5ba794e6..be420b66 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -22,11 +22,20 @@ operations: StopPipelineExecution: operation_type: Delete resource_name: PipelineExecution + AddTags: + operation_type: Update + resource_name: Model resources: Model: hooks: delta_pre_compare: code: customSetDefaults(a, b) + sdk_read_one_post_set_output: + template_path: model/sdk_read_one_post_set_output.go.tpl + sdk_update_pre_build_request: + template_path: model/sdk_update_pre_build_request.go.tpl + sdk_update_post_build_request: + template_path: model/sdk_update_post_build_request.go.tpl exceptions: errors: 404: diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 951b2ee2..a8925c11 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -30,6 +30,7 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrlrt "sigs.k8s.io/controller-runtime" ctrlrtcache "sigs.k8s.io/controller-runtime/pkg/cache" + ctrlrthealthz "sigs.k8s.io/controller-runtime/pkg/healthz" ctrlrtmetrics "sigs.k8s.io/controller-runtime/pkg/metrics" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" ctrlrtwebhook "sigs.k8s.io/controller-runtime/pkg/webhook" @@ -187,6 +188,21 @@ func main() { os.Exit(1) } + if err = mgr.AddHealthzCheck("health", ctrlrthealthz.Ping); err != nil { + setupLog.Error( + err, "unable to set up health check", + "aws.service", awsServiceAlias, + ) + os.Exit(1) + } + if err = mgr.AddReadyzCheck("check", ctrlrthealthz.Ping); err != nil { + setupLog.Error( + err, "unable to set up ready check", + "aws.service", awsServiceAlias, + ) + os.Exit(1) + } + setupLog.Info( "starting manager", "aws.service", awsServiceAlias, diff --git a/config/controller/deployment.yaml b/config/controller/deployment.yaml index 3964b564..6f4d4081 100644 --- a/config/controller/deployment.yaml +++ b/config/controller/deployment.yaml @@ -79,6 +79,18 @@ spec: capabilities: drop: - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 securityContext: seccompProfile: type: RuntimeDefault diff --git a/config/controller/kustomization.yaml b/config/controller/kustomization.yaml index b0507d3d..226a51cd 100644 --- a/config/controller/kustomization.yaml +++ b/config/controller/kustomization.yaml @@ -6,4 +6,4 @@ kind: Kustomization images: - name: controller newName: public.ecr.aws/aws-controllers-k8s/sagemaker-controller - newTag: 1.2.7 + newTag: 0.0.0-non-release-version diff --git a/config/crd/common/bases/services.k8s.aws_adoptedresources.yaml b/config/crd/common/bases/services.k8s.aws_adoptedresources.yaml index 65eff735..7dca541d 100644 --- a/config/crd/common/bases/services.k8s.aws_adoptedresources.yaml +++ b/config/crd/common/bases/services.k8s.aws_adoptedresources.yaml @@ -3,7 +3,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null name: adoptedresources.services.k8s.aws spec: group: services.k8s.aws @@ -20,19 +21,14 @@ spec: description: AdoptedResource is the schema for the AdoptedResource API. properties: apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -46,149 +42,126 @@ spec: additionalKeys: additionalProperties: type: string - description: |- - AdditionalKeys represents any additional arbitrary identifiers used when - describing the target resource. + description: AdditionalKeys represents any additional arbitrary + identifiers used when describing the target resource. type: object arn: - description: |- - ARN is the AWS Resource Name for the resource. It is a globally - unique identifier. + description: ARN is the AWS Resource Name for the resource. It + is a globally unique identifier. type: string nameOrID: - description: |- - NameOrId is a user-supplied string identifier for the resource. It may - or may not be globally unique, depending on the type of resource. + description: NameOrId is a user-supplied string identifier for + the resource. It may or may not be globally unique, depending + on the type of resource. type: string type: object kubernetes: - description: |- - ResourceWithMetadata provides the values necessary to create a - Kubernetes resource and override any of its metadata values. + description: ResourceWithMetadata provides the values necessary to + create a Kubernetes resource and override any of its metadata values. properties: group: type: string kind: type: string metadata: - description: |- - ObjectMeta is metadata that all persisted resources must have, which includes all objects - users must create. - It is not possible to use `metav1.ObjectMeta` inside spec, as the controller-gen - automatically converts this to an arbitrary string-string map. - https://github.com/kubernetes-sigs/controller-tools/issues/385 - - - Active discussion about inclusion of this field in the spec is happening in this PR: - https://github.com/kubernetes-sigs/controller-tools/pull/395 - - - Until this is allowed, or if it never is, we will produce a subset of the object meta - that contains only the fields which the user is allowed to modify in the metadata. + description: "ObjectMeta is metadata that all persisted resources + must have, which includes all objects users must create. It + is not possible to use `metav1.ObjectMeta` inside spec, as the + controller-gen automatically converts this to an arbitrary string-string + map. https://github.com/kubernetes-sigs/controller-tools/issues/385 + \n Active discussion about inclusion of this field in the spec + is happening in this PR: https://github.com/kubernetes-sigs/controller-tools/pull/395 + \n Until this is allowed, or if it never is, we will produce + a subset of the object meta that contains only the fields which + the user is allowed to modify in the metadata." properties: annotations: additionalProperties: type: string - description: |- - Annotations is an unstructured key value map stored with a resource that may be - set by external tools to store and retrieve arbitrary metadata. They are not - queryable and should be preserved when modifying objects. - More info: http://kubernetes.io/docs/user-guide/annotations + description: 'Annotations is an unstructured key value map + stored with a resource that may be set by external tools + to store and retrieve arbitrary metadata. They are not queryable + and should be preserved when modifying objects. More info: + http://kubernetes.io/docs/user-guide/annotations' type: object generateName: - description: |- - GenerateName is an optional prefix, used by the server, to generate a unique - name ONLY IF the Name field has not been provided. - If this field is used, the name returned to the client will be different - than the name passed. This value will also be combined with a unique suffix. - The provided value has the same validation rules as the Name field, - and may be truncated by the length of the suffix required to make the value - unique on the server. - - - If this field is specified and the generated name exists, the server will - NOT return a 409 - instead, it will either return 201 Created or 500 with Reason - ServerTimeout indicating a unique name could not be found in the time allotted, and the client - should retry (optionally after the time indicated in the Retry-After header). - - - Applied only if Name is not specified. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency + description: "GenerateName is an optional prefix, used by + the server, to generate a unique name ONLY IF the Name field + has not been provided. If this field is used, the name returned + to the client will be different than the name passed. This + value will also be combined with a unique suffix. The provided + value has the same validation rules as the Name field, and + may be truncated by the length of the suffix required to + make the value unique on the server. \n If this field is + specified and the generated name exists, the server will + NOT return a 409 - instead, it will either return 201 Created + or 500 with Reason ServerTimeout indicating a unique name + could not be found in the time allotted, and the client + should retry (optionally after the time indicated in the + Retry-After header). \n Applied only if Name is not specified. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency" type: string labels: additionalProperties: type: string - description: |- - Map of string keys and values that can be used to organize and categorize - (scope and select) objects. May match selectors of replication controllers - and services. - More info: http://kubernetes.io/docs/user-guide/labels + description: 'Map of string keys and values that can be used + to organize and categorize (scope and select) objects. May + match selectors of replication controllers and services. + More info: http://kubernetes.io/docs/user-guide/labels' type: object name: - description: |- - Name must be unique within a namespace. Is required when creating resources, although - some resources may allow a client to request the generation of an appropriate name - automatically. Name is primarily intended for creation idempotence and configuration - definition. - Cannot be updated. - More info: http://kubernetes.io/docs/user-guide/identifiers#names + description: 'Name must be unique within a namespace. Is required + when creating resources, although some resources may allow + a client to request the generation of an appropriate name + automatically. Name is primarily intended for creation idempotence + and configuration definition. Cannot be updated. More info: + http://kubernetes.io/docs/user-guide/identifiers#names' type: string namespace: - description: |- - Namespace defines the space within each name must be unique. An empty namespace is - equivalent to the "default" namespace, but "default" is the canonical representation. - Not all objects are required to be scoped to a namespace - the value of this field for - those objects will be empty. - - - Must be a DNS_LABEL. - Cannot be updated. - More info: http://kubernetes.io/docs/user-guide/namespaces + description: "Namespace defines the space within each name + must be unique. An empty namespace is equivalent to the + \"default\" namespace, but \"default\" is the canonical + representation. Not all objects are required to be scoped + to a namespace - the value of this field for those objects + will be empty. \n Must be a DNS_LABEL. Cannot be updated. + More info: http://kubernetes.io/docs/user-guide/namespaces" type: string ownerReferences: - description: |- - List of objects depended by this object. If ALL objects in the list have - been deleted, this object will be garbage collected. If this object is managed by a controller, - then an entry in this list will point to this controller, with the controller field set to true. - There cannot be more than one managing controller. + description: List of objects depended by this object. If ALL + objects in the list have been deleted, this object will + be garbage collected. If this object is managed by a controller, + then an entry in this list will point to this controller, + with the controller field set to true. There cannot be more + than one managing controller. items: - description: |- - OwnerReference contains enough information to let you identify an owning - object. An owning object must be in the same namespace as the dependent, or - be cluster-scoped, so there is no namespace field. + description: OwnerReference contains enough information + to let you identify an owning object. An owning object + must be in the same namespace as the dependent, or be + cluster-scoped, so there is no namespace field. properties: apiVersion: description: API version of the referent. type: string blockOwnerDeletion: - description: |- - If true, AND if the owner has the "foregroundDeletion" finalizer, then - the owner cannot be deleted from the key-value store until this - reference is removed. - See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion - for how the garbage collector interacts with this field and enforces the foreground deletion. - Defaults to false. - To set this field, a user needs "delete" permission of the owner, - otherwise 422 (Unprocessable Entity) will be returned. + description: If true, AND if the owner has the "foregroundDeletion" + finalizer, then the owner cannot be deleted from the + key-value store until this reference is removed. Defaults + to false. To set this field, a user needs "delete" + permission of the owner, otherwise 422 (Unprocessable + Entity) will be returned. type: boolean controller: description: If true, this reference points to the managing controller. type: boolean kind: - description: |- - Kind of the referent. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string name: - description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' type: string uid: - description: |- - UID of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids + description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids' type: string required: - apiVersion @@ -212,14 +185,13 @@ spec: AdoptedResource. properties: conditions: - description: |- - A collection of `ackv1alpha1.Condition` objects that describe the various - terminal states of the adopted resource CR and its target custom resource + description: A collection of `ackv1alpha1.Condition` objects that + describe the various terminal states of the adopted resource CR + and its target custom resource items: - description: |- - Condition is the common struct used by all CRDs managed by ACK service - controllers to indicate terminal states of the CR and its backend AWS - service API resource + description: Condition is the common struct used by all CRDs managed + by ACK service controllers to indicate terminal states of the + CR and its backend AWS service API resource properties: lastTransitionTime: description: Last time the condition transitioned from one status diff --git a/config/crd/common/bases/services.k8s.aws_fieldexports.yaml b/config/crd/common/bases/services.k8s.aws_fieldexports.yaml index 4d3a8f1d..4a7ab61b 100644 --- a/config/crd/common/bases/services.k8s.aws_fieldexports.yaml +++ b/config/crd/common/bases/services.k8s.aws_fieldexports.yaml @@ -3,7 +3,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null name: fieldexports.services.k8s.aws spec: group: services.k8s.aws @@ -20,19 +21,14 @@ spec: description: FieldExport is the schema for the FieldExport API. properties: apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -40,17 +36,15 @@ spec: description: FieldExportSpec defines the desired state of the FieldExport. properties: from: - description: |- - ResourceFieldSelector provides the values necessary to identify an individual - field on an individual K8s resource. + description: ResourceFieldSelector provides the values necessary to + identify an individual field on an individual K8s resource. properties: path: type: string resource: - description: |- - NamespacedResource provides all the values necessary to identify an ACK - resource of a given type (within the same namespace as the custom resource - containing this type). + description: NamespacedResource provides all the values necessary + to identify an ACK resource of a given type (within the same + namespace as the custom resource containing this type). properties: group: type: string @@ -68,18 +62,16 @@ spec: - resource type: object to: - description: |- - FieldExportTarget provides the values necessary to identify the - output path for a field export. + description: FieldExportTarget provides the values necessary to identify + the output path for a field export. properties: key: description: Key overrides the default value (`.`) for the FieldExport target type: string kind: - description: |- - FieldExportOutputType represents all types that can be produced by a field - export operation + description: FieldExportOutputType represents all types that can + be produced by a field export operation enum: - configmap - secret @@ -102,14 +94,12 @@ spec: description: FieldExportStatus defines the observed status of the FieldExport. properties: conditions: - description: |- - A collection of `ackv1alpha1.Condition` objects that describe the various - recoverable states of the field CR + description: A collection of `ackv1alpha1.Condition` objects that + describe the various recoverable states of the field CR items: - description: |- - Condition is the common struct used by all CRDs managed by ACK service - controllers to indicate terminal states of the CR and its backend AWS - service API resource + description: Condition is the common struct used by all CRDs managed + by ACK service controllers to indicate terminal states of the + CR and its backend AWS service API resource properties: lastTransitionTime: description: Last time the condition transitioned from one status diff --git a/generator.yaml b/generator.yaml index 5ba794e6..be420b66 100644 --- a/generator.yaml +++ b/generator.yaml @@ -22,11 +22,20 @@ operations: StopPipelineExecution: operation_type: Delete resource_name: PipelineExecution + AddTags: + operation_type: Update + resource_name: Model resources: Model: hooks: delta_pre_compare: code: customSetDefaults(a, b) + sdk_read_one_post_set_output: + template_path: model/sdk_read_one_post_set_output.go.tpl + sdk_update_pre_build_request: + template_path: model/sdk_update_pre_build_request.go.tpl + sdk_update_post_build_request: + template_path: model/sdk_update_post_build_request.go.tpl exceptions: errors: 404: diff --git a/helm/Chart.yaml b/helm/Chart.yaml index be6b51dc..50c62914 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v1 name: sagemaker-chart description: A Helm chart for the ACK service controller for Amazon SageMaker (SageMaker) -version: 1.2.7 -appVersion: 1.2.7 +version: 0.0.0-non-release-version +appVersion: 0.0.0-non-release-version home: https://github.com/aws-controllers-k8s/sagemaker-controller icon: https://raw.githubusercontent.com/aws/eks-charts/master/docs/logo/aws.png sources: diff --git a/helm/crds/services.k8s.aws_adoptedresources.yaml b/helm/crds/services.k8s.aws_adoptedresources.yaml index 65eff735..272119ee 100644 --- a/helm/crds/services.k8s.aws_adoptedresources.yaml +++ b/helm/crds/services.k8s.aws_adoptedresources.yaml @@ -183,12 +183,12 @@ spec: name: description: |- Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names + More info: http://kubernetes.io/docs/user-guide/identifiers#names type: string uid: description: |- UID of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids + More info: http://kubernetes.io/docs/user-guide/identifiers#uids type: string required: - apiVersion diff --git a/helm/templates/NOTES.txt b/helm/templates/NOTES.txt index 36dfcaa1..d654808f 100644 --- a/helm/templates/NOTES.txt +++ b/helm/templates/NOTES.txt @@ -1,5 +1,5 @@ {{ .Chart.Name }} has been installed. -This chart deploys "public.ecr.aws/aws-controllers-k8s/sagemaker-controller:1.2.7". +This chart deploys "public.ecr.aws/aws-controllers-k8s/sagemaker-controller:0.0.0-non-release-version". Check its status by running: kubectl --namespace {{ .Release.Namespace }} get pods -l "app.kubernetes.io/instance={{ .Release.Name }}" diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml index 3598fdc3..3ccf7bd1 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -132,6 +132,18 @@ spec: capabilities: drop: - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 securityContext: seccompProfile: type: RuntimeDefault diff --git a/helm/values.yaml b/helm/values.yaml index 90d0b12a..b53e8fbf 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -4,7 +4,7 @@ image: repository: public.ecr.aws/aws-controllers-k8s/sagemaker-controller - tag: 1.2.7 + tag: 0.0.0-non-release-version pullPolicy: IfNotPresent pullSecrets: [] diff --git a/pkg/resource/model/hooks.go b/pkg/resource/model/hooks.go new file mode 100644 index 00000000..07e1639d --- /dev/null +++ b/pkg/resource/model/hooks.go @@ -0,0 +1,120 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file 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 model + +import ( + "context" + + ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" + svcapitypes "github.com/aws-controllers-k8s/sagemaker-controller/apis/v1alpha1" + svcsdk "github.com/aws/aws-sdk-go/service/sagemaker" +) + +// deleteTags is used to keep tags in sync by calling Create and Delete API's +func (rm *resourceManager) deleteTags( + ctx context.Context, + desired *resource, + latest *resource, +) (resp *svcsdk.DeleteTagsOutput, err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.syncTags") + defer func(err error) { + exit(err) + }(err) + + resourceId := (*string)(latest.ko.Status.ACKResourceMetadata.ARN) + + toDelete := computeTagsDelta( + desired.ko.Spec.Tags, latest.ko.Spec.Tags, + ) + + if len(toDelete) > 0 { + rlog.Debug("removing tags from model resource", "tags", toDelete) + + keys := make([]*string, len(toDelete)) + for i, raw_key := range toDelete { + keys[i] = raw_key.Key + } + resp, err = rm.sdkapi.DeleteTagsWithContext( + ctx, + &svcsdk.DeleteTagsInput{ + ResourceArn: resourceId, + TagKeys: keys, + }, + ) + rm.metrics.RecordAPICall("UPDATE", "DeleteTags", err) + if err != nil { + return nil, err + } + + return resp, nil + + } + + return nil, nil +} + +// sdkTags converts *svcapitypes.Tag array to a *svcsdk.Tag array +func (rm *resourceManager) sdkTags( + tags []*svcapitypes.Tag, +) (sdktags []*svcsdk.Tag) { + + for _, i := range tags { + sdktag := rm.newTag(*i) + sdktags = append(sdktags, sdktag) + } + + return sdktags +} + +func (rm *resourceManager) newTag( + c svcapitypes.Tag, +) *svcsdk.Tag { + res := &svcsdk.Tag{} + if c.Key != nil { + res.SetKey(*c.Key) + } + if c.Value != nil { + res.SetValue(*c.Value) + } + + return res +} + +// computeTagsDelta returns tags to be added and removed from the resource +func computeTagsDelta( + desired []*svcapitypes.Tag, + latest []*svcapitypes.Tag, +) (toDelete []*svcapitypes.Tag) { + + desiredTags := map[string]string{} + for _, tag := range desired { + desiredTags[*tag.Key] = *tag.Value + } + + latestTags := map[string]string{} + for _, tag := range latest { + latestTags[*tag.Key] = *tag.Value + } + + for _, tag := range latest { + _, ok := desiredTags[*tag.Key] + if !ok { + toDelete = append(toDelete, tag) + } + } + + return toDelete + +} diff --git a/pkg/resource/model/sdk.go b/pkg/resource/model/sdk.go index 3aa1b281..edd1d86d 100644 --- a/pkg/resource/model/sdk.go +++ b/pkg/resource/model/sdk.go @@ -307,6 +307,25 @@ func (rm *resourceManager) sdkFind( } rm.setStatusDefaults(ko) + var resp_tags *svcsdk.ListTagsOutput + + resp_tags, err = rm.sdkapi.ListTagsWithContext(ctx, &svcsdk.ListTagsInput{ResourceArn: resp.ModelArn}) + rm.metrics.RecordAPICall("READ_ONE", "DescribeTags", err) + + if resp_tags != nil { + f6 := []*svcapitypes.Tag{} + for _, f6iter := range resp_tags.Tags { + f6elem := &svcapitypes.Tag{} + if f6iter.Key != nil { + f6elem.Key = f6iter.Key + } + if f6iter.Value != nil { + f6elem.Value = f6iter.Value + } + f6 = append(f6, f6elem) + } + ko.Spec.Tags = f6 + } return &resource{ko}, nil } @@ -601,8 +620,82 @@ func (rm *resourceManager) sdkUpdate( desired *resource, latest *resource, delta *ackcompare.Delta, -) (*resource, error) { - return nil, ackerr.NewTerminalError(ackerr.NotImplemented) +) (updated *resource, err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.sdkUpdate") + defer func() { + exit(err) + }() + // this to handle delete/remove tags + _, err = rm.deleteTags(ctx, desired, latest) + if err != nil { + return nil, err + } + input, err := rm.newUpdateRequestPayload(ctx, desired, delta) + if err != nil { + return nil, err + } + if desired.ko.Status.ACKResourceMetadata.ARN != nil { + input.SetResourceArn(string(*desired.ko.Status.ACKResourceMetadata.ARN)) + } + + var resp *svcsdk.AddTagsOutput + _ = resp + resp, err = rm.sdkapi.AddTagsWithContext(ctx, input) + rm.metrics.RecordAPICall("UPDATE", "AddTags", err) + if err != nil { + return nil, err + } + // Merge in the information we read from the API call above to the copy of + // the original Kubernetes object we passed to the function + ko := desired.ko.DeepCopy() + + if resp.Tags != nil { + f0 := []*svcapitypes.Tag{} + for _, f0iter := range resp.Tags { + f0elem := &svcapitypes.Tag{} + if f0iter.Key != nil { + f0elem.Key = f0iter.Key + } + if f0iter.Value != nil { + f0elem.Value = f0iter.Value + } + f0 = append(f0, f0elem) + } + ko.Spec.Tags = f0 + } else { + ko.Spec.Tags = nil + } + + rm.setStatusDefaults(ko) + return &resource{ko}, nil +} + +// newUpdateRequestPayload returns an SDK-specific struct for the HTTP request +// payload of the Update API call for the resource +func (rm *resourceManager) newUpdateRequestPayload( + ctx context.Context, + r *resource, + delta *ackcompare.Delta, +) (*svcsdk.AddTagsInput, error) { + res := &svcsdk.AddTagsInput{} + + if r.ko.Spec.Tags != nil { + f1 := []*svcsdk.Tag{} + for _, f1iter := range r.ko.Spec.Tags { + f1elem := &svcsdk.Tag{} + if f1iter.Key != nil { + f1elem.SetKey(*f1iter.Key) + } + if f1iter.Value != nil { + f1elem.SetValue(*f1iter.Value) + } + f1 = append(f1, f1elem) + } + res.SetTags(f1) + } + + return res, nil } // sdkDelete deletes the supplied resource in the backend AWS service API diff --git a/templates/model/sdk_read_one_post_set_output.go.tpl b/templates/model/sdk_read_one_post_set_output.go.tpl new file mode 100644 index 00000000..f5d117fb --- /dev/null +++ b/templates/model/sdk_read_one_post_set_output.go.tpl @@ -0,0 +1,19 @@ +var resp_tags *svcsdk.ListTagsOutput + +resp_tags, err = rm.sdkapi.ListTagsWithContext(ctx,&svcsdk.ListTagsInput{ResourceArn: resp.ModelArn}) +rm.metrics.RecordAPICall("READ_ONE", "DescribeTags", err) + +if resp_tags != nil { + f6 := []*svcapitypes.Tag{} + for _, f6iter := range resp_tags.Tags { + f6elem := &svcapitypes.Tag{} + if f6iter.Key != nil { + f6elem.Key = f6iter.Key + } + if f6iter.Value != nil { + f6elem.Value = f6iter.Value + } + f6 = append(f6, f6elem) + } + ko.Spec.Tags = f6 + } \ No newline at end of file diff --git a/templates/model/sdk_update_post_build_request.go.tpl b/templates/model/sdk_update_post_build_request.go.tpl new file mode 100644 index 00000000..1df1587a --- /dev/null +++ b/templates/model/sdk_update_post_build_request.go.tpl @@ -0,0 +1,3 @@ +if desired.ko.Status.ACKResourceMetadata.ARN != nil { + input.SetResourceArn(string(*desired.ko.Status.ACKResourceMetadata.ARN)) +} \ No newline at end of file diff --git a/templates/model/sdk_update_pre_build_request.go.tpl b/templates/model/sdk_update_pre_build_request.go.tpl new file mode 100644 index 00000000..c91ed1aa --- /dev/null +++ b/templates/model/sdk_update_pre_build_request.go.tpl @@ -0,0 +1,5 @@ +// this to handle delete/remove tags +_ , err = rm.deleteTags(ctx,desired,latest) +if err != nil { + return nil, err +} \ No newline at end of file diff --git a/test/e2e/tests/test_model.py b/test/e2e/tests/test_model.py index 02fda114..b4fc71bd 100644 --- a/test/e2e/tests/test_model.py +++ b/test/e2e/tests/test_model.py @@ -70,6 +70,27 @@ def test_create_model(self, xgboost_model): resource_tags = resource["spec"].get("tags", None) assert_tags_in_sync(model_arn, resource_tags) + # Add new tags + resource_tags.append({'key': 'newtagkey', 'value': 'newtagvalue'}) + + updates = { + "spec": {"tags": resource_tags}, + } + + k8s.patch_custom_resource(reference,updates) + time.sleep(cfg.TAG_DELAY_SLEEP) + assert_tags_in_sync(model_arn, resource_tags) + + + # Remove latest added tag + resource_tags = [i for i in resource_tags if not i["key"] == "newtagkey"] + updates = { + "spec": {"tags": resource_tags}, + } + k8s.patch_custom_resource(reference,updates) + time.sleep(cfg.TAG_DELAY_SLEEP) + assert_tags_in_sync(model_arn, resource_tags) + # Delete the k8s resource. assert delete_custom_resource( reference, cfg.DELETE_WAIT_PERIOD, cfg.DELETE_WAIT_LENGTH From d5ca8aef2b602966d82cfc7657d60ecee90f7edd Mon Sep 17 00:00:00 2001 From: rkurduka Date: Fri, 1 Mar 2024 09:20:15 +0000 Subject: [PATCH 2/6] added a hook to handle modeldatasource updates --- apis/v1alpha1/ack-generate-metadata.yaml | 4 ++-- apis/v1alpha1/generator.yaml | 6 +++--- generator.yaml | 6 +++--- pkg/resource/model/delta.go | 3 +++ pkg/resource/model/sdk.go | 4 ++++ templates/model/sdk_read_one_pre_set_output.go.tpl | 7 +++++++ 6 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 templates/model/sdk_read_one_pre_set_output.go.tpl diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index f54f476b..32cc3481 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,5 +1,5 @@ ack_generate_info: - build_date: "2024-03-01T07:39:37Z" + build_date: "2024-03-01T09:05:16Z" build_hash: c2165b65565ab3a094cfb474c4396f4d14c7ef1e go_version: go1.21.6 version: v0.27.1-55-gc2165b6 @@ -7,7 +7,7 @@ api_directory_checksum: 731faf4c5d6d6f5140b4e0786127df447f773217 api_version: v1alpha1 aws_sdk_go_version: v1.50.15 generator_config_info: - file_checksum: 0da0e2290f259a2cf64e436dd8090247fcb92ef4 + file_checksum: 9607a497bf7d01fe8ee0819ac9c0f182204d0118 original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index be420b66..6e94ebef 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -46,9 +46,9 @@ resources: - InvalidParameterValue - MissingParameter fields: - Tags: - compare: - is_ignored: true + # Tags: + # compare: + # is_ignored: true EnableNetworkIsolation: late_initialize: min_backoff_seconds: 5 diff --git a/generator.yaml b/generator.yaml index be420b66..6e94ebef 100644 --- a/generator.yaml +++ b/generator.yaml @@ -46,9 +46,9 @@ resources: - InvalidParameterValue - MissingParameter fields: - Tags: - compare: - is_ignored: true + # Tags: + # compare: + # is_ignored: true EnableNetworkIsolation: late_initialize: min_backoff_seconds: 5 diff --git a/pkg/resource/model/delta.go b/pkg/resource/model/delta.go index e9ad5243..12491f52 100644 --- a/pkg/resource/model/delta.go +++ b/pkg/resource/model/delta.go @@ -209,6 +209,9 @@ func newResourceDelta( } } } + if !ackcompare.MapStringStringEqual(ToACKTags(a.ko.Spec.Tags), ToACKTags(b.ko.Spec.Tags)) { + delta.Add("Spec.Tags", a.ko.Spec.Tags, b.ko.Spec.Tags) + } if ackcompare.HasNilDifference(a.ko.Spec.VPCConfig, b.ko.Spec.VPCConfig) { delta.Add("Spec.VPCConfig", a.ko.Spec.VPCConfig, b.ko.Spec.VPCConfig) } else if a.ko.Spec.VPCConfig != nil && b.ko.Spec.VPCConfig != nil { diff --git a/pkg/resource/model/sdk.go b/pkg/resource/model/sdk.go index edd1d86d..d548c82e 100644 --- a/pkg/resource/model/sdk.go +++ b/pkg/resource/model/sdk.go @@ -90,6 +90,10 @@ func (rm *resourceManager) sdkFind( // the original Kubernetes object we passed to the function ko := r.ko.DeepCopy() + if ko.Spec.PrimaryContainer.ModelDataSource == nil && resp.PrimaryContainer.ModelDataSource != nil { + resp.PrimaryContainer.ModelDataSource = nil + } + if resp.Containers != nil { f0 := []*svcapitypes.ContainerDefinition{} for _, f0iter := range resp.Containers { diff --git a/templates/model/sdk_read_one_pre_set_output.go.tpl b/templates/model/sdk_read_one_pre_set_output.go.tpl new file mode 100644 index 00000000..9b8a4940 --- /dev/null +++ b/templates/model/sdk_read_one_pre_set_output.go.tpl @@ -0,0 +1,7 @@ + +// This is require if only spec.primarycontainer.modeldataurl is in use and not the ko.spec.primarycontainer.ModelDataSource +// in this case, during find "ko.spec.primarycontainer.ModelDataSource" gets updated as well , which creates a new k8s generation + +if ko.Spec.PrimaryContainer.ModelDataSource == nil && resp.PrimaryContainer.ModelDataSource != nil { + resp.PrimaryContainer.ModelDataSource = nil +} \ No newline at end of file From 63768ef3a7bbb5a0ecd976b76db344c59e1108a0 Mon Sep 17 00:00:00 2001 From: rkurduka Date: Fri, 1 Mar 2024 10:20:39 +0000 Subject: [PATCH 3/6] fixed update modeldatasource issue in sdkfind --- apis/v1alpha1/ack-generate-metadata.yaml | 4 ++-- apis/v1alpha1/generator.yaml | 2 ++ generator.yaml | 2 ++ pkg/resource/model/sdk.go | 5 ++++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index 32cc3481..94ce90f6 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,5 +1,5 @@ ack_generate_info: - build_date: "2024-03-01T09:05:16Z" + build_date: "2024-03-01T09:24:39Z" build_hash: c2165b65565ab3a094cfb474c4396f4d14c7ef1e go_version: go1.21.6 version: v0.27.1-55-gc2165b6 @@ -7,7 +7,7 @@ api_directory_checksum: 731faf4c5d6d6f5140b4e0786127df447f773217 api_version: v1alpha1 aws_sdk_go_version: v1.50.15 generator_config_info: - file_checksum: 9607a497bf7d01fe8ee0819ac9c0f182204d0118 + file_checksum: 43c8931df528f38bf2c179722e7878f4615d5584 original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index 6e94ebef..692d6335 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -30,6 +30,8 @@ resources: hooks: delta_pre_compare: code: customSetDefaults(a, b) + sdk_read_one_pre_set_output: + template_path: model/sdk_read_one_pre_set_output.go.tpl sdk_read_one_post_set_output: template_path: model/sdk_read_one_post_set_output.go.tpl sdk_update_pre_build_request: diff --git a/generator.yaml b/generator.yaml index 6e94ebef..692d6335 100644 --- a/generator.yaml +++ b/generator.yaml @@ -30,6 +30,8 @@ resources: hooks: delta_pre_compare: code: customSetDefaults(a, b) + sdk_read_one_pre_set_output: + template_path: model/sdk_read_one_pre_set_output.go.tpl sdk_read_one_post_set_output: template_path: model/sdk_read_one_post_set_output.go.tpl sdk_update_pre_build_request: diff --git a/pkg/resource/model/sdk.go b/pkg/resource/model/sdk.go index d548c82e..473a4f14 100644 --- a/pkg/resource/model/sdk.go +++ b/pkg/resource/model/sdk.go @@ -90,8 +90,11 @@ func (rm *resourceManager) sdkFind( // the original Kubernetes object we passed to the function ko := r.ko.DeepCopy() + // This is require if only spec.primarycontainer.modeldataurl is in use and not the ko.spec.primarycontainer.ModelDataSource + // in this case, during find "ko.spec.primarycontainer.ModelDataSource" gets updated as well , which creates a new k8s generation + if ko.Spec.PrimaryContainer.ModelDataSource == nil && resp.PrimaryContainer.ModelDataSource != nil { - resp.PrimaryContainer.ModelDataSource = nil + resp.PrimaryContainer.ModelDataSource = nil } if resp.Containers != nil { From 0e99f494e55c3905dffa8f01a270b11e715ea457 Mon Sep 17 00:00:00 2001 From: rkurduka Date: Tue, 5 Mar 2024 06:22:04 +0000 Subject: [PATCH 4/6] fixed versioning for code-generator and on helm values --- apis/v1alpha1/ack-generate-metadata.yaml | 4 ++-- cmd/controller/main.go | 1 + config/controller/kustomization.yaml | 2 +- helm/Chart.yaml | 4 ++-- helm/templates/NOTES.txt | 2 +- helm/values.yaml | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index 94ce90f6..8e12f0e3 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,8 +1,8 @@ ack_generate_info: - build_date: "2024-03-01T09:24:39Z" + build_date: "2024-03-05T06:14:37Z" build_hash: c2165b65565ab3a094cfb474c4396f4d14c7ef1e go_version: go1.21.6 - version: v0.27.1-55-gc2165b6 + version: v0.31.0 api_directory_checksum: 731faf4c5d6d6f5140b4e0786127df447f773217 api_version: v1alpha1 aws_sdk_go_version: v1.50.15 diff --git a/cmd/controller/main.go b/cmd/controller/main.go index a8925c11..bcf90804 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -138,6 +138,7 @@ func main() { LeaderElection: ackCfg.EnableLeaderElection, LeaderElectionID: "ack-" + awsServiceAPIGroup, LeaderElectionNamespace: ackCfg.LeaderElectionNamespace, + }) if err != nil { setupLog.Error( diff --git a/config/controller/kustomization.yaml b/config/controller/kustomization.yaml index 226a51cd..b0507d3d 100644 --- a/config/controller/kustomization.yaml +++ b/config/controller/kustomization.yaml @@ -6,4 +6,4 @@ kind: Kustomization images: - name: controller newName: public.ecr.aws/aws-controllers-k8s/sagemaker-controller - newTag: 0.0.0-non-release-version + newTag: 1.2.7 diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 50c62914..be6b51dc 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v1 name: sagemaker-chart description: A Helm chart for the ACK service controller for Amazon SageMaker (SageMaker) -version: 0.0.0-non-release-version -appVersion: 0.0.0-non-release-version +version: 1.2.7 +appVersion: 1.2.7 home: https://github.com/aws-controllers-k8s/sagemaker-controller icon: https://raw.githubusercontent.com/aws/eks-charts/master/docs/logo/aws.png sources: diff --git a/helm/templates/NOTES.txt b/helm/templates/NOTES.txt index d654808f..36dfcaa1 100644 --- a/helm/templates/NOTES.txt +++ b/helm/templates/NOTES.txt @@ -1,5 +1,5 @@ {{ .Chart.Name }} has been installed. -This chart deploys "public.ecr.aws/aws-controllers-k8s/sagemaker-controller:0.0.0-non-release-version". +This chart deploys "public.ecr.aws/aws-controllers-k8s/sagemaker-controller:1.2.7". Check its status by running: kubectl --namespace {{ .Release.Namespace }} get pods -l "app.kubernetes.io/instance={{ .Release.Name }}" diff --git a/helm/values.yaml b/helm/values.yaml index b53e8fbf..90d0b12a 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -4,7 +4,7 @@ image: repository: public.ecr.aws/aws-controllers-k8s/sagemaker-controller - tag: 0.0.0-non-release-version + tag: 1.2.7 pullPolicy: IfNotPresent pullSecrets: [] From 9eca23ca2b19e0bca84836549c21c3dd8b6c7573 Mon Sep 17 00:00:00 2001 From: rkurduka Date: Wed, 6 Mar 2024 08:56:09 +0000 Subject: [PATCH 5/6] added check to validate update field --- cmd/controller/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/controller/main.go b/cmd/controller/main.go index bcf90804..42921b1f 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -138,7 +138,7 @@ func main() { LeaderElection: ackCfg.EnableLeaderElection, LeaderElectionID: "ack-" + awsServiceAPIGroup, LeaderElectionNamespace: ackCfg.LeaderElectionNamespace, - + }) if err != nil { setupLog.Error( From 99037c217949d2cf94caa2c273d4a1d10c73a9e9 Mon Sep 17 00:00:00 2001 From: rkurduka Date: Wed, 6 Mar 2024 09:01:11 +0000 Subject: [PATCH 6/6] added check to validate field for update --- apis/v1alpha1/ack-generate-metadata.yaml | 2 +- pkg/resource/model/sdk.go | 10 ++++++++++ templates/model/sdk_update_pre_build_request.go.tpl | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index 8e12f0e3..ab7f96d2 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,5 +1,5 @@ ack_generate_info: - build_date: "2024-03-05T06:14:37Z" + build_date: "2024-03-06T08:51:16Z" build_hash: c2165b65565ab3a094cfb474c4396f4d14c7ef1e go_version: go1.21.6 version: v0.31.0 diff --git a/pkg/resource/model/sdk.go b/pkg/resource/model/sdk.go index 473a4f14..cd88d69f 100644 --- a/pkg/resource/model/sdk.go +++ b/pkg/resource/model/sdk.go @@ -633,6 +633,16 @@ func (rm *resourceManager) sdkUpdate( defer func() { exit(err) }() + // checking if any delta is found other than tags + if len(delta.Differences) > 0 { + if delta.DifferentExcept("Spec.Tags") { + for _, parts := range delta.Differences { + if !parts.Path.Contains("Tags") { + return nil, fmt.Errorf("cannot update the following fields: %s , Allowed field to change: Spec.Tags", parts.Path) + } + } + } + } // this to handle delete/remove tags _, err = rm.deleteTags(ctx, desired, latest) if err != nil { diff --git a/templates/model/sdk_update_pre_build_request.go.tpl b/templates/model/sdk_update_pre_build_request.go.tpl index c91ed1aa..2fcc810d 100644 --- a/templates/model/sdk_update_pre_build_request.go.tpl +++ b/templates/model/sdk_update_pre_build_request.go.tpl @@ -1,3 +1,13 @@ +// checking if any delta is found other than tags +if len(delta.Differences) > 0 { + if delta.DifferentExcept("Spec.Tags") { + for _, parts := range delta.Differences { + if !parts.Path.Contains("Tags") { + return nil, fmt.Errorf("cannot update the following fields: %s , Allowed field to change: Spec.Tags", parts.Path) + } + } + } +} // this to handle delete/remove tags _ , err = rm.deleteTags(ctx,desired,latest) if err != nil {