router.HandleFunc("/projects/{project-name}/composite-apps/{composite-app-name}/{composite-app-version}/composite-profiles/{composite-profile-name}/profiles/{app-profile}", appProfileHandler.deleteAppProfileHandler).Methods("DELETE")
router.HandleFunc("/controllers", controlHandler.createHandler).Methods("POST")
- router.HandleFunc("/controllers", controlHandler.createHandler).Methods("PUT")
+ router.HandleFunc("/controllers", controlHandler.getHandler).Methods("GET")
+ router.HandleFunc("/controllers/{controller-name}", controlHandler.putHandler).Methods("PUT")
router.HandleFunc("/controllers/{controller-name}", controlHandler.getHandler).Methods("GET")
router.HandleFunc("/controllers/{controller-name}", controlHandler.deleteHandler).Methods("DELETE")
//setting routes for genericPlacementIntent
"net/http"
"github.com/gorilla/mux"
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/validation"
moduleLib "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module"
+ pkgerrors "github.com/pkg/errors"
)
// Used to store backend implementations objects
client moduleLib.ControllerManager
}
+// Check for valid format of input parameters
+func validateControllerInputs(c moduleLib.Controller) error {
+ // validate metadata
+ err := moduleLib.IsValidMetadata(c.Metadata)
+ if err != nil {
+ return pkgerrors.Wrap(err, "Invalid controller metadata")
+ }
+
+ errs := validation.IsValidName(c.Spec.Host)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid host name for controller %v, errors: %v", c.Spec.Host, errs)
+ }
+
+ errs = validation.IsValidNumber(c.Spec.Port, 0, 65535)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid controller port [%v], errors: %v", c.Spec.Port, errs)
+ }
+
+ found := false
+ for _, val := range moduleLib.CONTROLLER_TYPES {
+ if c.Spec.Type == val {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return pkgerrors.Errorf("Invalid controller type: %v", c.Spec.Type)
+ }
+
+ errs = validation.IsValidNumber(c.Spec.Priority, moduleLib.MinControllerPriority, moduleLib.MaxControllerPriority)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid controller priority = [%v], errors: %v", c.Spec.Priority, errs)
+ }
+
+ return nil
+}
+
// Create handles creation of the controller entry in the database
func (h controllerHandler) createHandler(w http.ResponseWriter, r *http.Request) {
var m moduleLib.Controller
}
// Name is required.
- if m.Name == "" {
+ if m.Metadata.Name == "" {
http.Error(w, "Missing name in POST request", http.StatusBadRequest)
return
}
- ret, err := h.client.CreateController(m)
+ ret, err := h.client.CreateController(m, false)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
-// Get handles GET operations on a particular controller Name
-// Returns a controller
-func (h controllerHandler) getHandler(w http.ResponseWriter, r *http.Request) {
+// Put handles creation or update of the controller entry in the database
+func (h controllerHandler) putHandler(w http.ResponseWriter, r *http.Request) {
+ var m moduleLib.Controller
vars := mux.Vars(r)
name := vars["controller-name"]
- ret, err := h.client.GetController(name)
+ err := json.NewDecoder(r.Body).Decode(&m)
+ 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 m.Metadata.Name == "" {
+ http.Error(w, "Missing name in POST request", http.StatusBadRequest)
+ return
+ }
+
+ // name in URL should match name in body
+ if m.Metadata.Name != name {
+ http.Error(w, "Mismatched name in PUT request", http.StatusBadRequest)
+ return
+ }
+
+ ret, err := h.client.CreateController(m, 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(ret)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+// Get handles GET operations on a particular controller Name
+// Returns a controller
+func (h controllerHandler) getHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ name := vars["controller-name"]
+ var ret interface{}
+ var err error
+
+ // handle the get all controllers case
+ if len(name) == 0 {
+ ret, err = h.client.GetControllers()
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ } else {
+
+ ret, err = h.client.GetController(name)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ }
+
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
err = json.NewEncoder(w).Encode(ret)
Err error
}
-func (m *mockControllerManager) CreateController(inp moduleLib.Controller) (moduleLib.Controller, error) {
+func (m *mockControllerManager) CreateController(inp moduleLib.Controller, mayExist bool) (moduleLib.Controller, error) {
if m.Err != nil {
return moduleLib.Controller{}, m.Err
}
return m.Items[0], nil
}
+func (m *mockControllerManager) GetControllers() ([]moduleLib.Controller, error) {
+ if m.Err != nil {
+ return []moduleLib.Controller{}, m.Err
+ }
+
+ return m.Items, nil
+}
+
func (m *mockControllerManager) DeleteController(name string) error {
return m.Err
}
label: "Create Controller",
expectedCode: http.StatusCreated,
reader: bytes.NewBuffer([]byte(`{
- "name":"testController",
+ "metadata": {
+ "name":"testController"
+ },
+ "spec": {
"ip-address":"10.188.234.1",
- "port":8080
+ "port":8080 }
}`)),
expected: moduleLib.Controller{
- Name: "testController",
- Host: "10.188.234.1",
- Port: 8080,
+ Metadata: moduleLib.Metadata{
+ Name: "testController",
+ },
+ Spec: moduleLib.ControllerSpec{
+ Host: "10.188.234.1",
+ Port: 8080,
+ },
},
controllerClient: &mockControllerManager{
//Items that will be returned by the mocked Client
Items: []moduleLib.Controller{
{
- Name: "testController",
- Host: "10.188.234.1",
- Port: 8080,
+ Metadata: moduleLib.Metadata{
+ Name: "testController",
+ },
+ Spec: moduleLib.ControllerSpec{
+ Host: "10.188.234.1",
+ Port: 8080,
+ },
},
},
},
label: "Get Controller",
expectedCode: http.StatusOK,
expected: moduleLib.Controller{
- Name: "testController",
- Host: "10.188.234.1",
- Port: 8080,
+ Metadata: moduleLib.Metadata{
+ Name: "testController",
+ },
+ Spec: moduleLib.ControllerSpec{
+ Host: "10.188.234.1",
+ Port: 8080,
+ },
},
name: "testController",
controllerClient: &mockControllerManager{
Items: []moduleLib.Controller{
{
- Name: "testController",
- Host: "10.188.234.1",
- Port: 8080,
+ Metadata: moduleLib.Metadata{
+ Name: "testController",
+ },
+ Spec: moduleLib.ControllerSpec{
+ Host: "10.188.234.1",
+ Port: 8080,
+ },
},
},
},
"io"
"net"
"regexp"
+ "strconv"
"strings"
pkgerrors "github.com/pkg/errors"
return errs
}
+func IsValidNumberStr(value string, min, max int) []string {
+ var errs []string
+
+ if min > max {
+ errs = append(errs, "invalid constraints")
+ return errs
+ }
+
+ n, err := strconv.Atoi(value)
+ if err != nil {
+ errs = append(errs, err.Error())
+ return errs
+ }
+ if n < min {
+ errs = append(errs, "value less than minimum")
+ }
+ if n > max {
+ errs = append(errs, "value greater than maximum")
+ }
+ return errs
+}
+
/*
IsValidParameterPresent method takes in a vars map and a array of string parameters
that you expect to be present in the GET request.
// Controller contains the parameters needed for Controllers
// It implements the interface for managing the Controllers
type Controller struct {
- Name string `json:"name"`
-
- Host string `json:"host"`
+ Metadata Metadata `json:"metadata"`
+ Spec ControllerSpec `json:"spec"`
+}
- Port int64 `json:"port"`
+type ControllerSpec struct {
+ Host string `json:"host"`
+ Port int `json:"port"`
+ Type string `json:"type"`
+ Priority int `json:"priority"`
}
+const MinControllerPriority = 1
+const MaxControllerPriority = 1000000
+const CONTROLLER_TYPE_ACTION string = "action"
+const CONTROLLER_TYPE_PLACEMENT string = "placement"
+
+var CONTROLLER_TYPES = [...]string{CONTROLLER_TYPE_ACTION, CONTROLLER_TYPE_PLACEMENT}
+
// ControllerKey is the key structure that is used in the database
type ControllerKey struct {
ControllerName string `json:"controller-name"`
// ControllerManager is an interface exposes the Controller functionality
type ControllerManager interface {
- CreateController(ms Controller) (Controller, error)
+ CreateController(ms Controller, mayExist bool) (Controller, error)
GetController(name string) (Controller, error)
+ GetControllers() ([]Controller, error)
DeleteController(name string) error
}
}
// CreateController a new collection based on the Controller
-func (mc *ControllerClient) CreateController(m Controller) (Controller, error) {
+func (mc *ControllerClient) CreateController(m Controller, mayExist bool) (Controller, error) {
//Construct the composite key to select the entry
key := ControllerKey{
- ControllerName: m.Name,
+ ControllerName: m.Metadata.Name,
}
//Check if this Controller already exists
- _, err := mc.GetController(m.Name)
- if err == nil {
+ _, err := mc.GetController(m.Metadata.Name)
+ if err == nil && !mayExist {
return Controller{}, pkgerrors.New("Controller already exists")
}
- err = db.DBconn.Create(mc.collectionName, key, mc.tagMeta, m)
+ err = db.DBconn.Insert(mc.collectionName, key, nil, mc.tagMeta, m)
if err != nil {
return Controller{}, pkgerrors.Wrap(err, "Creating DB Entry")
}
key := ControllerKey{
ControllerName: name,
}
- value, err := db.DBconn.Read(mc.collectionName, key, mc.tagMeta)
+ value, err := db.DBconn.Find(mc.collectionName, key, mc.tagMeta)
if err != nil {
return Controller{}, pkgerrors.Wrap(err, "Get Controller")
}
- //value is a byte array
if value != nil {
microserv := Controller{}
- err = db.DBconn.Unmarshal(value, µserv)
+ err = db.DBconn.Unmarshal(value[0], µserv)
if err != nil {
return Controller{}, pkgerrors.Wrap(err, "Unmarshaling Value")
}
return Controller{}, pkgerrors.New("Error getting Controller")
}
+// GetControllers returns all the Controllers that are registered
+func (mc *ControllerClient) GetControllers() ([]Controller, error) {
+
+ //Construct the composite key to select the entry
+ key := ControllerKey{
+ ControllerName: "",
+ }
+
+ var resp []Controller
+ values, err := db.DBconn.Find(mc.collectionName, key, mc.tagMeta)
+ if err != nil {
+ return []Controller{}, pkgerrors.Wrap(err, "Get Controller")
+ }
+
+ for _, value := range values {
+ microserv := Controller{}
+ err = db.DBconn.Unmarshal(value, µserv)
+ if err != nil {
+ return []Controller{}, pkgerrors.Wrap(err, "Unmarshaling Value")
+ }
+
+ // run healthcheck
+ /*
+ err = mc.HealthCheck(microserv.Name)
+ if err != nil {
+ log.Warn("HealthCheck Failed", log.Fields{
+ "Controller": microserv.Name,
+ })
+ }
+ */
+ resp = append(resp, microserv)
+ }
+
+ return resp, nil
+}
+
// DeleteController the Controller from database
func (mc *ControllerClient) DeleteController(name string) error {
key := ControllerKey{
ControllerName: name,
}
- err := db.DBconn.Delete(name, key, mc.tagMeta)
+ err := db.DBconn.Remove(mc.collectionName, key)
if err != nil {
return pkgerrors.Wrap(err, "Delete Controller Entry;")
}
{
label: "Create Controller",
inp: Controller{
- Name: "testController",
- Host: "132.156.0.10",
- Port: 8080,
+ Metadata: Metadata{
+ Name: "testController",
+ },
+ Spec: ControllerSpec{
+ Host: "132.156.0.10",
+ Port: 8080,
+ },
},
expected: Controller{
- Name: "testController",
- Host: "132.156.0.10",
- Port: 8080,
+ Metadata: Metadata{
+ Name: "testController",
+ },
+ Spec: ControllerSpec{
+ Host: "132.156.0.10",
+ Port: 8080,
+ },
},
expectedError: "",
mockdb: &db.MockDB{},
t.Run(testCase.label, func(t *testing.T) {
db.DBconn = testCase.mockdb
impl := NewControllerClient()
- got, err := impl.CreateController(testCase.inp)
+ got, err := impl.CreateController(testCase.inp, false)
if err != nil {
if testCase.expectedError == "" {
t.Fatalf("Create returned an unexpected error %s", err)
label: "Get Controller",
name: "testController",
expected: Controller{
- Name: "testController",
- Host: "132.156.0.10",
- Port: 8080,
+ Metadata: Metadata{
+ Name: "testController",
+ },
+ Spec: ControllerSpec{
+ Host: "132.156.0.10",
+ Port: 8080,
+ },
},
expectedError: "",
mockdb: &db.MockDB{
Items: map[string]map[string][]byte{
ControllerKey{ControllerName: "testController"}.String(): {
"controllermetadata": []byte(
- "{\"name\":\"testController\"," +
+ "{\"metadata\":{" +
+ "\"name\":\"testController\"" +
+ "}," +
+ "\"spec\":{" +
"\"host\":\"132.156.0.10\"," +
- "\"port\":8080}"),
+ "\"port\": 8080 }}"),
},
},
},
package module
+import (
+ "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/validation"
+ pkgerrors "github.com/pkg/errors"
+)
+
// Client for using the services in the orchestrator
type Client struct {
Project *ProjectClient
c.Instantiation = NewInstantiationClient()
return c
}
+
+// It implements the interface for managing the ClusterProviders
+const MAX_DESCRIPTION_LEN int = 1024
+const MAX_USERDATA_LEN int = 4096
+
+type Metadata struct {
+ Name string `json:"name" yaml:"name"`
+ Description string `json:"description" yaml:"-"`
+ UserData1 string `json:"userData1" yaml:"-"`
+ UserData2 string `json:"userData2" yaml:"-"`
+}
+
+// Check for valid format Metadata
+func IsValidMetadata(metadata Metadata) error {
+ errs := validation.IsValidName(metadata.Name)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid Metadata name=[%v], errors: %v", metadata.Name, errs)
+ }
+
+ errs = validation.IsValidString(metadata.Description, 0, MAX_DESCRIPTION_LEN, validation.VALID_ANY_STR)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid Metadata description=[%v], errors: %v", metadata.Description, errs)
+ }
+
+ errs = validation.IsValidString(metadata.UserData1, 0, MAX_DESCRIPTION_LEN, validation.VALID_ANY_STR)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid Metadata description=[%v], errors: %v", metadata.UserData1, errs)
+ }
+
+ errs = validation.IsValidString(metadata.UserData2, 0, MAX_DESCRIPTION_LEN, validation.VALID_ANY_STR)
+ if len(errs) > 0 {
+ return pkgerrors.Errorf("Invalid Metadata description=[%v], errors: %v", metadata.UserData2, errs)
+ }
+
+ return nil
+}