diff --git a/pkg/app/piped/planner/kubernetes/kubernetes.go b/pkg/app/piped/planner/kubernetes/kubernetes.go index daeac6543f..c795a5bab2 100644 --- a/pkg/app/piped/planner/kubernetes/kubernetes.go +++ b/pkg/app/piped/planner/kubernetes/kubernetes.go @@ -214,7 +214,8 @@ func (p *Planner) Plan(ctx context.Context, in planner.Input) (out planner.Outpu manifestCache.Put(in.MostRecentSuccessfulCommitHash, oldManifests) } - progressive, desc := decideStrategy(oldManifests, newManifests, cfg.Workloads, in.Logger) + pipelineDefined := cfg.Pipeline != nil && len(cfg.Pipeline.Stages) > 0 + progressive, desc := decideStrategy(oldManifests, newManifests, cfg.Workloads, pipelineDefined, in.Logger) out.Summary = desc if progressive { @@ -230,14 +231,26 @@ func (p *Planner) Plan(ctx context.Context, in planner.Input) (out planner.Outpu // First up, checks to see if the workload's `spec.template` has been changed, // and then checks if the configmap/secret's data. -func decideStrategy(olds, news []provider.Manifest, workloadRefs []config.K8sResourceReference, logger *zap.Logger) (progressive bool, desc string) { +// pipelineDefined should be true when the user has explicitly configured a pipeline +// in the application config, which forces progressive sync even when no workloads exist. +func decideStrategy(olds, news []provider.Manifest, workloadRefs []config.K8sResourceReference, pipelineDefined bool, logger *zap.Logger) (progressive bool, desc string) { oldWorkloads := findWorkloadManifests(olds, workloadRefs) if len(oldWorkloads) == 0 { + if pipelineDefined { + progressive = true + desc = "Sync progressively because pipeline is defined in the application config" + return + } desc = "Quick sync by applying all manifests because it was unable to find the currently running workloads" return } newWorkloads := findWorkloadManifests(news, workloadRefs) if len(newWorkloads) == 0 { + if pipelineDefined { + progressive = true + desc = "Sync progressively because pipeline is defined in the application config" + return + } desc = "Quick sync by applying all manifests because it was unable to find workloads in the new manifests" return } diff --git a/pkg/app/piped/planner/kubernetes/kubernetes_test.go b/pkg/app/piped/planner/kubernetes/kubernetes_test.go index d80fde06f6..11a24d6c78 100644 --- a/pkg/app/piped/planner/kubernetes/kubernetes_test.go +++ b/pkg/app/piped/planner/kubernetes/kubernetes_test.go @@ -408,7 +408,67 @@ func TestDecideStrategy(t *testing.T) { } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - gotProgressive, gotDesc := decideStrategy(tc.olds, tc.news, tc.workloadRefs, zap.NewNop()) + gotProgressive, gotDesc := decideStrategy(tc.olds, tc.news, tc.workloadRefs, false, zap.NewNop()) + assert.Equal(t, tc.wantProgressive, gotProgressive) + assert.Equal(t, tc.wantDesc, gotDesc) + }) + } +} + +func TestDecideStrategyWithPipelineDefined(t *testing.T) { + t.Parallel() + + configMapManifest := func(name, data string) provider.Manifest { + return provider.MakeManifest(provider.ResourceKey{ + APIVersion: "v1", + Kind: provider.KindConfigMap, + Name: name, + }, &unstructured.Unstructured{ + Object: map[string]interface{}{"data": data}}, + ) + } + + tests := []struct { + name string + olds []provider.Manifest + news []provider.Manifest + pipelineDefined bool + wantProgressive bool + wantDesc string + }{ + { + // Regression test for: https://github.com/pipe-cd/pipecd/issues/4799 + // ConfigMap-only app (no Deployment) with pipeline defined in app.pipecd.yaml + // must use progressive sync, not QUICK_SYNC. + name: "configmap-only app with pipeline defined should be progressive", + olds: []provider.Manifest{ + configMapManifest("my-config", "old-value"), + }, + news: []provider.Manifest{ + configMapManifest("my-config", "new-value"), + }, + pipelineDefined: true, + wantProgressive: true, + wantDesc: "Sync progressively because pipeline is defined in the application config", + }, + { + // Without a pipeline defined, ConfigMap-only app must still use QUICK_SYNC + // (no regression in the default/non-pipeline path). + name: "configmap-only app without pipeline defined should be quick sync", + olds: []provider.Manifest{ + configMapManifest("my-config", "old-value"), + }, + news: []provider.Manifest{ + configMapManifest("my-config", "new-value"), + }, + pipelineDefined: false, + wantProgressive: false, + wantDesc: "Quick sync by applying all manifests because it was unable to find the currently running workloads", + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + gotProgressive, gotDesc := decideStrategy(tc.olds, tc.news, nil, tc.pipelineDefined, zap.NewNop()) assert.Equal(t, tc.wantProgressive, gotProgressive) assert.Equal(t, tc.wantDesc, gotDesc) })