Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug 1829408: Reconcile machine power state annotation #578

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 24 additions & 0 deletions pkg/controller/vsphere/reconciler.go
Expand Up @@ -233,6 +233,11 @@ func (r *Reconciler) reconcileMachineWithCloudState(vm *virtualMachine, taskRef
return err
}

klog.V(3).Infof("%v: reconciling powerstate annotation", r.machine.GetName())
if err := r.reconcilePowerStateAnnontation(vm); err != nil {
return err
}

return setProviderStatus(taskRef, conditionSuccess(), r.machineScope, vm)
}

Expand Down Expand Up @@ -333,6 +338,25 @@ func (r *Reconciler) reconcileNetwork(vm *virtualMachine) error {
return nil
}

func (r *Reconciler) reconcilePowerStateAnnontation(vm *virtualMachine) error {
if vm == nil {
return errors.New("provided VM is nil")
}

// This can return an error if machine is being deleted
powerState, err := vm.getPowerState()
if err != nil {
return err
}

if r.machine.Annotations == nil {
r.machine.Annotations = map[string]string{}
}
r.machine.Annotations[machinecontroller.MachineInstanceStateAnnotationName] = string(powerState)

return nil
}

func validateMachine(machine machinev1.Machine) error {
if machine.Labels[machinev1.MachineClusterIDLabel] == "" {
return machinecontroller.InvalidMachineConfiguration("%v: missing %q label", machine.GetName(), machinev1.MachineClusterIDLabel)
Expand Down
84 changes: 82 additions & 2 deletions pkg/controller/vsphere/reconciler_test.go
Expand Up @@ -24,6 +24,7 @@ import (

machinev1 "github.com/openshift/machine-api-operator/pkg/apis/machine/v1beta1"
vsphereapi "github.com/openshift/machine-api-operator/pkg/apis/vsphereprovider/v1beta1"
machinecontroller "github.com/openshift/machine-api-operator/pkg/controller/machine"
"github.com/openshift/machine-api-operator/pkg/controller/vsphere/session"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/simulator"
Expand All @@ -40,6 +41,8 @@ import (
_ "github.com/vmware/govmomi/vapi/simulator"
)

const poweredOnState = "poweredOn"

func initSimulator(t *testing.T) (*simulator.Model, *session.Session, *simulator.Server) {
model := simulator.VPX()
model.Host = 0
Expand Down Expand Up @@ -289,7 +292,7 @@ func TestTaskIsFinished(t *testing.T) {

obj := simulator.Map.Any("VirtualMachine").(*simulator.VirtualMachine)
// Validate VM is powered on
if obj.Runtime.PowerState != "poweredOn" {
if obj.Runtime.PowerState != poweredOnState {
t.Fatal(obj.Runtime.PowerState)
}
vm := object.NewVirtualMachine(session.Client.Client, obj.Reference())
Expand Down Expand Up @@ -1456,10 +1459,87 @@ func TestReconcileMachineWithCloudState(t *testing.T) {
}

if expectedProviderID != *reconciler.machine.Spec.ProviderID {
t.Errorf("Expected: %s, got: %s", expectedProviderID, *reconciler.machine.Spec.ProviderID)
t.Errorf("Expected providerId: %s, got: %s", expectedProviderID, *reconciler.machine.Spec.ProviderID)
}

actualPowerState := reconciler.machine.Annotations[machinecontroller.MachineInstanceStateAnnotationName]
if poweredOnState != actualPowerState {
t.Errorf("Expected power state annotation: %s, got: %s", poweredOnState, actualPowerState)
}

// TODO: add zones and networks
}

func TestReconcilePowerStateAnnontation(t *testing.T) {
model, session, server := initSimulator(t)
defer model.Remove()
defer server.Close()

simulatorVM := simulator.Map.Any("VirtualMachine").(*simulator.VirtualMachine)
managedObjRef := simulatorVM.VirtualMachine.Reference()
vmObj := object.NewVirtualMachine(session.Client.Client, simulatorVM.Reference())
_, err := vmObj.PowerOn(context.Background())
if err != nil {
t.Fatal(err)
}

vm := &virtualMachine{
Context: context.Background(),
Obj: object.NewVirtualMachine(session.Client.Client, managedObjRef),
Ref: managedObjRef,
}

testCases := []struct {
name string
vm *virtualMachine
expectedError bool
}{
{
name: "Succefully reconcile annotation",
vm: vm,
},
{
name: "Error on nil VM",
vm: nil,
expectedError: true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
r := &Reconciler{
machineScope: &machineScope{
machine: &machinev1.Machine{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{},
},
},
},
}

err := r.reconcilePowerStateAnnontation(tc.vm)

if tc.expectedError {
if err == nil {
t.Errorf("reconcilePowerStateAnnontation is expected to return an error")
}

actualPowerState := r.machine.Annotations[machinecontroller.MachineInstanceStateAnnotationName]
if actualPowerState != "" {
t.Errorf("Expected power state annotation to be empty, got: %s", actualPowerState)
}
} else {
if err != nil {
t.Errorf("reconcilePowerStateAnnontation is not expected to return an error")
}

actualPowerState := r.machine.Annotations[machinecontroller.MachineInstanceStateAnnotationName]
if poweredOnState != actualPowerState {
t.Errorf("Expected power state annotation: %s, got: %s", poweredOnState, actualPowerState)
}
}
})
}
}

// See https://github.com/vmware/govmomi/blob/master/simulator/example_extend_test.go#L33:6 for extending behaviour example