Skip to content

Commit

Permalink
feat(manager/terraform): support registry aliases for docker images a…
Browse files Browse the repository at this point in the history
…nd oci helm charts (#22022)

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
  • Loading branch information
viceice and secustor committed May 9, 2023
1 parent 004b383 commit 3f33995
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 27 deletions.
1 change: 1 addition & 0 deletions docs/usage/configuration-options.md
Expand Up @@ -3140,6 +3140,7 @@ This feature works with the following managers:
- [`kubernetes`](/modules/manager/kubernetes)
- [`ansible`](/modules/manager/ansible)
- [`droneci`](/modules/manager/droneci)
- [`terraform`](/modules/manager/terraform)

## registryUrls

Expand Down
2 changes: 1 addition & 1 deletion lib/modules/manager/dockerfile/extract.ts
Expand Up @@ -182,7 +182,7 @@ export function getDep(
...getDep(`${value}/${groups.depName}`),
replaceString: currentFrom,
};
dep.autoReplaceStringTemplate = getAutoReplaceTemplate(dep)!;
dep.autoReplaceStringTemplate = getAutoReplaceTemplate(dep);
return dep;
}
}
Expand Down
4 changes: 4 additions & 0 deletions lib/modules/manager/terraform/__fixtures__/docker.tf
Expand Up @@ -12,6 +12,10 @@ resource "docker_image" "ignore_variable" {
pull_triggers = ["${data.docker_registry_image.ubuntu.sha256_digest}"]
}

resource "docker_image" "proxy" {
name = "hub.proxy.test/bitnami/nginx:1.24.0"
}


# docker_container resources
# https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/container
Expand Down
8 changes: 8 additions & 0 deletions lib/modules/manager/terraform/__fixtures__/helm.tf
Expand Up @@ -53,3 +53,11 @@ resource "helm_release" "karpenter_oci_repo" {
chart = "karpenter"
version = "v0.22.1"
}

## chart in OCI registry
resource "helm_release" "proxy_oci_repo" {
name = "kube-prometheus"
repository = "oci://hub.proxy.test/bitnamicharts"
chart = "kube-prometheus"
version = "8.9.1"
}
5 changes: 3 additions & 2 deletions lib/modules/manager/terraform/base.ts
@@ -1,7 +1,7 @@
import is from '@sindresorhus/is';
import { regEx } from '../../../util/regex';
import { TerraformProviderDatasource } from '../../datasource/terraform-provider';
import type { PackageDependency } from '../types';
import type { ExtractConfig, PackageDependency } from '../types';
import type { TerraformDefinitionFile } from './hcl/types';
import type { ProviderLock } from './lockfile/types';
import { getLockedVersion, massageProviderLookupName } from './util';
Expand All @@ -20,7 +20,8 @@ export abstract class DependencyExtractor {
*/
abstract extract(
hclRoot: TerraformDefinitionFile,
locks: ProviderLock[]
locks: ProviderLock[],
config: ExtractConfig
): PackageDependency[];
}

Expand Down
28 changes: 24 additions & 4 deletions lib/modules/manager/terraform/extract.spec.ts
Expand Up @@ -401,8 +401,10 @@ describe('modules/manager/terraform/extract', () => {
});

