Opscode
Home     Cookbooks     Blog     GitHub     Tickets 

Roles

Roles in Chef provide a mechanism for easily composing sets of functionality for each of your nodes through recipes and attributes. Nodes can have multiple roles applied, and they will be expanded in place, providing for a complete recipe list for that node. For example, let's say I have a list of recipes that should be applied on all my Ubuntu servers, and a list that is specific to machines that are Web Servers. This would map cleanly to two roles: 'ubuntu' and 'web_server'. Each role would specify the list of recipes it requires, in the order they should be applied. Where there is overlap (i.e., a recipe appears twice) it will be run when the first role is expanded (not run twice.)

You can create roles in Chef in 4 different ways: through the creation of role files in your chef repository (that utilize a ruby DSL, which gets compiled to JSON,) the creation of the JSON files directly in your chef repository, through the Web UI, or through the REST API. We will cover each option here.

Once you have created a role, you can add it to a node through the WebUI by dragging it from the Roles list to the client's "run list", or you can add it with a JSON file.

Roles work with chef-solo too!

You can use Roles with Chef Solo as well as the Client/Server. See the Chef Solo documentation.

The Ruby DSL


Roles created through this mechanism get compiled to JSON, and then are loaded in to the Chef Server. (We never execute ruby code directly on the chef server!) Each time you rake install your Chef Repository, we will re-compile the corresponding JSON and store it in the chef server.

This operation is destructive!

If you define your roles this way, or via the JSON file directly, then the Chef Server will destroy any changes you might make via the Web UI. So you make your choice - either define your roles via the Roles files, or via the Web UI (or a mix of the two - but never both for the same role.)

The advantage of using the DSL for role creation is that it is trivial to reach out to third party data sources, or anything else you can imagine to construct a role - it's just Ruby. The disadvantage is you can't have things both ways . To see an example, this role will build a list of every recipe in a repository's cookbooks.

You should create these files in the 'roles' subdirectory of your chef repository - if you are using the opscode canonical Chef Repository as a baseline, the latest version includes this directory and rake tasks to manipulate roles as documented on the repository page. If your repository doesn't have this directory, create it now.

A complete role file looks like this:

The Web Server Role

We'll go over each section below.

name

Each role must have a unique name, which is made up of [A-Z][a-z][0-9] and [_-]. Spaces are not allowed.

name field

description

A short description of what functionality is covered by this role.

description field

recipes

The list of recipes to apply for this role, in the order they should be applied.

recipes
Deprecated, Breaking change

This is deprecated as noted in the Breaking Changes document, and run_list is preferred.

run_list

In Chef 0.8.x, the recipes attribute is replaced with a run_list, which is identical to the one you specify for Node.

This is the list of recipes or roles to apply for this role, in the order they should be applied.

recipes

Would apply the "apache2" recipe, the "apache2::mod_ssl" recipe, and then anything required by the role "monitor".

default_attributes

An optional set of attributes that should be applied to all nodes with this role, assuming the node does not already have a value for that attribute. Use this to set site-wide defaults that can be overridden on a node-specific basis. The merge is 'deep', meaning that we will preserve nested attributes properly.

default_attributes

In the above example, all nodes with this role would have node[:apache2][:listen_ports] set to '80' and '443', assuming they do not already have a value.

If more than one role attempts to set a default value for the same attribute, the last role applied will win.

override_attributes

An optional set of attributes that should be applied to all nodes with this role, regardless of whether a node already has a value for that attribute. Useful for setting site-wide values that will always be set. The merge is 'deep', meaning that we will preserve nested attributes properly.

override_attributes

In the example above, node[:apache2][:max_children] will always be set to '50'.

If more than one role attempts to set an override value for the same attribute, the last role applied will win.

As JSON


The JSON format for roles maps directly to the Ruby DSL above. For the role we describe in that section, the corresponding JSON is:

The two additional fields are described below.

json_class

This should always be set to Chef::Role. This is used internally by Chef to auto-inflate this type of object. It should be ignored if you are re-building objects outside of Ruby, and its value may change in the future.

chef_type

This should always be set to role. This is the field you should rely on if you are building a system to consume Roles outside of Ruby.

The Web UI


Creating roles through the Web UI is a simple process. Adam Jacob demonstrates the Web UI in the following video:

Login to your Chef Server

If you haven't already, log in to your Chef Server.

Click the 'Roles' tab

In the primary navigation at the top, click the 'Roles' link.

Click the 'Create' link

You will have a sub-navigation appear, with a 'Create' link. Click it.

Fill in the 'Name'

Fill in the 'Name' field. Remember, role names allow [A-Z][a-z][0-9] and [_-]. Spaces are not allowed.

Fill in the 'Description'

Give this role a meaningful description.

Assign Recipes for this Role

Drag recipes from the 'Available Recipes' list on the left to the 'Recipes for this Role' list on the right. The initial target is the grey line. The list itself is sortable - the order you put the 'Recipes for this Role' is the order they will be applied to your node. You can drag these to reorder.

Assign Roles for this Role

Drag roles from the 'Available Roles' list on the left to the 'Run List' on the right. The initial target is the grey line. The list itself is sortable - the order you put the 'Run List' in is the order they will be applied to your node. You can drag these to reorder.

Set default and override attributes

Finally, set any default and override attributes using the JSON editor. As we stated earlier, these attributes will be merged in to the attributes for a given node.

