config.go raw
1 package linodego
2
3 import (
4 "fmt"
5 "os"
6 "strings"
7
8 "gopkg.in/ini.v1"
9 )
10
11 const (
12 DefaultConfigProfile = "default"
13 )
14
15 var DefaultConfigPaths = []string{
16 "%s/.config/linode",
17 "%s/.config/linode-cli",
18 }
19
20 type ConfigProfile struct {
21 APIToken string `ini:"token"`
22 APIVersion string `ini:"api_version"`
23 APIURL string `ini:"api_url"`
24 }
25
26 type LoadConfigOptions struct {
27 Path string
28 Profile string
29 SkipLoadProfile bool
30 }
31
32 // LoadConfig loads a Linode config according to the option's argument.
33 // If no options are specified, the following defaults will be used:
34 // Path: ~/.config/linode
35 // Profile: default
36 func (c *Client) LoadConfig(options *LoadConfigOptions) error {
37 path, err := resolveValidConfigPath()
38 if err != nil {
39 return err
40 }
41
42 profileOption := DefaultConfigProfile
43
44 if options != nil {
45 if options.Path != "" {
46 path = options.Path
47 }
48
49 if options.Profile != "" {
50 profileOption = options.Profile
51 }
52 }
53
54 cfg, err := ini.Load(path)
55 if err != nil {
56 return err
57 }
58
59 defaultConfig := ConfigProfile{
60 APIToken: "",
61 APIURL: APIHost,
62 APIVersion: APIVersion,
63 }
64
65 if cfg.HasSection("default") {
66 err := cfg.Section("default").MapTo(&defaultConfig)
67 if err != nil {
68 return fmt.Errorf("failed to map default profile: %w", err)
69 }
70 }
71
72 result := make(map[string]ConfigProfile)
73
74 for _, profile := range cfg.Sections() {
75 name := strings.ToLower(profile.Name())
76
77 f := defaultConfig
78 if err := profile.MapTo(&f); err != nil {
79 return fmt.Errorf("failed to map values: %w", err)
80 }
81
82 result[name] = f
83 }
84
85 c.configProfiles = result
86
87 if !options.SkipLoadProfile {
88 if err := c.UseProfile(profileOption); err != nil {
89 return fmt.Errorf("unable to use profile %s: %w", profileOption, err)
90 }
91 }
92
93 return nil
94 }
95
96 // UseProfile switches client to use the specified profile.
97 // The specified profile must be already be loaded using client.LoadConfig(...)
98 func (c *Client) UseProfile(name string) error {
99 name = strings.ToLower(name)
100
101 profile, ok := c.configProfiles[name]
102 if !ok {
103 return fmt.Errorf("profile %s does not exist", name)
104 }
105
106 if profile.APIToken == "" {
107 return fmt.Errorf("unable to resolve linode_token for profile %s", name)
108 }
109
110 if profile.APIURL == "" {
111 return fmt.Errorf("unable to resolve linode_api_url for profile %s", name)
112 }
113
114 if profile.APIVersion == "" {
115 return fmt.Errorf("unable to resolve linode_api_version for profile %s", name)
116 }
117
118 c.SetToken(profile.APIToken)
119 c.SetBaseURL(profile.APIURL)
120 c.SetAPIVersion(profile.APIVersion)
121 c.selectedProfile = name
122 c.loadedProfile = name
123
124 return nil
125 }
126
127 func FormatConfigPath(path string) (string, error) {
128 homeDir, err := os.UserHomeDir()
129 if err != nil {
130 return "", err
131 }
132
133 return fmt.Sprintf(path, homeDir), nil
134 }
135
136 func resolveValidConfigPath() (string, error) {
137 for _, cfg := range DefaultConfigPaths {
138 p, err := FormatConfigPath(cfg)
139 if err != nil {
140 return "", err
141 }
142
143 if _, err = os.Stat(p); err != nil {
144 continue
145 }
146
147 return p, err
148 }
149
150 // An empty result may not be an error
151 return "", nil
152 }
153