Last active
March 24, 2019 20:31
-
-
Save hemebond/57cb7b18815f452cee8c7285b47bf18f to your computer and use it in GitHub Desktop.
A passive AWS EC2 wrapper for the salt-cloud profile function
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
#!/usr/bin/env python | |
# 2019-03-22 Updated to be compatible with Salt 2018.3.3 | |
# Import python libs | |
import logging | |
import os.path | |
import time | |
import boto3 | |
import salt.config | |
import salt.wheel | |
import salt.cloud | |
import salt.runners.cloud | |
import salt.utils.aws as aws | |
from salt.exceptions import SaltInvocationError | |
# Get logging started | |
log = logging.getLogger(__name__) | |
def autosign(name, output=True): | |
''' | |
Create a file in minions_autosign to pre-approve a minion | |
''' | |
ret = {} | |
autosign_key = os.path.join(__opts__['pki_dir'], 'minions_autosign', name) | |
open(autosign_key, 'a').close() | |
ret['key'] = autosign_key | |
return ret | |
def profile(profile_name, minion_id, **kwargs): | |
''' | |
Pass is a profile to create a new EC2 instance with the name provided. | |
profile_name is the name of the Salt Cloud profile to use in creating the EC2 instance. | |
minion_id is the name of the instance | |
''' | |
# __opts__ = salt.config.client_config('/etc/salt/master') | |
wheel_client = salt.wheel.WheelClient(__opts__) | |
cloud_client = salt.cloud.CloudClient( | |
os.path.join(os.path.dirname(__opts__['conf_file']), 'cloud') | |
) | |
if not profile_name in cloud_client.opts['profiles']: | |
raise SaltInvocationError('Profile %s not found' % profile_name) | |
profile_details = cloud_client.opts['profiles'][profile_name] | |
log.debug('profile_details: %s' % profile_details) | |
alias, driver = profile_details['provider'].split(':') | |
provider_details = cloud_client.opts['providers'][alias][driver].copy() | |
vm_ = salt.cloud.Cloud.vm_config(minion_id, cloud_client.opts, provider_details, profile_details, {}) | |
# This is a passive provisioner so we don't want salt-cloud trying | |
# to SSH onto the instance and installing salt-minion | |
if salt.config.get_cloud_config_value('deploy', vm_, cloud_client.opts, default=True): | |
raise SaltCloudConfigError('Deploy can not be true') | |
# Generate the minion key and upload to S3 | |
aws_access_id = salt.config.get_cloud_config_value('id', vm_, cloud_client.opts) | |
aws_secret_key = salt.config.get_cloud_config_value('key', vm_, cloud_client.opts) | |
if not (aws_access_id and aws_secret_key): | |
raise SaltInvocationError('Need S3 credentials') | |
s3_bucket = salt.config.get_cloud_config_value('s3_bucket', vm_, cloud_client.opts) | |
s3_path = salt.config.get_cloud_config_value('s3_path', vm_, cloud_client.opts) | |
if not (s3_bucket and s3_path): | |
raise SaltInvocationError('No S3 bucket and path provided.') | |
# Generate a key for the new minion | |
keys = wheel_client.cmd('key.gen_accept', [minion_id]) | |
if keys == {}: | |
# If the key has already been accepted `keys` will be an empty dict | |
print('Minion key for %s already exists. No key will be created' % minion_id) | |
else: | |
# Upload the new minion keys to S3 | |
s3 = boto3.client('s3', aws_access_key_id=aws_access_id, | |
aws_secret_access_key=aws_secret_key) | |
s3.put_object(Key='%s/%s.pem' % (s3_path, minion_id), Bucket=s3_bucket, Body=keys['priv']) | |
s3.put_object(Key='%s/%s.pub' % (s3_path, minion_id), Bucket=s3_bucket, Body=keys['pub']) | |
info = cloud_client.profile(profile_name, [minion_id,]) | |
log.debug("New instance info: %s", info) | |
eip_id = salt.config.get_cloud_config_value('associate_eip', vm_, cloud_client.opts, default=False) | |
if eip_id: | |
# Associate an Elastic IP with the instance | |
# Can take a while for the instance to be available | |
# so we have to try multiple times | |
attempts = 5 | |
while attempts >= 0: | |
# Just a little delay between attempts... | |
time.sleep(10) | |
result = aws.query( | |
{ | |
'Action': 'AssociateAddress', | |
'InstanceId': info[minion_id]['instanceId'], | |
'AllocationId': eip_id | |
}, | |
return_root=True, | |
location=salt.config.get_cloud_config_value('location', vm_, cloud_client.opts, default='us-east-1'), | |
provider=(salt.config.get_cloud_config_value('provider', vm_, cloud_client.opts)).split(':')[0], | |
opts=cloud_client.opts, | |
sigver='4' | |
) | |
if 'error' in result: | |
log.warning('Failed to associate EIP. Remaining attempts %s', attempts) | |
attempts -= 1 | |
continue | |
else: | |
break | |
ret = { | |
'instance': info, | |
'keys': { | |
'priv': 's3://%s/%s/%s.pem' % (s3_bucket, s3_path, minion_id), | |
'pub': 's3://%s/%s/%s.pub' % (s3_bucket, s3_path, minion_id), | |
} | |
} | |
return ret |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment