diff --git a/pkg/app/api/grpcapi/piped_api.go b/pkg/app/api/grpcapi/piped_api.go index 3da696e40e..9b9eeb5264 100644 --- a/pkg/app/api/grpcapi/piped_api.go +++ b/pkg/app/api/grpcapi/piped_api.go @@ -424,6 +424,16 @@ func (a *PipedAPI) CreateDeployment(ctx context.Context, req *pipedservice.Creat a.logger.Error("failed to create deployment", zap.Error(err)) return nil, status.Error(codes.Internal, "failed to create deployment") } + + // If the deployment doesn't belong to another chain, return immediately. + if req.Deployment.DeploymentChainId == "" { + return &pipedservice.CreateDeploymentResponse{}, nil + } + + // Otherwise, add the created deployment ref to its chain block model. + if err = a.deploymentChainStore.UpdateDeploymentChain(ctx, req.Deployment.DeploymentChainId, datastore.DeploymentChainAddDeploymentToBlock(req.Deployment)); err != nil { + return nil, status.Error(codes.Internal, "failed to add deployment ref to its chain block") + } return &pipedservice.CreateDeploymentResponse{}, nil } diff --git a/pkg/app/piped/trigger/deployment.go b/pkg/app/piped/trigger/deployment.go index f1ba5f0896..a323fc9861 100644 --- a/pkg/app/piped/trigger/deployment.go +++ b/pkg/app/piped/trigger/deployment.go @@ -50,6 +50,7 @@ func buildDeployment( now time.Time, noti *config.DeploymentNotification, deploymentChainID string, + deploymentChainBlockIndex int32, ) (*model.Deployment, error) { var commitURL string @@ -92,15 +93,16 @@ func buildDeployment( SyncStrategy: syncStrategy, StrategySummary: strategySummary, }, - GitPath: app.GitPath, - CloudProvider: app.CloudProvider, - Labels: app.Labels, - Status: model.DeploymentStatus_DEPLOYMENT_PENDING, - StatusReason: "The deployment is waiting to be planned", - Metadata: metadata, - CreatedAt: now.Unix(), - UpdatedAt: now.Unix(), - DeploymentChainId: deploymentChainID, + GitPath: app.GitPath, + CloudProvider: app.CloudProvider, + Labels: app.Labels, + Status: model.DeploymentStatus_DEPLOYMENT_PENDING, + StatusReason: "The deployment is waiting to be planned", + Metadata: metadata, + CreatedAt: now.Unix(), + UpdatedAt: now.Unix(), + DeploymentChainId: deploymentChainID, + DeploymentChainBlockIndex: deploymentChainBlockIndex, } return deployment, nil diff --git a/pkg/app/piped/trigger/trigger.go b/pkg/app/piped/trigger/trigger.go index 0bf3464b6f..01f5fde135 100644 --- a/pkg/app/piped/trigger/trigger.go +++ b/pkg/app/piped/trigger/trigger.go @@ -248,10 +248,11 @@ func (t *Trigger) checkRepoCandidates(ctx context.Context, repoID string, cs []c } var ( - commander string - strategy model.SyncStrategy - strategySummary string - deploymentChainID string + commander string + strategy model.SyncStrategy + strategySummary string + deploymentChainID string + deploymentChainBlockIndex int32 ) switch c.kind { @@ -269,6 +270,7 @@ func (t *Trigger) checkRepoCandidates(ctx context.Context, repoID string, cs []c commander = c.command.Commander strategySummary = "Sync application in chain" deploymentChainID = c.command.GetChainSyncApplication().DeploymentChainId + deploymentChainBlockIndex = c.command.GetChainSyncApplication().BlockIndex case model.TriggerKind_ON_OUT_OF_SYNC: strategy = model.SyncStrategy_QUICK_SYNC @@ -289,6 +291,7 @@ func (t *Trigger) checkRepoCandidates(ctx context.Context, repoID string, cs []c time.Now(), appCfg.DeploymentNotification, deploymentChainID, + deploymentChainBlockIndex, ) if err != nil { msg := fmt.Sprintf("failed to build deployment for application %s: %v", app.Id, err) diff --git a/pkg/app/web/src/__fixtures__/dummy-deployment.ts b/pkg/app/web/src/__fixtures__/dummy-deployment.ts index 8ad653fdd3..fb73926e30 100644 --- a/pkg/app/web/src/__fixtures__/dummy-deployment.ts +++ b/pkg/app/web/src/__fixtures__/dummy-deployment.ts @@ -44,6 +44,7 @@ export const dummyDeployment: Deployment.AsObject = { kind: ApplicationKind.KUBERNETES, metadataMap: [], deploymentChainId: "", + deploymentChainBlockIndex: 0, }; export function createDeploymentFromObject(o: Deployment.AsObject): Deployment { diff --git a/pkg/app/web/src/components/deployments-detail-page/pipeline/index.stories.tsx b/pkg/app/web/src/components/deployments-detail-page/pipeline/index.stories.tsx index 0f27144609..3e28f50fad 100644 --- a/pkg/app/web/src/components/deployments-detail-page/pipeline/index.stories.tsx +++ b/pkg/app/web/src/components/deployments-detail-page/pipeline/index.stories.tsx @@ -119,6 +119,7 @@ const fakeDeployment: Deployment.AsObject = { createdAt: 1592203166, updatedAt: 1592203166, deploymentChainId: "", + deploymentChainBlockIndex: 0, }; export default { diff --git a/pkg/datastore/deploymentchainstore.go b/pkg/datastore/deploymentchainstore.go index 90bbb61665..f464cc1674 100644 --- a/pkg/datastore/deploymentchainstore.go +++ b/pkg/datastore/deploymentchainstore.go @@ -16,6 +16,7 @@ package datastore import ( "context" + "fmt" "time" "github.com/pipe-cd/pipe/pkg/model" @@ -23,8 +24,38 @@ import ( const DeploymentChainModelKind = "DeploymentChain" +var deploymentChainFactory = func() interface{} { + return &model.DeploymentChain{} +} + +var ( + DeploymentChainAddDeploymentToBlock = func(deployment *model.Deployment) func(*model.DeploymentChain) error { + return func(dc *model.DeploymentChain) error { + if deployment.DeploymentChainBlockIndex == 0 || deployment.DeploymentChainBlockIndex >= int32(len(dc.Blocks)) { + return fmt.Errorf("invalid block index provided") + } + block := dc.Blocks[deployment.DeploymentChainBlockIndex] + var updated bool + for _, node := range block.Nodes { + if node.ApplicationRef.ApplicationId != deployment.ApplicationId { + continue + } + node.DeploymentRef = &model.ChainDeploymentRef{ + DeploymentId: deployment.Id, + } + updated = true + } + if !updated { + return fmt.Errorf("unable to find the right node in chain to assign deployment to") + } + return nil + } + } +) + type DeploymentChainStore interface { AddDeploymentChain(ctx context.Context, d *model.DeploymentChain) error + UpdateDeploymentChain(ctx context.Context, id string, updater func(*model.DeploymentChain) error) error } type deploymentChainStore struct { @@ -54,3 +85,15 @@ func (s *deploymentChainStore) AddDeploymentChain(ctx context.Context, dc *model } return s.ds.Create(ctx, DeploymentChainModelKind, dc.Id, dc) } + +func (s *deploymentChainStore) UpdateDeploymentChain(ctx context.Context, id string, updater func(*model.DeploymentChain) error) error { + now := s.nowFunc().Unix() + return s.ds.Update(ctx, DeploymentChainModelKind, id, deploymentChainFactory, func(e interface{}) error { + dc := e.(*model.DeploymentChain) + if err := updater(dc); err != nil { + return err + } + dc.UpdatedAt = now + return dc.Validate() + }) +} diff --git a/pkg/model/deployment.proto b/pkg/model/deployment.proto index 7dcca595cb..8a1b5b2b50 100644 --- a/pkg/model/deployment.proto +++ b/pkg/model/deployment.proto @@ -89,6 +89,9 @@ message Deployment { // Reference to the chain which the deployment belongs to. // Empty means the deployment is a standalone deployment. string deployment_chain_id = 40; + // Index represents the offset of the node which this deployment + // belongs to. + int32 deployment_chain_block_index = 41; int64 completed_at = 100 [(validate.rules).int64.gte = 0]; int64 created_at = 101 [(validate.rules).int64.gte = 0];