We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
EKS update-config not working when updating cluster
I am not sure if by design. In case it isnt, executing:

```
aws eks update-kubeconfig --name dev-tools --kubeconfig ~/.kube/dev-tools
```

Yields into following the error message:
```
Cluster status not active
```
When the cluster is updating: `"status": "UPDATING",`.


**Steps:**
1. create cluster with older version
2. update-cluster to newer version
3. execute `aws eks update-kubeconfig`
4. error

------
After looking into the source code it looks like this is because it only checks for the status "Active". Everything else is not allowed.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `awscli/customizations/eks/update_kubeconfig.py`
Content:
```
1 # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License"). You
4 # may not use this file except in compliance with the License. A copy of
5 # the License is located at
6 #
7 # http://aws.amazon.com/apache2.0/
8 #
9 # or in the "license" file accompanying this file. This file is
10 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11 # ANY KIND, either express or implied. See the License for the specific
12 # language governing permissions and limitations under the License.
13 
14 import os
15 import logging
16 
17 from botocore.compat import OrderedDict
18 
19 from awscli.customizations.commands import BasicCommand
20 from awscli.customizations.utils import uni_print
21 from awscli.customizations.eks.exceptions import EKSClusterError
22 from awscli.customizations.eks.kubeconfig import (Kubeconfig,
23                                                   KubeconfigError,
24                                                   KubeconfigLoader,
25                                                   KubeconfigWriter,
26                                                   KubeconfigValidator,
27                                                   KubeconfigAppender)
28 from awscli.customizations.eks.ordered_yaml import ordered_yaml_dump
29 
30 LOG = logging.getLogger(__name__)
31 
32 DEFAULT_PATH = os.path.expanduser("~/.kube/config")
33 
34 # Use the endpoint for kubernetes 1.10
35 # To get the most recent endpoint we will need to
36 # Do a check on the cluster's version number
37 API_VERSION = "client.authentication.k8s.io/v1alpha1"
38 
39 class UpdateKubeconfigCommand(BasicCommand):
40     NAME = 'update-kubeconfig'
41 
42     DESCRIPTION = BasicCommand.FROM_FILE(
43         'eks',
44         'update-kubeconfig',
45         '_description.rst'
46     )
47 
48     ARG_TABLE = [
49         {
50             'name': 'name',
51             'help_text': ("The name of the cluster for which "
52                           "to create a kubeconfig entry. "
53                           "This cluster must exist in your account and in the "
54                           "specified or configured default Region "
55                           "for your AWS CLI installation."),
56             'required': True
57         },
58         {
59             'name': 'kubeconfig',
60             'help_text': ("Optionally specify a kubeconfig file to append "
61                           "with your configuration. "
62                           "By default, the configuration is written to the "
63                           "first file path in the KUBECONFIG "
64                           "environment variable (if it is set) "
65                           "or the default kubeconfig path (.kube/config) "
66                           "in your home directory."),
67             'required': False
68         },
69         {
70             'name': 'role-arn',
71             'help_text': ("To assume a role for cluster authentication, "
72                           "specify an IAM role ARN with this option. "
73                           "For example, if you created a cluster "
74                           "while assuming an IAM role, "
75                           "then you must also assume that role to "
76                           "connect to the cluster the first time."),
77             'required': False
78         },
79         {
80             'name': 'dry-run',
81             'action': 'store_true',
82             'default': False,
83             'help_text': ("Print the merged kubeconfig to stdout instead of "
84                           "writing it to the specified file."),
85             'required': False
86         },
87         {
88             'name': 'verbose',
89             'action': 'store_true',
90             'default': False,
91             'help_text': ("Print more detailed output "
92                           "when writing to the kubeconfig file, "
93                           "including the appended entries.")
94         },
95         {
96             'name': 'alias',
97             'help_text': ("Alias for the cluster context name. "
98                           "Defaults to match cluster ARN."),
99             'required': False
100         }
101     ]
102 
103     def _display_entries(self, entries):
104         """
105         Display entries in yaml format
106 
107         :param entries: a list of OrderedDicts to be printed
108         :type entries: list
109         """
110         uni_print("Entries:\n\n")
111         for entry in entries:
112             uni_print(ordered_yaml_dump(entry))
113             uni_print("\n")
114 
115     def _run_main(self, parsed_args, parsed_globals):
116         client = EKSClient(self._session,
117                            parsed_args.name,
118                            parsed_args.role_arn,
119                            parsed_globals)
120         new_cluster_dict = client.get_cluster_entry()
121         new_user_dict = client.get_user_entry()
122 
123         config_selector = KubeconfigSelector(
124             os.environ.get("KUBECONFIG", ""),
125             parsed_args.kubeconfig
126         )
127         config = config_selector.choose_kubeconfig(
128             new_cluster_dict["name"]
129         )
130         updating_existing = config.has_cluster(new_cluster_dict["name"])
131         appender = KubeconfigAppender()
132         new_context_dict = appender.insert_cluster_user_pair(config,
133                                                              new_cluster_dict,
134                                                              new_user_dict,
135                                                              parsed_args.alias)
136 
137         if parsed_args.dry_run:
138             uni_print(config.dump_content())
139         else:
140             writer = KubeconfigWriter()
141             writer.write_kubeconfig(config)
142 
143             if updating_existing:
144                 uni_print("Updated context {0} in {1}\n".format(
145                     new_context_dict["name"], config.path
146                 ))
147             else:
148                 uni_print("Added new context {0} to {1}\n".format(
149                     new_context_dict["name"], config.path
150                 ))
151 
152             if parsed_args.verbose:
153                 self._display_entries([
154                     new_context_dict,
155                     new_user_dict,
156                     new_cluster_dict
157                 ])
158 
159 
160 
161 class KubeconfigSelector(object):
162 
163     def __init__(self, env_variable, path_in, validator=None,
164                                               loader=None):
165         """
166         Parse KUBECONFIG into a list of absolute paths.
167         Also replace the empty list with DEFAULT_PATH
168 
169         :param env_variable: KUBECONFIG as a long string
170         :type env_variable: string
171 
172         :param path_in: The path passed in through the CLI
173         :type path_in: string or None
174         """
175         if validator is None:
176             validator = KubeconfigValidator()
177         self._validator = validator
178 
179         if loader is None:
180             loader = KubeconfigLoader(validator)
181         self._loader = loader
182 
183         if path_in is not None:
184             # Override environment variable
185             self._paths = [self._expand_path(path_in)]
186         else:
187             # Get the list of paths from the environment variable
188             if env_variable == "":
189                 env_variable = DEFAULT_PATH
190             self._paths = [self._expand_path(element)
191                            for element in env_variable.split(os.pathsep)
192                            if len(element.strip()) > 0]
193             if len(self._paths) == 0:
194                 self._paths = [DEFAULT_PATH]
195 
196     def choose_kubeconfig(self, cluster_name):
197         """
198         Choose which kubeconfig file to read from.
199         If name is already an entry in one of the $KUBECONFIG files,
200         choose that one.
201         Otherwise choose the first file.
202 
203         :param cluster_name: The name of the cluster which is going to be added
204         :type cluster_name: String
205 
206         :return: a chosen Kubeconfig based on above rules
207         :rtype: Kubeconfig
208         """
209         # Search for an existing entry to update
210         for candidate_path in self._paths:
211             try:
212                 loaded_config = self._loader.load_kubeconfig(candidate_path)
213 
214                 if loaded_config.has_cluster(cluster_name):
215                     LOG.debug("Found entry to update at {0}".format(
216                         candidate_path
217                     ))
218                     return loaded_config
219             except KubeconfigError as e:
220                 LOG.warning("Passing {0}:{1}".format(candidate_path, e))
221 
222         # No entry was found, use the first file in KUBECONFIG
223         #
224         # Note: This could raise KubeconfigErrors if paths[0] is corrupted
225         return self._loader.load_kubeconfig(self._paths[0])
226 
227     def _expand_path(self, path):
228         """ A helper to expand a path to a full absolute path. """
229         return os.path.abspath(os.path.expanduser(path))
230 
231 
232 class EKSClient(object):
233     def __init__(self, session, cluster_name, role_arn, parsed_globals=None):
234         self._session = session
235         self._cluster_name = cluster_name
236         self._role_arn = role_arn
237         self._cluster_description = None
238         self._globals = parsed_globals
239 
240     def _get_cluster_description(self):
241         """
242         Use an eks describe-cluster call to get the cluster description
243         Cache the response in self._cluster_description.
244         describe-cluster will only be called once.
245         """
246         if self._cluster_description is None:
247             if self._globals is None:
248                 client = self._session.create_client("eks")
249             else:
250                 client = self._session.create_client(
251                     "eks",
252                     region_name=self._globals.region,
253                     endpoint_url=self._globals.endpoint_url,
254                     verify=self._globals.verify_ssl
255                 )
256             full_description = client.describe_cluster(name=self._cluster_name)
257             self._cluster_description = full_description["cluster"]
258 
259             if "status" not in self._cluster_description:
260                 raise EKSClusterError("Cluster not found")
261             if self._cluster_description["status"] != "ACTIVE":
262                 raise EKSClusterError("Cluster status not active")
263 
264         return self._cluster_description
265 
266     def get_cluster_entry(self):
267         """
268         Return a cluster entry generated using
269         the previously obtained description.
270         """
271 
272         cert_data = self._get_cluster_description().get("certificateAuthority",
273                                                         {"data": ""})["data"]
274         endpoint = self._get_cluster_description().get("endpoint")
275         arn = self._get_cluster_description().get("arn")
276 
277         return OrderedDict([
278             ("cluster", OrderedDict([
279                 ("certificate-authority-data", cert_data),
280                 ("server", endpoint)
281             ])),
282             ("name", arn)
283         ])
284 
285     def get_user_entry(self):
286         """
287         Return a user entry generated using
288         the previously obtained description.
289         """
290 
291         region = self._get_cluster_description().get("arn").split(":")[3]
292 
293         generated_user = OrderedDict([
294             ("name", self._get_cluster_description().get("arn", "")),
295             ("user", OrderedDict([
296                 ("exec", OrderedDict([
297                     ("apiVersion", API_VERSION),
298                     ("args",
299                         [
300                             "--region",
301                             region,
302                             "eks",
303                             "get-token",
304                             "--cluster-name",
305                             self._cluster_name,
306                         ]),
307                     ("command", "aws")
308                 ]))
309             ]))
310         ])
311 
312         if self._role_arn is not None:
313             generated_user["user"]["exec"]["args"].extend([
314                 "--role",
315                 self._role_arn
316             ])
317 
318         if self._session.profile:
319             generated_user["user"]["exec"]["env"] = [OrderedDict([
320                 ("name", "AWS_PROFILE"),
321                 ("value", self._session.profile)
322             ])]
323 
324         return generated_user
325
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/awscli/customizations/eks/update_kubeconfig.py b/awscli/customizations/eks/update_kubeconfig.py
--- a/awscli/customizations/eks/update_kubeconfig.py
+++ b/awscli/customizations/eks/update_kubeconfig.py
@@ -258,8 +258,10 @@
 
             if "status" not in self._cluster_description:
                 raise EKSClusterError("Cluster not found")
-            if self._cluster_description["status"] != "ACTIVE":
-                raise EKSClusterError("Cluster status not active")
+            if self._cluster_description["status"] not in ["ACTIVE", "UPDATING"]:
+                raise EKSClusterError("Cluster status is {0}".format(
+                    self._cluster_description["status"]
+                ))
 
         return self._cluster_description

{"golden_diff": "diff --git a/awscli/customizations/eks/update_kubeconfig.py b/awscli/customizations/eks/update_kubeconfig.py\n--- a/awscli/customizations/eks/update_kubeconfig.py\n+++ b/awscli/customizations/eks/update_kubeconfig.py\n@@ -258,8 +258,10 @@\n \n             if \"status\" not in self._cluster_description:\n                 raise EKSClusterError(\"Cluster not found\")\n-            if self._cluster_description[\"status\"] != \"ACTIVE\":\n-                raise EKSClusterError(\"Cluster status not active\")\n+            if self._cluster_description[\"status\"] not in [\"ACTIVE\", \"UPDATING\"]:\n+                raise EKSClusterError(\"Cluster status is {0}\".format(\n+                    self._cluster_description[\"status\"]\n+                ))\n \n         return self._cluster_description\n", "issue": "EKS update-config not working when updating cluster\nI am not sure if by design. In case it isnt, executing:\r\n\r\n```\r\naws eks update-kubeconfig --name dev-tools --kubeconfig ~/.kube/dev-tools\r\n```\r\n\r\nYields into following the error message:\r\n```\r\nCluster status not active\r\n```\r\nWhen the cluster is updating: `\"status\": \"UPDATING\",`.\r\n\r\n\r\n**Steps:**\r\n1. create cluster with older version\r\n2. update-cluster to newer version\r\n3. execute `aws eks update-kubeconfig`\r\n4. error\r\n\r\n------\r\nAfter looking into the source code it looks like this is because it only checks for the status \"Active\". Everything else is not allowed.\n", "before_files": [{"content": "# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n# http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\n\nimport os\nimport logging\n\nfrom botocore.compat import OrderedDict\n\nfrom awscli.customizations.commands import BasicCommand\nfrom awscli.customizations.utils import uni_print\nfrom awscli.customizations.eks.exceptions import EKSClusterError\nfrom awscli.customizations.eks.kubeconfig import (Kubeconfig,\n                                                  KubeconfigError,\n                                                  KubeconfigLoader,\n                                                  KubeconfigWriter,\n                                                  KubeconfigValidator,\n                                                  KubeconfigAppender)\nfrom awscli.customizations.eks.ordered_yaml import ordered_yaml_dump\n\nLOG = logging.getLogger(__name__)\n\nDEFAULT_PATH = os.path.expanduser(\"~/.kube/config\")\n\n# Use the endpoint for kubernetes 1.10\n# To get the most recent endpoint we will need to\n# Do a check on the cluster's version number\nAPI_VERSION = \"client.authentication.k8s.io/v1alpha1\"\n\nclass UpdateKubeconfigCommand(BasicCommand):\n    NAME = 'update-kubeconfig'\n\n    DESCRIPTION = BasicCommand.FROM_FILE(\n        'eks',\n        'update-kubeconfig',\n        '_description.rst'\n    )\n\n    ARG_TABLE = [\n        {\n            'name': 'name',\n            'help_text': (\"The name of the cluster for which \"\n                          \"to create a kubeconfig entry. \"\n                          \"This cluster must exist in your account and in the \"\n                          \"specified or configured default Region \"\n                          \"for your AWS CLI installation.\"),\n            'required': True\n        },\n        {\n            'name': 'kubeconfig',\n            'help_text': (\"Optionally specify a kubeconfig file to append \"\n                          \"with your configuration. \"\n                          \"By default, the configuration is written to the \"\n                          \"first file path in the KUBECONFIG \"\n                          \"environment variable (if it is set) \"\n                          \"or the default kubeconfig path (.kube/config) \"\n                          \"in your home directory.\"),\n            'required': False\n        },\n        {\n            'name': 'role-arn',\n            'help_text': (\"To assume a role for cluster authentication, \"\n                          \"specify an IAM role ARN with this option. \"\n                          \"For example, if you created a cluster \"\n                          \"while assuming an IAM role, \"\n                          \"then you must also assume that role to \"\n                          \"connect to the cluster the first time.\"),\n            'required': False\n        },\n        {\n            'name': 'dry-run',\n            'action': 'store_true',\n            'default': False,\n            'help_text': (\"Print the merged kubeconfig to stdout instead of \"\n                          \"writing it to the specified file.\"),\n            'required': False\n        },\n        {\n            'name': 'verbose',\n            'action': 'store_true',\n            'default': False,\n            'help_text': (\"Print more detailed output \"\n                          \"when writing to the kubeconfig file, \"\n                          \"including the appended entries.\")\n        },\n        {\n            'name': 'alias',\n            'help_text': (\"Alias for the cluster context name. \"\n                          \"Defaults to match cluster ARN.\"),\n            'required': False\n        }\n    ]\n\n    def _display_entries(self, entries):\n        \"\"\"\n        Display entries in yaml format\n\n        :param entries: a list of OrderedDicts to be printed\n        :type entries: list\n        \"\"\"\n        uni_print(\"Entries:\\n\\n\")\n        for entry in entries:\n            uni_print(ordered_yaml_dump(entry))\n            uni_print(\"\\n\")\n\n    def _run_main(self, parsed_args, parsed_globals):\n        client = EKSClient(self._session,\n                           parsed_args.name,\n                           parsed_args.role_arn,\n                           parsed_globals)\n        new_cluster_dict = client.get_cluster_entry()\n        new_user_dict = client.get_user_entry()\n\n        config_selector = KubeconfigSelector(\n            os.environ.get(\"KUBECONFIG\", \"\"),\n            parsed_args.kubeconfig\n        )\n        config = config_selector.choose_kubeconfig(\n            new_cluster_dict[\"name\"]\n        )\n        updating_existing = config.has_cluster(new_cluster_dict[\"name\"])\n        appender = KubeconfigAppender()\n        new_context_dict = appender.insert_cluster_user_pair(config,\n                                                             new_cluster_dict,\n                                                             new_user_dict,\n                                                             parsed_args.alias)\n\n        if parsed_args.dry_run:\n            uni_print(config.dump_content())\n        else:\n            writer = KubeconfigWriter()\n            writer.write_kubeconfig(config)\n\n            if updating_existing:\n                uni_print(\"Updated context {0} in {1}\\n\".format(\n                    new_context_dict[\"name\"], config.path\n                ))\n            else:\n                uni_print(\"Added new context {0} to {1}\\n\".format(\n                    new_context_dict[\"name\"], config.path\n                ))\n\n            if parsed_args.verbose:\n                self._display_entries([\n                    new_context_dict,\n                    new_user_dict,\n                    new_cluster_dict\n                ])\n\n\n\nclass KubeconfigSelector(object):\n\n    def __init__(self, env_variable, path_in, validator=None,\n                                              loader=None):\n        \"\"\"\n        Parse KUBECONFIG into a list of absolute paths.\n        Also replace the empty list with DEFAULT_PATH\n\n        :param env_variable: KUBECONFIG as a long string\n        :type env_variable: string\n\n        :param path_in: The path passed in through the CLI\n        :type path_in: string or None\n        \"\"\"\n        if validator is None:\n            validator = KubeconfigValidator()\n        self._validator = validator\n\n        if loader is None:\n            loader = KubeconfigLoader(validator)\n        self._loader = loader\n\n        if path_in is not None:\n            # Override environment variable\n            self._paths = [self._expand_path(path_in)]\n        else:\n            # Get the list of paths from the environment variable\n            if env_variable == \"\":\n                env_variable = DEFAULT_PATH\n            self._paths = [self._expand_path(element)\n                           for element in env_variable.split(os.pathsep)\n                           if len(element.strip()) > 0]\n            if len(self._paths) == 0:\n                self._paths = [DEFAULT_PATH]\n\n    def choose_kubeconfig(self, cluster_name):\n        \"\"\"\n        Choose which kubeconfig file to read from.\n        If name is already an entry in one of the $KUBECONFIG files,\n        choose that one.\n        Otherwise choose the first file.\n\n        :param cluster_name: The name of the cluster which is going to be added\n        :type cluster_name: String\n\n        :return: a chosen Kubeconfig based on above rules\n        :rtype: Kubeconfig\n        \"\"\"\n        # Search for an existing entry to update\n        for candidate_path in self._paths:\n            try:\n                loaded_config = self._loader.load_kubeconfig(candidate_path)\n\n                if loaded_config.has_cluster(cluster_name):\n                    LOG.debug(\"Found entry to update at {0}\".format(\n                        candidate_path\n                    ))\n                    return loaded_config\n            except KubeconfigError as e:\n                LOG.warning(\"Passing {0}:{1}\".format(candidate_path, e))\n\n        # No entry was found, use the first file in KUBECONFIG\n        #\n        # Note: This could raise KubeconfigErrors if paths[0] is corrupted\n        return self._loader.load_kubeconfig(self._paths[0])\n\n    def _expand_path(self, path):\n        \"\"\" A helper to expand a path to a full absolute path. \"\"\"\n        return os.path.abspath(os.path.expanduser(path))\n\n\nclass EKSClient(object):\n    def __init__(self, session, cluster_name, role_arn, parsed_globals=None):\n        self._session = session\n        self._cluster_name = cluster_name\n        self._role_arn = role_arn\n        self._cluster_description = None\n        self._globals = parsed_globals\n\n    def _get_cluster_description(self):\n        \"\"\"\n        Use an eks describe-cluster call to get the cluster description\n        Cache the response in self._cluster_description.\n        describe-cluster will only be called once.\n        \"\"\"\n        if self._cluster_description is None:\n            if self._globals is None:\n                client = self._session.create_client(\"eks\")\n            else:\n                client = self._session.create_client(\n                    \"eks\",\n                    region_name=self._globals.region,\n                    endpoint_url=self._globals.endpoint_url,\n                    verify=self._globals.verify_ssl\n                )\n            full_description = client.describe_cluster(name=self._cluster_name)\n            self._cluster_description = full_description[\"cluster\"]\n\n            if \"status\" not in self._cluster_description:\n                raise EKSClusterError(\"Cluster not found\")\n            if self._cluster_description[\"status\"] != \"ACTIVE\":\n                raise EKSClusterError(\"Cluster status not active\")\n\n        return self._cluster_description\n\n    def get_cluster_entry(self):\n        \"\"\"\n        Return a cluster entry generated using\n        the previously obtained description.\n        \"\"\"\n\n        cert_data = self._get_cluster_description().get(\"certificateAuthority\",\n                                                        {\"data\": \"\"})[\"data\"]\n        endpoint = self._get_cluster_description().get(\"endpoint\")\n        arn = self._get_cluster_description().get(\"arn\")\n\n        return OrderedDict([\n            (\"cluster\", OrderedDict([\n                (\"certificate-authority-data\", cert_data),\n                (\"server\", endpoint)\n            ])),\n            (\"name\", arn)\n        ])\n\n    def get_user_entry(self):\n        \"\"\"\n        Return a user entry generated using\n        the previously obtained description.\n        \"\"\"\n\n        region = self._get_cluster_description().get(\"arn\").split(\":\")[3]\n\n        generated_user = OrderedDict([\n            (\"name\", self._get_cluster_description().get(\"arn\", \"\")),\n            (\"user\", OrderedDict([\n                (\"exec\", OrderedDict([\n                    (\"apiVersion\", API_VERSION),\n                    (\"args\",\n                        [\n                            \"--region\",\n                            region,\n                            \"eks\",\n                            \"get-token\",\n                            \"--cluster-name\",\n                            self._cluster_name,\n                        ]),\n                    (\"command\", \"aws\")\n                ]))\n            ]))\n        ])\n\n        if self._role_arn is not None:\n            generated_user[\"user\"][\"exec\"][\"args\"].extend([\n                \"--role\",\n                self._role_arn\n            ])\n\n        if self._session.profile:\n            generated_user[\"user\"][\"exec\"][\"env\"] = [OrderedDict([\n                (\"name\", \"AWS_PROFILE\"),\n                (\"value\", self._session.profile)\n            ])]\n\n        return generated_user\n", "path": "awscli/customizations/eks/update_kubeconfig.py"}], "after_files": [{"content": "# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n# http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\n\nimport os\nimport logging\n\nfrom botocore.compat import OrderedDict\n\nfrom awscli.customizations.commands import BasicCommand\nfrom awscli.customizations.utils import uni_print\nfrom awscli.customizations.eks.exceptions import EKSClusterError\nfrom awscli.customizations.eks.kubeconfig import (Kubeconfig,\n                                                  KubeconfigError,\n                                                  KubeconfigLoader,\n                                                  KubeconfigWriter,\n                                                  KubeconfigValidator,\n                                                  KubeconfigAppender)\nfrom awscli.customizations.eks.ordered_yaml import ordered_yaml_dump\n\nLOG = logging.getLogger(__name__)\n\nDEFAULT_PATH = os.path.expanduser(\"~/.kube/config\")\n\n# Use the endpoint for kubernetes 1.10\n# To get the most recent endpoint we will need to\n# Do a check on the cluster's version number\nAPI_VERSION = \"client.authentication.k8s.io/v1alpha1\"\n\nclass UpdateKubeconfigCommand(BasicCommand):\n    NAME = 'update-kubeconfig'\n\n    DESCRIPTION = BasicCommand.FROM_FILE(\n        'eks',\n        'update-kubeconfig',\n        '_description.rst'\n    )\n\n    ARG_TABLE = [\n        {\n            'name': 'name',\n            'help_text': (\"The name of the cluster for which \"\n                          \"to create a kubeconfig entry. \"\n                          \"This cluster must exist in your account and in the \"\n                          \"specified or configured default Region \"\n                          \"for your AWS CLI installation.\"),\n            'required': True\n        },\n        {\n            'name': 'kubeconfig',\n            'help_text': (\"Optionally specify a kubeconfig file to append \"\n                          \"with your configuration. \"\n                          \"By default, the configuration is written to the \"\n                          \"first file path in the KUBECONFIG \"\n                          \"environment variable (if it is set) \"\n                          \"or the default kubeconfig path (.kube/config) \"\n                          \"in your home directory.\"),\n            'required': False\n        },\n        {\n            'name': 'role-arn',\n            'help_text': (\"To assume a role for cluster authentication, \"\n                          \"specify an IAM role ARN with this option. \"\n                          \"For example, if you created a cluster \"\n                          \"while assuming an IAM role, \"\n                          \"then you must also assume that role to \"\n                          \"connect to the cluster the first time.\"),\n            'required': False\n        },\n        {\n            'name': 'dry-run',\n            'action': 'store_true',\n            'default': False,\n            'help_text': (\"Print the merged kubeconfig to stdout instead of \"\n                          \"writing it to the specified file.\"),\n            'required': False\n        },\n        {\n            'name': 'verbose',\n            'action': 'store_true',\n            'default': False,\n            'help_text': (\"Print more detailed output \"\n                          \"when writing to the kubeconfig file, \"\n                          \"including the appended entries.\")\n        },\n        {\n            'name': 'alias',\n            'help_text': (\"Alias for the cluster context name. \"\n                          \"Defaults to match cluster ARN.\"),\n            'required': False\n        }\n    ]\n\n    def _display_entries(self, entries):\n        \"\"\"\n        Display entries in yaml format\n\n        :param entries: a list of OrderedDicts to be printed\n        :type entries: list\n        \"\"\"\n        uni_print(\"Entries:\\n\\n\")\n        for entry in entries:\n            uni_print(ordered_yaml_dump(entry))\n            uni_print(\"\\n\")\n\n    def _run_main(self, parsed_args, parsed_globals):\n        client = EKSClient(self._session,\n                           parsed_args.name,\n                           parsed_args.role_arn,\n                           parsed_globals)\n        new_cluster_dict = client.get_cluster_entry()\n        new_user_dict = client.get_user_entry()\n\n        config_selector = KubeconfigSelector(\n            os.environ.get(\"KUBECONFIG\", \"\"),\n            parsed_args.kubeconfig\n        )\n        config = config_selector.choose_kubeconfig(\n            new_cluster_dict[\"name\"]\n        )\n        updating_existing = config.has_cluster(new_cluster_dict[\"name\"])\n        appender = KubeconfigAppender()\n        new_context_dict = appender.insert_cluster_user_pair(config,\n                                                             new_cluster_dict,\n                                                             new_user_dict,\n                                                             parsed_args.alias)\n\n        if parsed_args.dry_run:\n            uni_print(config.dump_content())\n        else:\n            writer = KubeconfigWriter()\n            writer.write_kubeconfig(config)\n\n            if updating_existing:\n                uni_print(\"Updated context {0} in {1}\\n\".format(\n                    new_context_dict[\"name\"], config.path\n                ))\n            else:\n                uni_print(\"Added new context {0} to {1}\\n\".format(\n                    new_context_dict[\"name\"], config.path\n                ))\n\n            if parsed_args.verbose:\n                self._display_entries([\n                    new_context_dict,\n                    new_user_dict,\n                    new_cluster_dict\n                ])\n\n\n\nclass KubeconfigSelector(object):\n\n    def __init__(self, env_variable, path_in, validator=None,\n                                              loader=None):\n        \"\"\"\n        Parse KUBECONFIG into a list of absolute paths.\n        Also replace the empty list with DEFAULT_PATH\n\n        :param env_variable: KUBECONFIG as a long string\n        :type env_variable: string\n\n        :param path_in: The path passed in through the CLI\n        :type path_in: string or None\n        \"\"\"\n        if validator is None:\n            validator = KubeconfigValidator()\n        self._validator = validator\n\n        if loader is None:\n            loader = KubeconfigLoader(validator)\n        self._loader = loader\n\n        if path_in is not None:\n            # Override environment variable\n            self._paths = [self._expand_path(path_in)]\n        else:\n            # Get the list of paths from the environment variable\n            if env_variable == \"\":\n                env_variable = DEFAULT_PATH\n            self._paths = [self._expand_path(element)\n                           for element in env_variable.split(os.pathsep)\n                           if len(element.strip()) > 0]\n            if len(self._paths) == 0:\n                self._paths = [DEFAULT_PATH]\n\n    def choose_kubeconfig(self, cluster_name):\n        \"\"\"\n        Choose which kubeconfig file to read from.\n        If name is already an entry in one of the $KUBECONFIG files,\n        choose that one.\n        Otherwise choose the first file.\n\n        :param cluster_name: The name of the cluster which is going to be added\n        :type cluster_name: String\n\n        :return: a chosen Kubeconfig based on above rules\n        :rtype: Kubeconfig\n        \"\"\"\n        # Search for an existing entry to update\n        for candidate_path in self._paths:\n            try:\n                loaded_config = self._loader.load_kubeconfig(candidate_path)\n\n                if loaded_config.has_cluster(cluster_name):\n                    LOG.debug(\"Found entry to update at {0}\".format(\n                        candidate_path\n                    ))\n                    return loaded_config\n            except KubeconfigError as e:\n                LOG.warning(\"Passing {0}:{1}\".format(candidate_path, e))\n\n        # No entry was found, use the first file in KUBECONFIG\n        #\n        # Note: This could raise KubeconfigErrors if paths[0] is corrupted\n        return self._loader.load_kubeconfig(self._paths[0])\n\n    def _expand_path(self, path):\n        \"\"\" A helper to expand a path to a full absolute path. \"\"\"\n        return os.path.abspath(os.path.expanduser(path))\n\n\nclass EKSClient(object):\n    def __init__(self, session, cluster_name, role_arn, parsed_globals=None):\n        self._session = session\n        self._cluster_name = cluster_name\n        self._role_arn = role_arn\n        self._cluster_description = None\n        self._globals = parsed_globals\n\n    def _get_cluster_description(self):\n        \"\"\"\n        Use an eks describe-cluster call to get the cluster description\n        Cache the response in self._cluster_description.\n        describe-cluster will only be called once.\n        \"\"\"\n        if self._cluster_description is None:\n            if self._globals is None:\n                client = self._session.create_client(\"eks\")\n            else:\n                client = self._session.create_client(\n                    \"eks\",\n                    region_name=self._globals.region,\n                    endpoint_url=self._globals.endpoint_url,\n                    verify=self._globals.verify_ssl\n                )\n            full_description = client.describe_cluster(name=self._cluster_name)\n            self._cluster_description = full_description[\"cluster\"]\n\n            if \"status\" not in self._cluster_description:\n                raise EKSClusterError(\"Cluster not found\")\n            if self._cluster_description[\"status\"] not in [\"ACTIVE\", \"UPDATING\"]:\n                raise EKSClusterError(\"Cluster status is {0}\".format(\n                    self._cluster_description[\"status\"]\n                ))\n\n        return self._cluster_description\n\n    def get_cluster_entry(self):\n        \"\"\"\n        Return a cluster entry generated using\n        the previously obtained description.\n        \"\"\"\n\n        cert_data = self._get_cluster_description().get(\"certificateAuthority\",\n                                                        {\"data\": \"\"})[\"data\"]\n        endpoint = self._get_cluster_description().get(\"endpoint\")\n        arn = self._get_cluster_description().get(\"arn\")\n\n        return OrderedDict([\n            (\"cluster\", OrderedDict([\n                (\"certificate-authority-data\", cert_data),\n                (\"server\", endpoint)\n            ])),\n            (\"name\", arn)\n        ])\n\n    def get_user_entry(self):\n        \"\"\"\n        Return a user entry generated using\n        the previously obtained description.\n        \"\"\"\n\n        region = self._get_cluster_description().get(\"arn\").split(\":\")[3]\n\n        generated_user = OrderedDict([\n            (\"name\", self._get_cluster_description().get(\"arn\", \"\")),\n            (\"user\", OrderedDict([\n                (\"exec\", OrderedDict([\n                    (\"apiVersion\", API_VERSION),\n                    (\"args\",\n                        [\n                            \"--region\",\n                            region,\n                            \"eks\",\n                            \"get-token\",\n                            \"--cluster-name\",\n                            self._cluster_name,\n                        ]),\n                    (\"command\", \"aws\")\n                ]))\n            ]))\n        ])\n\n        if self._role_arn is not None:\n            generated_user[\"user\"][\"exec\"][\"args\"].extend([\n                \"--role\",\n                self._role_arn\n            ])\n\n        if self._session.profile:\n            generated_user[\"user\"][\"exec\"][\"env\"] = [OrderedDict([\n                (\"name\", \"AWS_PROFILE\"),\n                (\"value\", self._session.profile)\n            ])]\n\n        return generated_user\n", "path": "awscli/customizations/eks/update_kubeconfig.py"}]}