Matches ctl version to upstream
This commit is contained in:
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
type NodeContext struct {
|
||||
Config *monov1alpha1.MonoKSConfig
|
||||
System *system.Runner
|
||||
SystemRunner *system.Runner
|
||||
}
|
||||
|
||||
type Step func(context.Context, *NodeContext) error
|
||||
|
||||
@@ -2,21 +2,61 @@ package node
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
system "undecided.project/monok8s/pkg/system"
|
||||
)
|
||||
|
||||
func InstallCNIIfRequested(context.Context, *NodeContext) error {
|
||||
klog.Info("install_cni_if_requested: TODO implement bridge/none CNI toggling")
|
||||
func ConfigureDefaultCNI(ctx context.Context, n *NodeContext) error {
|
||||
_ = ctx
|
||||
|
||||
const (
|
||||
cniDir = "/etc/cni/net.d"
|
||||
enabledPath = cniDir + "/10-crio-bridge.conflist"
|
||||
disabledPath = cniDir + "/10-crio-bridge.conflist.disabled"
|
||||
)
|
||||
|
||||
plugin := strings.TrimSpace(n.Config.Spec.CNIPlugin)
|
||||
|
||||
switch plugin {
|
||||
case "none":
|
||||
// Fail hard if we cannot ensure the default bridge CNI is disabled.
|
||||
if _, err := os.Stat(enabledPath); err == nil {
|
||||
if err := os.Rename(enabledPath, disabledPath); err != nil {
|
||||
return fmt.Errorf("disable default CRI-O bridge CNI: %w", err)
|
||||
}
|
||||
} else if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("stat %s: %w", enabledPath, err)
|
||||
}
|
||||
|
||||
klog.Infof("Default CRI-O bridge CNI disabled")
|
||||
return nil
|
||||
|
||||
case "bridge":
|
||||
fallthrough
|
||||
case "default":
|
||||
// Fail soft. User can still install or provide their own CNI.
|
||||
if _, err := os.Stat(disabledPath); err == nil {
|
||||
if err := os.Rename(disabledPath, enabledPath); err != nil {
|
||||
klog.Warningf("failed enabling default CRI-O bridge CNI: %v", err)
|
||||
return nil
|
||||
}
|
||||
} else if !os.IsNotExist(err) {
|
||||
klog.Warningf("failed stating %s while enabling default CRI-O bridge CNI: %v", disabledPath, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
klog.Infof("Default CRI-O bridge CNI enabled")
|
||||
return nil
|
||||
|
||||
}
|
||||
klog.Infof("unsupported CNIPlugin: %q", plugin)
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartCRIO(context.Context, *NodeContext) error {
|
||||
klog.Info("start_crio: TODO implement rc-service crio start")
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckCRIORunning(context.Context, *NodeContext) error {
|
||||
klog.Info("check_crio_running: TODO implement crictl readiness checks")
|
||||
return nil
|
||||
func StartCRIO(ctx context.Context, n *NodeContext) error {
|
||||
return system.EnsureServiceRunning(ctx, n.SystemRunner, "crio")
|
||||
}
|
||||
|
||||
@@ -1,36 +1,87 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"context"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func WaitForExistingClusterIfNeeded(context.Context, *NodeContext) error {
|
||||
klog.Info("wait_for_existing_cluster_if_needed: TODO implement kubelet/admin.conf waits")
|
||||
return nil
|
||||
klog.Info("wait_for_existing_cluster_if_needed: TODO implement kubelet/admin.conf waits")
|
||||
return nil
|
||||
}
|
||||
func CheckRequiredImages(context.Context, *NodeContext) error {
|
||||
klog.Info("check_required_images: TODO implement kubeadm image list + crictl image presence")
|
||||
return nil
|
||||
|
||||
func CheckRequiredImages(ctx context.Context, n *NodeContext) error {
|
||||
if n.Config.Spec.SkipImageCheck {
|
||||
klog.Infof("skipping image check (skipImageCheck=true)")
|
||||
return nil
|
||||
}
|
||||
|
||||
k8sVersion := strings.TrimSpace(n.Config.Spec.KubernetesVersion)
|
||||
if k8sVersion == "" {
|
||||
return fmt.Errorf("kubernetesVersion is required")
|
||||
}
|
||||
|
||||
klog.Infof("checking required Kubernetes images for %s...", k8sVersion)
|
||||
|
||||
result, err := n.SystemRunner.Run(ctx,
|
||||
"kubeadm", "config", "images", "list",
|
||||
"--kubernetes-version", k8sVersion,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("list required Kubernetes images for %s: %w", k8sVersion, err)
|
||||
}
|
||||
|
||||
var missing []string
|
||||
for _, img := range strings.Fields(result.Stdout) {
|
||||
if err := checkImagePresent(ctx, n, img); err != nil {
|
||||
klog.Errorf("MISSING image: %s", img)
|
||||
missing = append(missing, img)
|
||||
continue
|
||||
}
|
||||
klog.Infof("found image: %s", img)
|
||||
}
|
||||
|
||||
if len(missing) > 0 {
|
||||
return fmt.Errorf("preload the Kubernetes images before bootstrapping; missing: %s", strings.Join(missing, ", "))
|
||||
}
|
||||
|
||||
klog.Infof("all required images are present")
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkImagePresent(ctx context.Context, n *NodeContext, image string) error {
|
||||
image = strings.TrimSpace(image)
|
||||
if image == "" {
|
||||
return fmt.Errorf("image is required")
|
||||
}
|
||||
|
||||
// crictl inspecti exits non-zero when the image is absent.
|
||||
_, err := n.SystemRunner.Run(ctx, "crictl", "inspecti", image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("image %q not present: %w", image, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func GenerateKubeadmConfig(context.Context, *NodeContext) error {
|
||||
klog.Info("generate_kubeadm_config: TODO render kubeadm v1beta4 config from MonoKSConfig")
|
||||
return nil
|
||||
klog.Info("generate_kubeadm_config: TODO render kubeadm v1beta4 config from MonoKSConfig")
|
||||
return nil
|
||||
}
|
||||
func RunKubeadmInit(context.Context, *NodeContext) error {
|
||||
klog.Info("run_kubeadm_init: TODO implement kubeadm init --config <file>")
|
||||
return nil
|
||||
klog.Info("run_kubeadm_init: TODO implement kubeadm init --config <file>")
|
||||
return nil
|
||||
}
|
||||
func RunKubeadmUpgradeApply(context.Context, *NodeContext) error {
|
||||
klog.Info("run_kubeadm_upgrade_apply: TODO implement kubeadm upgrade apply")
|
||||
return nil
|
||||
klog.Info("run_kubeadm_upgrade_apply: TODO implement kubeadm upgrade apply")
|
||||
return nil
|
||||
}
|
||||
func RunKubeadmJoin(context.Context, *NodeContext) error {
|
||||
klog.Info("run_kubeadm_join: TODO implement kubeadm join")
|
||||
return nil
|
||||
klog.Info("run_kubeadm_join: TODO implement kubeadm join")
|
||||
return nil
|
||||
}
|
||||
func RunKubeadmUpgradeNode(context.Context, *NodeContext) error {
|
||||
klog.Info("run_kubeadm_upgrade_node: TODO implement kubeadm upgrade node")
|
||||
return nil
|
||||
klog.Info("run_kubeadm_upgrade_node: TODO implement kubeadm upgrade node")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
type NetworkConfig struct {
|
||||
Hostname string
|
||||
MgmtIface string
|
||||
MgmtAddress string
|
||||
MgmtGateway string
|
||||
@@ -48,11 +49,11 @@ func ConfigureMgmtInterface(cfg NetworkConfig) Step {
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := nctx.System.Run(ctx, "ip", "link", "show", "dev", cfg.MgmtIface); err != nil {
|
||||
if _, err := nctx.SystemRunner.Run(ctx, "ip", "link", "show", "dev", cfg.MgmtIface); err != nil {
|
||||
return fmt.Errorf("interface not found: %s: %w", cfg.MgmtIface, err)
|
||||
}
|
||||
|
||||
if _, err := nctx.System.Run(ctx, "ip", "link", "set", "dev", cfg.MgmtIface, "up"); err != nil {
|
||||
if _, err := nctx.SystemRunner.Run(ctx, "ip", "link", "set", "dev", cfg.MgmtIface, "up"); err != nil {
|
||||
return fmt.Errorf("failed to bring up interface %s: %w", cfg.MgmtIface, err)
|
||||
}
|
||||
|
||||
@@ -64,13 +65,13 @@ func ConfigureMgmtInterface(cfg NetworkConfig) Step {
|
||||
if hasAddr {
|
||||
klog.Infof("address already present on %s: %s", cfg.MgmtIface, wantCIDR)
|
||||
} else {
|
||||
if _, err := nctx.System.Run(ctx, "ip", "addr", "add", wantCIDR, "dev", cfg.MgmtIface); err != nil {
|
||||
if _, err := nctx.SystemRunner.Run(ctx, "ip", "addr", "add", wantCIDR, "dev", cfg.MgmtIface); err != nil {
|
||||
return fmt.Errorf("failed assigning %s to %s: %w", wantCIDR, cfg.MgmtIface, err)
|
||||
}
|
||||
}
|
||||
|
||||
if gw := strings.TrimSpace(cfg.MgmtGateway); gw != "" {
|
||||
if _, err := nctx.System.Run(ctx, "ip", "route", "replace", "default", "via", gw, "dev", cfg.MgmtIface); err != nil {
|
||||
if _, err := nctx.SystemRunner.Run(ctx, "ip", "route", "replace", "default", "via", gw, "dev", cfg.MgmtIface); err != nil {
|
||||
return fmt.Errorf("failed setting default route via %s dev %s: %w", gw, cfg.MgmtIface, err)
|
||||
}
|
||||
}
|
||||
@@ -85,7 +86,44 @@ func maskSize(m net.IPMask) int {
|
||||
}
|
||||
|
||||
func EnsureIPForward(ctx context.Context, n *NodeContext) error {
|
||||
return system.EnsureSysctl(ctx, n.System, "net.ipv4.ip_forward", "1")
|
||||
return system.EnsureSysctl(ctx, n.SystemRunner, "net.ipv4.ip_forward", "1")
|
||||
}
|
||||
|
||||
func ConfigureHostname(cfg NetworkConfig) Step {
|
||||
return func(context.Context, *NodeContext) error {
|
||||
want := strings.TrimSpace(cfg.Hostname)
|
||||
if want == "" {
|
||||
return fmt.Errorf("hostname is required")
|
||||
}
|
||||
|
||||
current, err := os.Hostname()
|
||||
if err != nil {
|
||||
current = ""
|
||||
}
|
||||
|
||||
if current == want {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := system.SetHostname(want); err != nil {
|
||||
return fmt.Errorf("set hostname to %q: %w", want, err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile("/etc/hostname", []byte(want+"\n"), 0o644); err != nil {
|
||||
return fmt.Errorf("write /etc/hostname: %w", err)
|
||||
}
|
||||
|
||||
current, err = os.Hostname()
|
||||
if err != nil {
|
||||
current = ""
|
||||
}
|
||||
|
||||
if current != want {
|
||||
return fmt.Errorf("Unable to set hostname: %q", want)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func ConfigureDNS(cfg NetworkConfig) Step {
|
||||
@@ -159,7 +197,7 @@ func ConfigureDNS(cfg NetworkConfig) Step {
|
||||
}
|
||||
|
||||
func interfaceHasIPv4(ctx context.Context, nctx *NodeContext, iface, wantIP string) (bool, error) {
|
||||
res, err := nctx.System.Run(ctx, "ip", "-o", "-4", "addr", "show", "dev", iface)
|
||||
res, err := nctx.SystemRunner.Run(ctx, "ip", "-o", "-4", "addr", "show", "dev", iface)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -1,27 +1,112 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"context"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func CheckPrereqs(context.Context, *NodeContext) error {
|
||||
klog.Info("check_prereqs: TODO implement command discovery and runtime validation")
|
||||
return nil
|
||||
}
|
||||
func ValidateNetworkRequirements(ctx context.Context, nct *NodeContext) error {
|
||||
requireLocalIP := func(wantedIP string) error {
|
||||
wantedIP = strings.TrimSpace(wantedIP)
|
||||
if wantedIP == "" {
|
||||
return fmt.Errorf("API server advertise address is required")
|
||||
}
|
||||
|
||||
func ValidateNetworkRequirements(context.Context, *NodeContext) error {
|
||||
klog.Info("validate_network_requirements: TODO implement local IP and API reachability checks")
|
||||
return nil
|
||||
ip := net.ParseIP(wantedIP)
|
||||
if ip == nil {
|
||||
return fmt.Errorf("invalid API server advertise address %q", wantedIP)
|
||||
}
|
||||
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return fmt.Errorf("list interfaces: %w", err)
|
||||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
var got net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
got = v.IP
|
||||
case *net.IPAddr:
|
||||
got = v.IP
|
||||
}
|
||||
if got != nil && got.Equal(ip) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("required local IP is not present on any interface: %s", wantedIP)
|
||||
}
|
||||
|
||||
checkAPIServerReachable := func(endpoint string) error {
|
||||
endpoint = strings.TrimSpace(endpoint)
|
||||
if endpoint == "" {
|
||||
return fmt.Errorf("API server endpoint is required")
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(endpoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid API server endpoint %q: %w", endpoint, err)
|
||||
}
|
||||
if strings.TrimSpace(host) == "" || strings.TrimSpace(port) == "" {
|
||||
return fmt.Errorf("invalid API server endpoint %q", endpoint)
|
||||
}
|
||||
|
||||
klog.Infof("checking API server reachability: %s:%s", host, port)
|
||||
|
||||
var lastErr error
|
||||
for i := 0; i < 20; i++ {
|
||||
d := net.Dialer{Timeout: 1 * time.Second}
|
||||
conn, err := d.DialContext(ctx, "tcp", endpoint)
|
||||
if err == nil {
|
||||
_ = conn.Close()
|
||||
klog.Infof("API server is reachable")
|
||||
return nil
|
||||
}
|
||||
lastErr = err
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-time.After(1 * time.Second):
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot reach API server at %s: %w", endpoint, lastErr)
|
||||
}
|
||||
|
||||
cfg := nct.Config.Spec
|
||||
switch strings.TrimSpace(cfg.ClusterRole) {
|
||||
case "control-plane":
|
||||
if err := requireLocalIP(cfg.APIServerAdvertiseAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
case "worker":
|
||||
if err := requireLocalIP(cfg.APIServerAdvertiseAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkAPIServerReachable(cfg.APIServerEndpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("Incorrect ClusterRole: %s", cfg.ClusterRole)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckUpgradePrereqs(context.Context, *NodeContext) error {
|
||||
klog.Info("check_upgrade_prereqs: TODO implement kubeadm version / skew checks")
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecideBootstrapAction(_ context.Context, nctx *NodeContext) error {
|
||||
klog.InfoS("decide_bootstrap_action", "bootstrapMode", nctx.Config.Spec.BootstrapMode, "joinKind", nctx.Config.Spec.JoinKind)
|
||||
return nil
|
||||
klog.Info("check_upgrade_prereqs: TODO implement kubeadm version / skew checks")
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user