Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/c7n_gcp/resources/gke.py: 62%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

123 statements  

1# Copyright The Cloud Custodian Authors. 

2# SPDX-License-Identifier: Apache-2.0 

3import re 

4 

5from c7n_gcp.provider import resources 

6from c7n_gcp.query import (QueryResourceManager, TypeInfo, ChildTypeInfo, 

7 ChildResourceManager) 

8from c7n.utils import type_schema, local_session 

9from c7n_gcp.actions import MethodAction 

10from c7n_gcp.utils import get_firewall_port_ranges 

11 

12from c7n.filters import ValueFilter 

13 

14 

15@resources.register('gke-cluster') 

16class KubernetesCluster(QueryResourceManager): 

17 """GCP resource: 

18 https://cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.locations.clusters 

19 """ 

20 

21 class resource_type(TypeInfo): 

22 service = 'container' 

23 version = 'v1' 

24 component = 'projects.locations.clusters' 

25 enum_spec = ('list', 'clusters[]', None) 

26 scope = 'project' 

27 scope_key = 'parent' 

28 scope_template = "projects/{}/locations/-" 

29 name = id = "name" 

30 default_report_fields = [ 

31 'name', 'description', 'status', 'currentMasterVersion', 'currentNodeVersion', 

32 'currentNodeCount', 'location'] 

33 asset_type = 'container.googleapis.com/Cluster' 

34 scc_type = 'google.container.Cluster' 

35 metric_key = 'resource.labels.cluster_name' 

36 urn_component = 'cluster' 

37 labels = True 

38 labels_op = 'setResourceLabels' 

39 urn_zonal = True 

40 

41 @staticmethod 

42 def get(client, resource_info): 

43 return client.execute_query( 

44 'get', verb_arguments={ 

45 'name': 'projects/{}/locations/{}/clusters/{}'.format( 

46 resource_info['project_id'], 

47 resource_info['location'], 

48 resource_info['cluster_name'])}) 

49 

50 @staticmethod 

51 def get_label_params(resource, all_labels): 

52 location_str = "locations" 

53 if resource['selfLink'].find(location_str) < 0: 

54 location_str = "zones" 

55 path_param_re = re.compile( 

56 "%s%s%s" % ( 

57 '.*?/projects/(.*?)/', location_str, '/(.*?)/clusters/(.*)' 

58 ) 

59 ) 

60 project, zone, cluster_name = path_param_re.match( 

61 resource['selfLink']).groups() 

62 return {'name': "%s%s%s%s%s%s" % ( 

63 'projects/', project, '/locations/', zone, '/clusters/', cluster_name), 

64 'body': { 

65 'resourceLabels': all_labels, 

66 'labelFingerprint': resource['labelFingerprint'] 

67 }} 

68 

69 @classmethod 

70 def refresh(cls, client, resource): 

71 project_id = resource['selfLink'].split("/")[5] 

72 return cls.get( 

73 client, 

74 { 

75 'project_id': project_id, 

76 'location': resource['zone'], 

77 'cluster_name': resource['name'] 

78 } 

79 ) 

80 

81 def augment(self, resources): 

82 if not resources: 

83 return [] 

84 for r in resources: 

85 if r.get('resourceLabels'): 

86 r['labels'] = r['resourceLabels'] 

87 return resources 

88 

89 

90@KubernetesCluster.filter_registry.register('effective-firewall') 

91class EffectiveFirewall(ValueFilter): 

92 """Filters gke clusters by their effective firewall rules. 

93 See `getEffectiveFirewalls 

94 https://cloud.google.com/workflows/docs/reference/googleapis/compute/v1/networks/getEffectiveFirewalls`_ 

95 for valid fields. 

96 

97 :example: 

98 

99 Filter all gke clusters that have a firewall rule that allows public 

100 access 

101 

102 .. code-block:: yaml 

103 

104 policies: 

105 - name: find-publicly-accessable-clusters 

106 resource: gcp.gke-cluster 

107 filters: 

108 - type: effective-firewall 

109 key: sourceRanges[] 

110 op: contains 

111 value: "0.0.0.0/0" 

112 """ 

113 

114 schema = type_schema('effective-firewall', rinherit=ValueFilter.schema) 

115 permissions = ('compute.instances.getEffectiveFirewalls',) 

116 annotation_key = "c7n:firewall" 

117 

118 def get_firewalls(self, client, p, r): 

119 if self.annotation_key not in r: 

120 firewalls = client.execute_command('getEffectiveFirewalls', 

121 verb_arguments={'project': p, 'network': r['network']}).get('firewalls', []) 

122 

123 r[self.annotation_key] = get_firewall_port_ranges(firewalls) 

124 return super(EffectiveFirewall, self).process(r[self.annotation_key], None) 

125 

126 def process(self, resources, event=None): 

127 session = local_session(self.manager.session_factory) 

128 project = session.get_default_project() 

129 client = session.client( 

130 "compute", "v1", "networks" 

131 ) 

132 resource_list = [r for r in resources 

133 if self.get_firewalls(client, project, r)] 

134 return resource_list 

135 

136 

137@resources.register('gke-nodepool') 

138class KubernetesClusterNodePool(ChildResourceManager): 

139 """GCP resource: 

140 https://cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.zones.clusters.nodePools 

141 """ 

142 

143 def _get_parent_resource_info(self, child_instance): 

