582 lines
13 KiB
Go
582 lines
13 KiB
Go
// Copyright 2019 Drone IO, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package build
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/drone/drone/core"
|
|
"github.com/drone/drone/store/shared/db"
|
|
)
|
|
|
|
// New returns a new Buildcore.
|
|
func New(db *db.DB) core.BuildStore {
|
|
return &buildStore{db}
|
|
}
|
|
|
|
type buildStore struct {
|
|
db *db.DB
|
|
}
|
|
|
|
// Find returns a build from the datacore.
|
|
func (s *buildStore) Find(ctx context.Context, id int64) (*core.Build, error) {
|
|
out := &core.Build{ID: id}
|
|
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
|
|
params := toParams(out)
|
|
query, args, err := binder.BindNamed(queryKey, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
row := queryer.QueryRow(query, args...)
|
|
return scanRow(row, out)
|
|
})
|
|
return out, err
|
|
}
|
|
|
|
// FindNumber returns a build from the datastore by build number.
|
|
func (s *buildStore) FindNumber(ctx context.Context, repo, number int64) (*core.Build, error) {
|
|
out := &core.Build{Number: number, RepoID: repo}
|
|
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
|
|
params := toParams(out)
|
|
query, args, err := binder.BindNamed(queryNumber, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
row := queryer.QueryRow(query, args...)
|
|
return scanRow(row, out)
|
|
})
|
|
return out, err
|
|
}
|
|
|
|
// FindLast returns the last build from the datastore by ref.
|
|
func (s *buildStore) FindRef(ctx context.Context, repo int64, ref string) (*core.Build, error) {
|
|
out := &core.Build{RepoID: repo, Ref: ref}
|
|
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
|
|
params := toParams(out)
|
|
query, args, err := binder.BindNamed(queryRowRef, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
row := queryer.QueryRow(query, args...)
|
|
return scanRow(row, out)
|
|
})
|
|
return out, err
|
|
}
|
|
|
|
// List returns a list of builds from the datastore by repository id.
|
|
func (s *buildStore) List(ctx context.Context, repo int64, limit, offset int) ([]*core.Build, error) {
|
|
var out []*core.Build
|
|
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
|
|
params := map[string]interface{}{
|
|
"build_repo_id": repo,
|
|
"limit": limit,
|
|
"offset": offset,
|
|
}
|
|
stmt, args, err := binder.BindNamed(queryRepo, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rows, err := queryer.Query(stmt, args...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out, err = scanRows(rows)
|
|
return err
|
|
})
|
|
return out, err
|
|
}
|
|
|
|
// ListRef returns a list of builds from the datastore by ref.
|
|
func (s *buildStore) ListRef(ctx context.Context, repo int64, ref string, limit, offset int) ([]*core.Build, error) {
|
|
var out []*core.Build
|
|
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
|
|
params := map[string]interface{}{
|
|
"build_repo_id": repo,
|
|
"build_ref": ref,
|
|
"limit": limit,
|
|
"offset": offset,
|
|
}
|
|
stmt, args, err := binder.BindNamed(queryRef, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rows, err := queryer.Query(stmt, args...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out, err = scanRows(rows)
|
|
return err
|
|
})
|
|
return out, err
|
|
}
|
|
|
|
// Pending returns a list of pending builds from the datastore by repository id.
|
|
func (s *buildStore) Pending(ctx context.Context) ([]*core.Build, error) {
|
|
var out []*core.Build
|
|
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
|
|
rows, err := queryer.Query(queryPending)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out, err = scanRows(rows)
|
|
return err
|
|
})
|
|
return out, err
|
|
}
|
|
|
|
// Running returns a list of running builds from the datastore by repository id.
|
|
func (s *buildStore) Running(ctx context.Context) ([]*core.Build, error) {
|
|
var out []*core.Build
|
|
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
|
|
rows, err := queryer.Query(queryRunning)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out, err = scanRows(rows)
|
|
return err
|
|
})
|
|
return out, err
|
|
}
|
|
|
|
// Create persists a build to the datacore.
|
|
func (s *buildStore) Create(ctx context.Context, build *core.Build, stages []*core.Stage) error {
|
|
if s.db.Driver() == db.Postgres {
|
|
return s.createPostgres(ctx, build, stages)
|
|
}
|
|
return s.create(ctx, build, stages)
|
|
}
|
|
|
|
func (s *buildStore) create(ctx context.Context, build *core.Build, stages []*core.Stage) error {
|
|
build.Version = 1
|
|
return s.db.Update(func(execer db.Execer, binder db.Binder) error {
|
|
params := toParams(build)
|
|
stmt, args, err := binder.BindNamed(stmtInsert, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
res, err := execer.Exec(stmt, args...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
build.ID, err = res.LastInsertId()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, stage := range stages {
|
|
stage.Version = 1
|
|
stage.BuildID = build.ID
|
|
params := toStageParams(stage)
|
|
stmt, args, err := binder.BindNamed(stmtStageInsert, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
res, err := execer.Exec(stmt, args...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
stage.ID, err = res.LastInsertId()
|
|
}
|
|
return err
|
|
})
|
|
}
|
|
|
|
func (s *buildStore) createPostgres(ctx context.Context, build *core.Build, stages []*core.Stage) error {
|
|
build.Version = 1
|
|
return s.db.Update(func(execer db.Execer, binder db.Binder) error {
|
|
params := toParams(build)
|
|
stmt, args, err := binder.BindNamed(stmtInsertPg, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = execer.QueryRow(stmt, args...).Scan(&build.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, stage := range stages {
|
|
stage.Version = 1
|
|
stage.BuildID = build.ID
|
|
params := toStageParams(stage)
|
|
stmt, args, err := binder.BindNamed(stmtStageInsertPg, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = execer.QueryRow(stmt, args...).Scan(&stage.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return err
|
|
})
|
|
}
|
|
|
|
// Update updates a build in the datacore.
|
|
func (s *buildStore) Update(ctx context.Context, build *core.Build) error {
|
|
versionNew := build.Version + 1
|
|
versionOld := build.Version
|
|
|
|
err := s.db.Lock(func(execer db.Execer, binder db.Binder) error {
|
|
params := toParams(build)
|
|
params["build_version_old"] = versionOld
|
|
params["build_version_new"] = versionNew
|
|
stmt, args, err := binder.BindNamed(stmtUpdate, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
res, err := execer.Exec(stmt, args...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
effected, err := res.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if effected == 0 {
|
|
return db.ErrOptimisticLock
|
|
}
|
|
return nil
|
|
})
|
|
if err == nil {
|
|
build.Version = versionNew
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Delete deletes a build from the datacore.
|
|
func (s *buildStore) Delete(ctx context.Context, build *core.Build) error {
|
|
return s.db.Lock(func(execer db.Execer, binder db.Binder) error {
|
|
params := toParams(build)
|
|
stmt, args, err := binder.BindNamed(stmtDelete, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = execer.Exec(stmt, args...)
|
|
return err
|
|
})
|
|
}
|
|
|
|
// Purge deletes builds from the database where the build number is less than n.
|
|
func (s *buildStore) Purge(ctx context.Context, repo, number int64) error {
|
|
build := &core.Build{
|
|
RepoID: repo,
|
|
Number: number,
|
|
}
|
|
return s.db.Lock(func(execer db.Execer, binder db.Binder) error {
|
|
params := toParams(build)
|
|
stmt, args, err := binder.BindNamed(stmtPurge, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = execer.Exec(stmt, args...)
|
|
return err
|
|
})
|
|
}
|
|
|
|
// Count returns a count of builds.
|
|
func (s *buildStore) Count(ctx context.Context) (i int64, err error) {
|
|
err = s.db.View(func(queryer db.Queryer, binder db.Binder) error {
|
|
return queryer.QueryRow(queryCount).Scan(&i)
|
|
})
|
|
return
|
|
}
|
|
|
|
const queryCount = `
|
|
SELECT COUNT(*)
|
|
FROM builds
|
|
`
|
|
|
|
const queryBase = `
|
|
SELECT
|
|
build_id
|
|
,build_repo_id
|
|
,build_trigger
|
|
,build_number
|
|
,build_parent
|
|
,build_status
|
|
,build_error
|
|
,build_event
|
|
,build_action
|
|
,build_link
|
|
,build_timestamp
|
|
,build_title
|
|
,build_message
|
|
,build_before
|
|
,build_after
|
|
,build_ref
|
|
,build_source_repo
|
|
,build_source
|
|
,build_target
|
|
,build_author
|
|
,build_author_name
|
|
,build_author_email
|
|
,build_author_avatar
|
|
,build_sender
|
|
,build_params
|
|
,build_cron
|
|
,build_deploy
|
|
,build_deploy_id
|
|
,build_started
|
|
,build_finished
|
|
,build_created
|
|
,build_updated
|
|
,build_version
|
|
`
|
|
|
|
const queryKey = queryBase + `
|
|
FROM builds
|
|
WHERE build_id = :build_id
|
|
`
|
|
|
|
const queryNumber = queryBase + `
|
|
FROM builds
|
|
WHERE build_repo_id = :build_repo_id
|
|
AND build_number = :build_number
|
|
`
|
|
|
|
const queryRef = queryBase + `
|
|
FROM builds
|
|
WHERE build_repo_id = :build_repo_id
|
|
AND build_ref = :build_ref
|
|
ORDER BY build_id DESC
|
|
LIMIT :limit OFFSET :offset
|
|
`
|
|
|
|
const queryRowRef = queryBase + `
|
|
FROM builds
|
|
WHERE build_repo_id = :build_repo_id
|
|
AND build_ref = :build_ref
|
|
ORDER BY build_id DESC
|
|
LIMIT 1
|
|
`
|
|
|
|
const queryRepo = queryBase + `
|
|
FROM builds
|
|
WHERE build_repo_id = :build_repo_id
|
|
ORDER BY build_id DESC
|
|
LIMIT :limit OFFSET :offset
|
|
`
|
|
|
|
const queryPending = queryBase + `
|
|
FROM builds
|
|
WHERE EXISTS (
|
|
SELECT stage_id
|
|
FROM stages
|
|
WHERE stages.stage_build_id = builds.build_id
|
|
AND stages.stage_status = 'pending'
|
|
)
|
|
ORDER BY build_id ASC
|
|
`
|
|
|
|
const queryRunning = queryBase + `
|
|
FROM builds
|
|
WHERE EXISTS (
|
|
SELECT stage_id
|
|
FROM stages
|
|
WHERE stages.stage_build_id = builds.build_id
|
|
AND stages.stage_status = 'running'
|
|
)
|
|
ORDER BY build_id ASC
|
|
`
|
|
|
|
// const queryRunningOLD = queryBase + `
|
|
// FROM builds
|
|
// WHERE build_status = 'running'
|
|
// ORDER BY build_id ASC
|
|
// `
|
|
|
|
const queryAll = queryBase + `
|
|
FROM builds
|
|
WHERE build_id > :build_id
|
|
LIMIT :limit OFFSET :offset
|
|
`
|
|
|
|
const stmtUpdate = `
|
|
UPDATE builds SET
|
|
build_parent = :build_parent
|
|
,build_status = :build_status
|
|
,build_error = :build_error
|
|
,build_event = :build_event
|
|
,build_action = :build_action
|
|
,build_link = :build_link
|
|
,build_timestamp = :build_timestamp
|
|
,build_title = :build_title
|
|
,build_message = :build_message
|
|
,build_before = :build_before
|
|
,build_after = :build_after
|
|
,build_ref = :build_ref
|
|
,build_source_repo = :build_source_repo
|
|
,build_source = :build_source
|
|
,build_target = :build_target
|
|
,build_author = :build_author
|
|
,build_author_name = :build_author_name
|
|
,build_author_email = :build_author_email
|
|
,build_author_avatar = :build_author_avatar
|
|
,build_sender = :build_sender
|
|
,build_params = :build_params
|
|
,build_cron = :build_cron
|
|
,build_deploy = :build_deploy
|
|
,build_started = :build_started
|
|
,build_finished = :build_finished
|
|
,build_updated = :build_updated
|
|
,build_version = :build_version_new
|
|
WHERE build_id = :build_id
|
|
AND build_version = :build_version_old
|
|
`
|
|
|
|
const stmtInsert = `
|
|
INSERT INTO builds (
|
|
build_repo_id
|
|
,build_trigger
|
|
,build_number
|
|
,build_parent
|
|
,build_status
|
|
,build_error
|
|
,build_event
|
|
,build_action
|
|
,build_link
|
|
,build_timestamp
|
|
,build_title
|
|
,build_message
|
|
,build_before
|
|
,build_after
|
|
,build_ref
|
|
,build_source_repo
|
|
,build_source
|
|
,build_target
|
|
,build_author
|
|
,build_author_name
|
|
,build_author_email
|
|
,build_author_avatar
|
|
,build_sender
|
|
,build_params
|
|
,build_cron
|
|
,build_deploy
|
|
,build_deploy_id
|
|
,build_started
|
|
,build_finished
|
|
,build_created
|
|
,build_updated
|
|
,build_version
|
|
) VALUES (
|
|
:build_repo_id
|
|
,:build_trigger
|
|
,:build_number
|
|
,:build_parent
|
|
,:build_status
|
|
,:build_error
|
|
,:build_event
|
|
,:build_action
|
|
,:build_link
|
|
,:build_timestamp
|
|
,:build_title
|
|
,:build_message
|
|
,:build_before
|
|
,:build_after
|
|
,:build_ref
|
|
,:build_source_repo
|
|
,:build_source
|
|
,:build_target
|
|
,:build_author
|
|
,:build_author_name
|
|
,:build_author_email
|
|
,:build_author_avatar
|
|
,:build_sender
|
|
,:build_params
|
|
,:build_cron
|
|
,:build_deploy
|
|
,:build_deploy_id
|
|
,:build_started
|
|
,:build_finished
|
|
,:build_created
|
|
,:build_updated
|
|
,:build_version
|
|
)
|
|
`
|
|
|
|
const stmtInsertPg = stmtInsert + `
|
|
RETURNING build_id
|
|
`
|
|
|
|
const stmtStageInsert = `
|
|
INSERT INTO stages (
|
|
stage_repo_id
|
|
,stage_build_id
|
|
,stage_number
|
|
,stage_name
|
|
,stage_kind
|
|
,stage_type
|
|
,stage_status
|
|
,stage_error
|
|
,stage_errignore
|
|
,stage_exit_code
|
|
,stage_limit
|
|
,stage_os
|
|
,stage_arch
|
|
,stage_variant
|
|
,stage_kernel
|
|
,stage_machine
|
|
,stage_started
|
|
,stage_stopped
|
|
,stage_created
|
|
,stage_updated
|
|
,stage_version
|
|
,stage_on_success
|
|
,stage_on_failure
|
|
,stage_depends_on
|
|
,stage_labels
|
|
) VALUES (
|
|
:stage_repo_id
|
|
,:stage_build_id
|
|
,:stage_number
|
|
,:stage_name
|
|
,:stage_kind
|
|
,:stage_type
|
|
,:stage_status
|
|
,:stage_error
|
|
,:stage_errignore
|
|
,:stage_exit_code
|
|
,:stage_limit
|
|
,:stage_os
|
|
,:stage_arch
|
|
,:stage_variant
|
|
,:stage_kernel
|
|
,:stage_machine
|
|
,:stage_started
|
|
,:stage_stopped
|
|
,:stage_created
|
|
,:stage_updated
|
|
,:stage_version
|
|
,:stage_on_success
|
|
,:stage_on_failure
|
|
,:stage_depends_on
|
|
,:stage_labels
|
|
)
|
|
`
|
|
|
|
const stmtStageInsertPg = stmtStageInsert + `
|
|
RETURNING stage_id
|
|
`
|
|
|
|
const stmtDelete = `
|
|
DELETE FROM builds
|
|
WHERE build_id = :build_id
|
|
`
|
|
|
|
const stmtPurge = `
|
|
DELETE FROM builds
|
|
WHERE build_repo_id = :build_repo_id
|
|
AND build_number < :build_number
|
|
`
|