pulumi/cmd/husk.go

896 lines
29 KiB
Go
Raw Normal View History

2017-02-25 16:25:33 +01:00
// Copyright 2016 Pulumi, Inc. All rights reserved.
package cmd
import (
"bufio"
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
"strconv"
"time"
"github.com/spf13/cobra"
2017-02-25 16:25:33 +01:00
"github.com/pulumi/coconut/pkg/compiler"
"github.com/pulumi/coconut/pkg/compiler/core"
"github.com/pulumi/coconut/pkg/compiler/errors"
"github.com/pulumi/coconut/pkg/compiler/symbols"
2017-02-25 16:25:33 +01:00
"github.com/pulumi/coconut/pkg/diag"
"github.com/pulumi/coconut/pkg/diag/colors"
"github.com/pulumi/coconut/pkg/encoding"
"github.com/pulumi/coconut/pkg/eval/heapstate"
"github.com/pulumi/coconut/pkg/eval/rt"
"github.com/pulumi/coconut/pkg/pack"
2017-02-25 16:25:33 +01:00
"github.com/pulumi/coconut/pkg/resource"
"github.com/pulumi/coconut/pkg/tokens"
"github.com/pulumi/coconut/pkg/util/cmdutil"
"github.com/pulumi/coconut/pkg/util/contract"
"github.com/pulumi/coconut/pkg/util/mapper"
"github.com/pulumi/coconut/pkg/workspace"
)
func newHuskCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "husk",
Short: "Manage or deploy into husks (deployment targets)",
Long: "Manage or deploy into husks (deployment targets)\n" +
"\n" +
"A husk is a named deployment target, and a single nut have many of them. Each husk\n" +
"has a deployment history associated with it, stored in the workspace, in addition to\n" +
"the last known good deployment. A husk may also have unique configuration entries.\n",
}
cmd.AddCommand(newHuskDeployCmd())
cmd.AddCommand(newHuskDestroyCmd())
cmd.AddCommand(newHuskInitCmd())
cmd.AddCommand(newHuskLsCmd())
cmd.AddCommand(newHuskRmCmd())
return cmd
}
func initHuskCmd(cmd *cobra.Command, args []string) *huskCmdInfo {
// Create a new context for the plan operations.
ctx := resource.NewContext(sink())
// Read in the name of the husk to use.
if len(args) == 0 {
exitError("missing required husk name")
}
// Read in the deployment information, bailing if an IO error occurs.
name := tokens.QName(args[0])
huskfile, husk, old := readHusk(ctx, name)
if husk == nil {
contract.Assert(!ctx.Diag.Success())
exitError("could not read huskfile required to proceed") // failure reading the husk information.
}
return &huskCmdInfo{
Ctx: ctx,
Husk: husk,
Huskfile: huskfile,
Old: old,
Args: args[1:],
Orig: args,
}
}
type huskCmdInfo struct {
Ctx *resource.Context // the resulting context
Husk *resource.Husk // the husk information
Huskfile *resource.Huskfile // the full serialized huskfile from which this came.
Old resource.Snapshot // the husk's latest deployment snapshot
Args []string // the rest of the args after extracting the husk name
Orig []string // the original args before extracting the husk name
}
func confirmPrompt(msg string, args ...interface{}) bool {
prompt := fmt.Sprintf(msg, args...)
fmt.Printf(
colors.ColorizeText(fmt.Sprintf("%v%v%v\n", colors.SpecAttention, prompt, colors.Reset)))
fmt.Printf("Please confirm that this is what you'd like to do by typing (\"yes\"): ")
reader := bufio.NewReader(os.Stdin)
if line, _ := reader.ReadString('\n'); line != "yes\n" {
fmt.Fprintf(os.Stderr, "Confirmation declined -- exiting without doing anything\n")
return false
}
return true
}
// create just creates a new empty husk without deploying anything into it.
// TODO: add the ability to configure the husk at the command line.
func create(name tokens.QName) {
husk := &resource.Husk{Name: name}
if success := saveHusk(husk, nil, "", false); success {
fmt.Printf("Coconut husk '%v' initialized; ready for deployments (see `coco husk deploy`)\n", name)
}
}
// remove permanently deletes the husk's information from the local workstation.
func remove(husk *resource.Husk) {
deleteHusk(husk)
msg := fmt.Sprintf("%sCoconut husk '%s' has been removed!%s\n",
colors.SpecAttention, husk.Name, colors.Reset)
fmt.Printf(colors.ColorizeText(msg))
}
func prepareCompiler(cmd *cobra.Command, args []string) (compiler.Compiler, *pack.Package) {
// If there's a --, we need to separate out the command args from the stack args.
flags := cmd.Flags()
dashdash := flags.ArgsLenAtDash()
var packArgs []string
if dashdash != -1 {
packArgs = args[dashdash:]
args = args[0:dashdash]
}
// Create a compiler options object and map any flags and arguments to settings on it.
opts := core.DefaultOptions()
opts.Args = dashdashArgsToMap(packArgs)
// In the case of an argument, load that specific package and new up a compiler based on its base path.
// Otherwise, use the default workspace and package logic (which consults the current working directory).
var comp compiler.Compiler
var pkg *pack.Package
if len(args) == 0 {
var err error
comp, err = compiler.Newwd(opts)
if err != nil {
// Create a temporary diagnostics sink so that we can issue an error and bail out.
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
sink().Errorf(errors.ErrorCantCreateCompiler, err)
}
} else {
fn := args[0]
if pkg = cmdutil.ReadPackageFromArg(fn); pkg != nil {
var err error
if fn == "-" {
comp, err = compiler.Newwd(opts)
} else {
comp, err = compiler.New(filepath.Dir(fn), opts)
}
if err != nil {
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
sink().Errorf(errors.ErrorCantReadPackage, fn, err)
}
}
}
return comp, pkg
}
// compile just uses the standard logic to parse arguments, options, and to locate/compile a package. It returns the
// CocoGL graph that is produced, or nil if an error occurred (in which case, we would expect non-0 errors).
func compile(cmd *cobra.Command, args []string, config resource.ConfigMap) *compileResult {
// Prepare the compiler info and, provided it succeeds, perform the compilation.
if comp, pkg := prepareCompiler(cmd, args); comp != nil {
// Create the preexec hook if the config map is non-nil.
var preexec compiler.Preexec
configVars := make(map[tokens.Token]*rt.Object)
if config != nil {
preexec = config.ConfigApplier(configVars)
}
// Now perform the compilation and extract the heap snapshot.
var heap *heapstate.Heap
var pkgsym *symbols.Package
if pkg == nil {
pkgsym, heap = comp.Compile(preexec)
} else {
pkgsym, heap = comp.CompilePackage(pkg, preexec)
}
return &compileResult{
C: comp,
Pkg: pkgsym,
Heap: heap,
ConfigVars: configVars,
}
}
return nil
}
type compileResult struct {
C compiler.Compiler
Pkg *symbols.Package
Heap *heapstate.Heap
ConfigVars map[tokens.Token]*rt.Object
}
// verify creates a compiler, much like compile, but only performs binding and verification on it. If verification
// succeeds, the return value is true; if verification fails, errors will have been output, and the return is false.
func verify(cmd *cobra.Command, args []string) bool {
// Prepare the compiler info and, provided it succeeds, perform the verification.
if comp, pkg := prepareCompiler(cmd, args); comp != nil {
// Now perform the compilation and extract the heap snapshot.
if pkg == nil {
return comp.Verify()
}
return comp.VerifyPackage(pkg)
}
return false
}
// plan just uses the standard logic to parse arguments, options, and to create a snapshot and plan.
func plan(cmd *cobra.Command, info *huskCmdInfo, delete bool) *planResult {
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
// If deleting, there is no need to create a new snapshot; otherwise, we will need to compile the package.
var new resource.Snapshot
var result *compileResult
if !delete {
// First, compile; if that yields errors or an empty heap, exit early.
if result = compile(cmd, info.Args, info.Husk.Config); result == nil || result.Heap == nil {
return nil
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
// Create a resource snapshot from the compiled/evaluated object graph.
var err error
new, err = resource.NewGraphSnapshot(
info.Ctx, info.Husk.Name, result.Pkg.Tok, result.C.Ctx().Opts.Args, result.Heap, info.Old)
if err != nil {
result.C.Diag().Errorf(errors.ErrorCantCreateSnapshot, err)
return nil
} else if !info.Ctx.Diag.Success() {
return nil
}
}
// Generate a plan; this API handles all interesting cases (create, update, delete).
plan, err := resource.NewPlan(info.Ctx, info.Old, new)
if err != nil {
result.C.Diag().Errorf(errors.ErrorCantCreateSnapshot, err)
return nil
}
if !info.Ctx.Diag.Success() {
return nil
}
return &planResult{
compileResult: result,
Info: info,
New: new,
Plan: plan,
}
}
type planResult struct {
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
*compileResult
Info *huskCmdInfo // plan command information.
Old resource.Snapshot // the existing snapshot (if any).
New resource.Snapshot // the new snapshot for this plan (if any).
Plan resource.Plan // the plan created by this command.
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
func apply(cmd *cobra.Command, info *huskCmdInfo, opts applyOptions) {
if result := plan(cmd, info, opts.Delete); result != nil {
// Now based on whether a dry run was specified, or not, either print or perform the planned operations.
if opts.DryRun {
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
// If no output file was requested, or "-", print to stdout; else write to that file.
if opts.Output == "" || opts.Output == "-" {
printPlan(info.Ctx.Diag, result, opts)
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
} else {
saveHusk(info.Husk, result.New, opts.Output, true /*overwrite*/)
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
} else {
// If show unchanged was requested, print them first, along with a header.
var header bytes.Buffer
printPrelude(&header, result, opts)
header.WriteString(fmt.Sprintf("%vDeploying changes:%v\n", colors.SpecUnimportant, colors.Reset))
fmt.Printf(colors.Colorize(&header))
// Print a nice message if the update is an empty one.
empty := checkEmpty(info.Ctx.Diag, result.Plan)
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
// Create an object to track progress and perform the actual operations.
start := time.Now()
progress := newProgress(info.Ctx, opts.Summary)
checkpoint, err, _, _ := result.Plan.Apply(progress)
if err != nil {
contract.Assert(!info.Ctx.Diag.Success()) // an error should have been emitted.
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
var summary bytes.Buffer
if !empty {
// Print out the total number of steps performed (and their kinds), the duration, and any summary info.
printSummary(&summary, progress.Ops, opts.ShowReplaceSteps, false)
summary.WriteString(fmt.Sprintf("%vDeployment duration: %v%v\n",
colors.SpecUnimportant, time.Since(start), colors.Reset))
}
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
if progress.MaybeCorrupt {
summary.WriteString(fmt.Sprintf(
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
"%vfatal: A catastrophic error occurred; resources states may be unknown%v\n",
colors.SpecAttention, colors.Reset))
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
// Now save the updated snapshot to the specified output file, if any, or the standard location otherwise.
// Note that if a failure has occurred, the Apply routine above will have returned a safe checkpoint.
husk := result.Info.Husk
saveHusk(husk, checkpoint, opts.Output, true /*overwrite*/)
fmt.Printf(colors.Colorize(&summary))
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
}
}
func checkEmpty(d diag.Sink, plan resource.Plan) bool {
// If we are doing an empty update, say so.
if plan.Empty() {
d.Infof(diag.Message("no resources need to be updated"))
return true
}
return false
}
// backupHusk makes a backup of an existing file, in preparation for writing a new one. Instead of a copy, it
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
// simply renames the file, which is simpler, more efficient, etc.
func backupHusk(file string) {
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
contract.Require(file != "", "file")
os.Rename(file, file+".bak") // ignore errors.
// TODO: consider multiple backups (.bak.bak.bak...etc).
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
// deleteHusk removes an existing snapshot file, leaving behind a backup.
func deleteHusk(husk *resource.Husk) {
contract.Require(husk != nil, "husk")
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
// Just make a backup of the file and don't write out anything new.
file := workspace.HuskPath(husk.Name)
backupHusk(file)
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
// readHusk reads in an existing snapshot file, issuing an error and returning nil if something goes awry.
func readHusk(ctx *resource.Context, name tokens.QName) (*resource.Huskfile, *resource.Husk, resource.Snapshot) {
contract.Require(name != "", "name")
file := workspace.HuskPath(name)
// Detect the encoding of the file so we can do our initial unmarshaling.
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
m, ext := encoding.Detect(file)
if m == nil {
ctx.Diag.Errorf(errors.ErrorIllegalMarkupExtension, ext)
return nil, nil, nil
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
2017-02-26 20:52:44 +01:00
// Now read the whole file into a byte blob.
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
b, err := ioutil.ReadFile(file)
if err != nil {
2017-02-26 20:52:44 +01:00
if os.IsNotExist(err) {
ctx.Diag.Errorf(errors.ErrorInvalidHuskName, name)
2017-02-26 20:52:44 +01:00
} else {
ctx.Diag.Errorf(errors.ErrorIO, err)
2017-02-26 20:52:44 +01:00
}
return nil, nil, nil
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
// Unmarshal the contents into a huskfile deployment structure.
var huskfile resource.Huskfile
if err = m.Unmarshal(b, &huskfile); err != nil {
ctx.Diag.Errorf(errors.ErrorCantReadDeployment, file, err)
return nil, nil, nil
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
// Next, use the mapping infrastructure to validate the contents.
// TODO: we can eliminate this redundant unmarshaling once Go supports strict unmarshaling.
var obj mapper.Object
if err = m.Unmarshal(b, &obj); err != nil {
ctx.Diag.Errorf(errors.ErrorCantReadDeployment, file, err)
return nil, nil, nil
} else {
if obj["latest"] != nil {
if latest, islatest := obj["latest"].(map[string]interface{}); islatest {
delete(latest, "resources") // remove the resources, since they require custom marshaling.
}
}
md := mapper.New(nil)
var ignore resource.Huskfile // just for errors.
if err = md.Decode(obj, &ignore); err != nil {
ctx.Diag.Errorf(errors.ErrorCantReadDeployment, file, err)
return nil, nil, nil
}
}
husk, snap := resource.DeserializeHuskfile(ctx, &huskfile)
contract.Assert(husk != nil)
return &huskfile, husk, snap
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
// saveHusk saves a new snapshot at the given location, backing up any existing ones.
func saveHusk(husk *resource.Husk, snap resource.Snapshot, file string, existok bool) bool {
contract.Require(husk != nil, "husk")
if file == "" {
file = workspace.HuskPath(husk.Name)
}
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
2017-02-25 16:25:33 +01:00
// Make a serializable CocoGL data structure and then use the encoder to encode it.
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
m, ext := encoding.Detect(file)
if m == nil {
sink().Errorf(errors.ErrorIllegalMarkupExtension, ext)
return false
}
if filepath.Ext(file) == "" {
file = file + ext
}
dep := resource.SerializeHuskfile(husk, snap, "")
b, err := m.Marshal(dep)
if err != nil {
sink().Errorf(errors.ErrorIO, err)
return false
}
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
// If it's not ok for the file to already exist, ensure that it doesn't.
if !existok {
if _, err := os.Stat(file); err == nil {
sink().Errorf(errors.ErrorIO, fmt.Errorf("file '%v' already exists", file))
return false
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
}
// Back up the existing file if it already exists.
backupHusk(file)
// Ensure the directory exists.
if err = os.MkdirAll(filepath.Dir(file), 0755); err != nil {
sink().Errorf(errors.ErrorIO, err)
return false
}
// And now write out the new snapshot file, overwriting that location.
if err = ioutil.WriteFile(file, b, 0644); err != nil {
sink().Errorf(errors.ErrorIO, err)
return false
}
return true
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
type applyOptions struct {
Create bool // true if we are creating resources.
Delete bool // true if we are deleting resources.
DryRun bool // true if we should just print the plan without performing it.
ShowConfig bool // true to show the configuration variables being used.
ShowReplaceSteps bool // true to show the replacement steps in the plan.
ShowUnchanged bool // true to show the resources that aren't updated, in addition to those that are.
Summary bool // true if we should only summarize resources and operations.
Output string // the place to store the output, if any.
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
// applyProgress pretty-prints the plan application process as it goes.
type applyProgress struct {
Ctx *resource.Context
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
Steps int
Ops map[resource.StepOp]int
MaybeCorrupt bool
Summary bool
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
func newProgress(ctx *resource.Context, summary bool) *applyProgress {
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
return &applyProgress{
Ctx: ctx,
Steps: 0,
Ops: make(map[resource.StepOp]int),
Summary: summary,
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
}
func (prog *applyProgress) Before(step resource.Step) {
// Print the step.
stepop := step.Op()
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
stepnum := prog.Steps + 1
var extra string
if stepop == resource.OpReplaceCreate || stepop == resource.OpReplaceDelete {
extra = " (part of a replacement step)"
}
var b bytes.Buffer
b.WriteString(fmt.Sprintf("Applying step #%v [%v]%v\n", stepnum, stepop, extra))
printStep(&b, step, prog.Summary, " ")
fmt.Printf(colors.Colorize(&b))
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
func (prog *applyProgress) After(step resource.Step, err error, state resource.ResourceState) {
if err == nil {
// Increment the counters.
prog.Steps++
prog.Ops[step.Op()]++
} else {
// Issue a true, bonafide error.
prog.Ctx.Diag.Errorf(errors.ErrorPlanApplyFailed, err)
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
// Print the state of the resource; we don't issue the error, because the apply above will do that.
var b bytes.Buffer
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
stepnum := prog.Steps + 1
2017-02-25 02:44:54 +01:00
b.WriteString(fmt.Sprintf("Step #%v failed [%v]: ", stepnum, step.Op()))
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
switch state {
case resource.StateOK:
b.WriteString(colors.SpecNote)
b.WriteString("provider successfully recovered from this failure")
case resource.StateUnknown:
b.WriteString(colors.SpecAttention)
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
b.WriteString("this failure was catastrophic and the provider cannot guarantee recovery")
prog.MaybeCorrupt = true
default:
contract.Failf("Unrecognized resource state: %v", state)
}
b.WriteString(colors.Reset)
b.WriteString("\n")
fmt.Printf(colors.Colorize(&b))
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
}
func printPlan(d diag.Sink, result *planResult, opts applyOptions) {
// First print config/unchanged/etc. if necessary.
var prelude bytes.Buffer
printPrelude(&prelude, result, opts)
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
// Now walk the plan's steps and and pretty-print them out.
prelude.WriteString(fmt.Sprintf("%vPlanned changes:%v\n", colors.SpecUnimportant, colors.Reset))
fmt.Printf(colors.Colorize(&prelude))
// Print a nice message if the update is an empty one.
if empty := checkEmpty(d, result.Plan); !empty {
var summary bytes.Buffer
step := result.Plan.Steps()
counts := make(map[resource.StepOp]int)
for step != nil {
op := step.Op()
// Print this step information (resource and all its properties).
// TODO: it would be nice if, in the output, we showed the dependencies a la `git log --graph`.
if opts.ShowReplaceSteps || (op != resource.OpReplaceCreate && op != resource.OpReplaceDelete) {
printStep(&summary, step, opts.Summary, "")
}
counts[step.Op()]++
step = step.Next()
}
// Print a summary of operation counts.
printSummary(&summary, counts, opts.ShowReplaceSteps, true)
fmt.Printf(colors.Colorize(&summary))
}
}
func printPrelude(b *bytes.Buffer, result *planResult, opts applyOptions) {
// If there are configuration variables, show them.
if opts.ShowConfig {
printConfig(b, result.compileResult)
}
// If show-sames was requested, walk the sames and print them.
if opts.ShowUnchanged {
printUnchanged(b, result.Plan, opts.Summary)
}
}
func printConfig(b *bytes.Buffer, result *compileResult) {
b.WriteString(fmt.Sprintf("%vConfiguration:%v\n", colors.SpecUnimportant, colors.Reset))
if result != nil && result.ConfigVars != nil {
var toks []string
for tok := range result.ConfigVars {
toks = append(toks, string(tok))
}
sort.Strings(toks)
for _, tok := range toks {
b.WriteString(fmt.Sprintf("%v%v: %v\n", detailsIndent, tok, result.ConfigVars[tokens.Token(tok)]))
}
}
}
func printSummary(b *bytes.Buffer, counts map[resource.StepOp]int, showReplaceSteps bool, plan bool) {
total := 0
for _, c := range counts {
total += c
}
var planned string
if plan {
planned = "planned "
}
var colon string
if total != 0 {
colon = ":"
}
b.WriteString(fmt.Sprintf("%v total %v%v%v\n", total, planned, plural("change", total), colon))
var planTo string
var pastTense string
if plan {
planTo = "to "
} else {
pastTense = "d"
}
for _, op := range resource.StepOps() {
if !showReplaceSteps && (op == resource.OpReplaceCreate || op == resource.OpReplaceDelete) {
// Unless the user requested it, don't show the fine-grained replacement steps; just the logical ones.
continue
}
if c := counts[op]; c > 0 {
b.WriteString(fmt.Sprintf(" %v%v %v %v%v%v%v\n",
op.Prefix(), c, plural("resource", c), planTo, op, pastTense, colors.Reset))
}
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
}
func plural(s string, c int) string {
if c != 1 {
s += "s"
}
return s
}
const detailsIndent = " " // 4 spaces, plus 2 for "+ ", "- ", and " " leaders
func printUnchanged(b *bytes.Buffer, plan resource.Plan, summary bool) {
b.WriteString(fmt.Sprintf("%vUnchanged resources:%v\n", colors.SpecUnimportant, colors.Reset))
for _, res := range plan.Unchanged() {
b.WriteString(" ") // simulate the 2 spaces for +, -, etc.
printResourceHeader(b, res, nil, "")
printResourceProperties(b, res, nil, nil, nil, summary, "")
}
}
func printStep(b *bytes.Buffer, step resource.Step, summary bool, indent string) {
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
// First print out the operation's prefix.
b.WriteString(step.Op().Prefix())
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
// Next print the resource URN, properties, etc.
printResourceHeader(b, step.Old(), step.New(), indent)
b.WriteString(step.Op().Suffix())
var replaces []resource.PropertyKey
if step.Old() != nil {
m := step.Old().URN()
replaceMap := step.Plan().Replaces()
replaces = replaceMap[m]
}
printResourceProperties(b, step.Old(), step.New(), step.NewProps(), replaces, summary, indent)
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
// Finally make sure to reset the color.
b.WriteString(colors.Reset)
}
func printResourceHeader(b *bytes.Buffer, old resource.Resource, new resource.Resource, indent string) {
var t tokens.Type
if old == nil {
t = new.Type()
} else {
t = old.Type()
}
// The primary header is the resource type (since it is easy on the eyes).
b.WriteString(fmt.Sprintf("%s:\n", string(t)))
}
func printResourceProperties(b *bytes.Buffer, old resource.Resource, new resource.Resource,
computed resource.PropertyMap, replaces []resource.PropertyKey, summary bool, indent string) {
indent += detailsIndent
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
// Print out the URN and, if present, the ID, as "pseudo-properties".
var id resource.ID
var URN resource.URN
if old == nil {
id = new.ID()
URN = new.URN()
} else {
id = old.ID()
URN = old.URN()
}
if id != "" {
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
b.WriteString(fmt.Sprintf("%s[id=%s]\n", indent, string(id)))
}
b.WriteString(fmt.Sprintf("%s[urn=%s]\n", indent, URN.Name()))
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
if !summary {
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
// Print all of the properties associated with this resource.
if old == nil && new != nil {
printObject(b, new.Properties(), indent)
} else if new == nil && old != nil {
printObject(b, old.Properties(), indent)
} else {
contract.Assert(computed != nil) // use computed properties for diffs.
printOldNewDiffs(b, old.Properties(), computed, replaces, indent)
}
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
}
func printObject(b *bytes.Buffer, props resource.PropertyMap, indent string) {
// Compute the maximum with of property keys so we can justify everything.
keys := resource.StablePropertyKeys(props)
maxkey := 0
for _, k := range keys {
if len(k) > maxkey {
maxkey = len(k)
}
}
// Now print out the values intelligently based on the type.
for _, k := range keys {
if v := props[k]; shouldPrintPropertyValue(v) {
printPropertyTitle(b, k, maxkey, indent)
printPropertyValue(b, v, indent)
}
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
}
func shouldPrintPropertyValue(v resource.PropertyValue) bool {
return !v.IsNull() // by default, don't print nulls (they just clutter up the output)
}
func printPropertyTitle(b *bytes.Buffer, k resource.PropertyKey, align int, indent string) {
b.WriteString(fmt.Sprintf("%s%-"+strconv.Itoa(align)+"s: ", indent, k))
}
func printPropertyValue(b *bytes.Buffer, v resource.PropertyValue, indent string) {
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
if v.IsNull() {
b.WriteString("<null>")
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
} else if v.IsBool() {
b.WriteString(fmt.Sprintf("%t", v.BoolValue()))
} else if v.IsNumber() {
b.WriteString(fmt.Sprintf("%v", v.NumberValue()))
} else if v.IsString() {
b.WriteString(fmt.Sprintf("\"%s\"", v.StringValue()))
} else if v.IsResource() {
b.WriteString(fmt.Sprintf("-> *%s", v.ResourceValue()))
} else if v.IsArray() {
b.WriteString(fmt.Sprintf("[\n"))
for i, elem := range v.ArrayValue() {
newIndent := printArrayElemHeader(b, i, indent)
printPropertyValue(b, elem, newIndent)
Repivot plan/apply commands; prepare for updates This change repivots the plan/apply commands slightly. This is largely in preparation for performing deletes and updates of existing environments. The old way was slightly confusing and made things appear more "magical" than they actually are. Namely, different things are needed for different kinds of deployment operations, and trying to present them each underneath a single pair of CLI commands just leads to weird modality and options. The new way is to offer three commands: create, update, and delete. Each does what it says on the tin: create provisions a new environment, update makes resource updates to an existing one, and delete tears down an existing one entirely. The arguments are what make this interesting: create demands a MuPackage to evaluate (producing the new desired state snapshot), update takes *both* an existing snapshot file plus a MuPackage to evaluate (producing the new desired state snapshot to diff against the existing one), and delete merely takes an existing snapshot file and no MuPackage, since all it must do is tear down an existing known environment. Replacing the plan functionality is the --dry-run (-n) flag that may be passed to any of the above commands. This will print out the plan without actually performing any opterations. All commands produce serializable resource files in the MuGL file format, and attempt to do smart things with respect to backups, etc., to support the intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 20:21:26 +01:00
}
b.WriteString(fmt.Sprintf("%s]", indent))
} else {
contract.Assert(v.IsObject())
b.WriteString("{\n")
printObject(b, v.ObjectValue(), indent+" ")
b.WriteString(fmt.Sprintf("%s}", indent))
}
b.WriteString("\n")
}
func getArrayElemHeader(b *bytes.Buffer, i int, indent string) (string, string) {
prefix := fmt.Sprintf(" %s[%d]: ", indent, i)
return prefix, fmt.Sprintf("%-"+strconv.Itoa(len(prefix))+"s", "")
}
func printArrayElemHeader(b *bytes.Buffer, i int, indent string) string {
prefix, newIndent := getArrayElemHeader(b, i, indent)
b.WriteString(prefix)
return newIndent
}
func printOldNewDiffs(b *bytes.Buffer, olds resource.PropertyMap, news resource.PropertyMap,
replaces []resource.PropertyKey, indent string) {
// Get the full diff structure between the two, and print it (recursively).
if diff := olds.Diff(news); diff != nil {
printObjectDiff(b, *diff, replaces, false, indent)
} else {
printObject(b, news, indent)
}
}
func printObjectDiff(b *bytes.Buffer, diff resource.ObjectDiff,
replaces []resource.PropertyKey, causedReplace bool, indent string) {
contract.Assert(len(indent) > 2)
// Compute the maximum with of property keys so we can justify everything.
keys := diff.Keys()
maxkey := 0
for _, k := range keys {
if len(k) > maxkey {
maxkey = len(k)
}
}
// If a list of what causes a resource to get replaced exist, create a handy map.
var replaceMap map[resource.PropertyKey]bool
if len(replaces) > 0 {
replaceMap = make(map[resource.PropertyKey]bool)
for _, k := range replaces {
replaceMap[k] = true
}
}
// To print an object diff, enumerate the keys in stable order, and print each property independently.
for _, k := range keys {
title := func(id string) { printPropertyTitle(b, k, maxkey, id) }
if add, isadd := diff.Adds[k]; isadd {
if shouldPrintPropertyValue(add) {
b.WriteString(colors.SpecAdded)
title(addIndent(indent))
printPropertyValue(b, add, addIndent(indent))
b.WriteString(colors.Reset)
}
} else if delete, isdelete := diff.Deletes[k]; isdelete {
if shouldPrintPropertyValue(delete) {
b.WriteString(colors.SpecDeleted)
title(deleteIndent(indent))
printPropertyValue(b, delete, deleteIndent(indent))
b.WriteString(colors.Reset)
}
} else if update, isupdate := diff.Updates[k]; isupdate {
if !causedReplace && replaceMap != nil {
causedReplace = replaceMap[k]
}
printPropertyValueDiff(b, title, update, causedReplace, indent)
} else if same := diff.Sames[k]; shouldPrintPropertyValue(same) {
title(indent)
printPropertyValue(b, diff.Sames[k], indent)
}
}
}
func printPropertyValueDiff(b *bytes.Buffer, title func(string), diff resource.ValueDiff,
causedReplace bool, indent string) {
contract.Assert(len(indent) > 2)
if diff.Array != nil {
title(indent)
b.WriteString("[\n")
a := diff.Array
for i := 0; i < a.Len(); i++ {
_, newIndent := getArrayElemHeader(b, i, indent)
title := func(id string) { printArrayElemHeader(b, i, id) }
if add, isadd := a.Adds[i]; isadd {
b.WriteString(resource.OpCreate.Color())
title(addIndent(indent))
printPropertyValue(b, add, addIndent(newIndent))
b.WriteString(colors.Reset)
} else if delete, isdelete := a.Deletes[i]; isdelete {
b.WriteString(resource.OpDelete.Color())
title(deleteIndent(indent))
printPropertyValue(b, delete, deleteIndent(newIndent))
b.WriteString(colors.Reset)
} else if update, isupdate := a.Updates[i]; isupdate {
printPropertyValueDiff(b, title, update, causedReplace, indent)
} else {
title(indent)
printPropertyValue(b, a.Sames[i], newIndent)
}
}
b.WriteString(fmt.Sprintf("%s]\n", indent))
} else if diff.Object != nil {
title(indent)
b.WriteString("{\n")
printObjectDiff(b, *diff.Object, nil, causedReplace, indent+" ")
b.WriteString(fmt.Sprintf("%s}\n", indent))
} else if diff.Old.IsResource() && diff.New.IsResource() && diff.New.ResourceValue().Replacement() {
// If the old and new are both resources, and the new is a replacement, show this in a special way (+-).
b.WriteString(resource.OpReplace.Color())
title(updateIndent(indent))
printPropertyValue(b, diff.Old, updateIndent(indent))
b.WriteString(colors.Reset)
} else {
// If we ended up here, the two values either differ by type, or they have different primitive values. We will
// simply emit a deletion line followed by an addition line.
if shouldPrintPropertyValue(diff.Old) {
var color string
if causedReplace {
color = resource.OpDelete.Color() // this property triggered replacement; color as a delete
} else {
color = resource.OpUpdate.Color()
}
b.WriteString(color)
title(deleteIndent(indent))
printPropertyValue(b, diff.Old, deleteIndent(indent))
b.WriteString(colors.Reset)
}
if shouldPrintPropertyValue(diff.New) {
var color string
if causedReplace {
color = resource.OpCreate.Color() // this property triggered replacement; color as a create
} else {
color = resource.OpUpdate.Color()
}
b.WriteString(color)
title(addIndent(indent))
printPropertyValue(b, diff.New, addIndent(indent))
b.WriteString(colors.Reset)
}
}
}
func addIndent(indent string) string { return indent[:len(indent)-2] + "+ " }
func deleteIndent(indent string) string { return indent[:len(indent)-2] + "- " }
func updateIndent(indent string) string { return indent[:len(indent)-2] + "+-" }