bosh-workspace 0.8.5 → 0.9.0.rc1
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +1 -2
- data/.ruby-gemset +1 -1
- data/.travis.yml +2 -0
- data/Guardfile +2 -2
- data/README.md +4 -4
- data/bosh-workspace.gemspec +2 -1
- data/lib/bosh/cli/commands/deployment_patch.rb +96 -0
- data/lib/bosh/cli/commands/prepare.rb +6 -4
- data/lib/bosh/workspace/credentials.rb +30 -0
- data/lib/bosh/workspace/deployment_patch.rb +90 -0
- data/lib/bosh/workspace/helpers/git_credentials_helper.rb +95 -0
- data/lib/bosh/workspace/helpers/spiff_helper.rb +1 -0
- data/lib/bosh/workspace/project_deployment.rb +2 -44
- data/lib/bosh/workspace/release.rb +20 -23
- data/lib/bosh/workspace/schemas/credentials.rb +27 -0
- data/lib/bosh/workspace/schemas/deployment_patch.rb +15 -0
- data/lib/bosh/workspace/schemas/project_deployment.rb +21 -0
- data/lib/bosh/workspace/schemas/releases.rb +16 -0
- data/lib/bosh/workspace/schemas/stemcells.rb +25 -0
- data/lib/bosh/workspace/shell.rb +67 -0
- data/lib/bosh/workspace/tasks/bosh_command_runner.rb +29 -0
- data/lib/bosh/workspace/tasks/deployment.rb +63 -0
- data/lib/bosh/workspace/tasks/workspace.rake +69 -0
- data/lib/bosh/workspace/tasks.rb +15 -0
- data/lib/bosh/workspace/version.rb +1 -1
- data/lib/bosh/workspace.rb +14 -0
- data/spec/assets/foo-boshrelease-repo-new-structure.zip +0 -0
- data/spec/assets/foo-boshrelease-repo-updated.zip +0 -0
- data/spec/assets/foo-boshrelease-repo.zip +0 -0
- data/spec/assets/foo-boshworkspace.zip +0 -0
- data/spec/commands/deployment_patch_spec.rb +152 -0
- data/spec/commands/prepare_spec.rb +5 -3
- data/spec/credentials_spec.rb +46 -0
- data/spec/deployment_patch_spec.rb +171 -0
- data/spec/helpers/git_credentials_helper_spec.rb +160 -0
- data/spec/helpers/spiff_helper_spec.rb +16 -3
- data/spec/project_deployment_spec.rb +52 -163
- data/spec/release_spec.rb +208 -80
- data/spec/schemas/credentials_spec.rb +28 -0
- data/spec/schemas/deployment_patch_spec.rb +30 -0
- data/spec/schemas/project_deployment_spec.rb +45 -0
- data/spec/schemas/releases_spec.rb +31 -0
- data/spec/schemas/stemcells_spec.rb +37 -0
- data/spec/shell_spec.rb +70 -0
- data/spec/spec_helper.rb +11 -5
- data/spec/support/shared_contexts/rake.rb +37 -0
- data/spec/tasks/bosh_command_runner_spec.rb +39 -0
- data/spec/tasks/deployment_spec.rb +80 -0
- data/spec/tasks/workspace_task_spec.rb +99 -0
- metadata +69 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a0e593085c05c3aa75cd00f1dd9d94782a3b8688
|
4
|
+
data.tar.gz: 55aaae5f63c4035d13650769a99605d90e678959
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4880764b4fa20f940a5ed3242f8317e58dfc5faeec1f35205b1b1e2dfb2fa729413831eabf71406bf116365d7861e68ca4da3cf3370d6d010454a33777f3d54c
|
7
|
+
data.tar.gz: 08daea7f845dcc9f5f9c8531b1504d88c6832cc1847dcbf49204830fd9e232bc087cd3efaca31b5afac293978de9d1f94f752f4fdd77c3ccf7e050a1be8d4398
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/.ruby-gemset
CHANGED
@@ -1 +1 @@
|
|
1
|
-
bosh-
|
1
|
+
bosh-workspace
|
data/.travis.yml
CHANGED
data/Guardfile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
guard :rspec, cmd: 'rspec' do
|
2
|
-
notification :off
|
1
|
+
guard :rspec, cmd: 'rspec', notification: false do
|
3
2
|
watch(%r{^spec/(.+_spec)\.rb$})
|
4
3
|
watch(%r{^lib/bosh/cli/commands/(.+)\.rb$}) { |m| "spec/commands/#{m[1]}_spec.rb" }
|
5
4
|
watch(%r{^lib/bosh/workspace/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
5
|
+
watch(%r{^lib/bosh/workspace/(.+)\.rake$}) { |m| "spec/#{m[1]}_task_spec.rb" }
|
6
6
|
watch('spec/spec_helper.rb') { "spec" }
|
7
7
|
end
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Bosh workspace
|
2
|
-
[](https://travis-ci.org/cloudfoundry-incubator/bosh-workspace) [](https://codeclimate.com/github/rkoster/bosh-workspace) [](https://codeclimate.com/github/rkoster/bosh-workspace) [](https://gemnasium.com/cloudfoundry-incubator/bosh-workspace)
|
3
3
|
|
4
4
|
|
5
5
|
This is a `bosh` cli plugin for creating reproducible and upgradable deployments.
|
@@ -18,9 +18,9 @@ cd fg-boshworkspace
|
|
18
18
|
Lets create the initial files & directories.
|
19
19
|
```
|
20
20
|
mkdir deployments templates
|
21
|
-
echo 'source "https://rubygems.org"\n\ngem "bosh-workspace"' > Gemfile
|
22
|
-
echo "2.
|
23
|
-
echo '.stemcells*\n.deployments*\n.releases*\n.stubs*\n' > .gitignore
|
21
|
+
echo -e 'source "https://rubygems.org"\n\ngem "bosh-workspace"' > Gemfile
|
22
|
+
echo "2.1.0" > .ruby-version
|
23
|
+
echo -e '.stemcells*\n.deployments*\n.releases*\n.stubs*\n' > .gitignore
|
24
24
|
```
|
25
25
|
|
26
26
|
Now install the gems by running bundler.
|
data/bosh-workspace.gemspec
CHANGED
@@ -24,7 +24,8 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_runtime_dependency "bosh_common", ">= 1.2682.0"
|
25
25
|
spec.add_runtime_dependency "semi_semantic", "~> 1.1.0"
|
26
26
|
spec.add_runtime_dependency "membrane", "~> 1.1.0"
|
27
|
-
spec.add_runtime_dependency "
|
27
|
+
spec.add_runtime_dependency "hashdiff", "~> 0.2.1"
|
28
|
+
spec.add_runtime_dependency "rugged", "~> 0.22.0b5"
|
28
29
|
|
29
30
|
spec.add_development_dependency "bundler", "~> 1.6"
|
30
31
|
spec.add_development_dependency "rspec", "~> 3.1.0"
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "bosh/workspace"
|
2
|
+
|
3
|
+
module Bosh::Cli::Command
|
4
|
+
class DeploymentPatch < Base
|
5
|
+
include Bosh::Workspace::ProjectDeploymentHelper
|
6
|
+
include Bosh::Workspace::GitCredenialsHelper
|
7
|
+
|
8
|
+
usage "create deployment patch"
|
9
|
+
desc "Extract patch from the current directory and optionally writes to file"
|
10
|
+
def create(deployment_patch)
|
11
|
+
require_project_deployment
|
12
|
+
current_deployment_patch.to_file(deployment_patch)
|
13
|
+
say "Wrote patch to #{deployment_patch}"
|
14
|
+
end
|
15
|
+
|
16
|
+
usage "apply deployment patch"
|
17
|
+
desc "Apply a build patch to the current working directory"
|
18
|
+
option "--dry-run", "only show the changes without applying them"
|
19
|
+
option "--no-commit", "do not commit applied changes"
|
20
|
+
def apply(deployment_patch)
|
21
|
+
require_project_deployment
|
22
|
+
@patch = Bosh::Workspace::DeploymentPatch.from_file(deployment_patch)
|
23
|
+
validate_deployment_patch(@patch, deployment_patch)
|
24
|
+
|
25
|
+
if current_deployment_patch.changes?(@patch)
|
26
|
+
if !options[:dry_run]
|
27
|
+
fetch_repo(templates_dir) if @patch.templates_ref
|
28
|
+
@patch.apply(current_deployment_file, templates_dir)
|
29
|
+
commit_all unless options[:no_commit]
|
30
|
+
say "Successfully applied deployment patch:"
|
31
|
+
else
|
32
|
+
say "Deployment patch:"
|
33
|
+
end
|
34
|
+
|
35
|
+
say patch_changes_table
|
36
|
+
else
|
37
|
+
say "No changes, nothing to do"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def templates_dir
|
44
|
+
File.join(Dir.getwd, 'templates')
|
45
|
+
end
|
46
|
+
|
47
|
+
def current_deployment_file
|
48
|
+
@current_deployment_file ||= project_deployment.file
|
49
|
+
end
|
50
|
+
|
51
|
+
def current_deployment_patch
|
52
|
+
@current_deployment_patch ||= begin
|
53
|
+
Bosh::Workspace::DeploymentPatch
|
54
|
+
.create(current_deployment_file, templates_dir)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def validate_deployment_patch(patch, file)
|
59
|
+
unless patch.valid?
|
60
|
+
say("Validation errors:".make_red)
|
61
|
+
patch.errors.each { |error| say("- #{error}") }
|
62
|
+
err("'#{file}' is not valid".make_red)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def commit_all
|
67
|
+
index = repo.index
|
68
|
+
index.read_tree(repo.head.target.tree)
|
69
|
+
index.add_all
|
70
|
+
options = {
|
71
|
+
tree: index.write_tree(repo),
|
72
|
+
message: changes_message,
|
73
|
+
update_ref: 'HEAD'
|
74
|
+
}
|
75
|
+
Rugged::Commit.create(repo, options)
|
76
|
+
end
|
77
|
+
|
78
|
+
def repo
|
79
|
+
@repo ||= Rugged::Repository.new(Dir.getwd)
|
80
|
+
end
|
81
|
+
|
82
|
+
def patch_changes
|
83
|
+
@patch_changes ||= current_deployment_patch.changes(@patch)
|
84
|
+
end
|
85
|
+
|
86
|
+
def changes_message
|
87
|
+
"Applied " + patch_changes.map { |k, v| "#{k} #{v}" }.join(', ').to_s
|
88
|
+
end
|
89
|
+
|
90
|
+
def patch_changes_table
|
91
|
+
table do |t|
|
92
|
+
patch_changes.each { |k, v| t << [k.to_s, v] }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -5,6 +5,7 @@ module Bosh::Cli::Command
|
|
5
5
|
include Bosh::Cli::Validation
|
6
6
|
include Bosh::Workspace
|
7
7
|
include ProjectDeploymentHelper
|
8
|
+
include GitCredenialsHelper
|
8
9
|
include ReleaseHelper
|
9
10
|
include StemcellHelper
|
10
11
|
|
@@ -25,7 +26,8 @@ module Bosh::Cli::Command
|
|
25
26
|
|
26
27
|
def prepare_release_repos
|
27
28
|
project_deployment_releases.each do |release|
|
28
|
-
say "
|
29
|
+
say "Fetching release '#{release.name.make_green}' to satisfy template references"
|
30
|
+
fetch_or_clone_repo(release.repo_dir, release.git_url)
|
29
31
|
release.update_repo
|
30
32
|
msg = "Version '#{release.version.to_s.make_green}'"
|
31
33
|
msg = "Ref '#{release.ref.make_green}'" if release.ref
|
@@ -64,11 +66,11 @@ module Bosh::Cli::Command
|
|
64
66
|
cached_stemcell_upload(stemcell)
|
65
67
|
end
|
66
68
|
end
|
67
|
-
|
68
|
-
def cached_stemcell_upload(stemcell)
|
69
|
+
|
70
|
+
def cached_stemcell_upload(stemcell)
|
69
71
|
unless stemcell.downloaded?
|
70
72
|
say "Downloading '#{stemcell.name_version.make_green}'"
|
71
|
-
stemcell_download(stemcell.file_name)
|
73
|
+
stemcell_download(stemcell.file_name)
|
72
74
|
end
|
73
75
|
say "Uploading '#{stemcell.name_version.make_green}'"
|
74
76
|
stemcell_upload(stemcell.file)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Bosh::Workspace
|
2
|
+
class Credentials
|
3
|
+
include Bosh::Cli::Validation
|
4
|
+
|
5
|
+
def initialize(file)
|
6
|
+
@raw_credentials = YAML.load_file file
|
7
|
+
end
|
8
|
+
|
9
|
+
def perform_validation(options = {})
|
10
|
+
Schemas::Credentials.new.validate @raw_credentials
|
11
|
+
rescue Membrane::SchemaValidationError => e
|
12
|
+
errors << e.message
|
13
|
+
end
|
14
|
+
|
15
|
+
def find_by_url(url)
|
16
|
+
credentials[url]
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def credentials
|
22
|
+
@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]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Bosh::Workspace
|
2
|
+
class DeploymentPatch
|
3
|
+
include Bosh::Cli::Validation
|
4
|
+
attr_reader :stemcells, :releases, :templates_ref
|
5
|
+
|
6
|
+
def self.create(deployment_file, templates_dir)
|
7
|
+
if File.exist? File.join(templates_dir, '.git')
|
8
|
+
ref = Rugged::Repository.new(templates_dir).head.target.oid
|
9
|
+
end
|
10
|
+
deployment = YAML.load_file deployment_file
|
11
|
+
new(deployment["stemcells"], deployment["releases"], ref)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.from_file(patch_file)
|
15
|
+
a = YAML.load_file patch_file
|
16
|
+
new(a["stemcells"], a["releases"], a["templates_ref"])
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(stemcells, releases, templates_ref)
|
20
|
+
@stemcells = stemcells
|
21
|
+
@releases = releases
|
22
|
+
@templates_ref = templates_ref
|
23
|
+
end
|
24
|
+
|
25
|
+
def perform_validation(options = {})
|
26
|
+
Schemas::DeploymentPatch.new.validate to_hash
|
27
|
+
rescue Membrane::SchemaValidationError => e
|
28
|
+
errors << e.message
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_hash
|
32
|
+
{ "stemcells" => stemcells, "releases" => releases }
|
33
|
+
.tap { |h| h["templates_ref"] = templates_ref if templates_ref }
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_yaml
|
37
|
+
to_hash.to_yaml
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_file(patch_file)
|
41
|
+
IO.write(patch_file, to_yaml)
|
42
|
+
end
|
43
|
+
|
44
|
+
def apply(deployment_file, templates_dir)
|
45
|
+
checkout_submodule(templates_dir, templates_ref) if templates_ref
|
46
|
+
deployment = YAML.load_file deployment_file
|
47
|
+
deployment.merge! 'stemcells' => stemcells, 'releases' => releases
|
48
|
+
IO.write(deployment_file, deployment.to_yaml)
|
49
|
+
end
|
50
|
+
|
51
|
+
def changes(patch)
|
52
|
+
{
|
53
|
+
stemcells: item_changes(stemcells, patch.stemcells),
|
54
|
+
releases: item_changes(releases, patch.releases),
|
55
|
+
templates_ref: item_changes(templates_ref, patch.templates_ref)
|
56
|
+
}.reject { |_, v| v.empty? }
|
57
|
+
end
|
58
|
+
|
59
|
+
def changes?(patch)
|
60
|
+
!changes(patch).empty?
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def item_changes(old, new)
|
66
|
+
return [] unless old
|
67
|
+
old, new = presentify_item(old), presentify_item(new)
|
68
|
+
changes = HashDiff.diff(old, new).map { |a| a.join(' ').squeeze(' ') }
|
69
|
+
presentify_changes(changes).join(', ')
|
70
|
+
end
|
71
|
+
|
72
|
+
def presentify_changes(changes)
|
73
|
+
translations = { '~' => "changed", '-' => 'removed', '+' => 'added' }
|
74
|
+
changes.map { |l| l.gsub(/^./) { |m| translations.fetch(m, m) } }
|
75
|
+
end
|
76
|
+
|
77
|
+
def presentify_item(item)
|
78
|
+
item.is_a?(Array) ? versions_hash(item) : item[0..6]
|
79
|
+
end
|
80
|
+
|
81
|
+
def versions_hash(array)
|
82
|
+
Hash[array.map { |v| [v["name"], v["version"]]}]
|
83
|
+
end
|
84
|
+
|
85
|
+
def checkout_submodule(dir, ref)
|
86
|
+
repo = Rugged::Repository.new(dir)
|
87
|
+
repo.checkout ref, strategy: :force
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Bosh::Workspace
|
2
|
+
module GitCredenialsHelper
|
3
|
+
REFSPEC = ['HEAD:refs/remotes/origin/HEAD']
|
4
|
+
|
5
|
+
def fetch_or_clone_repo(dir, url)
|
6
|
+
repo = File.exist?(dir) ? open_repo(dir) : init_repo(dir, url)
|
7
|
+
fetch_and_checkout(repo)
|
8
|
+
end
|
9
|
+
|
10
|
+
def fetch_repo(dir)
|
11
|
+
fetch_and_checkout(open_repo(dir))
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def fetch_and_checkout(repo)
|
17
|
+
url = repo.remotes['origin'].url
|
18
|
+
repo.fetch('origin', REFSPEC, connection_options_for(repo, url))
|
19
|
+
repo.checkout 'refs/remotes/origin/HEAD', strategy: :force
|
20
|
+
end
|
21
|
+
|
22
|
+
def connection_options_for(repo, url)
|
23
|
+
return {} if check_connection(repo, url)
|
24
|
+
|
25
|
+
options = { credentials: require_credetials_for(url) }
|
26
|
+
unless check_connection(repo, url, options)
|
27
|
+
say "Using credentials from: #{credentials_file}"
|
28
|
+
err "Invalid credentials for: #{url}"
|
29
|
+
end
|
30
|
+
options
|
31
|
+
end
|
32
|
+
|
33
|
+
def check_connection(repo, url, options = {})
|
34
|
+
repo.remotes.create_anonymous(url).check_connection(:fetch, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def init_repo(dir, url)
|
38
|
+
FileUtils.mkdir_p File.dirname(dir)
|
39
|
+
Rugged::Repository.init_at(dir).tap do |repo|
|
40
|
+
repo.remotes.create('origin', url)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def open_repo(dir)
|
45
|
+
Rugged::Repository.new(dir)
|
46
|
+
end
|
47
|
+
|
48
|
+
def credentials
|
49
|
+
@credentials ||= Credentials.new(credentials_file)
|
50
|
+
end
|
51
|
+
|
52
|
+
def require_credetials_for(url)
|
53
|
+
unless File.exist? credentials_file
|
54
|
+
say("Authentication is required for: #{url}".make_red)
|
55
|
+
err("Credentials file does not exist: #{credentials_file}".make_red)
|
56
|
+
end
|
57
|
+
unless credentials.valid?
|
58
|
+
say("Validation errors:".make_red)
|
59
|
+
credentials.errors.each { |error| say("- #{error}") }
|
60
|
+
err("'#{credentials_file}' is not valid".make_red)
|
61
|
+
end
|
62
|
+
if creds = credentials.find_by_url(url)
|
63
|
+
load_git_credentials(creds)
|
64
|
+
else
|
65
|
+
say("Credential look up failed in: #{credentials_file}")
|
66
|
+
err("No credentials found for: #{url}".make_red)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def credentials_file
|
71
|
+
File.join work_dir, '.credentials.yml'
|
72
|
+
end
|
73
|
+
|
74
|
+
def load_git_credentials(credentials)
|
75
|
+
case credentials.keys
|
76
|
+
when %i(private_key)
|
77
|
+
options = {
|
78
|
+
username: 'git',
|
79
|
+
privatekey: temp_key_file(credentials[:private_key])
|
80
|
+
}
|
81
|
+
Rugged::Credentials::SshKey.new(options)
|
82
|
+
when %i(username password)
|
83
|
+
Rugged::Credentials::UserPassword.new(credentials)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def temp_key_file(key)
|
88
|
+
file = Tempfile.new('sshkey')
|
89
|
+
file.write key
|
90
|
+
file.close
|
91
|
+
File.chmod(0600, file.path)
|
92
|
+
file.path
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -1,59 +1,17 @@
|
|
1
|
-
require "membrane"
|
2
1
|
module Bosh::Workspace
|
3
2
|
class ProjectDeployment
|
4
3
|
include Bosh::Cli::Validation
|
5
4
|
attr_writer :director_uuid
|
6
5
|
attr_reader :file
|
7
6
|
|
8
|
-
RELEASE_SCHEMA = Membrane::SchemaParser.parse do
|
9
|
-
{
|
10
|
-
"name" => String,
|
11
|
-
"version" => enum(Integer, "latest"),
|
12
|
-
optional("ref") => enum(String),
|
13
|
-
"git" => String,
|
14
|
-
}
|
15
|
-
end
|
16
|
-
|
17
|
-
class StemcellVersionValidator < Membrane::Schemas::Base
|
18
|
-
def validate(object)
|
19
|
-
return if object.is_a? Integer
|
20
|
-
return if object.is_a? Float
|
21
|
-
return if object == "latest"
|
22
|
-
return if object.to_s =~ /^\d+\.\d+$/
|
23
|
-
raise Membrane::SchemaValidationError.new(
|
24
|
-
"Should match: latest, version.patch or version. Given: #{object}")
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
STEMCELL_SCHEMA = Membrane::SchemaParser.parse do
|
29
|
-
{
|
30
|
-
"name" => String,
|
31
|
-
"version" => StemcellVersionValidator.new
|
32
|
-
}
|
33
|
-
end
|
34
|
-
|
35
|
-
UUID_REGEX = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
|
36
|
-
|
37
|
-
PROJECT_DEPLOYMENT_SCHEMA = Membrane::SchemaParser.parse do
|
38
|
-
{
|
39
|
-
"name" => String,
|
40
|
-
"director_uuid" => enum(UUID_REGEX, "current"),
|
41
|
-
optional("domain_name") => String,
|
42
|
-
"releases" => [RELEASE_SCHEMA],
|
43
|
-
"stemcells" => [STEMCELL_SCHEMA],
|
44
|
-
"templates" => [String],
|
45
|
-
"meta" => Hash
|
46
|
-
}
|
47
|
-
end
|
48
|
-
|
49
7
|
def initialize(file)
|
50
8
|
@file = file
|
51
9
|
err("Deployment file does not exist: #{file}") unless File.exist? @file
|
52
|
-
@manifest =
|
10
|
+
@manifest = YAML.load_file @file
|
53
11
|
end
|
54
12
|
|
55
13
|
def perform_validation(options = {})
|
56
|
-
|
14
|
+
Schemas::ProjectDeployment.new.validate @manifest
|
57
15
|
rescue Membrane::SchemaValidationError => e
|
58
16
|
errors << e.message
|
59
17
|
end
|
@@ -1,21 +1,17 @@
|
|
1
|
-
require "git"
|
2
1
|
module Bosh::Workspace
|
3
2
|
class Release
|
4
|
-
attr_reader :name, :
|
3
|
+
attr_reader :name, :git_url, :repo_dir
|
5
4
|
|
6
5
|
def initialize(release, releases_dir)
|
7
6
|
@name = release["name"]
|
8
7
|
@ref = release["ref"]
|
9
8
|
@spec_version = release["version"]
|
10
|
-
@
|
9
|
+
@git_url = release["git"]
|
11
10
|
@repo_dir = File.join(releases_dir, @name)
|
12
|
-
init_repo
|
13
11
|
end
|
14
12
|
|
15
13
|
def update_repo
|
16
|
-
|
17
|
-
@repo.checkout last_commit
|
18
|
-
@repo.checkout ref || version_ref
|
14
|
+
repo.checkout ref || version_ref, strategy: :force
|
19
15
|
end
|
20
16
|
|
21
17
|
def manifest_file
|
@@ -38,34 +34,35 @@ module Bosh::Workspace
|
|
38
34
|
@spec_version.to_i
|
39
35
|
end
|
40
36
|
|
37
|
+
def ref
|
38
|
+
@ref && repo.lookup(@ref).oid
|
39
|
+
end
|
40
|
+
|
41
41
|
private
|
42
42
|
|
43
|
+
def repo
|
44
|
+
@repo ||= Rugged::Repository.new(repo_dir)
|
45
|
+
end
|
46
|
+
|
43
47
|
# transforms releases/foo-1.yml, releases/bar-2.yml to:
|
44
48
|
# { "1" => foo-1.yml, "2" => bar-2.yml }
|
45
49
|
def final_releases
|
46
50
|
@final_releases ||= begin
|
47
|
-
|
51
|
+
new_style_repo = File.directory?(File.join(repo_dir, "releases", @name))
|
52
|
+
releases_dir = new_style_repo ? "releases/#{@name}" : "releases"
|
53
|
+
|
54
|
+
Hash[Dir[File.join(repo_dir, releases_dir, "*.yml")]
|
48
55
|
.reject { |f| f[/index.yml/] }
|
49
|
-
.map { |dir| File.join(
|
56
|
+
.map { |dir| File.join(releases_dir, File.basename(dir)) }
|
50
57
|
.map { |version| [version[/(\d+)/].to_i, version] }]
|
51
58
|
end
|
52
59
|
end
|
53
60
|
|
54
|
-
def last_commit
|
55
|
-
@repo.log.object("origin").first
|
56
|
-
end
|
57
|
-
|
58
61
|
def version_ref
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
if File.directory?(repo_dir)
|
64
|
-
@repo ||= Git.open(repo_dir)
|
65
|
-
else
|
66
|
-
releases_dir = File.dirname(repo_dir)
|
67
|
-
FileUtils.mkdir_p(releases_dir)
|
68
|
-
@repo = Git.clone(@git_uri, @name, path: releases_dir)
|
62
|
+
# figure out the last commit which changed the release manifest file
|
63
|
+
# in other words the commit sha of the final release
|
64
|
+
repo.walk(repo.head.target) do |commit|
|
65
|
+
return commit.oid if commit.tree.path(manifest)
|
69
66
|
end
|
70
67
|
end
|
71
68
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Bosh::Workspace
|
2
|
+
module Schemas
|
3
|
+
class Credentials < Membrane::Schemas::Base
|
4
|
+
def validate(object)
|
5
|
+
Membrane::SchemaParser.parse do
|
6
|
+
[enum(UsernamePassword.new, SshKey.new)]
|
7
|
+
end.validate object
|
8
|
+
end
|
9
|
+
|
10
|
+
class UsernamePassword < Membrane::Schemas::Base
|
11
|
+
def validate(object)
|
12
|
+
Membrane::SchemaParser.parse do
|
13
|
+
{ "url" => String, "username" => String, "password" => String }
|
14
|
+
end.validate object
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class SshKey < Membrane::Schemas::Base
|
19
|
+
def validate(object)
|
20
|
+
Membrane::SchemaParser.parse do
|
21
|
+
{ "url" => String, "private_key" => String }
|
22
|
+
end.validate object
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Bosh::Workspace
|
2
|
+
module Schemas
|
3
|
+
class DeploymentPatch < Membrane::Schemas::Base
|
4
|
+
def validate(object)
|
5
|
+
Membrane::SchemaParser.parse do
|
6
|
+
{
|
7
|
+
"stemcells" => Stemcells.new,
|
8
|
+
"releases" => Releases.new,
|
9
|
+
optional("templates_ref") => String
|
10
|
+
}
|
11
|
+
end.validate object
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Bosh::Workspace
|
2
|
+
module Schemas
|
3
|
+
class ProjectDeployment < Membrane::Schemas::Base
|
4
|
+
UUID_REGEX = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
|
5
|
+
|
6
|
+
def validate(object)
|
7
|
+
Membrane::SchemaParser.parse do
|
8
|
+
{
|
9
|
+
"name" => String,
|
10
|
+
"director_uuid" => enum(UUID_REGEX, "current"),
|
11
|
+
optional("domain_name") => String,
|
12
|
+
"releases" => Releases.new,
|
13
|
+
"stemcells" => Stemcells.new,
|
14
|
+
"templates" => [String],
|
15
|
+
"meta" => Hash
|
16
|
+
}
|
17
|
+
end.validate object
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Bosh::Workspace
|
2
|
+
module Schemas
|
3
|
+
class Releases < Membrane::Schemas::Base
|
4
|
+
def validate(object)
|
5
|
+
Membrane::SchemaParser.parse do
|
6
|
+
[{
|
7
|
+
"name" => String,
|
8
|
+
"version" => enum(Integer, "latest"),
|
9
|
+
optional("ref") => enum(String),
|
10
|
+
"git" => String,
|
11
|
+
}]
|
12
|
+
end.validate object
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|