talos

Форк
0
260 строк · 5.9 Кб
1
// This Source Code Form is subject to the terms of the Mozilla Public
2
// License, v. 2.0. If a copy of the MPL was not distributed with this
3
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4

5
package config
6

7
import (
8
	"bytes"
9
	"encoding/base64"
10
	"fmt"
11
	"io"
12
	"os"
13
	"path/filepath"
14

15
	"github.com/siderolabs/crypto/x509"
16
	"gopkg.in/yaml.v3"
17
)
18

19
// Config represents the client configuration file (talosconfig).
20
type Config struct {
21
	Context  string              `yaml:"context"`
22
	Contexts map[string]*Context `yaml:"contexts"`
23

24
	// path is the config Path config is read from.
25
	path Path
26
}
27

28
// NewConfig returns the client configuration file with a single context.
29
func NewConfig(contextName string, endpoints []string, caCrt []byte, client *x509.PEMEncodedCertificateAndKey) *Config {
30
	return &Config{
31
		Context: contextName,
32
		Contexts: map[string]*Context{
33
			contextName: {
34
				Endpoints: endpoints,
35
				CA:        base64.StdEncoding.EncodeToString(caCrt),
36
				Crt:       base64.StdEncoding.EncodeToString(client.Crt),
37
				Key:       base64.StdEncoding.EncodeToString(client.Key),
38
			},
39
		},
40
	}
41
}
42

43
func (c *Config) upgrade() {
44
	for _, ctx := range c.Contexts {
45
		ctx.upgrade()
46
	}
47
}
48

49
// Context represents the set of credentials required to talk to a target.
50
type Context struct {
51
	DeprecatedTarget string   `yaml:"target,omitempty"` // Field deprecated in favor of Endpoints
52
	Endpoints        []string `yaml:"endpoints"`
53
	Nodes            []string `yaml:"nodes,omitempty"`
54
	CA               string   `yaml:"ca,omitempty"`
55
	Crt              string   `yaml:"crt,omitempty"`
56
	Key              string   `yaml:"key,omitempty"`
57
	Auth             Auth     `yaml:"auth,omitempty"`
58
	Cluster          string   `yaml:"cluster,omitempty"`
59
}
60

61
// Auth may hold credentials for an authentication method such as Basic Auth.
62
type Auth struct {
63
	Basic    *Basic    `yaml:"basic,omitempty"`
64
	SideroV1 *SideroV1 `yaml:"siderov1,omitempty"`
65
}
66

67
// Basic holds Basic Auth credentials.
68
type Basic struct {
69
	Username string `yaml:"username"`
70
	Password string `yaml:"password"`
71
}
72

73
// SideroV1 holds information for SideroV1 API signature auth.
74
type SideroV1 struct {
75
	Identity string `yaml:"identity"`
76
}
77

78
func (c *Context) upgrade() {
79
	if c.DeprecatedTarget != "" {
80
		c.Endpoints = append(c.Endpoints, c.DeprecatedTarget)
81
		c.DeprecatedTarget = ""
82
	}
83
}
84

85
// Open reads the config and initializes a Config struct.
86
// If path is explicitly set, it will be used.
87
// If not, the default path rules will be used.
88
func Open(path string) (*Config, error) {
89
	var (
90
		confPath Path
91
		err      error
92
	)
93

94
	if path != "" { // path is explicitly specified, ensure that is created and use it
95
		confPath = Path{
96
			Path:         path,
97
			WriteAllowed: true,
98
		}
99

100
		err = ensure(confPath.Path)
101
		if err != nil {
102
			return nil, err
103
		}
104
	} else { // path is implicit, get the first already existing & readable path or ensure that it is created
105
		confPath, err = firstValidPath()
106
		if err != nil {
107
			return nil, err
108
		}
109
	}
110

111
	config, err := fromFile(confPath.Path)
112
	if err != nil {
113
		return nil, err
114
	}
115

116
	config.path = confPath
117

118
	return config, nil
119
}
120

