@@ -31,8 +31,11 @@ import (
3131 "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports"
3232 . "github.com/onsi/gomega" //nolint:revive
3333 "go.uber.org/mock/gomock"
34+ corev1 "k8s.io/api/core/v1"
3435 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3536 "k8s.io/utils/ptr"
37+ clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
38+ "sigs.k8s.io/cluster-api/util/conditions"
3639
3740 infrav1alpha1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1"
3841 infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
@@ -548,3 +551,184 @@ func Test_OpenStackServerReconcileCreate(t *testing.T) {
548551 })
549552 }
550553}
554+
555+ func TestOpenStackServerReconciler_getOrCreateServer (t * testing.T ) {
556+ tests := []struct {
557+ name string
558+ openStackServer * infrav1alpha1.OpenStackServer
559+ setupMocks func (r * recorders )
560+ wantServer * servers.Server
561+ wantErr bool
562+ wantCondition * clusterv1.Condition
563+ }{
564+ {
565+ name : "instanceID set in status but server not found" ,
566+ openStackServer : & infrav1alpha1.OpenStackServer {
567+ Status : infrav1alpha1.OpenStackServerStatus {
568+ InstanceID : ptr .To (instanceUUID ),
569+ },
570+ },
571+ setupMocks : func (r * recorders ) {
572+ r .compute .GetServer (instanceUUID ).Return (nil , gophercloud.ErrUnexpectedResponseCode {Actual : 404 })
573+ },
574+ wantErr : false ,
575+ wantCondition : & clusterv1.Condition {
576+ Type : infrav1 .InstanceReadyCondition ,
577+ Status : corev1 .ConditionFalse ,
578+ Reason : infrav1 .InstanceNotFoundReason ,
579+ Message : infrav1 .ServerUnexpectedDeletedMessage ,
580+ },
581+ },
582+ {
583+ name : "instanceID set in status but server not found with error" ,
584+ openStackServer : & infrav1alpha1.OpenStackServer {
585+ Status : infrav1alpha1.OpenStackServerStatus {
586+ InstanceID : ptr .To (instanceUUID ),
587+ },
588+ },
589+ setupMocks : func (r * recorders ) {
590+ r .compute .GetServer (instanceUUID ).Return (nil , fmt .Errorf ("error" ))
591+ },
592+ wantErr : true ,
593+ wantCondition : & clusterv1.Condition {
594+ Type : infrav1 .InstanceReadyCondition ,
595+ Status : corev1 .ConditionFalse ,
596+ Reason : infrav1 .OpenStackErrorReason ,
597+ Message : "get server \" " + instanceUUID + "\" detail failed: error" ,
598+ },
599+ },
600+ {
601+ name : "instanceStatus is nil but server found with machine name" ,
602+ openStackServer : & infrav1alpha1.OpenStackServer {
603+ ObjectMeta : metav1.ObjectMeta {
604+ Name : openStackServerName ,
605+ },
606+ Status : infrav1alpha1.OpenStackServerStatus {},
607+ },
608+ setupMocks : func (r * recorders ) {
609+ r .compute .ListServers (servers.ListOpts {
610+ Name : "^" + openStackServerName + "$" ,
611+ }).Return ([]servers.Server {{ID : instanceUUID }}, nil )
612+ },
613+ wantErr : false ,
614+ wantServer : & servers.Server {
615+ ID : instanceUUID ,
616+ },
617+ },
618+ {
619+ name : "instanceStatus is nil and server not found and then created" ,
620+ openStackServer : & infrav1alpha1.OpenStackServer {
621+ ObjectMeta : metav1.ObjectMeta {
622+ Name : openStackServerName ,
623+ },
624+ Status : infrav1alpha1.OpenStackServerStatus {
625+ Resolved : & infrav1alpha1.ResolvedServerSpec {
626+ ImageID : imageUUID ,
627+ FlavorID : flavorUUID ,
628+ Ports : defaultResolvedPorts ,
629+ },
630+ },
631+ },
632+ setupMocks : func (r * recorders ) {
633+ r .compute .ListServers (servers.ListOpts {
634+ Name : "^" + openStackServerName + "$" ,
635+ }).Return ([]servers.Server {}, nil )
636+ r .compute .CreateServer (gomock .Any (), gomock .Any ()).Return (& servers.Server {ID : instanceUUID }, nil )
637+ },
638+ wantErr : false ,
639+ wantServer : & servers.Server {
640+ ID : instanceUUID ,
641+ },
642+ // It's off but no condition is set because the server creation was kicked off but we
643+ // don't know the result yet in this function.
644+ },
645+ {
646+ name : "instanceStatus is nil and server not found and then created with error" ,
647+ openStackServer : & infrav1alpha1.OpenStackServer {
648+ ObjectMeta : metav1.ObjectMeta {
649+ Name : openStackServerName ,
650+ },
651+ Status : infrav1alpha1.OpenStackServerStatus {
652+ Resolved : & infrav1alpha1.ResolvedServerSpec {
653+ ImageID : imageUUID ,
654+ FlavorID : flavorUUID ,
655+ Ports : defaultResolvedPorts ,
656+ },
657+ },
658+ },
659+ setupMocks : func (r * recorders ) {
660+ r .compute .ListServers (servers.ListOpts {
661+ Name : "^" + openStackServerName + "$" ,
662+ }).Return ([]servers.Server {}, nil )
663+ r .compute .CreateServer (gomock .Any (), gomock .Any ()).Return (nil , fmt .Errorf ("error" ))
664+ },
665+ wantErr : true ,
666+ wantCondition : & clusterv1.Condition {
667+ Type : infrav1 .InstanceReadyCondition ,
668+ Status : corev1 .ConditionFalse ,
669+ Reason : infrav1 .InstanceCreateFailedReason ,
670+ Message : "error creating Openstack instance: " + "error" ,
671+ },
672+ },
673+ }
674+
675+ for _ , tt := range tests {
676+ t .Run (tt .name , func (t * testing.T ) {
677+ g := NewGomegaWithT (t )
678+ log := testr .New (t )
679+
680+ mockCtrl := gomock .NewController (t )
681+ defer mockCtrl .Finish ()
682+
683+ mockScopeFactory := scope .NewMockScopeFactory (mockCtrl , "" )
684+ scopeWithLogger := scope .NewWithLogger (mockScopeFactory , log )
685+
686+ computeRecorder := mockScopeFactory .ComputeClient .EXPECT ()
687+ imageRecorder := mockScopeFactory .ImageClient .EXPECT ()
688+ networkRecorder := mockScopeFactory .NetworkClient .EXPECT ()
689+ volumeRecorder := mockScopeFactory .VolumeClient .EXPECT ()
690+
691+ recorders := & recorders {
692+ compute : computeRecorder ,
693+ image : imageRecorder ,
694+ network : networkRecorder ,
695+ volume : volumeRecorder ,
696+ }
697+
698+ if tt .setupMocks != nil {
699+ tt .setupMocks (recorders )
700+ }
701+
702+ computeService , err := compute .NewService (scopeWithLogger )
703+ g .Expect (err ).ToNot (HaveOccurred ())
704+
705+ reconciler := OpenStackServerReconciler {}
706+ status , err := reconciler .getOrCreateServer (ctx , log , tt .openStackServer , computeService , []string {portUUID })
707+
708+ // Check error result
709+ if tt .wantErr {
710+ g .Expect (err ).To (HaveOccurred ())
711+ } else {
712+ g .Expect (err ).ToNot (HaveOccurred ())
713+ }
714+
715+ // Check instance status
716+ if tt .wantServer != nil {
717+ g .Expect (status .ID ()).To (Equal (tt .wantServer .ID ))
718+ }
719+
720+ // Check the condition is set correctly
721+ if tt .wantCondition != nil {
722+ // print openstackServer conditions
723+ for _ , condition := range tt .openStackServer .Status .Conditions {
724+ t .Logf ("Condition: %s, Status: %s, Reason: %s" , condition .Type , condition .Status , condition .Reason )
725+ }
726+ conditionType := conditions .Get (tt .openStackServer , tt .wantCondition .Type )
727+ g .Expect (conditionType ).ToNot (BeNil ())
728+ g .Expect (conditionType .Status ).To (Equal (tt .wantCondition .Status ))
729+ g .Expect (conditionType .Reason ).To (Equal (tt .wantCondition .Reason ))
730+ g .Expect (conditionType .Message ).To (Equal (tt .wantCondition .Message ))
731+ }
732+ })
733+ }
734+ }
0 commit comments