bosh-workspace 0.9.2 → 0.9.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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/bosh-workspace.gemspec +5 -4
  3. data/lib/bosh/cli/commands/deployment_patch.rb +0 -1
  4. data/lib/bosh/cli/commands/prepare.rb +12 -11
  5. data/lib/bosh/workspace.rb +4 -3
  6. data/lib/bosh/workspace/credentials.rb +12 -5
  7. data/lib/bosh/workspace/git_credentials_provider.rb +80 -0
  8. data/lib/bosh/workspace/{git_remote_url.rb → helpers/git_protocol_helper.rb} +4 -8
  9. data/lib/bosh/workspace/helpers/project_deployment_helper.rb +10 -2
  10. data/lib/bosh/workspace/helpers/release_helper.rb +33 -7
  11. data/lib/bosh/workspace/manifest_builder.rb +6 -8
  12. data/lib/bosh/workspace/merge_tool.rb +73 -0
  13. data/lib/bosh/workspace/project_deployment.rb +20 -5
  14. data/lib/bosh/workspace/release.rb +103 -67
  15. data/lib/bosh/workspace/schemas/credentials.rb +22 -0
  16. data/lib/bosh/workspace/schemas/project_deployment.rb +14 -1
  17. data/lib/bosh/workspace/version.rb +1 -1
  18. data/spec/assets/bin/spruce +0 -0
  19. data/spec/assets/manifests-repo/deployments/foo.yml +1 -0
  20. data/spec/assets/manifests-repo/stubs/foo.yml +4 -0
  21. data/spec/commands/prepare_spec.rb +39 -12
  22. data/spec/credentials_spec.rb +8 -0
  23. data/spec/git_credentials_provider_spec.rb +82 -0
  24. data/spec/{git_remote_url_spec.rb → helpers/git_protocol_helper_spec.rb} +10 -11
  25. data/spec/helpers/project_deployment_helper_spec.rb +12 -1
  26. data/spec/helpers/release_helper_spec.rb +157 -73
  27. data/spec/manifest_builder_spec.rb +6 -5
  28. data/spec/merge_tool_spec.rb +98 -0
  29. data/spec/project_deployment_spec.rb +43 -1
  30. data/spec/release_spec.rb +369 -354
  31. data/spec/rugged_spec.rb +64 -0
  32. data/spec/schemas/credentials_spec.rb +22 -5
  33. data/spec/spec_helper.rb +1 -0
  34. metadata +35 -15
  35. data/lib/bosh/workspace/helpers/git_credentials_helper.rb +0 -111
  36. data/lib/bosh/workspace/helpers/spiff_helper.rb +0 -34
  37. data/spec/helpers/git_credentials_helper_spec.rb +0 -190
  38. data/spec/helpers/spiff_helper_spec.rb +0 -68
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c532d7cdf4e457cb635fa36d895f286ea586222d
4
- data.tar.gz: 4378d7c1ee1a9bb859db2a55ceb3d48b1a6f90a8
3
+ metadata.gz: 095b4cec01561a415d908bc83c6a86e5a6861bac
4
+ data.tar.gz: 3038bc5ecd5756cf3672bbdc15405eee574a39c1
5
5
  SHA512:
6
- metadata.gz: ccef1b2f1067ba4aeb70f036ac8550d9cb0a39a521402b18f55980101800257849f321d121277b4016b7be2eaa91d5c6e2d37e6cffc1bb966c87d64257f25ea3
7
- data.tar.gz: 424aad5e9d34336c21119d4383b19b324352a96954606f0033b8738d803080575b6136c0f3e4614c92f47442cf75e07f80890cc28c15110dddc7ca296dc9c5a2
6
+ metadata.gz: 835a7af3d529d8264b273157ce4c2f80e5cdbe73ed3c8b817f469edebd874e730ff64110942b2f2aeb962930ac4fb0a3c39ef62445d9e0bb4c5ebbc04f32876c
7
+ data.tar.gz: 75e12938c90cea7260a8c2d16a6098d6c189bf9569431bf889e5cce7b23c70b57fafe297aa396b205e70cc54dc40adba7f47ec755007a9408cb60e3cd78bbd7d
@@ -20,16 +20,17 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.required_ruby_version = '>= 2.0.0'
22
22
 
