diff options
-rw-r--r-- | deployments/kubernetes/monitor-deploy.yaml | 11 | ||||
-rw-r--r-- | deployments/kubernetes/onap4k8s.yaml | 72 | ||||
-rwxr-xr-x | kud/tests/plugin_fw.sh | 6 | ||||
-rw-r--r-- | src/dcm/api/logicalCloudHandler.go | 18 | ||||
-rw-r--r-- | src/dcm/config.json | 12 | ||||
-rw-r--r-- | src/dcm/go.sum | 1 | ||||
-rw-r--r-- | src/dcm/pkg/module/apply.go | 8 | ||||
-rw-r--r-- | src/dcm/pkg/module/logicalcloud.go | 6 | ||||
-rw-r--r-- | src/k8splugin/api/instancehandler_test.go | 85 | ||||
-rw-r--r-- | src/k8splugin/go.mod | 28 | ||||
-rw-r--r-- | src/k8splugin/internal/app/client.go | 181 | ||||
-rw-r--r-- | src/k8splugin/internal/app/instance.go | 81 | ||||
-rw-r--r-- | src/k8splugin/internal/app/instance_test.go | 122 | ||||
-rw-r--r-- | src/k8splugin/internal/plugin/helpers.go | 4 | ||||
-rw-r--r-- | src/monitor/deploy/crds/k8splugin_v1alpha1_resourcebundlestate_crd.yaml | 5 |
15 files changed, 305 insertions, 335 deletions
diff --git a/deployments/kubernetes/monitor-deploy.yaml b/deployments/kubernetes/monitor-deploy.yaml index 29bb1eb6..ef8e5ec3 100644 --- a/deployments/kubernetes/monitor-deploy.yaml +++ b/deployments/kubernetes/monitor-deploy.yaml @@ -77,6 +77,10 @@ spec: items: type: object type: array + csrStatuses: + items: + type: object + type: array required: - ready - resourceCount @@ -89,6 +93,7 @@ spec: - ingressStatuses - jobStatuses - statefulSetStatuses + - csrStatuses type: object version: v1alpha1 versions: @@ -169,6 +174,12 @@ rules: - '*' verbs: - '*' +- apiGroups: + - certificates.k8s.io + resources: + - '*' + verbs: + - '*' --- apiVersion: rbac.authorization.k8s.io/v1 diff --git a/deployments/kubernetes/onap4k8s.yaml b/deployments/kubernetes/onap4k8s.yaml index 6e404873..1d0c55f7 100644 --- a/deployments/kubernetes/onap4k8s.yaml +++ b/deployments/kubernetes/onap4k8s.yaml @@ -361,3 +361,75 @@ spec: - key: config.json path: config.json +--- +# DCM Config Map +kind: ConfigMap +apiVersion: v1 +metadata: + name: dcm + labels: + app: dcm +data: + config.json: | + { + "database-type": "mongo", + "database-ip": "mongo", + "etcd-ip": "etcd", + "service-port": "9077" + } + +--- +# DCM Service +apiVersion: v1 +kind: Service +metadata: + name: dcm + labels: + app: dcm +spec: + selector: + app: dcm + type: NodePort + ports: + - name: http + port: 9077 + nodePort: 31877 + protocol: TCP + targetPort: 9077 + +--- +# DCM Deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dcm +spec: + replicas: 1 + selector: + matchLabels: + app: dcm + template: + metadata: + labels: + app: dcm + spec: + containers: + - name: dcm + image: emcov2/emco:stable + imagePullPolicy: Always + command: ["/opt/emco/entrypoint", "dcm"] + workingDir: /opt/emco + ports: + - containerPort: 9077 + volumeMounts: + - name: config + mountPath: /opt/emco/config.json + subPath: config.json + volumes: + - name: config + configMap: + name: dcm + items: + - key: config.json + path: config.json + diff --git a/kud/tests/plugin_fw.sh b/kud/tests/plugin_fw.sh index 28df372c..a503d661 100755 --- a/kud/tests/plugin_fw.sh +++ b/kud/tests/plugin_fw.sh @@ -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" diff --git a/src/dcm/api/logicalCloudHandler.go b/src/dcm/api/logicalCloudHandler.go index fb0f0c63..b5161efa 100644 --- a/src/dcm/api/logicalCloudHandler.go +++ b/src/dcm/api/logicalCloudHandler.go @@ -25,6 +25,7 @@ import ( "github.com/gorilla/mux" "github.com/onap/multicloud-k8s/src/dcm/pkg/module" + orch "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module" pkgerrors "github.com/pkg/errors" ) @@ -59,6 +60,15 @@ func (h logicalCloudHandler) createHandler(w http.ResponseWriter, r *http.Reques return } + // Validate that the specified Project exists + // before associating a Logical Cloud with it + p := orch.NewProjectClient() + _, err = p.GetProject(project) + if err != nil { + http.Error(w, "The specified project does not exist.", http.StatusNotFound) + return + } + ret, err := h.client.Create(project, v) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) @@ -186,7 +196,7 @@ func (h logicalCloudHandler) applyHandler(w http.ResponseWriter, r *http.Request return } - _, ctxVal, err := h.client.GetLogicalCloudContext(name) + _, ctxVal, err := h.client.GetLogicalCloudContext(project, name) if ctxVal != "" { err = pkgerrors.New("Logical Cloud already applied") http.Error(w, err.Error(), http.StatusConflict) @@ -212,7 +222,7 @@ func (h logicalCloudHandler) applyHandler(w http.ResponseWriter, r *http.Request return } - err = module.CreateEtcdContext(lc, clusters, quotas) + err = module.CreateEtcdContext(project, lc, clusters, quotas) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -237,7 +247,7 @@ func (h logicalCloudHandler) terminateHandler(w http.ResponseWriter, r *http.Req return } - _, ctxVal, err := h.client.GetLogicalCloudContext(name) + _, ctxVal, err := h.client.GetLogicalCloudContext(project, name) if ctxVal == "" { err = pkgerrors.New("Logical Cloud hasn't been applied yet") http.Error(w, err.Error(), http.StatusConflict) @@ -259,7 +269,7 @@ func (h logicalCloudHandler) terminateHandler(w http.ResponseWriter, r *http.Req return } - err = module.DestroyEtcdContext(lc, clusters, quotas) + err = module.DestroyEtcdContext(project, lc, clusters, quotas) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return diff --git a/src/dcm/config.json b/src/dcm/config.json index 65a18acb..7e1f579c 100644 --- a/src/dcm/config.json +++ b/src/dcm/config.json @@ -1,14 +1,6 @@ { - "database-ip": "172.18.0.2", "database-type": "mongo", - "plugin-dir": "plugins", - "service-port": "9077", - "ca-file": "ca.cert", - "server-cert": "server.cert", - "server-key": "server.key", - "password": "", + "database-ip": "172.18.0.2", "etcd-ip": "172.18.0.3", - "etcd-cert": "", - "etcd-key": "", - "etcd-ca-file": "" + "service-port": "9077" } diff --git a/src/dcm/go.sum b/src/dcm/go.sum index ad36ad8d..d1b1e30f 100644 --- a/src/dcm/go.sum +++ b/src/dcm/go.sum @@ -807,6 +807,7 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= +github.com/onap/multicloud-k8s v0.0.0-20200928235143-603a68284970 h1:yWZDIjZBhwtbV7+fa8QB/WhPlHCR4qBhY2OG7K83wGs= github.com/onap/multicloud-k8s/src/ncm v0.0.0-20200515060444-c77850a75eee/go.mod h1:q6s8c45A2NN2V4lxciJ7OmCZFaS1uQSWaGxGG3UM3kM= github.com/onap/multicloud-k8s/src/rsync v0.0.0-20200630152613-7c20f73e7c5d h1:0aXmwqPN8MjyqjKK5L1IhhP/hpP5nGj6xMgo6AOzCPI= github.com/onap/multicloud-k8s/src/rsync v0.0.0-20200630152613-7c20f73e7c5d/go.mod h1:pVhhvg5N0Qy8QDJkYRnWCQbxLDV5GYLmPyzlndbGx7w= diff --git a/src/dcm/pkg/module/apply.go b/src/dcm/pkg/module/apply.go index a866934a..0b370f4a 100644 --- a/src/dcm/pkg/module/apply.go +++ b/src/dcm/pkg/module/apply.go @@ -305,12 +305,11 @@ func callRsyncUninstall(contextid interface{}) error { return nil } -func CreateEtcdContext(logicalcloud LogicalCloud, clusterList []Cluster, +func CreateEtcdContext(project string, logicalcloud LogicalCloud, clusterList []Cluster, quotaList []Quota) error { APP := "logical-cloud" logicalCloudName := logicalcloud.MetaData.LogicalCloudName - project := "test-project" // FIXME(igordc): temporary, need to do some rework in the LC structs //Resource Names namespaceName := strings.Join([]string{logicalcloud.MetaData.LogicalCloudName, "+namespace"}, "") @@ -599,13 +598,12 @@ func CreateEtcdContext(logicalcloud LogicalCloud, clusterList []Cluster, // TODO: rename these methods // DestroyEtcdContext remove from rsync then delete appcontext and all resources -func DestroyEtcdContext(logicalcloud LogicalCloud, clusterList []Cluster, +func DestroyEtcdContext(project string, logicalcloud LogicalCloud, clusterList []Cluster, quotaList []Quota) error { logicalCloudName := logicalcloud.MetaData.LogicalCloudName - // project := "test-project" // FIXME(igordc): temporary, need to do some rework in the LC structs - _, ctxVal, err := NewLogicalCloudClient().GetLogicalCloudContext(logicalCloudName) + _, ctxVal, err := NewLogicalCloudClient().GetLogicalCloudContext(project, logicalCloudName) if err != nil { return pkgerrors.Wrapf(err, "Error finding AppContext for Logical Cloud: %v", logicalCloudName) } diff --git a/src/dcm/pkg/module/logicalcloud.go b/src/dcm/pkg/module/logicalcloud.go index 83ff153f..3ecb6288 100644 --- a/src/dcm/pkg/module/logicalcloud.go +++ b/src/dcm/pkg/module/logicalcloud.go @@ -73,7 +73,7 @@ type LogicalCloudManager interface { GetAll(project string) ([]LogicalCloud, error) Delete(project, name string) error Update(project, name string, c LogicalCloud) (LogicalCloud, error) - GetLogicalCloudContext(name string) (appcontext.AppContext, string, error) + GetLogicalCloudContext(project string, name string) (appcontext.AppContext, string, error) } // Interface facilitates unit testing by mocking functions @@ -237,11 +237,11 @@ func (v *LogicalCloudClient) Update(project, logicalCloudName string, c LogicalC } // GetClusterContext returns the AppContext for corresponding provider and name -func (v *LogicalCloudClient) GetLogicalCloudContext(name string) (appcontext.AppContext, string, error) { +func (v *LogicalCloudClient) GetLogicalCloudContext(project string, name string) (appcontext.AppContext, string, error) { //Construct key and tag to select the entry key := LogicalCloudKey{ LogicalCloudName: name, - Project: "test-project", // FIXME(igordc): temporary, need to do some rework in the LC structs + Project: project, } value, err := db.DBconn.Find(v.storeName, key, v.tagContext) diff --git a/src/k8splugin/api/instancehandler_test.go b/src/k8splugin/api/instancehandler_test.go index 7b6594cf..c0690fb2 100644 --- a/src/k8splugin/api/instancehandler_test.go +++ b/src/k8splugin/api/instancehandler_test.go @@ -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 diff --git a/src/k8splugin/go.mod b/src/k8splugin/go.mod index 57b03266..75cb7c7e 100644 --- a/src/k8splugin/go.mod +++ b/src/k8splugin/go.mod @@ -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 diff --git a/src/k8splugin/internal/app/client.go b/src/k8splugin/internal/app/client.go index ed606444..6762d1bc 100644 --- a/src/k8splugin/internal/app/client.go +++ b/src/k8splugin/internal/app/client.go @@ -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 { diff --git a/src/k8splugin/internal/app/instance.go b/src/k8splugin/internal/app/instance.go index 220c82da..a6e213c1 100644 --- a/src/k8splugin/internal/app/instance.go +++ b/src/k8splugin/internal/app/instance.go @@ -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 diff --git a/src/k8splugin/internal/app/instance_test.go b/src/k8splugin/internal/app/instance_test.go index 1b84b449..b79cf388 100644 --- a/src/k8splugin/internal/app/instance_test.go +++ b/src/k8splugin/internal/app/instance_test.go @@ -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 diff --git a/src/k8splugin/internal/plugin/helpers.go b/src/k8splugin/internal/plugin/helpers.go index 19ff03ab..7078b813 100644 --- a/src/k8splugin/internal/plugin/helpers.go +++ b/src/k8splugin/internal/plugin/helpers.go @@ -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 diff --git a/src/monitor/deploy/crds/k8splugin_v1alpha1_resourcebundlestate_crd.yaml b/src/monitor/deploy/crds/k8splugin_v1alpha1_resourcebundlestate_crd.yaml index 9ca95a32..25197f05 100644 --- a/src/monitor/deploy/crds/k8splugin_v1alpha1_resourcebundlestate_crd.yaml +++ b/src/monitor/deploy/crds/k8splugin_v1alpha1_resourcebundlestate_crd.yaml @@ -77,6 +77,10 @@ spec: items: type: object type: array + csrStatuses: + items: + type: object + type: array required: - ready - resourceCount @@ -89,6 +93,7 @@ spec: - ingressStatuses - jobStatuses - statefulSetStatuses + - csrStatuses type: object version: v1alpha1 versions: |