Esempio n. 1
0
def get_plugin_configuration(plugin_ref) -> Response:
    """
    Endpoint for getting a plugin's configuration profiles
    """

    project = Project.find()
    settings = PluginSettingsService(project, show_hidden=False)
    plugin = ConfigService(project).get_plugin(plugin_ref)

    discovery_service = PluginDiscoveryService(project)
    try:
        plugin_def = discovery_service.find_plugin(plugin.type, plugin.name)
        settings_group_validation = plugin_def.settings_group_validation
    except PluginNotFoundError:
        settings_group_validation = []

    profiles = settings.profiles_with_config(db.session, plugin, redacted=True)
    for profile in profiles:
        freeze_profile_config_keys(profile)

    return jsonify({
        "profiles":
        profiles,
        "settings":
        Canonical.as_canonical(settings.definitions(plugin)),
        "settings_group_validation":
        settings_group_validation,
    })
Esempio n. 2
0
def config(ctx, project, plugin_type, plugin_name, format):
    plugin_type = PluginType.from_cli_argument(
        plugin_type) if plugin_type else None

    config = ConfigService(project)
    plugin = config.find_plugin(plugin_name,
                                plugin_type=plugin_type,
                                configurable=True)

    _, Session = project_engine(project)
    session = Session()
    try:
        settings = PluginSettingsService(project).build(plugin)

        ctx.obj["settings"] = settings
        ctx.obj["session"] = session

        if ctx.invoked_subcommand is None:
            if format == "json":
                config = settings.as_config(session=session)
                print(json.dumps(config))
            elif format == "env":
                for env, value in settings.as_env(session=session).items():
                    print(f"{env}={value}")
    finally:
        session.close()
Esempio n. 3
0
def validate_plugin_config(plugin: PluginRef, name, value, project: Project,
                           settings: PluginSettingsService):
    setting_def = settings.find_setting(plugin, name)
    # we want to prevent the edition of protected settings from the UI
    if setting_def.protected:
        logging.warning("Cannot set a 'protected' configuration externally.")
        return False

    if setting_def.kind == "file" and value and value != "":
        uploads_directory = project.extract_dir(plugin.full_name)
        resolved_file_path = project.root_dir(value).resolve()
        if not str(resolved_file_path).startswith(
                str(uploads_directory) + "/"):
            logging.warning(
                "Cannot set a file configuration to a path outside the project directory"
            )
            return False

    old_value, source = settings.get_value(db.session, plugin, name)
    if source in (PluginSettingValueSource.ENV,
                  PluginSettingValueSource.MELTANO_YML):
        logging.warning(
            "Cannot override a configuration set in the environment or meltano.yml."
        )
        return False

    return True
Esempio n. 4
0
    def setup_js_context():
        # setup the appUrl
        appUrl = urlsplit(request.host_url)
        g.jsContext = {"appUrl": appUrl.geturl()[:-1]}

        if tracker.send_anonymous_usage_stats:
            g.jsContext["isSendAnonymousUsageStats"] = True
            g.jsContext["projectId"] = tracker.project_id

        g.jsContext["version"] = meltano.__version__

        # setup the airflowUrl
        try:
            airflow = ConfigService(project).find_plugin("airflow")
            settings = PluginSettingsService(project)
            airflow_port, _ = settings.get_value(db.session, airflow,
                                                 "webserver.web_server_port")
            g.jsContext["airflowUrl"] = appUrl._replace(
                netloc=f"{appUrl.hostname}:{airflow_port}").geturl()[:-1]
        except (PluginMissingError, PluginSettingMissingError):
            pass

        # setup the dbtDocsUrl
        g.jsContext["dbtDocsUrl"] = appUrl._replace(
            path="/-/dbt/").geturl()[:-1]
