mirror of
https://github.com/siderolabs/talos.git
synced 2025-08-21 22:51:13 +02:00
To be used in the `go-blockdevice` library. Signed-off-by: Artem Chernyshev <artem.0xD2@gmail.com>
182 lines
4.3 KiB
Go
182 lines
4.3 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 qemu
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/coreos/go-iptables/iptables"
|
|
"github.com/hashicorp/go-getter"
|
|
"github.com/talos-systems/go-cmd/pkg/cmd"
|
|
|
|
"github.com/talos-systems/talos/pkg/machinery/constants"
|
|
"github.com/talos-systems/talos/pkg/provision"
|
|
)
|
|
|
|
func (p *provisioner) preflightChecks(ctx context.Context, request provision.ClusterRequest, options provision.Options, arch Arch) error {
|
|
checkContext := preflightCheckContext{
|
|
request: request,
|
|
options: options,
|
|
arch: arch,
|
|
}
|
|
|
|
for _, check := range []func(ctx context.Context) error{
|
|
checkContext.verifyRoot,
|
|
checkContext.checkKVM,
|
|
checkContext.qemuExecutable,
|
|
checkContext.checkFlashImages,
|
|
checkContext.cniDirectories,
|
|
checkContext.cniBundle,
|
|
checkContext.checkIptables,
|
|
} {
|
|
if err := check(ctx); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type preflightCheckContext struct {
|
|
request provision.ClusterRequest
|
|
options provision.Options
|
|
arch Arch
|
|
}
|
|
|
|
func (check *preflightCheckContext) verifyRoot(ctx context.Context) error {
|
|
if os.Geteuid() != 0 {
|
|
return fmt.Errorf("error: please run as root user (CNI requirement), we recommend running with `sudo -E`")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (check *preflightCheckContext) checkKVM(ctx context.Context) error {
|
|
f, err := os.OpenFile("/dev/kvm", os.O_RDWR, 0)
|
|
if err != nil {
|
|
return fmt.Errorf("error opening /dev/kvm, please make sure KVM support is enabled in Linux kernel: %w", err)
|
|
}
|
|
|
|
return f.Close()
|
|
}
|
|
|
|
func (check *preflightCheckContext) qemuExecutable(ctx context.Context) error {
|
|
if _, err := cmd.Run(check.arch.QemuExecutable(), "--version"); err != nil {
|
|
return fmt.Errorf("error running QEMU %q, please install QEMU with package manager: %w", check.arch.QemuExecutable(), err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (check *preflightCheckContext) checkFlashImages(ctx context.Context) error {
|
|
for _, flashImage := range check.arch.PFlash(check.options.UEFIEnabled) {
|
|
found := false
|
|
|
|
for _, path := range flashImage.SourcePaths {
|
|
_, err := os.Stat(path)
|
|
if err == nil {
|
|
found = true
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
return fmt.Errorf("the required flash image was not found in any of the expected paths for (%q), please install it with the package manager", flashImage.SourcePaths)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (check *preflightCheckContext) cniDirectories(ctx context.Context) error {
|
|
cniDirs := append(check.request.Network.CNI.BinPath, check.request.Network.CNI.CacheDir, check.request.Network.CNI.ConfDir)
|
|
|
|
for _, cniDir := range cniDirs {
|
|
st, err := os.Stat(cniDir)
|
|
if err != nil {
|
|
if !os.IsNotExist(err) {
|
|
return fmt.Errorf("error checking CNI directory %q: %w", cniDir, err)
|
|
}
|
|
|
|
fmt.Printf("creating %q\n", cniDir)
|
|
|
|
err = os.MkdirAll(cniDir, 0o777)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
if !st.IsDir() {
|
|
return fmt.Errorf("CNI path %q exists, but it's not a directory", cniDir)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (check *preflightCheckContext) cniBundle(ctx context.Context) error {
|
|
var missing bool
|
|
|
|
requiredCNIPlugins := []string{"bridge", "firewall", "static", "tc-redirect-tap"}
|
|
|
|
for _, cniPlugin := range requiredCNIPlugins {
|
|
missing = true
|
|
|
|
for _, binPath := range check.request.Network.CNI.BinPath {
|
|
_, err := os.Stat(filepath.Join(binPath, cniPlugin))
|
|
if err == nil {
|
|
missing = false
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
if missing {
|
|
break
|
|
}
|
|
}
|
|
|
|
if !missing {
|
|
return nil
|
|
}
|
|
|
|
if check.request.Network.CNI.BundleURL == "" {
|
|
return fmt.Errorf("error: required CNI plugins %q were not found in %q", requiredCNIPlugins, check.request.Network.CNI.BinPath)
|
|
}
|
|
|
|
pwd, err := os.Getwd()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
client := getter.Client{
|
|
Ctx: ctx,
|
|
Src: strings.ReplaceAll(check.request.Network.CNI.BundleURL, constants.ArchVariable, check.options.TargetArch),
|
|
Dst: check.request.Network.CNI.BinPath[0],
|
|
Pwd: pwd,
|
|
Mode: getter.ClientModeDir,
|
|
}
|
|
|
|
fmt.Printf("downloading CNI bundle from %q to %q\n", client.Src, client.Dst)
|
|
|
|
return client.Get()
|
|
}
|
|
|
|
func (check *preflightCheckContext) checkIptables(ctx context.Context) error {
|
|
_, err := iptables.New()
|
|
if err != nil {
|
|
return fmt.Errorf("error accessing iptables: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|