@hiiretail/gcp-infra-cli 0.71.0 → 0.72.0

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.
@@ -5,7 +5,8 @@ locals {
5
5
  project_id = "<%-clanProject%>"
6
6
  project_env = "<%-env%>"
7
7
 
8
- project = local.project_id
9
- network = "tribe-network"
10
- tribe_project_id = "<%-tribeProject%>"
8
+ project = local.project_id
9
+ network = "tribe-network"
10
+ tribe_project_id = "<%-tribeProject%>"
11
+ monitoring_project_id = "<%-tribeProject%>" # possibly will be changed to hiiretail-monitoring-prod-6500 later
11
12
  }
@@ -1 +1 @@
1
- 1.0.7
1
+ 1.2.7
@@ -1 +1 @@
1
- 0.31.8
1
+ 0.38.0
@@ -123,7 +123,7 @@ module.exports = class extends BaseGenerator {
123
123
  this.log(`
124
124
  ${chalk.green('Your clan projects have now been created. To finalize your configuration, please continue with manual editing of the generated files.')}
125
125
  ${chalk.green('1.')} Add clan members and groups
126
- \u2192 ${chalk.cyan(path.join(clanDir, 'project.yaml'))}
126
+ \u2192 ${chalk.cyan(path.join(clanDir, 'clan.yaml'))}
127
127
  ${chalk.green('2.')} Configure APIs, service accounts and repositories
128
128
  \u2192 ${chalk.cyan(path.join(clanDir, 'prod', 'project.yaml'))}
129
129
  \u2192 ${chalk.cyan(path.join(clanDir, 'staging', 'project.yaml'))}
@@ -8,8 +8,10 @@
8
8
  # members:
9
9
  # groups: []
10
10
  # users:
11
- # - alice@extendaretail.com
12
- # - bob@extendaretail.com
11
+ # - name: Alice Test
12
+ # email: alice@extendaretail.com
13
+ # - name: Bob Test
14
+ # email: bob@extendaretail.com
13
15
  ###
14
16
  ---
15
17
  common-infra-repo: <%-commonInfraRepo%>
@@ -1,5 +1,5 @@
1
1
  terraform {
2
- source = "git::https://github.com/extenda/tf-module-gcp-project//?ref=v1.0.6"
2
+ source = "git::https://github.com/extenda/tf-module-gcp-project//?ref=v1.0.8"
3
3
  }
4
4
 
5
5
  dependency "parent_folder" {
@@ -0,0 +1,94 @@
1
+ const fs = require('fs');
2
+ const yaml = require('js-yaml');
3
+
4
+ const appendIncludeConfigSlo = async (fileContent, originalContentYaml, slosFilePath, inputs) => {
5
+ if (fileContent !== null && fileContent !== '') {
6
+ const configArray = Object.values(originalContentYaml);
7
+ const yamlPullArray = yaml.dump(configArray);
8
+ fs.writeFileSync(slosFilePath, `${yamlPullArray}`);
9
+ }
10
+
11
+ const newPullArray = [];
12
+
13
+ if (inputs.sli === 'availability') {
14
+ newPullArray.push(
15
+ {
16
+ display_name: 'Month - Availability',
17
+ slo_id: 'month-availability',
18
+ goal: 0.998,
19
+ calendar_period: 'MONTH',
20
+ type: 'windows_based_sli',
21
+ method: 'boolean_filter',
22
+ window_period: '60s',
23
+ },
24
+ );
25
+ }
26
+ if (inputs.sli === 'error-rate') {
27
+ newPullArray.push(
28
+ {
29
+ display_name: 'Month - Error rate',
30
+ slo_id: 'month-error-rate',
31
+ goal: 0.999,
32
+ calendar_period: 'MONTH',
33
+ type: 'request_based_sli',
34
+ method: 'good_total_ratio',
35
+ bad_service_filter:
36
+ `metric.type="knative.dev/serving/revision/request_count"
37
+ resource.type="knative_revision"
38
+ metric.labels.response_code_class="5xx"
39
+ resource.labels.service_name="${inputs.serviceName}"`,
40
+ total_service_filter:
41
+ `metric.type="knative.dev/serving/revision/request_count"
42
+ resource.type="knative_revision"
43
+ resource.labels.service_name=${inputs.serviceName}"`,
44
+ },
45
+ );
46
+ }
47
+ if (inputs.sli === 'latency') {
48
+ newPullArray.push(
49
+ {
50
+ display_name: 'Month - Latency',
51
+ slo_id: 'month-latency',
52
+ goal: 0.95,
53
+ calendar_period: 'MONTH',
54
+ type: 'request_based_sli',
55
+ method: 'distribution_cut',
56
+ metric_filter:
57
+ `metric.type="knative.dev/serving/revision/request_latencies"
58
+ resource.type="knative_revision"
59
+ resource.labels.service_name="${inputs.serviceName}"`,
60
+ range_min: 0,
61
+ range_max: 100,
62
+ },
63
+ );
64
+ }
65
+
66
+ const finalYamlPullArray = yaml.dump(newPullArray);
67
+ fs.appendFileSync(slosFilePath, finalYamlPullArray);
68
+ };
69
+
70
+ const appendIncludeConfigUptime = async (fileContent, uptimeContentYml, uptimeFilePath, inputs) => {
71
+ if (fileContent !== null && fileContent !== '') {
72
+ const configArray = Object.values(uptimeContentYml);
73
+ const yamlPullArray = yaml.dump(configArray);
74
+ fs.writeFileSync(uptimeFilePath, `${yamlPullArray}`);
75
+ }
76
+
77
+ const newPullArray = [];
78
+
79
+ newPullArray.push(
80
+ {
81
+ service_name: `${inputs.systemName}.${inputs.serviceName}`,
82
+ hostname: inputs.hostname,
83
+ path: inputs.path,
84
+ },
85
+ );
86
+
87
+ const finalYamlPullArray = yaml.dump(newPullArray);
88
+ fs.appendFileSync(uptimeFilePath, finalYamlPullArray);
89
+ };
90
+
91
+ module.exports = {
92
+ appendIncludeConfigSlo,
93
+ appendIncludeConfigUptime,
94
+ };
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "name": "Monitoring",
3
- "description": "Create monitoring dashboards and alers"
3
+ "description": "Create monitoring resources"
4
4
  }
