This change implements basic plugin management, but we do not yet actually use the plugins for anything (that comes next). Plugins are stored in `~/.pulumi/plugins`, and are expected to be in the format `pulumi-<KIND>-<NAME>-v<VERSION>[.exe]`. The KIND is one of `analyzer`, `language`, or `resource`, the NAME is a hyphen- delimited name (e.g., `aws` or `foo-bar`), and VERSION is the plugin's semantic version (e.g., `0.9.11`, `1.3.7-beta.a736cf`, etc). This commit includes four new CLI commands: * `pulumi plugin` is the top-level plugin command. It does nothing but show the help text for associated child commands. * `pulumi plugin install` can be used to install plugins manually. If run with no additional arguments, it will compute the set of plugins used by the current project, and download them all. It may be run to explicitly download a single plugin, however, by invoking it as `pulumi plugin install KIND NAME VERSION`. For example, `pulumi plugin install resource aws v0.9.11`. By default, this command uses the cloud backend in the usual way to perform the download, although a separate URL may be given with --cloud-url, just like all other commands that interact with our backend service. * `pulumi plugin ls` lists all plugins currently installed in the plugin cache. It displays some useful statistics, like the size of the plugin, when it was installed, when it was last used, and so on. It sorts the display alphabetically by plugin name, and for plugins with multiple versions, it shows the newest at the top. The command also summarizes how much disk space is currently being consumed by the plugin cache. There are no filtering capabilities yet. * `pulumi plugin prune` will delete plugins from the cache. By default, when run with no arguments, it will delete everything. It may be run with additional arguments, KIND, NAME, and VERSION, each one getting more specific about what it will delete. For instance, `pulumi plugin prune resource aws` will delete all AWS plugin versions, while `pulumi plugin prune resource aws <0.9` will delete all AWS plugins before version 0.9. Unless --yes is passed, the command will confirm the deletion with a count of how many plugins will be affected by the command. We do not yet actually download plugins on demand yet. That will come in a subsequent change.
97 lines
2.9 KiB
Go
97 lines
2.9 KiB
Go
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/blang/semver"
|
|
"github.com/hashicorp/go-multierror"
|
|
"github.com/pkg/errors"
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/pulumi/pulumi/pkg/util/cmdutil"
|
|
"github.com/pulumi/pulumi/pkg/workspace"
|
|
)
|
|
|
|
func newPluginPruneCmd() *cobra.Command {
|
|
var all bool
|
|
var yes bool
|
|
var cmd = &cobra.Command{
|
|
Use: "prune [KIND [NAME [VERSION]]]",
|
|
Args: cmdutil.MaximumNArgs(3),
|
|
Short: "Prune one or more plugins from the download cache",
|
|
Long: "Prune one or more plugins from the download cache.\n" +
|
|
"\n" +
|
|
"Specify KIND, NAME, and/or VERSION to narrow down what will be pruned.\n" +
|
|
"If none are specified, the entire cache will be pruned. If only KIND and\n" +
|
|
"NAME are specified, but not VERSION, all versions of the plugin with the\n" +
|
|
"given KIND and NAME will be pruned. VERSION may be a range.\n" +
|
|
"\n" +
|
|
"If a pruned plugin is subsequently required in order to execute a Pulumi\n" +
|
|
"program, it will be automatically re-downloaded. Plugins may be explicitly\n" +
|
|
"downloaded and installed using the plugin install command.",
|
|
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
|
|
// Parse the filters.
|
|
var kind workspace.PluginKind
|
|
var name string
|
|
var version *semver.Range
|
|
if len(args) > 0 {
|
|
if !workspace.IsPluginKind(args[0]) {
|
|
return errors.Errorf("unrecognized plugin kind: %s", kind)
|
|
}
|
|
kind = workspace.PluginKind(args[0])
|
|
}
|
|
if len(args) > 1 {
|
|
name = args[1]
|
|
}
|
|
if len(args) > 2 {
|
|
r, err := semver.ParseRange(args[2])
|
|
if err != nil {
|
|
return errors.Wrap(err, "invalid plugin semver")
|
|
}
|
|
version = &r
|
|
}
|
|
|
|
// Now build a list of plugins that match.
|
|
var deletes []workspace.PluginInfo
|
|
plugins, err := workspace.GetPlugins()
|
|
if err != nil {
|
|
return errors.Wrap(err, "loading plugins")
|
|
}
|
|
for _, plugin := range plugins {
|
|
if (kind == "" || plugin.Kind == kind) &&
|
|
(name == "" || plugin.Name == name) &&
|
|
(version == nil || (*version)(plugin.Version)) {
|
|
deletes = append(deletes, plugin)
|
|
}
|
|
}
|
|
|
|
// Confirm that the user wants to do this (unless --yes was passed), and do the deletes.
|
|
var suffix string
|
|
if len(deletes) != 1 {
|
|
suffix = "s"
|
|
}
|
|
prompt := fmt.Sprintf("This will remove %d plugin%s from the cache.", len(deletes), suffix)
|
|
if yes || confirmPrompt(prompt, "yes") {
|
|
var result error
|
|
for _, plugin := range deletes {
|
|
if err := plugin.Delete(); err != nil {
|
|
result = multierror.Append(
|
|
result, errors.Wrapf(err, "failed to delete plugin %s", plugin.Path))
|
|
}
|
|
}
|
|
if result != nil {
|
|
return result
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}),
|
|
}
|
|
|
|
cmd.PersistentFlags().BoolVarP(&all, "all", "a", false, "Remove all plugins")
|
|
cmd.PersistentFlags().BoolVar(&yes, "yes", false, "Skip confirmation prompts, and proceed with removal anyway")
|
|
|
|
return cmd
|
|
}
|