@@ -6,6 +6,8 @@ package shared
66
77import (
88 "context"
9+ "crypto/tls"
10+ "crypto/x509"
911 "fmt"
1012 "net/http"
1113 "net/url"
@@ -17,13 +19,15 @@ import (
1719var DefaultIonosBasePath = ""
1820
1921const (
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
99103type ServerConfigurations []ServerConfiguration
100104
101- // shared. Configuration stores the configuration of the API client
105+ // Configuration stores the configuration of the API client
102106type 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+
145202func 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
159216func (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