chef-gen-flavors

DESCRIPTION

chef-gen-flavors is a framework for creating custom templates for the 'chef generate' command provided by ChefDK.

This gem simply provides a framework; templates are provided by separate gems, which you can host privately for use within your organization or publicly for the Chef community to use.

chef-gen-flavor-base is a base class that makes it easy to compose a flavor from reusable snippets of functionality, and using it is highly recommended. Using chef-gen-flavors on its own is only suitable if you already have a template which is a copy of the skeleton provided by ChefDK.

At present this is focused primarily on providing templates for generation of cookbooks, as this is where most organization-specific customization takes place. Support for the other artifacts that ChefDK can generate may work, but is not the focus of early releases.

BREAKING API CHANGES

v0.9.0 of chef-gen-flavors is very different from chef-gen-flavors v0.8.x. Flavors will need to be re-worked, and the base flavor class and associated snippets have been extracted to their own gem.

v0.8.6 contained a warning about this change; if you are not ready to upgrade your flavor you can pin to this version to keep the old API.

INSTALLATION

You should not install this gem directly; it will be installed as a dependency of a flavor gem. If you are developing a flavor, declare this gem a dependency of your gem in your gemspec file:

Gem::Specification.new do |s|
  s.name = 'chef-gen-flavor-awesome'
  s.add_runtime_dependency('chef-gen-flavors', ['~> 0.9'])
  ...
end

Then make bundler read your gemspec:

source 'https://rubygems.org/'
gemspec

And use bundler to install your dependencies:

$ bundle

PREREQUISITES

This gem requires that you have ChefDK (at least version 0.3.6) installed. chef-dk is not a dependency of this gem because chef-dk should always be installed using the omnibus packages provided by Chef, not as a gem.

CONFIGURATION

In your knife.rb file, add this snippet:

# only load ChefGen::Flavors if we're being called from the ChefDK CLI
if defined?(ChefDK::CLI)
  require 'chef_gen/flavors'
  chefdk.generator_cookbook = ChefGen::Flavors.path
end

When you run chef generate, all available plugins will be loaded. If more than one plugin is found, you will be prompted as to which you want to use:

$ chef generate cookbook my_app

If you set the environment variable CHEFGEN_FLAVOR to the name of a plugin, it will be chosen instead of presenting a prompt:

$ CHEFGEN_FLAVOR=mytemplate chef generate cookbook my_app

USING THE BUILT-IN CHEFDK TEMPLATE

By default, this gem does not offer the built-in ChefDK template as an option. By setting the environment variable CHEFDK_FLAVOR, the option builtin will be offered.

FLAVORS

This gem uses little-plugger to make adding template flavors easy. Each flavor is defined by a plugin named using little-pluggers's rules.

The plugin must define a class inside the naming hierarchy ChefGen::Flavor::. The class name should be the filename converted to CamelCase (e.g. foo_bar.rb = FooBar)

The name of the class must not be in all caps, as little-plugger ignores these (assuming that they are constants).

You do not have to require your plugin; little-plugger searches all installed gems for files matching the globspec.

Flavor classes must also:

  • define a NAME constant which is the name of the flavor in lowercase
  • provide an #add_content method, which is responsible for copying content to the temporary directory created by chef-gen-flavors

EXAMPLE FLAVOR STRUCTURE

This example defines a flavor named Example. It can only generate cookbooks, as its code_generator cookbook contains no other recipes.

A functional copy of this plugin is available on rubygems as chef-gen-flavor-example.

The directory structure of a plugin looks like this:

chef-gen-flavor-example
├── lib
│   └── chef_gen
│       └── flavor
│           └── example.rb
└── shared
    └── flavor
        └── example
            ├── .rb
            └── recipes
                └── cookbook.rb

It is important that the name of the flavor matches the name in metadata.rb. ChefDK uses the last element of the path as the cookbook name, so if you put your cookbook in a folder called 'cookbook' but your metadata.rb declares the name as 'example', ChefDK won't be able to find your recipe.

COPYING CONTENT

When #path is called, chef-gen-flavors creates a temporary directory, which is passes as an argument to the flavor class' constructor:

class ChefGen
  module Flavor
    class Awesome
      NAME = 'awesome'

      def initialize(temp_path:)
        @temp_path = temp_path
      end
    end
  end
end

The flavor is responsible for copying everything required to run generation to that path by defining a #add_content method. For a simple flavor, that might be just a recursive copy:

class ChefGen
  module Flavor
    class Awesome
      ...
      def add_content
        FileUtils.cp_r(
          File.expand_path(
            File.join(
              File.dirname(__FILE__),
              '..', '..', '..', 'shared', 'flavor', NAME
            )
          ) + '/.',
          @temp_path
        )
      end
    end
  end
end

Once the add_content method has been called, it is returned as the value of chefdk.generator_cookbook, and generation proceeds as if you had given chef generate and alternate path by passing -g.

This path is cleaned up at exit unless the environment variable CHEFGEN_NOCLEANTMP is set.

When using ChefGen::FlavorBase, there are helper functions to compose the temporary directory from files that come from a mix of flavors and reusable components.

FEATURE TESTING FLAVORS

chef-gen-flavors provides a number of useful step definitions for Aruba (a CLI driver for Cucumber) to make it easier to test flavors. To access these definitions, add the following line to your features/support/env.rb file:

require 'chef_gen/flavors/cucumber'

For an example of how to use these steps in your features, refer to the reference implementation of a flavor: chef-gen-flavor-example.

Documentation for the steps themselves is in the file ARUBA_STEPS.md

AUTHOR

James FitzGibbon

LICENSE

Copyright 2015 Nordstrom, Inc.

Copyright 2015 James FitzGibbon

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.