Skip to content

Commit

Permalink
730 local project configuration (#777)
Browse files Browse the repository at this point in the history
* feat: allow not committing if empty

* feat: local project configuration

* feat: commit when there is a change

* refactor: move cli config command out of core

* refactor: remove renku --config option
  • Loading branch information
m-alisafaee authored Nov 4, 2019
1 parent 5b541a0 commit d54e877
Show file tree
Hide file tree
Showing 11 changed files with 417 additions and 174 deletions.
15 changes: 5 additions & 10 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,19 @@ def instance_path(renku_path, monkeypatch):


@pytest.fixture()
def runner(monkeypatch):
def runner():
"""Create a runner on isolated filesystem."""
from renku.core.management.config import RENKU_HOME
monkeypatch.setenv('RENKU_CONFIG', RENKU_HOME)
return CliRunner()


@pytest.fixture
def config_dir(monkeypatch, tmpdir_factory):
def global_config_dir(monkeypatch, tmpdir_factory):
"""Create a temporary renku config directory."""
from renku.core.management.config import ConfigManagerMixin

with monkeypatch.context() as m:
home_dir = tmpdir_factory.mktemp('fake_home')
conf_path = home_dir / 'renku.ini'
m.setattr(ConfigManagerMixin, 'config_path', conf_path)
home_dir = tmpdir_factory.mktemp('fake_home').strpath
m.setattr(ConfigManagerMixin, 'global_config_dir', home_dir)

yield m

Expand Down Expand Up @@ -124,10 +121,8 @@ def generate(args=('update', ), cwd=None, **streams):


@pytest.fixture()
def isolated_runner(monkeypatch):
def isolated_runner():
"""Create a runner on isolated filesystem."""
from renku.core.management.config import RENKU_HOME
monkeypatch.setenv('RENKU_CONFIG', RENKU_HOME)
runner_ = CliRunner()
with runner_.isolated_filesystem():
yield runner_
Expand Down
41 changes: 14 additions & 27 deletions renku/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@
Options:
--version Print version number.
--config PATH Location of client config files.
--config-path Print application config path.
--global-config-path Print global application's config path.
--install-completion Install completion for the current shell.
--path <path> Location of a Renku repository.
[default: (dynamic)]
Expand All @@ -61,18 +60,7 @@
``C:\Users\<user>\AppData\Roaming\Renku``
If in doubt where to look for the configuration file, you can display its path
by running ``renku --config-path``.
You can specify a different location via the ``RENKU_CONFIG`` environment
variable or the ``--config`` command line option. If both are specified, then
the ``--config`` option value is used. For example:
.. code-block:: console
$ renku --config ~/renku/config/ init
instructs Renku to store the configuration files in your ``~/renku/config/``
directory when running the ``init`` command.
by running ``renku --global-config-path``.
"""

import uuid
Expand Down Expand Up @@ -102,8 +90,7 @@
option_use_external_storage
from renku.core.commands.version import check_version, print_version
from renku.core.management.client import LocalClient
from renku.core.management.config import RENKU_HOME, default_config_dir, \
print_app_config_path
from renku.core.management.config import ConfigManagerMixin, RENKU_HOME
from renku.core.management.repository import default_path

#: Monkeypatch Click application.
Expand All @@ -118,6 +105,14 @@ def _uuid_representer(dumper, data):
yaml.add_representer(uuid.UUID, _uuid_representer)


def print_global_config_path(ctx, param, value):
"""Print global application's config path."""
if not value or ctx.resilient_parsing:
return
click.echo(ConfigManagerMixin().global_config_path)
ctx.exit()


@click.group(
cls=IssueFromTraceback,
context_settings={
Expand All @@ -134,20 +129,12 @@ def _uuid_representer(dumper, data):
help=print_version.__doc__
)
@click.option(
'--config',
envvar='RENKU_CONFIG',
default=default_config_dir,
type=click.Path(),
expose_value=False,
help='Location of client config files.'
)
@click.option(
'--config-path',
'--global-config-path',
is_flag=True,
callback=print_app_config_path,
callback=print_global_config_path,
expose_value=False,
is_eager=True,
help=print_app_config_path.__doc__
help=print_global_config_path.__doc__
)
@click.option(
'--install-completion',
Expand Down
65 changes: 56 additions & 9 deletions renku/cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,79 @@
$ renku config registry https://registry.gitlab.com/demo/demo
By default, configuration is stored locally in the project's directory. Use
``--global`` option to store configuration for all projects in your home
directory.
Remove values
~~~~~~~~~~~~~
To remove a specific key from configuration use:
.. code-block:: console
$ renku config --remove registry
By default, only local configuration is searched for removal. Use ``--global``
option to remove a global configuration value.
Query values
~~~~~~~~~~~~
You display a previously set value with:
You can display all configuration values with:
.. code-block:: console
$ renku config
Both local and global configuration files are read. Values in local
configuration take precedence over global values. Use ``--local`` or
``--global`` flag to read corresponding configuration only.
You can provide a KEY to display only its value:
.. code-block:: console
$ renku config registry
https://registry.gitlab.com/demo/demo
"""
import click

from renku.core.commands.config import update_config
from renku.core import errors
from renku.core.commands.config import read_config, update_config


@click.command()
@click.argument('key', required=True)
@click.argument('key', required=False, default=None)
@click.argument('value', required=False, default=None)
@click.option('--remove', is_flag=True, help='Remove specified key.')
@click.option(
'--local',
'local_only',
is_flag=True,
help='Read/store from/to local configuration only.'
)
@click.option(
'--global',
'is_global',
'global_only',
is_flag=True,
help='Store to global configuration.'
help='Read/store from/to global configuration only.'
)
def config(key, value, is_global):
def config(key, value, remove, local_only, global_only):
"""Manage configuration options."""
updated = update_config(key, value, is_global)
click.secho(updated)
is_write = value is not None

if is_write and remove:
raise errors.UsageError('Cannot remove and set at the same time.')
if remove and not key:
raise errors.UsageError('KEY is missing.')
if local_only and global_only:
raise errors.UsageError('Cannot use --local and --global together.')

if remove:
update_config(key, remove=remove, global_only=global_only)
elif is_write:
update_config(key, value=value, global_only=global_only)
else:
value = read_config(key, local_only, global_only)
click.secho(value)
9 changes: 6 additions & 3 deletions renku/core/commands/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ def pass_local_client(
commit=None,
commit_only=None,
ignore_std_streams=True,
allow_empty=True,
commit_empty=True,
raise_if_empty=False,
lock=None,
):
"""Pass client from the current context to the decorated command."""
Expand All @@ -56,7 +57,8 @@ def pass_local_client(
commit=commit,
commit_only=commit_only,
ignore_std_streams=ignore_std_streams,
allow_empty=allow_empty,
commit_empty=commit_empty,
raise_if_empty=raise_if_empty,
lock=lock,
)

Expand All @@ -75,7 +77,8 @@ def new_func(*args, **kwargs):
commit=commit,
commit_only=commit_only,
ignore_std_streams=ignore_std_streams,
allow_empty=allow_empty,
commit_empty=commit_empty,
raise_if_empty=raise_if_empty,
)
stack.enter_context(transaction)

Expand Down
54 changes: 37 additions & 17 deletions renku/core/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""Get and set Renku repository or global options."""
import configparser

from renku.core.management.config import get_config
from renku.core import errors
from renku.core.management.config import CONFIG_LOCAL_PATH

from .client import pass_local_client

Expand All @@ -31,18 +30,39 @@ def _split_section_and_key(key):
return 'renku', key


@pass_local_client
def update_config(client, key, value, is_global):
"""Manage configuration options."""
write_op = value is not None
config_ = get_config(client, write_op, is_global)
if write_op:
with config_:
section, config_key = _split_section_and_key(key)
config_.set_value(section, config_key, value)
return value
@pass_local_client(
clean=False,
commit=True,
commit_only=CONFIG_LOCAL_PATH,
commit_empty=False
)
def update_config(client, key, *, value=None, remove=False, global_only=False):
"""Add, update, or remove configuration values."""
section, section_key = _split_section_and_key(key)
if remove:
value = client.remove_value(
section, section_key, global_only=global_only
)
if value is None:
raise errors.ParameterError('Key "{}" not found.'.format(key))
else:
try:
return config_.get_value(*_split_section_and_key(key))
except configparser.NoSectionError:
raise KeyError('Requested configuration not found')
client.set_value(section, section_key, value, global_only=global_only)
return value


@pass_local_client
def read_config(client, key, local_only, global_only):
"""Read configuration."""
if key:
section, section_key = _split_section_and_key(key)
value = client.get_value(
section,
section_key,
local_only=local_only,
global_only=global_only
)
if value is None:
raise errors.ParameterError('Key "{}" not found.'.format(key))
return value

return client.get_config(local_only=local_only, global_only=global_only)
8 changes: 6 additions & 2 deletions renku/core/commands/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,14 +363,18 @@ def export_dataset(
if access_token is None or len(access_token) == 0:
raise InvalidAccessToken()

client.set_value(provider_id, config_key_secret, access_token)
client.set_value(
provider_id, config_key_secret, access_token, global_only=True
)
exporter.set_access_token(access_token)

try:
destination = exporter.export(publish, selected_tag)
except HTTPError as e:
if 'unauthorized' in str(e):
client.remove_value(provider_id, config_key_secret)
client.remove_value(
provider_id, config_key_secret, global_only=True
)

raise

Expand Down
4 changes: 1 addition & 3 deletions renku/core/commands/migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@


@pass_local_client(
clean=True,
commit=True,
allow_empty=False,
clean=True, commit=True, commit_empty=False, raise_if_empty=True
)
def migrate_datasets(client):
"""Migrate dataset metadata."""
Expand Down
Loading

0 comments on commit d54e877

Please sign in to comment.