diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/k8splugin/api/brokerhandler.go | 14 | ||||
-rw-r--r-- | src/k8splugin/api/brokerhandler_test.go | 49 | ||||
-rw-r--r-- | src/k8splugin/api/profilehandler.go | 29 | ||||
-rw-r--r-- | src/k8splugin/api/profilehandler_test.go | 13 | ||||
-rw-r--r-- | src/k8splugin/internal/app/instance.go | 27 | ||||
-rw-r--r-- | src/k8splugin/internal/db/README.md | 123 | ||||
-rw-r--r-- | src/k8splugin/internal/db/testing.go | 14 | ||||
-rw-r--r-- | src/k8splugin/internal/rb/definition.go | 48 | ||||
-rw-r--r-- | src/k8splugin/internal/rb/profile.go | 28 | ||||
-rw-r--r-- | src/orchestrator/go.sum | 1 | ||||
-rw-r--r-- | src/orchestrator/internal/db/README.md | 123 | ||||
-rw-r--r-- | src/orchestrator/internal/logutils/logger.go | 28 |
12 files changed, 433 insertions, 64 deletions
diff --git a/src/k8splugin/api/brokerhandler.go b/src/k8splugin/api/brokerhandler.go index b377baf1..7671db44 100644 --- a/src/k8splugin/api/brokerhandler.go +++ b/src/k8splugin/api/brokerhandler.go @@ -134,15 +134,13 @@ func (b brokerInstanceHandler) createHandler(w http.ResponseWriter, r *http.Requ return } - rbName := req.getAttributeValue(req.SDNCDirectives, "k8s-rb-definition-name") - if rbName == "" { - http.Error(w, "k8s-rb-definition-name is missing from sdnc-directives", http.StatusBadRequest) + if req.VFModuleModelInvariantID == "" { + http.Error(w, "vf-module-model-invariant-id is empty", http.StatusBadRequest) return } - rbVersion := req.getAttributeValue(req.SDNCDirectives, "k8s-rb-definition-version") - if rbVersion == "" { - http.Error(w, "k8s-rb-definition-version is missing from sdnc-directives", http.StatusBadRequest) + if req.VFModuleModelVersionID == "" { + http.Error(w, "vf-module-model-version-id is empty", 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 00ca3b7b..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": "k8s-rb-definition-name", - "attribute_value": "test-rbdef" + "attribute_name": "vf_module_name", + "attribute_value": "test-vf-module-name" }, { - "attribute_name": "k8s-rb-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: "Deprecated parameters passed (user_directives)", + label: "Missing parameter from sdnc_directives", input: bytes.NewBuffer([]byte(`{ - "vf-module-model-customization-id": "97sdfkio168", + "vf-module-model-customization-id": "84sdfkio938", + "vf-module-model-invariant-id": "123456qwerty", + "vf-module-model-version-id": "123qweasdzxc", "sdnc_directives": { "attributes": [ { @@ -77,22 +80,6 @@ func TestBrokerCreateHandler(t *testing.T) { "attribute_value": "test-vf-module-name" } ] - }, - "user_directives": { - "attributes": [ - { - "attribute_name": "rb-definition-name", - "attribute_value": "test-rbdef" - }, - { - "attribute_name": "rb-definition-version", - "attribute_value": "v1" - }, - { - "attribute_name": "rb-profile-name", - "attribute_value": "profile1" - } - ] } }`)), expectedCode: http.StatusBadRequest, @@ -101,6 +88,8 @@ func TestBrokerCreateHandler(t *testing.T) { 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": [ { @@ -108,14 +97,6 @@ func TestBrokerCreateHandler(t *testing.T) { "attribute_value": "test-vf-module-name" }, { - "attribute_name": "k8s-rb-definition-name", - "attribute_value": "test-rbdef" - }, - { - "attribute_name": "k8s-rb-definition-version", - "attribute_value": "v1" - }, - { "attribute_name": "k8s-rb-profile-name", "attribute_value": "profile1" } @@ -149,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/profilehandler.go b/src/k8splugin/api/profilehandler.go index 68ab77a4..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" @@ -30,12 +31,12 @@ import ( // Used to store backend implementations objects // Also simplifies mocking for unit testing purposes type rbProfileHandler struct { - // Interface that implements bundle Definition operations - // We will set this variable with a mock interface for testing + // Interface that implements bundle profile operations + // Set this variable with a mock interface for testing client rb.ProfileManager } -// createHandler handles creation of the definition entry in the database +// createHandler creates a profile entry in the database func (h rbProfileHandler) createHandler(w http.ResponseWriter, r *http.Request) { var p rb.Profile @@ -70,7 +71,7 @@ func (h rbProfileHandler) createHandler(w http.ResponseWriter, r *http.Request) } } -// uploadHandler handles upload of the bundle tar file into the database +// uploadHandler uploads the profile artifact tar file into the database func (h rbProfileHandler) uploadHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) rbName := vars["rbname"] @@ -97,8 +98,8 @@ func (h rbProfileHandler) uploadHandler(w http.ResponseWriter, r *http.Request) w.WriteHeader(http.StatusOK) } -// getHandler handles GET operations on a particular ids -// Returns a rb.Definition +// getHandler gets a Profile Key in the database +// Returns an rb.Profile func (h rbProfileHandler) getHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) rbName := vars["rbname"] @@ -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") @@ -120,8 +127,8 @@ func (h rbProfileHandler) getHandler(w http.ResponseWriter, r *http.Request) { } } -// getHandler handles GET operations on a particular ids -// Returns a rb.Definition +// getHandler gets all profiles of a Resource Bundle Key in the database +// Returns a list of rb.Profile func (h rbProfileHandler) listHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) rbName := vars["rbname"] @@ -142,7 +149,7 @@ func (h rbProfileHandler) listHandler(w http.ResponseWriter, r *http.Request) { } } -// deleteHandler handles DELETE operations on a particular bundle definition id +// deleteHandler deletes a particular Profile Key in the database func (h rbProfileHandler) deleteHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) rbName := vars["rbname"] 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/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/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/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/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, + } +} diff --git a/src/orchestrator/go.sum b/src/orchestrator/go.sum index dcda41d4..732bc280 100644 --- a/src/orchestrator/go.sum +++ b/src/orchestrator/go.sum @@ -201,6 +201,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rubenv/sql-migrate v0.0.0-20190902133344-8926f37f0bc1/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= diff --git a/src/orchestrator/internal/db/README.md b/src/orchestrator/internal/db/README.md new file mode 100644 index 00000000..cba1b7ea --- /dev/null +++ b/src/orchestrator/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/orchestrator/internal/logutils/logger.go b/src/orchestrator/internal/logutils/logger.go new file mode 100644 index 00000000..2e8f9969 --- /dev/null +++ b/src/orchestrator/internal/logutils/logger.go @@ -0,0 +1,28 @@ +package logutils + +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{}) +} + +// 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) +} |