Files
provider-kairos/internal/role/p2p/k3s.go
Dimitris Karakasilis e88f724d19 3100 fix embedded registry config (#812)
Part of: https://github.com/kairos-io/kairos/issues/3100
fixing the manual setup case.

A config similar to this should now work:

master:

```yaml
hostname: metal-{{ trunc 4 .MachineID }}
users:
- name: kairos # Change to your own user
  passwd: kairos # Change to your own password
  groups:
    - admin # This user needs to be part of the admin group


k3s:
  enabled: true
  embedded_registry: true

stages:
  boot:
    - name: "Add registries configuration for k3s/spegel"
      files:
        - path: /etc/rancher/k3s/registries.yaml
          content: |
            mirrors:
              "*":
```

worker:

```yaml
#cloud-config

hostname: metal-{{ trunc 4 .MachineID }}
users:
- name: kairos # Change to your own user
  passwd: kairos # Change to your own password
  groups:
    - admin # This user needs to be part of the admin group

k3s-agent: # Warning: the key is different from the master node one
  enabled: true
  args:
    - --with-node-id # will configure the agent to use the node ID to communicate with the master node
  env:
    K3S_TOKEN: "<token-from-master-/var/lib/rancher/k3s/server/node-token"
    K3S_URL: https://<master-ip-address-here>:6443 # Same IP that you use to log into your master node

stages:
  boot:
    - name: "Add registries configuration for k3s/spegel"
      files:
        - path: /etc/rancher/k3s/registries.yaml
          content: |
            mirrors:
              "*":
```

documentation:
https://docs.k3s.io/installation/registry-mirror#enabling-registry-mirroring

---------

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-08-06 17:15:47 +03:00

322 lines
6.3 KiB
Go

package role
import (
"encoding/base64"
"errors"
"fmt"
"os"
"strings"
"github.com/kairos-io/kairos-sdk/machine"
"github.com/kairos-io/kairos-sdk/utils"
providerConfig "github.com/kairos-io/provider-kairos/v2/internal/provider/config"
service "github.com/mudler/edgevpn/api/client/service"
)
const (
K3sDistroName = "k3s"
K3sMasterName = "server"
K3sWorkerName = "agent"
K3sMasterServiceName = "k3s"
K3sWorkerServiceName = "k3s-agent"
)
type K3sNode struct {
providerConfig *providerConfig.Config
roleConfig *service.RoleConfig
ip string
iface string
ifaceIP string
role string
}
func (k *K3sNode) IsWorker() bool {
return k.role == RoleWorker
}
func (k *K3sNode) K8sBin() string {
return utils.K3sBin()
}
func (k *K3sNode) DeployKubeVIP() error {
pconfig := k.ProviderConfig()
if !pconfig.KubeVIP.IsEnabled() {
return nil
}
return deployKubeVIP(k.iface, k.ip, pconfig)
}
func (k *K3sNode) GenArgs() ([]string, error) {
var args []string
pconfig := k.ProviderConfig()
if pconfig.P2P.UseVPNWithKubernetes() {
args = append(args, "--flannel-iface=edgevpn0")
}
if pconfig.KubeVIP.IsEnabled() {
args = append(args, fmt.Sprintf("--tls-san=%s", k.ip), fmt.Sprintf("--node-ip=%s", k.ifaceIP))
}
if pconfig.K3s.EmbeddedRegistry {
args = append(args, "--embedded-registry")
}
if pconfig.P2P.Auto.HA.ExternalDB != "" {
args = []string{fmt.Sprintf("--datastore-endpoint=%s", pconfig.P2P.Auto.HA.ExternalDB)}
}
if k.HA() && !k.ClusterInit() {
clusterInitIP, _ := k.roleConfig.Client.Get("master", "ip")
args = append(args, fmt.Sprintf("--server=https://%s:6443", clusterInitIP))
}
// The --cluster-init flag changes the embedded SQLite DB to etcd. We don't
// want to do this if we're using an external DB.
if k.ClusterInit() && pconfig.P2P.Auto.HA.ExternalDB == "" {
args = append(args, "--cluster-init")
}
args = k.AppendArgs(args)
return args, nil
}
func (k *K3sNode) AppendArgs(other []string) []string {
c := k.ProviderConfig()
if c.K3s.ReplaceArgs {
return c.K3s.Args
}
return append(other, c.K3s.Args...)
}
func (k *K3sNode) EnvUnit() string {
return machine.K3sEnvUnit("k3s")
}
func (k *K3sNode) Service() (machine.Service, error) {
if k.role == "worker" {
return machine.K3sAgent()
}
return machine.K3s()
}
func (k *K3sNode) Token() (string, error) {
return k.RoleConfig().Client.Get("nodetoken", "token")
}
func (k *K3sNode) GenerateEnv() (env map[string]string) {
env = make(map[string]string)
if k.HA() && !k.ClusterInit() {
nodeToken, _ := k.Token()
env["K3S_TOKEN"] = nodeToken
}
pConfig := k.ProviderConfig()
if pConfig.K3s.ReplaceEnv {
env = pConfig.K3s.Env
} else {
// Override opts with user-supplied
for k, v := range pConfig.K3s.Env {
env[k] = v
}
}
return env
}
func (k *K3sNode) ProviderConfig() *providerConfig.Config {
return k.providerConfig
}
func (k *K3sNode) SetRoleConfig(c *service.RoleConfig) {
k.roleConfig = c
}
func (k *K3sNode) RoleConfig() *service.RoleConfig {
return k.roleConfig
}
func (k *K3sNode) HA() bool {
return k.role == "master/ha"
}
func (k *K3sNode) ClusterInit() bool {
return k.role == "master/clusterinit"
}
func (k *K3sNode) IP() string {
return k.ip
}
func (k *K3sNode) PropagateData() error {
c := k.RoleConfig()
tokenB, err := os.ReadFile("/var/lib/rancher/k3s/server/node-token")
if err != nil {
c.Logger.Error(err)
return err
}
nodeToken := string(tokenB)
nodeToken = strings.TrimRight(nodeToken, "\n")
if nodeToken != "" {
err := c.Client.Set("nodetoken", "token", nodeToken)
if err != nil {
c.Logger.Error(err)
}
}
kubeB, err := os.ReadFile("/etc/rancher/k3s/k3s.yaml")
if err != nil {
c.Logger.Error(err)
return err
}
kubeconfig := string(kubeB)
if kubeconfig != "" {
err := c.Client.Set("kubeconfig", "master", base64.RawURLEncoding.EncodeToString(kubeB))
if err != nil {
c.Logger.Error(err)
}
}
return nil
}
func (k *K3sNode) WorkerArgs() ([]string, error) {
pconfig := k.ProviderConfig()
k3sConfig := providerConfig.K3s{}
if pconfig.K3sAgent.Enabled {
k3sConfig = pconfig.K3sAgent
}
args := []string{
"--with-node-id",
}
if pconfig.P2P.UseVPNWithKubernetes() {
ip := utils.GetInterfaceIP("edgevpn0")
if ip == "" {
return nil, errors.New("node doesn't have an ip yet")
}
args = append(args,
fmt.Sprintf("--node-ip %s", ip),
"--flannel-iface=edgevpn0")
} else {
iface := guessInterface(pconfig)
ip := utils.GetInterfaceIP(iface)
args = append(args,
fmt.Sprintf("--node-ip %s", ip))
}
if k3sConfig.ReplaceArgs {
args = k3sConfig.Args
} else {
args = append(args, k3sConfig.Args...)
}
return args, nil
}
func (k *K3sNode) SetupWorker(masterIP, nodeToken string) error {
pconfig := k.ProviderConfig()
nodeToken = strings.TrimRight(nodeToken, "\n")
k3sConfig := providerConfig.K3s{}
if pconfig.K3sAgent.Enabled {
k3sConfig = pconfig.K3sAgent
}
env := map[string]string{
"K3S_URL": fmt.Sprintf("https://%s:6443", masterIP),
"K3S_TOKEN": nodeToken,
}
if k3sConfig.ReplaceEnv {
env = k3sConfig.Env
} else {
// Override opts with user-supplied
for k, v := range k3sConfig.Env {
env[k] = v
}
}
if err := utils.WriteEnv(machine.K3sEnvUnit("k3s-agent"),
env,
); err != nil {
return err
}
return nil
}
func (k *K3sNode) Role() string {
if k.IsWorker() {
return K3sWorkerName
}
return K3sMasterName
}
func (k *K3sNode) ServiceName() string {
if k.IsWorker() {
return K3sWorkerServiceName
}
return K3sMasterServiceName
}
func (k *K3sNode) Env() map[string]string {
c := k.ProviderConfig()
if k.IsWorker() {
return c.K3sAgent.Env
}
return c.K3s.Env
}
func (k *K3sNode) Args() []string {
c := k.ProviderConfig()
var args []string
if k.IsWorker() {
args = c.K3sAgent.Args
} else {
args = c.K3s.Args
// Add embedded registry flag if enabled (for non-p2p mode, server only)
if c.K3s.EmbeddedRegistry {
args = append(args, "--embedded-registry")
}
}
return args
}
func (k *K3sNode) EnvFile() string {
return machine.K3sEnvUnit(k.ServiceName())
}
func (k *K3sNode) SetRole(role string) {
k.role = role
}
func (k *K3sNode) SetIP(ip string) {
k.ip = ip
}
func (k *K3sNode) GuessInterface() {
iface := guessInterface(k.ProviderConfig())
ifaceIP := utils.GetInterfaceIP(iface)
k.iface = iface
k.ifaceIP = ifaceIP
}
func (k *K3sNode) Distro() string {
return K3sDistroName
}