2017-06-26 23:46:34 +02:00
|
|
|
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
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-09-22 04:18:21 +02:00
|
|
|
package cmd
|
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
|
|
|
|
|
|
|
import (
|
2017-10-25 19:46:05 +02:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
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
|
|
|
"github.com/spf13/cobra"
|
Make major commands more pleasant
This change eliminates the need to constantly type in the environment
name when performing major commands like configuration, planning, and
deployment. It's probably due to my age, however, I keep fat-fingering
simple commands in front of investors and I am embarrassed!
In the new model, there is a notion of a "current environment", and
I have modeled it kinda sorta just like Git's notion of "current branch."
By default, the current environment is set when you `init` something.
Otherwise, there is the `coco env select <env>` command to change it.
(Running this command w/out a new <env> will show you the current one.)
The major commands `config`, `plan`, `deploy`, and `destroy` will prefer
to use the current environment, unless it is overridden by using the
--env flag. All of the `coco env <cmd> <env>` commands still require the
explicit passing of an environment which seems reasonable since they are,
after all, about manipulating environments.
As part of this, I've overhauled the aging workspace settings cruft,
which had fallen into disrepair since the initial prototype.
2017-03-22 03:23:32 +01:00
|
|
|
|
2017-10-25 19:46:05 +02:00
|
|
|
"github.com/pulumi/pulumi/pkg/apitype"
|
|
|
|
"github.com/pulumi/pulumi/pkg/diag/colors"
|
2017-09-22 04:18:21 +02:00
|
|
|
"github.com/pulumi/pulumi/pkg/engine"
|
2017-10-31 23:03:07 +01:00
|
|
|
"github.com/pulumi/pulumi/pkg/pack"
|
2017-10-19 00:37:18 +02:00
|
|
|
"github.com/pulumi/pulumi/pkg/resource/config"
|
2017-10-25 19:46:05 +02:00
|
|
|
"github.com/pulumi/pulumi/pkg/tokens"
|
|
|
|
"github.com/pulumi/pulumi/pkg/util/archive"
|
2017-09-22 04:18:21 +02:00
|
|
|
"github.com/pulumi/pulumi/pkg/util/cmdutil"
|
2017-10-25 19:46:05 +02:00
|
|
|
"github.com/pulumi/pulumi/pkg/workspace"
|
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-09-23 02:23:40 +02:00
|
|
|
func newUpdateCmd() *cobra.Command {
|
2017-10-25 19:46:05 +02:00
|
|
|
// Swap out which update command to use based on whether or not the Console API is available.
|
2017-10-27 07:14:56 +02:00
|
|
|
if usePulumiCloudCommands() {
|
|
|
|
return newCloudUpdateCmd()
|
2017-10-25 19:46:05 +02:00
|
|
|
}
|
2017-10-27 07:14:56 +02:00
|
|
|
return newFAFUpdateCmd()
|
2017-10-25 19:46:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// newFAFUpdateCmd returns the fire-and-forget version of the update command.
|
|
|
|
func newFAFUpdateCmd() *cobra.Command {
|
2017-03-11 19:07:34 +01:00
|
|
|
var analyzers []string
|
2017-07-14 02:09:46 +02:00
|
|
|
var debug 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
|
|
|
var dryRun bool
|
2017-10-16 21:04:35 +02:00
|
|
|
var stack string
|
2017-09-17 17:10:46 +02:00
|
|
|
var parallel int
|
2017-02-28 19:32:24 +01:00
|
|
|
var showConfig bool
|
2017-08-06 19:05:51 +02:00
|
|
|
var showReplacementSteps bool
|
2017-06-11 17:09:20 +02:00
|
|
|
var showSames bool
|
2017-02-25 16:55:22 +01:00
|
|
|
var 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
|
|
|
var cmd = &cobra.Command{
|
2017-10-21 02:36:47 +02:00
|
|
|
Use: "update",
|
|
|
|
Aliases: []string{"up"},
|
|
|
|
SuggestFor: []string{"deploy", "push"},
|
|
|
|
Short: "Update the resources in an stack",
|
2017-10-16 21:04:35 +02:00
|
|
|
Long: "Update the resources in an stack\n" +
|
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
|
|
|
"\n" +
|
2017-10-16 21:04:35 +02:00
|
|
|
"This command updates an existing stack whose state is represented by the\n" +
|
2017-09-23 00:15:42 +02:00
|
|
|
"existing snapshot file. The new desired state is computed by compiling and evaluating an\n" +
|
2017-03-09 16:43:28 +01:00
|
|
|
"executable package, and extracting all resource allocations from its resulting object graph.\n" +
|
2017-09-23 00:15:42 +02:00
|
|
|
"These allocations are then compared against the existing state to determine what operations\n" +
|
|
|
|
"must take place to achieve the desired state. This command results in a full snapshot of the\n" +
|
2017-10-16 21:04:35 +02:00
|
|
|
"stack's new resource state, so that it may be updated incrementally again later.\n" +
|
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
|
|
|
"\n" +
|
2017-10-06 17:50:54 +02:00
|
|
|
"The package to execute is loaded from the current directory. Use the `-C` or `--cwd` flag to\n" +
|
|
|
|
"use a different directory.",
|
2017-04-12 20:12:25 +02:00
|
|
|
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
|
2017-10-16 21:04:35 +02:00
|
|
|
stackName, err := explicitOrCurrent(stack)
|
2017-10-03 01:37:12 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-10-19 00:37:18 +02:00
|
|
|
cfg, err := getConfiguration(stackName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var decrypter config.ValueDecrypter = panicCrypter{}
|
|
|
|
|
|
|
|
if hasSecureValue(cfg) {
|
|
|
|
decrypter, err = getSymmetricCrypter()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
localProvider := localStackProvider{decrypter: decrypter}
|
|
|
|
pulumiEngine := engine.Engine{Targets: localProvider, Snapshots: localProvider}
|
|
|
|
|
2017-10-05 23:08:46 +02:00
|
|
|
events := make(chan engine.Event)
|
|
|
|
done := make(chan bool)
|
|
|
|
|
|
|
|
go displayEvents(events, done, debug)
|
|
|
|
|
2017-10-19 00:37:18 +02:00
|
|
|
if err = pulumiEngine.Deploy(stackName, events, engine.DeployOptions{
|
2017-08-06 19:05:51 +02:00
|
|
|
DryRun: dryRun,
|
|
|
|
Analyzers: analyzers,
|
2017-09-17 17:10:46 +02:00
|
|
|
Parallel: parallel,
|
2017-08-06 19:05:51 +02:00
|
|
|
ShowConfig: showConfig,
|
|
|
|
ShowReplacementSteps: showReplacementSteps,
|
|
|
|
ShowSames: showSames,
|
|
|
|
Summary: summary,
|
2017-10-21 02:28:35 +02:00
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-10-05 23:08:46 +02:00
|
|
|
|
|
|
|
<-done
|
2017-10-23 00:22:15 +02:00
|
|
|
close(events)
|
|
|
|
close(done)
|
2017-10-21 02:28:35 +02:00
|
|
|
return nil
|
2017-03-07 14:47:42 +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
|
|
|
}
|
|
|
|
|
2017-03-11 19:07:34 +01:00
|
|
|
cmd.PersistentFlags().StringSliceVar(
|
|
|
|
&analyzers, "analyzer", []string{},
|
2017-09-23 02:23:40 +02:00
|
|
|
"Run one or more analyzers as part of this update")
|
2017-07-14 02:09:46 +02:00
|
|
|
cmd.PersistentFlags().BoolVarP(
|
|
|
|
&debug, "debug", "d", false,
|
|
|
|
"Print detailed debugging output during resource operations")
|
Make major commands more pleasant
This change eliminates the need to constantly type in the environment
name when performing major commands like configuration, planning, and
deployment. It's probably due to my age, however, I keep fat-fingering
simple commands in front of investors and I am embarrassed!
In the new model, there is a notion of a "current environment", and
I have modeled it kinda sorta just like Git's notion of "current branch."
By default, the current environment is set when you `init` something.
Otherwise, there is the `coco env select <env>` command to change it.
(Running this command w/out a new <env> will show you the current one.)
The major commands `config`, `plan`, `deploy`, and `destroy` will prefer
to use the current environment, unless it is overridden by using the
--env flag. All of the `coco env <cmd> <env>` commands still require the
explicit passing of an environment which seems reasonable since they are,
after all, about manipulating environments.
As part of this, I've overhauled the aging workspace settings cruft,
which had fallen into disrepair since the initial prototype.
2017-03-22 03:23:32 +01:00
|
|
|
cmd.PersistentFlags().StringVarP(
|
2017-10-16 21:04:35 +02:00
|
|
|
&stack, "stack", "s", "",
|
|
|
|
"Choose an stack other than the currently selected one")
|
2017-09-17 17:10:46 +02:00
|
|
|
cmd.PersistentFlags().IntVarP(
|
|
|
|
¶llel, "parallel", "p", 0,
|
|
|
|
"Allow P resource operations to run in parallel at once (<=1 for no parallelism)")
|
2017-02-28 19:32:24 +01:00
|
|
|
cmd.PersistentFlags().BoolVar(
|
|
|
|
&showConfig, "show-config", false,
|
|
|
|
"Show configuration keys and variables")
|
Implement `get` functions on all resources
This change implements the `get` function for resources. Per pulumi/lumi#83,
this allows Lumi scripts to actually read from the target environment.
For example, we can now look up a SecurityGroup from its ARN:
let group = aws.ec2.SecurityGroup.get(
"arn:aws:ec2:us-west-2:153052954103:security-group:sg-02150d79");
The returned object is a fully functional resource object. So, we can then
link it up with an EC2 instance, for example, in the usual ways:
let instance = new aws.ec2.Instance(..., {
securityGroups: [ group ],
});
This didn't require any changes to the RPC or provider model, since we
already implement the Get function.
There are a few loose ends; two are short term:
1) URNs are not rehydrated.
2) Query is not yet implemented.
One is mid-term:
3) We probably want a URN-based lookup function. But we will likely
wait until we tackle pulumi/lumi#109 before adding this.
And one is long term (and subtle):
4) These amount to I/O and are not repeatable! A change in the target
environment may cause a script to generate a different plan
intermittently. Most likely we want to apply a different kind of
deployment "policy" for such scripts. These are inching towards the
scripting model of pulumi/lumi#121, which is an entirely different
beast than the repeatable immutable infrastructure deployments.
Finally, it is worth noting that with this, we have some of the fundamental
underpinnings required to finally tackle "inference" (pulumi/lumi#142).
2017-06-20 02:24:00 +02:00
|
|
|
cmd.PersistentFlags().BoolVar(
|
2017-08-06 19:05:51 +02:00
|
|
|
&showReplacementSteps, "show-replacement-steps", true,
|
|
|
|
"Show detailed resource replacement creates and deletes instead of a single step")
|
2017-02-27 19:58:24 +01:00
|
|
|
cmd.PersistentFlags().BoolVar(
|
2017-06-11 17:09:20 +02:00
|
|
|
&showSames, "show-sames", false,
|
2017-02-27 20:08:14 +01:00
|
|
|
"Show resources that needn't be updated because they haven't changed, alongside those that do")
|
2017-10-16 21:04:35 +02:00
|
|
|
cmd.PersistentFlags().BoolVar(
|
|
|
|
&summary, "summary", false,
|
2017-09-23 00:15:42 +02:00
|
|
|
"Only display summarization of resources and operations")
|
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 cmd
|
|
|
|
}
|
2017-10-25 19:46:05 +02:00
|
|
|
|
|
|
|
func newCloudUpdateCmd() *cobra.Command {
|
|
|
|
var stack string
|
|
|
|
|
|
|
|
var cmd = &cobra.Command{
|
|
|
|
Use: "update",
|
|
|
|
Aliases: []string{"up"},
|
|
|
|
SuggestFor: []string{"deploy", "push"},
|
|
|
|
Short: "Update the resources in an stack",
|
|
|
|
Long: "Update a Pulumi Program\n" +
|
|
|
|
"\n" +
|
|
|
|
"This command creates or updates a Stack hosted in a Pulumi Cloud.",
|
|
|
|
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
|
2017-10-31 01:47:12 +01:00
|
|
|
// Look up the owner, repository, and project from the workspace and nearest package.
|
|
|
|
w, err := newWorkspace()
|
2017-10-27 07:14:56 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-10-31 01:47:12 +01:00
|
|
|
projID, err := getCloudProjectIdentifier(w)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default to the workspace settings if stack isn't provided.
|
|
|
|
stackName := tokens.QName(stack)
|
|
|
|
if stackName == "" {
|
|
|
|
stackName = w.Settings().Stack
|
|
|
|
}
|
|
|
|
if stackName == "" {
|
|
|
|
return errors.New("stack argument not set and workspace does not have selected stack")
|
|
|
|
}
|
2017-10-27 07:14:56 +02:00
|
|
|
|
2017-10-25 19:46:05 +02:00
|
|
|
// Zip up the Pulumi program's directory, which may be a parent of CWD.
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("getting working directory: %v", err)
|
|
|
|
}
|
2017-10-31 23:03:07 +01:00
|
|
|
packagePath, err := workspace.DetectPackage(cwd)
|
2017-10-25 19:46:05 +02:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("looking for Pulumi package: %v", err)
|
|
|
|
}
|
2017-10-31 23:03:07 +01:00
|
|
|
if packagePath == "" {
|
2017-10-25 19:46:05 +02:00
|
|
|
return fmt.Errorf("no Pulumi package found")
|
|
|
|
}
|
2017-10-31 23:03:07 +01:00
|
|
|
// packagePath is the path to the pulumi.yaml file. Need its parent folder.
|
|
|
|
packageFolder := filepath.Dir(packagePath)
|
|
|
|
archive, err := archive.EncodePath(packageFolder)
|
2017-10-25 19:46:05 +02:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("creating archive: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-10-31 23:03:07 +01:00
|
|
|
// Load the package, since we now require passing the Runtime with the update request.
|
|
|
|
pkg, err := pack.Load(packagePath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-10-25 19:46:05 +02:00
|
|
|
// Gather up configuration.
|
|
|
|
// TODO(pulumi-service/issues/221): Have pulumi.com handle the encryption/decryption.
|
2017-10-27 07:14:56 +02:00
|
|
|
textConfig, err := getDecryptedConfig(stackName)
|
2017-10-25 19:46:05 +02:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "getting decrypted configuration")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the program in the Pulumi Cloud
|
|
|
|
updateRequest := apitype.UpdateProgramRequest{
|
2017-10-31 23:03:07 +01:00
|
|
|
Name: pkg.Name,
|
|
|
|
Runtime: pkg.Runtime,
|
2017-10-25 19:46:05 +02:00
|
|
|
ProgramArchive: archive,
|
|
|
|
Config: textConfig,
|
|
|
|
}
|
|
|
|
var updateResponse apitype.UpdateProgramResponse
|
2017-10-31 01:47:12 +01:00
|
|
|
path := fmt.Sprintf("/orgs/%s/programs/%s/%s/stacks/%s/update",
|
|
|
|
projID.Owner, projID.Repository, projID.Project, string(stackName))
|
2017-10-25 19:46:05 +02:00
|
|
|
if err = pulumiRESTCall("POST", path, &updateRequest, &updateResponse); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-10-27 07:14:56 +02:00
|
|
|
fmt.Printf("Updating Stack '%s' to version %d...\n", string(stackName), updateResponse.Version)
|
2017-10-25 19:46:05 +02:00
|
|
|
|
|
|
|
// Wait for the update to complete.
|
|
|
|
status, err := waitForUpdate(path)
|
2017-10-28 00:40:15 +02:00
|
|
|
fmt.Println() // The PPC's final message we print to STDOUT doesn't include a newline.
|
|
|
|
|
2017-10-25 19:46:05 +02:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("waiting for update: %v", err)
|
|
|
|
}
|
|
|
|
if status == apitype.StatusSucceeded {
|
2017-10-26 00:28:29 +02:00
|
|
|
fmt.Println("Update completed successfully.")
|
2017-10-25 19:46:05 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return fmt.Errorf("update unsuccessful: status %v", status)
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
|
2017-10-27 07:14:56 +02:00
|
|
|
cmd.PersistentFlags().StringVarP(
|
|
|
|
&stack, "stack", "s", "",
|
|
|
|
"Choose an stack other than the currently selected one")
|
2017-10-25 19:46:05 +02:00
|
|
|
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
|
|
|
// waitForUpdate waits for the current update of a Pulumi program to reach a terminal state. Returns the
|
|
|
|
// final state. "path" is the URL endpoint to poll for updates.
|
|
|
|
func waitForUpdate(path string) (apitype.UpdateStatus, error) {
|
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
|
|
|
|
// Events occur in sequence, filter out all the ones we have seen before in each request.
|
|
|
|
eventIndex := 0
|
|
|
|
for {
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
|
|
|
|
var updateResults apitype.UpdateResults
|
|
|
|
pathWithIndex := fmt.Sprintf("%s?afterIndex=%d", path, eventIndex)
|
|
|
|
if err := pulumiRESTCall("GET", pathWithIndex, nil, &updateResults); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, event := range updateResults.Events {
|
|
|
|
printEvent(event)
|
|
|
|
eventIndex = event.Index
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if in termal state.
|
|
|
|
updateStatus := apitype.UpdateStatus(updateResults.Status)
|
|
|
|
switch updateStatus {
|
|
|
|
case apitype.StatusFailed:
|
|
|
|
fallthrough
|
|
|
|
case apitype.StatusSucceeded:
|
|
|
|
return updateStatus, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func printEvent(event apitype.UpdateEvent) {
|
|
|
|
stream := os.Stdout // Ignoring event.Kind which could be StderrEvent.
|
2017-10-26 00:28:29 +02:00
|
|
|
rawEntry, ok := event.Fields["text"]
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
text := rawEntry.(string)
|
2017-10-25 19:46:05 +02:00
|
|
|
if colorize, ok := event.Fields["colorize"].(bool); ok && colorize {
|
|
|
|
text = colors.ColorizeText(text)
|
|
|
|
}
|
|
|
|
fmt.Fprint(stream, text)
|
|
|
|
}
|
|
|
|
|
|
|
|
// getDecryptedConfig returns the stack's configuration with any secrets in plain-text.
|
|
|
|
func getDecryptedConfig(stackName tokens.QName) (map[tokens.ModuleMember]string, error) {
|
|
|
|
cfg, err := getConfiguration(stackName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "getting configuration")
|
|
|
|
}
|
|
|
|
|
|
|
|
var decrypter config.ValueDecrypter = panicCrypter{}
|
|
|
|
if hasSecureValue(cfg) {
|
|
|
|
decrypter, err = getSymmetricCrypter()
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "getting symmetric crypter")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
textConfig := make(map[tokens.ModuleMember]string)
|
|
|
|
for key := range cfg {
|
|
|
|
decrypted, err := cfg[key].Value(decrypter)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not decrypt configuration value")
|
|
|
|
}
|
|
|
|
textConfig[key] = decrypted
|
|
|
|
}
|
|
|
|
return textConfig, nil
|
|
|
|
}
|