@@ -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,82 @@ 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+ 	if  v , ok  :=  utils .All2Str (value ); ! ok  {
126+ 		return  errno .ERR_UNSUPPORT_CONFIGURE_VALUE_TYPE .
127+ 			F ("hosts[%d].%s = %v" , hc .sequence , KEY_INSTANCES , value )
128+ 	} else  if  v , ok  :=  utils .Str2Int (v ); ! ok  {
129+ 		return  errno .ERR_CONFIGURE_VALUE_REQUIRES_INTEGER .
130+ 			F ("hosts[%d].%s = %v" , hc .sequence , KEY_INSTANCES , value )
131+ 	} else  if  v  <=  0  {
132+ 		return  errno .ERR_CONFIGURE_VALUE_REQUIRES_POSITIVE_INTEGER .
133+ 			F ("hosts[%d].%s = %v" , hc .sequence , KEY_INSTANCES , value )
134+ 	} else  {
135+ 		hc .instances  =  v 
136+ 		return  nil 
137+ 	}
138+ }
139+ 
140+ // convert config item to its required type after rendering, 
141+ // return error if convert failed 
142+ func  (hc  * HostConfig ) convert () error  {
143+ 	for  key , value  :=  range  hc .config  {
144+ 		if  key  ==  KEY_LABELS  ||  key  ==  KEY_ENVS  ||  key  ==  KEY_INSTANCES  {
145+ 			continue 
146+ 		}
147+ 		if  itemset .Get (key ) ==  nil  {
148+ 			return  errno .ERR_UNSUPPORT_HOSTS_CONFIGURE_ITEM .
149+ 				F ("hosts[%d].%s = %v" , hc .sequence , key , value )
150+ 		}
151+ 		if  v , err  :=  itemset .Build (key , value ); err  !=  nil  {
152+ 			return  err 
153+ 		} else  {
154+ 			hc .config [key ] =  v 
155+ 		}
156+ 	}
157+ 	privateKeyFile  :=  hc .GetPrivateKeyFile ()
158+ 	if  len (hc .GetName ()) ==  0  {
159+ 		return  errno .ERR_NAME_FIELD_MISSING .
160+ 			F ("hosts[%d].host/name = nil" , hc .sequence )
161+ 	}
162+ 	if  len (hc .GetHostname ()) ==  0  {
163+ 		return  errno .ERR_HOSTNAME_FIELD_MISSING .
164+ 			F ("hosts[%d].hostname = nil" , hc .sequence )
165+ 	}
166+ 	if  ! utils .IsValidAddress (hc .GetHostname ()) {
167+ 		return  errno .ERR_HOSTNAME_REQUIRES_VALID_IP_ADDRESS .
168+ 			F ("hosts[%d].hostname = %s" , hc .sequence , hc .GetHostname ())
169+ 	}
170+ 	if  hc .GetSSHPort () >  os .GetMaxPortNum () {
171+ 		return  errno .ERR_HOSTS_SSH_PORT_EXCEED_MAX_PORT_NUMBER .
172+ 			F ("hosts[%d].ssh_port = %d" , hc .sequence , hc .GetSSHPort ())
173+ 	}
174+ 	if  ! strings .HasPrefix (privateKeyFile , "/" ) {
175+ 		return  errno .ERR_PRIVATE_KEY_FILE_REQUIRE_ABSOLUTE_PATH .
176+ 			F ("hosts[%d].private_key_file = %s" , hc .sequence , privateKeyFile )
177+ 	}
110178
179+ 	if  ! hc .GetForwardAgent () {
180+ 		if  ! utils .PathExist (privateKeyFile ) {
181+ 			return  errno .ERR_PRIVATE_KEY_FILE_NOT_EXIST .
182+ 				F ("%s: no such file" , privateKeyFile )
183+ 		}
184+ 		if  utils .GetFilePermissions (privateKeyFile ) !=  PERMISSIONS_600  {
185+ 			return  errno .ERR_PRIVATE_KEY_FILE_REQUIRE_600_PERMISSIONS .
186+ 				F ("%s: mode (%d)" , privateKeyFile , utils .GetFilePermissions (privateKeyFile ))
187+ 		}
188+ 	}
111189	return  nil 
112190}
113191
114192func  (hc  * HostConfig ) Build () error  {
115193	for  key , value  :=  range  hc .config  {
116194		if  key  ==  KEY_LABELS  { // convert labels 
117- 			if  err  :=  hc .convertLables (); err  !=  nil  {
195+ 			if  err  :=  hc .convertLabels (); err  !=  nil  {
118196				return  err 
119197			}
120198			hc .config [key ] =  nil  // delete labels section 
@@ -123,7 +201,13 @@ func (hc *HostConfig) Build() error {
123201			if  err  :=  hc .convertEnvs (); err  !=  nil  {
124202				return  err 
125203			}
126- 			hc .config [key ] =  nil  // delete labels section 
204+ 			hc .config [key ] =  nil  // delete envs section 
205+ 			continue 
206+ 		} else  if  key  ==  KEY_INSTANCES  { // convert instances 
207+ 			if  err  :=  hc .convertInstances (); err  !=  nil  {
208+ 				return  err 
209+ 			}
210+ 			hc .config [key ] =  nil  // delete instances section 
127211			continue 
128212		}
129213
@@ -142,47 +226,136 @@ func (hc *HostConfig) Build() error {
142226
143227	privateKeyFile  :=  hc .GetPrivateKeyFile ()
144228	if  len (hc .GetName ()) ==  0  {
145- 		return  errno .ERR_HOST_FIELD_MISSING .
229+ 		return  errno .ERR_NAME_FIELD_MISSING .
146230			F ("hosts[%d].host/name = nil" , hc .sequence )
147- 	} else  if  len (hc .GetHostname ()) ==  0  {
231+ 	}
232+ 	if  len (hc .GetHostname ()) ==  0  {
148233		return  errno .ERR_HOSTNAME_FIELD_MISSING .
149234			F ("hosts[%d].hostname = nil" , hc .sequence )
150- 	} else  if  ! utils .IsValidAddress (hc .GetHostname ()) {
235+ 	}
236+ 	if  ! utils .IsValidAddress (hc .GetHostname ()) {
151237		return  errno .ERR_HOSTNAME_REQUIRES_VALID_IP_ADDRESS .
152238			F ("hosts[%d].hostname = %s" , hc .sequence , hc .GetHostname ())
153- 	} else  if  hc .GetSSHPort () >  os .GetMaxPortNum () {
239+ 	}
240+ 	if  hc .GetSSHPort () >  os .GetMaxPortNum () {
154241		return  errno .ERR_HOSTS_SSH_PORT_EXCEED_MAX_PORT_NUMBER .
155242			F ("hosts[%d].ssh_port = %d" , hc .sequence , hc .GetSSHPort ())
156- 	} else  if  ! strings .HasPrefix (privateKeyFile , "/" ) {
243+ 	}
244+ 	if  ! strings .HasPrefix (privateKeyFile , "/" ) {
157245		return  errno .ERR_PRIVATE_KEY_FILE_REQUIRE_ABSOLUTE_PATH .
158246			F ("hosts[%d].private_key_file = %s" , hc .sequence , privateKeyFile )
159247	}
160248
161- 	if  hc .GetForwardAgent ()  ==   false  {
249+ 	if  ! hc .GetForwardAgent () {
162250		if  ! utils .PathExist (privateKeyFile ) {
163251			return  errno .ERR_PRIVATE_KEY_FILE_NOT_EXIST .
164252				F ("%s: no such file" , privateKeyFile )
165- 		} else  if  utils .GetFilePermissions (privateKeyFile ) !=  PERMISSIONS_600  {
253+ 		}
254+ 		if  utils .GetFilePermissions (privateKeyFile ) !=  PERMISSIONS_600  {
166255			return  errno .ERR_PRIVATE_KEY_FILE_REQUIRE_600_PERMISSIONS .
167256				F ("%s: mode (%d)" , privateKeyFile , utils .GetFilePermissions (privateKeyFile ))
168257		}
169258	}
170259	return  nil 
171260}
172261
262+ // "PORT=112${instancesSquence}" -> "PORT=11201" 
263+ func  (hc  * HostConfig ) renderVariables () error  {
264+ 	//0. get vars 
265+ 	vars  :=  hc .GetVariables ()
266+ 	if  err  :=  vars .Build (); err  !=  nil  {
267+ 		log .Error ("Build variables failed" ,
268+ 			log .Field ("error" , err ))
269+ 		return  errno .ERR_RESOLVE_VARIABLE_FAILED .E (err )
270+ 	}
271+ 	//1. all config to str 
272+ 	for  k , v  :=  range  hc .config  {
273+ 		if  v  ==  nil  {
274+ 			continue 
275+ 		}
276+ 		if  strv , ok  :=  utils .All2Str (v ); ! ok  {
277+ 			return  errno .ERR_UNSUPPORT_CONFIGURE_VALUE_TYPE .
278+ 				F ("%s: %v" , k , v )
279+ 		} else  {
280+ 			hc .config [k ] =  strv 
281+ 		}
282+ 	}
283+ 	//2. rendering 
284+ 	//render labels and envs 
285+ 	err  :=  func (allStrs  ... []string ) error  {
286+ 		for  k  :=  range  allStrs  {
287+ 			strs  :=  allStrs [k ]
288+ 			for  i  :=  range  strs  {
289+ 				realValue , err  :=  vars .Rendering (strs [i ])
290+ 				if  err  !=  nil  {
291+ 					return  err 
292+ 				}
293+ 				strs [i ] =  realValue 
294+ 			}
295+ 		}
296+ 		return  nil 
297+ 	}(hc .labels , hc .envs )
298+ 	if  err  !=  nil  {
299+ 		return  errno .ERR_RENDERING_VARIABLE_FAILED .E (err )
300+ 	}
301+ 	//render config 
302+ 	for  k , v  :=  range  hc .config  {
303+ 		if  v  ==  nil  {
304+ 			continue 
305+ 		}
306+ 		realv , err  :=  vars .Rendering (v .(string ))
307+ 		if  err  !=  nil  {
308+ 			return  errno .ERR_RENDERING_VARIABLE_FAILED .E (err )
309+ 		}
310+ 		hc .config [k ] =  realv 
311+ 		build .DEBUG (build .DEBUG_TOPOLOGY ,
312+ 			build.Field {Key : k , Value : v },
313+ 			build.Field {Key : k , Value : realv })
314+ 	}
315+ 	//3. convert config item to its required type after rendering, 
316+ 	//	 return error if convert failed 
317+ 	return  hc .convert ()
318+ }
319+ 
173320func  NewHostConfig (sequence  int , config  map [string ]interface {}) * HostConfig  {
321+ 	vars  :=  variable .NewVariables ()
322+ 	return  & HostConfig {
323+ 		sequence :  sequence ,
324+ 		config :    config ,
325+ 		labels :    []string {},
326+ 		envs :      []string {},
327+ 		variables : vars ,
328+ 		//instances and instancesSquence only used in the memcached deploy 
329+ 		instances :         1 ,
330+ 		instancesSequence : 1 ,
331+ 	}
332+ }
333+ 
334+ // deepcopy a HostConfig with instancesSquence and return it (new variables) 
335+ func  copyHostConfig (src  * HostConfig , instancesSquence  int ) * HostConfig  {
336+ 	//deepcopy labels 
337+ 	newlabels  :=  make ([]string , len (src .labels ))
338+ 	copy (newlabels , src .labels )
339+ 	//deepcopy envs 
340+ 	newenvs  :=  make ([]string , len (src .envs ))
341+ 	copy (newenvs , src .envs )
342+ 	//create a new variables 
343+ 	vars  :=  variable .NewVariables ()
174344	return  & HostConfig {
175- 		sequence : sequence ,
176- 		config :   config ,
177- 		labels :   []string {},
345+ 		sequence :          src .sequence ,
346+ 		config :            utils .DeepCopy (src .config ),
347+ 		labels :            newlabels ,
348+ 		envs :              newenvs ,
349+ 		variables :         vars ,
350+ 		instances :         src .instances ,
351+ 		instancesSequence : instancesSquence ,
178352	}
179353}
180354
181355func  ParseHosts (data  string ) ([]* HostConfig , error ) {
182356	if  len (data ) ==  0  {
183357		return  nil , errno .ERR_EMPTY_HOSTS 
184358	}
185- 
186359	parser  :=  viper .NewWithOptions (viper .KeyDelimiter ("::" ))
187360	parser .SetConfigType ("yaml" )
188361	err  :=  parser .ReadConfig (bytes .NewBuffer ([]byte (data )))
@@ -210,9 +383,23 @@ func ParseHosts(data string) ([]*HostConfig, error) {
210383			return  nil , errno .ERR_DUPLICATE_NAME .
211384				F ("duplicate host: %s" , hc .GetName ())
212385		}
213- 		hcs  =  append (hcs , hc )
386+ 		//produce the instances of hc, append to hcs. (used in memcached deploy) 
387+ 		instances  :=  hc .GetInstances ()
388+ 		for  instancesSquence  :=  1 ; instancesSquence  <=  instances ; instancesSquence ++  {
389+ 			hc_new  :=  copyHostConfig (hc , instancesSquence )
390+ 			hcs  =  append (hcs , hc_new )
391+ 		}
214392		exist [hc .GetName ()] =  true 
215393	}
394+ 	//add Variables and Rendering 
395+ 	for  idx , hc  :=  range  hcs  {
396+ 		if  err  =  AddHostVariables (hcs , idx ); err  !=  nil  {
397+ 			return  nil , err  // already is error code 
398+ 		} else  if  err  =  hc .renderVariables (); err  !=  nil  {
399+ 			return  nil , err  // already is error code 
400+ 		}
401+ 		hc .GetVariables ().Debug ()
402+ 	}
216403	build .DEBUG (build .DEBUG_HOSTS , hosts )
217404	return  hcs , nil 
218405}
0 commit comments