Skip to content

Commit

Permalink
feat(terraform-provider): implement updating of helm releases
Browse files Browse the repository at this point in the history
  • Loading branch information
secustor committed Jun 28, 2020
1 parent 914ac1d commit 98a8b96
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 4 deletions.
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
29 changes: 29 additions & 0 deletions lib/manager/terraform/extract.spec.ts
@@ -1,6 +1,7 @@
import { readFileSync } from 'fs';
import {
TerraformDependencyTypes,
checkIfStringIsPath,
extractPackageFile,
getTerraformDependencyType,
} from './extract';
Expand All @@ -10,6 +11,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 @@ -25,6 +27,12 @@ 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);
});
});
describe('getTerraformDependencyType()', () => {
it('returns TerraformDependencyTypes.module', () => {
Expand Down Expand Up @@ -58,4 +66,25 @@ describe('lib/manager/terraform/extract', () => {
);
});
});
describe('checkIfStringIsPath()', () => {
it('check simple string', () => {
expect(checkIfStringIsPath('sdfsgdsfadfhfghfhgdfsdf')).toBe(false);
});
it('check local path', () => {
expect(checkIfStringIsPath('./sdfsgdsfadfhfghfhgdfsdf')).toBe(true);
});
it('check up path', () => {
expect(checkIfStringIsPath('../sdfsgdsfadfhfghfhgdfsdf')).toBe(true);
});
it('check up deep path', () => {
expect(
checkIfStringIsPath('../sdfsgdsfadfhfghfhgdfsdf/sklskdjfksd')
).toBe(true);
});
it('check up another deep path', () => {
expect(checkIfStringIsPath('sdfsgdsfadfhfghfhgdfsdf/sklskdjfksd')).toBe(
true
);
});
});
});
60 changes: 56 additions & 4 deletions lib/manager/terraform/extract.ts
@@ -1,5 +1,6 @@
import * as datasourceGitTags from '../../datasource/git-tags';
import * as datasourceGithubTags from '../../datasource/github-tags';
import * as datasourceHelm from '../../datasource/helm';
import * as datasourceTerraformModule from '../../datasource/terraform-module';
import * as datasourceTerraformProvider from '../../datasource/terraform-provider';
import { logger } from '../../logger';
Expand All @@ -12,6 +13,7 @@ export enum TerraformDependencyTypes {
module = 'module',
provider = 'provider',
required_providers = 'required_providers',
resource = 'resource',
}

export function getTerraformDependencyType(
Expand All @@ -27,6 +29,9 @@ export function getTerraformDependencyType(
case 'required_providers': {
return TerraformDependencyTypes.required_providers;
}
case 'resource': {
return TerraformDependencyTypes.resource;
}
default: {
return TerraformDependencyTypes.unknown;
}
Expand All @@ -42,8 +47,19 @@ function checkFileContainsDependency(
});
}

const dependencyBlockExtractionRegex = /^\s*(?<type>module|provider|required_providers)\s+("(?<lookupName>[^"]+)"\s+)?{\s*$/;
const contentCheckList = ['module "', 'provider "', 'required_providers '];
export function checkIfStringIsPath(path: string): boolean {
const regex = /(.|..)?(\/[^/])+/;
const match = regex.exec(path);
return !!match;
}

const dependencyBlockExtractionRegex = /^\s*(?<type>[a-z_]+)\s+("(?<lookupName>[^"]+)"\s+)?("(?<terraformName>[^"]+)"\s+)?{\s*$/;
const contentCheckList = [
'module "',
'provider "',
'required_providers ',
' "helm_release" ',
];
const keyValueExtractionRegex = /^\s*(?<key>[^\s]+)\s+=\s+"(?<value>[^"]+)"\s*$/; // extracts `exampleKey = exampleValue`
const githubRefMatchRegex = /github.com(\/|:)([^/]+\/[a-z0-9-.]+).*\?ref=(.*)$/;
const gitTagsRefMatchRegex = /(?:git::)?((?:http|https|ssh):\/\/(?:.*@)?(.*.*\/(.*\/.*)))\?ref=(.*)$/;
Expand All @@ -68,7 +84,7 @@ export function extractPackageFile(content: string): PackageFile | null {
);

if (tfDepType === TerraformDependencyTypes.unknown) {
/* istanbul ignore next */ logger.warn(
/* istanbul ignore next */ logger.debug(
`Could not identify TerraformDependencyType ${terraformDependency.groups.type} on line ${lineNumber}.`
);
} else if (tfDepType === TerraformDependencyTypes.required_providers) {
Expand All @@ -85,10 +101,30 @@ export function extractPackageFile(content: string): PackageFile | null {
if (kvMatch) {
dep.currentValue = kvMatch.groups.value;
dep.managerData.moduleName = kvMatch.groups.key;
dep.managerData.versionLine = lineNumber;
deps.push(dep);
}
} while (line.trim() !== '}');
} else if (tfDepType === TerraformDependencyTypes.resource) {
const dep: PackageDependency = {
managerData: {
terraformDependencyType: tfDepType,
},
};
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);
} else {
const dep: PackageDependency = {
managerData: {
Expand Down Expand Up @@ -181,6 +217,22 @@ export function extractPackageFile(content: string): PackageFile | null {
if (!isValid(dep.currentValue)) {
dep.skipReason = SkipReason.UnsupportedVersion;
}
} else if (
dep.managerData.terraformDependencyType ===
TerraformDependencyTypes.resource
) {
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;
}
delete dep.managerData;
/* eslint-enable no-param-reassign */
Expand Down

0 comments on commit 98a8b96

Please sign in to comment.