2018-05-22 21:43:36 +02:00
|
|
|
// Copyright 2016-2018, Pulumi Corporation.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2017-03-15 23:40:06 +01:00
|
|
|
|
2017-09-22 04:18:21 +02:00
|
|
|
package cmd
|
2017-03-15 23:40:06 +01:00
|
|
|
|
|
|
|
import (
|
2018-05-05 20:57:09 +02:00
|
|
|
"github.com/pkg/errors"
|
2017-03-15 23:40:06 +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
|
|
|
|
2018-05-05 20:57:09 +02:00
|
|
|
"github.com/pulumi/pulumi/pkg/backend"
|
2018-09-05 00:40:15 +02:00
|
|
|
"github.com/pulumi/pulumi/pkg/backend/display"
|
2018-05-05 20:57:09 +02:00
|
|
|
"github.com/pulumi/pulumi/pkg/engine"
|
2020-03-18 21:36:19 +01:00
|
|
|
"github.com/pulumi/pulumi/sdk/go/common/resource"
|
2017-09-22 04:18:21 +02:00
|
|
|
"github.com/pulumi/pulumi/pkg/util/cmdutil"
|
2019-03-14 02:00:49 +01:00
|
|
|
"github.com/pulumi/pulumi/pkg/util/result"
|
2017-03-15 23:40:06 +01:00
|
|
|
)
|
|
|
|
|
2017-09-23 00:15:42 +02:00
|
|
|
func newPreviewCmd() *cobra.Command {
|
2017-07-14 02:09:46 +02:00
|
|
|
var debug bool
|
2018-05-16 02:33:16 +02:00
|
|
|
var expectNop bool
|
2018-05-05 20:57:09 +02:00
|
|
|
var message string
|
2017-10-16 21:04:35 +02:00
|
|
|
var stack string
|
2019-09-26 02:45:36 +02:00
|
|
|
var configArray []string
|
Support lists and maps in config (#3342)
This change adds support for lists and maps in config. We now allow
lists/maps (and nested structures) in `Pulumi.<stack>.yaml` (or
`Pulumi.<stack>.json`; yes, we currently support that).
For example:
```yaml
config:
proj:blah:
- a
- b
- c
proj:hello: world
proj:outer:
inner: value
proj:servers:
- port: 80
```
While such structures could be specified in the `.yaml` file manually,
we support setting values in maps/lists from the command line.
As always, you can specify single values with:
```shell
$ pulumi config set hello world
```
Which results in the following YAML:
```yaml
proj:hello world
```
And single value secrets via:
```shell
$ pulumi config set --secret token shhh
```
Which results in the following YAML:
```yaml
proj:token:
secure: v1:VZAhuroR69FkEPTk:isKafsoZVMWA9pQayGzbWNynww==
```
Values in a list can be set from the command line using the new
`--path` flag, which indicates the config key contains a path to a
property in a map or list:
```shell
$ pulumi config set --path names[0] a
$ pulumi config set --path names[1] b
$ pulumi config set --path names[2] c
```
Which results in:
```yaml
proj:names
- a
- b
- c
```
Values can be obtained similarly:
```shell
$ pulumi config get --path names[1]
b
```
Or setting values in a map:
```shell
$ pulumi config set --path outer.inner value
```
Which results in:
```yaml
proj:outer:
inner: value
```
Of course, setting values in nested structures is supported:
```shell
$ pulumi config set --path servers[0].port 80
```
Which results in:
```yaml
proj:servers:
- port: 80
```
If you want to include a period in the name of a property, it can be
specified as:
```
$ pulumi config set --path 'nested["foo.bar"]' baz
```
Which results in:
```yaml
proj:nested:
foo.bar: baz
```
Examples of valid paths:
- root
- root.nested
- 'root["nested"]'
- root.double.nest
- 'root["double"].nest'
- 'root["double"]["nest"]'
- root.array[0]
- root.array[100]
- root.array[0].nested
- root.array[0][1].nested
- root.nested.array[0].double[1]
- 'root["key with \"escaped\" quotes"]'
- 'root["key with a ."]'
- '["root key with \"escaped\" quotes"].nested'
- '["root key with a ."][100]'
Note: paths that contain quotes can be surrounded by single quotes.
When setting values with `--path`, if the value is `"false"` or
`"true"`, it will be saved as the boolean value, and if it is
convertible to an integer, it will be saved as an integer.
Secure values are supported in lists/maps as well:
```shell
$ pulumi config set --path --secret tokens[0] shh
```
Will result in:
```yaml
proj:tokens:
- secure: v1:wpZRCe36sFg1RxwG:WzPeQrCn4n+m4Ks8ps15MxvFXg==
```
Note: maps of length 1 with a key of “secure” and string value are
reserved for storing secret values. Attempting to create such a value
manually will result in an error:
```shell
$ pulumi config set --path parent.secure foo
error: "secure" key in maps of length 1 are reserved
```
**Accessing config values from the command line with JSON**
```shell
$ pulumi config --json
```
Will output:
```json
{
"proj:hello": {
"value": "world",
"secret": false,
"object": false
},
"proj:names": {
"value": "[\"a\",\"b\",\"c\"]",
"secret": false,
"object": true,
"objectValue": [
"a",
"b",
"c"
]
},
"proj:nested": {
"value": "{\"foo.bar\":\"baz\"}",
"secret": false,
"object": true,
"objectValue": {
"foo.bar": "baz"
}
},
"proj:outer": {
"value": "{\"inner\":\"value\"}",
"secret": false,
"object": true,
"objectValue": {
"inner": "value"
}
},
"proj:servers": {
"value": "[{\"port\":80}]",
"secret": false,
"object": true,
"objectValue": [
{
"port": 80
}
]
},
"proj:token": {
"secret": true,
"object": false
},
"proj:tokens": {
"secret": true,
"object": true
}
}
```
If the value is a map or list, `"object"` will be `true`. `"value"` will
contain the object as serialized JSON and a new `"objectValue"` property
will be available containing the value of the object.
If the object contains any secret values, `"secret"` will be `true`, and
just like with scalar values, the value will not be outputted unless
`--show-secrets` is specified.
**Accessing config values from Pulumi programs**
Map/list values are available to Pulumi programs as serialized JSON, so
the existing
`getObject`/`requireObject`/`getSecretObject`/`requireSecretObject`
functions can be used to retrieve such values, e.g.:
```typescript
import * as pulumi from "@pulumi/pulumi";
interface Server {
port: number;
}
const config = new pulumi.Config();
const names = config.requireObject<string[]>("names");
for (const n of names) {
console.log(n);
}
const servers = config.requireObject<Server[]>("servers");
for (const s of servers) {
console.log(s.port);
}
```
2019-11-01 21:41:27 +01:00
|
|
|
var configPath bool
|
2018-01-18 20:10:15 +01:00
|
|
|
|
|
|
|
// Flags for engine.UpdateOptions.
|
2019-12-17 22:31:12 +01:00
|
|
|
var jsonDisplay bool
|
2019-08-11 09:39:03 +02:00
|
|
|
var policyPackPaths []string
|
2020-03-08 22:11:55 +01:00
|
|
|
var policyPackConfigPaths []string
|
Implement a refresh command
This change implements a `pulumi refresh` command. It operates a bit
like `pulumi update`, and friends, in that it supports `--preview` and
`--diff`, along with the usual flags, and will update your checkpoint.
It works through substitution of the deploy.Source abstraction, which
generates a sequence of resource registration events. This new
deploy.RefreshSource takes in a prior checkpoint and will walk it,
refreshing the state via the associated resource providers by invoking
Read for each resource encountered, and merging the resulting state with
the prior checkpoint, to yield a new resource.Goal state. This state is
then fed through the engine in the usual ways with a few minor caveats:
namely, although the engine must generate steps for the logical
operations (permitting us to get nice summaries, progress, and diffs),
it mustn't actually carry them out because the state being imported
already reflects reality (a deleted resource has *already* been deleted,
so of course the engine need not perform the deletion). The diffing
logic also needs to know how to treat the case of refresh slightly
differently, because we are going to be diffing outputs and not inputs.
Note that support for managed stacks is not yet complete, since that
requires updates to the service to support a refresh endpoint. That
will be coming soon ...
2018-04-10 20:22:39 +02:00
|
|
|
var diffDisplay bool
|
2019-09-07 02:07:54 +02:00
|
|
|
var eventLogPath string
|
2017-09-17 17:10:46 +02:00
|
|
|
var parallel int
|
2019-12-17 22:31:12 +01:00
|
|
|
var refresh bool
|
2017-03-15 23:40:06 +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
|
2019-09-18 18:49:13 +02:00
|
|
|
var showReads bool
|
2018-10-06 23:13:02 +02:00
|
|
|
var suppressOutputs bool
|
2019-12-17 22:31:12 +01:00
|
|
|
var targets []string
|
|
|
|
var replaces []string
|
|
|
|
var targetReplaces []string
|
|
|
|
var targetDependents bool
|
2018-01-18 20:10:15 +01:00
|
|
|
|
2017-03-15 23:40:06 +01:00
|
|
|
var cmd = &cobra.Command{
|
2017-10-21 02:36:47 +02:00
|
|
|
Use: "preview",
|
2017-11-13 23:59:11 +01:00
|
|
|
Aliases: []string{"pre"},
|
|
|
|
SuggestFor: []string{"build", "plan"},
|
2018-05-05 20:57:09 +02:00
|
|
|
Short: "Show a preview of updates to a stack's resources",
|
|
|
|
Long: "Show a preview of updates a stack's resources.\n" +
|
|
|
|
"\n" +
|
|
|
|
"This command displays a preview of the updates to an existing stack whose state is\n" +
|
2019-03-02 20:04:09 +01:00
|
|
|
"represented by an existing state file. The new desired state is computed by running\n" +
|
2018-05-05 20:57:09 +02:00
|
|
|
"a Pulumi program, and extracting all resource allocations from its resulting object graph.\n" +
|
|
|
|
"These allocations are then compared against the existing state to determine what\n" +
|
|
|
|
"operations must take place to achieve the desired state. No changes to the stack will\n" +
|
|
|
|
"actually take place.\n" +
|
|
|
|
"\n" +
|
|
|
|
"The program to run is loaded from the project in the current directory. Use the `-C` or\n" +
|
|
|
|
"`--cwd` flag to use a different directory.",
|
|
|
|
Args: cmdutil.NoArgs,
|
2019-03-19 20:40:10 +01:00
|
|
|
Run: cmdutil.RunResultFunc(func(cmd *cobra.Command, args []string) result.Result {
|
2019-04-30 19:31:53 +02:00
|
|
|
var displayType = display.DisplayProgress
|
|
|
|
if diffDisplay {
|
|
|
|
displayType = display.DisplayDiff
|
|
|
|
}
|
|
|
|
|
2019-12-17 22:31:12 +01:00
|
|
|
displayOpts := display.Options{
|
|
|
|
Color: cmdutil.GetGlobalColorization(),
|
|
|
|
ShowConfig: showConfig,
|
|
|
|
ShowReplacementSteps: showReplacementSteps,
|
|
|
|
ShowSameResources: showSames,
|
|
|
|
ShowReads: showReads,
|
|
|
|
SuppressOutputs: suppressOutputs,
|
|
|
|
IsInteractive: cmdutil.Interactive(),
|
|
|
|
Type: displayType,
|
|
|
|
JSONDisplay: jsonDisplay,
|
|
|
|
EventLogPath: eventLogPath,
|
|
|
|
Debug: debug,
|
2018-05-05 20:57:09 +02:00
|
|
|
}
|
2018-07-07 06:30:00 +02:00
|
|
|
|
2020-03-08 22:11:55 +01:00
|
|
|
if err := validatePolicyPackConfig(policyPackPaths, policyPackConfigPaths); err != nil {
|
|
|
|
return result.FromError(err)
|
|
|
|
}
|
|
|
|
|
2019-12-17 22:31:12 +01:00
|
|
|
s, err := requireStack(stack, true, displayOpts, true /*setCurrent*/)
|
2018-07-07 06:30:00 +02:00
|
|
|
if err != nil {
|
2019-03-14 02:00:49 +01:00
|
|
|
return result.FromError(err)
|
2018-07-07 06:30:00 +02:00
|
|
|
}
|
|
|
|
|
2019-09-26 02:45:36 +02:00
|
|
|
// Save any config values passed via flags.
|
2020-03-08 22:11:55 +01:00
|
|
|
if err = parseAndSaveConfigArray(s, configArray, configPath); err != nil {
|
2019-09-26 02:45:36 +02:00
|
|
|
return result.FromError(err)
|
|
|
|
}
|
|
|
|
|
2019-10-10 00:33:35 +02:00
|
|
|
proj, root, err := readProject()
|
2018-07-07 06:30:00 +02:00
|
|
|
if err != nil {
|
2019-03-14 02:00:49 +01:00
|
|
|
return result.FromError(err)
|
2018-07-07 06:30:00 +02:00
|
|
|
}
|
|
|
|
|
2019-12-17 22:31:12 +01:00
|
|
|
m, err := getUpdateMetadata(message, root)
|
2018-07-07 06:30:00 +02:00
|
|
|
if err != nil {
|
2019-03-14 02:00:49 +01:00
|
|
|
return result.FromError(errors.Wrap(err, "gathering environment metadata"))
|
2018-07-07 06:30:00 +02:00
|
|
|
}
|
|
|
|
|
2019-05-15 08:23:03 +02:00
|
|
|
sm, err := getStackSecretsManager(s)
|
2019-04-19 00:57:54 +02:00
|
|
|
if err != nil {
|
2019-05-15 08:23:03 +02:00
|
|
|
return result.FromError(errors.Wrap(err, "getting secrets manager"))
|
2019-04-19 00:57:54 +02:00
|
|
|
}
|
|
|
|
|
2019-05-15 08:23:03 +02:00
|
|
|
cfg, err := getStackConfiguration(s, sm)
|
2019-04-26 23:49:50 +02:00
|
|
|
if err != nil {
|
2019-05-15 08:23:03 +02:00
|
|
|
return result.FromError(errors.Wrap(err, "getting stack configuration"))
|
2019-04-26 23:49:50 +02:00
|
|
|
}
|
|
|
|
|
2019-12-17 22:31:12 +01:00
|
|
|
targetURNs := []resource.URN{}
|
|
|
|
for _, t := range targets {
|
|
|
|
targetURNs = append(targetURNs, resource.URN(t))
|
|
|
|
}
|
|
|
|
|
|
|
|
replaceURNs := []resource.URN{}
|
|
|
|
for _, r := range replaces {
|
|
|
|
replaceURNs = append(replaceURNs, resource.URN(r))
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tr := range targetReplaces {
|
|
|
|
targetURNs = append(targetURNs, resource.URN(tr))
|
|
|
|
replaceURNs = append(replaceURNs, resource.URN(tr))
|
|
|
|
}
|
|
|
|
|
|
|
|
opts := backend.UpdateOptions{
|
|
|
|
Engine: engine.UpdateOptions{
|
2020-03-08 22:11:55 +01:00
|
|
|
LocalPolicyPacks: engine.MakeLocalPolicyPacks(policyPackPaths, policyPackConfigPaths),
|
2020-01-30 22:31:41 +01:00
|
|
|
Parallel: parallel,
|
|
|
|
Debug: debug,
|
|
|
|
Refresh: refresh,
|
|
|
|
ReplaceTargets: replaceURNs,
|
|
|
|
UseLegacyDiff: useLegacyDiff(),
|
|
|
|
UpdateTargets: targetURNs,
|
|
|
|
TargetDependents: targetDependents,
|
2019-12-17 22:31:12 +01:00
|
|
|
},
|
|
|
|
Display: displayOpts,
|
|
|
|
}
|
|
|
|
|
2019-03-20 00:21:50 +01:00
|
|
|
changes, res := s.Preview(commandContext(), backend.UpdateOperation{
|
2019-04-19 00:57:54 +02:00
|
|
|
Proj: proj,
|
|
|
|
Root: root,
|
|
|
|
M: m,
|
|
|
|
Opts: opts,
|
|
|
|
StackConfiguration: cfg,
|
2019-04-26 23:49:50 +02:00
|
|
|
SecretsManager: sm,
|
2019-04-19 00:57:54 +02:00
|
|
|
Scopes: cancellationScopes,
|
2018-09-05 16:20:25 +02:00
|
|
|
})
|
2019-03-20 00:21:50 +01:00
|
|
|
|
2018-05-16 02:33:16 +02:00
|
|
|
switch {
|
2019-03-20 00:21:50 +01:00
|
|
|
case res != nil:
|
|
|
|
return PrintEngineResult(res)
|
2018-05-16 19:22:09 +02:00
|
|
|
case expectNop && changes != nil && changes.HasChanges():
|
2019-03-14 02:00:49 +01:00
|
|
|
return result.FromError(errors.New("error: no changes were expected but changes were proposed"))
|
2018-05-16 02:33:16 +02:00
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
2017-03-15 23:40:06 +01:00
|
|
|
}),
|
|
|
|
}
|
|
|
|
|
2017-07-14 02:09:46 +02:00
|
|
|
cmd.PersistentFlags().BoolVarP(
|
|
|
|
&debug, "debug", "d", false,
|
|
|
|
"Print detailed debugging output during resource operations")
|
2018-05-16 02:33:16 +02:00
|
|
|
cmd.PersistentFlags().BoolVar(
|
|
|
|
&expectNop, "expect-no-changes", false,
|
|
|
|
"Return an error if any changes are proposed by this preview")
|
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", "",
|
2018-06-26 02:24:53 +02:00
|
|
|
"The name of the stack to operate on. Defaults to the current stack")
|
2018-12-01 00:11:05 +01:00
|
|
|
cmd.PersistentFlags().StringVar(
|
|
|
|
&stackConfigFile, "config-file", "",
|
|
|
|
"Use the configuration values in the specified file rather than detecting the file name")
|
2019-09-26 02:45:36 +02:00
|
|
|
cmd.PersistentFlags().StringArrayVarP(
|
|
|
|
&configArray, "config", "c", []string{},
|
|
|
|
"Config to use during the preview")
|
Support lists and maps in config (#3342)
This change adds support for lists and maps in config. We now allow
lists/maps (and nested structures) in `Pulumi.<stack>.yaml` (or
`Pulumi.<stack>.json`; yes, we currently support that).
For example:
```yaml
config:
proj:blah:
- a
- b
- c
proj:hello: world
proj:outer:
inner: value
proj:servers:
- port: 80
```
While such structures could be specified in the `.yaml` file manually,
we support setting values in maps/lists from the command line.
As always, you can specify single values with:
```shell
$ pulumi config set hello world
```
Which results in the following YAML:
```yaml
proj:hello world
```
And single value secrets via:
```shell
$ pulumi config set --secret token shhh
```
Which results in the following YAML:
```yaml
proj:token:
secure: v1:VZAhuroR69FkEPTk:isKafsoZVMWA9pQayGzbWNynww==
```
Values in a list can be set from the command line using the new
`--path` flag, which indicates the config key contains a path to a
property in a map or list:
```shell
$ pulumi config set --path names[0] a
$ pulumi config set --path names[1] b
$ pulumi config set --path names[2] c
```
Which results in:
```yaml
proj:names
- a
- b
- c
```
Values can be obtained similarly:
```shell
$ pulumi config get --path names[1]
b
```
Or setting values in a map:
```shell
$ pulumi config set --path outer.inner value
```
Which results in:
```yaml
proj:outer:
inner: value
```
Of course, setting values in nested structures is supported:
```shell
$ pulumi config set --path servers[0].port 80
```
Which results in:
```yaml
proj:servers:
- port: 80
```
If you want to include a period in the name of a property, it can be
specified as:
```
$ pulumi config set --path 'nested["foo.bar"]' baz
```
Which results in:
```yaml
proj:nested:
foo.bar: baz
```
Examples of valid paths:
- root
- root.nested
- 'root["nested"]'
- root.double.nest
- 'root["double"].nest'
- 'root["double"]["nest"]'
- root.array[0]
- root.array[100]
- root.array[0].nested
- root.array[0][1].nested
- root.nested.array[0].double[1]
- 'root["key with \"escaped\" quotes"]'
- 'root["key with a ."]'
- '["root key with \"escaped\" quotes"].nested'
- '["root key with a ."][100]'
Note: paths that contain quotes can be surrounded by single quotes.
When setting values with `--path`, if the value is `"false"` or
`"true"`, it will be saved as the boolean value, and if it is
convertible to an integer, it will be saved as an integer.
Secure values are supported in lists/maps as well:
```shell
$ pulumi config set --path --secret tokens[0] shh
```
Will result in:
```yaml
proj:tokens:
- secure: v1:wpZRCe36sFg1RxwG:WzPeQrCn4n+m4Ks8ps15MxvFXg==
```
Note: maps of length 1 with a key of “secure” and string value are
reserved for storing secret values. Attempting to create such a value
manually will result in an error:
```shell
$ pulumi config set --path parent.secure foo
error: "secure" key in maps of length 1 are reserved
```
**Accessing config values from the command line with JSON**
```shell
$ pulumi config --json
```
Will output:
```json
{
"proj:hello": {
"value": "world",
"secret": false,
"object": false
},
"proj:names": {
"value": "[\"a\",\"b\",\"c\"]",
"secret": false,
"object": true,
"objectValue": [
"a",
"b",
"c"
]
},
"proj:nested": {
"value": "{\"foo.bar\":\"baz\"}",
"secret": false,
"object": true,
"objectValue": {
"foo.bar": "baz"
}
},
"proj:outer": {
"value": "{\"inner\":\"value\"}",
"secret": false,
"object": true,
"objectValue": {
"inner": "value"
}
},
"proj:servers": {
"value": "[{\"port\":80}]",
"secret": false,
"object": true,
"objectValue": [
{
"port": 80
}
]
},
"proj:token": {
"secret": true,
"object": false
},
"proj:tokens": {
"secret": true,
"object": true
}
}
```
If the value is a map or list, `"object"` will be `true`. `"value"` will
contain the object as serialized JSON and a new `"objectValue"` property
will be available containing the value of the object.
If the object contains any secret values, `"secret"` will be `true`, and
just like with scalar values, the value will not be outputted unless
`--show-secrets` is specified.
**Accessing config values from Pulumi programs**
Map/list values are available to Pulumi programs as serialized JSON, so
the existing
`getObject`/`requireObject`/`getSecretObject`/`requireSecretObject`
functions can be used to retrieve such values, e.g.:
```typescript
import * as pulumi from "@pulumi/pulumi";
interface Server {
port: number;
}
const config = new pulumi.Config();
const names = config.requireObject<string[]>("names");
for (const n of names) {
console.log(n);
}
const servers = config.requireObject<Server[]>("servers");
for (const s of servers) {
console.log(s.port);
}
```
2019-11-01 21:41:27 +01:00
|
|
|
cmd.PersistentFlags().BoolVar(
|
|
|
|
&configPath, "config-path", false,
|
|
|
|
"Config keys contain a path to a property in a map or list to set")
|
2018-01-18 20:10:15 +01:00
|
|
|
|
2018-05-05 20:57:09 +02:00
|
|
|
cmd.PersistentFlags().StringVarP(
|
|
|
|
&message, "message", "m", "",
|
|
|
|
"Optional message to associate with the preview operation")
|
|
|
|
|
2019-12-17 22:31:12 +01:00
|
|
|
cmd.PersistentFlags().StringArrayVarP(
|
|
|
|
&targets, "target", "t", []string{},
|
|
|
|
"Specify a single resource URN to update. Other resources will not be updated."+
|
|
|
|
" Multiple resources can be specified using --target urn1 --target urn2")
|
|
|
|
cmd.PersistentFlags().StringArrayVar(
|
|
|
|
&replaces, "replace", []string{},
|
|
|
|
"Specify resources to replace. Multiple resources can be specified using --replace run1 --replace urn2")
|
|
|
|
cmd.PersistentFlags().StringArrayVar(
|
|
|
|
&targetReplaces, "target-replace", []string{},
|
|
|
|
"Specify a single resource URN to replace. Other resources will not be updated."+
|
|
|
|
" Shorthand for --target urn --replace urn.")
|
|
|
|
cmd.PersistentFlags().BoolVar(
|
|
|
|
&targetDependents, "target-dependents", false,
|
|
|
|
"Allows updating of dependent targets discovered but not specified in --target list")
|
|
|
|
|
2018-01-18 20:10:15 +01:00
|
|
|
// Flags for engine.UpdateOptions.
|
2020-03-02 23:23:40 +01:00
|
|
|
cmd.PersistentFlags().StringSliceVar(
|
|
|
|
&policyPackPaths, "policy-pack", []string{},
|
|
|
|
"[PREVIEW] Run one or more policy packs as part of this update")
|
2020-03-08 22:11:55 +01:00
|
|
|
cmd.PersistentFlags().StringSliceVar(
|
|
|
|
&policyPackConfigPaths, "policy-pack-config", []string{},
|
|
|
|
`[PREVIEW] Path to JSON file containing the config for the policy pack of the corresponding "--policy-pack" flag`)
|
Implement a refresh command
This change implements a `pulumi refresh` command. It operates a bit
like `pulumi update`, and friends, in that it supports `--preview` and
`--diff`, along with the usual flags, and will update your checkpoint.
It works through substitution of the deploy.Source abstraction, which
generates a sequence of resource registration events. This new
deploy.RefreshSource takes in a prior checkpoint and will walk it,
refreshing the state via the associated resource providers by invoking
Read for each resource encountered, and merging the resulting state with
the prior checkpoint, to yield a new resource.Goal state. This state is
then fed through the engine in the usual ways with a few minor caveats:
namely, although the engine must generate steps for the logical
operations (permitting us to get nice summaries, progress, and diffs),
it mustn't actually carry them out because the state being imported
already reflects reality (a deleted resource has *already* been deleted,
so of course the engine need not perform the deletion). The diffing
logic also needs to know how to treat the case of refresh slightly
differently, because we are going to be diffing outputs and not inputs.
Note that support for managed stacks is not yet complete, since that
requires updates to the service to support a refresh endpoint. That
will be coming soon ...
2018-04-10 20:22:39 +02:00
|
|
|
cmd.PersistentFlags().BoolVar(
|
|
|
|
&diffDisplay, "diff", false,
|
|
|
|
"Display operation as a rich diff showing the overall change")
|
2019-04-20 22:34:58 +02:00
|
|
|
cmd.Flags().BoolVarP(
|
|
|
|
&jsonDisplay, "json", "j", false,
|
|
|
|
"Serialize the preview diffs, operations, and overall output as JSON")
|
2017-09-17 17:10:46 +02:00
|
|
|
cmd.PersistentFlags().IntVarP(
|
2018-08-28 01:41:20 +02:00
|
|
|
¶llel, "parallel", "p", defaultParallel,
|
2018-10-29 18:24:04 +01:00
|
|
|
"Allow P resource operations to run in parallel at once (1 for no parallelism). Defaults to unbounded.")
|
2019-12-17 22:31:12 +01:00
|
|
|
cmd.PersistentFlags().BoolVarP(
|
|
|
|
&refresh, "refresh", "r", false,
|
|
|
|
"Refresh the state of the stack's resources before this update")
|
2017-03-15 23:40:06 +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", false,
|
|
|
|
"Show detailed resource replacement creates and deletes instead of a single step")
|
2019-09-18 18:49:13 +02:00
|
|
|
|
2017-03-15 23:40:06 +01:00
|
|
|
cmd.PersistentFlags().BoolVar(
|
2017-06-11 17:09:20 +02:00
|
|
|
&showSames, "show-sames", false,
|
2017-03-15 23:40:06 +01:00
|
|
|
"Show resources that needn't be updated because they haven't changed, alongside those that do")
|
2019-09-18 18:49:13 +02:00
|
|
|
cmd.PersistentFlags().BoolVar(
|
|
|
|
&showReads, "show-reads", false,
|
|
|
|
"Show resources that are being read in, alongside those being managed directly in the stack")
|
|
|
|
|
2018-10-06 23:13:02 +02:00
|
|
|
cmd.PersistentFlags().BoolVar(
|
|
|
|
&suppressOutputs, "suppress-outputs", false,
|
|
|
|
"Suppress display of stack outputs (in case they contain sensitive values)")
|
2017-03-15 23:40:06 +01:00
|
|
|
|
2019-09-07 02:07:54 +02:00
|
|
|
if hasDebugCommands() {
|
|
|
|
cmd.PersistentFlags().StringVar(
|
|
|
|
&eventLogPath, "event-log", "",
|
|
|
|
"Log events to a file at this path")
|
|
|
|
}
|
2017-03-15 23:40:06 +01:00
|
|
|
return cmd
|
|
|
|
}
|