コード例 #1
0
ファイル: cli.py プロジェクト: mythmon/morgoth-cli
def init(ctx):
    """Initialize Morgoth."""
    url = click.prompt('Balrog URL',
                       settings.get('balrog_url', DEFAULT_BALROG_URL))
    username = click.prompt('Username', default=settings.get('username', ''))

    if username == '':
        username = None

    # Prompt for a password if a username is provided
    password = None
    if username:
        password = click.prompt('Password', hide_input=True)

    output('Attempting to validate Balrog credentials...', Fore.BLUE)
    get_validated_environment(url=url, username=username, password=password)

    gpg_homedir = None
    gpg_fingerprint = None
    if password:
        gpg_homedir = click.prompt(
            'GPG homedir', settings.get('gpg.homedir', GPG_HOMEDIR_DEFAULT))
        gpg_fingerprint = click.prompt('Public key fingerprint',
                                       settings.get('gpg.fingerprint'))

    # Create a settings file
    settings.path = CONFIG_PATH

    ctx.invoke(config, key='balrog_url', value=url)

    if username:
        ctx.invoke(config, key='username', value=username)
        ctx.invoke(config, key='gpg.homedir', value=gpg_homedir)
        ctx.invoke(config, key='gpg.fingerprint', value=gpg_fingerprint)
        ctx.invoke(config, key='password', value=password)
コード例 #2
0
ファイル: cli.py プロジェクト: mythmon/morgoth-cli
def config(key, value, delete, list):
    """Get or set a configuration value."""
    if list:
        for section in settings.config:
            for option in settings.config[section]:
                key = '{}.{}'.format(section, option)
                output('{} = {}'.format(key, settings.get(key)))
    elif delete:
        try:
            settings.delete(key)
        except KeyError:
            output('Setting does not exist.')
            exit(1)
    elif value == '':
        if settings.get(key):
            output(settings.get(key))
            exit(0)
    elif key:
        try:
            settings.set(key, value)
        except GPGImproperlyConfigured:
            output('GPG settings improperly configured.', Fore.RED)
            exit(1)

    settings.save()
コード例 #3
0
ファイル: cli.py プロジェクト: mythmon/morgoth-cli
def auth(ctx, username, verbose):
    """Update authentication settings."""
    if not username:
        username = click.prompt('Username', settings.get('username'))

    password = None
    while not password:
        password = click.prompt('Password', hide_input=True)

    output('Attempting to validate Balrog credentials...', Fore.BLUE)
    get_validated_environment(username=username,
                              password=password,
                              verbose=verbose)

    ctx.invoke(config, key='username', value=username)
    ctx.invoke(config, key='password', value=password)
コード例 #4
0
ファイル: cli.py プロジェクト: mythmon/morgoth-cli
def get_validated_environment(**kwargs):
    environment = Environment(kwargs.get('url', settings.get('balrog_url')),
                              username=kwargs.get('username',
                                                  settings.get('username')),
                              password=kwargs.get(
                                  'password',
                                  settings.get('password', decrypt=True)))

    try:
        environment.validate()
    except Timeout:
        output('Timeout while attempting to connect. Check VPN.', Fore.RED)
        exit(1)
    except HTTPError as err:
        if err.response.status_code == 401:
            output('Invalid Balrog credentials.', Fore.RED)
            if kwargs.get('verbose'):
                output('Error from server:')
                output(json.dumps(err.response.json(), indent=2))
            exit(1)
        raise

    return environment
