diff options
Diffstat (limited to 'src/k8splugin')
-rw-r--r-- | src/k8splugin/Makefile | 6 | ||||
-rw-r--r-- | src/k8splugin/api/brokerhandler.go | 18 | ||||
-rw-r--r-- | src/k8splugin/api/brokerhandler_test.go | 44 | ||||
-rw-r--r-- | src/k8splugin/api/instancehandler.go | 80 | ||||
-rw-r--r-- | src/k8splugin/api/profilehandler.go | 11 | ||||
-rw-r--r-- | src/k8splugin/api/profilehandler_test.go | 13 | ||||
-rw-r--r-- | src/k8splugin/internal/app/client.go | 43 | ||||
-rw-r--r-- | src/k8splugin/internal/app/instance.go | 27 | ||||
-rw-r--r-- | src/k8splugin/internal/config/config.go | 6 | ||||
-rw-r--r-- | src/k8splugin/internal/db/README.md | 123 | ||||
-rw-r--r-- | src/k8splugin/internal/db/etcd.go | 7 | ||||
-rw-r--r-- | src/k8splugin/internal/db/testing.go | 14 | ||||
-rw-r--r-- | src/k8splugin/internal/logutils/logger.go | 17 | ||||
-rw-r--r-- | src/k8splugin/internal/rb/definition.go | 48 | ||||
-rw-r--r-- | src/k8splugin/internal/rb/profile.go | 28 |
15 files changed, 408 insertions, 77 deletions
diff --git a/src/k8splugin/Makefile b/src/k8splugin/Makefile index 7d41158c..77196afa 100644 --- a/src/k8splugin/Makefile +++ b/src/k8splugin/Makefile @@ -25,8 +25,8 @@ deploy: build .PHONY: test test: clean - @go build -buildmode=plugin -o ./mock_files/mock_plugins/mockplugin.so ./mock_files/mock_plugins/mockplugin.go - @go test -v ./... + @go build -race -buildmode=plugin -o ./mock_files/mock_plugins/mockplugin.so ./mock_files/mock_plugins/mockplugin.go + @go test -race ./... format: @go fmt ./... @@ -40,5 +40,5 @@ clean: .PHONY: cover cover: - @go test ./... -coverprofile=coverage.out + @go test -race ./... -coverprofile=coverage.out @go tool cover -html=coverage.out -o coverage.html diff --git a/src/k8splugin/api/brokerhandler.go b/src/k8splugin/api/brokerhandler.go index 669b539f..7671db44 100644 --- a/src/k8splugin/api/brokerhandler.go +++ b/src/k8splugin/api/brokerhandler.go @@ -134,21 +134,19 @@ func (b brokerInstanceHandler) createHandler(w http.ResponseWriter, r *http.Requ return } - rbName := req.getAttributeValue(req.UserDirectives, "definition-name") - if rbName == "" { - http.Error(w, "definition-name is missing from user-directives", http.StatusBadRequest) + if req.VFModuleModelInvariantID == "" { + http.Error(w, "vf-module-model-invariant-id is empty", http.StatusBadRequest) return } - rbVersion := req.getAttributeValue(req.UserDirectives, "definition-version") - if rbVersion == "" { - http.Error(w, "definition-version is missing from user-directives", http.StatusBadRequest) + if req.VFModuleModelVersionID == "" { + http.Error(w, "vf-module-model-version-id is empty", http.StatusBadRequest) return } - profileName := req.getAttributeValue(req.UserDirectives, "profile-name") + profileName := req.getAttributeValue(req.SDNCDirectives, "k8s-rb-profile-name") if profileName == "" { - http.Error(w, "profile-name is missing from user-directives", http.StatusBadRequest) + http.Error(w, "k8s-rb-profile-name is missing from sdnc-directives", http.StatusBadRequest) return } @@ -160,8 +158,8 @@ func (b brokerInstanceHandler) createHandler(w http.ResponseWriter, r *http.Requ // Setup the resource parameters for making the request var instReq app.InstanceRequest - instReq.RBName = rbName - instReq.RBVersion = rbVersion + instReq.RBName = req.VFModuleModelInvariantID + instReq.RBVersion = req.VFModuleModelVersionID instReq.ProfileName = profileName instReq.CloudRegion = cloudRegion instReq.Labels = map[string]string{ diff --git a/src/k8splugin/api/brokerhandler_test.go b/src/k8splugin/api/brokerhandler_test.go index 8ef5e184..83ff588b 100644 --- a/src/k8splugin/api/brokerhandler_test.go +++ b/src/k8splugin/api/brokerhandler_test.go @@ -48,18 +48,19 @@ func TestBrokerCreateHandler(t *testing.T) { expectedCode: http.StatusUnprocessableEntity, }, { - label: "Missing parameter failure", + label: "Missing vf-module-*-id parameter", input: bytes.NewBuffer([]byte(`{ "vf-module-model-customization-id": "84sdfkio938", - "user_directives": { + "vf-module-model-invariant-id": "123456qwerty", + "sdnc_directives": { "attributes": [ { - "attribute_name": "definition-name", - "attribute_value": "test-rbdef" + "attribute_name": "vf_module_name", + "attribute_value": "test-vf-module-name" }, { - "attribute_name": "definition-version", - "attribute_value": "v1" + "attribute_name": "k8s-rb-profile-name", + "attribute_value": "profile1" } ] } @@ -67,9 +68,11 @@ func TestBrokerCreateHandler(t *testing.T) { expectedCode: http.StatusBadRequest, }, { - label: "Succesfully create an Instance", + label: "Missing parameter from sdnc_directives", input: bytes.NewBuffer([]byte(`{ "vf-module-model-customization-id": "84sdfkio938", + "vf-module-model-invariant-id": "123456qwerty", + "vf-module-model-version-id": "123qweasdzxc", "sdnc_directives": { "attributes": [ { @@ -77,19 +80,24 @@ func TestBrokerCreateHandler(t *testing.T) { "attribute_value": "test-vf-module-name" } ] - }, - "user_directives": { + } + }`)), + expectedCode: http.StatusBadRequest, + }, + { + label: "Succesfully create an Instance", + input: bytes.NewBuffer([]byte(`{ + "vf-module-model-customization-id": "84sdfkio938", + "vf-module-model-invariant-id": "123456qwerty", + "vf-module-model-version-id": "123qweasdzxc", + "sdnc_directives": { "attributes": [ { - "attribute_name": "definition-name", - "attribute_value": "test-rbdef" - }, - { - "attribute_name": "definition-version", - "attribute_value": "v1" + "attribute_name": "vf_module_name", + "attribute_value": "test-vf-module-name" }, { - "attribute_name": "profile-name", + "attribute_name": "k8s-rb-profile-name", "attribute_value": "profile1" } ] @@ -122,8 +130,8 @@ func TestBrokerCreateHandler(t *testing.T) { { ID: "HaKpys8e", Request: app.InstanceRequest{ - RBName: "test-rbdef", - RBVersion: "v1", + RBName: "123456qwerty", + RBVersion: "123qweasdzxc", ProfileName: "profile1", CloudRegion: "region1", }, diff --git a/src/k8splugin/api/instancehandler.go b/src/k8splugin/api/instancehandler.go index 1dcbcda9..b0437426 100644 --- a/src/k8splugin/api/instancehandler.go +++ b/src/k8splugin/api/instancehandler.go @@ -20,10 +20,10 @@ import ( "net/http" "github.com/onap/multicloud-k8s/src/k8splugin/internal/app" + log "github.com/onap/multicloud-k8s/src/k8splugin/internal/logutils" "github.com/gorilla/mux" pkgerrors "github.com/pkg/errors" - log "github.com/onap/multicloud-k8s/src/k8splugin/internal/logutils" ) // Used to store the backend implementation objects @@ -37,18 +37,25 @@ func (i instanceHandler) validateBody(body interface{}) error { switch b := body.(type) { case app.InstanceRequest: if b.CloudRegion == "" { - log.WithFields("CreateVnfRequest bad request", "CloudRegion", "Invalid/Missing CloudRegion in POST request") + log.Error("CreateVnfRequest Bad Request", log.Fields{ + "cloudRegion": "Missing CloudRegion in POST request", + }) werr := pkgerrors.Wrap(errors.New("Invalid/Missing CloudRegion in POST request"), "CreateVnfRequest bad request") return werr } if b.RBName == "" || b.RBVersion == "" { - log.WithFields("CreateVnfRequest bad request", "RBName", "Invalid/Missing resource bundle parameters in POST request") - log.WithFields("CreateVnfRequest bad request", "RBVersion", "Invalid/Missing resource bundle parameters in POST request") + log.Error("CreateVnfRequest Bad Request", log.Fields{ + "message": "One of RBName, RBVersion is missing", + "RBName": b.RBName, + "RBVersion": b.RBVersion, + }) werr := pkgerrors.Wrap(errors.New("Invalid/Missing resource bundle parameters in POST request"), "CreateVnfRequest bad request") return werr } if b.ProfileName == "" { - log.WithFields("CreateVnfRequest bad request", "ProfileName", "Invalid/Missing profile name in POST request") + log.Error("CreateVnfRequest bad request", log.Fields{ + "ProfileName": "Missing profile name in POST request", + }) werr := pkgerrors.Wrap(errors.New("Invalid/Missing profile name in POST request"), "CreateVnfRequest bad request") return werr } @@ -62,11 +69,15 @@ func (i instanceHandler) createHandler(w http.ResponseWriter, r *http.Request) { err := json.NewDecoder(r.Body).Decode(&resource) switch { case err == io.EOF: - log.WithFields("http.StatusBadRequest", "Error", "Body empty") + log.Error("Body Empty", log.Fields{ + "error": io.EOF, + }) http.Error(w, "Body empty", http.StatusBadRequest) return case err != nil: - log.WithFields("http.StatusUnprocessableEntity", "Error", "http.StatusUnprocessableEntity") + log.Error("Error unmarshaling Body", log.Fields{ + "error": err, + }) http.Error(w, err.Error(), http.StatusUnprocessableEntity) return } @@ -74,14 +85,19 @@ func (i instanceHandler) createHandler(w http.ResponseWriter, r *http.Request) { // Check body for expected parameters err = i.validateBody(resource) if err != nil { - log.WithFields("StatusUnprocessableEntity", "Error", "http.StatusUnprocessableEntity") + log.Error("Invalid Parameters in Body", log.Fields{ + "error": err, + }) http.Error(w, err.Error(), http.StatusUnprocessableEntity) return } resp, err := i.client.Create(resource) if err != nil { - log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError") + log.Error("Error Creating Resource", log.Fields{ + "error": err, + "resource": resource, + }) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -90,7 +106,10 @@ func (i instanceHandler) createHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) err = json.NewEncoder(w).Encode(resp) if err != nil { - log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError") + log.Error("Error Marshaling Response", log.Fields{ + "error": err, + "response": resp, + }) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -103,7 +122,10 @@ func (i instanceHandler) getHandler(w http.ResponseWriter, r *http.Request) { resp, err := i.client.Get(id) if err != nil { - log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError") + log.Error("Error getting Instance", log.Fields{ + "error": err, + "id": id, + }) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -112,7 +134,10 @@ func (i instanceHandler) getHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) err = json.NewEncoder(w).Encode(resp) if err != nil { - log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError") + log.Error("Error Marshaling Response", log.Fields{ + "error": err, + "response": resp, + }) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -125,7 +150,10 @@ func (i instanceHandler) statusHandler(w http.ResponseWriter, r *http.Request) { resp, err := i.client.Status(id) if err != nil { - log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError") + log.Error("Error getting Status", log.Fields{ + "error": err, + "id": id, + }) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -134,7 +162,10 @@ func (i instanceHandler) statusHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) err = json.NewEncoder(w).Encode(resp) if err != nil { - log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError") + log.Error("Error Marshaling Response", log.Fields{ + "error": err, + "response": resp, + }) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -147,11 +178,16 @@ func (i instanceHandler) listHandler(w http.ResponseWriter, r *http.Request) { //Which will list all instances rbName := r.FormValue("rb-name") rbVersion := r.FormValue("rb-version") - ProfileName := r.FormValue("profile-name") + profileName := r.FormValue("profile-name") - resp, err := i.client.List(rbName, rbVersion, ProfileName) + resp, err := i.client.List(rbName, rbVersion, profileName) if err != nil { - log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError") + log.Error("Error listing instances", log.Fields{ + "error": err, + "rb-name": rbName, + "rb-version": rbVersion, + "profile-name": profileName, + }) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -160,7 +196,10 @@ func (i instanceHandler) listHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) err = json.NewEncoder(w).Encode(resp) if err != nil { - log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError") + log.Error("Error Marshaling Response", log.Fields{ + "error": err, + "response": resp, + }) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -173,7 +212,9 @@ func (i instanceHandler) deleteHandler(w http.ResponseWriter, r *http.Request) { err := i.client.Delete(id) if err != nil { - log.WithFields("StatusInternalServerError", "Error", "http.StatusInternalServerError") + log.Error("Error Deleting Instance", log.Fields{ + "error": err, + }) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -181,4 +222,3 @@ func (i instanceHandler) deleteHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) } - diff --git a/src/k8splugin/api/profilehandler.go b/src/k8splugin/api/profilehandler.go index 9aed2990..acd23060 100644 --- a/src/k8splugin/api/profilehandler.go +++ b/src/k8splugin/api/profilehandler.go @@ -21,6 +21,7 @@ import ( "io" "io/ioutil" "net/http" + "strings" "github.com/onap/multicloud-k8s/src/k8splugin/internal/rb" @@ -107,8 +108,14 @@ func (h rbProfileHandler) getHandler(w http.ResponseWriter, r *http.Request) { ret, err := h.client.Get(rbName, rbVersion, prName) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return + // Separate "Not found" from generic DB errors + if strings.Contains(err.Error(), "Error finding") { + http.Error(w, err.Error(), http.StatusNotFound) + return + } else { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } w.Header().Set("Content-Type", "application/json") diff --git a/src/k8splugin/api/profilehandler_test.go b/src/k8splugin/api/profilehandler_test.go index 4dae377c..9ec9c54c 100644 --- a/src/k8splugin/api/profilehandler_test.go +++ b/src/k8splugin/api/profilehandler_test.go @@ -184,10 +184,19 @@ func TestRBProfileGetHandler(t *testing.T) { }, }, { - label: "Get Non-Exiting Bundle Profile", - expectedCode: http.StatusInternalServerError, + label: "Get Non-Existing Profile", + expectedCode: http.StatusNotFound, prname: "non-existing-profile", rbProClient: &mockRBProfile{ + Items: nil, + Err: pkgerrors.New("Error finding master table"), + }, + }, + { + label: "Faulty DB response", + expectedCode: http.StatusInternalServerError, + prname: "profile", + rbProClient: &mockRBProfile{ // list of Profiles that will be returned by the mockclient Items: []rb.Profile{}, Err: pkgerrors.New("Internal Error"), diff --git a/src/k8splugin/internal/app/client.go b/src/k8splugin/internal/app/client.go index e52225d4..d3e5081a 100644 --- a/src/k8splugin/internal/app/client.go +++ b/src/k8splugin/internal/app/client.go @@ -14,12 +14,13 @@ limitations under the License. package app import ( - "log" "os" + "strings" "time" "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" "github.com/onap/multicloud-k8s/src/k8splugin/internal/plugin" pkgerrors "github.com/pkg/errors" @@ -116,11 +117,27 @@ func (k *KubernetesClient) ensureNamespace(namespace string) error { }, }, namespace, k) + // Check for errors getting the namespace while ignoring errors where the namespace does not exist + // Error message when namespace does not exist: "namespaces "namespace-name" not found" + if err != nil && strings.Contains(err.Error(), "not found") == false { + log.Error("Error checking for namespace", log.Fields{ + "error": err, + "namespace": namespace, + }) + return pkgerrors.Wrap(err, "Error checking for namespace: "+namespace) + } + if ns == "" { - log.Println("Creating " + namespace + " namespace") + log.Info("Creating Namespace", log.Fields{ + "namespace": namespace, + }) _, err = pluginImpl.Create("", namespace, k) if err != nil { + log.Error("Error Creating Namespace", log.Fields{ + "error": err, + "namespace": namespace, + }) return pkgerrors.Wrap(err, "Error creating "+namespace+" namespace") } } @@ -134,7 +151,9 @@ func (k *KubernetesClient) createKind(resTempl helm.KubernetesResourceTemplate, return helm.KubernetesResource{}, pkgerrors.New("File " + resTempl.FilePath + "does not exists") } - log.Println("Processing file: " + resTempl.FilePath) + log.Info("Processing Kubernetes Resource", log.Fields{ + "filepath": resTempl.FilePath, + }) pluginImpl, err := plugin.GetPluginByKind(resTempl.GVK.Kind) if err != nil { @@ -143,11 +162,19 @@ func (k *KubernetesClient) createKind(resTempl helm.KubernetesResourceTemplate, createdResourceName, err := pluginImpl.Create(resTempl.FilePath, namespace, k) if err != nil { - log.Printf("Error: %s while creating: %s", err.Error(), resTempl.GVK.Kind) + log.Error("Error Creating 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.Print(createdResourceName + " created") + log.Info("Created Kubernetes Resource", log.Fields{ + "resource": createdResourceName, + "gvk": resTempl.GVK, + }) + return helm.KubernetesResource{ GVK: resTempl.GVK, Name: createdResourceName, @@ -175,14 +202,16 @@ func (k *KubernetesClient) createResources(sortedTemplates []helm.KubernetesReso } func (k *KubernetesClient) deleteKind(resource helm.KubernetesResource, namespace string) error { - log.Println("Deleting Kind: " + resource.GVK.Kind) + log.Warn("Deleting Resource", log.Fields{ + "gvk": resource.GVK, + "resource": resource.Name, + }) pluginImpl, err := plugin.GetPluginByKind(resource.GVK.Kind) if err != nil { return pkgerrors.Wrap(err, "Error loading plugin") } - log.Println("Deleting resource: " + resource.Name) err = pluginImpl.Delete(resource, namespace, k) if err != nil { return pkgerrors.Wrap(err, "Error deleting "+resource.Name) diff --git a/src/k8splugin/internal/app/instance.go b/src/k8splugin/internal/app/instance.go index fef9962f..5d8b2100 100644 --- a/src/k8splugin/internal/app/instance.go +++ b/src/k8splugin/internal/app/instance.go @@ -32,19 +32,21 @@ import ( // InstanceRequest contains the parameters needed for instantiation // of profiles type InstanceRequest struct { - RBName string `json:"rb-name"` - RBVersion string `json:"rb-version"` - ProfileName string `json:"profile-name"` - CloudRegion string `json:"cloud-region"` - Labels map[string]string `json:"labels"` + RBName string `json:"rb-name"` + RBVersion string `json:"rb-version"` + ProfileName string `json:"profile-name"` + CloudRegion string `json:"cloud-region"` + Labels map[string]string `json:"labels"` + OverrideValues map[string]string `json:"override-values"` } // InstanceResponse contains the response from instantiation type InstanceResponse struct { - ID string `json:"id"` - Request InstanceRequest `json:"request"` - Namespace string `json:"namespace"` - Resources []helm.KubernetesResource `json:"resources"` + ID string `json:"id"` + Request InstanceRequest `json:"request"` + Namespace string `json:"namespace"` + Resources []helm.KubernetesResource `json:"resources"` + OverrideValues map[string]string `json:"override-values"` } // InstanceMiniResponse contains the response from instantiation @@ -133,7 +135,14 @@ func (v *InstanceClient) Create(i InstanceRequest) (InstanceResponse, error) { return InstanceResponse{}, pkgerrors.New("Unable to find Profile to create instance") } + //Convert override values from map to array of strings of the following format + //foo=bar overrideValues := []string{} + if i.OverrideValues != nil { + for k, v := range i.OverrideValues { + overrideValues = append(overrideValues, k+"="+v) + } + } //Execute the kubernetes create command sortedTemplates, err := rb.NewProfileClient().Resolve(i.RBName, i.RBVersion, i.ProfileName, overrideValues) diff --git a/src/k8splugin/internal/config/config.go b/src/k8splugin/internal/config/config.go index 0e45308c..89f2553d 100644 --- a/src/k8splugin/internal/config/config.go +++ b/src/k8splugin/internal/config/config.go @@ -82,9 +82,9 @@ func defaultConfiguration() *Configuration { DatabaseType: "mongo", PluginDir: cwd, EtcdIP: "127.0.0.1", - EtcdCert: "etcd.cert", - EtcdKey: "etcd.key", - EtcdCAFile: "etcd-ca.cert", + EtcdCert: "", + EtcdKey: "", + EtcdCAFile: "", ServicePort: "9015", KubernetesLabelName: "k8splugin.io/rb-instance-id", } diff --git a/src/k8splugin/internal/db/README.md b/src/k8splugin/internal/db/README.md new file mode 100644 index 00000000..cba1b7ea --- /dev/null +++ b/src/k8splugin/internal/db/README.md @@ -0,0 +1,123 @@ +# Database Abstraction Layer + +This package contains implementations of the Database interface defined in `store.go` +Any database can be used as the backend as long as the following interface is implemented; + +```go +type Store interface { + // Returns nil if db health is good + HealthCheck() error + + // Unmarshal implements any unmarshaling needed for the database + Unmarshal(inp []byte, out interface{}) error + + // Creates a new master table with key and links data with tag and + // creates a pointer to the newly added data in the master table + Create(table string, key Key, tag string, data interface{}) error + + // Reads data for a particular key with specific tag. + Read(table string, key Key, tag string) ([]byte, error) + + // Update data for particular key with specific tag + Update(table string, key Key, tag string, data interface{}) error + + // Deletes a specific tag data for key. + // TODO: If tag is empty, it will delete all tags under key. + Delete(table string, key Key, tag string) error + + // Reads all master tables and data from the specified tag in table + ReadAll(table string, tag string) (map[string][]byte, error) +} +``` + +Therefore, `mongo.go`, `consul.go` implement the above interface and can be used as the backend as needed based on initial configuration. + +## Details on Mongo Implementation + +`mongo.go` implements the above interface using the `go.mongodb.org/mongo-driver` package. +The code converts incoming binary data and creates a new document in the database. + +### Create + +Arguments: +```go +collection string +key interface +tag string +data []byte +``` + +Create inserts the provided `data` into the `collection` which returns an auto-generated (by `mongodb`) ID which we then associate with the `key` that is provided as one of the arguments. + +We use the `FindOneAndUpdate` mongo API to achieve this with the `upsert` option set to `true`. +We create the following documents in mongodb for each new definition added to the database: + +There is a Master Key document that contains references to other documents which are related to this `key`. + +#### Master Key Entry +```json +{ + "_id" : ObjectId("5e0a8554b78a15f71d2dce7e"), + "key" : { "rbname" : "edgex", "rbversion" : "v1"}, + "defmetadata" : ObjectId("5e0a8554be261ecb57f067eb"), + "defcontent" : ObjectId("5e0a8377bcfcdd0f01dc7b0d") +} +``` +#### Metadata Key Entry +```json +{ + "_id" : ObjectId("5e0a8554be261ecb57f067eb"), + "defmetadata" : { "rbname" : "edgex", "rbversion" : "v1", "chartname" : "", "description" : "", "labels" : null } +} +``` +#### Definition Content +```json +{ + "_id" : ObjectId("5e0a8377bcfcdd0f01dc7b0d"), + "defcontent" : "H4sICCVd3FwAA3Byb2ZpbGUxLnRhcgDt1NEKgjAUxvFd7ylG98aWOsGXiYELxLRwJvj2rbyoIPDGiuD/uzmwM9iB7Vvruvrgw7CdXHsUn6Ejm2W3aopcP9eZLYRJM1voPN+ZndAm16kVSn9onheXMLheKeGqfdM0rq07/3bfUv9PJUkiR9+H+tSVajRymM6+lEqN7njxoVSbU+z2deX388r9nWzkr8fGSt5d79pnLOZfm0f+dRrzb7P4DZD/LyDJAAAAAAAAAAAAAAAA/+0Ksq1N5QAoAAA=" +} +``` + +### Unmarshal + +Data in mongo is stored as `bson` which is a compressed form of `json`. We need mongo to convert the stored `bson` data to regular `json` +that we can use in our code when returned. + +We just use the `bson.Unmarshal` API to achieve this. + +### Read + +Arguments: +```go +collection string +key interface +tag string +``` + +Read is straight forward and it uses the `FindOne` API to find our Mongo document based on the provided `key` and then gets the corresponding data for the given `tag`. It will return []byte which can then be passed to the `Unmarshal` function to get the desired GO object. + +### Delete + +Delete is similar to Read and deletes all the objectIDs being stored for a given `key` in the collection. + +## Testing Interfaces + +The following interface exists to allow for the development of unit tests which don't require mongo to be running. +It is mentioned so in the code as well. + +```go +// MongoCollection defines the a subset of MongoDB operations +// Note: This interface is defined mainly for mock testing +type MongoCollection interface { + InsertOne(ctx context.Context, document interface{}, + opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) + FindOne(ctx context.Context, filter interface{}, + opts ...*options.FindOneOptions) *mongo.SingleResult + FindOneAndUpdate(ctx context.Context, filter interface{}, + update interface{}, opts ...*options.FindOneAndUpdateOptions) *mongo.SingleResult + DeleteOne(ctx context.Context, filter interface{}, + opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) + Find(ctx context.Context, filter interface{}, + opts ...*options.FindOptions) (*mongo.Cursor, error) +} +```
\ No newline at end of file diff --git a/src/k8splugin/internal/db/etcd.go b/src/k8splugin/internal/db/etcd.go index fda44b2f..97771a07 100644 --- a/src/k8splugin/internal/db/etcd.go +++ b/src/k8splugin/internal/db/etcd.go @@ -71,7 +71,12 @@ func newClient(store *clientv3.Client, c EtcdConfig) (EtcdClient, error) { if len(c.CertFile) == 0 && len(c.KeyFile) == 0 && len(c.CAFile) == 0 { tlsConfig = nil } - endpoint := "https://" + c.Endpoint + ":2379" + endpoint := "" + if tlsConfig == nil { + endpoint = "http://" + c.Endpoint + ":2379" + } else { + endpoint = "https://" + c.Endpoint + ":2379" + } store, err = clientv3.New(clientv3.Config{ Endpoints: []string{endpoint}, diff --git a/src/k8splugin/internal/db/testing.go b/src/k8splugin/internal/db/testing.go index 5f69dcb4..9a427e03 100644 --- a/src/k8splugin/internal/db/testing.go +++ b/src/k8splugin/internal/db/testing.go @@ -15,6 +15,7 @@ package db import ( "encoding/json" + pkgerrors "github.com/pkg/errors" ) @@ -40,6 +41,19 @@ func (m *MockDB) HealthCheck() error { } func (m *MockDB) Create(table string, key Key, tag string, data interface{}) error { + djs, err := json.Marshal(data) + if err != nil { + return err + } + + d := make(map[string][]byte) + d[tag] = djs + + if m.Items == nil { + m.Items = make(map[string]map[string][]byte) + } + m.Items[key.String()] = d + return m.Err } diff --git a/src/k8splugin/internal/logutils/logger.go b/src/k8splugin/internal/logutils/logger.go index 7df23474..2e8f9969 100644 --- a/src/k8splugin/internal/logutils/logger.go +++ b/src/k8splugin/internal/logutils/logger.go @@ -4,12 +4,25 @@ import ( log "github.com/sirupsen/logrus" ) +//Fields is type that will be used by the calling function +type Fields map[string]interface{} + func init() { // Log as JSON instead of the default ASCII formatter. log.SetFormatter(&log.JSONFormatter{}) } -func WithFields(msg string, fkey string, fvalue string) { - log.WithFields(log.Fields{fkey: fvalue}).Error(msg) +// Error uses the fields provided and logs +func Error(msg string, fields Fields) { + log.WithFields(log.Fields(fields)).Error(msg) +} + +// Warn uses the fields provided and logs +func Warn(msg string, fields Fields) { + log.WithFields(log.Fields(fields)).Warn(msg) } +// Info uses the fields provided and logs +func Info(msg string, fields Fields) { + log.WithFields(log.Fields(fields)).Info(msg) +} diff --git a/src/k8splugin/internal/rb/definition.go b/src/k8splugin/internal/rb/definition.go index 65ae8e00..73ea44da 100644 --- a/src/k8splugin/internal/rb/definition.go +++ b/src/k8splugin/internal/rb/definition.go @@ -26,6 +26,7 @@ import ( "path/filepath" "github.com/onap/multicloud-k8s/src/k8splugin/internal/db" + "github.com/onap/multicloud-k8s/src/k8splugin/internal/logutils" pkgerrors "github.com/pkg/errors" ) @@ -101,6 +102,40 @@ func (v *DefinitionClient) Create(def Definition) (Definition, error) { return Definition{}, pkgerrors.Wrap(err, "Creating DB Entry") } + // Create a default profile automatically + prc := NewProfileClient() + pr, err := prc.Create(Profile{ + RBName: def.RBName, + RBVersion: def.RBVersion, + ProfileName: "default", + Namespace: "default", + ReleaseName: "default", + }) + + if err != nil { + logutils.Error("Create Default Profile", logutils.Fields{ + "error": err, + "rb-name": def.RBName, + "rb-version": def.RBVersion, + "profile-name": "default", + "namespace": "default", + "release-name": "default", + }) + return Definition{}, pkgerrors.Wrap(err, "Creating Default Profile") + } + + err = prc.Upload(pr.RBName, pr.RBVersion, pr.ProfileName, prc.getEmptyProfile()) + if err != nil { + logutils.Error("Upload Empty Profile", logutils.Fields{ + "error": err, + "rb-name": pr.RBName, + "rb-version": pr.RBVersion, + "profile-name": pr.ProfileName, + "profile-content": prc.getEmptyProfile(), + }) + return Definition{}, pkgerrors.Wrap(err, "Upload Empty Profile") + } + return def, nil } @@ -173,6 +208,19 @@ func (v *DefinitionClient) Delete(name string, version string) error { return pkgerrors.Wrap(err, "Delete Resource Bundle Definition Content") } + //Delete the default profile as well + prc := NewProfileClient() + err = prc.Delete(name, version, "default") + if err != nil { + logutils.Error("Delete Default Profile", logutils.Fields{ + "error": err, + "rb-name": name, + "rb-version": version, + "profile-name": "default", + }) + return pkgerrors.Wrap(err, "Deleting default profile") + } + return nil } diff --git a/src/k8splugin/internal/rb/profile.go b/src/k8splugin/internal/rb/profile.go index 49768d4b..6efa23b8 100644 --- a/src/k8splugin/internal/rb/profile.go +++ b/src/k8splugin/internal/rb/profile.go @@ -338,3 +338,31 @@ func (v *ProfileClient) Resolve(rbName string, rbVersion string, return sortedTemplates, nil } + +// Returns an empty profile with the following contents +// Contains a manifest.yaml pointing to an override_values.yaml +// The override_values.yaml file is empty. +func (v *ProfileClient) getEmptyProfile() []byte { + return []byte{ + 0x1F, 0x8B, 0x08, 0x08, 0x25, 0x5D, 0xDC, 0x5C, 0x00, 0x03, 0x70, + 0x72, 0x6F, 0x66, 0x69, 0x6C, 0x65, 0x31, 0x2E, 0x74, 0x61, 0x72, + 0x00, 0xED, 0xD4, 0xD1, 0x0A, 0x82, 0x30, 0x14, 0xC6, 0xF1, 0x5D, + 0xEF, 0x29, 0x46, 0xF7, 0xC6, 0x96, 0x3A, 0xC1, 0x97, 0x89, 0x81, + 0x0B, 0xC4, 0xB4, 0x70, 0x26, 0xF8, 0xF6, 0xAD, 0xBC, 0xA8, 0x20, + 0xF0, 0xC6, 0x8A, 0xE0, 0xFF, 0xBB, 0x39, 0xB0, 0x33, 0xD8, 0x81, + 0xED, 0x5B, 0xEB, 0xBA, 0xFA, 0xE0, 0xC3, 0xB0, 0x9D, 0x5C, 0x7B, + 0x14, 0x9F, 0xA1, 0x23, 0x9B, 0x65, 0xB7, 0x6A, 0x8A, 0x5C, 0x3F, + 0xD7, 0x99, 0x2D, 0x84, 0x49, 0x33, 0x5B, 0xE8, 0x3C, 0xDF, 0x99, + 0x9D, 0xD0, 0x26, 0xD7, 0xA9, 0x15, 0x4A, 0x7F, 0x68, 0x9E, 0x17, + 0x97, 0x30, 0xB8, 0x5E, 0x29, 0xE1, 0xAA, 0x7D, 0xD3, 0x34, 0xAE, + 0xAD, 0x3B, 0xFF, 0x76, 0xDF, 0x52, 0xFF, 0x4F, 0x25, 0x49, 0x22, + 0x47, 0xDF, 0x87, 0xFA, 0xD4, 0x95, 0x6A, 0x34, 0x72, 0x98, 0xCE, + 0xBE, 0x94, 0x4A, 0x8D, 0xEE, 0x78, 0xF1, 0xA1, 0x54, 0x9B, 0x53, + 0xEC, 0xF6, 0x75, 0xE5, 0xF7, 0xF3, 0xCA, 0xFD, 0x9D, 0x6C, 0xE4, + 0xAF, 0xC7, 0xC6, 0x4A, 0xDE, 0x5D, 0xEF, 0xDA, 0x67, 0x2C, 0xE6, + 0x5F, 0x9B, 0x47, 0xFE, 0x75, 0x1A, 0xF3, 0x6F, 0xB3, 0xF8, 0x0D, + 0x90, 0xFF, 0x2F, 0x20, 0xC9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x0A, 0xB2, 0xAD, + 0x4D, 0xE5, 0x00, 0x28, 0x00, 0x00, + } +} |