Анализ цен на EC2 по запросу с сайта ec2instances.info

Я пытаюсь использовать curl и jq для анализа цен по запросу AWS EC2 и создания карты JSON, подходящей для использования в модуле Terraform.

Сценарий, который я придумал, выглядит так, но он не кажется правильным:

curl --silent --show-error 'https://raw.githubusercontent.com/powdahound/ec2instances.info/master/www/instances.json' |
jq '.[]
    | .instance_type as $instance_type
    | (.pricing | keys) as $keys 
    | [.pricing[].linux.ondemand | .] as $values
    | reduce range(0; $keys|length) as $i 
        ({}; . + { ($keys[$i] + "|" + $instance_type): $values[$i] })'

Что я делаю не так? Вот небольшой пример кода, иллюстрирующий проблему:

curl --silent --show-error 'https://gist.githubusercontent.com/joshuaspence/0904a6ce25f8830d9ae2eac8fc44fc7a/raw/b24600ab2e536556a74f4dbb45e2ddaa432d430e/sample.json' |
jq '.[]
    | .instance_type as $instance_type
    | (.pricing | keys) as $keys
    | [.pricing[].linux.ondemand | .] as $values
    | reduce range(0; $keys|length) as $i
        ({}; . + { ($keys[$i] + "|" + $instance_type): $values[$i] })'

Ожидаемый результат приведенной выше команды:

{
  "ap-south-1|m1.small": "N/A",
  "us-east-1|m1.small": "0.061",
  "sa-east-1|m1.small": "0.058",
  "ap-northeast-2|m1.small": "0.058",
  "ap-southeast-2|m1.small": "0.058",
  "us-west-2|m1.small": "0.044",
  "us-gov-west-1|m1.small": "0.053",
  "us-west-1|m1.small": "0.047",
  "eu-central-1|m1.small": "N/A",
  "eu-west-1|m1.small": "0.047"
}
{
  "ap-south-1|m1.medium": "N/A",
  "us-east-1|m1.medium": "0.087",
  "ap-northeast-1|m1.medium": "0.122",
  "sa-east-1|m1.medium": "0.117",
  "ap-northeast-2|m1.medium": "N/A",
  "ap-southeast-1|m1.medium": "0.117",
  "ap-southeast-2|m1.medium": "0.117",
  "us-west-2|m1.medium": "0.087",
  "us-gov-west-1|m1.medium": "0.106",
  "us-west-1|m1.medium": "0.095",
  "us-central-1|m1.medium": "N/A",
  "us-west-1|m1.medium": "0.095"
}

Фактический результат:

{
  "ap-northeast-2|m1.small": "N/A",
  "ap-south-1|m1.small": "0.061",
  "ap-southeast-2|m1.small": "0.058",
  "eu-central-1|m1.small": "0.058",
  "eu-west-1|m1.small": "0.058",
  "sa-east-1|m1.small": "0.044",
  "us-east-1|m1.small": "0.053",
  "us-gov-west-1|m1.small": "0.047",
  "us-west-1|m1.small": "N/A",
  "us-west-2|m1.small": "0.047"
}
{
  "ap-northeast-1|m1.medium": "N/A",
  "ap-northeast-2|m1.medium": "0.087",
  "ap-south-1|m1.medium": "0.122",
  "ap-southeast-1|m1.medium": "0.117",
  "ap-southeast-2|m1.medium": "N/A",
  "eu-central-1|m1.medium": "0.117",
  "eu-west-1|m1.medium": "0.117",
  "sa-east-1|m1.medium": "0.087",
  "us-east-1|m1.medium": "0.106",
  "us-gov-west-1|m1.medium": "0.095",
  "us-west-1|m1.medium": "N/A",
  "us-west-2|m1.medium": "0.095"
}

jq
person Joshua Spence    schedule 29.06.2016    source источник


Ответы (2)


Причина, по которой ваш сценарий выдает неверный вывод, заключается в том, что объекты JSON не имеют определенного порядка по своим ключам, а встроенные функции jq нестабильны в отношении того, каков этот порядок. Это означает, что когда вы делаете (.pricing | keys) и [.pricing[].linux.ondemand | .], порядок ключей в первом не соответствует порядку значений во втором.

Упрощенная и рабочая версия вашей программы jq выглядит следующим образом:

jq '.[] | .instance_type as $it | .pricing | with_entries(.key |= "\(.)|\($it)" | .value |= .linux.ondemand)'

Эта программа jq использует with_entries для преобразования объекта JSON в массив JSON из {key, value} объектов и выполняет преобразование пар ключ-значение перед повторной сборкой исходного объекта.

person Community    schedule 30.06.2016

Вот решение, которое использует keys_unsorted для сохранения порядка ключей в исходном объекте .pricing.

    .[]
  | .instance_type as $instance_type
  | .pricing
  | [
        keys_unsorted[] as $k
      | .[$k].linux.ondemand
      | {("\($k)|\($instance_type)"): .}
    ]
  | add
person jq170727    schedule 28.08.2017