23
- spec.add_runtime_dependency "bosh_cli", ">= 1.2905.0"
24
- spec.add_runtime_dependency "bosh_common", ">= 1.2905.0"
23
+ spec.add_runtime_dependency "bosh_cli", ">= 1.2905.0"
24
+ spec.add_runtime_dependency "bosh_common", ">= 1.2905.0"
25
+ spec.add_runtime_dependency "bosh-template", ">= 1.2905.0"
25
26
  spec.add_runtime_dependency "semi_semantic", "~> 1.1.0"
26
27
  spec.add_runtime_dependency "membrane", "~> 1.1.0"
27
28
  spec.add_runtime_dependency "hashdiff", "~> 0.2.1"
28
29
  spec.add_runtime_dependency "rugged", "~> 0.23.0b3"
29
30
 
30
31
  spec.add_development_dependency "bundler", "~> 1.6"
31
- spec.add_development_dependency "rspec", "~> 3.1.0"
32
- spec.add_development_dependency "rspec-its", '~> 1.1.0'
32
+ spec.add_development_dependency "rspec", "~> 3.3.0"
33
+ spec.add_development_dependency "rspec-its", '~> 1.2.0'
33
34
  spec.add_development_dependency "rake"
34
35
  spec.add_development_dependency "archive-zip", "~> 0.7.0"
35
36
  end
@@ -3,7 +3,6 @@ require "bosh/workspace"
3
3
  module Bosh::Cli::Command
4
4
  class DeploymentPatch < Base
5
5
  include Bosh::Workspace::ProjectDeploymentHelper
6
- include Bosh::Workspace::GitCredentialsHelper
7
6
 
8
7
  usage "create deployment patch"
9
8
  desc "Extract patch from the current directory and optionally writes to file"
@@ -5,15 +5,16 @@ module Bosh::Cli::Command
5
5
  include Bosh::Cli::Validation
6
6
  include Bosh::Workspace
7
7
  include ProjectDeploymentHelper
8
- include GitCredentialsHelper
9
8
  include ReleaseHelper
10
9
  include StemcellHelper
11
10
 
12
11
  usage "prepare deployment"
13
12
  desc "Resolve deployment requirements"
13
+ option "--local", "only perform local git operations (don't fetch remote)"
14
14
  def prepare
15
15
  require_project_deployment
16
16
  auth_required
17
+ offline! if options[:local]
17
18
  nl
18
19
  prepare_release_repos
19
20
  nl
@@ -28,19 +29,18 @@ module Bosh::Cli::Command
28
29
  project_deployment_releases.each do |release|
29
30
  require_git_url_error if release.git_url.nil?
30
31
  say "Fetching release '#{release.name.make_green}' to satisfy template references"
31
- fetch_or_clone_repo(release.repo_dir, release.git_url)
32
32
  release.update_repo
33
- release.required_submodules.each do |submodule|
34
- fetch_or_clone_repo(File.join(release.repo_dir, submodule.path), submodule.url)
35
- release.update_submodule(submodule)
36
- end
37
- msg = "Version '#{release.version.to_s.make_green}'"
38
- msg = "Ref '#{release.ref.make_green}'" if release.ref
39
- say "#{msg} has been checkout into:"
40
- say "- #{release.repo_dir}"
33
+ print_prepare_release_repo_message(release)
41
34
  end
42
35
  end
43
36
 
37
+ def print_prepare_release_repo_message(release)
38
+ msg = "Version '#{release.version.to_s.make_green}'"
39
+ msg = "Ref '#{release.ref.make_green}'" if release.ref
40
+ say "#{msg} has been checkout into:"
41
+ say "- #{release.repo_dir}"
42
+ end
43
+
44
44
  def require_git_url_error
45
45
  say "`bosh prepare deployment' can not be used:"
46
46
  err("`git:' is missing from `release:'".make_red)
@@ -58,7 +58,7 @@ module Bosh::Cli::Command
58
58
  say "Skipping upload"
59
59
  elsif release.url
60
60
  say "Uploading '#{release.url}'"
61
- release_remote(release.url, release.release_dir)
61
+ release_upload_from_url(release.url)
62
62
  else
63
63
  say "Uploading '#{release.name_version.make_green}'"
64
64
  release_upload(release.manifest_file, release.release_dir)
@@ -82,6 +82,7 @@ module Bosh::Cli::Command
82
82
 
83
83
  def cached_stemcell_upload(stemcell)
84
84
  unless stemcell.downloaded?
85
+ err "Stemcell not available offline: #{stemcell.file_name}" if offline?
85
86
  say "Downloading '#{stemcell.name_version.make_green}'"
86
87
  stemcell_download(stemcell.file_name)
87
88
  end
