Skip to content

Commit

Permalink
feat(rules): detect Terraform resource name
Browse files Browse the repository at this point in the history
  • Loading branch information
bodinsamuel committed Jun 29, 2023
1 parent 9ef7d9e commit 58327d6
Show file tree
Hide file tree
Showing 13 changed files with 166 additions and 23 deletions.
2 changes: 2 additions & 0 deletions src/common/techs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export const list: TechItem[] = [
{ key: 'flyio', name: 'Fly.io', type: 'hosting' },
{ key: 'gce', name: 'GCE', type: 'hosting' },
{ key: 'gcp', name: 'GCP', type: 'hosting' },
{ key: 'gcp.cloudrun', name: 'Cloud Run', type: 'hosting' },
{ key: 'gcp.pubsub', name: 'PubSub', type: 'messaging' },
{ key: 'gitlabci', name: 'Gitlab CI', type: 'ci' },
{ key: 'github', name: 'Github', type: 'tool' },
{ key: 'githubactions', name: 'Github Actions', type: 'ci' },
Expand Down
2 changes: 2 additions & 0 deletions src/provider/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ export const IGNORED_DIVE_PATHS = [
// needed to detect github actions
// '.github',
'.gitlab',
'.gradle',
'.log',
'.metadata',
'.npm',
'.nuxt',
'.react-email',
Expand Down
5 changes: 4 additions & 1 deletion src/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const dependencies: Record<
npm: [],
docker: [],
terraform: [],
'terraform.resource': [],
};

export const rawList: Array<
Expand Down Expand Up @@ -74,7 +75,9 @@ export function register(rule: Rule) {
}

if (rule.detect) {
rulesComponents.push(rule.detect);
rulesComponents.push(
...(Array.isArray(rule.detect) ? rule.detect : [rule.detect])
);
}
}

Expand Down
12 changes: 12 additions & 0 deletions src/rules/hosting/gcp.cloudrun.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { register } from '../../rules.js';

register({
tech: 'gcp.cloudrun',
dependencies: [
{ type: 'npm', name: '@google-cloud/run' },
{
type: 'terraform.resource',
name: 'google_cloud_run_v2_service',
},
],
});
1 change: 1 addition & 0 deletions src/rules/hosting/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import './elasticstack.js';
import './expodev.js';
import './flyio.js';
import './gcp.js';
import './gcp.cloudrun.js';
import './githubpages.js';
import './heroku.js';
import './kubernetes.js';
Expand Down
12 changes: 12 additions & 0 deletions src/rules/messaging/gcp.pubsub.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { register } from '../../rules.js';

register({
tech: 'gcp.pubsub',
dependencies: [
{ type: 'npm', name: '@google-cloud/pubsub' },
{
type: 'terraform.resource',
name: 'google_pubsub_topic',
},
],
});
1 change: 1 addition & 0 deletions src/rules/messaging/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
import './rabbitmq.js';
import './gcp.pubsub.js';
69 changes: 53 additions & 16 deletions src/rules/spec/terraform/dependencies.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@ import { flatten } from '../../../payload/helpers.js';
import { FakeProvider } from '../../../provider/fake.js';
import { rawList } from '../../../rules.js';

const lockfile: string[] = [``];

for (const item of rawList) {
if (item.type !== 'dependency' || item.ref.type !== 'terraform') {
continue;
}

const example = 'example' in item.ref ? item.ref.example : item.ref.name;
lockfile.push(`
provider "${example}" {
version = "0.0.0"
}
`);
}

