Skip to content

Commit 0cc0519

Browse files
feat: add option to config data from file (#27)
1 parent caa9339 commit 0cc0519

File tree

5 files changed

+528
-15
lines changed

5 files changed

+528
-15
lines changed

shared/configuration.go

Lines changed: 137 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ package shared
66

77
import (
88
"context"
9+
"crypto/tls"
10+
"crypto/x509"
911
"fmt"
1012
"net/http"
1113
"net/url"
@@ -17,13 +19,15 @@ import (
1719
var DefaultIonosBasePath = ""
1820

1921
const (
20-
IonosUsernameEnvVar = "IONOS_USERNAME"
21-
IonosPasswordEnvVar = "IONOS_PASSWORD"
22-
IonosTokenEnvVar = "IONOS_TOKEN"
23-
IonosApiUrlEnvVar = "IONOS_API_URL"
24-
IonosPinnedCertEnvVar = "IONOS_PINNED_CERT"
25-
IonosLogLevelEnvVar = "IONOS_LOG_LEVEL"
26-
DefaultIonosServerUrl = "https://api.ionos.com/"
22+
IonosUsernameEnvVar = "IONOS_USERNAME"
23+
IonosPasswordEnvVar = "IONOS_PASSWORD"
24+
IonosTokenEnvVar = "IONOS_TOKEN"
25+
IonosApiUrlEnvVar = "IONOS_API_URL"
26+
IonosPinnedCertEnvVar = "IONOS_PINNED_CERT"
27+
IonosLogLevelEnvVar = "IONOS_LOG_LEVEL"
28+
IonosFilePathEnvVar = "IONOS_CONFIG_FILE"
29+
IonosCurrentProfileEnvVar = "IONOS_CURRENT_PROFILE"
30+
DefaultIonosServerUrl = "https://api.ionos.com/"
2731

2832
defaultMaxRetries = 3
2933
defaultWaitTime = time.Duration(100) * time.Millisecond
@@ -98,7 +102,7 @@ type ServerConfiguration struct {
98102
// ServerConfigurations stores multiple ServerConfiguration items
99103
type ServerConfigurations []ServerConfiguration
100104

101-
// shared.Configuration stores the configuration of the API client
105+
// Configuration stores the configuration of the API client
102106
type Configuration struct {
103107
Host string `json:"host,omitempty"`
104108
Scheme string `json:"scheme,omitempty"`
@@ -142,6 +146,59 @@ func NewConfiguration(username, password, token, hostUrl string) *Configuration
142146
return cfg
143147
}
144148

149+
// ClientOptions is a struct that represents the client options
150+
type ClientOptions struct {
151+
// Endpoint is the endpoint that will be overridden
152+
Endpoint string
153+
// SkipTLSVerify skips tls verification. Not recommended for production!
154+
SkipTLSVerify bool
155+
// Certificate is the certificate that will be used for tls verification
156+
Certificate string
157+
// Credentials are the credentials that will be used for authentication
158+
Credentials Credentials
159+
}
160+
161+
// Credentials are the credentials that will be used for authentication
162+
type Credentials struct {
163+
Username string `yaml:"username"`
164+
Password string `yaml:"password"`
165+
Token string `yaml:"token"`
166+
}
167+
168+
// NewConfigurationFromOptions returns a new shared.Configuration object created from the client options
169+
func NewConfigurationFromOptions(clientOptions ClientOptions) *Configuration {
170+
cfg := &Configuration{
171+
DefaultHeader: make(map[string]string),
172+
DefaultQueryParams: url.Values{},
173+
UserAgent: "shared-sdk-go",
174+
Username: clientOptions.Credentials.Username,
175+
Password: clientOptions.Credentials.Password,
176+
Token: clientOptions.Credentials.Token,
177+
MaxRetries: defaultMaxRetries,
178+
MaxWaitTime: defaultMaxWaitTime,
179+
WaitTime: defaultWaitTime,
180+
Servers: ServerConfigurations{},
181+
OperationServers: map[string]ServerConfigurations{},
182+
}
183+
if clientOptions.Endpoint != "" {
184+
cfg.Servers = ServerConfigurations{
185+
{
186+
URL: getServerUrl(clientOptions.Endpoint),
187+
Description: "Production",
188+
},
189+
}
190+
}
191+
if clientOptions.SkipTLSVerify {
192+
if transport, ok := cfg.HTTPClient.Transport.(*http.Transport); ok {
193+
transport.TLSClientConfig.InsecureSkipVerify = clientOptions.SkipTLSVerify
194+
}
195+
}
196+
if clientOptions.Certificate != "" {
197+
AddCertsToClient(cfg.HTTPClient, clientOptions.Certificate)
198+
}
199+
return cfg
200+
}
201+
145202
func NewConfigurationFromEnv() *Configuration {
146203
return NewConfiguration(os.Getenv(IonosUsernameEnvVar), os.Getenv(IonosPasswordEnvVar), os.Getenv(IonosTokenEnvVar), os.Getenv(IonosApiUrlEnvVar))
147204
}
@@ -158,10 +215,10 @@ func (c *Configuration) AddDefaultQueryParam(key string, value string) {
158215
// URL formats template on a index using given variables
159216
func (sc ServerConfigurations) URL(index int, variables map[string]string) (string, error) {
160217
if index < 0 || len(sc) <= index {
161-
return "", fmt.Errorf("Index %v out of range %v", index, len(sc)-1)
218+
return "", fmt.Errorf("index %v out of range %v", index, len(sc)-1)
162219
}
163220
server := sc[index]
164-
url := server.URL
221+
serverUrl := server.URL
165222

166223
// go through variables and replace placeholders
167224
for name, variable := range server.Variables {
@@ -173,14 +230,14 @@ func (sc ServerConfigurations) URL(index int, variables map[string]string) (stri
173230
}
174231
}
175232
if !found {
176-
return "", fmt.Errorf("The variable %s in the server URL has invalid value %v. Must be %v", name, value, variable.EnumValues)
233+
return "", fmt.Errorf("the variable %s in the server URL has invalid value %v. Must be %v", name, value, variable.EnumValues)
177234
}
178-
url = strings.Replace(url, "{"+name+"}", value, -1)
235+
serverUrl = strings.Replace(serverUrl, "{"+name+"}", value, -1)
179236
} else {
180-
url = strings.Replace(url, "{"+name+"}", variable.DefaultValue, -1)
237+
serverUrl = strings.Replace(serverUrl, "{"+name+"}", variable.DefaultValue, -1)
181238
}
182239
}
183-
return url, nil
240+
return serverUrl, nil
184241
}
185242

186243
// ServerURL returns URL based on server settings
@@ -194,7 +251,7 @@ func getServerIndex(ctx context.Context) (int, error) {
194251
if index, ok := si.(int); ok {
195252
return index, nil
196253
}
197-
return 0, reportError("Invalid type %T should be int", si)
254+
return 0, reportError("invalid type %T should be int", si)
198255
}
199256
return 0, nil
200257
}
@@ -284,3 +341,68 @@ func (c *Configuration) ServerURLWithContext(ctx context.Context, endpoint strin
284341

285342
return sc.URL(index, variables)
286343
}
344+
345+
// ConfigProvider is an interface that allows to get the configuration of shared clients
346+
type ConfigProvider interface {
347+
GetConfig() *Configuration
348+
}
349+
350+
// EndpointOverridden is a constant that is used to mark the endpoint as overridden and can be used to search for the location
351+
// in the server configuration.
352+
const EndpointOverridden = "endpoint from config file"
353+
354+
// OverrideLocationFor aims to override the server URL for a given client configuration, based on location and endpoint inputs.
355+
// Mutates the client configuration. It searches for the location in the server configuration and overrides the endpoint.
356+
// If the endpoint is empty, it early exits without making changes.
357+
func OverrideLocationFor(configProvider ConfigProvider, location, endpoint string, replaceServers bool) {
358+
if endpoint == "" {
359+
return
360+
}
361+
// If the replaceServers flag is set, we replace the servers with the new endpoint
362+
if replaceServers {
363+
SdkLogger.Printf("[DEBUG] Replacing all server configurations for location %s", location)
364+
configProvider.GetConfig().Servers = []ServerConfiguration{
365+
{
366+
URL: endpoint,
367+
Description: EndpointOverridden + location,
368+
},
369+
}
370+
return
371+
}
372+
location = strings.TrimSpace(location)
373+
endpoint = strings.TrimSpace(endpoint)
374+
servers := configProvider.GetConfig().Servers
375+
for idx := range servers {
376+
if strings.Contains(servers[idx].URL, location) {
377+
SdkLogger.Printf("[DEBUG] Overriding server configuration for location %s", location)
378+
servers[idx].URL = endpoint
379+
servers[idx].Description = EndpointOverridden + location
380+
return
381+
}
382+
}
383+
SdkLogger.Printf("[DEBUG] Adding new server configuration for location %s", location)
384+
configProvider.GetConfig().Servers = append(configProvider.GetConfig().Servers, ServerConfiguration{
385+
URL: endpoint,
386+
Description: EndpointOverridden + location,
387+
})
388+
}
389+
390+
func SetSkipTLSVerify(configProvider ConfigProvider, skipTLSVerify bool) {
391+
configProvider.GetConfig().HTTPClient.Transport = &http.Transport{
392+
TLSClientConfig: &tls.Config{InsecureSkipVerify: skipTLSVerify},
393+
}
394+
}
395+
396+
// AddCertsToClient adds certificates to the http client
397+
func AddCertsToClient(httpClient *http.Client, authorityData string) {
398+
rootCAs, _ := x509.SystemCertPool()
399+
if rootCAs == nil {
400+
rootCAs = x509.NewCertPool()
401+
}
402+
if ok := rootCAs.AppendCertsFromPEM([]byte(authorityData)); !ok && SdkLogLevel.Satisfies(Debug) {
403+
SdkLogger.Printf("No certs appended, using system certs only")
404+
}
405+
if transport, ok := httpClient.Transport.(*http.Transport); ok {
406+
transport.TLSClientConfig.RootCAs = rootCAs
407+
}
408+
}

0 commit comments

Comments
 (0)