vcloud-launcher 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /.ruby-version
7
7
  /Gemfile.lock
8
8
  /bundle/
9
+ /coverage/
9
10
  /pkg/
10
11
  fog_integration_test.config
11
12
  .idea
@@ -2,5 +2,7 @@
2
2
  language: ruby
3
3
  rvm:
4
4
  - 1.9.3
5
+ - 2.0.0
6
+ - 2.1.2
5
7
  notifications:
6
8
  email: false
@@ -1,3 +1,16 @@
1
+ ## 0.2.0 (2014-07-14)
2
+
3
+ Features:
4
+
5
+ - `vcloud-configure-edge --version` now only returns the version string
6
+ and no usage information.
7
+
8
+ API changes:
9
+
10
+ - New `Vcloud::Launcher::Preamble` class for generating preambles, containing
11
+ logic moved from vCloud Core. Thanks to @bazbremner for this contribution.
12
+ - The minimum required Ruby version is now 1.9.3.
13
+
1
14
  ## 0.1.0 (2014-06-02)
2
15
 
3
16
  Features:
@@ -0,0 +1,66 @@
1
+ # Contributing to vCloud Launcher
2
+
3
+ We really welcome contributions.
4
+
5
+ ## A quick guide on how to contribute
6
+
7
+ 1. Clone the repo:
8
+
9
+ git clone git@github.com:gds-operations/vcloud-launcher.git
10
+
11
+ 2. Run `bundle` to get the required dependecies
12
+
13
+ 3. Run the tests. Pull requests that add features must include unit tests,
14
+ so it is good to ensure you've got them passing to begin with.
15
+
16
+ bundle exec rake
17
+
18
+ If you have access to a live environment for testing, it would be great
19
+ if you could run the integration tests too - for more details on the
20
+ set-up for that, please see the [integration tests README]
21
+ (https://github.com/gds-operations/vcloud-launcher/blob/master/spec/integration/README.md)
22
+
23
+ 4. Add your functionality or bug fix and a test for your change. Only refactoring and
24
+ documentation changes do not require tests. If the functionality is at all complicated
25
+ then it is likely that more than one test will be required. If you would like help
26
+ with writing tests please do ask us.
27
+
28
+ 5. Make sure all the tests pass, including the integration tests if possible.
29
+
30
+ 6. Update the [CHANGELOG](https://github.com/gds-operations/vcloud-launcher/blob/master/CHANGELOG.md)
31
+ with a short description of what the change is. This may be a feature, a bugfix, or an
32
+ API change. If your change is documenation or refactoring, you do not need to add a line
33
+ to the CHANGELOG.
34
+
35
+ 7. Fork the repo, push to your fork, and submit a pull request.
36
+
37
+ ## How soon will we respond?
38
+
39
+ We will comment on your pull request within two working days. However, we might not be able to review it immediately.
40
+
41
+ We may come back to you with comments and suggestions, and if we would like you to make changes, we will close the pull request as well as adding details of the changes we'd like you to make.
42
+
43
+ If you feel your pull request has been outstanding too long, please feel free to bump it by making a comment on it.
44
+
45
+ ## Guidelines for making a pull request
46
+
47
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
48
+ "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
49
+ interpreted as described in RFC 2119.
50
+
51
+ ## In order for a pull request to be accepted, it MUST
52
+
53
+ - Include at least one test (unless it is documentation or refactoring). If you have any questions about how to write tests, please ask us, we will be happy to help
54
+ - Follow our [Git style guide](https://github.com/alphagov/styleguides/blob/master/git.md)
55
+ - Include a clear summary in the pull request comments as to what the change is and why
56
+ you are making it
57
+ - Be readable - we might ask you to change unclear variable names or obscure syntactic sugar
58
+ - Have [good commit messages](http://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message)
59
+ that explain the change being made in that commit. Don't be afraid to write a lot in the
60
+ detail.
61
+
62
+ ## In order for a pull request to be accepted, it SHOULD
63
+
64
+ - Include a line in the CHANGELOG unless it is a refactoring or documentation change
65
+ - If it is code, follow our [Ruby style guide](https://github.com/alphagov/styleguides/blob/master/ruby.md)
66
+ - If it is documentation, follow the [GDS content style guide](https://www.gov.uk/design-principles/style-guide/style-points)
data/Gemfile CHANGED
@@ -1,9 +1,9 @@
1
- source 'http://rubygems.org'
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
5
  if ENV['VCLOUD_CORE_DEV_MASTER']
6
- gem 'vcloud-core', :git => 'git@github.com:alphagov/vcloud-core.git', :branch => 'master'
6
+ gem 'vcloud-core', :git => 'git@github.com:gds-operations/vcloud-core.git', :branch => 'master'
7
7
  elsif ENV['VCLOUD_CORE_DEV_LOCAL']
8
8
  gem 'vcloud-core', :path => '../vcloud-core'
9
9
  end
data/Rakefile CHANGED
@@ -1,23 +1,17 @@
1
- require 'cucumber/rake/task'
2
1
  require 'rspec/core/rake_task'
3
2
  require 'gem_publisher'
4
3
 
5
- task :default => [:rubocop, :spec, :features]
4
+ task :default => [:rubocop, :spec]
6
5
  task :integration => ['integration:all']
7
6
 
8
7
  RSpec::Core::RakeTask.new(:spec) do |task|
9
8
  # Set a bogus Fog credential, otherwise it's possible for the unit
10
- # tests to accidentially run (and succeed against!) an actual
9
+ # tests to accidentially run (and succeed against!) an actual
11
10
  # environment, if Fog connection is not stubbed correctly.
12
11
  ENV['FOG_CREDENTIAL'] = 'random_nonsense_owiejfoweijf'
13
12
  task.pattern = FileList['spec/vcloud/**/*_spec.rb']
14
13
  end
15
14
 
16
- Cucumber::Rake::Task.new(:features) do |t|
17
- t.cucumber_opts = "--format pretty --no-source"
18
- t.fork = false
19
- end
20
-
21
15
  RSpec::Core::RakeTask.new('integration:quick') do |t|
22
16
  t.rspec_opts = %w(--tag ~take_too_long)
23
17
  t.pattern = FileList['spec/integration/**/*_spec.rb']
@@ -33,6 +27,6 @@ task :publish_gem do
33
27
  end
34
28
 
35
29
  require 'rubocop/rake_task'
36
- Rubocop::RakeTask.new(:rubocop) do |task|
30
+ RuboCop::RakeTask.new(:rubocop) do |task|
37
31
  task.options = ['--lint']
38
32
  end
@@ -1,48 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'rubygems'
4
- require 'bundler/setup'
5
- require 'optparse'
6
- require 'methadone'
7
-
8
3
  require 'vcloud/launcher'
9
4
 
10
- class App
11
- include Methadone::Main
12
- include Methadone::CLILogging
13
-
14
- main do |org_config_file|
15
- Vcloud::Launcher::Launch.new.run(org_config_file, options)
16
- end
17
-
18
- on("-x", "--dont-power-on", "Do not power on vApps (default is to power on)")
19
- on("-c", "--continue-on-error", "Continue on error ( default is false) ")
20
- on("-q", "--quiet", "Quiet output - only report errors")
21
- on("-v", "--verbose", "Verbose output")
22
-
23
- arg :org_config_file
24
-
25
- examples_dir = File.absolute_path(
26
- File.join(
27
- File.dirname(__FILE__),
28
- "..",
29
- "examples",
30
- File.basename(__FILE__),
31
- ))
32
-
33
- description "
34
- vcloud-launch takes a configuration describing a vCloud Org,
35
- and tries to make it a reality.
36
-
37
- See https://github.com/alphagov/vcloud-tools for more info
38
-
39
- Example configuration files can be found in:
40
- #{examples_dir}
41
- "
42
-
43
- version Vcloud::Launcher::VERSION
44
-
45
- #use_log_level_option
46
-
47
- go!
48
- end
5
+ Vcloud::Launcher::Cli.new(ARGV).run
@@ -1,11 +1,13 @@
1
1
  require 'vcloud/fog'
2
2
  require 'vcloud/core'
3
3
 
4
+ require 'vcloud/launcher/cli'
4
5
  require 'vcloud/launcher/schema/vm'
5
6
  require 'vcloud/launcher/schema/vapp'
6
7
  require 'vcloud/launcher/schema/launcher_vapps'
7
8
 
8
9
  require 'vcloud/launcher/launch'
10
+ require 'vcloud/launcher/preamble'
9
11
  require 'vcloud/launcher/vm_orchestrator'
10
12
  require 'vcloud/launcher/vapp_orchestrator'
11
13
 
@@ -0,0 +1,103 @@
1
+ require 'optparse'
2
+
3
+ module Vcloud
4
+ module Launcher
5
+ class Cli
6
+
7
+ def initialize(argv_array)
8
+ @config_file = nil
9
+ @usage_text = nil
10
+ @options = {
11
+ "dont-power-on" => false,
12
+ "continue-on-error" => false,
13
+ "quiet" => false,
14
+ "verbose" => false,
15
+ }
16
+
17
+ parse(argv_array)
18
+ end
19
+
20
+ def run
21
+ begin
22
+ Vcloud::Launcher::Launch.new.run(@config_file, @options)
23
+ rescue => error_msg
24
+ $stderr.puts(error_msg)
25
+ exit 1
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def parse(args)
32
+ opt_parser = OptionParser.new do |opts|
33
+ examples_dir = File.absolute_path(
34
+ File.join(
35
+ File.dirname(__FILE__),
36
+ "..",
37
+ "..",
38
+ "..",
39
+ "examples",
40
+ File.basename($0),
41
+ ))
42
+
43
+ opts.banner = <<-EOS
44
+ Usage: #{$0} [options] config_file
45
+
46
+ vcloud-launch takes a configuration describing a vCloud Org,
47
+ and tries to make it a reality.
48
+
49
+ See https://github.com/gds-operations/vcloud-tools for more info
50
+
51
+ Example configuration files can be found in:
52
+ #{examples_dir}
53
+ EOS
54
+
55
+ opts.separator ""
56
+ opts.separator "Options:"
57
+
58
+ opts.on("-x", "--dont-power-on", "Do not power on vApps (default is to power on)") do
59
+ @options["dont-power-on"] = true
60
+ end
61
+
62
+ opts.on("-c", "--continue-on-error", "Continue on error (default is false)") do
63
+ @options["continue-on-error"] = true
64
+ end
65
+
66
+ opts.on("-q", "--quiet", "Quiet output - only report errors") do
67
+ @options["quiet"] = true
68
+ end
69
+
70
+ opts.on("-v", "--verbose", "Verbose output") do
71
+ @options["verbose"] = true
72
+ end
73
+
74
+ opts.on("-h", "--help", "Print usage and exit") do
75
+ $stderr.puts opts
76
+ exit
77
+ end
78
+
79
+ opts.on("--version", "Display version and exit") do
80
+ puts Vcloud::Launcher::VERSION
81
+ exit
82
+ end
83
+ end
84
+
85
+ @usage_text = opt_parser.to_s
86
+ begin
87
+ opt_parser.parse!(args)
88
+ rescue OptionParser::InvalidOption => e
89
+ exit_error_usage(e)
90
+ end
91
+
92
+ exit_error_usage("must supply config_file") unless args.size == 1
93
+ @config_file = args.first
94
+ end
95
+
96
+ def exit_error_usage(error)
97
+ $stderr.puts "#{$0}: #{error}"
98
+ $stderr.puts @usage_text
99
+ exit 2
100
+ end
101
+ end
102
+ end
103
+ end
@@ -2,13 +2,21 @@ module Vcloud
2
2
  module Launcher
3
3
  class Launch
4
4
 
5
+ class MissingPreambleError < RuntimeError ; end
6
+ class MissingConfigurationError < RuntimeError ; end
7
+
8
+ attr_reader :config
9
+
5
10
  def initialize
6
11
  @config_loader = Vcloud::Core::ConfigLoader.new
7
12
  end
8
13
 
9
14
  def run(config_file = nil, cli_options = {})
10
15
  set_logging_level(cli_options)
11
- config = @config_loader.load_config(config_file, Vcloud::Launcher::Schema::LAUNCHER_VAPPS)
16
+ @config = @config_loader.load_config(config_file, Vcloud::Launcher::Schema::LAUNCHER_VAPPS)
17
+
18
+ validate_config
19
+
12
20
  config[:vapps].each do |vapp_config|
13
21
  Vcloud::Core.logger.info("Provisioning vApp #{vapp_config[:name]}.")
14
22
  begin
@@ -34,6 +42,33 @@ module Vcloud
34
42
  end
35
43
  end
36
44
 
45
+ private
46
+
47
+ def validate_config
48
+ @config[:vapps].each do |vapp_config|
49
+ validate_vapp_config(vapp_config)
50
+ end
51
+ end
52
+
53
+ def validate_vapp_config(vapp_config)
54
+ bootstrap_config = vapp_config.fetch(:bootstrap, nil)
55
+
56
+ return unless bootstrap_config
57
+
58
+ if ! bootstrap_config[:script_path]
59
+ raise MissingConfigurationError, "Preamble script (script_path) not specified"
60
+ end
61
+
62
+ if bootstrap_config[:script_path] && ! File.exist?( bootstrap_config[:script_path])
63
+ raise MissingPreambleError, "Unable to find specified preamble script (#{bootstrap_config[:script_path]})"
64
+ end
65
+
66
+ template_vars = bootstrap_config.fetch(:vars, {})
67
+
68
+ if bootstrap_config[:script_path] && template_vars.empty?
69
+ Vcloud::Core.logger.info("Preamble file/template specified without variables to template.")
70
+ end
71
+ end
37
72
  end
38
73
  end
39
74
  end
@@ -0,0 +1,60 @@
1
+ module Vcloud
2
+ module Launcher
3
+ class Preamble
4
+ class MissingConfigurationError < StandardError ; end
5
+ class MissingTemplateError < StandardError ; end
6
+
7
+ attr_reader :preamble_vars, :script_path
8
+
9
+ def initialize(vapp_name, vm_config)
10
+ @vapp_name = vapp_name
11
+ bootstrap_config = vm_config[:bootstrap]
12
+
13
+ raise MissingConfigurationError if bootstrap_config.nil?
14
+
15
+ @script_path = bootstrap_config.fetch(:script_path, nil)
16
+ raise MissingTemplateError unless @script_path
17
+
18
+ # Missing vars is acceptable - noop template.
19
+ @preamble_vars = bootstrap_config.fetch(:vars, {})
20
+ extra_disks = vm_config.fetch(:extra_disks, {})
21
+
22
+ @preamble_vars.merge!(extra_disks: extra_disks)
23
+
24
+ @script_post_processor = bootstrap_config.fetch(:script_post_processor, nil)
25
+ end
26
+
27
+ def generate
28
+ @script_post_processor ? post_process_erb_output : interpolated_preamble
29
+ end
30
+
31
+ def interpolated_preamble
32
+ @interpolated_preamble = interpolate_erb_file
33
+ end
34
+
35
+ private
36
+
37
+ def interpolate_erb_file
38
+ erb_vars = OpenStruct.new({
39
+ vapp_name: @vapp_name,
40
+ vars: @preamble_vars,
41
+ })
42
+ binding_object = erb_vars.instance_eval { binding }
43
+ template_string = load_erb_file
44
+
45
+ ERB.new(template_string, nil, '>-').result(binding_object)
46
+ end
47
+
48
+ def load_erb_file
49
+ File.read(File.expand_path(@script_path))
50
+ end
51
+
52
+ def post_process_erb_output
53
+ # Open3.capture2, as we just need to return STDOUT of the post_processor_script
54
+ Open3.capture2(
55
+ File.expand_path(@script_post_processor),
56
+ stdin_data: interpolated_preamble).first
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,5 +1,5 @@
1
1
  module Vcloud
2
2
  module Launcher
3
- VERSION = '0.1.0'
3
+ VERSION = '0.2.0'
4
4
  end
5
5
  end
@@ -17,13 +17,18 @@ module Vcloud
17
17
  end
18
18
  @vm.add_extra_disks(vm_config[:extra_disks])
19
19
  @vm.update_metadata(vm_config[:metadata])
20
- @vm.configure_guest_customization_section(
21
- @vm.vapp_name,
22
- vm_config[:bootstrap],
23
- vm_config[:extra_disks]
24
- )
20
+
21
+ preamble = vm_config[:bootstrap] ? generate_preamble(vm_config) : ''
22
+
23
+ @vm.configure_guest_customization_section(preamble)
25
24
  end
26
25
 
26
+ private
27
+
28
+ def generate_preamble(vm_config)
29
+ preamble = ::Vcloud::Launcher::Preamble.new(@vm.vapp_name, vm_config)
30
+ preamble.generate
31
+ end
27
32
  end
28
33
  end
29
34
  end