Beispiel #1
0
    def test_as_library(self):

        engine = PlumberyEngine(myEuropeanPlan, myAmericanBinding)
        DimensionDataNodeDriver.connectionCls.conn_classes = (
            None, DimensionDataMockHttp)
        DimensionDataMockHttp.type = None
        self.region = DimensionDataNodeDriver(*DIMENSIONDATA_PARAMS)

        engine.set_shared_secret('fake_secret')
        engine.set_user_name('fake_name')
        engine.set_user_password('fake_password')

        facilities = engine.list_facility('NA9')
        self.assertEqual(len(facilities), 1)

        facility = facilities[0]
        self.assertEqual(facility.get_setting('regionId'), 'dd-na')
        self.assertEqual(facility.get_setting('locationId'), 'NA9')

        self.assertTrue(facility.get_blueprint('fake') is None)

        blueprint = facility.get_blueprint('myBlueprint')

        node = blueprint['nodes'][0]
        self.assertEqual(list(node)[0], 'toto')

        config = node['toto']['cloud-config']
        self.assertEqual(config['hostname'], 'toto')
        self.assertEqual(config['write_files'][0]['content'].count('toto'), 6)

        engine.do('deploy')
        engine.do('dispose')
Beispiel #2
0
    def test_as_library(self):

        engine = PlumberyEngine(myFacility)
        DimensionDataNodeDriver.connectionCls.conn_classes = (
            None, DimensionDataMockHttp)
        DimensionDataMockHttp.type = None
        self.region = DimensionDataNodeDriver(*DIMENSIONDATA_PARAMS)

        engine.set_shared_secret('fake_secret')
        engine.set_user_name('fake_name')
        engine.set_user_password('fake_password')

        facilities = engine.list_facility('NA9')
        self.assertEqual(len(facilities), 1)

        facility = facilities[0]
        self.assertEqual(facility.get_setting('regionId'), 'dd-na')
        self.assertEqual(facility.get_setting('locationId'), 'NA9')

        blueprint = facility.get_blueprint('fake')
        self.assertEqual(blueprint.keys(),
                         ['ethernet', 'domain', 'nodes', 'target'])

        engine.do('deploy')
        engine.do('refresh')
        engine.do('dispose')
    def test_as_library(self):

        engine = PlumberyEngine(myEuropeanPlan, myAmericanBinding)
        DimensionDataNodeDriver.connectionCls.conn_classes = (
            None, DimensionDataMockHttp)
        DimensionDataMockHttp.type = None
        self.region = DimensionDataNodeDriver(*DIMENSIONDATA_PARAMS)

        engine.set_shared_secret('fake_secret')
        engine.set_user_name('fake_name')
        engine.set_user_password('fake_password')

        facilities = engine.list_facility('NA9')
        self.assertEqual(len(facilities), 1)

        facility = facilities[0]
        self.assertEqual(facility.get_setting('regionId'), 'dd-na')
        self.assertEqual(facility.get_setting('locationId'), 'NA9')

        self.assertTrue(facility.get_blueprint('fake') is None)

        blueprint = facility.get_blueprint('myBlueprint')

        node = blueprint['nodes'][0]
        self.assertEqual(list(node)[0], 'toto')

        config = node['toto']['cloud-config']
        self.assertEqual(config['hostname'], 'toto')
        self.assertEqual(config['write_files'][0]['content'].count('toto'), 6)

        engine.do('deploy')
        engine.do('dispose')
Beispiel #4
0
    def process(self, item, counter):
        """
        Processes one action

        Example actions:

            ('deploy', '')
            ('dispose', '')

        """
        print('Worker is working on {}'.format(counter))

        (verb, parameters) = item

        try:

            fittings = self.context.get('plumbery.fittings', '.')            \
                +'/'+self.context.get('worker.template', 'example/first')   \
                +'/fittings.yaml'
            print('- reading {}'.format(fittings))

            print('- loading plumbery engine')
            engine = PlumberyEngine(fittings)

        except Exception as feedback:
            print("Error while reading fittings plan")
            self.outbox.put("Error while reading fittings plan")
            return

        try:
            engine.do(verb)

            self.outbox.put(engine.document_elapsed())

        except Exception as feedback:
            print("Unable to do '{}'".format(verb))
            self.outbox.put("Unable to do '{}'".format(verb))
            return
