Compare commits

...

1 commit

Author SHA1 Message Date
evanboyle 951e868622 prototype "headless" pulumi 2020-09-09 16:48:30 -07:00
5 changed files with 173 additions and 11 deletions

View file

@ -128,7 +128,8 @@ func newHostCmd() *cobra.Command {
operation := s.Update
if isPreview {
operation = s.Preview
opts.Display.JSONDisplay = true
// we've disabled JSON for a demo of autorun
//opts.Display.JSONDisplay = true
}
// Now perform the update. This will stay alive until the user cancels the host.

106
sdk/go/x/auto/autorun.go Normal file
View file

@ -0,0 +1,106 @@
package auto
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/pulumi/pulumi/sdk/v2/go/pulumi"
)
func AutoRun(pulumiFunc pulumi.RunFunc) {
ctx := context.Background()
w, err := NewLocalWorkspace(ctx, WorkDir(filepath.Join(".")))
if err != nil {
fmt.Fprintf(os.Stderr, "error: program failed: %v\n", err)
os.Exit(1)
}
w.SetProgram(pulumiFunc)
operation, stackName := parseArgs()
if stackName == "" {
sSummary, err := w.Stack(ctx)
if err != nil || sSummary == nil {
fmt.Fprintf(os.Stderr, "error: program failed: no stack selected: %v\n", err)
os.Exit(1)
}
stackName = sSummary.Name
}
stack, err := SelectStack(ctx, stackName, w)
if err != nil {
fmt.Fprintf(os.Stderr, "error: program failed: unable to select stack: %v\n", err)
os.Exit(1)
}
switch operation {
case up:
_, err = stack.Up(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "error: program failed: %v\n", err)
os.Exit(1)
}
break
case preview:
_, err = stack.Preview(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "error: program failed: %v\n", err)
os.Exit(1)
}
break
case destroy:
_, err = stack.Destroy(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "error: program failed: %v\n", err)
os.Exit(1)
}
case refresh:
_, err = stack.Refresh(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "error: program failed: %v\n", err)
os.Exit(1)
}
}
}
type op int
const (
up op = iota
preview
refresh
destroy
)
func parseArgs() (op, string) {
operation := up
stack := ""
for _, a := range os.Args[1:] {
stackParts := strings.Split(a, "/")
if len(stackParts) == 3 {
stack = a
continue
}
switch a {
case "up", "update":
operation = up
continue
case "pre", "preview":
operation = preview
continue
case "refresh":
operation = refresh
continue
case "destroy":
operation = destroy
}
}
return operation, stack
}

View file

@ -46,3 +46,27 @@ func runPulumiCommandSync(
}
return stdout.String(), stderr.String(), code, err
}
func runPulumiCommandToStdout(
ctx context.Context,
workdir string,
additionalEnv []string,
args ...string,
) (string, string, int, error) {
// all commands should be run in non-interactive mode.
// this causes commands to fail rather than prompting for input (and thus hanging indefinitely)
args = append(args, "--non-interactive")
cmd := exec.CommandContext(ctx, "pulumi", args...)
cmd.Dir = workdir
cmd.Env = append(os.Environ(), additionalEnv...)
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = os.Stdout
cmd.Stderr = &stderr
code := unknownErrorCode
err := cmd.Run()
if exitError, ok := err.(*exec.ExitError); ok {
code = exitError.ExitCode()
}
return stdout.String(), stderr.String(), code, err
}

View file

@ -389,13 +389,15 @@ func (l *LocalWorkspace) ListStacks(ctx context.Context) ([]StackSummary, error)
if err != nil {
return nil, errors.Wrap(err, "unable to unmarshal config value")
}
for _, s := range stacks {
for i := range stacks {
s := stacks[i]
nameParts := strings.Split(s.Name, "/")
if len(nameParts) == 1 {
s.Name = fmt.Sprintf("%s/%s/%s", user, proj.Name.String(), s.Name)
} else {
s.Name = fmt.Sprintf("%s/%s/%s", nameParts[0], proj.Name.String(), nameParts[1])
}
stacks[i] = s
}
return stacks, nil
}

View file

