summaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--README.md39
-rw-r--r--mountpath/default/sampleAAIConfig.properties (renamed from src/dkv/configurations/sampleAAIConfig.properties)0
-rw-r--r--mountpath/default/sampleAPPCConfig.properties (renamed from src/dkv/configurations/sampleAPPCConfig.properties)0
-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/main.go27
24 files changed, 2005 insertions, 549 deletions
diff --git a/README.md b/README.md
index 0273978..ac3b1f9 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,39 @@
Distributed Key Value Store using Consul to store application configuration data.
-# TODO
-# Add documentation on how to run.
+# TODO (Add documentation on how to run)
+
+# Sample Curl examples:
+
+## Load default configuration
+`curl -X GET localhost:8080/v1/config/load-default`
+
+## Register new domain
+`curl -X POST -d '{"domain":"<project>"}' localhost:8080/v1/register`
+`export TOKEN=`
+## Register new sub domain
+`curl -X POST -d '{"subdomain":"<sub-project>"}' localhost:8080/v1/register/$TOKEN/subdomain`
+
+## Check if a domain is already registered.
+`curl -X GET localhost:8080/v1/register/$TOKEN`
+
+## List all sub domains in a domain.
+`TODO`
+
+## Upload properties file to domain or subdomain.
+`curl -X POST -F 'token=$TOKEN' -F 'configFile=@./example.properties' localhost:8080/v1/config`
+`curl -X POST -F 'token=$TOKEN' -F 'subdomain=<sub-domain>' -F 'configFile=@./example.properties' localhost:8080/v1/config`
+
+## Load properties file into Consul
+`curl -X POST -d '{"token":"$TOKEN", "filename": "example.properties"}' localhost:8080/v1/config/load`
+
+## Fetch properties file
+`curl -X GET localhost:8080/v1/config/$TOKEN/example.properties`
+`curl -X GET localhost:8080/v1/config/$TOKEN/<sub-domain>/example.properties`
+
+## Delete properties file
+`curl -X DELETE localhost:8080/v1/config/$TOKEN/example.properties`
+`curl -X DELETE localhost:8080/v1/config/$TOKEN/<sub-domain>/example.properties`
+
+## Delete project/sub project
+`curl -X DELETE localhost:8080/v1/register/$TOKEN/subdomain/<sub-domain>`
+`curl -X DELETE localhost:8080/v1/register/$TOKEN`
diff --git a/src/dkv/configurations/sampleAAIConfig.properties b/mountpath/default/sampleAAIConfig.properties
index 6052315..6052315 100644
--- a/src/dkv/configurations/sampleAAIConfig.properties
+++ b/mountpath/default/sampleAAIConfig.properties
diff --git a/src/dkv/configurations/sampleAPPCConfig.properties b/mountpath/default/sampleAPPCConfig.properties
index 484337f..484337f 100644
--- a/src/dkv/configurations/sampleAPPCConfig.properties
+++ b/mountpath/default/sampleAPPCConfig.properties
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/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))