Skip to content

Commit ef06c69

Browse files
authored
Merge pull request #7796 from yuxiang-zhang/fix-kubeconfig-validations
Fix kubeconfig validations
2 parents 0cde804 + d44e794 commit ef06c69

File tree

6 files changed

+180
-49
lines changed

6 files changed

+180
-49
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "bugfix",
3+
"category": "eks",
4+
"description": "Fix eks kubeconfig validations closes `#6564 <https://github.com/aws/aws-cli/issues/6564>`__, fixes `#4843 <https://github.com/aws/aws-cli/issues/4843>`__, fixes `#5532 <https://github.com/aws/aws-cli/issues/5532>`__"
5+
}

awscli/customizations/eks/kubeconfig.py

Lines changed: 47 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ def has_cluster(self, name):
6262
Return true if this kubeconfig contains an entry
6363
For the passed cluster name.
6464
"""
65-
if 'clusters' not in self.content:
65+
if self.content.get('clusters') is None:
6666
return False
6767
return name in [cluster['name']
68-
for cluster in self.content['clusters']]
68+
for cluster in self.content['clusters'] if 'name' in cluster]
6969

7070

7171
class KubeconfigValidator(object):
@@ -83,7 +83,7 @@ def validate_config(self, config):
8383
"""
8484
if not isinstance(config, Kubeconfig):
8585
raise KubeconfigCorruptedError("Internal error: "
86-
"Not a Kubeconfig object.")
86+
f"Not a {Kubeconfig}.")
8787
self._validate_config_types(config)
8888
self._validate_list_entry_types(config)
8989

@@ -96,18 +96,14 @@ def _validate_config_types(self, config):
9696
:type config: Kubeconfig
9797
"""
9898
if not isinstance(config.content, dict):
99-
raise KubeconfigCorruptedError("Content not a dictionary.")
99+
raise KubeconfigCorruptedError(f"Content not a {dict}.")
100100
for key, value in self._validation_content.items():
101101
if (key in config.content and
102102
config.content[key] is not None and
103103
not isinstance(config.content[key], type(value))):
104104
raise KubeconfigCorruptedError(
105-
"{0} is wrong type:{1} "
106-
"(Should be {2})".format(
107-
key,
108-
type(config.content[key]),
109-
type(value)
110-
)
105+
f"{key} is wrong type: {type(config.content[key])} "
106+
f"(Should be {type(value)})"
111107
)
112108

113109
def _validate_list_entry_types(self, config):
@@ -124,14 +120,14 @@ def _validate_list_entry_types(self, config):
124120
for element in config.content[key]:
125121
if not isinstance(element, OrderedDict):
126122
raise KubeconfigCorruptedError(
127-
"Entry in {0} not a dictionary.".format(key))
123+
f"Entry in {key} not a {dict}. ")
128124

129125

130126
class KubeconfigLoader(object):
131-
def __init__(self, validator=None):
127+
def __init__(self, validator = None):
132128
if validator is None:
133-
validator = KubeconfigValidator()
134-
self._validator = validator
129+
validator=KubeconfigValidator()
130+
self._validator=validator
135131

136132
def load_kubeconfig(self, path):
137133
"""
@@ -152,18 +148,18 @@ def load_kubeconfig(self, path):
152148
"""
153149
try:
154150
with open(path, "r") as stream:
155-
loaded_content = ordered_yaml_load(stream)
151+
loaded_content=ordered_yaml_load(stream)
156152
except IOError as e:
157153
if e.errno == errno.ENOENT:
158-
loaded_content = None
154+
loaded_content=None
159155
else:
160156
raise KubeconfigInaccessableError(
161-
"Can't open kubeconfig for reading: {0}".format(e))
157+
f"Can't open kubeconfig for reading: {e}")
162158
except yaml.YAMLError as e:
163159
raise KubeconfigCorruptedError(
164-
"YamlError while loading kubeconfig: {0}".format(e))
160+
f"YamlError while loading kubeconfig: {e}")
165161

166-
loaded_config = Kubeconfig(path, loaded_content)
162+
loaded_config=Kubeconfig(path, loaded_content)
167163
self._validator.validate_config(loaded_config)
168164

169165
return loaded_config
@@ -181,14 +177,14 @@ def write_kubeconfig(self, config):
181177
:raises KubeconfigInaccessableError: if the kubeconfig
182178
can't be opened for writing
183179
"""
184-
directory = os.path.dirname(config.path)
180+
directory=os.path.dirname(config.path)
185181