144 project_param_re = re.compile( 

145 '.*?/projects/(.*?)/zones/(.*?)/clusters/(.*?)/nodePools/(.*?)' 

146 ) 

147 parent_values = re.match(project_param_re, child_instance['selfLink']).groups() 

148 parent_info = dict( 

149 zip(('project_id', 'location', 'cluster_name', 'node_name'), parent_values) 

150 ) 

151 

152 return parent_info 

153 

154 def _get_child_enum_args(self, parent_instance): 

155 return { 

156 'parent': 'projects/{}/locations/{}/clusters/{}'.format( 

157 local_session(self.session_factory).get_default_project(), 

158 parent_instance['location'], 

159 parent_instance['name'] 

160 ) 

161 } 

162 

163 class resource_type(ChildTypeInfo): 

164 service = 'container' 

165 version = 'v1' 

166 component = 'projects.locations.clusters.nodePools' 

167 enum_spec = ('list', 'nodePools[]', None) 

168 scope = 'global' 

169 name = id = 'name' 

170 parent_spec = {'resource': 'gke-cluster'} 

171 asset_type = 'container.googleapis.com/NodePool' 

172 default_report_fields = ['name', 'status', 'version'] 

173 permissions = ('container.nodes.list',) 

174 urn_component = 'cluster-node-pool' 

175 urn_zonal = True 

176 

177 @staticmethod 

178 def get(client, resource_info): 

179 cluster_name = resource_info['cluster_name'] 

180 name = re.match( 

181 r".*{}-(.*)-[^-]+-[^-]?".format(cluster_name), 

182 resource_info['resourceName']).group(1) 

183 

184 return client.execute_command( 

185 'get', verb_arguments={ 

186 'name': 'projects/{}/locations/{}/clusters/{}/nodePools/{}'.format( 

187 resource_info['project_id'], 

188 resource_info['location'], 

189 resource_info['cluster_name'], 

190 name)} 

191 ) 

192 

193 @classmethod 

194 def _get_location(cls, resource): 

195 "Get the region from the parent - the cluster" 

196 return super()._get_location(cls.get_parent(resource)) 

197 

198 

199@KubernetesCluster.filter_registry.register('server-config') 

200@KubernetesClusterNodePool.filter_registry.register('server-config') 

201class ServerConfig(ValueFilter): 

202 """Filters kubernetes clusters or nodepools by their server config. 

203 See `getServerConfig 

204 https://cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.locations/getServerConfig` 

205 for valid fields. 

206 

207 :example: 

208 

209 Filter all clusters that is not running a supported version 

210 

211 .. code-block:: yaml 

212 

213 policies: 

214 - name: find-unsupported-cluster-version 

215 resource: gcp.gke-cluster 

216 filters: 

217 - type: server-config 

218 key: contains(serverConfig.validMasterVersions, resource.currentMasterVersion) 

219 value: false 

220 

221 Filter all nodepools that is not running a supported version 

222 

223 .. code-block:: yaml 

224 

225 policies: 

226 - name: find-unsupported-cluster-nodepools-version 

227 resource: gcp.gke-nodepool 

228 filters: 

229 - type: server-config 

230 key: contains(serverConfig.validNodeVersions, resource.version) 

231 value: false 

232 """ 

233 

234 schema = type_schema('server-config', rinherit=ValueFilter.schema) 

235 permissions = ('container.nodes.get', 'container.clusters.get') 

236 annotation_key = "c7n:config" 

237 

238 def _get_location(self, r): 

239 return r["location"] if "location" in r else r['selfLink'].split('/')[-5] 

240 

241 def get_config(self, client, project, resource): 

242 if self.annotation_key in resource: 

243 return 

244 location = self._get_location(resource) 

245 resource[self.annotation_key] = client.execute_command( 

246 'getServerConfig', verb_arguments={ 

247 'name': 'projects/{}/locations/{}'.format(project, location)} 

248 ) 

249 

250 def __call__(self, r): 

251 return super().__call__({"serverConfig": r[self.annotation_key], "resource": r}) 

252 

253 def process(self, resources, event=None): 

254 session = local_session(self.manager.session_factory) 

255 project = session.get_default_project() 

256 client = session.client("container", "v1", "projects.locations") 

257 for r in resources: 

258 self.get_config(client, project, r) 

259 return super().process(resources) 

260 

261 

262@KubernetesCluster.action_registry.register('delete') 

263class Delete(MethodAction): 

264 """Action to delete GKE clusters 

265 

266 It is recommended to use a filter to avoid unwanted deletion of GKE clusters 

267 

268 :example: 

269 

270 .. code-block:: yaml 

271 

272 policies: 

273 - name: gcp-delete-testing-gke-clusters 

274 resource: gcp.gke-cluster 

275 filters: 

276 - type: value 

277 key: name 

278 op: regex 

279 value: '^(test-|demo-)*' 

280 actions: 

281 - type: delete 

282 """ 

283 

284 schema = type_schema('delete') 

285 method_spec = {'op': 'delete'} 

286 

287 def get_resource_params(self, model, resource_info): 

288 project = local_session(self.manager.source.query.session_factory).get_default_project() 

289 

290 return {'name': 'projects/{}/locations/{}/clusters/{}'.format( 

291 project, 

292 resource_info['location'], 

293 resource_info['name'])}