Como expor informações usando saídas

Ao criar uma implantação, às vezes é necessário expor as principais propriedades de configurações ou modelos para o consumo de outros modelos ou usuários. Por exemplo, convém expor o endereço IP de um banco de dados criado em um modelo para que os usuários tenham facilidade em referenciar o IP ao configurar os modelos deles.

Você pode usar a seção de saídas do modelo ou da configuração para definir uma lista de pares de chave-valor que os usuários podem chamar. Na seção de saídas, você define as chaves arbitrárias e os respectivos valores como uma referência, uma propriedade de modelo ou uma variável de ambiente. Os usuários podem consumir saídas para acessar informações importantes sobre os recursos criados pelo modelo. Por exemplo, é possível declarar uma saída chamada databaseIP, que faz referência ao endereço IP de uma instância que hospeda um banco de dados e os usuários podem fazer referência a essa saída em outros modelos na mesma implantação.

Antes de começar

Exemplo

Veja abaixo um modelo de exemplo com saídas:

mongodb.jinja
{% set MASTER = env["name"] + "-" + env["deployment"] + "-mongodb" %}
resources:
- name: {{ MASTER }}
  type: instance
  ...
outputs:
- name: databaseIp
  value: $(ref.{{ MASTER }}.network[0].ip)  # Treated as a string during expansion
- name: databasePort
  value: 88

A seção de saídas declara duas propriedades: databaseIp e databasePort. databaseIp usa uma referência resolvida com o endereço IP da rede do recurso mestre, e databasePort é um valor estático. Em outro modelo, é possível importar mongodb.jinja, usar o modelo como um tipo e chamar as saídas. Exemplo:

imports:
- path: example/path/to/mongodb.jinja
  name: mongodb.jinja

resources:
- name: my_mongo
  type: mongodb.jinja
  properties:
    size: 100

- name: my_instance
  type: compute.v1.instance
  properties:
    …
    databaseIp: $(ref.my_mongo.databaseIp)
    databasePort: $(ref.my_mongo.databasePort)

Como declarar uma saída

Declare uma saída em um modelo ou uma configuração definindo uma seção outputs: no mesmo nível da seção resources:. As chaves de saída precisam ser únicas no modelo ou na configuração.

Por exemplo, uma seção de amostra outputs: pode ter esta aparência:

...
outputs:
- name: databaseIp
  value: $(ref.my-first-vm.networkInterfaces[0].accessConfigs[0].natIP)
- name: machineType
  value: {{ properties['machineType'] }}
- name: databasePort
  value: 88

As saídas em um modelo completo podem ficar assim:



resources:
- name: my-first-vm
  type: compute.v1.instance
  properties:
    zone: us-central1-a
    machineType: zones/us-central1-a/machineTypes/{{ properties['machineType'] }}
    disks:
    - deviceName: boot
      type: PERSISTENT
      boot: true
      autoDelete: true
      initializeParams:
        sourceImage: projects/debian-cloud/global/images/family/debian-11
    networkInterfaces:
    - network: global/networks/default
      accessConfigs:
      - name: External NAT
        type: ONE_TO_ONE_NAT

# Declare outputs here
outputs:
- name: databaseIp
  value: $(ref.my-first-vm.networkInterfaces[0].accessConfigs[0].natIP)
- name: machineType
  value: {{ properties['machineType'] }}
- name: databasePort
  value: 88

Os valores de saída podem ser:

Usar saídas de modelos

Para usar uma saída definida em um modelo, importe e use o modelo que tem a saída como um tipo. Por exemplo, para usar saídas definidas em um modelo chamado template_with_outputs.jinja, é preciso que ele seja importado e usado para criar um recurso:

# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
imports:
- path: template_with_outputs.jinja
  name: template.jinja

resources:
- name: my-first-vm
  type: template.jinja
  properties:
    machineType: n1-standard-1

outputs:
- name: databaseIp
  value: $(ref.my-first-vm.databaseIp)
- name: machineType
  value: $(ref.my-first-vm.machineType)
- name: databasePort
  value: $(ref.my-first-vm.databasePort)

Para chamar uma saída, use o seguinte formato:

$(ref.RESOURCE.OUTPUT)
  • RESOURCE é o nome do recurso criado pelo modelo. No exemplo acima, isso é my-first-vm.

  • OUTPUT é a saída declarada no modelo. No exemplo acima, seria databaseIp e databasePort. Essa é a mesma sintaxe usada para declarar referências. Também é possível fazer referência a itens da lista, por exemplo: $ref.template.property[0].

Quando você implanta a configuração, o Deployment Manager expande a configuração e substitui as referências a saídas pelos valores de saída.

Como descrever saídas em esquemas

