sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/scope/machinepoolmachine_test.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package scope
    18  
    19  import (
    20  	"context"
    21  	"reflect"
    22  	"testing"
    23  
    24  	"github.com/google/go-cmp/cmp"
    25  	. "github.com/onsi/gomega"
    26  	"github.com/pkg/errors"
    27  	"go.uber.org/mock/gomock"
    28  	corev1 "k8s.io/api/core/v1"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  	"k8s.io/utils/ptr"
    32  	infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
    33  	"sigs.k8s.io/cluster-api-provider-azure/azure"
    34  	"sigs.k8s.io/cluster-api-provider-azure/azure/mock_azure"
    35  	mock_scope "sigs.k8s.io/cluster-api-provider-azure/azure/scope/mocks"
    36  	"sigs.k8s.io/cluster-api-provider-azure/azure/services/scalesetvms"
    37  	infrav1exp "sigs.k8s.io/cluster-api-provider-azure/exp/api/v1beta1"
    38  	gomock2 "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock"
    39  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    40  	expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
    41  	"sigs.k8s.io/cluster-api/util/conditions"
    42  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    43  )
    44  
    45  const (
    46  	FakeProviderID = "/foo/bin/bazz"
    47  )
    48  
    49  func TestNewMachinePoolMachineScope(t *testing.T) {
    50  	scheme := runtime.NewScheme()
    51  	_ = expv1.AddToScheme(scheme)
    52  	_ = infrav1exp.AddToScheme(scheme)
    53  
    54  	cases := []struct {
    55  		Name  string
    56  		Input MachinePoolMachineScopeParams
    57  		Err   string
    58  	}{
    59  		{
    60  			Name: "successfully create machine scope",
    61  			Input: MachinePoolMachineScopeParams{
    62  				Client: fake.NewClientBuilder().WithScheme(scheme).Build(),
    63  				ClusterScope: &ClusterScope{
    64  					Cluster: &clusterv1.Cluster{
    65  						ObjectMeta: metav1.ObjectMeta{
    66  							Name: "clusterName",
    67  						},
    68  					},
    69  				},
    70  				MachinePool:             new(expv1.MachinePool),
    71  				AzureMachinePool:        new(infrav1exp.AzureMachinePool),
    72  				Machine:                 new(clusterv1.Machine),
    73  				AzureMachinePoolMachine: new(infrav1exp.AzureMachinePoolMachine),
    74  			},
    75  		},
    76  		{
    77  			Name: "no client",
    78  			Input: MachinePoolMachineScopeParams{
    79  				ClusterScope:            new(ClusterScope),
    80  				MachinePool:             new(expv1.MachinePool),
    81  				AzureMachinePool:        new(infrav1exp.AzureMachinePool),
    82  				Machine:                 new(clusterv1.Machine),
    83  				AzureMachinePoolMachine: new(infrav1exp.AzureMachinePoolMachine),
    84  			},
    85  			Err: "client is required when creating a MachinePoolScope",
    86  		},
    87  		{
    88  			Name: "no ClusterScope",
    89  			Input: MachinePoolMachineScopeParams{
    90  				Client:                  fake.NewClientBuilder().WithScheme(scheme).Build(),
    91  				MachinePool:             new(expv1.MachinePool),
    92  				AzureMachinePool:        new(infrav1exp.AzureMachinePool),
    93  				Machine:                 new(clusterv1.Machine),
    94  				AzureMachinePoolMachine: new(infrav1exp.AzureMachinePoolMachine),
    95  			},
    96  			Err: "cluster scope is required when creating a MachinePoolScope",
    97  		},
    98  		{
    99  			Name: "no MachinePool",
   100  			Input: MachinePoolMachineScopeParams{
   101  				Client:                  fake.NewClientBuilder().WithScheme(scheme).Build(),
   102  				ClusterScope:            new(ClusterScope),
   103  				AzureMachinePool:        new(infrav1exp.AzureMachinePool),
   104  				Machine:                 new(clusterv1.Machine),
   105  				AzureMachinePoolMachine: new(infrav1exp.AzureMachinePoolMachine),
   106  			},
   107  			Err: "machine pool is required when creating a MachinePoolScope",
   108  		},
   109  		{
   110  			Name: "no AzureMachinePool",
   111  			Input: MachinePoolMachineScopeParams{
   112  				Client:                  fake.NewClientBuilder().WithScheme(scheme).Build(),
   113  				ClusterScope:            new(ClusterScope),
   114  				MachinePool:             new(expv1.MachinePool),
   115  				Machine:                 new(clusterv1.Machine),
   116  				AzureMachinePoolMachine: new(infrav1exp.AzureMachinePoolMachine),
   117  			},
   118  			Err: "azure machine pool is required when creating a MachinePoolScope",
   119  		},
   120  		{
   121  			Name: "no AzureMachinePoolMachine",
   122  			Input: MachinePoolMachineScopeParams{
   123  				Client:           fake.NewClientBuilder().WithScheme(scheme).Build(),
   124  				ClusterScope:     new(ClusterScope),
   125  				MachinePool:      new(expv1.MachinePool),
   126  				Machine:          new(clusterv1.Machine),
   127  				AzureMachinePool: new(infrav1exp.AzureMachinePool),
   128  			},
   129  			Err: "azure machine pool machine is required when creating a MachinePoolScope",
   130  		},
   131  		{
   132  			Name: "no MachinePool Machine",
   133  			Input: MachinePoolMachineScopeParams{
   134  				Client:                  fake.NewClientBuilder().WithScheme(scheme).Build(),
   135  				ClusterScope:            new(ClusterScope),
   136  				MachinePool:             new(expv1.MachinePool),
   137  				AzureMachinePool:        new(infrav1exp.AzureMachinePool),
   138  				AzureMachinePoolMachine: new(infrav1exp.AzureMachinePoolMachine),
   139  			},
   140  			Err: "machine is required when creating a MachinePoolScope",
   141  		},
   142  	}
   143  
   144  	for _, c := range cases {
   145  		t.Run(c.Name, func(t *testing.T) {
   146  			g := NewWithT(t)
   147  			s, err := NewMachinePoolMachineScope(c.Input)
   148  			if c.Err != "" {
   149  				g.Expect(err).To(MatchError(c.Err))
   150  			} else {
   151  				g.Expect(err).NotTo(HaveOccurred())
   152  				g.Expect(s).NotTo(BeNil())
   153  			}
   154  		})
   155  	}
   156  }
   157  
   158  func TestMachinePoolMachineScope_ScaleSetVMSpecs(t *testing.T) {
   159  	tests := []struct {
   160  		name                    string
   161  		machinePoolMachineScope MachinePoolMachineScope
   162  		want                    azure.ResourceSpecGetter
   163  	}{
   164  		{
   165  			name: "return vmss vm spec for uniform vmss",
   166  			machinePoolMachineScope: MachinePoolMachineScope{
   167  				MachinePool: &expv1.MachinePool{},
   168  				AzureMachinePool: &infrav1exp.AzureMachinePool{
   169  					ObjectMeta: metav1.ObjectMeta{
   170  						Name: "machinepool-name",
   171  					},
   172  					Spec: infrav1exp.AzureMachinePoolSpec{
   173  						Template: infrav1exp.AzureMachinePoolMachineTemplate{
   174  							OSDisk: infrav1.OSDisk{
   175  								OSType: "Linux",
   176  							},
   177  						},
   178  						OrchestrationMode: infrav1.UniformOrchestrationMode,
   179  					},
   180  				},
   181  				AzureMachinePoolMachine: &infrav1exp.AzureMachinePoolMachine{
   182  					ObjectMeta: metav1.ObjectMeta{
   183  						Name: "machinepoolmachine-name",
   184  					},
   185  					Spec: infrav1exp.AzureMachinePoolMachineSpec{
   186  						ProviderID: "azure:///subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Compute/virtualMachineScaleSets/machinepool-name/virtualMachines/0",
   187  						InstanceID: "0",
   188  					},
   189  				},
   190  				ClusterScoper: &ClusterScope{
   191  					AzureCluster: &infrav1.AzureCluster{
   192  						Spec: infrav1.AzureClusterSpec{
   193  							ResourceGroup: "my-rg",
   194  						},
   195  					},
   196  				},
   197  				MachinePoolScope: &MachinePoolScope{
   198  					AzureMachinePool: &infrav1exp.AzureMachinePool{
   199  						ObjectMeta: metav1.ObjectMeta{
   200  							Name: "machinepool-name",
   201  						},
   202  					},
   203  				},
   204  			},
   205  			want: &scalesetvms.ScaleSetVMSpec{
   206  				Name:          "machinepoolmachine-name",
   207  				InstanceID:    "0",
   208  				ResourceGroup: "my-rg",
   209  				ScaleSetName:  "machinepool-name",
   210  				ProviderID:    "azure:///subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Compute/virtualMachineScaleSets/machinepool-name/virtualMachines/0",
   211  				IsFlex:        false,
   212  				ResourceID:    "",
   213  			},
   214  		},
   215  		{
   216  			name: "return vmss vm spec for vmss flex",
   217  			machinePoolMachineScope: MachinePoolMachineScope{
   218  				MachinePool: &expv1.MachinePool{},
   219  				AzureMachinePool: &infrav1exp.AzureMachinePool{
   220  					ObjectMeta: metav1.ObjectMeta{
   221  						Name: "machinepool-name",
   222  					},
   223  					Spec: infrav1exp.AzureMachinePoolSpec{
   224  						Template: infrav1exp.AzureMachinePoolMachineTemplate{
   225  							OSDisk: infrav1.OSDisk{
   226  								OSType: "Linux",
   227  							},
   228  						},
   229  						OrchestrationMode: infrav1.FlexibleOrchestrationMode,
   230  					},
   231  				},
   232  				AzureMachinePoolMachine: &infrav1exp.AzureMachinePoolMachine{
   233  					ObjectMeta: metav1.ObjectMeta{
   234  						Name: "machinepoolmachine-name",
   235  					},
   236  					Spec: infrav1exp.AzureMachinePoolMachineSpec{
   237  						ProviderID: "azure:///subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Compute/virtualMachineScaleSets/machinepool-name/virtualMachines/0",
   238  						InstanceID: "0",
   239  					},
   240  				},
   241  				ClusterScoper: &ClusterScope{
   242  					AzureCluster: &infrav1.AzureCluster{
   243  						Spec: infrav1.AzureClusterSpec{
   244  							ResourceGroup: "my-rg",
   245  						},
   246  					},
   247  				},
   248  				MachinePoolScope: &MachinePoolScope{
   249  					AzureMachinePool: &infrav1exp.AzureMachinePool{
   250  						ObjectMeta: metav1.ObjectMeta{
   251  							Name: "machinepool-name",
   252  						},
   253  					},
   254  				},
   255  			},
   256  			want: &scalesetvms.ScaleSetVMSpec{
   257  				Name:          "machinepoolmachine-name",
   258  				InstanceID:    "0",
   259  				ResourceGroup: "my-rg",
   260  				ScaleSetName:  "machinepool-name",
   261  				ProviderID:    "azure:///subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Compute/virtualMachineScaleSets/machinepool-name/virtualMachines/0",
   262  				IsFlex:        true,
   263  				ResourceID:    "/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Compute/virtualMachineScaleSets/machinepool-name/virtualMachines/0",
   264  			},
   265  		},
   266  	}
   267  	for _, tt := range tests {
   268  		t.Run(tt.name, func(t *testing.T) {
   269  			if got := tt.machinePoolMachineScope.ScaleSetVMSpec(); !reflect.DeepEqual(got, tt.want) {
   270  				t.Errorf("Diff between expected result and actual result: %+v", cmp.Diff(tt.want, got))
   271  			}
   272  		})
   273  	}
   274  }
   275  func TestMachineScope_updateDeleteMachineAnnotation(t *testing.T) {
   276  	cases := []struct {
   277  		name    string
   278  		machine clusterv1.Machine
   279  		ampm    infrav1exp.AzureMachinePoolMachine
   280  	}{
   281  		{
   282  			name: "add annotation to ampm",
   283  			machine: clusterv1.Machine{
   284  				ObjectMeta: metav1.ObjectMeta{
   285  					Annotations: map[string]string{
   286  						clusterv1.DeleteMachineAnnotation: "true",
   287  					},
   288  				},
   289  			},
   290  			ampm: infrav1exp.AzureMachinePoolMachine{},
   291  		},
   292  		{
   293  			name:    "do not add annotation to ampm when machine annotations are nil",
   294  			machine: clusterv1.Machine{},
   295  			ampm:    infrav1exp.AzureMachinePoolMachine{},
   296  		},
   297  		{
   298  			name: "do not add annotation to ampm",
   299  			machine: clusterv1.Machine{
   300  				ObjectMeta: metav1.ObjectMeta{
   301  					Annotations: map[string]string{},
   302  				},
   303  			},
   304  			ampm: infrav1exp.AzureMachinePoolMachine{},
   305  		},
   306  	}
   307  
   308  	for _, c := range cases {
   309  		c := c
   310  		t.Run(c.name, func(t *testing.T) {
   311  			g := NewWithT(t)
   312  
   313  			machineScope := &MachinePoolMachineScope{
   314  				Machine:                 &c.machine,
   315  				AzureMachinePoolMachine: &c.ampm,
   316  			}
   317  
   318  			machineScope.updateDeleteMachineAnnotation()
   319  			_, machineHasAnnotation := machineScope.Machine.Annotations[clusterv1.DeleteMachineAnnotation]
   320  			_, ampmHasAnnotation := machineScope.AzureMachinePoolMachine.Annotations[clusterv1.DeleteMachineAnnotation]
   321  			g.Expect(machineHasAnnotation).To(Equal(ampmHasAnnotation))
   322  		})
   323  	}
   324  }
   325  
   326  func TestMachineScope_UpdateNodeStatus(t *testing.T) {
   327  	scheme := runtime.NewScheme()
   328  	_ = expv1.AddToScheme(scheme)
   329  	_ = infrav1exp.AddToScheme(scheme)
   330  
   331  	mockCtrl := gomock.NewController(t)
   332  	defer mockCtrl.Finish()
   333  
   334  	clusterScope := mock_azure.NewMockClusterScoper(mockCtrl)
   335  	clusterScope.EXPECT().BaseURI().AnyTimes()
   336  	clusterScope.EXPECT().Location().AnyTimes()
   337  	clusterScope.EXPECT().SubscriptionID().AnyTimes()
   338  	clusterScope.EXPECT().ClusterName().Return("cluster-foo").AnyTimes()
   339  
   340  	cases := []struct {
   341  		Name   string
   342  		Setup  func(mockNodeGetter *mock_scope.MocknodeGetter, ampm *infrav1exp.AzureMachinePoolMachine) (*azure.VMSSVM, *infrav1exp.AzureMachinePoolMachine)
   343  		Verify func(g *WithT, scope *MachinePoolMachineScope)
   344  		Err    string
   345  	}{
   346  		{
   347  			Name: "should set kubernetes version, ready, and node reference upon finding the node",
   348  			Setup: func(mockNodeGetter *mock_scope.MocknodeGetter, ampm *infrav1exp.AzureMachinePoolMachine) (*azure.VMSSVM, *infrav1exp.AzureMachinePoolMachine) {
   349  				mockNodeGetter.EXPECT().GetNodeByProviderID(gomock2.AContext(), FakeProviderID).Return(getReadyNode(), nil)
   350  				return nil, ampm
   351  			},
   352  			Verify: func(g *WithT, scope *MachinePoolMachineScope) {
   353  				g.Expect(scope.AzureMachinePoolMachine.Status.Ready).To(BeTrue())
   354  				g.Expect(scope.AzureMachinePoolMachine.Status.Version).To(Equal("1.2.3"))
   355  				g.Expect(scope.AzureMachinePoolMachine.Status.NodeRef).To(Equal(&corev1.ObjectReference{
   356  					Name: "node1",
   357  				}))
   358  				assertCondition(t, scope.AzureMachinePoolMachine, conditions.TrueCondition(clusterv1.MachineNodeHealthyCondition))
   359  			},
   360  		},
   361  		{
   362  			Name: "should not mark AMPM ready if node is not ready",
   363  			Setup: func(mockNodeGetter *mock_scope.MocknodeGetter, ampm *infrav1exp.AzureMachinePoolMachine) (*azure.VMSSVM, *infrav1exp.AzureMachinePoolMachine) {
   364  				mockNodeGetter.EXPECT().GetNodeByProviderID(gomock2.AContext(), FakeProviderID).Return(getNotReadyNode(), nil)
   365  				return nil, ampm
   366  			},
   367  			Verify: func(g *WithT, scope *MachinePoolMachineScope) {
   368  				g.Expect(scope.AzureMachinePoolMachine.Status.Ready).To(BeFalse())
   369  				g.Expect(scope.AzureMachinePoolMachine.Status.Version).To(Equal("1.2.3"))
   370  				g.Expect(scope.AzureMachinePoolMachine.Status.NodeRef).To(Equal(&corev1.ObjectReference{
   371  					Name: "node1",
   372  				}))
   373  				assertCondition(t, scope.AzureMachinePoolMachine, conditions.FalseCondition(clusterv1.MachineNodeHealthyCondition, clusterv1.NodeConditionsFailedReason, clusterv1.ConditionSeverityWarning, ""))
   374  			},
   375  		},
   376  		{
   377  			Name: "fails fetching the node",
   378  			Setup: func(mockNodeGetter *mock_scope.MocknodeGetter, ampm *infrav1exp.AzureMachinePoolMachine) (*azure.VMSSVM, *infrav1exp.AzureMachinePoolMachine) {
   379  				mockNodeGetter.EXPECT().GetNodeByProviderID(gomock2.AContext(), FakeProviderID).Return(nil, errors.New("boom"))
   380  				return nil, ampm
   381  			},
   382  			Err: "failed to get node by providerID: boom",
   383  		},
   384  		{
   385  			Name: "node is not found by providerID without error",
   386  			Setup: func(mockNodeGetter *mock_scope.MocknodeGetter, ampm *infrav1exp.AzureMachinePoolMachine) (*azure.VMSSVM, *infrav1exp.AzureMachinePoolMachine) {
   387  				mockNodeGetter.EXPECT().GetNodeByProviderID(gomock2.AContext(), FakeProviderID).Return(nil, nil)
   388  				return nil, ampm
   389  			},
   390  			Verify: func(g *WithT, scope *MachinePoolMachineScope) {
   391  				assertCondition(t, scope.AzureMachinePoolMachine, conditions.FalseCondition(clusterv1.MachineNodeHealthyCondition, clusterv1.NodeProvisioningReason, clusterv1.ConditionSeverityInfo, ""))
   392  			},
   393  		},
   394  		{
   395  			Name: "node is found by ObjectReference",
   396  			Setup: func(mockNodeGetter *mock_scope.MocknodeGetter, ampm *infrav1exp.AzureMachinePoolMachine) (*azure.VMSSVM, *infrav1exp.AzureMachinePoolMachine) {
   397  				nodeRef := corev1.ObjectReference{
   398  					Name: "node1",
   399  				}
   400  				ampm.Status.NodeRef = &nodeRef
   401  				mockNodeGetter.EXPECT().GetNodeByObjectReference(gomock2.AContext(), nodeRef).Return(getReadyNode(), nil)
   402  				return nil, ampm
   403  			},
   404  			Verify: func(g *WithT, scope *MachinePoolMachineScope) {
   405  				g.Expect(scope.AzureMachinePoolMachine.Status.Ready).To(BeTrue())
   406  				g.Expect(scope.AzureMachinePoolMachine.Status.Version).To(Equal("1.2.3"))
   407  				g.Expect(scope.AzureMachinePoolMachine.Status.NodeRef).To(Equal(&corev1.ObjectReference{
   408  					Name: "node1",
   409  				}))
   410  				assertCondition(t, scope.AzureMachinePoolMachine, conditions.TrueCondition(clusterv1.MachineNodeHealthyCondition))
   411  			},
   412  		},
   413  	}
   414  
   415  	for _, c := range cases {
   416  		t.Run(c.Name, func(t *testing.T) {
   417  			var (
   418  				controller = gomock.NewController(t)
   419  				mockClient = mock_scope.NewMocknodeGetter(controller)
   420  				g          = NewWithT(t)
   421  				params     = MachinePoolMachineScopeParams{
   422  					Client:       fake.NewClientBuilder().WithScheme(scheme).Build(),
   423  					ClusterScope: clusterScope,
   424  					MachinePool: &expv1.MachinePool{
   425  						Spec: expv1.MachinePoolSpec{
   426  							Template: clusterv1.MachineTemplateSpec{
   427  								Spec: clusterv1.MachineSpec{
   428  									Version: ptr.To("v1.19.11"),
   429  								},
   430  							},
   431  						},
   432  					},
   433  					AzureMachinePool: new(infrav1exp.AzureMachinePool),
   434  					Machine:          new(clusterv1.Machine),
   435  				}
   436  			)
   437  
   438  			defer controller.Finish()
   439  
   440  			instance, ampm := c.Setup(mockClient, &infrav1exp.AzureMachinePoolMachine{
   441  				Spec: infrav1exp.AzureMachinePoolMachineSpec{
   442  					ProviderID: FakeProviderID,
   443  				},
   444  			})
   445  			params.AzureMachinePoolMachine = ampm
   446  			s, err := NewMachinePoolMachineScope(params)
   447  			g.Expect(err).NotTo(HaveOccurred())
   448  			g.Expect(s).NotTo(BeNil())
   449  			s.instance = instance
   450  			s.workloadNodeGetter = mockClient
   451  
   452  			err = s.UpdateNodeStatus(context.TODO())
   453  			if c.Err == "" {
   454  				g.Expect(err).To(Succeed())
   455  			} else {
   456  				g.Expect(err).To(MatchError(c.Err))
   457  			}
   458  
   459  			if c.Verify != nil {
   460  				c.Verify(g, s)
   461  			}
   462  		})
   463  	}
   464  }
   465  
   466  func getReadyNode() *corev1.Node {
   467  	return &corev1.Node{
   468  		ObjectMeta: metav1.ObjectMeta{
   469  			Name: "node1",
   470  		},
   471  		Status: corev1.NodeStatus{
   472  			NodeInfo: corev1.NodeSystemInfo{
   473  				KubeletVersion: "1.2.3",
   474  			},
   475  			Conditions: []corev1.NodeCondition{
   476  				{
   477  					Type:   corev1.NodeReady,
   478  					Status: corev1.ConditionTrue,
   479  				},
   480  			},
   481  		},
   482  	}
   483  }
   484  
   485  func getNotReadyNode() *corev1.Node {
   486  	return &corev1.Node{
   487  		ObjectMeta: metav1.ObjectMeta{
   488  			Name: "node1",
   489  		},
   490  		Status: corev1.NodeStatus{
   491  			NodeInfo: corev1.NodeSystemInfo{
   492  				KubeletVersion: "1.2.3",
   493  			},
   494  			Conditions: []corev1.NodeCondition{
   495  				{
   496  					Type:   corev1.NodeReady,
   497  					Status: corev1.ConditionFalse,
   498  				},
   499  			},
   500  		},
   501  	}
   502  }
   503  
   504  // asserts whether a condition of type is set on the Getter object
   505  // when the condition is true, asserting the reason/severity/message
   506  // for the condition are avoided.
   507  func assertCondition(t *testing.T, from conditions.Getter, condition *clusterv1.Condition) {
   508  	t.Helper()
   509  
   510  	g := NewWithT(t)
   511  	g.Expect(conditions.Has(from, condition.Type)).To(BeTrue())
   512  
   513  	if condition.Status == corev1.ConditionTrue {
   514  		conditions.IsTrue(from, condition.Type)
   515  	} else {
   516  		conditionToBeAsserted := conditions.Get(from, condition.Type)
   517  		g.Expect(conditionToBeAsserted.Status).To(Equal(condition.Status))
   518  		g.Expect(conditionToBeAsserted.Severity).To(Equal(condition.Severity))
   519  		g.Expect(conditionToBeAsserted.Reason).To(Equal(condition.Reason))
   520  		if condition.Message != "" {
   521  			g.Expect(conditionToBeAsserted.Message).To(Equal(condition.Message))
   522  		}
   523  	}
   524  }