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 <artem.0xD2@gmail.com>
This commit is contained in:
Artem Chernyshev 2020-07-18 16:20:13 +03:00 committed by talos-bot
parent 8dd81b0693
commit 19cd46459b
11 changed files with 162 additions and 102 deletions

@ -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

@ -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
}

@ -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)
}

@ -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
}

@ -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)
}

@ -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 {

@ -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
}

@ -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
}

@ -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)
}

@ -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)
}

@ -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
}