fix: config to support keys with special values (#9304)

This PR adds context-based `k=v` splits based
on the sub-system which was obtained, if the
keys are not provided an error will be thrown
during parsing, if keys are provided with wrong
values an error will be thrown. Keys can now
have values which are of a much more complex
form such as `k="v=v"` or `k=" v = v"`
and other variations.

additionally, deprecate unnecessary postgres/mysql
configuration styles, support only

- connection_string for Postgres
- dsn_string for MySQL

All other parameters are removed.
This commit is contained in:
Harshavardhana
2020-04-09 21:45:17 -07:00
committed by GitHub
parent 7c919329e8
commit 3184205519
7 changed files with 161 additions and 397 deletions

View File

@@ -78,7 +78,7 @@ func (adm *AdminClient) SetConfigKV(ctx context.Context, kv string) (err error)
}
// GetConfigKV - returns the key, value of the requested key, incoming data is encrypted.
func (adm *AdminClient) GetConfigKV(ctx context.Context, key string) (Targets, error) {
func (adm *AdminClient) GetConfigKV(ctx context.Context, key string) ([]byte, error) {
v := url.Values{}
v.Set("key", key)
@@ -100,10 +100,5 @@ func (adm *AdminClient) GetConfigKV(ctx context.Context, key string) (Targets, e
return nil, httpRespToErrorResponse(resp)
}
data, err := DecryptData(adm.getSecretKey(), resp.Body)
if err != nil {
return nil, err
}
return ParseSubSysTarget(data)
return DecryptData(adm.getSecretKey(), resp.Body)
}

View File

@@ -18,73 +18,10 @@
package madmin
import (
"bufio"
"bytes"
"fmt"
"strings"
"unicode"
)
// KV - is a shorthand of each key value.
type KV struct {
Key string `json:"key"`
Value string `json:"value"`
}
// KVS - is a shorthand for some wrapper functions
// to operate on list of key values.
type KVS []KV
// Empty - return if kv is empty
func (kvs KVS) Empty() bool {
return len(kvs) == 0
}
// Set sets a value, if not sets a default value.
func (kvs *KVS) Set(key, value string) {
for i, kv := range *kvs {
if kv.Key == key {
(*kvs)[i] = KV{
Key: key,
Value: value,
}
return
}
}
*kvs = append(*kvs, KV{
Key: key,
Value: value,
})
}
// Get - returns the value of a key, if not found returns empty.
func (kvs KVS) Get(key string) string {
v, ok := kvs.Lookup(key)
if ok {
return v
}
return ""
}
// Lookup - lookup a key in a list of KVS
func (kvs KVS) Lookup(key string) (string, bool) {
for _, kv := range kvs {
if kv.Key == key {
return kv.Value, true
}
}
return "", false
}
// Target signifies an individual target
type Target struct {
SubSystem string `json:"subSys"`
KVS KVS `json:"kvs"`
}
// Targets sub-system targets
type Targets []Target
// Standard config keys and values.
const (
EnableKey = "enable"
@@ -95,36 +32,6 @@ const (
EnableOff = "off"
)
func (kvs KVS) String() string {
var s strings.Builder
for _, kv := range kvs {
// Do not need to print state which is on.
if kv.Key == EnableKey && kv.Value == EnableOn {
continue
}
if kv.Key == CommentKey && kv.Value == "" {
continue
}
s.WriteString(kv.Key)
s.WriteString(KvSeparator)
spc := HasSpace(kv.Value)
if spc {
s.WriteString(KvDoubleQuote)
}
s.WriteString(kv.Value)
if spc {
s.WriteString(KvDoubleQuote)
}
s.WriteString(KvSpaceSeparator)
}
return s.String()
}
// Count - returns total numbers of target
func (t Targets) Count() int {
return len(t)
}
// HasSpace - returns if given string has space.
func HasSpace(s string) bool {
for _, r := range s {
@@ -135,23 +42,6 @@ func HasSpace(s string) bool {
return false
}
func (t Targets) String() string {
var s strings.Builder
count := t.Count()
// Print all "on" states entries
for _, targetKV := range t {
kv := targetKV.KVS
count--
s.WriteString(targetKV.SubSystem)
s.WriteString(KvSpaceSeparator)
s.WriteString(kv.String())
if len(t) > 1 && count > 0 {
s.WriteString(KvNewline)
}
}
return s.String()
}
// Constant separators
const (
SubSystemSeparator = `:`
@@ -170,69 +60,3 @@ func SanitizeValue(v string) string {
v = strings.TrimSuffix(strings.TrimPrefix(strings.TrimSpace(v), KvDoubleQuote), KvDoubleQuote)
return strings.TrimSuffix(strings.TrimPrefix(v, KvSingleQuote), KvSingleQuote)
}
// AddTarget - adds new targets, by parsing the input string s.
func (t *Targets) AddTarget(s string) error {
inputs := strings.SplitN(s, KvSpaceSeparator, 2)
if len(inputs) <= 1 {
return fmt.Errorf("invalid number of arguments '%s'", s)
}
subSystemValue := strings.SplitN(inputs[0], SubSystemSeparator, 2)
if len(subSystemValue) == 0 {
return fmt.Errorf("invalid number of arguments %s", s)
}
var kvs = KVS{}
var prevK string
for _, v := range strings.Fields(inputs[1]) {
kv := strings.SplitN(v, KvSeparator, 2)
if len(kv) == 0 {
continue
}
if len(kv) == 1 && prevK != "" {
value := strings.Join([]string{
kvs.Get(prevK),
SanitizeValue(kv[0]),
}, KvSpaceSeparator)
kvs.Set(prevK, value)
continue
}
if len(kv) == 2 {
prevK = kv[0]
kvs.Set(prevK, SanitizeValue(kv[1]))
continue
}
return fmt.Errorf("value for key '%s' cannot be empty", kv[0])
}
for i := range *t {
if (*t)[i].SubSystem == inputs[0] {
(*t)[i] = Target{
SubSystem: inputs[0],
KVS: kvs,
}
return nil
}
}
*t = append(*t, Target{
SubSystem: inputs[0],
KVS: kvs,
})
return nil
}
// ParseSubSysTarget - parse sub-system target
func ParseSubSysTarget(buf []byte) (Targets, error) {
var targets Targets
bio := bufio.NewScanner(bytes.NewReader(buf))
for bio.Scan() {
if err := targets.AddTarget(bio.Text()); err != nil {
return nil, err
}
}
if err := bio.Err(); err != nil {
return nil, err
}
return targets, nil
}