Skip to content

Commit 58ab650

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

File tree

10 files changed

+407
-31
lines changed

10 files changed

+407
-31
lines changed

internal/configure/hosts/hc_get.go

Lines changed: 5 additions & 1 deletion
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

Lines changed: 209 additions & 22 deletions
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,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

114192
func (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+
173320
func 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

181355
func 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
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package hosts
2+
3+
import (
4+
"fmt"
5+
"github.com/opencurve/curveadm/internal/errno"
6+
"github.com/opencurve/curveadm/pkg/variable"
7+
)
8+
9+
type Var struct {
10+
name string
11+
resolved bool
12+
}
13+
14+
var (
15+
hostVars = []Var{
16+
{name: "instances_sequence"},
17+
}
18+
)
19+
20+
func addVariables(hcs []*HostConfig, idx int, vars []Var) error {
21+
hc := hcs[idx]
22+
for _, v := range vars {
23+
err := hc.GetVariables().Register(variable.Variable{
24+
Name: v.name,
25+
Value: getValue(v.name, hcs, idx),
26+
})
27+
if err != nil {
28+
return errno.ERR_REGISTER_VARIABLE_FAILED.E(err)
29+
}
30+
}
31+
32+
return nil
33+
}
34+
35+
func AddHostVariables(hcs []*HostConfig, idx int) error {
36+
return addVariables(hcs, idx, hostVars)
37+
}
38+
39+
func getValue(name string, hcs []*HostConfig, idx int) string {
40+
hc := hcs[idx]
41+
switch name {
42+
case "instances_sequence":
43+
return fmt.Sprintf("%02d", hc.GetInstancesSequence())
44+
}
45+
return ""
46+
}

0 commit comments

Comments
 (0)