github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controllerutil/config_util_test.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package controllerutil
    21  
    22  import (
    23  	"encoding/json"
    24  	"reflect"
    25  	"testing"
    26  
    27  	. "github.com/onsi/ginkgo/v2"
    28  	. "github.com/onsi/gomega"
    29  
    30  	"github.com/StudioSol/set"
    31  	corev1 "k8s.io/api/core/v1"
    32  
    33  	"github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    34  	"github.com/1aal/kubeblocks/pkg/configuration/core"
    35  	cfgutil "github.com/1aal/kubeblocks/pkg/configuration/util"
    36  	"github.com/1aal/kubeblocks/pkg/constant"
    37  	"github.com/1aal/kubeblocks/pkg/controller/builder"
    38  	testapps "github.com/1aal/kubeblocks/pkg/testutil/apps"
    39  	testutil "github.com/1aal/kubeblocks/pkg/testutil/k8s"
    40  	"github.com/1aal/kubeblocks/test/testdata"
    41  )
    42  
    43  func TestFromUpdatedConfig(t *testing.T) {
    44  	type args struct {
    45  		base map[string]string
    46  		sets *set.LinkedHashSetString
    47  	}
    48  	tests := []struct {
    49  		name string
    50  		args args
    51  		want map[string]string
    52  	}{{
    53  		name: "normal_test",
    54  		args: args{
    55  			base: map[string]string{
    56  				"key1": "config context1",
    57  				"key2": "config context2",
    58  				"key3": "config context2",
    59  			},
    60  			sets: set.NewLinkedHashSetString("key1", "key3"),
    61  		},
    62  		want: map[string]string{
    63  			"key1": "config context1",
    64  			"key3": "config context2",
    65  		},
    66  	}, {
    67  		name: "none_updated_test",
    68  		args: args{
    69  			base: map[string]string{
    70  				"key1": "config context1",
    71  				"key2": "config context2",
    72  				"key3": "config context2",
    73  			},
    74  			sets: cfgutil.NewSet(),
    75  		},
    76  		want: map[string]string{},
    77  	}}
    78  	for _, tt := range tests {
    79  		t.Run(tt.name, func(t *testing.T) {
    80  			if got := fromUpdatedConfig(tt.args.base, tt.args.sets); !reflect.DeepEqual(got, tt.want) {
    81  				t.Errorf("fromUpdatedConfig() = %v, want %v", got, tt.want)
    82  			}
    83  		})
    84  	}
    85  }
    86  
    87  func TestIsRerender(t *testing.T) {
    88  	type args struct {
    89  		cm   *corev1.ConfigMap
    90  		item v1alpha1.ConfigurationItemDetail
    91  	}
    92  	tests := []struct {
    93  		name string
    94  		args args
    95  		want bool
    96  	}{{
    97  
    98  		name: "test",
    99  		args: args{
   100  			cm: nil,
   101  			item: v1alpha1.ConfigurationItemDetail{
   102  				Name: "test",
   103  			},
   104  		},
   105  		want: true,
   106  	}, {
   107  		name: "test",
   108  		args: args{
   109  			cm: builder.NewConfigMapBuilder("default", "test").GetObject(),
   110  			item: v1alpha1.ConfigurationItemDetail{
   111  				Name: "test",
   112  			},
   113  		},
   114  		want: false,
   115  	}, {
   116  		name: "test",
   117  		args: args{
   118  			cm: builder.NewConfigMapBuilder("default", "test").
   119  				GetObject(),
   120  			item: v1alpha1.ConfigurationItemDetail{
   121  				Name:    "test",
   122  				Version: "v1",
   123  			},
   124  		},
   125  		want: true,
   126  	}, {
   127  		name: "test",
   128  		args: args{
   129  			cm: builder.NewConfigMapBuilder("default", "test").
   130  				AddAnnotations(constant.CMConfigurationTemplateVersion, "v1").
   131  				GetObject(),
   132  			item: v1alpha1.ConfigurationItemDetail{
   133  				Name:    "test",
   134  				Version: "v2",
   135  			},
   136  		},
   137  		want: true,
   138  	}, {
   139  		name: "test",
   140  		args: args{
   141  			cm: builder.NewConfigMapBuilder("default", "test").
   142  				AddAnnotations(constant.CMConfigurationTemplateVersion, "v1").
   143  				GetObject(),
   144  			item: v1alpha1.ConfigurationItemDetail{
   145  				Name:    "test",
   146  				Version: "v1",
   147  			},
   148  		},
   149  		want: false,
   150  	}}
   151  	for _, tt := range tests {
   152  		t.Run(tt.name, func(t *testing.T) {
   153  			if got := IsRerender(tt.args.cm, tt.args.item); got != tt.want {
   154  				t.Errorf("IsRerender() = %v, want %v", got, tt.want)
   155  			}
   156  		})
   157  	}
   158  }
   159  
   160  func TestGetConfigSpecReconcilePhase(t *testing.T) {
   161  	type args struct {
   162  		cm     *corev1.ConfigMap
   163  		item   v1alpha1.ConfigurationItemDetail
   164  		status *v1alpha1.ConfigurationItemDetailStatus
   165  	}
   166  	tests := []struct {
   167  		name string
   168  		args args
   169  		want v1alpha1.ConfigurationPhase
   170  	}{{
   171  		name: "test",
   172  		args: args{
   173  			cm: nil,
   174  			item: v1alpha1.ConfigurationItemDetail{
   175  				Name: "test",
   176  			},
   177  		},
   178  		want: v1alpha1.CCreatingPhase,
   179  	}, {
   180  		name: "test",
   181  		args: args{
   182  			cm: builder.NewConfigMapBuilder("default", "test").GetObject(),
   183  			item: v1alpha1.ConfigurationItemDetail{
   184  				Name: "test",
   185  			},
   186  			status: &v1alpha1.ConfigurationItemDetailStatus{
   187  				Phase: v1alpha1.CInitPhase,
   188  			},
   189  		},
   190  		want: v1alpha1.CPendingPhase,
   191  	}, {
   192  		name: "test",
   193  		args: args{
   194  			cm: builder.NewConfigMapBuilder("default", "test").
   195  				AddAnnotations(constant.ConfigAppliedVersionAnnotationKey, `{"name":"test"}`).
   196  				GetObject(),
   197  			item: v1alpha1.ConfigurationItemDetail{
   198  				Name: "test",
   199  			},
   200  			status: &v1alpha1.ConfigurationItemDetailStatus{
   201  				Phase: v1alpha1.CUpgradingPhase,
   202  			},
   203  		},
   204  		want: v1alpha1.CUpgradingPhase,
   205  	}}
   206  	for _, tt := range tests {
   207  		t.Run(tt.name, func(t *testing.T) {
   208  			if got := GetConfigSpecReconcilePhase(tt.args.cm, tt.args.item, tt.args.status); got != tt.want {
   209  				t.Errorf("GetConfigSpecReconcilePhase() = %v, want %v", got, tt.want)
   210  			}
   211  		})
   212  	}
   213  }
   214  
   215  var _ = Describe("config_util", func() {
   216  
   217  	var k8sMockClient *testutil.K8sClientMockHelper
   218  
   219  	BeforeEach(func() {
   220  		// Add any setup steps that needs to be executed before each test
   221  		k8sMockClient = testutil.NewK8sMockClient()
   222  	})
   223  
   224  	AfterEach(func() {
   225  		// Add any teardown steps that needs to be executed after each test
   226  		k8sMockClient.Finish()
   227  	})
   228  
   229  	Context("MergeAndValidateConfigs", func() {
   230  		It("Should succeed with no error", func() {
   231  			type args struct {
   232  				configConstraint v1alpha1.ConfigConstraintSpec
   233  				baseCfg          map[string]string
   234  				updatedParams    []core.ParamPairs
   235  				cmKeys           []string
   236  			}
   237  
   238  			configConstraintObj := testapps.NewCustomizedObj("resources/mysql-config-constraint.yaml",
   239  				&v1alpha1.ConfigConstraint{}, func(cc *v1alpha1.ConfigConstraint) {
   240  					if ccContext, err := testdata.GetTestDataFileContent("cue_testdata/pg14.cue"); err == nil {
   241  						cc.Spec.ConfigurationSchema = &v1alpha1.CustomParametersValidation{
   242  							CUE: string(ccContext),
   243  						}
   244  					}
   245  					cc.Spec.FormatterConfig = &v1alpha1.FormatterConfig{
   246  						Format: v1alpha1.Properties,
   247  					}
   248  				})
   249  
   250  			cfgContext, err := testdata.GetTestDataFileContent("cue_testdata/pg14.conf")
   251  			Expect(err).Should(Succeed())
   252  
   253  			tests := []struct {
   254  				name    string
   255  				args    args
   256  				want    map[string]string
   257  				wantErr bool
   258  			}{{
   259  				name: "pg1_merge",
   260  				args: args{
   261  					configConstraint: configConstraintObj.Spec,
   262  					baseCfg: map[string]string{
   263  						"key":  string(cfgContext),
   264  						"key2": "not support context",
   265  					},
   266  					updatedParams: []core.ParamPairs{
   267  						{
   268  							Key: "key",
   269  							UpdatedParams: map[string]interface{}{
   270  								"max_connections": "200",
   271  								"shared_buffers":  "512M",
   272  							},
   273  						},
   274  					},
   275  					cmKeys: []string{"key", "key3"},
   276  				},
   277  				want: map[string]string{
   278  					"max_connections": "200",
   279  					"shared_buffers":  "512M",
   280  				},
   281  			}, {
   282  				name: "not_support_key_updated",
   283  				args: args{
   284  					configConstraint: configConstraintObj.Spec,
   285  					baseCfg: map[string]string{
   286  						"key":  string(cfgContext),
   287  						"key2": "not_support_context",
   288  					},
   289  					updatedParams: []core.ParamPairs{
   290  						{
   291  							Key: "key",
   292  							UpdatedParams: map[string]interface{}{
   293  								"max_connections": "200",
   294  								"shared_buffers":  "512M",
   295  							},
   296  						},
   297  					},
   298  					cmKeys: []string{"key1", "key2"},
   299  				},
   300  				wantErr: true,
   301  			}}
   302  			for _, tt := range tests {
   303  				got, err := MergeAndValidateConfigs(tt.args.configConstraint, tt.args.baseCfg, tt.args.cmKeys, tt.args.updatedParams)
   304  				Expect(err != nil).Should(BeEquivalentTo(tt.wantErr))
   305  				if tt.wantErr {
   306  					continue
   307  				}
   308  
   309  				option := core.CfgOption{
   310  					Type:    core.CfgTplType,
   311  					CfgType: tt.args.configConstraint.FormatterConfig.Format,
   312  				}
   313  
   314  				patch, err := core.CreateMergePatch(&core.ConfigResource{
   315  					ConfigData: tt.args.baseCfg,
   316  				}, &core.ConfigResource{
   317  					ConfigData: got,
   318  				}, option)
   319  				Expect(err).Should(Succeed())
   320  
   321  				var patchJSON map[string]string
   322  				Expect(json.Unmarshal(patch.UpdateConfig["key"], &patchJSON)).Should(Succeed())
   323  				Expect(patchJSON).Should(BeEquivalentTo(tt.want))
   324  			}
   325  		})
   326  	})
   327  
   328  })