knife-flip 0.1.6 → 0.1.7
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.
- data/README.md +4 -2
- data/knife-flip.gemspec +3 -2
- data/lib/chef/knife/nodeflip.rb +142 -27
- data/lib/knife-flip.rb +1 -1
- metadata +18 -2
data/README.md
CHANGED
@@ -16,10 +16,12 @@ gem install knife-flip
|
|
16
16
|
## What it does
|
17
17
|
|
18
18
|
````
|
19
|
-
knife node flip mynode.foo.com myenv
|
19
|
+
knife node flip mynode.foo.com myenv [--preview]
|
20
20
|
````
|
21
21
|
|
22
|
-
will move the node mynode.foo.com into the environment myenv
|
22
|
+
will move the node mynode.foo.com into the environment myenv. Passing in the --preview option
|
23
|
+
it will dry-run the flip and show you what cookbooks and versions would be applied if it were actually
|
24
|
+
flipped.
|
23
25
|
|
24
26
|
|
25
27
|
````
|
data/knife-flip.gemspec
CHANGED
@@ -13,8 +13,8 @@ Gem::Specification.new do |s|
|
|
13
13
|
## If your rubyforge_project name is different, then edit it and comment out
|
14
14
|
## the sub! line in the Rakefile
|
15
15
|
s.name = 'knife-flip'
|
16
|
-
s.version = '0.1.
|
17
|
-
s.date = '2013-
|
16
|
+
s.version = '0.1.7'
|
17
|
+
s.date = '2013-06-06'
|
18
18
|
s.rubyforge_project = 'knife-flip'
|
19
19
|
|
20
20
|
## Make sure your summary is short. The description may be as long
|
@@ -41,6 +41,7 @@ Gem::Specification.new do |s|
|
|
41
41
|
## List your runtime dependencies here. Runtime dependencies are those
|
42
42
|
## that are needed for an end user to actually USE your code.
|
43
43
|
s.add_dependency('chef', [">= 0.10.4"])
|
44
|
+
s.add_dependency('colorize', [">= 0.5.8"])
|
44
45
|
|
45
46
|
## Leave this section as-is. It will be automatically generated from the
|
46
47
|
## contents of your Git repository via the gemspec task. DO NOT REMOVE
|
data/lib/chef/knife/nodeflip.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
|
4
4
|
require 'chef/knife'
|
5
|
+
require 'colorize'
|
5
6
|
|
6
7
|
module KnifeFlip
|
7
8
|
class NodeFlip < Chef::Knife
|
@@ -12,7 +13,13 @@ module KnifeFlip
|
|
12
13
|
require 'chef/knife/core/object_loader'
|
13
14
|
end
|
14
15
|
|
15
|
-
banner "knife node flip NODE ENVIRONMENT"
|
16
|
+
banner "knife node flip NODE ENVIRONMENT (options)"
|
17
|
+
|
18
|
+
option :preview,
|
19
|
+
:long => '--preview',
|
20
|
+
:boolean => true,
|
21
|
+
:on => :tail,
|
22
|
+
:description => 'Preview the target environment to see affected cookbooks'
|
16
23
|
|
17
24
|
def run
|
18
25
|
unless @node_name = name_args[0]
|
@@ -25,42 +32,150 @@ module KnifeFlip
|
|
25
32
|
exit 1
|
26
33
|
end
|
27
34
|
|
28
|
-
|
35
|
+
if config[:preview] then
|
36
|
+
show_environmental_differences
|
37
|
+
else
|
38
|
+
puts "Looking for an fqdn of #{@node_name} or name of #{@node_name}"
|
39
|
+
|
40
|
+
searcher = Chef::Search::Query.new
|
41
|
+
result = searcher.search(:node, "fqdn:#{@node_name} OR name:#{@node_name}")
|
42
|
+
|
43
|
+
knife_search = Chef::Knife::Search.new
|
44
|
+
node = result.first.first
|
45
|
+
if node.nil?
|
46
|
+
puts "Could not find a node with the fqdn of #{@node_name} or name of #{@node_name}"
|
47
|
+
exit 1
|
48
|
+
end
|
49
|
+
|
50
|
+
begin
|
51
|
+
e = Chef::Environment.load(@environment)
|
52
|
+
rescue Net::HTTPServerException => e
|
53
|
+
if e.response.code.to_s == "404"
|
54
|
+
ui.error "The environment #{@environment} does not exist on the server, aborting."
|
55
|
+
Chef::Log.debug(e)
|
56
|
+
exit 1
|
57
|
+
else
|
58
|
+
raise
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
puts "Setting environment to #{@environment}"
|
63
|
+
node.chef_environment(@environment)
|
64
|
+
node.save
|
65
|
+
|
66
|
+
knife_search = Chef::Knife::Search.new
|
67
|
+
# ensure that 'start' and 'rows' are set since we don't seem to properly inherit the +config+ hash and therefore don't get sane defaults
|
68
|
+
config[:start] = 0
|
69
|
+
config[:rows] = 1000
|
70
|
+
knife_search.config = config # without this, the +config+ hash is empty
|
71
|
+
knife_search.name_args = ['node', "fqdn:#{@node_name} OR name:#{@node_name}"]
|
72
|
+
knife_search.run
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# Compare the two environments
|
79
|
+
def show_environmental_differences
|
80
|
+
# Re-iterate to the user that this won't flip you
|
81
|
+
remind_user_about_preview
|
82
|
+
|
83
|
+
# Load up the node information
|
84
|
+
load_node_object
|
85
|
+
|
86
|
+
# Get the source environment
|
87
|
+
get_source_environment
|
88
|
+
|
89
|
+
# Pull down the cookbooks for this node
|
90
|
+
cookbooks = get_cookbooks_for_node
|
91
|
+
|
92
|
+
# Get cookbooks for the environments we are checking
|
93
|
+
source_cookbooks = cookbooks_for_environment(@source_environment)
|
94
|
+
target_cookbooks = cookbooks_for_environment(@environment)
|
95
|
+
|
96
|
+
# Transform the uploaded cookbooks into a name => latest version hash
|
97
|
+
source_hash = get_cookbook_version_hash(source_cookbooks)
|
29
98
|
|
99
|
+
# Transform the production cookbooks into a name => latest version hash
|
100
|
+
target_hash = get_cookbook_version_hash(target_cookbooks)
|
101
|
+
|
102
|
+
# Intersect the production cookbook collection and ours
|
103
|
+
common_cookbooks = target_cookbooks.keys & cookbooks
|
104
|
+
changed_cookbooks = common_cookbooks.keep_if { |cookbook_key|
|
105
|
+
target_hash[cookbook_key] != source_hash[cookbook_key]
|
106
|
+
}
|
107
|
+
|
108
|
+
# Lets show what is different
|
109
|
+
show_cookbook_differences(changed_cookbooks, source_hash, target_hash)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Load up the node in question into a instance variable
|
113
|
+
def load_node_object
|
30
114
|
searcher = Chef::Search::Query.new
|
31
115
|
result = searcher.search(:node, "fqdn:#{@node_name} OR name:#{@node_name}")
|
32
116
|
|
33
|
-
|
34
|
-
node
|
35
|
-
if node.nil?
|
117
|
+
@node = result.first.first
|
118
|
+
if @node.nil?
|
36
119
|
puts "Could not find a node with the fqdn of #{@node_name} or name of #{@node_name}"
|
37
120
|
exit 1
|
38
|
-
end
|
121
|
+
end
|
122
|
+
end
|
39
123
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
124
|
+
# Extract the source environment from the node object
|
125
|
+
def get_source_environment
|
126
|
+
@source_environment = @node.chef_environment
|
127
|
+
end
|
128
|
+
|
129
|
+
# For the node being passed in, find and return an array of cookboock names
|
130
|
+
def get_cookbooks_for_node
|
131
|
+
cookbooks = @node.recipes.map {|recipe| recipe.match('^[^:]+')[0] }.uniq
|
132
|
+
|
133
|
+
return cookbooks
|
134
|
+
end
|
135
|
+
|
136
|
+
# For an environment, get all the cookbooks associated with it (in API object array form)
|
137
|
+
def cookbooks_for_environment(environment=nil, num_versions=1)
|
138
|
+
api_endpoint = environment ? "/environments/#{environment}/cookbooks?#{num_versions}" : "/cookbooks?#{num_versions}"
|
139
|
+
cookbooks = rest.get_rest(api_endpoint)
|
140
|
+
|
141
|
+
return cookbooks
|
142
|
+
end
|
143
|
+
|
144
|
+
# Given a cookbook array returned from the API, create a Hash of its name and the latest version
|
145
|
+
def get_cookbook_version_hash(cookbooks)
|
146
|
+
Hash[cookbooks.collect { |k, v| [k, v['versions'].first['version']] }]
|
147
|
+
end
|
148
|
+
|
149
|
+
# Takes in an array of modified cookbook names, the hash of uploaded cookbooks, and the hash of cookbooks
|
150
|
+
# from the environment being checked
|
151
|
+
def show_cookbook_differences(changed_cookbook_names, source_hash, target_hash)
|
152
|
+
changed_cookbook_count = changed_cookbook_names.size
|
153
|
+
(1..110).each { print "=".colorize(:cyan) }
|
154
|
+
puts ""
|
155
|
+
if changed_cookbook_count != 0 then
|
156
|
+
puts "#{changed_cookbook_count} difference(s) between environments"
|
157
|
+
changed_cookbook_names.each do |cookbook_name|
|
158
|
+
puts "#{cookbook_name}".colorize(:yellow) + ": " + "#{@source_environment}".colorize(:magenta) +
|
159
|
+
" version: " + "#{source_hash[cookbook_name]} ".colorize(:red) +
|
160
|
+
"will be changed to " + "#{target_hash[cookbook_name]}".colorize(:green) + " in " +
|
161
|
+
"#{@environment}".colorize(:magenta)
|
49
162
|
end
|
163
|
+
elsif (@source_environment == @environment) and (changed_cookbook_names.size == 0) then
|
164
|
+
puts "The environment the node is on and the one you are flipping to are identical, and thus "
|
165
|
+
puts "there are " + "NO".colorize(:red) +" cookbook differences"
|
166
|
+
else
|
167
|
+
puts "No differences".colorize(:red) +
|
168
|
+
" between the cookbook versions in the " + "#{@source_environment}".colorize(:magenta) + " and the " +
|
169
|
+
"#{@environment}".colorize(:magenta) + " environment for this node"
|
50
170
|
end
|
51
|
-
|
52
|
-
puts "
|
53
|
-
|
54
|
-
node.save
|
55
|
-
|
56
|
-
knife_search = Chef::Knife::Search.new
|
57
|
-
# ensure that 'start' and 'rows' are set since we don't seem to properly inherit the +config+ hash and therefore don't get sane defaults
|
58
|
-
config[:start] = 0
|
59
|
-
config[:rows] = 1000
|
60
|
-
knife_search.config = config # without this, the +config+ hash is empty
|
61
|
-
knife_search.name_args = ['node', "fqdn:#{@node_name} OR name:#{@node_name}"]
|
62
|
-
knife_search.run
|
171
|
+
(1..110).each { print "=".colorize(:cyan) }
|
172
|
+
puts ""
|
173
|
+
end
|
63
174
|
|
175
|
+
# Print out warning for users so they know that preview is a dry run
|
176
|
+
def remind_user_about_preview
|
177
|
+
puts "NOTE NOTE NOTE".colorize(:red) + " Running with --preview " + "WILL NOT".colorize(:red) +
|
178
|
+
" flip your node\n"
|
64
179
|
end
|
65
180
|
end
|
66
181
|
end
|
data/lib/knife-flip.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-flip
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-06-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: chef
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 0.10.4
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: colorize
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.5.8
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.5.8
|
30
46
|
description: A knife plugin to move a node, or all nodes in a role, to a specific
|
31
47
|
environment
|
32
48
|
email: jonlives@gmail.com
|