bosh-stemcell 1.5.0.pre.1113

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.rspec +3 -0
  2. data/README.md +104 -0
  3. data/Vagrantfile +37 -0
  4. data/bosh-stemcell.gemspec +28 -0
  5. data/lib/bosh/stemcell.rb +4 -0
  6. data/lib/bosh/stemcell/archive.rb +59 -0
  7. data/lib/bosh/stemcell/archive_filename.rb +29 -0
  8. data/lib/bosh/stemcell/aws/ami.rb +58 -0
  9. data/lib/bosh/stemcell/aws/light_stemcell.rb +44 -0
  10. data/lib/bosh/stemcell/aws/region.rb +15 -0
  11. data/lib/bosh/stemcell/builder_command.rb +174 -0
  12. data/lib/bosh/stemcell/builder_options.rb +85 -0
  13. data/lib/bosh/stemcell/disk_image.rb +61 -0
  14. data/lib/bosh/stemcell/infrastructure.rb +49 -0
  15. data/lib/bosh/stemcell/operating_system.rb +31 -0
  16. data/lib/bosh/stemcell/stage_collection.rb +154 -0
  17. data/lib/bosh/stemcell/stage_runner.rb +53 -0
  18. data/lib/bosh/stemcell/version.rb +5 -0
  19. data/lib/monkeypatch/serverspec/backend/exec.rb +86 -0
  20. data/spec/assets/fake-stemcell-aws.tgz +0 -0
  21. data/spec/assets/fake-stemcell-vsphere.tgz +0 -0
  22. data/spec/assets/light-fake-stemcell-aws.tgz +0 -0
  23. data/spec/bosh/monkeypatch/serverspec/backend/exec_spec.rb +46 -0
  24. data/spec/bosh/stemcell/archive_filename_spec.rb +42 -0
  25. data/spec/bosh/stemcell/archive_spec.rb +98 -0
  26. data/spec/bosh/stemcell/aws/ami_spec.rb +30 -0
  27. data/spec/bosh/stemcell/aws/light_stemcell_spec.rb +94 -0
  28. data/spec/bosh/stemcell/aws/region_spec.rb +12 -0
  29. data/spec/bosh/stemcell/builder_command_spec.rb +241 -0
  30. data/spec/bosh/stemcell/builder_options_spec.rb +201 -0
  31. data/spec/bosh/stemcell/disk_image_spec.rb +163 -0
  32. data/spec/bosh/stemcell/infrastructure_spec.rb +66 -0
  33. data/spec/bosh/stemcell/operating_system_spec.rb +47 -0
  34. data/spec/bosh/stemcell/stage_collection_spec.rb +213 -0
  35. data/spec/bosh/stemcell/stage_runner_spec.rb +141 -0
  36. data/spec/bosh/stemcell/version_spec.rb +12 -0
  37. data/spec/bosh/stemcell_spec.rb +6 -0
  38. data/spec/spec_helper.rb +6 -0
  39. data/spec/stemcells/aws_spec.rb +9 -0
  40. data/spec/stemcells/centos_spec.rb +131 -0
  41. data/spec/stemcells/openstack_spec.rb +9 -0
  42. data/spec/stemcells/ubuntu_spec.rb +143 -0
  43. data/spec/stemcells/vsphere_spec.rb +9 -0
  44. data/spec/support/rspec_fire.rb +9 -0
  45. data/spec/support/serverspec.rb +4 -0
  46. data/spec/support/spec_assets.rb +11 -0
  47. data/spec/support/stemcell_image.rb +26 -0
  48. data/spec/support/stemcell_shared_examples.rb +43 -0
  49. data/spec/support/stub_env.rb +5 -0
  50. metadata +236 -0
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format progress
3
+ --profile
data/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # bosh-stemcell
2
+
3
+ Tools for creating stemcells
4
+
5
+ ## Building a stemcell
6
+
7
+ #### Once-off manual steps:
8
+
9
+ 0. Upload a keypair called "bosh" to AWS that you'll use to connect to the remote vm later
10
+ 0. Create "bosh-stemcell" security group on AWS to allow SSH access to the stemcell (once per AWS account)
11
+ 0. Add instructions to set BOSH_AWS_... environment variables
12
+ 0. Install the vagrant plugins we use:
13
+
14
+ vagrant plugin install vagrant-aws --plugin-version 0.3.0
15
+
16
+ #### Bring up the vagrant stemcell building VM
17
+
18
+ From a fresh copy of the bosh repo:
19
+
20
+ export BOSH_AWS_ACCESS_KEY_ID=YOUR-AWS-ACCESS-KEY
21
+ export BOSH_AWS_SECRET_ACCESS_KEY=YOUR-AWS-SECRET-KEY
22
+ cd bosh-stemcell
23
+ vagrant up local
24
+
25
+ #### Build the stemcell from inside the VM
26
+
27
+ vagrant ssh -c '
28
+ cd /bosh
29
+ bundle install --local
30
+ CANDIDATE_BUILD_NUMBER=1045 http_proxy=http://localhost:3142/ bundle exec rake ci:build_stemcell[vsphere,centos]
31
+ ' local
32
+
33
+ # Run the stemcell locally with Fusion
34
+
35
+ VMware Fusion is the preferred local virtual environment. You can [purchase it here](http://www.vmware.com/products/fusion/). Once you have Fusion installed:
36
+
37
+ * Unpack the stemcell tarball.
38
+ * Rename the new `image` file to `image.tgz` and untar that as well. You should now have an `image.ovf` file.
39
+ * Start VMware Fusion and import this file (**File** -> **Import**)
40
+ * Save the imported OVF as `image.vmwarevm` in the `tmp` folder.
41
+ * You should see a new VM in your VM library.
42
+ * Double click that new VM and it should be booted.
43
+
44
+ ## Add the NIC
45
+
46
+ Before starting the VM in VirtualBox:
47
+
48
+ 1. Add a network interface (**Virtual Machine** > **Settings** > **Add Device...** > **Network Adapter**)
49
+ 1. Select NAT networking (the default)
50
+
51
+ # Run the stemcell locally with VirtualBox
52
+
53
+ ## Import the VM
54
+
55
+ - The stemcell is dropped into `bosh/tmp/bosh-stemcell-???-vsphere-esxi-centos.tgz`.
56
+ - Untar that file. You now have a file named `image`.
57
+ - Rename that to `image.tgz`, and untar *that* file. You now have a file named `image.ovf`.
58
+ - Double click on that file to open it in VirtualBox.
59
+
60
+ ## Add the NIC
61
+
62
+ Before starting the VM in VirtualBox:
63
+
64
+ 1. Add a network interface (**Settings** > **Network** > **Adapter 1** > **Enable**)
65
+ 1. Select NAT networking (the default)
66
+ 1. Click on advanced
67
+ 1. Click on Port Forwarding and enable the following rule:
68
+ * Name: SSH
69
+ * Protocol: TCP
70
+ * Host IP: 127.0.0.1
71
+ * Host Port: 3333
72
+ * Guest IP: blank
73
+ * Guest Port: 22
74
+
75
+ # Boot the VM
76
+
77
+ Save the configuration and boot the VM. Once you're booted, login as `root`/`c1oudc0w`.
78
+
79
+ ## Configure network locally
80
+
81
+ ### Fusion
82
+
83
+ ```bash
84
+ $ grep subnet /Library/Preferences/VMware\ Fusion/*/dhcpd.conf
85
+ /Library/Preferences/VMware Fusion/vmnet1/dhcpd.conf:subnet 172.16.0.0 netmask 255.255.255.0 {
86
+ /Library/Preferences/VMware Fusion/vmnet8/dhcpd.conf:subnet 172.16.210.0 netmask 255.255.255.0 {
87
+ ```
88
+
89
+ ```bash
90
+ ifconfig eth0 172.16.210.30/24 up
91
+ route add default gw 172.16.210.2 eth0
92
+ ```
93
+
94
+ ### VirtualBox
95
+
96
+ You'll want to pick an IP that's not in use by your stemcell building vm. 10.0.2.30 *should* be fine.
97
+
98
+ ```bash
99
+ ifconfig eth0 10.0.2.30/24 up
100
+ route add default gw 10.0.2.2 eth0
101
+ ```
102
+
103
+ Test the network with `ping 8.8.8.8`
104
+
data/Vagrantfile ADDED
@@ -0,0 +1,37 @@
1
+ Vagrant.configure('2') do |config|
2
+ env = ENV.to_hash
3
+ config.vm.box = 'bosh-stemcell'
4
+ config.ssh.username = 'ubuntu'
5
+
6
+ config.vm.define :local do |local|
7
+ local.vm.box_url = 'https://bosh-jenkins-artifacts.s3.amazonaws.com/bosh-vagrant-boxes/bosh-stemcell-virtualbox.box'
8
+ local.vm.provider :virtualbox do |virtualbox|
9
+ virtualbox.customize ['modifyvm', :id, '--cpus', '2']
10
+ virtualbox.customize ['modifyvm', :id, '--memory', '2048']
11
+ end
12
+ end
13
+
14
+ config.vm.define :remote do |remote|
15
+ remote.vm.box_url = 'https://bosh-jenkins-artifacts.s3.amazonaws.com/bosh-vagrant-boxes/bosh-stemcell-aws.box'
16
+ remote.vm.provider :aws do |aws, override|
17
+ aws.instance_type = 'c1.medium'
18
+ aws.access_key_id = env.fetch('BOSH_AWS_ACCESS_KEY_ID')
19
+ aws.secret_access_key = env.fetch('BOSH_AWS_SECRET_ACCESS_KEY')
20
+ aws.keypair_name = 'bosh'
21
+ aws.security_groups = ['bosh-stemcell']
22
+ aws.tags = { 'Name' => 'bosh-stemcell' }
23
+
24
+ override.ssh.username = 'ubuntu'
25
+ override.ssh.private_key_path = '~/.ssh/id_rsa_bosh'
26
+ end
27
+ end
28
+
29
+ config.vm.provision :shell do |shell|
30
+ shell.inline = <<-BASH
31
+ sudo mkdir -p /mnt/stemcells
32
+ sudo chown -R ubuntu /mnt/stemcells
33
+ BASH
34
+ end
35
+
36
+ config.vm.synced_folder '../', '/bosh'
37
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ version = File.read(File.expand_path('../../BOSH_VERSION', __FILE__)).strip
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = 'bosh-stemcell'
6
+ spec.version = version
7
+ spec.authors = 'Pivotal'
8
+ spec.email = 'support@cloudfoundry.com'
9
+ spec.description = 'Bosh::Stemcell provides tools to manage stemcells'
10
+ spec.summary = 'Bosh::Stemcell provides tools to manage stemcells'
11
+ spec.homepage = 'https://github.com/cloudfoundry/bosh'
12
+ spec.license = 'Apache 2.0'
13
+
14
+ spec.required_ruby_version = Gem::Requirement.new('>= 1.9.3')
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = %w[lib]
20
+
21
+ spec.add_dependency 'bosh_aws_cpi', "~>#{version}"
22
+ spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'rspec'
24
+ spec.add_development_dependency 'rspec-fire'
25
+ spec.add_development_dependency 'timecop'
26
+ spec.add_development_dependency 'serverspec'
27
+ spec.add_development_dependency 'foodcritic'
28
+ end
@@ -0,0 +1,4 @@
1
+ module Bosh
2
+ module Stemcell
3
+ end
4
+ end
@@ -0,0 +1,59 @@
1
+ require 'rake/file_utils_ext'
2
+ require 'yaml'
3
+ require 'bosh/stemcell/aws/region'
4
+
5
+ module Bosh::Stemcell
6
+ class Archive
7
+ attr_reader :path
8
+
9
+ def initialize(path = '')
10
+ @path = path
11
+ validate_stemcell
12
+ end
13
+
14
+ def manifest
15
+ @manifest ||= Psych.load(`tar -Oxzf #{path} stemcell.MF`)
16
+ end
17
+
18
+ def name
19
+ manifest.fetch('name')
20
+ end
21
+
22
+ def infrastructure
23
+ cloud_properties.fetch('infrastructure')
24
+ end
25
+
26
+ def version
27
+ cloud_properties.fetch('version')
28
+ end
29
+
30
+ def light?
31
+ infrastructure == 'aws' && ami_id
32
+ end
33
+
34
+ def ami_id(region = Aws::Region::DEFAULT)
35
+ cloud_properties.fetch('ami', {}).fetch(region, nil)
36
+ end
37
+
38
+ def extract(tar_options = {}, &block)
39
+ Dir.mktmpdir do |tmp_dir|
40
+ tar_cmd = "tar xzf #{path} --directory #{tmp_dir}"
41
+ tar_cmd << " --exclude=#{tar_options[:exclude]}" if tar_options.has_key?(:exclude)
42
+
43
+ Rake::FileUtilsExt.sh(tar_cmd)
44
+
45
+ block.call(tmp_dir, manifest)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def cloud_properties
52
+ manifest.fetch('cloud_properties')
53
+ end
54
+
55
+ def validate_stemcell
56
+ raise "Cannot find file `#{path}'" unless File.exists?(path)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,29 @@
1
+ module Bosh::Stemcell
2
+ class ArchiveFilename
3
+ def initialize(version, infrastructure, operating_system, base_name, light)
4
+ @version = version
5
+ @infrastructure = infrastructure
6
+ @operating_system = operating_system
7
+ @base_name = base_name
8
+ @light = light
9
+ end
10
+
11
+ def to_s
12
+ stemcell_filename_parts = [name, version, infrastructure.name, infrastructure.hypervisor, operating_system.name]
13
+
14
+ "#{stemcell_filename_parts.join('-')}.tgz"
15
+ end
16
+
17
+ private
18
+
19
+ def name
20
+ light ? "light-#{base_name}" : base_name
21
+ end
22
+
23
+ attr_reader :base_name,
24
+ :version,
25
+ :infrastructure,
26
+ :operating_system,
27
+ :light
28
+ end
29
+ end
@@ -0,0 +1,58 @@
1
+ require 'cloud'
2
+ require 'bosh_aws_cpi'
3
+ require 'ostruct'
4
+ require 'yaml'
5
+ require 'rake'
6
+ require 'bosh/stemcell/aws/region'
7
+
8
+ module Bosh::Stemcell::Aws
9
+ class Ami
10
+ attr_reader :stemcell
11
+
12
+ def initialize(stemcell, region)
13
+ @stemcell = stemcell
14
+ @region = region
15
+ end
16
+
17
+ def publish
18
+ cloud_config = OpenStruct.new(logger: Logger.new('ami.log'), task_checkpoint: nil)
19
+ Bosh::Clouds::Config.configure(cloud_config)
20
+
21
+ cloud = Bosh::Clouds::Provider.create('aws', options)
22
+
23
+ stemcell.extract do |tmp_dir, stemcell_manifest|
24
+ ami_id = cloud.create_stemcell("#{tmp_dir}/image", stemcell_manifest['cloud_properties'])
25
+ cloud.ec2.images[ami_id].public = true
26
+ ami_id
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :region
33
+
34
+ def options
35
+ # just fake the registry struct, as we don't use it
36
+ {
37
+ 'aws' => aws,
38
+ 'registry' => {
39
+ 'endpoint' => 'http://fake.registry',
40
+ 'user' => 'fake',
41
+ 'password' => 'fake'
42
+ }
43
+ }
44
+ end
45
+
46
+ def aws
47
+ access_key_id = ENV['BOSH_AWS_ACCESS_KEY_ID']
48
+ secret_access_key = ENV['BOSH_AWS_SECRET_ACCESS_KEY']
49
+
50
+ {
51
+ 'default_key_name' => 'fake',
52
+ 'region' => region.name,
53
+ 'access_key_id' => access_key_id,
54
+ 'secret_access_key' => secret_access_key
55
+ }
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,44 @@
1
+ require 'rake/file_utils'
2
+ require 'yaml'
3
+ require 'common/deep_copy'
4
+ require 'bosh/stemcell/aws/region'
5
+ require 'bosh/stemcell/aws/ami'
6
+
7
+ module Bosh::Stemcell::Aws
8
+ class LightStemcell
9
+ def initialize(stemcell)
10
+ @stemcell = stemcell
11
+ end
12
+
13
+ def write_archive
14
+ stemcell.extract(exclude: 'image') do |extracted_stemcell_dir|
15
+ Dir.chdir(extracted_stemcell_dir) do
16
+ FileUtils.touch('image', verbose: true)
17
+
18
+ File.open('stemcell.MF', 'w') do |out|
19
+ Psych.dump(manifest, out)
20
+ end
21
+
22
+ Rake::FileUtilsExt.sh("sudo tar cvzf #{path} *")
23
+ end
24
+ end
25
+ end
26
+
27
+ def path
28
+ File.join(File.dirname(stemcell.path), "light-#{File.basename(stemcell.path)}")
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :stemcell
34
+
35
+ def manifest
36
+ region = Region.new
37
+ ami = Ami.new(stemcell, region)
38
+ ami_id = ami.publish
39
+ manifest = Bosh::Common::DeepCopy.copy(stemcell.manifest)
40
+ manifest['cloud_properties']['ami'] = { region.name => ami_id }
41
+ manifest
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ require 'net/http'
2
+
3
+ module Bosh
4
+ module Stemcell
5
+ module Aws
6
+ class Region
7
+ DEFAULT = 'us-east-1'
8
+
9
+ def name
10
+ Net::HTTP.get('169.254.169.254', '/latest/meta-data/placement/availability-zone').chop
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,174 @@
1
+ require 'fileutils'
2
+
3
+ require 'bosh/core/shell'
4
+ require 'bosh/stemcell/builder_options'
5
+ require 'bosh/stemcell/disk_image'
6
+ require 'bosh/stemcell/infrastructure'
7
+ require 'bosh/stemcell/operating_system'
8
+ require 'bosh/stemcell/stage_collection'
9
+ require 'bosh/stemcell/stage_runner'
10
+
11
+ module Bosh::Stemcell
12
+ class BuilderCommand
13
+ def initialize(env, options)
14
+ @environment = env
15
+ @infrastructure = Infrastructure.for(options.fetch(:infrastructure_name))
16
+ @operating_system = OperatingSystem.for(options.fetch(:operating_system_name))
17
+
18
+ @stemcell_builder_options = BuilderOptions.new(
19
+ env,
20
+ tarball: options.fetch(:release_tarball_path),
21
+ stemcell_version: options.fetch(:version),
22
+ infrastructure: infrastructure,
23
+ operating_system: operating_system,
24
+ )
25
+ @shell = Bosh::Core::Shell.new
26
+ end
27
+
28
+ def build
29
+ sanitize
30
+
31
+ prepare_build_root
32
+
33
+ prepare_build_path
34
+
35
+ copy_stemcell_builder_to_build_path
36
+
37
+ prepare_work_root
38
+
39
+ persist_settings_for_bash
40
+
41
+ stage_collection = StageCollection.new(
42
+ infrastructure: infrastructure,
43
+ operating_system: operating_system
44
+ )
45
+ stage_runner = StageRunner.new(
46
+ build_path: build_path,
47
+ command_env: command_env,
48
+ settings_file: settings_path,
49
+ work_path: work_root
50
+ )
51
+ stage_runner.configure_and_apply(stage_collection.operating_system_stages)
52
+ stage_runner.configure_and_apply(stage_collection.infrastructure_stages)
53
+ system(rspec_command) || raise('Stemcell specs failed')
54
+
55
+ stemcell_file
56
+ end
57
+
58
+ def chroot_dir
59
+ File.join(work_path, 'chroot')
60
+ end
61
+
62
+ private
63
+
64
+ attr_reader(
65
+ :shell,
66
+ :environment,
67
+ :infrastructure,
68
+ :operating_system,
69
+ :stemcell_builder_options
70
+ )
71
+
72
+ def rspec_command
73
+ [
74
+ "cd #{File.expand_path('../../..', File.dirname(__FILE__))};",
75
+ "STEMCELL_IMAGE=#{image_file_path}",
76
+ 'bundle exec rspec -fd',
77
+ "spec/stemcells/#{operating_system.name}_spec.rb",
78
+ "spec/stemcells/#{infrastructure.name}_spec.rb",
79
+ ].join(' ')
80
+ end
81
+
82
+ def image_file_path
83
+ File.join(work_path, settings['stemcell_image_name'])
84
+ end
85
+
86
+ def image_mount_point
87
+ File.join(work_path, 'mnt')
88
+ end
89
+
90
+ def sanitize
91
+ FileUtils.rm_rf('*.tgz')
92
+
93
+ system("sudo umount #{File.join(work_path, 'mnt/tmp/grub', settings['stemcell_image_name'])} 2> /dev/null")
94
+ system("sudo umount #{image_mount_point} 2> /dev/null")
95
+ system("sudo rm -rf #{base_directory}")
96
+ end
97
+
98
+ def settings
99
+ stemcell_builder_options.default
100
+ end
101
+
102
+ def base_directory
103
+ File.join('/mnt', 'stemcells', infrastructure.name, infrastructure.hypervisor, operating_system.name)
104
+ end
105
+
106
+ def build_root
107
+ File.join(base_directory, 'build')
108
+ end
109
+
110
+ def work_root
111
+ File.join(base_directory, 'work')
112
+ end
113
+
114
+ def prepare_build_root
115
+ FileUtils.mkdir_p(build_root, verbose: true)
116
+ end
117
+
118
+ def prepare_work_root
119
+ FileUtils.mkdir_p(work_root, verbose: true)
120
+ end
121
+
122
+ def build_path
123
+ File.join(build_root, 'build')
124
+ end
125
+
126
+ def work_path
127
+ File.join(work_root, 'work')
128
+ end
129
+
130
+ def prepare_build_path
131
+ FileUtils.rm_rf(build_path, verbose: true) if Dir.exists?(build_path)
132
+ FileUtils.mkdir_p(build_path, verbose: true)
133
+ end
134
+
135
+ def stemcell_builder_source_dir
136
+ File.join(File.expand_path('../../../../..', __FILE__), 'stemcell_builder')
137
+ end
138
+
139
+ def copy_stemcell_builder_to_build_path
140
+ FileUtils.cp_r(Dir.glob("#{stemcell_builder_source_dir}/*"), build_path, preserve: true, verbose: true)
141
+ end
142
+
143
+ def settings_path
144
+ File.join(build_path, 'etc', 'settings.bash')
145
+ end
146
+
147
+ def persist_settings_for_bash
148
+ File.open(settings_path, 'a') do |f|
149
+ f.printf("\n# %s\n\n", '=' * 20)
150
+ settings.each do |k, v|
151
+ f.print "#{k}=#{v}\n"
152
+ end
153
+ end
154
+ end
155
+
156
+ def command_env
157
+ "env #{hash_as_bash_env(proxy_settings_from_environment)}"
158
+ end
159
+
160
+ def stemcell_file
161
+ File.join(work_path, settings['stemcell_tgz'])
162
+ end
163
+
164
+ def proxy_settings_from_environment
165
+ keep = %w(HTTP_PROXY NO_PROXY)
166
+
167
+ environment.select { |k| keep.include?(k.upcase) }
168
+ end
169
+
170
+ def hash_as_bash_env(env)
171
+ env.map { |k, v| "#{k}='#{v}'" }.join(' ')
172
+ end
173
+ end
174
+ end