persist and compare yaml for gating
This commit is contained in:
parent
1866ab3cab
commit
4569b60f09
21 changed files with 395 additions and 36 deletions
|
@ -391,6 +391,7 @@ func setupEvilGlobals(c *cli.Context, v store.Store) {
|
|||
|
||||
// storage
|
||||
droneserver.Config.Storage.Files = v
|
||||
droneserver.Config.Storage.Config = v
|
||||
|
||||
// services
|
||||
droneserver.Config.Services.Queue = setupQueue(c, v)
|
||||
|
|
|
@ -4,6 +4,7 @@ package model
|
|||
type Build struct {
|
||||
ID int64 `json:"id" meddler:"build_id,pk"`
|
||||
RepoID int64 `json:"-" meddler:"build_repo_id"`
|
||||
ConfigID int64 `json:"-" meddler:"build_config_id"`
|
||||
Number int `json:"number" meddler:"build_number"`
|
||||
Parent int `json:"parent" meddler:"build_parent"`
|
||||
Event string `json:"event" meddler:"build_event"`
|
||||
|
|
|
@ -1,24 +1,18 @@
|
|||
package model
|
||||
|
||||
// Config defines system configuration parameters.
|
||||
// ConfigStore persists pipeline configuration to storage.
|
||||
type ConfigStore interface {
|
||||
ConfigLoad(int64) (*Config, error)
|
||||
ConfigFind(*Repo, string) (*Config, error)
|
||||
ConfigUpdate(*Config) error
|
||||
ConfigInsert(*Config) error
|
||||
}
|
||||
|
||||
// Config represents a pipeline configuration.
|
||||
type Config struct {
|
||||
Open bool // Enables open registration
|
||||
Secret string // Secret token used to authenticate agents
|
||||
Admins map[string]bool // Administrative users
|
||||
Orgs map[string]bool // Organization whitelist
|
||||
}
|
||||
|
||||
// IsAdmin returns true if the user is a member of the administrator list.
|
||||
func (c *Config) IsAdmin(user *User) bool {
|
||||
return c.Admins[user.Login]
|
||||
}
|
||||
|
||||
// IsMember returns true if the user is a member of the whitelisted teams.
|
||||
func (c *Config) IsMember(teams []*Team) bool {
|
||||
for _, team := range teams {
|
||||
if c.Orgs[team.Login] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
ID int64 `json:"-" meddler:"config_id,pk"`
|
||||
RepoID int64 `json:"-" meddler:"config_repo_id"`
|
||||
Data string `json:"data" meddler:"config_data"`
|
||||
Hash string `json:"hash" meddler:"config_hash"`
|
||||
Approved bool `json:"approved" meddler:"config_approved"`
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ type Repo struct {
|
|||
IsTrusted bool `json:"trusted" meddler:"repo_trusted"`
|
||||
IsStarred bool `json:"starred,omitempty" meddler:"-"`
|
||||
IsGated bool `json:"gated" meddler:"repo_gated"`
|
||||
IsGatedConf bool `json:"gated_conf" meddler:"repo_gated_conf"`
|
||||
AllowPull bool `json:"allow_pr" meddler:"repo_allow_pr"`
|
||||
AllowPush bool `json:"allow_push" meddler:"repo_allow_push"`
|
||||
AllowDeploy bool `json:"allow_deploys" meddler:"repo_allow_deploys"`
|
||||
|
|
24
model/settings.go
Normal file
24
model/settings.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package model
|
||||
|
||||
// Settings defines system configuration parameters.
|
||||
type Settings struct {
|
||||
Open bool // Enables open registration
|
||||
Secret string // Secret token used to authenticate agents
|
||||
Admins map[string]bool // Administrative users
|
||||
Orgs map[string]bool // Organization whitelist
|
||||
}
|
||||
|
||||
// IsAdmin returns true if the user is a member of the administrator list.
|
||||
func (c *Settings) IsAdmin(user *User) bool {
|
||||
return c.Admins[user.Login]
|
||||
}
|
||||
|
||||
// IsMember returns true if the user is a member of the whitelisted teams.
|
||||
func (c *Settings) IsMember(teams []*Team) bool {
|
||||
for _, team := range teams {
|
||||
if c.Orgs[team.Login] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -19,8 +19,8 @@ func Config(cli *cli.Context) gin.HandlerFunc {
|
|||
}
|
||||
|
||||
// helper function to create the configuration from the CLI context.
|
||||
func setupConfig(c *cli.Context) *model.Config {
|
||||
return &model.Config{
|
||||
func setupConfig(c *cli.Context) *model.Settings {
|
||||
return &model.Settings{
|
||||
Open: c.Bool("open"),
|
||||
Secret: c.String("agent-secret"),
|
||||
Admins: sliceToMap2(c.StringSlice("admin")),
|
||||
|
|
|
@ -45,7 +45,7 @@ func SetUser() gin.HandlerFunc {
|
|||
})
|
||||
if err == nil {
|
||||
confv := c.MustGet("config")
|
||||
if conf, ok := confv.(*model.Config); ok {
|
||||
if conf, ok := confv.(*model.Settings); ok {
|
||||
user.Admin = conf.IsAdmin(user)
|
||||
}
|
||||
c.Set("user", user)
|
||||
|
|
|
@ -174,12 +174,16 @@ func PostApproval(c *gin.Context) {
|
|||
//
|
||||
|
||||
// fetch the build file from the database
|
||||
raw, err := remote_.File(user, repo, build, repo.Config)
|
||||
conf, err := Config.Storage.Config.ConfigLoad(build.ConfigID)
|
||||
if err != nil {
|
||||
logrus.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
||||
c.AbortWithError(404, err)
|
||||
return
|
||||
}
|
||||
if !conf.Approved {
|
||||
conf.Approved = true
|
||||
Config.Storage.Config.ConfigUpdate(conf)
|
||||
}
|
||||
|
||||
netrc, err := remote_.Netrc(user, repo)
|
||||
if err != nil {
|
||||
|
@ -222,7 +226,7 @@ func PostApproval(c *gin.Context) {
|
|||
Secs: secs,
|
||||
Regs: regs,
|
||||
Link: httputil.GetURL(c.Request),
|
||||
Yaml: string(raw),
|
||||
Yaml: conf.Data,
|
||||
}
|
||||
items, err := b.Build()
|
||||
if err != nil {
|
||||
|
@ -394,12 +398,16 @@ func PostBuild(c *gin.Context) {
|
|||
}
|
||||
|
||||
// fetch the .drone.yml file from the database
|
||||
raw, err := remote_.File(user, repo, build, repo.Config)
|
||||
conf, err := Config.Storage.Config.ConfigLoad(build.ConfigID)
|
||||
if err != nil {
|
||||
logrus.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
||||
c.AbortWithError(404, err)
|
||||
return
|
||||
}
|
||||
if !conf.Approved {
|
||||
conf.Approved = true
|
||||
Config.Storage.Config.ConfigUpdate(conf)
|
||||
}
|
||||
|
||||
netrc, err := remote_.Netrc(user, repo)
|
||||
if err != nil {
|
||||
|
@ -493,7 +501,7 @@ func PostBuild(c *gin.Context) {
|
|||
Secs: secs,
|
||||
Regs: regs,
|
||||
Link: httputil.GetURL(c.Request),
|
||||
Yaml: string(raw),
|
||||
Yaml: conf.Data,
|
||||
}
|
||||
// TODO inject environment varibles !!!!!! buildParams
|
||||
items, err := b.Build()
|
||||
|
|
|
@ -2,6 +2,7 @@ package server
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
@ -131,12 +132,38 @@ func PostHook(c *gin.Context) {
|
|||
}
|
||||
|
||||
// fetch the build file from the database
|
||||
raw, err := remote_.File(user, repo, build, repo.Config)
|
||||
confb, err := remote_.File(user, repo, build, repo.Config)
|
||||
if err != nil {
|
||||
logrus.Errorf("failure to get build config for %s. %s", repo.FullName, err)
|
||||
c.AbortWithError(404, err)
|
||||
return
|
||||
}
|
||||
sha := shasum(confb)
|
||||
conf, err := Config.Storage.Config.ConfigFind(repo, sha)
|
||||
if err != nil {
|
||||
conf = &model.Config{
|
||||
RepoID: repo.ID,
|
||||
Data: string(confb),
|
||||
Hash: sha,
|
||||
Approved: false,
|
||||
}
|
||||
if user.Login == repo.Owner || build.Event != model.EventPull {
|
||||
conf.Approved = true
|
||||
}
|
||||
err = Config.Storage.Config.ConfigInsert(conf)
|
||||
if err != nil {
|
||||
logrus.Errorf("failure to persist config for %s. %s", repo.FullName, err)
|
||||
c.AbortWithError(500, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if !conf.Approved {
|
||||
if user.Login == repo.Owner || build.Event != model.EventPull || !repo.IsGatedConf {
|
||||
conf.Approved = true
|
||||
Config.Storage.Config.ConfigUpdate(conf)
|
||||
}
|
||||
}
|
||||
build.ConfigID = conf.ID
|
||||
|
||||
netrc, err := remote_.Netrc(user, repo)
|
||||
if err != nil {
|
||||
|
@ -145,7 +172,7 @@ func PostHook(c *gin.Context) {
|
|||
}
|
||||
|
||||
// verify the branches can be built vs skipped
|
||||
branches, err := yaml.ParseBytes(raw)
|
||||
branches, err := yaml.ParseString(conf.Data)
|
||||
if err == nil {
|
||||
if !branches.Branches.Match(build.Branch) && build.Event != model.EventTag && build.Event != model.EventDeploy {
|
||||
c.String(200, "Branch does not match restrictions defined in yaml")
|
||||
|
@ -168,7 +195,7 @@ func PostHook(c *gin.Context) {
|
|||
build.Verified = true
|
||||
build.Status = model.StatusPending
|
||||
|
||||
if repo.IsGated {
|
||||
if repo.IsGated || repo.IsGatedConf {
|
||||
allowed, _ := Config.Services.Senders.SenderAllowed(user, repo, build)
|
||||
if !allowed {
|
||||
build.Status = model.StatusBlocked
|
||||
|
@ -212,7 +239,7 @@ func PostHook(c *gin.Context) {
|
|||
Secs: secs,
|
||||
Regs: regs,
|
||||
Link: httputil.GetURL(c.Request),
|
||||
Yaml: string(raw),
|
||||
Yaml: conf.Data,
|
||||
}
|
||||
items, err := b.Build()
|
||||
if err != nil {
|
||||
|
@ -442,7 +469,7 @@ func (b *builder) Build() ([]*buildItem, error) {
|
|||
linter.WithTrusted(b.Repo.IsTrusted),
|
||||
).Lint(parsed)
|
||||
if lerr != nil {
|
||||
return nil, err
|
||||
return nil, lerr
|
||||
}
|
||||
|
||||
var registries []compiler.Registry
|
||||
|
@ -511,3 +538,8 @@ func (b *builder) Build() ([]*buildItem, error) {
|
|||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func shasum(raw []byte) string {
|
||||
sum := sha256.Sum256(raw)
|
||||
return fmt.Sprintf("%x", sum)
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ type tokenPayload struct {
|
|||
}
|
||||
|
||||
// ToConfig returns the config from the Context
|
||||
func ToConfig(c *gin.Context) *model.Config {
|
||||
func ToConfig(c *gin.Context) *model.Settings {
|
||||
v := c.MustGet("config")
|
||||
return v.(*model.Config)
|
||||
return v.(*model.Settings)
|
||||
}
|
||||
|
|
|
@ -41,8 +41,9 @@ var Config = struct {
|
|||
// Repos model.RepoStore
|
||||
// Builds model.BuildStore
|
||||
// Logs model.LogStore
|
||||
Files model.FileStore
|
||||
Procs model.ProcStore
|
||||
Config model.ConfigStore
|
||||
Files model.FileStore
|
||||
Procs model.ProcStore
|
||||
// Registries model.RegistryStore
|
||||
// Secrets model.SecretStore
|
||||
}
|
||||
|
|
29
store/datastore/config.go
Normal file
29
store/datastore/config.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/store/datastore/sql"
|
||||
"github.com/russross/meddler"
|
||||
)
|
||||
|
||||
func (db *datastore) ConfigLoad(id int64) (*model.Config, error) {
|
||||
stmt := sql.Lookup(db.driver, "config-find-repo-id")
|
||||
conf := new(model.Config)
|
||||
err := meddler.QueryRow(db, conf, stmt, id)
|
||||
return conf, err
|
||||
}
|
||||
|
||||
func (db *datastore) ConfigFind(repo *model.Repo, hash string) (*model.Config, error) {
|
||||
stmt := sql.Lookup(db.driver, "config-find-repo-hash")
|
||||
conf := new(model.Config)
|
||||
err := meddler.QueryRow(db, conf, stmt, repo.ID, hash)
|
||||
return conf, err
|
||||
}
|
||||
|
||||
func (db *datastore) ConfigUpdate(config *model.Config) error {
|
||||
return meddler.Update(db, "config", config)
|
||||
}
|
||||
|
||||
func (db *datastore) ConfigInsert(config *model.Config) error {
|
||||
return meddler.Insert(db, "config", config)
|
||||
}
|
106
store/datastore/config_test.go
Normal file
106
store/datastore/config_test.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
s := newTest()
|
||||
defer func() {
|
||||
s.Exec("delete from config")
|
||||
s.Close()
|
||||
}()
|
||||
|
||||
var (
|
||||
data = "pipeline: [ { image: golang, commands: [ go build, go test ] } ]"
|
||||
hash = "8d8647c9aa90d893bfb79dddbe901f03e258588121e5202632f8ae5738590b26"
|
||||
)
|
||||
|
||||
if err := s.ConfigInsert(
|
||||
&model.Config{
|
||||
RepoID: 2,
|
||||
Data: data,
|
||||
Hash: hash,
|
||||
Approved: false,
|
||||
},
|
||||
); err != nil {
|
||||
t.Errorf("Unexpected error: insert config: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := s.ConfigFind(&model.Repo{ID: 2}, hash)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if got, want := config.ID, int64(1); got != want {
|
||||
t.Errorf("Want config id %d, got %d", want, got)
|
||||
}
|
||||
if got, want := config.RepoID, int64(2); got != want {
|
||||
t.Errorf("Want config repo id %d, got %d", want, got)
|
||||
}
|
||||
if got, want := config.Data, data; got != want {
|
||||
t.Errorf("Want config data %s, got %s", want, got)
|
||||
}
|
||||
if got, want := config.Hash, hash; got != want {
|
||||
t.Errorf("Want config hash %s, got %s", want, got)
|
||||
}
|
||||
if got, want := config.Approved, false; got != want {
|
||||
t.Errorf("Want config approved %v, got %v", want, got)
|
||||
}
|
||||
|
||||
config.Approved = true
|
||||
err = s.ConfigUpdate(config)
|
||||
if err != nil {
|
||||
t.Errorf("Want config updated, got error %q", err)
|
||||
return
|
||||
}
|
||||
|
||||
updated, err := s.ConfigFind(&model.Repo{ID: 2}, hash)
|
||||
if err != nil {
|
||||
t.Errorf("Want config find, got error %q", err)
|
||||
return
|
||||
}
|
||||
if got, want := updated.Approved, true; got != want {
|
||||
t.Errorf("Want config approved updated %v, got %v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// func TestConfigIndexes(t *testing.T) {
|
||||
// s := newTest()
|
||||
// defer func() {
|
||||
// s.Exec("delete from config")
|
||||
// s.Close()
|
||||
// }()
|
||||
//
|
||||
// if err := s.FileCreate(
|
||||
// &model.File{
|
||||
// BuildID: 1,
|
||||
// ProcID: 1,
|
||||
// Name: "hello.txt",
|
||||
// Size: 11,
|
||||
// Mime: "text/plain",
|
||||
// },
|
||||
// bytes.NewBufferString("hello world"),
|
||||
// ); err != nil {
|
||||
// t.Errorf("Unexpected error: insert file: %s", err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // fail due to duplicate file name
|
||||
// if err := s.FileCreate(
|
||||
// &model.File{
|
||||
// BuildID: 1,
|
||||
// ProcID: 1,
|
||||
// Name: "hello.txt",
|
||||
// Mime: "text/plain",
|
||||
// Size: 11,
|
||||
// },
|
||||
// bytes.NewBufferString("hello world"),
|
||||
// ); err == nil {
|
||||
// t.Errorf("Unexpected error: dupliate pid")
|
||||
// }
|
||||
// }
|
21
store/datastore/ddl/mysql/16.sql
Normal file
21
store/datastore/ddl/mysql/16.sql
Normal file
|
@ -0,0 +1,21 @@
|
|||
-- +migrate Up
|
||||
|
||||
CREATE TABLE config (
|
||||
config_id INTEGER PRIMARY KEY AUTO_INCREMENT
|
||||
,config_repo_id INTEGER
|
||||
,config_hash VARCHAR(250)
|
||||
,config_data MEDIUMBLOB
|
||||
,config_approved BOOLEAN
|
||||
|
||||
,UNIQUE(config_hash, config_repo_id)
|
||||
);
|
||||
|
||||
ALTER TABLE builds ADD COLUMN build_config_id INTEGER;
|
||||
UPDATE builds set build_config_id = 0;
|
||||
|
||||
ALTER TABLE repos ADD COLUMN repo_gated_conf BOOLEAN;
|
||||
UPDATE repos SET repo_gated_conf = 0;
|
||||
|
||||
-- +migrate Down
|
||||
|
||||
DROP TABLE config;
|
21
store/datastore/ddl/postgres/16.sql
Normal file
21
store/datastore/ddl/postgres/16.sql
Normal file
|
@ -0,0 +1,21 @@
|
|||
-- +migrate Up
|
||||
|
||||
CREATE TABLE config (
|
||||
config_id SERIAL PRIMARY KEY
|
||||
,config_repo_id INTEGER
|
||||
,config_hash VARCHAR(250)
|
||||
,config_data BYTEA
|
||||
,config_approved BOOLEAN
|
||||
|
||||
,UNIQUE(config_hash, config_repo_id)
|
||||
);
|
||||
|
||||
ALTER TABLE builds ADD COLUMN build_config_id INTEGER;
|
||||
UPDATE builds set build_config_id = 0;
|
||||
|
||||
ALTER TABLE repos ADD COLUMN repo_gated_conf BOOLEAN;
|
||||
UPDATE repos SET repo_gated_conf = 0;
|
||||
|
||||
-- +migrate Down
|
||||
|
||||
DROP TABLE config;
|
21
store/datastore/ddl/sqlite3/16.sql
Normal file
21
store/datastore/ddl/sqlite3/16.sql
Normal file
|
@ -0,0 +1,21 @@
|
|||
-- +migrate Up
|
||||
|
||||
CREATE TABLE config (
|
||||
config_id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
,config_repo_id INTEGER
|
||||
,config_hash TEXT
|
||||
,config_data BLOB
|
||||
,config_approved BOOLEAN
|
||||
|
||||
,UNIQUE(config_hash, config_repo_id)
|
||||
);
|
||||
|
||||
ALTER TABLE builds ADD COLUMN build_config_id INTEGER;
|
||||
UPDATE builds set build_config_id = 0;
|
||||
|
||||
ALTER TABLE repos ADD COLUMN repo_gated_conf BOOLEAN;
|
||||
UPDATE repos SET repo_gated_conf = 0;
|
||||
|
||||
-- +migrate Down
|
||||
|
||||
DROP TABLE config;
|
22
store/datastore/sql/postgres/files/config.sql
Normal file
22
store/datastore/sql/postgres/files/config.sql
Normal file
|
@ -0,0 +1,22 @@
|
|||
-- name: config-find-id
|
||||
|
||||
SELECT
|
||||
config_id
|
||||
,config_repo_id
|
||||
,config_hash
|
||||
,config_data
|
||||
,config_approved
|
||||
FROM config
|
||||
WHERE config_id = $1
|
||||
|
||||
-- name: config-find-repo-hash
|
||||
|
||||
SELECT
|
||||
config_id
|
||||
,config_repo_id
|
||||
,config_hash
|
||||
,config_data
|
||||
,config_approved
|
||||
FROM config
|
||||
WHERE config_repo_id = $1
|
||||
AND config_hash = $2
|
|
@ -6,6 +6,8 @@ func Lookup(name string) string {
|
|||
}
|
||||
|
||||
var index = map[string]string{
|
||||
"config-find-id": configFindId,
|
||||
"config-find-repo-hash": configFindRepoHash,
|
||||
"count-users": countUsers,
|
||||
"count-repos": countRepos,
|
||||
"count-builds": countBuilds,
|
||||
|
@ -33,6 +35,29 @@ var index = map[string]string{
|
|||
"task-delete": taskDelete,
|
||||
}
|
||||
|
||||
var configFindId = `
|
||||
SELECT
|
||||
config_id
|
||||
,config_repo_id
|
||||
,config_hash
|
||||
,config_data
|
||||
,config_approved
|
||||
FROM config
|
||||
WHERE config_id = $1
|
||||
`
|
||||
|
||||
var configFindRepoHash = `
|
||||
SELECT
|
||||
config_id
|
||||
,config_repo_id
|
||||
,config_hash
|
||||
,config_data
|
||||
,config_approved
|
||||
FROM config
|
||||
WHERE config_repo_id = $1
|
||||
AND config_hash = $2
|
||||
`
|
||||
|
||||
var countUsers = `
|
||||
SELECT reltuples
|
||||
FROM pg_class WHERE relname = 'users';
|
||||
|
|
22
store/datastore/sql/sqlite/files/config.sql
Normal file
22
store/datastore/sql/sqlite/files/config.sql
Normal file
|
@ -0,0 +1,22 @@
|
|||
-- name: config-find-id
|
||||
|
||||
SELECT
|
||||
config_id
|
||||
,config_repo_id
|
||||
,config_hash
|
||||
,config_data
|
||||
,config_approved
|
||||
FROM config
|
||||
WHERE config_id = ?
|
||||
|
||||
-- name: config-find-repo-hash
|
||||
|
||||
SELECT
|
||||
config_id
|
||||
,config_repo_id
|
||||
,config_hash
|
||||
,config_data
|
||||
,config_approved
|
||||
FROM config
|
||||
WHERE config_repo_id = ?
|
||||
AND config_hash = ?
|
|
@ -6,6 +6,8 @@ func Lookup(name string) string {
|
|||
}
|
||||
|
||||
var index = map[string]string{
|
||||
"config-find-id": configFindId,
|
||||
"config-find-repo-hash": configFindRepoHash,
|
||||
"count-users": countUsers,
|
||||
"count-repos": countRepos,
|
||||
"count-builds": countBuilds,
|
||||
|
@ -33,6 +35,29 @@ var index = map[string]string{
|
|||
"task-delete": taskDelete,
|
||||
}
|
||||
|
||||
var configFindId = `
|
||||
SELECT
|
||||
config_id
|
||||
,config_repo_id
|
||||
,config_hash
|
||||
,config_data
|
||||
,config_approved
|
||||
FROM config
|
||||
WHERE config_id = ?
|
||||
`
|
||||
|
||||
var configFindRepoHash = `
|
||||
SELECT
|
||||
config_id
|
||||
,config_repo_id
|
||||
,config_hash
|
||||
,config_data
|
||||
,config_approved
|
||||
FROM config
|
||||
WHERE config_repo_id = ?
|
||||
AND config_hash = ?
|
||||
`
|
||||
|
||||
var countUsers = `
|
||||
SELECT count(1)
|
||||
FROM users
|
||||
|
|
|
@ -92,6 +92,11 @@ type Store interface {
|
|||
// new functions
|
||||
//
|
||||
|
||||
ConfigLoad(int64) (*model.Config, error)
|
||||
ConfigFind(*model.Repo, string) (*model.Config, error)
|
||||
ConfigUpdate(*model.Config) error
|
||||
ConfigInsert(*model.Config) error
|
||||
|
||||
SenderFind(*model.Repo, string) (*model.Sender, error)
|
||||
SenderList(*model.Repo) ([]*model.Sender, error)
|
||||
SenderCreate(*model.Sender) error
|
||||
|
|
Loading…
Reference in a new issue