diff --git a/pkg/apis/cartographer/v1alpha1/workload_helpers.go b/pkg/apis/cartographer/v1alpha1/workload_helpers.go index 9fbbf2a0f..6f80aa1ab 100644 --- a/pkg/apis/cartographer/v1alpha1/workload_helpers.go +++ b/pkg/apis/cartographer/v1alpha1/workload_helpers.go @@ -219,6 +219,19 @@ func (w *WorkloadSpec) Merge(updates *WorkloadSpec) { for _, p := range updates.Params { w.MergeParams(p.Name, p.Value) } + sp := "" + if w.Source != nil { + sp = w.Source.Subpath + } + if updates.Image != "" { + w.MergeImage(updates.Image) + } + if updates.Source != nil && (updates.Source.Git != nil || updates.Source.Image != "") && sp != "" { + if w.Source == nil { + w.Source = &Source{} + } + w.Source.Subpath = sp + } if updates.Source != nil { s := updates.Source.DeepCopy() if s.Git != nil { @@ -227,13 +240,10 @@ func (w *WorkloadSpec) Merge(updates *WorkloadSpec) { if s.Image != "" { w.MergeSourceImage(s.Image) } - if s.Subpath != "" { + if s.Subpath != "" && w.Source != nil && (w.Source.Git != nil || w.Source.Image != "") { w.MergeSubPath(s.Subpath) } } - if updates.Image != "" { - w.MergeImage(updates.Image) - } for _, e := range updates.Env { w.MergeEnv(e) } diff --git a/pkg/apis/cartographer/v1alpha1/workload_test.go b/pkg/apis/cartographer/v1alpha1/workload_test.go index 3bdf34bde..738adc27c 100644 --- a/pkg/apis/cartographer/v1alpha1/workload_test.go +++ b/pkg/apis/cartographer/v1alpha1/workload_test.go @@ -1034,6 +1034,101 @@ func TestWorkload_Merge(t *testing.T) { Image: "ubuntu:bionic", }, }, + }, { + name: "image with sources nill", + seed: &Workload{ + Spec: WorkloadSpec{ + Image: "alpine:latest", + }, + }, + update: &Workload{ + Spec: WorkloadSpec{ + Image: "ubuntu:bionic", + Source: &Source{Subpath: "my-subpath"}, + }, + }, + want: &Workload{ + Spec: WorkloadSpec{ + Image: "ubuntu:bionic", + }, + }, + }, { + name: "image with sources source image and subpath", + seed: &Workload{ + Spec: WorkloadSpec{ + Image: "alpine:latest", + }, + }, + update: &Workload{ + Spec: WorkloadSpec{ + Image: "ubuntu:bionic", + Source: &Source{ + Image: "ubuntu:bionic", + Subpath: "my-subpath", + }, + }, + }, + want: &Workload{ + Spec: WorkloadSpec{ + Source: &Source{ + Image: "ubuntu:bionic", + Subpath: "my-subpath", + }, + }, + }, + }, { + name: "image update sources source image and subpath", + seed: &Workload{ + Spec: WorkloadSpec{ + Image: "alpine:latest", + Source: &Source{ + Subpath: "new-subpath", + }, + }, + }, + update: &Workload{ + Spec: WorkloadSpec{ + Image: "ubuntu:bionic", + Source: &Source{ + Image: "ubuntu:bionic", + Subpath: "my-subpath", + }, + }, + }, + want: &Workload{ + Spec: WorkloadSpec{ + Source: &Source{ + Image: "ubuntu:bionic", + Subpath: "my-subpath", + }, + }, + }, + }, { + name: "local source image with subpath update source image and subpath", + seed: &Workload{ + Spec: WorkloadSpec{ + Source: &Source{ + Image: "ubuntu:bionic", + Subpath: "/opt", + }, + }, + }, + update: &Workload{ + Spec: WorkloadSpec{ + Source: &Source{ + Image: "ubuntu:bionic", + Subpath: "/sys", + }, + }, + }, + want: &Workload{ + Spec: WorkloadSpec{ + Source: &Source{ + Image: "ubuntu:bionic", + Subpath: "/sys", + }, + }, + }, }, { name: "git", seed: &Workload{ @@ -1092,13 +1187,7 @@ func TestWorkload_Merge(t *testing.T) { }, }, }, - want: &Workload{ - Spec: WorkloadSpec{ - Source: &Source{ - Subpath: "test-path", - }, - }, - }, + want: &Workload{}, }, { name: "env", seed: &Workload{ diff --git a/pkg/commands/testdata/workload-lsp-image-non-subPath.yaml b/pkg/commands/testdata/workload-lsp-image-non-subPath.yaml new file mode 100644 index 000000000..275c93965 --- /dev/null +++ b/pkg/commands/testdata/workload-lsp-image-non-subPath.yaml @@ -0,0 +1,31 @@ +# Copyright 2023 VMware, Inc. +# +# 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. + +apiVersion: carto.run/v1alpha1 +kind: Workload +metadata: + annotations: + local-source-proxy.apps.tanzu.vmware.com: :default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69 + labels: + apps.tanzu.vmware.com/workload-type: web + name: my-workload + namespace: default +spec: + params: + - name: annotations + value: + autoscaling.knative.dev/minScale: "2" + image: my-registry/default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69 + source: + image: :default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69 diff --git a/pkg/commands/workload_apply_test.go b/pkg/commands/workload_apply_test.go index 2d66c12d4..3aa8b4e6b 100644 --- a/pkg/commands/workload_apply_test.go +++ b/pkg/commands/workload_apply_test.go @@ -9048,6 +9048,297 @@ To get status: "tanzu apps workload get my-workload" `, localSource), }, + { + Name: "update from local source using lsp from file with image", + Skip: runtm.GOOS == "windows", + Args: []string{workloadName, flags.LocalPathFlagName, localSource, flags.FilePathFlagName, "./testdata/workload-lsp-image-non-subPath.yaml", flags.YesFlagName}, + GivenObjects: []client.Object{ + parent. + MetadataDie(func(d *diemetav1.ObjectMetaDie) { + d.Annotations(map[string]string{apis.LocalSourceProxyAnnotationName: ":default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69"}) + d.Labels(map[string]string{apis.WorkloadTypeLabelName: "web"}) + }).SpecDie(func(d *diecartov1alpha1.WorkloadSpecDie) { + d.Source(&cartov1alpha1.Source{ + Image: ":default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69", + Subpath: "current-subpath", + }) + }), + }, + KubeConfigTransport: clitesting.NewFakeTransportFromResponse(respCreator(http.StatusOK, `{"statuscode": "200", "message": "any ignored message"}`, myWorkloadHeader)), + ExpectUpdates: []client.Object{ + &cartov1alpha1.Workload{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: defaultNamespace, + Name: workloadName, + Labels: map[string]string{ + apis.WorkloadTypeLabelName: "web", + }, + Annotations: map[string]string{ + "local-source-proxy.apps.tanzu.vmware.com": ":default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69", + }, + }, + Spec: cartov1alpha1.WorkloadSpec{ + Source: &cartov1alpha1.Source{ + Image: ":default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69", + Subpath: "current-subpath", + }, + Params: []cartov1alpha1.Param{ + { + Name: "annotations", + Value: apiextensionsv1.JSON{Raw: []byte(`{"autoscaling.knative.dev/minScale":"2"}`)}, + }, + }, + }, + }, + }, + ExpectOutput: fmt.Sprintf(` +❗ WARNING: Configuration file update strategy is changing. By default, provided configuration files will replace rather than merge existing configuration. The change will take place in the January 2024 TAP release (use "--update-strategy" to control strategy explicitly). + +Publishing source in "%s" to "local-source-proxy.tap-local-source-system.svc.cluster.local/source:default-my-workload"... +No source code is changed + +🔎 Update workload: +... + 8, 8 | apps.tanzu.vmware.com/workload-type: web + 9, 9 | name: my-workload + 10, 10 | namespace: default + 11, 11 |spec: + 12 + | params: + 13 + | - name: annotations + 14 + | value: + 15 + | autoscaling.knative.dev/minScale: "2" + 12, 16 | source: + 13, 17 | image: :default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69 + 14, 18 | subPath: current-subpath +👍 Updated workload "my-workload" + +To see logs: "tanzu apps workload tail my-workload --timestamp --since 1h" +To get status: "tanzu apps workload get my-workload" + +`, localSource), + }, + { + Name: "update image to local source with subpath using flags", + Skip: runtm.GOOS == "windows", + Args: []string{workloadName, flags.LocalPathFlagName, localSource, flags.SubPathFlagName, subpath, flags.YesFlagName}, + GivenObjects: []client.Object{ + parent. + MetadataDie(func(d *diemetav1.ObjectMetaDie) { + d.Labels(map[string]string{apis.WorkloadTypeLabelName: "web"}) + }).SpecDie(func(d *diecartov1alpha1.WorkloadSpecDie) { + d.Image(":default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69") + }), + }, + KubeConfigTransport: clitesting.NewFakeTransportFromResponse(respCreator(http.StatusOK, `{"statuscode": "200", "message": "any ignored message"}`, myWorkloadHeader)), + ExpectUpdates: []client.Object{ + parent. + MetadataDie(func(d *diemetav1.ObjectMetaDie) { + d.Annotations(map[string]string{apis.LocalSourceProxyAnnotationName: ":default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69"}) + d.Labels(map[string]string{apis.WorkloadTypeLabelName: "web"}) + }).SpecDie(func(d *diecartov1alpha1.WorkloadSpecDie) { + d.Source( + &cartov1alpha1.Source{ + Image: ":default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69", + Subpath: subpath, + }, + ) + }), + }, + ExpectOutput: fmt.Sprintf(` +Publishing source in "%s" to "local-source-proxy.tap-local-source-system.svc.cluster.local/source:default-my-workload"... +📥 Published source + +🔎 Update workload: + 1, 1 |--- + 2, 2 |apiVersion: carto.run/v1alpha1 + 3, 3 |kind: Workload + 4, 4 |metadata: + 5 + | annotations: + 6 + | local-source-proxy.apps.tanzu.vmware.com: :default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69 + 5, 7 | labels: + 6, 8 | apps.tanzu.vmware.com/workload-type: web + 7, 9 | name: my-workload + 8, 10 | namespace: default + 9, 11 |spec: + 10 - | image: :default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69 + 12 + | source: + 13 + | image: :default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69 + 14 + | subPath: testdata/local-source/subpath +👍 Updated workload "my-workload" + +To see logs: "tanzu apps workload tail my-workload --timestamp --since 1h" +To get status: "tanzu apps workload get my-workload" + +`, localSource), + }, + { + Name: "update image to git with subpath using flags", + Skip: runtm.GOOS == "windows", + Args: []string{workloadName, flags.GitBranchFlagName, "main", flags.GitRepoFlagName, "my-repo-server/my-repo", flags.SubPathFlagName, subpath, flags.YesFlagName}, + GivenObjects: []client.Object{ + parent. + MetadataDie(func(d *diemetav1.ObjectMetaDie) { + d.Labels(map[string]string{apis.WorkloadTypeLabelName: "web"}) + }).SpecDie(func(d *diecartov1alpha1.WorkloadSpecDie) { + d.Image(":default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69") + }), + }, + KubeConfigTransport: clitesting.NewFakeTransportFromResponse(respCreator(http.StatusOK, `{"statuscode": "200", "message": "any ignored message"}`, myWorkloadHeader)), + ExpectUpdates: []client.Object{ + parent. + MetadataDie(func(d *diemetav1.ObjectMetaDie) { + d.Labels(map[string]string{apis.WorkloadTypeLabelName: "web"}) + }).SpecDie(func(d *diecartov1alpha1.WorkloadSpecDie) { + d.Source( + &cartov1alpha1.Source{ + Git: &cartov1alpha1.GitSource{ + URL: "my-repo-server/my-repo", + Ref: cartov1alpha1.GitRef{ + Branch: "main", + }, + }, + Subpath: subpath, + }, + ) + }), + }, + ExpectOutput: ` +🔎 Update workload: +... + 6, 6 | apps.tanzu.vmware.com/workload-type: web + 7, 7 | name: my-workload + 8, 8 | namespace: default + 9, 9 |spec: + 10 - | image: :default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69 + 10 + | source: + 11 + | git: + 12 + | ref: + 13 + | branch: main + 14 + | url: my-repo-server/my-repo + 15 + | subPath: testdata/local-source/subpath +👍 Updated workload "my-workload" + +To see logs: "tanzu apps workload tail my-workload --timestamp --since 1h" +To get status: "tanzu apps workload get my-workload" + +`, + }, + { + Name: "update local source to git without changing subpath using flags", + Skip: runtm.GOOS == "windows", + Args: []string{workloadName, flags.GitBranchFlagName, "main", flags.GitRepoFlagName, "my-repo-server/my-repo", flags.YesFlagName}, + GivenObjects: []client.Object{ + parent. + MetadataDie(func(d *diemetav1.ObjectMetaDie) { + d.Annotations(map[string]string{apis.LocalSourceProxyAnnotationName: ":default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69"}) + d.Labels(map[string]string{apis.WorkloadTypeLabelName: "web"}) + }).SpecDie(func(d *diecartov1alpha1.WorkloadSpecDie) { + d.Source( + &cartov1alpha1.Source{ + Image: ":default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69", + Subpath: subpath, + }, + ) + }), + }, + KubeConfigTransport: clitesting.NewFakeTransportFromResponse(respCreator(http.StatusOK, `{"statuscode": "200", "message": "any ignored message"}`, myWorkloadHeader)), + ExpectUpdates: []client.Object{ + parent. + MetadataDie(func(d *diemetav1.ObjectMetaDie) { + d.Labels(map[string]string{apis.WorkloadTypeLabelName: "web"}) + }).SpecDie(func(d *diecartov1alpha1.WorkloadSpecDie) { + d.Source( + &cartov1alpha1.Source{ + Git: &cartov1alpha1.GitSource{ + URL: "my-repo-server/my-repo", + Ref: cartov1alpha1.GitRef{ + Branch: "main", + }, + }, + Subpath: subpath, + }, + ) + }), + }, + ExpectOutput: ` +🔎 Update workload: + 1, 1 |--- + 2, 2 |apiVersion: carto.run/v1alpha1 + 3, 3 |kind: Workload + 4, 4 |metadata: + 5 - | annotations: + 6 - | local-source-proxy.apps.tanzu.vmware.com: :default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69 + 7, 5 | labels: + 8, 6 | apps.tanzu.vmware.com/workload-type: web + 9, 7 | name: my-workload + 10, 8 | namespace: default + 11, 9 |spec: + 12, 10 | source: + 13 - | image: :default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69 + 11 + | git: + 12 + | ref: + 13 + | branch: main + 14 + | url: my-repo-server/my-repo + 14, 15 | subPath: testdata/local-source/subpath +👍 Updated workload "my-workload" + +To see logs: "tanzu apps workload tail my-workload --timestamp --since 1h" +To get status: "tanzu apps workload get my-workload" + +`, + }, + { + Name: "update local source to image using flags", + Skip: runtm.GOOS == "windows", + Args: []string{workloadName, flags.ImageFlagName, ":default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69", flags.YesFlagName}, + GivenObjects: []client.Object{ + parent. + MetadataDie(func(d *diemetav1.ObjectMetaDie) { + d.Annotations(map[string]string{apis.LocalSourceProxyAnnotationName: ":default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69"}) + d.Labels(map[string]string{apis.WorkloadTypeLabelName: "web"}) + }).SpecDie(func(d *diecartov1alpha1.WorkloadSpecDie) { + d.Source( + &cartov1alpha1.Source{ + Image: ":default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69", + Subpath: subpath, + }, + ) + }), + }, + KubeConfigTransport: clitesting.NewFakeTransportFromResponse(respCreator(http.StatusOK, `{"statuscode": "200", "message": "any ignored message"}`, myWorkloadHeader)), + ExpectUpdates: []client.Object{ + parent. + MetadataDie(func(d *diemetav1.ObjectMetaDie) { + d.Labels(map[string]string{apis.WorkloadTypeLabelName: "web"}) + }).SpecDie(func(d *diecartov1alpha1.WorkloadSpecDie) { + d.Image(":default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69") + }), + }, + Focus: true, + ExpectOutput: ` +🔎 Update workload: + 1, 1 |--- + 2, 2 |apiVersion: carto.run/v1alpha1 + 3, 3 |kind: Workload + 4, 4 |metadata: + 5 - | annotations: + 6 - | local-source-proxy.apps.tanzu.vmware.com: :default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69 + 7, 5 | labels: + 8, 6 | apps.tanzu.vmware.com/workload-type: web + 9, 7 | name: my-workload + 10, 8 | namespace: default + 11, 9 |spec: + 12 - | source: + 13 - | image: :default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69 + 14 - | subPath: testdata/local-source/subpath + 10 + | image: :default-my-workload@sha256:978be33a7f0cbe89bf48fbb438846047a28e1298d6d10d0de2d64bdc102a9e69 +👍 Updated workload "my-workload" + +To see logs: "tanzu apps workload tail my-workload --timestamp --since 1h" +To get status: "tanzu apps workload get my-workload" + +`, + }, } table.Run(t, scheme, func(ctx context.Context, c *cli.Config) *cobra.Command {