File: functions.sh

package info (click to toggle)
trillian 1.7.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,600 kB
  • sloc: sh: 1,181; javascript: 474; sql: 330; makefile: 39
file content (303 lines) | stat: -rwxr-xr-x 9,336 bytes parent folder | download
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# Functions for setting up Trillian integration tests

if [[ -z "${TMPDIR}" ]]; then
  TMPDIR=/tmp
fi
readonly TMPDIR
declare -a RPC_SERVER_PIDS
declare -a LOG_SIGNER_PIDS
declare -a TO_KILL
declare -a TO_DELETE
HTTP_SERVER_1=''
RPC_SERVER_1=''
RPC_SERVERS=''
ETCD_OPTS=''
ETCD_PID=''
ETCD_DB_DIR=''
readonly TRILLIAN_PATH=$(go list -f '{{.Dir}}' github.com/google/trillian)

# run_test runs the given test with additional output messages.
run_test() {
  local name=$1
  shift
  echo "=== RUN   ${name}"
  "$@"
  rc=$?
  if [ $rc -ne 0 ]; then
    echo "--- FAIL: ${name}"
  else
    echo "--- PASS: ${name}"
  fi
  return $rc
}

# wait_for_server_startup pauses until there is a response on the given port.
wait_for_server_startup() {
  # The server will 404 the request as there's no handler for it. This error doesn't matter
  # as the test will fail if the server is really not up.
  local port=$1
  set +e
  wget -q --spider --retry-connrefused --waitretry=2 -t 20 localhost:${port}
  # Wait a bit more to give it a chance to become actually available, e.g. if CI
  # environment is slow.
  sleep 5
  wget -q --spider -t 1 localhost:${port}
  local rc=$?
  set -e
  # wget emits rc=8 for server issuing an error response (e.g. 404)
  if [ ${rc} != 0 -a ${rc} != 8 ]; then
    echo "Failed to get response on localhost:${port}"
    exit 1
  fi
}

# pick_unused_port selects an apparently unused port.
pick_unused_port() {
  local avoid=${1:-0}
  local base=6962
  local port
  for (( port = "${base}" ; port <= 61000 ; port++ )); do
    if [[ $port == $avoid ]]; then
      continue
    fi
    if ! lsof -i :$port > /dev/null; then
      echo $port
      break
    fi
  done
}

# kill_pid tries to kill the given pid(s), first softly then more aggressively.
kill_pid() {
  local pids=$@
  set +e
  local count=0
  while kill -INT ${pids} > /dev/null 2>&1; do
    sleep 1
    ((count++))
    local im_still_alive=""
    for pid in ${pids}; do
      if ps -p ${pid} > /dev/null ; then
        # https://www.youtube.com/watch?time_continue=1&v=VuLktUzq23c
        im_still_alive+=" ${pid}"
      fi
    done
    pids="${im_still_alive}"
    if [ -z "${pids}" ]; then
      # all gone!
      break
    fi
    if [ $count -gt 5 ]; then
      echo "Now do kill -KILL ${pids}"
      kill -KILL ${pids}
      break
    fi
    echo "Retry kill -INT ${pids}"
  done
  set -e
}

