Repository: getmoto/moto

Issue:
Change state to `active` when describing load balancer
Would there be any objection to changing the `State` of a newly created load balancer to `active` instead of `provisioning`?

https://github.com/spulec/moto/blob/d3f20694a90209d1dd39f27e528edfa7d170d429/moto/elbv2/responses.py#L708-L710

The use case would be that if application code waits for the load balancer state to transition from `provisioning` to `active` before taking further actions.


Discussion:
Hi @giovtorres,  returning 'provisioning' in the `create_load_balancer`-call would be the appropriate AWS response.

Am I correct in assuming that you use the `describe_load_balancers`-call (or one of the waiters) to check when the state is 'active'?
Because changing the response of `describe_load_balancers` to say 'active' would make a lot of sense, and that keeps in line with the AWS behaviour.

In other services we sometimes have a counter, where a state will only advance (from 'provisioning' -> 'active') after a certain number of calls, to mimick the asynchronicity that will occur in real live. I don't have enough knowledge of ELB to say whether such an artificial wait time would make sense here, though
> Am I correct in assuming that you use the `describe_load_balancers`-call (or one of the waiters) to check when the state is 'active'?
> Because changing the response of `describe_load_balancers` to say 'active' would make a lot of sense, and that keeps in line with the AWS behaviour.

Yes, I would use `describe_load_balancers` to check when the state is `active`.

> In other services we sometimes have a counter, where a state will only advance (from 'provisioning' -> 'active') after a certain number of calls, to mimick the asynchronicity that will occur in real live. I don't have enough knowledge of ELB to say whether such an artificial wait time would make sense here, though

Do you have an example in the code where there is a counter to change the state?


The ELBv2 API seems to have 4 states:  `provisioning`, `active`, `active_impaired`, and `failed`.

Would it make sense to allow the user to configure the end state of the load balancer in order to test against the different states? The state could always start with `provisioning` and then transition to the end state after a configurable number of calls? Or, it could be less complex and just transition to `active`.

Thoughts?
I can't find an example with a counter atm, but a simple advance/transition can be done like this:
https://github.com/spulec/moto/blob/d3f20694a90209d1dd39f27e528edfa7d170d429/moto/support/models.py#L81-L101

A configurable endstate would be ideal, yes, which can be implemented with the user setting an environment variable. 
I imagine the less complex variant, `provisioning -> active`, would already be very useful for most users, so that can be a first iteration.

Resolving patch:
diff --git a/moto/elbv2/models.py b/moto/elbv2/models.py
--- a/moto/elbv2/models.py
+++ b/moto/elbv2/models.py
@@ -381,6 +381,7 @@ def __init__(
         vpc_id,
         arn,
         dns_name,
+        state,
         scheme="internet-facing",
     ):
         self.name = name
@@ -393,6 +394,7 @@ def __init__(
         self.tags = {}
         self.arn = arn
         self.dns_name = dns_name
+        self.state = state
 
         self.stack = "ipv4"
         self.attrs = {
@@ -419,6 +421,10 @@ def remove_tag(self, key):
         if key in self.tags:
             del self.tags[key]
 
+    def activate(self):
+        if self.state == "provisioning":
+            self.state = "active"
+
     def delete(self, region):
         """ Not exposed as part of the ELB API - used for CloudFormation. """
         elbv2_backends[region].delete_load_balancer(self.arn)
@@ -517,6 +523,8 @@ def create_load_balancer(
     ):
         vpc_id = None
         subnets = []
+        state = "provisioning"
+
         if not subnet_ids:
             raise SubnetNotFoundError()
         for subnet_id in subnet_ids:
@@ -542,6 +550,7 @@ def create_load_balancer(
             subnets=subnets,
             vpc_id=vpc_id,
             dns_name=dns_name,
+            state=state,
         )
         self.load_balancers[arn] = new_load_balancer
         return new_load_balancer
@@ -740,6 +749,8 @@ def describe_load_balancers(self, arns, names):
         arns = arns or []
         names = names or []
         if not arns and not names:
+            for balancer in balancers:
+                balancer.activate()
             return balancers
 
         matched_balancers = []
@@ -747,6 +758,7 @@ def describe_load_balancers(self, arns, names):
 
         for arn in arns:
             for balancer in balancers:
+                balancer.activate()
                 if balancer.arn == arn:
                     matched_balancer = balancer
             if matched_balancer is None:
@@ -756,6 +768,7 @@ def describe_load_balancers(self, arns, names):
 
         for name in names:
             for balancer in balancers:
+                balancer.activate()
                 if balancer.name == name:
                     matched_balancer = balancer
             if matched_balancer is None:
diff --git a/moto/elbv2/responses.py b/moto/elbv2/responses.py
--- a/moto/elbv2/responses.py
+++ b/moto/elbv2/responses.py
@@ -706,7 +706,7 @@ def _add_tags(self, resource):
         </SecurityGroups>
         <DNSName>{{ load_balancer.dns_name }}</DNSName>
         <State>
-          <Code>provisioning</Code>
+          <Code>{{ load_balancer.state }}</Code>
         </State>
         <Type>application</Type>
       </member>
@@ -877,7 +877,7 @@ def _add_tags(self, resource):
         </SecurityGroups>
         <DNSName>{{ load_balancer.dns_name }}</DNSName>
         <State>
-          <Code>provisioning</Code>
+          <Code>{{ load_balancer.state }}</Code>
         </State>
         <Type>application</Type>
         <IpAddressType>ipv4</IpAddressType>


Test patch:
diff --git a/tests/test_elbv2/test_elbv2.py b/tests/test_elbv2/test_elbv2.py
--- a/tests/test_elbv2/test_elbv2.py
+++ b/tests/test_elbv2/test_elbv2.py
@@ -52,6 +52,7 @@ def test_create_load_balancer():
         ]
     )
     lb.get("CreatedTime").tzinfo.should_not.be.none
+    lb.get("State").get("Code").should.equal("provisioning")
 
     # Ensure the tags persisted
     response = conn.describe_tags(ResourceArns=[lb.get("LoadBalancerArn")])
@@ -89,6 +90,7 @@ def test_describe_load_balancers():
     response.get("LoadBalancers").should.have.length_of(1)
     lb = response.get("LoadBalancers")[0]
     lb.get("LoadBalancerName").should.equal("my-lb")
+    lb.get("State").get("Code").should.equal("active")
 
     response = conn.describe_load_balancers(
         LoadBalancerArns=[lb.get("LoadBalancerArn")]