Разобрать вложенную переменную из файла YAML в bash - PullRequest
0 голосов
/ 01 марта 2019

Сложный .yaml файл из по этой ссылке необходимо ввести в сценарий bash, который запускается как часть программы автоматизации, работающей на экземпляре EC2 Amazon Linux 2. Обратите внимание, что .yamlФайл в ссылке выше содержит много объектов, и мне нужно извлечь одну из переменных среды, определенных внутри одного из множества объектов, определенных в файле.

В частности, как извлечь значение 192.168.0.0/16 переменной CALICO_IPV4POOL_CIDR в переменную bash?

        - name: CALICO_IPV4POOL_CIDR
          value: "192.168.0.0/16"

Я прочитал много других публикаций и записей в блоге о разборе более простых, более простых .yaml файлов, но ни один из этих других примеров не показывает, как извлечь вложенное значение, такое как value из CALICO_IPV4POOL_CIDR в этом вопросе.

Ответы [ 5 ]

0 голосов
/ 01 марта 2019

Поскольку другие комментируют, рекомендуется использовать yq (вместе с jq), если доступно.
Затем, пожалуйста, попробуйте следующее:

value=$(yq -r 'recurse | select(.name? == "CALICO_IPV4POOL_CIDR") | .value' "calico.yaml")
echo "$value"

Вывод:

192.168.0.0/16
0 голосов
/ 01 марта 2019

У вас есть две проблемы:

  • Как прочитать документ YAML из файла с несколькими документами
  • Как выбрать нужный ключ из этого документа YAML

Я догадался, что вам нужен документ YAML типа 'DaemonSet', прочитав ответ Грегори Нисбетта.

Я постараюсь использовать только те инструменты, которые, вероятно, уже установлены в вашей системе, посколькуВы упомянули, что хотите сделать это в скрипте Bash.Я предполагаю, что у вас есть JQ, потому что без него очень сложно сделать в Bash!

Для библиотеки YAML я склонен использовать Ruby для этого, потому что:

  • Большинство систем имеют Ruby
  • Библиотека Ruby's Psych была включена в состав начиная с Ruby 1.9
  • Библиотека PyYAML в Python немного негибкая и иногда ломается по сравнению с Ruby по моему опыту
  • Библиотека YAML в Perl часто не устанавливается по умолчанию

Это былопредложил использовать yq , но это не очень поможет в этом случае, потому что вам все еще нужен инструмент, который может извлечь документ YAML.

После извлечения документа я собираюсь сноваиспользуйте Ruby для сохранения файла в формате JSON.Затем мы можем использовать jq.

Извлечение документа YAML

Чтобы получить документ YAML с использованием Ruby и сохранить его как JSON:

url=...
curl -s $url | \
  ruby -ryaml -rjson -e \
    "puts YAML.load_stream(ARGF.read)
      .select{|doc| doc['kind']=='DaemonSet'}[0].to_json" \
  | jq . > calico.json

Дальнейшее объяснение:

  • YAML.load_stream считывает документы YAML и возвращает их все в виде массива
  • ARGF.read читает из файла, переданного через STDIN
  • Ruby'sselect позволяет легко выбирать документ YAML в соответствии с его добрым ключом
  • Затем мы берем элемент 4 и преобразуем в JSON.

Я передаю этот ответ через jq ., так чтоотформатирован для удобства чтения, но этот шаг не является действительно необходимым.Я мог бы сделать то же самое в Ruby, но я предполагаю, что вы хотите, чтобы код Ruby был минимальным.

Выбор нужной клавиши

Чтобы выбрать нужную клавишуможно использовать следующий JQ-запрос:

jq -r \
  '.spec.template.spec.containers[].env[] | select(.name=="CALICO_IPV4POOL_CIDR") | .value' \
  calico.json                                                          

Дальнейшее объяснение:

  • Первая часть spec.template.spec.containers[].env[] повторяется для всех контейнеров и для всех envs внутри них
  • Затем мы выбираем хэш, где ключ имени равен CALICO_IPV4POOL_CIDR, и возвращаем значение
  • -r удаляет кавычки вокруг строки

