diff --git a/cmd/glasskube/cmd/list.go b/cmd/glasskube/cmd/list.go index a601037bc..aaf6655d8 100644 --- a/cmd/glasskube/cmd/list.go +++ b/cmd/glasskube/cmd/list.go @@ -3,14 +3,21 @@ package cmd import ( "fmt" "os" + "strings" "github.com/glasskube/glasskube/internal/cliutils" - "github.com/glasskube/glasskube/internal/config" "github.com/glasskube/glasskube/pkg/client" "github.com/glasskube/glasskube/pkg/list" "github.com/spf13/cobra" ) +var listCmdOptions = struct { + ListInstalledOnly bool + ShowDescription bool + ShowLatestVersion bool + More bool +}{} + var listCmd = &cobra.Command{ Use: "list", Aliases: []string{"ls", "l"}, @@ -19,9 +26,14 @@ var listCmd = &cobra.Command{ "as well as their installation status in your cluster.\nYou can choose to only show installed packages.", PreRun: cliutils.SetupClientContext(true), Run: func(cmd *cobra.Command, args []string) { + if listCmdOptions.More { + listCmdOptions.ShowLatestVersion = true + listCmdOptions.ShowDescription = true + } + pkgClient := client.FromContext(cmd.Context()) listOptions := list.DefaultListOptions - if config.ListInstalledOnly { + if listCmdOptions.ListInstalledOnly { listOptions |= list.OnlyInstalled } pkgs, err := list.GetPackagesWithStatus(pkgClient, cmd.Context(), listOptions) @@ -30,46 +42,95 @@ var listCmd = &cobra.Command{ os.Exit(1) return } - if config.ListInstalledOnly && len(pkgs) == 0 { + if listCmdOptions.ListInstalledOnly && len(pkgs) == 0 { fmt.Println("There are currently no packages installed in your cluster.\n" + "Run \"glasskube help install\" to get started.") } else { - printPackageTable(pkgs, config.Verbose) + printPackageTable(pkgs) } }, } func init() { - listCmd.PersistentFlags().BoolVarP(&config.ListInstalledOnly, "installed", "i", false, + listCmd.PersistentFlags().BoolVarP(&listCmdOptions.ListInstalledOnly, "installed", "i", false, "list only installed packages") - listCmd.PersistentFlags().BoolVar(&config.Verbose, "show-description", false, - "show additional information to the packages") + listCmd.PersistentFlags().BoolVar(&listCmdOptions.ShowDescription, "show-description", false, + "show the package description") + listCmd.PersistentFlags().BoolVar(&listCmdOptions.ShowLatestVersion, "show-latest", false, + "show the latest version of packages if available") + listCmd.PersistentFlags().BoolVarP(&listCmdOptions.More, "more", "m", false, + "show additional information about packages (like --show-description --show-latest)") + + listCmd.MarkFlagsMutuallyExclusive("show-description", "more") + listCmd.MarkFlagsMutuallyExclusive("show-latest", "more") + RootCmd.AddCommand(listCmd) } -func printPackageTable(packages []*list.PackageTeaserWithStatus, verbose bool) { - header := make([]string, 3) - header[0] = "NAME" - header[1] = "STATUS" - if verbose { - header[2] = "DESCRIPTION" +func printPackageTable(packages []*list.PackageWithStatus) { + header := []string{"NAME", "STATUS", "VERSION"} + if listCmdOptions.ShowLatestVersion { + header = append(header, "LATEST VERSION") + } + if listCmdOptions.ShowDescription { + header = append(header, "DESCRIPTION") } - cliutils.PrintPackageTable(os.Stdout, + err := cliutils.PrintPackageTable(os.Stdout, packages, header, - func(pkg *list.PackageTeaserWithStatus) []string { - row := make([]string, 3) - row[0] = pkg.PackageName - var statusStr string - if pkg.Status == nil { - statusStr = "Not installed" - } else { - statusStr = pkg.Status.Status + func(pkg *list.PackageWithStatus) []string { + row := []string{pkg.Name, statusString(*pkg), versionString(*pkg)} + if listCmdOptions.ShowLatestVersion { + row = append(row, pkg.LatestVersion) } - row[1] = statusStr - if verbose { - row[2] = pkg.ShortDescription + if listCmdOptions.ShowDescription { + row = append(row, pkg.ShortDescription) } return row }) + if err != nil { + fmt.Fprintf(os.Stderr, "There was an error displaying the package table:\n%v\n(This is a bug)\n", err) + os.Exit(1) + } +} + +func statusString(pkg list.PackageWithStatus) string { + if pkg.Status != nil { + return pkg.Status.Status + } else { + return "Not installed" + } +} + +func versionString(pkg list.PackageWithStatus) string { + if pkg.Package != nil { + specVersion := pkg.Package.Spec.PackageInfo.Version + statusVersion := pkg.Package.Status.Version + repoVersion := pkg.LatestVersion + + if statusVersion != "" { + versionAddons := []string{} + if specVersion != "" && statusVersion != specVersion { + versionAddons = append(versionAddons, fmt.Sprintf("%v desired", specVersion)) + } + if repoVersion != "" && statusVersion != repoVersion { + versionAddons = append(versionAddons, fmt.Sprintf("%v available", repoVersion)) + } + if len(versionAddons) > 0 { + return fmt.Sprintf("%v (%v)", statusVersion, strings.Join(versionAddons, ", ")) + } else { + return statusVersion + } + } else if specVersion != "" { + if specVersion != repoVersion { + return fmt.Sprintf("%v (%v available)", specVersion, repoVersion) + } else { + return specVersion + } + } else { + return "n/a" + } + } else { + return "" + } } diff --git a/cmd/glasskube/cmd/uninstall.go b/cmd/glasskube/cmd/uninstall.go index b27141478..89884f128 100644 --- a/cmd/glasskube/cmd/uninstall.go +++ b/cmd/glasskube/cmd/uninstall.go @@ -5,13 +5,16 @@ import ( "os" "github.com/glasskube/glasskube/internal/cliutils" - "github.com/glasskube/glasskube/internal/config" pkgClient "github.com/glasskube/glasskube/pkg/client" "github.com/glasskube/glasskube/pkg/list" "github.com/glasskube/glasskube/pkg/uninstall" "github.com/spf13/cobra" ) +var uninstallCmdOptions = struct { + ForceUninstall bool +}{} + var uninstallCmd = &cobra.Command{ Use: "uninstall [package-name]", Short: "Uninstall a package", @@ -27,7 +30,7 @@ var uninstallCmd = &cobra.Command{ os.Exit(1) return } - proceed := config.ForceUninstall || cliutils.YesNoPrompt( + proceed := uninstallCmdOptions.ForceUninstall || cliutils.YesNoPrompt( fmt.Sprintf( "%v will be removed from your cluster (%v). Are you sure?", pkgName, @@ -51,7 +54,7 @@ var uninstallCmd = &cobra.Command{ } func init() { - uninstallCmd.PersistentFlags().BoolVar(&config.ForceUninstall, "force", false, + uninstallCmd.PersistentFlags().BoolVar(&uninstallCmdOptions.ForceUninstall, "force", false, "skip the confirmation question and uninstall right away") RootCmd.AddCommand(uninstallCmd) } diff --git a/internal/cliutils/table.go b/internal/cliutils/table.go index 79f3ebaad..e515b5dc5 100644 --- a/internal/cliutils/table.go +++ b/internal/cliutils/table.go @@ -9,24 +9,28 @@ import ( "github.com/glasskube/glasskube/pkg/list" ) +var tabSep = "\t" + func PrintPackageTable( w io.Writer, - packages []*list.PackageTeaserWithStatus, + packages []*list.PackageWithStatus, cols []string, - getColsOfRow func(pkg *list.PackageTeaserWithStatus) []string, -) { + getColsOfRow func(pkg *list.PackageWithStatus) []string, +) error { tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0) - sep := "\t" - fmt.Fprintf(tw, "%s\n", strings.Join(cols, sep)) + fmt.Fprintln(tw, strings.Join(cols, tabSep)) var sb strings.Builder for _, pkg := range packages { colsOfRow := getColsOfRow(pkg) + if len(colsOfRow) != len(cols) { + return fmt.Errorf("column mapping func returned %v columns instead of %v", len(colsOfRow), len(cols)) + } for _, col := range colsOfRow { sb.WriteString(col) - sb.WriteString(sep) + sb.WriteString(tabSep) } - fmt.Fprintf(tw, "%s\n", sb.String()) + fmt.Fprintln(tw, sb.String()) sb.Reset() } - _ = tw.Flush() + return tw.Flush() } diff --git a/internal/config/config.go b/internal/config/config.go index 22bd918bb..9cf52b28a 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,11 +1,8 @@ package config var ( - Kubeconfig string - ForceUninstall bool - ListInstalledOnly bool - Verbose bool - Version = "dev" - Commit = "none" - Date = "unknown" + Kubeconfig string + Version = "dev" + Commit = "none" + Date = "unknown" ) diff --git a/internal/repo/types.go b/internal/repo/types.go index 6aaa7f615..6d596f331 100644 --- a/internal/repo/types.go +++ b/internal/repo/types.go @@ -17,4 +17,5 @@ type PackageRepoIndexItem struct { Name string `json:"name"` ShortDescription string `json:"shortDescription,omitempty"` IconUrl string `json:"iconUrl,omitempty"` + LatestVersion string `json:"latestVersion,omitempty"` } diff --git a/internal/web/components/pkg_overview_btn/controller.go b/internal/web/components/pkg_overview_btn/controller.go index 09eee5a21..b37ad4243 100644 --- a/internal/web/components/pkg_overview_btn/controller.go +++ b/internal/web/components/pkg_overview_btn/controller.go @@ -35,12 +35,12 @@ func Render(w io.Writer, tmpl *template.Template, pkgName string, status *client }) } -func ForPkgOverviewBtn(pkgTeaser *list.PackageTeaserWithStatus) *pkgOverviewBtnInput { - buttonId := getButtonId(pkgTeaser.PackageName) +func ForPkgOverviewBtn(pkgTeaser *list.PackageWithStatus) *pkgOverviewBtnInput { + buttonId := getButtonId(pkgTeaser.Name) return &pkgOverviewBtnInput{ ButtonId: buttonId, Swap: "", - PackageName: pkgTeaser.PackageName, + PackageName: pkgTeaser.Name, Status: pkgTeaser.Status, Manifest: pkgTeaser.InstalledManifest, } diff --git a/internal/web/templates/pages/packages.html b/internal/web/templates/pages/packages.html index 9b1944da4..759c4fc9e 100644 --- a/internal/web/templates/pages/packages.html +++ b/internal/web/templates/pages/packages.html @@ -7,18 +7,18 @@