Skip to content

Commit

Permalink
Add support for Jobs (#52)
Browse files Browse the repository at this point in the history
* refactor SourceApp before adding SourceJob

* skip yaml files with wrong kind

* start working on ContainerJob

* update reconciler

* update tests

* skip on other kind

* update readme

* rewrite reconcile test to use t.Run()

* remove comments

* extend reconcile with job tests

* update readme
  • Loading branch information
simongottschlag authored Jul 12, 2023
1 parent 5244558 commit 6c9c215
Show file tree
Hide file tree
Showing 25 changed files with 3,202 additions and 702 deletions.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ run:
go run ./src \
--debug \
--resource-group-name $${RG_NAME} \
--own-resource-group-name $${OWN_RG_NAME} \
--subscription-id $${SUB_ID} \
--managed-environment-id $${ME_ID} \
--key-vault-name $${KV_NAME} \
Expand All @@ -58,7 +59,8 @@ run:
--git-url $${GIT_URL_AND_CREDS} \
--git-branch "main" \
--git-yaml-path "yaml/" \
--notifications-enabled
--notifications-enabled \
--environment $${ENV}

docker-build:
docker build . -t $(IMG)
Expand Down
57 changes: 53 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ It's [GitOps](https://opengitops.dev/#principles) for [Azure Container Apps](htt

> Please note: This is an early version and will have lots of breaking changes in the future.
## Changelog

Below, large (and eventually breaking) will be documented:

### v0.0.18

Support for `AzureContainerJob` was added.

**BREAKING CHANGES**

- `spec.remoteSecrets[].appSecretName` has been renamed `spec.remoteSecrets[].secretName`
- `apiVersion` has been updated from `aca.xenit.io/v1alpha1` to `aca.xenit.io/v1alpha2` for kind `AzureContainerApp`

## Overview

![overview](docs/overview.png "Overview of azcagit")
Expand All @@ -19,20 +32,20 @@ Platform is used for what we call "platform services", in this case the virtual

Tenant is used only to synchronize the Container Apps manifests. The Container Apps that are created by `azcagit` will reside here.

The manifests are in the same format as Kubernetes manifests ([Kubernetes Resource Model aka KRM](https://cloud.google.com/blog/topics/developers-practitioners/build-platform-krm-part-2-how-kubernetes-resource-model-works)), but with a hard coupling to the [Azure Container Apps specification](https://docs.microsoft.com/en-us/azure/templates/microsoft.app/containerapps?pivots=deployment-language-arm-template) for `spec.app`.
The manifests are in the same format as Kubernetes manifests ([Kubernetes Resource Model aka KRM](https://cloud.google.com/blog/topics/developers-practitioners/build-platform-krm-part-2-how-kubernetes-resource-model-works)), but with a hard coupling to the [Azure Container Apps specification](https://docs.microsoft.com/en-us/azure/templates/microsoft.app/containerapps?pivots=deployment-language-arm-template) for `spec.app` when using `kind: AzureContainerApp` and [Azure Container Jobs specification](https://learn.microsoft.com/en-us/azure/templates/microsoft.app/jobs?pivots=deployment-language-arm-template) for `spec.job` when using `kind: AzureContainerJob`.

An example manifest:
An example manifest of an app:

```yaml
kind: AzureContainerApp
apiVersion: aca.xenit.io/v1alpha1
apiVersion: aca.xenit.io/v1alpha2
metadata:
name: foobar
spec:
locationFilter:
- West Europe
remoteSecrets:
- appSecretName: connection-string
- secretName: connection-string
remoteSecretName: mssql-connection-string
replacements:
images:
Expand All @@ -59,6 +72,42 @@ spec:
maxReplicas: 1
```
example manifest of a job:
```yaml
kind: AzureContainerJob
apiVersion: aca.xenit.io/v1alpha2
metadata:
name: foobar
spec:
locationFilter:
- West Europe
remoteSecrets:
- secretName: connection-string
remoteSecretName: mssql-connection-string
replacements:
images:
- imageName: "mcr.microsoft.com/k8se/quickstart-jobs"
newImageTag: "latest"
job:
properties:
configuration:
scheduleTriggerConfig:
cronExpression: "*/5 * * * *"
parallelism: 1
replicaCompletionCount: 1
replicaRetryLimit: 1
replicaTimeout: 1800
triggerType: Schedule
template:
containers:
- name: main
image: mcr.microsoft.com/k8se/quickstart-jobs:foobar
resources:
cpu: 0.25
memory: .5Gi
```
YAML-files can contain one or more documents (with `---` as a document separator). As of right now, all files in the git repository path (configured with `--git-path` when launching `azcagit`) needs to pass validation for any deletion to occur (deletion will be disabled if any manifests contains validation errors).

## Features
Expand Down
86 changes: 86 additions & 0 deletions src/cache/job.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package cache

import (
"crypto/md5"
"fmt"
"time"

"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appcontainers/armappcontainers/v2"
)

type JobCacheEntry struct {
modified time.Time
sourceJobHash string
}

type JobCache map[string]JobCacheEntry

func NewJobCache() *JobCache {
c := make(JobCache)
return &c
}

func (c *JobCache) Set(name string, remoteJob, sourceJob *armappcontainers.Job) {
if remoteJob == nil {
return
}
if remoteJob.SystemData == nil {
return
}

timestamp := remoteJob.SystemData.LastModifiedAt
if timestamp == nil {
if remoteJob.SystemData.CreatedAt == nil {
return
}
timestamp = remoteJob.SystemData.CreatedAt
}

b, err := sourceJob.MarshalJSON()
if err != nil {
return
}
hash := fmt.Sprintf("%x", md5.Sum(b))

(*c)[name] = JobCacheEntry{
modified: *timestamp,
sourceJobHash: hash,
}
}

func (c *JobCache) NeedsUpdate(name string, remoteJob, sourceJob *armappcontainers.Job) (bool, string) {
entry, ok := (*c)[name]
if !ok {
return true, "not in JobCache"
}

if remoteJob == nil {
return true, "remoteJob nil"
}
if remoteJob.SystemData == nil {
return true, "remoteJob SystemData nil"
}

if remoteJob.SystemData.LastModifiedAt != nil {
if entry.modified != *remoteJob.SystemData.LastModifiedAt {
return true, "changed LastModifiedAt"
}
} else if remoteJob.SystemData.CreatedAt != nil {
if entry.modified != *remoteJob.SystemData.CreatedAt {
return true, "changed CreatedAt"
}
}

b, err := sourceJob.MarshalJSON()
if err != nil {
return true, "sourceJob MarshalJSON() failed"
}

hash := fmt.Sprintf("%x", md5.Sum(b))

if entry.sourceJobHash != hash {
return true, "changed sourceJob hash"
}

return false, "no changes"
}
10 changes: 8 additions & 2 deletions src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ func run(ctx context.Context, cfg config.Config) error {
return err
}

remoteClient, err := remote.NewAzureRemote(cfg, cred)
remoteAppClient, err := remote.NewAzureApp(cfg, cred)
if err != nil {
return err
}

remoteJobClient, err := remote.NewAzureJob(cfg, cred)
if err != nil {
return err
}
Expand All @@ -81,9 +86,10 @@ func run(ctx context.Context, cfg config.Config) error {
metricsClient := metrics.NewAzureMetrics(cfg, cred)

appCache := cache.NewAppCache()
jobCache := cache.NewJobCache()
secretCache := cache.NewSecretCache()

reconciler, err := reconcile.NewReconciler(cfg, sourceClient, remoteClient, secretClient, notificationClient, metricsClient, appCache, secretCache)
reconciler, err := reconcile.NewReconciler(cfg, sourceClient, remoteAppClient, remoteJobClient, secretClient, notificationClient, metricsClient, appCache, jobCache, secretCache)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit 6c9c215

Please sign in to comment.