pulumi/cmd/plugin_install.go
Pat Gavlin e884c63104 Extract a Pulumi API client.
These changes refactor direct interactions with the Pulumi API out of
the cloud backend and into a subpackage, `pkg/backend/cloud/client`.
This package exposes a slightly higher-level API that takes care of
calculating paths, performing HTTP calls, and occasionally wrapping
multiple physical calls into a single logical call (notably the creation
of an update and the upload of its program).

This is primarily intended as preparation for some of the changes
suggested in the feedback for #1067.
2018-03-21 16:45:54 -07:00

124 lines
4.1 KiB
Go

// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
package cmd
import (
"io"
"os"
"github.com/blang/semver"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/pulumi/pulumi/pkg/backend/cloud"
"github.com/pulumi/pulumi/pkg/util/cmdutil"
"github.com/pulumi/pulumi/pkg/workspace"
)
func newPluginInstallCmd() *cobra.Command {
var cloudURL string
var file string
var reinstall bool
var cmd = &cobra.Command{
Use: "install [KIND NAME VERSION]",
Args: cmdutil.MaximumNArgs(3),
Short: "Install one or more plugins",
Long: "Install one or more plugins.\n" +
"\n" +
"This command is used manually install plugins required by your program. It may\n" +
"be run either with a specific KIND, NAME, and VERSION, or by omitting these and\n" +
"letting Pulumi compute the set of plugins that may be required by the current\n" +
"project. VERSION cannot be a range: it must be a specific number.\n" +
"\n" +
"If you let Pulumi compute the set to download, it is conservative and may end up\n" +
"downloading more plugins than is strictly necessary.",
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
// Parse the kind, name, and version, if specified.
var installs []workspace.PluginInfo
if len(args) > 0 {
if !workspace.IsPluginKind(args[0]) {
return errors.Errorf("unrecognized plugin kind: %s", args[0])
} else if len(args) < 2 {
return errors.New("missing plugin name argument")
} else if len(args) < 3 {
return errors.New("missing plugin version argument")
}
version, err := semver.ParseTolerant(args[2])
if err != nil {
return errors.Wrap(err, "invalid plugin semver")
}
installs = append(installs, workspace.PluginInfo{
Kind: workspace.PluginKind(args[0]),
Name: args[1],
Version: &version,
})
} else {
if file != "" {
return errors.New("--file (-f) is only valid if a specific package is being installed")
}
// If a specific plugin wasn't given, compute the set of plugins the current project needs.
plugins, err := getProjectPlugins()
if err != nil {
return err
}
for _, plugin := range plugins {
// Skip language plugins; by definition, we already have one installed.
// TODO[pulumi/pulumi#956]: eventually we will want to honor and install these in the usual way.
if plugin.Kind != workspace.LanguagePlugin {
installs = append(installs, plugin)
}
}
}
// Target the cloud URL for downloads.
var releases cloud.Backend
if len(installs) > 0 && file == "" {
r, err := cloud.New(cmdutil.Diag(), cloud.ValueOrDefaultURL(cloudURL))
if err != nil {
return errors.Wrap(err, "creating API client")
}
releases = r
}
// Now for each kind, name, version pair, download it from the release website, and install it.
for _, install := range installs {
// If the plugin already exists, don't download it unless --reinstall was passed.
if !reinstall && workspace.HasPlugin(install) {
continue
}
// If we got here, actually try to do the download.
var source string
var tarball io.ReadCloser
var err error
if file == "" {
source = releases.CloudURL()
if tarball, err = releases.DownloadPlugin(install, true); err != nil {
return errors.Wrapf(err,
"downloading %s plugin %s from %s", install.Kind, install.String(), source)
}
} else {
source = file
if tarball, err = os.Open(file); err != nil {
return errors.Wrapf(err, "opening file %s", source)
}
}
if err = install.Install(tarball); err != nil {
return errors.Wrapf(err, "installing %s from %s", install.String(), source)
}
}
return nil
}),
}
cmd.PersistentFlags().StringVarP(&cloudURL,
"cloud-url", "c", "", "A cloud URL to download releases from")
cmd.PersistentFlags().StringVarP(&file,
"file", "f", "", "Install a plugin from a tarball file, instead of downloading it")
cmd.PersistentFlags().BoolVar(&reinstall,
"reinstall", false, "Reinstall a plugin even if it already exists")
return cmd
}