Andrey Smirnov 33332f4c74 chore: support bootloader emulation in firecracker provisioner
Firecracker launches tries to open VM disk image before every boot,
parses partition table, finds boot partition, tries to read it as FAT32
filesystem, extracts uncompressed kernel from `bzImage` (firecracker
doesn't support `bzImage` yet), extracts initramfs and passes it to
firecracker binary.

This flow allows for extended tests, e.g. testing installer, upgrade and
downgrade tests, etc.

Bootloader emulation is disabled by default for now, can be enabled via
`--with-bootloader-emulation` flag to `osctl cluster create`.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
2020-02-13 23:21:37 +03:00

109 lines
2.9 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 firecracker
import (
"context"
"fmt"
"os"
"path/filepath"
"gopkg.in/yaml.v2"
"github.com/talos-systems/talos/internal/pkg/provision"
)
// Create Talos cluster as a set of firecracker micro-VMs.
//
//nolint: gocyclo
func (p *provisioner) Create(ctx context.Context, request provision.ClusterRequest, opts ...provision.Option) (provision.Cluster, error) {
options := provision.DefaultOptions()
for _, opt := range opts {
if err := opt(&options); err != nil {
return nil, err
}
}
state := &state{
ProvisionerName: "firecracker",
statePath: filepath.Join(request.StateDirectory, request.Name),
}
fmt.Fprintf(options.LogWriter, "creating state directory in %q\n", state.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 osctl 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)
}
fmt.Fprintln(options.LogWriter, "creating network", request.Network.Name)
if err = p.createNetwork(ctx, state, request.Network); err != nil {
return nil, fmt.Errorf("unable to provision CNI network: %w", err)
}
fmt.Fprintln(options.LogWriter, "creating load balancer")
if err = p.createLoadBalancer(state, request); err != nil {
return nil, fmt.Errorf("error creating loadbalancer: %w", err)
}
var nodeInfo []provision.NodeInfo
fmt.Fprintln(options.LogWriter, "creating master nodes")
if nodeInfo, err = p.createNodes(state, request, request.Nodes.MasterNodes(), &options); err != nil {
return nil, err
}
fmt.Fprintln(options.LogWriter, "creating worker nodes")
var workerNodeInfo []provision.NodeInfo
if workerNodeInfo, err = p.createNodes(state, request, request.Nodes.WorkerNodes(), &options); err != nil {
return nil, err
}
nodeInfo = append(nodeInfo, workerNodeInfo...)
state.ClusterInfo = provision.ClusterInfo{
ClusterName: request.Name,
Network: provision.NetworkInfo{
Name: request.Network.Name,
CIDR: request.Network.CIDR,
GatewayAddr: request.Network.GatewayAddr,
MTU: request.Network.MTU,
},
Nodes: nodeInfo,
}
// save state
stateFile, err := os.Create(filepath.Join(state.statePath, stateFileName))
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()
}