Create appContext and save to etcd 79/107079/5
authorRajamohan Raj <rajamohan.raj@intel.com>
Thu, 30 Apr 2020 23:07:15 +0000 (23:07 +0000)
committerRajamohan Raj <rajamohan.raj@intel.com>
Tue, 12 May 2020 19:47:41 +0000 (19:47 +0000)
In this patch, following tasks are accomplished

1. Creation of appContext and storing the appcontexts for each app in
the compositeApp into etcd as part of the instantiation process
2. Added a util method to extract parameters from k8s manifest files.
3. Added a new testing script to auto create NCM artifacts through the NCM APIs
4. Modified the existing plugin_collection_v2.sh to better test the
orchestrator APIs.
5. Added logging to appcontext lib
6. Bug fix in the helm charts.

Issue-ID: MULTICLOUD-1064
Signed-off-by: Rajamohan Raj <rajamohan.raj@intel.com>
Change-Id: I1b0e4d1351ad3a083be529239748015ea5db2a41

kud/tests/plugin_collection_v2.sh
kud/tests/plugin_ncm_v2.sh [new file with mode: 0755]
kud/tests/vnfs/comp-app/collection/app2/helm/prometheus/templates/role.yaml
kud/tests/vnfs/comp-app/collection/app2/helm/prometheus/templates/rolebinding.yaml
src/orchestrator/pkg/appcontext/appcontext.go
src/orchestrator/pkg/gpic/gpic.go
src/orchestrator/pkg/module/instantiation.go
src/orchestrator/utils/utils.go

index 09dec5c..5ebed6a 100755 (executable)
@@ -33,6 +33,7 @@ if [ ${1:+1} ]; then
 fi
 
 base_url=${base_url:-"http://localhost:9015/v2"}
+
 kubeconfig_path="$HOME/.kube/config"
 csar_id=cb009bfe-bbee-11e8-9766-525400435678
 
@@ -69,12 +70,12 @@ appIntentNameForApp1="appIntentForApp1"
 appIntentForApp1Desc="AppIntentForApp1Desc"
 appIntentNameForApp2="appIntentForApp2"
 appIntentForApp2Desc="AppIntentForApp2Desc"
-providerName1="aws"
-providerName2="azure"
-clusterName1="edge1"
-clusterName2="edge2"
-clusterLabelName1="east-us1"
-clusterLabelName2="east-us2"
+providerName1="cluster_provider1"
+providerName2="cluster_provider2"
+clusterName1="clusterName1"
+clusterName2="clusterName2"
+clusterLabelName1="clusterLabel1"
+clusterLabelName2="clusterLabel2"
 
 deploymentIntentGroupName="test_deployment_intent_group"
 deploymentIntentGroupNameDesc="test_deployment_intent_group_desc"
@@ -93,7 +94,53 @@ cloud_region_owner="localhost"
 install_deps
 populate_CSAR_composite_app_helm "$csar_id"
 
-# BEGIN: Register project API
+# BEGIN :: Delete statements are issued so that we clean up the 'orchestrator' collection
+# and freshly populate the documents, also it serves as a direct test
+# for all our DELETE APIs and an indirect test for all GET APIs
+
+
+print_msg "Deleting intentToBeAddedinDeploymentIntentGroup"
+delete_resource "${base_url}/projects/${project_name}/composite-apps/${composite_app_name}/${composite_app_version}/deployment-intent-groups/${deploymentIntentGroupName}/intents/${intentToBeAddedinDeploymentIntentGroup}"
+
+print_msg "Deleting ${deploymentIntentGroupName}"
+delete_resource "${base_url}/projects/${project_name}/composite-apps/${composite_app_name}/${composite_app_version}/deployment-intent-groups/${deploymentIntentGroupName}"
+
+print_msg "Deleting ${appIntentNameForApp2}"
+delete_resource "${base_url}/projects/${project_name}/composite-apps/${composite_app_name}/${composite_app_version}/generic-placement-intents/${genericPlacementIntentName}/app-intents/${appIntentNameForApp2}"
+
+print_msg "Deleting ${appIntentNameForApp1}"
+delete_resource "${base_url}/projects/${project_name}/composite-apps/${composite_app_name}/${composite_app_version}/generic-placement-intents/${genericPlacementIntentName}/app-intents/${appIntentNameForApp1}"
+
+print_msg "Deleting ${genericPlacementIntentName}"
+delete_resource "${base_url}/projects/${project_name}/composite-apps/${composite_app_name}/${composite_app_version}/generic-placement-intents/${genericPlacementIntentName}"
+
+print_msg "Deleting ${sub_composite_profile_name2}"
+delete_resource "${base_url}/projects/${project_name}/composite-apps/${composite_app_name}/${composite_app_version}/composite-profiles/${main_composite_profile_name}/profiles/${sub_composite_profile_name2}"
+
+print_msg "Deleting ${sub_composite_profile_name1}"
+delete_resource "${base_url}/projects/${project_name}/composite-apps/${composite_app_name}/${composite_app_version}/composite-profiles/${main_composite_profile_name}/profiles/${sub_composite_profile_name1}"
+
+print_msg "Deleting ${main_composite_profile_name}"
+delete_resource "${base_url}/projects/${project_name}/composite-apps/${composite_app_name}/${composite_app_version}/composite-profiles/${main_composite_profile_name}"
+
+print_msg "Deleting ${app2_name}"
+delete_resource "${base_url}/projects/${project_name}/composite-apps/${composite_app_name}/${composite_app_version}/apps/${app2_name}"
+
+print_msg "Deleting ${app1_name}"
+delete_resource "${base_url}/projects/${project_name}/composite-apps/${composite_app_name}/${composite_app_version}/apps/${app1_name}"
+
+print_msg "Deleting ${composite_app_name}/${composite_app_version}"
+delete_resource "${base_url}/projects/${project_name}/composite-apps/${composite_app_name}/${composite_app_version}"
+
+print_msg "Deleting ${project_name}"
+delete_resource "${base_url}/projects/${project_name}"
+
+# END :: Delete statements were issued so that we clean up the db
+# and freshly populate the documents, also it serves as a direct test
+# for all our DELETE APIs and an indirect test for all GET APIs
+
+
+# BEGIN: Register project
 print_msg "Registering project"
 payload="$(cat <<EOF
 {
@@ -107,9 +154,9 @@ payload="$(cat <<EOF
 EOF
 )"
 call_api -d "${payload}" "${base_url}/projects"
