bosh_cli 0.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/README +4 -0
  2. data/Rakefile +55 -0
  3. data/bin/bosh +17 -0
  4. data/lib/cli.rb +76 -0
  5. data/lib/cli/cache.rb +44 -0
  6. data/lib/cli/changeset_helper.rb +142 -0
  7. data/lib/cli/command_definition.rb +52 -0
  8. data/lib/cli/commands/base.rb +245 -0
  9. data/lib/cli/commands/biff.rb +300 -0
  10. data/lib/cli/commands/blob.rb +125 -0
  11. data/lib/cli/commands/cloudcheck.rb +169 -0
  12. data/lib/cli/commands/deployment.rb +147 -0
  13. data/lib/cli/commands/job.rb +42 -0
  14. data/lib/cli/commands/job_management.rb +117 -0
  15. data/lib/cli/commands/log_management.rb +81 -0
  16. data/lib/cli/commands/maintenance.rb +131 -0
  17. data/lib/cli/commands/misc.rb +240 -0
  18. data/lib/cli/commands/package.rb +112 -0
  19. data/lib/cli/commands/property_management.rb +125 -0
  20. data/lib/cli/commands/release.rb +469 -0
  21. data/lib/cli/commands/ssh.rb +271 -0
  22. data/lib/cli/commands/stemcell.rb +184 -0
  23. data/lib/cli/commands/task.rb +213 -0
  24. data/lib/cli/commands/user.rb +28 -0
  25. data/lib/cli/commands/vms.rb +53 -0
  26. data/lib/cli/config.rb +154 -0
  27. data/lib/cli/core_ext.rb +145 -0
  28. data/lib/cli/dependency_helper.rb +62 -0
  29. data/lib/cli/deployment_helper.rb +263 -0
  30. data/lib/cli/deployment_manifest_compiler.rb +28 -0
  31. data/lib/cli/director.rb +633 -0
  32. data/lib/cli/director_task.rb +64 -0
  33. data/lib/cli/errors.rb +48 -0
  34. data/lib/cli/event_log_renderer.rb +351 -0
  35. data/lib/cli/job_builder.rb +226 -0
  36. data/lib/cli/package_builder.rb +254 -0
  37. data/lib/cli/packaging_helper.rb +248 -0
  38. data/lib/cli/release.rb +176 -0
  39. data/lib/cli/release_builder.rb +215 -0
  40. data/lib/cli/release_compiler.rb +178 -0
  41. data/lib/cli/release_tarball.rb +272 -0
  42. data/lib/cli/runner.rb +771 -0
  43. data/lib/cli/stemcell.rb +83 -0
  44. data/lib/cli/task_log_renderer.rb +40 -0
  45. data/lib/cli/templates/help_message.erb +75 -0
  46. data/lib/cli/validation.rb +42 -0
  47. data/lib/cli/version.rb +7 -0
  48. data/lib/cli/version_calc.rb +48 -0
  49. data/lib/cli/versions_index.rb +126 -0
  50. data/lib/cli/yaml_helper.rb +62 -0
  51. data/spec/assets/biff/bad_gateway_config.yml +28 -0
  52. data/spec/assets/biff/good_simple_config.yml +63 -0
  53. data/spec/assets/biff/good_simple_golden_config.yml +63 -0
  54. data/spec/assets/biff/good_simple_template.erb +69 -0
  55. data/spec/assets/biff/multiple_subnets_config.yml +40 -0
  56. data/spec/assets/biff/network_only_template.erb +34 -0
  57. data/spec/assets/biff/no_cc_config.yml +27 -0
  58. data/spec/assets/biff/no_range_config.yml +27 -0
  59. data/spec/assets/biff/no_subnet_config.yml +16 -0
  60. data/spec/assets/biff/ok_network_config.yml +30 -0
  61. data/spec/assets/biff/properties_template.erb +6 -0
  62. data/spec/assets/deployment.MF +0 -0
  63. data/spec/assets/plugins/bosh/cli/commands/echo.rb +43 -0
  64. data/spec/assets/plugins/bosh/cli/commands/ruby.rb +24 -0
  65. data/spec/assets/release/jobs/cacher.tgz +0 -0
  66. data/spec/assets/release/jobs/cacher/config/file1.conf +0 -0
  67. data/spec/assets/release/jobs/cacher/config/file2.conf +0 -0
  68. data/spec/assets/release/jobs/cacher/job.MF +6 -0
  69. data/spec/assets/release/jobs/cacher/monit +1 -0
  70. data/spec/assets/release/jobs/cleaner.tgz +0 -0
  71. data/spec/assets/release/jobs/cleaner/job.MF +4 -0
  72. data/spec/assets/release/jobs/cleaner/monit +1 -0
  73. data/spec/assets/release/jobs/sweeper.tgz +0 -0
  74. data/spec/assets/release/jobs/sweeper/config/test.conf +1 -0
  75. data/spec/assets/release/jobs/sweeper/job.MF +5 -0
  76. data/spec/assets/release/jobs/sweeper/monit +1 -0
  77. data/spec/assets/release/packages/mutator.tar.gz +0 -0
  78. data/spec/assets/release/packages/stuff.tgz +0 -0
  79. data/spec/assets/release/release.MF +17 -0
  80. data/spec/assets/release_invalid_checksum.tgz +0 -0
  81. data/spec/assets/release_invalid_jobs.tgz +0 -0
  82. data/spec/assets/release_no_name.tgz +0 -0
  83. data/spec/assets/release_no_version.tgz +0 -0
  84. data/spec/assets/stemcell/image +1 -0
  85. data/spec/assets/stemcell/stemcell.MF +6 -0
  86. data/spec/assets/stemcell_invalid_mf.tgz +0 -0
  87. data/spec/assets/stemcell_no_image.tgz +0 -0
  88. data/spec/assets/valid_release.tgz +0 -0
  89. data/spec/assets/valid_stemcell.tgz +0 -0
  90. data/spec/spec_helper.rb +25 -0
  91. data/spec/unit/base_command_spec.rb +66 -0
  92. data/spec/unit/biff_spec.rb +135 -0
  93. data/spec/unit/cache_spec.rb +36 -0
  94. data/spec/unit/cli_commands_spec.rb +481 -0
  95. data/spec/unit/config_spec.rb +139 -0
  96. data/spec/unit/core_ext_spec.rb +77 -0
  97. data/spec/unit/dependency_helper_spec.rb +52 -0
  98. data/spec/unit/deployment_manifest_compiler_spec.rb +63 -0
  99. data/spec/unit/director_spec.rb +511 -0
  100. data/spec/unit/director_task_spec.rb +48 -0
  101. data/spec/unit/event_log_renderer_spec.rb +171 -0
  102. data/spec/unit/hash_changeset_spec.rb +73 -0
  103. data/spec/unit/job_builder_spec.rb +454 -0
  104. data/spec/unit/package_builder_spec.rb +567 -0
  105. data/spec/unit/release_builder_spec.rb +65 -0
  106. data/spec/unit/release_spec.rb +66 -0
  107. data/spec/unit/release_tarball_spec.rb +33 -0
  108. data/spec/unit/runner_spec.rb +140 -0
  109. data/spec/unit/ssh_spec.rb +78 -0
  110. data/spec/unit/stemcell_spec.rb +17 -0
  111. data/spec/unit/version_calc_spec.rb +27 -0
  112. data/spec/unit/versions_index_spec.rb +132 -0
  113. metadata +338 -0
@@ -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
@@ -0,0 +1,7 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh
4
+ module Cli
5
+ VERSION = "0.16"
6
+ end
7
+ end
@@ -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