diff --git a/cluster/cluster.go b/cluster/cluster.go index bcf899d6..6533ce5f 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/docker/docker/api/types" "github.com/rancher/rke/authz" "github.com/rancher/rke/cloudprovider" "github.com/rancher/rke/docker" @@ -31,6 +32,7 @@ type Cluster struct { v3.RancherKubernetesEngineConfig `yaml:",inline"` ConfigPath string LocalKubeConfigPath string + StateFilePath string EtcdHosts []*hosts.Host WorkerHosts []*hosts.Host ControlPlaneHosts []*hosts.Host @@ -151,9 +153,12 @@ func ParseCluster( localConnDialerFactory hosts.DialerFactory, k8sWrapTransport k8s.WrapTransport) (*Cluster, error) { var err error + // get state filepath + stateFilePath := GetStateFilePath(clusterFilePath, configDir) c := &Cluster{ RancherKubernetesEngineConfig: *rkeConfig, ConfigPath: clusterFilePath, + StateFilePath: stateFilePath, DockerDialerFactory: dockerDialerFactory, LocalConnDialerFactory: localConnDialerFactory, PrivateRegistriesMap: make(map[string]v3.PrivateRegistry), @@ -506,3 +511,12 @@ func RestartClusterPods(ctx context.Context, kubeCluster *Cluster) error { } return nil } + +func (c *Cluster) GetHostInfoMap() map[string]types.Info { + hostsInfoMap := make(map[string]types.Info) + allHosts := hosts.GetUniqueHostList(c.EtcdHosts, c.ControlPlaneHosts, c.WorkerHosts) + for _, host := range allHosts { + hostsInfoMap[host.Address] = host.DockerInfo + } + return hostsInfoMap +} diff --git a/cluster/state.go b/cluster/state.go index af6cbd35..0db60bd8 100644 --- a/cluster/state.go +++ b/cluster/state.go @@ -2,9 +2,13 @@ package cluster import ( "context" + "encoding/json" "fmt" + "io/ioutil" "os" "path" + "path/filepath" + "strings" "time" "github.com/rancher/rke/hosts" @@ -18,8 +22,23 @@ import ( "gopkg.in/yaml.v2" "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/util/cert" ) +const ( + stateFileExt = ".rkestate" +) + +type RKEFullState struct { + DesiredState RKEState `json:"desiredState,omitempty"` + CurrentState RKEState `json:"currentState,omitempty"` +} + +type RKEState struct { + RancherKubernetesEngineConfig *v3.RancherKubernetesEngineConfig `json:"rkeConfig,omitempty"` + CertificatesBundle map[string]v3.CertificatePKI `json:"certificatesBundle,omitempty"` +} + func (c *Cluster) SaveClusterState(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConfig) error { if len(c.ControlPlaneHosts) > 0 { // Reinitialize kubernetes Client @@ -249,3 +268,86 @@ func GetK8sVersion(localConfigPath string, k8sWrapTransport k8s.WrapTransport) ( } return fmt.Sprintf("%#v", *serverVersion), nil } + +func GenerateDesiredState(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConfig, rkeFullState *RKEFullState) (RKEState, error) { + var desiredState RKEState + if rkeFullState.DesiredState.CertificatesBundle == nil { + // Get the certificate Bundle + certBundle, err := pki.GenerateRKECerts(ctx, *rkeConfig, "", "") + if err != nil { + return desiredState, fmt.Errorf("Failed to generate certificate bundle: %v", err) + } + // Convert rke certs to v3.certs + certificatesBundle := make(map[string]v3.CertificatePKI) + for name, certPKI := range certBundle { + certificatePEM := string(cert.EncodeCertPEM(certPKI.Certificate)) + keyPEM := string(cert.EncodePrivateKeyPEM(certPKI.Key)) + certificatesBundle[name] = v3.CertificatePKI{ + Name: certPKI.Name, + Config: certPKI.Config, + Certificate: certificatePEM, + Key: keyPEM, + EnvName: certPKI.EnvName, + KeyEnvName: certPKI.KeyEnvName, + ConfigEnvName: certPKI.ConfigEnvName, + Path: certPKI.Path, + KeyPath: certPKI.KeyPath, + ConfigPath: certPKI.ConfigPath, + CommonName: certPKI.CommonName, + OUName: certPKI.OUName, + } + } + desiredState.CertificatesBundle = certificatesBundle + } else { + desiredState.CertificatesBundle = rkeFullState.DesiredState.CertificatesBundle + } + desiredState.RancherKubernetesEngineConfig = rkeConfig + + return desiredState, nil +} + +func (s *RKEFullState) WriteStateFile(ctx context.Context, statePath string) error { + stateFile, err := json.MarshalIndent(s, "", " ") + if err != nil { + return fmt.Errorf("Failed to Marshal state object: %v", err) + } + logrus.Debugf("Writing state file: %s", stateFile) + if err := ioutil.WriteFile(statePath, []byte(stateFile), 0640); err != nil { + return fmt.Errorf("Failed to write state file: %v", err) + } + log.Infof(ctx, "Successfully Deployed state file at [%s]", statePath) + return nil +} + +func GetStateFilePath(configPath, configDir string) string { + baseDir := filepath.Dir(configPath) + if len(configDir) > 0 { + baseDir = filepath.Dir(configDir) + } + fileName := filepath.Base(configPath) + baseDir += "/" + fullPath := fmt.Sprintf("%s%s", baseDir, fileName) + trimmedName := strings.TrimSuffix(fullPath, filepath.Ext(fullPath)) + return trimmedName + stateFileExt +} + +func ReadStateFile(ctx context.Context, statePath string) (*RKEFullState, error) { + rkeFullState := &RKEFullState{} + fp, err := filepath.Abs(statePath) + if err != nil { + return rkeFullState, fmt.Errorf("failed to lookup current directory name: %v", err) + } + file, err := os.Open(fp) + if err != nil { + return rkeFullState, fmt.Errorf("Can not find RKE state file: %v", err) + } + defer file.Close() + buf, err := ioutil.ReadAll(file) + if err != nil { + return rkeFullState, fmt.Errorf("failed to read file: %v", err) + } + if err := json.Unmarshal(buf, rkeFullState); err != nil { + return rkeFullState, fmt.Errorf("failed to unmarshal the state file: %v", err) + } + return rkeFullState, nil +} diff --git a/cmd/up.go b/cmd/up.go index ffe5890d..be2097b4 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -49,6 +49,10 @@ func UpCommand() cli.Command { Name: "disable-port-check", Usage: "Disable port check validation between nodes", }, + cli.BoolFlag{ + Name: "init", + Usage: "test init", + }, } upFlags = append(upFlags, commonFlags...) @@ -61,6 +65,25 @@ func UpCommand() cli.Command { } } +func ClusterInit(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConfig, configDir string) error { + log.Infof(ctx, "Initiating Kubernetes cluster") + stateFilePath := cluster.GetStateFilePath(clusterFilePath, configDir) + rkeFullState, _ := cluster.ReadStateFile(ctx, stateFilePath) + kubeCluster, err := cluster.ParseCluster(ctx, rkeConfig, clusterFilePath, configDir, nil, nil, nil) + if err != nil { + return err + } + desiredState, err := cluster.GenerateDesiredState(ctx, &kubeCluster.RancherKubernetesEngineConfig, rkeFullState) + if err != nil { + return err + } + rkeState := cluster.RKEFullState{ + DesiredState: desiredState, + CurrentState: rkeFullState.CurrentState, + } + return rkeState.WriteStateFile(ctx, stateFilePath) +} + func ClusterUp( ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConfig, @@ -198,7 +221,9 @@ func clusterUpFromCli(ctx *cli.Context) error { } updateOnly := ctx.Bool("update-only") disablePortCheck := ctx.Bool("disable-port-check") - + if ctx.Bool("init") { + return ClusterInit(context.Background(), rkeConfig, "") + } _, _, _, _, _, err = ClusterUp(context.Background(), rkeConfig, nil, nil, nil, false, "", updateOnly, disablePortCheck) return err } diff --git a/vendor/github.com/rancher/types/apis/management.cattle.io/v3/rke_types.go b/vendor/github.com/rancher/types/apis/management.cattle.io/v3/rke_types.go index 44c249dc..c05667e9 100644 --- a/vendor/github.com/rancher/types/apis/management.cattle.io/v3/rke_types.go +++ b/vendor/github.com/rancher/types/apis/management.cattle.io/v3/rke_types.go @@ -282,6 +282,8 @@ type IngressConfig struct { type RKEPlan struct { // List of node Plans Nodes []RKEConfigNodePlan `json:"nodes,omitempty"` + // Certificates Key Pair + CertificatesBundle map[string]CertificatePKI `json:"certificatesBundle,omitempty"` } type RKEConfigNodePlan struct { @@ -567,3 +569,30 @@ type MonitoringConfig struct { // Metrics server options Options map[string]string `yaml:"options" json:"options,omitempty"` } + +type CertificatePKI struct { + // Name of the certificate pki + Name string + // Certificate in PEM format + Certificate string + // Key in PEM Format + Key string + // Kubeconfig file + Config string + // CommonName in the certificate + CommonName string + // Organizational Name in the certificate + OUName string + // Environment name of the certificate + EnvName string + // Path of the certificate on disk + Path string + // Environment name of the key + KeyEnvName string + // Path of the key on disk + KeyPath string + // Environment name of the kubeconfig + ConfigEnvName string + // Path of the kubeconfig on disk + ConfigPath string +}