Added redshift.get_cluster_credentials (#3611)
* Added redshift.get_cluster_credentials * Marked endpoint in list * Removed f string from tests * Python 2.7 compat changes * Fixed parameter retrieval * Formatting * Removed try/catch in favor of if * Changed to existing random_string util Co-authored-by: Andrea Amorosi <aamorosi@amazon.es>
This commit is contained in:
		
							parent
							
								
									d7f218bfec
								
							
						
					
					
						commit
						5a41866f71
					
				| @ -6721,7 +6721,7 @@ | |||||||
| 
 | 
 | ||||||
| ## redshift | ## redshift | ||||||
| <details> | <details> | ||||||
| <summary>28% implemented</summary> | <summary>29% implemented</summary> | ||||||
| 
 | 
 | ||||||
| - [ ] accept_reserved_node_exchange | - [ ] accept_reserved_node_exchange | ||||||
| - [ ] authorize_cluster_security_group_ingress | - [ ] authorize_cluster_security_group_ingress | ||||||
| @ -6789,7 +6789,7 @@ | |||||||
| - [X] disable_snapshot_copy | - [X] disable_snapshot_copy | ||||||
| - [ ] enable_logging | - [ ] enable_logging | ||||||
| - [X] enable_snapshot_copy | - [X] enable_snapshot_copy | ||||||
| - [ ] get_cluster_credentials | - [X] get_cluster_credentials | ||||||
| - [ ] get_reserved_node_exchange_offerings | - [ ] get_reserved_node_exchange_offerings | ||||||
| - [X] modify_cluster | - [X] modify_cluster | ||||||
| - [ ] modify_cluster_db_revision | - [ ] modify_cluster_db_revision | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ from boto3 import Session | |||||||
| from moto.compat import OrderedDict | from moto.compat import OrderedDict | ||||||
| from moto.core import BaseBackend, BaseModel, CloudFormationModel | from moto.core import BaseBackend, BaseModel, CloudFormationModel | ||||||
| from moto.core.utils import iso_8601_datetime_with_milliseconds | from moto.core.utils import iso_8601_datetime_with_milliseconds | ||||||
|  | from moto.utilities.utils import random_string | ||||||
| from moto.ec2 import ec2_backends | from moto.ec2 import ec2_backends | ||||||
| from .exceptions import ( | from .exceptions import ( | ||||||
|     ClusterAlreadyExistsFaultError, |     ClusterAlreadyExistsFaultError, | ||||||
| @ -941,6 +942,25 @@ class RedshiftBackend(BaseBackend): | |||||||
|         resource = self._get_resource_from_arn(resource_name) |         resource = self._get_resource_from_arn(resource_name) | ||||||
|         resource.delete_tags(tag_keys) |         resource.delete_tags(tag_keys) | ||||||
| 
 | 
 | ||||||
|  |     def get_cluster_credentials( | ||||||
|  |         self, cluster_identifier, db_user, auto_create, duration_seconds | ||||||
|  |     ): | ||||||
|  |         if duration_seconds < 900 or duration_seconds > 3600: | ||||||
|  |             raise InvalidParameterValueError( | ||||||
|  |                 "Token duration must be between 900 and 3600 seconds" | ||||||
|  |             ) | ||||||
|  |         expiration = datetime.datetime.now() + datetime.timedelta(0, duration_seconds) | ||||||
|  |         if cluster_identifier in self.clusters: | ||||||
|  |             user_prefix = "IAM:" if auto_create is False else "IAMA:" | ||||||
|  |             db_user = user_prefix + db_user | ||||||
|  |             return { | ||||||
|  |                 "DbUser": db_user, | ||||||
|  |                 "DbPassword": random_string(32), | ||||||
|  |                 "Expiration": expiration, | ||||||
|  |             } | ||||||
|  |         else: | ||||||
|  |             raise ClusterNotFoundError(cluster_identifier) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| redshift_backends = {} | redshift_backends = {} | ||||||
| for region in Session().get_available_regions("redshift"): | for region in Session().get_available_regions("redshift"): | ||||||
|  | |||||||
| @ -694,3 +694,24 @@ class RedshiftResponse(BaseResponse): | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
|  | 
 | ||||||
|  |     def get_cluster_credentials(self): | ||||||
|  |         cluster_identifier = self._get_param("ClusterIdentifier") | ||||||
|  |         db_user = self._get_param("DbUser") | ||||||
|  |         auto_create = self._get_bool_param("AutoCreate", False) | ||||||
|  |         duration_seconds = self._get_int_param("DurationSeconds", 900) | ||||||
|  | 
 | ||||||
|  |         cluster_credentials = self.redshift_backend.get_cluster_credentials( | ||||||
|  |             cluster_identifier, db_user, auto_create, duration_seconds | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         return self.get_response( | ||||||
|  |             { | ||||||
|  |                 "GetClusterCredentialsResponse": { | ||||||
|  |                     "GetClusterCredentialsResult": cluster_credentials, | ||||||
|  |                     "ResponseMetadata": { | ||||||
|  |                         "RequestId": "384ac68d-3775-11df-8963-01868b7c937a" | ||||||
|  |                     }, | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||||
| 
 | 
 | ||||||
|  | import time | ||||||
| import datetime | import datetime | ||||||
| 
 | 
 | ||||||
| import boto | import boto | ||||||
| @ -1487,3 +1488,107 @@ def test_resize_cluster(): | |||||||
|         ) |         ) | ||||||
|     ex.value.response["Error"]["Code"].should.equal("InvalidParameterValue") |     ex.value.response["Error"]["Code"].should.equal("InvalidParameterValue") | ||||||
|     ex.value.response["Error"]["Message"].should.contain("Invalid cluster type") |     ex.value.response["Error"]["Message"].should.contain("Invalid cluster type") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @mock_redshift | ||||||
|  | def test_get_cluster_credentials_non_existent_cluster(): | ||||||
|  |     client = boto3.client("redshift", region_name="us-east-1") | ||||||
|  | 
 | ||||||
|  |     with pytest.raises(ClientError) as ex: | ||||||
|  |         client.get_cluster_credentials(ClusterIdentifier="non-existent") | ||||||
|  |     ex.value.response["Error"]["Code"].should.equal("ClusterNotFound") | ||||||
|  |     ex.value.response["Error"]["Message"].should.match(r"Cluster .+ not found.") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @mock_redshift | ||||||
|  | def test_get_cluster_credentials_non_existent_cluster(): | ||||||
|  |     client = boto3.client("redshift", region_name="us-east-1") | ||||||
|  | 
 | ||||||
|  |     with pytest.raises(ClientError) as ex: | ||||||
|  |         client.get_cluster_credentials( | ||||||
|  |             ClusterIdentifier="non-existent", DbUser="some_user" | ||||||
|  |         ) | ||||||
|  |     ex.value.response["Error"]["Code"].should.equal("ClusterNotFound") | ||||||
|  |     ex.value.response["Error"]["Message"].should.match(r"Cluster .+ not found.") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @mock_redshift | ||||||
|  | def test_get_cluster_credentials_invalid_duration(): | ||||||
|  |     client = boto3.client("redshift", region_name="us-east-1") | ||||||
|  | 
 | ||||||
|  |     cluster_identifier = "my_cluster" | ||||||
|  |     client.create_cluster( | ||||||
|  |         ClusterIdentifier=cluster_identifier, | ||||||
|  |         ClusterType="single-node", | ||||||
|  |         DBName="test", | ||||||
|  |         MasterUsername="user", | ||||||
|  |         MasterUserPassword="password", | ||||||
|  |         NodeType="ds2.xlarge", | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     db_user = "some_user" | ||||||
|  |     with pytest.raises(ClientError) as ex: | ||||||
|  |         client.get_cluster_credentials( | ||||||
|  |             ClusterIdentifier=cluster_identifier, DbUser=db_user, DurationSeconds=899 | ||||||
|  |         ) | ||||||
|  |     ex.value.response["Error"]["Code"].should.equal("InvalidParameterValue") | ||||||
|  |     ex.value.response["Error"]["Message"].should.contain( | ||||||
|  |         "Token duration must be between 900 and 3600 seconds" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     with pytest.raises(ClientError) as ex: | ||||||
|  |         client.get_cluster_credentials( | ||||||
|  |             ClusterIdentifier=cluster_identifier, DbUser=db_user, DurationSeconds=3601 | ||||||
|  |         ) | ||||||
|  |     ex.value.response["Error"]["Code"].should.equal("InvalidParameterValue") | ||||||
|  |     ex.value.response["Error"]["Message"].should.contain( | ||||||
|  |         "Token duration must be between 900 and 3600 seconds" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @mock_redshift | ||||||
|  | def test_get_cluster_credentials(): | ||||||
|  |     client = boto3.client("redshift", region_name="us-east-1") | ||||||
|  | 
 | ||||||
|  |     cluster_identifier = "my_cluster" | ||||||
|  |     client.create_cluster( | ||||||
|  |         ClusterIdentifier=cluster_identifier, | ||||||
|  |         ClusterType="single-node", | ||||||
|  |         DBName="test", | ||||||
|  |         MasterUsername="user", | ||||||
|  |         MasterUserPassword="password", | ||||||
|  |         NodeType="ds2.xlarge", | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     expected_expiration = time.mktime( | ||||||
|  |         (datetime.datetime.now() + datetime.timedelta(0, 900)).timetuple() | ||||||
|  |     ) | ||||||
|  |     db_user = "some_user" | ||||||
|  |     response = client.get_cluster_credentials( | ||||||
|  |         ClusterIdentifier=cluster_identifier, DbUser=db_user, | ||||||
|  |     ) | ||||||
|  |     response["DbUser"].should.equal("IAM:%s" % db_user) | ||||||
|  |     assert time.mktime((response["Expiration"]).timetuple()) == pytest.approx( | ||||||
|  |         expected_expiration | ||||||
|  |     ) | ||||||
|  |     response["DbPassword"].should.have.length_of(32) | ||||||
|  | 
 | ||||||
|  |     response = client.get_cluster_credentials( | ||||||
|  |         ClusterIdentifier=cluster_identifier, DbUser=db_user, AutoCreate=True | ||||||
|  |     ) | ||||||
|  |     response["DbUser"].should.equal("IAMA:%s" % db_user) | ||||||
|  | 
 | ||||||
|  |     response = client.get_cluster_credentials( | ||||||
|  |         ClusterIdentifier=cluster_identifier, DbUser="some_other_user", AutoCreate=False | ||||||
|  |     ) | ||||||
|  |     response["DbUser"].should.equal("IAM:%s" % "some_other_user") | ||||||
|  | 
 | ||||||
|  |     expected_expiration = time.mktime( | ||||||
|  |         (datetime.datetime.now() + datetime.timedelta(0, 3000)).timetuple() | ||||||
|  |     ) | ||||||
|  |     response = client.get_cluster_credentials( | ||||||
|  |         ClusterIdentifier=cluster_identifier, DbUser=db_user, DurationSeconds=3000, | ||||||
|  |     ) | ||||||
|  |     assert time.mktime(response["Expiration"].timetuple()) == pytest.approx( | ||||||
|  |         expected_expiration | ||||||
|  |     ) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user