def to_dict(self): time_format = config.get('main', 'time_format') metadata = {} for m in self.metadata: metadata.update(m.to_dict()) return { 'id': str(self.id), 'packages': [p.to_dict() for p in self.packages], 'platforms': [platform.name for platform in self.platforms], 'references': string_to_list(self.references), 'stime': self.stime.strftime(config.get('main', 'time_format')) if self.stime else None, 'ftime': self.ftime.strftime(config.get('main', 'time_format')) if self.ftime else None, 'duration': self.duration.seconds if self.duration else None, 'metadata': metadata, 'user': self.user, 'team': self.team, 'notes': [n.content for n in self.notes], }
def to_dict(self): time_format = config.get('main', 'time_format') return { 'id': str(self.id), 'name': self.name, 'version': self.version, 'stime': self.stime.strftime(config.get('main', 'time_format')) if self.stime else None, 'ftime': self.ftime.strftime(config.get('main', 'time_format')) if self.ftime else None, 'duration': self.duration.seconds if self.duration else None, 'rollback': self.rollback, 'status': self.status, 'diff_url': self.diff_url, }
def verify_ldap_access(username, password): app.logger.debug("Verify ldap called") try: ldap_server = config.get('security', 'ldap_server') ldap_port = config.get('security', 'ldap_port') user_base_dn = config.get('security', 'user_base_dn') l = ldap.initialize('ldap://{}:{}'.format(ldap_server, ldap_port)) ldap_user = "******" % (username, user_base_dn) l.protocol_version = ldap.VERSION3 l.simple_bind_s(ldap_user, password) return True except ldap.LDAPError: return False
def test_import_param_package_stime(self): """ Test package stime attributes match """ self.assertEqual( self.package.stime.strftime(config.get("main", "time_format")), self.doc_dict[0]["packages"][0]["stime"] )
def _get_releases_time_filter(self, field, finished=False): """ Return releases given the time-based filter field Abstracts a bit of common code in the stime/ftime tests """ for _ in range(0, 3): if finished: self._create_finished_release() else: self._create_release() t_format = config.get('main', 'time_format') now = datetime.utcnow() yesterday = (now - timedelta(days=1)).strftime(t_format) tomorrow = (now + timedelta(days=1)).strftime(t_format) r_yesterday = self._get_releases( filters=['{}={}'.format(field, yesterday)] ) r_tomorrow = self._get_releases( filters=['{}={}'.format(field, tomorrow)] ) return r_yesterday, r_tomorrow
def _get_releases_time_filter(self, field, finished=False): """ Return releases given the time-based filter field Abstracts a bit of common code in the stime/ftime tests """ for _ in range(0, 3): if finished: self._create_finished_release() else: self._create_release() t_format = config.get('main', 'time_format') now = datetime.utcnow() yesterday = (now - timedelta(days=1)).strftime(t_format) tomorrow = (now + timedelta(days=1)).strftime(t_format) r_yesterday = self._get_releases( filters=['{}={}'.format(field, yesterday)], expected_status=None ) r_tomorrow = self._get_releases( filters=['{}={}'.format(field, tomorrow)], expected_status=None ) return r_yesterday, r_tomorrow
def test_import_param_package_stime(self): """ Test package stime attributes match """ self.assertEqual( self.package.stime.strftime(config.get('main', 'time_format')), self.doc_dict[0]['packages'][0]['stime'])
def info_package_versions(): """ Return current version of all packages :query bool platform: Filter versions by platform :query bool by_release: If true, a package that is part of a release, which is not SUCCESSFUL, will not be considered the current version, even if its own status is SUCCESSFUL. If false, a package will be the current version as long as its own status is SUCCESSFUL. Default: False. """ platform = request.args.get('platform') exclude_partial_releases = str_to_bool( request.args.get('by_release') or \ config.get('behaviour', 'versions_by_release') ) q = queries.package_versions( platform=platform, by_release=exclude_partial_releases) result = q.all() packages = {} for package in result: packages[package[0]] = package[1] return jsonify(packages), 200
def stop(self): """ Mark a release as stopped """ self.ftime = arrow.now(config.get('main', 'time_zone')) td = self.ftime - self.stime self.duration = td
def test_import_param_stime(self): """ Test start time matches """ self.assertEqual( self.release.stime.strftime(config.get('main', 'time_format')), self.doc_dict[0]['stime'])
def start(self): """ Start the deploy """ args = [config.get('deploy_shell', 'command_path')] for p in self.release.packages: args.append("{}={}".format(p.name, p.version)) app.logger.debug("Args: {}".format(str(args))) env = { 'ORLO_URL': self.server_url, 'ORLO_RELEASE': str(self.release.id) } for key, value in self.release.to_dict().items(): my_key = "ORLO_" + key.upper() env[my_key] = json.dumps(value) app.logger.debug("Env: {}".format(json.dumps(env))) metadata = {} for m in self.release.metadata: metadata.update(m.to_dict()) in_data = json.dumps(metadata) return self.run_command( args, env, in_data, timeout_sec=config.getint('deploy', 'timeout'))
def test_import_package_stime_equals_release(self): """ We use the release's stime for packages that don't define it """ self.assertEqual( self.package.stime.strftime(config.get('main', 'time_format')), self.doc_dict[0]['stime'])
def run(self, loglevel, console, workers, bind): """ Start the production server @param loglevel: @param console: @return: """ log_level = getattr(logging, loglevel.upper()) app.logger.setLevel(log_level) app.logger.propagate = False # Flask's ProductionHandler is locked at error unless debug mode is # enabled. We don't necessarily want to enable debug mode whenever we # capture debug logs, as it's a security risk. for h in app.logger.handlers: h.level = log_level app.logger.debug('Debug logging enabled') log_dir = config.get('logging', 'directory') gunicorn_options = { 'accesslog': os.path.join(log_dir, 'gunicorn_access.log') if not console else '-', 'bind': bind or config.get('gunicorn', 'bind'), 'capture_output': True, 'errorlog': os.path.join(log_dir, 'gunicorn_error.log') if not console else '-', 'logfile': os.path.join(log_dir, 'gunicorn.log') if not console else '-', 'loglevel': loglevel or config.get('logging', 'level'), 'on_starting': on_starting, 'workers': workers or config.get('gunicorn', 'workers'), } try: OrloApplication(app, gunicorn_options).run() except KeyboardInterrupt: app.logger.info('Caught KeyboardInterrupt') app.logger.debug('__main__ done')
def to_dict(self): time_format = config.get('main', 'time_format') metadata = {} for m in self.metadata: metadata.update(m.to_dict()) return { 'id': str(self.id), 'packages': [p.to_dict() for p in self.packages], 'platforms': [platform.name for platform in self.platforms], 'references': string_to_list(self.references), 'stime': self.stime.strftime(config.get('main', 'time_format')) if self.stime else None, 'ftime': self.ftime.strftime(config.get('main', 'time_format')) if self.ftime else None, 'duration': self.duration.seconds if self.duration else None, 'metadata': metadata, 'user': self.user, 'team': self.team, }
def get_token(): """ Get a token """ ttl = config.getint('security', 'token_ttl') token = token_manager.generate(g.current_user, ttl) return jsonify({ 'token': token.decode('ascii'), 'duration': config.get('security', 'token_ttl') })
def verify_auth_token(token): app.logger.debug("Verify auth token called") s = Serializer(config.get('security', 'secret_key')) try: data = s.loads(token) except SignatureExpired: return None # valid token, but expired except BadSignature: return None # invalid token user = data['id'] return user
def __repr__(self): """ :return: """ uri = config.get('db', 'uri') if uri.startswith('postgresql'): return "sa.dialects.postgresql.UUID()" elif uri.startswith('mysql'): return "sa.dialects.mysql.CHAR(32)" else: return "sa.types.CHAR(32)"
def __repr__(self): """ :return: """ uri = config.get("db", "uri") if uri.startswith("postgresql"): return "sa.dialects.postgresql.UUID()" elif uri.startswith("mysql"): return "sa.dialects.mysql.CHAR(32)" else: return "sa.types.CHAR(32)"
def to_dict(self): time_format = config.get('main', 'time_format') return { 'id': str(self.id), 'name': self.name, 'version': self.version, 'stime': self.stime.strftime(time_format) if self.stime else None, 'ftime': self.ftime.strftime(time_format) if self.ftime else None, 'duration': self.duration.seconds if self.duration else None, 'rollback': self.rollback, 'status': self.status, 'diff_url': self.diff_url, 'release_id': self.release_id, }
def verify_password_file(username=None, password=None): app.logger.debug("Verify password file called") password_file = config.get('security', 'passwd_file') with open(password_file) as f: for line in f: line = line.strip() user = line.split(':')[0] if not user == username: continue # found user return password elif user == username: app.logger.debug("Found user {} in file".format(username)) pw = ':'.join(line.split(':')[1:]) checked_password = check_password_hash(pw, password) if checked_password: return True else: return False
def stop(self, success): """ Mark a package deployment as stopped :param success: Whether or not the package deploy succeeded """ if self.stime is None: raise OrloWorkflowError( "Can not stop a package which has not been started") self.ftime = arrow.now(config.get('main', 'time_zone')) td = self.ftime - self.stime self.duration = td if success: self.status = 'SUCCESSFUL' else: self.status = 'FAILED'
from orlo import app from orlo.config import config from orlo.exceptions import OrloAuthError from flask.ext.httpauth import HTTPBasicAuth from flask.ext.tokenauth import TokenAuth, TokenManager from flask import request, jsonify, g, make_response from werkzeug.security import generate_password_hash, check_password_hash from itsdangerous import (TimedJSONWebSignatureSerializer as Serializer, BadSignature, SignatureExpired) from functools import update_wrapper, wraps import ldap # initialization user_auth = HTTPBasicAuth() token_auth = TokenAuth(config.get('security', 'secret_key')) token_manager = TokenManager(secret_key=config.get('security', 'secret_key')) class conditional_auth(object): """ Decorator which wraps a decorator, to only apply it if auth is enabled """ def __init__(self, decorator): self.decorator = decorator update_wrapper(self, decorator) def __call__(self, func): """ Call method """
def generate_auth_token(self, expiration=3600): secret_key = config.get('security', 'secret_key') s = Serializer(secret_key, expires_in=expiration) return s.dumps({'id': self.username})
from sqlalchemy_utils.types.arrow import ArrowType from orlo.app import app from orlo.config import config from orlo.exceptions import OrloWorkflowError import pytz import uuid import arrow import json __author__ = 'alforbes' db = SQLAlchemy(app) try: TIMEZONE = pytz.timezone(config.get('main', 'time_zone')) except pytz.exceptions.UnknownTimeZoneError: app.logger.critical( 'Unknown time zone "{}", see pytz docs for valid values'.format( config.get('main', 'timezone'))) # Map releases to platforms release_platform = db.Table( 'release_platform', db.Model.metadata, db.Column('release_id', UUIDType, db.ForeignKey('release.id')), db.Column('platform_id', UUIDType, db.ForeignKey('platform.id'))) def string_to_list(string): """ Load a list from a string
def start(self): """ Mark a release as started """ self.stime = arrow.now(config.get('main', 'time_zone'))
def start(self): """ Mark a package deployment as started """ self.stime = arrow.now(config.get('main', 'time_zone')) self.status = 'IN_PROGRESS'
def __init__(self, release): """ Perform a release """ self.release = release self.server_url = config.get('main', 'base_url')
from flask import Flask from flask_alembic import Alembic from orlo.config import config, CONFIG_FILE from orlo.exceptions import OrloStartupError __author__ = 'alforbes' app = Flask(__name__) alembic = Alembic() alembic.init_app(app) app.config['SQLALCHEMY_DATABASE_URI'] = config.get('db', 'uri') app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False if 'sqlite' not in app.config['SQLALCHEMY_DATABASE_URI']: # SQLite doesn't support these app.config['SQLALCHEMY_POOL_SIZE'] = config.getint('db', 'pool_size') app.config['SQLALCHEMY_POOL_RECYCLE'] = 2 app.config['SQLALCHEMY_MAX_OVERFLOW'] = 10 if config.getboolean('flask', 'propagate_exceptions'): app.config['PROPAGATE_EXCEPTIONS'] = True if config.getboolean('db', 'echo_queries'): app.config['SQLALCHEMY_ECHO'] = True if not config.getboolean('main', 'strict_slashes'):
def test_import_param_stime(self): """ Test start time matches """ self.assertEqual(self.release.stime.strftime(config.get("main", "time_format")), self.doc_dict[0]["stime"])
from flask import Flask from flask_alembic import Alembic from orlo.config import config from orlo.exceptions import OrloStartupError __author__ = 'alforbes' app = Flask(__name__) alembic = Alembic() alembic.init_app(app) app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('SQLALCHEMY_DATABASE_URI', config.get('db', 'uri')) app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False if not app.config['SQLALCHEMY_DATABASE_URI'].startswith('sqlite'): # SQLite doesn't support these app.config['SQLALCHEMY_POOL_SIZE'] = config.getint('db', 'pool_size') app.config['SQLALCHEMY_POOL_RECYCLE'] = 2 app.config['SQLALCHEMY_MAX_OVERFLOW'] = 10 if config.getboolean('flask', 'propagate_exceptions'): app.config['PROPAGATE_EXCEPTIONS'] = True if config.getboolean('db', 'echo_queries'): app.config['SQLALCHEMY_ECHO'] = True if not config.getboolean('flask', 'strict_slashes'):
class Start(Command): """ Run the Gunicorn API server, suitable for production use """ option_list = ( Option('-l', '--loglevel', default=config.get('logging', 'level'), choices=['debug', 'info', 'warning', 'error', 'critical'], help='Set logging level'), Option('-c', '--log-console', default=False, dest='console', action='store_true', help="Log to console instead of configured log files"), Option('-w', '--workers', default=config.get('gunicorn', 'workers'), help="Number of gunicorn workers to start"), Option('-b', '--bind', default=config.get('gunicorn', 'bind'), help="host:port to bind to"), ) def run(self, loglevel, console, workers, bind): """ Start the production server @param loglevel: @param console: @return: """ log_level = getattr(logging, loglevel.upper()) app.logger.setLevel(log_level) app.logger.propagate = False # Flask's ProductionHandler is locked at error unless debug mode is # enabled. We don't necessarily want to enable debug mode whenever we # capture debug logs, as it's a security risk. for h in app.logger.handlers: h.level = log_level app.logger.debug('Debug logging enabled') log_dir = config.get('logging', 'directory') gunicorn_options = { 'accesslog': os.path.join(log_dir, 'gunicorn_access.log') if not console else '-', 'bind': bind or config.get('gunicorn', 'bind'), 'capture_output': True, 'errorlog': os.path.join(log_dir, 'gunicorn_error.log') if not console else '-', 'logfile': os.path.join(log_dir, 'gunicorn.log') if not console else '-', 'loglevel': loglevel or config.get('logging', 'level'), 'on_starting': on_starting, 'workers': workers or config.get('gunicorn', 'workers'), } try: OrloApplication(app, gunicorn_options).run() except KeyboardInterrupt: app.logger.info('Caught KeyboardInterrupt') app.logger.debug('__main__ done')
from sqlalchemy_utils.types.arrow import ArrowType from orlo.app import app from orlo.config import config from orlo.exceptions import OrloWorkflowError import pytz import uuid import arrow import json __author__ = 'alforbes' db = SQLAlchemy(app) try: TIMEZONE = pytz.timezone(config.get('main', 'time_zone')) except pytz.exceptions.UnknownTimeZoneError: app.logger.critical( 'Unknown time zone "{}", see pytz docs for valid values'.format( config.get('main', 'timezone') )) # Map releases to platforms release_platform = db.Table( 'release_platform', db.Model.metadata, db.Column('release_id', UUIDType, db.ForeignKey('release.id')), db.Column('platform_id', UUIDType, db.ForeignKey('platform.id')) )
from orlo.app import app from orlo.config import config from orlo.exceptions import OrloAuthError from flask_httpauth import HTTPBasicAuth from flask_tokenauth import TokenAuth, TokenManager from flask import request, jsonify, g, make_response from werkzeug.security import generate_password_hash, check_password_hash from itsdangerous import (TimedJSONWebSignatureSerializer as Serializer, BadSignature, SignatureExpired) from functools import update_wrapper, wraps import ldap # initialization user_auth = HTTPBasicAuth() token_auth = TokenAuth(config.get('security', 'secret_key')) token_manager = TokenManager(secret_key=config.get('security', 'secret_key')) class conditional_auth(object): """ Decorator which wraps a decorator, to only apply it if auth is enabled """ def __init__(self, decorator): self.decorator = decorator update_wrapper(self, decorator) def __call__(self, func): """ Call method """