diff --git a/tests/test_inventory.py b/tests/test_inventory.py index c9061cc..5f3d7c2 100644 --- a/tests/test_inventory.py +++ b/tests/test_inventory.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import mock import unittest from collections import OrderedDict @@ -25,7 +26,9 @@ import inventory class TestInventory(unittest.TestCase): - def setUp(self): + @mock.patch('inventory.sys') + def setUp(self, sys_mock): + sys_mock.exit = mock.Mock() super(TestInventory, self).setUp() self.data = ['10.90.3.2', '10.90.3.3', '10.90.3.4'] self.inv = inventory.KargoInventory() diff --git a/utils/jenkins/kargo_deploy.sh b/utils/jenkins/kargo_deploy.sh index 2d85c84..00634a0 100755 --- a/utils/jenkins/kargo_deploy.sh +++ b/utils/jenkins/kargo_deploy.sh @@ -103,7 +103,7 @@ function wait_for_nodes { mkdir -p tmp logs # Allow non-Jenkins script to predefine info -if [[ -z "$SLAVE_IPS" && -z "$ADMIN_IP" ]]; then +if [[ -z "$REAPPLY" && -z "$SLAVE_IPS" && -z "$ADMIN_IP" ]]; then ENV_TYPE="fuel-devops" dos.py erase ${ENV_NAME} || true rm -rf logs/* @@ -114,7 +114,7 @@ if [[ -z "$SLAVE_IPS" && -z "$ADMIN_IP" ]]; then ADMIN_IP=${SLAVE_IPS[0]} wait_for_nodes $ADMIN_IP else - ENV_TYPE=${ENV_TYPE:-other} + ENV_TYPE=${ENV_TYPE:-other_or_reapply} SLAVE_IPS=( $SLAVE_IPS ) ADMIN_IP=${ADMIN_IP:-${SLAVE_IPS[0]}} fi @@ -193,8 +193,18 @@ if [ -n "$CUSTOM_YAML" ]; then custom_opts="-e @$ADMIN_WORKSPACE/kargo/custom.yaml" fi -echo "Generating ansible inventory on admin node..." -admin_node_command CONFIG_FILE=$ADMIN_WORKSPACE/kargo/inventory/inventory.cfg python3 $ADMIN_WORKSPACE/utils/kargo/inventory.py ${SLAVE_IPS[@]} +# Try to get IPs from inventory if it isn't provided +if [[ -z "$SLAVE_IPS" ]]; then + if admin_node_command stat $ADMIN_WORKSPACE/kargo/inventory/inventory.cfg; then + SLAVE_IPS=($(admin_node_command CONFIG_FILE=$ADMIN_WORKSPACE/kargo/inventory/inventory.cfg python3 $ADMIN_WORKSPACE/utils/kargo/inventory.py print_ips)) + else + echo "No slave nodes available. Unable to proceed!" + exit_gracefully 1 + fi +else + echo "Generating ansible inventory on admin node..." + admin_node_command CONFIG_FILE=$ADMIN_WORKSPACE/kargo/inventory/inventory.cfg python3 $ADMIN_WORKSPACE/utils/kargo/inventory.py ${SLAVE_IPS[@]} +fi echo "Waiting for all nodes to be reachable by SSH..." wait_for_nodes ${SLAVE_IPS[@]} @@ -292,5 +302,6 @@ set -x rm -f VLAN_IPS fi - +# TODO(mattymo): Shift to FORCE_NEW instead of REAPPLY +echo "To reapply deployment, run env REAPPLY=yes ADMIN_IP=$ADMIN_IP $0" exit_gracefully ${deploy_res} diff --git a/utils/kargo/inventory.py b/utils/kargo/inventory.py index d65bb48..0fbaccc 100644 --- a/utils/kargo/inventory.py +++ b/utils/kargo/inventory.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # Usage: kargo_inventory.py ip1 [ip2 ...] # Examples: kargo_inventory.py 10.10.1.3 10.10.1.4 10.10.1.5 # @@ -19,6 +19,7 @@ import sys ROLES = ['kube-master', 'all', 'k8s-cluster:children', 'kube-node', 'etcd'] PROTECTED_NAMES = ROLES +AVAILABLE_COMMANDS = ['help', 'print_cfg', 'print_ips'] _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True, '0': False, 'no': False, 'false': False, 'off': False} @@ -40,6 +41,10 @@ class KargoInventory(object): if config_file: self.config.read(config_file) + if changed_hosts and changed_hosts[0] in AVAILABLE_COMMANDS: + self.parse_command(changed_hosts[0], changed_hosts[1:]) + sys.exit(0) + self.ensure_required_groups(ROLES) if changed_hosts: @@ -50,6 +55,9 @@ class KargoInventory(object): self.set_k8s_cluster() self.set_kube_node(self.hosts.keys()) self.set_etcd(list(self.hosts.keys())[:3]) + else: # Show help if no options + self.show_help() + sys.exit(0) if config_file: with open(config_file, 'w') as f: @@ -174,6 +182,40 @@ class KargoInventory(object): for host in hosts: self.add_host_to_group('etcd', host) + def parse_command(self, command, args=None): + if command == 'help': + self.show_help() + elif command == 'print_cfg': + self.print_config() + elif command == 'print_ips': + self.print_ips() + else: + raise Exception("Invalid command specified.") + + def show_help(self): + help_text = '''Usage: inventory.py ip1 [ip2 ...] +Examples: inventory.py 10.10.1.3 10.10.1.4 10.10.1.5 + +Available commands: +help - Display this message +print_cfg - Write inventory file to stdout +print_ips - Write a space-delimited list of IPs from "all" group + +Advanced usage: +Add another host after initial creation: inventory.py 10.10.1.5 +Delete a host: inventory.py -10.10.1.3 +Delete a host by id: inventory.py -node1''' + print(help_text) + + def print_config(self): + self.config.write(sys.stdout) + + def print_ips(self): + ips = [] + for host, opts in self.config.items('all'): + ips.append(self.get_ip_from_opts(opts)) + print(' '.join(ips)) + def main(argv=None): if not argv: