diff --git a/Makefile.local b/Makefile.local --- a/Makefile.local +++ b/Makefile.local @@ -1,6 +1,6 @@ -FLAKEFLAGS='--exclude=swh/manage.py,swh/deposit/settings.py,swh/deposit/migrations/' +FLAKEFLAGS='--exclude=swh/deposit/manage.py,swh/deposit/settings.py,swh/deposit/migrations/' -MANAGE=python3 -m swh.manage +MANAGE=python3 -m swh.deposit.manage db-drop: dropdb swh-deposit-dev || return 0 @@ -27,4 +27,4 @@ gunicorn3 -b 127.0.0.1:5006 swh.deposit.wsgi test: - ./swh/manage.py test + ./swh/deposit/manage.py test diff --git a/bin/swh-deposit b/bin/swh-deposit deleted file mode 100755 --- a/bin/swh-deposit +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -# Use: ./swh-deposit --help -# -# Documentation: https://docs.softwareheritage.org/devel/swh-deposit/getting-started.html - -python3 -m swh.deposit.client.cli $@ diff --git a/docs/dev-info.rst b/docs/dev-info.rst --- a/docs/dev-info.rst +++ b/docs/dev-info.rst @@ -110,8 +110,8 @@ Production-like environment --------------------------- -Production-like environment needs two configuration files to work -properly. +Production-like environment needs additional section in the +configuration file to work properly. This is more close to what's actually running in production. @@ -119,13 +119,14 @@ ~~~~~~~~~~~~~ This expects the same file describes in the previous chapter. Plus, an -additional private **settings.yml** file containing secret information -that is not in the source code repository. +additional private section file containing private information that is +not in the source code repository. **``{/etc/softwareheritage | ~/.config/swh | ~/.swh}``/deposit/private.yml**: .. code:: yaml + private: secret_key: production-local db: name: swh-deposit-dev @@ -134,13 +135,14 @@ .. code:: yaml + private: secret_key: production-secret-key - db: - name: swh-deposit-dev - host: db - port: 5467 - user: user - password: user-password + db: + name: swh-deposit-dev + host: db + port: 5467 + user: user + password: user-password Run ~~~ diff --git a/docs/sys-info.rst b/docs/sys-info.rst --- a/docs/sys-info.rst +++ b/docs/sys-info.rst @@ -1,4 +1,4 @@ -Deployment of the swh-deposit +Deployment of the swh-deposit ============================= As usual, the debian packaged is created and uploaded to the swh debian @@ -9,7 +9,7 @@ ----------------------------------------------------------- This is defined through the packaged ``swh.deposit.settings.production`` -module and the expected **/etc/softwareheritage/deposit/private.yml**. +module and the expected **/etc/softwareheritage/deposit/server.yml**. As usual, the expected configuration files are deployed through our puppet manifest (cf. puppet-environment/swh-site, @@ -27,10 +27,12 @@ .. code:: shell - sudo django-admin loaddata --settings=swh.deposit.settings.production deposit_data + sudo django-admin loaddata \ + --settings=swh.deposit.settings.production deposit_data -This adds the minimal: - deposit request type 'archive' and 'metadata' - -'hal' collection +This adds the minimal: +- deposit request type 'archive' and 'metadata' +- 'hal' collection Note: swh.deposit.fixtures.deposit\_data is packaged @@ -39,7 +41,9 @@ .. code:: shell - python3 -m swh.deposit.create_user --platform production \ + SWH_CONFIG_FILENAME=/etc/softwareheritage/deposit/server.yml \ + swh-deposit --platform production \ + user create \ --collection \ --username \ --password @@ -48,4 +52,9 @@ ````. The password will be used for the authentication access to the deposit api. -Note: This creation procedure needs to be improved. +Note: + - If the collection does not exist, it is created alongside. + - The password is plain text but stored encrypted (so yes, for now + we know the user's password) + - A production requirement for the cli to work is to set the + SWH_CONFIG_FILENAME environment variable diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -44,13 +44,17 @@ author_email='swh-devel@inria.fr', url='https://forge.softwareheritage.org/source/swh-deposit/', packages=find_packages(), - scripts=['bin/swh-deposit'], # scripts to package install_requires=parse_requirements() + parse_requirements('swh'), tests_require=parse_requirements('test'), setup_requires=['vcversioner'], extras_require={'testing': parse_requirements('test')}, vcversioner={}, include_package_data=True, + entry_points=''' + [console_scripts] + swh-deposit=swh.deposit.cli:main + swh-deposit-client=swh.deposit.client.cli:main + ''', classifiers=[ "Programming Language :: Python :: 3", "Intended Audience :: Developers", diff --git a/swh/deposit/cli.py b/swh/deposit/cli.py new file mode 100644 --- /dev/null +++ b/swh/deposit/cli.py @@ -0,0 +1,158 @@ +# Copyright (C) 2017-2019 The Software Heritage developers +# See the AUTHORS file at the top-level directory of this distribution +# License: GNU General Public License version 3, or any later version +# See top-level LICENSE file for more information + +import click + +from swh.deposit.config import setup_django_for + + +CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help']) + + +@click.group(context_settings=CONTEXT_SETTINGS) +@click.option('--platform', default='development', + type=click.Choice(['development', 'production']), + help='development or production platform') +@click.pass_context +def cli(ctx, platform): + setup_django_for(platform) + + +@cli.group('user') +@click.pass_context +def user(ctx): + """Manipulate user.""" + pass + + +def _create_collection(name): + """Create the collection with name if it does not exist. + + Args: + name (str): collection's name + + Returns: + collection (DepositCollection): the existing collection object + (created or not) + + """ + # to avoid loading too early django namespaces + from swh.deposit.models import DepositCollection + + try: + collection = DepositCollection.objects.get(name=name) + click.echo('Collection %s exists, nothing to do.' % name) + except DepositCollection.DoesNotExist: + click.echo('Create new collection %s' % name) + collection = DepositCollection.objects.create(name=name) + click.echo('Collection %s created' % name) + return collection + + +@user.command('create') +@click.option('--username', required=True, help="User's name") +@click.option('--password', required=True, + help="Desired user's password (plain).") +@click.option('--firstname', default='', help="User's first name") +@click.option('--lastname', default='', help="User's last name") +@click.option('--email', default='', help="User's email") +@click.option('--collection', help="User's collection") +@click.pass_context +def user_create(ctx, username, password, firstname, lastname, email, + collection): + """Create a user with some needed information (password, collection) + + If the collection does not exist, the collection is then created + alongside. + + The password is stored encrypted using django's utilies. + + """ + # to avoid loading too early django namespaces + from swh.deposit.models import DepositClient + + click.echo('collection: %s' % collection) + # create the collection if it does not exist + collection = _create_collection(collection) + + # user create/update + try: + user = DepositClient.objects.get(username=username) + click.echo('User %s exists, updating information.' % user) + user.set_password(password) + except DepositClient.DoesNotExist: + click.echo('Create new user %s' % username) + user = DepositClient.objects.create_user( + username=username, + password=password) + + user.collections = [collection.id] + user.first_name = firstname + user.last_name = lastname + user.email = email + user.is_active = True + user.save() + + click.echo('Information registered for user %s' % user) + + +@user.command('list') +@click.pass_context +def user_list(ctx): + """List existing users. + + This entrypoint is not paginated yet as there is not a lot of + entry. + + """ + # to avoid loading too early django namespaces + from swh.deposit.models import DepositClient + users = DepositClient.objects.all() + if not users: + output = 'Empty user list' + else: + output = '\n'.join((user.username for user in users)) + click.echo(output) + + +@cli.group('collection') +@click.pass_context +def collection(ctx): + """Manipulate collection.""" + pass + + +@collection.command('create') +@click.option('--name', required=True, help="Collection's name") +@click.pass_context +def collection_create(ctx, name): + _create_collection(name) + + +@collection.command('list') +@click.pass_context +def collection_list(ctx): + """List existing collections. + + This entrypoint is not paginated yet as there is not a lot of + entry. + + """ + # to avoid loading too early django namespaces + from swh.deposit.models import DepositCollection + collections = DepositCollection.objects.all() + if not collections: + output = 'Empty collection list' + else: + output = '\n'.join((col.name for col in collections)) + click.echo(output) + + +def main(): + return cli(auto_envvar_prefix='SWH_DEPOSIT') + + +if __name__ == '__main__': + main() diff --git a/swh/deposit/create_user.py b/swh/deposit/create_user.py deleted file mode 100755 --- a/swh/deposit/create_user.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (C) 2017 The Software Heritage developers -# See the AUTHORS file at the top-level directory of this distribution -# License: GNU General Public License version 3, or any later version -# See top-level LICENSE file for more information - -import click - -from swh.deposit.config import setup_django_for - - -@click.command( - help='Create a user with some needed information (password, collection)') -@click.option('--platform', default='development', - help='development or production platform') -@click.option('--username', required=True, help="User's name") -@click.option('--password', required=True, help="Desired user's password.") -@click.option('--firstname', default='', help="User's first name") -@click.option('--lastname', default='', help="User's last name") -@click.option('--email', default='', help="User's email") -@click.option('--collection', help="User's collection") -def main(platform, username, password, firstname, lastname, email, collection): - setup_django_for(platform) - - from swh.deposit.models import DepositClient, DepositCollection - - try: - collection = DepositCollection.objects.get(name=collection) - except DepositCollection.DoesNotExist: - raise ValueError( - 'Collection %s does not exist, skipping' % collection) - - # user create/update - try: - user = DepositClient.objects.get(username=username) - print('User %s exists, updating information.' % user) - user.set_password(password) - except DepositClient.DoesNotExist: - print('Create new user %s' % username) - user = DepositClient.objects.create_user( - username=username, - password=password) - - user.collections = [collection.id] - user.first_name = firstname - user.last_name = lastname - user.email = email - user.is_active = True - user.save() - - print('Information registered for user %s' % user) - - -if __name__ == '__main__': - main() diff --git a/swh/manage.py b/swh/deposit/manage.py rename from swh/manage.py rename to swh/deposit/manage.py --- a/swh/manage.py +++ b/swh/deposit/manage.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (C) 2017 The Software Heritage developers +# Copyright (C) 2017-2019 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information diff --git a/swh/deposit/settings/production.py b/swh/deposit/settings/production.py --- a/swh/deposit/settings/production.py +++ b/swh/deposit/settings/production.py @@ -25,6 +25,10 @@ # and check the required setup is ok # If not raise an error explaining the errors config_file = os.environ.get('SWH_CONFIG_FILENAME') +if not config_file: + raise ValueError('Production: SWH_CONFIG_FILENANE must be set to the' + ' configuration file needed!') + if not os.path.exists(config_file): raise ValueError('Production: configuration file %s does not exist!' % ( config_file, ))