terrafaker 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -51,7 +51,7 @@ $ npm install -g terrafaker
51
51
  $ terrafaker COMMAND
52
52
  running command...
53
53
  $ terrafaker (--version)
54
- terrafaker/0.0.5 darwin-arm64 node-v24.8.0
54
+ terrafaker/0.0.7 darwin-arm64 node-v24.8.0
55
55
  $ terrafaker --help [COMMAND]
56
56
  USAGE
57
57
  $ terrafaker COMMAND
@@ -74,21 +74,54 @@ Generates a terraform file.
74
74
 
75
75
  ```
76
76
  USAGE
77
- $ terrafaker generate file [--name <value>] [--provider aws|gcp|azure] [--resource-count <value>] [-f] [-q]
77
+ $ terrafaker generate file [--chaos-tags | --tags <value> | --no-tags] [-f] [--name <value>] [--provider
78
+ aws|gcp|azure] [-q] [--resource-count <value>]
78
79
 
79
80
  FLAGS
80
- -f, --[no-]format Format the output terraform files. Requires `terraform` to be in your $PATH.
81
- -q, --quiet Suppress the logging output.
82
- --name=<value> Name for the generated file, which must end in .tf
83
- --provider=<option> Cloud provider to generate resources for
84
- <options: aws|gcp|azure>
85
- --resource-count=<value> [default: 3] Number of resources per file to generate
81
+ -f, --[no-]format
82
+ Format the output terraform files. Requires `terraform` to be in your $PATH.
83
+
84
+ -q, --quiet
85
+ Suppress the logging output.
86
+
87
+ --chaos-tags
88
+ Generate random tag keys & values
89
+
90
+ --name=<value>
91
+ Name for the generated file, which must end in .tf
92
+
93
+ --no-tags
94
+ Disable any tag generation
95
+
96
+ --provider=<option>
97
+ Cloud provider to generate resources for
98
+ <options: aws|gcp|azure>
99
+
100
+ --resource-count=<value>
101
+ [default: 3] Number of resources per file to generate
102
+
103
+ --tags=<value>
104
+ [default: Environment:Dev,Service:service] Custom tags to use for generated resources. Should be a comma-separated
105
+ list of tag names to generate random values for, or tag names with values delimited by a colon.
106
+
107
+ Examples:
108
+
109
+ Specify just tag keys to have a random value generated.
110
+ --tags Service,Team → {"Service":"(random value)","Team":"(random value)"}
111
+
112
+ Specify value for a key with the : delimiter. This can be mixed with just keys that are randomly generated.
113
+ --tags Service:web-app,Team → {"Service":"web-app","Team":"(random value)"}
114
+ --tags Service:web-app,Team:core → {"Service":"web-app","Team":"core"}
115
+
116
+ When specifying a key or value that has a space in it, the entire tag string needs to be quoted.
117
+ --tags "Service:my awesome web app,Team Name:The Core Team" → {"Service":"my awesome web app","Team Name":"The Core
118
+ Team"}
86
119
 
87
120
  DESCRIPTION
88
121
  Generates a terraform file.
89
122
  ```
90
123
 
