drone/internal/store/database/step.go

190 lines
4.7 KiB
Go

// 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"
"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"
sqlxtypes "github.com/jmoiron/sqlx/types"
)
var _ store.StepStore = (*stepStore)(nil)
const (
stepColumns = `
step_id
,step_stage_id
,step_number
,step_name
,step_status
,step_error
,step_errignore
,step_exit_code
,step_started
,step_stopped
,step_version
,step_depends_on
,step_image
,step_detached
,step_schema
`
)
type step struct {
ID int64 `db:"step_id"`
StageID int64 `db:"step_stage_id"`
Number int64 `db:"step_number"`
ParentGroupID int64 `db:"step_parent_group_id"`
Name string `db:"step_name"`
Status string `db:"step_status"`
Error string `db:"step_error"`
ErrIgnore bool `db:"step_errignore"`
ExitCode int `db:"step_exit_code"`
Started int64 `db:"step_started"`
Stopped int64 `db:"step_stopped"`
Version int64 `db:"step_version"`
DependsOn sqlxtypes.JSONText `db:"step_depends_on"`
Image string `db:"step_image"`
Detached bool `db:"step_detached"`
Schema string `db:"step_schema"`
}
// NewStepStore returns a new StepStore.
func NewStepStore(db *sqlx.DB) *stepStore {
return &stepStore{
db: db,
}
}
type stepStore struct {
db *sqlx.DB
}
// FindByNumber returns a step given a stage ID and a step number.
func (s *stepStore) FindByNumber(ctx context.Context, stageID int64, stepNum int) (*types.Step, error) {
const findQueryStmt = `
SELECT` + stepColumns + `
FROM steps
WHERE step_stage_id = $1 AND step_number = $2`
db := dbtx.GetAccessor(ctx, s.db)
dst := new(step)
if err := db.GetContext(ctx, dst, findQueryStmt, stageID, stepNum); err != nil {
return nil, database.ProcessSQLErrorf(err, "Failed to find step")
}
return mapInternalToStep(dst)
}
// Create creates a step.
func (s *stepStore) Create(ctx context.Context, step *types.Step) error {
const stepInsertStmt = `
INSERT INTO steps (
step_stage_id
,step_number
,step_name
,step_status
,step_error
,step_parent_group_id
,step_errignore
,step_exit_code
,step_started
,step_stopped
,step_version
,step_depends_on
,step_image
,step_detached
,step_schema
) VALUES (
:step_stage_id
,:step_number
,:step_name
,:step_status
,:step_error
,:step_parent_group_id
,:step_errignore
,:step_exit_code
,:step_started
,:step_stopped
,:step_version
,:step_depends_on
,:step_image
,:step_detached
,:step_schema
) RETURNING step_id`
db := dbtx.GetAccessor(ctx, s.db)
query, arg, err := db.BindNamed(stepInsertStmt, mapStepToInternal(step))
if err != nil {
return database.ProcessSQLErrorf(err, "Failed to bind step object")
}
if err = db.QueryRowContext(ctx, query, arg...).Scan(&step.ID); err != nil {
return database.ProcessSQLErrorf(err, "Step query failed")
}
return nil
}
// Update tries to update a step in the datastore and returns a locking error
// if it was unable to do so.
func (s *stepStore) Update(ctx context.Context, e *types.Step) error {
const stepUpdateStmt = `
UPDATE steps
SET
step_name = :step_name
,step_status = :step_status
,step_error = :step_error
,step_errignore = :step_errignore
,step_exit_code = :step_exit_code
,step_started = :step_started
,step_stopped = :step_stopped
,step_depends_on = :step_depends_on
,step_image = :step_image
,step_detached = :step_detached
,step_schema = :step_schema
,step_version = :step_version
WHERE step_id = :step_id AND step_version = :step_version - 1`
step := mapStepToInternal(e)
step.Version++
db := dbtx.GetAccessor(ctx, s.db)
query, arg, err := db.BindNamed(stepUpdateStmt, step)
if err != nil {
return database.ProcessSQLErrorf(err, "Failed to bind step object")
}
result, err := db.ExecContext(ctx, query, arg...)
if err != nil {
return database.ProcessSQLErrorf(err, "Failed to update step")
}
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
}
m, err := mapInternalToStep(step)
if err != nil {
return fmt.Errorf("Could not map step object: %w", err)
}
*e = *m
e.Version = step.Version
return nil
}