From a7cd07fe43cd7e5e0e79f7e1ddb6ca055d518d8a Mon Sep 17 00:00:00 2001 From: claire1618 <55173466+claire1618@users.noreply.github.com> Date: Fri, 29 Dec 2023 11:55:24 -0500 Subject: [PATCH 1/5] feat(api/database)!: store deployment record in database for Vela-targeted deployments (#1031) * feat: adding deployment database * updating things * fix reserved word user * testing and other * tests * fixing tests * fixing things * help * fixing various tests * new types changes * updating build deployNumber field to deploymentID * changing build DeployID field back to DeployNumber * fixing migrations issues * fixing lint * fixing lint things * fixing lint things * getting new types * fixing things * fix pls * fix pls * fix pls * fix pls * fixing things * ... * linter * linter * linter * fixing 2022 copyright lint errors * hopefully fixing tests * fixing integration tests * fixing vaders comments * fixing vaders comments pt 2 * fix * fixing eastons comments * linter * linter * making david mays changes * fixy fix * fixy fix --------- Co-authored-by: Claire.Nicholas Co-authored-by: ecrupper --- api/admin/step.go | 1 - api/build/restart.go | 19 +- api/deployment/create.go | 17 +- api/deployment/get.go | 21 ++- api/deployment/list.go | 31 +-- api/schedule/update.go | 1 + api/webhook/post.go | 45 +++++ cmd/vela-server/schedule.go | 3 +- compiler/native/environment_test.go | 4 +- database/build/build_test.go | 9 +- database/build/clean_test.go | 4 +- database/build/create_test.go | 7 +- database/build/get_repo_test.go | 5 +- database/build/interface.go | 2 - database/build/list_deployment.go | 66 ------- database/build/list_deployment_test.go | 111 ----------- database/build/list_test.go | 8 +- database/build/table.go | 2 + database/build/update_test.go | 7 +- database/database.go | 2 + database/deployment/count.go | 25 +++ database/deployment/count_repo.go | 31 +++ database/deployment/count_repo_test.go | 120 ++++++++++++ database/deployment/count_test.go | 148 +++++++++++++++ database/deployment/create.go | 33 ++++ database/deployment/create_test.go | 84 +++++++++ database/deployment/delete.go | 26 +++ database/deployment/delete_test.go | 84 +++++++++ database/deployment/deployment.go | 81 ++++++++ database/deployment/deployment_test.go | 217 +++++++++++++++++++++ database/deployment/get.go | 55 ++++++ database/deployment/get_repo.go | 61 ++++++ database/deployment/get_repo_test.go | 102 ++++++++++ database/deployment/get_test.go | 95 ++++++++++ database/deployment/index.go | 24 +++ database/deployment/index_test.go | 58 ++++++ database/deployment/interface.go | 47 +++++ database/deployment/list.go | 64 +++++++ database/deployment/list_repo.go | 75 ++++++++ database/deployment/list_repo_test.go | 121 ++++++++++++ database/deployment/list_test.go | 116 ++++++++++++ database/deployment/opts.go | 53 ++++++ database/deployment/opts_test.go | 159 ++++++++++++++++ database/deployment/table.go | 72 +++++++ database/deployment/table_test.go | 58 ++++++ database/deployment/update.go | 31 +++ database/deployment/update_test.go | 86 +++++++++ database/integration_test.go | 250 ++++++++++++++++++------- database/interface.go | 4 + database/resource.go | 12 ++ database/resource_test.go | 4 + go.mod | 2 +- go.sum | 4 +- router/middleware/build/build_test.go | 1 + router/middleware/logger.go | 1 + router/middleware/logger_test.go | 2 - scm/github/deployment.go | 14 +- scm/github/deployment_test.go | 182 ------------------ scm/github/webhook.go | 22 ++- scm/github/webhook_test.go | 56 ++++-- 60 files changed, 2539 insertions(+), 506 deletions(-) delete mode 100644 database/build/list_deployment.go delete mode 100644 database/build/list_deployment_test.go create mode 100644 database/deployment/count.go create mode 100644 database/deployment/count_repo.go create mode 100644 database/deployment/count_repo_test.go create mode 100644 database/deployment/count_test.go create mode 100644 database/deployment/create.go create mode 100644 database/deployment/create_test.go create mode 100644 database/deployment/delete.go create mode 100644 database/deployment/delete_test.go create mode 100644 database/deployment/deployment.go create mode 100644 database/deployment/deployment_test.go create mode 100644 database/deployment/get.go create mode 100644 database/deployment/get_repo.go create mode 100644 database/deployment/get_repo_test.go create mode 100644 database/deployment/get_test.go create mode 100644 database/deployment/index.go create mode 100644 database/deployment/index_test.go create mode 100644 database/deployment/interface.go create mode 100644 database/deployment/list.go create mode 100644 database/deployment/list_repo.go create mode 100644 database/deployment/list_repo_test.go create mode 100644 database/deployment/list_test.go create mode 100644 database/deployment/opts.go create mode 100644 database/deployment/opts_test.go create mode 100644 database/deployment/table.go create mode 100644 database/deployment/table_test.go create mode 100644 database/deployment/update.go create mode 100644 database/deployment/update_test.go diff --git a/api/admin/step.go b/api/admin/step.go index 88204ca45..a8b2de5d4 100644 --- a/api/admin/step.go +++ b/api/admin/step.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -//nolint:dupl // ignore similar code package admin import ( diff --git a/api/build/restart.go b/api/build/restart.go index 4b3487c66..213f263db 100644 --- a/api/build/restart.go +++ b/api/build/restart.go @@ -298,8 +298,6 @@ func RestartBuild(c *gin.Context) { } // check if the pipeline did not already exist in the database - // - //nolint:dupl // ignore duplicate code if pipeline == nil { pipeline = compiled pipeline.SetRepoID(r.GetID()) @@ -341,6 +339,23 @@ func RestartBuild(c *gin.Context) { c.JSON(http.StatusCreated, b) + // if the event is a deployment, update the build list + if !strings.EqualFold(b.GetEvent(), constants.EventDeploy) { + d, err := database.FromContext(c).GetDeploymentForRepo(c, r, b.GetDeployNumber()) + if err != nil { + logger.Errorf("unable to set get deployment for build %s: %v", entry, err) + } + + build := append(d.Builds, b) + + d.SetBuilds(build) + + _, err = database.FromContext(c).UpdateDeployment(d) + if err != nil { + logger.Errorf("unable to set update deployment for build %s: %v", entry, err) + } + } + // send API call to set the status on the commit err = scm.FromContext(c).Status(ctx, u, b, r.GetOrg(), r.GetName()) if err != nil { diff --git a/api/deployment/create.go b/api/deployment/create.go index 0c7112069..4f5d0faf1 100644 --- a/api/deployment/create.go +++ b/api/deployment/create.go @@ -5,8 +5,10 @@ package deployment import ( "fmt" "net/http" + "time" "github.com/gin-gonic/gin" + "github.com/go-vela/server/database" "github.com/go-vela/server/router/middleware/org" "github.com/go-vela/server/router/middleware/repo" "github.com/go-vela/server/router/middleware/user" @@ -82,7 +84,8 @@ func CreateDeployment(c *gin.Context) { // update fields in deployment object input.SetRepoID(r.GetID()) - input.SetUser(u.GetName()) + input.SetCreatedBy(u.GetName()) + input.SetCreatedAt(time.Now().Unix()) if len(input.GetDescription()) == 0 { input.SetDescription("Deployment request from Vela") @@ -107,5 +110,15 @@ func CreateDeployment(c *gin.Context) { return } - c.JSON(http.StatusCreated, input) + // send API call to create the deployment + d, err := database.FromContext(c).CreateDeployment(c, input) + if err != nil { + retErr := fmt.Errorf("unable to create new deployment for %s: %w", r.GetFullName(), err) + + util.HandleError(c, http.StatusInternalServerError, retErr) + + return + } + + c.JSON(http.StatusCreated, d) } diff --git a/api/deployment/get.go b/api/deployment/get.go index 1eef1c527..b940fd408 100644 --- a/api/deployment/get.go +++ b/api/deployment/get.go @@ -8,6 +8,7 @@ import ( "strconv" "github.com/gin-gonic/gin" + "github.com/go-vela/server/database" "github.com/go-vela/server/router/middleware/org" "github.com/go-vela/server/router/middleware/repo" "github.com/go-vela/server/router/middleware/user" @@ -85,12 +86,24 @@ func GetDeployment(c *gin.Context) { return } - // send API call to capture the deployment - d, err := scm.FromContext(c).GetDeployment(ctx, u, r, int64(number)) + // send API call to database to capture the deployment + d, err := database.FromContext(c).GetDeployment(int64(number)) if err != nil { - retErr := fmt.Errorf("unable to get deployment %s: %w", entry, err) + // send API call to SCM to capture the deployment + d, err = scm.FromContext(c).GetDeployment(ctx, u, r, int64(number)) + if err != nil { + retErr := fmt.Errorf("unable to get deployment %s: %w", entry, err) - util.HandleError(c, http.StatusInternalServerError, retErr) + util.HandleError(c, http.StatusInternalServerError, retErr) + + return + } + } + + if d == nil { + retErr := fmt.Errorf("unable to get deployment: %s", deployment) + + util.HandleError(c, http.StatusBadRequest, retErr) return } diff --git a/api/deployment/list.go b/api/deployment/list.go index 384255136..8b8c24798 100644 --- a/api/deployment/list.go +++ b/api/deployment/list.go @@ -13,9 +13,7 @@ import ( "github.com/go-vela/server/router/middleware/org" "github.com/go-vela/server/router/middleware/repo" "github.com/go-vela/server/router/middleware/user" - "github.com/go-vela/server/scm" "github.com/go-vela/server/util" - "github.com/go-vela/types/library" "github.com/sirupsen/logrus" ) @@ -80,7 +78,6 @@ func ListDeployments(c *gin.Context) { o := org.Retrieve(c) r := repo.Retrieve(c) u := user.Retrieve(c) - ctx := c.Request.Context() // update engine logger with API metadata // @@ -115,7 +112,7 @@ func ListDeployments(c *gin.Context) { perPage = util.MaxInt(1, util.MinInt(100, perPage)) // send API call to capture the total number of deployments for the repo - t, err := scm.FromContext(c).GetDeploymentCount(ctx, u, r) + t, err := database.FromContext(c).CountDeploymentsForRepo(c, r) if err != nil { retErr := fmt.Errorf("unable to get deployment count for %s: %w", r.GetFullName(), err) @@ -125,7 +122,7 @@ func ListDeployments(c *gin.Context) { } // send API call to capture the list of deployments for the repo - d, err := scm.FromContext(c).GetDeploymentList(ctx, u, r, page, perPage) + d, err := database.FromContext(c).ListDeploymentsForRepo(c, r, page, perPage) if err != nil { retErr := fmt.Errorf("unable to get deployments for %s: %w", r.GetFullName(), err) @@ -134,28 +131,6 @@ func ListDeployments(c *gin.Context) { return } - dWithBs := []*library.Deployment{} - - for _, deployment := range d { - b, _, err := database.FromContext(c).ListBuildsForDeployment(ctx, deployment, nil, 1, 3) - if err != nil { - retErr := fmt.Errorf("unable to get builds for deployment %d: %w", deployment.GetID(), err) - - util.HandleError(c, http.StatusInternalServerError, retErr) - - return - } - - builds := []library.Build{} - for _, build := range b { - builds = append(builds, *build) - } - - deployment.SetBuilds(builds) - - dWithBs = append(dWithBs, deployment) - } - // create pagination object pagination := api.Pagination{ Page: page, @@ -165,5 +140,5 @@ func ListDeployments(c *gin.Context) { // set pagination headers pagination.SetHeaderLink(c) - c.JSON(http.StatusOK, dWithBs) + c.JSON(http.StatusOK, d) } diff --git a/api/schedule/update.go b/api/schedule/update.go index fdf078d64..d1ff1e44f 100644 --- a/api/schedule/update.go +++ b/api/schedule/update.go @@ -125,6 +125,7 @@ func UpdateSchedule(c *gin.Context) { // set the updated by field using claims s.SetUpdatedBy(u.GetName()) + if input.GetBranch() != "" { s.SetBranch(input.GetBranch()) } diff --git a/api/webhook/post.go b/api/webhook/post.go index ef538898e..4414c8da9 100644 --- a/api/webhook/post.go +++ b/api/webhook/post.go @@ -5,6 +5,7 @@ package webhook import ( "bytes" "context" + "errors" "fmt" "io" "net/http" @@ -24,6 +25,7 @@ import ( "github.com/go-vela/types/library" "github.com/go-vela/types/pipeline" "github.com/sirupsen/logrus" + "gorm.io/gorm" ) var baseErr = "unable to process webhook" @@ -663,6 +665,49 @@ func PostWebhook(c *gin.Context) { // set the BuildID field h.SetBuildID(b.GetID()) + // if event is deployment, update the deployment record to include this build + if !strings.EqualFold(b.GetEvent(), constants.EventDeploy) { + d, err := database.FromContext(c).GetDeploymentForRepo(c, repo, webhook.Deployment.GetNumber()) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + deployment := webhook.Deployment + + deployment.SetRepoID(repo.GetID()) + deployment.SetBuilds([]*library.Build{b}) + + _, err := database.FromContext(c).CreateDeployment(c, deployment) + if err != nil { + retErr := fmt.Errorf("%s: failed to create deployment %s/%d: %w", baseErr, repo.GetFullName(), deployment.GetNumber(), err) + util.HandleError(c, http.StatusInternalServerError, retErr) + + h.SetStatus(constants.StatusFailure) + h.SetError(retErr.Error()) + + return + } + } else { + retErr := fmt.Errorf("%s: failed to get deployment %s/%d: %w", baseErr, repo.GetFullName(), webhook.Deployment.GetNumber(), err) + util.HandleError(c, http.StatusInternalServerError, retErr) + + h.SetStatus(constants.StatusFailure) + h.SetError(retErr.Error()) + + return + } + } else { + d.SetBuilds([]*library.Build{b}) + _, err := database.FromContext(c).UpdateDeployment(d) + if err != nil { + retErr := fmt.Errorf("%s: failed to update deployment %s/%d: %w", baseErr, repo.GetFullName(), d.GetNumber(), err) + util.HandleError(c, http.StatusInternalServerError, retErr) + + h.SetStatus(constants.StatusFailure) + h.SetError(retErr.Error()) + + return + } + } + } c.JSON(http.StatusOK, b) diff --git a/cmd/vela-server/schedule.go b/cmd/vela-server/schedule.go index bd7ce792b..2036306c4 100644 --- a/cmd/vela-server/schedule.go +++ b/cmd/vela-server/schedule.go @@ -103,7 +103,7 @@ func processSchedules(ctx context.Context, start time.Time, compiler compiler.En // // The previous occurrence of the schedule must be after the starting time of processing schedules. if !prevTime.After(start) { - logrus.Tracef("%s %s: previous occurence not after starting point", scheduleWait, schedule.GetName()) + logrus.Tracef("%s %s: previous occurrence not after starting point", scheduleWait, schedule.GetName()) continue } @@ -277,6 +277,7 @@ func processSchedule(ctx context.Context, s *library.Schedule, compiler compiler // parent should be "1" if it's the first build ran b.SetParent(1) } + r.SetCounter(r.GetCounter() + 1) // set the build link if a web address is provided diff --git a/compiler/native/environment_test.go b/compiler/native/environment_test.go index 784c8b9ad..a6a598acc 100644 --- a/compiler/native/environment_test.go +++ b/compiler/native/environment_test.go @@ -644,7 +644,7 @@ func TestNative_environment(t *testing.T) { m: &types.Metadata{Database: &types.Database{Driver: str, Host: str}, Queue: &types.Queue{Channel: str, Driver: str, Host: str}, Source: &types.Source{Driver: str, Host: str}, Vela: &types.Vela{Address: str, WebAddress: str}}, r: &library.Repo{ID: &num64, UserID: &num64, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL, AllowPull: &booL, AllowPush: &booL, AllowDeploy: &booL, AllowTag: &booL, AllowComment: &booL}, u: &library.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, - want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_COMMENT": "false", "REPOSITORY_ALLOW_DEPLOY": "false", "REPOSITORY_ALLOW_PULL": "false", "REPOSITORY_ALLOW_PUSH": "false", "REPOSITORY_ALLOW_TAG": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_COMMENT": "false", "VELA_REPO_ALLOW_DEPLOY": "false", "VELA_REPO_ALLOW_PULL": "false", "VELA_REPO_ALLOW_PUSH": "false", "VELA_REPO_ALLOW_TAG": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo"}, + want: map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_COMMENT": "false", "REPOSITORY_ALLOW_DEPLOY": "false", "REPOSITORY_ALLOW_PULL": "false", "REPOSITORY_ALLOW_PUSH": "false", "REPOSITORY_ALLOW_TAG": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_COMMENT": "false", "VELA_REPO_ALLOW_DEPLOY": "false", "VELA_REPO_ALLOW_PULL": "false", "VELA_REPO_ALLOW_PUSH": "false", "VELA_REPO_ALLOW_TAG": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo"}, }, } @@ -753,7 +753,7 @@ func Test_client_EnvironmentBuild(t *testing.T) { metadata: &types.Metadata{Database: &types.Database{Driver: str, Host: str}, Queue: &types.Queue{Channel: str, Driver: str, Host: str}, Source: &types.Source{Driver: str, Host: str}, Vela: &types.Vela{Address: str, WebAddress: str}}, repo: &library.Repo{ID: &num64, UserID: &num64, Org: &str, Name: &str, FullName: &str, Link: &str, Clone: &str, Branch: &str, Topics: &topics, BuildLimit: &num64, Timeout: &num64, Visibility: &str, Private: &booL, Trusted: &booL, Active: &booL, AllowPull: &booL, AllowPush: &booL, AllowDeploy: &booL, AllowTag: &booL, AllowComment: &booL}, user: &library.User{ID: &num64, Name: &str, Token: &str, Active: &booL, Admin: &booL}, - }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_COMMENT": "false", "REPOSITORY_ALLOW_DEPLOY": "false", "REPOSITORY_ALLOW_PULL": "false", "REPOSITORY_ALLOW_PUSH": "false", "REPOSITORY_ALLOW_TAG": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_COMMENT": "false", "VELA_REPO_ALLOW_DEPLOY": "false", "VELA_REPO_ALLOW_PULL": "false", "VELA_REPO_ALLOW_PUSH": "false", "VELA_REPO_ALLOW_TAG": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo"}, + }, map[string]string{"BUILD_AUTHOR": "foo", "BUILD_AUTHOR_EMAIL": "", "BUILD_BASE_REF": "foo", "BUILD_BRANCH": "foo", "BUILD_CHANNEL": "foo", "BUILD_CLONE": "foo", "BUILD_COMMIT": "foo", "BUILD_CREATED": "1", "BUILD_ENQUEUED": "1", "BUILD_EVENT": "deployment", "BUILD_HOST": "", "BUILD_LINK": "", "BUILD_MESSAGE": "foo", "BUILD_NUMBER": "1", "BUILD_PARENT": "1", "BUILD_REF": "refs/pull/1/head", "BUILD_SENDER": "foo", "BUILD_SOURCE": "foo", "BUILD_STARTED": "1", "BUILD_STATUS": "foo", "BUILD_TARGET": "production", "BUILD_TITLE": "foo", "BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "CI": "true", "REPOSITORY_ACTIVE": "false", "REPOSITORY_ALLOW_COMMENT": "false", "REPOSITORY_ALLOW_DEPLOY": "false", "REPOSITORY_ALLOW_PULL": "false", "REPOSITORY_ALLOW_PUSH": "false", "REPOSITORY_ALLOW_TAG": "false", "REPOSITORY_ALLOW_EVENTS": "", "REPOSITORY_BRANCH": "foo", "REPOSITORY_CLONE": "foo", "REPOSITORY_FULL_NAME": "foo", "REPOSITORY_LINK": "foo", "REPOSITORY_NAME": "foo", "REPOSITORY_ORG": "foo", "REPOSITORY_PRIVATE": "false", "REPOSITORY_TIMEOUT": "1", "REPOSITORY_TRUSTED": "false", "REPOSITORY_VISIBILITY": "foo", "VELA": "true", "VELA_ADDR": "foo", "VELA_BUILD_APPROVED_AT": "0", "VELA_BUILD_APPROVED_BY": "", "VELA_BUILD_AUTHOR": "foo", "VELA_BUILD_AUTHOR_EMAIL": "", "VELA_BUILD_BASE_REF": "foo", "VELA_BUILD_BRANCH": "foo", "VELA_BUILD_CHANNEL": "foo", "VELA_BUILD_CLONE": "foo", "VELA_BUILD_COMMIT": "foo", "VELA_BUILD_CREATED": "1", "VELA_BUILD_DISTRIBUTION": "", "VELA_BUILD_ENQUEUED": "1", "VELA_BUILD_EVENT": "deployment", "VELA_BUILD_EVENT_ACTION": "", "VELA_BUILD_HOST": "", "VELA_BUILD_LINK": "", "VELA_BUILD_MESSAGE": "foo", "VELA_BUILD_NUMBER": "1", "VELA_BUILD_PARENT": "1", "VELA_BUILD_REF": "refs/pull/1/head", "VELA_BUILD_RUNTIME": "", "VELA_BUILD_SENDER": "foo", "VELA_BUILD_SOURCE": "foo", "VELA_BUILD_STARTED": "1", "VELA_BUILD_STATUS": "foo", "VELA_BUILD_TARGET": "production", "VELA_BUILD_TITLE": "foo", "VELA_BUILD_WORKSPACE": "/vela/src/foo/foo/foo", "VELA_CHANNEL": "foo", "VELA_DATABASE": "foo", "VELA_DEPLOYMENT": "production", "VELA_DEPLOYMENT_NUMBER": "0", "VELA_DISTRIBUTION": "TODO", "VELA_HOST": "foo", "VELA_NETRC_MACHINE": "foo", "VELA_NETRC_PASSWORD": "foo", "VELA_NETRC_USERNAME": "x-oauth-basic", "VELA_QUEUE": "foo", "VELA_REPO_ACTIVE": "false", "VELA_REPO_ALLOW_COMMENT": "false", "VELA_REPO_ALLOW_DEPLOY": "false", "VELA_REPO_ALLOW_PULL": "false", "VELA_REPO_ALLOW_PUSH": "false", "VELA_REPO_ALLOW_TAG": "false", "VELA_REPO_ALLOW_EVENTS": "", "VELA_REPO_APPROVE_BUILD": "", "VELA_REPO_BRANCH": "foo", "VELA_REPO_BUILD_LIMIT": "1", "VELA_REPO_CLONE": "foo", "VELA_REPO_FULL_NAME": "foo", "VELA_REPO_LINK": "foo", "VELA_REPO_NAME": "foo", "VELA_REPO_ORG": "foo", "VELA_REPO_PIPELINE_TYPE": "", "VELA_REPO_PRIVATE": "false", "VELA_REPO_TIMEOUT": "1", "VELA_REPO_TOPICS": "cloud,security", "VELA_REPO_TRUSTED": "false", "VELA_REPO_VISIBILITY": "foo", "VELA_RUNTIME": "TODO", "VELA_SOURCE": "foo", "VELA_USER_ACTIVE": "false", "VELA_USER_ADMIN": "false", "VELA_USER_FAVORITES": "[]", "VELA_USER_NAME": "foo", "VELA_VERSION": "TODO", "VELA_WORKSPACE": "/vela/src/foo/foo/foo"}, }, } for _, tt := range tests { diff --git a/database/build/build_test.go b/database/build/build_test.go index c04e0c9a4..2478b2ded 100644 --- a/database/build/build_test.go +++ b/database/build/build_test.go @@ -11,6 +11,7 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/go-vela/types/library" + "github.com/go-vela/types/raw" "github.com/sirupsen/logrus" "gorm.io/driver/postgres" @@ -195,6 +196,7 @@ func testBuild() *library.Build { Started: new(int64), Finished: new(int64), Deploy: new(string), + DeployNumber: new(int64), Clone: new(string), Source: new(string), Title: new(string), @@ -219,16 +221,21 @@ func testBuild() *library.Build { // testDeployment is a test helper function to create a library // Repo type with all fields set to their zero values. func testDeployment() *library.Deployment { + builds := []*library.Build{} return &library.Deployment{ ID: new(int64), RepoID: new(int64), + Number: new(int64), URL: new(string), - User: new(string), Commit: new(string), Ref: new(string), Task: new(string), Target: new(string), Description: new(string), + Payload: new(raw.StringSliceMap), + CreatedAt: new(int64), + CreatedBy: new(string), + Builds: builds, } } diff --git a/database/build/clean_test.go b/database/build/clean_test.go index 28eb29195..ca5f4b688 100644 --- a/database/build/clean_test.go +++ b/database/build/clean_test.go @@ -45,8 +45,8 @@ func TestBuild_Engine_CleanBuilds(t *testing.T) { defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() // ensure the mock expects the name query - _mock.ExpectExec(`UPDATE "builds" SET "status"=$1,"error"=$2,"finished"=$3,"deploy_payload"=$4 WHERE created < $5 AND (status = 'running' OR status = 'pending')`). - WithArgs("error", "msg", NowTimestamp{}, AnyArgument{}, 3). + _mock.ExpectExec(`UPDATE "builds" SET "status"=$1,"error"=$2,"finished"=$3,"deploy_number"=$4,"deploy_payload"=$5 WHERE created < $6 AND (status = 'running' OR status = 'pending')`). + WithArgs("error", "msg", NowTimestamp{}, 0, AnyArgument{}, 3). WillReturnResult(sqlmock.NewResult(1, 2)) _sqlite := testSqlite(t) diff --git a/database/build/create_test.go b/database/build/create_test.go index 6a58c2618..c6ddc2a0d 100644 --- a/database/build/create_test.go +++ b/database/build/create_test.go @@ -16,6 +16,7 @@ func TestBuild_Engine_CreateBuild(t *testing.T) { _build.SetID(1) _build.SetRepoID(1) _build.SetNumber(1) + _build.SetDeployNumber(0) _build.SetDeployPayload(nil) _postgres, _mock := testPostgres(t) @@ -26,9 +27,9 @@ func TestBuild_Engine_CreateBuild(t *testing.T) { // ensure the mock expects the query _mock.ExpectQuery(`INSERT INTO "builds" -("repo_id","pipeline_id","number","parent","event","event_action","status","error","enqueued","created","started","finished","deploy","deploy_payload","clone","source","title","message","commit","sender","author","email","link","branch","ref","base_ref","head_ref","host","runtime","distribution","approved_at","approved_by","id") -VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33) RETURNING "id"`). - WithArgs(1, nil, 1, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, AnyArgument{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1). +("repo_id","pipeline_id","number","parent","event","event_action","status","error","enqueued","created","started","finished","deploy","deploy_number","deploy_payload","clone","source","title","message","commit","sender","author","email","link","branch","ref","base_ref","head_ref","host","runtime","distribution","approved_at","approved_by","id") +VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33,$34) RETURNING "id"`). + WithArgs(1, nil, 1, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 0, AnyArgument{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1). WillReturnRows(_rows) _sqlite := testSqlite(t) diff --git a/database/build/get_repo_test.go b/database/build/get_repo_test.go index 963aecb9d..5e4a46129 100644 --- a/database/build/get_repo_test.go +++ b/database/build/get_repo_test.go @@ -17,6 +17,7 @@ func TestBuild_Engine_GetBuildForRepo(t *testing.T) { _build.SetID(1) _build.SetRepoID(1) _build.SetNumber(1) + _build.SetDeployNumber(0) _build.SetDeployPayload(nil) _repo := testRepo() @@ -33,8 +34,8 @@ func TestBuild_Engine_GetBuildForRepo(t *testing.T) { // create expected result in mock _rows := sqlmock.NewRows( - []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "approved_at", "approved_by", "timestamp"}). - AddRow(1, 1, nil, 1, 0, "", "", "", "", 0, 0, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0, "", 0) + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_number", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "timestamp"}). + AddRow(1, 1, nil, 1, 0, "", "", "", "", 0, 0, 0, 0, "", 0, nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "builds" WHERE repo_id = $1 AND number = $2 LIMIT 1`).WithArgs(1, 1).WillReturnRows(_rows) diff --git a/database/build/interface.go b/database/build/interface.go index c2b468e39..e28d230b4 100644 --- a/database/build/interface.go +++ b/database/build/interface.go @@ -50,8 +50,6 @@ type BuildInterface interface { LastBuildForRepo(context.Context, *library.Repo, string) (*library.Build, error) // ListBuilds defines a function that gets a list of all builds. ListBuilds(context.Context) ([]*library.Build, error) - // ListBuildsForDeployment defines a function that gets a list of builds by deployment url. - ListBuildsForDeployment(context.Context, *library.Deployment, map[string]interface{}, int, int) ([]*library.Build, int64, error) // ListBuildsForOrg defines a function that gets a list of builds by org name. ListBuildsForOrg(context.Context, string, map[string]interface{}, int, int) ([]*library.Build, int64, error) // ListBuildsForRepo defines a function that gets a list of builds by repo ID. diff --git a/database/build/list_deployment.go b/database/build/list_deployment.go deleted file mode 100644 index 4c7b3468d..000000000 --- a/database/build/list_deployment.go +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package build - -import ( - "context" - - "github.com/go-vela/types/constants" - "github.com/go-vela/types/database" - "github.com/go-vela/types/library" - "github.com/sirupsen/logrus" -) - -// ListBuildsForDeployment gets a list of builds by deployment url from the database. -// -//nolint:lll // ignore long line length due to variable names -func (e *engine) ListBuildsForDeployment(ctx context.Context, d *library.Deployment, filters map[string]interface{}, page, perPage int) ([]*library.Build, int64, error) { - e.logger.WithFields(logrus.Fields{ - "deployment": d.GetURL(), - }).Tracef("listing builds for deployment %s from the database", d.GetURL()) - - // variables to store query results and return values - count := int64(0) - b := new([]database.Build) - builds := []*library.Build{} - - // count the results - count, err := e.CountBuildsForDeployment(ctx, d, filters) - if err != nil { - return builds, 0, err - } - - // short-circuit if there are no results - if count == 0 { - return builds, 0, nil - } - - // calculate offset for pagination through results - offset := perPage * (page - 1) - - err = e.client. - Table(constants.TableBuild). - Where("source = ?", d.GetURL()). - Where(filters). - Order("number DESC"). - Limit(perPage). - Offset(offset). - Find(&b). - Error - if err != nil { - return nil, count, err - } - - // iterate through all query results - for _, build := range *b { - // https://golang.org/doc/faq#closures_and_goroutines - tmp := build - - // convert query result to library type - // - // https://pkg.go.dev/github.com/go-vela/types/database#Build.ToLibrary - builds = append(builds, tmp.ToLibrary()) - } - - return builds, count, nil -} diff --git a/database/build/list_deployment_test.go b/database/build/list_deployment_test.go deleted file mode 100644 index 582e49a04..000000000 --- a/database/build/list_deployment_test.go +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package build - -import ( - "context" - "reflect" - "testing" - - "github.com/DATA-DOG/go-sqlmock" - "github.com/go-vela/types/library" -) - -func TestBuild_Engine_ListBuildsForDeployment(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - _buildOne.SetSource("https://github.com/github/octocat/deployments/1") - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - _buildTwo.SetSource("https://github.com/github/octocat/deployments/1") - - _deployment := testDeployment() - _deployment.SetID(1) - _deployment.SetRepoID(1) - _deployment.SetURL("https://github.com/github/octocat/deployments/1") - - _postgres, _mock := testPostgres(t) - defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() - - // create expected count query result in mock - _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) - - // ensure the mock expects the count query - _mock.ExpectQuery(`SELECT count(*) FROM "builds" WHERE source = $1`).WithArgs("https://github.com/github/octocat/deployments/1").WillReturnRows(_rows) - - // create expected query result in mock - _rows = sqlmock.NewRows( - []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "approved_at", "approved_by", "timestamp"}). - AddRow(2, 1, nil, 2, 0, "", "", "", "", 0, 0, 0, 0, "", nil, "", "https://github.com/github/octocat/deployments/1", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0, "", 0). - AddRow(1, 1, nil, 1, 0, "", "", "", "", 0, 0, 0, 0, "", nil, "", "https://github.com/github/octocat/deployments/1", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0, "", 0) - - // ensure the mock expects the query - _mock.ExpectQuery(`SELECT * FROM "builds" WHERE source = $1 ORDER BY number DESC LIMIT 10`).WithArgs("https://github.com/github/octocat/deployments/1").WillReturnRows(_rows) - - _sqlite := testSqlite(t) - defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() - - _, err := _sqlite.CreateBuild(context.TODO(), _buildOne) - if err != nil { - t.Errorf("unable to create test build for sqlite: %v", err) - } - - _, err = _sqlite.CreateBuild(context.TODO(), _buildTwo) - if err != nil { - t.Errorf("unable to create test build for sqlite: %v", err) - } - - // setup tests - tests := []struct { - failure bool - name string - database *engine - want []*library.Build - }{ - { - failure: false, - name: "postgres", - database: _postgres, - want: []*library.Build{_buildTwo, _buildOne}, - }, - { - failure: false, - name: "sqlite3", - database: _sqlite, - want: []*library.Build{_buildTwo, _buildOne}, - }, - } - - filters := map[string]interface{}{} - - // run tests - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got, _, err := test.database.ListBuildsForDeployment(context.TODO(), _deployment, filters, 1, 10) - - if test.failure { - if err == nil { - t.Errorf("ListBuildsForDeployment for %s should have returned err", test.name) - } - - return - } - - if err != nil { - t.Errorf("ListBuildsForDeployment for %s returned err: %v", test.name, err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("ListBuildsForDeployment for %s is %v, want %v", test.name, got, test.want) - } - }) - } -} diff --git a/database/build/list_test.go b/database/build/list_test.go index 1e5539789..2579ac937 100644 --- a/database/build/list_test.go +++ b/database/build/list_test.go @@ -17,12 +17,14 @@ func TestBuild_Engine_ListBuilds(t *testing.T) { _buildOne.SetID(1) _buildOne.SetRepoID(1) _buildOne.SetNumber(1) + _buildOne.SetDeployNumber(0) _buildOne.SetDeployPayload(nil) _buildTwo := testBuild() _buildTwo.SetID(2) _buildTwo.SetRepoID(1) _buildTwo.SetNumber(2) + _buildTwo.SetDeployNumber(0) _buildTwo.SetDeployPayload(nil) _postgres, _mock := testPostgres(t) @@ -36,9 +38,9 @@ func TestBuild_Engine_ListBuilds(t *testing.T) { // create expected result in mock _rows = sqlmock.NewRows( - []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "approved_at", "approved_by", "timestamp"}). - AddRow(1, 1, nil, 1, 0, "", "", "", "", 0, 0, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0, "", 0). - AddRow(2, 1, nil, 2, 0, "", "", "", "", 0, 0, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0, "", 0) + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_number", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "timestamp"}). + AddRow(1, 1, nil, 1, 0, "", "", "", "", 0, 0, 0, 0, "", 0, nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0). + AddRow(2, 1, nil, 2, 0, "", "", "", "", 0, 0, 0, 0, "", 0, nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "builds"`).WillReturnRows(_rows) diff --git a/database/build/table.go b/database/build/table.go index 8d05c91f1..fde722a17 100644 --- a/database/build/table.go +++ b/database/build/table.go @@ -28,6 +28,7 @@ builds ( started INTEGER, finished INTEGER, deploy VARCHAR(500), + deploy_number INTEGER, deploy_payload VARCHAR(2000), clone VARCHAR(1000), source VARCHAR(1000), @@ -71,6 +72,7 @@ builds ( started INTEGER, finished INTEGER, deploy TEXT, + deploy_number INTEGER, deploy_payload TEXT, clone TEXT, source TEXT, diff --git a/database/build/update_test.go b/database/build/update_test.go index 178e9f283..60966af77 100644 --- a/database/build/update_test.go +++ b/database/build/update_test.go @@ -16,6 +16,7 @@ func TestBuild_Engine_UpdateBuild(t *testing.T) { _build.SetID(1) _build.SetRepoID(1) _build.SetNumber(1) + _build.SetDeployNumber(0) _build.SetDeployPayload(nil) _postgres, _mock := testPostgres(t) @@ -23,9 +24,9 @@ func TestBuild_Engine_UpdateBuild(t *testing.T) { // ensure the mock expects the query _mock.ExpectExec(`UPDATE "builds" -SET "repo_id"=$1,"pipeline_id"=$2,"number"=$3,"parent"=$4,"event"=$5,"event_action"=$6,"status"=$7,"error"=$8,"enqueued"=$9,"created"=$10,"started"=$11,"finished"=$12,"deploy"=$13,"deploy_payload"=$14,"clone"=$15,"source"=$16,"title"=$17,"message"=$18,"commit"=$19,"sender"=$20,"author"=$21,"email"=$22,"link"=$23,"branch"=$24,"ref"=$25,"base_ref"=$26,"head_ref"=$27,"host"=$28,"runtime"=$29,"distribution"=$30,"approved_at"=$31,"approved_by"=$32 -WHERE "id" = $33`). - WithArgs(1, nil, 1, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, AnyArgument{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1). +SET "repo_id"=$1,"pipeline_id"=$2,"number"=$3,"parent"=$4,"event"=$5,"event_action"=$6,"status"=$7,"error"=$8,"enqueued"=$9,"created"=$10,"started"=$11,"finished"=$12,"deploy"=$13,"deploy_number"=$14,"deploy_payload"=$15,"clone"=$16,"source"=$17,"title"=$18,"message"=$19,"commit"=$20,"sender"=$21,"author"=$22,"email"=$23,"link"=$24,"branch"=$25,"ref"=$26,"base_ref"=$27,"head_ref"=$28,"host"=$29,"runtime"=$30,"distribution"=$31,"approved_at"=$32,"approved_by"=$33 +WHERE "id" = $34`). + WithArgs(1, nil, 1, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 0, AnyArgument{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1). WillReturnResult(sqlmock.NewResult(1, 1)) _sqlite := testSqlite(t) diff --git a/database/database.go b/database/database.go index d4d31e228..3fc1adb94 100644 --- a/database/database.go +++ b/database/database.go @@ -8,6 +8,7 @@ import ( "time" "github.com/go-vela/server/database/build" + "github.com/go-vela/server/database/deployment" "github.com/go-vela/server/database/executable" "github.com/go-vela/server/database/hook" "github.com/go-vela/server/database/log" @@ -61,6 +62,7 @@ type ( build.BuildInterface executable.BuildExecutableInterface + deployment.DeploymentInterface hook.HookInterface log.LogInterface pipeline.PipelineInterface diff --git a/database/deployment/count.go b/database/deployment/count.go new file mode 100644 index 000000000..c83f739c5 --- /dev/null +++ b/database/deployment/count.go @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + + "github.com/go-vela/types/constants" +) + +// CountDeployments gets the count of all deployments from the database. +func (e *engine) CountDeployments(ctx context.Context) (int64, error) { + e.logger.Tracef("getting count of all deployments from the database") + + // variable to store query results + var d int64 + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableDeployment). + Count(&d). + Error + + return d, err +} diff --git a/database/deployment/count_repo.go b/database/deployment/count_repo.go new file mode 100644 index 000000000..93a7c1ac8 --- /dev/null +++ b/database/deployment/count_repo.go @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + + "github.com/go-vela/types/constants" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// CountDeploymentsForRepo gets the count of deployments by repo ID from the database. +func (e *engine) CountDeploymentsForRepo(ctx context.Context, r *library.Repo) (int64, error) { + e.logger.WithFields(logrus.Fields{ + "org": r.GetOrg(), + "repo": r.GetName(), + }).Tracef("getting count of deployments for repo %s from the database", r.GetFullName()) + + // variable to store query results + var d int64 + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableDeployment). + Where("repo_id = ?", r.GetID()). + Count(&d). + Error + + return d, err +} diff --git a/database/deployment/count_repo_test.go b/database/deployment/count_repo_test.go new file mode 100644 index 000000000..6d3afece4 --- /dev/null +++ b/database/deployment/count_repo_test.go @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" +) + +func TestDeployment_Engine_CountDeploymentsForRepo(t *testing.T) { + builds := []*library.Build{} + + // setup types + _deploymentOne := testDeployment() + _deploymentOne.SetID(1) + _deploymentOne.SetRepoID(1) + _deploymentOne.SetNumber(1) + _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") + _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") + _deploymentOne.SetRef("refs/heads/master") + _deploymentOne.SetTask("vela-deploy") + _deploymentOne.SetTarget("production") + _deploymentOne.SetDescription("Deployment request from Vela") + _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) + _deploymentOne.SetCreatedAt(1) + _deploymentOne.SetCreatedBy("octocat") + _deploymentOne.SetBuilds(builds) + + _deploymentTwo := testDeployment() + _deploymentTwo.SetID(2) + _deploymentTwo.SetRepoID(2) + _deploymentTwo.SetNumber(2) + _deploymentTwo.SetURL("https://github.com/github/octocat/deployments/2") + _deploymentTwo.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135164") + _deploymentTwo.SetRef("refs/heads/master") + _deploymentTwo.SetTask("vela-deploy") + _deploymentTwo.SetTarget("production") + _deploymentTwo.SetDescription("Deployment request from Vela") + _deploymentTwo.SetPayload(map[string]string{"foo": "test1"}) + _deploymentTwo.SetCreatedAt(1) + _deploymentTwo.SetCreatedBy("octocat") + _deploymentTwo.SetBuilds(builds) + + _repo := testRepo() + _repo.SetID(1) + _repo.SetUserID(1) + _repo.SetOrg("foo") + _repo.SetName("bar") + _repo.SetFullName("foo/bar") + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows([]string{"count"}).AddRow(1) + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT count(*) FROM "deployments" WHERE repo_id = $1`).WithArgs(1).WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + _, err := _sqlite.CreateDeployment(context.TODO(), _deploymentOne) + if err != nil { + t.Errorf("unable to create test repo for sqlite: %v", err) + } + + _, err = _sqlite.CreateDeployment(context.TODO(), _deploymentTwo) + if err != nil { + t.Errorf("unable to create test deployment for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want int64 + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: 1, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: 1, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.CountDeploymentsForRepo(context.TODO(), _repo) + + if test.failure { + if err == nil { + t.Errorf("CountDeploymentsForRepo for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("CountDeploymentsForRepo for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("CountDeploymentsForRepo for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/deployment/count_test.go b/database/deployment/count_test.go new file mode 100644 index 000000000..5576be750 --- /dev/null +++ b/database/deployment/count_test.go @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" + "github.com/go-vela/types/raw" +) + +func TestDeployment_Engine_CountDeployments(t *testing.T) { + buildOne := new(library.Build) + buildOne.SetID(1) + buildOne.SetRepoID(1) + buildOne.SetPipelineID(1) + buildOne.SetNumber(1) + buildOne.SetParent(1) + buildOne.SetEvent("push") + buildOne.SetEventAction("") + buildOne.SetStatus("running") + buildOne.SetError("") + buildOne.SetEnqueued(1563474077) + buildOne.SetCreated(1563474076) + buildOne.SetStarted(1563474078) + buildOne.SetFinished(1563474079) + buildOne.SetDeploy("") + buildOne.SetDeployPayload(raw.StringSliceMap{"foo": "test1"}) + buildOne.SetClone("https://github.com/github/octocat.git") + buildOne.SetSource("https://github.com/github/octocat/deployments/1") + buildOne.SetTitle("push received from https://github.com/github/octocat") + buildOne.SetMessage("First commit...") + buildOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") + buildOne.SetSender("OctoKitty") + buildOne.SetAuthor("OctoKitty") + buildOne.SetEmail("OctoKitty@github.com") + buildOne.SetLink("https://example.company.com/github/octocat/1") + buildOne.SetBranch("main") + buildOne.SetRef("refs/heads/main") + buildOne.SetBaseRef("") + buildOne.SetHeadRef("changes") + buildOne.SetHost("example.company.com") + buildOne.SetRuntime("docker") + buildOne.SetDistribution("linux") + + builds := []*library.Build{} + builds = append(builds, buildOne) + + // setup types + _deploymentOne := testDeployment() + _deploymentOne.SetID(1) + _deploymentOne.SetRepoID(1) + _deploymentOne.SetNumber(1) + _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") + _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") + _deploymentOne.SetRef("refs/heads/master") + _deploymentOne.SetTask("vela-deploy") + _deploymentOne.SetTarget("production") + _deploymentOne.SetDescription("Deployment request from Vela") + _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) + _deploymentOne.SetCreatedAt(1) + _deploymentOne.SetCreatedBy("octocat") + _deploymentOne.SetBuilds(builds) + + _deploymentTwo := testDeployment() + _deploymentTwo.SetID(2) + _deploymentTwo.SetRepoID(2) + _deploymentTwo.SetNumber(2) + _deploymentTwo.SetURL("https://github.com/github/octocat/deployments/2") + _deploymentTwo.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135164") + _deploymentTwo.SetRef("refs/heads/master") + _deploymentTwo.SetTask("vela-deploy") + _deploymentTwo.SetTarget("production") + _deploymentTwo.SetDescription("Deployment request from Vela") + _deploymentTwo.SetPayload(map[string]string{"foo": "test1"}) + _deploymentTwo.SetCreatedAt(1) + _deploymentTwo.SetCreatedBy("octocat") + _deploymentTwo.SetBuilds(builds) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT count(*) FROM "deployments"`).WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + _, err := _sqlite.CreateDeployment(context.TODO(), _deploymentOne) + if err != nil { + t.Errorf("unable to create test deployment for sqlite: %v", err) + } + + _, err = _sqlite.CreateDeployment(context.TODO(), _deploymentTwo) + if err != nil { + t.Errorf("unable to create test deployment for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want int64 + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: 2, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: 2, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.CountDeployments(context.TODO()) + + if test.failure { + if err == nil { + t.Errorf("CountDeployments for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("CountDeployments for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("CountHooks for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/deployment/create.go b/database/deployment/create.go new file mode 100644 index 000000000..467ca7cbd --- /dev/null +++ b/database/deployment/create.go @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// CreateDeployment creates a new deployment in the database. +func (e *engine) CreateDeployment(ctx context.Context, d *library.Deployment) (*library.Deployment, error) { + e.logger.WithFields(logrus.Fields{ + "deployment": d.GetID(), + }).Tracef("creating deployment %d in the database", d.GetID()) + + // cast the library type to database type + deployment := database.DeploymentFromLibrary(d) + + // validate the necessary fields are populated + err := deployment.Validate() + if err != nil { + return nil, err + } + + result := e.client.Table(constants.TableDeployment).Create(deployment) + + // send query to the database + return deployment.ToLibrary(d.Builds), result.Error +} diff --git a/database/deployment/create_test.go b/database/deployment/create_test.go new file mode 100644 index 000000000..5f584f3b7 --- /dev/null +++ b/database/deployment/create_test.go @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" +) + +func TestDeployment_Engine_CreateDeployment(t *testing.T) { + builds := []*library.Build{} + + // setup types + _deploymentOne := testDeployment() + _deploymentOne.SetID(1) + _deploymentOne.SetRepoID(1) + _deploymentOne.SetNumber(1) + _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") + _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") + _deploymentOne.SetRef("refs/heads/master") + _deploymentOne.SetTask("vela-deploy") + _deploymentOne.SetTarget("production") + _deploymentOne.SetDescription("Deployment request from Vela") + _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) + _deploymentOne.SetCreatedAt(1) + _deploymentOne.SetCreatedBy("octocat") + _deploymentOne.SetBuilds(builds) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows([]string{"id"}).AddRow(1) + + // ensure the mock expects the query + _mock.ExpectQuery(`INSERT INTO "deployments" +("number","repo_id","url","commit","ref","task","target","description","payload","created_at","created_by","builds","id") +VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13) RETURNING "id"`). + WithArgs(1, 1, "https://github.com/github/octocat/deployments/1", "48afb5bdc41ad69bf22588491333f7cf71135163", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{}", 1). + WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + // setup tests + tests := []struct { + failure bool + name string + database *engine + }{ + { + failure: false, + name: "postgres", + database: _postgres, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, err := test.database.CreateDeployment(context.TODO(), _deploymentOne) + + if test.failure { + if err == nil { + t.Errorf("CreateDeployment for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("Create for %s returned err: %v", test.name, err) + } + }) + } +} diff --git a/database/deployment/delete.go b/database/deployment/delete.go new file mode 100644 index 000000000..489f91e81 --- /dev/null +++ b/database/deployment/delete.go @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// DeleteDeployment deletes an existing deployment from the database. +func (e *engine) DeleteDeployment(d *library.Deployment) error { + e.logger.WithFields(logrus.Fields{ + "deployment": d.GetID(), + }).Tracef("deleting deployment %d in the database", d.GetID()) + + // cast the library type to database type + deployment := database.DeploymentFromLibrary(d) + + // send query to the database + return e.client. + Table(constants.TableDeployment). + Delete(deployment). + Error +} diff --git a/database/deployment/delete_test.go b/database/deployment/delete_test.go new file mode 100644 index 000000000..65eb04673 --- /dev/null +++ b/database/deployment/delete_test.go @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" +) + +func TestDeployment_Engine_DeleteDeployment(t *testing.T) { + builds := []*library.Build{} + + // setup types + _deploymentOne := testDeployment() + _deploymentOne.SetID(1) + _deploymentOne.SetRepoID(1) + _deploymentOne.SetNumber(1) + _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") + _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") + _deploymentOne.SetRef("refs/heads/master") + _deploymentOne.SetTask("vela-deploy") + _deploymentOne.SetTarget("production") + _deploymentOne.SetDescription("Deployment request from Vela") + _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) + _deploymentOne.SetCreatedAt(1) + _deploymentOne.SetCreatedBy("octocat") + _deploymentOne.SetBuilds(builds) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // ensure the mock expects the query + _mock.ExpectExec(`DELETE FROM "deployments" WHERE "deployments"."id" = $1`). + WithArgs(1). + WillReturnResult(sqlmock.NewResult(1, 1)) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + _, err := _sqlite.CreateDeployment(context.TODO(), _deploymentOne) + if err != nil { + t.Errorf("unable to create test deployment for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + }{ + { + failure: false, + name: "postgres", + database: _postgres, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err = test.database.DeleteDeployment(_deploymentOne) + + if test.failure { + if err == nil { + t.Errorf("DeleteDeployment for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("DeleteDeployment for %s returned err: %v", test.name, err) + } + }) + } +} diff --git a/database/deployment/deployment.go b/database/deployment/deployment.go new file mode 100644 index 000000000..20df5e838 --- /dev/null +++ b/database/deployment/deployment.go @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "fmt" + + "github.com/go-vela/types/constants" + "github.com/sirupsen/logrus" + + "gorm.io/gorm" +) + +type ( + // config represents the settings required to create the engine that implements the DeploymentInterface interface. + config struct { + // specifies to skip creating tables and indexes for the Deployment engine + SkipCreation bool + } + + // engine represents the deployment functionality that implements the DeploymentInterface interface. + engine struct { + // engine configuration settings used in deployment functions + config *config + + ctx context.Context + + // gorm.io/gorm database client used in deployment functions + // + // https://pkg.go.dev/gorm.io/gorm#DB + client *gorm.DB + + // sirupsen/logrus logger used in deployment functions + // + // https://pkg.go.dev/github.com/sirupsen/logrus#Entry + logger *logrus.Entry + } +) + +// New creates and returns a Vela service for integrating with deployments in the database. +// +//nolint:revive // ignore returning unexported engine +func New(opts ...EngineOpt) (*engine, error) { + // create new Deployment engine + e := new(engine) + + // create new fields + e.client = new(gorm.DB) + e.config = new(config) + e.logger = new(logrus.Entry) + + // apply all provided configuration options + for _, opt := range opts { + err := opt(e) + if err != nil { + return nil, err + } + } + + // check if we should skip creating deployment database objects + if e.config.SkipCreation { + e.logger.Warning("skipping creation of deployment table and indexes in the database") + + return e, nil + } + + // create the deployments table + err := e.CreateDeploymentTable(e.ctx, e.client.Config.Dialector.Name()) + if err != nil { + return nil, fmt.Errorf("unable to create %s table: %w", constants.TableDeployment, err) + } + + // create the indexes for the deployments table + err = e.CreateDeploymentIndexes(e.ctx) + if err != nil { + return nil, fmt.Errorf("unable to create indexes for %s table: %w", constants.TableDeployment, err) + } + + return e, nil +} diff --git a/database/deployment/deployment_test.go b/database/deployment/deployment_test.go new file mode 100644 index 000000000..4421f4f9d --- /dev/null +++ b/database/deployment/deployment_test.go @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" + "github.com/go-vela/types/raw" + "github.com/sirupsen/logrus" + + "gorm.io/driver/postgres" + "gorm.io/driver/sqlite" + "gorm.io/gorm" +) + +func TestDeployment_New(t *testing.T) { + // setup types + logger := logrus.NewEntry(logrus.StandardLogger()) + + _sql, _mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) + if err != nil { + t.Errorf("unable to create new SQL mock: %v", err) + } + defer _sql.Close() + + _mock.ExpectExec(CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + + _config := &gorm.Config{SkipDefaultTransaction: true} + + _postgres, err := gorm.Open(postgres.New(postgres.Config{Conn: _sql}), _config) + if err != nil { + t.Errorf("unable to create new postgres database: %v", err) + } + + _sqlite, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), _config) + if err != nil { + t.Errorf("unable to create new sqlite database: %v", err) + } + + defer func() { _sql, _ := _sqlite.DB(); _sql.Close() }() + + // setup tests + tests := []struct { + failure bool + name string + client *gorm.DB + key string + logger *logrus.Entry + skipCreation bool + want *engine + }{ + { + failure: false, + name: "postgres", + client: _postgres, + logger: logger, + skipCreation: false, + want: &engine{ + client: _postgres, + config: &config{SkipCreation: false}, + logger: logger, + }, + }, + { + failure: false, + name: "sqlite3", + client: _sqlite, + logger: logger, + skipCreation: false, + want: &engine{ + client: _sqlite, + config: &config{SkipCreation: false}, + logger: logger, + }, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := New( + WithClient(test.client), + WithLogger(test.logger), + WithSkipCreation(test.skipCreation), + ) + + if test.failure { + if err == nil { + t.Errorf("New for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("New for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("New for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} + +// testPostgres is a helper function to create a Postgres engine for testing. +func testPostgres(t *testing.T) (*engine, sqlmock.Sqlmock) { + // create the new mock sql database + // + // https://pkg.go.dev/github.com/DATA-DOG/go-sqlmock#New + _sql, _mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) + if err != nil { + t.Errorf("unable to create new SQL mock: %v", err) + } + + _mock.ExpectExec(CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + + // create the new mock Postgres database client + // + // https://pkg.go.dev/gorm.io/gorm#Open + _postgres, err := gorm.Open( + postgres.New(postgres.Config{Conn: _sql}), + &gorm.Config{SkipDefaultTransaction: true}, + ) + if err != nil { + t.Errorf("unable to create new postgres database: %v", err) + } + + _engine, err := New( + WithClient(_postgres), + WithLogger(logrus.NewEntry(logrus.StandardLogger())), + WithSkipCreation(false), + ) + if err != nil { + t.Errorf("unable to create new postgres deployment engine: %v", err) + } + + return _engine, _mock +} + +// testSqlite is a helper function to create a Sqlite engine for testing. +func testSqlite(t *testing.T) *engine { + _sqlite, err := gorm.Open( + sqlite.Open("file::memory:?cache=shared"), + &gorm.Config{SkipDefaultTransaction: true}, + ) + if err != nil { + t.Errorf("unable to create new sqlite database: %v", err) + } + + _engine, err := New( + WithClient(_sqlite), + WithLogger(logrus.NewEntry(logrus.StandardLogger())), + WithSkipCreation(false), + ) + if err != nil { + t.Errorf("unable to create new sqlite deployment engine: %v", err) + } + + return _engine +} + +// testDeployment is a test helper function to create a library +// Deployment type with all fields set to their zero values. +func testDeployment() *library.Deployment { + builds := []*library.Build{} + return &library.Deployment{ + ID: new(int64), + RepoID: new(int64), + Number: new(int64), + URL: new(string), + Commit: new(string), + Ref: new(string), + Task: new(string), + Target: new(string), + Description: new(string), + Payload: new(raw.StringSliceMap), + CreatedAt: new(int64), + CreatedBy: new(string), + Builds: builds, + } +} + +// testRepo is a test helper function to create a library +// Repo type with all fields set to their zero values. +func testRepo() *library.Repo { + return &library.Repo{ + ID: new(int64), + UserID: new(int64), + BuildLimit: new(int64), + Timeout: new(int64), + Counter: new(int), + PipelineType: new(string), + Hash: new(string), + Org: new(string), + Name: new(string), + FullName: new(string), + Link: new(string), + Clone: new(string), + Branch: new(string), + Visibility: new(string), + PreviousName: new(string), + Private: new(bool), + Trusted: new(bool), + Active: new(bool), + AllowPull: new(bool), + AllowPush: new(bool), + AllowDeploy: new(bool), + AllowTag: new(bool), + AllowComment: new(bool), + } +} diff --git a/database/deployment/get.go b/database/deployment/get.go new file mode 100644 index 000000000..bdc7cd68b --- /dev/null +++ b/database/deployment/get.go @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "strconv" + + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" +) + +// GetDeployment gets a deployment by ID from the database. +func (e *engine) GetDeployment(id int64) (*library.Deployment, error) { + e.logger.Tracef("getting deployment %d from the database", id) + + // variable to store query results + d := new(database.Deployment) + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableDeployment). + Where("id = ?", id). + Take(d). + Error + if err != nil { + return nil, err + } + + builds := []*library.Build{} + + for _, a := range d.Builds { + bID, err := strconv.ParseInt(a, 10, 64) + if err != nil { + return nil, err + } + // variable to store query results + b := new(database.Build) + + // send query to the database and store result in variable + err = e.client. + Table(constants.TableBuild). + Where("id = ?", bID). + Take(b). + Error + if err != nil { + return nil, err + } + + builds = append(builds, b.ToLibrary()) + } + + // return the deployment + return d.ToLibrary(builds), nil +} diff --git a/database/deployment/get_repo.go b/database/deployment/get_repo.go new file mode 100644 index 000000000..b240adc20 --- /dev/null +++ b/database/deployment/get_repo.go @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "strconv" + + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// GetDeploymentForRepo gets a deployment by repo ID and number from the database. +func (e *engine) GetDeploymentForRepo(ctx context.Context, r *library.Repo, number int64) (*library.Deployment, error) { + e.logger.WithFields(logrus.Fields{ + "deployment": number, + "org": r.GetOrg(), + "repo": r.GetName(), + }).Tracef("getting deployment %s/%d from the database", r.GetFullName(), number) + + // variable to store query results + d := new(database.Deployment) + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableDeployment). + Where("repo_id = ?", r.GetID()). + Where("number = ?", number). + Take(d). + Error + if err != nil { + return nil, err + } + + builds := []*library.Build{} + + for _, a := range d.Builds { + bID, err := strconv.ParseInt(a, 10, 64) + if err != nil { + return nil, err + } + // variable to store query results + b := new(database.Build) + + // send query to the database and store result in variable + err = e.client. + Table(constants.TableBuild). + Where("id = ?", bID). + Take(b). + Error + if err != nil { + return nil, err + } + + builds = append(builds, b.ToLibrary()) + } + + return d.ToLibrary(builds), nil +} diff --git a/database/deployment/get_repo_test.go b/database/deployment/get_repo_test.go new file mode 100644 index 000000000..3a6458ece --- /dev/null +++ b/database/deployment/get_repo_test.go @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" +) + +func TestDeployment_Engine_GetDeploymentForRepo(t *testing.T) { + builds := []*library.Build{} + + // setup types + _deploymentOne := testDeployment() + _deploymentOne.SetID(1) + _deploymentOne.SetRepoID(1) + _deploymentOne.SetNumber(1) + _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") + _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") + _deploymentOne.SetRef("refs/heads/master") + _deploymentOne.SetTask("vela-deploy") + _deploymentOne.SetTarget("production") + _deploymentOne.SetDescription("Deployment request from Vela") + _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) + _deploymentOne.SetCreatedAt(1) + _deploymentOne.SetCreatedBy("octocat") + _deploymentOne.SetBuilds(builds) + + _repo := testRepo() + _repo.SetID(1) + _repo.SetUserID(1) + _repo.SetOrg("foo") + _repo.SetName("bar") + _repo.SetFullName("foo/bar") + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows( + []string{"id", "repo_id", "number", "url", "commit", "ref", "task", "target", "description", "payload", "created_at", "created_by", "builds"}). + AddRow(1, 1, 1, "https://github.com/github/octocat/deployments/1", "48afb5bdc41ad69bf22588491333f7cf71135163", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{}") + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT * FROM "deployments" WHERE repo_id = $1 AND number = $2 LIMIT 1`).WithArgs(1, 1).WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + _, err := _sqlite.CreateDeployment(context.TODO(), _deploymentOne) + if err != nil { + t.Errorf("unable to create test deployment for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want *library.Deployment + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: _deploymentOne, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: _deploymentOne, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.GetDeploymentForRepo(context.TODO(), _repo, 1) + + if test.failure { + if err == nil { + t.Errorf("GetDeploymentForRepo for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("GetDeploymentForRepo for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("GetDeploymentForRepo for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/deployment/get_test.go b/database/deployment/get_test.go new file mode 100644 index 000000000..49d4097f5 --- /dev/null +++ b/database/deployment/get_test.go @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" +) + +func TestDeployment_Engine_GetDeployment(t *testing.T) { + builds := []*library.Build{} + + // setup types + _deploymentOne := testDeployment() + _deploymentOne.SetID(1) + _deploymentOne.SetRepoID(1) + _deploymentOne.SetNumber(1) + _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") + _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") + _deploymentOne.SetRef("refs/heads/master") + _deploymentOne.SetTask("vela-deploy") + _deploymentOne.SetTarget("production") + _deploymentOne.SetDescription("Deployment request from Vela") + _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) + _deploymentOne.SetCreatedAt(1) + _deploymentOne.SetCreatedBy("octocat") + _deploymentOne.SetBuilds(builds) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows( + []string{"id", "repo_id", "number", "url", "commit", "ref", "task", "target", "description", "payload", "created_at", "created_by", "builds"}). + AddRow(1, 1, 1, "https://github.com/github/octocat/deployments/1", "48afb5bdc41ad69bf22588491333f7cf71135163", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{}") + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT * FROM "deployments" WHERE id = $1 LIMIT 1`).WithArgs(1).WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + _, err := _sqlite.CreateDeployment(context.TODO(), _deploymentOne) + if err != nil { + t.Errorf("unable to create test deployment for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want *library.Deployment + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: _deploymentOne, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: _deploymentOne, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.GetDeployment(1) + + if test.failure { + if err == nil { + t.Errorf("GetDeployment for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("GetDeployment for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("GetDeployment for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/deployment/index.go b/database/deployment/index.go new file mode 100644 index 000000000..0fa2fac4c --- /dev/null +++ b/database/deployment/index.go @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import "context" + +const ( + // CreateRepoIDIndex represents a query to create an + // index on the deployments table for the repo_id column. + CreateRepoIDIndex = ` +CREATE INDEX +IF NOT EXISTS +deployments_repo_id +ON deployments (repo_id); +` +) + +// CreateDeploymetsIndexes creates the indexes for the deployments table in the database. +func (e *engine) CreateDeploymentIndexes(ctx context.Context) error { + e.logger.Tracef("creating indexes for deployments table in the database") + + // create the repo_id column index for the deployments table + return e.client.Exec(CreateRepoIDIndex).Error +} diff --git a/database/deployment/index_test.go b/database/deployment/index_test.go new file mode 100644 index 000000000..5a3b282d5 --- /dev/null +++ b/database/deployment/index_test.go @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "testing" + + "github.com/DATA-DOG/go-sqlmock" +) + +func TestDeployment_Engine_CreateDeploymentIndexes(t *testing.T) { + // setup types + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + _mock.ExpectExec(CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + // setup tests + tests := []struct { + failure bool + name string + database *engine + }{ + { + failure: false, + name: "postgres", + database: _postgres, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := test.database.CreateDeploymentIndexes(context.TODO()) + + if test.failure { + if err == nil { + t.Errorf("CreateDeploymentIndexes for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("CreateDeploymentIndexes for %s returned err: %v", test.name, err) + } + }) + } +} diff --git a/database/deployment/interface.go b/database/deployment/interface.go new file mode 100644 index 000000000..0429c4d29 --- /dev/null +++ b/database/deployment/interface.go @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + + "github.com/go-vela/types/library" +) + +// DeploymentInterface represents the Vela interface for deployment +// functions with the supported Database backends. +// +//nolint:revive // ignore name stutter +type DeploymentInterface interface { + // Deployment Data Definition Language Functions + // + // https://en.wikipedia.org/wiki/Data_definition_language + + // CreateDeploymentIndexes defines a function that creates the indexes for the deployment table. + CreateDeploymentIndexes(context.Context) error + // CreateDeploymentTable defines a function that creates the deployment table. + CreateDeploymentTable(context.Context, string) error + + // Deployment Data Manipulation Language Functions + // + // https://en.wikipedia.org/wiki/Data_manipulation_language + + // CountDeployments defines a function that gets the count of all deployments. + CountDeployments(context.Context) (int64, error) + // CountDeploymentsForRepo defines a function that gets the count of deployments by repo ID. + CountDeploymentsForRepo(context.Context, *library.Repo) (int64, error) + // CreateDeployment defines a function that creates a new deployment. + CreateDeployment(context.Context, *library.Deployment) (*library.Deployment, error) + // DeleteDeployment defines a function that deletes an existing deployment. + DeleteDeployment(*library.Deployment) error + // GetDeployment defines a function that gets a deployment by ID. + GetDeployment(int64) (*library.Deployment, error) + // GetDeploymentForRepo defines a function that gets a deployment by repo ID and number. + GetDeploymentForRepo(context.Context, *library.Repo, int64) (*library.Deployment, error) + // ListDeployments defines a function that gets a list of all deployments. + ListDeployments(context.Context) ([]*library.Deployment, error) + // ListDeploymentsForRepo defines a function that gets a list of deployments by repo ID. + ListDeploymentsForRepo(context.Context, *library.Repo, int, int) ([]*library.Deployment, error) + // UpdateDeployment defines a function that updates an existing deployment. + UpdateDeployment(*library.Deployment) (*library.Deployment, error) +} diff --git a/database/deployment/list.go b/database/deployment/list.go new file mode 100644 index 000000000..b3f4da509 --- /dev/null +++ b/database/deployment/list.go @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "strconv" + + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" +) + +// ListDeployments gets a list of all deployments from the database. +func (e *engine) ListDeployments(ctx context.Context) ([]*library.Deployment, error) { + e.logger.Trace("listing all deployments from the database") + + // variables to store query results and return value + d := new([]database.Deployment) + deployments := []*library.Deployment{} + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableDeployment). + Find(&d). + Error + if err != nil { + return nil, err + } + + // iterate through all query results + for _, deployment := range *d { + // https://golang.org/doc/faq#closures_and_goroutines + tmp := deployment + + builds := []*library.Build{} + + for _, a := range tmp.Builds { + bID, err := strconv.ParseInt(a, 10, 64) + if err != nil { + return nil, err + } + // variable to store query results + b := new(database.Build) + + // send query to the database and store result in variable + err = e.client. + Table(constants.TableBuild). + Where("id = ?", bID). + Take(b). + Error + if err != nil { + return nil, err + } + + builds = append(builds, b.ToLibrary()) + } + + // convert query result to library type + deployments = append(deployments, tmp.ToLibrary(builds)) + } + + return deployments, nil +} diff --git a/database/deployment/list_repo.go b/database/deployment/list_repo.go new file mode 100644 index 000000000..471da6cf0 --- /dev/null +++ b/database/deployment/list_repo.go @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "strconv" + + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// ListDeploymentsForRepo gets a list of deployments by repo ID from the database. +func (e *engine) ListDeploymentsForRepo(ctx context.Context, r *library.Repo, page, perPage int) ([]*library.Deployment, error) { + e.logger.WithFields(logrus.Fields{ + "org": r.GetOrg(), + "repo": r.GetName(), + }).Tracef("listing deployments for repo %s from the database", r.GetFullName()) + + // variables to store query results and return value + d := new([]database.Deployment) + deployments := []*library.Deployment{} + + // calculate offset for pagination through results + offset := perPage * (page - 1) + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableDeployment). + Where("repo_id = ?", r.GetID()). + Order("number DESC"). + Limit(perPage). + Offset(offset). + Find(&d). + Error + if err != nil { + return nil, err + } + + // iterate through all query results + for _, deployment := range *d { + // https://golang.org/doc/faq#closures_and_goroutines + tmp := deployment + + builds := []*library.Build{} + + for _, a := range tmp.Builds { + bID, err := strconv.ParseInt(a, 10, 64) + if err != nil { + return nil, err + } + // variable to store query results + b := new(database.Build) + + // send query to the database and store result in variable + err = e.client. + Table(constants.TableBuild). + Where("id = ?", bID). + Take(b). + Error + if err != nil { + return nil, err + } + + builds = append(builds, b.ToLibrary()) + } + + // convert query result to library type + deployments = append(deployments, tmp.ToLibrary(builds)) + } + + return deployments, nil +} diff --git a/database/deployment/list_repo_test.go b/database/deployment/list_repo_test.go new file mode 100644 index 000000000..3297a4c4e --- /dev/null +++ b/database/deployment/list_repo_test.go @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" +) + +func TestDeployment_Engine_ListDeploymentsForRepo(t *testing.T) { + _repo := testRepo() + _repo.SetID(1) + _repo.SetOrg("foo") + _repo.SetName("bar") + _repo.SetFullName("foo/bar") + + builds := []*library.Build{} + + // setup types + _deploymentOne := testDeployment() + _deploymentOne.SetID(1) + _deploymentOne.SetRepoID(1) + _deploymentOne.SetNumber(1) + _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") + _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") + _deploymentOne.SetRef("refs/heads/master") + _deploymentOne.SetTask("vela-deploy") + _deploymentOne.SetTarget("production") + _deploymentOne.SetDescription("Deployment request from Vela") + _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) + _deploymentOne.SetCreatedAt(1) + _deploymentOne.SetCreatedBy("octocat") + _deploymentOne.SetBuilds(builds) + + _deploymentTwo := testDeployment() + _deploymentTwo.SetID(2) + _deploymentTwo.SetRepoID(2) + _deploymentTwo.SetNumber(2) + _deploymentTwo.SetURL("https://github.com/github/octocat/deployments/2") + _deploymentTwo.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135164") + _deploymentTwo.SetRef("refs/heads/master") + _deploymentTwo.SetTask("vela-deploy") + _deploymentTwo.SetTarget("production") + _deploymentTwo.SetDescription("Deployment request from Vela") + _deploymentTwo.SetPayload(map[string]string{"foo": "test1"}) + _deploymentTwo.SetCreatedAt(1) + _deploymentTwo.SetCreatedBy("octocat") + _deploymentTwo.SetBuilds(builds) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows( + []string{"id", "repo_id", "number", "url", "commit", "ref", "task", "target", "description", "payload", "created_at", "created_by", "builds"}). + AddRow(1, 1, 1, "https://github.com/github/octocat/deployments/1", "48afb5bdc41ad69bf22588491333f7cf71135163", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{}") + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT * FROM "deployments" WHERE repo_id = $1 ORDER BY number DESC LIMIT 10`).WithArgs(1).WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + _, err := _sqlite.CreateDeployment(context.TODO(), _deploymentOne) + if err != nil { + t.Errorf("unable to create test schedule for sqlite: %v", err) + } + + _, err = _sqlite.CreateDeployment(context.TODO(), _deploymentTwo) + if err != nil { + t.Errorf("unable to create test deployments for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want []*library.Deployment + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: []*library.Deployment{_deploymentOne}, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: []*library.Deployment{_deploymentOne}, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.ListDeploymentsForRepo(context.TODO(), _repo, 1, 10) + + if test.failure { + if err == nil { + t.Errorf("ListDeploymentsForRepo for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("ListDeploymentsForRepo for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ListDeploymentsForRepo for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/deployment/list_test.go b/database/deployment/list_test.go new file mode 100644 index 000000000..203bebbe3 --- /dev/null +++ b/database/deployment/list_test.go @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" +) + +func TestDeployment_Engine_ListDeployments(t *testing.T) { + builds := []*library.Build{} + + // setup types + _deploymentOne := testDeployment() + _deploymentOne.SetID(1) + _deploymentOne.SetRepoID(1) + _deploymentOne.SetNumber(1) + _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") + _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") + _deploymentOne.SetRef("refs/heads/master") + _deploymentOne.SetTask("vela-deploy") + _deploymentOne.SetTarget("production") + _deploymentOne.SetDescription("Deployment request from Vela") + _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) + _deploymentOne.SetCreatedAt(1) + _deploymentOne.SetCreatedBy("octocat") + _deploymentOne.SetBuilds(builds) + + _deploymentTwo := testDeployment() + _deploymentTwo.SetID(2) + _deploymentTwo.SetRepoID(2) + _deploymentTwo.SetNumber(2) + _deploymentTwo.SetURL("https://github.com/github/octocat/deployments/2") + _deploymentTwo.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135164") + _deploymentTwo.SetRef("refs/heads/master") + _deploymentTwo.SetTask("vela-deploy") + _deploymentTwo.SetTarget("production") + _deploymentTwo.SetDescription("Deployment request from Vela") + _deploymentTwo.SetPayload(map[string]string{"foo": "test1"}) + _deploymentTwo.SetCreatedAt(1) + _deploymentTwo.SetCreatedBy("octocat") + _deploymentTwo.SetBuilds(builds) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows( + []string{"id", "repo_id", "number", "url", "commit", "ref", "task", "target", "description", "payload", "created_at", "created_by", "builds"}). + AddRow(1, 1, 1, "https://github.com/github/octocat/deployments/1", "48afb5bdc41ad69bf22588491333f7cf71135163", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{}"). + AddRow(2, 2, 2, "https://github.com/github/octocat/deployments/2", "48afb5bdc41ad69bf22588491333f7cf71135164", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{}") + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT * FROM "deployments"`).WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + _, err := _sqlite.CreateDeployment(context.TODO(), _deploymentOne) + if err != nil { + t.Errorf("unable to create test deployment for sqlite: %v", err) + } + + _, err = _sqlite.CreateDeployment(context.TODO(), _deploymentTwo) + if err != nil { + t.Errorf("unable to create test deployment for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want []*library.Deployment + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: []*library.Deployment{_deploymentOne, _deploymentTwo}, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: []*library.Deployment{_deploymentOne, _deploymentTwo}, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.ListDeployments(context.TODO()) + + if test.failure { + if err == nil { + t.Errorf("ListDeployments for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("ListDeployments for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ListDeployments for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/deployment/opts.go b/database/deployment/opts.go new file mode 100644 index 000000000..db719b39d --- /dev/null +++ b/database/deployment/opts.go @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + + "github.com/sirupsen/logrus" + + "gorm.io/gorm" +) + +// EngineOpt represents a configuration option to initialize the database engine for Deployments. +type EngineOpt func(*engine) error + +// WithClient sets the gorm.io/gorm client in the database engine for Deployments. +func WithClient(client *gorm.DB) EngineOpt { + return func(e *engine) error { + // set the gorm.io/gorm client in the deployment engine + e.client = client + + return nil + } +} + +// WithLogger sets the github.com/sirupsen/logrus logger in the database engine for Deployments. +func WithLogger(logger *logrus.Entry) EngineOpt { + return func(e *engine) error { + // set the github.com/sirupsen/logrus logger in the deployment engine + e.logger = logger + + return nil + } +} + +// WithSkipCreation sets the skip creation logic in the database engine for Deployments. +func WithSkipCreation(skipCreation bool) EngineOpt { + return func(e *engine) error { + // set to skip creating tables and indexes in the deployment engine + e.config.SkipCreation = skipCreation + + return nil + } +} + +// WithContext sets the context in the database engine for Deployments. +func WithContext(ctx context.Context) EngineOpt { + return func(e *engine) error { + e.ctx = ctx + + return nil + } +} diff --git a/database/deployment/opts_test.go b/database/deployment/opts_test.go new file mode 100644 index 000000000..ad393db84 --- /dev/null +++ b/database/deployment/opts_test.go @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "reflect" + "testing" + + "github.com/sirupsen/logrus" + + "gorm.io/gorm" +) + +func TestDeployment_EngineOpt_WithClient(t *testing.T) { + // setup types + e := &engine{client: new(gorm.DB)} + + // setup tests + tests := []struct { + failure bool + name string + client *gorm.DB + want *gorm.DB + }{ + { + failure: false, + name: "client set to new database", + client: new(gorm.DB), + want: new(gorm.DB), + }, + { + failure: false, + name: "client set to nil", + client: nil, + want: nil, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := WithClient(test.client)(e) + + if test.failure { + if err == nil { + t.Errorf("WithClient for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("WithClient returned err: %v", err) + } + + if !reflect.DeepEqual(e.client, test.want) { + t.Errorf("WithClient is %v, want %v", e.client, test.want) + } + }) + } +} + +func TestDeployment_EngineOpt_WithLogger(t *testing.T) { + // setup types + e := &engine{logger: new(logrus.Entry)} + + // setup tests + tests := []struct { + failure bool + name string + logger *logrus.Entry + want *logrus.Entry + }{ + { + failure: false, + name: "logger set to new entry", + logger: new(logrus.Entry), + want: new(logrus.Entry), + }, + { + failure: false, + name: "logger set to nil", + logger: nil, + want: nil, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := WithLogger(test.logger)(e) + + if test.failure { + if err == nil { + t.Errorf("WithLogger for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("WithLogger returned err: %v", err) + } + + if !reflect.DeepEqual(e.logger, test.want) { + t.Errorf("WithLogger is %v, want %v", e.logger, test.want) + } + }) + } +} + +func TestDeployment_EngineOpt_WithSkipCreation(t *testing.T) { + // setup types + e := &engine{config: new(config)} + + // setup tests + tests := []struct { + failure bool + name string + skipCreation bool + want bool + }{ + { + failure: false, + name: "skip creation set to true", + skipCreation: true, + want: true, + }, + { + failure: false, + name: "skip creation set to false", + skipCreation: false, + want: false, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := WithSkipCreation(test.skipCreation)(e) + + if test.failure { + if err == nil { + t.Errorf("WithSkipCreation for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("WithSkipCreation returned err: %v", err) + } + + if !reflect.DeepEqual(e.config.SkipCreation, test.want) { + t.Errorf("WithSkipCreation is %v, want %v", e.config.SkipCreation, test.want) + } + }) + } +} diff --git a/database/deployment/table.go b/database/deployment/table.go new file mode 100644 index 000000000..5f72b2a3c --- /dev/null +++ b/database/deployment/table.go @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + + "github.com/go-vela/types/constants" +) + +const ( + // CreatePostgresTable represents a query to create the Postgres deployments table. + CreatePostgresTable = ` +CREATE TABLE +IF NOT EXISTS +deployments ( + id SERIAL PRIMARY KEY, + repo_id INTEGER, + number INTEGER, + url VARCHAR(500), + commit VARCHAR(500), + ref VARCHAR(500), + task VARCHAR(500), + target VARCHAR(500), + description VARCHAR(2500), + payload VARCHAR(2500), + created_at INTEGER, + created_by VARCHAR(250), + builds VARCHAR(50), + UNIQUE(repo_id, number) +); +` + + // CreateSqliteTable represents a query to create the Sqlite deployments table. + CreateSqliteTable = ` +CREATE TABLE +IF NOT EXISTS +deployments ( + id SERIAL PRIMARY KEY, + repo_id INTEGER, + number INTEGER, + url VARCHAR(1000), + "commit" VARCHAR(500), + ref VARCHAR(500), + task VARCHAR(500), + target VARCHAR(500), + description VARCHAR(2500), + payload VARCHAR(2500), + created_at INTEGER, + created_by VARCHAR(250), + builds VARCHAR(50), + UNIQUE(repo_id, number) +); +` +) + +// CreateDeploymentTable creates the deployments table in the database. +func (e *engine) CreateDeploymentTable(ctx context.Context, driver string) error { + e.logger.Tracef("creating deployments table in the database") + + // handle the driver provided to create the table + switch driver { + case constants.DriverPostgres: + // create the deployments table for Postgres + return e.client.Exec(CreatePostgresTable).Error + case constants.DriverSqlite: + fallthrough + default: + // create the deployments table for Sqlite + return e.client.Exec(CreateSqliteTable).Error + } +} diff --git a/database/deployment/table_test.go b/database/deployment/table_test.go new file mode 100644 index 000000000..4815ba948 --- /dev/null +++ b/database/deployment/table_test.go @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "testing" + + "github.com/DATA-DOG/go-sqlmock" +) + +func TestDeployment_Engine_CreateDeploymentTable(t *testing.T) { + // setup types + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + _mock.ExpectExec(CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + // setup tests + tests := []struct { + failure bool + name string + database *engine + }{ + { + failure: false, + name: "postgres", + database: _postgres, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := test.database.CreateDeploymentTable(context.TODO(), test.name) + + if test.failure { + if err == nil { + t.Errorf("CreateDeploymentTable for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("CreateDeploymentTable for %s returned err: %v", test.name, err) + } + }) + } +} diff --git a/database/deployment/update.go b/database/deployment/update.go new file mode 100644 index 000000000..e7f0973bb --- /dev/null +++ b/database/deployment/update.go @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// UpdateDeployment updates an existing deployment in the database. +func (e *engine) UpdateDeployment(d *library.Deployment) (*library.Deployment, error) { + e.logger.WithFields(logrus.Fields{ + "deployment": d.GetID(), + }).Tracef("updating deployment %d in the database", d.GetID()) + + // cast the library type to database type + deployment := database.DeploymentFromLibrary(d) + + // validate the necessary fields are populated + err := deployment.Validate() + if err != nil { + return nil, err + } + + result := e.client.Table(constants.TableDeployment).Save(deployment) + + // send query to the database + return deployment.ToLibrary(d.Builds), result.Error +} diff --git a/database/deployment/update_test.go b/database/deployment/update_test.go new file mode 100644 index 000000000..5b3e00f56 --- /dev/null +++ b/database/deployment/update_test.go @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: Apache-2.0 + +package deployment + +import ( + "context" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" +) + +func TestDeployment_Engine_UpdateDeployment(t *testing.T) { + builds := []*library.Build{} + + // setup types + _deploymentOne := testDeployment() + _deploymentOne.SetID(1) + _deploymentOne.SetRepoID(1) + _deploymentOne.SetNumber(1) + _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") + _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") + _deploymentOne.SetRef("refs/heads/master") + _deploymentOne.SetTask("vela-deploy") + _deploymentOne.SetTarget("production") + _deploymentOne.SetDescription("Deployment request from Vela") + _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) + _deploymentOne.SetCreatedAt(1) + _deploymentOne.SetCreatedBy("octocat") + _deploymentOne.SetBuilds(builds) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // ensure the mock expects the query + _mock.ExpectExec(`UPDATE "deployments" +SET "number"=$1,"repo_id"=$2,"url"=$3,"commit"=$4,"ref"=$5,"task"=$6,"target"=$7,"description"=$8,"payload"=$9,"created_at"=$10,"created_by"=$11,"builds"=$12 +WHERE "id" = $13`). + WithArgs(1, 1, "https://github.com/github/octocat/deployments/1", "48afb5bdc41ad69bf22588491333f7cf71135163", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{}", 1). + WillReturnResult(sqlmock.NewResult(1, 1)) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + _, err := _sqlite.CreateDeployment(context.TODO(), _deploymentOne) + if err != nil { + t.Errorf("unable to create test deployment for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + }{ + { + failure: false, + name: "postgres", + database: _postgres, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, err = test.database.UpdateDeployment(_deploymentOne) + + if test.failure { + if err == nil { + t.Errorf("UpdateDeployment for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("UpdateDeployment for %s returned err: %v", test.name, err) + } + }) + } +} diff --git a/database/integration_test.go b/database/integration_test.go index 2b77c52dd..818ba081f 100644 --- a/database/integration_test.go +++ b/database/integration_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/go-vela/server/database/build" + "github.com/go-vela/server/database/deployment" "github.com/go-vela/server/database/executable" "github.com/go-vela/server/database/hook" "github.com/go-vela/server/database/log" @@ -118,6 +119,8 @@ func TestDatabase_Integration(t *testing.T) { t.Run("test_builds", func(t *testing.T) { testBuilds(t, db, resources) }) + t.Run("test_deployments", func(t *testing.T) { testDeployments(t, db, resources) }) + t.Run("test_executables", func(t *testing.T) { testExecutables(t, db, resources) }) t.Run("test_hooks", func(t *testing.T) { testHooks(t, db, resources) }) @@ -252,24 +255,11 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to list builds: %v", err) } - if !cmp.Equal(list, resources.Builds) { - t.Errorf("ListBuilds() is %v, want %v", list, resources.Builds) + if diff := cmp.Diff(resources.Builds, list); diff != "" { + t.Errorf("ListBuilds() mismatch (-want +got):\n%s", diff) } methods["ListBuilds"] = true - // list the builds for a deployment - list, count, err = db.ListBuildsForDeployment(context.TODO(), resources.Deployments[0], nil, 1, 10) - if err != nil { - t.Errorf("unable to list builds for deployment %d: %v", resources.Deployments[0].GetID(), err) - } - if int(count) != len(resources.Builds) { - t.Errorf("ListBuildsForDeployment() is %v, want %v", count, len(resources.Builds)) - } - if !cmp.Equal(list, []*library.Build{resources.Builds[1], resources.Builds[0]}) { - t.Errorf("ListBuildsForDeployment() is %v, want %v", list, []*library.Build{resources.Builds[1], resources.Builds[0]}) - } - methods["ListBuildsForDeployment"] = true - // list the builds for an org list, count, err = db.ListBuildsForOrg(context.TODO(), resources.Repos[0].GetOrg(), nil, 1, 10) if err != nil { @@ -278,8 +268,8 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { if int(count) != len(resources.Builds) { t.Errorf("ListBuildsForOrg() is %v, want %v", count, len(resources.Builds)) } - if !cmp.Equal(list, resources.Builds) { - t.Errorf("ListBuildsForOrg() is %v, want %v", list, resources.Builds) + if diff := cmp.Diff(resources.Builds, list); diff != "" { + t.Errorf("ListBuildsForOrg() mismatch (-want +got):\n%s", diff) } methods["ListBuildsForOrg"] = true @@ -291,8 +281,8 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { if int(count) != len(resources.Builds) { t.Errorf("ListBuildsForRepo() is %v, want %v", count, len(resources.Builds)) } - if !cmp.Equal(list, []*library.Build{resources.Builds[1], resources.Builds[0]}) { - t.Errorf("ListBuildsForRepo() is %v, want %v", list, []*library.Build{resources.Builds[1], resources.Builds[0]}) + if diff := cmp.Diff([]*library.Build{resources.Builds[1], resources.Builds[0]}, list); diff != "" { + t.Errorf("ListBuildsForRepo() mismatch (-want +got):\n%s", diff) } methods["ListBuildsForRepo"] = true @@ -304,8 +294,8 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { if int(count) != len(resources.Builds) { t.Errorf("ListPendingAndRunningBuildsForRepo() is %v, want %v", count, len(resources.Builds)) } - if !cmp.Equal(list, []*library.Build{resources.Builds[0], resources.Builds[1]}) { - t.Errorf("ListPendingAndRunningBuildsForRepo() is %v, want %v", list, []*library.Build{resources.Builds[0], resources.Builds[1]}) + if diff := cmp.Diff([]*library.Build{resources.Builds[0], resources.Builds[1]}, list); diff != "" { + t.Errorf("ListPendingAndRunningBuildsForRepo() mismatch (-want +got):\n%s", diff) } methods["ListPendingAndRunningBuildsForRepo"] = true @@ -314,8 +304,8 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to list pending and running builds: %v", err) } - if !cmp.Equal(queueList, queueBuilds) { - t.Errorf("ListPendingAndRunningBuilds() is %v, want %v", queueList, queueBuilds) + if diff := cmp.Diff(queueBuilds, queueList); diff != "" { + t.Errorf("ListPendingAndRunningBuilds() mismatch (-want +got):\n%s", diff) } methods["ListPendingAndRunningBuilds"] = true @@ -324,8 +314,8 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to get last build for repo %d: %v", resources.Repos[0].GetID(), err) } - if !cmp.Equal(got, resources.Builds[1]) { - t.Errorf("LastBuildForRepo() is %v, want %v", got, resources.Builds[1]) + if diff := cmp.Diff(resources.Builds[1], got); diff != "" { + t.Errorf("LastBuildForRepo() mismatch (-want +got):\n%s", diff) } methods["LastBuildForRepo"] = true @@ -336,8 +326,8 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to get build %d for repo %d: %v", build.GetID(), repo.GetID(), err) } - if !cmp.Equal(got, build) { - t.Errorf("GetBuildForRepo() is %v, want %v", got, build) + if diff := cmp.Diff(build, got); diff != "" { + t.Errorf("GetBuildForRepo() mismatch (-want +got):\n%s", diff) } } methods["GetBuildForRepo"] = true @@ -365,8 +355,8 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to get build %d by ID: %v", build.GetID(), err) } - if !cmp.Equal(got, build) { - t.Errorf("GetBuild() is %v, want %v", got, build) + if diff := cmp.Diff(build, got); diff != "" { + t.Errorf("GetBuild() mismatch (-want +got):\n%s", diff) } } methods["UpdateBuild"] = true @@ -430,8 +420,8 @@ func testExecutables(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to get executable %d for build %d: %v", executable.GetID(), executable.GetBuildID(), err) } - if !cmp.Equal(got, executable) { - t.Errorf("PopBuildExecutable() is %v, want %v", got, executable) + if diff := cmp.Diff(executable, got); diff != "" { + t.Errorf("PopBuildExecutable() mismatch (-want +got):\n%s", diff) } } methods["PopBuildExecutable"] = true @@ -472,6 +462,125 @@ func testExecutables(t *testing.T, db Interface, resources *Resources) { } } +func testDeployments(t *testing.T, db Interface, resources *Resources) { + // create a variable to track the number of methods called for deployments + methods := make(map[string]bool) + // capture the element type of the deployment interface + element := reflect.TypeOf(new(deployment.DeploymentInterface)).Elem() + // iterate through all methods found in the deployment interface + for i := 0; i < element.NumMethod(); i++ { + // skip tracking the methods to create indexes and tables for deployments + // since those are already called when the database engine starts + if strings.Contains(element.Method(i).Name, "Index") || + strings.Contains(element.Method(i).Name, "Table") { + continue + } + + // add the method name to the list of functions + methods[element.Method(i).Name] = false + } + + // create the deployments + for _, deployment := range resources.Deployments { + _, err := db.CreateDeployment(context.TODO(), deployment) + if err != nil { + t.Errorf("unable to create deployment %d: %v", deployment.GetID(), err) + } + } + methods["CreateDeployment"] = true + + // count the deployments + count, err := db.CountDeployments(context.TODO()) + if err != nil { + t.Errorf("unable to count deployment: %v", err) + } + if int(count) != len(resources.Deployments) { + t.Errorf("CountDeployments() is %v, want %v", count, len(resources.Deployments)) + } + methods["CountDeployments"] = true + + // count the deployments for a repo + count, err = db.CountDeploymentsForRepo(context.TODO(), resources.Repos[0]) + if err != nil { + t.Errorf("unable to count deployments for repo %d: %v", resources.Repos[0].GetID(), err) + } + if int(count) != len(resources.Builds) { + t.Errorf("CountDeploymentsForRepo() is %v, want %v", count, len(resources.Builds)) + } + methods["CountDeploymentsForRepo"] = true + + // list the deployments + list, err := db.ListDeployments(context.TODO()) + if err != nil { + t.Errorf("unable to list deployments: %v", err) + } + if diff := cmp.Diff(resources.Deployments, list); diff != "" { + t.Errorf("ListDeployments() mismatch (-want +got):\n%s", diff) + } + methods["ListDeployments"] = true + + // list the deployments for a repo + list, err = db.ListDeploymentsForRepo(context.TODO(), resources.Repos[0], 1, 10) + if err != nil { + t.Errorf("unable to list deployments for repo %d: %v", resources.Repos[0].GetID(), err) + } + if int(count) != len(resources.Deployments) { + t.Errorf("ListDeploymentsForRepo() is %v, want %v", count, len(resources.Deployments)) + } + if diff := cmp.Diff([]*library.Deployment{resources.Deployments[1], resources.Deployments[0]}, list); diff != "" { + t.Errorf("ListDeploymentsForRepo() mismatch (-want +got):\n%s", diff) + } + methods["ListDeploymentsForRepo"] = true + + // lookup the deployments by name + for _, deployment := range resources.Deployments { + repo := resources.Repos[deployment.GetRepoID()-1] + got, err := db.GetDeploymentForRepo(context.TODO(), repo, deployment.GetNumber()) + if err != nil { + t.Errorf("unable to get deployment %d for repo %d: %v", deployment.GetID(), repo.GetID(), err) + } + if diff := cmp.Diff(deployment, got); diff != "" { + t.Errorf("GetDeploymentForRepo() mismatch (-want +got):\n%s", diff) + } + } + methods["GetDeploymentForRepo"] = true + + // update the deployments + for _, deployment := range resources.Deployments { + _, err = db.UpdateDeployment(deployment) + if err != nil { + t.Errorf("unable to update deployment %d: %v", deployment.GetID(), err) + } + + // lookup the deployment by ID + got, err := db.GetDeployment(deployment.GetID()) + if err != nil { + t.Errorf("unable to get deployment %d by ID: %v", deployment.GetID(), err) + } + if diff := cmp.Diff(deployment, got); diff != "" { + t.Errorf("GetDeployment() mismatch (-want +got):\n%s", diff) + } + } + methods["UpdateDeployment"] = true + methods["GetDeployment"] = true + + // delete the deployments + for _, deployment := range resources.Deployments { + err = db.DeleteDeployment(deployment) + if err != nil { + t.Errorf("unable to delete hook %d: %v", deployment.GetID(), err) + } + } + methods["DeleteDeployment"] = true + + // ensure we called all the methods we expected to + for method, called := range methods { + if !called { + t.Errorf("method %s was not called for deployments", method) + } + } +} + func testHooks(t *testing.T, db Interface, resources *Resources) { // create a variable to track the number of methods called for hooks methods := make(map[string]bool) @@ -524,8 +633,8 @@ func testHooks(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to list hooks: %v", err) } - if !cmp.Equal(list, resources.Hooks) { - t.Errorf("ListHooks() is %v, want %v", list, resources.Hooks) + if diff := cmp.Diff(resources.Hooks, list); diff != "" { + t.Errorf("ListHooks() mismatch (-want +got):\n%s", diff) } methods["ListHooks"] = true @@ -538,8 +647,8 @@ func testHooks(t *testing.T, db Interface, resources *Resources) { if int(count) != len(resources.Hooks)-1 { t.Errorf("ListHooksForRepo() is %v, want %v", count, len(resources.Hooks)) } - if !cmp.Equal(list, []*library.Hook{resources.Hooks[1], resources.Hooks[0]}) { - t.Errorf("ListHooksForRepo() is %v, want %v", list, []*library.Hook{resources.Hooks[1], resources.Hooks[0]}) + if diff := cmp.Diff([]*library.Hook{resources.Hooks[1], resources.Hooks[0]}, list); diff != "" { + t.Errorf("ListHooksForRepo() mismatch (-want +got):\n%s", diff) } methods["ListHooksForRepo"] = true @@ -548,8 +657,8 @@ func testHooks(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to get last hook for repo %d: %v", resources.Repos[0].GetID(), err) } - if !cmp.Equal(got, resources.Hooks[1]) { - t.Errorf("LastHookForRepo() is %v, want %v", got, resources.Hooks[1]) + if diff := cmp.Diff(resources.Hooks[1], got); diff != "" { + t.Errorf("LastHookForRepo() mismatch (-want +got):\n%s", diff) } methods["LastHookForRepo"] = true @@ -558,8 +667,8 @@ func testHooks(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to get last hook for repo %d: %v", resources.Repos[0].GetID(), err) } - if !reflect.DeepEqual(got, resources.Hooks[2]) { - t.Errorf("GetHookByWebhookID() is %v, want %v", got, resources.Hooks[2]) + if diff := cmp.Diff(resources.Hooks[2], got); diff != "" { + t.Errorf("GetHookByWebhookID() mismatch (-want +got):\n%s", diff) } methods["GetHookByWebhookID"] = true @@ -570,8 +679,8 @@ func testHooks(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to get hook %d for repo %d: %v", hook.GetID(), repo.GetID(), err) } - if !cmp.Equal(got, hook) { - t.Errorf("GetHookForRepo() is %v, want %v", got, hook) + if diff := cmp.Diff(hook, got); diff != "" { + t.Errorf("GetHookForRepo() mismatch (-want +got):\n%s", diff) } } methods["GetHookForRepo"] = true @@ -589,8 +698,8 @@ func testHooks(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to get hook %d by ID: %v", hook.GetID(), err) } - if !cmp.Equal(got, hook) { - t.Errorf("GetHook() is %v, want %v", got, hook) + if diff := cmp.Diff(hook, got); diff != "" { + t.Errorf("GetHook() mismatch (-want +got):\n%s", diff) } } methods["UpdateHook"] = true @@ -665,8 +774,8 @@ func testLogs(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to list logs: %v", err) } - if !cmp.Equal(list, resources.Logs) { - t.Errorf("ListLogs() is %v, want %v", list, resources.Logs) + if diff := cmp.Diff(resources.Logs, list); diff != "" { + t.Errorf("ListLogs() mismatch (-want +got):\n%s", diff) } methods["ListLogs"] = true @@ -678,8 +787,8 @@ func testLogs(t *testing.T, db Interface, resources *Resources) { if int(count) != len(resources.Logs) { t.Errorf("ListLogsForBuild() is %v, want %v", count, len(resources.Logs)) } - if !cmp.Equal(list, resources.Logs) { - t.Errorf("ListLogsForBuild() is %v, want %v", list, resources.Logs) + if diff := cmp.Diff(resources.Logs, list); diff != "" { + t.Errorf("ListLogsForBuild() mismatch (-want +got):\n%s", diff) } methods["ListLogsForBuild"] = true @@ -798,8 +907,8 @@ func testPipelines(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to list pipelines: %v", err) } - if !cmp.Equal(list, resources.Pipelines) { - t.Errorf("ListPipelines() is %v, want %v", list, resources.Pipelines) + if diff := cmp.Diff(resources.Pipelines, list); diff != "" { + t.Errorf("ListPipelines() mismatch (-want +got):\n%s", diff) } methods["ListPipelines"] = true @@ -811,8 +920,8 @@ func testPipelines(t *testing.T, db Interface, resources *Resources) { if int(count) != len(resources.Pipelines) { t.Errorf("ListPipelinesForRepo() is %v, want %v", count, len(resources.Pipelines)) } - if !cmp.Equal(list, resources.Pipelines) { - t.Errorf("ListPipelines() is %v, want %v", list, resources.Pipelines) + if diff := cmp.Diff(resources.Pipelines, list); diff != "" { + t.Errorf("ListPipelines() mismatch (-want +got):\n%s", diff) } methods["ListPipelinesForRepo"] = true @@ -823,8 +932,8 @@ func testPipelines(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to get pipeline %d for repo %d: %v", pipeline.GetID(), repo.GetID(), err) } - if !cmp.Equal(got, pipeline) { - t.Errorf("GetPipelineForRepo() is %v, want %v", got, pipeline) + if diff := cmp.Diff(pipeline, got); diff != "" { + t.Errorf("GetPipelineForRepo() mismatch (-want +got):\n%s", diff) } } methods["GetPipelineForRepo"] = true @@ -842,8 +951,8 @@ func testPipelines(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to get pipeline %d by ID: %v", pipeline.GetID(), err) } - if !cmp.Equal(got, pipeline) { - t.Errorf("GetPipeline() is %v, want %v", got, pipeline) + if diff := cmp.Diff(pipeline, got); diff != "" { + t.Errorf("GetPipeline() mismatch (-want +got):\n%s", diff) } } methods["UpdatePipeline"] = true @@ -928,8 +1037,8 @@ func testRepos(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to list repos: %v", err) } - if !cmp.Equal(list, resources.Repos) { - t.Errorf("ListRepos() is %v, want %v", list, resources.Repos) + if diff := cmp.Diff(resources.Repos, list); diff != "" { + t.Errorf("ListRepos() mismatch (-want +got):\n%s", diff) } methods["ListRepos"] = true @@ -941,8 +1050,8 @@ func testRepos(t *testing.T, db Interface, resources *Resources) { if int(count) != len(resources.Repos) { t.Errorf("ListReposForOrg() is %v, want %v", count, len(resources.Repos)) } - if !cmp.Equal(list, resources.Repos) { - t.Errorf("ListReposForOrg() is %v, want %v", list, resources.Repos) + if diff := cmp.Diff(resources.Repos, list); diff != "" { + t.Errorf("ListReposForOrg() mismatch (-want +got):\n%s", diff) } methods["ListReposForOrg"] = true @@ -954,8 +1063,8 @@ func testRepos(t *testing.T, db Interface, resources *Resources) { if int(count) != len(resources.Repos) { t.Errorf("ListReposForUser() is %v, want %v", count, len(resources.Repos)) } - if !cmp.Equal(list, resources.Repos) { - t.Errorf("ListReposForUser() is %v, want %v", list, resources.Repos) + if diff := cmp.Diff(resources.Repos, list); diff != "" { + t.Errorf("ListReposForUser() mismatch (-want +got):\n%s", diff) } methods["ListReposForUser"] = true @@ -965,8 +1074,8 @@ func testRepos(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to get repo %d by org: %v", repo.GetID(), err) } - if !cmp.Equal(got, repo) { - t.Errorf("GetRepoForOrg() is %v, want %v", got, repo) + if diff := cmp.Diff(repo, got); diff != "" { + t.Errorf("GetRepoForOrg() mismatch (-want +got):\n%s", diff) } } methods["GetRepoForOrg"] = true @@ -1898,6 +2007,7 @@ func newResources() *Resources { buildOne.SetStarted(1563474078) buildOne.SetFinished(1563474079) buildOne.SetDeploy("") + buildOne.SetDeployNumber(0) buildOne.SetDeployPayload(raw.StringSliceMap{"foo": "test1"}) buildOne.SetClone("https://github.com/github/octocat.git") buildOne.SetSource("https://github.com/github/octocat/deployments/1") @@ -1933,6 +2043,7 @@ func newResources() *Resources { buildTwo.SetStarted(1563474078) buildTwo.SetFinished(1563474079) buildTwo.SetDeploy("") + buildTwo.SetDeployNumber(0) buildTwo.SetDeployPayload(raw.StringSliceMap{"foo": "test1"}) buildTwo.SetClone("https://github.com/github/octocat.git") buildTwo.SetSource("https://github.com/github/octocat/deployments/1") @@ -1963,29 +2074,36 @@ func newResources() *Resources { executableTwo.SetBuildID(2) executableTwo.SetData([]byte("foo")) + builds := []*library.Build{} deploymentOne := new(library.Deployment) deploymentOne.SetID(1) + deploymentOne.SetNumber(1) deploymentOne.SetRepoID(1) deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") - deploymentOne.SetUser("octocat") deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") deploymentOne.SetRef("refs/heads/main") deploymentOne.SetTask("vela-deploy") deploymentOne.SetTarget("production") deploymentOne.SetDescription("Deployment request from Vela") deploymentOne.SetPayload(map[string]string{"foo": "test1"}) + deploymentOne.SetCreatedAt(1) + deploymentOne.SetCreatedBy("octocat") + deploymentOne.SetBuilds(builds) deploymentTwo := new(library.Deployment) - deploymentTwo.SetID(1) + deploymentTwo.SetID(2) + deploymentTwo.SetNumber(2) deploymentTwo.SetRepoID(1) deploymentTwo.SetURL("https://github.com/github/octocat/deployments/2") - deploymentTwo.SetUser("octocat") deploymentTwo.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135164") deploymentTwo.SetRef("refs/heads/main") deploymentTwo.SetTask("vela-deploy") deploymentTwo.SetTarget("production") deploymentTwo.SetDescription("Deployment request from Vela") deploymentTwo.SetPayload(map[string]string{"foo": "test1"}) + deploymentTwo.SetCreatedAt(1) + deploymentTwo.SetCreatedBy("octocat") + deploymentTwo.SetBuilds(builds) hookOne := new(library.Hook) hookOne.SetID(1) diff --git a/database/interface.go b/database/interface.go index cffbda96f..4a51019b3 100644 --- a/database/interface.go +++ b/database/interface.go @@ -4,6 +4,7 @@ package database import ( "github.com/go-vela/server/database/build" + "github.com/go-vela/server/database/deployment" "github.com/go-vela/server/database/executable" "github.com/go-vela/server/database/hook" "github.com/go-vela/server/database/log" @@ -38,6 +39,9 @@ type Interface interface { // BuildExecutableInterface defines the interface for build executables stored in the database. executable.BuildExecutableInterface + // DeploymentInterface defines the interface for deployments stored in the database. + deployment.DeploymentInterface + // HookInterface defines the interface for hooks stored in the database. hook.HookInterface diff --git a/database/resource.go b/database/resource.go index 35f7c7a97..0396bec72 100644 --- a/database/resource.go +++ b/database/resource.go @@ -6,6 +6,7 @@ import ( "context" "github.com/go-vela/server/database/build" + "github.com/go-vela/server/database/deployment" "github.com/go-vela/server/database/executable" "github.com/go-vela/server/database/hook" "github.com/go-vela/server/database/log" @@ -47,6 +48,17 @@ func (e *engine) NewResources(ctx context.Context) error { return err } + // create the database agnostic engine for deployments + e.DeploymentInterface, err = deployment.New( + deployment.WithContext(e.ctx), + deployment.WithClient(e.client), + deployment.WithLogger(e.logger), + deployment.WithSkipCreation(e.config.SkipCreation), + ) + if err != nil { + return err + } + // create the database agnostic engine for hooks e.HookInterface, err = hook.New( hook.WithContext(e.ctx), diff --git a/database/resource_test.go b/database/resource_test.go index aaa78548d..6f20fb4f5 100644 --- a/database/resource_test.go +++ b/database/resource_test.go @@ -8,6 +8,7 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/go-vela/server/database/build" + "github.com/go-vela/server/database/deployment" "github.com/go-vela/server/database/executable" "github.com/go-vela/server/database/hook" "github.com/go-vela/server/database/log" @@ -33,6 +34,9 @@ func TestDatabase_Engine_NewResources(t *testing.T) { _mock.ExpectExec(build.CreateStatusIndex).WillReturnResult(sqlmock.NewResult(1, 1)) // ensure the mock expects the build executable queries _mock.ExpectExec(executable.CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) + // ensure the mock expects the deployment queries + _mock.ExpectExec(deployment.CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(deployment.CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) // ensure the mock expects the hook queries _mock.ExpectExec(hook.CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(hook.CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) diff --git a/go.mod b/go.mod index 7d92d26b8..6ec5cae90 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/drone/envsubst v1.0.3 github.com/gin-gonic/gin v1.9.1 github.com/go-playground/assert/v2 v2.2.0 - github.com/go-vela/types v0.22.1-0.20231211143329-1eae2f5e371b + github.com/go-vela/types v0.22.1-0.20231222174844-26e54c869418 github.com/golang-jwt/jwt/v5 v5.1.0 github.com/google/go-cmp v0.6.0 github.com/google/go-github/v56 v56.0.0 diff --git a/go.sum b/go.sum index 6b0d6d882..e17adfcee 100644 --- a/go.sum +++ b/go.sum @@ -141,8 +141,8 @@ github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-vela/types v0.22.1-0.20231211143329-1eae2f5e371b h1:8rC4vEVWUFisc11LvGFOsVx76K1HmxxE+jub4s4sb0A= -github.com/go-vela/types v0.22.1-0.20231211143329-1eae2f5e371b/go.mod h1:ljNY36D6YkpObBbNF7Xslv3oxN4mGuQAwWhnnK/V06I= +github.com/go-vela/types v0.22.1-0.20231222174844-26e54c869418 h1:IzkCTpeEVs0r73mIwDPDdVxBtaUr/oxfJcVjUnNCF6g= +github.com/go-vela/types v0.22.1-0.20231222174844-26e54c869418/go.mod h1:cax3mW1kVz/ioI8qltZE+wl9rOLgOPdwBIvCooL09e4= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= diff --git a/router/middleware/build/build_test.go b/router/middleware/build/build_test.go index e0c51c3b7..f86353d3a 100644 --- a/router/middleware/build/build_test.go +++ b/router/middleware/build/build_test.go @@ -61,6 +61,7 @@ func TestBuild_Establish(t *testing.T) { want.SetStarted(0) want.SetFinished(0) want.SetDeploy("") + want.SetDeployNumber(0) want.SetClone("") want.SetSource("") want.SetTitle("") diff --git a/router/middleware/logger.go b/router/middleware/logger.go index 57bc11253..4f85a397e 100644 --- a/router/middleware/logger.go +++ b/router/middleware/logger.go @@ -184,5 +184,6 @@ func (f *ECSFormatter) Format(e *logrus.Entry) ([]byte, error) { TimestampFormat: "2006-01-02T15:04:05.000Z0700", FieldMap: ecsFieldMap, } + return jf.Format(e) } diff --git a/router/middleware/logger_test.go b/router/middleware/logger_test.go index 4a699520a..9264ade1a 100644 --- a/router/middleware/logger_test.go +++ b/router/middleware/logger_test.go @@ -224,7 +224,6 @@ func TestMiddleware_Logger_Sanitize(t *testing.T) { } func TestMiddleware_Format(t *testing.T) { - wantLabels := "labels.vela" // setup data, fields, and logger @@ -265,5 +264,4 @@ func TestMiddleware_Format(t *testing.T) { if !strings.Contains(string(got), "/foobar") { t.Errorf("Format returned %v, want to contain /foobar", string(got)) } - } diff --git a/scm/github/deployment.go b/scm/github/deployment.go index 52b81a88a..614cac08f 100644 --- a/scm/github/deployment.go +++ b/scm/github/deployment.go @@ -37,17 +37,20 @@ func (c *client) GetDeployment(ctx context.Context, u *library.User, r *library. c.Logger.Tracef("Unable to unmarshal payload for deployment id %v", deployment.ID) } + createdAt := deployment.CreatedAt.Unix() + return &library.Deployment{ ID: deployment.ID, RepoID: r.ID, URL: deployment.URL, - User: deployment.Creator.Login, Commit: deployment.SHA, Ref: deployment.Ref, Task: deployment.Task, Target: deployment.Environment, Description: deployment.Description, Payload: payload, + CreatedAt: &createdAt, + CreatedBy: deployment.Creator.Login, }, nil } @@ -129,18 +132,22 @@ func (c *client) GetDeploymentList(ctx context.Context, u *library.User, r *libr if err != nil { c.Logger.Tracef("Unable to unmarshal payload for deployment id %v", deployment.ID) } + + createdAt := deployment.CreatedAt.Unix() + // convert query result to library type deployments = append(deployments, &library.Deployment{ ID: deployment.ID, RepoID: r.ID, URL: deployment.URL, - User: deployment.Creator.Login, Commit: deployment.SHA, Ref: deployment.Ref, Task: deployment.Task, Target: deployment.Environment, Description: deployment.Description, Payload: payload, + CreatedAt: &createdAt, + CreatedBy: deployment.Creator.Login, }) } @@ -182,10 +189,9 @@ func (c *client) CreateDeployment(ctx context.Context, u *library.User, r *libra return err } - d.SetID(deploy.GetID()) + d.SetNumber(deploy.GetID()) d.SetRepoID(r.GetID()) d.SetURL(deploy.GetURL()) - d.SetUser(deploy.GetCreator().GetLogin()) d.SetCommit(deploy.GetSHA()) d.SetRef(deploy.GetRef()) d.SetTask(deploy.GetTask()) diff --git a/scm/github/deployment_test.go b/scm/github/deployment_test.go index 4558bb783..378a2417a 100644 --- a/scm/github/deployment_test.go +++ b/scm/github/deployment_test.go @@ -6,11 +6,8 @@ import ( "context" "net/http" "net/http/httptest" - "reflect" "testing" - "github.com/go-vela/types/raw" - "github.com/gin-gonic/gin" "github.com/go-vela/types/library" @@ -48,7 +45,6 @@ func TestGithub_CreateDeployment(t *testing.T) { d.SetID(1) d.SetRepoID(1) d.SetURL("https://api.github.com/repos/foo/bar/deployments/1") - d.SetUser("octocat") d.SetCommit("a84d88e7554fc1fa21bcbc4efae3c782a70d2b9d") d.SetRef("topic-branch") d.SetTask("deploy") @@ -68,181 +64,3 @@ func TestGithub_CreateDeployment(t *testing.T) { t.Errorf("CreateDeployment returned err: %v", err) } } - -func TestGithub_GetDeployment(t *testing.T) { - // setup context - gin.SetMode(gin.TestMode) - - resp := httptest.NewRecorder() - _, engine := gin.CreateTestContext(resp) - - // setup mock server - engine.GET("/api/v3/repos/:org/:repo/deployments/:deployment", func(c *gin.Context) { - c.Header("Content-Type", "application/json") - c.Status(http.StatusOK) - c.File("testdata/deployment.json") - }) - - s := httptest.NewServer(engine) - defer s.Close() - - // setup types - u := new(library.User) - u.SetName("foo") - u.SetToken("bar") - - r := new(library.Repo) - r.SetID(1) - r.SetOrg("foo") - r.SetName("bar") - r.SetFullName("foo/bar") - - want := new(library.Deployment) - want.SetID(1) - want.SetRepoID(1) - want.SetURL("https://api.github.com/repos/foo/bar/deployments/1") - want.SetUser("octocat") - want.SetCommit("a84d88e7554fc1fa21bcbc4efae3c782a70d2b9d") - want.SetRef("topic-branch") - want.SetTask("deploy") - want.SetTarget("production") - want.SetDescription("Deploy request from Vela") - want.SetPayload(raw.StringSliceMap{"deploy": "migrate"}) - - client, _ := NewTest(s.URL, "https://foo.bar.com") - - // run test - got, err := client.GetDeployment(context.TODO(), u, r, 1) - - if resp.Code != http.StatusOK { - t.Errorf("GetDeployment returned %v, want %v", resp.Code, http.StatusOK) - } - - if err != nil { - t.Errorf("GetDeployment returned err: %v", err) - } - - if !reflect.DeepEqual(got, want) { - t.Errorf("GetDeployment is %v, want %v", got, want) - } -} - -func TestGithub_GetDeploymentCount(t *testing.T) { - // setup context - gin.SetMode(gin.TestMode) - - resp := httptest.NewRecorder() - _, engine := gin.CreateTestContext(resp) - - // setup mock server - engine.GET("/api/v3/repos/:org/:repo/deployments", func(c *gin.Context) { - c.Header("Content-Type", "application/json") - c.Status(http.StatusOK) - c.File("testdata/deployments.json") - }) - - s := httptest.NewServer(engine) - defer s.Close() - - // setup types - u := new(library.User) - u.SetName("foo") - u.SetToken("bar") - - r := new(library.Repo) - r.SetID(1) - r.SetOrg("foo") - r.SetName("bar") - r.SetFullName("foo/bar") - - want := int64(2) - - client, _ := NewTest(s.URL, "https://foo.bar.com") - - // run test - got, err := client.GetDeploymentCount(context.TODO(), u, r) - - if resp.Code != http.StatusOK { - t.Errorf("GetDeployment returned %v, want %v", resp.Code, http.StatusOK) - } - - if err != nil { - t.Errorf("GetDeployment returned err: %v", err) - } - - if !reflect.DeepEqual(got, want) { - t.Errorf("GetDeployment is %v, want %v", got, want) - } -} - -func TestGithub_GetDeploymentList(t *testing.T) { - // setup context - gin.SetMode(gin.TestMode) - - resp := httptest.NewRecorder() - _, engine := gin.CreateTestContext(resp) - - // setup mock server - engine.GET("/api/v3/repos/:org/:repo/deployments", func(c *gin.Context) { - c.Header("Content-Type", "application/json") - c.Status(http.StatusOK) - c.File("testdata/deployments.json") - }) - - s := httptest.NewServer(engine) - defer s.Close() - - // setup types - u := new(library.User) - u.SetName("foo") - u.SetToken("bar") - - r := new(library.Repo) - r.SetID(1) - r.SetOrg("foo") - r.SetName("bar") - r.SetFullName("foo/bar") - - d1 := new(library.Deployment) - d1.SetID(1) - d1.SetRepoID(1) - d1.SetURL("https://api.github.com/repos/foo/bar/deployments/1") - d1.SetUser("octocat") - d1.SetCommit("a84d88e7554fc1fa21bcbc4efae3c782a70d2b9d") - d1.SetRef("topic-branch") - d1.SetTask("deploy") - d1.SetTarget("production") - d1.SetDescription("Deploy request from Vela") - d1.SetPayload(nil) - - d2 := new(library.Deployment) - d2.SetID(2) - d2.SetRepoID(1) - d2.SetURL("https://api.github.com/repos/foo/bar/deployments/2") - d2.SetUser("octocat") - d2.SetCommit("a84d88e7554fc1fa21bcbc4efae3c782a70d2b9d") - d2.SetRef("topic-branch") - d2.SetTask("deploy") - d2.SetTarget("production") - d2.SetDescription("Deploy request from Vela") - d2.SetPayload(raw.StringSliceMap{"deploy": "migrate"}) - - want := []*library.Deployment{d2, d1} - - client, _ := NewTest(s.URL, "https://foo.bar.com") - - // run test - got, err := client.GetDeploymentList(context.TODO(), u, r, 1, 100) - - if resp.Code != http.StatusOK { - t.Errorf("GetDeployment returned %v, want %v", resp.Code, http.StatusOK) - } - - if err != nil { - t.Errorf("GetDeployment returned err: %v", err) - } - - if !reflect.DeepEqual(got, want) { - t.Errorf("GetDeployment is %v, want %v", got, want) - } -} diff --git a/scm/github/webhook.go b/scm/github/webhook.go index f927dcb32..8dcda751d 100644 --- a/scm/github/webhook.go +++ b/scm/github/webhook.go @@ -318,6 +318,7 @@ func (c *client) processDeploymentEvent(h *library.Hook, payload *github.Deploym b.SetEvent(constants.EventDeploy) b.SetClone(repo.GetCloneURL()) b.SetDeploy(payload.GetDeployment().GetEnvironment()) + b.SetDeployNumber(payload.GetDeployment().GetID()) b.SetSource(payload.GetDeployment().GetURL()) b.SetTitle(fmt.Sprintf("%s received from %s", constants.EventDeploy, repo.GetHTMLURL())) b.SetMessage(payload.GetDeployment().GetDescription()) @@ -328,6 +329,18 @@ func (c *client) processDeploymentEvent(h *library.Hook, payload *github.Deploym b.SetBranch(payload.GetDeployment().GetRef()) b.SetRef(payload.GetDeployment().GetRef()) + d := new(library.Deployment) + + d.SetNumber(payload.GetDeployment().GetID()) + d.SetURL(payload.GetDeployment().GetURL()) + d.SetCommit(payload.GetDeployment().GetSHA()) + d.SetRef(b.GetRef()) + d.SetTask(payload.GetDeployment().GetTask()) + d.SetTarget(payload.GetDeployment().GetEnvironment()) + d.SetDescription(payload.GetDeployment().GetDescription()) + d.SetCreatedAt(time.Now().Unix()) + d.SetCreatedBy(payload.GetDeployment().GetCreator().GetLogin()) + // check if payload is provided within request // // use a length of 2 because the payload will @@ -371,10 +384,13 @@ func (c *client) processDeploymentEvent(h *library.Hook, payload *github.Deploym fmt.Sprintf("https://%s/%s/settings/hooks", h.GetHost(), r.GetFullName()), ) + d.SetRef(b.GetRef()) + return &types.Webhook{ - Hook: h, - Repo: r, - Build: b, + Hook: h, + Repo: r, + Build: b, + Deployment: d, }, nil } diff --git a/scm/github/webhook_test.go b/scm/github/webhook_test.go index f0bca7129..fb0aef9c2 100644 --- a/scm/github/webhook_test.go +++ b/scm/github/webhook_test.go @@ -396,6 +396,7 @@ func TestGithub_ProcessWebhook_Deployment(t *testing.T) { wantBuild.SetEvent("deployment") wantBuild.SetClone("https://github.com/Codertocat/Hello-World.git") wantBuild.SetDeploy("production") + wantBuild.SetDeployNumber(145988746) wantBuild.SetSource("https://api.github.com/repos/Codertocat/Hello-World/deployments/145988746") wantBuild.SetTitle("deployment received from https://github.com/Codertocat/Hello-World") wantBuild.SetMessage("") @@ -406,12 +407,24 @@ func TestGithub_ProcessWebhook_Deployment(t *testing.T) { wantBuild.SetBranch("main") wantBuild.SetRef("refs/heads/main") + wantDeployment := new(library.Deployment) + wantDeployment.SetNumber(145988746) + wantDeployment.SetURL("https://api.github.com/repos/Codertocat/Hello-World/deployments/145988746") + wantDeployment.SetCommit("f95f852bd8fca8fcc58a9a2d6c842781e32a215e") + wantDeployment.SetRef("refs/heads/main") + wantDeployment.SetTask("deploy") + wantDeployment.SetTarget("production") + wantDeployment.SetDescription("") + wantDeployment.SetCreatedAt(time.Now().UTC().Unix()) + wantDeployment.SetCreatedBy("Codertocat") + type args struct { file string hook *library.Hook repo *library.Repo build *library.Build deploymentPayload raw.StringSliceMap + deployment *library.Deployment } tests := []struct { @@ -419,7 +432,7 @@ func TestGithub_ProcessWebhook_Deployment(t *testing.T) { args args wantErr bool }{ - {"success", args{file: "deployment.json", hook: wantHook, repo: wantRepo, build: wantBuild, deploymentPayload: raw.StringSliceMap{"foo": "test1", "bar": "test2"}}, false}, + {"success", args{file: "deployment.json", hook: wantHook, repo: wantRepo, build: wantBuild, deploymentPayload: raw.StringSliceMap{"foo": "test1", "bar": "test2"}, deployment: wantDeployment}, false}, {"unexpected json payload", args{file: "deployment_unexpected_json_payload.json", deploymentPayload: raw.StringSliceMap{}}, true}, {"unexpected text payload", args{file: "deployment_unexpected_text_payload.json", deploymentPayload: raw.StringSliceMap{}}, true}, } @@ -446,9 +459,10 @@ func TestGithub_ProcessWebhook_Deployment(t *testing.T) { wantBuild.SetDeployPayload(tt.args.deploymentPayload) want := &types.Webhook{ - Hook: tt.args.hook, - Repo: tt.args.repo, - Build: tt.args.build, + Hook: tt.args.hook, + Repo: tt.args.repo, + Build: tt.args.build, + Deployment: tt.args.deployment, } got, err := client.ProcessWebhook(context.TODO(), request) @@ -515,6 +529,7 @@ func TestGithub_ProcessWebhook_Deployment_Commit(t *testing.T) { wantBuild.SetEvent("deployment") wantBuild.SetClone("https://github.com/Codertocat/Hello-World.git") wantBuild.SetDeploy("production") + wantBuild.SetDeployNumber(145988746) wantBuild.SetSource("https://api.github.com/repos/Codertocat/Hello-World/deployments/145988746") wantBuild.SetTitle("deployment received from https://github.com/Codertocat/Hello-World") wantBuild.SetMessage("") @@ -525,10 +540,23 @@ func TestGithub_ProcessWebhook_Deployment_Commit(t *testing.T) { wantBuild.SetBranch("main") wantBuild.SetRef("refs/heads/main") + wantDeployment := new(library.Deployment) + wantDeployment.SetNumber(145988746) + wantDeployment.SetURL("https://api.github.com/repos/Codertocat/Hello-World/deployments/145988746") + wantDeployment.SetCommit("f95f852bd8fca8fcc58a9a2d6c842781e32a215e") + wantDeployment.SetRef("refs/heads/main") + wantDeployment.SetTask("deploy") + wantDeployment.SetTarget("production") + wantDeployment.SetDescription("") + //wantDeployment.SetPayload(map[string]string{"foo": "test1"}) + wantDeployment.SetCreatedAt(time.Now().UTC().Unix()) + wantDeployment.SetCreatedBy("Codertocat") + want := &types.Webhook{ - Hook: wantHook, - Repo: wantRepo, - Build: wantBuild, + Hook: wantHook, + Repo: wantRepo, + Build: wantBuild, + Deployment: wantDeployment, } got, err := client.ProcessWebhook(context.TODO(), request) @@ -578,9 +606,10 @@ func TestGithub_ProcessWebhook_BadGithubEvent(t *testing.T) { wantHook.SetStatus(constants.StatusSuccess) want := &types.Webhook{ - Hook: wantHook, - Repo: nil, - Build: nil, + Hook: wantHook, + Repo: nil, + Build: nil, + Deployment: nil, } got, err := client.ProcessWebhook(context.TODO(), request) @@ -630,9 +659,10 @@ func TestGithub_ProcessWebhook_BadContentType(t *testing.T) { wantHook.SetStatus(constants.StatusSuccess) want := &types.Webhook{ - Hook: wantHook, - Repo: nil, - Build: nil, + Hook: wantHook, + Repo: nil, + Build: nil, + Deployment: nil, } got, err := client.ProcessWebhook(context.TODO(), request) From 730b0f4f90f307c2403d7efc60309408c1c38133 Mon Sep 17 00:00:00 2001 From: claire1618 <55173466+claire1618@users.noreply.github.com> Date: Wed, 3 Jan 2024 14:19:36 -0600 Subject: [PATCH 2/5] Fix: deployment table post.go and restart.go error (#1032) * feat: adding deployment database * updating things * fix reserved word user * testing and other * tests * fixing tests * fixing things * help * fixing various tests * new types changes * updating build deployNumber field to deploymentID * changing build DeployID field back to DeployNumber * fixing migrations issues * fixing lint * fixing lint things * fixing lint things * getting new types * fixing things * fix pls * fix pls * fix pls * fix pls * fixing things * ... * linter * linter * linter * fixing 2022 copyright lint errors * hopefully fixing tests * fixing integration tests * fixing vaders comments * fixing vaders comments pt 2 * fix * fixing eastons comments * linter * linter * making david mays changes * fixy fix * fixy fix * fix: changing code so that deployment builds are updated in post.go and restart.go --------- Co-authored-by: Claire.Nicholas Co-authored-by: ecrupper --- api/build/restart.go | 4 ++-- api/webhook/post.go | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/api/build/restart.go b/api/build/restart.go index 213f263db..d819c1805 100644 --- a/api/build/restart.go +++ b/api/build/restart.go @@ -340,13 +340,13 @@ func RestartBuild(c *gin.Context) { c.JSON(http.StatusCreated, b) // if the event is a deployment, update the build list - if !strings.EqualFold(b.GetEvent(), constants.EventDeploy) { + if strings.EqualFold(b.GetEvent(), constants.EventDeploy) { d, err := database.FromContext(c).GetDeploymentForRepo(c, r, b.GetDeployNumber()) if err != nil { logger.Errorf("unable to set get deployment for build %s: %v", entry, err) } - build := append(d.Builds, b) + build := append(d.GetBuilds(), b) d.SetBuilds(build) diff --git a/api/webhook/post.go b/api/webhook/post.go index 4414c8da9..7d44689fe 100644 --- a/api/webhook/post.go +++ b/api/webhook/post.go @@ -666,7 +666,7 @@ func PostWebhook(c *gin.Context) { // set the BuildID field h.SetBuildID(b.GetID()) // if event is deployment, update the deployment record to include this build - if !strings.EqualFold(b.GetEvent(), constants.EventDeploy) { + if strings.EqualFold(b.GetEvent(), constants.EventDeploy) { d, err := database.FromContext(c).GetDeploymentForRepo(c, repo, webhook.Deployment.GetNumber()) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { @@ -695,7 +695,8 @@ func PostWebhook(c *gin.Context) { return } } else { - d.SetBuilds([]*library.Build{b}) + build := append(d.GetBuilds(), b) + d.SetBuilds(build) _, err := database.FromContext(c).UpdateDeployment(d) if err != nil { retErr := fmt.Errorf("%s: failed to update deployment %s/%d: %w", baseErr, repo.GetFullName(), d.GetNumber(), err) From 49066352a370681644875dc85512736b29a4234f Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Fri, 5 Jan 2024 09:55:32 -0500 Subject: [PATCH 3/5] enhance(api/workers): add filters to list workers (#1029) * enhance(api/workers): add filters to list workers * add buffer to checked in for integration testing * test filters and fix swagger spec * change active to boolean type * remove default --- api/metrics.go | 2 +- api/worker/list.go | 40 ++++++++++++++++++++- database/integration_test.go | 6 ++-- database/worker/interface.go | 2 +- database/worker/list.go | 31 ++++++++-------- database/worker/list_test.go | 68 +++++++++++++++++++++++++++--------- 6 files changed, 112 insertions(+), 37 deletions(-) diff --git a/api/metrics.go b/api/metrics.go index b3dbbeb9f..afad74583 100644 --- a/api/metrics.go +++ b/api/metrics.go @@ -422,7 +422,7 @@ func recordGauges(c *gin.Context) { // worker_build_limit, active_worker_count, inactive_worker_count, idle_worker_count, available_worker_count, busy_worker_count, error_worker_count if q.WorkerBuildLimit || q.ActiveWorkerCount || q.InactiveWorkerCount || q.IdleWorkerCount || q.AvailableWorkerCount || q.BusyWorkerCount || q.ErrorWorkerCount { // send API call to capture the workers - workers, err := database.FromContext(c).ListWorkers(ctx) + workers, err := database.FromContext(c).ListWorkers(ctx, "all", time.Now().Unix(), 0) if err != nil { logrus.Errorf("unable to get workers: %v", err) } diff --git a/api/worker/list.go b/api/worker/list.go index 6b24484a6..f402144da 100644 --- a/api/worker/list.go +++ b/api/worker/list.go @@ -5,6 +5,8 @@ package worker import ( "fmt" "net/http" + "strconv" + "time" "github.com/gin-gonic/gin" "github.com/go-vela/server/database" @@ -20,6 +22,20 @@ import ( // --- // produces: // - application/json +// parameters: +// - in: query +// name: active +// description: Filter workers based on active status +// type: boolean +// - in: query +// name: checked_in_before +// description: filter workers that have checked in before a certain time +// type: integer +// - in: query +// name: checked_in_after +// description: filter workers that have checked in after a certain time +// type: integer +// default: 0 // security: // - ApiKeyAuth: [] // responses: @@ -48,7 +64,29 @@ func ListWorkers(c *gin.Context) { "user": u.GetName(), }).Info("reading workers") - w, err := database.FromContext(c).ListWorkers(ctx) + active := c.Query("active") + + // capture before query parameter if present, default to now + before, err := strconv.ParseInt(c.DefaultQuery("checked_in_before", strconv.FormatInt(time.Now().UTC().Unix(), 10)), 10, 64) + if err != nil { + retErr := fmt.Errorf("unable to convert `checked_in_before` query parameter: %w", err) + + util.HandleError(c, http.StatusBadRequest, retErr) + + return + } + + // capture after query parameter if present, default to 0 + after, err := strconv.ParseInt(c.DefaultQuery("checked_in_after", "0"), 10, 64) + if err != nil { + retErr := fmt.Errorf("unable to convert `checked_in_after` query parameter: %w", err) + + util.HandleError(c, http.StatusBadRequest, retErr) + + return + } + + w, err := database.FromContext(c).ListWorkers(ctx, active, before, after) if err != nil { retErr := fmt.Errorf("unable to get workers: %w", err) diff --git a/database/integration_test.go b/database/integration_test.go index 818ba081f..49b844fda 100644 --- a/database/integration_test.go +++ b/database/integration_test.go @@ -1938,7 +1938,7 @@ func testWorkers(t *testing.T, db Interface, resources *Resources) { methods["CountWorkers"] = true // list the workers - list, err := db.ListWorkers(context.TODO()) + list, err := db.ListWorkers(context.TODO(), "all", time.Now().Unix(), 0) if err != nil { t.Errorf("unable to list workers: %v", err) } @@ -2450,7 +2450,7 @@ func newResources() *Resources { workerOne.SetRunningBuildIDs([]string{"12345"}) workerOne.SetLastBuildStartedAt(time.Now().UTC().Unix()) workerOne.SetLastBuildFinishedAt(time.Now().UTC().Unix()) - workerOne.SetLastCheckedIn(time.Now().UTC().Unix()) + workerOne.SetLastCheckedIn(time.Now().UTC().Unix() - 60) workerOne.SetBuildLimit(1) workerTwo := new(library.Worker) @@ -2464,7 +2464,7 @@ func newResources() *Resources { workerTwo.SetRunningBuildIDs([]string{"12345"}) workerTwo.SetLastBuildStartedAt(time.Now().UTC().Unix()) workerTwo.SetLastBuildFinishedAt(time.Now().UTC().Unix()) - workerTwo.SetLastCheckedIn(time.Now().UTC().Unix()) + workerTwo.SetLastCheckedIn(time.Now().UTC().Unix() - 60) workerTwo.SetBuildLimit(1) return &Resources{ diff --git a/database/worker/interface.go b/database/worker/interface.go index c594c556a..589e8971b 100644 --- a/database/worker/interface.go +++ b/database/worker/interface.go @@ -37,7 +37,7 @@ type WorkerInterface interface { // GetWorkerForHostname defines a function that gets a worker by hostname. GetWorkerForHostname(context.Context, string) (*library.Worker, error) // ListWorkers defines a function that gets a list of all workers. - ListWorkers(context.Context) ([]*library.Worker, error) + ListWorkers(context.Context, string, int64, int64) ([]*library.Worker, error) // UpdateWorker defines a function that updates an existing worker. UpdateWorker(context.Context, *library.Worker) (*library.Worker, error) } diff --git a/database/worker/list.go b/database/worker/list.go index dbf8ff2a7..bb8169d61 100644 --- a/database/worker/list.go +++ b/database/worker/list.go @@ -4,6 +4,8 @@ package worker import ( "context" + "fmt" + "strconv" "github.com/go-vela/types/constants" "github.com/go-vela/types/database" @@ -11,30 +13,31 @@ import ( ) // ListWorkers gets a list of all workers from the database. -func (e *engine) ListWorkers(ctx context.Context) ([]*library.Worker, error) { +func (e *engine) ListWorkers(ctx context.Context, active string, before, after int64) ([]*library.Worker, error) { e.logger.Trace("listing all workers from the database") // variables to store query results and return value - count := int64(0) w := new([]database.Worker) workers := []*library.Worker{} - // count the results - count, err := e.CountWorkers(ctx) - if err != nil { - return nil, err - } + // build query with checked in constraints + query := e.client.Table(constants.TableWorker). + Where("last_checked_in < ?", before). + Where("last_checked_in > ?", after) + + // if active can be parsed as a boolean, add to query + if b, err := strconv.ParseBool(active); err == nil { + // convert bool to 0/1 for Sqlite + qBool := 0 + if b { + qBool = 1 + } - // short-circuit if there are no results - if count == 0 { - return workers, nil + query.Where("active = ?", fmt.Sprintf("%d", qBool)) } // send query to the database and store result in variable - err = e.client. - Table(constants.TableWorker). - Find(&w). - Error + err := query.Find(&w).Error if err != nil { return nil, err } diff --git a/database/worker/list_test.go b/database/worker/list_test.go index 9c5331fe4..4a5988228 100644 --- a/database/worker/list_test.go +++ b/database/worker/list_test.go @@ -4,44 +4,51 @@ package worker import ( "context" - "reflect" "testing" + "time" "github.com/DATA-DOG/go-sqlmock" "github.com/go-vela/types/library" + "github.com/google/go-cmp/cmp" ) func TestWorker_Engine_ListWorkers(t *testing.T) { + older := time.Now().Unix() - 60 + newer := time.Now().Unix() - 30 // setup types _workerOne := testWorker() _workerOne.SetID(1) _workerOne.SetHostname("worker_0") _workerOne.SetAddress("localhost") _workerOne.SetActive(true) + _workerOne.SetLastCheckedIn(newer) _workerTwo := testWorker() _workerTwo.SetID(2) _workerTwo.SetHostname("worker_1") _workerTwo.SetAddress("localhost") _workerTwo.SetActive(true) + _workerTwo.SetLastCheckedIn(older) + + _workerThree := testWorker() + _workerThree.SetID(3) + _workerThree.SetHostname("worker_2") + _workerThree.SetAddress("localhost") + _workerThree.SetActive(false) + _workerThree.SetLastCheckedIn(newer) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() // create expected result in mock - _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) - - // ensure the mock expects the query - _mock.ExpectQuery(`SELECT count(*) FROM "workers"`).WillReturnRows(_rows) - - // create expected result in mock - _rows = sqlmock.NewRows( + _rows := sqlmock.NewRows( []string{"id", "hostname", "address", "routes", "active", "status", "last_status_update_at", "running_build_ids", "last_build_started_at", "last_build_finished_at", "last_checked_in", "build_limit"}). - AddRow(1, "worker_0", "localhost", nil, true, nil, 0, nil, 0, 0, 0, 0). - AddRow(2, "worker_1", "localhost", nil, true, nil, 0, nil, 0, 0, 0, 0) + AddRow(1, "worker_0", "localhost", nil, true, nil, 0, nil, 0, 0, newer, 0). + AddRow(2, "worker_1", "localhost", nil, true, nil, 0, nil, 0, 0, older, 0). + AddRow(3, "worker_2", "localhost", nil, false, nil, 0, nil, 0, 0, newer, 0) // ensure the mock expects the query - _mock.ExpectQuery(`SELECT * FROM "workers"`).WillReturnRows(_rows) + _mock.ExpectQuery(`SELECT * FROM "workers" WHERE last_checked_in < $1 AND last_checked_in > $2`).WillReturnRows(_rows) _sqlite := testSqlite(t) defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() @@ -56,22 +63,49 @@ func TestWorker_Engine_ListWorkers(t *testing.T) { t.Errorf("unable to create test worker for sqlite: %v", err) } + _, err = _sqlite.CreateWorker(context.TODO(), _workerThree) + if err != nil { + t.Errorf("unable to create test worker for sqlite: %v", err) + } + // setup tests tests := []struct { failure bool + before int64 + active string name string database *engine want []*library.Worker }{ { failure: false, - name: "postgres", + before: newer, + active: "all", + name: "sqlite3 before filter", + database: _sqlite, + want: []*library.Worker{_workerTwo}, + }, + { + failure: false, + before: newer + 1, + active: "all", + name: "postgres catch all", database: _postgres, - want: []*library.Worker{_workerOne, _workerTwo}, + want: []*library.Worker{_workerOne, _workerTwo, _workerThree}, + }, + { + failure: false, + before: newer + 1, + active: "all", + name: "sqlite3 catch all", + database: _sqlite, + want: []*library.Worker{_workerOne, _workerTwo, _workerThree}, }, { failure: false, - name: "sqlite3", + before: newer + 1, + active: "true", + name: "sqlite3 active filter", database: _sqlite, want: []*library.Worker{_workerOne, _workerTwo}, }, @@ -80,7 +114,7 @@ func TestWorker_Engine_ListWorkers(t *testing.T) { // run tests for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := test.database.ListWorkers(context.TODO()) + got, err := test.database.ListWorkers(context.TODO(), test.active, test.before, 0) if test.failure { if err == nil { @@ -94,8 +128,8 @@ func TestWorker_Engine_ListWorkers(t *testing.T) { t.Errorf("ListWorkers for %s returned err: %v", test.name, err) } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("ListWorkers for %s is %v, want %v", test.name, got, test.want) + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("ListWorkers() mismatch (-want +got):\n%s", diff) } }) } From de5cf1020cbbe59e0917ed2281eac44053de4eb7 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Fri, 5 Jan 2024 16:48:30 -0500 Subject: [PATCH 4/5] fix(templates): handle nil PrivateGitHub (#1034) --- compiler/native/expand.go | 9 +++++++++ compiler/native/expand_test.go | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/compiler/native/expand.go b/compiler/native/expand.go index c0173417b..734c597e8 100644 --- a/compiler/native/expand.go +++ b/compiler/native/expand.go @@ -274,6 +274,11 @@ func (c *client) getTemplate(tmpl *yaml.Template, name string) ([]byte, error) { "host": src.Host, }).Tracef("Using authenticated GitHub client to pull template") + // verify private GitHub is actually set up + if c.PrivateGithub == nil { + return nil, fmt.Errorf("unable to fetch template %s: missing credentials", src.Name) + } + // use private (authenticated) github instance to pull from bytes, err = c.PrivateGithub.Template(c.user, src) if err != nil { @@ -307,6 +312,10 @@ func (c *client) getTemplate(tmpl *yaml.Template, name string) ([]byte, error) { "path": src.Name, }).Tracef("Using authenticated GitHub client to pull template") + if c.PrivateGithub == nil { + return nil, fmt.Errorf("unable to fetch template %s: missing credentials", src.Name) + } + // use private (authenticated) github instance to pull from bytes, err = c.PrivateGithub.Template(c.user, src) if err != nil { diff --git a/compiler/native/expand_test.go b/compiler/native/expand_test.go index 0ea845cfc..749bdccb2 100644 --- a/compiler/native/expand_test.go +++ b/compiler/native/expand_test.go @@ -143,13 +143,43 @@ func TestNative_ExpandStages(t *testing.T) { "bar": "test4", } - // run test + // run test -- missing private github compiler, err := New(c) if err != nil { t.Errorf("Creating new compiler returned err: %v", err) } - build, err := compiler.ExpandStages(&yaml.Build{Stages: stages, Services: yaml.ServiceSlice{}, Environment: raw.StringSliceMap{}}, tmpls, new(pipeline.RuleData)) + compiler.PrivateGithub = nil + _, err = compiler.ExpandStages( + &yaml.Build{ + Stages: stages, + Services: yaml.ServiceSlice{}, + Environment: raw.StringSliceMap{}, + }, + tmpls, + new(pipeline.RuleData), + ) + + if err == nil { + t.Errorf("ExpandStages should have returned error with empty PrivateGitHub") + } + + // run test + compiler, err = New(c) + if err != nil { + t.Errorf("Creating new compiler returned err: %v", err) + } + + build, err := compiler.ExpandStages( + &yaml.Build{ + Stages: stages, + Services: yaml.ServiceSlice{}, + Environment: raw.StringSliceMap{}, + }, + tmpls, + new(pipeline.RuleData), + ) + if err != nil { t.Errorf("ExpandStages returned err: %v", err) } From 78eca514b01d626405cc07887eaee1c4e8830352 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Fri, 5 Jan 2024 16:54:21 -0500 Subject: [PATCH 5/5] enhance(secrets)!: use the same allow_events system as repos for secrets (#1033) * init commit * use allowed instead of eventallowed --- api/secret/create.go | 18 ++++++++++++++++-- api/webhook/post.go | 2 +- database/integration_test.go | 3 +++ database/repo/repo_test.go | 3 +++ database/secret/create_test.go | 21 ++++++++++++--------- database/secret/get_org_test.go | 5 +++-- database/secret/get_repo_test.go | 5 +++-- database/secret/get_team_test.go | 5 +++-- database/secret/get_test.go | 5 +++-- database/secret/list_org_test.go | 8 +++++--- database/secret/list_repo_test.go | 8 +++++--- database/secret/list_team_test.go | 8 +++++--- database/secret/list_test.go | 8 +++++--- database/secret/secret_test.go | 27 +++++++++++++++++++++++++++ database/secret/table.go | 2 ++ database/secret/update_test.go | 21 ++++++++++++--------- go.mod | 2 +- go.sum | 4 ++-- secret/native/create_test.go | 4 ++++ secret/native/get_test.go | 1 + secret/native/list_test.go | 2 ++ secret/native/update.go | 5 +++++ secret/native/update_test.go | 2 ++ secret/vault/vault.go | 13 +++++++++++++ secret/vault/vault_test.go | 5 +++++ 25 files changed, 143 insertions(+), 44 deletions(-) diff --git a/api/secret/create.go b/api/secret/create.go index fc2dca67a..8423bc8cb 100644 --- a/api/secret/create.go +++ b/api/secret/create.go @@ -15,6 +15,7 @@ import ( "github.com/go-vela/server/util" "github.com/go-vela/types/constants" "github.com/go-vela/types/library" + "github.com/go-vela/types/library/actions" "github.com/sirupsen/logrus" ) @@ -207,8 +208,21 @@ func CreateSecret(c *gin.Context) { input.SetImages(util.Unique(input.GetImages())) } - if len(input.GetEvents()) > 0 { - input.SetEvents(util.Unique(input.GetEvents())) + // default event set for secrets + if input.GetAllowEvents().ToDatabase() == 0 { + e := new(library.Events) + + push := new(actions.Push) + push.SetBranch(true) + push.SetTag(true) + + deploy := new(actions.Deploy) + deploy.SetCreated(true) + + e.SetPush(push) + e.SetDeployment(deploy) + + input.SetAllowEvents(e) } if len(input.GetEvents()) == 0 { diff --git a/api/webhook/post.go b/api/webhook/post.go index 7d44689fe..4564b97f1 100644 --- a/api/webhook/post.go +++ b/api/webhook/post.go @@ -258,7 +258,7 @@ func PostWebhook(c *gin.Context) { } // verify the build has a valid event and the repo allows that event type - if !repo.EventAllowed(b.GetEvent(), b.GetEventAction()) { + if !repo.GetAllowEvents().Allowed(b.GetEvent(), b.GetEventAction()) { var actionErr string if len(b.GetEventAction()) > 0 { actionErr = ":" + b.GetEventAction() diff --git a/database/integration_test.go b/database/integration_test.go index 49b844fda..4d3b5104a 100644 --- a/database/integration_test.go +++ b/database/integration_test.go @@ -2311,6 +2311,7 @@ func newResources() *Resources { secretOrg.SetType("org") secretOrg.SetImages([]string{"alpine"}) secretOrg.SetEvents([]string{"push", "tag", "deployment"}) + secretOrg.SetAllowEvents(library.NewEventsFromMask(1)) secretOrg.SetAllowCommand(true) secretOrg.SetCreatedAt(time.Now().UTC().Unix()) secretOrg.SetCreatedBy("octocat") @@ -2327,6 +2328,7 @@ func newResources() *Resources { secretRepo.SetType("repo") secretRepo.SetImages([]string{"alpine"}) secretRepo.SetEvents([]string{"push", "tag", "deployment"}) + secretRepo.SetAllowEvents(library.NewEventsFromMask(1)) secretRepo.SetAllowCommand(true) secretRepo.SetCreatedAt(time.Now().UTC().Unix()) secretRepo.SetCreatedBy("octocat") @@ -2344,6 +2346,7 @@ func newResources() *Resources { secretShared.SetImages([]string{"alpine"}) secretShared.SetEvents([]string{"push", "tag", "deployment"}) secretShared.SetAllowCommand(true) + secretShared.SetAllowEvents(library.NewEventsFromMask(1)) secretShared.SetCreatedAt(time.Now().UTC().Unix()) secretShared.SetCreatedBy("octocat") secretShared.SetUpdatedAt(time.Now().Add(time.Hour * 1).UTC().Unix()) diff --git a/database/repo/repo_test.go b/database/repo/repo_test.go index 3316bb6bf..5e70956d7 100644 --- a/database/repo/repo_test.go +++ b/database/repo/repo_test.go @@ -222,6 +222,9 @@ func testEvents() *library.Events { Created: new(bool), Edited: new(bool), }, + Schedule: &actions.Schedule{ + Run: new(bool), + }, } } diff --git a/database/secret/create_test.go b/database/secret/create_test.go index 2d86fbb0c..b5d0c3ba5 100644 --- a/database/secret/create_test.go +++ b/database/secret/create_test.go @@ -24,6 +24,7 @@ func TestSecret_Engine_CreateSecret(t *testing.T) { _secretRepo.SetCreatedBy("user") _secretRepo.SetUpdatedAt(1) _secretRepo.SetUpdatedBy("user2") + _secretRepo.SetAllowEvents(library.NewEventsFromMask(1)) _secretOrg := testSecret() _secretOrg.SetID(2) @@ -36,6 +37,7 @@ func TestSecret_Engine_CreateSecret(t *testing.T) { _secretOrg.SetCreatedBy("user") _secretOrg.SetUpdatedAt(1) _secretOrg.SetUpdatedBy("user2") + _secretOrg.SetAllowEvents(library.NewEventsFromMask(3)) _secretShared := testSecret() _secretShared.SetID(3) @@ -48,6 +50,7 @@ func TestSecret_Engine_CreateSecret(t *testing.T) { _secretShared.SetCreatedBy("user") _secretShared.SetUpdatedAt(1) _secretShared.SetUpdatedBy("user2") + _secretShared.SetAllowEvents(library.NewEventsFromMask(1)) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -57,23 +60,23 @@ func TestSecret_Engine_CreateSecret(t *testing.T) { // ensure the mock expects the repo secrets query _mock.ExpectQuery(`INSERT INTO "secrets" -("org","repo","team","name","value","type","images","events","allow_command","created_at","created_by","updated_at","updated_by","id") -VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14) RETURNING "id"`). - WithArgs("foo", "bar", nil, "baz", AnyArgument{}, "repo", nil, nil, false, 1, "user", 1, "user2", 1). +("org","repo","team","name","value","type","images","events","allow_events","allow_command","created_at","created_by","updated_at","updated_by","id") +VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15) RETURNING "id"`). + WithArgs("foo", "bar", nil, "baz", AnyArgument{}, "repo", nil, nil, 1, false, 1, "user", 1, "user2", 1). WillReturnRows(_rows) // ensure the mock expects the org secrets query _mock.ExpectQuery(`INSERT INTO "secrets" -("org","repo","team","name","value","type","images","events","allow_command","created_at","created_by","updated_at","updated_by","id") -VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14) RETURNING "id"`). - WithArgs("foo", "*", nil, "bar", AnyArgument{}, "org", nil, nil, false, 1, "user", 1, "user2", 2). +("org","repo","team","name","value","type","images","events","allow_events","allow_command","created_at","created_by","updated_at","updated_by","id") +VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15) RETURNING "id"`). + WithArgs("foo", "*", nil, "bar", AnyArgument{}, "org", nil, nil, 3, false, 1, "user", 1, "user2", 2). WillReturnRows(_rows) // ensure the mock expects the shared secrets query _mock.ExpectQuery(`INSERT INTO "secrets" -("org","repo","team","name","value","type","images","events","allow_command","created_at","created_by","updated_at","updated_by","id") -VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14) RETURNING "id"`). - WithArgs("foo", nil, "bar", "baz", AnyArgument{}, "shared", nil, nil, false, 1, "user", 1, "user2", 3). +("org","repo","team","name","value","type","images","events","allow_events","allow_command","created_at","created_by","updated_at","updated_by","id") +VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15) RETURNING "id"`). + WithArgs("foo", nil, "bar", "baz", AnyArgument{}, "shared", nil, nil, 1, false, 1, "user", 1, "user2", 3). WillReturnRows(_rows) _sqlite := testSqlite(t) diff --git a/database/secret/get_org_test.go b/database/secret/get_org_test.go index fbdaf1f17..71b65bf09 100644 --- a/database/secret/get_org_test.go +++ b/database/secret/get_org_test.go @@ -25,14 +25,15 @@ func TestSecret_Engine_GetSecretForOrg(t *testing.T) { _secret.SetCreatedBy("user") _secret.SetUpdatedAt(1) _secret.SetUpdatedBy("user2") + _secret.SetAllowEvents(library.NewEventsFromMask(1)) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() // create expected result in mock _rows := sqlmock.NewRows( - []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). - AddRow(1, "org", "foo", "*", "", "baz", "bar", nil, nil, false, 1, "user", 1, "user2") + []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). + AddRow(1, "org", "foo", "*", "", "baz", "bar", nil, nil, 1, false, 1, "user", 1, "user2") // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "secrets" WHERE type = $1 AND org = $2 AND name = $3 LIMIT 1`). diff --git a/database/secret/get_repo_test.go b/database/secret/get_repo_test.go index 5f8678546..b3136ac1b 100644 --- a/database/secret/get_repo_test.go +++ b/database/secret/get_repo_test.go @@ -35,14 +35,15 @@ func TestSecret_Engine_GetSecretForRepo(t *testing.T) { _secret.SetCreatedBy("user") _secret.SetUpdatedAt(1) _secret.SetUpdatedBy("user2") + _secret.SetAllowEvents(library.NewEventsFromMask(1)) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() // create expected result in mock _rows := sqlmock.NewRows( - []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). - AddRow(1, "repo", "foo", "bar", "", "baz", "foob", nil, nil, false, 1, "user", 1, "user2") + []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). + AddRow(1, "repo", "foo", "bar", "", "baz", "foob", nil, nil, 1, false, 1, "user", 1, "user2") // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "secrets" WHERE type = $1 AND org = $2 AND repo = $3 AND name = $4 LIMIT 1`). diff --git a/database/secret/get_team_test.go b/database/secret/get_team_test.go index 30854550e..696cd2a37 100644 --- a/database/secret/get_team_test.go +++ b/database/secret/get_team_test.go @@ -25,14 +25,15 @@ func TestSecret_Engine_GetSecretForTeam(t *testing.T) { _secret.SetCreatedBy("user") _secret.SetUpdatedAt(1) _secret.SetUpdatedBy("user2") + _secret.SetAllowEvents(library.NewEventsFromMask(1)) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() // create expected result in mock _rows := sqlmock.NewRows( - []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). - AddRow(1, "shared", "foo", "", "bar", "baz", "foob", nil, nil, false, 1, "user", 1, "user2") + []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). + AddRow(1, "shared", "foo", "", "bar", "baz", "foob", nil, nil, 1, false, 1, "user", 1, "user2") // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "secrets" WHERE type = $1 AND org = $2 AND team = $3 AND name = $4 LIMIT 1`). diff --git a/database/secret/get_test.go b/database/secret/get_test.go index f57232a02..c44175f86 100644 --- a/database/secret/get_test.go +++ b/database/secret/get_test.go @@ -24,14 +24,15 @@ func TestSecret_Engine_GetSecret(t *testing.T) { _secret.SetCreatedBy("user") _secret.SetUpdatedAt(1) _secret.SetUpdatedBy("user2") + _secret.SetAllowEvents(library.NewEventsFromMask(1)) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() // create expected result in mock _rows := sqlmock.NewRows( - []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). - AddRow(1, "repo", "foo", "bar", "", "baz", "foob", nil, nil, false, 1, "user", 1, "user2") + []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). + AddRow(1, "repo", "foo", "bar", "", "baz", "foob", nil, nil, 1, false, 1, "user", 1, "user2") // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "secrets" WHERE id = $1 LIMIT 1`).WithArgs(1).WillReturnRows(_rows) diff --git a/database/secret/list_org_test.go b/database/secret/list_org_test.go index ecde40520..11967cd7d 100644 --- a/database/secret/list_org_test.go +++ b/database/secret/list_org_test.go @@ -25,6 +25,7 @@ func TestSecret_Engine_ListSecretsForOrg(t *testing.T) { _secretOne.SetCreatedBy("user") _secretOne.SetUpdatedAt(1) _secretOne.SetUpdatedBy("user2") + _secretOne.SetAllowEvents(library.NewEventsFromMask(1)) _secretTwo := testSecret() _secretTwo.SetID(2) @@ -37,6 +38,7 @@ func TestSecret_Engine_ListSecretsForOrg(t *testing.T) { _secretTwo.SetCreatedBy("user") _secretTwo.SetUpdatedAt(1) _secretTwo.SetUpdatedBy("user2") + _secretTwo.SetAllowEvents(library.NewEventsFromMask(1)) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -50,9 +52,9 @@ func TestSecret_Engine_ListSecretsForOrg(t *testing.T) { // create expected name query result in mock _rows = sqlmock.NewRows( - []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). - AddRow(2, "org", "foo", "*", "", "bar", "baz", nil, nil, false, 1, "user", 1, "user2"). - AddRow(1, "org", "foo", "*", "", "baz", "bar", nil, nil, false, 1, "user", 1, "user2") + []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). + AddRow(2, "org", "foo", "*", "", "bar", "baz", nil, nil, 1, false, 1, "user", 1, "user2"). + AddRow(1, "org", "foo", "*", "", "baz", "bar", nil, nil, 1, false, 1, "user", 1, "user2") // ensure the mock expects the name query _mock.ExpectQuery(`SELECT * FROM "secrets" WHERE type = $1 AND org = $2 ORDER BY id DESC LIMIT 10`). diff --git a/database/secret/list_repo_test.go b/database/secret/list_repo_test.go index dece5e57e..77d2d930f 100644 --- a/database/secret/list_repo_test.go +++ b/database/secret/list_repo_test.go @@ -36,6 +36,7 @@ func TestSecret_Engine_ListSecretsForRepo(t *testing.T) { _secretOne.SetCreatedBy("user") _secretOne.SetUpdatedAt(1) _secretOne.SetUpdatedBy("user2") + _secretOne.SetAllowEvents(library.NewEventsFromMask(1)) _secretTwo := testSecret() _secretTwo.SetID(2) @@ -48,6 +49,7 @@ func TestSecret_Engine_ListSecretsForRepo(t *testing.T) { _secretTwo.SetCreatedBy("user") _secretTwo.SetUpdatedAt(1) _secretTwo.SetUpdatedBy("user2") + _secretTwo.SetAllowEvents(library.NewEventsFromMask(1)) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -61,9 +63,9 @@ func TestSecret_Engine_ListSecretsForRepo(t *testing.T) { // create expected name query result in mock _rows = sqlmock.NewRows( - []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). - AddRow(2, "repo", "foo", "bar", "", "foob", "baz", nil, nil, false, 1, "user", 1, "user2"). - AddRow(1, "repo", "foo", "bar", "", "baz", "foob", nil, nil, false, 1, "user", 1, "user2") + []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). + AddRow(2, "repo", "foo", "bar", "", "foob", "baz", nil, nil, 1, false, 1, "user", 1, "user2"). + AddRow(1, "repo", "foo", "bar", "", "baz", "foob", nil, nil, 1, false, 1, "user", 1, "user2") // ensure the mock expects the name query _mock.ExpectQuery(`SELECT * FROM "secrets" WHERE type = $1 AND org = $2 AND repo = $3 ORDER BY id DESC LIMIT 10`). diff --git a/database/secret/list_team_test.go b/database/secret/list_team_test.go index 40d311805..071ac371b 100644 --- a/database/secret/list_team_test.go +++ b/database/secret/list_team_test.go @@ -26,6 +26,7 @@ func TestSecret_Engine_ListSecretsForTeam(t *testing.T) { _secretOne.SetCreatedBy("user") _secretOne.SetUpdatedAt(1) _secretOne.SetUpdatedBy("user2") + _secretOne.SetAllowEvents(library.NewEventsFromMask(1)) _secretTwo := testSecret() _secretTwo.SetID(2) @@ -38,6 +39,7 @@ func TestSecret_Engine_ListSecretsForTeam(t *testing.T) { _secretTwo.SetCreatedBy("user") _secretTwo.SetUpdatedAt(1) _secretTwo.SetUpdatedBy("user2") + _secretTwo.SetAllowEvents(library.NewEventsFromMask(1)) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -51,9 +53,9 @@ func TestSecret_Engine_ListSecretsForTeam(t *testing.T) { // create expected name query result in mock _rows = sqlmock.NewRows( - []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). - AddRow(2, "shared", "foo", "", "bar", "foob", "baz", nil, nil, false, 1, "user", 1, "user2"). - AddRow(1, "shared", "foo", "", "bar", "baz", "foob", nil, nil, false, 1, "user", 1, "user2") + []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). + AddRow(2, "shared", "foo", "", "bar", "foob", "baz", nil, nil, 1, false, 1, "user", 1, "user2"). + AddRow(1, "shared", "foo", "", "bar", "baz", "foob", nil, nil, 1, false, 1, "user", 1, "user2") // ensure the mock expects the name query _mock.ExpectQuery(`SELECT * FROM "secrets" WHERE type = $1 AND org = $2 AND team = $3 ORDER BY id DESC LIMIT 10`). diff --git a/database/secret/list_test.go b/database/secret/list_test.go index 22f8ea11f..8077637ee 100644 --- a/database/secret/list_test.go +++ b/database/secret/list_test.go @@ -24,6 +24,7 @@ func TestSecret_Engine_ListSecrets(t *testing.T) { _secretOne.SetCreatedBy("user") _secretOne.SetUpdatedAt(1) _secretOne.SetUpdatedBy("user2") + _secretOne.SetAllowEvents(library.NewEventsFromMask(1)) _secretTwo := testSecret() _secretTwo.SetID(2) @@ -36,6 +37,7 @@ func TestSecret_Engine_ListSecrets(t *testing.T) { _secretTwo.SetCreatedBy("user") _secretTwo.SetUpdatedAt(1) _secretTwo.SetUpdatedBy("user2") + _secretTwo.SetAllowEvents(library.NewEventsFromMask(1)) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -48,9 +50,9 @@ func TestSecret_Engine_ListSecrets(t *testing.T) { // create expected result in mock _rows = sqlmock.NewRows( - []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). - AddRow(1, "repo", "foo", "bar", "", "baz", "foob", nil, nil, false, 1, "user", 1, "user2"). - AddRow(2, "repo", "foo", "bar", "", "foob", "baz", nil, nil, false, 1, "user", 1, "user2") + []string{"id", "type", "org", "repo", "team", "name", "value", "images", "events", "allow_events", "allow_command", "created_at", "created_by", "updated_at", "updated_by"}). + AddRow(1, "repo", "foo", "bar", "", "baz", "foob", nil, nil, 1, false, 1, "user", 1, "user2"). + AddRow(2, "repo", "foo", "bar", "", "foob", "baz", nil, nil, 1, false, 1, "user", 1, "user2") // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "secrets"`).WillReturnRows(_rows) diff --git a/database/secret/secret_test.go b/database/secret/secret_test.go index 5a0e25998..3c6dfc0bd 100644 --- a/database/secret/secret_test.go +++ b/database/secret/secret_test.go @@ -10,6 +10,7 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/go-vela/types/library" + "github.com/go-vela/types/library/actions" "github.com/sirupsen/logrus" "gorm.io/driver/postgres" @@ -218,6 +219,7 @@ func testSecret() *library.Secret { Type: new(string), Images: new([]string), Events: new([]string), + AllowEvents: testEvents(), AllowCommand: new(bool), CreatedAt: new(int64), CreatedBy: new(string), @@ -226,6 +228,31 @@ func testSecret() *library.Secret { } } +func testEvents() *library.Events { + return &library.Events{ + Push: &actions.Push{ + Branch: new(bool), + Tag: new(bool), + }, + PullRequest: &actions.Pull{ + Opened: new(bool), + Edited: new(bool), + Synchronize: new(bool), + Reopened: new(bool), + }, + Deployment: &actions.Deploy{ + Created: new(bool), + }, + Comment: &actions.Comment{ + Created: new(bool), + Edited: new(bool), + }, + Schedule: &actions.Schedule{ + Run: new(bool), + }, + } +} + // This will be used with the github.com/DATA-DOG/go-sqlmock library to compare values // that are otherwise not easily compared. These typically would be values generated // before adding or updating them in the database. diff --git a/database/secret/table.go b/database/secret/table.go index ed10fbdb4..f3d42ea46 100644 --- a/database/secret/table.go +++ b/database/secret/table.go @@ -23,6 +23,7 @@ secrets ( value BYTEA, images VARCHAR(1000), events VARCHAR(1000), + allow_events INTEGER, allow_command BOOLEAN, created_at INTEGER, created_by VARCHAR(250), @@ -47,6 +48,7 @@ secrets ( value TEXT, images TEXT, events TEXT, + allow_events INTEGER, allow_command BOOLEAN, created_at INTEGER, created_by TEXT, diff --git a/database/secret/update_test.go b/database/secret/update_test.go index 74576535e..f26ddaa24 100644 --- a/database/secret/update_test.go +++ b/database/secret/update_test.go @@ -24,6 +24,7 @@ func TestSecret_Engine_UpdateSecret(t *testing.T) { _secretRepo.SetCreatedBy("user") _secretRepo.SetUpdatedAt(1) _secretRepo.SetUpdatedBy("user2") + _secretRepo.SetAllowEvents(library.NewEventsFromMask(1)) _secretOrg := testSecret() _secretOrg.SetID(2) @@ -36,6 +37,7 @@ func TestSecret_Engine_UpdateSecret(t *testing.T) { _secretOrg.SetCreatedBy("user") _secretOrg.SetUpdatedAt(1) _secretOrg.SetUpdatedBy("user2") + _secretOrg.SetAllowEvents(library.NewEventsFromMask(1)) _secretShared := testSecret() _secretShared.SetID(3) @@ -48,29 +50,30 @@ func TestSecret_Engine_UpdateSecret(t *testing.T) { _secretShared.SetCreatedBy("user") _secretShared.SetUpdatedAt(1) _secretShared.SetUpdatedBy("user2") + _secretShared.SetAllowEvents(library.NewEventsFromMask(1)) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() // ensure the mock expects the repo query _mock.ExpectExec(`UPDATE "secrets" -SET "org"=$1,"repo"=$2,"team"=$3,"name"=$4,"value"=$5,"type"=$6,"images"=$7,"events"=$8,"allow_command"=$9,"created_at"=$10,"created_by"=$11,"updated_at"=$12,"updated_by"=$13 -WHERE "id" = $14`). - WithArgs("foo", "bar", nil, "baz", AnyArgument{}, "repo", nil, nil, false, 1, "user", AnyArgument{}, "user2", 1). +SET "org"=$1,"repo"=$2,"team"=$3,"name"=$4,"value"=$5,"type"=$6,"images"=$7,"events"=$8,"allow_events"=$9,"allow_command"=$10,"created_at"=$11,"created_by"=$12,"updated_at"=$13,"updated_by"=$14 +WHERE "id" = $15`). + WithArgs("foo", "bar", nil, "baz", AnyArgument{}, "repo", nil, nil, 1, false, 1, "user", AnyArgument{}, "user2", 1). WillReturnResult(sqlmock.NewResult(1, 1)) // ensure the mock expects the org query _mock.ExpectExec(`UPDATE "secrets" -SET "org"=$1,"repo"=$2,"team"=$3,"name"=$4,"value"=$5,"type"=$6,"images"=$7,"events"=$8,"allow_command"=$9,"created_at"=$10,"created_by"=$11,"updated_at"=$12,"updated_by"=$13 -WHERE "id" = $14`). - WithArgs("foo", "*", nil, "bar", AnyArgument{}, "org", nil, nil, false, 1, "user", AnyArgument{}, "user2", 2). +SET "org"=$1,"repo"=$2,"team"=$3,"name"=$4,"value"=$5,"type"=$6,"images"=$7,"events"=$8,"allow_events"=$9,"allow_command"=$10,"created_at"=$11,"created_by"=$12,"updated_at"=$13,"updated_by"=$14 +WHERE "id" = $15`). + WithArgs("foo", "*", nil, "bar", AnyArgument{}, "org", nil, nil, 1, false, 1, "user", AnyArgument{}, "user2", 2). WillReturnResult(sqlmock.NewResult(1, 1)) // ensure the mock expects the shared query _mock.ExpectExec(`UPDATE "secrets" -SET "org"=$1,"repo"=$2,"team"=$3,"name"=$4,"value"=$5,"type"=$6,"images"=$7,"events"=$8,"allow_command"=$9,"created_at"=$10,"created_by"=$11,"updated_at"=$12,"updated_by"=$13 -WHERE "id" = $14`). - WithArgs("foo", nil, "bar", "baz", AnyArgument{}, "shared", nil, nil, false, 1, "user", NowTimestamp{}, "user2", 3). +SET "org"=$1,"repo"=$2,"team"=$3,"name"=$4,"value"=$5,"type"=$6,"images"=$7,"events"=$8,"allow_events"=$9,"allow_command"=$10,"created_at"=$11,"created_by"=$12,"updated_at"=$13,"updated_by"=$14 +WHERE "id" = $15`). + WithArgs("foo", nil, "bar", "baz", AnyArgument{}, "shared", nil, nil, 1, false, 1, "user", NowTimestamp{}, "user2", 3). WillReturnResult(sqlmock.NewResult(1, 1)) _sqlite := testSqlite(t) diff --git a/go.mod b/go.mod index 6ec5cae90..a1fe54c69 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/drone/envsubst v1.0.3 github.com/gin-gonic/gin v1.9.1 github.com/go-playground/assert/v2 v2.2.0 - github.com/go-vela/types v0.22.1-0.20231222174844-26e54c869418 + github.com/go-vela/types v0.22.1-0.20240105182535-a91bd54636bc github.com/golang-jwt/jwt/v5 v5.1.0 github.com/google/go-cmp v0.6.0 github.com/google/go-github/v56 v56.0.0 diff --git a/go.sum b/go.sum index e17adfcee..e583a0a5c 100644 --- a/go.sum +++ b/go.sum @@ -141,8 +141,8 @@ github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-vela/types v0.22.1-0.20231222174844-26e54c869418 h1:IzkCTpeEVs0r73mIwDPDdVxBtaUr/oxfJcVjUnNCF6g= -github.com/go-vela/types v0.22.1-0.20231222174844-26e54c869418/go.mod h1:cax3mW1kVz/ioI8qltZE+wl9rOLgOPdwBIvCooL09e4= +github.com/go-vela/types v0.22.1-0.20240105182535-a91bd54636bc h1:S59SXYfqFTJeuIBdwoKQE/oFRPAFU/LuzHCq3mRXe3w= +github.com/go-vela/types v0.22.1-0.20240105182535-a91bd54636bc/go.mod h1:cax3mW1kVz/ioI8qltZE+wl9rOLgOPdwBIvCooL09e4= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= diff --git a/secret/native/create_test.go b/secret/native/create_test.go index dd1b6b0ff..0463e33ff 100644 --- a/secret/native/create_test.go +++ b/secret/native/create_test.go @@ -23,6 +23,7 @@ func TestNative_Create_Org(t *testing.T) { want.SetType("org") want.SetImages([]string{"foo", "bar"}) want.SetEvents([]string{"foo", "bar"}) + want.SetAllowEvents(library.NewEventsFromMask(1)) want.SetAllowCommand(false) want.SetCreatedAt(1) want.SetCreatedBy("user") @@ -70,6 +71,7 @@ func TestNative_Create_Repo(t *testing.T) { want.SetType("repo") want.SetImages([]string{"foo", "bar"}) want.SetEvents([]string{"foo", "bar"}) + want.SetAllowEvents(library.NewEventsFromMask(1)) want.SetAllowCommand(false) want.SetCreatedAt(1) want.SetCreatedBy("user") @@ -117,6 +119,7 @@ func TestNative_Create_Shared(t *testing.T) { want.SetType("shared") want.SetImages([]string{"foo", "bar"}) want.SetEvents([]string{"foo", "bar"}) + want.SetAllowEvents(library.NewEventsFromMask(1)) want.SetAllowCommand(false) want.SetCreatedAt(1) want.SetCreatedBy("user") @@ -164,6 +167,7 @@ func TestNative_Create_Invalid(t *testing.T) { sec.SetType("invalid") sec.SetImages([]string{"foo", "bar"}) sec.SetEvents([]string{"foo", "bar"}) + sec.SetAllowEvents(library.NewEventsFromMask(1)) sec.SetAllowCommand(false) sec.SetCreatedAt(1) sec.SetCreatedBy("user") diff --git a/secret/native/get_test.go b/secret/native/get_test.go index 8272c9e99..a835e1143 100644 --- a/secret/native/get_test.go +++ b/secret/native/get_test.go @@ -23,6 +23,7 @@ func TestNative_Get(t *testing.T) { want.SetType("repo") want.SetImages([]string{"foo", "bar"}) want.SetEvents([]string{"foo", "bar"}) + want.SetAllowEvents(library.NewEventsFromMask(1)) want.SetAllowCommand(false) want.SetCreatedAt(1) want.SetCreatedBy("user") diff --git a/secret/native/list_test.go b/secret/native/list_test.go index f7bba5b81..41dbf1691 100644 --- a/secret/native/list_test.go +++ b/secret/native/list_test.go @@ -23,6 +23,7 @@ func TestNative_List(t *testing.T) { sOne.SetType("repo") sOne.SetImages([]string{"foo", "bar"}) sOne.SetEvents([]string{"foo", "bar"}) + sOne.SetAllowEvents(library.NewEventsFromMask(1)) sOne.SetAllowCommand(false) sOne.SetCreatedAt(1) sOne.SetCreatedBy("user") @@ -39,6 +40,7 @@ func TestNative_List(t *testing.T) { sTwo.SetType("repo") sTwo.SetImages([]string{"foo", "bar"}) sTwo.SetEvents([]string{"foo", "bar"}) + sTwo.SetAllowEvents(library.NewEventsFromMask(1)) sTwo.SetAllowCommand(false) sTwo.SetCreatedAt(1) sTwo.SetCreatedBy("user") diff --git a/secret/native/update.go b/secret/native/update.go index cd928de8c..ece04c5b9 100644 --- a/secret/native/update.go +++ b/secret/native/update.go @@ -24,6 +24,11 @@ func (c *client) Update(ctx context.Context, sType, org, name string, s *library secret.SetEvents(s.GetEvents()) } + // update allow events if set + if s.GetAllowEvents().ToDatabase() > 0 { + secret.SetAllowEvents(s.GetAllowEvents()) + } + // update the images if set if s.Images != nil { secret.SetImages(s.GetImages()) diff --git a/secret/native/update_test.go b/secret/native/update_test.go index 3b4e13172..b5dc82c97 100644 --- a/secret/native/update_test.go +++ b/secret/native/update_test.go @@ -24,6 +24,7 @@ func TestNative_Update(t *testing.T) { original.SetType("repo") original.SetImages([]string{"foo", "baz"}) original.SetEvents([]string{"foob", "bar"}) + original.SetAllowEvents(library.NewEventsFromMask(1)) original.SetAllowCommand(true) original.SetCreatedAt(1) original.SetCreatedBy("user") @@ -40,6 +41,7 @@ func TestNative_Update(t *testing.T) { want.SetType("repo") want.SetImages([]string{"foo", "bar"}) want.SetEvents([]string{"foo", "bar"}) + want.SetAllowEvents(library.NewEventsFromMask(3)) want.SetAllowCommand(false) want.SetCreatedAt(1) want.SetCreatedBy("user") diff --git a/secret/vault/vault.go b/secret/vault/vault.go index c1b0c9508..977ea60f9 100644 --- a/secret/vault/vault.go +++ b/secret/vault/vault.go @@ -156,6 +156,14 @@ func secretFromVault(vault *api.Secret) *library.Secret { } } + v, ok = data["allow_events"] + if ok { + mask, ok := v.(int64) + if ok { + s.SetAllowEvents(library.NewEventsFromMask(mask)) + } + } + // set images if found in Vault secret v, ok = data["images"] if ok { @@ -283,6 +291,11 @@ func vaultFromSecret(s *library.Secret) *api.Secret { vault.Data["events"] = s.GetEvents() } + // set allow events to mask + if s.GetAllowEvents().ToDatabase() != 0 { + vault.Data["allow_events"] = s.GetAllowEvents().ToDatabase() + } + // set images if found in Vela secret if len(s.GetImages()) > 0 { vault.Data["images"] = s.GetImages() diff --git a/secret/vault/vault_test.go b/secret/vault/vault_test.go index 1e61c7941..9cee893d9 100644 --- a/secret/vault/vault_test.go +++ b/secret/vault/vault_test.go @@ -95,6 +95,7 @@ func TestVault_secretFromVault(t *testing.T) { inputV1 := &api.Secret{ Data: map[string]interface{}{ "events": []interface{}{"foo", "bar"}, + "allow_events": int64(1), "images": []interface{}{"foo", "bar"}, "name": "bar", "org": "foo", @@ -114,6 +115,7 @@ func TestVault_secretFromVault(t *testing.T) { Data: map[string]interface{}{ "data": map[string]interface{}{ "events": []interface{}{"foo", "bar"}, + "allow_events": int64(1), "images": []interface{}{"foo", "bar"}, "name": "bar", "org": "foo", @@ -138,6 +140,7 @@ func TestVault_secretFromVault(t *testing.T) { want.SetValue("baz") want.SetType("org") want.SetEvents([]string{"foo", "bar"}) + want.SetAllowEvents(library.NewEventsFromMask(1)) want.SetImages([]string{"foo", "bar"}) want.SetAllowCommand(true) want.SetCreatedAt(1563474077) @@ -178,6 +181,7 @@ func TestVault_vaultFromSecret(t *testing.T) { s.SetValue("baz") s.SetType("org") s.SetEvents([]string{"foo", "bar"}) + s.SetAllowEvents(library.NewEventsFromMask(1)) s.SetImages([]string{"foo", "bar"}) s.SetAllowCommand(true) s.SetCreatedAt(1563474077) @@ -188,6 +192,7 @@ func TestVault_vaultFromSecret(t *testing.T) { want := &api.Secret{ Data: map[string]interface{}{ "events": []string{"foo", "bar"}, + "allow_events": int64(1), "images": []string{"foo", "bar"}, "name": "bar", "org": "foo",