如何有条件地传递对流层对象属性?

How to pass a Troposphere object attribute conditionally?

我正在使用 Troposphere 构建 CloudFormation 堆栈,并且希望仅在我的配置中设置了 Elastic Load Balancer ConnectionSettings 属性时才传递它,否则我不想指定它.

如果我将其设置为默认值 None,则会收到一条错误消息,提示该值不是 troposphere.elasticloadbalancing.ConnectionSettings 的预期类型。

我宁愿尽量避免在调用中设置明确的默认值,因为它可能会覆盖其他设置。

理想情况下,我希望能够向现有对象添加属性,例如:

lb = template.add_resource(elb.LoadBalancer(
  ...
))

if condition:
  lb.add_attribute(ConnectionSettings = elb.ConnectionSettings(
    ...
  ))

有办法实现吗?

更新:我使用隐藏的 Troposphere 方法实现了它,该方法有效但我不满意:

if condition:
  lb.__setattr__('ConnectionSettings', elb.ConnectionSettings(
    ....
  ))

我仍然对不涉及从模块外部使用私有方法的解决方案感兴趣。

所以最大的问题是 - ConnectionSettings 的配置从何而来?在 Cloudformation 本身(和对流层)内部,有条件、参数和 AWS::NoValue 参考。我在堆垛机 RDS 模板中大量使用它:

这是参数:https://github.com/remind101/stacker/blob/master/stacker/blueprints/rds/base.py#L126 这是条件:https://github.com/remind101/stacker/blob/master/stacker/blueprints/rds/base.py#L243 这是稍后在资源中使用它的方式,可选 - 如果 StorageType 参数为空,我们使用 AWS::NoValue,这是一个伪 Ref,用于实际不设置某些东西:(抱歉,不能 post 更多超过 2 个链接 - 转到同一文件中的第 304 行,看看我在说什么)

但是,如果您不使用参数,而是在 python 中执行所有条件,则可以执行类似的操作。类似于:

connection_setting = condition and <actual connection setting code> or Ref("AWS::NoValue")

另一种选择是完全在 python 中完成,这基本上就是您的示例。希望这会有所帮助,有很多方法可以解决这个问题,包括创建两个不同的 ELB 对象(一个有连接设置,一个没有),然后选择一个带有 python 代码(如果有条件)或 cloudformation 条件的对象.

主要的 README 避免使用像这样的属性名称:

from troposphere import Template
import troposphere.elasticloadbalancing as elb

template = Template()
webelb = elb.LoadBalancer(
    'ElasticLoadBalancer',
    Listeners=[
        elb.Listener(
            LoadBalancerPort="80",
            InstancePort="80",
            Protocol="HTTP",
        ),
    ],
)

if True:
    webelb.ConnectionSettings = elb.ConnectionSettings(IdleTimeout=30)
elasticLB = template.add_resource(webelb)
print(template.to_json())

如果值在 Python 中已知(即,它 不是 来自 CloudFormation 参数),您可以使用字典向资源添加可选属性在对流层模板中:

from troposphere import Template
import troposphere.elasticloadbalancing as elb

template = Template()

my_idle_timeout = 30  # replace this with your method for determining the value

my_elb_params = {}
if my_idle_timeout is not None:
    my_elb_params['ConnectionSettings'] = elb.ConnectionSettings(
        IdleTimeout=my_idle_timeout,
    )

my_elb = template.add_resource(elb.LoadBalancer(
    'ElasticLoadBalancer',
    Listeners=[
        elb.Listener(
            LoadBalancerPort="80",
            InstancePort="80",
            Protocol="HTTP",
        ),
    ],
    **my_elb_params,
))

print(template.to_json())

如果值来自 CloudFormation 参数,您需要创建一个 Condition 来测试参数值,如果没有为参数提供值,则使用 Ref("AWS::NoValue"),例如:

from troposphere import Template, Parameter, Equals, Ref, If
import troposphere.elasticloadbalancing as elb

template = Template()

my_idle_timeout = template.add_parameter(Parameter(
    "ElbIdleTimeout",
    Description="Idle timeout for the Elastic Load Balancer",
    Type="Number",
))

no_idle_timeout = "NoIdleTimeout"
template.add_condition(
    no_idle_timeout,
    Equals(Ref(my_idle_timeout), ""),
)

my_elb = template.add_resource(elb.LoadBalancer(
    'ElasticLoadBalancer',
    Listeners=[
        elb.Listener(
            LoadBalancerPort="80",
            InstancePort="80",
            Protocol="HTTP",
        ),
    ],
    ConnectionSettings=If(
        no_idle_timeout,
        Ref("AWS::NoValue"),
        elb.ConnectionSettings(
            IdleTimeout=Ref(my_idle_timeout),
        ),
    ),
))

print(template.to_json())