Merge branch 'ci_gitness' of _OKE5H2PQKOUfzFFDuD4FA/default/CODE/gitness (#273)

This commit is contained in:
Vistaar Juneja 2023-08-11 16:55:54 +00:00 committed by Harness
commit 29c494c23f
73 changed files with 3620 additions and 21 deletions

View File

@ -11,15 +11,19 @@ import (
"context"
cliserver "github.com/harness/gitness/cli/server"
"github.com/harness/gitness/encrypt"
"github.com/harness/gitness/events"
"github.com/harness/gitness/gitrpc"
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"
"github.com/harness/gitness/internal/api/controller/secret"
"github.com/harness/gitness/internal/api/controller/service"
"github.com/harness/gitness/internal/api/controller/serviceaccount"
"github.com/harness/gitness/internal/api/controller/space"
@ -79,6 +83,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
gitrpc.WireSet,
store.WireSet,
check.WireSet,
encrypt.WireSet,
cliserver.ProvideEventsConfig,
events.WireSet,
cliserver.ProvideWebhookConfig,
@ -90,6 +95,9 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
codecomments.WireSet,
gitrpccron.WireSet,
checkcontroller.WireSet,
execution.WireSet,
pipeline.WireSet,
secret.WireSet,
)
return &cliserver.System{}, nil
}

View File

@ -8,16 +8,21 @@ package main
import (
"context"
"github.com/harness/gitness/cli/server"
"github.com/harness/gitness/encrypt"
"github.com/harness/gitness/events"
"github.com/harness/gitness/gitrpc"
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"
"github.com/harness/gitness/internal/api/controller/secret"
"github.com/harness/gitness/internal/api/controller/service"
"github.com/harness/gitness/internal/api/controller/serviceaccount"
"github.com/harness/gitness/internal/api/controller/space"
@ -84,7 +89,17 @@ 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)
spaceController := space.ProvideController(db, provider, pathUID, authorizer, pathStore, spaceStore, repoStore, principalStore, repoController, membershipStore)
executionStore := database.ProvideExecutionStore(db)
pipelineStore := database.ProvidePipelineStore(db)
executionController := execution.ProvideController(db, authorizer, executionStore, pipelineStore, spaceStore)
secretStore := database.ProvideSecretStore(db)
spaceController := space.ProvideController(db, provider, pathUID, authorizer, pathStore, pipelineStore, secretStore, spaceStore, repoStore, principalStore, repoController, membershipStore)
pipelineController := pipeline.ProvideController(db, pathUID, pathStore, repoStore, authorizer, pipelineStore, spaceStore)
encrypter, err := encrypt.ProvideEncrypter(config)
if err != nil {
return nil, err
}
secretController := secret.ProvideController(db, pathUID, pathStore, encrypter, secretStore, authorizer, spaceStore)
pullReqStore := database.ProvidePullReqStore(db, principalInfoCache)
pullReqActivityStore := database.ProvidePullReqActivityStore(db, principalInfoCache)
codeCommentView := database.ProvideCodeCommentView(db)
@ -138,7 +153,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, secretController, 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)

84
encrypt/aesgcm.go Normal file
View File

@ -0,0 +1,84 @@
// Copyright 2023 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 encrypt
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"io"
)
// Aesgcm provides an encrypter that uses the aesgcm encryption
// algorithm.
type Aesgcm struct {
block cipher.Block
Compat bool
}
// Encrypt encrypts the plaintext using aesgcm.
func (e *Aesgcm) Encrypt(plaintext string) ([]byte, error) {
gcm, err := cipher.NewGCM(e.block)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
_, err = io.ReadFull(rand.Reader, nonce)
if err != nil {
return nil, err
}
return gcm.Seal(nonce, nonce, []byte(plaintext), nil), nil
}
// Decrypt decrypts the ciphertext using aesgcm.
func (e *Aesgcm) Decrypt(ciphertext []byte) (string, error) {
gcm, err := cipher.NewGCM(e.block)
if err != nil {
return "", err
}
if len(ciphertext) < gcm.NonceSize() {
// if the decryption utility is running in compatibility
// mode, it will return the ciphertext as plain text if
// decryption fails. This should be used when running the
// database in mixed-mode, where there is a mix of encrypted
// and unencrypted content.
if e.Compat {
return string(ciphertext), nil
}
return "", errors.New("malformed ciphertext")
}
plaintext, err := gcm.Open(nil,
ciphertext[:gcm.NonceSize()],
ciphertext[gcm.NonceSize():],
nil,
)
// if the decryption utility is running in compatibility
// mode, it will return the ciphertext as plain text if
// decryption fails. This should be used when running the
// database in mixed-mode, where there is a mix of encrypted
// and unencrypted content.
if err != nil && e.Compat {
return string(ciphertext), nil
}
return string(plaintext), err
}
// New provides a new aesgcm encrypter
func New(key string, compat bool) (Encrypter, error) {
if len(key) != 32 {
return nil, errKeySize
}
b := []byte(key)
block, err := aes.NewCipher(b)
if err != nil {
return nil, err
}
return &Aesgcm{block: block, Compat: compat}, nil
}

20
encrypt/encrypt.go Normal file
View File

@ -0,0 +1,20 @@
// 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 encrypt
import (
"errors"
)
// indicates key size is too small.
var errKeySize = errors.New("encryption key must be 32 bytes")
// Encrypter provides field encryption and decryption.
// Encrypted values are currently limited to strings, which is
// reflected in the interface design.
type Encrypter interface {
Encrypt(plaintext string) ([]byte, error)
Decrypt(ciphertext []byte) (string, error)
}

19
encrypt/none.go Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2023 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 encrypt
// none is an encryption strategy that stores secret
// values in plain text. This is the default strategy
// when no key is specified.
type none struct {
}
func (*none) Encrypt(plaintext string) ([]byte, error) {
return []byte(plaintext), nil
}
func (*none) Decrypt(ciphertext []byte) (string, error) {
return string(ciphertext), nil
}

19
encrypt/wire.go Normal file
View File

@ -0,0 +1,19 @@
package encrypt
import (
"github.com/harness/gitness/types"
"github.com/google/wire"
)
// WireSet provides a wire set for this package.
var WireSet = wire.NewSet(
ProvideEncrypter,
)
func ProvideEncrypter(config *types.Config) (Encrypter, error) {
if config.Encrypter.Secret == "" {
return &none{}, nil
}
return New(config.Encrypter.Secret, config.Encrypter.EncryptMixedContent)
}

View File

@ -0,0 +1,29 @@
// 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 auth
import (
"context"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/internal/auth/authz"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// CheckPipeline checks if a repo specific permission is granted for the current auth session
// in the scope of its parent.
// Returns nil if the permission is granted, otherwise returns an error.
// NotAuthenticated, NotAuthorized, or any underlying error.
func CheckPipeline(ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
parentPath, uid string, permission enum.Permission) error {
scope := &types.Scope{SpacePath: parentPath}
resource := &types.Resource{
Type: enum.ResourceTypePipeline,
Name: uid,
}
return Check(ctx, authorizer, session, scope, resource, permission)
}

View File

@ -0,0 +1,29 @@
// 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 auth
import (
"context"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/internal/auth/authz"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// CheckSecret checks if a repo specific permission is granted for the current auth session
// in the scope of its parent.
// Returns nil if the permission is granted, otherwise returns an error.
// NotAuthenticated, NotAuthorized, or any underlying error.
func CheckSecret(ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
parentPath, uid string, permission enum.Permission) error {
scope := &types.Scope{SpacePath: parentPath}
resource := &types.Resource{
Type: enum.ResourceTypeSecret,
Name: uid,
}
return Check(ctx, authorizer, session, scope, resource, permission)
}

View File

@ -0,0 +1,36 @@
// 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 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
pipelineStore store.PipelineStore
spaceStore store.SpaceStore
}
func NewController(
db *sqlx.DB,
authorizer authz.Authorizer,
executionStore store.ExecutionStore,
pipelineStore store.PipelineStore,
spaceStore store.SpaceStore,
) *Controller {
return &Controller{
db: db,
authorizer: authorizer,
executionStore: executionStore,
pipelineStore: pipelineStore,
spaceStore: spaceStore,
}
}

View File

@ -0,0 +1,66 @@
// 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 execution
import (
"context"
"fmt"
"time"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// TODO: Add more as needed.
type CreateInput struct {
Status string `json:"status"`
}
func (c *Controller) Create(
ctx context.Context,
session *auth.Session,
spaceRef string,
uid string,
in *CreateInput,
) (*types.Execution, error) {
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
if err != nil {
return nil, fmt.Errorf("could not find space: %w", err)
}
pipeline, err := c.pipelineStore.FindByUID(ctx, space.ID, uid)
if err != nil {
return nil, fmt.Errorf("could not find pipeline: %w", err)
}
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, pipeline.UID, enum.PermissionPipelineExecute)
if err != nil {
return nil, fmt.Errorf("failed to authorize: %w", err)
}
pipeline, err = c.pipelineStore.IncrementSeqNum(ctx, pipeline)
if err != nil {
return nil, fmt.Errorf("failed to increment sequence number: %w", err)
}
now := time.Now().UnixMilli()
execution := &types.Execution{
Number: pipeline.Seq,
Status: in.Status,
RepoID: pipeline.RepoID,
PipelineID: pipeline.ID,
Created: now,
Updated: now,
Version: 0,
}
err = c.executionStore.Create(ctx, execution)
if err != nil {
return nil, fmt.Errorf("execution creation failed: %w", err)
}
return execution, nil
}

View File

@ -0,0 +1,41 @@
// 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 execution
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) Delete(
ctx context.Context,
session *auth.Session,
spaceRef string,
pipelineUID string,
executionNum int64,
) error {
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
if err != nil {
return fmt.Errorf("could not find parent space: %w", err)
}
pipeline, err := c.pipelineStore.FindByUID(ctx, space.ID, pipelineUID)
if err != nil {
return fmt.Errorf("could not find pipeline: %w", err)
}
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, pipeline.UID, enum.PermissionPipelineDelete)
if err != nil {
return fmt.Errorf("could not authorize: %w", err)
}
err = c.executionStore.Delete(ctx, pipeline.ID, executionNum)
if err != nil {
return fmt.Errorf("could not delete execution: %w", err)
}
return nil
}

View 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 execution
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) Find(
ctx context.Context,
session *auth.Session,
spaceRef string,
pipelineUID string,
executionNum int64,
) (*types.Execution, error) {
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
if err != nil {
return nil, fmt.Errorf("could not find parent space: %w", err)
}
pipeline, err := c.pipelineStore.FindByUID(ctx, space.ID, pipelineUID)
if err != nil {
return nil, fmt.Errorf("could not find pipeline: %w", err)
}
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, pipeline.UID, enum.PermissionPipelineView)
if err != nil {
return nil, fmt.Errorf("could not authorize: %w", err)
}
return c.executionStore.Find(ctx, pipeline.ID, executionNum)
}

