custom type/provider case insensitive title - puppet

Is there anyway to normalize the title value through a munge call or something similiar?
I have a custom type/provider that manages local group membership on Windows machines (we needed more granularity over group membership, than just inside the user/group types.
group_member{"Group => Member":
ensure=>present,
}
group_member{"Group => DOMAIN\Member":
ensure=>present,
}
resources{'group_member':
purge =>true,
}
In general, when everything is consistent everything works great. The issue comes in that windows/active directory are not case sensitive. Therefore, when self.instances gets called, It generates a resource Group_member["Users => DOMAIN\SomeGroup"], but in our puppet manifest, Users is hardcoded, DOMAIN comes from a fact, and SomeGroup comes from a hiera value.
We've been fighting with the case sensitivity of puppet for about 5 years on this, and most of our administrators have gotten used to ensuring case matches between domain and yaml configurations.
Now, we have a new problem... somehow DOMAIN is messing up. On a new testing domain, the NETBIOS name is lowercase. The fact we had returning DOMAIN still returns uppercase, but Windows ADSI is returning a lowercase form. This wouldn't be an issue if we wern't using resources{'group_member': purge=>true}, but now in this test environment the groups get added (through their respective .pp files) and removed (through resources{'group_member': purge=>true}}) every puppet run.
Ideally i'd like to just normalize everything to lowercase in self.instances and in all of our .pp files, but we have 400 puppet modules and we use group_member 120 times, managed by quite a few different teams. It would be significantly easier if I could just munge the title metaparameter.
Any suggestions?

So, the way i'm handling this is to rename the existing type/provider and wrapping it in a define type that downcase's the title.

Related

How to reference hiera variable elsewhere in hiera?

We are using the roles pattern in puppet with hiera, meaning we have these lines in hiera.yaml:
- name: "Roles data"
path: "roles/%{::server_role}.yaml"
We have a custom fact that produces the role name when facter runs, but we would like to move this into hiera. Instead of the server_role variable being produced by facter, we want to specify the server_role inside of hiera, and let that variable be referenced elsewhere in hiera. Something like this:
hiera.yaml:
- name: "Per-node data"
path: "nodes/%{trusted.certname}.yaml"
- name: "Roles data"
path: "roles/%{lookup(server_role)}.yaml"
nodes/hostname.yaml:
server_role: foo_bar
I have seen this question, which says to use hiera() or lookup() but when I try to use those, I get this error message:
Interpolation using method syntax is not allowed in this context
So how can I use a hiera variable that's defined elsewhere in hiera?
Edit:
The prototypical code examples for defining roles could use any fact that's known to facter, often giving examples that are based on hostname. When you can't embed server config into hostname, a common(ish) workaround is to write a file such as /etc/server_role, but it seems to defeat the purpose of config management, when you need to ssh into a machine and edit a file. As the other comments & answer here so far mentioned, you could use an ENC, but again, the goal here is not to have config stored outside of version control. In fact, we have foreman as an ENC and we make a practice to never use it that way because then upgrades and other maintenance become unsustainable.
We could write a class which will pick up data from hiera, write it to /etc/server_role, and on the next puppet run, facter will pick that up and send it back to hiera, so then we'll have the server_role fact available to use in hiera.yaml. As gross as this sounds, so far, it's the best known solution. Still looking for better answers to this question.
Thanks.
As #MattSchuchard explained in comments, you cannot interpolate Hiera data into your Hiera config, because the config has to be known before the data can be looked up.
If you need a per-role level in your data hierarchy then an alternative would be to assign roles to machines via an external node classifier. You don't need it to assign any classes, just the server_role top-scope variable and probably also environment.
On the other hand, maybe you don't need a per-role level of your general hierarchy in the first place. Lots of people do roles & profiles without per-role data, but even if you don't want to do altogether without then it may be that module-specific data inside the module providing your role classes could be made to suffice.

How can I copy an existing overthere.SshHost file in XL Deploy UI using Puppet?