Esempio n. 5
0
def get_plugin_configuration(plugin_ref) -> Response:
    """
    Endpoint for getting a plugin's configuration
    """

    project = Project.find()

    plugins_service = ProjectPluginsService(project)
    plugin = plugins_service.get_plugin(plugin_ref)

    settings = PluginSettingsService(project,
                                     plugin,
                                     plugins_service=plugins_service,
                                     show_hidden=False)

    try:
        settings_group_validation = plugin.settings_group_validation
    except PluginNotFoundError:
        settings_group_validation = []

    return jsonify({
        **get_config_with_metadata(settings),
        "settings":
        Canonical.as_canonical(settings.definitions(extras=False)),
        "settings_group_validation":
        settings_group_validation,
    })
Esempio n. 6
0
    def after_install(
        self,
        installer: PluginInstallService,
        plugin: ProjectPlugin,
        reason: PluginInstallReason,
    ):
        project = installer.project
        plugins_service = installer.plugins_service

        plugin_settings_service = PluginSettingsService(
            project, plugin, plugins_service=plugins_service)
        update_config = plugin_settings_service.get("_update")
        paths_to_update = [
            path for path, to_update in update_config.items() if to_update
        ]

        if reason is PluginInstallReason.ADD:
            print(f"Adding '{plugin.name}' files to project...")

            for path in self.create_files(project, paths_to_update):
                print(f"Created {path}")
        elif reason is PluginInstallReason.UPGRADE:
            print(f"Updating '{plugin.name}' files in project...")

            updated_paths = self.update_files(project, paths_to_update)
            if not updated_paths:
                print("Nothing to update")
                return

            for path in updated_paths:
                print(f"Updated {path}")
        else:
            print(
                f"Run `meltano upgrade files` to update your project's '{plugin.name}' files."
            )
Esempio n. 7
0
def test_plugin_configuration(plugin_ref) -> Response:
    """
    Endpoint for testing a plugin configuration's valid connection
    """
    project = Project.find()
    payload = request.get_json()
    plugins_service = ProjectPluginsService(project)
    plugin = plugins_service.get_plugin(plugin_ref)

    settings = PluginSettingsService(project,
                                     plugin,
                                     plugins_service=plugins_service,
                                     show_hidden=False)

    config = payload.get("config", {})
    valid_config = {
        name: value
        for name, value in config.items()
        if validate_plugin_config(plugin, name, value, project, settings)
    }
    settings.config_override = PluginSettingsService.unredact(valid_config)

    async def test_stream(tap_stream) -> bool:
        while not tap_stream.at_eof():
            message = await tap_stream.readline()
            json_dict = json.loads(message)
            if json_dict["type"] == "RECORD":
                return True

        return False

    async def test_extractor():
        process = None
        try:
            invoker = invoker_factory(
                project,
                plugin,
                plugins_service=plugins_service,
                plugin_settings_service=settings,
            )
            with invoker.prepared(db.session):
                process = await invoker.invoke_async(
                    stdout=asyncio.subprocess.PIPE)
                return await test_stream(process.stdout)
        except Exception as err:
            logging.debug(err)
            # if anything happens, this is not successful
            return False
        finally:
            try:
                if process:
                    psutil.Process(process.pid).terminate()
            except Exception as err:
                logging.debug(err)

    loop = asyncio.get_event_loop()
    success = loop.run_until_complete(test_extractor())

    return jsonify({"is_success": success}), 200
Esempio n. 8
0
def get_plugin_configuration(plugin_ref) -> Response:
    """
    endpoint for getting a plugin's configuration
    """
    project = Project.find()
    settings = PluginSettingsService(project)
    config = flatten(settings.as_config(db.session, plugin_ref, redacted=True),
                     reducer="dot")

    return jsonify({
        # freeze the keys because they are used for lookups
        "config": freeze_keys(config),
        "settings": settings.get_definition(plugin_ref).settings,
    })
Esempio n. 9
0
    def plugin_config(self, project):
        _, Session = project_engine(project)
        session = Session()
        try:
            plugin_settings_service = PluginSettingsService(project)
            raw_config = plugin_settings_service.as_config(session, self)
        finally:
            session.close()

        config = {}
        for key, value in raw_config.items():
            nest(config, key, value, maxsplit=1)

        return config