Собирая все вместе:

#!/usr/bin/env bash

url='https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml'

curl -s $url | \
  ruby -ryaml -rjson -e \
    "puts YAML.load_stream(ARGF.read)
      .select{|doc| doc['kind']=='DaemonSet'}[0].to_json" \
  | jq . > calico.json

jq -r \
  '.spec.template.spec.containers[].env[] | select(.name=="CALICO_IPV4POOL_CIDR") | .value' \
  calico.json

Тестирование:

▶ bash test.sh
192.168.0.0/16
0 голосов
/ 01 марта 2019

Если вы можете установить новые зависимости и планируете работать с большим количеством файлов yaml, yq - это оболочка для jq, которая может обрабатывать yaml.Это позволит безопасный (не grep) способ доступа к вложенным значениям yaml.

Использование будет выглядеть примерно так: MY_VALUE=$(yq '.myValue.nested.value' < config-file.yaml)

В качестве альтернативы, Как я могу проанализировать файл YAMLиз сценария оболочки Linux? имеет парсер только для bash, который вы можете использовать для получения значения.

0 голосов
/ 01 марта 2019

Правильный способ сделать это - использовать язык сценариев и библиотеку синтаксического анализа YAML для извлечения интересующего вас поля.

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

#!/usr/bin/env python3

import yaml

def foo():
    with open('/tmp/calico.yaml', 'r') as fil:
        docs = yaml.safe_load_all(fil)
        doc = None
        for candidate in docs:
            if candidate["kind"] == "DaemonSet":
                doc = candidate
                break
        else:
            raise ValueError("no YAML document of kind DaemonSet")
        l1 = doc["spec"]
        l2 = l1["template"]
        l3 = l2["spec"]
        l4 = l3["containers"]
        for containers_item in l4:
            l5 = containers_item["env"]
            env = l5
            for entry in env:
                if entry["name"] == "CALICO_IPV4POOL_CIDR":
                    return entry["value"]
    raise ValueError("no CALICO_IPV4POOL_CIDR entry")

print(foo())

Однако иногда вам нужно решение прямо сейчас и сценарии оболочки очень хороши в этом.

Если вы работаете с конечной точкой API, YAML обычно печатается довольно красиво, так что вы можете с легкостью извлекать текст способами, которые неработать с произвольным YAML.

Что-то вроде следующего должно быть достаточно надежным:

cat </tmp/calico.yaml | grep -A1 CALICO_IPV4POOL_CIDR | grep value: | cut -d: -f2 | tr -d ' "'

Хотя в конце стоит проверить с помощью регулярного выражения, что извлеченное значение действительно является действительной нотацией CIDR IPv4.

Ключевым моментом здесь является grep -A1 CALICO_IPV4POOL_CIDR.

Упомянутый вами двухэлементный словарь (показанный ниже) всегда будет отображаться как один блок, поскольку он является поддеревом документа YAML.

    - name: CALICO_IPV4POOL_CIDR
      value: "192.168.0.0/16"

Клавиши в calico.yaml не сортируются в алфавитном порядке в целом, но в конструкциях {"name": <something>, "value": <something else>}, name последовательно появляется до value.

0 голосов
/ 01 марта 2019
MYVAR=$(\
curl https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml | \
grep -A 1 CALICO_IPV4POOL_CIDR | \
grep value | \
cut -d ':' -f2 | \
tr -d ' "')

Замените curl https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml на то, что вы используете файл.Это получает по трубопроводу grep -A 1 CALICO_IPV4POOL_CIDR.Это дает вам 2 строки текста: строку имени и строку значения.Это передается по каналу grep value, который теперь дает нам желаемую строку только со значением.Это передается по каналу cut -d ':' -f2, который использует двоеточие в качестве разделителя и дает нам второе поле.$(...) выполняет вложенный скрипт, и он присваивается MYVAR.После этого скрипта echo $MYVAR должен выдать 192.168.0.0/16.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...