summaryrefslogtreecommitdiffstats
path: root/src/dkv
diff options
context:
space:
mode:
authorShashank Kumar Shankar <shashank.kumar.shankar@intel.com>2018-02-28 17:54:59 -0800
committerShashank Kumar Shankar <shashank.kumar.shankar@intel.com>2018-03-02 16:52:39 -0800
commit67dd59385cf983ef1307e3b3e410a8f773d8a5c3 (patch)
treee53c1997f643a0826fb9cd5a8b44ccc6466dc392 /src/dkv
parentff8cba5a49e85fbb1d2e14f0fa0bcb5bf92caf34 (diff)
Add feature to hold configs in filesystem
This patch adds feature to hold config files on filesystem and adds all unit tests to have enough coverage for milestones. Change-Id: Icd6f3dc93e0f419500f82f0a6ccd62e500dfc918 Issue-ID: MUSIC-42 Signed-off-by: Shashank Kumar Shankar <shashank.kumar.shankar@intel.com>
Diffstat (limited to 'src/dkv')
-rw-r--r--src/dkv/Gopkg.lock8
-rw-r--r--src/dkv/api/backendConsulConnection.go (renamed from src/dkv/api/consulConnection.go)3
-rw-r--r--src/dkv/api/backendFilesystemConnection.go200
-rw-r--r--src/dkv/api/backendPropertiesConnection.go146
-rw-r--r--src/dkv/api/backendfakes.go162
-rw-r--r--src/dkv/api/configHandlers.go177
-rw-r--r--src/dkv/api/configHandlers_test.go234
-rw-r--r--src/dkv/api/consulConnection_test.go17
-rw-r--r--src/dkv/api/endpointViews_fake.go96
-rw-r--r--src/dkv/api/endpointViews_test.go214
-rw-r--r--src/dkv/api/initialise.go10
-rw-r--r--src/dkv/api/propertiesReader.go114
-rw-r--r--src/dkv/api/propertiesReader_test.go19
-rw-r--r--src/dkv/api/queryConsulHandlers.go (renamed from src/dkv/api/endpointViews.go)74
-rw-r--r--src/dkv/api/queryConsulHandlers_test.go105
-rw-r--r--src/dkv/api/registrationHandlers.go173
-rw-r--r--src/dkv/api/registrationHandlers_test.go276
-rw-r--r--src/dkv/api/token_service_map.json1
-rw-r--r--src/dkv/api/utils.go153
-rw-r--r--src/dkv/api/utils_test.go306
-rw-r--r--src/dkv/configurations/sampleAAIConfig.properties94
-rw-r--r--src/dkv/configurations/sampleAPPCConfig.properties113
-rw-r--r--src/dkv/main.go27
23 files changed, 1968 insertions, 754 deletions
diff --git a/src/dkv/Gopkg.lock b/src/dkv/Gopkg.lock
index 535922e..687a4d3 100644
--- a/src/dkv/Gopkg.lock
+++ b/src/dkv/Gopkg.lock
@@ -44,6 +44,12 @@
revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00"
[[projects]]
+ branch = "master"
+ name = "github.com/hashicorp/go-uuid"
+ packages = ["."]
+ revision = "27454136f0364f2d44b1276c552d69105cf8c498"
+
+[[projects]]
name = "github.com/hashicorp/serf"
packages = ["coordinate"]
revision = "d6574a5bb1226678d7010325fb6c985db20ee458"
@@ -76,6 +82,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
- inputs-digest = "000a855a3e5fca6c1f7b134951013777f7decbbde7be20af1e262da6005e874a"
+ inputs-digest = "ec895bb8f93ec798d68a3d83b9004eff00dc4078f5a2bccf75390bcb8fccbd72"
solver-name = "gps-cdcl"
solver-version = 1
diff --git a/src/dkv/api/consulConnection.go b/src/dkv/api/backendConsulConnection.go
index 5ea79fd..9c2f8d6 100644
--- a/src/dkv/api/consulConnection.go
+++ b/src/dkv/api/backendConsulConnection.go
@@ -49,6 +49,9 @@ the initialise.go file where we are creating a ConsulStruct and assigning it to
above.
*/
func (c *ConsulStruct) InitializeConsulClient() error {
+ if os.Getenv("CONSUL_IP") == "" {
+ return errors.New("CONSUL_IP environment variable not set.")
+ }
config := consulapi.DefaultConfig()
config.Address = os.Getenv("CONSUL_IP") + ":8500"
diff --git a/src/dkv/api/backendFilesystemConnection.go b/src/dkv/api/backendFilesystemConnection.go
new file mode 100644
index 0000000..f09e74f
--- /dev/null
+++ b/src/dkv/api/backendFilesystemConnection.go
@@ -0,0 +1,200 @@
+/*
+ * 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 api
+
+import (
+ "errors"
+ uuid "github.com/hashicorp/go-uuid"
+ "net/http"
+ "os"
+)
+
+type DirectoryOperationer interface {
+ // Service Operations.
+ CreateService(CreateRegisterServiceBody) (string, error)
+ RemoveService(string) error
+ CreateServiceSubdomain(string, string) error
+ RemoveServiceSubdomain(string, string) error
+ // Directory Operations.
+ CreateDirectory(string) error
+ RemoveDirectory(string) error
+ RemoveSubDirectory(string, string) error
+ RemoveFile(string, string, string) error
+ FindService(string) (string, bool, error)
+ FetchFile(http.ResponseWriter, *http.Request, string, string, string)
+ CreateFile(string) (*os.File, error)
+}
+
+type DirectoryStruct struct {
+ directory string
+}
+
+const (
+ MOUNTPATH = "../../mountpath/"
+ JSONPATH = "api/token_service_map.json"
+)
+
+var Directory DirectoryOperationer
+
+func (d *DirectoryStruct) CreateService(body CreateRegisterServiceBody) (string, error) {
+
+ // Having same name is prohibited?
+ found, err := FindServiceInJSON(JSONPATH, body.Domain)
+ if err != nil {
+ return "", err
+ }
+ if found {
+ return "", errors.New("Service already found. Check name.")
+ }
+
+ token, err := uuid.GenerateUUID()
+ if err != nil {
+ return "", err
+ }
+
+ err = d.CreateDirectory(token)
+ if err != nil {
+ return "", err
+ }
+
+ err = WriteJSON(JSONPATH, token, body.Domain)
+
+ if err != nil {
+ return "", err
+ }
+ return token, nil
+}
+
+func (d *DirectoryStruct) CreateServiceSubdomain(token string, subdomain string) error {
+ foundToken, err := FindTokenInJSON(JSONPATH, token)
+ if err != nil {
+ return err
+ }
+ if foundToken == false {
+ return errors.New("Token not found. Please check token or if service is created.")
+ }
+ err = d.CreateSubDirectory(token, subdomain)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (d *DirectoryStruct) RemoveService(token string) error {
+ err := DeleteInJSON(JSONPATH, token)
+ if err != nil {
+ return err
+ }
+ err = d.RemoveDirectory(token)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (d *DirectoryStruct) FindService(token string) (string, bool, error) {
+ service, found, err := GetServicebyToken(JSONPATH, token)
+ if err != nil {
+ return "", false, err
+ }
+ return service, found, nil
+}
+
+func (d *DirectoryStruct) RemoveServiceSubdomain(token string, subdomain string) error {
+ foundToken, err := FindTokenInJSON(JSONPATH, token)
+ if err != nil {
+ return err
+ }
+ if foundToken == false {
+ return errors.New("Token not found. Please check token or if service is created.")
+ }
+ err = d.RemoveSubDirectory(token, subdomain)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (d *DirectoryStruct) CreateDirectory(token string) error {
+ // Permissions inside mount point?
+ err := os.Mkdir(MOUNTPATH+token, os.FileMode(0770))
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (d *DirectoryStruct) CreateSubDirectory(token string, subdomain string) error {
+ err := os.Mkdir(MOUNTPATH+token+"/"+subdomain, os.FileMode(0770))
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (d *DirectoryStruct) RemoveDirectory(token string) error {
+ err := os.RemoveAll(MOUNTPATH + token)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (d *DirectoryStruct) RemoveSubDirectory(token string, subdomain string) error {
+ err := os.RemoveAll(MOUNTPATH + token + "/" + subdomain)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (d *DirectoryStruct) RemoveFile(token string, subdomain string, filename string) error {
+ var filepath = ""
+ if subdomain != "" {
+ filepath += MOUNTPATH + token + "/" + subdomain + "/" + filename
+ } else {
+ filepath += MOUNTPATH + token + "/" + filename
+ }
+ // If error, it seems to show the mounthpath back to the client. This is not good
+ // error return practise. It shoudn't return the exact file path on the system.
+ err := os.Remove(filepath)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (d *DirectoryStruct) FetchFile(
+ w http.ResponseWriter, r *http.Request, token string, subdomain string, filename string) {
+
+ var filepath = ""
+ if subdomain != "" {
+ filepath += MOUNTPATH + token + "/" + subdomain + "/" + filename
+ } else {
+ filepath += MOUNTPATH + token + "/" + filename
+ }
+
+ http.ServeFile(w, r, filepath)
+}
+
+func (d *DirectoryStruct) CreateFile(filepath string) (*os.File, error) {
+ f, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY, 0770)
+ if err != nil {
+ return nil, err
+ }
+ return f, nil
+}
diff --git a/src/dkv/api/backendPropertiesConnection.go b/src/dkv/api/backendPropertiesConnection.go
new file mode 100644
index 0000000..24140cd
--- /dev/null
+++ b/src/dkv/api/backendPropertiesConnection.go
@@ -0,0 +1,146 @@
+/*
+ * 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 api
+
+import (
+ "errors"
+ "github.com/magiconair/properties"
+ "io/ioutil"
+ "log"
+ "os"
+ "sync"
+)
+
+type KeyValuesInterface interface {
+ WriteKVsToConsul(string, string) error
+ ConfigReader(string, string, string) error
+ ReadMultiplePropertiesRecursive(string) error
+ ReadMultipleProperties(string) error
+ ReadProperty(string) error
+}
+
+type KeyValuesStruct struct {
+ sync.RWMutex
+ kvs map[string]string
+}
+
+var KeyValues KeyValuesInterface
+
+func (kvStruct *KeyValuesStruct) WriteKVsToConsul(token string, subdomain string) error {
+ var prefix = ""
+ if subdomain != "" {
+ prefix += token + "/" + subdomain
+ } else {
+ prefix += token + "/"
+ }
+ for key, value := range kvStruct.kvs {
+ key = prefix + key
+ err := Consul.RequestPUT(key, value)
+ if err != nil {
+ return err
+ }
+ log.Println("[INFO] Key: ", key, "| Value: ", value)
+ }
+ log.Println("[INFO] Wrote KVs to Consul.")
+ return nil
+}
+
+func (kvStruct *KeyValuesStruct) ConfigReader(token string, subdomain string, filename string) error {
+ defer kvStruct.Unlock()
+
+ kvStruct.Lock()
+ var filepath = MOUNTPATH
+
+ if filename != "" && subdomain != "" {
+ // Specific file in specific domain.
+ filepath += token + "/" + subdomain + "/" + filename
+ err := kvStruct.ReadProperty(filepath)
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+
+ if filename != "" && subdomain == "" {
+ // Specific file in Token
+ filepath += token + "/" + filename
+ err := kvStruct.ReadProperty(filepath)
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+
+ if filename == "" && subdomain != "" {
+ // All files in specific domain
+ filepath += token + "/" + subdomain
+ err := kvStruct.ReadMultipleProperties(filepath)
+ if err != nil {
+ return err
+ }
+ }
+
+ filepath += token
+ err := kvStruct.ReadMultiplePropertiesRecursive(filepath)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (kvStruct *KeyValuesStruct) ReadMultiplePropertiesRecursive(path string) error {
+ // Go inside each sub directory and run ReadMultipleProperties inside.
+ files, err := ioutil.ReadDir(path)
+ if err != nil {
+ return err
+ }
+
+ for _, f := range files {
+ fi, _ := os.Stat(path + "/" + f.Name())
+ if fi.Mode().IsDir() {
+ kvStruct.ReadMultipleProperties(path + "/" + f.Name())
+ } else {
+ kvStruct.ReadProperty(path + "/" + f.Name())
+ }
+ }
+ return nil
+}
+
+func (kvStruct *KeyValuesStruct) ReadMultipleProperties(path string) error {
+ files, err := ioutil.ReadDir(path)
+ if err != nil {
+ return err
+ }
+
+ for _, f := range files {
+ kvStruct.ReadProperty(path + f.Name())
+ }
+
+ return nil
+}
+
+func (kvStruct *KeyValuesStruct) ReadProperty(path string) error {
+ _, err := os.Stat(path)
+ if err != nil {
+ return errors.New("File does not exists.")
+ }
+ p := properties.MustLoadFile(path, properties.UTF8)
+ for _, key := range p.Keys() {
+ kvStruct.kvs[key] = p.MustGet(key)
+ }
+ return nil
+}
diff --git a/src/dkv/api/backendfakes.go b/src/dkv/api/backendfakes.go
new file mode 100644
index 0000000..5415608
--- /dev/null
+++ b/src/dkv/api/backendfakes.go
@@ -0,0 +1,162 @@
+/*
+ * 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 api
+
+import (
+ "errors"
+ "net/http"
+)
+
+/*
+A ConsulStruct is added inside this so that FakeConsul becomes an implementation of the Consul interface.
+If we don't add ConsulStruct inside this, it complains that the FakeConsul Struct doesn't implement all the methods
+defined in Consul interface.
+*/
+// Correct
+type FakeConsul struct {
+ ConsulStruct
+}
+
+func (f *FakeConsul) RequestGETS() ([]string, error) {
+ return []string{"key1", "key2"}, nil
+}
+
+func (f *FakeConsul) RequestGET(key string) (string, error) {
+ return key, nil
+}
+
+func (f *FakeConsul) RequestPUT(key string, value string) error {
+ return nil
+}
+
+func (f *FakeConsul) RequestDELETE(key string) error {
+ return nil
+}
+
+// Error
+type FakeConsulErr struct {
+ ConsulStruct
+}
+
+func (f *FakeConsulErr) RequestGETS() ([]string, error) {
+ return []string{"", ""}, errors.New("Internal Server Error")
+}
+
+func (f *FakeConsulErr) RequestGET(key string) (string, error) {
+ return "", errors.New("Internal Server Error")
+}
+
+func (f *FakeConsulErr) RequestDELETE(key string) error {
+ return errors.New("Internal Server Error")
+}
+
+/*
+This is done similar to the fake Consul above to pass FakeKeyValues to the interface and control method's outputs
+as required.
+*/
+//Correct
+type FakeKeyValues struct {
+ KeyValuesStruct
+}
+
+func (f *FakeKeyValues) ConfigReader(token string, subdomain string, filename string) error {
+ return nil
+}
+
+func (f *FakeKeyValues) WriteKVsToConsul(token string, subdomain string) error {
+ return nil
+}
+
+// Error
+type FakeKeyValuesErr struct {
+ KeyValuesStruct
+}
+
+func (f *FakeKeyValuesErr) ConfigReader(token string, subdomain string, filename string) error {
+ return errors.New("Internal Server Error")
+}
+
+func (f *FakeKeyValuesErr) WriteKVsToConsul(token string, subdomain string) error {
+ return errors.New("Internal Server Error")
+}
+
+// Correct
+type FakeDirectory struct {
+ DirectoryStruct
+}
+
+func (f *FakeDirectory) CreateService(CreateRegisterServiceBody) (string, error) {
+ return "", nil
+}
+
+func (f *FakeDirectory) RemoveService(token string) error {
+ return nil
+}
+
+func (f *FakeDirectory) CreateServiceSubdomain(token string, subdomain string) error {
+ return nil
+}
+
+func (f *FakeDirectory) RemoveServiceSubdomain(token string, subdomain string) error {
+ return nil
+}
+
+func (f *FakeDirectory) FindService(token string) (string, bool, error) {
+ return "service1", true, nil
+}
+
+func (f *FakeDirectory) FetchFile(
+ w http.ResponseWriter, r *http.Request, token string, subdomain string, filename string) {
+}
+
+func (f *FakeDirectory) RemoveFile(token string, subdomain string, filename string) error {
+ return nil
+}
+
+// Error
+type FakeDirectoryErr struct {
+ DirectoryStruct
+}
+
+func (f *FakeDirectoryErr) CreateService(CreateRegisterServiceBody) (string, error) {
+ return "", errors.New("Internal Server Error.")
+}
+
+func (f *FakeDirectoryErr) RemoveService(token string) error {
+ return errors.New("Internal Server Error.")
+}
+
+func (f *FakeDirectoryErr) CreateServiceSubdomain(token string, subdomain string) error {
+ return errors.New("Internal Server Error.")
+}
+
+func (f *FakeDirectoryErr) RemoveServiceSubdomain(token string, subdomain string) error {
+ return errors.New("Internal Server Error.")
+}
+
+func (f *FakeDirectoryErr) FindService(token string) (string, bool, error) {
+ return "", false, errors.New("Internal Server Error.")
+}
+
+func (f *FakeDirectoryErr) FetchFile(
+ w http.ResponseWriter, r *http.Request, token string, subdomain string, filename string) {
+
+}
+
+func (f *FakeDirectoryErr) RemoveFile(token string, subdomain string, filename string) error {
+ return errors.New("Internal Server Error.")
+}
diff --git a/src/dkv/api/configHandlers.go b/src/dkv/api/configHandlers.go
new file mode 100644
index 0000000..f5bac34
--- /dev/null
+++ b/src/dkv/api/configHandlers.go
@@ -0,0 +1,177 @@
+/*
+ * 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 api
+
+import (
+ "encoding/json"
+ "errors"
+ "github.com/gorilla/mux"
+ "io"
+ "mime/multipart"
+ "net/http"
+)
+
+type UploadConfigBody struct {
+ Token string
+ File multipart.File
+ Subdomain string
+}
+
+type LoadConfigBody struct {
+ Token string `json:"token"`
+ Filename string `json:"filename"`
+ Subdomain string `json:"subdomain"`
+}
+
+func ValidateLoadConfigBody(body LoadConfigBody) error {
+ if body.Token == "" {
+ return errors.New("Token not set. Please set Token in POST.")
+ }
+ return nil
+}
+
+func HandleConfigUpload(w http.ResponseWriter, r *http.Request) {
+ r.ParseMultipartForm(100000) // 2k bytes?
+ file, handler, err := r.FormFile("configFile")
+ if err != nil {
+ GenerateResponse(w, r, http.StatusInternalServerError, "Error in uploaded file.")
+ return
+ }
+ defer file.Close()
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+ return
+ }
+
+ token := r.Form.Get("token")
+ subdomain := r.Form.Get("subdomain")
+
+ if token == "" {
+ GenerateResponse(w, r, http.StatusBadRequest, "Token not present in Form data.")
+ return
+ }
+
+ var filename = ""
+ if subdomain != "" {
+ filename += token + "/" + subdomain + "/" + handler.Filename
+ } else {
+ filename += token + "/" + handler.Filename
+ }
+
+ f, err := Directory.CreateFile(MOUNTPATH + filename)
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+ return
+ }
+ defer f.Close()
+ io.Copy(f, file)
+}
+
+func HandleConfigLoad(w http.ResponseWriter, r *http.Request) {
+
+ var body LoadConfigBody
+
+ decoder := json.NewDecoder(r.Body)
+ err := decoder.Decode(&body)
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusBadRequest, "Empty body.")
+ return
+ }
+
+ err = ValidateLoadConfigBody(body)
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusBadRequest, string(err.Error()))
+ return
+ }
+
+ err = KeyValues.ConfigReader(body.Token, body.Subdomain, body.Filename)
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+ return
+ }
+
+ err = KeyValues.WriteKVsToConsul(body.Token, body.Subdomain)
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+ } else {
+ GenerateResponse(w, r, http.StatusOK, "Configuration read and Key Values loaded to Consul.")
+ }
+}
+
+func HandleDefaultConfigLoad(w http.ResponseWriter, r *http.Request) {
+ err := KeyValues.ConfigReader("default", "", "")
+ if err != nil {
+ GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+ return
+ }
+ err = KeyValues.WriteKVsToConsul("default", "")
+ if err != nil {
+ GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+ } else {
+ GenerateResponse(w, r, http.StatusOK, "Default Configuration read and default Key Values loaded to Consul.")
+ }
+}
+
+func HandleConfigGet(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ token := vars["token"]
+ filename := vars["filename"]
+ subdomain := vars["subdomain"]
+
+ if token == "" {
+ GenerateResponse(w, r, http.StatusBadRequest, "Token not passed.")
+ return
+ }
+
+ if filename == "" {
+ GenerateResponse(w, r, http.StatusBadRequest, "filename not passed.")
+ return
+ }
+
+ Directory.FetchFile(w, r, token, subdomain, filename)
+}
+
+func HandleConfigDelete(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ token := vars["token"]
+ filename := vars["filename"]
+ subdomain := vars["subdomain"]
+
+ if token == "" {
+ GenerateResponse(w, r, http.StatusBadRequest, "Token not passed.")
+ return
+ }
+
+ if filename == "" {
+ GenerateResponse(w, r, http.StatusBadRequest, "filename not passed.")
+ return
+ }
+
+ err := Directory.RemoveFile(token, subdomain, filename)
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+ } else {
+ GenerateResponse(w, r, http.StatusOK, "Deletion of config is successful.")
+ }
+}
diff --git a/src/dkv/api/configHandlers_test.go b/src/dkv/api/configHandlers_test.go
new file mode 100644
index 0000000..e2f796a
--- /dev/null
+++ b/src/dkv/api/configHandlers_test.go
@@ -0,0 +1,234 @@
+/*
+ * 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 api
+
+import (
+ "bytes"
+ "encoding/json"
+ "github.com/gorilla/mux"
+ "github.com/stretchr/testify/assert"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+)
+
+func RouterConfig() *mux.Router {
+ router := mux.NewRouter()
+ router.HandleFunc("/v1/config", HandleConfigUpload).Methods("POST")
+ router.HandleFunc("/v1/config/{token}/{filename}", HandleConfigGet).Methods("GET")
+ router.HandleFunc("/v1/config/{token}/{filename}", HandleConfigDelete).Methods("DELETE")
+ router.HandleFunc("/v1/config/load", HandleConfigLoad).Methods("POST")
+ router.HandleFunc("/v1/config/load-default", HandleDefaultConfigLoad).Methods("GET")
+ return router
+}
+
+func TestHandleConfigGet(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectory{}
+
+ defer func() { Directory = oldDirectory }()
+
+ request, _ := http.NewRequest("GET", "/v1/config/token1/filename1", nil)
+ response := httptest.NewRecorder()
+ RouterConfig().ServeHTTP(response, request)
+
+ assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+func TestHandleConfigDelete(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectory{}
+
+ defer func() { Directory = oldDirectory }()
+
+ request, _ := http.NewRequest("DELETE", "/v1/config/token1/filename1", nil)
+ response := httptest.NewRecorder()
+ RouterConfig().ServeHTTP(response, request)
+
+ assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleConfigDelete_err(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectoryErr{}
+
+ defer func() { Directory = oldDirectory }()
+
+ request, _ := http.NewRequest("DELETE", "/v1/config/token1/filename1", nil)
+ response := httptest.NewRecorder()
+ RouterConfig().ServeHTTP(response, request)
+
+ assert.Equal(t, 500, response.Code, "500 response is expected")
+}
+
+func TestHandleConfigPOST(t *testing.T) {
+ oldConsul := Consul
+ oldKeyValues := KeyValues
+
+ Consul = &FakeConsul{}
+ KeyValues = &FakeKeyValues{}
+
+ defer func() {
+ Consul = oldConsul
+ KeyValues = oldKeyValues
+ }()
+
+ body := &LoadConfigBody{
+ Token: "test",
+ Filename: "test",
+ Subdomain: "test",
+ }
+
+ b, _ := json.Marshal(body)
+
+ // json Marshal converts struct to json in Bytes. But bytes doesn't have
+ // io reader needed. So the byte is passed to NewBuffer.
+ request, _ := http.NewRequest("POST", "/v1/config/load", bytes.NewBuffer(b))
+ response := httptest.NewRecorder()
+ RouterConfig().ServeHTTP(response, request)
+
+ assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleConfigPOST_only_token(t *testing.T) {
+ oldConsul := Consul
+ oldKeyValues := KeyValues
+
+ Consul = &FakeConsul{}
+ KeyValues = &FakeKeyValues{}
+
+ defer func() {
+ Consul = oldConsul
+ KeyValues = oldKeyValues
+ }()
+
+ body := &LoadConfigBody{
+ Token: "test",
+ Filename: "",
+ Subdomain: "",
+ }
+
+ b, _ := json.Marshal(body)
+
+ // json Marshal converts struct to json in Bytes. But bytes doesn't have
+ // io reader needed. So the byte is passed to NewBuffer.
+ request, _ := http.NewRequest("POST", "/v1/config/load", bytes.NewBuffer(b))
+ response := httptest.NewRecorder()
+ RouterConfig().ServeHTTP(response, request)
+
+ assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleConfigPOST_no_body(t *testing.T) {
+ oldConsul := Consul
+ oldKeyValues := KeyValues
+
+ Consul = &FakeConsul{}
+ KeyValues = &FakeKeyValues{}
+
+ defer func() {
+ Consul = oldConsul
+ KeyValues = oldKeyValues
+ }()
+
+ body := &LoadConfigBody{}
+
+ b, _ := json.Marshal(body)
+
+ request, _ := http.NewRequest("POST", "/v1/config/load", bytes.NewBuffer(b))
+ response := httptest.NewRecorder()
+ RouterConfig().ServeHTTP(response, request)
+
+ assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+
+func TestHandleConfigPOST_ConsulError(t *testing.T) {
+ oldConsul := Consul
+ oldKeyValues := KeyValues
+
+ Consul = &FakeConsulErr{}
+ KeyValues = &FakeKeyValuesErr{}
+
+ defer func() {
+ Consul = oldConsul
+ KeyValues = oldKeyValues
+ }()
+
+ body := &LoadConfigBody{
+ Token: "test",
+ Filename: "test",
+ Subdomain: "test",
+ }
+
+ b, _ := json.Marshal(body)
+
+ request, _ := http.NewRequest("POST", "/v1/config/load", bytes.NewBuffer(b))
+ response := httptest.NewRecorder()
+ RouterConfig().ServeHTTP(response, request)
+
+ assert.Equal(t, 500, response.Code, "500 response is expected")
+}
+
+func TestHandleConfigUpload_err(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectory{}
+
+ defer func() { Directory = oldDirectory }()
+
+ request, _ := http.NewRequest("POST", "/v1/config", nil)
+ response := httptest.NewRecorder()
+ RouterConfig().ServeHTTP(response, request)
+
+ assert.Equal(t, 500, response.Code, "500 response is expected")
+}
+
+func TestHandleDefaultConfigLoad(t *testing.T) {
+ oldConsul := Consul
+ oldKeyValues := KeyValues
+
+ Consul = &FakeConsul{}
+ KeyValues = &FakeKeyValues{}
+
+ defer func() {
+ Consul = oldConsul
+ KeyValues = oldKeyValues
+ }()
+
+ request, _ := http.NewRequest("GET", "/v1/config/load-default", nil)
+ response := httptest.NewRecorder()
+ RouterConfig().ServeHTTP(response, request)
+
+ assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleDefaultConfigLoad_err(t *testing.T) {
+ oldConsul := Consul
+ oldKeyValues := KeyValues
+
+ Consul = &FakeConsul{}
+ KeyValues = &FakeKeyValuesErr{}
+
+ defer func() {
+ Consul = oldConsul
+ KeyValues = oldKeyValues
+ }()
+
+ request, _ := http.NewRequest("GET", "/v1/config/load-default", nil)
+ response := httptest.NewRecorder()
+ RouterConfig().ServeHTTP(response, request)
+
+ assert.Equal(t, 500, response.Code, "500 response is expected")
+}
diff --git a/src/dkv/api/consulConnection_test.go b/src/dkv/api/consulConnection_test.go
deleted file mode 100644
index cc973ce..0000000
--- a/src/dkv/api/consulConnection_test.go
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * 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 api
diff --git a/src/dkv/api/endpointViews_fake.go b/src/dkv/api/endpointViews_fake.go
deleted file mode 100644
index ecc6466..0000000
--- a/src/dkv/api/endpointViews_fake.go
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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 api
-
-import "errors"
-
-/*
-A ConsulStruct is added inside this so that FakeConsul becomes an implementation of the Consul interface.
-If we don't add ConsulStruct inside this, it complains that the FakeConsul Struct doesn't implement all the methods
-defined in Consul interface.
-*/
-// Correct
-type FakeConsul struct {
- ConsulStruct
-}
-
-func (f *FakeConsul) RequestGETS() ([]string, error) {
- return []string{"key1", "key2"}, nil
-}
-
-func (f *FakeConsul) RequestGET(key string) (string, error) {
- return key, nil
-}
-
-func (f *FakeConsul) RequestPUT(key string, value string) error {
- return nil
-}
-
-func (f *FakeConsul) RequestDELETE(key string) error {
- return nil
-}
-
-// Error
-type FakeConsulErr struct {
- ConsulStruct
-}
-
-func (f *FakeConsulErr) RequestGETS() ([]string, error) {
- return []string{"", ""}, errors.New("Internal Server Error")
-}
-
-func (f *FakeConsulErr) RequestGET(key string) (string, error) {
- return "", errors.New("Internal Server Error")
-}
-
-func (f *FakeConsulErr) RequestPUT(key string, value string) error {
- return errors.New("Internal Server Error")
-}
-
-func (f *FakeConsulErr) RequestDELETE(key string) error {
- return errors.New("Internal Server Error")
-}
-
-/*
-This is done similar to the fake Consul above to pass FakeKeyValues to the interface and control method's outputs
-as required.
-*/
-//Correct
-type FakeKeyValues struct {
- KeyValuesStruct
-}
-
-func (f *FakeKeyValues) ReadConfigs(body POSTBodyStruct) error {
- return nil
-}
-
-func (f *FakeKeyValues) WriteKVsToConsul(prefix string) error {
- return nil
-}
-
-// Error
-type FakeKeyValuesErr struct {
- KeyValuesStruct
-}
-
-func (f *FakeKeyValuesErr) ReadConfigs(body POSTBodyStruct) error {
- return errors.New("Internal Server Error")
-}
-
-func (f *FakeKeyValuesErr) WriteKVsToConsul(prefix string) error {
- return errors.New("Internal Server Error")
-}
diff --git a/src/dkv/api/endpointViews_test.go b/src/dkv/api/endpointViews_test.go
deleted file mode 100644
index 8e2799e..0000000
--- a/src/dkv/api/endpointViews_test.go
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * 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 api
-
-import (
- "bytes"
- "encoding/json"
- "github.com/gorilla/mux"
- "github.com/stretchr/testify/assert"
- "net/http"
- "net/http/httptest"
- "testing"
-)
-
-func Router() *mux.Router {
- router := mux.NewRouter()
- router.HandleFunc("/loadconfigs", HandlePOST).Methods("POST")
- router.HandleFunc("/getconfig/{key}", HandleGET).Methods("GET")
- router.HandleFunc("/deleteconfig/{key}", HandleDELETE).Methods("DELETE")
- router.HandleFunc("/getconfigs", HandleGETS).Methods("GET")
- return router
-}
-
-func TestHandleGETS(t *testing.T) {
- oldConsul := Consul
- Consul = &FakeConsul{}
- defer func() { Consul = oldConsul }()
-
- request, _ := http.NewRequest("GET", "/getconfigs", nil)
- response := httptest.NewRecorder()
- Router().ServeHTTP(response, request)
-
- assert.Equal(t, 200, response.Code, "200 response is expected")
-}
-
-func TestHandleGETS_err(t *testing.T) {
- oldConsul := Consul
- Consul = &FakeConsulErr{}
- defer func() { Consul = oldConsul }()
-
- request, _ := http.NewRequest("GET", "/getconfigs", nil)
- response := httptest.NewRecorder()
- Router().ServeHTTP(response, request)
-
- assert.Equal(t, 400, response.Code, "400 response is expected")
-}
-
-func TestHandleGET(t *testing.T) {
- oldConsul := Consul
- Consul = &FakeConsul{}
- defer func() { Consul = oldConsul }()
-
- request, _ := http.NewRequest("GET", "/getconfig/key1", nil)
- response := httptest.NewRecorder()
- Router().ServeHTTP(response, request)
-
- assert.Equal(t, 200, response.Code, "200 response is expected")
-}
-
-func TestHandleGET_err(t *testing.T) {
- oldConsul := Consul
- Consul = &FakeConsulErr{}
- defer func() { Consul = oldConsul }()
-
- request, _ := http.NewRequest("GET", "/getconfig/key1", nil)
- response := httptest.NewRecorder()
- Router().ServeHTTP(response, request)
-
- assert.Equal(t, 400, response.Code, "400 response is expected")
-}
-
-func TestHandlePOST(t *testing.T) {
- oldConsul := Consul
- oldKeyValues := KeyValues
-
- Consul = &FakeConsul{}
- KeyValues = &FakeKeyValues{}
-
- defer func() {
- Consul = oldConsul
- KeyValues = oldKeyValues
- }()
-
- body := &POSTBodyStruct{
- Domain: "test",
- Type: &TypeStruct{
- FilePath: "default",
- },
- }
-
- b, _ := json.Marshal(body)
-
- // json Marshal converts struct to json in Bytes. But bytes doesn't have
- // io reader needed. So the byte is passed to NewBuffer.
- request, _ := http.NewRequest("POST", "/loadconfigs", bytes.NewBuffer(b))
- response := httptest.NewRecorder()
- Router().ServeHTTP(response, request)
-
- assert.Equal(t, 200, response.Code, "200 response is expected")
-}
-
-func TestHandlePOST_no_body(t *testing.T) {
- oldConsul := Consul
- oldKeyValues := KeyValues
-
- Consul = &FakeConsul{}
- KeyValues = &FakeKeyValues{}
-
- defer func() {
- Consul = oldConsul
- KeyValues = oldKeyValues
- }()
-
- body := &POSTBodyStruct{}
-
- b, _ := json.Marshal(body)
-
- request, _ := http.NewRequest("POST", "/loadconfigs", bytes.NewBuffer(b))
- response := httptest.NewRecorder()
- Router().ServeHTTP(response, request)
-
- assert.Equal(t, 400, response.Code, "400 response is expected")
-}
-
-func TestHandlePOST_no_filepath(t *testing.T) {
- oldConsul := Consul
- oldKeyValues := KeyValues
-
- Consul = &FakeConsul{}
- KeyValues = &FakeKeyValues{}
-
- defer func() {
- Consul = oldConsul
- KeyValues = oldKeyValues
- }()
-
- body := &POSTBodyStruct{
- Type: &TypeStruct{},
- }
-
- b, _ := json.Marshal(body)
-
- request, _ := http.NewRequest("POST", "/loadconfigs", bytes.NewBuffer(b))
- response := httptest.NewRecorder()
- Router().ServeHTTP(response, request)
-
- assert.Equal(t, 400, response.Code, "400 response is expected")
-}
-
-func TestHandlePOST_ConsulError(t *testing.T) {
- oldConsul := Consul
- oldKeyValues := KeyValues
-
- Consul = &FakeConsulErr{}
- KeyValues = &FakeKeyValuesErr{}
-
- defer func() {
- Consul = oldConsul
- KeyValues = oldKeyValues
- }()
-
- body := &POSTBodyStruct{
- Domain: "test",
- Type: &TypeStruct{
- FilePath: "default",
- },
- }
-
- b, _ := json.Marshal(body)
-
- request, _ := http.NewRequest("POST", "/loadconfigs", bytes.NewBuffer(b))
- response := httptest.NewRecorder()
- Router().ServeHTTP(response, request)
-
- assert.Equal(t, 500, response.Code, "500 response is expected")
-}
-
-func TestHandleDELETE(t *testing.T) {
- oldConsul := Consul
- Consul = &FakeConsul{}
- defer func() { Consul = oldConsul }()
-
- request, _ := http.NewRequest("DELETE", "/deleteconfig/key1", nil)
- response := httptest.NewRecorder()
- Router().ServeHTTP(response, request)
-
- assert.Equal(t, 200, response.Code, "200 response is expected")
-}
-
-func TestHandleDELETE_err(t *testing.T) {
- oldConsul := Consul
- Consul = &FakeConsulErr{}
- defer func() { Consul = oldConsul }()
-
- request, _ := http.NewRequest("DELETE", "/deleteconfig/key1", nil)
- response := httptest.NewRecorder()
- Router().ServeHTTP(response, request)
-
- assert.Equal(t, 400, response.Code, "400 response is expected")
-}
diff --git a/src/dkv/api/initialise.go b/src/dkv/api/initialise.go
index 331c981..824ca81 100644
--- a/src/dkv/api/initialise.go
+++ b/src/dkv/api/initialise.go
@@ -16,18 +16,10 @@
package api
-import (
- "errors"
- "os"
-)
-
func Initialise() error {
- if os.Getenv("CONSUL_IP") == "" {
- return errors.New("CONSUL_IP environment variable not set.")
- }
-
Consul = &ConsulStruct{}
KeyValues = &KeyValuesStruct{kvs: make(map[string]string)}
+ Directory = &DirectoryStruct{directory: ""}
err := Consul.InitializeConsulClient()
if err != nil {
diff --git a/src/dkv/api/propertiesReader.go b/src/dkv/api/propertiesReader.go
deleted file mode 100644
index 559c0fc..0000000
--- a/src/dkv/api/propertiesReader.go
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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 api
-
-import (
- "errors"
- "github.com/magiconair/properties"
- "io/ioutil"
- "log"
- "path"
- "runtime"
- "sync"
-)
-
-type KeyValuesInterface interface {
- WriteKVsToConsul(string) error
- ReadConfigs(POSTBodyStruct) error
- PropertiesFilesToKV(string) error
- ReadMultipleProperties(string) error
- ReadProperty(string)
-}
-
-type KeyValuesStruct struct {
- sync.RWMutex
- kvs map[string]string
-}
-
-var KeyValues KeyValuesInterface
-
-func (kvStruct *KeyValuesStruct) WriteKVsToConsul(prefix string) error {
- for key, value := range kvStruct.kvs {
- key = prefix + "." + key
- err := Consul.RequestPUT(key, value)
- if err != nil {
- return err
- }
- log.Println("[INFO] Key: ", key, "| Value: ", value)
- }
- log.Println("[INFO] Wrote KVs to Consul.")
- return nil
-}
-
-func (kvStruct *KeyValuesStruct) ReadConfigs(body POSTBodyStruct) error {
- defer kvStruct.Unlock()
-
- kvStruct.Lock()
-
- err := kvStruct.PropertiesFilesToKV(body.Type.FilePath)
- if err != nil {
- return err
- }
- return nil
-}
-
-func (kvStruct *KeyValuesStruct) PropertiesFilesToKV(directory string) error {
-
- if directory == "default" {
- _, filename, _, ok := runtime.Caller(0)
- if !ok {
- return errors.New("No caller")
- }
-
- defaultDir := path.Dir(filename) + "/../configurations/"
- err := kvStruct.ReadMultipleProperties(defaultDir)
- if err != nil {
- return err
- }
-
- return nil
-
- } else {
- directory += "/"
- err := kvStruct.ReadMultipleProperties(directory)
- if err != nil {
- return err
- }
-
- return nil
- }
-}
-
-func (kvStruct *KeyValuesStruct) ReadMultipleProperties(path string) error {
- files, err := ioutil.ReadDir(path)
- if err != nil {
- return err
- }
-
- for _, f := range files {
- kvStruct.ReadProperty(path + f.Name())
- }
-
- return nil
-}
-
-func (kvStruct *KeyValuesStruct) ReadProperty(path string) {
- p := properties.MustLoadFile(path, properties.UTF8)
- for _, key := range p.Keys() {
- kvStruct.kvs[key] = p.MustGet(key)
- }
-}
diff --git a/src/dkv/api/propertiesReader_test.go b/src/dkv/api/propertiesReader_test.go
deleted file mode 100644
index 342542a..0000000
--- a/src/dkv/api/propertiesReader_test.go
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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 api
-
-// TODO(sshank)
diff --git a/src/dkv/api/endpointViews.go b/src/dkv/api/queryConsulHandlers.go
index 8f77061..fb63b6d 100644
--- a/src/dkv/api/endpointViews.go
+++ b/src/dkv/api/queryConsulHandlers.go
@@ -18,7 +18,6 @@ package api
import (
"encoding/json"
- "errors"
"github.com/gorilla/mux"
"net/http"
)
@@ -35,80 +34,9 @@ type ResponseGETSStruct struct {
Response []string `json:"response"`
}
-type POSTBodyStruct struct {
- Domain string `json:"domain"`
- Type *TypeStruct `json:"type"`
-}
-
-type TypeStruct struct {
- FilePath string `json:"file_path"`
-}
-
-func ValidateBody(body POSTBodyStruct) error {
- if body.Domain == "" {
- return errors.New("Domain not set. Please set domain in POST.")
- }
- if body.Type == nil {
- return errors.New("Type not set. Recheck POST data.")
- } else if body.Type.FilePath == "" {
- return errors.New("file_path not set")
- } else {
- return nil
- }
-}
-
-func HandlePOST(w http.ResponseWriter, r *http.Request) {
-
- var body POSTBodyStruct
-
- decoder := json.NewDecoder(r.Body)
- err := decoder.Decode(&body)
-
- if err != nil {
- req := ResponseStringStruct{Response: "Empty body."}
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusBadRequest)
- json.NewEncoder(w).Encode(&req)
- return
- }
-
- err = ValidateBody(body)
-
- if err != nil {
- req := ResponseStringStruct{Response: string(err.Error())}
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusBadRequest)
- json.NewEncoder(w).Encode(req)
- return
- }
-
- err = KeyValues.ReadConfigs(body)
-
- if err != nil {
- req := ResponseStringStruct{Response: string(err.Error())}
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusInternalServerError)
- json.NewEncoder(w).Encode(req)
- return
- }
-
- err = KeyValues.WriteKVsToConsul(body.Domain)
-
- if err != nil {
- req := ResponseStringStruct{Response: string(err.Error())}
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusInternalServerError)
- json.NewEncoder(w).Encode(req)
- } else {
- req := ResponseStringStruct{Response: "Configuration read and default Key Values loaded to Consul"}
- w.Header().Set("Content-Type", "application/json")
- json.NewEncoder(w).Encode(&req)
- }
-}
-
func HandleGET(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
- key := vars["key"]
+ key := vars["token"] + "/" + vars["key"]
value, err := Consul.RequestGET(key)
diff --git a/src/dkv/api/queryConsulHandlers_test.go b/src/dkv/api/queryConsulHandlers_test.go
new file mode 100644
index 0000000..38f3acd
--- /dev/null
+++ b/src/dkv/api/queryConsulHandlers_test.go
@@ -0,0 +1,105 @@
+/*
+ * 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 api
+
+import (
+ "github.com/gorilla/mux"
+ "github.com/stretchr/testify/assert"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+)
+
+func RouterConsul() *mux.Router {
+ router := mux.NewRouter()
+ router.HandleFunc("/v1/getconfig/{key}", HandleGET).Methods("GET")
+ router.HandleFunc("/v1/deleteconfig/{key}", HandleDELETE).Methods("DELETE")
+ router.HandleFunc("/v1/getconfigs", HandleGETS).Methods("GET")
+ return router
+}
+
+func TestHandleGETS(t *testing.T) {
+ oldConsul := Consul
+ Consul = &FakeConsul{}
+ defer func() { Consul = oldConsul }()
+
+ request, _ := http.NewRequest("GET", "/v1/getconfigs", nil)
+ response := httptest.NewRecorder()
+ RouterConsul().ServeHTTP(response, request)
+
+ assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleGETS_err(t *testing.T) {
+ oldConsul := Consul
+ Consul = &FakeConsulErr{}
+ defer func() { Consul = oldConsul }()
+
+ request, _ := http.NewRequest("GET", "/v1/getconfigs", nil)
+ response := httptest.NewRecorder()
+ RouterConsul().ServeHTTP(response, request)
+
+ assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+
+func TestHandleGET(t *testing.T) {
+ oldConsul := Consul
+ Consul = &FakeConsul{}
+ defer func() { Consul = oldConsul }()
+
+ request, _ := http.NewRequest("GET", "/v1/getconfig/key1", nil)
+ response := httptest.NewRecorder()
+ RouterConsul().ServeHTTP(response, request)
+
+ assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleGET_err(t *testing.T) {
+ oldConsul := Consul
+ Consul = &FakeConsulErr{}
+ defer func() { Consul = oldConsul }()
+
+ request, _ := http.NewRequest("GET", "/v1/getconfig/key1", nil)
+ response := httptest.NewRecorder()
+ RouterConsul().ServeHTTP(response, request)
+
+ assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+
+func TestHandleDELETE(t *testing.T) {
+ oldConsul := Consul
+ Consul = &FakeConsul{}
+ defer func() { Consul = oldConsul }()
+
+ request, _ := http.NewRequest("DELETE", "/v1/deleteconfig/key1", nil)
+ response := httptest.NewRecorder()
+ RouterConsul().ServeHTTP(response, request)
+
+ assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleDELETE_err(t *testing.T) {
+ oldConsul := Consul
+ Consul = &FakeConsulErr{}
+ defer func() { Consul = oldConsul }()
+
+ request, _ := http.NewRequest("DELETE", "/v1/deleteconfig/key1", nil)
+ response := httptest.NewRecorder()
+ RouterConsul().ServeHTTP(response, request)
+
+ assert.Equal(t, 400, response.Code, "400 response is expected")
+}
diff --git a/src/dkv/api/registrationHandlers.go b/src/dkv/api/registrationHandlers.go
new file mode 100644
index 0000000..1c50b33
--- /dev/null
+++ b/src/dkv/api/registrationHandlers.go
@@ -0,0 +1,173 @@
+/*
+ * 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 api
+
+import (
+ "encoding/json"
+ "errors"
+ "github.com/gorilla/mux"
+ "net/http"
+)
+
+type CreateRegisterServiceBody struct {
+ Domain string `json:"domain"`
+}
+
+type CreateServiceSubdomainBody struct {
+ Subdomain string `json:"subdomain"`
+}
+
+func ValidateCreateRegisterServiceBody(body CreateRegisterServiceBody) error {
+ if body.Domain == "" {
+ return errors.New("Domain not set. Please set domain in POST.")
+ }
+ if body.Domain == "default" {
+ return errors.New("Domain not allowed. Please set another domain in POST.")
+ }
+ return nil
+}
+
+/*
+ TODO(sshank): Add validations to check if tokens/sub-domains/files indeed
+ exist in the token_service JSON or in the directory. This is to avoid the service
+ returning the file system errors to the user.
+*/
+
+func HandleServiceCreate(w http.ResponseWriter, r *http.Request) {
+ var body CreateRegisterServiceBody
+
+ decoder := json.NewDecoder(r.Body)
+ err := decoder.Decode(&body)
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusBadRequest, "Empty body.")
+ return
+ }
+
+ err = ValidateCreateRegisterServiceBody(body)
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusBadRequest, string(err.Error()))
+ return
+ }
+
+ token, err := Directory.CreateService(body)
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+ } else {
+ GenerateResponse(w, r, http.StatusOK, "Registration Successful. Token: "+token)
+ }
+}
+
+func HandleServiceGet(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ token := vars["token"]
+
+ if token == "" {
+ GenerateResponse(w, r, http.StatusBadRequest, "Token not present in path.")
+ return
+ }
+
+ service, found, err := Directory.FindService(token)
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+ } else {
+ if found == true {
+ GenerateResponse(w, r, http.StatusOK, service)
+ } else {
+ GenerateResponse(w, r, http.StatusNotFound, "Service for Token:"+token+"not found.")
+ }
+
+ }
+}
+
+func HandleServiceDelete(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ token := vars["token"]
+
+ if token == "default" {
+ GenerateResponse(w, r, http.StatusNotAcceptable, "Default delete not allowed.")
+ return
+ }
+
+ err := Directory.RemoveService(token)
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+ } else {
+ GenerateResponse(w, r, http.StatusOK, "Deletion of service is successful.")
+ }
+}
+
+func HandleServiceSubdomainCreate(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ token := vars["token"]
+
+ var body CreateServiceSubdomainBody
+
+ decoder := json.NewDecoder(r.Body)
+ err := decoder.Decode(&body)
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusBadRequest, "Empty body.")
+ return
+ }
+
+ if body.Subdomain == "" {
+ GenerateResponse(w, r, http.StatusBadRequest, "Subdomain not found in POST.")
+ return
+ }
+
+ err = Directory.CreateServiceSubdomain(token, body.Subdomain)
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+ } else {
+ GenerateResponse(w, r, http.StatusOK, "Subdomain creation success with token: "+token)
+ }
+
+}
+
+func HandleServiceSubdomainGet(w http.ResponseWriter, r *http.Request) {
+ // TODO(sshank): This will list all subdomain in a service.
+}
+
+func HandleServiceSubdomainDelete(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ token := vars["token"]
+ subdomain := vars["subdomain"]
+
+ if token == "" {
+ GenerateResponse(w, r, http.StatusBadRequest, "Token not passed.")
+ return
+ }
+
+ if token == "default" && subdomain == "" {
+ GenerateResponse(w, r, http.StatusNotAcceptable, "Not allowerd.")
+ return
+ }
+
+ err := Directory.RemoveServiceSubdomain(token, subdomain)
+
+ if err != nil {
+ GenerateResponse(w, r, http.StatusInternalServerError, string(err.Error()))
+ } else {
+ GenerateResponse(w, r, http.StatusOK, "Deletion of service is successful.")
+ }
+}
diff --git a/src/dkv/api/registrationHandlers_test.go b/src/dkv/api/registrationHandlers_test.go
new file mode 100644
index 0000000..3482e8d
--- /dev/null
+++ b/src/dkv/api/registrationHandlers_test.go
@@ -0,0 +1,276 @@
+/*
+ * 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 api
+
+import (
+ "bytes"
+ "encoding/json"
+ "github.com/gorilla/mux"
+ "github.com/stretchr/testify/assert"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+)
+
+func RouterRegister() *mux.Router {
+ router := mux.NewRouter()
+ router.HandleFunc("/v1/register", HandleServiceCreate).Methods("POST")
+ router.HandleFunc("/v1/register/{token}", HandleServiceGet).Methods("GET")
+ router.HandleFunc("/v1/register/{token}", HandleServiceDelete).Methods("DELETE")
+ return router
+}
+
+func RouterRegisterSubdomain() *mux.Router {
+ router := mux.NewRouter()
+ router.HandleFunc("/v1/register/{token}/subdomain", HandleServiceSubdomainCreate).Methods("POST")
+ router.HandleFunc("/v1/register/{token}/subdomain/{subdomain}", HandleServiceSubdomainDelete).Methods("DELETE")
+ return router
+}
+
+func TestHandleServiceCreate(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectory{}
+
+ defer func() { Directory = oldDirectory }()
+
+ body := &CreateRegisterServiceBody{
+ Domain: "test",
+ }
+
+ b, _ := json.Marshal(body)
+
+ request, _ := http.NewRequest("POST", "/v1/register", bytes.NewBuffer(b))
+ response := httptest.NewRecorder()
+ RouterRegister().ServeHTTP(response, request)
+
+ assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleServiceCreate_default(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectory{}
+
+ defer func() { Directory = oldDirectory }()
+
+ body := &CreateRegisterServiceBody{
+ Domain: "default",
+ }
+
+ b, _ := json.Marshal(body)
+
+ request, _ := http.NewRequest("POST", "/v1/register", bytes.NewBuffer(b))
+ response := httptest.NewRecorder()
+ RouterRegister().ServeHTTP(response, request)
+
+ assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+func TestHandleServiceCreate_no_body(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectory{}
+
+ defer func() { Directory = oldDirectory }()
+
+ body := &CreateRegisterServiceBody{}
+
+ b, _ := json.Marshal(body)
+
+ request, _ := http.NewRequest("POST", "/v1/register", bytes.NewBuffer(b))
+ response := httptest.NewRecorder()
+ RouterRegister().ServeHTTP(response, request)
+
+ assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+
+func TestHandleServiceCreate_no_domain(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectory{}
+
+ defer func() { Directory = oldDirectory }()
+
+ body := &CreateRegisterServiceBody{
+ Domain: "",
+ }
+
+ b, _ := json.Marshal(body)
+
+ request, _ := http.NewRequest("POST", "/v1/register", bytes.NewBuffer(b))
+ response := httptest.NewRecorder()
+ RouterRegister().ServeHTTP(response, request)
+
+ assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+
+func TestHandleServiceGet(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectory{}
+ defer func() { Directory = oldDirectory }()
+
+ request, _ := http.NewRequest("GET", "/v1/register/token1", nil)
+ response := httptest.NewRecorder()
+ RouterRegister().ServeHTTP(response, request)
+
+ assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleServiceGet_err(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectoryErr{}
+ defer func() { Directory = oldDirectory }()
+
+ request, _ := http.NewRequest("GET", "/v1/register/token1", nil)
+ response := httptest.NewRecorder()
+ RouterRegister().ServeHTTP(response, request)
+
+ assert.Equal(t, 500, response.Code, "500 response is expected")
+}
+func TestHandleServiceDelete(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectory{}
+
+ defer func() { Directory = oldDirectory }()
+
+ request, _ := http.NewRequest("DELETE", "/v1/register/token1", nil)
+ response := httptest.NewRecorder()
+ RouterRegister().ServeHTTP(response, request)
+
+ assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleServiceDelete_default(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectory{}
+
+ defer func() { Directory = oldDirectory }()
+
+ request, _ := http.NewRequest("DELETE", "/v1/register/default", nil)
+ response := httptest.NewRecorder()
+ RouterRegister().ServeHTTP(response, request)
+
+ assert.Equal(t, 406, response.Code, "406 response is expected")
+}
+
+func TestHandleServiceDelete_err(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectoryErr{}
+
+ defer func() { Directory = oldDirectory }()
+
+ request, _ := http.NewRequest("DELETE", "/v1/register/token1", nil)
+ response := httptest.NewRecorder()
+ RouterRegister().ServeHTTP(response, request)
+
+ assert.Equal(t, 500, response.Code, "500 response is expected")
+}
+
+func TestHandleServiceSuddomainCreate(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectory{}
+
+ defer func() { Directory = oldDirectory }()
+
+ body := &CreateServiceSubdomainBody{
+ Subdomain: "test",
+ }
+
+ b, _ := json.Marshal(body)
+
+ request, _ := http.NewRequest("POST", "/v1/register/token1/subdomain", bytes.NewBuffer(b))
+ response := httptest.NewRecorder()
+ RouterRegisterSubdomain().ServeHTTP(response, request)
+
+ assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleServiceSuddomainCreate_no_body(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectory{}
+
+ defer func() { Directory = oldDirectory }()
+
+ body := &CreateServiceSubdomainBody{}
+
+ b, _ := json.Marshal(body)
+
+ request, _ := http.NewRequest("POST", "/v1/register/token1/subdomain", bytes.NewBuffer(b))
+ response := httptest.NewRecorder()
+ RouterRegisterSubdomain().ServeHTTP(response, request)
+
+ assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+
+func TestHandleServiceSuddomainCreate_no_subdomain(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectory{}
+
+ defer func() { Directory = oldDirectory }()
+
+ body := &CreateServiceSubdomainBody{
+ Subdomain: "",
+ }
+
+ b, _ := json.Marshal(body)
+
+ request, _ := http.NewRequest("POST", "/v1/register/token1/subdomain", bytes.NewBuffer(b))
+ response := httptest.NewRecorder()
+ RouterRegisterSubdomain().ServeHTTP(response, request)
+
+ assert.Equal(t, 400, response.Code, "400 response is expected")
+}
+
+func TestHandleServiceSuddomainCreate_err(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectoryErr{}
+
+ defer func() { Directory = oldDirectory }()
+
+ body := &CreateServiceSubdomainBody{
+ Subdomain: "test",
+ }
+
+ b, _ := json.Marshal(body)
+
+ request, _ := http.NewRequest("POST", "/v1/register/token1/subdomain", bytes.NewBuffer(b))
+ response := httptest.NewRecorder()
+ RouterRegisterSubdomain().ServeHTTP(response, request)
+
+ assert.Equal(t, 500, response.Code, "500 response is expected")
+}
+
+func TestHandleServiceSuddomainDelete(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectory{}
+
+ defer func() { Directory = oldDirectory }()
+
+ request, _ := http.NewRequest("DELETE", "/v1/register/token1/subdomain/subdomain1", nil)
+ response := httptest.NewRecorder()
+ RouterRegisterSubdomain().ServeHTTP(response, request)
+
+ assert.Equal(t, 200, response.Code, "200 response is expected")
+}
+
+func TestHandleServiceSuddomainDelete_err(t *testing.T) {
+ oldDirectory := Directory
+ Directory = &FakeDirectoryErr{}
+
+ defer func() { Directory = oldDirectory }()
+
+ request, _ := http.NewRequest("DELETE", "/v1/register/token1/subdomain/subdomain1", nil)
+ response := httptest.NewRecorder()
+ RouterRegisterSubdomain().ServeHTTP(response, request)
+
+ assert.Equal(t, 500, response.Code, "500 response is expected")
+}
diff --git a/src/dkv/api/token_service_map.json b/src/dkv/api/token_service_map.json
new file mode 100644
index 0000000..51d4dbb
--- /dev/null
+++ b/src/dkv/api/token_service_map.json
@@ -0,0 +1 @@
+[{"token":"default","service":"default"}] \ No newline at end of file
diff --git a/src/dkv/api/utils.go b/src/dkv/api/utils.go
new file mode 100644
index 0000000..c1094ab
--- /dev/null
+++ b/src/dkv/api/utils.go
@@ -0,0 +1,153 @@
+/*
+ * 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 api
+
+import (
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "net/http"
+)
+
+var (
+ IoutilRead = ioutil.ReadFile
+ IoutilWrite = ioutil.WriteFile
+ JsonReader = ReadJSON
+)
+
+type Token_service_map struct {
+ Token string `json:"token"`
+ Service string `json:"service"`
+}
+
+func ReadJSON(path string) ([]Token_service_map, error) {
+ var tsm_list []Token_service_map
+ // raw, err := ioutil.ReadFile("./token_service_map.json")
+ raw, err := IoutilRead(path)
+ if err != nil {
+ return tsm_list, err
+ }
+ json.Unmarshal(raw, &tsm_list)
+ return tsm_list, nil
+}
+
+func WriteJSON(path string, token string, service string) error {
+ tsm_list, err := JsonReader(path)
+ if err != nil {
+ return err
+ }
+ var tsm Token_service_map
+ tsm.Token = token
+ tsm.Service = service
+ tsm_list = append(tsm_list, tsm)
+ raw, err := json.Marshal(tsm_list)
+ if err != nil {
+ return err
+ }
+ err = IoutilWrite(path, raw, 0644)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func DeleteInJSON(path string, token string) error {
+ serviceList, err := JsonReader(path)
+ if err != nil {
+ return err
+ }
+
+ var resultList []Token_service_map
+ var foundFlag = false
+
+ // Linear search for the token. If found set found flag. If not, keep
+ // copying the different values to resultList.
+ for _, service := range serviceList {
+ if service.Token == token {
+ foundFlag = true
+ } else {
+ resultList = append(resultList, service)
+ }
+ }
+
+ if foundFlag == false {
+ return errors.New("Service not found. Check if Token is correct or service is registered.")
+ } else {
+ // This is done to avoid writing 'null' in the json file.
+ if len(serviceList) == 1 {
+ dummy := Token_service_map{Token: "", Service: ""}
+ resultList = append(resultList, dummy)
+ }
+ raw, err := json.Marshal(resultList)
+ if err != nil {
+ return err
+ }
+ err = IoutilWrite(path, raw, 0644)
+ if err != nil {
+ return err
+ }
+ return nil
+ }
+}
+
+func FindTokenInJSON(path string, token string) (bool, error) {
+ serviceList, err := JsonReader(path)
+ if err != nil {
+ return false, err
+ }
+
+ for _, service := range serviceList {
+ if service.Token == token {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+func FindServiceInJSON(path string, serviceName string) (bool, error) {
+ serviceList, err := JsonReader(path)
+ if err != nil {
+ return false, err
+ }
+
+ for _, service := range serviceList {
+ if service.Service == serviceName {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+func GetServicebyToken(path string, token string) (string, bool, error) {
+ serviceList, err := JsonReader(path)
+ if err != nil {
+ return "", false, err
+ }
+ for _, service := range serviceList {
+ if service.Token == token {
+ return service.Service, true, nil
+ }
+ }
+ return "", false, nil
+}
+
+func GenerateResponse(w http.ResponseWriter, r *http.Request, httpStatus int, msg string) {
+ req := ResponseStringStruct{Response: msg}
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(httpStatus)
+ json.NewEncoder(w).Encode(req)
+}
diff --git a/src/dkv/api/utils_test.go b/src/dkv/api/utils_test.go
new file mode 100644
index 0000000..0bca7c5
--- /dev/null
+++ b/src/dkv/api/utils_test.go
@@ -0,0 +1,306 @@
+/*
+ * 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 api
+
+import (
+ "github.com/stretchr/testify/assert"
+ "os"
+ "testing"
+)
+
+func TestReadJSON(t *testing.T) {
+ oldIoutilRead := IoutilRead
+
+ defer func() {
+ IoutilRead = oldIoutilRead
+ }()
+
+ IoutilRead = func(path string) ([]byte, error) {
+ return []byte("test"), nil
+ }
+
+ _, err := ReadJSON("path")
+ assert.Equal(t, nil, err, "Error should be nil.")
+}
+func TestReadJSON_err(t *testing.T) {
+ oldIoutilRead := IoutilRead
+
+ defer func() {
+ IoutilRead = oldIoutilRead
+ }()
+
+ _, err := ReadJSON("path")
+ assert.NotNil(t, err, "Err should not be nil.")
+}
+
+func TestWriteJSON(t *testing.T) {
+ oldReadJson := JsonReader
+ oldIoutilWrite := IoutilWrite
+
+ defer func() {
+ JsonReader = oldReadJson
+ IoutilWrite = oldIoutilWrite
+ }()
+
+ JsonReader = func(path string) ([]Token_service_map, error) {
+ var tsm_list []Token_service_map
+ o1 := Token_service_map{
+ Token: "token1",
+ Service: "service1",
+ }
+ o2 := Token_service_map{
+ Token: "token2",
+ Service: "service2",
+ }
+ tsm_list = append(tsm_list, o1, o2)
+ return tsm_list, nil
+ }
+
+ IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+ return nil
+ }
+
+ err := WriteJSON("path", "token3", "service3")
+ assert.Equal(t, nil, err, "Error should be nil.")
+
+}
+
+func TestDeleteInJSON(t *testing.T) {
+ oldReadJson := JsonReader
+ defer func() {
+ JsonReader = oldReadJson
+ }()
+
+ JsonReader = func(path string) ([]Token_service_map, error) {
+ var tsm_list []Token_service_map
+ o1 := Token_service_map{
+ Token: "token1",
+ Service: "service1",
+ }
+ o2 := Token_service_map{
+ Token: "token2",
+ Service: "service2",
+ }
+ tsm_list = append(tsm_list, o1, o2)
+ return tsm_list, nil
+ }
+
+ IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+ return nil
+ }
+
+ err := DeleteInJSON("path", "token2")
+ assert.Equal(t, nil, err, "Error should be nil.")
+}
+
+func TestDeleteInJSON_single(t *testing.T) {
+ oldReadJson := JsonReader
+ defer func() {
+ JsonReader = oldReadJson
+ }()
+
+ JsonReader = func(path string) ([]Token_service_map, error) {
+ var tsm_list []Token_service_map
+ o1 := Token_service_map{
+ Token: "token1",
+ Service: "service1",
+ }
+ tsm_list = append(tsm_list, o1)
+ return tsm_list, nil
+ }
+
+ IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+ return nil
+ }
+
+ err := DeleteInJSON("path", "token1")
+ assert.Equal(t, nil, err, "Error should be nil.")
+}
+
+func TestDeleteInJSON_not_found(t *testing.T) {
+ oldReadJson := JsonReader
+ defer func() {
+ JsonReader = oldReadJson
+ }()
+
+ JsonReader = func(path string) ([]Token_service_map, error) {
+ var tsm_list []Token_service_map
+ o1 := Token_service_map{
+ Token: "token1",
+ Service: "service1",
+ }
+ o2 := Token_service_map{
+ Token: "token2",
+ Service: "service2",
+ }
+ tsm_list = append(tsm_list, o1, o2)
+ return tsm_list, nil
+ }
+
+ IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+ return nil
+ }
+
+ err := DeleteInJSON("path", "token3")
+ assert.NotNil(t, err, "Err should not be nil.")
+}
+
+func TestFindTokenInJSON(t *testing.T) {
+ oldReadJson := JsonReader
+ defer func() {
+ JsonReader = oldReadJson
+ }()
+
+ JsonReader = func(path string) ([]Token_service_map, error) {
+ var tsm_list []Token_service_map
+ o1 := Token_service_map{
+ Token: "token1",
+ Service: "service1",
+ }
+ o2 := Token_service_map{
+ Token: "token2",
+ Service: "service2",
+ }
+ tsm_list = append(tsm_list, o1, o2)
+ return tsm_list, nil
+ }
+
+ IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+ return nil
+ }
+
+ found, _ := FindTokenInJSON("path", "token2")
+
+ assert.True(t, found, "Token should be found in JSON.")
+}
+
+func TestFindServiceInJSON(t *testing.T) {
+ oldReadJson := JsonReader
+ defer func() {
+ JsonReader = oldReadJson
+ }()
+
+ JsonReader = func(path string) ([]Token_service_map, error) {
+ var tsm_list []Token_service_map
+ o1 := Token_service_map{
+ Token: "token1",
+ Service: "service1",
+ }
+ o2 := Token_service_map{
+ Token: "token2",
+ Service: "service2",
+ }
+ tsm_list = append(tsm_list, o1, o2)
+ return tsm_list, nil
+ }
+
+ IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+ return nil
+ }
+
+ found, _ := FindServiceInJSON("path", "service2")
+
+ assert.True(t, found, "Token should be found in JSON.")
+}
+
+func TestFindServiceInJSON_not_found(t *testing.T) {
+ oldReadJson := JsonReader
+ defer func() {
+ JsonReader = oldReadJson
+ }()
+
+ JsonReader = func(path string) ([]Token_service_map, error) {
+ var tsm_list []Token_service_map
+ o1 := Token_service_map{
+ Token: "token1",
+ Service: "service1",
+ }
+ o2 := Token_service_map{
+ Token: "token2",
+ Service: "service2",
+ }
+ tsm_list = append(tsm_list, o1, o2)
+ return tsm_list, nil
+ }
+
+ IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+ return nil
+ }
+
+ found, _ := FindServiceInJSON("path", "service3")
+
+ assert.False(t, found, "Token should not be found in JSON.")
+}
+
+func TestGetServicebyToken(t *testing.T) {
+ oldReadJson := JsonReader
+ defer func() {
+ JsonReader = oldReadJson
+ }()
+
+ JsonReader = func(path string) ([]Token_service_map, error) {
+ var tsm_list []Token_service_map
+ o1 := Token_service_map{
+ Token: "token1",
+ Service: "service1",
+ }
+ o2 := Token_service_map{
+ Token: "token2",
+ Service: "service2",
+ }
+ tsm_list = append(tsm_list, o1, o2)
+ return tsm_list, nil
+ }
+
+ IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+ return nil
+ }
+
+ service, found, _ := GetServicebyToken("path", "token1")
+
+ assert.Equal(t, "service1", service, "Service not found")
+ assert.True(t, found, "Token should be found in JSON.")
+}
+
+func TestGetServicebyToken_not_found(t *testing.T) {
+ oldReadJson := JsonReader
+ defer func() {
+ JsonReader = oldReadJson
+ }()
+
+ JsonReader = func(path string) ([]Token_service_map, error) {
+ var tsm_list []Token_service_map
+ o1 := Token_service_map{
+ Token: "token1",
+ Service: "service1",
+ }
+ o2 := Token_service_map{
+ Token: "token2",
+ Service: "service2",
+ }
+ tsm_list = append(tsm_list, o1, o2)
+ return tsm_list, nil
+ }
+
+ IoutilWrite = func(val string, b []byte, f os.FileMode) error {
+ return nil
+ }
+
+ service, found, _ := GetServicebyToken("path", "token3")
+
+ assert.Equal(t, "", service, "Service is found")
+ assert.False(t, found, "Token should be found in JSON.")
+}
diff --git a/src/dkv/configurations/sampleAAIConfig.properties b/src/dkv/configurations/sampleAAIConfig.properties
deleted file mode 100644
index 6052315..0000000
--- a/src/dkv/configurations/sampleAAIConfig.properties
+++ /dev/null
@@ -1,94 +0,0 @@
-####################################################################
-# REMEMBER TO THINK ABOUT ENVIRONMENTAL DIFFERENCES AND CHANGE THE
-# TEMPLATE AND *ALL* DATAFILES
-####################################################################
-
-aai.config.checktime=1000
-
-# this could come from siteconfig.pl?
-aai.config.nodename=AutomaticallyOverwritten
-
-
-
-aai.auth.cspcookies_on=false
-aai.dbmodel.filename=ex5.json
-
-aai.server.url.base=<%= @AAI_SERVER_URL_BASE %>
-aai.server.url=<%= @AAI_SERVER_URL %>
-aai.global.callback.url=<%= @AAI_GLOBAL_CALLBACK_URL %>
-
-aai.tools.enableBasicAuth=true
-aai.tools.username=AAI
-aai.tools.password=AAI
-
-aai.truststore.filename=<%= @AAI_TRUSTSTORE_FILENAME %>
-aai.truststore.passwd.x=<%= @AAI_TRUSTSTORE_PASSWD_X %>
-aai.keystore.filename=<%= @AAI_KEYSTORE_FILENAME %>
-aai.keystore.passwd.x=<%= @AAI_KEYSTORE_PASSWD_X %>
-
-
-aai.notification.current.version=<%= @AAI_NOTIFICATION_CURRENT_VERSION %>
-aai.notificationEvent.default.status=<%= @AAI_NOTIFICATION_EVENT_DEFAULT_EVENT_STATUS %>
-aai.notificationEvent.default.eventType=<%= @AAI_NOTIFICATION_EVENT_DEFAULT_EVENT_TYPE %>
-aai.notificationEvent.default.domain=<%= @AAI_NOTIFICATION_EVENT_DEFAULT_DOMAIN %>
-aai.notificationEvent.default.sourceName=<%= @AAI_NOTIFICATION_EVENT_DEFAULT_SOURCE_NAME %>
-aai.notificationEvent.default.sequenceNumber=<%= @AAI_NOTIFICATION_EVENT_DEFAULT_SEQUENCE_NUMBER %>
-aai.notificationEvent.default.severity=<%= @AAI_NOTIFICATION_EVENT_DEFAULT_SEVERITY %>
-aai.notificationEvent.default.version=<%= @AAI_NOTIFICATION_EVENT_DEFAULT_VERSION %>
-# This one lets us enable/disable resource-version checking on updates/deletes
-aai.resourceversion.enableflag=<%= @RESOURCE_VERSION_ENABLE_FLAG %>
-aai.logging.maxStackTraceEntries=10
-aai.default.api.version=<%= @AAI_DEFAULT_API_VERSION %>
-
-
-
-# Used by Model-processing code
-aai.model.delete.sleep.per.vtx.msec=500
-aai.model.query.resultset.maxcount=50
-aai.model.query.timeout.sec=90
-
-# Used by Data Grooming
-aai.grooming.default.max.file=150
-aai.grooming.default.sleep.minutes=7
-
-aai.model.proc.max.levels=50
-aai.edgeTag.proc.max.levels=50
-
-# for transaction log
-aai.logging.hbase.interceptor=true
-aai.logging.hbase.enabled=true
-aai.logging.hbase.logrequest=true
-aai.logging.hbase.logresponse=true
-
-# for gremlin server
-aai.server.rebind=g
-hbase.table.name=<%= @TXN_HBASE_TABLE_NAME %>
-hbase.table.timestamp.format=YYYYMMdd-HH:mm:ss:SSS
-hbase.zookeeper.quorum=<%= @TXN_ZOOKEEPER_QUORUM %>
-hbase.zookeeper.property.clientPort=<%= @TXN_ZOOKEEPER_PROPERTY_CLIENTPORT %>
-hbase.zookeeper.znode.parent=<%= @TXN_HBASE_ZOOKEEPER_ZNODE_PARENT %>
-
-aai.logging.trace.enabled=true
-aai.logging.trace.logrequest=false
-aai.logging.trace.logresponse=false
-
-
-aai.transaction.logging=true
-aai.transaction.logging.get=false
-aai.transaction.logging.post=false
-
-#limit set for bulk consumer APIS
-aai.bulkconsumer.payloadlimit=30
-
-#uncomment and use header X-OverrideLimit with the value to override the bulk api limit
-#aai.bulkconsumer.payloadoverride=AAI-OVERRIDE-KEY
-aai.bulkconsumer.payloadoverride=false
-
-#timeout for crud enabled flag
-aai.crud.timeoutenabled=true
-
-#timeout app specific
-aai.crud.timeout.appspecific=JUNITTESTAPP1,1|JUNITTESTAPP2,-1|DCAE-CCS,-1|DCAES,-1|AAI-FILEGEN-GFPIP,-1
-
-#default timeout limit added for traversal if not overridden (in ms)
-aai.crud.timeoutlimit=180000 \ No newline at end of file
diff --git a/src/dkv/configurations/sampleAPPCConfig.properties b/src/dkv/configurations/sampleAPPCConfig.properties
deleted file mode 100644
index 484337f..0000000
--- a/src/dkv/configurations/sampleAPPCConfig.properties
+++ /dev/null
@@ -1,113 +0,0 @@
-### ###
-### Properties for demo ###
-### ###
-appc.demo.poolMembers=10.0.11.1:3904
-appc.demo.topic.read=APPC-CL
-appc.demo.topic.write=APPC-CL
-appc.demo.client.name=appcDemoEventListener
-appc.demo.threads.queuesize.min=1
-appc.demo.threads.queuesize.max=1000
-appc.demo.threads.poolsize.min=1
-appc.demo.threads.poolsize.max=2
-appc.demo.provider.user=admin
-appc.demo.provider.pass=Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U
-appc.demo.provider.url=http://localhost:8181/restconf/operations/appc-provider
-appc.provider.vfodl.url=http://admin:Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U@10.0.2.1:8282/restconf/config/network-topology:network-topology/topology/topology-netconf/node/NODE_NAME/yang-ext:mount/sample-plugin:sample-plugin/pg-streams/
-
-# The properties right below are needed to properly call the Master DG to serve demo purposes
-appc.service.logic.module.name=APPC
-appc.topology.dg.method=topology-operation-all
-appc.topology.dg.version=2.0.0
-
-# TEMP - Properties that might be needed to make the AAI-APPC connection
-org.onap.appc.db.url.appcctl=jdbc:mysql://dbhost:3306/appcctl
-org.onap.appc.db.user.appcctl=appcctl
-org.onap.appc.db.pass.appcctl=appcctl
-
-org.onap.appc.db.url.sdnctl=jdbc:mysql://dbhost:3306/sdnctl
-org.onap.appc.db.user.sdnctl=sdnctl
-org.onap.appc.db.pass.sdnctl=gamma
-
-
-### ###
-### OpenStack credentials (these properties also are used in appc-rest-adapter-bundle, appc-chef-adapter-bundle, appc-iaas-adapter-bundle) ###
-### ###
-provider1.type=OpenStackProvider
-provider1.name=OpenStack
-provider1.identity=http://localhost:8181/apidoc/explorer/index.html
-provider1.tenant1.name=default
-provider1.tenant1.domain=default
-provider1.tenant1.userid=admin
-provider1.tenant1.password=Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U
-
-
-
-
-
-### ###
-### Properties that are not covered or being replaced from default.properties files. Default value for DMaaP IP is 10.0.11.1:3904 ###
-### which is what the Master HEAT Template to instantiate ONAP is pointing to (version R1). All other default values are ###
-### left there since these are pre-defined as part of APP-C/ONAP default instantiation with Master HEAT Template ###
-### ###
-
-
-# Property below is valid in appc-command-executor-core, appc-license-manager-core, appc-lifecycle-management-core,
-# appc-request-handler-core, appc-workflow-management-core (all from the appc-dispatcher package).
-dmaap.poolMembers=10.0.11.1:3904
-
-
-# appc-event-listener-bundle properties (only defined in src/test of default.properties)
-appc.LCM.poolMembers=10.0.11.1:3904
-appc.LCM.topic.read=APPC-LCM-READ
-appc.LCM.topic.write=APPC-LCM-WRITE
-appc.LCM.client.name=APPC-EVENT-LISTENER-TEST
-appc.LCM.provider.user=admin
-appc.LCM.provider.pass=Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U
-appc.LCM.provider.url=http://localhost:8181/restconf/operations/appc-provider-lcm
-
-
-# properties from appc-netconf-adapter-bundle, appc-dg-common, appc-dmaap-adapter-bundle
-poolMembers=10.0.11.1:3904
-event.pool.members=10.0.11.1:3904
-restconf.user=admin
-restconf.pass=Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U
-
-
-# properties found in appc-rest-adapter-bundle, appc-chef-adapter-bundle, appc-iaas-adapter-bundle)
-#Your OpenStack IP
-test.ip=10.0.11.100
-# Your OpenStack Platform's Keystone Port (default is 5000)
-test.port=5000
-test.tenantid=test
-test.vmid=test
-# Port 8774 below is default port for OpenStack's Nova API Service
-test.url=http://api.appc.local/vm/9999999/test/99999999-9999-9999-9999-999999999999
-#skips hypervisor check which usually occurs during iaas-adapter-bundle startup
-org.onap.appc.iaas.skiphypervisorcheck=true
-
-
-# Properties from default.properties in the src/test and src/main paths of appc-asdc-listener-bundle
-appc.sdc.host=10.0.3.1:8443
-appc.sdc.env=APPC-ASDC-ENV
-appc.sdc.user=test
-appc.sdc.pass=test
-appc.sdc.consumer=APPC-ASDC-CONSUMER
-appc.sdc.consumer.id=APPC-ASDC-CONSUMER-ID
-appc.sdc.provider.url=http://localhost:8181/restconf/operations/AsdcMessage:configuration-document-request
-
-# Properties used by EventSenderDmaapImpl.java
-DCAE.dmaap.event.topic.write=EventSenderTest
-DCAE.dmaap.appc.username=test
-DCAE.dmaap.appc.password=test
-DCAE.dmaap.event.pool.members=10.0.11.1:3904
-
-# OAM Listener
-appc.OAM.disabled=true
-appc.OAM.provider.url=http://localhost:8181/restconf/operations/appc-oam
-appc.OAM.poolMembers=10.0.11.1:3904
-appc.OAM.service=ueb
-appc.OAM.topic.read=testOAM
-appc.OAM.topic.write=testOAM
-appc.OAM.client.name=testOAM
-appc.OAM.provider.user=admin
-appc.OAM.provider.pass=Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U \ No newline at end of file
diff --git a/src/dkv/main.go b/src/dkv/main.go
index 8b4850f..5bf076f 100644
--- a/src/dkv/main.go
+++ b/src/dkv/main.go
@@ -31,10 +31,29 @@ func main() {
log.Fatal(err)
}
router := mux.NewRouter()
- router.HandleFunc("/loadconfigs", api.HandlePOST).Methods("POST")
- router.HandleFunc("/getconfig/{key}", api.HandleGET).Methods("GET")
- router.HandleFunc("/deleteconfig/{key}", api.HandleDELETE).Methods("DELETE")
- router.HandleFunc("/getconfigs", api.HandleGETS).Methods("GET")
+ // Sevice Registration
+ // Domain CRUD
+ router.HandleFunc("/v1/register", api.HandleServiceCreate).Methods("POST")
+ router.HandleFunc("/v1/register/{token}", api.HandleServiceGet).Methods("GET")
+ router.HandleFunc("/v1/register/{token}", api.HandleServiceDelete).Methods("DELETE")
+ // Subdomain CRUD
+ router.HandleFunc("/v1/register/{token}/subdomain", api.HandleServiceSubdomainCreate).Methods("POST")
+ // router.HandleFunc("/v1/register/{token}/subdomain", api.HandleServiceSubdomainGet).Methods("GET")
+ router.HandleFunc("/v1/register/{token}/subdomain/{subdomain}", api.HandleServiceSubdomainDelete).Methods("DELETE")
+ // Configuration CRUD
+ router.HandleFunc("/v1/config", api.HandleConfigUpload).Methods("POST")
+ router.HandleFunc("/v1/config/{token}/{filename}", api.HandleConfigGet).Methods("GET")
+ router.HandleFunc("/v1/config/{token}/{subdomain}/{filename}", api.HandleConfigGet).Methods("GET")
+ router.HandleFunc("/v1/config/{token}/{filename}", api.HandleConfigDelete).Methods("DELETE")
+ router.HandleFunc("/v1/config/{token}/{subdomain}/{filename}", api.HandleConfigDelete).Methods("DELETE")
+ router.HandleFunc("/v1/config/load", api.HandleConfigLoad).Methods("POST")
+ // Load default configs
+ router.HandleFunc("/v1/config/load-default", api.HandleDefaultConfigLoad).Methods("GET")
+ // Direct Consul queries.
+ router.HandleFunc("/v1/getconfig/{token}/{key}", api.HandleGET).Methods("GET")
+ router.HandleFunc("/v1/deleteconfig/{key}", api.HandleDELETE).Methods("DELETE")
+ router.HandleFunc("/v1/getconfigs", api.HandleGETS).Methods("GET")
+
loggedRouter := handlers.LoggingHandler(os.Stdout, router)
log.Println("[INFO] Started Distributed KV Store server.")
log.Fatal(http.ListenAndServe(":8080", loggedRouter))