121
func fromFile(path string) (*Config, error) {
122
	file, err := os.Open(path)
123
	if err != nil {
124
		return nil, err
125
	}
126

127
	defer file.Close() //nolint:errcheck
128

129
	return ReadFrom(file)
130
}
131

132
// FromString returns a config from a string.
133
func FromString(p string) (c *Config, err error) {
134
	return ReadFrom(bytes.NewReader([]byte(p)))
135
}
136

137
// FromBytes returns a config from []byte.
138
func FromBytes(b []byte) (c *Config, err error) {
139
	return ReadFrom(bytes.NewReader(b))
140
}
141

142
// ReadFrom reads a config from io.Reader.
143
func ReadFrom(r io.Reader) (c *Config, err error) {
144
	c = &Config{}
145

146
	if err = yaml.NewDecoder(r).Decode(c); err != nil {
147
		return
148
	}
149

150
	c.upgrade()
151

152
	return
153
}
154

155
// Save writes the config to disk.
156
// If the path is not explicitly set, the default path rules will be used.
157
func (c *Config) Save(path string) error {
158
	var err error
159

160
	if path != "" { // path is explicitly specified, use it
161
		c.path = Path{
162
			Path:         path,
163
			WriteAllowed: true,
164
		}
165
	} else if c.path.Path == "" { // path is implicit and is not set on config, get the first already existing & writable path or create it
166
		c.path, err = firstValidPath()
167
		if err != nil {
168
			return err
169
		}
170
	}
171

172
	if !c.path.WriteAllowed {
173
		return fmt.Errorf("not allowed to write to config: %s", c.path.Path)
174
	}
175

176
	configBytes, err := c.Bytes()
177
	if err != nil {
178
		return err
179
	}
180

181
	if err = os.MkdirAll(filepath.Dir(c.path.Path), 0o700); err != nil {
182
		return err
183
	}
184

185
	return os.WriteFile(c.path.Path, configBytes, 0o600)
186
}
187

188
// Bytes gets yaml encoded config data.
189
func (c *Config) Bytes() ([]byte, error) {
190
	return yaml.Marshal(c)
191
}
192

193
// Path returns the filesystem path config was read from.
194
func (c *Config) Path() Path {
195
	return c.path
196
}
197

198
// Rename describes context rename during merge.
199
type Rename struct {
200
	From string
201
	To   string
202
}
203

204
// String converts to "from" -> "to".
205
func (r *Rename) String() string {
206
	return fmt.Sprintf("%q -> %q", r.From, r.To)
207
}
208

209
// Merge in additional contexts from another Config.
210
//
211
// Current context is overridden from passed in config.
212
func (c *Config) Merge(cfg *Config) []Rename {
213
	if c.Contexts == nil {
214
		c.Contexts = map[string]*Context{}
215
	}
216

217
	mappedContexts := map[string]string{}
218
	renames := []Rename{}
219

220
	for name, ctx := range cfg.Contexts {
221
		mergedName := name
222

223
		if _, exists := c.Contexts[mergedName]; exists {
224
			for i := 1; ; i++ {
225
				mergedName = fmt.Sprintf("%s-%d", name, i)
226

227
				if _, exists := c.Contexts[mergedName]; !exists {
228
					break
229
				}
230
			}
231
		}
232

233
		mappedContexts[name] = mergedName
234

235
		if name != mergedName {
236
			renames = append(renames, Rename{name, mergedName})
237
		}
238

239
		c.Contexts[mergedName] = ctx
240
	}
241

242
	if cfg.Context != "" {
243
		c.Context = mappedContexts[cfg.Context]
244
	}
245

246
	return renames
247
}
248

249
func ensure(path string) error {
250
	if _, err := os.Stat(path); os.IsNotExist(err) {
251
		config := &Config{
252
			Context:  "",
253
			Contexts: map[string]*Context{},
254
		}
255

256
		return config.Save(path)
257
	}
258

259
	return nil
260
}
261

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.