Use project name provided by API in DCM
[multicloud/k8s.git] / src / dcm / pkg / module / apply.go
1 /*
2 * Copyright 2020 Intel Corporation, Inc
3 *
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
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15  */
16
17 package module
18
19 import (
20         "crypto/rand"
21         "crypto/rsa"
22         "crypto/x509"
23         "crypto/x509/pkix"
24         "encoding/base64"
25         "encoding/json"
26         "encoding/pem"
27         "fmt"
28         "strings"
29
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"
37         "gopkg.in/yaml.v2"
38         certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
39         metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
40 )
41
42 // rsyncName denotes the name of the rsync controller
43 const rsyncName = "rsync"
44
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"`
53 }
54
55 type MetaDatas struct {
56         Name      string `yaml:"name"`
57         Namespace string `yaml:"namespace,omitempty"`
58 }
59
60 type Specs struct {
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"`
67 }
68
69 type RoleRules struct {
70         ApiGroups []string `yaml:"apiGroups"`
71         Resources []string `yaml:"resources"`
72         Verbs     []string `yaml:"verbs"`
73 }
74
75 type RoleSubjects struct {
76         Kind     string `yaml:"kind"`
77         Name     string `yaml:"name"`
78         ApiGroup string `yaml:"apiGroup"`
79 }
80
81 type RoleRef struct {
82         Kind     string `yaml:"kind"`
83         Name     string `yaml:"name"`
84         ApiGroup string `yaml:"apiGroup"`
85 }
86
87 func createNamespace(logicalcloud LogicalCloud) (string, error) {
88
89         namespace := Resource{
90                 ApiVersion: "v1",
91                 Kind:       "Namespace",
92                 MetaData: MetaDatas{
93                         Name: logicalcloud.Specification.NameSpace,
94                 },
95         }
96
97         nsData, err := yaml.Marshal(&namespace)
98         if err != nil {
99                 return "", err
100         }
101
102         return string(nsData), nil
103 }
104
105 func createRole(logicalcloud LogicalCloud) (string, error) {
106
107         userPermissions := logicalcloud.Specification.User.UserPermissions[0]
108
109         role := Resource{
110                 ApiVersion: "rbac.authorization.k8s.io/v1beta1",
111                 Kind:       "Role",
112                 MetaData: MetaDatas{
113                         Name:      strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "-role"}, ""),
114                         Namespace: logicalcloud.Specification.NameSpace,
115                 },
116                 Rules: []RoleRules{RoleRules{
117                         ApiGroups: userPermissions.APIGroups,
118                         Resources: userPermissions.Resources,
119                         Verbs:     userPermissions.Verbs,
120                 },
121                 },
122         }
123
124         roleData, err := yaml.Marshal(&role)
125         if err != nil {
126                 return "", err
127         }
128
129         return string(roleData), nil
130 }
131
132 func createRoleBinding(logicalcloud LogicalCloud) (string, error) {
133
134         roleBinding := Resource{
135                 ApiVersion: "rbac.authorization.k8s.io/v1beta1",
136                 Kind:       "RoleBinding",
137                 MetaData: MetaDatas{
138                         Name:      strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "-roleBinding"}, ""),
139                         Namespace: logicalcloud.Specification.NameSpace,
140                 },
141                 Subjects: []RoleSubjects{RoleSubjects{
142                         Kind:     "User",
143                         Name:     logicalcloud.Specification.User.UserName,
144                         ApiGroup: "",
145                 },
146                 },
147
148                 RoleRefs: RoleRef{
149                         Kind:     "Role",
150                         Name:     strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "-role"}, ""),
151                         ApiGroup: "",
152                 },
153         }
154
155         rBData, err := yaml.Marshal(&roleBinding)
156         if err != nil {
157                 return "", err
158         }
159
160         return string(rBData), nil
161 }
162
163 func createQuota(quota []Quota, namespace string) (string, error) {
164         lcQuota := quota[0]
165         q := Resource{
166                 ApiVersion: "v1",
167                 Kind:       "ResourceQuota",
168                 MetaData: MetaDatas{
169                         Name:      lcQuota.MetaData.QuotaName,
170                         Namespace: namespace,
171                 },
172                 Specification: Specs{
173                         Hard: lcQuota.Specification,
174                 },
175         }
176
177         qData, err := yaml.Marshal(&q)
178         if err != nil {
179                 return "", err
180         }
181
182         return string(qData), nil
183 }
184
185 func createUserCSR(logicalcloud LogicalCloud) (string, string, error) {
186         KEYSIZE := 4096
187         userName := logicalcloud.Specification.User.UserName
188
189         key, err := rsa.GenerateKey(rand.Reader, KEYSIZE)
190         if err != nil {
191                 return "", "", err
192         }
193
194         csrTemplate := x509.CertificateRequest{Subject: pkix.Name{CommonName: userName}}
195
196         csrCert, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, key)
197         if err != nil {
198                 return "", "", err
199         }
200
201         //Encode csr
202         csr := pem.EncodeToMemory(&pem.Block{
203                 Type:  "CERTIFICATE REQUEST",
204                 Bytes: csrCert,
205         })
206
207         csrObj := Resource{
208                 ApiVersion: "certificates.k8s.io/v1beta1",
209                 Kind:       "CertificateSigningRequest",
210                 MetaData: MetaDatas{
211                         Name: strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "-user-csr"}, ""),
212                         // Namespace: logicalcloud.Specification.NameSpace,
213                 },
214                 Specification: Specs{
215                         Request: base64.StdEncoding.EncodeToString(csr),
216                         Usages:  []string{"digital signature", "key encipherment"},
217                 },
218         }
219
220         csrData, err := yaml.Marshal(&csrObj)
221         if err != nil {
222                 return "", "", err
223         }
224
225         keyData := base64.StdEncoding.EncodeToString(pem.EncodeToMemory(
226                 &pem.Block{
227                         Type:  "RSA PRIVATE KEY",
228                         Bytes: x509.MarshalPKCS1PrivateKey(key),
229                 },
230         ))
231         if err != nil {
232                 return "", "", err
233         }
234
235         return string(csrData), string(keyData), nil
236 }
237
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"),
244         }
245         csrData, err := json.Marshal(subresource)
246         return string(csrData), err
247 }
248
249 /*
250 queryDBAndSetRsyncInfo queries the MCO db to find the record the sync controller
251 and then sets the RsyncInfo global variable.
252 */
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,
260                         })
261                         rsyncInfo := installappclient.NewRsyncInfo(v.Metadata.Name, v.Spec.Host, v.Spec.Port)
262                         return rsyncInfo, nil
263                 }
264         }
265         return installappclient.RsyncInfo{}, pkgerrors.Errorf("queryRsyncInfoInMCODB Failed - Could not get find rsync by name : %v", rsyncName)
266 }
267
268 /*
269 callRsyncInstall method shall take in the app context id and invokes the rsync service via grpc
270 */
271 func callRsyncInstall(contextid interface{}) error {
272         rsyncInfo, err := queryDBAndSetRsyncInfo()
273         log.Info("Calling the Rsync ", log.Fields{
274                 "RsyncName": rsyncInfo.RsyncName,
275         })
276         if err != nil {
277                 return err
278         }
279
280         appContextID := fmt.Sprintf("%v", contextid)
281         err = installappclient.InvokeInstallApp(appContextID)
282         if err != nil {
283                 return err
284         }
285         return nil
286 }
287
288 /*
289 callRsyncUninstall method shall take in the app context id and invokes the rsync service via grpc
290 */
291 func callRsyncUninstall(contextid interface{}) error {
292         rsyncInfo, err := queryDBAndSetRsyncInfo()
293         log.Info("Calling the Rsync ", log.Fields{
294                 "RsyncName": rsyncInfo.RsyncName,
295         })
296         if err != nil {
297                 return err
298         }
299
300         appContextID := fmt.Sprintf("%v", contextid)
301         err = installappclient.InvokeUninstallApp(appContextID)
302         if err != nil {
303                 return err
304         }
305         return nil
306 }
307
308 func CreateEtcdContext(project string, logicalcloud LogicalCloud, clusterList []Cluster,
309         quotaList []Quota) error {
310
311         APP := "logical-cloud"
312         logicalCloudName := logicalcloud.MetaData.LogicalCloudName
313
314         //Resource Names
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"}, "")
320
321         // Get resources to be added
322         namespace, err := createNamespace(logicalcloud)
323         if err != nil {
324                 return pkgerrors.Wrap(err, "Error Creating Namespace YAML for logical cloud")
325         }
326
327         role, err := createRole(logicalcloud)
328         if err != nil {
329                 return pkgerrors.Wrap(err, "Error Creating Role YAML for logical cloud")
330         }
331
332         roleBinding, err := createRoleBinding(logicalcloud)
333         if err != nil {
334                 return pkgerrors.Wrap(err, "Error Creating RoleBinding YAML for logical cloud")
335         }
336
337         quota, err := createQuota(quotaList, logicalcloud.Specification.NameSpace)
338         if err != nil {
339                 return pkgerrors.Wrap(err, "Error Creating Quota YAML for logical cloud")
340         }
341
342         csr, key, err := createUserCSR(logicalcloud)
343         if err != nil {
344                 return pkgerrors.Wrap(err, "Error Creating User CSR and Key for logical cloud")
345         }
346
347         approval, err := createApprovalSubresource(logicalcloud)
348
349         context := appcontext.AppContext{}
350         ctxVal, err := context.InitAppContext()
351         if err != nil {
352                 return pkgerrors.Wrap(err, "Error creating AppContext")
353         }
354
355         fmt.Printf("%v\n", ctxVal)
356
357         handle, err := context.CreateCompositeApp()
358         if err != nil {
359                 return pkgerrors.Wrap(err, "Error creating AppContext CompositeApp")
360         }
361
362         appHandle, err := context.AddApp(handle, APP)
363         if err != nil {
364                 cleanuperr := context.DeleteCompositeApp()
365                 if cleanuperr != nil {
366                         log.Warn("Error cleaning AppContext CompositeApp create failure", log.Fields{
367                                 "logical-cloud": logicalCloudName,
368                         })
369                 }
370                 return pkgerrors.Wrap(err, "Error adding App to AppContext")
371         }
372
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)
377
378                 if err != nil {
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,
385                                 })
386                         }
387                         return pkgerrors.Wrap(err, "Error adding Cluster to AppContext")
388                 }
389
390                 // Add namespace resource to each cluster
391                 _, err = context.AddResource(clusterHandle, namespaceName, namespace)
392                 if err != nil {
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,
399                                 })
400                         }
401                         return pkgerrors.Wrap(err, "Error adding Namespace Resource to AppContext")
402                 }
403
404                 // Add csr resource to each cluster
405                 csrHandle, err := context.AddResource(clusterHandle, csrName, csr)
406                 if err != nil {
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,
413                                 })
414                         }
415                         return pkgerrors.Wrap(err, "Error adding CSR Resource to AppContext")
416                 }
417
418                 // Add csr approval as a subresource of csr:
419                 _, err = context.AddLevelValue(csrHandle, "subresource/approval", approval)
420                 if err != nil {
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,
427                                 })
428                         }
429                         return pkgerrors.Wrap(err, "Error approving CSR via AppContext")
430                 }
431
432                 // Add private key to MongoDB
433                 lckey := LogicalCloudKey{
434                         LogicalCloudName: logicalcloud.MetaData.LogicalCloudName,
435                         Project:          project,
436                 }
437                 err = db.DBconn.Insert("orchestrator", lckey, nil, "privatekey", key)
438                 if err != nil {
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,
443                                 })
444                         }
445                         return pkgerrors.Wrap(err, "Error adding private key to DB")
446                 }
447
448                 // Add Role resource to each cluster
449                 _, err = context.AddResource(clusterHandle, roleName, role)
450                 if err != nil {
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,
457                                 })
458                         }
459                         return pkgerrors.Wrap(err, "Error adding role Resource to AppContext")
460                 }
461
462                 // Add RoleBinding resource to each cluster
463                 _, err = context.AddResource(clusterHandle, roleBindingName, roleBinding)
464                 if err != nil {
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,
471                                 })
472                         }
473                         return pkgerrors.Wrap(err, "Error adding roleBinding Resource to AppContext")
474                 }
475
476                 // Add quota resource to each cluster
477                 _, err = context.AddResource(clusterHandle, quotaName, quota)
478                 if err != nil {
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,
485                                 })
486                         }
487                         return pkgerrors.Wrap(err, "Error adding quota Resource to AppContext")
488                 }
489
490                 // Add Subresource Order and Subresource Dependency
491                 subresOrder, err := json.Marshal(map[string][]string{"subresorder": []string{"approval"}})
492                 if err != nil {
493                         return pkgerrors.Wrap(err, "Error creating subresource order JSON")
494                 }
495                 subresDependency, err := json.Marshal(map[string]map[string]string{"subresdependency": map[string]string{"approval": "go"}})
496
497                 // Add Resource Order and Resource Dependency
498                 resOrder, err := json.Marshal(map[string][]string{"resorder": []string{namespaceName, quotaName, csrName, roleName, roleBindingName}})
499                 if err != nil {
500                         return pkgerrors.Wrap(err, "Error creating resource order JSON")
501                 }
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}, "")}})
505
506                 // Add App Order and App Dependency
507                 appOrder, err := json.Marshal(map[string][]string{"apporder": []string{APP}})
508                 if err != nil {
509                         return pkgerrors.Wrap(err, "Error creating resource order JSON")
510                 }
511                 appDependency, err := json.Marshal(map[string]map[string]string{"appdependency": map[string]string{APP: "go"}})
512
513                 if err != nil {
514                         return pkgerrors.Wrap(err, "Error creating resource dependency JSON")
515                 }
516
517                 _, err = context.AddInstruction(clusterHandle, "resource", "order", string(resOrder))
518                 if err != nil {
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,
525                                 })
526                         }
527                         return pkgerrors.Wrap(err, "Error adding instruction order to AppContext")
528                 }
529
530                 _, err = context.AddInstruction(clusterHandle, "resource", "dependency", string(resDependency))
531                 if err != nil {
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,
538                                 })
539                         }
540                         return pkgerrors.Wrap(err, "Error adding instruction dependency to AppContext")
541                 }
542
543                 _, err = context.AddInstruction(csrHandle, "subresource", "order", string(subresOrder))
544                 if err != nil {
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,
551                                 })
552                         }
553                         return pkgerrors.Wrap(err, "Error adding instruction order to AppContext")
554                 }
555
556                 _, err = context.AddInstruction(csrHandle, "subresource", "dependency", string(subresDependency))
557                 if err != nil {
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,
564                                 })
565                         }
566                         return pkgerrors.Wrap(err, "Error adding instruction dependency to AppContext")
567                 }
568
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))
572         }
573         // save the context in the logicalcloud db record
574         lckey := LogicalCloudKey{
575                 LogicalCloudName: logicalcloud.MetaData.LogicalCloudName,
576                 Project:          project,
577         }
578         err = db.DBconn.Insert("orchestrator", lckey, nil, "lccontext", ctxVal)
579         if err != nil {
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,
584                         })
585                 }
586                 return pkgerrors.Wrap(err, "Error adding AppContext to DB")
587         }
588
589         // call resource synchronizer to instantiate the CRs in the cluster
590         err = callRsyncInstall(ctxVal)
591         if err != nil {
592                 return err
593         }
594
595         return nil
596
597 }
598
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 {
603
604         logicalCloudName := logicalcloud.MetaData.LogicalCloudName
605
606         _, ctxVal, err := NewLogicalCloudClient().GetLogicalCloudContext(project, logicalCloudName)
607         if err != nil {
608                 return pkgerrors.Wrapf(err, "Error finding AppContext for Logical Cloud: %v", logicalCloudName)
609         }
610
611         // call resource synchronizer to delete the CRs from every cluster of the logical cloud
612         err = callRsyncUninstall(ctxVal)
613         if err != nil {
614                 return err
615         }
616
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.........
622
623         // // remove the app context
624         // err = context.DeleteCompositeApp()
625         // if err != nil {
626         //      return pkgerrors.Wrap(err, "Error deleting AppContext CompositeApp")
627         // }
628
629         // remove the app context field from the cluster db record
630         // lckey := LogicalCloudKey{
631         //      LogicalCloudName: logicalcloud.MetaData.LogicalCloudName,
632         //      Project:          project,
633         // }
634         // err = db.DBconn.RemoveTag("orchestrator", lckey, "lccontext")
635         // if err != nil {
636         //      log.Warn("Error removing AppContext from Logical Cloud", log.Fields{
637         //              "logical-cloud": logicalCloudName,
638         //      })
639         // }
640
641         return nil
642 }