Click 'Create Role'

Once you are through, click 'Create Role', and rejoice.

The REST API


You can also create roles directly in a running Chef Server via the REST API. All calls to the API must include:

  • The HTTP Header 'Accepts: application/json'
  • The HTTP Header 'Content-Type: application/json'

The following end-points exist:

/roles

Prerequisites

You must be authenticated.

HTTP Methods

GET

A GET call to /roles returns a data structure that contains links to each available role.

Response to GET /roles
Response Codes
  • Responds with a 200 OK.

POST

A POST call to /roles creates a new role. The request body should contain the JSON representation of the role.

Request body for a POST to /roles

In response, you should receive:

Response to a POST on /roles
Response Codes
  • Responds with a 201 Created if the Role is created.
  • Responds with a 403 Forbidden if the Role already exists.

/roles/webserver (Role Name)

Prerequisites

You must be authenticated.

HTTP Methods

GET

A GET to /roles/webserver (which corresponds to the Role Name, above) returns the JSON representation of the role.

Response to GET /roles/webserver
Response Codes
  • Responds with a 200 OK if the role exists.
  • Responds with a 404 Not Found if the role does not exist.

PUT

A PUT to /roles/webserver updates the Role. You should be PUT-ing the results of a previous GET request, to ensure you do not cause any conflicting revisions.

Request body for a PUT to /roles/webserver

The above would alter the list of recipes (or run_list), change the override attributes, and remove the default attributes.

We return the updated representation of the role:

Response to a PUT on /roles/webserver
Response Codes
  • Responds with a 200 OK if the role is updated.
  • Responds with a 404 Not Found if the role does not exist.

DELETE

A DELETE to /roles/webserver deletes the role. This request has no body.

The response to a DELETE request is the role's state just before it was removed.

Response Codes
  • Responds with a 200 OK if role has been deleted.
  • Responds with a 404 Not Found if the role does not exist.
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Jun 08, 2009

    Thorsten says:

    I like the idea of roles, but I can't quite wrap my mind around some of the impl...

    I like the idea of roles, but I can't quite wrap my mind around some of the implementation here. There seems to be a global roles namespace (actually "global" isn't really defined either). Also, why do you put a role spec into a cookbook? I'd expect most roles to straddle cookbooks (else might as well create a recipe). In the end, which roles exist in the role namespace when a node starts? All this seems very ill-defined.

    On implementing roles, how would the following differ from what you describe? Create a "roles" cookbook. Create a recipe for each role. The recipe munges the attributes (to implement the default/override) and then includes each recipe used for the role in turn. Is what you propose different? Is is syntactic sugar? Does it have different semantics?

    1. Jun 08, 2009

      Adam Jacob says:

      The roles are not tied to cookbooks - they are totally separate entities (and ha...

      The roles are not tied to cookbooks - they are totally separate entities (and have their own namespace entirely.) Roles definitely straddle cookbooks. The 'roles' directory we talk about above is a top-level item in the chef-repo:

      /roles
      /cookbooks
      /site-cookbooks

      And so on.

      You could get similar functionality from using a 'roles' cookbook, in that you can have a recipe that performs an 'include_recipe' for each, and attribute files that implement something akin to default/override. The role concept is a higher-level abstraction, in that it exists outside of a given cookbook, can be distributed on it's own, and can be used to reason about the set of included recipes that will be run on the node (we'll still miss things that are dynamically included, but the final recipe list is still easier to grok if you compose it via roles.)

      1. Jun 08, 2009

        Thorsten says:

        Ah, I missed that the roles dir is at the top of the repo, not the cookbook. Tha...

        Ah, I missed that the roles dir is at the top of the repo, not the cookbook. Thanks for clarifying. I'm still not entirely clear on the roles namespace. The cookbook namespace is defined by the search path through repos (or directories). Is the roles namespace similarly defined?

  2. Jun 08, 2009

    Thorsten says:

    Another question your role ruby DSL brought up is what "rake install" does. I ha...

    Another question your role ruby DSL brought up is what "rake install" does. I haven't looked into it, but it seems to actively push data to a specific chef server. That seems to conteract the ability to publish cookbook libraries on github or similar public access repositories. It seems to me that a chef server should always extract information from files in the cookbook, and if a cookbook is updated, we may want to have ways to ping a chef server (or multiple) but that they should then come back and actively fetch the updated content. At least that's the way we've been planning to operate.

    1. Jun 08, 2009

      Adam Jacob says:

      See above for your question about roles/cookbooks. When you run 'rake install',...

      See above for your question about roles/cookbooks. When you run 'rake install', we create JSON representations of the role, and then load that in to the chef-server (in the same way you would by using the REST API, basically.) You can absolutely bring in the role data from external sources (eventually, this would be one of the things you can do with a shared community website.)

  3. Feb 15

    Chris Adams says:

    This may seem obvious to most, but it just bit me now, and error logs weren't im...

    This may seem obvious to most, but it just bit me now, and error logs weren't immediately obvious to me, simply returning a "Failed loading ChefServerSlice (409 "Conflict")" error.

    When loading in roles from the filesystem, a role's filename (without the filetype extension) and the role name inside the file must be the same, so example_server.json must have the name value listed like so: "name": "example_server".

    The first role with a mismatching name/filename combo will stop the subsequent roles being loaded, even if their names do match up as required.


Copyright © 2009 Opscode, Inc. All Rights Reserved.