コード例 #5
0
ファイル: cli.py プロジェクト: mythmon/morgoth-cli
def modify_rules(rule_ids, add, remove, verbose):
    """Modify rules."""
    environment = get_validated_environment(verbose=verbose)

    # Fetch a list of all releases
    data = environment.request('releases').json()
    releases = data.get('releases', [])
    release_names = [
        r.get('name') for r in releases if r.get('product') == 'SystemAddons'
    ]

    # Validate release to be added
    if add and add not in release_names:
        output('The release you are trying to add does not exist.', Fore.RED)
        exit(1)

    for rule_id in rule_ids:
        # Fetch existing rule
        rule = environment.fetch(f'rules/{rule_id}')

        # Fetch release for rule
        superblob = environment.fetch(f'releases/{rule["mapping"]}')

        # Construct new superblob with release
        if add and add not in superblob['blobs']:
            superblob['blobs'].append(add)
        if remove and remove in superblob['blobs']:
            superblob['blobs'].remove(remove)
        superblob['blobs'].sort()
        name_hash = sha256('-'.join(superblob['blobs']).encode()).hexdigest()
        superblob['name'] = f'Superblob-{name_hash}'

        # Check if the superblob already exists
        create_release = superblob['name'] not in release_names

        # Check if the mapping is already set
        update_mapping = rule['mapping'] != superblob['name']

        # Confirm changes
        if create_release:
            output(f'Will add new release {superblob["name"]}:')
            output('{}\n'.format(json.dumps(superblob, indent=2)),
                   Style.BRIGHT)

        if update_mapping:
            output(f'Will modify: {Style.BRIGHT}Rule {rule_id} '
                   f'(channel: {rule["channel"]}, version: {rule["version"]})')
            output(f'From mapping: {Style.BRIGHT}{rule["mapping"]}')
            output(f'To mapping: {Style.BRIGHT}{superblob["name"]}\n')

        if update_mapping or create_release:
            if not click.confirm('Apply these changes?'):
                output('Aborted!')
                exit(1)
        else:
            output(f'Skipping rule {rule_id}, nothing to change.', Fore.YELLOW)
            continue

        csrf_token = environment.csrf()

        # Create release
        if create_release:
            try:
                environment.request('releases',
                                    data={
                                        'blob': json.dumps(superblob),
                                        'name': superblob['name'],
                                        'product': 'SystemAddons',
                                        'csrf_token': csrf_token,
                                    })
            except HTTPError as err:
                response_data = err.response.json()
                output('Unable to create new release!', Fore.RED)
                if 'data' in response_data:
                    output(response_data.get('data'), Fore.RED)
                exit(1)
            release_names.append(superblob['name'])

        # Save new mapping to rule
        if update_mapping:
            ts_now = int(datetime.now().timestamp() * 1000)
            rule['mapping'] = superblob['name']
            try:
                environment.request(
                    'scheduled_changes/rules',
                    data={
                        **rule,
                        'when': ts_now + (5 * 1000),  # in five seconds
                        'change_type': 'update',
                        'csrf_token': csrf_token,
                    })
            except HTTPError as err:
                response_data = err.response.json()
                output('Unable to update rule!', Fore.RED)
                if 'data' in response_data:
                    output(response_data.get('data'), Fore.RED)
                exit(1)

    output('Done!', Fore.GREEN)
コード例 #6
0
ファイル: cli.py プロジェクト: mythmon/morgoth-cli
def make_superblob(releases, verbose):
    """Make a new superblob from releases."""
    names = []

    for release in releases:
        if os.path.exists(release):
            with open(release, 'r') as f:
                release_data = json.loads(f.read())
            names.append(release_data['name'])
        else:
            names.append(release)

    if not len(names):
        output('No releases specified.', Fore.RED)
        exit(1)

    names.sort()
    names_string = '-'.join(names)
    names_hash = sha256(names_string.encode()).hexdigest()

    sb_name = 'Superblob-{}'.format(names_hash)
    sb_data = {'blobs': names, 'name': sb_name, 'schema_version': 4000}

    if click.confirm('Upload release to Balrog?'):
        environment = get_validated_environment(verbose=verbose)

        try:
            environment.request('releases',
                                data={
                                    'blob': json.dumps(sb_data),
                                    'name': sb_name,
                                    'product': 'SystemAddons',
                                    'csrf_token': environment.csrf(),
                                })
        except HTTPError as err:
            response_data = err.response.json()
            output('Upload failed!', Fore.RED)
            if 'data' in response_data:
                output(response_data.get('data'), Fore.RED)
            exit(1)

        output('Uploaded: {}{}'.format(Style.BRIGHT, sb_name))
    elif click.confirm('Save release to file?'):
        sb_path = 'releases/superblobs/{}.json'.format(sb_name)
        os.makedirs('releases/superblobs', exist_ok=True)
        with open(sb_path, 'w') as f:
            f.write(json.dumps(sb_data, indent=2, sort_keys=True))

        output('Saving to: {}{}'.format(Style.BRIGHT, sb_path))
    else:
        output(json.dumps(sb_data, indent=2, sort_keys=True))

    output('')