View File

@ -0,0 +1,59 @@
// 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 execution
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) List(
ctx context.Context,
session *auth.Session,
spaceRef string,
pipelineUID string,
pagination types.Pagination,
) ([]*types.Execution, int64, error) {
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
if err != nil {
return nil, 0, fmt.Errorf("failed to find parent space: %w", err)
}
pipeline, err := c.pipelineStore.FindByUID(ctx, space.ID, pipelineUID)
if err != nil {
return nil, 0, fmt.Errorf("failed to find pipeline: %w", err)
}
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, pipeline.UID, enum.PermissionPipelineView)
if err != nil {
return nil, 0, fmt.Errorf("failed to authorize: %w", err)
}
var count int64
var executions []*types.Execution
err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) (err error) {
count, err = c.executionStore.Count(ctx, pipeline.ID)
if err != nil {
return fmt.Errorf("failed to count child executions: %w", err)
}
executions, err = c.executionStore.List(ctx, pipeline.ID, pagination)
if err != nil {
return fmt.Errorf("failed to list child executions: %w", err)
}
return
}, dbtx.TxDefaultReadOnly)
if err != nil {
return executions, count, fmt.Errorf("failed to fetch list: %w", err)
}
return executions, count, nil
}

View File

@ -0,0 +1,57 @@
// 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 execution
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
type UpdateInput struct {
Status string `json:"status"`
}
func (c *Controller) Update(
ctx context.Context,
session *auth.Session,
spaceRef string,
pipelineUID string,
executionNum int64,
in *UpdateInput) (*types.Execution, error) {
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
if err != nil {
return nil, fmt.Errorf("could not find space: %w", err)
}
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, pipelineUID, enum.PermissionPipelineEdit)
if err != nil {
return nil, fmt.Errorf("failed to check auth: %w", err)
}
pipeline, err := c.pipelineStore.FindByUID(ctx, space.ID, pipelineUID)
if err != nil {
return nil, fmt.Errorf("failed to find pipeline: %w", err)
}
execution, err := c.executionStore.Find(ctx, pipeline.ID, executionNum)
if err != nil {
return nil, fmt.Errorf("failed to find execution: %w", err)
}
return c.executionStore.UpdateOptLock(ctx,
execution, func(original *types.Execution) error {
// update values only if provided
if in.Status != "" {
original.Status = in.Status
}
return nil
})
}

View File

@ -0,0 +1,27 @@
// 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 execution
import (
"github.com/harness/gitness/internal/auth/authz"
"github.com/harness/gitness/internal/store"
"github.com/google/wire"
"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,
pipelineStore store.PipelineStore,
spaceStore store.SpaceStore,
) *Controller {
return NewController(db, authorizer, executionStore, pipelineStore, spaceStore)
}

View File

@ -0,0 +1,44 @@
// 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 (
"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,
}
}

View File

@ -0,0 +1,106 @@
// 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"
"strconv"
"strings"
"time"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/api/usererror"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
)
var (
// errPipelineRequiresParent if the user tries to create a pipeline without a parent space.
errPipelineRequiresParent = usererror.BadRequest(
"Parent space required - standalone pipelines are not supported.")
)
type CreateInput struct {
Description string `json:"description"`
SpaceRef string `json:"space_ref"`
UID string `json:"uid"`
RepoRef string `json:"repo_ref"` // empty if repo_type != gitness
RepoType enum.ScmType `json:"repo_type"`
DefaultBranch string `json:"default_branch"`
ConfigPath string `json:"config_path"`
}
func (c *Controller) Create(ctx context.Context, session *auth.Session, in *CreateInput) (*types.Pipeline, error) {
parentSpace, err := c.spaceStore.FindByRef(ctx, in.SpaceRef)
if err != nil {
return nil, fmt.Errorf("could not find parent by ref: %w", err)
}
err = apiauth.CheckPipeline(ctx, c.authorizer, session, parentSpace.Path, in.UID, enum.PermissionPipelineEdit)
if err != nil {
return nil, 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
now := time.Now().UnixMilli()
pipeline = &types.Pipeline{
Description: in.Description,
SpaceID: 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 nil, fmt.Errorf("pipeline creation failed: %w", err)
}
return pipeline, nil
}
func (c *Controller) sanitizeCreateInput(in *CreateInput) error {
parentRefAsID, err := strconv.ParseInt(in.SpaceRef, 10, 64)
if (err == nil && parentRefAsID <= 0) || (len(strings.TrimSpace(in.SpaceRef)) == 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
}

View File

@ -0,0 +1,31 @@
// 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"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types/enum"
)
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 fmt.Errorf("could not find parent space: %w", err)
}
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, uid, enum.PermissionPipelineDelete)
if err != nil {
return fmt.Errorf("could not authorize: %w", err)
}
err = c.pipelineStore.DeleteByUID(ctx, space.ID, uid)
if err != nil {
return fmt.Errorf("could not delete pipeline: %w", err)
}
return nil
}

View File

@ -0,0 +1,32 @@
// 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"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
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, fmt.Errorf("could not find parent space: %w", err)
}
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, uid, enum.PermissionPipelineView)
if err != nil {
return nil, fmt.Errorf("could not authorize: %w", err)
}
return c.pipelineStore.FindByUID(ctx, space.ID, uid)
}

View File

@ -0,0 +1,58 @@
// 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"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
type UpdateInput struct {
Description string `json:"description"`
UID string `json:"uid"`
ConfigPath string `json:"config_path"`
}
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, fmt.Errorf("could not find parent space: %w", err)
}
err = apiauth.CheckPipeline(ctx, c.authorizer, session, space.Path, uid, enum.PermissionPipelineEdit)
if err != nil {
return nil, fmt.Errorf("could not authorize: %w", err)
}
pipeline, err := c.pipelineStore.FindByUID(ctx, space.ID, uid)
if err != nil {
return nil, fmt.Errorf("could not find pipeline: %w", err)
}
return c.pipelineStore.UpdateOptLock(ctx, pipeline, func(pipeline *types.Pipeline) error {
if in.Description != "" {
pipeline.Description = in.Description
}
if in.UID != "" {
pipeline.UID = in.UID
}
if in.ConfigPath != "" {
pipeline.ConfigPath = in.ConfigPath
}
return nil
})
}

View File

@ -0,0 +1,30 @@
// 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 (
"github.com/harness/gitness/internal/auth/authz"
"github.com/harness/gitness/internal/store"
"github.com/harness/gitness/types/check"
"github.com/google/wire"
"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)
}

View File

@ -0,0 +1,44 @@
// 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 secret
import (
"github.com/harness/gitness/encrypt"
"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 {
db *sqlx.DB
uidCheck check.PathUID
pathStore store.PathStore
encrypter encrypt.Encrypter
secretStore store.SecretStore
authorizer authz.Authorizer
spaceStore store.SpaceStore
}
func NewController(
db *sqlx.DB,
uidCheck check.PathUID,
authorizer authz.Authorizer,
pathStore store.PathStore,
encrypter encrypt.Encrypter,
secretStore store.SecretStore,
spaceStore store.SpaceStore,
) *Controller {
return &Controller{
db: db,
uidCheck: uidCheck,
pathStore: pathStore,
encrypter: encrypter,
secretStore: secretStore,
authorizer: authorizer,
spaceStore: spaceStore,
}
}

View File

@ -0,0 +1,122 @@
// 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 secret
import (
"context"
"fmt"
"strconv"
"strings"
"time"
"github.com/harness/gitness/encrypt"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/api/usererror"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
)
var (
// errSecretRequiresParent if the user tries to create a secret without a parent space.
errSecretRequiresParent = usererror.BadRequest(
"Parent space required - standalone secret are not supported.")
)
type CreateInput struct {
Description string `json:"description"`
SpaceRef string `json:"space_ref"` // Ref of the parent space
UID string `json:"uid"`
Data string `json:"data"`
}
func (c *Controller) Create(ctx context.Context, session *auth.Session, in *CreateInput) (*types.Secret, error) {
parentSpace, err := c.spaceStore.FindByRef(ctx, in.SpaceRef)
if err != nil {
return nil, fmt.Errorf("could not find parent by ref: %w", err)
}
err = apiauth.CheckSecret(ctx, c.authorizer, session, parentSpace.Path, in.UID, enum.PermissionSecretEdit)
if err != nil {
return nil, err
}
if err := c.sanitizeCreateInput(in); err != nil {
return nil, fmt.Errorf("failed to sanitize input: %w", err)
}
var secret *types.Secret
now := time.Now().UnixMilli()
secret = &types.Secret{
Description: in.Description,
Data: in.Data,
SpaceID: parentSpace.ID,
UID: in.UID,
Created: now,
Updated: now,
Version: 0,
}
secret, err = enc(c.encrypter, secret)
if err != nil {
return nil, fmt.Errorf("could not encrypt secret: %w", err)
}
err = c.secretStore.Create(ctx, secret)
if err != nil {
return nil, fmt.Errorf("secret creation failed: %w", err)
}
if err != nil {
return nil, err
}
return secret, nil
}
func (c *Controller) sanitizeCreateInput(in *CreateInput) error {
parentRefAsID, err := strconv.ParseInt(in.SpaceRef, 10, 64)
if (err == nil && parentRefAsID <= 0) || (len(strings.TrimSpace(in.SpaceRef)) == 0) {
return errSecretRequiresParent
}
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
}
return nil
}
// helper function returns the same secret with encrypted data.
func enc(encrypt encrypt.Encrypter, secret *types.Secret) (*types.Secret, error) {
if secret == nil {
return nil, fmt.Errorf("cannot encrypt a nil secret")
}
s := *secret
ciphertext, err := encrypt.Encrypt(secret.Data)
if err != nil {
return nil, err
}
s.Data = string(ciphertext)
return &s, nil
}
// helper function returns the same secret with decrypted data.
func dec(encrypt encrypt.Encrypter, secret *types.Secret) (*types.Secret, error) {
if secret == nil {
return nil, fmt.Errorf("cannot decrypt a nil secret")
}
s := *secret
plaintext, err := encrypt.Decrypt([]byte(secret.Data))
if err != nil {
return nil, err
}
s.Data = plaintext
return &s, nil
}

View File

@ -0,0 +1,31 @@
// 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 secret
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types/enum"
)
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 fmt.Errorf("could not find space: %w", err)
}
err = apiauth.CheckSecret(ctx, c.authorizer, session, space.Path, uid, enum.PermissionSecretDelete)
if err != nil {
return fmt.Errorf("failed to authorize: %w", err)
}
err = c.secretStore.DeleteByUID(ctx, space.ID, uid)
if err != nil {
return fmt.Errorf("could not delete secret: %w", err)
}
return nil
}

