builderator 0.3.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rubocop.yml +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +30 -0
- data/Rakefile +2 -0
- data/Thorfile +1 -0
- data/VERSION +1 -0
- data/bin/build +17 -0
- data/builderator.gemspec +29 -0
- data/docs/clean.md +21 -0
- data/lib/builderator.rb +4 -0
- data/lib/builderator/control/ami.rb +64 -0
- data/lib/builderator/control/clean.rb +134 -0
- data/lib/builderator/metadata.rb +5 -0
- data/lib/builderator/model.rb +46 -0
- data/lib/builderator/model/images.rb +89 -0
- data/lib/builderator/model/instances.rb +55 -0
- data/lib/builderator/model/launch_configs.rb +46 -0
- data/lib/builderator/model/scaling_groups.rb +43 -0
- data/lib/builderator/model/snapshots.rb +49 -0
- data/lib/builderator/model/volumes.rb +48 -0
- data/lib/builderator/tasks.rb +37 -0
- data/lib/builderator/tasks/ami.rb +37 -0
- data/lib/builderator/tasks/berks.rb +68 -0
- data/lib/builderator/tasks/clean.rb +94 -0
- data/lib/builderator/tasks/cookbook.rb +42 -0
- data/lib/builderator/tasks/packer.rb +72 -0
- data/lib/builderator/tasks/vagrant.rb +66 -0
- data/lib/builderator/util.rb +53 -0
- data/lib/builderator/util/aws_exception.rb +31 -0
- data/lib/builderator/util/berkshim.rb +34 -0
- data/lib/builderator/util/cookbook.rb +42 -0
- data/lib/builderator/util/limit_exception.rb +38 -0
- data/lib/builderator/util/packer.rb +39 -0
- data/lib/builderator/util/shell.rb +44 -0
- data/lib/builderator/util/task_exception.rb +22 -0
- metadata +210 -0
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require_relative '../util'
|
3
|
+
|
4
|
+
module Builderator
|
5
|
+
module Model
|
6
|
+
|
7
|
+
def self.images
|
8
|
+
@images ||= Images.new
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# EC2 AMI Resources
|
13
|
+
##
|
14
|
+
class Images < Model::Base
|
15
|
+
LIMIT = 24
|
16
|
+
PROPERTIES = %w(image_location state owner_id public architecture image_type
|
17
|
+
name description root_device_type virtualization_type
|
18
|
+
hypervisor)
|
19
|
+
|
20
|
+
def fetch
|
21
|
+
@resources = {}.tap do |i|
|
22
|
+
Util.ec2.describe_images(:filters => [
|
23
|
+
{
|
24
|
+
:name => 'state',
|
25
|
+
:values => %w(available)
|
26
|
+
}
|
27
|
+
], :owners => %w(self)).each do |page|
|
28
|
+
page.images.each do |image|
|
29
|
+
properties = Util.from_tags(image.tags)
|
30
|
+
properties['creation_date'] = DateTime.iso8601(image.creation_date)
|
31
|
+
PROPERTIES.each { |pp| properties[pp] = image[pp.to_sym] }
|
32
|
+
|
33
|
+
i[image.image_id] = {
|
34
|
+
:id => image.image_id,
|
35
|
+
:properties => properties,
|
36
|
+
:snapshots => image.block_device_mappings.map { |b| b.ebs.snapshot_id rescue nil }.reject(&:nil?),
|
37
|
+
:parent => properties.fetch('parent_ami', '(undefined)')
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def snapshots
|
45
|
+
resources.values.map { |i| i[:snapshots] }.flatten
|
46
|
+
end
|
47
|
+
|
48
|
+
def latest(options = {})
|
49
|
+
{}.tap do |latest|
|
50
|
+
## Group images
|
51
|
+
group_by = options.fetch('group-by', [])
|
52
|
+
groups = {}.tap do |g|
|
53
|
+
break { 'all' => resources.values } if group_by.empty?
|
54
|
+
|
55
|
+
resources.each do |_, image|
|
56
|
+
(g[group_by.map do |gg|
|
57
|
+
image[:properties].fetch(gg.to_s, '(unknown)')
|
58
|
+
end.join(':')] ||= []) << image
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
## Sort each grouping
|
63
|
+
sort_by = options.fetch('sort-by', 'creation_date')
|
64
|
+
groups.each do |_, group|
|
65
|
+
group.sort! { |a, b| b[:properties][sort_by] <=> a[:properties][sort_by] }
|
66
|
+
end
|
67
|
+
|
68
|
+
## Slice to `keep` length
|
69
|
+
keep = options.fetch('keep', 5)
|
70
|
+
groups.each do |_, group|
|
71
|
+
group.slice!(keep..-1)
|
72
|
+
end
|
73
|
+
|
74
|
+
## Reduce
|
75
|
+
groups.values.flatten.each { |i| latest[i[:id]] = i }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def in_use(options = {})
|
80
|
+
{}.tap do |used|
|
81
|
+
used.merge!(select(Model.instances.images))
|
82
|
+
used.merge!(select(Model.launch_configs.images))
|
83
|
+
used.merge!(latest(options))
|
84
|
+
used.merge!(select(used.values.map { |i| i[:parent] }))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require_relative '../util'
|
3
|
+
|
4
|
+
module Builderator
|
5
|
+
module Model
|
6
|
+
|
7
|
+
def self.instances
|
8
|
+
@instances ||= Instances.new
|
9
|
+
end
|
10
|
+
##
|
11
|
+
# EC2 Instance resources
|
12
|
+
##
|
13
|
+
class Instances < Model::Base
|
14
|
+
PROPERTIES = %w(private_dns_name public_dns_name instance_type
|
15
|
+
subnet_id vpc_id private_ip_address public_ip_address
|
16
|
+
architecture root_device_type virtualization_type
|
17
|
+
hypervisor)
|
18
|
+
|
19
|
+
def fetch
|
20
|
+
@resources = {}.tap do |i|
|
21
|
+
Util.ec2.describe_instances(:filters => [
|
22
|
+
{
|
23
|
+
:name => 'instance-state-name',
|
24
|
+
:values => %w(pending running shutting-down stopping stopped)
|
25
|
+
}
|
26
|
+
]).each do |page|
|
27
|
+
page.reservations.each do |r|
|
28
|
+
r.instances.each do |instance|
|
29
|
+
properties = Util.from_tags(instance.tags)
|
30
|
+
properties['availability_zone'] = instance.placement.availability_zone
|
31
|
+
properties['creation_date'] = instance.launch_time.to_datetime
|
32
|
+
PROPERTIES.each { |pp| properties[pp] = instance[pp.to_sym] }
|
33
|
+
|
34
|
+
i[instance.instance_id] = {
|
35
|
+
:id => instance.instance_id,
|
36
|
+
:image => instance.image_id,
|
37
|
+
:volumes => instance.block_device_mappings.map { |b| b.ebs.volume_id },
|
38
|
+
:properties => properties
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def images
|
47
|
+
resources.values.map { |i| i[:image] }
|
48
|
+
end
|
49
|
+
|
50
|
+
def volumes
|
51
|
+
resources.values.map { |i| i[:volumes] }.flatten
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require_relative '../util'
|
3
|
+
|
4
|
+
module Builderator
|
5
|
+
module Model
|
6
|
+
|
7
|
+
def self.launch_configs
|
8
|
+
@launch_configs ||= LaunchConfigs.new
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# ASG LaunchConfiguration Resources
|
13
|
+
##
|
14
|
+
class LaunchConfigs < Model::Base
|
15
|
+
LIMIT = 24
|
16
|
+
PROPERTIES = %w(launch_configuration_arn key_name security_groups
|
17
|
+
user_data instance_type spot_price iam_instance_profile
|
18
|
+
ebs_optimized associate_public_ip_address placement_tenancy)
|
19
|
+
|
20
|
+
def fetch
|
21
|
+
@resources = {}.tap do |i|
|
22
|
+
Util.asg.describe_launch_configurations.each do |page|
|
23
|
+
page.launch_configurations.each do |l|
|
24
|
+
properties = { 'creation_date' => l.created_time.to_datetime }
|
25
|
+
PROPERTIES.each { |pp| properties[pp] = l[pp.to_sym] }
|
26
|
+
|
27
|
+
i[l.launch_configuration_name] = {
|
28
|
+
:id => l.launch_configuration_name,
|
29
|
+
:properties => properties,
|
30
|
+
:image => l.image_id
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def images
|
38
|
+
resources.values.map { |l| l[:image] }
|
39
|
+
end
|
40
|
+
|
41
|
+
def in_use(_)
|
42
|
+
select(Model.scaling_groups.launch_configs)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require_relative '../util'
|
3
|
+
|
4
|
+
module Builderator
|
5
|
+
module Model
|
6
|
+
|
7
|
+
def self.scaling_groups
|
8
|
+
@scaling_groups ||= ScalingGroups.new
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# AutoScaling Group Resoruces
|
13
|
+
##
|
14
|
+
class ScalingGroups < Model::Base
|
15
|
+
attr_reader :resources
|
16
|
+
PROPERTIES = %w(auto_scaling_group_arn min_size max_size desired_capacity
|
17
|
+
default_cooldown availability_zones load_balancer_names
|
18
|
+
vpc_zone_identifier status termination_policies)
|
19
|
+
|
20
|
+
def fetch
|
21
|
+
@resources = {}.tap do |i|
|
22
|
+
Util.asg.describe_auto_scaling_groups.each do |page|
|
23
|
+
page.auto_scaling_groups.each do |a|
|
24
|
+
properties = Util.from_tags(a.tags)
|
25
|
+
properties['creation_date'] = a.created_time.to_datetime
|
26
|
+
PROPERTIES.each { |pp| properties[pp] = a[pp.to_sym] }
|
27
|
+
|
28
|
+
i[a.launch_configuration_name] = {
|
29
|
+
:id => a.auto_scaling_group_name,
|
30
|
+
:properties => properties,
|
31
|
+
:config => a.launch_configuration_name
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def launch_configs
|
39
|
+
resources.values.map { |g| g[:config] }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require_relative '../util'
|
3
|
+
|
4
|
+
module Builderator
|
5
|
+
module Model
|
6
|
+
|
7
|
+
def self.snapshots
|
8
|
+
@snapshots ||= Snapshots.new
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# EC2 Snapshot Resources
|
13
|
+
##
|
14
|
+
class Snapshots < Model::Base
|
15
|
+
LIMIT = 24
|
16
|
+
PROPERTIES = %w(state owner_id description volume_size)
|
17
|
+
|
18
|
+
def fetch
|
19
|
+
@resources = {}.tap do |s|
|
20
|
+
Util.ec2.describe_snapshots(:filters => [
|
21
|
+
{
|
22
|
+
:name => 'status',
|
23
|
+
:values => %w(completed)
|
24
|
+
}
|
25
|
+
], :owner_ids => ['self']).each do |page|
|
26
|
+
page.snapshots.each do |snap|
|
27
|
+
properties = Util.from_tags(snap.tags)
|
28
|
+
properties['creation_date'] = snap.start_time.to_datetime
|
29
|
+
PROPERTIES.each { |pp| properties[pp] = snap[pp.to_sym] }
|
30
|
+
|
31
|
+
s[snap.snapshot_id] = {
|
32
|
+
:id => snap.snapshot_id,
|
33
|
+
:properties => properties,
|
34
|
+
:volume => snap.volume_id
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def in_use(_)
|
42
|
+
{}.tap do |used|
|
43
|
+
used.merge!(select(Model.volumes.snapshots))
|
44
|
+
used.merge!(select(Model.images.snapshots))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require_relative '../util'
|
3
|
+
|
4
|
+
module Builderator
|
5
|
+
module Model
|
6
|
+
|
7
|
+
def self.volumes
|
8
|
+
@volumes ||= Volumes.new
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# EC2 Volume Resources
|
13
|
+
##
|
14
|
+
class Volumes < Model::Base
|
15
|
+
LIMIT = 8
|
16
|
+
PROPERTIES = %w(size availability_zone state volume_type iops)
|
17
|
+
|
18
|
+
def fetch
|
19
|
+
@resources = {}.tap do |v|
|
20
|
+
Util.ec2.describe_volumes.each do |page|
|
21
|
+
page.volumes.each do |vol|
|
22
|
+
properties = Util.from_tags(vol.tags)
|
23
|
+
properties['creation_date'] = vol.create_time.to_datetime
|
24
|
+
PROPERTIES.each { |pp| properties[pp] = vol[pp.to_sym] }
|
25
|
+
|
26
|
+
|
27
|
+
v[vol.volume_id] = {
|
28
|
+
:id => vol.volume_id,
|
29
|
+
:properties => properties,
|
30
|
+
:snapshot => vol.snapshot_id
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def snapshots
|
38
|
+
resources.values.map { |v| v[:snapshot] }
|
39
|
+
end
|
40
|
+
|
41
|
+
def in_use(_)
|
42
|
+
{}.tap do |used|
|
43
|
+
used.merge!(select(Model.instances.volumes))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require_relative './tasks/ami'
|
3
|
+
require_relative './tasks/berks'
|
4
|
+
require_relative './tasks/clean'
|
5
|
+
require_relative './tasks/cookbook'
|
6
|
+
require_relative './tasks/packer'
|
7
|
+
require_relative './tasks/vagrant'
|
8
|
+
|
9
|
+
module Builderator
|
10
|
+
module Tasks
|
11
|
+
class CLI < Thor
|
12
|
+
desc 'ami SUBCOMMAND', 'Search for AMI IDs'
|
13
|
+
subcommand 'ami', Builderator::Tasks::AMI
|
14
|
+
|
15
|
+
desc 'berks SUBCOMMAND', 'Berkshelf helpers'
|
16
|
+
subcommand 'berks', Builderator::Tasks::Berks
|
17
|
+
|
18
|
+
desc 'clean SUBCOMMAND', 'Clean up things'
|
19
|
+
subcommand 'clean', Builderator::Tasks::Clean
|
20
|
+
|
21
|
+
desc 'cookbook SUBCOMMAND', 'Cookbook tasks'
|
22
|
+
subcommand 'cookbook', Builderator::Tasks::Cookbook
|
23
|
+
|
24
|
+
|
25
|
+
desc 'packer SUBCOMMAND', 'Run Packer tasks'
|
26
|
+
subcommand 'packer', Builderator::Tasks::Packer
|
27
|
+
|
28
|
+
desc 'vagrant SUBCOMMAND', 'Run Vagrant tasks'
|
29
|
+
subcommand 'vagrant', Builderator::Tasks::Vagrant
|
30
|
+
|
31
|
+
desc 'version', 'Print gem version'
|
32
|
+
def version
|
33
|
+
puts Builderator::VERSION
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require_relative '../control/ami'
|
3
|
+
|
4
|
+
module Builderator
|
5
|
+
module Tasks
|
6
|
+
class AMI < Thor
|
7
|
+
namespace :ami
|
8
|
+
|
9
|
+
class_option :root_device_type,
|
10
|
+
:default => 'ebs',
|
11
|
+
:desc => 'The type of the root device volume (ebs | instance-store)'
|
12
|
+
class_option :virtualization_type,
|
13
|
+
:default => 'hvm',
|
14
|
+
:desc => 'The virtualization type (paravirtual | hvm)'
|
15
|
+
class_option :architecture,
|
16
|
+
:default => 'x86_64',
|
17
|
+
:desc => 'The image architecture (i386 | x86_64)'
|
18
|
+
|
19
|
+
desc 'ubuntu SEARCH', 'Print the latest AMI ID for an Ubuntu image matching the SEARCH string'
|
20
|
+
def ubuntu(search = '*/hvm-ssd/ubuntu-trusty-daily-amd64-server-20*')
|
21
|
+
puts Control::AMI.latest(:owner => Builderator::Control::AMI::Owners::UBUNTU,
|
22
|
+
'root-device-type' => options['root_device_type'],
|
23
|
+
'virtualization-type' => options['virtualization_type'],
|
24
|
+
'architecture' => options['architecture'],
|
25
|
+
'name' => search).image_id
|
26
|
+
end
|
27
|
+
|
28
|
+
desc 'private [KEY VALUE ...]', 'Find the latest AMI ID with tags KEY=VALUE'
|
29
|
+
def private(*args)
|
30
|
+
puts Control::AMI.latest({ :owner => Builderator::Control::AMI::Owners::SELF,
|
31
|
+
'root-device-type' => options['root_device_type'],
|
32
|
+
'virtualization-type' => options['virtualization_type'],
|
33
|
+
'architecture' => options['architecture'] }.merge(Hash[*args])).image_id
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'thor/actions'
|
3
|
+
require_relative './cookbook'
|
4
|
+
require_relative '../util/cookbook'
|
5
|
+
|
6
|
+
module Builderator
|
7
|
+
module Tasks
|
8
|
+
class Berks < Thor
|
9
|
+
include Thor::Actions
|
10
|
+
class_option :config, :aliases => :c, :desc => "Path to Berkshelf's config.json"
|
11
|
+
class_option :cookbook, :aliases => :b, :desc => 'Path to the cookbook to use'
|
12
|
+
class_option :version, :type => :boolean,
|
13
|
+
:default => true,
|
14
|
+
:desc => 'Write current verison to file'
|
15
|
+
|
16
|
+
desc "local [PATH = #{ Util::Cookbook::DEFAULT_VENDOR }]", 'Vendor the local cookbook source and its dependencies'
|
17
|
+
def local(path = Util::Cookbook::DEFAULT_VENDOR)
|
18
|
+
Util::Cookbook.path(options['cookbook'])
|
19
|
+
|
20
|
+
command = 'BERKS_INSTALL_FROM=source'
|
21
|
+
command << " berks vendor #{ path }"
|
22
|
+
command << " -c #{ options['config'] }" if options.include?('config')
|
23
|
+
command << " -b #{ Util::Cookbook.berksfile }"
|
24
|
+
|
25
|
+
remove_file File.expand_path('Berksfile.lock', Util::Cookbook.path)
|
26
|
+
invoke Tasks::Cookbook, 'metadata', [], options
|
27
|
+
run command
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "vendor [PATH = #{ Util::Cookbook::DEFAULT_VENDOR }]", 'Vendor a cookbook release and its dependencies'
|
31
|
+
def vendor(path = Util::Cookbook::DEFAULT_VENDOR)
|
32
|
+
Util::Cookbook.path(options['cookbook'])
|
33
|
+
|
34
|
+
command = 'BERKS_INSTALL_FROM=release'
|
35
|
+
command << " berks vendor #{ path }"
|
36
|
+
command << " -c #{ options['config'] }" if options.include?('config')
|
37
|
+
command << " -b #{ Util::Cookbook.berksfile }"
|
38
|
+
|
39
|
+
remove_file File.expand_path('Berksfile.lock', Util::Cookbook.path)
|
40
|
+
run command
|
41
|
+
end
|
42
|
+
|
43
|
+
desc 'upload', 'Upload the local cookbook source and its dependencies to the Chef server'
|
44
|
+
option 'dry-run', :type => :boolean, :default => false
|
45
|
+
def upload(path = Util::Cookbook::DEFAULT_VENDOR)
|
46
|
+
command = 'BERKS_INSTALL_FROM=source'
|
47
|
+
command << " berks upload"
|
48
|
+
command << " -c #{ options['config'] }" if options.include?('config')
|
49
|
+
command << " -b #{ Util::Cookbook.berksfile }"
|
50
|
+
|
51
|
+
invoke Tasks::Berks, :local, [path], options
|
52
|
+
|
53
|
+
return say_status :dryrun, command if options['dry-run']
|
54
|
+
run command
|
55
|
+
end
|
56
|
+
|
57
|
+
desc 'uncache', 'Delete the Berkshelf cache'
|
58
|
+
def uncache
|
59
|
+
remove_dir File.join(ENV['HOME'], '.berkshelf/cookbooks')
|
60
|
+
end
|
61
|
+
|
62
|
+
desc "clean [PATH = #{ Util::Cookbook::DEFAULT_VENDOR }]", 'Remove a local vendor directory'
|
63
|
+
def clean(path = Util::Cookbook::DEFAULT_VENDOR)
|
64
|
+
remove_dir path
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|