@@ -28,16 +28,20 @@ import (
2828 "bytes"
2929 "strings"
3030
31+ "github.com/spf13/viper"
32+
3133 "github.com/opencurve/curveadm/internal/build"
3234 "github.com/opencurve/curveadm/internal/configure/os"
3335 "github.com/opencurve/curveadm/internal/errno"
3436 "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"
3639)
3740
3841const (
39- KEY_LABELS = "labels"
40- KEY_ENVS = "envs"
42+ KEY_LABELS = "labels"
43+ KEY_ENVS = "envs"
44+ KEY_INSTANCES = "instances"
4145
4246 PERMISSIONS_600 = 384 // -rw------- (256 + 128 = 384)
4347)
@@ -49,10 +53,16 @@ type (
4953 }
5054
5155 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
5666 }
5767)
5868
@@ -71,7 +81,7 @@ func merge(parent, child map[string]interface{}) {
7181 }
7282}
7383
74- func (hc * HostConfig ) convertLables () error {
84+ func (hc * HostConfig ) convertLabels () error {
7585 value := hc .config [KEY_LABELS ]
7686 slice , ok := (value ).([]interface {})
7787 if ! ok {
@@ -107,14 +117,137 @@ func (hc *HostConfig) convertEnvs() error {
107117 hc .envs = append (hc .envs , v )
108118 }
109119 }
120+ return nil
121+ }
122+
123+ // read the instances value from hc.config
124+ func (hc * HostConfig ) convertInstances () error {
125+ value := hc .config [KEY_INSTANCES ]
126+ v , ok := utils .All2Str (value )
127+ if ! ok {
128+ return errno .ERR_UNSUPPORT_CONFIGURE_VALUE_TYPE .
129+ F ("hosts[%d].%s = %v" , hc .sequence , KEY_INSTANCES , value )
130+ }
131+ if v , ok := utils .Str2Int (v ); ! ok {
132+ return errno .ERR_CONFIGURE_VALUE_REQUIRES_INTEGER .
133+ F ("hosts[%d].%s = %v" , hc .sequence , KEY_INSTANCES , value )
134+ } else if v <= 0 {
135+ return errno .ERR_CONFIGURE_VALUE_REQUIRES_POSITIVE_INTEGER .
136+ F ("hosts[%d].%s = %v" , hc .sequence , KEY_INSTANCES , value )
137+ } else {
138+ hc .instances = v
139+ return nil
140+ }
141+ }
142+
143+ // convert config item to its require type after rendering,
144+ // return error if convert failed
145+ //func (hc *HostConfig) convert() error {
146+ // for _, item := range itemset.GetAll() {
147+ // k := item.Key()
148+ // value := hc.get(item) // return config value or default value
149+ // if value == nil {
150+ // continue
151+ // }
152+ // v, ok := utils.All2Str(value)
153+ // if !ok {
154+ // return errno.ERR_UNSUPPORT_CONFIGURE_VALUE_TYPE.
155+ // F("%s: %v", k, value)
156+ // }
157+ //
158+ // switch item.require {
159+ // case REQUIRE_ANY:
160+ // // do nothing
161+ // case REQUIRE_INT:
162+ // if intv, ok := utils.Str2Int(v); !ok {
163+ // return errno.ERR_CONFIGURE_VALUE_REQUIRES_INTEGER.
164+ // F("%s: %v", k, value)
165+ // } else {
166+ // hc.config[k] = intv
167+ // }
168+ // case REQUIRE_STRING:
169+ // if len(v) == 0 {
170+ // return errno.ERR_CONFIGURE_VALUE_REQUIRES_NON_EMPTY_STRING.
171+ // F("%s: %v", k, value)
172+ // }
173+ // case REQUIRE_BOOL:
174+ // if boolv, ok := utils.Str2Bool(v); !ok {
175+ // return errno.ERR_CONFIGURE_VALUE_REQUIRES_BOOL.
176+ // F("%s: %v", k, value)
177+ // } else {
178+ // hc.config[k] = boolv
179+ // }
180+ // case REQUIRE_POSITIVE_INTEGER:
181+ // if intv, ok := utils.Str2Int(v); !ok {
182+ // return errno.ERR_CONFIGURE_VALUE_REQUIRES_INTEGER.
183+ // F("%s: %v", k, value)
184+ // } else if intv <= 0 {
185+ // return errno.ERR_CONFIGURE_VALUE_REQUIRES_POSITIVE_INTEGER.
186+ // F("%s: %v", k, value)
187+ // } else {
188+ // hc.config[k] = intv
189+ // }
190+ // }
191+ // }
192+ // return nil
193+ //}
194+
195+ // convert config item to its required type after rendering,
196+ // return error if convert failed
197+ func (hc * HostConfig ) convert () error {
198+ for key , value := range hc .config {
199+ if key == KEY_LABELS {
200+ continue
201+ } else if key == KEY_ENVS {
202+ continue
203+ } else if key == KEY_INSTANCES {
204+ continue
205+ }
206+ if itemset .Get (key ) == nil {
207+ return errno .ERR_UNSUPPORT_HOSTS_CONFIGURE_ITEM .
208+ F ("hosts[%d].%s = %v" , hc .sequence , key , value )
209+ }
210+ v , err := itemset .Build (key , value )
211+ if err != nil {
212+ return err
213+ } else {
214+ hc .config [key ] = v
215+ }
216+ }
217+ privateKeyFile := hc .GetPrivateKeyFile ()
218+ if len (hc .GetName ()) == 0 {
219+ return errno .ERR_NAME_FIELD_MISSING .
220+ F ("hosts[%d].host/name = nil" , hc .sequence )
221+ } else if len (hc .GetHostname ()) == 0 {
222+ return errno .ERR_HOSTNAME_FIELD_MISSING .
223+ F ("hosts[%d].hostname = nil" , hc .sequence )
224+ } else if ! utils .IsValidAddress (hc .GetHostname ()) {
225+ return errno .ERR_HOSTNAME_REQUIRES_VALID_IP_ADDRESS .
226+ F ("hosts[%d].hostname = %s" , hc .sequence , hc .GetHostname ())
227+ } else if hc .GetSSHPort () > os .GetMaxPortNum () {
228+ return errno .ERR_HOSTS_SSH_PORT_EXCEED_MAX_PORT_NUMBER .
229+ F ("hosts[%d].ssh_port = %d" , hc .sequence , hc .GetSSHPort ())
230+ } else if ! strings .HasPrefix (privateKeyFile , "/" ) {
231+ return errno .ERR_PRIVATE_KEY_FILE_REQUIRE_ABSOLUTE_PATH .
232+ F ("hosts[%d].private_key_file = %s" , hc .sequence , privateKeyFile )
233+ }
110234
235+ if ! hc .GetForwardAgent () {
236+ if ! utils .PathExist (privateKeyFile ) {
237+ return errno .ERR_PRIVATE_KEY_FILE_NOT_EXIST .
238+ F ("%s: no such file" , privateKeyFile )
239+ } else if utils .GetFilePermissions (privateKeyFile ) != PERMISSIONS_600 {
240+ return errno .ERR_PRIVATE_KEY_FILE_REQUIRE_600_PERMISSIONS .
241+ F ("%s: mode (%d)" , privateKeyFile , utils .GetFilePermissions (privateKeyFile ))
242+ }
243+ }
111244 return nil
112245}
113246
114247func (hc * HostConfig ) Build () error {
115248 for key , value := range hc .config {
116249 if key == KEY_LABELS { // convert labels
117- if err := hc .convertLables (); err != nil {
250+ if err := hc .convertLabels (); err != nil {
118251 return err
119252 }
120253 hc .config [key ] = nil // delete labels section
@@ -123,7 +256,13 @@ func (hc *HostConfig) Build() error {
123256 if err := hc .convertEnvs (); err != nil {
124257 return err
125258 }
126- hc .config [key ] = nil // delete labels section
259+ hc .config [key ] = nil // delete envs section
260+ continue
261+ } else if key == KEY_INSTANCES { // convert instances
262+ if err := hc .convertInstances (); err != nil {
263+ return err
264+ }
265+ hc .config [key ] = nil // delete instances section
127266 continue
128267 }
129268
@@ -142,7 +281,7 @@ func (hc *HostConfig) Build() error {
142281
143282 privateKeyFile := hc .GetPrivateKeyFile ()
144283 if len (hc .GetName ()) == 0 {
145- return errno .ERR_HOST_FIELD_MISSING .
284+ return errno .ERR_NAME_FIELD_MISSING .
146285 F ("hosts[%d].host/name = nil" , hc .sequence )
147286 } else if len (hc .GetHostname ()) == 0 {
148287 return errno .ERR_HOSTNAME_FIELD_MISSING .
@@ -158,7 +297,7 @@ func (hc *HostConfig) Build() error {
158297 F ("hosts[%d].private_key_file = %s" , hc .sequence , privateKeyFile )
159298 }
160299
161- if hc .GetForwardAgent () == false {
300+ if ! hc .GetForwardAgent () {
162301 if ! utils .PathExist (privateKeyFile ) {
163302 return errno .ERR_PRIVATE_KEY_FILE_NOT_EXIST .
164303 F ("%s: no such file" , privateKeyFile )
@@ -170,19 +309,115 @@ func (hc *HostConfig) Build() error {
170309 return nil
171310}
172311
312+ // "PORT=112${instancesSquence}" -> "PORT=11201"
313+ func (hc * HostConfig ) renderVariables () error {
314+ //0. get vars
315+ vars := hc .GetVariables ()
316+ if err := vars .Build (); err != nil {
317+ log .Error ("Build variables failed" ,
318+ log .Field ("error" , err ))
319+ return errno .ERR_RESOLVE_VARIABLE_FAILED .E (err )
320+ }
321+ //1. all config to str
322+ for k , v := range hc .config {
323+ if v == nil {
324+ continue
325+ }
326+ if strv , ok := utils .All2Str (v ); ok {
327+ hc .config [k ] = strv
328+ } else {
329+ return errno .ERR_UNSUPPORT_CONFIGURE_VALUE_TYPE .
330+ F ("%s: %v" , k , v )
331+ }
332+ }
333+ //2. rendering
334+ //render labels
335+ for i := range hc .labels {
336+ err := func (value * string ) error {
337+ realValue , err := vars .Rendering (* value )
338+ if err != nil {
339+ return err
340+ }
341+ * value = realValue
342+ return nil
343+ }(& hc .labels [i ])
344+ if err != nil {
345+ return errno .ERR_RENDERING_VARIABLE_FAILED .E (err )
346+ }
347+ }
348+ //render envs
349+ for i := range hc .envs {
350+ err := func (value * string ) error {
351+ realValue , err := vars .Rendering (* value )
352+ if err != nil {
353+ return err
354+ }
355+ * value = realValue
356+ return nil
357+ }(& hc .envs [i ])
358+ if err != nil {
359+ return errno .ERR_RENDERING_VARIABLE_FAILED .E (err )
360+ }
361+ }
362+ //render config
363+ for k , v := range hc .config {
364+ if v == nil {
365+ continue
366+ }
367+ realv , err := vars .Rendering (v .(string ))
368+ if err != nil {
369+ return errno .ERR_RENDERING_VARIABLE_FAILED .E (err )
370+ }
371+ hc .config [k ] = realv
372+ build .DEBUG (build .DEBUG_TOPOLOGY ,
373+ build.Field {Key : k , Value : v },
374+ build.Field {Key : k , Value : realv })
375+ }
376+ //3. convert config item to its required type after rendering,
377+ // return error if convert failed
378+ return hc .convert ()
379+ }
380+
173381func NewHostConfig (sequence int , config map [string ]interface {}) * HostConfig {
382+ vars := variable .NewVariables ()
383+
174384 return & HostConfig {
175- sequence : sequence ,
176- config : config ,
177- labels : []string {},
385+ sequence : sequence ,
386+ config : config ,
387+ labels : []string {},
388+ envs : []string {},
389+ variables : vars ,
390+ //instances and instancesSquence only used in the memcached deploy
391+ instances : 1 ,
392+ instancesSequence : 1 ,
393+ }
394+ }
395+
396+ // deepcopy a HostConfig with instancesSquence and return it (new variables)
397+ func copyHostConfig (src * HostConfig , instancesSquence int ) * HostConfig {
398+ //deepcopy labels
399+ newlabels := make ([]string , len (src .labels ))
400+ copy (newlabels , src .labels )
401+ //deepcopy envs
402+ newenvs := make ([]string , len (src .envs ))
403+ copy (newenvs , src .envs )
404+ //create a new variables
405+ vars := variable .NewVariables ()
406+ return & HostConfig {
407+ sequence : src .sequence ,
408+ config : utils .DeepCopy (src .config ),
409+ labels : newlabels ,
410+ envs : newenvs ,
411+ variables : vars ,
412+ instances : src .instances ,
413+ instancesSequence : instancesSquence ,
178414 }
179415}
180416
181417func ParseHosts (data string ) ([]* HostConfig , error ) {
182418 if len (data ) == 0 {
183419 return nil , errno .ERR_EMPTY_HOSTS
184420 }
185-
186421 parser := viper .NewWithOptions (viper .KeyDelimiter ("::" ))
187422 parser .SetConfigType ("yaml" )
188423 err := parser .ReadConfig (bytes .NewBuffer ([]byte (data )))
@@ -210,9 +445,23 @@ func ParseHosts(data string) ([]*HostConfig, error) {
210445 return nil , errno .ERR_DUPLICATE_NAME .
211446 F ("duplicate host: %s" , hc .GetName ())
212447 }
213- hcs = append (hcs , hc )
448+ //produce the instances of hc, append to hcs. (used in memcached deploy)
449+ instances := hc .GetInstances ()
450+ for instancesSquence := 1 ; instancesSquence <= instances ; instancesSquence ++ {
451+ hc_new := copyHostConfig (hc , instancesSquence )
452+ hcs = append (hcs , hc_new )
453+ }
214454 exist [hc .GetName ()] = true
215455 }
456+ //add Variables and Rendering
457+ for idx , hc := range hcs {
458+ if err = AddHostVariables (hcs , idx ); err != nil {
459+ return nil , err // already is error code
460+ } else if err = hc .renderVariables (); err != nil {
461+ return nil , err // already is error code
462+ }
463+ hc .GetVariables ().Debug ()
464+ }
216465 build .DEBUG (build .DEBUG_HOSTS , hosts )
217466 return hcs , nil
218467}
0 commit comments