head_chef 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +38 -0
- data/Rakefile +1 -0
- data/bin/head-chef +5 -0
- data/features/commands/diff.feature +74 -0
- data/features/commands/list.feature +20 -0
- data/features/commands/sync.feature +67 -0
- data/features/step_definitions/berkshelf_steps.rb +40 -0
- data/features/step_definitions/chef_server_steps.rb +81 -0
- data/features/step_definitions/git_steps.rb +3 -0
- data/features/support/env.rb +40 -0
- data/head_chef.gemspec +36 -0
- data/lib/head_chef/cli.rb +28 -0
- data/lib/head_chef/cookbook.rb +115 -0
- data/lib/head_chef/cookbook_diff.rb +80 -0
- data/lib/head_chef/tasks/diff.rb +42 -0
- data/lib/head_chef/tasks/env.rb +48 -0
- data/lib/head_chef/tasks/list.rb +17 -0
- data/lib/head_chef/tasks/sync.rb +38 -0
- data/lib/head_chef/tasks.rb +3 -0
- data/lib/head_chef/ui.rb +23 -0
- data/lib/head_chef/version.rb +3 -0
- data/lib/head_chef.rb +63 -0
- data/spec/cookbook_diff_spec.rb +77 -0
- data/spec/cookbook_spec.rb +60 -0
- data/spec/diff_spec.rb +38 -0
- data/spec/env_spec.rb +94 -0
- data/spec/fixtures/Berksfiles/default +3 -0
- data/spec/fixtures/Berksfiles/template.erb +5 -0
- data/spec/fixtures/cookbooks/test_cookbook/metadata.rb +2 -0
- data/spec/fixtures/cookbooks/test_cookbook/recipes/default.rb +8 -0
- data/spec/fixtures/cookbooks/test_cookbook_file_content_conflict/metadata.rb +2 -0
- data/spec/fixtures/cookbooks/test_cookbook_file_content_conflict/recipes/default.rb +10 -0
- data/spec/fixtures/cookbooks/test_cookbook_file_list_conflict/chefignore +2 -0
- data/spec/fixtures/cookbooks/test_cookbook_file_list_conflict/metadata.rb +2 -0
- data/spec/fixtures/cookbooks/test_cookbook_file_list_conflict/recipes/default.rb +10 -0
- data/spec/fixtures/cookbooks/test_cookbook_file_list_conflict/recipes/not_default.rb +1 -0
- data/spec/fixtures/dot_chef/head_chef.pem +27 -0
- data/spec/fixtures/dot_chef/knife.rb +4 -0
- data/spec/head_chef_spec.rb +79 -0
- data/spec/list_spec.rb +26 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/chef_server.rb +100 -0
- data/spec/support/path_helpers.rb +38 -0
- data/spec/sync_spec.rb +86 -0
- metadata +319 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
module HeadChef
|
2
|
+
class CookbookDiff
|
3
|
+
attr_reader :diff_hash
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@diff_hash = { add: [],
|
7
|
+
update: [],
|
8
|
+
remove: [],
|
9
|
+
revert: [],
|
10
|
+
conflict: [] }
|
11
|
+
end
|
12
|
+
|
13
|
+
# @TODO: cleanup
|
14
|
+
# @TODO: switch statements
|
15
|
+
def add(cookbook)
|
16
|
+
# Removal is the only operation that does not require a diff, as no
|
17
|
+
# cookbook will be uploaded
|
18
|
+
if cookbook.chef_version && !cookbook.berkshelf_version
|
19
|
+
@diff_hash[:remove] << cookbook and return
|
20
|
+
end
|
21
|
+
|
22
|
+
unless cookbook.diff
|
23
|
+
@diff_hash[:conflict] << cookbook and return
|
24
|
+
end
|
25
|
+
|
26
|
+
if cookbook.berkshelf_version && !cookbook.chef_version
|
27
|
+
@diff_hash[:add] << cookbook and return
|
28
|
+
end
|
29
|
+
|
30
|
+
berkshelf_version = Semantic::Version.new(cookbook.berkshelf_version)
|
31
|
+
chef_version = Semantic::Version.new(cookbook.chef_version)
|
32
|
+
|
33
|
+
if berkshelf_version > chef_version
|
34
|
+
@diff_hash[:update] << cookbook and return
|
35
|
+
elsif berkshelf_version < chef_version
|
36
|
+
@diff_hash[:revert] << cookbook and return
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def conflicts
|
41
|
+
@diff_hash[:conflict]
|
42
|
+
end
|
43
|
+
|
44
|
+
def conflicts?
|
45
|
+
!@diff_hash[:conflict].empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
def empty?
|
49
|
+
[:add, :update, :remove, :revert, :conflict].each do |method|
|
50
|
+
return false if !@diff_hash[method].empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def pretty_print
|
57
|
+
if self.empty?
|
58
|
+
HeadChef.ui.say("Berksfile and Chef environment are identical", :green)
|
59
|
+
return
|
60
|
+
end
|
61
|
+
|
62
|
+
colors = { add: :green,
|
63
|
+
update: :green,
|
64
|
+
remove: :red,
|
65
|
+
revert: :red,
|
66
|
+
conflict: :red }
|
67
|
+
|
68
|
+
[:add, :update, :remove, :revert, :conflict].each do |method|
|
69
|
+
color = colors[method]
|
70
|
+
|
71
|
+
unless @diff_hash[method].empty?
|
72
|
+
HeadChef.ui.say("#{method.to_s.upcase}:", color)
|
73
|
+
diff_hash[method].each do |cookbook|
|
74
|
+
HeadChef.ui.say(" #{cookbook.to_s}", color)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module HeadChef
|
2
|
+
class Diff
|
3
|
+
def self.diff(environment)
|
4
|
+
HeadChef.ui.info("Loading environment #{environment} from chef server...")
|
5
|
+
chef_environment = HeadChef.chef_server.environment.find(environment)
|
6
|
+
|
7
|
+
if chef_environment
|
8
|
+
chef_versions = chef_environment.cookbook_versions
|
9
|
+
else
|
10
|
+
HeadChef.ui.error("Environment #{environment} not found on chef server.")
|
11
|
+
Kernel.exit(1337)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Run berks install to populate cached cookbook list
|
15
|
+
# @NOTE: for now it is up to user to maintain Berksfile
|
16
|
+
HeadChef.ui.info('Loading cookbooks from berkshelf...')
|
17
|
+
cached_cookbooks = Berkshelf.ui.mute { HeadChef.berksfile.install }
|
18
|
+
|
19
|
+
HeadChef.ui.say('Calculating diff...', :cyan)
|
20
|
+
cookbook_diff = CookbookDiff.new
|
21
|
+
|
22
|
+
cached_cookbooks.each do |berkshelf_cookbook|
|
23
|
+
cookbook_name = berkshelf_cookbook.name.chomp("-#{berkshelf_cookbook.version}")
|
24
|
+
|
25
|
+
if chef_versions[cookbook_name]
|
26
|
+
chef_version = chef_versions[cookbook_name]
|
27
|
+
else
|
28
|
+
chef_version = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
cookbook_diff.add(Cookbook.new(cookbook_name, berkshelf_cookbook.version, chef_version))
|
32
|
+
chef_versions.delete(cookbook_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
chef_versions.each do |cookbook_name, cookbook_version|
|
36
|
+
cookbook_diff.add(Cookbook.new(cookbook_name, nil, cookbook_version))
|
37
|
+
end
|
38
|
+
|
39
|
+
cookbook_diff
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module HeadChef
|
2
|
+
class Env < Thor
|
3
|
+
class_option :environment,
|
4
|
+
aliases: '-e',
|
5
|
+
banner: '<environment>',
|
6
|
+
desc: 'Applies to the specified environment',
|
7
|
+
type: :string
|
8
|
+
|
9
|
+
desc 'diff', 'Shows cookbook diff between Berksfile and Chef <environment>'
|
10
|
+
long_desc <<-EOD
|
11
|
+
Shows cookbook version diff between Berksfile and Chef <environment>
|
12
|
+
|
13
|
+
By default, matches current git branch name against Chef environment.
|
14
|
+
EOD
|
15
|
+
def diff
|
16
|
+
environment = options[:environment] || HeadChef.current_branch
|
17
|
+
|
18
|
+
Diff.diff(environment).pretty_print
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'list', 'Lists cookbooks with versions from Chef <environment>.'
|
22
|
+
long_desc <<-EOD
|
23
|
+
Shows cookbook version diff between Berksfile and Chef <environment>
|
24
|
+
|
25
|
+
By default, matches current git branch name against Chef enviroment.
|
26
|
+
EOD
|
27
|
+
def list
|
28
|
+
environment = options[:environment] || HeadChef.current_branch
|
29
|
+
|
30
|
+
List.list(environment)
|
31
|
+
end
|
32
|
+
|
33
|
+
desc 'sync', 'Syncs Berksfile with Chef <environment>'
|
34
|
+
long_desc <<-EOD
|
35
|
+
Syncs Berksfile cookbook with Chef <environment>
|
36
|
+
|
37
|
+
By default, matches current git branch and against Chef enviroment. Chef
|
38
|
+
environment will be created if it does not exist.
|
39
|
+
EOD
|
40
|
+
option :force, banner: '', desc: 'Force upload of cookbooks to chef server'
|
41
|
+
def sync
|
42
|
+
environment = options[:environment] || HeadChef.current_branch
|
43
|
+
force = options[:force] ? true : false
|
44
|
+
|
45
|
+
Sync.sync(environment, force)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module HeadChef
|
2
|
+
class List
|
3
|
+
def self.list(environment)
|
4
|
+
chef_environment = HeadChef.chef_server.environment.find(environment)
|
5
|
+
|
6
|
+
unless chef_environment
|
7
|
+
HeadChef.ui.error "Environment #{environment} not found on chef server."
|
8
|
+
Kernel.exit(1337)
|
9
|
+
end
|
10
|
+
|
11
|
+
HeadChef.ui.say("COOKBOOKS:")
|
12
|
+
chef_environment.cookbook_versions.sort.each do |cookbook, version|
|
13
|
+
HeadChef.ui.say(" #{cookbook}: #{version}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module HeadChef
|
2
|
+
class Sync
|
3
|
+
def self.sync(environment, force)
|
4
|
+
# Check if environment exits, if not create it
|
5
|
+
# Perform first, if it fails no need to continue
|
6
|
+
unless HeadChef.chef_server.environment.find(environment)
|
7
|
+
HeadChef.chef_server.environment.create(name: environment)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Diff now performs all Berkshelf/lockfile dependency operations
|
11
|
+
HeadChef.ui.say("Determing side effects of sync with chef environment "\
|
12
|
+
"#{environment}...", :cyan)
|
13
|
+
cookbook_diff = HeadChef.ui.mute { Diff.diff(environment) }
|
14
|
+
|
15
|
+
unless force
|
16
|
+
if cookbook_diff.conflicts?
|
17
|
+
HeadChef.ui.error 'The following cookbooks are in conflict:'
|
18
|
+
cookbook_diff.conflicts.each do |cookbook|
|
19
|
+
HeadChef.ui.error "#{cookbook.name}: #{cookbook.berkshelf_version}"
|
20
|
+
end
|
21
|
+
HeadChef.ui.error 'Use --force to sync environment'
|
22
|
+
Kernel.exit(1337)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Retrieve berksfile
|
27
|
+
berksfile = HeadChef.berksfile
|
28
|
+
|
29
|
+
HeadChef.ui.say('Uploading cookbooks to chef server...', :cyan)
|
30
|
+
berksfile.upload({force: force})
|
31
|
+
|
32
|
+
# Apply without lock options argument
|
33
|
+
HeadChef.ui.say("Applying Berksfile.lock cookbook version to " \
|
34
|
+
"environment #{environment}...", :cyan)
|
35
|
+
berksfile.apply(environment, {})
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/head_chef/ui.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module HeadChef
|
2
|
+
module UI
|
3
|
+
def mute!
|
4
|
+
@mute = true
|
5
|
+
end
|
6
|
+
|
7
|
+
def unmute!
|
8
|
+
@mute = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def error(message, color = :red)
|
12
|
+
message = set_color(message, *color) if color
|
13
|
+
super(message)
|
14
|
+
end
|
15
|
+
|
16
|
+
def info(message, color = :cyan)
|
17
|
+
message = set_color(message, *color) if color
|
18
|
+
super(message)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Thor::Base.shell.send(:include, HeadChef::UI)
|
data/lib/head_chef.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# external requires
|
2
|
+
require 'berkshelf'
|
3
|
+
require 'ridley'
|
4
|
+
require 'grit'
|
5
|
+
require 'thor'
|
6
|
+
require 'semantic'
|
7
|
+
require 'pathname'
|
8
|
+
|
9
|
+
# internal requires
|
10
|
+
require_relative 'head_chef/tasks'
|
11
|
+
require_relative 'head_chef/cookbook'
|
12
|
+
require_relative 'head_chef/cookbook_diff'
|
13
|
+
require_relative 'head_chef/ui'
|
14
|
+
require_relative 'head_chef/version'
|
15
|
+
|
16
|
+
#@TODO: establish head_chef exit codes
|
17
|
+
#Create custom errors
|
18
|
+
module HeadChef
|
19
|
+
|
20
|
+
BERKSFILE_LOCATION = 'Berksfile'.freeze
|
21
|
+
BERKSFILE_COOKBOOK_DIR = '.head_chef'.freeze
|
22
|
+
|
23
|
+
class << self
|
24
|
+
def root
|
25
|
+
@root ||= Pathname.new(File.expand_path('../', File.dirname(__FILE__)))
|
26
|
+
end
|
27
|
+
|
28
|
+
def ui
|
29
|
+
@ui ||= Thor::Base.shell.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def chef_server
|
33
|
+
@chef_server ||= Ridley.from_chef_config()
|
34
|
+
end
|
35
|
+
|
36
|
+
# @TODO: refactor?
|
37
|
+
# Is grit necessary to get current branch, is shell command sufficient?
|
38
|
+
# This can look up dir's until it finds .git dir
|
39
|
+
def master_cookbook
|
40
|
+
begin
|
41
|
+
@master_cookbook ||= Grit::Repo.new('.')
|
42
|
+
rescue Grit::InvalidGitRepositoryError
|
43
|
+
puts Dir.pwd
|
44
|
+
HeadChef.ui.error 'head_chef must be run in root of git repo'
|
45
|
+
Kernel.exit(1337)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def current_branch
|
50
|
+
master_cookbook.head.name
|
51
|
+
end
|
52
|
+
|
53
|
+
def berksfile
|
54
|
+
@berksfile ||= Berkshelf::Berksfile.from_file(BERKSFILE_LOCATION)
|
55
|
+
end
|
56
|
+
|
57
|
+
def cleanup
|
58
|
+
if Dir.exists?(BERKSFILE_COOKBOOK_DIR)
|
59
|
+
FileUtils.rm_rf(BERKSFILE_COOKBOOK_DIR)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HeadChef::CookbookDiff do
|
4
|
+
let(:empty_diff_hash) do
|
5
|
+
{ add: [],
|
6
|
+
update: [],
|
7
|
+
remove: [],
|
8
|
+
revert: [],
|
9
|
+
conflict: [] }
|
10
|
+
end
|
11
|
+
|
12
|
+
shared_examples_for 'correct add to CookbookDiff' do |cookbook, method|
|
13
|
+
it "appends #{cookbook.name} cookbook to :#{method} list" do
|
14
|
+
subject.add(cookbook)
|
15
|
+
expect(subject.diff_hash[method]).to include(cookbook)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'ClassMethods' do
|
20
|
+
describe '#new' do
|
21
|
+
it 'initializes hash' do
|
22
|
+
expect(subject.diff_hash).to eq(empty_diff_hash)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'InstanceMethods' do
|
28
|
+
describe '#add(HeadChef::Cookbook)' do
|
29
|
+
|
30
|
+
context 'with cookbook content conflict' do
|
31
|
+
before(:each) do
|
32
|
+
HeadChef::Cookbook.any_instance.stub(:diff).and_return(false)
|
33
|
+
end
|
34
|
+
|
35
|
+
it_should_behave_like 'correct add to CookbookDiff',
|
36
|
+
HeadChef::Cookbook.new('add_test', '0.0.1', nil), :conflict
|
37
|
+
|
38
|
+
it_should_behave_like 'correct add to CookbookDiff',
|
39
|
+
HeadChef::Cookbook.new('update_test', '0.0.2', '0.0.1'), :conflict
|
40
|
+
|
41
|
+
it_should_behave_like 'correct add to CookbookDiff',
|
42
|
+
HeadChef::Cookbook.new('remove_test', nil, '0.0.1'), :remove
|
43
|
+
|
44
|
+
it_should_behave_like 'correct add to CookbookDiff',
|
45
|
+
HeadChef::Cookbook.new('revert_test', '0.0.1', '0.0.2'), :conflict
|
46
|
+
|
47
|
+
it_should_behave_like 'correct add to CookbookDiff',
|
48
|
+
HeadChef::Cookbook.new('conflict_test', '0.0.1', '0.0.1'), :conflict
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'without cookbook content conflict' do
|
52
|
+
before(:each) do
|
53
|
+
HeadChef::Cookbook.any_instance.stub(:diff).and_return(true)
|
54
|
+
end
|
55
|
+
|
56
|
+
it_should_behave_like 'correct add to CookbookDiff',
|
57
|
+
HeadChef::Cookbook.new('add_test', '0.0.1', nil), :add
|
58
|
+
|
59
|
+
it_should_behave_like 'correct add to CookbookDiff',
|
60
|
+
HeadChef::Cookbook.new('update_test', '0.0.2', '0.0.1'), :update
|
61
|
+
|
62
|
+
it_should_behave_like 'correct add to CookbookDiff',
|
63
|
+
HeadChef::Cookbook.new('remove_test', nil, '0.0.1'), :remove
|
64
|
+
|
65
|
+
it_should_behave_like 'correct add to CookbookDiff',
|
66
|
+
HeadChef::Cookbook.new('revert_test', '0.0.1', '0.0.2'), :revert
|
67
|
+
|
68
|
+
it 'does nothing when berkshelf version == chef version' do
|
69
|
+
cookbook = HeadChef::Cookbook.new('conflict_test', '0.0.1', '0.0.1')
|
70
|
+
subject.add(cookbook)
|
71
|
+
expect(subject.diff_hash).to eq(empty_diff_hash)
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HeadChef::Cookbook do
|
4
|
+
let(:cookbook_resource) { double('Ridley::CookbookResouce') }
|
5
|
+
let(:cached_cookbooks) { [] }
|
6
|
+
let(:cached_cookbook) { double('Berkshelf::CachedCookbook') }
|
7
|
+
|
8
|
+
subject { HeadChef::Cookbook.new('test', '0.0.1', '0.0.2') }
|
9
|
+
|
10
|
+
describe 'ClassMethods' do
|
11
|
+
describe '::new' do
|
12
|
+
it 'reads name' do
|
13
|
+
expect(subject.name).to eq('test')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'reads berkshelf version' do
|
17
|
+
expect(subject.berkshelf_version).to eq('0.0.1')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'reads chef version' do
|
21
|
+
expect(subject.chef_version).to eq('0.0.2')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'InstanceMethods' do
|
27
|
+
describe '#diff' do
|
28
|
+
before(:each) do
|
29
|
+
HeadChef.stub_chain(:chef_server, :cookbook).
|
30
|
+
and_return(cookbook_resource)
|
31
|
+
|
32
|
+
allow(cookbook_resource).to receive(:find).
|
33
|
+
with(subject.name, subject.berkshelf_version).
|
34
|
+
and_return(cookbook_resource)
|
35
|
+
allow(cookbook_resource).to receive(:manifest).and_return(Hashie::Mash.new)
|
36
|
+
|
37
|
+
HeadChef.stub_chain(:berksfile, :cached_cookbooks).
|
38
|
+
and_return(cached_cookbooks)
|
39
|
+
allow(cached_cookbooks).to receive(:find).and_return(cached_cookbook)
|
40
|
+
|
41
|
+
allow(cached_cookbook).to receive(:path)
|
42
|
+
allow(subject).to receive(:remove_ignored_files).and_return([])
|
43
|
+
end
|
44
|
+
|
45
|
+
after(:each) do
|
46
|
+
subject.diff
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'retrieves cookbook checksums from chef server' do
|
50
|
+
expect(cookbook_resource).to receive(:find).
|
51
|
+
with(subject.name, subject.berkshelf_version)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'loads cookbook from berkshelf cache' do
|
55
|
+
expect(cached_cookbooks).to receive(:find)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/spec/diff_spec.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HeadChef::Diff do
|
4
|
+
describe 'ClassMethods' do
|
5
|
+
let(:berksfile) { double('Berkshelf::Berksfile') }
|
6
|
+
let(:chef_environment) { double('Hashie::Mash') }
|
7
|
+
let(:environment) { 'test_env' }
|
8
|
+
|
9
|
+
describe '::diff' do
|
10
|
+
before(:each) do
|
11
|
+
HeadChef.stub_chain(:chef_server, :environment, :find).
|
12
|
+
with(environment).
|
13
|
+
and_return(chef_environment)
|
14
|
+
|
15
|
+
allow(chef_environment).to receive(:cookbook_versions).and_return({})
|
16
|
+
|
17
|
+
allow(HeadChef).to receive(:berksfile).and_return(berksfile)
|
18
|
+
allow(berksfile).to receive(:install).and_return([])
|
19
|
+
end
|
20
|
+
|
21
|
+
after(:each) do
|
22
|
+
described_class.diff(environment)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'loads chef environment' do
|
26
|
+
expect(chef_environment).to receive(:cookbook_versions)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'calls Berksfile#install to load berkshelf cookbooks into cache' do
|
30
|
+
expect(berksfile).to receive(:install)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'returns CookbookDiff' do
|
34
|
+
expect(described_class.diff(environment)).to be_an_instance_of(HeadChef::CookbookDiff)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/spec/env_spec.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HeadChef::Env do
|
4
|
+
let(:current_branch) { 'test_branch' }
|
5
|
+
let(:environment) { 'option_environment' }
|
6
|
+
|
7
|
+
shared_examples_for "HeadChef::Env command" do |klass, method, return_value|
|
8
|
+
|
9
|
+
context 'defaults' do
|
10
|
+
it 'uses branch name for environment' do
|
11
|
+
expect(klass).to receive(method) do |*args|
|
12
|
+
args[0].should eq(current_branch)
|
13
|
+
end.and_return(return_value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'with --environment' do
|
18
|
+
it 'uses environment option value' do
|
19
|
+
subject.options[:environment] = environment
|
20
|
+
|
21
|
+
expect(klass).to receive(method) do |*args|
|
22
|
+
args[0].should eq(environment)
|
23
|
+
end.and_return(return_value)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'commands' do
|
29
|
+
|
30
|
+
before(:each) do
|
31
|
+
allow(HeadChef).to receive(:current_branch).and_return(current_branch)
|
32
|
+
|
33
|
+
# Unfreeze Thor::CoreExt::HashWithIndifferentAccess
|
34
|
+
subject.options = subject.options.dup
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
describe '::diff' do
|
39
|
+
let(:cookbook_diff) { HeadChef::CookbookDiff.new }
|
40
|
+
|
41
|
+
after(:each) do
|
42
|
+
subject.diff
|
43
|
+
end
|
44
|
+
|
45
|
+
it_should_behave_like 'HeadChef::Env command',
|
46
|
+
HeadChef::Diff, :diff, HeadChef::CookbookDiff.new
|
47
|
+
|
48
|
+
context 'defaults' do
|
49
|
+
it 'outputs CookbookDiff' do
|
50
|
+
allow(HeadChef::Diff).to receive(:diff).
|
51
|
+
with(any_args).and_return(cookbook_diff)
|
52
|
+
|
53
|
+
expect(cookbook_diff).to receive(:pretty_print)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '::list' do
|
59
|
+
after(:each) do
|
60
|
+
subject.list
|
61
|
+
end
|
62
|
+
|
63
|
+
it_should_behave_like 'HeadChef::Env command',
|
64
|
+
HeadChef::List, :list, nil
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '::sync' do
|
68
|
+
after(:each) do
|
69
|
+
subject.sync
|
70
|
+
end
|
71
|
+
|
72
|
+
it_should_behave_like 'HeadChef::Env command',
|
73
|
+
HeadChef::Sync, :sync, nil
|
74
|
+
|
75
|
+
context 'defaults' do
|
76
|
+
it 'uses false for force option' do
|
77
|
+
expect(HeadChef::Sync).to receive(:sync) do |*args|
|
78
|
+
args[1].should eq(false)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'with --force' do
|
84
|
+
it 'sets force option to true' do
|
85
|
+
subject.options[:force] = true
|
86
|
+
|
87
|
+
expect(HeadChef::Sync).to receive(:sync) do |*args|
|
88
|
+
args[1].should eq(true)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
### DIFFERENT FILE
|