github.com/cilium/cilium@v1.16.2/operator/pkg/bgpv2/cluster_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package bgpv2
     5  
     6  import (
     7  	"context"
     8  	"testing"
     9  
    10  	"github.com/cilium/hive/hivetest"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
    14  	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  	"k8s.io/utils/ptr"
    16  
    17  	cilium_api_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    18  	cilium_api_v2alpha1 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1"
    19  	slim_meta_v1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    20  	"github.com/cilium/cilium/pkg/time"
    21  )
    22  
    23  var (
    24  	cluster1 = cilium_api_v2alpha1.CiliumBGPInstance{
    25  		Name:     "cluster-1-instance-65001",
    26  		LocalASN: ptr.To[int64](65001),
    27  		Peers: []cilium_api_v2alpha1.CiliumBGPPeer{
    28  			{
    29  				Name:        "cluster-1-instance-65002-peer-10.0.0.2",
    30  				PeerAddress: ptr.To[string]("10.0.0.2"),
    31  				PeerASN:     ptr.To[int64](65002),
    32  				PeerConfigRef: &cilium_api_v2alpha1.PeerConfigReference{
    33  					Name: "peer-1",
    34  				},
    35  			},
    36  		},
    37  	}
    38  
    39  	expectedNode1 = cilium_api_v2alpha1.CiliumBGPNodeInstance{
    40  		Name:     "cluster-1-instance-65001",
    41  		LocalASN: ptr.To[int64](65001),
    42  		Peers: []cilium_api_v2alpha1.CiliumBGPNodePeer{
    43  			{
    44  				Name:        "cluster-1-instance-65002-peer-10.0.0.2",
    45  				PeerAddress: ptr.To[string]("10.0.0.2"),
    46  				PeerASN:     ptr.To[int64](65002),
    47  				PeerConfigRef: &cilium_api_v2alpha1.PeerConfigReference{
    48  					Name: "peer-1",
    49  				},
    50  			},
    51  		},
    52  	}
    53  
    54  	nodeOverride1 = cilium_api_v2alpha1.CiliumBGPNodeConfigOverrideSpec{
    55  		BGPInstances: []cilium_api_v2alpha1.CiliumBGPNodeConfigInstanceOverride{
    56  			{
    57  				Name:      "cluster-1-instance-65001",
    58  				RouterID:  ptr.To[string]("10.10.10.10"),
    59  				LocalPort: ptr.To[int32](5400),
    60  				Peers: []cilium_api_v2alpha1.CiliumBGPNodeConfigPeerOverride{
    61  					{
    62  						Name:         "cluster-1-instance-65002-peer-10.0.0.2",
    63  						LocalAddress: ptr.To[string]("10.10.10.1"),
    64  					},
    65  				},
    66  			},
    67  		},
    68  	}
    69  
    70  	expectedNodeWithOverride1 = cilium_api_v2alpha1.CiliumBGPNodeInstance{
    71  		Name:      "cluster-1-instance-65001",
    72  		LocalASN:  ptr.To[int64](65001),
    73  		RouterID:  ptr.To[string]("10.10.10.10"),
    74  		LocalPort: ptr.To[int32](5400),
    75  		Peers: []cilium_api_v2alpha1.CiliumBGPNodePeer{
    76  			{
    77  				Name:         "cluster-1-instance-65002-peer-10.0.0.2",
    78  				PeerAddress:  ptr.To[string]("10.0.0.2"),
    79  				PeerASN:      ptr.To[int64](65002),
    80  				LocalAddress: ptr.To[string]("10.10.10.1"),
    81  				PeerConfigRef: &cilium_api_v2alpha1.PeerConfigReference{
    82  					Name: "peer-1",
    83  				},
    84  			},
    85  		},
    86  	}
    87  )
    88  
    89  func Test_NodeLabels(t *testing.T) {
    90  	tests := []struct {
    91  		description        string
    92  		node               *cilium_api_v2.CiliumNode
    93  		clusterConfig      *cilium_api_v2alpha1.CiliumBGPClusterConfig
    94  		expectedNodeConfig *cilium_api_v2alpha1.CiliumBGPNodeConfig
    95  	}{
    96  		{
    97  			description: "node without any labels",
    98  			node: &cilium_api_v2.CiliumNode{
    99  				ObjectMeta: meta_v1.ObjectMeta{
   100  					Name: "node-1",
   101  				},
   102  			},
   103  			clusterConfig: &cilium_api_v2alpha1.CiliumBGPClusterConfig{
   104  				ObjectMeta: meta_v1.ObjectMeta{
   105  					Name: "bgp-cluster-config",
   106  				},
   107  				Spec: cilium_api_v2alpha1.CiliumBGPClusterConfigSpec{
   108  					NodeSelector: &slim_meta_v1.LabelSelector{
   109  						MatchLabels: map[string]string{
   110  							"bgp": "rack1",
   111  						},
   112  					},
   113  					BGPInstances: []cilium_api_v2alpha1.CiliumBGPInstance{
   114  						cluster1,
   115  					},
   116  				},
   117  			},
   118  			expectedNodeConfig: nil,
   119  		},
   120  		{
   121  			description: "node with label and cluster config with MatchLabels",
   122  			node: &cilium_api_v2.CiliumNode{
   123  				ObjectMeta: meta_v1.ObjectMeta{
   124  					Name: "node-1",
   125  					Labels: map[string]string{
   126  						"bgp": "rack1",
   127  					},
   128  				},
   129  			},
   130  			clusterConfig: &cilium_api_v2alpha1.CiliumBGPClusterConfig{
   131  				ObjectMeta: meta_v1.ObjectMeta{
   132  					Name: "bgp-cluster-config",
   133  				},
   134  				Spec: cilium_api_v2alpha1.CiliumBGPClusterConfigSpec{
   135  					NodeSelector: &slim_meta_v1.LabelSelector{
   136  						MatchLabels: map[string]string{
   137  							"bgp": "rack1",
   138  						},
   139  					},
   140  					BGPInstances: []cilium_api_v2alpha1.CiliumBGPInstance{
   141  						cluster1,
   142  					},
   143  				},
   144  			},
   145  			expectedNodeConfig: &cilium_api_v2alpha1.CiliumBGPNodeConfig{
   146  				ObjectMeta: meta_v1.ObjectMeta{
   147  					Name: "node-1",
   148  					OwnerReferences: []meta_v1.OwnerReference{
   149  						{
   150  							Kind: cilium_api_v2alpha1.BGPCCKindDefinition,
   151  							Name: "bgp-cluster-config",
   152  						},
   153  					},
   154  				},
   155  				Spec: cilium_api_v2alpha1.CiliumBGPNodeSpec{
   156  					BGPInstances: []cilium_api_v2alpha1.CiliumBGPNodeInstance{
   157  						expectedNode1,
   158  					},
   159  				},
   160  			},
   161  		},
   162  		{
   163  			description: "node with label and cluster config with MatchExpression",
   164  			node: &cilium_api_v2.CiliumNode{
   165  				ObjectMeta: meta_v1.ObjectMeta{
   166  					Name: "node-1",
   167  					Labels: map[string]string{
   168  						"bgp": "rack1",
   169  					},
   170  				},
   171  			},
   172  			clusterConfig: &cilium_api_v2alpha1.CiliumBGPClusterConfig{
   173  				ObjectMeta: meta_v1.ObjectMeta{
   174  					Name: "bgp-cluster-config",
   175  				},
   176  				Spec: cilium_api_v2alpha1.CiliumBGPClusterConfigSpec{
   177  					NodeSelector: &slim_meta_v1.LabelSelector{
   178  						MatchExpressions: []slim_meta_v1.LabelSelectorRequirement{
   179  							{
   180  								Key:      "bgp",
   181  								Operator: slim_meta_v1.LabelSelectorOpIn,
   182  								Values:   []string{"rack1"},
   183  							},
   184  						},
   185  					},
   186  					BGPInstances: []cilium_api_v2alpha1.CiliumBGPInstance{
   187  						cluster1,
   188  					},
   189  				},
   190  			},
   191  			expectedNodeConfig: &cilium_api_v2alpha1.CiliumBGPNodeConfig{
   192  				ObjectMeta: meta_v1.ObjectMeta{
   193  					Name: "node-1",
   194  					OwnerReferences: []meta_v1.OwnerReference{
   195  						{
   196  							Kind: cilium_api_v2alpha1.BGPCCKindDefinition,
   197  							Name: "bgp-cluster-config",
   198  						},
   199  					},
   200  				},
   201  				Spec: cilium_api_v2alpha1.CiliumBGPNodeSpec{
   202  					BGPInstances: []cilium_api_v2alpha1.CiliumBGPNodeInstance{
   203  						expectedNode1,
   204  					},
   205  				},
   206  			},
   207  		},
   208  		{
   209  			description: "node with label and cluster config with nil node selector",
   210  			node: &cilium_api_v2.CiliumNode{
   211  				ObjectMeta: meta_v1.ObjectMeta{
   212  					Name: "node-1",
   213  					Labels: map[string]string{
   214  						"bgp": "rack1",
   215  					},
   216  				},
   217  			},
   218  			clusterConfig: &cilium_api_v2alpha1.CiliumBGPClusterConfig{
   219  				ObjectMeta: meta_v1.ObjectMeta{
   220  					Name: "bgp-cluster-config",
   221  				},
   222  				Spec: cilium_api_v2alpha1.CiliumBGPClusterConfigSpec{
   223  					NodeSelector: nil,
   224  					BGPInstances: []cilium_api_v2alpha1.CiliumBGPInstance{
   225  						cluster1,
   226  					},
   227  				},
   228  			},
   229  			expectedNodeConfig: &cilium_api_v2alpha1.CiliumBGPNodeConfig{
   230  				ObjectMeta: meta_v1.ObjectMeta{
   231  					Name: "node-1",
   232  					OwnerReferences: []meta_v1.OwnerReference{
   233  						{
   234  							Kind: cilium_api_v2alpha1.BGPCCKindDefinition,
   235  							Name: "bgp-cluster-config",
   236  						},
   237  					},
   238  				},
   239  				Spec: cilium_api_v2alpha1.CiliumBGPNodeSpec{
   240  					BGPInstances: []cilium_api_v2alpha1.CiliumBGPNodeInstance{
   241  						expectedNode1,
   242  					},
   243  				},
   244  			},
   245  		},
   246  	}
   247  
   248  	ctx, cancel := context.WithTimeout(context.Background(), TestTimeout)
   249  	defer cancel()
   250  
   251  	for _, tt := range tests {
   252  		t.Run(tt.description, func(t *testing.T) {
   253  			req := require.New(t)
   254  
   255  			f, watcherReady := newFixture(ctx, req)
   256  
   257  			tlog := hivetest.Logger(t)
   258  			f.hive.Start(tlog, ctx)
   259  			defer f.hive.Stop(tlog, ctx)
   260  
   261  			// blocking till all watchers are ready
   262  			watcherReady()
   263  
   264  			// setup node
   265  			upsertNode(req, ctx, f, tt.node)
   266  
   267  			// upsert BGP cluster config
   268  			upsertBGPCC(req, ctx, f, tt.clusterConfig)
   269  
   270  			// validate node configs
   271  			assert.EventuallyWithT(t, func(c *assert.CollectT) {
   272  				nodeConfigs, err := f.bgpnClient.List(ctx, meta_v1.ListOptions{})
   273  				if err != nil {
   274  					assert.NoError(c, err)
   275  					return
   276  				}
   277  
   278  				if tt.expectedNodeConfig == nil {
   279  					assert.Len(c, nodeConfigs.Items, 0)
   280  					return
   281  				}
   282  
   283  				nodeConfig, err := f.bgpnClient.Get(ctx, tt.expectedNodeConfig.Name, meta_v1.GetOptions{})
   284  				if err != nil {
   285  					assert.NoError(c, err)
   286  					return
   287  				}
   288  
   289  				assert.True(c, isSameOwner(tt.expectedNodeConfig.GetOwnerReferences(), nodeConfig.GetOwnerReferences()))
   290  				assert.Equal(c, tt.expectedNodeConfig.Spec, nodeConfig.Spec)
   291  
   292  			}, TestTimeout, 50*time.Millisecond)
   293  		})
   294  	}
   295  }
   296  
   297  // Test_ClusterConfigSteps is step based test to validate the BGP node config controller
   298  func Test_ClusterConfigSteps(t *testing.T) {
   299  	steps := []struct {
   300  		description         string
   301  		clusterConfig       *cilium_api_v2alpha1.CiliumBGPClusterConfig
   302  		nodes               []*cilium_api_v2.CiliumNode
   303  		nodeOverrides       []*cilium_api_v2alpha1.CiliumBGPNodeConfigOverride
   304  		expectedNodeConfigs []*cilium_api_v2alpha1.CiliumBGPNodeConfig
   305  	}{
   306  		{
   307  			description:   "initial node setup",
   308  			clusterConfig: nil,
   309  			nodes: []*cilium_api_v2.CiliumNode{
   310  				{
   311  					ObjectMeta: meta_v1.ObjectMeta{
   312  						Name: "node-1",
   313  						Labels: map[string]string{
   314  							"bgp": "rack1",
   315  						},
   316  					},
   317  				},
   318  				{
   319  					ObjectMeta: meta_v1.ObjectMeta{
   320  						Name: "node-2",
   321  						Labels: map[string]string{
   322  							"bgp": "rack1",
   323  						},
   324  					},
   325  				},
   326  			},
   327  			nodeOverrides:       nil,
   328  			expectedNodeConfigs: nil,
   329  		},
   330  		{
   331  			description: "initial bgp cluster config",
   332  			clusterConfig: &cilium_api_v2alpha1.CiliumBGPClusterConfig{
   333  				ObjectMeta: meta_v1.ObjectMeta{
   334  					Name: "bgp-cluster-config",
   335  				},
   336  				Spec: cilium_api_v2alpha1.CiliumBGPClusterConfigSpec{
   337  					NodeSelector: &slim_meta_v1.LabelSelector{
   338  						MatchLabels: map[string]string{
   339  							"bgp": "rack1",
   340  						},
   341  					},
   342  					BGPInstances: []cilium_api_v2alpha1.CiliumBGPInstance{
   343  						cluster1,
   344  					},
   345  				},
   346  			},
   347  			nodes:         nil,
   348  			nodeOverrides: nil,
   349  			expectedNodeConfigs: []*cilium_api_v2alpha1.CiliumBGPNodeConfig{
   350  				{
   351  					ObjectMeta: meta_v1.ObjectMeta{
   352  						Name: "node-1",
   353  						OwnerReferences: []meta_v1.OwnerReference{
   354  							{
   355  								Name: "bgp-cluster-config",
   356  							},
   357  						},
   358  					},
   359  					Spec: cilium_api_v2alpha1.CiliumBGPNodeSpec{
   360  						BGPInstances: []cilium_api_v2alpha1.CiliumBGPNodeInstance{
   361  							expectedNode1,
   362  						},
   363  					},
   364  				},
   365  				{
   366  					ObjectMeta: meta_v1.ObjectMeta{
   367  						Name: "node-2",
   368  						OwnerReferences: []meta_v1.OwnerReference{
   369  							{
   370  								Name: "bgp-cluster-config",
   371  							},
   372  						},
   373  					},
   374  					Spec: cilium_api_v2alpha1.CiliumBGPNodeSpec{
   375  						BGPInstances: []cilium_api_v2alpha1.CiliumBGPNodeInstance{
   376  							expectedNode1,
   377  						},
   378  					},
   379  				},
   380  			},
   381  		},
   382  		{
   383  			description:   "add node override",
   384  			clusterConfig: nil,
   385  			nodes:         nil,
   386  			nodeOverrides: []*cilium_api_v2alpha1.CiliumBGPNodeConfigOverride{
   387  				{
   388  					ObjectMeta: meta_v1.ObjectMeta{
   389  						Name: "node-1",
   390  					},
   391  
   392  					Spec: nodeOverride1,
   393  				},
   394  			},
   395  			expectedNodeConfigs: []*cilium_api_v2alpha1.CiliumBGPNodeConfig{
   396  				{
   397  					ObjectMeta: meta_v1.ObjectMeta{
   398  						Name: "node-1",
   399  						OwnerReferences: []meta_v1.OwnerReference{
   400  							{
   401  								Name: "bgp-cluster-config",
   402  							},
   403  						},
   404  					},
   405  					Spec: cilium_api_v2alpha1.CiliumBGPNodeSpec{
   406  						BGPInstances: []cilium_api_v2alpha1.CiliumBGPNodeInstance{
   407  							expectedNodeWithOverride1,
   408  						},
   409  					},
   410  				},
   411  				{
   412  					ObjectMeta: meta_v1.ObjectMeta{
   413  						Name: "node-2",
   414  						OwnerReferences: []meta_v1.OwnerReference{
   415  							{
   416  								Name: "bgp-cluster-config",
   417  							},
   418  						},
   419  					},
   420  					Spec: cilium_api_v2alpha1.CiliumBGPNodeSpec{
   421  						BGPInstances: []cilium_api_v2alpha1.CiliumBGPNodeInstance{
   422  							expectedNode1,
   423  						},
   424  					},
   425  				},
   426  			},
   427  		},
   428  		{
   429  			description:   "add new node",
   430  			clusterConfig: nil,
   431  			nodes: []*cilium_api_v2.CiliumNode{
   432  				{
   433  					ObjectMeta: meta_v1.ObjectMeta{
   434  						Name: "node-3",
   435  						Labels: map[string]string{
   436  							"bgp": "rack1",
   437  						},
   438  					},
   439  				},
   440  			},
   441  			nodeOverrides: nil,
   442  			expectedNodeConfigs: []*cilium_api_v2alpha1.CiliumBGPNodeConfig{
   443  				{
   444  					ObjectMeta: meta_v1.ObjectMeta{
   445  						Name: "node-1",
   446  						OwnerReferences: []meta_v1.OwnerReference{
   447  							{
   448  								Name: "bgp-cluster-config",
   449  							},
   450  						},
   451  					},
   452  					Spec: cilium_api_v2alpha1.CiliumBGPNodeSpec{
   453  						BGPInstances: []cilium_api_v2alpha1.CiliumBGPNodeInstance{
   454  							expectedNodeWithOverride1,
   455  						},
   456  					},
   457  				},
   458  				{
   459  					ObjectMeta: meta_v1.ObjectMeta{
   460  						Name: "node-2",
   461  						OwnerReferences: []meta_v1.OwnerReference{
   462  							{
   463  								Name: "bgp-cluster-config",
   464  							},
   465  						},
   466  					},
   467  					Spec: cilium_api_v2alpha1.CiliumBGPNodeSpec{
   468  						BGPInstances: []cilium_api_v2alpha1.CiliumBGPNodeInstance{
   469  							expectedNode1,
   470  						},
   471  					},
   472  				},
   473  				{
   474  					ObjectMeta: meta_v1.ObjectMeta{
   475  						Name: "node-3",
   476  						OwnerReferences: []meta_v1.OwnerReference{
   477  							{
   478  								Name: "bgp-cluster-config",
   479  							},
   480  						},
   481  					},
   482  					Spec: cilium_api_v2alpha1.CiliumBGPNodeSpec{
   483  						BGPInstances: []cilium_api_v2alpha1.CiliumBGPNodeInstance{
   484  							expectedNode1,
   485  						},
   486  					},
   487  				},
   488  			},
   489  		},
   490  		{
   491  			description:   "remove node labels",
   492  			clusterConfig: nil,
   493  			nodes: []*cilium_api_v2.CiliumNode{
   494  				{
   495  					ObjectMeta: meta_v1.ObjectMeta{
   496  						Name: "node-1",
   497  					},
   498  				},
   499  				{
   500  					ObjectMeta: meta_v1.ObjectMeta{
   501  						Name: "node-2",
   502  					},
   503  				},
   504  				{
   505  					ObjectMeta: meta_v1.ObjectMeta{
   506  						Name: "node-3",
   507  					},
   508  				},
   509  			},
   510  			nodeOverrides:       nil,
   511  			expectedNodeConfigs: nil,
   512  		},
   513  	}
   514  
   515  	ctx, cancel := context.WithTimeout(context.Background(), TestTimeout)
   516  	defer cancel()
   517  
   518  	f, watchersReady := newFixture(ctx, require.New(t))
   519  
   520  	tlog := hivetest.Logger(t)
   521  	f.hive.Start(tlog, ctx)
   522  	defer f.hive.Stop(tlog, ctx)
   523  
   524  	watchersReady()
   525  
   526  	for _, step := range steps {
   527  		t.Run(step.description, func(t *testing.T) {
   528  			req := require.New(t)
   529  
   530  			// setup nodes
   531  			for _, node := range step.nodes {
   532  				upsertNode(req, ctx, f, node)
   533  			}
   534  
   535  			// upsert BGP cluster config
   536  			upsertBGPCC(req, ctx, f, step.clusterConfig)
   537  
   538  			// upsert node overrides
   539  			for _, nodeOverride := range step.nodeOverrides {
   540  				upsertNodeOverrides(req, ctx, f, nodeOverride)
   541  			}
   542  
   543  			// validate node configs
   544  			assert.EventuallyWithT(t, func(c *assert.CollectT) {
   545  				nodes, err := f.bgpnClient.List(ctx, meta_v1.ListOptions{})
   546  				if err != nil {
   547  					assert.NoError(c, err)
   548  					return
   549  				}
   550  
   551  				assert.Equal(c, len(step.expectedNodeConfigs), len(nodes.Items))
   552  
   553  				for _, expectedNodeConfig := range step.expectedNodeConfigs {
   554  					nodeConfig, err := f.bgpnClient.Get(ctx, expectedNodeConfig.Name, meta_v1.GetOptions{})
   555  					if err != nil {
   556  						assert.NoError(c, err)
   557  						return
   558  					}
   559  
   560  					assert.Equal(c, expectedNodeConfig.Name, nodeConfig.Name)
   561  					assert.Equal(c, expectedNodeConfig.Spec, nodeConfig.Spec)
   562  				}
   563  			}, TestTimeout, 50*time.Millisecond)
   564  		})
   565  	}
   566  }
   567  
   568  func upsertNode(req *require.Assertions, ctx context.Context, f *fixture, node *cilium_api_v2.CiliumNode) {
   569  	_, err := f.nodeClient.Get(ctx, node.Name, meta_v1.GetOptions{})
   570  	if err != nil && k8sErrors.IsNotFound(err) {
   571  		_, err = f.nodeClient.Create(ctx, node, meta_v1.CreateOptions{})
   572  	} else if err != nil {
   573  		req.Fail(err.Error())
   574  	} else {
   575  		_, err = f.nodeClient.Update(ctx, node, meta_v1.UpdateOptions{})
   576  	}
   577  	req.NoError(err)
   578  }
   579  
   580  func upsertBGPCC(req *require.Assertions, ctx context.Context, f *fixture, bgpcc *cilium_api_v2alpha1.CiliumBGPClusterConfig) {
   581  	if bgpcc == nil {
   582  		return
   583  	}
   584  
   585  	_, err := f.bgpcClient.Get(ctx, bgpcc.Name, meta_v1.GetOptions{})
   586  	if err != nil && k8sErrors.IsNotFound(err) {
   587  		_, err = f.bgpcClient.Create(ctx, bgpcc, meta_v1.CreateOptions{})
   588  	} else if err != nil {
   589  		req.Fail(err.Error())
   590  	} else {
   591  		_, err = f.bgpcClient.Update(ctx, bgpcc, meta_v1.UpdateOptions{})
   592  	}
   593  	req.NoError(err)
   594  }
   595  
   596  func upsertNodeOverrides(req *require.Assertions, ctx context.Context, f *fixture, nodeOverride *cilium_api_v2alpha1.CiliumBGPNodeConfigOverride) {
   597  	_, err := f.bgpncoClient.Get(ctx, nodeOverride.Name, meta_v1.GetOptions{})
   598  	if err != nil && k8sErrors.IsNotFound(err) {
   599  		_, err = f.bgpncoClient.Create(ctx, nodeOverride, meta_v1.CreateOptions{})
   600  	} else {
   601  		_, err = f.bgpncoClient.Update(ctx, nodeOverride, meta_v1.UpdateOptions{})
   602  	}
   603  	req.NoError(err)
   604  }
   605  
   606  func isSameOwner(expectedOwners, runningOwners []meta_v1.OwnerReference) bool {
   607  	if len(expectedOwners) != len(runningOwners) {
   608  		return false
   609  	}
   610  
   611  	for i, owner := range expectedOwners {
   612  		if runningOwners[i].Kind != owner.Kind || runningOwners[i].Name != owner.Name {
   613  			return false
   614  		}
   615  	}
   616  	return true
   617  }