describe('terraform', () => {
describe('terraform (lockfile)', () => {
it('should match everything', async () => {
const lockfile: string[] = [``];

for (const item of rawList) {
if (item.type !== 'dependency' || item.ref.type !== 'terraform') {
continue;
}

const example = 'example' in item.ref ? item.ref.example : item.ref.name;
lockfile.push(`
provider "${example}" {
version = "0.0.0"
}
`);
}

const res = await analyser({
provider: new FakeProvider({
paths: {
Expand Down Expand Up @@ -57,3 +57,40 @@ describe('terraform', () => {
);
});
});

describe('terraform (resource)', () => {
it('should match everything', async () => {
const resource: string[] = [``];

for (const item of rawList) {
if (
item.type !== 'dependency' ||
item.ref.type !== 'terraform.resource'
) {
continue;
}

const example = 'example' in item.ref ? item.ref.example : item.ref.name;
resource.push(`
resource "${example}" "foobar" {
name = "hello"
}
`);
}

const res = await analyser({
provider: new FakeProvider({
paths: {
'/': ['main.tf'],
},
files: {
'/main.tf': resource.join(''),
},
}),
});

expect(
Array.from(flatten(res, { merge: true }).techs).sort()
).toStrictEqual(['gcp.cloudrun', 'gcp.pubsub', 'terraform']);
});
});
5 changes: 3 additions & 2 deletions src/rules/spec/terraform/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { register } from '../../../rules.js';

import { detectTerraformComponent } from './component.js';
import { detectTerraformLockfile } from './lockfile.js';
import { detectTerraformResource } from './resource.js';

register({
tech: 'terraform',
files: ['.terraform', '.terraform.lock.hcl', 'main.tf', 'variables.tf'],
dependencies: [],
detect: detectTerraformComponent,
detect: [detectTerraformLockfile, detectTerraformResource],
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { ComponentMatcher } from '../../../types/rule.js';

const LOCKFILE = '.terraform.lock.hcl';

export const detectTerraformComponent: ComponentMatcher = async (
export const detectTerraformLockfile: ComponentMatcher = async (
files,
provider
) => {
Expand All @@ -35,7 +35,6 @@ export const detectTerraformComponent: ComponentMatcher = async (
return false;
}

// const techs = detect(Object.keys(json.provider), 'terraform');
const pl = new Payload({
name: 'virtual',
folderPath: path.dirname(file.fp),
Expand Down
67 changes: 67 additions & 0 deletions src/rules/spec/terraform/resource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import path from 'node:path';

import { parse } from '@cdktf/hcl2json';

import { listIndexed } from '../../../common/techs.js';
import { Payload } from '../../../payload/index.js';
import { detect } from '../../../rules.js';
import type { ComponentMatcher } from '../../../types/rule.js';

const FILE = /.tf$/;

export const detectTerraformResource: ComponentMatcher = async (
files,
provider
) => {
for (const file of files) {
if (!FILE.test(file.name)) {
continue;
}

const content = await provider.open(file.fp);
if (!content) {
continue;
}

let json: Record<string, any>;
try {
json = await parse(file.fp, content);
} catch (err) /* istanbul ignore next */ {
console.warn('Failed to parse HCL', err);
return false;
}

if (!('resource' in json)) {
return false;
}

const pl = new Payload({
name: 'virtual',
folderPath: path.dirname(file.fp),
});

// We only register docker service with image and that we know
for (const name of Object.keys(json.resource)) {
const matched = [...detect([name], 'terraform.resource')];
if (!matched.length) {
continue;
}

const tech = matched[0];

pl.addChild(
new Payload({
name: listIndexed[tech].name,
folderPath: file.fp,
tech,
parent: pl,
dependencies: [['terraform.resource', name, 'unknown']],
})
);
}

return pl;
}

return false;
};
8 changes: 6 additions & 2 deletions src/types/rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import type { BaseProvider, ProviderFile } from '../provider/base.js';

import type { AllowedKeys, TechItem } from './techs.js';

export type SupportedDeps = 'docker' | 'npm' | 'terraform';
export type SupportedDeps =
| 'docker'
| 'npm'
| 'terraform.resource'
| 'terraform';

export type RuleDependency = {
type: SupportedDeps;
Expand All @@ -17,7 +21,7 @@ export type RuleDependency = {
export type Rule = {
tech: AllowedKeys;
dependencies?: RuleDependency[];
detect?: ComponentMatcher;
detect?: ComponentMatcher | ComponentMatcher[];
} & (RuleFiles | never);
export type RuleFiles =
| {
Expand Down
2 changes: 2 additions & 0 deletions src/types/techs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export type AllowedKeys =
| 'firebase'
| 'flyio'
| 'gce'
| 'gcp.cloudrun'
| 'gcp.pubsub'
| 'gcp'
| 'github'
| 'githubactions'
Expand Down

0 comments on commit 58327d6

Please sign in to comment.