bosh_cli 0.16
Sign up to get free protection for your applications and to get access to all the features.
- data/README +4 -0
- data/Rakefile +55 -0
- data/bin/bosh +17 -0
- data/lib/cli.rb +76 -0
- data/lib/cli/cache.rb +44 -0
- data/lib/cli/changeset_helper.rb +142 -0
- data/lib/cli/command_definition.rb +52 -0
- data/lib/cli/commands/base.rb +245 -0
- data/lib/cli/commands/biff.rb +300 -0
- data/lib/cli/commands/blob.rb +125 -0
- data/lib/cli/commands/cloudcheck.rb +169 -0
- data/lib/cli/commands/deployment.rb +147 -0
- data/lib/cli/commands/job.rb +42 -0
- data/lib/cli/commands/job_management.rb +117 -0
- data/lib/cli/commands/log_management.rb +81 -0
- data/lib/cli/commands/maintenance.rb +131 -0
- data/lib/cli/commands/misc.rb +240 -0
- data/lib/cli/commands/package.rb +112 -0
- data/lib/cli/commands/property_management.rb +125 -0
- data/lib/cli/commands/release.rb +469 -0
- data/lib/cli/commands/ssh.rb +271 -0
- data/lib/cli/commands/stemcell.rb +184 -0
- data/lib/cli/commands/task.rb +213 -0
- data/lib/cli/commands/user.rb +28 -0
- data/lib/cli/commands/vms.rb +53 -0
- data/lib/cli/config.rb +154 -0
- data/lib/cli/core_ext.rb +145 -0
- data/lib/cli/dependency_helper.rb +62 -0
- data/lib/cli/deployment_helper.rb +263 -0
- data/lib/cli/deployment_manifest_compiler.rb +28 -0
- data/lib/cli/director.rb +633 -0
- data/lib/cli/director_task.rb +64 -0
- data/lib/cli/errors.rb +48 -0
- data/lib/cli/event_log_renderer.rb +351 -0
- data/lib/cli/job_builder.rb +226 -0
- data/lib/cli/package_builder.rb +254 -0
- data/lib/cli/packaging_helper.rb +248 -0
- data/lib/cli/release.rb +176 -0
- data/lib/cli/release_builder.rb +215 -0
- data/lib/cli/release_compiler.rb +178 -0
- data/lib/cli/release_tarball.rb +272 -0
- data/lib/cli/runner.rb +771 -0
- data/lib/cli/stemcell.rb +83 -0
- data/lib/cli/task_log_renderer.rb +40 -0
- data/lib/cli/templates/help_message.erb +75 -0
- data/lib/cli/validation.rb +42 -0
- data/lib/cli/version.rb +7 -0
- data/lib/cli/version_calc.rb +48 -0
- data/lib/cli/versions_index.rb +126 -0
- data/lib/cli/yaml_helper.rb +62 -0
- data/spec/assets/biff/bad_gateway_config.yml +28 -0
- data/spec/assets/biff/good_simple_config.yml +63 -0
- data/spec/assets/biff/good_simple_golden_config.yml +63 -0
- data/spec/assets/biff/good_simple_template.erb +69 -0
- data/spec/assets/biff/multiple_subnets_config.yml +40 -0
- data/spec/assets/biff/network_only_template.erb +34 -0
- data/spec/assets/biff/no_cc_config.yml +27 -0
- data/spec/assets/biff/no_range_config.yml +27 -0
- data/spec/assets/biff/no_subnet_config.yml +16 -0
- data/spec/assets/biff/ok_network_config.yml +30 -0
- data/spec/assets/biff/properties_template.erb +6 -0
- data/spec/assets/deployment.MF +0 -0
- data/spec/assets/plugins/bosh/cli/commands/echo.rb +43 -0
- data/spec/assets/plugins/bosh/cli/commands/ruby.rb +24 -0
- data/spec/assets/release/jobs/cacher.tgz +0 -0
- data/spec/assets/release/jobs/cacher/config/file1.conf +0 -0
- data/spec/assets/release/jobs/cacher/config/file2.conf +0 -0
- data/spec/assets/release/jobs/cacher/job.MF +6 -0
- data/spec/assets/release/jobs/cacher/monit +1 -0
- data/spec/assets/release/jobs/cleaner.tgz +0 -0
- data/spec/assets/release/jobs/cleaner/job.MF +4 -0
- data/spec/assets/release/jobs/cleaner/monit +1 -0
- data/spec/assets/release/jobs/sweeper.tgz +0 -0
- data/spec/assets/release/jobs/sweeper/config/test.conf +1 -0
- data/spec/assets/release/jobs/sweeper/job.MF +5 -0
- data/spec/assets/release/jobs/sweeper/monit +1 -0
- data/spec/assets/release/packages/mutator.tar.gz +0 -0
- data/spec/assets/release/packages/stuff.tgz +0 -0
- data/spec/assets/release/release.MF +17 -0
- data/spec/assets/release_invalid_checksum.tgz +0 -0
- data/spec/assets/release_invalid_jobs.tgz +0 -0
- data/spec/assets/release_no_name.tgz +0 -0
- data/spec/assets/release_no_version.tgz +0 -0
- data/spec/assets/stemcell/image +1 -0
- data/spec/assets/stemcell/stemcell.MF +6 -0
- data/spec/assets/stemcell_invalid_mf.tgz +0 -0
- data/spec/assets/stemcell_no_image.tgz +0 -0
- data/spec/assets/valid_release.tgz +0 -0
- data/spec/assets/valid_stemcell.tgz +0 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/unit/base_command_spec.rb +66 -0
- data/spec/unit/biff_spec.rb +135 -0
- data/spec/unit/cache_spec.rb +36 -0
- data/spec/unit/cli_commands_spec.rb +481 -0
- data/spec/unit/config_spec.rb +139 -0
- data/spec/unit/core_ext_spec.rb +77 -0
- data/spec/unit/dependency_helper_spec.rb +52 -0
- data/spec/unit/deployment_manifest_compiler_spec.rb +63 -0
- data/spec/unit/director_spec.rb +511 -0
- data/spec/unit/director_task_spec.rb +48 -0
- data/spec/unit/event_log_renderer_spec.rb +171 -0
- data/spec/unit/hash_changeset_spec.rb +73 -0
- data/spec/unit/job_builder_spec.rb +454 -0
- data/spec/unit/package_builder_spec.rb +567 -0
- data/spec/unit/release_builder_spec.rb +65 -0
- data/spec/unit/release_spec.rb +66 -0
- data/spec/unit/release_tarball_spec.rb +33 -0
- data/spec/unit/runner_spec.rb +140 -0
- data/spec/unit/ssh_spec.rb +78 -0
- data/spec/unit/stemcell_spec.rb +17 -0
- data/spec/unit/version_calc_spec.rb +27 -0
- data/spec/unit/versions_index_spec.rb +132 -0
- metadata +338 -0
data/lib/cli/stemcell.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Cli
|
4
|
+
class Stemcell
|
5
|
+
include Validation
|
6
|
+
|
7
|
+
attr_reader :stemcell_file, :manifest
|
8
|
+
|
9
|
+
def initialize(tarball_path, cache)
|
10
|
+
@stemcell_file = File.expand_path(tarball_path, Dir.pwd)
|
11
|
+
@cache = cache
|
12
|
+
end
|
13
|
+
|
14
|
+
def perform_validation(options = {})
|
15
|
+
tmp_dir = Dir.mktmpdir
|
16
|
+
|
17
|
+
step("File exists and readable",
|
18
|
+
"Cannot find stemcell file #{@stemcell_file}", :fatal) do
|
19
|
+
File.exists?(@stemcell_file) && File.readable?(@stemcell_file)
|
20
|
+
end
|
21
|
+
|
22
|
+
cache_key = "%s_%s" % [@stemcell_file, File.mtime(@stemcell_file)]
|
23
|
+
|
24
|
+
manifest_yaml = @cache.read(cache_key)
|
25
|
+
|
26
|
+
if manifest_yaml
|
27
|
+
say("Using cached manifest...")
|
28
|
+
else
|
29
|
+
say("Manifest not found in cache, verifying tarball...")
|
30
|
+
|
31
|
+
step("Extract tarball",
|
32
|
+
"Cannot extract tarball #{@stemcell_file}", :fatal) do
|
33
|
+
`tar -C #{tmp_dir} -xzf #{@stemcell_file} 2>&1`
|
34
|
+
$?.exitstatus == 0
|
35
|
+
end
|
36
|
+
|
37
|
+
manifest_file = File.expand_path("stemcell.MF", tmp_dir)
|
38
|
+
|
39
|
+
step("Manifest exists", "Cannot find stemcell manifest", :fatal) do
|
40
|
+
File.exists?(manifest_file)
|
41
|
+
end
|
42
|
+
|
43
|
+
step("Stemcell image file",
|
44
|
+
"Stemcell image file is missing", :fatal) do
|
45
|
+
File.exists?(File.expand_path("image", tmp_dir))
|
46
|
+
end
|
47
|
+
|
48
|
+
say("Writing manifest to cache...")
|
49
|
+
manifest_yaml = File.read(manifest_file)
|
50
|
+
@cache.write(cache_key, manifest_yaml)
|
51
|
+
end
|
52
|
+
|
53
|
+
manifest = YAML.load(manifest_yaml)
|
54
|
+
|
55
|
+
step("Stemcell properties",
|
56
|
+
"Manifest should contain valid name, " +
|
57
|
+
"version and cloud properties") do
|
58
|
+
manifest.is_a?(Hash) && manifest.has_key?("name") &&
|
59
|
+
manifest.has_key?("version") &&
|
60
|
+
manifest.has_key?("cloud_properties") &&
|
61
|
+
manifest["name"].is_a?(String) &&
|
62
|
+
(manifest["version"].is_a?(String) ||
|
63
|
+
manifest["version"].kind_of?(Numeric)) &&
|
64
|
+
(manifest["cloud_properties"].nil? ||
|
65
|
+
manifest["cloud_properties"].is_a?(Hash))
|
66
|
+
end
|
67
|
+
|
68
|
+
print_info(manifest)
|
69
|
+
@manifest = manifest
|
70
|
+
ensure
|
71
|
+
FileUtils.rm_rf(tmp_dir)
|
72
|
+
end
|
73
|
+
|
74
|
+
def print_info(manifest)
|
75
|
+
say("\nStemcell info")
|
76
|
+
say("-------------")
|
77
|
+
|
78
|
+
say("Name: %s" % [manifest["name"] || "missing".red])
|
79
|
+
say("Version: %s" % [manifest["version"] || "missing".red])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Cli
|
4
|
+
class TaskLogRenderer
|
5
|
+
|
6
|
+
def self.create_for_log_type(log_type)
|
7
|
+
if log_type == "event"
|
8
|
+
EventLogRenderer.new
|
9
|
+
else
|
10
|
+
TaskLogRenderer.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :time_adjustment
|
15
|
+
attr_accessor :duration
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@out = Bosh::Cli::Config.output || $stdout
|
19
|
+
@out.sync = true
|
20
|
+
@lock = Mutex.new
|
21
|
+
@output = ""
|
22
|
+
@time_adjustment = 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_output(output)
|
26
|
+
@output = output
|
27
|
+
end
|
28
|
+
|
29
|
+
def refresh
|
30
|
+
@out.print(@output)
|
31
|
+
@output = ""
|
32
|
+
end
|
33
|
+
|
34
|
+
def finish(state)
|
35
|
+
refresh
|
36
|
+
@done = true
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
<%= basic_usage %>
|
2
|
+
Currently available bosh commands are:
|
3
|
+
|
4
|
+
Deployment
|
5
|
+
<%= command_usage(:deployment) %>
|
6
|
+
<%= command_usage(:delete_deployment) %>
|
7
|
+
<%= command_usage(:list_deployments) %>
|
8
|
+
<%= command_usage(:deploy) %>
|
9
|
+
<%= command_usage(:diff) %>
|
10
|
+
|
11
|
+
Release management
|
12
|
+
<%= command_usage(:create_release) %>
|
13
|
+
<%= command_usage(:delete_release) %>
|
14
|
+
<%= command_usage(:verify_release) %>
|
15
|
+
<%= command_usage(:upload_release) %>
|
16
|
+
<%= command_usage(:list_releases) %>
|
17
|
+
<%= command_usage(:reset_release) %>
|
18
|
+
|
19
|
+
<%= command_usage(:init_release) %>
|
20
|
+
<%= command_usage(:generate_package) %>
|
21
|
+
<%= command_usage(:generate_job) %>
|
22
|
+
|
23
|
+
Stemcells
|
24
|
+
<%= command_usage(:upload_stemcell) %>
|
25
|
+
<%= command_usage(:verify_stemcell) %>
|
26
|
+
<%= command_usage(:list_stemcells) %>
|
27
|
+
<%= command_usage(:delete_stemcell) %>
|
28
|
+
<%= command_usage(:list_public_stemcells) %>
|
29
|
+
<%= command_usage(:download_public_stemcell) %>
|
30
|
+
|
31
|
+
User management
|
32
|
+
<%= command_usage(:create_user) %>
|
33
|
+
|
34
|
+
Job management
|
35
|
+
<%= command_usage(:start_job) %>
|
36
|
+
<%= command_usage(:stop_job) %>
|
37
|
+
<%= command_usage(:restart_job) %>
|
38
|
+
<%= command_usage(:recreate_job) %>
|
39
|
+
|
40
|
+
Log management
|
41
|
+
<%= command_usage(:fetch_logs) %>
|
42
|
+
|
43
|
+
Task management
|
44
|
+
<%= command_usage(:list_running_tasks) %>
|
45
|
+
<%= command_usage(:list_recent_tasks) %>
|
46
|
+
<%= command_usage(:track_task) %>
|
47
|
+
<%= command_usage(:cancel_task) %>
|
48
|
+
|
49
|
+
Property management
|
50
|
+
<%= command_usage(:set_property) %>
|
51
|
+
<%= command_usage(:get_property) %>
|
52
|
+
<%= command_usage(:unset_property) %>
|
53
|
+
<%= command_usage(:list_properties) %>
|
54
|
+
|
55
|
+
Maintenance
|
56
|
+
<%= command_usage(:cleanup) %>
|
57
|
+
<%= command_usage(:cloudcheck) %>
|
58
|
+
|
59
|
+
Misc
|
60
|
+
<%= command_usage(:status) %>
|
61
|
+
<%= command_usage(:list_vms) %>
|
62
|
+
<%= command_usage(:target) %>
|
63
|
+
<%= command_usage(:login) %>
|
64
|
+
<%= command_usage(:logout) %>
|
65
|
+
<%= command_usage(:purge) %>
|
66
|
+
|
67
|
+
Remote access
|
68
|
+
<%= command_usage(:ssh) %>
|
69
|
+
<%= command_usage(:scp) %>
|
70
|
+
<%= command_usage(:ssh_cleanup) %>
|
71
|
+
|
72
|
+
Blob
|
73
|
+
<%= command_usage(:upload_blob) %>
|
74
|
+
<%= command_usage(:sync_blobs) %>
|
75
|
+
<%= command_usage(:blobs) %>
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Cli
|
4
|
+
class ValidationHalted < StandardError; end
|
5
|
+
|
6
|
+
module Validation
|
7
|
+
|
8
|
+
def errors
|
9
|
+
@errors ||= []
|
10
|
+
end
|
11
|
+
|
12
|
+
def valid?(options = {})
|
13
|
+
validate(options) unless @validated
|
14
|
+
errors.empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate(options = {})
|
18
|
+
perform_validation(options)
|
19
|
+
rescue ValidationHalted
|
20
|
+
ensure
|
21
|
+
@validated = true
|
22
|
+
end
|
23
|
+
|
24
|
+
def reset_validation
|
25
|
+
@validated = nil
|
26
|
+
@errors = []
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def step(name, error_message, kind = :non_fatal, &block)
|
32
|
+
passed = yield
|
33
|
+
|
34
|
+
say("%-60s %s" % [name, passed ? "OK".green : "FAILED".red])
|
35
|
+
|
36
|
+
unless passed
|
37
|
+
errors << error_message
|
38
|
+
raise ValidationHalted if kind == :fatal
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/cli/version.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Cli
|
4
|
+
module VersionCalc
|
5
|
+
|
6
|
+
# Returns 0 if two versions are the same,
|
7
|
+
# 1 if v1 > v2
|
8
|
+
# -1 if v1 < v2
|
9
|
+
def version_cmp(v1 = "0", v2 = "0")
|
10
|
+
vp1 = components(v1)
|
11
|
+
vp2 = components(v2)
|
12
|
+
|
13
|
+
[vp1.size, vp2.size].max.times do |i|
|
14
|
+
result = vp1[i].to_i <=> vp2[i].to_i
|
15
|
+
return result unless result == 0
|
16
|
+
end
|
17
|
+
|
18
|
+
0
|
19
|
+
end
|
20
|
+
|
21
|
+
def version_greater(v1, v2)
|
22
|
+
version_cmp(v1, v2) > 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def version_less(v1, v2)
|
26
|
+
version_cmp(v1, v2) < 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def version_same(v1, v2)
|
30
|
+
version_cmp(v1, v2) == 0
|
31
|
+
end
|
32
|
+
|
33
|
+
def major_version(v)
|
34
|
+
components(v)[0].to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
def minor_version(v)
|
38
|
+
components(v)[1].to_i
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def components(v)
|
44
|
+
v.to_s.split(".")
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Cli
|
4
|
+
class VersionsIndex
|
5
|
+
include VersionCalc
|
6
|
+
|
7
|
+
def initialize(storage_dir, name_prefix = nil)
|
8
|
+
@storage_dir = File.expand_path(storage_dir)
|
9
|
+
@index_file = File.join(@storage_dir, "index.yml")
|
10
|
+
@name_prefix = name_prefix
|
11
|
+
|
12
|
+
if File.file?(@index_file)
|
13
|
+
init_index(load_yaml_file(@index_file, nil))
|
14
|
+
else
|
15
|
+
init_index({})
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_by_checksum(checksum)
|
20
|
+
@data["builds"].each_pair do |fingerprint, build_data|
|
21
|
+
return build_data if build_data["sha1"] == checksum
|
22
|
+
end
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](fingerprint)
|
27
|
+
@data["builds"][fingerprint]
|
28
|
+
end
|
29
|
+
|
30
|
+
def latest_version(major = nil)
|
31
|
+
builds = @data["builds"].values
|
32
|
+
|
33
|
+
if major
|
34
|
+
builds = builds.select do |build|
|
35
|
+
major_version(build["version"]) == major
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
return nil if builds.empty?
|
40
|
+
|
41
|
+
sorted = builds.sort { |build1, build2|
|
42
|
+
cmp = version_cmp(build2["version"], build1["version"])
|
43
|
+
if cmp == 0
|
44
|
+
raise "There is a duplicate version `#{build1["version"]}' " +
|
45
|
+
"in index `#{@index_file}'"
|
46
|
+
end
|
47
|
+
cmp
|
48
|
+
}
|
49
|
+
|
50
|
+
sorted[0]["version"]
|
51
|
+
end
|
52
|
+
|
53
|
+
def version_exists?(version)
|
54
|
+
File.exists?(filename(version))
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_version(fingerprint, item, payload = nil)
|
58
|
+
version = item["version"]
|
59
|
+
|
60
|
+
if version.blank?
|
61
|
+
raise InvalidIndex,
|
62
|
+
"Cannot save index entry without knowing its version"
|
63
|
+
end
|
64
|
+
|
65
|
+
create_directories
|
66
|
+
|
67
|
+
if payload
|
68
|
+
File.open(filename(version), "w") do |f|
|
69
|
+
f.write(payload)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
@data["builds"].each_pair do |fp, build|
|
74
|
+
if version_cmp(build["version"], version) == 0 && fp != fingerprint
|
75
|
+
raise "Trying to add duplicate version `#{version}' " +
|
76
|
+
"into index `#{@index_file}'"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
@data["builds"][fingerprint] = item
|
81
|
+
if payload
|
82
|
+
@data["builds"][fingerprint]["sha1"] = Digest::SHA1.hexdigest(payload)
|
83
|
+
end
|
84
|
+
|
85
|
+
File.open(@index_file, "w") do |f|
|
86
|
+
f.write(YAML.dump(@data))
|
87
|
+
end
|
88
|
+
|
89
|
+
File.expand_path(filename(version))
|
90
|
+
end
|
91
|
+
|
92
|
+
def filename(version)
|
93
|
+
name = @name_prefix.blank? ?
|
94
|
+
"#{version}.tgz" : "#{@name_prefix}-#{version}.tgz"
|
95
|
+
File.join(@storage_dir, name)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def create_directories
|
101
|
+
begin
|
102
|
+
FileUtils.mkdir_p(@storage_dir)
|
103
|
+
rescue SystemCallError => e
|
104
|
+
raise InvalidIndex, "Cannot create index storage directory: #{e}"
|
105
|
+
end
|
106
|
+
|
107
|
+
begin
|
108
|
+
FileUtils.touch(@index_file)
|
109
|
+
rescue SystemCallError => e
|
110
|
+
raise InvalidIndex, "Cannot create index file: #{e}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def init_index(data)
|
115
|
+
data ||= {}
|
116
|
+
|
117
|
+
unless data.kind_of?(Hash)
|
118
|
+
raise InvalidIndex, "Invalid versions index data type, " +
|
119
|
+
"#{data.class} given, Hash expected"
|
120
|
+
end
|
121
|
+
@data = data
|
122
|
+
@data.delete("latest_version") # Indices used to track latest versions
|
123
|
+
@data["builds"] ||= {}
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Cli
|
4
|
+
class YamlHelper
|
5
|
+
class << self
|
6
|
+
private
|
7
|
+
def process_object(o)
|
8
|
+
case o
|
9
|
+
when @syck_class::Map
|
10
|
+
process_map(o)
|
11
|
+
when @syck_class::Seq
|
12
|
+
process_seq(o)
|
13
|
+
when @syck_class::Scalar
|
14
|
+
else
|
15
|
+
err("Unhandled class #{o.class}, fix yaml duplicate check")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def process_seq(s)
|
20
|
+
s.value.each do |v|
|
21
|
+
process_object(v)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def process_map(m)
|
26
|
+
return if m.class != @syck_class::Map
|
27
|
+
s = Set.new
|
28
|
+
m.value.each_key do |k|
|
29
|
+
raise "Found dup key #{k.value}" if s.include?(k.value)
|
30
|
+
s.add(k.value)
|
31
|
+
end
|
32
|
+
|
33
|
+
m.value.each_value do |v|
|
34
|
+
process_object(v)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
public
|
39
|
+
def check_duplicate_keys(path)
|
40
|
+
# Some Ruby builds on Ubuntu seem to expose a bug
|
41
|
+
# with the opposite order of Syck check, so we first
|
42
|
+
# check for Syck and then for YAML::Syck
|
43
|
+
if defined?(Syck)
|
44
|
+
@syck_class = Syck
|
45
|
+
elsif defined?(YAML::Syck)
|
46
|
+
@syck_class = YAML::Syck
|
47
|
+
else
|
48
|
+
raise "Cannot find Syck parser for YAML, " +
|
49
|
+
"please check your Ruby installation"
|
50
|
+
end
|
51
|
+
|
52
|
+
File.open(path) do |f|
|
53
|
+
begin
|
54
|
+
process_map(YAML.parse(f))
|
55
|
+
rescue => e
|
56
|
+
raise "Bad yaml file #{path}, " + e.message
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|