So, I didn't think of any of this until just about the time that we released the environments feature, which was way too late to do anything about it. Anyway, here's my alternate conception of environments.
- Cookbooks always get their version from a checksum of their contents. The way the checksum is generated isn't particularly important, but it's probably a checksum of all the individual files' checksums, like a Merkle tree without the tree.
knife cookbook upload
always uploads to a particular environment. The command line invocation can be the same as it is now, or more git-like, e.g.,knife cookbook upload ENV COOKBOOK
. The "_default" environment is assumed if no environment is given.knife cookbook upload
always constrains the environment to that exact version of the cookbook.
- You can't have two active versions of a cookbook in a single environment. If cookbook A needs cookbook Z at version 1 and cookbook B needs cookbook Z at version 2, and you need both A and B to run your infrastructure, then you need to modify one or more of the cookbooks to fix the incompatibility.
- The x.y.z version field of cookbook data may no longer have meaning to the chef-server, as environments would only care about the cookbook's checksum version. See next bullet point for discussion of how version constraints might be handled.
- Versioned dependencies in cookbook metadata must either be ignored, enforced by chef-client after fetching cookbook updates, or verified by the server upon upload. Each of these approaches has unfavorable trade-offs. Enforcing version constraints on the client side adds considerable delay between when a user causes and error and when they detect it; enforcing version constraints on the server may make it annoying to upload multiple cookbooks with "interlocking" version constraints. Ignoring version constraints entirely will likely confuse users who expect version constraints to be respected and potentially allow conflicting cookbook versions to be used together.
- Environments become less versionable (i.e., it's more difficult to track them as files in git); most likely, you'd upload environment attributes separately from version restrictions, which would in many cases be changing pretty frequently.
- Environments always constrain a cookbook to exactly one version. There's no greater than/less than/pessimistic greater than.
- Compared to tracking an environment as a file in a revision controlled repo, it's trickier to snapshot what version of everything is in use at a given time. Some tooling could help, for example by tagging a git repo with a cookbook's checksum at a particular commit.
- Every cookbook is always frozen, since if it changes it'll have a different checksum. Since cookbooks are individually added to environments, there's much less chance of a dev cookbook being pushed to production.
- Workflow is much simpler. You push a cookbook to the environment where
you want it to go. Compare to editing environment files or JSON or
setting up Ci to compile an environment. Even if you use
-E
and--freeze
withknife cookbook upload
you still have to fiddle the cookbook's version numbers by hand. - More efficient on the server-side: the current implementation requires chef-server to load every version of every cookbook (including dependency information) to compute the correct solution to the various version constraints. This uses lots of database IO and memory, and the resource requirements increase as you have more cookbook versions.
- It's easy to understand what version of a cookbook will be used on a
node. If you add a dependency "B" to cookbook "A", then upload a new
version of "A" without uploading any version of "B", chef-server will
act as if the new version of "A" doesn't exist, because its
dependencies can't be satisfied. Though
knife cookbook upload
guards against this particular case, I've observed similar "why isn't the new version of cookbook X being used" issues that could never be traced to a particular cause. - It's efficient to add a concept of promotion of cookbooks between environments, since you're simply copying one cookbook's checksum to a different environment's constraints.
There are two possibilities for selecting the final set of version constraints in an environment. The first option is an overlay model, where any cookbooks without explicit constraints fall back to using the "_default" environment's constraints. The second is to explicitly version every cookbook in every environment.
In the first model (fallback to default), the version constraints of the "_default" environment are mutable, but only by the Chef server; the Chef server will set them whenever a cookbook is uploaded with no explicit environment.
The set of version constraints is then computed by overlaying the constraints of the node's environment, e.g., "production" on top of the "_default" environment's constraints.
The downside to this model is that the user could introduce a cookbook that should be version constrained into, say, production without setting a constraint. A future update to the non-constrained cookbook could then break production.
In the second model, a cookbook without a constraint in a particular environment would appear not to exist in that environment. This fixes the accidental update issue. On the other hand, it adds friction to user interactions around creating new cookbooks, since a new cookbook has to be added to each environment.
- Add promotion as a first class concept at either the UX level or API level. Single cookbook, multiple cookbook (e.g., single cookbook plus dependencies), and whole environment promotion should be possible.
- Checksums don't sort temporally, so there needs to be automatic metadata to help track/sort which version is when. Timestamp, creator, scm id, etc. could all help here.
- Checksums don't convey semantic meaning, so they should be displayed along with automatic metadata in UIs. Highlighting which versions are assigned to a particular environment would also be helpful.
I would love to see versions of cookbooks generated as a digest of relevant (or all?) metadata. However we also regularly use multiple versions of the same cookbook in the same environment. It is not uncommon for us to lock a top level cookbook in the environment file and have it explicitly list the versions of it's dependencies. It would make QA significantly harder if we had to test upgrades of one cookbook across multiple application stacks just to support an upgrade in one application stack. It would be nice to be able to lock the versions of dependent cookbooks on upload - effectively uploading a dependency tree starting from a/some top-level cookbooks (TLCs), much like the model of git. It would also be nice to be able to promote the TLCs across environments and take all their dependencies across.
We currently model environments as that traditional environment notions prod, staging, uat, integration-test, dev and chef-dev. We have about 11 different stacks and several teams all independently evolving their stacks. Under the new regime we would end up having to model 80-90 environments just to maintain effective change control. This does not seem right.
This proposal seems to be doing is creating a new feature and giving it the same name as an existing one. I agree with @yfeldblum - environments have no clear meaning and I don't think this approach solves that. I would also hate to lose the tools to effectively model complex scenarios.