View 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 secret
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) Find(
ctx context.Context,
session *auth.Session,
spaceRef string,
uid string,
) (*types.Secret, error) {
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
if err != nil {
return nil, fmt.Errorf("could not find space: %w", err)
}
err = apiauth.CheckSecret(ctx, c.authorizer, session, space.Path, uid, enum.PermissionSecretView)
if err != nil {
return nil, fmt.Errorf("failed to authorize: %w", err)
}
secret, err := c.secretStore.FindByUID(ctx, space.ID, uid)
if err != nil {
return nil, fmt.Errorf("could not find secret: %w", err)
}
secret, err = dec(c.encrypter, secret)
if err != nil {
return nil, fmt.Errorf("could not decrypt secret: %w", err)
}
return secret, nil
}

View File

@ -0,0 +1,63 @@
// 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 secret
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// UpdateInput is used for updating a repo.
type UpdateInput struct {
Description string `json:"description"`
UID string `json:"uid"`
Data string `json:"data"`
}
func (c *Controller) Update(
ctx context.Context,
session *auth.Session,
spaceRef string,
uid string,
in *UpdateInput,
) (*types.Secret, error) {
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
if err != nil {
return nil, fmt.Errorf("could not find space: %w", err)
}
err = apiauth.CheckSecret(ctx, c.authorizer, session, space.Path, uid, enum.PermissionSecretEdit)
if err != nil {
return nil, fmt.Errorf("failed to authorize: %w", err)
}
secret, err := c.secretStore.FindByUID(ctx, space.ID, uid)
if err != nil {
return nil, fmt.Errorf("could not find secret: %w", err)
}
return c.secretStore.UpdateOptLock(ctx, secret, func(original *types.Secret) error {
if in.Description != "" {
original.Description = in.Description
}
if in.Data != "" {
data, err := c.encrypter.Encrypt(original.Data)
if err != nil {
return fmt.Errorf("could not encrypt secret: %w", err)
}
original.Data = string(data)
}
if in.UID != "" {
original.UID = in.UID
}
return nil
})
}

View File

@ -0,0 +1,31 @@
// 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 secret
import (
"github.com/harness/gitness/encrypt"
"github.com/harness/gitness/internal/auth/authz"
"github.com/harness/gitness/internal/store"
"github.com/harness/gitness/types/check"
"github.com/google/wire"
"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,
encrypter encrypt.Encrypter,
secretStore store.SecretStore,
authorizer authz.Authorizer,
spaceStore store.SpaceStore,
) *Controller {
return NewController(db, uidCheck, authorizer, pathStore, encrypter, secretStore, spaceStore)
}

View File

@ -20,6 +20,8 @@ type Controller struct {
uidCheck check.PathUID
authorizer authz.Authorizer
pathStore store.PathStore
pipelineStore store.PipelineStore
secretStore store.SecretStore
spaceStore store.SpaceStore
repoStore store.RepoStore
principalStore store.PrincipalStore
@ -29,9 +31,9 @@ type Controller struct {
func NewController(db *sqlx.DB, urlProvider *url.Provider,
uidCheck check.PathUID, authorizer authz.Authorizer,
pathStore store.PathStore, spaceStore store.SpaceStore,
repoStore store.RepoStore, principalStore store.PrincipalStore, repoCtrl *repo.Controller,
membershipStore store.MembershipStore,
pathStore store.PathStore, pipelineStore store.PipelineStore, secretStore store.SecretStore,
spaceStore store.SpaceStore, repoStore store.RepoStore, principalStore store.PrincipalStore,
repoCtrl *repo.Controller, membershipStore store.MembershipStore,
) *Controller {
return &Controller{
db: db,
@ -39,6 +41,8 @@ func NewController(db *sqlx.DB, urlProvider *url.Provider,
uidCheck: uidCheck,
authorizer: authorizer,
pathStore: pathStore,
pipelineStore: pipelineStore,
secretStore: secretStore,
spaceStore: spaceStore,
repoStore: repoStore,
principalStore: principalStore,

View 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 space
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// ListPipelines lists the pipelines in a space.
func (c *Controller) ListPipelines(
ctx context.Context,
session *auth.Session,
spaceRef string,
filter types.ListQueryFilter,
) ([]*types.Pipeline, int64, error) {
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
if err != nil {
return nil, 0, fmt.Errorf("failed to find parent space: %w", err)
}
err = apiauth.CheckSpace(ctx, c.authorizer, session, space, enum.PermissionPipelineView, false)
if err != nil {
return nil, 0, fmt.Errorf("could not authorize: %w", err)
}
var count int64
var pipelines []*types.Pipeline
err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) (err error) {
count, err = c.pipelineStore.Count(ctx, space.ID, filter)
if err != nil {
return fmt.Errorf("failed to count child executions: %w", err)
}
pipelines, err = c.pipelineStore.List(ctx, space.ID, filter)
if err != nil {
return fmt.Errorf("failed to count child executions: %w", err)
}
return
}, dbtx.TxDefaultReadOnly)
if err != nil {
return pipelines, count, fmt.Errorf("failed to list pipelines: %w", err)
}
return pipelines, count, nil
}

View 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 space
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/internal/api/auth"
"github.com/harness/gitness/internal/auth"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// ListSecrets lists the secrets in a space.
func (c *Controller) ListSecrets(
ctx context.Context,
session *auth.Session,
spaceRef string,
filter types.ListQueryFilter,
) ([]*types.Secret, int64, error) {
space, err := c.spaceStore.FindByRef(ctx, spaceRef)
if err != nil {
return nil, 0, fmt.Errorf("failed to find parent space: %w", err)
}
err = apiauth.CheckSpace(ctx, c.authorizer, session, space, enum.PermissionSecretView, false)
if err != nil {
return nil, 0, fmt.Errorf("could not authorize: %w", err)
}
var count int64
var secrets []*types.Secret
err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) (err error) {
count, err = c.secretStore.Count(ctx, space.ID, filter)
if err != nil {
return fmt.Errorf("failed to count child executions: %w", err)
}
secrets, err = c.secretStore.List(ctx, space.ID, filter)
if err != nil {
return fmt.Errorf("failed to list child executions: %w", err)
}
return
}, dbtx.TxDefaultReadOnly)
if err != nil {
return secrets, count, fmt.Errorf("failed to list secrets: %w", err)
}
return secrets, count, nil
}

View File

@ -21,12 +21,11 @@ var WireSet = wire.NewSet(
)
func ProvideController(db *sqlx.DB, urlProvider *url.Provider, uidCheck check.PathUID, authorizer authz.Authorizer,
pathStore store.PathStore, spaceStore store.SpaceStore, repoStore store.RepoStore,
principalStore store.PrincipalStore, repoCtrl *repo.Controller,
membershipStore store.MembershipStore,
pathStore store.PathStore, pipelineStore store.PipelineStore, secretStore store.SecretStore,
spaceStore store.SpaceStore, repoStore store.RepoStore, principalStore store.PrincipalStore,
repoCtrl *repo.Controller, membershipStore store.MembershipStore,
) *Controller {
return NewController(db, urlProvider, uidCheck, authorizer,
pathStore, spaceStore, repoStore,
principalStore, repoCtrl,
membershipStore)
pathStore, pipelineStore, secretStore, spaceStore, repoStore,
principalStore, repoCtrl, membershipStore)
}

View File

@ -0,0 +1,47 @@
// 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 execution
import (
"encoding/json"
"net/http"
"github.com/harness/gitness/internal/api/controller/execution"
"github.com/harness/gitness/internal/api/render"
"github.com/harness/gitness/internal/api/request"
"github.com/harness/gitness/internal/paths"
)
func HandleCreate(executionCtrl *execution.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
pipelineRef, err := request.GetPipelineRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
if err != nil {
render.TranslatedUserError(w, err)
return
}
in := new(execution.CreateInput)
err = json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(w, "Invalid Request Body: %s.", err)
return
}
execution, err := executionCtrl.Create(ctx, session, spaceRef, pipelineUID, in)
if err != nil {
render.TranslatedUserError(w, err)
return
}
render.JSON(w, http.StatusCreated, execution)
}
}

View File

@ -0,0 +1,44 @@
// 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 execution
import (
"net/http"
"github.com/harness/gitness/internal/api/controller/execution"
"github.com/harness/gitness/internal/api/render"
"github.com/harness/gitness/internal/api/request"
"github.com/harness/gitness/internal/paths"
)
func HandleDelete(executionCtrl *execution.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
pipelineRef, err := request.GetPipelineRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
if err != nil {
render.TranslatedUserError(w, err)
return
}
n, err := request.GetExecutionNumberFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
err = executionCtrl.Delete(ctx, session, spaceRef, pipelineUID, n)
if err != nil {
render.TranslatedUserError(w, err)
return
}
render.DeleteSuccessful(w)
}
}

View File

@ -0,0 +1,44 @@
// 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 execution
import (
"net/http"
"github.com/harness/gitness/internal/api/controller/execution"
"github.com/harness/gitness/internal/api/render"
"github.com/harness/gitness/internal/api/request"
"github.com/harness/gitness/internal/paths"
)
func HandleFind(executionCtrl *execution.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
pipelineRef, err := request.GetPipelineRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
n, err := request.GetExecutionNumberFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
if err != nil {
render.TranslatedUserError(w, err)
return
}
execution, err := executionCtrl.Find(ctx, session, spaceRef, pipelineUID, n)
if err != nil {
render.TranslatedUserError(w, err)
return
}
render.JSON(w, http.StatusOK, execution)
}
}

View File

@ -0,0 +1,42 @@
// 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 execution
import (
"net/http"
"github.com/harness/gitness/internal/api/controller/execution"
"github.com/harness/gitness/internal/api/render"
"github.com/harness/gitness/internal/api/request"
"github.com/harness/gitness/internal/paths"
)
func HandleList(executionCtrl *execution.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
pipelineRef, err := request.GetPipelineRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
if err != nil {
render.TranslatedUserError(w, err)
return
}
pagination := request.ParsePaginationFromRequest(r)
repos, totalCount, err := executionCtrl.List(ctx, session, spaceRef, pipelineUID, pagination)
if err != nil {
render.TranslatedUserError(w, err)
return
}
render.Pagination(r, w, pagination.Page, pagination.Size, int(totalCount))
render.JSON(w, http.StatusOK, repos)
}
}

View File

@ -0,0 +1,53 @@
// 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 execution
import (
"encoding/json"
"net/http"
"github.com/harness/gitness/internal/api/controller/execution"
"github.com/harness/gitness/internal/api/render"
"github.com/harness/gitness/internal/api/request"
"github.com/harness/gitness/internal/paths"
)
func HandleUpdate(executionCtrl *execution.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
in := new(execution.UpdateInput)
err := json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(w, "Invalid Request Body: %s.", err)
return
}
pipelineRef, err := request.GetPipelineRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
if err != nil {
render.TranslatedUserError(w, err)
return
}
n, err := request.GetExecutionNumberFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
pipeline, err := executionCtrl.Update(ctx, session, spaceRef, pipelineUID, n, in)
if err != nil {
render.TranslatedUserError(w, err)
return
}
render.JSON(w, http.StatusOK, pipeline)
}
}

