bosh-stemcell 1.5.0.pre.1113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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