Skip to content

Commit

Permalink
Merge branch 'release-1.25.72' into develop
Browse files Browse the repository at this point in the history
* release-1.25.72:
  Bumping version to 1.25.72
  Update changelog based on model updates
  Add support for cluster id
  • Loading branch information
aws-sdk-python-automation committed Sep 12, 2022
2 parents 112aaa4 + 2c5ca39 commit 6c0d168
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 27 deletions.
7 changes: 7 additions & 0 deletions .changes/1.25.72.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"category": "``eks``",
"description": "Adding support for local Amazon EKS clusters on Outposts",
"type": "api-change"
}
]
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
CHANGELOG
=========

1.25.72
=======

* api-change:``eks``: Adding support for local Amazon EKS clusters on Outposts


1.25.71
=======

Expand Down
2 changes: 1 addition & 1 deletion awscli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"""
import os

__version__ = '1.25.71'
__version__ = '1.25.72'

#
# Get our data path to be added to botocore's search path
Expand Down
57 changes: 37 additions & 20 deletions awscli/customizations/eks/get_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

from awscli.customizations.commands import BasicCommand
from awscli.customizations.utils import uni_print
from awscli.customizations.utils import validate_mutually_exclusive

AUTH_SERVICE = "sts"
AUTH_COMMAND = "GetCallerIdentity"
Expand Down Expand Up @@ -62,7 +63,7 @@

TOKEN_PREFIX = 'k8s-aws-v1.'

CLUSTER_NAME_HEADER = 'x-k8s-aws-id'
K8S_AWS_ID_HEADER = 'x-k8s-aws-id'


class GetTokenCommand(BasicCommand):
Expand All @@ -78,9 +79,9 @@ class GetTokenCommand(BasicCommand):
{
'name': 'cluster-name',
'help_text': (
"Specify the name of the Amazon EKS cluster to create a token for."
"Specify the name of the Amazon EKS cluster to create a token for. (Note: for local clusters on AWS Outposts, please use --cluster-id parameter)"
),
'required': True,
'required': False,
},
{
'name': 'role-arn',
Expand All @@ -93,6 +94,14 @@ class GetTokenCommand(BasicCommand):
),
'required': False,
},
{
'name': 'cluster-id',
# When EKS in-region cluster supports cluster-id, we will need to update this help text
'help_text': (
"Specify the id of the Amazon EKS cluster to create a token for. (Note: for local clusters on AWS Outposts only)"
),
'required': False,
},
]

def get_expiration_time(self):
Expand All @@ -106,7 +115,17 @@ def _run_main(self, parsed_args, parsed_globals):
sts_client = client_factory.get_sts_client(
region_name=parsed_globals.region, role_arn=parsed_args.role_arn
)
token = TokenGenerator(sts_client).get_token(parsed_args.cluster_name)

validate_mutually_exclusive(parsed_args, ['cluster_name'], ['cluster_id'])

if parsed_args.cluster_id:
identifier = parsed_args.cluster_id
elif parsed_args.cluster_name:
identifier = parsed_args.cluster_name
else:
return ValueError("Either parameter --cluster-name or --cluster-id must be specified.")

token = TokenGenerator(sts_client).get_token(identifier)

# By default STS signs the url for 15 minutes so we are creating a
# rfc3339 timestamp with expiration in 14 minutes as part of the token, which
Expand Down Expand Up @@ -193,18 +212,18 @@ class TokenGenerator(object):
def __init__(self, sts_client):
self._sts_client = sts_client

def get_token(self, cluster_name):
def get_token(self, k8s_aws_id):
"""Generate a presigned url token to pass to kubectl."""
url = self._get_presigned_url(cluster_name)
url = self._get_presigned_url(k8s_aws_id)
token = TOKEN_PREFIX + base64.urlsafe_b64encode(
url.encode('utf-8')
).decode('utf-8').rstrip('=')
return token

def _get_presigned_url(self, cluster_name):
def _get_presigned_url(self, k8s_aws_id):
return self._sts_client.generate_presigned_url(
'get_caller_identity',
Params={'ClusterName': cluster_name},
Params={K8S_AWS_ID_HEADER: k8s_aws_id},
ExpiresIn=URL_TIMEOUT,
HttpMethod='GET',
)
Expand All @@ -222,7 +241,7 @@ def get_sts_client(self, region_name=None, role_arn=None):
client_kwargs['aws_secret_access_key'] = creds['SecretAccessKey']
client_kwargs['aws_session_token'] = creds['SessionToken']
sts = self._session.create_client('sts', **client_kwargs)
self._register_cluster_name_handlers(sts)
self._register_k8s_aws_id_handlers(sts)
return sts

def _get_role_credentials(self, region_name, role_arn):
Expand All @@ -231,22 +250,20 @@ def _get_role_credentials(self, region_name, role_arn):
RoleArn=role_arn, RoleSessionName='EKSGetTokenAuth'
)['Credentials']

def _register_cluster_name_handlers(self, sts_client):
def _register_k8s_aws_id_handlers(self, sts_client):
sts_client.meta.events.register(
'provide-client-params.sts.GetCallerIdentity',
self._retrieve_cluster_name,
self._retrieve_k8s_aws_id,
)
sts_client.meta.events.register(
'before-sign.sts.GetCallerIdentity',
self._inject_cluster_name_header,
self._inject_k8s_aws_id_header,
)

def _retrieve_cluster_name(self, params, context, **kwargs):
if 'ClusterName' in params:
context['eks_cluster'] = params.pop('ClusterName')
def _retrieve_k8s_aws_id(self, params, context, **kwargs):
if K8S_AWS_ID_HEADER in params:
context[K8S_AWS_ID_HEADER] = params.pop(K8S_AWS_ID_HEADER)

def _inject_cluster_name_header(self, request, **kwargs):
if 'eks_cluster' in request.context:
request.headers[CLUSTER_NAME_HEADER] = request.context[
'eks_cluster'
]
def _inject_k8s_aws_id_header(self, request, **kwargs):
if K8S_AWS_ID_HEADER in request.context:
request.headers[K8S_AWS_ID_HEADER] = request.context[K8S_AWS_ID_HEADER]
15 changes: 12 additions & 3 deletions awscli/customizations/eks/update_kubeconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,17 @@ def get_user_entry(self):
Return a user entry generated using
the previously obtained description.
"""
cluster_description = self._get_cluster_description()
region = cluster_description.get("arn").split(":")[3]
outpost_config = cluster_description.get("outpostConfig")

