Fix Status API to actually provide instance status 71/113171/4
authorKonrad Bańka <k.banka@samsung.com>
Fri, 25 Sep 2020 14:35:02 +0000 (16:35 +0200)
committerKonrad Bańka <k.banka@samsung.com>
Wed, 30 Sep 2020 10:56:34 +0000 (12:56 +0200)
Provide information about instance resources and Pods inside status
response.

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

kud/tests/plugin_fw.sh
src/k8splugin/api/instancehandler_test.go
src/k8splugin/go.mod
src/k8splugin/internal/app/client.go
src/k8splugin/internal/app/instance.go
src/k8splugin/internal/app/instance_test.go
src/k8splugin/internal/plugin/helpers.go

index 28df372..a503d66 100755 (executable)
@@ -2,6 +2,7 @@
 # SPDX-license-identifier: Apache-2.0
 ##############################################################################
 # Copyright (c) 2018
+# Copyright © 2020 Samsung Electronics
 # All rights reserved. This program and the accompanying materials
 # are made available under the terms of the Apache License, Version 2.0
 # which accompanies this distribution, and is available at
@@ -116,6 +117,9 @@ print_msg "Not waiting for vFW to fully install as no further checks are impleme
 #sleep 8m
 print_msg "[END] Basic checks for instantiated resource"
 
+print_msg "Retrieving VNF status (this will result with long output)"
+call_api "${base_url}/instance/${vnf_id}/status"
+
 print_msg "Retrieving VNF details"
 response="$(call_api "${base_url}/instance/${vnf_id}")"
 echo "$response"
@@ -136,3 +140,5 @@ delete_resource "${base_url}/rb/definition/${rb_name}/${rb_version}"
 
 print_msg "Deleting ${cloud_region_id} cloud region connection"
 delete_resource "${base_url}/connectivity-info/${cloud_region_id}"
+
+print_msg "Test finished successfully"
index 7b6594c..c0690fb 100644 (file)
@@ -316,91 +316,6 @@ func TestInstanceGetHandler(t *testing.T) {
        }
 }
 