Esempio n. 10
0
def config(ctx, project, plugin_type, plugin_name, format, extras):
    plugin_type = PluginType.from_cli_argument(
        plugin_type) if plugin_type else None

    plugins_service = ProjectPluginsService(project)

    try:
        plugin = plugins_service.find_plugin(plugin_name,
                                             plugin_type=plugin_type,
                                             configurable=True)
    except PluginNotFoundError:
        if plugin_name == "meltano":
            plugin = None
        else:
            raise

    _, Session = project_engine(project)
    session = Session()
    try:
        if plugin:
            settings = PluginSettingsService(project,
                                             plugin,
                                             plugins_service=plugins_service)
        else:
            settings = ProjectSettingsService(
                project, config_service=plugins_service.config_service)

        ctx.obj["settings"] = settings
        ctx.obj["session"] = session

        if ctx.invoked_subcommand is None:
            if format == "json":
                process = extras is not True
                config = settings.as_dict(extras=extras,
                                          process=process,
                                          session=session)
                print(json.dumps(config, indent=2))
            elif format == "env":
                env = settings.as_env(extras=extras, session=session)

                with tempfile.NamedTemporaryFile() as temp_dotenv:
                    path = temp_dotenv.name
                    for key, value in env.items():
                        dotenv.set_key(path, key, value)

                    dotenv_content = Path(temp_dotenv.name).read_text()

                print(dotenv_content, end="")
    finally:
        session.close()
Esempio n. 11
0
def save_plugin_configuration(plugin_ref) -> Response:
    """
    Endpoint for persisting a plugin configuration
    """
    project = Project.find()
    payload = request.get_json()
    plugin = ConfigService(project).get_plugin(plugin_ref)

    # TODO iterate pipelines and save each, also set this connector's profile (reuse `pipelineInFocusIndex`?)

    settings = PluginSettingsService(project, show_hidden=False)

    for profile in payload:
        # select the correct profile
        name = profile["name"]
        plugin.use_profile(plugin.get_profile(name))

        for name, value in profile["config"].items():
            if not validate_plugin_config(plugin, name, value, project,
                                          settings):
                continue

            if value == "":
                settings.unset(db.session, plugin, name)
            else:
                settings.set(db.session, plugin, name, value)

    profiles = settings.profiles_with_config(db.session, plugin, redacted=True)
    for profile in profiles:
        freeze_profile_config_keys(profile)

    return jsonify(profiles)
Esempio n. 12
0
def config(ctx, project, plugin_name, format):
    config = ConfigService(project)
    plugin = config.find_plugin(plugin_name)

    _, Session = project_engine(project)
    session = Session()
    settings = PluginSettingsService(project)

    ctx.obj["settings"] = settings
    ctx.obj["plugin"] = plugin
    ctx.obj["session"] = session

    if ctx.invoked_subcommand is None:
        if format == "json":
            print(settings.as_config(session, plugin))

        if format == "env":
            for env, value in settings.as_env(session, plugin).items():
                print(f"{env}={value}")
Esempio n. 13
0
def save_plugin_configuration(plugin_ref) -> Response:
    """
    Endpoint for persisting a plugin configuration
    """
    project = Project.find()
    payload = request.get_json()
    plugins_service = ProjectPluginsService(project)
    plugin = plugins_service.get_plugin(plugin_ref)

    settings = PluginSettingsService(project,
                                     plugin,
                                     plugins_service=plugins_service,
                                     show_hidden=False)

    config = payload.get("config", {})
    for name, value in config.items():
        if not validate_plugin_config(plugin, name, value, project, settings):
            continue

        if value == "":
            settings.unset(name, session=db.session)
        else:
            settings.set(name, value, session=db.session)

    return jsonify(get_config_with_metadata(settings))
Esempio n. 14
0
    def plugin_context(self, plugin_ref: PluginRef, env={}, config={}):
        plugin = self.plugins_service.get_plugin(plugin_ref)

        return PluginContext(
            plugin=plugin,
            settings_service=PluginSettingsService(
                self.project,
                plugin,
                plugins_service=self.plugins_service,
                env_override=env,
                config_override=config,
            ),
            session=self._session,
        )
