diff --git a/.mise.toml b/.mise.toml index 1bfbde4..8778eb3 100644 --- a/.mise.toml +++ b/.mise.toml @@ -6,6 +6,9 @@ go = "1.25" [env] CGO_ENABLED=0 LGFLAGS="-extldflags '-static'" +SKPR_API="localhost:50051?insecure=true" +SKPR_SSH="localhost:2222" +SKPR_PROJECT="drupal" [tasks.vendor] description = "Vendor resets the main module's vendor directory to include all packages needed to build the application" diff --git a/cmd/skpr-rsh/main.go b/cmd/skpr-rsh/main.go index 8310ee2..17941a9 100644 --- a/cmd/skpr-rsh/main.go +++ b/cmd/skpr-rsh/main.go @@ -1,9 +1,10 @@ package main import ( + "context" "os" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/client/ssh" ) @@ -24,7 +25,7 @@ import ( // // See the "skpr rsync" command for how this is implemented. func main() { - client, _, err := wfclient.NewFromFile() + _, client, err := client.New(context.TODO()) if err != nil { panic(err) } diff --git a/cmd/skpr/alias/delete/command.go b/cmd/skpr/alias/delete/command.go index 396af58..04c1e65 100644 --- a/cmd/skpr/alias/delete/command.go +++ b/cmd/skpr/alias/delete/command.go @@ -3,7 +3,7 @@ package delete import ( "github.com/spf13/cobra" - v1delete "github.com/skpr/cli/internal/command/v1/alias/delete" + v1delete "github.com/skpr/cli/internal/command/alias/delete" ) var ( @@ -31,7 +31,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Alias = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/alias/list/command.go b/cmd/skpr/alias/list/command.go index e92f4fe..955e15e 100644 --- a/cmd/skpr/alias/list/command.go +++ b/cmd/skpr/alias/list/command.go @@ -3,7 +3,7 @@ package list import ( "github.com/spf13/cobra" - v1list "github.com/skpr/cli/internal/command/v1/alias/list" + v1list "github.com/skpr/cli/internal/command/alias/list" ) var ( diff --git a/cmd/skpr/alias/set/command.go b/cmd/skpr/alias/set/command.go index 2828238..c5de8d9 100644 --- a/cmd/skpr/alias/set/command.go +++ b/cmd/skpr/alias/set/command.go @@ -3,7 +3,7 @@ package set import ( "github.com/spf13/cobra" - v1set "github.com/skpr/cli/internal/command/v1/alias/set" + v1set "github.com/skpr/cli/internal/command/alias/set" ) var ( diff --git a/cmd/skpr/backup/create/command.go b/cmd/skpr/backup/create/command.go index cd3c691..a770c2c 100644 --- a/cmd/skpr/backup/create/command.go +++ b/cmd/skpr/backup/create/command.go @@ -1,11 +1,10 @@ package create import ( + v1create "github.com/skpr/cli/internal/command/backup/create" "time" "github.com/spf13/cobra" - - v1create "github.com/skpr/cli/internal/command/v1/backup/create" ) var ( @@ -33,7 +32,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/backup/list/command.go b/cmd/skpr/backup/list/command.go index ba57806..97035e1 100644 --- a/cmd/skpr/backup/list/command.go +++ b/cmd/skpr/backup/list/command.go @@ -1,9 +1,8 @@ package list import ( + v1list "github.com/skpr/cli/internal/command/backup/list" "github.com/spf13/cobra" - - v1list "github.com/skpr/cli/internal/command/v1/backup/list" ) var ( @@ -34,7 +33,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/config/delete/command.go b/cmd/skpr/config/delete/command.go index de712d5..834f776 100644 --- a/cmd/skpr/config/delete/command.go +++ b/cmd/skpr/config/delete/command.go @@ -1,9 +1,8 @@ package delete import ( + v1delete "github.com/skpr/cli/internal/command/config/delete" "github.com/spf13/cobra" - - v1delete "github.com/skpr/cli/internal/command/v1/config/delete" ) var ( @@ -29,7 +28,7 @@ func NewCommand() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] command.Key = args[1] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/config/get/command.go b/cmd/skpr/config/get/command.go index 43734e1..d144a12 100644 --- a/cmd/skpr/config/get/command.go +++ b/cmd/skpr/config/get/command.go @@ -1,9 +1,8 @@ package get import ( + v1get "github.com/skpr/cli/internal/command/config/get" "github.com/spf13/cobra" - - v1get "github.com/skpr/cli/internal/command/v1/config/get" ) var ( @@ -29,7 +28,7 @@ func NewCommand() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] command.Key = args[1] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/config/list/command.go b/cmd/skpr/config/list/command.go index e60bbd7..073306c 100644 --- a/cmd/skpr/config/list/command.go +++ b/cmd/skpr/config/list/command.go @@ -1,9 +1,8 @@ package list import ( + v1list "github.com/skpr/cli/internal/command/config/list" "github.com/spf13/cobra" - - v1list "github.com/skpr/cli/internal/command/v1/config/list" ) const ( @@ -39,7 +38,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/config/set/command.go b/cmd/skpr/config/set/command.go index d37d681..61ad7e1 100644 --- a/cmd/skpr/config/set/command.go +++ b/cmd/skpr/config/set/command.go @@ -1,9 +1,8 @@ package set import ( + v1set "github.com/skpr/cli/internal/command/config/set" "github.com/spf13/cobra" - - v1set "github.com/skpr/cli/internal/command/v1/config/set" ) var ( @@ -30,7 +29,7 @@ func NewCommand() *cobra.Command { command.Environment = args[0] command.Key = args[1] command.Value = args[2] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/create/command.go b/cmd/skpr/create/command.go index c770aa3..4926eb5 100644 --- a/cmd/skpr/create/command.go +++ b/cmd/skpr/create/command.go @@ -1,9 +1,8 @@ package create import ( + v1create "github.com/skpr/cli/internal/command/create" "github.com/spf13/cobra" - - v1create "github.com/skpr/cli/internal/command/v1/create" ) var ( @@ -28,7 +27,7 @@ func NewCommand() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] command.Version = args[1] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/delete/command.go b/cmd/skpr/delete/command.go index 8651e12..b6c2dcf 100644 --- a/cmd/skpr/delete/command.go +++ b/cmd/skpr/delete/command.go @@ -1,9 +1,8 @@ package delete import ( + v1delete "github.com/skpr/cli/internal/command/delete" "github.com/spf13/cobra" - - v1delete "github.com/skpr/cli/internal/command/v1/delete" ) var ( @@ -20,7 +19,7 @@ func NewCommand() *cobra.Command { command := v1delete.Command{} cmd := &cobra.Command{ - Use: "delete ", + Use: "delete [environment]", Args: cobra.ExactArgs(1), DisableFlagsInUseLine: true, Short: "Delete a previously deployed environment", @@ -28,7 +27,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Name = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/deploy/command.go b/cmd/skpr/deploy/command.go index e8bb65c..e8358cc 100644 --- a/cmd/skpr/deploy/command.go +++ b/cmd/skpr/deploy/command.go @@ -1,9 +1,8 @@ package deploy import ( + v1deploy "github.com/skpr/cli/internal/command/deploy" "github.com/spf13/cobra" - - v1deploy "github.com/skpr/cli/internal/command/v1/deploy" ) var ( @@ -29,7 +28,7 @@ func NewCommand() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] command.Version = args[1] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/exec/command.go b/cmd/skpr/exec/command.go index cd324b0..cddf4cc 100644 --- a/cmd/skpr/exec/command.go +++ b/cmd/skpr/exec/command.go @@ -1,9 +1,8 @@ package exec import ( + v1exec "github.com/skpr/cli/internal/command/exec" "github.com/spf13/cobra" - - v1exec "github.com/skpr/cli/internal/command/v1/exec" ) var ( @@ -29,7 +28,7 @@ func NewCommand() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] command.Command = args[1:] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/info/command.go b/cmd/skpr/info/command.go index 96de3e3..8332a91 100644 --- a/cmd/skpr/info/command.go +++ b/cmd/skpr/info/command.go @@ -1,9 +1,8 @@ package info import ( + v1info "github.com/skpr/cli/internal/command/info" "github.com/spf13/cobra" - - v1info "github.com/skpr/cli/internal/command/v1/info" ) var ( @@ -28,7 +27,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Name = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/list/command.go b/cmd/skpr/list/command.go index 6ed6ad3..36ce2cd 100644 --- a/cmd/skpr/list/command.go +++ b/cmd/skpr/list/command.go @@ -1,9 +1,8 @@ package list import ( + v1list "github.com/skpr/cli/internal/command/list" "github.com/spf13/cobra" - - v1list "github.com/skpr/cli/internal/command/v1/list" ) var ( @@ -26,10 +25,8 @@ func NewCommand() *cobra.Command { Short: "Overview of all environments and their current status", Long: cmdLong, Example: cmdExample, - Run: func(cmd *cobra.Command, args []string) { - if err := command.Run(); err != nil { - panic(err) - } + RunE: func(cmd *cobra.Command, args []string) error { + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/login/command.go b/cmd/skpr/login/command.go index c46ad81..dd96408 100644 --- a/cmd/skpr/login/command.go +++ b/cmd/skpr/login/command.go @@ -1,9 +1,8 @@ package login import ( + v1login "github.com/skpr/cli/internal/command/login" "github.com/spf13/cobra" - - v1login "github.com/skpr/cli/internal/command/v1/login" ) var ( @@ -22,12 +21,11 @@ func NewCommand() *cobra.Command { Use: "login", DisableFlagsInUseLine: true, Short: "Login to the Skpr cluster.", - Args: cobra.ExactArgs(1), + Args: cobra.NoArgs, Long: cmdLong, Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { - command.Callback = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/logout/command.go b/cmd/skpr/logout/command.go index b539817..aefd2fc 100644 --- a/cmd/skpr/logout/command.go +++ b/cmd/skpr/logout/command.go @@ -1,9 +1,8 @@ package logout import ( + v1logout "github.com/skpr/cli/internal/command/logout" "github.com/spf13/cobra" - - v1logout "github.com/skpr/cli/internal/command/v1/logout" ) var ( @@ -22,11 +21,11 @@ func NewCommand() *cobra.Command { Use: "logout", DisableFlagsInUseLine: true, Short: "Initiate a logout event from the Skpr hosting plstform", + Args: cobra.NoArgs, Long: cmdLong, Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { - command.Callback = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/main.go b/cmd/skpr/main.go index 342331c..ad79351 100644 --- a/cmd/skpr/main.go +++ b/cmd/skpr/main.go @@ -2,11 +2,11 @@ package main import ( "context" + "os" + "github.com/charmbracelet/fang" "github.com/charmbracelet/lipgloss/v2" - "github.com/skpr/cli/internal/color" "github.com/spf13/cobra" - "os" "github.com/skpr/cli/cmd/skpr/alias" "github.com/skpr/cli/cmd/skpr/backup" @@ -27,6 +27,7 @@ import ( "github.com/skpr/cli/cmd/skpr/rsync" "github.com/skpr/cli/cmd/skpr/shell" "github.com/skpr/cli/cmd/skpr/version" + "github.com/skpr/cli/internal/color" ) const cmdExample = ` diff --git a/cmd/skpr/mysql/backup/create/command.go b/cmd/skpr/mysql/backup/create/command.go index 5ea8755..33eff0a 100644 --- a/cmd/skpr/mysql/backup/create/command.go +++ b/cmd/skpr/mysql/backup/create/command.go @@ -1,9 +1,8 @@ package create import ( + v1create "github.com/skpr/cli/internal/command/mysql/backup/create" "github.com/spf13/cobra" - - v1create "github.com/skpr/cli/internal/command/v1/mysql/backup/create" ) var ( @@ -31,7 +30,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/mysql/backup/list/command.go b/cmd/skpr/mysql/backup/list/command.go index a956cdd..ada4aed 100644 --- a/cmd/skpr/mysql/backup/list/command.go +++ b/cmd/skpr/mysql/backup/list/command.go @@ -1,9 +1,8 @@ package list import ( + v1list "github.com/skpr/cli/internal/command/mysql/backup/list" "github.com/spf13/cobra" - - v1list "github.com/skpr/cli/internal/command/v1/mysql/backup/list" ) var ( @@ -34,7 +33,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/mysql/image/command.go b/cmd/skpr/mysql/image/command.go index cef8c16..c294997 100644 --- a/cmd/skpr/mysql/image/command.go +++ b/cmd/skpr/mysql/image/command.go @@ -5,6 +5,7 @@ import ( "github.com/skpr/cli/cmd/skpr/mysql/image/create" "github.com/skpr/cli/cmd/skpr/mysql/image/list" + "github.com/skpr/cli/cmd/skpr/mysql/image/pull" ) var ( @@ -31,6 +32,7 @@ func NewCommand() *cobra.Command { cmd.AddCommand(create.NewCommand()) cmd.AddCommand(list.NewCommand()) + cmd.AddCommand(pull.NewCommand()) return cmd } diff --git a/cmd/skpr/mysql/image/create/command.go b/cmd/skpr/mysql/image/create/command.go index f8e86d6..6f666e2 100644 --- a/cmd/skpr/mysql/image/create/command.go +++ b/cmd/skpr/mysql/image/create/command.go @@ -1,9 +1,8 @@ package create import ( + v1create "github.com/skpr/cli/internal/command/mysql/image/create" "github.com/spf13/cobra" - - v1create "github.com/skpr/cli/internal/command/v1/mysql/image/create" ) var ( @@ -28,7 +27,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/mysql/image/list/command.go b/cmd/skpr/mysql/image/list/command.go index bc66985..4829041 100644 --- a/cmd/skpr/mysql/image/list/command.go +++ b/cmd/skpr/mysql/image/list/command.go @@ -1,9 +1,8 @@ package list import ( + v1list "github.com/skpr/cli/internal/command/mysql/image/list" "github.com/spf13/cobra" - - v1list "github.com/skpr/cli/internal/command/v1/mysql/image/list" ) var ( @@ -28,7 +27,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/mysql/image/pull/command.go b/cmd/skpr/mysql/image/pull/command.go new file mode 100644 index 0000000..389ac71 --- /dev/null +++ b/cmd/skpr/mysql/image/pull/command.go @@ -0,0 +1,43 @@ +package pull + +import ( + "github.com/spf13/cobra" + + v1pull "github.com/skpr/cli/internal/command/mysql/image/pull" +) + +var ( + cmdLong = ` + Pulls a database image associated with an environment.` + + cmdExample = ` + # Pull the database image for an environment. + skpr mysql image pull ENVIRONMENT` +) + +// NewCommand creates a new cobra.Command for 'list' sub command +func NewCommand() *cobra.Command { + command := v1pull.Command{} + + cmd := &cobra.Command{ + Use: "list", + Args: cobra.MinimumNArgs(1), + DisableFlagsInUseLine: true, + Short: "List created database images for an environment", + Long: cmdLong, + Example: cmdExample, + RunE: func(cmd *cobra.Command, args []string) error { + command.Params.Environment = args[0] + + if len(args) > 1 { + command.Params.Databases = args[1:] + } else { + command.Params.Databases = []string{"default"} + } + + return command.Run(cmd.Context()) + }, + } + + return cmd +} diff --git a/cmd/skpr/mysql/restore/create/command.go b/cmd/skpr/mysql/restore/create/command.go index faff145..1c5130b 100644 --- a/cmd/skpr/mysql/restore/create/command.go +++ b/cmd/skpr/mysql/restore/create/command.go @@ -1,9 +1,8 @@ package create import ( + v1create "github.com/skpr/cli/internal/command/mysql/restore/create" "github.com/spf13/cobra" - - v1create "github.com/skpr/cli/internal/command/v1/mysql/restore/create" ) var ( @@ -32,7 +31,7 @@ func NewCommand() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] command.Backup = args[1] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/mysql/restore/list/command.go b/cmd/skpr/mysql/restore/list/command.go index 7f2d9d1..613d1b5 100644 --- a/cmd/skpr/mysql/restore/list/command.go +++ b/cmd/skpr/mysql/restore/list/command.go @@ -1,9 +1,8 @@ package list import ( + v1list "github.com/skpr/cli/internal/command/mysql/restore/list" "github.com/spf13/cobra" - - v1list "github.com/skpr/cli/internal/command/v1/mysql/restore/list" ) var ( @@ -28,7 +27,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/package/command.go b/cmd/skpr/package/command.go index 1c97744..b615659 100644 --- a/cmd/skpr/package/command.go +++ b/cmd/skpr/package/command.go @@ -1,9 +1,8 @@ package pkg import ( + v1package "github.com/skpr/cli/internal/command/package" "github.com/spf13/cobra" - - v1package "github.com/skpr/cli/internal/command/v1/package" ) var ( @@ -36,7 +35,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Params.Version = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } @@ -45,6 +44,7 @@ func NewCommand() *cobra.Command { cmd.Flags().BoolVar(&command.Params.NoPush, "no-push", command.Params.NoPush, "Do not push the image to the registry.") cmd.Flags().BoolVar(&command.PrintManifest, "print-manifest", command.PrintManifest, "Print the manifest to stdout.") cmd.Flags().StringVar(&command.PackageDir, "dir", ".skpr/package", "The location of the package directory.") + cmd.Flags().StringSliceVar(&command.BuildArgs, "build-arg", []string{}, "Additional build arguments.") cmd.Flags().BoolVar(&command.Debug, "debug", command.Debug, "Enable debug output.") return cmd diff --git a/cmd/skpr/purge/create/command.go b/cmd/skpr/purge/create/command.go index f7f4204..17999ad 100644 --- a/cmd/skpr/purge/create/command.go +++ b/cmd/skpr/purge/create/command.go @@ -1,9 +1,8 @@ package create import ( + v1create "github.com/skpr/cli/internal/command/purge/create" "github.com/spf13/cobra" - - v1create "github.com/skpr/cli/internal/command/v1/purge/create" ) var ( @@ -29,7 +28,7 @@ func NewCommand() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] command.Paths = args[1:] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/purge/list/command.go b/cmd/skpr/purge/list/command.go index d265d9e..f765812 100644 --- a/cmd/skpr/purge/list/command.go +++ b/cmd/skpr/purge/list/command.go @@ -1,9 +1,8 @@ package list import ( + v1list "github.com/skpr/cli/internal/command/purge/list" "github.com/spf13/cobra" - - v1list "github.com/skpr/cli/internal/command/v1/purge/list" ) var ( @@ -28,7 +27,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/release/info/command.go b/cmd/skpr/release/info/command.go index 233f554..d4ba6a8 100644 --- a/cmd/skpr/release/info/command.go +++ b/cmd/skpr/release/info/command.go @@ -1,9 +1,8 @@ package info import ( + v1list "github.com/skpr/cli/internal/command/release/info" "github.com/spf13/cobra" - - v1list "github.com/skpr/cli/internal/command/v1/release/info" ) var ( @@ -31,7 +30,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Version = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/release/list/command.go b/cmd/skpr/release/list/command.go index 1adecef..2b4709c 100644 --- a/cmd/skpr/release/list/command.go +++ b/cmd/skpr/release/list/command.go @@ -1,9 +1,8 @@ package list import ( + v1list "github.com/skpr/cli/internal/command/release/list" "github.com/spf13/cobra" - - v1list "github.com/skpr/cli/internal/command/v1/release/list" ) var ( @@ -30,7 +29,7 @@ func NewCommand() *cobra.Command { Long: cmdLong, Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/restore/create/command.go b/cmd/skpr/restore/create/command.go index fc778a9..8829885 100644 --- a/cmd/skpr/restore/create/command.go +++ b/cmd/skpr/restore/create/command.go @@ -1,11 +1,10 @@ package create import ( + v1create "github.com/skpr/cli/internal/command/restore/create" "time" "github.com/spf13/cobra" - - v1create "github.com/skpr/cli/internal/command/v1/restore/create" ) var ( @@ -34,7 +33,7 @@ func NewCommand() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] command.Backup = args[1] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/restore/list/command.go b/cmd/skpr/restore/list/command.go index 8c93e60..73971bb 100644 --- a/cmd/skpr/restore/list/command.go +++ b/cmd/skpr/restore/list/command.go @@ -1,9 +1,8 @@ package list import ( + v1list "github.com/skpr/cli/internal/command/restore/list" "github.com/spf13/cobra" - - v1list "github.com/skpr/cli/internal/command/v1/restore/list" ) var ( @@ -28,7 +27,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/rsync/command.go b/cmd/skpr/rsync/command.go index 1fe7689..7ba4e31 100644 --- a/cmd/skpr/rsync/command.go +++ b/cmd/skpr/rsync/command.go @@ -1,9 +1,8 @@ package rsync import ( + v1rsync "github.com/skpr/cli/internal/command/rsync" "github.com/spf13/cobra" - - v1rsync "github.com/skpr/cli/internal/command/v1/rsync" ) var ( @@ -28,7 +27,7 @@ func NewCommand() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { command.Source = args[0] command.Destination = args[1] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/shell/command.go b/cmd/skpr/shell/command.go index 92a00ad..2c3d076 100644 --- a/cmd/skpr/shell/command.go +++ b/cmd/skpr/shell/command.go @@ -1,9 +1,8 @@ package shell import ( + v1shell "github.com/skpr/cli/internal/command/shell" "github.com/spf13/cobra" - - v1shell "github.com/skpr/cli/internal/command/v1/shell" ) var ( @@ -27,7 +26,7 @@ func NewCommand() *cobra.Command { command := v1shell.Command{} cmd := &cobra.Command{ - Use: "shell", + Use: "shell [environment]", DisableFlagsInUseLine: true, Short: "Execute a multiple shell commands in a session", Args: cobra.ExactArgs(1), @@ -35,7 +34,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Environment = args[0] - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/cmd/skpr/version/command.go b/cmd/skpr/version/command.go index ae08b22..4e1bb04 100644 --- a/cmd/skpr/version/command.go +++ b/cmd/skpr/version/command.go @@ -1,9 +1,8 @@ package version import ( + v1version "github.com/skpr/cli/internal/command/version" "github.com/spf13/cobra" - - v1version "github.com/skpr/cli/internal/command/v1/version" ) var ( @@ -39,7 +38,7 @@ func NewCommand() *cobra.Command { Long: cmdLong, Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { - return command.Run() + return command.Run(cmd.Context()) }, } diff --git a/go.mod b/go.mod index 4f66723..d468110 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/skpr/cli go 1.24.3 require ( - dario.cat/mergo v1.0.2 github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 github.com/aquasecurity/table v1.11.0 github.com/aws/aws-sdk-go-v2 v1.39.0 @@ -25,9 +24,7 @@ require ( github.com/gosuri/uitable v0.0.4 github.com/jinzhu/now v1.1.5 github.com/jwalton/gchalk v1.3.0 - github.com/kelseyhightower/envconfig v1.4.0 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d - github.com/mitchellh/go-homedir v1.1.0 github.com/notaryproject/notation-core-go v1.3.0 github.com/notaryproject/notation-go v1.3.2 github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 @@ -41,7 +38,7 @@ require ( golang.org/x/oauth2 v0.31.0 golang.org/x/sync v0.17.0 golang.org/x/term v0.35.0 - google.golang.org/grpc v1.75.1 + google.golang.org/grpc v1.75.0 gopkg.in/yaml.v2 v2.4.0 oras.land/oras-go/v2 v2.6.0 ) diff --git a/go.sum b/go.sum index e141058..d55cd28 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= -dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= @@ -150,8 +148,6 @@ github.com/jwalton/gchalk v1.3.0/go.mod h1:ytRlj60R9f7r53IAElbpq4lVuPOPNg2J4tJcC github.com/jwalton/go-supportscolor v1.1.0/go.mod h1:hFVUAZV2cWg+WFFC4v8pT2X/S2qUUBYMioBD9AINXGs= github.com/jwalton/go-supportscolor v1.2.0 h1:g6Ha4u7Vm3LIsQ5wmeBpS4gazu0UP1DRDE8y6bre4H8= github.com/jwalton/go-supportscolor v1.2.0/go.mod h1:hFVUAZV2cWg+WFFC4v8pT2X/S2qUUBYMioBD9AINXGs= -github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= -github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= 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/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= @@ -167,8 +163,6 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= @@ -376,8 +370,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0 h1:MAKi5q709QWfnkkpNQ0M12hYJ1+e8qYVDyowc4U1XZM= google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= -google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= +google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/internal/buildpack/builder/builder.go b/internal/buildpack/builder/builder.go index c78db16..da7c759 100644 --- a/internal/buildpack/builder/builder.go +++ b/internal/buildpack/builder/builder.go @@ -56,12 +56,13 @@ type Image struct { // Params used for building the applications. type Params struct { - Auth docker.AuthConfiguration - Writer io.Writer - Context string - Registry string - NoPush bool - Version string + Auth docker.AuthConfiguration + Writer io.Writer + Context string + Registry string + NoPush bool + Version string + BuildArgs map[string]string } // Dockerfiles the docker build files. @@ -100,6 +101,13 @@ func (b *Builder) Build(dockerfiles Dockerfiles, params Params) (BuildResponse, }, } + for k, v := range params.BuildArgs { + args = append(args, docker.BuildArg{ + Name: k, + Value: v, + }) + } + start := time.Now() // We build the compile image first, as it is the base image for other images. diff --git a/internal/buildpack/utils/aws/ecr/ecr.go b/internal/buildpack/utils/aws/ecr/ecr.go index 98518ec..2435a46 100644 --- a/internal/buildpack/utils/aws/ecr/ecr.go +++ b/internal/buildpack/utils/aws/ecr/ecr.go @@ -3,9 +3,9 @@ package ecr import ( "context" "fmt" + skprcredentials "github.com/skpr/cli/internal/client/credentials" "strings" - "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/ecr" @@ -24,7 +24,7 @@ func IsRegistry(registry string) bool { // UpgradeAuth to use an AWS IAM token for authentication.. // https://docs.aws.amazon.com/cli/latest/reference/ecr/get-login.html -func UpgradeAuth(ctx context.Context, url string, creds aws.Credentials) (docker.AuthConfiguration, error) { +func UpgradeAuth(ctx context.Context, url string, creds skprcredentials.Credentials) (docker.AuthConfiguration, error) { var auth docker.AuthConfiguration region, err := extractRegionFromURL(url) @@ -35,7 +35,7 @@ func UpgradeAuth(ctx context.Context, url string, creds aws.Credentials) (docker cfg, err := config.LoadDefaultConfig( ctx, config.WithRegion(region), - config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(creds.AccessKeyID, creds.SecretAccessKey, creds.SessionToken)), + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(creds.Username, creds.Password, creds.Session)), ) if err != nil { diff --git a/internal/client/client.go b/internal/client/client.go index 4db99d2..9d8fb23 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -3,19 +3,16 @@ package client import ( "context" "fmt" + "github.com/skpr/cli/internal/client/config" + skprcredentials "github.com/skpr/cli/internal/client/credentials" + "github.com/skpr/cli/internal/client/ssh" - "github.com/aws/aws-sdk-go-v2/aws" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" "github.com/skpr/api/pb" - "github.com/skpr/cli/internal/client/config/clusters" - skprcredentials "github.com/skpr/cli/internal/client/config/credentials" - skprdiscovery "github.com/skpr/cli/internal/client/config/discovery" - "github.com/skpr/cli/internal/client/config/project" - "github.com/skpr/cli/internal/client/ssh" ) const ( @@ -29,170 +26,138 @@ const ( KeySession = "session" ) -// Options for the client. -type Options struct { - Username string - Password string - Credentials string - ClusterConfig string -} - -// New client built using options. -func (o *Options) New() (*Client, context.Context, error) { - return NewFromFile() -} - // Client for interacting with the Skipper server. type Client struct { - ClientConn *grpc.ClientConn - config project.Config - Discovery *skprdiscovery.Discovery - CredentialsProvider aws.CredentialsProvider - cluster clusters.Cluster + conn *grpc.ClientConn + // These are used by the ssh client. + config config.Config + // This is public so other clients eg. package and utilise them. + Credentials skprcredentials.Credentials } // New client. -func New(config project.Config, discovery *skprdiscovery.Discovery, credsProvider aws.CredentialsProvider, cluster clusters.Cluster) (*Client, context.Context, error) { - // https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md - // @todo, Attach credentials to this context. - awscreds, err := credsProvider.Retrieve(context.TODO()) +func New(ctx context.Context) (context.Context, *Client, error) { + config, err := config.New() if err != nil { - return &Client{}, nil, &ProjectInitError{fmt.Errorf("failed getting aws credentials %w", err)} - } - md := metadata.Pairs(KeyProject, config.Project, KeyUsername, awscreds.AccessKeyID, KeyPassword, awscreds.SecretAccessKey, KeySession, awscreds.SessionToken) - ctx := metadata.NewOutgoingContext(context.Background(), md) - - dial, err := Dial(cluster.API) - return &Client{dial, config, discovery, credsProvider, cluster}, ctx, err -} - -// Dial a connection to the API server. -func Dial(api clusters.API) (*grpc.ClientConn, error) { - server := fmt.Sprintf("%s:%d", api.Host, api.Port) - - if api.Insecure { - return grpc.NewClient(server, grpc.WithTransportCredentials(insecure.NewCredentials())) + return nil, nil, fmt.Errorf("could not create config: %w", err) } - return grpc.NewClient(server, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""))) -} - -// NewFromFile loads a file and uses that configuration to return a client. -func NewFromFile() (*Client, context.Context, error) { - discovery, err := skprdiscovery.New() + credentials, err := skprcredentials.New(ctx, config) if err != nil { - return nil, nil, &ProjectInitError{fmt.Errorf("failed to discover config %w", err)} + return nil, nil, fmt.Errorf("could not retrieve credentials: %w", err) } - configFile, err := discovery.Config() + conn, err := Dial(config) if err != nil { - return nil, nil, &ProjectInitError{fmt.Errorf("failed to get project config file path %w", err)} + return ctx, nil, fmt.Errorf("failed to dial server: %w", err) } - config, err := project.LoadConfig(configFile) - if err != nil { - return nil, nil, &ProjectInitError{fmt.Errorf("failed to get project config %w", err)} + // https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md + md := metadata.Pairs( + KeyProject, config.Project, + KeyUsername, credentials.Username, + KeyPassword, credentials.Password, + KeySession, credentials.Session, + ) + + client := &Client{ + conn: conn, + // These are used by the ssh client. + config: config, + // This is public so other clients eg. package and utilise them. + Credentials: credentials, } - credentialsFile, err := discovery.Credentials() - if err != nil { - return nil, nil, &CredsError{fmt.Errorf("failed to get credentials file %w", err)} - } - credsConfig := skprcredentials.NewConfig(credentialsFile) - credsResolver := skprcredentials.NewResolver(credsConfig) - creds, err := credsResolver.ResolveCredentials(config.Cluster) - if err != nil { - return nil, nil, &CredsError{fmt.Errorf("failed to get credentials config %w", err)} - } + return metadata.NewOutgoingContext(ctx, md), client, err +} - clusterFile, err := discovery.Clusters() - if err != nil { - return nil, nil, fmt.Errorf("failed to get clusters file %w", err) - } - clusterCfg, err := clusters.LoadFromFile(clusterFile, config.Cluster) - if err != nil { - return nil, nil, fmt.Errorf("failed to get clusters config %w", err) +// Dial a connection to the API server. +func Dial(config config.Config) (*grpc.ClientConn, error) { + server := fmt.Sprintf("%s:%d", config.API.Host(), config.API.Port()) + + if config.API.Insecure() { + return grpc.NewClient(server, grpc.WithTransportCredentials(insecure.NewCredentials())) } - return New(config, discovery, creds, clusterCfg) + return grpc.NewClient(server, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""))) } // Project client. func (c Client) Project() pb.ProjectClient { - return pb.NewProjectClient(c.ClientConn) + return pb.NewProjectClient(c.conn) } // Environment client operations. func (c Client) Environment() pb.EnvironmentClient { - return pb.NewEnvironmentClient(c.ClientConn) + return pb.NewEnvironmentClient(c.conn) } // Config client operations. func (c Client) Config() pb.ConfigClient { - return pb.NewConfigClient(c.ClientConn) + return pb.NewConfigClient(c.conn) } // Cron client operations. func (c Client) Cron() pb.CronClient { - return pb.NewCronClient(c.ClientConn) + return pb.NewCronClient(c.conn) } // Purge client operations. func (c Client) Purge() pb.PurgeClient { - return pb.NewPurgeClient(c.ClientConn) + return pb.NewPurgeClient(c.conn) } // Login client operations. func (c Client) Login() pb.LoginClient { - return pb.NewLoginClient(c.ClientConn) + return pb.NewLoginClient(c.conn) } // Logs client operations. func (c Client) Logs() pb.LogsClient { - return pb.NewLogsClient(c.ClientConn) + return pb.NewLogsClient(c.conn) } // Backup client operations. func (c Client) Backup() pb.BackupClient { - return pb.NewBackupClient(c.ClientConn) + return pb.NewBackupClient(c.conn) } // Release client operations. func (c Client) Release() pb.ReleaseClient { - return pb.NewReleaseClient(c.ClientConn) + return pb.NewReleaseClient(c.conn) } // Restore client operations. func (c Client) Restore() pb.RestoreClient { - return pb.NewRestoreClient(c.ClientConn) + return pb.NewRestoreClient(c.conn) } // Image client operations. func (c Client) Image() pb.MysqlClient { - return pb.NewMysqlClient(c.ClientConn) + return pb.NewMysqlClient(c.conn) } // Mysql client operations. func (c Client) Mysql() pb.MysqlClient { - return pb.NewMysqlClient(c.ClientConn) + return pb.NewMysqlClient(c.conn) } // SSH client operations. func (c Client) SSH() ssh.Interface { - return ssh.Client{Config: c.config, CredentialsProvider: c.CredentialsProvider, Cluster: c.cluster} + return ssh.Client{Config: c.config, Credentials: c.Credentials} } // Version client operations. func (c Client) Version() pb.VersionClient { - return pb.NewVersionClient(c.ClientConn) + return pb.NewVersionClient(c.conn) } // Volume client operations. func (c Client) Volume() pb.VolumeClient { - return pb.NewVolumeClient(c.ClientConn) + return pb.NewVolumeClient(c.conn) } // Daemon client operations. func (c Client) Daemon() pb.DaemonClient { - return pb.NewDaemonClient(c.ClientConn) + return pb.NewDaemonClient(c.conn) } diff --git a/internal/client/config/clusters/clusters.go b/internal/client/config/clusters/clusters.go deleted file mode 100644 index 2cc0f8f..0000000 --- a/internal/client/config/clusters/clusters.go +++ /dev/null @@ -1,102 +0,0 @@ -package clusters - -import ( - "os" - - "dario.cat/mergo" - "github.com/pkg/errors" - "gopkg.in/yaml.v2" -) - -const ( - // DefaultAPIPort when not provided. - DefaultAPIPort = 443 - // DefaultSSHPort when not provided. - DefaultSSHPort = 22 -) - -// Cluster which a user connects to. -type Cluster struct { - API API `yaml:"api"` - SSH SSH `yaml:"ssh"` -} - -// API connection details. -type API struct { - Host string `yaml:"host"` - Port int `yaml:"port"` - Insecure bool `yaml:"insecure"` -} - -// SSH connection details. -type SSH struct { - Host string `yaml:"host"` - Port int `yaml:"port"` -} - -// LoadFromFile will return a configuration from a file. -func LoadFromFile(file, name string) (Cluster, error) { - cluster := Cluster{ - API: API{ - Host: name, - Port: DefaultAPIPort, - }, - SSH: SSH{ - Host: name, - Port: DefaultSSHPort, - }, - } - - // If the file is not found, don't worry! Use the default cluster values. - if _, err := os.Stat(file); os.IsNotExist(err) { - return cluster, nil - } - - data, err := os.ReadFile(file) - if err != nil { - return cluster, errors.Wrap(err, "failed to read config") - } - - var clusters map[string]Cluster - - err = yaml.Unmarshal(data, &clusters) - if err != nil { - return cluster, errors.Wrap(err, "failed to marshal config") - } - - // If the cluster is specified, let's use that. - if _, ok := clusters[name]; ok { - err = mergo.Merge(&cluster, clusters[name], mergo.WithOverride) - if err != nil { - return cluster, errors.Wrap(err, "failed to merge config") - } - } - - err = cluster.Validate() - if err != nil { - return cluster, errors.Wrap(err, "validation failed") - } - - return cluster, nil -} - -// Validate the project config file. -func (c Cluster) Validate() error { - if c.API.Host == "" { - return errors.New("not found: api: host") - } - - if c.API.Port == 0 { - return errors.New("not found: api: port") - } - - if c.SSH.Host == "" { - return errors.New("not found: ssh: host") - } - - if c.SSH.Port == 0 { - return errors.New("not found: ssh: port") - } - - return nil -} diff --git a/internal/client/config/clusters/testdata/.skpr/clusters.yml b/internal/client/config/clusters/testdata/.skpr/clusters.yml deleted file mode 100644 index 3e527ee..0000000 --- a/internal/client/config/clusters/testdata/.skpr/clusters.yml +++ /dev/null @@ -1,8 +0,0 @@ -pnx.cluster.skpr.io: - api: - host: 127.0.0.1 - port: 443 - insecure: true - ssh: - host: 127.0.0.1 - port: 22 \ No newline at end of file diff --git a/internal/client/config/config.go b/internal/client/config/config.go new file mode 100644 index 0000000..2e94a6b --- /dev/null +++ b/internal/client/config/config.go @@ -0,0 +1,112 @@ +package config + +import ( + "errors" + "fmt" + "net" + "net/url" + "strconv" + "strings" +) + +// Config for connecting to the Skpr API. +type Config struct { + API URI // host:port?insecure=true|false + SSH URI // host:port + Project string +} + +// URI is a custom type for parsing URIs. +type URI string + +// Host extracts the hostname part of the URI. +func (u URI) Host() string { + s := string(u) + parts := strings.SplitN(s, "?", 2) + hostport := parts[0] + + if strings.Contains(hostport, ":") { + host, _, err := net.SplitHostPort(hostport) + if err == nil { + return host + } + } + + return hostport +} + +// Port extracts the port as an int, or 0 if not specified/invalid. +func (u URI) Port() int { + s := string(u) + parts := strings.SplitN(s, "?", 2) + hostport := parts[0] + + if strings.Contains(hostport, ":") { + _, portStr, err := net.SplitHostPort(hostport) + if err == nil { + p, _ := strconv.Atoi(portStr) + return p + } + } + return 0 +} + +// String returns the URI as a string. +func (u URI) String() string { + return string(u) +} + +// Insecure returns true if query param `insecure=true` is set. +func (u URI) Insecure() bool { + s := string(u) + + idx := strings.Index(s, "?") + if idx == -1 { + return false + } + + values, err := url.ParseQuery(s[idx+1:]) + if err != nil { + return false + } + + return strings.ToLower(values.Get("insecure")) == "true" +} + +type ConfigGetter func(*Config) error + +func New() (Config, error) { + var config Config + + funcs := []ConfigGetter{ + GetFromFile, + GetFromEnv, // Environment variables should be used instead of the file if set. + } + + for _, f := range funcs { + err := f(&config) + if err != nil { + return Config{}, err + } + } + + var errs []error + + if config.API == "" { + errs = append(errs, fmt.Errorf("api uri config not found")) + } + + if config.SSH == "" { + errs = append(errs, fmt.Errorf("ssh uri config not found")) + } + + // Project is optional. + // We want users to be able to connect to the API without a project set. + // This allows for commands like `skpr login` to work without a project. + + if len(errs) > 0 { + return Config{}, errors.Join(errs...) + } + + return config, nil +} diff --git a/internal/client/config/config_test.go b/internal/client/config/config_test.go new file mode 100644 index 0000000..3f8e534 --- /dev/null +++ b/internal/client/config/config_test.go @@ -0,0 +1,78 @@ +package config + +import "testing" + +func TestURIParsing(t *testing.T) { + tests := []struct { + name string + in URI + wantHost string + wantPort int + wantInsc bool + }{ + { + name: "host:port with insecure=true", + in: URI("example.com:8080?insecure=true"), + wantHost: "example.com", + wantPort: 8080, + wantInsc: true, + }, + { + name: "host:port no query", + in: URI("example.com:8080"), + wantHost: "example.com", + wantPort: 8080, + wantInsc: false, + }, + { + name: "host only, insecure=false", + in: URI("example.com?insecure=false"), + wantHost: "example.com", + wantPort: 0, + wantInsc: false, + }, + { + name: "numeric host, no port", + in: URI("12680448"), + wantHost: "12680448", + wantPort: 0, + wantInsc: false, + }, + { + name: "invalid port -> strip port, port=0", + in: URI("example.com:abc?insecure=true"), + wantHost: "example.com", // changed from "example.com:abc" + wantPort: 0, + wantInsc: true, + }, + { + name: "extra query params, insecure mixed case value", + in: URI("host.local:9?x=1&insecure=TrUe&y=2"), + wantHost: "host.local", + wantPort: 9, + wantInsc: true, + }, + { + name: "trailing question mark, no params", + in: URI("host:1?"), + wantHost: "host", + wantPort: 1, + wantInsc: false, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + if got := tc.in.Host(); got != tc.wantHost { + t.Fatalf("Host() = %q, want %q", got, tc.wantHost) + } + if got := tc.in.Port(); got != tc.wantPort { + t.Fatalf("Port() = %d, want %d", got, tc.wantPort) + } + if got := tc.in.Insecure(); got != tc.wantInsc { + t.Fatalf("Insecure() = %v, want %v", got, tc.wantInsc) + } + }) + } +} diff --git a/internal/client/config/credentials/config.go b/internal/client/config/credentials/config.go deleted file mode 100644 index 79a3410..0000000 --- a/internal/client/config/credentials/config.go +++ /dev/null @@ -1,118 +0,0 @@ -package credentials - -import ( - "errors" - "fmt" - "os" - "path/filepath" - - "gopkg.in/yaml.v2" -) - -// Clusters represents a map of clusters -type Clusters map[string]Cluster - -// Cluster represents a cluster -type Cluster struct { - AWS AWS `yaml:"aws"` -} - -// AWS represents the aws config -type AWS struct { - Profile string `yaml:"profile,omitempty"` - AccessKeyID string `yaml:"access_key_id,omitempty"` - SecretAccessKey string `yaml:"secret_access_key,omitempty"` -} - -// Config represents the credentials config. -type Config struct { - file string -} - -// NewConfig create a new credentials config. -func NewConfig(file string) *Config { - return &Config{ - file: file, - } -} - -// AddOrUpdate adds or updates a cluster in the config file. -func (c *Config) AddOrUpdate(clusterKey string, cluster Cluster) error { - // Avoid overwriting other cluster config. - clusters, err := c.Read() - if err != nil { - if errors.Is(err, os.ErrNotExist) { - clusters = make(Clusters) - } else { - return err - } - } - clusters[clusterKey] = cluster - err = c.Write(clusters) - return err -} - -// Delete adds or updates a cluster in the config file. -func (c *Config) Delete(clusterKey string) error { - // Avoid overwriting other cluster config. - clusters, err := c.Read() - if err != nil { - return err - } - delete(clusters, clusterKey) - err = c.Write(clusters) - return err -} - -// Get gets the cluster config for the specified key -func (c *Config) Get(clusterKey string) (Cluster, error) { - clusters, err := c.Read() - if err != nil { - return Cluster{}, err - } - if _, ok := clusters[clusterKey]; !ok { - return Cluster{}, fmt.Errorf("failed to get cluster config for %s", clusterKey) - } - cluster := clusters[clusterKey] - return cluster, nil -} - -// Read reads config from a file. -func (c *Config) Read() (Clusters, error) { - data, err := os.ReadFile(c.file) - if err != nil { - return Clusters{}, fmt.Errorf("failed to read cluster config: %w", err) - } - - var clusters Clusters - - err = yaml.Unmarshal(data, &clusters) - if err != nil { - return Clusters{}, fmt.Errorf("failed to unmarshal cluster config: %w", err) - } - return clusters, nil -} - -// Write writes config to a file. -func (c *Config) Write(clusters Clusters) error { - data, err := yaml.Marshal(clusters) - if err != nil { - return fmt.Errorf("failed to marshal clusters: %w", err) - } - - dir := filepath.Dir(c.file) - - if _, err := os.Stat(dir); os.IsNotExist(err) { - err = os.Mkdir(dir, 0700) - if err != nil { - return fmt.Errorf("failed to create directory: %w", err) - } - } - - err = os.WriteFile(c.file, data, 0600) - if err != nil { - return fmt.Errorf("failed to write clusters to file: %w", err) - } - - return nil -} diff --git a/internal/client/config/credentials/config_test.go b/internal/client/config/credentials/config_test.go deleted file mode 100644 index e8ec3fd..0000000 --- a/internal/client/config/credentials/config_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package credentials - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestConfig(t *testing.T) { - file := filepath.Join(os.TempDir(), "credentials.yml") - config := NewConfig(file) - clusterKey := "test.cluster.skpr.io" - err := config.AddOrUpdate(clusterKey, Cluster{ - AWS: AWS{ - Profile: "test.profile", - }, - }) - assert.NoError(t, err) - - assert.FileExists(t, file) - - cluster, err := config.Get(clusterKey) - assert.NoError(t, err) - assert.Equal(t, "test.profile", cluster.AWS.Profile) - -} diff --git a/internal/client/config/credentials/resolver.go b/internal/client/config/credentials/resolver.go deleted file mode 100644 index 04002d0..0000000 --- a/internal/client/config/credentials/resolver.go +++ /dev/null @@ -1,79 +0,0 @@ -package credentials - -import ( - "context" - "errors" - "os" - - "github.com/aws/aws-sdk-go-v2/aws" - awsconfig "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/credentials" - - credentialscache "github.com/skpr/cli/internal/client/config/credentials/cache" - providercache "github.com/skpr/cli/internal/client/config/credentials/provider/cache" -) - -// Resolver is the credentials resolver. -type Resolver struct { - config Config - sharedConfig string -} - -// NewResolver creates a new credentials resolver. -func NewResolver(config *Config, options ...func(*Resolver)) *Resolver { - resolver := &Resolver{ - config: *config, - sharedConfig: awsconfig.DefaultSharedCredentialsFilename(), - } - for _, option := range options { - option(resolver) - } - return resolver -} - -// ResolveCredentials resolves the credentials. -func (r *Resolver) ResolveCredentials(clusterKey string) (aws.CredentialsProvider, error) { - var credsProvider aws.CredentialsProvider - - if credentialscache.Exists(clusterKey) { - return providercache.New(clusterKey), nil - } - - cluster, err := r.config.Get(clusterKey) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - // Fall back to Skpr env vars if not found. - return NewSkprEnvCredentials(), nil - } - return credsProvider, err - } - - if cluster.AWS.AccessKeyID != "" && cluster.AWS.SecretAccessKey != "" { - return credentials.StaticCredentialsProvider{ - Value: aws.Credentials{ - AccessKeyID: cluster.AWS.AccessKeyID, - SecretAccessKey: cluster.AWS.SecretAccessKey, - SessionToken: "", - }, - }, nil - } - - if cluster.AWS.Profile != "" { - sharedConfig, err := awsconfig.LoadSharedConfigProfile(context.TODO(), cluster.AWS.Profile, func(opts *awsconfig.LoadSharedConfigOptions) { - opts.CredentialsFiles = []string{r.sharedConfig} - }) - if err != nil { - if errors.Is(err, &awsconfig.SharedConfigProfileNotExistError{}) { - // Fall back to Skpr env vars if not found. - return NewSkprEnvCredentials(), nil - } - return credsProvider, err - } - - return aws.CredentialsProviderFunc(func(ctx context.Context) (aws.Credentials, error) { - return sharedConfig.Credentials, nil - }), nil - } - - return NewSkprEnvCredentials(), nil -} diff --git a/internal/client/config/credentials/resolver_test.go b/internal/client/config/credentials/resolver_test.go deleted file mode 100644 index c34abbe..0000000 --- a/internal/client/config/credentials/resolver_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package credentials - -import ( - "context" - "os" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestCreds(t *testing.T) { - config := NewConfig("testdata/.skpr/credentials.yml") - resolver := NewResolver(config) - credentialsProvider, err := resolver.ResolveCredentials("foo.cluster.skpr.io") - assert.NoError(t, err) - - creds, err := credentialsProvider.Retrieve(context.TODO()) - assert.NoError(t, err) - - assert.Equal(t, "xxxx", creds.AccessKeyID) - assert.Equal(t, "yyyy", creds.SecretAccessKey) - -} - -func TestProfile(t *testing.T) { - config := NewConfig("testdata/.skpr/credentials.yml") - - profileConfig := func(res *Resolver) { - res.sharedConfig = "testdata/credentials" - } - resolver := NewResolver(config, profileConfig) - credentialsProvider, err := resolver.ResolveCredentials("bar.cluster.skpr.io") - assert.NoError(t, err) - - creds, err := credentialsProvider.Retrieve(context.TODO()) - assert.NoError(t, err) - - assert.Equal(t, "fizzy", creds.AccessKeyID) - assert.Equal(t, "popper", creds.SecretAccessKey) -} - -func TestSkprEnvVars(t *testing.T) { - - err := os.Setenv("SKPR_USERNAME", "wiz") - assert.NoError(t, err) - err = os.Setenv("SKPR_PASSWORD", "bang") - assert.NoError(t, err) - - config := NewConfig("NOT_FOUND") - resolver := NewResolver(config) - credentialsProvider, err := resolver.ResolveCredentials("bar.cluster.skpr.io") - assert.NoError(t, err) - - creds, err := credentialsProvider.Retrieve(context.TODO()) - assert.NoError(t, err) - - assert.Equal(t, "wiz", creds.AccessKeyID) - assert.Equal(t, "bang", creds.SecretAccessKey) - -} diff --git a/internal/client/config/credentials/skpr_env_provider.go b/internal/client/config/credentials/skpr_env_provider.go deleted file mode 100644 index 2967dd6..0000000 --- a/internal/client/config/credentials/skpr_env_provider.go +++ /dev/null @@ -1,50 +0,0 @@ -package credentials - -import ( - "context" - "errors" - "os" - - "github.com/aws/aws-sdk-go-v2/aws" -) - -// EnvProviderName provides a name of Env provider -const EnvProviderName = "SkprEnvProvider" - -// SkprEnvProvider is a credentials provider that uses SKPR env vars. -// * Access Key ID: SKPR_USERNAME -// * Secret Access Key: SKPR_PASSWORD -type SkprEnvProvider struct { - aws.CredentialsProvider - retrieved bool -} - -// NewSkprEnvCredentials returns new Skpr envvar credentials. -func NewSkprEnvCredentials() aws.CredentialsProvider { - return &SkprEnvProvider{} -} - -// Retrieve retrieves the keys from the environment. -func (e *SkprEnvProvider) Retrieve(ctx context.Context) (aws.Credentials, error) { - e.retrieved = false - - id := os.Getenv("SKPR_USERNAME") - secret := os.Getenv("SKPR_PASSWORD") - - if id == "" { - return aws.Credentials{Source: EnvProviderName}, errors.New("username not found") - } - - if secret == "" { - return aws.Credentials{Source: EnvProviderName}, errors.New("password not found") - } - - e.retrieved = true - return aws.Credentials{ - AccessKeyID: id, - SecretAccessKey: secret, - SessionToken: "", - Source: EnvProviderName, - CanExpire: false, - }, nil -} diff --git a/internal/client/config/credentials/testdata/.skpr/credentials.yml b/internal/client/config/credentials/testdata/.skpr/credentials.yml deleted file mode 100644 index 6fc16a6..0000000 --- a/internal/client/config/credentials/testdata/.skpr/credentials.yml +++ /dev/null @@ -1,8 +0,0 @@ -foo.cluster.skpr.io: - aws: - access_key_id: xxxx - secret_access_key: yyyy - -bar.cluster.skpr.io: - aws: - profile: zzzz diff --git a/internal/client/config/credentials/testdata/credentials b/internal/client/config/credentials/testdata/credentials deleted file mode 100644 index e13ae19..0000000 --- a/internal/client/config/credentials/testdata/credentials +++ /dev/null @@ -1,3 +0,0 @@ -[zzzz] -aws_access_key_id=fizzy -aws_secret_access_key=popper \ No newline at end of file diff --git a/internal/client/config/discovery/discovery.go b/internal/client/config/discovery/discovery.go deleted file mode 100644 index 2e0c3f0..0000000 --- a/internal/client/config/discovery/discovery.go +++ /dev/null @@ -1,108 +0,0 @@ -package discovery - -import ( - "fmt" - "os" - "os/exec" - "path" - "path/filepath" - "strings" - - "github.com/kelseyhightower/envconfig" - homedir "github.com/mitchellh/go-homedir" -) - -// Discovery stores the path to skpr configuration. -type Discovery struct { - User User - Project Project -} - -// User configuration. -type User struct { - Directory string `default:"~/.skpr"` - Credentials string `default:"credentials.yml"` - Clusters string `default:"clusters.yml"` -} - -// Project specific configuration. -type Project struct { - Directory string `default:".skpr"` - Config string `default:"config.yml"` -} - -const defaultProjectDir = ".skpr" - -var projectRootCmd func() (string, error) - -func gitProjectRootCmd() (string, error) { - _, err := exec.LookPath("git") - if err != nil { - return "", fmt.Errorf("could not find git command in path: %w", err) - } - - rootPath, err := exec.Command("git", "rev-parse", "--show-toplevel").Output() - if err != nil { - return "", err - } - - return strings.TrimSpace(string(rootPath)), nil -} - -func init() { - projectRootCmd = gitProjectRootCmd -} - -// New returns a populated discovery object. -func New() (*Discovery, error) { - d := &Discovery{} - - err := envconfig.Process("SKPR", d) - - if d.Project.Directory == "" { - d.Project.Directory = defaultProjectDir - } - - return d, err -} - -// Credentials returns the full path to the credentials config file. -func (d *Discovery) Credentials() (string, error) { - return homedir.Expand(path.Join(d.User.Directory, d.User.Credentials)) -} - -// Clusters returns the full path to the cluster config file. -func (d *Discovery) Clusters() (string, error) { - return homedir.Expand(path.Join(d.User.Directory, d.User.Clusters)) -} - -// Config returns the full path to the default config.yml file. -func (d *Discovery) Config() (string, error) { - projectDir, err := d.Project.GetDirectory() - if err != nil { - return "", err - } - - return filepath.Join(projectDir, d.Project.Config), nil -} - -// GetDirectory returns the project directory. -func (p *Project) GetDirectory() (string, error) { - // Check if project dir exists. - _, err := os.Stat(p.Directory) - if err != nil { - if !os.IsNotExist(err) { - return "", err - } - - // Try to find the project root. - rootPath, err := projectRootCmd() - if err != nil { - return "", fmt.Errorf("could not find project root: %w", err) - } - - return filepath.Join(rootPath, p.Directory), nil - } - - return p.Directory, nil -} diff --git a/internal/client/config/discovery/discovery_test.go b/internal/client/config/discovery/discovery_test.go deleted file mode 100644 index bd10fde..0000000 --- a/internal/client/config/discovery/discovery_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package discovery - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestLookupProjectDir(t *testing.T) { - projectRootCmd = func() (string, error) { - return "foo", nil - } - - disco, err := New() - assert.NoError(t, err) - - config, err := disco.Config() - assert.NoError(t, err) - - assert.Equal(t, "foo/.skpr/config.yml", config) - -} diff --git a/internal/client/config/env.go b/internal/client/config/env.go new file mode 100644 index 0000000..16b3a29 --- /dev/null +++ b/internal/client/config/env.go @@ -0,0 +1,36 @@ +package config + +import ( + "os" +) + +const ( + // EnvAPI is used to connect to the Skpr API server. + EnvAPI = "SKPR_API" + // EnvSSH is used to connect to the Skpr SSH server. + EnvSSH = "SKPR_SSH" + // EnvProject is used as the standard way to connect to different projects via environment variable. + EnvProject = "SKPR_PROJECT" +) + +func GetFromEnv(config *Config) error { + var ( + api = os.Getenv(EnvAPI) + ssh = os.Getenv(EnvSSH) + project = os.Getenv(EnvProject) + ) + + if api != "" { + config.API = URI(api) + } + + if ssh != "" { + config.SSH = URI(ssh) + } + + if project != "" { + config.Project = project + } + + return nil +} diff --git a/internal/client/config/file.go b/internal/client/config/file.go new file mode 100644 index 0000000..e9cd770 --- /dev/null +++ b/internal/client/config/file.go @@ -0,0 +1,63 @@ +package config + +import ( + "fmt" + "os" + "path/filepath" + + "gopkg.in/yaml.v2" + + "github.com/skpr/cli/internal/client/utils" +) + +const ( + // DefaultAPIPort when not provided. + DefaultAPIPort = 443 + // DefaultSSHPort when not provided. + DefaultSSHPort = 22 +) + +const ( + // FileName for project config discovery. + FileName = "config.yml" +) + +type File struct { + Cluster string `yaml:"cluster"` + Project string `yaml:"project"` +} + +func GetFromFile(config *Config) error { + projectDir := utils.FindSkprConfigDir(".") // @todo, Swap the dot for something better. + + if projectDir == "" { + return nil + } + + data, err := os.ReadFile(filepath.Join(projectDir, FileName)) + if err != nil { + if os.IsNotExist(err) { + return nil + } + + return err + } + + var file File + + err = yaml.Unmarshal(data, &file) + if err != nil { + return fmt.Errorf("failed to unmarshal yaml: %w", err) + } + + if file.Cluster != "" { + config.API = URI(fmt.Sprintf("%s:%d", file.Cluster, DefaultAPIPort)) + config.SSH = URI(fmt.Sprintf("%s:%d", file.Cluster, DefaultSSHPort)) + } + + if file.Project != "" { + config.Project = file.Project + } + + return nil +} diff --git a/internal/client/config/project/project.go b/internal/client/config/project/project.go deleted file mode 100644 index 56e0477..0000000 --- a/internal/client/config/project/project.go +++ /dev/null @@ -1,91 +0,0 @@ -package project - -import ( - "fmt" - "os" - "path/filepath" - - "gopkg.in/yaml.v2" -) - -const ( - // FileDefaults is used for loading environment defaults. - FileDefaults = "defaults.yml" - - // EnvConfigProject is the environment variable for the project name. - EnvConfigProject = "SKPR_CONFIG_PROJECT" - // EnvConfigCluster is the environment variable for the cluster name. - EnvConfigCluster = "SKPR_CONFIG_CLUSTER" -) - -// LoadFromDirectory project configuration. -func LoadFromDirectory(path, name string) (Environment, error) { - var environment Environment - - defaults := filepath.Join(path, FileDefaults) - - // Load default environment configuration. - if _, err := os.Stat(defaults); os.IsNotExist(err) { - return environment, err - } - - data, err := os.ReadFile(defaults) - if err != nil { - return environment, err - } - - err = yaml.Unmarshal(data, &environment) - if err != nil { - return environment, err - } - - // Load the environment specific configuration. - data, err = os.ReadFile(filepath.Join(path, fmt.Sprintf("%s.yml", name))) - if err != nil && !os.IsNotExist(err) { - return environment, err - } - - err = yaml.Unmarshal(data, &environment) - if err != nil { - return environment, err - } - - return environment, nil -} - -// LoadConfig from a file and fallback to environment variables. -func LoadConfig(filePath string) (Config, error) { - var config Config - - // If the configuration file exists. Load it from that file. - if _, err := os.Stat(filePath); err == nil { - data, err := os.ReadFile(filePath) - if err != nil { - return config, fmt.Errorf("failed to get project config: %w", err) - } - - err = yaml.Unmarshal(data, &config) - if err != nil { - return config, fmt.Errorf("failed to unmarshal project config: %w", err) - } - } - - if os.Getenv(EnvConfigCluster) != "" { - config.Cluster = os.Getenv(EnvConfigCluster) - } - - if os.Getenv(EnvConfigProject) != "" { - config.Project = os.Getenv(EnvConfigProject) - } - - // Validate configuration. - if config.Project == "" { - return config, fmt.Errorf("project configuration not found") - } - - if config.Cluster == "" { - return config, fmt.Errorf("cluster configuration not found") - } - - return config, nil -} diff --git a/internal/client/config/command/aliases/aliases.go b/internal/client/config/user/aliases/aliases.go similarity index 94% rename from internal/client/config/command/aliases/aliases.go rename to internal/client/config/user/aliases/aliases.go index ef6d82d..aeef248 100644 --- a/internal/client/config/command/aliases/aliases.go +++ b/internal/client/config/user/aliases/aliases.go @@ -5,7 +5,7 @@ import ( "regexp" "strings" - "github.com/skpr/cli/internal/client/config/command" + "github.com/skpr/cli/internal/client/config/user" ) // Expand expands aliases in the list of args. diff --git a/internal/client/config/command/aliases/aliases_test.go b/internal/client/config/user/aliases/aliases_test.go similarity index 95% rename from internal/client/config/command/aliases/aliases_test.go rename to internal/client/config/user/aliases/aliases_test.go index bde6b73..f7b74ce 100644 --- a/internal/client/config/command/aliases/aliases_test.go +++ b/internal/client/config/user/aliases/aliases_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/skpr/cli/internal/client/config/command" + "github.com/skpr/cli/internal/client/config/user" ) func TestExpandAliases(t *testing.T) { diff --git a/internal/client/config/command/config.go b/internal/client/config/user/config.go similarity index 100% rename from internal/client/config/command/config.go rename to internal/client/config/user/config.go diff --git a/internal/client/config/command/config_test.go b/internal/client/config/user/config_test.go similarity index 100% rename from internal/client/config/command/config_test.go rename to internal/client/config/user/config_test.go diff --git a/internal/client/config/credentials/provider/cache/provider.go b/internal/client/credentials/cache.go similarity index 50% rename from internal/client/config/credentials/provider/cache/provider.go rename to internal/client/credentials/cache.go index 69748f0..0c661ab 100644 --- a/internal/client/config/credentials/provider/cache/provider.go +++ b/internal/client/credentials/cache.go @@ -1,65 +1,54 @@ -package cache +package credentials import ( "context" "fmt" - "github.com/aws/aws-sdk-go-v2/aws" awsconfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/cognitoidentity" "github.com/pkg/errors" + cache2 "github.com/skpr/cli/internal/client/credentials/cache" skprcredentials "github.com/skpr/cli/internal/aws/credentials" - credentialscache "github.com/skpr/cli/internal/client/config/credentials/cache" ) -// Name of the provider. -const Name = "SkprCacheProvider" - -// Provider for getting cached credentials. -// Typically generated by "skpr login". -type Provider struct { - aws.CredentialsProvider - clusterKey string -} - -// New provider returns new Skpr cache credentials. -func New(clusterKey string) aws.CredentialsProvider { - return &Provider{ - clusterKey: clusterKey, +func GetFromCache(ctx context.Context, cluster string) (Credentials, bool, error) { + credentials, exists, err := cache2.Get(cluster) + if err != nil { + return Credentials{}, false, fmt.Errorf("failed to get cached credentials: %w", err) } -} -// Retrieve retrieves the keys from the cache. -func (e *Provider) Retrieve(ctx context.Context) (aws.Credentials, error) { - credentials, err := credentialscache.Get(e.clusterKey) - if err != nil { - return aws.Credentials{}, fmt.Errorf("failed to get cached credentials: %w", err) + if !exists { + return Credentials{}, false, nil } // If our credentials have not expired, return them. if !credentials.TemporaryCredentialsExpired() { - return credentials.Temporary, nil + return Credentials{ + Username: credentials.Temporary.AccessKeyID, + Password: credentials.Temporary.SecretAccessKey, + Session: credentials.Temporary.SessionToken, + }, true, nil } newToken, err := credentials.GetToken(ctx) if err != nil { - return aws.Credentials{}, fmt.Errorf("failed to get token: %w", err) + return Credentials{}, false, fmt.Errorf("failed to get token: %w", err) } - credentials.Token = credentialscache.Token{ + credentials.Token = cache2.Token{ Refresh: newToken.RefreshToken, } // Extract the ID Token from OAuth2 token. idToken, ok := newToken.Extra("id_token").(string) if !ok { - return aws.Credentials{}, errors.Wrap(err, "Missing id_token") + return Credentials{}, false, errors.Wrap(err, "Missing id_token") } cfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithRegion(credentials.Cognito.Region), awsconfig.WithCredentialsProvider(aws.AnonymousCredentials{})) if err != nil { - return aws.Credentials{}, fmt.Errorf("failed to load AWS config: %w", err) + return Credentials{}, false, fmt.Errorf("failed to load AWS config: %w", err) } newTemporary, err := skprcredentials.GetTempCredentials(ctx, cognitoidentity.NewFromConfig(cfg), skprcredentials.GetTempCredentialsParams{ @@ -68,19 +57,23 @@ func (e *Provider) Retrieve(ctx context.Context) (aws.Credentials, error) { IdentityProvider: credentials.Cognito.IdentityProviderID, }) if err != nil { - return aws.Credentials{}, fmt.Errorf("failed to get temporary credentials: %w", err) + return Credentials{}, false, fmt.Errorf("failed to get temporary credentials: %w", err) } // Set our name so that we can identify the source of the credentials. - newTemporary.Source = Name + newTemporary.Source = "SkprCacheProvider" // @todo, Make this a const again. credentials.Temporary = newTemporary // Save back the refreshed token and credentials. - err = credentialscache.Set(e.clusterKey, credentials) + err = cache2.Set(cluster, credentials) if err != nil { - return aws.Credentials{}, fmt.Errorf("failed to store refreshed credentials: %w", err) + return Credentials{}, false, fmt.Errorf("failed to store refreshed credentials: %w", err) } - return credentials.Temporary, nil + return Credentials{ + Username: credentials.Temporary.AccessKeyID, + Password: credentials.Temporary.SecretAccessKey, + Session: credentials.Temporary.SessionToken, + }, true, nil } diff --git a/internal/client/config/credentials/cache/cache.go b/internal/client/credentials/cache/cache.go similarity index 76% rename from internal/client/config/credentials/cache/cache.go rename to internal/client/credentials/cache/cache.go index 44fd62b..622de1f 100644 --- a/internal/client/config/credentials/cache/cache.go +++ b/internal/client/credentials/cache/cache.go @@ -38,17 +38,6 @@ func Set(clusterName string, credentials Credentials) error { return os.WriteFile(file, val, 0644) } -// Exists checks if a credentials cache file exists for a cluster. -func Exists(clusterName string) bool { - path, err := getFile(clusterName) - if err != nil { - return false - } - - _, err = os.Stat(path) - return err == nil -} - // Delete a credentials cache file for a cluster. func Delete(clusterName string) error { path, err := getFile(clusterName) @@ -59,30 +48,35 @@ func Delete(clusterName string) error { } // Get a credentials cache file for a cluster. -func Get(clusterName string) (Credentials, error) { +func Get(clusterName string) (Credentials, bool, error) { var credentials Credentials path, err := getFile(clusterName) if err != nil { - return credentials, fmt.Errorf("failed to get credentials cache file: %w", err) + return credentials, false, fmt.Errorf("failed to get credentials cache file: %w", err) } file, err := os.Open(path) if err != nil { - return credentials, fmt.Errorf("failed to open file %s: %w", path, err) + // We don't want to return an error if the file does not exist. + if os.IsNotExist(err) { + return credentials, false, nil + } + + return credentials, false, fmt.Errorf("failed to open file %s: %w", path, err) } defer file.Close() data, err := io.ReadAll(file) if err != nil { - return credentials, fmt.Errorf("failed to read file %s: %w", path, err) + return credentials, false, fmt.Errorf("failed to read file %s: %w", path, err) } if err := json.Unmarshal(data, &credentials); err != nil { - return credentials, fmt.Errorf("failed to unmarshal credentials: %w", err) + return credentials, false, fmt.Errorf("failed to unmarshal credentials: %w", err) } - return credentials, nil + return credentials, true, nil } // Helper function to get a credentials cache file for a cluster. diff --git a/internal/client/config/credentials/cache/types.go b/internal/client/credentials/cache/types.go similarity index 99% rename from internal/client/config/credentials/cache/types.go rename to internal/client/credentials/cache/types.go index 930327a..d54df96 100644 --- a/internal/client/config/credentials/cache/types.go +++ b/internal/client/credentials/cache/types.go @@ -3,6 +3,7 @@ package cache import ( "context" "fmt" + "github.com/aws/aws-sdk-go-v2/aws" "golang.org/x/oauth2" ) diff --git a/internal/client/credentials/credentials.go b/internal/client/credentials/credentials.go new file mode 100644 index 0000000..97dfde6 --- /dev/null +++ b/internal/client/credentials/credentials.go @@ -0,0 +1,37 @@ +package credentials + +import ( + "context" + "github.com/skpr/cli/internal/client/config" +) + +// Credentials for connecting to the Skpr API. +type Credentials struct { + Username string + Password string + Session string +} + +type ConfigGetter func(context.Context, string) (Credentials, bool, error) + +func New(ctx context.Context, config config.Config) (Credentials, error) { + funcs := []ConfigGetter{ + GetFromEnv, // Stop early if we have environment variables. + GetFromCache, + } + + for _, f := range funcs { + credentials, found, err := f(ctx, config.API.Host()) + if err != nil { + return Credentials{}, err + } + + if found { + return credentials, nil + } + } + + // If not credentials were found we will leave it up to the client to handle it. + // That allows us to support our login command and non-authenticated commands. + return Credentials{}, nil +} diff --git a/internal/client/credentials/env.go b/internal/client/credentials/env.go new file mode 100644 index 0000000..e9b2b76 --- /dev/null +++ b/internal/client/credentials/env.go @@ -0,0 +1,31 @@ +package credentials + +import ( + "context" + "os" +) + +const ( + // EnvUsername is used to authenticate with the Skpr cluster. + EnvUsername = "SKPR_USERNAME" + // EnvPassword is used to authenticate with the Skpr cluster. + EnvPassword = "SKPR_PASSWORD" + // EnvSession is used to authenticate with the Skpr cluster. + EnvSession = "SKPR_SESSION" +) + +func GetFromEnv(_ context.Context, _ string) (Credentials, bool, error) { + credentials := Credentials{ + Username: os.Getenv(EnvUsername), + Password: os.Getenv(EnvPassword), + Session: os.Getenv(EnvSession), + } + + if credentials.Username == "" || credentials.Password == "" { + // Missing required fields + return Credentials{}, false, nil + } + + // Both username and password are set + return credentials, true, nil +} diff --git a/internal/client/errors.go b/internal/client/errors.go deleted file mode 100644 index da13b79..0000000 --- a/internal/client/errors.go +++ /dev/null @@ -1,35 +0,0 @@ -package client - -import "fmt" - -// ProjectInitError for errors initialising the skpr client. -type ProjectInitError struct { - // Err is the nested error. - Err error -} - -// The Error message. -func (e ProjectInitError) Error() string { - return fmt.Sprintf("Failed to initialise the Skpr client. Are you in a project directory? See https://docs.skpr.io/setup/project/ %s", e.Err.Error()) -} - -// Unwrap returns the nested error. -func (e ProjectInitError) Unwrap() error { - return e.Err -} - -// CredsError for errors initialising the skpr client. -type CredsError struct { - // Err is the nested error. - Err error -} - -// The Error message. -func (e CredsError) Error() string { - return fmt.Sprintf("Failed to initialise the Skpr client. Have you configured credentials? See https://docs.skpr.io/getting-started/credentials/ %s", e.Err.Error()) -} - -// Unwrap returns the nested error. -func (e CredsError) Unwrap() error { - return e.Err -} diff --git a/internal/client/config/project/initializer.go b/internal/client/project/initializer.go similarity index 100% rename from internal/client/config/project/initializer.go rename to internal/client/project/initializer.go diff --git a/internal/client/config/project/initializer_test.go b/internal/client/project/initializer_test.go similarity index 100% rename from internal/client/config/project/initializer_test.go rename to internal/client/project/initializer_test.go diff --git a/internal/client/project/project.go b/internal/client/project/project.go new file mode 100644 index 0000000..269edd0 --- /dev/null +++ b/internal/client/project/project.go @@ -0,0 +1,49 @@ +package project + +import ( + "fmt" + "os" + "path/filepath" + + "gopkg.in/yaml.v2" +) + +const ( + // FileDefaults is used for loading environment defaults. + FileDefaults = "defaults.yml" +) + +// LoadFromDirectory project configuration. +func LoadFromDirectory(path, name string) (Environment, error) { + var environment Environment + + defaults := filepath.Join(path, FileDefaults) + + // Load default environment configuration. + if _, err := os.Stat(defaults); os.IsNotExist(err) { + return environment, err + } + + data, err := os.ReadFile(defaults) + if err != nil { + return environment, err + } + + err = yaml.Unmarshal(data, &environment) + if err != nil { + return environment, err + } + + // Load the environment specific configuration. + data, err = os.ReadFile(filepath.Join(path, fmt.Sprintf("%s.yml", name))) + if err != nil && !os.IsNotExist(err) { + return environment, err + } + + err = yaml.Unmarshal(data, &environment) + if err != nil { + return environment, err + } + + return environment, nil +} diff --git a/internal/client/config/project/project_test.go b/internal/client/project/project_test.go similarity index 100% rename from internal/client/config/project/project_test.go rename to internal/client/project/project_test.go diff --git a/internal/client/config/project/proto.go b/internal/client/project/proto.go similarity index 100% rename from internal/client/config/project/proto.go rename to internal/client/project/proto.go diff --git a/internal/client/config/project/proto_test.go b/internal/client/project/proto_test.go similarity index 100% rename from internal/client/config/project/proto_test.go rename to internal/client/project/proto_test.go diff --git a/internal/client/config/project/resources/config.yml b/internal/client/project/resources/config.yml similarity index 100% rename from internal/client/config/project/resources/config.yml rename to internal/client/project/resources/config.yml diff --git a/internal/client/config/project/resources/defaults.yml b/internal/client/project/resources/defaults.yml similarity index 100% rename from internal/client/config/project/resources/defaults.yml rename to internal/client/project/resources/defaults.yml diff --git a/internal/client/config/project/resources/dev.yml b/internal/client/project/resources/dev.yml similarity index 100% rename from internal/client/config/project/resources/dev.yml rename to internal/client/project/resources/dev.yml diff --git a/internal/client/config/project/resources/package/cli/Dockerfile b/internal/client/project/resources/package/cli/Dockerfile similarity index 100% rename from internal/client/config/project/resources/package/cli/Dockerfile rename to internal/client/project/resources/package/cli/Dockerfile diff --git a/internal/client/config/project/resources/package/cli/custom.ini b/internal/client/project/resources/package/cli/custom.ini similarity index 100% rename from internal/client/config/project/resources/package/cli/custom.ini rename to internal/client/project/resources/package/cli/custom.ini diff --git a/internal/client/config/project/resources/package/compile/Dockerfile b/internal/client/project/resources/package/compile/Dockerfile similarity index 100% rename from internal/client/config/project/resources/package/compile/Dockerfile rename to internal/client/project/resources/package/compile/Dockerfile diff --git a/internal/client/config/project/resources/package/fpm/Dockerfile b/internal/client/project/resources/package/fpm/Dockerfile similarity index 100% rename from internal/client/config/project/resources/package/fpm/Dockerfile rename to internal/client/project/resources/package/fpm/Dockerfile diff --git a/internal/client/config/project/resources/package/fpm/custom.ini b/internal/client/project/resources/package/fpm/custom.ini similarity index 100% rename from internal/client/config/project/resources/package/fpm/custom.ini rename to internal/client/project/resources/package/fpm/custom.ini diff --git a/internal/client/config/project/resources/package/nginx/Dockerfile b/internal/client/project/resources/package/nginx/Dockerfile similarity index 100% rename from internal/client/config/project/resources/package/nginx/Dockerfile rename to internal/client/project/resources/package/nginx/Dockerfile diff --git a/internal/client/config/project/resources/package/nginx/custom.conf b/internal/client/project/resources/package/nginx/custom.conf similarity index 100% rename from internal/client/config/project/resources/package/nginx/custom.conf rename to internal/client/project/resources/package/nginx/custom.conf diff --git a/internal/client/config/project/resources/package/nginx/redirects.conf b/internal/client/project/resources/package/nginx/redirects.conf similarity index 100% rename from internal/client/config/project/resources/package/nginx/redirects.conf rename to internal/client/project/resources/package/nginx/redirects.conf diff --git a/internal/client/config/project/resources/prod.yml b/internal/client/project/resources/prod.yml similarity index 100% rename from internal/client/config/project/resources/prod.yml rename to internal/client/project/resources/prod.yml diff --git a/internal/client/config/project/resources/stg.yml b/internal/client/project/resources/stg.yml similarity index 100% rename from internal/client/config/project/resources/stg.yml rename to internal/client/project/resources/stg.yml diff --git a/internal/client/config/project/testdata/.skpr/config.yml b/internal/client/project/testdata/.skpr/config.yml similarity index 100% rename from internal/client/config/project/testdata/.skpr/config.yml rename to internal/client/project/testdata/.skpr/config.yml diff --git a/internal/client/config/project/testdata/.skpr/defaults.yml b/internal/client/project/testdata/.skpr/defaults.yml similarity index 100% rename from internal/client/config/project/testdata/.skpr/defaults.yml rename to internal/client/project/testdata/.skpr/defaults.yml diff --git a/internal/client/config/project/testdata/.skpr/dev.yml b/internal/client/project/testdata/.skpr/dev.yml similarity index 100% rename from internal/client/config/project/testdata/.skpr/dev.yml rename to internal/client/project/testdata/.skpr/dev.yml diff --git a/internal/client/config/project/testdata/.skpr/prod.yml b/internal/client/project/testdata/.skpr/prod.yml similarity index 100% rename from internal/client/config/project/testdata/.skpr/prod.yml rename to internal/client/project/testdata/.skpr/prod.yml diff --git a/internal/client/config/project/testdata/.skpr/stg.yml b/internal/client/project/testdata/.skpr/stg.yml similarity index 100% rename from internal/client/config/project/testdata/.skpr/stg.yml rename to internal/client/project/testdata/.skpr/stg.yml diff --git a/internal/client/config/project/types.go b/internal/client/project/types.go similarity index 100% rename from internal/client/config/project/types.go rename to internal/client/project/types.go diff --git a/internal/client/ssh/exec.go b/internal/client/ssh/exec.go index d68e412..4b1bd2b 100644 --- a/internal/client/ssh/exec.go +++ b/internal/client/ssh/exec.go @@ -1,7 +1,6 @@ package ssh import ( - "context" "errors" "fmt" "net" @@ -16,15 +15,9 @@ func (c Client) Exec(params ExecParams) error { return errors.New("command was not provided") } - awsCreds, err := c.CredentialsProvider.Retrieve(context.TODO()) - if err != nil { - return err - } - var ( user = fmt.Sprintf("%s%s%s", c.Config.Project, UsernameSeparator, params.Environment) - pass = getPassword(awsCreds.AccessKeyID, awsCreds.SecretAccessKey, awsCreds.SessionToken) - host = fmt.Sprintf("%s:%d", c.Cluster.SSH.Host, c.Cluster.SSH.Port) + pass = getPassword(c.Credentials.Username, c.Credentials.Password, c.Credentials.Session) ) config := &ssh.ClientConfig{ @@ -38,7 +31,7 @@ func (c Client) Exec(params ExecParams) error { return nil } - client, err := ssh.Dial("tcp", host, config) + client, err := ssh.Dial("tcp", string(c.Config.SSH), config) if err != nil { return err } diff --git a/internal/client/ssh/shell.go b/internal/client/ssh/shell.go index 7b2332b..c2a613f 100644 --- a/internal/client/ssh/shell.go +++ b/internal/client/ssh/shell.go @@ -1,7 +1,6 @@ package ssh import ( - "context" "fmt" "net" "os" @@ -13,15 +12,9 @@ import ( // Shell creates a long lived "shell" session for the user. func (c Client) Shell(params ShellParams) error { - awsCreds, err := c.CredentialsProvider.Retrieve(context.TODO()) - if err != nil { - return err - } - var ( user = fmt.Sprintf("%s%s%s", c.Config.Project, UsernameSeparator, params.Environment) - pass = getPassword(awsCreds.AccessKeyID, awsCreds.SecretAccessKey, awsCreds.SessionToken) - host = fmt.Sprintf("%s:%d", c.Cluster.SSH.Host, c.Cluster.SSH.Port) + pass = getPassword(c.Credentials.Username, c.Credentials.Password, c.Credentials.Session) ) config := &ssh.ClientConfig{ @@ -35,7 +28,7 @@ func (c Client) Shell(params ShellParams) error { return nil } - client, err := ssh.Dial("tcp", host, config) + client, err := ssh.Dial("tcp", string(c.Config.SSH), config) if err != nil { return err } diff --git a/internal/client/ssh/types.go b/internal/client/ssh/types.go index 3a3fb3f..fa3d92e 100644 --- a/internal/client/ssh/types.go +++ b/internal/client/ssh/types.go @@ -1,12 +1,9 @@ package ssh import ( + "github.com/skpr/cli/internal/client/config" + "github.com/skpr/cli/internal/client/credentials" "io" - - "github.com/aws/aws-sdk-go-v2/aws" - - "github.com/skpr/cli/internal/client/config/clusters" - "github.com/skpr/cli/internal/client/config/project" ) // Interface for the SSH client. @@ -34,7 +31,6 @@ type ShellParams struct { // Client for SSH interactions. type Client struct { - Config project.Config - CredentialsProvider aws.CredentialsProvider - Cluster clusters.Cluster + Config config.Config + Credentials credentials.Credentials } diff --git a/internal/client/utils/config.go b/internal/client/utils/config.go new file mode 100644 index 0000000..6afbdf8 --- /dev/null +++ b/internal/client/utils/config.go @@ -0,0 +1,36 @@ +package utils + +import ( + "os" + "path/filepath" +) + +const ( + ProjectConfigDir = ".skpr" +) + +// FindSkprConfigDir walks up directories until it finds one containing `.skpr`. +// Returns the path to that directory, or an error if not found. +func FindSkprConfigDir(startDir string) string { + dir, err := filepath.Abs(startDir) + if err != nil { + return "" + } + + for { + skprPath := filepath.Join(dir, ProjectConfigDir) + info, err := os.Stat(skprPath) + if err == nil && info.IsDir() { + // Found the directory containing `.skpr` + return filepath.Join(dir, ProjectConfigDir) + } + + parent := filepath.Dir(dir) + if parent == dir { + // Reached filesystem root + return "" + } + + dir = parent + } +} diff --git a/internal/client/utils/environment/environment.go b/internal/client/utils/environment/environment.go deleted file mode 100644 index 8d77f51..0000000 --- a/internal/client/utils/environment/environment.go +++ /dev/null @@ -1,40 +0,0 @@ -package deploy - -import ( - "fmt" - - "github.com/skpr/api/pb" - - "github.com/skpr/cli/internal/client" -) - -// GetNames returns a list of environment names. -func GetNames(opts *client.Options) []string { - client, ctx, err := opts.New() - if err != nil { - return []string{} - } - - resp, err := client.Environment().List(ctx, &pb.EnvironmentListRequest{}) - if err != nil { - fmt.Println("failed to list environments: %w", err) - } - - var envs []string - for _, env := range resp.Environments { - envs = append(envs, env.Name) - } - - return envs -} - -// Contains checks if an environment already exists. -func Contains(name string, list []*pb.Environment) bool { - for _, item := range list { - if item.Name == name { - return true - } - } - - return false -} diff --git a/internal/client/utils/environment/environment_test.go b/internal/client/utils/environment/environment_test.go deleted file mode 100644 index cf21fa7..0000000 --- a/internal/client/utils/environment/environment_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package deploy - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/skpr/api/pb" -) - -func TestContains(t *testing.T) { - environments := []*pb.Environment{ - { - Name: "dev", - }, - { - Name: "stg", - }, - } - - assert.True(t, Contains("dev", environments)) - assert.False(t, Contains("staging", environments)) -} diff --git a/internal/command/v1/alias/delete/command.go b/internal/command/alias/delete/command.go similarity index 85% rename from internal/command/v1/alias/delete/command.go rename to internal/command/alias/delete/command.go index 65ff0f8..5f096d9 100644 --- a/internal/command/v1/alias/delete/command.go +++ b/internal/command/alias/delete/command.go @@ -1,10 +1,11 @@ package delete import ( + "context" "fmt" "path/filepath" - cmdconfig "github.com/skpr/cli/internal/client/config/command" + cmdconfig "github.com/skpr/cli/internal/client/config/user" ) // Command struct. @@ -14,23 +15,27 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { +func (cmd *Command) Run(ctx context.Context) error { configPath := filepath.Join(cmd.Dir, "config.yml") configFile := cmdconfig.NewConfigFile(configPath) + exists, err := configFile.Exists() if err != nil { return err } + if !exists { fmt.Printf("Alias '%s' does not exist\n", cmd.Alias) return nil } + var config cmdconfig.Config config, err = configFile.Read() if err != nil { return err } + _, ok := config.Aliases[cmd.Alias] if !ok { fmt.Printf("Alias '%s' does not exist\n", cmd.Alias) @@ -45,5 +50,6 @@ func (cmd *Command) Run() error { } fmt.Println("Alias deleted.") + return nil } diff --git a/internal/command/v1/alias/list/command.go b/internal/command/alias/list/command.go similarity index 91% rename from internal/command/v1/alias/list/command.go rename to internal/command/alias/list/command.go index 372c5f2..2ca1266 100644 --- a/internal/command/v1/alias/list/command.go +++ b/internal/command/alias/list/command.go @@ -5,7 +5,7 @@ import ( "os" "path/filepath" - cmdconfig "github.com/skpr/cli/internal/client/config/command" + cmdconfig "github.com/skpr/cli/internal/client/config/user" ) // Command struct. diff --git a/internal/command/v1/alias/set/command.go b/internal/command/alias/set/command.go similarity index 92% rename from internal/command/v1/alias/set/command.go rename to internal/command/alias/set/command.go index 9507e59..3d5368c 100644 --- a/internal/command/v1/alias/set/command.go +++ b/internal/command/alias/set/command.go @@ -4,7 +4,7 @@ import ( "fmt" "path/filepath" - cmdconfig "github.com/skpr/cli/internal/client/config/command" + cmdconfig "github.com/skpr/cli/internal/client/config/user" ) // Command struct. diff --git a/internal/command/v1/backup/create/command.go b/internal/command/backup/create/command.go similarity index 95% rename from internal/command/v1/backup/create/command.go rename to internal/command/backup/create/command.go index 6695f9c..35c404c 100644 --- a/internal/command/v1/backup/create/command.go +++ b/internal/command/backup/create/command.go @@ -9,7 +9,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" skprlog "github.com/skpr/cli/internal/log" "github.com/skpr/cli/internal/retry" ) @@ -29,8 +29,8 @@ type MySQL struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/backup/create/command_test.go b/internal/command/backup/create/command_test.go similarity index 100% rename from internal/command/v1/backup/create/command_test.go rename to internal/command/backup/create/command_test.go diff --git a/internal/command/v1/backup/list/command.go b/internal/command/backup/list/command.go similarity index 92% rename from internal/command/v1/backup/list/command.go rename to internal/command/backup/list/command.go index 6895f20..5b19df9 100644 --- a/internal/command/v1/backup/list/command.go +++ b/internal/command/backup/list/command.go @@ -1,6 +1,7 @@ package list import ( + "context" "encoding/json" "fmt" "os" @@ -8,7 +9,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/color" "github.com/skpr/cli/internal/table" ) @@ -30,8 +31,8 @@ type Row struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/config/delete/command.go b/internal/command/config/delete/command.go similarity index 84% rename from internal/command/v1/config/delete/command.go rename to internal/command/config/delete/command.go index 5874245..24843b8 100644 --- a/internal/command/v1/config/delete/command.go +++ b/internal/command/config/delete/command.go @@ -1,9 +1,11 @@ package delete import ( + "context" + "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/confirmation" ) @@ -15,8 +17,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/config/get/command.go b/internal/command/config/get/command.go similarity index 76% rename from internal/command/v1/config/get/command.go rename to internal/command/config/get/command.go index c40425d..43f2609 100644 --- a/internal/command/v1/config/get/command.go +++ b/internal/command/config/get/command.go @@ -1,11 +1,12 @@ package get import ( + "context" "fmt" "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" ) // Command for getting a config. @@ -16,8 +17,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/config/list/command.go b/internal/command/config/list/command.go similarity index 95% rename from internal/command/v1/config/list/command.go rename to internal/command/config/list/command.go index b3737c9..57184f3 100644 --- a/internal/command/v1/config/list/command.go +++ b/internal/command/config/list/command.go @@ -1,6 +1,7 @@ package list import ( + "context" "encoding/json" "fmt" "io" @@ -10,7 +11,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/color" "github.com/skpr/cli/internal/components/tooltip" "github.com/skpr/cli/internal/table" @@ -31,8 +32,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/config/list/command_test.go b/internal/command/config/list/command_test.go similarity index 100% rename from internal/command/v1/config/list/command_test.go rename to internal/command/config/list/command_test.go diff --git a/internal/command/v1/config/list/sort.go b/internal/command/config/list/sort.go similarity index 100% rename from internal/command/v1/config/list/sort.go rename to internal/command/config/list/sort.go diff --git a/internal/command/v1/config/list/sort_test.go b/internal/command/config/list/sort_test.go similarity index 100% rename from internal/command/v1/config/list/sort_test.go rename to internal/command/config/list/sort_test.go diff --git a/internal/command/v1/config/set/command.go b/internal/command/config/set/command.go similarity index 90% rename from internal/command/v1/config/set/command.go rename to internal/command/config/set/command.go index 8f54838..4efa80a 100644 --- a/internal/command/v1/config/set/command.go +++ b/internal/command/config/set/command.go @@ -1,12 +1,13 @@ package set import ( + "context" "fmt" "os" "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/confirmation" ) @@ -21,8 +22,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/create/command.go b/internal/command/create/command.go similarity index 81% rename from internal/command/v1/create/command.go rename to internal/command/create/command.go index 46d2760..6a5a235 100644 --- a/internal/command/v1/create/command.go +++ b/internal/command/create/command.go @@ -1,7 +1,11 @@ package create import ( + "context" "fmt" + "github.com/skpr/cli/internal/client/project" + "github.com/skpr/cli/internal/client/utils" + "github.com/skpr/cli/internal/command/validate" "io" "os" @@ -11,10 +15,8 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" - "github.com/skpr/cli/internal/client/config/project" - envutils "github.com/skpr/cli/internal/client/utils/environment" - "github.com/skpr/cli/internal/command/v1/validate" + "github.com/skpr/cli/internal/client" + envutils "github.com/skpr/cli/internal/environment" ) // Command for creating an environment. @@ -24,17 +26,19 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } fmt.Println("Loading environment configuration") - projectDir, err := client.Discovery.Project.GetDirectory() - if err != nil { - return err + + projectDir := utils.FindSkprConfigDir(".") // @todo, Make this dynamic. + if projectDir == "" { + return fmt.Errorf("could not find project directory") } + env, err := project.LoadFromDirectory(projectDir, cmd.Environment) if err != nil { return errors.Wrap(err, "failed to load environment") diff --git a/internal/command/v1/cron/job/list/command.go b/internal/command/cron/job/list/command.go similarity index 85% rename from internal/command/v1/cron/job/list/command.go rename to internal/command/cron/job/list/command.go index a5305c4..633588d 100644 --- a/internal/command/v1/cron/job/list/command.go +++ b/internal/command/cron/job/list/command.go @@ -1,12 +1,13 @@ package list import ( + "context" "fmt" "os" "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/color" "github.com/skpr/cli/internal/table" ) @@ -17,8 +18,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/cron/list/command.go b/internal/command/cron/list/command.go similarity index 86% rename from internal/command/v1/cron/list/command.go rename to internal/command/cron/list/command.go index a39cc96..b385f18 100644 --- a/internal/command/v1/cron/list/command.go +++ b/internal/command/cron/list/command.go @@ -1,13 +1,14 @@ package list import ( + "context" "fmt" "os" "strconv" "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/table" ) @@ -17,8 +18,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/cron/resume/command.go b/internal/command/cron/resume/command.go similarity index 74% rename from internal/command/v1/cron/resume/command.go rename to internal/command/cron/resume/command.go index 384a767..4c49c71 100644 --- a/internal/command/v1/cron/resume/command.go +++ b/internal/command/cron/resume/command.go @@ -1,12 +1,13 @@ package resume import ( + "context" "fmt" "os" "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" ) // Command for resuming cron jobs. @@ -15,8 +16,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/cron/suspend/command.go b/internal/command/cron/suspend/command.go similarity index 90% rename from internal/command/v1/cron/suspend/command.go rename to internal/command/cron/suspend/command.go index caca437..87e813a 100644 --- a/internal/command/v1/cron/suspend/command.go +++ b/internal/command/cron/suspend/command.go @@ -1,13 +1,14 @@ package suspend import ( + "context" "fmt" "os" "time" "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" ) // Command to suspend cron jobs. @@ -18,8 +19,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/daemon/list/command.go b/internal/command/daemon/list/command.go similarity index 87% rename from internal/command/v1/daemon/list/command.go rename to internal/command/daemon/list/command.go index de8ce92..914bf4d 100644 --- a/internal/command/v1/daemon/list/command.go +++ b/internal/command/daemon/list/command.go @@ -1,6 +1,7 @@ package list import ( + "context" "encoding/json" "fmt" "io" @@ -9,7 +10,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/table" ) @@ -20,8 +21,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/daemon/resume/command.go b/internal/command/daemon/resume/command.go similarity index 74% rename from internal/command/v1/daemon/resume/command.go rename to internal/command/daemon/resume/command.go index 98ecece..61b432c 100644 --- a/internal/command/v1/daemon/resume/command.go +++ b/internal/command/daemon/resume/command.go @@ -1,12 +1,13 @@ package resume import ( + "context" "fmt" "os" "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" ) // Command that resumes daemons. @@ -15,8 +16,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/daemon/suspend/command.go b/internal/command/daemon/suspend/command.go similarity index 89% rename from internal/command/v1/daemon/suspend/command.go rename to internal/command/daemon/suspend/command.go index a02b7c1..054c4c0 100644 --- a/internal/command/v1/daemon/suspend/command.go +++ b/internal/command/daemon/suspend/command.go @@ -1,13 +1,14 @@ package suspend import ( + "context" "fmt" "os" "time" "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" ) // Command that suspends daemons. @@ -18,8 +19,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/dashboard/command.go b/internal/command/dashboard/command.go similarity index 84% rename from internal/command/v1/dashboard/command.go rename to internal/command/dashboard/command.go index 4ae8317..837654f 100644 --- a/internal/command/v1/dashboard/command.go +++ b/internal/command/dashboard/command.go @@ -1,13 +1,14 @@ package dashboard import ( + "context" "fmt" "github.com/skratchdot/open-golang/open" "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" ) // Command for dashboard access. @@ -17,8 +18,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/delete/command.go b/internal/command/delete/command.go similarity index 94% rename from internal/command/v1/delete/command.go rename to internal/command/delete/command.go index 3d988a6..a309430 100644 --- a/internal/command/v1/delete/command.go +++ b/internal/command/delete/command.go @@ -1,6 +1,7 @@ package delete import ( + "context" "fmt" "os" @@ -9,7 +10,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/confirmation" ) @@ -22,8 +23,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/deploy/command.go b/internal/command/deploy/command.go similarity index 81% rename from internal/command/v1/deploy/command.go rename to internal/command/deploy/command.go index 237b109..66d0e8a 100644 --- a/internal/command/v1/deploy/command.go +++ b/internal/command/deploy/command.go @@ -1,7 +1,11 @@ package deploy import ( + "context" "fmt" + "github.com/skpr/cli/internal/client/project" + "github.com/skpr/cli/internal/client/utils" + "github.com/skpr/cli/internal/command/validate" "io" "os" @@ -11,10 +15,8 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" - "github.com/skpr/cli/internal/client/config/project" - envutils "github.com/skpr/cli/internal/client/utils/environment" - "github.com/skpr/cli/internal/command/v1/validate" + "github.com/skpr/cli/internal/client" + envutils "github.com/skpr/cli/internal/environment" ) // Command to delete the environment. @@ -24,17 +26,19 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } fmt.Println("Loading environment configuration") - projectDir, err := client.Discovery.Project.GetDirectory() - if err != nil { - return err + + projectDir := utils.FindSkprConfigDir(".") // @todo, Make this dynamic. + if projectDir == "" { + return fmt.Errorf("could not find project directory") } + env, err := project.LoadFromDirectory(projectDir, cmd.Environment) if err != nil { return errors.Wrap(err, "failed to load environment") diff --git a/internal/command/v1/exec/command.go b/internal/command/exec/command.go similarity index 78% rename from internal/command/v1/exec/command.go rename to internal/command/exec/command.go index 62d6670..7d20de4 100644 --- a/internal/command/v1/exec/command.go +++ b/internal/command/exec/command.go @@ -1,12 +1,13 @@ package exec import ( + "context" + "github.com/skpr/cli/internal/client/ssh" "os" "github.com/pkg/errors" - wfclient "github.com/skpr/cli/internal/client" - "github.com/skpr/cli/internal/client/ssh" + "github.com/skpr/cli/internal/client" ) // Command to exec a command on an environment. @@ -16,8 +17,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, _, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + _, client, err := client.New(ctx) if err != nil { return errors.Wrap(err, "failed to create client") } diff --git a/internal/command/v1/info/command.go b/internal/command/info/command.go similarity index 84% rename from internal/command/v1/info/command.go rename to internal/command/info/command.go index cf6cd16..c5e2c63 100644 --- a/internal/command/v1/info/command.go +++ b/internal/command/info/command.go @@ -1,6 +1,7 @@ package info import ( + "context" "encoding/json" "fmt" @@ -8,7 +9,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" ) // Command to get information about the environment. @@ -18,8 +19,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/init/command.go b/internal/command/init/command.go similarity index 92% rename from internal/command/v1/init/command.go rename to internal/command/init/command.go index d8a84a9..e2e5005 100644 --- a/internal/command/v1/init/command.go +++ b/internal/command/init/command.go @@ -2,13 +2,14 @@ package init import ( "bufio" + "context" "fmt" "os" "strings" "github.com/pkg/errors" - "github.com/skpr/cli/internal/client/config/project" + "github.com/skpr/cli/internal/client/project" ) // Command for initializing a project. @@ -20,7 +21,7 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { +func (cmd *Command) Run(ctx context.Context) error { reader := bufio.NewReader(os.Stdin) diff --git a/internal/command/v1/list/command.go b/internal/command/list/command.go similarity index 91% rename from internal/command/v1/list/command.go rename to internal/command/list/command.go index 5099e2d..f0354a3 100644 --- a/internal/command/v1/list/command.go +++ b/internal/command/list/command.go @@ -1,6 +1,7 @@ package list import ( + "context" "fmt" "io" "os" @@ -11,7 +12,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/color" "github.com/skpr/cli/internal/table" ) @@ -20,8 +21,8 @@ import ( type Command struct{} // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/list/command_test.go b/internal/command/list/command_test.go similarity index 100% rename from internal/command/v1/list/command_test.go rename to internal/command/list/command_test.go diff --git a/internal/command/v1/login/command.go b/internal/command/login/command.go similarity index 73% rename from internal/command/v1/login/command.go rename to internal/command/login/command.go index 699fd5f..61bd261 100644 --- a/internal/command/v1/login/command.go +++ b/internal/command/login/command.go @@ -10,14 +10,11 @@ import ( "golang.org/x/sync/errgroup" "github.com/skpr/api/pb" - "github.com/skpr/cli/internal/aws/cognito/oidc/login" "github.com/skpr/cli/internal/aws/cognito/oidc/rand" "github.com/skpr/cli/internal/client" - "github.com/skpr/cli/internal/client/config/clusters" - credentialscache "github.com/skpr/cli/internal/client/config/credentials/cache" - skprdiscovery "github.com/skpr/cli/internal/client/config/discovery" - "github.com/skpr/cli/internal/client/config/project" + "github.com/skpr/cli/internal/client/config" + cache2 "github.com/skpr/cli/internal/client/credentials/cache" ) const ( @@ -31,37 +28,17 @@ type Command struct { } // Run the login command. -func (cmd *Command) Run() error { +func (cmd *Command) Run(ctx context.Context) error { log.Println("Connecting to cluster") - discovery, err := skprdiscovery.New() - if err != nil { - return fmt.Errorf("failed to discover config %w", err) - } - - configFile, err := discovery.Config() + config, err := config.New() if err != nil { - return fmt.Errorf("failed to get project config file path %w", err) + return fmt.Errorf("could not create config: %w", err) } - config, err := project.LoadConfig(configFile) + conn, err := client.Dial(config) if err != nil { - return fmt.Errorf("failed to get project config %w", err) - } - - clusterFile, err := discovery.Clusters() - if err != nil { - return fmt.Errorf("failed to get clusters file %w", err) - } - - clusterCfg, err := clusters.LoadFromFile(clusterFile, config.Cluster) - if err != nil { - return fmt.Errorf("failed to get clusters config %w", err) - } - - conn, err := client.Dial(clusterCfg.API) - if err != nil { - return err + return fmt.Errorf("could not connect to cluster: %w", err) } // @todo, We should not have to do this many function calls @@ -69,8 +46,6 @@ func (cmd *Command) Run() error { client := pb.NewLoginClient(conn) - ctx := context.TODO() - providerInfo, err := client.GetProviderInfo(ctx, &pb.LoginGetProviderInfoRequest{}) if err != nil { return err @@ -130,19 +105,19 @@ func (cmd *Command) Run() error { } // Store this information for later so we can generate temporary credentials. - credentials := credentialscache.Credentials{ + credentials := cache2.Credentials{ Config: oath2Config, - Token: credentialscache.Token{ + Token: cache2.Token{ Refresh: token.RefreshToken, }, - Cognito: credentialscache.Cognito{ + Cognito: cache2.Cognito{ Region: providerInfo.Cognito.Region, IdentityPoolID: providerInfo.Cognito.IdentityPoolID, IdentityProviderID: providerInfo.Cognito.IdentityProviderID, }, } - err = credentialscache.Set(config.Cluster, credentials) + err = cache2.Set(config.API.Host(), credentials) if err != nil { return fmt.Errorf("failed to store credentials: %w", err) } diff --git a/internal/command/v1/logout/command.go b/internal/command/logout/command.go similarity index 71% rename from internal/command/v1/logout/command.go rename to internal/command/logout/command.go index 5e39b6b..462da06 100644 --- a/internal/command/v1/logout/command.go +++ b/internal/command/logout/command.go @@ -13,13 +13,10 @@ import ( "golang.org/x/sync/errgroup" "github.com/skpr/api/pb" - oidclogin "github.com/skpr/cli/internal/aws/cognito/oidc/login" "github.com/skpr/cli/internal/client" - "github.com/skpr/cli/internal/client/config/clusters" - credentialscache "github.com/skpr/cli/internal/client/config/credentials/cache" - skprdiscovery "github.com/skpr/cli/internal/client/config/discovery" - "github.com/skpr/cli/internal/client/config/project" + "github.com/skpr/cli/internal/client/config" + credentialscache "github.com/skpr/cli/internal/client/credentials/cache" ) // Command to logout from the platform. @@ -28,34 +25,22 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { +func (cmd *Command) Run(ctx context.Context) error { log.Println("Connecting to cluster") - ctx := context.TODO() - - discovery, err := skprdiscovery.New() - if err != nil { - return fmt.Errorf("failed to discover config %w", err) - } - - configFile, err := discovery.Config() + config, err := config.New() if err != nil { - return fmt.Errorf("failed to get project config file path %w", err) + return fmt.Errorf("failed to get project config: %w", err) } - config, err := project.LoadConfig(configFile) + credentials, found, err := credentialscache.Get(config.API.Host()) if err != nil { - return fmt.Errorf("failed to get project config %w", err) + return fmt.Errorf("failed to get cached credentials: %w", err) } - if credentialscache.Exists(config.Cluster) { + if found { log.Println("Deleting cached credentials") - credentials, err := credentialscache.Get(config.Cluster) - if err != nil { - return fmt.Errorf("failed to get cached credentials: %w", err) - } - token, err := credentials.GetToken(ctx) if err != nil { return fmt.Errorf("failed to get token source: %w", err) @@ -76,25 +61,15 @@ func (cmd *Command) Run() error { } // Delete the file now that we have invalidated our tokens. - err = credentialscache.Delete(config.Cluster) + err = credentialscache.Delete(config.API.Host()) if err != nil { return fmt.Errorf("failed to delete credentials cache %w", err) } } - clusterFile, err := discovery.Clusters() + conn, err := client.Dial(config) if err != nil { - return fmt.Errorf("failed to get clusters file %w", err) - } - - clusterCfg, err := clusters.LoadFromFile(clusterFile, config.Cluster) - if err != nil { - return fmt.Errorf("failed to get clusters config %w", err) - } - - conn, err := client.Dial(clusterCfg.API) - if err != nil { - return err + return fmt.Errorf("could not connect to cluster: %w", err) } loginClient := pb.NewLoginClient(conn) diff --git a/internal/command/v1/logs/list/command.go b/internal/command/logs/list/command.go similarity index 89% rename from internal/command/v1/logs/list/command.go rename to internal/command/logs/list/command.go index e149830..1451caa 100644 --- a/internal/command/v1/logs/list/command.go +++ b/internal/command/logs/list/command.go @@ -1,12 +1,13 @@ package list import ( + "context" "fmt" "os" "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/components/tooltip" skprtable "github.com/skpr/cli/internal/table" ) @@ -22,8 +23,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/logs/tail/command.go b/internal/command/logs/tail/command.go similarity index 94% rename from internal/command/v1/logs/tail/command.go rename to internal/command/logs/tail/command.go index 28acdd5..2280db6 100644 --- a/internal/command/v1/logs/tail/command.go +++ b/internal/command/logs/tail/command.go @@ -1,6 +1,7 @@ package tail import ( + "context" "encoding/json" "fmt" "io" @@ -14,7 +15,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/color" ) @@ -26,8 +27,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/mysql/backup/create/command.go b/internal/command/mysql/backup/create/command.go similarity index 90% rename from internal/command/v1/mysql/backup/create/command.go rename to internal/command/mysql/backup/create/command.go index f34244d..964b614 100644 --- a/internal/command/v1/mysql/backup/create/command.go +++ b/internal/command/mysql/backup/create/command.go @@ -1,6 +1,7 @@ package create import ( + "context" "fmt" "os" "time" @@ -9,7 +10,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" ) // Command to create a backup. @@ -20,8 +21,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/mysql/backup/list/command.go b/internal/command/mysql/backup/list/command.go similarity index 94% rename from internal/command/v1/mysql/backup/list/command.go rename to internal/command/mysql/backup/list/command.go index 9e261d7..ba4d7b5 100644 --- a/internal/command/v1/mysql/backup/list/command.go +++ b/internal/command/mysql/backup/list/command.go @@ -1,6 +1,7 @@ package list import ( + "context" "encoding/json" "fmt" "os" @@ -10,7 +11,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/color" "github.com/skpr/cli/internal/table" timeutils "github.com/skpr/cli/internal/time" @@ -34,8 +35,8 @@ type Row struct { } // Run the list command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return errors.Wrap(err, "failed to create client") } diff --git a/internal/command/v1/mysql/image/create/command.go b/internal/command/mysql/image/create/command.go similarity index 83% rename from internal/command/v1/mysql/image/create/command.go rename to internal/command/mysql/image/create/command.go index 5f3c576..9319200 100644 --- a/internal/command/v1/mysql/image/create/command.go +++ b/internal/command/mysql/image/create/command.go @@ -1,13 +1,14 @@ package create import ( + "context" "fmt" "github.com/pkg/errors" "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" ) // Command to create an image. @@ -19,8 +20,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return errors.Wrap(err, "failed to create client") } diff --git a/internal/command/v1/mysql/image/list/command.go b/internal/command/mysql/image/list/command.go similarity index 93% rename from internal/command/v1/mysql/image/list/command.go rename to internal/command/mysql/image/list/command.go index d003641..c6b1733 100644 --- a/internal/command/v1/mysql/image/list/command.go +++ b/internal/command/mysql/image/list/command.go @@ -1,6 +1,7 @@ package list import ( + "context" "fmt" "os" "strings" @@ -10,7 +11,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/color" "github.com/skpr/cli/internal/table" timeutils "github.com/skpr/cli/internal/time" @@ -32,8 +33,8 @@ type Row struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return errors.Wrap(err, "failed to create client") } diff --git a/internal/command/v1/mysql/image/pull/command.go b/internal/command/mysql/image/pull/command.go similarity index 86% rename from internal/command/v1/mysql/image/pull/command.go rename to internal/command/mysql/image/pull/command.go index f823c60..3bf9dc7 100644 --- a/internal/command/v1/mysql/image/pull/command.go +++ b/internal/command/mysql/image/pull/command.go @@ -1,6 +1,7 @@ package pull import ( + "context" "fmt" "log/slog" "os" @@ -12,7 +13,7 @@ import ( "github.com/skpr/api/pb" "github.com/skpr/cli/internal/buildpack/utils/aws/ecr" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" skprlog "github.com/skpr/cli/internal/log" ) @@ -35,8 +36,8 @@ type Params struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } @@ -53,23 +54,17 @@ func (cmd *Command) Run() error { Environment: cmd.Params.Environment, }) if err != nil { - logger.Info("Using backwards compatibility command to pull image.") - return cmd.runBackwardsCompat() - } - - creds, err := client.CredentialsProvider.Retrieve(ctx) - if err != nil { - return errors.Wrap(err, "failed to get credentials") + return fmt.Errorf("failed to get repository: %w", err) } auth := docker.AuthConfiguration{ - Username: creds.AccessKeyID, - Password: creds.SecretAccessKey, + Username: client.Credentials.Username, + Password: client.Credentials.Password, } // @todo, Consider abstracting this if another registry + credentials pair is required. if ecr.IsRegistry(getRepositoryResp.Repository) { - auth, err = ecr.UpgradeAuth(ctx, getRepositoryResp.Repository, creds) + auth, err = ecr.UpgradeAuth(ctx, getRepositoryResp.Repository, client.Credentials) if err != nil { return errors.Wrap(err, "failed to upgrade AWS ECR authentication") } diff --git a/internal/command/v1/mysql/image/pull/image.go b/internal/command/mysql/image/pull/image.go similarity index 100% rename from internal/command/v1/mysql/image/pull/image.go rename to internal/command/mysql/image/pull/image.go diff --git a/internal/command/v1/mysql/image/pull/image_test.go b/internal/command/mysql/image/pull/image_test.go similarity index 100% rename from internal/command/v1/mysql/image/pull/image_test.go rename to internal/command/mysql/image/pull/image_test.go diff --git a/internal/command/v1/mysql/image/pull/tag.go b/internal/command/mysql/image/pull/tag.go similarity index 100% rename from internal/command/v1/mysql/image/pull/tag.go rename to internal/command/mysql/image/pull/tag.go diff --git a/internal/command/v1/mysql/image/pull/tag_test.go b/internal/command/mysql/image/pull/tag_test.go similarity index 100% rename from internal/command/v1/mysql/image/pull/tag_test.go rename to internal/command/mysql/image/pull/tag_test.go diff --git a/internal/command/v1/mysql/restore/create/command.go b/internal/command/mysql/restore/create/command.go similarity index 92% rename from internal/command/v1/mysql/restore/create/command.go rename to internal/command/mysql/restore/create/command.go index bec929b..978ce8a 100644 --- a/internal/command/v1/mysql/restore/create/command.go +++ b/internal/command/mysql/restore/create/command.go @@ -1,6 +1,7 @@ package create import ( + "context" "fmt" "os" "time" @@ -9,7 +10,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/confirmation" ) @@ -23,8 +24,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/mysql/restore/list/command.go b/internal/command/mysql/restore/list/command.go similarity index 94% rename from internal/command/v1/mysql/restore/list/command.go rename to internal/command/mysql/restore/list/command.go index 1f84dde..edbe301 100644 --- a/internal/command/v1/mysql/restore/list/command.go +++ b/internal/command/mysql/restore/list/command.go @@ -1,6 +1,7 @@ package list import ( + "context" "encoding/json" "fmt" "os" @@ -10,7 +11,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/color" "github.com/skpr/cli/internal/table" timeutils "github.com/skpr/cli/internal/time" @@ -35,8 +36,8 @@ type Row struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return errors.Wrap(err, "failed to create client") } diff --git a/internal/command/v1/package/command.go b/internal/command/package/command.go similarity index 89% rename from internal/command/v1/package/command.go rename to internal/command/package/command.go index 5788d71..d3e8faa 100644 --- a/internal/command/v1/package/command.go +++ b/internal/command/package/command.go @@ -1,6 +1,7 @@ package pkg import ( + "context" "encoding/json" "fmt" "os" @@ -22,7 +23,8 @@ import ( "github.com/skpr/cli/internal/buildpack/utils/aws/ecr" "github.com/skpr/cli/internal/buildpack/utils/finder" "github.com/skpr/cli/internal/buildpack/utils/notation/utils" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/slice" ) // Command to package an application. @@ -31,12 +33,13 @@ type Command struct { PackageDir string Params buildpack.Params PrintManifest bool + BuildArgs []string Debug bool } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return fmt.Errorf("failed to create client: %w", err) } @@ -60,21 +63,20 @@ func (cmd *Command) Run() error { cmd.Params.Registry = project.Registry.Application cmd.Params.Writer = os.Stderr - creds, err := client.CredentialsProvider.Retrieve(ctx) - if err != nil { - return fmt.Errorf("failed to get credentials: %w", err) - } - cmd.Params.Auth = docker.AuthConfiguration{ - Username: creds.AccessKeyID, - Password: creds.SecretAccessKey, + Username: client.Credentials.Username, + Password: client.Credentials.Password, } + // Convert build args from slice to map. + // eg. --build-arg=KEY=VALUE to map[string]string{"KEY": "VALUE"} + cmd.Params.BuildArgs = slice.ToMap(cmd.BuildArgs, "=") + isECR := ecr.IsRegistry(cmd.Params.Registry) // @todo, Consider abstracting this if another registry + credentials pair is required. if isECR { - auth, err := ecr.UpgradeAuth(ctx, cmd.Params.Registry, creds) + auth, err := ecr.UpgradeAuth(ctx, cmd.Params.Registry, client.Credentials) if err != nil { return fmt.Errorf("failed to upgrade AWS ECR authentication: %w", err) } @@ -133,7 +135,7 @@ func (cmd *Command) Run() error { cfg, err := config.LoadDefaultConfig( ctx, config.WithRegion(cmd.Region), - config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(creds.AccessKeyID, creds.SecretAccessKey, creds.SessionToken)), + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(client.Credentials.Username, client.Credentials.Password, client.Credentials.Session)), ) if err != nil { return fmt.Errorf("failed to load AWS config: %w", err) diff --git a/internal/command/v1/purge/create/command.go b/internal/command/purge/create/command.go similarity index 78% rename from internal/command/v1/purge/create/command.go rename to internal/command/purge/create/command.go index 1a68814..5ffa838 100644 --- a/internal/command/v1/purge/create/command.go +++ b/internal/command/purge/create/command.go @@ -1,13 +1,14 @@ package create import ( + "context" "fmt" "github.com/pkg/errors" "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" ) // Command to create a purge request. @@ -17,8 +18,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/purge/list/command.go b/internal/command/purge/list/command.go similarity index 85% rename from internal/command/v1/purge/list/command.go rename to internal/command/purge/list/command.go index b9a43ae..15879f0 100644 --- a/internal/command/v1/purge/list/command.go +++ b/internal/command/purge/list/command.go @@ -1,6 +1,7 @@ package list import ( + "context" "fmt" "github.com/gosuri/uitable" @@ -8,7 +9,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/list" ) @@ -18,8 +19,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/release/info/command.go b/internal/command/release/info/command.go similarity index 90% rename from internal/command/v1/release/info/command.go rename to internal/command/release/info/command.go index 6073348..3ea4403 100644 --- a/internal/command/v1/release/info/command.go +++ b/internal/command/release/info/command.go @@ -1,6 +1,7 @@ package info import ( + "context" "encoding/json" "fmt" "io" @@ -8,7 +9,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/table" ) @@ -30,8 +31,8 @@ type manifestItem struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/release/list/command.go b/internal/command/release/list/command.go similarity index 85% rename from internal/command/v1/release/list/command.go rename to internal/command/release/list/command.go index 8ae5f90..92b80da 100644 --- a/internal/command/v1/release/list/command.go +++ b/internal/command/release/list/command.go @@ -1,6 +1,7 @@ package list import ( + "context" "fmt" "os" "strings" @@ -9,7 +10,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/table" ) @@ -21,8 +22,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/restore/create/command.go b/internal/command/restore/create/command.go similarity index 95% rename from internal/command/v1/restore/create/command.go rename to internal/command/restore/create/command.go index 1247c0d..7c6141f 100644 --- a/internal/command/v1/restore/create/command.go +++ b/internal/command/restore/create/command.go @@ -9,7 +9,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/confirmation" skprlog "github.com/skpr/cli/internal/log" "github.com/skpr/cli/internal/retry" @@ -26,8 +26,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/restore/create/command_test.go b/internal/command/restore/create/command_test.go similarity index 100% rename from internal/command/v1/restore/create/command_test.go rename to internal/command/restore/create/command_test.go diff --git a/internal/command/v1/restore/list/command.go b/internal/command/restore/list/command.go similarity index 93% rename from internal/command/v1/restore/list/command.go rename to internal/command/restore/list/command.go index c4ccb6b..b508113 100644 --- a/internal/command/v1/restore/list/command.go +++ b/internal/command/restore/list/command.go @@ -1,6 +1,7 @@ package list import ( + "context" "encoding/json" "fmt" "os" @@ -8,7 +9,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/color" "github.com/skpr/cli/internal/table" ) @@ -31,8 +32,8 @@ type Row struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/rsync/command.go b/internal/command/rsync/command.go similarity index 94% rename from internal/command/v1/rsync/command.go rename to internal/command/rsync/command.go index 3de8cda..23ebe2c 100644 --- a/internal/command/v1/rsync/command.go +++ b/internal/command/rsync/command.go @@ -1,6 +1,7 @@ package rsync import ( + "context" "fmt" "os" "os/exec" @@ -18,7 +19,7 @@ type Command struct { } // Run the command. -func (c *Command) Run() error { +func (c *Command) Run(_ context.Context) error { executable, err := os.Executable() if err != nil { return errors.Wrap(err, "failed to lookup binary path") diff --git a/internal/command/v1/rsync/command_test.go b/internal/command/rsync/command_test.go similarity index 100% rename from internal/command/v1/rsync/command_test.go rename to internal/command/rsync/command_test.go diff --git a/internal/command/v1/shell/command.go b/internal/command/shell/command.go similarity index 76% rename from internal/command/v1/shell/command.go rename to internal/command/shell/command.go index 8975477..6fb4469 100644 --- a/internal/command/v1/shell/command.go +++ b/internal/command/shell/command.go @@ -1,12 +1,13 @@ package shell import ( + "context" + "github.com/skpr/cli/internal/client/ssh" "os" "github.com/pkg/errors" - wfclient "github.com/skpr/cli/internal/client" - "github.com/skpr/cli/internal/client/ssh" + "github.com/skpr/cli/internal/client" ) // Command to shell into an environment. @@ -15,8 +16,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, _, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + _, client, err := client.New(ctx) if err != nil { return errors.Wrap(err, "failed to create client") } diff --git a/internal/command/v1/mysql/image/pull/backwards.go b/internal/command/v1/mysql/image/pull/backwards.go deleted file mode 100644 index d44b641..0000000 --- a/internal/command/v1/mysql/image/pull/backwards.go +++ /dev/null @@ -1,118 +0,0 @@ -package pull - -import ( - "fmt" - - docker "github.com/fsouza/go-dockerclient" - "github.com/gosuri/uilive" - "github.com/pkg/errors" - - "github.com/skpr/api/pb" - - "github.com/skpr/cli/internal/buildpack/utils/aws/ecr" - wfclient "github.com/skpr/cli/internal/client" -) - -// @todo, Remove in a future release. -func (cmd *Command) runBackwardsCompat() error { - client, ctx, err := wfclient.NewFromFile() - if err != nil { - return err - } - - project, err := client.Project().Get(ctx, &pb.ProjectGetRequest{}) - if err != nil { - return errors.Wrap(err, "failed to get project") - } - - creds, err := client.CredentialsProvider.Retrieve(ctx) - if err != nil { - return errors.Wrap(err, "failed to get credentials") - } - - auth := docker.AuthConfiguration{ - Username: creds.AccessKeyID, - Password: creds.SecretAccessKey, - } - - // @todo, Consider abstracting this if another registry + credentials pair is required. - if ecr.IsRegistry(project.Registry.MySQL) { - auth, err = ecr.UpgradeAuth(ctx, project.Registry.MySQL, creds) - if err != nil { - return errors.Wrap(err, "failed to upgrade AWS ECR authentication") - } - } - - dockerclient, err := docker.NewClientFromEnv() - if err != nil { - return errors.Wrap(err, "failed to setup Docker client") - } - - writer := uilive.New() - writer.Start() - - var images []string - - for _, database := range cmd.Params.Databases { - tag, err := getTag(ctx, client.Mysql(), database, cmd.Params) - if err != nil { - return fmt.Errorf("failed to compute image tag: %w", err) - } - - imageName := fmt.Sprintf("%s:%s", project.Registry.MySQL, tag) - - // Keep for later so we can inform the developer on which images they can use. - images = append(images, imageName) - - fmt.Printf("Pulling: %s\n", imageName) - - // Lookup the ID of the current image so we can delete it after we pull the image one. - cleanup, err := dockerclient.InspectImage(imageName) - if err != nil && !errors.Is(err, docker.ErrNoSuchImage) { - return err - } - - opts := docker.PullImageOptions{ - OutputStream: writer, - Repository: project.Registry.MySQL, - Tag: tag, - } - - err = dockerclient.PullImage(opts, auth) - if err != nil { - return err - } - - // Check if there was an old images which - if cleanup == nil { - continue - } - - current, err := dockerclient.InspectImage(imageName) - if err != nil { - return err - } - - // Don't cleanup the old image if it was the latest and never needed to be updated. - if cleanup.ID == current.ID { - continue - } - - fmt.Println("Cleaning up old image with:", cleanup.ID) - - err = dockerclient.RemoveImage(cleanup.ID) - if err != nil { - return err - } - } - - writer.Stop() - - fmt.Println("The following images have been successfully pulled:") - - for _, image := range images { - fmt.Println("-", image) - } - - return nil -} diff --git a/internal/command/v1/validate/command.go b/internal/command/validate/command.go similarity index 82% rename from internal/command/v1/validate/command.go rename to internal/command/validate/command.go index 46c2352..8475794 100644 --- a/internal/command/v1/validate/command.go +++ b/internal/command/validate/command.go @@ -3,6 +3,7 @@ package validate import ( "context" "fmt" + "io" "os" @@ -10,9 +11,9 @@ import ( "github.com/pkg/errors" "github.com/skpr/api/pb" - - wfclient "github.com/skpr/cli/internal/client" - "github.com/skpr/cli/internal/client/config/project" + "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client/project" + "github.com/skpr/cli/internal/client/utils" "github.com/skpr/cli/internal/table" ) @@ -23,16 +24,13 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } - projectDir, err := client.Discovery.Project.GetDirectory() - if err != nil { - return err - } + projectDir := utils.FindSkprConfigDir(".") // @todo, Make this dynamic. env, err := project.LoadFromDirectory(projectDir, cmd.Environment) if err != nil { @@ -60,7 +58,7 @@ func (cmd *Command) Run() error { // PrintTable of validation findings. // Used by the create and deploy commands. -func PrintTable(ctx context.Context, w io.Writer, client *wfclient.Client, proto *pb.Environment) (bool, error) { +func PrintTable(ctx context.Context, w io.Writer, client *client.Client, proto *pb.Environment) (bool, error) { resp, err := client.Environment().Validate(ctx, &pb.EnvironmentValidateRequest{ Environment: proto, }) diff --git a/internal/command/v1/version/command.go b/internal/command/version/command.go similarity index 87% rename from internal/command/v1/version/command.go rename to internal/command/version/command.go index bb8bfd5..9f10c69 100644 --- a/internal/command/v1/version/command.go +++ b/internal/command/version/command.go @@ -1,12 +1,13 @@ package version import ( + "context" "fmt" "os" "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/version" ) @@ -25,14 +26,14 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { +func (cmd *Command) Run(ctx context.Context) error { params := version.PrintParams{ ClientVersion: GitVersion, ClientBuildDate: BuildDate, } // Get server version if we are in a project directory. - client, ctx, err := wfclient.NewFromFile() + ctx, client, err := client.New(ctx) if err == nil { resp, err := client.Version().Get(ctx, &pb.VersionGetRequest{}) if err != nil && cmd.Debug { diff --git a/internal/command/v1/volume/backup/create/command.go b/internal/command/volume/backup/create/command.go similarity index 90% rename from internal/command/v1/volume/backup/create/command.go rename to internal/command/volume/backup/create/command.go index 458373c..ebc052a 100644 --- a/internal/command/v1/volume/backup/create/command.go +++ b/internal/command/volume/backup/create/command.go @@ -1,6 +1,7 @@ package create import ( + "context" "fmt" "os" "time" @@ -9,7 +10,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" ) // Command to create a volume backup. @@ -20,8 +21,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/volume/backup/list/command.go b/internal/command/volume/backup/list/command.go similarity index 94% rename from internal/command/v1/volume/backup/list/command.go rename to internal/command/volume/backup/list/command.go index 2686138..f860ea2 100644 --- a/internal/command/v1/volume/backup/list/command.go +++ b/internal/command/volume/backup/list/command.go @@ -1,6 +1,7 @@ package list import ( + "context" "encoding/json" "fmt" "os" @@ -10,7 +11,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/color" "github.com/skpr/cli/internal/table" timeutils "github.com/skpr/cli/internal/time" @@ -34,8 +35,8 @@ type Row struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return errors.Wrap(err, "failed to create client") } diff --git a/internal/command/v1/volume/restore/create/command.go b/internal/command/volume/restore/create/command.go similarity index 92% rename from internal/command/v1/volume/restore/create/command.go rename to internal/command/volume/restore/create/command.go index cef3254..fff23a6 100644 --- a/internal/command/v1/volume/restore/create/command.go +++ b/internal/command/volume/restore/create/command.go @@ -1,6 +1,7 @@ package create import ( + "context" "fmt" "os" "time" @@ -9,7 +10,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/confirmation" ) @@ -23,8 +24,8 @@ type Command struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return err } diff --git a/internal/command/v1/volume/restore/list/command.go b/internal/command/volume/restore/list/command.go similarity index 94% rename from internal/command/v1/volume/restore/list/command.go rename to internal/command/volume/restore/list/command.go index 2d9e9df..f70642b 100644 --- a/internal/command/v1/volume/restore/list/command.go +++ b/internal/command/volume/restore/list/command.go @@ -1,6 +1,7 @@ package list import ( + "context" "encoding/json" "fmt" "os" @@ -10,7 +11,7 @@ import ( "github.com/skpr/api/pb" - wfclient "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/color" "github.com/skpr/cli/internal/table" timeutils "github.com/skpr/cli/internal/time" @@ -35,8 +36,8 @@ type Row struct { } // Run the command. -func (cmd *Command) Run() error { - client, ctx, err := wfclient.NewFromFile() +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) if err != nil { return errors.Wrap(err, "failed to create client") } diff --git a/internal/environment/environment.go b/internal/environment/environment.go new file mode 100644 index 0000000..d454785 --- /dev/null +++ b/internal/environment/environment.go @@ -0,0 +1,14 @@ +package environment + +import "github.com/skpr/api/pb" + +// Contains checks if an environment already exists. +func Contains(name string, list []*pb.Environment) bool { + for _, item := range list { + if item.Name == name { + return true + } + } + + return false +} diff --git a/internal/slice/slice.go b/internal/slice/slice.go index 3009638..8d83640 100644 --- a/internal/slice/slice.go +++ b/internal/slice/slice.go @@ -1,5 +1,7 @@ package slice +import "strings" + // Contains a string within a slice. func Contains(slice []string, s string) bool { for _, item := range slice { @@ -67,3 +69,20 @@ func AppendIfMissing(slice []string, i string) []string { return append(slice, i) } + +// ToMap converts a slice of strings into a map using the provided delimiter. +func ToMap(slice []string, delimiter string) map[string]string { + m := make(map[string]string, len(slice)) + + for _, s := range slice { + sl := strings.Split(s, delimiter) + + if len(sl) != 2 { + continue + } + + m[sl[0]] = sl[1] + } + + return m +}