@@ -0,0 +1,26 @@
1
+ const fs = require('fs');
2
+ const yaml = require('js-yaml');
3
+ const { appendIncludeConfigSlo } = require('./append');
4
+
5
+ const handleSlosFile = async (answers, slosFilePath) => {
6
+ const {
7
+ serviceName,
8
+ sli,
9
+ systemName,
10
+ } = answers;
11
+
12
+ const sloFileContent = fs.readFileSync(slosFilePath, 'utf8');
13
+
14
+ const inputs = {
15
+ ...this.answers,
16
+ serviceName,
17
+ sli,
18
+ systemName,
19
+ };
20
+
21
+ const originalContentYaml = yaml.load(sloFileContent);
22
+ const fileContent = sloFileContent;
23
+ await appendIncludeConfigSlo(fileContent, originalContentYaml, slosFilePath, inputs);
24
+ };
25
+
26
+ module.exports = handleSlosFile;
@@ -0,0 +1,28 @@
1
+ const fs = require('fs');
2
+ const yaml = require('js-yaml');
3
+ const { appendIncludeConfigUptime } = require('./append');
4
+
5
+ const handleUptimeFile = async (answers, uptimeFilePath) => {
6
+ const {
7
+ serviceName,
8
+ hostname,
9
+ path,
10
+ systemName,
11
+ } = answers;
12
+
13
+ const uptimeFileContent = fs.readFileSync(uptimeFilePath, 'utf8');
14
+
15
+ const inputs = {
16
+ ...this.answers,
17
+ serviceName,
18
+ hostname,
19
+ path,
20
+ systemName,
21
+ };
22
+
23
+ const originalContentYaml = yaml.load(uptimeFileContent);
24
+ const fileContent = uptimeFileContent;
25
+ await appendIncludeConfigUptime(fileContent, originalContentYaml, uptimeFilePath, inputs);
26
+ };
27
+
28
+ module.exports = handleUptimeFile;
@@ -1,6 +1,11 @@
1
1
  const path = require('path');
2
2
  const chalk = require('chalk');
3
+ const fs = require('fs');
3
4
  const BaseGenerator = require('../../../src/BaseGenerator');
5
+ const { required } = require('../../../src/validators');
6
+ const helper = require('./validate');
7
+ const handleSlosFile = require('./handle-slos');
8
+ const handleUptimeFile = require('./handle-uptime');
4
9
 
5
10
  module.exports = class extends BaseGenerator {
6
11
  prompting() {
@@ -8,9 +13,51 @@ module.exports = class extends BaseGenerator {
8
13
  {
9
14
  type: 'list',
10
15
  name: 'monitoringResource',
11
- message: 'Select the resource that you want to monitor',
12
- default: 'dataflow',
13
- choices: ['cloudrun', 'cloudfunction', 'dataflow'],
16
+ message: 'Select the resource you want to create',
17
+ default: 'uptime-checks',
18
+ choices: ['uptime-checks', 'slos'],
19
+ },
20
+ {
21
+ when: (response) => response.monitoringResource === 'uptime-checks' || 'slos',
22
+ type: 'input',
23
+ name: 'systemName',
24
+ message: 'Please provide three-letter system name as defined in Styra',
25
+ validate: required && helper.validSystemName,
26
+ },
27
+ {
28
+ when: (response) => response.monitoringResource === 'uptime-checks' || 'slos',
29
+ type: 'input',
30
+ name: 'serviceName',
31
+ message: 'Please provide the namespace where the service resides',
32
+ validate: required,
33
+ },
34
+ {
35
+ when: (response) => response.monitoringResource === 'uptime-checks',
36
+ type: 'input',
37
+ name: 'hostname',
38
+ message: 'Please provide the base hostname of the service (example: my-service.retailsvc.com)',
39
+ validate: required && helper.validHostname,
40
+ },
41
+ {
42
+ when: (response) => response.monitoringResource === 'uptime-checks',
43
+ type: 'input',
44
+ name: 'path',
45
+ message: 'Please provide the path to the page to run the check against. (example: /health)',
46
+ validate: required,
47
+ },
48
+ {
49
+ when: (response) => response.monitoringResource === 'slos',
50
+ type: 'list',
51
+ name: 'sli',
52
+ message: 'Please select the SLI',
53
+ default: 'availability',
54
+ choices: ['availability', 'error-rate', 'latency'],
55
+ },
56
+ {
57
+ when: (response) => response.monitoringResource === 'slos' && response.sli === 'availability',
58
+ type: 'confirm',
59
+ name: 'info',
60
+ message: 'WARNING: Make sure that an uptime check has been created before applying availability SLI',
14
61
  },
15
62
  ];
16
63
 
@@ -19,37 +66,102 @@ module.exports = class extends BaseGenerator {
19
66
  });
20
67
  }
21
68
 
22
- writing() {
69
+ async writing() {
23
70
  const {
24
71
  monitoringResource,
72
+ serviceName,
73
+ hostname,
74
+ sli,
75
+ systemName,
25
76
  } = this.answers;
26
77
 
27
- ['prod', 'staging'].forEach((env) => {
28
- this.copyDir(
29
- path.join(monitoringResource),
30
- path.join('infra', env, 'monitoring', monitoringResource),
31
- {
32
- ...this.answers,
33
- env,
34
- },
35
- );
36
- });
78
+ const serviceFolderName = serviceName.replace(/ /g, '-').toLowerCase();
79
+ const serviceDir = path.join(process.cwd(), 'infra', 'prod', 'monitoring', monitoringResource, serviceFolderName);
80
+ const uptimeDirPath = path.join(process.cwd(), 'infra', 'prod', 'monitoring', monitoringResource);
37
81
 
38
- ['prod', 'staging'].forEach((env) => {
39
- this.copyDir(
40
- 'notification-channels',
41
- path.join('infra', env, 'monitoring', 'notification-channels'),
42
- {
43
- ...this.answers,
44
- env,
45
- },
46
- );
47
- });
82
+ if (monitoringResource === 'uptime-checks') {
83
+ if (!fs.existsSync(uptimeDirPath)) {
84
+ fs.mkdirSync(uptimeDirPath, { recursive: true });
85
+ }
86
+
87
+ const uptimeYamlFile = `${uptimeDirPath}/uptime-checks.yaml`;
88
+ if (!fs.existsSync(uptimeYamlFile)) {
89
+ this.copyDir(
90
+ 'uptime-checks',
91
+ uptimeDirPath,
92
+ {
93
+ ...this.answers,
94
+ serviceName,
95
+ hostname,
96
+ systemName,
97
+ },
98
+ );
99
+ } else {
100
+ await handleUptimeFile(this.answers, uptimeYamlFile);
101
+ }
102
+ }
103
+
104
+ if (monitoringResource === 'slos') {
105
+ const fileContainsFilter = (fileName, str) => {
106
+ const contents = fs.readFileSync(fileName, 'utf-8');
107
+ const result = contents.includes(str);
108
+ return result;
109
+ };
110
+
111
+ if (!fs.existsSync(serviceDir)) {
112
+ fs.mkdirSync(serviceDir, { recursive: true });
113
+ }
114
+
115
+ if (fs.existsSync(`${serviceDir}/terragrunt.hcl`)) {
116
+ if (fileContainsFilter(`${serviceDir}/terragrunt.hcl`, 'metric_filter') === false) {
117
+ this.fs.copyTpl(
118
+ this.templatePath('slos/terragrunt.hcl'),
119
+ this.destinationPath(`${serviceDir}/terragrunt.hcl`),
120
+ {
121
+ ...this.answers,
122
+ monitoringResource,
123
+ serviceName,
124
+ systemName,
125
+ },
126
+ );
127
+ }
128
+ } else {
129
+ this.fs.copyTpl(
130
+ this.templatePath('slos/terragrunt.hcl'),
131
+ this.destinationPath(`${serviceDir}/terragrunt.hcl`),
132
+ {
133
+ ...this.answers,
134
+ monitoringResource,
135
+ serviceName,
136
+ systemName,
137
+ },
138
+ );
139
+ }
140
+
141
+ const sloYamlFile = `${serviceDir}/slos.yaml`;
142
+ if (!fs.existsSync(sloYamlFile)) {
143
+ this.fs.copyTpl(
144
+ this.templatePath('slos/slos.yaml'),
145
+ this.destinationPath(sloYamlFile),
146
+ {
147
+ ...this.answers,
148
+ monitoringResource,
149
+ serviceName,
150
+ systemName,
151
+ sli,
152
+ },
153
+ );
154
+ } else {
155
+ await handleSlosFile(this.answers, sloYamlFile);
156
+ }
157
+ }
48
158
  }
49
159
 
50
160
  end() {
51
161
  this.log(`
52
162
  ${chalk.green('Your Monitoring resources have now been created.')}
53
- ${chalk.green('1.')} Push this change in a feature branch and open a pull request.`);
163
+ ${chalk.green('1.')} To finalize your configuration, please continue with manual editing of the generated files.
164
+ ${chalk.green('2.')} Push the changes in a feature branch and open a pull request.
165
+ `);
54
166
  }
55
167
  };
@@ -18,5 +18,7 @@ locals {
18
18
  inputs = merge(local.project_vars.locals, local.common_vars.locals,
19
19
  {
20
20
  clan_project_id = local.project_vars.locals.project_id
21
+ # Use var below if we decide to go with hiiretail-monitoring-prod project
22
+ #tribe_project_id = local.common_vars.locals.monitoring_project_id
21
23
  }
22
24
  )
@@ -0,0 +1,32 @@
1
+ <% if (sli === 'latency') { %>- display_name: Month - Latency
2
+ slo_id: month-latency
3
+ goal: 0.95
4
+ calendar_period: MONTH
5
+ type: request_based_sli
6
+ method: distribution_cut
7
+ metric_filter: |-
8
+ metric.type="knative.dev/serving/revision/request_latencies"
9
+ resource.type="knative_revision"
10
+ resource.labels.service_name="<%-serviceName%>"
11
+ range_min: 0
12
+ range_max: 100<% } %><% if (sli === 'availability') { %>- display_name: Month - Availability
13
+ slo_id: month-availability
14
+ goal: 0.998
15
+ calendar_period: MONTH
16
+ type: windows_based_sli
17
+ method: boolean_filter
18
+ window_period: 60s<% } %><% if (sli === 'error-rate') { %>- display_name: Month - Error rate
19
+ slo_id: month-error-rate
20
+ goal: 0.999
21
+ calendar_period: MONTH
22
+ type: request_based_sli
23
+ method: good_total_ratio
24
+ bad_service_filter: |-
25
+ metric.type="knative.dev/serving/revision/request_count"
26
+ resource.type="knative_revision"
27
+ metric.labels.response_code_class="5xx"
28
+ resource.labels.service_name="<%-serviceName%>"
29
+ total_service_filter: |-
30
+ metric.type="knative.dev/serving/revision/request_count"
31
+ resource.type="knative_revision"
32
+ resource.labels.service_name="<%-serviceName%>"<% } %>
@@ -0,0 +1,36 @@
1
+ # Terragrunt will copy the Terraform configurations specified by the source parameter, along with any files in the
2
+ # working directory, into a temporary folder, and execute your Terraform commands in that folder.
3
+ terraform {
4
+ source = "git::https://github.com/extenda/tf-module-gcp-slo//?ref=v0.1.0"
5
+ }
6
+
7
+ # Include all settings from the root terragrunt.hcl file
8
+ include {
9
+ path = find_in_parent_folders("terragrunt_root.hcl")
10
+ }
11
+
12
+ dependency "uptimecheck_id" {
13
+ config_path = "../../uptime-checks"
14
+ mock_outputs = {
15
+ uptime_check_ids = ["dummy-id"]
16
+ }
17
+ }
18
+
19
+ locals {
20
+ project_vars = read_terragrunt_config(find_in_parent_folders("project.hcl"))
21
+ }
22
+
23
+ # These are the variables we have to pass in to use the module specified in the terragrunt configuration above
24
+ inputs = merge(
25
+ local.project_vars.locals,
26
+ {
27
+ service_name = "<%-systemName%>.<%-serviceName%>"
28
+ slos = yamldecode(file("${get_terragrunt_dir()}/slos.yaml")),
29
+ <% if (sli === 'availability') { %>
30
+ metric_filter = {
31
+ "metric.type" = "monitoring.googleapis.com/uptime_check/check_passed"
32
+ "resource.type" = "uptime_url"
33
+ "metric.labels.check_id" = dependency.uptimecheck_id.outputs.uptime_check_ids["<%-systemName%>.<%-serviceName%>"]
34
+ }<% } %>
35
+ }
36
+ )
@@ -0,0 +1,36 @@
1
+ # Terragrunt will copy the Terraform configurations specified by the source parameter, along with any files in the
2
+ # working directory, into a temporary folder, and execute your Terraform commands in that folder.
3
+ terraform {
4
+ source = "git::https://github.com/extenda/tf-module-gcp-uptime-check//?ref=v0.1.0"
5
+ }
6
+
7
+ # Include all settings from the root terragrunt.hcl file
8
+ include {
9
+ path = find_in_parent_folders("terragrunt_root.hcl")
10
+ }
11
+
12
+ dependency "notification_channels" {
13
+ config_path = "../notification-channels"
14
+ mock_outputs = {
15
+ notification_channels = ["dummy-channel"]
16
+ }
17
+ }
18
+
19
+ locals {
20
+ project_vars = read_terragrunt_config(find_in_parent_folders("project.hcl"))
21
+ common_vars = read_terragrunt_config(find_in_parent_folders("common.hcl"))
22
+ }
23
+
24
+ # These are the variables we have to pass in to use the module specified in the terragrunt configuration above
25
+ inputs = merge(
26
+ local.project_vars.locals,
27
+ local.common_vars.locals,
28
+ {
29
+ notification_channels = dependency.notification_channels.outputs.notification_channels
30
+ uptime_checks = yamldecode(file("${get_terragrunt_dir()}/uptime-checks.yaml")),
31
+ labels = {
32
+ clan = local.common_vars.locals.clan_name
33
+ cc = local.common_vars.locals.cost_center
34
+ }
35
+ }
36
+ )
@@ -0,0 +1,3 @@
1
+ - service_name: "<%-systemName%>.<%-serviceName%>"
2
+ hostname: "<%-hostname%>"
3
+ path: "<%-path%>"
@@ -0,0 +1,18 @@
1
+ const helper = {};
2
+
3
+ helper.validHostname = (input) => {
4
+ const regex = new RegExp(/^(?:[a-z-]+\.){1,3}[a-z-]+$/g);
5
+ if (input.match(regex)) {
6
+ return true;
7
+ }
8
+ return 'Hostname must not include path to the page to run the check against or spaces';
9
+ };
10
+
11
+ helper.validSystemName = (input) => {
12
+ if (input.replace(/\s/g, '').length === 3) {
13
+ return true;
14
+ }
15
+ return 'System name must be 3 characters';
16
+ };
17
+
18
+ module.exports = helper;
@@ -1,5 +1,5 @@
1
1
  terraform {
2
- source = "git::https://github.com/extenda/tf-module-gcp-project//?ref=v1.0.6"
2
+ source = "git::https://github.com/extenda/tf-module-gcp-project//?ref=v1.0.8"
3
3
  }
4
4
 
5
5
  dependency "tribe_folder" {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hiiretail/gcp-infra-cli",
3
- "version": "0.71.0",
3
+ "version": "0.72.0",
4
4
  "description": "Infrastructure as code generator for GCP.",
5
5
  "main": "src/cli.js",
6
6
  "bin": {