-# END: Register project API
+# END: Register project
 
-# BEGIN: Register composite-app API
+# BEGIN: Register composite-app
 print_msg "Registering composite-app"
 payload="$(cat <<EOF
 {
@@ -126,7 +173,7 @@ payload="$(cat <<EOF
 EOF
 )"
 call_api -d "${payload}" "${base_url}/projects/${project_name}/composite-apps"
-# END: Register composite-app API
+# END: Register composite-app
 
 
 
diff --git a/kud/tests/plugin_ncm_v2.sh b/kud/tests/plugin_ncm_v2.sh
new file mode 100755 (executable)
index 0000000..b7d791f
--- /dev/null
@@ -0,0 +1,196 @@
+# /*
+#  * Copyright 2020 Intel Corporation, Inc
+#  *
+#  * Licensed under the Apache License, Version 2.0 (the "License");
+#  * you may not use this file except in compliance with the License.
+#  * You may obtain a copy of the License at
+#  *
+#  *     http://www.apache.org/licenses/LICENSE-2.0
+#  *
+#  * Unless required by applicable law or agreed to in writing, software
+#  * distributed under the License is distributed on an "AS IS" BASIS,
+#  * 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.
+#  */
+
+set -o errexit
+set -o nounset
+set -o pipefail
+#set -o xtrace
+
+source _common_test.sh
+source _functions.sh
+source _common.sh
+
+base_url=${base_url:-"http://localhost:9016/v2"}
+
+kubeconfig_path="$HOME/.kube/config"
+
+cluster_provider_name1="cluster_provider1"
+cluster_provider_name2="cluster_provider2"
+cluster_provider_desc1="cluster_provider1_Desc"
+cluster_provider_desc2="cluster_provider2_Desc"
+userData1="user1"
+userData2="user2"
+
+clusterName1="clusterName1"
+cluster_desc1="cluster_desc1"
+clusterName2="clusterName2"
+cluster_desc2="cluster_desc2"
+#clusterName3 and clusterName4 shall be added with clusterLabel1 and clusterLabel2
+# under cluster_provider1 and cluster_provider2 respectively
+clusterName3="clusterName3"
+cluster_desc3="cluster_desc3"
+clusterName4="clusterName4"
+cluster_desc4="cluster_desc4"
+
+clusterLabel1="clusterLabel1"
+clusterLabel2="clusterLabel2"
+
+# BEGIN :: Delete statements are issued so that we clean up the 'cluster' collection
+# and freshly populate the documents, also it serves as a direct test
+# for all our DELETE APIs and an indirect test for all GET APIs
+print_msg "Deleting the clusterLabel1 and clusterLabel2, if they were existing"
+delete_resource "${base_url}/cluster-providers/${cluster_provider_name1}/clusters/${clusterName3}/labels/${clusterLabel1}"
+delete_resource "${base_url}/cluster-providers/${cluster_provider_name2}/clusters/${clusterName4}/labels/${clusterLabel2}"
+# Above statements delete the clusterLabel1 and clusterLabel2 which are linked to cluster3 and cluster4
+
+print_msg "Deleting the cluster1, cluster2, cluster3, cluster4 if they were existing"
+delete_resource "${base_url}/cluster-providers/${cluster_provider_name1}/clusters/${clusterName1}"
+delete_resource "${base_url}/cluster-providers/${cluster_provider_name2}/clusters/${clusterName2}"
+delete_resource "${base_url}/cluster-providers/${cluster_provider_name1}/clusters/${clusterName3}"
+delete_resource "${base_url}/cluster-providers/${cluster_provider_name2}/clusters/${clusterName4}"
+
+print_msg "Deleting the cluster-providers, if they were existing"
+delete_resource "${base_url}/cluster-providers/${cluster_provider_name1}"
+delete_resource "${base_url}/cluster-providers/${cluster_provider_name2}"
+
+# END :: Delete statements are issued so that we clean up the 'cluster' collection
+# and freshly populate the documents, also it serves as a direct test
+# for all our DELETE APIs and an indirect test for all GET APIs
+
+# BEGIN: Register cluster_provider_name1 and cluster_provider_name2
+print_msg "Deleting the cluster-providers, if they were existing"
+delete_resource "${base_url}/cluster-providers/${cluster_provider_name1}"
+delete_resource "${base_url}/cluster-providers/${cluster_provider_name2}"
+
+print_msg "Registering cluster_provider_name1"
+payload="$(cat <<EOF
+{
+  "metadata": {
+    "name": "${cluster_provider_name1}",
+    "description": "${cluster_provider_desc1}",
+    "userData1": "${userData1}",
+    "userData2": "${userData2}"
+   }
+}
+EOF
+)"
+call_api -d "${payload}" "${base_url}/cluster-providers"
+
+print_msg "Registering cluster_provider_name2"
+payload="$(cat <<EOF
+{
+  "metadata": {
+    "name": "${cluster_provider_name2}",
+    "description": "${cluster_provider_desc2}",
+    "userData1": "${userData1}",
+    "userData2": "${userData2}"
+   }
+}
+EOF
+)"
+call_api -d "${payload}" "${base_url}/cluster-providers"
+# END: Register cluster_provider_name1 and cluster_provider_name2
+
+# BEGIN : Register cluster1, cluster2, cluster3 and cluster4
+print_msg "Registering cluster1"
+payload="$(cat <<EOF
+{
+  "metadata": {
+    "name": "${clusterName1}",
+    "description": "${cluster_desc1}",
+    "userData1": "${userData1}",
+    "userData2": "${userData2}"
+   }
+}
+EOF
+)"
+call_api -F "metadata=$payload" \
+         -F "file=@$kubeconfig_path" \
+         "${base_url}/cluster-providers/${cluster_provider_name1}/clusters" >/dev/null #massive output
+
+
+print_msg "Registering cluster2"
+payload="$(cat <<EOF
+{
+  "metadata": {
+    "name": "${clusterName2}",
+    "description": "${cluster_desc2}",
+    "userData1": "${userData1}",
+    "userData2": "${userData2}"
+   }
+}
+EOF
+)"
+call_api -F "metadata=$payload" \
+         -F "file=@$kubeconfig_path" \
+         "${base_url}/cluster-providers/${cluster_provider_name2}/clusters" >/dev/null #massive output
+
+
+print_msg "Registering cluster3"
+payload="$(cat <<EOF
+{
+  "metadata": {
+    "name": "${clusterName3}",
+    "description": "${cluster_desc3}",
+    "userData1": "${userData1}",
+    "userData2": "${userData2}"
+   }
+}
+EOF
+)"
+call_api -F "metadata=$payload" \
+         -F "file=@$kubeconfig_path" \
+         "${base_url}/cluster-providers/${cluster_provider_name1}/clusters" >/dev/null #massive output
+
+
+print_msg "Registering cluster4"
+payload="$(cat <<EOF
+{
+  "metadata": {
+    "name": "${clusterName4}",
+    "description": "${cluster_desc4}",
+    "userData1": "${userData1}",
+    "userData2": "${userData2}"
+   }
+}
+EOF
+)"
+call_api -F "metadata=$payload" \
+         -F "file=@$kubeconfig_path" \
+         "${base_url}/cluster-providers/${cluster_provider_name2}/clusters" >/dev/null #massive output
+
+# END : Register cluster1, cluster2, cluster3 and cluster4
+
+
+# BEGIN: adding labels to cluster3 and cluster4
+print_msg "Adding label to cluster3"
+payload="$(cat <<EOF
+{
+  "label-name" : "${clusterLabel1}"
+}
+EOF
+)"
+call_api -d "${payload}" "${base_url}/cluster-providers/${cluster_provider_name1}/clusters/${clusterName3}/labels"
+
+print_msg "Adding label to cluster2"
+payload="$(cat <<EOF
+{
+  "label-name" : "${clusterLabel2}"
+}
+EOF
+)"
+call_api -d "${payload}" "${base_url}/cluster-providers/${cluster_provider_name2}/clusters/${clusterName4}/labels"
+
index dfb932d..a3c69c3 100644 (file)
@@ -1,7 +1,5 @@
 apiVersion: rbac.authorization.k8s.io/v1
 kind: Role