View File

@ -0,0 +1,36 @@
// 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 (
"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"
)
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)
}
}

View File

@ -0,0 +1,39 @@
// 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"
"github.com/harness/gitness/internal/paths"
)
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.GetPipelineRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
if err != nil {
render.TranslatedUserError(w, err)
return
}
err = pipelineCtrl.Delete(ctx, session, spaceRef, pipelineUID)
if err != nil {
render.TranslatedUserError(w, err)
return
}
render.DeleteSuccessful(w)
}
}

View File

@ -0,0 +1,39 @@
// 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"
"github.com/harness/gitness/internal/paths"
)
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.GetPipelineRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
if err != nil {
render.TranslatedUserError(w, err)
return
}
pipeline, err := pipelineCtrl.Find(ctx, session, spaceRef, pipelineUID)
if err != nil {
render.TranslatedUserError(w, err)
return
}
render.JSON(w, http.StatusOK, pipeline)
}
}

View File

@ -0,0 +1,48 @@
// 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 (
"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"
"github.com/harness/gitness/internal/paths"
)
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.GetPipelineRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
spaceRef, pipelineUID, err := paths.DisectLeaf(pipelineRef)
if err != nil {
render.TranslatedUserError(w, err)
return
}
pipeline, err := pipelineCtrl.Update(ctx, session, spaceRef, pipelineUID, in)
if err != nil {
render.TranslatedUserError(w, err)
return
}
render.JSON(w, http.StatusOK, pipeline)
}
}

View File

@ -0,0 +1,37 @@
// 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 secret
import (
"encoding/json"
"net/http"
"github.com/harness/gitness/internal/api/controller/secret"
"github.com/harness/gitness/internal/api/render"
"github.com/harness/gitness/internal/api/request"
)
// HandleCreate returns a http.HandlerFunc that creates a new secretsitory.
func HandleCreate(secretCtrl *secret.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
in := new(secret.CreateInput)
err := json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(w, "Invalid Request Body: %s.", err)
return
}
secret, err := secretCtrl.Create(ctx, session, in)
if err != nil {
render.TranslatedUserError(w, err)
return
}
render.JSON(w, http.StatusCreated, secret.CopyWithoutData())
}
}

View File

@ -0,0 +1,39 @@
// 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 secret
import (
"net/http"
"github.com/harness/gitness/internal/api/controller/secret"
"github.com/harness/gitness/internal/api/render"
"github.com/harness/gitness/internal/api/request"
"github.com/harness/gitness/internal/paths"
)
func HandleDelete(secretCtrl *secret.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
secretRef, err := request.GetSecretRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
spaceRef, secretUID, err := paths.DisectLeaf(secretRef)
if err != nil {
render.TranslatedUserError(w, err)
return
}
err = secretCtrl.Delete(ctx, session, spaceRef, secretUID)
if err != nil {
render.TranslatedUserError(w, err)
return
}
render.DeleteSuccessful(w)
}
}

View File

@ -0,0 +1,39 @@
// 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 secret
import (
"net/http"
"github.com/harness/gitness/internal/api/controller/secret"
"github.com/harness/gitness/internal/api/render"
"github.com/harness/gitness/internal/api/request"
"github.com/harness/gitness/internal/paths"
)
// HandleFind finds a secret from the database.
func HandleFind(secretCtrl *secret.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
secretRef, err := request.GetSecretRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
spaceRef, secretUID, err := paths.DisectLeaf(secretRef)
if err != nil {
render.TranslatedUserError(w, err)
}
secret, err := secretCtrl.Find(ctx, session, spaceRef, secretUID)
if err != nil {
render.TranslatedUserError(w, err)
return
}
render.JSON(w, http.StatusOK, secret.CopyWithoutData())
}
}

View File

@ -0,0 +1,47 @@
// 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 secret
import (
"encoding/json"
"net/http"
"github.com/harness/gitness/internal/api/controller/secret"
"github.com/harness/gitness/internal/api/render"
"github.com/harness/gitness/internal/api/request"
"github.com/harness/gitness/internal/paths"
)
func HandleUpdate(secretCtrl *secret.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
in := new(secret.UpdateInput)
err := json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(w, "Invalid Request Body: %s.", err)
return
}
secretRef, err := request.GetSecretRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
spaceRef, secretUID, err := paths.DisectLeaf(secretRef)
if err != nil {
render.TranslatedUserError(w, err)
}
secret, err := secretCtrl.Update(ctx, session, spaceRef, secretUID, in)
if err != nil {
render.TranslatedUserError(w, err)
return
}
render.JSON(w, http.StatusOK, secret.CopyWithoutData())
}
}

View File

@ -0,0 +1,35 @@
// 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 space
import (
"net/http"
"github.com/harness/gitness/internal/api/controller/space"
"github.com/harness/gitness/internal/api/render"
"github.com/harness/gitness/internal/api/request"
)
func HandleListPipelines(spaceCtrl *space.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
spaceRef, err := request.GetSpaceRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
filter := request.ParseListQueryFilterFromRequest(r)
repos, totalCount, err := spaceCtrl.ListPipelines(ctx, session, spaceRef, filter)
if err != nil {
render.TranslatedUserError(w, err)
return
}
render.Pagination(r, w, filter.Page, filter.Size, int(totalCount))
render.JSON(w, http.StatusOK, repos)
}
}

View File

@ -0,0 +1,42 @@
// 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 space
import (
"net/http"
"github.com/harness/gitness/internal/api/controller/space"
"github.com/harness/gitness/internal/api/render"
"github.com/harness/gitness/internal/api/request"
"github.com/harness/gitness/types"
)
func HandleListSecrets(spaceCtrl *space.Controller) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
spaceRef, err := request.GetSpaceRefFromPath(r)
if err != nil {
render.TranslatedUserError(w, err)
return
}
filter := request.ParseListQueryFilterFromRequest(r)
ret, totalCount, err := spaceCtrl.ListSecrets(ctx, session, spaceRef, filter)
if err != nil {
render.TranslatedUserError(w, err)
return
}
// Strip out data in the returned value
secrets := []types.Secret{}
for _, s := range ret {
secrets = append(secrets, *s.CopyWithoutData())
}
render.Pagination(r, w, filter.Page, filter.Size, int(totalCount))
render.JSON(w, http.StatusOK, secrets)
}
}

View File

@ -41,6 +41,8 @@ func Generate() *openapi3.Spec {
buildPrincipals(&reflector)
spaceOperations(&reflector)
repoOperations(&reflector)
pipelineOperations(&reflector)
secretOperations(&reflector)
resourceOperations(&reflector)
pullReqOperations(&reflector)
webhookOperations(&reflector)

View File

@ -0,0 +1,162 @@
// 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 openapi
import (
"net/http"
"github.com/harness/gitness/internal/api/controller/execution"
"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 pipelineRequest struct {
Ref string `path:"pipeline_ref"`
}
type executionRequest struct {
pipelineRequest
Number string `path:"execution_number"`
}
type createExecutionRequest struct {
pipelineRequest
execution.CreateInput
}
type createPipelineRequest struct {
pipeline.CreateInput
}
type getExecutionRequest struct {
executionRequest
}
type getPipelineRequest struct {
pipelineRequest
}
type updateExecutionRequest struct {
executionRequest
execution.UpdateInput
}
type updatePipelineRequest struct {
pipelineRequest
pipeline.UpdateInput
}
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(getPipelineRequest), http.MethodGet)
_ = reflector.SetJSONResponse(&opFind, new(types.Pipeline), 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(getPipelineRequest), 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)
executionCreate := openapi3.Operation{}
executionCreate.WithTags("pipeline")
executionCreate.WithMapOfAnything(map[string]interface{}{"operationId": "createExecution"})
_ = reflector.SetRequest(&executionCreate, new(createExecutionRequest), http.MethodPost)
_ = reflector.SetJSONResponse(&executionCreate, new(types.Execution), http.StatusCreated)
_ = reflector.SetJSONResponse(&executionCreate, new(usererror.Error), http.StatusBadRequest)
_ = reflector.SetJSONResponse(&executionCreate, new(usererror.Error), http.StatusInternalServerError)
_ = reflector.SetJSONResponse(&executionCreate, new(usererror.Error), http.StatusUnauthorized)
_ = reflector.SetJSONResponse(&executionCreate, new(usererror.Error), http.StatusForbidden)
_ = reflector.Spec.AddOperation(http.MethodPost,
"/pipelines/{pipeline_ref}/executions", executionCreate)
executionFind := openapi3.Operation{}
executionFind.WithTags("pipeline")
executionFind.WithMapOfAnything(map[string]interface{}{"operationId": "findExecution"})
_ = reflector.SetRequest(&executionFind, new(getExecutionRequest), http.MethodGet)
_ = reflector.SetJSONResponse(&executionFind, new(types.Execution), http.StatusOK)
_ = reflector.SetJSONResponse(&executionFind, new(usererror.Error), http.StatusInternalServerError)
_ = reflector.SetJSONResponse(&executionFind, new(usererror.Error), http.StatusUnauthorized)
_ = reflector.SetJSONResponse(&executionFind, new(usererror.Error), http.StatusForbidden)
_ = reflector.SetJSONResponse(&executionFind, new(usererror.Error), http.StatusNotFound)
_ = reflector.Spec.AddOperation(http.MethodGet,
"/pipelines/{pipeline_ref}/executions/{execution_number}", executionFind)
executionDelete := openapi3.Operation{}
executionDelete.WithTags("pipeline")
executionDelete.WithMapOfAnything(map[string]interface{}{"operationId": "deleteExecution"})
_ = reflector.SetRequest(&executionDelete, new(getExecutionRequest), http.MethodDelete)
_ = reflector.SetJSONResponse(&executionDelete, nil, http.StatusNoContent)
_ = reflector.SetJSONResponse(&executionDelete, new(usererror.Error), http.StatusInternalServerError)
_ = reflector.SetJSONResponse(&executionDelete, new(usererror.Error), http.StatusUnauthorized)
_ = reflector.SetJSONResponse(&executionDelete, new(usererror.Error), http.StatusForbidden)
_ = reflector.SetJSONResponse(&executionDelete, new(usererror.Error), http.StatusNotFound)
_ = reflector.Spec.AddOperation(http.MethodDelete,
"/pipelines/{pipeline_ref}/executions/{execution_number}", executionDelete)
executionUpdate := openapi3.Operation{}
executionUpdate.WithTags("pipeline")
executionUpdate.WithMapOfAnything(map[string]interface{}{"operationId": "updateExecution"})
_ = reflector.SetRequest(&executionUpdate, new(updateExecutionRequest), http.MethodPatch)
_ = reflector.SetJSONResponse(&executionUpdate, new(types.Execution), http.StatusOK)
_ = reflector.SetJSONResponse(&executionUpdate, new(usererror.Error), http.StatusBadRequest)
_ = reflector.SetJSONResponse(&executionUpdate, new(usererror.Error), http.StatusInternalServerError)
_ = reflector.SetJSONResponse(&executionUpdate, new(usererror.Error), http.StatusUnauthorized)
_ = reflector.SetJSONResponse(&executionUpdate, new(usererror.Error), http.StatusForbidden)
_ = reflector.SetJSONResponse(&executionUpdate, new(usererror.Error), http.StatusNotFound)
_ = reflector.Spec.AddOperation(http.MethodPatch,
"/pipelines/{pipeline_ref}/executions/{execution_number}", executionUpdate)
executionList := openapi3.Operation{}
executionList.WithTags("pipeline")
executionList.WithMapOfAnything(map[string]interface{}{"operationId": "listExecutions"})
executionList.WithParameters(queryParameterPage, queryParameterLimit)
_ = reflector.SetRequest(&executionList, new(pipelineRequest), http.MethodGet)
_ = reflector.SetJSONResponse(&executionList, []types.Execution{}, http.StatusOK)
_ = reflector.SetJSONResponse(&executionList, new(usererror.Error), http.StatusInternalServerError)
_ = reflector.SetJSONResponse(&executionList, new(usererror.Error), http.StatusUnauthorized)
_ = reflector.SetJSONResponse(&executionList, new(usererror.Error), http.StatusForbidden)
_ = reflector.SetJSONResponse(&executionList, new(usererror.Error), http.StatusNotFound)
_ = reflector.Spec.AddOperation(http.MethodGet,
"/pipelines/{pipeline_ref}/executions", executionList)
}

