Как конвертировать (записывать) файлы hcl как tfvars?

Мы используем Terraformer для получения состояния и HCL файлов, которые затем используем в модулях Terraform для управления нашей инфраструктурой и предотвращения дрейфа. Поскольку файлы tfvars передаются в модуль как переменные, нам нужен вывод tfvars для автоматизации всего процесса. Вот простая иллюстрация:

infrastructure -> terraformer -> (HCl, statefile) -> tfvars -> terraform module -> infrastructure

Поскольку Terraformer не поддерживает вывод tfvars, я пытаюсь написать модуль Go для преобразования HCL файлов (или statefile) в .tfvars файлы. Поискав в Интернете, я наткнулся на этот вопрос и решение о переполнении стека; однако речь шла о разборе файла tfvars. В принятом ответе предлагалось использовать модуль cty в качестве парсера низкого уровня. Учитывая это, я попробовал следующее:

// ...
parser := hclparse.NewParser()
// filePath point to a hclfile in JOSON format
hclFile, diags := parser.ParseJSONFile(filePath)
attrs, diags := hclFile.Body.JustAttributes()
if diags.HasErrors() {
    log.Fatal(diags.Error())
}
vals := make(map[string]cty.Value, len(attrs))

for name, attr := range attrs {
    vals[name], diags = attr.Expr.Value(nil)
    if diags.HasErrors() {
        log.Fatal(diags.Error())
    }
}

До сих пор мне удавалось разобрать HCL на типы cty. Однако, поскольку cty не поддерживает сериализацию в файл tfvars, я попытался написать собственный сериализатор. Это правильный обходной путь? Как я мог достичь этой цели?


Вот сериализатор:

func serializeValue(value cty.Value) string {
    switch {
    case value.Type().IsPrimitiveType():
        switch value.Type() {
        case cty.String:
            return fmt.Sprintf("\"%s\"", value.AsString())
        case cty.Number:
            return value.AsBigFloat().Text('f', -1)
        case cty.Bool:
            return fmt.Sprintf("%t", value.True())
        }
    case value.Type().IsListType() || value.Type().IsTupleType():
        return serializeList(value)
    case value.Type().IsMapType() || value.Type().IsObjectType():
        return serializeMapOrObject(value)
    default:
        panic("Unhandled type")
    }
    return ""
}

func serializeMapOrObject(value cty.Value) string {
    var elements []string
    for key, val := range value.AsValueMap() {
        elements = append(elements, fmt.Sprintf("\"%s\" = %s\n", key, serializeValue(val)))
    }
    return fmt.Sprintf("{%s}", strings.Join(elements, "\n"))
}

func serializeList(value cty.Value) string {
    var elements []string
    for _, elem := range value.AsValueSlice() {
        elements = append(elements, serializeValue(elem))
    }
    return fmt.Sprintf("[%s]", strings.Join(elements, ", "))
}

Если это сработает, кого это волнует ;)

Christoph 07.07.2024 17:02
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
1
1
87
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если у вас есть cty.Value, который, как вы знаете, относится к типу объекта или карты и что все имена атрибутов/ключей являются действительными идентификаторами HCL, вы можете использовать пакет hclwrite HCL, чтобы сгенерировать что-то, что Terraform примет в качестве контента .tfvars файл.

Формат файла .tfvars Terraform — это относительно простое применение HCL, где корневое тело интерпретируется как «просто атрибуты» (в терминологии HCL), а затем для каждого атрибута оценивается выражение без доступных переменных или функций.

Вы можете сгенерировать такой файл следующим образом, с подходящим cty.Value сохраненным в переменной obj:

f := hclwrite.NewEmptyFile()
body := f.Body()
for it := obj.ElementIterator(); it.Next(); {
    k, v := it.Element()
    name := k.AsString()
    body.SetAttributeValue(name, v)
}
result := f.Bytes()

После выполнения вышеизложенного result становится []byte, содержащим контент, который вы можете записать в свой .tfvars файл.

cty не выполняет итерацию элементов карты или атрибутов объекта в каком-либо предсказуемом порядке, поэтому атрибуты в результате также будут находиться в непредсказуемом порядке. Если вы хотите гарантировать порядок, вам нужно будет сделать что-то более сложное, чем это, но я оставлю это в качестве упражнения, поскольку выше показан базовый механизм создания файла HCL, содержащего только атрибуты, значения которых константы.

Terraform принимает два разных формата для предоставления входных переменных в корневой модуль:

  • .tfvars основан на синтаксисе HCL
  • .tfvars.json основан на синтаксисе JSON

Если ваши определения входных переменных всегда генерируются машиной (никогда не поддерживаются людьми вручную), то вместо этого обычно проще сгенерировать формат .tfvars.json, поскольку большинство языков программирования имеют подходящую существующую библиотеку для преобразования значений в сопоставимые значения JSON в синтаксисе JSON.

Для файла .tfvars.json Terraform ожидает, что файл будет состоять из объекта JSON, имена свойств которого соответствуют именам входных переменных вашего корневого модуля. Значения, присвоенные этим свойствам, преобразуются в значения Terraform с использованием таблицы перевода, аналогичной функции jsondecode, а затем эти значения преобразуются в типы, объявленные для каждой переменной, с использованием аргумента type в каждом блоке variable.

Этот подход означает, что вашему генератору вообще не нужно знать синтаксис HCL, и вместо этого он может просто генерировать JSON.

Другие вопросы по теме