diff --git a/lib/modules/manager/terraform/__fixtures__/kubernetes.tf b/lib/modules/manager/terraform/__fixtures__/kubernetes.tf index bd8e5886b4f494ee9abe100df3b77d62610f30b0..cba164672a054c45b935dc9454d46b51a915c0e7 100644 --- a/lib/modules/manager/terraform/__fixtures__/kubernetes.tf +++ b/lib/modules/manager/terraform/__fixtures__/kubernetes.tf @@ -11,6 +11,10 @@ resource "kubernetes_cron_job_v1" "demo" { name = "kaniko" image = "gcr.io/kaniko-project/executor:v1.7.0@sha256:8504bde9a9a8c9c4e9a4fe659703d265697a36ff13607b7669a4caa4407baa52" } + container { + name = "node" + image = "node:14" + } } } } @@ -126,6 +130,7 @@ resource "kubernetes_job" "demo_invalid" { } } } + image = "nginx:1.21.6" } } diff --git a/lib/modules/manager/terraform/extract.spec.ts b/lib/modules/manager/terraform/extract.spec.ts index e876b5bc0a56a92a46179897c0cde3395452c26a..cc82364c0cbdd087c44068bf83b3a3a84f1d22a5 100644 --- a/lib/modules/manager/terraform/extract.spec.ts +++ b/lib/modules/manager/terraform/extract.spec.ts @@ -107,8 +107,8 @@ describe('modules/manager/terraform/extract', () => { it('extracts kubernetes resources', async () => { const res = await extractPackageFile(kubernetes, 'kubernetes.tf', {}); - expect(res.deps).toHaveLength(16); - expect(res.deps.filter((dep) => dep.skipReason)).toHaveLength(2); + expect(res.deps).toHaveLength(18); + expect(res.deps.filter((dep) => dep.skipReason)).toHaveLength(1); expect(res.deps).toMatchObject([ { depName: 'gcr.io/kaniko-project/executor', @@ -117,6 +117,11 @@ describe('modules/manager/terraform/extract', () => { 'sha256:8504bde9a9a8c9c4e9a4fe659703d265697a36ff13607b7669a4caa4407baa52', depType: 'kubernetes_cron_job_v1', }, + { + depName: 'node', + currentValue: '14', + depType: 'kubernetes_cron_job_v1', + }, { depName: 'gcr.io/kaniko-project/executor', currentValue: 'v1.8.0', @@ -149,7 +154,6 @@ describe('modules/manager/terraform/extract', () => { currentValue: '1.21.5', depType: 'kubernetes_job', }, - { skipReason: 'invalid-dependency-specification' }, { skipReason: 'invalid-value' }, { depName: 'nginx', @@ -176,11 +180,21 @@ describe('modules/manager/terraform/extract', () => { currentValue: '1.21.10', depType: 'kubernetes_replication_controller_v1', }, + { + depName: 'nginx', + currentValue: '1.21.11', + depType: 'kubernetes_stateful_set', + }, { depName: 'prom/prometheus', currentValue: 'v2.2.1', depType: 'kubernetes_stateful_set', }, + { + depName: 'nginx', + currentValue: '1.21.12', + depType: 'kubernetes_stateful_set_v1', + }, { depName: 'prom/prometheus', currentValue: 'v2.2.2', diff --git a/lib/modules/manager/terraform/extract/kubernetes.ts b/lib/modules/manager/terraform/extract/kubernetes.ts new file mode 100644 index 0000000000000000000000000000000000000000..dee54e8fb0e29eeefda74dd41f01d5b7f6aeff88 --- /dev/null +++ b/lib/modules/manager/terraform/extract/kubernetes.ts @@ -0,0 +1,76 @@ +import is from '@sindresorhus/is'; +import { logger } from '../../../../logger'; +import { regEx } from '../../../../util/regex'; +import type { PackageDependency } from '../../types'; +import { TerraformDependencyTypes } from '../common'; +import type { ExtractionResult, ResourceManagerData } from '../types'; +import { keyValueExtractionRegex } from '../util'; + +export function extractTerraformKubernetesResource( + startingLine: number, + lines: string[], + resourceType: string +): ExtractionResult { + let lineNumber = startingLine; + const deps: PackageDependency<ResourceManagerData>[] = []; + + /** + * Iterates over all lines of the resource to extract the relevant key value pairs, + * e.g. the chart name for helm charts or the terraform_version for tfe_workspace + */ + let braceCounter = 0; + let inContainer = -1; + do { + // istanbul ignore if + if (lineNumber > lines.length - 1) { + logger.debug(`Malformed Terraform file detected.`); + } + + const line = lines[lineNumber]; + + // istanbul ignore else + if (is.string(line)) { + // `{` will be counted with +1 and `}` with -1. Therefore if we reach braceCounter == 0. We have found the end of the terraform block + const openBrackets = (line.match(regEx(/\{/g)) || []).length; + const closedBrackets = (line.match(regEx(/\}/g)) || []).length; + braceCounter = braceCounter + openBrackets - closedBrackets; + + if (line.match(regEx(/^\s*(?:init_)?container(?:\s*\{|$)/s))) { + inContainer = braceCounter; + } else if (braceCounter < inContainer) { + inContainer = -1; + } + + const managerData: ResourceManagerData = { + terraformDependencyType: TerraformDependencyTypes.resource, + resourceType, + }; + const dep: PackageDependency<ResourceManagerData> = { + managerData, + }; + + const kvMatch = keyValueExtractionRegex.exec(line); + if (kvMatch?.groups && inContainer > 0) { + switch (kvMatch.groups.key) { + case 'image': + managerData[kvMatch.groups.key] = kvMatch.groups.value; + managerData.sourceLine = lineNumber; + deps.push(dep); + break; + default: + /* istanbul ignore next */ + break; + } + } + } else { + // stop - something went wrong + braceCounter = 0; + inContainer = -1; + } + lineNumber += 1; + } while (braceCounter !== 0); + + // remove last lineNumber addition to not skip a line after the last bracket + lineNumber -= 1; + return { lineNumber, dependencies: deps }; +} diff --git a/lib/modules/manager/terraform/lockfile/index.spec.ts b/lib/modules/manager/terraform/lockfile/index.spec.ts index 3aa366621aa7fee214110c8486024961f2bcba9a..350666da081dde11ff8ed121a2003bd27b6bd5de 100644 --- a/lib/modules/manager/terraform/lockfile/index.spec.ts +++ b/lib/modules/manager/terraform/lockfile/index.spec.ts @@ -3,8 +3,8 @@ import { fs, loadFixture, mocked } from '../../../../../test/util'; import { GlobalConfig } from '../../../../config/global'; import { getPkgReleases } from '../../../datasource'; import type { UpdateArtifactsConfig } from '../../types'; +import { updateArtifacts } from '../index'; import { TerraformProviderHash } from './hash'; -import { updateArtifacts } from './index'; // auto-mock fs jest.mock('../../../../util/fs'); diff --git a/lib/modules/manager/terraform/resources.ts b/lib/modules/manager/terraform/resources.ts index 9811c8f104196184c8da3b7f5757d61dc5201b35..c691e940e7fe522eae41fc46b95a88c9f8fadc54 100644 --- a/lib/modules/manager/terraform/resources.ts +++ b/lib/modules/manager/terraform/resources.ts @@ -5,6 +5,7 @@ import { HelmDatasource } from '../../datasource/helm'; import { getDep } from '../dockerfile/extract'; import type { PackageDependency } from '../types'; import { TerraformDependencyTypes, TerraformResourceTypes } from './common'; +import { extractTerraformKubernetesResource } from './extract/kubernetes'; import { analyseTerraformVersion } from './required-version'; import type { ExtractionResult, ResourceManagerData } from './types'; import { @@ -46,6 +47,14 @@ export function extractTerraformResource( return TerraformResourceTypes[key].includes(resourceType); }); + if (isKnownType && resourceType.startsWith('kubernetes_')) { + return extractTerraformKubernetesResource( + startingLine, + lines, + resourceType + ); + } + managerData.resourceType = isKnownType ? resourceType : TerraformResourceTypes.unknown[0];