diff options
Diffstat (limited to 'src/dcm')
-rw-r--r-- | src/dcm/api/api.go | 4 | ||||
-rw-r--r-- | src/dcm/api/clusterHandler.go | 10 | ||||
-rw-r--r-- | src/dcm/api/keyValueHandler.go | 10 | ||||
-rw-r--r-- | src/dcm/api/logicalCloudHandler.go | 75 | ||||
-rw-r--r-- | src/dcm/api/quotaHandler.go | 10 | ||||
-rw-r--r-- | src/dcm/api/userPermissionsHandler.go | 12 | ||||
-rw-r--r-- | src/dcm/config.json | 14 | ||||
-rw-r--r-- | src/dcm/go.mod | 2 | ||||
-rw-r--r-- | src/dcm/pkg/module/apply.go | 268 | ||||
-rw-r--r-- | src/dcm/pkg/module/cluster.go | 9 | ||||
-rw-r--r-- | src/dcm/pkg/module/keyvalue.go | 4 | ||||
-rw-r--r-- | src/dcm/pkg/module/logicalcloud.go | 53 | ||||
-rw-r--r-- | src/dcm/pkg/module/logicalcloud_test.go | 16 | ||||
-rw-r--r-- | src/dcm/pkg/module/quota.go | 10 | ||||
-rw-r--r-- | src/dcm/pkg/module/userpermissions.go | 4 |
15 files changed, 448 insertions, 53 deletions
diff --git a/src/dcm/api/api.go b/src/dcm/api/api.go index de1d5c97..0f68a517 100644 --- a/src/dcm/api/api.go +++ b/src/dcm/api/api.go @@ -44,6 +44,7 @@ func NewRouter( quotaClient = module.NewQuotaClient() } + // Set up Logical Cloud API logicalCloudHandler := logicalCloudHandler{client: logicalCloudClient, clusterClient: clusterClient, quotaClient: quotaClient, @@ -67,6 +68,9 @@ func NewRouter( lcRouter.HandleFunc( "/logical-clouds/{logical-cloud-name}/apply", logicalCloudHandler.applyHandler).Methods("POST") + lcRouter.HandleFunc( + "/logical-clouds/{logical-cloud-name}/terminate", + logicalCloudHandler.terminateHandler).Methods("POST") // To Do // get kubeconfig /*lcRouter.HandleFunc( diff --git a/src/dcm/api/clusterHandler.go b/src/dcm/api/clusterHandler.go index f4a3abdc..d0c1e62c 100644 --- a/src/dcm/api/clusterHandler.go +++ b/src/dcm/api/clusterHandler.go @@ -91,6 +91,10 @@ func (h clusterHandler) getHandler(w http.ResponseWriter, r *http.Request) { } else { ret, err = h.client.GetCluster(project, logicalCloud, name) if err != nil { + if err.Error() == "Cluster Reference does not exist" { + http.Error(w, err.Error(), http.StatusNotFound) + return + } http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -131,12 +135,16 @@ func (h clusterHandler) updateHandler(w http.ResponseWriter, r *http.Request) { ret, err := h.client.UpdateCluster(project, logicalCloud, name, v) if err != nil { + if err.Error() == "Cluster Reference does not exist" { + http.Error(w, err.Error(), http.StatusNotFound) + return + } http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) + w.WriteHeader(http.StatusOK) err = json.NewEncoder(w).Encode(ret) if err != nil { http.Error(w, err.Error(), diff --git a/src/dcm/api/keyValueHandler.go b/src/dcm/api/keyValueHandler.go index c67504f2..a4a4f14a 100644 --- a/src/dcm/api/keyValueHandler.go +++ b/src/dcm/api/keyValueHandler.go @@ -90,6 +90,10 @@ func (h keyValueHandler) getHandler(w http.ResponseWriter, r *http.Request) { } else { ret, err = h.client.GetKVPair(project, logicalCloud, name) if err != nil { + if err.Error() == "KV Pair does not exist" { + http.Error(w, err.Error(), http.StatusNotFound) + return + } http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -130,12 +134,16 @@ func (h keyValueHandler) updateHandler(w http.ResponseWriter, r *http.Request) { ret, err := h.client.UpdateKVPair(project, logicalCloud, name, v) if err != nil { + if err.Error() == "KV Pair does not exist" { + http.Error(w, err.Error(), http.StatusNotFound) + return + } http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) + w.WriteHeader(http.StatusOK) err = json.NewEncoder(w).Encode(ret) if err != nil { http.Error(w, err.Error(), diff --git a/src/dcm/api/logicalCloudHandler.go b/src/dcm/api/logicalCloudHandler.go index d9a3e5f5..fb0f0c63 100644 --- a/src/dcm/api/logicalCloudHandler.go +++ b/src/dcm/api/logicalCloudHandler.go @@ -25,6 +25,7 @@ import ( "github.com/gorilla/mux" "github.com/onap/multicloud-k8s/src/dcm/pkg/module" + pkgerrors "github.com/pkg/errors" ) // logicalCloudHandler is used to store backend implementations objects @@ -91,6 +92,10 @@ func (h logicalCloudHandler) getHandler(w http.ResponseWriter, r *http.Request) } else { ret, err = h.client.Get(project, name) if err != nil { + if err.Error() == "Logical Cloud does not exist" { + http.Error(w, err.Error(), http.StatusNotFound) + return + } http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -129,6 +134,10 @@ func (h logicalCloudHandler) updateHandler(w http.ResponseWriter, r *http.Reques ret, err := h.client.Update(project, name, v) if err != nil { + if err.Error() == "Logical Cloud does not exist" { + http.Error(w, err.Error(), http.StatusNotFound) + return + } http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -150,6 +159,10 @@ func (h logicalCloudHandler) deleteHandler(w http.ResponseWriter, r *http.Reques err := h.client.Delete(project, name) if err != nil { + if err.Error() == "Logical Cloud does not exist" { + http.Error(w, err.Error(), http.StatusNotFound) + return + } http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -165,14 +178,29 @@ func (h logicalCloudHandler) applyHandler(w http.ResponseWriter, r *http.Request // Get logical cloud lc, err := h.client.Get(project, name) if err != nil { + if err.Error() == "Logical Cloud does not exist" { + http.Error(w, err.Error(), http.StatusNotFound) + return + } http.Error(w, err.Error(), http.StatusInternalServerError) return } + _, ctxVal, err := h.client.GetLogicalCloudContext(name) + if ctxVal != "" { + err = pkgerrors.New("Logical Cloud already applied") + http.Error(w, err.Error(), http.StatusConflict) + return + } + // Get Clusters clusters, err := h.clusterClient.GetAllClusters(project, name) if err != nil { + if err.Error() == "No Cluster References associated" { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -192,3 +220,50 @@ func (h logicalCloudHandler) applyHandler(w http.ResponseWriter, r *http.Request return } + +func (h logicalCloudHandler) terminateHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + project := vars["project-name"] + name := vars["logical-cloud-name"] + + // Get logical cloud + lc, err := h.client.Get(project, name) + if err != nil { + if err.Error() == "Logical Cloud does not exist" { + http.Error(w, err.Error(), http.StatusNotFound) + return + } + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + _, ctxVal, err := h.client.GetLogicalCloudContext(name) + if ctxVal == "" { + err = pkgerrors.New("Logical Cloud hasn't been applied yet") + http.Error(w, err.Error(), http.StatusConflict) + return + } + + // Get Clusters + clusters, err := h.clusterClient.GetAllClusters(project, name) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + //Get Quotas + quotas, err := h.quotaClient.GetAllQuotas(project, name) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + err = module.DestroyEtcdContext(lc, clusters, quotas) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + return +} diff --git a/src/dcm/api/quotaHandler.go b/src/dcm/api/quotaHandler.go index deb18e18..fd9b40f8 100644 --- a/src/dcm/api/quotaHandler.go +++ b/src/dcm/api/quotaHandler.go @@ -91,6 +91,10 @@ func (h quotaHandler) getHandler(w http.ResponseWriter, r *http.Request) { } else { ret, err = h.client.GetQuota(project, logicalCloud, name) if err != nil { + if err.Error() == "Cluster Quota does not exist" { + http.Error(w, err.Error(), http.StatusNotFound) + return + } http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -131,12 +135,16 @@ func (h quotaHandler) updateHandler(w http.ResponseWriter, r *http.Request) { ret, err := h.client.UpdateQuota(project, logicalCloud, name, v) if err != nil { + if err.Error() == "Cluster Quota does not exist" { + http.Error(w, err.Error(), http.StatusNotFound) + return + } http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) + w.WriteHeader(http.StatusOK) err = json.NewEncoder(w).Encode(ret) if err != nil { http.Error(w, err.Error(), diff --git a/src/dcm/api/userPermissionsHandler.go b/src/dcm/api/userPermissionsHandler.go index 156c390f..3ac955fa 100644 --- a/src/dcm/api/userPermissionsHandler.go +++ b/src/dcm/api/userPermissionsHandler.go @@ -89,8 +89,12 @@ func (h userPermissionHandler) getHandler(w http.ResponseWriter, r *http.Request return } } else { - ret, err = h.client.GetAllUserPerms(project, logicalCloud) + ret, err = h.client.GetUserPerm(project, logicalCloud, name) if err != nil { + if err.Error() == "User Permission does not exist" { + http.Error(w, err.Error(), http.StatusNotFound) + return + } http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -131,12 +135,16 @@ func (h userPermissionHandler) updateHandler(w http.ResponseWriter, r *http.Requ ret, err := h.client.UpdateUserPerm(project, logicalCloud, name, v) if err != nil { + if err.Error() == "User Permission does not exist" { + http.Error(w, err.Error(), http.StatusNotFound) + return + } http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) + w.WriteHeader(http.StatusOK) err = json.NewEncoder(w).Encode(ret) if err != nil { http.Error(w, err.Error(), diff --git a/src/dcm/config.json b/src/dcm/config.json new file mode 100644 index 00000000..65a18acb --- /dev/null +++ b/src/dcm/config.json @@ -0,0 +1,14 @@ +{ + "database-ip": "172.18.0.2", + "database-type": "mongo", + "plugin-dir": "plugins", + "service-port": "9077", + "ca-file": "ca.cert", + "server-cert": "server.cert", + "server-key": "server.key", + "password": "", + "etcd-ip": "172.18.0.3", + "etcd-cert": "", + "etcd-key": "", + "etcd-ca-file": "" +} diff --git a/src/dcm/go.mod b/src/dcm/go.mod index 35f64d80..1f04ac12 100644 --- a/src/dcm/go.mod +++ b/src/dcm/go.mod @@ -8,6 +8,8 @@ require ( github.com/russross/blackfriday/v2 v2.0.1 github.com/stretchr/testify v1.5.1 gopkg.in/yaml.v2 v2.2.8 + k8s.io/api v0.18.2 + k8s.io/apimachinery v0.18.2 k8s.io/kubernetes v1.16.9 ) diff --git a/src/dcm/pkg/module/apply.go b/src/dcm/pkg/module/apply.go index dbcbf8ac..a866934a 100644 --- a/src/dcm/pkg/module/apply.go +++ b/src/dcm/pkg/module/apply.go @@ -28,11 +28,20 @@ import ( "strings" "github.com/onap/multicloud-k8s/src/orchestrator/pkg/appcontext" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/appcontext/subresources" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/grpc/installappclient" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" log "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/logutils" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module/controller" pkgerrors "github.com/pkg/errors" "gopkg.in/yaml.v2" + certificatesv1beta1 "k8s.io/api/certificates/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// rsyncName denotes the name of the rsync controller +const rsyncName = "rsync" + type Resource struct { ApiVersion string `yaml:"apiVersion"` Kind string `yaml:"kind"` @@ -51,8 +60,10 @@ type MetaDatas struct { type Specs struct { Request string `yaml:"request,omitempty"` Usages []string `yaml:"usages,omitempty"` - //Hard logicalcloud.QSpec `yaml:"hard,omitempty"` - Hard QSpec `yaml:"hard,omitempty"` + // TODO: validate quota keys + // //Hard logicalcloud.QSpec `yaml:"hard,omitempty"` + // Hard QSpec `yaml:"hard,omitempty"` + Hard map[string]string `yaml:"hard,omitempty"` } type RoleRules struct { @@ -147,12 +158,10 @@ func createRoleBinding(logicalcloud LogicalCloud) (string, error) { } return string(rBData), nil - } func createQuota(quota []Quota, namespace string) (string, error) { lcQuota := quota[0] - q := Resource{ ApiVersion: "v1", Kind: "ResourceQuota", @@ -171,23 +180,22 @@ func createQuota(quota []Quota, namespace string) (string, error) { } return string(qData), nil - } -func createUserCSR(logicalcloud LogicalCloud) (string, error) { +func createUserCSR(logicalcloud LogicalCloud) (string, string, error) { KEYSIZE := 4096 userName := logicalcloud.Specification.User.UserName key, err := rsa.GenerateKey(rand.Reader, KEYSIZE) if err != nil { - return "", err + return "", "", err } csrTemplate := x509.CertificateRequest{Subject: pkix.Name{CommonName: userName}} csrCert, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, key) if err != nil { - return "", err + return "", "", err } //Encode csr @@ -200,8 +208,8 @@ func createUserCSR(logicalcloud LogicalCloud) (string, error) { ApiVersion: "certificates.k8s.io/v1beta1", Kind: "CertificateSigningRequest", MetaData: MetaDatas{ - Name: strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "-user-csr"}, ""), - Namespace: logicalcloud.Specification.NameSpace, + Name: strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "-user-csr"}, ""), + // Namespace: logicalcloud.Specification.NameSpace, }, Specification: Specs{ Request: base64.StdEncoding.EncodeToString(csr), @@ -211,27 +219,98 @@ func createUserCSR(logicalcloud LogicalCloud) (string, error) { csrData, err := yaml.Marshal(&csrObj) if err != nil { - return "", err + return "", "", err } - return string(csrData), nil + keyData := base64.StdEncoding.EncodeToString(pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + }, + )) + if err != nil { + return "", "", err + } + return string(csrData), string(keyData), nil } -// TODO: -// Install istio -// Store user key for user creation -// Code to run kubectl commands for user -// kubectl certificate approve lc1-user-cert -// kubectl get csr lc1-user-cert -o jsonpath='{.status.certificate}' | base64 --decode > user.crt -// kubectl config set-credentials user --client-certificate=<user.crt> --client-key=<user.key> -// kubectl config set-context user-context --cluster=cluster-name --namespace=lc1 --user=user +func createApprovalSubresource(logicalcloud LogicalCloud) (string, error) { + subresource := subresources.ApprovalSubresource{ + Message: "Approved for Logical Cloud authentication", + Reason: "LogicalCloud", + Type: string(certificatesv1beta1.CertificateApproved), + LastUpdateTime: metav1.Now().Format("2006-01-02T15:04:05Z"), + } + csrData, err := json.Marshal(subresource) + return string(csrData), err +} + +/* +queryDBAndSetRsyncInfo queries the MCO db to find the record the sync controller +and then sets the RsyncInfo global variable. +*/ +func queryDBAndSetRsyncInfo() (installappclient.RsyncInfo, error) { + client := controller.NewControllerClient() + vals, _ := client.GetControllers() + for _, v := range vals { + if v.Metadata.Name == rsyncName { + log.Info("Initializing RPC connection to resource synchronizer", log.Fields{ + "Controller": v.Metadata.Name, + }) + rsyncInfo := installappclient.NewRsyncInfo(v.Metadata.Name, v.Spec.Host, v.Spec.Port) + return rsyncInfo, nil + } + } + return installappclient.RsyncInfo{}, pkgerrors.Errorf("queryRsyncInfoInMCODB Failed - Could not get find rsync by name : %v", rsyncName) +} + +/* +callRsyncInstall method shall take in the app context id and invokes the rsync service via grpc +*/ +func callRsyncInstall(contextid interface{}) error { + rsyncInfo, err := queryDBAndSetRsyncInfo() + log.Info("Calling the Rsync ", log.Fields{ + "RsyncName": rsyncInfo.RsyncName, + }) + if err != nil { + return err + } + + appContextID := fmt.Sprintf("%v", contextid) + err = installappclient.InvokeInstallApp(appContextID) + if err != nil { + return err + } + return nil +} + +/* +callRsyncUninstall method shall take in the app context id and invokes the rsync service via grpc +*/ +func callRsyncUninstall(contextid interface{}) error { + rsyncInfo, err := queryDBAndSetRsyncInfo() + log.Info("Calling the Rsync ", log.Fields{ + "RsyncName": rsyncInfo.RsyncName, + }) + if err != nil { + return err + } + + appContextID := fmt.Sprintf("%v", contextid) + err = installappclient.InvokeUninstallApp(appContextID) + if err != nil { + return err + } + return nil +} func CreateEtcdContext(logicalcloud LogicalCloud, clusterList []Cluster, quotaList []Quota) error { APP := "logical-cloud" logicalCloudName := logicalcloud.MetaData.LogicalCloudName + project := "test-project" // FIXME(igordc): temporary, need to do some rework in the LC structs //Resource Names namespaceName := strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "+namespace"}, "") @@ -261,11 +340,13 @@ func CreateEtcdContext(logicalcloud LogicalCloud, clusterList []Cluster, return pkgerrors.Wrap(err, "Error Creating Quota YAML for logical cloud") } - csr, err := createUserCSR(logicalcloud) + csr, key, err := createUserCSR(logicalcloud) if err != nil { - return pkgerrors.Wrap(err, "Error Creating User CSR for logical cloud") + return pkgerrors.Wrap(err, "Error Creating User CSR and Key for logical cloud") } + approval, err := createApprovalSubresource(logicalcloud) + context := appcontext.AppContext{} ctxVal, err := context.InitAppContext() if err != nil { @@ -322,7 +403,7 @@ func CreateEtcdContext(logicalcloud LogicalCloud, clusterList []Cluster, } // Add csr resource to each cluster - _, err = context.AddResource(clusterHandle, csrName, csr) + csrHandle, err := context.AddResource(clusterHandle, csrName, csr) if err != nil { cleanuperr := context.DeleteCompositeApp() if cleanuperr != nil { @@ -335,6 +416,36 @@ func CreateEtcdContext(logicalcloud LogicalCloud, clusterList []Cluster, return pkgerrors.Wrap(err, "Error adding CSR Resource to AppContext") } + // Add csr approval as a subresource of csr: + _, err = context.AddLevelValue(csrHandle, "subresource/approval", approval) + if err != nil { + cleanuperr := context.DeleteCompositeApp() + if cleanuperr != nil { + log.Warn("Error cleaning AppContext after add CSR approval failure", log.Fields{ + "cluster-provider": cluster.Specification.ClusterProvider, + "cluster": cluster.Specification.ClusterName, + "logical-cloud": logicalCloudName, + }) + } + return pkgerrors.Wrap(err, "Error approving CSR via AppContext") + } + + // Add private key to MongoDB + lckey := LogicalCloudKey{ + LogicalCloudName: logicalcloud.MetaData.LogicalCloudName, + Project: project, + } + err = db.DBconn.Insert("orchestrator", lckey, nil, "privatekey", key) + if err != nil { + cleanuperr := context.DeleteCompositeApp() + if cleanuperr != nil { + log.Warn("Error cleaning AppContext after DB insert failure", log.Fields{ + "logical-cloud": logicalcloud.MetaData.LogicalCloudName, + }) + } + return pkgerrors.Wrap(err, "Error adding private key to DB") + } + // Add Role resource to each cluster _, err = context.AddResource(clusterHandle, roleName, role) if err != nil { @@ -377,16 +488,29 @@ func CreateEtcdContext(logicalcloud LogicalCloud, clusterList []Cluster, return pkgerrors.Wrap(err, "Error adding quota Resource to AppContext") } + // Add Subresource Order and Subresource Dependency + subresOrder, err := json.Marshal(map[string][]string{"subresorder": []string{"approval"}}) + if err != nil { + return pkgerrors.Wrap(err, "Error creating subresource order JSON") + } + subresDependency, err := json.Marshal(map[string]map[string]string{"subresdependency": map[string]string{"approval": "go"}}) + // Add Resource Order and Resource Dependency resOrder, err := json.Marshal(map[string][]string{"resorder": []string{namespaceName, quotaName, csrName, roleName, roleBindingName}}) if err != nil { return pkgerrors.Wrap(err, "Error creating resource order JSON") } - resDependency, err := json.Marshal(map[string]map[string]string{"resdependency": map[string]string{namespaceName: "go", quotaName: strings.Join([]string{"wait on ", namespaceName}, ""), csrName: strings.Join([]string{"wait on ", quotaName}, ""), roleName: strings.Join([]string{"wait on ", csrName}, ""), roleBindingName: strings.Join([]string{"wait on ", roleName}, "")}}) + // Add App Order and App Dependency + appOrder, err := json.Marshal(map[string][]string{"apporder": []string{APP}}) + if err != nil { + return pkgerrors.Wrap(err, "Error creating resource order JSON") + } + appDependency, err := json.Marshal(map[string]map[string]string{"appdependency": map[string]string{APP: "go"}}) + if err != nil { return pkgerrors.Wrap(err, "Error creating resource dependency JSON") } @@ -417,8 +541,104 @@ func CreateEtcdContext(logicalcloud LogicalCloud, clusterList []Cluster, return pkgerrors.Wrap(err, "Error adding instruction dependency to AppContext") } + _, err = context.AddInstruction(csrHandle, "subresource", "order", string(subresOrder)) + if err != nil { + cleanuperr := context.DeleteCompositeApp() + if cleanuperr != nil { + log.Warn("Error cleaning AppContext after add instruction failure", log.Fields{ + "cluster-provider": cluster.Specification.ClusterProvider, + "cluster": cluster.Specification.ClusterName, + "logical-cloud": logicalCloudName, + }) + } + return pkgerrors.Wrap(err, "Error adding instruction order to AppContext") + } + + _, err = context.AddInstruction(csrHandle, "subresource", "dependency", string(subresDependency)) + if err != nil { + cleanuperr := context.DeleteCompositeApp() + if cleanuperr != nil { + log.Warn("Error cleaning AppContext after add instruction failure", log.Fields{ + "cluster-provider": cluster.Specification.ClusterProvider, + "cluster": cluster.Specification.ClusterName, + "logical-cloud": logicalCloudName, + }) + } + return pkgerrors.Wrap(err, "Error adding instruction dependency to AppContext") + } + + // Add App-level Order and Dependency + _, err = context.AddInstruction(handle, "app", "order", string(appOrder)) + _, err = context.AddInstruction(handle, "app", "dependency", string(appDependency)) + } + // save the context in the logicalcloud db record + lckey := LogicalCloudKey{ + LogicalCloudName: logicalcloud.MetaData.LogicalCloudName, + Project: project, + } + err = db.DBconn.Insert("orchestrator", lckey, nil, "lccontext", ctxVal) + if err != nil { + cleanuperr := context.DeleteCompositeApp() + if cleanuperr != nil { + log.Warn("Error cleaning AppContext after DB insert failure", log.Fields{ + "logical-cloud": logicalcloud.MetaData.LogicalCloudName, + }) + } + return pkgerrors.Wrap(err, "Error adding AppContext to DB") + } + + // call resource synchronizer to instantiate the CRs in the cluster + err = callRsyncInstall(ctxVal) + if err != nil { + return err } return nil } + +// TODO: rename these methods +// DestroyEtcdContext remove from rsync then delete appcontext and all resources +func DestroyEtcdContext(logicalcloud LogicalCloud, clusterList []Cluster, + quotaList []Quota) error { + + logicalCloudName := logicalcloud.MetaData.LogicalCloudName + // project := "test-project" // FIXME(igordc): temporary, need to do some rework in the LC structs + + _, ctxVal, err := NewLogicalCloudClient().GetLogicalCloudContext(logicalCloudName) + if err != nil { + return pkgerrors.Wrapf(err, "Error finding AppContext for Logical Cloud: %v", logicalCloudName) + } + + // call resource synchronizer to delete the CRs from every cluster of the logical cloud + err = callRsyncUninstall(ctxVal) + if err != nil { + return err + } + + // TODO: status handling for logical cloud after terminate: + // rsync updates the status of the appcontext to Terminated + // dcm should launch thread to observe status of appcontext before concluding logical cloud is terminated + // dcm should somewhat mimic the status tracking of rsync + // logical cloud might be in a non-applied non-terminated state for a long period of time......... + + // // remove the app context + // err = context.DeleteCompositeApp() + // if err != nil { + // return pkgerrors.Wrap(err, "Error deleting AppContext CompositeApp") + // } + + // remove the app context field from the cluster db record + // lckey := LogicalCloudKey{ + // LogicalCloudName: logicalcloud.MetaData.LogicalCloudName, + // Project: project, + // } + // err = db.DBconn.RemoveTag("orchestrator", lckey, "lccontext") + // if err != nil { + // log.Warn("Error removing AppContext from Logical Cloud", log.Fields{ + // "logical-cloud": logicalCloudName, + // }) + // } + + return nil +} diff --git a/src/dcm/pkg/module/cluster.go b/src/dcm/pkg/module/cluster.go index 206d79a6..85b20117 100644 --- a/src/dcm/pkg/module/cluster.go +++ b/src/dcm/pkg/module/cluster.go @@ -133,7 +133,7 @@ func (v *ClusterClient) GetCluster(project, logicalCloud, clusterReference strin 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 @@ -149,6 +149,9 @@ func (v *ClusterClient) GetAllClusters(project, logicalCloud string) ([]Cluster, 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{} @@ -188,12 +191,12 @@ func (v *ClusterClient) UpdateCluster(project, logicalCloud, clusterReference st //Check for name mismatch in cluster reference if c.MetaData.ClusterReference != clusterReference { - return Cluster{}, pkgerrors.New("Update Error - Cluster reference mismatch") + 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("Update Error - Cluster reference doesn't exist") + return Cluster{}, pkgerrors.New("Cluster Reference does not exist") } err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) if err != nil { diff --git a/src/dcm/pkg/module/keyvalue.go b/src/dcm/pkg/module/keyvalue.go index 37c74a84..0127a6f4 100644 --- a/src/dcm/pkg/module/keyvalue.go +++ b/src/dcm/pkg/module/keyvalue.go @@ -133,7 +133,7 @@ func (v *KeyValueClient) GetKVPair(project, logicalCloud, kvPairName string) (Ke return kv, nil } - return KeyValue{}, pkgerrors.New("Error getting Key Value") + return KeyValue{}, pkgerrors.New("Key Value does not exist") } // Get All lists all key value pairs @@ -194,7 +194,7 @@ func (v *KeyValueClient) UpdateKVPair(project, logicalCloud, kvPairName string, //Check if this Key Value exists _, err := v.GetKVPair(project, logicalCloud, kvPairName) if err != nil { - return KeyValue{}, pkgerrors.New("Update Error - Key Value Pair doesn't exist") + return KeyValue{}, pkgerrors.New("KV Pair does not exist") } err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) if err != nil { diff --git a/src/dcm/pkg/module/logicalcloud.go b/src/dcm/pkg/module/logicalcloud.go index 51ee387d..61d7b7a5 100644 --- a/src/dcm/pkg/module/logicalcloud.go +++ b/src/dcm/pkg/module/logicalcloud.go @@ -17,6 +17,7 @@ package module import ( + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/appcontext" "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module" @@ -72,6 +73,7 @@ type LogicalCloudManager interface { GetAll(project string) ([]LogicalCloud, error) Delete(project, name string) error Update(project, name string, c LogicalCloud) (LogicalCloud, error) + GetLogicalCloudContext(name string) (appcontext.AppContext, string, error) } // Interface facilitates unit testing by mocking functions @@ -87,9 +89,10 @@ type Utility interface { // LogicalCloudClient implements the LogicalCloudManager // It will also be used to maintain some localized state type LogicalCloudClient struct { - storeName string - tagMeta string - util Utility + storeName string + tagMeta string + tagContext string + util Utility } // Added for unit testing; implements Utility interface @@ -145,7 +148,7 @@ func (v *LogicalCloudClient) Get(project, logicalCloudName string) (LogicalCloud } value, err := v.util.DBFind(v.storeName, key, v.tagMeta) if err != nil { - return LogicalCloud{}, pkgerrors.Wrap(err, "Get Logical Cloud") + return LogicalCloud{}, pkgerrors.Wrap(err, "Error getting Logical Cloud") } //value is a byte array @@ -153,12 +156,12 @@ func (v *LogicalCloudClient) Get(project, logicalCloudName string) (LogicalCloud lc := LogicalCloud{} err = v.util.DBUnmarshal(value[0], &lc) if err != nil { - return LogicalCloud{}, pkgerrors.Wrap(err, "Unmarshaling value") + return LogicalCloud{}, pkgerrors.Wrap(err, "Error unmarshaling value") } return lc, nil } - return LogicalCloud{}, pkgerrors.New("Error getting Logical Cloud") + return LogicalCloud{}, pkgerrors.New("Logical Cloud does not exist") } // GetAll returns Logical Clouds in the project @@ -196,7 +199,12 @@ func (v *LogicalCloudClient) Delete(project, logicalCloudName string) error { Project: project, LogicalCloudName: logicalCloudName, } - err := v.util.DBRemove(v.storeName, key) + //Check if this Logical Cloud exists + _, err := v.Get(project, logicalCloudName) + if err != nil { + return pkgerrors.New("Logical Cloud does not exist") + } + err = v.util.DBRemove(v.storeName, key) if err != nil { return pkgerrors.Wrap(err, "Delete Logical Cloud") } @@ -213,12 +221,12 @@ func (v *LogicalCloudClient) Update(project, logicalCloudName string, c LogicalC } // Check for mismatch, logicalCloudName and payload logical cloud name if c.MetaData.LogicalCloudName != logicalCloudName { - return LogicalCloud{}, pkgerrors.New("Update Error - Logical Cloud name mismatch") + return LogicalCloud{}, pkgerrors.New("Logical Cloud name mismatch") } //Check if this Logical Cloud exists _, err := v.Get(project, logicalCloudName) if err != nil { - return LogicalCloud{}, pkgerrors.New("Update Error - Logical Cloud doesn't exist") + return LogicalCloud{}, pkgerrors.New("Logical Cloud does not exist") } err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) if err != nil { @@ -227,6 +235,33 @@ func (v *LogicalCloudClient) Update(project, logicalCloudName string, c LogicalC return c, nil } +// GetClusterContext returns the AppContext for corresponding provider and name +func (v *LogicalCloudClient) GetLogicalCloudContext(name string) (appcontext.AppContext, string, error) { + //Construct key and tag to select the entry + key := LogicalCloudKey{ + LogicalCloudName: name, + Project: "test-project", // FIXME(igordc): temporary, need to do some rework in the LC structs + } + + value, err := db.DBconn.Find(v.storeName, key, v.tagContext) + if err != nil { + return appcontext.AppContext{}, "", pkgerrors.Wrap(err, "Get Logical Cloud Context") + } + + //value is a byte array + if value != nil { + ctxVal := string(value[0]) + var lcc appcontext.AppContext + _, err = lcc.LoadAppContext(ctxVal) + if err != nil { + return appcontext.AppContext{}, "", pkgerrors.Wrap(err, "Reinitializing Logical Cloud AppContext") + } + return lcc, ctxVal, nil + } + + return appcontext.AppContext{}, "", pkgerrors.New("Error getting Logical Cloud AppContext") +} + func (d DBService) DBInsert(storeName string, key db.Key, query interface{}, meta string, c interface{}) error { err := db.DBconn.Insert(storeName, key, nil, meta, c) diff --git a/src/dcm/pkg/module/logicalcloud_test.go b/src/dcm/pkg/module/logicalcloud_test.go index fb205753..4700eff0 100644 --- a/src/dcm/pkg/module/logicalcloud_test.go +++ b/src/dcm/pkg/module/logicalcloud_test.go @@ -78,7 +78,7 @@ func TestCreateLogicalCloud(t *testing.T) { myMocks.On("DBInsert", "test_dcm", key, nil, "test_meta", lc).Return(nil) myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, err1) - lcClient := LogicalCloudClient{"test_dcm", "test_meta", myMocks} + lcClient := LogicalCloudClient{"test_dcm", "test_meta", "test_context", myMocks} _, err := lcClient.Create("test_project", lc) if err != nil { t.Errorf("Some error occured!") @@ -101,7 +101,7 @@ func TestGetLogicalCloud(t *testing.T) { myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, nil) myMocks.On("DBUnmarshal", data2).Return(nil) - lcClient := LogicalCloudClient{"test_dcm", "test_meta", myMocks} + lcClient := LogicalCloudClient{"test_dcm", "test_meta", "test_context", myMocks} _, err := lcClient.Get("test_project", "test_asdf") if err != nil { t.Errorf("Some error occured!") @@ -117,9 +117,17 @@ func TestDeleteLogicalCloud(t *testing.T) { myMocks := new(mockValues) + data1 := [][]byte{ + []byte("abc"), + } + data2 := []byte("abc") + myMocks.On("DBRemove", "test_dcm", key).Return(nil) + myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, nil) + myMocks.On("DBUnmarshal", data2).Return(nil) + // TODO also test for when the logical cloud doesn't exist - lcClient := LogicalCloudClient{"test_dcm", "test_meta", myMocks} + lcClient := LogicalCloudClient{"test_dcm", "test_meta", "test_context", myMocks} err := lcClient.Delete("test_project", "test_asdf") if err != nil { t.Errorf("Some error occured!") @@ -148,7 +156,7 @@ func TestUpdateLogicalCloud(t *testing.T) { myMocks.On("DBInsert", "test_dcm", key, nil, "test_meta", lc).Return(nil) myMocks.On("DBFind", "test_dcm", key, "test_meta").Return(data1, nil) myMocks.On("DBUnmarshal", data2).Return(nil) - lcClient := LogicalCloudClient{"test_dcm", "test_meta", myMocks} + lcClient := LogicalCloudClient{"test_dcm", "test_meta", "test_context", myMocks} _, err := lcClient.Update("test_project", "test_asdf", lc) if err != nil { t.Errorf("Some error occured!") diff --git a/src/dcm/pkg/module/quota.go b/src/dcm/pkg/module/quota.go index cbd9c8b7..c961fdfc 100644 --- a/src/dcm/pkg/module/quota.go +++ b/src/dcm/pkg/module/quota.go @@ -22,8 +22,9 @@ import ( // Quota contains the parameters needed for a Quota type Quota struct { - MetaData QMetaDataList `json:"metadata"` - Specification QSpec `json:"spec"` + MetaData QMetaDataList `json:"metadata"` + // Specification QSpec `json:"spec"` + Specification map[string]string `json:"spec"` } // MetaData contains the parameters needed for metadata @@ -32,6 +33,7 @@ type QMetaDataList struct { Description string `json:"description"` } +// TODO: use QSpec fields to validate quota keys // Spec contains the parameters needed for spec type QSpec struct { LimitsCPU string `json:"limits.cpu"` @@ -152,7 +154,7 @@ func (v *QuotaClient) GetQuota(project, logicalCloud, quotaName string) (Quota, return q, nil } - return Quota{}, pkgerrors.New("Error getting Quota") + return Quota{}, pkgerrors.New("Cluster Quota does not exist") } // GetAll returns all cluster quotas in the logical cloud @@ -211,7 +213,7 @@ func (v *QuotaClient) UpdateQuota(project, logicalCloud, quotaName string, c Quo //Check if this Quota exists _, err := v.GetQuota(project, logicalCloud, quotaName) if err != nil { - return Quota{}, pkgerrors.New("Update Error - Quota doesn't exist") + return Quota{}, pkgerrors.New("Cluster Quota does not exist") } err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) if err != nil { diff --git a/src/dcm/pkg/module/userpermissions.go b/src/dcm/pkg/module/userpermissions.go index 2cff712b..4c918f0f 100644 --- a/src/dcm/pkg/module/userpermissions.go +++ b/src/dcm/pkg/module/userpermissions.go @@ -124,7 +124,7 @@ func (v *UserPermissionClient) GetUserPerm(project, logicalCloud, userPermName s return up, nil } - return UserPermission{}, pkgerrors.New("Error getting User Permission") + return UserPermission{}, pkgerrors.New("User Permission does not exist") } // GetAll lists all user permissions @@ -184,7 +184,7 @@ func (v *UserPermissionClient) UpdateUserPerm(project, logicalCloud, userPermNam _, err := v.GetUserPerm(project, logicalCloud, userPermName) if err != nil { return UserPermission{}, pkgerrors.New( - "Update Error - User Permission doesn't exist") + "User Permission does not exist") } err = v.util.DBInsert(v.storeName, key, nil, v.tagMeta, c) if err != nil { |