Skip to content

Commit 14f021d

Browse files
committed
fix: return error instead of panicking on empty rollback stages
1 parent d322d3e commit 14f021d

3 files changed

Lines changed: 38 additions & 6 deletions

File tree

pkg/app/pipedv1/plugin/ecs/deployment/pipeline.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,15 @@
1515
package deployment
1616

1717
import (
18+
"errors"
19+
1820
sdk "github.com/pipe-cd/piped-plugin-sdk-go"
1921
)
2022

23+
// ErrRollbackRequiresStages is returned when a rollback is requested but no
24+
// stage was provided in the request.
25+
var ErrRollbackRequiresStages = errors.New("rollback requires at least one stage")
26+
2127
const (
2228
// StageECSSync represents the ECS sync stage.
2329
StageECSSync = "ECS_SYNC"
@@ -76,7 +82,7 @@ func buildQuickSyncPipeline(autoRollback bool) []sdk.QuickSyncStage {
7682
return out
7783
}
7884

79-
func buildPipelineStages(input *sdk.BuildPipelineSyncStagesInput) []sdk.PipelineStage {
85+
func buildPipelineStages(input *sdk.BuildPipelineSyncStagesInput) ([]sdk.PipelineStage, error) {
8086
stages := input.Request.Stages
8187

8288
out := make([]sdk.PipelineStage, 0, len(stages)+1)
@@ -90,6 +96,11 @@ func buildPipelineStages(input *sdk.BuildPipelineSyncStagesInput) []sdk.Pipeline
9096
}
9197

9298
if input.Request.Rollback {
99+
// Guard against a rollback request without any stage, which would
100+
// panic on stages[0] below.
101+
if len(stages) == 0 {
102+
return nil, ErrRollbackRequiresStages
103+
}
93104
// The rollback stage must reuse one of the requested indexes and
94105
// piped runs rollback stages as a trail sorted by index.
95106
// Use the smallest requested index so our rollback runs first among all plugins' rollbacks.
@@ -106,5 +117,5 @@ func buildPipelineStages(input *sdk.BuildPipelineSyncStagesInput) []sdk.Pipeline
106117
})
107118
}
108119

109-
return out
120+
return out, nil
110121
}

pkg/app/pipedv1/plugin/ecs/deployment/pipeline_test.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ func TestBuildPipelineStages(t *testing.T) {
8181
Rollback: tc.rollback,
8282
},
8383
}
84-
got := buildPipelineStages(input)
84+
got, err := buildPipelineStages(input)
85+
require.NoError(t, err)
8586

8687
require.Len(t, got, len(tc.wantNames))
8788
for i, s := range got {
@@ -93,6 +94,21 @@ func TestBuildPipelineStages(t *testing.T) {
9394
}
9495
}
9596

97+
// TestBuildPipelineStagesRollbackWithoutStages ensures a rollback request with
98+
// no stages returns an error instead of panicking on stages[0].
99+
func TestBuildPipelineStagesRollbackWithoutStages(t *testing.T) {
100+
t.Parallel()
101+
102+
got, err := buildPipelineStages(&sdk.BuildPipelineSyncStagesInput{
103+
Request: sdk.BuildPipelineSyncStagesRequest{
104+
Stages: []sdk.StageConfig{},
105+
Rollback: true,
106+
},
107+
})
108+
assert.Nil(t, got)
109+
assert.ErrorIs(t, err, ErrRollbackRequiresStages)
110+
}
111+
96112
// replica of piped's controller.validateStageIndexes (unexported there), kept in sync manually.
97113
func validateStageIndexes(req []sdk.StageConfig, res []sdk.PipelineStage) error {
98114
reqIndexes := make(map[int]struct{})
@@ -117,14 +133,15 @@ func TestBuildPipelineStagesRollbackIndexContract(t *testing.T) {
117133
{Name: StageECSPrimaryRollout, Index: 0},
118134
{Name: StageECSCanaryRollout, Index: 1},
119135
}
120-
got := buildPipelineStages(&sdk.BuildPipelineSyncStagesInput{
136+
got, err := buildPipelineStages(&sdk.BuildPipelineSyncStagesInput{
121137
Request: sdk.BuildPipelineSyncStagesRequest{
122138
Stages: reqStages,
123139
Rollback: true,
124140
},
125141
})
142+
require.NoError(t, err)
126143

127-
err := validateStageIndexes(reqStages, got)
144+
err = validateStageIndexes(reqStages, got)
128145
assert.NoError(t, err, "rollback stage index must be one of the requested indexes")
129146
}
130147

pkg/app/pipedv1/plugin/ecs/deployment/plugin.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,12 @@ func (p *ECSPlugin) BuildPipelineSyncStages(
5555
_ *ecsconfig.ECSPluginConfig,
5656
input *sdk.BuildPipelineSyncStagesInput,
5757
) (*sdk.BuildPipelineSyncStagesResponse, error) {
58+
stages, err := buildPipelineStages(input)
59+
if err != nil {
60+
return nil, err
61+
}
5862
return &sdk.BuildPipelineSyncStagesResponse{
59-
Stages: buildPipelineStages(input),
63+
Stages: stages,
6064
}, nil
6165
}
6266

0 commit comments

Comments
 (0)