2025-03-12 11:33:37 +01:00
|
|
|
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"
|
2025-03-20 22:25:41 +01:00
|
|
|
common "github.com/kairos-io/provider-kairos/v2/internal/role"
|
2025-03-12 11:33:37 +01:00
|
|
|
service "github.com/mudler/edgevpn/api/client/service"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2025-03-18 00:26:03 +01:00
|
|
|
K3sDistroName = "k3s"
|
2025-03-12 11:33:37 +01:00
|
|
|
)
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
type K3sControlPlane struct {
|
2025-03-12 11:33:37 +01:00
|
|
|
providerConfig *providerConfig.Config
|
|
|
|
roleConfig *service.RoleConfig
|
|
|
|
ip string
|
|
|
|
iface string
|
|
|
|
ifaceIP string
|
|
|
|
role string
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
type K3sWorker struct {
|
|
|
|
providerConfig *providerConfig.Config
|
|
|
|
roleConfig *service.RoleConfig
|
|
|
|
ip string
|
|
|
|
iface string
|
|
|
|
ifaceIP string
|
|
|
|
role string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *K3sControlPlane) K8sBin() string {
|
|
|
|
return utils.K3sBin()
|
2025-03-12 11:33:37 +01:00
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sWorker) K8sBin() string {
|
2025-03-12 11:33:37 +01:00
|
|
|
return utils.K3sBin()
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sControlPlane) DeployKubeVIP() error {
|
2025-03-12 11:33:37 +01:00
|
|
|
pconfig := k.ProviderConfig()
|
|
|
|
if !pconfig.KubeVIP.IsEnabled() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return deployKubeVIP(k.iface, k.ip, pconfig)
|
|
|
|
}
|
|
|
|
|
2025-03-20 22:08:06 +01:00
|
|
|
func (k *K3sControlPlane) Args() ([]string, error) {
|
2025-03-12 11:33:37 +01:00
|
|
|
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() {
|
2025-03-18 00:26:03 +01:00
|
|
|
clusterInitIP, _ := k.roleConfig.Client.Get("control-plane", "ip")
|
2025-03-12 11:33:37 +01:00
|
|
|
args = append(args, fmt.Sprintf("--server=https://%s:6443", clusterInitIP))
|
|
|
|
}
|
2025-03-21 08:57:29 +01:00
|
|
|
// 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 == "" {
|
2025-03-12 11:33:37 +01:00
|
|
|
args = append(args, "--cluster-init")
|
|
|
|
}
|
|
|
|
|
|
|
|
args = k.AppendArgs(args)
|
|
|
|
|
|
|
|
return args, nil
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sControlPlane) AppendArgs(other []string) []string {
|
|
|
|
c := k.ProviderConfig()
|
|
|
|
if c.K3s.ReplaceArgs {
|
|
|
|
return c.K3s.Args
|
|
|
|
}
|
|
|
|
|
|
|
|
return append(other, c.K3s.Args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *K3sWorker) AppendArgs(other []string) []string {
|
2025-03-12 11:33:37 +01:00
|
|
|
c := k.ProviderConfig()
|
|
|
|
if c.K3s.ReplaceArgs {
|
|
|
|
return c.K3s.Args
|
|
|
|
}
|
|
|
|
|
|
|
|
return append(other, c.K3s.Args...)
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sControlPlane) EnvUnit() string {
|
2025-03-12 11:33:37 +01:00
|
|
|
return machine.K3sEnvUnit("k3s")
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sWorker) EnvUnit() string {
|
|
|
|
return machine.K3sEnvUnit("k3s")
|
|
|
|
}
|
2025-03-12 11:33:37 +01:00
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sControlPlane) Service() (machine.Service, error) {
|
2025-03-12 11:33:37 +01:00
|
|
|
return machine.K3s()
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sWorker) Service() (machine.Service, error) {
|
|
|
|
return machine.K3sAgent()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *K3sControlPlane) Token() (string, error) {
|
2025-03-12 11:33:37 +01:00
|
|
|
return k.RoleConfig().Client.Get("nodetoken", "token")
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sWorker) Token() (string, error) {
|
|
|
|
return k.RoleConfig().Client.Get("nodetoken", "token")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *K3sControlPlane) GenerateEnv() (env map[string]string) {
|
2025-03-12 11:33:37 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sControlPlane) ProviderConfig() *providerConfig.Config {
|
2025-03-12 11:33:37 +01:00
|
|
|
return k.providerConfig
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sWorker) ProviderConfig() *providerConfig.Config {
|
|
|
|
return k.providerConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *K3sControlPlane) SetRoleConfig(c *service.RoleConfig) {
|
|
|
|
k.roleConfig = c
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *K3sWorker) SetRoleConfig(c *service.RoleConfig) {
|
2025-03-12 11:33:37 +01:00
|
|
|
k.roleConfig = c
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sControlPlane) RoleConfig() *service.RoleConfig {
|
|
|
|
return k.roleConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *K3sWorker) RoleConfig() *service.RoleConfig {
|
2025-03-12 11:33:37 +01:00
|
|
|
return k.roleConfig
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sControlPlane) HA() bool {
|
2025-03-20 22:25:41 +01:00
|
|
|
return k.role == common.RoleControlPlaneHA
|
2025-03-12 11:33:37 +01:00
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sControlPlane) ClusterInit() bool {
|
2025-03-20 22:25:41 +01:00
|
|
|
return k.role == common.RoleControlPlaneClusterInit
|
2025-03-12 11:33:37 +01:00
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sControlPlane) IP() string {
|
2025-03-12 11:33:37 +01:00
|
|
|
return k.ip
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sWorker) IP() string {
|
|
|
|
return k.ip
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *K3sControlPlane) PropagateData() error {
|
2025-03-12 11:33:37 +01:00
|
|
|
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 != "" {
|
2025-03-18 00:26:03 +01:00
|
|
|
err := c.Client.Set("kubeconfig", "control-plane", base64.RawURLEncoding.EncodeToString(kubeB))
|
2025-03-12 11:33:37 +01:00
|
|
|
if err != nil {
|
|
|
|
c.Logger.Error(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2025-03-20 22:08:06 +01:00
|
|
|
func (k *K3sWorker) Args() ([]string, error) {
|
2025-03-12 11:33:37 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sWorker) SetupWorker(controlPlaneIP, nodeToken string) error {
|
2025-03-12 11:33:37 +01:00
|
|
|
pconfig := k.ProviderConfig()
|
|
|
|
|
|
|
|
nodeToken = strings.TrimRight(nodeToken, "\n")
|
|
|
|
|
|
|
|
k3sConfig := providerConfig.K3s{}
|
|
|
|
if pconfig.K3sAgent.Enabled {
|
|
|
|
k3sConfig = pconfig.K3sAgent
|
|
|
|
}
|
|
|
|
|
|
|
|
env := map[string]string{
|
2025-03-18 00:26:03 +01:00
|
|
|
"K3S_URL": fmt.Sprintf("https://%s:6443", controlPlaneIP),
|
2025-03-12 11:33:37 +01:00
|
|
|
"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
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sControlPlane) Role() string {
|
|
|
|
return "server"
|
|
|
|
}
|
2025-03-12 11:33:37 +01:00
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sWorker) Role() string {
|
|
|
|
return "agent"
|
2025-03-12 11:33:37 +01:00
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sControlPlane) ServiceName() string {
|
|
|
|
return "k3s"
|
|
|
|
}
|
2025-03-12 11:33:37 +01:00
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sWorker) ServiceName() string {
|
|
|
|
return "k3s-agent"
|
2025-03-12 11:33:37 +01:00
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sControlPlane) Env() map[string]string {
|
2025-03-12 11:33:37 +01:00
|
|
|
c := k.ProviderConfig()
|
|
|
|
|
|
|
|
return c.K3s.Env
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sWorker) Env() map[string]string {
|
|
|
|
c := k.ProviderConfig()
|
|
|
|
return c.K3sAgent.Env
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *K3sControlPlane) EnvFile() string {
|
|
|
|
return machine.K3sEnvUnit(k.ServiceName())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *K3sWorker) EnvFile() string {
|
2025-03-12 11:33:37 +01:00
|
|
|
return machine.K3sEnvUnit(k.ServiceName())
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sControlPlane) SetRole(role string) {
|
2025-03-12 11:33:37 +01:00
|
|
|
k.role = role
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sWorker) SetRole(role string) {
|
|
|
|
k.role = role
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *K3sControlPlane) SetIP(ip string) {
|
2025-03-12 11:33:37 +01:00
|
|
|
k.ip = ip
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sWorker) SetIP(ip string) {
|
|
|
|
k.ip = ip
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *K3sControlPlane) GuessInterface() {
|
2025-03-12 11:33:37 +01:00
|
|
|
iface := guessInterface(k.ProviderConfig())
|
|
|
|
ifaceIP := utils.GetInterfaceIP(iface)
|
|
|
|
|
|
|
|
k.iface = iface
|
|
|
|
k.ifaceIP = ifaceIP
|
|
|
|
}
|
|
|
|
|
2025-03-18 00:26:03 +01:00
|
|
|
func (k *K3sWorker) GuessInterface() {
|
|
|
|
iface := guessInterface(k.ProviderConfig())
|
|
|
|
ifaceIP := utils.GetInterfaceIP(iface)
|
|
|
|
|
|
|
|
k.iface = iface
|
|
|
|
k.ifaceIP = ifaceIP
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *K3sControlPlane) Distro() string {
|
|
|
|
return K3sDistroName
|
|
|
|
}
|
|
|
|
|
|
|
|
func (k *K3sWorker) Distro() string {
|
2025-03-12 11:33:37 +01:00
|
|
|
return K3sDistroName
|
|
|
|
}
|