cartography 0.112.0__py3-none-any.whl → 0.114.0__py3-none-any.whl

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.

Potentially problematic release.


This version of cartography might be problematic. Click here for more details.

Files changed (82) hide show
  1. cartography/_version.py +2 -2
  2. cartography/cli.py +8 -0
  3. cartography/config.py +4 -0
  4. cartography/data/indexes.cypher +0 -31
  5. cartography/intel/aws/apigatewayv2.py +116 -0
  6. cartography/intel/aws/iam.py +741 -492
  7. cartography/intel/aws/organizations.py +7 -8
  8. cartography/intel/aws/permission_relationships.py +4 -16
  9. cartography/intel/aws/resources.py +2 -0
  10. cartography/intel/azure/__init__.py +16 -0
  11. cartography/intel/azure/app_service.py +105 -0
  12. cartography/intel/azure/functions.py +124 -0
  13. cartography/intel/entra/__init__.py +31 -0
  14. cartography/intel/entra/app_role_assignments.py +277 -0
  15. cartography/intel/entra/applications.py +4 -238
  16. cartography/intel/entra/federation/__init__.py +0 -0
  17. cartography/intel/entra/federation/aws_identity_center.py +77 -0
  18. cartography/intel/entra/service_principals.py +217 -0
  19. cartography/intel/gcp/__init__.py +136 -436
  20. cartography/intel/gcp/clients.py +65 -0
  21. cartography/intel/gcp/compute.py +18 -44
  22. cartography/intel/gcp/crm/__init__.py +0 -0
  23. cartography/intel/gcp/crm/folders.py +108 -0
  24. cartography/intel/gcp/crm/orgs.py +65 -0
  25. cartography/intel/gcp/crm/projects.py +109 -0
  26. cartography/intel/gcp/dns.py +82 -169
  27. cartography/intel/gcp/gke.py +72 -113
  28. cartography/intel/gcp/iam.py +66 -54
  29. cartography/intel/gcp/storage.py +75 -159
  30. cartography/intel/github/__init__.py +41 -0
  31. cartography/intel/github/commits.py +423 -0
  32. cartography/intel/github/repos.py +73 -39
  33. cartography/models/aws/apigatewayv2/__init__.py +0 -0
  34. cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -0
  35. cartography/models/aws/iam/access_key.py +103 -0
  36. cartography/models/aws/iam/account_role.py +24 -0
  37. cartography/models/aws/iam/federated_principal.py +60 -0
  38. cartography/models/aws/iam/group.py +60 -0
  39. cartography/models/aws/iam/group_membership.py +26 -0
  40. cartography/models/aws/iam/inline_policy.py +78 -0
  41. cartography/models/aws/iam/managed_policy.py +51 -0
  42. cartography/models/aws/iam/policy_statement.py +57 -0
  43. cartography/models/aws/iam/role.py +83 -0
  44. cartography/models/aws/iam/root_principal.py +52 -0
  45. cartography/models/aws/iam/service_principal.py +30 -0
  46. cartography/models/aws/iam/sts_assumerole_allow.py +38 -0
  47. cartography/models/aws/iam/user.py +54 -0
  48. cartography/models/azure/__init__.py +0 -0
  49. cartography/models/azure/app_service.py +59 -0
  50. cartography/models/azure/function_app.py +59 -0
  51. cartography/models/entra/entra_user_to_aws_sso.py +41 -0
  52. cartography/models/entra/service_principal.py +104 -0
  53. cartography/models/gcp/compute/subnet.py +74 -0
  54. cartography/models/gcp/crm/__init__.py +0 -0
  55. cartography/models/gcp/crm/folders.py +98 -0
  56. cartography/models/gcp/crm/organizations.py +21 -0
  57. cartography/models/gcp/crm/projects.py +100 -0
  58. cartography/models/gcp/dns.py +109 -0
  59. cartography/models/gcp/gke.py +69 -0
  60. cartography/models/gcp/iam.py +3 -0
  61. cartography/models/gcp/storage/__init__.py +0 -0
  62. cartography/models/gcp/storage/bucket.py +119 -0
  63. cartography/models/github/commits.py +63 -0
  64. {cartography-0.112.0.dist-info → cartography-0.114.0.dist-info}/METADATA +7 -5
  65. {cartography-0.112.0.dist-info → cartography-0.114.0.dist-info}/RECORD +69 -39
  66. cartography/data/jobs/cleanup/aws_import_account_access_key_cleanup.json +0 -17
  67. cartography/data/jobs/cleanup/aws_import_groups_cleanup.json +0 -13
  68. cartography/data/jobs/cleanup/aws_import_principals_cleanup.json +0 -30
  69. cartography/data/jobs/cleanup/aws_import_roles_cleanup.json +0 -13
  70. cartography/data/jobs/cleanup/aws_import_users_cleanup.json +0 -8
  71. cartography/data/jobs/cleanup/gcp_compute_vpc_subnet_cleanup.json +0 -35
  72. cartography/data/jobs/cleanup/gcp_crm_folder_cleanup.json +0 -23
  73. cartography/data/jobs/cleanup/gcp_crm_organization_cleanup.json +0 -17
  74. cartography/data/jobs/cleanup/gcp_crm_project_cleanup.json +0 -23
  75. cartography/data/jobs/cleanup/gcp_dns_cleanup.json +0 -29
  76. cartography/data/jobs/cleanup/gcp_gke_cluster_cleanup.json +0 -17
  77. cartography/data/jobs/cleanup/gcp_storage_bucket_cleanup.json +0 -29
  78. cartography/intel/gcp/crm.py +0 -355
  79. {cartography-0.112.0.dist-info → cartography-0.114.0.dist-info}/WHEEL +0 -0
  80. {cartography-0.112.0.dist-info → cartography-0.114.0.dist-info}/entry_points.txt +0 -0
  81. {cartography-0.112.0.dist-info → cartography-0.114.0.dist-info}/licenses/LICENSE +0 -0
  82. {cartography-0.112.0.dist-info → cartography-0.114.0.dist-info}/top_level.txt +0 -0
