vaquero_io 0.1.1

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.
@@ -0,0 +1,195 @@
1
+ require 'resolv'
2
+ require 'uri'
3
+ require 'vaquero_io/provision'
4
+
5
+ module VaqueroIo
6
+ # rubocop:disable ClassLength
7
+ class Platform
8
+ include VaqueroIo::Platform::Provision
9
+
10
+ attr_accessor :provider
11
+ attr_accessor :product
12
+ attr_accessor :product_provider, :product_provider_version
13
+ attr_accessor :environments
14
+ attr_accessor :nodename
15
+ attr_accessor :components
16
+ attr_accessor :required
17
+ attr_accessor :env_definition
18
+
19
+ # rubocop:disable MethodLength, LineLength
20
+ def initialize(provider = nil)
21
+ @provider = provider
22
+ fail unless platform_files_exist?
23
+ platform = YAML.load_file(PLATFORMFILE).fetch('platform')
24
+ @product = platform['product']
25
+ @product_provider = platform['provider']
26
+
27
+ # Lazy provider loading if we weren't given one...
28
+ if @provider.nil?
29
+ @provider = VaqueroIo::Provider.new(@product_provider)
30
+ # re-test platform health
31
+ fail unless platform_files_exist?
32
+ end
33
+
34
+ @product_provider_version = platform['plugin_version']
35
+ @environments = platform['environments']
36
+ @nodename = platform['nodename']
37
+ @components = platform['components']
38
+ @required = {}
39
+ @provider.definition['structure']['require'].each do |required_file|
40
+ @required[required_file] = YAML.load_file(@provider.definition['structure'][required_file]['path'] + required_file + '.yml').fetch(required_file)
41
+ end
42
+ end
43
+ # rubocop:enable MethodLength, LineLength
44
+
45
+ # rubocop:disable MethodLength, LineLength, CyclomaticComplexity, PerceivedComplexity
46
+ def healthy?(env = '')
47
+ health = ''
48
+ health += (FAIL_PROVIDER + "\n") if @provider.definition['name'] != @product_provider
49
+ puts WARN_PROVIDER_VERSION if @provider.definition['version'] != @product_provider_version
50
+ health += (FAIL_EMPTY_ENVIRONMENTS + "\n") unless @environments.all?
51
+ @environments.each do |e|
52
+ health += (FAIL_MISSING_ENV + e + "\n") unless File.file?(ENVIRONMENTFILE + e + '.yml')
53
+ end if @environments.all?
54
+ health += (FAIL_EMPTY_NODENAME + "\n") unless @nodename.all?
55
+
56
+ # confirm that the platform definition references all required files
57
+ @provider.definition['structure']['require'].each do |required_file|
58
+ @components.each do |_param, value|
59
+ health += (FAIL_REQUIRED_FILES + required_file + "\n") unless value.key?(required_file)
60
+ # TODO: Validate that the value for the required file key actually matches a defined key value
61
+ end
62
+ # Validate required files against template
63
+ health += validate(@provider.definition['structure'][required_file]['params'], @required[required_file])
64
+ end
65
+ # Validate platform against template
66
+ health += validate(@provider.definition['structure']['platform']['params'], @components)
67
+
68
+ # TODO: if an environment is specified then we are checking the health of the environment rather than platform
69
+ puts 'list nodes' unless env.empty?
70
+ if health.length > 0
71
+ puts health + "\n#{health.lines.count} platform offense(s) detected"
72
+ false
73
+ else
74
+ true
75
+ end
76
+ end
77
+ # rubocop:enable MethodLength, LineLength, CyclomaticComplexity, PerceivedComplexity
78
+
79
+ # rubocop:disable MethodLength, LineLength
80
+ def environment(env)
81
+ return unless healthy?
82
+ build_env = YAML.load_file(ENVIRONMENTFILE + env + '.yml').fetch(env)
83
+ @components.each do |component, _config|
84
+ begin
85
+ build_env['components'][component].merge!(@components[component]) { |_key, v1, _v2| v1 } unless build_env['components'][component].nil?
86
+ rescue => error
87
+ puts "ERROR: build_env: could not merge component \"#{component}\" for environment \"#{env}\"!"
88
+ raise error
89
+ end
90
+ end
91
+ build_env['components'].each do |component, config|
92
+ @required.each do |required_item, _values|
93
+ build_env['components'][component][required_item] = @required[required_item][build_env['components'][component][required_item]]
94
+ end
95
+ config['component_role'] = config['component_role'].gsub('#', component) if config['component_role']
96
+ end
97
+ @env_definition = {}
98
+ build_env['components'].each do |component, config|
99
+ nodes = []
100
+ (1..config['count']).each do |n|
101
+ nodes << node_name(env, component, n)
102
+ end
103
+ # build_env['components'][component].merge!(@components[component])
104
+ build_env['components'][component]['nodes'] = nodes
105
+ end
106
+ @env_definition = build_env
107
+ end
108
+ # rubocop:enable MethodLength, LineLength
109
+
110
+ private
111
+
112
+ # rubocop:disable MethodLength
113
+ def node_name(env, component, instance)
114
+ name = ''
115
+ @nodename.each do |i|
116
+ case i
117
+ when 'environment'
118
+ name += env
119
+ when 'component'
120
+ name += component
121
+ when 'instance'
122
+ name += instance.to_s.rjust(2, '0')
123
+ else
124
+ name += i
125
+ end
126
+ end
127
+ name
128
+ end
129
+ # rubocop:enable MethodLength
130
+
131
+ # rubocop:disable MethodLength, LineLength, CyclomaticComplexity, PerceivedComplexity
132
+ def validate(templatefile, definitionfile)
133
+ health = ''
134
+ templatefile.each do |param, value|
135
+ definitionfile.each do |component, keys|
136
+ case
137
+ when value['array']
138
+ if keys[param].class == Array
139
+ keys[param].each do |i|
140
+ health += "Validation error: #{component}:#{param}\n" unless valid?(i, value['array'])
141
+ end
142
+ else
143
+ health += "Validation error: #{component}:#{param}\n"
144
+ end
145
+ when value['hash']
146
+ keys[param].each do |k, v|
147
+ health += "Validation error: #{component}:#{param}\n" unless valid?(v, value['hash'][k])
148
+ end
149
+ else
150
+ health += "Validation error: #{component}:#{param}\n" unless valid?(keys[param], value)
151
+ end
152
+ end
153
+ end
154
+ health
155
+ end
156
+ # rubocop:enable MethodLength, LineLength, CyclomaticComplexity, PerceivedComplexity
157
+
158
+ # rubocop:disable MethodLength, DoubleNegation, CyclomaticComplexity, PerceivedComplexity, LineLength
159
+ def valid?(value, validate)
160
+ if value.nil?
161
+ false
162
+ else
163
+ case validate['type']
164
+ when 'integer'
165
+ rng = validate['range'].split('..').map { |d| Integer(d) }
166
+ (value.class != Fixnum) ? false : Range.new(rng[0], rng[1]).member?(value)
167
+ when 'string'
168
+ (value.class != String) ? false : value.match(Regexp.new(validate['match']))
169
+ when 'IP'
170
+ (value.class != String) ? false : Resolv::IPv4::Regex.match(value)
171
+ when 'URL'
172
+ (value.class != String) ? false : value.match(URI.regexp)
173
+ when 'boolean'
174
+ !!value == value
175
+ else
176
+ false
177
+ end
178
+ end
179
+ end
180
+ # rubocop:enable MethodLength, DoubleNegation, CyclomaticComplexity, PerceivedComplexity, LineLength
181
+
182
+ # rubocop:disable LineLength
183
+ def platform_files_exist?
184
+ fail(IOError, MISSING_PLATFORM) unless File.file?(PLATFORMFILE)
185
+ unless @provider.nil? # We can't check that everything's present yet...
186
+ @provider.definition['structure']['require'].each do |required_file|
187
+ fail(IOError, MISSING_PLATFORM + required_file) unless File.file?(@provider.definition['structure'][required_file]['path'] + required_file + '.yml')
188
+ end
189
+ end
190
+ true
191
+ end
192
+ # rubocop:enable LineLength
193
+ end
194
+ # rubocop:enable ClassLength
195
+ end
@@ -0,0 +1,118 @@
1
+ require 'pathname'
2
+ require 'yaml'
3
+ require 'git'
4
+ require 'fileutils'
5
+
6
+ module VaqueroIo
7
+ # top level comment
8
+ class Plugin < Thor
9
+ include Thor::Actions
10
+
11
+ # list of installed providers (includes PWD)
12
+ # rubocop:disable LineLength
13
+ desc 'list', DESC_PLUGIN_LIST
14
+ def list
15
+ installed_providers.each { |k, v| puts "#{k} (#{v}) #{ENV[PUTENV_PROVIDER] == k ? '<default' : ''}" }
16
+ end
17
+ # rubocop:enable LineLength
18
+
19
+ desc 'init', DESC_PLUGIN_INIT
20
+ def init
21
+ template(TEMPLATE_PROVIDER, PROVIDERFILE)
22
+ end
23
+
24
+ # rubocop:disable LineLength
25
+ desc 'install LOCATION', DESC_PLUGIN_INSTALL
26
+ def install(path)
27
+ latest = get_working_copy(path)
28
+ if installed_providers.key?(latest['name'])
29
+ puts "#{latest['name']} already installed"
30
+ else
31
+ Dir.mkdir(PROVIDERS_PATH) unless Dir.exist?(PROVIDERS_PATH)
32
+ FileUtils.cp_r("#{TMP_INSTALL_FOLDER}/.", "#{PROVIDERS_PATH}#{latest['name']}")
33
+ puts "Successfully installed #{latest['name']} (#{latest['version']})"
34
+ end if latest != NO_PROVIDER_FILE
35
+ clear_working_copy
36
+ fail(IOError, NO_PROVIDER_FILE) if latest == NO_PROVIDER_FILE
37
+ end
38
+ # rubocop:enable LineLength
39
+
40
+ # rubocop:disable MethodLength, LineLength
41
+ desc 'update [PROVIDER]', DESC_PLUGIN_UPDATE
42
+ def update(plugin = '')
43
+ update_list = {}
44
+ providers = installed_providers
45
+ if plugin.empty?
46
+ update_list = providers
47
+ else
48
+ if installed_providers.key?(plugin)
49
+ update_list[plugin] = providers[plugin]
50
+ else
51
+ update_list = NO_PROVIDER_FILE
52
+ end
53
+ end
54
+ update_list.each { |prov, ins_vers| update_plugin(prov, ins_vers) } if update_list != NO_PROVIDER_FILE
55
+ fail(IOError, NO_PROVIDER_FILE) if update_list == NO_PROVIDER_FILE
56
+ end
57
+ # rubocop:enable MethodLength, LineLength
58
+
59
+ desc 'remove PROVIDER', DESC_PLUGIN_REMOVE
60
+ def remove(plugin)
61
+ if installed_providers.key?(plugin)
62
+ FileUtils.remove_dir("#{PROVIDERS_PATH}#{plugin}")
63
+ puts "#{plugin} removed"
64
+ else
65
+ fail(IOError, NO_PROVIDER_FILE)
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def get_working_copy(path)
72
+ Git.clone(path, TMP_INSTALL_FOLDER)
73
+ NO_PROVIDER_FILE_FILE unless File.file?(TMP_INSTALL_PROVIDER)
74
+ YAML.load_file(TMP_INSTALL_PROVIDER).fetch('provider', NO_PROVIDER_FILE)
75
+ end
76
+
77
+ def clear_working_copy
78
+ FileUtils.remove_dir(TMP_INSTALL_FOLDER)
79
+ end
80
+
81
+ def installed_providers
82
+ providers = {}
83
+ Pathname.glob(LIST_PLUGINS_PATH).each do |prov|
84
+ plugin = YAML.load_file(prov).fetch('provider')
85
+ providers[plugin['name']] = plugin['version']
86
+ end
87
+ providers
88
+ end
89
+
90
+ def plugin_locations
91
+ locations = {}
92
+ Pathname.glob(LIST_PLUGINS_PATH).each do |prov|
93
+ plugin = YAML.load_file(prov).fetch('provider')
94
+ locations[plugin['name']] = plugin['location']
95
+ end
96
+ locations
97
+ end
98
+
99
+ # rubocop:disable LineLength
100
+ def update_plugin(plugin, version)
101
+ locations = plugin_locations
102
+ latest = get_working_copy(locations[plugin])
103
+ if latest['version'] != version
104
+ FileUtils.remove_dir("#{PROVIDERS_PATH}#{plugin}")
105
+ FileUtils.cp_r("#{TMP_INSTALL_FOLDER}/.", "#{PROVIDERS_PATH}#{latest['name']}")
106
+ puts "Updated #{plugin} version #{version} -> #{latest['version']}"
107
+ else
108
+ puts "#{plugin}#{PLUGINS_CURRENT}"
109
+ end
110
+ clear_working_copy
111
+ end
112
+ # rubocop:enable LineLength
113
+
114
+ def self.source_root
115
+ File.dirname(__FILE__)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,48 @@
1
+ module VaqueroIo
2
+ # comment
3
+ class Provider
4
+ attr_accessor :provider
5
+ attr_accessor :definition
6
+
7
+ # rubocop:disable LineLength
8
+ def initialize(use_provider)
9
+ @provider = resolve_provider(use_provider)
10
+ @definition = YAML.load_file(PROVIDERS_PATH + @provider + '/' + PROVIDERFILE).fetch('provider')
11
+ require PROVIDERS_PATH + @provider + '/' + @provider.gsub('-', '_') + '.rb'
12
+ end
13
+ # rubocop:enable LineLength
14
+
15
+ # rubocop:disable MethodLength, LineLength
16
+ def new_definition
17
+ if File.exist?('platform.yml')
18
+ fail(IOError, PLATFORM_EXISTS)
19
+ else
20
+ Pathname.glob(PROVIDERS_PATH + @provider + '/templates/*.yml').each do |prov|
21
+ path = @definition['structure'][File.basename(prov, '.yml')]['path']
22
+ if @definition['structure'][File.basename(prov, '.yml')]['path'].nil?
23
+ path = ''
24
+ else
25
+ unless File.directory?(@definition['structure'][File.basename(prov, '.yml')]['path'])
26
+ FileUtils.mkdir(@definition['structure'][File.basename(prov, '.yml')]['path'])
27
+ end
28
+ end
29
+ FileUtils.cp(prov, path + File.basename(prov))
30
+ end
31
+ puts MSG_NEW_SUCCESS
32
+ end
33
+ end
34
+ # rubocop:enable MethodLength, LineLength
35
+
36
+ private
37
+
38
+ # rubocop:disable LineLength
39
+ def resolve_provider(provider)
40
+ if provider.nil?
41
+ ENV[PUTENV_PROVIDER].nil? ? fail(IOError, NO_PROVIDER) : ENV[PUTENV_PROVIDER]
42
+ else
43
+ provider
44
+ end
45
+ end
46
+ # rubocop:enable LineLength
47
+ end
48
+ end
@@ -0,0 +1,13 @@
1
+ # Basically a dummy placeholder for the provision method that
2
+ # plugins will override
3
+ module VaqueroIo
4
+ # comment
5
+ class Platform
6
+ # comment
7
+ module Provision
8
+ def provision(_env)
9
+ puts 'success'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,42 @@
1
+ require 'vaquero_io'
2
+
3
+ module VaqueroIo
4
+ # wrapper to assist aruba in single process execution
5
+ class Runner
6
+ # rubocop:disable LineLength
7
+ def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel)
8
+ @argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel
9
+ end
10
+ # rubocop:enable LineLength
11
+
12
+ # rubocop:disable MethodLength
13
+ def execute!
14
+ exit_code = begin
15
+ $stderr = @stderr
16
+ $stdin = @stdin
17
+ $stdout = @stdout
18
+
19
+ VaqueroIo::CLI.start(@argv)
20
+
21
+ # Thor::Base#start does not have a return value
22
+ # assume success if no exception is raised.
23
+ 0
24
+ rescue StandardError => err
25
+ # The ruby interpreter would pipe this to STDERR and
26
+ # exit 1 in the case of an unhandled exception
27
+ b = err.backtrace
28
+ b.unshift("#{b.shift}: #{err.message} (#{err.class})")
29
+ @stderr.puts(b.map { |s| "\tfrom #{s}" }.join("\n"))
30
+ 1
31
+ ensure
32
+ # put them back.
33
+ $stderr = STDERR
34
+ $stdin = STDIN
35
+ $stdout = STDOUT
36
+ end
37
+ # Proxy exit code back to the injected kernel.
38
+ @kernel.exit(exit_code)
39
+ end
40
+ # rubocop:enable MethodLength
41
+ end
42
+ end
@@ -0,0 +1,86 @@
1
+ # vaquero: Provider template definition file
2
+ #
3
+ # Complete provider file documentation can be found on the vaquero wiki
4
+ # https://github.com/vaquero-io/vaquero/wiki/Custom-Provider-Plugins
5
+ #
6
+ provider:
7
+ # define the plugin name that can be used to select this provider from the command line or environmental variable
8
+ name:
9
+ version: 0.0.0
10
+
11
+ # Repository location for installation and updates
12
+ location:
13
+
14
+ structure:
15
+
16
+ platform:
17
+ # default to present working directory to find platform.yml
18
+ path:
19
+
20
+ # Use of additional reference files is optional (see documentation)
21
+ #
22
+ # Example
23
+ #
24
+ # references:
25
+ # - compute
26
+ # - tags
27
+ #
28
+ references:
29
+ -
30
+
31
+ params:
32
+ name:
33
+ type: string
34
+ match: 'regex'
35
+ description:
36
+ type: string
37
+ match: 'regex'
38
+ environments:
39
+ array:
40
+ type: string
41
+ match: 'regex'
42
+ nodename:
43
+ array:
44
+ type: string
45
+ match: 'regex'
46
+ pools:
47
+ count:
48
+ type: integer
49
+ range: 1..100
50
+ runlist:
51
+ array:
52
+ type: string
53
+ match: 'regex'
54
+ componentrole:
55
+ type: string
56
+ match: 'regex'
57
+ compute:
58
+ type: string
59
+ match: 'regex'
60
+
61
+ components:
62
+
63
+ environment:
64
+ path: 'environments/'
65
+
66
+ compute:
67
+ path: 'infrastructure/'
68
+ references:
69
+ params:
70
+ ram:
71
+ type: integer
72
+ range: 128..262144
73
+ cpu:
74
+ type: integer
75
+ range: 1..16
76
+ drive:
77
+ hash:
78
+ mount:
79
+ type: string
80
+ match: 'regex'
81
+ capacity:
82
+ type: integer
83
+ range: 8..2048
84
+ image:
85
+ type: string
86
+ match: 'regex'
@@ -0,0 +1,4 @@
1
+ # simple gem version number tracking
2
+ module VaqueroIo
3
+ VERSION = '0.1.1'
4
+ end
data/lib/vaquero_io.rb ADDED
@@ -0,0 +1,43 @@
1
+ require 'thor'
2
+ require 'vaquero_io/messages'
3
+ require 'vaquero_io/config'
4
+ require 'vaquero_io/plugin'
5
+ require 'vaquero_io/provider'
6
+ require 'vaquero_io/platform'
7
+ require 'vaquero_io/build'
8
+ require 'vaquero_io/provision'
9
+
10
+ # Refer to README.md for use instructions
11
+ module VaqueroIo
12
+ # Start of main CLI
13
+ class CLI < Thor
14
+ package_name 'vaquero_io'
15
+ map '--version' => :version
16
+ map '-v' => :version
17
+
18
+ desc 'version, -v', DESC_VERSION
19
+ def version
20
+ puts VERSION
21
+ end
22
+
23
+ desc 'new', DESC_NEW
24
+ method_options %w( provider -p ) => :string, :required => true
25
+ def new
26
+ VaqueroIo::Provider.new(options[:provider]).new_definition
27
+ end
28
+
29
+ # rubocop:disable LineLength
30
+ desc 'health [ENVIRONMENT]', DESC_HEALTH
31
+ method_options %w( provider -p ) => :string
32
+ def health(env = '')
33
+ provider = options[:provider] ? VaqueroIo::Provider.new(options[:provider]) : nil
34
+ # puts HEALTHY if VaqueroIo::Platform.new(VaqueroIo::Provider.new(options[:provider])).healthy?(env)
35
+ puts HEALTHY if VaqueroIo::Platform.new(provider).healthy?(env)
36
+ end
37
+ # rubocop:enable LineLength
38
+
39
+ # subcommand in Thor called as registered class
40
+ register(VaqueroIo::Plugin, 'plugin', 'plugin COMMAND', DESC_PLUGIN)
41
+ register(VaqueroIo::Build, 'build', 'build TARGET', DESC_BUILD)
42
+ end
43
+ end
@@ -0,0 +1,4 @@
1
+ # $LOAD_PATH << '../../lib'
2
+ #
3
+ require 'coveralls'
4
+ Coveralls.wear!
@@ -0,0 +1,38 @@
1
+ # rubocop:disable all
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
4
+ require 'vaquero_io/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'vaquero_io'
8
+ spec.version = VaqueroIo::VERSION
9
+ spec.authors = %w('Nic Cheneweth','Gregory Ruiz-ade')
10
+ spec.email = %w('Nic.Cheneweth@thoughtworks.com','gregory.ruiz-ade@activenetwork.com')
11
+ spec.summary = 'Automated provisioning of application environments'
12
+ spec.description = 'Command line tool to automate the provision and bootstrap of virtual machine application environments'
13
+ spec.homepage = 'http://vaquero.io/'
14
+ spec.license = 'Apache 2.0'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
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 = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '>= 1.7'
22
+ spec.add_development_dependency 'rake', '>= 10.0'
23
+ spec.add_development_dependency 'rubocop', '>= 0.27.0'
24
+ spec.add_development_dependency 'aruba', '>= 0.6'
25
+ spec.add_development_dependency 'rspec', '>= 3.0'
26
+ spec.add_development_dependency 'coveralls', '>= 0.7.9'
27
+ spec.add_development_dependency 'guard', '>= 2.10.0'
28
+ spec.add_development_dependency 'guard-rubocop', '>= 1.1.0'
29
+ spec.add_development_dependency 'guard-rspec', '>= 4.5.0'
30
+ spec.add_development_dependency 'guard-cucumber', '>= 1.6.0'
31
+ spec.add_development_dependency 'guard-yard', '>= 2.1.4'
32
+ # growl functionality in Guardfile depends on growlnotify binary
33
+ spec.add_development_dependency 'growl'
34
+ spec.add_development_dependency 'yard', '>= 0.8'
35
+ spec.add_dependency 'thor', '>= 0.18.0'
36
+ spec.add_dependency 'git', '>= 1.2.0'
37
+ end
38
+ # rubocop:enable all