mirror of https://github.com/k3s-io/k3s
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
653 lines
20 KiB
653 lines
20 KiB
package server |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
"os" |
|
"path" |
|
"path/filepath" |
|
"runtime/debug" |
|
"strconv" |
|
"strings" |
|
"sync" |
|
"time" |
|
|
|
helmchart "github.com/k3s-io/helm-controller/pkg/controllers/chart" |
|
helmcommon "github.com/k3s-io/helm-controller/pkg/controllers/common" |
|
"github.com/k3s-io/k3s/pkg/cli/cmds" |
|
"github.com/k3s-io/k3s/pkg/clientaccess" |
|
"github.com/k3s-io/k3s/pkg/daemons/config" |
|
"github.com/k3s-io/k3s/pkg/daemons/control" |
|
"github.com/k3s-io/k3s/pkg/datadir" |
|
"github.com/k3s-io/k3s/pkg/deploy" |
|
"github.com/k3s-io/k3s/pkg/node" |
|
"github.com/k3s-io/k3s/pkg/nodepassword" |
|
"github.com/k3s-io/k3s/pkg/rootlessports" |
|
"github.com/k3s-io/k3s/pkg/secretsencrypt" |
|
"github.com/k3s-io/k3s/pkg/static" |
|
"github.com/k3s-io/k3s/pkg/util" |
|
"github.com/k3s-io/k3s/pkg/version" |
|
"github.com/pkg/errors" |
|
"github.com/rancher/wrangler/pkg/apply" |
|
v1 "github.com/rancher/wrangler/pkg/generated/controllers/core/v1" |
|
"github.com/rancher/wrangler/pkg/leader" |
|
"github.com/rancher/wrangler/pkg/resolvehome" |
|
"github.com/sirupsen/logrus" |
|
corev1 "k8s.io/api/core/v1" |
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
|
clientset "k8s.io/client-go/kubernetes" |
|
"k8s.io/client-go/tools/clientcmd" |
|
) |
|
|
|
func ResolveDataDir(dataDir string) (string, error) { |
|
dataDir, err := datadir.Resolve(dataDir) |
|
return filepath.Join(dataDir, "server"), err |
|
} |
|
|
|
func StartServer(ctx context.Context, config *Config, cfg *cmds.Server) error { |
|
if err := setupDataDirAndChdir(&config.ControlConfig); err != nil { |
|
return err |
|
} |
|
|
|
if err := setNoProxyEnv(&config.ControlConfig); err != nil { |
|
return err |
|
} |
|
|
|
if err := control.Server(ctx, &config.ControlConfig); err != nil { |
|
return errors.Wrap(err, "starting kubernetes") |
|
} |
|
|
|
wg := &sync.WaitGroup{} |
|
wg.Add(len(config.StartupHooks)) |
|
|
|
config.ControlConfig.Runtime.Handler = router(ctx, config, cfg) |
|
config.ControlConfig.Runtime.StartupHooksWg = wg |
|
|
|
shArgs := cmds.StartupHookArgs{ |
|
APIServerReady: config.ControlConfig.Runtime.APIServerReady, |
|
KubeConfigSupervisor: config.ControlConfig.Runtime.KubeConfigSupervisor, |
|
Skips: config.ControlConfig.Skips, |
|
Disables: config.ControlConfig.Disables, |
|
} |
|
for _, hook := range config.StartupHooks { |
|
if err := hook(ctx, wg, shArgs); err != nil { |
|
return errors.Wrap(err, "startup hook") |
|
} |
|
} |
|
go startOnAPIServerReady(ctx, config) |
|
|
|
if err := printTokens(&config.ControlConfig); err != nil { |
|
return err |
|
} |
|
|
|
return writeKubeConfig(config.ControlConfig.Runtime.ServerCA, config) |
|
} |
|
|
|
func startOnAPIServerReady(ctx context.Context, config *Config) { |
|
select { |
|
case <-ctx.Done(): |
|
return |
|
case <-config.ControlConfig.Runtime.APIServerReady: |
|
if err := runControllers(ctx, config); err != nil { |
|
logrus.Fatalf("failed to start controllers: %v", err) |
|
} |
|
} |
|
} |
|
|
|
func runControllers(ctx context.Context, config *Config) error { |
|
controlConfig := &config.ControlConfig |
|
|
|
sc, err := NewContext(ctx, controlConfig.Runtime.KubeConfigSupervisor, true) |
|
if err != nil { |
|
return errors.Wrap(err, "failed to create new server context") |
|
} |
|
|
|
controlConfig.Runtime.StartupHooksWg.Wait() |
|
if err := stageFiles(ctx, sc, controlConfig); err != nil { |
|
return errors.Wrap(err, "failed to stage files") |
|
} |
|
|
|
// run migration before we set controlConfig.Runtime.Core |
|
if err := nodepassword.MigrateFile( |
|
sc.Core.Core().V1().Secret(), |
|
sc.Core.Core().V1().Node(), |
|
controlConfig.Runtime.NodePasswdFile); err != nil { |
|
logrus.Warn(errors.Wrap(err, "error migrating node-password file")) |
|
} |
|
controlConfig.Runtime.Event = sc.Event |
|
controlConfig.Runtime.Core = sc.Core |
|
|
|
for name, cb := range controlConfig.Runtime.ClusterControllerStarts { |
|
go runOrDie(ctx, name, cb) |
|
} |
|
|
|
for _, controller := range config.Controllers { |
|
if err := controller(ctx, sc); err != nil { |
|
return errors.Wrapf(err, "failed to start %s controller", util.GetFunctionName(controller)) |
|
} |
|
} |
|
|
|
if err := sc.Start(ctx); err != nil { |
|
return errors.Wrap(err, "failed to start wranger controllers") |
|
} |
|
|
|
if !controlConfig.DisableAPIServer { |
|
controlConfig.Runtime.LeaderElectedClusterControllerStarts[version.Program] = func(ctx context.Context) { |
|
apiserverControllers(ctx, sc, config) |
|
} |
|
} |
|
|
|
go setNodeLabelsAndAnnotations(ctx, sc.Core.Core().V1().Node(), config) |
|
|
|
go setClusterDNSConfig(ctx, config, sc.Core.Core().V1().ConfigMap()) |
|
|
|
if controlConfig.NoLeaderElect { |
|
for name, cb := range controlConfig.Runtime.LeaderElectedClusterControllerStarts { |
|
go runOrDie(ctx, name, cb) |
|
} |
|
} else { |
|
for name, cb := range controlConfig.Runtime.LeaderElectedClusterControllerStarts { |
|
go leader.RunOrDie(ctx, "", name, sc.K8s, cb) |
|
} |
|
} |
|
|
|
return nil |
|
} |
|
|
|
// apiServerControllers starts the core controllers, as well as the leader-elected controllers |
|
// that should only run on a control-plane node. |
|
func apiserverControllers(ctx context.Context, sc *Context, config *Config) { |
|
if err := coreControllers(ctx, sc, config); err != nil { |
|
panic(err) |
|
} |
|
for _, controller := range config.LeaderControllers { |
|
if err := controller(ctx, sc); err != nil { |
|
panic(errors.Wrapf(err, "failed to start %s leader controller", util.GetFunctionName(controller))) |
|
} |
|
} |
|
|
|
// Re-run context startup after core and leader-elected controllers have started. Additional |
|
// informer caches may need to start for the newly added OnChange callbacks. |
|
if err := sc.Start(ctx); err != nil { |
|
panic(errors.Wrap(err, "failed to start wranger controllers")) |
|
} |
|
} |
|
|
|
// runOrDie is similar to leader.RunOrDie, except that it runs the callback |
|
// immediately, without performing leader election. |
|
func runOrDie(ctx context.Context, name string, cb leader.Callback) { |
|
defer func() { |
|
if err := recover(); err != nil { |
|
logrus.WithField("stack", string(debug.Stack())).Fatalf("%s controller panic: %v", name, err) |
|
} |
|
}() |
|
cb(ctx) |
|
<-ctx.Done() |
|
} |
|
|
|
// coreControllers starts the following controllers, if they are enabled: |
|
// * Node controller (manages nodes passwords and coredns hosts file) |
|
// * Helm controller |
|
// * Secrets encryption |
|
// * Rootless ports |
|
// These controllers should only be run on nodes with a local apiserver |
|
func coreControllers(ctx context.Context, sc *Context, config *Config) error { |
|
if err := node.Register(ctx, |
|
!config.ControlConfig.Skips["coredns"], |
|
sc.Core.Core().V1().Secret(), |
|
sc.Core.Core().V1().ConfigMap(), |
|
sc.Core.Core().V1().Node()); err != nil { |
|
return err |
|
} |
|
|
|
// apply SystemDefaultRegistry setting to Helm before starting controllers |
|
if config.ControlConfig.HelmJobImage != "" { |
|
helmchart.DefaultJobImage = config.ControlConfig.HelmJobImage |
|
} else if config.ControlConfig.SystemDefaultRegistry != "" { |
|
helmchart.DefaultJobImage = config.ControlConfig.SystemDefaultRegistry + "/" + helmchart.DefaultJobImage |
|
} |
|
|
|
if !config.ControlConfig.DisableHelmController { |
|
restConfig, err := clientcmd.BuildConfigFromFlags("", config.ControlConfig.Runtime.KubeConfigSupervisor) |
|
if err != nil { |
|
return err |
|
} |
|
restConfig.UserAgent = util.GetUserAgent(helmcommon.Name) |
|
|
|
k8s, err := clientset.NewForConfig(restConfig) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
apply := apply.New(k8s, apply.NewClientFactory(restConfig)).WithDynamicLookup() |
|
helm := sc.Helm.WithAgent(restConfig.UserAgent) |
|
batch := sc.Batch.WithAgent(restConfig.UserAgent) |
|
auth := sc.Auth.WithAgent(restConfig.UserAgent) |
|
core := sc.Core.WithAgent(restConfig.UserAgent) |
|
helmchart.Register(ctx, |
|
metav1.NamespaceAll, |
|
helmcommon.Name, |
|
strconv.Itoa(config.ControlConfig.APIServerPort), |
|
k8s, |
|
apply, |
|
util.BuildControllerEventRecorder(k8s, helmcommon.Name, metav1.NamespaceAll), |
|
helm.V1().HelmChart(), |
|
helm.V1().HelmChart().Cache(), |
|
helm.V1().HelmChartConfig(), |
|
helm.V1().HelmChartConfig().Cache(), |
|
batch.V1().Job(), |
|
batch.V1().Job().Cache(), |
|
auth.V1().ClusterRoleBinding(), |
|
core.V1().ServiceAccount(), |
|
core.V1().ConfigMap(), |
|
core.V1().Secret()) |
|
} |
|
|
|
if config.ControlConfig.EncryptSecrets { |
|
if err := secretsencrypt.Register(ctx, |
|
sc.K8s, |
|
&config.ControlConfig, |
|
sc.Core.Core().V1().Node(), |
|
sc.Core.Core().V1().Secret()); err != nil { |
|
return err |
|
} |
|
} |
|
|
|
if config.ControlConfig.Rootless { |
|
return rootlessports.Register(ctx, |
|
sc.Core.Core().V1().Service(), |
|
!config.ControlConfig.DisableServiceLB, |
|
config.ControlConfig.HTTPSPort) |
|
} |
|
|
|
return nil |
|
} |
|
|
|
func stageFiles(ctx context.Context, sc *Context, controlConfig *config.Control) error { |
|
if controlConfig.DisableAPIServer { |
|
return nil |
|
} |
|
dataDir := filepath.Join(controlConfig.DataDir, "static") |
|
if err := static.Stage(dataDir); err != nil { |
|
return err |
|
} |
|
dataDir = filepath.Join(controlConfig.DataDir, "manifests") |
|
|
|
dnsIPFamilyPolicy := "PreferDualStack" |
|
if len(controlConfig.ClusterDNSs) == 1 { |
|
dnsIPFamilyPolicy = "SingleStack" |
|
} |
|
|
|
templateVars := map[string]string{ |
|
"%{CLUSTER_DNS}%": controlConfig.ClusterDNS.String(), |
|
"%{CLUSTER_DNS_LIST}%": fmt.Sprintf("[%s]", util.JoinIPs(controlConfig.ClusterDNSs)), |
|
"%{CLUSTER_DNS_IPFAMILYPOLICY}%": dnsIPFamilyPolicy, |
|
"%{CLUSTER_DOMAIN}%": controlConfig.ClusterDomain, |
|
"%{DEFAULT_LOCAL_STORAGE_PATH}%": controlConfig.DefaultLocalStoragePath, |
|
"%{SYSTEM_DEFAULT_REGISTRY}%": registryTemplate(controlConfig.SystemDefaultRegistry), |
|
"%{SYSTEM_DEFAULT_REGISTRY_RAW}%": controlConfig.SystemDefaultRegistry, |
|
"%{PREFERRED_ADDRESS_TYPES}%": addrTypesPrioTemplate(controlConfig.FlannelExternalIP), |
|
} |
|
|
|
skip := controlConfig.Skips |
|
if !skip["traefik"] && isHelmChartTraefikV1(sc) { |
|
logrus.Warn("Skipping Traefik v2 deployment due to existing Traefik v1 installation") |
|
skip["traefik"] = true |
|
} |
|
if err := deploy.Stage(dataDir, templateVars, skip); err != nil { |
|
return err |
|
} |
|
|
|
restConfig, err := clientcmd.BuildConfigFromFlags("", controlConfig.Runtime.KubeConfigSupervisor) |
|
if err != nil { |
|
return err |
|
} |
|
restConfig.UserAgent = util.GetUserAgent("deploy") |
|
|
|
k8s, err := clientset.NewForConfig(restConfig) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
apply := apply.New(k8s, apply.NewClientFactory(restConfig)).WithDynamicLookup() |
|
k3s := sc.K3s.WithAgent(restConfig.UserAgent) |
|
|
|
return deploy.WatchFiles(ctx, |
|
k8s, |
|
apply, |
|
k3s.V1().Addon(), |
|
controlConfig.Disables, |
|
dataDir) |
|
} |
|
|
|
// registryTemplate behaves like the system_default_registry template in Rancher helm charts, |
|
// and returns the registry value with a trailing forward slash if the registry string is not empty. |
|
// If it is empty, it is passed through as a no-op. |
|
func registryTemplate(registry string) string { |
|
if registry == "" { |
|
return registry |
|
} |
|
return registry + "/" |
|
} |
|
|
|
// addressTypesTemplate prioritizes ExternalIP addresses if we are in the multi-cloud env where |
|
// cluster traffic flows over the external IPs only |
|
func addrTypesPrioTemplate(flannelExternal bool) string { |
|
if flannelExternal { |
|
return "ExternalIP,InternalIP,Hostname" |
|
} |
|
|
|
return "InternalIP,ExternalIP,Hostname" |
|
} |
|
|
|
// isHelmChartTraefikV1 checks for an existing HelmChart resource with spec.chart containing traefik-1, |
|
// as deployed by the legacy chart (https://%{KUBERNETES_API}%/static/charts/traefik-1.81.0.tgz) |
|
func isHelmChartTraefikV1(sc *Context) bool { |
|
prefix := "traefik-1." |
|
helmChart, err := sc.Helm.Helm().V1().HelmChart().Get(metav1.NamespaceSystem, "traefik", metav1.GetOptions{}) |
|
if err != nil { |
|
logrus.WithError(err).Info("Failed to get existing traefik HelmChart") |
|
return false |
|
} |
|
chart := path.Base(helmChart.Spec.Chart) |
|
if strings.HasPrefix(chart, prefix) { |
|
logrus.WithField("chart", chart).Info("Found existing traefik v1 HelmChart") |
|
return true |
|
} |
|
return false |
|
} |
|
|
|
func HomeKubeConfig(write, rootless bool) (string, error) { |
|
if write { |
|
if os.Getuid() == 0 && !rootless { |
|
return datadir.GlobalConfig, nil |
|
} |
|
return resolvehome.Resolve(datadir.HomeConfig) |
|
} |
|
|
|
if _, err := os.Stat(datadir.GlobalConfig); err == nil { |
|
return datadir.GlobalConfig, nil |
|
} |
|
|
|
return resolvehome.Resolve(datadir.HomeConfig) |
|
} |
|
|
|
func printTokens(config *config.Control) error { |
|
var serverTokenFile string |
|
if config.Runtime.ServerToken != "" { |
|
serverTokenFile = filepath.Join(config.DataDir, "token") |
|
if err := writeToken(config.Runtime.ServerToken, serverTokenFile, config.Runtime.ServerCA); err != nil { |
|
return err |
|
} |
|
|
|
// backwards compatibility |
|
np := filepath.Join(config.DataDir, "node-token") |
|
if !isSymlink(np) { |
|
if err := os.RemoveAll(np); err != nil { |
|
return err |
|
} |
|
if err := os.Symlink(serverTokenFile, np); err != nil { |
|
return err |
|
} |
|
} |
|
|
|
logrus.Infof("Server node token is available at %s", serverTokenFile) |
|
printToken(config.SupervisorPort, config.BindAddressOrLoopback(true, true), "To join server node to cluster:", "server", "SERVER_NODE_TOKEN") |
|
} |
|
|
|
var agentTokenFile string |
|
if config.Runtime.AgentToken != "" { |
|
if config.AgentToken != "" { |
|
agentTokenFile = filepath.Join(config.DataDir, "agent-token") |
|
if isSymlink(agentTokenFile) { |
|
if err := os.RemoveAll(agentTokenFile); err != nil { |
|
return err |
|
} |
|
} |
|
if err := writeToken(config.Runtime.AgentToken, agentTokenFile, config.Runtime.ServerCA); err != nil { |
|
return err |
|
} |
|
} else if serverTokenFile != "" { |
|
agentTokenFile = filepath.Join(config.DataDir, "agent-token") |
|
if !isSymlink(agentTokenFile) { |
|
if err := os.RemoveAll(agentTokenFile); err != nil { |
|
return err |
|
} |
|
if err := os.Symlink(serverTokenFile, agentTokenFile); err != nil { |
|
return err |
|
} |
|
} |
|
} |
|
} |
|
|
|
if agentTokenFile != "" { |
|
logrus.Infof("Agent node token is available at %s", agentTokenFile) |
|
printToken(config.SupervisorPort, config.BindAddressOrLoopback(true, true), "To join agent node to cluster:", "agent", "AGENT_NODE_TOKEN") |
|
} |
|
|
|
return nil |
|
} |
|
|
|
func writeKubeConfig(certs string, config *Config) error { |
|
ip := config.ControlConfig.BindAddressOrLoopback(false, true) |
|
port := config.ControlConfig.HTTPSPort |
|
// on servers without a local apiserver, tunnel access via the loadbalancer |
|
if config.ControlConfig.DisableAPIServer { |
|
ip = config.ControlConfig.Loopback(true) |
|
port = config.ControlConfig.APIServerPort |
|
} |
|
url := fmt.Sprintf("https://%s:%d", ip, port) |
|
kubeConfig, err := HomeKubeConfig(true, config.ControlConfig.Rootless) |
|
def := true |
|
if err != nil { |
|
kubeConfig = filepath.Join(config.ControlConfig.DataDir, "kubeconfig-"+version.Program+".yaml") |
|
def = false |
|
} |
|
kubeConfigSymlink := kubeConfig |
|
if config.ControlConfig.KubeConfigOutput != "" { |
|
kubeConfig = config.ControlConfig.KubeConfigOutput |
|
} |
|
|
|
if isSymlink(kubeConfigSymlink) { |
|
if err := os.Remove(kubeConfigSymlink); err != nil { |
|
logrus.Errorf("Failed to remove kubeconfig symlink") |
|
} |
|
} |
|
|
|
if err = clientaccess.WriteClientKubeConfig(kubeConfig, url, config.ControlConfig.Runtime.ServerCA, config.ControlConfig.Runtime.ClientAdminCert, |
|
config.ControlConfig.Runtime.ClientAdminKey); err == nil { |
|
logrus.Infof("Wrote kubeconfig %s", kubeConfig) |
|
} else { |
|
logrus.Errorf("Failed to generate kubeconfig: %v", err) |
|
return err |
|
} |
|
|
|
if config.ControlConfig.KubeConfigMode != "" { |
|
mode, err := strconv.ParseInt(config.ControlConfig.KubeConfigMode, 8, 0) |
|
if err == nil { |
|
util.SetFileModeForPath(kubeConfig, os.FileMode(mode)) |
|
} else { |
|
logrus.Errorf("Failed to set %s to mode %s: %v", kubeConfig, os.FileMode(mode), err) |
|
} |
|
} else { |
|
util.SetFileModeForPath(kubeConfig, os.FileMode(0600)) |
|
} |
|
|
|
if kubeConfigSymlink != kubeConfig { |
|
if err := writeConfigSymlink(kubeConfig, kubeConfigSymlink); err != nil { |
|
logrus.Errorf("Failed to write kubeconfig symlink: %v", err) |
|
} |
|
} |
|
|
|
if def { |
|
logrus.Infof("Run: %s kubectl", filepath.Base(os.Args[0])) |
|
} |
|
|
|
return nil |
|
} |
|
|
|
func setupDataDirAndChdir(config *config.Control) error { |
|
var ( |
|
err error |
|
) |
|
|
|
config.DataDir, err = ResolveDataDir(config.DataDir) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
dataDir := config.DataDir |
|
|
|
if err := os.MkdirAll(dataDir, 0700); err != nil { |
|
return errors.Wrapf(err, "can not mkdir %s", dataDir) |
|
} |
|
|
|
if err := os.Chdir(dataDir); err != nil { |
|
return errors.Wrapf(err, "can not chdir %s", dataDir) |
|
} |
|
|
|
return nil |
|
} |
|
|
|
func printToken(httpsPort int, advertiseIP, prefix, cmd, varName string) { |
|
logrus.Infof("%s %s %s -s https://%s:%d -t ${%s}", prefix, version.Program, cmd, advertiseIP, httpsPort, varName) |
|
} |
|
|
|
func writeToken(token, file, certs string) error { |
|
if len(token) == 0 { |
|
return nil |
|
} |
|
|
|
token, err := clientaccess.FormatToken(token, certs) |
|
if err != nil { |
|
return err |
|
} |
|
return os.WriteFile(file, []byte(token+"\n"), 0600) |
|
} |
|
|
|
func setNoProxyEnv(config *config.Control) error { |
|
splitter := func(c rune) bool { |
|
return c == ',' |
|
} |
|
envList := []string{} |
|
envList = append(envList, strings.FieldsFunc(os.Getenv("NO_PROXY"), splitter)...) |
|
envList = append(envList, strings.FieldsFunc(os.Getenv("no_proxy"), splitter)...) |
|
envList = append(envList, |
|
".svc", |
|
"."+config.ClusterDomain, |
|
util.JoinIPNets(config.ClusterIPRanges), |
|
util.JoinIPNets(config.ServiceIPRanges), |
|
) |
|
os.Unsetenv("no_proxy") |
|
return os.Setenv("NO_PROXY", strings.Join(envList, ",")) |
|
} |
|
|
|
func writeConfigSymlink(kubeconfig, kubeconfigSymlink string) error { |
|
if err := os.Remove(kubeconfigSymlink); err != nil && !os.IsNotExist(err) { |
|
return fmt.Errorf("failed to remove %s file: %v", kubeconfigSymlink, err) |
|
} |
|
if err := os.MkdirAll(filepath.Dir(kubeconfigSymlink), 0755); err != nil { |
|
return fmt.Errorf("failed to create path for symlink: %v", err) |
|
} |
|
if err := os.Symlink(kubeconfig, kubeconfigSymlink); err != nil { |
|
return fmt.Errorf("failed to create symlink: %v", err) |
|
} |
|
return nil |
|
} |
|
|
|
func isSymlink(config string) bool { |
|
if fi, err := os.Lstat(config); err == nil && (fi.Mode()&os.ModeSymlink == os.ModeSymlink) { |
|
return true |
|
} |
|
return false |
|
} |
|
|
|
func setNodeLabelsAndAnnotations(ctx context.Context, nodes v1.NodeClient, config *Config) error { |
|
if config.DisableAgent || config.ControlConfig.DisableAPIServer { |
|
return nil |
|
} |
|
for { |
|
nodeName := os.Getenv("NODE_NAME") |
|
if nodeName == "" { |
|
logrus.Info("Waiting for control-plane node agent startup") |
|
time.Sleep(1 * time.Second) |
|
continue |
|
} |
|
node, err := nodes.Get(nodeName, metav1.GetOptions{}) |
|
if err != nil { |
|
logrus.Infof("Waiting for control-plane node %s startup: %v", nodeName, err) |
|
time.Sleep(1 * time.Second) |
|
continue |
|
} |
|
if node.Labels == nil { |
|
node.Labels = make(map[string]string) |
|
} |
|
v, ok := node.Labels[util.ControlPlaneRoleLabelKey] |
|
if !ok || v != "true" { |
|
node.Labels[util.ControlPlaneRoleLabelKey] = "true" |
|
node.Labels[util.MasterRoleLabelKey] = "true" |
|
} |
|
|
|
if config.ControlConfig.EncryptSecrets { |
|
if err = secretsencrypt.BootstrapEncryptionHashAnnotation(node, config.ControlConfig.Runtime); err != nil { |
|
logrus.Infof("Unable to set encryption hash annotation %s", err.Error()) |
|
break |
|
} |
|
} |
|
|
|
_, err = nodes.Update(node) |
|
if err == nil { |
|
logrus.Infof("Labels and annotations have been set successfully on node: %s", nodeName) |
|
break |
|
} |
|
select { |
|
case <-ctx.Done(): |
|
return ctx.Err() |
|
case <-time.After(time.Second): |
|
} |
|
} |
|
return nil |
|
} |
|
|
|
func setClusterDNSConfig(ctx context.Context, config *Config, configMap v1.ConfigMapClient) error { |
|
if config.ControlConfig.DisableAPIServer { |
|
return nil |
|
} |
|
// check if configmap already exists |
|
_, err := configMap.Get("kube-system", "cluster-dns", metav1.GetOptions{}) |
|
if err == nil { |
|
logrus.Infof("Cluster dns configmap already exists") |
|
return nil |
|
} |
|
clusterDNS := config.ControlConfig.ClusterDNS |
|
clusterDomain := config.ControlConfig.ClusterDomain |
|
c := &corev1.ConfigMap{ |
|
TypeMeta: metav1.TypeMeta{ |
|
Kind: "ConfigMap", |
|
APIVersion: "v1", |
|
}, |
|
ObjectMeta: metav1.ObjectMeta{ |
|
Name: "cluster-dns", |
|
Namespace: "kube-system", |
|
}, |
|
Data: map[string]string{ |
|
"clusterDNS": clusterDNS.String(), |
|
"clusterDomain": clusterDomain, |
|
}, |
|
} |
|
for { |
|
_, err = configMap.Create(c) |
|
if err == nil { |
|
logrus.Infof("Cluster dns configmap has been set successfully") |
|
break |
|
} |
|
logrus.Infof("Waiting for control-plane dns startup: %v", err) |
|
|
|
select { |
|
case <-ctx.Done(): |
|
return ctx.Err() |
|
case <-time.After(time.Second): |
|
} |
|
} |
|
return nil |
|
}
|
|
|