@@ -6,14 +6,15 @@ require "rugged"
6
6
  require "hashdiff"
7
7
  require "cli/core_ext"
8
8
  require "cli/validation"
9
+ require "bosh/template/renderer"
9
10
 
10
- require "bosh/workspace/helpers/spiff_helper"
11
11
  require "bosh/workspace/helpers/project_deployment_helper"
12
- require "bosh/workspace/helpers/git_credentials_helper"
13
12
  require "bosh/workspace/helpers/release_helper"
14
13
  require "bosh/workspace/helpers/stemcell_helper"
15
14
  require "bosh/workspace/helpers/dns_helper"
15
+ require "bosh/workspace/helpers/git_protocol_helper"
16
16
 
17
+ require "bosh/workspace/merge_tool"
17
18
  require "bosh/workspace/schemas/project_deployment"
18
19
  require "bosh/workspace/schemas/deployment_patch"
19
20
  require "bosh/workspace/schemas/releases"
@@ -28,5 +29,5 @@ require "bosh/workspace/project_deployment"
28
29
  require "bosh/workspace/stub_file"
29
30
  require "bosh/workspace/deployment_patch"
30
31
  require "bosh/workspace/credentials"
31
- require "bosh/workspace/git_remote_url"
32
+ require "bosh/workspace/git_credentials_provider"
32
33
  require "bosh/workspace/version"
@@ -1,13 +1,15 @@
1
1
  module Bosh::Workspace
2
2
  class Credentials
3
3
  include Bosh::Cli::Validation
4
+ include GitProtocolHelper
5
+ attr_reader :raw_credentials
4
6
 
5
7
  def initialize(file)
6
8
  @raw_credentials = YAML.load_file file
7
9
  end
8
10
 
9
11
  def perform_validation(options = {})
10
- Schemas::Credentials.new.validate @raw_credentials
12
+ Schemas::Credentials.new.validate raw_credentials
11
13
  rescue Membrane::SchemaValidationError => e
12
14
  errors << e.message
13
15
  end
@@ -16,15 +18,20 @@ module Bosh::Workspace
16
18
  credentials[url]
17
19
  end
18
20
 
21
+ def url_protocols
22
+ Hash[raw_credentials.map { |c| [c['url'], git_protocol_from_url(c['url'])] }]
23
+ end
24
+
19
25
  private
20
26
 
21
27
  def credentials
22
28
  @credentials ||= begin
23
- Hash[@raw_credentials.map do |c|
24
- [c.delete('url'),
25
- c.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }]
26
- end]
29
+ Hash[raw_credentials.map { |c| [c.delete('url'), symbolize_keys(c)] }]
27
30
  end
28
31
  end
32
+
33
+ def symbolize_keys(hash)
34
+ hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
35
+ end
29
36
  end
30
37
  end
@@ -0,0 +1,80 @@
1
+ module Bosh::Workspace
2
+ class GitCredentialsProvider
3
+ attr_reader :credentials_file
4
+
5
+ def initialize(credentials_file)
6
+ @credentials_file = credentials_file
7
+ end
8
+
9
+ def callback
10
+ proc do |url, user, allowed_types|
11
+ require_credentials_file_for!(url)
12
+ validate_credentials!
13
+ validate_url_protocol_support!
14
+ credentials_for(url, user, allowed_types)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def credentials
21
+ @credentials ||= Credentials.new(@credentials_file)
22
+ end
23
+
24
+ def require_credentials_file_for!(url)
25
+ return if File.exist? @credentials_file
26
+ say("Authentication is required for: #{url}".make_red)
27
+ err("Credentials file does not exist: #{@credentials_file}".make_red)
28
+ end
29
+
30
+ def validate_credentials!
31
+ return if credentials.valid?
32
+ say("Validation errors:".make_red)
33
+ credentials.errors.each { |error| say("- #{error}") }
34
+ err("'#{credentials_file}' is not valid".make_red)
35
+ end
36
+
37
+ def validate_url_protocol_support!
38
+ credentials.url_protocols.each do |url, protocol|
39
+ next if Rugged.features.include? protocol
40
+ say("Please reinstall Rugged gem with #{protocol} support: http://git.io/veiyJ")
41
+ err("Rugged requires #{protocol} support for: #{url}")
42
+ end
43
+ end
44
+
45
+ def credentials_for(url, user, allowed_types)
46
+ if creds = credentials.find_by_url(url)
47
+ load_git_credentials(creds, user, allowed_types)
48
+ else
49
+ say("Credential look up failed in: #{credentials_file}")
50
+ err("No credentials found for: #{url}".make_red)
51
+ end
52
+ end
53
+
54
+ def load_git_credentials(credentials, user, allowed_types)
55
+ case allowed_types
56
+ when %i(ssh_key)
57
+ key_file = temp_key_file(credentials[:private_key])
58
+ Rugged::Credentials::SshKey.new username: user, privatekey: key_file
59
+ when %i(plain_text)
60
+ Rugged::Credentials::UserPassword.new(credentials)
61
+ end
62
+ end
63
+
64
+ def temp_key_file(key)
65
+ file = Tempfile.new('sshkey')
66
+ file.write key
67
+ file.close
68
+ File.chmod(0600, file.path)
69
+ file.path
70
+ end
71
+
72
+ def say(*args)
73
+ super
74
+ end
75
+
76
+ def err(*args)
77
+ super
78
+ end
79
+ end
80
+ end
@@ -1,11 +1,7 @@
1
1
  module Bosh::Workspace
