-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathmanage.sh
executable file
·220 lines (185 loc) · 6.48 KB
/
manage.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#!/bin/bash
help() {
echo "Setup and run a Couchbase cluster node. Uses Consul to find other"
echo "nodes in the cluster or bootstraps the cluster if it does not yet"
echo "exist."
echo
echo "Usage: ./manage.sh health => runs health check and bootstrap."
echo " ./manage.sh <command> => run another function for debugging."
}
trap cleanup EXIT
# This container's private IP
export IP_PRIVATE=$(ip addr show eth0 | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}')
# Discovery vars
COUCHBASE_SERVICE_NAME=${COUCHBASE_SERVICE_NAME:-couchbase-api}
export CONSUL=${CONSUL:-consul}
# Couchbase username and password
export COUCHBASE_USER=${COUCHBASE_USER:-Administrator}
export COUCHBASE_PASS=${COUCHBASE_PASS:-password}
CB_CONN="-c 127.0.0.1:8091 -u ${COUCHBASE_USER} -p ${COUCHBASE_PASS}"
# -------------------------------------------
# Top-level health check handler
health() {
# if we're already initialized and joined to a cluster,
# we can just run the health check and exit
checkLock
initNode
isNodeInCluster
if [ $? -eq 0 ]; then
doHealthCheck
exit $?
fi
# if there is a healthy cluster we join it, otherwise we try
# to create a new cluster. If another node is in the process
# of creating a new cluster, we'll wait for it instead.
echo 'Looking for an existing cluster...'
while true; do
local node=$(getHealthyClusterIp)
if [[ ${node} != "null" ]]; then
joinCluster $node
else
obtainBootstrapLock
if [ $? -eq 0 ]; then
initCluster
else
sleep 3
fi
fi
done
}
# -------------------------------------------
# Status checking
# The couchbase-cli provides no documented mechanism to verify that we've
# initialized the node. But if we try to node-init with the default password
# and it fails, then we know we've previously initialized this node.
# Either way we can merrily continue.
initNode() {
# couchbase takes a while to become responsive on start, so we need to
# make sure it's up first.
while true; do
# an uninitialized node will have default creds
couchbase-cli server-info -c 127.0.0.1:8091 -u access -p password &>/dev/null
if [ $? -eq 0 ]; then
break
fi
# check the initialized creds as well
couchbase-cli server-info ${CB_CONN} &>/dev/null
if [ $? -eq 0 ]; then
break
fi
echo -n '.'
sleep 1
done
couchbase-cli node-init -c 127.0.0.1:8091 -u access -p password \
--node-init-data-path=/opt/couchbase/var/lib/couchbase/data \
--node-init-index-path=/opt/couchbase/var/lib/couchbase/data \
--node-init-hostname=${IP_PRIVATE} &>/dev/null \
&& echo '# Node initialized'
}
isNodeInCluster() {
couchbase-cli server-list ${CB_CONN} | grep ${IP_PRIVATE} &>/dev/null
return $?
}
doHealthCheck() {
local status=$(couchbase-cli server-info ${CB_CONN} | jq -r .status)
if [[ $status != "healthy" ]]; then
echo "Node not healthy, status was: $status"
return 1
fi
return 0
}
# -------------------------------------------
# Joining a cluster
# We only need one IP from the healthy cluster in order to join it.
getHealthyClusterIp() {
echo $(curl -Lsf http://${CONSUL}:8500/v1/health/service/${COUCHBASE_SERVICE_NAME}?passing | jq -r .[0].Service.Address)
}
# If we fail to join the cluster, then bail out and hit it on the
# next health check
joinCluster(){
echo '# Joining cluster...'
local node=$1
curl -Lsif -u ${COUCHBASE_USER}:${COUCHBASE_PASS} \
-d "hostname=${IP_PRIVATE}&user=admin&password=password" \
"http://${node}:8091/controller/addNode" || exit 1
echo 'Joined cluster!'
rebalance
exit 0
}
# We need to rebalance for each node because we can't guarantee that we won't
# try to rebalance while another node is coming up. Doing this in a loop because
# we can't queue-up rebalances -- the rebalance command cannot be called while a
# rebalance is in progress
rebalance() {
echo '# Rebalancing cluster...'
while true; do
echo -n '.'
couchbase-cli rebalance ${CB_CONN} && return
sleep .7
done
}
# -------------------------------------------
# Bootstrapping a cluster
# Try to obtain a lock in Consul. If we can't get the lock then another node
# is trying to bootstrap the cluster. The cluster-init node will have 120s
# to show up as healthy in Consul.
obtainBootstrapLock() {
echo 'No cluster nodes found, trying to obtain lock on bootstrap...'
local session=$(curl -Lsf -XPUT -d '{"Name": "couchbase-bootstrap", "TTL": "120s"}' http://${CONSUL}:8500/v1/session/create | jq -r .ID) || return $?
local lock=$(curl -Lsf -XPUT http://${CONSUL}:8500/v1/kv/couchbase-bootstrap?acquire=$session)
if [[ $lock == "true" ]]; then
return 0
else
return 1
fi
}
# bootstrap the Couchbase cluster and set resource limits
initCluster() {
echo
echo '# Bootstrapping cluster...'
# Couchbase resource limits
local avail_memory=$(free -m | grep -o "Mem:\s*[0-9]*" | grep -o "[0-9]*")
local cb_memory=$((($avail_memory/10)*7))
couchbase-cli cluster-init -c 127.0.0.1:8091 -u access -p password \
--cluster-init-username=${COUCHBASE_USER} \
--cluster-init-password=${COUCHBASE_PASS} \
--cluster-init-port=8091 \
--cluster-init-ramsize=${cb_memory} \
--services=data,index,query
echo '# Cluster bootstrapped'
echo
exit 0
}
# filters JSON coming back from REST API for this node with argument. examples:
# stats mcdMemoryAllocated
# stats systemStats.mem_free
stats() {
curl -s --fail -u ${COUCHBASE_USER}:${COUCHBASE_PASS} \
http://127.0.0.1:8091/pools/default | \
jq -r "$(printf '.nodes[] | select(.hostname | contains("%s")) | .%s' "${IP_PRIVATE}" "$1")"
}
# -------------------------------------------
# helpers
# make sure we're running only one init process at a time
# even with overlapping health check handlers
checkLock() {
if ! mkdir /var/lock/couchbase-init; then
echo 'couchbase-init lock in place, skipping'
fi
}
cleanup() {
rm -rf /var/lock/couchbase-init
}
# -------------------------------------------
until
cmd=$1
if [ -z "$cmd" ]; then
help
fi
shift 1
$cmd "$@"
[ "$?" -ne 127 ]
do
help
exit
done