Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(terraform-provider): Implement terraform helm chart update #6631

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
40 changes: 40 additions & 0 deletions lib/manager/terraform/__fixtures__/helm.tf
@@ -0,0 +1,40 @@
# legit use cases
## complete example
resource "helm_release" "example" {
name = "my-redis-release"
repository = "https://kubernetes-charts.storage.googleapis.com"
chart = "redis"
version = "1.0.1"
}

## example without version, this will default to latest in Terraform
resource "helm_release" "example" {
name = "my-redis-release"
repository = "https://kubernetes-charts.storage.googleapis.com"
chart = "redis"
}

## local chart
resource "helm_release" "local" {
name = "my-local-chart"
chart = "./charts/example"
}

## malformed examples
resource "helm_release" "example" {
name = "my-redis-release"
repository = "https://kubernetes-charts.storage.googleapis.com"
version = "4.0.1"
}

resource "helm_release" "example" {
repository = "https://kubernetes-charts.storage.googleapis.com"
chart = "redis"
version = "5.0.1"
}

resource "helm_release" "example" {
name = "my-redis-release"
chart = "redis"
version = "6.0.1"
}
68 changes: 68 additions & 0 deletions lib/manager/terraform/__snapshots__/extract.spec.ts.snap
@@ -1,5 +1,73 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`lib/manager/terraform/extract extractPackageFile() extract helm releases 1`] = `
Object {
"deps": Array [
Object {
"currentValue": "1.0.1",
"datasource": "helm",
"depName": "redis",
"depNameShort": "redis",
"depType": "helm",
"registryUrls": Array [
"https://kubernetes-charts.storage.googleapis.com",
],
},
Object {
"datasource": "helm",
"depName": "redis",
"depNameShort": "redis",
"depType": "helm",
"registryUrls": Array [
"https://kubernetes-charts.storage.googleapis.com",
],
"skipReason": "unsupported-version",
},
Object {
"datasource": "helm",
"depName": "./charts/example",
"depNameShort": "./charts/example",
"depType": "helm",
"registryUrls": Array [
undefined,
],
"skipReason": "local-chart",
},
Object {
"currentValue": "4.0.1",
"datasource": "helm",
"depName": undefined,
"depNameShort": undefined,
"depType": "helm",
"registryUrls": Array [
"https://kubernetes-charts.storage.googleapis.com",
],
"skipReason": "invalid-name",
},
Object {
"currentValue": "5.0.1",
"datasource": "helm",
"depName": "redis",
"depNameShort": "redis",
"depType": "helm",
"registryUrls": Array [
"https://kubernetes-charts.storage.googleapis.com",
],
},
Object {
"currentValue": "6.0.1",
"datasource": "helm",
"depName": "redis",
"depNameShort": "redis",
"depType": "helm",
"registryUrls": Array [
undefined,
],
},
],
}
`;

