Introducing GitOps Versions: A Unified Way to Version Your Argo CD Applications

Introducing GitOps Versions: A Unified Way to Version Your Argo CD Applications

5 min read

Last month, we announced our new GitOps Environment dashboard that finally allows you to promote Argo CD applications easily between different environments.

We have seen why this dashboard is groundbreaking (compared to vanilla Argo CD) as it allows you to easily promote application and configuration changes between environments while providing rich information about your applications including:

  • Which application instance exists in which environments
  • Who the last person was to touch an environment
  • What source code change is currently deployed to an environment
  • Which Pull Requests would affect this environment and application

In the previous article of the series, we explained how Codefresh understands the relationship between applications and environments and how you can even promote by drag-n-drop. Codefresh will then autogenerate a PR for you with all the necessary changes.

One thing that we never explained however, was the versioning scheme used in the UI. It is obvious in the screenshot that we promote the “accounts” application from version 1.0.7 to version 1.0.9. But what is this version exactly? And how is it calculated by Codefresh?

Versions, versions, versions

Kubernetes is a very flexible platform. Its power comes from defining APIs and custom resources that can have different implementations in different organizations. Most people who adopt Kubernetes are initially puzzled by the lack of an “application” resource. Kubernetes offers you the low-level mechanics of pods and replicasets but it is up to you to define what exactly is an “application”.

This poses a great challenge for organizations that adopt Kubernetes and find out that there is no native support for an Application object. Argo CD introduces an “Application” Resource which is not a real application in the traditional sense. Instead of describing a packaged artifact Argo CD applications simply define a sync point between a Git repository and a Kubernetes cluster. The sync point is a Git hash which in most cases is not what matches the application source code. 

Taken straight from official Argo CD documentation:

kind: Application
  name: guestbook
  namespace: argocd
  project: default
    targetRevision: HEAD
    path: guestbook
    server: https://kubernetes.default.svc
    namespace: guestbook

The “Application” object of Argo CD knows nothing about the container image tag let alone the source code version. 

A lot of teams have different approaches when it comes to source code versions. Some popular choices are:

  • Git hash of the source code commit
  • Numerical version coming from the application framework (e.g. maven pom.xml or package.json)
  • Build number as it comes from the Continuous Integration system
  • Build date matching the time the artifact was created

Unfortunately none of these options are visible to an Argo CD “Application”.

In the real world each application needs a version. Versions are important because they show how a single release is progressing between the different environments. And again, vanilla Kubernetes and Argo CDs don’t say anything about application versions. This has forced a lot of people to introduce their own “version” concept on top of Kubernetes.

The most obvious definition of a “version” is the one provided by Helm charts. A Helm chart can actually contain two versions. The chart version as well as an “application version.” The reasoning behind this approach was that people might want to track changes on the helm chart itself in addition to the application(s) it contains.

Unfortunately, Helm doesn’t actually restrict you on when/how you update those fields. The application field was also designed to be optional. This means that several processes have emerged in practice for teams that adopted Helm:

  • Some organizations just use the chart version of everything
  • Some organizations use both fields but they are always matched
  • Some organizations use both fields but bump them in an ad-hoc manner
  • Some organizations use both fields with their original reasoning. They bump the application version when the application changes, and the chart version when the manifests are updated
Helm update strategies

The end result is that, currently, there is no common agreement on how to use Helm chart versions. 

And remember, we are only talking about Helm apps so far. People also use Kustomize, JSonnet, plain manifests, or their own custom tool with no concept of a “version.”

If you combine all these possibilities with the previous options about artifact versioning (build number, build date, git hash etc) the permutations are endless. 

Now we have a big problem when it comes to promotions. We want to be able to promote an “application” between “environments,” and the first step would be to understand what “version” should be moved from the “source” environment to the target “environment.” So how do we achieve this?

Versioning for GitOps applications

To be as flexible as possible for each Codefresh/Argo CD application, you can define precisely what YOU consider the “version.” To this purpose, as part of the PromotionTemplate CRD you can now explain to Codefresh from which file (and optionally which json/yaml path) to get a “version” for the application.

Here is an example specification. Notice the “versionSource” property.

kind: ApplicationConfiguration
  name: base-helm
  	app: helm-app
  priority: 3
	file: Chart.yaml
	jsonPath: appVersion

This application is a Helm chart and we have defined that for a “version,” we want to use the application version of the chart (and completely ignore the chart version). This configuration can easily map existing Helm applications into Codefresh dashboards.

As another example, let’s look at the real-world scheme that we use internally for our own developer teams.

kind: ApplicationConfiguration
  name: cf-deployer
  	app: cf-deployer
  priority: 2
	file: service.yaml
	jsonPath: version

This defines a Kubernetes application that includes a file called “service.yaml” with a single property for a version. You can see how the application looks at any of the Codefresh Open Source repositories

Finally, here is an example of using the node.js version (from package.json) as the “version” for the whole application.

kind: ApplicationConfiguration
  name: cli
  	app: cli
  priority: 2
	file: package.json
	jsonPath: version

In essence, you have great flexibility in defining what application versions mean for you. The end result is that you have a unified way of describing versions and explaining how applications traverse via environments.

Unified GitOps versions

This allows Codefresh now to understand what is YOUR definition for an application version and how you want promotions to happen between environments.

This is even more important for large organizations as with Codefresh, you can now promote applications between environments regardless of the configuration/templating tool each team is using.

Coming next: decide what to promote

Having the description of a version is only half of the story when you promote an application. The other half of course, is choosing what to promote. Just the values files? Just some lines in the values file? The whole Kustomize overlay? Only the image? Only the configmap? Some lines in the configmap?

We will cover this in the next blog post, where we will talk about the Promotion template for each application. Stay tuned…

Happy New Year!

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

Build your GitOps skills and credibility today with a GitOps Certification.

Get GitOps Certified

Ready to Get Started?
  • safer deployments
  • More frequent deployments
  • resilient deployments