Umbrella Project: Automate
Project State: Deprecated
Issues Response Time Maximum: None
Pull Request Response Time Maximum: None
Delivery Sugar is a library cookbook that includes a collection of helpful sugars and custom resources that make creating build cookbooks for Chef Delivery projects a delightful experience.
If you are using Berkshelf, add delivery-sugar
to your Berksfile
:
cookbook 'delivery-sugar'
In order to use Delivery Sugar in your build cookbook recipes, you'll first
need to declare your dependency on it in your metadata.rb
.
depends 'delivery-sugar'
Declaring your dependency will automatically extend the Recipe DSL,
Chef::Resource
and Chef::Provider
with helpful methods. It will also
automatically make available the custom resources included with Delivery Sugar.
There is no need to include Delivery Sugar in any of your recipes
The following are DSL helper methods available to you when you include Delivery Sugar in your build cookbook.
Helpers that can assist you in detecting and communicating with the larger Automate environment.
automate_knife_rb
The path to the knife config that can communicate with the Automate Chef Server.
Default Value: /var/opt/delivery/workspace/.chef/knife.rb
automate_chef_server_details
Cheffish details you can pass into Provisioning or Cheffish resources (i.e
chef_environment
).
build_user
The name of the local user executing the job (e.g. dbuild
).
Helpers that provide the paths to the relevant workspace directories on the build node.
workflow_workspace
The path to the shared workspace on the Build Nodes. This workspace is shared
across all organizations and projects. In this directory are things like
builder keys, ssh wrappers, etc. Default Value: /var/opt/delivery/workspace
workflow_workspace_repo
The path to the root of your project's code repository on the the build node.
workflow_workspace_chef
The path to the directory where the chef-client run associated with the phase job is executed from.
workflow_workspace_cache
The path to a cache directory associated with this phase run.
workflow_workspace_root
The parent directory of repo, chef, and cache.
workflow_stage
The name of the stage currently being executed (i.e. verify, build, etc).
workflow_phase
The name of the phase currently being executed (i.e. unit, lint, etc)
workflow_chef_environment_for_stage
The name of the Chef Environment associated with the current stage.
workflow_project_acceptance_environment
The name of the Chef Environment associated with the Acceptance stage for this project.
Details that are specific to the current change.
workflow_change_enterprise
The name of the Automate enterprise associated with the change.
workflow_change_organization
The name of the Automate organization associated with the change.
workflow_change_project
The name of the Automate project associated with the change.
workflow_change_pipeline
The name of the Automate pipeline associated with the change.
workflow_change_id
The Change ID associated with the current phase run.
workflow_change_merge_sha
The merge SHA associated with the current change. Will be null
for phases in
the Verify stage.
workflow_change_patchset_branch
The name of the branch originally given to the change when it was submitted for review.
changed_cookbooks
Returns an array of DeliverySugar::Cookbook
objects for each cookbook that
was modified in the current change.
changed_files
Returns a list of all the files modified in the current change. File names are scoped to the project root.
changed_dirs
Returns a list of all the directories modified in the current change. Optionally provide an integer to specify the desired directory depth.
change_log
Returns a list of commits from the SCM log in reverse chronological order.
workflow_project_slug
Returns a unique string that can be used to identify the current project.
Format: <ENTERPRISE>-<ORGANIZATION>-<PROJECT>
workflow_organization_slug
Returns a unique string that can be used to identify the organization associated with the current project.
Format: <ENTERPRISE>-<ORGANIZATION>
workflow_enterprise_slug
Returns a unique string that can be used to identify the current enterprise.
Format: <ENTERPRISE>
Sometimes you need to perform actions in your build cookbook as though it was
running against a Chef Server. To do this, you can use the with_server_config
DSL. Behind the scenes, during the compile phase of the chef client run, we
temporarily modify the Chef::Config
object to point towards Automate's Chef
Server. Here's an example of us running a node search against the Automate
Chef Server to find a specific node.
with_server_config do
search(:node, 'role:web',
:filter_result => { 'name' => [ 'name' ],
'ip' => [ 'ipaddress' ],
'kernel_version' => [ 'kernel', 'version' ]
}
).each do |result|
puts result['name']
puts result['ip']
puts result['kernel_version']
end
end
We have noticed that in some use cases, the with_server_config
DSL does not
work for some users because with_server_config
only modifies the Chef::Config
object during the initial compilation of the resource collection, not
during the execution phase. If you run into issues with things like automate_chef_server_details
not working for you, you may need to use the DSL run_recipe_against_automate_chef_server
instead. Rather than restoring the initial Chef::Config
after compilation,
run_recipe_against_automate_chef_server
leaves the Chef::Config
object configured
with the Automate Chef Server details for the entire chef run. We strongly
encourage that you use run_recipe_against_automate_chef_server
only as a last resort.
delivery_supermarket
With this new resource you can easily share your cookbook to Supermarket by just calling:
delivery_supermarket 'share_cookbook' do
site 'https://my-private-supermarket.example.com'
end
That will take all the defaults from Delivery. It means that if you are
sharing a cookbook to your Private Supermarket it will use the delivery
credentials that the cluster is linked to.
If you want to customize your resource you can use more attributes:
secrets = get_project_secrets
delivery_supermarket 'share_custom_cookbook' do
config '/path/to/my/knife.rb'
cookbook 'my_cookbook'
category 'Applications'
path '/path/to/my/cookbook/on/disk/my_cookbook'
user secrets['supermarket_user']
key secrets['supermarket_key']
action :share
end
Note that by not specifying the site
you will be publishing to the Public
Supermarket.
Find a list of available categories here.
The resource delivery_terraform
will allow your projects to use Terraform
in order to provision ephemeral nodes.
More on that topic here
The resource delivery_test_kitchen
will enable your projects to use Test Kitchen
in Delivery. Currently, we support: kitchen-ec2 driver and kitchen-azurerm, kitchen-dokken, and chef-provisioning-vsphere drivers.
In order to enable this functionality, perform the following prerequisite steps:
Add the following items to the appropriate data bag as specified in the Handling Secrets section
delivery-secrets
{
"id": "<ent>-<org>-<project>",
"ec2": {
"access_key": "<ec2-access-key>",
"secret_key": "<ec2-secret-key>",
"keypair_name": "<ec2-keypair-name>",
"private_key": "<JSON-compatible-ec2-keypair-private-key-content>"
}
}
You can convert the private key content to a JSON-compatible string with a command like this:
ruby -e 'require "json"; puts File.read("<path-to-ec2-private-key>").to_json'
Customize your kitchen YAML file with all the required information needed by the kitchen-ec2 driver driver. delivery-sugar will expose the following ENV variabls for use by kitchen:
KITCHEN_INSTANCE_NAME
- set to the <project-name>-<change-id>
values provided by delivery-cliKITCHEN_EC2_SSH_KEY_PATH
- path to the SSH private key created from the delivery-secrets data bagThese variables may be used in your kitchen YAML like the following example:
---
driver:
name: ec2
region: us-west-2
availability_zone: a
instance_type: t2.micro
image_id: ami-5189a661
subnet_id: subnet-19ac017c
tags:
Name: <%= ENV['KITCHEN_INSTANCE_NAME'] || 'delivery-kitchen-instance' %>
transport:
ssh_key: <%= ENV['KITCHEN_EC2_SSH_KEY_PATH'] %>
provisioner:
name: chef_zero
platforms:
- name: ubuntu-14.04
suites:
- name: default
run_list:
- recipe[test-build-cookbook::default]
attributes:
Ensure you have set up a Service Principal in Azure according to the kitchen-azurerm README
Additionally at this point, installing the kitchen-azurerm
requires build tools on the build nodes. You will need to customize your build cookbook as follows:
depends 'build-essential', '~> 7.0.2'
to the metadata.rb
of the build cookbook.include_recipe 'build-essential::default'
to the default.rb
of the build cookbook.Add the following items to the appropriate data bag as specified in the Handling Secrets section
delivery-secrets
{
"id": "<ent>-<org>-<project>",
"azurerm": {
"subscription_id": "<YOUR-SUBSCRIPTION-ID-HERE>",
"client_id": "<48b9bba3-YOUR-GUID-HERE-90f0b68ce8ba>",
"client_secret": "<your-client-secret-here>",
"tenant_id": "<9c117323-YOUR-GUID-HERE-9ee430723ba3>"
}
}
Customize your kitchen YAML file with all the required information needed by the kitchen-azurerm driver driver. For example:
---
driver:
name: azurerm
driver_config:
subscription_id: 'YOUR-SUBSCRIPTION-ID-HERE'
location: 'West Europe'
machine_size: 'Standard_D1'
transport:
ssh_key: ~/.ssh/id_kitchen-azurerm
provisioner:
name: chef_zero
verifier:
name: inspec
platforms:
- name: ubuntu-14.04
driver_config:
image_urn: Canonical:UbuntuServer:14.04.4-LTS:latest
vm_name: trusty-vm
suites:
- name: default
run_list:
- recipe[azure_test::default]
verifier:
inspec_tests:
- test/recipes
attributes:
You can leverage the kitchen-dokken driver in your tests
as well. This does not require the use of delivery-secrets
. To enable kitchen-dokken
, do the following to
install Docker on all of your builders/runners:
Add depends 'docker', '~> 2.0'
to the metadata.rb
of the build cookbook.
Add the following code to the default.rb
of the build cookbook:
docker_service 'default' do
action [:create, :start]
end
group 'docker' do
action :modify
members 'dbuild'
append true
end
Ensure you have a vCenter and valid login credentials according to the chef-provisioning-vsphere README
Add the following items to the appropriate data bag as specified in the Handling Secrets section
delivery-secrets
{
"id": "<ent>-<org>-<project>",
"vsphere": {
"insecure": true | false,
"host": "<your-vsphere-host-here>",
"user": "<your-vsphere-username-here>",
"password": "<your-vsphere-password-here>"
}
}
Once you have the prerequisites you can use delivery_test_kitchen
anywhere in your project pipeline, you
just need to call the resource within your build-cookbook of your project.
Trigger a kitchen test using Ec2 driver
delivery_test_kitchen 'functional_test' do
driver 'ec2'
end
Trigger a kitchen converge & destroy action using Ec2 driver and pointing to .kitchen.ec2.yml
file inside the repository path in Delivery.
delivery_test_kitchen 'quality_converge_destroy' do
yaml '.kitchen.ec2.yml'
driver 'ec2'
repo_path delivery_workspace_repo
action [:converge, :destroy]
end
Trigger a kitchen create passing extra options for debugging
delivery_test_kitchen 'unit_create' do
driver 'ec2'
options '--log-level=debug'
suite 'default'
action :create
end
Trigger a kitchen create extending the timeout to 20 minutes
delivery_test_kitchen 'unit_create' do
driver 'ec2'
suite 'default'
timeout 1200
action :create
end
Trigger a kitchen create injecting arbitrary environment variables
delivery_test_kitchen 'unit_create' do
driver 'ec2'
suite 'default'
environment('TK_EC2_REGION' => 'us-west-2', 'TK_MACHINE_SIZE' => 't2.micro')
action :test
end
This assumes your .kitchen.yml
is leveraging environment variables e.g.:
driver:
name: ec2
region: <%= ENV['TK_EC2_REGION'] %>
instance_type: <%= ENV['TK_INSTANCE_TYPE'] %>
The resource delivery_inspec
will enable your projects to run any InSpec tests in the cookbook against your nodes in Acceptance, Union, Rehearsal, or Delivered. Currently, we only support running tests against Linux or Windows nodes.
In order to enable this functionality, perform the following prerequisite steps:
Add the following items to the appropriate data bag as specified in the Handling Secrets section
delivery-secrets
{
"id": "<ent>-<org>-<project>",
"inspec": {
"ssh-user": "inspec",
"ssh-private-key": "<YOUR-PRIVATE-KEY-HERE",
"winrm-user": "inspec",
"winrm-password": "<YOUR-PASSWORD-HERE>"
}
}
You can convert the private key content to a JSON-compatible string with a command like this:
ruby -e 'require "json"; puts File.read("<path-to-inspec-private-key>").to_json'
Ensure that the associated user for either ssh-user
or winrm-user
exists on the nodes to be tested, with either the public key added to authorized_keys
(if Linux), or the password set (if Windows). The associated user must either have passwordless sudo, or be in the Administrators group (if Windows).
Note that the delivery_inspec
resource also supports "organization-level" data bag items, so the above item could also be set at "id": "<ent>-<org>"
.
Trigger InSpec testing as follows
search_query = "recipes:#{node['delivery']['change']['project']}* AND " \
"chef_environment:#{delivery_environment}"
nodes = delivery_chef_server_search(:node, search_query.to_s)
nodes.each do |i_node|
delivery_inspec "inspec_#{node['delivery']['change']['project']}" do
infra_node i_node['ipaddress']
os i_node['os']
end
end
The default value for tests are in the test/recipes
directory of your cookbook, but you can over-ride it with the optional inspec_test_path
parameter. For example:
delivery_inspec "run_inspec" do
infra_node '10.0.0.1'
os 'windows'
inspec_test_path 'test/smoke'
end
This cookbook implements a rudimentary approach to handling secrets. This process is largely out of band from Chef Automate for the time being.
get_project_secrets
Your build cookbook will look for secrets in the delivery-secrets
data bag on the
Delivery Chef Server. It will expect to find an item in that data bag named
<ent>-<org>-<project>
. For example, lets imagine a cookbook called 'delivery-test'
that is kept in the 'open-source' org of the 'chef' enterprise so it's data bag name
would be chef-open-source-delivery-test
.
This cookbook expects this data bag item to be encrypted with the same encrypted_data_bag_secret that is on your builders. You will need to ensure that the data bag is available on the Chef Server before you run this cookbook for the first time otherwise it will fail.
To get this data bag you can use the DSL get_project_secrets
to get the
contents of the data bag.
my_secrets = get_project_secrets
puts my_secrets['id'] # chef-Delivery-Build-Cookbooks-delivery-truck
If the project item does not exist, delivery-sugar will try to load the secrets
of the organization that your project lives in. It will look for an item called
<ent>-<org>
. For the same example above it would be chef-open-source
. This is
useful if you would like to share secrets across projects within the same organization.
get_chef_vault_data
Using the DSL method get_chef_vault_data
will return a merged Ruby hash from the
Chef Vaults in workflow-vaults
on your Automate Chef Server.
In order to use this DSL method you must use the following naming standard for your
Chef Vault items under the workflow_vaults
vault:
#{ent_name}
#{ent_name}-#{org_name}
#{ent_name}-#{org_name}-#{project_name}
The data in these vaults will be merged into a single Ruby hash. Any duplicate key names will be merged as follows:
#{ent_name}-#{org_name}-#{project_name}
will overwrite #{ent_name}-#{org_name}
and #{ent_name}
.#{ent_name}-#{org_name}
will overwrite #{ent_name}
.You can access the data like so:
vault_data = get_chef_vault_data
puts vault_data['my_key']
Example Creation of the workflow_vaults
Chef Vault and a vault item for the following:
brewinc
breworg
mysql-server
$ cat tmp/secrets.json
{
"id": "brewinc-breworg-mysql-server",
"openstack-password": "secret-password"
}
$ knife vault create workflow-vaults brewinc-breworg-mysql-server -S "name:automate_runner**" -A "delivery,admin" -J tmp/secrets.json -M client
NOTE: We recommend to have always the latest version of ChefDK installed on your Runners.
Copyright:: 2015 Chef Software, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.