@@ -7,28 +7,20 @@ import neo4j
7
7
  from googleapiclient.discovery import HttpError
8
8
  from googleapiclient.discovery import Resource
9
9
 
10
- from cartography.util import run_cleanup_job
10
+ from cartography.client.core.tx import load
11
+ from cartography.graph.job import GraphJob
12
+ from cartography.models.gcp.dns import GCPDNSZoneSchema
13
+ from cartography.models.gcp.dns import GCPRecordSetSchema
11
14
  from cartography.util import timeit
12
15
 
13
16
  logger = logging.getLogger(__name__)
14
17
 
15
18
 
16
19
  @timeit
17
- def get_dns_zones(dns: Resource, project_id: str) -> List[Resource]:
18
- """
19
- Returns a list of DNS zones within the given project.
20
-
21
- :type dns: The GCP DNS resource object
22
- :param dns: The DNS resource object created by googleapiclient.discovery.build()
23
-
24
- :type project_id: str
25
- :param project_id: Current Google Project Id
26
-
27
- :rtype: list
28
- :return: List of DNS zones
29
- """
20
+ def get_dns_zones(dns: Resource, project_id: str) -> List[Dict]:
21
+ """Returns a list of DNS zones within the given project."""
30
22
  try:
31
- zones = []
23
+ zones: List[Dict] = []
32
24
  request = dns.managedZones().list(project=project_id)
33
25
  while request is not None:
34
26
  response = request.execute()
@@ -47,40 +39,22 @@ def get_dns_zones(dns: Resource, project_id: str) -> List[Resource]:
47
39
  ):
48
40
  logger.warning(
49
41
  (
50
- "Could not retrieve DNS zones on project %s due to permissions issues. Code: %s, Message: %s"
42
+ "Could not retrieve DNS zones on project %s due to permissions issues. "
43
+ "Code: %s, Message: %s"
51
44
  ),
52
45
  project_id,
53
46
  err["code"],
54
47
  err["message"],
55
48
  )
56
49
  return []
57
- else:
58
- raise
50
+ raise
59
51
 
60
52
 
61
53
  @timeit