-func TestStatusHandler(t *testing.T) {
-       testCases := []struct {
-               label            string
-               input            string
-               expectedCode     int
-               expectedResponse *app.InstanceStatus
-               instClient       *mockInstanceClient
-       }{
-               {
-                       label:        "Fail to Get Status",
-                       input:        "HaKpys8e",
-                       expectedCode: http.StatusInternalServerError,
-                       instClient: &mockInstanceClient{
-                               err: pkgerrors.New("Internal error"),
-                       },
-               },
-               {
-                       label:        "Succesful GET Status",
-                       input:        "HaKpys8e",
-                       expectedCode: http.StatusOK,
-                       expectedResponse: &app.InstanceStatus{
-                               Request: app.InstanceRequest{
-                                       RBName:      "test-rbdef",
-                                       RBVersion:   "v1",
-                                       ProfileName: "profile1",
-                                       CloudRegion: "region1",
-                               },
-                               Ready:         true,
-                               ResourceCount: 2,
-                               PodStatuses: []app.PodStatus{
-                                       {
-                                               Name:        "test-pod1",
-                                               Namespace:   "default",
-                                               Ready:       true,
-                                               IPAddresses: []string{"192.168.1.1", "192.168.2.1"},
-                                       },
-                                       {
-                                               Name:        "test-pod2",
-                                               Namespace:   "default",
-                                               Ready:       true,
-                                               IPAddresses: []string{"192.168.3.1", "192.168.5.1"},
-                                       },
-                               },
-                       },
-                       instClient: &mockInstanceClient{
-                               statusItem: app.InstanceStatus{
-                                       Request: app.InstanceRequest{
-                                               RBName:      "test-rbdef",
-                                               RBVersion:   "v1",
-                                               ProfileName: "profile1",
-                                               CloudRegion: "region1",
-                                       },
-                                       Ready:         true,
-                                       ResourceCount: 2,
-                                       PodStatuses: []app.PodStatus{
-                                               {
-                                                       Name:        "test-pod1",
-                                                       Namespace:   "default",
-                                                       Ready:       true,
-                                                       IPAddresses: []string{"192.168.1.1", "192.168.2.1"},
-                                               },
-                                               {
-                                                       Name:        "test-pod2",
-                                                       Namespace:   "default",
-                                                       Ready:       true,
-                                                       IPAddresses: []string{"192.168.3.1", "192.168.5.1"},
-                                               },
-                                       },
-                               },
-                       },
-               },
-       }
-
-       for _, testCase := range testCases {
-               t.Run(testCase.label, func(t *testing.T) {
-                       request := httptest.NewRequest("GET", "/v1/instance/"+testCase.input+"/status", nil)
-                       resp := executeRequest(request, NewRouter(nil, nil, testCase.instClient, nil, nil, nil))
-
-                       if testCase.expectedCode != resp.StatusCode {
-                               t.Fatalf("Request method returned: %v and it was expected: %v", resp.StatusCode, testCase.expectedCode)
-                       }
-               })
-       }
-}
-
 func TestInstanceListHandler(t *testing.T) {
        testCases := []struct {
                label            string
index 57b0326..75cb7c7 100644 (file)
@@ -1,8 +1,6 @@
 module github.com/onap/multicloud-k8s/src/k8splugin
 
 require (
-       cloud.google.com/go v0.38.0 // indirect
-       github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
        github.com/DATA-DOG/go-sqlmock v1.3.3 // indirect
        github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e // indirect
        github.com/Masterminds/semver v1.4.2 // indirect
@@ -10,35 +8,26 @@ require (
        github.com/aokoli/goutils v1.1.0 // indirect
        github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 // indirect
        github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 // indirect
-       github.com/coreos/bbolt v1.3.3 // indirect
-       github.com/coreos/go-semver v0.3.0 // indirect
        github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
        github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
-       github.com/cyphar/filepath-securejoin v0.2.2 // indirect
-       github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
        github.com/docker/docker v0.7.3-0.20190912223608-ad718029b705 // indirect
        github.com/docker/engine v0.0.0-20190620014054-c513a4c6c298
        github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
        github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1 // indirect
        github.com/evanphx/json-patch v4.5.0+incompatible // indirect
-       github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
-       github.com/fatih/camelcase v1.0.0 // indirect
        github.com/fvbommel/util v0.0.2
        github.com/ghodss/yaml v1.0.0
        github.com/go-openapi/spec v0.19.3 // indirect
        github.com/go-stack/stack v1.8.0 // indirect
        github.com/gobuffalo/packr v1.30.1 // indirect
-       github.com/gobwas/glob v0.2.3 // indirect
        github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff // indirect
        github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
        github.com/googleapis/gnostic v0.2.0 // indirect
-       github.com/gorilla/context v1.1.1 // indirect
        github.com/gorilla/handlers v1.3.0
        github.com/gorilla/mux v1.7.0
        github.com/gorilla/websocket v1.4.1 // indirect
        github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f // indirect
        github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 // indirect
-       github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
        github.com/grpc-ecosystem/grpc-gateway v1.11.1 // indirect
        github.com/hashicorp/consul v1.4.0
        github.com/hashicorp/go-msgpack v0.5.5 // indirect
@@ -46,20 +35,11 @@ require (
        github.com/hashicorp/memberlist v0.1.5 // indirect
        github.com/hashicorp/serf v0.8.1 // indirect
        github.com/huandu/xstrings v1.2.0 // indirect
-       github.com/imdario/mergo v0.3.5 // indirect
        github.com/jmoiron/sqlx v1.2.0 // indirect
-       github.com/jonboulle/clockwork v0.1.0 // indirect
-       github.com/json-iterator/go v1.1.7 // indirect
        github.com/lib/pq v1.2.0 // indirect
-       github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
        github.com/mitchellh/go-testing-interface v1.0.0 // indirect
-       github.com/mitchellh/go-wordwrap v1.0.0 // indirect
-       github.com/modern-go/reflect2 v1.0.1 // indirect
        github.com/onsi/ginkgo v1.10.1 // indirect
        github.com/onsi/gomega v1.7.0 // indirect
-       github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
-       github.com/pborman/uuid v1.2.0 // indirect
-       github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
        github.com/pkg/errors v0.8.1
        github.com/rubenv/sql-migrate v0.0.0-20190902133344-8926f37f0bc1 // indirect
        github.com/sirupsen/logrus v1.4.2
@@ -71,18 +51,11 @@ require (
        github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect
        github.com/xdg/stringprep v1.0.0 // indirect
        github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
-       github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 // indirect
        github.com/ziutek/mymysql v1.5.4 // indirect
-       go.etcd.io/bbolt v1.3.3 // indirect
        go.etcd.io/etcd v3.3.12+incompatible
        go.mongodb.org/mongo-driver v1.0.0
        golang.org/x/net v0.0.0-20191004110552-13f9640d40b9
-       golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 // indirect
-       google.golang.org/appengine v1.5.0 // indirect
        gopkg.in/gorp.v1 v1.7.2 // indirect
-       gopkg.in/inf.v0 v0.9.1 // indirect
-       gopkg.in/square/go-jose.v2 v2.2.2 // indirect
-       gotest.tools v2.2.0+incompatible // indirect
        k8s.io/api v0.18.2
        k8s.io/apiextensions-apiserver v0.18.2
        k8s.io/apimachinery v0.18.2
@@ -90,7 +63,6 @@ require (
        k8s.io/client-go v12.0.0+incompatible
        k8s.io/cloud-provider v0.18.2
        k8s.io/helm v2.16.12+incompatible
-       k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf // indirect
        k8s.io/kubernetes v1.16.9
        k8s.io/utils v0.0.0-20190907131718-3d4f5b7dea0b // indirect
        sigs.k8s.io/kustomize v2.0.3+incompatible
index ed60644..6762d1b 100644 (file)
@@ -1,5 +1,7 @@
 /*
 Copyright 2018 Intel Corporation.
+Copyright © 2020 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
@@ -18,6 +20,7 @@ import (
        "strings"
        "time"
 
+       "github.com/onap/multicloud-k8s/src/k8splugin/internal/config"
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/connection"
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/helm"
        log "github.com/onap/multicloud-k8s/src/k8splugin/internal/logutils"
@@ -25,6 +28,9 @@ import (
 
        pkgerrors "github.com/pkg/errors"
        "k8s.io/apimachinery/pkg/api/meta"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+       "k8s.io/apimachinery/pkg/runtime"
        "k8s.io/apimachinery/pkg/runtime/schema"
        "k8s.io/client-go/discovery/cached/disk"
        "k8s.io/client-go/dynamic"
@@ -43,6 +49,79 @@ type KubernetesClient struct {
        instanceID     string
 }
 
+// ResourceStatus holds Resource Runtime Data
+type ResourceStatus struct {
+       Name   string                    `json:"name"`
+       GVK    schema.GroupVersionKind   `json:"GVK"`
+       Status unstructured.Unstructured `json:"status"`
+}
+
+// getPodsByLabel yields status of all pods under given instance ID
+func (k *KubernetesClient) getPodsByLabel(namespace string) ([]ResourceStatus, error) {
+       client := k.GetStandardClient().CoreV1().Pods(namespace)
+       listOpts := metav1.ListOptions{
+               LabelSelector: config.GetConfiguration().KubernetesLabelName + "=" + k.instanceID,
+       }
+       podList, err := client.List(listOpts)
+       if err != nil {
+               return nil, pkgerrors.Wrap(err, "Retrieving PodList from cluster")
+       }
+       resp := make([]ResourceStatus, 0, len(podList.Items))
+       cumulatedErrorMsg := make([]string, 0)
+       for _, pod := range podList.Items {
+               podContent, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&pod)
+               if err != nil {
+                       cumulatedErrorMsg = append(cumulatedErrorMsg, err.Error())
+                       continue
+               }
+               var unstrPod unstructured.Unstructured
+               unstrPod.SetUnstructuredContent(podContent)
+               podStatus := ResourceStatus{
+                       Name:   unstrPod.GetName(),
+                       GVK:    schema.FromAPIVersionAndKind("v1", "Pod"),
+                       Status: unstrPod,
+               }
+               resp = append(resp, podStatus)
+       }
+       if len(cumulatedErrorMsg) != 0 {
+               return resp, pkgerrors.New("Converting podContent to unstruct error:\n" +
+                       strings.Join(cumulatedErrorMsg, "\n"))
+       }
+       return resp, nil
+}
+
+// getResourcesStatus yields status of given generic resource
+func (k *KubernetesClient) getResourceStatus(res helm.KubernetesResource, namespace string) (ResourceStatus, error) {
+       dynClient := k.GetDynamicClient()
+       mapper := k.GetMapper()
+       mapping, err := mapper.RESTMapping(schema.GroupKind{
+               Group: res.GVK.Group,
+               Kind:  res.GVK.Kind,
+       }, res.GVK.Version)
+       if err != nil {
+               return ResourceStatus{},
+                       pkgerrors.Wrap(err, "Preparing mapper based on GVK")
+       }
+
+       gvr := mapping.Resource
+       opts := metav1.GetOptions{}
+       var unstruct *unstructured.Unstructured
+       switch mapping.Scope.Name() {
+       case meta.RESTScopeNameNamespace:
+               unstruct, err = dynClient.Resource(gvr).Namespace(namespace).Get(res.Name, opts)
+       case meta.RESTScopeNameRoot:
+               unstruct, err = dynClient.Resource(gvr).Get(res.Name, opts)
+       default:
+               return ResourceStatus{}, pkgerrors.New("Got an unknown RESTSCopeName for mapping: " + res.GVK.String())
+       }
+
+       if err != nil {
+               return ResourceStatus{}, pkgerrors.Wrap(err, "Getting object status")
+       }
+
+       return ResourceStatus{unstruct.GetName(), res.GVK, *unstruct}, nil
+}
+
 // getKubeConfig uses the connectivity client to get the kubeconfig based on the name
 // of the cloudregion. This is written out to a file.
 func (k *KubernetesClient) getKubeConfig(cloudregion string) (string, error) {
@@ -182,40 +261,40 @@ func (k *KubernetesClient) createKind(resTempl helm.KubernetesResourceTemplate,
 }
 
 func (k *KubernetesClient) updateKind(resTempl helm.KubernetesResourceTemplate,
-        namespace string) (helm.KubernetesResource, error) {
-
-        if _, err := os.Stat(resTempl.FilePath); os.IsNotExist(err) {
-                return helm.KubernetesResource{}, pkgerrors.New("File " + resTempl.FilePath + "does not exists")
-        }
-
-        log.Info("Processing Kubernetes Resource", log.Fields{
-                "filepath": resTempl.FilePath,
-        })
-
-        pluginImpl, err := plugin.GetPluginByKind(resTempl.GVK.Kind)
-        if err != nil {
-                return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error loading plugin")
-        }
-
-        updatedResourceName, err := pluginImpl.Update(resTempl.FilePath, namespace, k)
-        if err != nil {
-                log.Error("Error Updating Resource", log.Fields{
-                        "error":    err,
-                        "gvk":      resTempl.GVK,
-                        "filepath": resTempl.FilePath,
-                })
-                return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error in plugin "+resTempl.GVK.Kind+" plugin")
-        }
-
-        log.Info("Updated Kubernetes Resource", log.Fields{
-                "resource": updatedResourceName,
-                "gvk":      resTempl.GVK,
-        })
-
-        return helm.KubernetesResource{
-                GVK:  resTempl.GVK,
-                Name: updatedResourceName,
-        }, nil
+       namespace string) (helm.KubernetesResource, error) {
+
+       if _, err := os.Stat(resTempl.FilePath); os.IsNotExist(err) {
+               return helm.KubernetesResource{}, pkgerrors.New("File " + resTempl.FilePath + "does not exists")
+       }
+
+       log.Info("Processing Kubernetes Resource", log.Fields{
+               "filepath": resTempl.FilePath,
+       })
+
+       pluginImpl, err := plugin.GetPluginByKind(resTempl.GVK.Kind)
+       if err != nil {
+               return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error loading plugin")
+       }
+
+       updatedResourceName, err := pluginImpl.Update(resTempl.FilePath, namespace, k)
+       if err != nil {
+               log.Error("Error Updating Resource", log.Fields{
+                       "error":    err,
+                       "gvk":      resTempl.GVK,
+                       "filepath": resTempl.FilePath,
+               })
+               return helm.KubernetesResource{}, pkgerrors.Wrap(err, "Error in plugin "+resTempl.GVK.Kind+" plugin")
+       }
+
+       log.Info("Updated Kubernetes Resource", log.Fields{
+               "resource": updatedResourceName,
+               "gvk":      resTempl.GVK,
+       })
+
+       return helm.KubernetesResource{
+               GVK:  resTempl.GVK,
+               Name: updatedResourceName,
+       }, nil
 }
 
 func (k *KubernetesClient) createResources(sortedTemplates []helm.KubernetesResourceTemplate,
@@ -239,23 +318,23 @@ func (k *KubernetesClient) createResources(sortedTemplates []helm.KubernetesReso
 }
 
 func (k *KubernetesClient) updateResources(sortedTemplates []helm.KubernetesResourceTemplate,
-        namespace string) ([]helm.KubernetesResource, error) {
-
-        err := k.ensureNamespace(namespace)
-        if err != nil {
-                return nil, pkgerrors.Wrap(err, "Creating Namespace")
-        }
-
-        var updatedResources []helm.KubernetesResource
-        for _, resTempl := range sortedTemplates {
-                resUpdated, err := k.updateKind(resTempl, namespace)
-                if err != nil {
-                        return nil, pkgerrors.Wrapf(err, "Error updating kind: %+v", resTempl.GVK)
-                }
-                updatedResources = append(updatedResources, resUpdated)
-        }
-
-        return updatedResources, nil
+       namespace string) ([]helm.KubernetesResource, error) {
+
+       err := k.ensureNamespace(namespace)
+       if err != nil {
+               return nil, pkgerrors.Wrap(err, "Creating Namespace")
+       }
+
+       var updatedResources []helm.KubernetesResource
+       for _, resTempl := range sortedTemplates {
+               resUpdated, err := k.updateKind(resTempl, namespace)
+               if err != nil {
+                       return nil, pkgerrors.Wrapf(err, "Error updating kind: %+v", resTempl.GVK)
+               }
+               updatedResources = append(updatedResources, resUpdated)
+       }
+
+       return updatedResources, nil
 }
 
 func (k *KubernetesClient) deleteKind(resource helm.KubernetesResource, namespace string) error {
index 220c82d..a6e213c 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright 2018 Intel Corporation, Inc
+ * Copyright © 2020 Samsung Electronics
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +20,7 @@ package app
 import (
        "encoding/json"
        "log"
+       "strings"
 
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/db"
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/helm"
@@ -26,7 +28,6 @@ import (
        "github.com/onap/multicloud-k8s/src/k8splugin/internal/rb"
 
        pkgerrors "github.com/pkg/errors"
-       corev1 "k8s.io/api/core/v1"
 )
 
 // InstanceRequest contains the parameters needed for instantiation
@@ -60,22 +61,12 @@ type InstanceMiniResponse struct {
        Namespace   string          `json:"namespace"`
 }
 
-// PodStatus defines the observed state of ResourceBundleState
-type PodStatus struct {
-       Name        string           `json:"name"`
-       Namespace   string           `json:"namespace"`
-       Ready       bool             `json:"ready"`
-       Status      corev1.PodStatus `json:"status,omitempty"`
-       IPAddresses []string         `json:"ipaddresses"`
-}
-
 // InstanceStatus is what is returned when status is queried for an instance
 type InstanceStatus struct {
        Request         InstanceRequest  `json:"request"`
        Ready           bool             `json:"ready"`
        ResourceCount   int32            `json:"resourceCount"`
-       PodStatuses     []PodStatus      `json:"podStatuses"`
-       ServiceStatuses []corev1.Service `json:"serviceStatuses"`
+       ResourcesStatus []ResourceStatus `json:"resourcesStatus"`
 }
 
 // InstanceManager is an interface exposes the instantiation functionality
@@ -107,18 +98,16 @@ func (dk InstanceKey) String() string {
 // InstanceClient implements the InstanceManager interface
 // It will also be used to maintain some localized state
 type InstanceClient struct {
-       storeName     string
-       tagInst       string
-       tagInstStatus string
+       storeName string
+       tagInst   string
 }
 
 // NewInstanceClient returns an instance of the InstanceClient
 // which implements the InstanceManager
 func NewInstanceClient() *InstanceClient {
        return &InstanceClient{
-               storeName:     "rbdef",
-               tagInst:       "instance",
-               tagInstStatus: "instanceStatus",
+               storeName: "rbdef",
+               tagInst:   "instance",
        }
 }
 
@@ -217,22 +206,64 @@ func (v *InstanceClient) Status(id string) (InstanceStatus, error) {
                ID: id,
        }
 
-       value, err := db.DBconn.Read(v.storeName, key, v.tagInstStatus)
+       value, err := db.DBconn.Read(v.storeName, key, v.tagInst)
        if err != nil {
                return InstanceStatus{}, pkgerrors.Wrap(err, "Get Instance")
        }
 
        //value is a byte array
-       if value != nil {
-               resp := InstanceStatus{}
-               err = db.DBconn.Unmarshal(value, &resp)
+       if value == nil {
+               return InstanceStatus{}, pkgerrors.New("Status is not available")
+       }
+
+       resResp := InstanceResponse{}
+       err = db.DBconn.Unmarshal(value, &resResp)
+       if err != nil {
+               return InstanceStatus{}, pkgerrors.Wrap(err, "Unmarshaling Instance Value")
+       }
+
+       k8sClient := KubernetesClient{}
+       err = k8sClient.init(resResp.Request.CloudRegion, id)
+       if err != nil {
+               return InstanceStatus{}, pkgerrors.Wrap(err, "Getting CloudRegion Information")
+       }
+
+       cumulatedErrorMsg := make([]string, 0)
+       podsStatus, err := k8sClient.getPodsByLabel(resResp.Namespace)
+       if err != nil {
+               cumulatedErrorMsg = append(cumulatedErrorMsg, err.Error())
+       }
+
+       generalStatus := make([]ResourceStatus, 0, len(resResp.Resources))
+Main:
+       for _, resource := range resResp.Resources {
+               for _, pod := range podsStatus {
+                       if resource.GVK == pod.GVK && resource.Name == pod.Name {
+                               continue Main //Don't double check pods if someone decided to define pod explicitly in helm chart
+                       }
+               }
+               status, err := k8sClient.getResourceStatus(resource, resResp.Namespace)
                if err != nil {
-                       return InstanceStatus{}, pkgerrors.Wrap(err, "Unmarshaling Instance Value")
+                       cumulatedErrorMsg = append(cumulatedErrorMsg, err.Error())
+               } else {
+                       generalStatus = append(generalStatus, status)
                }
-               return resp, nil
+       }
+       resp := InstanceStatus{
+               Request:         resResp.Request,
+               ResourceCount:   int32(len(generalStatus) + len(podsStatus)),
+               Ready:           false, //FIXME To determine readiness, some parsing of status fields is necessary
+               ResourcesStatus: append(generalStatus, podsStatus...),
        }
 
-       return InstanceStatus{}, pkgerrors.New("Status is not available")
+       if len(cumulatedErrorMsg) != 0 {
+               err = pkgerrors.New("Getting Resources Status:\n" +
+                       strings.Join(cumulatedErrorMsg, "\n"))
+               return resp, err
+       }
+       //TODO Filter response content by requested verbosity (brief, ...)?
+
+       return resp, nil
 }
 
 // List returns the instance for corresponding ID
index 1b84b44..b79cf38 100644 (file)
@@ -318,128 +318,6 @@ func TestInstanceGet(t *testing.T) {
        })
 }
 
-func TestInstanceStatus(t *testing.T) {
-       oldkrdPluginData := utils.LoadedPlugins
-
-       defer func() {
-               utils.LoadedPlugins = oldkrdPluginData
-       }()
-
-       err := LoadMockPlugins(utils.LoadedPlugins)
-       if err != nil {
-               t.Fatalf("LoadMockPlugins returned an error (%s)", err)
-       }
-
-       t.Run("Successfully Get Instance Status", func(t *testing.T) {
-               db.DBconn = &db.MockDB{
-                       Items: map[string]map[string][]byte{
-                               InstanceKey{ID: "HaKpys8e"}.String(): {
-                                       "instanceStatus": []byte(
-                                               `{
-                                                       "request": {
-                                                               "profile-name":"profile1",
-                                                               "rb-name":"test-rbdef",
-                                                               "rb-version":"v1",
-                                                               "cloud-region":"region1"
-                                                       },
-                                                       "ready": true,
-                                                       "resourceCount": 2,
-                                                       "podStatuses": [
-                                                               {
-                                                                       "name":        "test-pod1",
-                                                                       "namespace":   "default",
-                                                                       "ready":       true,
-                                                                       "ipaddresses": ["192.168.1.1", "192.168.2.1"]
-                                                               },
-                                                               {
-                                                                       "name":        "test-pod2",
-                                                                       "namespace":   "default",
-                                                                       "ready":       true,
-                                                                       "ipaddresses": ["192.168.4.1", "192.168.5.1"]
-                                                               }
-                                                       ]
-                                               }`),
-                               },
-                       },
-               }
-
-               expected := InstanceStatus{
-                       Request: InstanceRequest{
-                               RBName:      "test-rbdef",
-                               RBVersion:   "v1",
-                               ProfileName: "profile1",
-                               CloudRegion: "region1",
-                       },
-                       Ready:         true,
-                       ResourceCount: 2,
-                       PodStatuses: []PodStatus{
-                               {
-                                       Name:        "test-pod1",
-                                       Namespace:   "default",
-                                       Ready:       true,
-                                       IPAddresses: []string{"192.168.1.1", "192.168.2.1"},
-                               },
-                               {
-                                       Name:        "test-pod2",
-                                       Namespace:   "default",
-                                       Ready:       true,
-                                       IPAddresses: []string{"192.168.4.1", "192.168.5.1"},
-                               },
-                       },
-               }
-               ic := NewInstanceClient()
-               id := "HaKpys8e"
-               data, err := ic.Status(id)
-               if err != nil {
-                       t.Fatalf("TestInstanceStatus returned an error (%s)", err)
-               }
-               if !reflect.DeepEqual(expected, data) {
-                       t.Fatalf("TestInstanceStatus returned:\n result=%v\n expected=%v",
-                               data, expected)
-               }
-       })
-
-       t.Run("Get non-existing Instance", func(t *testing.T) {
-               db.DBconn = &db.MockDB{
-                       Items: map[string]map[string][]byte{
-                               InstanceKey{ID: "HaKpys8e"}.String(): {
-                                       "instanceStatus": []byte(
-                                               `{
-                                                       "request": {
-                                                               "profile-name":"profile1",
-                                                               "rb-name":"test-rbdef",
-                                                               "rb-version":"v1",
-                                                               "cloud-region":"region1"
-                                                       },
-                                                       "ready": true,
-                                                       "resourceCount": 2,
-                                                       "podStatuses": [
-                                                               {
-                                                                       "name":        "test-pod1",
-                                                                       "namespace":   "default",
-                                                                       "ready":       true,
-                                                                       "ipaddresses": ["192.168.1.1", "192.168.2.1"]
-                                                               },
-                                                               {
-                                                                       "name":        "test-pod2",
-                                                                       "namespace":   "default",
-                                                                       "ready":       true,
-                                                                       "ipaddresses": ["192.168.4.1", "192.168.5.1"]
-                                                               }
-                                                       ]
-                                               }`),
-                               },
-                       },
-               }
-
-               ic := NewInstanceClient()
-               _, err := ic.Get("non-existing")
-               if err == nil {
-                       t.Fatal("Expected error, got pass", err)
-               }
-       })
-}
-
 func TestInstanceFind(t *testing.T) {
        oldkrdPluginData := utils.LoadedPlugins
 
index 19ff03a..7078b81 100644 (file)
@@ -69,8 +69,8 @@ type Reference interface {
        //Delete a kubernetes resource described in the provided namespace
        Delete(resource helm.KubernetesResource, namespace string, client KubernetesConnector) error
 
-        //Update kubernetes resource based on the groupVersionKind and resourceName provided in resource
-        Update(yamlFilePath string, namespace string, client KubernetesConnector) (string, error)
+       //Update kubernetes resource based on the groupVersionKind and resourceName provided in resource
+       Update(yamlFilePath string, namespace string, client KubernetesConnector) (string, error)
 }
 
 // GetPluginByKind returns a plugin by the kind name