Skip to content

Commit

Permalink
refactor get to use subcommands instead of a manual case statement
Browse files Browse the repository at this point in the history
  • Loading branch information
laverya committed Dec 1, 2021
1 parent c164414 commit 1b95233
Show file tree
Hide file tree
Showing 5 changed files with 397 additions and 301 deletions.
171 changes: 171 additions & 0 deletions cmd/kots/cli/get-apps.go
@@ -0,0 +1,171 @@
package cli

import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
"github.com/replicatedhq/kots/pkg/api/handlers/types"
"github.com/replicatedhq/kots/pkg/auth"
"github.com/replicatedhq/kots/pkg/k8sutil"
"github.com/replicatedhq/kots/pkg/logger"
"github.com/replicatedhq/kots/pkg/print"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"io/ioutil"
"net/http"
"os"
)

func GetAppsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "apps",
Aliases: []string{"app"},
Short: "Get apps",
Long: "",
SilenceUsage: false,
SilenceErrors: false,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlags(cmd.Flags())
},
RunE: getAppsCmd,
}

cmd.Flags().StringP("output", "o", "", "output format. supported values: json")

return cmd
}

func getAppsCmd(cmd *cobra.Command, args []string) error {
v := viper.GetViper()

log := logger.NewCLILogger()

stopCh := make(chan struct{})
defer close(stopCh)

clientset, err := k8sutil.GetClientset()
if err != nil {
return errors.Wrap(err, "failed to get clientset")
}

namespace := v.GetString("namespace")
if err := validateNamespace(namespace); err != nil {
return errors.Wrap(err, "failed to validate namespace")
}

podName, err := k8sutil.FindKotsadm(clientset, namespace)
if err != nil {
return errors.Wrap(err, "failed to find kotsadm pod")
}

localPort, errChan, err := k8sutil.PortForward(0, 3000, namespace, podName, false, stopCh, log)
if err != nil {
log.FinishSpinnerWithError()
return errors.Wrap(err, "failed to start port forwarding")
}

go func() {
select {
case err := <-errChan:
if err != nil {
log.Error(err)
}
case <-stopCh:
}
}()

authSlug, err := auth.GetOrCreateAuthSlug(clientset, namespace)
if err != nil {
log.FinishSpinnerWithError()
log.Info("Unable to authenticate to the Admin Console running in the %s namespace. Ensure you have read access to secrets in this namespace and try again.", namespace)
if v.GetBool("debug") {
return errors.Wrap(err, "failed to get kotsadm auth slug")
}
os.Exit(2) // not returning error here as we don't want to show the entire stack trace to normal users
}

url := fmt.Sprintf("http://localhost:%d/api/v1/apps", localPort)
apps, err := getApps(url, authSlug)
if err != nil {
return errors.Wrap(err, "failed to get apps")
}

printableApps := make([]print.App, 0)
for _, app := range apps.Apps {
versionLabel := ""
for _, d := range app.Downstreams {
if d.CurrentVersion != nil {
versionLabel = d.CurrentVersion.VersionLabel
break
}
}
url := fmt.Sprintf("http://localhost:%d/api/v1/app/%s/status", localPort, app.Slug)
appStatus, err := getAppStatus(url, authSlug)
if err != nil {
return errors.Wrapf(err, "failed to get app status for %s", app.Slug)
}
printableApps = append(printableApps, print.App{
Slug: app.Slug,
State: string(appStatus.AppStatus.State),
VersionLabel: versionLabel,
})
}

print.Apps(printableApps, v.GetString("output"))

return nil
}

func getApps(url string, authSlug string) (*types.ListAppsResponse, error) {
newReq, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, errors.Wrap(err, "failed to create request")
}
newReq.Header.Add("Content-Type", "application/json")
newReq.Header.Add("Authorization", authSlug)

resp, err := http.DefaultClient.Do(newReq)
if err != nil {
return nil, errors.Wrap(err, "failed to execute request")
}
defer resp.Body.Close()

b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrap(err, "failed to read")
}

apps := &types.ListAppsResponse{}
if err := json.Unmarshal(b, apps); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal apps")
}

return apps, nil
}

func getAppStatus(url string, authSlug string) (*types.AppStatusResponse, error) {
newReq, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, errors.Wrap(err, "failed to create request")
}
newReq.Header.Add("Content-Type", "application/json")
newReq.Header.Add("Authorization", authSlug)

resp, err := http.DefaultClient.Do(newReq)
if err != nil {
return nil, errors.Wrap(err, "failed to execute request")
}
defer resp.Body.Close()

b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrap(err, "failed to read")
}

status := &types.AppStatusResponse{}
if err := json.Unmarshal(b, status); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal status")
}

return status, nil
}
44 changes: 44 additions & 0 deletions cmd/kots/cli/get-backups.go
@@ -0,0 +1,44 @@
package cli

import (
"github.com/pkg/errors"
"github.com/replicatedhq/kots/pkg/print"
"github.com/replicatedhq/kots/pkg/snapshot"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func GetBackupsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "backups",
Aliases: []string{"backup"},
Short: "Get backups",
Long: "",
SilenceUsage: false,
SilenceErrors: false,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlags(cmd.Flags())
},
RunE: getBackupsCmd,
}

