18
18
import io
19
19
import os
20
20
import re
21
+ import sys
21
22
22
23
import yaml
23
24
24
25
NAME_RE = re .compile (r'[a-zA-z0-9_\.]+$' )
25
26
26
27
XGOOGLE = 'x-google-marketplace'
27
28
XTYPE_PASSWORD = 'GENERATED_PASSWORD'
29
+ XTYPE_SERVICE_ACCOUNT = 'SERVICE_ACCOUNT'
30
+ XTYPE_STORAGE_CLASS = 'STORAGE_CLASS'
28
31
29
32
30
33
class InvalidName (Exception ):
@@ -39,7 +42,21 @@ class InvalidSchema(Exception):
39
42
pass
40
43
41
44
42
- def read_values_to_dict (values_dir , codec , schema ):
45
+ def load_values (values_file ,
46
+ values_dir ,
47
+ values_dir_encoding ,
48
+ schema ):
49
+ if values_file == '-' :
50
+ return yaml .safe_load (sys .stdin .read ())
51
+ if values_file and os .path .isfile (values_file ):
52
+ with open (values_file , 'r' ) as f :
53
+ return yaml .safe_load (f .read ())
54
+ return _read_values_to_dict (values_dir ,
55
+ values_dir_encoding ,
56
+ schema )
57
+
58
+
59
+ def _read_values_to_dict (values_dir , codec , schema ):
43
60
"""Returns a dict constructed from files in values_dir."""
44
61
files = [f for f in os .listdir (values_dir )
45
62
if os .path .isfile (os .path .join (values_dir , f ))]
@@ -63,8 +80,8 @@ class Schema:
63
80
"""Wrapper class providing convenient access to a JSON schema."""
64
81
65
82
@staticmethod
66
- def load_yaml_file (filepath , encoding = 'utf_8' ):
67
- with io .open (filepath , 'r' , encoding = encoding ) as f :
83
+ def load_yaml_file (filepath ):
84
+ with io .open (filepath , 'r' ) as f :
68
85
d = yaml .load (f )
69
86
return Schema (d )
70
87
@@ -75,10 +92,16 @@ def load_yaml(yaml_str):
75
92
def __init__ (self , dictionary ):
76
93
self ._required = dictionary .get ('required' , [])
77
94
self ._properties = {
78
- k : SchemaProperty (k , v )
95
+ k : SchemaProperty (k , v , k in self . _required )
79
96
for k , v in dictionary .get ('properties' , {}).iteritems ()
80
97
}
81
98
99
+ bad_required_names = [x for x in self ._required
100
+ if x not in self ._properties ]
101
+ if bad_required_names :
102
+ raise InvalidSchema ('Undefined property names found in required: {}'
103
+ .format (', ' .join (bad_required_names )))
104
+
82
105
@property
83
106
def required (self ):
84
107
return self ._required
@@ -87,16 +110,23 @@ def required(self):
87
110
def properties (self ):
88
111
return self ._properties
89
112
113
+ def properties_matching (self , definition ):
114
+ return [v for k , v in self ._properties .iteritems ()
115
+ if v .matches_definition (definition )]
116
+
90
117
91
118
class SchemaProperty :
92
119
"""Wrapper class providing convenient access to a JSON schema property."""
93
120
94
- def __init__ (self , name , dictionary ):
121
+ def __init__ (self , name , dictionary , required ):
95
122
self ._name = name
96
123
self ._d = dictionary
124
+ self ._required = required
97
125
self ._default = dictionary .get ('default' , None )
98
126
self ._x = dictionary .get (XGOOGLE , None )
99
127
self ._password = None
128
+ self ._service_account = None
129
+ self ._storage_class = None
100
130
101
131
if not NAME_RE .match (name ):
102
132
raise InvalidSchema ('Invalid property name: {}' .format (name ))
@@ -130,11 +160,22 @@ def __init__(self, name, dictionary):
130
160
'base64' : d .get ('base64' , True ),
131
161
}
132
162
self ._password = SchemaXPassword (** spec )
163
+ elif xt == XTYPE_SERVICE_ACCOUNT :
164
+ d = self ._x .get ('serviceAccount' , {})
165
+ self ._service_account = SchemaXServiceAccount (d )
166
+ elif xt == XTYPE_STORAGE_CLASS :
167
+ d = self ._x .get ('storageClass' , {})
168
+ self ._storage_class = SchemaXStorageClass (d )
169
+
133
170
134
171
@property
135
172
def name (self ):
136
173
return self ._name
137
174
175
+ @property
176
+ def required (self ):
177
+ return self ._required
178
+
138
179
@property
139
180
def default (self ):
140
181
return self ._default
@@ -154,6 +195,14 @@ def xtype(self):
154
195
def password (self ):
155
196
return self ._password
156
197
198
+ @property
199
+ def service_account (self ):
200
+ return self ._service_account
201
+
202
+ @property
203
+ def storage_class (self ):
204
+ return self ._storage_class
205
+
157
206
def str_to_type (self , str_val ):
158
207
if self ._type == bool :
159
208
if str_val in {'true' , 'True' , 'yes' , 'Yes' }:
@@ -200,3 +249,49 @@ def __eq__(self, other):
200
249
['length' ,
201
250
'include_symbols' ,
202
251
'base64' ])
252
+
253
+
254
+ class SchemaXServiceAccount :
255
+ """Wrapper class providing convenient access to SERVICE_ACCOUNT property."""
256
+
257
+ def __init__ (self , dictionary ):
258
+ self ._roles = dictionary .get ('roles' , [])
259
+
260
+ def custom_role_rules (self ):
261
+ """Returns a list of rules for custom Roles."""
262
+ return [role .get ('rules' , [])
263
+ for role in self ._roles
264
+ if role ['type' ] == 'Role'
265
+ and role ['rulesType' ] == 'CUSTOM' ]
266
+
267
+ def custom_cluster_role_rules (self ):
268
+ """Returns a list of rules for custom ClusterRoles."""
269
+ return [role .get ('rules' , [])
270
+ for role in self ._roles
271
+ if role ['type' ] == 'ClusterRole'
272
+ and role ['rulesType' ] == 'CUSTOM' ]
273
+
274
+ def predefined_roles (self ):
275
+ """Returns a list of predefined Roles."""
276
+ return [role .get ('rulesFromRoleName' )
277
+ for role in self ._roles
278
+ if role ['type' ] == 'Role'
279
+ and role ['rulesType' ] == 'PREDEFINED' ]
280
+
281
+ def predefined_cluster_roles (self ):
282
+ """Returns a list of predefined ClusterRoles."""
283
+ return [role .get ('rulesFromRoleName' )
284
+ for role in self ._roles
285
+ if role ['type' ] == 'ClusterRole'
286
+ and role ['rulesType' ] == 'PREDEFINED' ]
287
+
288
+
289
+ class SchemaXStorageClass :
290
+ """Wrapper class providing convenient access to STORAGE_CLASS property"""
291
+
292
+ def __init__ (self , dictionary ):
293
+ self ._type = dictionary ['type' ]
294
+
295
+ @property
296
+ def ssd (self ):
297
+ return self ._type == 'SSD'
0 commit comments