예제 #1
0
파일: gui.py 프로젝트: graywen24/conjure-up
    def render(self):

        if len(self.step_metas) == 0:
            self.finish(None, None, done=True)
            return

        step_widgets = deque()
        
        for step_meta_path in self.step_metas:
            step_ex_path, ext = path.splitext(step_meta_path)
            if not path.isfile(step_ex_path) or \
               not os.access(step_ex_path, os.X_OK):
                app.log.error(
                    'Unable to process step, missing {}'.format(step_ex_path))
                continue
            step_metadata = {}
            with open(step_meta_path) as fp:
                step_metadata = yaml.load(fp.read())

            try:
                # Store step model and its widget
                model = StepModel(step_metadata, step_meta_path)
                step_widget = StepWidget(
                    app,
                    model,
                    self.finish)
                if not step_widget.model.viewable:
                    app.log.debug("Skipping step: {}".format(step_widget))
                    continue
                model.path = step_ex_path
                step_widgets.append(step_widget)
                app.log.debug("Queueing step: {}".format(step_widget))
            except Exception as e:
                self.__handle_exception('E002', e)
                return

        try:
            self.all_step_widgets = list(step_widgets)
            self.view = StepsView(app, step_widgets, self.finish)

            # Set initial step as active and viewable
            step_widgets[0].description.set_text((
                'body', step_widgets[0].model.description))
            step_widgets[0].icon.set_text((
                'pending_icon', step_widgets[0].icon.get_text()[0]
            ))
            step_widgets[0].generate_additional_input()
            self.view.step_pile.focus_position = 2

        except Exception as e:
            self.__handle_exception('E002', e)
            return

        app.ui.set_header(
            title="Additional Application Configuration",
            excerpt="Please finish the installation by configuring your "
            "application with these steps.")
        app.ui.set_body(self.view)
        app.ui.set_footer('')
        self.update()
예제 #2
0
파일: gui.py 프로젝트: mnama/conjure-up
    def render(self):

        if len(self.step_metas) == 0:
            self.finish(None, None, done=True)
            return

        step_widgets = deque()

        for step_meta_path in self.step_metas:
            step_ex_path, ext = path.splitext(step_meta_path)
            if not path.isfile(step_ex_path) or \
               not os.access(step_ex_path, os.X_OK):
                app.log.error(
                    'Unable to process step, missing {}'.format(step_ex_path))
                continue
            step_metadata = {}
            with open(step_meta_path) as fp:
                step_metadata = yaml.load(fp.read())

            try:
                # Store step model and its widget
                model = StepModel(step_metadata, step_meta_path)
                step_widget = StepWidget(app, model, self.finish)
                if not step_widget.model.viewable:
                    app.log.debug("Skipping step: {}".format(step_widget))
                    continue
                model.path = step_ex_path
                step_widgets.append(step_widget)
                app.log.debug("Queueing step: {}".format(step_widget))
            except Exception as e:
                self.__handle_exception('E002', e)
                return

        try:
            self.all_step_widgets = list(step_widgets)
            self.view = StepsView(app, step_widgets, self.finish)

            # Set initial step as active and viewable
            step_widgets[0].description.set_text(
                ('body', step_widgets[0].model.description))
            step_widgets[0].icon.set_text(
                ('pending_icon', step_widgets[0].icon.get_text()[0]))
            step_widgets[0].generate_additional_input()
            self.view.step_pile.focus_position = 2

        except Exception as e:
            self.__handle_exception('E002', e)
            return

        app.ui.set_header(
            title="Additional Application Configuration",
            excerpt="Please finish the installation by configuring your "
            "application with these steps.")
        app.ui.set_body(self.view)
        app.ui.set_footer('')
        self.update()
예제 #3
0
파일: gui.py 프로젝트: tomconte/conjure-up
 def finish(self, spellname):
     utils.set_terminal_title("conjure-up {}".format(spellname))
     utils.set_chosen_spell(spellname,
                            os.path.join(app.argv.cache_dir, spellname))
     download_local(os.path.join(app.config['spells-dir'], spellname),
                    app.config['spell-dir'])
     utils.set_spell_metadata()
     StepModel.load_spell_steps()
     AddonModel.load_spell_addons()
     utils.setup_metadata_controller()
     return controllers.use('addons').render()
