From 40cb5f317b8e56525bbc52bb17d7c0d1ef31f5dc Mon Sep 17 00:00:00 2001 From: noah Date: Fri, 18 Mar 2022 22:44:28 +0900 Subject: [PATCH 1/5] Add a validator for serialization --- internal/interactor/deployment.go | 1 + internal/interactor/validator.go | 19 +++++++++++++++++++ pkg/e/code.go | 2 ++ pkg/e/trans.go | 6 ++++-- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/internal/interactor/deployment.go b/internal/interactor/deployment.go index 42f3b82f..7ee47c09 100644 --- a/internal/interactor/deployment.go +++ b/internal/interactor/deployment.go @@ -27,6 +27,7 @@ type ( FindDeploymentByID(ctx context.Context, id int) (*ent.Deployment, error) FindDeploymentByUID(ctx context.Context, uid int64) (*ent.Deployment, error) FindDeploymentOfRepoByNumber(ctx context.Context, r *ent.Repo, number int) (*ent.Deployment, error) + FindPrevRunningDeployment(ctx context.Context, d *ent.Deployment) (*ent.Deployment, error) FindPrevSuccessDeployment(ctx context.Context, d *ent.Deployment) (*ent.Deployment, error) GetNextDeploymentNumberOfRepo(ctx context.Context, r *ent.Repo) (int, error) CreateDeployment(ctx context.Context, d *ent.Deployment) (*ent.Deployment, error) diff --git a/internal/interactor/validator.go b/internal/interactor/validator.go index 9d21acb5..7d443eec 100644 --- a/internal/interactor/validator.go +++ b/internal/interactor/validator.go @@ -137,3 +137,22 @@ func (v *ReviewValidator) Validate(d *ent.Deployment) error { return e.NewError(e.ErrorCodeDeploymentNotApproved, nil) } + +// SerializationValidator verify if there is currently a running deployment +// for the environment. +type SerializationValidator struct { + Store DeploymentStore +} + +func (v *SerializationValidator) Validate(d *ent.Deployment) error { + d, err := v.Store.FindPrevRunningDeployment(context.Background(), d) + if !e.HasErrorCode(err, e.ErrorCodeEntityNotFound) { + return err + } + + if d != nil { + return e.NewError(e.ErrorCodeDeploymentSerialization, nil) + } + + return nil +} diff --git a/pkg/e/code.go b/pkg/e/code.go index 0f0a739f..c33e981a 100644 --- a/pkg/e/code.go +++ b/pkg/e/code.go @@ -21,6 +21,8 @@ const ( ErrorCodeDeploymentFrozen ErrorCode = "deployment_frozen" // ErrorCodeDeploymentUnapproved is when the deployment is not approved. ErrorCodeDeploymentNotApproved ErrorCode = "deployment_not_approved" + // ErrorCodeDeploymentSerialization is the serialization error. + ErrorCodeDeploymentSerialization ErrorCode = "deployment_serialization" // ErrorCodeDeploymentStatusNotWaiting is the status must be 'waiting' to create a remote deployment. ErrorCodeDeploymentStatusInvalid ErrorCode = "deployment_status_invalid" diff --git a/pkg/e/trans.go b/pkg/e/trans.go index 7e570403..6d5d414b 100644 --- a/pkg/e/trans.go +++ b/pkg/e/trans.go @@ -9,8 +9,9 @@ var messages = map[ErrorCode]string{ ErrorCodeDeploymentInvalid: "The validation has failed.", ErrorCodeDeploymentLocked: "The environment is locked.", ErrorCodeDeploymentFrozen: "It is in the deploy freeze window.", - ErrorCodeDeploymentNotApproved: "The deployment is not approved", - ErrorCodeDeploymentStatusInvalid: "The deployment status is invalid", + ErrorCodeDeploymentNotApproved: "The deployment is not approved.", + ErrorCodeDeploymentSerialization: "There is a running deployment.", + ErrorCodeDeploymentStatusInvalid: "The deployment status is invalid.", ErrorCodeEntityNotFound: "It is not found.", ErrorCodeEntityUnprocessable: "Invalid request payload.", ErrorCodeInternalError: "Server internal error.", @@ -38,6 +39,7 @@ var httpCodes = map[ErrorCode]int{ ErrorCodeDeploymentLocked: http.StatusUnprocessableEntity, ErrorCodeDeploymentFrozen: http.StatusUnprocessableEntity, ErrorCodeDeploymentNotApproved: http.StatusUnprocessableEntity, + ErrorCodeDeploymentSerialization: http.StatusUnprocessableEntity, ErrorCodeDeploymentStatusInvalid: http.StatusUnprocessableEntity, ErrorCodeEntityNotFound: http.StatusNotFound, ErrorCodeEntityUnprocessable: http.StatusUnprocessableEntity, From 058f22e972243a83b62302c51260e78baf33b929 Mon Sep 17 00:00:00 2001 From: noah Date: Fri, 18 Mar 2022 22:53:14 +0900 Subject: [PATCH 2/5] Fix error message --- pkg/e/code.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/e/code.go b/pkg/e/code.go index c33e981a..dd359e06 100644 --- a/pkg/e/code.go +++ b/pkg/e/code.go @@ -3,6 +3,7 @@ package e import ( "errors" "fmt" + "strings" ) const ( @@ -78,7 +79,16 @@ func NewErrorWithMessage(code ErrorCode, message string, wrap error) *Error { } func (e *Error) Error() string { - return fmt.Sprintf("code: %s, message: %s, wrap: %s", e.Code, e.Message, e.Wrap) + msgs := []string{ + fmt.Sprintf("Code: %s", e.Code), + fmt.Sprintf("Message: %s", e.Message), + } + + if e.Wrap != nil { + msgs = append(msgs, fmt.Sprintf("Wrap: %s", e.Wrap)) + } + + return strings.Join(msgs, ", ") } func (e *Error) Unwrap() error { From 1ca4aab23d17b85310b63b1e21b2570e53e137ca Mon Sep 17 00:00:00 2001 From: noah Date: Fri, 18 Mar 2022 23:18:48 +0900 Subject: [PATCH 3/5] Add `FindPrevRunningDeployment` method to store --- internal/pkg/store/deployment.go | 26 ++++++++++++ internal/pkg/store/deployment_test.go | 61 +++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/internal/pkg/store/deployment.go b/internal/pkg/store/deployment.go index f82733ed..f6dd2a63 100644 --- a/internal/pkg/store/deployment.go +++ b/internal/pkg/store/deployment.go @@ -208,6 +208,32 @@ func (s *Store) GetNextDeploymentNumberOfRepo(ctx context.Context, r *ent.Repo) return cnt + 1, nil } +// FindPrevRunningDeployment find a deployment of which the status is created, queued, or running. +func (s *Store) FindPrevRunningDeployment(ctx context.Context, d *ent.Deployment) (*ent.Deployment, error) { + d, err := s.c.Deployment. + Query(). + Where( + deployment.And( + deployment.RepoIDEQ(d.RepoID), + deployment.EnvEQ(d.Env), + deployment.StatusIn( + deployment.StatusCreated, + deployment.StatusQueued, + deployment.StatusRunning, + ), + ), + ). + Order(ent.Desc(deployment.FieldCreatedAt)). + First(ctx) + if ent.IsNotFound(err) { + return nil, e.NewErrorWithMessage(e.ErrorCodeEntityNotFound, "The deployment is not found.", err) + } else if err != nil { + return nil, e.NewError(e.ErrorCodeInternalError, err) + } + + return d, nil +} + func (s *Store) FindPrevSuccessDeployment(ctx context.Context, d *ent.Deployment) (*ent.Deployment, error) { d, err := s.c.Deployment. Query(). diff --git a/internal/pkg/store/deployment_test.go b/internal/pkg/store/deployment_test.go index 74e27826..21ec8772 100644 --- a/internal/pkg/store/deployment_test.go +++ b/internal/pkg/store/deployment_test.go @@ -10,6 +10,7 @@ import ( "github.com/gitploy-io/gitploy/model/ent/deployment" "github.com/gitploy-io/gitploy/model/ent/enttest" "github.com/gitploy-io/gitploy/model/ent/migrate" + "github.com/gitploy-io/gitploy/pkg/e" _ "github.com/mattn/go-sqlite3" ) @@ -340,6 +341,66 @@ func TestStore_GetNextDeploymentNumberOfRepo(t *testing.T) { }) } +func TestStore_FindPrevRunningDeployment(t *testing.T) { + t.Run("Returns an not_found error if there's no deployment.", func(t *testing.T) { + client := enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1", + enttest.WithMigrateOptions(migrate.WithForeignKeys(false)), + ) + defer client.Close() + + client.Deployment.Create(). + SetType(deployment.TypeBranch). + SetNumber(1). + SetType("branch"). + SetRef("main"). + SetEnv("prod"). + SetStatus(deployment.StatusSuccess). + SetCreatedAt(time.Now().Add(-1 * time.Hour)). + SetRepoID(1). + SetUserID(1). + SaveX(context.Background()) + + s := NewStore(client) + + _, err := s.FindPrevRunningDeployment(context.Background(), &ent.Deployment{ + Env: "prod", + RepoID: 1, + }) + if !e.HasErrorCode(err, e.ErrorCodeEntityNotFound) { + t.Fatalf("FindPrevRunningDeployment error != NotFoundError: %s", err) + } + }) + + t.Run("Return the latest running deployment.", func(t *testing.T) { + client := enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1", + enttest.WithMigrateOptions(migrate.WithForeignKeys(false)), + ) + defer client.Close() + + client.Deployment.Create(). + SetType(deployment.TypeBranch). + SetNumber(1). + SetType("branch"). + SetRef("main"). + SetEnv("prod"). + SetStatus(deployment.StatusRunning). + SetCreatedAt(time.Now().Add(-1 * time.Hour)). + SetRepoID(1). + SetUserID(1). + SaveX(context.Background()) + + s := NewStore(client) + + _, err := s.FindPrevRunningDeployment(context.Background(), &ent.Deployment{ + Env: "prod", + RepoID: 1, + }) + if err != nil { + t.Fatalf("FindPrevRunningDeployment returns an error: %s", err) + } + }) +} + func TestStore_FindPrevSuccessDeployment(t *testing.T) { ca := time.Now() From 57e8b14cc5bcb13f62379d5b0355a2368cf48c3b Mon Sep 17 00:00:00 2001 From: noah Date: Fri, 18 Mar 2022 23:33:16 +0900 Subject: [PATCH 4/5] Fix `SerializationValidator` --- internal/interactor/_mock.sh | 1 + internal/interactor/deployment.go | 2 + internal/interactor/mock/pkg.go | 128 +++----------------------- internal/interactor/validator.go | 14 ++- internal/interactor/validator_test.go | 36 ++++++++ model/extent/config.go | 4 + 6 files changed, 68 insertions(+), 117 deletions(-) diff --git a/internal/interactor/_mock.sh b/internal/interactor/_mock.sh index 43cb7f97..9053570c 100644 --- a/internal/interactor/_mock.sh +++ b/internal/interactor/_mock.sh @@ -7,6 +7,7 @@ github.com/gitploy-io/gitploy/internal/interactor=user.go\ ,github.com/gitploy-io/gitploy/internal/interactor=deploymentstatistics.go\ ,github.com/gitploy-io/gitploy/internal/interactor=lock.go\ ,github.com/gitploy-io/gitploy/internal/interactor=event.go\ +,github.com/gitploy-io/gitploy/internal/interactor=review.go\ -source ./interface.go \ -package mock \ -destination ./mock/pkg.go \ No newline at end of file diff --git a/internal/interactor/deployment.go b/internal/interactor/deployment.go index 7ee47c09..dc5dee40 100644 --- a/internal/interactor/deployment.go +++ b/internal/interactor/deployment.go @@ -77,6 +77,7 @@ func (i *DeploymentInteractor) Deploy(ctx context.Context, u *ent.User, r *ent.R &RefValidator{Env: env}, &FrozenWindowValidator{Env: env}, &LockValidator{Repo: r, Store: i.store}, + &SerializationValidator{Env: env, Store: i.store}, }) if err := v.Validate(d); err != nil { return nil, err @@ -206,6 +207,7 @@ func (i *DeploymentInteractor) DeployToRemote(ctx context.Context, u *ent.User, &RefValidator{Env: env}, &FrozenWindowValidator{Env: env}, &LockValidator{Repo: r, Store: i.store}, + &SerializationValidator{Env: env, Store: i.store}, &ReviewValidator{Store: i.store}, }) if err := v.Validate(d); err != nil { diff --git a/internal/interactor/mock/pkg.go b/internal/interactor/mock/pkg.go index 146706cd..e9021537 100644 --- a/internal/interactor/mock/pkg.go +++ b/internal/interactor/mock/pkg.go @@ -454,6 +454,21 @@ func (mr *MockStoreMockRecorder) FindPermOfRepo(ctx, r, u interface{}) *gomock.C return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindPermOfRepo", reflect.TypeOf((*MockStore)(nil).FindPermOfRepo), ctx, r, u) } +// FindPrevRunningDeployment mocks base method. +func (m *MockStore) FindPrevRunningDeployment(ctx context.Context, d *ent.Deployment) (*ent.Deployment, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindPrevRunningDeployment", ctx, d) + ret0, _ := ret[0].(*ent.Deployment) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindPrevRunningDeployment indicates an expected call of FindPrevRunningDeployment. +func (mr *MockStoreMockRecorder) FindPrevRunningDeployment(ctx, d interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindPrevRunningDeployment", reflect.TypeOf((*MockStore)(nil).FindPrevRunningDeployment), ctx, d) +} + // FindPrevSuccessDeployment mocks base method. func (m *MockStore) FindPrevSuccessDeployment(ctx context.Context, d *ent.Deployment) (*ent.Deployment, error) { m.ctrl.T.Helper() @@ -1145,119 +1160,6 @@ func (mr *MockDeploymentStatusStoreMockRecorder) SyncDeploymentStatus(ctx, ds in return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncDeploymentStatus", reflect.TypeOf((*MockDeploymentStatusStore)(nil).SyncDeploymentStatus), ctx, ds) } -// MockReviewStore is a mock of ReviewStore interface. -type MockReviewStore struct { - ctrl *gomock.Controller - recorder *MockReviewStoreMockRecorder -} - -// MockReviewStoreMockRecorder is the mock recorder for MockReviewStore. -type MockReviewStoreMockRecorder struct { - mock *MockReviewStore -} - -// NewMockReviewStore creates a new mock instance. -func NewMockReviewStore(ctrl *gomock.Controller) *MockReviewStore { - mock := &MockReviewStore{ctrl: ctrl} - mock.recorder = &MockReviewStoreMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockReviewStore) EXPECT() *MockReviewStoreMockRecorder { - return m.recorder -} - -// CreateReview mocks base method. -func (m *MockReviewStore) CreateReview(ctx context.Context, rv *ent.Review) (*ent.Review, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateReview", ctx, rv) - ret0, _ := ret[0].(*ent.Review) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateReview indicates an expected call of CreateReview. -func (mr *MockReviewStoreMockRecorder) CreateReview(ctx, rv interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateReview", reflect.TypeOf((*MockReviewStore)(nil).CreateReview), ctx, rv) -} - -// FindReviewByID mocks base method. -func (m *MockReviewStore) FindReviewByID(ctx context.Context, id int) (*ent.Review, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindReviewByID", ctx, id) - ret0, _ := ret[0].(*ent.Review) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindReviewByID indicates an expected call of FindReviewByID. -func (mr *MockReviewStoreMockRecorder) FindReviewByID(ctx, id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindReviewByID", reflect.TypeOf((*MockReviewStore)(nil).FindReviewByID), ctx, id) -} - -// FindReviewOfUser mocks base method. -func (m *MockReviewStore) FindReviewOfUser(ctx context.Context, u *ent.User, d *ent.Deployment) (*ent.Review, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindReviewOfUser", ctx, u, d) - ret0, _ := ret[0].(*ent.Review) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindReviewOfUser indicates an expected call of FindReviewOfUser. -func (mr *MockReviewStoreMockRecorder) FindReviewOfUser(ctx, u, d interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindReviewOfUser", reflect.TypeOf((*MockReviewStore)(nil).FindReviewOfUser), ctx, u, d) -} - -// ListReviews mocks base method. -func (m *MockReviewStore) ListReviews(ctx context.Context, d *ent.Deployment) ([]*ent.Review, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListReviews", ctx, d) - ret0, _ := ret[0].([]*ent.Review) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListReviews indicates an expected call of ListReviews. -func (mr *MockReviewStoreMockRecorder) ListReviews(ctx, d interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListReviews", reflect.TypeOf((*MockReviewStore)(nil).ListReviews), ctx, d) -} - -// SearchReviews mocks base method. -func (m *MockReviewStore) SearchReviews(ctx context.Context, u *ent.User) ([]*ent.Review, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SearchReviews", ctx, u) - ret0, _ := ret[0].([]*ent.Review) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SearchReviews indicates an expected call of SearchReviews. -func (mr *MockReviewStoreMockRecorder) SearchReviews(ctx, u interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchReviews", reflect.TypeOf((*MockReviewStore)(nil).SearchReviews), ctx, u) -} - -// UpdateReview mocks base method. -func (m *MockReviewStore) UpdateReview(ctx context.Context, rv *ent.Review) (*ent.Review, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateReview", ctx, rv) - ret0, _ := ret[0].(*ent.Review) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// UpdateReview indicates an expected call of UpdateReview. -func (mr *MockReviewStoreMockRecorder) UpdateReview(ctx, rv interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateReview", reflect.TypeOf((*MockReviewStore)(nil).UpdateReview), ctx, rv) -} - // MockSCM is a mock of SCM interface. type MockSCM struct { ctrl *gomock.Controller diff --git a/internal/interactor/validator.go b/internal/interactor/validator.go index 7d443eec..dec4f26b 100644 --- a/internal/interactor/validator.go +++ b/internal/interactor/validator.go @@ -141,18 +141,24 @@ func (v *ReviewValidator) Validate(d *ent.Deployment) error { // SerializationValidator verify if there is currently a running deployment // for the environment. type SerializationValidator struct { + Env *extent.Env Store DeploymentStore } func (v *SerializationValidator) Validate(d *ent.Deployment) error { - d, err := v.Store.FindPrevRunningDeployment(context.Background(), d) - if !e.HasErrorCode(err, e.ErrorCodeEntityNotFound) { - return err + // Skip if the serialization field is disabled. + if v.Env.Serialization == nil || !*v.Env.Serialization { + return nil } + d, err := v.Store.FindPrevRunningDeployment(context.Background(), d) if d != nil { return e.NewError(e.ErrorCodeDeploymentSerialization, nil) } - return nil + if e.HasErrorCode(err, e.ErrorCodeEntityNotFound) { + return nil + } + + return err } diff --git a/internal/interactor/validator_test.go b/internal/interactor/validator_test.go index 04d4562d..a168c8cd 100644 --- a/internal/interactor/validator_test.go +++ b/internal/interactor/validator_test.go @@ -102,3 +102,39 @@ func TestReviewValidator_Validate(t *testing.T) { } }) } + +func TestSerializationValidator_Validate(t *testing.T) { + t.Run("Returns nil if the serialization is empty.", func(t *testing.T) { + ctrl := gomock.NewController(t) + store := mock.NewMockStore(ctrl) + + v := &i.SerializationValidator{ + Env: &extent.Env{}, + Store: store, + } + + if err := v.Validate(&ent.Deployment{}); err != nil { + t.Fatalf("Validate returns an error: %v", err) + } + }) + + t.Run("Returns an deployment_serialization error if there is a running deployment.", func(t *testing.T) { + t.Log("Start mocking:") + ctrl := gomock.NewController(t) + store := mock.NewMockStore(ctrl) + + t.Log("Return a running deployment.") + store.EXPECT(). + FindPrevRunningDeployment(gomock.Any(), gomock.AssignableToTypeOf(&ent.Deployment{})). + Return(&ent.Deployment{}, nil) + + v := &i.SerializationValidator{ + Env: &extent.Env{Serialization: pointer.ToBool(true)}, + Store: store, + } + + if err := v.Validate(&ent.Deployment{}); !e.HasErrorCode(err, e.ErrorCodeDeploymentSerialization) { + t.Fatalf("Error is not deployment_serialization: %v", err) + } + }) +} diff --git a/model/extent/config.go b/model/extent/config.go index 6fab0ecf..30cd5975 100644 --- a/model/extent/config.go +++ b/model/extent/config.go @@ -35,9 +35,13 @@ type ( // DeployableRef validates the ref is deployable or not. DeployableRef *string `json:"deployable_ref" yaml:"deployable_ref"` + // AutoDeployOn deploys automatically when the pattern is matched. AutoDeployOn *string `json:"auto_deploy_on" yaml:"auto_deploy_on"` + // Serialization verify if there is a running deployment. + Serialization *bool `json:"serialization" yaml:"serialization"` + // Review is the configuration of Review, // It is disabled when it is empty. Review *Review `json:"review,omitempty" yaml:"review"` From 8cdf6f63ca1a4e37f4af857d1fe261c340c3ee85 Mon Sep 17 00:00:00 2001 From: noah Date: Sat, 19 Mar 2022 17:24:27 +0900 Subject: [PATCH 5/5] Fix to validate after building the deployment --- internal/interactor/deployment.go | 25 +++++++++++++------------ internal/interactor/validator.go | 6 ++++++ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/internal/interactor/deployment.go b/internal/interactor/deployment.go index dc5dee40..28f7ca4a 100644 --- a/internal/interactor/deployment.go +++ b/internal/interactor/deployment.go @@ -72,22 +72,12 @@ type ( // But if it requires a review, it saves the payload on the store and waits until reviewed. // It returns an error for a undeployable payload. func (i *DeploymentInteractor) Deploy(ctx context.Context, u *ent.User, r *ent.Repo, d *ent.Deployment, env *extent.Env) (*ent.Deployment, error) { - i.log.Debug("Validate the request.") - v := NewDeploymentValidator([]Validator{ - &RefValidator{Env: env}, - &FrozenWindowValidator{Env: env}, - &LockValidator{Repo: r, Store: i.store}, - &SerializationValidator{Env: env, Store: i.store}, - }) - if err := v.Validate(d); err != nil { - return nil, err - } - number, err := i.store.GetNextDeploymentNumberOfRepo(ctx, r) if err != nil { return nil, e.NewError(e.ErrorCodeInternalError, err) } + i.log.Debug("Get the next number, and build the deployment.") d = &ent.Deployment{ Number: number, Type: d.Type, @@ -100,6 +90,17 @@ func (i *DeploymentInteractor) Deploy(ctx context.Context, u *ent.User, r *ent.R RepoID: r.ID, } + i.log.Debug("Validate the deployment before a request.") + v := NewDeploymentValidator([]Validator{ + &RefValidator{Env: env}, + &FrozenWindowValidator{Env: env}, + &LockValidator{Repo: r, Store: i.store}, + &SerializationValidator{Env: env, Store: i.store}, + }) + if err := v.Validate(d); err != nil { + return nil, err + } + if env.HasReview() { i.log.Debug("Save the deployment to wait reviews.") d, err = i.store.CreateDeployment(ctx, d) @@ -201,7 +202,7 @@ func (i *DeploymentInteractor) requestReviews(ctx context.Context, u *ent.User, // after review has finished. // It returns an error for a undeployable payload. func (i *DeploymentInteractor) DeployToRemote(ctx context.Context, u *ent.User, r *ent.Repo, d *ent.Deployment, env *extent.Env) (*ent.Deployment, error) { - i.log.Debug("Validate the request.") + i.log.Debug("Validate the deployment before a request.") v := NewDeploymentValidator([]Validator{ &StatusValidator{Status: deployment.StatusWaiting}, &RefValidator{Env: env}, diff --git a/internal/interactor/validator.go b/internal/interactor/validator.go index dec4f26b..521ef7b0 100644 --- a/internal/interactor/validator.go +++ b/internal/interactor/validator.go @@ -9,6 +9,7 @@ import ( "github.com/gitploy-io/gitploy/model/ent/review" "github.com/gitploy-io/gitploy/model/extent" "github.com/gitploy-io/gitploy/pkg/e" + "go.uber.org/zap" ) // DeploymentValidator validate that it is deployable. @@ -146,8 +147,12 @@ type SerializationValidator struct { } func (v *SerializationValidator) Validate(d *ent.Deployment) error { + log := zap.L().Named("serialization-validator") + defer log.Sync() + // Skip if the serialization field is disabled. if v.Env.Serialization == nil || !*v.Env.Serialization { + log.Debug("Skip the serialization validator.") return nil } @@ -157,6 +162,7 @@ func (v *SerializationValidator) Validate(d *ent.Deployment) error { } if e.HasErrorCode(err, e.ErrorCodeEntityNotFound) { + log.Debug("There is no running deployment.") return nil }