The Infra team in my company has provided us with sample overthere.SshHost under 'Infrastructure' in XL-Deploy UI that has a predefined private key file and passphrase which is not shared with us.
We are asked to duplicate this file manually in the UI, rename it and create infra entries for our application.
How can I achieve this with puppet?
Lets say the sample file is placed under: Infrastructure/Project1/COMMONS/Template_SshHost
and I need to create an overthere.SshHost under Infrastructure/Project1/UAT/Uat_SshHost and Infrastructure/Project1/PREPROD/Preprod_SshHost by copying the sample file.
Thanks in advance!
You can sync a target file with another file accessible via the local file system by using a File resource whose source attribute specifies the path to the original. You can produce a modified copy in a variety of ways, such as by applying one or more File_line resources (from stdlib) or by applying an appropriate script via an Exec resource.
But if you go that route then you have to either
accept that the target file will be re-synced on every Puppet run, OR
set the File resource's replace attribute to false, in which case changes to the original file will not be propagated into the customized copy.
The latter is probably the more acceptable choice for most people. Its file-copying part might look something like this:
$project_dir = '/path/to/Infrastructure/Project1'
file { "${project_dir}/UAT/Uat_SshHost/overthere.SshHost":
ensure => 'file',
source => "${project_dir}/COMMONS/Template_SshHost/overthere.SshHost",
replace => false,
}
But you might want to consider instead writing a custom type and provider for the target file. That would allow you to incorporate changes from the original template without re-syncing the file on every run, and it would give you a lot more flexibility with respect to the customizations you need to apply. It would also present a simpler interface for you to use in your manifests, which could make managing these easier. But, of course, that's offset by the cost is that writing and maintaining a custom type and provider. Only you can determine whether that would be a worthwhile trade-off.

How to run one feature file as initialization (i.e. before all other feature files) in cucumber-jvm?

I have a cucumber feature file 'A' that serves as setting up environment (data clean up and initialization). I want to have it executed before all other feature files can run.
It's it kind of like #before hook as in http://zsoltfabok.com/blog/2012/09/cucumber-jvm-hooks/. However, that does not work because my feature files 'A' contains hundreds of cucumber steps and it is not as simple as:
#Before
public void beforeScenario() {
tomcat.start();
tomcat.deploy("munger");
browser = new FirefoxDriver();
}
instead it's better to be able to run 'A' as a feature file as a whole.
I've searched around but did not find a answer. I am so surprised that no one has this type of requirement before.
The closest i found is 'background'. But that means i can have only one huge feature file with the content of 'A' as 'background' at the top, and rest of my test in the same file. I really do not want to do that.
Any suggestions?
By default, Cucumber features are run single thread in order by:
Alphabetically by feature file directory
Alphabetically by feature file name within directory
Scenario execution is then by order within the feature file.
So have your initialization feature in the first directory (alhpabetically) with a file name that sorts first (alphabetically) in that directory.
That being said it is generally a bad practice to require an execution order in your feature files. We run our feature files in parallel so order is meaningless. For Jenkins or TeamCity you could add a build step that executes the one feature file followed by a second build step that executes the rest of your feature files.
I have also a project, where we have a single feature file, that contains a very long scenario called Scenario: Test data with a lot of very long scenarios, like this:
Given the system knows about the following employees
|uuid|user-key|name|nickname|
|1|0101140000|Anna|annie|
... hundreds of lines like this follow ...
We see this long SystemKnows scenarios as quite valuable, so that our testers, Product Owner and developers have a baseline of what data are in the system. Our domain is quite complex, and we need this baseline of reference data for everyone to be able to understand the tests.
(These reference data become almost like well known personas, and are a shared team metaphore)
In the beginning, we were relying on the alphabetic naming convention, to have the AAA.feature to be run first.
Later, we discovered that this setup was brittle, and decided to use the following trick, inspired by the PageObject pattern:
Add a background with the single line Given(~'^I set test data for all feature files$')
In the step definition, have a factory to create the test data, and make sure inside the factore method, that it is only created once, like testFactory.createTestData()
In this way, you have both the convenience of expressing reference setup as a scenario, that enhances team communication, but you also have a stable test setup.
Hope this is helpful!
Agata

