bento-lpn 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
@@ -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
@@ -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