vanagon 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
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"