@@ -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,84 @@ func (hc *HostConfig) convertEnvs() error {
107117 hc .envs = append (hc .envs , v )
108118 }
109119 }
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+ }
110141
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+ }
111191 return nil
112192}
113193
114194func (hc * HostConfig ) Build () error {
115195 for key , value := range hc .config {
116196 if key == KEY_LABELS { // convert labels
117- if err := hc .convertLables (); err != nil {
197+ if err := hc .convertLabels (); err != nil {
118198 return err
119199 }
120200 hc .config [key ] = nil // delete labels section
@@ -123,7 +203,13 @@ func (hc *HostConfig) Build() error {
123203 if err := hc .convertEnvs (); err != nil {
124204 return err
125205 }
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
127213 continue
128214 }
129215
@@ -142,47 +228,136 @@ func (hc *HostConfig) Build() error {
142228
143229 privateKeyFile := hc .GetPrivateKeyFile ()
144230 if len (hc .GetName ()) == 0 {
145- return errno .ERR_HOST_FIELD_MISSING .
231+ return errno .ERR_NAME_FIELD_MISSING .
146232 F ("hosts[%d].host/name = nil" , hc .sequence )
147- } else if len (hc .GetHostname ()) == 0 {
233+ }
234+ if len (hc .GetHostname ()) == 0 {
148235 return errno .ERR_HOSTNAME_FIELD_MISSING .
149236 F ("hosts[%d].hostname = nil" , hc .sequence )
150- } else if ! utils .IsValidAddress (hc .GetHostname ()) {
237+ }
238+ if ! utils .IsValidAddress (hc .GetHostname ()) {
151239 return errno .ERR_HOSTNAME_REQUIRES_VALID_IP_ADDRESS .
152240 F ("hosts[%d].hostname = %s" , hc .sequence , hc .GetHostname ())
153- } else if hc .GetSSHPort () > os .GetMaxPortNum () {
241+ }
242+ if hc .GetSSHPort () > os .GetMaxPortNum () {
154243 return errno .ERR_HOSTS_SSH_PORT_EXCEED_MAX_PORT_NUMBER .
155244 F ("hosts[%d].ssh_port = %d" , hc .sequence , hc .GetSSHPort ())
156- } else if ! strings .HasPrefix (privateKeyFile , "/" ) {
245+ }
246+ if ! strings .HasPrefix (privateKeyFile , "/" ) {
157247 return errno .ERR_PRIVATE_KEY_FILE_REQUIRE_ABSOLUTE_PATH .
158248 F ("hosts[%d].private_key_file = %s" , hc .sequence , privateKeyFile )
159249 }
160250
161- if hc .GetForwardAgent () == false {
251+ if ! hc .GetForwardAgent () {
162252 if ! utils .PathExist (privateKeyFile ) {
163253 return errno .ERR_PRIVATE_KEY_FILE_NOT_EXIST .
164254 F ("%s: no such file" , privateKeyFile )
165- } else if utils .GetFilePermissions (privateKeyFile ) != PERMISSIONS_600 {
255+ }
256+ if utils .GetFilePermissions (privateKeyFile ) != PERMISSIONS_600 {
166257 return errno .ERR_PRIVATE_KEY_FILE_REQUIRE_600_PERMISSIONS .
167258 F ("%s: mode (%d)" , privateKeyFile , utils .GetFilePermissions (privateKeyFile ))
168259 }
169260 }
170261 return nil
171262}
172263
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+
173322func 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 ()
174346 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 ,
178354 }
179355}
180356
181357func ParseHosts (data string ) ([]* HostConfig , error ) {
182358 if len (data ) == 0 {
183359 return nil , errno .ERR_EMPTY_HOSTS
184360 }
185-
186361 parser := viper .NewWithOptions (viper .KeyDelimiter ("::" ))
187362 parser .SetConfigType ("yaml" )
188363 err := parser .ReadConfig (bytes .NewBuffer ([]byte (data )))
@@ -210,9 +385,23 @@ func ParseHosts(data string) ([]*HostConfig, error) {
210385 return nil , errno .ERR_DUPLICATE_NAME .
211386 F ("duplicate host: %s" , hc .GetName ())
212387 }
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+ }
214394 exist [hc .GetName ()] = true
215395 }
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+ }
216405 build .DEBUG (build .DEBUG_HOSTS , hosts )
217406 return hcs , nil
218407}
0 commit comments