Merge pull request #1825 from donny-dont/feature/global_secrets
Implementing global secrets
This commit is contained in:
commit
58f84ced84
17 changed files with 423 additions and 216 deletions
|
@ -69,6 +69,15 @@ type Client interface {
|
||||||
// TeamSecretDel deletes a named team secret.
|
// TeamSecretDel deletes a named team secret.
|
||||||
TeamSecretDel(string, string) error
|
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 returns a repository build by number.
|
||||||
Build(string, string, int) (*model.Build, error)
|
Build(string, string, int) (*model.Build, error)
|
||||||
|
|
||||||
|
|
|
@ -24,28 +24,30 @@ const (
|
||||||
pathLogs = "%s/api/queue/logs/%d"
|
pathLogs = "%s/api/queue/logs/%d"
|
||||||
pathLogsAuth = "%s/api/queue/logs/%d?access_token=%s"
|
pathLogsAuth = "%s/api/queue/logs/%d?access_token=%s"
|
||||||
|
|
||||||
pathSelf = "%s/api/user"
|
pathSelf = "%s/api/user"
|
||||||
pathFeed = "%s/api/user/feed"
|
pathFeed = "%s/api/user/feed"
|
||||||
pathRepos = "%s/api/user/repos"
|
pathRepos = "%s/api/user/repos"
|
||||||
pathRepo = "%s/api/repos/%s/%s"
|
pathRepo = "%s/api/repos/%s/%s"
|
||||||
pathChown = "%s/api/repos/%s/%s/chown"
|
pathChown = "%s/api/repos/%s/%s/chown"
|
||||||
pathEncrypt = "%s/api/repos/%s/%s/encrypt"
|
pathEncrypt = "%s/api/repos/%s/%s/encrypt"
|
||||||
pathBuilds = "%s/api/repos/%s/%s/builds"
|
pathBuilds = "%s/api/repos/%s/%s/builds"
|
||||||
pathBuild = "%s/api/repos/%s/%s/builds/%v"
|
pathBuild = "%s/api/repos/%s/%s/builds/%v"
|
||||||
pathJob = "%s/api/repos/%s/%s/builds/%d/%d"
|
pathJob = "%s/api/repos/%s/%s/builds/%d/%d"
|
||||||
pathLog = "%s/api/repos/%s/%s/logs/%d/%d"
|
pathLog = "%s/api/repos/%s/%s/logs/%d/%d"
|
||||||
pathKey = "%s/api/repos/%s/%s/key"
|
pathKey = "%s/api/repos/%s/%s/key"
|
||||||
pathSign = "%s/api/repos/%s/%s/sign"
|
pathSign = "%s/api/repos/%s/%s/sign"
|
||||||
pathRepoSecrets = "%s/api/repos/%s/%s/secrets"
|
pathRepoSecrets = "%s/api/repos/%s/%s/secrets"
|
||||||
pathRepoSecret = "%s/api/repos/%s/%s/secrets/%s"
|
pathRepoSecret = "%s/api/repos/%s/%s/secrets/%s"
|
||||||
pathTeamSecrets = "%s/api/teams/%s/secrets"
|
pathTeamSecrets = "%s/api/teams/%s/secrets"
|
||||||
pathTeamSecret = "%s/api/teams/%s/secrets/%s"
|
pathTeamSecret = "%s/api/teams/%s/secrets/%s"
|
||||||
pathNodes = "%s/api/nodes"
|
pathGlobalSecrets = "%s/api/global/secrets"
|
||||||
pathNode = "%s/api/nodes/%d"
|
pathGlobalSecret = "%s/api/global/secrets/%s"
|
||||||
pathUsers = "%s/api/users"
|
pathNodes = "%s/api/nodes"
|
||||||
pathUser = "%s/api/users/%s"
|
pathNode = "%s/api/nodes/%d"
|
||||||
pathBuildQueue = "%s/api/builds"
|
pathUsers = "%s/api/users"
|
||||||
pathAgent = "%s/api/agents"
|
pathUser = "%s/api/users/%s"
|
||||||
|
pathBuildQueue = "%s/api/builds"
|
||||||
|
pathAgent = "%s/api/agents"
|
||||||
)
|
)
|
||||||
|
|
||||||
type client struct {
|
type client struct {
|
||||||
|
@ -280,7 +282,7 @@ func (c *client) SecretDel(owner, name, secret string) error {
|
||||||
return c.delete(uri)
|
return c.delete(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TeamSecretList returns a list of a repository secrets.
|
// TeamSecretList returns a list of organizational secrets.
|
||||||
func (c *client) TeamSecretList(team string) ([]*model.Secret, error) {
|
func (c *client) TeamSecretList(team string) ([]*model.Secret, error) {
|
||||||
var out []*model.Secret
|
var out []*model.Secret
|
||||||
uri := fmt.Sprintf(pathTeamSecrets, c.base, team)
|
uri := fmt.Sprintf(pathTeamSecrets, c.base, team)
|
||||||
|
@ -288,18 +290,38 @@ func (c *client) TeamSecretList(team string) ([]*model.Secret, error) {
|
||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TeamSecretPost create or updates a repository secret.
|
// TeamSecretPost create or updates a organizational secret.
|
||||||
func (c *client) TeamSecretPost(team string, secret *model.Secret) error {
|
func (c *client) TeamSecretPost(team string, secret *model.Secret) error {
|
||||||
uri := fmt.Sprintf(pathTeamSecrets, c.base, team)
|
uri := fmt.Sprintf(pathTeamSecrets, c.base, team)
|
||||||
return c.post(uri, secret, nil)
|
return c.post(uri, secret, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TeamSecretDel deletes a named repository secret.
|
// TeamSecretDel deletes a named orgainization secret.
|
||||||
func (c *client) TeamSecretDel(team, secret string) error {
|
func (c *client) TeamSecretDel(team, secret string) error {
|
||||||
uri := fmt.Sprintf(pathTeamSecret, c.base, team, secret)
|
uri := fmt.Sprintf(pathTeamSecret, c.base, team, secret)
|
||||||
return c.delete(uri)
|
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.
|
// Sign returns a cryptographic signature for the input string.
|
||||||
func (c *client) Sign(owner, name string, in []byte) ([]byte, error) {
|
func (c *client) Sign(owner, name string, in []byte) ([]byte, error) {
|
||||||
uri := fmt.Sprintf(pathSign, c.base, owner, name)
|
uri := fmt.Sprintf(pathSign, c.base, owner, name)
|
||||||
|
|
11
drone/global.go
Normal file
11
drone/global.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/codegangsta/cli"
|
||||||
|
|
||||||
|
var globalCmd = cli.Command{
|
||||||
|
Name: "global",
|
||||||
|
Usage: "manage global state",
|
||||||
|
Subcommands: []cli.Command{
|
||||||
|
globalSecretCmd,
|
||||||
|
},
|
||||||
|
}
|
13
drone/global_secret.go
Normal file
13
drone/global_secret.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/codegangsta/cli"
|
||||||
|
|
||||||
|
var globalSecretCmd = cli.Command{
|
||||||
|
Name: "secret",
|
||||||
|
Usage: "manage secrets",
|
||||||
|
Subcommands: []cli.Command{
|
||||||
|
globalSecretAddCmd,
|
||||||
|
globalSecretRemoveCmd,
|
||||||
|
globalSecretListCmd,
|
||||||
|
},
|
||||||
|
}
|
41
drone/global_secret_add.go
Normal file
41
drone/global_secret_add.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var globalSecretAddCmd = cli.Command{
|
||||||
|
Name: "add",
|
||||||
|
Usage: "adds a secret",
|
||||||
|
ArgsUsage: "[key] [value]",
|
||||||
|
Action: func(c *cli.Context) {
|
||||||
|
if err := globalSecretAdd(c); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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)
|
||||||
|
}
|
1
drone/global_secret_info.go
Normal file
1
drone/global_secret_info.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package main
|
33
drone/global_secret_list.go
Normal file
33
drone/global_secret_list.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var globalSecretListCmd = cli.Command{
|
||||||
|
Name: "ls",
|
||||||
|
Usage: "list all secrets",
|
||||||
|
Action: func(c *cli.Context) {
|
||||||
|
if err := globalSecretList(c); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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)
|
||||||
|
}
|
33
drone/global_secret_rm.go
Normal file
33
drone/global_secret_rm.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var globalSecretRemoveCmd = cli.Command{
|
||||||
|
Name: "rm",
|
||||||
|
Usage: "remove a secret",
|
||||||
|
Action: func(c *cli.Context) {
|
||||||
|
if err := globalSecretRemove(c); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
|
@ -43,6 +43,7 @@ func main() {
|
||||||
repoCmd,
|
repoCmd,
|
||||||
userCmd,
|
userCmd,
|
||||||
orgCmd,
|
orgCmd,
|
||||||
|
globalCmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Run(os.Args)
|
app.Run(os.Args)
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/drone/drone/model"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var orgSecretAddCmd = cli.Command{
|
var orgSecretAddCmd = cli.Command{
|
||||||
|
@ -19,26 +15,7 @@ var orgSecretAddCmd = cli.Command{
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Flags: []cli.Flag{
|
Flags: secretAddFlags(),
|
||||||
cli.StringSliceFlag{
|
|
||||||
Name: "event",
|
|
||||||
Usage: "inject the secret for these event types",
|
|
||||||
Value: &cli.StringSlice{
|
|
||||||
model.EventPush,
|
|
||||||
model.EventTag,
|
|
||||||
model.EventDeploy,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func orgSecretAdd(c *cli.Context) error {
|
func orgSecretAdd(c *cli.Context) error {
|
||||||
|
@ -51,27 +28,9 @@ func orgSecretAdd(c *cli.Context) error {
|
||||||
name := c.Args().Get(1)
|
name := c.Args().Get(1)
|
||||||
value := c.Args().Get(2)
|
value := c.Args().Get(2)
|
||||||
|
|
||||||
secret := &model.Secret{}
|
secret, err := secretParseCmd(name, value, c)
|
||||||
secret.Name = name
|
if err != nil {
|
||||||
secret.Value = value
|
return err
|
||||||
secret.Images = c.StringSlice("image")
|
|
||||||
secret.Events = c.StringSlice("event")
|
|
||||||
|
|
||||||
if len(secret.Images) == 0 {
|
|
||||||
return fmt.Errorf("Please specify the --image parameter")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 ferr
|
|
||||||
}
|
|
||||||
secret.Value = string(out)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := newClient(c)
|
client, err := newClient(c)
|
||||||
|
|
|
@ -2,9 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
)
|
)
|
||||||
|
@ -17,21 +14,7 @@ var orgSecretListCmd = cli.Command{
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Flags: []cli.Flag{
|
Flags: secretListFlags(),
|
||||||
cli.StringFlag{
|
|
||||||
Name: "format",
|
|
||||||
Usage: "format output",
|
|
||||||
Value: tmplOrgSecretList,
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "image",
|
|
||||||
Usage: "filter by image",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "event",
|
|
||||||
Usage: "filter by event",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func orgSecretList(c *cli.Context) error {
|
func orgSecretList(c *cli.Context) error {
|
||||||
|
@ -53,35 +36,5 @@ func orgSecretList(c *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl, err := template.New("_").Funcs(orgSecretFuncMap).Parse(c.String("format") + "\n")
|
return secretDisplayList(secrets, c)
|
||||||
|
|
||||||
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 tmplOrgSecretList = "\x1b[33m{{ .Name }} \x1b[0m" + `
|
|
||||||
Images: {{ list .Images }}
|
|
||||||
Events: {{ list .Events }}
|
|
||||||
`
|
|
||||||
|
|
||||||
var orgSecretFuncMap = template.FuncMap{
|
|
||||||
"list": func(s []string) string {
|
|
||||||
return strings.Join(s, ", ")
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
120
drone/secret.go
120
drone/secret.go
|
@ -1,6 +1,15 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/codegangsta/cli"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/drone/drone/model"
|
||||||
|
)
|
||||||
|
|
||||||
var secretCmd = cli.Command{
|
var secretCmd = cli.Command{
|
||||||
Name: "secret",
|
Name: "secret",
|
||||||
|
@ -11,3 +20,112 @@ var secretCmd = cli.Command{
|
||||||
secretListCmd,
|
secretListCmd,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func secretAddFlags() []cli.Flag {
|
||||||
|
return []cli.Flag{
|
||||||
|
cli.StringSliceFlag{
|
||||||
|
Name: "event",
|
||||||
|
Usage: "inject the secret for these event types",
|
||||||
|
Value: &cli.StringSlice{
|
||||||
|
model.EventPush,
|
||||||
|
model.EventTag,
|
||||||
|
model.EventDeploy,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
if len(secret.Images) == 0 {
|
||||||
|
return nil, fmt.Errorf("Please specify the --image parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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" + `
|
||||||
|
Images: {{ list .Images }}
|
||||||
|
Events: {{ list .Events }}
|
||||||
|
SkipVerify: {{ .SkipVerify }}
|
||||||
|
`
|
||||||
|
|
||||||
|
var secretFuncMap = template.FuncMap{
|
||||||
|
"list": func(s []string) string {
|
||||||
|
return strings.Join(s, ", ")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/drone/drone/model"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var secretAddCmd = cli.Command{
|
var secretAddCmd = cli.Command{
|
||||||
|
@ -19,26 +15,7 @@ var secretAddCmd = cli.Command{
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Flags: []cli.Flag{
|
Flags: secretAddFlags(),
|
||||||
cli.StringSliceFlag{
|
|
||||||
Name: "event",
|
|
||||||
Usage: "inject the secret for these event types",
|
|
||||||
Value: &cli.StringSlice{
|
|
||||||
model.EventPush,
|
|
||||||
model.EventTag,
|
|
||||||
model.EventDeploy,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func secretAdd(c *cli.Context) error {
|
func secretAdd(c *cli.Context) error {
|
||||||
|
@ -54,27 +31,9 @@ func secretAdd(c *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
secret := &model.Secret{}
|
secret, err := secretParseCmd(tail[0], tail[1], c)
|
||||||
secret.Name = tail[0]
|
if err != nil {
|
||||||
secret.Value = tail[1]
|
return err
|
||||||
secret.Images = c.StringSlice("image")
|
|
||||||
secret.Events = c.StringSlice("event")
|
|
||||||
|
|
||||||
if len(secret.Images) == 0 {
|
|
||||||
return fmt.Errorf("Please specify the --image parameter")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 ferr
|
|
||||||
}
|
|
||||||
secret.Value = string(out)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := newClient(c)
|
client, err := newClient(c)
|
||||||
|
|
|
@ -2,9 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
)
|
)
|
||||||
|
@ -17,21 +14,7 @@ var secretListCmd = cli.Command{
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Flags: []cli.Flag{
|
Flags: secretListFlags(),
|
||||||
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 secretList(c *cli.Context) error {
|
func secretList(c *cli.Context) error {
|
||||||
|
@ -53,35 +36,5 @@ func secretList(c *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(c.String("format") + "\n")
|
return secretDisplayList(secrets, c)
|
||||||
|
|
||||||
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" + `
|
|
||||||
Images: {{ list .Images }}
|
|
||||||
Events: {{ list .Events }}
|
|
||||||
`
|
|
||||||
|
|
||||||
var secretFuncMap = template.FuncMap{
|
|
||||||
"list": func(s []string) string {
|
|
||||||
return strings.Join(s, ", ")
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,15 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 := e.Group("/api/repos/:owner/:name")
|
||||||
{
|
{
|
||||||
repos.POST("", server.PostRepo)
|
repos.POST("", server.PostRepo)
|
||||||
|
|
62
server/global_secret.go
Normal file
62
server/global_secret.go
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/drone/drone/model"
|
||||||
|
"github.com/drone/drone/store"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetGlobalSecrets(c *gin.Context) {
|
||||||
|
secrets, err := store.GetGlobalSecretList(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
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, "")
|
||||||
|
}
|
|
@ -146,6 +146,8 @@ type Store interface {
|
||||||
DeleteAgent(*model.Agent) error
|
DeleteAgent(*model.Agent) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const globalTeamName = "__global__"
|
||||||
|
|
||||||
// GetUser gets a user by unique ID.
|
// GetUser gets a user by unique ID.
|
||||||
func GetUser(c context.Context, id int64) (*model.User, error) {
|
func GetUser(c context.Context, id int64) (*model.User, error) {
|
||||||
return FromContext(c).GetUser(id)
|
return FromContext(c).GetUser(id)
|
||||||
|
@ -246,12 +248,30 @@ func DeleteTeamSecret(c context.Context, s *model.TeamSecret) error {
|
||||||
return FromContext(c).DeleteTeamSecret(s)
|
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) {
|
func GetMergedSecretList(c context.Context, r *model.Repo) ([]*model.Secret, error) {
|
||||||
var (
|
var (
|
||||||
secrets []*model.Secret
|
secrets []*model.Secret
|
||||||
)
|
)
|
||||||
|
|
||||||
repoSecs, err := FromContext(c).GetSecretList(r)
|
repoSecs, err := GetSecretList(c, r)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -261,7 +281,7 @@ func GetMergedSecretList(c context.Context, r *model.Repo) ([]*model.Secret, err
|
||||||
secrets = append(secrets, secret.Secret())
|
secrets = append(secrets, secret.Secret())
|
||||||
}
|
}
|
||||||
|
|
||||||
teamSecs, err := FromContext(c).GetTeamSecretList(r.Owner)
|
teamSecs, err := GetTeamSecretList(c, r.Owner)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -271,6 +291,16 @@ func GetMergedSecretList(c context.Context, r *model.Repo) ([]*model.Secret, err
|
||||||
secrets = append(secrets, secret.Secret())
|
secrets = append(secrets, secret.Secret())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
globalSecs, err := GetGlobalSecretList(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, secret := range globalSecs {
|
||||||
|
secrets = append(secrets, secret.Secret())
|
||||||
|
}
|
||||||
|
|
||||||
return secrets, nil
|
return secrets, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue