From a9c9d56779a3c10b3d8a26bd4eb412c674d4215b Mon Sep 17 00:00:00 2001 From: Sebastian Poxhofer Date: Thu, 29 Apr 2021 15:39:35 +0200 Subject: [PATCH] feat(argocd): implement manager (#9771) --- lib/manager/api.ts | 2 + .../__fixtures__/malformedApplications.yml | 11 ++++ .../argocd/__fixtures__/randomManifest.yml | 21 ++++++++ .../argocd/__fixtures__/validApplication.yml | 36 +++++++++++++ .../argocd/__snapshots__/extract.spec.ts.snap | 27 ++++++++++ lib/manager/argocd/extract.spec.ts | 34 +++++++++++++ lib/manager/argocd/extract.ts | 50 +++++++++++++++++++ lib/manager/argocd/index.ts | 5 ++ lib/manager/argocd/readme.md | 33 ++++++++++++ lib/manager/argocd/types.ts | 9 ++++ lib/manager/argocd/util.ts | 3 ++ 11 files changed, 231 insertions(+) create mode 100644 lib/manager/argocd/__fixtures__/malformedApplications.yml create mode 100644 lib/manager/argocd/__fixtures__/randomManifest.yml create mode 100644 lib/manager/argocd/__fixtures__/validApplication.yml create mode 100644 lib/manager/argocd/__snapshots__/extract.spec.ts.snap create mode 100644 lib/manager/argocd/extract.spec.ts create mode 100644 lib/manager/argocd/extract.ts create mode 100644 lib/manager/argocd/index.ts create mode 100644 lib/manager/argocd/readme.md create mode 100644 lib/manager/argocd/types.ts create mode 100644 lib/manager/argocd/util.ts diff --git a/lib/manager/api.ts b/lib/manager/api.ts index 0a1a2c49d2e5f6..27ddd92efaf2f0 100644 --- a/lib/manager/api.ts +++ b/lib/manager/api.ts @@ -1,5 +1,6 @@ import * as ansible from './ansible'; import * as ansibleGalaxy from './ansible-galaxy'; +import * as argoCD from './argocd'; import * as azurePipelines from './azure-pipelines'; import * as batect from './batect'; import * as batectWrapper from './batect-wrapper'; @@ -65,6 +66,7 @@ export default api; api.set('ansible', ansible); api.set('ansible-galaxy', ansibleGalaxy); +api.set('argocd', argoCD); api.set('azure-pipelines', azurePipelines); api.set('batect', batect); api.set('batect-wrapper', batectWrapper); diff --git a/lib/manager/argocd/__fixtures__/malformedApplications.yml b/lib/manager/argocd/__fixtures__/malformedApplications.yml new file mode 100644 index 00000000000000..574b683230cc89 --- /dev/null +++ b/lib/manager/argocd/__fixtures__/malformedApplications.yml @@ -0,0 +1,11 @@ +--- +# malformed application as the source section is missing +apiVersion: argoproj.io/v1alpha1 +kind: Application +spec: + target: + namespace: testing +--- +# malformed application as the source section is missing +apiVersion: argoproj.io/v1alpha1 +kind: Application diff --git a/lib/manager/argocd/__fixtures__/randomManifest.yml b/lib/manager/argocd/__fixtures__/randomManifest.yml new file mode 100644 index 00000000000000..007ecd317d2de6 --- /dev/null +++ b/lib/manager/argocd/__fixtures__/randomManifest.yml @@ -0,0 +1,21 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 diff --git a/lib/manager/argocd/__fixtures__/validApplication.yml b/lib/manager/argocd/__fixtures__/validApplication.yml new file mode 100644 index 00000000000000..d8146bec1b02f5 --- /dev/null +++ b/lib/manager/argocd/__fixtures__/validApplication.yml @@ -0,0 +1,36 @@ +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +spec: + source: + chart: kube-state-metrics + repoURL: https://prometheus-community.github.io/helm-charts + targetRevision: 2.4.1 +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +spec: + source: + chart: traefik + helm: + values: | + traefik: + service: + spec: + loadBalancerIP: 1.2.3.4 + repoURL: gs://helm-charts-internal + targetRevision: 0.0.2 +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +spec: + source: + repoURL: https://git.example.com/foo/bar.git + targetRevision: v1.2.0 +--- +# malformed application as the source section is missing +apiVersion: argoproj.io/v1alpha1 +kind: Application +spec: + target: + namespace: testing diff --git a/lib/manager/argocd/__snapshots__/extract.spec.ts.snap b/lib/manager/argocd/__snapshots__/extract.spec.ts.snap new file mode 100644 index 00000000000000..c5c2f9abf557f0 --- /dev/null +++ b/lib/manager/argocd/__snapshots__/extract.spec.ts.snap @@ -0,0 +1,27 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`manager/argocd/extract extractPackageFile() full test 1`] = ` +Array [ + Object { + "currentValue": "2.4.1", + "datasource": "helm", + "depName": "kube-state-metrics", + "registryUrls": Array [ + "https://prometheus-community.github.io/helm-charts", + ], + }, + Object { + "currentValue": "0.0.2", + "datasource": "helm", + "depName": "traefik", + "registryUrls": Array [ + "gs://helm-charts-internal", + ], + }, + Object { + "currentValue": "v1.2.0", + "datasource": "git-tags", + "depName": "https://git.example.com/foo/bar.git", + }, +] +`; diff --git a/lib/manager/argocd/extract.spec.ts b/lib/manager/argocd/extract.spec.ts new file mode 100644 index 00000000000000..1160b6b95f4b44 --- /dev/null +++ b/lib/manager/argocd/extract.spec.ts @@ -0,0 +1,34 @@ +import { getName, loadFixture } from '../../../test/util'; +import { extractPackageFile } from './extract'; + +const validApplication = loadFixture('validApplication.yml'); +const malformedApplication = loadFixture('malformedApplications.yml'); +const randomManifest = loadFixture('randomManifest.yml'); + +describe(getName(), () => { + describe('extractPackageFile()', () => { + it('returns null for empty', () => { + expect(extractPackageFile('nothing here', 'applications.yml')).toBeNull(); + }); + + it('return null for kubernetes manifest', () => { + const result = extractPackageFile(randomManifest, 'applications.yml'); + expect(result).toBeNull(); + }); + + it('return null if deps array would be empty', () => { + const result = extractPackageFile( + malformedApplication, + 'applications.yml' + ); + expect(result).toBeNull(); + }); + + it('full test', () => { + const result = extractPackageFile(validApplication, 'applications.yml'); + expect(result).not.toBeNull(); + expect(result.deps).toBeArrayOfSize(3); + expect(result.deps).toMatchSnapshot(); + }); + }); +}); diff --git a/lib/manager/argocd/extract.ts b/lib/manager/argocd/extract.ts new file mode 100644 index 00000000000000..3ff72772f8ce2a --- /dev/null +++ b/lib/manager/argocd/extract.ts @@ -0,0 +1,50 @@ +import { safeLoadAll } from 'js-yaml'; +import * as gitTags from '../../datasource/git-tags'; +import * as helm from '../../datasource/helm'; +import type { ExtractConfig, PackageDependency, PackageFile } from '../types'; +import type { ApplicationDefinition } from './types'; +import { fileTestRegex } from './util'; + +function createDependency( + definition: ApplicationDefinition +): PackageDependency { + const source = definition.spec?.source; + + if (source == null) { + return null; + } + + // a chart variable is defined this is helm declaration + if (source.chart) { + return { + depName: source.chart, + registryUrls: [source.repoURL], + currentValue: source.targetRevision, + datasource: helm.id, + }; + } + return { + depName: source.repoURL, + currentValue: source.targetRevision, + datasource: gitTags.id, + }; +} + +export function extractPackageFile( + content: string, + fileName: string, + config?: ExtractConfig +): PackageFile | null { + // check for argo reference. API version for the kind attribute is used + if (fileTestRegex.test(content) === false) { + return null; + } + + const definitions: ApplicationDefinition[] = safeLoadAll(content); + + const deps = definitions + .map((definition) => createDependency(definition)) + .filter(Boolean); + + return deps.length ? { deps } : null; +} diff --git a/lib/manager/argocd/index.ts b/lib/manager/argocd/index.ts new file mode 100644 index 00000000000000..1012c71468dbc4 --- /dev/null +++ b/lib/manager/argocd/index.ts @@ -0,0 +1,5 @@ +export { extractPackageFile } from './extract'; + +export const defaultConfig = { + fileMatch: [], +}; diff --git a/lib/manager/argocd/readme.md b/lib/manager/argocd/readme.md new file mode 100644 index 00000000000000..4bc8a70e7945c8 --- /dev/null +++ b/lib/manager/argocd/readme.md @@ -0,0 +1,33 @@ +The `argocd` manager has no `fileMatch` default patterns, so it won't match any files until you configure it with a pattern. This is because there is no commonly accepted file/directory naming convention for argocd YAML files and we don't want to check every single `*.yaml` file in repositories just in case any of them contain ArgoCD definitions. + +If most `.yaml` files in your repository are argocd ones, then you could add this to your config: + +```json +{ + "argocd": { + "fileMatch": ["\\.yaml$"] + } +} +``` + +If instead you have them all inside a `argocd/` directory, you would add this: + +```json +{ + "argocd": { + "fileMatch": ["argocd/.+\\.yaml$"] + } +} +``` + +Or if it's just a single file then something like this: + +```json +{ + "argocd": { + "fileMatch": ["^config/applications\\.yaml$"] + } +} +``` + +If you need to change the versioning format, read the [versioning](https://docs.renovatebot.com/modules/versioning/) documentation to learn more. diff --git a/lib/manager/argocd/types.ts b/lib/manager/argocd/types.ts new file mode 100644 index 00000000000000..8a84291575d077 --- /dev/null +++ b/lib/manager/argocd/types.ts @@ -0,0 +1,9 @@ +export interface ApplicationDefinition { + spec: { + source: { + chart?: string; + repoURL: string; + targetRevision: string; + }; + }; +} diff --git a/lib/manager/argocd/util.ts b/lib/manager/argocd/util.ts new file mode 100644 index 00000000000000..0c0d1434820980 --- /dev/null +++ b/lib/manager/argocd/util.ts @@ -0,0 +1,3 @@ +export const keyValueExtractionRegex = /^\s*(?[^\s]+):\s+"?(?[^"\s]+)"?\s*$/; +// looks for `apiVersion: argoproj.io/ +export const fileTestRegex = /\s*apiVersion:\s*argoproj.io\/\s*/;