112 lines
2.4 KiB
Go
112 lines
2.4 KiB
Go
package base
|
|
|
|
import (
|
|
"sync/atomic"
|
|
|
|
v1 "github.com/cirruslabs/orchard/pkg/resource/v1"
|
|
mapset "github.com/deckarep/golang-set/v2"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type VM struct {
|
|
// Backward compatibility with v1.VM specification's "Status" field
|
|
//
|
|
// "started" is always true after the first "tart run",
|
|
// whereas ConditionReady can be used to tell if a VM
|
|
// is really running or not.
|
|
started atomic.Bool
|
|
|
|
// A more orthogonal alternative to v1.VM specification's "Status" field,
|
|
// which allows a VM to have more than one state.
|
|
//
|
|
// For example, a VM can be both in ConditionReady and ConditionSuspending/
|
|
// ConditionStopping states for a short time. This way in run() we know
|
|
// that we're in a process of rebooting a VM, so we can avoid throwing
|
|
// an error about unexpected VM termination.
|
|
conditions mapset.Set[v1.ConditionType]
|
|
|
|
statusMessage atomic.Pointer[string]
|
|
err atomic.Pointer[error]
|
|
|
|
logger *zap.SugaredLogger
|
|
}
|
|
|
|
func NewVM(logger *zap.SugaredLogger) *VM {
|
|
return &VM{
|
|
conditions: mapset.NewSet(v1.ConditionTypeCloning),
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
func (vm *VM) SetStarted(val bool) {
|
|
vm.started.Store(val)
|
|
}
|
|
|
|
func (vm *VM) Status() v1.VMStatus {
|
|
if vm.Err() != nil {
|
|
return v1.VMStatusFailed
|
|
}
|
|
|
|
if vm.started.Load() {
|
|
return v1.VMStatusRunning
|
|
}
|
|
|
|
return v1.VMStatusPending
|
|
}
|
|
|
|
func (vm *VM) StatusMessage() string {
|
|
status := vm.statusMessage.Load()
|
|
|
|
if status != nil {
|
|
return *status
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func (vm *VM) SetStatusMessage(status string) {
|
|
vm.logger.Debugf(status)
|
|
vm.statusMessage.Store(&status)
|
|
}
|
|
|
|
func (vm *VM) Err() error {
|
|
if err := vm.err.Load(); err != nil {
|
|
return *err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (vm *VM) SetErr(err error) {
|
|
if vm.err.CompareAndSwap(nil, &err) {
|
|
vm.logger.Error(err)
|
|
}
|
|
}
|
|
|
|
func (vm *VM) ConditionsSet() mapset.Set[v1.ConditionType] {
|
|
return vm.conditions
|
|
}
|
|
|
|
func (vm *VM) Conditions() []v1.Condition {
|
|
// Only expose a minimum amount of conditions necessary
|
|
// for the Orchard Controller to make decisions
|
|
return []v1.Condition{
|
|
vm.conditionTypeToCondition(v1.ConditionTypeRunning),
|
|
}
|
|
}
|
|
|
|
func (vm *VM) conditionTypeToCondition(conditionType v1.ConditionType) v1.Condition {
|
|
var conditionState v1.ConditionState
|
|
|
|
if vm.ConditionsSet().ContainsOne(conditionType) {
|
|
conditionState = v1.ConditionStateTrue
|
|
} else {
|
|
conditionState = v1.ConditionStateFalse
|
|
}
|
|
|
|
return v1.Condition{
|
|
Type: conditionType,
|
|
State: conditionState,
|
|
}
|
|
}
|