mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-04-25 21:24:11 +00:00
N:N relationship between build and config. Restarts work
This commit is contained in:
parent
9504b00a40
commit
f9c5fcec0d
11 changed files with 122 additions and 80 deletions
|
@ -16,18 +16,24 @@ package model
|
||||||
|
|
||||||
// ConfigStore persists pipeline configuration to storage.
|
// ConfigStore persists pipeline configuration to storage.
|
||||||
type ConfigStore interface {
|
type ConfigStore interface {
|
||||||
ConfigLoad(buildID int64) ([]*Config, error)
|
ConfigsForBuild(buildID int64) ([]*Config, error)
|
||||||
ConfigFind(repo *Repo, sha string) (*Config, error)
|
ConfigFindIdentical(repoID int64, sha string) (*Config, error)
|
||||||
ConfigFindApproved(*Config) (bool, error)
|
ConfigFindApproved(*Config) (bool, error)
|
||||||
ConfigCreate(*Config) error
|
ConfigCreate(*Config) error
|
||||||
|
BuildConfigCreate(*BuildConfig) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config represents a pipeline configuration.
|
// Config represents a pipeline configuration.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ID int64 `json:"-" meddler:"config_id,pk"`
|
ID int64 `json:"-" meddler:"config_id,pk"`
|
||||||
RepoID int64 `json:"-" meddler:"config_repo_id"`
|
RepoID int64 `json:"-" meddler:"config_repo_id"`
|
||||||
BuildID int64 `json:"-" meddler:"config_build_id"`
|
Data string `json:"data" meddler:"config_data"`
|
||||||
Data string `json:"data" meddler:"config_data"`
|
Hash string `json:"hash" meddler:"config_hash"`
|
||||||
Hash string `json:"hash" meddler:"config_hash"`
|
Name string `json:"name" meddler:"config_name"`
|
||||||
Name string `json:"name" meddler:"config_name"`
|
}
|
||||||
|
|
||||||
|
// BuildConfig is the n:n relation between Build and Config
|
||||||
|
type BuildConfig struct {
|
||||||
|
ConfigID int64 `json:"-" meddler:"config_id"`
|
||||||
|
BuildID int64 `json:"-" meddler:"build_id"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -268,7 +268,7 @@ func PostApproval(c *gin.Context) {
|
||||||
build.Reviewer = user.Login
|
build.Reviewer = user.Login
|
||||||
|
|
||||||
// fetch the build file from the database
|
// fetch the build file from the database
|
||||||
configs, err := Config.Storage.Config.ConfigLoad(build.ID)
|
configs, err := Config.Storage.Config.ConfigsForBuild(build.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
logrus.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
||||||
c.AbortWithError(404, err)
|
c.AbortWithError(404, err)
|
||||||
|
@ -440,7 +440,7 @@ func PostBuild(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch the .drone.yml file from the database
|
// fetch the .drone.yml file from the database
|
||||||
configs, err := Config.Storage.Config.ConfigLoad(build.ID)
|
configs, err := Config.Storage.Config.ConfigsForBuild(build.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
logrus.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
||||||
c.AbortWithError(404, err)
|
c.AbortWithError(404, err)
|
||||||
|
|
|
@ -149,14 +149,6 @@ func PostHook(c *gin.Context) {
|
||||||
c.AbortWithError(404, err)
|
c.AbortWithError(404, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// persist the build config for historical correctness, restarts, etc
|
|
||||||
// conf, err := findOrPersistPipelineConfig(repo, remoteYamlConfig)
|
|
||||||
// if err != nil {
|
|
||||||
// logrus.Errorf("failure to find or persist build config for %s. %s", repo.FullName, err)
|
|
||||||
// c.AbortWithError(500, err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// build.ConfigID = conf.ID
|
|
||||||
|
|
||||||
// verify that pipeline can be built at all
|
// verify that pipeline can be built at all
|
||||||
// parsedPipelineConfig, err := yaml.ParseString(conf.Data)
|
// parsedPipelineConfig, err := yaml.ParseString(conf.Data)
|
||||||
|
@ -186,6 +178,17 @@ func PostHook(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// persist the build config for historical correctness, restarts, etc
|
||||||
|
for _, remoteYamlConfig := range remoteYamlConfigs {
|
||||||
|
conf, err := findOrPersistPipelineConfig(build, remoteYamlConfig.Data)
|
||||||
|
fmt.Println(conf)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failure to find or persist build config for %s. %s", repo.FullName, err)
|
||||||
|
c.AbortWithError(500, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
c.JSON(200, build)
|
c.JSON(200, build)
|
||||||
|
|
||||||
if build.Status == model.StatusBlocked {
|
if build.Status == model.StatusBlocked {
|
||||||
|
@ -262,25 +265,34 @@ func PostHook(c *gin.Context) {
|
||||||
queueBuild(build, repo, buildItems)
|
queueBuild(build, repo, buildItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
func findOrPersistPipelineConfig(repo *model.Repo, remoteYamlConfig []byte) (*model.Config, error) {
|
func findOrPersistPipelineConfig(build *model.Build, remoteYamlConfig []byte) (*model.Config, error) {
|
||||||
sha := shasum(remoteYamlConfig)
|
sha := shasum(remoteYamlConfig)
|
||||||
conf, err := Config.Storage.Config.ConfigFind(repo, sha)
|
conf, err := Config.Storage.Config.ConfigFindIdentical(build.RepoID, sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conf = &model.Config{
|
conf = &model.Config{
|
||||||
RepoID: repo.ID,
|
RepoID: build.RepoID,
|
||||||
Data: string(remoteYamlConfig),
|
Data: string(remoteYamlConfig),
|
||||||
Hash: sha,
|
Hash: sha,
|
||||||
}
|
}
|
||||||
err = Config.Storage.Config.ConfigCreate(conf)
|
err = Config.Storage.Config.ConfigCreate(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// retry in case we receive two hooks at the same time
|
// retry in case we receive two hooks at the same time
|
||||||
conf, err = Config.Storage.Config.ConfigFind(repo, sha)
|
conf, err = Config.Storage.Config.ConfigFindIdentical(build.RepoID, sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildConfig := &model.BuildConfig{
|
||||||
|
ConfigID: conf.ID,
|
||||||
|
BuildID: build.ID,
|
||||||
|
}
|
||||||
|
err = Config.Storage.Config.BuildConfigCreate(buildConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return conf, nil
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,17 +22,17 @@ import (
|
||||||
"github.com/russross/meddler"
|
"github.com/russross/meddler"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (db *datastore) ConfigLoad(buildID int64) ([]*model.Config, error) {
|
func (db *datastore) ConfigsForBuild(buildID int64) ([]*model.Config, error) {
|
||||||
stmt := sql.Lookup(db.driver, "config-find-id")
|
stmt := sql.Lookup(db.driver, "config-find-id")
|
||||||
var configs = []*model.Config{}
|
var configs = []*model.Config{}
|
||||||
err := meddler.QueryAll(db, &configs, stmt, buildID)
|
err := meddler.QueryAll(db, &configs, stmt, buildID)
|
||||||
return configs, err
|
return configs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *datastore) ConfigFind(repo *model.Repo, hash string) (*model.Config, error) {
|
func (db *datastore) ConfigFindIdentical(repoID int64, hash string) (*model.Config, error) {
|
||||||
stmt := sql.Lookup(db.driver, "config-find-repo-hash")
|
stmt := sql.Lookup(db.driver, "config-find-repo-hash")
|
||||||
conf := new(model.Config)
|
conf := new(model.Config)
|
||||||
err := meddler.QueryRow(db, conf, stmt, repo.ID, hash)
|
err := meddler.QueryRow(db, conf, stmt, repoID, hash)
|
||||||
return conf, err
|
return conf, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,3 +51,7 @@ func (db *datastore) ConfigFindApproved(config *model.Config) (bool, error) {
|
||||||
func (db *datastore) ConfigCreate(config *model.Config) error {
|
func (db *datastore) ConfigCreate(config *model.Config) error {
|
||||||
return meddler.Insert(db, "config", config)
|
return meddler.Insert(db, "config", config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *datastore) BuildConfigCreate(buildConfig *model.BuildConfig) error {
|
||||||
|
return meddler.Insert(db, "build_config", buildConfig)
|
||||||
|
}
|
||||||
|
|
|
@ -33,20 +33,28 @@ func TestConfig(t *testing.T) {
|
||||||
buildID = int64(1)
|
buildID = int64(1)
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := s.ConfigCreate(
|
config := &model.Config{
|
||||||
&model.Config{
|
RepoID: 2,
|
||||||
RepoID: 2,
|
Data: data,
|
||||||
BuildID: 1,
|
Hash: hash,
|
||||||
Data: data,
|
Name: "default",
|
||||||
Hash: hash,
|
}
|
||||||
Name: "default",
|
if err := s.ConfigCreate(config); err != nil {
|
||||||
|
t.Errorf("Unexpected error: insert config: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.BuildConfigCreate(
|
||||||
|
&model.BuildConfig{
|
||||||
|
ConfigID: config.ID,
|
||||||
|
BuildID: buildID,
|
||||||
},
|
},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
t.Errorf("Unexpected error: insert config: %s", err)
|
t.Errorf("Unexpected error: insert config: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config, err := s.ConfigFind(&model.Repo{ID: 2}, hash)
|
config, err := s.ConfigFindIdentical(int64(2), hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -57,9 +65,6 @@ func TestConfig(t *testing.T) {
|
||||||
if got, want := config.RepoID, int64(2); got != want {
|
if got, want := config.RepoID, int64(2); got != want {
|
||||||
t.Errorf("Want config repo id %d, got %d", want, got)
|
t.Errorf("Want config repo id %d, got %d", want, got)
|
||||||
}
|
}
|
||||||
if got, want := config.BuildID, buildID; got != want {
|
|
||||||
t.Errorf("Want config build id %d, got %d", want, got)
|
|
||||||
}
|
|
||||||
if got, want := config.Data, data; got != want {
|
if got, want := config.Data, data; got != want {
|
||||||
t.Errorf("Want config data %s, got %s", want, got)
|
t.Errorf("Want config data %s, got %s", want, got)
|
||||||
}
|
}
|
||||||
|
@ -70,7 +75,7 @@ func TestConfig(t *testing.T) {
|
||||||
t.Errorf("Want config name %s, got %s", want, got)
|
t.Errorf("Want config name %s, got %s", want, got)
|
||||||
}
|
}
|
||||||
|
|
||||||
loaded, err := s.ConfigLoad(buildID)
|
loaded, err := s.ConfigsForBuild(buildID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Want config by id, got error %q", err)
|
t.Errorf("Want config by id, got error %q", err)
|
||||||
return
|
return
|
||||||
|
@ -120,13 +125,17 @@ func TestConfigApproved(t *testing.T) {
|
||||||
s.CreateBuild(buildBlocked)
|
s.CreateBuild(buildBlocked)
|
||||||
s.CreateBuild(buildPending)
|
s.CreateBuild(buildPending)
|
||||||
conf := &model.Config{
|
conf := &model.Config{
|
||||||
RepoID: repo.ID,
|
ID: int64(8),
|
||||||
BuildID: buildBlocked.ID,
|
RepoID: repo.ID,
|
||||||
Data: data,
|
Data: data,
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
}
|
}
|
||||||
if err := s.ConfigCreate(conf); err != nil {
|
buildConfig := &model.BuildConfig{
|
||||||
t.Errorf("Unexpected error: insert config: %s", err)
|
ConfigID: int64(8),
|
||||||
|
BuildID: buildBlocked.ID,
|
||||||
|
}
|
||||||
|
if err := s.BuildConfigCreate(buildConfig); err != nil {
|
||||||
|
t.Errorf("Unexpected error: insert build_config: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,12 +146,16 @@ func TestConfigApproved(t *testing.T) {
|
||||||
|
|
||||||
s.CreateBuild(buildRunning)
|
s.CreateBuild(buildRunning)
|
||||||
conf2 := &model.Config{
|
conf2 := &model.Config{
|
||||||
RepoID: repo.ID,
|
ID: int64(9),
|
||||||
BuildID: buildRunning.ID,
|
RepoID: repo.ID,
|
||||||
Data: data,
|
Data: data,
|
||||||
Hash: "xxx",
|
Hash: "xxx",
|
||||||
}
|
}
|
||||||
if err := s.ConfigCreate(conf2); err != nil {
|
buildConfig2 := &model.BuildConfig{
|
||||||
|
ConfigID: int64(9),
|
||||||
|
BuildID: buildRunning.ID,
|
||||||
|
}
|
||||||
|
if err := s.BuildConfigCreate(buildConfig2); err != nil {
|
||||||
t.Errorf("Unexpected error: insert config: %s", err)
|
t.Errorf("Unexpected error: insert config: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,12 +161,8 @@ var migrations = []struct {
|
||||||
stmt: alterTableUpdateFileMeta,
|
stmt: alterTableUpdateFileMeta,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "alter-table-add-config-build-id",
|
name: "create-table-build-config",
|
||||||
stmt: alterTableAddConfigBuildId,
|
stmt: createTableBuildConfig,
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "update-table-set-config-config-id",
|
|
||||||
stmt: updateTableSetConfigConfigId,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "alter-table-add-config-name",
|
name: "alter-table-add-config-name",
|
||||||
|
@ -641,15 +637,17 @@ UPDATE files SET
|
||||||
`
|
`
|
||||||
|
|
||||||
//
|
//
|
||||||
// 019_add_column_config_build_id.sql
|
// 019_create_table_build_config.sql
|
||||||
//
|
//
|
||||||
|
|
||||||
var alterTableAddConfigBuildId = `
|
var createTableBuildConfig = `
|
||||||
ALTER TABLE config ADD COLUMN config_build_id INTEGER
|
CREATE TABLE IF NOT EXISTS build_config (
|
||||||
`
|
config_id INTEGER NOT NULL
|
||||||
|
,build_id INTEGER NOT NULL
|
||||||
var updateTableSetConfigConfigId = `
|
,PRIMARY KEY (config_id, build_id)
|
||||||
UPDATE config SET config_build_id = (SELECT builds.build_id FROM builds WHERE builds.build_config_id = config.config_id)
|
,FOREIGN KEY (config_id) REFERENCES config (config_id)
|
||||||
|
,FOREIGN KEY (build_id) REFERENCES builds (build_id)
|
||||||
|
);
|
||||||
`
|
`
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
-- name: alter-table-add-config-build-id
|
|
||||||
|
|
||||||
ALTER TABLE config ADD COLUMN config_build_id INTEGER
|
|
||||||
|
|
||||||
-- name: update-table-set-config-config-id
|
|
||||||
|
|
||||||
UPDATE config SET config_build_id = (SELECT builds.build_id FROM builds WHERE builds.build_config_id = config.config_id)
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
-- name: create-table-build-config
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS build_config (
|
||||||
|
config_id INTEGER NOT NULL
|
||||||
|
,build_id INTEGER NOT NULL
|
||||||
|
,PRIMARY KEY (config_id, build_id)
|
||||||
|
,FOREIGN KEY (config_id) REFERENCES config (config_id)
|
||||||
|
,FOREIGN KEY (build_id) REFERENCES builds (build_id)
|
||||||
|
);
|
|
@ -1,21 +1,20 @@
|
||||||
-- name: config-find-id
|
-- name: config-find-id
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
config_id
|
config.config_id
|
||||||
,config_repo_id
|
,config_repo_id
|
||||||
,config_build_id
|
|
||||||
,config_hash
|
,config_hash
|
||||||
,config_data
|
,config_data
|
||||||
,config_name
|
,config_name
|
||||||
FROM config
|
FROM config
|
||||||
WHERE config_build_id = ?
|
LEFT JOIN build_config ON config.config_id = build_config.config_id
|
||||||
|
WHERE build_config.build_id = ?
|
||||||
|
|
||||||
-- name: config-find-repo-hash
|
-- name: config-find-repo-hash
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
config_id
|
config_id
|
||||||
,config_repo_id
|
,config_repo_id
|
||||||
,config_build_id
|
|
||||||
,config_hash
|
,config_hash
|
||||||
,config_data
|
,config_data
|
||||||
,config_name
|
,config_name
|
||||||
|
@ -27,6 +26,10 @@ WHERE config_repo_id = ?
|
||||||
|
|
||||||
SELECT build_id FROM builds
|
SELECT build_id FROM builds
|
||||||
WHERE build_repo_id = ?
|
WHERE build_repo_id = ?
|
||||||
AND build_id in (SELECT config_build_id FROM config WHERE config.config_id = ?)
|
AND build_id in (
|
||||||
|
SELECT build_id
|
||||||
|
FROM build_config
|
||||||
|
WHERE build_config.config_id = ?
|
||||||
|
)
|
||||||
AND build_status NOT IN ('blocked', 'pending')
|
AND build_status NOT IN ('blocked', 'pending')
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
|
|
|
@ -55,21 +55,20 @@ var index = map[string]string{
|
||||||
|
|
||||||
var configFindId = `
|
var configFindId = `
|
||||||
SELECT
|
SELECT
|
||||||
config_id
|
config.config_id
|
||||||
,config_repo_id
|
,config_repo_id
|
||||||
,config_build_id
|
|
||||||
,config_hash
|
,config_hash
|
||||||
,config_data
|
,config_data
|
||||||
,config_name
|
,config_name
|
||||||
FROM config
|
FROM config
|
||||||
WHERE config_build_id = ?
|
LEFT JOIN build_config ON config.config_id = build_config.config_id
|
||||||
|
WHERE build_config.build_id = ?
|
||||||
`
|
`
|
||||||
|
|
||||||
var configFindRepoHash = `
|
var configFindRepoHash = `
|
||||||
SELECT
|
SELECT
|
||||||
config_id
|
config_id
|
||||||
,config_repo_id
|
,config_repo_id
|
||||||
,config_build_id
|
|
||||||
,config_hash
|
,config_hash
|
||||||
,config_data
|
,config_data
|
||||||
,config_name
|
,config_name
|
||||||
|
@ -81,7 +80,11 @@ WHERE config_repo_id = ?
|
||||||
var configFindApproved = `
|
var configFindApproved = `
|
||||||
SELECT build_id FROM builds
|
SELECT build_id FROM builds
|
||||||
WHERE build_repo_id = ?
|
WHERE build_repo_id = ?
|
||||||
AND build_id in (SELECT config_build_id FROM config WHERE config.config_id = ?)
|
AND build_id in (
|
||||||
|
SELECT build_id
|
||||||
|
FROM build_config
|
||||||
|
WHERE build_config.config_id = ?
|
||||||
|
)
|
||||||
AND build_status NOT IN ('blocked', 'pending')
|
AND build_status NOT IN ('blocked', 'pending')
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
`
|
`
|
||||||
|
|
|
@ -111,10 +111,11 @@ type Store interface {
|
||||||
PermDelete(perm *model.Perm) error
|
PermDelete(perm *model.Perm) error
|
||||||
PermFlush(user *model.User, before int64) error
|
PermFlush(user *model.User, before int64) error
|
||||||
|
|
||||||
ConfigLoad(int64) ([]*model.Config, error)
|
ConfigsForBuild(buildID int64) ([]*model.Config, error)
|
||||||
ConfigFind(*model.Repo, string) (*model.Config, error)
|
ConfigFindIdentical(repoID int64, sha string) (*model.Config, error)
|
||||||
ConfigFindApproved(*model.Config) (bool, error)
|
ConfigFindApproved(*model.Config) (bool, error)
|
||||||
ConfigCreate(*model.Config) error
|
ConfigCreate(*model.Config) error
|
||||||
|
BuildConfigCreate(*model.BuildConfig) error
|
||||||
|
|
||||||
SenderFind(*model.Repo, string) (*model.Sender, error)
|
SenderFind(*model.Repo, string) (*model.Sender, error)
|
||||||
SenderList(*model.Repo) ([]*model.Sender, error)
|
SenderList(*model.Repo) ([]*model.Sender, error)
|
||||||
|
|
Loading…
Reference in a new issue