mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-01-24 16:18:42 +00:00
added code to manage secrets
This commit is contained in:
parent
b9037b9d7c
commit
f3709922b3
17 changed files with 435 additions and 24 deletions
23
api/build.go
23
api/build.go
|
@ -35,7 +35,7 @@ func init() {
|
||||||
}
|
}
|
||||||
droneSec = fmt.Sprintf("%s.sec", strings.TrimSuffix(droneYml, filepath.Ext(droneYml)))
|
droneSec = fmt.Sprintf("%s.sec", strings.TrimSuffix(droneYml, filepath.Ext(droneYml)))
|
||||||
if os.Getenv("CANARY") == "true" {
|
if os.Getenv("CANARY") == "true" {
|
||||||
droneSec = fmt.Sprintf("%s.sig", strings.TrimSuffix(droneYml, filepath.Ext(droneYml)))
|
droneSec = fmt.Sprintf("%s.sig", droneYml)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +291,10 @@ func PostBuild(c *gin.Context) {
|
||||||
// get the previous build so that we can send
|
// get the previous build so that we can send
|
||||||
// on status change notifications
|
// on status change notifications
|
||||||
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
||||||
secs, _ := store.GetSecretList(c, repo)
|
secs, err := store.GetSecretList(c, repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err)
|
||||||
|
}
|
||||||
|
|
||||||
// IMPORTANT. PLEASE READ
|
// IMPORTANT. PLEASE READ
|
||||||
//
|
//
|
||||||
|
@ -305,14 +308,24 @@ func PostBuild(c *gin.Context) {
|
||||||
var verified bool
|
var verified bool
|
||||||
|
|
||||||
signature, err := jose.ParseSigned(string(sec))
|
signature, err := jose.ParseSigned(string(sec))
|
||||||
if err == nil && len(sec) != 0 {
|
if err != nil {
|
||||||
|
log.Debugf("cannot parse .drone.yml.sig file. %s", err)
|
||||||
|
} else if len(sec) == 0 {
|
||||||
|
log.Debugf("cannot parse .drone.yml.sig file. empty file")
|
||||||
|
} else {
|
||||||
signed = true
|
signed = true
|
||||||
output, err := signature.Verify(repo.Hash)
|
output, err := signature.Verify([]byte(repo.Hash))
|
||||||
if err == nil && string(output) == string(raw) {
|
if err != nil {
|
||||||
|
log.Debugf("cannot verify .drone.yml.sig file. %s", err)
|
||||||
|
} else if string(output) != string(raw) {
|
||||||
|
log.Debugf("cannot verify .drone.yml.sig file. no match. %q <> %q", string(output), string(raw))
|
||||||
|
} else {
|
||||||
verified = true
|
verified = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf(".drone.yml is signed=%v and verified=%v", signed, verified)
|
||||||
|
|
||||||
bus.Publish(c, bus.NewBuildEvent(bus.Enqueued, repo, build))
|
bus.Publish(c, bus.NewBuildEvent(bus.Enqueued, repo, build))
|
||||||
for _, job := range jobs {
|
for _, job := range jobs {
|
||||||
queue.Publish(c, &queue.Work{
|
queue.Publish(c, &queue.Work{
|
||||||
|
|
|
@ -3,11 +3,21 @@ package client
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/drone/drone/model"
|
||||||
"github.com/drone/drone/queue"
|
"github.com/drone/drone/queue"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client is used to communicate with a Drone server.
|
// Client is used to communicate with a Drone server.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
|
// Sign returns a cryptographic signature for the input string.
|
||||||
|
Sign(string, string, []byte) ([]byte, 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
|
||||||
|
|
||||||
// Pull pulls work from the server queue.
|
// Pull pulls work from the server queue.
|
||||||
Pull(os, arch string) (*queue.Work, error)
|
Pull(os, arch string) (*queue.Work, error)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -22,6 +23,24 @@ const (
|
||||||
pathWait = "%s/api/queue/wait/%d"
|
pathWait = "%s/api/queue/wait/%d"
|
||||||
pathStream = "%s/api/queue/stream/%d"
|
pathStream = "%s/api/queue/stream/%d"
|
||||||
pathPush = "%s/api/queue/status/%d"
|
pathPush = "%s/api/queue/status/%d"
|
||||||
|
|
||||||
|
pathSelf = "%s/api/user"
|
||||||
|
pathFeed = "%s/api/user/feed"
|
||||||
|
pathRepos = "%s/api/user/repos"
|
||||||
|
pathRepo = "%s/api/repos/%s/%s"
|
||||||
|
pathEncrypt = "%s/api/repos/%s/%s/encrypt"
|
||||||
|
pathBuilds = "%s/api/repos/%s/%s/builds"
|
||||||
|
pathBuild = "%s/api/repos/%s/%s/builds/%v"
|
||||||
|
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"
|
||||||
|
pathSecrets = "%s/api/repos/%s/%s/secrets"
|
||||||
|
pathSecret = "%s/api/repos/%s/%s/secrets/%s"
|
||||||
|
pathNodes = "%s/api/nodes"
|
||||||
|
pathNode = "%s/api/nodes/%d"
|
||||||
|
pathUsers = "%s/api/users"
|
||||||
|
pathUser = "%s/api/users/%s"
|
||||||
)
|
)
|
||||||
|
|
||||||
type client struct {
|
type client struct {
|
||||||
|
@ -34,14 +53,50 @@ func NewClient(uri string) Client {
|
||||||
return &client{http.DefaultClient, uri}
|
return &client{http.DefaultClient, uri}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClientToken returns a client at the specified url that
|
// NewClientToken returns a client at the specified url that authenticates all
|
||||||
// authenticates all outbound requests with the given token.
|
// outbound requests with the given token.
|
||||||
func NewClientToken(uri, token string) Client {
|
func NewClientToken(uri, token string) Client {
|
||||||
config := new(oauth2.Config)
|
config := new(oauth2.Config)
|
||||||
auther := config.Client(oauth2.NoContext, &oauth2.Token{AccessToken: token})
|
auther := config.Client(oauth2.NoContext, &oauth2.Token{AccessToken: token})
|
||||||
return &client{auther, uri}
|
return &client{auther, uri}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewClientTokenTLS returns a client at the specified url that authenticates
|
||||||
|
// all outbound requests with the given token and tls.Config if provided.
|
||||||
|
func NewClientTokenTLS(uri, token string, c *tls.Config) Client {
|
||||||
|
config := new(oauth2.Config)
|
||||||
|
auther := config.Client(oauth2.NoContext, &oauth2.Token{AccessToken: token})
|
||||||
|
if c != nil {
|
||||||
|
if trans, ok := auther.Transport.(*oauth2.Transport); ok {
|
||||||
|
trans.Base = &http.Transport{TLSClientConfig: c}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &client{auther, uri}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretPost create or updates a repository secret.
|
||||||
|
func (c *client) SecretPost(owner, name string, secret *model.Secret) error {
|
||||||
|
uri := fmt.Sprintf(pathSecrets, 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(pathSecret, c.base, owner, name, 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)
|
||||||
|
}
|
||||||
|
|
||||||
// Pull pulls work from the server queue.
|
// Pull pulls work from the server queue.
|
||||||
func (c *client) Pull(os, arch string) (*queue.Work, error) {
|
func (c *client) Pull(os, arch string) (*queue.Work, error) {
|
||||||
out := new(queue.Work)
|
out := new(queue.Work)
|
||||||
|
|
61
client/http.go
Normal file
61
client/http.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// helper function to stream an http request
|
||||||
|
func stream(client *http.Client, rawurl, method string, in, out interface{}) (io.ReadCloser, error) {
|
||||||
|
uri, err := url.Parse(rawurl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 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 {
|
||||||
|
// nothing
|
||||||
|
} else if rw, ok := in.(io.ReadWriter); ok {
|
||||||
|
buf = rw
|
||||||
|
} else if b, ok := in.([]byte); ok {
|
||||||
|
buf = new(bytes.Buffer)
|
||||||
|
buf.Write(b)
|
||||||
|
} else {
|
||||||
|
buf = new(bytes.Buffer)
|
||||||
|
err := json.NewEncoder(buf).Encode(in)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// creates a new http request to bitbucket.
|
||||||
|
req, err := http.NewRequest(method, uri.String(), buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if in == nil {
|
||||||
|
// nothing
|
||||||
|
} else if _, ok := in.(io.ReadWriter); ok {
|
||||||
|
req.Header.Set("Content-Type", "plain/text")
|
||||||
|
} else {
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode > http.StatusPartialContent {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
out, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
return nil, fmt.Errorf(string(out))
|
||||||
|
}
|
||||||
|
return resp.Body, nil
|
||||||
|
}
|
|
@ -83,7 +83,16 @@ var AgentCmd = cli.Command{
|
||||||
EnvVar: "DRONE_PLUGIN_NETRC",
|
EnvVar: "DRONE_PLUGIN_NETRC",
|
||||||
Name: "netrc-plugin",
|
Name: "netrc-plugin",
|
||||||
Usage: "plugins that receive the netrc file",
|
Usage: "plugins that receive the netrc file",
|
||||||
Value: &cli.StringSlice{"git", "hg"},
|
Value: &cli.StringSlice{
|
||||||
|
"git",
|
||||||
|
"git:*",
|
||||||
|
"hg",
|
||||||
|
"hg:*",
|
||||||
|
"plugins/hg",
|
||||||
|
"plugins/hg:*",
|
||||||
|
"plugins/git",
|
||||||
|
"plugins/git:*",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
EnvVar: "DRONE_PLUGIN_PRIVILEGED",
|
EnvVar: "DRONE_PLUGIN_PRIVILEGED",
|
||||||
|
|
|
@ -66,25 +66,29 @@ func (r *pipeline) run() error {
|
||||||
secrets = append(secrets, &model.Secret{
|
secrets = append(secrets, &model.Secret{
|
||||||
Name: "DRONE_NETRC_USERNAME",
|
Name: "DRONE_NETRC_USERNAME",
|
||||||
Value: w.Netrc.Login,
|
Value: w.Netrc.Login,
|
||||||
Images: []string{"git", "hg"}, // TODO(bradrydzewski) use the command line parameters here
|
Images: r.config.netrc, // TODO(bradrydzewski) use the command line parameters here
|
||||||
Events: []string{model.EventDeploy, model.EventPull, model.EventPush, model.EventTag},
|
Events: []string{model.EventDeploy, model.EventPull, model.EventPush, model.EventTag},
|
||||||
})
|
})
|
||||||
secrets = append(secrets, &model.Secret{
|
secrets = append(secrets, &model.Secret{
|
||||||
Name: "DRONE_NETRC_PASSWORD",
|
Name: "DRONE_NETRC_PASSWORD",
|
||||||
Value: w.Netrc.Password,
|
Value: w.Netrc.Password,
|
||||||
Images: []string{"git", "hg"},
|
Images: r.config.netrc,
|
||||||
Events: []string{model.EventDeploy, model.EventPull, model.EventPush, model.EventTag},
|
Events: []string{model.EventDeploy, model.EventPull, model.EventPush, model.EventTag},
|
||||||
})
|
})
|
||||||
secrets = append(secrets, &model.Secret{
|
secrets = append(secrets, &model.Secret{
|
||||||
Name: "DRONE_NETRC_MACHINE",
|
Name: "DRONE_NETRC_MACHINE",
|
||||||
Value: w.Netrc.Machine,
|
Value: w.Netrc.Machine,
|
||||||
Images: []string{"git", "hg"},
|
Images: r.config.netrc,
|
||||||
Events: []string{model.EventDeploy, model.EventPull, model.EventPush, model.EventTag},
|
Events: []string{model.EventDeploy, model.EventPull, model.EventPush, model.EventTag},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, secret := range secrets {
|
||||||
|
fmt.Printf("SECRET %s %s\n", secret.Name, secret.Value)
|
||||||
|
}
|
||||||
|
|
||||||
trans := []compiler.Transform{
|
trans := []compiler.Transform{
|
||||||
builtin.NewCloneOp("plugins/git:latest", true),
|
builtin.NewCloneOp("git", true),
|
||||||
builtin.NewCacheOp(
|
builtin.NewCacheOp(
|
||||||
"plugins/cache:latest",
|
"plugins/cache:latest",
|
||||||
"/var/lib/drone/cache/"+w.Repo.FullName,
|
"/var/lib/drone/cache/"+w.Repo.FullName,
|
||||||
|
|
|
@ -19,10 +19,25 @@ func main2() {
|
||||||
app.Name = "drone"
|
app.Name = "drone"
|
||||||
app.Version = version.Version
|
app.Version = version.Version
|
||||||
app.Usage = "command line utility"
|
app.Usage = "command line utility"
|
||||||
|
app.Flags = []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "t, token",
|
||||||
|
Value: "",
|
||||||
|
Usage: "server auth token",
|
||||||
|
EnvVar: "DRONE_TOKEN",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "s, server",
|
||||||
|
Value: "",
|
||||||
|
Usage: "server location",
|
||||||
|
EnvVar: "DRONE_SERVER",
|
||||||
|
},
|
||||||
|
}
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
agent.AgentCmd,
|
agent.AgentCmd,
|
||||||
server.ServeCmd,
|
server.ServeCmd,
|
||||||
|
SignCmd,
|
||||||
|
SecretCmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Run(os.Args)
|
app.Run(os.Args)
|
||||||
|
|
104
drone/secret.go
Normal file
104
drone/secret.go
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/drone/drone/model"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SecretCmd is the exported command for managing secrets.
|
||||||
|
var SecretCmd = cli.Command{
|
||||||
|
Name: "secret",
|
||||||
|
Usage: "manage secrets",
|
||||||
|
Subcommands: []cli.Command{
|
||||||
|
// Secret Add
|
||||||
|
{
|
||||||
|
Name: "add",
|
||||||
|
Usage: "add a secret",
|
||||||
|
ArgsUsage: "[repo] [key] [value]",
|
||||||
|
UsageText: "foo",
|
||||||
|
Action: func(c *cli.Context) {
|
||||||
|
if err := secretAdd(c); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Flags: []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{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Secret Delete
|
||||||
|
{
|
||||||
|
Name: "rm",
|
||||||
|
Usage: "remove a secret",
|
||||||
|
Action: func(c *cli.Context) {
|
||||||
|
if err := secretDel(c); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func secretAdd(c *cli.Context) error {
|
||||||
|
|
||||||
|
repo := c.Args().First()
|
||||||
|
owner, name, err := parseRepo(repo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tail := c.Args().Tail()
|
||||||
|
if len(tail) != 2 {
|
||||||
|
cli.ShowSubcommandHelp(c)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := &model.Secret{}
|
||||||
|
secret.Name = tail[0]
|
||||||
|
secret.Value = tail[1]
|
||||||
|
secret.Images = c.StringSlice("image")
|
||||||
|
secret.Events = c.StringSlice("event")
|
||||||
|
|
||||||
|
if len(secret.Images) == 0 {
|
||||||
|
return fmt.Errorf("Please specify the --image parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := newClient(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.SecretPost(owner, name, secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func secretDel(c *cli.Context) error {
|
||||||
|
repo := c.Args().First()
|
||||||
|
owner, name, err := parseRepo(repo)
|
||||||
|
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)
|
||||||
|
}
|
56
drone/sign.go
Normal file
56
drone/sign.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SignCmd is the exported command for signing the yaml.
|
||||||
|
var SignCmd = cli.Command{
|
||||||
|
Name: "sign",
|
||||||
|
Usage: "creates a secure yaml file",
|
||||||
|
Action: func(c *cli.Context) {
|
||||||
|
if err := sign(c); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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)
|
||||||
|
}
|
53
drone/util.go
Normal file
53
drone/util.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/drone/drone/client"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/jackspirou/syscerts"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newClient(c *cli.Context) (client.Client, error) {
|
||||||
|
var token = c.GlobalString("token")
|
||||||
|
var server = c.GlobalString("server")
|
||||||
|
|
||||||
|
// if no server url is provided we can default
|
||||||
|
// to the hosted Drone service.
|
||||||
|
if len(server) == 0 {
|
||||||
|
return nil, fmt.Errorf("Error: you must provide the Drone server address.")
|
||||||
|
}
|
||||||
|
if len(token) == 0 {
|
||||||
|
return nil, fmt.Errorf("Error: you must provide your Drone access token.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to find system CA certs
|
||||||
|
certs := syscerts.SystemRootsPool()
|
||||||
|
tlsConfig := &tls.Config{RootCAs: certs}
|
||||||
|
|
||||||
|
// create the drone client with TLS options
|
||||||
|
return client.NewClientTokenTLS(server, token, tlsConfig), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRepo(str string) (user, repo string, err error) {
|
||||||
|
var parts = strings.Split(str, "/")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
err = fmt.Errorf("Error: Invalid or missing repository. eg octocat/hello-world.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user = parts[0]
|
||||||
|
repo = parts[1]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readInput(in string) ([]byte, error) {
|
||||||
|
if in == "-" {
|
||||||
|
return ioutil.ReadAll(os.Stdin)
|
||||||
|
}
|
||||||
|
return ioutil.ReadFile(in)
|
||||||
|
}
|
|
@ -79,7 +79,7 @@ func argsToEnv(from map[string]interface{}, to map[string]string) error {
|
||||||
} else {
|
} else {
|
||||||
out, err = json.YAMLToJSON(out)
|
out, err = json.YAMLToJSON(out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println(err.Error())
|
// return err TODO(bradrydzewski) unit test coverage for possible errors
|
||||||
}
|
}
|
||||||
to[k] = string(out)
|
to[k] = string(out)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,6 @@ type Work struct {
|
||||||
Netrc *model.Netrc `json:"netrc"`
|
Netrc *model.Netrc `json:"netrc"`
|
||||||
Keys *model.Key `json:"keys"`
|
Keys *model.Key `json:"keys"`
|
||||||
System *model.System `json:"system"`
|
System *model.System `json:"system"`
|
||||||
Secrets []*model.Secret `json:"secret"`
|
Secrets []*model.Secret `json:"secrets"`
|
||||||
User *model.User `json:"user"`
|
User *model.User `json:"user"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,10 +33,7 @@ func TestRepos(t *testing.T) {
|
||||||
err1 := s.CreateRepo(&repo)
|
err1 := s.CreateRepo(&repo)
|
||||||
err2 := s.UpdateRepo(&repo)
|
err2 := s.UpdateRepo(&repo)
|
||||||
getrepo, err3 := s.GetRepo(repo.ID)
|
getrepo, err3 := s.GetRepo(repo.ID)
|
||||||
if err3 != nil {
|
|
||||||
println("Get Repo Error")
|
|
||||||
println(err3.Error())
|
|
||||||
}
|
|
||||||
g.Assert(err1 == nil).IsTrue()
|
g.Assert(err1 == nil).IsTrue()
|
||||||
g.Assert(err2 == nil).IsTrue()
|
g.Assert(err2 == nil).IsTrue()
|
||||||
g.Assert(err3 == nil).IsTrue()
|
g.Assert(err3 == nil).IsTrue()
|
||||||
|
|
7
stream/reader_test.go
Normal file
7
stream/reader_test.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package stream
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TetsReader(t *testing.T) {
|
||||||
|
t.Skip() //TODO(bradrydzewski) implement reader tests
|
||||||
|
}
|
7
stream/stream_impl_test.go
Normal file
7
stream/stream_impl_test.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package stream
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TetsStream(t *testing.T) {
|
||||||
|
t.Skip() //TODO(bradrydzewski) implement stream tests
|
||||||
|
}
|
7
stream/writer_test.go
Normal file
7
stream/writer_test.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package stream
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TetsWriter(t *testing.T) {
|
||||||
|
t.Skip() //TODO(bradrydzewski) implement writer tests
|
||||||
|
}
|
23
web/hook.go
23
web/hook.go
|
@ -33,7 +33,7 @@ func init() {
|
||||||
}
|
}
|
||||||
droneSec = fmt.Sprintf("%s.sec", strings.TrimSuffix(droneYml, filepath.Ext(droneYml)))
|
droneSec = fmt.Sprintf("%s.sec", strings.TrimSuffix(droneYml, filepath.Ext(droneYml)))
|
||||||
if os.Getenv("CANARY") == "true" {
|
if os.Getenv("CANARY") == "true" {
|
||||||
droneSec = fmt.Sprintf("%s.sig", strings.TrimSuffix(droneYml, filepath.Ext(droneYml)))
|
droneSec = fmt.Sprintf("%s.sig", droneYml)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +209,10 @@ func PostHook(c *gin.Context) {
|
||||||
// get the previous build so that we can send
|
// get the previous build so that we can send
|
||||||
// on status change notifications
|
// on status change notifications
|
||||||
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
||||||
secs, _ := store.GetSecretList(c, repo)
|
secs, err := store.GetSecretList(c, repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error getting secrets for %s#%d. %s", repo.FullName, build.Number, err)
|
||||||
|
}
|
||||||
|
|
||||||
// IMPORTANT. PLEASE READ
|
// IMPORTANT. PLEASE READ
|
||||||
//
|
//
|
||||||
|
@ -223,14 +226,24 @@ func PostHook(c *gin.Context) {
|
||||||
var verified bool
|
var verified bool
|
||||||
|
|
||||||
signature, err := jose.ParseSigned(string(sec))
|
signature, err := jose.ParseSigned(string(sec))
|
||||||
if err == nil && len(sec) != 0 {
|
if err != nil {
|
||||||
|
log.Debugf("cannot parse .drone.yml.sig file. %s", err)
|
||||||
|
} else if len(sec) == 0 {
|
||||||
|
log.Debugf("cannot parse .drone.yml.sig file. empty file")
|
||||||
|
} else {
|
||||||
signed = true
|
signed = true
|
||||||
output, err := signature.Verify(repo.Hash)
|
output, err := signature.Verify([]byte(repo.Hash))
|
||||||
if err == nil && string(output) == string(raw) {
|
if err != nil {
|
||||||
|
log.Debugf("cannot verify .drone.yml.sig file. %s", err)
|
||||||
|
} else if string(output) != string(raw) {
|
||||||
|
log.Debugf("cannot verify .drone.yml.sig file. no match")
|
||||||
|
} else {
|
||||||
verified = true
|
verified = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf(".drone.yml is signed=%v and verified=%v", signed, verified)
|
||||||
|
|
||||||
bus.Publish(c, bus.NewBuildEvent(bus.Enqueued, repo, build))
|
bus.Publish(c, bus.NewBuildEvent(bus.Enqueued, repo, build))
|
||||||
for _, job := range jobs {
|
for _, job := range jobs {
|
||||||
queue.Publish(c, &queue.Work{
|
queue.Publish(c, &queue.Work{
|
||||||
|
|
Loading…
Reference in a new issue