diff options
Diffstat (limited to 'src/dcm/pkg/module/cluster.go')
-rw-r--r-- | src/dcm/pkg/module/cluster.go | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/src/dcm/pkg/module/cluster.go b/src/dcm/pkg/module/cluster.go index 85b20117..6cb18539 100644 --- a/src/dcm/pkg/module/cluster.go +++ b/src/dcm/pkg/module/cluster.go @@ -17,7 +17,15 @@ package module import ( + "encoding/base64" + "encoding/json" + "strings" + + clm "github.com/onap/multicloud-k8s/src/clm/pkg/cluster" + rb "github.com/onap/multicloud-k8s/src/monitor/pkg/apis/k8splugin/v1alpha1" + log "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/logutils" pkgerrors "github.com/pkg/errors" + "gopkg.in/yaml.v2" ) // Cluster contains the parameters needed for a Cluster @@ -37,6 +45,7 @@ type ClusterSpec struct { ClusterProvider string `json:"cluster-provider"` ClusterName string `json:"cluster-name"` LoadBalancerIP string `json:"loadbalancer-ip"` + Certificate string `json:"certificate"` } type ClusterKey struct { @@ -45,6 +54,48 @@ type ClusterKey struct { ClusterReference string `json:"clname"` } +type KubeConfig struct { + ApiVersion string `yaml:"apiVersion"` + Kind string `yaml:"kind"` + Clusters []KubeCluster `yaml:"clusters"` + Contexts []KubeContext `yaml:"contexts"` + CurrentContext string `yaml:"current-context` + Preferences map[string]string `yaml:"preferences"` + Users []KubeUser `yaml:"users"` +} + +type KubeCluster struct { + ClusterDef KubeClusterDef `yaml:"cluster"` + ClusterName string `yaml:"name"` +} + +type KubeClusterDef struct { + CertificateAuthorityData string `yaml:"certificate-authority-data"` + Server string `yaml:"server"` +} + +type KubeContext struct { + ContextDef KubeContextDef `yaml:"context"` + ContextName string `yaml:"name"` +} + +type KubeContextDef struct { + Cluster string `yaml:"cluster"` + Namespace string `yaml:"namespace,omitempty"` + User string `yaml:"user"` +} + +type KubeUser struct { + UserName string `yaml:"name"` + UserDef KubeUserDef `yaml:"user"` +} + +type KubeUserDef struct { + ClientCertificateData string `yaml:"client-certificate-data"` + ClientKeyData string `yaml:"client-key-data"` + // client-certificate and client-key are NOT implemented +} + // ClusterManager is an interface that exposes the connection // functionality type ClusterManager interface { @@ -53,6 +104,7 @@ type ClusterManager interface { GetAllClusters(project, logicalCloud string) ([]Cluster, error) DeleteCluster(project, logicalCloud, name string) error UpdateCluster(project, logicalCloud, name string, c Cluster) (Cluster, error) + GetClusterConfig(project, logicalcloud, name string) (string, error) } // ClusterClient implements the ClusterManager @@ -204,3 +256,165 @@ func (v *ClusterClient) UpdateCluster(project, logicalCloud, clusterReference st } return c, nil } + +// Get returns Cluster's kubeconfig for corresponding cluster reference +func (v *ClusterClient) GetClusterConfig(project, logicalCloud, clusterReference string) (string, error) { + lcClient := NewLogicalCloudClient() + context, ctxVal, err := lcClient.GetLogicalCloudContext(logicalCloud) + if err != nil { + return "", pkgerrors.Wrap(err, "Error getting logical cloud context.") + } + if ctxVal == "" { + return "", pkgerrors.New("Logical Cloud hasn't been applied yet") + } + + // private key comes from logical cloud + lckey := LogicalCloudKey{ + Project: project, + LogicalCloudName: logicalCloud, + } + // get logical cloud resource + lc, err := lcClient.Get(project, logicalCloud) + if err != nil { + return "", pkgerrors.Wrap(err, "Failed getting logical cloud") + } + // get user's private key + privateKeyData, err := v.util.DBFind(v.storeName, lckey, "privatekey") + if err != nil { + return "", pkgerrors.Wrap(err, "Failed getting private key from logical cloud") + } + + // get cluster from dcm (need provider/name) + cluster, err := v.GetCluster(project, logicalCloud, clusterReference) + if err != nil { + return "", pkgerrors.Wrap(err, "Failed getting cluster") + } + + // before attempting to generate a kubeconfig, + // check if certificate has been issued and copy it from etcd to mongodb + if cluster.Specification.Certificate == "" { + log.Info("Certificate not yet in MongoDB, checking etcd.", log.Fields{}) + + // access etcd + clusterName := strings.Join([]string{cluster.Specification.ClusterProvider, "+", cluster.Specification.ClusterName}, "") + + // get the app context handle for the status of this cluster (which should contain the certificate inside, if already issued) + statusHandle, err := context.GetClusterStatusHandle("logical-cloud", clusterName) + + if err != nil { + return "", pkgerrors.New("The cluster doesn't contain status, please check if all services are up and running.") + } + statusRaw, err := context.GetValue(statusHandle) + if err != nil { + return "", pkgerrors.Wrap(err, "An error occurred while reading the cluster status.") + } + + var rbstatus rb.ResourceBundleStatus + err = json.Unmarshal([]byte(statusRaw.(string)), &rbstatus) + if err != nil { + return "", pkgerrors.Wrap(err, "An error occurred while parsing the cluster status.") + } + + // validate that we indeed obtained a certificate before persisting it in the database: + approved := false + for _, c := range rbstatus.CsrStatuses[0].Status.Conditions { + if c.Type == "Denied" { + return "", pkgerrors.Wrap(err, "Certificate was denied!") + } + if c.Type == "Failed" { + return "", pkgerrors.Wrap(err, "Certificate issue failed.") + } + if c.Type == "Approved" { + approved = true + } + } + if approved { + //just double-check certificate field contents aren't empty: + cert := rbstatus.CsrStatuses[0].Status.Certificate + if len(cert) > 0 { + cluster.Specification.Certificate = base64.StdEncoding.EncodeToString([]byte(cert)) + } else { + return "", pkgerrors.Wrap(err, "Certificate issued was invalid.") + } + } + + // copy key to MongoDB + // func (v *ClusterClient) + // UpdateCluster(project, logicalCloud, clusterReference string, c Cluster) (Cluster, error) { + _, err = v.UpdateCluster(project, logicalCloud, clusterReference, cluster) + if err != nil { + return "", pkgerrors.Wrap(err, "An error occurred while storing the certificate.") + } + } else { + // certificate is already in MongoDB so just hand it over to create the API response + log.Info("Certificate already in MongoDB, pass it to API.", log.Fields{}) + } + + // contact clm about admins cluster kubeconfig (to retrieve CA cert) + clusterContent, err := clm.NewClusterClient().GetClusterContent(cluster.Specification.ClusterProvider, cluster.Specification.ClusterName) + if err != nil { + return "", pkgerrors.Wrap(err, "Failed getting cluster content from CLM") + } + adminConfig, err := base64.StdEncoding.DecodeString(clusterContent.Kubeconfig) + if err != nil { + return "", pkgerrors.Wrap(err, "Failed decoding CLM kubeconfig from base64") + } + + // unmarshall clm kubeconfig into struct + adminKubeConfig := KubeConfig{} + err = yaml.Unmarshal(adminConfig, &adminKubeConfig) + if err != nil { + return "", pkgerrors.Wrap(err, "Failed parsing CLM kubeconfig yaml") + } + + // all data needed for final kubeconfig: + privateKey := string(privateKeyData[0]) + signedCert := cluster.Specification.Certificate + clusterCert := adminKubeConfig.Clusters[0].ClusterDef.CertificateAuthorityData + clusterAddr := adminKubeConfig.Clusters[0].ClusterDef.Server + namespace := lc.Specification.NameSpace + userName := lc.Specification.User.UserName + contextName := userName + "@" + clusterReference + + kubeconfig := KubeConfig{ + ApiVersion: "v1", + Kind: "Config", + Clusters: []KubeCluster{ + KubeCluster{ + ClusterName: clusterReference, + ClusterDef: KubeClusterDef{ + CertificateAuthorityData: clusterCert, + Server: clusterAddr, + }, + }, + }, + Contexts: []KubeContext{ + KubeContext{ + ContextName: contextName, + ContextDef: KubeContextDef{ + Cluster: clusterReference, + Namespace: namespace, + User: userName, + }, + }, + }, + CurrentContext: contextName, + Preferences: map[string]string{}, + Users: []KubeUser{ + KubeUser{ + UserName: userName, + UserDef: KubeUserDef{ + ClientCertificateData: signedCert, + ClientKeyData: privateKey, + }, + }, + }, + } + + yaml, err := yaml.Marshal(&kubeconfig) + if err != nil { + return "", pkgerrors.Wrap(err, "Failed marshaling user kubeconfig into yaml") + } + + return string(yaml), nil +} |