View File

@ -0,0 +1,79 @@
// 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 openapi
import (
"net/http"
"github.com/harness/gitness/internal/api/controller/secret"
"github.com/harness/gitness/internal/api/usererror"
"github.com/harness/gitness/types"
"github.com/swaggest/openapi-go/openapi3"
)
type createSecretRequest struct {
secret.CreateInput
}
type secretRequest struct {
Ref string `path:"secret_ref"`
}
type getSecretRequest struct {
secretRequest
}
type updateSecretRequest struct {
secretRequest
secret.UpdateInput
}
func secretOperations(reflector *openapi3.Reflector) {
opCreate := openapi3.Operation{}
opCreate.WithTags("secret")
opCreate.WithMapOfAnything(map[string]interface{}{"operationId": "createSecret"})
_ = reflector.SetRequest(&opCreate, new(createSecretRequest), http.MethodPost)
_ = reflector.SetJSONResponse(&opCreate, new(types.Secret), 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, "/secrets", opCreate)
opFind := openapi3.Operation{}
opFind.WithTags("secret")
opFind.WithMapOfAnything(map[string]interface{}{"operationId": "findSecret"})
_ = reflector.SetRequest(&opFind, new(getSecretRequest), http.MethodGet)
_ = reflector.SetJSONResponse(&opFind, new(types.Secret), 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, "/secrets/{secret_ref}", opFind)
opDelete := openapi3.Operation{}
opDelete.WithTags("secret")
opDelete.WithMapOfAnything(map[string]interface{}{"operationId": "deleteSecret"})
_ = reflector.SetRequest(&opDelete, new(getSecretRequest), 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, "/secrets/{secret_ref}", opDelete)
opUpdate := openapi3.Operation{}
opUpdate.WithTags("secret")
opUpdate.WithMapOfAnything(map[string]interface{}{"operationId": "updateSecret"})
_ = reflector.SetRequest(&opUpdate, new(updateSecretRequest), http.MethodPatch)
_ = reflector.SetJSONResponse(&opUpdate, new(types.Secret), 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, "/secrets/{secret_ref}", opUpdate)
}

View File

@ -230,6 +230,30 @@ func spaceOperations(reflector *openapi3.Reflector) {
_ = reflector.SetJSONResponse(&opRepos, new(usererror.Error), http.StatusNotFound)
_ = reflector.Spec.AddOperation(http.MethodGet, "/spaces/{space_ref}/repos", opRepos)
opPipelines := openapi3.Operation{}
opPipelines.WithTags("space")
opPipelines.WithMapOfAnything(map[string]interface{}{"operationId": "listPipelines"})
opPipelines.WithParameters(queryParameterQueryRepo, queryParameterPage, queryParameterLimit)
_ = reflector.SetRequest(&opPipelines, new(spaceRequest), http.MethodGet)
_ = reflector.SetJSONResponse(&opPipelines, []types.Pipeline{}, http.StatusOK)
_ = reflector.SetJSONResponse(&opPipelines, new(usererror.Error), http.StatusInternalServerError)
_ = reflector.SetJSONResponse(&opPipelines, new(usererror.Error), http.StatusUnauthorized)
_ = reflector.SetJSONResponse(&opPipelines, new(usererror.Error), http.StatusForbidden)
_ = reflector.SetJSONResponse(&opPipelines, new(usererror.Error), http.StatusNotFound)
_ = reflector.Spec.AddOperation(http.MethodGet, "/spaces/{space_ref}/pipelines", opPipelines)
opSecrets := openapi3.Operation{}
opSecrets.WithTags("space")
opSecrets.WithMapOfAnything(map[string]interface{}{"operationId": "listSecrets"})
opSecrets.WithParameters(queryParameterQueryRepo, queryParameterPage, queryParameterLimit)
_ = reflector.SetRequest(&opSecrets, new(spaceRequest), http.MethodGet)
_ = reflector.SetJSONResponse(&opSecrets, []types.Secret{}, http.StatusOK)
_ = reflector.SetJSONResponse(&opSecrets, new(usererror.Error), http.StatusInternalServerError)
_ = reflector.SetJSONResponse(&opSecrets, new(usererror.Error), http.StatusUnauthorized)
_ = reflector.SetJSONResponse(&opSecrets, new(usererror.Error), http.StatusForbidden)
_ = reflector.SetJSONResponse(&opSecrets, new(usererror.Error), http.StatusNotFound)
_ = reflector.Spec.AddOperation(http.MethodGet, "/spaces/{space_ref}/secrets", opSecrets)
opServiceAccounts := openapi3.Operation{}
opServiceAccounts.WithTags("space")
opServiceAccounts.WithMapOfAnything(map[string]interface{}{"operationId": "listServiceAccounts"})

View File

@ -0,0 +1,29 @@
// 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 (
PathParamPipelineRef = "pipeline_ref"
PathParamExecutionNumber = "execution_number"
)
func GetPipelineRefFromPath(r *http.Request) (string, error) {
rawRef, err := PathParamOrError(r, PathParamPipelineRef)
if err != nil {
return "", err
}
// paths are unescaped
return url.PathUnescape(rawRef)
}
func GetExecutionNumberFromPath(r *http.Request) (int64, error) {
return PathParamAsPositiveInt64(r, PathParamExecutionNumber)
}

View File

@ -0,0 +1,24 @@
// 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 (
PathParamSecretRef = "secret_ref"
)
func GetSecretRefFromPath(r *http.Request) (string, error) {
rawRef, err := PathParamOrError(r, PathParamSecretRef)
if err != nil {
return "", err
}
// paths are unescaped
return url.PathUnescape(rawRef)
}

View File

