@@ -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