@@ -18,13 +18,15 @@ import (
1818 "context"
1919 "encoding/json"
2020 "fmt"
21+ "time"
2122
2223 "go.uber.org/zap"
2324 "google.golang.org/grpc"
2425 "google.golang.org/grpc/codes"
2526 "google.golang.org/grpc/status"
2627
2728 config "github.com/pipe-cd/pipecd/pkg/configv1"
29+ "github.com/pipe-cd/pipecd/pkg/model"
2830 "github.com/pipe-cd/pipecd/pkg/plugin/api/v1alpha1/deployment"
2931 "github.com/pipe-cd/pipecd/pkg/plugin/logpersister"
3032 "github.com/pipe-cd/pipecd/pkg/plugin/pipedapi"
@@ -71,7 +73,7 @@ type PipelineSyncPlugin[Config, DeployTargetConfig any] interface {
7173 // FetchDefinedStages returns the list of stages that the plugin can execute.
7274 FetchDefinedStages () []string
7375 // BuildPipelineSyncStages builds the stages that will be executed by the plugin.
74- BuildPipelineSyncStages (context.Context , * Config , * Client , TODO ) (TODO , error )
76+ BuildPipelineSyncStages (context.Context , * Config , * Client , * BuildPipelineSyncStagesRequest ) (* BuildPipelineSyncStagesResponse , error )
7577 // ExecuteStage executes the given stage.
7678 ExecuteStage (context.Context , * Config , []* DeployTarget [DeployTargetConfig ], * Client , logpersister.StageLogPersister , TODO ) (TODO , error )
7779}
@@ -156,7 +158,7 @@ func (s *DeploymentPluginServiceServer[Config, DeployTargetConfig]) DetermineStr
156158 return nil , status .Errorf (codes .Unimplemented , "method DetermineStrategy not implemented" )
157159}
158160func (s * DeploymentPluginServiceServer [Config , DeployTargetConfig ]) BuildPipelineSyncStages (ctx context.Context , request * deployment.BuildPipelineSyncStagesRequest ) (* deployment.BuildPipelineSyncStagesResponse , error ) {
159- return nil , status . Errorf ( codes . Unimplemented , "method BuildPipelineSyncStages not implemented" )
161+ return buildPipelineSyncStages ( ctx , s . base , & s . config , nil , request ) // TODO: pass the real client
160162}
161163func (s * DeploymentPluginServiceServer [Config , DeployTargetConfig ]) BuildQuickSyncStages (context.Context , * deployment.BuildQuickSyncStagesRequest ) (* deployment.BuildQuickSyncStagesResponse , error ) {
162164 return nil , status .Errorf (codes .Unimplemented , "method BuildQuickSyncStages not implemented" )
@@ -213,11 +215,144 @@ func (s *PipelineSyncPluginServiceServer[Config, DeployTargetConfig]) DetermineS
213215 return & deployment.DetermineStrategyResponse {Unsupported : true }, nil
214216}
215217func (s * PipelineSyncPluginServiceServer [Config , DeployTargetConfig ]) BuildPipelineSyncStages (ctx context.Context , request * deployment.BuildPipelineSyncStagesRequest ) (* deployment.BuildPipelineSyncStagesResponse , error ) {
216- return nil , status . Errorf ( codes . Unimplemented , "method BuildPipelineSyncStages not implemented" )
218+ return buildPipelineSyncStages ( ctx , s . base , & s . config , nil , request ) // TODO: pass the real client
217219}
218220func (s * PipelineSyncPluginServiceServer [Config , DeployTargetConfig ]) BuildQuickSyncStages (context.Context , * deployment.BuildQuickSyncStagesRequest ) (* deployment.BuildQuickSyncStagesResponse , error ) {
219221 return nil , status .Errorf (codes .Unimplemented , "method BuildQuickSyncStages not implemented" )
220222}
221223func (s * PipelineSyncPluginServiceServer [Config , DeployTargetConfig ]) ExecuteStage (context.Context , * deployment.ExecuteStageRequest ) (* deployment.ExecuteStageResponse , error ) {
222224 return nil , status .Errorf (codes .Unimplemented , "method ExecuteStage not implemented" )
223225}
226+
227+ // buildPipelineSyncStages builds the stages that will be executed by the plugin.
228+ func buildPipelineSyncStages [Config , DeployTargetConfig any ](ctx context.Context , plugin PipelineSyncPlugin [Config , DeployTargetConfig ], config * Config , client * Client , request * deployment.BuildPipelineSyncStagesRequest ) (* deployment.BuildPipelineSyncStagesResponse , error ) {
229+ resp , err := plugin .BuildPipelineSyncStages (ctx , config , client , newPipelineSyncStagesRequest (request ))
230+ if err != nil {
231+ return nil , status .Errorf (codes .Internal , "failed to build pipeline sync stages: %v" , err )
232+ }
233+ return newPipelineSyncStagesResponse (plugin , time .Now (), request , resp )
234+ }
235+
236+ // ManualOperation represents the manual operation that the user can perform.
237+ type ManualOperation int
238+
239+ const (
240+ // ManualOperationNone indicates that there is no manual operation.
241+ ManualOperationNone ManualOperation = iota
242+ // ManualOperationSkip indicates that the manual operation is to skip the stage.
243+ ManualOperationSkip
244+ // ManualOperationApprove indicates that the manual operation is to approve the stage.
245+ ManualOperationApprove
246+ )
247+
248+ // toModelEnum converts the ManualOperation to the model.ManualOperation.
249+ func (o ManualOperation ) toModelEnum () model.ManualOperation {
250+ switch o {
251+ case ManualOperationNone :
252+ return model .ManualOperation_MANUAL_OPERATION_NONE
253+ case ManualOperationSkip :
254+ return model .ManualOperation_MANUAL_OPERATION_SKIP
255+ case ManualOperationApprove :
256+ return model .ManualOperation_MANUAL_OPERATION_APPROVE
257+ default :
258+ return model .ManualOperation_MANUAL_OPERATION_UNKNOWN
259+ }
260+ }
261+
262+ // newPipelineSyncStagesRequest converts the request to the internal representation.
263+ func newPipelineSyncStagesRequest (request * deployment.BuildPipelineSyncStagesRequest ) * BuildPipelineSyncStagesRequest {
264+ stages := make ([]StageConfig , 0 , len (request .Stages ))
265+ for _ , s := range request .GetStages () {
266+ stages = append (stages , StageConfig {
267+ Index : int (s .GetIndex ()),
268+ Name : s .GetName (),
269+ Config : s .GetConfig (),
270+ })
271+ }
272+ return & BuildPipelineSyncStagesRequest {
273+ Rollback : request .GetRollback (),
274+ Stages : stages ,
275+ }
276+ }
277+
278+ // newPipelineSyncStagesResponse converts the response to the external representation.
279+ func newPipelineSyncStagesResponse (plugin Plugin , now time.Time , request * deployment.BuildPipelineSyncStagesRequest , response * BuildPipelineSyncStagesResponse ) (* deployment.BuildPipelineSyncStagesResponse , error ) {
280+ // Convert the request stages to a map for easier access.
281+ requestStages := make (map [int ]* deployment.BuildPipelineSyncStagesRequest_StageConfig , len (request .GetStages ()))
282+ for _ , s := range request .GetStages () {
283+ requestStages [int (s .GetIndex ())] = s
284+ }
285+
286+ stages := make ([]* model.PipelineStage , 0 , len (response .Stages ))
287+ for _ , s := range response .Stages {
288+ // Find the corresponding stage in the request.
289+ requestStage , ok := requestStages [s .Index ]
290+ if ! ok {
291+ return nil , status .Errorf (codes .Internal , "missing stage with index %d in the request, it's unexpected behavior of the plugin" , s .Index )
292+ }
293+ id := requestStage .GetId ()
294+ if id == "" {
295+ id = fmt .Sprintf ("%s-stage-%d" , plugin .Name (), s .Index )
296+ }
297+ stages = append (stages , & model.PipelineStage {
298+ Id : id ,
299+ Name : s .Name ,
300+ Desc : requestStage .GetDesc (),
301+ Index : int32 (s .Index ),
302+ Status : model .StageStatus_STAGE_NOT_STARTED_YET ,
303+ StatusReason : "" , // TODO: set the reason
304+ Metadata : s .Metadata ,
305+ Rollback : s .Rollback ,
306+ CreatedAt : now .Unix (),
307+ UpdatedAt : now .Unix (),
308+ AvailableOperation : s .AvailableOperation .toModelEnum (),
309+ })
310+ }
311+ return & deployment.BuildPipelineSyncStagesResponse {
312+ Stages : stages ,
313+ }, nil
314+ }
315+
316+ // BuildPipelineSyncStagesRequest is the request to build pipeline sync stages.
317+ // Rollback indicates whether the stages for rollback are requested.
318+ type BuildPipelineSyncStagesRequest struct {
319+ // Rollback indicates whether the stages for rollback are requested.
320+ Rollback bool
321+ // Stages contains the stage names and their configurations.
322+ Stages []StageConfig
323+ }
324+
325+ // StageConfig represents the configuration of a stage.
326+ type StageConfig struct {
327+ // Index is the order of the stage in the pipeline.
328+ Index int
329+ // Name is the name of the stage.
330+ // It must be one of the stages returned by FetchDefinedStages.
331+ Name string
332+ // Config is the configuration of the stage.
333+ // It should be marshaled to JSON bytes.
334+ // The plugin should unmarshal it to the appropriate struct.
335+ Config []byte
336+ }
337+
338+ // BuildPipelineSyncStagesResponse is the response of the request to build pipeline sync stages.
339+ type BuildPipelineSyncStagesResponse struct {
340+ Stages []PipelineStage
341+ }
342+
343+ // PipelineStage represents a stage in the pipeline.
344+ type PipelineStage struct {
345+ // Index is the order of the stage in the pipeline.
346+ // The value must be one of the index of the stage in the request.
347+ // The rollback stage should have the same index as the original stage.
348+ Index int
349+ // Name is the name of the stage.
350+ // It must be one of the stages returned by FetchDefinedStages.
351+ Name string
352+ // Rollback indicates whether the stage is for rollback.
353+ Rollback bool
354+ // Metadata contains the metadata of the stage.
355+ Metadata map [string ]string
356+ // AvailableOperation indicates the manual operation that the user can perform.
357+ AvailableOperation ManualOperation
358+ }
0 commit comments