vanagon 0.6.1 → 0.6.2

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +130 -23
  3. data/bin/build +0 -4
  4. data/bin/build_host_info +21 -0
  5. data/bin/inspect +25 -0
  6. data/lib/vanagon/driver.rb +17 -8
  7. data/lib/vanagon/engine/base.rb +11 -2
  8. data/lib/vanagon/engine/docker.rb +21 -6
  9. data/lib/vanagon/engine/ec2.rb +75 -0
  10. data/lib/vanagon/engine/hardware.rb +20 -3
  11. data/lib/vanagon/engine/local.rb +18 -3
  12. data/lib/vanagon/engine/pooler.rb +22 -7
  13. data/lib/vanagon/extensions/hashable.rb +41 -0
  14. data/lib/vanagon/extensions/ostruct/json.rb +12 -0
  15. data/lib/vanagon/extensions/set/json.rb +12 -0
  16. data/lib/vanagon/extensions/string.rb +1 -1
  17. data/lib/vanagon/platform.rb +2 -0
  18. data/lib/vanagon/platform/dsl.rb +63 -0
  19. data/lib/vanagon/platform/osx.rb +6 -0
  20. data/lib/vanagon/project/dsl.rb +1 -1
  21. data/lib/vanagon/utilities.rb +10 -4
  22. data/resources/osx/uninstaller.tool.erb +24 -0
  23. data/resources/rpm/project.spec.erb +6 -0
  24. data/spec/lib/vanagon/driver_spec.rb +88 -0
  25. data/spec/lib/vanagon/engine/docker_spec.rb +6 -0
  26. data/spec/lib/vanagon/engine/ec2_spec.rb +33 -0
  27. data/spec/lib/vanagon/engine/hardware_spec.rb +4 -0
  28. data/spec/lib/vanagon/engine/local_spec.rb +5 -0
  29. data/spec/lib/vanagon/engine/pooler_spec.rb +5 -0
  30. data/spec/lib/vanagon/extensions/ostruct/json_spec.rb +16 -0
  31. data/spec/lib/vanagon/extensions/set/json_spec.rb +17 -0
  32. data/spec/lib/vanagon/extensions/string_spec.rb +30 -0
  33. data/spec/lib/vanagon/utilities_spec.rb +1 -1
  34. metadata +22 -6
  35. data/resources/windows/wix/componentgroup.wxs.erb +0 -33
  36. data/resources/windows/wix/project.wxs.erb +0 -34
  37. data/resources/windows/wix/registryEntries.wxs.erb +0 -47
@@ -0,0 +1,75 @@
1
+ require 'aws-sdk'
2
+ require 'erb'
3
+ require 'base64'
4
+ require 'vanagon/engine/base'
5
+
6
+ class Vanagon
7
+ class Engine
8
+ class Ec2 < Base
9
+ attr_accessor :ami, :key_name, :userdata, :key, :key_name, :shutdown_behavior
10
+ attr_accessor :subnet_id, :instance_type
11
+
12
+ def initialize(platform, target = nil)
13
+ super
14
+
15
+ @ami = @platform.aws_ami
16
+ @target_user = @platform.target_user
17
+ @subnet_id = @platform.aws_subnet_id
18
+ @userdata = @platform.aws_user_data
19
+ @region = @platform.aws_region || 'us-east-1'
20
+ @key_name = @platform.aws_key_name
21
+ @key = @platform.aws_key
22
+ @instance_type = @platform.aws_instance_type || "t1.micro"
23
+ @required_attributes = ["ssh_port", "aws_ami", "aws_key_name"]
24
+ @shutdown_behavior = @platform.aws_shutdown_behavior
25
+
26
+ @ec2 = ::Aws::EC2::Client.new(region: @region)
27
+ @resource = ::Aws::EC2::Resource.new(client: @ec2)
28
+ end
29
+
30
+ def name
31
+ 'ec2'
32
+ end
33
+
34
+ def get_userdata
35
+ unless @userdata.nil?
36
+ Base64.encode64(ERB.new(@userdata).result(binding))
37
+ end
38
+ end
39
+
40
+ def instances
41
+ @instances ||= @resource.create_instances({
42
+ image_id: ami,
43
+ min_count: 1,
44
+ max_count: 1,
45
+ key_name: key_name,
46
+ instance_type: instance_type,
47
+ subnet_id: subnet_id,
48
+ user_data: get_userdata,
49
+ monitoring: {
50
+ enabled: false,
51
+ }
52
+ })
53
+ end
54
+
55
+ def instance
56
+ @instance ||= instances.first
57
+ end
58
+
59
+ def select_target
60
+ puts "Instance created id: #{instance.id}"
61
+ puts "Created instance waiting for status ok"
62
+ @ec2.wait_until(:instance_status_ok, instance_ids: [instance.id])
63
+ puts "Instance running"
64
+ @target = instance.private_ip_address
65
+ rescue ::Aws::Waiters::Errors::WaiterFailed => error
66
+ fail "Failed to wait for ec2 instance to start got error #{error}"
67
+ end
68
+
69
+ def teardown
70
+ puts "Destroying instance on AWS id: #{instance.id}"
71
+ instances.batch_terminate!
72
+ end
73
+ end
74
+ end
75
+ end
@@ -51,15 +51,32 @@ class Vanagon
51
51
  end
