2020-07-07 23:25:11 +02:00
|
|
|
package auto
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"os/exec"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (s *Stack) Up() (UpResult, error) {
|
2020-07-10 07:30:52 +02:00
|
|
|
var upResult UpResult
|
|
|
|
|
2020-07-07 23:25:11 +02:00
|
|
|
// TODO setup - merge pulumi.yaml, set config, etc.
|
2020-07-10 07:30:52 +02:00
|
|
|
res, err := s.initOrSelectStack()
|
|
|
|
if err != nil {
|
|
|
|
return res, errors.Wrap(err, "could not initialize or select stack")
|
|
|
|
}
|
2020-07-07 23:25:11 +02:00
|
|
|
|
2020-07-10 07:30:52 +02:00
|
|
|
stdout, stderr, err := s.runCmd("pulumi", "up", "--yes")
|
2020-07-07 23:25:11 +02:00
|
|
|
if err != nil {
|
2020-07-10 07:30:52 +02:00
|
|
|
return UpResult{
|
|
|
|
StdErr: stderr,
|
|
|
|
StdOut: stdout,
|
|
|
|
}, errors.Wrapf(err, "stderr: %s", stderr)
|
2020-07-07 23:25:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
outs, secrets, err := s.getOutputs()
|
|
|
|
if err != nil {
|
|
|
|
return upResult, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO - last histroy item.
|
|
|
|
|
|
|
|
return UpResult{
|
2020-07-10 07:30:52 +02:00
|
|
|
StdOut: stdout,
|
|
|
|
StdErr: stderr,
|
2020-07-07 23:25:11 +02:00
|
|
|
Outputs: outs,
|
|
|
|
SecretOutputs: secrets,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type UpResult struct {
|
|
|
|
StdOut string
|
|
|
|
StdErr string
|
2020-07-10 07:30:52 +02:00
|
|
|
Outputs map[string]interface{}
|
|
|
|
SecretOutputs map[string]interface{}
|
2020-07-07 23:25:11 +02:00
|
|
|
Summary map[string]interface{}
|
|
|
|
}
|
|
|
|
|
2020-07-10 07:30:52 +02:00
|
|
|
func (s *Stack) initOrSelectStack() (UpResult, error) {
|
|
|
|
var upResult UpResult
|
|
|
|
|
|
|
|
_, _, err := s.runCmd("pulumi", "stack", "select", s.Name)
|
|
|
|
if err != nil {
|
|
|
|
initStdout, initStderr, err := s.runCmd("pulumi", "stack", "init", s.Name)
|
|
|
|
if err != nil {
|
|
|
|
return UpResult{
|
|
|
|
StdErr: initStderr,
|
|
|
|
StdOut: initStdout,
|
|
|
|
}, errors.Wrapf(err, "unable to select or init stack: %s", initStderr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now parse pulumi.yaml, pulumi.stack.yaml,
|
|
|
|
// merge with our model
|
|
|
|
// re-write
|
|
|
|
// pulumi config set, config set --secrets`
|
|
|
|
|
|
|
|
// TODO consider storing hash of the yaml files so we don't have to do this for every 'up'
|
|
|
|
return upResult, nil
|
|
|
|
}
|
|
|
|
|
2020-07-07 23:25:11 +02:00
|
|
|
const secretSentinel = "[secret]"
|
|
|
|
|
|
|
|
//getOutputs returns a set of plain outputs, secret outputs, and an error
|
2020-07-10 07:30:52 +02:00
|
|
|
func (s *Stack) getOutputs() (map[string]interface{}, map[string]interface{}, error) {
|
2020-07-07 23:25:11 +02:00
|
|
|
// standard outputs
|
2020-07-10 07:30:52 +02:00
|
|
|
outStdout, outStderr, err := s.runCmd("pulumi", "stack", "output", "--json")
|
2020-07-07 23:25:11 +02:00
|
|
|
if err != nil {
|
2020-07-10 07:30:52 +02:00
|
|
|
return nil, nil, errors.Wrapf(err, "could not get outputs: stderr: %s", outStderr)
|
2020-07-07 23:25:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// secret outputs
|
2020-07-10 07:30:52 +02:00
|
|
|
secretStdout, secretStderr, err := s.runCmd("pulumi", "stack", "output", "--json", "--show-secrets")
|
2020-07-07 23:25:11 +02:00
|
|
|
if err != nil {
|
2020-07-10 07:30:52 +02:00
|
|
|
return nil, nil, errors.Wrapf(err, "could not get secret outputs: stderr: %s", secretStderr)
|
2020-07-07 23:25:11 +02:00
|
|
|
}
|
|
|
|
|
2020-07-10 07:30:52 +02:00
|
|
|
var outputs map[string]interface{}
|
|
|
|
var secrets map[string]interface{}
|
2020-07-07 23:25:11 +02:00
|
|
|
|
2020-07-10 07:30:52 +02:00
|
|
|
if err = json.Unmarshal([]byte(outStdout), &outputs); err != nil {
|
|
|
|
return nil, nil, errors.Wrapf(err, "error unmarshalling outputs: %s", secretStderr)
|
2020-07-07 23:25:11 +02:00
|
|
|
}
|
|
|
|
|
2020-07-10 07:30:52 +02:00
|
|
|
if err = json.Unmarshal([]byte(secretStdout), &secrets); err != nil {
|
|
|
|
return nil, nil, errors.Wrapf(err, "error unmarshalling secret outputs: %s", secretStderr)
|
2020-07-07 23:25:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range outputs {
|
|
|
|
if v == secretSentinel {
|
|
|
|
delete(outputs, k)
|
|
|
|
} else {
|
|
|
|
delete(secrets, k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return outputs, secrets, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//summary - call history and get last item
|
|
|
|
|
|
|
|
// configure - write the pulumi.yaml, pulumi.<stack>.yaml, set config & secrets
|
2020-07-10 07:30:52 +02:00
|
|
|
|
|
|
|
// runCmd execs the given command with appropriate stack context
|
|
|
|
// returning stdout, stderr, and an error value
|
|
|
|
func (s *Stack) runCmd(name string, arg ...string) (string, string, error) {
|
|
|
|
cmd := exec.Command(name, arg...)
|
|
|
|
cmd.Dir = s.Project.SourcePath
|
|
|
|
var stdout bytes.Buffer
|
|
|
|
var stderr bytes.Buffer
|
|
|
|
cmd.Stdout = &stdout
|
|
|
|
cmd.Stderr = &stderr
|
|
|
|
err := cmd.Run()
|
|
|
|
return stdout.String(), stderr.String(), err
|
|
|
|
}
|