Provide Healthcheck API MVP 41/118441/4 0.8.0
authorKonrad Bańka <k.banka@samsung.com>
Wed, 24 Feb 2021 23:08:05 +0000 (00:08 +0100)
committerKonrad Bańka <k.banka@samsung.com>
Thu, 25 Feb 2021 14:30:45 +0000 (15:30 +0100)
Implements basic functionality of running starting Healthcheck.
Results can be inspected so-far without dedicated API, by using, for
example, Query API.

Issue-ID: MULTICLOUD-1233
Signed-off-by: Konrad Bańka <k.banka@samsung.com>
Change-Id: Ia4d96d936d573173d7d8f41e6c39d059bf5f8b1f

20 files changed:
src/k8splugin/api/api.go
src/k8splugin/api/brokerhandler_test.go
src/k8splugin/api/defhandler_test.go
src/k8splugin/api/healthcheckhandler_test.go
src/k8splugin/api/instancehandler_test.go
src/k8splugin/api/instancehchandler.go [new file with mode: 0644]
src/k8splugin/api/profilehandler_test.go
src/k8splugin/cmd/main.go
src/k8splugin/go.sum
src/k8splugin/internal/app/client.go
src/k8splugin/internal/app/client_test.go
src/k8splugin/internal/app/config_backend.go
src/k8splugin/internal/app/instance.go
src/k8splugin/internal/app/instance_test.go
src/k8splugin/internal/healthcheck/healthcheck.go [new file with mode: 0644]
src/k8splugin/internal/healthcheck/kubeclient.go [new file with mode: 0644]
src/k8splugin/internal/helm/helm.go
src/k8splugin/internal/helm/helm_test.go
src/k8splugin/internal/rb/profile.go
src/k8splugin/internal/rb/profile_test.go