52
52
 
53
53
  def initialize(platform, target)
54
+ super
55
+
54
56
  Vanagon::Driver.logger.debug "Hardware engine invoked."
55
- @name = 'hardware'
56
- @platform = platform
57
57
  @build_hosts = platform.build_hosts
58
58
  # Redis is the only backend supported in lock_manager currently
59
59
  @lockman = LockManager.new(type: 'redis', server: LOCK_MANAGER_HOST)
60
- super
61
60
  @required_attributes << "build_hosts"
62
61
  end
62
+
63
+ # Get the engine name
64
+ def name
65
+ 'hardware'
66
+ end
67
+
68
+ # Get the first build host name to build on
69
+ def build_host_name
70
+ if @build_host_name.nil?
71
+ validate_platform
72
+ # For now, get the first build host. In the future, lock management
73
+ # will be pushed into the pooler (or something that wraps it), and
74
+ # the hardware engine can go away.
75
+ @build_host_name = @build_hosts.first
76
+ end
77
+
78
+ @build_host_name
79
+ end
63
80
  end
64
81
  end
65
82
  end
@@ -7,9 +7,8 @@ class Vanagon
7
7
  class Local < Base
8
8
 
9
9
  def initialize(platform, target = nil)
10
- @target = target || "local machine"
11
- @name = 'local'
12
- super
10
+ # local engine can't be used with a target
11
+ super(platform, 'local machine')
13
12
 
14
13
  # We inherit a set of required attributes from Base,
15
14
  # and rather than instantiate a new empty array for
@@ -18,6 +17,21 @@ class Vanagon
18
17
  @required_attributes.clear
19
18
  end
20
19
 
20
+ # Get the engine name
21
+ def name
22
+ 'local'
23
+ end
24
+
25
+ # Return the target name to build on
26
+ def build_host_name
27
+ if @build_host_name.nil?
28
+ validate_platform
29
+ @build_host_name = @target
30
+ end
31
+
32
+ @build_host_name
33
+ end
34
+
21
35
  # Dispatches the command for execution
22
36
  def dispatch(command, return_output = false)
23
37
  Vanagon::Utilities.local_command(command, return_command_output: return_output)
@@ -31,6 +45,7 @@ class Vanagon
31
45
  FileUtils.mkdir_p("output")
32
46
  FileUtils.cp_r(Dir.glob("#{@remote_workdir}/output/*"), "output/")
33
47
  end
48
+
34
49
  end
35
50
  end
36
51
  end
@@ -7,11 +7,26 @@ class Vanagon
7
7
 
8
8
  # The vmpooler_template is required to use the pooler engine
9
9
  def initialize(platform, target = nil)
10
+ super
11
+
10
12
  @pooler = "http://vmpooler.delivery.puppetlabs.net"
11
13
  @token = load_token
12
- super
13
14
  @required_attributes << "vmpooler_template"
14
- @name = 'pooler'
15
+ end
16
+
17
+ # Get the engine name
18
+ def name
19
+ 'pooler'
20
+ end
21
+
22
+ # Return the vmpooler template name to build on
23
+ def build_host_name
24
+ if @build_host_template_name.nil?
25
+ validate_platform
26
+ @build_host_template_name = @platform.vmpooler_template
27
+ end
28
+
29
+ @build_host_template_name
15
30
  end
