summaryrefslogtreecommitdiffstats
path: root/src/k8splugin
diff options
context:
space:
mode:
Diffstat (limited to 'src/k8splugin')
-rw-r--r--src/k8splugin/Makefile6
-rw-r--r--src/k8splugin/api/brokerhandler.go18
-rw-r--r--src/k8splugin/api/brokerhandler_test.go44
-rw-r--r--src/k8splugin/api/instancehandler.go80
-rw-r--r--src/k8splugin/api/profilehandler.go11
-rw-r--r--src/k8splugin/api/profilehandler_test.go13
-rw-r--r--src/k8splugin/internal/app/client.go43
-rw-r--r--src/k8splugin/internal/app/instance.go27
-rw-r--r--src/k8splugin/internal/config/config.go6
-rw-r--r--src/k8splugin/internal/db/README.md123
-rw-r--r--src/k8splugin/internal/db/etcd.go7
-rw-r--r--src/k8splugin/internal/db/testing.go14
-rw-r--r--src/k8splugin/internal/logutils/logger.go17
-rw-r--r--src/k8splugin/internal/rb/definition.go48
-rw-r--r--src/k8splugin/internal/rb/profile.go28
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,
+ }
+}