github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/servercfg/yaml_config_test.go (about)

     1  // Copyright 2020 Dolthub, Inc.
     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 servercfg
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/require"
    22  	"gopkg.in/yaml.v2"
    23  )
    24  
    25  var trueValue = true
    26  
    27  func TestUnmarshall(t *testing.T) {
    28  	testStr := `
    29  log_level: info
    30  
    31  behavior:
    32      read_only: false
    33      autocommit: true
    34      dolt_transaction_commit: true
    35      persistence_behavior: load
    36      disable_client_multi_statements: false
    37      event_scheduler: ON
    38  
    39  user:
    40      name: ""
    41      password: ""
    42  
    43  listener:
    44      host: localhost
    45      port: 3306
    46      max_connections: 100
    47      read_timeout_millis: 28800000
    48      write_timeout_millis: 28800000
    49      
    50  data_dir: some nonsense
    51  
    52  metrics:
    53      host: 123.45.67.89
    54      port: 9091
    55      labels:
    56          label1: value1
    57          label2: 2
    58          label3: true
    59  
    60  user_session_vars:
    61      - name: user0
    62        vars:
    63            var1: val0_1
    64            var2: val0_2
    65            var3: val0_3
    66      - name: user1
    67        vars:
    68            var1: val1_1
    69            var2: val1_2
    70            var4: val1_4
    71  
    72  privilege_file: some other nonsense
    73  
    74  branch_control_file: third nonsense
    75  
    76  jwks:
    77    - name: jwks_name
    78      location_url: https://website.com
    79      claims: 
    80        field1: a
    81        field2: b
    82      fields_to_log: [field1, field2]
    83    - name: jwks_name2
    84      location_url: https://website.com
    85      claims: 
    86        field1: a
    87      fields_to_log:
    88  `
    89  	expected := ServerConfigAsYAMLConfig(DefaultServerConfig())
    90  
    91  	expected.BehaviorConfig.DoltTransactionCommit = &trueValue
    92  	expected.CfgDirStr = nillableStrPtr("")
    93  	expected.PrivilegeFile = ptr("some other nonsense")
    94  	expected.BranchControlFile = ptr("third nonsense")
    95  
    96  	expected.MetricsConfig = MetricsYAMLConfig{
    97  		Host: ptr("123.45.67.89"),
    98  		Port: ptr(9091),
    99  		Labels: map[string]string{
   100  			"label1": "value1",
   101  			"label2": "2",
   102  			"label3": "true",
   103  		},
   104  	}
   105  	expected.DataDirStr = ptr("some nonsense")
   106  	expected.SystemVars_ = nil
   107  	expected.Vars = []UserSessionVars{
   108  		{
   109  			Name: "user0",
   110  			Vars: map[string]string{
   111  				"var1": "val0_1",
   112  				"var2": "val0_2",
   113  				"var3": "val0_3",
   114  			},
   115  		},
   116  		{
   117  			Name: "user1",
   118  			Vars: map[string]string{
   119  				"var1": "val1_1",
   120  				"var2": "val1_2",
   121  				"var4": "val1_4",
   122  			},
   123  		},
   124  	}
   125  	expected.Jwks = []JwksConfig{
   126  		{
   127  			Name:        "jwks_name",
   128  			LocationUrl: "https://website.com",
   129  			Claims: map[string]string{
   130  				"field1": "a",
   131  				"field2": "b",
   132  			},
   133  			FieldsToLog: []string{"field1", "field2"},
   134  		},
   135  		{
   136  			Name:        "jwks_name2",
   137  			LocationUrl: "https://website.com",
   138  			Claims: map[string]string{
   139  				"field1": "a",
   140  			},
   141  			FieldsToLog: nil,
   142  		},
   143  	}
   144  
   145  	config, err := NewYamlConfig([]byte(testStr))
   146  	require.NoError(t, err)
   147  	assert.Equal(t, expected, config, "Expected:\n%v\nActual:\n%v", expected, config)
   148  }
   149  
   150  func TestUnmarshallRemotesapiPort(t *testing.T) {
   151  	testStr := `
   152  remotesapi:
   153    port: 8000
   154  `
   155  	config, err := NewYamlConfig([]byte(testStr))
   156  	require.NoError(t, err)
   157  	require.NotNil(t, config.RemotesapiPort())
   158  	require.Equal(t, 8000, *config.RemotesapiPort())
   159  }
   160  
   161  func TestUnmarshallCluster(t *testing.T) {
   162  	testStr := `
   163  cluster:
   164    standby_remotes:
   165    - name: standby
   166      remote_url_template: http://doltdb-1.doltdb:50051/{database}
   167    bootstrap_role: primary
   168    bootstrap_epoch: 0
   169    remotesapi:
   170      port: 50051
   171  `
   172  	config, err := NewYamlConfig([]byte(testStr))
   173  	require.NoError(t, err)
   174  	require.NotNil(t, config.ClusterConfig())
   175  	require.NotNil(t, config.ClusterConfig().RemotesAPIConfig())
   176  	require.Equal(t, 50051, config.ClusterConfig().RemotesAPIConfig().Port())
   177  	require.Len(t, config.ClusterConfig().StandbyRemotes(), 1)
   178  	require.Equal(t, "primary", config.ClusterConfig().BootstrapRole())
   179  	require.Equal(t, 0, config.ClusterConfig().BootstrapEpoch())
   180  	require.Equal(t, "standby", config.ClusterConfig().StandbyRemotes()[0].Name())
   181  	require.Equal(t, "http://doltdb-1.doltdb:50051/{database}", config.ClusterConfig().StandbyRemotes()[0].RemoteURLTemplate())
   182  }
   183  
   184  func TestValidateClusterConfig(t *testing.T) {
   185  	cases := []struct {
   186  		Name   string
   187  		Config string
   188  		Error  bool
   189  	}{
   190  		{
   191  			Name:   "no cluster: config",
   192  			Config: "",
   193  			Error:  false,
   194  		},
   195  		{
   196  			Name: "all fields valid",
   197  			Config: `
   198  cluster:
   199    standby_remotes:
   200    - name: standby
   201      remote_url_template: http://localhost:50051/{database}
   202    bootstrap_role: primary
   203    bootstrap_epoch: 0
   204    remotesapi:
   205      port: 50051
   206  `,
   207  			Error: false,
   208  		},
   209  		{
   210  			Name: "bad bootstrap_role",
   211  			Config: `
   212  cluster:
   213    standby_remotes:
   214    - name: standby
   215      remote_url_template: http://localhost:50051/{database}
   216    bootstrap_role: backup
   217    bootstrap_epoch: 0
   218    remotesapi:
   219      port: 50051
   220  `,
   221  			Error: true,
   222  		},
   223  		{
   224  			Name: "negative bootstrap_epoch",
   225  			Config: `
   226  cluster:
   227    standby_remotes:
   228    - name: standby
   229      remote_url_template: http://localhost:50051/{database}
   230    bootstrap_role: primary
   231    bootstrap_epoch: -1
   232    remotesapi:
   233      port: 50051
   234  `,
   235  			Error: true,
   236  		},
   237  		{
   238  			Name: "negative remotesapi port",
   239  			Config: `
   240  cluster:
   241    standby_remotes:
   242    - name: standby
   243      remote_url_template: http://localhost:50051/{database}
   244    bootstrap_role: primary
   245    bootstrap_epoch: 0
   246    remotesapi:
   247      port: -5
   248  `,
   249  			Error: true,
   250  		},
   251  		{
   252  			Name: "bad remote_url_template",
   253  			Config: `
   254  cluster:
   255    standby_remotes:
   256    - name: standby
   257      remote_url_template: http://localhost:50051/{database
   258    bootstrap_role: primary
   259    bootstrap_epoch: 0
   260    remotesapi:
   261      port: 50051
   262  `,
   263  			Error: true,
   264  		},
   265  		{
   266  			Name: "no standby remotes",
   267  			Config: `
   268  cluster:
   269    standby_remotes:
   270    bootstrap_role: primary
   271    bootstrap_epoch: 0
   272    remotesapi:
   273      port: 50051
   274  `,
   275  			Error: true,
   276  		},
   277  	}
   278  	for _, c := range cases {
   279  		t.Run(c.Name, func(t *testing.T) {
   280  			cfg, err := NewYamlConfig([]byte(c.Config))
   281  			require.NoError(t, err)
   282  			if c.Error {
   283  				require.Error(t, ValidateClusterConfig(cfg.ClusterConfig()))
   284  			} else {
   285  				require.NoError(t, ValidateClusterConfig(cfg.ClusterConfig()))
   286  			}
   287  		})
   288  	}
   289  }
   290  
   291  // Tests that a common YAML error (incorrect indentation) throws an error
   292  func TestUnmarshallError(t *testing.T) {
   293  	testStr := `
   294  log_level: info
   295  
   296  behavior:
   297  read_only: false
   298  autocommit: true
   299  
   300  user:
   301      name: root
   302      password: ""
   303  
   304  listener:
   305      host: localhost
   306      port: 3306
   307      max_connections: 1
   308      read_timeout_millis: 28800000
   309      write_timeout_millis: 28800000
   310      
   311  databases:
   312      - name: irs_soi
   313        path: ./datasets/irs-soi
   314      - name: noaa
   315        path: /Users/brian/datasets/noaa
   316  `
   317  	_, err := NewYamlConfig([]byte(testStr))
   318  	assert.Error(t, err)
   319  }
   320  
   321  func TestYAMLConfigDefaults(t *testing.T) {
   322  	var cfg YAMLConfig
   323  	err := yaml.Unmarshal([]byte{}, &cfg)
   324  	require.NoError(t, err)
   325  
   326  	assert.Equal(t, DefaultHost, cfg.Host())
   327  	assert.Equal(t, DefaultPort, cfg.Port())
   328  	assert.Equal(t, DefaultUser, cfg.User())
   329  	assert.Equal(t, DefaultPass, cfg.Password())
   330  	assert.Equal(t, uint64(DefaultTimeout), cfg.WriteTimeout())
   331  	assert.Equal(t, uint64(DefaultTimeout), cfg.ReadTimeout())
   332  	assert.Equal(t, DefaultReadOnly, cfg.ReadOnly())
   333  	assert.Equal(t, DefaultLogLevel, cfg.LogLevel())
   334  	assert.Equal(t, DefaultAutoCommit, cfg.AutoCommit())
   335  	assert.Equal(t, DefaultDoltTransactionCommit, cfg.DoltTransactionCommit())
   336  	assert.Equal(t, uint64(DefaultMaxConnections), cfg.MaxConnections())
   337  	assert.Equal(t, "", cfg.TLSKey())
   338  	assert.Equal(t, "", cfg.TLSCert())
   339  	assert.Equal(t, false, cfg.RequireSecureTransport())
   340  	assert.Equal(t, false, cfg.AllowCleartextPasswords())
   341  	assert.Equal(t, false, cfg.DisableClientMultiStatements())
   342  	assert.Equal(t, DefaultMetricsHost, cfg.MetricsHost())
   343  	assert.Equal(t, DefaultMetricsPort, cfg.MetricsPort())
   344  	assert.Nil(t, cfg.MetricsConfig.Labels)
   345  	assert.Equal(t, DefaultAllowCleartextPasswords, cfg.AllowCleartextPasswords())
   346  	assert.Nil(t, cfg.RemotesapiPort())
   347  
   348  	c, err := LoadTLSConfig(cfg)
   349  	assert.NoError(t, err)
   350  	assert.Nil(t, c)
   351  }
   352  
   353  func TestYAMLConfigTLS(t *testing.T) {
   354  	var cfg YAMLConfig
   355  	err := yaml.Unmarshal([]byte(`
   356  listener:
   357    tls_key: testdata/selfsigned_key.pem
   358    tls_cert: testdata/selfsigned_cert.pem
   359  `), &cfg)
   360  	require.NoError(t, err)
   361  
   362  	c, err := LoadTLSConfig(cfg)
   363  	assert.NoError(t, err)
   364  	assert.NotNil(t, c)
   365  	assert.Len(t, c.Certificates, 1)
   366  	assert.Len(t, c.Certificates[0].Certificate, 1)
   367  
   368  	err = yaml.Unmarshal([]byte(`
   369  listener:
   370    tls_key: testdata/chain_key.pem
   371    tls_cert: testdata/chain_cert.pem
   372  `), &cfg)
   373  	require.NoError(t, err)
   374  
   375  	c, err = LoadTLSConfig(cfg)
   376  	assert.NoError(t, err)
   377  	assert.NotNil(t, c)
   378  	assert.Len(t, c.Certificates, 1)
   379  	assert.Len(t, c.Certificates[0].Certificate, 1)
   380  
   381  	cfg = YAMLConfig{}
   382  	err = yaml.Unmarshal([]byte(`
   383  listener:
   384    tls_key: testdata/chain_key.pem
   385  `), &cfg)
   386  	require.NoError(t, err)
   387  	c, err = LoadTLSConfig(cfg)
   388  	assert.Error(t, err)
   389  
   390  	cfg = YAMLConfig{}
   391  	err = yaml.Unmarshal([]byte(`
   392  listener:
   393    tls_cert: testdata/chain_cert.pem
   394  `), &cfg)
   395  	require.NoError(t, err)
   396  	c, err = LoadTLSConfig(cfg)
   397  	assert.Error(t, err)
   398  
   399  	cfg = YAMLConfig{}
   400  	err = yaml.Unmarshal([]byte(`
   401  listener:
   402    tls_cert: testdata/doesnotexist_cert.pem
   403    tls_key: testdata/doesnotexist_key.pem
   404  `), &cfg)
   405  	require.NoError(t, err)
   406  	c, err = LoadTLSConfig(cfg)
   407  	assert.Error(t, err)
   408  
   409  	cfg = YAMLConfig{}
   410  	err = yaml.Unmarshal([]byte(`
   411  listener:
   412    require_secure_transport: true
   413  `), &cfg)
   414  	require.NoError(t, err)
   415  	err = ValidateConfig(cfg)
   416  	assert.Error(t, err)
   417  }