cmd.Flags().StringP("output", "o", "", "output format. supported values: json")

return cmd
}

func getBackupsCmd(cmd *cobra.Command, args []string) error {
v := viper.GetViper()

options := snapshot.ListInstanceBackupsOptions{
Namespace: v.GetString("namespace"),
}
backups, err := snapshot.ListInstanceBackups(cmd.Context(), options)
if err != nil {
return errors.Wrap(err, "failed to list instance backups")
}

print.Backups(backups, v.GetString("output"))

return nil
}
133 changes: 133 additions & 0 deletions cmd/kots/cli/get-config.go
@@ -0,0 +1,133 @@
package cli

import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
"github.com/replicatedhq/kots/pkg/auth"
"github.com/replicatedhq/kots/pkg/handlers"
"github.com/replicatedhq/kots/pkg/k8sutil"
"github.com/replicatedhq/kots/pkg/logger"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"io/ioutil"
"net/http"
"os"
"sigs.k8s.io/yaml"
)

func GetConfigCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "config --sequence=1 --appslug=my-app",
Short: "Get config values for an application",
Long: "",
SilenceUsage: false,
SilenceErrors: false,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlags(cmd.Flags())
},
RunE: getConfigCmd,
}

cmd.Flags().Int("sequence", -1, "app sequence to retrieve config for")
cmd.Flags().String("appslug", "", "app slug to retrieve config for")

return cmd
}

func getConfigCmd(cmd *cobra.Command, args []string) error {
v := viper.GetViper()

log := logger.NewCLILogger()

stopCh := make(chan struct{})
defer close(stopCh)

clientset, err := k8sutil.GetClientset()
if err != nil {
return errors.Wrap(err, "failed to get clientset")
}

namespace := v.GetString("namespace")
if err := validateNamespace(namespace); err != nil {
return errors.Wrap(err, "failed to validate namespace")
}

podName, err := k8sutil.FindKotsadm(clientset, namespace)
if err != nil {
return errors.Wrap(err, "failed to find kotsadm pod")
}

localPort, errChan, err := k8sutil.PortForward(0, 3000, namespace, podName, false, stopCh, log)
if err != nil {
log.FinishSpinnerWithError()
return errors.Wrap(err, "failed to start port forwarding")
}

go func() {
select {
case err := <-errChan:
if err != nil {
log.Error(err)
}
case <-stopCh:
}
}()

authSlug, err := auth.GetOrCreateAuthSlug(clientset, namespace)
if err != nil {
log.FinishSpinnerWithError()
log.Info("Unable to authenticate to the Admin Console running in the %s namespace. Ensure you have read access to secrets in this namespace and try again.", namespace)
if v.GetBool("debug") {
return errors.Wrap(err, "failed to get kotsadm auth slug")
}
os.Exit(2) // not returning error here as we don't want to show the entire stack trace to normal users
}

url := fmt.Sprintf("http://localhost:%d/api/v1/app/%s/config/%d", localPort, v.GetString("appslug"), v.GetInt("sequence"))
config, err := getConfig(url, authSlug)
if err != nil {
return errors.Wrap(err, "failed to get config")
}

values := handlers.ConfigGroupToValues(config.ConfigGroups)
configYaml, err := yaml.Marshal(values)
if err != nil {
return errors.Wrap(err, "failed to marshal config")
}

fmt.Print(string(configYaml))

return nil
}

func getConfig(url string, authSlug string) (*handlers.CurrentAppConfigResponse, error) {
newReq, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, errors.Wrap(err, "failed to create request")
}
newReq.Header.Add("Content-Type", "application/json")
newReq.Header.Add("Authorization", authSlug)

resp, err := http.DefaultClient.Do(newReq)
if err != nil {
return nil, errors.Wrap(err, "failed to execute request")
}
defer resp.Body.Close()

b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrap(err, "failed to read")
}

config := &handlers.CurrentAppConfigResponse{}
if err := json.Unmarshal(b, config); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal status")
}

if !config.Success {
return nil, fmt.Errorf("failed to get config: %s", config.Error)
}

return config, nil
}
44 changes: 44 additions & 0 deletions cmd/kots/cli/get-restores.go
@@ -0,0 +1,44 @@
package cli

import (
"github.com/pkg/errors"
"github.com/replicatedhq/kots/pkg/print"
"github.com/replicatedhq/kots/pkg/snapshot"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func GetRestoresCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "restores",
Aliases: []string{"restore"},
Short: "Get restores",
Long: "",
SilenceUsage: false,
SilenceErrors: false,
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlags(cmd.Flags())
},
RunE: getRestoresCmd,
}

cmd.Flags().StringP("output", "o", "", "output format. supported values: json")

return cmd
}

func getRestoresCmd(cmd *cobra.Command, args []string) error {
v := viper.GetViper()

options := snapshot.ListInstanceRestoresOptions{
Namespace: v.GetString("namespace"),
}
restores, err := snapshot.ListInstanceRestores(cmd.Context(), options)
if err != nil {
return errors.Wrap(err, "failed to list instance restores")
}

print.Restores(restores, v.GetString("output"))

return nil
}

0 comments on commit 1b95233

Please sign in to comment.