// 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. package cmd import ( "fmt" "sort" "github.com/dustin/go-humanize" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/pulumi/pulumi/pkg/util/cmdutil" "github.com/pulumi/pulumi/pkg/workspace" ) func newPluginLsCmd() *cobra.Command { var projectOnly bool var jsonOut bool cmd := &cobra.Command{ Use: "ls", Short: "List plugins", Args: cmdutil.NoArgs, Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error { // Produce a list of plugins, sorted by name and version. var plugins []workspace.PluginInfo var err error if projectOnly { if plugins, err = getProjectPlugins(); err != nil { return errors.Wrapf(err, "loading project plugins") } } else { if plugins, err = workspace.GetPlugins(); err != nil { return errors.Wrapf(err, "loading plugins") } } // Sort the plugins: by name first alphabetical ascending and version descending, so that plugins // with the same name/kind sort by newest to oldest. sort.Slice(plugins, func(i, j int) bool { pi, pj := plugins[i], plugins[j] if pi.Name < pj.Name { return true } else if pi.Name == pj.Name && pi.Kind == pj.Kind && (pi.Version == nil || (pj.Version != nil && pi.Version.GT(*pj.Version))) { return true } return false }) if jsonOut { return formatPluginsJSON(plugins) } return formatPluginConsole(plugins) }), } cmd.PersistentFlags().BoolVarP( &projectOnly, "project", "p", false, "List only the plugins used by the current project") cmd.PersistentFlags().BoolVarP( &jsonOut, "json", "j", false, "Emit output as JSON") return cmd } // pluginInfoJSON is the shape of the --json output for a configuration value. While we can add fields to this // structure in the future, we should not change existing fields. type pluginInfoJSON struct { Name string `json:"name"` Kind string `json:"kind"` Version string `json:"version"` Size int `json:"size"` InstallTime *string `json:"installTime,omitempty"` LastUsedTime *string `json:"lastUsedTime,omitempty"` } func formatPluginsJSON(plugins []workspace.PluginInfo) error { makeStringRef := func(s string) *string { return &s } jsonPluginInfo := make([]pluginInfoJSON, len(plugins)) for idx, plugin := range plugins { jsonPluginInfo[idx] = pluginInfoJSON{ Name: plugin.Name, Kind: string(plugin.Kind), Version: plugin.Version.String(), Size: int(plugin.Size), } if !plugin.InstallTime.IsZero() { jsonPluginInfo[idx].InstallTime = makeStringRef(plugin.InstallTime.UTC().Format(timeFormat)) } if !plugin.LastUsedTime.IsZero() { jsonPluginInfo[idx].LastUsedTime = makeStringRef(plugin.LastUsedTime.UTC().Format(timeFormat)) } } return printJSON(jsonPluginInfo) } func formatPluginConsole(plugins []workspace.PluginInfo) error { var totalSize uint64 rows := []cmdutil.TableRow{} for _, plugin := range plugins { var version string if plugin.Version != nil { version = plugin.Version.String() } var bytes string if plugin.Size == 0 { bytes = naString } else { bytes = humanize.Bytes(uint64(plugin.Size)) } var installTime string if plugin.InstallTime.IsZero() { installTime = naString } else { installTime = humanize.Time(plugin.InstallTime) } var lastUsedTime string if plugin.LastUsedTime.IsZero() { lastUsedTime = humanNeverTime } else { lastUsedTime = humanize.Time(plugin.LastUsedTime) } rows = append(rows, cmdutil.TableRow{ Columns: []string{plugin.Name, string(plugin.Kind), version, bytes, installTime, lastUsedTime}, }) totalSize += uint64(plugin.Size) } cmdutil.PrintTable(cmdutil.Table{ Headers: []string{"NAME", "KIND", "VERSION", "SIZE", "INSTALLED", "LAST USED"}, Rows: rows, }) fmt.Printf("\n") fmt.Printf("TOTAL plugin cache size: %s\n", humanize.Bytes(totalSize)) return nil } const humanNeverTime = "never" const naString = "n/a"