예제 #4
0
 async def pre_bootstrap(self):
     """ runs pre bootstrap script if exists
     """
     step = StepModel({},
                      filename='00_pre-bootstrap',
                      name='pre-bootstrap')
     await step.run(self.msg_cb)
예제 #5
0
async def wait_for_applications(msg_cb):
    await events.DeploymentComplete.wait()
    msg = 'Waiting for deployment to settle.'
    app.log.info(msg)
    msg_cb(msg)

    # retry done check a few times to work around
    # https://bugs.launchpad.net/juju-wait/+bug/1680963
    for i in range(3):
        try:
            step = StepModel({},
                             filename='00_deploy-done',
                             name='Deployment Watcher')
            await utils.run_step(step, msg_cb)
            break
        except Exception as e:
            if i < 2 and 'Applications did not start successfully' in str(e):
                await asyncio.sleep(5)
                app.log.debug('Retrying 00_deploy-done: {}'.format(i))
                continue
            raise

    events.ModelSettled.set()
    msg = 'Model settled.'
    app.log.info(msg)
    msg_cb(msg)
예제 #6
0
async def pre_deploy(msg_cb):
    """ runs pre deploy script if exists
    """
    await events.ModelConnected.wait()
    step = StepModel({}, filename='00_pre-deploy', name='pre-deploy')
    await step.run(msg_cb)
    events.PreDeployComplete.set()
예제 #7
0
    async def do_bootstrap(self):
        await self.pre_bootstrap()
        self.emit('Bootstrapping Juju controller.')
        track_event("Juju Bootstrap", "Started", "")
        cloud_with_region = app.provider.cloud
        if app.provider.region:
            cloud_with_region = '/'.join([app.provider.cloud,
                                          app.provider.region])
        success = await juju.bootstrap(app.provider.controller,
                                       cloud_with_region,
                                       app.provider.model,
                                       credential=app.provider.credential)
        if not success:
            log_file = '{}-bootstrap.err'.format(app.provider.controller)
            log_file = Path(app.config['spell-dir']) / log_file
            err_log = log_file.read_text('utf8').splitlines()
            app.log.error("Error bootstrapping controller: "
                          "{}".format(err_log))
            app.sentry.context.merge({'extra': {'err_log': err_log[-400:]}})
            raise Exception('Unable to bootstrap (cloud type: {})'.format(
                app.provider.cloud_type))

        self.emit('Bootstrap complete.')
        track_event("Juju Bootstrap", "Done", "")

        await juju.login()  # login to the newly created (default) model

        step = StepModel({},
                         filename='00_post-bootstrap',
                         name='post-bootstrap')
        await step.run(self.msg_cb, 'Juju Post-Bootstrap')
        events.Bootstrapped.set()
예제 #8
0
 def __init__(self, name):
     self.name = name
     self.path = Path(app.config['spell-dir']) / 'addons' / name
     self.metadata = self._read('metadata.yaml')
     self.bundle = self._read('bundle.yaml')
     self.steps = [
         StepModel.load(step_path)
         for step_path in sorted((self.path / 'steps').glob('*.yaml'))
     ]
예제 #9
0
 def build_widget(self):
     app.steps_data['name'] = {
         'USERNAME': '******',
         'PASSWORD': '',
         'EMAIL': '',
     }
     super().build_widget()
     self.add_step(
         StepForm(
             app,
             StepModel(
                 {
                     'description': 'Download Deis CLI',
                     'viewable': True,
                 }, '', '')))
     self.add_step(
         StepForm(
             app,
             StepModel(
                 {
                     'description':
                     'Create Admin User',
                     'viewable':
                     True,
                     'additional-input': [
                         {
                             'label': 'Username',
                             'key': 'USERNAME',
                             'type': 'text'
                         },
                         {
                             'label': 'Password',
                             'key': 'PASSWORD',
                             'type': 'password'
                         },
                         {
                             'label': 'Email',
                             'key': 'EMAIL',
                             'type': 'text'
                         },
                     ],
                 }, 'filename', 'name')))
     return self.step_pile