region = self._get_cluster_description().get("arn").split(":")[3]
if outpost_config is None:
cluster_identification_parameter = "--cluster-name"
cluster_identification_value = self._cluster_name
else:
# If cluster contains outpostConfig, use id for identification
cluster_identification_parameter = "--cluster-id"
cluster_identification_value = cluster_description.get("id")

generated_user = OrderedDict([
("name", self._get_cluster_description().get("arn", "")),
Expand All @@ -302,8 +311,8 @@ def get_user_entry(self):
region,
"eks",
"get-token",
"--cluster-name",
self._cluster_name,
cluster_identification_parameter,
cluster_identification_value,
]),
("command", "aws"),
]))
Expand Down
2 changes: 1 addition & 1 deletion doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
# The short X.Y version.
version = '1.25.'
# The full version, including alpha/beta/rc tags.
release = '1.25.71'
release = '1.25.72'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ universal = 0

[metadata]
requires_dist =
botocore==1.27.70
botocore==1.27.71
docutils>=0.10,<0.17
s3transfer>=0.6.0,<0.7.0
PyYAML>=3.10,<5.5
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def find_version(*file_paths):


install_requires = [
'botocore==1.27.70',
'botocore==1.27.71',
'docutils>=0.10,<0.17',
's3transfer>=0.6.0,<0.7.0',
'PyYAML>=3.10,<5.5',
Expand Down
34 changes: 34 additions & 0 deletions tests/functional/eks/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,40 @@ def describe_cluster_response():
}
}