Beispiel #5
0
    def test_lifecycle(self):

        engine = PlumberyEngine()
        DimensionDataNodeDriver.connectionCls.conn_classes = (
            None, DimensionDataMockHttp)
        DimensionDataMockHttp.type = None
        self.region = DimensionDataNodeDriver(*DIMENSIONDATA_PARAMS)

        engine.set_shared_secret('fake_secret')
        engine.set_user_name('fake_name')
        engine.set_user_password('fake_password')

        engine.do('build')
        engine.build_all_blueprints()
        engine.build_blueprint('myBlueprint')

        engine.do('start')
        engine.start_all_blueprints()
        engine.start_blueprint('myBlueprint')

        engine.do('polish')
        engine.polish_all_blueprints()
        engine.polish_blueprint('myBlueprint')

        engine.do('stop')
        engine.stop_all_blueprints()
        engine.stop_blueprint('myBlueprint')

        engine.wipe_all_blueprints()
        engine.wipe_blueprint('myBlueprint')

        engine.do('destroy')
        engine.destroy_all_blueprints()
        engine.destroy_blueprint('myBlueprint')

        banner = engine.document_elapsed()
        self.assertEqual('Worked for you' in banner, True)
Beispiel #6
0
def main(args=None, engine=None):
    """
    Runs plumbery from the command line

    :param args: arguments to be considered for this invocation
    :type args: a list of ``str``

    :param engine: an instance of the plumbery engine
    :type engine: :class:`plumbery.PlumberEngine`

    Example::

        $ python -m plumbery fittings.yaml build web

    In this example, plumbery loads fittings plan from ``fittings.yaml``, then
    it builds the blueprint named ``web``.

    If no blueprint is mentioned, then plumbery looks at all blueprint
    definitions in the fittings plan. In other terms, the following command
    builds the entire fittings plan, eventually across multiple facilities::

        $ python -m plumbery fittings.yaml build

    Of course, plumbery can be invoked through the entire life cycle of your
    fittings::

        $ python -m plumbery fittings.yaml build
        $ python -m plumbery fittings.yaml start
        $ python -m plumbery fittings.yaml polish

        ... nodes are up and running ...

        $ python -m plumbery fittings.yaml stop

        ... nodes have been stopped ...

        $ python -m plumbery fittings.yaml wipe

        ... nodes have been destroyed, but the infrastructure remains ...

        $ python -m plumbery fittings.yaml destroy

        ... every virtual resources has been removed ...


    To focus at a single location, put the character '@' followed by the id.
    For example, to build fittings only at 'NA12' you would type::

        $ python -m plumbery fittings.yaml build @NA12

    To focus on one blueprint just mention its name on the command line.
    For example, if fittings plan has a blueprint for nodes running Docker,
    then you may use following statements to bootstrap each node::

        $ python -m plumbery fittings.yaml build docker
        $ python -m plumbery fittings.yaml start docker
        $ python -m plumbery fittings.yaml prepare docker

        ... Docker is up and running at multiple nodes ...

    If you create a new polisher and put it in the directory
    ``plumbery\polishers``, then it will become automatically available::

        $ python -m plumbery fittings.yaml my_special_stuff

    To get some help, you can type::

        $ python -m plumbery -h

    """

    # part 1 - understand what the user wants

    if args is None:
        args = sys.argv[1:]

    try:
        args = parse_args(args)

    except Exception as feedback:
        plogging.error("Incorrect arguments. "
                       "Maybe the following can help: python -m plumbery -h")
        if plogging.getEffectiveLevel() == logging.DEBUG:
            raise
        else:
            plogging.error("{}: {}".format(feedback.__class__.__name__,
                                           str(feedback)))
        sys.exit(2)

    # part 2 - get a valid and configured engine

    if engine is None:
        try:
            engine = PlumberyEngine(args.fittings, args.parameters)

            if args.safe:
                engine.safeMode = True

        except Exception as feedback:
            if plogging.getEffectiveLevel() == logging.DEBUG:
                plogging.error("Cannot read fittings plan from '{}'".format(
                    args.fittings))
                raise
            else:
                plogging.error("Cannot read fittings plan from '{}'"
                               ", run with -d for debug".format(args.fittings))
                plogging.error("{}: {}".format(feedback.__class__.__name__,
                                               str(feedback)))
            sys.exit(2)

    # part 3 - do the job

    try:
        engine.do(args.action, args.blueprints, args.facilities)

        plogging.info(engine.document_elapsed())

    except Exception as feedback:
        if plogging.getEffectiveLevel() == logging.DEBUG:
            plogging.error("Unable to do '{}'".format(args.action))
            raise
        else:
            plogging.error("Unable to do '{}', run with -d for debug".format(
                args.action))
            plogging.error("{}: {}".format(feedback.__class__.__name__,
                                           str(feedback)))
        sys.exit(1)
