diff --git a/internal/configure/hosts/hc_get.go b/internal/configure/hosts/hc_get.go index c7c0c6a07..614e59d69 100644 --- a/internal/configure/hosts/hc_get.go +++ b/internal/configure/hosts/hc_get.go @@ -25,25 +25,25 @@ package hosts import ( - comm "github.com/opencurve/curveadm/internal/configure/common" "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{} { +func (hc *HostConfig) get(i *item) interface{} { if v, ok := hc.config[i.Key()]; ok { return v } - defaultValue := i.DefaultValue() + defaultValue := i.defaultValue if defaultValue != nil && utils.IsFunc(defaultValue) { return defaultValue.(func(*HostConfig) interface{})(hc) } return defaultValue } -func (hc *HostConfig) getString(i *comm.Item) string { +func (hc *HostConfig) getString(i *item) string { v := hc.get(i) if v == nil { return "" @@ -51,7 +51,7 @@ func (hc *HostConfig) getString(i *comm.Item) string { return v.(string) } -func (hc *HostConfig) getInt(i *comm.Item) int { +func (hc *HostConfig) getInt(i *item) int { v := hc.get(i) if v == nil { return 0 @@ -59,7 +59,7 @@ func (hc *HostConfig) getInt(i *comm.Item) int { return v.(int) } -func (hc *HostConfig) getBool(i *comm.Item) bool { +func (hc *HostConfig) getBool(i *item) bool { v := hc.get(i) if v == nil { return false @@ -67,16 +67,18 @@ func (hc *HostConfig) getBool(i *comm.Item) bool { return v.(bool) } -func (hc *HostConfig) GetHost() string { return hc.getString(CONFIG_HOST) } -func (hc *HostConfig) GetHostname() string { return hc.getString(CONFIG_HOSTNAME) } -func (hc *HostConfig) GetSSHHostname() string { return hc.getString(CONFIG_SSH_HOSTNAME) } -func (hc *HostConfig) GetSSHPort() int { return hc.getInt(CONFIG_SSH_PORT) } -func (hc *HostConfig) GetPrivateKeyFile() string { return hc.getString(CONFIG_PRIVATE_CONFIG_FILE) } -func (hc *HostConfig) GetForwardAgent() bool { return hc.getBool(CONFIG_FORWARD_AGENT) } -func (hc *HostConfig) GetBecomeUser() string { return hc.getString(CONFIG_BECOME_USER) } -func (hc *HostConfig) GetLabels() []string { return hc.labels } -func (hc *HostConfig) GetEnvs() []string { return hc.envs } - +func (hc *HostConfig) GetHost() string { return hc.getString(CONFIG_HOST) } +func (hc *HostConfig) GetHostname() string { return hc.getString(CONFIG_HOSTNAME) } +func (hc *HostConfig) GetSSHHostname() string { return hc.getString(CONFIG_SSH_HOSTNAME) } +func (hc *HostConfig) GetSSHPort() int { return hc.getInt(CONFIG_SSH_PORT) } +func (hc *HostConfig) GetPrivateKeyFile() string { return hc.getString(CONFIG_PRIVATE_CONFIG_FILE) } +func (hc *HostConfig) GetForwardAgent() bool { return hc.getBool(CONFIG_FORWARD_AGENT) } +func (hc *HostConfig) GetBecomeUser() string { return hc.getString(CONFIG_BECOME_USER) } +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}" { @@ -84,7 +86,6 @@ func (hc *HostConfig) GetUser() string { } return user } - func (hc *HostConfig) GetSSHConfig() *module.SSHConfig { hostname := hc.GetSSHHostname() if len(hostname) == 0 { diff --git a/internal/configure/hosts/hc_item.go b/internal/configure/hosts/hc_item.go index 6900cbd63..1edbf2f41 100644 --- a/internal/configure/hosts/hc_item.go +++ b/internal/configure/hosts/hc_item.go @@ -26,75 +26,201 @@ package hosts import ( "fmt" + "github.com/opencurve/curveadm/internal/errno" - comm "github.com/opencurve/curveadm/internal/configure/common" "github.com/opencurve/curveadm/internal/utils" ) const ( + REQUIRE_ANY = iota + REQUIRE_INT + REQUIRE_STRING + REQUIRE_BOOL + REQUIRE_POSITIVE_INTEGER + REQUIRE_STRING_SLICE + DEFAULT_SSH_PORT = 22 ) +type ( + // config item + item struct { + key string + require int + exclude bool // exclude for service config + defaultValue interface{} // nil means no default value + } + + itemSet struct { + items []*item + key2item map[string]*item + } +) + var ( - itemset = comm.NewItemSet() + itemset = &itemSet{ + items: []*item{}, + key2item: map[string]*item{}, + } - CONFIG_HOST = itemset.Insert( + CONFIG_HOST = itemset.insert( "host", - comm.REQUIRE_STRING, + REQUIRE_STRING, false, nil, ) - CONFIG_HOSTNAME = itemset.Insert( + CONFIG_HOSTNAME = itemset.insert( "hostname", - comm.REQUIRE_STRING, + REQUIRE_STRING, false, nil, ) - CONFIG_SSH_HOSTNAME = itemset.Insert( + CONFIG_SSH_HOSTNAME = itemset.insert( "ssh_hostname", - comm.REQUIRE_STRING, + REQUIRE_STRING, false, nil, ) - CONFIG_USER = itemset.Insert( + CONFIG_USER = itemset.insert( "user", - comm.REQUIRE_STRING, + REQUIRE_STRING, false, func(hc *HostConfig) interface{} { return utils.GetCurrentUser() }, ) - CONFIG_SSH_PORT = itemset.Insert( + CONFIG_SSH_PORT = itemset.insert( "ssh_port", - comm.REQUIRE_POSITIVE_INTEGER, + REQUIRE_POSITIVE_INTEGER, false, DEFAULT_SSH_PORT, ) - CONFIG_PRIVATE_CONFIG_FILE = itemset.Insert( + CONFIG_PRIVATE_CONFIG_FILE = itemset.insert( "private_key_file", - comm.REQUIRE_STRING, + REQUIRE_STRING, false, func(hc *HostConfig) interface{} { return fmt.Sprintf("%s/.ssh/id_rsa", utils.GetCurrentHomeDir()) }, ) - CONFIG_FORWARD_AGENT = itemset.Insert( + CONFIG_FORWARD_AGENT = itemset.insert( "forward_agent", - comm.REQUIRE_BOOL, + REQUIRE_BOOL, false, false, ) - CONFIG_BECOME_USER = itemset.Insert( + CONFIG_BECOME_USER = itemset.insert( "become_user", - comm.REQUIRE_STRING, + REQUIRE_STRING, false, nil, ) ) + +func convertSlice[T any](key, value any) ([]T, error) { + var slice []T + if !utils.IsAnySlice(value) || len(value.([]any)) == 0 { + return slice, errno.ERR_CONFIGURE_VALUE_REQUIRES_NONEMPTY_SLICE + } + anySlice := value.([]any) + switch anySlice[0].(type) { + case T: + for _, element := range anySlice { + slice = append(slice, element.(T)) + } + default: + return slice, errno.ERR_UNSUPPORT_CONFIGURE_VALUE_TYPE. + F("%s: %v", key, value) + } + + return slice, nil +} + +func (i *item) Key() string { + return i.key +} + +func (itemset *itemSet) insert(key string, require int, exclude bool, defaultValue interface{}) *item { + i := &item{key, require, exclude, defaultValue} + itemset.key2item[key] = i + itemset.items = append(itemset.items, i) + return i +} + +func (itemset *itemSet) get(key string) *item { + return itemset.key2item[key] +} + +func (itemset *itemSet) getAll() []*item { + return itemset.items +} + +func (itemset *itemSet) Build(key string, value interface{}) (interface{}, error) { + item := itemset.get(key) + if item == nil { + return value, nil + } + + v, ok := utils.All2Str(value) + if !ok { + if !utils.IsAnySlice(value) { + return nil, errno.ERR_UNSUPPORT_CONFIGURE_VALUE_TYPE. + F("%s: %v", key, value) + } + } + + switch item.require { + case REQUIRE_ANY: + // do nothing + + case REQUIRE_STRING: + if len(v) == 0 { + return nil, errno.ERR_CONFIGURE_VALUE_REQUIRES_NON_EMPTY_STRING. + F("%s: %v", key, value) + } else { + return v, nil + } + + case REQUIRE_INT: + if v, ok := utils.Str2Int(v); !ok { + return nil, errno.ERR_CONFIGURE_VALUE_REQUIRES_INTEGER. + F("%s: %v", key, value) + } else { + return v, nil + } + + case REQUIRE_POSITIVE_INTEGER: + if v, ok := utils.Str2Int(v); !ok { + return nil, errno.ERR_CONFIGURE_VALUE_REQUIRES_INTEGER. + F("%s: %v", key, value) + } else if v <= 0 { + return nil, errno.ERR_CONFIGURE_VALUE_REQUIRES_POSITIVE_INTEGER. + F("%s: %v", key, value) + } else { + return v, nil + } + + case REQUIRE_BOOL: + if v, ok := utils.Str2Bool(v); !ok { + return nil, errno.ERR_CONFIGURE_VALUE_REQUIRES_BOOL. + F("%s: %v", key, value) + } else { + return v, nil + } + + case REQUIRE_STRING_SLICE: + return convertSlice[string](key, value) + + default: + // do nothing + } + + return value, nil +} diff --git a/internal/configure/hosts/hosts.go b/internal/configure/hosts/hosts.go index c8fa86011..a54f6acf6 100644 --- a/internal/configure/hosts/hosts.go +++ b/internal/configure/hosts/hosts.go @@ -26,18 +26,21 @@ package hosts import ( "bytes" + "github.com/opencurve/curveadm/pkg/variable" "strings" "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" + log "github.com/opencurve/curveadm/pkg/log/glg" "github.com/spf13/viper" ) const ( - KEY_LABELS = "labels" - KEY_ENVS = "envs" + KEY_LABELS = "labels" + KEY_ENVS = "envs" + KEY_INSTANCES = "instances" PERMISSIONS_600 = 384 // -rw------- (256 + 128 = 384) ) @@ -49,10 +52,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 } ) @@ -111,6 +120,80 @@ func (hc *HostConfig) convertEnvs() error { 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 { + if !utils.IsAnySlice(value) { + 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 +} + func (hc *HostConfig) Build() error { for key, value := range hc.config { if key == KEY_LABELS { // convert labels @@ -123,11 +206,17 @@ 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 } - if itemset.Get(key) == nil { + if itemset.get(key) == nil { return errno.ERR_UNSUPPORT_HOSTS_CONFIGURE_ITEM. F("hosts[%d].%s = %v", hc.sequence, key, value) } @@ -158,7 +247,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) @@ -170,11 +259,108 @@ 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 require 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, } } @@ -182,7 +368,6 @@ 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))) @@ -210,9 +395,23 @@ func ParseHosts(data string) ([]*HostConfig, error) { return nil, errno.ERR_DUPLICATE_HOST. F("duplicate host: %s", hc.GetHost()) } - hcs = append(hcs, hc) + //produce the instances of hc, append to hcs. + instances := hc.GetInstances() + for instancesSquence := 1; instancesSquence <= instances; instancesSquence++ { + hc_new := copyHostConfig(hc, instancesSquence) + hcs = append(hcs, hc_new) + } exist[hc.GetHost()] = 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 } diff --git a/internal/configure/hosts/variables.go b/internal/configure/hosts/variables.go new file mode 100644 index 000000000..4d31bfb66 --- /dev/null +++ b/internal/configure/hosts/variables.go @@ -0,0 +1,46 @@ +package hosts + +import ( + "fmt" + "github.com/opencurve/curveadm/internal/errno" + "github.com/opencurve/curveadm/pkg/variable" +) + +type Var struct { + name string + resolved bool +} + +var ( + hostVars = []Var{ + {name: "instances_sequence"}, + } +) + +func addVariables(hcs []*HostConfig, idx int, vars []Var) error { + hc := hcs[idx] + for _, v := range vars { + err := hc.GetVariables().Register(variable.Variable{ + Name: v.name, + Value: getValue(v.name, hcs, idx), + }) + if err != nil { + return errno.ERR_REGISTER_VARIABLE_FAILED.E(err) + } + } + + return nil +} + +func AddHostVariables(hcs []*HostConfig, idx int) error { + return addVariables(hcs, idx, hostVars) +} + +func getValue(name string, hcs []*HostConfig, idx int) string { + hc := hcs[idx] + switch name { + case "instances_sequence": + return fmt.Sprintf("%02d", hc.GetInstancesSequence()) + } + return "" +} diff --git a/internal/errno/errno.go b/internal/errno/errno.go index 46b8228c5..7b402037f 100644 --- a/internal/errno/errno.go +++ b/internal/errno/errno.go @@ -287,6 +287,7 @@ var ( ERR_CONFIGURE_VALUE_REQUIRES_NON_EMPTY_STRING = EC(301004, "configure value requires non-empty string") ERR_CONFIGURE_VALUE_REQUIRES_POSITIVE_INTEGER = EC(301005, "configure value requires positive integer") ERR_CONFIGURE_VALUE_REQUIRES_STRING_SLICE = EC(301006, "configure value requires string array") + ERR_CONFIGURE_VALUE_REQUIRES_NONEMPTY_SLICE = EC(301007, "configure value requires nonempty array") ERR_UNSUPPORT_VARIABLE_VALUE_TYPE = EC(301100, "unsupport variable value type") ERR_INVALID_VARIABLE_VALUE = EC(301101, "invalid variable value") diff --git a/internal/utils/common.go b/internal/utils/common.go index 056a649a8..91515a81c 100644 --- a/internal/utils/common.go +++ b/internal/utils/common.go @@ -97,6 +97,10 @@ func IsStringAnyMap(v interface{}) bool { return Type(v) == "string_interface_map" } +func IsAnySlice(v interface{}) bool { + return Type(v) == "any_slice" +} + func IsFunc(v interface{}) bool { return reflect.TypeOf(v).Kind() == reflect.Func } diff --git a/playbook/memcached/hosts_instances.yaml b/playbook/memcached/hosts_instances.yaml new file mode 100644 index 000000000..487ee49ee --- /dev/null +++ b/playbook/memcached/hosts_instances.yaml @@ -0,0 +1,46 @@ +global: + user: curve + ssh_port: 22 + private_key_file: /home/curve/.ssh/id_rsa + +hosts: + - host: server-host1 + hostname: 10.0.1.1 + labels: + - memcached + envs: + - SUDO_ALIAS=sudo + - ENGINE=docker + - IMAGE=memcached:1.6.17 + - EXPORTER_IMAGE=quay.io/prometheus/memcached-exporter:v0.13.0 + - LISTEN=10.0.1.1 + - PORT=112${instances_sequence} + - EXPORTER_PORT=91${instances_sequence} + - USER=root + - MEMORY_LIMIT=32768 # item memory in megabytes + - MAX_ITEM_SIZE=8m # adjusts max item size (default: 1m, min: 1k, max: 1024m) + - EXT_PATH=/mnt/memcachefile/cachefile:10${instances_sequence}G + - EXT_WBUF_SIZE=8 # size in megabytes of page write buffers. + - EXT_ITEM_AGE=1 # store items idle at least this long (seconds, default: no age limit) + - VERBOSE="v" + instances: 3 + + - host: server-host2 + hostname: 10.0.1.2 + labels: + - memcached + envs: + - SUDO_ALIAS=sudo + - ENGINE=docker + - IMAGE=memcached:1.6.17 + - EXPORTER_IMAGE=quay.io/prometheus/memcached-exporter:v0.13.0 + - LISTEN=10.0.1.2 + - PORT=11211 + - EXPORTER_PORT=9151 + - USER=root + - MEMORY_LIMIT=32768 # item memory in megabytes + - MAX_ITEM_SIZE=8m # adjusts max item size (default: 1m, min: 1k, max: 1024m) + - EXT_PATH=/mnt/memcachefile/cachefile:1024G + - EXT_WBUF_SIZE=8 # size in megabytes of page write buffers. + - EXT_ITEM_AGE=1 # store items idle at least this long (seconds, default: no age limit) + - VERBOSE="v" \ No newline at end of file diff --git a/playbook/memcached/scripts/clean.sh b/playbook/memcached/scripts/clean.sh index 84e0f2a6a..96b36554a 100644 --- a/playbook/memcached/scripts/clean.sh +++ b/playbook/memcached/scripts/clean.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash g_container_name="memcached-"${PORT} +g_exporter_container_name="memcached-exporter-"${EXPORTER_PORT} g_docker_cmd="${SUDO_ALIAS} ${ENGINE}" g_rm_cmd="${SUDO_ALIAS} rm -rf" @@ -23,6 +24,13 @@ precheck() { die "container [${g_container_name}] not exists!!!\n" exit 1 fi + if [ "${EXPORTER_PORT}" ];then + container_id=`${g_docker_cmd} ps --all --format "{{.ID}}" --filter name=${g_exporter_container_name}` + if [ -z ${container_id} ]; then + die "container [${g_exporter_container_name}] not exists!!!\n" + exit 1 + fi + fi } stop_container() { @@ -32,6 +40,14 @@ stop_container() { exit 1 fi success "rm container[${g_container_name}]\n" + if [ "${EXPORTER_PORT}" ];then + msg=`${g_docker_cmd} rm ${g_exporter_container_name}` + if [ $? -ne 0 ];then + die "${msg}\n" + exit 1 + fi + success "rm container[${g_exporter_container_name}]\n" + fi } rm_cachefile() { diff --git a/playbook/memcached/scripts/deploy.sh b/playbook/memcached/scripts/deploy.sh index f2c48b165..7df5a2c25 100644 --- a/playbook/memcached/scripts/deploy.sh +++ b/playbook/memcached/scripts/deploy.sh @@ -1,7 +1,9 @@ #!/usr/bin/env bash g_container_name="memcached-"${PORT} +g_exporter_container_name="memcached-exporter-"${EXPORTER_PORT} g_start_args="" +g_exporter_start_args="" g_docker_cmd="${SUDO_ALIAS} ${ENGINE}" g_lsof_cmd="${SUDO_ALIAS} lsof" g_rm_cmd="${SUDO_ALIAS} rm -rf" @@ -28,7 +30,21 @@ precheck() { container_id=`${g_docker_cmd} ps --format "{{.ID}}" --filter name=${g_container_name} --all` if [ "${container_id}" ]; then success "container [${g_container_name}] already exists, skip\n" - exit 0 + exit 1 + fi + + if [ "${EXPORTER_PORT}" ];then + container_id=`${g_docker_cmd} ps --format "{{.ID}}" --filter name=${g_exporter_container_name} --all` + if [ "${container_id}" ]; then + success "container [${g_exporter_container_name}] already exists, skip\n" + exit 1 + fi + + ${g_lsof_cmd} -i:${EXPORTER_PORT} >& /dev/null + if [ $? -eq 0 ];then + die "port[${EXPORTER_PORT}] is in use!\n" + exit 1 + fi fi # check port @@ -79,14 +95,28 @@ init() { if [ "${VERBOSE}" ];then g_start_args="${g_start_args} -${VERBOSE}" fi + + + if [ "${EXPORTER_PORT}" ];then + g_exporter_start_args="${g_exporter_start_args} --memcached.address=${LISTEN}:${PORT}" + g_exporter_start_args="${g_exporter_start_args} --web.listen-address=${LISTEN}:${EXPORTER_PORT}" + fi } create_container() { - success "create container [${g_container_name}]\n" ${g_docker_cmd} create --name ${g_container_name} ${g_user} --network host ${g_volume_bind} ${IMAGE} memcached ${g_start_args} >& /dev/null - - success "start container [${g_container_name}]\n" + success "create container [${g_container_name}]\n" + if [ "${EXPORTER_PORT}" ];then + ${g_docker_cmd} create --name ${g_exporter_container_name} --network host ${EXPORTER_IMAGE} ${g_exporter_start_args} >& /dev/null + success "create container [${g_exporter_container_name}]\n" + fi ${g_docker_cmd} start ${g_container_name} >& /dev/null + success "start container [${g_container_name}]\n" + if [ "${EXPORTER_PORT}" ];then + ${g_docker_cmd} start ${g_exporter_container_name} >& /dev/null + success "start container [${g_exporter_container_name}]\n" + fi + success "wait 3 seconds, check container status...\n" sleep 3 @@ -95,14 +125,25 @@ create_container() { if [ ${g_status} != "running" ]; then exit 1 fi + if [ "${EXPORTER_PORT}" ];then + if [ ${g_exporter_status} != "running" ]; then + exit 1 + fi + fi } get_status_container() { g_status=`${g_docker_cmd} inspect --format='{{.State.Status}}' ${g_container_name}` + if [ "${EXPORTER_PORT}" ];then + g_exporter_status=`${g_docker_cmd} inspect --format='{{.State.Status}}' ${g_exporter_container_name}` + fi } show_info_container() { ${g_docker_cmd} ps --all --filter "name=${g_container_name}" --format="table {{.ID}}\t{{.Names}}\t{{.Status}}" + if [ "${EXPORTER_PORT}" ];then + ${g_docker_cmd} ps --all --filter "name=${g_exporter_container_name}" --format="table {{.ID}}\t{{.Names}}\t{{.Status}}" + fi } precheck diff --git a/playbook/memcached/scripts/start.sh b/playbook/memcached/scripts/start.sh index 9c7c380fa..bd9e33455 100644 --- a/playbook/memcached/scripts/start.sh +++ b/playbook/memcached/scripts/start.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash g_container_name="memcached-"${PORT} +g_exporter_container_name="memcached-exporter-"${EXPORTER_PORT} g_docker_cmd="${SUDO_ALIAS} ${ENGINE}" g_rm_cmd="${SUDO_ALIAS} rm -rf" g_mkdir_cmd="${SUDO_ALIAS} mkdir -p" @@ -34,6 +35,10 @@ precheck() { start_container() { ${g_docker_cmd} start ${g_container_name} >& /dev/null success "start container[${g_container_name}]\n" + if [ "${EXPORTER_PORT}" ];then + ${g_docker_cmd} start ${g_exporter_container_name} >& /dev/null + success "start container[${g_exporter_container_name}]\n" + fi } get_status_container() { diff --git a/playbook/memcached/scripts/status.sh b/playbook/memcached/scripts/status.sh index ed875ec85..32507bb5a 100644 --- a/playbook/memcached/scripts/status.sh +++ b/playbook/memcached/scripts/status.sh @@ -1,11 +1,11 @@ #!/usr/bin/env bash g_container_name="memcached-"${PORT} +g_exporter_container_name="memcached-exporter-"${EXPORTER_PORT} g_start_args="" g_docker_cmd="${SUDO_ALIAS} ${ENGINE}" g_volume_bind="" g_container_id="" -g_status="running" function msg() { printf '%b' "$1" >&2 @@ -26,20 +26,39 @@ precheck() { success "container [${g_container_name}] not exists!!!" exit 1 fi + if [ "${EXPORTER_PORT}" ];then + g_container_id=`${g_docker_cmd} ps --all --format "{{.ID}}" --filter name=${g_exporter_container_name}` + if [ -z ${g_container_id} ]; then + success "container [${g_exporter_container_name}] not exists!!!" + exit 1 + fi + fi } show_info_container() { ${g_docker_cmd} ps --all --filter "name=${g_container_name}" --format="table {{.ID}}\t{{.Names}}\t{{.Status}}" + if [ "${EXPORTER_PORT}" ];then + ${g_docker_cmd} ps --all --filter "name=${g_exporter_container_name}" --format="table {{.ID}}\t{{.Names}}\t{{.Status}}" + fi } show_ip_port() { printf "memcached addr:\t%s:%d\n" ${LISTEN} ${PORT} + if [ "${EXPORTER_PORT}" ];then + printf "memcached-exporter addr:\t%s:%d\n" ${LISTEN} ${EXPORTER_PORT} + fi } get_status_container() { - g_status=`${g_docker_cmd} inspect --format='{{.State.Status}}' ${g_container_name}` - if [ ${g_status} != "running" ]; then + status=`${g_docker_cmd} inspect --format='{{.State.Status}}' ${g_container_name}` + if [ ${status} != "running" ]; then + exit 1 + fi + if [ "${EXPORTER_PORT}" ];then + status=`${g_docker_cmd} inspect --format='{{.State.Status}}' ${g_exporter_container_name}` + if [ ${status} != "running" ]; then exit 1 + fi fi } diff --git a/playbook/memcached/scripts/stop.sh b/playbook/memcached/scripts/stop.sh index 2dc84e53f..2d51a2b5b 100644 --- a/playbook/memcached/scripts/stop.sh +++ b/playbook/memcached/scripts/stop.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash g_container_name="memcached-"${PORT} +g_exporter_container_name="memcached-exporter-"${EXPORTER_PORT} g_docker_cmd="${SUDO_ALIAS} ${ENGINE}" function msg() { @@ -22,11 +23,22 @@ precheck() { die "container [${g_container_name}] not exists!!!\n" exit 1 fi + if [ "${EXPORTER_PORT}" ];then + container_id=`${g_docker_cmd} ps --all --format "{{.ID}}" --filter name=${g_exporter_container_name}` + if [ -z ${container_id} ]; then + die "container [${g_exporter_container_name}] not exists!!!\n" + exit 1 + fi + fi } stop_container() { ${g_docker_cmd} stop ${g_container_name} >& /dev/null success "stop container[${g_container_name}]\n" + if [ "${EXPORTER_PORT}" ];then + ${g_docker_cmd} stop ${g_exporter_container_name} >& /dev/null + success "stop container[${g_exporter_container_name}]\n" + fi } precheck