bento-lpn 1.1.3
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 +7 -0
- data/.gitignore +50 -0
- data/.travis.yml +19 -0
- data/CHANGELOG.md +137 -0
- data/Gemfile +12 -0
- data/LICENSE +201 -0
- data/README.md +66 -0
- data/Rakefile +22 -0
- data/bento-lpn.gemspec +26 -0
- data/bin/bento +17 -0
- data/lib/bento.rb +2 -0
- data/lib/bento/build.rb +95 -0
- data/lib/bento/buildmetadata.rb +87 -0
- data/lib/bento/cli.rb +227 -0
- data/lib/bento/common.rb +105 -0
- data/lib/bento/delete.rb +22 -0
- data/lib/bento/normalize.rb +82 -0
- data/lib/bento/packerexec.rb +31 -0
- data/lib/bento/providermetadata.rb +84 -0
- data/lib/bento/release.rb +22 -0
- data/lib/bento/revoke.rb +22 -0
- data/lib/bento/test.rb +60 -0
- data/lib/bento/upload.rb +47 -0
- data/lib/bento/version.rb +3 -0
- data/templates/bootstrap.sh.erb +1 -0
- data/templates/kitchen.yml.erb +29 -0
- metadata +98 -0
data/lib/bento/common.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require "benchmark"
|
2
|
+
require "fileutils"
|
3
|
+
require "json"
|
4
|
+
require "tempfile"
|
5
|
+
require "yaml"
|
6
|
+
require "vagrant_cloud"
|
7
|
+
|
8
|
+
MEGABYTE = 1024.0 * 1024.0
|
9
|
+
|
10
|
+
module Common
|
11
|
+
def vc_account
|
12
|
+
VagrantCloud::Account.new(ENV["VAGRANT_CLOUD_ORG"], ENV["VAGRANT_CLOUD_TOKEN"])
|
13
|
+
end
|
14
|
+
|
15
|
+
def banner(msg)
|
16
|
+
puts "==> #{msg}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def info(msg)
|
20
|
+
puts " #{msg}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def warn(msg)
|
24
|
+
puts ">>> #{msg}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def duration(total)
|
28
|
+
total = 0 if total.nil?
|
29
|
+
minutes = (total / 60).to_i
|
30
|
+
seconds = (total - (minutes * 60))
|
31
|
+
format("%dm%.2fs", minutes, seconds)
|
32
|
+
end
|
33
|
+
|
34
|
+
def box_metadata(metadata_file)
|
35
|
+
metadata = Hash.new
|
36
|
+
file = File.read(metadata_file)
|
37
|
+
json = JSON.parse(file)
|
38
|
+
|
39
|
+
# metadata needed for upload: boxname, version, provider, box filename
|
40
|
+
metadata["name"] = json["name"]
|
41
|
+
metadata["version"] = json["version"]
|
42
|
+
metadata["box_basename"] = json["box_basename"]
|
43
|
+
metadata["tools"] = json["tools"]
|
44
|
+
metadata["providers"] = Hash.new
|
45
|
+
json["providers"].each do |provider|
|
46
|
+
metadata["providers"][provider["name"]] = provider.reject { |k, _| k == "name" }
|
47
|
+
end
|
48
|
+
metadata
|
49
|
+
end
|
50
|
+
|
51
|
+
def metadata_files
|
52
|
+
@metadata_files ||= compute_metadata_files
|
53
|
+
end
|
54
|
+
|
55
|
+
def compute_metadata_files
|
56
|
+
`ls builds/*.json`.split("\n")
|
57
|
+
end
|
58
|
+
|
59
|
+
def builds_yml
|
60
|
+
YAML.load(File.read("builds.yml"))
|
61
|
+
end
|
62
|
+
|
63
|
+
def build_list
|
64
|
+
bit32 = []
|
65
|
+
bit64 = []
|
66
|
+
builds_yml["public"].each do |platform, versions|
|
67
|
+
versions.each do |version, archs|
|
68
|
+
archs.each do |arch|
|
69
|
+
folder = case platform
|
70
|
+
when "opensuse-leap"
|
71
|
+
"opensuse"
|
72
|
+
when "oracle"
|
73
|
+
"oraclelinux"
|
74
|
+
else
|
75
|
+
platform
|
76
|
+
end
|
77
|
+
case arch
|
78
|
+
when "i386"
|
79
|
+
bit32 << "#{folder}/#{platform}-#{version}-#{arch}"
|
80
|
+
else
|
81
|
+
bit64 << "#{folder}/#{platform}-#{version}-#{arch}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
bit64 + bit32
|
87
|
+
end
|
88
|
+
|
89
|
+
def private_box?(boxname)
|
90
|
+
proprietary_os_list = %w{macos windows sles solaris rhel}
|
91
|
+
proprietary_os_list.any? { |p| boxname.include?(p) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def os_x?
|
95
|
+
!!(RUBY_PLATFORM =~ /darwin/)
|
96
|
+
end
|
97
|
+
|
98
|
+
def unix?
|
99
|
+
!windows?
|
100
|
+
end
|
101
|
+
|
102
|
+
def windows?
|
103
|
+
!!(RUBY_PLATFORM =~ /mswin|mingw|windows/)
|
104
|
+
end
|
105
|
+
end
|
data/lib/bento/delete.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "bento/common"
|
2
|
+
|
3
|
+
class DeleteRunner
|
4
|
+
include Common
|
5
|
+
|
6
|
+
attr_reader :box, :version
|
7
|
+
|
8
|
+
def initialize(opts)
|
9
|
+
@box = opts.box
|
10
|
+
@version = opts.version
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
banner("Starting Delete...")
|
15
|
+
time = Benchmark.measure do
|
16
|
+
box = vc_account.get_box(box)
|
17
|
+
version = box.get_version(version)
|
18
|
+
version.delete
|
19
|
+
end
|
20
|
+
banner("Delete finished in #{duration(time.real)}.")
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "bento/common"
|
2
|
+
|
3
|
+
class NormalizeRunner
|
4
|
+
include Common
|
5
|
+
include PackerExec
|
6
|
+
|
7
|
+
attr_reader :templates, :build_timestamp, :debug, :override_version
|
8
|
+
|
9
|
+
def initialize(opts)
|
10
|
+
@templates = opts.template_files
|
11
|
+
@debug = opts.debug
|
12
|
+
@modified = []
|
13
|
+
@build_timestamp = Time.now.gmtime.strftime("%Y%m%d%H%M%S")
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
banner("Normalizing for templates:")
|
18
|
+
templates.each { |t| puts "- #{t}" }
|
19
|
+
time = Benchmark.measure do
|
20
|
+
templates.each do |file|
|
21
|
+
dir, template = file.split("/")[0], file.split("/")[1]
|
22
|
+
Dir.chdir dir
|
23
|
+
validate(template)
|
24
|
+
Dir.chdir("..")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
if !@modified.empty?
|
28
|
+
info("")
|
29
|
+
info("The following templates were modified:")
|
30
|
+
@modified.sort.each { |template| info(" * #{template}") }
|
31
|
+
end
|
32
|
+
banner("Normalizing finished in #{duration(time.real)}.")
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def checksum(file)
|
38
|
+
Digest::MD5.file(file).hexdigest
|
39
|
+
end
|
40
|
+
|
41
|
+
def fix(template)
|
42
|
+
file = "#{template}.json"
|
43
|
+
|
44
|
+
banner("[#{template}] Fixing")
|
45
|
+
original_checksum = checksum(file)
|
46
|
+
output = `packer fix #{file}`
|
47
|
+
raise "[#{template}] Error fixing, exited #{$?}" if $?.exitstatus != 0
|
48
|
+
# preserve ampersands in shell commands,
|
49
|
+
# see: https://github.com/mitchellh/packer/issues/784
|
50
|
+
output.gsub!("\\u0026", "&")
|
51
|
+
File.open(file, "wb") { |dest| dest.write(output) }
|
52
|
+
fixed_checksum = checksum(file)
|
53
|
+
|
54
|
+
if original_checksum == fixed_checksum
|
55
|
+
puts("No changes made.")
|
56
|
+
else
|
57
|
+
warn("Template #{template} has been modified.")
|
58
|
+
@modified << template
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def packer_validate_cmd(template, var_file)
|
63
|
+
vars = "#{template}.variables.json"
|
64
|
+
cmd = %W{packer validate -var-file=#{var_file} #{template}.json}
|
65
|
+
cmd.insert(2, "-var-file=#{vars}") if File.exist?(vars)
|
66
|
+
cmd
|
67
|
+
end
|
68
|
+
|
69
|
+
def validate(template)
|
70
|
+
for_packer_run_with(template) do |md_file, var_file|
|
71
|
+
cmd = packer_validate_cmd(template, var_file.path)
|
72
|
+
banner("[#{template}] Validating: '#{cmd.join(' ')}'")
|
73
|
+
if debug
|
74
|
+
banner("[#{template}] DEBUG: var_file(#{var_file.path}) is:")
|
75
|
+
puts IO.read(var_file.path)
|
76
|
+
banner("[#{template}] DEBUG: md_file(#{md_file.path}) is:")
|
77
|
+
puts IO.read(md_file.path)
|
78
|
+
end
|
79
|
+
system(*cmd) || raise( "[#{template}] Error validating, exited #{$?}")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
module PackerExec
|
3
|
+
def for_packer_run_with(template)
|
4
|
+
Tempfile.open("#{template}-metadata.json") do |md_file|
|
5
|
+
Tempfile.open("#{template}-metadata-var-file") do |var_file|
|
6
|
+
write_box_metadata(template, md_file)
|
7
|
+
write_var_file(template, md_file, var_file)
|
8
|
+
yield md_file, var_file
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def write_box_metadata(template, io)
|
14
|
+
md = BuildMetadata.new(template, build_timestamp, override_version).read
|
15
|
+
io.write(JSON.pretty_generate(md))
|
16
|
+
io.close
|
17
|
+
end
|
18
|
+
|
19
|
+
def write_var_file(template, md_file, io)
|
20
|
+
md = BuildMetadata.new(template, build_timestamp, override_version).read
|
21
|
+
|
22
|
+
io.write(JSON.pretty_generate({
|
23
|
+
box_basename: md[:box_basename],
|
24
|
+
build_timestamp: md[:build_timestamp],
|
25
|
+
git_revision: md[:git_revision],
|
26
|
+
metadata: md_file.path,
|
27
|
+
version: md[:version],
|
28
|
+
}))
|
29
|
+
io.close
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "digest"
|
2
|
+
require "bento/common"
|
3
|
+
|
4
|
+
class ProviderMetadata
|
5
|
+
include Common
|
6
|
+
|
7
|
+
def initialize(path, box_basename)
|
8
|
+
@base = File.join(path, box_basename)
|
9
|
+
end
|
10
|
+
|
11
|
+
def read
|
12
|
+
Dir.glob("#{base}.*.box").map do |file|
|
13
|
+
{
|
14
|
+
name: provider_from_file(file),
|
15
|
+
version: version(provider_from_file(file)),
|
16
|
+
file: "#{File.basename(file)}",
|
17
|
+
checksum_type: "sha256",
|
18
|
+
checksum: shasum(file),
|
19
|
+
size: "#{size_in_mb(file)} MB",
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :base
|
27
|
+
|
28
|
+
def provider_from_file(file)
|
29
|
+
provider = file.sub(/^.*\.([^.]+)\.box$/, '\1')
|
30
|
+
if provider == "vmware"
|
31
|
+
"vmware_desktop"
|
32
|
+
else
|
33
|
+
provider
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def shasum(file)
|
38
|
+
Digest::SHA256.file(file).hexdigest
|
39
|
+
end
|
40
|
+
|
41
|
+
def size_in_mb(file)
|
42
|
+
size = File.size(file)
|
43
|
+
size_mb = size / MEGABYTE
|
44
|
+
size_mb.ceil.to_s
|
45
|
+
end
|
46
|
+
|
47
|
+
def version(provider)
|
48
|
+
case provider
|
49
|
+
when /vmware/
|
50
|
+
ver_vmware
|
51
|
+
when /virtualbox/
|
52
|
+
ver_vbox
|
53
|
+
when /parallels/
|
54
|
+
ver_parallels
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def ver_vmware
|
59
|
+
if os_x?
|
60
|
+
path = File.join('/Applications/VMware\ Fusion.app/Contents/Library')
|
61
|
+
fusion_cmd = File.join(path, "vmware-vmx -v")
|
62
|
+
cmd = Mixlib::ShellOut.new(fusion_cmd)
|
63
|
+
cmd.run_command
|
64
|
+
cmd.stderr.split(" ")[5]
|
65
|
+
else
|
66
|
+
cmd = Mixlib::ShellOut.new("vmware --version")
|
67
|
+
cmd.run_command
|
68
|
+
cmd.stdout.split(" ")[2]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def ver_parallels
|
73
|
+
raise "Platform is not macOS / OS X, exiting..." unless os_x?
|
74
|
+
cmd = Mixlib::ShellOut.new("prlctl --version")
|
75
|
+
cmd.run_command
|
76
|
+
cmd.stdout.split(" ")[2]
|
77
|
+
end
|
78
|
+
|
79
|
+
def ver_vbox
|
80
|
+
cmd = Mixlib::ShellOut.new("VBoxManage --version")
|
81
|
+
cmd.run_command
|
82
|
+
cmd.stdout.split("r")[0]
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "bento/common"
|
2
|
+
|
3
|
+
class ReleaseRunner
|
4
|
+
include Common
|
5
|
+
|
6
|
+
attr_reader :box, :version
|
7
|
+
|
8
|
+
def initialize(opts)
|
9
|
+
@box = opts.box
|
10
|
+
@version = opts.version
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
banner("Releasing #{box}/#{version}...")
|
15
|
+
time = Benchmark.measure do
|
16
|
+
b = vc_account.get_box(box)
|
17
|
+
v = b.get_version(version)
|
18
|
+
v.release
|
19
|
+
end
|
20
|
+
banner("Release finished in #{duration(time.real)}.")
|
21
|
+
end
|
22
|
+
end
|
data/lib/bento/revoke.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "bento/common"
|
2
|
+
|
3
|
+
class RevokeRunner
|
4
|
+
include Common
|
5
|
+
|
6
|
+
attr_reader :box, :version
|
7
|
+
|
8
|
+
def initialize(opts)
|
9
|
+
@box = opts.box
|
10
|
+
@version = opts.version
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
banner("Revoking #{box}/#{version}...")
|
15
|
+
time = Benchmark.measure do
|
16
|
+
box = vc_account.get_box(box)
|
17
|
+
version = box.get_version(version)
|
18
|
+
version.revoke
|
19
|
+
end
|
20
|
+
banner("Revoke finished in #{duration(time.real)}.")
|
21
|
+
end
|
22
|
+
end
|
data/lib/bento/test.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require "bento/common"
|
2
|
+
require "mixlib/shellout"
|
3
|
+
require "erb"
|
4
|
+
|
5
|
+
class TestRunner
|
6
|
+
include Common
|
7
|
+
|
8
|
+
attr_reader :shared_folder, :boxname, :provider, :box_url, :no_shared, :provisioner
|
9
|
+
|
10
|
+
def initialize(opts)
|
11
|
+
@debug = opts.debug
|
12
|
+
@no_shared = opts.no_shared
|
13
|
+
@provisioner = opts.provisioner.nil? ? "shell" : opts.provisioner
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
banner("Starting testing...")
|
18
|
+
time = Benchmark.measure do
|
19
|
+
metadata_files.each do |metadata_file|
|
20
|
+
destroy_all_bento
|
21
|
+
test_box(metadata_file)
|
22
|
+
destroy_all_bento
|
23
|
+
end
|
24
|
+
end
|
25
|
+
banner("Testing finished in #{duration(time.real)}.")
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def destroy_all_bento
|
31
|
+
cmd = Mixlib::ShellOut.new("vagrant box list | grep 'bento-'")
|
32
|
+
cmd.run_command
|
33
|
+
boxes = cmd.stdout.split("\n")
|
34
|
+
|
35
|
+
boxes.each do |box|
|
36
|
+
b = box.split(" ")
|
37
|
+
rm_cmd = Mixlib::ShellOut.new("vagrant box remove --force #{b[0]} --provider #{b[1].to_s.gsub(/(,|\()/, '')}")
|
38
|
+
banner("Removing #{b[0]} for provider #{b[1].to_s.gsub(/(,|\()/, '')}")
|
39
|
+
rm_cmd.run_command
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_box(md_json)
|
44
|
+
md = box_metadata(md_json)
|
45
|
+
@boxname = md["name"]
|
46
|
+
@providers = md["providers"]
|
47
|
+
@share_disabled = no_shared || /(bsd|opensuse)/.match(boxname) ? true : false
|
48
|
+
|
49
|
+
dir = "#{File.expand_path("../../", File.dirname(__FILE__))}/templates"
|
50
|
+
%w{.kitchen.yml bootstrap.sh}.each do |file|
|
51
|
+
t = file =~ /kitchen/ ? "kitchen.yml.erb" : "#{file}.erb"
|
52
|
+
erb = ERB.new(File.read(dir + "/#{t}"), nil, "-").result(binding)
|
53
|
+
File.open(file, "w") { |f| f.puts erb }
|
54
|
+
end
|
55
|
+
|
56
|
+
test = Mixlib::ShellOut.new("kitchen test", :timeout => 900, live_stream: STDOUT)
|
57
|
+
test.run_command
|
58
|
+
test.error!
|
59
|
+
end
|
60
|
+
end
|
data/lib/bento/upload.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require "bento/common"
|
2
|
+
|
3
|
+
class UploadRunner
|
4
|
+
include Common
|
5
|
+
|
6
|
+
attr_reader :md_json
|
7
|
+
|
8
|
+
def initialize(opts)
|
9
|
+
@md_json = opts.md_json
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
banner("Starting uploads...")
|
14
|
+
time = Benchmark.measure do
|
15
|
+
files = md_json ? [md_json] : metadata_files
|
16
|
+
files.each do |md_file|
|
17
|
+
upload(md_file)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
banner("Uploads finished in #{duration(time.real)}.")
|
21
|
+
end
|
22
|
+
|
23
|
+
def upload(md_file)
|
24
|
+
md = box_metadata(md_file)
|
25
|
+
box_desc = "a bento box for #{md['name']}"
|
26
|
+
box = vc_account.ensure_box(md["name"], box_desc, private_box?(md["name"]))
|
27
|
+
box_ver = box.ensure_version(md["version"], File.read(md_file))
|
28
|
+
|
29
|
+
if builds_yml["slugs"].values.include?(box.name)
|
30
|
+
slug_desc = "a bento box for #{builds_yml['slugs'].key(box.name)}"
|
31
|
+
slug = vc_account.ensure_box(builds_yml["slugs"].key(box.name), slug_desc, false)
|
32
|
+
slug_ver = slug.ensure_version(md["version"], File.read(md_file))
|
33
|
+
end
|
34
|
+
|
35
|
+
md["providers"].each do |k, v|
|
36
|
+
provider = box_ver.ensure_provider(k, nil)
|
37
|
+
banner("Uploading #{box.name}/#{box_ver.version}/#{provider.name}...")
|
38
|
+
provider.upload_file("builds/#{v['file']}")
|
39
|
+
banner("#{provider.download_url}")
|
40
|
+
next unless builds_yml["slugs"].values.include?(box.name)
|
41
|
+
slug_provider = slug_ver.ensure_provider(k, nil)
|
42
|
+
banner("Uploading #{slug.name}/#{slug_ver.version}/#{slug_provider.name}...")
|
43
|
+
slug_provider.upload_file("builds/#{v['file']}")
|
44
|
+
banner("#{slug_provider.download_url}")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|