github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/configuration/core/config_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  	"context"
    24  	"encoding/json"
    25  	"sort"
    26  	"strings"
    27  	"testing"
    28  
    29  	"github.com/StudioSol/set"
    30  	"github.com/bhmj/jsonslice"
    31  	"github.com/stretchr/testify/require"
    32  	"sigs.k8s.io/controller-runtime/pkg/client"
    33  	"sigs.k8s.io/controller-runtime/pkg/log"
    34  
    35  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    36  	"github.com/1aal/kubeblocks/pkg/configuration/util"
    37  )
    38  
    39  var iniConfig = `
    40  [mysqld]
    41  innodb-buffer-pool-size=512M
    42  log-bin=master-bin
    43  gtid_mode=OFF
    44  consensus_auto_leader_transfer=ON
    45  
    46  log_error=/data/mysql/log/mysqld.err
    47  character-sets-dir=/usr/share/mysql-8.0/charsets
    48  datadir=/data/mysql/data
    49  port=3306
    50  general_log=1
    51  general_log_file=/data/mysql/mysqld.log
    52  pid-file=/data/mysql/run/mysqld.pid
    53  server-id=1
    54  slow_query_log=1
    55  #slow_query_log_file=/data/mysql/mysqld-slow.log2
    56  slow_query_log_file=/data/mysql/mysqld-slow.log
    57  socket=/data/mysql/tmp/mysqld.sock
    58  ssl-ca=/data/mysql/std_data/cacert.pem
    59  ssl-cert=/data/mysql/std_data/server-cert.pem
    60  ssl-key=/data/mysql/std_data/server-key.pem
    61  tmpdir=/data/mysql/tmp/
    62  loose-sha256_password_auto_generate_rsa_keys=0
    63  loose-caching_sha2_password_auto_generate_rsa_keys=0
    64  secure-file-priv=/data/mysql
    65  
    66  [client]
    67  socket=/data/mysql/tmp/mysqld.sock
    68  host=localhost
    69  `
    70  
    71  func TestConfigMapConfig(t *testing.T) {
    72  	cfg, err := NewConfigLoader(CfgOption{
    73  		Type:    CfgCmType,
    74  		Log:     log.FromContext(context.Background()),
    75  		CfgType: appsv1alpha1.Ini,
    76  		ConfigResource: &ConfigResource{
    77  			CfgKey: client.ObjectKey{
    78  				Name:      "xxxx",    // set cm name
    79  				Namespace: "default", // set cm namespace
    80  			},
    81  			ResourceReader: func(key client.ObjectKey) (map[string]string, error) {
    82  				return map[string]string{
    83  					"my.cnf":      iniConfig,
    84  					"my_test.cnf": iniConfig,
    85  				}, nil
    86  			},
    87  		},
    88  	})
    89  
    90  	require.Nil(t, err)
    91  	log.Log.Info("cfg option: %v", cfg.Option)
    92  
    93  	require.Equal(t, cfg.fileCount, 2)
    94  	require.NotNil(t, cfg.getConfigObject(NewCfgOptions("my.cnf")))
    95  	require.Nil(t, cfg.getConfigObject(NewCfgOptions("my2.cnf")))
    96  
    97  	res, err := cfg.Query("$..slow_query_log_file", NewCfgOptions(""))
    98  	require.Nil(t, err)
    99  	require.NotNil(t, res)
   100  
   101  	require.Equal(t, "[\"/data/mysql/mysqld-slow.log\"]", string(res))
   102  
   103  	// patch
   104  	{
   105  
   106  		ctx := NewCfgOptions("my.cnf",
   107  			func(ctx *CfgOpOption) {
   108  				ctx.IniContext = &IniContext{
   109  					SectionName: "mysqld",
   110  				}
   111  			})
   112  
   113  		require.Nil(t,
   114  			cfg.MergeFrom(map[string]interface{}{
   115  				"slow_query_log": 0,
   116  				"general_log":    0,
   117  			}, ctx))
   118  
   119  		content, _ := cfg.ToCfgContent()
   120  		patch, err := CreateMergePatch(&ConfigResource{
   121  			ConfigData: map[string]string{
   122  				"my.cnf":  iniConfig,
   123  				"my2.cnf": iniConfig,
   124  			},
   125  		}, FromConfigData(content, nil), cfg.Option)
   126  
   127  		require.Nil(t, err)
   128  		require.NotNil(t, patch)
   129  
   130  		// add config: my_test.cnf
   131  		// delete config: my2.cnf
   132  
   133  		_, ok := patch.AddConfig["my_test.cnf"]
   134  		require.True(t, ok)
   135  
   136  		_, ok = patch.DeleteConfig["my2.cnf"]
   137  		require.True(t, ok)
   138  
   139  		updated, ok := patch.UpdateConfig["my.cnf"]
   140  		require.True(t, ok)
   141  
   142  		// update my.cnf
   143  		// update slow_query_log 0
   144  		res, _ := jsonslice.Get(updated, "$.mysqld.slow_query_log")
   145  		require.Equal(t, []byte(`"0"`), res)
   146  
   147  		// update general_log 0
   148  		res, _ = jsonslice.Get(updated, "$.mysqld.general_log")
   149  		require.Equal(t, []byte(`"0"`), res)
   150  	}
   151  }
   152  
   153  func TestGenerateVisualizedParamsList(t *testing.T) {
   154  	type args struct {
   155  		configPatch  *ConfigPatchInfo
   156  		formatConfig *appsv1alpha1.FormatterConfig
   157  		sets         *set.LinkedHashSetString
   158  	}
   159  
   160  	var (
   161  		testJSON          any
   162  		fileUpdatedParams = []byte(`{"mysqld": { "max_connections": "666", "read_buffer_size": "55288" }}`)
   163  		testUpdatedParams = []byte(`{"mysqld": { "max_connections": "666", "read_buffer_size": "55288", "delete_params": null }}`)
   164  	)
   165  
   166  	require.Nil(t, json.Unmarshal(fileUpdatedParams, &testJSON))
   167  	tests := []struct {
   168  		name string
   169  		args args
   170  		want []VisualizedParam
   171  	}{{
   172  		name: "visualizedParamsTest",
   173  		args: args{
   174  			configPatch: &ConfigPatchInfo{
   175  				IsModify: false,
   176  			},
   177  		},
   178  		want: nil,
   179  	}, {
   180  		name: "visualizedParamsTest",
   181  		args: args{
   182  			configPatch: &ConfigPatchInfo{
   183  				IsModify:     true,
   184  				UpdateConfig: map[string][]byte{"key": testUpdatedParams}},
   185  		},
   186  		want: []VisualizedParam{{
   187  			Key:        "key",
   188  			UpdateType: UpdatedType,
   189  			Parameters: []ParameterPair{
   190  				{
   191  					Key:   "mysqld.max_connections",
   192  					Value: util.ToPointer("666"),
   193  				}, {
   194  					Key:   "mysqld.read_buffer_size",
   195  					Value: util.ToPointer("55288"),
   196  				}, {
   197  					Key:   "mysqld.delete_params",
   198  					Value: nil,
   199  				}},
   200  		}},
   201  	}, {
   202  		name: "visualizedParamsTest",
   203  		args: args{
   204  			configPatch: &ConfigPatchInfo{
   205  				IsModify:     true,
   206  				UpdateConfig: map[string][]byte{"key": testUpdatedParams}},
   207  			formatConfig: &appsv1alpha1.FormatterConfig{
   208  				Format: appsv1alpha1.Ini,
   209  				FormatterOptions: appsv1alpha1.FormatterOptions{IniConfig: &appsv1alpha1.IniConfig{
   210  					SectionName: "mysqld",
   211  				}},
   212  			},
   213  		},
   214  		want: []VisualizedParam{{
   215  			Key:        "key",
   216  			UpdateType: UpdatedType,
   217  			Parameters: []ParameterPair{
   218  				{
   219  					Key:   "max_connections",
   220  					Value: util.ToPointer("666"),
   221  				}, {
   222  					Key:   "read_buffer_size",
   223  					Value: util.ToPointer("55288"),
   224  				}, {
   225  					Key:   "delete_params",
   226  					Value: nil,
   227  				}},
   228  		}},
   229  	}, {
   230  		name: "addFileTest",
   231  		args: args{
   232  			configPatch: &ConfigPatchInfo{
   233  				IsModify:  true,
   234  				AddConfig: map[string]interface{}{"key": testJSON},
   235  			},
   236  			formatConfig: &appsv1alpha1.FormatterConfig{
   237  				Format: appsv1alpha1.Ini,
   238  				FormatterOptions: appsv1alpha1.FormatterOptions{IniConfig: &appsv1alpha1.IniConfig{
   239  					SectionName: "mysqld",
   240  				}},
   241  			},
   242  		},
   243  		want: []VisualizedParam{{
   244  			Key:        "key",
   245  			UpdateType: AddedType,
   246  			Parameters: []ParameterPair{
   247  				{
   248  					Key:   "max_connections",
   249  					Value: util.ToPointer("666"),
   250  				}, {
   251  					Key:   "read_buffer_size",
   252  					Value: util.ToPointer("55288"),
   253  				}},
   254  		}},
   255  	}, {
   256  		name: "deleteFileTest",
   257  		args: args{
   258  			configPatch: &ConfigPatchInfo{
   259  				IsModify:     true,
   260  				DeleteConfig: map[string]interface{}{"key": testJSON},
   261  			},
   262  			formatConfig: &appsv1alpha1.FormatterConfig{
   263  				Format: appsv1alpha1.Ini,
   264  				FormatterOptions: appsv1alpha1.FormatterOptions{IniConfig: &appsv1alpha1.IniConfig{
   265  					SectionName: "mysqld",
   266  				}},
   267  			},
   268  		},
   269  		want: []VisualizedParam{{
   270  			Key:        "key",
   271  			UpdateType: DeletedType,
   272  			Parameters: []ParameterPair{
   273  				{
   274  					Key:   "max_connections",
   275  					Value: util.ToPointer("666"),
   276  				}, {
   277  					Key:   "read_buffer_size",
   278  					Value: util.ToPointer("55288"),
   279  				}},
   280  		}},
   281  	}}
   282  	for _, tt := range tests {
   283  		t.Run(tt.name, func(t *testing.T) {
   284  			got := GenerateVisualizedParamsList(tt.args.configPatch, tt.args.formatConfig, tt.args.sets)
   285  			sortParams(got)
   286  			sortParams(tt.want)
   287  			require.Equal(t, got, tt.want)
   288  		})
   289  	}
   290  }
   291  
   292  func sortParams(param []VisualizedParam) {
   293  	for _, v := range param {
   294  		sort.SliceStable(v.Parameters, func(i, j int) bool {
   295  			return strings.Compare(v.Parameters[i].Key, v.Parameters[j].Key) <= 0
   296  		})
   297  	}
   298  	if len(param) > 0 {
   299  		sort.SliceStable(param, func(i, j int) bool {
   300  			return strings.Compare(param[i].Key, param[j].Key) <= 0
   301  		})
   302  	}
   303  }