Using multiple roles against the same cookbook

I designed a custom cookbook for deployment of a Windows service. My goal was to use a pattern that could be used for all in-house windows services. To scale out to more services I thought we would simply create a new role for each service. This appears to be problematic and I am not sure why. When the chef-client runs it seems to only recognize one role during convergence, and not both. I preferred one role in json format per service over a hash per service defined in an attributes.rb file because the multiple hash attribute approach means every single service will be installed on a machine during convergence. It is more ideal to be able to apply 1 to n roles against a given machine. Team A may not need Team B’s service installed on the same instance, and vice versa. Seems like more overhead and less granularity with everything shoved into one place then looping through the objects. It also makes it more difficult for the dev teams to manager their service themselves and our team the recipe code when all of them are defined in one massive attributes file.

I have made this work for now by executing chef-client -o role[<role_name>] to override default run list from TeamCity feeding in the role name as a parameter

Not sure I fully understand the problem.

In your cookbook, you could do a recipe per service

recipes/serviceA.rb
recipes/serviceB.rb

Then you would have a role per service

roles/serviceA.json
roles/serviceB.json

In the role definition, you specify which recipe you want to include in the runlist.

serviceA role

{
  "name": "serviceA",
  "description": "service",
  "json_class": "Chef::Role",
  "default_attributes": {

  },
  "override_attributes": {

  },
  "run_list": [
    "recipe[foo::serviceA]"
  ]

}

serviceB role

{
  "name": "serviceB",
  "description": "service",
  "json_class": "Chef::Role",
  "default_attributes": {

  },
  "override_attributes": {

  },
  "run_list": [
    "recipe[foo::serviceB]"
  ]

}

But then I would be maintaining the same recipe code in two places, and that doesn’t feel ideal. What I was going for was 1 recipe to deploy a windows service, and each team able to consume that recipe via creating a role they are able to maintain

Here is a little more info:

Role 1

{
   "name": "service_one_svc",
   "description": "Service 1",
   "json_class": "Chef::Role",
   "default_attributes": {
     "zo_win_svc": {
       "name": "Service1",
       "display_name": "Service1",
       "description": "Service 1",
       "exe": "Service1.exe",
       "config": "Service1.exe.config",
       "deploy_artifact_key": "Service1."
     }
   },
   "override_attributes": {
   },
   "chef_type": "role",
   "run_list": ["recipe[zo_win_svc::win_svc_deploy]",
                "recipe[zo_win_svc::artifact_cleanup]"
   ]
}

Role 2

{
   "name": "service_two_svc",
   "description": "Service 2",
   "json_class": "Chef::Role",
   "default_attributes": {
     "zo_win_svc": {
       "name": "Service2",
       "display_name": "Service2",
       "description": "Service 2",
       "exe": "Service2.exe",
       "config": "Service2.exe.config",
       "deploy_artifact_key": "Service2."
     }
   },
   "override_attributes": {
   },
   "chef_type": "role",
   "run_list": ["recipe[zo_win_svc::win_svc_deploy]",
                "recipe[zo_win_svc::artifact_cleanup]"
   ]
}

The nodes run list: role[base],role[service_one_svc], role[service_two_svc]

The result is only role[service_two_svc] being compiled and converged

Recipes only intended to run once, no matter how many roles you put them in, during a Chef run. You’re also overwriting the same attribute data when you put it in multiple roles, as all attribute data is merged together when the chef run starts. This is why you’re having to run roles separately (and it isn’t a best practice).

Perhaps you could publish some Chef resources and providers that each team could use in their own cookbooks, and then you could simply converge whatever cookbooks on whichever nodes need the specific services?

“Perhaps you could publish some Chef resources and providers”

Not exactly sure what you mean by that so if you could be kind enough to elaborate it would be much appreciated. From what I gather the best way to do this (use the same recipe for multiple objects that slightly differ in nature) is define multiple objects in an attributes file via a hash then loop over the collection in the recipe. Doesn’t seem like a great pattern to me but maybe part of that is a lack of general understanding