Skip to content

Commit

Permalink
Produce proper standard images with k3s/k0s versions
Browse files Browse the repository at this point in the history
USes a provider flag and a defualt empty version flag to provide the
specific version that you want.

It goes directly to k0s installer and generates the service files
itself so it doesnt need to run anything

For k3s it uses the installers with some flags to generate the services
directly.

Signed-off-by: Itxaka <[email protected]>
  • Loading branch information
Itxaka committed Feb 3, 2025
1 parent a04d4ff commit 8786063
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 39 deletions.
11 changes: 8 additions & 3 deletions Dockerfile.test
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
ARG BASE_IMAGE=ubuntu:24.04
ARG VALIDATE_IMAGE=ubuntu:24.04


FROM golang AS build
WORKDIR /app
COPY go.mod go.sum .
Expand All @@ -19,9 +20,13 @@ COPY --from=build /app/kairos-init /kairos-init
RUN /kairos-init -l debug --validate

FROM ${BASE_IMAGE}
ARG VARIANT=core
ARG MODEL=generic
ARG TRUSTED_BOOT=false
ARG K3S_PROVIDER=k3s

COPY --from=kairos-init /kairos-init /kairos-init
RUN /kairos-init -l debug -s install
RUN /kairos-init -l debug -s init
RUN /kairos-init -l debug --validate
RUN /kairos-init -l debug -s install -m "${MODEL}" -v "${VARIANT}" -t "${TRUSTED_BOOT}" -k "${K3S_PROVIDER}"
RUN /kairos-init -l debug -s init -m "${MODEL}" -v "${VARIANT}" -t "${TRUSTED_BOOT}" -k "${K3S_PROVIDER}"
RUN /kairos-init -l debug --validate -m "${MODEL}" -v "${VARIANT}" -t "${TRUSTED_BOOT}" -k "${K3S_PROVIDER}"
RUN rm /kairos-init
32 changes: 30 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ import (
func main() {
var trusted string
var validate bool
var variant string
var ksProvider string

flag.StringVar(&config.DefaultConfig.Level, "l", "info", "set the log level")
flag.StringVar(&config.DefaultConfig.Stage, "s", "all", "set the stage to run")
flag.StringVar(&config.DefaultConfig.Model, "m", "generic", "model to build for, like generic or rpi4")
flag.StringVar(&config.DefaultConfig.Variant, "v", "core", "variant to build (core or standard for k3s flavor) (shorthand: -v)")
flag.StringVar(&variant, "v", "core", "variant to build (core or standard for k3s flavor) (shorthand: -v)")
flag.StringVar(&ksProvider, "k", "k3s", "Kubernetes provider (shorthand: -k)")
flag.StringVar(&config.DefaultConfig.Registry, "r", "quay.io/kairos", "registry and org where the image is gonna be pushed. This is mainly used on upgrades to search for available images to upgrade to")
flag.StringVar(&trusted, "t", "false", "init the system for Trusted Boot, changes bootloader to systemd")
flag.StringVar(&config.DefaultConfig.FrameworkVersion, "f", values.GetFrameworkVersion(), "set the framework version to use")
Expand Down Expand Up @@ -50,19 +54,43 @@ func main() {
os.Exit(0)
}

if variant == "" {
// Set default variant
config.DefaultConfig.Variant = config.CoreVariant
} else {
// Try to load the variant
err := config.DefaultConfig.Variant.FromString(variant)
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
os.Exit(1)
}
}

if ksProvider == "" {
// Set default variant
config.DefaultConfig.KubernetesProvider = config.K3sProvider
} else {
// Try to load the variant
err := config.DefaultConfig.KubernetesProvider.FromString(ksProvider)
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
os.Exit(1)
}
}

logger := types.NewKairosLogger("kairos-init", config.DefaultConfig.Level, false)
logger.Infof("Starting kairos-init version %s", values.GetVersion())
logger.Debug(litter.Sdump(values.GetFullVersion()))