Puppet: manage the recursiveness of owner/group/mode attributes separately

I need to write a Puppet script to manage the directory /foo/bar such that:
the file mode on /foo/bar is 777, but the permissions of everything within the directory are not managed by Puppet.
the owner/group on /foo/bar and everything within it is baz.
That is, the first requirement is non-recursive, but the second attribute is recursive.
Puppet provides a single recursive attribute, which affects the behavior of owner, group, and mode simultaneously. This means that I cannot specify the desired behavior using a single resource declaration.
I tried using two resource declarations, but then I get the error
Error: Duplicate declaration: File[/foo/bar] is already declared in file /my/puppet/file.pp at line XX; cannot redeclare
Yes, this will not work. Mind that Puppet is not a scripting engine, but a tool to model your desired state.
You will therefor have to decide how you want to manage your directory: As a single file system entry (recurse => false) or a whole tree (recurse => true). In the latter case, Puppet will always manage all properties for which you are passing values.
In your situation, you will likely have to fall back to the workaround of managing the permissions of the directory itself through a different resource, likely an exec resource that calls chmod, independently of the file resource. The latter must not pass a value for mode in this constellation, otherwise the two resources will always work against one another.
It's no ideal, but Puppet is not well equipped to deal with your specific requirements.

Import Failure - Role With Id Does Not Exist

I am getting an import error in a specific environment with a managed CRM 2011 solution. The solution has been imported before into many other environments, but the one in particular where it is failing is throwing the following error:
Dependency Calculation
role With Id = 9e2d2d9b-645f-409f-b31d-3a9c39fcc340 Does Not Exist
I am a bit confused about this. I searched within the solution XML and was not able to find any reference to this particular GUID of 9e2d2d9b-645f-409f-b31d-3a9c39fcc340. I cannot really find it in SQL either, just wandering through the different tables, but perhaps I do not know exactly where to look there.
I have tried importing the solution multiple times. As a desperation effort, I tried renaming all of the security roles in the destination environment prior to importing, but this did not help.
Where is this reference to a security role actually stored? Is this something that is supposed to be within my solution--which my existing CRM deployment is expecting me to import?
How do I fix the problem so that I am able to import this solution?
This is the code we used to fix the issue. We had to run two different scripts. Script A we had to run a total of four times. Run it once, attempt the import, and then consult the log to find the role that is causing the problem--if you receive another error for another role.
To run script A, you must use a valid RoleTemplateId from your database. We just picked a random one. It should not matter precisely which one you use, because you will erase that data element with script B.
After all of the roles are fixed, we got a different error (complaining something about the RoleTemplateId was already related to a role), and had to run script B. That removes the RoleTemplateId from multiple different roles and sets it to NULL.
Script A:
insert into RoleBaseIds(RoleId)
values ('WXYZ74FA-7EA3-452B-ACDD-A491E6821234')
insert into RoleBase(RoleId
,RoleTemplateId
,OrganizationId
,Name
,BusinessUnitId
,CreatedOn
,ModifiedOn
,CreatedBy
)
values ('WXYZ74FA-7EA3-452B-ACDD-A491E6821234'
,'ABCD89FF-7C35-4D69-9900-999C3F605678'
,(select organizationid from Organization)
,'ROLE IMPORT FIX'
,(select BusinessUnitID from BusinessUnit where ParentBusinessUnitId is null)
,GETDATE()
,GETDATE()
,null
)
Script B:
update RoleBase
set RoleTemplateId = NULL
where RoleTemplateID='ABCD89FF-7C35-4D69-9900-999C3F605678'
Perfect solution, worked for me! My only comment would be the error in Script B: it shouldn't clear the template IDs of all roles for the given template, only the template ID of the newly created "fix" role, as follows:
update RoleBase
set RoleTemplateId = NULL
where RoleID='WXYZ74FA-7EA3-452B-ACDD-A491E6821234'
I would've gladly put this in a comment to the answer, but not enough rep as of now.

Resources