bosh-workspace 0.9.2 → 0.9.3

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