コード例 #7
0
ファイル: cli.py プロジェクト: mythmon/morgoth-cli
def make_release(xpi_file, profile, verbose):
    """Make a new release from an XPI file."""
    prefix = settings.get('aws.prefix', DEFAULT_AWS_PREFIX)

    try:
        xpi = XPI(xpi_file)
    except XPI.DoesNotExist:
        output('File does not exist.', Fore.RED)
        exit(1)
    except XPI.BadZipfile:
        output('XPI cannot be unzipped.', Fore.RED)
        exit(1)
    except XPI.BadXPIfile:
        output('XPI is not properly configured.', Fore.RED)
        exit(1)
    else:
        output('Found: {}'.format(xpi.release_name), Fore.CYAN)

        if not click.confirm('Is this correct?'):
            output('Release could not be auto-generated.', Fore.RED)
            exit(1)

        session = boto3.Session(profile_name=profile)
        s3 = session.resource('s3')
        bucket = s3.Bucket(
            settings.get('aws.bucket_name', DEFAULT_AWS_BUCKET_NAME))

        exists = False
        for obj in bucket.objects.filter(Prefix=prefix):
            if obj.key == xpi.get_ftp_path(prefix):
                exists = True

        uploaded = False
        if exists:
            tmpdir = tempfile.mkdtemp()
            download_path = os.path.join(tmpdir, xpi.file_name)
            bucket.download_file(xpi.get_ftp_path(prefix), download_path)
            uploaded_xpi = XPI(download_path)

            if uploaded_xpi.sha512sum == xpi.sha512sum:
                output('XPI already uploaded.', Fore.GREEN)
                uploaded = True
            else:
                output('XPI with matching filename already uploaded.',
                       Fore.YELLOW)

        if not uploaded:
            if exists and not click.confirm('Would you like to replace it?'):
                output('Aborting.', Fore.RED)
                exit(1)
            with open(xpi.path, 'rb') as data:
                bucket.put_object(Key=xpi.get_ftp_path(prefix), Body=data)
                output('XPI uploaded successfully.', Fore.GREEN)

        release_data = xpi.generate_release_data(
            settings.get('aws.base_url', DEFAULT_AWS_BASE_URL), prefix)

        if click.confirm('Upload release to Balrog?'):
            environment = get_validated_environment(verbose=verbose)

            environment.request('releases',
                                data={
                                    'blob': json.dumps(release_data),
                                    'name': xpi.release_name,
                                    'product': 'SystemAddons',
                                    'csrf_token': environment.csrf(),
                                })

            output('Uploaded: {}{}'.format(Style.BRIGHT, xpi.release_name))
        elif click.confirm('Save release to file?'):
            json_path = 'releases/{}.json'.format(xpi.release_name)

            if os.path.exists(json_path):
                output('Release JSON file already exists.', Fore.YELLOW)
                if not click.confirm('Replace existing release JSON file?'):
                    output('Aborting.', Fore.RED)
                    exit(1)

            output('Saving to: {}{}'.format(Style.BRIGHT, json_path))

            os.makedirs('releases', exist_ok=True)
            with open(json_path, 'w') as f:
                f.write(json.dumps(release_data, indent=2, sort_keys=True))
        else:
            output(json.dumps(release_data, indent=2, sort_keys=True))

    output('')
コード例 #8
0
ファイル: cli.py プロジェクト: mythmon/morgoth-cli
def status(verbose):
    """Show the current status."""
    if verbose:
        output(STATUS_5H17)