From 19cd46459b5494910dd72ab0e6349c01aa7921a4 Mon Sep 17 00:00:00 2001 From: Artem Chernyshev Date: Sat, 18 Jul 2020 16:20:13 +0300 Subject: [PATCH] chore: initial extraction of base vm provisioner Created base provisioner struct for all VM based provisioners. Moved state.go and reflect.go to the common module. Signed-off-by: Artem Chernyshev --- .../providers/firecracker/crashdump.go | 3 +- .../provision/providers/firecracker/create.go | 44 +++------ .../providers/firecracker/destroy.go | 3 +- .../providers/firecracker/firecracker.go | 10 +- .../providers/firecracker/loadbalancer.go | 12 +-- .../providers/firecracker/network.go | 7 +- .../provision/providers/firecracker/node.go | 24 ++--- .../provision/providers/firecracker/state.go | 39 -------- .../providers/{firecracker => vm}/reflect.go | 11 ++- internal/pkg/provision/providers/vm/state.go | 97 +++++++++++++++++++ internal/pkg/provision/providers/vm/vm.go | 14 +++ 11 files changed, 162 insertions(+), 102 deletions(-) delete mode 100644 internal/pkg/provision/providers/firecracker/state.go rename internal/pkg/provision/providers/{firecracker => vm}/reflect.go (86%) create mode 100644 internal/pkg/provision/providers/vm/state.go create mode 100644 internal/pkg/provision/providers/vm/vm.go diff --git a/internal/pkg/provision/providers/firecracker/crashdump.go b/internal/pkg/provision/providers/firecracker/crashdump.go index b69805a00..befb6edc2 100644 --- a/internal/pkg/provision/providers/firecracker/crashdump.go +++ b/internal/pkg/provision/providers/firecracker/crashdump.go @@ -13,12 +13,13 @@ import ( "strings" "github.com/talos-systems/talos/internal/pkg/provision" + "github.com/talos-systems/talos/internal/pkg/provision/providers/vm" "github.com/talos-systems/talos/internal/pkg/tail" ) // CrashDump produces debug information to help with debugging failures. func (p *provisioner) CrashDump(ctx context.Context, cluster provision.Cluster, out io.Writer) { - state, ok := cluster.(*state) + state, ok := cluster.(*vm.State) if !ok { fmt.Fprintf(out, "error inspecting firecracker state, %#+v\n", cluster) return diff --git a/internal/pkg/provision/providers/firecracker/create.go b/internal/pkg/provision/providers/firecracker/create.go index 87c7b4de7..0d3e89f85 100644 --- a/internal/pkg/provision/providers/firecracker/create.go +++ b/internal/pkg/provision/providers/firecracker/create.go @@ -7,12 +7,10 @@ package firecracker import ( "context" "fmt" - "os" "path/filepath" - "gopkg.in/yaml.v2" - "github.com/talos-systems/talos/internal/pkg/provision" + "github.com/talos-systems/talos/internal/pkg/provision/providers/vm" ) // Create Talos cluster as a set of firecracker micro-VMs. @@ -27,28 +25,17 @@ func (p *provisioner) Create(ctx context.Context, request provision.ClusterReque } } - state := &state{ - ProvisionerName: "firecracker", - statePath: filepath.Join(request.StateDirectory, request.Name), - } + statePath := filepath.Join(request.StateDirectory, request.Name) - fmt.Fprintf(options.LogWriter, "creating state directory in %q\n", state.statePath) + fmt.Fprintf(options.LogWriter, "creating state directory in %q\n", statePath) - _, err := os.Stat(state.statePath) - if err == nil { - return nil, fmt.Errorf( - "state directory %q already exists, is the cluster %q already running? remove cluster state with talosctl cluster destroy", - state.statePath, - request.Name, - ) - } - - if !os.IsNotExist(err) { - return nil, fmt.Errorf("error checking state directory: %w", err) - } - - if err = os.MkdirAll(state.statePath, os.ModePerm); err != nil { - return nil, fmt.Errorf("error creating state directory: %w", err) + state, err := vm.NewState( + statePath, + p.Name, + request.Name, + ) + if err != nil { + return nil, err } fmt.Fprintln(options.LogWriter, "creating network", request.Network.Name) @@ -92,17 +79,10 @@ func (p *provisioner) Create(ctx context.Context, request provision.ClusterReque Nodes: nodeInfo, } - // save state - stateFile, err := os.Create(filepath.Join(state.statePath, stateFileName)) + err = state.Save() if err != nil { return nil, err } - defer stateFile.Close() //nolint: errcheck - - if err = yaml.NewEncoder(stateFile).Encode(&state); err != nil { - return nil, fmt.Errorf("error marshaling state: %w", err) - } - - return state, stateFile.Close() + return state, nil } diff --git a/internal/pkg/provision/providers/firecracker/destroy.go b/internal/pkg/provision/providers/firecracker/destroy.go index 49841684f..5663c743a 100644 --- a/internal/pkg/provision/providers/firecracker/destroy.go +++ b/internal/pkg/provision/providers/firecracker/destroy.go @@ -10,6 +10,7 @@ import ( "os" "github.com/talos-systems/talos/internal/pkg/provision" + "github.com/talos-systems/talos/internal/pkg/provision/providers/vm" ) // Destroy Talos cluster as set of Firecracker VMs. @@ -28,7 +29,7 @@ func (p *provisioner) Destroy(ctx context.Context, cluster provision.Cluster, op return err } - state, ok := cluster.(*state) + state, ok := cluster.(*vm.State) if !ok { return fmt.Errorf("error inspecting firecracker state, %#+v", cluster) } diff --git a/internal/pkg/provision/providers/firecracker/firecracker.go b/internal/pkg/provision/providers/firecracker/firecracker.go index a306a4a9d..ad80a86ce 100644 --- a/internal/pkg/provision/providers/firecracker/firecracker.go +++ b/internal/pkg/provision/providers/firecracker/firecracker.go @@ -10,18 +10,22 @@ import ( "github.com/talos-systems/talos/internal/app/machined/pkg/runtime" "github.com/talos-systems/talos/internal/pkg/provision" + "github.com/talos-systems/talos/internal/pkg/provision/providers/vm" "github.com/talos-systems/talos/pkg/config/types/v1alpha1" "github.com/talos-systems/talos/pkg/config/types/v1alpha1/generate" ) -const stateFileName = "state.yaml" - type provisioner struct { + vm.Provisioner } // NewProvisioner initializes docker provisioner. func NewProvisioner(ctx context.Context) (provision.Provisioner, error) { - p := &provisioner{} + p := &provisioner{ + vm.Provisioner{ + Name: "firecracker", + }, + } return p, nil } diff --git a/internal/pkg/provision/providers/firecracker/loadbalancer.go b/internal/pkg/provision/providers/firecracker/loadbalancer.go index d6542b899..5f844d88d 100644 --- a/internal/pkg/provision/providers/firecracker/loadbalancer.go +++ b/internal/pkg/provision/providers/firecracker/loadbalancer.go @@ -9,12 +9,12 @@ import ( "io/ioutil" "os" "os/exec" - "path/filepath" "strconv" "strings" "syscall" "github.com/talos-systems/talos/internal/pkg/provision" + "github.com/talos-systems/talos/internal/pkg/provision/providers/vm" ) const ( @@ -22,10 +22,10 @@ const ( lbLog = "lb.log" ) -func (p *provisioner) createLoadBalancer(state *state, clusterReq provision.ClusterRequest) error { - pidPath := filepath.Join(state.statePath, lbPid) +func (p *provisioner) createLoadBalancer(state *vm.State, clusterReq provision.ClusterRequest) error { + pidPath := state.GetRelativePath(lbPid) - logFile, err := os.OpenFile(filepath.Join(state.statePath, lbLog), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) + logFile, err := os.OpenFile(state.GetRelativePath(lbLog), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) if err != nil { return err } @@ -67,8 +67,8 @@ func (p *provisioner) createLoadBalancer(state *state, clusterReq provision.Clus return nil } -func (p *provisioner) destroyLoadBalancer(state *state) error { - pidPath := filepath.Join(state.statePath, lbPid) +func (p *provisioner) destroyLoadBalancer(state *vm.State) error { + pidPath := state.GetRelativePath(lbPid) return stopProcessByPidfile(pidPath) } diff --git a/internal/pkg/provision/providers/firecracker/network.go b/internal/pkg/provision/providers/firecracker/network.go index f776a9022..d9bf04d41 100644 --- a/internal/pkg/provision/providers/firecracker/network.go +++ b/internal/pkg/provision/providers/firecracker/network.go @@ -20,10 +20,11 @@ import ( "github.com/jsimonetti/rtnetlink" "github.com/talos-systems/talos/internal/pkg/provision" + "github.com/talos-systems/talos/internal/pkg/provision/providers/vm" talosnet "github.com/talos-systems/talos/pkg/net" ) -func (p *provisioner) createNetwork(ctx context.Context, state *state, network provision.NetworkRequest) error { +func (p *provisioner) createNetwork(ctx context.Context, state *vm.State, network provision.NetworkRequest) error { // build bridge interface name by taking part of checksum of the network name // so that interface name is defined by network name, and different networks have // different bridge interfaces @@ -111,14 +112,14 @@ func (p *provisioner) createNetwork(ctx context.Context, state *state, network p return fmt.Errorf("error templating VM CNI config: %w", err) } - if state.vmCNIConfig, err = libcni.ConfListFromBytes(buf.Bytes()); err != nil { + if state.VMCNIConfig, err = libcni.ConfListFromBytes(buf.Bytes()); err != nil { return fmt.Errorf("error parsing VM CNI config: %w", err) } return nil } -func (p *provisioner) destroyNetwork(state *state) error { +func (p *provisioner) destroyNetwork(state *vm.State) error { // destroy bridge interface by name to clean up iface, err := net.InterfaceByName(state.BridgeName) if err != nil { diff --git a/internal/pkg/provision/providers/firecracker/node.go b/internal/pkg/provision/providers/firecracker/node.go index ec9dfad3e..d9b2f1b92 100644 --- a/internal/pkg/provision/providers/firecracker/node.go +++ b/internal/pkg/provision/providers/firecracker/node.go @@ -11,22 +11,22 @@ import ( "math" "os" "os/exec" - "path/filepath" "strconv" "syscall" - "github.com/firecracker-microvm/firecracker-go-sdk" + firecracker "github.com/firecracker-microvm/firecracker-go-sdk" models "github.com/firecracker-microvm/firecracker-go-sdk/client/models" - "github.com/hashicorp/go-multierror" + multierror "github.com/hashicorp/go-multierror" "k8s.io/apimachinery/pkg/util/json" "github.com/talos-systems/go-procfs/procfs" "github.com/talos-systems/talos/internal/pkg/provision" + "github.com/talos-systems/talos/internal/pkg/provision/providers/vm" ) -func (p *provisioner) createDisk(state *state, nodeReq provision.NodeRequest) (diskPath string, err error) { - diskPath = filepath.Join(state.statePath, fmt.Sprintf("%s.disk", nodeReq.Name)) +func (p *provisioner) createDisk(state *vm.State, nodeReq provision.NodeRequest) (diskPath string, err error) { + diskPath = state.GetRelativePath(fmt.Sprintf("%s.disk", nodeReq.Name)) var diskF *os.File @@ -42,7 +42,7 @@ func (p *provisioner) createDisk(state *state, nodeReq provision.NodeRequest) (d return } -func (p *provisioner) createNodes(state *state, clusterReq provision.ClusterRequest, nodeReqs []provision.NodeRequest, opts *provision.Options) ([]provision.NodeInfo, error) { +func (p *provisioner) createNodes(state *vm.State, clusterReq provision.ClusterRequest, nodeReqs []provision.NodeRequest, opts *provision.Options) ([]provision.NodeInfo, error) { errCh := make(chan error) nodeCh := make(chan provision.NodeInfo, len(nodeReqs)) @@ -74,9 +74,9 @@ func (p *provisioner) createNodes(state *state, clusterReq provision.ClusterRequ return nodesInfo, multiErr.ErrorOrNil() } -func (p *provisioner) createNode(state *state, clusterReq provision.ClusterRequest, nodeReq provision.NodeRequest, opts *provision.Options) (provision.NodeInfo, error) { - socketPath := filepath.Join(state.statePath, fmt.Sprintf("%s.sock", nodeReq.Name)) - pidPath := filepath.Join(state.statePath, fmt.Sprintf("%s.pid", nodeReq.Name)) +func (p *provisioner) createNode(state *vm.State, clusterReq provision.ClusterRequest, nodeReq provision.NodeRequest, opts *provision.Options) (provision.NodeInfo, error) { + socketPath := state.GetRelativePath(fmt.Sprintf("%s.sock", nodeReq.Name)) + pidPath := state.GetRelativePath(fmt.Sprintf("%s.pid", nodeReq.Name)) vcpuCount := int64(math.RoundToEven(float64(nodeReq.NanoCPUs) / 1000 / 1000 / 1000)) if vcpuCount < 2 { @@ -128,7 +128,7 @@ func (p *provisioner) createNode(state *state, clusterReq provision.ClusterReque BinPath: clusterReq.Network.CNI.BinPath, ConfDir: clusterReq.Network.CNI.ConfDir, CacheDir: clusterReq.Network.CNI.CacheDir, - NetworkConfig: state.vmCNIConfig, + NetworkConfig: state.VMCNIConfig, Args: [][2]string{ {"IP", fmt.Sprintf("%s/%d", nodeReq.IP, ones)}, {"GATEWAY", clusterReq.Network.GatewayAddr.String()}, @@ -148,7 +148,7 @@ func (p *provisioner) createNode(state *state, clusterReq provision.ClusterReque }, } - logFile, err := os.OpenFile(filepath.Join(state.statePath, fmt.Sprintf("%s.log", nodeReq.Name)), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) + logFile, err := os.OpenFile(state.GetRelativePath(fmt.Sprintf("%s.log", nodeReq.Name)), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) if err != nil { return provision.NodeInfo{}, err } @@ -167,7 +167,7 @@ func (p *provisioner) createNode(state *state, clusterReq provision.ClusterReque BootloaderEmulation: opts.BootloaderEmulation, } - launchConfigFile, err := os.Create(filepath.Join(state.statePath, fmt.Sprintf("%s.config", nodeReq.Name))) + launchConfigFile, err := os.Create(state.GetRelativePath(fmt.Sprintf("%s.config", nodeReq.Name))) if err != nil { return provision.NodeInfo{}, err } diff --git a/internal/pkg/provision/providers/firecracker/state.go b/internal/pkg/provision/providers/firecracker/state.go deleted file mode 100644 index 49386799d..000000000 --- a/internal/pkg/provision/providers/firecracker/state.go +++ /dev/null @@ -1,39 +0,0 @@ -// 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 firecracker - -import ( - "fmt" - - "github.com/containernetworking/cni/libcni" - - "github.com/talos-systems/talos/internal/pkg/provision" -) - -type state struct { - ProvisionerName string - BridgeName string - - ClusterInfo provision.ClusterInfo - - vmCNIConfig *libcni.NetworkConfigList - statePath string -} - -func (s *state) Provisioner() string { - return "firecracker" -} - -func (s *state) Info() provision.ClusterInfo { - return s.ClusterInfo -} - -func (s *state) StatePath() (string, error) { - if s.statePath == "" { - return "", fmt.Errorf("state path is not set") - } - - return s.statePath, nil -} diff --git a/internal/pkg/provision/providers/firecracker/reflect.go b/internal/pkg/provision/providers/vm/reflect.go similarity index 86% rename from internal/pkg/provision/providers/firecracker/reflect.go rename to internal/pkg/provision/providers/vm/reflect.go index 3e38eb763..d98ac2b61 100644 --- a/internal/pkg/provision/providers/firecracker/reflect.go +++ b/internal/pkg/provision/providers/vm/reflect.go @@ -2,7 +2,7 @@ // 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 firecracker +package vm import ( "context" @@ -10,12 +10,13 @@ import ( "os" "path/filepath" - "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v2" "github.com/talos-systems/talos/internal/pkg/provision" ) -func (p *provisioner) Reflect(ctx context.Context, clusterName, stateDirectory string) (provision.Cluster, error) { +// Reflect decode state file. +func (p *Provisioner) Reflect(ctx context.Context, clusterName, stateDirectory string) (provision.Cluster, error) { statePath := filepath.Join(stateDirectory, clusterName) st, err := os.Stat(statePath) @@ -38,13 +39,13 @@ func (p *provisioner) Reflect(ctx context.Context, clusterName, stateDirectory s defer stateFile.Close() //nolint: errcheck - state := &state{} + state := &State{} if err = yaml.NewDecoder(stateFile).Decode(state); err != nil { return nil, fmt.Errorf("error unmarshalling state file: %w", err) } - if state.ProvisionerName != "firecracker" { + if state.ProvisionerName != p.Name { return nil, fmt.Errorf("cluster %q was created with different provisioner %q", clusterName, state.ProvisionerName) } diff --git a/internal/pkg/provision/providers/vm/state.go b/internal/pkg/provision/providers/vm/state.go new file mode 100644 index 000000000..14e480fee --- /dev/null +++ b/internal/pkg/provision/providers/vm/state.go @@ -0,0 +1,97 @@ +// 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 vm + +import ( + "fmt" + "os" + "path/filepath" + + yaml "gopkg.in/yaml.v2" + + "github.com/containernetworking/cni/libcni" + + "github.com/talos-systems/talos/internal/pkg/provision" +) + +// State common state representation for vm provisioners. +type State struct { + ProvisionerName string + BridgeName string + + ClusterInfo provision.ClusterInfo + + VMCNIConfig *libcni.NetworkConfigList + statePath string +} + +// NewState create new vm provisioner state. +func NewState(statePath, provisionerName, clusterName string) (*State, error) { + s := &State{ + ProvisionerName: provisionerName, + statePath: statePath, + } + + _, err := os.Stat(s.statePath) + + if err == nil { + return nil, fmt.Errorf( + "state directory %q already exists, is the cluster %q already running? remove cluster state with talosctl cluster destroy", + s.statePath, + clusterName, + ) + } + + if !os.IsNotExist(err) { + return nil, fmt.Errorf("error checking state directory: %w", err) + } + + if err = os.MkdirAll(s.statePath, os.ModePerm); err != nil { + return nil, fmt.Errorf("error creating state directory: %w", err) + } + + return s, nil +} + +// Provisioner get provisioner name. +func (s *State) Provisioner() string { + return s.ProvisionerName +} + +// Info get cluster info. +func (s *State) Info() provision.ClusterInfo { + return s.ClusterInfo +} + +// StatePath get state config file path. +func (s *State) StatePath() (string, error) { + if s.statePath == "" { + return "", fmt.Errorf("state path is not set") + } + + return s.statePath, nil +} + +// Save save state to config file. +func (s *State) Save() error { + // save state + stateFile, err := os.Create(filepath.Join(s.statePath, stateFileName)) + if err != nil { + return err + } + + defer stateFile.Close() //nolint: errcheck + + if err = yaml.NewEncoder(stateFile).Encode(&s); err != nil { + return fmt.Errorf("error marshaling state: %w", err) + } + + return stateFile.Close() +} + +// GetRelativePath get file path relative to config folder. +func (s *State) GetRelativePath(path string) string { + return filepath.Join(s.statePath, path) +} diff --git a/internal/pkg/provision/providers/vm/vm.go b/internal/pkg/provision/providers/vm/vm.go new file mode 100644 index 000000000..e580f3424 --- /dev/null +++ b/internal/pkg/provision/providers/vm/vm.go @@ -0,0 +1,14 @@ +// 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 vm implements common methods for VM provisioners. +package vm + +const stateFileName = "state.yaml" + +// Provisioner base for VM provisioners. +type Provisioner struct { + // Name actual provisioner type. + Name string +}