16
31
 
17
32
  # This method loads the pooler token from one of two locations
@@ -35,12 +50,12 @@ class Vanagon
35
50
  response = Vanagon::Utilities.http_request(
36
51
  "#{@pooler}/vm",
37
52
  'POST',
38
- '{"' + @platform.vmpooler_template + '":"1"}',
53
+ '{"' + build_host_name + '":"1"}',
39
54
  { 'X-AUTH-TOKEN' => @token }
40
55
  )
41
56
  if response["ok"]
42
- @target = response[@platform.vmpooler_template]['hostname'] + '.' + response['domain']
43
- Vanagon::Driver.logger.info "Reserving #{@target} (#{@platform.vmpooler_template}) [#{@token ? 'token used' : 'no token used'}]"
57
+ @target = response[build_host_name]['hostname'] + '.' + response['domain']
58
+ Vanagon::Driver.logger.info "Reserving #{@target} (#{build_host_name}) [#{@token ? 'token used' : 'no token used'}]"
44
59
 
45
60
  tags = {
46
61
  'tags' => {
@@ -51,13 +66,13 @@ class Vanagon
51
66
  }
52
67
 
53
68
  Vanagon::Utilities.http_request(
54
- "#{@pooler}/vm/#{response[@platform.vmpooler_template]['hostname']}",
69
+ "#{@pooler}/vm/#{response[build_host_name]['hostname']}",
55
70
  'PUT',
56
71
  tags.to_json,
57
72
  { 'X-AUTH-TOKEN' => @token }
58
73
  )
59
74
  else
60
- raise Vanagon::Error, "Something went wrong getting a target vm to build on, maybe the pool for #{@platform.vmpooler_template} is empty?"
75
+ raise Vanagon::Error, "Something went wrong getting a target vm to build on, maybe the pool for #{build_host_name} is empty?"
61
76
  end
62
77
  end
63
78
 
@@ -0,0 +1,41 @@
1
+ # This is bad, and I feel bad. This will let you append
2
+ # broadly useful Hash and JSON casting on any class that
3
+ # includes it. But it's pretty naive, in that it just turns
4
+ # attributes names into keys while retaining their
5
+ # associated values.
6
+ module HashableAttributes
7
+ # @return [Hash] Converts an object to a hash with keys representing
8
+ # each attribute (as symbols) and their corresponding values
9
+ def to_hash
10
+ instance_variables.each_with_object({}) do |var, hash|
11
+ hash[var.to_s.delete("@")] = instance_variable_get(var)
12
+ end
13
+ end
14
+ alias_method :to_h, :to_hash
15
+
16
+ def to_json(*options)
17
+ to_hash.to_json options
18
+ end
19
+ end
20
+
21
+ # Vanagon classes generally don't implement JSON or Hash functionality
22
+ # so those need to be monkey-patched for useful inspection.
23
+ class Vanagon
24
+ class Platform
25
+ include HashableAttributes
26
+ end
27
+
28
+ class Common
29
+ class Pathname
30
+ include HashableAttributes
31
+ end
32
+ end
33
+
34
+ class Component
35
+ include HashableAttributes
36
+ end
37
+
38
+ class Patch
39
+ include HashableAttributes
40
+ end
41
+ end
@@ -0,0 +1,12 @@
1
+ require 'ostruct'
2
+ require 'json'
3
+
4
+ module OpenStructJson
5
+ def to_json(*options)
6
+ to_h.to_json options
7
+ end
8
+ end
9
+
10
+ class OpenStruct
11
+ prepend OpenStructJson
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'set'
2
+ require 'json'
3
+
4
+ module SetJson
5
+ def to_json(*options)
6
+ to_a.to_json *options
7
+ end
8
+ end
9
+
10
+ class Set
11
+ prepend SetJson
12
+ end
@@ -6,6 +6,6 @@
6
6
  class String
7
7
  # @return [String]
8
8
  def undent
9
- gsub(/^.{#{slice(/^ +/).length}}/, '')
9
+ gsub(/^.{#{slice(/^\s+/).length}}/, '')
10
10
  end
11
11
  end
@@ -7,6 +7,8 @@ class Vanagon
7
7
  attr_accessor :servicetype, :patch, :architecture, :codename, :os_name, :os_version
8
8
  attr_accessor :docker_image, :ssh_port, :rpmbuild, :install, :platform_triple
9
9
  attr_accessor :target_user, :package_type, :find, :sort, :build_hosts, :copy, :cross_compiled
10
+ attr_accessor :aws_ami, :aws_user_data, :aws_shutdown_behavior, :aws_key_name, :aws_region, :aws_key
11
+ attr_accessor :aws_instance_type, :aws_vpc_id, :aws_subnet_id
10
12
 
11
13
  # Platform names currently contain some information about the platform. Fields
12
14
  # within the name are delimited by the '-' character, and this regex can be used to
@@ -228,6 +228,55 @@ class Vanagon
228
228
  @platform.docker_image = name
229
229
  end
230
230
 
231
+ # Set the ami for the platform to use
232
+ #
233
+ # @param ami [String] the ami id used.
234
+ def aws_ami(ami)
235
+ @platform.aws_ami = ami
236
+ end
237
+
238
+ # Set the user data used in AWS to do setup. Like cloud-config
239
+ #
240
+ # @param userdata [String] a string used to send to the node to do the intial setup
241
+ def aws_user_data(userdata)
242
+ @platform.aws_user_data = userdata
243
+ end
244
+
245
+ # Set the region, this defaults to us-east-1
246
+ #
247
+ # @param region [String] a string used to setup the region
248
+ def aws_region(region = 'us-east-1')
249
+ @platform.aws_region = region
250
+ end
251
+
252
+ # Set the shutdown behavior for aws. This will default to terminate the instance on shutdown
253
+ #
254
+ # @param shutdown_behavior [String] a string used to set the shutdown behavior
255
+ def aws_shutdown_behavior(shutdown_behavior = 'terminate')
256
+ @platform.aws_shutdown_behavior = shutdown_behavior
257
+ end
258
+
259
+ # Set the key_name used. This should already exist on AWS.
260
+ #
261
+ # @param key_name [String] this defaults to the keyname vanagon. Can be set to any
262
+ def aws_key_name(key_name = 'vanagon')
263
+ @platform.aws_key_name = key_name
264
+ end
265
+
266
+ # Set the instaince type. This defaults to t1.micro which is the free instance
267
+ #
268
+ # @param instance_type [String] a string to define the instaince type
269
+ def aws_instance_type(instance_type = 't1.micro')
270
+ @platform.aws_instance_type = instance_type
271
+ end
272
+
273
+ # Set the subnet_id. Use this to setup a subnet for your VPC to use.
274
+ #
275
+ # @param subnet_id[String] a string to define the subnet_id in AWS
276
+ def aws_subnet_id(subnet_id)
277
+ @platform.aws_subnet_id = subnet_id
278
+ end
279
+
231
280
  # Set the port for ssh to use if it's not 22
232
281
  #
233
282
  # @param port [Integer] port number for ssh
@@ -235,6 +284,20 @@ class Vanagon
235
284
  @platform.ssh_port = port
236
285
  end
237
286
 
287
+ # Set the target user to login with. Defaults to root.
288
+ #
289
+ # @param user[String] a user string to login with.
290
+ def target_user(user = "root")
291
+ @platform.target_user = user
292
+ end
293
+
294
+ # Set the target ip address or hostname to start build
295
+ #
296
+ # @param target_host[String] a host string to login with.
297
+ def target_host(target_host)
298
+ @platform.target_host = target_host
299
+ end
300
+
238
301
  # Set the platform_triple for the platform
239
302
  #
240
303
  # @param triple[String] platform_triple for use in building out compiled
@@ -25,8 +25,11 @@ class Vanagon
25
25
  # Setup build directories
26
26
  ["bash -c 'mkdir -p $(tempdir)/osx/build/{dmg,pkg,scripts,resources,root,payload,plugins}'",
27
27
  "mkdir -p $(tempdir)/osx/build/root/#{project.name}-#{project.version}",
28
+ "mkdir -p $(tempdir)/osx/build/pkg",
28
29
  # Grab distribution xml, scripts and other external resources
29
30
  "cp #{project.name}-installer.xml $(tempdir)/osx/build/",
31
+ #copy the uninstaller to the pkg dir, where eventually the installer will go too
32
+ "cp #{project.name}-uninstaller.tool $(tempdir)/osx/build/pkg/",
30
33
  "cp scripts/* $(tempdir)/osx/build/scripts/",
31
34
  "if [ -d resources/osx/productbuild ] ; then cp -r resources/osx/productbuild/* $(tempdir)/osx/build/; fi",
32
35
  # Unpack the project
@@ -73,6 +76,9 @@ class Vanagon
73
76
  FileUtils.chmod 0755, File.join(script_dir, script_file)
74
77
  end
75
78
 
79
+ erb_file(File.join(VANAGON_ROOT, 'resources', 'osx', 'uninstaller.tool.erb'), File.join(workdir, "#{name}-uninstaller.tool"), false, { :binding => binding })
80
+ FileUtils.chmod 0755, File.join(workdir, "#{name}-uninstaller.tool")
81
+
76
82
  # Probably a better way to do this, but OSX tends to need some extra stuff
77
83
  FileUtils.cp_r("resources/osx/.", resources_dir) if File.exist?("resources/osx/")
78
84
  end
@@ -194,7 +194,7 @@ class Vanagon
194
194
  #
195
195
  # @param name [String] name of component to add. must be present in configdir/components and named $name.rb currently
196
196
  def component(name)
197
- puts "Loading #{name}"
197
+ puts "Loading #{name}" if @project.settings[:verbose]
198
198
  if @include_components.empty? or @include_components.include?(name)
199
199
  component = Vanagon::Component.load_component(name, File.join(Vanagon::Driver.configdir, "components"), @project.settings, @project.platform)
200
200
  @project.components << component
@@ -129,18 +129,24 @@ class Vanagon
129
129
  # @return [true] If the block succeeds, true is returned
130
130
  # @raise [Vanagon::Error] if the block fails after the retries are exhausted, an error is raised
131
131
  def retry_with_timeout(tries = 5, timeout = 1, &blk)
132
- tries.times do
133
- Timeout::timeout(timeout) do
132
+ error = nil
133
+ tries.to_i.times do
134
+ Timeout::timeout(timeout.to_i) do
134
135
  begin
135
136
  blk.call
136
137
  return true
137
- rescue
138
+ rescue => e
138
139
  warn 'An error was encountered evaluating block. Retrying..'
140
+ error = e
139
141
  end
140
142
  end
141
143
  end
142
144
 
143
- raise Vanagon::Error, "Block failed maximum of #{tries} tries. Exiting.."
145
+ message = "Block failed maximum number of #{tries} tries"
146
+ message += "\n with error #{error.message}" unless error.nil?
147
+ message += "\nExiting..."
148
+ raise error, message unless error.nil?
149
+ raise Vanagon::Error, "Block failed maximum number of #{tries} tries"
144
150
  end
145
151
 
146
152
  # Simple wrapper around git command line executes the given commands and
@@ -0,0 +1,24 @@
1
+ #!/bin/bash
2
+
3
+ <%- if has_services? -%>
4
+ <%- get_services.each do |service| -%>
5
+ echo "Unloading service <%= service.name %>..."
6
+ /bin/launchctl unload <%= service.service_file %>
7
+ echo "OK"
8
+ echo "Removing service file <%= service.service_file %>..."
9
+ /bin/rm <%= service.service_file %>
10
+ echo "OK"
11
+ <%- end -%>
12
+ <%- end -%>
13
+
14
+ echo "Removing identifier: <%= @identifier-%>.<%= @name -%>..."
15
+ if /usr/sbin/pkgutil --files <%= @identifier-%>.<%= @name -%> >/dev/null 2>&1 ; then
16
+ /usr/sbin/pkgutil --forget <%= @identifier-%>.<%= @name -%>
17
+
18
+ fi
19
+ echo "OK"
20
+
21
+ echo "Removing Files"
22
+ /bin/rm -Rf <%= get_directories.map {|d| d.path}.join(" ").to_s -%>
23
+
24
+ echo "OK"