Browse Source

Merge pull request #123 from jwhonce/wip/command

* Refactor bin/cluster to use argparse.subparsers
Thomas Wiest 10 years ago
parent
commit
08fbdf801e
1 changed files with 126 additions and 56 deletions
  1. 126 56
      bin/cluster

+ 126 - 56
bin/cluster

@@ -8,33 +8,79 @@ import os
 
 
 class Cluster(object):
-    """Python wrapper to ensure environment is correct for running ansible playbooks
     """
-
-    def __init__(self, args):
-        self.args = args
-
+    Control and Configuration Interface for OpenShift Clusters
+    """
+    def __init__(self):
         # setup ansible ssh environment
         if 'ANSIBLE_SSH_ARGS' not in os.environ:
             os.environ['ANSIBLE_SSH_ARGS'] = (
-                '-o ForwardAgent=yes'
-                ' -o StrictHostKeyChecking=no'
-                ' -o UserKnownHostsFile=/dev/null'
-                ' -o ControlMaster=auto'
-                ' -o ControlPersist=600s'
+                '-o ForwardAgent=yes '
+                '-o StrictHostKeyChecking=no '
+                '-o UserKnownHostsFile=/dev/null '
+                '-o ControlMaster=auto '
+                '-o ControlPersist=600s '
             )
 
-    def apply(self):
-        # setup ansible playbook environment
+    def create(self, args):
+        """
+        Create an OpenShift cluster for given provider
+        :param args: command line arguments provided by user
+        :return: exit status from run command
+        """
+        env = {'cluster_id': args.cluster_id}
+        playbook = "playbooks/{}/openshift-cluster/launch.yml".format(args.provider)
+        inventory = self.setup_provider(args.provider)
+
+        env['masters'] = args.masters
+        env['nodes'] = args.nodes
+
+        return self.action(args, inventory, env, playbook)
+
+    def terminate(self, args):
+        """
+        Destroy OpenShift cluster
+        :param args: command line arguments provided by user
+        :return: exit status from run command
+        """
+        env = {'cluster_id': args.cluster_id}
+        playbook = "playbooks/{}/openshift-cluster/terminate.yml".format(args.provider)
+        inventory = self.setup_provider(args.provider)
+
+        return self.action(args, inventory, env, playbook)
+
+    def list(self, args):
+        """
+        List VMs in cluster
+        :param args: command line arguments provided by user
+        :return: exit status from run command
+        """
+        raise NotImplementedError("ACTION [{}] not implemented".format(sys._getframe().f_code.co_name))
+
+    def update(self, args):
+        """
+        Update OpenShift across clustered VMs
+        :param args: command line arguments provided by user
+        :return: exit status from run command
+        """
+        raise NotImplementedError("ACTION [{}] not implemented".format(sys._getframe().f_code.co_name))
+
+
+    def setup_provider(self, provider):
+        """
+        Setup ansible playbook environment
+        :param provider: command line arguments provided by user
+        :return: path to inventory for given provider
+        """
         config = ConfigParser.ConfigParser()
-        if 'gce' == self.args.provider:
+        if 'gce' == provider:
             config.readfp(open('inventory/gce/gce.ini'))
 
             for key in config.options('gce'):
                 os.environ[key] = config.get('gce', key)
 
             inventory = '-i inventory/gce/gce.py'
-        elif 'aws' == self.args.provider:
+        elif 'aws' == provider:
             config.readfp(open('inventory/aws/ec2.ini'))
 
             for key in config.options('ec2'):
@@ -43,30 +89,23 @@ class Cluster(object):
             inventory = '-i inventory/aws/ec2.py'
         else:
             # this code should never be reached
-            raise argparse.ArgumentError("invalid PROVIDER {}".format(self.args.provider))
-
-        env = {'cluster_id': self.args.cluster_id}
-
-        if 'create' == self.args.action:
-            playbook = "playbooks/{}/openshift-cluster/launch.yml".format(self.args.provider)
-            env['masters'] = self.args.masters
-            env['nodes'] = self.args.nodes
-
-        elif 'terminate' == self.args.action:
-            playbook = "playbooks/{}/openshift-cluster/terminate.yml".format(self.args.provider)
-        elif 'list' == self.args.action:
-            # todo: implement cluster list
-            raise argparse.ArgumentError("ACTION {} not implemented".format(self.args.action))
-        elif 'update' == self.args.action:
-            # todo: implement cluster update
-            raise argparse.ArgumentError("ACTION {} not implemented".format(self.args.action))
-        else:
-            # this code should never be reached
-            raise argparse.ArgumentError("invalid ACTION {}".format(self.args.action))
+            raise ValueError("invalid PROVIDER {}".format(provider))
+
+        return inventory
+
+    def action(self, args, inventory, env, playbook):
+        """
+        Build ansible-playbook command line and execute
+        :param args: command line arguments provided by user
+        :param inventory: derived provider library
+        :param env: environment variables for kubernetes
+        :param playbook: ansible playbook to execute
+        :return: exit status from ansible-playbook command
+        """
 
         verbose = ''
-        if self.args.verbose > 0:
-            verbose = '-{}'.format('v' * self.args.verbose)
+        if args.verbose > 0:
+            verbose = '-{}'.format('v' * args.verbose)
 
         ansible_env = '-e \'{}\''.format(
             ' '.join(['%s=%s' % (key, value) for (key, value) in env.items()])
@@ -76,36 +115,67 @@ class Cluster(object):
             verbose, inventory, ansible_env, playbook
         )
 
-        if self.args.verbose > 1:
+        if args.verbose > 1:
             command = 'time {}'.format(command)
 
-        if self.args.verbose > 0:
+        if args.verbose > 0:
             sys.stderr.write('RUN [{}]\n'.format(command))
             sys.stderr.flush()
 
-        status = os.system(command)
-        if status != 0:
-            sys.stderr.write("RUN [{}] failed with exit status %d".format(command, status))
-            exit(status)
-
+        return os.system(command)
 
 
 if __name__ == '__main__':
-    parser = argparse.ArgumentParser(description='Manage OpenShift Cluster')
-    parser.add_argument('-m', '--masters', default=1, type=int, help='number of masters to create in cluster')
-    parser.add_argument('-n', '--nodes', default=2, type=int, help='number of nodes to create in cluster')
+    """
+    Implemented to support writing unit tests
+    """
+
+    cluster = Cluster()
+
+    providers = ['gce', 'aws']
+    parser = argparse.ArgumentParser(
+        description='Python wrapper to ensure proper environment for OpenShift ansible playbooks',
+    )
     parser.add_argument('-v', '--verbose', action='count', help='Multiple -v options increase the verbosity')
-    parser.add_argument('--version', action='version', version='%(prog)s 0.1')
-    parser.add_argument('action', choices=['create', 'terminate', 'update', 'list'])
-    parser.add_argument('provider', choices=['gce', 'aws'])
-    parser.add_argument('cluster_id', help='prefix for cluster VM names')
+    parser.add_argument('--version', action='version', version='%(prog)s 0.2')
+
+    meta_parser = argparse.ArgumentParser(add_help=False)
+    meta_parser.add_argument('provider', choices=providers, help='provider')
+    meta_parser.add_argument('cluster_id', help='prefix for cluster VM names')
+
+    action_parser = parser.add_subparsers(dest='action', title='actions', description='Choose from valid actions')
+
+    create_parser = action_parser.add_parser('create', help='Create a cluster', parents=[meta_parser])
+    create_parser.add_argument('-m', '--masters', default=1, type=int, help='number of masters to create in cluster')
+    create_parser.add_argument('-n', '--nodes', default=2, type=int, help='number of nodes to create in cluster')
+    create_parser.set_defaults(func=cluster.create)
+
+    terminate_parser = action_parser.add_parser('terminate', help='Destroy a cluster', parents=[meta_parser])
+    terminate_parser.add_argument('-f', '--force', action='store_true', help='Destroy cluster without confirmation')
+    terminate_parser.set_defaults(func=cluster.terminate)
+
+    update_parser = action_parser.add_parser('update', help='Update OpenShift across cluster', parents=[meta_parser])
+    update_parser.add_argument('-f', '--force', action='store_true', help='Update cluster without confirmation')
+    update_parser.set_defaults(func=cluster.update)
+
+    list_parser = action_parser.add_parser('list', help='List VMs in cluster', parents=[meta_parser])
+    list_parser.set_defaults(func=cluster.list)
+
     args = parser.parse_args()
 
-    if 'terminate' == args.action:
-        sys.stderr.write("This will terminate the ENTIRE {} environment. Are you sure? [y/N] ".format(args.cluster_id))
-        sys.stderr.flush()
-        answer = sys.stdin.read(1)
+    if 'terminate' == args.action and not args.force:
+        answer = raw_input("This will destroy the ENTIRE {} environment. Are you sure? [y/N] ".format(args.cluster_id))
+        if answer not in ['y', 'Y']:
+            sys.stderr.write('\nACTION [terminate] aborted by user!\n')
+            exit(1)
+
+    if 'update' == args.action and not args.force:
+        answer = raw_input("This is destructive and could corrupt {} environment. Continue? [y/N] ".format(args.cluster_id))
         if answer not in ['y', 'Y']:
-            exit(0)
+            sys.stderr.write('\nACTION [update] aborted by user!\n')
+            exit(1)
 
-    Cluster(args).apply()
+    status = args.func(args)
+    if status != 0:
+        sys.stderr.write("ACTION [{}] failed with exit status {}\n".format(args.action, status))
+    exit(status)