2
- class GitRemoteUrl
3
- def initialize(url)
4
- @url = url
5
- end
6
-
7
- def protocol()
8
- case @url
2
+ module GitProtocolHelper
3
+ def git_protocol_from_url(url)
4
+ case url
9
5
  when /^git:/
10
6
  return :git
11
7
  when /^https:/
@@ -15,7 +11,7 @@ module Bosh::Workspace
15
11
  when /(@.+:|^ssh:)/
16
12
  return :ssh
17
13
  else
18
- raise "Unsupported protocol for remote git url: #{@url}"
14
+ return nil
19
15
  end
20
16
  end
21
17
  end
@@ -15,7 +15,7 @@ module Bosh::Workspace
15
15
  end
16
16
 
17
17
  def project_deployment_file?(deployment)
18
- Psych.load(ERB.new(File.read(deployment)).result).has_key?("templates")
18
+ ProjectDeployment.new(deployment).manifest.has_key?("templates")
19
19
  end
20
20
 
21
21
  def require_project_deployment
@@ -46,7 +46,7 @@ module Bosh::Workspace
46
46
 
47
47
  say("Generating deployment manifest")
48
48
  ManifestBuilder.build(project_deployment, work_dir)
49
-
49
+
50
50
  if domain_name = project_deployment.domain_name
51
51
  say("Transforming to dynamic networking (dns)")
52
52
  DnsHelper.transform(project_deployment.merged_file, domain_name)
@@ -57,6 +57,14 @@ module Bosh::Workspace
57
57
  use_targeted_director_uuid if director_uuid_current?
58
58
  end
59
59
 
60
+ def offline!
61
+ @offline = true
62
+ end
63
+
64
+ def offline?
65
+ @offline
66
+ end
67
+
60
68
  private
61
69
 
62
70
  def use_targeted_director_uuid
@@ -7,10 +7,13 @@ module Bosh::Workspace
7
7
  remote_release && remote_release["versions"].include?(version.to_s)
8
8
  end
9
9
 
10
- def release_upload(manifest_file_or_release_url, release_dir)
11
- Dir.chdir(release_dir) do
12
- release_cmd.upload(manifest_file_or_release_url)
13
- end
10
+ def release_upload_from_url(release_url)
11
+ upload_release_cmd.upload(release_url)
12
+ end
13
+
14
+ def release_upload(manifest_file, release_dir)
15
+ release_tarball = create_release(manifest_file, release_dir)
16
+ upload_release_cmd.upload(release_tarball)
14
17
  end
15
18
 
16
19
  def releases_dir
@@ -20,15 +23,38 @@ module Bosh::Workspace
20
23
  end
21
24
 
22
25
  def project_deployment_releases
23
- @releases ||= begin
24
- project_deployment.releases.map { |r| Release.new(r, releases_dir) }
26
+ project_deployment.releases.map do |r|
27
+ Release.new(r, releases_dir, credentials_callback, offline: offline?)
25
28
  end
26
29
  end
27
30
 
28
31
  private
29
32
 
30
- def release_cmd
33
+ def create_release(release_manifest, release_dir)
34
+ release_tarball = release_manifest.sub('yml', 'tgz')
35
+ return release_tarball if File.exist?(release_tarball)
36
+ err "Final release tarball missing: #{release_tarball}" if offline?
37
+ create_release_cmd(release_dir).create(release_manifest)
38
+ release_tarball
39
+ end
40
+
41
+ def credentials_callback
42
+ @callback ||= GitCredentialsProvider.new(credentials_file).callback
43
+ end
44
+
45
+ def credentials_file
46
+ File.join(work_dir, '.credentials.yml')
47
+ end
48
+
49
+ def upload_release_cmd
31
50
  Bosh::Cli::Command::Release::UploadRelease.new
