2 * Copyright 2020 Intel Corporation, Inc
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
30 "github.com/onap/multicloud-k8s/src/orchestrator/pkg/appcontext"
31 "github.com/onap/multicloud-k8s/src/orchestrator/pkg/appcontext/subresources"
32 "github.com/onap/multicloud-k8s/src/orchestrator/pkg/grpc/installappclient"
33 "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
34 log "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/logutils"
35 "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module/controller"
36 pkgerrors "github.com/pkg/errors"
38 certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
39 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
42 // rsyncName denotes the name of the rsync controller
43 const rsyncName = "rsync"
45 type Resource struct {
46 ApiVersion string `yaml:"apiVersion"`
47 Kind string `yaml:"kind"`
48 MetaData MetaDatas `yaml:"metadata"`
49 Specification Specs `yaml:"spec,omitempty"`
50 Rules []RoleRules `yaml:"rules,omitempty"`
51 Subjects []RoleSubjects `yaml:"subjects,omitempty"`
52 RoleRefs RoleRef `yaml:"roleRef,omitempty"`
55 type MetaDatas struct {
56 Name string `yaml:"name"`
57 Namespace string `yaml:"namespace,omitempty"`
61 Request string `yaml:"request,omitempty"`
62 Usages []string `yaml:"usages,omitempty"`
63 // TODO: validate quota keys
64 // //Hard logicalcloud.QSpec `yaml:"hard,omitempty"`
65 // Hard QSpec `yaml:"hard,omitempty"`
66 Hard map[string]string `yaml:"hard,omitempty"`
69 type RoleRules struct {
70 ApiGroups []string `yaml:"apiGroups"`
71 Resources []string `yaml:"resources"`
72 Verbs []string `yaml:"verbs"`
75 type RoleSubjects struct {
76 Kind string `yaml:"kind"`
77 Name string `yaml:"name"`
78 ApiGroup string `yaml:"apiGroup"`
82 Kind string `yaml:"kind"`
83 Name string `yaml:"name"`
84 ApiGroup string `yaml:"apiGroup"`
87 func createNamespace(logicalcloud LogicalCloud) (string, error) {
89 namespace := Resource{
93 Name: logicalcloud.Specification.NameSpace,
97 nsData, err := yaml.Marshal(&namespace)
102 return string(nsData), nil
105 func createRole(logicalcloud LogicalCloud) (string, error) {
107 userPermissions := logicalcloud.Specification.User.UserPermissions[0]
110 ApiVersion: "rbac.authorization.k8s.io/v1beta1",
113 Name: strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "-role"}, ""),
114 Namespace: logicalcloud.Specification.NameSpace,
116 Rules: []RoleRules{RoleRules{
117 ApiGroups: userPermissions.APIGroups,
118 Resources: userPermissions.Resources,
119 Verbs: userPermissions.Verbs,
124 roleData, err := yaml.Marshal(&role)
129 return string(roleData), nil
132 func createRoleBinding(logicalcloud LogicalCloud) (string, error) {
134 roleBinding := Resource{
135 ApiVersion: "rbac.authorization.k8s.io/v1beta1",
138 Name: strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "-roleBinding"}, ""),
139 Namespace: logicalcloud.Specification.NameSpace,
141 Subjects: []RoleSubjects{RoleSubjects{
143 Name: logicalcloud.Specification.User.UserName,
150 Name: strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "-role"}, ""),
155 rBData, err := yaml.Marshal(&roleBinding)
160 return string(rBData), nil
163 func createQuota(quota []Quota, namespace string) (string, error) {
167 Kind: "ResourceQuota",
169 Name: lcQuota.MetaData.QuotaName,
170 Namespace: namespace,
172 Specification: Specs{
173 Hard: lcQuota.Specification,
177 qData, err := yaml.Marshal(&q)
182 return string(qData), nil
185 func createUserCSR(logicalcloud LogicalCloud) (string, string, error) {
187 userName := logicalcloud.Specification.User.UserName
189 key, err := rsa.GenerateKey(rand.Reader, KEYSIZE)
194 csrTemplate := x509.CertificateRequest{Subject: pkix.Name{CommonName: userName}}
196 csrCert, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, key)
202 csr := pem.EncodeToMemory(&pem.Block{
203 Type: "CERTIFICATE REQUEST",
208 ApiVersion: "certificates.k8s.io/v1beta1",
209 Kind: "CertificateSigningRequest",
211 Name: strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "-user-csr"}, ""),
212 // Namespace: logicalcloud.Specification.NameSpace,
214 Specification: Specs{
215 Request: base64.StdEncoding.EncodeToString(csr),
216 Usages: []string{"digital signature", "key encipherment"},
220 csrData, err := yaml.Marshal(&csrObj)
225 keyData := base64.StdEncoding.EncodeToString(pem.EncodeToMemory(
227 Type: "RSA PRIVATE KEY",
228 Bytes: x509.MarshalPKCS1PrivateKey(key),
235 return string(csrData), string(keyData), nil
238 func createApprovalSubresource(logicalcloud LogicalCloud) (string, error) {
239 subresource := subresources.ApprovalSubresource{
240 Message: "Approved for Logical Cloud authentication",
241 Reason: "LogicalCloud",
242 Type: string(certificatesv1beta1.CertificateApproved),
243 LastUpdateTime: metav1.Now().Format("2006-01-02T15:04:05Z"),
245 csrData, err := json.Marshal(subresource)
246 return string(csrData), err
250 queryDBAndSetRsyncInfo queries the MCO db to find the record the sync controller
251 and then sets the RsyncInfo global variable.
253 func queryDBAndSetRsyncInfo() (installappclient.RsyncInfo, error) {
254 client := controller.NewControllerClient()
255 vals, _ := client.GetControllers()
256 for _, v := range vals {
257 if v.Metadata.Name == rsyncName {
258 log.Info("Initializing RPC connection to resource synchronizer", log.Fields{
259 "Controller": v.Metadata.Name,
261 rsyncInfo := installappclient.NewRsyncInfo(v.Metadata.Name, v.Spec.Host, v.Spec.Port)
262 return rsyncInfo, nil
265 return installappclient.RsyncInfo{}, pkgerrors.Errorf("queryRsyncInfoInMCODB Failed - Could not get find rsync by name : %v", rsyncName)
269 callRsyncInstall method shall take in the app context id and invokes the rsync service via grpc
271 func callRsyncInstall(contextid interface{}) error {
272 rsyncInfo, err := queryDBAndSetRsyncInfo()
273 log.Info("Calling the Rsync ", log.Fields{
274 "RsyncName": rsyncInfo.RsyncName,
280 appContextID := fmt.Sprintf("%v", contextid)
281 err = installappclient.InvokeInstallApp(appContextID)
289 callRsyncUninstall method shall take in the app context id and invokes the rsync service via grpc
291 func callRsyncUninstall(contextid interface{}) error {
292 rsyncInfo, err := queryDBAndSetRsyncInfo()
293 log.Info("Calling the Rsync ", log.Fields{
294 "RsyncName": rsyncInfo.RsyncName,
300 appContextID := fmt.Sprintf("%v", contextid)
301 err = installappclient.InvokeUninstallApp(appContextID)
308 func CreateEtcdContext(project string, logicalcloud LogicalCloud, clusterList []Cluster,
309 quotaList []Quota) error {
311 APP := "logical-cloud"
312 logicalCloudName := logicalcloud.MetaData.LogicalCloudName
315 namespaceName := strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "+namespace"}, "")
316 roleName := strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "+role"}, "")
317 roleBindingName := strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "+roleBinding"}, "")
318 quotaName := strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "+quota"}, "")
319 csrName := strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "+CertificateSigningRequest"}, "")
321 // Get resources to be added
322 namespace, err := createNamespace(logicalcloud)
324 return pkgerrors.Wrap(err, "Error Creating Namespace YAML for logical cloud")
327 role, err := createRole(logicalcloud)
329 return pkgerrors.Wrap(err, "Error Creating Role YAML for logical cloud")
332 roleBinding, err := createRoleBinding(logicalcloud)
334 return pkgerrors.Wrap(err, "Error Creating RoleBinding YAML for logical cloud")
337 quota, err := createQuota(quotaList, logicalcloud.Specification.NameSpace)
339 return pkgerrors.Wrap(err, "Error Creating Quota YAML for logical cloud")
342 csr, key, err := createUserCSR(logicalcloud)
344 return pkgerrors.Wrap(err, "Error Creating User CSR and Key for logical cloud")
347 approval, err := createApprovalSubresource(logicalcloud)
349 context := appcontext.AppContext{}
350 ctxVal, err := context.InitAppContext()
352 return pkgerrors.Wrap(err, "Error creating AppContext")
355 fmt.Printf("%v\n", ctxVal)
357 handle, err := context.CreateCompositeApp()
359 return pkgerrors.Wrap(err, "Error creating AppContext CompositeApp")
362 appHandle, err := context.AddApp(handle, APP)
364 cleanuperr := context.DeleteCompositeApp()
365 if cleanuperr != nil {
366 log.Warn("Error cleaning AppContext CompositeApp create failure", log.Fields{
367 "logical-cloud": logicalCloudName,
370 return pkgerrors.Wrap(err, "Error adding App to AppContext")
373 // Iterate through cluster list and add all the clusters
374 for _, cluster := range clusterList {
375 clusterName := strings.Join([]string{cluster.Specification.ClusterProvider, "+", cluster.Specification.ClusterName}, "")
376 clusterHandle, err := context.AddCluster(appHandle, clusterName)
379 cleanuperr := context.DeleteCompositeApp()
380 if cleanuperr != nil {
381 log.Warn("Error cleaning AppContext after add cluster failure", log.Fields{
382 "cluster-provider": cluster.Specification.ClusterProvider,
383 "cluster": cluster.Specification.ClusterName,
384 "logical-cloud": logicalCloudName,
387 return pkgerrors.Wrap(err, "Error adding Cluster to AppContext")
390 // Add namespace resource to each cluster
391 _, err = context.AddResource(clusterHandle, namespaceName, namespace)
393 cleanuperr := context.DeleteCompositeApp()
394 if cleanuperr != nil {
395 log.Warn("Error cleaning AppContext after add namespace resource failure", log.Fields{
396 "cluster-provider": cluster.Specification.ClusterProvider,
397 "cluster": cluster.Specification.ClusterName,
398 "logical-cloud": logicalCloudName,
401 return pkgerrors.Wrap(err, "Error adding Namespace Resource to AppContext")
404 // Add csr resource to each cluster
405 csrHandle, err := context.AddResource(clusterHandle, csrName, csr)
407 cleanuperr := context.DeleteCompositeApp()
408 if cleanuperr != nil {
409 log.Warn("Error cleaning AppContext after add CSR resource failure", log.Fields{
410 "cluster-provider": cluster.Specification.ClusterProvider,
411 "cluster": cluster.Specification.ClusterName,
412 "logical-cloud": logicalCloudName,
415 return pkgerrors.Wrap(err, "Error adding CSR Resource to AppContext")
418 // Add csr approval as a subresource of csr:
419 _, err = context.AddLevelValue(csrHandle, "subresource/approval", approval)
421 cleanuperr := context.DeleteCompositeApp()
422 if cleanuperr != nil {
423 log.Warn("Error cleaning AppContext after add CSR approval failure", log.Fields{
424 "cluster-provider": cluster.Specification.ClusterProvider,
425 "cluster": cluster.Specification.ClusterName,
426 "logical-cloud": logicalCloudName,
429 return pkgerrors.Wrap(err, "Error approving CSR via AppContext")
432 // Add private key to MongoDB
433 lckey := LogicalCloudKey{
434 LogicalCloudName: logicalcloud.MetaData.LogicalCloudName,
437 err = db.DBconn.Insert("orchestrator", lckey, nil, "privatekey", key)
439 cleanuperr := context.DeleteCompositeApp()
440 if cleanuperr != nil {
441 log.Warn("Error cleaning AppContext after DB insert failure", log.Fields{
442 "logical-cloud": logicalcloud.MetaData.LogicalCloudName,
445 return pkgerrors.Wrap(err, "Error adding private key to DB")
448 // Add Role resource to each cluster
449 _, err = context.AddResource(clusterHandle, roleName, role)
451 cleanuperr := context.DeleteCompositeApp()
452 if cleanuperr != nil {
453 log.Warn("Error cleaning AppContext after add role resource failure", log.Fields{
454 "cluster-provider": cluster.Specification.ClusterProvider,
455 "cluster": cluster.Specification.ClusterName,
456 "logical-cloud": logicalCloudName,
459 return pkgerrors.Wrap(err, "Error adding role Resource to AppContext")
462 // Add RoleBinding resource to each cluster
463 _, err = context.AddResource(clusterHandle, roleBindingName, roleBinding)
465 cleanuperr := context.DeleteCompositeApp()
466 if cleanuperr != nil {
467 log.Warn("Error cleaning AppContext after add roleBinding resource failure", log.Fields{
468 "cluster-provider": cluster.Specification.ClusterProvider,
469 "cluster": cluster.Specification.ClusterName,
470 "logical-cloud": logicalCloudName,
473 return pkgerrors.Wrap(err, "Error adding roleBinding Resource to AppContext")
476 // Add quota resource to each cluster
477 _, err = context.AddResource(clusterHandle, quotaName, quota)
479 cleanuperr := context.DeleteCompositeApp()
480 if cleanuperr != nil {
481 log.Warn("Error cleaning AppContext after add quota resource failure", log.Fields{
482 "cluster-provider": cluster.Specification.ClusterProvider,
483 "cluster": cluster.Specification.ClusterName,
484 "logical-cloud": logicalCloudName,
487 return pkgerrors.Wrap(err, "Error adding quota Resource to AppContext")
490 // Add Subresource Order and Subresource Dependency
491 subresOrder, err := json.Marshal(map[string][]string{"subresorder": []string{"approval"}})
493 return pkgerrors.Wrap(err, "Error creating subresource order JSON")
495 subresDependency, err := json.Marshal(map[string]map[string]string{"subresdependency": map[string]string{"approval": "go"}})
497 // Add Resource Order and Resource Dependency
498 resOrder, err := json.Marshal(map[string][]string{"resorder": []string{namespaceName, quotaName, csrName, roleName, roleBindingName}})
500 return pkgerrors.Wrap(err, "Error creating resource order JSON")
502 resDependency, err := json.Marshal(map[string]map[string]string{"resdependency": map[string]string{namespaceName: "go",
503 quotaName: strings.Join([]string{"wait on ", namespaceName}, ""), csrName: strings.Join([]string{"wait on ", quotaName}, ""),
504 roleName: strings.Join([]string{"wait on ", csrName}, ""), roleBindingName: strings.Join([]string{"wait on ", roleName}, "")}})
506 // Add App Order and App Dependency
507 appOrder, err := json.Marshal(map[string][]string{"apporder": []string{APP}})
509 return pkgerrors.Wrap(err, "Error creating resource order JSON")
511 appDependency, err := json.Marshal(map[string]map[string]string{"appdependency": map[string]string{APP: "go"}})
514 return pkgerrors.Wrap(err, "Error creating resource dependency JSON")
517 _, err = context.AddInstruction(clusterHandle, "resource", "order", string(resOrder))
519 cleanuperr := context.DeleteCompositeApp()
520 if cleanuperr != nil {
521 log.Warn("Error cleaning AppContext after add instruction failure", log.Fields{
522 "cluster-provider": cluster.Specification.ClusterProvider,
523 "cluster": cluster.Specification.ClusterName,
524 "logical-cloud": logicalCloudName,
527 return pkgerrors.Wrap(err, "Error adding instruction order to AppContext")
530 _, err = context.AddInstruction(clusterHandle, "resource", "dependency", string(resDependency))
532 cleanuperr := context.DeleteCompositeApp()
533 if cleanuperr != nil {
534 log.Warn("Error cleaning AppContext after add instruction failure", log.Fields{
535 "cluster-provider": cluster.Specification.ClusterProvider,
536 "cluster": cluster.Specification.ClusterName,
537 "logical-cloud": logicalCloudName,
540 return pkgerrors.Wrap(err, "Error adding instruction dependency to AppContext")
543 _, err = context.AddInstruction(csrHandle, "subresource", "order", string(subresOrder))
545 cleanuperr := context.DeleteCompositeApp()
546 if cleanuperr != nil {
547 log.Warn("Error cleaning AppContext after add instruction failure", log.Fields{
548 "cluster-provider": cluster.Specification.ClusterProvider,
549 "cluster": cluster.Specification.ClusterName,
550 "logical-cloud": logicalCloudName,
553 return pkgerrors.Wrap(err, "Error adding instruction order to AppContext")
556 _, err = context.AddInstruction(csrHandle, "subresource", "dependency", string(subresDependency))
558 cleanuperr := context.DeleteCompositeApp()
559 if cleanuperr != nil {
560 log.Warn("Error cleaning AppContext after add instruction failure", log.Fields{
561 "cluster-provider": cluster.Specification.ClusterProvider,
562 "cluster": cluster.Specification.ClusterName,
563 "logical-cloud": logicalCloudName,
566 return pkgerrors.Wrap(err, "Error adding instruction dependency to AppContext")
569 // Add App-level Order and Dependency
570 _, err = context.AddInstruction(handle, "app", "order", string(appOrder))
571 _, err = context.AddInstruction(handle, "app", "dependency", string(appDependency))
573 // save the context in the logicalcloud db record
574 lckey := LogicalCloudKey{
575 LogicalCloudName: logicalcloud.MetaData.LogicalCloudName,
578 err = db.DBconn.Insert("orchestrator", lckey, nil, "lccontext", ctxVal)
580 cleanuperr := context.DeleteCompositeApp()
581 if cleanuperr != nil {
582 log.Warn("Error cleaning AppContext after DB insert failure", log.Fields{
583 "logical-cloud": logicalcloud.MetaData.LogicalCloudName,
586 return pkgerrors.Wrap(err, "Error adding AppContext to DB")
589 // call resource synchronizer to instantiate the CRs in the cluster
590 err = callRsyncInstall(ctxVal)
599 // TODO: rename these methods
600 // DestroyEtcdContext remove from rsync then delete appcontext and all resources
601 func DestroyEtcdContext(project string, logicalcloud LogicalCloud, clusterList []Cluster,
602 quotaList []Quota) error {
604 logicalCloudName := logicalcloud.MetaData.LogicalCloudName
606 _, ctxVal, err := NewLogicalCloudClient().GetLogicalCloudContext(project, logicalCloudName)
608 return pkgerrors.Wrapf(err, "Error finding AppContext for Logical Cloud: %v", logicalCloudName)
611 // call resource synchronizer to delete the CRs from every cluster of the logical cloud
612 err = callRsyncUninstall(ctxVal)
617 // TODO: status handling for logical cloud after terminate:
618 // rsync updates the status of the appcontext to Terminated
619 // dcm should launch thread to observe status of appcontext before concluding logical cloud is terminated
620 // dcm should somewhat mimic the status tracking of rsync
621 // logical cloud might be in a non-applied non-terminated state for a long period of time.........
623 // // remove the app context
624 // err = context.DeleteCompositeApp()
626 // return pkgerrors.Wrap(err, "Error deleting AppContext CompositeApp")
629 // remove the app context field from the cluster db record
630 // lckey := LogicalCloudKey{
631 // LogicalCloudName: logicalcloud.MetaData.LogicalCloudName,
634 // err = db.DBconn.RemoveTag("orchestrator", lckey, "lccontext")
636 // log.Warn("Error removing AppContext from Logical Cloud", log.Fields{
637 // "logical-cloud": logicalCloudName,