clc-promote 0.4.3 → 0.4.4
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 +14 -0
- data/Rakefile +6 -0
- data/clc-promote.gemspec +24 -0
- data/lib/kitchen/provisioner/environment.rb +79 -0
- data/lib/promote/version.rb +3 -0
- data/spec/unit/kitchen/provisioner/environment_spec.rb +104 -0
- data/spec/unit/promote/config_spec.rb +92 -0
- data/spec/unit/promote/cookbook_spec.rb +250 -0
- data/spec/unit/promote/promoter_spec.rb +59 -0
- data/spec/unit/promote/uploader_spec.rb +208 -0
- data/spec/unit/promote/versioner_spec.rb +291 -0
- data/spec/unit/stubs/cookbook_1/Berksfile +3 -0
- data/spec/unit/stubs/cookbook_1/metadata.rb +9 -0
- data/spec/unit/support/dummy_log.rb +47 -0
- data/spec/unit/support/dummy_metadata.rb +20 -0
- metadata +28 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 674a20f898ac27d30598579081401a13f660e374
|
4
|
+
data.tar.gz: 55dd1f8a1927e4ff519e40bc33762abd56d40680
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd0d30a5d62a51f14c8f30398971ac4cc4be3252c0152817e4ee71aaa66224bf481d2936fd537bfeadd28cbe0a2e990b181604d6b5aa72011140fd7c32d0c090
|
7
|
+
data.tar.gz: 1d1462b21bddf6cb3ed0bb5bd05929f79732e70b8ef782600b72c018e2d7f9c6cb5bb12dca81bbb8a02b8db4d7edac92aaca47c11ea04fd21ed449136f1766fc
|
data/.gitignore
ADDED
data/Rakefile
ADDED
data/clc-promote.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/lib')
|
2
|
+
require 'promote/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'clc-promote'
|
6
|
+
s.version = Promote::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.extra_rdoc_files = ['README.md']
|
9
|
+
s.summary = 'Provides versioning and promotion logic for chef artifacts.'
|
10
|
+
s.description = s.summary
|
11
|
+
s.authors = ['CenturyLink Cloud']
|
12
|
+
s.email = 'matt.wrock@CenturyLinkCloud.com'
|
13
|
+
s.homepage = 'https://github.com/tier3/DevOps/gems/clc-promote'
|
14
|
+
|
15
|
+
s.require_path = 'lib'
|
16
|
+
s.files = `git ls-files -z`.split("\x0")
|
17
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
18
|
+
|
19
|
+
s.add_runtime_dependency 'clc-git', '~> 1.2', '>= 1.2.8'
|
20
|
+
s.add_runtime_dependency 'berkshelf', '~> 3.1', '>= 3.1.4'
|
21
|
+
|
22
|
+
s.add_development_dependency 'rspec', '~> 3.0', '>= 3.0.0'
|
23
|
+
s.add_development_dependency 'rake', '~> 10.3', '>= 10.3.2'
|
24
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require "berkshelf/downloader"
|
3
|
+
require "kitchen/provisioner/chef_zero"
|
4
|
+
require "promote"
|
5
|
+
|
6
|
+
module Kitchen
|
7
|
+
|
8
|
+
module Provisioner
|
9
|
+
|
10
|
+
class Environment < ChefZero
|
11
|
+
|
12
|
+
def create_sandbox
|
13
|
+
super
|
14
|
+
prepare_environment_dependencies
|
15
|
+
create_node
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def create_node
|
21
|
+
node_file = File.join(instance.busser[:test_base_path], "nodes/#{instance.suite.name}.json")
|
22
|
+
if File.exist?(node_file)
|
23
|
+
node = JSON.parse(File.read(node_file))
|
24
|
+
node[:run_list] = config[:run_list]
|
25
|
+
File.open(node_file, 'w') do |out|
|
26
|
+
out << JSON.pretty_generate(node)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def prepare_environment_dependencies
|
32
|
+
if !config[:client_rb].has_key?(:environment)
|
33
|
+
info("No environment specified to lock")
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
environment = config[:client_rb][:environment]
|
38
|
+
repo = File.dirname(config[:environments_path])
|
39
|
+
promote_config = Promote::Config.new(:repo_root => repo)
|
40
|
+
env_file = Promote::EnvironmentFile.new(environment, promote_config)
|
41
|
+
|
42
|
+
info("comparing current cookbook versions to those in #{environment}")
|
43
|
+
Kitchen.mutex.synchronize do
|
44
|
+
berks_path = File.join(config[:kitchen_root], 'Berksfile')
|
45
|
+
::Berkshelf.set_format :null
|
46
|
+
berks = ::Berkshelf::Berksfile.from_file(berks_path)
|
47
|
+
downloader = ::Berkshelf::Downloader.new(berks)
|
48
|
+
berks.install
|
49
|
+
my_deps = berks.list
|
50
|
+
|
51
|
+
my_deps.each do | dep |
|
52
|
+
next if dep.location.respond_to?(:relative_path)
|
53
|
+
env_version = env_file.cookbook_versions[dep.name]
|
54
|
+
|
55
|
+
if !env_version.nil? && dep.locked_version.to_s != env_version
|
56
|
+
if dep.name == File.basename(config[:kitchen_root])
|
57
|
+
raise "cookbook '#{dep.name}' is outdated. Expected v#{env_version} from #{environment}"
|
58
|
+
end
|
59
|
+
|
60
|
+
info("replacing v#{dep.locked_version} of #{dep.name} with #{env_version}")
|
61
|
+
downloader.download(dep.name, env_version) do |stash|
|
62
|
+
FileUtils.copy_entry(stash, File.join(tmpbooks_dir, dep.name))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# we do this because the vendoring converts metadayta.rb to json
|
68
|
+
# any subsequent berks command on the vendored cookbook will fail
|
69
|
+
FileUtils.rm_rf Dir.glob("#{tmpbooks_dir}/**/Berksfile*")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def tmpbooks_dir
|
74
|
+
File.join(sandbox_path, "cookbooks")
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'kitchen'
|
2
|
+
require 'kitchen/configurable'
|
3
|
+
require 'kitchen/provisioner/environment'
|
4
|
+
|
5
|
+
describe Kitchen::Provisioner::Environment do
|
6
|
+
let(:logged_output) { StringIO.new }
|
7
|
+
let(:logger) { Logger.new(logged_output) }
|
8
|
+
let(:config) do
|
9
|
+
{ :test_base_path => "/b", :kitchen_root => "/cookbook_4", :log_level => :info, :sudo => true, :client_rb => client_rb_config, :environments_path => '/env/path' }
|
10
|
+
end
|
11
|
+
let(:suite) do
|
12
|
+
double('suite', :name => "fries")
|
13
|
+
end
|
14
|
+
let(:transport) do
|
15
|
+
double('transport', :sudo => config[:sudo], :shell => "bourne")
|
16
|
+
end
|
17
|
+
let(:instance) do
|
18
|
+
double('instance', :name => "coolbeans", :logger => logger, :suite => suite, :transport => transport, :busser => {:test_base_path => 'base_path'})
|
19
|
+
end
|
20
|
+
let(:downloader) { double('downloader') }
|
21
|
+
let(:fake_berks) { double('berksfile', :install => nil, :update => nil, :list => [
|
22
|
+
double('Dependency', :name => 'cookbook_2', :locked_version => Semverse::Version.new('1.1.1'), :location => nil),
|
23
|
+
double('Dependency', :name => 'cookbook_3', :locked_version => Semverse::Version.new('2.2.2'), :location => nil),
|
24
|
+
double('Dependency', :name => 'cookbook_4', :locked_version => Semverse::Version.new('3.3.3'), :location => nil)
|
25
|
+
]) }
|
26
|
+
let(:client_rb_config) { {:environment => 'environment'} }
|
27
|
+
let (:env_file) { double('env', :cookbook_versions => {
|
28
|
+
'cookbook_2' => '1.1.1',
|
29
|
+
'cookbook_3' => '2.2.2',
|
30
|
+
'cookbook_4' => '3.3.3'
|
31
|
+
})}
|
32
|
+
|
33
|
+
subject { Kitchen::Provisioner::Environment.new(config).finalize_config!(instance) }
|
34
|
+
|
35
|
+
before {
|
36
|
+
allow(FileUtils).to receive(:rm_rf)
|
37
|
+
allow(FileUtils).to receive(:copy_entry)
|
38
|
+
allow(Berkshelf::Berksfile).to receive(:from_file).and_return(fake_berks)
|
39
|
+
allow(Promote::EnvironmentFile).to receive(:new).and_return(env_file)
|
40
|
+
allow(Berkshelf::Downloader).to receive(:new).and_return(downloader)
|
41
|
+
}
|
42
|
+
|
43
|
+
context "when no environment specified" do
|
44
|
+
let(:client_rb_config) { {} }
|
45
|
+
|
46
|
+
it "does not get berks dependencies" do
|
47
|
+
expect(fake_berks).not_to receive(:list)
|
48
|
+
subject.create_sandbox
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "wnem local cookbooks are outdated" do
|
53
|
+
let (:env_file) { double('env', :cookbook_versions => {
|
54
|
+
'cookbook_2' => '2.2.2',
|
55
|
+
'cookbook_3' => '2.2.2',
|
56
|
+
'cookbook_4' => '3.3.3'
|
57
|
+
})}
|
58
|
+
|
59
|
+
it "downloads the outdated cookbooks" do
|
60
|
+
expect(downloader).to receive(:download).with('cookbook_2', '2.2.2')
|
61
|
+
subject.create_sandbox
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when environment file is missing a dependency" do
|
66
|
+
let (:env_file) { double('env', :cookbook_versions => {
|
67
|
+
'cookbook_2' => '2.2.2',
|
68
|
+
'cookbook_4' => '3.3.3'
|
69
|
+
})}
|
70
|
+
|
71
|
+
it "downloads only the outdated cookbook" do
|
72
|
+
expect(downloader).to receive(:download).with('cookbook_2', '2.2.2')
|
73
|
+
subject.create_sandbox
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "when cookbook under test is outdated" do
|
78
|
+
let (:env_file) { double('env', :cookbook_versions => {
|
79
|
+
'cookbook_2' => '1.1.1',
|
80
|
+
'cookbook_3' => '2.2.2',
|
81
|
+
'cookbook_4' => '4.4.4'
|
82
|
+
})}
|
83
|
+
|
84
|
+
it "downloads only the outdated cookbooks" do
|
85
|
+
expect{subject.create_sandbox}.to raise_error(/4\.4\.4/)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "when outdated cookbook is from a local source" do
|
90
|
+
let(:fake_berks) { double('berksfile', :install => nil, :update => nil, :list => [
|
91
|
+
double('Dependency', :name => 'cookbook_2', :locked_version => Semverse::Version.new('1.1.1'), :location => nil),
|
92
|
+
double('Dependency',
|
93
|
+
:name => 'cookbook_3',
|
94
|
+
:locked_version => Semverse::Version.new('1.1.1'),
|
95
|
+
:location => double('location', :relative_path => '../cookbook')),
|
96
|
+
double('Dependency', :name => 'cookbook_4', :locked_version => Semverse::Version.new('3.3.3'), :location => nil)
|
97
|
+
]) }
|
98
|
+
|
99
|
+
it "downloads only the outdated cookbooks" do
|
100
|
+
expect(downloader).not_to receive(:download)
|
101
|
+
subject.create_sandbox
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'promote'
|
2
|
+
|
3
|
+
describe Promote::Config do
|
4
|
+
let(:opts) {{
|
5
|
+
:repo_root => "root",
|
6
|
+
:cookbook_directory => "cookbooks",
|
7
|
+
:environment_directory => "environments",
|
8
|
+
:data_bag_directory => "data_bags",
|
9
|
+
:temp_directory => "temp",
|
10
|
+
:node_name => "user",
|
11
|
+
:client_key => "key",
|
12
|
+
:chef_server_url => "url"}}
|
13
|
+
subject { Promote::Config.new(opts) }
|
14
|
+
|
15
|
+
it "assigns options to node_name attribute" do
|
16
|
+
expect(subject.node_name).to eq(opts[:node_name])
|
17
|
+
end
|
18
|
+
it "assigns options to client_key attribute" do
|
19
|
+
expect(subject.client_key).to eq(opts[:client_key])
|
20
|
+
end
|
21
|
+
it "assigns options to chef_server_url attribute" do
|
22
|
+
expect(subject.chef_server_url).to eq(opts[:chef_server_url])
|
23
|
+
end
|
24
|
+
it "assigns options to repo_root attribute" do
|
25
|
+
expect(subject.repo_root).to eq(opts[:repo_root])
|
26
|
+
end
|
27
|
+
it "assigns options to cookbook_directory attribute" do
|
28
|
+
expect(subject.cookbook_directory).to eq(opts[:cookbook_directory])
|
29
|
+
end
|
30
|
+
it "assigns options to data_bag_directory attribute" do
|
31
|
+
expect(subject.data_bag_directory).to eq(opts[:data_bag_directory])
|
32
|
+
end
|
33
|
+
it "assigns options to environment_directory attribute" do
|
34
|
+
expect(subject.environment_directory).to eq(opts[:environment_directory])
|
35
|
+
end
|
36
|
+
it "assigns options to temp_directory attribute" do
|
37
|
+
expect(subject.temp_directory).to eq(opts[:temp_directory])
|
38
|
+
end
|
39
|
+
it "can correctly convert to a hash" do
|
40
|
+
hash = subject.to_hash
|
41
|
+
expect(hash[:repo_root]).to eq(opts[:repo_root])
|
42
|
+
end
|
43
|
+
|
44
|
+
context "directories are not in options" do
|
45
|
+
let(:opts) {{
|
46
|
+
:node_name => "user",
|
47
|
+
:client_key => "key",
|
48
|
+
:chef_server_url => "url"}}
|
49
|
+
subject { Promote::Config.new(opts) }
|
50
|
+
|
51
|
+
it "assigns repo_root to pwd" do
|
52
|
+
expect(subject.repo_root).to eq(Dir.pwd)
|
53
|
+
end
|
54
|
+
it "assigns cookbook_directory to cookbooks off root" do
|
55
|
+
expect(subject.cookbook_directory).to eq(File.join(subject.repo_root, "cookbooks"))
|
56
|
+
end
|
57
|
+
it "assigns data_bag_directory to data_bags off root" do
|
58
|
+
expect(subject.data_bag_directory).to eq(File.join(subject.repo_root, "data_bags"))
|
59
|
+
end
|
60
|
+
it "assigns environment_directory to environments off root" do
|
61
|
+
expect(subject.environment_directory).to eq(File.join(subject.repo_root, "environments"))
|
62
|
+
end
|
63
|
+
it "assigns temp_directory to tmp" do
|
64
|
+
expect(subject.temp_directory).to eq("/tmp/promote")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "directories are not in options but repo root is" do
|
69
|
+
let(:opts) {{
|
70
|
+
:repo_root => "root",
|
71
|
+
:node_name => "user",
|
72
|
+
:client_key => "key",
|
73
|
+
:chef_server_url => "url"}}
|
74
|
+
subject { Promote::Config.new(opts) }
|
75
|
+
|
76
|
+
it "assigns repo_root to pwd" do
|
77
|
+
expect(subject.repo_root).to eq(opts[:repo_root])
|
78
|
+
end
|
79
|
+
it "assigns cookbook_directory to cookbooks off root" do
|
80
|
+
expect(subject.cookbook_directory).to eq(File.join(subject.repo_root, "cookbooks"))
|
81
|
+
end
|
82
|
+
it "assigns data_bag_directory to data_bags off root" do
|
83
|
+
expect(subject.data_bag_directory).to eq(File.join(subject.repo_root, "data_bags"))
|
84
|
+
end
|
85
|
+
it "assigns environment_directory to environments off root" do
|
86
|
+
expect(subject.environment_directory).to eq(File.join(subject.repo_root, "environments"))
|
87
|
+
end
|
88
|
+
it "assigns temp_directory to tmp" do
|
89
|
+
expect(subject.temp_directory).to eq("/tmp/promote")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,250 @@
|
|
1
|
+
require 'promote'
|
2
|
+
|
3
|
+
describe Promote::Cookbook do
|
4
|
+
let(:cookbook_dir) {'/tmp/promote_test_cookbooks/stubs'}
|
5
|
+
let(:config) { Promote::Config.new({
|
6
|
+
:cookbook_directory => cookbook_dir})
|
7
|
+
}
|
8
|
+
let(:fake_berks) { double('berksfile', :list => nil, :install => nil, :update => nil) }
|
9
|
+
|
10
|
+
before(:all) {
|
11
|
+
cb_dir = '/tmp/promote_test_cookbooks'
|
12
|
+
FileUtils.rm_rf(cb_dir) if Dir.exist?(cb_dir)
|
13
|
+
Dir.mkdir(cb_dir)
|
14
|
+
FileUtils.cp_r(File.join(File.dirname(File.dirname(__FILE__)), 'stubs'), cb_dir)
|
15
|
+
}
|
16
|
+
|
17
|
+
subject { Promote::Cookbook.new('cookbook_1', config) }
|
18
|
+
|
19
|
+
describe "dependencies" do
|
20
|
+
before {
|
21
|
+
allow(Berkshelf::Berksfile).to receive(:from_file).and_return(fake_berks)
|
22
|
+
allow(fake_berks).to receive(:list).and_return([
|
23
|
+
double('Dependency', :name => 'cookbook_2', :locked_version => Semverse::Version.new('1.1.1')),
|
24
|
+
double('Dependency', :name => 'cookbook_3', :locked_version => Semverse::Version.new('2.2.2')),
|
25
|
+
double('Dependency', :name => 'cookbook_4', :locked_version => Semverse::Version.new('3.3.3'))
|
26
|
+
])
|
27
|
+
}
|
28
|
+
|
29
|
+
it "returns dependencies from lockfile" do
|
30
|
+
expect(subject.dependencies.keys.count).to be 3
|
31
|
+
expect(subject.dependencies['cookbook_2'].to_s).to eq '1.1.1'
|
32
|
+
expect(subject.dependencies['cookbook_3'].to_s).to eq '2.2.2'
|
33
|
+
expect(subject.dependencies['cookbook_4'].to_s).to eq '3.3.3'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "metadata_dependencies" do
|
38
|
+
|
39
|
+
it "returns dependencies from metadata.rb" do
|
40
|
+
expect(subject.metadata_dependencies.keys.count).to be 1
|
41
|
+
expect(subject.metadata_dependencies['cookbook_2'].to_s).to eq '= 1.1.1'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "path" do
|
46
|
+
|
47
|
+
it "returns the correct path of the cookbook" do
|
48
|
+
expect(subject.path).to eq(File.join(cookbook_dir, 'cookbook_1'))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "version" do
|
53
|
+
|
54
|
+
it "reads the current version" do
|
55
|
+
expect(subject.version.to_s).to eq('1.0.0')
|
56
|
+
end
|
57
|
+
|
58
|
+
it "writes changed version to metadata.rb" do
|
59
|
+
subject.version = Semverse::Version.new('2.2.2')
|
60
|
+
expect(Promote::Cookbook.new('cookbook_1', config).version.to_s).to eq '2.2.2'
|
61
|
+
end
|
62
|
+
|
63
|
+
it "returns the new version after a version has changed" do
|
64
|
+
subject.version = Semverse::Version.new('2.2.2')
|
65
|
+
expect(subject.version.to_s).to eq('2.2.2')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "stamp_commit" do
|
70
|
+
it "writes the sha1 to the end of the metadata file" do
|
71
|
+
subject.stamp_commit('commit_1')
|
72
|
+
expect(subject.raw_metadata).to end_with "\n#sha1 'commit_1'"
|
73
|
+
end
|
74
|
+
|
75
|
+
it "does not write the sha1 m ore than once" do
|
76
|
+
subject.stamp_commit('commit_1')
|
77
|
+
subject.stamp_commit('commit_2')
|
78
|
+
expect(subject.raw_metadata).not_to include "commit_1"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "sync_berksfile" do
|
83
|
+
before {
|
84
|
+
allow(File).to receive(:exist?).with(/Berksfile$/).and_return(true)
|
85
|
+
allow(File).to receive(:exist?).with(/Berksfile.lock/).and_return(true)
|
86
|
+
}
|
87
|
+
|
88
|
+
it "Installs berks dependencies" do
|
89
|
+
dummy = double('berksfile')
|
90
|
+
expect(Berkshelf::Berksfile).to receive(:from_file).with(
|
91
|
+
File.join(subject.path, "Berksfile")).and_return(dummy)
|
92
|
+
expect(dummy).to receive(:install)
|
93
|
+
|
94
|
+
subject.sync_berksfile
|
95
|
+
end
|
96
|
+
|
97
|
+
it "Updates berks dependencies when asked to update" do
|
98
|
+
dummy = double('berksfile')
|
99
|
+
expect(Berkshelf::Berksfile).to receive(:from_file).with(
|
100
|
+
File.join(subject.path, "Berksfile")).and_return(dummy)
|
101
|
+
expect(dummy).to receive(:update)
|
102
|
+
|
103
|
+
subject.sync_berksfile(true)
|
104
|
+
end
|
105
|
+
|
106
|
+
context "update berksfile with no lock file" do
|
107
|
+
before {
|
108
|
+
allow(File).to receive(:exist?).with(/Berksfile.lock/).and_return(false)
|
109
|
+
}
|
110
|
+
|
111
|
+
it "Installs berks dependencies instead of update" do
|
112
|
+
dummy = double('berksfile')
|
113
|
+
expect(Berkshelf::Berksfile).to receive(:from_file).with(
|
114
|
+
File.join(subject.path, "Berksfile")).and_return(dummy)
|
115
|
+
expect(dummy).to receive(:install)
|
116
|
+
|
117
|
+
subject.sync_berksfile(true)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "dependencies_changed_after_update?" do
|
123
|
+
before {
|
124
|
+
allow(File).to receive(:exist?).and_return(true)
|
125
|
+
allow(Berkshelf::Berksfile).to receive(:from_file).and_return(fake_berks)
|
126
|
+
allow(fake_berks).to receive(:list).and_return([
|
127
|
+
double('Dependency', :name => 'cookbook_2', :locked_version => Semverse::Version.new('1.1.1')),
|
128
|
+
double('Dependency', :name => 'cookbook_3', :locked_version => Semverse::Version.new('2.2.2')),
|
129
|
+
double('Dependency', :name => 'cookbook_4', :locked_version => Semverse::Version.new('3.3.3'))
|
130
|
+
])
|
131
|
+
}
|
132
|
+
|
133
|
+
it "performs a berks update" do
|
134
|
+
expect(fake_berks).to receive(:update)
|
135
|
+
subject.dependencies_changed_after_update?
|
136
|
+
end
|
137
|
+
|
138
|
+
context "there is no change" do
|
139
|
+
|
140
|
+
before {
|
141
|
+
allow(fake_berks).to receive(:list).and_return(
|
142
|
+
[
|
143
|
+
double('Dependency', :name => 'cookbook_2', :locked_version => Semverse::Version.new('1.1.1')),
|
144
|
+
double('Dependency', :name => 'cookbook_3', :locked_version => Semverse::Version.new('2.2.2')),
|
145
|
+
double('Dependency', :name => 'cookbook_4', :locked_version => Semverse::Version.new('3.3.3'))
|
146
|
+
],
|
147
|
+
[
|
148
|
+
double('Dependency', :name => 'cookbook_2', :locked_version => Semverse::Version.new('1.1.1')),
|
149
|
+
double('Dependency', :name => 'cookbook_3', :locked_version => Semverse::Version.new('2.2.2')),
|
150
|
+
double('Dependency', :name => 'cookbook_4', :locked_version => Semverse::Version.new('3.3.3'))
|
151
|
+
]
|
152
|
+
)
|
153
|
+
}
|
154
|
+
|
155
|
+
it "returns false" do
|
156
|
+
expect(subject.dependencies_changed_after_update?).to be false
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "there is a version change" do
|
161
|
+
|
162
|
+
before {
|
163
|
+
allow(Berkshelf::Berksfile).to receive(:from_file).and_return(fake_berks)
|
164
|
+
allow(fake_berks).to receive(:list).and_return(
|
165
|
+
[
|
166
|
+
double('Dependency', :name => 'cookbook_2', :locked_version => Semverse::Version.new('1.1.1')),
|
167
|
+
double('Dependency', :name => 'cookbook_3', :locked_version => Semverse::Version.new('2.2.2')),
|
168
|
+
double('Dependency', :name => 'cookbook_4', :locked_version => Semverse::Version.new('3.3.3'))
|
169
|
+
],
|
170
|
+
[
|
171
|
+
double('Dependency', :name => 'cookbook_2', :locked_version => Semverse::Version.new('1.1.1')),
|
172
|
+
double('Dependency', :name => 'cookbook_3', :locked_version => Semverse::Version.new('2.2.3')),
|
173
|
+
double('Dependency', :name => 'cookbook_4', :locked_version => Semverse::Version.new('3.3.3'))
|
174
|
+
]
|
175
|
+
)
|
176
|
+
}
|
177
|
+
|
178
|
+
it "returns true" do
|
179
|
+
expect(subject.dependencies_changed_after_update?).to be true
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
context "a dependency is removed" do
|
184
|
+
|
185
|
+
before {
|
186
|
+
allow(Berkshelf::Berksfile).to receive(:from_file).and_return(fake_berks)
|
187
|
+
allow(fake_berks).to receive(:list).and_return(
|
188
|
+
[
|
189
|
+
double('Dependency', :name => 'cookbook_2', :locked_version => Semverse::Version.new('1.1.1')),
|
190
|
+
double('Dependency', :name => 'cookbook_3', :locked_version => Semverse::Version.new('2.2.2')),
|
191
|
+
double('Dependency', :name => 'cookbook_4', :locked_version => Semverse::Version.new('3.3.3'))
|
192
|
+
],
|
193
|
+
[
|
194
|
+
double('Dependency', :name => 'cookbook_2', :locked_version => Semverse::Version.new('1.1.1')),
|
195
|
+
double('Dependency', :name => 'cookbook_4', :locked_version => Semverse::Version.new('3.3.3'))
|
196
|
+
]
|
197
|
+
)
|
198
|
+
}
|
199
|
+
|
200
|
+
it "returns true" do
|
201
|
+
expect(subject.dependencies_changed_after_update?).to be true
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context "a dependency is added" do
|
206
|
+
|
207
|
+
before {
|
208
|
+
allow(Berkshelf::Berksfile).to receive(:from_file).and_return(fake_berks)
|
209
|
+
allow(fake_berks).to receive(:list).and_return(
|
210
|
+
[
|
211
|
+
double('Dependency', :name => 'cookbook_2', :locked_version => Semverse::Version.new('1.1.1')),
|
212
|
+
double('Dependency', :name => 'cookbook_3', :locked_version => Semverse::Version.new('2.2.2')),
|
213
|
+
double('Dependency', :name => 'cookbook_4', :locked_version => Semverse::Version.new('3.3.3'))
|
214
|
+
],
|
215
|
+
[
|
216
|
+
double('Dependency', :name => 'cookbook_2', :locked_version => Semverse::Version.new('1.1.1')),
|
217
|
+
double('Dependency', :name => 'cookbook_3', :locked_version => Semverse::Version.new('2.2.2')),
|
218
|
+
double('Dependency', :name => 'cookbook_4', :locked_version => Semverse::Version.new('3.3.3')),
|
219
|
+
double('Dependency', :name => 'cookbook_5', :locked_version => Semverse::Version.new('3.3.3'))
|
220
|
+
]
|
221
|
+
)
|
222
|
+
}
|
223
|
+
|
224
|
+
it "returns true" do
|
225
|
+
expect(subject.dependencies_changed_after_update?).to be true
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context "the cookbook under test has changed" do
|
230
|
+
|
231
|
+
before {
|
232
|
+
allow(Berkshelf::Berksfile).to receive(:from_file).and_return(fake_berks)
|
233
|
+
allow(fake_berks).to receive(:list).and_return(
|
234
|
+
[
|
235
|
+
double('Dependency', :name => 'cookbook_1', :locked_version => Semverse::Version.new('1.1.1')),
|
236
|
+
double('Dependency', :name => 'cookbook_3', :locked_version => Semverse::Version.new('2.2.2')),
|
237
|
+
],
|
238
|
+
[
|
239
|
+
double('Dependency', :name => 'cookbook_1', :locked_version => Semverse::Version.new('1.1.2')),
|
240
|
+
double('Dependency', :name => 'cookbook_3', :locked_version => Semverse::Version.new('2.2.2')),
|
241
|
+
]
|
242
|
+
)
|
243
|
+
}
|
244
|
+
|
245
|
+
it "returns false" do
|
246
|
+
expect(subject.dependencies_changed_after_update?).to be false
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'promote'
|
2
|
+
|
3
|
+
describe Promote::Promoter do
|
4
|
+
context "Promote one environment to another" do
|
5
|
+
let(:config) { Promote::Config.new({
|
6
|
+
:node_name => 'user',
|
7
|
+
:cookbook_directory => '/cookbooks',
|
8
|
+
:client_key => 'key',
|
9
|
+
:chef_server_url => 'https://some.chef.server'}) }
|
10
|
+
let(:fake_file){double('file')}
|
11
|
+
let(:env1) do
|
12
|
+
<<-EOS
|
13
|
+
{
|
14
|
+
"name": "QA1",
|
15
|
+
"chef_type": "environment",
|
16
|
+
"json_class": "Chef::Environment",
|
17
|
+
"cookbook_versions": {
|
18
|
+
"build-essential": "2.0.0",
|
19
|
+
"newrelic": "2.0.0",
|
20
|
+
"platform_haproxy": "2.0.0"
|
21
|
+
}
|
22
|
+
}
|
23
|
+
EOS
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:env2) do
|
27
|
+
<<-EOS
|
28
|
+
{
|
29
|
+
"name": "QA1",
|
30
|
+
"chef_type": "environment",
|
31
|
+
"json_class": "Chef::Environment",
|
32
|
+
"cookbook_versions": {
|
33
|
+
"build-essential": "1.0.0",
|
34
|
+
"platform_haproxy": "1.0.0"
|
35
|
+
}
|
36
|
+
}
|
37
|
+
EOS
|
38
|
+
end
|
39
|
+
|
40
|
+
before {
|
41
|
+
allow(File).to receive(:read).with(/env1\.json$/).and_return(env1)
|
42
|
+
allow(File).to receive(:read).with(/env2\.json$/).and_return(env2)
|
43
|
+
allow(File).to receive(:open).with(/env2\.json$/, "w").and_yield(fake_file)
|
44
|
+
}
|
45
|
+
|
46
|
+
subject { Promote::Promoter.new(config) }
|
47
|
+
|
48
|
+
it "copies the cookbook constraints" do
|
49
|
+
expect(fake_file).to receive(:<<).with(an_instance_of(String)) do |arg|
|
50
|
+
parsed = JSON.parse(arg)
|
51
|
+
expect(parsed['cookbook_versions'].length).to eq(3)
|
52
|
+
expect(parsed['cookbook_versions']['build-essential']).to eq('2.0.0')
|
53
|
+
expect(parsed['cookbook_versions']['newrelic']).to eq('2.0.0')
|
54
|
+
expect(parsed['cookbook_versions']['platform_haproxy']).to eq('2.0.0')
|
55
|
+
end
|
56
|
+
subject.promote_to("env1", "env2")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'promote'
|
2
|
+
require_relative '../support/dummy_metadata'
|
3
|
+
|
4
|
+
describe Promote::Uploader do
|
5
|
+
let(:knife_config) {{ :cookbook_path => "path"}}
|
6
|
+
let(:temp_dir) { "/tmp/berks" }
|
7
|
+
let(:config) { Promote::Config.new({
|
8
|
+
:node_name => 'user',
|
9
|
+
:cookbook_directory => '/cookbooks',
|
10
|
+
:client_key => 'key',
|
11
|
+
:temp_directory => temp_dir,
|
12
|
+
:chef_server_url => 'https://some.chef.server'}) }
|
13
|
+
|
14
|
+
subject { Promote::Uploader.new(config) }
|
15
|
+
|
16
|
+
context "upload cookbooks" do
|
17
|
+
let(:env_name) { "QA1" }
|
18
|
+
let(:metadata) { PromoteSpecs::DummyMetadata.new({'version' => "1.1.1"}) }
|
19
|
+
let(:environment) do
|
20
|
+
<<-EOS
|
21
|
+
{
|
22
|
+
"name": "#{env_name}",
|
23
|
+
"chef_type": "environment",
|
24
|
+
"json_class": "Chef::Environment",
|
25
|
+
"cookbook_versions": {
|
26
|
+
"int_cookbook1": "1.1.1",
|
27
|
+
"int_cookbook3": "1.1.1",
|
28
|
+
"cookbook3": "3.3.3"
|
29
|
+
}
|
30
|
+
}
|
31
|
+
EOS
|
32
|
+
end
|
33
|
+
let(:berks_cookbooks) {[
|
34
|
+
"/berks/cookbook1-1.1.1",
|
35
|
+
"/berks/cookbook1-1.1.2",
|
36
|
+
"/berks/cookbook2-2.2.2",
|
37
|
+
"/berks/cookbook3-3.3.3",
|
38
|
+
"/berks/cookbook4-3.3.3",
|
39
|
+
]}
|
40
|
+
let(:internal_cookbooks) {[
|
41
|
+
"/cookbooks/int_cookbook1",
|
42
|
+
"/cookbooks/int_cookbook2",
|
43
|
+
"/cookbooks/int_cookbook3",
|
44
|
+
]}
|
45
|
+
let(:server_cookbooks){{
|
46
|
+
"cookbook1" => {"versions" => [{"version" => "1.1.1"}]},
|
47
|
+
"cookbook2" => {"versions" => [{"version" => "1.1.1"}]},
|
48
|
+
"int_cookbook1" => {"versions" => [{"version" => "1.1.1"}]},
|
49
|
+
"int_cookbook2" => {"versions" => [{"version" => "2.2.2"}]}
|
50
|
+
}}
|
51
|
+
let(:rest){double('rest')}
|
52
|
+
let(:knife) { instance_double('CookbookUpload', :run => nil) }
|
53
|
+
before {
|
54
|
+
allow(File).to receive(:read).with(File.join(config.environment_directory, "#{env_name}.json")).and_return(environment)
|
55
|
+
allow(File).to receive(:read).with(/metadata\.json$/).and_return('')
|
56
|
+
allow(Chef::REST).to receive(:new).and_return(rest)
|
57
|
+
allow(rest).to receive(:get_rest).and_return(server_cookbooks)
|
58
|
+
allow(Chef::Cookbook::Metadata).to receive(:new).and_return(metadata)
|
59
|
+
allow(Chef::Knife::CookbookUpload).to receive(:new).and_return(knife)
|
60
|
+
allow(Dir).to receive(:glob).with(File.expand_path("~/.berkshelf/cookbooks/*")).and_return(berks_cookbooks)
|
61
|
+
allow(Dir).to receive(:glob).with(File.join(config.cookbook_directory, "*")).and_return(internal_cookbooks)
|
62
|
+
allow(Dir).to receive(:mkdir)
|
63
|
+
allow(Dir).to receive(:exist?).and_return(false)
|
64
|
+
allow(knife).to receive(:config).and_return(knife_config)
|
65
|
+
allow(FileUtils).to receive(:copy_entry)
|
66
|
+
allow(File).to receive(:exist?).with(/metadata\.rb$/).and_return(true)
|
67
|
+
}
|
68
|
+
|
69
|
+
it "copies updated cookbooks to temp directory" do
|
70
|
+
expect(FileUtils).not_to receive(:copy_entry).with("/berks/cookbook1-1.1.1", File.join(config.temp_directory, "cookbook1"))
|
71
|
+
expect(FileUtils).not_to receive(:copy_entry).with("/berks/cookbook1-1.1.2", File.join(config.temp_directory, "cookbook1"))
|
72
|
+
expect(FileUtils).not_to receive(:copy_entry).with("/berks/cookbook4-3.3.3", File.join(config.temp_directory, "cookbook4"))
|
73
|
+
expect(FileUtils).not_to receive(:copy_entry).with("/berks/cookbook2-2.2.2", File.join(config.temp_directory, "cookbook2"))
|
74
|
+
expect(FileUtils).to receive(:copy_entry).with("/berks/cookbook3-3.3.3", File.join(config.temp_directory, "cookbook3"))
|
75
|
+
expect(FileUtils).not_to receive(:copy_entry).with("/cookbooks/int_cookbook2", File.join(config.temp_directory, "int_cookbook2"))
|
76
|
+
expect(FileUtils).not_to receive(:copy_entry).with("/cookbooks/int_cookbook1", File.join(config.temp_directory, "int_cookbook1"))
|
77
|
+
expect(FileUtils).to receive(:copy_entry).with("/cookbooks/int_cookbook3", File.join(config.temp_directory, "int_cookbook3"))
|
78
|
+
subject.upload_cookbooks(env_name)
|
79
|
+
end
|
80
|
+
it "uploads cookbooks to chef server" do
|
81
|
+
expect(knife).to receive(:run)
|
82
|
+
|
83
|
+
subject.upload_cookbooks(env_name)
|
84
|
+
end
|
85
|
+
it "Creates temp directory" do
|
86
|
+
expect(Dir).to receive(:mkdir).with(config.temp_directory)
|
87
|
+
|
88
|
+
subject.upload_cookbooks(env_name)
|
89
|
+
end
|
90
|
+
it "uploads cookbooks from config temp path" do
|
91
|
+
subject.upload_cookbooks(env_name)
|
92
|
+
expect(knife.config[:cookbook_path]).to eq(config.temp_directory)
|
93
|
+
end
|
94
|
+
it "uploads all cookbooks" do
|
95
|
+
subject.upload_cookbooks(env_name)
|
96
|
+
expect(knife.config[:all]).to eq(true)
|
97
|
+
end
|
98
|
+
it "freezes all cookbooks" do
|
99
|
+
subject.upload_cookbooks(env_name)
|
100
|
+
expect(knife.config[:freeze]).to eq(true)
|
101
|
+
end
|
102
|
+
|
103
|
+
context "no environment is given" do
|
104
|
+
it "copies updated cookbooks and does not filter by environment" do
|
105
|
+
expect(FileUtils).not_to receive(:copy_entry).with("/berks/cookbook1-1.1.1", File.join(config.temp_directory, "cookbook1"))
|
106
|
+
expect(FileUtils).to receive(:copy_entry).with("/berks/cookbook1-1.1.2", File.join(config.temp_directory, "cookbook1"))
|
107
|
+
expect(FileUtils).to receive(:copy_entry).with("/berks/cookbook4-3.3.3", File.join(config.temp_directory, "cookbook4"))
|
108
|
+
expect(FileUtils).to receive(:copy_entry).with("/berks/cookbook2-2.2.2", File.join(config.temp_directory, "cookbook2"))
|
109
|
+
expect(FileUtils).to receive(:copy_entry).with("/berks/cookbook3-3.3.3", File.join(config.temp_directory, "cookbook3"))
|
110
|
+
expect(FileUtils).to receive(:copy_entry).with("/cookbooks/int_cookbook2", File.join(config.temp_directory, "int_cookbook2"))
|
111
|
+
expect(FileUtils).not_to receive(:copy_entry).with("/cookbooks/int_cookbook1", File.join(config.temp_directory, "int_cookbook1"))
|
112
|
+
expect(FileUtils).to receive(:copy_entry).with("/cookbooks/int_cookbook3", File.join(config.temp_directory, "int_cookbook3"))
|
113
|
+
subject.upload_cookbooks
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "temp directory already exists" do
|
118
|
+
before {allow(Dir).to receive(:exist?).and_return(true)}
|
119
|
+
|
120
|
+
it "deletes temp directory" do
|
121
|
+
expect(FileUtils).to receive(:rm_rf).with(config.temp_directory)
|
122
|
+
subject.upload_cookbooks(env_name)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "no metadata.rb file exists for cookbook" do
|
127
|
+
before {allow(File).to receive(:exist?).with(/rb$/).and_return(false)}
|
128
|
+
|
129
|
+
it "loads json instead of ruby file" do
|
130
|
+
expect(metadata).to receive(:from_json).at_least(1).times
|
131
|
+
expect(metadata).not_to receive(:from_file)
|
132
|
+
subject.upload_cookbooks(env_name)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "nothing to upload" do
|
137
|
+
let(:berks_cookbooks) {[]}
|
138
|
+
let(:internal_cookbooks) {[]}
|
139
|
+
|
140
|
+
it "does not call knife" do
|
141
|
+
expect(Chef::Knife::CookbookUpload).not_to receive(:new)
|
142
|
+
|
143
|
+
subject.upload_cookbooks(env_name)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context "config missing node_name" do
|
148
|
+
let(:config) { Promote::Config.new({
|
149
|
+
:client_key => 'key',
|
150
|
+
:chef_server_url => 'https://some.chef.server'}) }
|
151
|
+
|
152
|
+
it "raises error that node_name is missing" do
|
153
|
+
expect{subject.upload_cookbooks(env_name)}.to raise_error(/node_name/)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context "config missing client_key" do
|
158
|
+
let(:config) { Promote::Config.new({
|
159
|
+
:node_name => 'user',
|
160
|
+
:chef_server_url => 'https://some.chef.server'}) }
|
161
|
+
|
162
|
+
it "raises error that client_key is missing" do
|
163
|
+
expect{subject.upload_cookbooks(env_name)}.to raise_error(/client_key/)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context "config missing chef_server_url" do
|
168
|
+
let(:config) { Promote::Config.new({
|
169
|
+
:node_name => 'user',
|
170
|
+
:client_key => 'key'}) }
|
171
|
+
|
172
|
+
it "raises error that chef_server_url is missing" do
|
173
|
+
expect{subject.upload_cookbooks(env_name)}.to raise_error(/chef_server_url/)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context "upload json artifacts" do
|
179
|
+
context "upload an environment" do
|
180
|
+
let(:env_name) { "my_test" }
|
181
|
+
|
182
|
+
it "uploads the environment" do
|
183
|
+
expect(Chef::ChefFS::FileSystem).to receive(:copy_to).with(an_instance_of(Chef::ChefFS::FilePattern), anything(), anything(), anything(), anything()) do |arg|
|
184
|
+
expect(arg.pattern).to eq(File.join("/environments", "#{env_name}.json"))
|
185
|
+
end
|
186
|
+
subject.upload_environment(env_name)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context "upload all environments" do
|
191
|
+
it "uploads the environment" do
|
192
|
+
expect(Chef::ChefFS::FileSystem).to receive(:copy_to).with(an_instance_of(Chef::ChefFS::FilePattern), anything(), anything(), anything(), anything()) do |arg|
|
193
|
+
expect(arg.pattern).to eq(File.join("/environments/*.json"))
|
194
|
+
end
|
195
|
+
subject.upload_environments
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
context "upload data_bags" do
|
200
|
+
it "uploads the data_bags" do
|
201
|
+
expect(Chef::ChefFS::FileSystem).to receive(:copy_to).with(an_instance_of(Chef::ChefFS::FilePattern), anything(), anything(), anything(), anything()) do |arg|
|
202
|
+
expect(arg.pattern).to eq("/data_bags/**/*.json")
|
203
|
+
end
|
204
|
+
subject.upload_data_bags
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,291 @@
|
|
1
|
+
require 'promote'
|
2
|
+
require_relative '../support/dummy_log'
|
3
|
+
|
4
|
+
describe Promote::Versioner do
|
5
|
+
before(:all) {
|
6
|
+
cb_dir = '/tmp/promote_test_cookbooks'
|
7
|
+
FileUtils.rm_rf(cb_dir) if Dir.exist?(cb_dir)
|
8
|
+
Dir.mkdir(cb_dir)
|
9
|
+
FileUtils.cp_r(File.join(File.dirname(File.dirname(__FILE__)), 'stubs'), cb_dir)
|
10
|
+
}
|
11
|
+
|
12
|
+
let(:cookbook_dir) {'/tmp/promote_test_cookbooks/stubs'}
|
13
|
+
let(:test_cookbook) {'cookbook_1'}
|
14
|
+
let(:config) {Promote::Config.new(:cookbook_directory => cookbook_dir)}
|
15
|
+
let(:fake_file) { double('file') }
|
16
|
+
|
17
|
+
let(:artifact_file) { "" }
|
18
|
+
let(:fake_berks) { double('berksfile', :install => nil) }
|
19
|
+
before {
|
20
|
+
allow(Berkshelf::Berksfile).to receive(:from_file).and_return(fake_berks)
|
21
|
+
allow_any_instance_of(Promote::GitRepo).to receive(:git).and_return(Git::Base.new)
|
22
|
+
allow_any_instance_of(Git::Base).to receive(:tags).and_return([
|
23
|
+
double("tag", :name => '1.0', :sha => 'abc'),
|
24
|
+
double("tag", :name => '1.1', :sha => 'def'),
|
25
|
+
double("tag", :name => '1.2', :sha => 'ghi')
|
26
|
+
])
|
27
|
+
allow_any_instance_of(Git::Base).to receive(:log).with(10000).and_return(
|
28
|
+
PromoteSpecs::DummyLog.new([
|
29
|
+
{:msg => 'blah', :sha => 'aaa'},
|
30
|
+
{:msg => 'CI:bumping', :sha => 'bbb'},
|
31
|
+
{:msg => 'blah', :sha => 'ccc'}
|
32
|
+
]))
|
33
|
+
config = Promote::Config.new
|
34
|
+
}
|
35
|
+
|
36
|
+
subject { Promote::Versioner.new(config) }
|
37
|
+
|
38
|
+
context 'version_cookbook' do
|
39
|
+
context 'when versioning a new cookbook version' do
|
40
|
+
it "writes the new version and sha to the file" do
|
41
|
+
subject.version_cookbook(test_cookbook)
|
42
|
+
expect(Promote::Cookbook.new(test_cookbook, config).version.to_s).to eq('1.2.2')
|
43
|
+
end
|
44
|
+
|
45
|
+
it "adds the new version and sha to the cookbook" do
|
46
|
+
subject.version_cookbook(test_cookbook)
|
47
|
+
expect(Promote::Cookbook.new(test_cookbook, config).raw_metadata).to end_with("#sha1 'aaa'")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when versioning a cookbook with no changes' do
|
52
|
+
it "does not write to metadata rb" do
|
53
|
+
expect(subject.version_cookbook(test_cookbook)).to be nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'version_cookbooks' do
|
59
|
+
let(:cookbooks) {[
|
60
|
+
File.join(subject.config.cookbook_directory, "cookbook1"),
|
61
|
+
File.join(subject.config.cookbook_directory, "cookbook2"),
|
62
|
+
File.join(subject.config.cookbook_directory, "cookbook3")
|
63
|
+
]}
|
64
|
+
before {
|
65
|
+
allow(subject).to receive(:version_cookbook)
|
66
|
+
allow(Dir).to receive(:glob).with(
|
67
|
+
File.join(subject.config.cookbook_directory, "*")).and_return(cookbooks)
|
68
|
+
}
|
69
|
+
|
70
|
+
it "versions each cookbook" do
|
71
|
+
cookbooks.each do |cookbook|
|
72
|
+
expect(subject).to receive(:version_cookbook).with(File.basename(cookbook))
|
73
|
+
end
|
74
|
+
subject.version_cookbooks
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'version_environment' do
|
79
|
+
before {
|
80
|
+
regex = File.join(config.environment_directory,'test.json')
|
81
|
+
allow(File).to receive(:read).with(regex).and_return(artifact_file)
|
82
|
+
allow(File).to receive(:open).with(regex, "w").and_yield(fake_file)
|
83
|
+
}
|
84
|
+
|
85
|
+
context 'when versioning a new environment with no version' do
|
86
|
+
let(:artifact_file) do
|
87
|
+
<<-EOS
|
88
|
+
{
|
89
|
+
"name": "QA1",
|
90
|
+
"chef_type": "environment",
|
91
|
+
"json_class": "Chef::Environment",
|
92
|
+
"override_attributes": {
|
93
|
+
"environment_parent": "QA"
|
94
|
+
}
|
95
|
+
}
|
96
|
+
EOS
|
97
|
+
end
|
98
|
+
|
99
|
+
it "writes the new version and sha to the file" do
|
100
|
+
parsed = JSON.parse(artifact_file)
|
101
|
+
parsed['override_attributes']['version'] = '1.2.2'
|
102
|
+
parsed['override_attributes']['sha1'] = 'aaa'
|
103
|
+
expect(fake_file).to receive(:<<).with(JSON.pretty_generate(parsed))
|
104
|
+
subject.version_environment('test')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'when versioning a new environment with old version' do
|
109
|
+
|
110
|
+
let(:artifact_file) do
|
111
|
+
<<-EOS
|
112
|
+
{
|
113
|
+
"name": "QA1",
|
114
|
+
"chef_type": "environment",
|
115
|
+
"json_class": "Chef::Environment",
|
116
|
+
"override_attributes": {
|
117
|
+
"environment_parent": "QA",
|
118
|
+
"version": "1.0.0",
|
119
|
+
"sha1": "ccc"
|
120
|
+
}
|
121
|
+
}
|
122
|
+
EOS
|
123
|
+
end
|
124
|
+
|
125
|
+
it "writes the new version and sha to the file" do
|
126
|
+
parsed = JSON.parse(artifact_file)
|
127
|
+
parsed['override_attributes']['version'] = '1.2.2'
|
128
|
+
parsed['override_attributes']['sha1'] = 'aaa'
|
129
|
+
expect(fake_file).to receive(:<<).with(JSON.pretty_generate(parsed))
|
130
|
+
subject.version_environment('test')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context 'when commiting an environment with no changes' do
|
135
|
+
let(:artifact_file) do
|
136
|
+
<<-EOS
|
137
|
+
{
|
138
|
+
"name": "QA1",
|
139
|
+
"chef_type": "environment",
|
140
|
+
"json_class": "Chef::Environment",
|
141
|
+
"override_attributes": {
|
142
|
+
"environment_parent": "QA",
|
143
|
+
"version": "1.2.2",
|
144
|
+
"sha1": "aaa"
|
145
|
+
}
|
146
|
+
}
|
147
|
+
EOS
|
148
|
+
end
|
149
|
+
|
150
|
+
it "does not write to the file" do
|
151
|
+
expect(fake_file).not_to receive(:<<)
|
152
|
+
subject.version_environment('test')
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'version_environments' do
|
158
|
+
let(:environments) {%w{dir/environment1.json dir/environment2.json dir/environment3.json}}
|
159
|
+
before {
|
160
|
+
allow(subject).to receive(:version_environment)
|
161
|
+
allow(Dir).to receive(:glob).with(
|
162
|
+
File.join(subject.config.environment_directory, "*.json")).and_return(environments)
|
163
|
+
}
|
164
|
+
|
165
|
+
it "versions each environment" do
|
166
|
+
environments.each do |environment|
|
167
|
+
expect(subject).to receive(:version_environment).with(
|
168
|
+
File.basename(environment ,File.extname(environment)))
|
169
|
+
end
|
170
|
+
subject.version_environments
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context 'constrain_environment' do
|
175
|
+
before {
|
176
|
+
regex = File.join(config.environment_directory,'test.json')
|
177
|
+
allow(File).to receive(:read).with(regex).and_return(artifact_file)
|
178
|
+
allow(File).to receive(:open).with(regex, "w").and_yield(fake_file)
|
179
|
+
}
|
180
|
+
|
181
|
+
let(:artifact_file) do
|
182
|
+
<<-EOS
|
183
|
+
{
|
184
|
+
"name": "QA1",
|
185
|
+
"chef_type": "environment",
|
186
|
+
"json_class": "Chef::Environment"
|
187
|
+
}
|
188
|
+
EOS
|
189
|
+
end
|
190
|
+
before {
|
191
|
+
allow(File).to receive(:exist?).with(/Berksfile$/).and_return(true)
|
192
|
+
allow(fake_berks).to receive(:list).and_return([
|
193
|
+
double('Dependency', :name => 'cookbook1', :locked_version => Semverse::Version.new('1.1.1')),
|
194
|
+
double('Dependency', :name => 'cookbook2', :locked_version => Semverse::Version.new('2.2.2')),
|
195
|
+
double('Dependency', :name => 'cookbook3', :locked_version => Semverse::Version.new('3.3.3'))
|
196
|
+
])
|
197
|
+
}
|
198
|
+
it "writes the cookbook constraints to the file" do
|
199
|
+
expect(fake_file).to receive(:<<).with(an_instance_of(String)) do |arg|
|
200
|
+
parsed = JSON.parse(arg)
|
201
|
+
expect(parsed['cookbook_versions']['cookbook1']).to eq('1.1.1')
|
202
|
+
expect(parsed['cookbook_versions']['cookbook2']).to eq('2.2.2')
|
203
|
+
expect(parsed['cookbook_versions']['cookbook3']).to eq('3.3.3')
|
204
|
+
end
|
205
|
+
subject.constrain_environment('test', 'test_cookbook')
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context 'is_dirty' do
|
210
|
+
before {
|
211
|
+
regex = File.join(config.environment_directory,'test.json')
|
212
|
+
allow(File).to receive(:read).with(regex).and_return(artifact_file)
|
213
|
+
allow(File).to receive(:open).with(regex, "w").and_yield(fake_file)
|
214
|
+
allow(File).to receive(:read).with(/new.json/).and_return(
|
215
|
+
<<-EOS
|
216
|
+
{
|
217
|
+
"name": "test",
|
218
|
+
"chef_type": "environment",
|
219
|
+
"json_class": "Chef::Environment",
|
220
|
+
"cookbook_versions": {
|
221
|
+
"cookbook1": "1.1.1",
|
222
|
+
"cookbook2": "2.2.2",
|
223
|
+
"cookbook3": "3.3.3"
|
224
|
+
}
|
225
|
+
}
|
226
|
+
EOS
|
227
|
+
)
|
228
|
+
}
|
229
|
+
|
230
|
+
context "cookbook is dirty" do
|
231
|
+
|
232
|
+
let(:artifact_file) do
|
233
|
+
<<-EOS
|
234
|
+
{
|
235
|
+
"name": "QA1",
|
236
|
+
"chef_type": "environment",
|
237
|
+
"json_class": "Chef::Environment",
|
238
|
+
"cookbook_versions": {
|
239
|
+
"cookbook1": "1.1.1",
|
240
|
+
"cookbook2": "1.1.1",
|
241
|
+
"cookbook3": "3.3.3"
|
242
|
+
}
|
243
|
+
}
|
244
|
+
EOS
|
245
|
+
end
|
246
|
+
|
247
|
+
it "returns dirty" do
|
248
|
+
expect(subject.is_dirty('new', 'test', 'cookbook2')).to be(true)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
context "cookbook is not dirty" do
|
252
|
+
let(:artifact_file) do
|
253
|
+
<<-EOS
|
254
|
+
{
|
255
|
+
"name": "QA1",
|
256
|
+
"chef_type": "environment",
|
257
|
+
"json_class": "Chef::Environment",
|
258
|
+
"cookbook_versions": {
|
259
|
+
"cookbook1": "1.1.1",
|
260
|
+
"cookbook2": "2.2.2",
|
261
|
+
"cookbook3": "3.3.3"
|
262
|
+
}
|
263
|
+
}
|
264
|
+
EOS
|
265
|
+
end
|
266
|
+
|
267
|
+
it "returns clean" do
|
268
|
+
expect(subject.is_dirty('new', 'test', 'cookbook2')).to be(false)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
context "cookbook is new to environment" do
|
272
|
+
let(:artifact_file) do
|
273
|
+
<<-EOS
|
274
|
+
{
|
275
|
+
"name": "QA1",
|
276
|
+
"chef_type": "environment",
|
277
|
+
"json_class": "Chef::Environment",
|
278
|
+
"cookbook_versions": {
|
279
|
+
"cookbook1": "1.1.1",
|
280
|
+
"cookbook3": "3.3.3"
|
281
|
+
}
|
282
|
+
}
|
283
|
+
EOS
|
284
|
+
end
|
285
|
+
|
286
|
+
it "returns dirty" do
|
287
|
+
expect(subject.is_dirty('new', 'test', 'cookbook2')).to be(true)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'git'
|
2
|
+
|
3
|
+
module PromoteSpecs
|
4
|
+
|
5
|
+
class DummyLog < Git::Log
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def initialize(commits)
|
9
|
+
@commits = commits
|
10
|
+
@author = nil
|
11
|
+
@grep = nil
|
12
|
+
@object = nil
|
13
|
+
@path = nil
|
14
|
+
@since = nil
|
15
|
+
@skip = nil
|
16
|
+
@until = nil
|
17
|
+
@between = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def size
|
21
|
+
run_log.size rescue nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def first
|
25
|
+
run_log.first rescue nil
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def run_log
|
31
|
+
filtered = @commits.select { |c| @grep.nil? ? true : c[:msg] == @grep }
|
32
|
+
filtered.map { |c| DummyCommit.new(c[:sha]) }
|
33
|
+
ensure
|
34
|
+
@grep = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class DummyCommit
|
40
|
+
def initialize(sha)
|
41
|
+
@sha=sha
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_accessor :sha
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module PromoteSpecs
|
2
|
+
|
3
|
+
class DummyMetadata < Chef::Cookbook::Metadata
|
4
|
+
def initialize(hash = nil)
|
5
|
+
@hash = hash
|
6
|
+
end
|
7
|
+
|
8
|
+
def from_file(file_path)
|
9
|
+
@name = File.basename(File.dirname(file_path))
|
10
|
+
idx = @name.rindex('-')
|
11
|
+
if !idx.nil?
|
12
|
+
@version = @name[idx+1,@name.length-idx]
|
13
|
+
@name = @name[0,idx]
|
14
|
+
else
|
15
|
+
from_hash(@hash) unless @hash.nil?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clc-promote
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- CenturyLink Cloud
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clc-git
|
@@ -97,8 +97,12 @@ extensions: []
|
|
97
97
|
extra_rdoc_files:
|
98
98
|
- README.md
|
99
99
|
files:
|
100
|
+
- ".gitignore"
|
100
101
|
- README.md
|
102
|
+
- Rakefile
|
103
|
+
- clc-promote.gemspec
|
101
104
|
- lib/chef/knife/promote.rb
|
105
|
+
- lib/kitchen/provisioner/environment.rb
|
102
106
|
- lib/promote.rb
|
103
107
|
- lib/promote/config.rb
|
104
108
|
- lib/promote/cookbook.rb
|
@@ -108,7 +112,18 @@ files:
|
|
108
112
|
- lib/promote/rake_tasks.rb
|
109
113
|
- lib/promote/uploader.rb
|
110
114
|
- lib/promote/utils.rb
|
115
|
+
- lib/promote/version.rb
|
111
116
|
- lib/promote/versioner.rb
|
117
|
+
- spec/unit/kitchen/provisioner/environment_spec.rb
|
118
|
+
- spec/unit/promote/config_spec.rb
|
119
|
+
- spec/unit/promote/cookbook_spec.rb
|
120
|
+
- spec/unit/promote/promoter_spec.rb
|
121
|
+
- spec/unit/promote/uploader_spec.rb
|
122
|
+
- spec/unit/promote/versioner_spec.rb
|
123
|
+
- spec/unit/stubs/cookbook_1/Berksfile
|
124
|
+
- spec/unit/stubs/cookbook_1/metadata.rb
|
125
|
+
- spec/unit/support/dummy_log.rb
|
126
|
+
- spec/unit/support/dummy_metadata.rb
|
112
127
|
homepage: https://github.com/tier3/DevOps/gems/clc-promote
|
113
128
|
licenses: []
|
114
129
|
metadata: {}
|
@@ -132,5 +147,15 @@ rubygems_version: 2.4.1
|
|
132
147
|
signing_key:
|
133
148
|
specification_version: 4
|
134
149
|
summary: Provides versioning and promotion logic for chef artifacts.
|
135
|
-
test_files:
|
150
|
+
test_files:
|
151
|
+
- spec/unit/kitchen/provisioner/environment_spec.rb
|
152
|
+
- spec/unit/promote/config_spec.rb
|
153
|
+
- spec/unit/promote/cookbook_spec.rb
|
154
|
+
- spec/unit/promote/promoter_spec.rb
|
155
|
+
- spec/unit/promote/uploader_spec.rb
|
156
|
+
- spec/unit/promote/versioner_spec.rb
|
157
|
+
- spec/unit/stubs/cookbook_1/Berksfile
|
158
|
+
- spec/unit/stubs/cookbook_1/metadata.rb
|
159
|
+
- spec/unit/support/dummy_log.rb
|
160
|
+
- spec/unit/support/dummy_metadata.rb
|
136
161
|
has_rdoc:
|