-
-
Save jollyroger/0315ac0293f4e264c42c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
''' | |
Management of Bitbucket deploy keys | |
=================================== | |
.. code-block:: yaml | |
production_server_deploy_key: | |
bitbucket_deploy_key.present: | |
- user: username | |
- repo: my_awesome_app | |
- admin_user: admin | |
- admin_pass: secret | |
- name: app-deploy-production-key | |
- pubkey_file: /path/to/id_rsa.pub | |
''' | |
# Import python libs | |
import os | |
import requests | |
from Crypto.PublicKey import RSA | |
# Import salt libs | |
import salt.utils | |
def __virtual__(): | |
''' | |
Only load if the mysql module is in __salt__ | |
''' | |
return 'bitbucket_deployment_key' | |
class APIError(Exception): | |
def __init__(self, status_code, text, more=None): | |
self.status_code = status_code | |
self.message = text | |
if more: | |
self.more = more | |
return | |
def __str__(self): | |
return "HTTP status code {0}: {1}. Details: {2}".format( | |
self.status_code, self.message, self.more) | |
class BitbucketConnection: | |
def __init__(self, user, repo, admin_user, password): | |
self.user = user | |
self.repo = repo | |
self.admin_user = admin_user if admin_user else user | |
self.password = password | |
return | |
def url(self): | |
return 'https://bitbucket.org/api/1.0/repositories/{0}/{1}/deploy-keys'.format( | |
self.user, self.repo) | |
def keys(self): | |
reply = requests.get(self.url(), auth=(self.admin_user, self.password)) | |
if reply.status_code == 200: | |
return reply.json() | |
else: | |
raise APIError(reply.status_code, reply.reason) | |
def add(self, key, label=None): | |
data = {'key': key} | |
if label: | |
data['label'] = label | |
reply = requests.post(self.url(), auth=(self.admin_user, | |
self.password), data=data) | |
if reply.status_code == 200: | |
return reply.json() | |
else: | |
raise APIError(reply.status_code, reply.reason, reply.content) | |
def get(self, key): | |
key = key.strip() | |
for _key in self.keys(): | |
if _key['key'] == key: | |
return _key | |
else: | |
raise KeyError("Public key not found") | |
def delete(self, pk): | |
url2 = self.url() + "/" + str(pk) | |
reply = requests.delete(url2, auth=(self.admin_user, self.password)) | |
if reply.status_code != 204: | |
raise APIError(reply.status_code, reply.reason, reply.content) | |
def present(name, | |
user=None, | |
repo=None, | |
admin_user=None, | |
password=None, | |
pubkey_file=None): | |
''' | |
Ensure that the named deployment key is present in repository's key list. | |
Repository is identified by ``user`` and ``repo`` options. ``admin_user`` | |
should be able to access admin page of the repository. If no ``admin_user`` | |
option is set, ``user`` option is silently used. | |
name | |
The name of the deployment key in the repo list. Corresponds to the | |
``label`` option in Bitbucket API | |
user | |
Bitbucket user who is owner of the repository. | |
repo | |
Repository that belongs to mentioned user. | |
admin_user | |
Bitbucket account name which is capable to manage deployment keys. | |
password | |
The password to use for above user. | |
pubkey_file | |
Path to the public key. | |
''' | |
ret = {'name': name, | |
'changes': {}, | |
'result': True, | |
'comment': 'Key {0} is already present for repository {1}/{2}'.format( | |
name, user, repo)} | |
if not user: | |
ret['comment'] = 'User should be specified' | |
ret['result'] = False | |
return ret | |
elif not repo: | |
ret['comment'] = 'Repository name should be specified' | |
ret['result'] = False | |
return ret | |
elif not password: | |
ret['comment'] = 'User password should be specified' | |
ret['result'] = False | |
return ret | |
elif not pubkey_file: | |
ret['comment'] = 'Password to the public key file should be specified' | |
ret['result'] = False | |
return ret | |
if not admin_user: | |
admin_user = user | |
max_pubkey_size = 655536L | |
# read the contents of the public key | |
try: | |
statinfo = os.stat(pubkey_file) | |
if statinfo.st_size > max_pubkey_size: | |
ret['comment'] = 'Something\'s wrong with the pubkey_file: file ' \ | |
'size too large' | |
ret['result'] = False | |
return ret | |
pubkey_raw = open(pubkey_file, 'r').read() | |
pubkey = RSA.importKey(pubkey_raw) | |
except IOError, err: | |
ret['comment'] = 'Error reading pubkey_file {0}: {1}'.format( | |
pubkey_file, err) | |
ret['result'] = False | |
return ret | |
except ValueError, err: | |
ret['comment'] = 'pubkey_file does not contain public key: {0}'.format( | |
err) | |
ret['result'] = False | |
return ret | |
conn = BitbucketConnection(user, repo, admin_user, password) | |
try: | |
key = conn.get(pubkey_raw) | |
ret['comment'] = 'Key is already present for repository {0}/{1} ' \ | |
'with name {2}'.format(user, repo, key['label']) | |
except APIError, err: | |
if err.status_code != 401: | |
raise | |
ret['comment'] = 'Cannot access {0}/{1} deployment keys: invalid ' \ | |
'credentials for user {2}'.format(user, repo, admin_user) | |
ret['result'] = False | |
return ret | |
except KeyError: | |
if __opts__['test'] == True: | |
ret['comment'] = 'New deployment key will be added to ' \ | |
'{0}/{1} repository'.format(user, repo) | |
ret['changes'] = { | |
'old': '', | |
'new': 'New deploy key will be added: {0}\n{1}'.format(name, | |
pubkey_raw)} | |
ret['result'] = None | |
return ret | |
try: | |
result = conn.add(pubkey_raw, name) | |
except APIError, err: | |
ret['comment'] = 'Error adding deployment key {0}: {1}'.format( | |
name, err) | |
ret['result'] = False | |
return ret | |
ret['comment'] = 'Key {0} has been added to {1}/{2} repository'.format( | |
result['label'], user, repo) | |
return ret |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% import "company/macros.jinja" as global %} | |
{% import "company/buildbot/macros.jinja" as cfg %} | |
{% set ssh_dir = "%s/.ssh"|format(cfg.home) %} | |
{% set ssh_authorized_keys = "%s/authorized_keys"|format(ssh_dir) %} | |
{% set ssh_private_key = "%s/id_rsa"|format(ssh_dir) %} | |
{% set ssh_public_key = "%s.pub"|format(ssh_private_key) %} | |
service_ssh_key: | |
ssh_known_hosts.present: | |
- name: bitbucket.org | |
- user: {{ cfg.user }} | |
- fingerprint: 97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40 | |
{{ ssh_dir }}: | |
file.directory: | |
- user: {{ cfg.user }} | |
- group: {{ cfg.group }} | |
- mode: 0700 | |
- clean: False | |
# Generate ssh deploy key on the server | |
generate_deploy_private_key: | |
cmd.run: | |
- name: "ssh-keygen -q -N '' -f {{ ssh_private_key }}" | |
- user: {{ cfg.user }} | |
- group: {{ cfg.group }} | |
- creates: {{ ssh_private_key }} | |
{{ ssh_private_key }}: | |
file.managed: | |
- user: {{ cfg.user }} | |
- group: {{ cfg.group }} | |
- mode: 0600 | |
- require: | |
- cmd: generate_deploy_private_key | |
- require_in: | |
- file: {{ ssh_dir }} | |
generate_deploy_public_key: | |
cmd.run: | |
- name: "ssh-keygen -y -f {{ ssh_private_key }} > {{ ssh_public_key }}" | |
- user: {{ cfg.user }} | |
- group: {{ cfg.group }} | |
- unless: "test {{ ssh_public_key }} -nt {{ ssh_private_key }}" | |
- creates: {{ ssh_public_key }} | |
- require: | |
- file: {{ ssh_private_key }} | |
{{ ssh_public_key }}: | |
file.managed: | |
- user: {{ cfg.user }} | |
- group: {{ cfg.group }} | |
- mode: 0644 | |
- require: | |
- cmd: generate_deploy_public_key | |
- require_in: | |
- file: {{ ssh_dir }} | |
populate_buildmaster_deploy_key: | |
bitbucket_deployment_key.present: | |
- name: {{ grains.host }}-{{ cfg.host }} | |
- user: {{ cfg.bitbucket_user }} | |
- repo: {{ cfg.bitbucket_repo }} | |
- admin_user: {{ cfg.bitbucket_admin_user }} | |
- password: {{ cfg.bitbucket_password }} | |
- pubkey_file: {{ ssh_public_key }} | |
- require: | |
- cmd: generate_deploy_public_key | |
- file: {{ ssh_public_key }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% set name = "myproject") %} | |
{% set domains = ["buildbot.mycompany.com"]) %} | |
{% set user = "buildbot" %} | |
{% set group = user %} | |
{% set home = "/var/lib/%s"|format(user) %} | |
{% set buildbot_dir = "%s/masters/%s"|format(home, name) %} | |
{% set bitbucket_user = salt['pillar.get']('bitbucket:user', 'mycompany') %} | |
{% set bitbucket_admin_user = salt['pillar.get']('bitbucket:admin_user', bitbucket_user) %} | |
{% set bitbucket_password = salt['pillar.get']('bitbucket:admin_pass', 'secret') %} | |
{% set bitbucket_repo = 'buildbotcfg' %} | |
{% set htpasswd_auth = salt['pillar.get']('nginx:control_vhost:auth_file', '') %} | |
# These are taken from buildbot repo and not updated automatically | |
{% set buildbot_pb_port = 9989 %} | |
{% set buildbot_http_port = 8011 %} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment