github.com/banzaicloud/operator-tools@v0.28.10/pkg/merge/merge_test.go (about)

     1  // Copyright © 2020 Banzai Cloud
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package merge
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/banzaicloud/operator-tools/pkg/typeoverride"
    21  	"github.com/banzaicloud/operator-tools/pkg/utils"
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  	v1 "k8s.io/api/apps/v1"
    25  	corev1 "k8s.io/api/core/v1"
    26  	"k8s.io/apimachinery/pkg/api/resource"
    27  	v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/util/intstr"
    29  )
    30  
    31  func TestMerge(t *testing.T) {
    32  	base := &v1.DaemonSet{
    33  		Spec: v1.DaemonSetSpec{
    34  			Template: corev1.PodTemplateSpec{
    35  				Spec: corev1.PodSpec{
    36  					Containers: []corev1.Container{
    37  						{
    38  							Name:  "container-a",
    39  							Image: "image-a",
    40  						},
    41  						{
    42  							Name:  "container-b",
    43  							Image: "image-b",
    44  							Resources: corev1.ResourceRequirements{
    45  								Limits: corev1.ResourceList{
    46  									corev1.ResourceCPU:    resource.MustParse("100m"),
    47  									corev1.ResourceMemory: resource.MustParse("100M"),
    48  								},
    49  							},
    50  						},
    51  						{
    52  							Name:  "container-c",
    53  							Image: "image-c",
    54  							// make sure we keep extra fields on the original slice item
    55  							Command: []string{"fake"},
    56  						},
    57  					},
    58  				},
    59  			},
    60  		},
    61  	}
    62  	overrides := &v1.DaemonSet{
    63  		Spec: v1.DaemonSetSpec{
    64  			Template: corev1.PodTemplateSpec{
    65  				Spec: corev1.PodSpec{
    66  					Containers: []corev1.Container{
    67  						{
    68  							Name:  "container-a",
    69  							Image: "image-a-2",
    70  						},
    71  						{
    72  							Name: "container-b",
    73  							Resources: corev1.ResourceRequirements{
    74  								Limits: corev1.ResourceList{
    75  									corev1.ResourceCPU: resource.MustParse("123m"),
    76  								},
    77  							},
    78  						},
    79  						{
    80  							Name:  "container-d",
    81  							Image: "image-d",
    82  						},
    83  					},
    84  				},
    85  			},
    86  		},
    87  	}
    88  
    89  	err := Merge(base, overrides)
    90  	require.NoError(t, err)
    91  
    92  	assert.Len(t, base.Spec.Template.Spec.Containers, 4)
    93  
    94  	// container a has a modified image
    95  	assert.Equal(t, "container-a", base.Spec.Template.Spec.Containers[0].Name)
    96  	assert.Equal(t, "image-a-2", base.Spec.Template.Spec.Containers[0].Image)
    97  	assert.Equal(t, corev1.ResourceRequirements{}, base.Spec.Template.Spec.Containers[0].Resources)
    98  
    99  	// container b has the same image but updated resource requirements
   100  	assert.Equal(t, "container-b", base.Spec.Template.Spec.Containers[1].Name)
   101  	assert.Equal(t, "image-b", base.Spec.Template.Spec.Containers[1].Image)
   102  	assert.Equal(t, corev1.ResourceRequirements{
   103  		Limits: corev1.ResourceList{
   104  			corev1.ResourceCPU:    resource.MustParse("123m"),
   105  			corev1.ResourceMemory: resource.MustParse("100M"),
   106  		},
   107  	}, base.Spec.Template.Spec.Containers[1].Resources)
   108  
   109  	// container d is added as a new item (note that it will come before container-c)
   110  	assert.Equal(t, corev1.Container{
   111  		Name:  "container-d",
   112  		Image: "image-d",
   113  	}, base.Spec.Template.Spec.Containers[2])
   114  
   115  	// container c is not modified (note that it's index will change)
   116  	assert.Equal(t, corev1.Container{
   117  		Name:  "container-c",
   118  		Image: "image-c",
   119  		Command: []string{"fake"},
   120  	}, base.Spec.Template.Spec.Containers[3])
   121  }
   122  
   123  func TestVolume(t *testing.T) {
   124  	base := &v1.DaemonSet{
   125  		Spec: v1.DaemonSetSpec{
   126  			Template: corev1.PodTemplateSpec{
   127  				Spec: corev1.PodSpec{
   128  					Containers: []corev1.Container{
   129  						{
   130  							Name:  "container",
   131  							Image: "image",
   132  							VolumeMounts: []corev1.VolumeMount{
   133  								{
   134  									Name:      "asd",
   135  									MountPath: "/asd-path",
   136  									ReadOnly:  true,
   137  								},
   138  							},
   139  						},
   140  					},
   141  				},
   142  			},
   143  		},
   144  	}
   145  	overrides := &v1.DaemonSet{
   146  		Spec: v1.DaemonSetSpec{
   147  			Template: corev1.PodTemplateSpec{
   148  				Spec: corev1.PodSpec{
   149  					Containers: []corev1.Container{
   150  						{
   151  							Name:  "container",
   152  							Image: "image",
   153  							VolumeMounts: []corev1.VolumeMount{
   154  								{
   155  									Name:      "different",
   156  									MountPath: "/different-path",
   157  								},
   158  							},
   159  						},
   160  					},
   161  				},
   162  			},
   163  		},
   164  	}
   165  
   166  	err := Merge(base, overrides)
   167  	require.NoError(t, err)
   168  
   169  	require.Equal(t,  &v1.DaemonSet{
   170  		Spec: v1.DaemonSetSpec{
   171  			Template: corev1.PodTemplateSpec{
   172  				Spec: corev1.PodSpec{
   173  					Containers: []corev1.Container{
   174  						{
   175  							Name:  "container",
   176  							Image: "image",
   177  							VolumeMounts: []corev1.VolumeMount{
   178  								{
   179  									Name:      "different",
   180  									MountPath: "/different-path",
   181  								},
   182  								{
   183  									Name:      "asd",
   184  									MountPath: "/asd-path",
   185  									ReadOnly:  true,
   186  								},
   187  							},
   188  						},
   189  					},
   190  				},
   191  			},
   192  		},
   193  	}, base)
   194  }
   195  
   196  func TestMergeWithEmbeddedType(t *testing.T) {
   197  	base := &v1.DaemonSet{
   198  		Spec: v1.DaemonSetSpec{
   199  			Template: corev1.PodTemplateSpec{
   200  				Spec: corev1.PodSpec{
   201  					Containers: []corev1.Container{
   202  						{
   203  							Name:  "container-a",
   204  							Image: "image-a",
   205  						},
   206  						{
   207  							Name:  "container-b",
   208  							Image: "image-b",
   209  							Resources: corev1.ResourceRequirements{
   210  								Limits: corev1.ResourceList{
   211  									corev1.ResourceCPU:    resource.MustParse("100m"),
   212  									corev1.ResourceMemory: resource.MustParse("100M"),
   213  								},
   214  							},
   215  						},
   216  						{
   217  							Name:  "container-c",
   218  							Image: "image-c",
   219  						},
   220  					},
   221  				},
   222  			},
   223  		},
   224  	}
   225  	overrides := &typeoverride.DaemonSet{
   226  		Spec: typeoverride.DaemonSetSpec{
   227  			Template: typeoverride.PodTemplateSpec{
   228  				Spec: typeoverride.PodSpec{
   229  					Containers: []corev1.Container{
   230  						{
   231  							Name:  "container-a",
   232  							Image: "image-a-2",
   233  						},
   234  						{
   235  							Name: "container-b",
   236  							Resources: corev1.ResourceRequirements{
   237  								Limits: corev1.ResourceList{
   238  									corev1.ResourceCPU: resource.MustParse("123m"),
   239  								},
   240  							},
   241  						},
   242  						{
   243  							Name:  "container-d",
   244  							Image: "image-d",
   245  						},
   246  					},
   247  				},
   248  			},
   249  		},
   250  	}
   251  
   252  	err := Merge(base, overrides)
   253  	require.NoError(t, err)
   254  
   255  	assert.Len(t, base.Spec.Template.Spec.Containers, 4)
   256  
   257  	// container a has a modified image
   258  	assert.Equal(t, "container-a", base.Spec.Template.Spec.Containers[0].Name)
   259  	assert.Equal(t, "image-a-2", base.Spec.Template.Spec.Containers[0].Image)
   260  	assert.Equal(t, corev1.ResourceRequirements{}, base.Spec.Template.Spec.Containers[0].Resources)
   261  
   262  	// container b has the same image but updated resource requirements
   263  	assert.Equal(t, "container-b", base.Spec.Template.Spec.Containers[1].Name)
   264  	assert.Equal(t, "image-b", base.Spec.Template.Spec.Containers[1].Image)
   265  	assert.Equal(t, corev1.ResourceRequirements{
   266  		Limits: corev1.ResourceList{
   267  			corev1.ResourceCPU:    resource.MustParse("123m"),
   268  			corev1.ResourceMemory: resource.MustParse("100M"),
   269  		},
   270  	}, base.Spec.Template.Spec.Containers[1].Resources)
   271  
   272  	// container d is added as a new item (note that it will come before container-c)
   273  	assert.Equal(t, corev1.Container{
   274  		Name:  "container-d",
   275  		Image: "image-d",
   276  	}, base.Spec.Template.Spec.Containers[2])
   277  
   278  	// container c is not modified (note that it's index will change)
   279  	assert.Equal(t, corev1.Container{
   280  		Name:  "container-c",
   281  		Image: "image-c",
   282  	}, base.Spec.Template.Spec.Containers[3])
   283  }
   284  
   285  func TestMergeStatefulSetReplicas(t *testing.T) {
   286  	base := &v1.StatefulSet{
   287  		Spec: v1.StatefulSetSpec{
   288  			Replicas: utils.IntPointer(1),
   289  		},
   290  	}
   291  	overrides := v1.StatefulSet{
   292  		Spec: v1.StatefulSetSpec{
   293  			Replicas: utils.IntPointer(0),
   294  		},
   295  	}
   296  	err := Merge(base, overrides)
   297  	require.NoError(t, err)
   298  
   299  	assert.Equal(t, *base.Spec.Replicas, int32(0))
   300  }
   301  
   302  func TestMergeArrayOverride(t *testing.T) {
   303  	base := &corev1.Service{
   304  		Spec: corev1.ServiceSpec{
   305  			ExternalIPs: []string{
   306  				"a", "b",
   307  			},
   308  		},
   309  	}
   310  	overrides := &typeoverride.Service{
   311  		Spec: corev1.ServiceSpec{
   312  			ExternalIPs: []string{
   313  				"c", "d",
   314  			},
   315  		},
   316  	}
   317  
   318  	err := Merge(base, overrides)
   319  	require.NoError(t, err)
   320  
   321  	require.Equal(t, []string{"c", "d"}, base.Spec.ExternalIPs)
   322  }
   323  
   324  func TestMergeMap(t *testing.T) {
   325  	base := &corev1.Service{
   326  		ObjectMeta: v12.ObjectMeta{
   327  			Labels: map[string]string{
   328  				"a": "1",
   329  				"b": "2",
   330  			},
   331  		},
   332  	}
   333  	overrides := &corev1.Service{
   334  		ObjectMeta: v12.ObjectMeta{
   335  			Labels: map[string]string{
   336  				"b": "3",
   337  				"c": "4",
   338  			},
   339  		},
   340  	}
   341  
   342  	err := Merge(base, overrides)
   343  	require.NoError(t, err)
   344  
   345  	require.Equal(t, map[string]string{
   346  		"a": "1",
   347  		"b": "3",
   348  		"c": "4",
   349  	}, base.ObjectMeta.Labels)
   350  }
   351  
   352  func TestMergeMapWithEmbeddedType(t *testing.T) {
   353  	base := &corev1.Service{
   354  		ObjectMeta: v12.ObjectMeta{
   355  			Labels: map[string]string{
   356  				"a": "1",
   357  				"b": "2",
   358  			},
   359  		},
   360  	}
   361  	overrides := &typeoverride.Service{
   362  		ObjectMeta: typeoverride.ObjectMeta{
   363  			Labels: map[string]string{
   364  				"b": "3",
   365  				"c": "4",
   366  			},
   367  		},
   368  	}
   369  
   370  	err := Merge(base, overrides)
   371  	require.NoError(t, err)
   372  
   373  	require.Equal(t, map[string]string{
   374  		"a": "1",
   375  		"b": "3",
   376  		"c": "4",
   377  	}, base.ObjectMeta.Labels)
   378  }
   379  
   380  func TestMergeService(t *testing.T) {
   381  	base := &corev1.Service{
   382  		ObjectMeta: v12.ObjectMeta{
   383  			Labels: map[string]string{
   384  				"a": "1",
   385  				"b": "2",
   386  			},
   387  		},
   388  		Spec: corev1.ServiceSpec{
   389  			Ports: []corev1.ServicePort{
   390  				{
   391  					Name:       "http",
   392  					Port:       80,
   393  					TargetPort: intstr.FromInt(8080),
   394  				},
   395  			},
   396  			Selector: map[string]string{
   397  				"a": "1",
   398  				"b": "2",
   399  			},
   400  			LoadBalancerIP: "1.2.3.4",
   401  		},
   402  	}
   403  	overrides := &typeoverride.Service{
   404  		ObjectMeta: typeoverride.ObjectMeta{
   405  			Labels: map[string]string{
   406  				"b": "3",
   407  				"c": "4",
   408  			},
   409  		},
   410  		Spec: corev1.ServiceSpec{
   411  			Ports: []corev1.ServicePort{
   412  				{
   413  					Name:       "http1",
   414  					Port:       80,
   415  					TargetPort: intstr.FromInt(8081),
   416  				},
   417  				{
   418  					Name:       "http2",
   419  					Port:       82,
   420  					TargetPort: intstr.FromInt(8082),
   421  				},
   422  			},
   423  			Selector:map[string]string{
   424  				"b": "3",
   425  				"c": "4",
   426  			},
   427  		},
   428  	}
   429  
   430  	err := Merge(base, overrides)
   431  	require.NoError(t, err)
   432  
   433  	require.Equal(t, map[string]string{
   434  		"a": "1",
   435  		"b": "3",
   436  		"c": "4",
   437  	}, base.ObjectMeta.Labels)
   438  
   439  	require.Equal(t, base.Spec, corev1.ServiceSpec{
   440  		Ports: []corev1.ServicePort{
   441  			{
   442  				Name:       "http1",
   443  				Port:       80,
   444  				TargetPort: intstr.FromInt(8081),
   445  			},
   446  			{
   447  				Name:       "http2",
   448  				Port:       82,
   449  				TargetPort: intstr.FromInt(8082),
   450  			},
   451  		},
   452  		Selector: map[string]string{
   453  			"a": "1",
   454  			"b": "3",
   455  			"c": "4",
   456  		},
   457  		LoadBalancerIP: "1.2.3.4",
   458  	})
   459  }