vanagon 0.16.0 → 0.19.0
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.
- checksums.yaml +4 -4
- data/README.md +48 -23
- data/bin/build +3 -1
- data/bin/build_host_info +3 -1
- data/bin/build_requirements +3 -1
- data/bin/inspect +3 -1
- data/bin/render +3 -1
- data/bin/repo +3 -1
- data/bin/ship +3 -1
- data/bin/sign +3 -1
- data/extras/completions/vanagon.bash +38 -0
- data/extras/completions/vanagon.zsh +41 -0
- data/lib/vanagon/cli.rb +12 -2
- data/lib/vanagon/cli/build.rb +12 -3
- data/lib/vanagon/cli/build_host_info.rb +12 -3
- data/lib/vanagon/cli/build_requirements.rb +13 -5
- data/lib/vanagon/cli/completion.rb +44 -0
- data/lib/vanagon/cli/inspect.rb +12 -3
- data/lib/vanagon/cli/list.rb +74 -0
- data/lib/vanagon/cli/render.rb +11 -2
- data/lib/vanagon/cli/ship.rb +6 -5
- data/lib/vanagon/cli/sign.rb +3 -2
- data/lib/vanagon/component.rb +11 -9
- data/lib/vanagon/component/dsl.rb +6 -5
- data/lib/vanagon/component/source.rb +2 -1
- data/lib/vanagon/component/source/git.rb +7 -6
- data/lib/vanagon/component/source/http.rb +3 -2
- data/lib/vanagon/component/source/local.rb +2 -1
- data/lib/vanagon/component/source/rewrite.rb +3 -2
- data/lib/vanagon/driver.rb +35 -34
- data/lib/vanagon/engine/always_be_scheduling.rb +272 -1
- data/lib/vanagon/engine/docker.rb +2 -1
- data/lib/vanagon/engine/ec2.rb +5 -4
- data/lib/vanagon/engine/hardware.rb +4 -3
- data/lib/vanagon/engine/pooler.rb +13 -8
- data/lib/vanagon/environment.rb +3 -2
- data/lib/vanagon/logger.rb +31 -0
- data/lib/vanagon/platform.rb +6 -5
- data/lib/vanagon/platform/deb.rb +2 -0
- data/lib/vanagon/platform/dsl.rb +5 -4
- data/lib/vanagon/platform/windows.rb +3 -1
- data/lib/vanagon/project.rb +20 -14
- data/lib/vanagon/project/dsl.rb +6 -5
- data/lib/vanagon/utilities.rb +35 -12
- data/resources/deb/control.erb +1 -1
- data/resources/osx/postinstall.erb +5 -1
- data/resources/rpm/project.spec.erb +1 -1
- data/resources/solaris/10/depend.erb +2 -2
- data/resources/solaris/10/postinstall.erb +10 -2
- data/resources/solaris/11/p5m.erb +2 -2
- data/spec/lib/vanagon/cli_spec.rb +143 -0
- data/spec/lib/vanagon/component/dsl_spec.rb +9 -2
- data/spec/lib/vanagon/component_spec.rb +3 -2
- data/spec/lib/vanagon/driver_spec.rb +1 -1
- data/spec/lib/vanagon/engine/always_be_scheduling_spec.rb +113 -1
- data/spec/lib/vanagon/engine/ec2_spec.rb +2 -0
- data/spec/lib/vanagon/engine/pooler_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- metadata +32 -27
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'fustigit'
|
2
|
+
require 'vanagon/logger'
|
2
3
|
require 'vanagon/component/source/http'
|
3
4
|
require 'vanagon/component/source/git'
|
4
5
|
require 'vanagon/component/source/local'
|
@@ -66,7 +67,7 @@ class Vanagon
|
|
66
67
|
timeout = 5
|
67
68
|
if Vanagon::Component::Source::Git.valid_remote?(uri, timeout)
|
68
69
|
if uri =~ /^http/
|
69
|
-
|
70
|
+
VanagonLogger.info "Passing git URLs as http(s) addresses is deprecated! Please prefix your source URL with `git:`"
|
70
71
|
end
|
71
72
|
return :git
|
72
73
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'vanagon/utilities'
|
2
2
|
require 'vanagon/errors'
|
3
|
+
require 'vanagon/logger'
|
3
4
|
# This stupid library requires a capital 'E' in its name
|
4
5
|
# but it provides a wealth of useful constants
|
5
6
|
require 'English'
|
@@ -81,7 +82,7 @@ class Vanagon
|
|
81
82
|
# There is no md5 to manually verify here, so this is a noop.
|
82
83
|
def verify
|
83
84
|
# nothing to do here, so just tell users that and return
|
84
|
-
|
85
|
+
VanagonLogger.info "Nothing to verify for '#{dirname}' (using Git reference '#{ref}')"
|
85
86
|
end
|
86
87
|
|
87
88
|
# The dirname to reference when building from the repo
|
@@ -131,8 +132,8 @@ class Vanagon
|
|
131
132
|
# Clone a remote repo, make noise about it, and fail entirely
|
132
133
|
# if we're unable to retrieve the remote repo
|
133
134
|
def clone!
|
134
|
-
|
135
|
-
|
135
|
+
VanagonLogger.info "Cloning Git repo '#{url}'"
|
136
|
+
VanagonLogger.info "Successfully cloned '#{dirname}'" if clone
|
136
137
|
rescue ::Git::GitExecuteError
|
137
138
|
raise Vanagon::InvalidRepo, "Unable to clone from '#{url}'"
|
138
139
|
end
|
@@ -141,7 +142,7 @@ class Vanagon
|
|
141
142
|
# Checkout desired ref/sha, make noise about it, and fail
|
142
143
|
# entirely if we're unable to checkout that given ref/sha
|
143
144
|
def checkout!
|
144
|
-
|
145
|
+
VanagonLogger.info "Checking out '#{ref}' from Git repo '#{dirname}'"
|
145
146
|
clone.checkout(ref)
|
146
147
|
rescue ::Git::GitExecuteError
|
147
148
|
raise Vanagon::CheckoutFailed, "unable to checkout #{ref} from '#{url}'"
|
@@ -151,7 +152,7 @@ class Vanagon
|
|
151
152
|
# Attempt to update submodules, and do not panic
|
152
153
|
# if there are no submodules to initialize
|
153
154
|
def update_submodules
|
154
|
-
|
155
|
+
VanagonLogger.info "Attempting to update submodules for repo '#{dirname}'"
|
155
156
|
clone.update_submodules(init: true)
|
156
157
|
end
|
157
158
|
private :update_submodules
|
@@ -163,7 +164,7 @@ class Vanagon
|
|
163
164
|
def describe
|
164
165
|
clone.describe(ref, tags: true)
|
165
166
|
rescue ::Git::GitExecuteError
|
166
|
-
|
167
|
+
VanagonLogger.info "Directory '#{dirname}' cannot be versioned by Git. Maybe it hasn't been tagged yet?"
|
167
168
|
end
|
168
169
|
private :describe
|
169
170
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'vanagon/utilities'
|
2
|
+
require 'vanagon/logger'
|
2
3
|
require 'vanagon/component/source/local'
|
3
4
|
require 'net/http'
|
4
5
|
require 'uri'
|
@@ -93,7 +94,7 @@ class Vanagon
|
|
93
94
|
#
|
94
95
|
# @raise [RuntimeError] an exception is raised if the sum does not match the sum of the file
|
95
96
|
def verify # rubocop:disable Metrics/AbcSize
|
96
|
-
|
97
|
+
VanagonLogger.info "Verifying file: #{file} against sum: '#{sum}'"
|
97
98
|
actual = get_sum(File.join(workdir, file), sum_type)
|
98
99
|
return true if sum == actual
|
99
100
|
|
@@ -107,7 +108,7 @@ class Vanagon
|
|
107
108
|
uri = URI.parse(target_url.to_s)
|
108
109
|
target_file ||= File.basename(uri.path)
|
109
110
|
|
110
|
-
|
111
|
+
VanagonLogger.info "Downloading file '#{target_file}' from url '#{target_url}'"
|
111
112
|
|
112
113
|
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
113
114
|
http.request(Net::HTTP::Get.new(uri, headers)) do |response|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'vanagon/utilities'
|
2
|
+
require 'vanagon/logger'
|
2
3
|
|
3
4
|
class Vanagon
|
4
5
|
class Component
|
@@ -57,7 +58,7 @@ class Vanagon
|
|
57
58
|
#
|
58
59
|
# @raise [RuntimeError, Vanagon::Error] an exception is raised if the URI scheme cannot be handled
|
59
60
|
def copy
|
60
|
-
|
61
|
+
VanagonLogger.info "Copying file '#{url.basename}' to workdir"
|
61
62
|
|
62
63
|
FileUtils.cp_r(url, file)
|
63
64
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'vanagon/component/source'
|
2
|
+
require 'vanagon/logger'
|
2
3
|
|
3
4
|
class Vanagon
|
4
5
|
class Component
|
@@ -16,7 +17,7 @@ class Vanagon
|
|
16
17
|
# @deprecated Please use the component DSL method #mirror(<URI>)
|
17
18
|
# instead. This method will be removed before Vanagon 1.0.0.
|
18
19
|
def register_rewrite_rule(protocol, rule)
|
19
|
-
|
20
|
+
VanagonLogger.info <<-HERE.undent
|
20
21
|
rewrite rule support is deprecated and will be removed before Vanagon 1.0.0.
|
21
22
|
Rewritten URLs will be automatically converted into mirror URLs for now but
|
22
23
|
please use the component DSL method '#mirror url' to define new mirror URL
|
@@ -76,7 +77,7 @@ class Vanagon
|
|
76
77
|
def parse_and_rewrite(uri)
|
77
78
|
return uri if rewrite_rules.empty?
|
78
79
|
if !!uri.match(/^git:http/)
|
79
|
-
|
80
|
+
VanagonLogger.info <<-HERE.undent
|
80
81
|
`fustigit` parsing doesn't get along with how we specify the source
|
81
82
|
type by prefixing `git`. As `rewrite_rules` are deprecated, we'll
|
82
83
|
replace `git:http` with `http` in your uri. At some point this will
|
data/lib/vanagon/driver.rb
CHANGED
@@ -4,6 +4,7 @@ require 'vanagon/component'
|
|
4
4
|
require 'vanagon/utilities'
|
5
5
|
require 'vanagon/common'
|
6
6
|
require 'vanagon/errors'
|
7
|
+
require 'vanagon/logger'
|
7
8
|
require 'tmpdir'
|
8
9
|
require 'logger'
|
9
10
|
|
@@ -20,33 +21,29 @@ class Vanagon
|
|
20
21
|
@retry_count ||= @project.retry_count || ENV["VANAGON_RETRY_COUNT"] || 1
|
21
22
|
end
|
22
23
|
|
23
|
-
def initialize(platform, project, options = {
|
24
|
-
@
|
25
|
-
@
|
24
|
+
def initialize(platform, project, options = {}) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
25
|
+
@options = options
|
26
|
+
@verbose = options[:verbose] || false
|
27
|
+
@preserve = options[:preserve] || false
|
26
28
|
@workdir = options[:workdir] || Dir.mktmpdir
|
27
29
|
|
28
30
|
@@configdir = options[:configdir] || File.join(Dir.pwd, "configs")
|
29
31
|
components = options[:components] || []
|
30
32
|
only_build = options[:only_build]
|
31
|
-
target = options[:target]
|
32
33
|
|
33
34
|
@platform = Vanagon::Platform.load_platform(platform, File.join(@@configdir, "platforms"))
|
34
|
-
@project = Vanagon::Project.load_project(
|
35
|
+
@project = Vanagon::Project.load_project(
|
36
|
+
project, File.join(@@configdir, "projects"), @platform, components
|
37
|
+
)
|
35
38
|
@project.settings[:verbose] = options[:verbose]
|
36
|
-
@project.settings[:skipcheck] = options[:skipcheck]
|
39
|
+
@project.settings[:skipcheck] = options[:skipcheck] || false
|
37
40
|
filter_out_components(only_build) if only_build
|
38
41
|
loginit('vanagon_hosts.log')
|
39
42
|
|
40
43
|
@remote_workdir = options[:"remote-workdir"]
|
41
44
|
|
42
|
-
|
43
|
-
|
44
|
-
load_engine_object(options[:engine], @platform, target)
|
45
|
-
else
|
46
|
-
# Use 'pooler' as a default, but also apply selection logic that may
|
47
|
-
# choose something different based on platform configuration.
|
48
|
-
load_engine('pooler', @platform, target)
|
49
|
-
end
|
45
|
+
engine = pick_engine(options)
|
46
|
+
load_engine_object(engine, @platform, options[:target])
|
50
47
|
end
|
51
48
|
|
52
49
|
def filter_out_components(only_build)
|
@@ -54,29 +51,31 @@ class Vanagon
|
|
54
51
|
# flatten all the results in to one array and set project.components to that.
|
55
52
|
@project.components = only_build.flat_map { |comp| @project.filter_component(comp) }.uniq
|
56
53
|
if @verbose
|
57
|
-
|
58
|
-
@project.components.each { |comp|
|
54
|
+
VanagonLogger.info "Only building:"
|
55
|
+
@project.components.each { |comp| VanagonLogger.info comp.name }
|
59
56
|
end
|
60
57
|
end
|
61
58
|
|
62
|
-
def
|
63
|
-
if
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
59
|
+
def pick_engine(options) # rubocop:disable Metrics/PerceivedComplexity
|
60
|
+
if options[:engine] && !options[:target]
|
61
|
+
options[:engine]
|
62
|
+
elsif @platform.build_hosts
|
63
|
+
'hardware'
|
64
|
+
elsif @platform.aws_ami
|
65
|
+
'ec2'
|
66
|
+
elsif @platform.docker_image
|
67
|
+
'docker'
|
68
|
+
elsif options[:target]
|
69
|
+
'base'
|
70
|
+
else
|
71
|
+
'always_be_scheduling'
|
73
72
|
end
|
74
|
-
load_engine_object(engine_type, platform, target)
|
75
73
|
end
|
76
74
|
|
77
75
|
def load_engine_object(engine_type, platform, target)
|
78
76
|
require "vanagon/engine/#{engine_type}"
|
79
|
-
@engine = Object::const_get("Vanagon::Engine::#{camelize(engine_type)}")
|
77
|
+
@engine = Object::const_get("Vanagon::Engine::#{camelize(engine_type)}")
|
78
|
+
.new(platform, target, remote_workdir: remote_workdir)
|
80
79
|
rescue StandardError, ScriptError => e
|
81
80
|
raise Vanagon::Error.wrap(e, "Could not load the desired engine '#{engine_type}'")
|
82
81
|
end
|
@@ -103,7 +102,9 @@ class Vanagon
|
|
103
102
|
{ "name" => @engine.build_host_name, "engine" => @engine.name }
|
104
103
|
end
|
105
104
|
|
106
|
-
# Returns the set difference between the build_requires and the
|
105
|
+
# Returns the set difference between the build_requires and the
|
106
|
+
# components to get a list of external dependencies that need to
|
107
|
+
# be installed.
|
107
108
|
def list_build_dependencies
|
108
109
|
@project.components.map(&:build_requires).flatten.uniq - @project.components.map(&:name)
|
109
110
|
end
|
@@ -134,7 +135,7 @@ class Vanagon
|
|
134
135
|
end
|
135
136
|
|
136
137
|
@engine.startup(workdir)
|
137
|
-
|
138
|
+
VanagonLogger.info "Target is #{@engine.target}"
|
138
139
|
Vanagon::Utilities.retry_with_timeout(retry_count, timeout) do
|
139
140
|
install_build_dependencies
|
140
141
|
end
|
@@ -159,8 +160,8 @@ class Vanagon
|
|
159
160
|
@engine.teardown
|
160
161
|
cleanup_workdir
|
161
162
|
end
|
162
|
-
|
163
|
-
|
163
|
+
VanagonLogger.error(e)
|
164
|
+
VanagonLogger.error e.backtrace.join("\n")
|
164
165
|
raise e
|
165
166
|
ensure
|
166
167
|
if ["hardware", "ec2"].include?(@engine.name)
|
@@ -174,7 +175,7 @@ class Vanagon
|
|
174
175
|
raise Vanagon::Error, "Project requires a version set, all is lost."
|
175
176
|
end
|
176
177
|
|
177
|
-
|
178
|
+
VanagonLogger.info "rendering Makefile"
|
178
179
|
@project.fetch_sources(workdir, retry_count, timeout)
|
179
180
|
@project.make_bill_of_materials(workdir)
|
180
181
|
@project.generate_packaging_artifacts(workdir)
|
@@ -1,5 +1,7 @@
|
|
1
|
-
require 'vanagon/engine/base'
|
2
1
|
require 'json'
|
2
|
+
require 'vanagon/engine/base'
|
3
|
+
require 'vanagon/logger'
|
4
|
+
require 'yaml'
|
3
5
|
|
4
6
|
class Vanagon
|
5
7
|
class Engine
|
@@ -82,9 +84,15 @@ class Vanagon
|
|
82
84
|
# {"name":"aix-53-ppc","engine":"always_be_scheduling"}
|
83
85
|
# ```
|
84
86
|
class AlwaysBeScheduling < Base
|
87
|
+
attr_reader :token
|
88
|
+
attr_reader :token_vmpooler
|
89
|
+
|
85
90
|
def initialize(platform, target, **opts)
|
86
91
|
super
|
87
92
|
|
93
|
+
@available_abs_endpoint = "https://abs-prod.k8s.infracore.puppet.net/api/v2"
|
94
|
+
@token_vmpooler = ENV['VMPOOLER_TOKEN']
|
95
|
+
@token = load_token
|
88
96
|
Vanagon::Driver.logger.debug "AlwaysBeScheduling engine invoked."
|
89
97
|
end
|
90
98
|
|
@@ -94,6 +102,7 @@ class Vanagon
|
|
94
102
|
end
|
95
103
|
|
96
104
|
# return the platform name as the "host" name
|
105
|
+
# order of preference: abs_resource_name, vmpooler_template or name
|
97
106
|
def build_host_name
|
98
107
|
if @platform.abs_resource_name
|
99
108
|
@platform.abs_resource_name
|
@@ -103,6 +112,268 @@ class Vanagon
|
|
103
112
|
@platform.name
|
104
113
|
end
|
105
114
|
end
|
115
|
+
|
116
|
+
# Retrieve the ABS token from an environment variable
|
117
|
+
# ("ABS_TOKEN") or from a number of potential configuration
|
118
|
+
# files (~/.vanagon-token or ~/.vmfloaty.yml).
|
119
|
+
# @return [String, nil] token for use with the vmpooler
|
120
|
+
def load_token
|
121
|
+
ENV['ABS_TOKEN'] || token_from_file
|
122
|
+
end
|
123
|
+
|
124
|
+
# a wrapper method around retrieving a token,
|
125
|
+
# with an explicitly ordered preference for a Vanagon-specific
|
126
|
+
# token file or a preexisting vmfoaty yaml file.
|
127
|
+
#
|
128
|
+
# @return [String, nil] token for use with the vmpooler
|
129
|
+
def token_from_file
|
130
|
+
read_vanagon_token || read_vmfloaty_token
|
131
|
+
end
|
132
|
+
private :token_from_file
|
133
|
+
|
134
|
+
# Read an ABS/vmpooler token from the plaintext vanagon-token file,
|
135
|
+
# as outlined in the project README.
|
136
|
+
# The first line should be the ABS token
|
137
|
+
# and the second (optional) line should be the vmpooler token
|
138
|
+
# Saves the second line into the @token_vmpooler variable
|
139
|
+
#
|
140
|
+
# @return [String, nil] the vanagon vmpooler token value
|
141
|
+
def read_vanagon_token(path = "~/.vanagon-token")
|
142
|
+
absolute_path = File.expand_path(path)
|
143
|
+
return nil unless File.exist?(absolute_path)
|
144
|
+
|
145
|
+
VanagonLogger.info "Reading ABS token from: #{path}"
|
146
|
+
contents = File.read(absolute_path).chomp
|
147
|
+
lines = contents.each_line.map(&:chomp)
|
148
|
+
|
149
|
+
abs = lines.shift
|
150
|
+
@token_vmpooler = lines.shift
|
151
|
+
|
152
|
+
VanagonLogger.info "Please add a second line with the vmpooler token to be able to modify or see the VM in floaty/bit-bar" if @token_vmpooler.nil?
|
153
|
+
return abs
|
154
|
+
end
|
155
|
+
private :read_vanagon_token
|
156
|
+
|
157
|
+
# Read a vmpooler token from the yaml formatted vmfloaty config,
|
158
|
+
# as outlined by the vmfloaty project:
|
159
|
+
# https://github.com/puppetlabs/vmfloaty
|
160
|
+
#
|
161
|
+
# It returns the top-level token value or the first 'abs' service
|
162
|
+
# token value it finds in the services list
|
163
|
+
# it sets @token_vmpooler with the optional vmpooler token
|
164
|
+
# if the abs services has a vmpooler_fallback, and that service
|
165
|
+
# has a token eg
|
166
|
+
#
|
167
|
+
# TOP-LEVEL DEFINED => would get only the abs token
|
168
|
+
# user: 'jdoe'
|
169
|
+
# url: 'https://abs.example.net'
|
170
|
+
# token: '456def789'
|
171
|
+
#
|
172
|
+
# MULTIPLE SERVICES DEFINED => would get the abs token in 'abs-prod' and the vmpooler token in 'vmpooler-prod'
|
173
|
+
# user: 'jdoe'
|
174
|
+
# services:
|
175
|
+
# abs-prod:
|
176
|
+
# type: 'abs'
|
177
|
+
# url: 'https://abs.example.net/api/v2'
|
178
|
+
# token: '123abc456'
|
179
|
+
# vmpooler_fallback: 'vmpooler-prod'
|
180
|
+
# vmpooler-dev:
|
181
|
+
# type: 'vmpooler'
|
182
|
+
# url: 'https://vmpooler-dev.example.net'
|
183
|
+
# token: '987dsa654'
|
184
|
+
# vmpooler-prod:
|
185
|
+
# type: 'vmpooler'
|
186
|
+
# url: 'https://vmpooler.example.net'
|
187
|
+
# token: '456def789'
|
188
|
+
#
|
189
|
+
# @return [String, nil] the vmfloaty vmpooler token value
|
190
|
+
def read_vmfloaty_token(path = "~/.vmfloaty.yml") # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
191
|
+
absolute_path = File.expand_path(path)
|
192
|
+
return nil unless File.exist?(absolute_path)
|
193
|
+
|
194
|
+
yaml_config = YAML.load_file(absolute_path)
|
195
|
+
abs_token = nil
|
196
|
+
abs_service_name = nil
|
197
|
+
if yaml_config['token'] # top level
|
198
|
+
abs_token = yaml_config['token']
|
199
|
+
elsif yaml_config['services']
|
200
|
+
yaml_config['services'].each do |name, value|
|
201
|
+
if value['type'] == "abs" && value['token']
|
202
|
+
abs_token = value['token']
|
203
|
+
abs_service_name = name
|
204
|
+
vmpooler_fallback = value['vmpooler_fallback']
|
205
|
+
unless vmpooler_fallback.nil? || yaml_config['services'][vmpooler_fallback].nil? || yaml_config['services'][vmpooler_fallback]['token'].nil?
|
206
|
+
@token_vmpooler = yaml_config['services'][vmpooler_fallback]['token']
|
207
|
+
end
|
208
|
+
break
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
message = "Reading ABS token from: #{path}"
|
213
|
+
if abs_service_name
|
214
|
+
message.concat(" for service named #{abs_service_name}")
|
215
|
+
if @token_vmpooler.nil?
|
216
|
+
message.concat(" but there was no vmpooler_fallback value, please add one if you want to be able to modify the VM via bit-bar/floaty")
|
217
|
+
else
|
218
|
+
message.concat(" with a vmpooler_fallback token")
|
219
|
+
end
|
220
|
+
end
|
221
|
+
VanagonLogger.info message
|
222
|
+
return abs_token
|
223
|
+
end
|
224
|
+
private :read_vmfloaty_token
|
225
|
+
|
226
|
+
# This method is used to obtain a vm to build upon using Puppet's internal
|
227
|
+
# ABS (https://github.com/puppetlabs/always-be-scheduling) which is a level of abstraction for other
|
228
|
+
# engines using similar APIs
|
229
|
+
# @raise [Vanagon::Error] if a target cannot be obtained
|
230
|
+
def select_target
|
231
|
+
@pooler = select_target_from(@available_abs_endpoint)
|
232
|
+
raise Vanagon::Error, "Something went wrong getting a target vm to build on" if @pooler.empty?
|
233
|
+
end
|
234
|
+
|
235
|
+
# Attempt to provision a host from a specific pooler.
|
236
|
+
def select_target_from(pooler) # rubocop:disable Metrics/AbcSize
|
237
|
+
request_object = build_request_object
|
238
|
+
|
239
|
+
VanagonLogger.info "Requesting VMs with job_id: #{@saved_job_id}. Will poll for up to an hour."
|
240
|
+
#the initial request is always replied with "come back again"
|
241
|
+
response = Vanagon::Utilities.http_request_generic(
|
242
|
+
"#{pooler}/request",
|
243
|
+
'POST',
|
244
|
+
request_object.to_json,
|
245
|
+
{ 'X-AUTH-TOKEN' => @token }
|
246
|
+
)
|
247
|
+
|
248
|
+
unless response.code == "202"
|
249
|
+
VanagonLogger.info "failed to request ABS with code #{response.code}"
|
250
|
+
if valid_json?(response.body)
|
251
|
+
response_json = JSON.parse(response.body)
|
252
|
+
VanagonLogger.info "reason: #{response_json['reason']}"
|
253
|
+
end
|
254
|
+
return ''
|
255
|
+
end
|
256
|
+
response_body = check_queue(pooler, request_object)
|
257
|
+
|
258
|
+
return '' unless response_body["ok"]
|
259
|
+
@target = response_body[build_host_name]['hostname']
|
260
|
+
Vanagon::Driver.logger.info "Reserving #{@target} (#{build_host_name}) [#{@token ? 'token used' : 'no token used'}]"
|
261
|
+
return pooler
|
262
|
+
end
|
263
|
+
|
264
|
+
# main loop where the status of the request is checked, to see if the request
|
265
|
+
# has been allocated
|
266
|
+
def check_queue(pooler, request_object)
|
267
|
+
retries = 360 # ~ one hour
|
268
|
+
response_body = nil
|
269
|
+
begin
|
270
|
+
(1..retries).each do |i|
|
271
|
+
response = Vanagon::Utilities.http_request_generic(
|
272
|
+
"#{pooler}/request",
|
273
|
+
'POST',
|
274
|
+
request_object.to_json,
|
275
|
+
{ 'X-AUTH-TOKEN' => @token }
|
276
|
+
)
|
277
|
+
response_body = validate_queue_status_response(response.code, response.body)
|
278
|
+
break if response_body
|
279
|
+
|
280
|
+
sleep_seconds = 10 if i >= 10
|
281
|
+
sleep_seconds = i if i < 10
|
282
|
+
VanagonLogger.info "Waiting #{sleep_seconds} seconds to check if ABS request has been filled. (x#{i})"
|
283
|
+
|
284
|
+
sleep(sleep_seconds)
|
285
|
+
end
|
286
|
+
rescue SystemExit, Interrupt
|
287
|
+
VanagonLogger.error "\nVanagon interrupted during mains ABS polling. Make sure you delete the requested job_id #{@saved_job_id}"
|
288
|
+
raise
|
289
|
+
end
|
290
|
+
response_body = translated(response_body, @saved_job_id)
|
291
|
+
response_body
|
292
|
+
end
|
293
|
+
|
294
|
+
def validate_queue_status_response(status_code, body)
|
295
|
+
case status_code
|
296
|
+
when "200"
|
297
|
+
return JSON.parse(body) unless body.empty? || !valid_json?(body)
|
298
|
+
when "202"
|
299
|
+
return nil
|
300
|
+
when "401"
|
301
|
+
raise Vanagon::Error, "HTTP #{status_code}: The token provided could not authenticate.\n#{body}"
|
302
|
+
when "503"
|
303
|
+
return nil
|
304
|
+
else
|
305
|
+
raise Vanagon::Error, "HTTP #{status_code}: request to ABS failed!\n#{body}"
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
# This method is used to tell the ABS to delete the job_id requested
|
310
|
+
# otherwise the resources will eventually get allocated asynchronously
|
311
|
+
# and will keep running until the end of their lifetime.
|
312
|
+
def teardown # rubocop:disable Metrics/AbcSize
|
313
|
+
request_object = {
|
314
|
+
'job_id' => @saved_job_id,
|
315
|
+
}
|
316
|
+
|
317
|
+
response = Vanagon::Utilities.http_request_generic(
|
318
|
+
"#{@available_abs_endpoint}/return",
|
319
|
+
"POST",
|
320
|
+
request_object.to_json,
|
321
|
+
{ 'X-AUTH-TOKEN' => @token }
|
322
|
+
)
|
323
|
+
if response && response.body == 'OK'
|
324
|
+
Vanagon::Driver.logger.info "#{@saved_job_id} has been scheduled for removal"
|
325
|
+
VanagonLogger.info "#{@saved_job_id} has been scheduled for removal"
|
326
|
+
else
|
327
|
+
Vanagon::Driver.logger.info "#{@saved_job_id} could not be scheduled for removal: #{response.body}"
|
328
|
+
VanagonLogger.info "#{@saved_job_id} could not be scheduled for removal"
|
329
|
+
end
|
330
|
+
rescue Vanagon::Error => e
|
331
|
+
Vanagon::Driver.logger.info "#{@saved_job_id} could not be scheduled for removal (#{e.message})"
|
332
|
+
VanagonLogger.info "#{@saved_job_id} could not be scheduled for removal (#{e.message})"
|
333
|
+
end
|
334
|
+
|
335
|
+
private
|
336
|
+
|
337
|
+
def translated(response_body, job_id)
|
338
|
+
vmpooler_formatted_body = { 'job_id' => job_id }
|
339
|
+
|
340
|
+
response_body.each do |host| # in this context there should be only one host
|
341
|
+
vmpooler_formatted_body[host['type']] = { 'hostname' => host['hostname'] }
|
342
|
+
end
|
343
|
+
vmpooler_formatted_body['ok'] = true
|
344
|
+
|
345
|
+
vmpooler_formatted_body
|
346
|
+
end
|
347
|
+
|
348
|
+
def build_request_object
|
349
|
+
user = ENV['USER'] || ENV['USERNAME'] || 'unknown'
|
350
|
+
|
351
|
+
@saved_job_id = user + "-" + DateTime.now.strftime('%Q')
|
352
|
+
request_object = {
|
353
|
+
:resources => { build_host_name => 1 },
|
354
|
+
:job => {
|
355
|
+
:id => @saved_job_id,
|
356
|
+
:tags => {
|
357
|
+
:jenkins_build_url => ENV['BUILD_URL'],
|
358
|
+
:project => ENV['JOB_NAME'] || 'vanagon',
|
359
|
+
:created_by => user,
|
360
|
+
:user => user
|
361
|
+
},
|
362
|
+
},
|
363
|
+
:priority => 1, # DO NOT use priority 1 in automated CI runs
|
364
|
+
}
|
365
|
+
unless @token_vmpooler.nil?
|
366
|
+
request_object[:vm_token] = @token_vmpooler
|
367
|
+
end
|
368
|
+
request_object
|
369
|
+
end
|
370
|
+
|
371
|
+
def valid_json?(json)
|
372
|
+
JSON.parse(json)
|
373
|
+
return true
|
374
|
+
rescue TypeError, JSON::ParserError
|
375
|
+
return false
|
376
|
+
end
|
106
377
|
end
|
107
378
|
end
|
108
379
|
end
|