Skip to content
This repository was archived by the owner on Apr 13, 2020. It is now read-only.

Commit 01b6cb7

Browse files
authored
Add support for backend config in spk infra scaffold (#106)
* Updated documentation, added support for backend in scaffold, cleaned up unit/mock testing
1 parent 96bf94e commit 01b6cb7

File tree

6 files changed

+77
-318
lines changed

6 files changed

+77
-318
lines changed

docs/cloud-infra-management.md

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ spk infra scaffold|s [options]
7676
> `spk infra scaffold --name discovery-service --source https://github.com/microsoft/bedrock --version "0.0.1" --template /microsoft/bedrock/cluster/environments/azure-simple`
7777
7878
Options:
79-
--hcl Generate HCL definition file (terragrunt.hcl)
8079
-n, --name <name> Cluster name for scaffolding
8180
-s, --source <cluster definition github repo> Source URL for the repository containing the terraform deployment
8281
-v, --version <repository version> Version or tag for the repository so a fixed version is referenced
@@ -130,36 +129,3 @@ definition.json
130129
}
131130
}
132131
```
133-
134-
terrgrunt.hcl
135-
136-
```
137-
inputs = {
138-
agent_vm_count = "3"
139-
agent_vm_size = "Standard_D2s_v3"
140-
acr_enabled = "true"
141-
gc_enabled = "true"
142-
cluster_name = ""
143-
dns_prefix = ""
144-
flux_recreate = ""
145-
kubeconfig_recreate = ""
146-
gitops_ssh_url = ""
147-
gitops_ssh_key = ""
148-
gitops_path = ""
149-
gitops_url_branch = "master"
150-
resource_group_name = ""
151-
ssh_public_key = ""
152-
service_principal_id = ""
153-
service_principal_secret = ""
154-
gitops_poll_interval = "5m"
155-
vnet_name = ""
156-
service_cidr = "10.0.0.0/16"
157-
dns_ip = "10.0.0.10"
158-
docker_cidr = "172.17.0.1/16"
159-
address_space = "10.10.0.0/16"
160-
subnet_prefix = "10.10.1.0/24"
161-
network_plugin = "azure"
162-
network_policy = "azure"
163-
oms_agent_enabled = "false"
164-
}
165-
```

src/commands/infra/scaffold.test.ts

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ import {
33
enableVerboseLogging,
44
logger
55
} from "../../logger";
6-
import {
7-
generateClusterDefinition,
8-
parseVariablesTf,
9-
scaffoldHcl
10-
} from "./scaffold";
6+
import { generateClusterDefinition, parseVariablesTf } from "./scaffold";
117

128
beforeAll(() => {
139
enableVerboseLogging();
@@ -56,32 +52,21 @@ describe("Validate generation of sample scaffold definition", () => {
5652
' type = "string"\n' +
5753
' default = "5m"\n' +
5854
"} \n";
59-
const def = generateClusterDefinition(
55+
const backendTfVars =
56+
'storage_account_name="<storage account name>"\n' +
57+
'access_key="<storage access key>"\n' +
58+
'container_name="<storage account container>"\n' +
59+
'key="tfstate-azure-simple"\n';
60+
const def = await generateClusterDefinition(
6061
"test-scaffold",
6162
"https://github.com/microsoft/bedrock",
6263
"cluster/environments/azure-simple",
6364
"v1.0.0",
65+
backendTfVars,
6466
sampleVarTf
6567
);
6668
expect(def.name).toBe("test-scaffold");
6769
expect(def.variables.resource_group_name).toBe("<insert value>");
68-
});
69-
});
70-
71-
describe("Validate generation of a valid cluster HCL file", () => {
72-
test("Validate that a variables.tf sample can be parsed and translated to an HCL file", async () => {
73-
const mockFileName = "src/commands/mocks/azure-simple";
74-
const sampleVarTf = "src/commands/mocks/azure-simple/variables.tf";
75-
const value = await scaffoldHcl(mockFileName, sampleVarTf);
76-
expect(value).toBe(true);
77-
});
78-
});
79-
80-
describe("Failure testing for generation of a valid cluster HCL file", () => {
81-
test("Mocked a failed scenario of HCL generation", async () => {
82-
const mockFileName = "src/commands/mocks/azure-simple";
83-
const sampleVarTf = "src/commands/mocks/azure-simple";
84-
const value = await scaffoldHcl(mockFileName, sampleVarTf);
85-
expect(value).toBe(false);
70+
expect(def.backend.key).toBe("tfstate-azure-simple");
8671
});
8772
});

src/commands/infra/scaffold.ts

Lines changed: 68 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ export const scaffoldCommandDecorator = (command: commander.Command): void => {
2727
"-t, --template <path to variables.tf> ",
2828
"Location of the variables.tf for the terraform deployment"
2929
)
30-
.option("--hcl", "Generates cluster definition HCL file")
3130
.action(async opts => {
3231
try {
3332
if (opts.name && opts.source && opts.version && opts.template) {
@@ -39,21 +38,8 @@ export const scaffoldCommandDecorator = (command: commander.Command): void => {
3938
}
4039
await copyTfTemplate(opts.template, opts.name);
4140
await validateVariablesTf(path.join(opts.template, "variables.tf"));
41+
await scaffold(opts.name, opts.source, opts.version, opts.template);
4242
await renameTfvars(opts.name);
43-
if (opts.hcl) {
44-
logger.info("Generating HCL cluster definition file.");
45-
await scaffoldHcl(
46-
opts.name,
47-
path.join(opts.template, "variables.tf")
48-
);
49-
} else {
50-
await scaffoldJson(
51-
opts.name,
52-
opts.source,
53-
opts.version,
54-
opts.template
55-
);
56-
}
5743
} catch (err) {
5844
logger.error("Error occurred while generating scaffold");
5945
logger.error(err);
@@ -64,7 +50,7 @@ export const scaffoldCommandDecorator = (command: commander.Command): void => {
6450
/**
6551
* Checks if working variables.tf is present
6652
*
67-
* @param templatePath Path the variables.tf file
53+
* @param templatePath Path to the variables.tf file
6854
*/
6955
export const validateVariablesTf = async (
7056
templatePath: string
@@ -77,7 +63,7 @@ export const validateVariablesTf = async (
7763
return false;
7864
}
7965
logger.info(
80-
`Terraform variables.tf file found. Attempting to generate definition JSON/HCL file...`
66+
`Terraform variables.tf file found. Attempting to generate definition.json file.`
8167
);
8268
} catch (_) {
8369
logger.error(`Unable to validate Terraform variables.tf.`);
@@ -87,15 +73,32 @@ export const validateVariablesTf = async (
8773
};
8874

8975
/**
90-
* Rename any .tfvars file by appending ".backup"
76+
* Checks if backend.tfvars is present
77+
*
78+
* @param dir Path to the backend.tfvars file
79+
*/
80+
export const validateBackendTfvars = async (name: string): Promise<boolean> => {
81+
const backendConfig = path.join(name, "backend.tfvars");
82+
logger.info(backendConfig);
83+
if (fs.existsSync(backendConfig)) {
84+
logger.info(`A remote backend configuration was found : ${backendConfig}`);
85+
return true;
86+
} else {
87+
logger.info(`No remote backend configuration was found.`);
88+
return false;
89+
}
90+
};
91+
92+
/**
93+
* Renames any .tfvars file by appending ".backup"
9194
*
9295
* @param dir path to template directory
9396
*/
9497
export const renameTfvars = async (dir: string): Promise<void> => {
9598
try {
9699
const tfFiles = fs.readdirSync(dir);
97100
tfFiles.forEach(file => {
98-
if (file.indexOf(".tfvars") !== -1) {
101+
if (file.substr(file.lastIndexOf(".") + 1) === "tfvars") {
99102
fs.renameSync(path.join(dir, file), path.join(dir, file + ".backup"));
100103
}
101104
});
@@ -157,7 +160,7 @@ export const parseVariablesTf = (data: string) => {
157160
const fields: { [name: string]: string | "" } = {};
158161
const fieldSplitRegex = /\"\s{0,}\{/;
159162
const defaultRegex = /default\s{0,}=\s{0,}(.*)/;
160-
blocks.forEach((b, idx) => {
163+
blocks.forEach(b => {
161164
b = b.trim();
162165
const elt = b.split(fieldSplitRegex);
163166
elt[0] = elt[0].trim().replace('"', "");
@@ -180,11 +183,39 @@ export const parseVariablesTf = (data: string) => {
180183
return fields;
181184
};
182185

183-
export const generateClusterDefinition = (
186+
/**
187+
* Parses and reformats the backend.tfvars
188+
*
189+
* @param backendTfvarData path to the directory of backend.tfvars
190+
*/
191+
export const parseBackendTfvars = (backendData: string) => {
192+
const backend: { [name: string]: string | "" } = {};
193+
const block = backendData.replace(/\=/g, ":").split("\n");
194+
block.forEach(b => {
195+
const elt = b.split(":");
196+
if (elt[0].length > 0) {
197+
backend[elt[0]] = elt[1].replace(/\"/g, "");
198+
}
199+
});
200+
return backend;
201+
};
202+
203+
/**
204+
* Generates cluster definition as definition.json
205+
*
206+
* @param name name of destination directory
207+
* @param source git url of source repo
208+
* @param template name of Terraform environment
209+
* @param version a tag/branch/release of source repo
210+
* @param backendTfvars path to directory that contains backend.tfvars
211+
* @param vartfData path to the variables.tf file
212+
*/
213+
export const generateClusterDefinition = async (
184214
name: string,
185215
source: string,
186216
template: string,
187217
version: string,
218+
backendData: string,
188219
vartfData: string
189220
) => {
190221
const fields: { [name: string]: string | null } = parseVariablesTf(vartfData);
@@ -194,6 +225,10 @@ export const generateClusterDefinition = (
194225
template,
195226
version
196227
};
228+
if (backendData !== "") {
229+
const backend = parseBackendTfvars(backendData);
230+
def.backend = backend;
231+
}
197232
if (Object.keys(fields).length > 0) {
198233
const fieldDict: { [name: string]: string | null } = {};
199234
Object.keys(fields).forEach(key => {
@@ -213,26 +248,33 @@ export const generateClusterDefinition = (
213248
* @param bedrockVersion The version of the repo used
214249
* @param tfVariableFile Path to the variable file to parse
215250
*/
216-
export const scaffoldJson = async (
251+
export const scaffold = async (
217252
name: string,
218253
bedrockSource: string,
219254
bedrockVersion: string,
220255
template: string
221256
): Promise<boolean> => {
222257
try {
223258
const tfVariableFile = path.join(name, "variables.tf");
259+
const backendTfvarsFile = path.join(name, "backend.tfvars");
260+
const backendBool = await validateBackendTfvars(name);
261+
let backendData = "";
262+
if (backendBool === true) {
263+
backendData = fs.readFileSync(backendTfvarsFile, "utf8");
264+
}
224265
// Identify which environment the user selected
225266
if (fs.existsSync(tfVariableFile)) {
226-
logger.info(`variables.tf file found : ${tfVariableFile}`);
267+
logger.info(`A variables.tf file found : ${tfVariableFile}`);
227268
const data: string = fs.readFileSync(tfVariableFile, "utf8");
228269
if (data) {
229270
const baseDef: {
230271
[name: string]: string | null | any;
231-
} = generateClusterDefinition(
272+
} = await generateClusterDefinition(
232273
name,
233274
bedrockSource,
234275
template,
235276
bedrockVersion,
277+
backendData,
236278
data
237279
);
238280
if (baseDef) {
@@ -252,51 +294,9 @@ export const scaffoldJson = async (
252294
logger.error(`Unable to read variable file: ${tfVariableFile}.`);
253295
}
254296
}
255-
} catch (_) {
256-
logger.warn("Unable to create scaffold");
257-
}
258-
return false;
259-
};
260-
261-
export const generateHclClusterDefinition = (vartfData: string) => {
262-
const data: string = fs.readFileSync(vartfData, "utf8");
263-
const fields: { [name: string]: string | "" | any } = parseVariablesTf(data);
264-
const def: { [name: string]: string | "" | any } = {};
265-
def.inputs = fields;
266-
return def;
267-
};
268-
269-
/**
270-
* This function creates a primary base Terragrunt HCL definition for
271-
* generating cluster definitions from.
272-
*
273-
* @param name Name of the cluster definition
274-
* @param vartfData Path to the variable.tf file to parse
275-
*/
276-
export const scaffoldHcl = async (
277-
dirName: string,
278-
vartfData: string
279-
): Promise<boolean> => {
280-
try {
281-
const def = generateHclClusterDefinition(vartfData);
282-
const confPath: string = path.format({
283-
base: "terragrunt.hcl",
284-
dir: dirName,
285-
root: "/ignored"
286-
});
287-
const hcl = JSON.stringify(def, null, 2)
288-
.replace(/\"([^(\")"]+)\":/g, "$1:")
289-
.replace(new RegExp(":", "g"), " =")
290-
.replace(new RegExp(",", "g"), " ")
291-
.replace("{", "")
292-
.replace(/\}([^}]*)$/, "$1")
293-
.replace(/(^[ \t]*\n)/gm, "")
294-
.trim();
295-
fs.writeFileSync(confPath, hcl);
296297
} catch (err) {
297-
logger.error("Failed to create HCL file.");
298+
logger.warn("Unable to create scaffold");
298299
logger.error(err);
299-
return false;
300300
}
301-
return true;
301+
return false;
302302
};

src/commands/mocks/azure-simple/main.tf

Lines changed: 0 additions & 50 deletions
This file was deleted.

0 commit comments

Comments
 (0)