TT Lab
Get started
Learn Learning paths Courses

Ansible in Practice

Build an in-house collection and ship it with the version pinned

Continue in TT Lab

Goal

You learn to run one full cycle yourself: bundle several roles into one collection and give it a version, install the built bundle and call it by FQCN, and lock the installed version with a pinning file.

Why it matters

A role is a unit of reuse, and a collection is a unit of distribution. As teams grow, the problem shifts from "how do I use this role?" to "which version of the role is running on that server right now?" Collections introduced three things to answer that question. They write the origin in the name (FQCN), they attach a version to the bundle (SemVer), and they write the version to install in a file (requirements.yml). Without these three, the order of the search path decides what gets executed, and that order is decided not by people but by the environment. Add role argument specs on top, and wrong values are blocked before a task runs — this is something you should have in place before documentation when you send out code that others will use. This lab goes through that cycle using only a local bundle, with no internet.

Steps

  1. Use ansible-galaxy collection init to create the skeleton of the acme.platform collection under /root/ans/coll/src. Also create an inventory listing web1, web2, and db1 in /root/ans/inventory/hosts.ini (ansible_host=127.0.0.1, ansible_port=2222, ansible_user=root).
  2. Set the version in /root/ans/coll/src/acme/platform/galaxy.yml to 1.2.0, fill in description, authors, and repository with in-house values, and put MIT in license and infrastructure and linux in tags. Also write requires_ansible as a version range in meta/runtime.yml of the same collection.
  3. Create a role in roles/motd/ inside the collection. Put motd_banner (default acme-platform), motd_owner (default platform), and motd_path (default /root/ans/coll/out/motd.txt) in defaults/main.yml, and fill tasks/main.yml with a single named task that writes two lines, banner=<motd_banner> and owner=<motd_owner>, to motd_path. Call the module by its FQCN.
  4. Create roles/motd/meta/argument_specs.yml and write a short_description and the three arguments for the main entry point. All three must be type: str and have a description, and for motd_owner allow only the two values platform and sre through choices.
  5. Build the collection to create /root/ans/coll/dist/acme-platform-1.2.0.tar.gz, and check that the bundle contains MANIFEST.json, FILES.json, and the role files.
  6. Install the bundle you made into /root/ans/coll/collections, and check that acme.platform shows up as 1.2.0 from that path and that the arguments of the acme.platform.motd role can be looked up with ansible-doc.
  7. Create /root/ans/coll/use.yml that calls the acme.platform.motd role by FQCN, passing motd_banner as acme-platform in production and motd_owner as sre. Run it against web1 of the inventory to produce /root/ans/coll/out/motd.txt.
  8. Raise the galaxy.yml version of the source to 1.3.0 and build again (you now have two bundles), then pin the 1.2.0 bundle as type: file in /root/ans/coll/requirements.yml and install from that file. Also leave the installed version on one line in /root/ans/coll/out/pinned.txt.

Notes

Create the collection skeleton

Use ansible-galaxy collection init to create the skeleton of the acme.platform collection under /root/ans/coll/src. Also create an inventory listing web1, web2, and db1 in /root/ans/inventory/hosts.ini (ansible_host=127.0.0.1, ansible_port=2222, ansible_user=root).

Give the collection name as a single unit, <네임스페이스>.<이름> (namespace and name). Specify where to create it with --init-path, and the skeleton is created under it in two levels, namespace/name.

Fill in galaxy.yml and pin the minimum ansible version

Set the version in /root/ans/coll/src/acme/platform/galaxy.yml to 1.2.0, fill in description, authors, and repository with in-house values, and put MIT in license and infrastructure and linux in tags. Also write requires_ansible as a version range in meta/runtime.yml of the same collection.

If the example phrases the skeleton put in (your name, your collection description) remain, the build succeeds but the result cannot be used as a deliverable. runtime.yml is almost all comments, so it is faster to write just the keys you need from scratch.

Put a role inside the collection

Create a role in roles/motd/ inside the collection. Put motd_banner (default acme-platform), motd_owner (default platform), and motd_path (default /root/ans/coll/out/motd.txt) in defaults/main.yml, and fill tasks/main.yml with a single named task that writes two lines, banner=<motd_banner> and owner=<motd_owner>, to motd_path. Call the module by its FQCN.

The directory conventions for a role inside a collection are exactly the same as for an existing role. If you hard-code the values the role cannot be reused, so take all three values as variables. To write the two lines at once, pass a multi-line string (|) as the content.

Block wrong values with an argument spec

Create roles/motd/meta/argument_specs.yml and write a short_description and the three arguments for the main entry point. All three must be type: str and have a description, and for motd_owner allow only the two values platform and sre through choices.

A spec is validation, not documentation. Call the role with a value that is not in the list and check for yourself that it stops before the first task runs.

Build a bundle and check its contents

Build the collection to create /root/ans/coll/dist/acme-platform-1.2.0.tar.gz, and check that the bundle contains MANIFEST.json, FILES.json, and the role files.

Run the build inside the collection source directory. Specify where to put the result with --output-path. To see inside the bundle, you can just list it without extracting it.

Install to a local path and verify with the docs

Install the bundle you made into /root/ans/coll/collections, and check that acme.platform shows up as 1.2.0 from that path and that the arguments of the acme.platform.motd role can be looked up with ansible-doc.

Give the install path with -p. When you look things up, you have to make ansible see that path, and there is a separate environment variable that sets the search path. Hundreds of collections from the distribution are installed, so specify a name when you view the list.

Call and run it by FQCN

Create /root/ans/coll/use.yml that calls the acme.platform.motd role by FQCN, passing motd_banner as acme-platform in production and motd_owner as sre. Run it against web1 of the inventory to produce /root/ans/coll/out/motd.txt.

Values you pass when calling a role win over the role's defaults. If it says the collection cannot be found, check the search path first — the place you installed to and the place you look at when running must be the same.

Pin the installed version even when the source moves ahead

Raise the galaxy.yml version of the source to 1.3.0 and build again (you now have two bundles), then pin the 1.2.0 bundle as type: file in /root/ans/coll/requirements.yml and install from that file. Also leave the installed version on one line in /root/ans/coll/out/pinned.txt.

To point to a local bundle file, write the path in the name and state the type separately. Do not write the installed version by hand; extract it from the output of collection list.