feat(manager/terraform): support multiple container images (#16107)

Co-authored-by: Rhys Arkins <rhys@arkins.net>
This commit is contained in:
Michael Kriese 2022-06-16 16:06:36 +02:00 committed by GitHub
parent d05e22e081
commit 827d313de4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 108 additions and 4 deletions

View file

@ -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"
}
}

View file

@ -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',

View file

@ -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 };
}

View file

@ -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');

View file

@ -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];