Skip to content

Commit

Permalink
Add an option to create (and delete) a bootstrap user as part of the …
Browse files Browse the repository at this point in the history
…tool installation process
  • Loading branch information
afgane committed Aug 14, 2015
1 parent ab64c06 commit b9b006f
Show file tree
Hide file tree
Showing 6 changed files with 306 additions and 10 deletions.
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,26 @@ None
Role variables
--------------
### Required variables ###
Only one of the two variables is requried (if both are set, the API key
takes precedence and a bootstrap user is not created):
- `galaxy_tools_api_key`: the Galaxy API key for an admin user on the target
Galaxy instance
Galaxy instance (not required if the bootstrap user is being created)
- `galaxy_tools_admin_user_password`: a password for the Galaxy bootstrap user
(required only if `galaxy_install_bootstrap_user` variable is set)

### Optional variables ###
- `galaxy_tools_instance_url`: (default `127.0.0.1:8080`) a URL or an IP address
for the Galaxy instance where the tools are to be installed
- `galaxy_tools_tool_list_file`: (default `files/tool_list.yaml`) the file
containing all the tools to be installed. See `files/tool_list.yaml.sample`
file for more about the format requirements of this file.
- `galaxy_tools_base_dir`: (default: `/tmp`) the system path from where this
role will be run
See `defaults/main.yml` for the available variables and their defaults.

### Control flow variables ###
The following variables can be set to either `yes` or `no` to indicate if the
given part of the role should be executed:

- `galaxy_tools_install_tools`: (default: `yes`) whether or not to run the
tools installation script
- `galaxy_tools_create_bootstrap_user`: (default: `no`) whether or not to
create a bootstrap Galaxy admin user
- `galaxy_tools_delete_bootstrap_user`: (default: `no`) whether or not to
delete a bootstrap Galaxy admin user

Example playbook
----------------
Expand Down
42 changes: 41 additions & 1 deletion defaults/main.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,47 @@
---

galaxy_tools_install_tools: yes
galaxy_tools_create_bootstrap_user: no
galaxy_tools_delete_bootstrap_user: no

galaxy_tools_instance_url: 127.0.0.1:8080
# A URL or an IP address for the Galaxy instance where the tools are to be
# installed
galaxy_tools_galaxy_instance_url: 127.0.0.1:8080

# A file containing all the tools to be installed. See `files/tool_list.yaml.sample`
# file for more about the format requirements of this file.
galaxy_tools_tool_list_file: tool_list.yaml

# A system path from where this role will be run
galaxy_tools_base_dir: /tmp

# Blank variable to make sure it's defined
galaxy_tools_api_key: ''


## Variables below are used only if the galaxy_install_bootstrap_user var is set

# An email address with which the Galaxy bootstrap user will be created
galaxy_tools_admin_user: [email protected]

# Name of Galaxy's PID file
galaxy_tools_pid_file_name: main.pid

# Name of Galaxy's log file
galaxy_tools_log_file_name: main.log


## Below variable names are used in several other roles that compose Galaxy's
## playbooks and are hence scoped differently

# User name for the system galaxy user
galaxy_user_name: galaxy

# A path where Galaxy is installed
galaxy_server_dir: /mnt/galaxy/galaxy-app

# A system path where a virtualend for Galaxy is installed
galaxy_venv_dir: "{{ galaxy_server_dir }}/.venv"

# A system path for Galaxy's main configuration file
galaxy_config_file: "{{ galaxy_server_dir }}/config/galaxy.ini"
209 changes: 209 additions & 0 deletions files/manage_bootstrap_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
#!/usr/bin/env python

import ConfigParser
import logging
import os
import re
import sys

import argparse

new_path = [ os.path.join( os.getcwd(), "lib" ) ]
new_path.extend( sys.path[1:] )
sys.path = new_path

from galaxy import eggs
eggs.require( "SQLAlchemy >= 0.4" )
eggs.require( 'mercurial' )