예제 #10
0
def load_step(step_meta_path):
    step_meta_path = Path(step_meta_path)
    step_name = step_meta_path.stem
    step_ex_path = step_meta_path.parent / step_name
    if not step_ex_path.is_file():
        raise ValidationError(
            'Step {} has no implementation'.format(step_name))
    elif not os.access(str(step_ex_path), os.X_OK):
        raise ValidationError('Step {} is not executable'.format(step_name))
    step_metadata = yaml.load(step_meta_path.read_text())
    model = StepModel(step_metadata, str(step_ex_path), step_name)
    return model
예제 #11
0
파일: tui.py 프로젝트: sincereuk/conjure-up
 def render(self):
     for step_meta_path in self.step_metas:
         step_ex_path, ext = path.splitext(step_meta_path)
         if not path.isfile(step_ex_path) or \
            not os.access(step_ex_path, os.X_OK):
             app.log.error(
                 'Unable to process step, missing {}'.format(step_ex_path))
             continue
         step_metadata = {}
         with open(step_meta_path) as fp:
             step_metadata = yaml.load(fp.read())
         model = StepModel(step_metadata, step_meta_path)
         model.path = step_ex_path
         app.log.debug("Running step: {}".format(model))
         try:
             step_model, _ = common.do_step(model, None, utils.info)
             self.results[step_model.title] = step_model.result
         except Exception as e:
             utils.error("Failed to run {}: {}".format(model.path, e))
             sys.exit(1)
     self.finish()
예제 #12
0
파일: addon.py 프로젝트: zhatin/conjure-up
 def __init__(self, name):
     self.name = name
     self.path = Path(app.config['spell-dir']) / 'addons' / name
     self.metadata = self._read('metadata.yaml')
     self.bundle = self._read('bundle.yaml')
     self.steps = [
         StepModel.load(step_path,
                        source=self.friendly_name,
                        addon_name=name)
         for step_path in sorted((self.path / 'steps').glob('*'))
         if step_path.is_dir()
     ]
예제 #13
0
def load_step(step_meta_path):
    step_ex_path, ext = path.splitext(step_meta_path)
    short_path = '/'.join(step_ex_path.split('/')[-3:])
    if not path.isfile(step_ex_path):
        raise ValidationError(
            'Step {} has no implementation'.format(short_path))
    elif not os.access(step_ex_path, os.X_OK):
        raise ValidationError('Step {} is not executable, make sure it has '
                              'the executable bit set'.format(short_path))
    with open(step_meta_path) as fp:
        step_metadata = yaml.load(fp.read())
        model = StepModel(step_metadata, step_ex_path)
        return model
예제 #14
0
    async def pre_bootstrap(self):
        """ runs pre bootstrap script if exists
        """

        # Set provider type for post-bootstrap
        app.env['JUJU_PROVIDERTYPE'] = juju.get_cloud_types_by_name()[
            app.current_cloud]
        app.env['JUJU_CONTROLLER'] = app.current_controller
        app.env['JUJU_MODEL'] = app.current_model
        app.env['CONJURE_UP_SPELLSDIR'] = app.argv.spells_dir

        step = StepModel({}, filename='00_pre-bootstrap', name='pre-bootstrap')
        await utils.run_step(step, self.msg_cb)
예제 #15
0
 def render(self):
     for step_meta_path in self.step_metas:
         step_ex_path, ext = path.splitext(step_meta_path)
         if not path.isfile(step_ex_path) or \
            not os.access(step_ex_path, os.X_OK):
             app.log.error(
                 'Unable to process step, missing {}'.format(step_ex_path))
             continue
         step_metadata = {}
         with open(step_meta_path) as fp:
             step_metadata = yaml.load(fp.read())
         model = StepModel(step_metadata, step_meta_path)
         model.path = step_ex_path
         app.log.debug("Running step: {}".format(model))
         try:
             step_model, _ = common.do_step(model,
                                            None,
                                            utils.info)
             self.results[step_model.title] = step_model.result
         except Exception as e:
             utils.error("Failed to run {}: {}".format(model.path, e))
             sys.exit(1)
     self.finish()
예제 #16
0
async def wait_for_applications(msg_cb):
    await events.DeploymentComplete.wait()
    msg = 'Waiting for deployment to settle.'
    app.log.info(msg)
    msg_cb(msg)

    step = StepModel({'title': 'Deployment Watcher'},
                     filename='00_deploy-done',
                     name='00_deploy-done')
    await step.run(msg_cb)

    events.ModelSettled.set()
    msg = 'Model settled.'
    app.log.info(msg)
    msg_cb(msg)