# log_prep_test prepares a set of running processes for a Trillian log test.
# Parameters:
#   - number of log servers to run
#   - number of log signers to run
# Env:
#   - If TEST_MYSQL_URI is set, uses that for the server --mysql_uri flag.
# Populates:
#  - HTTP_SERVER_1   : first HTTP server
#  - RPC_SERVER_1    : first RPC server
#  - RPC_SERVERS     : RPC target, either comma-separated list of RPC addresses or etcd service
#  - RPC_SERVER_PIDS : bash array of RPC server pids
#  - LOG_SIGNER_PIDS : bash array of signer pids
#  - ETCD_OPTS       : common option to configure etcd location
# If the ETCD_DIR var points to a valid etcd, also populates:
#  - ETCD_PID        : etcd pid
#  - ETCD_DB_DIR     : location of etcd database
# If WITH_PKCS11 is set, also populates:
#  - SOFTHSM_CONF    : location of the SoftHSM configuration file
#
log_prep_test() {
  set -x
  # Default to one of each.
  local rpc_server_count=${1:-1}
  local log_signer_count=${2:-1}

  # Wipe the test database
  if [[ "${TEST_MYSQL_URI}" != "" ]]; then
    yes | bash "${TRILLIAN_PATH}/scripts/resetdb.sh"
  elif [[ "${TEST_COCKROACHDB_URI}" != "" ]]; then
    yes | bash "${TRILLIAN_PATH}/scripts/resetcrdb.sh"
  elif [[ "${TEST_POSTGRESQL_URI}" != "" ]]; then
    yes | bash "${TRILLIAN_PATH}/scripts/resetpgdb.sh"
  fi

  local logserver_opts=''
  local logsigner_opts=''
  local has_etcd=0

  if [[ "${TEST_MYSQL_URI}" != "" ]]; then
    logserver_opts+=" --mysql_uri=${TEST_MYSQL_URI}"
    logsigner_opts+=" --mysql_uri=${TEST_MYSQL_URI}"
  elif [[ "${TEST_COCKROACHDB_URI}" != "" ]]; then
    logserver_opts+="--quota_system=crdb --storage_system=crdb --crdb_uri=${TEST_COCKROACHDB_URI}"
    logsigner_opts+="--quota_system=crdb --storage_system=crdb --crdb_uri=${TEST_COCKROACHDB_URI}"
  elif [[ "${TEST_POSTGRESQL_URI}" != "" ]]; then
    logserver_opts+="--quota_system=postgresql --storage_system=postgresql --postgresql_uri=${TEST_POSTGRESQL_URI}"
    logsigner_opts+="--quota_system=postgresql --storage_system=postgresql --postgresql_uri=${TEST_POSTGRESQL_URI}"
  fi

  # Start a local etcd instance (if configured).
  if [[ -x "${ETCD_DIR}/etcd" ]]; then
    has_etcd=1
    local etcd_port=2379
    local etcd_server="localhost:${etcd_port}"
    echo "Starting local etcd server on ${etcd_server}"
    ${ETCD_DIR}/etcd &
    ETCD_PID=$!
    ETCD_OPTS="--etcd_servers=${etcd_server}"
    ETCD_DB_DIR=default.etcd
    wait_for_server_startup ${etcd_port}
    logserver_opts="${logserver_opts} --etcd_http_service=trillian-logserver-http --etcd_service=trillian-logserver --quota_system=etcd"
    logsigner_opts="${logsigner_opts} --etcd_http_service=trillian-logsigner-http --quota_system=etcd"
  else
    if  [[ ${log_signer_count} > 1 ]]; then
      echo "*** Warning: running multiple signers with no etcd instance ***"
    fi
    logsigner_opts="${logsigner_opts} --force_master"
  fi

  if [[ "${WITH_PKCS11}" == "true" ]]; then
    export SOFTHSM_CONF=${TMPDIR}/softhsm.conf
    local pkcs11_opts="--pkcs11_module_path ${PKCS11_MODULE:-/usr/lib/softhsm/libsofthsm.so}"
  fi

  # Start a set of Log RPC servers.
  for ((i=0; i < rpc_server_count; i++)); do
    port=$(pick_unused_port)
    RPC_SERVERS="${RPC_SERVERS},localhost:${port}"
    http=$(pick_unused_port ${port})

    echo "Starting Log RPC server on localhost:${port}, HTTP on localhost:${http}"
    go run ${GOFLAGS} github.com/google/trillian/cmd/trillian_log_server \
      ${ETCD_OPTS} ${pkcs11_opts} ${logserver_opts} \
      --rpc_endpoint="localhost:${port}" \
      --http_endpoint="localhost:${http}" \
      ${LOGGING_OPTS} \
      &
    pid=$!
    RPC_SERVER_PIDS+=(${pid})
    wait_for_server_startup ${port}

    # Use the first Log server as the Admin server (any would do)
    if [[ $i -eq 0 ]]; then
      HTTP_SERVER_1="localhost:${http}"
      RPC_SERVER_1="localhost:${port}"
    fi
  done
  RPC_SERVERS="${RPC_SERVERS:1}"

  # Setup etcd quotas, if applicable
  if [[ ${has_etcd} -eq 1 ]]; then
    setup_etcd_quotas "${RPC_SERVER_1}"
  fi

  # Start a set of signers.
  for ((i=0; i < log_signer_count; i++)); do
    port=$(pick_unused_port)
    http=$(pick_unused_port ${port})
    echo "Starting Log signer, HTTP on localhost:${http}"
    go run ${GOFLAGS} github.com/google/trillian/cmd/trillian_log_signer \
      ${ETCD_OPTS} ${pkcs11_opts} ${logsigner_opts} \
      --sequencer_interval="1s" \
      --batch_size=500 \
      --rpc_endpoint="localhost:${port}" \
      --http_endpoint="localhost:${http}" \
      --num_sequencers 2 \
      ${LOGGING_OPTS} \
      &
    pid=$!
    LOG_SIGNER_PIDS+=(${pid})
    wait_for_server_startup ${http}
  done

  if [[ ! -z "${ETCD_OPTS}" ]]; then
    RPC_SERVERS="trillian-logserver"
    echo "Registered log servers @${RPC_SERVERS}/"
    ETCDCTL_API=3 etcdctl get ${RPC_SERVERS}/ --prefix
    echo "Registered HTTP endpoints"
    ETCDCTL_API=3 etcdctl get trillian-logserver-http/ --prefix
    ETCDCTL_API=3 etcdctl get trillian-logsigner-http/ --prefix
  fi
}

