chef-dk 0.5.0.rc.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +63 -24
  3. data/lib/chef-dk/builtin_commands.rb +2 -0
  4. data/lib/chef-dk/command/diff.rb +312 -0
  5. data/lib/chef-dk/command/push.rb +1 -1
  6. data/lib/chef-dk/command/shell_init.rb +21 -3
  7. data/lib/chef-dk/command/update.rb +28 -5
  8. data/lib/chef-dk/configurable.rb +1 -1
  9. data/lib/chef-dk/exceptions.rb +3 -0
  10. data/lib/chef-dk/pager.rb +106 -0
  11. data/lib/chef-dk/policyfile/chef_repo_cookbook_source.rb +114 -0
  12. data/lib/chef-dk/policyfile/comparison_base.rb +124 -0
  13. data/lib/chef-dk/policyfile/cookbook_sources.rb +1 -0
  14. data/lib/chef-dk/policyfile/differ.rb +266 -0
  15. data/lib/chef-dk/policyfile/dsl.rb +26 -3
  16. data/lib/chef-dk/policyfile/uploader.rb +4 -5
  17. data/lib/chef-dk/policyfile_compiler.rb +8 -0
  18. data/lib/chef-dk/policyfile_lock.rb +135 -3
  19. data/lib/chef-dk/policyfile_services/install.rb +1 -0
  20. data/lib/chef-dk/policyfile_services/update_attributes.rb +104 -0
  21. data/lib/chef-dk/service_exceptions.rb +12 -0
  22. data/lib/chef-dk/ui.rb +8 -0
  23. data/lib/chef-dk/version.rb +1 -1
  24. data/spec/spec_helper.rb +6 -0
  25. data/spec/test_helpers.rb +4 -0
  26. data/spec/unit/command/diff_spec.rb +283 -0
  27. data/spec/unit/command/shell_init_spec.rb +19 -2
  28. data/spec/unit/command/update_spec.rb +96 -0
  29. data/spec/unit/command/verify_spec.rb +0 -6
  30. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/Berksfile +3 -0
  31. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/README.md +4 -0
  32. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/chefignore +96 -0
  33. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/metadata.rb +9 -0
  34. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/recipes/default.rb +8 -0
  35. data/spec/unit/pager_spec.rb +119 -0
  36. data/spec/unit/policyfile/chef_repo_cookbook_source_spec.rb +66 -0
  37. data/spec/unit/policyfile/comparison_base_spec.rb +343 -0
  38. data/spec/unit/policyfile/differ_spec.rb +687 -0
  39. data/spec/unit/policyfile_evaluation_spec.rb +87 -0
  40. data/spec/unit/policyfile_lock_build_spec.rb +247 -8
  41. data/spec/unit/policyfile_lock_serialization_spec.rb +47 -0
  42. data/spec/unit/policyfile_services/export_repo_spec.rb +2 -0
  43. data/spec/unit/policyfile_services/push_spec.rb +2 -0
  44. data/spec/unit/policyfile_services/update_attributes_spec.rb +217 -0
  45. metadata +62 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ba6c0ab110f7a9fdc6434c4999a965e966a6d764
4
- data.tar.gz: 1cdc0b41d6847ad1db3f23a83bcc02eaa33f5c1c
3
+ metadata.gz: f4c7157bf3d3e4ddd0f51f9250986b3bf63acc43
4
+ data.tar.gz: 02544f6eef80b3a956f1adb70b4804a0ff63e548
5
5
  SHA512:
6
- metadata.gz: 74ad30f7c8a851fec368a512fb64c352b5bd9e6275598658680394469933a8b9af12fbdeb510f5d04b358ea21ef97ff8afc1b418cf389034944234b6e87fff6c
7
- data.tar.gz: a621d898759ebe1527bf1b4c43e7619bbc2c6a4ca001c0fc625aea937c2b9087d55262f15f8a7e19428c2e57cadbcf0f5bd2f2aa1657e15007a4f8fbd5ca46c4
6
+ metadata.gz: ad02bbd68abf16906f4d04dfcd2514c606783a391d5d782576e5c0493819fad22a59ea0efdde4c96d12761a9db13df49e69bdf18ada982b8dd2d5540c2d45e2d
7
+ data.tar.gz: bf912bf1c52def5d04f34ab29b7d18ddcf2a689679b067e4c9e70f401e5e01db4814eb24e09b96183dd064e98da580b091b23315eb9bf9bb6747f1eaaa955229
data/README.md CHANGED
@@ -87,21 +87,6 @@ executables included with a gem you install will be created in
87
87
  `chef exec`, or use `chef shell-init` to add ChefDK's paths to your
