2017-11-02 10:07:10 +00:00
|
|
|
package cluster
|
|
|
|
|
|
|
|
import (
|
2018-01-09 22:10:56 +00:00
|
|
|
"context"
|
2017-11-02 10:07:10 +00:00
|
|
|
"fmt"
|
|
|
|
"net"
|
2017-11-15 01:12:33 +00:00
|
|
|
"path/filepath"
|
2017-11-30 23:16:45 +00:00
|
|
|
"strings"
|
2017-11-02 10:07:10 +00:00
|
|
|
|
2017-12-14 21:56:19 +00:00
|
|
|
"github.com/rancher/rke/authz"
|
2018-01-30 18:15:14 +00:00
|
|
|
"github.com/rancher/rke/docker"
|
2017-11-02 10:07:10 +00:00
|
|
|
"github.com/rancher/rke/hosts"
|
2018-02-01 21:28:31 +00:00
|
|
|
"github.com/rancher/rke/k8s"
|
2018-01-09 22:10:56 +00:00
|
|
|
"github.com/rancher/rke/log"
|
2017-11-02 10:07:10 +00:00
|
|
|
"github.com/rancher/rke/pki"
|
|
|
|
"github.com/rancher/rke/services"
|
2017-12-05 16:55:58 +00:00
|
|
|
"github.com/rancher/types/apis/management.cattle.io/v3"
|
2017-11-13 21:28:38 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2017-12-05 16:55:58 +00:00
|
|
|
"gopkg.in/yaml.v2"
|
2017-11-02 10:07:10 +00:00
|
|
|
"k8s.io/client-go/kubernetes"
|
2017-11-30 23:16:45 +00:00
|
|
|
"k8s.io/client-go/tools/clientcmd"
|
2017-11-17 00:45:51 +00:00
|
|
|
"k8s.io/client-go/util/cert"
|
2017-11-02 10:07:10 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Cluster struct {
|
2017-12-05 16:55:58 +00:00
|
|
|
v3.RancherKubernetesEngineConfig `yaml:",inline"`
|
2017-12-16 03:38:15 +00:00
|
|
|
ConfigPath string
|
2017-11-15 01:12:33 +00:00
|
|
|
LocalKubeConfigPath string
|
2017-11-30 23:16:45 +00:00
|
|
|
EtcdHosts []*hosts.Host
|
|
|
|
WorkerHosts []*hosts.Host
|
|
|
|
ControlPlaneHosts []*hosts.Host
|
2017-11-14 18:11:21 +00:00
|
|
|
KubeClient *kubernetes.Clientset
|
|
|
|
KubernetesServiceIP net.IP
|
|
|
|
Certificates map[string]pki.CertificatePKI
|
|
|
|
ClusterDomain string
|
|
|
|
ClusterCIDR string
|
|
|
|
ClusterDNSServer string
|
2017-12-19 22:18:27 +00:00
|
|
|
DockerDialerFactory hosts.DialerFactory
|
2018-01-11 01:00:14 +00:00
|
|
|
LocalConnDialerFactory hosts.DialerFactory
|
2018-01-30 18:15:14 +00:00
|
|
|
PrivateRegistriesMap map[string]v3.PrivateRegistry
|
2017-11-02 10:07:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2017-12-05 01:29:29 +00:00
|
|
|
X509AuthenticationProvider = "x509"
|
|
|
|
StateConfigMapName = "cluster-state"
|
|
|
|
UpdateStateTimeout = 30
|
|
|
|
GetStateTimeout = 30
|
|
|
|
KubernetesClientTimeOut = 30
|
2017-12-14 21:56:19 +00:00
|
|
|
NoneAuthorizationMode = "none"
|
2017-12-22 01:01:53 +00:00
|
|
|
LocalNodeAddress = "127.0.0.1"
|
|
|
|
LocalNodeHostname = "localhost"
|
|
|
|
LocalNodeUser = "root"
|
2017-11-02 10:07:10 +00:00
|
|
|
)
|
|
|
|
|
2018-01-09 22:10:56 +00:00
|
|
|
func (c *Cluster) DeployControlPlane(ctx context.Context) error {
|
2017-12-26 22:07:25 +00:00
|
|
|
// Deploy Etcd Plane
|
2018-01-30 18:15:14 +00:00
|
|
|
if err := services.RunEtcdPlane(ctx, c.EtcdHosts, c.Services.Etcd, c.LocalConnDialerFactory, c.PrivateRegistriesMap); err != nil {
|
2017-11-02 10:07:10 +00:00
|
|
|
return fmt.Errorf("[etcd] Failed to bring up Etcd Plane: %v", err)
|
|
|
|
}
|
2017-12-26 22:07:25 +00:00
|
|
|
// Deploy Control plane
|
2018-01-09 22:10:56 +00:00
|
|
|
if err := services.RunControlPlane(ctx, c.ControlPlaneHosts,
|
2017-12-08 23:05:55 +00:00
|
|
|
c.EtcdHosts,
|
|
|
|
c.Services,
|
2018-01-30 12:32:50 +00:00
|
|
|
c.SystemImages.KubernetesServicesSidecar,
|
2017-12-19 22:18:27 +00:00
|
|
|
c.Authorization.Mode,
|
2018-01-30 18:15:14 +00:00
|
|
|
c.LocalConnDialerFactory,
|
|
|
|
c.PrivateRegistriesMap); err != nil {
|
2017-11-02 10:07:10 +00:00
|
|
|
return fmt.Errorf("[controlPlane] Failed to bring up Control Plane: %v", err)
|
|
|
|
}
|
2017-12-26 22:07:25 +00:00
|
|
|
// Apply Authz configuration after deploying controlplane
|
2018-01-09 22:10:56 +00:00
|
|
|
if err := c.ApplyAuthzResources(ctx); err != nil {
|
2017-12-14 21:56:19 +00:00
|
|
|
return fmt.Errorf("[auths] Failed to apply RBAC resources: %v", err)
|
|
|
|
}
|
2017-12-26 22:07:25 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-01-09 22:10:56 +00:00
|
|
|
func (c *Cluster) DeployWorkerPlane(ctx context.Context) error {
|
2017-12-26 22:07:25 +00:00
|
|
|
// Deploy Worker Plane
|
2018-01-09 22:10:56 +00:00
|
|
|
if err := services.RunWorkerPlane(ctx, c.ControlPlaneHosts,
|
2017-12-08 23:05:55 +00:00
|
|
|
c.WorkerHosts,
|
2018-01-19 01:48:51 +00:00
|
|
|
c.EtcdHosts,
|
2017-12-08 23:05:55 +00:00
|
|
|
c.Services,
|
2018-01-30 12:32:50 +00:00
|
|
|
c.SystemImages.NginxProxy,
|
|
|
|
c.SystemImages.KubernetesServicesSidecar,
|
2018-01-30 18:15:14 +00:00
|
|
|
c.LocalConnDialerFactory,
|
|
|
|
c.PrivateRegistriesMap); err != nil {
|
2017-11-02 10:07:10 +00:00
|
|
|
return fmt.Errorf("[workerPlane] Failed to bring up Worker Plane: %v", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-12-16 03:38:15 +00:00
|
|
|
func ParseConfig(clusterFile string) (*v3.RancherKubernetesEngineConfig, error) {
|
2017-11-02 10:07:10 +00:00
|
|
|
logrus.Debugf("Parsing cluster file [%v]", clusterFile)
|
2017-12-16 03:38:15 +00:00
|
|
|
var rkeConfig v3.RancherKubernetesEngineConfig
|
|
|
|
if err := yaml.Unmarshal([]byte(clusterFile), &rkeConfig); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &rkeConfig, nil
|
|
|
|
}
|
|
|
|
|
2017-12-22 01:01:53 +00:00
|
|
|
func ParseCluster(
|
|
|
|
ctx context.Context,
|
|
|
|
rkeConfig *v3.RancherKubernetesEngineConfig,
|
|
|
|
clusterFilePath, configDir string,
|
|
|
|
dockerDialerFactory,
|
|
|
|
localConnDialerFactory hosts.DialerFactory) (*Cluster, error) {
|
2017-11-02 10:07:10 +00:00
|
|
|
var err error
|
2017-12-16 03:38:15 +00:00
|
|
|
c := &Cluster{
|
|
|
|
RancherKubernetesEngineConfig: *rkeConfig,
|
|
|
|
ConfigPath: clusterFilePath,
|
2017-12-19 22:18:27 +00:00
|
|
|
DockerDialerFactory: dockerDialerFactory,
|
2018-01-11 01:00:14 +00:00
|
|
|
LocalConnDialerFactory: localConnDialerFactory,
|
2018-01-30 18:15:14 +00:00
|
|
|
PrivateRegistriesMap: make(map[string]v3.PrivateRegistry),
|
2017-11-02 10:07:10 +00:00
|
|
|
}
|
2017-12-16 03:38:15 +00:00
|
|
|
// Setting cluster Defaults
|
2018-01-09 22:10:56 +00:00
|
|
|
c.setClusterDefaults(ctx)
|
2017-12-16 03:38:15 +00:00
|
|
|
|
|
|
|
if err := c.InvertIndexHosts(); err != nil {
|
2017-11-02 10:07:10 +00:00
|
|
|
return nil, fmt.Errorf("Failed to classify hosts from config file: %v", err)
|
|
|
|
}
|
2017-11-21 19:25:08 +00:00
|
|
|
|
2017-12-16 03:38:15 +00:00
|
|
|
if err := c.ValidateCluster(); err != nil {
|
2017-11-21 19:25:08 +00:00
|
|
|
return nil, fmt.Errorf("Failed to validate cluster: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-11-07 15:44:17 +00:00
|
|
|
c.KubernetesServiceIP, err = services.GetKubernetesServiceIP(c.Services.KubeAPI.ServiceClusterIPRange)
|
2017-11-02 10:07:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Failed to get Kubernetes Service IP: %v", err)
|
|
|
|
}
|
|
|
|
c.ClusterDomain = c.Services.Kubelet.ClusterDomain
|
2017-11-06 20:50:41 +00:00
|
|
|
c.ClusterCIDR = c.Services.KubeController.ClusterCIDR
|
2017-11-08 00:32:55 +00:00
|
|
|
c.ClusterDNSServer = c.Services.Kubelet.ClusterDNSServer
|
2017-11-15 01:12:33 +00:00
|
|
|
if len(c.ConfigPath) == 0 {
|
|
|
|
c.ConfigPath = DefaultClusterConfig
|
|
|
|
}
|
2017-12-22 01:01:53 +00:00
|
|
|
c.LocalKubeConfigPath = GetLocalKubeConfig(c.ConfigPath, configDir)
|
2018-01-30 18:15:14 +00:00
|
|
|
|
|
|
|
for _, pr := range c.PrivateRegistries {
|
|
|
|
if pr.URL == "" {
|
|
|
|
pr.URL = docker.DockerRegistryURL
|
|
|
|
}
|
|
|
|
c.PrivateRegistriesMap[pr.URL] = pr
|
|
|
|
}
|
|
|
|
|
2017-11-02 10:07:10 +00:00
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
2017-12-22 01:01:53 +00:00
|
|
|
func GetLocalKubeConfig(configPath, configDir string) string {
|
2017-11-15 01:12:33 +00:00
|
|
|
baseDir := filepath.Dir(configPath)
|
2017-12-22 01:01:53 +00:00
|
|
|
if len(configDir) > 0 {
|
|
|
|
baseDir = filepath.Dir(configDir)
|
|
|
|
}
|
2017-11-15 01:12:33 +00:00
|
|
|
fileName := filepath.Base(configPath)
|
|
|
|
baseDir += "/"
|
|
|
|
return fmt.Sprintf("%s%s%s", baseDir, pki.KubeAdminConfigPrefix, fileName)
|
|
|
|
}
|
2017-11-17 00:45:51 +00:00
|
|
|
|
2018-01-09 22:10:56 +00:00
|
|
|
func rebuildLocalAdminConfig(ctx context.Context, kubeCluster *Cluster) error {
|
|
|
|
log.Infof(ctx, "[reconcile] Rebuilding and updating local kube config")
|
2017-12-06 02:22:50 +00:00
|
|
|
var workingConfig, newConfig string
|
2018-01-16 23:10:14 +00:00
|
|
|
currentKubeConfig := kubeCluster.Certificates[pki.KubeAdminCertName]
|
2017-11-30 23:16:45 +00:00
|
|
|
caCrt := kubeCluster.Certificates[pki.CACertName].Certificate
|
|
|
|
for _, cpHost := range kubeCluster.ControlPlaneHosts {
|
2017-12-06 02:22:50 +00:00
|
|
|
if (currentKubeConfig == pki.CertificatePKI{}) {
|
|
|
|
kubeCluster.Certificates = make(map[string]pki.CertificatePKI)
|
|
|
|
newConfig = getLocalAdminConfigWithNewAddress(kubeCluster.LocalKubeConfigPath, cpHost.Address)
|
|
|
|
} else {
|
|
|
|
kubeURL := fmt.Sprintf("https://%s:6443", cpHost.Address)
|
|
|
|
caData := string(cert.EncodeCertPEM(caCrt))
|
|
|
|
crtData := string(cert.EncodeCertPEM(currentKubeConfig.Certificate))
|
|
|
|
keyData := string(cert.EncodePrivateKeyPEM(currentKubeConfig.Key))
|
2018-01-16 23:10:14 +00:00
|
|
|
newConfig = pki.GetKubeConfigX509WithData(kubeURL, pki.KubeAdminCertName, caData, crtData, keyData)
|
2017-12-06 02:22:50 +00:00
|
|
|
}
|
2018-01-09 22:10:56 +00:00
|
|
|
if err := pki.DeployAdminConfig(ctx, newConfig, kubeCluster.LocalKubeConfigPath); err != nil {
|
2017-11-30 23:16:45 +00:00
|
|
|
return fmt.Errorf("Failed to redeploy local admin config with new host")
|
2017-11-21 19:25:08 +00:00
|
|
|
}
|
2017-11-30 23:16:45 +00:00
|
|
|
workingConfig = newConfig
|
2017-12-06 02:22:50 +00:00
|
|
|
if _, err := GetK8sVersion(kubeCluster.LocalKubeConfigPath); err == nil {
|
2018-01-09 22:10:56 +00:00
|
|
|
log.Infof(ctx, "[reconcile] host [%s] is active master on the cluster", cpHost.Address)
|
2017-11-30 23:16:45 +00:00
|
|
|
break
|
2017-11-26 18:23:06 +00:00
|
|
|
}
|
2017-11-17 00:45:51 +00:00
|
|
|
}
|
2017-11-30 23:16:45 +00:00
|
|
|
currentKubeConfig.Config = workingConfig
|
2018-01-16 23:10:14 +00:00
|
|
|
kubeCluster.Certificates[pki.KubeAdminCertName] = currentKubeConfig
|
2017-11-17 00:45:51 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-01-09 22:10:56 +00:00
|
|
|
func isLocalConfigWorking(ctx context.Context, localKubeConfigPath string) bool {
|
2017-11-30 23:16:45 +00:00
|
|
|
if _, err := GetK8sVersion(localKubeConfigPath); err != nil {
|
2018-01-09 22:10:56 +00:00
|
|
|
log.Infof(ctx, "[reconcile] Local config is not vaild, rebuilding admin config")
|
2017-11-30 23:16:45 +00:00
|
|
|
return false
|
2017-11-26 18:23:06 +00:00
|
|
|
}
|
2017-11-30 23:16:45 +00:00
|
|
|
return true
|
2017-11-26 18:23:06 +00:00
|
|
|
}
|
|
|
|
|
2017-11-30 23:16:45 +00:00
|
|
|
func getLocalConfigAddress(localConfigPath string) (string, error) {
|
|
|
|
config, err := clientcmd.BuildConfigFromFlags("", localConfigPath)
|
2017-11-17 00:45:51 +00:00
|
|
|
if err != nil {
|
2017-11-30 23:16:45 +00:00
|
|
|
return "", err
|
2017-11-17 00:45:51 +00:00
|
|
|
}
|
2017-11-30 23:16:45 +00:00
|
|
|
splittedAdress := strings.Split(config.Host, ":")
|
|
|
|
address := splittedAdress[1]
|
|
|
|
return address[2:], nil
|
2017-11-17 00:45:51 +00:00
|
|
|
}
|
2017-12-06 02:22:50 +00:00
|
|
|
|
|
|
|
func getLocalAdminConfigWithNewAddress(localConfigPath, cpAddress string) string {
|
|
|
|
config, _ := clientcmd.BuildConfigFromFlags("", localConfigPath)
|
|
|
|
config.Host = fmt.Sprintf("https://%s:6443", cpAddress)
|
|
|
|
return pki.GetKubeConfigX509WithData(
|
|
|
|
"https://"+cpAddress+":6443",
|
2018-01-16 23:10:14 +00:00
|
|
|
pki.KubeAdminCertName,
|
2017-12-06 02:22:50 +00:00
|
|
|
string(config.CAData),
|
|
|
|
string(config.CertData),
|
|
|
|
string(config.KeyData))
|
|
|
|
}
|
2017-12-14 21:56:19 +00:00
|
|
|
|
2018-01-09 22:10:56 +00:00
|
|
|
func (c *Cluster) ApplyAuthzResources(ctx context.Context) error {
|
|
|
|
if err := authz.ApplyJobDeployerServiceAccount(ctx, c.LocalKubeConfigPath); err != nil {
|
2017-12-14 21:56:19 +00:00
|
|
|
return fmt.Errorf("Failed to apply the ServiceAccount needed for job execution: %v", err)
|
|
|
|
}
|
2017-12-14 21:56:19 +00:00
|
|
|
if c.Authorization.Mode == NoneAuthorizationMode {
|
|
|
|
return nil
|
|
|
|
}
|
2017-12-14 21:56:19 +00:00
|
|
|
if c.Authorization.Mode == services.RBACAuthorizationMode {
|
2018-01-09 22:10:56 +00:00
|
|
|
if err := authz.ApplySystemNodeClusterRoleBinding(ctx, c.LocalKubeConfigPath); err != nil {
|
2017-12-14 21:56:19 +00:00
|
|
|
return fmt.Errorf("Failed to apply the ClusterRoleBinding needed for node authorization: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2017-12-20 01:51:07 +00:00
|
|
|
if c.Authorization.Mode == services.RBACAuthorizationMode && c.Services.KubeAPI.PodSecurityPolicy {
|
2018-01-09 22:10:56 +00:00
|
|
|
if err := authz.ApplyDefaultPodSecurityPolicy(ctx, c.LocalKubeConfigPath); err != nil {
|
2017-12-20 01:51:07 +00:00
|
|
|
return fmt.Errorf("Failed to apply default PodSecurityPolicy: %v", err)
|
|
|
|
}
|
2018-01-09 22:10:56 +00:00
|
|
|
if err := authz.ApplyDefaultPodSecurityPolicyRole(ctx, c.LocalKubeConfigPath); err != nil {
|
2017-12-20 01:51:07 +00:00
|
|
|
return fmt.Errorf("Failed to apply default PodSecurityPolicy ClusterRole and ClusterRoleBinding: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2017-12-14 21:56:19 +00:00
|
|
|
return nil
|
|
|
|
}
|
2018-01-16 18:29:09 +00:00
|
|
|
|
|
|
|
func (c *Cluster) getUniqueHostList() []*hosts.Host {
|
|
|
|
hostList := []*hosts.Host{}
|
|
|
|
hostList = append(hostList, c.EtcdHosts...)
|
|
|
|
hostList = append(hostList, c.ControlPlaneHosts...)
|
|
|
|
hostList = append(hostList, c.WorkerHosts...)
|
|
|
|
// little trick to get a unique host list
|
|
|
|
uniqHostMap := make(map[*hosts.Host]bool)
|
|
|
|
for _, host := range hostList {
|
|
|
|
uniqHostMap[host] = true
|
|
|
|
}
|
|
|
|
uniqHostList := []*hosts.Host{}
|
|
|
|
for host := range uniqHostMap {
|
|
|
|
uniqHostList = append(uniqHostList, host)
|
|
|
|
}
|
|
|
|
return uniqHostList
|
|
|
|
}
|
2018-02-01 21:28:31 +00:00
|
|
|
|
|
|
|
func (c *Cluster) DeployAddons(ctx context.Context) error {
|
|
|
|
if err := c.DeployK8sAddOns(ctx); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return c.DeployUserAddOns(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cluster) SyncLabelsAndTaints(ctx context.Context) error {
|
|
|
|
log.Infof(ctx, "[sync] Syncing nodes Labels and Taints")
|
|
|
|
k8sClient, err := k8s.NewClient(c.LocalKubeConfigPath)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Failed to initialize new kubernetes client: %v", err)
|
|
|
|
}
|
|
|
|
for _, host := range c.getUniqueHostList() {
|
|
|
|
if err := k8s.SyncLabels(k8sClient, host.HostnameOverride, host.ToAddLabels, host.ToDelLabels); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Taints are not being added by user
|
|
|
|
if err := k8s.SyncTaints(k8sClient, host.HostnameOverride, host.ToAddTaints, host.ToDelTaints); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log.Infof(ctx, "[sync] Successfully synced nodes Labels and Taints")
|
|
|
|
return nil
|
|
|
|
}
|