Beispiel #7
0
class TestPlumberyEngine(unittest.TestCase):

    def test_set(self):

        settings = {
            'safeMode': False,
            'polishers': [
                {'ansible': {}},
                {'spit': {}},
                ]
            }

        self.engine = PlumberyEngine()
        self.engine.set_shared_secret('fake_secret')
        self.assertEqual(self.engine.get_shared_secret(), 'fake_secret')

        random = self.engine.get_secret('random')
        self.assertEqual(len(random), 9)
        self.assertEqual(self.engine.get_secret('random'), random)

        self.engine.set_user_name('fake_name')
        self.assertEqual(self.engine.get_user_name(), 'fake_name')

        self.engine.set_user_password('fake_password')
        self.assertEqual(self.engine.get_user_password(), 'fake_password')

        self.engine.set(settings)
        self.assertEqual(self.engine.safeMode, False)

        try:
            self.engine.from_text(myPlan)
            cloudConfig = self.engine.get_cloud_config()
            self.assertEqual(len(cloudConfig.keys()), 3)
            self.engine.add_facility(myFacility)
            self.assertEqual(len(self.engine.facilities), 2)

        except socket.gaierror:
            pass
        except InvalidCredsError:
            pass

    def test_lifecycle(self):

        self.engine = PlumberyEngine()
        self.engine.set_shared_secret('fake_secret')
        self.assertEqual(self.engine.get_shared_secret(), 'fake_secret')

        self.engine.set_user_name('fake_name')
        self.assertEqual(self.engine.get_user_name(), 'fake_name')

        self.engine.set_user_password('fake_password')
        self.assertEqual(self.engine.get_user_password(), 'fake_password')

        try:
            self.engine.do('build')
            self.engine.build_all_blueprints()
            self.engine.build_blueprint('myBlueprint')

            self.engine.do('start')
            self.engine.start_all_blueprints()
            self.engine.start_blueprint('myBlueprint')

            self.engine.do('polish')
            self.engine.polish_all_blueprints()
            self.engine.polish_blueprint('myBlueprint')

            self.engine.do('stop')
            self.engine.stop_all_blueprints()
            self.engine.stop_blueprint('myBlueprint')

            self.engine.wipe_all_blueprints()
            self.engine.wipe_blueprint('myBlueprint')

            self.engine.do('destroy')
            self.engine.destroy_all_blueprints()
            self.engine.destroy_blueprint('myBlueprint')

        except socket.gaierror:
            pass
        except InvalidCredsError:
            pass

    def test_lookup(self):

        self.engine = PlumberyEngine()
        self.assertEqual(self.engine.lookup('plumbery.version'), __version__)

        self.engine.secrets = {}
        random = self.engine.lookup('random.secret')
        self.assertEqual(len(random), 9)
        self.assertEqual(self.engine.lookup('random.secret'), random)

        md5 = self.engine.lookup('random.md5.secret')
        self.assertEqual(len(md5), 32)
        self.assertNotEqual(md5, random)

        sha = self.engine.lookup('random.sha1.secret')
        self.assertEqual(len(sha), 40)
        self.assertNotEqual(sha, random)

        sha = self.engine.lookup('random.sha256.secret')
        self.assertEqual(len(sha), 64)
        self.assertNotEqual(sha, random)

        id1 = self.engine.lookup('id1.uuid')
        self.assertEqual(len(id1), 36)
        self.assertEqual(self.engine.lookup('id1.uuid'), id1)
        id2 = self.engine.lookup('id2.uuid')
        self.assertEqual(len(id2), 36)
        self.assertNotEqual(id1, id2)

        self.engine.lookup('application.secret')
        self.engine.lookup('database.secret')
        self.engine.lookup('master.secret')
        self.engine.lookup('slave.secret')

        original = 'hello world'
        text = self.engine.lookup('pair1.rsa_public')
        self.assertEqual(text.startswith('ssh-rsa '), True)
        key = RSA.importKey(text)
        encrypted = key.publickey().encrypt(original, 32)

        privateKey = self.engine.lookup('pair1.rsa_private')
        self.assertEqual(privateKey.startswith(
            '-----BEGIN RSA PRIVATE KEY-----'), True)
        key = RSA.importKey(self.engine.lookup('pair1.rsa_private'))
        decrypted = key.decrypt(ast.literal_eval(str(encrypted)))
        self.assertEqual(decrypted, original)

        self.assertEqual(len(self.engine.secrets), 12)

        with self.assertRaises(LookupError):
            localKey = self.engine.lookup('local.rsa_private')

        localKey = self.engine.lookup('local.rsa_public')
        try:
            path = '~/.ssh/id_rsa.pub'
            with open(os.path.expanduser(path)) as stream:
                text = stream.read()
                stream.close()
                self.assertEqual(localKey.strip(), text.strip())
                logging.info("Successful lookup of local public key")

        except IOError:
            pass

    def test_secrets(self):

        engine = PlumberyEngine()
        engine.secrets = {'hello': 'world'}
        engine.save_secrets(plan='test_engine.yaml')
        self.assertEqual(os.path.isfile('.test_engine.secrets'), True)
        engine.secrets = {}
        engine.load_secrets(plan='test_engine.yaml')
        self.assertEqual(engine.secrets['hello'], 'world')
        engine.forget_secrets(plan='test_engine.yaml')
        self.assertEqual(os.path.isfile('.test_engine.secrets'), False)

    def test_defaults(self):

        engine = PlumberyEngine()
        engine.from_text(defaultsPlan)
        self.assertEqual(engine.get_default('locationId'), 'EU6')
        self.assertEqual(engine.get_default('regionId'), 'dd-eu')
        self.assertEqual(engine.get_default('ipv4'), 'auto')

    def test_parser(self):
        args = parse_args(['fittings.yaml', 'build', 'web'])
        self.assertEqual(args.fittings, 'fittings.yaml')
        self.assertEqual(args.action, 'build')
        self.assertEqual(args.blueprints, ['web'])
        self.assertEqual(args.facilities, None)
        args = parse_args(['fittings.yaml', 'build', 'web', '-d'])
        self.assertEqual(
            logging.getLogger().getEffectiveLevel(), logging.DEBUG)
        args = parse_args(['fittings.yaml', 'build', 'web', '-q'])
        self.assertEqual(
            logging.getLogger().getEffectiveLevel(), logging.WARNING)
        args = parse_args(['fittings.yaml', 'start', '@NA12'])
        self.assertEqual(args.fittings, 'fittings.yaml')
        self.assertEqual(args.action, 'start')
        self.assertEqual(args.blueprints, None)
        self.assertEqual(args.facilities, ['NA12'])
        args = parse_args([
            'fittings.yaml', 'rub', 'web', 'sql', '@NA9', '@NA12'])
        self.assertEqual(args.fittings, 'fittings.yaml')
        self.assertEqual(args.action, 'rub')
        self.assertEqual(args.blueprints, ['web', 'sql'])
        self.assertEqual(args.facilities, ['NA9', 'NA12'])
        args = parse_args([
            'fittings.yaml', 'rub', 'web', '@NA9', 'sql', '@NA12'])
        self.assertEqual(args.fittings, 'fittings.yaml')
        self.assertEqual(args.action, 'rub')
        self.assertEqual(args.blueprints, ['web', 'sql'])
        self.assertEqual(args.facilities, ['NA9', 'NA12'])
        args = parse_args(['fittings.yaml', 'polish'])
        self.assertEqual(args.fittings, 'fittings.yaml')
        self.assertEqual(args.action, 'polish')
        self.assertEqual(args.blueprints, None)
        self.assertEqual(args.facilities, None)

    def test_main(self):
        engine = PlumberyEngine()
        engine.from_text(myPlan)
        engine.set_user_name('fake_name')
        engine.set_user_password('fake_password')
        with self.assertRaises(SystemExit):
            main(['bad args'], engine)
        with self.assertRaises(SystemExit):
            main(['fittings.yaml'], engine)
        with self.assertRaises(SystemExit):
            main(['fittings.yaml', 'xyz123', 'web'], engine)
        with self.assertRaises(SystemExit):
            main(['-v'], engine)
        with self.assertRaises(SystemExit):
            main(['fittings.yaml', 'build', 'web', '-v'], engine)
