diff --git a/config/config.go b/config/config.go index dea4b75..30dd0c3 100644 --- a/config/config.go +++ b/config/config.go @@ -21,8 +21,17 @@ import ( // State represents a desired system state type State struct { - Targets task.Targets `json:"targets"` - Env map[string]string `json:"env"` + Targets task.Targets `json:"targets"` + AuthMethods []AuthMethod `json:"auths"` + Env map[string]string `json:"env"` +} + +// AuthMethod represents a method of authentication for a target +type AuthMethod struct { + Name string `json:"name"` // name of the auth method + Path string `json:"path"` // path within the secret store + UserKey string `json:"user_key"` // key for username + PassKey string `json:"pass_key"` // key for password } // ConfigFromDirectory searches a directory for configuration files and @@ -72,6 +81,7 @@ func (cb *configBuilder) construct(hostname string) (err error) { cb.vm.Run(`'use strict'; var STATE = { targets: [], + auths: [], env: {} }; @@ -90,6 +100,15 @@ function T(t) { function E(k, v) { STATE.env[k] = v } + +function A(a) { + if(a.name === undefined) { throw "auth name undefined"; } + if(a.path === undefined) { throw "auth path undefined"; } + if(a.user_key === undefined) { throw "auth user_key undefined"; } + if(a.pass_key === undefined) { throw "auth pass_key undefined"; } + + STATE.auths.push(a); +} `) cb.vm.Set("HOSTNAME", hostname) //nolint:errcheck diff --git a/go.mod b/go.mod index 1116440..10d7426 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/picostack/pico go 1.13 require ( - github.com/Southclaws/gitwatch v1.3.2 + github.com/Southclaws/gitwatch v1.3.3 github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/eapache/go-resiliency v1.2.0 github.com/frankban/quicktest v1.4.1 // indirect diff --git a/go.sum b/go.sum index 65687cb..f457427 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,8 @@ github.com/Southclaws/gitwatch v1.3.1 h1:4XtiujsnxHKSKze3Tb5sWwTdBxSVW/JLbK54ruJ github.com/Southclaws/gitwatch v1.3.1/go.mod h1:xCudUiwWxkDYZ69cEhlTwAKIzbG1OpnA/s/pjPIW6gU= github.com/Southclaws/gitwatch v1.3.2 h1:zmt571n8ItXgkRJPyCFtFjcymvsFOGcm7JnHNpFDP+8= github.com/Southclaws/gitwatch v1.3.2/go.mod h1:xCudUiwWxkDYZ69cEhlTwAKIzbG1OpnA/s/pjPIW6gU= +github.com/Southclaws/gitwatch v1.3.3 h1:w5AI9IcMEVqb6cPyDjM9tvOI4r26m4UHAl5BVEvgKac= +github.com/Southclaws/gitwatch v1.3.3/go.mod h1:xCudUiwWxkDYZ69cEhlTwAKIzbG1OpnA/s/pjPIW6gU= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= diff --git a/service/service.go b/service/service.go index 2db7c54..108291f 100644 --- a/service/service.go +++ b/service/service.go @@ -101,7 +101,7 @@ func Initialise(c Config) (app *App, err error) { app.config.Directory, app.bus, app.config.CheckInterval, - authMethod, + secretStore, ) return diff --git a/task/target.go b/task/target.go index 474321b..f36cd3b 100644 --- a/task/target.go +++ b/task/target.go @@ -48,6 +48,9 @@ type Target struct { // Whether or not to run `Command` on first run, useful if the command is `docker-compose up` InitialRun bool `json:"initial_run"` + + // Auth method to use from the auth store + Auth string `json:"auth"` } // Execute runs the target's command in the specified directory with the diff --git a/watcher/git.go b/watcher/git.go index 5ae001d..0da26a7 100644 --- a/watcher/git.go +++ b/watcher/git.go @@ -11,8 +11,10 @@ import ( "github.com/pkg/errors" "go.uber.org/zap" "gopkg.in/src-d/go-git.v4/plumbing/transport" + "gopkg.in/src-d/go-git.v4/plumbing/transport/http" "github.com/picostack/pico/config" + "github.com/picostack/pico/secret" "github.com/picostack/pico/task" ) @@ -24,7 +26,7 @@ type GitWatcher struct { directory string bus chan task.ExecutionTask checkInterval time.Duration - ssh transport.AuthMethod + secrets secret.Store targetsWatcher *gitwatch.Session state config.State @@ -42,13 +44,13 @@ func NewGitWatcher( directory string, bus chan task.ExecutionTask, checkInterval time.Duration, - ssh transport.AuthMethod, + secrets secret.Store, ) *GitWatcher { return &GitWatcher{ directory: directory, bus: bus, checkInterval: checkInterval, - ssh: ssh, + secrets: secrets, initialise: make(chan bool), newState: make(chan config.State, 16), @@ -161,11 +163,16 @@ func (w *GitWatcher) watchTargets() (err error) { if t.Branch != "" { dir = fmt.Sprintf("%s_%s", t.Name, t.Branch) } + auth, err := w.getAuthForTarget(t) + if err != nil { + return err + } zap.L().Debug("assigned target", zap.String("url", t.RepoURL), zap.String("directory", dir)) targetRepos[i] = gitwatch.Repository{ URL: t.RepoURL, Branch: t.Branch, Directory: dir, + Auth: auth, } } @@ -177,7 +184,7 @@ func (w *GitWatcher) watchTargets() (err error) { targetRepos, w.checkInterval, w.directory, - w.ssh, + nil, false) if err != nil { return errors.Wrap(err, "failed to watch targets") @@ -211,6 +218,31 @@ func (w *GitWatcher) handle(e gitwatch.Event) (err error) { return nil } +func (w GitWatcher) getAuthForTarget(t task.Target) (transport.AuthMethod, error) { + for _, a := range w.state.AuthMethods { + if a.Name == t.Auth { + s, err := w.secrets.GetSecretsForTarget(a.Path) + if err != nil { + return nil, err + } + username, ok := s[a.UserKey] + if !ok { + return nil, errors.Errorf("auth object 'user_key' did not point to a valid element in the specified secret at '%s'", a.Path) + } + password, ok := s[a.PassKey] + if !ok { + return nil, errors.Errorf("auth object 'pass_key' did not point to a valid element in the specified secret at '%s'", a.Path) + } + zap.L().Debug("using auth method for target", zap.String("name", a.Name)) + return &http.BasicAuth{ + Username: username, + Password: password, + }, nil + } + } + return nil, nil +} + func (w GitWatcher) executeTargets(targets []task.Target, shutdown bool) { zap.L().Debug("executing all targets", zap.Bool("shutdown", shutdown),