Skip to content

Commit

Permalink
feat: Support regional credentials (#858)
Browse files Browse the repository at this point in the history
* Add credentials to region config

* Use regionally defined credentials

* import ordering

* Update internal/region/region.go

Co-authored-by: Alex Plischke <alex.plischke@saucelabs.com>

* whitespace

---------

Co-authored-by: Alex Plischke <alex.plischke@saucelabs.com>
  • Loading branch information
mhan83 and alexplischke authored Nov 28, 2023
1 parent 0a19867 commit e84ec40
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 35 deletions.
3 changes: 1 addition & 2 deletions internal/cmd/run/apitest.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"os"

cmds "github.com/saucelabs/saucectl/internal/cmd"
"github.com/saucelabs/saucectl/internal/credentials"
"github.com/saucelabs/saucectl/internal/http"
"github.com/saucelabs/saucectl/internal/usage"

Expand Down Expand Up @@ -38,7 +37,7 @@ func runApitest(cmd *cobra.Command, isCLIDriven bool) (int, error) {
}

regio := region.FromString(p.Sauce.Region)
creds := credentials.Get()
creds := regio.Credentials()

apitestingClient := http.NewAPITester(regio.APIBaseURL(), creds.Username, creds.AccessKey, apitestingTimeout)
restoClient := http.NewResto(regio.APIBaseURL(), creds.Username, creds.AccessKey, 0)
Expand Down
3 changes: 1 addition & 2 deletions internal/cmd/run/cucumber.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"github.com/saucelabs/saucectl/internal/ci"
cmds "github.com/saucelabs/saucectl/internal/cmd"
"github.com/saucelabs/saucectl/internal/config"
"github.com/saucelabs/saucectl/internal/credentials"
"github.com/saucelabs/saucectl/internal/cucumber"
"github.com/saucelabs/saucectl/internal/flags"
"github.com/saucelabs/saucectl/internal/framework"
Expand Down Expand Up @@ -115,7 +114,7 @@ func runCucumber(cmd *cobra.Command, isCLIDriven bool) (int, error) {

cleanupArtifacts(p.Artifacts)

creds := credentials.Get()
creds := regio.Credentials()

restoClient := http.NewResto(regio.APIBaseURL(), creds.Username, creds.AccessKey, 0)
restoClient.ArtifactConfig = p.Artifacts.Download
Expand Down
3 changes: 1 addition & 2 deletions internal/cmd/run/cypress.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"os"

cmds "github.com/saucelabs/saucectl/internal/cmd"
"github.com/saucelabs/saucectl/internal/credentials"
"github.com/saucelabs/saucectl/internal/http"

"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -137,7 +136,7 @@ func runCypress(cmd *cobra.Command, isCLIDriven bool) (int, error) {

cleanupArtifacts(p.GetArtifactsCfg())

creds := credentials.Get()
creds := regio.Credentials()

restoClient := http.NewResto(regio.APIBaseURL(), creds.Username, creds.AccessKey, 0)
restoClient.ArtifactConfig = p.GetArtifactsCfg().Download
Expand Down
3 changes: 1 addition & 2 deletions internal/cmd/run/espresso.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"os"

cmds "github.com/saucelabs/saucectl/internal/cmd"
"github.com/saucelabs/saucectl/internal/credentials"
"github.com/saucelabs/saucectl/internal/http"

"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -137,7 +136,7 @@ func runEspresso(cmd *cobra.Command, espressoFlags espressoFlags, isCLIDriven bo
func runEspressoInCloud(p espresso.Project, regio region.Region) (int, error) {
log.Info().Msg("Running Espresso in Sauce Labs")

creds := credentials.Get()
creds := regio.Credentials()
restoClient := http.NewResto(regio.APIBaseURL(), creds.Username, creds.AccessKey, 0)
restoClient.ArtifactConfig = p.Artifacts.Download
testcompClient := http.NewTestComposer(regio.APIBaseURL(), creds, testComposerTimeout)
Expand Down
3 changes: 1 addition & 2 deletions internal/cmd/run/imagerunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ 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/http"
"github.com/saucelabs/saucectl/internal/imagerunner"
"github.com/saucelabs/saucectl/internal/region"
Expand Down Expand Up @@ -66,7 +65,7 @@ func runImageRunner(cmd *cobra.Command) (int, error) {

cleanupArtifacts(p.Artifacts)

creds := credentials.Get()
creds := regio.Credentials()
imageRunnerClient := http.NewImageRunner(regio.APIBaseURL(), creds, imgExecTimeout)
restoClient := http.NewResto(regio.APIBaseURL(), creds.Username, creds.AccessKey, 0)

Expand Down
3 changes: 1 addition & 2 deletions internal/cmd/run/playwright.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"os"

cmds "github.com/saucelabs/saucectl/internal/cmd"
"github.com/saucelabs/saucectl/internal/credentials"
"github.com/saucelabs/saucectl/internal/http"

"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -150,7 +149,7 @@ func runPlaywright(cmd *cobra.Command, isCLIDriven bool) (int, error) {

cleanupArtifacts(p.Artifacts)

creds := credentials.Get()
creds := regio.Credentials()

restoClient := http.NewResto(regio.APIBaseURL(), creds.Username, creds.AccessKey, 0)
restoClient.ArtifactConfig = p.Artifacts.Download
Expand Down
3 changes: 1 addition & 2 deletions internal/cmd/run/replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"os"

cmds "github.com/saucelabs/saucectl/internal/cmd"
"github.com/saucelabs/saucectl/internal/credentials"
"github.com/saucelabs/saucectl/internal/http"

"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -125,7 +124,7 @@ func runReplay(cmd *cobra.Command, isCLIDriven bool) (int, error) {
func runPuppeteerReplayInSauce(p replay.Project, regio region.Region) (int, error) {
log.Info().Msg("Replaying chrome devtools recordings")

creds := credentials.Get()
creds := regio.Credentials()
restoClient := http.NewResto(regio.APIBaseURL(), creds.Username, creds.AccessKey, 0)
restoClient.ArtifactConfig = p.Artifacts.Download
testcompClient := http.NewTestComposer(regio.APIBaseURL(), creds, testComposerTimeout)
Expand Down
3 changes: 1 addition & 2 deletions internal/cmd/run/testcafe.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"os"

cmds "github.com/saucelabs/saucectl/internal/cmd"
"github.com/saucelabs/saucectl/internal/credentials"
"github.com/saucelabs/saucectl/internal/http"

"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -172,7 +171,7 @@ func runTestcafe(cmd *cobra.Command, tcFlags testcafeFlags, isCLIDriven bool) (i

cleanupArtifacts(p.Artifacts)

creds := credentials.Get()
creds := regio.Credentials()

restoClient := http.NewResto(regio.APIBaseURL(), creds.Username, creds.AccessKey, 0)
restoClient.ArtifactConfig = p.Artifacts.Download
Expand Down
3 changes: 1 addition & 2 deletions internal/cmd/run/xcuitest.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"os"

cmds "github.com/saucelabs/saucectl/internal/cmd"
"github.com/saucelabs/saucectl/internal/credentials"
"github.com/saucelabs/saucectl/internal/http"

"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -137,7 +136,7 @@ func runXcuitest(cmd *cobra.Command, xcuiFlags xcuitestFlags, isCLIDriven bool)
func runXcuitestInCloud(p xcuitest.Project, regio region.Region) (int, error) {
log.Info().Msg("Running XCUITest in Sauce Labs")

creds := credentials.Get()
creds := regio.Credentials()

restoClient := http.NewResto(regio.APIBaseURL(), creds.Username, creds.AccessKey, 0)
restoClient.ArtifactConfig = p.Artifacts.Download
Expand Down
87 changes: 70 additions & 17 deletions internal/region/region.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"path/filepath"

"github.com/rs/zerolog/log"
"github.com/saucelabs/saucectl/internal/credentials"
"github.com/saucelabs/saucectl/internal/iam"
"gopkg.in/yaml.v2"
)

Expand All @@ -27,10 +29,11 @@ func init() {
type Region string

type regionMeta struct {
Name string `yaml:"name"`
APIBaseURL string `yaml:"apiBaseURL"`
AppBaseURL string `yaml:"appBaseURL"`
WebdriverBaseURL string `yaml:"webdriverBaseURL"`
Name string `yaml:"name"`
APIBaseURL string `yaml:"apiBaseURL"`
AppBaseURL string `yaml:"appBaseURL"`
WebdriverBaseURL string `yaml:"webdriverBaseURL"`
Credentials iam.Credentials `yaml:"credentials"`
}

// None is an undefined sauce labs region.
Expand All @@ -48,47 +51,91 @@ const EUCentral1 Region = "eu-central-1"
// Staging is a sauce labs internal pre-production environment.
const Staging Region = "staging"

var defaultCreds = credentials.Get()

var sauceRegionMetas = []regionMeta{
{
None.String(),
"",
"",
"",
iam.Credentials{},
},
{
USWest1.String(),
"https://api.us-west-1.saucelabs.com",
"https://app.saucelabs.com",
"https://ondemand.us-west-1.saucelabs.com",
defaultCreds,
},
{
USEast4.String(),
"https://api.us-east-4.saucelabs.com",
"https://app.us-east-4.saucelabs.com",
"https://ondemand.us-east-4.saucelabs.com",
defaultCreds,
},
{
EUCentral1.String(),
"https://api.eu-central-1.saucelabs.com",
"https://app.eu-central-1.saucelabs.com",
"https://ondemand.eu-central-1.saucelabs.com",
defaultCreds,
},
{
Staging.String(),
"https://api.staging.saucelabs.net",
"https://app.staging.saucelabs.net",
"https://ondemand.staging.saucelabs.net",
defaultCreds,
},
}

// userRegionMetas is a list of user defined regions that is loaded
// from the user's ~/.sauce directory.
var userRegionMetas []regionMeta

func mergeRegionMetas(base regionMeta, overlay regionMeta) regionMeta {
merged := base
if overlay.Name != "" {
merged.Name = overlay.Name
}
if overlay.APIBaseURL != "" {
merged.APIBaseURL = overlay.APIBaseURL
}
if overlay.AppBaseURL != "" {
merged.AppBaseURL = overlay.AppBaseURL
}
if overlay.WebdriverBaseURL != "" {
merged.WebdriverBaseURL = overlay.WebdriverBaseURL
}
if overlay.Credentials.IsSet() {
merged.Credentials = overlay.Credentials
}

return merged
}

// allRegionMetas concats the list of known Sauce region metadata and the user's
// list of region metadata.
func allRegionMetas() []regionMeta {
return append(sauceRegionMetas, userRegionMetas...)
func allRegionMetas(sauce []regionMeta, user []regionMeta) map[Region]regionMeta {
mappedRegions := make(map[Region]regionMeta)
for _, m := range sauce {
mappedRegions[Region(m.Name)] = m
}

for _, userMeta := range user {
userRegion := Region(userMeta.Name)

curr, ok := mappedRegions[userRegion]
if !ok {
mappedRegions[userRegion] = userMeta
continue
}
mappedRegions[userRegion] = mergeRegionMetas(curr, userMeta)
}

return mappedRegions
}

func (r Region) String() string {
Expand All @@ -98,23 +145,19 @@ func (r Region) String() string {
// FromString converts the given string to the corresponding Region.
// Returns None if the string did not match any Region.
func FromString(s string) Region {
for _, m := range allRegionMetas() {
if s == m.Name {
return Region(m.Name)
}
_, ok := allRegionMetas(sauceRegionMetas, userRegionMetas)[Region(s)]
if ok {
return Region(s)
}
return None
}

func lookupMeta(r Region) regionMeta {
var found regionMeta
for _, m := range allRegionMetas() {
if m.Name == string(r) {
found = m
break
}
m, ok := allRegionMetas(sauceRegionMetas, userRegionMetas)[r]
if ok {
return m
}
return found
return regionMeta{}
}

// APIBaseURL returns the API base URL for the region.
Expand All @@ -134,3 +177,13 @@ func (r Region) WebDriverBaseURL() string {
meta := lookupMeta(r)
return meta.WebdriverBaseURL
}

func (r Region) Credentials() iam.Credentials {
meta := lookupMeta(r)
// check if there are any region specific credentials first
if meta.Credentials.IsSet() {
return meta.Credentials
}

return defaultCreds
}
Loading

0 comments on commit e84ec40

Please sign in to comment.