Beispiel #8
0
def main(args=[], engine=None):
    """
    Runs plumbery from the command line

    Example::

        $ python -m plumbery fittings.yaml build web

    In this example, plumbery loads fittings plan from ``fittings.yaml``, then
    it builds the blueprint named ``web``.

    If no blueprint is mentioned, then plumbery looks at all blueprint
    definitions in the fittings plan. In other terms, the following command
    builds the entire fittings plan, eventually across multiple facilities::

        $ python -m plumbery fittings.yaml build

    Of course, plumbery can be invoked through the entire life cycle of your
    fittings::

        $ python -m plumbery fittings.yaml build
        $ python -m plumbery fittings.yaml start
        $ python -m plumbery fittings.yaml polish

        ... nodes are up and running ...

        $ python -m plumbery fittings.yaml stop

        ... nodes have been stopped ...

        $ python -m plumbery fittings.yaml wipe

        ... nodes have been destroyed, but the infrastructure remains ...

        $ python -m plumbery fittings.yaml destroy

        ... every virtual resources has been removed ...


    To focus at a single location, put the character '@' followed by the id.
    For example, to build fittings only at 'NA12' you would type::

        $ python -m plumbery fittings.yaml build @NA12

    To apply a polisher just mention its name on the command line. For example,
    if fittings plan has a blueprint for nodes running Docker, then you may
    use following statements to bootstrap each node::

        $ python -m plumbery fittings.yaml build docker
        $ python -m plumbery fittings.yaml start docker
        $ python -m plumbery fittings.yaml prepare docker

        ... Docker is up and running at multiple nodes ...

    If you create a new polisher and put it in the directory
    ``plumbery\polishers``, then it will become automatically available::

        $ python -m plumbery fittings.yaml my_special_stuff

    To get some help, you can type::

        $ python -m plumbery -h

    """

    # part 1 - understand what the user wants

    try:
        args = parse_args(args)

    except Exception as feedback:
        logging.error("Incorrect arguments. "
                      "Maybe the following can help: python -m plumbery -h")
        if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
            raise
        else:
            logging.error("{}: {}".format(
                feedback.__class__.__name__,
                str(feedback)))
        sys.exit(2)

    # part 2 - acquire the toolbox

    if engine is None:
        try:
            engine = PlumberyEngine(args.fittings, args.parameters)

            if args.safe:
                engine.safeMode = True

        except Exception as feedback:
            if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
                logging.error("Cannot read fittings plan from '{}'".format(
                    args.fittings))
                raise
            else:
                logging.error("Cannot read fittings plan from '{}'"
                              ", run with -d for debug".format(
                                  args.fittings))
                logging.error("{}: {}".format(
                    feedback.__class__.__name__,
                    str(feedback)))
            sys.exit(2)

    # part 3 - do the job

    try:
        engine.do(args.action, args.blueprints, args.facilities)

        logging.info(engine.document_elapsed())

    except Exception as feedback:
        if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
            logging.error("Unable to do '{}'".format(args.action))
            raise
        else:
            logging.error("Unable to do '{}', run with -d for debug".format(
                args.action))
            logging.error("{}: {}".format(
                feedback.__class__.__name__,
                str(feedback)))
        sys.exit(2)
    def test_lifecycle(self):

        engine = PlumberyEngine()
        DimensionDataNodeDriver.connectionCls.conn_classes = (
            None, DimensionDataMockHttp)
        DimensionDataMockHttp.type = None
        self.region = DimensionDataNodeDriver(*DIMENSIONDATA_PARAMS)

        engine.set_shared_secret('fake_secret')
        engine.set_user_name('fake_name')
        engine.set_user_password('fake_password')

        engine.do('build')
        engine.build_all_blueprints()
        engine.do('build', 'myBlueprint')
        engine.build_blueprint('myBlueprint')

        engine.do('deploy')
        engine.do('deploy', 'myBlueprint')

        engine.do('destroy')
        engine.destroy_all_blueprints()
        engine.do('destroy', 'myBlueprint')
        engine.destroy_blueprint('myBlueprint')

        engine.do('dispose')
        engine.do('dispose', 'myBlueprint')

        engine.do('polish')
        engine.polish_all_blueprints()
        engine.do('polish', 'myBlueprint')
        engine.polish_blueprint('myBlueprint')

        engine.do('secrets')

        engine.do('start')
        engine.start_all_blueprints()
        engine.do('start', 'myBlueprint')
        engine.start_blueprint('myBlueprint')

        engine.do('stop')
        engine.stop_all_blueprints()
        engine.do('stop', 'myBlueprint')
        engine.stop_blueprint('myBlueprint')

        engine.do('wipe')
        engine.wipe_all_blueprints()
        engine.do('wipe', 'myBlueprint')
        engine.wipe_blueprint('myBlueprint')

        banner = engine.document_elapsed()
        self.assertEqual('Worked for you' in banner, True)
