From accd0e3961bb191d99045adaf94a834bc0812788 Mon Sep 17 00:00:00 2001 From: sebv Date: Thu, 26 Oct 2023 14:37:56 +0800 Subject: [PATCH] improving local config (hack, do not merge) --- internal/clientconfig/clientconfig.go | 58 ++++++++++++++++++++++++ internal/cmd/imagerunner/cmd.go | 2 +- internal/cmd/run/imagerunner.go | 5 +++ internal/credentials/credentials.go | 29 ++++++++++++ internal/http/imagerunner.go | 1 - internal/region/region.go | 64 ++++++++++++++++++++++++--- 6 files changed, 150 insertions(+), 9 deletions(-) create mode 100644 internal/clientconfig/clientconfig.go diff --git a/internal/clientconfig/clientconfig.go b/internal/clientconfig/clientconfig.go new file mode 100644 index 000000000..3f78eab80 --- /dev/null +++ b/internal/clientconfig/clientconfig.go @@ -0,0 +1,58 @@ +package clientconfig + +import ( + "os" + "path/filepath" + + "github.com/saucelabs/saucectl/internal/iam" + "gopkg.in/yaml.v2" +) + +// just breaking the dependendency cycle, must be better ways +type RegionConf struct { + Name string `yaml:"name"` + APIBaseURL string `yaml:"apiBaseURL,omitempty"` + AppBaseURL string `yaml:"appBaseURL,omitempty"` + WebDriverBaseURL string `yaml:"webdriverBaseURL,omitempty"` +} + +type ClientConfig struct { + Regions []RegionConf `yaml:"regions,omitempty"` + Credentials map[string]iam.Credentials `yaml:"credentials,omitempty"` +} + +func FromFile(path string) (*ClientConfig, error) { + // read the contents of the YAML file + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + // unmarshal the YAML data into a struct + var config ClientConfig + err = yaml.Unmarshal(data, &config) + if err != nil { + return nil, err + } + return &config, nil +} + +var config *ClientConfig + +func Get() (*ClientConfig, error) { + if config != nil { + return config, nil + } + homeDir, _ := os.UserHomeDir() + configPath := filepath.Join(homeDir, ".sauce", "saucectl.yaml") + // if file config file does not exist, return nil + if _, err := os.Stat(configPath); os.IsNotExist(err) { + return nil, nil + } + _config, err := FromFile(configPath) + if err != nil { + return nil, err + } + config = _config + return config, nil +} diff --git a/internal/cmd/imagerunner/cmd.go b/internal/cmd/imagerunner/cmd.go index 5d1d34310..94065e9f7 100644 --- a/internal/cmd/imagerunner/cmd.go +++ b/internal/cmd/imagerunner/cmd.go @@ -35,7 +35,7 @@ func Command(preRun func(cmd *cobra.Command, args []string)) *cobra.Command { segment.DefaultTracker.Enabled = false } - creds := credentials.Get() + creds := credentials.GetV2(region.FromString(regio)) url := region.FromString(regio).APIBaseURL() imagerunnerClient = http.NewImageRunner(url, creds, 15*time.Minute) diff --git a/internal/cmd/run/imagerunner.go b/internal/cmd/run/imagerunner.go index abc8d29bd..0e6fa8ce3 100644 --- a/internal/cmd/run/imagerunner.go +++ b/internal/cmd/run/imagerunner.go @@ -5,6 +5,7 @@ import ( cmds "github.com/saucelabs/saucectl/internal/cmd" "github.com/saucelabs/saucectl/internal/config" + "github.com/saucelabs/saucectl/internal/credentials" "github.com/saucelabs/saucectl/internal/imagerunner" "github.com/saucelabs/saucectl/internal/region" "github.com/saucelabs/saucectl/internal/report" @@ -35,8 +36,12 @@ func runImageRunner(cmd *cobra.Command) (int, error) { } regio := region.FromString(p.Sauce.Region) + creds := credentials.GetV2(regio) + imageRunnerClient.Creds = creds imageRunnerClient.URL = regio.APIBaseURL() restoClient.URL = regio.APIBaseURL() + restoClient.Username = creds.Username + restoClient.AccessKey = creds.AccessKey tracker := segment.DefaultTracker if regio == region.Staging { diff --git a/internal/credentials/credentials.go b/internal/credentials/credentials.go index d0f22edd6..f5c2aa6c2 100644 --- a/internal/credentials/credentials.go +++ b/internal/credentials/credentials.go @@ -6,7 +6,9 @@ import ( "path/filepath" "github.com/rs/zerolog/log" + "github.com/saucelabs/saucectl/internal/clientconfig" "github.com/saucelabs/saucectl/internal/iam" + "github.com/saucelabs/saucectl/internal/region" "github.com/saucelabs/saucectl/internal/yaml" yamlbase "gopkg.in/yaml.v2" ) @@ -36,7 +38,34 @@ func Get() iam.Credentials { if c := FromEnv(); c.IsSet() { return c } + /*clientConf, err := clientconfig.Get() + if err != nil { + log.Warn().Err(err).Msg("failed to read client config") + } + */ + return FromFile() +} +// Get returns the configured credentials. +// Effectively a convenience wrapper around FromEnv, followed by a call to FromFile. +// +// The lookup order is: +// 1. Environment variables (see FromEnv) +// 2. Per region Credentials +// 3. Credentials file (see FromFile) +func GetV2(region region.Region) iam.Credentials { + if c := FromEnv(); c.IsSet() { + return c + } + clientConf, err := clientconfig.Get() + if err != nil { + log.Warn().Err(err).Msg("failed to read client config") + } + if clientConf != nil { + if creds, ok := clientConf.Credentials[region.String()]; ok { + return creds + } + } return FromFile() } diff --git a/internal/http/imagerunner.go b/internal/http/imagerunner.go index 9005c3152..7375ca0ee 100644 --- a/internal/http/imagerunner.go +++ b/internal/http/imagerunner.go @@ -43,7 +43,6 @@ func (c *ImageRunner) TriggerRun(ctx context.Context, spec imagerunner.RunnerSpe if err != nil { return runner, err } - req.SetBasicAuth(c.Creds.Username, c.Creds.AccessKey) req.Header.Set("Content-Type", "application/json") diff --git a/internal/region/region.go b/internal/region/region.go index b49bf1168..085faa404 100644 --- a/internal/region/region.go +++ b/internal/region/region.go @@ -1,5 +1,10 @@ package region +import ( + "github.com/rs/zerolog/log" + "github.com/saucelabs/saucectl/internal/clientconfig" +) + // Region represents the sauce labs region. type Region uint @@ -16,12 +21,14 @@ const ( Staging ) -var meta = []struct { - Name string - APIBaseURL string - AppBaseURL string - WebDriverBaseURL string -}{ +type RegionData struct { + Name string `json:"name"` + APIBaseURL string `json:"apiBaseURL,omitempty"` + AppBaseURL string `json:"appBaseURL,omitempty"` + WebDriverBaseURL string `json:"webdriverBaseURL,omitempty"` +} + +var meta = []RegionData{ // None { "", @@ -59,19 +66,62 @@ var meta = []struct { }, } +var initialized = false + +func Init() { + if initialized { + return + } + clientConf, err := clientconfig.Get() + if err != nil { + log.Warn().Err(err).Msg("failed to read client config") + return + } + if clientConf != nil { + for _, r := range clientConf.Regions { + found := false + for i, m := range meta { + if m.Name == r.Name { + found = true + if r.APIBaseURL != "" { + meta[i].APIBaseURL = r.APIBaseURL + } + if r.AppBaseURL != "" { + meta[i].AppBaseURL = r.AppBaseURL + } + if r.WebDriverBaseURL != "" { + meta[i].WebDriverBaseURL = r.WebDriverBaseURL + } + break + } + } + if !found { + meta = append(meta, RegionData{ + Name: r.Name, + APIBaseURL: r.APIBaseURL, + AppBaseURL: r.AppBaseURL, + WebDriverBaseURL: r.WebDriverBaseURL, + }) + } + } + } + initialized = true +} + func (r Region) String() string { + Init() return meta[r].Name } // FromString converts the given string to the corresponding Region. // Returns None if the string did not match any Region. func FromString(s string) Region { + Init() for i, m := range meta { if m.Name == s { return Region(i) } } - return None }