import galaxy.config
from galaxy.web import security
from galaxy.model import mapping

log = logging.getLogger( __name__ )


VALID_PUBLICNAME_RE = re.compile( "^[a-z0-9\-]+$" )
VALID_EMAIL_RE = re.compile( "[^@]+@[^@]+\.[^@]+" )


class BootstrapGalaxyApplication( object ):
"""
Creates a basic Tool Shed application in order to discover the database
connection and use SQL to create a user and API key.
"""

def __init__( self, config ):
self.config = config
if not self.config.database_connection:
self.config.database_connection = "sqlite:///%s?isolation_level=IMMEDIATE" % str( config.database )
# Setup the database engine and ORM
self.model = mapping.init( self.config.file_path,
self.config.database_connection,
engine_options={},
create_tables=False )
self.security = security.SecurityHelper( id_secret=self.config.id_secret )

@property
def sa_session( self ):
"""Returns a SQLAlchemy session."""
return self.model.context.current

def shutdown( self ):
pass


def create_api_key( app, user ):
api_key = app.security.get_new_guid()
new_key = app.model.APIKeys()
new_key.user_id = user.id
new_key.key = api_key
app.sa_session.add( new_key )
app.sa_session.flush()
return api_key


def get_or_create_api_key( app, user ):
if user.api_keys:
key = user.api_keys[0].key
else:
key = create_api_key( app, user )
return key


def create_user( app, email, password, username ):
if email and password and username:
invalid_message = validate( email, password, username )
if invalid_message:
log.error(invalid_message)
else:
user = app.model.User( email=email )
user.set_password_cleartext( password )
user.username = username
app.sa_session.add( user )
app.sa_session.flush()
app.model.security_agent.create_private_user_role( user )
return user
else:
log.error("Missing required values for email: {0}, password: {1}, "
"username: {2}".format(email, password, username))
return None


def get_or_create_user( app, email, password, username ):
user = app.sa_session.query( app.model.User ).filter(
app.model.User.table.c.username==username ).first()
if user:
return user
else:
return create_user( app, email, password, username )


def delete_user( app, username ):
user = app.sa_session.query( app.model.User ).filter(
app.model.User.table.c.username==username ).first()
app.sa_session.delete( user )
app.sa_session.flush()
return user


def validate( email, password, username ):
message = validate_email( email )
if not message:
message = validate_password( password )
if not message:
message = validate_publicname( username )
return message


def validate_email( email ):
"""Validates the email format."""
message = ''
if not( VALID_EMAIL_RE.match( email ) ):
message = "Please enter a real email address."
elif len( email ) > 255:
message = "Email address exceeds maximum allowable length."
return message


def validate_password( password ):
if len( password ) < 6:
return "Use a password of at least 6 characters"
return ''


def validate_publicname( username ):
"""Validates the public username."""
if len( username ) < 3:
return "Public name must be at least 3 characters in length"
if len( username ) > 255:
return "Public name cannot be more than 255 characters in length"
if not( VALID_PUBLICNAME_RE.match( username ) ):
return "Public name must contain only lower-case letters, numbers and '-'"
return ''


def get_bootstrap_app(ini_file):
config_parser = ConfigParser.ConfigParser( { 'here': os.getcwd() } )
config_parser.read( ini_file )
config_dict = {}
for key, value in config_parser.items( "app:main" ):
config_dict[ key ] = value
config = galaxy.config.Configuration( **config_dict )
app = BootstrapGalaxyApplication( config )
return app


def create_bootstrap_user(ini_file, username, user_email, password):
app = get_bootstrap_app(ini_file)
user = get_or_create_user( app, user_email, password, username )
if user is not None:
api_key = get_or_create_api_key( app, user )
print api_key
exit(0)
else:
log.error("Problem creating a new user: {0} and an associated API key."
.format(username))
exit(1)


