mirror of
https://github.com/siderolabs/talos.git
synced 2025-09-14 02:11:10 +02:00
191 lines
4.0 KiB
Go
191 lines
4.0 KiB
Go
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package config
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/talos-systems/crypto/x509"
|
|
yaml "gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// Config represents the client configuration file (talosconfig).
|
|
type Config struct {
|
|
Context string `yaml:"context"`
|
|
Contexts map[string]*Context `yaml:"contexts"`
|
|
}
|
|
|
|
// NewConfig returns the client configuration file with a single context.
|
|
func NewConfig(contextName string, endpoints []string, caCrt []byte, client *x509.PEMEncodedCertificateAndKey) *Config {
|
|
return &Config{
|
|
Context: contextName,
|
|
Contexts: map[string]*Context{
|
|
contextName: {
|
|
Endpoints: endpoints,
|
|
CA: base64.StdEncoding.EncodeToString(caCrt),
|
|
Crt: base64.StdEncoding.EncodeToString(client.Crt),
|
|
Key: base64.StdEncoding.EncodeToString(client.Key),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (c *Config) upgrade() {
|
|
for _, ctx := range c.Contexts {
|
|
ctx.upgrade()
|
|
}
|
|
}
|
|
|
|
// Context represents the set of credentials required to talk to a target.
|
|
type Context struct {
|
|
DeprecatedTarget string `yaml:"target,omitempty"` // Field deprecated in favor of Endpoints
|
|
Endpoints []string `yaml:"endpoints"`
|
|
Nodes []string `yaml:"nodes,omitempty"`
|
|
CA string `yaml:"ca"`
|
|
Crt string `yaml:"crt"`
|
|
Key string `yaml:"key"`
|
|
}
|
|
|
|
func (c *Context) upgrade() {
|
|
if c.DeprecatedTarget != "" {
|
|
c.Endpoints = append(c.Endpoints, c.DeprecatedTarget)
|
|
c.DeprecatedTarget = ""
|
|
}
|
|
}
|
|
|
|
// Open reads the config and initializes a Config struct.
|
|
func Open(p string) (c *Config, err error) {
|
|
if err = ensure(p); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var f *os.File
|
|
|
|
f, err = os.Open(p)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
defer f.Close() //nolint:errcheck
|
|
|
|
return ReadFrom(f)
|
|
}
|
|
|
|
// FromString returns a config from a string.
|
|
func FromString(p string) (c *Config, err error) {
|
|
return ReadFrom(bytes.NewReader([]byte(p)))
|
|
}
|
|
|
|
// FromBytes returns a config from []byte.
|
|
func FromBytes(b []byte) (c *Config, err error) {
|
|
return ReadFrom(bytes.NewReader(b))
|
|
}
|
|
|
|
// ReadFrom reads a config from io.Reader.
|
|
func ReadFrom(r io.Reader) (c *Config, err error) {
|
|
c = &Config{}
|
|
|
|
if err = yaml.NewDecoder(r).Decode(c); err != nil {
|
|
return
|
|
}
|
|
|
|
c.upgrade()
|
|
|
|
return
|
|
}
|
|
|
|
// Save writes the config to disk.
|
|
func (c *Config) Save(p string) (err error) {
|
|
configBytes, err := c.Bytes()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if err = os.MkdirAll(filepath.Dir(p), 0o700); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = ioutil.WriteFile(p, configBytes, 0o600); err != nil {
|
|
return
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Bytes gets yaml encoded config data.
|
|
func (c *Config) Bytes() ([]byte, error) {
|
|
return yaml.Marshal(c)
|
|
}
|
|
|
|
// Rename describes context rename during merge.
|
|
type Rename struct {
|
|
From string
|
|
To string
|
|
}
|
|
|
|
// String converts to "from" -> "to".
|
|
func (r *Rename) String() string {
|
|
return fmt.Sprintf("%q -> %q", r.From, r.To)
|
|
}
|
|
|
|
// Merge in additional contexts from another Config.
|
|
//
|
|
// Current context is overridden from passed in config.
|
|
func (c *Config) Merge(cfg *Config) []Rename {
|
|
if c.Contexts == nil {
|
|
c.Contexts = map[string]*Context{}
|
|
}
|
|
|
|
mappedContexts := map[string]string{}
|
|
renames := []Rename{}
|
|
|
|
for name, ctx := range cfg.Contexts {
|
|
mergedName := name
|
|
|
|
if _, exists := c.Contexts[mergedName]; exists {
|
|
for i := 1; ; i++ {
|
|
mergedName = fmt.Sprintf("%s-%d", name, i)
|
|
|
|
if _, exists := c.Contexts[mergedName]; !exists {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
mappedContexts[name] = mergedName
|
|
|
|
if name != mergedName {
|
|
renames = append(renames, Rename{name, mergedName})
|
|
}
|
|
|
|
c.Contexts[mergedName] = ctx
|
|
}
|
|
|
|
if cfg.Context != "" {
|
|
c.Context = mappedContexts[cfg.Context]
|
|
}
|
|
|
|
return renames
|
|
}
|
|
|
|
func ensure(filename string) (err error) {
|
|
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
|
config := &Config{
|
|
Context: "",
|
|
Contexts: map[string]*Context{},
|
|
}
|
|
|
|
return config.Save(filename)
|
|
}
|
|
|
|
return nil
|
|
}
|