knife-spork 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +151 -0
- data/Rakefile +122 -0
- data/knife-spork.gemspec +65 -0
- data/lib/chef/knife/spork-bump.rb +163 -0
- data/lib/chef/knife/spork-check.rb +123 -0
- data/lib/chef/knife/spork-promote.rb +228 -0
- data/lib/chef/knife/spork-upload.rb +227 -0
- data/lib/knife-spork.rb +3 -0
- metadata +76 -0
data/README.md
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
# knife-spork
|
2
|
+
|
3
|
+
A workflow plugin for Chef::Knife which helps multiple devs work on the same chef server and repo without treading on eachothers toes. This plugin was designed around the workflow we have here at Etsy, where several people are working on the chef repo and chef server at the same time. It contains several functions, documented below:
|
4
|
+
|
5
|
+
# Spork Check
|
6
|
+
|
7
|
+
This function is designed to help you avoid trampling on other people's cookbook versions, and to make sure that when you come to version your own work it's easy to see what version numbers have already been used and if the one you're using will overwrite anything.
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
````
|
12
|
+
knife spork check COOKBOOK
|
13
|
+
````
|
14
|
+
|
15
|
+
## Example (Checking an Unfrozen Cookbook with version clash)
|
16
|
+
|
17
|
+
````
|
18
|
+
$ knife spork check apache
|
19
|
+
Checking versions for cookbook apache...
|
20
|
+
|
21
|
+
Current local version: 0.1.0
|
22
|
+
|
23
|
+
Remote versions:
|
24
|
+
*0.1.0, unfrozen
|
25
|
+
0.0.0, unfrozen
|
26
|
+
|
27
|
+
DANGER: Your local cookbook version number clashes with an unfrozen remote version.
|
28
|
+
|
29
|
+
If you upload now, you'll overwrite it.
|
30
|
+
````
|
31
|
+
|
32
|
+
## Example (Checking a Frozen Cookbook with version clash)
|
33
|
+
|
34
|
+
````
|
35
|
+
$ knife spork check apache2
|
36
|
+
Checking versions for cookbook apache2...
|
37
|
+
|
38
|
+
Current local version: 1.0.6
|
39
|
+
|
40
|
+
Remote versions:
|
41
|
+
*1.0.6, frozen
|
42
|
+
1.0.5, frozen
|
43
|
+
1.0.4, frozen
|
44
|
+
1.0.3, frozen
|
45
|
+
1.0.2, frozen
|
46
|
+
1.0.1, frozen
|
47
|
+
1.0.0, frozen
|
48
|
+
|
49
|
+
DANGER: Your local cookbook has same version number as the starred version above!
|
50
|
+
|
51
|
+
Please bump your local version or you won't be able to upload.
|
52
|
+
````
|
53
|
+
|
54
|
+
## Example (No version clashes)
|
55
|
+
|
56
|
+
````
|
57
|
+
$ knife spork check apache2
|
58
|
+
Checking versions for cookbook apache2...
|
59
|
+
|
60
|
+
Current local version: 1.0.7
|
61
|
+
|
62
|
+
Remote versions:
|
63
|
+
1.0.6, frozen
|
64
|
+
1.0.5, frozen
|
65
|
+
1.0.4, frozen
|
66
|
+
1.0.3, frozen
|
67
|
+
1.0.2, frozen
|
68
|
+
1.0.1, frozen
|
69
|
+
1.0.0, frozen
|
70
|
+
|
71
|
+
Everything looks fine, no version clashes. You can upload!
|
72
|
+
````
|
73
|
+
|
74
|
+
# Spork Bump
|
75
|
+
|
76
|
+
This function lets you easily version your cookbooks without having to manually edit the cookbook's metadata.rb file. You can either specify the version level you'd like to bump (major, minor or patch), or you can manually specify a version number. This might be used if, for example, you want to jump several version numbers in one go and don't want to have to run knife bump once for each number.
|
77
|
+
|
78
|
+
## Usage
|
79
|
+
|
80
|
+
````
|
81
|
+
knife bump COOKBOOK <MAJOR | MINOR | PATCH | MANUAL x.x.x>
|
82
|
+
|
83
|
+
````
|
84
|
+
|
85
|
+
## Example (Bumping patch level)
|
86
|
+
|
87
|
+
````
|
88
|
+
$ knife spork bump apache2 patch
|
89
|
+
Bumping patch level of the apache2 cookbook from 1.0.6 to 1.0.7
|
90
|
+
````
|
91
|
+
|
92
|
+
## Example (Manually setting version)
|
93
|
+
|
94
|
+
````
|
95
|
+
$ knife spork bump apache2 manual 1.0.13
|
96
|
+
Manually bumped version of the apache2 cookbook from 1.0.7 to 1.0.13
|
97
|
+
````
|
98
|
+
|
99
|
+
#Spork Upload
|
100
|
+
|
101
|
+
This function works mostly the same as normal "knife cookbook upload" except that this version automatically freezes cookbooks when you upload them. If you don't want to have to remember to add "--freeze" to your "knife cookbook upload" commands, then use this version.
|
102
|
+
|
103
|
+
## Usage
|
104
|
+
|
105
|
+
````
|
106
|
+
knife spork upload COOKBOOK
|
107
|
+
````
|
108
|
+
|
109
|
+
## Example
|
110
|
+
|
111
|
+
````
|
112
|
+
$ knife spork upload apache
|
113
|
+
|
114
|
+
Uploading and freezing apache [1.0.6]
|
115
|
+
upload complete
|
116
|
+
````
|
117
|
+
|
118
|
+
# Spork Promote
|
119
|
+
|
120
|
+
This function lets you easily set a version constraint in an environment for a particular cookbook. By default it will set the version constraint to whatever the local version of the specified cookbook is. Optionally, you can include a --version option which will set the version constraint for the specified cookbook to whatever version number you provide. You might want to use this if, for example, you pushed a version constraint for a cookbook version you don't want your nodes to use anymore, so you want to "roll back" the environment to a previous version. You can also specify the --remote option if you'd like to automatically upload your changed local environment file to the server.
|
121
|
+
|
122
|
+
## Usage
|
123
|
+
|
124
|
+
````
|
125
|
+
knife spork promote ENVIRONMENT COOKBOOK (OPTIONS: --version, --remote)
|
126
|
+
````
|
127
|
+
|
128
|
+
## Example (Using local Cookbook version number, into environment "foo", uploading to chef server)
|
129
|
+
|
130
|
+
````
|
131
|
+
$ knife spork promote foo php --remote
|
132
|
+
Adding version constraint php = 0.1.0
|
133
|
+
|
134
|
+
Saving changes into foo.json
|
135
|
+
|
136
|
+
Uploading foo to server
|
137
|
+
|
138
|
+
Promotion complete! Please remember to upload your changed Environment file to the Chef Server.
|
139
|
+
````
|
140
|
+
|
141
|
+
## Example (Using manual version, into environment "foo", saving to local environment file only)
|
142
|
+
|
143
|
+
````
|
144
|
+
$ knife spork promote foo php -v 1.0.6
|
145
|
+
Adding version constraint php = 1.0.6
|
146
|
+
|
147
|
+
Saving changes into foo.json
|
148
|
+
|
149
|
+
Promotion complete! Please remember to upload your changed Environment file to the Chef Server.
|
150
|
+
````
|
151
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
#############################################################################
|
6
|
+
#
|
7
|
+
# Helper functions
|
8
|
+
#
|
9
|
+
#############################################################################
|
10
|
+
|
11
|
+
def name
|
12
|
+
@name ||= Dir['*.gemspec'].first.split('.').first
|
13
|
+
end
|
14
|
+
|
15
|
+
def version
|
16
|
+
line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
|
17
|
+
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
18
|
+
end
|
19
|
+
|
20
|
+
def date
|
21
|
+
Date.today.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def rubyforge_project
|
25
|
+
name
|
26
|
+
end
|
27
|
+
|
28
|
+
def gemspec_file
|
29
|
+
"#{name}.gemspec"
|
30
|
+
end
|
31
|
+
|
32
|
+
def gem_file
|
33
|
+
"#{name}-#{version}.gem"
|
34
|
+
end
|
35
|
+
|
36
|
+
def replace_header(head, header_name)
|
37
|
+
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
|
38
|
+
end
|
39
|
+
|
40
|
+
#############################################################################
|
41
|
+
#
|
42
|
+
# Standard tasks
|
43
|
+
#
|
44
|
+
#############################################################################
|
45
|
+
|
46
|
+
task :default => :validate
|
47
|
+
|
48
|
+
desc "Open an irb session preloaded with this library"
|
49
|
+
task :console do
|
50
|
+
sh "irb -rubygems -r ./lib/#{name}.rb"
|
51
|
+
end
|
52
|
+
|
53
|
+
#############################################################################
|
54
|
+
#
|
55
|
+
# Custom tasks (add your own tasks here)
|
56
|
+
#
|
57
|
+
#############################################################################
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
#############################################################################
|
62
|
+
#
|
63
|
+
# Packaging tasks
|
64
|
+
#
|
65
|
+
#############################################################################
|
66
|
+
|
67
|
+
desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
|
68
|
+
task :release => :build do
|
69
|
+
unless `git branch` =~ /^\* master$/
|
70
|
+
puts "You must be on the master branch to release!"
|
71
|
+
exit!
|
72
|
+
end
|
73
|
+
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
74
|
+
sh "git tag v#{version}"
|
75
|
+
sh "git push origin master"
|
76
|
+
sh "git push origin v#{version}"
|
77
|
+
sh "gem push pkg/#{name}-#{version}.gem"
|
78
|
+
end
|
79
|
+
|
80
|
+
desc "Build #{gem_file} into the pkg directory"
|
81
|
+
task :build => :gemspec do
|
82
|
+
sh "mkdir -p pkg"
|
83
|
+
sh "gem build #{gemspec_file}"
|
84
|
+
sh "mv #{gem_file} pkg"
|
85
|
+
end
|
86
|
+
|
87
|
+
desc "Generate #{gemspec_file}"
|
88
|
+
task :gemspec => :validate do
|
89
|
+
# read spec file and split out manifest section
|
90
|
+
spec = File.read(gemspec_file)
|
91
|
+
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
92
|
+
|
93
|
+
# replace name version and date
|
94
|
+
replace_header(head, :name)
|
95
|
+
replace_header(head, :version)
|
96
|
+
replace_header(head, :date)
|
97
|
+
#comment this out if your rubyforge_project has a different name
|
98
|
+
replace_header(head, :rubyforge_project)
|
99
|
+
|
100
|
+
# determine file list from git ls-files
|
101
|
+
files = `git ls-files`.
|
102
|
+
split("\n").
|
103
|
+
sort.
|
104
|
+
reject { |file| file =~ /^\./ }.
|
105
|
+
reject { |file| file =~ /^(rdoc|pkg)/ }.
|
106
|
+
map { |file| " #{file}" }.
|
107
|
+
join("\n")
|
108
|
+
|
109
|
+
# piece file back together and write
|
110
|
+
manifest = " s.files = %w[\n#{files}\n ]\n"
|
111
|
+
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
|
112
|
+
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
113
|
+
puts "Updated #{gemspec_file}"
|
114
|
+
end
|
115
|
+
|
116
|
+
desc "Validate #{gemspec_file}"
|
117
|
+
task :validate do
|
118
|
+
unless Dir['VERSION*'].empty?
|
119
|
+
puts "A `VERSION` file at root level violates Gem best practices."
|
120
|
+
exit!
|
121
|
+
end
|
122
|
+
end
|
data/knife-spork.gemspec
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
## This is the rakegem gemspec template. Make sure you read and understand
|
2
|
+
## all of the comments. Some sections require modification, and others can
|
3
|
+
## be deleted if you don't need them. Once you understand the contents of
|
4
|
+
## this file, feel free to delete any comments that begin with two hash marks.
|
5
|
+
## You can find comprehensive Gem::Specification documentation, at
|
6
|
+
## http://docs.rubygems.org/read/chapter/20
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
9
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
10
|
+
s.rubygems_version = '1.3.5'
|
11
|
+
|
12
|
+
## Leave these as is they will be modified for you by the rake gemspec task.
|
13
|
+
## If your rubyforge_project name is different, then edit it and comment out
|
14
|
+
## the sub! line in the Rakefile
|
15
|
+
s.name = 'knife-spork'
|
16
|
+
s.version = '0.1.0'
|
17
|
+
s.date = '2012-01-28'
|
18
|
+
s.rubyforge_project = 'knife-spork'
|
19
|
+
|
20
|
+
## Make sure your summary is short. The description may be as long
|
21
|
+
## as you like.
|
22
|
+
s.summary = "A workflow plugin to help many devs work with the same chef repo/server"
|
23
|
+
s.description = "A workflow plugin to help many devs work with the same chef repo/server"
|
24
|
+
|
25
|
+
## List the primary authors. If there are a bunch of authors, it's probably
|
26
|
+
## better to set the email to an email list or something. If you don't have
|
27
|
+
## a custom homepage, consider using your GitHub URL or the like.
|
28
|
+
s.authors = ["Jon Cowie"]
|
29
|
+
s.email = 'jonlives@gmail.com'
|
30
|
+
s.homepage = 'https://github.com/jonlives/knife-spork'
|
31
|
+
|
32
|
+
## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
|
33
|
+
## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
|
34
|
+
s.require_paths = %w[lib]
|
35
|
+
|
36
|
+
## Specify any RDoc options here. You'll want to add your README and
|
37
|
+
## LICENSE files to the extra_rdoc_files list.
|
38
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
39
|
+
s.extra_rdoc_files = %w[README.md]
|
40
|
+
|
41
|
+
## List your runtime dependencies here. Runtime dependencies are those
|
42
|
+
## that are needed for an end user to actually USE your code.
|
43
|
+
s.add_dependency('chef', [">= 0.10.4"])
|
44
|
+
s.add_dependency('git', [">= 1.2.5"])
|
45
|
+
|
46
|
+
## Leave this section as-is. It will be automatically generated from the
|
47
|
+
## contents of your Git repository via the gemspec task. DO NOT REMOVE
|
48
|
+
## THE MANIFEST COMMENTS, they are used as delimiters by the task.
|
49
|
+
# = MANIFEST =
|
50
|
+
s.files = %w[
|
51
|
+
README.md
|
52
|
+
Rakefile
|
53
|
+
knife-spork.gemspec
|
54
|
+
lib/chef/knife/spork-bump.rb
|
55
|
+
lib/chef/knife/spork-check.rb
|
56
|
+
lib/chef/knife/spork-promote.rb
|
57
|
+
lib/chef/knife/spork-upload.rb
|
58
|
+
lib/knife-spork.rb
|
59
|
+
]
|
60
|
+
# = MANIFEST =
|
61
|
+
|
62
|
+
## Test files will be grabbed from the file list. Make sure the path glob
|
63
|
+
## matches what you actually use.
|
64
|
+
s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ }
|
65
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
#
|
2
|
+
# Modifying Author:: Jon Cowie (<jonlives@gmail.com>)
|
3
|
+
# Copyright:: Copyright (c) 2011 Jon Cowie
|
4
|
+
# License:: GPL
|
5
|
+
|
6
|
+
# Based on the knife-cookbook-bump plugin by:
|
7
|
+
# Alalanta (no license specified)
|
8
|
+
|
9
|
+
require 'chef/knife'
|
10
|
+
require 'chef/cookbook_loader'
|
11
|
+
require 'chef/cookbook_uploader'
|
12
|
+
|
13
|
+
module KnifeSpork
|
14
|
+
class SporkBump < Chef::Knife
|
15
|
+
|
16
|
+
TYPE_INDEX = { "major" => 0, "minor" => 1, "patch" => 2, "manual" => 3 }
|
17
|
+
|
18
|
+
banner "knife spork bump COOKBOOK [MAJOR|MINOR|PATCH|MANUAL]"
|
19
|
+
|
20
|
+
@@gitavail = true
|
21
|
+
deps do
|
22
|
+
begin
|
23
|
+
require "git"
|
24
|
+
rescue LoadError
|
25
|
+
@@gitavail = false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def run
|
30
|
+
|
31
|
+
bump_type=""
|
32
|
+
|
33
|
+
self.config = Chef::Config.merge!(config)
|
34
|
+
if config.has_key?(:cookbook_path)
|
35
|
+
cookbook_path = config["cookbook_path"]
|
36
|
+
else
|
37
|
+
ui.fatal "No default cookbook_path; Specify with -o or fix your knife.rb."
|
38
|
+
show_usage
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
|
42
|
+
if name_args.size == 0
|
43
|
+
show_usage
|
44
|
+
exit 0
|
45
|
+
end
|
46
|
+
|
47
|
+
if name_args.size == 3
|
48
|
+
bump_type = name_args[1]
|
49
|
+
elsif name_args.size == 2
|
50
|
+
bump_type = name_args.last
|
51
|
+
else
|
52
|
+
ui.fatal "Please specify the cookbook whose version you which to bump, and the type of bump you wish to apply."
|
53
|
+
show_usage
|
54
|
+
exit 1
|
55
|
+
end
|
56
|
+
|
57
|
+
unless TYPE_INDEX.has_key?(bump_type)
|
58
|
+
ui.fatal "Sorry, '#{name_args.last}' isn't a valid bump type. Specify one of 'major', 'minor', 'patch' or 'manual'"
|
59
|
+
show_usage
|
60
|
+
exit 1
|
61
|
+
end
|
62
|
+
|
63
|
+
if bump_type == "manual"
|
64
|
+
manual_version = name_args.last
|
65
|
+
cookbook = name_args.first
|
66
|
+
cookbook_path = Array(config[:cookbook_path]).first
|
67
|
+
patch_type = "manual"
|
68
|
+
patch_manual(cookbook_path, cookbook, manual_version)
|
69
|
+
else
|
70
|
+
cookbook = name_args.first
|
71
|
+
patch_type = name_args.last
|
72
|
+
cookbook_path = Array(config[:cookbook_path]).first
|
73
|
+
patch(cookbook_path, cookbook, patch_type)
|
74
|
+
end
|
75
|
+
|
76
|
+
if !@@gitavail
|
77
|
+
ui.msg "Git gem not available, skipping git add.\n\n"
|
78
|
+
else
|
79
|
+
git_add(cookbook)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def patch(cookbook_path, cookbook, type)
|
86
|
+
t = TYPE_INDEX[type]
|
87
|
+
current_version = get_version(cookbook_path, cookbook).split(".").map{|i| i.to_i}
|
88
|
+
bumped_version = current_version.clone
|
89
|
+
bumped_version[t] = bumped_version[t] + 1
|
90
|
+
while t < 2
|
91
|
+
t+=1
|
92
|
+
bumped_version[t] = 0
|
93
|
+
end
|
94
|
+
metadata_file = File.join(cookbook_path, cookbook, "metadata.rb")
|
95
|
+
old_version = current_version.join('.')
|
96
|
+
new_version = bumped_version.join('.')
|
97
|
+
update_metadata(old_version, new_version, metadata_file)
|
98
|
+
ui.msg("Bumping #{type} level of the #{cookbook} cookbook from #{old_version} to #{new_version}\n\n")
|
99
|
+
end
|
100
|
+
|
101
|
+
def patch_manual(cookbook_path, cookbook, version)
|
102
|
+
current_version = get_version(cookbook_path, cookbook)
|
103
|
+
v = version.split(".")
|
104
|
+
if v.size < 3 or v.size > 3
|
105
|
+
ui.msg "That isn't a valid version number to bump to."
|
106
|
+
exit 1
|
107
|
+
end
|
108
|
+
|
109
|
+
v.each do |v_comp|
|
110
|
+
if !v_comp.is_i?
|
111
|
+
ui.msg "That isn't a valid version number to bump to."
|
112
|
+
exit 1
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
metadata_file = File.join(cookbook_path, cookbook, "metadata.rb")
|
117
|
+
update_metadata(current_version, version, metadata_file)
|
118
|
+
ui.msg("Manually bumped version of the #{cookbook} cookbook from #{current_version} to #{version}")
|
119
|
+
end
|
120
|
+
|
121
|
+
def update_metadata(old_version, new_version, metadata_file)
|
122
|
+
open_file = File.open(metadata_file, "r")
|
123
|
+
body_of_file = open_file.read
|
124
|
+
open_file.close
|
125
|
+
body_of_file.gsub!(old_version, new_version)
|
126
|
+
File.open(metadata_file, "w") { |file| file << body_of_file }
|
127
|
+
end
|
128
|
+
|
129
|
+
def get_version(cookbook_path, cookbook)
|
130
|
+
loader = ::Chef::CookbookLoader.new(cookbook_path)
|
131
|
+
return loader[cookbook].version
|
132
|
+
end
|
133
|
+
|
134
|
+
def git_add(cookbook)
|
135
|
+
strio = StringIO.new
|
136
|
+
l = Logger.new strio
|
137
|
+
cookbook_path = config[:cookbook_path]
|
138
|
+
if cookbook_path.size > 1
|
139
|
+
ui.warn "It looks like you have multiple cookbook paths defined so I can't tell if you're running inside a git repo.\n\n"
|
140
|
+
else
|
141
|
+
begin
|
142
|
+
path = cookbook_path[0].gsub("cookbooks","")
|
143
|
+
ui.msg "Opening git repo #{path}\n\n"
|
144
|
+
g = Git.open(path, :log => Logger.new(strio))
|
145
|
+
ui.msg "Git add'ing #{path}cookbooks/#{cookbook}/metadata.rb\n\n"
|
146
|
+
g.add("#{path}cookbooks/#{cookbook}/metadata.rb")
|
147
|
+
rescue ArgumentError => e
|
148
|
+
puts "Git Error: The root of your chef repo doesn't look like it's a git repo. Skipping git add...\n\n"
|
149
|
+
rescue
|
150
|
+
puts "Git Error: Something went wrong, Dumping log info..."
|
151
|
+
puts "#{strio.string}"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
class String
|
160
|
+
def is_i?
|
161
|
+
!!(self =~ /^[-+]?[0-9]+$/)
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Jon Cowie (<jonlives@gmail.com>)
|
3
|
+
# Copyright:: Copyright (c) 2011 Jon Cowie
|
4
|
+
# License:: GPL
|
5
|
+
|
6
|
+
|
7
|
+
require 'json'
|
8
|
+
require 'chef/knife'
|
9
|
+
require 'chef/cookbook_loader'
|
10
|
+
|
11
|
+
module KnifeSpork
|
12
|
+
class SporkCheck < Chef::Knife
|
13
|
+
|
14
|
+
deps do
|
15
|
+
require 'chef/json_compat'
|
16
|
+
require 'uri'
|
17
|
+
require 'chef/cookbook_version'
|
18
|
+
end
|
19
|
+
banner "knife spork check COOKBOOK"
|
20
|
+
|
21
|
+
def run
|
22
|
+
|
23
|
+
self.config = Chef::Config.merge!(config)
|
24
|
+
|
25
|
+
if config.has_key?(:cookbook_path)
|
26
|
+
cookbook_path = config["cookbook_path"]
|
27
|
+
else
|
28
|
+
ui.fatal "No default cookbook_path; Specify with -o or fix your knife.rb."
|
29
|
+
show_usage
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
|
33
|
+
if name_args.size == 0
|
34
|
+
show_usage
|
35
|
+
exit 0
|
36
|
+
end
|
37
|
+
|
38
|
+
unless name_args.size == 1
|
39
|
+
ui.fatal "Please specify the cookbook whose version you which to check."
|
40
|
+
show_usage
|
41
|
+
exit 1
|
42
|
+
end
|
43
|
+
|
44
|
+
cookbook = name_args.first
|
45
|
+
cookbook_path = Array(config[:cookbook_path]).first
|
46
|
+
|
47
|
+
local_version = get_local_cookbook_version(cookbook_path, cookbook)
|
48
|
+
remote_versions = get_remote_cookbook_versions(cookbook)
|
49
|
+
|
50
|
+
check_versions(cookbook, local_version, remote_versions)
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def get_local_cookbook_version(cookbook_path, cookbook)
|
55
|
+
current_version = get_version(cookbook_path, cookbook).split(".").map{|i| i.to_i}
|
56
|
+
metadata_file = File.join(cookbook_path, cookbook, "metadata.rb")
|
57
|
+
local_version = current_version.join('.')
|
58
|
+
return local_version
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_remote_cookbook_versions(cookbook)
|
62
|
+
env = config[:environment]
|
63
|
+
api_endpoint = env ? "environments/#{env}/cookbooks/#{cookbook}" : "cookbooks/#{cookbook}"
|
64
|
+
cookbooks = rest.get_rest(api_endpoint)
|
65
|
+
versions = cookbooks[cookbook]["versions"]
|
66
|
+
return versions
|
67
|
+
end
|
68
|
+
|
69
|
+
def check_versions(cookbook, local_version, remote_versions)
|
70
|
+
|
71
|
+
conflict = false
|
72
|
+
frozen = false
|
73
|
+
ui.msg "Checking versions for cookbook #{cookbook}..."
|
74
|
+
ui.msg ""
|
75
|
+
ui.msg "Current local version: #{local_version}"
|
76
|
+
ui.msg ""
|
77
|
+
ui.msg "Remote versions:"
|
78
|
+
remote_versions.each do |v|
|
79
|
+
|
80
|
+
version_frozen = check_frozen(cookbook,v["version"])
|
81
|
+
|
82
|
+
if version_frozen then
|
83
|
+
pretty_frozen = "frozen"
|
84
|
+
else
|
85
|
+
pretty_frozen = "unfrozen"
|
86
|
+
end
|
87
|
+
|
88
|
+
if v["version"] == local_version then
|
89
|
+
ui.msg "*" + v["version"] + ", " + pretty_frozen
|
90
|
+
conflict = true
|
91
|
+
if version_frozen then
|
92
|
+
frozen = true
|
93
|
+
end
|
94
|
+
else
|
95
|
+
ui.msg v["version"] + ", " + pretty_frozen
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
ui.msg ""
|
100
|
+
|
101
|
+
if conflict && frozen
|
102
|
+
ui.msg "DANGER: Your local cookbook has same version number as the starred version above!\n\nPlease bump your local version or you won't be able to upload."
|
103
|
+
elsif conflict && !frozen
|
104
|
+
ui.msg "DANGER: Your local cookbook version number clashes with an unfrozen remote version.\n\nIf you upload now, you'll overwrite it."
|
105
|
+
else
|
106
|
+
ui.msg "Everything looks fine, no version clashes. You can upload!"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_version(cookbook_path, cookbook)
|
111
|
+
loader = ::Chef::CookbookLoader.new(cookbook_path)
|
112
|
+
return loader[cookbook].version
|
113
|
+
end
|
114
|
+
|
115
|
+
def check_frozen(cookbook,version)
|
116
|
+
env = config[:environment]
|
117
|
+
api_endpoint = env ? "environments/#{env}/cookbooks/#{cookbook}" : "cookbooks/#{cookbook}/#{version}"
|
118
|
+
cookbooks = rest.get_rest(api_endpoint)
|
119
|
+
cookbook_hash = cookbooks.to_hash
|
120
|
+
return cookbook_hash["frozen?"]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Jon Cowie (<jonlives@gmail.com>)
|
3
|
+
# Copyright:: Copyright (c) 2011 Jon Cowie
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# Uses code from the knife cookbook upload plugin by:
|
8
|
+
#
|
9
|
+
# Author:: Adam Jacob (<adam@opscode.com>)
|
10
|
+
# Author:: Christopher Walters (<cw@opscode.com>)
|
11
|
+
# Author:: Nuo Yan (<yan.nuo@gmail.com>)
|
12
|
+
# Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
|
13
|
+
# License:: Apache License, Version 2.0
|
14
|
+
#
|
15
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
16
|
+
# you may not use this file except in compliance with the License.
|
17
|
+
# You may obtain a copy of the License at
|
18
|
+
#
|
19
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
20
|
+
#
|
21
|
+
# Unless required by applicable law or agreed to in writing, software
|
22
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
23
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
24
|
+
# See the License for the specific language governing permissions and
|
25
|
+
# limitations under the License.
|
26
|
+
#
|
27
|
+
|
28
|
+
require 'chef/knife'
|
29
|
+
require 'json'
|
30
|
+
|
31
|
+
module KnifeSpork
|
32
|
+
class SporkPromote < Chef::Knife
|
33
|
+
|
34
|
+
@@gitavail = true
|
35
|
+
deps do
|
36
|
+
require 'chef/exceptions'
|
37
|
+
require 'chef/cookbook_loader'
|
38
|
+
require 'chef/knife/core/object_loader'
|
39
|
+
begin
|
40
|
+
require "git"
|
41
|
+
rescue LoadError
|
42
|
+
@@gitavail = false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
banner "knife spork promote ENVIRONMENT COOKBOOK (options)"
|
47
|
+
|
48
|
+
option :version,
|
49
|
+
:short => '-v',
|
50
|
+
:long => '--version VERSION',
|
51
|
+
:description => "Set the environment's version constraint to the specified version",
|
52
|
+
:default => nil
|
53
|
+
|
54
|
+
option :remote,
|
55
|
+
:long => '--remote',
|
56
|
+
:description => "Save the environment to the chef server in addition to the local JSON file",
|
57
|
+
:default => nil
|
58
|
+
|
59
|
+
def run
|
60
|
+
config[:cookbook_path] ||= Chef::Config[:cookbook_path]
|
61
|
+
|
62
|
+
if @name_args.empty?
|
63
|
+
show_usage
|
64
|
+
ui.error("You must specify a cookbook name and an environment")
|
65
|
+
exit 1
|
66
|
+
elsif @name_args.size != 2
|
67
|
+
show_usage
|
68
|
+
ui.error("You must specify a cookbook name and an environment")
|
69
|
+
exit 1
|
70
|
+
end
|
71
|
+
|
72
|
+
if !@@gitavail
|
73
|
+
ui.msg "Git gem not available, skipping git pull.\n\n"
|
74
|
+
else
|
75
|
+
git_pull_if_repo
|
76
|
+
end
|
77
|
+
|
78
|
+
@cookbook = @name_args[1]
|
79
|
+
@environment = loader.load_from("environments", @name_args[0] + ".json")
|
80
|
+
|
81
|
+
if @cookbook == "all"
|
82
|
+
ui.msg "Promoting ALL cookbooks to environment #{@environment}\n\n"
|
83
|
+
cookbook_names = get_all_cookbooks
|
84
|
+
cookbook_names.each do |c|
|
85
|
+
@environment = promote(@environment, c)
|
86
|
+
end
|
87
|
+
else
|
88
|
+
@environment = promote(@environment, @cookbook)
|
89
|
+
end
|
90
|
+
|
91
|
+
ui.msg "\nSaving changes into #{@name_args[0]}.json"
|
92
|
+
new_environment_json = pretty_print(@environment)
|
93
|
+
save_environment_changes(@name_args[0],new_environment_json)
|
94
|
+
|
95
|
+
if config[:remote]
|
96
|
+
ui.msg "\nUploading #{@name_args[0]} to server"
|
97
|
+
save_environment_changes_remote(@name_args[0] + ".json")
|
98
|
+
ui.info "\nPromotion complete, and environment uploaded."
|
99
|
+
else
|
100
|
+
ui.info "\nPromotion complete! Please remember to upload your changed Environment file to the Chef Server."
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
def update_version_constraints(environment,cookbook,version_constraint)
|
106
|
+
environment.cookbook_versions[cookbook] = "= #{version_constraint}"
|
107
|
+
return environment
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_version(cookbook_path, cookbook)
|
111
|
+
loader = ::Chef::CookbookLoader.new(cookbook_path)
|
112
|
+
return loader[cookbook].version
|
113
|
+
end
|
114
|
+
|
115
|
+
def load_environment(env)
|
116
|
+
e = Chef::Environment.load(env)
|
117
|
+
ejson = JSON.parse(e.to_json)
|
118
|
+
puts JSON.pretty_generate(ejson)
|
119
|
+
return e
|
120
|
+
rescue Net::HTTPServerException => e
|
121
|
+
if e.response.code.to_s == "404"
|
122
|
+
ui.error "The environment #{env} does not exist on the server, aborting."
|
123
|
+
Chef::Log.debug(e)
|
124
|
+
exit 1
|
125
|
+
else
|
126
|
+
raise
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def valid_version(version)
|
131
|
+
v = version.split(".")
|
132
|
+
if v.size < 3 or v.size > 3
|
133
|
+
return false
|
134
|
+
end
|
135
|
+
v.each do |v_comp|
|
136
|
+
if !v_comp.is_i?
|
137
|
+
return false
|
138
|
+
end
|
139
|
+
end
|
140
|
+
return true
|
141
|
+
end
|
142
|
+
|
143
|
+
def loader
|
144
|
+
@loader ||= Chef::Knife::Core::ObjectLoader.new(Chef::Environment, ui)
|
145
|
+
end
|
146
|
+
|
147
|
+
def save_environment_changes_remote(environment)
|
148
|
+
@loader ||= Knife::Core::ObjectLoader.new(Chef::Environment, ui)
|
149
|
+
updated = loader.load_from("environments", environment)
|
150
|
+
updated.save
|
151
|
+
end
|
152
|
+
|
153
|
+
def save_environment_changes(environment,envjson)
|
154
|
+
cookbook_path = config[:cookbook_path]
|
155
|
+
|
156
|
+
if cookbook_path.size > 1
|
157
|
+
ui.warn "It looks like you have multiple cookbook paths defined so I'm not sure where to save your changed environment file.\n\n"
|
158
|
+
ui.msg "Here's the JSON for you to paste into #{environment}.json in the environments directory you wish to use.\n\n"
|
159
|
+
ui.msg "#{envjson}\n\n"
|
160
|
+
else
|
161
|
+
path = cookbook_path[0].gsub("cookbooks","environments") + "/#{environment}.json"
|
162
|
+
|
163
|
+
File.open(path, 'w') do |f2|
|
164
|
+
# use "\n" for two lines of text
|
165
|
+
f2.puts envjson
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def promote(environment,cookbook)
|
171
|
+
|
172
|
+
if config[:version]
|
173
|
+
if !valid_version(config[:version])
|
174
|
+
ui.error("#{config[:version]} isn't a valid version number.")
|
175
|
+
return 1
|
176
|
+
else
|
177
|
+
@version = config[:version]
|
178
|
+
end
|
179
|
+
else
|
180
|
+
@version = get_version(config[:cookbook_path], cookbook)
|
181
|
+
end
|
182
|
+
|
183
|
+
ui.msg "Adding version constraint #{cookbook} = #{@version}"
|
184
|
+
return update_version_constraints(environment,cookbook,@version)
|
185
|
+
end
|
186
|
+
|
187
|
+
def get_all_cookbooks
|
188
|
+
results = []
|
189
|
+
cookbooks = ::Chef::CookbookLoader.new(config[:cookbook_path])
|
190
|
+
cookbooks.each do |c|
|
191
|
+
results << c
|
192
|
+
end
|
193
|
+
return results
|
194
|
+
end
|
195
|
+
|
196
|
+
def pretty_print(environment)
|
197
|
+
return JSON.pretty_generate(JSON.parse(environment.to_json))
|
198
|
+
end
|
199
|
+
|
200
|
+
def git_pull_if_repo
|
201
|
+
strio = StringIO.new
|
202
|
+
l = Logger.new strio
|
203
|
+
cookbook_path = config[:cookbook_path]
|
204
|
+
if cookbook_path.size > 1
|
205
|
+
ui.warn "It looks like you have multiple cookbook paths defined so I can't tell if you're running inside a git repo.\n\n"
|
206
|
+
else
|
207
|
+
begin
|
208
|
+
path = cookbook_path[0].gsub("cookbooks","")
|
209
|
+
ui.msg "Opening git repo #{path}\n\n"
|
210
|
+
g = Git.open(path, :log => Logger.new(strio))
|
211
|
+
ui.msg "Pulling latest changes from git\n\n"
|
212
|
+
g.pull
|
213
|
+
rescue ArgumentError => e
|
214
|
+
puts "Git Error: The root of your chef repo doesn't look like it's a git repo. Skipping git pull...\n\n"
|
215
|
+
rescue
|
216
|
+
puts "Git Error: Something went wrong, Dumping log info..."
|
217
|
+
puts "#{strio.string}"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
class String
|
225
|
+
def is_i?
|
226
|
+
!!(self =~ /^[-+]?[0-9]+$/)
|
227
|
+
end
|
228
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
#
|
2
|
+
# Modifying Author:: Jon Cowie (<jonlives@gmail.com>)
|
3
|
+
# Copyright:: Copyright (c) 2011 Jon Cowie
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Modified cookbook upload to always freeze, and disable --force option
|
7
|
+
# Based on the knife cookbook upload plugin by:
|
8
|
+
#
|
9
|
+
# Author:: Adam Jacob (<adam@opscode.com>)
|
10
|
+
# Author:: Christopher Walters (<cw@opscode.com>)
|
11
|
+
# Author:: Nuo Yan (<yan.nuo@gmail.com>)
|
12
|
+
# Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
|
13
|
+
# License:: Apache License, Version 2.0
|
14
|
+
#
|
15
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
16
|
+
# you may not use this file except in compliance with the License.
|
17
|
+
# You may obtain a copy of the License at
|
18
|
+
#
|
19
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
20
|
+
#
|
21
|
+
# Unless required by applicable law or agreed to in writing, software
|
22
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
23
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
24
|
+
# See the License for the specific language governing permissions and
|
25
|
+
# limitations under the License.
|
26
|
+
#
|
27
|
+
|
28
|
+
require 'chef/knife'
|
29
|
+
|
30
|
+
module KnifeSpork
|
31
|
+
class SporkUpload < Chef::Knife
|
32
|
+
|
33
|
+
CHECKSUM = "checksum"
|
34
|
+
MATCH_CHECKSUM = /[0-9a-f]{32,}/
|
35
|
+
|
36
|
+
deps do
|
37
|
+
require 'chef/exceptions'
|
38
|
+
require 'chef/cookbook_loader'
|
39
|
+
require 'chef/cookbook_uploader'
|
40
|
+
end
|
41
|
+
|
42
|
+
banner "knife spork upload [COOKBOOKS...] (options)"
|
43
|
+
|
44
|
+
option :cookbook_path,
|
45
|
+
:short => "-o PATH:PATH",
|
46
|
+
:long => "--cookbook-path PATH:PATH",
|
47
|
+
:description => "A colon-separated path to look for cookbooks in",
|
48
|
+
:proc => lambda { |o| o.split(":") }
|
49
|
+
|
50
|
+
option :freeze,
|
51
|
+
:long => '--freeze',
|
52
|
+
:description => 'Freeze this version of the cookbook so that it cannot be overwritten',
|
53
|
+
:boolean => true
|
54
|
+
|
55
|
+
option :environment,
|
56
|
+
:short => '-E',
|
57
|
+
:long => '--environment ENVIRONMENT',
|
58
|
+
:description => "Set ENVIRONMENT's version dependency match the version you're uploading.",
|
59
|
+
:default => nil
|
60
|
+
|
61
|
+
option :depends,
|
62
|
+
:short => "-d",
|
63
|
+
:long => "--include-dependencies",
|
64
|
+
:description => "Also upload cookbook dependencies"
|
65
|
+
|
66
|
+
def run
|
67
|
+
config[:cookbook_path] ||= Chef::Config[:cookbook_path]
|
68
|
+
|
69
|
+
assert_environment_valid!
|
70
|
+
warn_about_cookbook_shadowing
|
71
|
+
version_constraints_to_update = {}
|
72
|
+
# Get a list of cookbooks and their versions from the server
|
73
|
+
# for checking existence of dependending cookbooks.
|
74
|
+
@server_side_cookbooks = Chef::CookbookVersion.list
|
75
|
+
|
76
|
+
if @name_args.empty?
|
77
|
+
show_usage
|
78
|
+
ui.error("You must specify the --all flag or at least one cookbook name")
|
79
|
+
exit 1
|
80
|
+
end
|
81
|
+
justify_width = @name_args.map {|name| name.size }.max.to_i + 2
|
82
|
+
@name_args.each do |cookbook_name|
|
83
|
+
begin
|
84
|
+
cookbook = cookbook_repo[cookbook_name]
|
85
|
+
if config[:depends]
|
86
|
+
cookbook.metadata.dependencies.each do |dep, versions|
|
87
|
+
@name_args.push dep
|
88
|
+
end
|
89
|
+
end
|
90
|
+
ui.info("Uploading and freezing #{cookbook.name.to_s.ljust(justify_width + 10)} [#{cookbook.version}]")
|
91
|
+
upload(cookbook, justify_width)
|
92
|
+
cookbook.freeze_version
|
93
|
+
upload(cookbook, justify_width)
|
94
|
+
version_constraints_to_update[cookbook_name] = cookbook.version
|
95
|
+
rescue Chef::Exceptions::CookbookNotFoundInRepo => e
|
96
|
+
ui.error("Could not find cookbook #{cookbook_name} in your cookbook path, skipping it")
|
97
|
+
Chef::Log.debug(e)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
ui.info "upload complete"
|
102
|
+
update_version_constraints(version_constraints_to_update) if config[:environment]
|
103
|
+
end
|
104
|
+
|
105
|
+
def cookbook_repo
|
106
|
+
@cookbook_loader ||= begin
|
107
|
+
Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, config[:cookbook_path]) }
|
108
|
+
Chef::CookbookLoader.new(config[:cookbook_path])
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def update_version_constraints(new_version_constraints)
|
113
|
+
new_version_constraints.each do |cookbook_name, version|
|
114
|
+
environment.cookbook_versions[cookbook_name] = "= #{version}"
|
115
|
+
end
|
116
|
+
environment.save
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
def environment
|
121
|
+
@environment ||= config[:environment] ? Chef::Environment.load(config[:environment]) : nil
|
122
|
+
end
|
123
|
+
|
124
|
+
def warn_about_cookbook_shadowing
|
125
|
+
unless cookbook_repo.merged_cookbooks.empty?
|
126
|
+
ui.warn "* " * 40
|
127
|
+
ui.warn(<<-WARNING)
|
128
|
+
The cookbooks: #{cookbook_repo.merged_cookbooks.join(', ')} exist in multiple places in your cookbook_path.
|
129
|
+
A composite version of these cookbooks has been compiled for uploading.
|
130
|
+
|
131
|
+
#{ui.color('IMPORTANT:', :red, :bold)} In a future version of Chef, this behavior will be removed and you will no longer
|
132
|
+
be able to have the same version of a cookbook in multiple places in your cookbook_path.
|
133
|
+
WARNING
|
134
|
+
ui.warn "The affected cookbooks are located:"
|
135
|
+
ui.output ui.format_for_display(cookbook_repo.merged_cookbook_paths)
|
136
|
+
ui.warn "* " * 40
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def assert_environment_valid!
|
143
|
+
environment
|
144
|
+
rescue Net::HTTPServerException => e
|
145
|
+
if e.response.code.to_s == "404"
|
146
|
+
ui.error "The environment #{config[:environment]} does not exist on the server, aborting."
|
147
|
+
Chef::Log.debug(e)
|
148
|
+
exit 1
|
149
|
+
else
|
150
|
+
raise
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def upload(cookbook, justify_width)
|
155
|
+
|
156
|
+
check_for_broken_links(cookbook)
|
157
|
+
check_dependencies(cookbook)
|
158
|
+
Chef::CookbookUploader.new(cookbook, config[:cookbook_path]).upload_cookbook
|
159
|
+
rescue Net::HTTPServerException => e
|
160
|
+
case e.response.code
|
161
|
+
when "409"
|
162
|
+
ui.error "Version #{cookbook.version} of cookbook #{cookbook.name} is frozen. Please bump your version number."
|
163
|
+
Chef::Log.debug(e)
|
164
|
+
else
|
165
|
+
raise
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# if only you people wouldn't put broken symlinks in your cookbooks in
|
170
|
+
# the first place. ;)
|
171
|
+
def check_for_broken_links(cookbook)
|
172
|
+
# MUST!! dup the cookbook version object--it memoizes its
|
173
|
+
# manifest object, but the manifest becomes invalid when you
|
174
|
+
# regenerate the metadata
|
175
|
+
broken_files = cookbook.dup.manifest_records_by_path.select do |path, info|
|
176
|
+
info[CHECKSUM].nil? || info[CHECKSUM] !~ MATCH_CHECKSUM
|
177
|
+
end
|
178
|
+
unless broken_files.empty?
|
179
|
+
broken_filenames = Array(broken_files).map {|path, info| path}
|
180
|
+
ui.error "The cookbook #{cookbook.name} has one or more broken files"
|
181
|
+
ui.info "This is probably caused by broken symlinks in the cookbook directory"
|
182
|
+
ui.info "The broken file(s) are: #{broken_filenames.join(' ')}"
|
183
|
+
exit 1
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def check_dependencies(cookbook)
|
188
|
+
# for each dependency, check if the version is on the server, or
|
189
|
+
# the version is in the cookbooks being uploaded. If not, exit and warn the user.
|
190
|
+
cookbook.metadata.dependencies.each do |cookbook_name, version|
|
191
|
+
unless check_server_side_cookbooks(cookbook_name, version) || check_uploading_cookbooks(cookbook_name, version)
|
192
|
+
# warn the user and exit
|
193
|
+
ui.error "Cookbook #{cookbook.name} depends on cookbook #{cookbook_name} version #{version},"
|
194
|
+
ui.error "which is not currently being uploaded and cannot be found on the server."
|
195
|
+
exit 1
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def check_server_side_cookbooks(cookbook_name, version)
|
201
|
+
if @server_side_cookbooks[cookbook_name].nil?
|
202
|
+
false
|
203
|
+
else
|
204
|
+
@server_side_cookbooks[cookbook_name]["versions"].each do |versions_hash|
|
205
|
+
return true if Chef::VersionConstraint.new(version).include?(versions_hash["version"])
|
206
|
+
end
|
207
|
+
false
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def check_uploading_cookbooks(cookbook_name, version)
|
212
|
+
if config[:all]
|
213
|
+
# check from all local cookbooks in the path
|
214
|
+
unless cookbook_repo[cookbook_name].nil?
|
215
|
+
return Chef::VersionConstraint.new(version).include?(cookbook_repo[cookbook_name].version)
|
216
|
+
end
|
217
|
+
else
|
218
|
+
# check from only those in the command argument
|
219
|
+
if @name_args.include?(cookbook_name)
|
220
|
+
return Chef::VersionConstraint.new(version).include?(cookbook_repo[cookbook_name].version)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
false
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
end
|
data/lib/knife-spork.rb
ADDED
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: knife-spork
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jon Cowie
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-01-28 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: chef
|
16
|
+
requirement: &70176180960680 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.10.4
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70176180960680
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: git
|
27
|
+
requirement: &70176180960200 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.2.5
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70176180960200
|
36
|
+
description: A workflow plugin to help many devs work with the same chef repo/server
|
37
|
+
email: jonlives@gmail.com
|
38
|
+
executables: []
|
39
|
+
extensions: []
|
40
|
+
extra_rdoc_files:
|
41
|
+
- README.md
|
42
|
+
files:
|
43
|
+
- README.md
|
44
|
+
- Rakefile
|
45
|
+
- knife-spork.gemspec
|
46
|
+
- lib/chef/knife/spork-bump.rb
|
47
|
+
- lib/chef/knife/spork-check.rb
|
48
|
+
- lib/chef/knife/spork-promote.rb
|
49
|
+
- lib/chef/knife/spork-upload.rb
|
50
|
+
- lib/knife-spork.rb
|
51
|
+
homepage: https://github.com/jonlives/knife-spork
|
52
|
+
licenses: []
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options:
|
55
|
+
- --charset=UTF-8
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project: knife-spork
|
72
|
+
rubygems_version: 1.8.15
|
73
|
+
signing_key:
|
74
|
+
specification_version: 2
|
75
|
+
summary: A workflow plugin to help many devs work with the same chef repo/server
|
76
|
+
test_files: []
|