Feature: ACM: in_use_by (#4414)
This commit is contained in:
parent
d916fd636f
commit
1f13d6c406
@ -144,6 +144,7 @@ class CertBundle(BaseModel):
|
|||||||
self._chain = None
|
self._chain = None
|
||||||
self.type = cert_type # Should really be an enum
|
self.type = cert_type # Should really be an enum
|
||||||
self.status = cert_status # Should really be an enum
|
self.status = cert_status # Should really be an enum
|
||||||
|
self.in_use_by = []
|
||||||
|
|
||||||
# AWS always returns your chain + root CA
|
# AWS always returns your chain + root CA
|
||||||
if self.chain is None:
|
if self.chain is None:
|
||||||
@ -361,7 +362,7 @@ class CertBundle(BaseModel):
|
|||||||
"Certificate": {
|
"Certificate": {
|
||||||
"CertificateArn": self.arn,
|
"CertificateArn": self.arn,
|
||||||
"DomainName": self.common_name,
|
"DomainName": self.common_name,
|
||||||
"InUseBy": [],
|
"InUseBy": self.in_use_by,
|
||||||
"Issuer": self._cert.issuer.get_attributes_for_oid(
|
"Issuer": self._cert.issuer.get_attributes_for_oid(
|
||||||
cryptography.x509.OID_COMMON_NAME
|
cryptography.x509.OID_COMMON_NAME
|
||||||
)[0].value,
|
)[0].value,
|
||||||
@ -430,6 +431,13 @@ class AWSCertificateManagerBackend(BaseBackend):
|
|||||||
)
|
)
|
||||||
return AWSResourceNotFoundException(msg)
|
return AWSResourceNotFoundException(msg)
|
||||||
|
|
||||||
|
def set_certificate_in_use_by(self, arn, load_balancer_name):
|
||||||
|
if arn not in self._certificates:
|
||||||
|
raise self._arn_not_found(arn)
|
||||||
|
|
||||||
|
cert_bundle = self._certificates[arn]
|
||||||
|
cert_bundle.in_use_by.append(load_balancer_name)
|
||||||
|
|
||||||
def _get_arn_from_idempotency_token(self, token):
|
def _get_arn_from_idempotency_token(self, token):
|
||||||
"""
|
"""
|
||||||
If token doesnt exist, return None, later it will be
|
If token doesnt exist, return None, later it will be
|
||||||
|
@ -13,6 +13,13 @@ class DuplicateTagKeysError(ELBClientError):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CertificateNotFoundException(ELBClientError):
|
||||||
|
def __init__(self):
|
||||||
|
super(CertificateNotFoundException, self).__init__(
|
||||||
|
"CertificateNotFoundException", "Supplied certificate was not found"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class LoadBalancerNotFoundError(ELBClientError):
|
class LoadBalancerNotFoundError(ELBClientError):
|
||||||
def __init__(self, cidr):
|
def __init__(self, cidr):
|
||||||
super(LoadBalancerNotFoundError, self).__init__(
|
super(LoadBalancerNotFoundError, self).__init__(
|
||||||
|
@ -23,6 +23,7 @@ from .exceptions import (
|
|||||||
InvalidSecurityGroupError,
|
InvalidSecurityGroupError,
|
||||||
LoadBalancerNotFoundError,
|
LoadBalancerNotFoundError,
|
||||||
TooManyTagsError,
|
TooManyTagsError,
|
||||||
|
CertificateNotFoundException,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -79,6 +80,7 @@ class FakeLoadBalancer(CloudFormationModel):
|
|||||||
vpc_id=None,
|
vpc_id=None,
|
||||||
subnets=None,
|
subnets=None,
|
||||||
security_groups=None,
|
security_groups=None,
|
||||||
|
elb_backend=None,
|
||||||
):
|
):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.health_check = None
|
self.health_check = None
|
||||||
@ -111,6 +113,11 @@ class FakeLoadBalancer(CloudFormationModel):
|
|||||||
"ssl_certificate_id", port.get("SSLCertificateId")
|
"ssl_certificate_id", port.get("SSLCertificateId")
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
if listener.ssl_certificate_id:
|
||||||
|
elb_backend._register_certificate(
|
||||||
|
listener.ssl_certificate_id, self.dns_name
|
||||||
|
)
|
||||||
|
|
||||||
self.listeners.append(listener)
|
self.listeners.append(listener)
|
||||||
|
|
||||||
# it is unclear per the AWS documentation as to when or how backend
|
# it is unclear per the AWS documentation as to when or how backend
|
||||||
@ -310,6 +317,7 @@ class ELBBackend(BaseBackend):
|
|||||||
subnets=subnets,
|
subnets=subnets,
|
||||||
security_groups=security_groups,
|
security_groups=security_groups,
|
||||||
vpc_id=vpc_id,
|
vpc_id=vpc_id,
|
||||||
|
elb_backend=self,
|
||||||
)
|
)
|
||||||
self.load_balancers[name] = new_load_balancer
|
self.load_balancers[name] = new_load_balancer
|
||||||
return new_load_balancer
|
return new_load_balancer
|
||||||
@ -332,6 +340,10 @@ class ELBBackend(BaseBackend):
|
|||||||
raise DuplicateListenerError(name, lb_port)
|
raise DuplicateListenerError(name, lb_port)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
if ssl_certificate_id:
|
||||||
|
self._register_certificate(
|
||||||
|
ssl_certificate_id, balancer.dns_name
|
||||||
|
)
|
||||||
balancer.listeners.append(
|
balancer.listeners.append(
|
||||||
FakeListener(
|
FakeListener(
|
||||||
lb_port, instance_port, protocol, ssl_certificate_id
|
lb_port, instance_port, protocol, ssl_certificate_id
|
||||||
@ -406,7 +418,10 @@ class ELBBackend(BaseBackend):
|
|||||||
for idx, listener in enumerate(balancer.listeners):
|
for idx, listener in enumerate(balancer.listeners):
|
||||||
if lb_port == listener.load_balancer_port:
|
if lb_port == listener.load_balancer_port:
|
||||||
balancer.listeners[idx].ssl_certificate_id = ssl_certificate_id
|
balancer.listeners[idx].ssl_certificate_id = ssl_certificate_id
|
||||||
|
if ssl_certificate_id:
|
||||||
|
self._register_certificate(
|
||||||
|
ssl_certificate_id, balancer.dns_name
|
||||||
|
)
|
||||||
return balancer
|
return balancer
|
||||||
|
|
||||||
def register_instances(
|
def register_instances(
|
||||||
@ -509,6 +524,15 @@ class ELBBackend(BaseBackend):
|
|||||||
load_balancer.listeners[listener_idx] = listener
|
load_balancer.listeners[listener_idx] = listener
|
||||||
return load_balancer
|
return load_balancer
|
||||||
|
|
||||||
|
def _register_certificate(self, ssl_certificate_id, dns_name):
|
||||||
|
from moto.acm.models import acm_backends, AWSResourceNotFoundException
|
||||||
|
|
||||||
|
acm_backend = acm_backends[self.region_name]
|
||||||
|
try:
|
||||||
|
acm_backend.set_certificate_in_use_by(ssl_certificate_id, dns_name)
|
||||||
|
except AWSResourceNotFoundException:
|
||||||
|
raise CertificateNotFoundException()
|
||||||
|
|
||||||
|
|
||||||
elb_backends = {}
|
elb_backends = {}
|
||||||
for region in ec2_backends.keys():
|
for region in ec2_backends.keys():
|
||||||
|
@ -11,11 +11,11 @@ from botocore.exceptions import ClientError
|
|||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
from cryptography.hazmat.primitives import serialization, hashes
|
from cryptography.hazmat.primitives import serialization, hashes
|
||||||
from freezegun import freeze_time
|
from freezegun import freeze_time
|
||||||
from moto import mock_acm, settings
|
from moto import mock_acm, mock_elb, settings
|
||||||
from moto.core import ACCOUNT_ID
|
from moto.core import ACCOUNT_ID
|
||||||
|
|
||||||
from unittest import SkipTest, mock
|
from unittest import SkipTest, mock
|
||||||
|
|
||||||
|
|
||||||
RESOURCE_FOLDER = os.path.join(os.path.dirname(__file__), "resources")
|
RESOURCE_FOLDER = os.path.join(os.path.dirname(__file__), "resources")
|
||||||
_GET_RESOURCE = lambda x: open(os.path.join(RESOURCE_FOLDER, x), "rb").read()
|
_GET_RESOURCE = lambda x: open(os.path.join(RESOURCE_FOLDER, x), "rb").read()
|
||||||
CA_CRT = _GET_RESOURCE("ca.pem")
|
CA_CRT = _GET_RESOURCE("ca.pem")
|
||||||
@ -640,3 +640,38 @@ def test_request_certificate_with_mutiple_times():
|
|||||||
)
|
)
|
||||||
arn = resp["CertificateArn"]
|
arn = resp["CertificateArn"]
|
||||||
arn.should_not.equal(original_arn)
|
arn.should_not.equal(original_arn)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_acm
|
||||||
|
@mock_elb
|
||||||
|
def test_elb_acm_in_use_by():
|
||||||
|
acm_client = boto3.client("acm", region_name="us-west-2")
|
||||||
|
elb_client = boto3.client("elb", region_name="us-west-2")
|
||||||
|
|
||||||
|
acm_request_response = acm_client.request_certificate(
|
||||||
|
DomainName="fake.domain.com",
|
||||||
|
DomainValidationOptions=[
|
||||||
|
{"DomainName": "fake.domain.com", "ValidationDomain": "domain.com"}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
certificate_arn = acm_request_response["CertificateArn"]
|
||||||
|
|
||||||
|
create_load_balancer_request = elb_client.create_load_balancer(
|
||||||
|
LoadBalancerName="test",
|
||||||
|
Listeners=[
|
||||||
|
{
|
||||||
|
"Protocol": "https",
|
||||||
|
"LoadBalancerPort": 443,
|
||||||
|
"InstanceProtocol": "http",
|
||||||
|
"InstancePort": 80,
|
||||||
|
"SSLCertificateId": certificate_arn,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
response = acm_client.describe_certificate(CertificateArn=certificate_arn)
|
||||||
|
|
||||||
|
response["Certificate"]["InUseBy"].should.equal(
|
||||||
|
[create_load_balancer_request["DNSName"]]
|
||||||
|
)
|
||||||
|
@ -14,7 +14,7 @@ from boto.exception import BotoServerError
|
|||||||
import pytest
|
import pytest
|
||||||
import sure # noqa
|
import sure # noqa
|
||||||
|
|
||||||
from moto import mock_elb, mock_ec2, mock_elb_deprecated, mock_ec2_deprecated
|
from moto import mock_acm, mock_elb, mock_ec2, mock_elb_deprecated, mock_ec2_deprecated
|
||||||
from moto.core import ACCOUNT_ID
|
from moto.core import ACCOUNT_ID
|
||||||
from tests import EXAMPLE_AMI_ID
|
from tests import EXAMPLE_AMI_ID
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
@ -201,42 +201,21 @@ def test_create_elb_in_multiple_region_boto3():
|
|||||||
west_names.shouldnt.contain(name_east)
|
west_names.shouldnt.contain(name_east)
|
||||||
|
|
||||||
|
|
||||||
# Has boto3 equivalent
|
@mock_acm
|
||||||
@mock_elb_deprecated
|
|
||||||
def test_create_load_balancer_with_certificate():
|
|
||||||
conn = boto.connect_elb()
|
|
||||||
|
|
||||||
zones = ["us-east-1a"]
|
|
||||||
ports = [
|
|
||||||
(
|
|
||||||
443,
|
|
||||||
8443,
|
|
||||||
"https",
|
|
||||||
"arn:aws:iam:{}:server-certificate/test-cert".format(ACCOUNT_ID),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
conn.create_load_balancer("my-lb", zones, ports)
|
|
||||||
|
|
||||||
balancers = conn.get_all_load_balancers()
|
|
||||||
balancer = balancers[0]
|
|
||||||
balancer.name.should.equal("my-lb")
|
|
||||||
balancer.scheme.should.equal("internet-facing")
|
|
||||||
set(balancer.availability_zones).should.equal(set(["us-east-1a"]))
|
|
||||||
listener = balancer.listeners[0]
|
|
||||||
listener.load_balancer_port.should.equal(443)
|
|
||||||
listener.instance_port.should.equal(8443)
|
|
||||||
listener.protocol.should.equal("HTTPS")
|
|
||||||
listener.ssl_certificate_id.should.equal(
|
|
||||||
"arn:aws:iam:{}:server-certificate/test-cert".format(ACCOUNT_ID)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@mock_elb
|
@mock_elb
|
||||||
def test_create_load_balancer_with_certificate_boto3():
|
def test_create_load_balancer_with_certificate_boto3():
|
||||||
|
acm_client = boto3.client("acm", region_name="us-east-2")
|
||||||
|
acm_request_response = acm_client.request_certificate(
|
||||||
|
DomainName="fake.domain.com",
|
||||||
|
DomainValidationOptions=[
|
||||||
|
{"DomainName": "fake.domain.com", "ValidationDomain": "domain.com"},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
certificate_arn = acm_request_response["CertificateArn"]
|
||||||
|
|
||||||
client = boto3.client("elb", region_name="us-east-2")
|
client = boto3.client("elb", region_name="us-east-2")
|
||||||
|
|
||||||
name = str(uuid4())[0:6]
|
name = str(uuid4())[0:6]
|
||||||
cert_id = "arn:aws:iam:{}:server-certificate/test-cert".format(ACCOUNT_ID)
|
|
||||||
|
|
||||||
client.create_load_balancer(
|
client.create_load_balancer(
|
||||||
LoadBalancerName=name,
|
LoadBalancerName=name,
|
||||||
@ -245,7 +224,7 @@ def test_create_load_balancer_with_certificate_boto3():
|
|||||||
"Protocol": "https",
|
"Protocol": "https",
|
||||||
"LoadBalancerPort": 8443,
|
"LoadBalancerPort": 8443,
|
||||||
"InstancePort": 443,
|
"InstancePort": 443,
|
||||||
"SSLCertificateId": cert_id,
|
"SSLCertificateId": certificate_arn,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
AvailabilityZones=["us-east-1a"],
|
AvailabilityZones=["us-east-1a"],
|
||||||
@ -257,7 +236,30 @@ def test_create_load_balancer_with_certificate_boto3():
|
|||||||
|
|
||||||
listener = describe["ListenerDescriptions"][0]["Listener"]
|
listener = describe["ListenerDescriptions"][0]["Listener"]
|
||||||
listener.should.have.key("Protocol").equal("HTTPS")
|
listener.should.have.key("Protocol").equal("HTTPS")
|
||||||
listener.should.have.key("SSLCertificateId").equals(cert_id)
|
listener.should.have.key("SSLCertificateId").equals(certificate_arn)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_elb
|
||||||
|
def test_create_load_balancer_with_invalid_certificate():
|
||||||
|
client = boto3.client("elb", region_name="us-east-2")
|
||||||
|
|
||||||
|
name = str(uuid4())[0:6]
|
||||||
|
|
||||||
|
with pytest.raises(ClientError) as exc:
|
||||||
|
client.create_load_balancer(
|
||||||
|
LoadBalancerName=name,
|
||||||
|
Listeners=[
|
||||||
|
{
|
||||||
|
"Protocol": "https",
|
||||||
|
"LoadBalancerPort": 8443,
|
||||||
|
"InstancePort": 443,
|
||||||
|
"SSLCertificateId": "invalid_arn",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
AvailabilityZones=["us-east-1a"],
|
||||||
|
)
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("CertificateNotFoundException")
|
||||||
|
|
||||||
|
|
||||||
@mock_elb
|
@mock_elb
|
||||||
@ -433,26 +435,87 @@ def test_create_and_delete_listener_boto3_support():
|
|||||||
list(balancer["ListenerDescriptions"]).should.have.length_of(1)
|
list(balancer["ListenerDescriptions"]).should.have.length_of(1)
|
||||||
|
|
||||||
|
|
||||||
# Has boto3 equivalent
|
@mock_acm
|
||||||
@mock_elb_deprecated
|
@mock_elb
|
||||||
def test_set_sslcertificate():
|
def test_create_lb_listener_with_ssl_certificate():
|
||||||
conn = boto.connect_elb()
|
acm_client = boto3.client("acm", region_name="eu-west-1")
|
||||||
|
acm_request_response = acm_client.request_certificate(
|
||||||
|
DomainName="fake.domain.com",
|
||||||
|
DomainValidationOptions=[
|
||||||
|
{"DomainName": "fake.domain.com", "ValidationDomain": "domain.com"},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
certificate_arn = acm_request_response["CertificateArn"]
|
||||||
|
|
||||||
zones = ["us-east-1a", "us-east-1b"]
|
client = boto3.client("elb", region_name="eu-west-1")
|
||||||
ports = [(443, 8443, "tcp")]
|
|
||||||
conn.create_load_balancer("my-lb", zones, ports)
|
client.create_load_balancer(
|
||||||
conn.set_lb_listener_SSL_certificate("my-lb", "443", "arn:certificate")
|
LoadBalancerName="my-lb",
|
||||||
balancers = conn.get_all_load_balancers()
|
Listeners=[{"Protocol": "http", "LoadBalancerPort": 80, "InstancePort": 8080}],
|
||||||
balancer = balancers[0]
|
AvailabilityZones=["us-east-1a", "us-east-1b"],
|
||||||
listener1 = balancer.listeners[0]
|
)
|
||||||
listener1.load_balancer_port.should.equal(443)
|
|
||||||
listener1.instance_port.should.equal(8443)
|
client.create_load_balancer_listeners(
|
||||||
listener1.protocol.should.equal("TCP")
|
LoadBalancerName="my-lb",
|
||||||
listener1.ssl_certificate_id.should.equal("arn:certificate")
|
Listeners=[
|
||||||
|
{
|
||||||
|
"Protocol": "tcp",
|
||||||
|
"LoadBalancerPort": 443,
|
||||||
|
"InstancePort": 8443,
|
||||||
|
"SSLCertificateId": certificate_arn,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
balancer = client.describe_load_balancers()["LoadBalancerDescriptions"][0]
|
||||||
|
listeners = balancer["ListenerDescriptions"]
|
||||||
|
listeners.should.have.length_of(2)
|
||||||
|
|
||||||
|
listeners[0]["Listener"]["Protocol"].should.equal("HTTP")
|
||||||
|
listeners[0]["Listener"]["SSLCertificateId"].should.equal("None")
|
||||||
|
|
||||||
|
listeners[1]["Listener"]["Protocol"].should.equal("TCP")
|
||||||
|
listeners[1]["Listener"]["SSLCertificateId"].should.equal(certificate_arn)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_acm
|
||||||
|
@mock_elb
|
||||||
|
def test_create_lb_listener_with_invalid_ssl_certificate():
|
||||||
|
client = boto3.client("elb", region_name="eu-west-1")
|
||||||
|
|
||||||
|
client.create_load_balancer(
|
||||||
|
LoadBalancerName="my-lb",
|
||||||
|
Listeners=[{"Protocol": "http", "LoadBalancerPort": 80, "InstancePort": 8080}],
|
||||||
|
AvailabilityZones=["us-east-1a", "us-east-1b"],
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(ClientError) as exc:
|
||||||
|
client.create_load_balancer_listeners(
|
||||||
|
LoadBalancerName="my-lb",
|
||||||
|
Listeners=[
|
||||||
|
{
|
||||||
|
"Protocol": "tcp",
|
||||||
|
"LoadBalancerPort": 443,
|
||||||
|
"InstancePort": 8443,
|
||||||
|
"SSLCertificateId": "unknownarn",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("CertificateNotFoundException")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_acm
|
||||||
@mock_elb
|
@mock_elb
|
||||||
def test_set_sslcertificate_boto3():
|
def test_set_sslcertificate_boto3():
|
||||||
|
acm_client = boto3.client("acm", region_name="us-east-1")
|
||||||
|
acm_request_response = acm_client.request_certificate(
|
||||||
|
DomainName="fake.domain.com",
|
||||||
|
DomainValidationOptions=[
|
||||||
|
{"DomainName": "fake.domain.com", "ValidationDomain": "domain.com"},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
certificate_arn = acm_request_response["CertificateArn"]
|
||||||
|
|
||||||
client = boto3.client("elb", region_name="us-east-1")
|
client = boto3.client("elb", region_name="us-east-1")
|
||||||
lb_name = str(uuid4())[0:6]
|
lb_name = str(uuid4())[0:6]
|
||||||
|
|
||||||
@ -466,9 +529,7 @@ def test_set_sslcertificate_boto3():
|
|||||||
)
|
)
|
||||||
|
|
||||||
client.set_load_balancer_listener_ssl_certificate(
|
client.set_load_balancer_listener_ssl_certificate(
|
||||||
LoadBalancerName=lb_name,
|
LoadBalancerName=lb_name, LoadBalancerPort=81, SSLCertificateId=certificate_arn,
|
||||||
LoadBalancerPort=81,
|
|
||||||
SSLCertificateId="arn:certificate",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
elb = client.describe_load_balancers()["LoadBalancerDescriptions"][0]
|
elb = client.describe_load_balancers()["LoadBalancerDescriptions"][0]
|
||||||
@ -479,7 +540,7 @@ def test_set_sslcertificate_boto3():
|
|||||||
|
|
||||||
listener = elb["ListenerDescriptions"][1]["Listener"]
|
listener = elb["ListenerDescriptions"][1]["Listener"]
|
||||||
listener.should.have.key("LoadBalancerPort").equals(81)
|
listener.should.have.key("LoadBalancerPort").equals(81)
|
||||||
listener.should.have.key("SSLCertificateId").equals("arn:certificate")
|
listener.should.have.key("SSLCertificateId").equals(certificate_arn)
|
||||||
|
|
||||||
|
|
||||||
# Has boto3 equivalent
|
# Has boto3 equivalent
|
||||||
|
Loading…
Reference in New Issue
Block a user