Esempio n. 15
0
def add_plugin_configuration_profile(plugin_ref) -> Response:
    """
    Endpoint for adding a configuration profile to a plugin
    """
    payload = request.get_json()
    project = Project.find()
    config = ConfigService(project)
    plugin = config.get_plugin(plugin_ref)
    settings = PluginSettingsService(project)

    # create the new profile for this plugin
    name = payload["name"]
    profile = plugin.add_profile(slugify(name), label=name)

    config.update_plugin(plugin)

    profile_config = settings.profile_with_config(db.session,
                                                  plugin,
                                                  profile,
                                                  redacted=True)
    freeze_profile_config_keys(profile_config)

    return jsonify(profile_config)
Esempio n. 16
0
    def run(self, session, dry_run=False, models=None):
        # we should probably refactor this part to have an ELTContext object already
        # filled with the each plugins' configuration so we don't have to query
        # multiple times for the same data.

        settings_service = PluginSettingsService(self.project)
        try:
            load = self.connection_service.load_params()
            analyze = self.connection_service.analyze_params()
            env = {
                # inject the inferred 'schemas' from the ELTContext
                "MELTANO_LOAD_SCHEMA": load["schema"],
                "MELTANO_ANALYZE_SCHEMA": analyze["schema"],
                "DBT_TARGET": self.connection_service.dialect,
                # inject the extractor & loader configuration as ENV variables.
                # that means dbt will have access to all the configuration of
                # the extractor and loader
                **settings_service.as_env(session, self.context.extractor.ref),
                **settings_service.as_env(session, self.context.loader.ref),
            }
        except Exception as e:
            logging.debug("Could not inject environment to dbt.")
            logging.debug(
                f"Could not hydrate ENV from the EltContext: {str(e)}")
            raise e

        # Get an asyncio event loop and use it to run the dbt commands
        loop = asyncio.get_event_loop()
        loop.run_until_complete(self.dbt_service.deps())

        if models is not None:
            models = models.replace("-", "_")

        if dry_run:
            loop.run_until_complete(self.dbt_service.compile(models, env=env))
        else:
            loop.run_until_complete(self.dbt_service.run(models, env=env))
Esempio n. 17
0
def validate_plugin_config(plugin: PluginRef, name, value, project: Project,
                           settings: PluginSettingsService):
    setting_def = settings.find_setting(name)
    # we want to prevent the edition of protected settings from the UI
    if setting_def.protected:
        logging.warning("Cannot set a 'protected' setting externally.")
        return False

    if setting_def.kind == "file" and value and value != "":
        uploads_directory = project.extract_dir(plugin.name)
        resolved_file_path = project.root_dir(value).resolve()
        if not str(resolved_file_path).startswith(
                str(uploads_directory) + "/"):
            logging.warning(
                "Cannot set a file configuration to a path outside the project directory"
            )
            return False

    old_value, metadata = settings.get_with_metadata(name, session=db.session)
    if not metadata["overwritable"]:
        logging.warning("Cannot overwrite this setting.")
        return False

    return True
Esempio n. 18
0
def save_plugin_configuration(plugin_ref) -> Response:
    """
    endpoint for persisting a plugin configuration
    """
    project = Project.find()
    payload = request.get_json()

    settings = PluginSettingsService(project)
    for name, value in payload.items():
        # we want to prevent the edition of protected settings from the UI
        if settings.find_setting(plugin_ref, name).get("protected"):
            logging.warning(
                "Cannot set a 'protected' configuration externally.")
            continue

        if value == "":
            settings.unset(db.session, plugin_ref, name)
        else:
            settings.set(db.session, plugin_ref, name, value)

    return jsonify(settings.as_config(db.session, plugin_ref, redacted=True))
