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.
|
2018-01-05 21:46:13 +01:00
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
|
2018-02-28 21:46:18 +01:00
|
|
|
"github.com/hashicorp/go-multierror"
|
2018-01-05 21:46:13 +01:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
|
2018-02-28 19:02:02 +01:00
|
|
|
"github.com/pulumi/pulumi/pkg/apitype"
|
2018-07-07 06:30:00 +02:00
|
|
|
"github.com/pulumi/pulumi/pkg/backend"
|
2018-02-28 21:46:18 +01:00
|
|
|
"github.com/pulumi/pulumi/pkg/diag"
|
2018-05-25 22:29:59 +02:00
|
|
|
"github.com/pulumi/pulumi/pkg/resource/stack"
|
2018-01-05 21:46:13 +01:00
|
|
|
"github.com/pulumi/pulumi/pkg/util/cmdutil"
|
|
|
|
)
|
|
|
|
|
|
|
|
func newStackImportCmd() *cobra.Command {
|
2018-02-28 21:46:18 +01:00
|
|
|
var force bool
|
2018-04-03 06:34:54 +02:00
|
|
|
var file string
|
2018-06-26 02:24:53 +02:00
|
|
|
var stackName string
|
2018-02-28 21:46:18 +01:00
|
|
|
cmd := &cobra.Command{
|
2018-01-05 21:46:13 +01:00
|
|
|
Use: "import",
|
|
|
|
Args: cmdutil.MaximumNArgs(0),
|
2018-01-27 18:40:05 +01:00
|
|
|
Short: "Import a deployment from standard in into an existing stack",
|
2018-01-05 21:46:13 +01:00
|
|
|
Long: "Import a deployment from standard in into an existing stack.\n" +
|
|
|
|
"\n" +
|
|
|
|
"A deployment that was exported from a stack using `pulumi stack export` and\n" +
|
|
|
|
"hand-edited to correct inconsistencies due to failed updates, manual changes\n" +
|
|
|
|
"to cloud resources, etc. can be reimported to the stack using this command.\n" +
|
|
|
|
"The updated deployment will be read from standard in.",
|
|
|
|
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
|
2018-07-07 06:30:00 +02:00
|
|
|
opts := backend.DisplayOptions{
|
|
|
|
Color: cmdutil.GetGlobalColorization(),
|
|
|
|
}
|
|
|
|
|
2018-01-05 21:46:13 +01:00
|
|
|
// Fetch the current stack and import a deployment.
|
Initial support for passing URLs to `new` and `up` (#1727)
* Initial support for passing URLs to `new` and `up`
This PR adds initial support for `pulumi new` using Git under the covers
to manage Pulumi templates, providing the same experience as before.
You can now also optionally pass a URL to a Git repository, e.g.
`pulumi new [<url>]`, including subdirectories within the repository,
and arbitrary branches, tags, or commits.
The following commands result in the same behavior from the user's
perspective:
- `pulumi new javascript`
- `pulumi new https://github.com/pulumi/templates/templates/javascript`
- `pulumi new https://github.com/pulumi/templates/tree/master/templates/javascript`
- `pulumi new https://github.com/pulumi/templates/tree/HEAD/templates/javascript`
To specify an arbitrary branch, tag, or commit:
- `pulumi new https://github.com/pulumi/templates/tree/<branch>/templates/javascript`
- `pulumi new https://github.com/pulumi/templates/tree/<tag>/templates/javascript`
- `pulumi new https://github.com/pulumi/templates/tree/<commit>/templates/javascript`
Branches and tags can include '/' separators, and `pulumi` will still
find the right subdirectory.
URLs to Gists are also supported, e.g.:
`pulumi new https://gist.github.com/justinvp/6673959ceb9d2ac5a14c6d536cb871a6`
If the specified subdirectory in the repository does not contain a
`Pulumi.yaml`, it will look for subdirectories within containing
`Pulumi.yaml` files, and prompt the user to choose a template, along the
lines of how `pulumi new` behaves when no template is specified.
The following commands result in the CLI prompting to choose a template:
- `pulumi new`
- `pulumi new https://github.com/pulumi/templates/templates`
- `pulumi new https://github.com/pulumi/templates/tree/master/templates`
- `pulumi new https://github.com/pulumi/templates/tree/HEAD/templates`
Of course, arbitrary branches, tags, or commits can be specified as well:
- `pulumi new https://github.com/pulumi/templates/tree/<branch>/templates`
- `pulumi new https://github.com/pulumi/templates/tree/<tag>/templates`
- `pulumi new https://github.com/pulumi/templates/tree/<commit>/templates`
This PR also includes initial support for passing URLs to `pulumi up`,
providing a streamlined way to deploy installable cloud applications
with Pulumi, without having to manage source code locally before doing
a deployment.
For example, `pulumi up https://github.com/justinvp/aws` can be used to
deploy a sample AWS app. The stack can be updated with different
versions, e.g.
`pulumi up https://github.com/justinvp/aws/tree/v2 -s <stack-to-update>`
Config values can optionally be passed via command line flags, e.g.
`pulumi up https://github.com/justinvp/aws -c aws:region=us-west-2 -c foo:bar=blah`
Gists can also be used, e.g.
`pulumi up https://gist.github.com/justinvp/62fde0463f243fcb49f5a7222e51bc76`
* Fix panic when hitting ^C from "choose template" prompt
* Add description to templates
When running `pulumi new` without specifying a template, include the template description along with the name in the "choose template" display.
```
$ pulumi new
Please choose a template:
aws-go A minimal AWS Go program
aws-javascript A minimal AWS JavaScript program
aws-python A minimal AWS Python program
aws-typescript A minimal AWS TypeScript program
> go A minimal Go program
hello-aws-javascript A simple AWS serverless JavaScript program
javascript A minimal JavaScript program
python A minimal Python program
typescript A minimal TypeScript program
```
* React to changes to the pulumi/templates repo.
We restructured the `pulumi/templates` repo to have all the templates in the root instead of in a `templates` subdirectory, so make the change here to no longer look for templates in `templates`.
This also fixes an issue around using `Depth: 1` that I found while testing this. When a named template is used, we attempt to clone or pull from the `pulumi/templates` repo to `~/.pulumi/templates`. Having it go in this well-known directory allows us to maintain previous behavior around allowing offline use of templates. If we use `Depth: 1` for the initial clone, it will fail when attempting to pull when there are updates to the remote repository. Unfortunately, there's no built-in `--unshallow` support in `go-git` and setting a larger `Depth` doesn't appear to help. There may be a workaround, but for now, if we're cloning the pulumi templates directory to `~/.pulumi/templates`, we won't use `Depth: 1`. For template URLs, we will continue to use `Depth: 1` as we clone those to a temp directory (which gets deleted) that we'll never try to update.
* List available templates in help text
* Address PR Feedback
* Don't show "Installing dependencies" message for `up`
* Fix secrets handling
When prompting for config, if the existing stack value is a secret, keep it a secret and mask the prompt. If the template says it should be secret, make it a secret.
* Fix ${PROJECT} and ${DESCRIPTION} handling for `up`
Templates used with `up` should already have a filled-in project name and description, but if it's a `new`-style template, that has `${PROJECT}` and/or `${DESCRIPTION}`, be helpful and just replace these with better values.
* Fix stack handling
Add a bool `setCurrent` param to `requireStack` to control whether the current stack should be saved in workspace settings. For the `up <url>` case, we don't want to save. Also, split the `up` code into two separate functions: one for the `up <url>` case and another for the normal `up` case where you have workspace in your current directory. While we may be able to combine them back into a single function, right now it's a bit cleaner being separate, even with some small amount of duplication.
* Fix panic due to nil crypter
Lazily get the crypter only if needed inside `promptForConfig`.
* Embellish comment
* Harden isPreconfiguredEmptyStack check
Fix the code to check to make sure the URL specified on the command line matches the URL stored in the `pulumi:template` config value, and that the rest of the config from the stack satisfies the config requirements of the template.
2018-08-11 03:08:16 +02:00
|
|
|
s, err := requireStack(stackName, false, opts, true /*setCurrent*/)
|
2018-01-05 21:46:13 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-03 06:34:54 +02:00
|
|
|
// Read from stdin or a specified file
|
|
|
|
reader := os.Stdin
|
|
|
|
if file != "" {
|
|
|
|
reader, err = os.Open(file)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "could not open file")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-03 21:12:54 +01:00
|
|
|
// Read the checkpoint from stdin. We decode this into a json.RawMessage so as not to lose any fields
|
|
|
|
// sent by the server that the client CLI does not recognize (enabling round-tripping).
|
2018-03-31 00:51:40 +02:00
|
|
|
var deployment apitype.UntypedDeployment
|
2018-04-03 06:34:54 +02:00
|
|
|
if err = json.NewDecoder(reader).Decode(&deployment); err != nil {
|
2018-01-05 21:46:13 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-03-03 21:12:54 +01:00
|
|
|
// We do, however, now want to unmarshal the json.RawMessage into a real, typed deployment. We do this so
|
2018-04-03 06:34:54 +02:00
|
|
|
// we can check that the deployment doesn't contain resources from a stack other than the selected one. This
|
|
|
|
// catches errors wherein someone imports the wrong stack's deployment (which can seriously hork things).
|
2018-08-03 23:06:00 +02:00
|
|
|
snapshot, err := stack.DeserializeUntypedDeployment(&deployment)
|
2018-05-25 22:29:59 +02:00
|
|
|
if err != nil {
|
|
|
|
switch err {
|
|
|
|
case stack.ErrDeploymentSchemaVersionTooOld:
|
|
|
|
return fmt.Errorf("the stack '%s' is too old to be used by this version of the Pulumi CLI",
|
|
|
|
s.Name().StackName())
|
|
|
|
case stack.ErrDeploymentSchemaVersionTooNew:
|
|
|
|
return fmt.Errorf("the stack '%s' is newer than what this version of the Pulumi CLI understands. "+
|
|
|
|
"Please update your version of the Pulumi CLI", s.Name().StackName())
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors.Wrap(err, "could not deserialize deployment")
|
2018-03-03 21:12:54 +01:00
|
|
|
}
|
2018-05-25 22:29:59 +02:00
|
|
|
|
2018-02-28 21:46:18 +01:00
|
|
|
var result error
|
2018-05-25 22:29:59 +02:00
|
|
|
for _, res := range snapshot.Resources {
|
2018-04-18 20:25:16 +02:00
|
|
|
if res.URN.Stack() != s.Name().StackName() {
|
2018-02-28 21:46:18 +01:00
|
|
|
msg := fmt.Sprintf("resource '%s' is from a different stack (%s != %s)",
|
2018-04-18 20:25:16 +02:00
|
|
|
res.URN, res.URN.Stack(), s.Name().StackName())
|
2018-02-28 21:46:18 +01:00
|
|
|
if force {
|
|
|
|
// If --force was passed, just issue a warning and proceed anyway.
|
2018-04-10 21:03:11 +02:00
|
|
|
// Note: we could associate this diagnostic with the resource URN
|
|
|
|
// we have. However, this sort of message seems to be better as
|
|
|
|
// something associated with the stack as a whole.
|
|
|
|
cmdutil.Diag().Warningf(diag.Message("" /*urn*/, msg))
|
2018-02-28 21:46:18 +01:00
|
|
|
} else {
|
|
|
|
// Otherwise, gather up an error so that we can quit before doing damage.
|
|
|
|
result = multierror.Append(result, errors.New(msg))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if result != nil {
|
2018-02-28 22:12:19 +01:00
|
|
|
return multierror.Append(result,
|
|
|
|
errors.New("importing this file could be dangerous; rerun with --force to proceed anyway"))
|
2018-02-28 21:46:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now perform the deployment.
|
2018-05-08 03:23:03 +02:00
|
|
|
if err = s.ImportDeployment(commandContext(), &deployment); err != nil {
|
2018-01-05 21:46:13 +01:00
|
|
|
return errors.Wrap(err, "could not import deployment")
|
|
|
|
}
|
|
|
|
fmt.Printf("Import successful.\n")
|
|
|
|
return nil
|
|
|
|
}),
|
|
|
|
}
|
2018-02-28 21:46:18 +01:00
|
|
|
|
2018-06-26 02:24:53 +02:00
|
|
|
cmd.PersistentFlags().StringVarP(
|
|
|
|
&stackName, "stack", "s", "", "The name of the stack to operate on. Defaults to the current stack")
|
2018-02-28 21:46:18 +01:00
|
|
|
cmd.PersistentFlags().BoolVarP(
|
|
|
|
&force, "force", "f", false,
|
|
|
|
"Force the import to occur, even if apparent errors are discovered beforehand (not recommended)")
|
2018-04-03 06:34:54 +02:00
|
|
|
cmd.PersistentFlags().StringVarP(
|
|
|
|
&file, "file", "", "", "A filename to read stack input from")
|
2018-02-28 21:46:18 +01:00
|
|
|
|
|
|
|
return cmd
|
2018-01-05 21:46:13 +01:00
|
|
|
}
|