Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 116 additions & 2 deletions shared/fileconfiguration/fileconfiguration.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ func NewFromEnv() (*FileConfig, error) {

// GetProfileNames returns a list of profile names from the loaded configuration
func (f *FileConfig) GetProfileNames() []string {
if f == nil {
return nil
}

names := make([]string, len(f.Profiles))
for i, p := range f.Profiles {
names[i] = p.Name
Expand All @@ -217,6 +221,10 @@ func (f *FileConfig) GetProfileNames() []string {

// GetEnvironmentNames returns a list of environment names from the loaded configuration
func (f *FileConfig) GetEnvironmentNames() []string {
if f == nil {
return nil
}

names := make([]string, len(f.Environments))
for i, e := range f.Environments {
names[i] = e.Name
Expand All @@ -226,9 +234,15 @@ func (f *FileConfig) GetEnvironmentNames() []string {

// GetOverride returns the endpoint for a specific product and location
// with fallback to the global endpoint if no location is found.
// This function will pick the first endpoint defined for the product to attempt fallback. Even if there are global
// endpoints configured, if the first is location-based, fallback will fail.
//
// It is a helper function combining GetProductLocationOverrides and GetProductOverrides
func (f *FileConfig) GetOverride(productName, location string) *Endpoint {
if f == nil {
return nil
}

if locEp := f.GetProductLocationOverrides(productName, location); locEp != nil {
return locEp
}
Expand All @@ -238,8 +252,10 @@ func (f *FileConfig) GetOverride(productName, location string) *Endpoint {
// Check if we actually got a location-specific endpoint (e.g. the user asked for a wrong location
// and GetProductOverrides returned the first location-specific endpoint)
if shared.SdkLogLevel.Satisfies(shared.Debug) {
shared.SdkLogger.Printf("[DEBUG] Retrieved location-specific (%s) override '%s' for product '%s' "+
"when a location-less override was expected, discarding...", location, prod.Endpoints[0].Name, productName)
shared.SdkLogger.Printf(
"[DEBUG] Retrieved location-specific (%s) override '%s' for product '%s' "+
"when a location-less override was expected, discarding...", location, prod.Endpoints[0].Name, productName,
)
}
return nil
}
Expand All @@ -248,6 +264,35 @@ func (f *FileConfig) GetOverride(productName, location string) *Endpoint {
return nil
}

// GetLocationOverridesWithGlobalFallback returns the endpoint for a specific product and location
// with fallback to the first global endpoint defined if the location is not found.
// Unlike GetOverride, this function ensures that fallback is done only with global endpoints. It will maintain the order
// in which the endpoints are defined.
//
// GetLocationOverridesWithGlobalFallback should fail only if the location requested does not exist and there are no
// global endpoints in the product configuration to fallback on.
func (f *FileConfig) GetLocationOverridesWithGlobalFallback(productName, location string) *Endpoint {
if f == nil {
return nil
}

if locEp := f.GetProductLocationOverrides(productName, location); locEp != nil {
return locEp
}

globalEndpoint := f.GetProductGlobalOverrides(productName, 0)
if globalEndpoint == nil {
if shared.SdkLogLevel.Satisfies(shared.Debug) {
shared.SdkLogger.Printf(
"[DEBUG] no global endpoints found for product %s to fallback on for location %s", productName, location,
)
}
return nil
}

return globalEndpoint
}

// GetCurrentProfile returns the current profile from the loaded configuration
// if the current profile is not set, it returns nil
// if the current profile is set and found in the loaded configuration, it returns the profile
Expand Down Expand Up @@ -317,6 +362,52 @@ func (f *FileConfig) GetProductOverrides(productName string) *Product {
return nil
}

// FilterOverrides returns all endpoints for which the predicate function returns true
func (f *FileConfig) FilterOverrides(productName string, predicate func(Endpoint) bool) []Endpoint {
if f == nil {
return nil
}

product := f.GetProductOverrides(productName)
if product == nil {
return nil
}

var endpoints []Endpoint
for _, endpoint := range product.Endpoints {
if predicate(endpoint) {
endpoints = append(endpoints, endpoint)
}
}
return endpoints
}

// FilterGlobalOverrides returns all global endpoints defined for a given product
func (f *FileConfig) FilterGlobalOverrides(productName string) []Endpoint {
if f == nil {
return nil
}

return f.FilterOverrides(
productName, func(endpoint Endpoint) bool {
return endpoint.Location == ""
},
)
}

// FilterLocationOverrides returns all location-based endpoints defined for a given product
func (f *FileConfig) FilterLocationOverrides(productName string) []Endpoint {
if f == nil {
return nil
}

return f.FilterOverrides(
productName, func(endpoint Endpoint) bool {
return endpoint.Location != ""
},
)
}

// GetProductLocationOverrides returns the overrides for a specific product and location for the current environment
func (f *FileConfig) GetProductLocationOverrides(productName, location string) *Endpoint {
if f == nil {
Expand All @@ -336,3 +427,26 @@ func (f *FileConfig) GetProductLocationOverrides(productName, location string) *
}
return nil
}

// GetProductGlobalOverrides returns the n-th overrides for a specific product for the current environment.
func (f *FileConfig) GetProductGlobalOverrides(productName string, index int) *Endpoint {
if f == nil {
return nil
}

endpoints := f.FilterGlobalOverrides(productName)
if endpoints == nil {
if shared.SdkLogLevel.Satisfies(shared.Debug) {
shared.SdkLogger.Printf("[DEBUG] no global endpoint overrides found for product %s", productName)
}
return nil
}

if index >= len(endpoints) {
if shared.SdkLogLevel.Satisfies(shared.Debug) {
shared.SdkLogger.Printf("[DEBUG] index %d out of range for global endpoints of product %s, only %d global endpoints found", index, productName, len(endpoints))
}
return nil
}
return &endpoints[index]
}
74 changes: 69 additions & 5 deletions shared/fileconfiguration/fileconfiguration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ environments:
assert.NoError(t, err)

// Validate the loaded config
assert.Equal(t, 1.0, config.Version)
assert.Equal(t, Version(1.0), config.Version)
assert.Equal(t, "testProfile", config.CurrentProfile)
assert.Equal(t, "testUser", config.Profiles[0].Credentials.Username)
assert.Equal(t, "testPass", config.Profiles[0].Credentials.Password)
Expand Down Expand Up @@ -136,10 +136,20 @@ func makeTestConfig() *FileConfig {
{
Name: "dns",
Endpoints: []Endpoint{
{Location: "", Name: "https://global.dns", SkipTLSVerify: false},
{Location: "de/fra", Name: "https://dns.de-fra", SkipTLSVerify: false},
{Location: "de/txl", Name: "https://dns.de-txl", SkipTLSVerify: false},
},
},
{
Name: Cloud,
Endpoints: []Endpoint{
{Location: "de/fra", Name: "https://cloud.de-fra", SkipTLSVerify: false},
{Location: "de/txl", Name: "https://cloud.de-txl", SkipTLSVerify: false},
{Location: "", Name: "https://cloud.global-1", SkipTLSVerify: false},
{Location: "", Name: "https://cloud.global-2", SkipTLSVerify: true},
},
},
},
},
{
Expand Down Expand Up @@ -196,14 +206,68 @@ func TestGetOverride_GlobalWhenLocationEmpty(t *testing.T) {
cfg := makeTestConfig()
ep := cfg.GetOverride("dns", "")
assert.NotNil(t, ep)
// first endpoint in Product.Endpoints is the location-specific one, so fallback to GetProductOverrides:
assert.Equal(t, "https://dns.de-fra", ep.Name)
assert.Equal(t, "https://global.dns", ep.Name)
}

func TestGetOverride_NotFound(t *testing.T) {
cfg := makeTestConfig()
// unknown product
assert.Nil(t, cfg.GetOverride("unknown", ""))
// known product but wrong location
assert.Nil(t, cfg.GetOverride("dns", "wrong/location"))
// known product but wrong location, fallback to global endpoint (first in endpoint list), so should not be nil
assert.NotNil(t, cfg.GetOverride("dns", "wrong/location"))
}

func TestFilterOverrides(t *testing.T) {
cfg := makeTestConfig()
ep := cfg.FilterOverrides(
Cloud, func(endpoint Endpoint) bool {
return endpoint.SkipTLSVerify == true
},
)
assert.NotNil(t, ep)
assert.Equal(t, 1, len(ep))
assert.Equal(t, "https://cloud.global-2", ep[0].Name)
assert.Equal(t, true, ep[0].SkipTLSVerify)
}

func TestFilterGlobalOverrides(t *testing.T) {
cfg := makeTestConfig()
ep := cfg.FilterGlobalOverrides(Cloud)
assert.NotNil(t, ep)
assert.Equal(t, 2, len(ep))
assert.Equal(t, "", ep[0].Location)
assert.Equal(t, "", ep[1].Location)
}

func TestFilterLocationOverrides(t *testing.T) {
cfg := makeTestConfig()
ep := cfg.FilterLocationOverrides(Cloud)
assert.NotNil(t, ep)
assert.Equal(t, 2, len(ep))
assert.Equal(t, "de/fra", ep[0].Location)
assert.Equal(t, "de/txl", ep[1].Location)
}

func TestGetLocationOverridesWithGlobalFallback_LocationMatch(t *testing.T) {
cfg := makeTestConfig()
ep := cfg.GetLocationOverridesWithGlobalFallback(Cloud, "de/fra")
assert.NotNil(t, ep)
assert.Equal(t, "https://cloud.de-fra", ep.Name)
assert.Equal(t, "de/fra", ep.Location)
}

func TestGetLocationOverridesWithGlobalFallback_GlobalMatch(t *testing.T) {
cfg := makeTestConfig()
ep := cfg.GetLocationOverridesWithGlobalFallback(Cloud, "")
assert.NotNil(t, ep)
assert.Equal(t, "https://cloud.global-1", ep.Name)
assert.Equal(t, "", ep.Location)
}

func TestGetLocationOverridesWithGlobalFallback_LocationNotFound_GlobalFallback(t *testing.T) {
cfg := makeTestConfig()
ep := cfg.GetLocationOverridesWithGlobalFallback(Cloud, "us/las")
assert.NotNil(t, ep)
assert.Equal(t, "https://cloud.global-1", ep.Name)
assert.Equal(t, "", ep.Location)
}
Loading