Как передать массив, разделенный запятыми, на ресурс в terraform v0.12.0?

В следующем блоке кода я пытаюсь передать массив имен серверов в блок attributes_json:

resource "aws_instance" "consul-server" {
    ami = var.consul-server
    instance_type = "t2.nano"
    key_name = var.aws_key_name
    iam_instance_profile = "dna_inst_mgmt"
    vpc_security_group_ids = [
        "${aws_security_group.yutani_consul.id}",
        "${aws_security_group.yutani_ssh.id}"
    ]
        subnet_id = "${aws_subnet.public_1_subnet_us_east_1c.id}"
        associate_public_ip_address = true
      tags = {
        Name = "consul-server${count.index}"
    }

    root_block_device {
        volume_size = "30"
        delete_on_termination = "true"
    }

    connection {
        type = "ssh"
        user = "chef"
        private_key = "${file("${var.aws_key_path}")}"
        timeout = "2m"
        agent = false
        host = self.public_ip
    }

   count = var.consul-server_count

   provisioner "chef" {
         attributes_json = <<-EOF
                {
                    "consul": {
                            "servers": ["${split(",",aws_instance.consul-server[count.index].id)}"]
                      }
                }
                EOF
        use_policyfile = true
        policy_name = "consul_server"
        policy_group = "aws_stage_enc"
        node_name       = "consul-server${count.index}"
        server_url      = var.chef_server_url
        recreate_client = true
        skip_install = true
        user_name       = var.chef_username
        user_key        = "${file("${var.chef_user_key}")}"
       version         = "14"
    }
   }

Запуск этого дает мне ошибку:

Error: Cycle: aws_instance.consul-server[1], aws_instance.consul-server[0]

(Это после объявления счетчика 2 в переменной для var.consul-server_count)

Может ли кто-нибудь сказать мне, как правильно это сделать?


person TyMac    schedule 26.05.2019    source источник


Ответы (1)


Здесь есть две проблемы: (1) как интерполировать список, разделенный запятыми, в строку JSON; и (2) Причина ошибки циклической зависимости.

Как интерполировать список для создания действительного массива JSON

Используйте jsonencode

Самый чистый метод - вообще не использовать heredoc, а просто использовать _1 _ функция.

Вы могли сделать это:

locals {
  arr = ["host1", "host2", "host3"]
}

output "test" {
  value = jsonencode(
    {
      "consul" = {
        "servers" = local.arr
      }
    })
}

И это дает как результат:

Outputs:

test = {"consul":{"servers":["host1","host2","host3"]}}

Используйте функцию соединения и heredoc

В документации Chef Provider предлагается использовать heredoc для строки JSON, поэтому вы также можете сделать это:

locals {
  arr = ["host1", "host2", "host3"]
  sep = "\", \""
}

output "test" {
  value = <<-EOF
    {
      "consul": {
        "servers": ["${join(local.sep, local.arr)}"]
      }
    }
  EOF
}

Если я применяю это:

Outputs:

test = {
  "consul": {
    "servers": ["host1", "host2", "host3"]
  }
}

Здесь есть на что обратить внимание:

  • Вы пытаетесь присоединиться к своим хостам, чтобы они стали действительными JSON в контексте массива JSON. Их нужно соединять с помощью ",", а не просто запятой. Вот почему я определил локальную переменную sep = "\", \"".

  • Кажется, вы пытаетесь split там, когда вам очевидно нужно join.

Проблема с циклической зависимостью

Причина сообщения об ошибке:

Error: Cycle: aws_instance.consul-server[1], aws_instance.consul-server[0]

Разве что у вас циклическая зависимость. Рассмотрим этот упрощенный пример:

resource "aws_instance" "example" {
  count         = 3
  ami           = "ami-08589eca6dcc9b39c"
  instance_type = "t2.micro"
  user_data     = <<-EOF
    hosts="${join(",", aws_instance.example[count.index].id)}"
  EOF
}

Или вы можете использовать обозначение splat для того же результата, то есть aws_instance.example.*.id.

Тогда план Terraform дает:

▶ terraform012 plan 
...
Error: Cycle: aws_instance.example[2], aws_instance.example[1], aws_instance.example[0]

Таким образом, вы получаете ошибку цикла, потому что aws_instance.example.*.id зависит от создаваемого aws_instance.example, поэтому ресурс зависит от самого себя. Другими словами, вы не можете использовать экспортированные значения ресурсов внутри самого ресурса.

Что делать

Я мало что знаю о Consul, но все же я немного сбит с толку, потому что вам нужны идентификаторы экземпляров EC2 в поле servers. Разве конфигурация Consul не ожидала бы там IP-адресов или имен хостов?

В любом случае вам, вероятно, потребуется вычислить имена хостов самостоятельно за пределами этого ресурса, либо как статический входной параметр, либо как что-то, что вы можете вычислить каким-либо образом. И я полагаю, у вас получится что-то вроде:

variable "host_names" {
  type    = list
  default = ["myhost1"]
}

resource "aws_instance" "consul_server" {
  ...
  provisioner "chef" {
    attributes_json = jsonencode(
      {
        "consul" = {
          "servers" = var.host_names
        }
      })
  }
}
person Alex Harvey    schedule 26.05.2019
comment
Спасибо :) Я надеялся, что смогу сделать этот список переменной в зависимости от количества экземпляров, которые я хотел развернуть. Будет ли внутри этого определения местных жителей работать вложенный цикл, чтобы обеспечить это? - person TyMac; 26.05.2019
comment
@TyMac. Ok. Я вижу здесь проблему, вы получаете циклическую зависимость, потому что идентификатор экземпляра AWS доступен только после того, как поставщик создал экземпляры. Я дополню свой ответ некоторыми предложениями, когда у меня будет время. - person Alex Harvey; 27.05.2019
comment
@TyMac, сильно расширен. Это поможет? - person Alex Harvey; 27.05.2019
comment
Очень развернутое спасибо! Причина появления поля серверов похожа на то, о чем вы догадываетесь - Chef настраивает параметр retry_join сервера консула со списком экземпляров. Я хочу объединить имена экземпляров с именами хостов, именами узлов шеф-повара и именами DNS, чтобы имена в поле retry_join отображались одинаково во всех местах, особенно при работе с членами консула. Похоже, мне следовало бы использовать что-то вроде .dns_name вместо .id. - person TyMac; 27.05.2019
comment
@TyMac, есть .public_dns, но он не поможет, потому что вы все еще не можете ссылаться на него внутри блока ресурсов. Я думаю, вам нужно заранее сообщить Terraform имена DNS. - person Alex Harvey; 27.05.2019
comment
Похоже, мне нужно основывать все, что я использую (имя узла, имя экземпляра, dns ... и т. Д.), Вне подсчета, чтобы оно оставалось динамичным, однако ... в противном случае похоже, что мне пришлось бы изменить имена в нескольких местах. Однако не знаю, как это сделать с помощью count. - person TyMac; 27.05.2019
comment
@TyMac, помогает ли это? - person Alex Harvey; 28.05.2019
comment
Кажется, что вы на правильном пути ... однако возникает ошибка: Ошибка: недопустимое значение интерполяции шаблона: невозможно включить данное значение в строковый шаблон: требуется строка. - person TyMac; 28.05.2019
comment
серверы: \\\ $ {local.consul_json} \\\ почти работает после определения блока locals: consul_json = $ {join (,, var.consul_list)} - ​​необходимо сделать это, поскольку я фактически передаю это в блок json ... к сожалению, он производит следующее: retry_join = \ consul-server01, consul-server02, consul-server03 \ - person TyMac; 28.05.2019