From d5ba3d2949bd1dc794020f36fafafb61ca749e44 Mon Sep 17 00:00:00 2001 From: Shinnosuke Sawada-Dazai Date: Fri, 13 Dec 2024 18:58:37 +0900 Subject: [PATCH] Implement preparing the deploy source in pipedv1 planner / scheduler (#5410) * Implement prepare deploy source in planner Signed-off-by: Shinnosuke Sawada-Dazai * Create tools directory if it does not exist in tool registry Signed-off-by: Shinnosuke Sawada-Dazai * Implement deploy source preparation in scheduler Signed-off-by: Shinnosuke Sawada-Dazai * Start log persister in plugin run method Signed-off-by: Shinnosuke Sawada-Dazai * Refactor deployment source handling in scheduler to use Provider interface Signed-off-by: Shinnosuke Sawada-Dazai * Add fakeDeploySourceProvider Signed-off-by: Yoshiki Fujikane * Import configv1 in the sourceprocessor Signed-off-by: Yoshiki Fujikane * Fix to return nil byte when pipeline is not set Signed-off-by: Yoshiki Fujikane * Nit Signed-off-by: Yoshiki Fujikane * Fix test Signed-off-by: Yoshiki Fujikane --------- Signed-off-by: Shinnosuke Sawada-Dazai Signed-off-by: Yoshiki Fujikane Co-authored-by: Yoshiki Fujikane Co-authored-by: Yoshiki Fujikane <40124947+ffjlabo@users.noreply.github.com> --- .../cmd/piped/grpcapi/tool_registry.go | 4 ++ pkg/app/pipedv1/controller/planner.go | 52 ++++++++++------ pkg/app/pipedv1/controller/scheduler.go | 53 +++++++++++++--- pkg/app/pipedv1/controller/scheduler_test.go | 26 ++++++-- pkg/app/pipedv1/deploysource/deploysource.go | 60 ++++++++++-------- pkg/app/pipedv1/deploysource/sourcecloner.go | 2 +- pkg/app/pipedv1/plugin/kubernetes/server.go | 8 ++- pkg/app/pipedv1/sourceprocesser/attacher.go | 2 +- .../pipedv1/sourceprocesser/attacher_test.go | 2 +- pkg/app/pipedv1/sourceprocesser/decrypter.go | 2 +- .../pipedv1/sourceprocesser/decrypter_test.go | 2 +- .../sourceprocesser/sourceprocesser_test.go | 2 +- pkg/configv1/application.go | 10 ++- pkg/configv1/application_test.go | 61 +++++++++++++++++++ 14 files changed, 222 insertions(+), 64 deletions(-) diff --git a/pkg/app/pipedv1/cmd/piped/grpcapi/tool_registry.go b/pkg/app/pipedv1/cmd/piped/grpcapi/tool_registry.go index 6774f9754d..f83bdbd65d 100644 --- a/pkg/app/pipedv1/cmd/piped/grpcapi/tool_registry.go +++ b/pkg/app/pipedv1/cmd/piped/grpcapi/tool_registry.go @@ -46,6 +46,10 @@ type templateValues struct { } func newToolRegistry(toolsDir string) (*toolRegistry, error) { + if err := os.MkdirAll(toolsDir, 0o755); err != nil { + return nil, fmt.Errorf("failed to create the tools directory: %w", err) + } + tmpDir, err := os.MkdirTemp("", "tool-registry") if err != nil { return nil, fmt.Errorf("failed to create a temporary directory: %w", err) diff --git a/pkg/app/pipedv1/controller/planner.go b/pkg/app/pipedv1/controller/planner.go index 2984aef823..c527fcd5cd 100644 --- a/pkg/app/pipedv1/controller/planner.go +++ b/pkg/app/pipedv1/controller/planner.go @@ -18,6 +18,8 @@ import ( "context" "encoding/json" "fmt" + "io" + "path/filepath" "sort" "time" @@ -27,6 +29,7 @@ import ( "go.uber.org/zap" "github.com/pipe-cd/pipecd/pkg/app/pipedv1/controller/controllermetrics" + "github.com/pipe-cd/pipecd/pkg/app/pipedv1/deploysource" "github.com/pipe-cd/pipecd/pkg/app/pipedv1/metadatastore" "github.com/pipe-cd/pipecd/pkg/app/server/service/pipedservice" config "github.com/pipe-cd/pipecd/pkg/configv1" @@ -181,27 +184,38 @@ func (p *planner) Run(ctx context.Context) error { controllermetrics.UpdateDeploymentStatus(p.deployment, p.doneDeploymentStatus) }() - // TODO: Prepare running deploy source and target deploy source. + // Prepare running deploy source and target deploy source. var runningDS, targetDS *deployment.DeploymentSource - // repoCfg := config.PipedRepository{ - // RepoID: p.deployment.GitPath.Repo.Id, - // Remote: p.deployment.GitPath.Repo.Remote, - // Branch: p.deployment.GitPath.Repo.Branch, - // } - - // Prepare target deploy source. - // targetDSP := deploysource.NewProvider( - // filepath.Join(p.workingDir, "deploysource"), - // deploysource.NewGitSourceCloner(p.gitClient, repoCfg, "target", p.deployment.Trigger.Commit.Hash), - // *p.deployment.GitPath, - // nil, // TODO: Revise this secret decryter, is this need? - // ) - - // targetDS, err := targetDSP.Get(ctx, io.Discard) - // if err != nil { - // return fmt.Errorf("error while preparing deploy source data (%v)", err) - // } + repoCfg := config.PipedRepository{ + RepoID: p.deployment.GitPath.Repo.Id, + Remote: p.deployment.GitPath.Repo.Remote, + Branch: p.deployment.GitPath.Repo.Branch, + } + + runningDSP := deploysource.NewProvider( + filepath.Join(p.workingDir, "running-deploysource"), + deploysource.NewGitSourceCloner(p.gitClient, repoCfg, "running", p.lastSuccessfulCommitHash), + p.deployment.GetGitPath(), nil, // TODO: pass secret decrypter? + ) + rds, err := runningDSP.Get(ctx, io.Discard) // TODO: pass not io.Discard + if err != nil { + // TODO: log error + return fmt.Errorf("error while preparing deploy source data (%v)", err) + } + runningDS = rds.ToPluginDeploySource() + + targetDSP := deploysource.NewProvider( + filepath.Join(p.workingDir, "target-deploysource"), + deploysource.NewGitSourceCloner(p.gitClient, repoCfg, "target", p.deployment.Trigger.Commit.Hash), + p.deployment.GetGitPath(), nil, // TODO: pass secret decrypter? + ) + tds, err := targetDSP.Get(ctx, io.Discard) // TODO: pass not io.Discard + if err != nil { + // TODO: log error + return fmt.Errorf("error while preparing deploy source data (%v)", err) + } + targetDS = tds.ToPluginDeploySource() // TODO: Pass running DS as well if need? out, err := p.buildPlan(ctx, runningDS, targetDS) diff --git a/pkg/app/pipedv1/controller/scheduler.go b/pkg/app/pipedv1/controller/scheduler.go index b3f8671423..0fbd3d28bc 100644 --- a/pkg/app/pipedv1/controller/scheduler.go +++ b/pkg/app/pipedv1/controller/scheduler.go @@ -18,6 +18,8 @@ import ( "context" "encoding/json" "fmt" + "io" + "path/filepath" "time" "go.opentelemetry.io/otel/attribute" @@ -27,6 +29,7 @@ import ( "go.uber.org/zap" "github.com/pipe-cd/pipecd/pkg/app/pipedv1/controller/controllermetrics" + "github.com/pipe-cd/pipecd/pkg/app/pipedv1/deploysource" "github.com/pipe-cd/pipecd/pkg/app/pipedv1/metadatastore" "github.com/pipe-cd/pipecd/pkg/app/server/service/pipedservice" config "github.com/pipe-cd/pipecd/pkg/configv1" @@ -47,8 +50,8 @@ type scheduler struct { metadataStore metadatastore.MetadataStore notifier notifier - targetDS *deployment.DeploymentSource - runningDS *deployment.DeploymentSource + targetDSP deploysource.Provider + runningDSP deploysource.Provider // Current status of each stages. // We stores their current statuses into this field @@ -214,9 +217,32 @@ func (s *scheduler) Run(ctx context.Context) error { ) deploymentStatus = model.DeploymentStatus_DEPLOYMENT_SUCCESS - /// TODO: prepare the targetDS and runningDS - var targetDS *deployment.DeploymentSource - cfg, err := config.DecodeYAML[*config.GenericApplicationSpec](targetDS.GetApplicationConfig()) + repoCfg := config.PipedRepository{ + RepoID: s.deployment.GitPath.Repo.Id, + Remote: s.deployment.GitPath.Repo.Remote, + Branch: s.deployment.GitPath.Repo.Branch, + } + + s.runningDSP = deploysource.NewProvider( + filepath.Join(s.workingDir, "running-deploysource"), + deploysource.NewGitSourceCloner(s.gitClient, repoCfg, "running", s.deployment.RunningCommitHash), + s.deployment.GetGitPath(), nil, // TODO: pass secret decrypter? + ) + + s.targetDSP = deploysource.NewProvider( + filepath.Join(s.workingDir, "target-deploysource"), + deploysource.NewGitSourceCloner(s.gitClient, repoCfg, "target", s.deployment.Trigger.Commit.Hash), + s.deployment.GetGitPath(), nil, // TODO: pass secret decrypter? + ) + + ds, err := s.targetDSP.Get(ctx, io.Discard) + if err != nil { + deploymentStatus = model.DeploymentStatus_DEPLOYMENT_FAILURE + statusReason = fmt.Sprintf("Failed to get deploy source at target commit (%v)", err) + s.reportDeploymentCompleted(ctx, deploymentStatus, statusReason, "") + return err + } + cfg, err := config.DecodeYAML[*config.GenericApplicationSpec](ds.ApplicationConfig) if err != nil { deploymentStatus = model.DeploymentStatus_DEPLOYMENT_FAILURE statusReason = fmt.Sprintf("Failed to decode application configuration at target commit (%v)", err) @@ -441,6 +467,18 @@ func (s *scheduler) executeStage(sig StopSignal, ps *model.PipelineStage) (final originalStatus = ps.Status ) + rds, err := s.runningDSP.Get(ctx, io.Discard) + if err != nil { + s.logger.Error("failed to get running deployment source", zap.Error(err)) + return model.StageStatus_STAGE_FAILURE + } + + tds, err := s.targetDSP.Get(ctx, io.Discard) + if err != nil { + s.logger.Error("failed to get target deployment source", zap.Error(err)) + return model.StageStatus_STAGE_FAILURE + } + // Check whether to execute the script rollback stage or not. // If the base stage is executed, the script rollback stage will be executed. if ps.Rollback { @@ -477,7 +515,6 @@ func (s *scheduler) executeStage(sig StopSignal, ps *model.PipelineStage) (final } // Load the stage configuration. - // TODO: Check this works with pre-defined stages. (stages added to the pipeline without user-defined configuration) stageConfig, stageConfigFound := s.genericApplicationConfig.GetStageByte(ps.Index) if !stageConfigFound { s.logger.Error("Unable to find the stage configuration") @@ -493,8 +530,8 @@ func (s *scheduler) executeStage(sig StopSignal, ps *model.PipelineStage) (final Deployment: s.deployment, Stage: ps, StageConfig: stageConfig, - RunningDeploymentSource: s.runningDS, // TODO: prepare this - TargetDeploymentSource: s.targetDS, // TODO: prepare this + RunningDeploymentSource: rds.ToPluginDeploySource(), + TargetDeploymentSource: tds.ToPluginDeploySource(), }, }) if err != nil { diff --git a/pkg/app/pipedv1/controller/scheduler_test.go b/pkg/app/pipedv1/controller/scheduler_test.go index 793e937b9c..f94be6a339 100644 --- a/pkg/app/pipedv1/controller/scheduler_test.go +++ b/pkg/app/pipedv1/controller/scheduler_test.go @@ -16,6 +16,7 @@ package controller import ( "context" + "io" "testing" "time" @@ -23,6 +24,7 @@ import ( "go.uber.org/zap/zaptest" "google.golang.org/grpc" + "github.com/pipe-cd/pipecd/pkg/app/pipedv1/deploysource" "github.com/pipe-cd/pipecd/pkg/app/server/service/pipedservice" config "github.com/pipe-cd/pipecd/pkg/configv1" "github.com/pipe-cd/pipecd/pkg/model" @@ -181,7 +183,7 @@ func TestExecuteStage(t *testing.T) { expected: model.StageStatus_STAGE_FAILURE, }, { - name: "stage without config, should be set as failed", + name: "stage without config, should be success", deployment: &model.Deployment{ Stages: []*model.PipelineStage{ { @@ -200,7 +202,7 @@ func TestExecuteStage(t *testing.T) { Stages: []config.PipelineStage{}, }, }, - expected: model.StageStatus_STAGE_FAILURE, + expected: model.StageStatus_STAGE_SUCCESS, }, } @@ -210,7 +212,9 @@ func TestExecuteStage(t *testing.T) { for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { s := &scheduler{ - apiClient: &fakeApiClient{}, + apiClient: &fakeApiClient{}, + targetDSP: &fakeDeploySourceProvider{}, + runningDSP: &fakeDeploySourceProvider{}, stageBasedPluginsMap: map[string]pluginapi.PluginClient{ "stage-name": &fakeExecutorPluginClient{}, }, @@ -237,12 +241,22 @@ func TestExecuteStage(t *testing.T) { } } +type fakeDeploySourceProvider struct { + deploysource.Provider +} + +func (f *fakeDeploySourceProvider) Get(ctx context.Context, logWriter io.Writer) (*deploysource.DeploySource, error) { + return &deploysource.DeploySource{}, nil +} + func TestExecuteStage_SignalTerminated(t *testing.T) { logger := zaptest.NewLogger(t) sig, handler := NewStopSignal() s := &scheduler{ - apiClient: &fakeApiClient{}, + apiClient: &fakeApiClient{}, + targetDSP: &fakeDeploySourceProvider{}, + runningDSP: &fakeDeploySourceProvider{}, stageBasedPluginsMap: map[string]pluginapi.PluginClient{ "stage-name": &fakeExecutorPluginClient{}, }, @@ -278,7 +292,9 @@ func TestExecuteStage_SignalCancelled(t *testing.T) { sig, handler := NewStopSignal() s := &scheduler{ - apiClient: &fakeApiClient{}, + apiClient: &fakeApiClient{}, + targetDSP: &fakeDeploySourceProvider{}, + runningDSP: &fakeDeploySourceProvider{}, stageBasedPluginsMap: map[string]pluginapi.PluginClient{ "stage-name": &fakeExecutorPluginClient{}, }, diff --git a/pkg/app/pipedv1/deploysource/deploysource.go b/pkg/app/pipedv1/deploysource/deploysource.go index 2637291ccd..f562715082 100644 --- a/pkg/app/pipedv1/deploysource/deploysource.go +++ b/pkg/app/pipedv1/deploysource/deploysource.go @@ -24,16 +24,26 @@ import ( "sync" "github.com/pipe-cd/pipecd/pkg/app/pipedv1/sourceprocesser" - "github.com/pipe-cd/pipecd/pkg/config" + config "github.com/pipe-cd/pipecd/pkg/configv1" "github.com/pipe-cd/pipecd/pkg/model" + "github.com/pipe-cd/pipecd/pkg/plugin/api/v1alpha1/deployment" ) type DeploySource struct { - RepoDir string - AppDir string - Revision string - ApplicationConfig *config.Config - GenericApplicationConfig *config.GenericApplicationSpec + RepoDir string + AppDir string + Revision string + ApplicationConfig []byte + ApplicationConfigFilename string +} + +func (d *DeploySource) ToPluginDeploySource() *deployment.DeploymentSource { + return &deployment.DeploymentSource{ + ApplicationDirectory: d.AppDir, + Revision: d.Revision, + ApplicationConfig: d.ApplicationConfig, + ApplicationConfigFilename: d.ApplicationConfigFilename, + } } type Provider interface { @@ -50,7 +60,7 @@ type provider struct { cloner SourceCloner revisionName string revision string - appGitPath model.ApplicationGitPath + appGitPath *model.ApplicationGitPath secretDecrypter secretDecrypter done bool @@ -63,7 +73,7 @@ type provider struct { func NewProvider( workingDir string, cloner SourceCloner, - appGitPath model.ApplicationGitPath, + appGitPath *model.ApplicationGitPath, sd secretDecrypter, ) Provider { @@ -134,7 +144,14 @@ func (p *provider) prepare(ctx context.Context, lw io.Writer) (*DeploySource, er cfgFileRelPath = p.appGitPath.GetApplicationConfigFilePath() cfgFileAbsPath = filepath.Join(repoDir, cfgFileRelPath) ) - cfg, err := config.LoadFromYAML(cfgFileAbsPath) + + cfgFileContent, err := os.ReadFile(cfgFileAbsPath) + if err != nil { + fmt.Fprintf(lw, "Unable to load the application configuration file at %s (%v)\n", cfgFileRelPath, err) + return nil, err + } + cfg, err := config.DecodeYAML[*config.GenericApplicationSpec](cfgFileContent) + if err != nil { fmt.Fprintf(lw, "Unable to load the application configuration file at %s (%v)\n", cfgFileRelPath, err) @@ -144,11 +161,8 @@ func (p *provider) prepare(ctx context.Context, lw io.Writer) (*DeploySource, er return nil, err } - gac, ok := cfg.GetGenericApplication() - if !ok { - fmt.Fprintf(lw, "Invalid application kind %s\n", cfg.Kind) - return nil, fmt.Errorf("unsupport application kind %s", cfg.Kind) - } + gac := cfg.Spec + fmt.Fprintln(lw, "Successfully loaded the application configuration file") var templProcessors []sourceprocesser.SourceTemplateProcessor @@ -172,11 +186,10 @@ func (p *provider) prepare(ctx context.Context, lw io.Writer) (*DeploySource, er } return &DeploySource{ - RepoDir: repoDir, - AppDir: appDir, - Revision: p.revision, - ApplicationConfig: cfg, - GenericApplicationConfig: &gac, + RepoDir: repoDir, + AppDir: appDir, + Revision: p.revision, + ApplicationConfig: cfgFileContent, }, nil } @@ -201,10 +214,9 @@ func (p *provider) copy(lw io.Writer) (*DeploySource, error) { } return &DeploySource{ - RepoDir: dest, - AppDir: filepath.Join(dest, p.appGitPath.Path), - Revision: p.revision, - ApplicationConfig: p.source.ApplicationConfig, - GenericApplicationConfig: p.source.GenericApplicationConfig, + RepoDir: dest, + AppDir: filepath.Join(dest, p.appGitPath.Path), + Revision: p.revision, + ApplicationConfig: p.source.ApplicationConfig, }, nil } diff --git a/pkg/app/pipedv1/deploysource/sourcecloner.go b/pkg/app/pipedv1/deploysource/sourcecloner.go index f7bad175aa..e956d9f6a8 100644 --- a/pkg/app/pipedv1/deploysource/sourcecloner.go +++ b/pkg/app/pipedv1/deploysource/sourcecloner.go @@ -17,7 +17,7 @@ package deploysource import ( "context" - "github.com/pipe-cd/pipecd/pkg/config" + config "github.com/pipe-cd/pipecd/pkg/configv1" "github.com/pipe-cd/pipecd/pkg/git" ) diff --git a/pkg/app/pipedv1/plugin/kubernetes/server.go b/pkg/app/pipedv1/plugin/kubernetes/server.go index 9653dc7b01..40d1b45eaa 100644 --- a/pkg/app/pipedv1/plugin/kubernetes/server.go +++ b/pkg/app/pipedv1/plugin/kubernetes/server.go @@ -115,6 +115,12 @@ func (s *plugin) run(ctx context.Context, input cli.Input) (runErr error) { }) } + // Start log persister + persister := logpersister.NewPersister(pipedapiClient, input.Logger) + group.Go(func() error { + return persister.Run(ctx) + }) + // Start a gRPC server for handling external API requests. { var ( @@ -122,7 +128,7 @@ func (s *plugin) run(ctx context.Context, input cli.Input) (runErr error) { cfg, input.Logger, toolregistry.NewToolRegistry(pipedapiClient), - logpersister.NewPersister(pipedapiClient, input.Logger), + persister, ) opts = []rpc.Option{ rpc.WithPort(cfg.Port), diff --git a/pkg/app/pipedv1/sourceprocesser/attacher.go b/pkg/app/pipedv1/sourceprocesser/attacher.go index 22ee5e360c..4d6b1e0ba2 100644 --- a/pkg/app/pipedv1/sourceprocesser/attacher.go +++ b/pkg/app/pipedv1/sourceprocesser/attacher.go @@ -22,7 +22,7 @@ import ( "github.com/Masterminds/sprig/v3" - "github.com/pipe-cd/pipecd/pkg/config" + config "github.com/pipe-cd/pipecd/pkg/configv1" ) type attachmentProcessor struct { diff --git a/pkg/app/pipedv1/sourceprocesser/attacher_test.go b/pkg/app/pipedv1/sourceprocesser/attacher_test.go index 3ac21afc2b..08623ca9eb 100644 --- a/pkg/app/pipedv1/sourceprocesser/attacher_test.go +++ b/pkg/app/pipedv1/sourceprocesser/attacher_test.go @@ -24,7 +24,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/pipe-cd/pipecd/pkg/config" + config "github.com/pipe-cd/pipecd/pkg/configv1" ) func TestAttachData(t *testing.T) { diff --git a/pkg/app/pipedv1/sourceprocesser/decrypter.go b/pkg/app/pipedv1/sourceprocesser/decrypter.go index c925518c14..63fed0c5b2 100644 --- a/pkg/app/pipedv1/sourceprocesser/decrypter.go +++ b/pkg/app/pipedv1/sourceprocesser/decrypter.go @@ -22,7 +22,7 @@ import ( "github.com/Masterminds/sprig/v3" - "github.com/pipe-cd/pipecd/pkg/config" + config "github.com/pipe-cd/pipecd/pkg/configv1" ) type secretDecrypter interface { diff --git a/pkg/app/pipedv1/sourceprocesser/decrypter_test.go b/pkg/app/pipedv1/sourceprocesser/decrypter_test.go index c0fdfe43fb..6fa1373da2 100644 --- a/pkg/app/pipedv1/sourceprocesser/decrypter_test.go +++ b/pkg/app/pipedv1/sourceprocesser/decrypter_test.go @@ -24,7 +24,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/pipe-cd/pipecd/pkg/config" + config "github.com/pipe-cd/pipecd/pkg/configv1" ) type testSecretDecrypter struct { diff --git a/pkg/app/pipedv1/sourceprocesser/sourceprocesser_test.go b/pkg/app/pipedv1/sourceprocesser/sourceprocesser_test.go index ddb4d8395c..d3dc09ae9a 100644 --- a/pkg/app/pipedv1/sourceprocesser/sourceprocesser_test.go +++ b/pkg/app/pipedv1/sourceprocesser/sourceprocesser_test.go @@ -24,7 +24,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/pipe-cd/pipecd/pkg/config" + config "github.com/pipe-cd/pipecd/pkg/configv1" ) func TestSourceProcessor(t *testing.T) { diff --git a/pkg/configv1/application.go b/pkg/configv1/application.go index 4341eb5487..f1d69f0a04 100644 --- a/pkg/configv1/application.go +++ b/pkg/configv1/application.go @@ -40,7 +40,7 @@ type GenericApplicationSpec struct { // Forcibly use QuickSync or Pipeline when commit message matched the specified pattern. CommitMatcher DeploymentCommitMatcher `json:"commitMatcher"` // Pipeline for deploying progressively. - Pipeline *DeploymentPipeline `json:"pipeline"` + Pipeline *DeploymentPipeline `json:"pipeline" default:"{}"` // The trigger configuration use to determine trigger logic. Trigger Trigger `json:"trigger"` // Configuration to be used once the deployment is triggered successfully. @@ -167,7 +167,15 @@ func (s GenericApplicationSpec) GetStage(index int32) (PipelineStage, bool) { return s.Pipeline.Stages[index], true } +// GetStageByte returns the JSON-encoded byte representation of the stage at the specified index. +// If the pipeline is not defined, it returns nil and true. This is QuickSync specific. +// If the stage index is invalid, it returns nil and false. func (s GenericApplicationSpec) GetStageByte(index int32) ([]byte, bool) { + // Return empty byte if the pipeline is not defined. + if len(s.Pipeline.Stages) == 0 { + return nil, true + } + stage, ok := s.GetStage(index) if !ok { return nil, false diff --git a/pkg/configv1/application_test.go b/pkg/configv1/application_test.go index 15eb2f17bb..ab7c0baf6b 100644 --- a/pkg/configv1/application_test.go +++ b/pkg/configv1/application_test.go @@ -18,6 +18,7 @@ import ( "testing" "time" + "github.com/creasty/defaults" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -403,6 +404,7 @@ func TestGenericTriggerConfiguration(t *testing.T) { Planner: DeploymentPlanner{ AutoRollback: newBoolPointer(true), }, + Pipeline: &DeploymentPipeline{}, }, expectedError: nil, }, @@ -446,6 +448,7 @@ func TestTrueByDefaultBoolConfiguration(t *testing.T) { Planner: DeploymentPlanner{ AutoRollback: newBoolPointer(true), }, + Pipeline: &DeploymentPipeline{}, }, expectedError: nil, }, @@ -467,6 +470,7 @@ func TestTrueByDefaultBoolConfiguration(t *testing.T) { Planner: DeploymentPlanner{ AutoRollback: newBoolPointer(true), }, + Pipeline: &DeploymentPipeline{}, }, expectedError: nil, }, @@ -488,6 +492,7 @@ func TestTrueByDefaultBoolConfiguration(t *testing.T) { Planner: DeploymentPlanner{ AutoRollback: newBoolPointer(true), }, + Pipeline: &DeploymentPipeline{}, }, expectedError: nil, }, @@ -549,6 +554,7 @@ func TestGenericPostSyncConfiguration(t *testing.T) { }, }, }, + Pipeline: &DeploymentPipeline{}, }, expectedError: nil, }, @@ -565,3 +571,58 @@ func TestGenericPostSyncConfiguration(t *testing.T) { }) } } +func TestGetStageByte(t *testing.T) { + testcases := []struct { + name string + s GenericApplicationSpec + index int32 + want []byte + wantOk bool + }{ + { + name: "pipeline not defined", + s: GenericApplicationSpec{}, + index: 0, + want: nil, + wantOk: true, + }, + { + name: "valid stage index", + s: GenericApplicationSpec{ + Pipeline: &DeploymentPipeline{ + Stages: []PipelineStage{ + { + Name: model.StageK8sSync, + }, + }, + }, + }, + index: 0, + want: []byte(`{"id":"","name":"K8S_SYNC","timeout":"0s","with":null}`), + wantOk: true, + }, + { + name: "invalid stage index", + s: GenericApplicationSpec{ + Pipeline: &DeploymentPipeline{ + Stages: []PipelineStage{ + { + Name: model.StageK8sSync, + }, + }, + }, + }, + index: 1, + want: nil, + wantOk: false, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + defaults.Set(&tc.s) + got, ok := tc.s.GetStageByte(tc.index) + assert.Equal(t, tc.wantOk, ok) + assert.Equal(t, string(tc.want), string(got)) + }) + } +}