mirror of
https://github.com/harness/drone.git
synced 2025-05-21 11:29:52 +08:00
add pipelines and executions handlers to gitness
This commit is contained in:
parent
b5a15b5ce1
commit
8cdcecb56f
@ -16,7 +16,9 @@ import (
|
||||
gitrpcserver "github.com/harness/gitness/gitrpc/server"
|
||||
gitrpccron "github.com/harness/gitness/gitrpc/server/cron"
|
||||
checkcontroller "github.com/harness/gitness/internal/api/controller/check"
|
||||
"github.com/harness/gitness/internal/api/controller/execution"
|
||||
"github.com/harness/gitness/internal/api/controller/githook"
|
||||
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||
"github.com/harness/gitness/internal/api/controller/principal"
|
||||
"github.com/harness/gitness/internal/api/controller/pullreq"
|
||||
"github.com/harness/gitness/internal/api/controller/repo"
|
||||
@ -90,6 +92,8 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
|
||||
codecomments.WireSet,
|
||||
gitrpccron.WireSet,
|
||||
checkcontroller.WireSet,
|
||||
execution.WireSet,
|
||||
pipeline.WireSet,
|
||||
)
|
||||
return &cliserver.System{}, nil
|
||||
}
|
||||
|
@ -14,7 +14,9 @@ import (
|
||||
server3 "github.com/harness/gitness/gitrpc/server"
|
||||
"github.com/harness/gitness/gitrpc/server/cron"
|
||||
check2 "github.com/harness/gitness/internal/api/controller/check"
|
||||
"github.com/harness/gitness/internal/api/controller/execution"
|
||||
"github.com/harness/gitness/internal/api/controller/githook"
|
||||
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||
"github.com/harness/gitness/internal/api/controller/principal"
|
||||
"github.com/harness/gitness/internal/api/controller/pullreq"
|
||||
"github.com/harness/gitness/internal/api/controller/repo"
|
||||
@ -84,7 +86,11 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
||||
return nil, err
|
||||
}
|
||||
repoController := repo.ProvideController(config, db, provider, pathUID, authorizer, pathStore, repoStore, spaceStore, principalStore, gitrpcInterface)
|
||||
executionStore := database.ProvideExecutionStore(db)
|
||||
executionController := execution.ProvideController(db, authorizer, executionStore, repoStore, spaceStore)
|
||||
spaceController := space.ProvideController(db, provider, pathUID, authorizer, pathStore, spaceStore, repoStore, principalStore, repoController, membershipStore)
|
||||
pipelineStore := database.ProvidePipelineStore(db)
|
||||
pipelineController := pipeline.ProvideController(db, pathUID, pathStore, repoStore, authorizer, pipelineStore, spaceStore)
|
||||
pullReqStore := database.ProvidePullReqStore(db, principalInfoCache)
|
||||
pullReqActivityStore := database.ProvidePullReqActivityStore(db, principalInfoCache)
|
||||
codeCommentView := database.ProvideCodeCommentView(db)
|
||||
@ -138,7 +144,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
||||
principalController := principal.ProvideController(principalStore)
|
||||
checkStore := database.ProvideCheckStore(db, principalInfoCache)
|
||||
checkController := check2.ProvideController(db, authorizer, repoStore, checkStore, gitrpcInterface)
|
||||
apiHandler := router.ProvideAPIHandler(config, authenticator, repoController, spaceController, pullreqController, webhookController, githookController, serviceaccountController, controller, principalController, checkController)
|
||||
apiHandler := router.ProvideAPIHandler(config, authenticator, repoController, executionController, spaceController, pipelineController, pullreqController, webhookController, githookController, serviceaccountController, controller, principalController, checkController)
|
||||
gitHandler := router.ProvideGitHandler(config, provider, repoStore, authenticator, authorizer, gitrpcInterface)
|
||||
webHandler := router.ProvideWebHandler(config)
|
||||
routerRouter := router.ProvideRouter(config, apiHandler, gitHandler, webHandler)
|
||||
|
31
internal/api/controller/execution/controller.go
Normal file
31
internal/api/controller/execution/controller.go
Normal file
@ -0,0 +1,31 @@
|
||||
package execution
|
||||
|
||||
import (
|
||||
"github.com/harness/gitness/internal/auth/authz"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
db *sqlx.DB
|
||||
authorizer authz.Authorizer
|
||||
executionStore store.ExecutionStore
|
||||
repoStore store.RepoStore
|
||||
spaceStore store.SpaceStore
|
||||
}
|
||||
|
||||
func NewController(
|
||||
db *sqlx.DB,
|
||||
authorizer authz.Authorizer,
|
||||
executionStore store.ExecutionStore,
|
||||
repoStore store.RepoStore,
|
||||
spaceStore store.SpaceStore,
|
||||
) *Controller {
|
||||
return &Controller{
|
||||
db: db,
|
||||
authorizer: authorizer,
|
||||
executionStore: executionStore,
|
||||
repoStore: repoStore,
|
||||
spaceStore: spaceStore,
|
||||
}
|
||||
}
|
24
internal/api/controller/execution/wire.go
Normal file
24
internal/api/controller/execution/wire.go
Normal file
@ -0,0 +1,24 @@
|
||||
package execution
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"github.com/harness/gitness/internal/auth/authz"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
// WireSet provides a wire set for this package.
|
||||
var WireSet = wire.NewSet(
|
||||
ProvideController,
|
||||
)
|
||||
|
||||
func ProvideController(db *sqlx.DB,
|
||||
authorizer authz.Authorizer,
|
||||
executionStore store.ExecutionStore,
|
||||
repoStore store.RepoStore,
|
||||
spaceStore store.SpaceStore,
|
||||
) *Controller {
|
||||
return NewController(db, authorizer, executionStore,
|
||||
repoStore,
|
||||
spaceStore)
|
||||
}
|
39
internal/api/controller/pipeline/controller.go
Normal file
39
internal/api/controller/pipeline/controller.go
Normal file
@ -0,0 +1,39 @@
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"github.com/harness/gitness/internal/auth/authz"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types/check"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
defaultBranch string
|
||||
db *sqlx.DB
|
||||
uidCheck check.PathUID
|
||||
pathStore store.PathStore
|
||||
repoStore store.RepoStore
|
||||
authorizer authz.Authorizer
|
||||
pipelineStore store.PipelineStore
|
||||
spaceStore store.SpaceStore
|
||||
}
|
||||
|
||||
func NewController(
|
||||
db *sqlx.DB,
|
||||
uidCheck check.PathUID,
|
||||
authorizer authz.Authorizer,
|
||||
pathStore store.PathStore,
|
||||
repoStore store.RepoStore,
|
||||
pipelineStore store.PipelineStore,
|
||||
spaceStore store.SpaceStore,
|
||||
) *Controller {
|
||||
return &Controller{
|
||||
db: db,
|
||||
uidCheck: uidCheck,
|
||||
pathStore: pathStore,
|
||||
repoStore: repoStore,
|
||||
authorizer: authorizer,
|
||||
pipelineStore: pipelineStore,
|
||||
spaceStore: spaceStore,
|
||||
}
|
||||
}
|
113
internal/api/controller/pipeline/create.go
Normal file
113
internal/api/controller/pipeline/create.go
Normal file
@ -0,0 +1,113 @@
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/api/usererror"
|
||||
"github.com/harness/gitness/internal/auth"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/check"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
var (
|
||||
// errRepositoryRequiresParent if the user tries to create a repo without a parent space.
|
||||
errPipelineRequiresParent = usererror.BadRequest(
|
||||
"Parent space required - standalone pipelines are not supported.")
|
||||
)
|
||||
|
||||
type CreateInput struct {
|
||||
Description string `json:"description"`
|
||||
ParentRef string `json:"parent_ref"` // Ref of the parent space
|
||||
UID string `json:"uid"`
|
||||
RepoRef string `json:"repo_ref"` // null if repo_type != gitness
|
||||
RepoType enum.ScmType `json:"repo_type"`
|
||||
DefaultBranch string `json:"default_branch"`
|
||||
ConfigPath string `json:"config_path"`
|
||||
}
|
||||
|
||||
// Create creates a new pipeline
|
||||
func (c *Controller) Create(ctx context.Context, session *auth.Session, in *CreateInput) (*types.Pipeline, error) {
|
||||
// TODO: Add auth
|
||||
// parentSpace, err := c.getSpaceCheckAuthRepoCreation(ctx, session, in.ParentRef)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
parentSpace, err := c.spaceStore.FindByRef(ctx, in.ParentRef)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find parent by ref: %w", err)
|
||||
}
|
||||
var repoID int64
|
||||
|
||||
if in.RepoType == enum.ScmTypeGitness {
|
||||
repo, err := c.repoStore.FindByRef(ctx, in.RepoRef)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find repo by ref: %w", err)
|
||||
}
|
||||
repoID = repo.ID
|
||||
}
|
||||
|
||||
if err := c.sanitizeCreateInput(in); err != nil {
|
||||
return nil, fmt.Errorf("failed to sanitize input: %w", err)
|
||||
}
|
||||
|
||||
var pipeline *types.Pipeline
|
||||
err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) error {
|
||||
// lock parent space path to ensure it doesn't get updated while we setup new pipeline
|
||||
_, err := c.pathStore.FindPrimaryWithLock(ctx, enum.PathTargetTypeSpace, parentSpace.ID)
|
||||
if err != nil {
|
||||
return usererror.BadRequest("Parent not found")
|
||||
}
|
||||
|
||||
now := time.Now().UnixMilli()
|
||||
pipeline = &types.Pipeline{
|
||||
Description: in.Description,
|
||||
ParentID: parentSpace.ID,
|
||||
UID: in.UID,
|
||||
Seq: 0,
|
||||
RepoID: repoID,
|
||||
RepoType: in.RepoType,
|
||||
DefaultBranch: in.DefaultBranch,
|
||||
ConfigPath: in.ConfigPath,
|
||||
Created: now,
|
||||
Updated: now,
|
||||
Version: 0,
|
||||
}
|
||||
err = c.pipelineStore.Create(ctx, pipeline)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pipeline creation failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return pipeline, nil
|
||||
}
|
||||
|
||||
func (c *Controller) sanitizeCreateInput(in *CreateInput) error {
|
||||
parentRefAsID, err := strconv.ParseInt(in.ParentRef, 10, 64)
|
||||
|
||||
if (err == nil && parentRefAsID <= 0) || (len(strings.TrimSpace(in.ParentRef)) == 0) {
|
||||
return errPipelineRequiresParent
|
||||
}
|
||||
|
||||
if err := c.uidCheck(in.UID, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
in.Description = strings.TrimSpace(in.Description)
|
||||
if err := check.Description(in.Description); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if in.DefaultBranch == "" {
|
||||
in.DefaultBranch = c.defaultBranch
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
34
internal/api/controller/pipeline/delete.go
Normal file
34
internal/api/controller/pipeline/delete.go
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/internal/auth"
|
||||
)
|
||||
|
||||
// Delete deletes a pipeline.
|
||||
func (c *Controller) Delete(ctx context.Context, session *auth.Session, spaceRef string, uid string) error {
|
||||
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: Add auth
|
||||
// if err = apiauth.CheckSpace(ctx, c.authorizer, session, space, enum.PermissionSpaceDelete, false); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// TODO: uncomment when soft delete is implemented
|
||||
pipeline, err := c.pipelineStore.FindByUID(ctx, space.ID, uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.pipelineStore.Delete(ctx, pipeline.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not delete pipeline: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
26
internal/api/controller/pipeline/find.go
Normal file
26
internal/api/controller/pipeline/find.go
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/internal/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
// Find finds a repo.
|
||||
func (c *Controller) Find(ctx context.Context, session *auth.Session, spaceRef string, uid string) (*types.Pipeline, error) {
|
||||
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: Add auth
|
||||
// if err = apiauth.CheckSpace(ctx, c.authorizer, session, space, enum.PermissionSpaceDelete, false); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// TODO: uncomment when soft delete is implemented
|
||||
return c.pipelineStore.FindByUID(ctx, space.ID, uid)
|
||||
}
|
50
internal/api/controller/pipeline/update.go
Normal file
50
internal/api/controller/pipeline/update.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/internal/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
// UpdateInput is used for updating a repo.
|
||||
type UpdateInput struct {
|
||||
Description string `json:"description"`
|
||||
UID string `json:"uid"`
|
||||
ConfigPath string `json:"config_path"`
|
||||
}
|
||||
|
||||
// Update updates a repository.
|
||||
func (c *Controller) Update(ctx context.Context, session *auth.Session,
|
||||
spaceRef string, uid string, in *UpdateInput) (*types.Pipeline, error) {
|
||||
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pipeline, err := c.pipelineStore.FindByUID(ctx, space.ID, uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if in.Description != "" {
|
||||
pipeline.Description = in.Description
|
||||
}
|
||||
if in.UID != "" {
|
||||
pipeline.UID = in.UID
|
||||
}
|
||||
if in.ConfigPath != "" {
|
||||
pipeline.ConfigPath = in.ConfigPath
|
||||
}
|
||||
|
||||
// TODO: Add auth
|
||||
// if err = apiauth.CheckRepo(ctx, c.authorizer, session, repo, enum.PermissionRepoEdit, false); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return c.pipelineStore.Update(ctx, pipeline)
|
||||
}
|
25
internal/api/controller/pipeline/wire.go
Normal file
25
internal/api/controller/pipeline/wire.go
Normal file
@ -0,0 +1,25 @@
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"github.com/harness/gitness/internal/auth/authz"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types/check"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
// WireSet provides a wire set for this package.
|
||||
var WireSet = wire.NewSet(
|
||||
ProvideController,
|
||||
)
|
||||
|
||||
func ProvideController(db *sqlx.DB,
|
||||
uidCheck check.PathUID,
|
||||
pathStore store.PathStore,
|
||||
repoStore store.RepoStore,
|
||||
authorizer authz.Authorizer,
|
||||
pipelineStore store.PipelineStore,
|
||||
spaceStore store.SpaceStore,
|
||||
) *Controller {
|
||||
return NewController(db, uidCheck, authorizer, pathStore, repoStore, pipelineStore, spaceStore)
|
||||
}
|
33
internal/api/handler/pipeline/create.go
Normal file
33
internal/api/handler/pipeline/create.go
Normal file
@ -0,0 +1,33 @@
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
)
|
||||
|
||||
// HandleCreate returns a http.HandlerFunc that creates a new pipelinesitory.
|
||||
func HandleCreate(pipelineCtrl *pipeline.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
in := new(pipeline.CreateInput)
|
||||
err := json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(w, "Invalid Request Body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
pipeline, err := pipelineCtrl.Create(ctx, session, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusCreated, pipeline)
|
||||
}
|
||||
}
|
40
internal/api/handler/pipeline/delete.go
Normal file
40
internal/api/handler/pipeline/delete.go
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
)
|
||||
|
||||
/*
|
||||
* Deletes a pipeline.
|
||||
*/
|
||||
func HandleDelete(pipelineCtrl *pipeline.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
pipelineRef, err := request.GetPipelinePathRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
spaceRef, pipelineUID, err := SplitRef(pipelineRef)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
}
|
||||
|
||||
err = pipelineCtrl.Delete(ctx, session, spaceRef, pipelineUID)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.DeleteSuccessful(w)
|
||||
}
|
||||
}
|
49
internal/api/handler/pipeline/find.go
Normal file
49
internal/api/handler/pipeline/find.go
Normal file
@ -0,0 +1,49 @@
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
)
|
||||
|
||||
// HandleFind writes json-encoded repository information to the http response body.
|
||||
func HandleFind(pipelineCtrl *pipeline.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
pipelineRef, err := request.GetPipelinePathRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
spaceRef, pipelineUID, err := SplitRef(pipelineRef)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
}
|
||||
|
||||
pipeline, err := pipelineCtrl.Find(ctx, session, spaceRef, pipelineUID)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, pipeline)
|
||||
}
|
||||
}
|
||||
|
||||
func SplitRef(ref string) (string, string, error) {
|
||||
lastIndex := strings.LastIndex(ref, "/")
|
||||
if lastIndex == -1 {
|
||||
// The input string does not contain a "/".
|
||||
return "", "", errors.New("could not split ref")
|
||||
}
|
||||
|
||||
spaceRef := ref[:lastIndex]
|
||||
uid := ref[lastIndex+1:]
|
||||
|
||||
return spaceRef, uid, nil
|
||||
}
|
45
internal/api/handler/pipeline/update.go
Normal file
45
internal/api/handler/pipeline/update.go
Normal file
@ -0,0 +1,45 @@
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
)
|
||||
|
||||
/*
|
||||
* Updates an existing pipeline.
|
||||
*/
|
||||
func HandleUpdate(pipelineCtrl *pipeline.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
in := new(pipeline.UpdateInput)
|
||||
err := json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(w, "Invalid Request Body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
pipelineRef, err := request.GetPipelinePathRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
spaceRef, pipelineUID, err := SplitRef(pipelineRef)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
}
|
||||
|
||||
pipeline, err := pipelineCtrl.Update(ctx, session, spaceRef, pipelineUID, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, pipeline)
|
||||
}
|
||||
}
|
@ -41,6 +41,7 @@ func Generate() *openapi3.Spec {
|
||||
buildPrincipals(&reflector)
|
||||
spaceOperations(&reflector)
|
||||
repoOperations(&reflector)
|
||||
pipelineOperations(&reflector)
|
||||
resourceOperations(&reflector)
|
||||
pullReqOperations(&reflector)
|
||||
webhookOperations(&reflector)
|
||||
|
81
internal/api/openapi/pipeline.go
Normal file
81
internal/api/openapi/pipeline.go
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this pipelinesitory.
|
||||
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||
"github.com/harness/gitness/internal/api/usererror"
|
||||
"github.com/harness/gitness/types"
|
||||
|
||||
"github.com/swaggest/openapi-go/openapi3"
|
||||
)
|
||||
|
||||
type createPipelineRequest struct {
|
||||
pipeline.CreateInput
|
||||
}
|
||||
|
||||
type pipelineRequest struct {
|
||||
Ref string `path:"pipeline_ref"`
|
||||
}
|
||||
|
||||
type updatePipelineRequest struct {
|
||||
pipelineRequest
|
||||
pipeline.UpdateInput
|
||||
}
|
||||
|
||||
type scmType string
|
||||
|
||||
type pipelineGetResponse struct {
|
||||
types.Pipeline
|
||||
}
|
||||
|
||||
func pipelineOperations(reflector *openapi3.Reflector) {
|
||||
opCreate := openapi3.Operation{}
|
||||
opCreate.WithTags("pipeline")
|
||||
opCreate.WithMapOfAnything(map[string]interface{}{"operationId": "createPipeline"})
|
||||
_ = reflector.SetRequest(&opCreate, new(createPipelineRequest), http.MethodPost)
|
||||
_ = reflector.SetJSONResponse(&opCreate, new(types.Pipeline), http.StatusCreated)
|
||||
_ = reflector.SetJSONResponse(&opCreate, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opCreate, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opCreate, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opCreate, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPost, "/pipelines", opCreate)
|
||||
|
||||
opFind := openapi3.Operation{}
|
||||
opFind.WithTags("pipeline")
|
||||
opFind.WithMapOfAnything(map[string]interface{}{"operationId": "findPipeline"})
|
||||
_ = reflector.SetRequest(&opFind, new(pipelineRequest), http.MethodGet)
|
||||
_ = reflector.SetJSONResponse(&opFind, new(pipelineGetResponse), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opFind, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opFind, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opFind, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opFind, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/pipelines/{pipeline_ref}", opFind)
|
||||
|
||||
opDelete := openapi3.Operation{}
|
||||
opDelete.WithTags("pipeline")
|
||||
opDelete.WithMapOfAnything(map[string]interface{}{"operationId": "deletePipeline"})
|
||||
_ = reflector.SetRequest(&opDelete, new(pipelineRequest), http.MethodDelete)
|
||||
_ = reflector.SetJSONResponse(&opDelete, nil, http.StatusNoContent)
|
||||
_ = reflector.SetJSONResponse(&opDelete, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opDelete, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opDelete, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opDelete, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodDelete, "/pipelines/{pipeline_ref}", opDelete)
|
||||
|
||||
opUpdate := openapi3.Operation{}
|
||||
opUpdate.WithTags("pipeline")
|
||||
opUpdate.WithMapOfAnything(map[string]interface{}{"operationId": "updatePipeline"})
|
||||
_ = reflector.SetRequest(&opUpdate, new(updatePipelineRequest), http.MethodPatch)
|
||||
_ = reflector.SetJSONResponse(&opUpdate, new(types.Pipeline), http.StatusOK)
|
||||
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.SetJSONResponse(&opUpdate, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPatch, "/pipelines/{pipeline_ref}", opUpdate)
|
||||
}
|
54
internal/api/request/pipeline.go
Normal file
54
internal/api/request/pipeline.go
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2022 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package request
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const (
|
||||
PipelinePathRef = "pipeline_ref"
|
||||
PipelineUID = "pipeline_uid"
|
||||
)
|
||||
|
||||
func GetPipelinePathRefFromPath(r *http.Request) (string, error) {
|
||||
rawRef, err := PathParamOrError(r, PipelinePathRef)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// paths are unescaped
|
||||
return url.PathUnescape(rawRef)
|
||||
}
|
||||
|
||||
func GetPipelineUIDFromPath(r *http.Request) (string, error) {
|
||||
rawRef, err := PathParamOrError(r, PipelineUID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// paths are unescaped
|
||||
return url.PathUnescape(rawRef)
|
||||
}
|
||||
|
||||
// TODO: Add list filters
|
||||
// // ParseSortRepo extracts the repo sort parameter from the url.
|
||||
// func ParseSortRepo(r *http.Request) enum.RepoAttr {
|
||||
// return enum.ParseRepoAtrr(
|
||||
// r.URL.Query().Get(QueryParamSort),
|
||||
// )
|
||||
// }
|
||||
|
||||
// // ParseRepoFilter extracts the repository filter from the url.
|
||||
// func ParseRepoFilter(r *http.Request) *types.RepoFilter {
|
||||
// return &types.RepoFilter{
|
||||
// Query: ParseQuery(r),
|
||||
// Order: ParseOrder(r),
|
||||
// Page: ParsePage(r),
|
||||
// Sort: ParseSortRepo(r),
|
||||
// Size: ParseLimit(r),
|
||||
// }
|
||||
// }
|
@ -10,7 +10,9 @@ import (
|
||||
|
||||
"github.com/harness/gitness/githook"
|
||||
"github.com/harness/gitness/internal/api/controller/check"
|
||||
"github.com/harness/gitness/internal/api/controller/execution"
|
||||
controllergithook "github.com/harness/gitness/internal/api/controller/githook"
|
||||
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||
"github.com/harness/gitness/internal/api/controller/principal"
|
||||
"github.com/harness/gitness/internal/api/controller/pullreq"
|
||||
"github.com/harness/gitness/internal/api/controller/repo"
|
||||
@ -21,6 +23,7 @@ import (
|
||||
"github.com/harness/gitness/internal/api/handler/account"
|
||||
handlercheck "github.com/harness/gitness/internal/api/handler/check"
|
||||
handlergithook "github.com/harness/gitness/internal/api/handler/githook"
|
||||
handlerpipeline "github.com/harness/gitness/internal/api/handler/pipeline"
|
||||
handlerprincipal "github.com/harness/gitness/internal/api/handler/principal"
|
||||
handlerpullreq "github.com/harness/gitness/internal/api/handler/pullreq"
|
||||
handlerrepo "github.com/harness/gitness/internal/api/handler/repo"
|
||||
@ -62,7 +65,9 @@ func NewAPIHandler(
|
||||
config *types.Config,
|
||||
authenticator authn.Authenticator,
|
||||
repoCtrl *repo.Controller,
|
||||
executionCtrl *execution.Controller,
|
||||
spaceCtrl *space.Controller,
|
||||
pipelineCtrl *pipeline.Controller,
|
||||
pullreqCtrl *pullreq.Controller,
|
||||
webhookCtrl *webhook.Controller,
|
||||
githookCtrl *controllergithook.Controller,
|
||||
@ -92,7 +97,7 @@ func NewAPIHandler(
|
||||
r.Use(middlewareauthn.Attempt(authenticator, authn.SourceRouterAPI))
|
||||
|
||||
r.Route("/v1", func(r chi.Router) {
|
||||
setupRoutesV1(r, repoCtrl, spaceCtrl, pullreqCtrl, webhookCtrl, githookCtrl,
|
||||
setupRoutesV1(r, config, repoCtrl, executionCtrl, pipelineCtrl, spaceCtrl, pullreqCtrl, webhookCtrl, githookCtrl,
|
||||
saCtrl, userCtrl, principalCtrl, checkCtrl)
|
||||
})
|
||||
|
||||
@ -114,7 +119,10 @@ func corsHandler(config *types.Config) func(http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
func setupRoutesV1(r chi.Router,
|
||||
config *types.Config,
|
||||
repoCtrl *repo.Controller,
|
||||
executionCtrl *execution.Controller,
|
||||
pipelineCtrl *pipeline.Controller,
|
||||
spaceCtrl *space.Controller,
|
||||
pullreqCtrl *pullreq.Controller,
|
||||
webhookCtrl *webhook.Controller,
|
||||
@ -126,6 +134,7 @@ func setupRoutesV1(r chi.Router,
|
||||
) {
|
||||
setupSpaces(r, spaceCtrl)
|
||||
setupRepos(r, repoCtrl, pullreqCtrl, webhookCtrl, checkCtrl)
|
||||
setupPipelines(r, pipelineCtrl, executionCtrl)
|
||||
setupUser(r, userCtrl)
|
||||
setupServiceAccounts(r, saCtrl)
|
||||
setupPrincipals(r, principalCtrl)
|
||||
@ -266,6 +275,20 @@ func setupRepos(r chi.Router,
|
||||
})
|
||||
}
|
||||
|
||||
func setupPipelines(r chi.Router, pipelineCtrl *pipeline.Controller, executionCtrl *execution.Controller) {
|
||||
r.Route("/pipelines", func(r chi.Router) {
|
||||
// Create takes path and parentId via body, not uri
|
||||
r.Post("/", handlerpipeline.HandleCreate(pipelineCtrl))
|
||||
r.Route(fmt.Sprintf("/{%s}", request.PipelinePathRef), func(r chi.Router) {
|
||||
r.Get("/", handlerpipeline.HandleFind(pipelineCtrl))
|
||||
r.Patch("/", handlerpipeline.HandleUpdate(pipelineCtrl))
|
||||
r.Delete("/", handlerpipeline.HandleDelete(pipelineCtrl))
|
||||
// TODO: setup executions here
|
||||
// SetupExecutions(r, executionCtrl)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func setupInternal(r chi.Router, githookCtrl *controllergithook.Controller) {
|
||||
r.Route("/internal", func(r chi.Router) {
|
||||
SetupGitHooks(r, githookCtrl)
|
||||
|
@ -7,7 +7,9 @@ package router
|
||||
import (
|
||||
"github.com/harness/gitness/gitrpc"
|
||||
"github.com/harness/gitness/internal/api/controller/check"
|
||||
"github.com/harness/gitness/internal/api/controller/execution"
|
||||
"github.com/harness/gitness/internal/api/controller/githook"
|
||||
"github.com/harness/gitness/internal/api/controller/pipeline"
|
||||
"github.com/harness/gitness/internal/api/controller/principal"
|
||||
"github.com/harness/gitness/internal/api/controller/pullreq"
|
||||
"github.com/harness/gitness/internal/api/controller/repo"
|
||||
@ -57,7 +59,9 @@ func ProvideAPIHandler(
|
||||
config *types.Config,
|
||||
authenticator authn.Authenticator,
|
||||
repoCtrl *repo.Controller,
|
||||
executionCtrl *execution.Controller,
|
||||
spaceCtrl *space.Controller,
|
||||
pipelineCtrl *pipeline.Controller,
|
||||
pullreqCtrl *pullreq.Controller,
|
||||
webhookCtrl *webhook.Controller,
|
||||
githookCtrl *githook.Controller,
|
||||
@ -66,7 +70,7 @@ func ProvideAPIHandler(
|
||||
principalCtrl principal.Controller,
|
||||
checkCtrl *check.Controller,
|
||||
) APIHandler {
|
||||
return NewAPIHandler(config, authenticator, repoCtrl, spaceCtrl, pullreqCtrl,
|
||||
return NewAPIHandler(config, authenticator, repoCtrl, executionCtrl, spaceCtrl, pipelineCtrl, pullreqCtrl,
|
||||
webhookCtrl, githookCtrl, saCtrl, userCtrl, principalCtrl, checkCtrl)
|
||||
}
|
||||
|
||||
|
@ -439,4 +439,85 @@ type (
|
||||
// Delete removes a required status checks for a repo.
|
||||
Delete(ctx context.Context, repoID, reqCheckID int64) error
|
||||
}
|
||||
PipelineStore interface {
|
||||
// Find returns a pipeline given a pipeline ID from the datastore.
|
||||
Find(context.Context, int64) (*types.Pipeline, error)
|
||||
|
||||
// FindByUID returns a pipeline with a given UID in a space
|
||||
FindByUID(context.Context, int64, string) (*types.Pipeline, error)
|
||||
|
||||
// Create creates a new pipeline in the datastore.
|
||||
Create(context.Context, *types.Pipeline) error
|
||||
|
||||
// Update tries to update a pipeline in the datastore with optimistic locking.
|
||||
Update(context.Context, *types.Pipeline) (*types.Pipeline, error)
|
||||
|
||||
// List lists the pipelines present in a parent space ID in the datastore.
|
||||
List(context.Context, int64, *types.PipelineFilter) ([]*types.Pipeline, error)
|
||||
|
||||
// Delete deletes a pipeline ID from the datastore.
|
||||
Delete(context.Context, int64) error
|
||||
}
|
||||
|
||||
// TODO: Implement the execution store interface
|
||||
ExecutionStore interface {
|
||||
// Find returns a build from the datastore.
|
||||
// Find(context.Context, int64) (*types.Execution, error)
|
||||
|
||||
// FindNumber returns a build from the datastore by build number.
|
||||
// FindNumber(context.Context, int64, int64) (*types.Execution, error)
|
||||
|
||||
// FindLast returns the last build from the datastore by ref.
|
||||
// FindRef(context.Context, int64, string) (*types.Execution, error)
|
||||
|
||||
// List returns a list of builds from the datastore by repository id.
|
||||
// List(context.Context, int64, int, int) ([]*types.Execution, error)
|
||||
|
||||
// ListRef returns a list of builds from the datastore by ref.
|
||||
// ListRef(context.Context, int64, string, int, int) ([]*types.Execution, error)
|
||||
|
||||
// LatestBranches returns the latest builds from the
|
||||
// datastore by branch.
|
||||
// LatestBranches(context.Context, int64) ([]*types.Execution, error)
|
||||
|
||||
// LatestPulls returns the latest builds from the
|
||||
// datastore by pull request.
|
||||
// LatestPulls(context.Context, int64) ([]*types.Execution, error)
|
||||
|
||||
// LatestDeploys returns the latest builds from the
|
||||
// datastore by deployment target.
|
||||
// LatestDeploys(context.Context, int64) ([]*types.Execution, error)
|
||||
|
||||
// Pending returns a list of pending builds from the
|
||||
// datastore by repository id (DEPRECATED).
|
||||
// Pending(context.Context) ([]*types.Execution, error)
|
||||
|
||||
// Running returns a list of running builds from the
|
||||
// datastore by repository id (DEPRECATED).
|
||||
// Running(context.Context) ([]*types.Execution, error)
|
||||
|
||||
// Create persists a build to the datastore.
|
||||
// Create(context.Context, *types.Execution, []*Stage) error
|
||||
|
||||
// Update updates a build in the datastore.
|
||||
// Update(context.Context, *types.Execution) error
|
||||
|
||||
// // Delete deletes a build from the datastore.
|
||||
// Delete(context.Context, *types.Execution) error
|
||||
|
||||
// // DeletePull deletes a pull request index from the datastore.
|
||||
// DeletePull(context.Context, int64, int) error
|
||||
|
||||
// // DeleteBranch deletes a branch index from the datastore.
|
||||
// DeleteBranch(context.Context, int64, string) error
|
||||
|
||||
// // DeleteDeploy deletes a deploy index from the datastore.
|
||||
// DeleteDeploy(context.Context, int64, string) error
|
||||
|
||||
// // Purge deletes builds from the database where the build number is less than n.
|
||||
// Purge(context.Context, int64, int64) error
|
||||
|
||||
// // Count returns a count of builds.
|
||||
// Count(context.Context) (int64, error)
|
||||
}
|
||||
)
|
||||
|
59
internal/store/database/execution.go
Normal file
59
internal/store/database/execution.go
Normal file
@ -0,0 +1,59 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
var _ store.ExecutionStore = (*executionStore)(nil)
|
||||
|
||||
// NewSpaceStore returns a new PathStore.
|
||||
func NewExecutionStore(db *sqlx.DB) *executionStore {
|
||||
return &executionStore{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
type executionStore struct {
|
||||
db *sqlx.DB
|
||||
}
|
||||
|
||||
const (
|
||||
executionColumns = `
|
||||
execution_id
|
||||
,execution_scm_type
|
||||
,execution_repo_id
|
||||
,execution_trigger
|
||||
,execution_number
|
||||
,execution_parent
|
||||
,execution_status
|
||||
,execution_error
|
||||
,execution_event
|
||||
,execution_action
|
||||
,execution_link
|
||||
,execution_timestamp
|
||||
,execution_title
|
||||
,execution_message
|
||||
,execution_before
|
||||
,execution_after
|
||||
,execution_ref
|
||||
,execution_source_repo
|
||||
,execution_source
|
||||
,execution_target
|
||||
,execution_author
|
||||
,execution_author_name
|
||||
,execution_author_email
|
||||
,execution_author_avatar
|
||||
,execution_sender
|
||||
,execution_params
|
||||
,execution_cron
|
||||
,execution_deploy
|
||||
,execution_deploy_id
|
||||
,execution_debug
|
||||
,execution_started
|
||||
,execution_finished
|
||||
,execution_created
|
||||
,execution_updated
|
||||
,execution_version
|
||||
`
|
||||
)
|
@ -0,0 +1,48 @@
|
||||
CREATE TABLE IF NOT EXISTS executions (
|
||||
execution_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
execution_pipeline_id INTEGER NOT NULL,
|
||||
execution_repo_id INTEGER,
|
||||
execution_repo_type TEXT,
|
||||
execution_repo_name TEXT,
|
||||
execution_trigger TEXT,
|
||||
execution_number INTEGER NOT NULL,
|
||||
execution_parent INTEGER,
|
||||
execution_status TEXT,
|
||||
execution_error TEXT,
|
||||
execution_event TEXT,
|
||||
execution_action TEXT,
|
||||
execution_link TEXT,
|
||||
execution_timestamp INTEGER,
|
||||
execution_title TEXT,
|
||||
execution_message TEXT,
|
||||
execution_before TEXT,
|
||||
execution_after TEXT,
|
||||
execution_ref TEXT,
|
||||
execution_source_repo TEXT,
|
||||
execution_source TEXT,
|
||||
execution_target TEXT,
|
||||
execution_author TEXT,
|
||||
execution_author_name TEXT,
|
||||
execution_author_email TEXT,
|
||||
execution_author_avatar TEXT,
|
||||
execution_sender TEXT,
|
||||
execution_params TEXT,
|
||||
execution_cron TEXT,
|
||||
execution_deploy TEXT,
|
||||
execution_deploy_id INTEGER,
|
||||
execution_debug BOOLEAN NOT NULL DEFAULT 0,
|
||||
execution_started INTEGER,
|
||||
execution_finished INTEGER,
|
||||
execution_created INTEGER,
|
||||
execution_updated INTEGER,
|
||||
execution_version INTEGER,
|
||||
|
||||
-- Ensure unique combination of pipeline ID and number
|
||||
UNIQUE (execution_pipeline_id, execution_number),
|
||||
|
||||
-- Foreign key to pipelines table
|
||||
CONSTRAINT fk_execution_pipeline_id FOREIGN KEY (execution_pipeline_id)
|
||||
REFERENCES pipelines (pipeline_id) MATCH SIMPLE
|
||||
ON UPDATE NO ACTION
|
||||
ON DELETE CASCADE
|
||||
);
|
@ -0,0 +1 @@
|
||||
DROP TABLE pipelines;
|
@ -0,0 +1,30 @@
|
||||
CREATE TABLE IF NOT EXISTS pipelines (
|
||||
pipeline_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
pipeline_description TEXT,
|
||||
pipeline_parent_id INTEGER NOT NULL,
|
||||
pipeline_uid TEXT NOT NULL,
|
||||
pipeline_seq INTEGER NOT NULL DEFAULT 0,
|
||||
pipeline_repo_id INTEGER,
|
||||
pipeline_repo_type TEXT NOT NULL,
|
||||
pipeline_repo_name TEXT,
|
||||
pipeline_default_branch TEXT,
|
||||
pipeline_config_path TEXT,
|
||||
pipeline_created INTEGER,
|
||||
pipeline_updated INTEGER,
|
||||
pipeline_version INTEGER,
|
||||
|
||||
-- Ensure unique combination of UID and ParentID
|
||||
UNIQUE (pipeline_parent_id, pipeline_uid),
|
||||
|
||||
-- Foreign key to spaces table
|
||||
CONSTRAINT fk_pipeline_parent_id FOREIGN KEY (pipeline_parent_id)
|
||||
REFERENCES spaces (space_id) MATCH SIMPLE
|
||||
ON UPDATE NO ACTION
|
||||
ON DELETE CASCADE
|
||||
|
||||
-- Foreign key to repositories table
|
||||
CONSTRAINT fk_pipeline_repo_id FOREIGN KEY (pipeline_repo_id)
|
||||
REFERENCES repositories (repo_id) MATCH SIMPLE
|
||||
ON UPDATE NO ACTION
|
||||
ON DELETE CASCADE
|
||||
);
|
@ -0,0 +1 @@
|
||||
DROP TABLE executions;
|
@ -0,0 +1,46 @@
|
||||
CREATE TABLE IF NOT EXISTS executions (
|
||||
execution_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
execution_pipeline_id INTEGER NOT NULL,
|
||||
execution_repo_id INTEGER,
|
||||
execution_trigger TEXT,
|
||||
execution_number INTEGER NOT NULL,
|
||||
execution_parent INTEGER,
|
||||
execution_status TEXT,
|
||||
execution_error TEXT,
|
||||
execution_event TEXT,
|
||||
execution_action TEXT,
|
||||
execution_link TEXT,
|
||||
execution_timestamp INTEGER,
|
||||
execution_title TEXT,
|
||||
execution_message TEXT,
|
||||
execution_before TEXT,
|
||||
execution_after TEXT,
|
||||
execution_ref TEXT,
|
||||
execution_source_repo TEXT,
|
||||
execution_source TEXT,
|
||||
execution_target TEXT,
|
||||
execution_author TEXT,
|
||||
execution_author_name TEXT,
|
||||
execution_author_email TEXT,
|
||||
execution_author_avatar TEXT,
|
||||
execution_sender TEXT,
|
||||
execution_params TEXT,
|
||||
execution_cron TEXT,
|
||||
execution_deploy TEXT,
|
||||
execution_deploy_id INTEGER,
|
||||
execution_debug BOOLEAN NOT NULL DEFAULT 0,
|
||||
execution_started INTEGER,
|
||||
execution_finished INTEGER,
|
||||
execution_created INTEGER,
|
||||
execution_updated INTEGER,
|
||||
execution_version INTEGER,
|
||||
|
||||
-- Ensure unique combination of pipeline ID and number
|
||||
UNIQUE (execution_pipeline_id, execution_number),
|
||||
|
||||
-- Foreign key to pipelines table
|
||||
CONSTRAINT fk_execution_pipeline_id FOREIGN KEY (execution_pipeline_id)
|
||||
REFERENCES pipelines (pipeline_id) MATCH SIMPLE
|
||||
ON UPDATE NO ACTION
|
||||
ON DELETE CASCADE
|
||||
);
|
228
internal/store/database/pipeline.go
Normal file
228
internal/store/database/pipeline.go
Normal file
@ -0,0 +1,228 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/store"
|
||||
gitness_store "github.com/harness/gitness/store"
|
||||
"github.com/harness/gitness/store/database"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var _ store.PipelineStore = (*pipelineStore)(nil)
|
||||
|
||||
const (
|
||||
pipelineQueryBase = `
|
||||
SELECT
|
||||
pipeline_id,
|
||||
pipeline_description,
|
||||
pipeline_parent_id,
|
||||
pipeline_uid,
|
||||
pipeline_seq,
|
||||
pipeline_repo_id,
|
||||
pipeline_repo_type,
|
||||
pipeline_repo_name,
|
||||
pipeline_default_branch,
|
||||
pipeline_config_path,
|
||||
pipeline_created,
|
||||
pipeline_updated,
|
||||
pipeline_version
|
||||
FROM pipelines
|
||||
`
|
||||
|
||||
pipelineColumns = `
|
||||
pipeline_id,
|
||||
pipeline_description,
|
||||
pipeline_parent_id,
|
||||
pipeline_uid,
|
||||
pipeline_seq,
|
||||
pipeline_repo_id,
|
||||
pipeline_repo_type,
|
||||
pipeline_repo_name,
|
||||
pipeline_default_branch,
|
||||
pipeline_config_path,
|
||||
pipeline_created,
|
||||
pipeline_updated,
|
||||
pipeline_version
|
||||
`
|
||||
|
||||
pipelineInsertStmt = `
|
||||
INSERT INTO pipelines (
|
||||
pipeline_description,
|
||||
pipeline_parent_id,
|
||||
pipeline_uid,
|
||||
pipeline_seq,
|
||||
pipeline_repo_id,
|
||||
pipeline_repo_type,
|
||||
pipeline_repo_name,
|
||||
pipeline_default_branch,
|
||||
pipeline_config_path,
|
||||
pipeline_created,
|
||||
pipeline_updated,
|
||||
pipeline_version
|
||||
) VALUES (
|
||||
:pipeline_description,
|
||||
:pipeline_parent_id,
|
||||
:pipeline_uid,
|
||||
:pipeline_seq,
|
||||
:pipeline_repo_id,
|
||||
:pipeline_repo_type,
|
||||
:pipeline_repo_name,
|
||||
:pipeline_default_branch,
|
||||
:pipeline_config_path,
|
||||
:pipeline_created,
|
||||
:pipeline_updated,
|
||||
:pipeline_version
|
||||
) RETURNING pipeline_id`
|
||||
|
||||
pipelineUpdateStmt = `
|
||||
UPDATE pipelines
|
||||
SET
|
||||
pipeline_description = :pipeline_description,
|
||||
pipeline_parent_id = :pipeline_parent_id,
|
||||
pipeline_uid = :pipeline_uid,
|
||||
pipeline_seq = :pipeline_seq,
|
||||
pipeline_repo_id = :pipeline_repo_id,
|
||||
pipeline_repo_type = :pipeline_repo_type,
|
||||
pipeline_repo_name = :pipeline_repo_name,
|
||||
pipeline_default_branch = :pipeline_default_branch,
|
||||
pipeline_config_path = :pipeline_config_path,
|
||||
pipeline_created = :pipeline_created,
|
||||
pipeline_updated = :pipeline_updated,
|
||||
pipeline_version = :pipeline_version
|
||||
WHERE pipeline_id = :pipeline_id AND pipeline_version = :pipeline_version - 1`
|
||||
)
|
||||
|
||||
// NewPipelineStore returns a new PipelineStore.
|
||||
func NewPipelineStore(db *sqlx.DB) *pipelineStore {
|
||||
return &pipelineStore{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
type pipelineStore struct {
|
||||
db *sqlx.DB
|
||||
}
|
||||
|
||||
// Find returns a pipeline given a pipeline ID
|
||||
func (s *pipelineStore) Find(ctx context.Context, id int64) (*types.Pipeline, error) {
|
||||
const findQueryStmt = pipelineQueryBase + `
|
||||
WHERE pipeline_id = $1`
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
dst := new(types.Pipeline)
|
||||
if err := db.GetContext(ctx, dst, findQueryStmt, id); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(err, "Failed to find pipeline")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// FindByUID returns a pipeline in a given space with a given UID
|
||||
func (s *pipelineStore) FindByUID(ctx context.Context, spaceID int64, uid string) (*types.Pipeline, error) {
|
||||
const findQueryStmt = pipelineQueryBase + `
|
||||
WHERE pipeline_parent_id = $1 AND pipeline_uid = $2`
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
dst := new(types.Pipeline)
|
||||
if err := db.GetContext(ctx, dst, findQueryStmt, spaceID, uid); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(err, "Failed to find pipeline")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// Create creates a pipeline
|
||||
func (s *pipelineStore) Create(ctx context.Context, pipeline *types.Pipeline) error {
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
query, arg, err := db.BindNamed(pipelineInsertStmt, pipeline)
|
||||
if err != nil {
|
||||
return database.ProcessSQLErrorf(err, "Failed to bind pipeline object")
|
||||
}
|
||||
|
||||
if err = db.QueryRowContext(ctx, query, arg...).Scan(&pipeline.ID); err != nil {
|
||||
return database.ProcessSQLErrorf(err, "Pipeline query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *pipelineStore) Update(ctx context.Context, pipeline *types.Pipeline) (*types.Pipeline, error) {
|
||||
updatedAt := time.Now()
|
||||
|
||||
pipeline.Version++
|
||||
pipeline.Updated = updatedAt.UnixMilli()
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
query, arg, err := db.BindNamed(pipelineUpdateStmt, pipeline)
|
||||
if err != nil {
|
||||
return nil, database.ProcessSQLErrorf(err, "Failed to bind pipeline object")
|
||||
}
|
||||
|
||||
result, err := db.ExecContext(ctx, query, arg...)
|
||||
if err != nil {
|
||||
return nil, database.ProcessSQLErrorf(err, "Failed to update pipeline")
|
||||
}
|
||||
|
||||
count, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return nil, database.ProcessSQLErrorf(err, "Failed to get number of updated rows")
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return nil, gitness_store.ErrVersionConflict
|
||||
}
|
||||
|
||||
return s.Find(ctx, pipeline.ID)
|
||||
|
||||
}
|
||||
|
||||
// List lists all the pipelines present in a space
|
||||
func (s *pipelineStore) List(ctx context.Context, parentID int64, opts *types.PipelineFilter) ([]*types.Pipeline, error) {
|
||||
stmt := database.Builder.
|
||||
Select(pipelineColumns).
|
||||
From("pipelines").
|
||||
Where("pipeline_parent_id = ?", fmt.Sprint(parentID))
|
||||
|
||||
if opts.Query != "" {
|
||||
stmt = stmt.Where("LOWER(pipeline_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(opts.Query)))
|
||||
}
|
||||
|
||||
stmt = stmt.Limit(database.Limit(opts.Size))
|
||||
stmt = stmt.Offset(database.Offset(opts.Page, opts.Size))
|
||||
|
||||
sql, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to convert query to sql")
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
dst := []*types.Pipeline{}
|
||||
if err = db.SelectContext(ctx, &dst, sql, args...); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(err, "Failed executing custom list query")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// Delete deletes a pipeline given a pipeline ID
|
||||
func (s *pipelineStore) Delete(ctx context.Context, id int64) error {
|
||||
const pipelineDeleteStmt = `
|
||||
DELETE FROM pipelines
|
||||
WHERE pipeline_id = $1`
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
if _, err := db.ExecContext(ctx, pipelineDeleteStmt, id); err != nil {
|
||||
return database.ProcessSQLErrorf(err, "Could not delete pipeline")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -23,6 +23,8 @@ var WireSet = wire.NewSet(
|
||||
ProvidePathStore,
|
||||
ProvideSpaceStore,
|
||||
ProvideRepoStore,
|
||||
ProvideExecutionStore,
|
||||
ProvidePipelineStore,
|
||||
ProvideRepoGitInfoView,
|
||||
ProvideMembershipStore,
|
||||
ProvideTokenStore,
|
||||
@ -78,6 +80,16 @@ func ProvideRepoStore(db *sqlx.DB, pathCache store.PathCache) store.RepoStore {
|
||||
return NewRepoStore(db, pathCache)
|
||||
}
|
||||
|
||||
// ProvidePipelineStore provides a pipeline store.
|
||||
func ProvidePipelineStore(db *sqlx.DB) store.PipelineStore {
|
||||
return NewPipelineStore(db)
|
||||
}
|
||||
|
||||
// ProvideExecutionStore provides a build store
|
||||
func ProvideExecutionStore(db *sqlx.DB) store.ExecutionStore {
|
||||
return NewExecutionStore(db)
|
||||
}
|
||||
|
||||
// ProvideRepoGitInfoView provides a repo git UID view.
|
||||
func ProvideRepoGitInfoView(db *sqlx.DB) store.RepoGitInfoView {
|
||||
return NewRepoGitInfoView(db)
|
||||
|
20
types/enum/scm.go
Normal file
20
types/enum/scm.go
Normal file
@ -0,0 +1,20 @@
|
||||
package enum
|
||||
|
||||
// ScmType defines the different types of principal types at harness.
|
||||
type ScmType string
|
||||
|
||||
func (ScmType) Enum() []interface{} { return toInterfaceSlice(scmTypes) }
|
||||
|
||||
var scmTypes = ([]ScmType{
|
||||
ScmTypeGitness,
|
||||
ScmTypeGithub,
|
||||
ScmTypeGitlab,
|
||||
ScmTypeUnknown,
|
||||
})
|
||||
|
||||
const (
|
||||
ScmTypeUnknown ScmType = "UNKNOWN"
|
||||
ScmTypeGitness ScmType = "GITNESS"
|
||||
ScmTypeGithub ScmType = "GITHUB"
|
||||
ScmTypeGitlab ScmType = "GITLAB"
|
||||
)
|
42
types/execution.go
Normal file
42
types/execution.go
Normal file
@ -0,0 +1,42 @@
|
||||
package types
|
||||
|
||||
// Execution represents an instance of a pipeline execution
|
||||
type Execution struct {
|
||||
ID int64 `db:"execution_id" json:"id"`
|
||||
PipelineID int64 `db:"execution_pipeline_id" json:"pipeline_id"`
|
||||
RepoID int64 `db:"execution_repo_id" json:"repo_id"`
|
||||
Trigger string `db:"execution_trigger" json:"trigger"`
|
||||
Number int64 `db:"execution_number" json:"number"`
|
||||
Parent int64 `db:"execution_parent" json:"parent,omitempty"`
|
||||
Status string `db:"execution_status" json:"status"`
|
||||
Error string `db:"execution_error" json:"error,omitempty"`
|
||||
Event string `db:"execution_event" json:"event"`
|
||||
Action string `db:"execution_action" json:"action"`
|
||||
Link string `db:"execution_link" json:"link"`
|
||||
Timestamp int64 `db:"execution_timestamp" json:"timestamp"`
|
||||
Title string `db:"execution_title" json:"title,omitempty"`
|
||||
Message string `db:"execution_message" json:"message"`
|
||||
Before string `db:"execution_before" json:"before"`
|
||||
After string `db:"execution_after" json:"after"`
|
||||
Ref string `db:"execution_ref" json:"ref"`
|
||||
Fork string `db:"execution_source_repo" json:"source_repo"`
|
||||
Source string `db:"execution_source" json:"source"`
|
||||
Target string `db:"execution_target" json:"target"`
|
||||
Author string `db:"execution_author" json:"author_login"`
|
||||
AuthorName string `db:"execution_author_name" json:"author_name"`
|
||||
AuthorEmail string `db:"execution_author_email" json:"author_email"`
|
||||
AuthorAvatar string `db:"execution_author_avatar" json:"author_avatar"`
|
||||
Sender string `db:"execution_sender" json:"sender"`
|
||||
Params map[string]string `db:"execution_params" json:"params,omitempty"`
|
||||
Cron string `db:"execution_cron" json:"cron,omitempty"`
|
||||
Deploy string `db:"execution_deploy" json:"deploy_to,omitempty"`
|
||||
DeployID int64 `db:"execution_deploy_id" json:"deploy_id,omitempty"`
|
||||
Debug bool `db:"execution_debug" json:"debug,omitempty"`
|
||||
Started int64 `db:"execution_started" json:"started"`
|
||||
Finished int64 `db:"execution_finished" json:"finished"`
|
||||
Created int64 `db:"execution_created" json:"created"`
|
||||
Updated int64 `db:"execution_updated" json:"updated"`
|
||||
Version int64 `db:"execution_version" json:"version"`
|
||||
// TODO: (Vistaar) Add stages
|
||||
// Stages []*Stage `db:"-" json:"stages,omitempty"`
|
||||
}
|
27
types/pipeline.go
Normal file
27
types/pipeline.go
Normal file
@ -0,0 +1,27 @@
|
||||
package types
|
||||
|
||||
import "github.com/harness/gitness/types/enum"
|
||||
|
||||
type Pipeline struct {
|
||||
ID int64 `db:"pipeline_id" json:"id"`
|
||||
Description string `db:"pipeline_description" json:"description"`
|
||||
ParentID int64 `db:"pipeline_parent_id" json:"parent_id"` // ID of the parent space
|
||||
UID string `db:"pipeline_uid" json:"uid"`
|
||||
Seq int64 `db:"pipeline_seq" json:"seq"` // last execution number for this pipeline
|
||||
RepoID int64 `db:"pipeline_repo_id" json:"repo_id"` // null if repo_type != gitness
|
||||
RepoType enum.ScmType `db:"pipeline_repo_type" json:"repo_type"`
|
||||
RepoName string `db:"pipeline_repo_name" json:"repo_name"`
|
||||
DefaultBranch string `db:"pipeline_default_branch" json:"default_branch"`
|
||||
ConfigPath string `db:"pipeline_config_path" json:"config_path"`
|
||||
Created int64 `db:"pipeline_created" json:"created"`
|
||||
Updated int64 `db:"pipeline_updated" json:"updated"`
|
||||
Version int64 `db:"pipeline_version" json:"version"`
|
||||
}
|
||||
|
||||
// RepoFilter stores repo query parameters.
|
||||
type PipelineFilter struct {
|
||||
Page int `json:"page"`
|
||||
Size int `json:"size"`
|
||||
Query string `json:"query"`
|
||||
Order enum.Order `json:"order"`
|
||||
}
|
Loading…
Reference in New Issue
Block a user