diff options
author | Bin Yang <bin.yang@windriver.com> | 2020-02-15 03:02:14 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@onap.org> | 2020-02-15 03:02:14 +0000 |
commit | 8af74ae61508a3fbfd54c25d1cfe037f3ad08ca5 (patch) | |
tree | f22f86d106d281aec7ccedf6b7a4f1078f5cef9c | |
parent | e8e3cfc3d3df008505520836fafc9920f5643ca6 (diff) | |
parent | c3e1c9a5fefde3fcb6aaf05c19b18f211c1a43ba (diff) |
Merge changes I56fb4643,Ia1e9802a
* changes:
Add etcd support in infrastructure layer
Restructure code and create module library
-rw-r--r-- | src/orchestrator/api/api.go | 19 | ||||
-rw-r--r-- | src/orchestrator/api/projecthandler.go | 12 | ||||
-rw-r--r-- | src/orchestrator/api/projecthandler_test.go | 38 | ||||
-rw-r--r-- | src/orchestrator/cmd/main.go | 14 | ||||
-rw-r--r-- | src/orchestrator/examples/example_module.go | 48 | ||||
-rw-r--r-- | src/orchestrator/go.sum | 1 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/auth/auth.go (renamed from src/orchestrator/internal/auth/auth.go) | 0 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/auth/auth_test.go (renamed from src/orchestrator/internal/auth/auth_test.go) | 6 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/config/config.go (renamed from src/orchestrator/internal/config/config.go) | 6 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/config/config_test.go (renamed from src/orchestrator/internal/config/config_test.go) | 2 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/contextdb/contextdb.go | 72 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/contextdb/etcd.go | 175 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/contextdb/etcd_test.go | 276 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/contextdb/mock.go | 58 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/db/README.md (renamed from src/orchestrator/internal/db/README.md) | 0 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/db/mock.go (renamed from src/orchestrator/internal/db/mock.go) | 0 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/db/mongo.go (renamed from src/orchestrator/internal/db/mongo.go) | 2 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/db/mongo_test.go (renamed from src/orchestrator/internal/db/mongo_test.go) | 0 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/db/store.go (renamed from src/orchestrator/internal/db/store.go) | 2 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/db/store_test.go (renamed from src/orchestrator/internal/db/store_test.go) | 0 | ||||
-rw-r--r-- | src/orchestrator/pkg/infra/logutils/logger.go (renamed from src/orchestrator/internal/logutils/logger.go) | 0 | ||||
-rw-r--r-- | src/orchestrator/pkg/module/module.go | 34 | ||||
-rw-r--r-- | src/orchestrator/pkg/module/project.go (renamed from src/orchestrator/internal/project/project.go) | 30 | ||||
-rw-r--r-- | src/orchestrator/pkg/module/project_test.go (renamed from src/orchestrator/internal/project/project_test.go) | 10 |
24 files changed, 737 insertions, 68 deletions
diff --git a/src/orchestrator/api/api.go b/src/orchestrator/api/api.go index 83f17bbe..e37b158a 100644 --- a/src/orchestrator/api/api.go +++ b/src/orchestrator/api/api.go @@ -10,29 +10,28 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ - package api import ( - "github.com/onap/multicloud-k8s/src/orchestrator/internal/project" + moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module" "github.com/gorilla/mux" ) - +var moduleClient *moduleLib.Client // NewRouter creates a router that registers the various urls that are supported -func NewRouter(projectClient project.ProjectManager) *mux.Router { +func NewRouter(projectClient moduleLib.ProjectManager) *mux.Router { router := mux.NewRouter().PathPrefix("/v2").Subrouter() - + moduleClient = moduleLib.NewClient() if projectClient == nil { - projectClient = project.NewProjectClient() + projectClient = moduleClient.Project } projHandler := projectHandler{ client: projectClient, } - router.HandleFunc("/project", projHandler.createHandler).Methods("POST") - router.HandleFunc("/project/{project-name}", projHandler.getHandler).Methods("GET") - router.HandleFunc("/project/{project-name}", projHandler.deleteHandler).Methods("DELETE") + router.HandleFunc("/projects", projHandler.createHandler).Methods("POST") + router.HandleFunc("/projects/{project-name}", projHandler.getHandler).Methods("GET") + router.HandleFunc("/projects/{project-name}", projHandler.deleteHandler).Methods("DELETE") return router -} +}
\ No newline at end of file diff --git a/src/orchestrator/api/projecthandler.go b/src/orchestrator/api/projecthandler.go index 30f21de3..1830b91d 100644 --- a/src/orchestrator/api/projecthandler.go +++ b/src/orchestrator/api/projecthandler.go @@ -21,7 +21,7 @@ import ( "io" "net/http" - "github.com/onap/multicloud-k8s/src/orchestrator/internal/project" + moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module" "github.com/gorilla/mux" ) @@ -31,12 +31,12 @@ import ( type projectHandler struct { // Interface that implements Project operations // We will set this variable with a mock interface for testing - client project.ProjectManager + client moduleLib.ProjectManager } // Create handles creation of the Project entry in the database func (h projectHandler) createHandler(w http.ResponseWriter, r *http.Request) { - var p project.Project + var p moduleLib.Project err := json.NewDecoder(r.Body).Decode(&p) switch { @@ -54,7 +54,7 @@ func (h projectHandler) createHandler(w http.ResponseWriter, r *http.Request) { return } - ret, err := h.client.Create(p) + ret, err := h.client.CreateProject(p) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -75,7 +75,7 @@ func (h projectHandler) getHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name := vars["project-name"] - ret, err := h.client.Get(name) + ret, err := h.client.GetProject(name) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -95,7 +95,7 @@ func (h projectHandler) deleteHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name := vars["project-name"] - err := h.client.Delete(name) + err := h.client.DeleteProject(name) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return diff --git a/src/orchestrator/api/projecthandler_test.go b/src/orchestrator/api/projecthandler_test.go index 2699f2e3..41f515d0 100644 --- a/src/orchestrator/api/projecthandler_test.go +++ b/src/orchestrator/api/projecthandler_test.go @@ -25,7 +25,7 @@ import ( "reflect" "testing" - "github.com/onap/multicloud-k8s/src/orchestrator/internal/project" + moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module" pkgerrors "github.com/pkg/errors" ) @@ -36,27 +36,27 @@ import ( type mockProjectManager struct { // Items and err will be used to customize each test // via a localized instantiation of mockProjectManager - Items []project.Project + Items []moduleLib.Project Err error } -func (m *mockProjectManager) Create(inp project.Project) (project.Project, error) { +func (m *mockProjectManager) CreateProject(inp moduleLib.Project) (moduleLib.Project, error) { if m.Err != nil { - return project.Project{}, m.Err + return moduleLib.Project{}, m.Err } return m.Items[0], nil } -func (m *mockProjectManager) Get(name string) (project.Project, error) { +func (m *mockProjectManager) GetProject(name string) (moduleLib.Project, error) { if m.Err != nil { - return project.Project{}, m.Err + return moduleLib.Project{}, m.Err } return m.Items[0], nil } -func (m *mockProjectManager) Delete(name string) error { +func (m *mockProjectManager) DeleteProject(name string) error { return m.Err } @@ -64,7 +64,7 @@ func TestProjectCreateHandler(t *testing.T) { testCases := []struct { label string reader io.Reader - expected project.Project + expected moduleLib.Project expectedCode int projectClient *mockProjectManager }{ @@ -80,13 +80,13 @@ func TestProjectCreateHandler(t *testing.T) { "project-name":"testProject", "description":"Test Project used for unit testing" }`)), - expected: project.Project{ + expected: moduleLib.Project{ ProjectName: "testProject", Description: "Test Project used for unit testing", }, projectClient: &mockProjectManager{ //Items that will be returned by the mocked Client - Items: []project.Project{ + Items: []moduleLib.Project{ { ProjectName: "testProject", Description: "Test Project used for unit testing", @@ -106,7 +106,7 @@ func TestProjectCreateHandler(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.label, func(t *testing.T) { - request := httptest.NewRequest("POST", "/v2/project", testCase.reader) + request := httptest.NewRequest("POST", "/v2/projects", testCase.reader) resp := executeRequest(request, NewRouter(testCase.projectClient)) //Check returned code @@ -116,7 +116,7 @@ func TestProjectCreateHandler(t *testing.T) { //Check returned body only if statusCreated if resp.StatusCode == http.StatusCreated { - got := project.Project{} + got := moduleLib.Project{} json.NewDecoder(resp.Body).Decode(&got) if reflect.DeepEqual(testCase.expected, got) == false { @@ -132,7 +132,7 @@ func TestProjectGetHandler(t *testing.T) { testCases := []struct { label string - expected project.Project + expected moduleLib.Project name, version string expectedCode int projectClient *mockProjectManager @@ -140,13 +140,13 @@ func TestProjectGetHandler(t *testing.T) { { label: "Get Project", expectedCode: http.StatusOK, - expected: project.Project{ + expected: moduleLib.Project{ ProjectName: "testProject", Description: "A Test project for unit testing", }, name: "testProject", projectClient: &mockProjectManager{ - Items: []project.Project{ + Items: []moduleLib.Project{ { ProjectName: "testProject", Description: "A Test project for unit testing", @@ -159,7 +159,7 @@ func TestProjectGetHandler(t *testing.T) { expectedCode: http.StatusInternalServerError, name: "nonexistingproject", projectClient: &mockProjectManager{ - Items: []project.Project{}, + Items: []moduleLib.Project{}, Err: pkgerrors.New("Internal Error"), }, }, @@ -167,7 +167,7 @@ func TestProjectGetHandler(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.label, func(t *testing.T) { - request := httptest.NewRequest("GET", "/v2/project/"+testCase.name, nil) + request := httptest.NewRequest("GET", "/v2/projects/"+testCase.name, nil) resp := executeRequest(request, NewRouter(testCase.projectClient)) //Check returned code @@ -177,7 +177,7 @@ func TestProjectGetHandler(t *testing.T) { //Check returned body only if statusOK if resp.StatusCode == http.StatusOK { - got := project.Project{} + got := moduleLib.Project{} json.NewDecoder(resp.Body).Decode(&got) if reflect.DeepEqual(testCase.expected, got) == false { @@ -216,7 +216,7 @@ func TestProjectDeleteHandler(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.label, func(t *testing.T) { - request := httptest.NewRequest("DELETE", "/v2/project/"+testCase.name, nil) + request := httptest.NewRequest("DELETE", "/v2/projects/"+testCase.name, nil) resp := executeRequest(request, NewRouter(testCase.projectClient)) //Check returned code diff --git a/src/orchestrator/cmd/main.go b/src/orchestrator/cmd/main.go index 657d5bf5..f46fe910 100644 --- a/src/orchestrator/cmd/main.go +++ b/src/orchestrator/cmd/main.go @@ -23,10 +23,10 @@ import ( "time" "github.com/onap/multicloud-k8s/src/orchestrator/api" - "github.com/onap/multicloud-k8s/src/orchestrator/internal/auth" - "github.com/onap/multicloud-k8s/src/orchestrator/internal/config" - "github.com/onap/multicloud-k8s/src/orchestrator/internal/db" - + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/auth" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/config" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" + contextDb "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/contextdb" "github.com/gorilla/handlers" ) @@ -40,6 +40,12 @@ func main() { log.Println(err) log.Fatalln("Exiting...") } + err = contextDb.InitializeContextDatabase() + if err != nil { + log.Println("Unable to initialize database connection...") + log.Println(err) + log.Fatalln("Exiting...") + } httpRouter := api.NewRouter(nil) loggedRouter := handlers.LoggingHandler(os.Stdout, httpRouter) diff --git a/src/orchestrator/examples/example_module.go b/src/orchestrator/examples/example_module.go new file mode 100644 index 00000000..29ecdc23 --- /dev/null +++ b/src/orchestrator/examples/example_module.go @@ -0,0 +1,48 @@ +/* + * Copyright 2018 Intel Corporation, Inc + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test + +import ( + moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module" + "log" +) + +// ExampleClient_Project to test Project +func ExampleClient_Project() { + // Get handle to the client + c := moduleLib.NewClient() + // Check if project is initialized + if c.Project == nil { + log.Println("Project is Uninitialized") + return + } + // Perform operations on Project Module + _, err := c.Project.CreateProject(moduleLib.Project{ProjectName: "test"}) + if err != nil { + log.Println(err) + return + } + _, err = c.Project.GetProject("test") + if err != nil { + log.Println(err) + return + } + err = c.Project.DeleteProject("test") + if err != nil { + log.Println(err) + } +} diff --git a/src/orchestrator/go.sum b/src/orchestrator/go.sum index 732bc280..d2015406 100644 --- a/src/orchestrator/go.sum +++ b/src/orchestrator/go.sum @@ -171,6 +171,7 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onap/multicloud-k8s v0.0.0-20191115005109-f168ebb73d8d h1:3uFucXVv6gqa3H1u85CjoLOvGraREfD8/NL7m/9W9tc= +github.com/onap/multicloud-k8s v0.0.0-20200131010833-90e13d101cf0 h1:2qDo6s4pdg/g7Vj6QGrCK02EP4jjwVehgEObnAfipSM= github.com/onap/multicloud-k8s/src/k8splugin v0.0.0-20191115005109-f168ebb73d8d h1:ucIEjqzNVeFPnQofeuBfUqro0OnilX//fajEFxuLsgA= github.com/onap/multicloud-k8s/src/k8splugin v0.0.0-20191115005109-f168ebb73d8d/go.mod h1:EnQd/vQGZR1/55IihaHxiux4ZUig/zfXZux7bfmU0S8= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= diff --git a/src/orchestrator/internal/auth/auth.go b/src/orchestrator/pkg/infra/auth/auth.go index 3da8f2af..3da8f2af 100644 --- a/src/orchestrator/internal/auth/auth.go +++ b/src/orchestrator/pkg/infra/auth/auth.go diff --git a/src/orchestrator/internal/auth/auth_test.go b/src/orchestrator/pkg/infra/auth/auth_test.go index e41cb1ac..fdf81e6d 100644 --- a/src/orchestrator/internal/auth/auth_test.go +++ b/src/orchestrator/pkg/infra/auth/auth_test.go @@ -28,9 +28,9 @@ func TestGetTLSConfig(t *testing.T) { if err == nil { t.Errorf("Test failed, expected error but got none") } - tlsConfig, err := GetTLSConfig("../../tests/certs/auth_test_certificate.pem", - "../../tests/certs/auth_test_certificate.pem", - "../../tests/certs/auth_test_key.pem") + tlsConfig, err := GetTLSConfig("../../../tests/certs/auth_test_certificate.pem", + "../../../tests/certs/auth_test_certificate.pem", + "../../../tests/certs/auth_test_key.pem") if err != nil { t.Fatal("Test Failed as GetTLSConfig returned error: " + err.Error()) } diff --git a/src/orchestrator/internal/config/config.go b/src/orchestrator/pkg/infra/config/config.go index cb4656f0..df9cec92 100644 --- a/src/orchestrator/internal/config/config.go +++ b/src/orchestrator/pkg/infra/config/config.go @@ -83,9 +83,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: "orchestrator.io/rb-instance-id", } diff --git a/src/orchestrator/internal/config/config_test.go b/src/orchestrator/pkg/infra/config/config_test.go index ce7641ae..dce37e76 100644 --- a/src/orchestrator/internal/config/config_test.go +++ b/src/orchestrator/pkg/infra/config/config_test.go @@ -29,7 +29,7 @@ func TestReadConfigurationFile(t *testing.T) { }) t.Run("Read Configuration File", func(t *testing.T) { - conf, err := readConfigFile("../../tests/configs/mock_config.json") + conf, err := readConfigFile("../../../tests/configs/mock_config.json") if err != nil { t.Fatal("ReadConfigurationFile: Error reading file: ", err) } diff --git a/src/orchestrator/pkg/infra/contextdb/contextdb.go b/src/orchestrator/pkg/infra/contextdb/contextdb.go new file mode 100644 index 00000000..d18af227 --- /dev/null +++ b/src/orchestrator/pkg/infra/contextdb/contextdb.go @@ -0,0 +1,72 @@ +/* +Copyright 2020 Intel Corporation. +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 + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package contextdb + +import ( + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/config" + pkgerrors "github.com/pkg/errors" +) + +// Db interface used to talk a concrete Database connection +var Db ContextDb + +// ContextDb is an interface for accessing the context database +type ContextDb interface { + // Returns nil if db health is good + HealthCheck() error + // Puts Json Struct in db with key + Put(key string, value interface{}) error + // Delete k,v + Delete(key string) error + // Gets Json Struct from db + Get(key string, value interface{}) error + // Returns all keys with a prefix + GetAllKeys(path string) ([]string, error) +} + +// createContextDBClient creates the DB client +func createContextDBClient(dbType string) error { + var err error + switch dbType { + case "etcd": + c := EtcdConfig{ + Endpoint: config.GetConfiguration().EtcdIP, + CertFile: config.GetConfiguration().EtcdCert, + KeyFile: config.GetConfiguration().EtcdKey, + CAFile: config.GetConfiguration().EtcdCAFile, + } + Db, err = NewEtcdClient(nil, c) + if err != nil { + pkgerrors.Wrap(err, "Etcd Client Initialization failed with error") + } + default: + return pkgerrors.New(dbType + "DB not supported") + } + return err +} + +// InitializeContextDatabase sets up the connection to the +// configured database to allow the application to talk to it. +func InitializeContextDatabase() error { + // Only support Etcd for now + err := createContextDBClient("etcd") + if err != nil { + return pkgerrors.Cause(err) + } + err = Db.HealthCheck() + if err != nil { + return pkgerrors.Cause(err) + } + return nil +} diff --git a/src/orchestrator/pkg/infra/contextdb/etcd.go b/src/orchestrator/pkg/infra/contextdb/etcd.go new file mode 100644 index 00000000..a1922d3b --- /dev/null +++ b/src/orchestrator/pkg/infra/contextdb/etcd.go @@ -0,0 +1,175 @@ +/* + * Copyright 2020 Intel Corporation, Inc + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package contextdb + +import ( + "context" + "encoding/json" + pkgerrors "github.com/pkg/errors" + "go.etcd.io/etcd/clientv3" + "go.etcd.io/etcd/pkg/transport" + "time" +) + +// EtcdConfig Configuration values needed for Etcd Client +type EtcdConfig struct { + Endpoint string + CertFile string + KeyFile string + CAFile string +} + +// EtcdClient for Etcd +type EtcdClient struct { + cli *clientv3.Client + endpoint string +} + +// Etcd For Mocking purposes +type Etcd interface { + Put(ctx context.Context, key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error) + Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) + Delete(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.DeleteResponse, error) +} + +var getEtcd = func(e *EtcdClient) Etcd { + return e.cli +} + +// NewEtcdClient function initializes Etcd client +func NewEtcdClient(store *clientv3.Client, c EtcdConfig) (ContextDb, error) { + var endpoint string + if store == nil { + tlsInfo := transport.TLSInfo{ + CertFile: c.CertFile, + KeyFile: c.KeyFile, + CAFile: c.CAFile, + } + tlsConfig, err := tlsInfo.ClientConfig() + if err != nil { + return nil, pkgerrors.Errorf("Error creating etcd TLSInfo: %s", err.Error()) + } + // NOTE: Client relies on nil tlsConfig + // for non-secure connections, update the implicit variable + if len(c.CertFile) == 0 && len(c.KeyFile) == 0 && len(c.CAFile) == 0 { + tlsConfig = nil + } + endpoint = "" + if tlsConfig == nil { + endpoint = "http://" + c.Endpoint + ":2379" + } else { + endpoint = "https://" + c.Endpoint + ":2379" + } + + store, err = clientv3.New(clientv3.Config{ + Endpoints: []string{endpoint}, + DialTimeout: 5 * time.Second, + TLS: tlsConfig, + }) + if err != nil { + return nil, pkgerrors.Errorf("Error creating etcd client: %s", err.Error()) + } + } + + return &EtcdClient{ + cli: store, + endpoint: endpoint, + }, nil +} + +// Put values in Etcd DB +func (e *EtcdClient) Put(key string, value interface{}) error { + cli := getEtcd(e) + if cli == nil { + return pkgerrors.Errorf("Etcd Client not initialized") + } + if key == "" { + return pkgerrors.Errorf("Key is null") + } + if value == nil { + return pkgerrors.Errorf("Value is nil") + } + v, err := json.Marshal(value) + if err != nil { + return pkgerrors.Errorf("Json Marshal error: %s", err.Error()) + } + _, err = cli.Put(context.Background(), key, string(v)) + if err != nil { + return pkgerrors.Errorf("Error creating etcd entry: %s", err.Error()) + } + return nil +} + +// Get values from Etcd DB and decodes from json +func (e *EtcdClient) Get(key string, value interface{}) error { + cli := getEtcd(e) + if cli == nil { + return pkgerrors.Errorf("Etcd Client not initialized") + } + if key == "" { + return pkgerrors.Errorf("Key is null") + } + if value == nil { + return pkgerrors.Errorf("Value is nil") + } + getResp, err := cli.Get(context.Background(), key) + if err != nil { + return pkgerrors.Errorf("Error getting etcd entry: %s", err.Error()) + } + if getResp.Count == 0 { + return pkgerrors.Errorf("Key doesn't exist") + } + return json.Unmarshal(getResp.Kvs[0].Value, value) +} + +// GetAllKeys values from Etcd DB +func (e *EtcdClient) GetAllKeys(key string) ([]string, error) { + cli := getEtcd(e) + if cli == nil { + return nil, pkgerrors.Errorf("Etcd Client not initialized") + } + getResp, err := cli.Get(context.Background(), key, clientv3.WithPrefix()) + if err != nil { + return nil, pkgerrors.Errorf("Error getting etcd entry: %s", err.Error()) + } + if getResp.Count == 0 { + return nil, pkgerrors.Errorf("Key doesn't exist") + } + var keys []string + for _, ev := range getResp.Kvs { + keys = append(keys, string(ev.Key)) + } + return keys, nil +} + +// Delete values from Etcd DB +func (e *EtcdClient) Delete(key string) error { + cli := getEtcd(e) + if cli == nil { + return pkgerrors.Errorf("Etcd Client not initialized") + } + _, err := cli.Delete(context.Background(), key, clientv3.WithPrefix()) + if err != nil { + return pkgerrors.Errorf("Delete failed etcd entry: %s", err.Error()) + } + return nil +} + +// HealthCheck for checking health of the etcd cluster +func (e *EtcdClient) HealthCheck() error { + return nil +} diff --git a/src/orchestrator/pkg/infra/contextdb/etcd_test.go b/src/orchestrator/pkg/infra/contextdb/etcd_test.go new file mode 100644 index 00000000..17b7a5d5 --- /dev/null +++ b/src/orchestrator/pkg/infra/contextdb/etcd_test.go @@ -0,0 +1,276 @@ +/* +Copyright 2020 Intel Corporation. +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 + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package contextdb + +import ( + "context" + mvccpb "github.com/coreos/etcd/mvcc/mvccpb" + pkgerrors "github.com/pkg/errors" + "go.etcd.io/etcd/clientv3" + "strings" + "testing" +) + +type kv struct { + Key []byte + Value []byte +} + +// MockEtcdClient for mocking etcd +type MockEtcdClient struct { + Kvs []*mvccpb.KeyValue + Count int64 + Err error +} + +// Mocking only Single Value +// Put function +func (e *MockEtcdClient) Put(ctx context.Context, key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error) { + var m mvccpb.KeyValue + m.Key = []byte(key) + m.Value = []byte(val) + e.Count = e.Count + 1 + e.Kvs = append(e.Kvs, &m) + return &clientv3.PutResponse{}, e.Err +} + +// Get function +func (e *MockEtcdClient) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) { + var g clientv3.GetResponse + g.Kvs = e.Kvs + g.Count = e.Count + return &g, e.Err +} + +// Delete function +func (e *MockEtcdClient) Delete(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.DeleteResponse, error) { + return &clientv3.DeleteResponse{}, e.Err +} + +type testStruct struct { + Name string `json:"name"` + Num int `json:"num"` +} + +// TestPut test Put +func TestPut(t *testing.T) { + testCases := []struct { + label string + mockEtcd *MockEtcdClient + expectedError string + key string + value *testStruct + }{ + { + label: "Success Case", + mockEtcd: &MockEtcdClient{}, + key: "test1", + value: &testStruct{Name: "test", Num: 5}, + }, + { + label: "Key is null", + mockEtcd: &MockEtcdClient{}, + key: "", + expectedError: "Key is null", + }, + { + label: "Value is nil", + mockEtcd: &MockEtcdClient{}, + key: "test1", + value: nil, + expectedError: "Value is nil", + }, + { + label: "Error creating etcd entry", + mockEtcd: &MockEtcdClient{Err: pkgerrors.New("DB Error")}, + key: "test1", + value: &testStruct{Name: "test", Num: 5}, + expectedError: "Error creating etcd entry: DB Error", + }, + } + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + cli, _ := NewEtcdClient(&clientv3.Client{}, EtcdConfig{}) + getEtcd = func(e *EtcdClient) Etcd { + return testCase.mockEtcd + } + err := cli.Put(testCase.key, testCase.value) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("Method returned an un-expected (%s)", err) + } + if !strings.Contains(string(err.Error()), testCase.expectedError) { + t.Fatalf("Method returned an error (%s)", err) + } + } + + }) + } +} + +func TestGet(t *testing.T) { + testCases := []struct { + label string + mockEtcd *MockEtcdClient + expectedError string + key string + value *testStruct + }{ + { + label: "Key is null", + mockEtcd: &MockEtcdClient{}, + key: "", + value: nil, + expectedError: "Key is null", + }, + { + label: "Key doesn't exist", + mockEtcd: &MockEtcdClient{}, + key: "test1", + value: &testStruct{}, + expectedError: "Key doesn't exist", + }, + { + label: "Error getting etcd entry", + mockEtcd: &MockEtcdClient{Err: pkgerrors.New("DB Error")}, + key: "test1", + value: &testStruct{}, + expectedError: "Error getting etcd entry: DB Error", + }, + } + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + cli, _ := NewEtcdClient(&clientv3.Client{}, EtcdConfig{}) + getEtcd = func(e *EtcdClient) Etcd { + return testCase.mockEtcd + } + err := cli.Get(testCase.key, testCase.value) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("Method returned an un-expected (%s)", err) + } + if !strings.Contains(string(err.Error()), testCase.expectedError) { + t.Fatalf("Method returned an error (%s)", err) + } + } + + }) + } +} + +func TestGetString(t *testing.T) { + testCases := []struct { + label string + mockEtcd *MockEtcdClient + expectedError string + value string + }{ + { + label: "Success Case", + mockEtcd: &MockEtcdClient{}, + }, + } + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + cli, _ := NewEtcdClient(&clientv3.Client{}, EtcdConfig{}) + getEtcd = func(e *EtcdClient) Etcd { + return testCase.mockEtcd + } + err := cli.Put("test", "test1") + if err != nil { + t.Error("Test failed", err) + } + var s string + err = cli.Get("test", &s) + if err != nil { + t.Error("Test failed", err) + } + if "test1" != s { + t.Error("Get Failed") + } + }) + } +} + +func TestDelete(t *testing.T) { + testCases := []struct { + label string + mockEtcd *MockEtcdClient + expectedError string + }{ + { + label: "Success Case", + mockEtcd: &MockEtcdClient{}, + }, + { + label: "Delete failed etcd entry", + mockEtcd: &MockEtcdClient{Err: pkgerrors.New("DB Error")}, + expectedError: "Delete failed etcd entry: DB Error", + }, + } + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + cli, _ := NewEtcdClient(&clientv3.Client{}, EtcdConfig{}) + getEtcd = func(e *EtcdClient) Etcd { + return testCase.mockEtcd + } + err := cli.Delete("test") + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("Method returned an un-expected (%s)", err) + } + if !strings.Contains(string(err.Error()), testCase.expectedError) { + t.Fatalf("Method returned an error (%s)", err) + } + } + + }) + } +} + +func TestGetAll(t *testing.T) { + testCases := []struct { + label string + mockEtcd *MockEtcdClient + expectedError string + }{ + { + label: "Key doesn't exist", + mockEtcd: &MockEtcdClient{}, + expectedError: "Key doesn't exist", + }, + { + label: "Error getting etcd entry", + mockEtcd: &MockEtcdClient{Err: pkgerrors.New("DB Error")}, + expectedError: "Error getting etcd entry: DB Error", + }, + } + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + cli, _ := NewEtcdClient(&clientv3.Client{}, EtcdConfig{}) + getEtcd = func(e *EtcdClient) Etcd { + return testCase.mockEtcd + } + _, err := cli.GetAllKeys("test") + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("Method returned an un-expected (%s)", err) + } + if !strings.Contains(string(err.Error()), testCase.expectedError) { + t.Fatalf("Method returned an error (%s)", err) + } + } + }) + } +} diff --git a/src/orchestrator/pkg/infra/contextdb/mock.go b/src/orchestrator/pkg/infra/contextdb/mock.go new file mode 100644 index 00000000..fc0f8ff7 --- /dev/null +++ b/src/orchestrator/pkg/infra/contextdb/mock.go @@ -0,0 +1,58 @@ +/* +Copyright 2020 Intel Corporation. +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 + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package contextdb + +import ( + pkgerrors "github.com/pkg/errors" +) + +type MockEtcd struct { + Items map[string]interface{} + Err error +} + +func (c *MockEtcd) Put(key string, value interface{}) error { + if c.Items == nil { + c.Items = make(map[string]interface{}) + } + c.Items[key] = value + return c.Err +} + +func (c *MockEtcd) Get(key string, value interface{}) error { + for kvKey, kvValue := range c.Items { + if kvKey == key { + value = kvValue + return nil + } + } + return pkgerrors.Errorf("Key doesn't exist") +} + +func (c *MockEtcd) Delete(key string) error { + delete(c.Items, key) + return c.Err +} + +func (c *MockEtcd) GetAllKeys(path string) ([]string, error) { + var keys []string + for k, _ := range c.Items { + keys = append(keys, string(k)) + } + return keys, nil +} + +func (e *MockEtcd) HealthCheck() error { + return nil +} diff --git a/src/orchestrator/internal/db/README.md b/src/orchestrator/pkg/infra/db/README.md index cba1b7ea..cba1b7ea 100644 --- a/src/orchestrator/internal/db/README.md +++ b/src/orchestrator/pkg/infra/db/README.md diff --git a/src/orchestrator/internal/db/mock.go b/src/orchestrator/pkg/infra/db/mock.go index 1dbca4b4..1dbca4b4 100644 --- a/src/orchestrator/internal/db/mock.go +++ b/src/orchestrator/pkg/infra/db/mock.go diff --git a/src/orchestrator/internal/db/mongo.go b/src/orchestrator/pkg/infra/db/mongo.go index 3720a4f2..32d0b549 100644 --- a/src/orchestrator/internal/db/mongo.go +++ b/src/orchestrator/pkg/infra/db/mongo.go @@ -21,7 +21,7 @@ import ( "golang.org/x/net/context" - "github.com/onap/multicloud-k8s/src/orchestrator/internal/config" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/config" pkgerrors "github.com/pkg/errors" "go.mongodb.org/mongo-driver/bson" diff --git a/src/orchestrator/internal/db/mongo_test.go b/src/orchestrator/pkg/infra/db/mongo_test.go index 171c908f..171c908f 100644 --- a/src/orchestrator/internal/db/mongo_test.go +++ b/src/orchestrator/pkg/infra/db/mongo_test.go diff --git a/src/orchestrator/internal/db/store.go b/src/orchestrator/pkg/infra/db/store.go index ed394205..1a9632e7 100644 --- a/src/orchestrator/internal/db/store.go +++ b/src/orchestrator/pkg/infra/db/store.go @@ -17,7 +17,7 @@ import ( "encoding/json" "reflect" - "github.com/onap/multicloud-k8s/src/orchestrator/internal/config" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/config" pkgerrors "github.com/pkg/errors" ) diff --git a/src/orchestrator/internal/db/store_test.go b/src/orchestrator/pkg/infra/db/store_test.go index 42a41787..42a41787 100644 --- a/src/orchestrator/internal/db/store_test.go +++ b/src/orchestrator/pkg/infra/db/store_test.go diff --git a/src/orchestrator/internal/logutils/logger.go b/src/orchestrator/pkg/infra/logutils/logger.go index 2e8f9969..2e8f9969 100644 --- a/src/orchestrator/internal/logutils/logger.go +++ b/src/orchestrator/pkg/infra/logutils/logger.go diff --git a/src/orchestrator/pkg/module/module.go b/src/orchestrator/pkg/module/module.go new file mode 100644 index 00000000..e4482098 --- /dev/null +++ b/src/orchestrator/pkg/module/module.go @@ -0,0 +1,34 @@ +/* + * Copyright 2018 Intel Corporation, Inc + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package module + +import ( + ) + +// Client for using the services in the orchestrator +type Client struct { + Project *ProjectClient + // Add Clients for API's here +} + +// NewClient creates a new client for using the services +func NewClient() *Client { + c:= &Client{} + c.Project = NewProjectClient() + // Add Client API handlers here + return c +}
\ No newline at end of file diff --git a/src/orchestrator/internal/project/project.go b/src/orchestrator/pkg/module/project.go index f0c50065..e44164f9 100644 --- a/src/orchestrator/internal/project/project.go +++ b/src/orchestrator/pkg/module/project.go @@ -14,12 +14,12 @@ * limitations under the License. */ -package project +package module import ( "encoding/json" - "github.com/onap/multicloud-k8s/src/orchestrator/internal/db" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" pkgerrors "github.com/pkg/errors" ) @@ -47,14 +47,14 @@ func (pk ProjectKey) String() string { return string(out) } -// ProjectManager is an interface exposes the Project functionality +// Manager is an interface exposes the Project functionality type ProjectManager interface { - Create(pr Project) (Project, error) - Get(name string) (Project, error) - Delete(name string) error + CreateProject(pr Project) (Project, error) + GetProject(name string) (Project, error) + DeleteProject(name string) error } -// ProjectClient implements the ProjectManager +// ProjectClient implements the Manager // It will also be used to maintain some localized state type ProjectClient struct { storeName string @@ -62,15 +62,15 @@ type ProjectClient struct { } // NewProjectClient returns an instance of the ProjectClient -// which implements the ProjectManager +// which implements the Manager func NewProjectClient() *ProjectClient { return &ProjectClient{ tagMeta: "projectmetadata", } } -// Create a new collection based on the project -func (v *ProjectClient) Create(p Project) (Project, error) { +// CreateProject a new collection based on the project +func (v *ProjectClient) CreateProject(p Project) (Project, error) { //Construct the composite key to select the entry key := ProjectKey{ @@ -78,7 +78,7 @@ func (v *ProjectClient) Create(p Project) (Project, error) { } //Check if this Project already exists - _, err := v.Get(p.ProjectName) + _, err := v.GetProject(p.ProjectName) if err == nil { return Project{}, pkgerrors.New("Project already exists") } @@ -91,8 +91,8 @@ func (v *ProjectClient) Create(p Project) (Project, error) { return p, nil } -// Get returns the Project for corresponding name -func (v *ProjectClient) Get(name string) (Project, error) { +// GetProject returns the Project for corresponding name +func (v *ProjectClient) GetProject(name string) (Project, error) { //Construct the composite key to select the entry key := ProjectKey{ @@ -116,8 +116,8 @@ func (v *ProjectClient) Get(name string) (Project, error) { return Project{}, pkgerrors.New("Error getting Project") } -// Delete the Project from database -func (v *ProjectClient) Delete(name string) error { +// DeleteProject the Project from database +func (v *ProjectClient) DeleteProject(name string) error { //Construct the composite key to select the entry key := ProjectKey{ diff --git a/src/orchestrator/internal/project/project_test.go b/src/orchestrator/pkg/module/project_test.go index cc691e33..7f4d9b3e 100644 --- a/src/orchestrator/internal/project/project_test.go +++ b/src/orchestrator/pkg/module/project_test.go @@ -14,14 +14,14 @@ * limitations under the License. */ -package project +package module import ( "reflect" "strings" "testing" - "github.com/onap/multicloud-k8s/src/orchestrator/internal/db" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/db" pkgerrors "github.com/pkg/errors" ) @@ -60,7 +60,7 @@ func TestCreateProject(t *testing.T) { t.Run(testCase.label, func(t *testing.T) { db.DBconn = testCase.mockdb impl := NewProjectClient() - got, err := impl.Create(testCase.inp) + got, err := impl.CreateProject(testCase.inp) if err != nil { if testCase.expectedError == "" { t.Fatalf("Create returned an unexpected error %s", err) @@ -119,7 +119,7 @@ func TestGetProject(t *testing.T) { t.Run(testCase.label, func(t *testing.T) { db.DBconn = testCase.mockdb impl := NewProjectClient() - got, err := impl.Get(testCase.name) + got, err := impl.GetProject(testCase.name) if err != nil { if testCase.expectedError == "" { t.Fatalf("Get returned an unexpected error: %s", err) @@ -163,7 +163,7 @@ func TestDeleteProject(t *testing.T) { t.Run(testCase.label, func(t *testing.T) { db.DBconn = testCase.mockdb impl := NewProjectClient() - err := impl.Delete(testCase.name) + err := impl.DeleteProject(testCase.name) if err != nil { if testCase.expectedError == "" { t.Fatalf("Delete returned an unexpected error %s", err) |