I am trying to build two Puppet profiles for the Hashicorp Consul DCS. Consul can run as a client or server agent, the server mode being a superset of the client mode. This is directly mirrored in the configuration:
Consul server agents typically require a superset of configuration required by Consul client agents.
My Puppet design approach is based on this pattern: https://puppet.com/docs/pe/2018.1/the_roles_and_profiles_method.html
According to the Puppet documentation, it should be possible (and most probably desirable) to include the consul_client
profile in the consul_server
profile to avoid code duplication:
Profiles can include other profiles.
Trying to implement this, I used some mandatory parameters on both profiles and ran into problems during execution of the automatic rspec unit tests.
In the consul_client
unit test file consul_client_spec.rb
, I just provided the required parameters as follows:
let(:params) { {
'datacenter' => 'unit-test',
'encrypt' => 'DUMMY',
'server_agent_nodes' => [ '1.2.3.4' ]
} }
Issues arised when trying to run the consul_server_spec.rb
unit test. Naively, I just passed the one additional required parameter of the consul_server
profile:
let(:params) { {
'bootstrap_expect' => 3,
} }
As the consul_client
profile is include
ed / require
ed by the consul_server
profile, the test failed with missing parameters for the consul_client
profile class. This seems to be indicative of some general structural problem with this approach.
Now, I am unsure if I should re-declare all the parameters of the consul_client
profile class in the consul_server
profile class - which, in my opinion, would violate the DRY principle. Also, when using Hiera data in the future, this would lead to a situation where profile::consul_client::*
and profile::consul_server::*
would contain some of the same, duplicate data, as the client-related part of the data would have to be repeated for both profiles.
Added Note: And duplicating parameters in the consul_server
class would probably not even work, as parameters cannot be passed explicitly, but only via data, to include-like resource definitions - so those duplicated parameters couldn't be passed to the consul_client
class.
On the contrary, the documentation states the following, but I am not sure if this applies to included profile classes (as they may not be component classes?) as well:
Profiles own all the class parameters for their component classes. If the profile omits one, that means you definitely want the default value; the component class shouldn't use a value from Hiera data. If you need to set a class parameter that was omitted previously, refactor the profile.
In addition to these thoughts, one could also see the two profile classes being refactored into normal classes of a seperate module, which may help to see the implications of different design approaches.
In conclusion, the following questions arise:
- How should parameters in nested profiles be handled, should be set exclusively by hiera data separately for each profile class?
- How to pass parameters to nested profiles during unit tests? Would it be the right way to somehow provide mock hiera data as part of the test fixture?
- Would mocking of the
consul_client
profile class be the better option?