Skip to content

Commit

Permalink
feat(cli): add --login option to 'pro list' which reuses the loft con…
Browse files Browse the repository at this point in the history
…fig to check the authentication status of pro instances
  • Loading branch information
pascalbreuninger committed Feb 8, 2024
1 parent 4f1c45e commit 0c7b98f
Showing 1 changed file with 88 additions and 6 deletions.
94 changes: 88 additions & 6 deletions cmd/pro/list.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package pro

import (
"bytes"
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"sort"
"strings"
"time"

"github.com/loft-sh/devpod/cmd/flags"
"github.com/loft-sh/devpod/pkg/binaries"
"github.com/loft-sh/devpod/pkg/config"
"github.com/loft-sh/devpod/pkg/provider"
"github.com/loft-sh/devpod/pkg/workspace"
Expand All @@ -21,6 +27,7 @@ type ListCmd struct {
flags.GlobalFlags

Output string
Login bool
}

// NewListCmd creates a new command
Expand All @@ -39,6 +46,7 @@ func NewListCmd(flags *flags.GlobalFlags) *cobra.Command {
}

listCmd.Flags().StringVar(&cmd.Output, "output", "plain", "The output format to use. Can be json or plain")
listCmd.Flags().BoolVar(&cmd.Login, "login", false, "Check if the user is logged into the pro instance")
return listCmd
}

Expand All @@ -57,24 +65,45 @@ func (cmd *ListCmd) Run(ctx context.Context) error {
if cmd.Output == "plain" {
tableEntries := [][]string{}
for _, proInstance := range proInstances {
tableEntries = append(tableEntries, []string{
entry := []string{
proInstance.Host,
proInstance.Provider,
time.Since(proInstance.CreationTimestamp.Time).Round(1 * time.Second).String(),
})
}
if cmd.Login {
err = checkLogin(ctx, devPodConfig, proInstance)
entry = append(entry, fmt.Sprintf("%t", err == nil))
}

tableEntries = append(tableEntries, entry)
}
sort.SliceStable(tableEntries, func(i, j int) bool {
return tableEntries[i][0] < tableEntries[j][0]
})

table.PrintTable(log.Default, []string{
tableHeaders := []string{
"Host",
"Provider",
"Age",
}, tableEntries)
}
if cmd.Login {
tableHeaders = append(tableHeaders, "Authenticated")
}

table.PrintTable(log.Default, tableHeaders, tableEntries)
} else if cmd.Output == "json" {
tableEntries := []*provider.ProInstance{}
tableEntries = append(tableEntries, proInstances...)
tableEntries := []*proTableEntry{}
for _, proInstance := range proInstances {
entry := &proTableEntry{ProInstance: proInstance}
if cmd.Login {
err = checkLogin(ctx, devPodConfig, proInstance)
isAuthenticated := err == nil
entry.Authenticated = &isAuthenticated
}

tableEntries = append(tableEntries, entry)
}

sort.SliceStable(tableEntries, func(i, j int) bool {
return tableEntries[i].Host < tableEntries[j].Host
})
Expand All @@ -89,3 +118,56 @@ func (cmd *ListCmd) Run(ctx context.Context) error {

return nil
}

type proTableEntry struct {
*provider.ProInstance

Authenticated *bool `json:"authenticated,omitempty"`
}

func checkLogin(ctx context.Context, devPodConfig *config.Config, proInstance *provider.ProInstance) error {
providerConfig, err := provider.LoadProviderConfig(devPodConfig.DefaultContext, proInstance.Provider)
if err != nil {
return err
}

providerBinaries, err := binaries.GetBinaries(devPodConfig.DefaultContext, providerConfig)
if err != nil {
return fmt.Errorf("get provider binaries: %w", err)
} else if providerBinaries[LOFT_PROVIDER_BINARY] == "" {
return fmt.Errorf("provider is missing %s binary", LOFT_PROVIDER_BINARY)
}

providerDir, err := provider.GetProviderDir(devPodConfig.DefaultContext, providerConfig.Name)
if err != nil {
return err
}

args := []string{
"login",
"--log-output=raw",
}

extraEnv := []string{
"LOFT_SKIP_VERSION_CHECK=true",
"LOFT_CONFIG=" + filepath.Join(providerDir, "loft-config.json"),
}

stdout := &bytes.Buffer{}

// start the command
loginCmd := exec.CommandContext(ctx, providerBinaries[LOFT_PROVIDER_BINARY], args...)
loginCmd.Env = os.Environ()
loginCmd.Env = append(loginCmd.Env, extraEnv...)
loginCmd.Stdout = stdout
err = loginCmd.Run()
if err != nil {
return fmt.Errorf("run login command: %w", err)
}

if stdout.Len() > 0 && strings.Contains(stdout.String(), "Not logged in") {
return fmt.Errorf("not logged into %s", proInstance.Host)
}

return nil
}

0 comments on commit 0c7b98f

Please sign in to comment.