예제 #17
0
async def pre_deploy(msg_cb):
    """ runs pre deploy script if exists
    """
    await events.ModelConnected.wait()

    # Set provider type for post-bootstrap
    app.env['JUJU_PROVIDERTYPE'] = app.juju.client.info.provider_type
    # Set current credential name (localhost doesn't have one)
    app.env['JUJU_CREDENTIAL'] = app.current_credential or ''
    app.env['JUJU_CONTROLLER'] = app.current_controller
    app.env['JUJU_MODEL'] = app.current_model
    app.env['CONJURE_UP_SPELLSDIR'] = app.argv.spells_dir

    step = StepModel({}, filename='00_pre-deploy', name='pre-deploy')
    await utils.run_step(step, msg_cb)
    events.PreDeployComplete.set()
예제 #18
0
    async def do_bootstrap(self, creds):
        if app.is_jaas:
            return

        await self.pre_bootstrap()
        self.emit('Bootstrapping Juju controller.')
        track_event("Juju Bootstrap", "Started", "")
        cloud_with_region = app.current_cloud
        if app.current_region:
            cloud_with_region = '/'.join(
                [app.current_cloud, app.current_region])
        success = await juju.bootstrap(app.current_controller,
                                       cloud_with_region,
                                       app.current_model,
                                       credential=creds)
        if not success:
            log_file = '{}-bootstrap.err'.format(app.current_controller)
            log_file = Path(app.config['spell-dir']) / log_file
            err_log = log_file.read_text('utf8').splitlines()
            app.log.error("Error bootstrapping controller: "
                          "{}".format(err_log))
            app.sentry.context.merge({'extra': {'err_log': err_log}})
            raise Exception('Unable to bootstrap (cloud type: {})'.format(
                app.current_cloud_type))

        self.emit('Bootstrap complete.')
        track_event("Juju Bootstrap", "Done", "")

        await juju.login()  # login to the newly created (default) model

        # Set provider type for post-bootstrap
        app.env['JUJU_PROVIDERTYPE'] = app.juju.client.info.provider_type
        app.env['JUJU_CONTROLLER'] = app.current_controller
        app.env['JUJU_MODEL'] = app.current_model

        step = StepModel({},
                         filename='00_post-bootstrap',
                         name='post-bootstrap')
        await utils.run_step(step, self.msg_cb, 'Juju Post-Bootstrap')
        events.Bootstrapped.set()
