forked from aerospike/aerospike-management-lib
-
Notifications
You must be signed in to change notification settings - Fork 0
/
conf.go
160 lines (126 loc) · 3.52 KB
/
conf.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package asconfig
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"github.com/go-logr/logr"
"github.com/xeipuuv/gojsonschema"
lib "github.com/tanmayja/aerospike-management-lib"
)
// Conf is format for configs
// It has list for named sections like namespace, set, dc, tls, logging file
type Conf = lib.Stats
// DotConf is string of aerospike.conf content
type DotConf = string
// CfgValue is config details
type CfgValue struct {
Value interface{}
Context string
Name string
}
// confIsValid checks if passed conf is valid. If it is not valid
// then returns json validation error string. String is nil in case of other
// error conditions.
func confIsValid(log logr.Logger, flatConf *Conf, ver string) (bool, []*ValidationErr, error) {
confJSON, err := json.Marshal(expandConf(log, flatConf, sep))
if err != nil {
return false, nil, fmt.Errorf("failed to do json.Marshal for flatten aerospike conf: %v", err)
}
confLoader := gojsonschema.NewStringLoader(string(confJSON))
schema, err := getSchema(ver)
if err != nil {
return false, nil, fmt.Errorf("failed to get aerospike config schema for version %s: %v", ver, err)
}
schemaLoader := gojsonschema.NewStringLoader(schema)
result, err := gojsonschema.Validate(schemaLoader, confLoader)
if err != nil {
return false, nil, err
}
if result.Valid() {
return true, nil, nil
}
vErrs := make([]*ValidationErr, 0)
for _, desc := range result.Errors() {
vErr := &ValidationErr{
ErrType: desc.Type(),
Context: desc.Context().String(),
Description: desc.Description(),
Field: desc.Field(),
Value: desc.Value(),
}
vErrs = append(vErrs, vErr)
}
return false, vErrs, ErrConfigSchema
}
func ConfValuesValid(flatConf *Conf) []*ValidationErr {
vErrs := make([]*ValidationErr, 0)
var vErr *ValidationErr
for key, value := range *flatConf {
baseKey := BaseKey(key)
switch val := value.(type) {
case []string:
vErrs = append(vErrs, validateSlice(baseKey, val)...)
case string:
vErrs = append(vErrs, validateString(baseKey, val))
case bool, int, uint64, int64, float64:
continue
case lib.Stats:
// Ignoring changes in map type as each key is being compared separately eg. security {}.
continue
default:
vErr = &ValidationErr{
Description: "Unhandled value type in config",
Field: key,
Value: val,
}
vErrs = append(vErrs, vErr)
}
}
return vErrs
}
func validateSlice(baseKey string, val []string) []*ValidationErr {
vErrs := make([]*ValidationErr, 0)
for _, v := range val {
vErrs = append(vErrs, validateString(baseKey, v))
}
return vErrs
}
func validateString(baseKey, v string) *ValidationErr {
literals := strings.Fields(v)
switch baseKey {
case keyNodeAddressPorts:
if len(literals) > 3 {
return &ValidationErr{
Description: "Invalid node-address-ports",
Field: baseKey,
Value: v,
}
}
case keyReportDataOp:
if len(literals) > 2 {
return &ValidationErr{
Description: "Invalid report-data-op",
Field: baseKey,
Value: v,
}
}
default:
if len(literals) > 1 {
return &ValidationErr{
Description: "Invalid value",
Field: baseKey,
Value: v,
}
}
}
return nil
}
// confToDotConf takes Conf as parameter and returns server
// aerospike.conf file. Returns error in case the Conf does
// not adhere to standards.
func confToDotConf(log logr.Logger, flatConf *Conf) DotConf {
var buf bytes.Buffer
writeDotConf(log, &buf, expandConf(log, flatConf, sep), 0, nil)
return buf.String()
}