Skip to content

Commit

Permalink
playbook(memcache): support instances and instances_sequence
Browse files Browse the repository at this point in the history
Signed-off-by: Ericwai <[email protected]>
  • Loading branch information
EricWai committed Dec 20, 2023
1 parent a703f1e commit 51c7f6f
Show file tree
Hide file tree
Showing 10 changed files with 465 additions and 27 deletions.
6 changes: 5 additions & 1 deletion internal/configure/hosts/hc_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/opencurve/curveadm/internal/configure/curveadm"
"github.com/opencurve/curveadm/internal/utils"
"github.com/opencurve/curveadm/pkg/module"
"github.com/opencurve/curveadm/pkg/variable"
)

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

func (hc *HostConfig) GetInstances() int { return hc.instances }
func (hc *HostConfig) GetInstancesSequence() int { return hc.instancesSequence }
func (hc *HostConfig) GetVariables() *variable.Variables { return hc.variables }

func (hc *HostConfig) GetUser() string {
user := hc.getString(CONFIG_USER)
if user == "${user}" {
return utils.GetCurrentUser()
}
return user
}

func (hc *HostConfig) GetSSHConfig() *module.SSHConfig {
hostname := hc.GetSSHHostname()
if len(hostname) == 0 {
Expand Down
283 changes: 266 additions & 17 deletions internal/configure/hosts/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,20 @@ import (
"bytes"
"strings"

"github.com/spf13/viper"

"github.com/opencurve/curveadm/internal/build"
"github.com/opencurve/curveadm/internal/configure/os"
"github.com/opencurve/curveadm/internal/errno"
"github.com/opencurve/curveadm/internal/utils"
"github.com/spf13/viper"
log "github.com/opencurve/curveadm/pkg/log/glg"
"github.com/opencurve/curveadm/pkg/variable"
)

const (
KEY_LABELS = "labels"
KEY_ENVS = "envs"
KEY_LABELS = "labels"
KEY_ENVS = "envs"
KEY_INSTANCES = "instances"

PERMISSIONS_600 = 384 // -rw------- (256 + 128 = 384)
)
Expand All @@ -49,10 +53,16 @@ type (
}

HostConfig struct {
sequence int
config map[string]interface{}
labels []string
envs []string
sequence int
config map[string]interface{}
labels []string
envs []string
variables *variable.Variables
//instances and instancesSequence only used in the memcached deploy
//instances is the num of memcached servers will be deployed in the same host
instances int
//instancesSquence is the sequence num of memcached servers in the same host
instancesSequence int
}
)

Expand All @@ -71,7 +81,7 @@ func merge(parent, child map[string]interface{}) {
}
}

