summaryrefslogtreecommitdiffstats
path: root/src/dcm/pkg/module/cluster.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/dcm/pkg/module/cluster.go')
-rw-r--r--src/dcm/pkg/module/cluster.go486
1 files changed, 352 insertions, 134 deletions
diff --git a/src/dcm/pkg/module/cluster.go b/src/dcm/pkg/module/cluster.go
index 38848990..6ad46404 100644
--- a/src/dcm/pkg/module/cluster.go
+++ b/src/dcm/pkg/module/cluster.go
@@ -12,194 +12,412 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-*/
+ */
package module
import (
- pkgerrors "github.com/pkg/errors"
+ "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
type Cluster struct {
- MetaData ClusterMeta `json:"metadata"`
- Specification ClusterSpec `json:"spec"`
+ MetaData ClusterMeta `json:"metadata"`
+ Specification ClusterSpec `json:"spec"`
}
type ClusterMeta struct {
- ClusterReference string `json:"name"`
- Description string `json:"description"`
- UserData1 string `json:"userData1"`
- UserData2 string `json:"userData2"`
+ ClusterReference string `json:"name"`
+ Description string `json:"description"`
+ UserData1 string `json:"userData1"`
+ UserData2 string `json:"userData2"`
}
type ClusterSpec struct {
- ClusterProvider string `json:"cluster-provider"`
- ClusterName string `json:"cluster-name"`
- LoadBalancerIP string `json:"loadbalancer-ip"`
+ ClusterProvider string `json:"cluster-provider"`
+ ClusterName string `json:"cluster-name"`
+ LoadBalancerIP string `json:"loadbalancer-ip"`
+ Certificate string `json:"certificate"`
}
-
type ClusterKey struct {
- Project string `json:"project"`
- LogicalCloudName string `json:"logical-cloud-name"`
- ClusterReference string `json:"clname"`
+ Project string `json:"project"`
+ LogicalCloudName string `json:"logical-cloud-name"`
+ 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 {
- CreateCluster(project, logicalCloud string, c Cluster) (Cluster, error)
- GetCluster(project, logicalCloud, name string) (Cluster, error)
- GetAllClusters(project, logicalCloud string) ([]Cluster, error)
- DeleteCluster(project, logicalCloud, name string) error
- UpdateCluster(project, logicalCloud, name string, c Cluster) (Cluster, error)
+ CreateCluster(project, logicalCloud string, c Cluster) (Cluster, error)
+ GetCluster(project, logicalCloud, name string) (Cluster, error)
+ 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
// It will also be used to maintain some localized state
type ClusterClient struct {
- storeName string
- tagMeta string
- util Utility
+ storeName string
+ tagMeta string
+ util Utility
}
// ClusterClient returns an instance of the ClusterClient
// which implements the ClusterManager
func NewClusterClient() *ClusterClient {
- service := DBService{}
- return &ClusterClient{
- storeName: "orchestrator",
- tagMeta: "cluster",
- util: service,
- }
+ service := DBService{}
+ return &ClusterClient{
+ storeName: "orchestrator",
+ tagMeta: "cluster",
+ util: service,
+ }
}
// Create entry for the cluster reference resource in the database
func (v *ClusterClient) CreateCluster(project, logicalCloud string, c Cluster) (Cluster, error) {
- //Construct key consisting of name
- key := ClusterKey{
- Project: project,
- LogicalCloudName: logicalCloud,
- ClusterReference: c.MetaData.ClusterReference,
- }
-
- //Check if project exists
- err := v.util.CheckProject(project)
- if err != nil {
- return Cluster{}, pkgerrors.New("Unable to find the project")
- }
- //check if logical cloud exists
- err = v.util.CheckLogicalCloud(project, logicalCloud)
- if err != nil {
- return Cluster{}, pkgerrors.New("Unable to find the logical cloud")
- }
- //Check if this Cluster reference already exists
- _, err = v.GetCluster(project, logicalCloud, c.MetaData.ClusterReference)
- if err == nil {
- return Cluster{}, pkgerrors.New("Cluster reference already exists")
- }
-
- err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c)
- if err != nil {
- return Cluster{}, pkgerrors.Wrap(err, "Creating DB Entry")
- }
-
- return c, nil
+ //Construct key consisting of name
+ key := ClusterKey{
+ Project: project,
+ LogicalCloudName: logicalCloud,
+ ClusterReference: c.MetaData.ClusterReference,
+ }
+
+ //Check if project exists
+ err := v.util.CheckProject(project)
+ if err != nil {
+ return Cluster{}, pkgerrors.New("Unable to find the project")
+ }
+ //check if logical cloud exists
+ err = v.util.CheckLogicalCloud(project, logicalCloud)
+ if err != nil {
+ return Cluster{}, pkgerrors.New("Unable to find the logical cloud")
+ }
+ //Check if this Cluster reference already exists
+ _, err = v.GetCluster(project, logicalCloud, c.MetaData.ClusterReference)
+ if err == nil {
+ return Cluster{}, pkgerrors.New("Cluster reference already exists")
+ }
+
+ err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c)
+ if err != nil {
+ return Cluster{}, pkgerrors.Wrap(err, "Creating DB Entry")
+ }
+
+ return c, nil
}
// Get returns Cluster for corresponding cluster reference
-func (v *ClusterClient) GetCluster(project, logicalCloud, clusterReference string)(Cluster, error) {
+func (v *ClusterClient) GetCluster(project, logicalCloud, clusterReference string) (Cluster, error) {
- //Construct the composite key to select the entry
- key := ClusterKey{
- Project: project,
- LogicalCloudName: logicalCloud,
- ClusterReference: clusterReference,
- }
+ //Construct the composite key to select the entry
+ key := ClusterKey{
+ Project: project,
+ LogicalCloudName: logicalCloud,
+ ClusterReference: clusterReference,
+ }
- value, err := v.util.DBFind(v.storeName, key, v.tagMeta)
- if err != nil {
- return Cluster{}, pkgerrors.Wrap(err, "Get Cluster reference")
- }
+ value, err := v.util.DBFind(v.storeName, key, v.tagMeta)
+ if err != nil {
+ return Cluster{}, pkgerrors.Wrap(err, "Get Cluster reference")
+ }
- //value is a byte array
- if value != nil {
- cl := Cluster{}
- err = v.util.DBUnmarshal(value[0], &cl)
- if err != nil {
- return Cluster{}, pkgerrors.Wrap(err, "Unmarshaling value")
- }
- return cl, nil
- }
+ //value is a byte array
+ if value != nil {
+ cl := Cluster{}
+ err = v.util.DBUnmarshal(value[0], &cl)
+ if err != nil {
+ return Cluster{}, pkgerrors.Wrap(err, "Unmarshaling value")
+ }
+ return cl, nil
+ }
- return Cluster{}, pkgerrors.New("Error getting Cluster")
+ return Cluster{}, pkgerrors.New("Cluster Reference does not exist")
}
-
// GetAll returns all cluster references in the logical cloud
-func (v *ClusterClient) GetAllClusters(project, logicalCloud string)([]Cluster, error) {
- //Construct the composite key to select clusters
- key := ClusterKey{
- Project: project,
- LogicalCloudName: logicalCloud,
- ClusterReference: "",
- }
- var resp []Cluster
- values, err := v.util.DBFind(v.storeName, key, v.tagMeta)
- if err != nil {
- return []Cluster{}, pkgerrors.Wrap(err, "Get All Cluster references")
- }
-
- for _, value := range values {
- cl := Cluster{}
- err = v.util.DBUnmarshal(value, &cl)
- if err != nil {
- return []Cluster{}, pkgerrors.Wrap(err, "Unmarshaling values")
- }
- resp = append(resp, cl)
- }
-
- return resp, nil
+func (v *ClusterClient) GetAllClusters(project, logicalCloud string) ([]Cluster, error) {
+ //Construct the composite key to select clusters
+ key := ClusterKey{
+ Project: project,
+ LogicalCloudName: logicalCloud,
+ ClusterReference: "",
+ }
+ var resp []Cluster
+ values, err := v.util.DBFind(v.storeName, key, v.tagMeta)
+ if err != nil {
+ return []Cluster{}, pkgerrors.Wrap(err, "Get All Cluster references")
+ }
+ if len(values) == 0 {
+ return []Cluster{}, pkgerrors.New("No Cluster References associated")
+ }
+
+ for _, value := range values {
+ cl := Cluster{}
+ err = v.util.DBUnmarshal(value, &cl)
+ if err != nil {
+ return []Cluster{}, pkgerrors.Wrap(err, "Unmarshaling values")
+ }
+ resp = append(resp, cl)
+ }
+
+ return resp, nil
}
// Delete the Cluster reference entry from database
func (v *ClusterClient) DeleteCluster(project, logicalCloud, clusterReference string) error {
- //Construct the composite key to select the entry
- key := ClusterKey{
- Project: project,
- LogicalCloudName: logicalCloud,
- ClusterReference: clusterReference,
- }
- err := v.util.DBRemove(v.storeName, key)
- if err != nil {
- return pkgerrors.Wrap(err, "Delete Cluster Reference")
- }
- return nil
+ //Construct the composite key to select the entry
+ key := ClusterKey{
+ Project: project,
+ LogicalCloudName: logicalCloud,
+ ClusterReference: clusterReference,
+ }
+ err := v.util.DBRemove(v.storeName, key)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Delete Cluster Reference")
+ }
+ return nil
}
// Update an entry for the Cluster reference in the database
func (v *ClusterClient) UpdateCluster(project, logicalCloud, clusterReference string, c Cluster) (Cluster, error) {
- key := ClusterKey{
- Project: project,
- LogicalCloudName: logicalCloud,
- ClusterReference: clusterReference,
- }
-
- //Check for name mismatch in cluster reference
- if c.MetaData.ClusterReference != clusterReference {
- return Cluster{}, pkgerrors.New("Update Error - Cluster reference mismatch")
- }
- //Check if this Cluster reference exists
- _, err := v.GetCluster(project, logicalCloud, clusterReference)
- if err != nil {
- return Cluster{}, pkgerrors.New("Update Error - Cluster reference doesn't exist")
- }
- err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c)
- if err != nil {
- return Cluster{}, pkgerrors.Wrap(err, "Updating DB Entry")
- }
- return c, nil
+ key := ClusterKey{
+ Project: project,
+ LogicalCloudName: logicalCloud,
+ ClusterReference: clusterReference,
+ }
+
+ //Check for name mismatch in cluster reference
+ if c.MetaData.ClusterReference != clusterReference {
+ return Cluster{}, pkgerrors.New("Cluster Reference mismatch")
+ }
+ //Check if this Cluster reference exists
+ _, err := v.GetCluster(project, logicalCloud, clusterReference)
+ if err != nil {
+ return Cluster{}, pkgerrors.New("Cluster Reference does not exist")
+ }
+ err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c)
+ if err != nil {
+ return Cluster{}, pkgerrors.Wrap(err, "Updating DB Entry")
+ }
+ return c, nil
+}
+
+// Get returns Cluster's kubeconfig for corresponding cluster reference
+func (v *ClusterClient) GetClusterConfig(project, logicalCloud, clusterReference string) (string, error) {
+ lcClient := NewLogicalCloudClient()
+ lckey := LogicalCloudKey{
+ Project: project,
+ LogicalCloudName: logicalCloud,
+ }
+ context, ctxVal, err := lcClient.util.GetLogicalCloudContext(lcClient.storeName, lckey, lcClient.tagMeta, project, 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")
+ }
+
+ // 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.")
+ }
+
+ if len(rbstatus.CsrStatuses) == 0 {
+ return "", pkgerrors.New("The certificate for this cluster hasn't been issued yet. Please try later.")
+ }
+
+ // 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
}