In our last post we discussed how to package our Chef Cookbooks in a Habitat package using Policyfiles. We showed that we could package all of our cookbooks into a single, easily distributable, package.
We were even able to set node attributes via the Policyfile enabling us to create Policyfile specific settings. However, we don’t want to manage a dozen different versions of the resolver package since the only thing that changes is node attributes.
For instance, all of our servers use the same DNS server but will have a different domain based on the data center they live in. We’ll use qubitrenegade.com
, so suppose we have us-east-1.qubitrenegade.com
and us-west-1.qubitreengade.com
.
Setup
First, let’s update our base policy file. Let’s add a domain
attribute to our default
settings.
--- a/policyfiles/base.rb
+++ b/policyfiles/base.rb
@@ -4,7 +4,10 @@ name 'base'
default_source :supermarket
# Environment Attributes
-default['resolver']['nameservers'] = ['1.1.1.1']
+default['resolver'] = {
+ 'domain' = 'qubitrenegade.com',
+ 'nameservers' = ['1.1.1.1'],
+}
Multiple Policyfiles
Since policy files include other policy files, we could create a new policy for each environment.
First, let’s update our Habitat plan.sh
to include the policy name in the name of our package.
--- a/habitat/plan.sh
+++ b/habitat/plan.sh
@@ -1,6 +1,11 @@
# habichef_demo/habitat/plan.sh
-scaffold_policy_name="base"
-pkg_name="habichef_demo"
+if [ -z ${CHEF_POLICYFILE+x} ]; then
+ policy_name="base"
+else
+ policy_name="${CHEF_POLICYFILE}"
+fi
+
+pkg_name="habichef_demo-${policy_name}"
Next, let’s create a Policyfile for us-east-1
that includes our base Policyfile.
# habichef_demo/policyfiles/us-east-1.rb
name 'us-east-1'
include_policy 'base'
default['resolver']['domain'] = 'us-east-1.qubitrenegade.com'
Then we can build our project, using the same process as before. (If you’re receiving errors, you can check out the 2019-04-28-pt1-multiple-policy-files
branch of the HabiChef Demo repo.
$ hab studio enter
... snip ...
[1][default:/src:0]# build
... snip ...
habichef_demo-base: Installed Path: /hab/pkgs/qbrd/habichef_demo-base/0.1.0/20190428185221
habichef_demo-base: Artifact: /src/results/qbrd-habichef_demo-base-0.1.0-20190428185221-x86_64-linux.hart
habichef_demo-base: Build Report: /src/results/last_build.env
habichef_demo-base: SHA256 Checksum: 4a126ec5c47dba1643ef767c7ccc02acd36ea780d2f88110db9c964c4598c2b9
habichef_demo-base: Blake2b Checksum: 7fc45fcf07bebe57f90301c0c83df6700f772fc8925ec0162b1efbfa85b89b6a
habichef_demo-base:
habichef_demo-base: I love it when a plan.sh comes together.
habichef_demo-base:
habichef_demo-base: Build time: 0m5s
[2][default:/src:0]#
However, note that the name of our package is habichef_demo-base
. We need to set `
CHEF_POLICYFILE` environment variable to build the correct package.
[2][default:/src:0]# CHEF_POLICYFILE=us-east-1 build
... snip ...
Building policy us-east-1
Error: Failed to generate Policyfile.lock
Reason: (ChefDK::LocalPolicyfileLockNotFound) The provided path ./base.lock.json does not exist.
Well, it seems that something is blowing away the base.lock.json
file… This probably needs follow up with the Habitat team.
UPDATE: #2536 indicates I may have misunderstood the usage pattern.
Regardless, this example starts to break down as you start to deploy to more environments as you have effectively the same package being generated with minor changes.
user.toml
Config Changes
Let’s continue working with habichef_demo
package.
If you’ve been following along, we’ll want to explicitly set our scaffold_policy_name="base"
--- a/habitat/plan.sh
+++ b/habitat/plan.sh
@@ -5,8 +5,8 @@ else
policy_name="${CHEF_POLICYFILE}"
fi
-scaffold_policy_name="${policy_name}"
-pkg_name="habichef_demo-${policy_name}"
+scaffold_policy_name="base"
+pkg_name="habichef_demo"
(You can also remove the conditional statement, or just check out the 2019-04-28-pt2-user.toml
branch)
Next, let’s create our user.toml
file. Habitat will load user config settings from /hab/user/<service name>/config/user.toml
.
[15][default:/src:130]# mkdir -p /hab/user/habichef_demo/config/
[16][default:/src:0]# printf '[attributes]\n [attributes.resolver]\n domain = "us-east-1.qubitrenegade.com"\n' | tee /hab/user/habichef_demo/config/user.toml
[attributes]
[attributes.resolver]
domain = "us-east-1.qubitrenegade.com"
Then we’ll build and run the package.
$ hab studio enter
... snip ...
[1][default:/src:0]# build
... snip ...
habichef_demo: Installed Path: /hab/pkgs/qbrd/habichef_demo/0.1.0/20190428192425
habichef_demo: Artifact: /src/results/qbrd-habichef_demo-0.1.0-20190428192425-x86_64-linux.hart
habichef_demo: Build Report: /src/results/last_build.env
habichef_demo: SHA256 Checksum: b0016e36ae76f77e876e27681219eae415eb84ef5801813d83fbd56c46163099
habichef_demo: Blake2b Checksum: 1be21e1176536917f28b50f3b2856acdc7ad51116009c8aee23ef7bb48708880
habichef_demo:
habichef_demo: I love it when a plan.sh comes together.
habichef_demo:
habichef_demo: Build time: 0m7s
[2][default:/src:0]# source results/last_build.env
[3][default:/src:0]# hab svc load $pkg_ident
[4][default:/src:0]# sup-log
... snip ...
habichef_demo.default(O): +# This file is generated by Chef
habichef_demo.default(O): +# Do not edit, changes will be overwritten
habichef_demo.default(O): +#
habichef_demo.default(O): +domain us-east-1.qubitrenegade.com
habichef_demo.default(O): +nameserver 1.1.1.1
As you can see, you can set per-instance settings via user.toml
. Typically this is populated by a third-party tool such as Chef (though in the case of HabiChef, is it the chicken or the egg?), or Terraform. Terraform is particularly useful when coupled with the Habitat provisioner.
Running config changes
We’ve been running HabiChef for a few weeks now and have several nodes deployed (possibly even with a bastion ring, but that is a topic for another day). A mandate comes down from management that we need to change all of the domains. Sure, we could log into every node and update the user.toml
file… but that seems like a lot of work and rather error prone.
Fortunately, Habitat provides a method for applying config changes to a running cluster.
First, let’s create a temporary .toml
file. (There is an example file in the habichef_demo/habitat
directory)
# /tmp/config-demo.toml
[attributes]
[attributes.resolver]
domain = "example.local"
Then we can apply it with:
hab config apply <SERVICE_GROUP> <VERSION_NUMBER> <FILE>
We’ll use a timestamp generated with $(date +%s)
as the version number. This way when we apply the update again we don’t have to modify the command.
Our command should look something like:
hab config apply habichef_demo.default $(date +%s) habitat/config-apply-demo.toml
And we should see something to the effect of:
[2][default:/src:0]# hab config apply habichef_demo.default $(date +%s) habitat/config-apply-demo.toml && sup-log
... snip ...
hab-sup(CMD): Setting new configuration version 1556489698 for habichef_demo.default
habichef_demo.default(CF): Modified configuration file /hab/svc/habichef_demo/config/attributes.json
... snip ...
habichef_demo.default(O): * template[/etc/resolv.conf] action create
habichef_demo.default(O): - update content in file /etc/resolv.conf from 217c99 to 19c14a
habichef_demo.default(O): --- /etc/resolv.conf 2019-04-28 19:44:54.719939924 +0000
habichef_demo.default(O): +++ /etc/.chef-resolv20190428-32280-15wxpjo.conf 2019-04-28 22:15:11.311781489 +0000
habichef_demo.default(O): @@ -2,7 +2,7 @@
habichef_demo.default(O): # This file is generated by Chef
habichef_demo.default(O): # Do not edit, changes will be overwritten
habichef_demo.default(O): #
habichef_demo.default(O): -domain asdf.com
habichef_demo.default(O): +domain example.local
Success! Typically these changes would be applied to a cluster, and the update propagated through the cluster via Habitat’s inbuilt gossip layer. So if you’re thinking this is a lot of work to change the domain in the resolv.conf
file, you’re not wrong.
Conclusion
Today we’ve seen three different ways (well, two and a half) to apply changes to HabiChef cookbooks. Big picture, you’d probably want to manage these via some form of CI pipeline, and discourage users from manually hab config apply
ing changes. Hopefully the gravity of being able to apply config changes to a cluster will become more apparent in the “Habitat: PtX Running at Scale”.
Tune in next time when we discuss the internals of scaffolding-chef
and how to include local cookbooks.