@@ -6,7 +6,10 @@ package shared
66
77import (
88 "context"
9+ "crypto/tls"
10+ "crypto/x509"
911 "fmt"
12+ "net"
1013 "net/http"
1114 "net/url"
1215 "os"
@@ -17,13 +20,15 @@ import (
1720var DefaultIonosBasePath = ""
1821
1922const (
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/"
23+ IonosUsernameEnvVar = "IONOS_USERNAME"
24+ IonosPasswordEnvVar = "IONOS_PASSWORD"
25+ IonosTokenEnvVar = "IONOS_TOKEN"
26+ IonosApiUrlEnvVar = "IONOS_API_URL"
27+ IonosPinnedCertEnvVar = "IONOS_PINNED_CERT"
28+ IonosLogLevelEnvVar = "IONOS_LOG_LEVEL"
29+ IonosFilePathEnvVar = "IONOS_CONFIG_FILE"
30+ IonosCurrentProfileEnvVar = "IONOS_CURRENT_PROFILE"
31+ DefaultIonosServerUrl = "https://api.ionos.com/"
2732
2833 defaultMaxRetries = 3
2934 defaultWaitTime = time .Duration (100 ) * time .Millisecond
@@ -99,7 +104,7 @@ type ServerConfiguration struct {
99104// ServerConfigurations stores multiple ServerConfiguration items
100105type ServerConfigurations []ServerConfiguration
101106
102- // shared. Configuration stores the configuration of the API client
107+ // Configuration stores the configuration of the API client
103108type Configuration struct {
104109 Host string `json:"host,omitempty"`
105110 Scheme string `json:"scheme,omitempty"`
@@ -145,6 +150,75 @@ func NewConfiguration(username, password, token, hostUrl string) *Configuration
145150 return cfg
146151}
147152
153+ // ClientOptions is a struct that represents the client options
154+ type ClientOptions struct {
155+ // Endpoint is the endpoint that will be overridden
156+ Endpoint string
157+ // SkipTLSVerify skips tls verification. Not recommended for production!
158+ SkipTLSVerify bool
159+ // Certificate is the certificate that will be used for tls verification
160+ Certificate string
161+ // Credentials are the credentials that will be used for authentication
162+ Credentials Credentials
163+ }
164+
165+ // Credentials are the credentials that will be used for authentication
166+ type Credentials struct {
167+ Username string `yaml:"username"`
168+ Password string `yaml:"password"`
169+ Token string `yaml:"token"`
170+ }
171+
172+ // NewConfigurationFromOptions returns a new shared.Configuration object created from the client options
173+ func NewConfigurationFromOptions (clientOptions ClientOptions ) * Configuration {
174+ cfg := & Configuration {
175+ DefaultHeader : make (map [string ]string ),
176+ DefaultQueryParams : url.Values {},
177+ UserAgent : "shared-sdk-go" ,
178+ Username : clientOptions .Credentials .Username ,
179+ Password : clientOptions .Credentials .Password ,
180+ Token : clientOptions .Credentials .Token ,
181+ MaxRetries : defaultMaxRetries ,
182+ MaxWaitTime : defaultMaxWaitTime ,
183+ WaitTime : defaultWaitTime ,
184+ Servers : ServerConfigurations {},
185+ OperationServers : map [string ]ServerConfigurations {},
186+ HTTPClient : http .DefaultClient ,
187+ }
188+ if clientOptions .Endpoint != "" {
189+ cfg .Servers = ServerConfigurations {
190+ {
191+ URL : getServerUrl (clientOptions .Endpoint ),
192+ Description : "Production" ,
193+ },
194+ }
195+ }
196+ cfg .HTTPClient .Transport = CreateTransport (clientOptions .SkipTLSVerify , clientOptions .Certificate )
197+ return cfg
198+ }
199+
200+ func CreateTransport (insecure bool , certificate string ) * http.Transport {
201+ dialer := & net.Dialer {
202+ Timeout : 30 * time .Second ,
203+ KeepAlive : 30 * time .Second ,
204+ }
205+ transport := & http.Transport {
206+ Proxy : http .ProxyFromEnvironment ,
207+ DialContext : dialer .DialContext ,
208+ DisableKeepAlives : true ,
209+ IdleConnTimeout : 30 * time .Second ,
210+ TLSHandshakeTimeout : 15 * time .Second ,
211+ ExpectContinueTimeout : 1 * time .Second ,
212+ MaxIdleConnsPerHost : 3 ,
213+ MaxConnsPerHost : 3 ,
214+ }
215+ transport .TLSClientConfig = & tls.Config {InsecureSkipVerify : insecure }
216+ if certificate != "" {
217+ transport .TLSClientConfig .RootCAs = AddCertsToClient (certificate )
218+ }
219+ return transport
220+ }
221+
148222func NewConfigurationFromEnv () * Configuration {
149223 return NewConfiguration (os .Getenv (IonosUsernameEnvVar ), os .Getenv (IonosPasswordEnvVar ), os .Getenv (IonosTokenEnvVar ), os .Getenv (IonosApiUrlEnvVar ))
150224}
@@ -161,7 +235,7 @@ func (c *Configuration) AddDefaultQueryParam(key string, value string) {
161235// URL formats template on a index using given variables
162236func (sc ServerConfigurations ) URL (index int , variables map [string ]string ) (string , error ) {
163237 if index < 0 || len (sc ) <= index {
164- return "" , fmt .Errorf ("Index %v out of range %v" , index , len (sc )- 1 )
238+ return "" , fmt .Errorf ("index %v out of range %v" , index , len (sc )- 1 )
165239 }
166240 server := sc [index ]
167241 url := server .URL
@@ -182,7 +256,7 @@ func (sc ServerConfigurations) URL(index int, variables map[string]string) (stri
182256 }
183257 }
184258 if ! found {
185- return "" , fmt .Errorf ("The variable %s in the server URL has invalid value %v. Must be %v" , name , value , variable .EnumValues )
259+ return "" , fmt .Errorf ("the variable %s in the server URL has invalid value %v. Must be %v" , name , value , variable .EnumValues )
186260 }
187261 url = strings .Replace (url , "{" + name + "}" , value , - 1 )
188262 } else {
@@ -203,7 +277,7 @@ func getServerIndex(ctx context.Context) (int, error) {
203277 if index , ok := si .(int ); ok {
204278 return index , nil
205279 }
206- return 0 , reportError ("Invalid type %T should be int" , si )
280+ return 0 , reportError ("invalid type %T should be int" , si )
207281 }
208282 return 0 , nil
209283}
@@ -291,3 +365,66 @@ func (c *Configuration) ServerURLWithContext(ctx context.Context, endpoint strin
291365
292366 return sc .URL (index , variables )
293367}
368+
369+ // ConfigProvider is an interface that allows to get the configuration of shared clients
370+ type ConfigProvider interface {
371+ GetConfig () * Configuration
372+ }
373+
374+ // EndpointOverridden is a constant that is used to mark the endpoint as overridden and can be used to search for the location
375+ // in the server configuration.
376+ const EndpointOverridden = "endpoint from config file"
377+
378+ // OverrideLocationFor aims to override the server URL for a given client configuration, based on location and endpoint inputs.
379+ // Mutates the client configuration. It searches for the location in the server configuration and overrides the endpoint.
380+ // If the endpoint is empty, it early exits without making changes.
381+ func OverrideLocationFor (configProvider ConfigProvider , location , endpoint string , replaceServers bool ) {
382+ if endpoint == "" {
383+ return
384+ }
385+ // If the replaceServers flag is set, we replace the servers with the new endpoint
386+ if replaceServers {
387+ SdkLogger .Printf ("[DEBUG] Replacing all server configurations for location %s" , location )
388+ configProvider .GetConfig ().Servers = []ServerConfiguration {
389+ {
390+ URL : endpoint ,
391+ Description : EndpointOverridden + location ,
392+ },
393+ }
394+ return
395+ }
396+ location = strings .TrimSpace (location )
397+ endpoint = strings .TrimSpace (endpoint )
398+ servers := configProvider .GetConfig ().Servers
399+ for idx := range servers {
400+ if strings .Contains (servers [idx ].URL , location ) {
401+ SdkLogger .Printf ("[DEBUG] Overriding server configuration for location %s" , location )
402+ servers [idx ].URL = endpoint
403+ servers [idx ].Description = EndpointOverridden + location
404+ return
405+ }
406+ }
407+ SdkLogger .Printf ("[DEBUG] Adding new server configuration for location %s" , location )
408+ configProvider .GetConfig ().Servers = append (configProvider .GetConfig ().Servers , ServerConfiguration {
409+ URL : endpoint ,
410+ Description : EndpointOverridden + location ,
411+ })
412+ }
413+
414+ func SetSkipTLSVerify (configProvider ConfigProvider , skipTLSVerify bool ) {
415+ configProvider .GetConfig ().HTTPClient .Transport = & http.Transport {
416+ TLSClientConfig : & tls.Config {InsecureSkipVerify : skipTLSVerify },
417+ }
418+ }
419+
420+ // AddCertsToClient adds certificates to the http client
421+ func AddCertsToClient (authorityData string ) * x509.CertPool {
422+ rootCAs , _ := x509 .SystemCertPool ()
423+ if rootCAs == nil {
424+ rootCAs = x509 .NewCertPool ()
425+ }
426+ if ok := rootCAs .AppendCertsFromPEM ([]byte (authorityData )); ! ok && SdkLogLevel .Satisfies (Debug ) {
427+ SdkLogger .Printf ("No certs appended, using system certs only" )
428+ }
429+ return rootCAs
430+ }
0 commit comments