Skip to content
Snippets Groups Projects
Commit c4622e78 authored by Florent Gluck's avatar Florent Gluck
Browse files

Added template rename (to be tested)

parent 83114af2
No related branches found
No related tags found
No related merge requests found
...@@ -92,6 +92,7 @@ Here is an example of `users.json` defining user Jane Doe: ...@@ -92,6 +92,7 @@ Here is an example of `users.json` defining user Jane Doe:
"pwd": "$2a$10$3MfOrdLLAEJFu0rZkGxGW.bHGiMC/uRD3B5igg5bvdwUedToRqOdO", "pwd": "$2a$10$3MfOrdLLAEJFu0rZkGxGW.bHGiMC/uRD3B5igg5bvdwUedToRqOdO",
"caps": { "caps": {
"TPL_CREATE": 1, "TPL_CREATE": 1,
"TPL_EDIT": 1,
"TPL_DESTROY": 1, "TPL_DESTROY": 1,
"TPL_LIST": 1, "TPL_LIST": 1,
"USER_CREATE": 1, "USER_CREATE": 1,
...@@ -135,14 +136,15 @@ A template directory contains the following files: ...@@ -135,14 +136,15 @@ A template directory contains the following files:
- `template.json` is the template's configuration - `template.json` is the template's configuration
- `disk.qcow` is the template's disk image in QCOW2 format - `disk.qcow` is the template's disk image in QCOW2 format
The `template.json` file defines the ID of the template, its name, its owner, and the access type which can either be `public` or `private`: The `template.json` file defines the ID of the template, its name, its owner, the access type which can either be `public` or `private`, and its creation time:
```{.json} ```{.json}
{ {
"id": "12e064f2-2092-428e-9685-f3e573c18802", "id": "12e064f2-2092-428e-9685-f3e573c18802",
"name": "Xubuntu 22.04 with gcc toolchain and vscodium editor", "name": "Xubuntu 22.04 with gcc toolchain and vscodium editor",
"owner": "janedoe@nexus.org", "owner": "janedoe@nexus.org",
"access": "public" "access": "public",
"creationTime": "2022-07-31T16:42:17+01:00"
} }
``` ```
...@@ -289,6 +291,8 @@ The table below lists all potential capabilities associated to a user: ...@@ -289,6 +291,8 @@ The table below lists all potential capabilities associated to a user:
| VM_WRITEFS_ANY | Can import files into **ANY** VM | | VM_WRITEFS_ANY | Can import files into **ANY** VM |
| VM_SET_ACCESS | Can change (edit or delete) a VM's access | | VM_SET_ACCESS | Can change (edit or delete) a VM's access |
| TPL_CREATE | Can create a template | | TPL_CREATE | Can create a template |
| TPL_EDIT | Can edit a template |
| TPL_EDIT_ANY | Can edit **ANY** template |
| TPL_DESTROY | Can destroy a template | | TPL_DESTROY | Can destroy a template |
| TPL_DESTROY_ANY | Can destroy **ANY** template | | TPL_DESTROY_ANY | Can destroy **ANY** template |
| TPL_LIST | Can list public or owned templates | | TPL_LIST | Can list public or owned templates |
...@@ -387,6 +391,7 @@ These capabilities are called "VM access capabilities": ...@@ -387,6 +391,7 @@ These capabilities are called "VM access capabilities":
|--- |--- |--- |--- |--- | |--- |--- |--- |--- |--- |
| `/templates/vm` | create a template | POST | vmID,name,access | `TPL_CREATE` | | `/templates/vm` | create a template | POST | vmID,name,access | `TPL_CREATE` |
| `/templates/qcow` | create a template | POST | qcow,name,access | `TPL_CREATE` | | `/templates/qcow` | create a template | POST | qcow,name,access | `TPL_CREATE` |
| `/templates/{id}` | edit a template | PUT | name,access | `TPL_EDIT_ANY` OR `TPL_EDIT` |
| `/templates/{id}` | delete a template | DELETE | | `TPL_DESTROY_ANY` OR `TPL_DESTROY` | | `/templates/{id}` | delete a template | DELETE | | `TPL_DESTROY_ANY` OR `TPL_DESTROY` |
| `/templates/{id}/disk` | download a template's disk | GET | | `TPL_READFS_ANY` OR `TPL_READFS` | | `/templates/{id}/disk` | download a template's disk | GET | | `TPL_READFS_ANY` OR `TPL_READFS` |
| `/templates` | list templates | GET | | `TPL_LIST_ANY` OR `TPL_LIST` | | `/templates` | list templates | GET | | `TPL_LIST_ANY` OR `TPL_LIST` |
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
"VM_WRITEFS_ANY":1, "VM_WRITEFS_ANY":1,
"VM_SET_ACCESS":1, "VM_SET_ACCESS":1,
"TPL_CREATE":1, "TPL_CREATE":1,
"TPL_EDIT":1,
"TPL_DESTROY":1, "TPL_DESTROY":1,
"TPL_DESTROY_ANY":1, "TPL_DESTROY_ANY":1,
"TPL_LIST":1, "TPL_LIST":1,
......
...@@ -29,6 +29,8 @@ const ( ...@@ -29,6 +29,8 @@ const (
CAP_VM_WRITEFS_ANY = "VM_WRITEFS_ANY" CAP_VM_WRITEFS_ANY = "VM_WRITEFS_ANY"
CAP_TPL_CREATE = "TPL_CREATE" CAP_TPL_CREATE = "TPL_CREATE"
CAP_TPL_EDIT = "TPL_EDIT"
CAP_TPL_EDIT_ANY = "TPL_EIDT_ANY"
CAP_TPL_LIST = "TPL_LIST" CAP_TPL_LIST = "TPL_LIST"
CAP_TPL_LIST_ANY = "TPL_LIST_ANY" CAP_TPL_LIST_ANY = "TPL_LIST_ANY"
CAP_TPL_DESTROY = "TPL_DESTROY" CAP_TPL_DESTROY = "TPL_DESTROY"
...@@ -56,6 +58,8 @@ var userCaps = Capabilities { ...@@ -56,6 +58,8 @@ var userCaps = Capabilities {
CAP_VM_WRITEFS_ANY: 1, CAP_VM_WRITEFS_ANY: 1,
CAP_TPL_CREATE: 1, CAP_TPL_CREATE: 1,
CAP_TPL_EDIT: 1,
CAP_TPL_EDIT_ANY: 1,
CAP_TPL_DESTROY: 1, CAP_TPL_DESTROY: 1,
CAP_TPL_DESTROY_ANY: 1, CAP_TPL_DESTROY_ANY: 1,
CAP_TPL_LIST: 1, CAP_TPL_LIST: 1,
......
...@@ -102,6 +102,7 @@ func (router *Router)Start(port int) { ...@@ -102,6 +102,7 @@ func (router *Router)Start(port int) {
templatesGroup.POST("/qcow", router.tpl.CreateTemplateFromQCOW) templatesGroup.POST("/qcow", router.tpl.CreateTemplateFromQCOW)
templatesGroup.GET("/:id/disk", router.tpl.ExportDisk) templatesGroup.GET("/:id/disk", router.tpl.ExportDisk)
templatesGroup.DELETE("/:id", router.tpl.DeleteTemplateByID) templatesGroup.DELETE("/:id", router.tpl.DeleteTemplateByID)
templatesGroup.PUT("/:id", router.tpl.EditTemplateByID)
// Starts server in a dedicated goroutine. // Starts server in a dedicated goroutine.
go func() { go func() {
......
...@@ -221,6 +221,59 @@ func (r *RouterTemplates)DeleteTemplateByID(c echo.Context) error { ...@@ -221,6 +221,59 @@ func (r *RouterTemplates)DeleteTemplateByID(c echo.Context) error {
return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps)
} }
// Edit a template based on its ID.
// Requires either of:
// CAP_TPL_EDIT_ANY: any template can be edited.
// CAP_TPL_EDIT: only a template owned by the user can be edited.
// curl --cacert ca.pem -X PUT https://localhost:1077/templates/4913a2bb-edfe-4dfe-af53-38197a44523b -H "Authorization: Bearer <AccessToken>"
func (r *RouterTemplates)EditTemplateByID(c echo.Context) error {
// Retrieves logged user from context.
user, err := getLoggedUser(r.users, c)
if err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
}
// Retrieves template ID.
tplID, err := uuid.Parse(c.Param("id"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
// Deserializes and validates the client's parameters.
// Given these parameters are optional, we can't use a validator on them.
// Validation is performed in templates.EditTemplate() instead.
type Parameters struct {
Name string `json:"name"`
Access string `json:"access"`
}
p := new(Parameters)
if user.HasCapability(caps.CAP_TPL_EDIT_ANY) {
if err := decodeJson(c, &p); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
if err := r.tpl.EditTemplate(tplID, p.Name, p.Access); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
return c.JSONPretty(http.StatusOK, jsonMsg("OK"), " ")
} else if user.HasCapability(caps.CAP_TPL_EDIT) {
template, err := r.tpl.GetTemplate(tplID)
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, err.Error())
}
if template.Owner == user.Email {
if err := r.tpl.EditTemplate(tplID, p.Name, p.Access); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
return c.JSONPretty(http.StatusOK, jsonMsg("OK"), " ")
}
}
return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps)
}
// Exports a template's disk image. // Exports a template's disk image.
// Requires either of: // Requires either of:
// CAP_TPL_READFS_ANY: any template can have its disk exported. // CAP_TPL_READFS_ANY: any template can have its disk exported.
......
...@@ -91,6 +91,7 @@ func (templates *Templates)GetTemplates(keep TemplateKeeperFn) []Template { ...@@ -91,6 +91,7 @@ func (templates *Templates)GetTemplates(keep TemplateKeeperFn) []Template {
func (templates *Templates)GetTemplate(tplID uuid.UUID) (Template, error) { func (templates *Templates)GetTemplate(tplID uuid.UUID) (Template, error) {
templates.rwlock.RLock() templates.rwlock.RLock()
defer templates.rwlock.RUnlock() defer templates.rwlock.RUnlock()
template, exists := templates.m[tplID.String()] template, exists := templates.m[tplID.String()]
if !exists { if !exists {
return dummyTemplate, errors.New("Template not found") return dummyTemplate, errors.New("Template not found")
...@@ -142,3 +143,34 @@ func (templates *Templates)AddTemplate(template *Template) error { ...@@ -142,3 +143,34 @@ func (templates *Templates)AddTemplate(template *Template) error {
func (templates *Templates)getDir() string { func (templates *Templates)getDir() string {
return templates.dir return templates.dir
} }
// Edit a template' specs: name, access
func (templates *Templates)EditTemplate(tplID uuid.UUID, name, access string) error {
tpl, err := templates.GetTemplate(tplID)
if err != nil {
return err
}
// Only updates fields that have changed.
if name != "" {
tpl.Name = name
}
if access != "" {
tpl.Access = access
}
if err = tpl.validate(); err != nil {
return err
}
err = tpl.writeConfig()
if err != nil {
return err
}
key := tpl.ID.String()
delete(templates.m, key)
templates.m[key] = tpl
return nil
}
...@@ -137,7 +137,7 @@ func (vms *VMs)DeleteVM(vmID uuid.UUID) error { ...@@ -137,7 +137,7 @@ func (vms *VMs)DeleteVM(vmID uuid.UUID) error {
} }
vm.mutex.Unlock() vm.mutex.Unlock()
// Removes the VM from the map. // Removes the VM from the map.
delete(vms.m, vm.ID.String()) delete(vms.m, vmID.String())
return nil return nil
} }
...@@ -314,7 +314,7 @@ func (vms *VMs)EditVM(vmID uuid.UUID, name string, cpus, ram int, nic NicType) e ...@@ -314,7 +314,7 @@ func (vms *VMs)EditVM(vmID uuid.UUID, name string, cpus, ram int, nic NicType) e
defer vms.rwlock.Unlock() defer vms.rwlock.Unlock()
if err = vms.updateVM(&vm); err != nil { if err = vms.updateVM(&vm); err != nil {
return errors.New("Failed updating VM") return err
} }
return nil return nil
...@@ -350,7 +350,7 @@ func (vms *VMs)SetVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string, ne ...@@ -350,7 +350,7 @@ func (vms *VMs)SetVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string, ne
vm.Access[userEmail] = newAccess vm.Access[userEmail] = newAccess
if err = vms.updateVM(&vm); err != nil { if err = vms.updateVM(&vm); err != nil {
return errors.New("Failed updating VM") return err
} }
return nil return nil
...@@ -383,7 +383,7 @@ func (vms *VMs)DeleteVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string) ...@@ -383,7 +383,7 @@ func (vms *VMs)DeleteVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string)
delete(vm.Access, userEmail) delete(vm.Access, userEmail)
if err = vms.updateVM(&vm); err != nil { if err = vms.updateVM(&vm); err != nil {
return errors.New("Failed updating VM") return err
} }
return nil return nil
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment