From 6ffde8e55b9f12243fba5e819292875a70d5165a Mon Sep 17 00:00:00 2001 From: JcdeA Date: Tue, 17 Aug 2021 12:40:31 +0900 Subject: [PATCH] add some commands --- check/auth.go | 15 +++++ check/errors.go | 22 +++++++ cmd/alias.go | 46 ++++++++++++++ cmd/aliasProject.go | 95 +++++++++++++++++++++++++++++ cmd/login.go | 84 ++++++++++++++++++++++++-- cmd/project.go | 46 ++++++++++++++ cmd/projectLs.go | 63 +++++++++++++++++++ cmd/root.go | 59 +++++++++++++++--- cmd/ssh.go | 144 +++++++++++++++++++++++++++++++++++++++----- go.mod | 6 ++ go.sum | 62 +++++++++++++++++++ main.go | 4 +- request/request.go | 30 +++++++++ spinner/spinner.go | 30 +++++++++ types/alias.go | 36 +++++++++++ 15 files changed, 713 insertions(+), 29 deletions(-) create mode 100644 check/auth.go create mode 100644 check/errors.go create mode 100644 cmd/alias.go create mode 100644 cmd/aliasProject.go create mode 100644 cmd/project.go create mode 100644 cmd/projectLs.go create mode 100644 request/request.go create mode 100644 spinner/spinner.go create mode 100644 types/alias.go diff --git a/check/auth.go b/check/auth.go new file mode 100644 index 0000000..a4c85f8 --- /dev/null +++ b/check/auth.go @@ -0,0 +1,15 @@ +package check + +import ( + "os" + + "github.com/manifoldco/promptui" + "github.com/spf13/viper" +) + +func CheckSignin() { + if viper.Get("key") == nil { + println(promptui.IconBad + (" Please log in using `fhctl login` first.\n")) + os.Exit(0) + } +} diff --git a/check/errors.go b/check/errors.go new file mode 100644 index 0000000..be1c7db --- /dev/null +++ b/check/errors.go @@ -0,0 +1,22 @@ +package check + +import ( + "fmt" + "os" +) + +func CheckErr(err error, msg string) { + if err != nil { + if err.Error() == "^C" { + os.Exit(0) + } + if msg != "" { + fmt.Printf("%v: %v\n", msg, err) + return + } + if msg == "" { + fmt.Printf("Error: %v\n", err) + return + } + } +} diff --git a/cmd/alias.go b/cmd/alias.go new file mode 100644 index 0000000..04b7657 --- /dev/null +++ b/cmd/alias.go @@ -0,0 +1,46 @@ +/* +Copyright © 2021 JcdeA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package cmd + +import ( + "github.com/spf13/cobra" +) + +// aliasCmd represents the alias command +var aliasCmd = &cobra.Command{ + Use: "alias", + Short: "Alias resources for convenience", +} + +func init() { + rootCmd.AddCommand(aliasCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // aliasCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // aliasCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/aliasProject.go b/cmd/aliasProject.go new file mode 100644 index 0000000..5965161 --- /dev/null +++ b/cmd/aliasProject.go @@ -0,0 +1,95 @@ +/* +Copyright © 2021 JcdeA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package cmd + +import ( + "errors" + "fmt" + "strings" + + "github.com/jcdea/fhctl/check" + "github.com/jcdea/fhctl/request" + "github.com/jcdea/fhctl/types" + "github.com/manifoldco/promptui" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// aliasProjectCmd represents the aliasProject command +var aliasProjectCmd = &cobra.Command{ + Use: "project", + Short: "Alias a project", + + Run: func(cmd *cobra.Command, args []string) { + projectResp, err := request.GetProjects() + + var projectNames []string + for _, item := range projectResp.Projects { + projectNames = append(projectNames, item.Name) + } + check.CheckErr(err, "") + selectProject := promptui.Select{ + Label: "Select project", + Items: projectNames, + } + selectedIndex, selected, err := selectProject.Run() + check.CheckErr(err, "") + + validate := func(aliasName string) error { + + if strings.Contains(aliasName, ":") { + return errors.New("invalid name") + } + return nil + } + aliasPrompt := promptui.Prompt{ + Label: fmt.Sprintf("Name to alias \"%v\" to", selected), + Validate: validate, + } + + aliasName, err := aliasPrompt.Run() + check.CheckErr(err, "") + + viper.Set("alias."+aliasName, types.AliasItem{ + Id: projectResp.Projects[selectedIndex].Id, + Type: types.Project, + }) + + createOrWriteConfig(0600) + println(promptui.IconGood + " Successfully aliased resource!") + + }, +} + +func init() { + aliasCmd.AddCommand(aliasProjectCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // aliasProjectCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // aliasProjectCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/login.go b/cmd/login.go index fdc029b..26670f0 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -22,23 +22,95 @@ THE SOFTWARE. package cmd import ( + "errors" "fmt" + "net/mail" + "os" + "github.com/jcdea/aarch64-client-go" + "github.com/jcdea/fhctl/check" + "github.com/jcdea/fhctl/spinner" + "github.com/manifoldco/promptui" "github.com/spf13/cobra" + "github.com/spf13/viper" ) // loginCmd represents the login command var loginCmd = &cobra.Command{ Use: "login", Short: "Login to Fosshost", - Long: `A longer description that spans multiple lines and likely contains examples -and usage of using your command. For example: -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { - fmt.Println("login called") + validate := func(email string) error { + _, err := mail.ParseAddress(email) + if err != nil { + return errors.New("invalid email") + } + return nil + } + if viper.ConfigFileUsed() != "" { + choose := promptui.Select{ + Label: fmt.Sprintf("Configuration file found at %v", viper.ConfigFileUsed()), + Items: []string{"use existing configuration", "overwite config file"}, + } + i, _, err := choose.Run() + check.CheckErr(err, "") + + switch i { + case 0: + println("using existing configuration.") + os.Exit(0) + } + + } + + emailPrompt := promptui.Prompt{ + Label: "Your Email", + Validate: validate, + } + email, err := emailPrompt.Run() + check.CheckErr(err, "Prompt failed") + + PwPrompt := promptui.Prompt{ + Label: "Password", + Mask: '*', + } + pw, err := PwPrompt.Run() + + check.CheckErr(err, "Prompt failed") + + s, err := spinner.SpinnerWithMsg(spinner.SpinnerMsgs{Suffix: "Signing in", + SuccessMsg: "Successfully logged in!\n", + FailMsg: "Failed to sign in. Please try again.\n"}) + check.CheckErr(err, "") + + s.Start() + + client := aarch64.NewClient("") + + resp, err := client.Login(email, pw) + + check.CheckErr(err, "") + + if resp.Meta.Success { + + s.Stop() + configWriteSpinner, err := spinner.SpinnerWithMsg(spinner.SpinnerMsgs{ + Suffix: "Writing configuration file", + SuccessMsg: "Successfully saved configuration file!\n"}) + check.CheckErr(err, "") + configWriteSpinner.Start() + + viper.Set("email", email) + viper.Set("key", resp.Key) + createOrWriteConfig(0600) + + configWriteSpinner.Stop() + + } else { + s.StopFail() + } + }, } diff --git a/cmd/project.go b/cmd/project.go new file mode 100644 index 0000000..ebccbbf --- /dev/null +++ b/cmd/project.go @@ -0,0 +1,46 @@ +/* +Copyright © 2021 JcdeA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package cmd + +import ( + "github.com/spf13/cobra" +) + +// projectCmd represents the project command +var projectCmd = &cobra.Command{ + Use: "project", + Short: "Commands for managing projects", +} + +func init() { + rootCmd.AddCommand(projectCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // projectCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // projectCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/projectLs.go b/cmd/projectLs.go new file mode 100644 index 0000000..6f8560c --- /dev/null +++ b/cmd/projectLs.go @@ -0,0 +1,63 @@ +/* +Copyright © 2021 JcdeA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package cmd + +import ( + "fmt" + + "github.com/jcdea/aarch64-client-go" + "github.com/jcdea/fhctl/check" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// projectLsCmd represents the projectLs command +var projectLsCmd = &cobra.Command{ + Use: "ls", + Short: "List projects", + + Run: func(cmd *cobra.Command, args []string) { + check.CheckSignin() + + client := aarch64.NewClient(viper.GetString("key")) + resp, err := client.Projects() + + check.CheckErr(err, "") + for i, item := range resp.Projects { + fmt.Printf("%v: %v\n", i, item.Name) + } + }, +} + +func init() { + projectCmd.AddCommand(projectLsCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // projectLsCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // projectLsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/root.go b/cmd/root.go index 107c603..47b204c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -22,8 +22,11 @@ THE SOFTWARE. package cmd import ( - "fmt" + "io/fs" "os" + "os/signal" + "path/filepath" + "syscall" "github.com/spf13/cobra" @@ -32,11 +35,12 @@ import ( var cfgFile string +const API_URL = "https://console.aarch64.com/api" + // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "fhctl", Short: "Administer the Fosshost & aarch64.com infrastructure from the terminal", - Long: `Administer the Fosshost & aarch64.com infrastructure from the terminal`, // Uncomment the following line if your bare application // has an action associated with it: // Run: func(cmd *cobra.Command, args []string) { }, @@ -49,6 +53,19 @@ func Execute() { } func init() { + + signalChannel := make(chan os.Signal, 1) + signal.Notify(signalChannel, os.Interrupt, syscall.SIGINT) + go func() { + sig := <-signalChannel + switch sig { + case os.Interrupt: + os.Exit(0) + case syscall.SIGINT: + os.Exit(0) + } + }() + cobra.OnInitialize(initConfig) // Here you will define your flags and configuration settings. @@ -57,9 +74,6 @@ func init() { rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.fhctl.yaml)") - // Cobra also supports local flags, which will only run - // when this action is called directly. - rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } // initConfig reads in config file and ENV variables if set. @@ -67,6 +81,7 @@ func initConfig() { if cfgFile != "" { // Use config file from the flag. viper.SetConfigFile(cfgFile) + } else { // Find home directory. home, err := os.UserHomeDir() @@ -74,14 +89,42 @@ func initConfig() { // Search config in home directory with name ".fhctl" (without extension). viper.AddConfigPath(home) - viper.SetConfigType("yaml") + viper.SetConfigType("json") viper.SetConfigName(".fhctl") } viper.AutomaticEnv() // read in environment variables that match // If a config file is found, read it in. - if err := viper.ReadInConfig(); err == nil { - fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) + viper.ReadInConfig() + +} + +func createOrWriteConfig(mode fs.FileMode) (err error) { + if viper.ConfigFileUsed() == "" { + home, err := os.UserHomeDir() + cobra.CheckErr(err) + + configPath := filepath.Join(home, "/fhctl.json") + + _, err = os.Stat(configPath) + if !os.IsExist(err) { + if _, err := os.Create(configPath); err != nil { + cobra.CheckErr(err) + } + } + if err := viper.SafeWriteConfig(); err != nil { + cobra.CheckErr(err) + } + + err = os.Chmod(viper.ConfigFileUsed(), mode) + if err != nil { + return err + } + } else { + if err = viper.WriteConfig(); err != nil { + cobra.CheckErr(err) + } } + return nil } diff --git a/cmd/ssh.go b/cmd/ssh.go index d6d27f8..0136d69 100644 --- a/cmd/ssh.go +++ b/cmd/ssh.go @@ -23,35 +23,151 @@ package cmd import ( "fmt" + "net" + "os" + "os/exec" + "strings" + "github.com/jcdea/aarch64-client-go" + "github.com/jcdea/fhctl/check" + "github.com/jcdea/fhctl/request" + "github.com/jcdea/fhctl/types" + "github.com/manifoldco/promptui" "github.com/spf13/cobra" ) // sshCmd represents the ssh command var sshCmd = &cobra.Command{ Use: "ssh", - Short: "A brief description of your command", - Long: `A longer description that spans multiple lines and likely contains examples -and usage of using your command. For example: + Short: "SSH into a vm", -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { - fmt.Println("ssh called") + // We already check if user is signed in at GetProjects + + if len(args) != 0 && args[0] != "" { + err := sshCmdWithAlias(args) + if err != nil { + println(fmt.Sprintf("No alias found for\"%v\"\n", args[0])) + } + } + + projectResp, err := request.GetProjects() + check.CheckErr(err, "Failed to retrieve project list") + + var projectNames []string + for _, item := range projectResp.Projects { + projectNames = append(projectNames, item.Name) + } + + check.CheckErr(err, "") + selectProject := promptui.Select{ + Label: "Select project", + Items: projectNames, + } + selectedProjectIndex, _, err := selectProject.Run() + check.CheckErr(err, "") + + selectedProject := projectResp.Projects[selectedProjectIndex] + + var VMNames []string + for _, item := range selectedProject.VMs { + VMNames = append(VMNames, item.Id) + + } + + check.CheckErr(err, "") + selectVM := promptui.Select{ + Label: "Select VM", + Items: VMNames, + } + selectedVMIndex, _, err := selectVM.Run() + check.CheckErr(err, "") + + sshVM(selectedProject.VMs[selectedVMIndex]) + }, } +// Search for alias, then ssh if alias is found. +// if not found: returns not found error +func sshCmdWithAlias(args []string) error { + var vms []aarch64.VM + + project, err := types.SearchProjectAlias(args[0]) + if err != nil { + return err + } + + println(fmt.Sprintf("Using alias %v=%v\n", args[0], project.Id)) + + projectResp, err := request.GetProjects() + check.CheckErr(err, "Failed to retrieve project list") + + for _, item := range projectResp.Projects { + if item.Id == project.Id { + vms = item.VMs + + } + + } + var VMNames []string + for _, item := range vms { + VMNames = append(VMNames, item.Id) + } + + selectVM := promptui.Select{Label: "Select VM", Items: VMNames} + index, _, err := selectVM.Run() + check.CheckErr(err, "") + + sshVM(vms[index]) + return nil + +} + func init() { rootCmd.AddCommand(sshCmd) +} + +// SSH into VM +func sshVM(vm aarch64.VM) { + if ipv6able() { + cmd := exec.Command("/usr/bin/ssh", strings.Split(fmt.Sprintf("root@%v", vm.Address), "/")[0]) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err := cmd.Run() + if err != nil { + if err.Error() != "exit status 130" { + check.CheckErr(err, "") + } + } + + } else { + fmt.Printf("establishing connection to %v through a SSH jump server...\n\n", strings.Split(fmt.Sprintf("root@%v", vm.Address), "/")[0]) + cmd := exec.Command("/usr/bin/ssh", "-J", fmt.Sprintf("jump@%v%v.infra.aarch64.com", vm.PoP, vm.Host), strings.Split(fmt.Sprintf("root@%v", vm.Address), "/")[0]) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr - // Here you will define your flags and configuration settings. + err := cmd.Run() // add error checking - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // sshCmd.PersistentFlags().String("foo", "", "A help for foo") + if err != nil { + if err.Error() != "exit status 130" { + check.CheckErr(err, "") + } + } + + } + +} - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // sshCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +// Does the client support ipv6? +func ipv6able() bool { + _, err := net.Dial("tcp", "2606:4700:4700::1111") + if err != nil { + println("ipv6 not available") + return false + } + return true } diff --git a/go.mod b/go.mod index 5db7a4c..5242d2d 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,12 @@ module github.com/jcdea/fhctl go 1.16 require ( + github.com/briandowns/spinner v1.16.0 // indirect + github.com/charmbracelet/lipgloss v0.3.0 // indirect + github.com/jcdea/aarch64-client-go v0.0.4 + github.com/manifoldco/promptui v0.8.0 + github.com/melbahja/goph v1.2.1 // indirect github.com/spf13/cobra v1.2.1 github.com/spf13/viper v1.8.1 + github.com/theckman/yacspin v0.8.0 // indirect ) diff --git a/go.sum b/go.sum index d6ff538..db1ae87 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,13 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/briandowns/spinner v1.16.0 h1:DFmp6hEaIx2QXXuqSJmtfSBSAjRmpGiKG6ip2Wm/yOs= +github.com/briandowns/spinner v1.16.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/charmbracelet/lipgloss v0.3.0 h1:5MysOD6sHr4RP4jkZNWGVIul5GKoOsP12NgbgXPvAlA= +github.com/charmbracelet/lipgloss v0.3.0/go.mod h1:VkhdBS2eNAmRkTwRKLJCFhCOVkjntMusBDxv7TXahuk= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -66,6 +71,7 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -164,23 +170,55 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jcdea/aarch64-client-go v0.0.0-20210815101444-bac34c5aea85 h1:OvYame6zf9SWSY+LiEDrK3Xwa6RbMMWF7tkbo6lWUNo= +github.com/jcdea/aarch64-client-go v0.0.0-20210815101444-bac34c5aea85/go.mod h1:wpof6DO14ii9ItkM/zZdVClclN7b4LQdctOAGrhlEss= +github.com/jcdea/aarch64-client-go v0.0.1 h1:zdZeATO1c6ne45Q2lPoHy/SxD3MfWM4DcXeQgbb2ZcM= +github.com/jcdea/aarch64-client-go v0.0.1/go.mod h1:wpof6DO14ii9ItkM/zZdVClclN7b4LQdctOAGrhlEss= +github.com/jcdea/aarch64-client-go v0.0.2 h1:c2KYTmpbofQdpnFXtGFZeCBWTXySMpjH4vm49C3c3RU= +github.com/jcdea/aarch64-client-go v0.0.2/go.mod h1:wpof6DO14ii9ItkM/zZdVClclN7b4LQdctOAGrhlEss= +github.com/jcdea/aarch64-client-go v0.0.3 h1:rr72ioqm9Q8/liWNwhJDhNE534nMzjGISyZqxDhSldw= +github.com/jcdea/aarch64-client-go v0.0.3/go.mod h1:wpof6DO14ii9ItkM/zZdVClclN7b4LQdctOAGrhlEss= +github.com/jcdea/aarch64-client-go v0.0.4 h1:mRV00Ma7kj80pzSlXE8AykNkkktU1WQMPEmvhvG2OmU= +github.com/jcdea/aarch64-client-go v0.0.4/go.mod h1:wpof6DO14ii9ItkM/zZdVClclN7b4LQdctOAGrhlEss= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= +github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo= +github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= +github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/melbahja/goph v1.2.1 h1:msPxbLxf1PnbxRQGhv9mVqm7T16tPo3wqLWah0hJhKQ= +github.com/melbahja/goph v1.2.1/go.mod h1:y+wS4c0UtZOLSwNz6ktaGiyUeYZBeT1e8PiSz+YK77o= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -194,15 +232,27 @@ github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 h1:y1p/ycavWjGT9FnmSjdbWUlLGvcxrY0Rw3ATltrxOhk= +github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= +github.com/muesli/termenv v0.8.1 h1:9q230czSP3DHVpkaPDXGp0TOfAwyjyYwXlUCQxQSaBk= +github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/fQSFKo0= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.12.0 h1:/f3b24xrDhkhddlaobPe2JgBqfdt+gC/NYl0QY9IOuI= +github.com/pkg/sftp v1.12.0/go.mod h1:fUqqXB5vEgVCZ131L+9say31RAri6aF6KDViawhxKK8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -235,6 +285,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/theckman/yacspin v0.8.0 h1:9LA2kUol1/+eH5m/ptlbYCrnCEfLCaX4Xn+5tK/AprI= +github.com/theckman/yacspin v0.8.0/go.mod h1:K1H1naXCpDytqETpvmlxWzAq8BbOMy3Wrd0iy0ZNzRI= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -260,6 +312,9 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA= +golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -357,7 +412,9 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -367,9 +424,11 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -386,6 +445,7 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -396,7 +456,9 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/main.go b/main.go index 599470f..6e76431 100644 --- a/main.go +++ b/main.go @@ -21,7 +21,9 @@ THE SOFTWARE. */ package main -import "github.com/jcdea/fhctl/cmd" +import ( + "github.com/jcdea/fhctl/cmd" +) func main() { cmd.Execute() diff --git a/request/request.go b/request/request.go new file mode 100644 index 0000000..e700e01 --- /dev/null +++ b/request/request.go @@ -0,0 +1,30 @@ +package request + +import ( + "github.com/jcdea/aarch64-client-go" + + "github.com/jcdea/fhctl/check" + "github.com/jcdea/fhctl/spinner" + "github.com/spf13/viper" +) + +func GetProjects() (response aarch64.ProjectsResponse, err error) { + check.CheckSignin() + s, err := spinner.SpinnerWithMsg(spinner.SpinnerMsgs{ + Suffix: "Retrieving project list", + SuccessMsg: "Success", + FailMsg: "Failed retrieving project list", + }) + check.CheckErr(err, "") + + s.Start() + client := aarch64.NewClient(viper.GetString("key")) + + resp, err := client.Projects() + if err != nil { + s.StopFail() + return aarch64.ProjectsResponse{}, err + } + s.Stop() + return resp, nil +} diff --git a/spinner/spinner.go b/spinner/spinner.go new file mode 100644 index 0000000..7cb3a3c --- /dev/null +++ b/spinner/spinner.go @@ -0,0 +1,30 @@ +package spinner + +import ( + "time" + + "github.com/theckman/yacspin" +) + +func SpinnerWithMsg(m SpinnerMsgs) (*yacspin.Spinner, error) { + var spinnerCfg = yacspin.Config{ + Frequency: 100 * time.Millisecond, + CharSet: yacspin.CharSets[11], + Suffix: " " + m.Suffix, + SuffixAutoColon: true, + StopCharacter: "✔", + StopFailCharacter: "✗", + StopFailMessage: m.FailMsg, + StopMessage: m.SuccessMsg, + StopFailColors: []string{"fgRed"}, + StopColors: []string{"fgGreen"}, + } + return yacspin.New(spinnerCfg) +} + +type SpinnerMsgs struct { + Suffix string + + SuccessMsg string + FailMsg string +} diff --git a/types/alias.go b/types/alias.go new file mode 100644 index 0000000..d644352 --- /dev/null +++ b/types/alias.go @@ -0,0 +1,36 @@ +package types + +import ( + "errors" + "fmt" + + "github.com/spf13/viper" +) + +type AliasItem struct { + Type resourceType + Id string +} + +type resourceType int + +const ( + Project resourceType = iota + Vm + Proxy + Pop + Plan +) + +// Search for project alias +func SearchProjectAlias(q string) (item AliasItem, err error) { + var itemType resourceType + + id := viper.GetString(fmt.Sprintf("alias.%v.id", q)) + itemType = resourceType(viper.GetInt(fmt.Sprintf("alias.%v.id", q))) + if id == "" { + return AliasItem{}, errors.New("not found") + } + + return AliasItem{Id: id, Type: itemType}, nil +}