forj 0.0.48 → 1.0.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/Gemfile +3 -2
- data/bin/forj +59 -17
- data/lib/appinit.rb +2 -0
- data/lib/build_tmpl/bootstrap_build.sh +145 -0
- data/lib/build_tmpl/write-mime-multipart.py +128 -0
- data/lib/connection.rb +6 -8
- data/lib/defaults.yaml +130 -46
- data/lib/down.rb +1 -3
- data/{spec/yaml_parse_spec.rb → lib/forj/ForjCli.rb} +22 -0
- data/lib/forj/ForjCore.rb +111 -0
- data/lib/forj/process/ForjProcess.rb +623 -0
- data/lib/forj-account.rb +3 -380
- data/lib/forj-config.rb +6 -6
- data/lib/lib-forj/lib/core/core.rb +1687 -0
- data/lib/lib-forj/lib/core/definition.rb +441 -0
- data/lib/lib-forj/lib/core/definition_internal.rb +306 -0
- data/lib/lib-forj/lib/core_process/CloudProcess.rb +311 -0
- data/lib/lib-forj/lib/core_process/global_process.rb +403 -0
- data/lib/lib-forj/lib/core_process/network_process.rb +606 -0
- data/lib/lib-forj/lib/lib-forj.rb +37 -0
- data/lib/lib-forj/lib/providers/hpcloud/Hpcloud.rb +406 -0
- data/lib/lib-forj/lib/providers/hpcloud/compute.rb +108 -0
- data/lib/lib-forj/lib/providers/hpcloud/network.rb +107 -0
- data/lib/lib-forj/lib/providers/hpcloud/security_groups.rb +67 -0
- data/lib/lib-forj/lib/providers/templates/compute.rb +42 -0
- data/lib/lib-forj/lib/providers/templates/core.rb +61 -0
- data/lib/{yaml_parse.rb → lib-forj/lib/providers/templates/network.rb} +14 -26
- data/lib/log.rb +12 -1
- data/lib/repositories.rb +0 -6
- data/lib/ssh.rb +18 -15
- metadata +35 -6
- data/lib/boot.rb +0 -263
- data/lib/setup.rb +0 -45
@@ -0,0 +1,623 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
# (c) Copyright 2014 Hewlett-Packard Development Company, L.P.
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
# Forj Process solution
|
19
|
+
|
20
|
+
require 'git'
|
21
|
+
require 'fileutils'
|
22
|
+
require 'find'
|
23
|
+
require 'digest'
|
24
|
+
require 'json'
|
25
|
+
require 'encryptor' # gem install encryptor
|
26
|
+
require 'base64'
|
27
|
+
|
28
|
+
$INFRA_VERSION = "0.0.37"
|
29
|
+
|
30
|
+
# Functions for boot
|
31
|
+
class ForjCoreProcess
|
32
|
+
|
33
|
+
def build_metadata(sObjectType, hParams)
|
34
|
+
key_file = File.join($FORJ_CREDS_PATH, '.key')
|
35
|
+
|
36
|
+
if not File.exists?(key_file)
|
37
|
+
# Need to create a random key.
|
38
|
+
entr = {
|
39
|
+
:key => rand(36**10).to_s(36),
|
40
|
+
:salt => Time.now.to_i.to_s,
|
41
|
+
:iv => Base64::strict_encode64(OpenSSL::Cipher::Cipher.new('aes-256-cbc').random_iv)
|
42
|
+
}
|
43
|
+
|
44
|
+
Logging.debug("Writing '%s' key file" % key_file)
|
45
|
+
File.open(key_file, 'w') do |out|
|
46
|
+
out.write(Base64::encode64(entr.to_yaml))
|
47
|
+
end
|
48
|
+
else
|
49
|
+
Logging.debug("Loading '%s' key file" % key_file)
|
50
|
+
encoded_key = IO.read(key_file)
|
51
|
+
entr = YAML.load(Base64::decode64(encoded_key))
|
52
|
+
end
|
53
|
+
os_enckey = hParams[:os_enckey]
|
54
|
+
|
55
|
+
begin
|
56
|
+
os_key = Encryptor.decrypt(
|
57
|
+
:value => Base64::strict_decode64(os_enckey),
|
58
|
+
:key => entr[:key],
|
59
|
+
:iv => Base64::strict_decode64(entr[:iv]),
|
60
|
+
:salt => entr[:salt]
|
61
|
+
)
|
62
|
+
rescue => e
|
63
|
+
raise "Unable to decript your password. You need to re-execute setup."
|
64
|
+
end
|
65
|
+
|
66
|
+
hpcloud_priv = nil
|
67
|
+
IO.popen('gzip -c' , 'r+') {|pipe|
|
68
|
+
pipe.puts('HPCLOUD_OS_USER=%s' % [hParams[:os_user]] )
|
69
|
+
pipe.puts('HPCLOUD_OS_KEY=%s' % [os_key] )
|
70
|
+
pipe.puts('DNS_KEY=%s' % [hParams[:account_id]] )
|
71
|
+
pipe.puts('DNS_SECRET=%s' % [hParams[:account_key]])
|
72
|
+
pipe.close_write
|
73
|
+
hpcloud_priv = pipe.read
|
74
|
+
}
|
75
|
+
|
76
|
+
config.set(:server_name, "maestro.%s" % hParams[:instance_name]) # Used by :server object
|
77
|
+
|
78
|
+
hMeta = {
|
79
|
+
'cdksite' => config.get(:server_name),
|
80
|
+
'cdkdomain' => hParams[:domain_name],
|
81
|
+
'eroip' => '127.0.0.1',
|
82
|
+
'erosite' => config.get(:server_name),
|
83
|
+
'erodomain' => hParams[:domain_name],
|
84
|
+
'gitbranch' => hParams[:branch],
|
85
|
+
'security_groups' => hParams[:security_group],
|
86
|
+
'tenant_name' => hParams[:tenant_name],
|
87
|
+
'network_name' => hParams[:network_name],
|
88
|
+
'hpcloud_os_region' => hParams[:compute],
|
89
|
+
'PUPPET_DEBUG' => 'True',
|
90
|
+
'image_name' => hParams[:image_name],
|
91
|
+
'key_name' => hParams[:keypair_name],
|
92
|
+
'hpcloud_priv' => Base64.strict_encode64(hpcloud_priv).gsub('=', '') # Remove pad
|
93
|
+
}
|
94
|
+
|
95
|
+
if hParams[:dns_service]
|
96
|
+
hMeta['dns_zone'] = hParams[:dns_service]
|
97
|
+
hMeta['dns_tenantid'] = hParams[:dns_tenant_id]
|
98
|
+
end
|
99
|
+
# If requested by user, ask Maestro to instantiate a blueprint.
|
100
|
+
hMeta['blueprint'] = hParams[:blueprint] if hParams[:blueprint]
|
101
|
+
|
102
|
+
# Add init additionnal git clone steps.
|
103
|
+
hMeta['repos'] = hParams[:repos] if hParams[:repos]
|
104
|
+
# Add init bootstrap additionnal steps
|
105
|
+
hMeta['bootstrap'] = hParams[:bootstrap] if hParams[:bootstrap]
|
106
|
+
|
107
|
+
config.set(:meta_data, hMeta) # Used by :server object
|
108
|
+
|
109
|
+
Logging.info("Metadata set:\n%s" % hMeta)
|
110
|
+
|
111
|
+
oMetaData = register(hMeta, sObjectType)
|
112
|
+
oMetaData[:meta_data] = hMeta
|
113
|
+
|
114
|
+
oMetaData
|
115
|
+
end
|
116
|
+
|
117
|
+
def build_forge(sObjectType, hParams)
|
118
|
+
|
119
|
+
oServer = object.Create(:server)
|
120
|
+
|
121
|
+
# Define the log lines to get and test.
|
122
|
+
config.set(:log_lines, 5)
|
123
|
+
|
124
|
+
Logging.info("Maestro server '%s' id is '%s'." % [oServer[:name], oServer[:id]])
|
125
|
+
# Waiting for server to come online before assigning a public IP.
|
126
|
+
|
127
|
+
sStatus = :checking
|
128
|
+
maestro_create_status(sStatus)
|
129
|
+
if oServer[:attrs][:status] == :active
|
130
|
+
oAddresses = object.Query(:public_ip, :server_id => oServer[:id])
|
131
|
+
if oAddresses.length == 0
|
132
|
+
sStatus = :assign_ip
|
133
|
+
else
|
134
|
+
oAddress = oAddresses[0]
|
135
|
+
sMsg = <<-END
|
136
|
+
Your server is up and running and is publically accessible through IP '#{oAddress[:public_ip]}'.
|
137
|
+
|
138
|
+
You can connect to '#{oServer[:name]}' with:
|
139
|
+
ssh ubuntu@#{oAddress[:public_ip]} -o StrictHostKeyChecking=no -i #{get_data(:keypairs, :private_key_file)}
|
140
|
+
END
|
141
|
+
if not object.get_data(:keypairs)[:coherent]
|
142
|
+
sMsg += ANSI.bold("\nUnfortunatelly") + " your current keypair is not usable to connect to your server.\nYou need to fix this issue to gain access to your server."
|
143
|
+
end
|
144
|
+
Logging.info(sMsg)
|
145
|
+
|
146
|
+
oLog = object.Get(:server_log, 5)[:attrs][:output]
|
147
|
+
if /cloud-init boot finished/ =~ oLog
|
148
|
+
sStatus = :active
|
149
|
+
else
|
150
|
+
sStatus = :cloud_init
|
151
|
+
end
|
152
|
+
end
|
153
|
+
else
|
154
|
+
sleep 5
|
155
|
+
sStatus = :starting
|
156
|
+
end
|
157
|
+
|
158
|
+
while sStatus != :active
|
159
|
+
maestro_create_status(sStatus)
|
160
|
+
begin
|
161
|
+
oServer = object.Get(:server, oServer[:attrs][:id])
|
162
|
+
rescue => e
|
163
|
+
Logging.error(e.message)
|
164
|
+
end
|
165
|
+
if sStatus == :starting
|
166
|
+
if oServer[:attrs][:status] == :active
|
167
|
+
sStatus = :assign_ip
|
168
|
+
end
|
169
|
+
elsif sStatus == :assign_ip
|
170
|
+
# Assigning Public IP.
|
171
|
+
oAddress = object.Create(:public_ip)
|
172
|
+
sMsg = <<-END
|
173
|
+
Public IP for server '#{oServer[:name]}' is assigned'
|
174
|
+
Now, as soon as the server respond to the ssh port, you will be able to get a tail of the build with:
|
175
|
+
while [ 1 = 1 ]
|
176
|
+
do
|
177
|
+
ssh ubuntu@#{oAddress[:public_ip]} -o StrictHostKeyChecking=no -i #{get_data(:keypairs, :private_key_file)} tail -f /var/log/cloud-init.log
|
178
|
+
sleep 5
|
179
|
+
done
|
180
|
+
END
|
181
|
+
if not object.get_data(:keypairs)[:coherent]
|
182
|
+
sMsg += ANSI.bold("\nUnfortunatelly") + " your current keypair is not usable to connect to your server.\nYou need to fix this issue to gain access to your server."
|
183
|
+
end
|
184
|
+
Logging.info(sMsg)
|
185
|
+
sStatus = :cloud_init
|
186
|
+
elsif sStatus == :cloud_init
|
187
|
+
oLog = object.Get(:server_log, 5)[:attrs][:output]
|
188
|
+
if /cloud-init boot finished/ =~ oLog
|
189
|
+
sStatus = :active
|
190
|
+
end
|
191
|
+
end
|
192
|
+
sleep(5) if sStatus != :active
|
193
|
+
end
|
194
|
+
Logging.info("Server '%s' is now ACTIVE. Bootstrap done." % oServer[:name])
|
195
|
+
oServer
|
196
|
+
end
|
197
|
+
|
198
|
+
def maestro_create_status(sStatus)
|
199
|
+
case sStatus
|
200
|
+
when :checking
|
201
|
+
Logging.state("Checking server status")
|
202
|
+
when :starting
|
203
|
+
Logging.state("STARTING")
|
204
|
+
when :assign_ip
|
205
|
+
Logging.state("ACTIVE - Assigning Public IP")
|
206
|
+
when :cloud_init
|
207
|
+
Logging.state("ACTIVE - Currently running cloud-init. Be patient.")
|
208
|
+
when :active
|
209
|
+
Logging.info("Server is active")
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def clone_or_use_maestro_repo(sObjectType, hParams)
|
214
|
+
|
215
|
+
maestro_url = hParams[:maestro_url]
|
216
|
+
maestro_repo = File.expand_path(hParams[:maestro_repo]) unless hParams[:maestro_repo].nil?
|
217
|
+
path_maestro = File.expand_path('~/.forj/')
|
218
|
+
hResult = {}
|
219
|
+
|
220
|
+
begin
|
221
|
+
if maestro_repo and File.directory?(maestro_repo)
|
222
|
+
Logging.info("Using maestro repo '%s'" % maestro_repo)
|
223
|
+
hResult[:maestro_repo] = maestro_repo
|
224
|
+
else
|
225
|
+
hResult[:maestro_repo] = File.join(path_maestro, 'maestro')
|
226
|
+
Logging.state("Cloning maestro repo from '%s' to '%s'" % [maestro_url, File.join(path_maestro, 'maestro')])
|
227
|
+
if File.directory?(path_maestro)
|
228
|
+
if File.directory?(File.join(path_maestro, 'maestro'))
|
229
|
+
FileUtils.rm_r File.join(path_maestro, 'maestro')
|
230
|
+
end
|
231
|
+
end
|
232
|
+
git = Git.clone(maestro_url, 'maestro', :path => path_maestro)
|
233
|
+
git.checkout(config[:branch]) if config[:branch] != 'master'
|
234
|
+
Logging.info("Maestro repo '%s' cloned on branch '%s'" % [File.join(path_maestro, 'maestro'), config[:branch]])
|
235
|
+
end
|
236
|
+
rescue => e
|
237
|
+
Logging.error("Error while cloning the repo from %s\n%s\n%s" % [maestro_url, e.message, e.backtrace.join("\n")])
|
238
|
+
Logging.info("If this error persist you could clone the repo manually in ~/.forj/")
|
239
|
+
end
|
240
|
+
oMaestro = register(hResult, sObjectType)
|
241
|
+
oMaestro[:maestro_repo] = hResult[:maestro_repo]
|
242
|
+
oMaestro
|
243
|
+
end
|
244
|
+
|
245
|
+
def create_or_use_infra(sObjectType, hParams)
|
246
|
+
infra = File.expand_path(hParams[:infra_repo])
|
247
|
+
maestro_repo = hParams[:maestro_repository, :maestro_repo]
|
248
|
+
branch = hParams[:branch]
|
249
|
+
dest_cloud_init = File.join(infra, 'cloud-init')
|
250
|
+
template = File.join(maestro_repo, 'templates', 'infra')
|
251
|
+
cloud_init = File.join(template, 'cloud-init')
|
252
|
+
|
253
|
+
hInfra = { :infra_repo => dest_cloud_init}
|
254
|
+
|
255
|
+
AppInit.ensure_dir_exists(dest_cloud_init)
|
256
|
+
|
257
|
+
bReBuildInfra = infra_is_original?(infra, maestro_repo)
|
258
|
+
|
259
|
+
if bReBuildInfra
|
260
|
+
Logging.state("Building your infra workspace in '%s'" % [infra])
|
261
|
+
|
262
|
+
Logging.debug("Copying recursively '%s' to '%s'" % [cloud_init, infra])
|
263
|
+
FileUtils.copy_entry(cloud_init, dest_cloud_init)
|
264
|
+
|
265
|
+
file_ver = File.join(infra, 'forj-cli.ver')
|
266
|
+
File.write(file_ver, $INFRA_VERSION)
|
267
|
+
Logging.info("The infra workspace '%s' has been built from maestro predefined files." % [infra])
|
268
|
+
else
|
269
|
+
Logging.info("Re-using your infra... in '%s'" % [infra])
|
270
|
+
end
|
271
|
+
|
272
|
+
|
273
|
+
oInfra = register(hInfra, sObjectType)
|
274
|
+
oInfra[:infra_repo] = hInfra[:infra_repo]
|
275
|
+
oInfra
|
276
|
+
end
|
277
|
+
|
278
|
+
# Function which compare directories from maestro templates to infra.
|
279
|
+
def infra_is_original?(infra_dir, maestro_dir)
|
280
|
+
dest_cloud_init = File.join(infra_dir, 'cloud-init')
|
281
|
+
template = File.join(maestro_dir, 'templates', 'infra')
|
282
|
+
sMD5List = File.join(infra_dir, '.maestro_original.yaml')
|
283
|
+
bResult = true
|
284
|
+
hResult = {}
|
285
|
+
if File.exists?(sMD5List)
|
286
|
+
begin
|
287
|
+
hResult = YAML.load_file(sMD5List)
|
288
|
+
rescue => e
|
289
|
+
Logging.error("Unable to load valid Original files list '%s'. Your infra workspace won't be migrated, until fixed." % sMD5List)
|
290
|
+
bResult = false
|
291
|
+
end
|
292
|
+
if not hResult
|
293
|
+
hResult = {}
|
294
|
+
bResult = false
|
295
|
+
end
|
296
|
+
end
|
297
|
+
# We are taking care on bootstrap files only.
|
298
|
+
Find.find(File.join(template, 'cloud-init')) { | path |
|
299
|
+
if not File.directory?(path)
|
300
|
+
sMaestroRelPath = path.clone
|
301
|
+
sMaestroRelPath[File.join(template, 'cloud-init/')] = ""
|
302
|
+
sInfra_path = File.join(dest_cloud_init, sMaestroRelPath)
|
303
|
+
if File.exists?(sInfra_path)
|
304
|
+
md5_file = Digest::MD5.file(sInfra_path).hexdigest
|
305
|
+
if hResult.key?(sMaestroRelPath) and hResult[sMaestroRelPath] != md5_file
|
306
|
+
bResult = false
|
307
|
+
Logging.info("'%s' infra file has changed from original template in maestro." % sInfra_path)
|
308
|
+
else
|
309
|
+
Logging.debug("'%s' infra file has not been updated." % sInfra_path)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
md5_file = Digest::MD5.file(path).hexdigest
|
313
|
+
hResult[sMaestroRelPath] = md5_file
|
314
|
+
end
|
315
|
+
}
|
316
|
+
begin
|
317
|
+
File.open(sMD5List, 'w') do |out|
|
318
|
+
YAML.dump(hResult, out)
|
319
|
+
end
|
320
|
+
rescue => e
|
321
|
+
Logging.error("%s\n%s" % [e.message, e.backtrace.join("\n")])
|
322
|
+
end
|
323
|
+
if bResult
|
324
|
+
Logging.debug("No original files found has been updated. Infra workspace can be updated/created if needed.")
|
325
|
+
else
|
326
|
+
Logging.warning("At least, one file has been updated. Infra workspace won't be updated by forj cli.")
|
327
|
+
end
|
328
|
+
bResult
|
329
|
+
end
|
330
|
+
|
331
|
+
def infra_rebuild(infra_dir)
|
332
|
+
return false if not File.exists?(infra_dir)
|
333
|
+
|
334
|
+
file_ver = File.join(infra_dir, 'forj-cli.ver')
|
335
|
+
forj_infra_version = nil
|
336
|
+
forj_infra_version = File.read(file_ver) if File.exist?(file_ver)
|
337
|
+
|
338
|
+
if forj_infra_version.nil? or forj_infra_version == ""
|
339
|
+
# Prior version 37
|
340
|
+
return(old_infra_data_update(oConfig, '0.0.36', infra_dir))
|
341
|
+
elsif Gem::Version.new(forj_infra_version) < Gem::Version.new($INFRA_VERSION)
|
342
|
+
return(old_infra_data_update(oConfig, forj_infra_version, infra_dir))
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
def old_infra_data_update(oConfig, version, infra_dir)
|
347
|
+
Logging.info("Migrating your local infra repo (%s) to the latest version." % version)
|
348
|
+
bRebuild = false # Be default migration is successful. No need to rebuild it.
|
349
|
+
case version
|
350
|
+
when '0.0.36'
|
351
|
+
# Moving from 0.0.36 or less to 0.0.37 or higher.
|
352
|
+
# SET_COMPUTE="{SET_COMPUTE!}" => Setting for Compute. ignored. Coming from HPC
|
353
|
+
# SET_TENANT_NAME="{SET_TENANT_NAME!}" => Setting for Compute. ignored. Need to query HPC from current Tenant ID
|
354
|
+
|
355
|
+
# SET_DNS_TENANTID="{SET_DNS_TENANTID!}" => Setting for DNS. meta = dns_tenantid
|
356
|
+
# ==> :forj_accounts, sAccountName, :dns, :tenant_id
|
357
|
+
|
358
|
+
# SET_DNS_ZONE="{SET_DNS_ZONE!}" => Setting for DNS. meta = dns_zone
|
359
|
+
# ==> :forj_accounts, sAccountName, :dns, :service
|
360
|
+
|
361
|
+
# SET_DOMAIN="{SET_DOMAIN!}" => Setting for Maestro (required) and DNS if enabled.
|
362
|
+
# ==> :forj_accounts, sAccountName, :dns, :domain_name
|
363
|
+
sAccountName = oConfig.get(:account_name)
|
364
|
+
|
365
|
+
yDns = {}
|
366
|
+
yDns = oConfig.oConfig.ExtraGet(:forj_accounts, sAccountName, :dns) if oConfig.oConfig.ExtraExist?(:forj_accounts, sAccountName, :dns)
|
367
|
+
Dir.foreach(infra_dir) do | file |
|
368
|
+
next if not /^maestro\.box\..*\.env$/ =~ file
|
369
|
+
build_env = File.join(infra_dir, file)
|
370
|
+
Logging.debug("Reading data from '%s'" % build_env)
|
371
|
+
tags = {'SET_DNS_TENANTID' => :tenant_id,
|
372
|
+
'SET_DNS_ZONE' => :service,
|
373
|
+
'SET_DOMAIN' => :domain_name
|
374
|
+
}
|
375
|
+
begin
|
376
|
+
bUpdate = nil
|
377
|
+
|
378
|
+
File.open(build_env) do |f|
|
379
|
+
f.each_line do |line|
|
380
|
+
mObj = line.match(/^(SET_[A-Z_]+)=["'](.*)["'].*$/)
|
381
|
+
if mObj
|
382
|
+
Logging.debug("Reviewing detected '%s' tag" % [mObj[1]])
|
383
|
+
tag = (tags[mObj[1]]? tags[mObj[1]] : nil)
|
384
|
+
if tag and mObj[2]
|
385
|
+
if bUpdate == nil and rhGet(yDns, tag) and rhGet(yDns, tag) != mObj[2]
|
386
|
+
Logging.message("Your account setup is different than build env.")
|
387
|
+
Logging.message("We suggest you to update your account setup with data from your build env.")
|
388
|
+
bUpdate = agree("Do you want to update your setup with those build environment data?")
|
389
|
+
end
|
390
|
+
if bUpdate != nil and bUpdate
|
391
|
+
Logging.debug("Saved: '%s' = '%s'" % [mObj[1],mObj[2]])
|
392
|
+
rhSet(yDns, mObj[2], tag)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
rescue => e
|
399
|
+
Logging.fatal(1, "Failed to open the build environment file '%s'" % build_env, e)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
file_ver = File.join(infra_dir, 'forj-cli.ver')
|
403
|
+
File.write(file_ver, $INFRA_VERSION)
|
404
|
+
oConfig.oConfig.ExtraSet(:forj_accounts, sAccountName, :dns, yDns)
|
405
|
+
oConfig.oConfig.ExtraSave(File.join($FORJ_ACCOUNTS_PATH, sAccountName), :forj_accounts, sAccountName)
|
406
|
+
return bRebuild
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
def build_userdata(sObjectType, hParams)
|
411
|
+
# get the paths for maestro and infra repositories
|
412
|
+
maestro_path = hParams[:maestro_repository].values
|
413
|
+
infra_path = hParams[:infra_repository].values
|
414
|
+
|
415
|
+
# concatenate the paths for boothook and cloud_config files
|
416
|
+
#~ build_dir = File.expand_path(File.join($FORJ_DATA_PATH, '.build'))
|
417
|
+
#~ boothook = File.join(maestro_path, 'build', 'bin', 'build-tools')
|
418
|
+
#~ cloud_config = File.join(maestro_path, 'build', 'maestro')
|
419
|
+
|
420
|
+
mime = File.join($FORJ_BUILD_PATH, 'userdata.mime.%s' % rand(36**5).to_s(36))
|
421
|
+
|
422
|
+
meta_data = JSON.generate(hParams[:metadata, :meta_data])
|
423
|
+
|
424
|
+
build_tmpl_dir = File.expand_path(File.join($LIB_PATH, 'build_tmpl'))
|
425
|
+
|
426
|
+
Logging.state("Preparing user_data - file '%s'" % mime)
|
427
|
+
# generate boot_*.sh
|
428
|
+
mime_cmd = "#{build_tmpl_dir}/write-mime-multipart.py"
|
429
|
+
bootstrap = "#{build_tmpl_dir}/bootstrap_build.sh"
|
430
|
+
|
431
|
+
cmd = "%s '%s' '%s' '%s' '%s' '%s' '%s' '%s'" % [
|
432
|
+
bootstrap, # script
|
433
|
+
$FORJ_DATA_PATH, # $1 = Forj data base dir
|
434
|
+
hParams[:maestro_repository, :maestro_repo], # $2 = Maestro repository dir
|
435
|
+
config[:bootstrap_dirs], # $3 = Bootstrap directories
|
436
|
+
config[:bootstrap_extra_dir], # $4 = Bootstrap extra directory
|
437
|
+
meta_data, # $5 = meta_data (string)
|
438
|
+
mime_cmd, # $6: mime script file to execute.
|
439
|
+
mime # $7: mime file generated.
|
440
|
+
]
|
441
|
+
raise ForjError.new, "#{bootstrap} script file is not found." if not File.exists?(bootstrap)
|
442
|
+
Logging.debug("Running '%s'" % cmd)
|
443
|
+
Kernel.system(cmd)
|
444
|
+
|
445
|
+
raise ForjError.new(), "mime file '%s' not found." % mime if not File.exists?(mime)
|
446
|
+
|
447
|
+
begin
|
448
|
+
user_data = File.read(mime)
|
449
|
+
rescue => e
|
450
|
+
Logging.fatal(1, e.message)
|
451
|
+
end
|
452
|
+
if $LIB_FORJ_DEBUG < 5
|
453
|
+
File.delete(mime)
|
454
|
+
else
|
455
|
+
ForjLib.debug(5, "user_data temp file '%s' kept" % mime)
|
456
|
+
end
|
457
|
+
|
458
|
+
config[:user_data] = user_data
|
459
|
+
|
460
|
+
oUserData = register(hParams, sObjectType)
|
461
|
+
oUserData[:user_data] = user_data
|
462
|
+
oUserData[:user_data_encoded] = Base64.strict_encode64(user_data)
|
463
|
+
oUserData[:mime] = mime
|
464
|
+
Logging.info("user_data prepared. File: '%s'" % mime)
|
465
|
+
oUserData
|
466
|
+
end
|
467
|
+
|
468
|
+
end
|
469
|
+
|
470
|
+
# Functions for setup
|
471
|
+
class ForjCoreProcess
|
472
|
+
|
473
|
+
# Check files existence
|
474
|
+
def forj_check_keypairs_files(keypath)
|
475
|
+
key_name = config.get(:keypair_name)
|
476
|
+
|
477
|
+
keys_entered = keypair_detect(key_name, keypath)
|
478
|
+
if not keys_entered[:private_key_exist? ] and not keys_entered[:public_key_exist? ]
|
479
|
+
if agree("The key you entered was not found. Do you want to create this one?")
|
480
|
+
base_dir = keys_entered[:keypair_path]
|
481
|
+
if not File.directory?(base_dir)
|
482
|
+
if agree("'%s' doesn't exist. Do you want to create it?" % base_dir)
|
483
|
+
AppInit.ensure_dir_exists(base_dir)
|
484
|
+
else
|
485
|
+
return false
|
486
|
+
end
|
487
|
+
end
|
488
|
+
else
|
489
|
+
return false
|
490
|
+
end
|
491
|
+
end
|
492
|
+
true
|
493
|
+
end
|
494
|
+
|
495
|
+
# keypair_files post setup
|
496
|
+
def forj_setup_keypairs_files
|
497
|
+
# Getting Account keypair information
|
498
|
+
key_name = config.get(:keypair_name)
|
499
|
+
key_path = File.expand_path(config.get(:keypair_files))
|
500
|
+
|
501
|
+
keys_imported = nil
|
502
|
+
keys_imported = keypair_detect(key_name, config.oConfig.LocalGet(key_name, :imported_keys)) if config.oConfig.LocalExist?(key_name, :imported_keys)
|
503
|
+
keys = keypair_detect(key_name, key_path)
|
504
|
+
|
505
|
+
if keys_imported and keys_imported[:key_basename] != keys[:key_basename] and $FORJ_KEYPAIRS_PATH != keys[:keypair_path]
|
506
|
+
Logging.warning("The private key '%s' was assigned to a different private key file '%s'.\nTo not overwrite it, we recommend you to choose a different keypair name." % [key_name, keys_imported[:key_basename] ])
|
507
|
+
new_key_name = key_name
|
508
|
+
sMsg = "Please, provide a different keypair name:"
|
509
|
+
while key_name == new_key_name
|
510
|
+
new_key_name = ask (sMsg) do | q |
|
511
|
+
q.validate = /.+/
|
512
|
+
end
|
513
|
+
new_key_name = new_key_name.to_s
|
514
|
+
sMsg = "Incorrect. You have to choose a keypair name different than '#{key_name}'. If you want to interrupt, press Ctrl-C and retry later.\nSo, please, provide a different keypair name:" if key_name == new_key_name
|
515
|
+
end
|
516
|
+
key_name = new_key_name
|
517
|
+
config.set(:key_name, key_name)
|
518
|
+
keys = keypair_detect(key_name, key_path)
|
519
|
+
end
|
520
|
+
|
521
|
+
private_key_file = File.join(keys[:keypair_path], keys[:private_key_name])
|
522
|
+
public_key_file = File.join(keys[:keypair_path], keys[:public_key_name])
|
523
|
+
|
524
|
+
|
525
|
+
# Creation sequences
|
526
|
+
if not keys[:private_key_exist? ]
|
527
|
+
# Need to create a key. ask if we need so.
|
528
|
+
Logging.message("The private key file attached to keypair named '%s' is not found. Running ssh-keygen to create it." % keys[:keypair_name])
|
529
|
+
if not File.exists?(private_key_file)
|
530
|
+
AppInit.ensure_dir_exists(File.dirname(private_key_file))
|
531
|
+
command = 'ssh-keygen -t rsa -f %s' % private_key_file
|
532
|
+
Logging.debug("Executing '%s'" % command)
|
533
|
+
system(command)
|
534
|
+
end
|
535
|
+
if not File.exists?(private_key_file)
|
536
|
+
Logging.fatal(1, "'%s' not found. Unable to add your keypair to hpcloud. Create it yourself and provide it with -p option. Then retry." % [private_key_file])
|
537
|
+
else
|
538
|
+
Logging.fatal(1, "ssh-keygen did not created your key pairs. Aborting. Please review errors in ~/.forj/forj.log")
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
if not keys[:public_key_exist? ]
|
543
|
+
Logging.message("Your public key '%s' was not found. Getting it from the private one. It may require your passphrase." % [public_key_file])
|
544
|
+
command = 'ssh-keygen -y -f %s > %s' % [private_key_file,public_key_file ]
|
545
|
+
Logging.debug("Executing '%s'" % command)
|
546
|
+
system(command)
|
547
|
+
end
|
548
|
+
|
549
|
+
forj_private_key_file = File.join($FORJ_KEYPAIRS_PATH, key_name )
|
550
|
+
forj_public_key_file = File.join($FORJ_KEYPAIRS_PATH, key_name + ".pub")
|
551
|
+
|
552
|
+
# Saving sequences
|
553
|
+
|
554
|
+
if keys[:keypair_path] != $FORJ_KEYPAIRS_PATH
|
555
|
+
if not File.exists?(forj_private_key_file) or not File.exists?(forj_public_key_file)
|
556
|
+
Logging.info("Importing key pair to FORJ keypairs list.")
|
557
|
+
FileUtils.copy(private_key_file, forj_private_key_file)
|
558
|
+
FileUtils.copy(public_key_file, forj_public_key_file)
|
559
|
+
# Attaching this keypair to the account
|
560
|
+
rhSet(@hAccountData, key_name, :credentials, 'keypair_name')
|
561
|
+
rhSet(@hAccountData, forj_private_key_file, :credentials, 'keypair_path')
|
562
|
+
config.oConfig.LocalSet(key_name.to_s, private_key_file, :imported_keys)
|
563
|
+
else
|
564
|
+
# Checking source/dest files content
|
565
|
+
if Digest::MD5.file(private_key_file).hexdigest != Digest::MD5.file(forj_private_key_file).hexdigest
|
566
|
+
Logging.info("Updating private key keypair piece to FORJ keypairs list.")
|
567
|
+
FileUtils.copy(private_key_file, forj_private_key_file)
|
568
|
+
else
|
569
|
+
Logging.info("Private key keypair up to date.")
|
570
|
+
end
|
571
|
+
if Digest::MD5.file(public_key_file).hexdigest != Digest::MD5.file(forj_public_key_file).hexdigest
|
572
|
+
Logging.info("Updating public key keypair piece to FORJ keypairs list.")
|
573
|
+
FileUtils.copy(public_key_file, forj_public_key_file)
|
574
|
+
else
|
575
|
+
Logging.info("Public key keypair up to date.")
|
576
|
+
end
|
577
|
+
end
|
578
|
+
end
|
579
|
+
# Saving internal copy of private key file for forj use.
|
580
|
+
config.set(:keypair_path, forj_private_key_file )
|
581
|
+
Logging.info("Configured forj keypair '%s' with '%s'" % [ keys[:keypair_name], File.join(keys[:keypair_path], keys[:key_basename]) ] )
|
582
|
+
end
|
583
|
+
|
584
|
+
def forj_DNS_settings()
|
585
|
+
sAsk = "Optionally, you can ask Maestro to use/manage a domain name on your cloud. It requires your DNS cloud service to be enabled.\nDo you want to configure it?"
|
586
|
+
config.set(:dns_settings, agree(sAsk))
|
587
|
+
true
|
588
|
+
end
|
589
|
+
|
590
|
+
def forj_DNS_settings?(sKey)
|
591
|
+
# Return true to ask the question. false otherwise
|
592
|
+
if not config.get(:dns_settings)
|
593
|
+
config.set(sKey, nil)
|
594
|
+
return false # Do not ask
|
595
|
+
end
|
596
|
+
true
|
597
|
+
end
|
598
|
+
|
599
|
+
def setup_tenant_name()
|
600
|
+
# TODO: To re-introduce with a Controller call instead.
|
601
|
+
oSSLError=SSLErrorMgt.new # Retry object
|
602
|
+
Logging.debug("Getting tenants from hpcloud cli libraries")
|
603
|
+
begin
|
604
|
+
tenants = Connection.instance.tenants(@sAccountName)
|
605
|
+
rescue => e
|
606
|
+
if not oSSLError.ErrorDetected(e.message,e.backtrace, e)
|
607
|
+
retry
|
608
|
+
end
|
609
|
+
Logging.fatal(1, 'Network: Unable to connect.')
|
610
|
+
end
|
611
|
+
tenant_id = rhGet(@oConfig.ExtraGet(:hpc_accounts, @sAccountName, :credentials), :tenant_id)
|
612
|
+
tenant_name = nil
|
613
|
+
tenants.each { |elem| tenant_name = elem['name'] if elem['id'] == tenant_id }
|
614
|
+
if tenant_name
|
615
|
+
Logging.debug("Tenant ID '%s': '%s' found." % [tenant_id, tenant_name])
|
616
|
+
rhSet(@hAccountData, tenant_name, :maestro, :tenant_name)
|
617
|
+
else
|
618
|
+
Logging.error("Unable to find the tenant Name for '%s' ID." % tenant_id)
|
619
|
+
end
|
620
|
+
@oConfig.set('tenants', tenants)
|
621
|
+
end
|
622
|
+
|
623
|
+
end
|