exports[`lib/manager/terraform/extract extractPackageFile() extracts 1`] = `
Object {
"deps": Array [
Expand Down
7 changes: 7 additions & 0 deletions lib/manager/terraform/extract.spec.ts
Expand Up @@ -6,6 +6,7 @@ const tf2 = `module "relative" {
source = "../../modules/fe"
}
`;
const helm = readFileSync('lib/manager/terraform/__fixtures__/helm.tf', 'utf8');

describe('lib/manager/terraform/extract', () => {
describe('extractPackageFile()', () => {
Expand All @@ -21,5 +22,11 @@ describe('lib/manager/terraform/extract', () => {
it('returns null if only local deps', () => {
expect(extractPackageFile(tf2)).toBeNull();
});
it('extract helm releases', () => {
const res = extractPackageFile(helm);
expect(res).toMatchSnapshot();
expect(res.deps).toHaveLength(6);
expect(res.deps.filter((dep) => dep.skipReason)).toHaveLength(3);
});
});
});
20 changes: 18 additions & 2 deletions lib/manager/terraform/extract.ts
Expand Up @@ -6,14 +6,23 @@ import {
extractTerraformProvider,
} from './providers';
import { extractTerraformRequiredProviders } from './required_providers';
import {
analyseTerraformResource,
extractTerraformResource,
} from './resources';
import {
TerraformDependencyTypes,
checkFileContainsDependency,
getTerraformDependencyType,
} from './util';

const dependencyBlockExtractionRegex = /^\s*(?<type>module|provider|required_providers)\s+("(?<lookupName>[^"]+)"\s+)?{\s*$/;
const contentCheckList = ['module "', 'provider "', 'required_providers '];
const dependencyBlockExtractionRegex = /^\s*(?<type>[a-z_]+)\s+("(?<lookupName>[^"]+)"\s+)?("(?<terraformName>[^"]+)"\s+)?{\s*$/;
const contentCheckList = [
'module "',
'provider "',
'required_providers ',
' "helm_release" ',
];

export function extractPackageFile(content: string): PackageFile | null {
logger.trace({ content }, 'terraform.extractPackageFile()');
Expand Down Expand Up @@ -55,6 +64,10 @@ export function extractPackageFile(content: string): PackageFile | null {
);
break;
}
case TerraformDependencyTypes.resource: {
result = extractTerraformResource(lineNumber, lines);
break;
}
/* istanbul ignore next */
default:
logger.warn(
Expand All @@ -81,6 +94,9 @@ export function extractPackageFile(content: string): PackageFile | null {
case TerraformDependencyTypes.module:
analyseTerraformModule(dep);
break;
case TerraformDependencyTypes.resource:
analyseTerraformResource(dep);
break;
/* istanbul ignore next */
default:
}
Expand Down
58 changes: 58 additions & 0 deletions lib/manager/terraform/resources.ts
@@ -0,0 +1,58 @@
import * as datasourceHelm from '../../datasource/helm';
import { SkipReason } from '../../types';
import { isValid } from '../../versioning/hashicorp';
import { PackageDependency } from '../common';
import {
ExtractionResult,
TerraformDependencyTypes,
checkIfStringIsPath,
keyValueExtractionRegex,
} from './util';

export function extractTerraformResource(
startingLine: number,
lines: string[]
): ExtractionResult {
let lineNumber = startingLine;
let line;
const deps: PackageDependency[] = [];
const dep: PackageDependency = {
managerData: {
terraformDependencyType: TerraformDependencyTypes.resource,
},
};

do {
lineNumber += 1;
line = lines[lineNumber];
const kvMatch = keyValueExtractionRegex.exec(line);
if (kvMatch) {
if (kvMatch.groups.key === 'version') {
dep.currentValue = kvMatch.groups.value;
} else if (kvMatch.groups.key === 'chart') {
dep.managerData.chart = kvMatch.groups.value;
} else if (kvMatch.groups.key === 'repository') {
dep.managerData.repository = kvMatch.groups.value;
}
}
} while (line.trim() !== '}');
deps.push(dep);
return { lineNumber, dependencies: deps };
}
viceice marked this conversation as resolved.
Show resolved Hide resolved

export function analyseTerraformResource(dep: PackageDependency): void {
/* eslint-disable no-param-reassign */
if (dep.managerData.chart == null) {
dep.skipReason = SkipReason.InvalidName;
} else if (checkIfStringIsPath(dep.managerData.chart)) {
dep.skipReason = SkipReason.LocalChart;
} else if (!isValid(dep.currentValue)) {
dep.skipReason = SkipReason.UnsupportedVersion;
}
dep.depType = 'helm';
dep.registryUrls = [dep.managerData.repository];
dep.depName = dep.managerData.chart;
dep.depNameShort = dep.managerData.chart;
dep.datasource = datasourceHelm.id;
/* eslint-enable no-param-reassign */
}
10 changes: 10 additions & 0 deletions lib/manager/terraform/util.ts
Expand Up @@ -12,6 +12,7 @@ export enum TerraformDependencyTypes {
module = 'module',
provider = 'provider',
required_providers = 'required_providers',
resource = 'resource',
}

export function getTerraformDependencyType(
Expand All @@ -27,6 +28,9 @@ export function getTerraformDependencyType(
case 'required_providers': {
return TerraformDependencyTypes.required_providers;
}
case 'resource': {
return TerraformDependencyTypes.resource;
}
default: {
return TerraformDependencyTypes.unknown;
}
Expand All @@ -41,3 +45,9 @@ export function checkFileContainsDependency(
return content.includes(check);
});
}

const pathStringRegex = /(.|..)?(\/[^/])+/;
export function checkIfStringIsPath(path: string): boolean {
const match = pathStringRegex.exec(path);
return !!match;
}