it('extracts docker resources', async () => {
const res = await extractPackageFile(docker, 'docker.tf', {});
expect(res?.deps).toHaveLength(6);
const res = await extractPackageFile(docker, 'docker.tf', {
registryAliases: { 'hub.proxy.test': 'index.docker.io' },
});
expect(res?.deps).toHaveLength(7);
expect(res?.deps.filter((dep) => dep.skipReason)).toHaveLength(3);
expect(res?.deps).toMatchObject([
{
Expand All @@ -426,6 +428,15 @@ describe('modules/manager/terraform/extract', () => {
depType: 'docker_image',
replaceString: 'nginx:1.7.8',
},
{
autoReplaceStringTemplate:
'hub.proxy.test/bitnami/nginx:{{#if newValue}}{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
currentValue: '1.24.0',
datasource: 'docker',
depName: 'index.docker.io/bitnami/nginx',
depType: 'docker_image',
replaceString: 'hub.proxy.test/bitnami/nginx:1.24.0',
},
{
autoReplaceStringTemplate:
'{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
Expand Down Expand Up @@ -574,8 +585,10 @@ describe('modules/manager/terraform/extract', () => {
});

it('extract helm releases', async () => {
const res = await extractPackageFile(helm, 'helm.tf', {});
expect(res?.deps).toHaveLength(8);
const res = await extractPackageFile(helm, 'helm.tf', {
registryAliases: { 'hub.proxy.test': 'index.docker.io' },
});
expect(res?.deps).toHaveLength(9);
expect(res?.deps.filter((dep) => dep.skipReason)).toHaveLength(2);
expect(res?.deps).toMatchObject([
{
Expand Down Expand Up @@ -617,6 +630,13 @@ describe('modules/manager/terraform/extract', () => {
depType: 'helm_release',
skipReason: 'local-chart',
},
{
currentValue: '8.9.1',
datasource: 'docker',
depName: 'kube-prometheus',
depType: 'helm_release',
packageName: 'index.docker.io/bitnamicharts/kube-prometheus',
},
{
currentValue: '1.0.1',
datasource: 'helm',
Expand Down
2 changes: 1 addition & 1 deletion lib/modules/manager/terraform/extract.ts
Expand Up @@ -46,7 +46,7 @@ export async function extractPackageFile(
const locks = await extractLocksForPackageFile(fileName);

for (const extractor of passedExtractors) {
const deps = extractor.extract(hclMap, locks);
const deps = extractor.extract(hclMap, locks, config);
dependencies.push(...deps);
}

Expand Down
Expand Up @@ -4,7 +4,7 @@ describe('modules/manager/terraform/extractors/resources/generic-docker-image-re
const extractor = new GenericDockerImageRefExtractor();

it('return empty array if no resource is found', () => {
const res = extractor.extract({});
const res = extractor.extract({}, [], {});
expect(res).toBeArrayOfSize(0);
});
});
@@ -1,16 +1,21 @@
import is from '@sindresorhus/is';
import { getDep } from '../../../dockerfile/extract';
import type { PackageDependency } from '../../../types';
import type { ExtractConfig, PackageDependency } from '../../../types';
import { DependencyExtractor } from '../../base';
import type { TerraformDefinitionFile } from '../../hcl/types';
import type { ProviderLock } from '../../lockfile/types';
import { generic_image_resource } from './utils';

export class GenericDockerImageRefExtractor extends DependencyExtractor {
getCheckList(): string[] {
return generic_image_resource.map((value) => `"${value.type}"`);
}

extract(hclMap: TerraformDefinitionFile): PackageDependency[] {
extract(
hclMap: TerraformDefinitionFile,
_locks: ProviderLock[],
config: ExtractConfig
): PackageDependency[] {
const resourceTypMap = hclMap.resource;
if (is.nullOrUndefined(resourceTypMap)) {
return [];
Expand All @@ -28,7 +33,9 @@ export class GenericDockerImageRefExtractor extends DependencyExtractor {

// loop over instances of a resource type
for (const instance of Object.values(resourceInstancesMap).flat()) {
dependencies.push(...this.walkPath({ depType: type }, instance, path));
dependencies.push(
...this.walkPath({ depType: type }, instance, path, config)
);
}
}
return dependencies;
Expand All @@ -45,7 +52,8 @@ export class GenericDockerImageRefExtractor extends DependencyExtractor {
private walkPath(
abstractDep: PackageDependency,
parentElement: unknown,
leftPath: string[]
leftPath: string[],
config: ExtractConfig
): PackageDependency[] {
const dependencies: PackageDependency[] = [];
// if there are no path elements left, we have reached the end of the path
Expand All @@ -59,7 +67,7 @@ export class GenericDockerImageRefExtractor extends DependencyExtractor {
},
];
}
const test = getDep(parentElement);
const test = getDep(parentElement, true, config.registryAliases);
const dep: PackageDependency = {
...abstractDep,
...test,
Expand Down Expand Up @@ -87,11 +95,11 @@ export class GenericDockerImageRefExtractor extends DependencyExtractor {
if (is.array(element)) {
for (const arrayElement of element) {
dependencies.push(
...this.walkPath(abstractDep, arrayElement, leftPath.slice(1))
...this.walkPath(abstractDep, arrayElement, leftPath.slice(1), config)
);
}
return dependencies;
}
return this.walkPath(abstractDep, element, leftPath.slice(1));
return this.walkPath(abstractDep, element, leftPath.slice(1), config);
}
}
Expand Up @@ -4,7 +4,7 @@ describe('modules/manager/terraform/extractors/resources/helm-release', () => {
const extractor = new HelmReleaseExtractor();

it('return empty array if no resource is found', () => {
const res = extractor.extract({});
const res = extractor.extract({}, [], {});
expect(res).toBeArrayOfSize(0);
});
});
40 changes: 30 additions & 10 deletions lib/modules/manager/terraform/extractors/resources/helm-release.ts
@@ -1,20 +1,25 @@
import is from '@sindresorhus/is';
import { logger } from '../../../../../logger';
import { joinUrlParts } from '../../../../../util/url';
import { DockerDatasource } from '../../../../datasource/docker';
import { HelmDatasource } from '../../../../datasource/helm';
import { getDep } from '../../../dockerfile/extract';
import { isOCIRegistry } from '../../../helmv3/utils';
import type { PackageDependency } from '../../../types';
import type { ExtractConfig, PackageDependency } from '../../../types';
import { DependencyExtractor } from '../../base';
import type { TerraformDefinitionFile } from '../../hcl/types';
import type { ProviderLock } from '../../lockfile/types';
import { checkIfStringIsPath } from '../../util';

export class HelmReleaseExtractor extends DependencyExtractor {
getCheckList(): string[] {
return [`"helm_release"`];
}

override extract(hclMap: TerraformDefinitionFile): PackageDependency[] {
override extract(
hclMap: TerraformDefinitionFile,
_locks: ProviderLock[],
config: ExtractConfig
): PackageDependency[] {
const dependencies = [];

const helmReleases = hclMap?.resource?.helm_release;
Expand Down Expand Up @@ -46,19 +51,20 @@ export class HelmReleaseExtractor extends DependencyExtractor {
} else if (isOCIRegistry(helmRelease.chart)) {
// For oci charts, we remove the oci:// and use the docker datasource
dep.depName = helmRelease.chart.replace('oci://', '');
dep.datasource = DockerDatasource.id;
this.processOCI(dep.depName, config, dep);
} else if (checkIfStringIsPath(helmRelease.chart)) {
dep.skipReason = 'local-chart';
} else if (is.nonEmptyString(helmRelease.repository)) {
if (isOCIRegistry(helmRelease.repository)) {
{
// For oci repos, we remove the oci://, join the chart name and use the docker datasource
dep.packageName = joinUrlParts(
// For oci charts, we remove the oci:// and use the docker datasource
this.processOCI(
joinUrlParts(
helmRelease.repository.replace('oci://', ''),
helmRelease.chart
);
dep.datasource = DockerDatasource.id;
}
),
config,
dep
);
} else {
dep.registryUrls = [helmRelease.repository];
}
Expand All @@ -67,4 +73,18 @@ export class HelmReleaseExtractor extends DependencyExtractor {

return dependencies;
}

private processOCI(
depName: string,
config: ExtractConfig,
dep: PackageDependency
): void {
const { depName: packageName, datasource } = getDep(
depName,
false,
config.registryAliases
);
dep.packageName = packageName;
dep.datasource = datasource;
}
}

0 comments on commit 3f33995

Please sign in to comment.