// Validate flags are being passed with actual values
// We dont care about variant and provider as we are setting that to a default value if value passed is empty
requiredFlags := []struct {
name string
value string
}{
{"l", config.DefaultConfig.Level},
{"s", config.DefaultConfig.Stage},
{"m", config.DefaultConfig.Model},
{"v", config.DefaultConfig.Variant},
{"r", config.DefaultConfig.Registry},
{"t", trusted},
{"f", config.DefaultConfig.FrameworkVersion},
Expand Down
62 changes: 54 additions & 8 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,62 @@
package config

import "fmt"

// Config is the struct to track the config of the init image
// So we can access it from anywhere
type Config struct {
Level string
Stage string
Model string
FrameworkVersion string
Variant string
Registry string
TrustedBoot bool
Fips bool
Level string
Stage string
Model string
FrameworkVersion string
Variant Variant
Registry string
TrustedBoot bool
Fips bool
KubernetesProvider KubernetesProvider
KubernetesVersion string
}

var DefaultConfig = Config{}

type Variant string

func (v Variant) Equal(s string) bool {
return string(v) == s
}

func (v Variant) String() string {
return string(v)
}

func (v *Variant) FromString(variant string) error {
*v = Variant(variant)
switch *v {
case CoreVariant, StandardVariant:
return nil
default:
return fmt.Errorf("invalid variant: %s, possible values are %s", variant, ValidVariants)
}
}

const CoreVariant Variant = "core"
const StandardVariant Variant = "standard"

var ValidVariants = []Variant{CoreVariant, StandardVariant}

type KubernetesProvider string

func (v *KubernetesProvider) FromString(provider string) error {
*v = KubernetesProvider(provider)
switch *v {
case K3sProvider, K0sProvider:
return nil
default:
return fmt.Errorf("invalid Kubernetes provider: %s, possible values are %s", provider, ValidProviders)
}
}

const K3sProvider KubernetesProvider = "k3s"
const K0sProvider KubernetesProvider = "k0s"

var ValidProviders = []KubernetesProvider{K3sProvider, K0sProvider}
199 changes: 178 additions & 21 deletions pkg/stages/stages.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func GetKairosReleaseStage(sis values.System, log types.KairosLogger) []schema.S
"KAIROS_FLAVOR_RELEASE": flavorRelease,
"KAIROS_FAMILY": sis.Family.String(),
"KAIROS_MODEL": config.DefaultConfig.Model, // NEEDED or it breaks boot!
"KAIROS_VARIANT": config.DefaultConfig.Variant,
"KAIROS_VARIANT": config.DefaultConfig.Variant.String(),
"KAIROS_REGISTRY_AND_ORG": config.DefaultConfig.Registry, // Needed for upgrades to search for images
"KAIROS_BUG_REPORT_URL": "https://github.com/kairos-io/kairos/issues",
"KAIROS_HOME_URL": "https://github.com/kairos-io/kairos",
Expand Down Expand Up @@ -420,6 +420,182 @@ func GetInstallFrameworkStage(_ values.System, _ types.KairosLogger) []schema.St
}
}