-apiVersion: rbac.authorization.k8s.io/v1
-kind: Role
 metadata:
   name: {{ template "prometheus.fullname" . }}-prometheus
   labels:
index 04932ee..a721cd4 100644 (file)
@@ -1,7 +1,5 @@
 apiVersion: rbac.authorization.k8s.io/v1
 kind: RoleBinding
-apiVersion: rbac.authorization.k8s.io/v1
-kind: RoleBinding
 metadata:
   name: {{ template "prometheus.fullname" . }}-prometheus
   labels:
index d92b1d1..8f7841a 100644 (file)
@@ -22,6 +22,8 @@ import (
 
        "github.com/onap/multicloud-k8s/src/orchestrator/pkg/rtcontext"
        pkgerrors "github.com/pkg/errors"
+       //"log"
+       log "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/logutils"
 )
 
 type AppContext struct {
@@ -81,6 +83,7 @@ func (ac *AppContext) AddApp(handle interface{}, appname string) (interface{}, e
        if err != nil {
                return nil, err
        }
+       log.Info(":: Added app handle ::", log.Fields{"AppHandle":h})
        return h, nil
 }
 
@@ -93,7 +96,7 @@ func (ac *AppContext) DeleteApp(handle interface{}) error {
        return nil
 }
 
-//Returns the hanlde for a given app
+//Returns the handle for a given app
 func (ac *AppContext) GetAppHandle(appname string) (interface{}, error) {
        if appname == "" {
                return nil, pkgerrors.Errorf("Not a valid run time context app name")
@@ -123,6 +126,7 @@ func (ac *AppContext) AddCluster(handle interface{}, clustername string) (interf
        if err != nil {
                return nil, err
        }
+       log.Info(":: Added cluster handle ::", log.Fields{"ClusterHandler":h})
        return h, nil
 }
 
@@ -193,11 +197,13 @@ func (ac *AppContext) GetClusterNames(appname string) ([]string, error) {
 }
 
 //Add resource under app and cluster
-func (ac *AppContext) AddResource(handle interface{}, resname string, value interface{}) (interface{}, error) {
+func (ac *AppContext) AddResource(handle interface{}, resname string, value []byte) (interface{}, error) {
        h, err := ac.rtc.RtcAddResource(handle, resname, value)
        if err != nil {
                return nil, err
        }
+       log.Info(":: Added resource handle ::", log.Fields{"ResourceHandler":h})
+
        return h, nil
 }
 
@@ -238,7 +244,7 @@ func (ac *AppContext) GetResourceHandle(appname string, clustername string, resn
 }
 
 //Update the resource value usign the given handle
-func (ac *AppContext) UpdateResourceValue(handle interface{}, value interface{}) error {
+func (ac *AppContext) UpdateResourceValue(handle interface{}, value []byte) error {
        return ac.rtc.RtcUpdateValue(handle, value)
 }
 
@@ -254,6 +260,7 @@ func (ac *AppContext) AddInstruction(handle interface{}, level string, insttype
        if err != nil {
                return nil, err
        }
+       log.Info(":: Added instruction handle ::", log.Fields{"InstructionHandler":h})
        return h, nil
 }
 
index f02e535..256d3b4 100644 (file)
@@ -22,14 +22,14 @@ package gpic
 */
 
 import (
-       "log"
        ncmmodule "github.com/onap/multicloud-k8s/src/ncm/pkg/module"
        pkgerrors "github.com/pkg/errors"
+       "log"
 )
 
 // Clusters has 1 field - a list of ClusterNames
 type Clusters struct {
-       ClustersWithName  []ClusterWithName
+       ClustersWithName []ClusterWithName
 }
 
 // ClusterWithName has two fields - ProviderName and ClusterName
@@ -82,7 +82,7 @@ func intentResolverHelper(pn, cn, cln string, clustersWithName []ClusterWithName
                for _, eachClusterName := range clusterNamesList {
                        eachClusterWithPN := ClusterWithName{pn, eachClusterName}
                        clustersWithName = append(clustersWithName, eachClusterWithPN)
-                       log.Printf("Added Cluster: %s ", cln)
+                       log.Printf("Added Cluster :: %s through its label: %s ", eachClusterName, cln)
                }
        }
        return clustersWithName, nil
@@ -95,13 +95,13 @@ func IntentResolver(intent IntentStruc) (Clusters, error) {
 
        for _, eachAllOf := range intent.AllOfArray {
                clustersWithName, err = intentResolverHelper(eachAllOf.ProviderName, eachAllOf.ClusterName, eachAllOf.ClusterLabelName, clustersWithName)
-               if err!=nil {
+               if err != nil {
                        return Clusters{}, pkgerrors.Wrap(err, "intentResolverHelper error")
                }
                if len(eachAllOf.AnyOfArray) > 0 {
                        for _, eachAnyOf := range eachAllOf.AnyOfArray {
                                clustersWithName, err = intentResolverHelper(eachAnyOf.ProviderName, eachAnyOf.ClusterName, eachAnyOf.ClusterLabelName, clustersWithName)
-                               if err!=nil {
+                               if err != nil {
                                        return Clusters{}, pkgerrors.Wrap(err, "intentResolverHelper error")
                                }
                        }
@@ -110,7 +110,7 @@ func IntentResolver(intent IntentStruc) (Clusters, error) {
        if len(intent.AnyOfArray) > 0 {
                for _, eachAnyOf := range intent.AnyOfArray {
                        clustersWithName, err = intentResolverHelper(eachAnyOf.ProviderName, eachAnyOf.ClusterName, eachAnyOf.ClusterLabelName, clustersWithName)
-                       if err!=nil {
+                       if err != nil {
                                return Clusters{}, pkgerrors.Wrap(err, "intentResolverHelper error")
                        }
                }
index 5602154..58706ef 100644 (file)
 package module
 
 import (
+       "encoding/base64"
        "fmt"
-
+       "github.com/onap/multicloud-k8s/src/orchestrator/pkg/appcontext"
        gpic "github.com/onap/multicloud-k8s/src/orchestrator/pkg/gpic"
-
-       "encoding/base64"
-
+       "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db"
+       "github.com/onap/multicloud-k8s/src/orchestrator/utils"
        "github.com/onap/multicloud-k8s/src/orchestrator/utils/helm"
        pkgerrors "github.com/pkg/errors"
-       "log"
+       "io/ioutil"
+       //"log"
+       log "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/logutils"
 )
 
 // ManifestFileName is the name given to the manifest file in the profile package
@@ -34,10 +36,29 @@ const ManifestFileName = "manifest.yaml"
 // GenericPlacementIntentName denotes the generic placement intent name
 const GenericPlacementIntentName = "generic-placement-intent"
 
+// SEPARATOR used while creating clusternames to store in etcd
+const SEPARATOR = "+"
+
 // InstantiationClient implements the InstantiationManager
 type InstantiationClient struct {
-       storeName   string
-       tagMetaData string
+       db InstantiationClientDbInfo
+}
+
+/*
+InstantiationKey used in storing the contextid in the momgodb
+It consists of
+GenericPlacementIntentName,
+ProjectName,
+CompositeAppName,
+CompositeAppVersion,
+DeploymentIntentGroup
+*/
+type InstantiationKey struct {
+       IntentName            string
+       Project               string
+       CompositeApp          string
+       Version               string
+       DeploymentIntentGroup string
 }
 
 // InstantiationManager is an interface which exposes the
@@ -47,11 +68,19 @@ type InstantiationManager interface {
        Instantiate(p string, ca string, v string, di string) error
 }
 
+// InstantiationClientDbInfo consists of storeName and tagContext
+type InstantiationClientDbInfo struct {
+       storeName  string // name of the mongodb collection to use for Instantiationclient documents
+       tagContext string // attribute key name for context object in App Context
+}
+
 // NewInstantiationClient returns an instance of InstantiationClient
 func NewInstantiationClient() *InstantiationClient {
        return &InstantiationClient{
-               storeName:   "orchestrator",
-               tagMetaData: "instantiation",
+               db: InstantiationClientDbInfo{
+                       storeName:  "orchestrator",
+                       tagContext: "contextid",
+               },
        }
 }
 
@@ -70,10 +99,10 @@ func getOverrideValuesByAppName(ov []OverrideValues, a string) map[string]string
 }
 
 /*
-FindGenericPlacementIntent takes in projectName, CompositeAppName, CompositeAppVersion, DeploymentIntentName
+findGenericPlacementIntent takes in projectName, CompositeAppName, CompositeAppVersion, DeploymentIntentName
 and returns the name of the genericPlacementIntentName. Returns empty value if string not found.
 */
-func FindGenericPlacementIntent(p, ca, v, di string) (string, error) {
+func findGenericPlacementIntent(p, ca, v, di string) (string, error) {
        var gi string
        var found bool
        iList, err := NewIntentClient().GetAllIntents(p, ca, v, di)
@@ -82,7 +111,7 @@ func FindGenericPlacementIntent(p, ca, v, di string) (string, error) {
        }
        for _, eachMap := range iList.ListOfIntents {
                if gi, found := eachMap[GenericPlacementIntentName]; found {
-                       log.Printf("::Name of the generic-placement-intent:: %s", gi)
+                       log.Info(":: Name of the generic-placement-intent ::", log.Fields{"GenPlmtIntent":gi})
                        return gi, err
                }
        }
@@ -97,7 +126,8 @@ func FindGenericPlacementIntent(p, ca, v, di string) (string, error) {
 //It takes in arguments - appName, project, compositeAppName, releaseName, compositeProfileName, array of override values
 func GetSortedTemplateForApp(appName, p, ca, v, rName, cp string, overrideValues []OverrideValues) ([]helm.KubernetesResourceTemplate, error) {
 
-       log.Println("Processing App.. ", appName)
+
+       log.Info(":: Processing App ::", log.Fields{"appName":appName})
 
        var sortedTemplates []helm.KubernetesResourceTemplate
 
@@ -109,7 +139,8 @@ func GetSortedTemplateForApp(appName, p, ca, v, rName, cp string, overrideValues
        if err != nil {
                return sortedTemplates, pkgerrors.Wrap(err, "Fail to convert to byte array")
        }
-       log.Println("Got the app content..")
+
+       log.Info(":: Got the app content.. ::", log.Fields{"appName":appName})
 
        appPC, err := NewAppProfileClient().GetAppProfileContentByApp(p, ca, v, cp, appName)
        if err != nil {
@@ -120,7 +151,7 @@ func GetSortedTemplateForApp(appName, p, ca, v, rName, cp string, overrideValues
                return sortedTemplates, pkgerrors.Wrap(err, "Fail to convert to byte array")
        }
 
-       log.Println("Got the app Profile content ...")
+       log.Info(":: Got the app Profile content .. ::", log.Fields{"appName":appName})
 
        overrideValuesOfApp := getOverrideValuesByAppName(overrideValues, appName)
        //Convert override values from map to array of strings of the following format
@@ -137,12 +168,111 @@ func GetSortedTemplateForApp(appName, p, ca, v, rName, cp string, overrideValues
                appProfileContent, overrideValuesOfAppStr,
                appName)
 
-       log.Printf("The len of the sortedTemplates :: %d", len(sortedTemplates))
+       log.Info(":: Total no. of sorted templates ::", log.Fields{"len(sortedTemplates):":len(sortedTemplates)})
 
        return sortedTemplates, err
 }
 
-// Instantiate methods takes in project
+// resource consists of name of reource
+type resource struct {
+       name        string
+       filecontent []byte
+}
+
+// getResources shall take in the sorted templates and output the resources
+// which consists of name(name+kind) and filecontent
+func getResources(st []helm.KubernetesResourceTemplate) ([]resource, error) {
+       var resources []resource
+       for _, t := range st {
+               yamlStruct, err := utils.ExtractYamlParameters(t.FilePath)
+               yamlFile, err := ioutil.ReadFile(t.FilePath)
+               if err != nil {
+                       return nil, pkgerrors.Wrap(err, "Failed to get the resources..")
+               }
+               n := yamlStruct.Metadata.Name + SEPARATOR + yamlStruct.Kind
+
+               resources = append(resources, resource{name: n, filecontent: yamlFile})
+
+               log.Info(":: Added resource into resource-order ::", log.Fields{"ResourceName":n})
+       }
+       return resources, nil
+}
+
+func addResourcesToCluster(ct appcontext.AppContext, ch interface{}, resources []resource, resourceOrder []string) error {
+       for _, resource := range resources {
+
+               resourceOrder = append(resourceOrder, resource.name)
+               _, err := ct.AddResource(ch, resource.name, resource.filecontent)
+               if err != nil {
+                       cleanuperr := ct.DeleteCompositeApp()
+                       if cleanuperr != nil {
+                               log.Info(":: Error Cleaning up AppContext after add resource failure ::", log.Fields{"Resource":resource.name, "Error":cleanuperr.Error})
+                       }
+                       return pkgerrors.Wrapf(err, "Error adding resource ::%s to AppContext", resource.name)
+               }
+               _, err = ct.AddInstruction(ch, "resource", "order", resourceOrder)
+               if err != nil {
+                       cleanuperr := ct.DeleteCompositeApp()
+                       if cleanuperr != nil {
+                               log.Info(":: Error Cleaning up AppContext after add instruction failure ::", log.Fields{"Resource":resource.name, "Error":cleanuperr.Error})
+                       }
+                       return pkgerrors.Wrapf(err, "Error adding instruction for resource ::%s to AppContext", resource.name)
+               }
+       }
+       return nil
+}
+
+func addClustersToAppContext(l gpic.Clusters, ct appcontext.AppContext, appHandle interface{}, resources []resource) error {
+       for _, c := range l.ClustersWithName {
+               p := c.ProviderName
+               n := c.ClusterName
+               var resourceOrder []string
+               clusterhandle, err := ct.AddCluster(appHandle, p+SEPARATOR+n)
+               if err != nil {
+                       cleanuperr := ct.DeleteCompositeApp()
+                       if cleanuperr != nil {
+                               log.Info(":: Error Cleaning up AppContext after add cluster failure ::", log.Fields{"cluster-provider":p, "cluster-name":n, "Error":cleanuperr.Error})
+                       }
+                       return pkgerrors.Wrapf(err, "Error adding Cluster(provider::%s and name::%s) to AppContext", p, n)
+               }
+
+               err = addResourcesToCluster(ct, clusterhandle, resources, resourceOrder)
+               if err != nil {
+                       return pkgerrors.Wrapf(err, "Error adding Resources to Cluster(provider::%s and name::%s) to AppContext", p, n)
+               }
+       }
+       return nil
+}
+
+/*
+verifyResources method is just to check if the resource handles are correctly saved.
+*/
+
+func verifyResources(l gpic.Clusters, ct appcontext.AppContext, resources []resource, appName string) error {
+       for _, c := range l.ClustersWithName {
+               p := c.ProviderName
+               n := c.ClusterName
+               cn := p + SEPARATOR + n
+               for _, res := range resources {
+
+                       rh, err := ct.GetResourceHandle(appName, cn, res.name)
+                       if err != nil {
+                               return pkgerrors.Wrapf(err, "Error getting resoure handle for resource :: %s, app:: %s, cluster :: %s", appName, res.name, cn)
+                       }
+                       log.Info(":: GetResourceHandle ::", log.Fields{"ResourceHandler":rh, "appName":appName, "Cluster": cn, "Resource":res.name})
+
+               }
+
+       }
+
+       return nil
+}
+
+/*
+Instantiate methods takes in projectName, compositeAppName, compositeAppVersion,
+DeploymentIntentName. This method is responsible for template resolution, intent
+resolution, creation and saving of context for saving into etcd.
+*/
 func (c InstantiationClient) Instantiate(p string, ca string, v string, di string) error {
 
        dIGrp, err := NewDeploymentIntentGroupClient().GetDeploymentIntentGroup(di, p, ca, v)
@@ -153,36 +283,102 @@ func (c InstantiationClient) Instantiate(p string, ca string, v string, di strin
        overrideValues := dIGrp.Spec.OverrideValuesObj
        cp := dIGrp.Spec.Profile
 
-       gIntent, err := FindGenericPlacementIntent(p, ca, v, di)
+       gIntent, err := findGenericPlacementIntent(p, ca, v, di)
        if err != nil {
                return err
        }
-       log.Printf("The name of the GenPlacIntent:: %s", gIntent)
 
-       log.Printf("dIGrp :: %s, releaseName :: %s and cp :: %s \n", dIGrp.MetaData.Name, rName, cp)
+       log.Info(":: The name of the GenPlacIntent ::", log.Fields{"GenPlmtIntent":gIntent})
+       log.Info(":: DeploymentIntentGroup, ReleaseName, CompositeProfile ::", log.Fields{"dIGrp":dIGrp.MetaData.Name, "releaseName":rName, "cp":cp})
+
        allApps, err := NewAppClient().GetApps(p, ca, v)
        if err != nil {
                return pkgerrors.Wrap(err, "Not finding the apps")
        }
+
+       // Make an app context for the compositeApp
+       context := appcontext.AppContext{}
+       ctxval, err := context.InitAppContext()
+       if err != nil {
+               return pkgerrors.Wrap(err, "Error creating AppContext CompositeApp")
+       }
+       compositeHandle, err := context.CreateCompositeApp()
+       if err != nil {
+               return pkgerrors.Wrap(err, "Error creating AppContext")
+       }
+
+       var appOrder []string
+
+       // Add composite app using appContext
        for _, eachApp := range allApps {
+               appOrder = append(appOrder, eachApp.Metadata.Name)
                sortedTemplates, err := GetSortedTemplateForApp(eachApp.Metadata.Name, p, ca, v, rName, cp, overrideValues)
+
                if err != nil {
                        return pkgerrors.Wrap(err, "Unable to get the sorted templates for app")
                }
-               log.Printf("Resolved all the templates for app :: %s under the compositeApp...", eachApp.Metadata.Name)
-               log.Printf("sortedTemplates :: %v ", sortedTemplates)
+
+               log.Info(":: Resolved all the templates ::", log.Fields{"appName":eachApp.Metadata.Name, "SortedTemplate":sortedTemplates})
+
+               resources, err := getResources(sortedTemplates)
+               if err != nil {
+                       return pkgerrors.Wrapf(err, "Unable to get the resources for app :: %s", eachApp.Metadata.Name)
+               }
 
                specData, err := NewAppIntentClient().GetAllIntentsByApp(eachApp.Metadata.Name, p, ca, v, gIntent)
                if err != nil {
                        return pkgerrors.Wrap(err, "Unable to get the intents for app")
                }
-               listOfClusters,err := gpic.IntentResolver(specData.Intent)
-               if err!=nil {
+               listOfClusters, err := gpic.IntentResolver(specData.Intent)
+               if err != nil {
                        return pkgerrors.Wrap(err, "Unable to get the intents resolved for app")
                }
-               log.Printf("::listOfClusters:: %v", listOfClusters)
 
+               log.Info(":: listOfClusters ::", log.Fields{"listOfClusters":listOfClusters})
+
+               //BEGIN: storing into etcd
+               // Add an app to the app context
+               apphandle, err := context.AddApp(compositeHandle, eachApp.Metadata.Name)
+               if err != nil {
+                       cleanuperr := context.DeleteCompositeApp()
+                       if cleanuperr != nil {
+                               log.Info(":: Error Cleaning up AppContext compositeApp failure ::", log.Fields{"Error":cleanuperr.Error(), "AppName":eachApp.Metadata.Name})
+                       }
+                       return pkgerrors.Wrap(err, "Error adding App to AppContext")
+               }
+               err = addClustersToAppContext(listOfClusters, context, apphandle, resources)
+               if err != nil {
+                       log.Info(":: Error while adding cluster and resources to app ::", log.Fields{"Error":err.Error(), "AppName":eachApp.Metadata.Name})
+               }
+               err = verifyResources(listOfClusters, context, resources, eachApp.Metadata.Name)
+               if err != nil {
+                       log.Info(":: Error while verifying resources in app ::", log.Fields{"Error":err.Error(), "AppName":eachApp.Metadata.Name})
+               }
+
+       }
+       context.AddInstruction(compositeHandle, "app", "order", appOrder)
+       //END: storing into etcd
+
+       // BEGIN:: save the context in the orchestrator db record
+       key := InstantiationKey{
+               IntentName:            gIntent,
+               Project:               p,
+               CompositeApp:          ca,
+               Version:               v,
+               DeploymentIntentGroup: di,
+       }
+
+       err = db.DBconn.Insert(c.db.storeName, key, nil, c.db.tagContext, ctxval)
+       if err != nil {
+               cleanuperr := context.DeleteCompositeApp()
+               if cleanuperr != nil {
+
+                       log.Info(":: Error Cleaning up AppContext while saving context in the db for GPIntent ::", log.Fields{"Error":cleanuperr.Error(), "GPIntent":gIntent, "DeploymentIntentGroup":di, "CompositeApp":ca, "CompositeAppVersion":v, "Project":p})
+               }
+               return pkgerrors.Wrap(err, "Error adding AppContext to DB")
        }
-       log.Printf("Done with instantiation...")
+       // END:: save the context in the orchestrator db record
+
+       log.Info(":: Done with instantiation... ::", log.Fields{"CompositeAppName":ca})
        return err
 }
index 13c78ba..22ce903 100644 (file)
@@ -19,15 +19,105 @@ package utils
 import (
        "archive/tar"
        "compress/gzip"
+       "strings"
+
        "io"
        "io/ioutil"
+       "log"
        "os"
        "path"
        "path/filepath"
 
        pkgerrors "github.com/pkg/errors"
+       yaml "gopkg.in/yaml.v3"
 )
 
+// ListYamlStruct is applied when the kind is list
+type ListYamlStruct struct {
+       APIVersion string       `yaml:"apiVersion,omitempty"`
+       Kind       string       `yaml:"kind,omitempty"`
+       items      []YamlStruct `yaml:"items,omitempty"`
+}
+
+// YamlStruct represents normal parameters in a manifest file.
+// Over the course of time, Pls add more parameters as and when you require.
+type YamlStruct struct {
+       APIVersion string `yaml:"apiVersion,omitempty"`
+       Kind       string `yaml:"kind,omitempty"`
+       Metadata   struct {
+               Name      string `yaml:"name,omitempty"`
+               Namespace string `yaml:"namespace,omitempty"`
+               Labels    struct {
+                       RouterDeisIoRoutable string `yaml:"router.deis.io/routable,omitempty"`
+               } `yaml:"labels"`
+               Annotations struct {
+                       RouterDeisIoDomains string `yaml:"router.deis.io/domains,omitempty"`
+               } `yaml:"annotations,omitempty"`
+       } `yaml:"metadata,omitempty"`
+       Spec struct {
+               Type     string `yaml:"type,omitempty"`
+               Selector struct {
+                       App string `yaml:"app,omitempty"`
+               } `yaml:"selector,omitempty"`
+               Ports []struct {
+                       Name     string `yaml:"name,omitempty"`
+                       Port     int    `yaml:"port,omitempty"`
+                       NodePort int    `yaml:"nodePort,omitempty"`
+               } `yaml:"ports"`
+       } `yaml:"spec"`
+}
+
+func (y YamlStruct) isValid() bool {
+       if y.APIVersion == "" {
+               log.Printf("apiVersion is missing in manifest file")
+               return false
+       }
+       if y.Kind == "" {
+               log.Printf("kind is missing in manifest file")
+               return false
+       }
+       if y.Metadata.Name == "" {
+               log.Printf("metadata.name is missing in manifest file")
+               return false
+       }
+       return true
+}
+
+// ExtractYamlParameters is a method which takes in the abolute path of a manifest file
+// and returns a struct accordingly
+func ExtractYamlParameters(f string) (YamlStruct, error) {
+       filename, _ := filepath.Abs(f)
+       yamlFile, err := ioutil.ReadFile(filename)
+
+       var yamlStruct YamlStruct
+
+       err = yaml.Unmarshal(yamlFile, &yamlStruct)
+       if err != nil {
+               return YamlStruct{}, pkgerrors.New("Cant unmarshal yaml file ..")
+       }
+
+       /* This is a special case handling when the kind is "List".
+       When the kind is list and the metadata name is empty.
+       We set the metadata name as the file name. For eg:
+       if filename is "/tmp/helm-tmpl-240995533/prometheus/templates/serviceaccount.yaml-0".
+       We set metadata name as "serviceaccount.yaml-0"
+       Usually when the kind is list, the list might contains a list of
+       */
+       if yamlStruct.Kind == "List" && yamlStruct.Metadata.Name == "" {
+               li := strings.LastIndex(filename, "/")
+               fn := string(filename[li+1:])
+               yamlStruct.Metadata.Name = fn
+               log.Printf("Setting the metadata name as :: %s", fn)
+       }
+       if yamlStruct.isValid() {
+               log.Printf("YAML parameters for file ::%s \n %v", f, yamlStruct)
+               return yamlStruct, nil
+       }
+       log.Printf("YAML file ::%s has errors", f)
+       return YamlStruct{}, pkgerrors.Errorf("Cant extract parameters from yaml file :: %s", filename)
+
+}
+
 //ExtractTarBall provides functionality to extract a tar.gz file
 //into a temporary location for later use.
 //It returns the path to the new location
@@ -114,3 +204,13 @@ func EnsureDirectory(f string) error {
        }
        return os.MkdirAll(base, 0755)
 }
+
+// func main() {
+//     filename := "./test.yaml"
+//     yamlStruct, err := ExtractYamlParameters(filename)
+//     if err!=nil {
+//             log.Print(err)
+//     }
+//     fmt.Printf("%s+%s", yamlStruct.Metadata.Name, yamlStruct.Kind)
+//     fmt.Printf("%v", yamlStruct)
+// }