molecule-plugins icon indicating copy to clipboard operation
molecule-plugins copied to clipboard

Proposal: move cookiecutter code into proper ansible collection

Open beargiles opened this issue 2 years ago • 5 comments

One of the biggest annoyances I have with the current approach is that it's basically impossible to upgrade the driver. It creates the scripts in the same location as our custom code (converge, verify, etc.) and a lot of us are weenies who don't to risk re-running a script nuking what we have.

(I know - we could probably commit, clean out the directory, re-run the script, merge, commit... but that's a lot of work.)

A better idea (IMHO) is to move the cookie cutter implementations into an Ansible collection. The molecule.yml file and stub prepare, create, and destroy scripts would remain but they would do nothing but import the corresponding tasks from the new collection.

This makes updates, e.g., for bug fixes, trivial. It would leave our custom scripts untouched.

This could be created as a separate project, or this repo could be modified with the goal of moving the existing python code into the collection's 'modules' directory with just a stub left for the 'molecule init --driver...' call. Among other things this would potentially allow that code to be reused since it would be available as, e.g., 'ansible_community.molecule_plugins.ec2_create_test_stub'.

beargiles avatar Apr 19 '23 15:04 beargiles

we found a way around it. In cookie cutter templates we have (for instance for create action) following code

- name: Create
  ansible.builtin.import_playbook: <our namespace>.<our collection>.create 

At the same time in dependency section in molecule.yml we have following

---
dependency:
  name: shell
  # yamllint disable rule:line-length
  command: |
    ansible-galaxy install -r $MOLECULE_SCENARIO_DIRECTORY/requirements.yml
  # yamllint enable rule:line-length

This will install all collections, mentioned in requirements.yml (this file is also in molecule driver in cookie cutter) and collection . is mentioned in requirements.yml

With every run molecule dependency all dependent collections are installed.

Hope this helps.

kksat avatar Apr 20 '23 12:04 kksat

@beargiles I'm not sure to understand. Are you modifying the cookiecutter templates from inside the driver directory, leading to loss of changes after every update ? Can't you use provisioner.playbooks.create in molecule.yml ?

apatard avatar Apr 26 '23 13:04 apatard

I'll have a prototype soon - I've been adding a ton of verification, tracking things more carefully, etc.

The general idea is that the cookiecutter for create.yml and destroy.yml will become

---
{% raw -%}
---
- name: Create
  hosts: localhost
  connection: local
  gather_facts: false
  no_log: "{{ molecule_no_log }}"
  tasks:
    - ansible.builtin.include_role:
      name: snapteam.molecule_plugins.ec2_create
{%- endraw %}

and

---
{% raw -%}
---
- name: Destroy
  hosts: localhost
  connection: local
  gather_facts: false
  no_log: "{{ molecule_no_log }}"
  tasks:
    - ansible.builtin.include_role:
      name: snapteam.molecule_plugins.ec2_destroy
{%- endraw %}

('snapteam' is internal project - obviously it would be better to use ansible-community.something)

This uses a new collection 'snapteam.molecule-plugins' that's based on the current cookiecutter implementation but made into real roles. E.g., a lot of 'vars:' is replaced by ec2-create/vars/main.yml, etc.

It's entirely independent of the existing plugin - it simply moves two cookiecutter files into a new collection that can be independently maintained and updated. The existing module includes the code via 'include_role'.

(Note: I started with a single role but ultimately decided everything was much clearer after I split it into two roles and the old create.yml and destroy.yml became the prototypes for the new main.yml.)

.....

That said... one of the nice things about Collections is that they make it easy to include modules in addition to roles. There's no reason why the existing python code couldn't be converted into modules, e.g., 'ansible-community.molecule_plugins.ec2_install', with just a stub left so molecule can find the required configuration details and code.

In fact the more I look at it the more I think it may make sense to ultimately ditch the ansible implementration entirely. Since we only require a few functions, even with my extended validation, it should be much easier to maintain than my current account that requires quite a lot of 'register' and 'assert' plays.

If create and destroy are converted to python modules the cookiecutter code would be reduced to

---
{% raw -%}
---
- name: Create
  hosts: localhost
  connection: local
  gather_facts: false
  no_log: "{{ molecule_no_log }}"
  tasks:
    - snapteam.molecule_plugins.ec2_create
{%- endraw %}

Of course there would still be ways to support additional functionality. In this iteration my focus has been on moving as much as possible into a separate collection while maintaining sane defaults. You can do a lot if you're using a role and have a vars/main.yml file.

beargiles avatar Apr 26 '23 22:04 beargiles

Hi @beargiles , Within current plugins release ec2 one doesn't work out of the box, it's mentioned here .

As per @apatard words:

fwiw, for some plugins (I guess it's including ec2), there's an issue coming from molecule. Cookiecutter support has been removed (and I don't remember seeing some kind of replacement possible from -plugins point of view).

Can you guys elaborate on what exactly is the issue that create.yml and destroy.yml are not detected by molecule.

NikodimMatvienko avatar Mar 11 '25 08:03 NikodimMatvienko

I haven't looked at this for a long time but the existing implementation used 'cookiecutter' to recreate customized code for your environment - including all of the functionality. That was the reason for my suggestion - it meant that the test infrastructure was frozen at the time you created the molecule tests. If you hit a limitation you had to nuke your existing tests and rebuild them. In theory you could create backup of everything except this module and then restore them but in practice that's a risky gamble.

That's why I'm not surprised the upstream project removed cookiecutter - it might be a quick fix when prototyping solutions but it's not maintainable in an environment with thousands of tests.

I had a loose architecture in mind to replace and extend this module. The initial idea focused on just EC2 but I hit some serious limitations since I needed to be able to set up S3, SQS, etc. in my tests and that meant the ansible code started to get really complex.

I decided an agent would work better. This would be a small application (serverless or that mechanism that predated EC2 but I;m drawing a blank on now) on AWS, and the equivaent on the other cloud providers.

The new AWS module would be pretty shallow, just a REST client that collects the required information from the configuration, builds a REST request, then accepts the response. It would include all required identifiers, IP addresses, etc. A second REST request would release the resourses. If it makes sense the molecule tests could use the IP address as the token, not a separate one like today - and that would simplify a lot of things.

The molecule driver would also need an authentication token of some kind - you don't want the agent to be available to everyone. Not even within your own organization.

The agent - which could be java, python, whatever - would parse the request and either create the requested resources or provide a reason for refusal.

The agent would also keep track of all requests and automatically shut down anything after 58 minutes unless the request explicitly requested more time. That's more than enough time for most tests and there's no benefit to releasing the instance earlier since biling is in 60 minute increments. If the molecule tests fail to shut down the instance you won't get an unexpected bill, you'll just get a nastygram telling you to clean up your tests.

This approach is also a tremendous security improvement. The existing approach requires substantial permissions to be granted to anyone running molecule tests. You can do a lot to limit the damage, e.g., using a dedicated VPC, etc., but that takes time and knowledge and a lot of places will overlook it. So it's a big risk.

In contrast this approach doesn't have any direct access to AWS resources - it can only send a REST request to the agent. The agent may then grant selected RO or RW permissions for that user before sending the response. It can remove those permissions before releasing the resources. (The API may have changed but in the past permissions did not have transitive removal when the resource was deleted.)

There's also some issues regarding access to the server logs (I can think of several different ways), possibly creating and updating related JIRA tickets with both the request and possibly the results if that's part of the payload sent in the 'release' call, etc.

But....

That's a lot of work. It's one thing if I an do it as part of my job, something else if I need to dedicated months of evenings and weekends for both development and testing. Plus there's now a number of other ways to run tests against temporary AWS resources; They might not mesh weil with molecule tests but it definitely reduces the potential need for a new driver.

beargiles avatar Mar 11 '25 19:03 beargiles