Skip to content

Commit 83ac19c

Browse files
committed
playbook(memcache): support instances and instances_sequence
Signed-off-by: Ericwai <[email protected]>
1 parent 8d9c430 commit 83ac19c

File tree

10 files changed

+464
-26
lines changed

10 files changed

+464
-26
lines changed

internal/configure/hosts/hc_get.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/opencurve/curveadm/internal/configure/curveadm"
3030
"github.com/opencurve/curveadm/internal/utils"
3131
"github.com/opencurve/curveadm/pkg/module"
32+
"github.com/opencurve/curveadm/pkg/variable"
3233
)
3334

3435
func (hc *HostConfig) get(i *comm.Item) interface{} {
@@ -77,14 +78,17 @@ func (hc *HostConfig) GetBecomeUser() string { return hc.getString(CONFIG_BE
7778
func (hc *HostConfig) GetLabels() []string { return hc.labels }
7879
func (hc *HostConfig) GetEnvs() []string { return hc.envs }
7980

81+
func (hc *HostConfig) GetInstances() int { return hc.instances }
82+
func (hc *HostConfig) GetInstancesSequence() int { return hc.instancesSequence }
83+
func (hc *HostConfig) GetVariables() *variable.Variables { return hc.variables }
84+
8085
func (hc *HostConfig) GetUser() string {
8186
user := hc.getString(CONFIG_USER)
8287
if user == "${user}" {
8388
return utils.GetCurrentUser()
8489
}
8590
return user
8691
}
87-
8892
func (hc *HostConfig) GetSSHConfig() *module.SSHConfig {
8993
hostname := hc.GetSSHHostname()
9094
if len(hostname) == 0 {

internal/configure/hosts/hosts.go

+266-17
Original file line numberDiff line numberDiff line change
@@ -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

3841
const (
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

114247
func (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+
173381
func 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

181417
func 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

Comments
 (0)