62
- def get_dns_rrs(
63
- dns: Resource,
64
- dns_zones: List[Dict],
65
- project_id: str,
66
- ) -> List[Resource]:
67
- """
68
- Returns a list of DNS Resource Record Sets within the given project.
69
-
70
- :type dns: The GCP DNS resource object
71
- :param dns: The DNS resource object created by googleapiclient.discovery.build()
72
-
73
- :type dns_zones: list
74
- :param dns_zones: List of DNS zones for the project
75
-
76
- :type project_id: str
77
- :param project_id: Current Google Project Id
78
-
79
- :rtype: list
80
- :return: List of Resource Record Sets
81
- """
54
+ def get_dns_rrs(dns: Resource, dns_zones: List[Dict], project_id: str) -> List[Dict]:
55
+ """Returns a list of DNS Resource Record Sets within the given project."""
82
56
  try:
83
- rrs: List[Resource] = []
57
+ rrs: List[Dict] = []
84
58
  for zone in dns_zones:
85
59
  request = dns.resourceRecordSets().list(
86
60
  project=project_id,
@@ -104,16 +78,53 @@ def get_dns_rrs(
104
78
  ):
105
79
  logger.warning(
106
80
  (
107
- "Could not retrieve DNS RRS on project %s due to permissions issues. Code: %s, Message: %s"
81
+ "Could not retrieve DNS RRS on project %s due to permissions issues. "
82
+ "Code: %s, Message: %s"
108
83
  ),
109
84
  project_id,
110
85
  err["code"],
111
86
  err["message"],
112
87
  )
113
88
  return []
114
- else:
115
- raise
116
- raise e
89
+ raise
90
+
91
+
92
+ @timeit
93
+ def transform_dns_zones(dns_zones: List[Dict]) -> List[Dict]:
94
+ """Transform raw DNS zone responses into Neo4j-ready dicts."""
95
+ zones: List[Dict] = []
96
+ for z in dns_zones:
97
+ zones.append(
98
+ {
99
+ "id": z["id"],
100
+ "name": z.get("name"),
101
+ "dns_name": z.get("dnsName"),
102
+ "description": z.get("description"),
103
+ "visibility": z.get("visibility"),
104
+ "kind": z.get("kind"),
105
+ "nameservers": z.get("nameServers"),
106
+ "created_at": z.get("creationTime"),
107
+ }
108
+ )
109
+ return zones
110
+
111
+
112
+ @timeit
113
+ def transform_dns_rrs(dns_rrs: List[Dict]) -> List[Dict]:
114
+ """Transform raw DNS record set responses into Neo4j-ready dicts."""
115
+ records: List[Dict] = []
116
+ for r in dns_rrs:
117
+ records.append(
118
+ {
119
+ "id": r["name"],
120
+ "name": r["name"],
121
+ "type": r.get("type"),
122
+ "ttl": r.get("ttl"),
123
+ "data": r.get("rrdatas"),
124
+ "zone_id": r.get("zone"),
125
+ }
126
+ )
127
+ return records
117
128
 
118
129
 
119
130
  @timeit
@@ -123,102 +134,30 @@ def load_dns_zones(
123
134
  project_id: str,
124
135
  gcp_update_tag: int,
125
136
  ) -> None:
126
- """
127
- Ingest GCP DNS Zones into Neo4j
128
-
129
- :type neo4j_session: Neo4j session object
130
- :param neo4j session: The Neo4j session object
131
-
132
- :type dns_resp: Dict
133
- :param dns_resp: A DNS response object from the GKE API
134
-
135
- :type project_id: str
136
- :param project_id: Current Google Project Id
137
-
138
- :type gcp_update_tag: timestamp
139
- :param gcp_update_tag: The timestamp value to set our new Neo4j nodes with
140
-
141
- :rtype: NoneType
142
- :return: Nothing
143
- """
144
-
145
- ingest_records = """
146
- UNWIND $records as record
147
- MERGE(zone:GCPDNSZone{id:record.id})
148
- ON CREATE SET
149
- zone.firstseen = timestamp(),
150
- zone.created_at = record.creationTime
151
- SET
152
- zone.name = record.name,
153
- zone.dns_name = record.dnsName,
154
- zone.description = record.description,
155
- zone.visibility = record.visibility,
156
- zone.kind = record.kind,
157
- zone.nameservers = record.nameServers,
158
- zone.lastupdated = $gcp_update_tag
159
- WITH zone
160
- MATCH (owner:GCPProject{id:$ProjectId})
161
- MERGE (owner)-[r:RESOURCE]->(zone)
162
- ON CREATE SET
163
- r.firstseen = timestamp(),
164
- r.lastupdated = $gcp_update_tag
165
- """
166
- neo4j_session.run(
167
- ingest_records,
168
- records=dns_zones,
169
- ProjectId=project_id,
170
- gcp_update_tag=gcp_update_tag,
137
+ """Ingest GCP DNS Zones into Neo4j."""
138
+ load(
139
+ neo4j_session,
140
+ GCPDNSZoneSchema(),
141
+ dns_zones,
142
+ lastupdated=gcp_update_tag,
143
+ PROJECT_ID=project_id,
171
144
  )
172
145
 
173
146
 
174
147
  @timeit
175
148
  def load_rrs(
176
149
  neo4j_session: neo4j.Session,
177
- dns_rrs: List[Resource],
150
+ dns_rrs: List[Dict],
178
151
  project_id: str,
179
152
  gcp_update_tag: int,
180
153
  ) -> None:
181
- """
182
- Ingest GCP RRS into Neo4j
183
-
184
- :type neo4j_session: Neo4j session object
185
- :param neo4j session: The Neo4j session object
186
-
187
- :type dns_rrs: list
188
- :param dns_rrs: A list of RRS
189
-
190
- :type project_id: str
191
- :param project_id: Current Google Project Id
192
-
193
- :type gcp_update_tag: timestamp
194
- :param gcp_update_tag: The timestamp value to set our new Neo4j nodes with
195
-
196
- :rtype: NoneType
197
- :return: Nothing
198
- """
199
-
200
- ingest_records = """
201
- UNWIND $records as record
202
- MERGE(rrs:GCPRecordSet{id:record.name})
203
- ON CREATE SET
204
- rrs.firstseen = timestamp()
205
- SET
206
- rrs.name = record.name,
207
- rrs.type = record.type,
208
- rrs.ttl = record.ttl,
209
- rrs.data = record.rrdatas,
210
- rrs.lastupdated = $gcp_update_tag
211
- WITH rrs, record
212
- MATCH (zone:GCPDNSZone{id:record.zone})
213
- MERGE (zone)-[r:HAS_RECORD]->(rrs)
214
- ON CREATE SET
215
- r.firstseen = timestamp(),
216
- r.lastupdated = $gcp_update_tag
217
- """
218
- neo4j_session.run(
219
- ingest_records,
220
- records=dns_rrs,
221
- gcp_update_tag=gcp_update_tag,
154
+ """Ingest GCP DNS Resource Record Sets into Neo4j."""
155
+ load(
156
+ neo4j_session,
157
+ GCPRecordSetSchema(),
158
+ dns_rrs,
159
+ lastupdated=gcp_update_tag,
160
+ PROJECT_ID=project_id,
222
161
  )
223
162
 
224
163
 
@@ -227,19 +166,14 @@ def cleanup_dns_records(
227
166
  neo4j_session: neo4j.Session,
228
167
  common_job_parameters: Dict,
229
168
  ) -> None:
230
- """
231
- Delete out-of-date GCP DNS Zones and RRS nodes and relationships
232
-
233
- :type neo4j_session: The Neo4j session object
234
- :param neo4j_session: The Neo4j session
235
-
236
- :type common_job_parameters: dict
237
- :param common_job_parameters: Dictionary of other job parameters to pass to Neo4j
238
-
239
- :rtype: NoneType
240
- :return: Nothing
241
- """
242
- run_cleanup_job("gcp_dns_cleanup.json", neo4j_session, common_job_parameters)
169
+ """Delete out-of-date GCP DNS Zones and Record Sets nodes and relationships."""
170
+ # Record sets depend on zones, so clean them up first.
171
+ GraphJob.from_node_schema(GCPRecordSetSchema(), common_job_parameters).run(
172
+ neo4j_session,
173
+ )
174
+ GraphJob.from_node_schema(GCPDNSZoneSchema(), common_job_parameters).run(
175
+ neo4j_session,
176
+ )
243
177
 
244
178
 
245
179
  @timeit
@@ -250,33 +184,12 @@ def sync(
250
184
  gcp_update_tag: int,
251
185
  common_job_parameters: Dict,
252
186
  ) -> None:
253
- """
254
- Get GCP DNS Zones and Resource Record Sets using the DNS resource object, ingest to Neo4j, and clean up old data.
255
-
256
- :type neo4j_session: The Neo4j session object
257
- :param neo4j_session: The Neo4j session
258
-
259
- :type dns: The DNS resource object created by googleapiclient.discovery.build()
260
- :param dns: The GCP DNS resource object
261
-
262
- :type project_id: str
263
- :param project_id: The project ID of the corresponding project
264
-
265
- :type gcp_update_tag: timestamp
266
- :param gcp_update_tag: The timestamp value to set our new Neo4j nodes with
267
-
268
- :type common_job_parameters: dict
269
- :param common_job_parameters: Dictionary of other job parameters to pass to Neo4j
270
-
271
- :rtype: NoneType
272
- :return: Nothing
273
- """
187
+ """Get GCP DNS Zones and Record Sets, load them into Neo4j, and clean up old data."""
274
188
  logger.info("Syncing DNS records for project %s.", project_id)
275
- # DNS ZONES
276
- dns_zones = get_dns_zones(dns, project_id)
189
+ dns_zones_resp = get_dns_zones(dns, project_id)
190
+ dns_zones = transform_dns_zones(dns_zones_resp)
277
191
  load_dns_zones(neo4j_session, dns_zones, project_id, gcp_update_tag)
278
- # RECORD SETS
279
- dns_rrs = get_dns_rrs(dns, dns_zones, project_id)
192
+ dns_rrs_resp = get_dns_rrs(dns, dns_zones_resp, project_id)
193
+ dns_rrs = transform_dns_rrs(dns_rrs_resp)
280
194
  load_rrs(neo4j_session, dns_rrs, project_id, gcp_update_tag)
281
- # TODO scope the cleanup to the current project - https://github.com/cartography-cncf/cartography/issues/381
282
195
  cleanup_dns_records(neo4j_session, common_job_parameters)
@@ -1,12 +1,16 @@
1
1
  import json
2
2
  import logging
3
+ from typing import Any
3
4
  from typing import Dict
5
+ from typing import List
4
6
 
5
7
  import neo4j
6
8
  from googleapiclient.discovery import HttpError
7
9
  from googleapiclient.discovery import Resource
8
10
 
9
- from cartography.util import run_cleanup_job
11
+ from cartography.client.core.tx import load
12
+ from cartography.graph.job import GraphJob
13
+ from cartography.models.gcp.gke import GCPGKEClusterSchema
10
14
  from cartography.util import timeit
11
15
 
12
16
  logger = logging.getLogger(__name__)
@@ -56,105 +60,20 @@ def load_gke_clusters(
56
60
  gcp_update_tag: int,
57
61
  ) -> None:
58
62
  """
59
- Ingest GCP GKE Clusters to Neo4j
60
-
61
- :type neo4j_session: Neo4j session object
62
- :param neo4j session: The Neo4j session object
63
-
64
- :type cluster_resp: Dict
65
- :param cluster_resp: A cluster response object from the GKE API
66
-
67
- :type gcp_update_tag: timestamp
68
- :param gcp_update_tag: The timestamp value to set our new Neo4j nodes with
69
-
70
- :rtype: NoneType
71
- :return: Nothing
63
+ Ingest GCP GKE clusters using the data model loader.
72
64
  """
65
+ clusters: List[Dict[str, Any]] = transform_gke_clusters(cluster_resp)
73
66
 
74
- query = """
75
- MERGE(cluster:GKECluster{id:$ClusterSelfLink})
76
- ON CREATE SET
77
- cluster.firstseen = timestamp(),
78
- cluster.created_at = $ClusterCreateTime
79
- SET
80
- cluster.name = $ClusterName,
81
- cluster.self_link = $ClusterSelfLink,
82
- cluster.description = $ClusterDescription,
83
- cluster.logging_service = $ClusterLoggingService,
84
- cluster.monitoring_service = $ClusterMonitoringService,
85
- cluster.network = $ClusterNetwork,
86
- cluster.subnetwork = $ClusterSubnetwork,
87
- cluster.cluster_ipv4cidr = $ClusterIPv4Cidr,
88
- cluster.zone = $ClusterZone,
89
- cluster.location = $ClusterLocation,
90
- cluster.endpoint = $ClusterEndpoint,
91
- cluster.initial_version = $ClusterInitialVersion,
92
- cluster.current_master_version = $ClusterMasterVersion,
93
- cluster.status = $ClusterStatus,
94
- cluster.services_ipv4cidr = $ClusterServicesIPv4Cidr,
95
- cluster.database_encryption = $ClusterDatabaseEncryption,
96
- cluster.network_policy = $ClusterNetworkPolicy,
97
- cluster.master_authorized_networks = $ClusterMasterAuthorizedNetworks,
98
- cluster.legacy_abac = $ClusterAbac,
99
- cluster.shielded_nodes = $ClusterShieldedNodes,
100
- cluster.private_nodes = $ClusterPrivateNodes,
101
- cluster.private_endpoint_enabled = $ClusterPrivateEndpointEnabled,
102
- cluster.private_endpoint = $ClusterPrivateEndpoint,
103
- cluster.public_endpoint = $ClusterPublicEndpoint,
104
- cluster.masterauth_username = $ClusterMasterUsername,
105
- cluster.masterauth_password = $ClusterMasterPassword
106
- WITH cluster
107
- MATCH (owner:GCPProject{id:$ProjectId})
108
- MERGE (owner)-[r:RESOURCE]->(cluster)
109
- ON CREATE SET r.firstseen = timestamp()
110
- SET r.lastupdated = $gcp_update_tag
111
- """
112
- for cluster in cluster_resp.get("clusters", []):
113
- neo4j_session.run(
114
- query,
115
- ProjectId=project_id,
116
- ClusterSelfLink=cluster["selfLink"],
117
- ClusterCreateTime=cluster["createTime"],
118
- ClusterName=cluster["name"],
119
- ClusterDescription=cluster.get("description"),
120
- ClusterLoggingService=cluster.get("loggingService"),
121
- ClusterMonitoringService=cluster.get("monitoringService"),
122
- ClusterNetwork=cluster.get("network"),
123
- ClusterSubnetwork=cluster.get("subnetwork"),
124
- ClusterIPv4Cidr=cluster.get("clusterIpv4Cidr"),
125
- ClusterZone=cluster.get("zone"),
126
- ClusterLocation=cluster.get("location"),
127
- ClusterEndpoint=cluster.get("endpoint"),
128
- ClusterInitialVersion=cluster.get("initialClusterVersion"),
129
- ClusterMasterVersion=cluster.get("currentMasterVersion"),
130
- ClusterStatus=cluster.get("status"),
131
- ClusterServicesIPv4Cidr=cluster.get("servicesIpv4Cidr"),
132
- ClusterDatabaseEncryption=cluster.get("databaseEncryption", {}).get(
133
- "state",
134
- ),
135
- ClusterNetworkPolicy=_process_network_policy(cluster),
136
- ClusterMasterAuthorizedNetworks=cluster.get(
137
- "masterAuthorizedNetworksConfig",
138
- {},
139
- ).get("enabled"),
140
- ClusterAbac=cluster.get("legacyAbac", {}).get("enabled"),
141
- ClusterShieldedNodes=cluster.get("shieldedNodes", {}).get("enabled"),
142
- ClusterPrivateNodes=cluster.get("privateClusterConfig", {}).get(
143
- "enablePrivateNodes",
144
- ),
145
- ClusterPrivateEndpointEnabled=cluster.get("privateClusterConfig", {}).get(
146
- "enablePrivateEndpoint",
147
- ),
148
- ClusterPrivateEndpoint=cluster.get("privateClusterConfig", {}).get(
149
- "privateEndpoint",
150
- ),
151
- ClusterPublicEndpoint=cluster.get("privateClusterConfig", {}).get(
152
- "publicEndpoint",
153
- ),
154
- ClusterMasterUsername=cluster.get("masterAuth", {}).get("username"),
155
- ClusterMasterPassword=cluster.get("masterAuth", {}).get("password"),
156
- gcp_update_tag=gcp_update_tag,
157
- )
67
+ if not clusters:
68
+ return
69
+
70
+ load(
71
+ neo4j_session,
72
+ GCPGKEClusterSchema(),
73
+ clusters,
74
+ lastupdated=gcp_update_tag,
75
+ PROJECT_ID=project_id,
76
+ )
158
77
 
159
78
 
160
79
  def _process_network_policy(cluster: Dict) -> bool:
@@ -175,21 +94,10 @@ def cleanup_gke_clusters(
175
94
  common_job_parameters: Dict,
176
95
  ) -> None:
177
96
  """
178
- Delete out-of-date GCP GKE Clusters nodes and relationships
179
-
180
- :type neo4j_session: The Neo4j session object
181
- :param neo4j_session: The Neo4j session
182
-
183
- :type common_job_parameters: dict
184
- :param common_job_parameters: Dictionary of other job parameters to pass to Neo4j
185
-
186
- :rtype: NoneType
187
- :return: Nothing
97
+ Scoped cleanup for GKE clusters based on the project sub-resource relationship.
188
98
  """
189
- run_cleanup_job(
190
- "gcp_gke_cluster_cleanup.json",
99
+ GraphJob.from_node_schema(GCPGKEClusterSchema(), common_job_parameters).run(
191
100
  neo4j_session,
192
- common_job_parameters,
193
101
  )
194
102
 
195
103
 
@@ -222,8 +130,59 @@ def sync_gke_clusters(
222
130
  :rtype: NoneType
223
131
  :return: Nothing
224
132
  """
225
- logger.info("Syncing Compute objects for project %s.", project_id)
133
+ logger.info("Syncing GKE clusters for project %s.", project_id)
226
134
  gke_res = get_gke_clusters(container, project_id)
227
135
  load_gke_clusters(neo4j_session, gke_res, project_id, gcp_update_tag)
228
- # TODO scope the cleanup to the current project - https://github.com/cartography-cncf/cartography/issues/381
229
136
  cleanup_gke_clusters(neo4j_session, common_job_parameters)
137
+
138
+
139
+ def transform_gke_clusters(api_result: Dict[str, Any]) -> List[Dict[str, Any]]:
140
+ """
141
+ Transform GKE API response into a list of dicts suitable for the data model loader.
142
+ """
143
+ result: List[Dict[str, Any]] = []
144
+ for c in api_result.get("clusters", []):
145
+ transformed: Dict[str, Any] = {
146
+ # Required fields
147
+ "id": c["selfLink"],
148
+ "self_link": c["selfLink"],
149
+ "name": c["name"],
150
+ "created_at": c.get("createTime"),
151
+ # Optional fields
152
+ "description": c.get("description"),
153
+ "logging_service": c.get("loggingService"),
154
+ "monitoring_service": c.get("monitoringService"),
155
+ "network": c.get("network"),
156
+ "subnetwork": c.get("subnetwork"),
157
+ "cluster_ipv4cidr": c.get("clusterIpv4Cidr"),
158
+ "zone": c.get("zone"),
159
+ "location": c.get("location"),
160
+ "endpoint": c.get("endpoint"),
161
+ "initial_version": c.get("initialClusterVersion"),
162
+ "current_master_version": c.get("currentMasterVersion"),
163
+ "status": c.get("status"),
164
+ "services_ipv4cidr": c.get("servicesIpv4Cidr"),
165
+ "database_encryption": (c.get("databaseEncryption", {}) or {}).get("state"),
166
+ "network_policy": _process_network_policy(c),
167
+ "master_authorized_networks": (
168
+ c.get("masterAuthorizedNetworksConfig", {}) or {}
169
+ ).get("enabled"),
170
+ "legacy_abac": (c.get("legacyAbac", {}) or {}).get("enabled"),
171
+ "shielded_nodes": (c.get("shieldedNodes", {}) or {}).get("enabled"),
172
+ "private_nodes": (c.get("privateClusterConfig", {}) or {}).get(
173
+ "enablePrivateNodes"
174
+ ),
175
+ "private_endpoint_enabled": (c.get("privateClusterConfig", {}) or {}).get(
176
+ "enablePrivateEndpoint"
177
+ ),
178
+ "private_endpoint": (c.get("privateClusterConfig", {}) or {}).get(
179
+ "privateEndpoint"
180
+ ),
181
+ "public_endpoint": (c.get("privateClusterConfig", {}) or {}).get(
182
+ "publicEndpoint"
183
+ ),
184
+ "masterauth_username": (c.get("masterAuth", {}) or {}).get("username"),
185
+ "masterauth_password": (c.get("masterAuth", {}) or {}).get("password"),
186
+ }
187
+ result.append(transformed)
188
+ return result