From 3a3742aa3c5844029a52404363110506e9d215f2 Mon Sep 17 00:00:00 2001 From: akartasov Date: Tue, 13 Dec 2022 15:47:14 +0700 Subject: [PATCH 1/7] feat(engine): add customOptions for logicalDump (pg_dump) and logicalRestore (pg_restore) jobs (#361) --- .../config.example.logical_generic.yml | 15 ++++++++++++++ .../config.example.logical_rds_iam.yml | 15 ++++++++++++++ .../retrieval/engine/postgres/logical/dump.go | 15 +++++++++----- .../engine/postgres/logical/restore.go | 6 ++++-- engine/pkg/models/configuration.go | 2 ++ engine/pkg/util/projection/yaml.go | 16 +++++++++++++++ engine/pkg/util/ptypes/mapping.go | 20 +++++++++++++++++++ 7 files changed, 82 insertions(+), 7 deletions(-) diff --git a/engine/configs/config.example.logical_generic.yml b/engine/configs/config.example.logical_generic.yml index c302939da..c053f547d 100644 --- a/engine/configs/config.example.logical_generic.yml +++ b/engine/configs/config.example.logical_generic.yml @@ -255,6 +255,15 @@ retrieval: # # Option to adjust PostgreSQL configuration for a logical dump job. # # It's useful if a dumped database contains non-standard extensions. # <<: *db_configs + # # Custom options for pg_restore command. + # customOptions: + # - "--no-privileges" + # - "--no-owner" + # - "--exit-on-error" + + # Custom options for pg_dump command. + customOptions: + # - "--verbose" # Restores PostgreSQL database from the provided dump. If you use this block, do not use # "restore" option in the "logicalDump" job. @@ -305,6 +314,12 @@ retrieval: # Inline SQL. Queries run after scripts placed in 'queryPath'. inline: "" + # Custom options for pg_restore command. + customOptions: + - "--no-privileges" + - "--no-owner" + - "--exit-on-error" + logicalSnapshot: options: # Adjust PostgreSQL configuration diff --git a/engine/configs/config.example.logical_rds_iam.yml b/engine/configs/config.example.logical_rds_iam.yml index 39e6d2ab8..a3fed9eb2 100644 --- a/engine/configs/config.example.logical_rds_iam.yml +++ b/engine/configs/config.example.logical_rds_iam.yml @@ -252,6 +252,15 @@ retrieval: # # Option to adjust PostgreSQL configuration for a logical dump job. # # It's useful if a dumped database contains non-standard extensions. # <<: *db_configs + # # Custom options for pg_restore command. + # customOptions: + # - "--no-privileges" + # - "--no-owner" + # - "--exit-on-error" + + # Custom options for pg_dump command. + customOptions: + - "--exclude-schema=rdsdms" # Restores PostgreSQL database from the provided dump. If you use this block, do not use # "restore" option in the "logicalDump" job. @@ -306,6 +315,12 @@ retrieval: # Inline SQL. Queries run after scripts placed in 'queryPath'. inline: "" + # Custom options for pg_restore command. + customOptions: + - "--no-privileges" + - "--no-owner" + - "--exit-on-error" + logicalSnapshot: options: # Adjust PostgreSQL configuration diff --git a/engine/internal/retrieval/engine/postgres/logical/dump.go b/engine/internal/retrieval/engine/postgres/logical/dump.go index 4f6d5926f..81a707194 100644 --- a/engine/internal/retrieval/engine/postgres/logical/dump.go +++ b/engine/internal/retrieval/engine/postgres/logical/dump.go @@ -88,6 +88,7 @@ type DumpOptions struct { Databases map[string]DumpDefinition `yaml:"databases"` ParallelJobs int `yaml:"parallelJobs"` Restore ImmediateRestore `yaml:"immediateRestore"` + CustomOptions []string `yaml:"customOptions"` } // Source describes source of data to dump. @@ -133,9 +134,10 @@ type Connection struct { // ImmediateRestore contains options for direct data restore without saving the dump file on disk. type ImmediateRestore struct { - Enabled bool `yaml:"enabled"` - ForceInit bool `yaml:"forceInit"` - Configs map[string]string `yaml:"configs"` + Enabled bool `yaml:"enabled"` + ForceInit bool `yaml:"forceInit"` + Configs map[string]string `yaml:"configs"` + CustomOptions []string `yaml:"customOptions"` } // NewDumpJob creates a new DumpJob. @@ -694,6 +696,8 @@ func (d *DumpJob) buildLogicalDumpCommand(dbName string, dump DumpDefinition) [] dumpCmd = append(dumpCmd, "--exclude-table", table) } + dumpCmd = append(dumpCmd, d.DumpOptions.CustomOptions...) + // Define if restore directly or export to dump location. if d.DumpOptions.Restore.Enabled { dumpCmd = append(dumpCmd, "--format", customFormat) @@ -711,8 +715,7 @@ func (d *DumpJob) buildLogicalDumpCommand(dbName string, dump DumpDefinition) [] } func (d *DumpJob) buildLogicalRestoreCommand(dbName string) []string { - restoreCmd := []string{"|", "pg_restore", "--username", d.globalCfg.Database.User(), "--dbname", defaults.DBName, - "--no-privileges", "--no-owner", "--exit-on-error"} + restoreCmd := []string{"|", "pg_restore", "--username", d.globalCfg.Database.User(), "--dbname", defaults.DBName} if dbName != defaults.DBName { // To avoid recreating of the default database. @@ -723,6 +726,8 @@ func (d *DumpJob) buildLogicalRestoreCommand(dbName string) []string { restoreCmd = append(restoreCmd, "--clean", "--if-exists") } + restoreCmd = append(restoreCmd, d.DumpOptions.Restore.CustomOptions...) + return restoreCmd } diff --git a/engine/internal/retrieval/engine/postgres/logical/restore.go b/engine/internal/retrieval/engine/postgres/logical/restore.go index 55c4b90d2..a6c739c54 100644 --- a/engine/internal/retrieval/engine/postgres/logical/restore.go +++ b/engine/internal/retrieval/engine/postgres/logical/restore.go @@ -104,6 +104,7 @@ type RestoreOptions struct { ParallelJobs int `yaml:"parallelJobs"` Configs map[string]string `yaml:"configs"` QueryPreprocessing query.PreprocessorCfg `yaml:"queryPreprocessing"` + CustomOptions []string `yaml:"customOptions"` } // Partial defines tables and rules for a partial logical restore. @@ -736,8 +737,7 @@ func (r *RestoreJob) buildPlainTextCommand(dumpName string, definition DumpDefin } func (r *RestoreJob) buildPGRestoreCommand(dumpName string, definition DumpDefinition) []string { - restoreCmd := []string{"pg_restore", "--username", r.globalCfg.Database.User(), "--dbname", defaults.DBName, - "--no-privileges", "--no-owner", "--exit-on-error"} + restoreCmd := []string{"pg_restore", "--username", r.globalCfg.Database.User(), "--dbname", defaults.DBName} if definition.dbName != defaults.DBName { // To avoid recreating of the default database. @@ -760,6 +760,8 @@ func (r *RestoreJob) buildPGRestoreCommand(dumpName string, definition DumpDefin restoreCmd = append(restoreCmd, r.getDumpLocation(definition.Format, dumpName)) + restoreCmd = append(restoreCmd, r.RestoreOptions.CustomOptions...) + return restoreCmd } diff --git a/engine/pkg/models/configuration.go b/engine/pkg/models/configuration.go index a38512885..5d6b69e4b 100644 --- a/engine/pkg/models/configuration.go +++ b/engine/pkg/models/configuration.go @@ -25,4 +25,6 @@ type ConfigProjection struct { DBList map[string]interface{} `proj:"retrieval.spec.logicalDump.options.databases,createKey"` DumpParallelJobs *int64 `proj:"retrieval.spec.logicalDump.options.parallelJobs"` RestoreParallelJobs *int64 `proj:"retrieval.spec.logicalRestore.options.parallelJobs"` + DumpCustomOptions []interface{} `proj:"retrieval.spec.logicalDump.options.customOptions"` + RestoreCustomOptions []interface{} `proj:"retrieval.spec.logicalRestore.options.customOptions"` } diff --git a/engine/pkg/util/projection/yaml.go b/engine/pkg/util/projection/yaml.go index 5523d97f3..c98721fca 100644 --- a/engine/pkg/util/projection/yaml.go +++ b/engine/pkg/util/projection/yaml.go @@ -75,6 +75,14 @@ func (y *yamlSoft) Set(set FieldSet) error { return nil } + if seq, ok := set.Value.([]interface{}); ok { + if err := node.Encode(seq); err != nil { + return fmt.Errorf("cannot encode slice: %w", err) + } + + return nil + } + conv, err := ptypes.Convert(set.Value, ptypes.String) if err != nil { return err @@ -109,6 +117,10 @@ func (y *yamlSoft) Get(get FieldGet) (interface{}, error) { return convertMap(node) } + if node.Tag == "!!seq" { + return convertSlice(node) + } + typed, err := ptypes.Convert(node.Value, get.Type) if err != nil { return nil, err @@ -191,6 +203,8 @@ func ptypeToNodeTag(t ptypes.Type) string { return "!!bool" case ptypes.Map: return "!!map" + case ptypes.Slice: + return "!!seq" default: return "" } @@ -208,6 +222,8 @@ func nodeTagToPType(nodeTag string) ptypes.Type { return ptypes.Bool case "!!map": return ptypes.Map + case "!!seq": + return ptypes.Slice default: return ptypes.Invalid } diff --git a/engine/pkg/util/ptypes/mapping.go b/engine/pkg/util/ptypes/mapping.go index 2f800bc70..913e1cdaf 100644 --- a/engine/pkg/util/ptypes/mapping.go +++ b/engine/pkg/util/ptypes/mapping.go @@ -24,6 +24,8 @@ const ( Bool // Map is a map type. Map + // Slice is a slice type. + Slice ) // Convert converts the value to the given type. @@ -39,6 +41,8 @@ func Convert(value interface{}, expected Type) (interface{}, error) { return convertBool(value) case Map: return convertMap(value) + case Slice: + return convertSlice(value) } return nil, errors.Errorf("unsupported type for conversion: %T", value) @@ -123,6 +127,18 @@ func convertMap(value interface{}) (interface{}, error) { return nil, errors.Errorf("unsupported map type: %T", value) } +func convertSlice(value interface{}) (interface{}, error) { + switch v := value.(type) { + case []string: + return v, nil + + case []interface{}: + return v, nil + } + + return nil, errors.Errorf("unsupported slice type: %T", value) +} + // MapKindToType returns the type of the given kind. func MapKindToType(kind reflect.Kind) Type { switch kind { @@ -136,6 +152,8 @@ func MapKindToType(kind reflect.Kind) Type { return Bool case reflect.Map: return Map + case reflect.Slice: + return Slice } return Invalid @@ -154,6 +172,8 @@ func NewPtr(value interface{}) reflect.Value { return reflect.ValueOf(&v) case map[string]interface{}: return reflect.ValueOf(&v) + case []interface{}: + return reflect.ValueOf(&v) } return reflect.Value{} -- GitLab From 2898bd66bbd8e9e8c3974d8c32087bdd5f1df892 Mon Sep 17 00:00:00 2001 From: akartasov Date: Tue, 13 Dec 2022 16:38:07 +0700 Subject: [PATCH 2/7] fix tests --- .../engine/postgres/logical/restore_test.go | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/engine/internal/retrieval/engine/postgres/logical/restore_test.go b/engine/internal/retrieval/engine/postgres/logical/restore_test.go index 4ec172e68..03e40299d 100644 --- a/engine/internal/retrieval/engine/postgres/logical/restore_test.go +++ b/engine/internal/retrieval/engine/postgres/logical/restore_test.go @@ -40,25 +40,27 @@ func TestRestoreCommandBuilding(t *testing.T) { Format: customFormat, }, }, - DumpLocation: "/tmp/db.dump", + DumpLocation: "/tmp/db.dump", + CustomOptions: []string{"--no-privileges", "--no-owner", "--exit-on-error"}, }, - command: []string{"pg_restore", "--username", "john", "--dbname", "postgres", "--no-privileges", "--no-owner", "--exit-on-error", "--create", "--jobs", "1", "/tmp/db.dump"}, + command: []string{"pg_restore", "--username", "john", "--dbname", "postgres", "--create", "--jobs", "1", "/tmp/db.dump", "--no-privileges", "--no-owner", "--exit-on-error"}, }, { copyOptions: RestoreOptions{ ParallelJobs: 4, ForceInit: true, }, - command: []string{"pg_restore", "--username", "john", "--dbname", "postgres", "--no-privileges", "--no-owner", "--exit-on-error", "--create", "--clean", "--if-exists", "--jobs", "4", ""}, + command: []string{"pg_restore", "--username", "john", "--dbname", "postgres", "--create", "--clean", "--if-exists", "--jobs", "4"}, }, { copyOptions: RestoreOptions{ - ParallelJobs: 2, - ForceInit: false, - Databases: map[string]DumpDefinition{"testDB": {}}, - DumpLocation: "/tmp/db.dump", + ParallelJobs: 2, + ForceInit: false, + Databases: map[string]DumpDefinition{"testDB": {}}, + DumpLocation: "/tmp/db.dump", + CustomOptions: []string{"--no-privileges", "--no-owner", "--exit-on-error"}, }, - command: []string{"pg_restore", "--username", "john", "--dbname", "postgres", "--no-privileges", "--no-owner", "--exit-on-error", "--create", "--jobs", "2", "/tmp/db.dump/testDB"}, + command: []string{"pg_restore", "--username", "john", "--dbname", "postgres", "--create", "--jobs", "2", "/tmp/db.dump/testDB", "--no-privileges", "--no-owner", "--exit-on-error"}, }, { copyOptions: RestoreOptions{ @@ -69,9 +71,10 @@ func TestRestoreCommandBuilding(t *testing.T) { Format: directoryFormat, }, }, - DumpLocation: "/tmp/db.dump", + DumpLocation: "/tmp/db.dump", + CustomOptions: []string{"--no-privileges", "--no-owner", "--exit-on-error"}, }, - command: []string{"pg_restore", "--username", "john", "--dbname", "postgres", "--no-privileges", "--no-owner", "--exit-on-error", "--create", "--jobs", "1", "--table", "test", "--table", "users", "/tmp/db.dump/testDB"}, + command: []string{"pg_restore", "--username", "john", "--dbname", "postgres", "--create", "--jobs", "1", "--table", "test", "--table", "users", "/tmp/db.dump/testDB", "--no-privileges", "--no-owner", "--exit-on-error"}, }, { copyOptions: RestoreOptions{ @@ -133,6 +136,11 @@ func TestDumpCommandBuilding(t *testing.T) { Password: "secret", }, }, + globalCfg: &global.Config{ + Database: global.Database{ + Username: "postgres", + }, + }, } testCases := []struct { @@ -152,8 +160,31 @@ func TestDumpCommandBuilding(t *testing.T) { }, }, }, + CustomOptions: []string{"--exclude-scheme=test-scheme"}, + }, + command: []string{"pg_dump", "--create", "--host", "localhost", "--port", "5432", "--username", "john", "--dbname", "testDB", "--jobs", "1", "--table", "test", "--table", "users", "--exclude-table", "test2", "--exclude-table", "users2", "--exclude-scheme=test-scheme", "--format", "directory", "--file", "/tmp/db.dump/testDB"}, + }, + { + copyOptions: DumpOptions{ + + ParallelJobs: 1, + DumpLocation: "/tmp/db.dump", + Databases: map[string]DumpDefinition{ + "testDB": { + Tables: []string{"test", "users"}, + ExcludeTables: []string{ + "test2", + "users2", + }, + }, + }, + Restore: ImmediateRestore{ + Enabled: true, + CustomOptions: []string{"--no-privileges", "--no-owner", "--exit-on-error"}, + }, + CustomOptions: []string{"--exclude-scheme=test-scheme"}, }, - command: []string{"pg_dump", "--create", "--host", "localhost", "--port", "5432", "--username", "john", "--dbname", "testDB", "--jobs", "1", "--table", "test", "--table", "users", "--exclude-table", "test2", "--exclude-table", "users2", "--format", "directory", "--file", "/tmp/db.dump/testDB"}, + command: []string{"sh", "-c", "pg_dump --create --host localhost --port 5432 --username john --dbname testDB --jobs 1 --table test --table users --exclude-table test2 --exclude-table users2 --exclude-scheme=test-scheme --format custom | pg_restore --username postgres --dbname postgres --create --no-privileges --no-owner --exit-on-error"}, }, } -- GitLab From 0fa546f13342f8fb9d3353c0a987822a9e09f3ae Mon Sep 17 00:00:00 2001 From: akartasov Date: Wed, 14 Dec 2022 10:47:48 +0700 Subject: [PATCH 3/7] validate content of custom options --- .../config.example.logical_generic.yml | 3 +- engine/internal/srv/config.go | 30 ++++++++++++++ engine/internal/srv/config_test.go | 41 +++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 engine/internal/srv/config_test.go diff --git a/engine/configs/config.example.logical_generic.yml b/engine/configs/config.example.logical_generic.yml index c053f547d..1c1122397 100644 --- a/engine/configs/config.example.logical_generic.yml +++ b/engine/configs/config.example.logical_generic.yml @@ -263,7 +263,8 @@ retrieval: # Custom options for pg_dump command. customOptions: - # - "--verbose" + # - --no-publications + # - --no-subscriptions # Restores PostgreSQL database from the provided dump. If you use this block, do not use # "restore" option in the "logicalDump" job. diff --git a/engine/internal/srv/config.go b/engine/internal/srv/config.go index 0934a3e33..5ce591902 100644 --- a/engine/internal/srv/config.go +++ b/engine/internal/srv/config.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "net/http" + "regexp" "time" "github.com/docker/docker/api/types" @@ -341,6 +342,14 @@ func (s *Server) validateConfig( return err } + if err := validateCustomOptions(proj.DumpCustomOptions); err != nil { + return fmt.Errorf("invalid custom dump options: %w", err) + } + + if err := validateCustomOptions(proj.RestoreCustomOptions); err != nil { + return fmt.Errorf("invalid custom restore options: %w", err) + } + if proj.DockerImage != nil { stream, err := s.docker.ImagePull(ctx, *proj.DockerImage, types.ImagePullOptions{}) if err != nil { @@ -355,3 +364,24 @@ func (s *Server) validateConfig( return nil } + +var ( + isValidCustomOption = regexp.MustCompile("^[A-Za-z0-9-_=]+$").MatchString + errInvalidOption = fmt.Errorf("custom options must contain only letters, numbers, hyphen, underscore or equal") + errInvalidOptionType = fmt.Errorf("invalid type of custom option") +) + +func validateCustomOptions(customOptions []interface{}) error { + for _, opt := range customOptions { + castedValue, ok := opt.(string) + if !ok { + return fmt.Errorf("%w: %q", errInvalidOptionType, opt) + } + + if !isValidCustomOption(castedValue) { + return fmt.Errorf("invalid option %q: %w", castedValue, errInvalidOption) + } + } + + return nil +} diff --git a/engine/internal/srv/config_test.go b/engine/internal/srv/config_test.go new file mode 100644 index 000000000..bd50a57e4 --- /dev/null +++ b/engine/internal/srv/config_test.go @@ -0,0 +1,41 @@ +package srv + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCustomOptions(t *testing.T) { + testCases := []struct { + customOptions []interface{} + expectedResult error + }{ + { + customOptions: []interface{}{"--verbose"}, + expectedResult: nil, + }, + { + customOptions: []interface{}{"--exclude-scheme=test_scheme"}, + expectedResult: nil, + }, + { + customOptions: []interface{}{"--table=$(echo 'test')"}, + expectedResult: errInvalidOption, + }, + { + customOptions: []interface{}{"--table=test&table"}, + expectedResult: errInvalidOption, + }, + { + customOptions: []interface{}{5}, + expectedResult: errInvalidOptionType, + }, + } + + for _, tc := range testCases { + validationResult := validateCustomOptions(tc.customOptions) + + require.ErrorIs(t, validationResult, tc.expectedResult) + } +} -- GitLab From b75336d7e8d4b5226cb479b0369f5ad2240d97ad Mon Sep 17 00:00:00 2001 From: Vitaliy Kukharik Date: Thu, 15 Dec 2022 03:23:28 +0000 Subject: [PATCH 4/7] Apply 2 suggestion(s) to 2 file(s) --- engine/configs/config.example.logical_generic.yml | 1 + engine/configs/config.example.logical_rds_iam.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/engine/configs/config.example.logical_generic.yml b/engine/configs/config.example.logical_generic.yml index 1c1122397..0ac100d2f 100644 --- a/engine/configs/config.example.logical_generic.yml +++ b/engine/configs/config.example.logical_generic.yml @@ -317,6 +317,7 @@ retrieval: # Custom options for pg_restore command. customOptions: + - "--no-tablespaces" - "--no-privileges" - "--no-owner" - "--exit-on-error" diff --git a/engine/configs/config.example.logical_rds_iam.yml b/engine/configs/config.example.logical_rds_iam.yml index a3fed9eb2..22693551e 100644 --- a/engine/configs/config.example.logical_rds_iam.yml +++ b/engine/configs/config.example.logical_rds_iam.yml @@ -317,6 +317,7 @@ retrieval: # Custom options for pg_restore command. customOptions: + - "--no-tablespaces" - "--no-privileges" - "--no-owner" - "--exit-on-error" -- GitLab From e11903a3b4dfb9bfe518f5b557f6a2ffc79c09eb Mon Sep 17 00:00:00 2001 From: Nikolay Samokhvalov Date: Fri, 16 Dec 2022 02:50:18 +0000 Subject: [PATCH 5/7] Apply 1 suggestion(s) to 1 file(s) --- engine/internal/srv/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/internal/srv/config.go b/engine/internal/srv/config.go index 5ce591902..1fa76ae40 100644 --- a/engine/internal/srv/config.go +++ b/engine/internal/srv/config.go @@ -367,7 +367,7 @@ func (s *Server) validateConfig( var ( isValidCustomOption = regexp.MustCompile("^[A-Za-z0-9-_=]+$").MatchString - errInvalidOption = fmt.Errorf("custom options must contain only letters, numbers, hyphen, underscore or equal") + errInvalidOption = fmt.Errorf("due to security reasons, current implementation of custom options supports only letters, numbers, hyphen, underscore, equal sign, and double quotes") errInvalidOptionType = fmt.Errorf("invalid type of custom option") ) -- GitLab From 8890d3cb2f89846f498262ab068ee25d1674e312 Mon Sep 17 00:00:00 2001 From: akartasov Date: Fri, 16 Dec 2022 09:57:50 +0700 Subject: [PATCH 6/7] support quotes --- engine/internal/srv/config.go | 2 +- engine/internal/srv/config_test.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/internal/srv/config.go b/engine/internal/srv/config.go index 1fa76ae40..55927cb25 100644 --- a/engine/internal/srv/config.go +++ b/engine/internal/srv/config.go @@ -366,7 +366,7 @@ func (s *Server) validateConfig( } var ( - isValidCustomOption = regexp.MustCompile("^[A-Za-z0-9-_=]+$").MatchString + isValidCustomOption = regexp.MustCompile("^[A-Za-z0-9-_=\"]+$").MatchString errInvalidOption = fmt.Errorf("due to security reasons, current implementation of custom options supports only letters, numbers, hyphen, underscore, equal sign, and double quotes") errInvalidOptionType = fmt.Errorf("invalid type of custom option") ) diff --git a/engine/internal/srv/config_test.go b/engine/internal/srv/config_test.go index bd50a57e4..289b3c680 100644 --- a/engine/internal/srv/config_test.go +++ b/engine/internal/srv/config_test.go @@ -19,6 +19,10 @@ func TestCustomOptions(t *testing.T) { customOptions: []interface{}{"--exclude-scheme=test_scheme"}, expectedResult: nil, }, + { + customOptions: []interface{}{`--exclude-scheme="test_scheme"`}, + expectedResult: nil, + }, { customOptions: []interface{}{"--table=$(echo 'test')"}, expectedResult: errInvalidOption, -- GitLab From 31ade47bf782d4d727f35e482ec784fedf31c358 Mon Sep 17 00:00:00 2001 From: akartasov Date: Fri, 16 Dec 2022 10:07:38 +0700 Subject: [PATCH 7/7] fix lint --- engine/internal/srv/config.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/internal/srv/config.go b/engine/internal/srv/config.go index 55927cb25..c39120053 100644 --- a/engine/internal/srv/config.go +++ b/engine/internal/srv/config.go @@ -366,8 +366,9 @@ func (s *Server) validateConfig( } var ( - isValidCustomOption = regexp.MustCompile("^[A-Za-z0-9-_=\"]+$").MatchString - errInvalidOption = fmt.Errorf("due to security reasons, current implementation of custom options supports only letters, numbers, hyphen, underscore, equal sign, and double quotes") + isValidCustomOption = regexp.MustCompile("^[A-Za-z0-9-_=\"]+$").MatchString + errInvalidOption = fmt.Errorf("due to security reasons, current implementation of custom options supports only " + + "letters, numbers, hyphen, underscore, equal sign, and double quotes") errInvalidOptionType = fmt.Errorf("invalid type of custom option") ) -- GitLab