Skip to content

What is Git Events Runner

As mentioned above this is custom Kubernetes operator which serves to:

  • Watch on some Git repositories and run Jobs if something was changed in the watched branch/tag.
  • Run the same Jobs when webhook has been received.
  • Do the same just periodically even if nothing was changed.

How it works

  • Central resources of the Git Events Runner are triggers, there are two - ScheduleTrigger and WebhookTrigger.
  • Obviously, triggers interact with some sources to watch on changes, there are two kinds of sources: GitRepo and ClusterGitRepo.
  • Finally, if some trigger fires (something was changed in the branch or just time arrived, or webhook received) it pushes third type of resource - actions - to perform its dedication and run the Job (yes, it's just a classic Kubernetes Job) which is described by Action and ClusterAction resources.

Detailed explanations of all custom resources with examples can be found in the Guides section.

Typical flow

Imagine we need to run script deploy.sh from the root of Git repo https://git.example.com/the-best-team/cool-project when anything was changed in the main branch (we have fine-grained branch protection and no one can merge to the main without approve). That script, for example, upgrades Helm release in our K8s cluster in the namespace of the trigger:

#!/bin/bash

# 1st argument is path to the folder with Helm chart

helm upgrade ${TRIGGER_SOURCE_NAME} ${1} -f configs/values.yaml --install --wait && \
    (echo "Has been successfully deployed"; exit 0) || \
    (echo "Something went wrong"; exit 1)

and we need to check for branch changes every 5 minutes.

To implement these requirements (except putting the script to the repo), we create three simple resources:

  • GitRepo:
apiVersion: git-events-runner.rs/v1alpha1
kind: GitRepo
metadata:
  name: cool-project-repo
  namespace: default
spec:
  repoUri: https://git.example.com/the-best-team/cool-project.git
  authConfig:
    type: basic
    secretRef:
      name: cool-project-auth
  • ScheduleTrigger:
apiVersion: git-events-runner.rs/v1alpha1
kind: ScheduleTrigger
metadata:
  name: cool-trigger
  namespace: default
spec:
  sources:
    kind: GitRepo
    names:
      - cool-project-repo
    watchOn:
      onChangeOnly: true
      reference:
        branch: main
  schedule:
    interval: 5m
  action:
    kind: Action
    name: run-deploy-sh
  • Action:
apiVersion: git-events-runner.rs/v1alpha1
kind: Action
metadata:
  name: run-deploy-sh
  namespace: default
spec:
  actionJob:
    args:
      - ./deploy.sh
      - ./chart/
    serviceAccount: git-events-runner-jobs

Using these resources Git Events Runner does following for each repo mentioned in the source list of the trigger:

  1. Clone specified branch (main is here) of the repo using URI and credentials (if needed) defined in GitRepo resource.
  2. Compare the latest commit hash with previously stored value (from previous run if it was).
  3. If hash differs, it uses Action resource to create Job with two containers:
    • init (cloner) container runs a special image which clones repo to volume shared with second (worker) container.
    • worker container set current directory to folder with cloned repo and "runs" worker image with arguments specified in Action resource.
  4. Stores last commit hash for the next run.
  5. Repeat the same for each source in the trigger.
  6. Sleep for the specified interval and repeat everything.

Notes:

  • GitRepo and Action instances can be used by several different triggers, as well as ScheduleTrigger can refer to several sources of some kind.
  • If a source refers to some secret(s) with credentials (if needed), that secret(s) should be created separately.

One more chance to run the same Job is to define WebhookTrigger like this one:

apiVersion: git-events-runner.rs/v1alpha1
kind: WebhookTrigger
metadata:
  name: coll-trigger
  namespace: default
spec:
  sources:
    kind: GitRepo
    names:
      - cool-project-repo # the same as before
    watchOn:
      onChangeOnly: false # pay attention to this
      reference:
        branch: main
  action:
    kind: Action
    name: run-deploy-sh # the same action
  webhook:
    multiSource: true
    authConfig:
      secretRef:
        name: webhook-auth
      key: token

Notice that we:

  • Reused existing GitRepo and Action for another trigger, even of another type.
  • Set onChangeOnly to false to force running Action every time trigger fires (webhook request received) even if there were no changes in the main repo branch.

Namespaced vs. cluster resources

Both types of triggers are namespaced resources. However, Acton and GitRepo have their cluster-level counterparts. This means:

  • Triggers can refer to any kind of actions or sources: namespaced or cluster-wide.
  • WebhookTrigger can use Secrets in its own namespace only.
  • GitRepo can use Secrets in its own namespace only, but ClusterGitRepo can refer secrets in other namespaces but with respect to controller's permissions.
  • Both Action and ClusterAction can create Jobs in the trigger's namespace only.

Cluster-wide sources and actions are useful:

  • in multi-tenant clusters;
  • to share restricted configs between tenants/namespaces;
  • just to avoid repetitions of configuration.

Security aspects

This is pretty risky to bring some code (shell or Python scripts, Ansible playbooks, etc.) to a Kubernetes cluster and run it as a Job. So the first security concern is to ensure we use only code we trust to:

  • On the Git side, this can be guarantied by applying strict code review requirements, restrict permissions to merge code to protected branches or to use protected tags.
  • On the Git Events Runner side, you should configure trigger to use only some specific (secured, restricted) branches and tags.

The second important aspect is permissions within Kubernetes cluster, which the operator uses by itself and to run Jobs. And a whole huge set of the instruments, provided by Kubernetes, can (and should) be used:

  • Role-Based Access Control (RBAC) in conjunction with ServiceAccounts (SA).
  • Running operator with its own SA but Jobs with their separate SAs.
  • Restrictions inside Job containers.
  • Using approved Job images only (ClusterAction as shared restricted resource).

Differences from GitOps

At first glance, Git Events Runner has some similarities with GitOps. However, actually it implements a completely different approach.

First of all, GitOps is about desired configuration of application (or infrastructure), and it's about actual state of application: main task of GitOps operator (let's call it so) is to make actual state the same as desired, and doesn't matter where were changes: at application side (actual state) or in the git repo with config (desired state). So GitOps operator reacts on changes at both sides: desired (git) and actual (application or infrastructure).

Git Events Runner is also intended to react on changes at Git side but not at application/infrastructure (actual) side. This is a huge difference: we just use Git to store code, configs, etc. which we need to execute within a Kubernetes cluster. So we even don't have such an entity as "actual state": Git Events Runner is about "changes as events".

Yes, theoretically it's possible to implement GitOps approach using Git Events Runner, but lots of brilliant GitOps solutions are already implemented.