From 8348c858ba8ef37330b3aea1d2b4df6089b87a3b Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Tue, 11 Apr 2017 19:06:45 +0200 Subject: [PATCH] wip enable pluggable secret and registry backends --- client/client.go | 48 ++-- client/client_impl.go | 118 +++------ drone/global.go | 11 - drone/global_secret.go | 13 - drone/global_secret_add.go | 33 --- drone/global_secret_info.go | 1 - drone/global_secret_list.go | 25 -- drone/global_secret_rm.go | 25 -- drone/main.go | 3 - drone/org.go | 11 - drone/org_secret.go | 13 - drone/org_secret_add.go | 34 --- drone/org_secret_info.go | 1 - drone/org_secret_list.go | 32 --- drone/org_secret_rm.go | 34 --- drone/registry_info.go | 2 +- drone/registry_list.go | 3 +- drone/secret.go | 131 +--------- drone/secret_add.go | 77 ++++-- drone/secret_info.go | 57 +++++ drone/secret_list.go | 67 ++++- drone/secret_rm.go | 31 ++- drone/secret_set.go | 57 +++++ drone/server.go | 29 ++- drone/sign.go | 50 ---- model/approval.go | 17 -- model/gate.go | 23 -- model/registry.go | 9 + model/repo.go | 1 + model/repo_secret.go | 58 ----- model/secret.go | 92 ++++--- model/secret_test.go | 41 +--- model/sender.go | 28 +++ model/sys.go | 9 - model/team_secret.go | 58 ----- plugins/internal/http.go | 61 +++++ plugins/registry.go | 45 ++++ plugins/registry/builtin.go | 38 +++ plugins/registry/builtin_test.go | 1 + plugins/registry/plugin.go | 46 ++++ plugins/registry/plugin_test.go | 1 + plugins/secrets.go | 45 ++++ plugins/secrets/builtin.go | 38 +++ plugins/secrets/builtin_test.go | 1 + plugins/secrets/plugin.go | 46 ++++ plugins/secrets/plugin_test.go | 1 + plugins/sender/builtin.go | 44 ++++ plugins/sender/builtin_test.go | 1 + plugins/sender/plugin.go | 49 ++++ plugins/sender/plugin_test.go | 1 + router/middleware/config.go | 6 +- router/middleware/store.go | 52 ++++ router/router.go | 26 +- server/build.go | 22 +- server/hook.go | 88 ++----- server/registry.go | 19 +- server/repo.go | 13 +- server/rpc.go | 81 +++++-- server/secret.go | 229 +++++++----------- server/stream.go | 4 +- store/datastore/ddl/mysql/14.sql | 22 ++ store/datastore/ddl/postgres/14.sql | 22 ++ store/datastore/ddl/sqlite3/14.sql | 22 ++ store/datastore/repo_secret.go | 53 ---- store/datastore/repo_secret_test.go | 98 -------- store/datastore/secret.go | 35 +++ store/datastore/secret_test.go | 139 +++++++++++ store/datastore/sender.go | 35 +++ store/datastore/sender_test.go | 131 ++++++++++ store/datastore/sql/postgres/files/secret.sql | 32 +++ store/datastore/sql/postgres/files/sender.sql | 30 +++ store/datastore/sql/postgres/sql_gen.go | 71 ++++++ store/datastore/sql/sqlite/files/secret.sql | 32 +++ store/datastore/sql/sqlite/files/sender.sql | 30 +++ store/datastore/sql/sqlite/sql_gen.go | 71 ++++++ store/datastore/team_secret.go | 53 ---- store/datastore/team_secret_test.go | 98 -------- store/store.go | 207 +--------------- 78 files changed, 1770 insertions(+), 1611 deletions(-) delete mode 100644 drone/global.go delete mode 100644 drone/global_secret.go delete mode 100644 drone/global_secret_add.go delete mode 100644 drone/global_secret_info.go delete mode 100644 drone/global_secret_list.go delete mode 100644 drone/global_secret_rm.go delete mode 100644 drone/org.go delete mode 100644 drone/org_secret.go delete mode 100644 drone/org_secret_add.go delete mode 100644 drone/org_secret_info.go delete mode 100644 drone/org_secret_list.go delete mode 100644 drone/org_secret_rm.go create mode 100644 drone/secret_set.go delete mode 100644 drone/sign.go delete mode 100644 model/approval.go delete mode 100644 model/gate.go delete mode 100644 model/repo_secret.go create mode 100644 model/sender.go delete mode 100644 model/sys.go delete mode 100644 model/team_secret.go create mode 100644 plugins/internal/http.go create mode 100644 plugins/registry.go create mode 100644 plugins/registry/builtin.go create mode 100644 plugins/registry/builtin_test.go create mode 100644 plugins/registry/plugin.go create mode 100644 plugins/registry/plugin_test.go create mode 100644 plugins/secrets.go create mode 100644 plugins/secrets/builtin.go create mode 100644 plugins/secrets/builtin_test.go create mode 100644 plugins/secrets/plugin.go create mode 100644 plugins/secrets/plugin_test.go create mode 100644 plugins/sender/builtin.go create mode 100644 plugins/sender/builtin_test.go create mode 100644 plugins/sender/plugin.go create mode 100644 plugins/sender/plugin_test.go create mode 100644 store/datastore/ddl/mysql/14.sql create mode 100644 store/datastore/ddl/postgres/14.sql create mode 100644 store/datastore/ddl/sqlite3/14.sql delete mode 100644 store/datastore/repo_secret.go delete mode 100644 store/datastore/repo_secret_test.go create mode 100644 store/datastore/secret.go create mode 100644 store/datastore/secret_test.go create mode 100644 store/datastore/sender.go create mode 100644 store/datastore/sender_test.go create mode 100644 store/datastore/sql/postgres/files/secret.sql create mode 100644 store/datastore/sql/postgres/files/sender.sql create mode 100644 store/datastore/sql/sqlite/files/secret.sql create mode 100644 store/datastore/sql/sqlite/files/sender.sql delete mode 100644 store/datastore/team_secret.go delete mode 100644 store/datastore/team_secret_test.go diff --git a/client/client.go b/client/client.go index 443e62ca2..712204b70 100644 --- a/client/client.go +++ b/client/client.go @@ -26,9 +26,6 @@ type Client interface { // UserDel deletes a user account. UserDel(string) error - // // UserFeed returns the user's activity feed. - // UserFeed() ([]*Activity, error) - // Repo returns a repository by name. Repo(string, string) (*model.Repo, error) @@ -48,36 +45,6 @@ type Client interface { // RepoDel deletes a repository. RepoDel(string, string) error - // Sign returns a cryptographic signature for the input string. - Sign(string, string, []byte) ([]byte, error) - - // SecretList returns a list of all repository secrets. - SecretList(string, string) ([]*model.Secret, error) - - // SecretPost create or updates a repository secret. - SecretPost(string, string, *model.Secret) error - - // SecretDel deletes a named repository secret. - SecretDel(string, string, string) error - - // TeamSecretList returns a list of all team secrets. - TeamSecretList(string) ([]*model.Secret, error) - - // TeamSecretPost create or updates a team secret. - TeamSecretPost(string, *model.Secret) error - - // TeamSecretDel deletes a named team secret. - TeamSecretDel(string, string) error - - // GlobalSecretList returns a list of global secrets. - GlobalSecretList() ([]*model.Secret, error) - - // GlobalSecretPost create or updates a global secret. - GlobalSecretPost(secret *model.Secret) error - - // GlobalSecretDel deletes a named global secret. - GlobalSecretDel(secret string) error - // Build returns a repository build by number. Build(string, string, int) (*model.Build, error) @@ -129,4 +96,19 @@ type Client interface { // RegistryDelete deletes a registry. RegistryDelete(owner, name, hostname string) error + + // Secret returns a secret by name. + Secret(owner, name, secret string) (*model.Secret, error) + + // SecretList returns a list of all repository secrets. + SecretList(owner, name string) ([]*model.Secret, error) + + // SecretCreate creates a registry. + SecretCreate(owner, name string, secret *model.Secret) (*model.Secret, error) + + // SecretUpdate updates a registry. + SecretUpdate(owner, name string, secret *model.Secret) (*model.Secret, error) + + // SecretDelete deletes a secret. + SecretDelete(owner, name, secret string) error } diff --git a/client/client_impl.go b/client/client_impl.go index d1da1adf2..9acf7cb08 100644 --- a/client/client_impl.go +++ b/client/client_impl.go @@ -31,25 +31,16 @@ const ( pathRepos = "%s/api/user/repos" pathRepo = "%s/api/repos/%s/%s" pathChown = "%s/api/repos/%s/%s/chown" - pathEncrypt = "%s/api/repos/%s/%s/encrypt" pathBuilds = "%s/api/repos/%s/%s/builds" pathBuild = "%s/api/repos/%s/%s/builds/%v" pathApprove = "%s/api/repos/%s/%s/builds/%d/approve" pathDecline = "%s/api/repos/%s/%s/builds/%d/decline" pathJob = "%s/api/repos/%s/%s/builds/%d/%d" pathLog = "%s/api/repos/%s/%s/logs/%d/%d" - pathKey = "%s/api/repos/%s/%s/key" - pathSign = "%s/api/repos/%s/%s/sign" pathRepoSecrets = "%s/api/repos/%s/%s/secrets" pathRepoSecret = "%s/api/repos/%s/%s/secrets/%s" pathRepoRegistries = "%s/api/repos/%s/%s/registry" pathRepoRegistry = "%s/api/repos/%s/%s/registry/%s" - pathTeamSecrets = "%s/api/teams/%s/secrets" - pathTeamSecret = "%s/api/teams/%s/secrets/%s" - pathGlobalSecrets = "%s/api/global/secrets" - pathGlobalSecret = "%s/api/global/secrets/%s" - pathNodes = "%s/api/nodes" - pathNode = "%s/api/nodes/%d" pathUsers = "%s/api/users" pathUser = "%s/api/users/%s" pathBuildQueue = "%s/api/builds" @@ -295,77 +286,6 @@ func (c *client) Deploy(owner, name string, num int, env string, params map[stri return out, err } -// SecretList returns a list of a repository secrets. -func (c *client) SecretList(owner, name string) ([]*model.Secret, error) { - var out []*model.Secret - uri := fmt.Sprintf(pathRepoSecrets, c.base, owner, name) - err := c.get(uri, &out) - return out, err -} - -// SecretPost create or updates a repository secret. -func (c *client) SecretPost(owner, name string, secret *model.Secret) error { - uri := fmt.Sprintf(pathRepoSecrets, c.base, owner, name) - return c.post(uri, secret, nil) -} - -// SecretDel deletes a named repository secret. -func (c *client) SecretDel(owner, name, secret string) error { - uri := fmt.Sprintf(pathRepoSecret, c.base, owner, name, secret) - return c.delete(uri) -} - -// TeamSecretList returns a list of organizational secrets. -func (c *client) TeamSecretList(team string) ([]*model.Secret, error) { - var out []*model.Secret - uri := fmt.Sprintf(pathTeamSecrets, c.base, team) - err := c.get(uri, &out) - return out, err -} - -// TeamSecretPost create or updates a organizational secret. -func (c *client) TeamSecretPost(team string, secret *model.Secret) error { - uri := fmt.Sprintf(pathTeamSecrets, c.base, team) - return c.post(uri, secret, nil) -} - -// TeamSecretDel deletes a named orgainization secret. -func (c *client) TeamSecretDel(team, secret string) error { - uri := fmt.Sprintf(pathTeamSecret, c.base, team, secret) - return c.delete(uri) -} - -// GlobalSecretList returns a list of global secrets. -func (c *client) GlobalSecretList() ([]*model.Secret, error) { - var out []*model.Secret - uri := fmt.Sprintf(pathGlobalSecrets, c.base) - err := c.get(uri, &out) - return out, err -} - -// GlobalSecretPost create or updates a global secret. -func (c *client) GlobalSecretPost(secret *model.Secret) error { - uri := fmt.Sprintf(pathGlobalSecrets, c.base) - return c.post(uri, secret, nil) -} - -// GlobalSecretDel deletes a named global secret. -func (c *client) GlobalSecretDel(secret string) error { - uri := fmt.Sprintf(pathGlobalSecret, c.base, secret) - return c.delete(uri) -} - -// Sign returns a cryptographic signature for the input string. -func (c *client) Sign(owner, name string, in []byte) ([]byte, error) { - uri := fmt.Sprintf(pathSign, c.base, owner, name) - rc, err := stream(c.client, uri, "POST", in, nil) - if err != nil { - return nil, err - } - defer rc.Close() - return ioutil.ReadAll(rc) -} - // Registry returns a registry by hostname. func (c *client) Registry(owner, name, hostname string) (*model.Registry, error) { out := new(model.Registry) @@ -404,6 +324,44 @@ func (c *client) RegistryDelete(owner, name, hostname string) error { return c.delete(uri) } +// Secret returns a secret by name. +func (c *client) Secret(owner, name, secret string) (*model.Secret, error) { + out := new(model.Secret) + uri := fmt.Sprintf(pathRepoSecret, c.base, owner, name, secret) + err := c.get(uri, out) + return out, err +} + +// SecretList returns a list of all repository secrets. +func (c *client) SecretList(owner string, name string) ([]*model.Secret, error) { + var out []*model.Secret + uri := fmt.Sprintf(pathRepoSecrets, c.base, owner, name) + err := c.get(uri, &out) + return out, err +} + +// SecretCreate creates a secret. +func (c *client) SecretCreate(owner, name string, in *model.Secret) (*model.Secret, error) { + out := new(model.Secret) + uri := fmt.Sprintf(pathRepoSecrets, c.base, owner, name) + err := c.post(uri, in, out) + return out, err +} + +// SecretUpdate updates a secret. +func (c *client) SecretUpdate(owner, name string, in *model.Secret) (*model.Secret, error) { + out := new(model.Secret) + uri := fmt.Sprintf(pathRepoSecret, c.base, owner, name, in.Name) + err := c.patch(uri, in, out) + return out, err +} + +// SecretDelete deletes a secret. +func (c *client) SecretDelete(owner, name, secret string) error { + uri := fmt.Sprintf(pathRepoSecret, c.base, owner, name, secret) + return c.delete(uri) +} + // // http request helper functions // diff --git a/drone/global.go b/drone/global.go deleted file mode 100644 index 6bf706df7..000000000 --- a/drone/global.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import "github.com/urfave/cli" - -var globalCmd = cli.Command{ - Name: "global", - Usage: "manage global state", - Subcommands: []cli.Command{ - globalSecretCmd, - }, -} diff --git a/drone/global_secret.go b/drone/global_secret.go deleted file mode 100644 index a3d2e06ba..000000000 --- a/drone/global_secret.go +++ /dev/null @@ -1,13 +0,0 @@ -package main - -import "github.com/urfave/cli" - -var globalSecretCmd = cli.Command{ - Name: "secret", - Usage: "manage secrets", - Subcommands: []cli.Command{ - globalSecretAddCmd, - globalSecretRemoveCmd, - globalSecretListCmd, - }, -} diff --git a/drone/global_secret_add.go b/drone/global_secret_add.go deleted file mode 100644 index 3543fa197..000000000 --- a/drone/global_secret_add.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import "github.com/urfave/cli" - -var globalSecretAddCmd = cli.Command{ - Name: "add", - Usage: "adds a secret", - ArgsUsage: "[key] [value]", - Action: globalSecretAdd, - Flags: secretAddFlags(), -} - -func globalSecretAdd(c *cli.Context) error { - if len(c.Args()) != 2 { - cli.ShowSubcommandHelp(c) - return nil - } - - name := c.Args().First() - value := c.Args().Get(1) - - secret, err := secretParseCmd(name, value, c) - if err != nil { - return err - } - - client, err := newClient(c) - if err != nil { - return err - } - - return client.GlobalSecretPost(secret) -} diff --git a/drone/global_secret_info.go b/drone/global_secret_info.go deleted file mode 100644 index 06ab7d0f9..000000000 --- a/drone/global_secret_info.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/drone/global_secret_list.go b/drone/global_secret_list.go deleted file mode 100644 index 81c049a40..000000000 --- a/drone/global_secret_list.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import "github.com/urfave/cli" - -var globalSecretListCmd = cli.Command{ - Name: "ls", - Usage: "list all secrets", - Action: globalSecretList, - Flags: secretListFlags(), -} - -func globalSecretList(c *cli.Context) error { - client, err := newClient(c) - if err != nil { - return err - } - - secrets, err := client.GlobalSecretList() - - if err != nil || len(secrets) == 0 { - return err - } - - return secretDisplayList(secrets, c) -} diff --git a/drone/global_secret_rm.go b/drone/global_secret_rm.go deleted file mode 100644 index 398cb72e7..000000000 --- a/drone/global_secret_rm.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import "github.com/urfave/cli" - -var globalSecretRemoveCmd = cli.Command{ - Name: "rm", - Usage: "remove a secret", - Action: globalSecretRemove, -} - -func globalSecretRemove(c *cli.Context) error { - if len(c.Args()) != 1 { - cli.ShowSubcommandHelp(c) - return nil - } - - secret := c.Args().First() - - client, err := newClient(c) - if err != nil { - return err - } - - return client.GlobalSecretDel(secret) -} diff --git a/drone/main.go b/drone/main.go index cd8cdfe2d..27c9d74b2 100644 --- a/drone/main.go +++ b/drone/main.go @@ -40,11 +40,8 @@ func main() { registryCmd, secretCmd, serverCmd, - signCmd, repoCmd, userCmd, - orgCmd, - globalCmd, } if err := app.Run(os.Args); err != nil { diff --git a/drone/org.go b/drone/org.go deleted file mode 100644 index b4e78d60e..000000000 --- a/drone/org.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import "github.com/urfave/cli" - -var orgCmd = cli.Command{ - Name: "org", - Usage: "manage organizations", - Subcommands: []cli.Command{ - orgSecretCmd, - }, -} diff --git a/drone/org_secret.go b/drone/org_secret.go deleted file mode 100644 index 542d32e20..000000000 --- a/drone/org_secret.go +++ /dev/null @@ -1,13 +0,0 @@ -package main - -import "github.com/urfave/cli" - -var orgSecretCmd = cli.Command{ - Name: "secret", - Usage: "manage secrets", - Subcommands: []cli.Command{ - orgSecretAddCmd, - orgSecretRemoveCmd, - orgSecretListCmd, - }, -} diff --git a/drone/org_secret_add.go b/drone/org_secret_add.go deleted file mode 100644 index c41973641..000000000 --- a/drone/org_secret_add.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import "github.com/urfave/cli" - -var orgSecretAddCmd = cli.Command{ - Name: "add", - Usage: "adds a secret", - ArgsUsage: "[org] [key] [value]", - Action: orgSecretAdd, - Flags: secretAddFlags(), -} - -func orgSecretAdd(c *cli.Context) error { - if len(c.Args()) != 3 { - cli.ShowSubcommandHelp(c) - return nil - } - - team := c.Args().First() - name := c.Args().Get(1) - value := c.Args().Get(2) - - secret, err := secretParseCmd(name, value, c) - if err != nil { - return err - } - - client, err := newClient(c) - if err != nil { - return err - } - - return client.TeamSecretPost(team, secret) -} diff --git a/drone/org_secret_info.go b/drone/org_secret_info.go deleted file mode 100644 index 06ab7d0f9..000000000 --- a/drone/org_secret_info.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/drone/org_secret_list.go b/drone/org_secret_list.go deleted file mode 100644 index 6f2e95139..000000000 --- a/drone/org_secret_list.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import "github.com/urfave/cli" - -var orgSecretListCmd = cli.Command{ - Name: "ls", - Usage: "list all secrets", - Action: orgSecretList, - Flags: secretListFlags(), -} - -func orgSecretList(c *cli.Context) error { - if len(c.Args()) != 1 { - cli.ShowSubcommandHelp(c) - return nil - } - - team := c.Args().First() - - client, err := newClient(c) - if err != nil { - return err - } - - secrets, err := client.TeamSecretList(team) - - if err != nil || len(secrets) == 0 { - return err - } - - return secretDisplayList(secrets, c) -} diff --git a/drone/org_secret_rm.go b/drone/org_secret_rm.go deleted file mode 100644 index c8748f9bd..000000000 --- a/drone/org_secret_rm.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "log" - - "github.com/urfave/cli" -) - -var orgSecretRemoveCmd = cli.Command{ - Name: "rm", - Usage: "remove a secret", - Action: func(c *cli.Context) { - if err := orgSecretRemove(c); err != nil { - log.Fatalln(err) - } - }, -} - -func orgSecretRemove(c *cli.Context) error { - if len(c.Args()) != 2 { - cli.ShowSubcommandHelp(c) - return nil - } - - team := c.Args().First() - secret := c.Args().Get(1) - - client, err := newClient(c) - if err != nil { - return err - } - - return client.TeamSecretDel(team, secret) -} diff --git a/drone/registry_info.go b/drone/registry_info.go index 47506030b..09880c95e 100644 --- a/drone/registry_info.go +++ b/drone/registry_info.go @@ -23,7 +23,7 @@ var registryInfoCmd = cli.Command{ }, cli.StringFlag{ Name: "format", - Usage: "repository name (e.g. octocat/hello-world)", + Usage: "format output", Value: tmplRegistryList, Hidden: true, }, diff --git a/drone/registry_list.go b/drone/registry_list.go index f136f8610..1a5b468ae 100644 --- a/drone/registry_list.go +++ b/drone/registry_list.go @@ -18,7 +18,7 @@ var registryListCmd = cli.Command{ }, cli.StringFlag{ Name: "format", - Usage: "repository name (e.g. octocat/hello-world)", + Usage: "format output", Value: tmplRegistryList, Hidden: true, }, @@ -58,6 +58,5 @@ func registryList(c *cli.Context) error { // template for build list information var tmplRegistryList = "\x1b[33m{{ .Address }} \x1b[0m" + ` Username: {{ .Username }} -Password: ******** Email: {{ .Email }} ` diff --git a/drone/secret.go b/drone/secret.go index 0cd864ace..648eade57 100644 --- a/drone/secret.go +++ b/drone/secret.go @@ -1,136 +1,15 @@ package main -import ( - "io/ioutil" - "os" - "strings" - "text/template" - - "github.com/drone/drone/model" - "github.com/urfave/cli" -) +import "github.com/urfave/cli" var secretCmd = cli.Command{ Name: "secret", Usage: "manage secrets", Subcommands: []cli.Command{ - secretAddCmd, - secretRemoveCmd, + secretCreateCmd, + secretDeleteCmd, + secretUpdateCmd, + secretInfoCmd, secretListCmd, }, } - -func secretAddFlags() []cli.Flag { - return []cli.Flag{ - cli.StringSliceFlag{ - Name: "event", - Usage: "inject the secret for these event types", - }, - cli.StringSliceFlag{ - Name: "image", - Usage: "inject the secret for these image types", - Value: &cli.StringSlice{}, - }, - cli.StringFlag{ - Name: "input", - Usage: "input secret value from a file", - }, - cli.BoolFlag{ - Name: "skip-verify", - Usage: "skip verification for the secret", - }, - cli.BoolFlag{ - Name: "conceal", - Usage: "conceal secret in build logs", - }, - } -} - -func secretListFlags() []cli.Flag { - return []cli.Flag{ - cli.StringFlag{ - Name: "format", - Usage: "format output", - Value: tmplSecretList, - }, - cli.StringFlag{ - Name: "image", - Usage: "filter by image", - }, - cli.StringFlag{ - Name: "event", - Usage: "filter by event", - }, - } -} - -func secretParseCmd(name string, value string, c *cli.Context) (*model.Secret, error) { - secret := &model.Secret{} - secret.Name = name - secret.Value = value - secret.Images = c.StringSlice("image") - secret.Events = c.StringSlice("event") - secret.SkipVerify = c.Bool("skip-verify") - secret.Conceal = c.Bool("conceal") - if len(secret.Events) == 0 { - secret.Events = []string{ - model.EventPush, - model.EventTag, - model.EventDeploy, - } - } - - // TODO(bradrydzewski) below we use an @ sybmol to denote that the secret - // value should be loaded from a file (inspired by curl). I'd prefer to use - // a --input flag to explicitly specify a filepath instead. - - if strings.HasPrefix(secret.Value, "@") { - path := secret.Value[1:] - out, ferr := ioutil.ReadFile(path) - if ferr != nil { - return nil, ferr - } - secret.Value = string(out) - } - - return secret, nil -} - -func secretDisplayList(secrets []*model.Secret, c *cli.Context) error { - tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(c.String("format") + "\n") - - if err != nil { - return err - } - - for _, secret := range secrets { - if c.String("image") != "" && !stringInSlice(c.String("image"), secret.Images) { - continue - } - - if c.String("event") != "" && !stringInSlice(c.String("event"), secret.Events) { - continue - } - - tmpl.Execute(os.Stdout, secret) - } - - return nil - -} - -// template for secret list items -var tmplSecretList = "\x1b[33m{{ .Name }} \x1b[0m" + ` -Events: {{ list .Events }} -{{- if .Images }} -Images: {{ list .Images }} -{{- else }} -Images: -{{- end }} -` - -var secretFuncMap = template.FuncMap{ - "list": func(s []string) string { - return strings.Join(s, ", ") - }, -} diff --git a/drone/secret_add.go b/drone/secret_add.go index 8ba78e65c..e8d14c91e 100644 --- a/drone/secret_add.go +++ b/drone/secret_add.go @@ -1,37 +1,66 @@ package main -import "github.com/urfave/cli" +import ( + "github.com/drone/drone/model" + "github.com/urfave/cli" +) -var secretAddCmd = cli.Command{ - Name: "add", - Usage: "adds a secret", - ArgsUsage: "[repo] [key] [value]", - Action: secretAdd, - Flags: secretAddFlags(), +var secretCreateCmd = cli.Command{ + Name: "add", + Usage: "adds a secret", + Action: secretCreate, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "repository", + Usage: "repository name (e.g. octocat/hello-world)", + }, + cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + cli.StringFlag{ + Name: "value", + Usage: "secret value", + }, + cli.StringSliceFlag{ + Name: "event", + Usage: "secret limited to these events", + }, + cli.StringSliceFlag{ + Name: "image", + Usage: "secret limited to these images", + }, + }, } -func secretAdd(c *cli.Context) error { - repo := c.Args().First() - owner, name, err := parseRepo(repo) +func secretCreate(c *cli.Context) error { + reponame := c.String("repository") + if reponame == "" { + reponame = c.Args().First() + } + owner, name, err := parseRepo(reponame) if err != nil { return err } - - tail := c.Args().Tail() - if len(tail) != 2 { - cli.ShowSubcommandHelp(c) - return nil - } - - secret, err := secretParseCmd(tail[0], tail[1], c) - if err != nil { - return err - } - client, err := newClient(c) if err != nil { return err } - - return client.SecretPost(owner, name, secret) + secret := &model.Secret{ + Name: c.String("name"), + Value: c.String("value"), + Images: c.StringSlice("image"), + Events: c.StringSlice("events"), + } + if len(secret.Events) == 0 { + secret.Events = defaultSecretEvents + } + _, err = client.SecretUpdate(owner, name, secret) + return err +} + +var defaultSecretEvents = []string{ + model.EventPush, + model.EventTag, + model.EventDeploy, } diff --git a/drone/secret_info.go b/drone/secret_info.go index 06ab7d0f9..74f460d48 100644 --- a/drone/secret_info.go +++ b/drone/secret_info.go @@ -1 +1,58 @@ package main + +import ( + "html/template" + "os" + + "github.com/urfave/cli" +) + +var secretInfoCmd = cli.Command{ + Name: "info", + Usage: "display secret info", + Action: secretInfo, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "repository", + Usage: "repository name (e.g. octocat/hello-world)", + }, + cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + cli.StringFlag{ + Name: "format", + Usage: "format output", + Value: tmplSecretList, + Hidden: true, + }, + }, +} + +func secretInfo(c *cli.Context) error { + var ( + secretName = c.String("name") + repoName = c.String("repository") + format = c.String("format") + "\n" + ) + if repoName == "" { + repoName = c.Args().First() + } + owner, name, err := parseRepo(repoName) + if err != nil { + return err + } + client, err := newClient(c) + if err != nil { + return err + } + secret, err := client.Secret(owner, name, secretName) + if err != nil { + return err + } + tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format) + if err != nil { + return err + } + return tmpl.Execute(os.Stdout, secret) +} diff --git a/drone/secret_list.go b/drone/secret_list.go index 6a94938c4..aafc06ccb 100644 --- a/drone/secret_list.go +++ b/drone/secret_list.go @@ -1,32 +1,73 @@ package main -import "github.com/urfave/cli" +import ( + "html/template" + "os" + "strings" + + "github.com/urfave/cli" +) var secretListCmd = cli.Command{ Name: "ls", - Usage: "list all secrets", + Usage: "list secrets", Action: secretList, - Flags: secretListFlags(), + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "repository", + Usage: "repository name (e.g. octocat/hello-world)", + }, + cli.StringFlag{ + Name: "format", + Usage: "format output", + Value: tmplSecretList, + Hidden: true, + }, + }, } func secretList(c *cli.Context) error { - owner, name, err := parseRepo(c.Args().First()) - + var ( + format = c.String("format") + "\n" + reponame = c.String("repository") + ) + if reponame == "" { + reponame = c.Args().First() + } + owner, name, err := parseRepo(reponame) if err != nil { return err } - client, err := newClient(c) - if err != nil { return err } - - secrets, err := client.SecretList(owner, name) - - if err != nil || len(secrets) == 0 { + list, err := client.SecretList(owner, name) + if err != nil { return err } - - return secretDisplayList(secrets, c) + tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format) + if err != nil { + return err + } + for _, registry := range list { + tmpl.Execute(os.Stdout, registry) + } + return nil +} + +// template for secret list items +var tmplSecretList = "\x1b[33m{{ .Name }} \x1b[0m" + ` +Events: {{ list .Events }} +{{- if .Images }} +Images: {{ list .Images }} +{{- else }} +Images: +{{- end }} +` + +var secretFuncMap = template.FuncMap{ + "list": func(s []string) string { + return strings.Join(s, ", ") + }, } diff --git a/drone/secret_rm.go b/drone/secret_rm.go index 37538af13..9b84d6496 100644 --- a/drone/secret_rm.go +++ b/drone/secret_rm.go @@ -2,24 +2,37 @@ package main import "github.com/urfave/cli" -var secretRemoveCmd = cli.Command{ +var secretDeleteCmd = cli.Command{ Name: "rm", Usage: "remove a secret", - Action: secretRemove, + Action: secretDelete, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "repository", + Usage: "repository name (e.g. octocat/hello-world)", + }, + cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + }, } -func secretRemove(c *cli.Context) error { - repo := c.Args().First() - owner, name, err := parseRepo(repo) +func secretDelete(c *cli.Context) error { + var ( + secret = c.String("name") + reponame = c.String("repository") + ) + if reponame == "" { + reponame = c.Args().First() + } + owner, name, err := parseRepo(reponame) if err != nil { return err } - - secret := c.Args().Get(1) - client, err := newClient(c) if err != nil { return err } - return client.SecretDel(owner, name, secret) + return client.SecretDelete(owner, name, secret) } diff --git a/drone/secret_set.go b/drone/secret_set.go new file mode 100644 index 000000000..36acc31d1 --- /dev/null +++ b/drone/secret_set.go @@ -0,0 +1,57 @@ +package main + +import ( + "github.com/drone/drone/model" + "github.com/urfave/cli" +) + +var secretUpdateCmd = cli.Command{ + Name: "update", + Usage: "update a secret", + Action: secretUpdate, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "repository", + Usage: "repository name (e.g. octocat/hello-world)", + }, + cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + cli.StringFlag{ + Name: "value", + Usage: "secret value", + }, + cli.StringSliceFlag{ + Name: "event", + Usage: "secret limited to these events", + }, + cli.StringSliceFlag{ + Name: "image", + Usage: "secret limited to these images", + }, + }, +} + +func secretUpdate(c *cli.Context) error { + reponame := c.String("repository") + if reponame == "" { + reponame = c.Args().First() + } + owner, name, err := parseRepo(reponame) + if err != nil { + return err + } + client, err := newClient(c) + if err != nil { + return err + } + secret := &model.Secret{ + Name: c.String("name"), + Value: c.String("value"), + Images: c.StringSlice("image"), + Events: c.StringSlice("events"), + } + _, err = client.SecretUpdate(owner, name, secret) + return err +} diff --git a/drone/server.go b/drone/server.go index 39900f808..0d100c068 100644 --- a/drone/server.go +++ b/drone/server.go @@ -22,10 +22,10 @@ var serverCmd = cli.Command{ Name: "debug", Usage: "start the server in debug mode", }, - cli.BoolFlag{ - EnvVar: "DRONE_BROKER_DEBUG", - Name: "broker-debug", - Usage: "start the broker in debug mode", + cli.StringFlag{ + EnvVar: "DRONE_SERVER_HOST", + Name: "server-host", + Usage: "server host", }, cli.StringFlag{ EnvVar: "DRONE_SERVER_ADDR", @@ -58,12 +58,6 @@ var serverCmd = cli.Command{ Name: "open", Usage: "open user registration", }, - cli.StringFlag{ - EnvVar: "DRONE_YAML", - Name: "yaml", - Usage: "build configuraton file name", - Value: ".drone.yml", - }, cli.DurationFlag{ EnvVar: "DRONE_CACHE_TTL", Name: "cache-ttl", @@ -75,6 +69,21 @@ var serverCmd = cli.Command{ Name: "agent-secret", Usage: "agent secret passcode", }, + cli.StringFlag{ + EnvVar: "DRONE_SECRET_ENDPOINT", + Name: "secret-service", + Usage: "secret plugin endpoint", + }, + cli.StringFlag{ + EnvVar: "DRONE_REGISTRY_ENDPOINT", + Name: "registry-service", + Usage: "registry plugin endpoint", + }, + cli.StringFlag{ + EnvVar: "DRONE_GATEKEEPER_ENDPOINT", + Name: "gating-service", + Usage: "gated build endpoint", + }, cli.StringFlag{ EnvVar: "DRONE_DATABASE_DRIVER,DATABASE_DRIVER", Name: "driver", diff --git a/drone/sign.go b/drone/sign.go deleted file mode 100644 index e9fc1f0f3..000000000 --- a/drone/sign.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "io/ioutil" - - "github.com/urfave/cli" -) - -var signCmd = cli.Command{ - Name: "sign", - Usage: "creates a secure yaml file", - Action: sign, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "in", - Usage: "input file", - Value: ".drone.yml", - }, - cli.StringFlag{ - Name: "out", - Usage: "output file signature", - Value: ".drone.yml.sig", - }, - }, -} - -func sign(c *cli.Context) error { - repo := c.Args().First() - owner, name, err := parseRepo(repo) - if err != nil { - return err - } - - in, err := readInput(c.String("in")) - if err != nil { - return err - } - - client, err := newClient(c) - if err != nil { - return err - } - - sig, err := client.Sign(owner, name, in) - if err != nil { - return err - } - - return ioutil.WriteFile(c.String("out"), sig, 0664) -} diff --git a/model/approval.go b/model/approval.go deleted file mode 100644 index 66d295546..000000000 --- a/model/approval.go +++ /dev/null @@ -1,17 +0,0 @@ -package model - -type ( - // ApprovalStore persists approved committers to storage. - ApprovalStore interface { - Approve(*Repo) error - } - - // Approval represents an approved committer. - Approval struct { - Username string `json:"username" meddler:"approval_username"` - Approved bool `json:"approved" meddler:"approval_approved"` - Comments string `json:"comments" meddler:"approval_comments"` - CreatedBy string `json:"created_by" meddler:"approval_created_by"` - CreatedAt int64 `json:"created_at" meddler:"approval_created_at"` - } -) diff --git a/model/gate.go b/model/gate.go deleted file mode 100644 index decc4ad6f..000000000 --- a/model/gate.go +++ /dev/null @@ -1,23 +0,0 @@ -package model - -// Gated indicates a build is gated and requires approval to proceed. It also -// includes the reason the build was gated. -type Gated struct { - Gated bool `json:"gated"` - Reason string `json:"reason"` -} - -// GateService defines a service for gating builds. -type GateService interface { - Gated(*User, *Repo, *Build) (*Gated, error) -} - -type gateService struct{} - -func (s *gateService) Gated(owner *User, repo *Repo, build *Build) (*Gated, error) { - g := new(Gated) - if repo.IsPrivate && build.Event == EventPull && build.Sender != owner.Login { - g.Gated = true - } - return g, nil -} diff --git a/model/registry.go b/model/registry.go index 8d26499ee..5f074800d 100644 --- a/model/registry.go +++ b/model/registry.go @@ -8,6 +8,15 @@ var ( errRegistryPasswordInvalid = errors.New("Invalid Registry Password") ) +// RegistryService defines a service for managing registries. +type RegistryService interface { + RegistryFind(*Repo, string) (*Registry, error) + RegistryList(*Repo) ([]*Registry, error) + RegistryCreate(*Repo, *Registry) error + RegistryUpdate(*Repo, *Registry) error + RegistryDelete(*Repo, string) error +} + // RegistryStore persists registry information to storage. type RegistryStore interface { RegistryFind(*Repo, string) (*Registry, error) diff --git a/model/repo.go b/model/repo.go index 1b20eb705..9d4e1fad4 100644 --- a/model/repo.go +++ b/model/repo.go @@ -25,6 +25,7 @@ type Repo struct { IsPrivate bool `json:"private,omitempty" meddler:"repo_private"` IsTrusted bool `json:"trusted" meddler:"repo_trusted"` IsStarred bool `json:"starred,omitempty" meddler:"-"` + IsGated bool `json:"gated" meddler:"repo_gated"` 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"` diff --git a/model/repo_secret.go b/model/repo_secret.go deleted file mode 100644 index 0763e15f7..000000000 --- a/model/repo_secret.go +++ /dev/null @@ -1,58 +0,0 @@ -package model - -type RepoSecret struct { - // the id for this secret. - ID int64 `json:"id" meddler:"secret_id,pk"` - - // the foreign key for this secret. - RepoID int64 `json:"-" meddler:"secret_repo_id"` - - // the name of the secret which will be used as the environment variable - // name at runtime. - Name string `json:"name" meddler:"secret_name"` - - // the value of the secret which will be provided to the runtime environment - // as a named environment variable. - Value string `json:"value" meddler:"secret_value"` - - // the secret is restricted to this list of images. - Images []string `json:"image,omitempty" meddler:"secret_images,json"` - - // the secret is restricted to this list of events. - Events []string `json:"event,omitempty" meddler:"secret_events,json"` - - // whether the secret requires verification - SkipVerify bool `json:"skip_verify" meddler:"secret_skip_verify"` - - // whether the secret should be concealed in the build log - Conceal bool `json:"conceal" meddler:"secret_conceal"` -} - -// Secret transforms a repo secret into a simple secret. -func (s *RepoSecret) Secret() *Secret { - return &Secret{ - Name: s.Name, - Value: s.Value, - Images: s.Images, - Events: s.Events, - SkipVerify: s.SkipVerify, - Conceal: s.Conceal, - } -} - -// Clone provides a repo secrets clone without the value. -func (s *RepoSecret) Clone() *RepoSecret { - return &RepoSecret{ - ID: s.ID, - Name: s.Name, - Images: s.Images, - Events: s.Events, - SkipVerify: s.SkipVerify, - Conceal: s.Conceal, - } -} - -// Validate validates the required fields and formats. -func (s *RepoSecret) Validate() error { - return nil -} diff --git a/model/secret.go b/model/secret.go index f93624d6d..1cc66d9e8 100644 --- a/model/secret.go +++ b/model/secret.go @@ -1,50 +1,48 @@ package model import ( + "errors" "path/filepath" ) +var ( + errSecretNameInvalid = errors.New("Invalid Secret Name") + errSecretValueInvalid = errors.New("Invalid Secret Value") +) + +// SecretService defines a service for managing secrets. +type SecretService interface { + SecretFind(*Repo, string) (*Secret, error) + SecretList(*Repo) ([]*Secret, error) + SecretCreate(*Repo, *Secret) error + SecretUpdate(*Repo, *Secret) error + SecretDelete(*Repo, string) error +} + +// SecretStore persists secret information to storage. +type SecretStore interface { + SecretFind(*Repo, string) (*Secret, error) + SecretList(*Repo) ([]*Secret, error) + SecretCreate(*Secret) error + SecretUpdate(*Secret) error + SecretDelete(*Secret) error +} + +// Secret represents a secret variable, such as a password or token. +// swagger:model registry type Secret struct { - // the name of the secret which will be used as the environment variable - // name at runtime. - Name string `json:"name"` - - // the value of the secret which will be provided to the runtime environment - // as a named environment variable. - Value string `json:"value"` - - // the secret is restricted to this list of images. - Images []string `json:"image,omitempty"` - - // the secret is restricted to this list of events. - Events []string `json:"event,omitempty"` - - // whether the secret requires verification - SkipVerify bool `json:"skip_verify"` - - // whether the secret should be concealed in the build log - Conceal bool `json:"conceal"` + ID int64 `json:"id" meddler:"secret_id,pk"` + RepoID int64 `json:"-" meddler:"secret_repo_id"` + Name string `json:"name" meddler:"secret_name"` + Value string `json:"value,omitempty" meddler:"secret_value"` + Images []string `json:"image" meddler:"secret_images,json"` + Events []string `json:"event" meddler:"secret_events,json"` + SkipVerify bool `json:"-" meddler:"secret_skip_verify"` + Conceal bool `json:"-" meddler:"secret_conceal"` } // Match returns true if an image and event match the restricted list. -func (s *Secret) Match(image, event string) bool { - return s.MatchImage(image) && s.MatchEvent(event) -} - -// MatchImage returns true if an image matches the restricted list. -func (s *Secret) MatchImage(image string) bool { - for _, pattern := range s.Images { - if match, _ := filepath.Match(pattern, image); match { - return true - } else if pattern == "*" { - return true - } - } - return false -} - -// MatchEvent returns true if an event matches the restricted list. -func (s *Secret) MatchEvent(event string) bool { +func (s *Secret) Match(event string) bool { for _, pattern := range s.Events { if match, _ := filepath.Match(pattern, event); match { return true @@ -55,5 +53,23 @@ func (s *Secret) MatchEvent(event string) bool { // Validate validates the required fields and formats. func (s *Secret) Validate() error { - return nil + switch { + case len(s.Name) == 0: + return errSecretNameInvalid + case len(s.Value) == 0: + return errSecretValueInvalid + default: + return nil + } +} + +// Copy makes a copy of the secret without the value. +func (s *Secret) Copy() *Secret { + return &Secret{ + ID: s.ID, + RepoID: s.RepoID, + Name: s.Name, + Images: s.Images, + Events: s.Events, + } } diff --git a/model/secret_test.go b/model/secret_test.go index 30d63434b..b2f39149b 100644 --- a/model/secret_test.go +++ b/model/secret_test.go @@ -11,52 +11,15 @@ func TestSecret(t *testing.T) { g := goblin.Goblin(t) g.Describe("Secret", func() { - g.It("should match image", func() { - secret := Secret{} - secret.Images = []string{"golang"} - g.Assert(secret.MatchImage("golang")).IsTrue() - }) g.It("should match event", func() { secret := Secret{} secret.Events = []string{"pull_request"} - g.Assert(secret.MatchEvent("pull_request")).IsTrue() - }) - g.It("should match image patterns", func() { - secret := Secret{} - secret.Images = []string{"golang:*"} - g.Assert(secret.MatchImage("golang:1.4.2")).IsTrue() - }) - g.It("should match any image", func() { - secret := Secret{} - secret.Images = []string{"*"} - g.Assert(secret.MatchImage("custom/golang")).IsTrue() - }) - g.It("should match any event", func() { - secret := Secret{} - secret.Events = []string{"*"} - g.Assert(secret.MatchEvent("pull_request")).IsTrue() - }) - g.It("should not match image", func() { - secret := Secret{} - secret.Images = []string{"golang"} - g.Assert(secret.MatchImage("node")).IsFalse() - }) - g.It("should not match image substring", func() { - secret := Secret{} - secret.Images = []string{"golang"} - - // image is only authorized for golang, not golang:1.4.2 - g.Assert(secret.MatchImage("golang:1.4.2")).IsFalse() - }) - g.It("should not match empty image", func() { - secret := Secret{} - secret.Images = []string{} - g.Assert(secret.MatchImage("node")).IsFalse() + g.Assert(secret.Match("pull_request")).IsTrue() }) g.It("should not match event", func() { secret := Secret{} secret.Events = []string{"pull_request"} - g.Assert(secret.MatchEvent("push")).IsFalse() + g.Assert(secret.Match("push")).IsFalse() }) g.It("should pass validation") g.Describe("should fail validation", func() { diff --git a/model/sender.go b/model/sender.go new file mode 100644 index 000000000..0f8fab0b8 --- /dev/null +++ b/model/sender.go @@ -0,0 +1,28 @@ +package model + +type SenderService interface { + SenderAllowed(*User, *Repo, *Build) (bool, error) + SenderCreate(*Repo, *Sender) error + SenderUpdate(*Repo, *Sender) error + SenderDelete(*Repo, string) error + SenderList(*Repo) ([]*Sender, error) +} + +type SenderStore interface { + SenderFind(*Repo, string) (*Sender, error) + SenderList(*Repo) ([]*Sender, error) + SenderCreate(*Sender) error + SenderUpdate(*Sender) error + SenderDelete(*Sender) error +} + +type Sender struct { + ID int64 `json:"-" meddler:"sender_id,pk"` + RepoID int64 `json:"-" meddler:"sender_repo_id"` + Login string `json:"login" meddler:"sender_login"` + Allow bool `json:"allow" meddler:"sender_allow"` + Block bool `json:"block" meddler:"sender_block"` + Branch []string `json:"branch" meddler:"-"` + Deploy []string `json:"deploy" meddler:"-"` + Event []string `json:"event" meddler:"-"` +} diff --git a/model/sys.go b/model/sys.go deleted file mode 100644 index d66cc6ed5..000000000 --- a/model/sys.go +++ /dev/null @@ -1,9 +0,0 @@ -package model - -type System struct { - Version string `json:"version"` - Link string `json:"link_url"` - Plugins []string `json:"plugins"` - Globals []string `json:"globals"` - Escalates []string `json:"privileged_plugins"` -} diff --git a/model/team_secret.go b/model/team_secret.go deleted file mode 100644 index 60f6f9f88..000000000 --- a/model/team_secret.go +++ /dev/null @@ -1,58 +0,0 @@ -package model - -type TeamSecret struct { - // the id for this secret. - ID int64 `json:"id" meddler:"team_secret_id,pk"` - - // the foreign key for this secret. - Key string `json:"-" meddler:"team_secret_key"` - - // the name of the secret which will be used as the environment variable - // name at runtime. - Name string `json:"name" meddler:"team_secret_name"` - - // the value of the secret which will be provided to the runtime environment - // as a named environment variable. - Value string `json:"value" meddler:"team_secret_value"` - - // the secret is restricted to this list of images. - Images []string `json:"image,omitempty" meddler:"team_secret_images,json"` - - // the secret is restricted to this list of events. - Events []string `json:"event,omitempty" meddler:"team_secret_events,json"` - - // whether the secret requires verification - SkipVerify bool `json:"skip_verify" meddler:"team_secret_skip_verify"` - - // whether the secret should be concealed in the build log - Conceal bool `json:"conceal" meddler:"team_secret_conceal"` -} - -// Secret transforms a repo secret into a simple secret. -func (s *TeamSecret) Secret() *Secret { - return &Secret{ - Name: s.Name, - Value: s.Value, - Images: s.Images, - Events: s.Events, - SkipVerify: s.SkipVerify, - Conceal: s.Conceal, - } -} - -// Clone provides a repo secrets clone without the value. -func (s *TeamSecret) Clone() *TeamSecret { - return &TeamSecret{ - ID: s.ID, - Name: s.Name, - Images: s.Images, - Events: s.Events, - SkipVerify: s.SkipVerify, - Conceal: s.Conceal, - } -} - -// Validate validates the required fields and formats. -func (s *TeamSecret) Validate() error { - return nil -} diff --git a/plugins/internal/http.go b/plugins/internal/http.go new file mode 100644 index 000000000..2ae958d14 --- /dev/null +++ b/plugins/internal/http.go @@ -0,0 +1,61 @@ +package internal + +import ( + "bytes" + "encoding/json" + "errors" + "io" + "io/ioutil" + "net/http" + "net/url" +) + +// Send makes an http request to the given endpoint, writing the input +// to the request body and unmarshaling the output from the response body. +func Send(method, path string, in, out interface{}) error { + uri, err := url.Parse(path) + if err != nil { + return err + } + + // if we are posting or putting data, we need to + // write it to the body of the request. + var buf io.ReadWriter + if in != nil { + buf = new(bytes.Buffer) + jsonerr := json.NewEncoder(buf).Encode(in) + if jsonerr != nil { + return jsonerr + } + } + + // creates a new http request to bitbucket. + req, err := http.NewRequest(method, uri.String(), buf) + if err != nil { + return err + } + if in != nil { + req.Header.Set("Content-Type", "application/json") + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + // if an error is encountered, parse and return the + // error response. + if resp.StatusCode > http.StatusPartialContent { + out, _ := ioutil.ReadAll(resp.Body) + return errors.New(string(out)) + } + + // if a json response is expected, parse and return + // the json response. + if out != nil { + return json.NewDecoder(resp.Body).Decode(out) + } + + return nil +} diff --git a/plugins/registry.go b/plugins/registry.go new file mode 100644 index 000000000..e577592e8 --- /dev/null +++ b/plugins/registry.go @@ -0,0 +1,45 @@ +package plugins + +import ( + "fmt" + + "github.com/drone/drone/model" +) + +type registryPlugin struct { + endpoint string +} + +// NewRegistry returns a new registry plugin. +func NewRegistry(endpoint string) interface{} { + return registryPlugin{endpoint} +} + +func (r *registryPlugin) RegistryFind(repo *model.Repo, name string) (*model.Registry, error) { + path := fmt.Sprintf("%s/registry/%s/%s/%s", r.endpoint, repo.Owner, repo.Name, name) + out := new(model.Registry) + err := do("GET", path, nil, out) + return out, err +} + +func (r *registryPlugin) RegistryList(repo *model.Repo) ([]*model.Registry, error) { + path := fmt.Sprintf("%s/registry/%s/%s", r.endpoint, repo.Owner, repo.Name) + out := []*model.Registry{} + err := do("GET", path, nil, out) + return out, err +} + +func (r *registryPlugin) RegistryCreate(repo *model.Repo, in *model.Registry) error { + path := fmt.Sprintf("%s/registry/%s/%s", r.endpoint, repo.Owner, repo.Name) + return do("PATCH", path, in, nil) +} + +func (r *registryPlugin) RegistryUpdate(repo *model.Repo, in *model.Registry) error { + path := fmt.Sprintf("%s/registry/%s/%s/%s", r.endpoint, repo.Owner, repo.Name, in.Address) + return do("PATCH", path, in, nil) +} + +func (r *registryPlugin) RegistryDelete(repo *model.Repo, name string) error { + path := fmt.Sprintf("%s/registry/%s/%s/%s", r.endpoint, repo.Owner, repo.Name, name) + return do("DELETE", path, nil, nil) +} diff --git a/plugins/registry/builtin.go b/plugins/registry/builtin.go new file mode 100644 index 000000000..872a4d7a6 --- /dev/null +++ b/plugins/registry/builtin.go @@ -0,0 +1,38 @@ +package registry + +import ( + "github.com/drone/drone/model" +) + +type builtin struct { + store model.RegistryStore +} + +// New returns a new local registry service. +func New(store model.RegistryStore) model.RegistryService { + return &builtin{store} +} + +func (b *builtin) RegistryFind(repo *model.Repo, name string) (*model.Registry, error) { + return b.store.RegistryFind(repo, name) +} + +func (b *builtin) RegistryList(repo *model.Repo) ([]*model.Registry, error) { + return b.store.RegistryList(repo) +} + +func (b *builtin) RegistryCreate(repo *model.Repo, in *model.Registry) error { + return b.store.RegistryCreate(in) +} + +func (b *builtin) RegistryUpdate(repo *model.Repo, in *model.Registry) error { + return b.store.RegistryUpdate(in) +} + +func (b *builtin) RegistryDelete(repo *model.Repo, addr string) error { + registry, err := b.RegistryFind(repo, addr) + if err != nil { + return err + } + return b.store.RegistryDelete(registry) +} diff --git a/plugins/registry/builtin_test.go b/plugins/registry/builtin_test.go new file mode 100644 index 000000000..b2a276fb4 --- /dev/null +++ b/plugins/registry/builtin_test.go @@ -0,0 +1 @@ +package registry diff --git a/plugins/registry/plugin.go b/plugins/registry/plugin.go new file mode 100644 index 000000000..a0dbf3693 --- /dev/null +++ b/plugins/registry/plugin.go @@ -0,0 +1,46 @@ +package registry + +import ( + "fmt" + + "github.com/drone/drone/model" + "github.com/drone/drone/plugins/internal" +) + +type plugin struct { + endpoint string +} + +// NewRemote returns a new remote registry service. +func NewRemote(endpoint string) model.RegistryService { + return &plugin{endpoint} +} + +func (p *plugin) RegistryFind(repo *model.Repo, name string) (*model.Registry, error) { + path := fmt.Sprintf("%s/registry/%s/%s/%s", p.endpoint, repo.Owner, repo.Name, name) + out := new(model.Registry) + err := internal.Send("GET", path, nil, out) + return out, err +} + +func (p *plugin) RegistryList(repo *model.Repo) ([]*model.Registry, error) { + path := fmt.Sprintf("%s/registry/%s/%s", p.endpoint, repo.Owner, repo.Name) + out := []*model.Registry{} + err := internal.Send("GET", path, nil, out) + return out, err +} + +func (p *plugin) RegistryCreate(repo *model.Repo, in *model.Registry) error { + path := fmt.Sprintf("%s/registry/%s/%s", p.endpoint, repo.Owner, repo.Name) + return internal.Send("PATCH", path, in, nil) +} + +func (p *plugin) RegistryUpdate(repo *model.Repo, in *model.Registry) error { + path := fmt.Sprintf("%s/registry/%s/%s/%s", p.endpoint, repo.Owner, repo.Name, in.Address) + return internal.Send("PATCH", path, in, nil) +} + +func (p *plugin) RegistryDelete(repo *model.Repo, name string) error { + path := fmt.Sprintf("%s/registry/%s/%s/%s", p.endpoint, repo.Owner, repo.Name, name) + return internal.Send("DELETE", path, nil, nil) +} diff --git a/plugins/registry/plugin_test.go b/plugins/registry/plugin_test.go new file mode 100644 index 000000000..b2a276fb4 --- /dev/null +++ b/plugins/registry/plugin_test.go @@ -0,0 +1 @@ +package registry diff --git a/plugins/secrets.go b/plugins/secrets.go new file mode 100644 index 000000000..a329f2a7a --- /dev/null +++ b/plugins/secrets.go @@ -0,0 +1,45 @@ +package plugins + +import ( + "fmt" + + "github.com/drone/drone/model" +) + +type secretPlugin struct { + endpoint string +} + +// NewSecret returns a new secret plugin. +func NewSecret(endpoint string) interface{} { + return secretPlugin{endpoint} +} + +func (s *secretPlugin) SecretFind(repo *model.Repo, name string) (*model.Secret, error) { + path := fmt.Sprintf("%s/secrets/%s/%s/%s", s.endpoint, repo.Owner, repo.Name, name) + out := new(model.Secret) + err := do("GET", path, nil, out) + return out, err +} + +func (s *secretPlugin) SecretList(repo *model.Repo) ([]*model.Secret, error) { + path := fmt.Sprintf("%s/secrets/%s/%s", s.endpoint, repo.Owner, repo.Name) + out := []*model.Secret{} + err := do("GET", path, nil, out) + return out, err +} + +func (s *secretPlugin) SecretCreate(repo *model.Repo, in *model.Secret) error { + path := fmt.Sprintf("%s/secrets/%s/%s", s.endpoint, repo.Owner, repo.Name) + return do("POST", path, in, nil) +} + +func (s *secretPlugin) SecretUpdate(repo *model.Repo, in *model.Secret) error { + path := fmt.Sprintf("%s/secrets/%s/%s/%s", s.endpoint, repo.Owner, repo.Name, in.Name) + return do("PATCH", path, in, nil) +} + +func (s *secretPlugin) SecretDelete(repo *model.Repo, name string) error { + path := fmt.Sprintf("%s/secrets/%s/%s/%s", s.endpoint, repo.Owner, repo.Name, name) + return do("DELETE", path, nil, nil) +} diff --git a/plugins/secrets/builtin.go b/plugins/secrets/builtin.go new file mode 100644 index 000000000..05a7b00c9 --- /dev/null +++ b/plugins/secrets/builtin.go @@ -0,0 +1,38 @@ +package secrets + +import ( + "github.com/drone/drone/model" +) + +type builtin struct { + store model.SecretStore +} + +// New returns a new local secret service. +func New(store model.SecretStore) model.SecretService { + return &builtin{store} +} + +func (b *builtin) SecretFind(repo *model.Repo, name string) (*model.Secret, error) { + return b.store.SecretFind(repo, name) +} + +func (b *builtin) SecretList(repo *model.Repo) ([]*model.Secret, error) { + return b.store.SecretList(repo) +} + +func (b *builtin) SecretCreate(repo *model.Repo, in *model.Secret) error { + return b.store.SecretCreate(in) +} + +func (b *builtin) SecretUpdate(repo *model.Repo, in *model.Secret) error { + return b.store.SecretUpdate(in) +} + +func (b *builtin) SecretDelete(repo *model.Repo, name string) error { + secret, err := b.store.SecretFind(repo, name) + if err != nil { + return err + } + return b.store.SecretDelete(secret) +} diff --git a/plugins/secrets/builtin_test.go b/plugins/secrets/builtin_test.go new file mode 100644 index 000000000..b2b97d555 --- /dev/null +++ b/plugins/secrets/builtin_test.go @@ -0,0 +1 @@ +package secrets diff --git a/plugins/secrets/plugin.go b/plugins/secrets/plugin.go new file mode 100644 index 000000000..34e0d4e8d --- /dev/null +++ b/plugins/secrets/plugin.go @@ -0,0 +1,46 @@ +package secrets + +import ( + "fmt" + + "github.com/drone/drone/model" + "github.com/drone/drone/plugins/internal" +) + +type plugin struct { + endpoint string +} + +// NewRemote returns a new remote secret service. +func NewRemote(endpoint string) model.SecretService { + return &plugin{endpoint} +} + +func (p *plugin) SecretFind(repo *model.Repo, name string) (*model.Secret, error) { + path := fmt.Sprintf("%s/secrets/%s/%s/%s", p.endpoint, repo.Owner, repo.Name, name) + out := new(model.Secret) + err := internal.Send("GET", path, nil, out) + return out, err +} + +func (p *plugin) SecretList(repo *model.Repo) ([]*model.Secret, error) { + path := fmt.Sprintf("%s/secrets/%s/%s", p.endpoint, repo.Owner, repo.Name) + out := []*model.Secret{} + err := internal.Send("GET", path, nil, out) + return out, err +} + +func (p *plugin) SecretCreate(repo *model.Repo, in *model.Secret) error { + path := fmt.Sprintf("%s/secrets/%s/%s", p.endpoint, repo.Owner, repo.Name) + return internal.Send("POST", path, in, nil) +} + +func (p *plugin) SecretUpdate(repo *model.Repo, in *model.Secret) error { + path := fmt.Sprintf("%s/secrets/%s/%s/%s", p.endpoint, repo.Owner, repo.Name, in.Name) + return internal.Send("PATCH", path, in, nil) +} + +func (p *plugin) SecretDelete(repo *model.Repo, name string) error { + path := fmt.Sprintf("%s/secrets/%s/%s/%s", p.endpoint, repo.Owner, repo.Name, name) + return internal.Send("DELETE", path, nil, nil) +} diff --git a/plugins/secrets/plugin_test.go b/plugins/secrets/plugin_test.go new file mode 100644 index 000000000..b2b97d555 --- /dev/null +++ b/plugins/secrets/plugin_test.go @@ -0,0 +1 @@ +package secrets diff --git a/plugins/sender/builtin.go b/plugins/sender/builtin.go new file mode 100644 index 000000000..3cc1f1650 --- /dev/null +++ b/plugins/sender/builtin.go @@ -0,0 +1,44 @@ +package sender + +import ( + "github.com/drone/drone/model" +) + +type builtin struct { + store model.SenderStore +} + +// New returns a new local gating service. +func New(store model.SenderStore) model.SenderService { + return &builtin{store} +} + +func (b *builtin) SenderAllowed(user *model.User, repo *model.Repo, build *model.Build) (bool, error) { + if repo.IsPrivate == false && build.Event == model.EventPull && build.Sender != user.Login { + sender, err := b.store.SenderFind(repo, build.Sender) + if err != nil || sender.Block { + return false, nil + } + } + return true, nil +} + +func (b *builtin) SenderCreate(repo *model.Repo, sender *model.Sender) error { + return b.store.SenderCreate(sender) +} + +func (b *builtin) SenderUpdate(repo *model.Repo, sender *model.Sender) error { + return b.store.SenderUpdate(sender) +} + +func (b *builtin) SenderDelete(repo *model.Repo, login string) error { + sender, err := b.store.SenderFind(repo, login) + if err != nil { + return err + } + return b.store.SenderDelete(sender) +} + +func (b *builtin) SenderList(repo *model.Repo) ([]*model.Sender, error) { + return b.store.SenderList(repo) +} diff --git a/plugins/sender/builtin_test.go b/plugins/sender/builtin_test.go new file mode 100644 index 000000000..0e314aed0 --- /dev/null +++ b/plugins/sender/builtin_test.go @@ -0,0 +1 @@ +package sender diff --git a/plugins/sender/plugin.go b/plugins/sender/plugin.go new file mode 100644 index 000000000..446ea9bc3 --- /dev/null +++ b/plugins/sender/plugin.go @@ -0,0 +1,49 @@ +package sender + +import ( + "fmt" + + "github.com/drone/drone/model" + "github.com/drone/drone/plugins/internal" +) + +type plugin struct { + endpoint string +} + +// NewRemote returns a new remote gating service. +func NewRemote(endpoint string) model.SenderService { + return &plugin{endpoint} +} + +func (p *plugin) SenderAllowed(user *model.User, repo *model.Repo, build *model.Build) (bool, error) { + path := fmt.Sprintf("%s/sender/%s/%s/%s", p.endpoint, repo.Owner, repo.Name, build.Sender) + out := new(model.Sender) + err := internal.Send("POST", path, build, out) + if err != nil { + return false, err + } + return out.Allow, nil +} + +func (p *plugin) SenderCreate(repo *model.Repo, sender *model.Sender) error { + path := fmt.Sprintf("%s/sender/%s/%s", p.endpoint, repo.Owner, repo.Name) + return internal.Send("POST", path, sender, nil) +} + +func (p *plugin) SenderUpdate(repo *model.Repo, sender *model.Sender) error { + path := fmt.Sprintf("%s/sender/%s/%s", p.endpoint, repo.Owner, repo.Name) + return internal.Send("PUT", path, sender, nil) +} + +func (p *plugin) SenderDelete(repo *model.Repo, login string) error { + path := fmt.Sprintf("%s/sender/%s/%s/%s", p.endpoint, repo.Owner, repo.Name, login) + return internal.Send("DELETE", path, nil, nil) +} + +func (p *plugin) SenderList(repo *model.Repo) ([]*model.Sender, error) { + path := fmt.Sprintf("%s/sender/%s/%s", p.endpoint, repo.Owner, repo.Name) + out := []*model.Sender{} + err := internal.Send("GET", path, nil, out) + return out, err +} diff --git a/plugins/sender/plugin_test.go b/plugins/sender/plugin_test.go new file mode 100644 index 000000000..0e314aed0 --- /dev/null +++ b/plugins/sender/plugin_test.go @@ -0,0 +1 @@ +package sender diff --git a/router/middleware/config.go b/router/middleware/config.go index e38e79d7a..77a0217f5 100644 --- a/router/middleware/config.go +++ b/router/middleware/config.go @@ -23,13 +23,13 @@ func setupConfig(c *cli.Context) *model.Config { return &model.Config{ Open: c.Bool("open"), Secret: c.String("agent-secret"), - Admins: sliceToMap(c.StringSlice("admin")), - Orgs: sliceToMap(c.StringSlice("orgs")), + Admins: sliceToMap2(c.StringSlice("admin")), + Orgs: sliceToMap2(c.StringSlice("orgs")), } } // helper function to convert a string slice to a map. -func sliceToMap(s []string) map[string]bool { +func sliceToMap2(s []string) map[string]bool { v := map[string]bool{} for _, ss := range s { v[ss] = true diff --git a/router/middleware/store.go b/router/middleware/store.go index c7d706d56..90f910674 100644 --- a/router/middleware/store.go +++ b/router/middleware/store.go @@ -1,6 +1,15 @@ package middleware import ( + "context" + + "github.com/cncd/logging" + "github.com/cncd/pubsub" + "github.com/cncd/queue" + "github.com/drone/drone/plugins/registry" + "github.com/drone/drone/plugins/secrets" + "github.com/drone/drone/plugins/sender" + "github.com/drone/drone/server" "github.com/drone/drone/store" "github.com/drone/drone/store/datastore" "github.com/urfave/cli" @@ -12,6 +21,40 @@ import ( // the context of every http.Request. func Store(cli *cli.Context) gin.HandlerFunc { v := setupStore(cli) + + // HACK during refactor period. Please ignore my mess. + + // storage + server.Config.Storage.Files = v + + // services + server.Config.Services.Queue = queue.New() + server.Config.Services.Logs = logging.New() + server.Config.Services.Pubsub = pubsub.New() + server.Config.Services.Pubsub.Create(context.Background(), "topic/events") + server.Config.Services.Registries = registry.New(v) + server.Config.Services.Secrets = secrets.New(v) + server.Config.Services.Senders = sender.New(v) + if endpoint := cli.String("registry-service"); endpoint != "" { + server.Config.Services.Registries = registry.NewRemote(endpoint) + } + if endpoint := cli.String("secret-service"); endpoint != "" { + server.Config.Services.Secrets = secrets.NewRemote(endpoint) + } + if endpoint := cli.String("gating-service"); endpoint != "" { + server.Config.Services.Senders = sender.NewRemote(endpoint) + } + + // server configuration + server.Config.Server.Cert = cli.String("server-cert") + server.Config.Server.Key = cli.String("server-key") + server.Config.Server.Pass = cli.String("agent-secret") + server.Config.Server.Host = cli.String("server-host") + server.Config.Server.Port = cli.String("server-addr") + // server.Config.Server.Open = cli.Bool("open") + // server.Config.Server.Orgs = sliceToMap(cli.StringSlice("orgs")) + // server.Config.Server.Admins = sliceToMap(cli.StringSlice("admin")) + return func(c *gin.Context) { store.ToContext(c, v) c.Next() @@ -25,3 +68,12 @@ func setupStore(c *cli.Context) store.Store { c.String("datasource"), ) } + +// helper function to convert a string slice to a map. +func sliceToMap(s []string) map[string]struct{} { + v := map[string]struct{}{} + for _, ss := range s { + v[ss] = struct{}{} + } + return v +} diff --git a/router/router.go b/router/router.go index ff96a223e..4c01926e0 100644 --- a/router/router.go +++ b/router/router.go @@ -70,27 +70,6 @@ func Load(middleware ...gin.HandlerFunc) http.Handler { users.DELETE("/:login", server.DeleteUser) } - teams := e.Group("/api/teams") - { - teams.Use(session.MustTeamAdmin()) - - team := teams.Group("/:team") - { - team.GET("/secrets", server.GetTeamSecrets) - team.POST("/secrets", server.PostTeamSecret) - team.DELETE("/secrets/:secret", server.DeleteTeamSecret) - } - } - - global := e.Group("/api/global") - { - global.Use(session.MustAdmin()) - - global.GET("/secrets", server.GetGlobalSecrets) - global.POST("/secrets", server.PostGlobalSecret) - global.DELETE("/secrets/:secret", server.DeleteGlobalSecret) - } - repos := e.Group("/api/repos/:owner/:name") { repos.POST("", server.PostRepo) @@ -107,8 +86,11 @@ func Load(middleware ...gin.HandlerFunc) http.Handler { repo.GET("/logs/:number/:ppid/:proc", server.GetBuildLogs) repo.POST("/sign", session.MustPush, server.Sign) - repo.GET("/secrets", session.MustPush, server.GetSecrets) + // requires push permissions + repo.GET("/secrets", session.MustPush, server.GetSecretList) repo.POST("/secrets", session.MustPush, server.PostSecret) + repo.GET("/secrets/:secret", session.MustPush, server.GetSecret) + repo.PATCH("/secrets/:secret", session.MustPush, server.PatchSecret) repo.DELETE("/secrets/:secret", session.MustPush, server.DeleteSecret) // requires push permissions diff --git a/server/build.go b/server/build.go index 13aa98645..7aaf95d58 100644 --- a/server/build.go +++ b/server/build.go @@ -138,7 +138,7 @@ func DeleteBuild(c *gin.Context) { // TODO cancel child procs store.FromContext(c).ProcUpdate(proc) - config.queue.Error(context.Background(), fmt.Sprint(proc.ID), queue.ErrCancel) + Config.Services.Queue.Error(context.Background(), fmt.Sprint(proc.ID), queue.ErrCancel) c.String(204, "") } @@ -197,11 +197,11 @@ func PostApproval(c *gin.Context) { // get the previous build so that we can send // on status change notifications last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID) - secs, err := store.GetMergedSecretList(c, repo) + secs, err := Config.Services.Secrets.SecretList(repo) if err != nil { logrus.Debugf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err) } - regs, err := store.FromContext(c).RegistryList(repo) + regs, err := Config.Services.Registries.RegistryList(repo) if err != nil { logrus.Debugf("Error getting registry credentials for %s#%d. %s", repo.FullName, build.Number, err) } @@ -277,7 +277,7 @@ func PostApproval(c *gin.Context) { Build: buildCopy, }) // TODO remove global reference - config.pubsub.Publish(c, "topic/events", message) + Config.Services.Pubsub.Publish(c, "topic/events", message) // // end publish topic @@ -298,8 +298,8 @@ func PostApproval(c *gin.Context) { Timeout: b.Repo.Timeout, }) - config.logger.Open(context.Background(), task.ID) - config.queue.Push(context.Background(), task) + Config.Services.Logs.Open(context.Background(), task.ID) + Config.Services.Queue.Push(context.Background(), task) } } @@ -476,11 +476,11 @@ func PostBuild(c *gin.Context) { // get the previous build so that we can send // on status change notifications last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID) - secs, err := store.GetMergedSecretList(c, repo) + secs, err := Config.Services.Secrets.SecretList(repo) if err != nil { logrus.Debugf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err) } - regs, err := store.FromContext(c).RegistryList(repo) + regs, err := Config.Services.Registries.RegistryList(repo) if err != nil { logrus.Debugf("Error getting registry credentials for %s#%d. %s", repo.FullName, build.Number, err) } @@ -561,7 +561,7 @@ func PostBuild(c *gin.Context) { Build: buildCopy, }) // TODO remove global reference - config.pubsub.Publish(c, "topic/events", message) + Config.Services.Pubsub.Publish(c, "topic/events", message) // // end publish topic // @@ -581,7 +581,7 @@ func PostBuild(c *gin.Context) { Timeout: b.Repo.Timeout, }) - config.logger.Open(context.Background(), task.ID) - config.queue.Push(context.Background(), task) + Config.Services.Logs.Open(context.Background(), task.ID) + Config.Services.Queue.Push(context.Background(), task) } } diff --git a/server/hook.go b/server/hook.go index 482f58e64..da5c7bfa5 100644 --- a/server/hook.go +++ b/server/hook.go @@ -40,7 +40,7 @@ var skipRe = regexp.MustCompile(`\[(?i:ci *skip|skip *ci)\]`) func GetQueueInfo(c *gin.Context) { c.IndentedJSON(200, - config.queue.Info(c), + Config.Services.Queue.Info(c), ) } @@ -153,80 +153,30 @@ func PostHook(c *gin.Context) { } } - secs, err := store.GetMergedSecretList(c, repo) + secs, err := Config.Services.Secrets.SecretList(repo) if err != nil { logrus.Debugf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err) } - regs, err := store.FromContext(c).RegistryList(repo) + regs, err := Config.Services.Registries.RegistryList(repo) if err != nil { logrus.Debugf("Error getting registry credentials for %s#%d. %s", repo.FullName, build.Number, err) } - // var mustApprove bool - // if build.Event == model.EventPull { - // for _, sec := range secs { - // if sec.SkipVerify { - // continue - // } - // if sec.MatchEvent(model.EventPull) { - // mustApprove = true - // break - // } - // } - // if !mustApprove { - // logrus.Debugf("no secrets exposed to pull_request: status: accepted") - // } - // } - - // if build.Event == model.EventPull && mustApprove { - // old, ferr := remote_.FileRef(user, repo, build.Branch, repo.Config) - // if ferr != nil { - // build.Status = model.StatusBlocked - // logrus.Debugf("cannot fetch base yaml: status: blocked") - // } else if bytes.Equal(old, raw) { - // build.Status = model.StatusPending - // logrus.Debugf("base yaml matches head yaml: status: accepted") - // } else { - // // this block is executed if the target yaml file - // // does not match the base yaml. - // - // // TODO unfortunately we have no good way to get the - // // sender repository permissions unless the user is - // // a registered drone user. - // sender, uerr := store.GetUserLogin(c, build.Sender) - // if uerr != nil { - // build.Status = model.StatusBlocked - // logrus.Debugf("sender does not have a drone account: status: blocked") - // } else { - // if refresher, ok := remote_.(remote.Refresher); ok { - // ok, _ := refresher.Refresh(sender) - // if ok { - // store.UpdateUser(c, sender) - // } - // } - // // if the sender does not have push access to the - // // repository the pull request should be blocked. - // perm, perr := remote_.Perm(sender, repo.Owner, repo.Name) - // if perr == nil && perm.Push == true { - // build.Status = model.StatusPending - // logrus.Debugf("sender %s has push access: status: accepted", sender.Login) - // } else { - // build.Status = model.StatusBlocked - // logrus.Debugf("sender %s does not have push access: status: blocked", sender.Login) - // } - // } - // } - // } else { - // build.Status = model.StatusPending - // } - // update some build fields build.RepoID = repo.ID build.Verified = true build.Status = model.StatusPending - if err := store.CreateBuild(c, build, build.Procs...); err != nil { + if repo.IsGated { + allowed, _ := Config.Services.Senders.SenderAllowed(user, repo, build) + if !allowed { + build.Status = model.StatusBlocked + } + } + + err = store.CreateBuild(c, build, build.Procs...) + if err != nil { logrus.Errorf("failure to save commit for %s. %s", repo.FullName, err) c.AbortWithError(500, err) return @@ -234,9 +184,9 @@ func PostHook(c *gin.Context) { c.JSON(200, build) - // if build.Status == model.StatusBlocked { - // return - // } + if build.Status == model.StatusBlocked { + return + } // get the previous build so that we can send // on status change notifications @@ -321,7 +271,7 @@ func PostHook(c *gin.Context) { Build: buildCopy, }) // TODO remove global reference - config.pubsub.Publish(c, "topic/events", message) + Config.Services.Pubsub.Publish(c, "topic/events", message) // // end publish topic // @@ -341,8 +291,8 @@ func PostHook(c *gin.Context) { Timeout: b.Repo.Timeout, }) - config.logger.Open(context.Background(), task.ID) - config.queue.Push(context.Background(), task) + Config.Services.Logs.Open(context.Background(), task.ID) + Config.Services.Queue.Push(context.Background(), task) } } @@ -460,7 +410,7 @@ func (b *builder) Build() ([]*buildItem, error) { var secrets []compiler.Secret for _, sec := range b.Secs { - if !sec.MatchEvent(b.Curr.Event) { + if !sec.Match(b.Curr.Event) { continue } secrets = append(secrets, compiler.Secret{ diff --git a/server/registry.go b/server/registry.go index 5cefd09e4..d1a46b6af 100644 --- a/server/registry.go +++ b/server/registry.go @@ -5,7 +5,6 @@ import ( "github.com/drone/drone/model" "github.com/drone/drone/router/middleware/session" - "github.com/drone/drone/store" "github.com/gin-gonic/gin" ) @@ -17,7 +16,7 @@ func GetRegistry(c *gin.Context) { repo = session.Repo(c) name = c.Param("registry") ) - registry, err := store.FromContext(c).RegistryFind(repo, name) + registry, err := Config.Services.Registries.RegistryFind(repo, name) if err != nil { c.String(404, "Error getting registry %q. %s", name, err) return @@ -46,7 +45,7 @@ func PostRegistry(c *gin.Context) { c.String(400, "Error inserting registry. %s", err) return } - if err := store.FromContext(c).RegistryCreate(registry); err != nil { + if err := Config.Services.Registries.RegistryCreate(repo, registry); err != nil { c.String(500, "Error inserting registry %q. %s", in.Address, err) return } @@ -67,7 +66,7 @@ func PatchRegistry(c *gin.Context) { return } - registry, err := store.FromContext(c).RegistryFind(repo, name) + registry, err := Config.Services.Registries.RegistryFind(repo, name) if err != nil { c.String(404, "Error getting registry %q. %s", name, err) return @@ -89,7 +88,7 @@ func PatchRegistry(c *gin.Context) { c.String(400, "Error updating registry. %s", err) return } - if err := store.FromContext(c).RegistryUpdate(registry); err != nil { + if err := Config.Services.Registries.RegistryUpdate(repo, registry); err != nil { c.String(500, "Error updating registry %q. %s", in.Address, err) return } @@ -100,7 +99,7 @@ func PatchRegistry(c *gin.Context) { // to the response in json format. func GetRegistryList(c *gin.Context) { repo := session.Repo(c) - list, err := store.FromContext(c).RegistryList(repo) + list, err := Config.Services.Registries.RegistryList(repo) if err != nil { c.String(500, "Error getting registry list. %s", err) return @@ -119,13 +118,7 @@ func DeleteRegistry(c *gin.Context) { repo = session.Repo(c) name = c.Param("registry") ) - registry, err := store.FromContext(c).RegistryFind(repo, name) - if err != nil { - c.String(404, "Error getting registry %q. %s", name, err) - return - } - err = store.FromContext(c).RegistryDelete(registry) - if err != nil { + if err := Config.Services.Registries.RegistryDelete(repo, name); err != nil { c.String(500, "Error deleting registry %q. %s", name, err) return } diff --git a/server/repo.go b/server/repo.go index 14d693c5d..232ad7e9f 100644 --- a/server/repo.go +++ b/server/repo.go @@ -98,6 +98,7 @@ func PatchRepo(c *gin.Context) { in := &struct { IsTrusted *bool `json:"trusted,omitempty"` + IsGated *bool `json:"gated,omitempty"` Timeout *int64 `json:"timeout,omitempty"` AllowPull *bool `json:"allow_pr,omitempty"` AllowPush *bool `json:"allow_push,omitempty"` @@ -109,6 +110,11 @@ func PatchRepo(c *gin.Context) { return } + if (in.IsTrusted != nil || in.Timeout != nil || in.IsGated != nil) && !user.Admin { + c.String(403, "Insufficient privileges") + return + } + if in.AllowPush != nil { repo.AllowPush = *in.AllowPush } @@ -121,10 +127,13 @@ func PatchRepo(c *gin.Context) { if in.AllowTag != nil { repo.AllowTag = *in.AllowTag } - if in.IsTrusted != nil && user.Admin { + if in.IsGated != nil { + repo.IsGated = *in.IsGated + } + if in.IsTrusted != nil { repo.IsTrusted = *in.IsTrusted } - if in.Timeout != nil && user.Admin { + if in.Timeout != nil { repo.Timeout = *in.Timeout } diff --git a/server/rpc.go b/server/rpc.go index 6be318816..ff1896f34 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -5,7 +5,6 @@ import ( "context" "encoding/json" "log" - "os" "strconv" "github.com/cncd/logging" @@ -22,23 +21,57 @@ import ( // This file is a complete disaster because I'm trying to wedge in some // experimental code. Please pardon our appearance during renovations. -var config = struct { - pubsub pubsub.Publisher - queue queue.Queue - logger logging.Log - secret string - host string -}{ - pubsub.New(), - queue.New(), - logging.New(), - os.Getenv("DRONE_SECRET"), - os.Getenv("DRONE_HOST"), -} +// Config is an evil global configuration that will be used as we transition / +// refactor the codebase to move away from storing these values in the Context. +var Config = struct { + Services struct { + Pubsub pubsub.Publisher + Queue queue.Queue + Logs logging.Log + Senders model.SenderService + Secrets model.SecretService + Registries model.RegistryService + } + Storage struct { + // Users model.UserStore + // Repos model.RepoStore + // Builds model.BuildStore + // Logs model.LogStore + Files model.FileStore + Procs model.ProcStore + // Registries model.RegistryStore + // Secrets model.SecretStore + } + Server struct { + Key string + Cert string + Host string + Port string + Pass string + // Open bool + // Orgs map[string]struct{} + // Admins map[string]struct{} + } + Pipeline struct { + Volumes []string + Networks []string + Privileged []string + } +}{} -func init() { - config.pubsub.Create(context.Background(), "topic/events") -} +// var config = struct { +// pubsub pubsub.Publisher +// queue queue.Queue +// logger logging.Log +// secret string +// host string +// }{ +// pubsub.New(), +// queue.New(), +// logging.New(), +// os.Getenv("DRONE_SECRET"), +// os.Getenv("DRONE_HOST"), +// } // func SetupRPC() gin.HandlerFunc { // return func(c *gin.Context) { @@ -48,18 +81,18 @@ func init() { func RPCHandler(c *gin.Context) { - if secret := c.Request.Header.Get("Authorization"); secret != "Bearer "+config.secret { - log.Printf("Unable to connect agent. Invalid authorization token %q does not match %q", secret, config.secret) + if secret := c.Request.Header.Get("Authorization"); secret != "Bearer "+Config.Server.Pass { + log.Printf("Unable to connect agent. Invalid authorization token %q does not match %q", secret, Config.Server.Pass) c.String(401, "Unable to connect agent. Invalid authorization token") return } peer := RPC{ remote: remote.FromContext(c), store: store.FromContext(c), - queue: config.queue, - pubsub: config.pubsub, - logger: config.logger, - host: config.host, + queue: Config.Services.Queue, + pubsub: Config.Services.Pubsub, + logger: Config.Services.Logs, + host: Config.Server.Host, } rpc.NewServer(&peer).ServeHTTP(c.Writer, c.Request) } @@ -201,7 +234,7 @@ func (s *RPC) Upload(c context.Context, id string, file *rpc.File) error { ) } - return s.store.FileCreate(&model.File{ + return Config.Storage.Files.FileCreate(&model.File{ BuildID: proc.BuildID, ProcID: proc.ID, Mime: file.Mime, diff --git a/server/secret.go b/server/secret.go index 8ef490b36..34bed546b 100644 --- a/server/secret.go +++ b/server/secret.go @@ -5,173 +5,118 @@ import ( "github.com/drone/drone/model" "github.com/drone/drone/router/middleware/session" - "github.com/drone/drone/store" "github.com/gin-gonic/gin" ) -func GetGlobalSecrets(c *gin.Context) { - secrets, err := store.GetGlobalSecretList(c) - +// GetSecret gets the named secret from the database and writes +// to the response in json format. +func GetSecret(c *gin.Context) { + var ( + repo = session.Repo(c) + name = c.Param("secret") + ) + secret, err := Config.Services.Secrets.SecretFind(repo, name) if err != nil { - c.AbortWithStatus(http.StatusInternalServerError) + c.String(404, "Error getting secret %q. %s", name, err) return } - - var list []*model.TeamSecret - - for _, s := range secrets { - list = append(list, s.Clone()) - } - - c.JSON(http.StatusOK, list) -} - -func PostGlobalSecret(c *gin.Context) { - in := &model.TeamSecret{} - err := c.Bind(in) - if err != nil { - c.String(http.StatusBadRequest, "Invalid JSON input. %s", err.Error()) - return - } - in.ID = 0 - - err = store.SetGlobalSecret(c, in) - if err != nil { - c.String(http.StatusInternalServerError, "Unable to persist global secret. %s", err.Error()) - return - } - - c.String(http.StatusOK, "") -} - -func DeleteGlobalSecret(c *gin.Context) { - name := c.Param("secret") - - secret, err := store.GetGlobalSecret(c, name) - if err != nil { - c.String(http.StatusNotFound, "Cannot find secret %s.", name) - return - } - err = store.DeleteGlobalSecret(c, secret) - if err != nil { - c.String(http.StatusInternalServerError, "Unable to delete global secret. %s", err.Error()) - return - } - - c.String(http.StatusOK, "") -} - -func GetSecrets(c *gin.Context) { - repo := session.Repo(c) - secrets, err := store.GetSecretList(c, repo) - - if err != nil { - c.AbortWithStatus(http.StatusInternalServerError) - return - } - - var list []*model.RepoSecret - - for _, s := range secrets { - list = append(list, s.Clone()) - } - - c.JSON(http.StatusOK, list) + c.JSON(200, secret.Copy()) } +// PostSecret persists the secret to the database. func PostSecret(c *gin.Context) { repo := session.Repo(c) - in := &model.RepoSecret{} - err := c.Bind(in) - if err != nil { - c.String(http.StatusBadRequest, "Invalid JSON input. %s", err.Error()) + in := new(model.Secret) + if err := c.Bind(in); err != nil { + c.String(http.StatusBadRequest, "Error parsing secret. %s", err) return } - in.ID = 0 - in.RepoID = repo.ID - - err = store.SetSecret(c, in) - if err != nil { - c.String(http.StatusInternalServerError, "Unable to persist secret. %s", err.Error()) + secret := &model.Secret{ + RepoID: repo.ID, + Name: in.Name, + Value: in.Value, + Events: in.Events, + Images: in.Images, + } + if err := secret.Validate(); err != nil { + c.String(400, "Error inserting secret. %s", err) return } - - c.String(http.StatusOK, "") + if err := Config.Services.Secrets.SecretCreate(repo, secret); err != nil { + c.String(500, "Error inserting secret %q. %s", in.Name, err) + return + } + c.JSON(200, secret.Copy()) } -func DeleteSecret(c *gin.Context) { +// PatchSecret updates the secret in the database. +func PatchSecret(c *gin.Context) { + var ( + repo = session.Repo(c) + name = c.Param("secret") + ) + + in := new(model.Secret) + err := c.Bind(in) + if err != nil { + c.String(http.StatusBadRequest, "Error parsing secret. %s", err) + return + } + + secret, err := Config.Services.Secrets.SecretFind(repo, name) + if err != nil { + c.String(404, "Error getting secret %q. %s", name, err) + return + } + if in.Value != "" { + secret.Value = in.Value + } + if len(in.Events) != 0 { + secret.Events = in.Events + } + if len(in.Images) != 0 { + secret.Images = in.Images + } + + if err := secret.Validate(); err != nil { + c.String(400, "Error updating secret. %s", err) + return + } + if err := Config.Services.Secrets.SecretUpdate(repo, secret); err != nil { + c.String(500, "Error updating secret %q. %s", in.Name, err) + return + } + c.JSON(200, secret.Copy()) +} + +// GetSecretList gets the secret list from the database and writes +// to the response in json format. +func GetSecretList(c *gin.Context) { repo := session.Repo(c) - name := c.Param("secret") - - secret, err := store.GetSecret(c, repo, name) + list, err := Config.Services.Secrets.SecretList(repo) if err != nil { - c.String(http.StatusNotFound, "Cannot find secret %s.", name) + c.String(500, "Error getting secret list. %s", err) return } - err = store.DeleteSecret(c, secret) - if err != nil { - c.String(http.StatusInternalServerError, "Unable to delete secret. %s", err.Error()) - return + // copy the secret detail to remove the sensitive + // password and token fields. + for i, secret := range list { + list[i] = secret.Copy() } - - c.String(http.StatusOK, "") + c.JSON(200, list) } -func GetTeamSecrets(c *gin.Context) { - team := c.Param("team") - secrets, err := store.GetTeamSecretList(c, team) - - if err != nil { - c.AbortWithStatus(http.StatusInternalServerError) +// DeleteSecret deletes the named secret from the database. +func DeleteSecret(c *gin.Context) { + var ( + repo = session.Repo(c) + name = c.Param("secret") + ) + if err := Config.Services.Secrets.SecretDelete(repo, name); err != nil { + c.String(500, "Error deleting secret %q. %s", name, err) return } - - var list []*model.TeamSecret - - for _, s := range secrets { - list = append(list, s.Clone()) - } - - c.JSON(http.StatusOK, list) -} - -func PostTeamSecret(c *gin.Context) { - team := c.Param("team") - - in := &model.TeamSecret{} - err := c.Bind(in) - if err != nil { - c.String(http.StatusBadRequest, "Invalid JSON input. %s", err.Error()) - return - } - in.ID = 0 - in.Key = team - - err = store.SetTeamSecret(c, in) - if err != nil { - c.String(http.StatusInternalServerError, "Unable to persist team secret. %s", err.Error()) - return - } - - c.String(http.StatusOK, "") -} - -func DeleteTeamSecret(c *gin.Context) { - team := c.Param("team") - name := c.Param("secret") - - secret, err := store.GetTeamSecret(c, team, name) - if err != nil { - c.String(http.StatusNotFound, "Cannot find secret %s.", name) - return - } - err = store.DeleteTeamSecret(c, secret) - if err != nil { - c.String(http.StatusInternalServerError, "Unable to delete team secret. %s", err.Error()) - return - } - - c.String(http.StatusOK, "") + c.String(204, "") } diff --git a/server/stream.go b/server/stream.go index a3c7ed73c..2412453ed 100644 --- a/server/stream.go +++ b/server/stream.go @@ -102,7 +102,7 @@ func LogStream(c *gin.Context) { go func() { // TODO remove global variable - config.logger.Tail(ctx, fmt.Sprint(proc.ID), func(entries ...*logging.Entry) { + Config.Services.Logs.Tail(ctx, fmt.Sprint(proc.ID), func(entries ...*logging.Entry) { for _, entry := range entries { select { case <-ctx.Done(): @@ -167,7 +167,7 @@ func EventStream(c *gin.Context) { go func() { // TODO remove this from global config - config.pubsub.Subscribe(c, "topic/events", func(m pubsub.Message) { + Config.Services.Pubsub.Subscribe(c, "topic/events", func(m pubsub.Message) { name := m.Labels["repo"] priv := m.Labels["private"] if repo[name] || priv == "false" { diff --git a/store/datastore/ddl/mysql/14.sql b/store/datastore/ddl/mysql/14.sql new file mode 100644 index 000000000..3524e5512 --- /dev/null +++ b/store/datastore/ddl/mysql/14.sql @@ -0,0 +1,22 @@ +-- +migrate Up + +ALTER TABLE repos ADD COLUMN repo_gated BOOLEAN; +UPDATE repos SET repo_gated = false; + +CREATE TABLE senders ( + sender_id INTEGER PRIMARY KEY AUTO_INCREMENT +,sender_repo_id INTEGER +,sender_login VARCHAR(250) +,sender_allow BOOLEAN +,sender_block BOOLEAN + +,UNIQUE(sender_repo_id,sender_login) +); + +CREATE INDEX sender_repo_ix ON senders (sender_repo_id); + +-- +migrate Down + +ALTER TABLE repos DROP COLUMN repo_gated; +DROP INDEX sender_repo_ix; +DROP TABLE senders; diff --git a/store/datastore/ddl/postgres/14.sql b/store/datastore/ddl/postgres/14.sql new file mode 100644 index 000000000..a80fed769 --- /dev/null +++ b/store/datastore/ddl/postgres/14.sql @@ -0,0 +1,22 @@ +-- +migrate Up + +ALTER TABLE repos ADD COLUMN repo_gated BOOLEAN; +UPDATE repos SET repo_gated = false; + +CREATE TABLE senders ( + sender_id SERIAL PRIMARY KEY +,sender_repo_id INTEGER +,sender_login VARCHAR(250) +,sender_allow BOOLEAN +,sender_block BOOLEAN + +,UNIQUE(sender_repo_id,sender_login) +); + +CREATE INDEX sender_repo_ix ON senders (sender_repo_id); + +-- +migrate Down + +ALTER TABLE repos DROP COLUMN repo_gated; +DROP INDEX sender_repo_ix; +DROP TABLE senders; diff --git a/store/datastore/ddl/sqlite3/14.sql b/store/datastore/ddl/sqlite3/14.sql new file mode 100644 index 000000000..cc91f2024 --- /dev/null +++ b/store/datastore/ddl/sqlite3/14.sql @@ -0,0 +1,22 @@ +-- +migrate Up + +ALTER TABLE repos ADD COLUMN repo_gated BOOLEAN; +UPDATE repos SET repo_gated = 0; + +CREATE TABLE senders ( + sender_id INTEGER PRIMARY KEY AUTOINCREMENT +,sender_repo_id INTEGER +,sender_login BOOLEAN +,sender_allow BOOLEAN +,sender_block BOOLEAN + +,UNIQUE(sender_repo_id,sender_login) +); + +CREATE INDEX sender_repo_ix ON senders (sender_repo_id); + +-- +migrate Down + +ALTER TABLE repos DROP COLUMN repo_gated; +DROP INDEX sender_repo_ix; +DROP TABLE senders; diff --git a/store/datastore/repo_secret.go b/store/datastore/repo_secret.go deleted file mode 100644 index 6252c0bc8..000000000 --- a/store/datastore/repo_secret.go +++ /dev/null @@ -1,53 +0,0 @@ -package datastore - -import ( - "github.com/drone/drone/model" - "github.com/russross/meddler" -) - -func (db *datastore) GetSecretList(repo *model.Repo) ([]*model.RepoSecret, error) { - var secrets = []*model.RepoSecret{} - var err = meddler.QueryAll(db, &secrets, rebind(secretListQuery), repo.ID) - return secrets, err -} - -func (db *datastore) GetSecret(repo *model.Repo, name string) (*model.RepoSecret, error) { - var secret = new(model.RepoSecret) - var err = meddler.QueryRow(db, secret, rebind(secretNameQuery), repo.ID, name) - return secret, err -} - -func (db *datastore) SetSecret(sec *model.RepoSecret) error { - var got = new(model.RepoSecret) - var err = meddler.QueryRow(db, got, rebind(secretNameQuery), sec.RepoID, sec.Name) - if err == nil && got.ID != 0 { - sec.ID = got.ID // update existing id - } - return meddler.Save(db, secretTable, sec) -} - -func (db *datastore) DeleteSecret(sec *model.RepoSecret) error { - _, err := db.Exec(rebind(secretDeleteStmt), sec.ID) - return err -} - -const secretTable = "secrets" - -const secretListQuery = ` -SELECT * -FROM secrets -WHERE secret_repo_id = ? -` - -const secretNameQuery = ` -SELECT * -FROM secrets -WHERE secret_repo_id = ? - AND secret_name = ? -LIMIT 1; -` - -const secretDeleteStmt = ` -DELETE FROM secrets -WHERE secret_id = ? -` diff --git a/store/datastore/repo_secret_test.go b/store/datastore/repo_secret_test.go deleted file mode 100644 index 91e51d314..000000000 --- a/store/datastore/repo_secret_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package datastore - -import ( - "testing" - - "github.com/drone/drone/model" - "github.com/franela/goblin" -) - -func TestRepoSecrets(t *testing.T) { - db := openTest() - defer db.Close() - - s := From(db) - g := goblin.Goblin(t) - g.Describe("RepoSecrets", func() { - - // before each test be sure to purge the package - // table data from the database. - g.BeforeEach(func() { - db.Exec(rebind("DELETE FROM secrets")) - }) - - g.It("Should set and get a secret", func() { - secret := &model.RepoSecret{ - RepoID: 1, - Name: "foo", - Value: "bar", - Images: []string{"docker", "gcr"}, - Events: []string{"push", "tag"}, - SkipVerify: true, - Conceal: true, - } - err := s.SetSecret(secret) - g.Assert(err == nil).IsTrue() - g.Assert(secret.ID != 0).IsTrue() - - got, err := s.GetSecret(&model.Repo{ID: 1}, secret.Name) - g.Assert(err == nil).IsTrue() - g.Assert(got.Name).Equal(secret.Name) - g.Assert(got.Value).Equal(secret.Value) - g.Assert(got.Images).Equal(secret.Images) - g.Assert(got.Events).Equal(secret.Events) - g.Assert(got.SkipVerify).Equal(secret.SkipVerify) - g.Assert(got.Conceal).Equal(secret.Conceal) - }) - - g.It("Should update a secret", func() { - secret := &model.RepoSecret{ - RepoID: 1, - Name: "foo", - Value: "bar", - } - s.SetSecret(secret) - secret.Value = "baz" - s.SetSecret(secret) - - got, err := s.GetSecret(&model.Repo{ID: 1}, secret.Name) - g.Assert(err == nil).IsTrue() - g.Assert(got.Name).Equal(secret.Name) - g.Assert(got.Value).Equal(secret.Value) - }) - - g.It("Should list secrets", func() { - s.SetSecret(&model.RepoSecret{ - RepoID: 1, - Name: "foo", - Value: "bar", - }) - s.SetSecret(&model.RepoSecret{ - RepoID: 1, - Name: "bar", - Value: "baz", - }) - secrets, err := s.GetSecretList(&model.Repo{ID: 1}) - g.Assert(err == nil).IsTrue() - g.Assert(len(secrets)).Equal(2) - }) - - g.It("Should delete a secret", func() { - secret := &model.RepoSecret{ - RepoID: 1, - Name: "foo", - Value: "bar", - } - s.SetSecret(secret) - - _, err := s.GetSecret(&model.Repo{ID: 1}, secret.Name) - g.Assert(err == nil).IsTrue() - - err = s.DeleteSecret(secret) - g.Assert(err == nil).IsTrue() - - _, err = s.GetSecret(&model.Repo{ID: 1}, secret.Name) - g.Assert(err != nil).IsTrue("expect a no rows in result set error") - }) - }) -} diff --git a/store/datastore/secret.go b/store/datastore/secret.go new file mode 100644 index 000000000..33e8c0c7e --- /dev/null +++ b/store/datastore/secret.go @@ -0,0 +1,35 @@ +package datastore + +import ( + "github.com/drone/drone/model" + "github.com/drone/drone/store/datastore/sql" + "github.com/russross/meddler" +) + +func (db *datastore) SecretFind(repo *model.Repo, name string) (*model.Secret, error) { + stmt := sql.Lookup(db.driver, "secret-find-repo-name") + data := new(model.Secret) + err := meddler.QueryRow(db, data, stmt, repo.ID, name) + return data, err +} + +func (db *datastore) SecretList(repo *model.Repo) ([]*model.Secret, error) { + stmt := sql.Lookup(db.driver, "secret-find-repo") + data := []*model.Secret{} + err := meddler.QueryAll(db, &data, stmt, repo.ID) + return data, err +} + +func (db *datastore) SecretCreate(secret *model.Secret) error { + return meddler.Insert(db, "secrets", secret) +} + +func (db *datastore) SecretUpdate(secret *model.Secret) error { + return meddler.Update(db, "secrets", secret) +} + +func (db *datastore) SecretDelete(secret *model.Secret) error { + stmt := sql.Lookup(db.driver, "secret-delete") + _, err := db.Exec(stmt, secret.ID) + return err +} diff --git a/store/datastore/secret_test.go b/store/datastore/secret_test.go new file mode 100644 index 000000000..16109b21b --- /dev/null +++ b/store/datastore/secret_test.go @@ -0,0 +1,139 @@ +package datastore + +import ( + "testing" + + "github.com/drone/drone/model" +) + +func TestSecretFind(t *testing.T) { + s := newTest() + defer func() { + s.Exec("delete from secrets") + s.Close() + }() + + err := s.SecretCreate(&model.Secret{ + RepoID: 1, + Name: "password", + Value: "correct-horse-battery-staple", + Images: []string{"golang", "node"}, + Events: []string{"push", "tag"}, + }) + if err != nil { + t.Errorf("Unexpected error: insert secret: %s", err) + return + } + + secret, err := s.SecretFind(&model.Repo{ID: 1}, "password") + if err != nil { + t.Error(err) + return + } + if got, want := secret.RepoID, int64(1); got != want { + t.Errorf("Want repo id %d, got %d", want, got) + } + if got, want := secret.Name, "password"; got != want { + t.Errorf("Want secret name %s, got %s", want, got) + } + if got, want := secret.Value, "correct-horse-battery-staple"; got != want { + t.Errorf("Want secret value %s, got %s", want, got) + } + if got, want := secret.Events[0], "push"; got != want { + t.Errorf("Want secret event %s, got %s", want, got) + } + if got, want := secret.Events[1], "tag"; got != want { + t.Errorf("Want secret event %s, got %s", want, got) + } + if got, want := secret.Images[0], "golang"; got != want { + t.Errorf("Want secret image %s, got %s", want, got) + } + if got, want := secret.Images[1], "node"; got != want { + t.Errorf("Want secret image %s, got %s", want, got) + } +} + +func TestSecretList(t *testing.T) { + s := newTest() + defer func() { + s.Exec("delete from secrets") + s.Close() + }() + + s.SecretCreate(&model.Secret{ + RepoID: 1, + Name: "foo", + Value: "bar", + }) + s.SecretCreate(&model.Secret{ + RepoID: 1, + Name: "baz", + Value: "qux", + }) + + list, err := s.SecretList(&model.Repo{ID: 1}) + if err != nil { + t.Error(err) + return + } + if got, want := len(list), 2; got != want { + t.Errorf("Want %d registries, got %d", want, got) + } +} + +func TestSecretUpdate(t *testing.T) { + s := newTest() + defer func() { + s.Exec("delete from secrets") + s.Close() + }() + + secret := &model.Secret{ + RepoID: 1, + Name: "foo", + Value: "baz", + } + if err := s.SecretCreate(secret); err != nil { + t.Errorf("Unexpected error: insert secret: %s", err) + return + } + secret.Value = "qux" + if err := s.SecretUpdate(secret); err != nil { + t.Errorf("Unexpected error: update secret: %s", err) + return + } + updated, err := s.SecretFind(&model.Repo{ID: 1}, "foo") + if err != nil { + t.Error(err) + return + } + if got, want := updated.Value, "qux"; got != want { + t.Errorf("Want secret value %s, got %s", want, got) + } +} + +func TestSecretIndexes(t *testing.T) { + s := newTest() + defer func() { + s.Exec("delete from secrets") + s.Close() + }() + + if err := s.SecretCreate(&model.Secret{ + RepoID: 1, + Name: "foo", + Value: "bar", + }); err != nil { + t.Errorf("Unexpected error: insert secret: %s", err) + return + } + + // fail due to duplicate name + if err := s.SecretCreate(&model.Secret{ + RepoID: 1, + Name: "foo", + Value: "baz", + }); err == nil { + t.Errorf("Unexpected error: dupliate name") + } +} diff --git a/store/datastore/sender.go b/store/datastore/sender.go new file mode 100644 index 000000000..784310863 --- /dev/null +++ b/store/datastore/sender.go @@ -0,0 +1,35 @@ +package datastore + +import ( + "github.com/drone/drone/model" + "github.com/drone/drone/store/datastore/sql" + "github.com/russross/meddler" +) + +func (db *datastore) SenderFind(repo *model.Repo, login string) (*model.Sender, error) { + stmt := sql.Lookup(db.driver, "sender-find-repo-login") + data := new(model.Sender) + err := meddler.QueryRow(db, data, stmt, repo.ID, login) + return data, err +} + +func (db *datastore) SenderList(repo *model.Repo) ([]*model.Sender, error) { + stmt := sql.Lookup(db.driver, "sender-find-repo") + data := []*model.Sender{} + err := meddler.QueryAll(db, &data, stmt, repo.ID) + return data, err +} + +func (db *datastore) SenderCreate(sender *model.Sender) error { + return meddler.Insert(db, "senders", sender) +} + +func (db *datastore) SenderUpdate(sender *model.Sender) error { + return meddler.Update(db, "senders", sender) +} + +func (db *datastore) SenderDelete(sender *model.Sender) error { + stmt := sql.Lookup(db.driver, "sender-delete") + _, err := db.Exec(stmt, sender.ID) + return err +} diff --git a/store/datastore/sender_test.go b/store/datastore/sender_test.go new file mode 100644 index 000000000..346ce2c6f --- /dev/null +++ b/store/datastore/sender_test.go @@ -0,0 +1,131 @@ +package datastore + +import ( + "testing" + + "github.com/drone/drone/model" +) + +func TestSenderFind(t *testing.T) { + s := newTest() + defer func() { + s.Exec("delete from senders") + s.Close() + }() + + err := s.SenderCreate(&model.Sender{ + RepoID: 1, + Login: "octocat", + Allow: true, + Block: false, + }) + if err != nil { + t.Errorf("Unexpected error: insert secret: %s", err) + return + } + + sender, err := s.SenderFind(&model.Repo{ID: 1}, "octocat") + if err != nil { + t.Error(err) + return + } + if got, want := sender.RepoID, int64(1); got != want { + t.Errorf("Want repo id %d, got %d", want, got) + } + if got, want := sender.Login, "octocat"; got != want { + t.Errorf("Want sender login %s, got %s", want, got) + } + if got, want := sender.Allow, true; got != want { + t.Errorf("Want sender allow %v, got %v", want, got) + } +} + +func TestSenderList(t *testing.T) { + s := newTest() + defer func() { + s.Exec("delete from senders") + s.Close() + }() + + s.SenderCreate(&model.Sender{ + RepoID: 1, + Login: "octocat", + Allow: true, + Block: false, + }) + s.SenderCreate(&model.Sender{ + RepoID: 1, + Login: "defunkt", + Allow: true, + Block: false, + }) + + list, err := s.SenderList(&model.Repo{ID: 1}) + if err != nil { + t.Error(err) + return + } + if got, want := len(list), 2; got != want { + t.Errorf("Want %d senders, got %d", want, got) + } +} + +func TestSenderUpdate(t *testing.T) { + s := newTest() + defer func() { + s.Exec("delete from senders") + s.Close() + }() + + sender := &model.Sender{ + RepoID: 1, + Login: "octocat", + Allow: true, + Block: false, + } + if err := s.SenderCreate(sender); err != nil { + t.Errorf("Unexpected error: insert sender: %s", err) + return + } + sender.Allow = false + if err := s.SenderUpdate(sender); err != nil { + t.Errorf("Unexpected error: update sender: %s", err) + return + } + updated, err := s.SenderFind(&model.Repo{ID: 1}, "octocat") + if err != nil { + t.Error(err) + return + } + if got, want := updated.Allow, false; got != want { + t.Errorf("Want allow value %v, got %v", want, got) + } +} + +func TestSenderIndexes(t *testing.T) { + s := newTest() + defer func() { + s.Exec("delete from senders") + s.Close() + }() + + if err := s.SenderCreate(&model.Sender{ + RepoID: 1, + Login: "octocat", + Allow: true, + Block: false, + }); err != nil { + t.Errorf("Unexpected error: insert sender: %s", err) + return + } + + // fail due to duplicate name + if err := s.SenderCreate(&model.Sender{ + RepoID: 1, + Login: "octocat", + Allow: true, + Block: false, + }); err == nil { + t.Errorf("Unexpected error: dupliate login") + } +} diff --git a/store/datastore/sql/postgres/files/secret.sql b/store/datastore/sql/postgres/files/secret.sql new file mode 100644 index 000000000..4b7f024f3 --- /dev/null +++ b/store/datastore/sql/postgres/files/secret.sql @@ -0,0 +1,32 @@ +-- name: secret-find-repo + +SELECT + secret_id +,secret_repo_id +,secret_name +,secret_value +,secret_images +,secret_events +,secret_conceal +,secret_skip_verify +FROM secrets +WHERE secret_repo_id = $1 + +-- name: secret-find-repo-name + +SELECT +secret_id +,secret_repo_id +,secret_name +,secret_value +,secret_images +,secret_events +,secret_conceal +,secret_skip_verify +FROM secrets +WHERE secret_repo_id = $1 + AND secret_name = $2 + +-- name: secret-delete + +DELETE FROM secrets WHERE secret_id = $1 diff --git a/store/datastore/sql/postgres/files/sender.sql b/store/datastore/sql/postgres/files/sender.sql new file mode 100644 index 000000000..666eb4a9a --- /dev/null +++ b/store/datastore/sql/postgres/files/sender.sql @@ -0,0 +1,30 @@ +-- name: sender-find-repo + +SELECT + sender_id +,sender_repo_id +,sender_login +,sender_allow +,sender_block +FROM senders +WHERE sender_repo_id = $1 + +-- name: sender-find-repo-login + +SELECT + sender_id +,sender_repo_id +,sender_login +,sender_allow +,sender_block +FROM senders +WHERE sender_repo_id = $1 + AND sender_login = $2 + +-- name: sender-delete-repo + +DELETE FROM senders WHERE sender_repo_id = $1 + +-- name: sender-delete + +DELETE FROM senders WHERE sender_id = $1 diff --git a/store/datastore/sql/postgres/sql_gen.go b/store/datastore/sql/postgres/sql_gen.go index c898055f1..654f1e759 100644 --- a/store/datastore/sql/postgres/sql_gen.go +++ b/store/datastore/sql/postgres/sql_gen.go @@ -19,6 +19,13 @@ var index = map[string]string{ "registry-find-repo-addr": registryFindRepoAddr, "registry-delete-repo": registryDeleteRepo, "registry-delete": registryDelete, + "secret-find-repo": secretFindRepo, + "secret-find-repo-name": secretFindRepoName, + "secret-delete": secretDelete, + "sender-find-repo": senderFindRepo, + "sender-find-repo-login": senderFindRepoLogin, + "sender-delete-repo": senderDeleteRepo, + "sender-delete": senderDelete, } var filesFindBuild = ` @@ -188,3 +195,67 @@ DELETE FROM registry WHERE registry_repo_id = $1 var registryDelete = ` DELETE FROM registry WHERE registry_id = $1 ` + +var secretFindRepo = ` +SELECT + secret_id +,secret_repo_id +,secret_name +,secret_value +,secret_images +,secret_events +,secret_conceal +,secret_skip_verify +FROM secrets +WHERE secret_repo_id = $1 +` + +var secretFindRepoName = ` +SELECT +secret_id +,secret_repo_id +,secret_name +,secret_value +,secret_images +,secret_events +,secret_conceal +,secret_skip_verify +FROM secrets +WHERE secret_repo_id = $1 + AND secret_name = $2 +` + +var secretDelete = ` +DELETE FROM secrets WHERE secret_id = $1 +` + +var senderFindRepo = ` +SELECT + sender_id +,sender_repo_id +,sender_login +,sender_allow +,sender_block +FROM senders +WHERE sender_repo_id = $1 +` + +var senderFindRepoLogin = ` +SELECT + sender_id +,sender_repo_id +,sender_login +,sender_allow +,sender_block +FROM senders +WHERE sender_repo_id = $1 + AND sender_login = $2 +` + +var senderDeleteRepo = ` +DELETE FROM senders WHERE sender_repo_id = $1 +` + +var senderDelete = ` +DELETE FROM senders WHERE sender_id = $1 +` diff --git a/store/datastore/sql/sqlite/files/secret.sql b/store/datastore/sql/sqlite/files/secret.sql new file mode 100644 index 000000000..1eea03fa6 --- /dev/null +++ b/store/datastore/sql/sqlite/files/secret.sql @@ -0,0 +1,32 @@ +-- name: secret-find-repo + +SELECT + secret_id +,secret_repo_id +,secret_name +,secret_value +,secret_images +,secret_events +,secret_conceal +,secret_skip_verify +FROM secrets +WHERE secret_repo_id = ? + +-- name: secret-find-repo-name + +SELECT +secret_id +,secret_repo_id +,secret_name +,secret_value +,secret_images +,secret_events +,secret_conceal +,secret_skip_verify +FROM secrets +WHERE secret_repo_id = ? + AND secret_name = ? + +-- name: secret-delete + +DELETE FROM secrets WHERE secret_id = ? diff --git a/store/datastore/sql/sqlite/files/sender.sql b/store/datastore/sql/sqlite/files/sender.sql new file mode 100644 index 000000000..7e87a6541 --- /dev/null +++ b/store/datastore/sql/sqlite/files/sender.sql @@ -0,0 +1,30 @@ +-- name: sender-find-repo + +SELECT + sender_id +,sender_repo_id +,sender_login +,sender_allow +,sender_block +FROM senders +WHERE sender_repo_id = ? + +-- name: sender-find-repo-login + +SELECT + sender_id +,sender_repo_id +,sender_login +,sender_allow +,sender_block +FROM senders +WHERE sender_repo_id = ? + AND sender_login = ? + +-- name: sender-delete-repo + +DELETE FROM senders WHERE sender_repo_id = ? + +-- name: sender-delete + +DELETE FROM senders WHERE sender_id = ? diff --git a/store/datastore/sql/sqlite/sql_gen.go b/store/datastore/sql/sqlite/sql_gen.go index a12228186..61698f07b 100644 --- a/store/datastore/sql/sqlite/sql_gen.go +++ b/store/datastore/sql/sqlite/sql_gen.go @@ -19,6 +19,13 @@ var index = map[string]string{ "registry-find-repo-addr": registryFindRepoAddr, "registry-delete-repo": registryDeleteRepo, "registry-delete": registryDelete, + "secret-find-repo": secretFindRepo, + "secret-find-repo-name": secretFindRepoName, + "secret-delete": secretDelete, + "sender-find-repo": senderFindRepo, + "sender-find-repo-login": senderFindRepoLogin, + "sender-delete-repo": senderDeleteRepo, + "sender-delete": senderDelete, } var filesFindBuild = ` @@ -188,3 +195,67 @@ DELETE FROM registry WHERE registry_repo_id = ? var registryDelete = ` DELETE FROM registry WHERE registry_id = ? ` + +var secretFindRepo = ` +SELECT + secret_id +,secret_repo_id +,secret_name +,secret_value +,secret_images +,secret_events +,secret_conceal +,secret_skip_verify +FROM secrets +WHERE secret_repo_id = ? +` + +var secretFindRepoName = ` +SELECT +secret_id +,secret_repo_id +,secret_name +,secret_value +,secret_images +,secret_events +,secret_conceal +,secret_skip_verify +FROM secrets +WHERE secret_repo_id = ? + AND secret_name = ? +` + +var secretDelete = ` +DELETE FROM secrets WHERE secret_id = ? +` + +var senderFindRepo = ` +SELECT + sender_id +,sender_repo_id +,sender_login +,sender_allow +,sender_block +FROM senders +WHERE sender_repo_id = ? +` + +var senderFindRepoLogin = ` +SELECT + sender_id +,sender_repo_id +,sender_login +,sender_allow +,sender_block +FROM senders +WHERE sender_repo_id = ? + AND sender_login = ? +` + +var senderDeleteRepo = ` +DELETE FROM senders WHERE sender_repo_id = ? +` + +var senderDelete = ` +DELETE FROM senders WHERE sender_id = ? +` diff --git a/store/datastore/team_secret.go b/store/datastore/team_secret.go deleted file mode 100644 index edf86b8ed..000000000 --- a/store/datastore/team_secret.go +++ /dev/null @@ -1,53 +0,0 @@ -package datastore - -import ( - "github.com/drone/drone/model" - "github.com/russross/meddler" -) - -func (db *datastore) GetTeamSecretList(team string) ([]*model.TeamSecret, error) { - var secrets = []*model.TeamSecret{} - var err = meddler.QueryAll(db, &secrets, rebind(teamSecretListQuery), team) - return secrets, err -} - -func (db *datastore) GetTeamSecret(team, name string) (*model.TeamSecret, error) { - var secret = new(model.TeamSecret) - var err = meddler.QueryRow(db, secret, rebind(teamSecretNameQuery), team, name) - return secret, err -} - -func (db *datastore) SetTeamSecret(sec *model.TeamSecret) error { - var got = new(model.TeamSecret) - var err = meddler.QueryRow(db, got, rebind(teamSecretNameQuery), sec.Key, sec.Name) - if err == nil && got.ID != 0 { - sec.ID = got.ID // update existing id - } - return meddler.Save(db, teamSecretTable, sec) -} - -func (db *datastore) DeleteTeamSecret(sec *model.TeamSecret) error { - _, err := db.Exec(rebind(teamSecretDeleteStmt), sec.ID) - return err -} - -const teamSecretTable = "team_secrets" - -const teamSecretListQuery = ` -SELECT * -FROM team_secrets -WHERE team_secret_key = ? -` - -const teamSecretNameQuery = ` -SELECT * -FROM team_secrets -WHERE team_secret_key = ? - AND team_secret_name = ? -LIMIT 1; -` - -const teamSecretDeleteStmt = ` -DELETE FROM team_secrets -WHERE team_secret_id = ? -` diff --git a/store/datastore/team_secret_test.go b/store/datastore/team_secret_test.go deleted file mode 100644 index 5ac640b6b..000000000 --- a/store/datastore/team_secret_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package datastore - -import ( - "testing" - - "github.com/drone/drone/model" - "github.com/franela/goblin" -) - -func TestTeamSecrets(t *testing.T) { - db := openTest() - defer db.Close() - - s := From(db) - g := goblin.Goblin(t) - g.Describe("TeamSecrets", func() { - - // before each test be sure to purge the package - // table data from the database. - g.BeforeEach(func() { - db.Exec(rebind("DELETE FROM team_secrets")) - }) - - g.It("Should set and get a secret", func() { - secret := &model.TeamSecret{ - Key: "octocat", - Name: "foo", - Value: "bar", - Images: []string{"docker", "gcr"}, - Events: []string{"push", "tag"}, - SkipVerify: true, - Conceal: true, - } - err := s.SetTeamSecret(secret) - g.Assert(err == nil).IsTrue() - g.Assert(secret.ID != 0).IsTrue() - - got, err := s.GetTeamSecret("octocat", secret.Name) - g.Assert(err == nil).IsTrue() - g.Assert(got.Name).Equal(secret.Name) - g.Assert(got.Value).Equal(secret.Value) - g.Assert(got.Images).Equal(secret.Images) - g.Assert(got.Events).Equal(secret.Events) - g.Assert(got.SkipVerify).Equal(secret.SkipVerify) - g.Assert(got.Conceal).Equal(secret.Conceal) - }) - - g.It("Should update a secret", func() { - secret := &model.TeamSecret{ - Key: "octocat", - Name: "foo", - Value: "bar", - } - s.SetTeamSecret(secret) - secret.Value = "baz" - s.SetTeamSecret(secret) - - got, err := s.GetTeamSecret("octocat", secret.Name) - g.Assert(err == nil).IsTrue() - g.Assert(got.Name).Equal(secret.Name) - g.Assert(got.Value).Equal(secret.Value) - }) - - g.It("Should list secrets", func() { - s.SetTeamSecret(&model.TeamSecret{ - Key: "octocat", - Name: "foo", - Value: "bar", - }) - s.SetTeamSecret(&model.TeamSecret{ - Key: "octocat", - Name: "bar", - Value: "baz", - }) - secrets, err := s.GetTeamSecretList("octocat") - g.Assert(err == nil).IsTrue() - g.Assert(len(secrets)).Equal(2) - }) - - g.It("Should delete a secret", func() { - secret := &model.TeamSecret{ - Key: "octocat", - Name: "foo", - Value: "bar", - } - s.SetTeamSecret(secret) - - _, err := s.GetTeamSecret("octocat", secret.Name) - g.Assert(err == nil).IsTrue() - - err = s.DeleteTeamSecret(secret) - g.Assert(err == nil).IsTrue() - - _, err = s.GetTeamSecret("octocat", secret.Name) - g.Assert(err != nil).IsTrue("expect a no rows in result set error") - }) - }) -} diff --git a/store/store.go b/store/store.go index 03461c10a..a19deceb7 100644 --- a/store/store.go +++ b/store/store.go @@ -58,30 +58,6 @@ type Store interface { // DeleteRepo deletes a user repository. DeleteRepo(*model.Repo) error - // GetSecretList gets a list of repository secrets - GetSecretList(*model.Repo) ([]*model.RepoSecret, error) - - // GetSecret gets the named repository secret. - GetSecret(*model.Repo, string) (*model.RepoSecret, error) - - // SetSecret sets the named repository secret. - SetSecret(*model.RepoSecret) error - - // DeleteSecret deletes the named repository secret. - DeleteSecret(*model.RepoSecret) error - - // GetTeamSecretList gets a list of team secrets - GetTeamSecretList(string) ([]*model.TeamSecret, error) - - // GetTeamSecret gets the named team secret. - GetTeamSecret(string, string) (*model.TeamSecret, error) - - // SetTeamSecret sets the named team secret. - SetTeamSecret(*model.TeamSecret) error - - // DeleteTeamSecret deletes the named team secret. - DeleteTeamSecret(*model.TeamSecret) error - // GetBuild gets a build by unique ID. GetBuild(int64) (*model.Build, error) @@ -112,38 +88,21 @@ type Store interface { // UpdateBuild updates a build. UpdateBuild(*model.Build) error - // // GetJob gets a job by unique ID. - // GetJob(int64) (*model.Job, error) // - // // GetJobNumber gets a job by number. - // GetJobNumber(*model.Build, int) (*model.Job, error) + // new functions // - // // GetJobList gets a list of all users in the system. - // GetJobList(*model.Build) ([]*model.Job, error) - // - // // CreateJob creates a job. - // CreateJob(*model.Job) error - // - // // UpdateJob updates a job. - // UpdateJob(*model.Job) error - // - // // ReadLog reads the Job logs from the datastore. - // ReadLog(*model.Job) (io.ReadCloser, error) - // - // // WriteLog writes the job logs to the datastore. - // WriteLog(*model.Job, io.Reader) error - // GetAgent(int64) (*model.Agent, error) - // - // GetAgentAddr(string) (*model.Agent, error) - // - // GetAgentList() ([]*model.Agent, error) - // - // CreateAgent(*model.Agent) error - // - // UpdateAgent(*model.Agent) error - // - // DeleteAgent(*model.Agent) error + SenderFind(*model.Repo, string) (*model.Sender, error) + SenderList(*model.Repo) ([]*model.Sender, error) + SenderCreate(*model.Sender) error + SenderUpdate(*model.Sender) error + SenderDelete(*model.Sender) error + + SecretFind(*model.Repo, string) (*model.Secret, error) + SecretList(*model.Repo) ([]*model.Secret, error) + SecretCreate(*model.Secret) error + SecretUpdate(*model.Secret) error + SecretDelete(*model.Secret) error RegistryFind(*model.Repo, string) (*model.Registry, error) RegistryList(*model.Repo) ([]*model.Registry, error) @@ -168,8 +127,6 @@ type Store interface { FileCreate(*model.File, io.Reader) error } -const globalTeamName = "__global__" - // GetUser gets a user by unique ID. func GetUser(c context.Context, id int64) (*model.User, error) { return FromContext(c).GetUser(id) @@ -238,94 +195,6 @@ func DeleteRepo(c context.Context, repo *model.Repo) error { return FromContext(c).DeleteRepo(repo) } -func GetSecretList(c context.Context, r *model.Repo) ([]*model.RepoSecret, error) { - return FromContext(c).GetSecretList(r) -} - -func GetSecret(c context.Context, r *model.Repo, name string) (*model.RepoSecret, error) { - return FromContext(c).GetSecret(r, name) -} - -func SetSecret(c context.Context, s *model.RepoSecret) error { - return FromContext(c).SetSecret(s) -} - -func DeleteSecret(c context.Context, s *model.RepoSecret) error { - return FromContext(c).DeleteSecret(s) -} - -func GetTeamSecretList(c context.Context, team string) ([]*model.TeamSecret, error) { - return FromContext(c).GetTeamSecretList(team) -} - -func GetTeamSecret(c context.Context, team, name string) (*model.TeamSecret, error) { - return FromContext(c).GetTeamSecret(team, name) -} - -func SetTeamSecret(c context.Context, s *model.TeamSecret) error { - return FromContext(c).SetTeamSecret(s) -} - -func DeleteTeamSecret(c context.Context, s *model.TeamSecret) error { - return FromContext(c).DeleteTeamSecret(s) -} - -func GetGlobalSecretList(c context.Context) ([]*model.TeamSecret, error) { - return GetTeamSecretList(c, globalTeamName) -} - -func GetGlobalSecret(c context.Context, name string) (*model.TeamSecret, error) { - return GetTeamSecret(c, globalTeamName, name) -} - -func SetGlobalSecret(c context.Context, s *model.TeamSecret) error { - s.Key = globalTeamName - return SetTeamSecret(c, s) -} - -func DeleteGlobalSecret(c context.Context, s *model.TeamSecret) error { - s.Key = globalTeamName - return DeleteTeamSecret(c, s) -} - -func GetMergedSecretList(c context.Context, r *model.Repo) ([]*model.Secret, error) { - var ( - secrets []*model.Secret - ) - - globalSecs, err := GetGlobalSecretList(c) - - if err != nil { - return nil, err - } - - for _, secret := range globalSecs { - secrets = append(secrets, secret.Secret()) - } - - teamSecs, err := GetTeamSecretList(c, r.Owner) - - if err != nil { - return nil, err - } - - for _, secret := range teamSecs { - secrets = append(secrets, secret.Secret()) - } - - repoSecs, err := GetSecretList(c, r) - - if err != nil { - return nil, err - } - - for _, secret := range repoSecs { - secrets = append(secrets, secret.Secret()) - } - - return secrets, nil -} - func GetBuild(c context.Context, id int64) (*model.Build, error) { return FromContext(c).GetBuild(id) } @@ -365,55 +234,3 @@ func CreateBuild(c context.Context, build *model.Build, procs ...*model.Proc) er func UpdateBuild(c context.Context, build *model.Build) error { return FromContext(c).UpdateBuild(build) } - -// func GetJob(c context.Context, id int64) (*model.Job, error) { -// return FromContext(c).GetJob(id) -// } -// -// func GetJobNumber(c context.Context, build *model.Build, num int) (*model.Job, error) { -// return FromContext(c).GetJobNumber(build, num) -// } -// -// func GetJobList(c context.Context, build *model.Build) ([]*model.Job, error) { -// return FromContext(c).GetJobList(build) -// } -// -// func CreateJob(c context.Context, job *model.Job) error { -// return FromContext(c).CreateJob(job) -// } -// -// func UpdateJob(c context.Context, job *model.Job) error { -// return FromContext(c).UpdateJob(job) -// } -// -// func ReadLog(c context.Context, job *model.Job) (io.ReadCloser, error) { -// return FromContext(c).ReadLog(job) -// } -// -// func WriteLog(c context.Context, job *model.Job, r io.Reader) error { -// return FromContext(c).WriteLog(job, r) -// } - -// func GetAgent(c context.Context, id int64) (*model.Agent, error) { -// return FromContext(c).GetAgent(id) -// } -// -// func GetAgentAddr(c context.Context, addr string) (*model.Agent, error) { -// return FromContext(c).GetAgentAddr(addr) -// } -// -// func GetAgentList(c context.Context) ([]*model.Agent, error) { -// return FromContext(c).GetAgentList() -// } -// -// func CreateAgent(c context.Context, agent *model.Agent) error { -// return FromContext(c).CreateAgent(agent) -// } -// -// func UpdateAgent(c context.Context, agent *model.Agent) error { -// return FromContext(c).UpdateAgent(agent) -// } -// -// func DeleteAgent(c context.Context, agent *model.Agent) error { -// return FromContext(c).DeleteAgent(agent) -// }