Skip to content

Commit 273c061

Browse files
fix: rename to fileconfiguration
1 parent 89b1868 commit 273c061

File tree

5 files changed

+450
-478
lines changed

5 files changed

+450
-478
lines changed

shared/configuration.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package shared
66

77
import (
88
"context"
9+
"crypto/tls"
910
"fmt"
1011
"net/http"
1112
"net/url"
@@ -286,3 +287,54 @@ func (c *Configuration) ServerURLWithContext(ctx context.Context, endpoint strin
286287

287288
return sc.URL(index, variables)
288289
}
290+
291+
// ConfigProvider is an interface that allows to get the configuration of shared clients
292+
type ConfigProvider interface {
293+
GetConfig() *Configuration
294+
}
295+
296+
// EndpointOverridden is a constant that is used to mark the endpoint as overridden and can be used to search for the location
297+
// in the server configuration.
298+
const EndpointOverridden = "endpoint from config file"
299+
300+
// OverrideLocationFor aims to override the server URL for a given client configuration, based on location and endpoint inputs.
301+
// Mutates the client configuration. It searches for the location in the server configuration and overrides the endpoint.
302+
// If the endpoint is empty, it early exits without making changes.
303+
func OverrideLocationFor(configProvider ConfigProvider, location, endpoint string, replaceServers bool) {
304+
if endpoint == "" {
305+
return
306+
}
307+
// If the replaceServers flag is set, we replace the servers with the new endpoint
308+
if replaceServers {
309+
SdkLogger.Printf("[DEBUG] Replacing all server configurations for location %s", location)
310+
configProvider.GetConfig().Servers = []ServerConfiguration{
311+
{
312+
URL: endpoint,
313+
Description: EndpointOverridden + location,
314+
},
315+
}
316+
return
317+
}
318+
location = strings.TrimSpace(location)
319+
endpoint = strings.TrimSpace(endpoint)
320+
servers := configProvider.GetConfig().Servers
321+
for idx := range servers {
322+
if strings.Contains(servers[idx].URL, location) {
323+
SdkLogger.Printf("[DEBUG] Overriding server configuration for location %s", location)
324+
servers[idx].URL = endpoint
325+
servers[idx].Description = EndpointOverridden + location
326+
return
327+
}
328+
}
329+
SdkLogger.Printf("[DEBUG] Adding new server configuration for location %s", location)
330+
configProvider.GetConfig().Servers = append(configProvider.GetConfig().Servers, ServerConfiguration{
331+
URL: endpoint,
332+
Description: EndpointOverridden + location,
333+
})
334+
}
335+
336+
func SetSkipTLSVerify(configProvider ConfigProvider, skipTLSVerify bool) {
337+
configProvider.GetConfig().HTTPClient.Transport = &http.Transport{
338+
TLSClientConfig: &tls.Config{InsecureSkipVerify: skipTLSVerify},
339+
}
340+
}
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
package fileconfiguration
2+
3+
import (
4+
"crypto/x509"
5+
"fmt"
6+
"github.com/ionos-cloud/sdk-go-bundle/shared"
7+
"gopkg.in/yaml.v3"
8+
"net/http"
9+
"os"
10+
"path/filepath"
11+
)
12+
13+
const (
14+
Cloud = "cloud"
15+
Mariadb = "mariadb"
16+
Logging = "logging"
17+
PSQL = "psql"
18+
Monitoring = "monitoring"
19+
Cert = "cert"
20+
ContainerRegistry = "containerregistry"
21+
Dataplatform = "dataplatform"
22+
DNS = "dns"
23+
Autoscaling = "autoscaling"
24+
Kafka = "kafka"
25+
CDN = "cdn"
26+
APIGateway = "apigateway"
27+
VPN = "vpn"
28+
InMemoryDB = "inmemorydb"
29+
ObjectStorage = "objectstorage"
30+
NFS = "nfs"
31+
)
32+
33+
// ClientOverrideOptions is a struct that represents the client override options
34+
type ClientOverrideOptions struct {
35+
// Endpoint is the endpoint that will be overridden
36+
Endpoint string
37+
// SkipTLSVerify skips tls verification. Not recommended for production!
38+
SkipTLSVerify bool
39+
// Certificate is the certificate that will be used for tls verification
40+
Certificate string
41+
// Credentials are the credentials that will be used for authentication
42+
Credentials Credentials
43+
}
44+
45+
// Credentials are the credentials that will be used for authentication
46+
type Credentials struct {
47+
Username string `yaml:"username"`
48+
Password string `yaml:"password"`
49+
Token string `yaml:"token"`
50+
}
51+
52+
// Endpoint is a struct that represents an endpoint
53+
type Endpoint struct {
54+
// the location or the region
55+
Location string `yaml:"location"`
56+
Name string `yaml:"name"`
57+
SkipTLSVerify bool `yaml:"skipTlsVerify"`
58+
CertificateAuthData string `yaml:"certificateAuthData,omitempty"`
59+
}
60+
61+
// Product is a struct that represents a product
62+
type Product struct {
63+
// Name is the name of the product
64+
Name string `yaml:"name"`
65+
Endpoints []Endpoint `yaml:"endpoints"`
66+
}
67+
68+
// Environment is a struct that represents an environment
69+
type Environment struct {
70+
Name string `yaml:"name"`
71+
// CertificateAuthData
72+
CertificateAuthData string `yaml:"certificateAuthData,omitempty"`
73+
// Products is a list of ionos products for which we will override endpoint, tls verification
74+
Products []Product `yaml:"products"`
75+
}
76+
77+
// Profiles wrapper to read only the profiles from the config file
78+
type Profiles struct {
79+
// CurrentProfile active profile for configuration
80+
CurrentProfile string `yaml:"currentProfile"`
81+
// Profiles
82+
Profiles []Profile `yaml:"profiles"`
83+
}
84+
85+
// Profile is a struct that represents a profile and it's Credentials
86+
type Profile struct {
87+
Name string `yaml:"name"`
88+
Environment string `yaml:"environment"`
89+
Credentials Credentials
90+
}
91+
92+
// FileConfig is a struct that represents the loaded configuration
93+
type FileConfig struct {
94+
// Version of the configuration
95+
Version float64 `yaml:"version"`
96+
// CurrentProfile active profile for configuration
97+
CurrentProfile string `yaml:"currentProfile"`
98+
// Profiles list of profiles
99+
Profiles []Profile `yaml:"profiles"`
100+
// Environments list of environments
101+
Environments []Environment `yaml:"environments"`
102+
}
103+
104+
// DefaultConfigFileName returns the default file path for the loaded configuration
105+
func DefaultConfigFileName() (string, error) {
106+
home := ""
107+
var err error
108+
if home, err = os.UserHomeDir(); err != nil {
109+
return home, err
110+
}
111+
if home == "" {
112+
return home, fmt.Errorf("could not determine home directory")
113+
}
114+
return filepath.Join(home, ".ionos", "config"), nil
115+
116+
}
117+
118+
func findConfigFile() string {
119+
filePath := ""
120+
filePath = os.Getenv(shared.IonosFilePathEnvVar)
121+
var err error
122+
if filePath == "" {
123+
if filePath, err = DefaultConfigFileName(); err != nil {
124+
return ""
125+
}
126+
}
127+
if _, err = os.Stat(filePath); os.IsNotExist(err) {
128+
return ""
129+
}
130+
return filePath
131+
}
132+
133+
// ReadProfilesFromFile reads profiles from yaml file, loads it into a struct and returns it
134+
func ReadProfilesFromFile() *Profiles {
135+
filePath := findConfigFile()
136+
if filePath == "" {
137+
return nil
138+
}
139+
var content []byte
140+
var err error
141+
if content, err = os.ReadFile(filePath); err != nil {
142+
return nil
143+
}
144+
loadedProfiles := &Profiles{}
145+
_ = yaml.Unmarshal(content, loadedProfiles)
146+
return loadedProfiles
147+
}
148+
149+
// ReadConfigFromFile reads yaml file, loads it into a struct and returns it
150+
// IONOS_CONFIG_FILE environment variable can be set to point to the file to be loaded
151+
func ReadConfigFromFile() (*FileConfig, error) {
152+
filePath := ""
153+
filePath = os.Getenv(shared.IonosFilePathEnvVar)
154+
var err error
155+
if filePath == "" {
156+
if filePath, err = DefaultConfigFileName(); err != nil {
157+
return nil, err
158+
}
159+
}
160+
if _, err = os.Stat(filePath); os.IsNotExist(err) {
161+
return nil, fmt.Errorf("file %s does not exist", filePath)
162+
}
163+
var content []byte
164+
if content, err = os.ReadFile(filePath); err != nil {
165+
return nil, err
166+
}
167+
if len(content) == 0 {
168+
return nil, fmt.Errorf("file %s is empty", filePath)
169+
}
170+
loadedConfig := &FileConfig{}
171+
if err = yaml.Unmarshal(content, loadedConfig); err != nil {
172+
return nil, fmt.Errorf("while unmarshalling file %s, %w", filePath, err)
173+
}
174+
if os.Getenv(shared.IonosCurrentProfileEnvVar) != "" {
175+
loadedConfig.CurrentProfile = os.Getenv(shared.IonosCurrentProfileEnvVar)
176+
}
177+
return loadedConfig, nil
178+
}
179+
180+
// GetCurrentProfile returns the current profile from the loaded configuration
181+
// if the current profile is not set, it returns nil
182+
// if the current profile is set and found in the loaded configuration, it returns the profile
183+
func (fileConfig *FileConfig) GetCurrentProfile() *Profile {
184+
currentProfile := os.Getenv(shared.IonosCurrentProfileEnvVar)
185+
if currentProfile == "" {
186+
currentProfile = fileConfig.CurrentProfile
187+
}
188+
if currentProfile == "" {
189+
shared.SdkLogger.Printf("[WARN] no current profile set")
190+
return nil
191+
}
192+
for _, profile := range fileConfig.Profiles {
193+
if profile.Name == currentProfile {
194+
if shared.SdkLogLevel.Satisfies(shared.Debug) {
195+
shared.SdkLogger.Printf("[DEBUG] using profile %s", profile.Name)
196+
}
197+
return &profile
198+
}
199+
}
200+
if shared.SdkLogLevel.Satisfies(shared.Debug) {
201+
shared.SdkLogger.Printf("[WARN] no profile found for %s", currentProfile)
202+
}
203+
return nil
204+
}
205+
206+
func (fileConfig *FileConfig) GetEnvForCurrentProfile() string {
207+
if fileConfig == nil {
208+
return ""
209+
}
210+
if currentProfile := fileConfig.GetCurrentProfile(); currentProfile != nil {
211+
return currentProfile.Environment
212+
}
213+
return ""
214+
}
215+
216+
// GetProductOverrides returns the overrides for a specific product for the current environment
217+
// if no current environment is found, the first environment is used for the product that matches productName is returned
218+
func (fileConfig *FileConfig) GetProductOverrides(productName string) *Product {
219+
if fileConfig == nil {
220+
return nil
221+
}
222+
if productName == "" {
223+
if shared.SdkLogLevel.Satisfies(shared.Debug) {
224+
shared.SdkLogger.Printf("[DEBUG] cannot get overrides as product name is empty")
225+
}
226+
return nil
227+
}
228+
currentEnv := fileConfig.GetEnvForCurrentProfile()
229+
for _, environment := range fileConfig.Environments {
230+
if currentEnv != "" && environment.Name != currentEnv {
231+
continue
232+
}
233+
for _, product := range environment.Products {
234+
if product.Name == productName {
235+
return &product
236+
}
237+
}
238+
}
239+
if shared.SdkLogLevel.Satisfies(shared.Debug) {
240+
shared.SdkLogger.Printf("[DEBUG] no environment overrides found for product %s", productName)
241+
}
242+
return nil
243+
}
244+
245+
func (fileConfig *FileConfig) GetProductLocationOverrides(productName, location string) *Endpoint {
246+
if fileConfig == nil {
247+
return nil
248+
}
249+
product := fileConfig.GetProductOverrides(productName)
250+
if product == nil || len(product.Endpoints) == 0 {
251+
return nil
252+
}
253+
for _, endpoint := range product.Endpoints {
254+
if endpoint.Location == location {
255+
return &endpoint
256+
}
257+
}
258+
if shared.SdkLogLevel.Satisfies(shared.Debug) {
259+
shared.SdkLogger.Printf("[DEBUG] no endpoint overrides found for product %s and location %s", productName, location)
260+
}
261+
return nil
262+
}
263+
264+
// AddCertsToClient adds certificates to the http client
265+
func AddCertsToClient(httpClient *http.Client, authorityData string) {
266+
rootCAs, _ := x509.SystemCertPool()
267+
if rootCAs == nil {
268+
rootCAs = x509.NewCertPool()
269+
}
270+
if ok := rootCAs.AppendCertsFromPEM([]byte(authorityData)); !ok && shared.SdkLogLevel.Satisfies(shared.Debug) {
271+
shared.SdkLogger.Printf("No certs appended, using system certs only")
272+
}
273+
httpClient.Transport.(*http.Transport).TLSClientConfig.RootCAs = rootCAs
274+
//httpClient.Transport = &http.Transport{
275+
// TLSClientConfig: &tls.Config{
276+
// RootCAs: rootCAs,
277+
// },
278+
//}
279+
}

0 commit comments

Comments
 (0)