Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/app/piped/executor/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/pipe-cd/pipecd/pkg/app/piped/executor/ecs"
"github.com/pipe-cd/pipecd/pkg/app/piped/executor/kubernetes"
"github.com/pipe-cd/pipecd/pkg/app/piped/executor/lambda"
"github.com/pipe-cd/pipecd/pkg/app/piped/executor/scriptrun"
"github.com/pipe-cd/pipecd/pkg/app/piped/executor/terraform"
"github.com/pipe-cd/pipecd/pkg/app/piped/executor/wait"
"github.com/pipe-cd/pipecd/pkg/app/piped/executor/waitapproval"
Expand Down Expand Up @@ -112,4 +113,5 @@ func init() {
wait.Register(defaultRegistry)
waitapproval.Register(defaultRegistry)
customsync.Register(defaultRegistry)
scriptrun.Register(defaultRegistry)
}
68 changes: 68 additions & 0 deletions pkg/app/piped/executor/scriptrun/scriptrun.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package scriptrun

import (
"os"
"os/exec"

"github.com/pipe-cd/pipecd/pkg/app/piped/executor"
"github.com/pipe-cd/pipecd/pkg/model"
)

type registerer interface {
Register(stage model.Stage, f executor.Factory) error
RegisterRollback(kind model.RollbackKind, f executor.Factory) error
}

type Executor struct {
executor.Input
}

func (e *Executor) Execute(sig executor.StopSignal) model.StageStatus {
e.LogPersister.Infof("Start executing the script run stage")

opts := e.Input.StageConfig.ScriptRunStageOptions
if opts == nil {
e.LogPersister.Infof("option for script run stage not found")
return model.StageStatus_STAGE_FAILURE
}

if opts.Run == "" {
return model.StageStatus_STAGE_SUCCESS
}

envs := make([]string, 0, len(opts.Env))
for key, value := range opts.Env {
envs = append(envs, key+"="+value)
}

cmd := exec.Command("/bin/sh", "-l", "-c", opts.Run)
cmd.Env = append(os.Environ(), envs...)
cmd.Stdout = e.LogPersister
cmd.Stderr = e.LogPersister

e.LogPersister.Infof("executing script:")
e.LogPersister.Infof(opts.Run)

if err := cmd.Run(); err != nil {
return model.StageStatus_STAGE_FAILURE
}
return model.StageStatus_STAGE_SUCCESS
}

type RollbackExecutor struct {
executor.Input
}

func (e *RollbackExecutor) Execute(sig executor.StopSignal) model.StageStatus {
e.LogPersister.Infof("Unimplement: rollbacking the script run stage")
return model.StageStatus_STAGE_FAILURE
}

// Register registers this executor factory into a given registerer.
func Register(r registerer) {
r.Register(model.StageScriptRun, func(in executor.Input) executor.Executor {
return &Executor{
Input: in,
}
})
}
22 changes: 22 additions & 0 deletions pkg/config/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ type PipelineStage struct {
WaitStageOptions *WaitStageOptions
WaitApprovalStageOptions *WaitApprovalStageOptions
AnalysisStageOptions *AnalysisStageOptions
ScriptRunStageOptions *ScriptRunStageOptions

K8sPrimaryRolloutStageOptions *K8sPrimaryRolloutStageOptions
K8sCanaryRolloutStageOptions *K8sCanaryRolloutStageOptions
Expand Down Expand Up @@ -291,6 +292,12 @@ func (s *PipelineStage) UnmarshalJSON(data []byte) error {
if len(gs.With) > 0 {
err = json.Unmarshal(gs.With, s.AnalysisStageOptions)
}
case model.StageScriptRun:
s.ScriptRunStageOptions = &ScriptRunStageOptions{}
if len(gs.With) > 0 {
err = json.Unmarshal(gs.With, s.ScriptRunStageOptions)
}

case model.StageK8sPrimaryRollout:
s.K8sPrimaryRolloutStageOptions = &K8sPrimaryRolloutStageOptions{}
if len(gs.With) > 0 {
Expand Down Expand Up @@ -485,6 +492,21 @@ func (a *AnalysisStageOptions) Validate() error {
return nil
}

// ScriptRunStageOptions contains all configurable values for a SCRIPT_RUN stage.
type ScriptRunStageOptions struct {
Env map[string]string `json:"env"`
Run string `json:"run"`
OnRollback string `json:"onRollback"`
}

// Validate checks the required fields of ScriptRunStageOptions.
func (s *ScriptRunStageOptions) Validate() error {
if s.Run == "" {
return fmt.Errorf("SCRIPT_RUN stage requires run field")
}
return nil
}

type AnalysisTemplateRef struct {
Name string `json:"name"`
AppArgs map[string]string `json:"appArgs"`
Expand Down
30 changes: 30 additions & 0 deletions pkg/config/application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -702,3 +702,33 @@ func TestCustomSyncConfig(t *testing.T) {
})
}
}

// TODO: Add testcases for other kinds of applications.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we want here? I don't see any kinds here 👀

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

b1e6555
Sorry this comment is meaningless 🙏 so deleted

func TestScriptSycConfiguration(t *testing.T) {
testcases := []struct {
name string
opts ScriptRunStageOptions
wantErr bool
}{
{
name: "valid",
opts: ScriptRunStageOptions{
Run: "echo 'hello world'",
},
wantErr: false,
},
{
name: "invalid",
opts: ScriptRunStageOptions{
Run: "",
},
wantErr: true,
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
err := tc.opts.Validate()
assert.Equal(t, tc.wantErr, err != nil)
})
}
}
3 changes: 3 additions & 0 deletions pkg/model/stage.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const (
// StageAnalysis represents the waiting state for analysing
// the application status based on metrics, log, http request...
StageAnalysis Stage = "ANALYSIS"
// StageScriptRun represents a state where
// the specified script will be executed.
StageScriptRun Stage = "SCRIPT_RUN"

// StageK8sSync represents the state where
// all resources should be synced with the Git state.
Expand Down