Ability to set VM's power state and retrieve backing Tart VM's name (#373)

* Ability to set VM's power state and retrieve backing Tart VM's name

* Validate user-provided "powerState" field

* Introduce TestSpecUpdatePowerStateSuspend

* Introduce TestSpecUpdatePowerStateStopped

* OpenAPI specification: add note about suspended VMs to "tartName" desc.

* Sometimes we need to wait more than 30 seconds
This commit is contained in:
Nikolay Edigaryev
2025-12-02 16:43:17 -05:00
committed by GitHub
parent eac2c33e88
commit 76a552bade
11 changed files with 612 additions and 53 deletions
+25
View File
@@ -10,6 +10,7 @@ import (
storepkg "github.com/cirruslabs/orchard/internal/controller/store"
"github.com/cirruslabs/orchard/internal/responder"
"github.com/cirruslabs/orchard/internal/simplename"
"github.com/cirruslabs/orchard/internal/worker/ondiskname"
"github.com/cirruslabs/orchard/pkg/resource/v1"
"github.com/gin-gonic/gin"
"github.com/google/go-cmp/cmp"
@@ -43,6 +44,16 @@ func (controller *Controller) createVM(ctx *gin.Context) responder.Responder {
vm.RestartedAt = time.Time{}
vm.RestartCount = 0
vm.UID = uuid.New().String()
vm.PowerState = v1.PowerStateRunning
vm.TartName = ondiskname.New(vm.Name, vm.UID, vm.RestartCount).String()
vm.Generation = 0
vm.ObservedGeneration = 0
vm.Conditions = []v1.Condition{
{
Type: v1.ConditionTypeScheduled,
State: v1.ConditionStateFalse,
},
}
// Softnet-specific logic: automatically enable Softnet when NetSoftnetAllow or NetSoftnetBlock are set
// and propagate deprecated and non-deprecated boolean fields into each other
@@ -150,6 +161,20 @@ func (controller *Controller) updateVMSpec(ctx *gin.Context) responder.Responder
"toggled for suspendable VMs"))
}
// Power state-specific sanity checks
if !userVM.PowerState.Valid() {
return responder.JSON(http.StatusPreconditionFailed, NewErrorResponse("invalid \"powerState\" "+
"value: %s", userVM.PowerState))
}
if dbVM.PowerState.TerminalState() {
return responder.JSON(http.StatusPreconditionFailed, NewErrorResponse("invalid \"powerState\" "+
"transition: cannot transition from a terminal power state"))
}
if !dbVM.Suspendable && userVM.PowerState == v1.PowerStateSuspended {
return responder.JSON(http.StatusPreconditionFailed, NewErrorResponse("invalid \"powerState\" "+
"transition: only suspendable VMs can be suspended"))
}
if cmp.Equal(dbVM.VMSpec, userVM.VMSpec) {
// Nothing was changed
return responder.JSON(http.StatusOK, dbVM)