Beispiel #10
0
def main(args=[], engine=None):
    """
    Runs plumbery from the command line

    Example::

        $ python -m plumbery fittings.yaml build web

    In this example, plumbery loads fittings plan from ``fittings.yaml``, then
    it builds the blueprint named ``web``.

    If no blueprint is mentioned, then plumbery looks at all blueprint
    definitions in the fittings plan. In other terms, the following command
    builds the entire fittings plan, eventually across multiple facilities::

        $ python -m plumbery fittings.yaml build

    Of course, plumbery can be invoked through the entire life cycle of your
    fittings::

        $ python -m plumbery fittings.yaml build
        $ python -m plumbery fittings.yaml start
        $ python -m plumbery fittings.yaml polish

        ... nodes are up and running here ...

        $ python -m plumbery fittings.yaml stop
        $ python -m plumbery fittings.yaml destroy

    To focus at a single location, put the character '@' followed by the id.
    For example, to build fittings only at 'NA12' you would type::

        $ python -m plumbery fittings.yaml build @NA12

    To apply a polisher just mention its name on the command line. For example,
    if fittings plan has a blueprint for nodes running Docker, then you may
    use following statements to bootstrap each node::

        $ python -m plumbery fittings.yaml build docker
        $ python -m plumbery fittings.yaml start docker
        $ python -m plumbery fittings.yaml rub docker

        ... Docker is up and running at multiple nodes ...

    If you create a new polisher and put it in the directory
    ``plumbery\polishers``, then it will become automatically available::

        $ python -m plumbery fittings.yaml my_special_stuff

    To get some help, you can type::

        $ python -m plumbery -h

    """

    args = parse_args(args)

    if engine is None:
        try:
            engine = PlumberyEngine(args.fittings)

        except Exception as feedback:
            logging.info(
                "{}: error: cannot read fittings plan from '{}'".format(
                    'plumbery', args.fittings))
            logging.debug(str(feedback))
            sys.exit(2)

    try:
        engine.do(args.action, args.blueprints, args.facilities)

    except PlumberyException as feedback:
        logging.getLogger().setLevel(logging.INFO)
        print("{}: error: unrecognised action '{}'".format(
            'plumbery', args.action))
        sys.exit(2)