@ -9,6 +9,7 @@ import (
"strconv"
"github.com/harness/gitness/internal/api/usererror"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/go-chi/chi"
@ -17,11 +18,10 @@ import (
const (
PathParamRemainder = "*"
QueryParamSort = "sort"
QueryParamOrder = "order"
QueryParamQuery = "query"
QueryParamCreatedBy = "created_by"
QueryParamSort = "sort"
QueryParamOrder = "order"
QueryParamQuery = "query"
QueryParamState = "state"
QueryParamKind = "kind"
@ -204,3 +204,19 @@ func ParseOrder(r *http.Request) enum.Order {
func ParseSort(r *http.Request) string {
return r.URL.Query().Get(QueryParamSort)
}
// ParsePaginationFromRequest parses pagination related info from the url.
func ParsePaginationFromRequest(r *http.Request) types.Pagination {
return types.Pagination{
Page: ParsePage(r),
Size: ParseLimit(r),
}
}
// ParseListQueryFilterFromRequest parses pagination and query related info from the url.
func ParseListQueryFilterFromRequest(r *http.Request) types.ListQueryFilter {
return types.ListQueryFilter{
Query: ParseQuery(r),
Pagination: ParsePaginationFromRequest(r),
}
}

View File

@ -63,6 +63,12 @@ func (a *MembershipAuthorizer) Check(
case enum.ResourceTypeServiceAccount:
spaceRef = scope.SpacePath
case enum.ResourceTypePipeline:
spaceRef = scope.SpacePath
case enum.ResourceTypeSecret:
spaceRef = scope.SpacePath
case enum.ResourceTypeUser:
// a user is allowed to view / edit themselves
if resource.Name == session.Principal.UID &&

View File

@ -10,21 +10,27 @@ 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"
"github.com/harness/gitness/internal/api/controller/secret"
"github.com/harness/gitness/internal/api/controller/serviceaccount"
"github.com/harness/gitness/internal/api/controller/space"
"github.com/harness/gitness/internal/api/controller/user"
"github.com/harness/gitness/internal/api/controller/webhook"
"github.com/harness/gitness/internal/api/handler/account"
handlercheck "github.com/harness/gitness/internal/api/handler/check"
handlerexecution "github.com/harness/gitness/internal/api/handler/execution"
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"
"github.com/harness/gitness/internal/api/handler/resource"
handlersecret "github.com/harness/gitness/internal/api/handler/secret"
handlerserviceaccount "github.com/harness/gitness/internal/api/handler/serviceaccount"
handlerspace "github.com/harness/gitness/internal/api/handler/space"
"github.com/harness/gitness/internal/api/handler/system"
@ -54,7 +60,7 @@ type APIHandler interface {
var (
// terminatedPathPrefixesAPI is the list of prefixes that will require resolving terminated paths.
terminatedPathPrefixesAPI = []string{"/v1/spaces/", "/v1/repos/"}
terminatedPathPrefixesAPI = []string{"/v1/spaces/", "/v1/repos/", "/v1/pipelines/", "/v1/secrets/"}
)
// NewAPIHandler returns a new APIHandler.
@ -62,7 +68,10 @@ func NewAPIHandler(
config *types.Config,
authenticator authn.Authenticator,
repoCtrl *repo.Controller,
executionCtrl *execution.Controller,
spaceCtrl *space.Controller,
pipelineCtrl *pipeline.Controller,
secretCtrl *secret.Controller,
pullreqCtrl *pullreq.Controller,
webhookCtrl *webhook.Controller,
githookCtrl *controllergithook.Controller,
@ -92,7 +101,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, repoCtrl, executionCtrl, pipelineCtrl, secretCtrl, spaceCtrl, pullreqCtrl, webhookCtrl, githookCtrl,
saCtrl, userCtrl, principalCtrl, checkCtrl)
})
@ -115,6 +124,9 @@ func corsHandler(config *types.Config) func(http.Handler) http.Handler {
func setupRoutesV1(r chi.Router,
repoCtrl *repo.Controller,
executionCtrl *execution.Controller,
pipelineCtrl *pipeline.Controller,
secretCtrl *secret.Controller,
spaceCtrl *space.Controller,
pullreqCtrl *pullreq.Controller,
webhookCtrl *webhook.Controller,
@ -126,6 +138,8 @@ func setupRoutesV1(r chi.Router,
) {
setupSpaces(r, spaceCtrl)
setupRepos(r, repoCtrl, pullreqCtrl, webhookCtrl, checkCtrl)
setupPipelines(r, pipelineCtrl, executionCtrl)
setupSecrets(r, secretCtrl)
setupUser(r, userCtrl)
setupServiceAccounts(r, saCtrl)
setupPrincipals(r, principalCtrl)
@ -151,6 +165,8 @@ func setupSpaces(r chi.Router, spaceCtrl *space.Controller) {
r.Get("/spaces", handlerspace.HandleListSpaces(spaceCtrl))
r.Get("/repos", handlerspace.HandleListRepos(spaceCtrl))
r.Get("/service-accounts", handlerspace.HandleListServiceAccounts(spaceCtrl))
r.Get("/pipelines", handlerspace.HandleListPipelines(spaceCtrl))
r.Get("/secrets", handlerspace.HandleListSecrets(spaceCtrl))
// Child collections
r.Route("/paths", func(r chi.Router) {
@ -268,6 +284,43 @@ 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.PathParamPipelineRef), func(r chi.Router) {
r.Get("/", handlerpipeline.HandleFind(pipelineCtrl))
r.Patch("/", handlerpipeline.HandleUpdate(pipelineCtrl))
r.Delete("/", handlerpipeline.HandleDelete(pipelineCtrl))
setupExecutions(r, pipelineCtrl, executionCtrl)
})
})
}
func setupSecrets(r chi.Router, secretCtrl *secret.Controller) {
r.Route("/secrets", func(r chi.Router) {
// Create takes path and parentId via body, not uri
r.Post("/", handlersecret.HandleCreate(secretCtrl))
r.Route(fmt.Sprintf("/{%s}", request.PathParamSecretRef), func(r chi.Router) {
r.Get("/", handlersecret.HandleFind(secretCtrl))
r.Patch("/", handlersecret.HandleUpdate(secretCtrl))
r.Delete("/", handlersecret.HandleDelete(secretCtrl))
})
})
}
func setupExecutions(r chi.Router, pipelineCtrl *pipeline.Controller, executionCtrl *execution.Controller) {
r.Route("/executions", func(r chi.Router) {
r.Get("/", handlerexecution.HandleList(executionCtrl))
r.Post("/", handlerexecution.HandleCreate(executionCtrl))
r.Route(fmt.Sprintf("/{%s}", request.PathParamExecutionNumber), func(r chi.Router) {
r.Get("/", handlerexecution.HandleFind(executionCtrl))
r.Patch("/", handlerexecution.HandleUpdate(executionCtrl))
r.Delete("/", handlerexecution.HandleDelete(executionCtrl))
})
})
}
func setupInternal(r chi.Router, githookCtrl *controllergithook.Controller) {
r.Route("/internal", func(r chi.Router) {
SetupGitHooks(r, githookCtrl)

View File

@ -7,10 +7,13 @@ 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"
"github.com/harness/gitness/internal/api/controller/secret"
"github.com/harness/gitness/internal/api/controller/serviceaccount"
"github.com/harness/gitness/internal/api/controller/space"
"github.com/harness/gitness/internal/api/controller/user"
@ -57,7 +60,10 @@ func ProvideAPIHandler(
config *types.Config,
authenticator authn.Authenticator,
repoCtrl *repo.Controller,
executionCtrl *execution.Controller,
spaceCtrl *space.Controller,
pipelineCtrl *pipeline.Controller,
secretCtrl *secret.Controller,
pullreqCtrl *pullreq.Controller,
webhookCtrl *webhook.Controller,
githookCtrl *githook.Controller,
@ -66,8 +72,8 @@ func ProvideAPIHandler(
principalCtrl principal.Controller,
checkCtrl *check.Controller,
) APIHandler {
return NewAPIHandler(config, authenticator, repoCtrl, spaceCtrl, pullreqCtrl,
webhookCtrl, githookCtrl, saCtrl, userCtrl, principalCtrl, checkCtrl)
return NewAPIHandler(config, authenticator, repoCtrl, executionCtrl, spaceCtrl, pipelineCtrl, secretCtrl,
pullreqCtrl, webhookCtrl, githookCtrl, saCtrl, userCtrl, principalCtrl, checkCtrl)
}
func ProvideWebHandler(config *types.Config) WebHandler {

View File

@ -439,4 +439,90 @@ 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(ctx context.Context, id int64) (*types.Pipeline, error)
// FindByUID returns a pipeline with a given UID in a space
FindByUID(ctx context.Context, id int64, uid string) (*types.Pipeline, error)
// Create creates a new pipeline in the datastore.
Create(ctx context.Context, pipeline *types.Pipeline) error
// Update tries to update a pipeline in the datastore
Update(ctx context.Context, pipeline *types.Pipeline) error
// List lists the pipelines present in a parent space ID in the datastore.
List(ctx context.Context, spaceID int64, pagination types.ListQueryFilter) ([]*types.Pipeline, error)
// UpdateOptLock updates the pipeline using the optimistic locking mechanism.
UpdateOptLock(ctx context.Context, pipeline *types.Pipeline,
mutateFn func(pipeline *types.Pipeline) error) (*types.Pipeline, error)
// Delete deletes a pipeline ID from the datastore.
Delete(ctx context.Context, id int64) error
// Count the number of pipelines in a space matching the given filter.
Count(ctx context.Context, spaceID int64, filter types.ListQueryFilter) (int64, error)
// DeleteByUID deletes a pipeline with a given UID in a space
DeleteByUID(ctx context.Context, spaceID int64, uid string) error
// IncrementSeqNum increments the sequence number of the pipeline
IncrementSeqNum(ctx context.Context, pipeline *types.Pipeline) (*types.Pipeline, error)
}
SecretStore interface {
// Find returns a secret given an ID
Find(ctx context.Context, id int64) (*types.Secret, error)
// FindByUID returns a secret given a space ID and a UID
FindByUID(ctx context.Context, spaceID int64, uid string) (*types.Secret, error)
// Create creates a new secret
Create(ctx context.Context, secret *types.Secret) error
// Count the number of secrets in a space matching the given filter.
Count(ctx context.Context, spaceID int64, pagination types.ListQueryFilter) (int64, error)
// UpdateOptLock updates the secret using the optimistic locking mechanism.
UpdateOptLock(ctx context.Context, secret *types.Secret,
mutateFn func(secret *types.Secret) error) (*types.Secret, error)
// Update tries to update a secret.
Update(ctx context.Context, secret *types.Secret) error
// Delete deletes a secret given an ID.
Delete(ctx context.Context, id int64) error
// DeleteByUID deletes a secret given a space ID and a uid
DeleteByUID(ctx context.Context, spaceID int64, uid string) error
// List lists the secrets in a given space
List(ctx context.Context, spaceID int64, filter types.ListQueryFilter) ([]*types.Secret, error)
}
ExecutionStore interface {
// Find returns a execution given a pipeline and an execution number
Find(ctx context.Context, pipelineID int64, num int64) (*types.Execution, error)
// Create creates a new execution in the datastore.
Create(ctx context.Context, execution *types.Execution) error
// Update tries to update an execution.
Update(ctx context.Context, execution *types.Execution) error
// UpdateOptLock updates the execution using the optimistic locking mechanism.
UpdateOptLock(ctx context.Context, exectuion *types.Execution,
mutateFn func(execution *types.Execution) error) (*types.Execution, error)
// List lists the executions for a given pipeline ID
List(ctx context.Context, pipelineID int64, pagination types.Pagination) ([]*types.Execution, error)
// Delete deletes an execution given a pipeline ID and an execution number
Delete(ctx context.Context, pipelineID int64, num int64) error
// Count the number of executions in a space
Count(ctx context.Context, parentID int64) (int64, error)
}
)

View File

@ -0,0 +1,315 @@
// 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 database
import (
"context"
"fmt"
"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.ExecutionStore = (*executionStore)(nil)
// NewExecutionStore returns a new ExecutionStore.
func NewExecutionStore(db *sqlx.DB) *executionStore {
return &executionStore{
db: db,
}
}
type executionStore struct {
db *sqlx.DB
}
const (
executionColumns = `
execution_id
,execution_pipeline_id
,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
`
)
// Find returns an execution given a pipeline ID and an execution number.
func (s *executionStore) Find(ctx context.Context, pipelineID int64, executionNum int64) (*types.Execution, error) {
const findQueryStmt = `
SELECT` + executionColumns + `
FROM executions
WHERE execution_pipeline_id = $1 AND execution_number = $2`
db := dbtx.GetAccessor(ctx, s.db)
dst := new(types.Execution)
if err := db.GetContext(ctx, dst, findQueryStmt, pipelineID, executionNum); err != nil {
return nil, database.ProcessSQLErrorf(err, "Failed to find execution")
}
return dst, nil
}
// Create creates a new execution in the datastore.
func (s *executionStore) Create(ctx context.Context, execution *types.Execution) error {
const executionInsertStmt = `
INSERT INTO executions (
execution_pipeline_id
,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
) VALUES (
:execution_pipeline_id
,: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
) RETURNING execution_id`
db := dbtx.GetAccessor(ctx, s.db)
query, arg, err := db.BindNamed(executionInsertStmt, execution)
if err != nil {
return database.ProcessSQLErrorf(err, "Failed to bind execution object")
}
if err = db.QueryRowContext(ctx, query, arg...).Scan(&execution.ID); err != nil {
return database.ProcessSQLErrorf(err, "Execution query failed")
}
return nil
}
// Update tries to update an execution in the datastore with optimistic locking.
func (s *executionStore) Update(ctx context.Context, e *types.Execution) error {
const executionUpdateStmt = `
UPDATE executions
SET
,execution_status = :execution_status
,execution_error = :execution_error
,execution_event = :execution_event
,execution_started = :execution_started
,execution_finished = :execution_finished
,execution_updated = :execution_updated
,execution_version = :execution_version
WHERE execution_id = :execution_id AND execution_version = :execution_version - 1`
updatedAt := time.Now()
execution := *e
execution.Version++
execution.Updated = updatedAt.UnixMilli()
db := dbtx.GetAccessor(ctx, s.db)
query, arg, err := db.BindNamed(executionUpdateStmt, execution)
if err != nil {
return database.ProcessSQLErrorf(err, "Failed to bind execution object")
}
result, err := db.ExecContext(ctx, query, arg...)
if err != nil {
return database.ProcessSQLErrorf(err, "Failed to update execution")
}
count, err := result.RowsAffected()
if err != nil {
return database.ProcessSQLErrorf(err, "Failed to get number of updated rows")
}
if count == 0 {
return gitness_store.ErrVersionConflict
}
e.Version = execution.Version
e.Updated = execution.Updated
return nil
}
// UpdateOptLock updates the pipeline using the optimistic locking mechanism.
func (s *executionStore) UpdateOptLock(ctx context.Context,
execution *types.Execution,
mutateFn func(execution *types.Execution) error) (*types.Execution, error) {
for {
dup := *execution
err := mutateFn(&dup)
if err != nil {
return nil, err
}
err = s.Update(ctx, &dup)
if err == nil {
return &dup, nil
}
if !errors.Is(err, gitness_store.ErrVersionConflict) {
return nil, err
}
execution, err = s.Find(ctx, execution.PipelineID, execution.Number)
if err != nil {
return nil, err
}
}
}
// List lists the executions for a given pipeline ID.
func (s *executionStore) List(
ctx context.Context,
pipelineID int64,
pagination types.Pagination,
) ([]*types.Execution, error) {
stmt := database.Builder.
Select(executionColumns).
From("executions").
Where("execution_pipeline_id = ?", fmt.Sprint(pipelineID))
stmt = stmt.Limit(database.Limit(pagination.Size))
stmt = stmt.Offset(database.Offset(pagination.Page, pagination.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.Execution{}
if err = db.SelectContext(ctx, &dst, sql, args...); err != nil {
return nil, database.ProcessSQLErrorf(err, "Failed executing custom list query")
}
return dst, nil
}
// Count of executions in a space.
func (s *executionStore) Count(ctx context.Context, pipelineID int64) (int64, error) {
stmt := database.Builder.
Select("count(*)").
From("executions").
Where("execution_pipeline_id = ?", pipelineID)
sql, args, err := stmt.ToSql()
if err != nil {
return 0, errors.Wrap(err, "Failed to convert query to sql")
}
db := dbtx.GetAccessor(ctx, s.db)
var count int64
err = db.QueryRowContext(ctx, sql, args...).Scan(&count)
if err != nil {
return 0, database.ProcessSQLErrorf(err, "Failed executing count query")
}
return count, nil
}
// Delete deletes an execution given a pipeline ID and an execution number.
func (s *executionStore) Delete(ctx context.Context, pipelineID int64, executionNum int64) error {
const executionDeleteStmt = `
DELETE FROM executions
WHERE execution_pipeline_id = $1 AND execution_number = $2`
db := dbtx.GetAccessor(ctx, s.db)
if _, err := db.ExecContext(ctx, executionDeleteStmt, pipelineID, executionNum); err != nil {
return database.ProcessSQLErrorf(err, "Could not delete execution")
}
return nil
}

View File

@ -0,0 +1,103 @@
CREATE TABLE IF NOT EXISTS pipelines (
pipeline_id INTEGER PRIMARY KEY AUTOINCREMENT
,pipeline_description TEXT NOT NULL
,pipeline_space_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 NOT NULL
,pipeline_created INTEGER NOT NULL
,pipeline_updated INTEGER NOT NULL
,pipeline_version INTEGER NOT NULL
-- Ensure unique combination of UID and ParentID
,UNIQUE (pipeline_space_id, pipeline_uid)
-- Foreign key to spaces table
,CONSTRAINT fk_pipeline_space_id FOREIGN KEY (pipeline_space_id)
REFERENCES spaces (space_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE CASCADE
-- Foreign key to repositories table
,CONSTRAINT fk_pipelines_repo_id FOREIGN KEY (pipeline_repo_id)
REFERENCES repositories (repo_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE CASCADE
);
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 NOT NULL
,execution_updated INTEGER NOT NULL
,execution_version INTEGER NOT NULL
-- Ensure unique combination of pipeline ID and number
,UNIQUE (execution_pipeline_id, execution_number)
-- Foreign key to pipelines table
,CONSTRAINT fk_executions_pipeline_id FOREIGN KEY (execution_pipeline_id)
REFERENCES pipelines (pipeline_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE CASCADE
-- Foreign key to repositories table
,CONSTRAINT fk_executions_repo_id FOREIGN KEY (execution_repo_id)
REFERENCES repositories (repo_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS secrets (
secret_id INTEGER PRIMARY KEY AUTOINCREMENT
,secret_uid TEXT NOT NULL
,secret_space_id INTEGER NOT NULL
,secret_description TEXT NOT NULL
,secret_data BLOB NOT NULL
,secret_created INTEGER NOT NULL
,secret_updated INTEGER NOT NULL
,secret_version INTEGER NOT NULL
-- Ensure unique combination of space ID and UID
,UNIQUE (secret_space_id, secret_uid)
-- Foreign key to spaces table
,CONSTRAINT fk_secrets_space_id FOREIGN KEY (secret_space_id)
REFERENCES spaces (space_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE CASCADE
);

View File

@ -0,0 +1,307 @@
// 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 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` +
pipelineColumns + `
FROM pipelines`
pipelineColumns = `
pipeline_id
,pipeline_description
,pipeline_space_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
`
)
// 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_space_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 {
const pipelineInsertStmt = `
INSERT INTO pipelines (
pipeline_description
,pipeline_space_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_space_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`
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
}
// Update updates a pipeline.
func (s *pipelineStore) Update(ctx context.Context, p *types.Pipeline) error {
const pipelineUpdateStmt = `
UPDATE pipelines
SET
pipeline_description = :pipeline_description,
pipeline_uid = :pipeline_uid,
pipeline_seq = :pipeline_seq,
pipeline_default_branch = :pipeline_default_branch,
pipeline_config_path = :pipeline_config_path,
pipeline_updated = :pipeline_updated,
pipeline_version = :pipeline_version
WHERE pipeline_id = :pipeline_id AND pipeline_version = :pipeline_version - 1`
updatedAt := time.Now()
pipeline := *p
pipeline.Version++
pipeline.Updated = updatedAt.UnixMilli()
db := dbtx.GetAccessor(ctx, s.db)
query, arg, err := db.BindNamed(pipelineUpdateStmt, pipeline)
if err != nil {
return database.ProcessSQLErrorf(err, "Failed to bind pipeline object")
}
result, err := db.ExecContext(ctx, query, arg...)
if err != nil {
return database.ProcessSQLErrorf(err, "Failed to update pipeline")
}
count, err := result.RowsAffected()
if err != nil {
return database.ProcessSQLErrorf(err, "Failed to get number of updated rows")
}
if count == 0 {
return gitness_store.ErrVersionConflict
}
p.Updated = pipeline.Updated
p.Version = pipeline.Version
return nil
}
// List lists all the pipelines present in a space.
func (s *pipelineStore) List(
ctx context.Context,
parentID int64,
filter types.ListQueryFilter,
) ([]*types.Pipeline, error) {
stmt := database.Builder.
Select(pipelineColumns).
From("pipelines").
Where("pipeline_space_id = ?", fmt.Sprint(parentID))
if filter.Query != "" {
stmt = stmt.Where("LOWER(pipeline_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(filter.Query)))
}
stmt = stmt.Limit(database.Limit(filter.Size))
stmt = stmt.Offset(database.Offset(filter.Page, filter.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
}
// UpdateOptLock updates the pipeline using the optimistic locking mechanism.
func (s *pipelineStore) UpdateOptLock(ctx context.Context,
pipeline *types.Pipeline,
mutateFn func(pipeline *types.Pipeline) error) (*types.Pipeline, error) {
for {
dup := *pipeline
err := mutateFn(&dup)
if err != nil {
return nil, err
}
err = s.Update(ctx, &dup)
if err == nil {
return &dup, nil
}
if !errors.Is(err, gitness_store.ErrVersionConflict) {
return nil, err
}
pipeline, err = s.Find(ctx, pipeline.ID)
if err != nil {
return nil, err
}
}
}
// Count of pipelines in a space.
func (s *pipelineStore) Count(ctx context.Context, parentID int64, filter types.ListQueryFilter) (int64, error) {
stmt := database.Builder.
Select("count(*)").
From("pipelines").
Where("pipeline_space_id = ?", parentID)
if filter.Query != "" {
stmt = stmt.Where("pipeline_uid LIKE ?", fmt.Sprintf("%%%s%%", filter.Query))
}
sql, args, err := stmt.ToSql()
if err != nil {
return 0, errors.Wrap(err, "Failed to convert query to sql")
}
db := dbtx.GetAccessor(ctx, s.db)
var count int64
err = db.QueryRowContext(ctx, sql, args...).Scan(&count)
if err != nil {
return 0, database.ProcessSQLErrorf(err, "Failed executing count query")
}
return count, 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
}
// DeleteByUID deletes a pipeline with a given UID in a space.
func (s *pipelineStore) DeleteByUID(ctx context.Context, spaceID int64, uid string) error {
const pipelineDeleteStmt = `
DELETE FROM pipelines
WHERE pipeline_space_id = $1 AND pipeline_uid = $2`
db := dbtx.GetAccessor(ctx, s.db)
if _, err := db.ExecContext(ctx, pipelineDeleteStmt, spaceID, uid); err != nil {
return database.ProcessSQLErrorf(err, "Could not delete pipeline")
}
return nil
}
// Increment increments the pipeline sequence number. It will keep retrying in case
// of optimistic lock errors.
func (s *pipelineStore) IncrementSeqNum(ctx context.Context, pipeline *types.Pipeline) (*types.Pipeline, error) {
for {
var err error
pipeline.Seq++
err = s.Update(ctx, pipeline)
if err == nil {
return pipeline, nil
} else if !errors.Is(err, gitness_store.ErrVersionConflict) {
return pipeline, errors.Wrap(err, "could not increment pipeline sequence number")
}
pipeline, err = s.Find(ctx, pipeline.ID)
if err != nil {
return nil, errors.Wrap(err, "could not increment pipeline sequence number")
}
}
}

View File

@ -0,0 +1,266 @@
// 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 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.SecretStore = (*secretStore)(nil)
const (
secretQueryBase = `
SELECT` + secretColumns + `
FROM secrets`
secretColumns = `
secret_id,
secret_description,
secret_space_id,
secret_uid,
secret_data,
secret_created,
secret_updated,
secret_version
`
)
// NewSecretStore returns a new SecretStore.
func NewSecretStore(db *sqlx.DB) *secretStore {
return &secretStore{
db: db,
}
}
type secretStore struct {
db *sqlx.DB
}
// Find returns a secret given a secret ID.
func (s *secretStore) Find(ctx context.Context, id int64) (*types.Secret, error) {
const findQueryStmt = secretQueryBase + `
WHERE secret_id = $1`
db := dbtx.GetAccessor(ctx, s.db)
dst := new(types.Secret)
if err := db.GetContext(ctx, dst, findQueryStmt, id); err != nil {
return nil, database.ProcessSQLErrorf(err, "Failed to find secret")
}
return dst, nil
}
// FindByUID returns a secret in a given space with a given UID.
func (s *secretStore) FindByUID(ctx context.Context, spaceID int64, uid string) (*types.Secret, error) {
const findQueryStmt = secretQueryBase + `
WHERE secret_space_id = $1 AND secret_uid = $2`
db := dbtx.GetAccessor(ctx, s.db)
dst := new(types.Secret)
if err := db.GetContext(ctx, dst, findQueryStmt, spaceID, uid); err != nil {
return nil, database.ProcessSQLErrorf(err, "Failed to find secret")
}
return dst, nil
}
// Create creates a secret.
func (s *secretStore) Create(ctx context.Context, secret *types.Secret) error {
const secretInsertStmt = `
INSERT INTO secrets (
secret_description,
secret_space_id,
secret_uid,
secret_data,
secret_created,
secret_updated,
secret_version
) VALUES (
:secret_description,
:secret_space_id,
:secret_uid,
:secret_data,
:secret_created,
:secret_updated,
:secret_version
) RETURNING secret_id`
db := dbtx.GetAccessor(ctx, s.db)
query, arg, err := db.BindNamed(secretInsertStmt, secret)
if err != nil {
return database.ProcessSQLErrorf(err, "Failed to bind secret object")
}
if err = db.QueryRowContext(ctx, query, arg...).Scan(&secret.ID); err != nil {
return database.ProcessSQLErrorf(err, "secret query failed")
}
return nil
}
func (s *secretStore) Update(ctx context.Context, p *types.Secret) error {
const secretUpdateStmt = `
UPDATE secrets
SET
secret_description = :secret_description,
secret_uid = :secret_uid,
secret_data = :secret_data,
secret_updated = :secret_updated,
secret_version = :secret_version
WHERE secret_id = :secret_id AND secret_version = :secret_version - 1`
updatedAt := time.Now()
secret := *p
secret.Version++
secret.Updated = updatedAt.UnixMilli()
db := dbtx.GetAccessor(ctx, s.db)
query, arg, err := db.BindNamed(secretUpdateStmt, secret)
if err != nil {
return database.ProcessSQLErrorf(err, "Failed to bind secret object")
}
result, err := db.ExecContext(ctx, query, arg...)
if err != nil {
return database.ProcessSQLErrorf(err, "Failed to update secret")
}
count, err := result.RowsAffected()
if err != nil {
return database.ProcessSQLErrorf(err, "Failed to get number of updated rows")
}
if count == 0 {
return gitness_store.ErrVersionConflict
}
p.Version = secret.Version
p.Updated = secret.Updated
return nil
}
// UpdateOptLock updates the pipeline using the optimistic locking mechanism.
func (s *secretStore) UpdateOptLock(ctx context.Context,
secret *types.Secret,
mutateFn func(secret *types.Secret) error,
) (*types.Secret, error) {
for {
dup := *secret
err := mutateFn(&dup)
if err != nil {
return nil, err
}
err = s.Update(ctx, &dup)
if err == nil {
return &dup, nil
}
if !errors.Is(err, gitness_store.ErrVersionConflict) {
return nil, err
}
secret, err = s.Find(ctx, secret.ID)
if err != nil {
return nil, err
}
}
}
// List lists all the secrets present in a space.
func (s *secretStore) List(ctx context.Context, parentID int64, filter types.ListQueryFilter) ([]*types.Secret, error) {
stmt := database.Builder.
Select(secretColumns).
From("secrets").
Where("secret_space_id = ?", fmt.Sprint(parentID))
if filter.Query != "" {
stmt = stmt.Where("LOWER(secret_uid) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(filter.Query)))
}
stmt = stmt.Limit(database.Limit(filter.Size))
stmt = stmt.Offset(database.Offset(filter.Page, filter.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.Secret{}
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 secret given a secret ID.
func (s *secretStore) Delete(ctx context.Context, id int64) error {
const secretDeleteStmt = `
DELETE FROM secrets
WHERE secret_id = $1`
db := dbtx.GetAccessor(ctx, s.db)
if _, err := db.ExecContext(ctx, secretDeleteStmt, id); err != nil {
return database.ProcessSQLErrorf(err, "Could not delete secret")
}
return nil
}
// DeleteByUID deletes a secret with a given UID in a space.
func (s *secretStore) DeleteByUID(ctx context.Context, spaceID int64, uid string) error {
const secretDeleteStmt = `
DELETE FROM secrets
WHERE secret_space_id = $1 AND secret_uid = $2`
db := dbtx.GetAccessor(ctx, s.db)
if _, err := db.ExecContext(ctx, secretDeleteStmt, spaceID, uid); err != nil {
return database.ProcessSQLErrorf(err, "Could not delete secret")
}
return nil
}
// Count of secrets in a space.
func (s *secretStore) Count(ctx context.Context, parentID int64, filter types.ListQueryFilter) (int64, error) {
stmt := database.Builder.
Select("count(*)").
From("secrets").
Where("secret_space_id = ?", parentID)
if filter.Query != "" {
stmt = stmt.Where("secret_uid LIKE ?", fmt.Sprintf("%%%s%%", filter.Query))
}
sql, args, err := stmt.ToSql()
if err != nil {
return 0, errors.Wrap(err, "Failed to convert query to sql")
}
db := dbtx.GetAccessor(ctx, s.db)
var count int64
err = db.QueryRowContext(ctx, sql, args...).Scan(&count)
if err != nil {
return 0, database.ProcessSQLErrorf(err, "Failed executing count query")
}
return count, nil
}

View File

@ -23,6 +23,9 @@ var WireSet = wire.NewSet(
ProvidePathStore,
ProvideSpaceStore,
ProvideRepoStore,
ProvideExecutionStore,
ProvidePipelineStore,
ProvideSecretStore,
ProvideRepoGitInfoView,
ProvideMembershipStore,
ProvideTokenStore,
@ -78,6 +81,21 @@ 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)
}
// ProvideSecretStore provides a secret store.
func ProvideSecretStore(db *sqlx.DB) store.SecretStore {
return NewSecretStore(db)
}
// ProvideExecutionStore provides an execution 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)

View File

@ -8,9 +8,10 @@ import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
user "github.com/harness/gitness/internal/api/controller/user"
types "github.com/harness/gitness/types"
gomock "github.com/golang/mock/gomock"
)
// MockClient is a mock of Client interface.

View File

@ -8,9 +8,10 @@ import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
types "github.com/harness/gitness/types"
enum "github.com/harness/gitness/types/enum"
gomock "github.com/golang/mock/gomock"
)
// MockPrincipalStore is a mock of PrincipalStore interface.

View File

@ -52,6 +52,12 @@ type Config struct {
DefaultBranch string `envconfig:"GITNESS_GIT_DEFAULTBRANCH" default:"main"`
}
// Encrypter defines the parameters for the encrypter
Encrypter struct {
Secret string `envconfig:"GITNESS_ENCRYPTER_SECRET"` // key used for encryption
MixedContent bool `envconfig:"GITNESS_ENCRYPTER_MIXED_CONTENT"`
}
// Server defines the server configuration parameters.
Server struct {
// HTTP defines the http configuration parameters

View File

@ -24,10 +24,14 @@ var membershipRoleReaderPermissions = slices.Clip(slices.Insert([]Permission{},
PermissionRepoView,
PermissionSpaceView,
PermissionServiceAccountView,
PermissionPipelineView,
PermissionSecretView,
))
var membershipRoleExecutorPermissions = slices.Clip(slices.Insert(membershipRoleReaderPermissions, 0,
PermissionCommitCheckReport,
PermissionPipelineExecute,
PermissionSecretAccess,
))
var membershipRoleContributorPermissions = slices.Clip(slices.Insert(membershipRoleReaderPermissions, 0,
@ -47,6 +51,16 @@ var membershipRoleSpaceOwnerPermissions = slices.Clip(slices.Insert(membershipRo
PermissionServiceAccountCreate,
PermissionServiceAccountEdit,
PermissionServiceAccountDelete,
PermissionPipelineEdit,
PermissionPipelineExecute,
PermissionPipelineDelete,
PermissionPipelineView,
PermissionSecretAccess,
PermissionSecretDelete,
PermissionSecretEdit,
PermissionSecretView,
))
func init() {

View File

@ -13,6 +13,8 @@ const (
ResourceTypeUser ResourceType = "USER"
ResourceTypeServiceAccount ResourceType = "SERVICEACCOUNT"
ResourceTypeService ResourceType = "SERVICE"
ResourceTypePipeline ResourceType = "PIPELINE"
ResourceTypeSecret ResourceType = "SECRET"
// ResourceType_Branch ResourceType = "BRANCH"
)
@ -71,6 +73,26 @@ const (
PermissionServiceEditAdmin Permission = "service_editAdmin"
)
const (
/*
----- PIPELINE -----
*/
PermissionPipelineView Permission = "pipeline_view"
PermissionPipelineEdit Permission = "pipeline_edit"
PermissionPipelineDelete Permission = "pipeline_delete"
PermissionPipelineExecute Permission = "pipeline_execute"
)
const (
/*
----- SECRET -----
*/
PermissionSecretView Permission = "secret_view"
PermissionSecretEdit Permission = "secret_edit"
PermissionSecretDelete Permission = "secret_delete"
PermissionSecretAccess Permission = "secret_access"
)
const (
/*
----- COMMIT CHECK -----

24
types/enum/scm.go Normal file
View File

@ -0,0 +1,24 @@
// 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 enum
// ScmType defines the different SCM types supported for CI.
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"
)

46
types/execution.go Normal file
View File

@ -0,0 +1,46 @@
// 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 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 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"`
}

7
types/list_filters.go Normal file
View File

@ -0,0 +1,7 @@
package types
// ListQueryFilter has pagination related info and a query param
type ListQueryFilter struct {
Pagination
Query string `json:"query"`
}

7
types/pagination.go Normal file
View File

@ -0,0 +1,7 @@
package types
// Pagination stores pagination related params
type Pagination struct {
Page int `json:"page"`
Size int `json:"size"`
}

23
types/pipeline.go Normal file
View File

@ -0,0 +1,23 @@
// 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 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"`
SpaceID int64 `db:"pipeline_space_id" json:"space_id"`
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"`
}

29
types/secret.go Normal file
View File

@ -0,0 +1,29 @@
// Copyright 2023 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 types
type Secret struct {
ID int64 `db:"secret_id" json:"id"`
Description string `db:"secret_description" json:"description"`
SpaceID int64 `db:"secret_space_id" json:"space_id"`
UID string `db:"secret_uid" json:"uid"`
Data string `db:"secret_data" json:"-"`
Created int64 `db:"secret_created" json:"created"`
Updated int64 `db:"secret_updated" json:"updated"`
Version int64 `db:"secret_version" json:"version"`
}
// Copy makes a copy of the secret without the value.
func (s *Secret) CopyWithoutData() *Secret {
return &Secret{
ID: s.ID,
Description: s.Description,
UID: s.UID,
SpaceID: s.SpaceID,
Created: s.Created,
Updated: s.Updated,
Version: s.Version,
}
}