def describe_cluster_response_outpost_cluster():
"""Get an example describe_cluster call (For mocking)"""
return {
"cluster": {
"status": "ACTIVE",
"endpoint": "https://endpoint.amazonaws.com",
"name": EXAMPLE_NAME,
"certificateAuthority": {
"data": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpWR1Z6ZEdsdVp5QkVZWFJoRFFwVVpYTjBhVzVuSUVSaGRHRU5DbFJsYzNScGJtY2dSR0YwWVEwS2EzVmlaWEp1WlhSbGN6QWVGdzBLVkdWemRHbHVaeUJFWVhSaERRcFVaWE4wYVc1bklFUmhkR0ZWQkFNVERRcHJkV0psY201bGRHVnpNQUVpTUEwS1ZHVnpkR2x1WnlCRVlYUmhEUXBVWlhOMGFXNW5JRVJoZEdFTkNsUmxjM1JwYm1jZ1JHRjBZY3UvR1FnbmFTcDNZaHBDTWhGVVpYTjBhVzVuSUVSaGRHRXl3clZqeEpWNjNwNFVHRmpZdHdGR1drUldJVkV1VkdWemRHbHVaeUJFWVhSaGJzT0MxSVJiTDhPd0lpMVhiWGg2VkdWemRHbHVaeUJFWVhSaFpXVndTTk9VVUZKNmN5QWJaaFpnWVNkTUV3MEtGMVJsYzNScGJtY2dSR0YwWVFZRFZSMFBBUUVFQkFNQ0FsUmxjM1JwYm1jZ1JHRjBZUUV3RFFvR0NTcElEUXBVWlhOMGFXNW5JRVJoZEdGcEgxc1pPRTNMa3lrMU9DWUNHUloyTEZjM3paOCtHell3WEZSbGMzUnBibWNnUkdGMFlYMUR5NjFNMVlGV1AxWVRIMVJsYzNScGJtY2dSR0YwWVd0aE5oMVphM2dWUDBGaGNSWjdKaW9oZVc4N1JsUmxjM1JwYm1jZ1JHRjBZUVpIVHd4NE9IdzZmZz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t"
},
"roleArn": "arn:aws:iam::111222333444/eksRole",
"resourcesVpcConfig": {
"subnetIds": [
"subnet-00000000000000000",
"subnet-00000000000000001",
"subnet-00000000000000002"
],
"vpcId": "vpc-00000000000000000",
"securityGroupIds": [
"sg-00000000000000000"
]
},
"version": "1.10",
"arn": "arn:aws:eks:region:111222333444:cluster/" + EXAMPLE_NAME,
"createdAt": 1500000000.000,
"id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"outpostConfig": {
"outpostArns": [
"arn:aws:outposts:us-west-2:111222333444:outpost/op-00000000000000000"
],
}
}
}

def describe_cluster_no_status_response():
"""Get an example describe_cluster call (For mocking)"""
return {
Expand Down
30 changes: 30 additions & 0 deletions tests/unit/customizations/eks/test_update_kubeconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from awscli.customizations.eks.ordered_yaml import ordered_yaml_load
from tests.functional.eks.test_util import get_testdata
from tests.functional.eks.test_util import (describe_cluster_response,
describe_cluster_response_outpost_cluster,
describe_cluster_no_status_response,
describe_cluster_creating_response,
describe_cluster_deleting_response)
Expand Down Expand Up @@ -185,6 +186,25 @@ def setUp(self):
]))
])

self._correct_user_entry_outpost_cluster = OrderedDict([
("name", describe_cluster_response()["cluster"]["arn"]),
("user", OrderedDict([
("exec", OrderedDict([
("apiVersion", API_VERSION),
("args",
[
"--region",
"region",
"eks",
"get-token",
"--cluster-id",
"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
]),
("command", "aws")
]))
]))
])

self._mock_client = mock.Mock()
self._mock_client.describe_cluster.return_value =\
describe_cluster_response()
Expand Down Expand Up @@ -229,6 +249,16 @@ def test_get_user_entry(self):
)
self._session.create_client.assert_called_once_with("eks")

def test_get_user_entry_outpost_cluster(self):
self._mock_client.describe_cluster.return_value =\
describe_cluster_response_outpost_cluster()
self.assertEqual(self._client.get_user_entry(),
self._correct_user_entry_outpost_cluster)
self._mock_client.describe_cluster.assert_called_once_with(
name="ExampleCluster"
)
self._session.create_client.assert_called_once_with("eks")

def test_get_both(self):
self.assertEqual(self._client.get_cluster_entry(),
self._correct_cluster_entry)
Expand Down

0 comments on commit 6c0d168

Please sign in to comment.