package cluster import ( "bytes" "context" "encoding/json" "fmt" "io/ioutil" "net/http" "os" "os/exec" "strings" "time" "github.com/rancher/rke/addons" "github.com/rancher/rke/authz" "github.com/rancher/rke/k8s" "github.com/rancher/rke/log" "github.com/rancher/rke/services" "github.com/rancher/rke/templates" v3 "github.com/rancher/rke/types" "github.com/rancher/rke/types/kdm" "github.com/rancher/rke/util" "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" ) const ( UserAddonResourceName = "rke-user-addon" IngressAddonResourceName = "rke-ingress-controller" UserAddonsIncludeResourceName = "rke-user-includes-addons" IngressAddonJobName = "rke-ingress-controller-deploy-job" MetricsServerAddonJobName = "rke-metrics-addon-deploy-job" UserAddonJobName = "rke-user-addon-deploy-job" UserAddonIncludeJobName = "rke-user-includes-addons-deploy-job" MetricsServerAddonResourceName = "rke-metrics-addon" KubeDNSAddonAppName = "kube-dns" KubeDNSAutoscalerAppName = "kube-dns-autoscaler" CoreDNSAutoscalerAppName = "coredns-autoscaler" KubeAPIAuthAppName = "kube-api-auth" CattleClusterAgentAppName = "cattle-cluster-agent" CoreDNSPriorityClassNameKey = "coredns_priority_class_name" CoreDNSAutoscalerPriorityClassNameKey = "coredns_autoscaler_priority_class_name" KubeDNSPriorityClassNameKey = "kube_dns_priority_class_name" KubeDNSAutoscalerPriorityClassNameKey = "kube_dns_autoscaler_priority_class_name" CoreDNSProvider = "coredns" KubeDNSProvider = "kube-dns" Nodelocal = "nodelocal" NginxIngressAddonAppName = "ingress-nginx" NginxIngressAddonAppNamespace = "ingress-nginx" NginxIngressAddonDefaultBackendName = "default-http-backend" NginxIngressAddonDefaultBackendNamespace = "ingress-nginx" ) var ( DNSProviders = []string{KubeDNSProvider, CoreDNSProvider} NginxIngressAddonJobNames = []string{"ingress-nginx-admission-create", "ingress-nginx-admission-patch"} ) type ingressOptions struct { RBACConfig string Options map[string]string NodeSelector map[string]string ExtraArgs map[string]string ExtraEnvs []v3.ExtraEnv ExtraVolumes []v3.ExtraVolume ExtraVolumeMounts []v3.ExtraVolumeMount DNSPolicy string AlpineImage string IngressImage string IngressBackend string IngressWebhook string HTTPPort int HTTPSPort int NetworkMode string DefaultBackend bool UpdateStrategy *appsv1.DaemonSetUpdateStrategy Tolerations []v1.Toleration NginxIngressControllerPriorityClassName string DefaultHTTPBackendPriorityClassName string } type MetricsServerOptions struct { RBACConfig string Options map[string]string NodeSelector map[string]string MetricsServerImage string Version string UpdateStrategy *appsv1.DeploymentStrategy Replicas *int32 Tolerations []v1.Toleration MetricsServerPriorityClassName string } type CoreDNSOptions struct { RBACConfig string CoreDNSImage string CoreDNSAutoScalerImage string ClusterDomain string ClusterDNSServer string ReverseCIDRs []string UpstreamNameservers []string NodeSelector map[string]string UpdateStrategy *appsv1.DeploymentStrategy LinearAutoscalerParams string Tolerations []v1.Toleration CoreDNSPriorityClassName string CoreDNSAutoscalerPriorityClassName string } type KubeDNSOptions struct { RBACConfig string KubeDNSImage string DNSMasqImage string KubeDNSAutoScalerImage string KubeDNSSidecarImage string ClusterDomain string ClusterDNSServer string ReverseCIDRs []string UpstreamNameservers []string StubDomains map[string][]string NodeSelector map[string]string UpdateStrategy *appsv1.DeploymentStrategy LinearAutoscalerParams string Tolerations []v1.Toleration KubeDNSPriorityClassName string KubeDNSAutoscalerPriorityClassName string } type NodelocalOptions struct { RBACConfig string NodelocalImage string ClusterDomain string ClusterDNSServer string IPAddress string NodeSelector map[string]string UpdateStrategy *appsv1.DaemonSetUpdateStrategy NodeLocalDNSPriorityClassName string } type addonError struct { err string isCritical bool } func (e *addonError) Error() string { return e.err } func getAddonResourceName(addon string) string { AddonResourceName := "rke-" + addon + "-addon" return AddonResourceName } func (c *Cluster) deployK8sAddOns(ctx context.Context, data map[string]interface{}) error { if err := c.deployDNS(ctx, data); err != nil { if err, ok := err.(*addonError); ok && err.isCritical { return err } log.Warnf(ctx, "Failed to deploy DNS addon execute job for provider %s: %v", c.DNS.Provider, err) } if err := c.deployMetricServer(ctx, data); err != nil { if err, ok := err.(*addonError); ok && err.isCritical { return err } log.Warnf(ctx, "Failed to deploy addon execute job [%s]: %v", MetricsServerAddonResourceName, err) } if err := c.deployIngress(ctx, data); err != nil { if err, ok := err.(*addonError); ok && err.isCritical { return err } log.Warnf(ctx, "Failed to deploy addon execute job [%s]: %v", IngressAddonResourceName, err) } return nil } func (c *Cluster) deployUserAddOns(ctx context.Context) error { log.Infof(ctx, "[addons] Setting up user addons") if c.Addons != "" { if err := c.doAddonDeploy(ctx, c.Addons, UserAddonResourceName, false); err != nil { return err } } else { addonJobExists, err := addons.AddonJobExists(UserAddonJobName, c.LocalKubeConfigPath, c.K8sWrapTransport) if err != nil { return nil } if addonJobExists { log.Infof(ctx, "[addons] Removing user addons") if err := c.doAddonDelete(ctx, UserAddonResourceName, false); err != nil { return err } log.Infof(ctx, "[addons] User addons removed successfully") } } if len(c.AddonsInclude) > 0 { if err := c.deployAddonsInclude(ctx); err != nil { return err } } else { addonJobExists, err := addons.AddonJobExists(UserAddonIncludeJobName, c.LocalKubeConfigPath, c.K8sWrapTransport) if err != nil { return nil } if addonJobExists { if err := c.doAddonDelete(ctx, UserAddonsIncludeResourceName, false); err != nil { return err } } } if c.Addons == "" && len(c.AddonsInclude) == 0 { log.Infof(ctx, "[addons] no user addons defined") } else { log.Infof(ctx, "[addons] User addons deployed successfully") } return nil } func (c *Cluster) deployAddonsInclude(ctx context.Context) error { var manifests []byte log.Infof(ctx, "[addons] Checking for included user addons") if len(c.AddonsInclude) == 0 { log.Infof(ctx, "[addons] No included addon paths or urls") return nil } for _, addon := range c.AddonsInclude { if strings.HasPrefix(addon, "http") { addonYAML, err := getAddonFromURL(addon) if err != nil { return err } log.Infof(ctx, "[addons] Adding addon from url %s", addon) logrus.Debugf("URL Yaml: %s", addonYAML) // make sure we properly separated manifests addonYAMLStr := string(addonYAML) formattedAddonYAML := formatAddonYAML(addonYAMLStr) addonYAML = []byte(formattedAddonYAML) logrus.Debugf("Formatted Yaml: %s", addonYAML) if err := validateUserAddonYAML(addonYAML); err != nil { return err } manifests = append(manifests, addonYAML...) } else if isFilePath(addon) { addonYAML, err := ioutil.ReadFile(addon) if err != nil { return err } log.Infof(ctx, "[addons] Adding addon from %s", addon) logrus.Debugf("FilePath Yaml: %s", string(addonYAML)) // make sure we properly separated manifests addonYAMLStr := string(addonYAML) formattedAddonYAML := formatAddonYAML(addonYAMLStr) addonYAML = []byte(formattedAddonYAML) logrus.Debugf("Formatted Yaml: %s", addonYAML) if err := validateUserAddonYAML(addonYAML); err != nil { return err } manifests = append(manifests, addonYAML...) } else { log.Warnf(ctx, "[addons] Unable to determine if %s is a file path or url, skipping", addon) } } log.Infof(ctx, "[addons] Deploying %s", UserAddonsIncludeResourceName) logrus.Debugf("[addons] Compiled addons yaml: %s", string(manifests)) return c.doAddonDeploy(ctx, string(manifests), UserAddonsIncludeResourceName, false) } func formatAddonYAML(addonYAMLStr string) string { if !strings.HasPrefix(addonYAMLStr, "---") { logrus.Debug("Yaml does not start with dashes") addonYAMLStr = fmt.Sprintf("%s\n%s", "---", addonYAMLStr) } if !strings.HasSuffix(addonYAMLStr, "\n") { logrus.Debug("Yaml does not end with newline") addonYAMLStr = fmt.Sprintf("%s\n", addonYAMLStr) } return addonYAMLStr } func validateUserAddonYAML(addon []byte) error { yamlContents := make(map[string]interface{}) return yaml.Unmarshal(addon, &yamlContents) } func isFilePath(addonPath string) bool { if _, err := os.Stat(addonPath); os.IsNotExist(err) { return false } return true } func getAddonFromURL(yamlURL string) ([]byte, error) { resp, err := http.Get(yamlURL) if err != nil { return nil, err } defer resp.Body.Close() addonYaml, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } return addonYaml, nil } func (c *Cluster) deployKubeDNS(ctx context.Context, data map[string]interface{}) error { log.Infof(ctx, "[addons] Setting up %s", c.DNS.Provider) KubeDNSConfig := KubeDNSOptions{ KubeDNSImage: c.SystemImages.KubeDNS, KubeDNSSidecarImage: c.SystemImages.KubeDNSSidecar, KubeDNSAutoScalerImage: c.SystemImages.KubeDNSAutoscaler, DNSMasqImage: c.SystemImages.DNSmasq, RBACConfig: c.Authorization.Mode, ClusterDomain: c.ClusterDomain, ClusterDNSServer: c.ClusterDNSServer, UpstreamNameservers: c.DNS.UpstreamNameservers, ReverseCIDRs: c.DNS.ReverseCIDRs, StubDomains: c.DNS.StubDomains, NodeSelector: c.DNS.NodeSelector, UpdateStrategy: &appsv1.DeploymentStrategy{ Type: c.DNS.UpdateStrategy.Strategy, RollingUpdate: c.DNS.UpdateStrategy.RollingUpdate, }, Tolerations: c.DNS.Tolerations, KubeDNSPriorityClassName: c.DNS.Options[KubeDNSPriorityClassNameKey], KubeDNSAutoscalerPriorityClassName: c.DNS.Options[KubeDNSAutoscalerPriorityClassNameKey], } linearModeBytes, err := json.Marshal(c.DNS.LinearAutoscalerParams) if err != nil { return err } KubeDNSConfig.LinearAutoscalerParams = string(linearModeBytes) tmplt, err := templates.GetVersionedTemplates(kdm.KubeDNS, data, c.Version) if err != nil { return err } kubeDNSYaml, err := templates.CompileTemplateFromMap(tmplt, KubeDNSConfig) if err != nil { return err } if err := c.doAddonDeploy(ctx, kubeDNSYaml, getAddonResourceName(c.DNS.Provider), true); err != nil { return err } log.Infof(ctx, "[addons] %s deployed successfully", c.DNS.Provider) return nil } func (c *Cluster) deployCoreDNS(ctx context.Context, data map[string]interface{}) error { log.Infof(ctx, "[addons] Setting up %s", c.DNS.Provider) CoreDNSConfig := CoreDNSOptions{ CoreDNSImage: c.SystemImages.CoreDNS, CoreDNSAutoScalerImage: c.SystemImages.CoreDNSAutoscaler, RBACConfig: c.Authorization.Mode, ClusterDomain: c.ClusterDomain, ClusterDNSServer: c.ClusterDNSServer, UpstreamNameservers: c.DNS.UpstreamNameservers, ReverseCIDRs: c.DNS.ReverseCIDRs, NodeSelector: c.DNS.NodeSelector, UpdateStrategy: &appsv1.DeploymentStrategy{ Type: c.DNS.UpdateStrategy.Strategy, RollingUpdate: c.DNS.UpdateStrategy.RollingUpdate, }, Tolerations: c.DNS.Tolerations, CoreDNSPriorityClassName: c.DNS.Options[CoreDNSPriorityClassNameKey], CoreDNSAutoscalerPriorityClassName: c.DNS.Options[CoreDNSAutoscalerPriorityClassNameKey], } linearModeBytes, err := json.Marshal(c.DNS.LinearAutoscalerParams) if err != nil { return err } CoreDNSConfig.LinearAutoscalerParams = string(linearModeBytes) tmplt, err := templates.GetVersionedTemplates(kdm.CoreDNS, data, c.Version) if err != nil { return err } coreDNSYaml, err := templates.CompileTemplateFromMap(tmplt, CoreDNSConfig) if err != nil { return err } if err := c.doAddonDeploy(ctx, coreDNSYaml, getAddonResourceName(c.DNS.Provider), true); err != nil { return err } log.Infof(ctx, "[addons] CoreDNS deployed successfully") return nil } func (c *Cluster) deployMetricServer(ctx context.Context, data map[string]interface{}) error { if c.Monitoring.Provider == "none" { addonJobExists, err := addons.AddonJobExists(MetricsServerAddonJobName, c.LocalKubeConfigPath, c.K8sWrapTransport) if err != nil { return nil } if addonJobExists { log.Infof(ctx, "[ingress] Removing installed metrics server") if err := c.doAddonDelete(ctx, MetricsServerAddonResourceName, false); err != nil { return err } log.Infof(ctx, "[ingress] Metrics server removed successfully") } else { log.Infof(ctx, "[ingress] Metrics Server is disabled, skipping Metrics server installation") } return nil } log.Infof(ctx, "[addons] Setting up Metrics Server") s := strings.Split(c.SystemImages.MetricsServer, ":") versionTag := s[len(s)-1] MetricsServerConfig := MetricsServerOptions{ MetricsServerImage: c.SystemImages.MetricsServer, RBACConfig: c.Authorization.Mode, Options: c.Monitoring.Options, NodeSelector: c.Monitoring.NodeSelector, Version: util.GetTagMajorVersion(versionTag), UpdateStrategy: &appsv1.DeploymentStrategy{ Type: c.Monitoring.UpdateStrategy.Strategy, RollingUpdate: c.Monitoring.UpdateStrategy.RollingUpdate, }, Replicas: c.Monitoring.Replicas, Tolerations: c.Monitoring.Tolerations, MetricsServerPriorityClassName: c.Monitoring.MetricsServerPriorityClassName, } tmplt, err := templates.GetVersionedTemplates(kdm.MetricsServer, data, c.Version) if err != nil { return err } metricsYaml, err := templates.CompileTemplateFromMap(tmplt, MetricsServerConfig) if err != nil { return err } if err := c.doAddonDeploy(ctx, metricsYaml, MetricsServerAddonResourceName, true); err != nil { return err } log.Infof(ctx, "[addons] Metrics Server deployed successfully") return nil } func (c *Cluster) deployWithKubectl(ctx context.Context, addonYaml string) error { buf := bytes.NewBufferString(addonYaml) cmd := exec.Command("kubectl", "--kubeconfig", c.LocalKubeConfigPath, "apply", "-f", "-") cmd.Stdin = buf cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() } func (c *Cluster) doAddonDeploy(ctx context.Context, addonYaml, resourceName string, isCritical bool) error { if c.UseKubectlDeploy { if err := c.deployWithKubectl(ctx, addonYaml); err != nil { return &addonError{fmt.Sprintf("%v", err), isCritical} } } addonUpdated, err := c.StoreAddonConfigMap(ctx, addonYaml, resourceName) if err != nil { return &addonError{fmt.Sprintf("Failed to save addon ConfigMap: %v", err), isCritical} } log.Infof(ctx, "[addons] Executing deploy job %s", resourceName) k8sClient, err := k8s.NewClient(c.LocalKubeConfigPath, c.K8sWrapTransport) if err != nil { return &addonError{fmt.Sprintf("%v", err), isCritical} } node, err := k8s.GetNode(k8sClient, c.ControlPlaneHosts[0].HostnameOverride) if err != nil { return &addonError{fmt.Sprintf("Failed to get Node [%s]: %v", c.ControlPlaneHosts[0].HostnameOverride, err), isCritical} } addonJob, err := addons.GetAddonsExecuteJob(resourceName, node.Name, c.Services.KubeAPI.Image) if err != nil { return &addonError{fmt.Sprintf("Failed to generate addon execute job: %v", err), isCritical} } if err = c.ApplySystemAddonExecuteJob(addonJob, addonUpdated); err != nil { return &addonError{fmt.Sprintf("%v", err), isCritical} } return nil } func (c *Cluster) doAddonDelete(ctx context.Context, resourceName string, isCritical bool) error { k8sClient, err := k8s.NewClient(c.LocalKubeConfigPath, c.K8sWrapTransport) if err != nil { return &addonError{fmt.Sprintf("%v", err), isCritical} } node, err := k8s.GetNode(k8sClient, c.ControlPlaneHosts[0].HostnameOverride) if err != nil { return &addonError{fmt.Sprintf("Failed to get Node [%s]: %v", c.ControlPlaneHosts[0].HostnameOverride, err), isCritical} } deleteJob, err := addons.GetAddonsDeleteJob(resourceName, node.Name, c.Services.KubeAPI.Image) if err != nil { return &addonError{fmt.Sprintf("Failed to generate addon delete job: %v", err), isCritical} } if err := k8s.ApplyK8sSystemJob(deleteJob, c.LocalKubeConfigPath, c.K8sWrapTransport, c.AddonJobTimeout*2, false); err != nil { return &addonError{fmt.Sprintf("%v", err), isCritical} } // At this point, the addon should be deleted. We need to clean up by deleting the deploy and delete jobs. tmpJobYaml, err := addons.GetAddonsExecuteJob(resourceName, node.Name, c.Services.KubeAPI.Image) if err != nil { return err } if err := k8s.DeleteK8sSystemJob(tmpJobYaml, k8sClient, c.AddonJobTimeout); err != nil { return err } return k8s.DeleteK8sSystemJob(deleteJob, k8sClient, c.AddonJobTimeout) } func (c *Cluster) StoreAddonConfigMap(ctx context.Context, addonYaml string, addonName string) (bool, error) { log.Infof(ctx, "[addons] Saving ConfigMap for addon %s to Kubernetes", addonName) updated := false kubeClient, err := k8s.NewClient(c.LocalKubeConfigPath, c.K8sWrapTransport) if err != nil { return updated, err } timeout := make(chan bool, 1) go func() { for { updated, err = k8s.UpdateConfigMap(kubeClient, []byte(addonYaml), addonName) if err != nil { time.Sleep(time.Second * 5) continue } log.Infof(ctx, "[addons] Successfully saved ConfigMap for addon %s to Kubernetes", addonName) timeout <- true break } }() select { case <-timeout: return updated, nil case <-time.After(time.Second * UpdateStateTimeout): return updated, fmt.Errorf("[addons] Timeout waiting for kubernetes to be ready") } } func (c *Cluster) ApplySystemAddonExecuteJob(addonJob string, addonUpdated bool) error { return k8s.ApplyK8sSystemJob(addonJob, c.LocalKubeConfigPath, c.K8sWrapTransport, c.AddonJobTimeout, addonUpdated) } func (c *Cluster) deployIngress(ctx context.Context, data map[string]interface{}) error { if c.Ingress.Provider == "none" { addonJobExists, err := addons.AddonJobExists(IngressAddonJobName, c.LocalKubeConfigPath, c.K8sWrapTransport) if err != nil { return nil } if addonJobExists { log.Infof(ctx, "[ingress] removing installed ingress controller") if err := c.doAddonDelete(ctx, IngressAddonResourceName, false); err != nil { return err } log.Infof(ctx, "[ingress] ingress controller removed successfully") } else { log.Infof(ctx, "[ingress] ingress controller is disabled, skipping ingress controller") } return nil } log.Infof(ctx, "[ingress] Setting up %s ingress controller", c.Ingress.Provider) ingressConfig := ingressOptions{ RBACConfig: c.Authorization.Mode, Options: c.Ingress.Options, NodeSelector: c.Ingress.NodeSelector, ExtraArgs: c.Ingress.ExtraArgs, DNSPolicy: c.Ingress.DNSPolicy, IngressImage: c.SystemImages.Ingress, IngressBackend: c.SystemImages.IngressBackend, IngressWebhook: c.SystemImages.IngressWebhook, ExtraEnvs: c.Ingress.ExtraEnvs, ExtraVolumes: c.Ingress.ExtraVolumes, ExtraVolumeMounts: c.Ingress.ExtraVolumeMounts, HTTPPort: c.Ingress.HTTPPort, HTTPSPort: c.Ingress.HTTPSPort, NetworkMode: c.Ingress.NetworkMode, DefaultBackend: *c.Ingress.DefaultBackend, UpdateStrategy: &appsv1.DaemonSetUpdateStrategy{ Type: c.Ingress.UpdateStrategy.Strategy, RollingUpdate: c.Ingress.UpdateStrategy.RollingUpdate, }, Tolerations: c.Ingress.Tolerations, NginxIngressControllerPriorityClassName: c.Ingress.NginxIngressControllerPriorityClassName, DefaultHTTPBackendPriorityClassName: c.Ingress.DefaultHTTPBackendPriorityClassName, } // since nginx ingress controller 0.16.0, it can be run as non-root and doesn't require privileged anymore. // So we can use securityContext instead of setting privileges via initContainer. ingressSplits := strings.SplitN(c.SystemImages.Ingress, ":", 2) if len(ingressSplits) == 2 { version := strings.Split(ingressSplits[1], "-")[0] if version < "0.16.0" { ingressConfig.AlpineImage = c.SystemImages.Alpine } // since nginx ingress controller 0.40.0, admission batch jobs are deployed. // Before deployment of the new ingress controller based on the update strategy, remove admission batch jobs if they exist. if version > "0.40.0" { log.Infof(ctx, "[ingress] removing admission batch jobs if they exist") kubeClient, err := k8s.NewClient(c.LocalKubeConfigPath, c.K8sWrapTransport) if err != nil { return err } for _, jobName := range NginxIngressAddonJobNames { if err = k8s.DeleteK8sJobIfExists(kubeClient, jobName, NginxIngressAddonAppNamespace); err != nil { return err } } } } tmplt, err := templates.GetVersionedTemplates(kdm.NginxIngress, data, c.Version) if err != nil { return err } // Currently only deploying nginx ingress controller ingressYaml, err := templates.CompileTemplateFromMap(tmplt, ingressConfig) if err != nil { return err } if err := c.doAddonDeploy(ctx, ingressYaml, IngressAddonResourceName, true); err != nil { return err } // ingress runs in it's own namespace, so it needs it's own role/rolebinding for PSP if c.Authorization.Mode == services.RBACAuthorizationMode && c.Services.KubeAPI.PodSecurityPolicy { if err := authz.ApplyDefaultPodSecurityPolicyRole(ctx, c.LocalKubeConfigPath, NginxIngressAddonAppName, c.K8sWrapTransport); err != nil { return fmt.Errorf("Failed to apply default PodSecurityPolicy ClusterRole and ClusterRoleBinding: %v", err) } } // After deployment of the new ingress controller based on the update strategy, remove the default backend as requested. if !ingressConfig.DefaultBackend { log.Infof(ctx, "[ingress] removing default backend service and deployment if they exist") kubeClient, err := k8s.NewClient(c.LocalKubeConfigPath, c.K8sWrapTransport) if err != nil { return err } if err = k8s.DeleteServiceIfExists(ctx, kubeClient, NginxIngressAddonDefaultBackendName, NginxIngressAddonDefaultBackendNamespace); err != nil { return err } if err = k8s.DeleteDeploymentIfExists(ctx, kubeClient, NginxIngressAddonDefaultBackendName, NginxIngressAddonDefaultBackendNamespace); err != nil { return err } } log.Infof(ctx, "[ingress] ingress controller %s deployed successfully", c.Ingress.Provider) return nil } func (c *Cluster) removeDNSProvider(ctx context.Context, dnsprovider string) error { AddonJobExists, err := addons.AddonJobExists(getAddonResourceName(dnsprovider)+"-deploy-job", c.LocalKubeConfigPath, c.K8sWrapTransport) if err != nil { return err } if AddonJobExists { log.Infof(ctx, "[dns] removing DNS provider %s", dnsprovider) if err := c.doAddonDelete(ctx, getAddonResourceName(dnsprovider), false); err != nil { return err } log.Infof(ctx, "[dns] DNS provider %s removed successfully", dnsprovider) return nil } return nil } func (c *Cluster) deployDNS(ctx context.Context, data map[string]interface{}) error { for _, dnsprovider := range DNSProviders { if strings.EqualFold(dnsprovider, c.DNS.Provider) { continue } if err := c.removeDNSProvider(ctx, dnsprovider); err != nil { return err } } switch DNSProvider := c.DNS.Provider; DNSProvider { case DefaultDNSProvider: if err := c.deployKubeDNS(ctx, data); err != nil { if err, ok := err.(*addonError); ok && err.isCritical { return err } log.Warnf(ctx, "Failed to deploy addon execute job [%s]: %v", getAddonResourceName(c.DNS.Provider), err) } log.Infof(ctx, "[dns] DNS provider %s deployed successfully", c.DNS.Provider) case CoreDNSProvider: if err := c.deployCoreDNS(ctx, data); err != nil { if err, ok := err.(*addonError); ok && err.isCritical { return err } log.Warnf(ctx, "Failed to deploy addon execute job [%s]: %v", getAddonResourceName(c.DNS.Provider), err) } log.Infof(ctx, "[dns] DNS provider %s deployed successfully", c.DNS.Provider) case "none": return nil default: log.Warnf(ctx, "[dns] No valid DNS provider configured: %s", c.DNS.Provider) return nil } // Check for nodelocal DNS if c.DNS.Nodelocal == nil { AddonJobExists, err := addons.AddonJobExists(getAddonResourceName(Nodelocal)+"-deploy-job", c.LocalKubeConfigPath, c.K8sWrapTransport) if err != nil { return err } if AddonJobExists { log.Infof(ctx, "[dns] removing %s", Nodelocal) if err := c.doAddonDelete(ctx, getAddonResourceName(Nodelocal), false); err != nil { return err } log.Infof(ctx, "[dns] %s removed successfully", Nodelocal) return nil } } if c.DNS.Nodelocal != nil && c.DNS.Nodelocal.IPAddress != "" { if err := c.deployNodelocal(ctx, data); err != nil { if err, ok := err.(*addonError); ok && err.isCritical { return err } log.Warnf(ctx, "Failed to deploy addon execute job [%s]: %v", getAddonResourceName(Nodelocal), err) } return nil } return nil } func (c *Cluster) deployNodelocal(ctx context.Context, data map[string]interface{}) error { log.Infof(ctx, "[dns] Setting up %s", Nodelocal) NodelocalConfig := NodelocalOptions{ NodelocalImage: c.SystemImages.Nodelocal, RBACConfig: c.Authorization.Mode, ClusterDomain: c.ClusterDomain, ClusterDNSServer: c.ClusterDNSServer, IPAddress: c.DNS.Nodelocal.IPAddress, NodeSelector: c.DNS.Nodelocal.NodeSelector, NodeLocalDNSPriorityClassName: c.DNS.Nodelocal.NodeLocalDNSPriorityClassName, } if c.DNS.Nodelocal.UpdateStrategy != nil { NodelocalConfig.UpdateStrategy = &appsv1.DaemonSetUpdateStrategy{ Type: c.DNS.Nodelocal.UpdateStrategy.Strategy, RollingUpdate: c.DNS.Nodelocal.UpdateStrategy.RollingUpdate, } } tmplt, err := templates.GetVersionedTemplates(kdm.Nodelocal, data, c.Version) if err != nil { return err } nodelocalYaml, err := templates.CompileTemplateFromMap(tmplt, NodelocalConfig) if err != nil { return err } if err := c.doAddonDeploy(ctx, nodelocalYaml, getAddonResourceName(Nodelocal), true); err != nil { return err } log.Infof(ctx, "[dns] %s deployed successfully", Nodelocal) return nil }