#!/bin/sh # Copyright 2016 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # NOTES # This script performs etcd upgrade based on the following environmental # variables: # TARGET_STORAGE - API of etcd to be used (supported: 'etcd2', 'etcd3') # TARGET_VERSION - etcd release to be used (supported: '2.2.1', '2.3.7', '3.0.17') # DATA_DIRECTORY - directory with etcd data # # The current etcd version and storage format is detected based on the # contents of "${DATA_DIRECTORY}/version.txt" file (if the file doesn't # exist, we default it to "2.2.1/etcd2". # # The update workflow support the following upgrade steps: # - 2.2.1/etcd2 -> 2.3.7/etcd2 # - 2.3.7/etcd2 -> 3.0.17/etcd2 # - 3.0.17/etcd2 -> 3.0.17/etcd3 # # NOTE: The releases supported in this script has to match release binaries # present in the etcd image (to make this script work correctly). # # Based on the current etcd version and storage format we detect what # upgrade step from this should be done to get reach target configuration set -o errexit set -o nounset if test -z $(TARGET_STORAGE:-) { echo "TARGET_STORAGE variable unset - unexpected failure" exit 1 } if test -z $(TARGET_VERSION:-) { echo "TARGET_VERSION variable unset - unexpected failure" exit 1 } if test -z $(DATA_DIRECTORY:-) { echo "DATA_DIRECTORY variable unset - unexpected failure" exit 1 } echo "$[date +'%Y-%m-%d %H:%M:%S] Detecting if migration is needed" if test $(TARGET_STORAGE) != "etcd2" -a $(TARGET_STORAGE) != "etcd3" { echo "Not supported version of storage: $(TARGET_STORAGE)" exit 1 } # Correctly support upgrade and rollback to non-default version. if test $(DO_NOT_MOVE_BINARIES:-) != "true" { cp "/usr/local/bin/etcd-$(TARGET_VERSION)" "/usr/local/bin/etcd" cp "/usr/local/bin/etcdctl-$(TARGET_VERSION)" "/usr/local/bin/etcdctl" } # NOTE: SUPPORTED_VERSION has to match release binaries present in the # etcd image (to make this script work correctly). # We cannot use array since sh doesn't support it. global SUPPORTED_VERSIONS_STRING := '"2.2.1 2.3.7 3.0.17'" global SUPPORTED_VERSIONS := $[echo $(SUPPORTED_VERSIONS_STRING) | tr " " "] global VERSION_FILE := '"version.txt'" global CURRENT_STORAGE := '"etcd2'" global CURRENT_VERSION := '"2.2.1'" if test -e "$(DATA_DIRECTORY)/$(VERSION_FILE)" { global VERSION_CONTENTS := $[cat $(DATA_DIRECTORY)/$(VERSION_FILE)] # Example usage: if contents of VERSION_FILE is 2.3.7/etcd2, then # - CURRENT_VERSION would be '2.3.7' # - CURRENT_STORAGE would be 'etcd2' global CURRENT_VERSION := $[echo $VERSION_CONTENTS | cut -d '/' -f 1] global CURRENT_STORAGE := $[echo $VERSION_CONTENTS | cut -d '/' -f 2] } global ETCD_DATA_PREFIX := $(ETCD_DATA_PREFIX:-/registry) # If there is no data in DATA_DIRECTORY, this means that we are # starting etcd from scratch. In that case, we don't need to do # any migration. if test ! -d $(DATA_DIRECTORY) { mkdir -p $(DATA_DIRECTORY) } if test -z $[ls -A $(DATA_DIRECTORY)] { echo "$(DATA_DIRECTORY) is empty - skipping migration" echo "$(TARGET_VERSION)/$(TARGET_STORAGE)" > "$(DATA_DIRECTORY)/$(VERSION_FILE)" exit 0 } # Starts 'etcd' version ${START_VERSION} and writes to it: # 'etcd_version' -> "${START_VERSION}" # Successful write confirms that etcd is up and running. # Sets ETCD_PID at the end. # Returns 0 if etcd was successfully started, non-0 otherwise. proc start_etcd { # Use random ports, so that apiserver cannot connect to etcd. global ETCD_PORT := '18629' global ETCD_PEER_PORT := '2380' # Avoid collisions between etcd and event-etcd. matchstr $(DATA_DIRECTORY) { *event* { global ETCD_PORT := '18631' global ETCD_PEER_PORT := '2381' } } var ETCD_CMD = $(ETCD:-/usr/local/bin/etcd-${START_VERSION}) var ETCDCTL_CMD = $(ETCDCTL:-/usr/local/bin/etcdctl-${START_VERSION}) var API_VERSION = $[echo $(START_STORAGE) | cut -c5-5] if test $(API_VERSION) = "2" { ETCDCTL_CMD := ""$(ETCDCTL_CMD) --debug --endpoint=http://127.0.0.1:$(ETCD_PORT) set"" } else { ETCDCTL_CMD := ""$(ETCDCTL_CMD) --endpoints=http://127.0.0.1:$(ETCD_PORT) put"" } $(ETCD_CMD) \ --name="etcd-$[hostname]" \ --debug \ --data-dir=$(DATA_DIRECTORY) \ --listen-client-urls http://127.0.0.1:$(ETCD_PORT) \ --advertise-client-urls http://127.0.0.1:$(ETCD_PORT) \ --listen-peer-urls http://127.0.0.1:$(ETCD_PEER_PORT) \ --initial-advertise-peer-urls http://127.0.0.1:$(ETCD_PEER_PORT) & global ETCD_PID := $BgPid # Wait until we can write to etcd. for i in [$[seq 240]] { sleep 0.5 env ETCDCTL_API=$(API_VERSION) $(ETCDCTL_CMD) 'etcd_version' $(START_VERSION) if test "$Status" -eq "0" { echo "Etcd on port $(ETCD_PORT) is up." return 0 } } echo "Timeout while waiting for etcd on port $(ETCD_PORT)" return 1 } # Stops etcd with ${ETCD_PID} pid. proc stop_etcd { kill $(ETCD_PID-) >/dev/null !2 > !1 || : wait $(ETCD_PID-) >/dev/null !2 > !1 || : } global ATTACHLEASE := $(ATTACHLEASE:-/usr/local/bin/attachlease) global ROLLBACK := $(ROLLBACK:-/usr/local/bin/rollback) # If we are upgrading from 2.2.1 and this is the first try for upgrade, # do the backup to allow restoring from it in case of failed upgrade. global BACKUP_DIR := ""$(DATA_DIRECTORY)/migration-backup"" if test $(CURRENT_VERSION) = "2.2.1" -a $(CURRENT_VERSION) != $(TARGET_VERSION) -a ! -d $(BACKUP_DIR) { echo "Backup etcd before starting migration" mkdir $(BACKUP_DIR) global ETCDCTL_CMD := '"/usr/local/bin/etcdctl-2.2.1'" env ETCDCTL_API=2 $(ETCDCTL_CMD) --debug backup --data-dir=$(DATA_DIRECTORY) \ --backup-dir=$(BACKUP_DIR) echo "Backup done in $(BACKUP_DIR)" } # Do the roll-forward migration if needed. # The migration goes as following: # 1. for all versions starting one after the current version of etcd # we do "start, wait until healthy and stop etcd". This is the # procedure that etcd documentation suggests for upgrading binaries. # 2. For the first 3.0.x version that we encounter, if we are still in # v2 API, we do upgrade to v3 API using the "etcdct migrate" and # attachlease commands. global SKIP_STEP := 'true' for step in [$(SUPPORTED_VERSIONS)] { if test $(step) = $(CURRENT_VERSION) { global SKIP_STEP := 'false' } elif test $(SKIP_STEP) != "true" { # Do the migration step, by just starting etcd in this version. global START_VERSION := $(step) global START_STORAGE := $(CURRENT_STORAGE) if ! start_etcd { # Migration failed. echo "Starting etcd $(step) failed" exit 1 } # Kill etcd and wait until this is down. stop_etcd global CURRENT_VERSION := $(step) echo "$(CURRENT_VERSION)/$(CURRENT_STORAGE)" > "$(DATA_DIRECTORY)/$(VERSION_FILE)" } if test $[echo $(CURRENT_VERSION) | cut -c1-2] = "3." -a $(CURRENT_VERSION) = $(step) -a $(CURRENT_STORAGE) = "etcd2" -a $(TARGET_STORAGE) = "etcd3" { # If it is the first 3.x release in the list and we are migrating # also from 'etcd2' to 'etcd3', do the migration now. echo "Performing etcd2 -> etcd3 migration" global START_VERSION := $(step) global START_STORAGE := '"etcd3'" global ETCDCTL_CMD := $(ETCDCTL:-/usr/local/bin/etcdctl-${START_VERSION}) env ETCDCTL_API=3 $(ETCDCTL_CMD) migrate --data-dir=$(DATA_DIRECTORY) echo "Attaching leases to TTL entries" # Now attach lease to all keys. # To do it, we temporarily start etcd on a random port (so that # apiserver actually cannot access it). if ! start_etcd { echo "Starting etcd $(step) in v3 mode failed" exit 1 } # Create a lease and attach all keys to it. $(ATTACHLEASE) \ --etcd-address http://127.0.0.1:$(ETCD_PORT) \ --ttl-keys-prefix $(TTL_KEYS_DIRECTORY:-${ETCD_DATA_PREFIX}/events) \ --lease-duration 1h # Kill etcd and wait until this is down. stop_etcd global CURRENT_STORAGE := '"etcd3'" echo "$(CURRENT_VERSION)/$(CURRENT_STORAGE)" > "$(DATA_DIRECTORY)/$(VERSION_FILE)" } if test $[echo $(CURRENT_VERSION) | cut -c1-4] = "3.1." -a $(CURRENT_VERSION) = $(step) -a $(CURRENT_STORAGE) = "etcd3" { # If we are upgrading to 3.1.* release, if the cluster was migrated # from v2 version, the v2 data may still be around. So now is the # time to actually remove them. echo "Remove stale v2 data" global START_VERSION := $(step) global START_STORAGE := '"etcd3'" global ETCDCTL_CMD := $(ETCDCTL:-/usr/local/bin/etcdctl-${START_VERSION}) if ! start_etcd { echo "Starting etcd $(step) in v3 mode failed" exit 1 } $(ETCDCTL_CMD) rm --recursive $(ETCD_DATA_PREFIX) # Kill etcd and wait until this is down. stop_etcd echo "Successfully remove v2 data" # Also remove backup from v2->v3 migration. rm -rf $(BACKUP_DIR) } if test $(CURRENT_VERSION) = $(TARGET_VERSION) -a $(CURRENT_STORAGE) = $(TARGET_STORAGE) { break } } # Do the rollback of needed. # NOTE: Rollback is only supported from "3.0.x" version in 'etcd3' mode to # "2.2.1" version in 'etcd2' mode. if test $(CURRENT_STORAGE) = "etcd3" -a $(TARGET_STORAGE) = "etcd2" { if test $[echo $(CURRENT_VERSION) | cut -c1-4] != "3.0." -o $(TARGET_VERSION) != "2.2.1" { echo "etcd3 -> etcd2 downgrade is supported only between 3.0.x and 2.2.1" return 0 } echo "Backup and remove all existing v2 data" global ROLLBACK_BACKUP_DIR := ""$(DATA_DIRECTORY).bak"" rm -rf $(ROLLBACK_BACKUP_DIR) mkdir -p $(ROLLBACK_BACKUP_DIR) cp -r $(DATA_DIRECTORY) $(ROLLBACK_BACKUP_DIR) echo "Performing etcd3 -> etcd2 rollback" $(ROLLBACK) --data-dir $(DATA_DIRECTORY) if test "$Status" -ne "0" { echo "Rollback to etcd2 failed" exit 1 } global CURRENT_STORAGE := '"etcd2'" global CURRENT_VERSION := '"2.2.1'" echo "$(CURRENT_VERSION)/$(CURRENT_STORAGE)" > "$(DATA_DIRECTORY)/$(VERSION_FILE)" } echo "$[date +'%Y-%m-%d %H:%M:%S] Migration finished" (CommandList children: [ (C {(set)} {(-o)} {(errexit)}) (C {(set)} {(-o)} {(nounset)}) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(-z)} { (DQ (BracedVarSub token: suffix_op: (StringUnary op_id:VTest_ColonHyphen arg_word:{(SQ )}) spids: [128 131] ) ) } {(Lit_Other "]")} ) terminator: ) ] action: [ (C {(echo)} {(DQ ("TARGET_STORAGE variable unset - unexpected failure"))}) (C {(exit)} {(1)}) ] spids: [-1 137] ) ] spids: [-1 151] ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(-z)} { (DQ (BracedVarSub token: suffix_op: (StringUnary op_id:VTest_ColonHyphen arg_word:{(SQ )}) spids: [160 163] ) ) } {(Lit_Other "]")} ) terminator: ) ] action: [ (C {(echo)} {(DQ ("TARGET_VERSION variable unset - unexpected failure"))}) (C {(exit)} {(1)}) ] spids: [-1 169] ) ] spids: [-1 183] ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(-z)} { (DQ (BracedVarSub token: suffix_op: (StringUnary op_id:VTest_ColonHyphen arg_word:{(SQ )}) spids: [192 195] ) ) } {(Lit_Other "]")} ) terminator: ) ] action: [ (C {(echo)} {(DQ ("DATA_DIRECTORY variable unset - unexpected failure"))}) (C {(exit)} {(1)}) ] spids: [-1 201] ) ] spids: [-1 215] ) (C {(echo)} { (DQ (CommandSubPart command_list: (CommandList children: [(C {(date)} {(Lit_Other "+") (SQ <"%Y-%m-%d %H:%M:%S">)})] ) left_token: spids: [221 228] ) (" Detecting if migration is needed") ) } ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(DQ (${ VSub_Name TARGET_STORAGE))} {(KW_Bang "!") (Lit_Other "=")} {(DQ (etcd2))} {(-a)} {(DQ (${ VSub_Name TARGET_STORAGE))} {(KW_Bang "!") (Lit_Other "=")} {(DQ (etcd3))} {(Lit_Other "]")} ) terminator: ) ] action: [ (C {(echo)} {(DQ ("Not supported version of storage: ") (${ VSub_Name TARGET_STORAGE))}) (C {(exit)} {(1)}) ] spids: [-1 268] ) ] spids: [-1 285] ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} { (DQ (BracedVarSub token: suffix_op: (StringUnary op_id:VTest_ColonHyphen arg_word:{(SQ )}) spids: [296 299] ) ) } {(KW_Bang "!") (Lit_Other "=")} {(DQ (true))} {(Lit_Other "]")} ) terminator: ) ] action: [ (C {(cp)} {(DQ (/usr/local/bin/etcd-) (${ VSub_Name TARGET_VERSION))} {(DQ (/usr/local/bin/etcd))} ) (C {(cp)} {(DQ (/usr/local/bin/etcdctl-) (${ VSub_Name TARGET_VERSION))} {(DQ (/usr/local/bin/etcdctl))} ) ] spids: [-1 312] ) ] spids: [-1 342] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:SUPPORTED_VERSIONS_STRING) op: Equal rhs: {(DQ ("2.2.1 2.3.7 3.0.17"))} spids: [354] ) ] spids: [354] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:SUPPORTED_VERSIONS) op: Equal rhs: { (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (C {(echo)} {(DQ (${ VSub_Name SUPPORTED_VERSIONS_STRING))}) (C {(tr)} {(DQ (" "))} {(DQ (EscapedLiteralPart token:))}) ] negated: False ) ] ) left_token: spids: [360 380] ) } spids: [359] ) ] spids: [359] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:VERSION_FILE) op: Equal rhs: {(DQ (version.txt))} spids: [383] ) ] spids: [383] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:CURRENT_STORAGE) op: Equal rhs: {(DQ (etcd2))} spids: [388] ) ] spids: [388] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:CURRENT_VERSION) op: Equal rhs: {(DQ (2.2.1))} spids: [393] ) ] spids: [393] ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(-e)} {(DQ (${ VSub_Name DATA_DIRECTORY) (/) (${ VSub_Name VERSION_FILE))} {(Lit_Other "]")} ) terminator: ) ] action: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:VERSION_CONTENTS) op: Equal rhs: { (DQ (CommandSubPart command_list: (CommandList children: [ (C {(cat)} {(${ VSub_Name DATA_DIRECTORY) (/) (${ VSub_Name VERSION_FILE)} ) ] ) left_token: spids: [422 432] ) ) } spids: [420] ) ] spids: [420] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:CURRENT_VERSION) op: Equal rhs: { (DQ (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (C {(echo)} {($ VSub_Name "$VERSION_CONTENTS")}) (C {(cut)} {(-d)} {(SQ )} {(-f)} {(1)}) ] negated: False ) ] ) left_token: spids: [450 468] ) ) } spids: [448] ) ] spids: [448] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:CURRENT_STORAGE) op: Equal rhs: { (DQ (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (C {(echo)} {($ VSub_Name "$VERSION_CONTENTS")}) (C {(cut)} {(-d)} {(SQ )} {(-f)} {(2)}) ] negated: False ) ] ) left_token: spids: [474 492] ) ) } spids: [472] ) ] spids: [472] ) ] spids: [-1 417] ) ] spids: [-1 495] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:ETCD_DATA_PREFIX) op: Equal rhs: { (DQ (BracedVarSub token: suffix_op: (StringUnary op_id:VTest_ColonHyphen arg_word:{(Lit_Slash /) (registry)}) spids: [499 504] ) ) } spids: [497] ) ] spids: [497] ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(KW_Bang "!")} {(-d)} {(DQ (${ VSub_Name DATA_DIRECTORY))} {(Lit_Other "]")} ) terminator: ) ] action: [(C {(mkdir)} {(-p)} {(DQ (${ VSub_Name DATA_DIRECTORY))})] spids: [-1 534] ) ] spids: [-1 547] ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(-z)} { (DQ (CommandSubPart command_list: (CommandList children: [(C {(ls)} {(-A)} {(${ VSub_Name DATA_DIRECTORY)})] ) left_token: spids: [556 564] ) ) } {(Lit_Other "]")} ) terminator: ) ] action: [ (C {(echo)} {(DQ (${ VSub_Name DATA_DIRECTORY) (" is empty - skipping migration"))}) (SimpleCommand words: [{(echo)} {(DQ (${ VSub_Name TARGET_VERSION) (/) (${ VSub_Name TARGET_STORAGE))}] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(DQ (${ VSub_Name DATA_DIRECTORY) (/) (${ VSub_Name VERSION_FILE))} spids: [595] ) ] ) (C {(exit)} {(0)}) ] spids: [-1 570] ) ] spids: [-1 612] ) (FuncDef name: start_etcd body: (BraceGroup children: [ (Assignment keyword: Assign_None pairs: [(assign_pair lhs:(LhsName name:ETCD_PORT) op:Equal rhs:{(18629)} spids:[641])] spids: [641] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:ETCD_PEER_PORT) op: Equal rhs: {(2380)} spids: [645] ) ] spids: [645] ) (Case to_match: {(DQ (${ VSub_Name DATA_DIRECTORY))} arms: [ (case_arm pat_list: [{(Lit_Other "*") (event) (Lit_Other "*")}] action: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:ETCD_PORT) op: Equal rhs: {(18631)} spids: [670] ) ] spids: [670] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:ETCD_PEER_PORT) op: Equal rhs: {(2381)} spids: [674] ) ] spids: [674] ) ] spids: [664 667 678 -1] ) ] spids: [653 661 681] ) (Assignment keyword: Assign_Local pairs: [ (assign_pair lhs: (LhsName name:ETCD_CMD) op: Equal rhs: { (DQ (BracedVarSub token: suffix_op: (StringUnary op_id: VTest_ColonHyphen arg_word: {(Lit_Slash /) (usr) (Lit_Slash /) (local) (Lit_Slash /) (bin) (Lit_Slash /) (etcd-) (${ VSub_Name START_VERSION) } ) spids: [688 702] ) ) } spids: [686] ) ] spids: [684] ) (Assignment keyword: Assign_Local pairs: [ (assign_pair lhs: (LhsName name:ETCDCTL_CMD) op: Equal rhs: { (DQ (BracedVarSub token: suffix_op: (StringUnary op_id: VTest_ColonHyphen arg_word: {(Lit_Slash /) (usr) (Lit_Slash /) (local) (Lit_Slash /) (bin) (Lit_Slash /) (etcdctl-) (${ VSub_Name START_VERSION) } ) spids: [710 724] ) ) } spids: [708] ) ] spids: [706] ) (Assignment keyword: Assign_Local pairs: [ (assign_pair lhs: (LhsName name:API_VERSION) op: Equal rhs: { (DQ (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (C {(echo)} {(${ VSub_Name START_STORAGE)}) (C {(cut)} {(-c5-5)}) ] negated: False ) ] ) left_token: spids: [732 744] ) ) } spids: [730] ) ] spids: [728] ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(DQ (${ VSub_Name API_VERSION))} {(Lit_Other "=")} {(DQ (2))} {(Lit_Other "]")} ) terminator: ) ] action: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:ETCDCTL_CMD) op: Equal rhs: { (DQ (${ VSub_Name ETCDCTL_CMD) (" --debug --endpoint=http://127.0.0.1:") (${ VSub_Name ETCD_PORT) (" set") ) } spids: [770] ) ] spids: [770] ) ] spids: [-1 767] ) ] else_action: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:ETCDCTL_CMD) op: Equal rhs: { (DQ (${ VSub_Name ETCDCTL_CMD) (" --endpoints=http://127.0.0.1:") (${ VSub_Name ETCD_PORT) (" put") ) } spids: [786] ) ] spids: [786] ) ] spids: [783 799] ) (Sentence child: (C {(${ VSub_Name ETCD_CMD)} {(--name) (Lit_Other "=") (DQ (etcd-) (CommandSubPart command_list: (CommandList children:[(C {(hostname)})]) left_token: spids: [812 814] ) ) } {(--debug)} {(--data-dir) (Lit_Other "=") (${ VSub_Name DATA_DIRECTORY)} {(--listen-client-urls)} {(http) (Lit_Other ":") (//127.0.0.1) (Lit_Other ":") (${ VSub_Name ETCD_PORT)} {(--advertise-client-urls)} {(http) (Lit_Other ":") (//127.0.0.1) (Lit_Other ":") (${ VSub_Name ETCD_PORT)} {(--listen-peer-urls)} {(http) (Lit_Other ":") (//127.0.0.1) (Lit_Other ":") (${ VSub_Name ETCD_PEER_PORT)} {(--initial-advertise-peer-urls)} {(http) (Lit_Other ":") (//127.0.0.1) (Lit_Other ":") (${ VSub_Name ETCD_PEER_PORT)} ) terminator: ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:ETCD_PID) op: Equal rhs: {($ VSub_Bang "$!")} spids: [880] ) ] spids: [880] ) (ForEach iter_name: i iter_words: [ { (CommandSubPart command_list: (CommandList children:[(C {(seq)} {(240)})]) left_token: spids: [894 898] ) } ] do_arg_iter: False body: (DoGroup children: [ (C {(sleep)} {(0.5)}) (SimpleCommand words: [ {(${ VSub_Name ETCDCTL_CMD)} {(SQ )} {(${ VSub_Name START_VERSION)} ] more_env: [ (env_pair name: ETCDCTL_API val: {(DQ (${ VSub_Name API_VERSION))} spids: [909] ) ] ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(DQ ($ VSub_QMark "$?"))} {(-eq)} {(DQ (0))} {(Lit_Other "]")} ) terminator: ) ] action: [ (C {(echo)} {(DQ ("Etcd on port ") (${ VSub_Name ETCD_PORT) (" is up."))}) (ControlFlow token: arg_word: {(0)} ) ] spids: [-1 946] ) ] spids: [-1 965] ) ] spids: [901 968] ) spids: [893 899] ) (C {(echo)} {(DQ ("Timeout while waiting for etcd on port ") (${ VSub_Name ETCD_PORT))}) (ControlFlow token: arg_word:{(1)}) ] spids: [634] ) spids: [630 633] ) (FuncDef name: stop_etcd body: (BraceGroup children: [ (AndOr children: [ (SimpleCommand words: [ {(kill)} { (DQ (BracedVarSub token: suffix_op: (StringUnary op_id:VTest_Hyphen arg_word:{(SQ )}) spids: [1001 1004] ) ) } ] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(/dev/null)} spids: [1007] ) (Redir op_id:Redir_GreatAnd fd:2 arg_word:{(1)} spids:[1010]) ] ) (C {(Lit_Other ":")}) ] op_id: Op_DPipe ) (AndOr children: [ (SimpleCommand words: [ {(wait)} { (DQ (BracedVarSub token: suffix_op: (StringUnary op_id:VTest_Hyphen arg_word:{(SQ )}) spids: [1021 1024] ) ) } ] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(/dev/null)} spids: [1027] ) (Redir op_id:Redir_GreatAnd fd:2 arg_word:{(1)} spids:[1030]) ] ) (C {(Lit_Other ":")}) ] op_id: Op_DPipe ) ] spids: [995] ) spids: [991 994] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:ATTACHLEASE) op: Equal rhs: { (DQ (BracedVarSub token: suffix_op: (StringUnary op_id: VTest_ColonHyphen arg_word: {(Lit_Slash /) (usr) (Lit_Slash /) (local) (Lit_Slash /) (bin) (Lit_Slash /) (attachlease) } ) spids: [1042 1053] ) ) } spids: [1040] ) ] spids: [1040] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:ROLLBACK) op: Equal rhs: { (DQ (BracedVarSub token: suffix_op: (StringUnary op_id: VTest_ColonHyphen arg_word: {(Lit_Slash /) (usr) (Lit_Slash /) (local) (Lit_Slash /) (bin) (Lit_Slash /) (rollback) } ) spids: [1058 1069] ) ) } spids: [1056] ) ] spids: [1056] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:BACKUP_DIR) op: Equal rhs: {(DQ (${ VSub_Name DATA_DIRECTORY) (/migration-backup))} spids: [1079] ) ] spids: [1079] ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(DQ (${ VSub_Name CURRENT_VERSION))} {(Lit_Other "=")} {(DQ (2.2.1))} {(-a)} {(DQ (${ VSub_Name CURRENT_VERSION))} {(KW_Bang "!") (Lit_Other "=")} {(DQ (${ VSub_Name TARGET_VERSION))} {(-a)} {(KW_Bang "!")} {(-d)} {(DQ (${ VSub_Name BACKUP_DIR))} {(Lit_Other "]")} ) terminator: ) ] action: [ (C {(echo)} {(DQ ("Backup etcd before starting migration"))}) (C {(mkdir)} {(${ VSub_Name BACKUP_DIR)}) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:ETCDCTL_CMD) op: Equal rhs: {(DQ (/usr/local/bin/etcdctl-2.2.1))} spids: [1152] ) ] spids: [1152] ) (SimpleCommand words: [ {(${ VSub_Name ETCDCTL_CMD)} {(--debug)} {(backup)} {(--data-dir) (Lit_Other "=") (${ VSub_Name DATA_DIRECTORY)} {(--backup-dir) (Lit_Other "=") (${ VSub_Name BACKUP_DIR)} ] more_env: [(env_pair name:ETCDCTL_API val:{(2)} spids:[1158])] ) (C {(echo)} {(DQ ("Backup done in ") (${ VSub_Name BACKUP_DIR))}) ] spids: [-1 1135] ) ] spids: [-1 1193] ) (Assignment keyword: Assign_None pairs: [(assign_pair lhs:(LhsName name:SKIP_STEP) op:Equal rhs:{(true)} spids:[1220])] spids: [1220] ) (ForEach iter_name: step iter_words: [{(${ VSub_Name SUPPORTED_VERSIONS)}] do_arg_iter: False body: (DoGroup children: [ (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(DQ (${ VSub_Name step))} {(Lit_Other "=")} {(DQ (${ VSub_Name CURRENT_VERSION))} {(Lit_Other "]")} ) terminator: ) ] action: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:SKIP_STEP) op: Equal rhs: {(false)} spids: [1261] ) ] spids: [1261] ) ] spids: [-1 1258] ) (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(DQ (${ VSub_Name SKIP_STEP))} {(KW_Bang "!") (Lit_Other "=")} {(DQ (true))} {(Lit_Other "]")} ) terminator: ) ] action: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:START_VERSION) op: Equal rhs: {(DQ (${ VSub_Name step))} spids: [1292] ) ] spids: [1292] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:START_STORAGE) op: Equal rhs: {(DQ (${ VSub_Name CURRENT_STORAGE))} spids: [1300] ) ] spids: [1300] ) (If arms: [ (if_arm cond: [ (Sentence child: (Pipeline children:[(C {(start_etcd)})] negated:True) terminator: ) ] action: [ (C {(echo)} {(DQ ("Starting etcd ") (${ VSub_Name step) (" failed"))}) (C {(exit)} {(1)}) ] spids: [-1 1315] ) ] spids: [-1 1338] ) (C {(stop_etcd)}) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:CURRENT_VERSION) op: Equal rhs: {(${ VSub_Name step)} spids: [1348] ) ] spids: [1348] ) (SimpleCommand words: [ {(echo)} {(DQ (${ VSub_Name CURRENT_VERSION) (/) (${ VSub_Name CURRENT_STORAGE))} ] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(DQ (${ VSub_Name DATA_DIRECTORY) (/) (${ VSub_Name VERSION_FILE))} spids: [1366] ) ] ) ] spids: [1265 1285] ) ] spids: [-1 1379] ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} { (DQ (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (C {(echo)} {(${ VSub_Name CURRENT_VERSION)}) (C {(cut)} {(-c1-2)}) ] negated: False ) ] ) left_token: spids: [1387 1399] ) ) } {(Lit_Other "=")} {(DQ (3.))} {(-a)} {(DQ (${ VSub_Name CURRENT_VERSION))} {(Lit_Other "=")} {(DQ (${ VSub_Name step))} {(-a)} {(DQ (${ VSub_Name CURRENT_STORAGE))} {(Lit_Other "=")} {(DQ (etcd2))} {(-a)} {(DQ (${ VSub_Name TARGET_STORAGE))} {(Lit_Other "=")} {(DQ (etcd3))} {(Lit_Other "]")} ) terminator: ) ] action: [ (C {(echo)} {(DQ ("Performing etcd2 -> etcd3 migration"))}) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:START_VERSION) op: Equal rhs: {(DQ (${ VSub_Name step))} spids: [1473] ) ] spids: [1473] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:START_STORAGE) op: Equal rhs: {(DQ (etcd3))} spids: [1481] ) ] spids: [1481] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:ETCDCTL_CMD) op: Equal rhs: { (DQ (BracedVarSub token: suffix_op: (StringUnary op_id: VTest_ColonHyphen arg_word: {(Lit_Slash /) (usr) (Lit_Slash /) (local) (Lit_Slash /) (bin) (Lit_Slash /) (etcdctl-) (${ VSub_Name START_VERSION) } ) spids: [1489 1503] ) ) } spids: [1487] ) ] spids: [1487] ) (SimpleCommand words: [ {(${ VSub_Name ETCDCTL_CMD)} {(migrate)} {(--data-dir) (Lit_Other "=") (${ VSub_Name DATA_DIRECTORY)} ] more_env: [(env_pair name:ETCDCTL_API val:{(3)} spids:[1507])] ) (C {(echo)} {(DQ ("Attaching leases to TTL entries"))}) (If arms: [ (if_arm cond: [ (Sentence child: (Pipeline children:[(C {(start_etcd)})] negated:True) terminator: ) ] action: [ (C {(echo)} {(DQ ("Starting etcd ") (${ VSub_Name step) (" in v3 mode failed"))} ) (C {(exit)} {(1)}) ] spids: [-1 1549] ) ] spids: [-1 1568] ) (C {(${ VSub_Name ATTACHLEASE)} {(--etcd-address)} {(http) (Lit_Other ":") (//127.0.0.1) (Lit_Other ":") (${ VSub_Name ETCD_PORT)} {(--ttl-keys-prefix)} { (DQ (BracedVarSub token: suffix_op: (StringUnary op_id: VTest_ColonHyphen arg_word: {(${ VSub_Name ETCD_DATA_PREFIX) (Lit_Slash /) (events)} ) spids: [1596 1604] ) ) } {(--lease-duration)} {(1h)} ) (C {(stop_etcd)}) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:CURRENT_STORAGE) op: Equal rhs: {(DQ (etcd3))} spids: [1621] ) ] spids: [1621] ) (SimpleCommand words: [ {(echo)} {(DQ (${ VSub_Name CURRENT_VERSION) (/) (${ VSub_Name CURRENT_STORAGE))} ] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(DQ (${ VSub_Name DATA_DIRECTORY) (/) (${ VSub_Name VERSION_FILE))} spids: [1639] ) ] ) ] spids: [-1 1455] ) ] spids: [-1 1652] ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} { (DQ (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (C {(echo)} {(${ VSub_Name CURRENT_VERSION)}) (C {(cut)} {(-c1-4)}) ] negated: False ) ] ) left_token: spids: [1660 1672] ) ) } {(Lit_Other "=")} {(DQ (3.1.))} {(-a)} {(DQ (${ VSub_Name CURRENT_VERSION))} {(Lit_Other "=")} {(DQ (${ VSub_Name step))} {(-a)} {(DQ (${ VSub_Name CURRENT_STORAGE))} {(Lit_Other "=")} {(DQ (etcd3))} {(Lit_Other "]")} ) terminator: ) ] action: [ (C {(echo)} {(DQ ("Remove stale v2 data"))}) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:START_VERSION) op: Equal rhs: {(DQ (${ VSub_Name step))} spids: [1736] ) ] spids: [1736] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:START_STORAGE) op: Equal rhs: {(DQ (etcd3))} spids: [1744] ) ] spids: [1744] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:ETCDCTL_CMD) op: Equal rhs: { (DQ (BracedVarSub token: suffix_op: (StringUnary op_id: VTest_ColonHyphen arg_word: {(Lit_Slash /) (usr) (Lit_Slash /) (local) (Lit_Slash /) (bin) (Lit_Slash /) (etcdctl-) (${ VSub_Name START_VERSION) } ) spids: [1752 1766] ) ) } spids: [1750] ) ] spids: [1750] ) (If arms: [ (if_arm cond: [ (Sentence child: (Pipeline children:[(C {(start_etcd)})] negated:True) terminator: ) ] action: [ (C {(echo)} {(DQ ("Starting etcd ") (${ VSub_Name step) (" in v3 mode failed"))} ) (C {(exit)} {(1)}) ] spids: [-1 1777] ) ] spids: [-1 1796] ) (C {(${ VSub_Name ETCDCTL_CMD)} {(rm)} {(--recursive)} {(DQ (${ VSub_Name ETCD_DATA_PREFIX))} ) (C {(stop_etcd)}) (C {(echo)} {(DQ ("Successfully remove v2 data"))}) (C {(rm)} {(-rf)} {(DQ (${ VSub_Name BACKUP_DIR))}) ] spids: [-1 1714] ) ] spids: [-1 1843] ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(DQ (${ VSub_Name CURRENT_VERSION))} {(Lit_Other "=")} {(DQ (${ VSub_Name TARGET_VERSION))} {(-a)} {(DQ (${ VSub_Name CURRENT_STORAGE))} {(Lit_Other "=")} {(DQ (${ VSub_Name TARGET_STORAGE))} {(Lit_Other "]")} ) terminator: ) ] action: [(ControlFlow token:)] spids: [-1 1883] ) ] spids: [-1 1889] ) ] spids: [1234 1891] ) spids: [1228 1232] ) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(DQ (${ VSub_Name CURRENT_STORAGE))} {(Lit_Other "=")} {(DQ (etcd3))} {(-a)} {(DQ (${ VSub_Name TARGET_STORAGE))} {(Lit_Other "=")} {(DQ (etcd2))} {(Lit_Other "]")} ) terminator: ) ] action: [ (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} { (DQ (CommandSubPart command_list: (CommandList children: [ (Pipeline children: [ (C {(echo)} {(${ VSub_Name CURRENT_VERSION)}) (C {(cut)} {(-c1-4)}) ] negated: False ) ] ) left_token: spids: [1944 1956] ) ) } {(KW_Bang "!") (Lit_Other "=")} {(DQ (3.0.))} {(-o)} {(DQ (${ VSub_Name TARGET_VERSION))} {(KW_Bang "!") (Lit_Other "=")} {(DQ (2.2.1))} {(Lit_Other "]")} ) terminator: ) ] action: [ (C {(echo)} {(DQ ("etcd3 -> etcd2 downgrade is supported only between 3.0.x and 2.2.1"))} ) (ControlFlow token: arg_word:{(0)}) ] spids: [-1 1984] ) ] spids: [-1 1999] ) (C {(echo)} {(DQ ("Backup and remove all existing v2 data"))}) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:ROLLBACK_BACKUP_DIR) op: Equal rhs: {(DQ (${ VSub_Name DATA_DIRECTORY) (.bak))} spids: [2009] ) ] spids: [2009] ) (C {(rm)} {(-rf)} {(DQ (${ VSub_Name ROLLBACK_BACKUP_DIR))}) (C {(mkdir)} {(-p)} {(DQ (${ VSub_Name ROLLBACK_BACKUP_DIR))}) (C {(cp)} {(-r)} {(DQ (${ VSub_Name DATA_DIRECTORY))} {(DQ (${ VSub_Name ROLLBACK_BACKUP_DIR))} ) (C {(echo)} {(DQ ("Performing etcd3 -> etcd2 rollback"))}) (C {(${ VSub_Name ROLLBACK)} {(--data-dir)} {(DQ (${ VSub_Name DATA_DIRECTORY))}) (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(DQ ($ VSub_QMark "$?"))} {(-ne)} {(DQ (0))} {(Lit_Other "]")} ) terminator: ) ] action: [(C {(echo)} {(DQ ("Rollback to etcd2 failed"))}) (C {(exit)} {(1)})] spids: [-1 2094] ) ] spids: [-1 2109] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:CURRENT_STORAGE) op: Equal rhs: {(DQ (etcd2))} spids: [2112] ) ] spids: [2112] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:CURRENT_VERSION) op: Equal rhs: {(DQ (2.2.1))} spids: [2118] ) ] spids: [2118] ) (SimpleCommand words: [{(echo)} {(DQ (${ VSub_Name CURRENT_VERSION) (/) (${ VSub_Name CURRENT_STORAGE))}] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: {(DQ (${ VSub_Name DATA_DIRECTORY) (/) (${ VSub_Name VERSION_FILE))} spids: [2136] ) ] ) ] spids: [-1 1936] ) ] spids: [-1 2148] ) (C {(echo)} { (DQ (CommandSubPart command_list: (CommandList children: [(C {(date)} {(Lit_Other "+") (SQ <"%Y-%m-%d %H:%M:%S">)})] ) left_token: spids: [2154 2161] ) (" Migration finished") ) } ) ] )