diff --git a/pkg/app/pipedv1/plugin/kubernetes_multicluster/config/application.go b/pkg/app/pipedv1/plugin/kubernetes_multicluster/config/application.go index 15aaed2622..7b6845b259 100644 --- a/pkg/app/pipedv1/plugin/kubernetes_multicluster/config/application.go +++ b/pkg/app/pipedv1/plugin/kubernetes_multicluster/config/application.go @@ -253,6 +253,9 @@ func (o *K8sBaselineRolloutStageOptions) UnmarshalJSON(data []byte) error { return nil } +// K8sBaselineCleanStageOptions contains all configurable values for a K8S_BASELINE_CLEAN stage. +type K8sBaselineCleanStageOptions struct{} + // K8sResourcePatch represents a patch operation for a Kubernetes resource. type K8sResourcePatch struct { // The target of the patch operation. diff --git a/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/baseline.go b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/baseline.go index d762581d74..fb5c933ea4 100644 --- a/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/baseline.go +++ b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/baseline.go @@ -235,3 +235,95 @@ func generateBaselineManifests(appCfg *kubeconfig.KubernetesApplicationSpec, man return baselineManifests, nil } + +func (p *Plugin) executeK8sMultiBaselineCleanStage(ctx context.Context, input *sdk.ExecuteStageInput[kubeconfig.KubernetesApplicationSpec], dts []*sdk.DeployTarget[kubeconfig.KubernetesDeployTargetConfig]) sdk.StageStatus { + lp := input.Client.LogPersister() + + cfg, err := input.Request.TargetDeploymentSource.AppConfig() + if err != nil { + lp.Errorf("Failed while decoding application config (%v)", err) + return sdk.StageStatusFailure + } + + deployTargetMap := make(map[string]*sdk.DeployTarget[kubeconfig.KubernetesDeployTargetConfig], len(dts)) + for _, dt := range dts { + deployTargetMap[dt.Name] = dt + } + + type targetConfig struct { + deployTarget *sdk.DeployTarget[kubeconfig.KubernetesDeployTargetConfig] + multiTarget *kubeconfig.KubernetesMultiTarget + } + + targetConfigs := make([]targetConfig, 0, len(dts)) + if len(cfg.Spec.Input.MultiTargets) == 0 { + for _, dt := range dts { + targetConfigs = append(targetConfigs, targetConfig{deployTarget: dt}) + } + } else { + for _, mt := range cfg.Spec.Input.MultiTargets { + dt, ok := deployTargetMap[mt.Target.Name] + if !ok { + lp.Infof("Ignore multi target '%s': not matched any deployTarget", mt.Target.Name) + continue + } + targetConfigs = append(targetConfigs, targetConfig{deployTarget: dt, multiTarget: &mt}) + } + } + + eg, ctx := errgroup.WithContext(ctx) + for _, tc := range targetConfigs { + eg.Go(func() error { + lp.Infof("Start cleaning BASELINE variant on target %s", tc.deployTarget.Name) + if err := p.baselineClean(ctx, input, tc.deployTarget, tc.multiTarget, cfg); err != nil { + return fmt.Errorf("failed to clean BASELINE variant on target %s: %w", tc.deployTarget.Name, err) + } + return nil + }) + } + + if err := eg.Wait(); err != nil { + lp.Errorf("Failed while cleaning BASELINE variant (%v)", err) + return sdk.StageStatusFailure + } + + return sdk.StageStatusSuccess +} + +func (p *Plugin) baselineClean( + ctx context.Context, + input *sdk.ExecuteStageInput[kubeconfig.KubernetesApplicationSpec], + dt *sdk.DeployTarget[kubeconfig.KubernetesDeployTargetConfig], + multiTarget *kubeconfig.KubernetesMultiTarget, + cfg *sdk.ApplicationConfig[kubeconfig.KubernetesApplicationSpec], +) error { + lp := input.Client.LogPersister() + + var ( + appCfg = cfg.Spec + variantLabel = appCfg.VariantLabel.Key + baselineVariant = appCfg.VariantLabel.BaselineValue + ) + + toolRegistry := toolregistry.NewRegistry(input.Client.ToolRegistry()) + + // Resolve kubectl version: multiTarget > spec > deployTarget + kubectlVersion := cmp.Or(appCfg.Input.KubectlVersion, dt.Config.KubectlVersion) + if multiTarget != nil { + kubectlVersion = cmp.Or(multiTarget.KubectlVersion, kubectlVersion) + } + + kubectlPath, err := toolRegistry.Kubectl(ctx, kubectlVersion) + if err != nil { + return fmt.Errorf("failed while getting kubectl tool: %w", err) + } + + kubectl := provider.NewKubectl(kubectlPath) + applier := provider.NewApplier(kubectl, appCfg.Input, dt.Config, input.Logger) + + if err := deleteVariantResources(ctx, lp, kubectl, dt.Config.KubeConfigPath, applier, input.Request.Deployment.ApplicationID, variantLabel, baselineVariant); err != nil { + return fmt.Errorf("unable to remove baseline resources: %w", err) + } + + return nil +} diff --git a/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/baseline_test.go b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/baseline_test.go index 5e1f04b86f..e5a6815486 100644 --- a/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/baseline_test.go +++ b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/baseline_test.go @@ -34,6 +34,96 @@ import ( kubeconfig "github.com/pipe-cd/pipecd/pkg/app/pipedv1/plugin/kubernetes_multicluster/config" ) +// baselineDeployment builds a pre-created baseline Deployment for test setup. +func baselineDeployment(namespace string) *unstructured.Unstructured { + return &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]any{ + "name": "simple-baseline", + "namespace": namespace, + "labels": map[string]any{ + "app": "simple", + "pipecd.dev/managed-by": "piped", + "pipecd.dev/piped": "piped-id", + "pipecd.dev/application": "app-id", + "pipecd.dev/variant": "baseline", + }, + "annotations": map[string]any{ + "pipecd.dev/managed-by": "piped", + "pipecd.dev/application": "app-id", + "pipecd.dev/variant": "baseline", + }, + }, + "spec": map[string]any{ + "replicas": int64(1), + "selector": map[string]any{ + "matchLabels": map[string]any{ + "app": "simple", + "pipecd.dev/variant": "baseline", + }, + }, + "template": map[string]any{ + "metadata": map[string]any{ + "labels": map[string]any{ + "app": "simple", + "pipecd.dev/variant": "baseline", + }, + }, + "spec": map[string]any{ + "containers": []any{ + map[string]any{ + "name": "helloworld", + "image": "ghcr.io/pipe-cd/helloworld:v0.32.0", + }, + }, + }, + }, + }, + }, + } +} + +// baselineService builds a pre-created baseline Service for test setup. +func baselineService(namespace string) *unstructured.Unstructured { + return &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "v1", + "kind": "Service", + "metadata": map[string]any{ + "name": "simple-baseline", + "namespace": namespace, + "labels": map[string]any{ + "app": "simple", + "pipecd.dev/managed-by": "piped", + "pipecd.dev/piped": "piped-id", + "pipecd.dev/application": "app-id", + "pipecd.dev/variant": "baseline", + }, + "annotations": map[string]any{ + "pipecd.dev/managed-by": "piped", + "pipecd.dev/application": "app-id", + "pipecd.dev/variant": "baseline", + }, + }, + "spec": map[string]any{ + "selector": map[string]any{ + "app": "simple", + "pipecd.dev/variant": "baseline", + }, + "ports": []any{ + map[string]any{ + "protocol": "TCP", + "port": int64(9085), + "targetPort": int64(9085), + }, + }, + }, + }, + } +} + func TestPlugin_executeK8sMultiBaselineRolloutStage_SingleCluster(t *testing.T) { t.Parallel() @@ -341,3 +431,212 @@ func TestPlugin_executeK8sMultiBaselineRolloutStage_Failure(t *testing.T) { assert.Equal(t, sdk.StageStatusFailure, status) } + +func TestPlugin_executeK8sMultiBaselineCleanStage(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + testRegistry := toolregistrytest.NewTestToolRegistry(t) + + appCfg := sdk.LoadApplicationConfigForTest[kubeconfig.KubernetesApplicationSpec](t, filepath.Join("testdata", "simple", "app.pipecd.yaml"), "kubernetes_multicluster") + + input := &sdk.ExecuteStageInput[kubeconfig.KubernetesApplicationSpec]{ + Request: sdk.ExecuteStageRequest[kubeconfig.KubernetesApplicationSpec]{ + StageName: StageK8sMultiBaselineClean, + StageConfig: []byte(`{}`), + TargetDeploymentSource: sdk.DeploymentSource[kubeconfig.KubernetesApplicationSpec]{ + ApplicationDirectory: filepath.Join("testdata", "simple"), + CommitHash: "0123456789", + ApplicationConfig: appCfg, + ApplicationConfigFilename: "app.pipecd.yaml", + }, + Deployment: sdk.Deployment{ + PipedID: "piped-id", + ApplicationID: "app-id", + }, + }, + Client: sdk.NewClient(nil, "kubernetes_multicluster", "", "", logpersistertest.NewTestLogPersister(t), testRegistry), + Logger: zaptest.NewLogger(t), + } + + dtConfig, dynamicClient := setupTestDeployTargetConfigAndDynamicClient(t) + + deploymentRes := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"} + + // Pre-create the baseline deployment (simulating what K8S_BASELINE_ROLLOUT would have done). + _, err := dynamicClient.Resource(deploymentRes).Namespace("default").Create(ctx, baselineDeployment("default"), metav1.CreateOptions{}) + require.NoError(t, err) + + _, err = dynamicClient.Resource(deploymentRes).Namespace("default").Get(ctx, "simple-baseline", metav1.GetOptions{}) + require.NoError(t, err) + + plugin := &Plugin{} + status := plugin.executeK8sMultiBaselineCleanStage(ctx, input, []*sdk.DeployTarget[kubeconfig.KubernetesDeployTargetConfig]{ + {Name: "default", Config: *dtConfig}, + }) + + assert.Equal(t, sdk.StageStatusSuccess, status) + + _, err = dynamicClient.Resource(deploymentRes).Namespace("default").Get(ctx, "simple-baseline", metav1.GetOptions{}) + require.Error(t, err) + assert.True(t, k8serrors.IsNotFound(err)) +} + +func TestPlugin_executeK8sMultiBaselineCleanStage_multipleTargets(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + testRegistry := toolregistrytest.NewTestToolRegistry(t) + + appCfg := sdk.LoadApplicationConfigForTest[kubeconfig.KubernetesApplicationSpec](t, filepath.Join("testdata", "simple", "app.pipecd.yaml"), "kubernetes_multicluster") + + clusterUS := setupCluster(t, "cluster-us") + clusterEU := setupCluster(t, "cluster-eu") + + input := &sdk.ExecuteStageInput[kubeconfig.KubernetesApplicationSpec]{ + Request: sdk.ExecuteStageRequest[kubeconfig.KubernetesApplicationSpec]{ + StageName: StageK8sMultiBaselineClean, + StageConfig: []byte(`{}`), + TargetDeploymentSource: sdk.DeploymentSource[kubeconfig.KubernetesApplicationSpec]{ + ApplicationDirectory: filepath.Join("testdata", "simple"), + CommitHash: "0123456789", + ApplicationConfig: appCfg, + ApplicationConfigFilename: "app.pipecd.yaml", + }, + Deployment: sdk.Deployment{ + PipedID: "piped-id", + ApplicationID: "app-id", + }, + }, + Client: sdk.NewClient(nil, "kubernetes_multicluster", "", "", logpersistertest.NewTestLogPersister(t), testRegistry), + Logger: zaptest.NewLogger(t), + } + + deploymentRes := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"} + + for _, c := range []*cluster{clusterUS, clusterEU} { + _, err := c.cli.Resource(deploymentRes).Namespace("default").Create(ctx, baselineDeployment("default"), metav1.CreateOptions{}) + require.NoError(t, err) + } + + plugin := &Plugin{} + status := plugin.executeK8sMultiBaselineCleanStage(ctx, input, []*sdk.DeployTarget[kubeconfig.KubernetesDeployTargetConfig]{ + {Name: clusterUS.name, Config: *clusterUS.dtc}, + {Name: clusterEU.name, Config: *clusterEU.dtc}, + }) + + assert.Equal(t, sdk.StageStatusSuccess, status) + + for _, c := range []*cluster{clusterUS, clusterEU} { + _, err := c.cli.Resource(deploymentRes).Namespace("default").Get(ctx, "simple-baseline", metav1.GetOptions{}) + require.Error(t, err) + assert.True(t, k8serrors.IsNotFound(err), "baseline deployment should be deleted on cluster %s", c.name) + } +} + +func TestPlugin_executeK8sMultiBaselineCleanStage_withCreateService(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + testRegistry := toolregistrytest.NewTestToolRegistry(t) + + configDir := filepath.Join("testdata", "baseline_clean_with_create_service") + appCfg := sdk.LoadApplicationConfigForTest[kubeconfig.KubernetesApplicationSpec](t, filepath.Join(configDir, "app.pipecd.yaml"), "kubernetes_multicluster") + + input := &sdk.ExecuteStageInput[kubeconfig.KubernetesApplicationSpec]{ + Request: sdk.ExecuteStageRequest[kubeconfig.KubernetesApplicationSpec]{ + StageName: StageK8sMultiBaselineClean, + StageConfig: []byte(`{}`), + TargetDeploymentSource: sdk.DeploymentSource[kubeconfig.KubernetesApplicationSpec]{ + ApplicationDirectory: configDir, + CommitHash: "0123456789", + ApplicationConfig: appCfg, + ApplicationConfigFilename: "app.pipecd.yaml", + }, + Deployment: sdk.Deployment{ + PipedID: "piped-id", + ApplicationID: "app-id", + }, + }, + Client: sdk.NewClient(nil, "kubernetes_multicluster", "", "", logpersistertest.NewTestLogPersister(t), testRegistry), + Logger: zaptest.NewLogger(t), + } + + dtConfig, dynamicClient := setupTestDeployTargetConfigAndDynamicClient(t) + + deploymentRes := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"} + serviceRes := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"} + + // Pre-create both baseline deployment and service. + _, err := dynamicClient.Resource(deploymentRes).Namespace("default").Create(ctx, baselineDeployment("default"), metav1.CreateOptions{}) + require.NoError(t, err) + _, err = dynamicClient.Resource(serviceRes).Namespace("default").Create(ctx, baselineService("default"), metav1.CreateOptions{}) + require.NoError(t, err) + + plugin := &Plugin{} + status := plugin.executeK8sMultiBaselineCleanStage(ctx, input, []*sdk.DeployTarget[kubeconfig.KubernetesDeployTargetConfig]{ + {Name: "default", Config: *dtConfig}, + }) + + assert.Equal(t, sdk.StageStatusSuccess, status) + + _, err = dynamicClient.Resource(deploymentRes).Namespace("default").Get(ctx, "simple-baseline", metav1.GetOptions{}) + require.Error(t, err) + assert.True(t, k8serrors.IsNotFound(err)) + + _, err = dynamicClient.Resource(serviceRes).Namespace("default").Get(ctx, "simple-baseline", metav1.GetOptions{}) + require.Error(t, err) + assert.True(t, k8serrors.IsNotFound(err)) +} + +func TestPlugin_executeK8sMultiBaselineCleanStage_withoutCreateService(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + testRegistry := toolregistrytest.NewTestToolRegistry(t) + + configDir := filepath.Join("testdata", "baseline_clean_without_create_service") + appCfg := sdk.LoadApplicationConfigForTest[kubeconfig.KubernetesApplicationSpec](t, filepath.Join(configDir, "app.pipecd.yaml"), "kubernetes_multicluster") + + input := &sdk.ExecuteStageInput[kubeconfig.KubernetesApplicationSpec]{ + Request: sdk.ExecuteStageRequest[kubeconfig.KubernetesApplicationSpec]{ + StageName: StageK8sMultiBaselineClean, + StageConfig: []byte(`{}`), + TargetDeploymentSource: sdk.DeploymentSource[kubeconfig.KubernetesApplicationSpec]{ + ApplicationDirectory: configDir, + CommitHash: "0123456789", + ApplicationConfig: appCfg, + ApplicationConfigFilename: "app.pipecd.yaml", + }, + Deployment: sdk.Deployment{ + PipedID: "piped-id", + ApplicationID: "app-id", + }, + }, + Client: sdk.NewClient(nil, "kubernetes_multicluster", "", "", logpersistertest.NewTestLogPersister(t), testRegistry), + Logger: zaptest.NewLogger(t), + } + + dtConfig, dynamicClient := setupTestDeployTargetConfigAndDynamicClient(t) + + deploymentRes := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"} + + // Pre-create only a baseline deployment (no service). + _, err := dynamicClient.Resource(deploymentRes).Namespace("default").Create(ctx, baselineDeployment("default"), metav1.CreateOptions{}) + require.NoError(t, err) + + plugin := &Plugin{} + status := plugin.executeK8sMultiBaselineCleanStage(ctx, input, []*sdk.DeployTarget[kubeconfig.KubernetesDeployTargetConfig]{ + {Name: "default", Config: *dtConfig}, + }) + + assert.Equal(t, sdk.StageStatusSuccess, status) + + _, err = dynamicClient.Resource(deploymentRes).Namespace("default").Get(ctx, "simple-baseline", metav1.GetOptions{}) + require.Error(t, err) + assert.True(t, k8serrors.IsNotFound(err)) +} diff --git a/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/pipeline.go b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/pipeline.go index 75e02e4256..dcbecdae17 100644 --- a/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/pipeline.go +++ b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/pipeline.go @@ -34,6 +34,8 @@ const ( StageK8sMultiPrimaryRollout = "K8S_PRIMARY_ROLLOUT" // StageK8sMultiBaselineRollout represents the state where the current version is deployed as BASELINE to all targets. StageK8sMultiBaselineRollout = "K8S_BASELINE_ROLLOUT" + // StageK8sMultiBaselineClean represents the state where all baseline resources should be removed. + StageK8sMultiBaselineClean = "K8S_BASELINE_CLEAN" ) var allStages = []string{ @@ -43,6 +45,7 @@ var allStages = []string{ StageK8sMultiCanaryClean, StageK8sMultiPrimaryRollout, StageK8sMultiBaselineRollout, + StageK8sMultiBaselineClean, } const ( @@ -58,6 +61,8 @@ const ( StageDescriptionK8sMultiPrimaryRollout = "Rollout the new version as PRIMARY to all targets" // StageDescriptionK8sMultiBaselineRollout represents the description of the K8sBaselineRollout stage. StageDescriptionK8sMultiBaselineRollout = "Rollout the current version as BASELINE to all targets" + // StageDescriptionK8sMultiBaselineClean represents the description of the K8sBaselineClean stage. + StageDescriptionK8sMultiBaselineClean = "Remove all baseline resources" ) func buildQuickSyncPipeline(autoRollback bool) []sdk.QuickSyncStage { diff --git a/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/plugin.go b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/plugin.go index 271ccc8b28..778d00e8f1 100644 --- a/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/plugin.go +++ b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/plugin.go @@ -77,6 +77,10 @@ func (p *Plugin) ExecuteStage(ctx context.Context, _ *sdk.ConfigNone, dts []*sdk return &sdk.ExecuteStageResponse{Status: p.executeK8sMultiPrimaryRolloutStage(ctx, input, dts)}, nil case StageK8sMultiBaselineRollout: return &sdk.ExecuteStageResponse{Status: p.executeK8sMultiBaselineRolloutStage(ctx, input, dts)}, nil + case StageK8sMultiBaselineClean: + return &sdk.ExecuteStageResponse{ + Status: p.executeK8sMultiBaselineCleanStage(ctx, input, dts), + }, nil default: return nil, errors.New("unimplemented or unsupported stage") } diff --git a/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_with_create_service/app.pipecd.yaml b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_with_create_service/app.pipecd.yaml new file mode 100644 index 0000000000..3b1cd4ec8a --- /dev/null +++ b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_with_create_service/app.pipecd.yaml @@ -0,0 +1,24 @@ +apiVersion: pipecd.dev/v1beta1 +kind: KubernetesApp +spec: + name: baseline-clean + labels: + env: example + team: product + description: | + This app is test data for baseline clean with create service. + pipeline: + stages: + - name: K8S_BASELINE_ROLLOUT + with: + createService: true + - name: K8S_BASELINE_CLEAN + plugins: + kubernetes_multicluster: + input: + manifests: + - deployment.yaml + - service.yaml + kubectlVersion: 1.32.2 + service: + name: simple diff --git a/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_with_create_service/deployment.yaml b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_with_create_service/deployment.yaml new file mode 100644 index 0000000000..a7b8a57fe6 --- /dev/null +++ b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_with_create_service/deployment.yaml @@ -0,0 +1,21 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: simple + labels: + app: simple +spec: + replicas: 2 + selector: + matchLabels: + app: simple + template: + metadata: + labels: + app: simple + spec: + containers: + - name: helloworld + image: ghcr.io/pipe-cd/helloworld:v0.32.0 + ports: + - containerPort: 9085 diff --git a/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_with_create_service/service.yaml b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_with_create_service/service.yaml new file mode 100644 index 0000000000..52ca9d1f59 --- /dev/null +++ b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_with_create_service/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: simple +spec: + selector: + app: simple + ports: + - protocol: TCP + port: 9085 + targetPort: 9085 diff --git a/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_without_create_service/app.pipecd.yaml b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_without_create_service/app.pipecd.yaml new file mode 100644 index 0000000000..f3ab7470ad --- /dev/null +++ b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_without_create_service/app.pipecd.yaml @@ -0,0 +1,19 @@ +apiVersion: pipecd.dev/v1beta1 +kind: KubernetesApp +spec: + name: baseline-clean + labels: + env: example + team: product + description: | + This app is test data for baseline clean without create service. + pipeline: + stages: + - name: K8S_BASELINE_ROLLOUT + - name: K8S_BASELINE_CLEAN + plugins: + kubernetes_multicluster: + input: + manifests: + - deployment.yaml + kubectlVersion: 1.32.2 diff --git a/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_without_create_service/deployment.yaml b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_without_create_service/deployment.yaml new file mode 100644 index 0000000000..a7b8a57fe6 --- /dev/null +++ b/pkg/app/pipedv1/plugin/kubernetes_multicluster/deployment/testdata/baseline_clean_without_create_service/deployment.yaml @@ -0,0 +1,21 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: simple + labels: + app: simple +spec: + replicas: 2 + selector: + matchLabels: + app: simple + template: + metadata: + labels: + app: simple + spec: + containers: + - name: helloworld + image: ghcr.io/pipe-cd/helloworld:v0.32.0 + ports: + - containerPort: 9085