91
- _See code: [src/commands/generate/file.ts](https://github.com/brandongregoryscott/terrafaker/blob/v0.0.5/src/commands/generate/file.ts)_
124
+ _See code: [src/commands/generate/file.ts](https://github.com/brandongregoryscott/terrafaker/blob/v0.0.7/src/commands/generate/file.ts)_
92
125
 
93
126
  ## `terrafaker generate repo`
94
127
 
@@ -96,28 +129,70 @@ Generates repo(s) with multiple terraform files.
96
129
 
97
130
  ```
98
131
  USAGE
99
- $ terrafaker generate repo [--directory <value>] [--count <value>] [--file-count <value>] [--resource-count
100
- <value>] [--prefix <value>] [--provider aws|gcp|azure] [-f] [--create-remote] [--public] [-q]
132
+ $ terrafaker generate repo [--chaos-tags | --tags <value> | --no-tags] [--count <value>] [--create-remote]
133
+ [--directory <value>] [--file-count <value>] [-f] [--prefix <value>] [--provider aws|gcp|azure] [--public] [-q]
134
+ [--resource-count <value>]
101
135
 
102
136
  FLAGS
103
- -f, --[no-]format Format the output terraform files. Requires `terraform` to be in your $PATH.
104
- -q, --quiet Suppress the logging output.
105
- --count=<value> [default: 1] Number of repos to generate
106
- --create-remote Create and push a remote GitHub repo. Requires the `gh` CLI to be installed. To install,
107
- run `brew install gh`.
108
- --directory=<value> [default: .] Directory to generate the repo(s) in
109
- --file-count=<value> [default: 3] Number of files per repo to generate
110
- --prefix=<value> [default: tf_] Prefix for repo names, useful for quickly identifying generated content
111
- --provider=<option> Cloud provider to generate resources for
112
- <options: aws|gcp|azure>
113
- --public Whether the remote repo(s) created are public.
114
- --resource-count=<value> [default: 3] Number of resources per file to generate
137
+ -f, --[no-]format
138
+ Format the output terraform files. Requires `terraform` to be in your $PATH.
139
+
140
+ -q, --quiet
141
+ Suppress the logging output.
142
+
143
+ --chaos-tags
144
+ Generate random tag keys & values
145
+
146
+ --count=<value>
147
+ [default: 1] Number of repos to generate
148
+
149
+ --create-remote
150
+ Create and push a remote GitHub repo. Requires the `gh` CLI to be installed. To install, run `brew install gh`.
151
+
152
+ --directory=<value>
153
+ [default: .] Directory to generate the repo(s) in
154
+
155
+ --file-count=<value>
156
+ [default: 3] Number of files per repo to generate
157
+
158
+ --no-tags
159
+ Disable any tag generation
160
+
161
+ --prefix=<value>
162
+ [default: tf_] Prefix for repo names, useful for quickly identifying generated content
163
+
164
+ --provider=<option>
165
+ Cloud provider to generate resources for
166
+ <options: aws|gcp|azure>
167
+
168
+ --public
169
+ Whether the remote repo(s) created are public.
170
+
171
+ --resource-count=<value>
172
+ [default: 3] Number of resources per file to generate
173
+
174
+ --tags=<value>
175
+ [default: Environment:Dev,Service:service] Custom tags to use for generated resources. Should be a comma-separated
176
+ list of tag names to generate random values for, or tag names with values delimited by a colon.
177
+
178
+ Examples:
179
+
180
+ Specify just tag keys to have a random value generated.
181
+ --tags Service,Team → {"Service":"(random value)","Team":"(random value)"}
182
+
183
+ Specify value for a key with the : delimiter. This can be mixed with just keys that are randomly generated.
184
+ --tags Service:web-app,Team → {"Service":"web-app","Team":"(random value)"}
185
+ --tags Service:web-app,Team:core → {"Service":"web-app","Team":"core"}
186
+
187
+ When specifying a key or value that has a space in it, the entire tag string needs to be quoted.
188
+ --tags "Service:my awesome web app,Team Name:The Core Team" → {"Service":"my awesome web app","Team Name":"The Core
189
+ Team"}
115
190
 
116
191
  DESCRIPTION
117
192
  Generates repo(s) with multiple terraform files.
118
193
  ```
119
194
 
120
- _See code: [src/commands/generate/repo.ts](https://github.com/brandongregoryscott/terrafaker/blob/v0.0.5/src/commands/generate/repo.ts)_
195
+ _See code: [src/commands/generate/repo.ts](https://github.com/brandongregoryscott/terrafaker/blob/v0.0.7/src/commands/generate/repo.ts)_
121
196
 
122
197
  ## `terrafaker gh clone-repos`
123
198
 
@@ -136,7 +211,7 @@ DESCRIPTION
136
211
  `gh` CLI to be installed. To install, run `brew install gh`.
137
212
  ```
138
213
 
139
- _See code: [src/commands/gh/clone-repos.ts](https://github.com/brandongregoryscott/terrafaker/blob/v0.0.5/src/commands/gh/clone-repos.ts)_
214
+ _See code: [src/commands/gh/clone-repos.ts](https://github.com/brandongregoryscott/terrafaker/blob/v0.0.7/src/commands/gh/clone-repos.ts)_
140
215
 
141
216
  ## `terrafaker gh delete-repos`
142
217
 
@@ -156,7 +231,7 @@ DESCRIPTION
156
231
  If the deletion fails, you may need to refresh your CLI permissions with `gh auth refresh -s delete_repo`
157
232
  ```
158
233
 
159
- _See code: [src/commands/gh/delete-repos.ts](https://github.com/brandongregoryscott/terrafaker/blob/v0.0.5/src/commands/gh/delete-repos.ts)_
234
+ _See code: [src/commands/gh/delete-repos.ts](https://github.com/brandongregoryscott/terrafaker/blob/v0.0.7/src/commands/gh/delete-repos.ts)_
160
235
 
161
236
  ## `terrafaker help [COMMAND]`
162
237
 
@@ -1,23 +1,27 @@
1
1
  import { Flags } from "@oclif/core";
2
2
  import { BaseCommand } from "../../utilities/base-command.js";
3
- import { formatFlag, providerFlag, quietFlag, resourceCountFlag, } from "../../utilities/flags.js";
3
+ import { chaosTagsFlag, formatFlag, getTagsOption, noTagsFlag, providerFlag, quietFlag, resourceCountFlag, tagsFlag, } from "../../utilities/flags.js";
4
4
  import { formatTfFileName, success } from "../../utilities/string-utils.js";
5
5
  import { randomProvider } from "../../utilities/generators/generator-utils.js";
6
6
  import { FileGenerator } from "../../utilities/generators/file-generator.js";
7
7
  class File extends BaseCommand {
8
8
  static description = "Generates a terraform file.";
9
9
  static flags = {
10
+ "chaos-tags": chaosTagsFlag,
11
+ format: formatFlag,
10
12
  name: Flags.string({
11
13
  description: "Name for the generated file, which must end in .tf",
12
14
  }),
15
+ "no-tags": noTagsFlag,
13
16
  provider: providerFlag,
14
- "resource-count": resourceCountFlag,
15
- format: formatFlag,
16
17
  quiet: quietFlag,
18
+ "resource-count": resourceCountFlag,
19
+ tags: tagsFlag(),
17
20
  };
18
21
  async run() {
19
22
  const { flags } = await this.parse(File);
20
23
  const { name, quiet, format, "resource-count": resourceCount } = flags;
24
+ const tags = getTagsOption(flags);
21
25
  const provider = flags.provider ?? randomProvider();
22
26
  const fileName = formatTfFileName(name ?? "main.tf");
23
27
  FileGenerator.generate({
@@ -25,6 +29,7 @@ class File extends BaseCommand {
25
29
  provider,
26
30
  resourceCount,
27
31
  format,
32
+ tags,
28
33
  });
29
34
  if (!quiet) {
30
35
  this.log(success(`Successfully generated '${fileName}'`));
@@ -1,7 +1,7 @@
1
1
  import { Flags } from "@oclif/core";
2
2
  import path from "node:path";
3
3
  import { BaseCommand } from "../../utilities/base-command.js";
4
- import { formatFlag, providerFlag, quietFlag, resourceCountFlag, } from "../../utilities/flags.js";
4
+ import { chaosTagsFlag, formatFlag, getTagsOption, noTagsFlag, providerFlag, quietFlag, resourceCountFlag, tagsFlag, } from "../../utilities/flags.js";
5
5
  import { $ } from "zx";
6
6
  import { HelpMessages } from "../../enums/help-messages.js";
7
7
  import { RepoGenerator } from "../../utilities/generators/repo-generator.js";
@@ -9,37 +9,41 @@ import { success } from "../../utilities/string-utils.js";
9
9
  class Repo extends BaseCommand {
10
10
  static description = "Generates repo(s) with multiple terraform files.";
11
11
  static flags = {
12
- directory: Flags.string({
13
- description: "Directory to generate the repo(s) in",
14
- default: ".",
15
- }),
12
+ "chaos-tags": chaosTagsFlag,
16
13
  count: Flags.integer({
17
14
  description: "Number of repos to generate",
18
15
  default: 1,
19
16
  }),
17
+ "create-remote": Flags.boolean({
18
+ description: `Create and push a remote GitHub repo. ${HelpMessages.RequiresGhCli}`,
19
+ }),
20
+ directory: Flags.string({
21
+ description: "Directory to generate the repo(s) in",
22
+ default: ".",
23
+ }),
20
24
  "file-count": Flags.integer({
21
25
  description: "Number of files per repo to generate",
22
26
  default: 3,
23
27
  }),
24
- "resource-count": resourceCountFlag,
28
+ format: formatFlag,
29
+ "no-tags": noTagsFlag,
25
30
  prefix: Flags.string({
26
31
  description: "Prefix for repo names, useful for quickly identifying generated content",
27
32
  default: "tf_",
28
33
  }),
29
34
  provider: providerFlag,
30
- format: formatFlag,
31
- "create-remote": Flags.boolean({
32
- description: `Create and push a remote GitHub repo. ${HelpMessages.RequiresGhCli}`,
33
- }),
34
35
  public: Flags.boolean({
35
36
  description: "Whether the remote repo(s) created are public.",
36
37
  default: false,
37
38
  }),
38
39
  quiet: quietFlag,
40
+ "resource-count": resourceCountFlag,
41
+ tags: tagsFlag(),
39
42
  };
40
43
  async run() {
41
44
  const { flags } = await this.parse(Repo);
42
45
  const { format, prefix, count, public: isPublic, quiet, "resource-count": resourceCount, "file-count": fileCount, "create-remote": createRemote, } = flags;
46
+ const tags = getTagsOption(flags);
43
47
  const provider = flags.provider;
44
48
  const directory = path.resolve(process.cwd(), flags.directory);
45
49
  for (let i = 0; i < count; i++) {
@@ -51,6 +55,7 @@ class Repo extends BaseCommand {
51
55
  provider,
52
56
  resourceCount,
53
57
  quiet,
58
+ tags,
54
59
  });
55
60
  if (createRemote) {
56
61
  await $ `gh repo create ${name} --source ${path} ${isPublic ? "--public" : "--private"} --push`;
@@ -1,7 +1,7 @@
1
1
  import { Args } from "@oclif/core";
2
2
  import { compact, first } from "lodash-es";
3
3
  import { BaseCommand } from "../../utilities/base-command.js";
4
- import { stringifySingleLineArray } from "../../utilities/string-utils.js";
4
+ import { stringifySingleLineArray } from "../../utilities/collection-utils.js";
5
5
  const EXAMPLE_INSTANCE_TYPES = ["m5.large", "m5.xlarge", "m5.2xlarge"];
6
6
  class FormatPsv extends BaseCommand {
7
7
  static hidden = true;
@@ -1,6 +1,6 @@
1
1
  import { Args, Flags } from "@oclif/core";
2
2
  import { BaseCommand } from "../../utilities/base-command.js";
3
- import { stringifySingleLineArray } from "../../utilities/string-utils.js";
3
+ import { stringifySingleLineArray } from "../../utilities/collection-utils.js";
4
4
  import { compact } from "lodash-es";
5
5
  const EXAMPLE = `c4d-standard-2 2 7 No Up to 10 N/A`;
6
6
  class FormatTsv extends BaseCommand {
@@ -1,10 +1,10 @@
1
- import { upperFirst } from "lodash-es";
1
+ import { mapUpperFirstVariants } from "../utilities/collection-utils.js";
2
2
  const SHORT_ENVIRONMENT_TAGS = ["dev", "stage", "prod"];
3
3
  const LONG_ENVIRONMENT_TAGS = ["development", "staging", "production"];
4
- const ENVIRONMENT_TAGS = [
4
+ const ENVIRONMENT_TAGS = mapUpperFirstVariants([
5
5
  ...SHORT_ENVIRONMENT_TAGS,
6
6
  ...LONG_ENVIRONMENT_TAGS,
7
- ].flatMap((tag) => [tag, upperFirst(tag)]);
7
+ ]);
8
8
  const SERVICE_TAGS = [
9
9
  "web-app",
10
10
  "api",
@@ -14,4 +14,18 @@ const SERVICE_TAGS = [
14
14
  "cache",
15
15
  "database",
16
16
  ];
17
- export { ENVIRONMENT_TAGS, LONG_ENVIRONMENT_TAGS, SERVICE_TAGS, SHORT_ENVIRONMENT_TAGS, };
17
+ const DEFAULT_TAGS = {
18
+ Environment: "Dev",
19
+ Service: "service",
20
+ };
21
+ const TAG_KEYS = mapUpperFirstVariants([
22
+ "environment",
23
+ "env",
24
+ "service",
25
+ "name",
26
+ "team",
27
+ "business",
28
+ "department",
29
+ "dept",
30
+ ]);
31
+ export { DEFAULT_TAGS, ENVIRONMENT_TAGS, LONG_ENVIRONMENT_TAGS, SERVICE_TAGS, SHORT_ENVIRONMENT_TAGS, TAG_KEYS, };
@@ -0,0 +1,4 @@
1
+ import { upperFirst } from "lodash-es";
2
+ const stringifySingleLineArray = (values) => `[${values.map((value) => `"${value}"`).join(", ")}]`;
3
+ const mapUpperFirstVariants = (values) => values.flatMap((value) => [value, upperFirst(value)]);
4
+ export { mapUpperFirstVariants, stringifySingleLineArray };
@@ -1,5 +1,7 @@
1
1
  import { Flags } from "@oclif/core";
2
2
  import { Providers } from "../enums/providers.js";
3
+ import { DEFAULT_TAGS } from "../constants/tags.js";
4
+ import { parseTagString, stringifyTags } from "./tag-utils.js";
3
5
  const formatFlag = Flags.boolean({
4
6
  char: "f",
5
7
  description: "Format the output terraform files. Requires `terraform` to be in your $PATH.",
@@ -18,4 +20,53 @@ const providerFlag = Flags.string({
18
20
  description: "Cloud provider to generate resources for",
19
21
  options: Object.values(Providers),
20
22
  });
21
- export { formatFlag, providerFlag, quietFlag, resourceCountFlag };
23
+ const TAGS_WITH_VALUES = {
24
+ Service: "web-app",
25
+ Team: "core",
26
+ };
27
+ const TAGS_WITH_SPACES = {
28
+ Service: "my awesome web app",
29
+ "Team Name": "The Core Team",
30
+ };
31
+ const tagsFlag = Flags.custom({
32
+ description: `[default: ${stringifyTags(DEFAULT_TAGS)}] Custom tags to use for generated resources. Should be a comma-separated list of tag names to generate random values for, or tag names with values delimited by a colon.
33
+
34
+ Examples:
35
+
36
+ Specify just tag keys to have a random value generated.
37
+ --tags Service,Team → ${JSON.stringify({ Service: "(random value)", Team: "(random value)" })}
38
+
39
+ Specify value for a key with the : delimiter. This can be mixed with just keys that are randomly generated.
40
+ --tags Service:web-app,Team → ${JSON.stringify({ Service: "web-app", Team: "(random value)" })}
41
+ --tags ${stringifyTags(TAGS_WITH_VALUES)} → ${JSON.stringify(TAGS_WITH_VALUES)}
42
+
43
+ When specifying a key or value that has a space in it, the entire tag string needs to be quoted.
44
+ --tags "${stringifyTags(TAGS_WITH_SPACES)}" → ${JSON.stringify(TAGS_WITH_SPACES)}
45
+ `,
46
+ exclusive: ["chaos-tags", "no-tags"],
47
+ parse: async (input) => parseTagString(input),
48
+ });
49
+ const noTagsFlag = Flags.boolean({
50
+ description: "Disable any tag generation",
51
+ exclusive: ["tags", "chaos-tags"],
52
+ });
53
+ const chaosTagsFlag = Flags.boolean({
54
+ description: "Generate random tag keys & values",
55
+ exclusive: ["tags", "no-tags"],
56
+ });
57
+ /**
58
+ * Utility for returning the underlying `tags` option for the `ProviderGenerator` based on various
59
+ * flag configurations.
60
+ */
61
+ const getTagsOption = (flags) => {
62
+ const { "no-tags": noTags, "chaos-tags": chaosTags } = flags;
63
+ let tags = flags.tags ?? DEFAULT_TAGS;
64
+ if (chaosTags) {
65
+ tags = "chaos";
66
+ }
67
+ if (noTags) {
68
+ tags = undefined;
69
+ }
70
+ return tags;
71
+ };
72
+ export { chaosTagsFlag, formatFlag, getTagsOption, noTagsFlag, providerFlag, quietFlag, resourceCountFlag, tagsFlag, };
@@ -1,5 +1,5 @@
1
1
  import { AWS_EBS_VOLUME_TYPES, AWS_INSTANCE_TYPES, AWS_LAMBDA_RUNTIMES, AWS_REGIONS, } from "../../constants/aws.js";
2
- import { maybe, randomEnvironmentTag, randomId, randomItem, randomMemorableSlug, randomMemorySize, randomServiceTag, unique, } from "./generator-utils.js";
2
+ import { maybe, randomId, randomItem, randomMemorableSlug, randomMemorySize, unique, } from "./generator-utils.js";
3
3
  import { ProviderGenerator } from "./provider-generator.js";
4
4
  const AwsResourceType = {
5
5
  ComputeInstance: "aws_instance",
@@ -11,8 +11,6 @@ class AwsGenerator extends ProviderGenerator {
11
11
  }
12
12
  addComputeInstance() {
13
13
  const name = randomMemorableSlug();
14
- const environment = randomEnvironmentTag();
15
- const service = randomServiceTag();
16
14
  const ami = this.randomAmi();
17
15
  const instanceType = randomItem(AWS_INSTANCE_TYPES);
18
16
  const rootBlockDevice = maybe(0.5)
@@ -31,14 +29,12 @@ class AwsGenerator extends ProviderGenerator {
31
29
  ami,
32
30
  instance_type: instanceType,
33
31
  ...rootBlockDevice,
34
- tags: { name, environment, service },
32
+ ...this.getTagsBlock(),
35
33
  });
36
34
  return this;
37
35
  }
38
36
  addLambdaFunction() {
39
37
  const name = randomMemorableSlug();
40
- const environment = randomEnvironmentTag();
41
- const service = randomServiceTag();
42
38
  // https://docs.aws.amazon.com/lambda/latest/dg/configuration-memory.html
43
39
  const memorySize = randomMemorySize({
44
40
  min: 128,
@@ -57,7 +53,7 @@ class AwsGenerator extends ProviderGenerator {
57
53
  function_name: functionName,
58
54
  memory_size: memorySize,
59
55
  role,
60
- tags: { name, environment, service },
56
+ ...this.getTagsBlock(),
61
57
  });
62
58
  return this;
63
59
  }
@@ -1,5 +1,5 @@
1
1
  import { AZURE_INSTANCE_TYPES, AZURE_LAMBDA_RUNTIMES, AZURE_REGIONS, } from "../../constants/azure.js";
2
- import { randomEnvironmentTag, randomItem, randomMemorableSlug, randomMemorySize, randomServiceTag, } from "./generator-utils.js";
2
+ import { randomItem, randomMemorableSlug, randomMemorySize, } from "./generator-utils.js";
3
3
  import { ProviderGenerator } from "./provider-generator.js";
4
4
  const AzureResourceType = {
5
5
  ComputeInstance: "azurerm_linux_virtual_machine",
@@ -11,19 +11,15 @@ class AzureGenerator extends ProviderGenerator {
11
11
  }
12
12
  addComputeInstance() {
13
13
  const name = randomMemorableSlug();
14
- const environment = randomEnvironmentTag();
15
- const service = randomServiceTag();
16
14
  const instanceType = randomItem(AZURE_INSTANCE_TYPES);
17
15
  this.tfg.resource(AzureResourceType.ComputeInstance, name, {
18
16
  size: instanceType,
19
- tags: { name, environment, service },
17
+ ...this.getTagsBlock(),
20
18
  });
21
19
  return this;
22
20
  }
23
21
  addLambdaFunction() {
24
22
  const name = randomMemorableSlug();
25
- const environment = randomEnvironmentTag();
26
- const service = randomServiceTag();
27
23
  const runtime = randomItem(AZURE_LAMBDA_RUNTIMES);
28
24
  // There's a lot of different configurations listed, so just guessing here.
29
25
  // https://learn.microsoft.com/en-us/azure/azure-functions/functions-scale
@@ -36,7 +32,7 @@ class AzureGenerator extends ProviderGenerator {
36
32
  ...runtime,
37
33
  name,
38
34
  instance_memory_in_mb: instanceMemoryInMb,
39
- labels: { name, environment, service },
35
+ ...this.getTagsBlock(),
40
36
  });
41
37
  return this;
42
38
  }
@@ -2,8 +2,8 @@ import { randomProvider } from "./generator-utils.js";
2
2
  import { ProviderGeneratorFactory } from "./provider-generator-factory.js";
3
3
  class FileGenerator {
4
4
  static generate(options) {
5
- const { provider = randomProvider(), resourceCount = 3, fileName, directory, format, } = options;
6
- const generator = ProviderGeneratorFactory.get(provider);
5
+ const { provider = randomProvider(), resourceCount = 3, fileName, directory, format, tags, } = options;
6
+ const generator = ProviderGeneratorFactory.get(provider, { tags });
7
7
  for (let i = 0; i < resourceCount; i++) {
8
8
  generator.addRandomResource();
9
9
  }
@@ -1,18 +1,20 @@
1
1
  import { GCP_GPU_MACHINE_TYPES, GCP_INSTANCE_TYPES, GCP_LAMBDA_RUNTIMES, GCP_REGIONS, } from "../../constants/gcp.js";
2
- import { maybe, randomEnvironmentTag, randomInt, randomItem, randomMemorableSlug, randomMemorySize, randomServiceTag, } from "./generator-utils.js";
2
+ import { maybe, randomInt, randomItem, randomMemorableSlug, randomMemorySize, } from "./generator-utils.js";
3
3
  import { ProviderGenerator } from "./provider-generator.js";
4
4
  const GcpResourceType = {
5
5
  ComputeInstance: "google_compute_instance",
6
6
  LambdaFunction: "google_cloudfunctions_function",
7
7
  };
8
8
  class GcpGenerator extends ProviderGenerator {
9
+ constructor(options) {
10
+ super(options);
11
+ this.tagKey = "labels";
12
+ }
9
13
  addProvider() {
10
14
  this.tfg.provider("google", { region: this.region });
11
15
  }
12
16
  addComputeInstance() {
13
17
  const name = randomMemorableSlug();
14
- const environment = randomEnvironmentTag();
15
- const service = randomServiceTag();
16
18
  const machineType = randomItem(GCP_INSTANCE_TYPES);
17
19
  const guestAccelerator = maybe(0.5)
18
20
  ? {
@@ -26,14 +28,12 @@ class GcpGenerator extends ProviderGenerator {
26
28
  zone: this.region,
27
29
  machine_type: machineType,
28
30
  ...guestAccelerator,
29
- labels: { name, environment, service },
31
+ ...this.getTagsBlock(),
30
32
  });
31
33
  return this;
32
34
  }
33
35
  addLambdaFunction() {
34
36
  const name = randomMemorableSlug();
35
- const environment = randomEnvironmentTag();
36
- const service = randomServiceTag();
37
37
  const runtime = randomItem(GCP_LAMBDA_RUNTIMES);
38
38
  // @see https://docs.cloud.google.com/run/docs/configuring/services/memory-limits
39
39
  const availableMemoryMb = randomMemorySize({
@@ -45,7 +45,7 @@ class GcpGenerator extends ProviderGenerator {
45
45
  runtime,
46
46
  name,
47
47
  available_memory_mb: availableMemoryMb,
48
- labels: { name, environment, service },
48
+ ...this.getTagsBlock(),
49
49
  });
50
50
  return this;
51
51
  }
@@ -1,6 +1,6 @@
1
1
  import { faker } from "@faker-js/faker";
2
2
  import { snakeSlugify } from "../string-utils.js";
3
- import { ENVIRONMENT_TAGS, SERVICE_TAGS } from "../../constants/tags.js";
3
+ import { ENVIRONMENT_TAGS, SERVICE_TAGS, TAG_KEYS, } from "../../constants/tags.js";
4
4
  import { Providers } from "../../enums/providers.js";
5
5
  import { range } from "lodash-es";
6
6
  const MAX_UNIQUE_GENERATOR_ATTEMPTS = 50;
@@ -25,10 +25,19 @@ function unique(generator) {
25
25
  }
26
26
  const randomMemorableSlug = unique(() => snakeSlugify(`${faker.word.adjective()}_${faker.color.human()}_${faker.animal.type()}`));
27
27
  const randomItem = (values) => faker.helpers.arrayElement(values);
28
+ const randomItems = (values, count) => faker.helpers.arrayElements(values, count);
28
29
  const randomProvider = () => randomItem(Object.values(Providers));
29
30
  const randomId = unique(() => faker.internet.mac({ separator: "" }));
30
31
  const randomEnvironmentTag = () => randomItem(ENVIRONMENT_TAGS);
31
32
  const randomServiceTag = () => randomItem(SERVICE_TAGS);
33
+ const randomTags = (additionalTags) => {
34
+ const keys = randomItems(TAG_KEYS, { min: 1, max: 4 });
35
+ const tags = keys.reduce((accumulated, key) => {
36
+ accumulated[key] = randomMemorableSlug();
37
+ return accumulated;
38
+ }, {});
39
+ return { ...tags, ...additionalTags };
40
+ };
32
41
  const randomMemorySize = (options) => {
33
42
  const { min, max, step } = options;
34
43
  const values = range(min, max, step);
@@ -44,4 +53,4 @@ const randomInt = (options) => {
44
53
  * Returns true/false depending on the provided probability (0 to 1)
45
54
  */
46
55
  const maybe = (probability) => Math.random() < probability;
47
- export { maybe, randomEnvironmentTag, randomId, randomInt, randomItem, randomMemorableSlug, randomMemorySize, randomProvider, randomServiceTag, unique, };
56
+ export { maybe, randomEnvironmentTag, randomId, randomInt, randomItem, randomItems, randomMemorableSlug, randomMemorySize, randomProvider, randomServiceTag, randomTags, unique, };
@@ -3,15 +3,15 @@ import { AwsGenerator } from "./aws-generator.js";
3
3
  import { AzureGenerator } from "./azure-generator.js";
4
4
  import { GcpGenerator } from "./gcp-generator.js";
5
5
  class ProviderGeneratorFactory {
6
- static get(provider) {
6
+ static get(provider, options) {
7
7
  switch (provider) {
8
8
  case Providers.Azure:
9
- return new AzureGenerator();
9
+ return new AzureGenerator(options);
10
10
  case Providers.GCP:
11
- return new GcpGenerator();
11
+ return new GcpGenerator(options);
12
12
  default:
13
13
  case Providers.AWS:
14
- return new AwsGenerator();
14
+ return new AwsGenerator(options);
15
15
  }
16
16
  }
17
17
  }
@@ -1,14 +1,22 @@
1
- import { TerraformGenerator } from "terraform-generator";
1
+ import { map, TerraformGenerator } from "terraform-generator";
2
2
  import { ResourceTypes } from "../../enums/resource-types.js";
3
- import { randomItem, randomMemorableSlug } from "./generator-utils.js";
3
+ import { randomItem, randomMemorableSlug, randomTags, } from "./generator-utils.js";
4
4
  import { formatTfFileName } from "../string-utils.js";
5
5
  class ProviderGenerator {
6
6
  tfg;
7
7
  region;
8
- constructor() {
8
+ tags;
9
+ /**
10
+ * Provider-specific key for `tags` objects.
11
+ * @default tags
12
+ */
13
+ tagKey;
14
+ constructor(options) {
9
15
  this.tfg = new TerraformGenerator();
10
16
  this.region = this.randomRegion();
11
17
  this.addProvider();
18
+ this.tags = options?.tags;
19
+ this.tagKey = "tags";
12
20
  }
13
21
  addResourceByType(type) {
14
22
  switch (type) {
@@ -24,6 +32,24 @@ class ProviderGenerator {
24
32
  const type = randomItem(Object.values(ResourceTypes));
25
33
  return this.addResourceByType(type);
26
34
  }
35
+ getTags() {
36
+ if (this.tags === "chaos") {
37
+ return randomTags();
38
+ }
39
+ return this.tags;
40
+ }
41
+ /**
42
+ * Constructs the tag object to spread onto a resource, where the key is provider specific
43
+ * (usually `tags` or `labels`) and the value is a `Map` constructed from the object returned from
44
+ * `getTags`.
45
+ */
46
+ getTagsBlock() {
47
+ const tags = this.getTags();
48
+ if (tags === undefined) {
49
+ return undefined;
50
+ }
51
+ return { [this.tagKey]: map(tags) };
52
+ }
27
53
  toString() {
28
54
  const { tf } = this.tfg.generate();
29
55
  return tf.trim();
@@ -6,7 +6,7 @@ import { mkdir } from "node:fs/promises";
6
6
  import { success } from "../string-utils.js";
7
7
  class RepoGenerator {
8
8
  static async generate(options) {
9
- const { provider, prefix = "tf_", format, fileCount = 3, resourceCount, quiet, } = options;
9
+ const { provider, prefix = "tf_", format, fileCount = 3, resourceCount, quiet, tags, } = options;
10
10
  const directory = path.resolve(process.cwd(), options.directory ?? ".");
11
11
  const repoName = `${prefix}${randomMemorableSlug()}`;
12
12
  const repoPath = path.join(directory, repoName);
@@ -19,6 +19,7 @@ class RepoGenerator {
19
19
  provider,
20
20
  resourceCount,
21
21
  format,
22
+ tags,
22
23
  });
23
24
  }
24
25
  await sh `git add . && git commit -m "initial commit"`;
@@ -3,6 +3,6 @@ import { ux } from "@oclif/core";
3
3
  const slugify = (value) => faker.helpers.slugify(value).toLowerCase();
4
4
  const snakeSlugify = (value) => slugify(value).replaceAll("-", "_");
5
5
  const success = (message) => `${ux.colorize("green", "✓")} ${message}`;
6
- const stringifySingleLineArray = (values) => `[${values.map((value) => `"${value}"`).join(", ")}]`;
6
+ const warn = (message) => `${ux.colorize("yellow", "⚠")} ${message}`;
7
7
  const formatTfFileName = (fileName) => fileName.endsWith(".tf") ? fileName : `${fileName}.tf`;
8
- export { formatTfFileName, slugify, snakeSlugify, stringifySingleLineArray, success, };
8
+ export { formatTfFileName, slugify, snakeSlugify, success, warn };
@@ -0,0 +1,18 @@
1
+ import { randomMemorableSlug } from "./generators/generator-utils.js";
2
+ import { warn } from "./string-utils.js";
3
+ const parseTagString = (tagString) => {
4
+ const csvTags = tagString.split(",").map((value) => value.trim());
5
+ const tags = csvTags.reduce((accumulated, csvTag) => {
6
+ const [tag, value] = csvTag.split(":");
7
+ if (tag in accumulated) {
8
+ console.warn(warn(`tag '${tag}' specified more than once, earlier value will be overwritten.`));
9
+ }
10
+ accumulated[tag] = value ?? randomMemorableSlug();
11
+ return accumulated;
12
+ }, {});
13
+ return tags;
14
+ };
15
+ const stringifyTags = (tags) => Object.entries(tags)
16
+ .map(([key, value]) => `${key}:${value}`)
17
+ .join(",");
18
+ export { parseTagString, stringifyTags };
@@ -5,6 +5,23 @@
5
5
  "args": {},
6
6
  "description": "Generates a terraform file.",
7
7
  "flags": {
8
+ "chaos-tags": {
9
+ "description": "Generate random tag keys & values",
10
+ "exclusive": [
11
+ "tags",
12
+ "no-tags"
13
+ ],
14
+ "name": "chaos-tags",
15
+ "allowNo": false,
16
+ "type": "boolean"
17
+ },
18
+ "format": {
19
+ "char": "f",
20
+ "description": "Format the output terraform files. Requires `terraform` to be in your $PATH.",
21
+ "name": "format",
22
+ "allowNo": true,
23
+ "type": "boolean"
24
+ },
8
25
  "name": {
9
26
  "description": "Name for the generated file, which must end in .tf",
10
27
  "name": "name",
@@ -12,6 +29,16 @@
12
29
  "multiple": false,
13
30
  "type": "option"
14
31
  },
32
+ "no-tags": {
33
+ "description": "Disable any tag generation",
34
+ "exclusive": [
35
+ "tags",
36
+ "chaos-tags"
37
+ ],
38
+ "name": "no-tags",
39
+ "allowNo": false,
40
+ "type": "boolean"
41
+ },
15
42
  "provider": {
16
43
  "description": "Cloud provider to generate resources for",
17
44
  "name": "provider",
@@ -24,6 +51,13 @@
24
51
  ],
25
52
  "type": "option"
26
53
  },
54
+ "quiet": {
55
+ "char": "q",
56
+ "description": "Suppress the logging output.",
57
+ "name": "quiet",
58
+ "allowNo": false,
59
+ "type": "boolean"
60
+ },
27
61
  "resource-count": {
28
62
  "description": "Number of resources per file to generate",
29
63
  "name": "resource-count",
@@ -32,19 +66,16 @@
32
66
  "multiple": false,
33
67
  "type": "option"
34
68
  },
35
- "format": {
36
- "char": "f",
37
- "description": "Format the output terraform files. Requires `terraform` to be in your $PATH.",
38
- "name": "format",
39
- "allowNo": true,
40
- "type": "boolean"
41
- },
42
- "quiet": {
43
- "char": "q",
44
- "description": "Suppress the logging output.",
45
- "name": "quiet",
46
- "allowNo": false,
47
- "type": "boolean"
69
+ "tags": {
70
+ "description": "[default: Environment:Dev,Service:service] Custom tags to use for generated resources. Should be a comma-separated list of tag names to generate random values for, or tag names with values delimited by a colon.\n\nExamples:\n\nSpecify just tag keys to have a random value generated.\n --tags Service,Team → {\"Service\":\"(random value)\",\"Team\":\"(random value)\"}\n\nSpecify value for a key with the : delimiter. This can be mixed with just keys that are randomly generated.\n --tags Service:web-app,Team → {\"Service\":\"web-app\",\"Team\":\"(random value)\"}\n --tags Service:web-app,Team:core → {\"Service\":\"web-app\",\"Team\":\"core\"}\n\nWhen specifying a key or value that has a space in it, the entire tag string needs to be quoted.\n --tags \"Service:my awesome web app,Team Name:The Core Team\" → {\"Service\":\"my awesome web app\",\"Team Name\":\"The Core Team\"}\n",
71
+ "exclusive": [
72
+ "chaos-tags",
73
+ "no-tags"
74
+ ],
75
+ "name": "tags",
76
+ "hasDynamicHelp": false,
77
+ "multiple": false,
78
+ "type": "option"
48
79
  }
49
80
  },
50
81
  "hasDynamicHelp": false,
@@ -88,13 +119,15 @@
88
119
  "args": {},
89
120
  "description": "Generates repo(s) with multiple terraform files.",
90
121
  "flags": {
91
- "directory": {
92
- "description": "Directory to generate the repo(s) in",
93
- "name": "directory",
94
- "default": ".",
95
- "hasDynamicHelp": false,
96
- "multiple": false,
97
- "type": "option"
122
+ "chaos-tags": {
123
+ "description": "Generate random tag keys & values",
124
+ "exclusive": [
125
+ "tags",
126
+ "no-tags"
127
+ ],
128
+ "name": "chaos-tags",
129
+ "allowNo": false,
130
+ "type": "boolean"
98
131
  },
99
132
  "count": {
100
133
  "description": "Number of repos to generate",
@@ -104,22 +137,45 @@
104
137
  "multiple": false,
105
138
  "type": "option"
106
139
  },
107
- "file-count": {
108
- "description": "Number of files per repo to generate",
109
- "name": "file-count",
110
- "default": 3,
140
+ "create-remote": {
141
+ "description": "Create and push a remote GitHub repo. Requires the `gh` CLI to be installed. To install, run `brew install gh`.",
142
+ "name": "create-remote",
143
+ "allowNo": false,
144
+ "type": "boolean"
145
+ },
146
+ "directory": {
147
+ "description": "Directory to generate the repo(s) in",
148
+ "name": "directory",
149
+ "default": ".",
111
150
  "hasDynamicHelp": false,
112
151
  "multiple": false,
113
152
  "type": "option"
114
153
  },
115
- "resource-count": {
116
- "description": "Number of resources per file to generate",
117
- "name": "resource-count",
154
+ "file-count": {
155
+ "description": "Number of files per repo to generate",
156
+ "name": "file-count",
118
157
  "default": 3,
119
158
  "hasDynamicHelp": false,
120
159
  "multiple": false,
121
160
  "type": "option"
122
161
  },
162
+ "format": {
163
+ "char": "f",
164
+ "description": "Format the output terraform files. Requires `terraform` to be in your $PATH.",
165
+ "name": "format",
166
+ "allowNo": true,
167
+ "type": "boolean"
168
+ },
169
+ "no-tags": {
170
+ "description": "Disable any tag generation",
171
+ "exclusive": [
172
+ "tags",
173
+ "chaos-tags"
174
+ ],
175
+ "name": "no-tags",
176
+ "allowNo": false,
177
+ "type": "boolean"
178
+ },
123
179
  "prefix": {
124
180
  "description": "Prefix for repo names, useful for quickly identifying generated content",
125
181
  "name": "prefix",
@@ -140,19 +196,6 @@
140
196
  ],
141
197
  "type": "option"
142
198
  },
143
- "format": {
144
- "char": "f",
145
- "description": "Format the output terraform files. Requires `terraform` to be in your $PATH.",
146
- "name": "format",
147
- "allowNo": true,
148
- "type": "boolean"
149
- },
150
- "create-remote": {
151
- "description": "Create and push a remote GitHub repo. Requires the `gh` CLI to be installed. To install, run `brew install gh`.",
152
- "name": "create-remote",
153
- "allowNo": false,
154
- "type": "boolean"
155
- },
156
199
  "public": {
157
200
  "description": "Whether the remote repo(s) created are public.",
158
201
  "name": "public",
@@ -165,6 +208,25 @@
165
208
  "name": "quiet",
166
209
  "allowNo": false,
167
210
  "type": "boolean"
211
+ },
212
+ "resource-count": {
213
+ "description": "Number of resources per file to generate",
214
+ "name": "resource-count",
215
+ "default": 3,
216
+ "hasDynamicHelp": false,
217
+ "multiple": false,
218
+ "type": "option"
219
+ },
220
+ "tags": {
221
+ "description": "[default: Environment:Dev,Service:service] Custom tags to use for generated resources. Should be a comma-separated list of tag names to generate random values for, or tag names with values delimited by a colon.\n\nExamples:\n\nSpecify just tag keys to have a random value generated.\n --tags Service,Team → {\"Service\":\"(random value)\",\"Team\":\"(random value)\"}\n\nSpecify value for a key with the : delimiter. This can be mixed with just keys that are randomly generated.\n --tags Service:web-app,Team → {\"Service\":\"web-app\",\"Team\":\"(random value)\"}\n --tags Service:web-app,Team:core → {\"Service\":\"web-app\",\"Team\":\"core\"}\n\nWhen specifying a key or value that has a space in it, the entire tag string needs to be quoted.\n --tags \"Service:my awesome web app,Team Name:The Core Team\" → {\"Service\":\"my awesome web app\",\"Team Name\":\"The Core Team\"}\n",
222
+ "exclusive": [
223
+ "chaos-tags",
224
+ "no-tags"
225
+ ],
226
+ "name": "tags",
227
+ "hasDynamicHelp": false,
228
+ "multiple": false,
229
+ "type": "option"
168
230
  }
169
231
  },
170
232
  "hasDynamicHelp": false,
@@ -356,5 +418,5 @@
356
418
  ]
357
419
  }
358
420
  },
359
- "version": "0.0.5"
421
+ "version": "0.0.7"
360
422
  }
package/package.json CHANGED
@@ -77,5 +77,5 @@
77
77
  },
78
78
  "type": "module",
79
79
  "types": "dist/index.d.ts",
80
- "version": "0.0.5"
80
+ "version": "0.0.7"
81
81
  }