UDS Packages
A UDS Package is a Zarf Package with two additions:
- It is meant to be deployed on top of UDS Core.
- It contains the UDS Package Kubernetes custom resource.
These packages include all the OCI images (docker containers), Helm charts, and supplemental Kubernetes manifests required for the app to communicate with UDS Core. The UDS Operator in turn auto-applies appropriate security and network policies to assure a secure and compliant running environment. A UDS package does not include dependencies like databases or object storage*. These external dependencies are deployed next to a UDS Package inside a UDS Bundle.
To move from the theoretical to the concrete, see the next section on the anatomy of a UDS Package repo.
Anatomy of a UDS Package Repo
Disclaimer: the exact file structure of UDS Packages is subject to change. This document will fall out of date but should retain conceptual accuracy. After understanding this point-in-time snapshot of how a UDS package is built, it should be fairly trivial to extend that knowledge to grasp UDS packages as improved in the interim. To aid in it’s utility as a teaching tool, links to source code are pinned to a specific GitLab Package release.
For an in-depth developer-focused treatment of UDS Packages, see the documentation in GitHub. You can also view the UDS package template in GitHub here. This document will go over the main components of a UDS package and their functions at an overview level, and then show specifically how these components are tied together in the case of GitLab.
Anatomy Overview
Directory / Top-level file | Role | Function |
---|---|---|
.github/ | CI/CD | Directives to GitHub, primarily it contains the build, test, and release pipeline(s). |
adr/ | Docs | ”ADR” stands for Architectural Decision Records. These documents record key architectural decisions and their reasoning. |
bundle/ | Testing & Development | When you’re testing a UDS Package, you need to be able to deploy it with other applications such as databases in order to test your configuration. The bundle/ directories in UDS Package repos are for just that. They deploy light-weight databases, key-value stores, and object stores as needed alongside the application to permit testing. They also serve as an example of how to use the UDS Package in a bundle. Nothing in the bundle/ directory ever becomes part of the UDS Application Package. |
charts/ | UDS Package Component | This is for helm charts which are created supplementally to the application’s helm chart. This includes at minimum the UDS Package manifest and the SAML/OIDC configuration for automatic integration with our Keycloak SSO application (which is part of UDS Core). Not infrequently it will also include another resource or two as needed to fully integrate into the UDS ecosystem on an app-specific basis. |
common/ | UDS Package Component | This directory holds a single zarf.yaml file which is the base Zarf package definition. It is imported by the root-level zarf.yaml . You can think of it like the parent-class object in an object-oriented-programming model. This generally pulls the charts from the charts/ directory and the main application’s helm chart into the Zarf package but leaves flavor specific details out. |
docs/ | Docs | Documentation about the UDS Package. |
src/ | Testing & Development | This contains additional zarf package source code in each leaf-directory. These are never made into a part of the UDS Package. Rather, they are included in the test bundle to help glue the application to the larger ecosystem. This often includes a zarf package that contains only the application’s namespace resource. By putting this in a separate zarf package and deploying it ahead of time, secrets deployed to the application’s namespace by other packages (such as authentication secrets) do not get deleted when you run zarf package remove <app package> . This will be revisited when discussing the bundle/ directory in the GitLab repo below. |
tasks/ | Testing & Development | These tasks run via the UDS CLI which uses the maru task runner under the surface to perform workflows like “build, deploy, test” (normally called dev ) or “publish built artifact”. These tasks are a mixture of bespoke repo-specific tasks and included tasks from the uds-common repo. The entrypoint to these tasks is the top-level tasks.yaml file which is to the UDS CLI what a Makefile is to GNU make or as a Rakefile is to Ruby. These tasks are also executed as part of the CI/CD pipeline defined in .github/ . |
tests/ | Testing & Development | This contains files related to the playbook tests which are used to verify that the application in a UDS Package appears to be working as configured in the test bundle (in bundle/ ). These tests are integration-level tests focused on validating connections between the application and the UDS ecosystem. |
values/ | UDS Package Component | This directory typically contains four helm values.yaml files which are fed into the main application’s helm chart to configure it. Of note, the common-values.yaml file contains all configuration common to all deployment flavors and the <flavor>-values.yaml files contain the image URLs for the given value and any configuration changes required to make it work with this specific flavor of images. |
misc top-level files | Licensing, tool configurations, etc. | Most of the top-level files are self-explanatory or largely irrelevant to most users. The remainder of this table will discuss only the most important ones. |
tasks.yaml | Testing & Development | As mentioned when discussing the tasks/ directory. This file defines all tasks accessible to the command uds run <task> . View them by running uds run --list . |
zarf.yaml | UDS Package Component | This is the Zarf package which is in this case also a UDS Package. If this were a code repository, this file would be the main function. It defines all top-level Zarf variables, and then includes one component per flavor, each component importing the common/zarf.yaml package. Each component (which stands for a package flavor) adds the values/<flavor>-values.yaml file to set the images to the desired flavor in the helm chart and lists the needed images so Zarf can pull them down and add them to the package at build time. These components are turned on or off by the “flavor” variable value at build time producing only one of the components at any time in the final UDS Package. |
These directories may be easiest understood through a detailed example.
GitLab’s UDS Package Anatomy
GitLab is the cornerstone application in the UDS Software Factory. You can view the UDS Package for GitLab on Defense Unicorn’s GitHub. This dive into UDS Package anatomy will use the repo as it existed in release v17.3.6-uds.1. That is, GitLab version 17.3.6, second package release for that version (zero-indexed).
Rather than reviewing the repo according to the alphabetical ordering of it’s directories as in the table above, components of the repo are discussed in terms of how they build to produce a UDS Package, and not all components will receive the same screen-time, rather, what follows is something of a guided tour through the repository.
GitLab’s UDS Package Components
common/zarf.yaml
Starting with common/zarf.yaml
we have the base ZarfPackageConfig
. It is reprinted in abbreviated form below with comments added for clear in-line explanation.
./zarf.yaml
If we go now to the root-level zarf.yaml file we can see the Zarf variables and flavors get added in.
GitLab’s Testing & Development
As explained in the anatomy overview, the bundle/ directory contains a bundle using the GitLab UDS Package and serves two functions. First, it provides a way to deploy and test GitLab as configured by the UDS Package. Second, like any good test, it is a form of documentation showing how Gitlab may be connected into a bundle.
Bundle files get larger than the zarf.yaml
files previously explored so this one will be more severely abbreviated.
uds-bundle.yaml excerpts:
Footnotes
*object storage is the type of storage most commonly associated with AWS’ S3 service. In Azure it’s called Blob Storage, in Kubernetes it is typically MinIO, and it goes by various other names in other storage platforms.