summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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))