github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/configuration/core/reconfigure_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 core
    21  
    22  import (
    23  	"testing"
    24  
    25  	"github.com/StudioSol/set"
    26  	"github.com/stretchr/testify/require"
    27  	corev1 "k8s.io/api/core/v1"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"sigs.k8s.io/controller-runtime/pkg/client"
    30  
    31  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    32  	"github.com/1aal/kubeblocks/pkg/configuration/util"
    33  	"github.com/1aal/kubeblocks/pkg/constant"
    34  )
    35  
    36  func TestGetUpdateParameterList(t *testing.T) {
    37  	testData := `{
    38  	"a": "b",
    39  	"f": 10.2,
    40  	"c": [
    41  		"edcl",
    42  		"cde"
    43  	],
    44  	"d" : [],
    45  	"n" : [{}],
    46  	"xxx" : [
    47  		{
    48  			"test1": 2,
    49  			"test2": 5
    50  		}
    51  	],
    52  	"g": {
    53  		"cd" : "abcd",
    54  		"msld" :  {
    55  			"cakl": 100,
    56  			"dg": "abcd"
    57  		}
    58  	}}
    59  `
    60  	expected := set.NewLinkedHashSetString("a", "f", "c", "xxx.test1", "xxx.test2", "g.msld.cakl", "g.msld.dg", "g.cd")
    61  	params, err := getUpdateParameterList(newCfgDiffMeta(testData, nil, nil), "")
    62  	require.Nil(t, err)
    63  	require.True(t, util.EqSet(expected,
    64  		set.NewLinkedHashSetString(params...)), "param: %v, expected: %v", params, expected.AsSlice())
    65  
    66  	// for trim
    67  	expected = set.NewLinkedHashSetString("msld.cakl", "msld.dg", "cd")
    68  	params, err = getUpdateParameterList(newCfgDiffMeta(testData, nil, nil), "g")
    69  	require.Nil(t, err)
    70  	require.True(t, util.EqSet(expected,
    71  		set.NewLinkedHashSetString(params...)), "param: %v, expected: %v", params, expected.AsSlice())
    72  
    73  }
    74  
    75  func newCfgDiffMeta(testData string, add, delete map[string]interface{}) *ConfigPatchInfo {
    76  	return &ConfigPatchInfo{
    77  		UpdateConfig: map[string][]byte{
    78  			"test": []byte(testData),
    79  		},
    80  		AddConfig:    add,
    81  		DeleteConfig: delete,
    82  	}
    83  }
    84  
    85  func TestIsUpdateDynamicParameters(t *testing.T) {
    86  	type args struct {
    87  		ccSpec *appsv1alpha1.ConfigConstraintSpec
    88  		diff   *ConfigPatchInfo
    89  	}
    90  	tests := []struct {
    91  		name    string
    92  		args    args
    93  		want    bool
    94  		wantErr bool
    95  	}{{
    96  		name: "test",
    97  		// null
    98  		args: args{
    99  			ccSpec: &appsv1alpha1.ConfigConstraintSpec{},
   100  			diff:   newCfgDiffMeta(`null`, nil, nil),
   101  		},
   102  		want:    false,
   103  		wantErr: false,
   104  	}, {
   105  		name: "test",
   106  		// error
   107  		args: args{
   108  			ccSpec: &appsv1alpha1.ConfigConstraintSpec{},
   109  			diff:   newCfgDiffMeta(`invalid json formatter`, nil, nil),
   110  		},
   111  		want:    false,
   112  		wantErr: true,
   113  	}, {
   114  		name: "test",
   115  		// add/delete config file
   116  		args: args{
   117  			ccSpec: &appsv1alpha1.ConfigConstraintSpec{},
   118  			diff:   newCfgDiffMeta(`{}`, map[string]interface{}{"a": "b"}, nil),
   119  		},
   120  		want:    false,
   121  		wantErr: false,
   122  	}, {
   123  		name: "test",
   124  		// not set static or dynamic parameters
   125  		args: args{
   126  			ccSpec: &appsv1alpha1.ConfigConstraintSpec{},
   127  			diff:   newCfgDiffMeta(`{"a":"b"}`, nil, nil),
   128  		},
   129  		want:    false,
   130  		wantErr: false,
   131  	}, {
   132  		name: "test",
   133  		// static parameters contains
   134  		args: args{
   135  			ccSpec: &appsv1alpha1.ConfigConstraintSpec{
   136  				StaticParameters: []string{"param1", "param2", "param3"},
   137  			},
   138  			diff: newCfgDiffMeta(`{"param3":"b"}`, nil, nil),
   139  		},
   140  		want:    false,
   141  		wantErr: false,
   142  	}, {
   143  		name: "test",
   144  		// static parameters not contains
   145  		args: args{
   146  			ccSpec: &appsv1alpha1.ConfigConstraintSpec{
   147  				StaticParameters: []string{"param1", "param2", "param3"},
   148  			},
   149  			diff: newCfgDiffMeta(`{"param4":"b"}`, nil, nil),
   150  		},
   151  		want:    true,
   152  		wantErr: false,
   153  	}, {
   154  		name: "test",
   155  		// dynamic parameters contains
   156  		args: args{
   157  			ccSpec: &appsv1alpha1.ConfigConstraintSpec{
   158  				DynamicParameters: []string{"param1", "param2", "param3"},
   159  			},
   160  			diff: newCfgDiffMeta(`{"param1":"b", "param3": 20}`, nil, nil),
   161  		},
   162  		want:    true,
   163  		wantErr: false,
   164  	}, {
   165  		name: "test",
   166  		// dynamic parameters not contains
   167  		args: args{
   168  			ccSpec: &appsv1alpha1.ConfigConstraintSpec{
   169  				DynamicParameters: []string{"param1", "param2", "param3"},
   170  			},
   171  			diff: newCfgDiffMeta(`{"param1":"b", "param4": 20}`, nil, nil),
   172  		},
   173  		want:    false,
   174  		wantErr: false,
   175  	}, {
   176  		name: "test",
   177  		// dynamic/static parameters not contains
   178  		args: args{
   179  			ccSpec: &appsv1alpha1.ConfigConstraintSpec{
   180  				DynamicParameters: []string{"dparam1", "dparam2", "dparam3"},
   181  				StaticParameters:  []string{"sparam1", "sparam2", "sparam3"},
   182  			},
   183  			diff: newCfgDiffMeta(`{"a":"b"}`, nil, nil),
   184  		},
   185  		want:    false,
   186  		wantErr: false,
   187  	}}
   188  	for _, tt := range tests {
   189  		t.Run(tt.name, func(t *testing.T) {
   190  			got, err := IsUpdateDynamicParameters(tt.args.ccSpec, tt.args.diff)
   191  			if (err != nil) != tt.wantErr {
   192  				t.Errorf("IsUpdateDynamicParameters() error = %v, wantErr %v", err, tt.wantErr)
   193  				return
   194  			}
   195  			if got != tt.want {
   196  				t.Errorf("IsUpdateDynamicParameters() got = %v, want %v", got, tt.want)
   197  			}
   198  		})
   199  	}
   200  }
   201  
   202  func TestIsSchedulableConfigResource(t *testing.T) {
   203  	tests := []struct {
   204  		name   string
   205  		object client.Object
   206  		want   bool
   207  	}{{
   208  		name:   "test",
   209  		object: &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{}},
   210  		want:   false,
   211  	}, {
   212  		name: "test",
   213  		object: &corev1.ConfigMap{
   214  			ObjectMeta: metav1.ObjectMeta{
   215  				Labels: map[string]string{
   216  					constant.AppNameLabelKey:        "test",
   217  					constant.AppInstanceLabelKey:    "test",
   218  					constant.KBAppComponentLabelKey: "component",
   219  				},
   220  			},
   221  		},
   222  		want: false,
   223  	}, {
   224  		name: "test",
   225  		object: &corev1.ConfigMap{
   226  			ObjectMeta: metav1.ObjectMeta{
   227  				Labels: map[string]string{
   228  					constant.AppNameLabelKey:                        "test",
   229  					constant.AppInstanceLabelKey:                    "test",
   230  					constant.KBAppComponentLabelKey:                 "component",
   231  					constant.CMConfigurationTemplateNameLabelKey:    "test_config_template",
   232  					constant.CMConfigurationConstraintsNameLabelKey: "test_config_constraint",
   233  					constant.CMConfigurationSpecProviderLabelKey:    "for_test_config",
   234  					constant.CMConfigurationTypeLabelKey:            constant.ConfigInstanceType,
   235  				},
   236  			},
   237  		},
   238  		want: true,
   239  	}}
   240  	for _, tt := range tests {
   241  		t.Run(tt.name, func(t *testing.T) {
   242  			if got := IsSchedulableConfigResource(tt.object); got != tt.want {
   243  				t.Errorf("IsSchedulableConfigResource() = %v, want %v", got, tt.want)
   244  			}
   245  		})
   246  	}
   247  }
   248  
   249  func TestSetParametersUpdateSource(t *testing.T) {
   250  	mockConfigMap := func() *corev1.ConfigMap {
   251  		return &corev1.ConfigMap{
   252  			ObjectMeta: metav1.ObjectMeta{
   253  				Name:      "test",
   254  				Namespace: "default",
   255  				Labels:    make(map[string]string),
   256  			},
   257  			Data: make(map[string]string),
   258  		}
   259  	}
   260  
   261  	cm := mockConfigMap()
   262  	require.False(t, IsParametersUpdateFromManager(cm))
   263  	require.False(t, IsNotUserReconfigureOperation(cm))
   264  	SetParametersUpdateSource(cm, constant.ReconfigureManagerSource)
   265  	require.True(t, IsParametersUpdateFromManager(cm))
   266  	require.False(t, IsNotUserReconfigureOperation(cm))
   267  
   268  	// check user reconfigure
   269  	cm.Annotations[constant.CMInsEnableRerenderTemplateKey] = "true"
   270  	require.True(t, IsNotUserReconfigureOperation(cm))
   271  
   272  	SetParametersUpdateSource(cm, constant.ReconfigureUserSource)
   273  	require.False(t, IsNotUserReconfigureOperation(cm))
   274  }
   275  
   276  func TestValidateConfigPatch(t *testing.T) {
   277  	type args struct {
   278  		patch     *ConfigPatchInfo
   279  		formatCfg *appsv1alpha1.FormatterConfig
   280  	}
   281  	tests := []struct {
   282  		name    string
   283  		args    args
   284  		wantErr bool
   285  	}{{
   286  		name: "test",
   287  		args: args{
   288  			patch:     &ConfigPatchInfo{},
   289  			formatCfg: &appsv1alpha1.FormatterConfig{Format: appsv1alpha1.YAML},
   290  		},
   291  		wantErr: false,
   292  	}, {
   293  		name: "test",
   294  		args: args{
   295  			patch: &ConfigPatchInfo{
   296  				IsModify: true,
   297  				UpdateConfig: map[string][]byte{
   298  					"file1": []byte(`{"a":"b"}`),
   299  				},
   300  			},
   301  			formatCfg: &appsv1alpha1.FormatterConfig{Format: appsv1alpha1.YAML},
   302  		},
   303  		wantErr: false,
   304  	}, {
   305  		name: "test-failed",
   306  		args: args{
   307  			patch: &ConfigPatchInfo{
   308  				IsModify: true,
   309  				UpdateConfig: map[string][]byte{
   310  					"file1": []byte(`{"a":null}`),
   311  				},
   312  			},
   313  			formatCfg: &appsv1alpha1.FormatterConfig{Format: appsv1alpha1.YAML},
   314  		},
   315  		wantErr: true,
   316  	}}
   317  	for _, tt := range tests {
   318  		t.Run(tt.name, func(t *testing.T) {
   319  			if err := ValidateConfigPatch(tt.args.patch, tt.args.formatCfg); (err != nil) != tt.wantErr {
   320  				t.Errorf("ValidateConfigPatch() error = %v, wantErr %v", err, tt.wantErr)
   321  			}
   322  		})
   323  	}
   324  }