# log_stop_tests closes down a set of running processes for a log test.
# Assumes the following variables are set:
#  - LOG_SIGNER_PIDS : bash array of signer pids
#  - RPC_SERVER_PIDS : bash array of RPC server pids
#  - ETCD_PID        : etcd pid
log_stop_test() {
  local pids
  echo "Stopping Log signers (pids ${LOG_SIGNER_PIDS[@]})"
  pids+=" ${LOG_SIGNER_PIDS[@]}"
  echo "Stopping Log RPC servers (pids ${RPC_SERVER_PIDS[@]})"
  pids+=" ${RPC_SERVER_PIDS[@]}"
  if [[ "${ETCD_PID}" != "" ]]; then
    echo "Stopping local etcd server (pid ${ETCD_PID})"
    pids+=" ${ETCD_PID}"
  fi
  kill_pid ${pids}
}

# setup_etcd_quotas creates the etcd quota configurations used by tests.
#
# Parameters:
#   - server : GRPC endpoint for the quota API (eg, logserver grpc port)
#
# Outputs:
#   DeleteConfig and CreateConfig responses.
#
# Returns:
#   0 if success, non-zero otherwise.
setup_etcd_quotas() {
  local server="$1"
  local name='quotas/global/write/config'

  # Remove the config before creating. It's OK if it doesn't exist.
  local delete_output=$(grpcurl -plaintext -d "name: ${name}" ${server} quotapb.Quota.DeleteConfig )
  printf 'quotapb.Quota.DeleteConfig %s: %s\n' "${name}" "${delete_output}"

  local create_output=$(grpcurl -plaintext -d @ ${server} quotapb.Quota.CreateConfig <<EOF
{
  "name": "${name}",
  "config": {
    "state": "ENABLED",
    "max_tokens": 1000,
    "sequencing_based": {
    }
  }
}
EOF
  )
  printf 'quotapb.Quota.CreateConfig %s: %s\n' "${name}" "${create_output}"

  # Success responses have the config name in them
  echo "${create_output}" | grep '"name":' > /dev/null
}

# on_exit will clean up anything in ${TO_KILL} and ${TO_DELETE}.
on_exit() {
  local pids=
  for pid in "${TO_KILL[@]}"; do
    echo "Killing ${pid} on exit"
    pids+=" ${pid}"
  done
  kill_pid "${pids}"

  local file=""
  for file in "${TO_DELETE[@]}"; do
    echo "Deleting ${file} on exit"
    rm -rf ${file}
  done
}

trap on_exit EXIT