// GetInstallProviderAndKubernetes will install the provider and kubernetes packages
func GetInstallProviderAndKubernetes(_ values.System, _ types.KairosLogger) []schema.Stage {
var data []schema.Stage

// If its core we dont do anything here
if config.DefaultConfig.Variant.String() == "core" {
return data
}

data = append(data, []schema.Stage{
{
Name: "Install Provider packages",
UnpackImages: []schema.UnpackImageConf{
{
Source: values.GetProviderPackage(),
Target: "/",
},
},
},
}...)

switch config.DefaultConfig.KubernetesProvider {
case config.K3sProvider:
cmd := "INSTALL_K3S_BIN_DIR=/usr/bin INSTALL_K3S_SKIP_ENABLE=true INSTALL_K3S_SKIP_SELINUX_RPM=true"
// Append version if any, otherwise default to latest
if config.DefaultConfig.KubernetesVersion != "" {
cmd = fmt.Sprintf("INSTALL_K3S_VERSION=v%s %s", config.DefaultConfig.KubernetesVersion, cmd)
}
data = append(data, []schema.Stage{
{
Name: "Install Kubernetes packages",
Commands: []string{
"curl -sfL https://get.k3s.io > installer.sh",
"chmod +x installer.sh",
fmt.Sprintf("%s sh installer.sh", cmd),
fmt.Sprintf("%s sh installer.sh agent", cmd),
},
},
}...)
case config.K0sProvider:
cmd := "sh installer.sh"
// Append version if any, otherwise default to latest
if config.DefaultConfig.KubernetesVersion != "" {
cmd = fmt.Sprintf("K0S_VERSION=%s %s", config.DefaultConfig.KubernetesVersion, cmd)
}
data = append(data, []schema.Stage{
{
Name: "Install Kubernetes packages",
Commands: []string{
"curl -sfL https://get.k0s.sh > installer.sh",
"chmod +x installer.sh",
cmd,
"rm installer.sh",
"mv /usr/local/bin/k0s /usr/bin/k0s",
},
},
{
Name: "Create k0s services for systemd",
If: `[ -e "/sbin/systemctl" ] || [ -e "/usr/bin/systemctl" ] || [ -e "/usr/sbin/systemctl" ] || [ -e "/usr/bin/systemctl" ]`,
Files: []schema.File{
{
Path: "/etc/systemd/system/k0scontroller.service",
Permissions: 0644,
Owner: 0,
Group: 0,
Content: `[Unit]
Description=k0s - Zero Friction Kubernetes
Documentation=https://docs.k0sproject.io
ConditionFileIsExecutable=/usr/bin/k0s
After=network-online.target
Wants=network-online.target
[Service]
StartLimitInterval=5
StartLimitBurst=10
ExecStart=/usr/bin/k0s controller
RestartSec=10
Delegate=yes
KillMode=process
LimitCORE=infinity
TasksMax=infinity
TimeoutStartSec=0
LimitNOFILE=999999
Restart=always
[Install]
WantedBy=multi-user.target`,
},
{
Path: "/etc/systemd/system/k0sworker.service",
Permissions: 0644,
Owner: 0,
Group: 0,
Content: `[Unit]
Description=k0s - Zero Friction Kubernetes
Documentation=https://docs.k0sproject.io
ConditionFileIsExecutable=/usr/bin/k0s
After=network-online.target
Wants=network-online.target
[Service]
StartLimitInterval=5
StartLimitBurst=10
ExecStart=/usr/bin/k0s worker
RestartSec=10
Delegate=yes
KillMode=process
LimitCORE=infinity
TasksMax=infinity
TimeoutStartSec=0
LimitNOFILE=999999
Restart=always
[Install]
WantedBy=multi-user.target`,
},
},
},
{
Name: "Create k0s services for openrc",
If: `[ -f "/sbin/openrc" ]`,
Files: []schema.File{
{
Path: "/etc/init.d/k0scontroller",
Permissions: 0755,
Owner: 0,
Group: 0,
Content: `#!/sbin/openrc-run
supervisor=supervise-daemon
description="k0s - Zero Friction Kubernetes"
command=/usr/bin/k0s
command_args="'controller' "
name=$(basename $(readlink -f $command))
supervise_daemon_args="--stdout /var/log/${name}.log --stderr /var/log/${name}.err"
: "${rc_ulimit=-n 1048576 -u unlimited}"
depend() {
need cgroups
need net
use dns
after firewall
}`,
},
{
Path: "/etc/init.d/k0sworker",
Permissions: 0755,
Owner: 0,
Group: 0,
Content: `#!/sbin/openrc-run
supervisor=supervise-daemon
description="k0s - Zero Friction Kubernetes"
command=/usr/bin/k0s
command_args="'worker' "
name=$(basename $(readlink -f $command))
supervise_daemon_args="--stdout /var/log/${name}.log --stderr /var/log/${name}.err"
: "${rc_ulimit=-n 1048576 -u unlimited}"
depend() {
need cgroups
need net
use dns
after firewall
}`,
},
},
},
}...)
}

return data
}

func GetServicesStage(_ values.System, _ types.KairosLogger) []schema.Stage {
return []schema.Stage{
{
Expand Down Expand Up @@ -474,25 +650,6 @@ func GetServicesStage(_ values.System, _ types.KairosLogger) []schema.Stage {
}
}

// GetInstallStandardStage Returns the standard install stage so it installs the standard packages like k3s and provider
// Not used for now
func GetInstallStandardStage(sis values.System, logger types.KairosLogger) []schema.Stage {
var data []schema.Stage
/*
if config.DefaultConfig.Variant == "standard" {
data = append(data, schema.Stage{
Name: "Install standard packages",
Commands: []string{
"luet install -y k9s-openrc/systemd/k3s",
"luet install -y provider-kairos",
},
})
}
*/

return data
}

// RunAllStages Runs all the stages in the correct order
func RunAllStages(logger types.KairosLogger) (schema.YipConfig, error) {
fullYipConfig := schema.YipConfig{Stages: map[string][]schema.Stage{}}
Expand Down Expand Up @@ -535,7 +692,7 @@ func RunInstallStage(logger types.KairosLogger) (schema.YipConfig, error) {
data.Stages["install"] = installStage
// Add the framework stage
data.Stages["install"] = append(data.Stages["install"], GetInstallFrameworkStage(sis, logger)...)
data.Stages["install"] = append(data.Stages["install"], GetInstallStandardStage(sis, logger)...)
data.Stages["install"] = append(data.Stages["install"], GetInstallProviderAndKubernetes(sis, logger)...)

// Run things after we install packages and framework
data.Stages["after-install"] = []schema.Stage{}
Expand Down
Loading

0 comments on commit 8786063

Please sign in to comment.