Configure providers from variables, in a generic w

2019-08-15 23:58发布

问题:

How can I create a recipe that will populate its attributes using the fiels from an instance of an object in a generic way?

As an example, consider the following recipe:

component = $auth_docker
docker_image component.name do
    registry component.registry
    tag component.tag
    action :pull
end

When you have 50s of recipes that look like this, maintaining them really gets overwhelming.

In Python, i would probably implement a solution that would look a bit like this:

docker_image = DockerImage(**$auth_docker)

Or, I would create some sort of helper function to build it for me:

def generate_docker_image_lwrp(attributes):
    lwrp = DockerImage()
    lwrp.registry = attributes.registry
    lwrp.tag = attributes.tag
    return lwrp

The goal is to reduce maintenance on the recipes. For instance this morning I wanted to add Chef's "retries" attribute on all recipes that pull an image. I had to edit all of them - I don't want that. I should've been able to a) add the attribute to the stack's JSON b) edit the Ruby wrapper class so that instances of it (i.e.: $auth_docker) get the "retries" field, then c) add the retries attribute to the lwrp-generator. Since all recipes would use the same generator, recipes wouldn't need to be edited at all.

Is this possible using Chef, in a way that 'notifies' still work?

回答1:

Quoting the Documentation

A definition is code that is reused across recipes, similar to a compile-time macro. A definition is created using arbitrary code wrapped around built-in chef-client resources—file, execute, template, and so on—by declaring those resources into the definition as if they were declared in a recipe. A definition is then used in one (or more) recipes as if it were a resource.

Though a definition behaves like a resource, some key differences exist. A definition:

Is not a resource or a lightweight resource Is defined from within the /definitions directory of a cookbook Is loaded before resources during the chef-client run; this ensures the definition is available to all of the resources that may need it May not notify resources in the resource collection because a definition is loaded before the resource collection itself is created; however, a resource in a definition may notify a resource that exists within the same definition Automatically supports why-run mode, unlike lightweight resources Use a defintion when repeating patterns exist across resources and/or when a simple, direct approach is desired. There is no limit to the number of resources that may be included in a definition: use as many built-in chef-client resources as necessary.

I.e: you can create a definition for this in a library cookbook used solely for this.

docker_library/defintions/default.rb

define :my_docker_image, :component => nil do
  component = params[:component]
  docker_image component.name do
    registry component.registry
    tag component.tag
    action :pull
  end
end 

in your recipes (need to have a depends on the docker_library cookbook in the metadata.rb):

my_component = $auth_docker
my_docker_image my_component.name do 
  component my_component
end

A more complete exemple of definition is available in the logrotate cookbook