186182
try:
187183
os.makedirs(directory)
188184
except OSError as e:
189185
if e.errno != errno.EEXIST:
190186
raise KubeconfigInaccessableError(
191-
"Can't create directory for writing: {0}".format(e))
187+
f"Can't create directory for writing: {e}")
192188
try:
193189
with os.fdopen(
194190
os.open(
@@ -199,42 +195,44 @@ def write_kubeconfig(self, config):
199195
ordered_yaml_dump(config.content, stream)
200196
except (IOError, OSError) as e:
201197
raise KubeconfigInaccessableError(
202-
"Can't open kubeconfig for writing: {0}".format(e))
198+
f"Can't open kubeconfig for writing: {e}")
203199

204200

205201
class KubeconfigAppender(object):
206-
def insert_entry(self, config, key, entry):
202+
def insert_entry(self, config, key, new_entry):
207203
"""
208-
Insert entry into the array at content[key]
204+
Insert entry into the entries list at content[key]
209205
Overwrite an existing entry if they share the same name
210206
211207
:param config: The kubeconfig to insert an entry into
212208
:type config: Kubeconfig
213209
"""
214-
if key not in config.content:
215-
config.content[key] = []
216-
array = config.content[key]
217-
if not isinstance(array, list):
218-
raise KubeconfigError("Tried to insert into {0},"
219-
"which is a {1} "
220-
"not a {2}".format(key,
221-
type(array),
222-
list))
223-
found = False
224-
for counter, existing_entry in enumerate(array):
225-
if "name" in existing_entry and\
226-
"name" in entry and\
227-
existing_entry["name"] == entry["name"]:
228-
array[counter] = entry
229-
found = True
230-
231-
if not found:
232-
array.append(entry)
233-
234-
config.content[key] = array
210+
entries=self._setdefault_existing_entries(config, key)
211+
same_name_index=self._index_same_name(entries, new_entry)
212+
if same_name_index is None:
213+
entries.append(new_entry)
214+
else:
215+
entries[same_name_index]=new_entry
235216
return config
236217

237-
def _make_context(self, cluster, user, alias=None):
218+
def _setdefault_existing_entries(self, config, key):
219+
config.content[key]=config.content.get(key) or []
220+
entries=config.content[key]
221+
if not isinstance(entries, list):
222+
raise KubeconfigError(f"Tried to insert into {key}, "
223+
f"which is a {type(entries)} "
224+
f"not a {list}")
225+
return entries
226+
227+
def _index_same_name(self, entries, new_entry):
228+
if "name" in new_entry:
229+
name_to_search=new_entry["name"]
230+
for i, entry in enumerate(entries):
231+
if "name" in entry and entry["name"] == name_to_search:
232+
return i
233+
return None
234+
235+
def _make_context(self, cluster, user, alias = None):
238236
""" Generate a context to associate cluster and user with a given alias."""
239237
return OrderedDict([
240238
("context", OrderedDict([
@@ -244,7 +242,7 @@ def _make_context(self, cluster, user, alias=None):
244242
("name", alias or user["name"])
245243
])
246244

247-
def insert_cluster_user_pair(self, config, cluster, user, alias=None):
245+
def insert_cluster_user_pair(self, config, cluster, user, alias = None):
248246
"""
249247
Insert the passed cluster entry and user entry,
250248
then make a context to associate them
@@ -266,11 +264,11 @@ def insert_cluster_user_pair(self, config, cluster, user, alias=None):
266264
:return: The generated context
267265
:rtype: OrderedDict
268266
"""
269-
context = self._make_context(cluster, user, alias=alias)
267+
context=self._make_context(cluster, user, alias = alias)
270268
self.insert_entry(config, "clusters", cluster)
271269
self.insert_entry(config, "users", user)
272270
self.insert_entry(config, "contexts", context)
273271

274-
config.content["current-context"] = context["name"]
272+
config.content["current-context"]=context["name"]
275273

276274
return context
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
apiVersion: v1
2+
clusters: null
3+
contexts:
4+
- context:
5+
cluster: arn:aws:eks:us-west-2:111222333444:cluster/Existing
6+
user: arn:aws:eks:us-west-2:111222333444:cluster/Existing
7+
name: arn:aws:eks:us-west-2:111222333444:cluster/Existing
8+
current-context: arn:aws:eks:us-west-2:111222333444:cluster/Existing
9+
kind: Config
10+
preferences: {}
11+
users:
12+
- name: arn:aws:eks:us-west-2:111222333444:cluster/Existing
13+
user:
14+
exec:
15+
apiVersion: client.authentication.k8s.io/v1alpha1
16+
args:
17+
- --region
18+
- us-west-2
19+
- eks
20+
- get-token
21+
- --cluster-name
22+
- Existing
23+
command: aws
24+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
apiVersion: v1
2+
contexts: null
3+
clusters:
4+
- cluster:
5+
certificate-authority-data: DATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATA=
6+
server: https://existingEndpoint.eks.amazonaws.com
7+
name: arn:aws:eks:us-west-2:111222333444:cluster/Existing
8+
current-context: arn:aws:eks:us-west-2:111222333444:cluster/Existing
9+
kind: Config
10+
preferences: {}
11+
users:
12+
- name: arn:aws:eks:us-west-2:111222333444:cluster/Existing
13+
user:
14+
exec:
15+
apiVersion: client.authentication.k8s.io/v1alpha1
16+
args:
17+
- --region
18+
- us-west-2
19+
- eks
20+
- get-token
21+
- --cluster-name
22+
- Existing
23+
command: aws
24+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apiVersion: v1
2+
clusters:
3+
- cluster:
4+
certificate-authority-data: DATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATADATA=
5+
server: https://existingEndpoint.eks.amazonaws.com
6+
name: arn:aws:eks:us-west-2:111222333444:cluster/Existing
7+
contexts:
8+
- context:
9+
cluster: arn:aws:eks:us-west-2:111222333444:cluster/Existing
10+
user: arn:aws:eks:us-west-2:111222333444:cluster/Existing
11+
name: arn:aws:eks:us-west-2:111222333444:cluster/Existing
12+
current-context: arn:aws:eks:us-west-2:111222333444:cluster/Existing
13+
users: null
14+
kind: Config
15+
preferences: {}
16+

tests/unit/customizations/eks/test_kubeconfig.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,31 @@ def test_has_cluster_with_no_clusters(self):
6565
config = Kubeconfig(self._path, self._content)
6666
self.assertFalse(config.has_cluster("clustername"))
6767

68+
def test_has_cluster_with_none_clusters(self):
69+
self._content["clusters"] = None
70+
71+
config = Kubeconfig(self._path, self._content)
72+
self.assertFalse(config.has_cluster("anyclustername"))
73+
74+
def test_has_cluster_with_cluster_no_name(self):
75+
self._content["clusters"] = [
76+
OrderedDict([
77+
("cluster", None),
78+
("name", "clustername")
79+
]),
80+
OrderedDict([
81+
("cluster", None),
82+
("name", None),
83+
]),
84+
OrderedDict([
85+
("cluster", None),
86+
]),
87+
]
88+
89+
config = Kubeconfig(self._path, self._content)
90+
self.assertTrue(config.has_cluster("clustername"))
91+
self.assertFalse(config.has_cluster("othercluster"))
92+
6893

6994
class TestKubeconfigWriter(unittest.TestCase):
7095

@@ -257,6 +282,45 @@ def test_key_not_exist(self):
257282
cluster)
258283
self.assertDictEqual(updated.content, correct)
259284

285+
def test_key_none(self):
286+
initial = OrderedDict([
287+
("apiVersion", "v1"),
288+
("clusters", None),
289+
("contexts", []),
290+
("current-context", None),
291+
("kind", "Config"),
292+
("preferences", OrderedDict()),
293+
("users", [])
294+
])
295+
cluster = OrderedDict([
296+
("cluster", OrderedDict([
297+
("certificate-authority-data", "data"),
298+
("server", "endpoint")
299+
])),
300+
("name", "clustername")
301+
])
302+
correct = OrderedDict([
303+
("apiVersion", "v1"),
304+
("clusters", [
305+
OrderedDict([
306+
("cluster", OrderedDict([
307+
("certificate-authority-data", "data"),
308+
("server", "endpoint")
309+
])),
310+
("name", "clustername")
311+
])
312+
]),
313+
("contexts", []),
314+
("current-context", None),
315+
("kind", "Config"),
316+
("preferences", OrderedDict()),
317+
("users", []),
318+
])
319+
updated = self._appender.insert_entry(Kubeconfig(None, initial),
320+
"clusters",
321+
cluster)
322+
self.assertDictEqual(updated.content, correct)
323+
260324
def test_key_not_array(self):
261325
initial = OrderedDict([
262326
("apiVersion", "v1"),

0 commit comments

Comments
 (0)