func (hc *HostConfig) convertLables() error {
func (hc *HostConfig) convertLabels() error {
value := hc.config[KEY_LABELS]
slice, ok := (value).([]interface{})
if !ok {
Expand Down Expand Up @@ -107,14 +117,137 @@ func (hc *HostConfig) convertEnvs() error {
hc.envs = append(hc.envs, v)
}
}
return nil
}

// read the instances value from hc.config
func (hc *HostConfig) convertInstances() error {
value := hc.config[KEY_INSTANCES]
v, ok := utils.All2Str(value)
if !ok {
return errno.ERR_UNSUPPORT_CONFIGURE_VALUE_TYPE.
F("hosts[%d].%s = %v", hc.sequence, KEY_INSTANCES, value)
}
if v, ok := utils.Str2Int(v); !ok {
return errno.ERR_CONFIGURE_VALUE_REQUIRES_INTEGER.
F("hosts[%d].%s = %v", hc.sequence, KEY_INSTANCES, value)
} else if v <= 0 {
return errno.ERR_CONFIGURE_VALUE_REQUIRES_POSITIVE_INTEGER.
F("hosts[%d].%s = %v", hc.sequence, KEY_INSTANCES, value)
} else {
hc.instances = v
return nil
}
}

// convert config item to its require type after rendering,
// return error if convert failed
//func (hc *HostConfig) convert() error {
// for _, item := range itemset.GetAll() {
// k := item.Key()
// value := hc.get(item) // return config value or default value
// if value == nil {
// continue
// }
// v, ok := utils.All2Str(value)
// if !ok {
// return errno.ERR_UNSUPPORT_CONFIGURE_VALUE_TYPE.
// F("%s: %v", k, value)
// }
//
// switch item.require {
// case REQUIRE_ANY:
// // do nothing
// case REQUIRE_INT:
// if intv, ok := utils.Str2Int(v); !ok {
// return errno.ERR_CONFIGURE_VALUE_REQUIRES_INTEGER.
// F("%s: %v", k, value)
// } else {
// hc.config[k] = intv
// }
// case REQUIRE_STRING:
// if len(v) == 0 {
// return errno.ERR_CONFIGURE_VALUE_REQUIRES_NON_EMPTY_STRING.
// F("%s: %v", k, value)
// }
// case REQUIRE_BOOL:
// if boolv, ok := utils.Str2Bool(v); !ok {
// return errno.ERR_CONFIGURE_VALUE_REQUIRES_BOOL.
// F("%s: %v", k, value)
// } else {
// hc.config[k] = boolv
// }
// case REQUIRE_POSITIVE_INTEGER:
// if intv, ok := utils.Str2Int(v); !ok {
// return errno.ERR_CONFIGURE_VALUE_REQUIRES_INTEGER.
// F("%s: %v", k, value)
// } else if intv <= 0 {
// return errno.ERR_CONFIGURE_VALUE_REQUIRES_POSITIVE_INTEGER.
// F("%s: %v", k, value)
// } else {
// hc.config[k] = intv
// }
// }
// }
// return nil
//}

// convert config item to its required type after rendering,
// return error if convert failed
func (hc *HostConfig) convert() error {
for key, value := range hc.config {
if key == KEY_LABELS {
continue
} else if key == KEY_ENVS {
continue
} else if key == KEY_INSTANCES {
continue
}
if itemset.Get(key) == nil {
return errno.ERR_UNSUPPORT_HOSTS_CONFIGURE_ITEM.
F("hosts[%d].%s = %v", hc.sequence, key, value)
}
v, err := itemset.Build(key, value)
if err != nil {
return err
} else {
hc.config[key] = v
}
}
privateKeyFile := hc.GetPrivateKeyFile()
if len(hc.GetName()) == 0 {
return errno.ERR_NAME_FIELD_MISSING.
F("hosts[%d].host/name = nil", hc.sequence)
} else if len(hc.GetHostname()) == 0 {
return errno.ERR_HOSTNAME_FIELD_MISSING.
F("hosts[%d].hostname = nil", hc.sequence)
} else if !utils.IsValidAddress(hc.GetHostname()) {
return errno.ERR_HOSTNAME_REQUIRES_VALID_IP_ADDRESS.
F("hosts[%d].hostname = %s", hc.sequence, hc.GetHostname())
} else if hc.GetSSHPort() > os.GetMaxPortNum() {
return errno.ERR_HOSTS_SSH_PORT_EXCEED_MAX_PORT_NUMBER.
F("hosts[%d].ssh_port = %d", hc.sequence, hc.GetSSHPort())
} else if !strings.HasPrefix(privateKeyFile, "/") {
return errno.ERR_PRIVATE_KEY_FILE_REQUIRE_ABSOLUTE_PATH.
F("hosts[%d].private_key_file = %s", hc.sequence, privateKeyFile)
}

if !hc.GetForwardAgent() {
if !utils.PathExist(privateKeyFile) {
return errno.ERR_PRIVATE_KEY_FILE_NOT_EXIST.
F("%s: no such file", privateKeyFile)
} else if utils.GetFilePermissions(privateKeyFile) != PERMISSIONS_600 {
return errno.ERR_PRIVATE_KEY_FILE_REQUIRE_600_PERMISSIONS.
F("%s: mode (%d)", privateKeyFile, utils.GetFilePermissions(privateKeyFile))
}
}
return nil
}

func (hc *HostConfig) Build() error {
for key, value := range hc.config {
if key == KEY_LABELS { // convert labels
if err := hc.convertLables(); err != nil {
if err := hc.convertLabels(); err != nil {
return err
}
hc.config[key] = nil // delete labels section
Expand All @@ -123,7 +256,13 @@ func (hc *HostConfig) Build() error {
if err := hc.convertEnvs(); err != nil {
return err
}
hc.config[key] = nil // delete labels section
hc.config[key] = nil // delete envs section
continue
} else if key == KEY_INSTANCES { // convert instances
if err := hc.convertInstances(); err != nil {
return err
}
hc.config[key] = nil // delete instances section
continue
}

Expand All @@ -142,7 +281,7 @@ func (hc *HostConfig) Build() error {

privateKeyFile := hc.GetPrivateKeyFile()
if len(hc.GetName()) == 0 {
return errno.ERR_HOST_FIELD_MISSING.
return errno.ERR_NAME_FIELD_MISSING.
F("hosts[%d].host/name = nil", hc.sequence)
} else if len(hc.GetHostname()) == 0 {
return errno.ERR_HOSTNAME_FIELD_MISSING.
Expand All @@ -158,7 +297,7 @@ func (hc *HostConfig) Build() error {
F("hosts[%d].private_key_file = %s", hc.sequence, privateKeyFile)
}

if hc.GetForwardAgent() == false {
if !hc.GetForwardAgent() {
if !utils.PathExist(privateKeyFile) {
return errno.ERR_PRIVATE_KEY_FILE_NOT_EXIST.
F("%s: no such file", privateKeyFile)
Expand All @@ -170,19 +309,115 @@ func (hc *HostConfig) Build() error {
return nil
}

// "PORT=112${instancesSquence}" -> "PORT=11201"
func (hc *HostConfig) renderVariables() error {
//0. get vars
vars := hc.GetVariables()
if err := vars.Build(); err != nil {
log.Error("Build variables failed",
log.Field("error", err))
return errno.ERR_RESOLVE_VARIABLE_FAILED.E(err)
}
//1. all config to str
for k, v := range hc.config {
if v == nil {
continue
}
if strv, ok := utils.All2Str(v); ok {
hc.config[k] = strv
} else {
return errno.ERR_UNSUPPORT_CONFIGURE_VALUE_TYPE.
F("%s: %v", k, v)
}
}
//2. rendering
//render labels
for i := range hc.labels {
err := func(value *string) error {
realValue, err := vars.Rendering(*value)
if err != nil {
return err
}
*value = realValue
return nil
}(&hc.labels[i])
if err != nil {
return errno.ERR_RENDERING_VARIABLE_FAILED.E(err)
}
}
//render envs
for i := range hc.envs {
err := func(value *string) error {
realValue, err := vars.Rendering(*value)
if err != nil {
return err
}
*value = realValue
return nil
}(&hc.envs[i])
if err != nil {
return errno.ERR_RENDERING_VARIABLE_FAILED.E(err)
}
}
//render config
for k, v := range hc.config {
if v == nil {
continue
}
realv, err := vars.Rendering(v.(string))
if err != nil {
return errno.ERR_RENDERING_VARIABLE_FAILED.E(err)
}
hc.config[k] = realv
build.DEBUG(build.DEBUG_TOPOLOGY,
build.Field{Key: k, Value: v},
build.Field{Key: k, Value: realv})
}
//3. convert config item to its required type after rendering,
// return error if convert failed
return hc.convert()
}

func NewHostConfig(sequence int, config map[string]interface{}) *HostConfig {
vars := variable.NewVariables()

return &HostConfig{
sequence: sequence,
config: config,
labels: []string{},
sequence: sequence,
config: config,
labels: []string{},
envs: []string{},
variables: vars,
//instances and instancesSquence only used in the memcached deploy
instances: 1,
instancesSequence: 1,
}
}

// deepcopy a HostConfig with instancesSquence and return it (new variables)
func copyHostConfig(src *HostConfig, instancesSquence int) *HostConfig {
//deepcopy labels
newlabels := make([]string, len(src.labels))
copy(newlabels, src.labels)
//deepcopy envs
newenvs := make([]string, len(src.envs))
copy(newenvs, src.envs)
//create a new variables
vars := variable.NewVariables()
return &HostConfig{
sequence: src.sequence,
config: utils.DeepCopy(src.config),
labels: newlabels,
envs: newenvs,
variables: vars,
instances: src.instances,
instancesSequence: instancesSquence,
}
}

func ParseHosts(data string) ([]*HostConfig, error) {
if len(data) == 0 {
return nil, errno.ERR_EMPTY_HOSTS
}

parser := viper.NewWithOptions(viper.KeyDelimiter("::"))
parser.SetConfigType("yaml")
err := parser.ReadConfig(bytes.NewBuffer([]byte(data)))
Expand Down Expand Up @@ -210,9 +445,23 @@ func ParseHosts(data string) ([]*HostConfig, error) {
return nil, errno.ERR_DUPLICATE_NAME.
F("duplicate host: %s", hc.GetName())
}
hcs = append(hcs, hc)
//produce the instances of hc, append to hcs. (used in memcached deploy)
instances := hc.GetInstances()
for instancesSquence := 1; instancesSquence <= instances; instancesSquence++ {
hc_new := copyHostConfig(hc, instancesSquence)
hcs = append(hcs, hc_new)
}
exist[hc.GetName()] = true
}
//add Variables and Rendering
for idx, hc := range hcs {
if err = AddHostVariables(hcs, idx); err != nil {
return nil, err // already is error code
} else if err = hc.renderVariables(); err != nil {
return nil, err // already is error code
}
hc.GetVariables().Debug()
}
build.DEBUG(build.DEBUG_HOSTS, hosts)
return hcs, nil
}
Loading

0 comments on commit 51c7f6f

Please sign in to comment.