예제 #19
0
def main():
    if os.geteuid() == 0:
        print("")
        print("  !! This should _not_ be run as root or with sudo. !!")
        print("")
        sys.exit(1)

    # Verify we can access ~/.local/share/juju if it exists
    juju_dir = pathlib.Path('~/.local/share/juju').expanduser()
    if juju_dir.exists():
        try:
            for f in juju_dir.iterdir():
                if f.is_file():
                    f.read_text()
        except PermissionError:
            print("")
            print("  !! Unable to read from ~/.local/share/juju, please "
                  "double check your permissions on that directory "
                  "and its files. !!")
            print("")
            sys.exit(1)

    utils.set_terminal_title("conjure-up")
    opts = parse_options(sys.argv[1:])
    spell = os.path.basename(os.path.abspath(opts.spell))

    if not os.path.isdir(opts.cache_dir):
        os.makedirs(opts.cache_dir)

    # Application Config
    os.environ['UNIT_STATE_DB'] = os.path.join(opts.cache_dir, '.state.db')
    app.state = unitdata.kv()

    app.env = os.environ.copy()
    app.config = {'metadata': None}
    app.argv = opts
    app.log = setup_logging(app, os.path.join(opts.cache_dir,
                                              'conjure-up.log'), opts.debug)

    if app.argv.conf_file.expanduser().exists():
        conf = configparser.ConfigParser()
        conf.read_string(app.argv.conf_file.expanduser().read_text())
        app.notrack = conf.getboolean('REPORTING', 'notrack', fallback=False)
        app.noreport = conf.getboolean('REPORTING', 'noreport', fallback=False)
    if app.argv.notrack:
        app.notrack = True
    if app.argv.noreport:
        app.noreport = True

    # Grab current LXD and Juju versions
    app.log.debug("Juju version: {}, "
                  "conjure-up version: {}".format(utils.juju_version(),
                                                  VERSION))

    # Setup proxy
    apply_proxy()

    app.session_id = os.getenv('CONJURE_TEST_SESSION_ID', str(uuid.uuid4()))

    spells_dir = app.argv.spells_dir

    app.config['spells-dir'] = spells_dir
    spells_index_path = os.path.join(app.config['spells-dir'],
                                     'spells-index.yaml')
    spells_registry_branch = os.getenv('CONJUREUP_REGISTRY_BRANCH', 'stable')

    if not app.argv.nosync:
        if not os.path.exists(spells_dir):
            utils.info("No spells found, syncing from registry, please wait.")
        try:
            download_or_sync_registry(app.argv.registry,
                                      spells_dir,
                                      branch=spells_registry_branch)
        except subprocess.CalledProcessError as e:
            if not os.path.exists(spells_dir):
                utils.error("Could not load from registry")
                sys.exit(1)

            app.log.debug('Could not sync spells from github: {}'.format(e))
    else:
        if not os.path.exists(spells_index_path):
            utils.error(
                "You opted to not sync from the spells registry, however, "
                "we could not find any suitable spells in: "
                "{}".format(spells_dir))
            sys.exit(1)

    with open(spells_index_path) as fp:
        app.spells_index = yaml.safe_load(fp.read())

    spell_name = spell
    app.endpoint_type = detect_endpoint(opts.spell)

    if app.endpoint_type == EndpointType.LOCAL_SEARCH:
        spells = utils.find_spells_matching(opts.spell)

        if len(spells) == 0:
            utils.error("Can't find a spell matching '{}'".format(opts.spell))
            sys.exit(1)

        # One result means it was a direct match and we can copy it
        # now. Changing the endpoint type then stops us from showing
        # the picker UI. More than one result means we need to show
        # the picker UI and will defer the copy to
        # SpellPickerController.finish(), so nothing to do here.
        if len(spells) == 1:
            app.log.debug("found spell {}".format(spells[0][1]))
            spell = spells[0][1]
            utils.set_chosen_spell(spell_name,
                                   os.path.join(opts.cache_dir, spell['key']))
            download_local(
                os.path.join(app.config['spells-dir'], spell['key']),
                app.config['spell-dir'])
            utils.set_spell_metadata()
            StepModel.load_spell_steps()
            AddonModel.load_spell_addons()
            app.endpoint_type = EndpointType.LOCAL_DIR

    # download spell if necessary
    elif app.endpoint_type == EndpointType.LOCAL_DIR:
        if not os.path.isdir(opts.spell):
            utils.warning("Could not find spell {}".format(opts.spell))
            sys.exit(1)

        if not os.path.exists(os.path.join(opts.spell, "metadata.yaml")):
            utils.warning("'{}' does not appear to be a spell. "
                          "{}/metadata.yaml was not found.".format(
                              opts.spell, opts.spell))
            sys.exit(1)

        spell_name = os.path.basename(os.path.abspath(spell))
        utils.set_chosen_spell(spell_name, path.join(opts.cache_dir,
                                                     spell_name))
        download_local(opts.spell, app.config['spell-dir'])
        utils.set_spell_metadata()
        StepModel.load_spell_steps()
        AddonModel.load_spell_addons()

    elif app.endpoint_type in [EndpointType.VCS, EndpointType.HTTP]:

        utils.set_chosen_spell(spell, path.join(opts.cache_dir, spell))
        remote = get_remote_url(opts.spell)

        if remote is None:
            utils.warning("Can't guess URL matching '{}'".format(opts.spell))
            sys.exit(1)

        download(remote, app.config['spell-dir'], True)
        utils.set_spell_metadata()
        StepModel.load_spell_steps()
        AddonModel.load_spell_addons()

    app.env['CONJURE_UP_CACHEDIR'] = app.argv.cache_dir

    if app.argv.show_env:
        if app.endpoint_type in [None, EndpointType.LOCAL_SEARCH]:
            utils.error("Please specify a spell for headless mode.")
            sys.exit(1)

        show_env()

    app.sentry = raven.Client(
        dsn=SENTRY_DSN,
        release=VERSION,
        transport=RequestsHTTPTransport,
        processors=('conjureup.utils.SanitizeDataProcessor', ))

    track_screen("Application Start")
    track_event("OS", platform.platform(), "")

    app.loop = asyncio.get_event_loop()
    app.loop.add_signal_handler(signal.SIGINT, events.Shutdown.set)
    try:
        if app.argv.cloud:
            cloud = None
            region = None
            if '/' in app.argv.cloud:
                parse_cli_cloud = app.argv.cloud.split('/')
                cloud, region = parse_cli_cloud
                app.log.debug("Region found {} for cloud {}".format(
                    cloud, region))
            else:
                cloud = app.argv.cloud

            cloud_types = juju.get_cloud_types_by_name()
            if cloud not in cloud_types:
                utils.error('Unknown cloud: {}'.format(cloud))
                sys.exit(1)

            if app.endpoint_type in [None, EndpointType.LOCAL_SEARCH]:
                utils.error("Please specify a spell for headless mode.")
                sys.exit(1)

            app.provider = load_schema(cloud_types[cloud])

            try:
                app.provider.load(cloud)
            except SchemaErrorUnknownCloud as e:
                utils.error(e)
                sys.exit(1)

            if region:
                app.provider.region = region

            app.headless = True
            app.ui = None
            app.env['CONJURE_UP_HEADLESS'] = "1"
            app.loop.create_task(events.shutdown_watcher())
            app.loop.create_task(_start())
            app.loop.run_forever()

        else:
            app.ui = ConjureUI()

            EventLoop.build_loop(app.ui,
                                 STYLES,
                                 unhandled_input=events.unhandled_input)
            app.loop.create_task(events.shutdown_watcher())
            app.loop.create_task(_start())
            EventLoop.run()
    finally:
        # explicitly close asyncio event loop to avoid hitting the
        # following issue due to signal handlers added by
        # asyncio.create_subprocess_exec being cleaned up during final
        # garbage collection: https://github.com/python/asyncio/issues/396
        app.loop.close()
    sys.exit(app.exit_code)
