Matches ctl version to upstream
This commit is contained in:
@@ -20,8 +20,8 @@ var (
|
||||
type MonoKSConfig struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
Spec MonoKSConfigSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
|
||||
Status MonoKSConfigStatus `json:"status,omitempty" yaml:"status,omitempty"`
|
||||
Spec MonoKSConfigSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
|
||||
Status *MonoKSConfigStatus `json:"status,omitempty" yaml:"status,omitempty"`
|
||||
}
|
||||
|
||||
type MonoKSConfigList struct {
|
||||
@@ -35,13 +35,13 @@ type MonoKSConfigSpec struct {
|
||||
NodeName string `json:"nodeName,omitempty" yaml:"nodeName,omitempty"`
|
||||
ClusterName string `json:"clusterName,omitempty" yaml:"clusterName,omitempty"`
|
||||
ClusterDomain string `json:"clusterDomain,omitempty" yaml:"clusterDomain,omitempty"`
|
||||
ClusterRole string `json:"clusterRole,omitempty" yaml:"clusterRole,omitempty"`
|
||||
InitControlPlane bool `json:"initControlPlane,omitempty" yaml:"initControlPlane,omitempty"`
|
||||
PodSubnet string `json:"podSubnet,omitempty" yaml:"podSubnet,omitempty"`
|
||||
ServiceSubnet string `json:"serviceSubnet,omitempty" yaml:"serviceSubnet,omitempty"`
|
||||
APIServerAdvertiseAddress string `json:"apiServerAdvertiseAddress,omitempty" yaml:"apiServerAdvertiseAddress,omitempty"`
|
||||
APIServerEndpoint string `json:"apiServerEndpoint,omitempty" yaml:"apiServerEndpoint,omitempty"`
|
||||
ContainerRuntimeEndpoint string `json:"containerRuntimeEndpoint,omitempty" yaml:"containerRuntimeEndpoint,omitempty"`
|
||||
BootstrapMode string `json:"bootstrapMode,omitempty" yaml:"bootstrapMode,omitempty"`
|
||||
JoinKind string `json:"joinKind,omitempty" yaml:"joinKind,omitempty"`
|
||||
BootstrapToken string `json:"bootstrapToken,omitempty" yaml:"bootstrapToken,omitempty"`
|
||||
DiscoveryTokenCACertHash string `json:"discoveryTokenCACertHash,omitempty" yaml:"discoveryTokenCACertHash,omitempty"`
|
||||
ControlPlaneCertKey string `json:"controlPlaneCertKey,omitempty" yaml:"controlPlaneCertKey,omitempty"`
|
||||
@@ -74,8 +74,8 @@ type MonoKSConfigStatus struct {
|
||||
type OSUpgrade struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
Spec OSUpgradeSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
|
||||
Status OSUpgradeStatus `json:"status,omitempty" yaml:"status,omitempty"`
|
||||
Spec OSUpgradeSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
|
||||
Status *OSUpgradeStatus `json:"status,omitempty" yaml:"status,omitempty"`
|
||||
}
|
||||
|
||||
type OSUpgradeList struct {
|
||||
|
||||
@@ -12,6 +12,7 @@ type Registry struct {
|
||||
|
||||
func NewRegistry(ctx *node.NodeContext) *Registry {
|
||||
netCfg := node.NetworkConfig{
|
||||
Hostname: ctx.Config.Spec.Network.Hostname,
|
||||
MgmtIface: ctx.Config.Spec.Network.ManagementIface,
|
||||
MgmtAddress: ctx.Config.Spec.Network.ManagementCIDR,
|
||||
MgmtGateway: ctx.Config.Spec.Network.ManagementGW,
|
||||
@@ -21,13 +22,10 @@ func NewRegistry(ctx *node.NodeContext) *Registry {
|
||||
|
||||
return &Registry{
|
||||
steps: map[string]node.Step{
|
||||
"check_prereqs": node.CheckPrereqs,
|
||||
"validate_network_requirements": node.ValidateNetworkRequirements,
|
||||
"install_cni_if_requested": node.InstallCNIIfRequested,
|
||||
"configure_default_cni": node.ConfigureDefaultCNI,
|
||||
"start_crio": node.StartCRIO,
|
||||
"check_crio_running": node.CheckCRIORunning,
|
||||
"wait_for_existing_cluster_if_needed": node.WaitForExistingClusterIfNeeded,
|
||||
"decide_bootstrap_action": node.DecideBootstrapAction,
|
||||
"check_required_images": node.CheckRequiredImages,
|
||||
"generate_kubeadm_config": node.GenerateKubeadmConfig,
|
||||
"run_kubeadm_init": node.RunKubeadmInit,
|
||||
@@ -35,6 +33,7 @@ func NewRegistry(ctx *node.NodeContext) *Registry {
|
||||
"apply_local_node_metadata_if_possible": node.ApplyLocalNodeMetadataIfPossible,
|
||||
"allow_single_node_scheduling": node.AllowSingleNodeScheduling,
|
||||
"ensure_ip_forward": node.EnsureIPForward,
|
||||
"configure_hostname": node.ConfigureHostname(netCfg),
|
||||
"configure_mgmt_interface": node.ConfigureMgmtInterface(netCfg),
|
||||
"configure_dns": node.ConfigureDNS(netCfg),
|
||||
"set_hostname_if_needed": node.SetHostnameIfNeeded,
|
||||
|
||||
@@ -16,8 +16,8 @@ type Runner struct {
|
||||
func NewRunner(cfg *monov1alpha1.MonoKSConfig) *Runner {
|
||||
runnerCfg := system.RunnerConfig{}
|
||||
nctx := &node.NodeContext{
|
||||
Config: cfg,
|
||||
System: system.NewRunner(runnerCfg),
|
||||
Config: cfg,
|
||||
SystemRunner: system.NewRunner(runnerCfg),
|
||||
}
|
||||
return &Runner{
|
||||
NodeCtx: nctx,
|
||||
@@ -27,11 +27,13 @@ func NewRunner(cfg *monov1alpha1.MonoKSConfig) *Runner {
|
||||
|
||||
func (r *Runner) Init(ctx context.Context) error {
|
||||
for _, name := range []string{
|
||||
"check_prereqs",
|
||||
"configure_hostname",
|
||||
"configure_dns",
|
||||
"configure_mgmt_interface",
|
||||
"validate_network_requirements",
|
||||
"install_cni_if_requested",
|
||||
"configure_default_cni",
|
||||
"start_crio",
|
||||
"check_crio_running",
|
||||
"check_container_images",
|
||||
"wait_for_existing_cluster_if_needed",
|
||||
"decide_bootstrap_action",
|
||||
"check_required_images",
|
||||
|
||||
1
clitools/pkg/buildinfo/README
Normal file
1
clitools/pkg/buildinfo/README
Normal file
@@ -0,0 +1 @@
|
||||
Use `make build` to generate the files. Do not modify.
|
||||
@@ -2,9 +2,9 @@ package create
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"undecided.project/monok8s/pkg/templates"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
render "undecided.project/monok8s/pkg/render"
|
||||
)
|
||||
|
||||
func NewCmdCreate() *cobra.Command {
|
||||
@@ -14,7 +14,11 @@ func NewCmdCreate() *cobra.Command {
|
||||
Use: "config",
|
||||
Short: "Print a MonoKSConfig template",
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
_, err := fmt.Fprint(cmd.OutOrStdout(), templates.MonoKSConfigYAML)
|
||||
out, err := render.RenderMonoKSConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fmt.Fprint(cmd.OutOrStdout(), out)
|
||||
return err
|
||||
},
|
||||
},
|
||||
@@ -22,7 +26,11 @@ func NewCmdCreate() *cobra.Command {
|
||||
Use: "osupgrade",
|
||||
Short: "Print an OSUpgrade template",
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
_, err := fmt.Fprint(cmd.OutOrStdout(), templates.OSUpgradeYAML)
|
||||
out, err := render.RenderOSUpgrade()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fmt.Fprint(cmd.OutOrStdout(), out)
|
||||
return err
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,15 +3,16 @@ package root
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/klog/v2"
|
||||
agentcmd "undecided.project/monok8s/pkg/cmd/agent"
|
||||
applycmd "undecided.project/monok8s/pkg/cmd/apply"
|
||||
checkconfigcmd "undecided.project/monok8s/pkg/cmd/checkconfig"
|
||||
createcmd "undecided.project/monok8s/pkg/cmd/create"
|
||||
initcmd "undecided.project/monok8s/pkg/cmd/initcmd"
|
||||
internalcmd "undecided.project/monok8s/pkg/cmd/internal"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/klog/v2"
|
||||
versioncmd "undecided.project/monok8s/pkg/cmd/version"
|
||||
)
|
||||
|
||||
func NewRootCmd() *cobra.Command {
|
||||
@@ -30,6 +31,7 @@ func NewRootCmd() *cobra.Command {
|
||||
|
||||
flags.AddFlags(cmd.PersistentFlags())
|
||||
cmd.AddCommand(
|
||||
versioncmd.NewCmdVersion(),
|
||||
initcmd.NewCmdInit(flags),
|
||||
checkconfigcmd.NewCmdCheckConfig(),
|
||||
createcmd.NewCmdCreate(),
|
||||
|
||||
22
clitools/pkg/cmd/version/version.go
Normal file
22
clitools/pkg/cmd/version/version.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package apply
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
buildInfo "undecided.project/monok8s/pkg/buildinfo"
|
||||
)
|
||||
|
||||
func NewCmdVersion() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the version information",
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
|
||||
_, err := fmt.Fprintln(cmd.OutOrStdout(), buildInfo.Version)
|
||||
return err
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
monov1alpha1 "undecided.project/monok8s/pkg/apis/monok8s/v1alpha1"
|
||||
"gopkg.in/yaml.v3"
|
||||
monov1alpha1 "undecided.project/monok8s/pkg/apis/monok8s/v1alpha1"
|
||||
)
|
||||
|
||||
const EnvVar = "MONOKSCONFIG"
|
||||
@@ -62,11 +62,8 @@ func ApplyDefaults(cfg *monov1alpha1.MonoKSConfig) {
|
||||
if cfg.Spec.ContainerRuntimeEndpoint == "" {
|
||||
cfg.Spec.ContainerRuntimeEndpoint = "unix:///var/run/crio/crio.sock"
|
||||
}
|
||||
if cfg.Spec.BootstrapMode == "" {
|
||||
cfg.Spec.BootstrapMode = "init"
|
||||
}
|
||||
if cfg.Spec.JoinKind == "" {
|
||||
cfg.Spec.JoinKind = "worker"
|
||||
if cfg.Spec.ClusterRole == "" {
|
||||
cfg.Spec.ClusterRole = "control-plane"
|
||||
}
|
||||
if cfg.Spec.CNIPlugin == "" {
|
||||
cfg.Spec.CNIPlugin = "none"
|
||||
@@ -102,30 +99,26 @@ func Validate(cfg *monov1alpha1.MonoKSConfig) error {
|
||||
if !strings.Contains(cfg.Spec.Network.ManagementCIDR, "/") {
|
||||
problems = append(problems, "spec.network.managementCIDR must include a CIDR prefix")
|
||||
}
|
||||
if cfg.Spec.BootstrapMode != "init" && cfg.Spec.BootstrapMode != "join" {
|
||||
problems = append(problems, "spec.bootstrapMode must be init or join")
|
||||
}
|
||||
if cfg.Spec.JoinKind != "worker" && cfg.Spec.JoinKind != "control-plane" {
|
||||
problems = append(problems, "spec.joinKind must be worker or control-plane")
|
||||
if cfg.Spec.ClusterRole != "control-plane" && cfg.Spec.ClusterRole != "worker" {
|
||||
problems = append(problems, "spec.clusterRole can either be control-plane or worker")
|
||||
}
|
||||
for _, ns := range cfg.Spec.Network.DNSNameservers {
|
||||
if ns == "10.96.0.10" {
|
||||
problems = append(problems, "spec.network.dnsNameservers must not include cluster DNS service IP 10.96.0.10")
|
||||
}
|
||||
}
|
||||
if cfg.Spec.BootstrapMode == "join" {
|
||||
if cfg.Spec.ClusterRole == "worker" {
|
||||
if cfg.Spec.APIServerEndpoint == "" {
|
||||
problems = append(problems, "spec.apiServerEndpoint is required for join mode")
|
||||
problems = append(problems, "spec.apiServerEndpoint is required to join a cluster")
|
||||
}
|
||||
if cfg.Spec.BootstrapToken == "" {
|
||||
problems = append(problems, "spec.bootstrapToken is required for join mode")
|
||||
problems = append(problems, "spec.bootstrapToken is required to join a cluster")
|
||||
}
|
||||
if cfg.Spec.DiscoveryTokenCACertHash == "" {
|
||||
problems = append(problems, "spec.discoveryTokenCACertHash is required for join mode")
|
||||
}
|
||||
if cfg.Spec.JoinKind == "control-plane" && cfg.Spec.ControlPlaneCertKey == "" {
|
||||
problems = append(problems, "spec.controlPlaneCertKey is required for control-plane join")
|
||||
problems = append(problems, "spec.discoveryTokenCACertHash is required to join a cluster")
|
||||
}
|
||||
} else if !cfg.Spec.InitControlPlane && cfg.Spec.ControlPlaneCertKey == "" {
|
||||
problems = append(problems, "spec.controlPlaneCertKey is required for control-plane join")
|
||||
}
|
||||
if len(problems) > 0 {
|
||||
return errors.New(strings.Join(problems, "; "))
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
55
clitools/pkg/render/monoks.go
Normal file
55
clitools/pkg/render/monoks.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/json"
|
||||
|
||||
"undecided.project/monok8s/pkg/scheme"
|
||||
tmpl "undecided.project/monok8s/pkg/templates"
|
||||
)
|
||||
|
||||
func RenderMonoKSConfig() (string, error) {
|
||||
cfg := tmpl.DefaultMonoKSConfig()
|
||||
|
||||
s := runtime.NewScheme()
|
||||
if err := scheme.AddToScheme(s); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
serializer := json.NewYAMLSerializer(
|
||||
json.DefaultMetaFactory,
|
||||
s,
|
||||
s,
|
||||
)
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := serializer.Encode(&cfg, &buf); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func RenderOSUpgrade() (string, error) {
|
||||
cfg := tmpl.DefaultOSUpgrade()
|
||||
|
||||
s := runtime.NewScheme()
|
||||
if err := scheme.AddToScheme(s); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
serializer := json.NewYAMLSerializer(
|
||||
json.DefaultMetaFactory,
|
||||
s,
|
||||
s,
|
||||
)
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := serializer.Encode(&cfg, &buf); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
27
clitools/pkg/scheme/v1alpha1.go
Normal file
27
clitools/pkg/scheme/v1alpha1.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package scheme
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
types "undecided.project/monok8s/pkg/apis/monok8s/v1alpha1"
|
||||
)
|
||||
|
||||
var (
|
||||
GroupVersion = schema.GroupVersion{
|
||||
Group: "monok8s.io",
|
||||
Version: "v1alpha1",
|
||||
}
|
||||
)
|
||||
|
||||
func AddToScheme(s *runtime.Scheme) error {
|
||||
s.AddKnownTypes(GroupVersion,
|
||||
&types.MonoKSConfig{},
|
||||
)
|
||||
|
||||
// Required for meta stuff
|
||||
metav1.AddToGroupVersion(s, GroupVersion)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -5,15 +5,19 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
const DefaultSecond = 1_000_000_000
|
||||
|
||||
func EnsureServiceRunning(ctx context.Context, r *Runner, svc string) error {
|
||||
|
||||
if _, err := r.Run(ctx, " rc-service", svc, "status"); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
klog.Infof("Starting service: %q", svc)
|
||||
_, err := r.RunRetry(ctx, RetryOptions{
|
||||
Attempts: 3,
|
||||
Delay: 2 * DefaultSecond,
|
||||
|
||||
12
clitools/pkg/system/hostname_linux.go
Normal file
12
clitools/pkg/system/hostname_linux.go
Normal file
@@ -0,0 +1,12 @@
|
||||
//go:build linux
|
||||
|
||||
package system
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
func SetHostname(hostname string) error {
|
||||
if hostname == "" {
|
||||
return nil
|
||||
}
|
||||
return unix.Sethostname([]byte(hostname))
|
||||
}
|
||||
8
clitools/pkg/system/hostname_stub.go
Normal file
8
clitools/pkg/system/hostname_stub.go
Normal file
@@ -0,0 +1,8 @@
|
||||
//go:build !linux
|
||||
|
||||
package system
|
||||
|
||||
func SetHostname(hostname string) error {
|
||||
// intentionally a no-op
|
||||
return nil
|
||||
}
|
||||
@@ -1,54 +1,93 @@
|
||||
package templates
|
||||
|
||||
const MonoKSConfigYAML = `apiVersion: monok8s.io/v1alpha1
|
||||
kind: MonoKSConfig
|
||||
metadata:
|
||||
name: example
|
||||
namespace: kube-system
|
||||
spec:
|
||||
kubernetesVersion: v1.35.3
|
||||
nodeName: monok8s-master-1
|
||||
clusterName: monok8s
|
||||
clusterDomain: cluster.local
|
||||
podSubnet: 10.244.0.0/16
|
||||
serviceSubnet: 10.96.0.0/12
|
||||
apiServerAdvertiseAddress: 10.0.0.10
|
||||
apiServerEndpoint: 10.0.0.10:6443
|
||||
containerRuntimeEndpoint: unix:///var/run/crio/crio.sock
|
||||
bootstrapMode: init
|
||||
joinKind: worker
|
||||
cniPlugin: none
|
||||
allowSchedulingOnControlPlane: true
|
||||
skipImageCheck: false
|
||||
kubeProxyNodePortAddresses:
|
||||
- primary
|
||||
subjectAltNames:
|
||||
- 10.0.0.10
|
||||
nodeLabels:
|
||||
node-role.kubernetes.io/control-plane: ""
|
||||
nodeAnnotations: {}
|
||||
network:
|
||||
hostname: monok8s-master-1
|
||||
managementIface: eth0
|
||||
managementCIDR: 10.0.0.10/24
|
||||
managementGateway: 10.0.0.1
|
||||
dnsNameservers:
|
||||
- 1.1.1.1
|
||||
- 8.8.8.8
|
||||
dnsSearchDomains:
|
||||
- lan
|
||||
`
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
types "undecided.project/monok8s/pkg/apis/monok8s/v1alpha1"
|
||||
buildinfo "undecided.project/monok8s/pkg/buildinfo"
|
||||
)
|
||||
|
||||
const OSUpgradeYAML = `apiVersion: monok8s.io/v1alpha1
|
||||
kind: OSUpgrade
|
||||
metadata:
|
||||
name: example
|
||||
namespace: kube-system
|
||||
spec:
|
||||
version: v0.0.1
|
||||
imageURL: https://example.invalid/images/monok8s-v0.0.1.img.zst
|
||||
targetPartition: B
|
||||
nodeSelector:
|
||||
- monok8s-master-1
|
||||
force: false
|
||||
`
|
||||
func DefaultMonoKSConfig() types.MonoKSConfig {
|
||||
return types.MonoKSConfig{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "monok8s.io/v1alpha1",
|
||||
Kind: "MonoKSConfig",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "example",
|
||||
Namespace: "kube-system",
|
||||
},
|
||||
Spec: types.MonoKSConfigSpec{
|
||||
KubernetesVersion: buildinfo.Version,
|
||||
NodeName: "monok8s-master-1",
|
||||
|
||||
ClusterRole: "control-plane",
|
||||
InitControlPlane: true,
|
||||
|
||||
ClusterName: "monok8s",
|
||||
ClusterDomain: "cluster.local",
|
||||
|
||||
PodSubnet: "10.244.0.0/16",
|
||||
ServiceSubnet: "10.96.0.0/12",
|
||||
|
||||
APIServerAdvertiseAddress: "10.0.0.10",
|
||||
APIServerEndpoint: "10.0.0.10:6443",
|
||||
|
||||
ContainerRuntimeEndpoint: "unix:///var/run/crio/crio.sock",
|
||||
|
||||
CNIPlugin: "default",
|
||||
|
||||
AllowSchedulingOnControlPlane: true,
|
||||
SkipImageCheck: false,
|
||||
|
||||
KubeProxyNodePortAddresses: []string{
|
||||
"primary",
|
||||
},
|
||||
|
||||
SubjectAltNames: []string{
|
||||
"10.0.0.10",
|
||||
},
|
||||
|
||||
NodeLabels: map[string]string{
|
||||
"node-role.kubernetes.io/control-plane": "",
|
||||
},
|
||||
|
||||
NodeAnnotations: map[string]string{},
|
||||
|
||||
Network: types.NetworkSpec{
|
||||
Hostname: "monok8s-master-1",
|
||||
ManagementIface: "eth0",
|
||||
ManagementCIDR: "10.0.0.10/24",
|
||||
ManagementGW: "10.0.0.1",
|
||||
DNSNameservers: []string{
|
||||
"1.1.1.1",
|
||||
"8.8.8.8",
|
||||
},
|
||||
DNSSearchDomains: []string{
|
||||
"lan",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultOSUpgrade() types.OSUpgrade {
|
||||
return types.OSUpgrade{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "monok8s.io/v1alpha1",
|
||||
Kind: "OSUpgrade",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "example",
|
||||
Namespace: "kube-system",
|
||||
},
|
||||
Spec: types.OSUpgradeSpec{
|
||||
Version: "v0.0.1",
|
||||
ImageURL: "https://example.invalid/images/monok8s-v0.0.1.img.zst",
|
||||
TargetPartition: "B",
|
||||
NodeSelector: []string{
|
||||
"monok8s-master-1",
|
||||
},
|
||||
Force: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user