Beispiel #11
0
class TestPlumberyEngine(unittest.TestCase):

    def test_set(self):

        settings = {
            'safeMode': False,
            'polishers': [
                {'ansible': {}},
                {'spit': {}},
                ]
            }

        self.engine = PlumberyEngine()
        self.engine.set_shared_secret('fake_secret')
        self.assertEqual(self.engine.get_shared_secret(), 'fake_secret')

        random = self.engine.get_random_secret()
        self.assertEqual(len(random), 9)
        self.assertEqual(self.engine.get_random_secret(), random)

        self.engine.set_user_name('fake_name')
        self.assertEqual(self.engine.get_user_name(), 'fake_name')

        self.engine.set_user_password('fake_password')
        self.assertEqual(self.engine.get_user_password(), 'fake_password')

        self.engine.set(settings)
        self.assertEqual(self.engine.safeMode, False)

        try:
            self.engine.from_text(myPlan)
            self.engine.add_facility(myFacility)
            self.assertEqual(len(self.engine.facilities), 2)

        except socket.gaierror:
            pass
        except InvalidCredsError:
            pass

    def test_lifecycle(self):

        self.engine = PlumberyEngine()
        self.engine.set_shared_secret('fake_secret')
        self.assertEqual(self.engine.get_shared_secret(), 'fake_secret')

        self.engine.set_user_name('fake_name')
        self.assertEqual(self.engine.get_user_name(), 'fake_name')

        self.engine.set_user_password('fake_password')
        self.assertEqual(self.engine.get_user_password(), 'fake_password')

        try:
            self.engine.do('build')
            self.engine.build_all_blueprints()
            self.engine.build_blueprint('myBlueprint')

            self.engine.do('start')
            self.engine.start_all_blueprints()
            self.engine.start_blueprint('myBlueprint')

            self.engine.do('polish')
            self.engine.polish_all_blueprints()
            self.engine.polish_blueprint('myBlueprint')

            self.engine.do('stop')
            self.engine.stop_all_blueprints()
            self.engine.stop_blueprint('myBlueprint')

            self.engine.wipe_all_blueprints()
            self.engine.wipe_blueprint('myBlueprint')

            self.engine.do('destroy')
            self.engine.destroy_all_blueprints()
            self.engine.destroy_blueprint('myBlueprint')

        except socket.gaierror:
            pass
        except InvalidCredsError:
            pass

    def test_lookup(self):

        self.engine = PlumberyEngine()
        self.assertEqual(self.engine.lookup('plumbery.version'), __version__)

        random = self.engine.lookup('random.secret')
        self.assertEqual(len(random), 9)
        self.assertEqual(self.engine.lookup('random.secret'), random)

    def test_parser(self):
        args = parse_args(['fittings.yaml', 'build', 'web'])
        self.assertEqual(args.fittings, 'fittings.yaml')
        self.assertEqual(args.action, 'build')
        self.assertEqual(args.blueprints, ['web'])
        self.assertEqual(args.facilities, None)
        args = parse_args(['fittings.yaml', 'build', 'web', '-d'])
        self.assertEqual(
            logging.getLogger().getEffectiveLevel(), logging.DEBUG)
        args = parse_args(['fittings.yaml', 'build', 'web', '-q'])
        self.assertEqual(
            logging.getLogger().getEffectiveLevel(), logging.WARNING)
        args = parse_args(['fittings.yaml', 'start', '@NA12'])
        self.assertEqual(args.fittings, 'fittings.yaml')
        self.assertEqual(args.action, 'start')
        self.assertEqual(args.blueprints, None)
        self.assertEqual(args.facilities, ['NA12'])
        args = parse_args([
            'fittings.yaml', 'rub', 'web', 'sql', '@NA9', '@NA12'])
        self.assertEqual(args.fittings, 'fittings.yaml')
        self.assertEqual(args.action, 'rub')
        self.assertEqual(args.blueprints, ['web', 'sql'])
        self.assertEqual(args.facilities, ['NA9', 'NA12'])
        args = parse_args([
            'fittings.yaml', 'rub', 'web', '@NA9', 'sql', '@NA12'])
        self.assertEqual(args.fittings, 'fittings.yaml')
        self.assertEqual(args.action, 'rub')
        self.assertEqual(args.blueprints, ['web', 'sql'])
        self.assertEqual(args.facilities, ['NA9', 'NA12'])
        args = parse_args(['fittings.yaml', 'polish'])
        self.assertEqual(args.fittings, 'fittings.yaml')
        self.assertEqual(args.action, 'polish')
        self.assertEqual(args.blueprints, None)
        self.assertEqual(args.facilities, None)

    def test_main(self):
        engine = PlumberyEngine()
        engine.from_text(myPlan)
        engine.set_user_name('fake_name')
        engine.set_user_password('fake_password')
        with self.assertRaises(SystemExit):
            main(['bad args'], engine)
        with self.assertRaises(SystemExit):
            main(['fittings.yaml'], engine)
        with self.assertRaises(SystemExit):
            main(['fittings.yaml', 'xyz123', 'web'], engine)
        with self.assertRaises(SystemExit):
            main(['-v'], engine)
        with self.assertRaises(SystemExit):
            main(['fittings.yaml', 'build', 'web', '-v'], engine)