Para os modelos que são acompanhados de esquemas, é possível descrever as propriedades de saída com mais detalhes. O Deployment Manager não impõe nem valida informações na seção "outputs", mas o uso dessa seção é potencialmente útil para fornecer mais informações sobre saídas relevantes, beneficiando usuários que utilizam os modelos.

No arquivo de esquema, forneça uma seção "outputs" que corresponda à saída no modelo. Exemplo:

...
outputs:
  databaseIp:
    description: Reference to ip address of your new cluster
    type: string
  databasePort:
    description: Port to talk on
    type: integer

Os usuários podem referenciar o arquivo de esquema para entender o uso e o tipo das saídas.

Como pesquisar valores de saída finais

Após implantar os modelos que usam saídas, veja os valores de saída finais ao visualizar o layout de configuração da implantação. Os valores de saída finais são indicados pela propriedade finalValue. Todos os valores de saída estão incluídos nesse campo, inclusive os valores de saída de modelos aninhados. Exemplo:

layout: |
  resources:
  - name: vm_template
    outputs:
    - finalValue: 104.197.69.69
      name: databaseIp
      value: $(ref.vm-test.networkInterfaces[0].accessConfigs[0].natIP)
    properties:
      zone: us-central1-a
    resources:
    - name: datadisk-example-instance
      type: compute.v1.disk
    - name: vm-test
      type: compute.v1.instance
    type: vm_template.jinja
name: manifest-1455057116997

Evitar dependências circulares

Tome cuidado ao criar modelos em que dois ou mais recursos dependem de saídas um do outro. O Deployment Manager não impede essa estrutura, mas, se as saídas causarem uma dependência circular, a implantação não terá êxito. Por exemplo, o seguinte snippet é aceito pelo Deployment Manager, mas, se o conteúdo dos modelos causar uma dependência circular, a implantação falhará:

resources:
- name: frontend
  type: frontend.jinja
  properties:
    ip: $(ref.backend.ip)
- name: backend
  type: backend.jinja
  properties:
    ip: $(ref.frontend.ip)

Como exemplo de uma dependência circular, em que ocorre falha na implantação, suponha que frontend.jinja e backend.jinja sejam assim:

resources:
- name: {{ env['name'] }}
  type: compute.v1.instance
  properties:
    zone: us-central1-f
    ...
    networkInterfaces:
    - network: global/networks/default
      accessConfigs:
      - name: External NAT
        type: ONE_TO_ONE_NAT
    metadata:
      items:
      - key: startup-script
        value: |
          #!/bin/bash
          export IP={{ properties["ip"] }}
      ...

outputs:
- name: ip
  value: $(ref.{{ env['name'] }}.networkInterfaces[0].accessConfigs[0].natIP)

Lembre-se de que ambos os recursos usaram a propriedade de saída IP do recurso oposto:

resources:
- name: frontend
  type: frontend.jinja
  properties:
    ip: $(ref.backend.ip)
- name: backend
  type: backend.jinja
  properties:
    ip: $(ref.frontend.ip)

Porém, nenhum dos valores IP pode ser preenchido, porque ambas as propriedades dependem da existência do outro recurso, o que cria uma dependência circular. Aqui está o mesmo modelo, totalmente expandido:

resources:
- name: frontend
  type: compute.v1.instance
  properties:
    zone: us-central1-f
    ...
    networkInterfaces:
    - network: global/networks/default
      accessConfigs:
      - name: External NAT
        type: ONE_TO_ONE_NAT
    metadata:
      items:
      - key: startup-script
        value: |
          #!/bin/bash
          export IP=$(ref.backend.networkInterfaces[0].accessConfigs[0].natIP)
- name: backend
  type: compute.v1.instance
  properties:
    zone: us-central1-f
    ...
    networkInterfaces:
    - network: global/networks/default
      accessConfigs:
      - name: External NAT
        type: ONE_TO_ONE_NAT
    metadata:
      items:
      - key: startup-script
        value: |
          #!/bin/bash
          export IP=$(ref.frontend.networkInterfaces[0].accessConfigs[0].natIP)

O Deployment Manager retornará um erro se você tentar executar a configuração:

 code: u'CONDITION_NOT_MET'
 message: u'A dependency cycle was found amongst backend, frontend.'>]>

Porém, esse modelo funcionaria se:

  1. frontend.jinja tivesse criado duas instâncias de máquina virtual, vm-1 e vm-2;
  2. backend.jinja tivesse criado vm-3 e vm-4;
  3. vm-1 tivesse exposto seu IP externo como uma saída, que teria sido usada por vm-4;
  4. vm-3 tivesse exposto um IP externo como uma saída, que teria sido usada por vm-2.

A seguir