From b2f51225bc78212682fd087a4ef8a67c51a94188 Mon Sep 17 00:00:00 2001 From: Lukasz Rajewski Date: Fri, 15 Oct 2021 12:11:44 +0200 Subject: Expose Update Handlers Expose Update Handlers for Definition, Profile and Config Tmpl Issue-ID: MULTICLOUD-1410 Signed-off-by: Lukasz Rajewski Change-Id: Ibe6fe05458f2af28f3e1ca14a54492a4bae19362 --- src/k8splugin/api/api.go | 3 ++ src/k8splugin/api/configtemplatehandler.go | 53 ++++++++++++++++++++++- src/k8splugin/api/defhandler.go | 52 ++++++++++++++++++++-- src/k8splugin/api/profilehandler.go | 64 +++++++++++++++++++++++++++- src/k8splugin/api/profilehandler_test.go | 2 +- src/k8splugin/internal/rb/config_template.go | 24 ++++++++--- src/k8splugin/internal/rb/definition.go | 25 ++++++++++- src/k8splugin/internal/rb/definition_test.go | 3 +- src/k8splugin/internal/rb/profile.go | 25 +++++++---- src/k8splugin/internal/rb/profile_test.go | 2 +- 10 files changed, 227 insertions(+), 26 deletions(-) diff --git a/src/k8splugin/api/api.go b/src/k8splugin/api/api.go index ed23f392..a3e53dc8 100644 --- a/src/k8splugin/api/api.go +++ b/src/k8splugin/api/api.go @@ -104,6 +104,7 @@ func NewRouter(defClient rb.DefinitionManager, resRouter.HandleFunc("/definition/{rbname}", defHandler.listVersionsHandler).Methods("GET") resRouter.HandleFunc("/definition", defHandler.listAllHandler).Methods("GET") resRouter.HandleFunc("/definition/{rbname}/{rbversion}", defHandler.getHandler).Methods("GET") + resRouter.HandleFunc("/definition/{rbname}/{rbversion}", defHandler.updateHandler).Methods("PUT") resRouter.HandleFunc("/definition/{rbname}/{rbversion}", defHandler.deleteHandler).Methods("DELETE") //Setup resource bundle profile routes @@ -115,6 +116,7 @@ func NewRouter(defClient rb.DefinitionManager, resRouter.HandleFunc("/definition/{rbname}/{rbversion}/profile", profileHandler.listHandler).Methods("GET") resRouter.HandleFunc("/definition/{rbname}/{rbversion}/profile/{prname}/content", profileHandler.uploadHandler).Methods("POST") resRouter.HandleFunc("/definition/{rbname}/{rbversion}/profile/{prname}", profileHandler.getHandler).Methods("GET") + resRouter.HandleFunc("/definition/{rbname}/{rbversion}/profile/{prname}", profileHandler.updateHandler).Methods("PUT") resRouter.HandleFunc("/definition/{rbname}/{rbversion}/profile/{prname}", profileHandler.deleteHandler).Methods("DELETE") // Config Template @@ -126,6 +128,7 @@ func NewRouter(defClient rb.DefinitionManager, resRouter.HandleFunc("/definition/{rbname}/{rbversion}/config-template", templateHandler.listHandler).Methods("GET") resRouter.HandleFunc("/definition/{rbname}/{rbversion}/config-template/{tname}/content", templateHandler.uploadHandler).Methods("POST") resRouter.HandleFunc("/definition/{rbname}/{rbversion}/config-template/{tname}", templateHandler.getHandler).Methods("GET") + resRouter.HandleFunc("/definition/{rbname}/{rbversion}/config-template/{tname}", templateHandler.updateHandler).Methods("PUT") resRouter.HandleFunc("/definition/{rbname}/{rbversion}/config-template/{tname}", templateHandler.deleteHandler).Methods("DELETE") // Config value diff --git a/src/k8splugin/api/configtemplatehandler.go b/src/k8splugin/api/configtemplatehandler.go index 0560c7ea..e8750fd8 100644 --- a/src/k8splugin/api/configtemplatehandler.go +++ b/src/k8splugin/api/configtemplatehandler.go @@ -59,7 +59,7 @@ func (h rbTemplateHandler) createHandler(w http.ResponseWriter, r *http.Request) return } - err = h.client.Create(rbName, rbVersion, p) + err = h.client.CreateOrUpdate(rbName, rbVersion, p, false) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -123,6 +123,57 @@ func (h rbTemplateHandler) getHandler(w http.ResponseWriter, r *http.Request) { } } +// createHandler handles creation of the template entry in the database +func (h rbTemplateHandler) updateHandler(w http.ResponseWriter, r *http.Request) { + var p rb.ConfigTemplate + + vars := mux.Vars(r) + rbName := vars["rbname"] + rbVersion := vars["rbversion"] + templateName := vars["tname"] + + err := json.NewDecoder(r.Body).Decode(&p) + switch { + case err == io.EOF: + http.Error(w, "Empty body", http.StatusBadRequest) + return + case err != nil: + http.Error(w, err.Error(), http.StatusUnprocessableEntity) + return + } + + // Name is required. + if p.TemplateName == "" { + http.Error(w, "Missing name in POST request", http.StatusBadRequest) + return + } + + ret, err := h.client.Get(rbName, rbVersion, templateName) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if p.TemplateName != "" && p.TemplateName != ret.TemplateName { + http.Error(w, "Template name mismatch", http.StatusBadRequest) + return + } + + err = h.client.CreateOrUpdate(rbName, rbVersion, p, true) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + err = json.NewEncoder(w).Encode(p) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + // getHandler handles GET operations on a particular template func (h rbTemplateHandler) listHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) diff --git a/src/k8splugin/api/defhandler.go b/src/k8splugin/api/defhandler.go index 480d4be5..3dea8ade 100644 --- a/src/k8splugin/api/defhandler.go +++ b/src/k8splugin/api/defhandler.go @@ -35,7 +35,7 @@ type rbDefinitionHandler struct { client rb.DefinitionManager } -// createHandler handles creation of the definition entry in the database +// createOrUpdateHandler handles creation of the definition entry in the database func (h rbDefinitionHandler) createHandler(w http.ResponseWriter, r *http.Request) { var v rb.Definition @@ -48,20 +48,64 @@ func (h rbDefinitionHandler) createHandler(w http.ResponseWriter, r *http.Reques http.Error(w, err.Error(), http.StatusUnprocessableEntity) return } + h.createOrUpdateHandler(v, w, false) +} + +// createOrUpdateHandler handles creation of the definition entry in the database +func (h rbDefinitionHandler) updateHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + name := vars["rbname"] + version := vars["rbversion"] + + var v rb.Definition + err := json.NewDecoder(r.Body).Decode(&v) + switch { + case err == io.EOF: + http.Error(w, "Empty body", http.StatusBadRequest) + return + case err != nil: + http.Error(w, err.Error(), http.StatusUnprocessableEntity) + return + } + + if v.RBVersion != "" && v.RBVersion != version { + http.Error(w, "RB version mismatch", http.StatusBadRequest) + return + } + + if v.RBName != "" && v.RBName != name { + http.Error(w, "RB name mismatch", http.StatusBadRequest) + return + } + + v.RBVersion = version + v.RBName = name + + h.createOrUpdateHandler(v, w, true) +} + +// createOrUpdateHandler handles creation of the definition entry in the database +func (h rbDefinitionHandler) createOrUpdateHandler(v rb.Definition, w http.ResponseWriter, update bool) { // Name is required. if v.RBName == "" { - http.Error(w, "Missing name in POST request", http.StatusBadRequest) + http.Error(w, "Missing name in request", http.StatusBadRequest) return } // Version is required. if v.RBVersion == "" { - http.Error(w, "Missing version in POST request", http.StatusBadRequest) + http.Error(w, "Missing version in request", http.StatusBadRequest) return } - ret, err := h.client.Create(v) + var ret rb.Definition + var err error + if update { + ret, err = h.client.Update(v) + } else { + ret, err = h.client.Create(v) + } if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return diff --git a/src/k8splugin/api/profilehandler.go b/src/k8splugin/api/profilehandler.go index acd23060..1babc4ae 100644 --- a/src/k8splugin/api/profilehandler.go +++ b/src/k8splugin/api/profilehandler.go @@ -56,7 +56,7 @@ func (h rbProfileHandler) createHandler(w http.ResponseWriter, r *http.Request) return } - ret, err := h.client.Create(p) + ret, err := h.client.CreateOrUpdate(p, false) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -127,6 +127,68 @@ func (h rbProfileHandler) getHandler(w http.ResponseWriter, r *http.Request) { } } +// updateHandler updates Profile Key in the database +// Returns an rb.Profile +func (h rbProfileHandler) updateHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + rbName := vars["rbname"] + rbVersion := vars["rbversion"] + prName := vars["prname"] + + ret, err := h.client.Get(rbName, rbVersion, prName) + if err != nil { + // Separate "Not found" from generic DB errors + if strings.Contains(err.Error(), "Error finding") { + http.Error(w, err.Error(), http.StatusNotFound) + return + } else { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } + + var p rb.Profile + + err = json.NewDecoder(r.Body).Decode(&p) + switch { + case err == io.EOF: + http.Error(w, "Empty body", http.StatusBadRequest) + return + case err != nil: + http.Error(w, err.Error(), http.StatusUnprocessableEntity) + return + } + + if p.ProfileName != "" && p.ProfileName != ret.ProfileName { + http.Error(w, "Profile name mismatch", http.StatusBadRequest) + return + } + + if p.RBVersion != "" && p.RBVersion != ret.RBVersion { + http.Error(w, "RB version mismatch", http.StatusBadRequest) + return + } + + if p.RBName != "" && p.RBName != ret.RBName { + http.Error(w, "RB name mismatch", http.StatusBadRequest) + return + } + + p.ProfileName = ret.ProfileName + p.RBVersion = ret.RBVersion + p.RBName = ret.RBName + + ret, err = h.client.CreateOrUpdate(p, true) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + err = json.NewEncoder(w).Encode(ret) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + // getHandler gets all profiles of a Resource Bundle Key in the database // Returns a list of rb.Profile func (h rbProfileHandler) listHandler(w http.ResponseWriter, r *http.Request) { diff --git a/src/k8splugin/api/profilehandler_test.go b/src/k8splugin/api/profilehandler_test.go index 32d0061f..181b775b 100644 --- a/src/k8splugin/api/profilehandler_test.go +++ b/src/k8splugin/api/profilehandler_test.go @@ -42,7 +42,7 @@ type mockRBProfile struct { Err error } -func (m *mockRBProfile) Create(inp rb.Profile) (rb.Profile, error) { +func (m *mockRBProfile) CreateOrUpdate(inp rb.Profile, update bool) (rb.Profile, error) { if m.Err != nil { return rb.Profile{}, m.Err } diff --git a/src/k8splugin/internal/rb/config_template.go b/src/k8splugin/internal/rb/config_template.go index b84b6461..97fe0fb4 100644 --- a/src/k8splugin/internal/rb/config_template.go +++ b/src/k8splugin/internal/rb/config_template.go @@ -41,7 +41,7 @@ type ConfigTemplate struct { // ConfigTemplateManager is an interface exposes the resource bundle ConfigTemplate functionality type ConfigTemplateManager interface { - Create(rbName, rbVersion string, p ConfigTemplate) error + CreateOrUpdate(rbName, rbVersion string, p ConfigTemplate, update bool) error Get(rbName, rbVersion, templateName string) (ConfigTemplate, error) List(rbName, rbVersion string) ([]ConfigTemplate, error) Delete(rbName, rbVersion, templateName string) error @@ -84,8 +84,8 @@ func NewConfigTemplateClient() *ConfigTemplateClient { } } -// Create an entry for the resource bundle ConfigTemplate in the database -func (v *ConfigTemplateClient) Create(rbName, rbVersion string, p ConfigTemplate) error { +// CreateOrUpdate an entry for the resource bundle ConfigTemplate in the database +func (v *ConfigTemplateClient) CreateOrUpdate(rbName, rbVersion string, p ConfigTemplate, update bool) error { log.Printf("[ConfigiTemplate]: create %s", rbName) // Name is required @@ -95,9 +95,12 @@ func (v *ConfigTemplateClient) Create(rbName, rbVersion string, p ConfigTemplate //Check if ConfigTemplate already exists _, err := v.Get(rbName, rbVersion, p.TemplateName) - if err == nil { + if err == nil && !update { return pkgerrors.New(" ConfigTemplate already exists for this Definition") } + if err != nil && update { + return pkgerrors.New(" ConfigTemplate does not exist for this Definition") + } //Check if provided resource bundle information is valid _, err = NewDefinitionClient().Get(rbName, rbVersion) @@ -111,9 +114,16 @@ func (v *ConfigTemplateClient) Create(rbName, rbVersion string, p ConfigTemplate TemplateName: p.TemplateName, } - err = db.DBconn.Create(v.storeName, key, v.tagMeta, p) - if err != nil { - return pkgerrors.Wrap(err, "Creating ConfigTemplate DB Entry") + if update { + err = db.DBconn.Update(v.storeName, key, v.tagMeta, p) + if err != nil { + return pkgerrors.Wrap(err, "Updating ConfigTemplate DB Entry") + } + } else { + err = db.DBconn.Create(v.storeName, key, v.tagMeta, p) + if err != nil { + return pkgerrors.Wrap(err, "Creating ConfigTemplate DB Entry") + } } return nil diff --git a/src/k8splugin/internal/rb/definition.go b/src/k8splugin/internal/rb/definition.go index 73ea44da..aa76afaa 100644 --- a/src/k8splugin/internal/rb/definition.go +++ b/src/k8splugin/internal/rb/definition.go @@ -61,6 +61,7 @@ func (dk DefinitionKey) String() string { // DefinitionManager is an interface exposes the resource bundle definition functionality type DefinitionManager interface { Create(def Definition) (Definition, error) + Update(def Definition) (Definition, error) List(name string) ([]Definition, error) Get(name string, version string) (Definition, error) Delete(name string, version string) error @@ -104,13 +105,13 @@ func (v *DefinitionClient) Create(def Definition) (Definition, error) { // Create a default profile automatically prc := NewProfileClient() - pr, err := prc.Create(Profile{ + pr, err := prc.CreateOrUpdate(Profile{ RBName: def.RBName, RBVersion: def.RBVersion, ProfileName: "default", Namespace: "default", ReleaseName: "default", - }) + }, false) if err != nil { logutils.Error("Create Default Profile", logutils.Fields{ @@ -139,6 +140,26 @@ func (v *DefinitionClient) Create(def Definition) (Definition, error) { return def, nil } +// Update an entry for the resource in the database` +func (v *DefinitionClient) Update(def Definition) (Definition, error) { + + //Construct composite key consisting of name and version + key := DefinitionKey{RBName: def.RBName, RBVersion: def.RBVersion} + + //Check if this definition already exists + _, err := v.Get(def.RBName, def.RBVersion) + if err != nil { + return Definition{}, pkgerrors.New("Definition does not exists") + } + + err = db.DBconn.Update(v.storeName, key, v.tagMeta, def) + if err != nil { + return Definition{}, pkgerrors.Wrap(err, "Updating DB Entry") + } + + return def, nil +} + // List all resource entry's versions in the database func (v *DefinitionClient) List(name string) ([]Definition, error) { res, err := db.DBconn.ReadAll(v.storeName, v.tagMeta) diff --git a/src/k8splugin/internal/rb/definition_test.go b/src/k8splugin/internal/rb/definition_test.go index 0140b459..42fb5374 100644 --- a/src/k8splugin/internal/rb/definition_test.go +++ b/src/k8splugin/internal/rb/definition_test.go @@ -18,12 +18,13 @@ package rb import ( "bytes" - "github.com/onap/multicloud-k8s/src/k8splugin/internal/db" "reflect" "sort" "strings" "testing" + "github.com/onap/multicloud-k8s/src/k8splugin/internal/db" + pkgerrors "github.com/pkg/errors" ) diff --git a/src/k8splugin/internal/rb/profile.go b/src/k8splugin/internal/rb/profile.go index 78023e59..77398580 100644 --- a/src/k8splugin/internal/rb/profile.go +++ b/src/k8splugin/internal/rb/profile.go @@ -46,7 +46,7 @@ type Profile struct { // ProfileManager is an interface exposes the resource bundle profile functionality type ProfileManager interface { - Create(def Profile) (Profile, error) + CreateOrUpdate(def Profile, update bool) (Profile, error) Get(rbName, rbVersion, prName string) (Profile, error) List(rbName, rbVersion string) ([]Profile, error) Delete(rbName, rbVersion, prName string) error @@ -89,8 +89,8 @@ func NewProfileClient() *ProfileClient { } } -// Create an entry for the resource bundle profile in the database -func (v *ProfileClient) Create(p Profile) (Profile, error) { +// CreateOrUpdate an entry for the resource bundle profile in the database +func (v *ProfileClient) CreateOrUpdate(p Profile, update bool) (Profile, error) { // Name is required if p.ProfileName == "" { @@ -99,10 +99,12 @@ func (v *ProfileClient) Create(p Profile) (Profile, error) { //Check if profile already exists _, err := v.Get(p.RBName, p.RBVersion, p.ProfileName) - if err == nil { + if err == nil && !update { return Profile{}, pkgerrors.New("Profile already exists for this Definition") } - + if err != nil && update { + return Profile{}, pkgerrors.New("Profile does not exists for this Definition") + } //Check if provided resource bundle information is valid _, err = NewDefinitionClient().Get(p.RBName, p.RBVersion) if err != nil { @@ -120,9 +122,16 @@ func (v *ProfileClient) Create(p Profile) (Profile, error) { ProfileName: p.ProfileName, } - err = db.DBconn.Create(v.storeName, key, v.tagMeta, p) - if err != nil { - return Profile{}, pkgerrors.Wrap(err, "Creating Profile DB Entry") + if update { + err = db.DBconn.Update(v.storeName, key, v.tagMeta, p) + if err != nil { + return Profile{}, pkgerrors.Wrap(err, "Updating Profile DB Entry") + } + } else { + err = db.DBconn.Create(v.storeName, key, v.tagMeta, p) + if err != nil { + return Profile{}, pkgerrors.Wrap(err, "Creating Profile DB Entry") + } } return p, nil diff --git a/src/k8splugin/internal/rb/profile_test.go b/src/k8splugin/internal/rb/profile_test.go index 2a9dc4fd..e52897ce 100644 --- a/src/k8splugin/internal/rb/profile_test.go +++ b/src/k8splugin/internal/rb/profile_test.go @@ -105,7 +105,7 @@ func TestCreateProfile(t *testing.T) { t.Run(testCase.label, func(t *testing.T) { db.DBconn = testCase.mockdb impl := NewProfileClient() - got, err := impl.Create(testCase.inp) + got, err := impl.CreateOrUpdate(testCase.inp, false) if err != nil { if testCase.expectedError == "" { t.Fatalf("Create returned an unexpected error %s", err) -- cgit 1.2.3-korg