@ -213,18 +213,19 @@ func (s *Stack) Preview(ctx context.Context, opts ...optpreview.Option) (Preview
return res, newAutoError(errors.Wrap(err, "failed to run preview"), stdout, stderr, code)
}
} else {
args := []string{"preview", "--json", fmt.Sprintf("--exec-kind=%s", constant.ExecKindAutoLocal)}
args := []string{"preview", fmt.Sprintf("--exec-kind=%s", constant.ExecKindAutoLocal)}
args = append(args, sharedArgs...)
stdout, stderr, code, err = s.runPulumiCmdSync(ctx, args...)
stdout, stderr, code, err = s.runPulumiCmdToStdOut(ctx, args...)
if err != nil {
return res, newAutoError(errors.Wrap(err, "failed to run preview"), stdout, stderr, code)
}
}
err = json.Unmarshal([]byte(stdout), &res)
if err != nil {
return res, errors.Wrap(err, "unable to unmarshal preview result")
}
// we're just hacking things together to pipe everything to stdout for autorun mode
// err = json.Unmarshal([]byte(stdout), &res)
// if err != nil {
// return res, errors.Wrap(err, "unable to unmarshal preview result")
// }
return res, nil
}
@ -342,7 +343,7 @@ func (s *Stack) Refresh(ctx context.Context, opts ...optrefresh.Option) (Refresh
}
args = append(args, fmt.Sprintf("--exec-kind=%s", execKind))
stdout, stderr, code, err := s.runPulumiCmdSync(ctx, args...)
stdout, stderr, code, err := s.runPulumiCmdToStdOut(ctx, args...)
if err != nil {
return res, newAutoError(errors.Wrap(err, "failed to refresh stack"), stdout, stderr, code)
}
@ -399,7 +400,7 @@ func (s *Stack) Destroy(ctx context.Context, opts ...optdestroy.Option) (Destroy
}
args = append(args, fmt.Sprintf("--exec-kind=%s", execKind))
stdout, stderr, code, err := s.runPulumiCmdSync(ctx, args...)
stdout, stderr, code, err := s.runPulumiCmdToStdOut(ctx, args...)
if err != nil {
return res, newAutoError(errors.Wrap(err, "failed to destroy stack"), stdout, stderr, code)
}
@ -628,6 +629,34 @@ type DestroyResult struct {
// secretSentinel represents the CLI response for an output marked as "secret"
const secretSentinel = "[secret]"
func (s *Stack) runPulumiCmdToStdOut(ctx context.Context, args ...string) (string, string, int, error) {
var env []string
if s.Workspace().PulumiHome() != "" {
homeEnv := fmt.Sprintf("%s=%s", pulumiHomeEnv, s.Workspace().PulumiHome())
env = append(env, homeEnv)
}
if envvars := s.Workspace().GetEnvVars(); envvars != nil {
for k, v := range envvars {
e := []string{k, v}
env = append(env, strings.Join(e, "="))
}
}
additionalArgs, err := s.Workspace().SerializeArgsForOp(ctx, s.Name())
if err != nil {
return "", "", -1, errors.Wrap(err, "failed to exec command, error getting additional args")
}
args = append(args, additionalArgs...)
stdout, stderr, errCode, err := runPulumiCommandToStdout(ctx, s.Workspace().WorkDir(), env, args...)
if err != nil {
return stdout, stderr, errCode, err
}
err = s.Workspace().PostCommandCallback(ctx, s.Name())
if err != nil {
return stdout, stderr, errCode, errors.Wrap(err, "command ran successfully, but error running PostCommandCallback")
}
return stdout, stderr, errCode, nil
}
func (s *Stack) runPulumiCmdSync(ctx context.Context, args ...string) (string, string, int, error) {
var env []string
if s.Workspace().PulumiHome() != "" {
@ -678,7 +707,7 @@ func (s *Stack) host(ctx context.Context, additionalArgs []string, parallel int)
cmd.Env = append(os.Environ(), homeEnv)
}
cmd.Stdout = &stdout
cmd.Stdout = os.Stdout
stderr, _ := cmd.StderrPipe()
err = cmd.Start()
if err != nil {