index a7aa0be..2c53a29 100644 (file)
@@ -17,6 +17,7 @@ package api
 import (
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/app"
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/connection"
+       "github.com/onap/multicloud-k8s/src/k8splugin/internal/healthcheck"
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/rb"
 
        "github.com/gorilla/mux"
@@ -28,7 +29,8 @@ func NewRouter(defClient rb.DefinitionManager,
        instClient app.InstanceManager,
        configClient app.ConfigManager,
        connectionClient connection.ConnectionManager,
-       templateClient rb.ConfigTemplateManager) *mux.Router {
+       templateClient rb.ConfigTemplateManager,
+       healthcheckClient healthcheck.InstanceHCManager) *mux.Router {
 
        router := mux.NewRouter()
 
@@ -120,6 +122,16 @@ func NewRouter(defClient rb.DefinitionManager,
        instRouter.HandleFunc("/instance/{instID}/config/rollback", configHandler.rollbackHandler).Methods("POST")
        instRouter.HandleFunc("/instance/{instID}/config/tagit", configHandler.tagitHandler).Methods("POST")
 
+       // Instance Healthcheck API
+       if healthcheckClient == nil {
+               healthcheckClient = healthcheck.NewHCClient()
+       }
+       healthcheckHandler := instanceHCHandler{client: healthcheckClient}
+       instRouter.HandleFunc("/instance/{instID}/healthcheck", healthcheckHandler.listHandler).Methods("GET")
+       instRouter.HandleFunc("/instance/{instID}/healthcheck", healthcheckHandler.createHandler).Methods("POST")
+       instRouter.HandleFunc("/instance/{instID}/healthcheck/{hcID}", healthcheckHandler.getHandler).Methods("GET")
+       instRouter.HandleFunc("/instance/{instID}/healthcheck/{hcID}", healthcheckHandler.deleteHandler).Methods("DELETE")
+
        // Add healthcheck path
        instRouter.HandleFunc("/healthcheck", healthCheckHandler).Methods("GET")
 
index c822f6d..97c8a39 100644 (file)
@@ -313,7 +313,7 @@ func TestBrokerCreateHandler(t *testing.T) {
                t.Run(testCase.label, func(t *testing.T) {
 
                        request := httptest.NewRequest("POST", "/cloudowner/cloudregion/infra_workload", testCase.input)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
                        defer resp.Body.Close()
 
                        if testCase.expectedCode != resp.StatusCode {
@@ -409,7 +409,7 @@ func TestBrokerGetHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/cloudowner/cloudregion/infra_workload/"+testCase.input, nil)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                t.Fatalf("Request method returned: %v and it was expected: %v",
@@ -489,7 +489,7 @@ func TestBrokerFindHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/cloudowner/cloudregion/infra_workload?name="+testCase.input, nil)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                t.Fatalf("Request method returned: %v and it was expected: %v",
@@ -551,7 +551,7 @@ func TestBrokerDeleteHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("DELETE", "/cloudowner/cloudregion/infra_workload/"+testCase.input, nil)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                t.Fatalf("Request method returned: %v and it was expected: %v", resp.StatusCode, testCase.expectedCode)
index dcfea1d..bb2f9dc 100644 (file)
@@ -139,7 +139,7 @@ func TestRBDefCreateHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("POST", "/v1/rb/definition", testCase.reader)
-                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -208,7 +208,7 @@ func TestRBDefListVersionsHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v1/rb/definition/testresourcebundle", nil)
-                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -288,7 +288,7 @@ func TestRBDefListAllHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v1/rb/definition", nil)
-                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -368,7 +368,7 @@ func TestRBDefGetHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v1/rb/definition/"+testCase.name+"/"+testCase.version, nil)
-                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -419,7 +419,7 @@ func TestRBDefDeleteHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("DELETE", "/v1/rb/definition/"+testCase.name+"/"+testCase.version, nil)
-                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -476,7 +476,7 @@ func TestRBDefUploadHandler(t *testing.T) {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("POST",
                                "/v1/rb/definition/"+testCase.name+"/"+testCase.version+"/content", testCase.body)
-                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(testCase.rbDefClient, nil, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
index ab9322f..293ddf9 100644 (file)
@@ -35,7 +35,7 @@ func TestHealthCheckHandler(t *testing.T) {
                        Err: nil,
                }
                request := httptest.NewRequest("GET", "/v1/healthcheck", nil)
-               resp := executeRequest(request, NewRouter(nil, nil, nil, nil, nil, nil))
+               resp := executeRequest(request, NewRouter(nil, nil, nil, nil, nil, nil, nil))
 
                //Check returned code
                if resp.StatusCode != http.StatusOK {
@@ -48,7 +48,7 @@ func TestHealthCheckHandler(t *testing.T) {
                        Err: pkgerrors.New("Runtime Error in DB"),
                }
                request := httptest.NewRequest("GET", "/v1/healthcheck", nil)
-               resp := executeRequest(request, NewRouter(nil, nil, nil, nil, nil, nil))
+               resp := executeRequest(request, NewRouter(nil, nil, nil, nil, nil, nil, nil))
 
                //Check returned code
                if resp.StatusCode != http.StatusInternalServerError {
index c0690fb..8e6c72f 100644 (file)
@@ -194,7 +194,7 @@ func TestInstanceCreateHandler(t *testing.T) {
                t.Run(testCase.label, func(t *testing.T) {
 
                        request := httptest.NewRequest("POST", "/v1/instance", testCase.input)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                body, _ := ioutil.ReadAll(resp.Body)
@@ -295,7 +295,7 @@ func TestInstanceGetHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v1/instance/"+testCase.input, nil)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                t.Fatalf("Request method returned: %v and it was expected: %v",
@@ -430,7 +430,7 @@ func TestInstanceListHandler(t *testing.T) {
                                }
                                request.URL.RawQuery = q.Encode()
                        }
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                t.Fatalf("Request method returned: %v and it was expected: %v",
@@ -489,7 +489,7 @@ func TestDeleteHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("DELETE", "/v1/instance/"+testCase.input, nil)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil, nil))
 
                        if testCase.expectedCode != resp.StatusCode {
                                t.Fatalf("Request method returned: %v and it was expected: %v", resp.StatusCode, testCase.expectedCode)
diff --git a/src/k8splugin/api/instancehchandler.go b/src/k8splugin/api/instancehchandler.go
new file mode 100644 (file)
index 0000000..fc1c3be
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+Copyright © 2021 Samsung Electronics
+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.
+*/
+
+package api
+
+import (
+       "encoding/json"
+       "net/http"
+
+       "github.com/onap/multicloud-k8s/src/k8splugin/internal/healthcheck"
+       log "github.com/onap/multicloud-k8s/src/k8splugin/internal/logutils"
+
+       "github.com/gorilla/mux"
+)
+
+// Used to store the backend implementation objects
+// Also simplifies the mocking needed for unit testing
+type instanceHCHandler struct {
+       // Interface that implements Healthcheck operations
+       client healthcheck.InstanceHCManager
+}
+
+func (ih instanceHCHandler) createHandler(w http.ResponseWriter, r *http.Request) {
+       vars := mux.Vars(r)
+       id := vars["instID"]
+
+       resp, err := ih.client.Create(id)
+       if err != nil {
+               log.Error("Error scheduling healhtcheck", log.Fields{
+                       "error":    err,
+                       "instance": id,
+                       "response": resp,
+               })
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(http.StatusOK)
+       err = json.NewEncoder(w).Encode(resp)
+       if err != nil {
+               log.Error("Error Marshaling Reponse", log.Fields{
+                       "error":    err,
+                       "response": resp,
+               })
+               http.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+}
+
+func (ih instanceHCHandler) getHandler(w http.ResponseWriter, r *http.Request) {
+}
+
+func (ih instanceHCHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
+}
+
+func (ih instanceHCHandler) listHandler(w http.ResponseWriter, r *http.Request) {
+}
index 9ec9c54..6897e01 100644 (file)
@@ -127,7 +127,7 @@ func TestRBProfileCreateHandler(t *testing.T) {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("POST", "/v1/rb/definition/test-rbdef/v1/profile",
                                testCase.reader)
-                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -207,7 +207,7 @@ func TestRBProfileGetHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v1/rb/definition/test-rbdef/v1/profile/"+testCase.prname, nil)
-                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -288,7 +288,7 @@ func TestRBProfileListHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("GET", "/v1/rb/definition/"+testCase.def+"/"+testCase.version+"/profile", nil)
-                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -347,7 +347,7 @@ func TestRBProfileDeleteHandler(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("DELETE", "/v1/rb/definition/test-rbdef/v1/profile/"+testCase.prname, nil)
-                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
@@ -400,7 +400,7 @@ func TestRBProfileUploadHandler(t *testing.T) {
                t.Run(testCase.label, func(t *testing.T) {
                        request := httptest.NewRequest("POST",
                                "/v1/rb/definition/test-rbdef/v1/profile/"+testCase.prname+"/content", testCase.body)
-                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil))
+                       resp := executeRequest(request, NewRouter(nil, testCase.rbProClient, nil, nil, nil, nil, nil))
 
                        //Check returned code
                        if resp.StatusCode != testCase.expectedCode {
index 8f9ffc5..2b7346b 100644 (file)
@@ -1,5 +1,6 @@
 /*
 Copyright 2018 Intel Corporation.
+Copyright © 2021 Samsung Electronics
 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
@@ -39,7 +40,7 @@ func main() {
 
        rand.Seed(time.Now().UnixNano())
 
-       httpRouter := api.NewRouter(nil, nil, nil, nil, nil, nil)
+       httpRouter := api.NewRouter(nil, nil, nil, nil, nil, nil, nil)
        loggedRouter := handlers.LoggingHandler(os.Stdout, httpRouter)
        log.Println("Starting Kubernetes Multicloud API")
 
index af48913..17d143a 100644 (file)
@@ -241,6 +241,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
 github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/onap/multicloud-k8s v0.0.0-20210224130448-2f09583725c8 h1:eJ6xWCP7UmTqn+5Z8emFxI0rH/EOPzWupxw0G7hBkxs=
 github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
index f0edf8c..85fefe6 100644 (file)
@@ -16,6 +16,7 @@ limitations under the License.
 package app
 
 import (
+       "io/ioutil"
        "os"
        "strings"
        "time"
@@ -32,9 +33,11 @@ import (
        "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
        "k8s.io/apimachinery/pkg/runtime"
        "k8s.io/apimachinery/pkg/runtime/schema"
+       "k8s.io/client-go/discovery"
        "k8s.io/client-go/discovery/cached/disk"
        "k8s.io/client-go/dynamic"
        "k8s.io/client-go/kubernetes"
+       "k8s.io/client-go/rest"
        "k8s.io/client-go/restmapper"
        "k8s.io/client-go/tools/clientcmd"
 )
@@ -42,6 +45,8 @@ import (
 // KubernetesClient encapsulates the different clients' interfaces
 // we need when interacting with a Kubernetes cluster
 type KubernetesClient struct {
+       rawConfig      clientcmd.ClientConfig
+       restConfig     *rest.Config
        clientSet      kubernetes.Interface
        dynamicClient  dynamic.Interface
        discoverClient *disk.CachedDiscoveryClient
@@ -168,8 +173,8 @@ func (k *KubernetesClient) getKubeConfig(cloudregion string) (string, error) {
        return kubeConfigPath, nil
 }
 
-// init loads the Kubernetes configuation values stored into the local configuration file
-func (k *KubernetesClient) init(cloudregion string, iid string) error {
+// Init loads the Kubernetes configuation values stored into the local configuration file
+func (k *KubernetesClient) Init(cloudregion string, iid string) error {
        if cloudregion == "" {
                return pkgerrors.New("Cloudregion is empty")
        }
@@ -209,6 +214,21 @@ func (k *KubernetesClient) init(cloudregion string, iid string) error {
        }
 
        k.restMapper = restmapper.NewDeferredDiscoveryRESTMapper(k.discoverClient)
+       k.restConfig = config
+
+       //Spawn ClientConfig
+       kubeFile, err := os.Open(configPath)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Opening kubeConfig")
+       }
+       kubeData, err := ioutil.ReadAll(kubeFile)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Reading kubeConfig")
+       }
+       k.rawConfig, err = clientcmd.NewClientConfigFromBytes(kubeData)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Creating rawConfig")
+       }
 
        return nil
 }
@@ -423,3 +443,18 @@ func (k *KubernetesClient) GetStandardClient() kubernetes.Interface {
 func (k *KubernetesClient) GetInstanceID() string {
        return k.instanceID
 }
+
+//Following set of methods are implemented so that KubernetesClient
+//implements genericclioptions.RESTClientGetter interface
+func (k *KubernetesClient) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
+       return k.discoverClient, nil
+}
+func (k *KubernetesClient) ToRESTMapper() (meta.RESTMapper, error) {
+       return k.GetMapper(), nil
+}
+func (k *KubernetesClient) ToRawKubeConfigLoader() clientcmd.ClientConfig {
+       return k.rawConfig
+}
+func (k *KubernetesClient) ToRESTConfig() (*rest.Config, error) {
+       return k.restConfig, nil
+}
index 7001d9e..6db541a 100644 (file)
@@ -72,7 +72,7 @@ func TestInit(t *testing.T) {
 
                kubeClient := KubernetesClient{}
                // Refer to the connection via its name
-               err = kubeClient.init("mock_connection", "abcdefg")
+               err = kubeClient.Init("mock_connection", "abcdefg")
                if err != nil {
                        t.Fatalf("TestGetKubeClient returned an error (%s)", err)
                }
index 5771c83..e2f802c 100644 (file)
@@ -391,7 +391,7 @@ func scheduleResources(c chan configResourceList) {
                        log.Printf("[scheduleResources]: POST %v %v", data.profile, data.resourceTemplates)
                        for _, inst := range resp {
                                k8sClient := KubernetesClient{}
-                               err = k8sClient.init(inst.Request.CloudRegion, inst.ID)
+                               err = k8sClient.Init(inst.Request.CloudRegion, inst.ID)
                                if err != nil {
                                        log.Printf("Getting CloudRegion Information: %s", err.Error())
                                        //Move onto the next cloud region
@@ -418,7 +418,7 @@ func scheduleResources(c chan configResourceList) {
                        log.Printf("[scheduleResources]: DELETE %v %v", data.profile, data.resourceTemplates)
                        for _, inst := range resp {
                                k8sClient := KubernetesClient{}
-                               err = k8sClient.init(inst.Request.CloudRegion, inst.ID)
+                               err = k8sClient.Init(inst.Request.CloudRegion, inst.ID)
                                if err != nil {
                                        log.Printf("Getting CloudRegion Information: %s", err.Error())
                                        //Move onto the next cloud region
@@ -488,7 +488,7 @@ var resolve = func(rbName, rbVersion, profileName string, p Config) (configResou
                profile.ReleaseName)
 
        chartPath := filepath.Join(chartBasePath, t.ChartName)
-       resTemplates, err = helmClient.GenerateKubernetesArtifacts(chartPath,
+       resTemplates, _, err = helmClient.GenerateKubernetesArtifacts(chartPath,
                []string{outputfile.Name()},
                nil)
        if err != nil {
index b11283e..337ce68 100644 (file)
@@ -22,6 +22,8 @@ import (
        "log"
        "strings"
 
+       protorelease "k8s.io/helm/pkg/proto/hapi/release"
+
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/db"
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/helm"
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/namegenerator"
@@ -49,6 +51,7 @@ type InstanceResponse struct {
        Namespace   string                    `json:"namespace"`
        ReleaseName string                    `json:"release-name"`
        Resources   []helm.KubernetesResource `json:"resources"`
+       Hooks       []*protorelease.Hook      `json:"hooks"`
 }
 
 // InstanceMiniResponse contains the response from instantiation
@@ -147,7 +150,7 @@ func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) {
        }
 
        //Execute the kubernetes create command
-       sortedTemplates, releaseName, err := rb.NewProfileClient().Resolve(i.RBName, i.RBVersion, i.ProfileName, overrideValues, i.ReleaseName)
+       sortedTemplates, hookList, releaseName, err := rb.NewProfileClient().Resolve(i.RBName, i.RBVersion, i.ProfileName, overrideValues, i.ReleaseName)
        if err != nil {
                return InstanceResponse{}, pkgerrors.Wrap(err, "Error resolving helm charts")
        }
@@ -156,7 +159,7 @@ func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) {
        id := namegenerator.Generate()
 
        k8sClient := KubernetesClient{}
-       err = k8sClient.init(i.CloudRegion, id)
+       err = k8sClient.Init(i.CloudRegion, id)
        if err != nil {
                return InstanceResponse{}, pkgerrors.Wrap(err, "Getting CloudRegion Information")
        }
@@ -173,6 +176,7 @@ func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) {
                Namespace:   profile.Namespace,
                ReleaseName: releaseName,
                Resources:   createdResources,
+               Hooks:       hookList,
        }
 
        key := InstanceKey{
@@ -230,7 +234,7 @@ func (v *InstanceClient) Query(id, apiVersion, kind, name, labels string) (Insta
        }
 
        k8sClient := KubernetesClient{}
-       err = k8sClient.init(resResp.Request.CloudRegion, id)
+       err = k8sClient.Init(resResp.Request.CloudRegion, id)
        if err != nil {
                return InstanceStatus{}, pkgerrors.Wrap(err, "Getting CloudRegion Information")
        }
@@ -296,7 +300,7 @@ func (v *InstanceClient) Status(id string) (InstanceStatus, error) {
        }
 
        k8sClient := KubernetesClient{}
-       err = k8sClient.init(resResp.Request.CloudRegion, id)
+       err = k8sClient.Init(resResp.Request.CloudRegion, id)
        if err != nil {
                return InstanceStatus{}, pkgerrors.Wrap(err, "Getting CloudRegion Information")
        }
@@ -432,7 +436,7 @@ func (v *InstanceClient) Delete(id string) error {
        }
 
        k8sClient := KubernetesClient{}
-       err = k8sClient.init(inst.Request.CloudRegion, inst.ID)
+       err = k8sClient.Init(inst.Request.CloudRegion, inst.ID)
        if err != nil {
                return pkgerrors.Wrap(err, "Getting CloudRegion Information")
        }
index b79cf38..2711a52 100644 (file)
@@ -179,7 +179,7 @@ func TestInstanceCreate(t *testing.T) {
                log.Println(ir)
 
                if len(ir.Resources) == 0 {
-                       t.Fatalf("TestInstanceCreate returned empty data (%s)", ir)
+                       t.Fatalf("TestInstanceCreate returned empty data (%+v)", ir)
                }
        })
 
diff --git a/src/k8splugin/internal/healthcheck/healthcheck.go b/src/k8splugin/internal/healthcheck/healthcheck.go
new file mode 100644 (file)
index 0000000..341b1db
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+Copyright © 2021 Samsung Electronics
+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.
+*/
+
+package healthcheck
+
+import (
+       "encoding/json"
+
+       protorelease "k8s.io/helm/pkg/proto/hapi/release"
+       "k8s.io/helm/pkg/releasetesting"
+
+       "github.com/onap/multicloud-k8s/src/k8splugin/internal/app"
+       "github.com/onap/multicloud-k8s/src/k8splugin/internal/db"
+       log "github.com/onap/multicloud-k8s/src/k8splugin/internal/logutils"
+       "github.com/onap/multicloud-k8s/src/k8splugin/internal/namegenerator"
+
+       pkgerrors "github.com/pkg/errors"
+)
+
+// HealthcheckState holds possible states of Healthcheck instance
+type HealthcheckState string
+
+const (
+       HcS_UNKNOWN HealthcheckState = "UNKNOWN"
+       HcS_STARTED HealthcheckState = "STARTED"
+       HcS_RUNNING HealthcheckState = "RUNNING"
+       HcS_SUCCESS HealthcheckState = "SUCCESS"
+       HcS_FAILURE HealthcheckState = "FAILURE"
+)
+
+// InstanceHCManager interface exposes instance Healthcheck fuctionalities
+type InstanceHCManager interface {
+       Create(instanceId string) (InstanceHCStatus, error)
+       Get(instanceId, healthcheckId string) (InstanceHCStatus, error)
+       List(instanceId string) ([]InstanceHCStatus, error)
+       Delete(instanceId, healthcheckId string) error
+}
+
+// HealthcheckKey is used as the primary key in the db
+type HealthcheckKey struct {
+       InstanceId    string `json:"instance-id"`
+       HealthcheckId string `json:"healthcheck-id"`
+}
+
+// We will use json marshalling to convert to string to
+// preserve the underlying structure.
+func (dk HealthcheckKey) String() string {
+       out, err := json.Marshal(dk)
+       if err != nil {
+               return ""
+       }
+
+       return string(out)
+}
+
+// InstanceHCClient implements InstanceHCManager
+type InstanceHCClient struct {
+       storeName string
+       tagInst   string
+}
+
+// InstanceHCStatus holds healthcheck status
+type InstanceHCStatus struct {
+       releasetesting.TestSuite
+       Id     string
+       Status HealthcheckState
+}
+
+func NewHCClient() *InstanceHCClient {
+       return &InstanceHCClient{
+               storeName: "rbdef",
+               tagInst:   "instanceHC",
+       }
+}
+
+func (ihc InstanceHCClient) Create(instanceId string) (InstanceHCStatus, error) {
+       //Generate ID
+       id := namegenerator.Generate()
+
+       //Determine Cloud Region and namespace
+       v := app.NewInstanceClient()
+       instance, err := v.Get(instanceId)
+       if err != nil {
+               return InstanceHCStatus{}, pkgerrors.Wrap(err, "Getting instance")
+       }
+
+       //Prepare Environment, Request and Release structs
+       //TODO In future could derive params from request
+       client, err := NewKubeClient(instanceId, instance.Request.CloudRegion)
+       if err != nil {
+               return InstanceHCStatus{}, pkgerrors.Wrap(err, "Preparing KubeClient")
+       }
+       env := &releasetesting.Environment{
+               Namespace:  instance.Namespace,
+               KubeClient: client,
+               Parallel:   false,
+       }
+       release := protorelease.Release{
+               Name:  instance.ReleaseName,
+               Hooks: instance.Hooks,
+       }
+
+       //Run HC
+       testSuite, err := releasetesting.NewTestSuite(&release)
+       if err != nil {
+               log.Error("Error creating TestSuite", log.Fields{
+                       "Release": release,
+               })
+               return InstanceHCStatus{}, pkgerrors.Wrap(err, "Creating TestSuite")
+       }
+       if err = testSuite.Run(env); err != nil {
+               log.Error("Error running TestSuite", log.Fields{
+                       "TestSuite":   testSuite,
+                       "Environment": env,
+               })
+               return InstanceHCStatus{}, pkgerrors.Wrap(err, "Running TestSuite")
+       }
+
+       //Save state
+       ihcs := InstanceHCStatus{
+               TestSuite: *testSuite,
+               Id:        id,
+               Status:    HcS_STARTED,
+       }
+       key := HealthcheckKey{
+               InstanceId:    instance.ID,
+               HealthcheckId: id,
+       }
+       err = db.DBconn.Create(ihc.storeName, key, ihc.tagInst, ihcs)
+       if err != nil {
+               return ihcs, pkgerrors.Wrap(err, "Creating Instance DB Entry")
+       }
+
+       return ihcs, nil
+}
+
+func (ihc InstanceHCClient) Get(instanceId, healthcheckId string) (InstanceHCStatus, error) {
+       return InstanceHCStatus{}, nil
+}
+
+func (ihc InstanceHCClient) Delete(instanceId, healthcheckId string) error {
+       return nil
+}
+
+func (ihc InstanceHCClient) List(instanceId string) ([]InstanceHCStatus, error) {
+       return make([]InstanceHCStatus, 0), nil
+}
diff --git a/src/k8splugin/internal/healthcheck/kubeclient.go b/src/k8splugin/internal/healthcheck/kubeclient.go
new file mode 100644 (file)
index 0000000..be4c6fc
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+Copyright © 2021 Samsung Electronics
+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.
+*/
+
+package healthcheck
+
+import (
+       "k8s.io/helm/pkg/kube"
+       "k8s.io/helm/pkg/tiller/environment"
+
+       "github.com/onap/multicloud-k8s/src/k8splugin/internal/app"
+       "github.com/onap/multicloud-k8s/src/k8splugin/internal/config"
+
+       pkgerrors "github.com/pkg/errors"
+)
+
+//implements environment.KubeClient but overrides it so that
+//custom labels can be injected into created resources
+// using internal k8sClient
+type KubeClientImpl struct {
+       environment.KubeClient
+       labels map[string]string
+       k      app.KubernetesClient
+}
+
+func NewKubeClient(instanceId, cloudRegion string) (*KubeClientImpl, error) {
+       k8sClient := app.KubernetesClient{}
+       err := k8sClient.Init(cloudRegion, instanceId)
+       if err != nil {
+               return nil, pkgerrors.Wrap(err, "Initializing k8sClient")
+       }
+       return &KubeClientImpl{
+               labels: map[string]string{
+                       config.GetConfiguration().KubernetesLabelName: instanceId,
+               },
+               KubeClient: kube.New(&k8sClient),
+               k:          k8sClient,
+       }, nil
+}
+
+/* FIXME
+// Need to correct this later and provide override of Create method to use our k8sClient
+// So that healthcheck hook resources would be labeled with vf-module data just like currently
+// every k8splugin-managed resource is
+
+//Create function is overrided to label test resources with custom labels
+func (kci *KubeClientImpl) Create(namespace string, reader io.Reader, timeout int64, shouldWait bool) error {
+       return nil
+}
+*/
index d3715fc..d39e840 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright 2018 Intel Corporation, Inc
+ * Copyright © 2021 Samsung Electronics
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,6 +25,7 @@ import (
        "path/filepath"
        "regexp"
        "sort"
+       "strconv"
        "strings"
 
        utils "github.com/onap/multicloud-k8s/src/k8splugin/internal"
@@ -35,8 +37,10 @@ import (
        "k8s.io/apimachinery/pkg/util/validation"
        k8syaml "k8s.io/apimachinery/pkg/util/yaml"
        "k8s.io/helm/pkg/chartutil"
+       "k8s.io/helm/pkg/hooks"
        "k8s.io/helm/pkg/manifest"
        "k8s.io/helm/pkg/proto/hapi/chart"
+       protorelease "k8s.io/helm/pkg/proto/hapi/release"
        "k8s.io/helm/pkg/releaseutil"
        "k8s.io/helm/pkg/renderutil"
        "k8s.io/helm/pkg/tiller"
@@ -46,6 +50,7 @@ import (
 // Template is the interface for all helm templating commands
 // Any backend implementation will implement this interface and will
 // access the functionality via this.
+// FIXME Template is not referenced anywhere
 type Template interface {
        GenerateKubernetesArtifacts(
                chartPath string,
@@ -74,6 +79,12 @@ func NewTemplateClient(k8sversion, namespace, releasename string) *TemplateClien
        }
 }
 
+// Define hooks that are honored by k8splugin
+var honoredEvents = map[string]protorelease.Hook_Event{
+       hooks.ReleaseTestSuccess: protorelease.Hook_RELEASE_TEST_SUCCESS,
+       hooks.ReleaseTestFailure: protorelease.Hook_RELEASE_TEST_FAILURE,
+}
+
 // Combines valueFiles and values into a single values stream.
 // values takes precedence over valueFiles
 func (h *TemplateClient) processValues(valueFiles []string, values []string) ([]byte, error) {
@@ -138,12 +149,60 @@ func (h *TemplateClient) mergeValues(dest map[string]interface{}, src map[string
        return dest
 }
 
+// Checks whether resource is a hook and if it is, returns hook struct
+//Logic is based on private method
+//file *manifestFile) sort(result *result) error
+//of helm/pkg/tiller package
+func isHook(path, resource string) (*protorelease.Hook, error) {
+
+       var entry releaseutil.SimpleHead
+       err := yaml.Unmarshal([]byte(resource), &entry)
+       if err != nil {
+               return nil, pkgerrors.Wrap(err, "Loading resource to YAML")
+       }
+       //If resource has no metadata it can't be a hook
+       if entry.Metadata == nil ||
+               entry.Metadata.Annotations == nil ||
+               len(entry.Metadata.Annotations) == 0 {
+               return nil, nil
+       }
+       //Determine hook weight
+       hookWeight, err := strconv.Atoi(entry.Metadata.Annotations[hooks.HookWeightAnno])
+       if err != nil {
+               hookWeight = 0
+       }
+       //Prepare hook obj
+       resultHook := &protorelease.Hook{
+               Name:           entry.Metadata.Name,
+               Kind:           entry.Kind,
+               Path:           path,
+               Manifest:       resource,
+               Events:         []protorelease.Hook_Event{},
+               Weight:         int32(hookWeight),
+               DeletePolicies: []protorelease.Hook_DeletePolicy{},
+       }
+       //Determine hook's events
+       hookTypes, ok := entry.Metadata.Annotations[hooks.HookAnno]
+       if !ok {
+               return resultHook, nil
+       }
+       for _, hookType := range strings.Split(hookTypes, ",") {
+               hookType = strings.ToLower(strings.TrimSpace(hookType))
+               e, ok := honoredEvents[hookType]
+               if ok {
+                       resultHook.Events = append(resultHook.Events, e)
+               }
+       }
+       return resultHook, nil
+}
+
 // GenerateKubernetesArtifacts a mapping of type to fully evaluated helm template
 func (h *TemplateClient) GenerateKubernetesArtifacts(inputPath string, valueFiles []string,
-       values []string) ([]KubernetesResourceTemplate, error) {
+       values []string) ([]KubernetesResourceTemplate, []*protorelease.Hook, error) {
 
        var outputDir, chartPath, namespace, releaseName string
        var retData []KubernetesResourceTemplate
+       var hookList []*protorelease.Hook
 
        releaseName = h.releaseName
        namespace = h.kubeNameSpace
@@ -151,16 +210,16 @@ func (h *TemplateClient) GenerateKubernetesArtifacts(inputPath string, valueFile
        // verify chart path exists
        if _, err := os.Stat(inputPath); err == nil {
                if chartPath, err = filepath.Abs(inputPath); err != nil {
-                       return retData, err
+                       return retData, hookList, err
                }
        } else {
-               return retData, err
+               return retData, hookList, err
        }
 
        //Create a temp directory in the system temp folder
        outputDir, err := ioutil.TempDir("", "helm-tmpl-")
        if err != nil {
-               return retData, pkgerrors.Wrap(err, "Got error creating temp dir")
+               return retData, hookList, pkgerrors.Wrap(err, "Got error creating temp dir")
        }
 
        if namespace == "" {
@@ -170,18 +229,18 @@ func (h *TemplateClient) GenerateKubernetesArtifacts(inputPath string, valueFile
        // get combined values and create config
        rawVals, err := h.processValues(valueFiles, values)
        if err != nil {
-               return retData, err
+               return retData, hookList, err
        }
        config := &chart.Config{Raw: string(rawVals), Values: map[string]*chart.Value{}}
 
        if msgs := validation.IsDNS1123Label(releaseName); releaseName != "" && len(msgs) > 0 {
-               return retData, fmt.Errorf("release name %s is not a valid DNS label: %s", releaseName, strings.Join(msgs, ";"))
+               return retData, hookList, fmt.Errorf("release name %s is not a valid DNS label: %s", releaseName, strings.Join(msgs, ";"))
        }
 
        // Check chart requirements to make sure all dependencies are present in /charts
        c, err := chartutil.Load(chartPath)
        if err != nil {
-               return retData, pkgerrors.Errorf("Got error: %s", err.Error())
+               return retData, hookList, pkgerrors.Errorf("Got error: %s", err.Error())
        }
 
        renderOpts := renderutil.Options{
@@ -197,7 +256,7 @@ func (h *TemplateClient) GenerateKubernetesArtifacts(inputPath string, valueFile
 
        renderedTemplates, err := renderutil.Render(c, config, renderOpts)
        if err != nil {
-               return retData, err
+               return retData, hookList, err
        }
 
        newRenderedTemplates := make(map[string]string)
@@ -246,16 +305,24 @@ func (h *TemplateClient) GenerateKubernetesArtifacts(inputPath string, valueFile
                        continue
                }
 
+               hook, _ := isHook(b, data)
+               // if hook is not nil, then append it to hooks list and continue
+               // if it's not, disregard error
+               if hook != nil {
+                       hookList = append(hookList, hook)
+                       continue
+               }
+
                mfilePath := filepath.Join(outputDir, m.Name)
                utils.EnsureDirectory(mfilePath)
                err = ioutil.WriteFile(mfilePath, []byte(data), 0666)
                if err != nil {
-                       return retData, err
+                       return retData, hookList, err
                }
 
                gvk, err := getGroupVersionKind(data)
                if err != nil {
-                       return retData, err
+                       return retData, hookList, err
                }
 
                kres := KubernetesResourceTemplate{
@@ -264,7 +331,7 @@ func (h *TemplateClient) GenerateKubernetesArtifacts(inputPath string, valueFile
                }
                retData = append(retData, kres)
        }
-       return retData, nil
+       return retData, hookList, nil
 }
 
 func getGroupVersionKind(data string) (schema.GroupVersionKind, error) {
index d25ca09..358577e 100644 (file)
@@ -200,7 +200,7 @@ func TestGenerateKubernetesArtifacts(t *testing.T) {
        for _, testCase := range testCases {
                t.Run(testCase.label, func(t *testing.T) {
                        tc := NewTemplateClient("1.12.3", "testnamespace", "testreleasename")
-                       out, err := tc.GenerateKubernetesArtifacts(testCase.chartPath, testCase.valueFiles,
+                       out, _, err := tc.GenerateKubernetesArtifacts(testCase.chartPath, testCase.valueFiles,
                                testCase.values)
                        if err != nil {
                                if testCase.expectedError == "" {
index f8b07ab..df4d2e5 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright 2018 Intel Corporation, Inc
+ * Copyright © 2021 Samsung Electronics
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,6 +24,8 @@ import (
        "log"
        "path/filepath"
 
+       protorelease "k8s.io/helm/pkg/proto/hapi/release"
+
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/db"
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/helm"
 
@@ -270,49 +273,50 @@ func (v *ProfileClient) Download(rbName, rbVersion, prName string) ([]byte, erro
 //Resolve returns the path where the helm chart merged with
 //configuration overrides resides and final ReleaseName picked for instantiation
 func (v *ProfileClient) Resolve(rbName string, rbVersion string,
-       profileName string, values []string, overrideReleaseName string) ([]helm.KubernetesResourceTemplate, string, error) {
+       profileName string, values []string, overrideReleaseName string) ([]helm.KubernetesResourceTemplate, []*protorelease.Hook, string, error) {
 
        var sortedTemplates []helm.KubernetesResourceTemplate
+       var hookList []*protorelease.Hook
        var finalReleaseName string
 
        //Download and process the profile first
        //If everything seems okay, then download the definition
        prData, err := v.Download(rbName, rbVersion, profileName)
        if err != nil {
-               return sortedTemplates, finalReleaseName, pkgerrors.Wrap(err, "Downloading Profile")
+               return sortedTemplates, hookList, finalReleaseName, pkgerrors.Wrap(err, "Downloading Profile")
        }
 
        prPath, err := ExtractTarBall(bytes.NewBuffer(prData))
        if err != nil {
-               return sortedTemplates, finalReleaseName, pkgerrors.Wrap(err, "Extracting Profile Content")
+               return sortedTemplates, hookList, finalReleaseName, pkgerrors.Wrap(err, "Extracting Profile Content")
        }
 
        prYamlClient, err := ProcessProfileYaml(prPath, v.manifestName)
        if err != nil {
-               return sortedTemplates, finalReleaseName, pkgerrors.Wrap(err, "Processing Profile Manifest")
+               return sortedTemplates, hookList, finalReleaseName, pkgerrors.Wrap(err, "Processing Profile Manifest")
        }
 
        definitionClient := NewDefinitionClient()
 
        definition, err := definitionClient.Get(rbName, rbVersion)
        if err != nil {
-               return sortedTemplates, finalReleaseName, pkgerrors.Wrap(err, "Getting Definition Metadata")
+               return sortedTemplates, hookList, finalReleaseName, pkgerrors.Wrap(err, "Getting Definition Metadata")
        }
 
        defData, err := definitionClient.Download(rbName, rbVersion)
        if err != nil {
-               return sortedTemplates, finalReleaseName, pkgerrors.Wrap(err, "Downloading Definition")
+               return sortedTemplates, hookList, finalReleaseName, pkgerrors.Wrap(err, "Downloading Definition")
        }
 
        chartBasePath, err := ExtractTarBall(bytes.NewBuffer(defData))
        if err != nil {
-               return sortedTemplates, finalReleaseName, pkgerrors.Wrap(err, "Extracting Definition Charts")
+               return sortedTemplates, hookList, finalReleaseName, pkgerrors.Wrap(err, "Extracting Definition Charts")
        }
 
        //Get the definition ID and download its contents
        profile, err := v.Get(rbName, rbVersion, profileName)
        if err != nil {
-               return sortedTemplates, finalReleaseName, pkgerrors.Wrap(err, "Getting Profile")
+               return sortedTemplates, hookList, finalReleaseName, pkgerrors.Wrap(err, "Getting Profile")
        }
 
        //Copy the profile configresources to the chart locations
@@ -322,7 +326,7 @@ func (v *ProfileClient) Resolve(rbName string, rbVersion string,
        //   chartpath: chart/config/resources/config.yaml
        err = prYamlClient.CopyConfigurationOverrides(chartBasePath)
        if err != nil {
-               return sortedTemplates, finalReleaseName, pkgerrors.Wrap(err, "Copying configresources to chart")
+               return sortedTemplates, hookList, finalReleaseName, pkgerrors.Wrap(err, "Copying configresources to chart")
        }
 
        if overrideReleaseName == "" {
@@ -336,14 +340,14 @@ func (v *ProfileClient) Resolve(rbName string, rbVersion string,
                finalReleaseName)
 
        chartPath := filepath.Join(chartBasePath, definition.ChartName)
-       sortedTemplates, err = helmClient.GenerateKubernetesArtifacts(chartPath,
+       sortedTemplates, hookList, err = helmClient.GenerateKubernetesArtifacts(chartPath,
                []string{prYamlClient.GetValues()},
                values)
        if err != nil {
-               return sortedTemplates, finalReleaseName, pkgerrors.Wrap(err, "Generate final k8s yaml")
+               return sortedTemplates, hookList, finalReleaseName, pkgerrors.Wrap(err, "Generate final k8s yaml")
        }
 
-       return sortedTemplates, finalReleaseName, nil
+       return sortedTemplates, hookList, finalReleaseName, nil
 }
 
 // Returns an empty profile with the following contents
index a434e5a..3c40c2c 100644 (file)
@@ -773,7 +773,7 @@ func TestResolveProfile(t *testing.T) {
                t.Run(testCase.label, func(t *testing.T) {
                        db.DBconn = testCase.mockdb
                        impl := NewProfileClient()
-                       data, releaseName, err := impl.Resolve(testCase.rbname,
+                       data, _, releaseName, err := impl.Resolve(testCase.rbname,
                                testCase.rbversion, testCase.prname, []string{}, testCase.releaseName)
                        defer cleanup(data)
                        if err != nil {