diff options
author | Shashank Kumar Shankar <shashank.kumar.shankar@intel.com> | 2018-02-28 17:54:59 -0800 |
---|---|---|
committer | Shashank Kumar Shankar <shashank.kumar.shankar@intel.com> | 2018-03-02 16:52:39 -0800 |
commit | 67dd59385cf983ef1307e3b3e410a8f773d8a5c3 (patch) | |
tree | e53c1997f643a0826fb9cd5a8b44ccc6466dc392 | |
parent | ff8cba5a49e85fbb1d2e14f0fa0bcb5bf92caf34 (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>
24 files changed, 2005 insertions, 549 deletions
@@ -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)) |