mirror of
https://github.com/dolthub/dolt.git
synced 2026-04-21 19:39:04 -05:00
Merge pull request #4386 from dolthub/aaron/sql-server-yaml-parse-and-validate-cluster-config
go/cmd/dolt/commands/sqlserver: yaml_config.go: Parse and validate cluster: config.
This commit is contained in:
@@ -16,9 +16,11 @@ package sqlserver
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/commands/engine"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/env"
|
||||
@@ -146,6 +148,24 @@ type ServerConfig interface {
|
||||
// as a dolt remote for things like `clone`, `fetch` and read
|
||||
// replication.
|
||||
RemotesapiPort() *int
|
||||
// ClusterConfig is the configuration for clustering in this sql-server.
|
||||
ClusterConfig() ClusterConfig
|
||||
}
|
||||
|
||||
type ClusterConfig interface {
|
||||
StandbyRemotes() []StandbyRemoteConfig
|
||||
BootstrapRole() string
|
||||
BootstrapEpoch() int
|
||||
RemotesAPIConfig() RemotesAPIConfig
|
||||
}
|
||||
|
||||
type RemotesAPIConfig interface {
|
||||
Port() int
|
||||
}
|
||||
|
||||
type StandbyRemoteConfig interface {
|
||||
Name() string
|
||||
RemoteURLTemplate() string
|
||||
}
|
||||
|
||||
type commandLineServerConfig struct {
|
||||
@@ -273,6 +293,10 @@ func (cfg *commandLineServerConfig) RemotesapiPort() *int {
|
||||
return cfg.remotesapiPort
|
||||
}
|
||||
|
||||
func (cfg *commandLineServerConfig) ClusterConfig() ClusterConfig {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrivilegeFilePath returns the path to the file which contains all needed privilege information in the form of a
|
||||
// JSON string.
|
||||
func (cfg *commandLineServerConfig) PrivilegeFilePath() string {
|
||||
@@ -453,6 +477,34 @@ func ValidateConfig(config ServerConfig) error {
|
||||
if config.RequireSecureTransport() && config.TLSCert() == "" && config.TLSKey() == "" {
|
||||
return fmt.Errorf("require_secure_transport can only be `true` when a tls_key and tls_cert are provided.")
|
||||
}
|
||||
return ValidateClusterConfig(config.ClusterConfig())
|
||||
}
|
||||
|
||||
func ValidateClusterConfig(config ClusterConfig) error {
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
remotes := config.StandbyRemotes()
|
||||
if len(remotes) == 0 {
|
||||
return errors.New("cluster config: must supply standby_remotes when supplying cluster configuration.")
|
||||
}
|
||||
for i := range remotes {
|
||||
if remotes[i].Name() == "" {
|
||||
return fmt.Errorf("cluster: standby_remotes[%d]: name: Cannot be empty", i)
|
||||
}
|
||||
if strings.Index(remotes[i].RemoteURLTemplate(), "{database}") == -1 {
|
||||
return fmt.Errorf("cluster: standby_remotes[%d]: remote_url_template: is \"%s\" but must include the {database} template parameter", i, remotes[i].RemoteURLTemplate())
|
||||
}
|
||||
}
|
||||
if config.BootstrapRole() != "" && config.BootstrapRole() != "primary" && config.BootstrapRole() != "standby" {
|
||||
return fmt.Errorf("cluster: boostrap_role: is \"%s\" but must be \"primary\" or \"standby\"", config.BootstrapRole())
|
||||
}
|
||||
if config.BootstrapEpoch() < 0 {
|
||||
return fmt.Errorf("cluster: boostrap_epoch: is %d but must be >= 0", config.BootstrapEpoch())
|
||||
}
|
||||
if config.RemotesAPIConfig().Port() < 0 || config.RemotesAPIConfig().Port() > 65535 {
|
||||
return fmt.Errorf("cluster: remotesapi: port: is not in range 0-65535: %d", config.RemotesAPIConfig().Port())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,11 @@ type MetricsYAMLConfig struct {
|
||||
}
|
||||
|
||||
type RemotesapiYAMLConfig struct {
|
||||
Port *int `yaml:"port"`
|
||||
Port_field *int `yaml:"port"`
|
||||
}
|
||||
|
||||
func (r RemotesapiYAMLConfig) Port() int {
|
||||
return *r.Port_field
|
||||
}
|
||||
|
||||
type UserSessionVars struct {
|
||||
@@ -134,6 +138,7 @@ type YAMLConfig struct {
|
||||
CfgDirStr *string `yaml:"cfg_dir"`
|
||||
MetricsConfig MetricsYAMLConfig `yaml:"metrics"`
|
||||
RemotesapiConfig RemotesapiYAMLConfig `yaml:"remotesapi"`
|
||||
ClusterCfg *ClusterYAMLConfig `yaml:"cluster"`
|
||||
PrivilegeFile *string `yaml:"privilege_file"`
|
||||
Vars []UserSessionVars `yaml:"user_session_vars"`
|
||||
Jwks []engine.JwksConfig `yaml:"jwks"`
|
||||
@@ -340,7 +345,7 @@ func (cfg YAMLConfig) MetricsPort() int {
|
||||
}
|
||||
|
||||
func (cfg YAMLConfig) RemotesapiPort() *int {
|
||||
return cfg.RemotesapiConfig.Port
|
||||
return cfg.RemotesapiConfig.Port_field
|
||||
}
|
||||
|
||||
// PrivilegeFilePath returns the path to the file which contains all needed privilege information in the form of a
|
||||
@@ -444,3 +449,58 @@ func (cfg YAMLConfig) Socket() string {
|
||||
}
|
||||
return *cfg.ListenerConfig.Socket
|
||||
}
|
||||
|
||||
func (cfg YAMLConfig) ClusterConfig() ClusterConfig {
|
||||
if cfg.ClusterCfg == nil {
|
||||
return nil
|
||||
}
|
||||
return cfg.ClusterCfg
|
||||
}
|
||||
|
||||
type ClusterYAMLConfig struct {
|
||||
StandbyRemotes_field []standbyRemoteYAMLConfig `yaml:"standby_remotes"`
|
||||
BootstrapRole_field string `yaml:"bootstrap_role"`
|
||||
BootstrapEpoch_field int `yaml:"bootstrap_epoch"`
|
||||
Remotesapi clusterRemotesAPIYAMLConfig `yaml:"remotesapi"`
|
||||
}
|
||||
|
||||
type standbyRemoteYAMLConfig struct {
|
||||
Name_field string `yaml:"name"`
|
||||
RemoteURLTemplate_field string `yaml:"remote_url_template"`
|
||||
}
|
||||
|
||||
func (c standbyRemoteYAMLConfig) Name() string {
|
||||
return c.Name_field
|
||||
}
|
||||
|
||||
func (c standbyRemoteYAMLConfig) RemoteURLTemplate() string {
|
||||
return c.RemoteURLTemplate_field
|
||||
}
|
||||
|
||||
func (c *ClusterYAMLConfig) StandbyRemotes() []StandbyRemoteConfig {
|
||||
ret := make([]StandbyRemoteConfig, len(c.StandbyRemotes_field))
|
||||
for i := range c.StandbyRemotes_field {
|
||||
ret[i] = c.StandbyRemotes_field[i]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *ClusterYAMLConfig) BootstrapRole() string {
|
||||
return c.BootstrapRole_field
|
||||
}
|
||||
|
||||
func (c *ClusterYAMLConfig) BootstrapEpoch() int {
|
||||
return c.BootstrapEpoch_field
|
||||
}
|
||||
|
||||
func (c *ClusterYAMLConfig) RemotesAPIConfig() RemotesAPIConfig {
|
||||
return c.Remotesapi
|
||||
}
|
||||
|
||||
type clusterRemotesAPIYAMLConfig struct {
|
||||
P int `yaml:"port"`
|
||||
}
|
||||
|
||||
func (c clusterRemotesAPIYAMLConfig) Port() int {
|
||||
return c.P
|
||||
}
|
||||
|
||||
@@ -161,6 +161,136 @@ remotesapi:
|
||||
require.Equal(t, 8000, *config.RemotesapiPort())
|
||||
}
|
||||
|
||||
func TestUnmarshallCluster(t *testing.T) {
|
||||
testStr := `
|
||||
cluster:
|
||||
standby_remotes:
|
||||
- name: standby
|
||||
remote_url_template: http://doltdb-1.doltdb:50051/{database}
|
||||
bootstrap_role: primary
|
||||
bootstrap_epoch: 0
|
||||
remotesapi:
|
||||
port: 50051
|
||||
`
|
||||
config, err := NewYamlConfig([]byte(testStr))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, config.ClusterConfig())
|
||||
require.NotNil(t, config.ClusterConfig().RemotesAPIConfig())
|
||||
require.Equal(t, 50051, config.ClusterConfig().RemotesAPIConfig().Port())
|
||||
require.Len(t, config.ClusterConfig().StandbyRemotes(), 1)
|
||||
require.Equal(t, "primary", config.ClusterConfig().BootstrapRole())
|
||||
require.Equal(t, 0, config.ClusterConfig().BootstrapEpoch())
|
||||
require.Equal(t, "standby", config.ClusterConfig().StandbyRemotes()[0].Name())
|
||||
require.Equal(t, "http://doltdb-1.doltdb:50051/{database}", config.ClusterConfig().StandbyRemotes()[0].RemoteURLTemplate())
|
||||
}
|
||||
|
||||
func TestValidateClusterConfig(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
Config string
|
||||
Error bool
|
||||
}{
|
||||
{
|
||||
Name: "no cluster: config",
|
||||
Config: "",
|
||||
Error: false,
|
||||
},
|
||||
{
|
||||
Name: "all fields valid",
|
||||
Config: `
|
||||
cluster:
|
||||
standby_remotes:
|
||||
- name: standby
|
||||
remote_url_template: http://localhost:50051/{database}
|
||||
bootstrap_role: primary
|
||||
bootstrap_epoch: 0
|
||||
remotesapi:
|
||||
port: 50051
|
||||
`,
|
||||
Error: false,
|
||||
},
|
||||
{
|
||||
Name: "bad bootstrap_role",
|
||||
Config: `
|
||||
cluster:
|
||||
standby_remotes:
|
||||
- name: standby
|
||||
remote_url_template: http://localhost:50051/{database}
|
||||
bootstrap_role: backup
|
||||
bootstrap_epoch: 0
|
||||
remotesapi:
|
||||
port: 50051
|
||||
`,
|
||||
Error: true,
|
||||
},
|
||||
{
|
||||
Name: "negative bootstrap_epoch",
|
||||
Config: `
|
||||
cluster:
|
||||
standby_remotes:
|
||||
- name: standby
|
||||
remote_url_template: http://localhost:50051/{database}
|
||||
bootstrap_role: primary
|
||||
bootstrap_epoch: -1
|
||||
remotesapi:
|
||||
port: 50051
|
||||
`,
|
||||
Error: true,
|
||||
},
|
||||
{
|
||||
Name: "negative remotesapi port",
|
||||
Config: `
|
||||
cluster:
|
||||
standby_remotes:
|
||||
- name: standby
|
||||
remote_url_template: http://localhost:50051/{database}
|
||||
bootstrap_role: primary
|
||||
bootstrap_epoch: 0
|
||||
remotesapi:
|
||||
port: -5
|
||||
`,
|
||||
Error: true,
|
||||
},
|
||||
{
|
||||
Name: "bad remote_url_template",
|
||||
Config: `
|
||||
cluster:
|
||||
standby_remotes:
|
||||
- name: standby
|
||||
remote_url_template: http://localhost:50051/{database
|
||||
bootstrap_role: primary
|
||||
bootstrap_epoch: 0
|
||||
remotesapi:
|
||||
port: 50051
|
||||
`,
|
||||
Error: true,
|
||||
},
|
||||
{
|
||||
Name: "no standby remotes",
|
||||
Config: `
|
||||
cluster:
|
||||
standby_remotes:
|
||||
bootstrap_role: primary
|
||||
bootstrap_epoch: 0
|
||||
remotesapi:
|
||||
port: 50051
|
||||
`,
|
||||
Error: true,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.Name, func(t *testing.T) {
|
||||
cfg, err := NewYamlConfig([]byte(c.Config))
|
||||
require.NoError(t, err)
|
||||
if c.Error {
|
||||
require.Error(t, ValidateClusterConfig(cfg.ClusterConfig()))
|
||||
} else {
|
||||
require.NoError(t, ValidateClusterConfig(cfg.ClusterConfig()))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that a common YAML error (incorrect indentation) throws an error
|
||||
func TestUnmarshallError(t *testing.T) {
|
||||
testStr := `
|
||||
|
||||
Reference in New Issue
Block a user