Esempio n. 19
0
 def __init__(
     self,
     project: Project,
     config_service: ConfigService = None,
     plugin_settings_service: PluginSettingsService = None,
     plugin_discovery_service: PluginDiscoveryService = None,
 ):
     self.project = project
     self.config_service = config_service or ConfigService(project)
     self.plugin_discovery_service = (
         plugin_discovery_service
         or PluginDiscoveryService(project, config_service=config_service))
     self.plugin_settings_service = plugin_settings_service or PluginSettingsService(
         project,
         config_service=config_service,
         plugin_discovery_service=plugin_discovery_service,
     )
     self._extractor = None
     self._loader = None
     self._job = None
Esempio n. 20
0
 async def test_extractor(config={}):
     try:
         settings_service = settings.with_config_override(
             PluginSettingsService.unredact(config))
         invoker = invoker_factory(
             project,
             plugin,
             prepare_with_session=db.session,
             plugin_settings_service=settings_service,
         )
         process = await invoker.invoke_async(stdout=asyncio.subprocess.PIPE
                                              )
         return await test_stream(process.stdout)
     except Exception as err:
         # if anything happens, this is not successful
         return False
     finally:
         try:
             if process:
                 psutil.Process(process.pid).terminate()
         except Exception as err:
             logging.debug(err)
Esempio n. 21
0
 def current_select(self):
     plugin_settings_service = PluginSettingsService(
         self.project, self.extractor, plugins_service=self.plugins_service)
     return plugin_settings_service.get("_select")
Esempio n. 22
0
def plugin_settings_service(project, config_service, plugin_discovery_service):
    return PluginSettingsService(
        project,
        config_service=config_service,
        plugin_discovery_service=plugin_discovery_service,
    )
Esempio n. 23
0
    def after_install(self, project, args=[]):
        _, Session = project_engine(project)
        session = Session()

        plugin_config_service = PluginConfigService(
            self,
            config_dir=project.plugin_dir(self),
            run_dir=project.run_dir(self.name),
        )

        plugin_settings_service = PluginSettingsService(project)
        airflow_cfg_path = plugin_config_service.run_dir.joinpath(
            "airflow.cfg")
        stub_path = plugin_config_service.config_dir.joinpath("airflow.cfg")
        invoker = invoker_factory(
            project,
            self,
            prepare_with_session=session,
            plugin_config_service=plugin_config_service,
        )

        try:
            # generate the default `airflow.cfg`
            handle = invoker.invoke("--help",
                                    stdout=subprocess.DEVNULL,
                                    stderr=subprocess.DEVNULL)
            handle.wait()
            logging.debug(f"Generated default '{str(airflow_cfg_path)}'")

            # move it to the config dir
            shutil.move(airflow_cfg_path, stub_path)
            airflow_cfg_path = stub_path
            logging.debug(f"Moved to '{str(stub_path)}'")

            # open the configuration and update it
            # now we let's update the config to use our stubs
            airflow_cfg = configparser.ConfigParser()

            with airflow_cfg_path.open() as cfg:
                airflow_cfg.read_file(cfg)
                logging.debug(f"Loaded '{str(airflow_cfg_path)}'")

            config = {}
            for key, value in plugin_settings_service.as_config(session,
                                                                self).items():
                nest(config, key, str(value))

            for section, cfg in config.items():
                airflow_cfg[section].update(cfg)
                logging.debug(f"\tUpdated section [{section}] with {cfg}")

            with airflow_cfg_path.open("w") as cfg:
                airflow_cfg.write(cfg)
                logging.debug(f"Saved '{str(airflow_cfg_path)}'")

            # we've changed the configuration here, so we need to call
            # prepare again on the invoker so it re-reads the configuration
            # for the Airflow plugin
            invoker.prepare(session)
            handle = invoker.invoke(
                "initdb",
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                universal_newlines=True,
            )
            initdb = handle.wait()

            if initdb:
                raise SubprocessError("airflow initdb failed", handle)

            logging.debug(f"Completed `airflow initdb`")
        finally:
            session.close()
Esempio n. 24
0
 def _factory(plugin, **kwargs):
     return PluginSettingsService(
         project, plugin, plugins_service=project_plugins_service, **kwargs
     )