@@ -28,16 +28,20 @@ import (
28
28
"bytes"
29
29
"strings"
30
30
31
+ "github.com/spf13/viper"
32
+
31
33
"github.com/opencurve/curveadm/internal/build"
32
34
"github.com/opencurve/curveadm/internal/configure/os"
33
35
"github.com/opencurve/curveadm/internal/errno"
34
36
"github.com/opencurve/curveadm/internal/utils"
35
- "github.com/spf13/viper"
37
+ log "github.com/opencurve/curveadm/pkg/log/glg"
38
+ "github.com/opencurve/curveadm/pkg/variable"
36
39
)
37
40
38
41
const (
39
- KEY_LABELS = "labels"
40
- KEY_ENVS = "envs"
42
+ KEY_LABELS = "labels"
43
+ KEY_ENVS = "envs"
44
+ KEY_INSTANCES = "instances"
41
45
42
46
PERMISSIONS_600 = 384 // -rw------- (256 + 128 = 384)
43
47
)
@@ -49,10 +53,16 @@ type (
49
53
}
50
54
51
55
HostConfig struct {
52
- sequence int
53
- config map [string ]interface {}
54
- labels []string
55
- envs []string
56
+ sequence int
57
+ config map [string ]interface {}
58
+ labels []string
59
+ envs []string
60
+ variables * variable.Variables
61
+ //instances and instancesSequence only used in the memcached deploy
62
+ //instances is the num of memcached servers will be deployed in the same host
63
+ instances int
64
+ //instancesSquence is the sequence num of memcached servers in the same host
65
+ instancesSequence int
56
66
}
57
67
)
58
68
@@ -71,7 +81,7 @@ func merge(parent, child map[string]interface{}) {
71
81
}
72
82
}
73
83
74
- func (hc * HostConfig ) convertLables () error {
84
+ func (hc * HostConfig ) convertLabels () error {
75
85
value := hc .config [KEY_LABELS ]
76
86
slice , ok := (value ).([]interface {})
77
87
if ! ok {
@@ -107,14 +117,84 @@ func (hc *HostConfig) convertEnvs() error {
107
117
hc .envs = append (hc .envs , v )
108
118
}
109
119
}
120
+ return nil
121
+ }
122
+
123
+ func (hc * HostConfig ) convertInstances () error {
124
+ value := hc .config [KEY_INSTANCES ]
125
+ instancesStr , instances , ok := "" , 0 , false
126
+ if instancesStr , ok = utils .All2Str (value ); ! ok {
127
+ return errno .ERR_UNSUPPORT_CONFIGURE_VALUE_TYPE .
128
+ F ("hosts[%d].%s = %v" , hc .sequence , KEY_INSTANCES , value )
129
+ }
130
+ if instances , ok = utils .Str2Int (instancesStr ); ! ok {
131
+ return errno .ERR_CONFIGURE_VALUE_REQUIRES_INTEGER .
132
+ F ("hosts[%d].%s = %v" , hc .sequence , KEY_INSTANCES , value )
133
+ }
134
+ if instances <= 0 {
135
+ return errno .ERR_CONFIGURE_VALUE_REQUIRES_POSITIVE_INTEGER .
136
+ F ("hosts[%d].%s = %v" , hc .sequence , KEY_INSTANCES , value )
137
+ }
138
+ hc .instances = instances
139
+ return nil
140
+ }
110
141
142
+ // convert config item to its required type after rendering,
143
+ // return error if convert failed
144
+ func (hc * HostConfig ) convert () error {
145
+ for key , value := range hc .config {
146
+ if key == KEY_LABELS || key == KEY_ENVS || key == KEY_INSTANCES {
147
+ continue
148
+ }
149
+ if itemset .Get (key ) == nil {
150
+ return errno .ERR_UNSUPPORT_HOSTS_CONFIGURE_ITEM .
151
+ F ("hosts[%d].%s = %v" , hc .sequence , key , value )
152
+ }
153
+ if v , err := itemset .Build (key , value ); err != nil {
154
+ return err
155
+ } else {
156
+ hc .config [key ] = v
157
+ }
158
+ }
159
+ privateKeyFile := hc .GetPrivateKeyFile ()
160
+ if len (hc .GetName ()) == 0 {
161
+ return errno .ERR_NAME_FIELD_MISSING .
162
+ F ("hosts[%d].host/name = nil" , hc .sequence )
163
+ }
164
+ if len (hc .GetHostname ()) == 0 {
165
+ return errno .ERR_HOSTNAME_FIELD_MISSING .
166
+ F ("hosts[%d].hostname = nil" , hc .sequence )
167
+ }
168
+ if ! utils .IsValidAddress (hc .GetHostname ()) {
169
+ return errno .ERR_HOSTNAME_REQUIRES_VALID_IP_ADDRESS .
170
+ F ("hosts[%d].hostname = %s" , hc .sequence , hc .GetHostname ())
171
+ }
172
+ if hc .GetSSHPort () > os .GetMaxPortNum () {
173
+ return errno .ERR_HOSTS_SSH_PORT_EXCEED_MAX_PORT_NUMBER .
174
+ F ("hosts[%d].ssh_port = %d" , hc .sequence , hc .GetSSHPort ())
175
+ }
176
+ if ! strings .HasPrefix (privateKeyFile , "/" ) {
177
+ return errno .ERR_PRIVATE_KEY_FILE_REQUIRE_ABSOLUTE_PATH .
178
+ F ("hosts[%d].private_key_file = %s" , hc .sequence , privateKeyFile )
179
+ }
180
+
181
+ if ! hc .GetForwardAgent () {
182
+ if ! utils .PathExist (privateKeyFile ) {
183
+ return errno .ERR_PRIVATE_KEY_FILE_NOT_EXIST .
184
+ F ("%s: no such file" , privateKeyFile )
185
+ }
186
+ if utils .GetFilePermissions (privateKeyFile ) != PERMISSIONS_600 {
187
+ return errno .ERR_PRIVATE_KEY_FILE_REQUIRE_600_PERMISSIONS .
188
+ F ("%s: mode (%d)" , privateKeyFile , utils .GetFilePermissions (privateKeyFile ))
189
+ }
190
+ }
111
191
return nil
112
192
}
113
193
114
194
func (hc * HostConfig ) Build () error {
115
195
for key , value := range hc .config {
116
196
if key == KEY_LABELS { // convert labels
117
- if err := hc .convertLables (); err != nil {
197
+ if err := hc .convertLabels (); err != nil {
118
198
return err
119
199
}
120
200
hc .config [key ] = nil // delete labels section
@@ -123,7 +203,13 @@ func (hc *HostConfig) Build() error {
123
203
if err := hc .convertEnvs (); err != nil {
124
204
return err
125
205
}
126
- hc .config [key ] = nil // delete labels section
206
+ hc .config [key ] = nil // delete envs section
207
+ continue
208
+ } else if key == KEY_INSTANCES { // convert instances
209
+ if err := hc .convertInstances (); err != nil {
210
+ return err
211
+ }
212
+ hc .config [key ] = nil // delete instances section
127
213
continue
128
214
}
129
215
@@ -142,47 +228,136 @@ func (hc *HostConfig) Build() error {
142
228
143
229
privateKeyFile := hc .GetPrivateKeyFile ()
144
230
if len (hc .GetName ()) == 0 {
145
- return errno .ERR_HOST_FIELD_MISSING .
231
+ return errno .ERR_NAME_FIELD_MISSING .
146
232
F ("hosts[%d].host/name = nil" , hc .sequence )
147
- } else if len (hc .GetHostname ()) == 0 {
233
+ }
234
+ if len (hc .GetHostname ()) == 0 {
148
235
return errno .ERR_HOSTNAME_FIELD_MISSING .
149
236
F ("hosts[%d].hostname = nil" , hc .sequence )
150
- } else if ! utils .IsValidAddress (hc .GetHostname ()) {
237
+ }
238
+ if ! utils .IsValidAddress (hc .GetHostname ()) {
151
239
return errno .ERR_HOSTNAME_REQUIRES_VALID_IP_ADDRESS .
152
240
F ("hosts[%d].hostname = %s" , hc .sequence , hc .GetHostname ())
153
- } else if hc .GetSSHPort () > os .GetMaxPortNum () {
241
+ }
242
+ if hc .GetSSHPort () > os .GetMaxPortNum () {
154
243
return errno .ERR_HOSTS_SSH_PORT_EXCEED_MAX_PORT_NUMBER .
155
244
F ("hosts[%d].ssh_port = %d" , hc .sequence , hc .GetSSHPort ())
156
- } else if ! strings .HasPrefix (privateKeyFile , "/" ) {
245
+ }
246
+ if ! strings .HasPrefix (privateKeyFile , "/" ) {
157
247
return errno .ERR_PRIVATE_KEY_FILE_REQUIRE_ABSOLUTE_PATH .
158
248
F ("hosts[%d].private_key_file = %s" , hc .sequence , privateKeyFile )
159
249
}
160
250
161
- if hc .GetForwardAgent () == false {
251
+ if ! hc .GetForwardAgent () {
162
252
if ! utils .PathExist (privateKeyFile ) {
163
253
return errno .ERR_PRIVATE_KEY_FILE_NOT_EXIST .
164
254
F ("%s: no such file" , privateKeyFile )
165
- } else if utils .GetFilePermissions (privateKeyFile ) != PERMISSIONS_600 {
255
+ }
256
+ if utils .GetFilePermissions (privateKeyFile ) != PERMISSIONS_600 {
166
257
return errno .ERR_PRIVATE_KEY_FILE_REQUIRE_600_PERMISSIONS .
167
258
F ("%s: mode (%d)" , privateKeyFile , utils .GetFilePermissions (privateKeyFile ))
168
259
}
169
260
}
170
261
return nil
171
262
}
172
263
264
+ // "PORT=112${instancesSquence}" -> "PORT=11201"
265
+ func (hc * HostConfig ) renderVariables () error {
266
+ //0. get vars
267
+ vars := hc .GetVariables ()
268
+ if err := vars .Build (); err != nil {
269
+ log .Error ("Build variables failed" ,
270
+ log .Field ("error" , err ))
271
+ return errno .ERR_RESOLVE_VARIABLE_FAILED .E (err )
272
+ }
273
+ //1. all config to str
274
+ for k , v := range hc .config {
275
+ if v == nil {
276
+ continue
277
+ }
278
+ if strv , ok := utils .All2Str (v ); ! ok {
279
+ return errno .ERR_UNSUPPORT_CONFIGURE_VALUE_TYPE .
280
+ F ("%s: %v" , k , v )
281
+ } else {
282
+ hc .config [k ] = strv
283
+ }
284
+ }
285
+ //2. rendering
286
+ //render labels and envs
287
+ err := func (allStrs ... []string ) error {
288
+ for k := range allStrs {
289
+ strs := allStrs [k ]
290
+ for i := range strs {
291
+ realValue , err := vars .Rendering (strs [i ])
292
+ if err != nil {
293
+ return err
294
+ }
295
+ strs [i ] = realValue
296
+ }
297
+ }
298
+ return nil
299
+ }(hc .labels , hc .envs )
300
+ if err != nil {
301
+ return errno .ERR_RENDERING_VARIABLE_FAILED .E (err )
302
+ }
303
+ //render config
304
+ for k , v := range hc .config {
305
+ if v == nil {
306
+ continue
307
+ }
308
+ realv , err := vars .Rendering (v .(string ))
309
+ if err != nil {
310
+ return errno .ERR_RENDERING_VARIABLE_FAILED .E (err )
311
+ }
312
+ hc .config [k ] = realv
313
+ build .DEBUG (build .DEBUG_TOPOLOGY ,
314
+ build.Field {Key : k , Value : v },
315
+ build.Field {Key : k , Value : realv })
316
+ }
317
+ //3. convert config item to its required type after rendering,
318
+ // return error if convert failed
319
+ return hc .convert ()
320
+ }
321
+
173
322
func NewHostConfig (sequence int , config map [string ]interface {}) * HostConfig {
323
+ vars := variable .NewVariables ()
324
+ return & HostConfig {
325
+ sequence : sequence ,
326
+ config : config ,
327
+ labels : []string {},
328
+ envs : []string {},
329
+ variables : vars ,
330
+ //instances and instancesSquence only used in the memcached deploy
331
+ instances : 1 ,
332
+ instancesSequence : 1 ,
333
+ }
334
+ }
335
+
336
+ // deepcopy a HostConfig with instancesSquence and return it (new variables)
337
+ func copyHostConfig (src * HostConfig , instancesSquence int ) * HostConfig {
338
+ //deepcopy labels
339
+ newlabels := make ([]string , len (src .labels ))
340
+ copy (newlabels , src .labels )
341
+ //deepcopy envs
342
+ newenvs := make ([]string , len (src .envs ))
343
+ copy (newenvs , src .envs )
344
+ //create a new variables
345
+ vars := variable .NewVariables ()
174
346
return & HostConfig {
175
- sequence : sequence ,
176
- config : config ,
177
- labels : []string {},
347
+ sequence : src .sequence ,
348
+ config : utils .DeepCopy (src .config ),
349
+ labels : newlabels ,
350
+ envs : newenvs ,
351
+ variables : vars ,
352
+ instances : src .instances ,
353
+ instancesSequence : instancesSquence ,
178
354
}
179
355
}
180
356
181
357
func ParseHosts (data string ) ([]* HostConfig , error ) {
182
358
if len (data ) == 0 {
183
359
return nil , errno .ERR_EMPTY_HOSTS
184
360
}
185
-
186
361
parser := viper .NewWithOptions (viper .KeyDelimiter ("::" ))
187
362
parser .SetConfigType ("yaml" )
188
363
err := parser .ReadConfig (bytes .NewBuffer ([]byte (data )))
@@ -210,9 +385,23 @@ func ParseHosts(data string) ([]*HostConfig, error) {
210
385
return nil , errno .ERR_DUPLICATE_NAME .
211
386
F ("duplicate host: %s" , hc .GetName ())
212
387
}
213
- hcs = append (hcs , hc )
388
+ //produce the instances of hc, append to hcs. (used in memcached deploy)
389
+ instances := hc .GetInstances ()
390
+ for instancesSquence := 1 ; instancesSquence <= instances ; instancesSquence ++ {
391
+ hc_new := copyHostConfig (hc , instancesSquence )
392
+ hcs = append (hcs , hc_new )
393
+ }
214
394
exist [hc .GetName ()] = true
215
395
}
396
+ //add Variables and Rendering
397
+ for idx , hc := range hcs {
398
+ if err = AddHostVariables (hcs , idx ); err != nil {
399
+ return nil , err // already is error code
400
+ } else if err = hc .renderVariables (); err != nil {
401
+ return nil , err // already is error code
402
+ }
403
+ hc .GetVariables ().Debug ()
404
+ }
216
405
build .DEBUG (build .DEBUG_HOSTS , hosts )
217
406
return hcs , nil
218
407
}
0 commit comments