Andrey Smirnov caee24bf61
feat: implement KubeSpan identity controller
Fixes #4138

When KubeSpan is enabled, Talos automatically generates or loads
KubeSpan identity which consists of Wireguard key pair. ULA address is
calculated based on ClusterID and first NIC MAC address.

Some code was borrowed from #3577.

Example:

```
$ talosctl -n 172.20.0.2 get ksi
NODE         NAMESPACE   TYPE               ID      VERSION   ADDRESS                                       PUBLICKEY
172.20.0.2   kubespan    KubeSpanIdentity   local   1         fd71:6e1d:86be:6302:e871:1bff:feb2:ccee/128   Oak2fBEWngBhwslBxDVgnRNHXs88OAp4kjroSX0uqUE=
```

Additional changes:

* `--with-kubespan` flag for `talosctl cluster create` for quick testing
* validate that cluster discovery (and KubeSpan) requires ClusterID and
ClusterSecret.

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
Signed-off-by: Seán C McCord <ulexus@gmail.com>
Co-authored-by: Seán C McCord <ulexus@gmail.com>
2021-08-27 18:49:15 +03:00

61 lines
1.5 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 controllers provides common methods for controller operations.
package controllers
import (
"fmt"
"os"
"reflect"
yaml "gopkg.in/yaml.v3"
)
// LoadOrNewFromFile either loads value from file.yaml or generates new values and saves as file.yaml.
func LoadOrNewFromFile(path string, empty interface{}, generate func(interface{}) error) error {
f, err := os.OpenFile(path, os.O_RDONLY, 0)
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("error reading state file: %w", err)
}
// file doesn't exist yet, generate new value and save it
if f == nil {
if err = generate(empty); err != nil {
return err
}
f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0o600)
if err != nil {
return fmt.Errorf("error creating state file: %w", err)
}
defer f.Close() //nolint:errcheck
encoder := yaml.NewEncoder(f)
if err = encoder.Encode(empty); err != nil {
return fmt.Errorf("error marshaling: %w", err)
}
if err = encoder.Close(); err != nil {
return err
}
return f.Close()
}
// read existing cached value
defer f.Close() //nolint:errcheck
if err = yaml.NewDecoder(f).Decode(empty); err != nil {
return fmt.Errorf("error unmarshaling: %w", err)
}
if reflect.ValueOf(empty).Elem().IsZero() {
return fmt.Errorf("value is still zero after unmarshaling")
}
return f.Close()
}