mirror of
https://github.com/kevingruesser/bootstrap-vz.git
synced 2025-08-22 18:00:35 +00:00
Merge branch 'python' of https://github.com/andsens/build-debian-cloud into python
This commit is contained in:
commit
cd6e10c6a1
21 changed files with 414 additions and 125 deletions
10
README.md
10
README.md
|
@ -13,11 +13,13 @@ Pull requests are also welcome!
|
|||
|
||||
Dependencies
|
||||
------------
|
||||
You will need to run debian wheezy with python 2.7 and debootstrap installed.
|
||||
You will need to run debian wheezy with **python 2.7** and **debootstrap** installed.
|
||||
Also the following python libraries are required:
|
||||
* boto
|
||||
* jsomschema
|
||||
* termcolor
|
||||
* **boto**
|
||||
* **jsomschema** ([version 2.0.0](https://pypi.python.org/pypi/jsonschema), only available through pip)
|
||||
* **termcolor**
|
||||
|
||||
Bootstrapping instance store AMIs requires **euca2ools** to be installed.
|
||||
|
||||
Highlights
|
||||
----------
|
||||
|
|
|
@ -3,4 +3,6 @@
|
|||
class BootstrapInformation(object):
|
||||
def __init__(self, manifest=None, debug=False):
|
||||
self.manifest = manifest
|
||||
self.debug = debug
|
||||
self.debug = debug
|
||||
import random
|
||||
self.run_id = random.randrange(16**8)
|
||||
|
|
|
@ -49,7 +49,7 @@ class Manifest(object):
|
|||
self.bootstrapper = data['bootstrapper']
|
||||
if 'tarball' not in self.bootstrapper:
|
||||
self.bootstrapper['tarball'] = False
|
||||
if 'tarball_dir' not in self.bootstrapper:
|
||||
if 'tarball_dir' not in self.bootstrapper and self.bootstrapper['tarball']:
|
||||
self.bootstrapper['tarball_dir'] = '/tmp'
|
||||
self.volume = data['volume']
|
||||
self.system = data['system']
|
||||
|
|
|
@ -5,6 +5,7 @@ def log_check_call(command, input=None):
|
|||
if status != 0:
|
||||
from subprocess import CalledProcessError
|
||||
raise CalledProcessError(status, ' '.join(command), '\n'.join(stderr))
|
||||
return stdout
|
||||
|
||||
|
||||
def log_call(command, input=None):
|
||||
|
|
|
@ -1,30 +1,29 @@
|
|||
{
|
||||
"provider" : "ec2",
|
||||
"provider": "ec2",
|
||||
"virtualization": "pvm",
|
||||
"credentials" : {
|
||||
"credentials": {
|
||||
"access-key": null,
|
||||
"secret-key": null
|
||||
},
|
||||
|
||||
"bootstrapper": {
|
||||
"mount_dir": "/target",
|
||||
"tarball": true
|
||||
"mount_dir": "/target"
|
||||
},
|
||||
"image": {
|
||||
"name" : "debian-{release}-{architecture}-{virtualization}-{%y}{%m}{%d}",
|
||||
"name": "debian-{release}-{architecture}-{virtualization}-{%y}{%m}{%d}",
|
||||
"description": "Debian {release} {architecture} AMI ({virtualization})"
|
||||
},
|
||||
"system": {
|
||||
"release" : "wheezy",
|
||||
"release": "wheezy",
|
||||
"architecture": "amd64",
|
||||
"timezone" : "UTC",
|
||||
"locale" : "en_US",
|
||||
"charmap" : "UTF-8"
|
||||
"timezone": "UTC",
|
||||
"locale": "en_US",
|
||||
"charmap": "UTF-8"
|
||||
},
|
||||
"volume": {
|
||||
"backing" : "ebs",
|
||||
"backing": "ebs",
|
||||
"filesystem": "ext4",
|
||||
"size" : 1024
|
||||
"size": 1024
|
||||
},
|
||||
"plugins": {
|
||||
"admin_user": {
|
||||
|
@ -32,10 +31,10 @@
|
|||
},
|
||||
"build_metadata": {
|
||||
"enabled": false,
|
||||
"path" : "/root/build-metadata-{ami_name}"
|
||||
"path": "/root/build-metadata-{ami_name}"
|
||||
},
|
||||
"prebootstrapped": {
|
||||
"enabled": false,
|
||||
"enabled": false,
|
||||
"snapshot": ""
|
||||
}
|
||||
}
|
||||
|
|
38
manifests/ec2-s3-pvm.manifest.json
Normal file
38
manifests/ec2-s3-pvm.manifest.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"provider": "ec2",
|
||||
"virtualization": "pvm",
|
||||
"credentials": {
|
||||
"access-key": null,
|
||||
"secret-key": null,
|
||||
"certificate": null,
|
||||
"private-key": null,
|
||||
"user-id": null
|
||||
},
|
||||
|
||||
"bootstrapper": {
|
||||
"mount_dir": "/target"
|
||||
},
|
||||
"image": {
|
||||
"name": "debian-{release}-{architecture}-{virtualization}-{%y}{%m}{%d}",
|
||||
"description": "Debian {release} {architecture} AMI ({virtualization})"
|
||||
"bucket": ""
|
||||
},
|
||||
"system": {
|
||||
"release": "wheezy",
|
||||
"architecture": "amd64",
|
||||
"timezone": "UTC",
|
||||
"locale": "en_US",
|
||||
"charmap": "UTF-8"
|
||||
},
|
||||
"volume": {
|
||||
"backing": "s3",
|
||||
"filesystem": "ext4",
|
||||
"size": 1024
|
||||
},
|
||||
"plugins": {
|
||||
"prebootstrapped": {
|
||||
"enabled": false,
|
||||
"image": null
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,35 @@
|
|||
from tasks import CreateSnapshot
|
||||
from tasks import CreateVolumeFromSnapshot
|
||||
from tasks import Snapshot
|
||||
from tasks import CopyImage
|
||||
from tasks import CreateFromSnapshot
|
||||
from tasks import CreateFromImage
|
||||
from providers.ec2.tasks import ebs
|
||||
from providers.ec2.tasks import loopback
|
||||
|
||||
|
||||
def tasks(tasklist, manifest):
|
||||
from providers.ec2.tasks import bootstrap
|
||||
from providers.ec2.tasks import filesystem
|
||||
if manifest.plugins['prebootstrapped']['snapshot'] == "":
|
||||
tasklist.add(CreateSnapshot())
|
||||
settings = manifest.plugins['prebootstrapped']
|
||||
if manifest.volume['backing'] == 'ebs':
|
||||
if 'snapshot' in settings and settings['snapshot'] is not None:
|
||||
tasklist.replace(ebs.Create, CreateFromSnapshot())
|
||||
tasklist.remove(filesystem.FormatVolume,
|
||||
filesystem.TuneVolumeFS,
|
||||
filesystem.AddXFSProgs,
|
||||
bootstrap.MakeTarball,
|
||||
bootstrap.Bootstrap)
|
||||
else:
|
||||
tasklist.add(Snapshot())
|
||||
else:
|
||||
tasklist.replace(ebs.CreateVolume, CreateVolumeFromSnapshot())
|
||||
tasklist.remove(filesystem.FormatVolume,
|
||||
filesystem.TuneVolumeFS,
|
||||
filesystem.AddXFSProgs,
|
||||
bootstrap.MakeTarball,
|
||||
bootstrap.Bootstrap)
|
||||
if 'image' in settings and settings['image'] is not None:
|
||||
tasklist.replace(loopback.Create, CreateFromImage())
|
||||
tasklist.remove(filesystem.FormatVolume,
|
||||
filesystem.TuneVolumeFS,
|
||||
filesystem.AddXFSProgs,
|
||||
bootstrap.MakeTarball,
|
||||
bootstrap.Bootstrap)
|
||||
else:
|
||||
tasklist.add(CopyImage())
|
||||
|
||||
|
||||
def rollback_tasks(tasklist, tasks_completed, manifest):
|
||||
|
@ -24,7 +39,10 @@ def rollback_tasks(tasklist, tasks_completed, manifest):
|
|||
if task in completed and counter not in completed:
|
||||
tasklist.add(counter())
|
||||
|
||||
counter_task(CreateVolumeFromSnapshot, ebs.DeleteVolume)
|
||||
if manifest.volume['backing'] == 'ebs':
|
||||
counter_task(CreateFromSnapshot, ebs.Delete)
|
||||
else:
|
||||
counter_task(CreateFromImage, loopback.Delete)
|
||||
|
||||
|
||||
def validate_manifest(data, schema_validate):
|
||||
|
|
|
@ -11,9 +11,11 @@
|
|||
"properties": {
|
||||
"snapshot": {
|
||||
"type": "string"
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["snapshot"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["prebootstrapped"]
|
||||
|
|
|
@ -1,18 +1,28 @@
|
|||
from base import Task
|
||||
from common import phases
|
||||
from providers.ec2.tasks import connection
|
||||
from providers.ec2.tasks import ebs
|
||||
from providers.ec2.tasks import loopback
|
||||
from providers.ec2.tasks import bootstrap
|
||||
import time
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CreateVolumeFromSnapshot(Task):
|
||||
class Snapshot(ebs.Snapshot):
|
||||
description = 'Creating a snapshot of the bootstrapped volume'
|
||||
phase = phases.os_installation
|
||||
after = [bootstrap.Bootstrap]
|
||||
|
||||
def run(self, info):
|
||||
super(Snapshot, self).run(info)
|
||||
msg = 'A snapshot of the bootstrapped volume was created. ID: {id}'.format(id=info.snapshot.id)
|
||||
log.info(msg)
|
||||
|
||||
|
||||
class CreateFromSnapshot(Task):
|
||||
description = 'Creating EBS volume from a snapshot'
|
||||
phase = phases.volume_creation
|
||||
after = [connection.Connect]
|
||||
before = [ebs.AttachVolume]
|
||||
before = [ebs.Attach]
|
||||
|
||||
def run(self, info):
|
||||
volume_size = int(info.manifest.volume['size']/1024)
|
||||
|
@ -25,12 +35,30 @@ class CreateVolumeFromSnapshot(Task):
|
|||
info.volume.update()
|
||||
|
||||
|
||||
class CreateSnapshot(ebs.CreateSnapshot):
|
||||
class CopyImage(Task):
|
||||
description = 'Creating a snapshot of the bootstrapped volume'
|
||||
phase = phases.os_installation
|
||||
after = [bootstrap.Bootstrap]
|
||||
|
||||
def run(self, info):
|
||||
super(CreateSnapshot, self).run(info)
|
||||
msg = 'A snapshot of the bootstrapped volume was created. ID: {id}'.format(id=info.snapshot.id)
|
||||
import os.path
|
||||
from shutil import copyfile
|
||||
loopback_backup_name = 'loopback-{id:x}.img.backup'.format(id=info.run_id)
|
||||
image_copy_path = os.path.join('/tmp', loopback_backup_name)
|
||||
copyfile(info.loopback_file, image_copy_path)
|
||||
msg = 'A copy of the bootstrapped volume was created. Path: {path}'.format(path=image_copy_path)
|
||||
log.info(msg)
|
||||
|
||||
|
||||
class CreateFromImage(Task):
|
||||
description = 'Creating loopback image from a copy'
|
||||
phase = phases.volume_creation
|
||||
before = [loopback.Attach]
|
||||
|
||||
def run(self, info):
|
||||
loopback_filename = 'loopback-{id:x}.img'.format(id=info.run_id)
|
||||
import os.path
|
||||
info.loopback_file = os.path.join(info.manifest.volume['loopback_dir'], loopback_filename)
|
||||
loopback_backup_path = info.manifest.plugins['prebootstrapped']['image']
|
||||
from shutil import copyfile
|
||||
copyfile(loopback_backup_path, info.loopback_file)
|
||||
|
|
|
@ -5,6 +5,7 @@ from tasks import connection
|
|||
from tasks import host
|
||||
from tasks import ami
|
||||
from tasks import ebs
|
||||
from tasks import loopback
|
||||
from tasks import filesystem
|
||||
from tasks import bootstrap
|
||||
from tasks import locale
|
||||
|
@ -28,20 +29,13 @@ def tasks(tasklist, manifest):
|
|||
connection.GetCredentials(),
|
||||
host.GetInfo(),
|
||||
ami.AMIName(),
|
||||
connection.Connect())
|
||||
if manifest.volume['backing'].lower() == 'ebs':
|
||||
tasklist.add(ebs.CreateVolume(),
|
||||
ebs.AttachVolume())
|
||||
tasklist.add(filesystem.FormatVolume())
|
||||
if manifest.volume['filesystem'].lower() == 'xfs':
|
||||
tasklist.add(filesystem.AddXFSProgs())
|
||||
if manifest.volume['filesystem'].lower() in ['ext2', 'ext3', 'ext4']:
|
||||
tasklist.add(filesystem.TuneVolumeFS())
|
||||
tasklist.add(filesystem.CreateMountDir(),
|
||||
filesystem.MountVolume())
|
||||
if manifest.bootstrapper['tarball']:
|
||||
tasklist.add(bootstrap.MakeTarball())
|
||||
tasklist.add(bootstrap.Bootstrap(),
|
||||
connection.Connect(),
|
||||
|
||||
filesystem.FormatVolume(),
|
||||
filesystem.CreateMountDir(),
|
||||
filesystem.MountVolume(),
|
||||
|
||||
bootstrap.Bootstrap(),
|
||||
filesystem.MountSpecials(),
|
||||
locale.GenerateLocale(),
|
||||
locale.SetTimezone(),
|
||||
|
@ -68,12 +62,31 @@ def tasks(tasklist, manifest):
|
|||
apt.EnableDaemonAutostart(),
|
||||
filesystem.UnmountSpecials(),
|
||||
filesystem.UnmountVolume(),
|
||||
filesystem.DeleteMountDir())
|
||||
if manifest.volume['backing'].lower() == 'ebs':
|
||||
tasklist.add(ebs.DetachVolume(),
|
||||
ebs.CreateSnapshot(),
|
||||
ebs.DeleteVolume())
|
||||
tasklist.add(ami.RegisterAMI())
|
||||
filesystem.DeleteMountDir(),
|
||||
ami.RegisterAMI())
|
||||
|
||||
if manifest.bootstrapper['tarball']:
|
||||
tasklist.add(bootstrap.MakeTarball())
|
||||
|
||||
backing_specific_tasks = {'ebs': [ebs.Create(),
|
||||
ebs.Attach(),
|
||||
ebs.Detach(),
|
||||
ebs.Snapshot(),
|
||||
ebs.Delete()],
|
||||
's3': [loopback.Create(),
|
||||
loopback.Attach(),
|
||||
loopback.Detach(),
|
||||
ami.BundleImage(),
|
||||
ami.UploadImage(),
|
||||
loopback.Delete(),
|
||||
ami.RemoveBundle()]}
|
||||
tasklist.add(*backing_specific_tasks.get(manifest.volume['backing'].lower()))
|
||||
|
||||
filesystem_specific_tasks = {'xfs': [filesystem.AddXFSProgs()],
|
||||
'ext2': [filesystem.TuneVolumeFS()],
|
||||
'ext3': [filesystem.TuneVolumeFS()],
|
||||
'ext4': [filesystem.TuneVolumeFS()]}
|
||||
tasklist.add(*filesystem_specific_tasks.get(manifest.volume['filesystem'].lower()))
|
||||
|
||||
|
||||
def rollback_tasks(tasklist, tasks_completed, manifest):
|
||||
|
@ -84,8 +97,11 @@ def rollback_tasks(tasklist, tasks_completed, manifest):
|
|||
tasklist.add(counter())
|
||||
|
||||
if manifest.volume['backing'].lower() == 'ebs':
|
||||
counter_task(ebs.CreateVolume, ebs.DeleteVolume)
|
||||
counter_task(ebs.AttachVolume, ebs.DetachVolume)
|
||||
counter_task(ebs.Create, ebs.Delete)
|
||||
counter_task(ebs.Attach, ebs.Detach)
|
||||
if manifest.volume['backing'].lower() == 's3':
|
||||
counter_task(loopback.Create, loopback.Delete)
|
||||
counter_task(loopback.Attach, loopback.Detach)
|
||||
counter_task(filesystem.CreateMountDir, filesystem.DeleteMountDir)
|
||||
counter_task(filesystem.MountVolume, filesystem.UnmountVolume)
|
||||
counter_task(filesystem.MountSpecials, filesystem.UnmountSpecials)
|
||||
|
|
23
providers/ec2/assets/certs/cert-ec2.pem
Normal file
23
providers/ec2/assets/certs/cert-ec2.pem
Normal file
|
@ -0,0 +1,23 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzjCCAzegAwIBAgIJALDnZV+lpZdSMA0GCSqGSIb3DQEBBQUAMIGhMQswCQYD
|
||||
VQQGEwJaQTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIwEAYDVQQHEwlDYXBlIFRv
|
||||
d24xJzAlBgNVBAoTHkFtYXpvbiBEZXZlbG9wbWVudCBDZW50cmUgKFNBKTEMMAoG
|
||||
A1UECxMDQUVTMREwDwYDVQQDEwhBRVMgVGVzdDEdMBsGCSqGSIb3DQEJARYOYWVz
|
||||
QGFtYXpvbi5jb20wHhcNMDUwODA5MTYwMTA5WhcNMDYwODA5MTYwMTA5WjCBoTEL
|
||||
MAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2Fw
|
||||
ZSBUb3duMScwJQYDVQQKEx5BbWF6b24gRGV2ZWxvcG1lbnQgQ2VudHJlIChTQSkx
|
||||
DDAKBgNVBAsTA0FFUzERMA8GA1UEAxMIQUVTIFRlc3QxHTAbBgkqhkiG9w0BCQEW
|
||||
DmFlc0BhbWF6b24uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8v/X5
|
||||
zZv8CAVfNmvBM0br/RUcf1wU8xC5d2otFQQsQKB3qiWoj3oHeOWskOlTPFVZ8N+/
|
||||
hEaMjyOUkg2+g6XEagCQtFCEBzUVoMjiQIBPiWj5CWkFtlav2zt33LZ0ErTND4xl
|
||||
j7FQFqbaytHU9xuQcFO2p12bdITiBs5Kwoi9bQIDAQABo4IBCjCCAQYwHQYDVR0O
|
||||
BBYEFPQnsX1kDVzPtX+38ACV8RhoYcw8MIHWBgNVHSMEgc4wgcuAFPQnsX1kDVzP
|
||||
tX+38ACV8RhoYcw8oYGnpIGkMIGhMQswCQYDVQQGEwJaQTEVMBMGA1UECBMMV2Vz
|
||||
dGVybiBDYXBlMRIwEAYDVQQHEwlDYXBlIFRvd24xJzAlBgNVBAoTHkFtYXpvbiBE
|
||||
ZXZlbG9wbWVudCBDZW50cmUgKFNBKTEMMAoGA1UECxMDQUVTMREwDwYDVQQDEwhB
|
||||
RVMgVGVzdDEdMBsGCSqGSIb3DQEJARYOYWVzQGFtYXpvbi5jb22CCQCw52VfpaWX
|
||||
UjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAJJlWll4uGlrqBzeIw7u
|
||||
M3RvomlxMESwGKb9gI+ZeORlnHAyZxvd9XngIcjPuU+8uc3wc10LRQUCn45a5hFs
|
||||
zaCp9BSewLCCirn6awZn2tP8JlagSbjrN9YShStt8S3S/Jj+eBoRvc7jJnmEeMkx
|
||||
O0wHOzp5ZHRDK7tGULD6jCfU
|
||||
-----END CERTIFICATE-----
|
31
providers/ec2/manifest-schema-s3.json
Normal file
31
providers/ec2/manifest-schema-s3.json
Normal file
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "EC2 manifest for instance store AMIs",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentials": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"certificate": {
|
||||
"type": "string"
|
||||
},
|
||||
"private-key": {
|
||||
"type": "string"
|
||||
},
|
||||
"user-id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"image": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bucket": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["bucket"]
|
||||
}
|
||||
},
|
||||
"required": ["image"]
|
||||
}
|
|
@ -3,6 +3,17 @@
|
|||
"title": "EC2 manifest",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentials": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access-key": {
|
||||
"type": "string"
|
||||
},
|
||||
"secret-key": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"volume": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -8,9 +8,13 @@ class Manifest(base.Manifest):
|
|||
from os import path
|
||||
schema_path = path.join(path.dirname(__file__), 'manifest-schema.json')
|
||||
self.schema_validate(data, schema_path)
|
||||
if data['volume']['backing'] == 'ebs' and data['volume']['size'] % 1024 != 0:
|
||||
msg = 'The volume size must be a multiple of 1024 when using EBS backing'
|
||||
raise ManifestError(msg, self)
|
||||
if data['volume']['backing'] == 'ebs':
|
||||
if data['volume']['size'] % 1024 != 0:
|
||||
msg = 'The volume size must be a multiple of 1024 when using EBS backing'
|
||||
raise ManifestError(msg, self)
|
||||
else:
|
||||
schema_path = path.join(path.dirname(__file__), 'manifest-schema-s3.json')
|
||||
self.schema_validate(data, schema_path)
|
||||
|
||||
def parse(self, data):
|
||||
super(Manifest, self).parse(data)
|
||||
|
@ -19,3 +23,7 @@ class Manifest(base.Manifest):
|
|||
self.image = data['image']
|
||||
if data['volume']['backing'] == 'ebs':
|
||||
self.ebs_volume_size = data['volume']['size'] / 1024
|
||||
if 'loopback_dir' not in self.volume and self.volume['backing'].lower() == 's3':
|
||||
self.volume['loopback_dir'] = '/tmp'
|
||||
if 'bundle_dir' not in self.image and self.volume['backing'].lower() == 's3':
|
||||
self.image['bundle_dir'] = '/tmp'
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
from base import Task
|
||||
from common import phases
|
||||
from ebs import CreateSnapshot
|
||||
from connection import Connect
|
||||
from common.exceptions import TaskError
|
||||
from common.tools import log_check_call
|
||||
from ebs import Snapshot
|
||||
from connection import Connect
|
||||
import os.path
|
||||
|
||||
cert_ec2 = os.path.normpath(os.path.join(os.path.dirname(__file__), '../assets/certs/cert-ec2.pem'))
|
||||
|
||||
|
||||
class AMIName(Task):
|
||||
|
@ -35,42 +39,99 @@ class AMIName(Task):
|
|||
info.ami_description = ami_description
|
||||
|
||||
|
||||
class BundleImage(Task):
|
||||
description = 'Bundling the image'
|
||||
phase = phases.image_registration
|
||||
|
||||
def run(self, info):
|
||||
bundle_name = 'bundle-{id:x}'.format(id=info.run_id)
|
||||
info.bundle_path = os.path.join(info.manifest.image['bundle_dir'], bundle_name)
|
||||
log_check_call(['/usr/bin/euca-bundle-image',
|
||||
'--image', info.loopback_file,
|
||||
'--user', info.credentials['user-id'],
|
||||
'--privatekey', info.credentials['private-key'],
|
||||
'--cert', info.credentials['certificate'],
|
||||
'--ec2cert', cert_ec2,
|
||||
'--destination', info.bundle_path,
|
||||
'--prefix', info.ami_name])
|
||||
|
||||
|
||||
class UploadImage(Task):
|
||||
description = 'Uploading the image bundle'
|
||||
phase = phases.image_registration
|
||||
after = [BundleImage]
|
||||
|
||||
def run(self, info):
|
||||
manifest_file = os.path.join(info.bundle_path, info.ami_name + '.manifest.xml')
|
||||
if info.host['region'] == 'us-east-1':
|
||||
s3_url = 'https://s3.amazonaws.com/'
|
||||
else:
|
||||
s3_url = 'https://s3-{region}.amazonaws.com/'.format(region=info.host['region'])
|
||||
log_check_call(['/usr/bin/euca-upload-bundle',
|
||||
'--bucket', info.manifest.image['bucket'],
|
||||
'--manifest', manifest_file,
|
||||
'--access-key', info.credentials['access-key'],
|
||||
'--secret-key', info.credentials['secret-key'],
|
||||
'--url', s3_url,
|
||||
'--region', info.host['region'],
|
||||
'--ec2cert', cert_ec2])
|
||||
|
||||
|
||||
class RemoveBundle(Task):
|
||||
description = 'Removing the bundle files'
|
||||
phase = phases.cleaning
|
||||
|
||||
def run(self, info):
|
||||
from shutil import rmtree
|
||||
rmtree(info.bundle_path)
|
||||
del info.bundle_path
|
||||
|
||||
|
||||
class RegisterAMI(Task):
|
||||
description = 'Registering the image as an AMI'
|
||||
phase = phases.image_registration
|
||||
after = [CreateSnapshot]
|
||||
after = [Snapshot, UploadImage]
|
||||
|
||||
kernel_mapping = {'us-east-1': {'amd64': 'aki-88aa75e1',
|
||||
'i386': 'aki-b6aa75df'},
|
||||
'us-west-1': {'amd64': 'aki-f77e26b2',
|
||||
'i386': 'aki-f57e26b0'},
|
||||
'us-west-2': {'amd64': 'aki-fc37bacc',
|
||||
'i386': 'aki-fa37baca'},
|
||||
'eu-west-1': {'amd64': 'aki-71665e05',
|
||||
'i386': 'aki-75665e01'},
|
||||
'ap-southeast-1': {'amd64': 'aki-fe1354ac',
|
||||
'i386': 'aki-f81354aa'},
|
||||
'ap-southeast-2': {'amd64': 'aki-31990e0b',
|
||||
'i386': 'aki-33990e09'},
|
||||
'ap-northeast-1': {'amd64': 'aki-44992845',
|
||||
'i386': 'aki-42992843'},
|
||||
'sa-east-1': {'amd64': 'aki-c48f51d9',
|
||||
'i386': 'aki-ca8f51d7'},
|
||||
'us-gov-west-1': {'amd64': 'aki-79a4c05a',
|
||||
'i386': 'aki-7ba4c058'}}
|
||||
|
||||
def run(self, info):
|
||||
arch = {'i386': 'i386',
|
||||
'amd64': 'x86_64'}.get(info.manifest.system['architecture'])
|
||||
kernel_mapping = {'us-east-1': {'amd64': 'aki-88aa75e1',
|
||||
'i386': 'aki-b6aa75df'},
|
||||
'us-west-1': {'amd64': 'aki-f77e26b2',
|
||||
'i386': 'aki-f57e26b0'},
|
||||
'us-west-2': {'amd64': 'aki-fc37bacc',
|
||||
'i386': 'aki-fa37baca'},
|
||||
'eu-west-1': {'amd64': 'aki-71665e05',
|
||||
'i386': 'aki-75665e01'},
|
||||
'ap-southeast-1': {'amd64': 'aki-fe1354ac',
|
||||
'i386': 'aki-f81354aa'},
|
||||
'ap-southeast-2': {'amd64': 'aki-31990e0b',
|
||||
'i386': 'aki-33990e09'},
|
||||
'ap-northeast-1': {'amd64': 'aki-44992845',
|
||||
'i386': 'aki-42992843'},
|
||||
'sa-east-1': {'amd64': 'aki-c48f51d9',
|
||||
'i386': 'aki-ca8f51d7'},
|
||||
'us-gov-west-1': {'amd64': 'aki-79a4c05a',
|
||||
'i386': 'aki-7ba4c058'}}
|
||||
kernel_id = kernel_mapping.get(info.host['region']).get(info.manifest.system['architecture'])
|
||||
arch = {'i386': 'i386', 'amd64': 'x86_64'}.get(info.manifest.system['architecture'])
|
||||
kernel_id = self.kernel_mapping.get(info.host['region']).get(info.manifest.system['architecture'])
|
||||
|
||||
from boto.ec2.blockdevicemapping import BlockDeviceType
|
||||
from boto.ec2.blockdevicemapping import BlockDeviceMapping
|
||||
block_device = BlockDeviceType(snapshot_id=info.snapshot.id, delete_on_termination=True,
|
||||
size=info.manifest.ebs_volume_size)
|
||||
block_device_map = BlockDeviceMapping()
|
||||
block_device_map['/dev/sda1'] = block_device
|
||||
if info.manifest.volume['backing'] == 'ebs':
|
||||
from boto.ec2.blockdevicemapping import BlockDeviceType
|
||||
from boto.ec2.blockdevicemapping import BlockDeviceMapping
|
||||
block_device = BlockDeviceType(snapshot_id=info.snapshot.id, delete_on_termination=True,
|
||||
size=info.manifest.ebs_volume_size)
|
||||
block_device_map = BlockDeviceMapping()
|
||||
block_device_map['/dev/sda1'] = block_device
|
||||
|
||||
info.image = info.connection.register_image(name=info.ami_name, description=info.ami_description,
|
||||
architecture=arch, kernel_id=kernel_id,
|
||||
root_device_name='/dev/sda1',
|
||||
block_device_map=block_device_map)
|
||||
info.image = info.connection.register_image(name=info.ami_name, description=info.ami_description,
|
||||
architecture=arch, kernel_id=kernel_id,
|
||||
root_device_name='/dev/sda1',
|
||||
block_device_map=block_device_map)
|
||||
if info.manifest.volume['backing'] == 's3':
|
||||
image_location = ('{bucket}/{ami_name}.manifest.xml'
|
||||
.format(bucket=info.manifest.image['bucket'],
|
||||
ami_name=info.ami_name))
|
||||
info.image = info.connection.register_image(description=info.ami_description,
|
||||
architecture=arch, kernel_id=kernel_id,
|
||||
root_device_name='/dev/sda1',
|
||||
image_location=image_location)
|
||||
|
|
|
@ -8,24 +8,24 @@ class GetCredentials(Task):
|
|||
phase = phases.preparation
|
||||
|
||||
def run(self, info):
|
||||
info.credentials = self.get_credentials(info.manifest)
|
||||
keys = ['access-key', 'secret-key']
|
||||
if info.manifest.volume['backing'] == 's3':
|
||||
keys.extend(['certificate', 'private-key', 'user-id'])
|
||||
info.credentials = self.get_credentials(info.manifest, keys)
|
||||
|
||||
def get_credentials(self, manifest):
|
||||
def get_credentials(self, manifest, keys):
|
||||
from os import getenv
|
||||
# manifest overrides environment
|
||||
if(manifest.credentials['access-key'] and manifest.credentials['secret-key']):
|
||||
return {'access_key': manifest.credentials['access-key'],
|
||||
'secret_key': manifest.credentials['secret-key']}
|
||||
if(getenv('EC2_ACCESS_KEY') and getenv('EC2_SECRET_KEY')):
|
||||
return {'access_key': getenv('EC2_ACCESS_KEY'),
|
||||
'secret_key': getenv('EC2_SECRET_KEY')}
|
||||
|
||||
if(bool(manifest.credentials['access-key']) != bool(manifest.credentials['secret-key'])):
|
||||
raise RuntimeError('Both the access key and secret key must be specified in the manifest.')
|
||||
if(bool(getenv('EC2_ACCESS_KEY')) != bool(getenv('EC2_SECRET_KEY'))):
|
||||
raise RuntimeError('Both the access key and secret key must be specified as environment variables.')
|
||||
|
||||
raise RuntimeError('No ec2 credentials found.')
|
||||
creds = {}
|
||||
if all(key in manifest.credentials for key in keys):
|
||||
for key in keys:
|
||||
creds[key] = manifest.credentials[key]
|
||||
return creds
|
||||
if all(getenv(key) is not None for key in keys):
|
||||
for key in keys:
|
||||
creds[key] = getenv(key)
|
||||
return creds
|
||||
raise RuntimeError(('No ec2 credentials found, they must all be specified '
|
||||
'exclusively via environment variables or through the manifest.'))
|
||||
|
||||
|
||||
class Connect(Task):
|
||||
|
@ -36,5 +36,5 @@ class Connect(Task):
|
|||
def run(self, info):
|
||||
from boto.ec2 import connect_to_region
|
||||
info.connection = connect_to_region(info.host['region'],
|
||||
aws_access_key_id=info.credentials['access_key'],
|
||||
aws_secret_access_key=info.credentials['secret_key'])
|
||||
aws_access_key_id=info.credentials['access-key'],
|
||||
aws_secret_access_key=info.credentials['secret-key'])
|
||||
|
|
|
@ -5,7 +5,7 @@ from filesystem import UnmountVolume
|
|||
import time
|
||||
|
||||
|
||||
class CreateVolume(Task):
|
||||
class Create(Task):
|
||||
description = 'Creating an EBS volume for bootstrapping'
|
||||
phase = phases.volume_creation
|
||||
|
||||
|
@ -17,10 +17,10 @@ class CreateVolume(Task):
|
|||
info.volume.update()
|
||||
|
||||
|
||||
class AttachVolume(Task):
|
||||
class Attach(Task):
|
||||
description = 'Attaching the EBS volume'
|
||||
phase = phases.volume_creation
|
||||
after = [CreateVolume]
|
||||
after = [Create]
|
||||
|
||||
def run(self, info):
|
||||
def char_range(c1, c2):
|
||||
|
@ -45,7 +45,7 @@ class AttachVolume(Task):
|
|||
info.volume.update()
|
||||
|
||||
|
||||
class DetachVolume(Task):
|
||||
class Detach(Task):
|
||||
description = 'Detaching the EBS volume'
|
||||
phase = phases.volume_unmounting
|
||||
after = [UnmountVolume]
|
||||
|
@ -57,7 +57,7 @@ class DetachVolume(Task):
|
|||
info.volume.update()
|
||||
|
||||
|
||||
class CreateSnapshot(Task):
|
||||
class Snapshot(Task):
|
||||
description = 'Creating a snapshot of the EBS volume'
|
||||
phase = phases.image_registration
|
||||
|
||||
|
@ -68,10 +68,9 @@ class CreateSnapshot(Task):
|
|||
info.snapshot.update()
|
||||
|
||||
|
||||
class DeleteVolume(Task):
|
||||
class Delete(Task):
|
||||
description = 'Deleting the EBS volume'
|
||||
phase = phases.cleaning
|
||||
after = []
|
||||
|
||||
def run(self, info):
|
||||
info.volume.delete()
|
||||
|
|
|
@ -42,7 +42,7 @@ class CreateMountDir(Task):
|
|||
def run(self, info):
|
||||
import os
|
||||
mount_dir = info.manifest.bootstrapper['mount_dir']
|
||||
info.root = '{mount_dir}/{vol_id}'.format(mount_dir=mount_dir, vol_id=info.volume.id)
|
||||
info.root = '{mount_dir}/{id:x}'.format(mount_dir=mount_dir, id=info.run_id)
|
||||
# Works recursively, fails if last part exists, which is exaclty what we want.
|
||||
os.makedirs(info.root)
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ class GenerateLocale(Task):
|
|||
search = '# ' + locale_str
|
||||
sed_i(locale_gen, search, locale_str)
|
||||
|
||||
command = ['/usr/sbin/chroot', info.root, '/usr/sbin/dpkg-reconfigure', '--priority=critical', 'locales']
|
||||
command = ['/usr/sbin/chroot', info.root, '/usr/sbin/locale-gen']
|
||||
log_check_call(command)
|
||||
|
||||
|
||||
|
|
48
providers/ec2/tasks/loopback.py
Normal file
48
providers/ec2/tasks/loopback.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
from base import Task
|
||||
from common import phases
|
||||
from filesystem import UnmountVolume
|
||||
from common.tools import log_check_call
|
||||
|
||||
|
||||
class Create(Task):
|
||||
description = 'Creating a loopback volume'
|
||||
phase = phases.volume_creation
|
||||
|
||||
def run(self, info):
|
||||
loopback_filename = 'loopback-{id:x}.img'.format(id=info.run_id)
|
||||
import os.path
|
||||
info.loopback_file = os.path.join(info.manifest.volume['loopback_dir'], loopback_filename)
|
||||
log_check_call(['/bin/dd',
|
||||
'if=/dev/zero', 'of='+info.loopback_file,
|
||||
'bs=1M', 'seek='+str(info.manifest.volume['size']), 'count=0'])
|
||||
|
||||
|
||||
class Attach(Task):
|
||||
description = 'Attaching the loopback volume'
|
||||
phase = phases.volume_creation
|
||||
after = [Create]
|
||||
|
||||
def run(self, info):
|
||||
info.bootstrap_device = {}
|
||||
[info.bootstrap_device['path']] = log_check_call(['/sbin/losetup', '--find'])
|
||||
log_check_call(['/sbin/losetup', info.bootstrap_device['path'], info.loopback_file])
|
||||
|
||||
|
||||
class Detach(Task):
|
||||
description = 'Detaching the loopback volume'
|
||||
phase = phases.volume_unmounting
|
||||
after = [UnmountVolume]
|
||||
|
||||
def run(self, info):
|
||||
log_check_call(['/sbin/losetup', '-d', info.bootstrap_device['path']])
|
||||
del info.bootstrap_device
|
||||
|
||||
|
||||
class Delete(Task):
|
||||
description = 'Deleting the loopback volume'
|
||||
phase = phases.cleaning
|
||||
|
||||
def run(self, info):
|
||||
from os import remove
|
||||
remove(info.loopback_file)
|
||||
del info.loopback_file
|
|
@ -10,6 +10,8 @@ class HostPackages(Task):
|
|||
packages = set(['debootstrap'])
|
||||
if info.manifest.volume['filesystem'] == 'xfs':
|
||||
packages.add('xfsprogs')
|
||||
if info.manifest.volume['backing'] == 's3':
|
||||
packages.add('euca2ools')
|
||||
|
||||
info.host_packages = packages
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue