diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9527a9e1..df479728 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
### BLUEPRINTS
+- [[#856](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/856)] Add network firewall metrics to network dashboard ([maunope](https://github.com/maunope))
- [[#868](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/868)] **incompatible change:** Refactor GKE module for Terraform 1.3 ([ludoo](https://github.com/ludoo))
- [[#818](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/818)] Example wordpress ([skalolazka](https://github.com/skalolazka))
- [[#861](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/861)] Leverage new shared VPC project config defaults across the repo ([juliocc](https://github.com/juliocc))
@@ -24,7 +25,7 @@ All notable changes to this project will be documented in this file.
### DOCUMENTATION
- [[#863](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/863)] Fabric vs CFT doc ([ludoo](https://github.com/ludoo))
-- [[#806](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/806)] Companion Guide ([ajlopezn](https://github.com/ajlopezn))
+- [[#806](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/806)] FAST Companion Guide ([ajlopezn](https://github.com/ajlopezn))
### FAST
@@ -41,6 +42,7 @@ All notable changes to this project will be documented in this file.
### MODULES
+- [[#869](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/869)] Fix optionals for resource_usage_export field in `gke-cluster` ([juliocc](https://github.com/juliocc))
- [[#868](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/868)] **incompatible change:** Refactor GKE module for Terraform 1.3 ([ludoo](https://github.com/ludoo))
- [[#866](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/866)] Update ipprefix_by_netmask.sh in nva module ([sruffilli](https://github.com/sruffilli))
- [[#860](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/860)] **incompatible change:** Refactor compute-vm for Terraform 1.3 ([ludoo](https://github.com/ludoo))
diff --git a/blueprints/cloud-operations/network-dashboard/README.md b/blueprints/cloud-operations/network-dashboard/README.md
index b300089b..254c8055 100644
--- a/blueprints/cloud-operations/network-dashboard/README.md
+++ b/blueprints/cloud-operations/network-dashboard/README.md
@@ -9,7 +9,7 @@ Here is an example of dashboard you can get with this solution:
Here you see utilization (usage compared to the limit) for a specific metric (number of instances per VPC) for multiple VPCs and projects.
-3 metrics are created: Usage, limit and utilization. You can follow each of these and create alerting policies if a threshold is reached.
+Three metric descriptors are created for each monitored resource: usage, limit and utilization. You can follow each of these and create alerting policies if a threshold is reached.
## Usage
@@ -45,6 +45,7 @@ The Cloud Function currently tracks usage, limit and utilization of:
- Dynamic routes per VPC
- Dynamic routes per VPC peering group
- IP utilization per subnet (% of IP addresses used in a subnet)
+- VPC firewall rules per project (VPC drill down is available for usage)
It writes this values to custom metrics in Cloud Monitoring and creates a dashboard to visualize the current utilization of these metrics in Cloud Monitoring.
diff --git a/blueprints/cloud-operations/network-dashboard/cloud-function/main.py b/blueprints/cloud-operations/network-dashboard/cloud-function/main.py
index 9f284136..7b36c79a 100644
--- a/blueprints/cloud-operations/network-dashboard/cloud-function/main.py
+++ b/blueprints/cloud-operations/network-dashboard/cloud-function/main.py
@@ -20,7 +20,7 @@ import time
from google.cloud import monitoring_v3, asset_v1
from google.protobuf import field_mask_pb2
from googleapiclient import discovery
-from metrics import ilb_fwrules, instances, networks, metrics, limits, peerings, routes, subnets
+from metrics import ilb_fwrules, instances, networks, metrics, limits, peerings, routes, subnets, vpc_firewalls
def get_monitored_projects_list(config):
@@ -33,7 +33,7 @@ def get_monitored_projects_list(config):
monitored_projects (List of strings): Full list of projects to be monitored
'''
monitored_projects = config["monitored_projects"]
- monitored_folders = os.environ.get("MONITORED_FOLDERS_LIST").split(",")
+ monitored_folders = [] #os.environ.get("MONITORED_FOLDERS_LIST").split(",")
# Handling empty monitored folders list
if monitored_folders == ['']:
@@ -94,7 +94,7 @@ config = {
# list of projects from which function will get quotas information
"monitored_projects":
os.environ.get("MONITORED_PROJECTS_LIST").split(","),
- "monitoring_project_link":
+ "monitoring_project":
os.environ.get('MONITORING_PROJECT_ID'),
"monitoring_project_link":
f"projects/{os.environ.get('MONITORING_PROJECT_ID')}",
@@ -143,6 +143,9 @@ def main(event, context):
metrics_dict, limits_dict = metrics.create_metrics(
config["monitoring_project_link"])
+ project_quotas_dict = limits.get_quota_project_limit(config)
+
+ firewalls_dict = vpc_firewalls.get_firewalls_dict(config)
# IP utilization subnet level metrics
subnets.get_subnets(config, metrics_dict)
@@ -153,6 +156,10 @@ def main(event, context):
l7_forwarding_rules_dict = ilb_fwrules.get_forwarding_rules_dict(config, "L7")
subnet_range_dict = networks.get_subnet_ranges_dict(config)
+ # Per Project metrics
+ vpc_firewalls.get_firewalls_data(config, metrics_dict, project_quotas_dict,
+ firewalls_dict)
+
# Per Network metrics
instances.get_gce_instances_data(config, metrics_dict, gce_instance_dict,
limits_dict['number_of_instances_limit'])
@@ -197,4 +204,4 @@ def main(event, context):
if __name__ == "__main__":
- main(None, None)
\ No newline at end of file
+ main(None, None)
diff --git a/blueprints/cloud-operations/network-dashboard/cloud-function/metrics.yaml b/blueprints/cloud-operations/network-dashboard/cloud-function/metrics.yaml
index 05f5369c..2a9c8a4e 100644
--- a/blueprints/cloud-operations/network-dashboard/cloud-function/metrics.yaml
+++ b/blueprints/cloud-operations/network-dashboard/cloud-function/metrics.yaml
@@ -159,4 +159,16 @@ metrics_per_peering_group:
default_value: 300
utilization:
name: dynamic_routes_per_peering_group_utilization
- description: Number of Dynamic routes per peering group - utilization.
\ No newline at end of file
+ description: Number of Dynamic routes per peering group - utilization.
+metrics_per_project:
+ firewalls:
+ usage:
+ name: firewalls_per_project_vpc_usage
+ description: Number of VPC firewall rules in a project - usage.
+ limit:
+ # Firewalls limit is per project and we get the limit for the GCP quota API in vpc_firewalls.py
+ name: firewalls_per_project_limit
+ description: Number of VPC firewall rules in a project - limit.
+ utilization:
+ name: firewalls_per_project_utilization
+ description: Number of VPC firewall rules in a project - utilization.
diff --git a/blueprints/cloud-operations/network-dashboard/cloud-function/metrics/limits.py b/blueprints/cloud-operations/network-dashboard/cloud-function/metrics/limits.py
index d17f8be4..a379a5c0 100644
--- a/blueprints/cloud-operations/network-dashboard/cloud-function/metrics/limits.py
+++ b/blueprints/cloud-operations/network-dashboard/cloud-function/metrics/limits.py
@@ -19,6 +19,60 @@ from google.cloud import monitoring_v3
from . import metrics
+def get_quotas_dict(quotas_list):
+ '''
+ Creates a dictionary of quotas from a list, with lower case quota name as keys
+ Parameters:
+ quotas_array (array): array of quotas
+ Returns:
+ quotas_dict (dict): dictionary of quotas
+ '''
+ quota_keys = [q['metric'] for q in quotas_list]
+ quotas_dict = dict()
+ i = 0
+ for key in quota_keys:
+ if ("metric" in quotas_list[i]):
+ del (quotas_list[i]["metric"])
+ quotas_dict[key.lower()] = quotas_list[i]
+ i += 1
+ return quotas_dict
+
+
+def get_quota_project_limit(config, regions=["global"]):
+ '''
+ Retrieves limit for a specific project quota
+ Parameters:
+ project_link (string): Project link.
+ Returns:
+ quotas (dict): quotas for all selected regions, default 'global'
+ '''
+ try:
+ request = {}
+ quotas = dict()
+ for project in config["monitored_projects"]:
+ quotas[project] = dict()
+ if regions != ["global"]:
+ for region in regions:
+ request = config["clients"]["discovery_client"].compute.regions().get(
+ region=region, project=project)
+ response = request.execute()
+ quotas[project][region] = get_quotas_dict(response['quotas'])
+ else:
+ region = "global"
+ request = config["clients"]["discovery_client"].projects().get(
+ project=project, fields="quotas")
+ response = request.execute()
+ quotas[project][region] = get_quotas_dict(response['quotas'])
+
+ return quotas
+ except exceptions.PermissionDenied as err:
+ print(
+ f"Warning: error reading quotas for {project}. " +
+ f"This can happen if you don't have permissions on the project, for example if the project is in another organization or a Google managed project"
+ )
+ return None
+
+
def get_ppg(network_link, limit_dict):
'''
Checks if this network has a specific limit for a metric, if so, returns that limit, if not, returns the default limit.
diff --git a/blueprints/cloud-operations/network-dashboard/cloud-function/metrics/metrics.py b/blueprints/cloud-operations/network-dashboard/cloud-function/metrics/metrics.py
index 4194a13a..155365b6 100644
--- a/blueprints/cloud-operations/network-dashboard/cloud-function/metrics/metrics.py
+++ b/blueprints/cloud-operations/network-dashboard/cloud-function/metrics/metrics.py
@@ -36,7 +36,7 @@ def create_metrics(monitoring_project):
existing_metrics.append(desc.type)
limits_dict = {}
- with open("metrics.yaml", 'r') as stream:
+ with open("./metrics.yaml", 'r') as stream:
try:
metrics_dict = yaml.safe_load(stream)
@@ -52,8 +52,9 @@ def create_metrics(monitoring_project):
# Subnet level metrics have a different limit: the subnet IP range size
if sub_metric_key == "limit" and metric_name != "ip_usage_per_subnet":
limits_dict_for_metric = {}
- for network_link, limit_value in sub_metric["values"].items():
- limits_dict_for_metric[network_link] = limit_value
+ if "values" in sub_metric:
+ for network_link, limit_value in sub_metric["values"].items():
+ limits_dict_for_metric[network_link] = limit_value
limits_dict[sub_metric["name"]] = limits_dict_for_metric
return metrics_dict, limits_dict
@@ -84,7 +85,7 @@ def create_metric(metric_name, description, monitoring_project):
def write_data_to_metric(config, monitored_project_id, value, metric_name,
- network_name, subnet_id=None):
+ network_name=None, subnet_id=None):
'''
Writes data to Cloud Monitoring custom metrics.
Parameters:
@@ -103,9 +104,10 @@ def write_data_to_metric(config, monitored_project_id, value, metric_name,
series = monitoring_v3.TimeSeries()
series.metric.type = f"custom.googleapis.com/{metric_name}"
series.resource.type = "global"
- series.metric.labels["network_name"] = network_name
series.metric.labels["project"] = monitored_project_id
- if subnet_id:
+ if network_name != None:
+ series.metric.labels["network_name"] = network_name
+ if subnet_id != None:
series.metric.labels["subnet_id"] = subnet_id
now = time.time()
diff --git a/blueprints/cloud-operations/network-dashboard/cloud-function/metrics/vpc_firewalls.py b/blueprints/cloud-operations/network-dashboard/cloud-function/metrics/vpc_firewalls.py
new file mode 100644
index 00000000..8d15eacd
--- /dev/null
+++ b/blueprints/cloud-operations/network-dashboard/cloud-function/metrics/vpc_firewalls.py
@@ -0,0 +1,111 @@
+#
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import re
+from collections import defaultdict
+from pydoc import doc
+from collections import defaultdict
+from google.protobuf import field_mask_pb2
+from . import metrics, networks, limits, peerings, routers
+
+
+def get_firewalls_dict(config: dict):
+ '''
+ Calls the Asset Inventory API to get all VPC Firewall Rules under the GCP organization.
+
+ Parameters:
+ config (dict): The dict containing config like clients and limits
+ Returns:
+ firewalls_dict (dictionary of dictionary: int): Keys are projects, subkeys are networks, values count #of VPC Firewall Rules
+ '''
+
+ firewalls_dict = defaultdict(int)
+ read_mask = field_mask_pb2.FieldMask()
+ read_mask.FromJsonString('name,versionedResources')
+
+ response = config["clients"]["asset_client"].search_all_resources(
+ request={
+ "scope": f"organizations/{config['organization']}",
+ "asset_types": ["compute.googleapis.com/Firewall"],
+ "read_mask": read_mask,
+ })
+ for resource in response:
+ project_id = re.search("(compute.googleapis.com/projects/)([\w\-\d]+)",
+ resource.name).group(2)
+ network_name = ""
+ for versioned in resource.versioned_resources:
+ for field_name, field_value in versioned.resource.items():
+ if field_name == "network":
+ network_name = re.search("[a-z0-9\-]*$", field_value).group(0)
+ firewalls_dict[project_id] = defaultdict(
+ int
+ ) if not project_id in firewalls_dict else firewalls_dict[project_id]
+ firewalls_dict[project_id][
+ network_name] = 1 if not network_name in firewalls_dict[
+ project_id] else firewalls_dict[project_id][network_name] + 1
+ break
+ break
+ return firewalls_dict
+
+
+def get_firewalls_data(config, metrics_dict, project_quotas_dict,
+ firewalls_dict):
+ '''
+ Gets the data for VPC Firewall Rules per VPC Network and writes it to the metric defined in vpc_firewalls_metric.
+
+ Parameters:
+ config (dict): The dict containing config like clients and limits
+ metrics_dict (dictionary of dictionary of string: string): metrics names and descriptions.
+ limit_dict (dictionary of string:int): Dictionary with the network link as key and the limit as value.
+ firewalls_dict (dictionary of dictionary): Keys are projects, subkeys are networks, values count #of VPC Firewall Rules
+ Returns:
+ None
+ '''
+ for project in config["monitored_projects"]:
+
+ current_quota_limit = project_quotas_dict[project]['global']["firewalls"]
+ if current_quota_limit is None:
+ print(
+ f"Could not write VPC firewal rules to metric for projects/{project} due to missing quotas"
+ )
+ continue
+
+ network_dict = networks.get_networks(config, project)
+
+ project_usage = 0
+ for net in network_dict:
+ usage = 0
+ if project in firewalls_dict and net['network_name'] in firewalls_dict[
+ project]:
+ usage = firewalls_dict[project][net['network_name']]
+ project_usage += usage
+ metrics.write_data_to_metric(
+ config, project, usage,
+ metrics_dict["metrics_per_project"][f"firewalls"]["usage"]["name"],
+ net['network_name'])
+
+ # firewall quotas are per project, not per single VPC
+ metrics.write_data_to_metric(
+ config, project, current_quota_limit['limit'],
+ metrics_dict["metrics_per_project"][f"firewalls"]["limit"]["name"])
+ metrics.write_data_to_metric(
+ config, project, project_usage / current_quota_limit['limit']
+ if current_quota_limit['limit'] != 0 else 0,
+ metrics_dict["metrics_per_project"][f"firewalls"]["utilization"]
+ ["name"])
+
+ print(
+ f"Wrote number of VPC Firewall Rules to metric for projects/{project}")
diff --git a/blueprints/cloud-operations/network-dashboard/dashboards/quotas-utilization.json b/blueprints/cloud-operations/network-dashboard/dashboards/quotas-utilization.json
index 6db2499a..923dbc84 100644
--- a/blueprints/cloud-operations/network-dashboard/dashboards/quotas-utilization.json
+++ b/blueprints/cloud-operations/network-dashboard/dashboards/quotas-utilization.json
@@ -1,4 +1,5 @@
{
+ "category": "CUSTOM",
"displayName": "quotas_utilization",
"mosaicLayout": {
"columns": 12,
@@ -17,6 +18,7 @@
"plotType": "LINE",
"targetAxis": "Y1",
"timeSeriesQuery": {
+ "apiSource": "DEFAULT_CLOUD",
"timeSeriesFilter": {
"aggregation": {
"alignmentPeriod": "3600s",
@@ -38,7 +40,9 @@
}
}
},
- "width": 6
+ "width": 6,
+ "xPos": 0,
+ "yPos": 0
},
{
"height": 4,
@@ -54,6 +58,7 @@
"plotType": "LINE",
"targetAxis": "Y1",
"timeSeriesQuery": {
+ "apiSource": "DEFAULT_CLOUD",
"timeSeriesFilter": {
"aggregation": {
"alignmentPeriod": "3600s",
@@ -76,6 +81,7 @@
}
},
"width": 6,
+ "xPos": 0,
"yPos": 12
},
{
@@ -92,6 +98,7 @@
"plotType": "LINE",
"targetAxis": "Y1",
"timeSeriesQuery": {
+ "apiSource": "DEFAULT_CLOUD",
"timeSeriesFilter": {
"aggregation": {
"alignmentPeriod": "3600s",
@@ -114,6 +121,7 @@
}
},
"width": 6,
+ "xPos": 0,
"yPos": 8
},
{
@@ -130,6 +138,7 @@
"plotType": "LINE",
"targetAxis": "Y1",
"timeSeriesQuery": {
+ "apiSource": "DEFAULT_CLOUD",
"timeSeriesFilter": {
"aggregation": {
"alignmentPeriod": "3600s",
@@ -169,6 +178,7 @@
"plotType": "LINE",
"targetAxis": "Y1",
"timeSeriesQuery": {
+ "apiSource": "DEFAULT_CLOUD",
"timeSeriesFilter": {
"aggregation": {
"alignmentPeriod": "3600s",
@@ -191,6 +201,7 @@
}
},
"width": 6,
+ "xPos": 0,
"yPos": 4
},
{
@@ -207,6 +218,7 @@
"plotType": "LINE",
"targetAxis": "Y1",
"timeSeriesQuery": {
+ "apiSource": "DEFAULT_CLOUD",
"timeSeriesFilter": {
"aggregation": {
"alignmentPeriod": "3600s",
@@ -229,6 +241,7 @@
}
},
"width": 6,
+ "xPos": 0,
"yPos": 16
},
{
@@ -245,6 +258,7 @@
"plotType": "LINE",
"targetAxis": "Y1",
"timeSeriesQuery": {
+ "apiSource": "DEFAULT_CLOUD",
"timeSeriesFilter": {
"aggregation": {
"alignmentPeriod": "3600s",
@@ -267,7 +281,8 @@
}
},
"width": 6,
- "xPos": 6
+ "xPos": 6,
+ "yPos": 0
},
{
"height": 4,
@@ -283,6 +298,7 @@
"plotType": "LINE",
"targetAxis": "Y1",
"timeSeriesQuery": {
+ "apiSource": "DEFAULT_CLOUD",
"timeSeriesFilter": {
"aggregation": {
"alignmentPeriod": "3600s",
@@ -322,6 +338,7 @@
"plotType": "LINE",
"targetAxis": "Y1",
"timeSeriesQuery": {
+ "apiSource": "DEFAULT_CLOUD",
"timeSeriesFilter": {
"aggregation": {
"alignmentPeriod": "3600s",
@@ -357,6 +374,7 @@
"plotType": "LINE",
"targetAxis": "Y1",
"timeSeriesQuery": {
+ "apiSource": "DEFAULT_CLOUD",
"timeSeriesFilter": {
"aggregation": {
"alignmentPeriod": "60s",
@@ -375,6 +393,97 @@
}
},
"width": 6,
+ "xPos": 6,
+ "yPos": 16
+ },
+ {
+ "height": 4,
+ "widget": {
+ "title": "firewalls_per_project_vpc_usage",
+ "xyChart": {
+ "chartOptions": {
+ "mode": "COLOR"
+ },
+ "dataSets": [
+ {
+ "minAlignmentPeriod": "60s",
+ "plotType": "LINE",
+ "targetAxis": "Y1",
+ "timeSeriesQuery": {
+ "apiSource": "DEFAULT_CLOUD",
+ "timeSeriesFilter": {
+ "aggregation": {
+ "alignmentPeriod": "60s",
+ "crossSeriesReducer": "REDUCE_SUM",
+ "groupByFields": [
+ "metric.label.\"project\""
+ ],
+ "perSeriesAligner": "ALIGN_MEAN"
+ },
+ "filter": "metric.type=\"custom.googleapis.com/firewalls_per_project_vpc_usage\" resource.type=\"global\"",
+ "secondaryAggregation": {
+ "alignmentPeriod": "60s",
+ "perSeriesAligner": "ALIGN_NONE"
+ }
+ }
+ }
+ }
+ ],
+ "thresholds": [],
+ "timeshiftDuration": "0s",
+ "yAxis": {
+ "label": "y1Axis",
+ "scale": "LINEAR"
+ }
+ }
+ },
+ "width": 6,
+ "xPos": 0,
+ "yPos": 20
+ },
+ {
+ "height": 4,
+ "widget": {
+ "title": "firewalls_per_project_utilization",
+ "xyChart": {
+ "chartOptions": {
+ "mode": "COLOR"
+ },
+ "dataSets": [
+ {
+ "minAlignmentPeriod": "60s",
+ "plotType": "LINE",
+ "targetAxis": "Y1",
+ "timeSeriesQuery": {
+ "apiSource": "DEFAULT_CLOUD",
+ "timeSeriesFilter": {
+ "aggregation": {
+ "alignmentPeriod": "60s",
+ "crossSeriesReducer": "REDUCE_MAX",
+ "groupByFields": [
+ "metric.label.\"project\""
+ ],
+ "perSeriesAligner": "ALIGN_MAX"
+ },
+ "filter": "metric.type=\"custom.googleapis.com/firewalls_per_project_utilization\" resource.type=\"global\"",
+ "secondaryAggregation": {
+ "alignmentPeriod": "60s",
+ "perSeriesAligner": "ALIGN_NONE"
+ }
+ }
+ }
+ }
+ ],
+ "thresholds": [],
+ "timeshiftDuration": "0s",
+ "yAxis": {
+ "label": "y1Axis",
+ "scale": "LINEAR"
+ }
+ }
+ },
+ "width": 6,
+ "xPos": 6,
"yPos": 20
},
{
@@ -391,15 +500,13 @@
"plotType": "LINE",
"targetAxis": "Y1",
"timeSeriesQuery": {
+ "apiSource": "DEFAULT_CLOUD",
"timeSeriesFilter": {
"aggregation": {
"alignmentPeriod": "60s",
"perSeriesAligner": "ALIGN_MEAN"
},
- "filter": "metric.type=\"custom.googleapis.com/ip_addresses_per_subnet_utilization\" resource.type=\"global\"",
- "secondaryAggregation": {
- "alignmentPeriod": "60s"
- }
+ "filter": "metric.type=\"custom.googleapis.com/ip_addresses_per_subnet_utilization\" resource.type=\"global\""
}
}
}
@@ -412,48 +519,9 @@
}
},
"width": 6,
- "xPos": 6,
- "yPos": 16
- },
- {
- "height": 4,
- "widget": {
- "title": "dynamic_routes_ppg_utilization",
- "xyChart": {
- "chartOptions": {
- "mode": "COLOR"
- },
- "dataSets": [
- {
- "minAlignmentPeriod": "60s",
- "plotType": "LINE",
- "targetAxis": "Y1",
- "timeSeriesQuery": {
- "timeSeriesFilter": {
- "aggregation": {
- "alignmentPeriod": "60s",
- "perSeriesAligner": "ALIGN_MEAN"
- },
- "filter": "metric.type=\"custom.googleapis.com/dynamic_routes_per_peering_group_utilization\" resource.type=\"global\"",
- "secondaryAggregation": {
- "alignmentPeriod": "60s"
- }
- }
- }
- }
- ],
- "timeshiftDuration": "0s",
- "yAxis": {
- "label": "y1Axis",
- "scale": "LINEAR"
- }
- }
- },
- "width": 6,
- "xPos": 6,
- "yPos": 20
+ "xPos": 0,
+ "yPos": 24
}
]
- },
- "name": "projects/347834224817/dashboards/1bdcd06a-030d-4977-bf4b-f32231aa3b77"
+ }
}
\ No newline at end of file
diff --git a/modules/gke-cluster/README.md b/modules/gke-cluster/README.md
index d2b2d9b9..f5a0dc0c 100644
--- a/modules/gke-cluster/README.md
+++ b/modules/gke-cluster/README.md
@@ -76,24 +76,24 @@ module "cluster-1" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [location](variables.tf#L118) | Cluster zone or region. | string
| ✓ | |
-| [name](variables.tf#L170) | Cluster name. | string
| ✓ | |
-| [project_id](variables.tf#L197) | Cluster project id. | string
| ✓ | |
-| [vpc_config](variables.tf#L208) | VPC-level configuration. | object({…})
| ✓ | |
+| [location](variables.tf#L117) | Cluster zone or region. | string
| ✓ | |
+| [name](variables.tf#L169) | Cluster name. | string
| ✓ | |
+| [project_id](variables.tf#L196) | Cluster project id. | string
| ✓ | |
+| [vpc_config](variables.tf#L207) | VPC-level configuration. | object({…})
| ✓ | |
| [cluster_autoscaling](variables.tf#L17) | Enable and configure limits for Node Auto-Provisioning with Cluster Autoscaler. | object({…})
| | null
|
| [description](variables.tf#L38) | Cluster description. | string
| | null
|
| [enable_addons](variables.tf#L44) | Addons enabled in the cluster (true means enabled). | object({…})
| | {…}
|
-| [enable_features](variables.tf#L68) | Enable cluster-level features. Certain features allow configuration. | object({…})
| | {…}
|
-| [issue_client_certificate](variables.tf#L106) | Enable issuing client certificate. | bool
| | false
|
-| [labels](variables.tf#L112) | Cluster resource labels. | map(string)
| | null
|
-| [logging_config](variables.tf#L123) | Logging configuration. | list(string)
| | ["SYSTEM_COMPONENTS"]
|
-| [maintenance_config](variables.tf#L129) | Maintenance window configuration. | object({…})
| | {…}
|
-| [max_pods_per_node](variables.tf#L152) | Maximum number of pods per node in this cluster. | number
| | 110
|
-| [min_master_version](variables.tf#L158) | Minimum version of the master, defaults to the version of the most recent official release. | string
| | null
|
-| [monitoring_config](variables.tf#L164) | Monitoring components. | list(string)
| | ["SYSTEM_COMPONENTS"]
|
-| [node_locations](variables.tf#L175) | Zones in which the cluster's nodes are located. | list(string)
| | []
|
-| [private_cluster_config](variables.tf#L182) | Private cluster configuration. | object({…})
| | null
|
-| [release_channel](variables.tf#L202) | Release channel for GKE upgrades. | string
| | null
|
+| [enable_features](variables.tf#L68) | Enable cluster-level features. Certain features allow configuration. | object({…})
| | {…}
|
+| [issue_client_certificate](variables.tf#L105) | Enable issuing client certificate. | bool
| | false
|
+| [labels](variables.tf#L111) | Cluster resource labels. | map(string)
| | null
|
+| [logging_config](variables.tf#L122) | Logging configuration. | list(string)
| | ["SYSTEM_COMPONENTS"]
|
+| [maintenance_config](variables.tf#L128) | Maintenance window configuration. | object({…})
| | {…}
|
+| [max_pods_per_node](variables.tf#L151) | Maximum number of pods per node in this cluster. | number
| | 110
|
+| [min_master_version](variables.tf#L157) | Minimum version of the master, defaults to the version of the most recent official release. | string
| | null
|
+| [monitoring_config](variables.tf#L163) | Monitoring components. | list(string)
| | ["SYSTEM_COMPONENTS"]
|
+| [node_locations](variables.tf#L174) | Zones in which the cluster's nodes are located. | list(string)
| | []
|
+| [private_cluster_config](variables.tf#L181) | Private cluster configuration. | object({…})
| | null
|
+| [release_channel](variables.tf#L201) | Release channel for GKE upgrades. | string
| | null
|
## Outputs
diff --git a/modules/gke-cluster/variables.tf b/modules/gke-cluster/variables.tf
index 97332266..52e64c84 100644
--- a/modules/gke-cluster/variables.tf
+++ b/modules/gke-cluster/variables.tf
@@ -85,9 +85,9 @@ variable "enable_features" {
l4_ilb_subsetting = optional(bool, false)
pod_security_policy = optional(bool, false)
resource_usage_export = optional(object({
- dataset = optional(string)
- enable_network_egress_metering = optional(bool, false)
- enable_resource_consumption_metering = optional(bool, false)
+ dataset = string
+ enable_network_egress_metering = optional(bool)
+ enable_resource_consumption_metering = optional(bool)
}))
shielded_nodes = optional(bool, false)
tpu = optional(bool, false)
@@ -98,8 +98,7 @@ variable "enable_features" {
workload_identity = optional(bool, false)
})
default = {
- workload_identity = true
- resource_usage_export = null
+ workload_identity = true
}
}