chef-dk 0.5.0.rc.1 → 0.5.0

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.
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
+