예제 #20
0
파일: app.py 프로젝트: huxili/conjure-up
def main():
    if os.geteuid() == 0:
        print("")
        print("  !! This should _not_ be run as root or with sudo. !!")
        print("")
        sys.exit(1)

    # Verify we can access ~/.local/share/juju if it exists
    juju_dir = pathlib.Path('~/.local/share/juju').expanduser()
    if juju_dir.exists():
        try:
            for f in juju_dir.iterdir():
                if f.is_file():
                    f.read_text()
        except PermissionError:
            print("")
            print("  !! Unable to read from ~/.local/share/juju, please "
                  "double check your permissions on that directory "
                  "and its files. !!")
            print("")
            sys.exit(1)

    utils.set_terminal_title("conjure-up")
    opts = parse_options(sys.argv[1:])
    opt_defaults = parse_options([])

    # Load conjurefile, merge any overridding options from argv
    if not opts.conf_file:
        opts.conf_file = []
    if pathlib.Path('~/.config/conjure-up.conf').expanduser().exists():
        opts.conf_file.insert(
            0, pathlib.Path('~/.config/conjure-up.conf').expanduser())
    if (pathlib.Path('.') / 'Conjurefile').exists():
        opts.conf_file.insert(0, pathlib.Path('.') / 'Conjurefile')
    for conf in opts.conf_file:
        if not conf.exists():
            print("Unable to locate config {} for processing.".format(
                str(conf)))
            sys.exit(1)

    try:
        app.conjurefile = Conjurefile.load(opts.conf_file)
    except ValueError as e:
        print(str(e))
        sys.exit(1)
    app.conjurefile.merge_argv(opts, opt_defaults)

    if app.conjurefile['gen-config']:
        Conjurefile.print_tpl()
        sys.exit(0)

    spell = os.path.basename(os.path.abspath(app.conjurefile['spell']))

    if not os.path.isdir(app.conjurefile['cache-dir']):
        os.makedirs(app.conjurefile['cache-dir'])

    # Application Config
    kv_db = os.path.join(app.conjurefile['cache-dir'], '.state.db')
    app.state = KV(kv_db)

    app.env = os.environ.copy()
    app.env['KV_DB'] = kv_db
    app.config = {'metadata': None}

    app.log = setup_logging(app,
                            os.path.join(app.conjurefile['cache-dir'],
                                         'conjure-up.log'),
                            app.conjurefile.get('debug', False))

    # Make sure juju paths are setup
    juju.set_bin_path()
    juju.set_wait_path()

    app.no_track = app.conjurefile['no-track']
    app.no_report = app.conjurefile['no-report']

    # Grab current LXD and Juju versions
    app.log.debug("Juju version: {}, "
                  "conjure-up version: {}".format(
                      utils.juju_version(),
                      VERSION))

    # Setup proxy
    apply_proxy()

    app.session_id = os.getenv('CONJURE_TEST_SESSION_ID',
                               str(uuid.uuid4()))

    spells_dir = app.conjurefile['spells-dir']

    app.config['spells-dir'] = spells_dir
    spells_index_path = os.path.join(app.config['spells-dir'],
                                     'spells-index.yaml')
    spells_registry_branch = os.getenv('CONJUREUP_REGISTRY_BRANCH', 'master')

    if not app.conjurefile['no-sync']:
        if not os.path.exists(spells_dir):
            utils.info("No spells found, syncing from registry, please wait.")
        try:
            download_or_sync_registry(
                app.conjurefile['registry'],
                spells_dir, branch=spells_registry_branch)
        except subprocess.CalledProcessError as e:
            if not os.path.exists(spells_dir):
                utils.error("Could not load from registry")
                sys.exit(1)

            app.log.debug(
                'Could not sync spells from github: {}'.format(e))
    else:
        if not os.path.exists(spells_index_path):
            utils.error(
                "You opted to not sync from the spells registry, however, "
                "we could not find any suitable spells in: "
                "{}".format(spells_dir))
            sys.exit(1)

    with open(spells_index_path) as fp:
        app.spells_index = yaml.safe_load(fp.read())

    addons_aliases_index_path = os.path.join(app.config['spells-dir'],
                                             'addons-aliases.yaml')
    if os.path.exists(addons_aliases_index_path):
        with open(addons_aliases_index_path) as fp:
            app.addons_aliases = yaml.safe_load(fp.read())

    spell_name = spell
    app.endpoint_type = detect_endpoint(app.conjurefile['spell'])

    if app.conjurefile['spell'] != consts.UNSPECIFIED_SPELL:
        app.spell_given = True

    # Check if spell is actually an addon
    addon = utils.find_addons_matching(app.conjurefile['spell'])
    if addon:
        app.log.debug("addon found, setting required spell")
        utils.set_chosen_spell(addon['spell'],
                               os.path.join(app.conjurefile['cache-dir'],
                                            addon['spell']))
        download_local(os.path.join(app.config['spells-dir'],
                                    addon['spell']),
                       app.config['spell-dir'])
        utils.set_spell_metadata()
        StepModel.load_spell_steps()
        AddonModel.load_spell_addons()
        app.selected_addons = addon['addons']
        app.alias_given = True
        controllers.setup_metadata_controller()
        app.endpoint_type = EndpointType.LOCAL_DIR

    elif app.endpoint_type == EndpointType.LOCAL_SEARCH:
        spells = utils.find_spells_matching(app.conjurefile['spell'])

        if len(spells) == 0:
            utils.error("Can't find a spell matching '{}'".format(
                app.conjurefile['spell']))
            sys.exit(1)

        # One result means it was a direct match and we can copy it
        # now. Changing the endpoint type then stops us from showing
        # the picker UI. More than one result means we need to show
        # the picker UI and will defer the copy to
        # SpellPickerController.finish(), so nothing to do here.
        if len(spells) == 1:
            app.log.debug("found spell {}".format(spells[0][1]))
            spell = spells[0][1]
            utils.set_chosen_spell(spell_name,
                                   os.path.join(app.conjurefile['cache-dir'],
                                                spell['key']))
            download_local(os.path.join(app.config['spells-dir'],
                                        spell['key']),
                           app.config['spell-dir'])
            utils.set_spell_metadata()
            StepModel.load_spell_steps()
            AddonModel.load_spell_addons()
            app.endpoint_type = EndpointType.LOCAL_DIR

    # download spell if necessary
    elif app.endpoint_type == EndpointType.LOCAL_DIR:
        if not os.path.isdir(app.conjurefile['spell']):
            utils.warning("Could not find spell {}".format(
                app.conjurefile['spell']))
            sys.exit(1)

        if not os.path.exists(os.path.join(app.conjurefile['spell'],
                                           "metadata.yaml")):
            utils.warning("'{}' does not appear to be a spell. "
                          "{}/metadata.yaml was not found.".format(
                              app.conjurefile['spell'],
                              app.conjurefile['spell']))
            sys.exit(1)

        spell_name = os.path.basename(os.path.abspath(spell))
        utils.set_chosen_spell(spell_name,
                               path.join(app.conjurefile['cache-dir'],
                                         spell_name))
        download_local(app.conjurefile['spell'], app.config['spell-dir'])
        utils.set_spell_metadata()
        StepModel.load_spell_steps()
        AddonModel.load_spell_addons()

    elif app.endpoint_type in [EndpointType.VCS, EndpointType.HTTP]:

        utils.set_chosen_spell(spell, path.join(
            app.conjurefile['cache-dir'], spell))
        remote = get_remote_url(app.conjurefile['spell'])

        if remote is None:
            utils.warning("Can't guess URL matching '{}'".format(
                app.conjurefile['spell']))
            sys.exit(1)

        download(remote, app.config['spell-dir'], True)
        utils.set_spell_metadata()
        StepModel.load_spell_steps()
        AddonModel.load_spell_addons()

    app.env['CONJURE_UP_CACHEDIR'] = app.conjurefile['cache-dir']
    app.env['PATH'] = "/snap/bin:{}".format(app.env['PATH'])

    if app.conjurefile['show-env']:
        if app.endpoint_type in [None, EndpointType.LOCAL_SEARCH]:
            utils.error("Please specify a spell for headless mode.")
            sys.exit(1)

        show_env()

    app.sentry = raven.Client(
        dsn=SENTRY_DSN,
        release=VERSION,
        transport=RequestsHTTPTransport,
        processors=(
            'conjureup.utils.SanitizeDataProcessor',
        )
    )

    track_screen("Application Start")
    track_event("OS", platform.platform(), "")

    app.loop = asyncio.get_event_loop()
    app.loop.add_signal_handler(signal.SIGINT, events.Shutdown.set)

    # Enable charmstore querying
    app.juju.charmstore = CharmStore(app.loop)
    try:
        if app.conjurefile.is_valid:
            cloud = None
            region = None
            if '/' in app.conjurefile['cloud']:
                parse_cli_cloud = app.conjurefile['cloud'].split('/')
                cloud, region = parse_cli_cloud
                app.log.debug(
                    "Region found {} for cloud {}".format(cloud,
                                                          region))
            else:
                cloud = app.conjurefile['cloud']

            cloud_types = juju.get_cloud_types_by_name()
            if cloud not in cloud_types:
                utils.error('Unknown cloud: {}'.format(cloud))
                sys.exit(1)

            if app.endpoint_type in [None, EndpointType.LOCAL_SEARCH]:
                utils.error("Please specify a spell for headless mode.")
                sys.exit(1)

            app.provider = load_schema(cloud_types[cloud])

            try:
                app.provider.load(cloud)
            except errors.SchemaCloudError as e:
                utils.error(e)
                sys.exit(1)

            if region:
                app.provider.region = region

            app.headless = True
            app.ui = None
            app.env['CONJURE_UP_HEADLESS'] = "1"
            app.loop.create_task(events.shutdown_watcher())
            app.loop.create_task(_start())
            app.loop.run_forever()

        else:
            app.ui = ConjureUI()
            app.ui.set_footer('Press ? for help')

            EventLoop.build_loop(app.ui, STYLES,
                                 unhandled_input=events.unhandled_input,
                                 handle_mouse=False)
            app.loop.create_task(events.shutdown_watcher())
            app.loop.create_task(_start())
            EventLoop.run()
    finally:
        # explicitly close asyncio event loop to avoid hitting the
        # following issue due to signal handlers added by
        # asyncio.create_subprocess_exec being cleaned up during final
        # garbage collection: https://github.com/python/asyncio/issues/396
        app.loop.close()
    sys.exit(app.exit_code)