Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 41 additions & 7 deletions backend/src/controllers/terraform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,40 @@ import updateHead from "../githubapi/updateHead";
import {getModeNumber} from "../githubapi/util";
import {NamedAwsBackend} from "../terraform/awsBackend";
import {AwsProvider} from "../terraform/awsProvider";
import {DynamoDb} from "../terraform/DynamoDb";
import {Ec2} from "../terraform/ec2";
import {Gce} from "../terraform/gce";
import {GlacierVault} from "../terraform/glacierVault";
import {NamedGoogleBackend} from "../terraform/googleBackend";
import {GoogleProvider} from "../terraform/googleProvider";
import {lambdaFunction} from "../terraform/lambdaFunction";
import {prefabNetworkFromArr, splitForPrefab} from "../terraform/prefab";
import {S3} from "../terraform/s3";
import {rootBlockSplitBackend} from "../terraform/terraform";
import {internalErrorHandler} from "../types/errorHandler";
import {TerraformResource} from "../types/terraform";
import {jsonToHcl} from "../util";

export const createTerraformSettings = (req: Request, res: Response): void => {
const provider = req.body.settings?.provider as "aws" | "google" | "azure";

const secure = req.body.settings.secure ?? false;
const allowSsh = req.body.settings.allowSsh ?? false;
const allowEgressWeb = req.body.settings.allowEgressWeb ?? false;
const allowIngressWeb = req.body.settings.allowIngressWeb ?? false;

//Only needed for google
const project =
provider === "google" ? (req.body.settings?.project as string) : "";

const resourcesRaw = req.body.settings?.resources as (TerraformResource & {
type: "ec2" | "gce" | "s3" | "glacierVault" | "lambdaFunction";
type:
| "ec2"
| "gce"
| "s3"
| "glacierVault"
| "lambdaFunction"
| "dynamoDb";
})[];
const repo = req.body.repo as string;
const token = req.headers?.token as string;
Expand All @@ -49,6 +63,13 @@ export const createTerraformSettings = (req: Request, res: Response): void => {
} else if (resource.type === "glacierVault") {
const glacierVault: GlacierVault = resource as GlacierVault;
return new GlacierVault(glacierVault.id, glacierVault.autoIam);
} else if (resource.type === "dynamoDb") {
const dynamoDb: DynamoDb = resource as DynamoDb;
return new DynamoDb(
dynamoDb.id,
dynamoDb.attributes,
dynamoDb.autoIam
);
} else if (resource.type === "lambdaFunction") {
const lambdaFunc: lambdaFunction = resource as lambdaFunction;
return new lambdaFunction(
Expand All @@ -67,12 +88,25 @@ export const createTerraformSettings = (req: Request, res: Response): void => {
return;
}

const [gce, lambda, networkedResources] = splitForPrefab(resources);

const network =
networkedResources.length > 0 && provider === "aws"
? prefabNetworkFromArr(networkedResources, {
allEgress: !secure,
allIngress: !secure,
ssh: secure && allowSsh,
webEgress: secure && allowEgressWeb,
webIngress: secure && allowIngressWeb
})
: networkedResources;

const [root, backend] = rootBlockSplitBackend(
provider === "aws" ? new AwsProvider() : new GoogleProvider(project),
provider === "aws"
? new NamedAwsBackend()
: new NamedGoogleBackend(project),
resources
[...gce, ...lambda, ...network]
);

getHead(token, repo, "main")
Expand All @@ -81,7 +115,7 @@ export const createTerraformSettings = (req: Request, res: Response): void => {
const blobRoot = await postBlob(
token,
repo,
JSON.stringify(root, null, 2) + "\n"
jsonToHcl(root) + "\n"
);

/*
Expand All @@ -90,14 +124,14 @@ export const createTerraformSettings = (req: Request, res: Response): void => {
const blobBackend = await postBlob(
token,
repo,
JSON.stringify(backend, null, 2) + "\n"
jsonToHcl(backend) + "\n"
);
*/

// Create a new branch to post our commit to
const branchName = "DevXP-Configuration";
const newBranch = await createBranch(
`refs/heads/${branchName}`,
branchName,
token,
repo,
head.sha
Expand All @@ -115,7 +149,7 @@ export const createTerraformSettings = (req: Request, res: Response): void => {
//Create a new tree within that one
const newTree = await createTree(token, repo, tree.sha, [
{
path: "terraform.tf.json",
path: "terraform.tf",
mode: getModeNumber("blob"),
type: "blob",
sha: blobRoot.sha,
Expand All @@ -125,7 +159,7 @@ export const createTerraformSettings = (req: Request, res: Response): void => {
/*
Removed for M1 presentation. We'll solve the chicken and egg for milestone 2
{
path: "backend.tf.json",
path: "backend.tf",
mode: getModeNumber("blob"),
type: "blob",
sha: blobBackend.sha,
Expand Down
19 changes: 16 additions & 3 deletions backend/src/githubapi/createBranch.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
/* eslint-disable prettier/prettier */
import axios from "axios";
import {GithubBranch, isGithubBranch} from "../types/github";
import getHead from "./getHead";
import {GITHUB_BASE_URL, createGithubHeader} from "./util";

export default (
ref: string,
branchName: string,
token: string,
repo: string,
treeSha: string
): Promise<GithubBranch> =>
new Promise<GithubBranch>((resolve, reject) => {
let errCache: any;

axios
.post(
`${GITHUB_BASE_URL}/repos/${repo}/git/refs`,
{
sha: treeSha,
ref: ref
ref: `refs/heads/${branchName}`
},
createGithubHeader(token)
)
Expand All @@ -31,5 +34,15 @@ export default (
reject(new Error("Invalid response from github"));
}
})
.catch(reject);
.catch(err => {
errCache = err;
//Maybe the branch exists so we should try to retrieve it
return getHead(token, repo, branchName);

//TODO: Refactor this
})
.then(head => resolve(head as GithubBranch))
.catch(() => {
reject(errCache);
});
});
41 changes: 38 additions & 3 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,44 @@ server.route("/", mainRouter);

// import {testToFileAws} from "./util";
// import {Ec2} from "./terraform/ec2";
// testToFileAws("/home/brennan/aws_test/devxp.tf.json", [
// new Ec2("AUTO_UBUNTU", "t2.medium", "myinstance", true)
// ]);
// import {prefabNetwork} from "./terraform/prefab";
// import {S3} from "./terraform/s3";
// import {GlacierVault} from "./terraform/glacierVault";
// import {DynamoDb} from "./terraform/DynamoDb";

// testToFileAws(
// "/home/brennan/aws_test/devxp.tf",
// prefabNetwork(
// {
// ec2: [new Ec2("AUTO_UBUNTU", "t2.micro", "instance_a", true)],
// s3: [
// new S3(
// "devxp_test_bucket_a",
// false,
// false,
// "devxp-test-bucket-a"
// )
// ],
// glacier: new GlacierVault(
// "devxp_test_vault",
// false,
// "devxp-test-vault"
// ),
// dynamo: new DynamoDb("devxp_test_dynamo_db", [
// {
// name: "field1",
// type: "S",
// isHash: true
// }
// ])
// },
// {
// ssh: true,
// webEgress: true,
// webIngress: true
// }
// )
// );

mongoose.connection.on(
"error",
Expand Down
23 changes: 23 additions & 0 deletions backend/src/terraform/AwsIamInstanceProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {jsonRoot} from "./util";
import {Resource} from "./resource";

export interface AwsIamInstanceProfile {
role: string;
}
export class AwsIamInstanceProfile
extends Resource<AwsIamInstanceProfile>
implements AwsIamInstanceProfile
{
constructor(id: string, role: string, name?: string) {
super(id, "AwsIamInstanceProfile", false, name);
this.role = role;
}

//Returns an array of resource blocks
toJSON() {
return jsonRoot("aws_iam_instance_profile", this.id, {
name: this.name,
role: `\${aws_iam_role.${this.role}.name}`
});
}
}
22 changes: 22 additions & 0 deletions backend/src/terraform/AwsInternetGateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {jsonRoot} from "./util";
import {Resource} from "./resource";

export interface AwsInternetGateway {
vpc: string;
}
export class AwsInternetGateway
extends Resource<AwsInternetGateway>
implements AwsInternetGateway
{
constructor(id: string, vpc: string, name?: string) {
super(id, "AwsInternetGateway", false, name);
this.vpc = vpc;
}

//Returns a resource block
toJSON() {
return jsonRoot("aws_internet_gateway", this.id, {
vpc_id: `\${aws_vpc.${this.vpc}.id}`
});
}
}
30 changes: 30 additions & 0 deletions backend/src/terraform/AwsRoute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {jsonRoot} from "./util";
import {Resource} from "./resource";

export interface AwsRoute {
route_table_id: string;
cidr_block: string;
gateway_id: string;
}
export class AwsRoute extends Resource<AwsRoute> implements AwsRoute {
constructor(
id: string,
route_table_id: string,
cidr_block: string,
gateway_id: string
) {
super(id, "AwsRoute", false);
this.route_table_id = route_table_id;
this.cidr_block = cidr_block;
this.gateway_id = gateway_id;
}

//Returns a resource block
toJSON() {
return jsonRoot("aws_route", this.id, {
route_table_id: `\${aws_route_table.${this.route_table_id}.id}`,
destination_cidr_block: this.cidr_block,
gateway_id: `\${aws_internet_gateway.${this.gateway_id}.id}`
});
}
}
47 changes: 47 additions & 0 deletions backend/src/terraform/AwsRouteTable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {jsonRoot} from "./util";
import {Resource} from "./resource";
import {AwsRoute} from "../types/terraform";

export interface AwsRouteTable {
vpc: string;
routes: AwsRoute[];
defaultTable: boolean;
}
export class AwsRouteTable
extends Resource<AwsRouteTable>
implements AwsRouteTable
{
constructor(
id: string,
vpc: string,
routes: AwsRoute[],
defaultTable = false,
name?: string
) {
super(id, "AwsRouteTable", false, name);
this.vpc = vpc;
this.routes = routes;
this.defaultTable = defaultTable;
}

//Returns a resource block
toJSON() {
const resource = this.defaultTable
? "aws_default_route_table"
: "aws_route_table";

const json: Record<string, any> = {};

if (this.routes.length > 0) {
json.route = this.routes;
}

if (this.defaultTable) {
json.default_route_table_id = `\${aws_vpc.${this.vpc}.default_route_table_id}`;
} else {
json.vpc_id = `\${aws_vpc.${this.vpc}.id}`;
}

return jsonRoot(resource, this.id, json);
}
}
42 changes: 42 additions & 0 deletions backend/src/terraform/AwsSecurityGroup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {jsonRoot} from "./util";
import {Resource} from "./resource";
import {Firewall} from "../types/terraform";

const removeType = (f: any) => {
delete f.type;
return f;
};

export interface AwsSecurityGroup {
vpc: string;
firewalls: Firewall[];
}
export class AwsSecurityGroup
extends Resource<AwsSecurityGroup>
implements AwsSecurityGroup
{
constructor(
id: string,
vpc: string,
firewalls: Firewall[] = [],
name?: string
) {
super(id, "AwsSecurityGroup", false, name);
this.vpc = vpc;
this.firewalls = firewalls;
}

//Returns a resource block
toJSON() {
return jsonRoot("aws_security_group", this.id, {
vpc_id: `\${aws_vpc.${this.vpc}.id}`,
name: this.name,
ingress: this.firewalls
.filter(f => f.type === "ingress")
.map(removeType),
egress: this.firewalls
.filter(f => f.type === "egress")
.map(removeType)
});
}
}
Loading