32
51
  end
52
+
53
+ def create_release_cmd(release_dir)
54
+ Bosh::Cli::Command::Release::CreateRelease.new.tap do |r|
55
+ r.add_option(:with_tarball, true)
56
+ r.add_option(:dir, release_dir)
57
+ end
58
+ end
33
59
  end
34
60
  end
@@ -1,6 +1,6 @@
1
1
  module Bosh::Workspace
2
2
  class ManifestBuilder
3
- include Bosh::Workspace::SpiffHelper
3
+ attr_reader :merge_tool
4
4
 
5
5
  def self.build(project_deployment, work_dir)
6
6
  manifest_builder = ManifestBuilder.new(project_deployment, work_dir)
@@ -9,22 +9,20 @@ module Bosh::Workspace
9
9
 
10
10
  def initialize(project_deployment, work_dir)
11
11
  @project_deployment = project_deployment
12
+ @merge_tool = @project_deployment.merge_tool
12
13
  @work_dir = work_dir
13
14
  end
14
15
 
15
16
  def merge_templates
16
- spiff_merge spiff_template_paths, @project_deployment.merged_file
17
+ @merge_tool.merge(template_paths, @project_deployment.merged_file)
17
18
  end
18
19
 
19
20
  private
20
21
 
21
- def spiff_template_paths
22
- spiff_templates = template_paths
23
- spiff_templates << stub_file_path
24
- end
25
-
26
22
  def template_paths
27
- @project_deployment.templates.map { |t| template_path(t) }
23
+ @template_paths ||= @project_deployment.templates.map do |t|
24
+ template_path(t)
25
+ end.push(stub_file_path)
28
26
  end
29
27
 
30
28
  def stub_file_path
@@ -0,0 +1,73 @@
1
+ module Bosh::Workspace
2
+ class MergeTool
3
+ include Bosh::Exec
4
+
5
+ attr_accessor :name, :version
6
+
7
+ def initialize(merge_tool = nil)
8
+ @name, @version = case merge_tool
9
+ when Hash
10
+ [merge_tool['name'], merge_tool['version']]
11
+ when String
12
+ [merge_tool, 'current']
13
+ else
14
+ ['spiff', 'current']
15
+ end
16
+
17
+ unless available_tool_names.include?(@name)
18
+ say("#{@name} is not supported, please specify spiff or spruce instead.".make_red)
19
+ end
20
+ end
21
+
22
+ def available_tool_names
23
+ %w(spiff spruce)
24
+ end
25
+
26
+ def merge(templates, target_file)
27
+ check_tool_version if version != 'current'
28
+ run_merge_tool(:merge, templates) do |output|
29
+ File.open(target_file, 'w') { |file| file.write(output) }
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def run_merge_tool(verb, params = [])
36
+ params.map!(&:shellescape)
37
+ cmd = [name, verb.to_s] + params + ['2>&1']
38
+ sh(cmd.join(" "), :yield => :on_false) do |result|
39
+ command_not_found if result.not_found?
40
+ command_failed(result.command, result.output) if result.failed?
41
+ yield result.output
42
+ end
43
+ end
44
+
45
+ def check_tool_version
46
+ run_merge_tool('-v') do |output|
47
+ actual_version = output.match(/(\d+\.\d+\.\d+)/).to_a.first
48
+ if actual_version.nil? || actual_version != version
49
+ warning "Deployment requires #{name} to have version #{version}. " +
50
+ "Your actual #{name} version is #{actual_version}."
51
+ end
52
+ end
53
+ end
54
+
55
+ def command_not_found(command)
56
+ say("Can't find #{name} in $PATH".make_red)
57
+ say("Go to #{installation_instructions_url} for installation instructions")
58
+ err("Please make sure #{name} is installed")
59
+ end
60
+
61
+ def installation_instructions_url
62
+ case name
63
+ when 'spiff' then 'spiff.cfapps.io'
64
+ when 'spruce' then 'https://github.com/geofffranks/spruce#installation'
65
+ end
66
+ end
67
+
68
+ def command_failed(command, output)
69
+ say("Command failed: '#{command}'")
70
+ err(output)
71
+ end
72
+ end
73
+ end