88
88
  environment. Those commands are documented below.
89
89
 
90
- #### `chef verify`
91
- `chef verify` tests the embedded applications. By default it runs a
92
- quick "smoke test" to verify that the embedded applications are
93
- installed correctly and can run basic commands. As an end user this is
94
- probably all you'll ever need, but `verify` can also optionally run unit
95
- and integration tests by supplying the `--unit` and `--integration`
96
- flags, respectively.
97
-
98
- *WARNING:* The integration tests will do dangerous things like start
99
- HTTP servers with access to your filesystem and even create users and
100
- groups if run with sufficient privileges. The tests may also be
101
- sensitive to your machine's configuration. If you choose to run these,
102
- we recommend to only run them on dedicated, isolated hosts (we do this
103
- in our build cluster to verify each build).
104
-
105
90
  ### `chef exec`
106
91
  `chef exec <command>` runs any arbitrary shell command with the PATH
107
92
  environment variable and the ruby environment variables (`GEM_HOME`,
@@ -109,9 +94,10 @@ environment variable and the ruby environment variables (`GEM_HOME`,
109
94
 
110
95
  ### `chef shell-init`
111
96
  `chef shell-init SHELL_NAME` emits shell commands that modify your
112
- environment to make ChefDK your primary ruby. For more information to
113
- help you decide if this is desirable and instructions, see "Using ChefDK
114
- as Your Primary Development Environment" below.
97
+ environment to make ChefDK your primary ruby. It supports bash, zsh,
98
+ fish and PowerShell (posh). For more information to help you decide if
99
+ this is desirable and instructions, see "Using ChefDK as Your Primary
100
+ Development Environment" below.
115
101
 
116
102
  ### `chef install`
117
103
  `chef install` reads a `Policyfile.rb` document, which contains a
@@ -122,11 +108,7 @@ run list and locked cookbook set. The `Policyfile.lock.json` can be used
122
108
  to install the cookbooks on another machine. The policy lock can be
123
109
  uploaded to a Chef Server (via the `chef push` command) to apply the
124
110
  expanded run list and locked cookbook set to nodes in your
125
- infrastructure. The Policyfile feature is currently incomplete and of
126
- beta quality; changes to the Chef Server APIs will need to be
127
- implemented before the feature is production-ready. The feature
128
- currently operates in a compatibility mode. See the POLICYFILE_README.md
129
- for further details.
111
+ infrastructure. See the POLICYFILE_README.md for further details.
130
112
 
131
113
  ### `chef push`
132
114
  `chef push POLICY_GROUP` uploads a Policyfile.lock.json along with the cookbooks it
@@ -136,6 +118,31 @@ cookbook set. This command operates in compatibility mode and has the
136
118
  same caveats as `chef install`. See the POLICYFILE_README.md for further
137
119
  details.
138
120
 
121
+ ### `chef update`
122
+ `chef update` updates a Policyfile.lock.json with the latest cookbooks
123
+ from upstream sources. It supports an `--attributes` flag which will
124
+ cause only attributes from the Policyfile.rb to be updated.
125
+
126
+ ### `chef diff`
127
+ `chef diff` shows an itemized diff between Policyfile locks. It can
128
+ compare Policyfile locks from local disk, git, and/or the Chef Server,
129
+ based on the options given.
130
+
131
+ #### `chef verify`
132
+ `chef verify` tests the embedded applications. By default it runs a
133
+ quick "smoke test" to verify that the embedded applications are
134
+ installed correctly and can run basic commands. As an end user this is
135
+ probably all you'll ever need, but `verify` can also optionally run unit
136
+ and integration tests by supplying the `--unit` and `--integration`
137
+ flags, respectively.
138
+
139
+ *WARNING:* The integration tests will do dangerous things like start
140
+ HTTP servers with access to your filesystem and even create users and
141
+ groups if run with sufficient privileges. The tests may also be
142
+ sensitive to your machine's configuration. If you choose to run these,
143
+ we recommend to only run them on dedicated, isolated hosts (we do this
144
+ in our build cluster to verify each build).
145
+
139
146
  ### Using ChefDK as Your Primary Development Environment
140
147
 
141
148
  By default, ChefDK only adds a few select applications to your `PATH`
@@ -177,6 +184,38 @@ echo 'eval "$(chef shell-init SHELL_NAME)"' >> ~/.YOUR_SHELL_PROFILE
177
184
  Where `YOUR_SHELL_PROFILE` is `~/.bash_profile` for most bash users,
178
185
  `~/.zshrc` for zsh, and `~/.bashrc` on Ubuntu.
179
186
 
187
+ #### Powershell
188
+
189
+ You can use `chef shell-init` with PowerShell on Windows.
190
+
191
+ To try it in your current session:
192
+
193
+ ```posh
194
+ chef shell-init powershell | Invoke-Expression
195
+ ```
196
+
197
+ To enable it permanently:
198
+
199
+ ```posh
200
+ "chef shell-init powershell | Invoke-Expression" >> $PROFILE
201
+ ```
202
+
203
+ #### Fish
204
+
205
+ `chef shell-init` also supports fish.
206
+
207
+ To try it:
208
+
209
+ ```fish
210
+ eval (chef shell-init fish)
211
+ ```
212
+
213
+ To permanently enable:
214
+
215
+ ```fish
216
+ echo 'eval (chef shell-init SHELL_NAME)' >> ~/.config/fish/config.fish
217
+ ```
218
+
180
219
  ## Uninstallation Instructions
181
220
 
182
221
  ### Mac OS X
@@ -225,7 +264,7 @@ dpkg -P chefdk
225
264
 
226
265
  [Berkshelf]: http://berkshelf.com "Berkshelf"
227
266
  [Chef]: https://www.chef.io "Chef"
228
- [ChefDK]: https://www.chef.io/downloads/chef-dk "Chef Development Kit"
267
+ [ChefDK]: https://downloads.chef.io/chef-dk "Chef Development Kit"
229
268
  [Chef Documentation]: http://docs.chef.io "Chef Documentation"
230
269
  [ChefSpec]: http://chefspec.org "ChefSpec"
231
270
  [Foodcritic]: http://foodcritic.io "Foodcritic"
@@ -33,6 +33,8 @@ ChefDK.commands do |c|
33
33
 
34
34
  c.builtin "push", :Push, desc: "Push a local policy lock to a policy group on the server"
35
35
 
36
+ c.builtin "diff", :Diff, desc: "Generate an itemized diff of two Policyfile lock documents"
37
+
36
38
  c.builtin "export", :Export, desc: "Export a policy lock as a Chef Zero code repo"
37
39
 
38
40
  c.builtin "verify", :Verify, desc: "Test the embedded ChefDK applications"
@@ -0,0 +1,312 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2015 Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'chef-dk/command/base'
19
+ require 'chef-dk/ui'
20
+ require 'chef-dk/pager'
21
+ require 'chef-dk/policyfile/differ'
22
+ require 'chef-dk/policyfile/comparison_base'
23
+ require 'chef-dk/policyfile/storage_config'
24
+ require 'chef-dk/configurable'
25
+ require 'chef-dk/authenticated_http'
26
+
27
+ module ChefDK
28
+ module Command
29
+
30
+ class Diff < Base
31
+
32
+ include Configurable
33
+ include Policyfile::StorageConfigDelegation
34
+
35
+ banner(<<-BANNER)
36
+ Usage: chef diff [POLICYFILE] [--head | --git GIT_REF | POLICY_GROUP | POLICY_GROUP...POLICY_GROUP ]
37
+
38
+ `chef diff` displays an itemized diff comparing two revisions of a
39
+ Policyfile lock.
40
+
41
+ When the `--git` option is given, `chef diff` either compares a given
42
+ git reference against the current lockfile revision on disk or compares
43
+ between two git references. Examples:
44
+
45
+ * `chef diff --git HEAD`: compares the current lock with the latest
46
+ commit on the current branch.
47
+ * `chef diff --git master` compares the current lock with the latest
48
+ commit to master.
49
+ * `chef diff --git v1.0.0`: compares the current lock with the revision
50
+ as of the `v1.0.0` tag.
51
+ * `chef diff --git master...dev-branch` compares the Policyfile lock on
52
+ master with the revision on the `dev-branch` branch.
53
+ * `chef diff --git v1.0.0...master` compares the Policyfile lock at the
54
+ `v1.0.0` tag with the lastest revision on the master branch.
55
+
56
+ `chef diff --head` is a shortcut for `chef diff --git HEAD`.
57
+
58
+ When no git-specific flag is given, `chef diff` either compares the
59
+ current lockfile revision on disk to one on the server or compares two
60
+ lockfiles on the server. Lockfiles on the Chef Server are specified by
61
+ Policy Group. Examples:
62
+
63
+ * `chef diff staging`: compares the current lock with the one currently
64
+ assigned to the `staging` Policy Group.
65
+ * `chef diff production...staging` compares the lock currently assigned
66
+ to the `production` Policy Group to the lock currently assigned to the
67
+ `staging` Policy Group.
68
+
69
+ Options:
70
+ BANNER
71
+
72
+ option :git,
73
+ short: "-g GIT_REF",
74
+ long: "--git GIT_REF",
75
+ description: "Compare local lock against GIT_REF, or between two git commits"
76
+
77
+ option :head,
78
+ long: "--head",
79
+ description: "Compare local lock against last git commit",
80
+ boolean: true
81
+
82
+ option :pager,
83
+ long: "--[no-]pager",
84
+ description: "Enable/disable paged diff ouput (default: enabled)",
85
+ default: true,
86
+ boolean: true
87
+
88
+ option :config_file,
89
+ short: "-c CONFIG_FILE",
90
+ long: "--config CONFIG_FILE",
91
+ description: "Path to configuration file"
92
+
93
+ option :debug,
94
+ short: "-D",
95
+ long: "--debug",
96
+ description: "Enable stacktraces and other debug output",
97
+ default: false
98
+
99
+ attr_accessor :ui
100
+
101
+ attr_reader :old_base
102
+ attr_reader :new_base
103
+
104
+ attr_reader :storage_config
105
+
106
+ def initialize(*args)
107
+ super
108
+
109
+ @ui = UI.new
110
+
111
+ @old_base = nil
112
+ @new_base = nil
113
+ @policyfile_relative_path = nil
114
+ @storage_config = nil
115
+ @http_client = nil
116
+
117
+ @old_lock = nil
118
+ @new_lock = nil
119
+ end
120
+
121
+ def run(params = [])
122
+ return 1 unless apply_params!(params)
123
+ print_diff
124
+ 0
125
+ rescue PolicyfileServiceError => e
126
+ handle_error(e)
127
+ 1
128
+ end
129
+
130
+ def handle_error(error)
131
+ ui.err("Error: #{error.message}")
132
+ if error.respond_to?(:reason)
133
+ ui.err("Reason: #{error.reason}")
134
+ ui.err("")
135
+ ui.err(error.extended_error_info) if debug?
136
+ ui.err(error.cause.backtrace.join("\n")) if debug?
137
+ end
138
+ end
139
+
140
+ def print_diff
141
+ # eagerly evaluate locks so we hit any errors before we've entered
142
+ # pagerland. Also, git commands behave weirdly when run while the pager
143
+ # is active, doing this eagerly also avoids that issue
144
+ materialize_locks
145
+ Pager.new(enable_pager: config[:pager]).with_pager do |pager|
146
+ differ = differ(pager.ui)
147
+ differ.run_report
148
+ end
149
+ end
150
+
151
+ def differ(ui = ui())
152
+ Policyfile::Differ.new(old_name: old_base.name,
153
+ old_lock: old_lock,
154
+ new_name: new_base.name,
155
+ new_lock: new_lock,
156
+ ui: ui)
157
+ end
158
+
159
+ def http_client
160
+ @http_client ||= ChefDK::AuthenticatedHTTP.new(chef_config.chef_server_url,
161
+ signing_key_filename: chef_config.client_key,
162
+ client_name: chef_config.node_name)
163
+ end
164
+
165
+ def old_lock
166
+ materialize_locks unless @old_lock
167
+ @old_lock
168
+ end
169
+
170
+ def new_lock
171
+ materialize_locks unless @new_lock
172
+ @new_lock
173
+ end
174
+
175
+ def policy_name
176
+ local_lock["name"]
177
+ end
178
+
179
+ def local_lock
180
+ @local_lock ||= local_lock_comparison_base.lock
181
+ end
182
+
183
+ # ComparisonBase for the local lockfile. This is used to get the
184
+ # policy_name which is needed to query the server for the lockfile of a
185
+ # particular policy_group.
186
+ def local_lock_comparison_base
187
+ Policyfile::ComparisonBase::Local.new(policyfile_lock_relpath)
188
+ end
189
+
190
+ def policyfile_lock_relpath
191
+ storage_config.policyfile_lock_filename
192
+ end
193
+
194
+ def apply_params!(params)
195
+ remaining_args = parse_options(params)
196
+
197
+ if no_comparison_specified?(remaining_args)
198
+ ui.err("No comparison specified")
199
+ ui.err("")
200
+ ui.err(opt_parser)
201
+ false
202
+ elsif conflicting_args_and_opts_given?(remaining_args)
203
+ ui.err("Conflicting arguments and options: git and Policy Group comparisons cannot be mixed")
204
+ ui.err("")
205
+ ui.err(opt_parser)
206
+ false
207
+ elsif conflicting_git_options_given?
208
+ ui.err("Conflicting git options: --head and --git are exclusive")
209
+ ui.err("")
210
+ ui.err(opt_parser)
211
+
212
+ false
213
+ elsif config[:head]
214
+ set_policyfile_path_from_args(remaining_args)
215
+ @old_base = Policyfile::ComparisonBase::Git.new("HEAD", policyfile_lock_relpath)
216
+ @new_base = Policyfile::ComparisonBase::Local.new(policyfile_lock_relpath)
217
+ true
218
+ elsif config[:git]
219
+ set_policyfile_path_from_args(remaining_args)
220
+ parse_git_comparison(config[:git])
221
+ else
222
+ set_policyfile_path_from_args(remaining_args)
223
+ parse_server_comparison(remaining_args)
224
+ end
225
+ end
226
+
227
+ def parse_server_comparison(args)
228
+ comparison_string = args.last
229
+ if comparison_string.include?("...")
230
+ old_pgroup, new_pgroup, *extra = comparison_string.split("...")
231
+ @old_base, @new_base = [old_pgroup, new_pgroup].map do |g|
232
+ Policyfile::ComparisonBase::PolicyGroup.new(g, policy_name, http_client)
233
+ end
234
+
235
+ unless extra.empty?
236
+ ui.err("Unable to parse policy group comparison `#{comparison_string}`. Only 2 references can be specified.")
237
+ return false
238
+ end
239
+ else
240
+ @old_base = Policyfile::ComparisonBase::PolicyGroup.new(comparison_string, policy_name, http_client)
241
+ @new_base = Policyfile::ComparisonBase::Local.new(policyfile_lock_relpath)
242
+ end
243
+ true
244
+ end
245
+
246
+ def parse_git_comparison(git_ref)
247
+ if git_ref.include?("...")
248
+ old_ref, new_ref, *extra = git_ref.split("...")
249
+ @old_base, @new_base = [old_ref, new_ref].map do |r|
250
+ Policyfile::ComparisonBase::Git.new(r, policyfile_lock_relpath)
251
+ end
252
+
253
+ unless extra.empty?
254
+ ui.err("Unable to parse git comparison `#{git_ref}`. Only 2 references can be specified.")
255
+ return false
256
+ end
257
+ else
258
+ @old_base = Policyfile::ComparisonBase::Git.new(git_ref, policyfile_lock_relpath)
259
+ @new_base = Policyfile::ComparisonBase::Local.new(policyfile_lock_relpath)
260
+ end
261
+ true
262
+ end
263
+
264
+ def no_comparison_specified?(args)
265
+ !policy_group_comparison?(args) && !config[:head] && !config[:git]
266
+ end
267
+
268
+ def conflicting_args_and_opts_given?(args)
269
+ (config[:git] || config[:head]) && policy_group_comparison?(args)
270
+ end
271
+
272
+ def conflicting_git_options_given?
273
+ config[:git] && config[:head]
274
+ end
275
+
276
+ def comparing_policy_groups?
277
+ !(config[:git] || config[:head])
278
+ end
279
+
280
+ # Try to detect if the only argument given is a policyfile path. This is
281
+ # necessary because we support an optional argument with the path to the
282
+ # ruby policyfile. It would be easier if we used an option like `-f`, but
283
+ # that would be inconsistent with other commands (`chef install`, `chef
284
+ # push`, etc.).
285
+ def policy_group_comparison?(args)
286
+ return false if args.empty?
287
+ return true if args.size > 1
288
+ !(args.first =~ /\.rb\Z/)
289
+ end
290
+
291
+ def set_policyfile_path_from_args(args)
292
+ policyfile_relative_path =
293
+ if !comparing_policy_groups?
294
+ args.first || "Policyfile.rb"
295
+ elsif args.size == 1
296
+ "Policyfile.rb"
297
+ else
298
+ args.first
299
+ end
300
+ @storage_config = Policyfile::StorageConfig.new.use_policyfile(policyfile_relative_path)
301
+ end
302
+
303
+ def materialize_locks
304
+ @old_lock = old_base.lock
305
+ @new_lock = new_base.lock
306
+ end
307
+
308
+ end
309
+
310
+ end
311
+ end
312
+