def delete_bootstrap_user(ini_file, username):
app = get_bootstrap_app(ini_file)
user = delete_user( app, username )
if user is not None:
exit(0)
else:
log.error("Problem deleting user: {0}".format(username))
exit(1)

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="usage: python %prog [options]")
parser.add_argument("-c", "--config",
required=True,
help="Path to <galaxy .ini file>")

subparsers = parser.add_subparsers(title="action", help='create or delete bootstrap users')

parser_create = subparsers.add_parser('create', help='create a new bootstrap user')
parser_create.set_defaults(action='create')
parser_create.add_argument("-u", "--username",
default="cloud",
help="Username to create. Defaults to 'cloud'",)
parser_create.add_argument("-e", "--email",
default="[email protected]",
help="Email for user",)
parser_create.add_argument("-p", "--password",
default="password",
help="Password for user",)

parser_delete = subparsers.add_parser('delete', help='delete an existing bootstrap user')
parser_delete.set_defaults(action='delete')
parser_delete.add_argument("-u", "--username",
default="cloud",
help="Username to delete. Defaults to 'cloud'",)
args = parser.parse_args()

if args.action == "create":
create_bootstrap_user(args.config, args.username, args.email, args.password)
elif args.action == "delete":
delete_bootstrap_user(args.config, args.username)
25 changes: 25 additions & 0 deletions tasks/bootstrap_user.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---

- name: Copy the bootstrap user management script
copy: src=manage_bootstrap_user.py dest={{ galaxy_server_dir }}/manage_bootstrap_user.py owner={{ galaxy_user_name }}

- name: Create Galaxy bootstrap user
command: chdir={{ galaxy_server_dir }} {{ galaxy_venv_dir }}/bin/python manage_bootstrap_user.py -c {{ galaxy_config_file }} create -e {{ galaxy_tools_admin_user }} -p {{ galaxy_tools_admin_user_password }}
register: api_key
when: galaxy_tools_create_bootstrap_user

- set_fact: galaxy_tools_api_key="{{ api_key.stdout_lines[0] }}" #"
when: galaxy_tools_create_bootstrap_user

- name: Set bootstrap user as Galaxy Admin
lineinfile: dest={{ galaxy_config_file }} state=present insertafter="app:main" regexp="admin_users =" line="admin_users = {{ galaxy_tools_admin_user }}" #"
when: galaxy_tools_create_bootstrap_user

- name: Delete Galaxy bootstrap user
command: chdir={{ galaxy_server_dir }} {{ galaxy_venv_dir }}/bin/python manage_bootstrap_user.py -c {{ galaxy_config_file }} delete
when: galaxy_tools_delete_bootstrap_user

- include: restart_galaxy.yml

- name: Remove the bootstrap user management script
file: dest={{ galaxy_server_dir }}/manage_bootstrap_user.py state=absent
8 changes: 7 additions & 1 deletion tasks/main.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
---

- include: bootstrap_user.yml
when: galaxy_tools_create_bootstrap_user and not galaxy_tools_api_key

- include: tools.yml
when: galaxy_install_tools
when: galaxy_tools_install_tools

- include: bootstrap_user.yml
when: galaxy_tools_delete_bootstrap_user and not galaxy_tools_api_key
14 changes: 14 additions & 0 deletions tasks/restart_galaxy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---

- name: Stop Galaxy
shell: "{{ galaxy_server_dir }}/run.sh --pid-file={{ galaxy_tools_pid_file_name }} --log-file={{ galaxy_tools_log_file_name }} --stop-daemon"
ignore_errors: true

- name: Wait for Galaxy to stop
wait_for: port=8080 delay=5 state=stopped timeout=150

- name: Start Galaxy
shell: "{{ galaxy_server_dir }}/run.sh --pid-file={{ galaxy_tools_pid_file_name }} --log-file={{ galaxy_tools_log_file_name }} --daemon"

- name: Wait for Galaxy to start
wait_for: port=8080 delay=5 state=started timeout=150

0 comments on commit b9b006f

Please sign in to comment.