Expose Update Handlers 22/125022/1
authorLukasz Rajewski <lukasz.rajewski@orange.com>
Fri, 15 Oct 2021 10:11:44 +0000 (12:11 +0200)
committerLukasz Rajewski <lukasz.rajewski@orange.com>
Fri, 15 Oct 2021 10:12:44 +0000 (12:12 +0200)
Expose Update Handlers for Definition, Profile and Config Tmpl

Issue-ID: MULTICLOUD-1410
Signed-off-by: Lukasz Rajewski <lukasz.rajewski@orange.com>
Change-Id: Ibe6fe05458f2af28f3e1ca14a54492a4bae19362

src/k8splugin/api/api.go
src/k8splugin/api/configtemplatehandler.go
src/k8splugin/api/defhandler.go
src/k8splugin/api/profilehandler.go
src/k8splugin/api/profilehandler_test.go
src/k8splugin/internal/rb/config_template.go
src/k8splugin/internal/rb/definition.go
src/k8splugin/internal/rb/definition_test.go
src/k8splugin/internal/rb/profile.go
src/k8splugin/internal/rb/profile_test.go

index ed23f39..a3e53dc 100644 (file)
@@ -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
index 0560c7e..e8750fd 100644 (file)
@@ -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)
index 480d4be..3dea8ad 100644 (file)
@@ -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
index acd2306..1babc4a 100644 (file)
@@ -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) {
index 32d0061..181b775 100644 (file)
@@ -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
        }
index b84b646..97fe0fb 100644 (file)
@@ -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
index 73ea44d..aa76afa 100644 (file)
@@ -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)
index 0140b45..42fb537 100644 (file)
@@ -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"
 )
 
index 78023e5..7739858 100644 (file)
@@ -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
index 2a9dc4f..e52897c 100644 (file)
@@ -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)