运维编排场景系列-----如何使用jq

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
系统运维管理,不限时长
EMR Serverless StarRocks,5000CU*H 48000GB*H
简介: 本文主要讲解在OOS如何对任务中的值进行筛选,任务中值的筛选主要依赖模版中两个常见字段,一个是ValueSelector,另一个是PropertySelector,且这两个字段必须配合jq表达式使用。

本文主要讲解在OOS如何对任务中的值进行筛选,任务中值的筛选主要依赖模版中两个常见字段,一个是ValueSelector,另一个是PropertySelector,且这两个字段必须配合jq表达式使用。那么接下来就分别讲下jq是什么,两个筛选字段何时需要定义、在哪定义和作用,以及各场景下jq表达式使用的分析。

jq介绍

jq 是一个开源的数据处理工具,它可处理JSON格式的数据,比如按照您指定的jq表达式,筛选出JSON中你想要的部分。筛选时,jq把JSON格式的数据作为input,并根据指定的jq表达式进行筛选,然后返回一个output。jq有很多灵活的使用方法,详见后附的jq语法手册。

OOS中的筛选字段

先说下OOS任务中值的筛选目的,无非两种,一种是筛出期待的结果,并将其赋值给某个参数,供其他任务使用该参数;另一种则是筛选出期待的结果,在任务中将结果值与预先指定的值进行比较,用来检查某种条件。所以OOS中有两个用来筛选的字段,分别是ValueSelector和PropertySelector,前者用在给参数赋值前,后者用在检查某个条件前。

当任务有具体Outputs时,需要在Outputs中定义ValueSelector字段,该字段用来筛选任务动作返回的值,并且将筛选结果赋值给Outputs输出的参数。具体讲,如任务的动作为API类动作,则动作返回值即任务调用API后返回的JSON值;若任务的动作为事件触发器,则动作返回值为触发器监控事件发生的JSON消息通知体。筛选方式是jq把动作返回值作为输入参数,通过字段的jq表达式进行筛选,筛选结果作为任务的Outputs输出参数值。

当任务的动作为ACS::WaitFor 或ACS::CheckFor时,需要在任务的Properties部分定义PropertySelector字段,
该字段用来筛选任务调用API返回的JSON值,筛选方式是jq把API返回的JSON作为输入参数,并通过字段的jq表达式进行筛选,最后OOS将筛选结果与Properties中的DesiredValues或NotDesiredValues字段内包含的值进行比较,比较结果作为任务等待或判断的参考,进而执行后面的逻辑。

jq在OOS中的使用

下面带大家分析下一些公共模版中jq表达式的使用。

ValueSelector

场景1

FormatVersion: OOS-2019-06-01
Description: Adds backend servers by specifying tag.
Parameters:
  loadBalancerId:
    Description: The ID of the SLB instance.
    Type: String
    AllowedPattern: lb-[A-Za-z0-9]*
    MaxLength: 30
    MinLength: 1
  tagKey:
    Description: The specific tag key.
    Type: String
    MinLength: 1
  tagValue:
    Description: The specific tag value.
    Type: String
    MaxLength: 30
    MinLength: 1
  OOSAssumeRole:
    Description: The RAM role to be assumed by OOS.
    Type: String
    Default: OOSServiceRole
RamRole: '{{ OOSAssumeRole }}'
Tasks:
- Name: describeRunningInstancesByTag
  Action: ACS::ExecuteApi
  Description: Views the ECS instances by specifying tag.
  Properties:
    Service: ECS
    API: DescribeInstances
    Parameters:
      Status: Running
      Tags:
      - Key: '{{ tagKey }}'
        Value: '{{ tagValue }}'
  Outputs:
    instanceIds:
      Type: List
      ValueSelector: .Instances.Instance[].InstanceId

我们来看下上面的模版,请直接看Tasks部分的describeRunningInstancesByTag任务,该任务是有具体Outputs的,其定义了输出名称为instanceIds的参数,参数Type为List,同时也定义了ValueSelector字段,并且该字段的jq表达式为.Instances.Instance[].InstanceId
就如Outputs中参数名 instanceIds 字面意思,其表示输出API返回结果中所有ECS实例ID,那么该任务调用的API是DescribeInstances,API返回的JSON结果样例如下。结合此JSON样例,我们看下在.Instances.Instance[].InstanceId作用下,instanceIds最终返回值的获取过程。首先筛选出JSON中Key为Instances的结果,然后对结果继续筛选,获取Key为Instance的结果,该结果是一个数组,此时又对所得结果进行迭代,并将每个迭代结果中Key为InstanceId的值以List类型进行返回,于是得到最终返回值["id-001","id-002"],即'{{describeRunningInstancesByTag.instanceIds}}'的值为["id-001","id-002"]'。

API调用返回样例(JSON)

{
    "PageNumber":"1",
    "TotalCount":"1",
    "PageSize":"10",
    "RequestId":"14A07460-EBE7-47CA-9757-12CC4761D47A",
    "Instances":{
        "Instance":[
            {
                "ImageId":"centos6u5_64_20G_aliaegis_20150130.vhd",
                "InnerIpAddress":{
                    "IpAddress":[
                        "10.170.XX.XXX"
                    ]
                },
                "InstanceId":"id-001",
                "EipAddress":{},
                "InternetMaxBandwidthIn":"-1",
                "ZoneId":"cn-shenzhen-a",
                "InstanceNetworkType":"classic",
                "PublicIpAddress":{
                    "IpAddress":[
                        "120.25.XX.XXX"
                    ]
                },
                "InternetChargeType":"PayByTraffic",
                "HostName":"iZ94t3s0j***",
                "InstanceType":"ecs.s2.large",
                "SerialNumber":"51d1353b-22bf-4567-a176-8b3e12e43135",
                "IoOptimized":"false",
                "CreationTime":"2015-07-27T07:08Z",
                "Status":"Running",
                "VpcAttributes":{
                    "PrivateIpAddress":{
                        "IpAddress":[]
                    }
                },
                "InternetMaxBandwidthOut":"1",
                "SecurityGroupIds":{
                    "SecurityGroupId":[
                        "sg-94kd0c***"
                    ]
                },
                "RegionId":"cn-shenzhen",
                "OperationLocks":{
                    "LockReason":[]
                },
                "InstanceChargeType":"PostPaid",
                "ExpiredTime":"2011-09-08T16:00Z",
                "InstanceName":"FinanceJoshua1"
            },            
      {
                "ImageId":"centos6u5_64_20G_aliaegis_20150130.vhd",
                "InnerIpAddress":{
                    "IpAddress":[
                        "10.170.XX.XXX"
                    ]
                },
                "InstanceId":"id-002",
                "EipAddress":{},
                "InternetMaxBandwidthIn":"-1",
                "ZoneId":"cn-shenzhen-a",
                "InstanceNetworkType":"classic",
                "PublicIpAddress":{
                    "IpAddress":[
                        "120.25.XX.XXX"
                    ]
                },
                "InternetChargeType":"PayByTraffic",
                "HostName":"iZ94t3s0j***",
                "InstanceType":"ecs.s2.large",
                "SerialNumber":"51d1353b-22bf-4567-a176-8b3e12e43135",
                "IoOptimized":"false",
                "CreationTime":"2015-07-27T07:08Z",
                "Status":"Running",
                "VpcAttributes":{
                    "PrivateIpAddress":{
                        "IpAddress":[]
                    }
                },
                "InternetMaxBandwidthOut":"1",
                "SecurityGroupIds":{
                    "SecurityGroupId":[
                        "sg-94kd0c***"
                    ]
                },
                "RegionId":"cn-shenzhen",
                "OperationLocks":{
                    "LockReason":[]
                },
                "InstanceChargeType":"PostPaid",
                "ExpiredTime":"2011-09-08T16:00Z",
                "InstanceName":"FinanceJoshua2"
            }
        ]
    }
}

场景2

FormatVersion: OOS-2019-06-01
Description: Resets system disks to its initial state by specifying instance IDs.
  The specified ECS instances must be in stopped status.
Parameters:
  instanceIds:
    Description: The ID list of the ECS instance.
    Type: List
  maxErrors:
    Description: The maximum number of errors allowed during task execution.
    Type: Number
    Default: 0
  concurrency:
    Description: Concurrency ratio of task execution.
    Type: Number
    Default: 1
  OOSAssumeRole:
    Description: The RAM role to be assumed by OOS.
    Type: String
    Default: OOSServiceRole
RamRole: '{{ OOSAssumeRole }}'
Tasks:
- Name: checkInstanceReady
  Action: ACS::CheckFor
  Description: Checks ECS instances is in the stopped status.
  Properties:
    Service: ECS
    API: DescribeInstances
    Parameters:
      InstanceIds:
      - '{{ ACS::TaskLoopItem }}'
    DesiredValues:
    - Stopped
    PropertySelector: Instances.Instance[].Status
  Loop:
    MaxErrors: '{{ maxErrors }}'
    Concurrency: '{{ concurrency }}'
    Items: '{{ instanceIds }}'
- Name: querySystemDisks
  Action: ACS::ExecuteAPI
  Description: Views system disk of the ECS instance.
  Properties:
    Service: ECS
    API: DescribeDisks
    Parameters:
      InstanceId: '{{ ACS::TaskLoopItem }}'
      DiskType: system
  Loop:
    Items: '{{ instanceIds }}'
    Outputs:
      diskIds:
        AggregateType: Fn::ListJoin
        AggregateField: diskId
  Outputs:
    diskId:
      Type: String
      ValueSelector: .Disks.Disk[].DiskId

我们来看下上面的模版,请直接看Tasks部分的querySystemDisks任务,该任务是有具体Outputs的,其定义了输出名称为 diskId的参数,参数Type为 String ,同时也定义了ValueSelector字段,并且该字段的jq表达式为 .Disks.Disk[].DiskId 。
就如Outputs中参数名diskId字面意思,其表示输出API返回结果中的磁盘ID,那么该任务调用的API是DescribeDisks,API返回的JSON结果样例如下。结合此JSON样例,我们看下在 .Disks.Disk[].DiskId 作用下,diskId最终返回值的获取过程。首先筛选出JSON中Key为Disks的结果,然后对结果继续筛选,获取Key为Disk的结果,该结果是一个数组,此时又对所得结果进行迭代,并将每个迭代结果中Key为DiskId的值以String类型进行返回,于是得到最终返回值 'd-28m5zb1sz',即'{{querySystemDisks.diskId}}'的值为'd-28m5zb1sz'。

上面可能有疑问,该场景的jq表达式和场景一的类似,且处理的数据结构也一样,为何此处最后只返回了一个String,而不像场景一返回了List。简单解释下,其实返回参数的Type是不同的,当Type为List时,OOS最后会把返回的一个或多个结果装到List中作为最终结果返回;当Type为String时,OOS最后会取返回的一个或多个结果中的第一个来作为最终结果。

API调用返回样例(JSON)

{
    "PageNumber":1,
    "TotalCount":9,
    "PageSize":2,
    "RequestId":"ACD9BBB0-A9D1-46D7-9630-B7A69889E110",
    "Disks":{
        "Disk":[
            {
                "ImageId":"",
                "Description":"",
                "Device":"",
                "ProductCode":"",
                "Portable":true,
                "DetachedTime":"2014-07-23T08:28:48Z",
                "Type":"data",
                "InstanceId":"",
                "ZoneId":"cn-qingdao-b",
                "EnableAutoSnapshot":false,
                "DiskName":"",
                "AttachedTime":"2014-07-23T07:47:35Z",
                "SourceSnapshotId":"",
                "CreationTime":"2014-07-23T02:44:07Z",
                "Status":"Available",
                "DeleteAutoSnapshot":true,
                "Category":"cloud",
                "RegionId":"cn-qingdao",
                "DeleteWithInstance":false,
                "OperationLocks":{
                    "OperationLock":[]
                },
                "DiskId":"d-28m5zb1sz",
                "Size":5
            },
            {
                "ImageId":"",
                "Description":"",
                "Device":"",
                "ProductCode":"",
                "Portable":true,
                "DetachedTime":"",
                "Type":"data",
                "InstanceId":"",
                "ZoneId":"cn-qingdao-b",
                "EnableAutoSnapshot":false,
                "DiskName":"",
                "AttachedTime":"",
                "SourceSnapshotId":"",
                "CreationTime":"2014-07-23T02:44:06Z",
                "Status":"Available",
                "DeleteAutoSnapshot":true,
                "Category":"cloud",
                "RegionId":"cn-qingdao",
                "DeleteWithInstance":false,
                "OperationLocks":{
                    "OperationLock":[]
                },
                "DiskId":"d-28zfrmo13",
                "Size":5
            }
        ]
    }
}

场景3

---
FormatVersion: OOS-2019-06-01
Description: Start ECS instance when instance is stopped.
Parameters:
  accessToken:
    Description: Dingtalk access_token to be notified.
    Type: String
    AllowedPattern: '[A-Za-z0-9]*'
  regionId:
    Description: The Region Id of SLB and Instance.
    Type: String
    MinLength: 1
    MaxLength: 30
  OOSAssumeRole:
    Description: The RAM role to be assumed by OOS.
    Type: String
    Default: OOSServiceRole
  loadBalancerId:
    Description: The ID of the SLB instance.
    Type: String
    AllowedPattern: lb-[A-Za-z0-9]*
    MaxLength: 30
    MinLength: 1
RamRole: '{{OOSAssumeRole}}'
Tasks:
  - Name: whenInstanceStopped
    Action: 'ACS::EventTrigger'
    Properties:
      Product: ECS
      Name:
        - 'Instance:StateChange'
      Level:
        - INFO
      Content:
        state:
          - Stopped
    Outputs:
      instanceId:
        ValueSelector: content.resourceId
        Type: String
  - Name: checkNeedRemoveOrNot
    Action: ACS::CheckFor
    Description: check the interrupted Instance is being added on SLB or Not.
    OnError: ACS::END
    OnSuccess: ACS::NEXT
    Properties:
      Service: SLB
      API: DescribeLoadBalancerAttribute
      Parameters:
        RegionId: '{{ regionId }}'
        LoadBalancerId: '{{ loadBalancerId }}'
      DesiredValues:
        - "{{whenInstanceStopped.instanceId}}"
      PropertySelector: 'BackendServers.BackendServer[].ServerId|select(.=="{{whenInstanceStopped.instanceId}}")'
  - Name: queryLoadBalancerAttribute
    Action: ACS::ExecuteApi
    Description: Views ECS instances to backend servers.
    Properties:
      Service: SLB
      API: DescribeLoadBalancerAttribute
      Parameters:
        RegionId: '{{ regionId }}'
        LoadBalancerId: '{{ loadBalancerId }}'
    Outputs:
      removeOrNot:
        Type: List
        ValueSelector: 'BackendServers.BackendServer[]|select(.ServerId=="{{whenInstanceStopped.instanceId}}")'
      backendServersToRemove:
        Type: List
        ValueSelector: 'BackendServers.BackendServer[]|select(.ServerId=="{{whenInstanceStopped.instanceId}}")|del(.Type)'

我们来看下上面的模版,先看Tasks部分的whenInstanceStopped任务,它会输出一个名为 instanceId 的参数,关于这个任务知道这些就够了。然后看Tasks部分的 queryLoadBalancerAttribute 任务,该任务是有具体Outputs的,比如其定义了输出名称为 backendServersToRemove 的参数,参数Type为 List ,同时也在参数中定义了ValueSelector字段,并且该字段的jq表达式为 .BackendServers.BackendServer[]|
select(.ServerId=="{{whenInstanceStopped.instanceId}}")|del(.Type) ,表达式好长,没关系,我们分析完,您就会发现它很简单。
就如Outputs中参数名backendServersToRemove字面意思,其表示输出API返回结果中的后端服务器列表,那么该任务调用的API是DescribeLoadBalancerAttribute,API返回的JSON结果样例如下。结合此JSON样例,我们看下在这个很长的jq表达式作用下,backendServersToRemove最终返回值的获取过程。首先筛选出JSON中Key为BackendServers的结果,然后对结果继续筛选,获取Key为BackendServer的结果,该结果是一个数组,此时对所得结果进行迭代,然后我们看到的管道符 | ,也就是把迭代获取的所有结果交到下一段操作,操作是,将每个迭代结果中Key为ServerId的值与whenInstanceStopped任务输出参数instanceId的值进行比较。如果比较结果是相等,则拿到该ServerId所在的这个Mapping,并将该Mapping中Key为的Type的key-val对剔除,再把该Mapping返回;如果比较结果为不相等,则忽略掉这个ServerId所在的Mapping。比较完后做下整理把所有返回的Mapping放在一个List中返回。假如whenInstanceStopped.instanceId值是"i-bp1234",得到最终返回值则为 [{"ServerId":"i-bp1234","Weight":100 }] ,即'{{queryLoadBalancerAttribute.backendServersToRemove}}'的值为[{"ServerId":"i-bp1234","Weight":100 }]

API调用返回样例(JSON)

{
    "CreateTimeStamp":1541679713000,
    "RegionIdAlias":"cn-hangzhou",
    "HasReservedInfo":"false",
    "BackendServers":{
        "BackendServer":[
            {
                "ServerId":"i-bpxccv123456jo7v",
                "Weight":100,
                "Type":"ecs"
            }
        ]
    },
    "ListenerPorts":{
        "ListenerPort":[]
    },
    "VSwitchId":"vsw-bp1ccv123456jk9bmlj",
    "InternetChargeType":"paybytraffic",
    "VpcId":"vpc-bp1ccv123456j1qc5cm",
    "SlaveZoneId":"cn-hangzhou-d",
    "NetworkType":"vpc",
    "LoadBalancerSpec":"slb.s2.small",
    "ListenerPortsAndProtocol":{
        "ListenerPortAndProtocol":[]
    },
    "PayType":"PayOnDemand",
    "Bandwidth":5120,
    "LoadBalancerName":"abc1",
    "ResourceGroupId":"rg-acfmxazb4ph6aiy",
    "AddressIPVersion":"ipv4",
    "LoadBalancerId":"lb-bp23456jfuca5",
    "EndTimeStamp":32493801600000,
    "MasterZoneId":"cn-hangzhou-b",
    "ListenerPortsAndProtocal":{
        "ListenerPortAndProtocal":[]
    },
    "Address":"192.168.0.6",
    "RegionId":"cn-hangzhou",
    "RequestId":"35D745B3-1567-4855-9EF1-F5CED3C1670C",
    "CreateTime":"2018-11-08T12:21:53Z",
    "EndTime":"2999-09-08T16:00:00Z",
    "AddressType":"intranet",
    "LoadBalancerStatus":"active"
}

PropertySelector

场景1(CheckFor)

FormatVersion: OOS-2019-06-01
Description: Creates a disk and attaches a data disk to an ECS instance.
Parameters:
  diskName:
    Description: The name of the disk.
    Type: String
  zoneId:
    Description: The zone ID that is available in the specified region.
    Type: String
  diskCategory:
    Description: The category of the data disk.
    Type: String
  instanceId:
    Description: Creates a subscription disk and the system automatically attaches
      it to a subscription instance with the InstanceId you specified.
    Type: String
    AllowedPattern: i-[A-Za-z0-9]*
    MinLength: 1
    MaxLength: 30
  size:
    Description: The size of the disk.
    Type: String
Tasks:
- Name: checkInstanceReady
  Action: ACS::CheckFor
  Description: Checks whether the ECS instance status is running or stopped.
  Properties:
    Service: ECS
    API: DescribeInstances
    Parameters:
      InstanceIds:
      - '{{ instanceId }}'
    DesiredValues:
    - Running
    - Stopped
    PropertySelector: .Instances.Instance[].Status

我们来看下上面的模版,请直接看Tasks部分的checkInstanceReady任务,该任务的动作是 ACS::CheckFor ,其Properties部分定义了DesiredValues的值为 ['Running','Stopped'] ,同时也定义了PropertySelector字段,并且该字段的jq表达式为 .Instances.Instance[].Status 。
该任务中,DesiredValues和PropertySelector两个字段会被联合起来,用来验证指定的条件即['Running','Stopped'],验证过程为先把API返回的JSON结果通过jq表达式筛选处理,再检查处理得到的筛选结果是否在DesiredValues字段的List中。如果在List中,则该任务执行结果为Success,否则该任务执行结果为Failed。
那么该任务调用的API是DescribeInstances,API返回的JSON结果样例如下。结合此JSON样例,我们看下在 .Instances.Instance[].Status 作用下,最终返回值的获取过程。首先筛选出JSON中Key为Instances的结果,然后对结果继续筛选,获取Key为Instance的结果,该结果是一个数组,此时对所得结果进行迭代,迭代获取数组中的第一个元素,并将该元素中Key为 Status 的值作为与指定的条件比较的值,即检查'Running'是否在['Running','Stopped']中。

API调用返回样例(JSON)

{
    "PageNumber":"1",
    "TotalCount":"1",
    "PageSize":"10",
    "RequestId":"14A07460-EBE7-47CA-9757-12CC4761D47A",
    "Instances":{
        "Instance":[
            {
                "ImageId":"centos6u5_64_20G_aliaegis_20150130.vhd",
                "InnerIpAddress":{
                    "IpAddress":[
                        "10.170.XX.XXX"
                    ]
                },
                "InstanceId":"id-001",
                "EipAddress":{},
                "InternetMaxBandwidthIn":"-1",
                "ZoneId":"cn-shenzhen-a",
                "InstanceNetworkType":"classic",
                "PublicIpAddress":{
                    "IpAddress":[
                        "120.25.XX.XXX"
                    ]
                },
                "InternetChargeType":"PayByTraffic",
                "HostName":"iZ94t3s0j***",
                "InstanceType":"ecs.s2.large",
                "SerialNumber":"51d1353b-22bf-4567-a176-8b3e12e43135",
                "IoOptimized":"false",
                "CreationTime":"2015-07-27T07:08Z",
                "Status":"Running",
                "VpcAttributes":{
                    "PrivateIpAddress":{
                        "IpAddress":[]
                    }
                },
                "InternetMaxBandwidthOut":"1",
                "SecurityGroupIds":{
                    "SecurityGroupId":[
                        "sg-94kd0c***"
                    ]
                },
                "RegionId":"cn-shenzhen",
                "OperationLocks":{
                    "LockReason":[]
                },
                "InstanceChargeType":"PostPaid",
                "ExpiredTime":"2011-09-08T16:00Z",
                "InstanceName":"FinanceJoshua1"
            },            
      {
                "ImageId":"centos6u5_64_20G_aliaegis_20150130.vhd",
                "InnerIpAddress":{
                    "IpAddress":[
                        "10.170.XX.XXX"
                    ]
                },
                "InstanceId":"id-002",
                "EipAddress":{},
                "InternetMaxBandwidthIn":"-1",
                "ZoneId":"cn-shenzhen-a",
                "InstanceNetworkType":"classic",
                "PublicIpAddress":{
                    "IpAddress":[
                        "120.25.XX.XXX"
                    ]
                },
                "InternetChargeType":"PayByTraffic",
                "HostName":"iZ94t3s0j***",
                "InstanceType":"ecs.s2.large",
                "SerialNumber":"51d1353b-22bf-4567-a176-8b3e12e43135",
                "IoOptimized":"false",
                "CreationTime":"2015-07-27T07:08Z",
                "Status":"Running",
                "VpcAttributes":{
                    "PrivateIpAddress":{
                        "IpAddress":[]
                    }
                },
                "InternetMaxBandwidthOut":"1",
                "SecurityGroupIds":{
                    "SecurityGroupId":[
                        "sg-94kd0c***"
                    ]
                },
                "RegionId":"cn-shenzhen",
                "OperationLocks":{
                    "LockReason":[]
                },
                "InstanceChargeType":"PostPaid",
                "ExpiredTime":"2011-09-08T16:00Z",
                "InstanceName":"FinanceJoshua2"
            }
        ]
    }
}

场景2(WaitFor)

FormatVersion: OOS-2019-06-01
Description: Creates a custom image.
Parameters:
  imageName:
    Description: The image name.
    Type: String
  instanceId:
    Description: The ID of the instance.
    Type: String
    AllowedPattern: i-[A-Za-z0-9]*
    MinLength: 1
    MaxLength: 30
Tasks:
- Name: checkInstanceReady
  Action: ACS::CheckFor
  Description: Checks whether the ECS instance status is running or stopped.
  Properties:
    Service: ECS
    API: DescribeInstances
    Parameters:
      InstanceIds:
      - '{{ instanceId }}'
    DesiredValues:
    - Running
    - Stopped
    PropertySelector: Instances.Instance[].Status
- Name: createImage
  Action: ACS::ExecuteAPI
  Description: Creates a custom image.
  Properties:
    Service: ECS
    API: CreateImage
    Parameters:
      ImageName: '{{ imageName }}'
      InstanceId: '{{ instanceId }}'
  Outputs:
    imageId:
      Type: String
      ValueSelector: ImageId
- Name: untilImageReady
  Action: ACS::WaitFor
  Description: Waits for the image to be available.
  Properties:
    Service: ECS
    API: DescribeImages
    Parameters:
      ImageId: '{{ createImage.imageId }}'
    DesiredValues:
    - Available
    PropertySelector: .Images.Image[].Status

我们来看下上面的模版,请直接看Tasks部分的untilImageReady任务,该任务的动作是 ACS::WaitFor ,其Properties部分定义了DesiredValues的值为 ['Available'] ,同时也定义了PropertySelector字段,并且该字段的jq表达式为 .Images.Image[].Status 。
该任务中,DesiredValues和PropertySelector两个字段会被联合起来,用来验证指定的条件即['Available'],验证过程为先把API返回的JSON结果通过jq表达式筛选处理,再检查处理得到的筛选结果是否在DesiredValues字段的List中。如果在List中,则该任务不再继续等待,并结束该任务向下执行;否则该任务会隔段时间再次调用API,并检查结果是否满足条件,直至条件满足或超出最大检查次数。
那么该任务调用的API是DescribeImages,API返回的JSON结果样例如下。结合此JSON样例,我们看下在 .Images.Image[].Status作用下,最终返回值的获取过程。首先筛选出JSON中Key为Images的结果,然后对结果继续筛选,获取Key为Image的结果,该结果是一个数组,此时对所得结果进行迭代,迭代获取数组中的第一个元素,并将该元素中Key为 Status 的值作为与指定的条件比较的值,即检查'Available'是否在['Available']中。

API调用返回样例(JSON)

{
    "PageNumber":1,
    "TotalCount":24,
    "PageSize":1,
    "RequestId":"49CBCED4-C9B9-4851-BEB5-8FB5E5169E30",
    "RegionId":"cn-hangzhou",
    "Images":{
        "Image":[
            {
                "ImageId":"suse11sp3_64_20G_aliaegis_20150428.vhd",
                "OSType":"linux",
                "Architecture":"x86_64",
                "OSName":"SUSE Linux  Enterprise Server 11 SP3 64位",
                "DiskDeviceMappings":{
                    "DiskDeviceMapping":[
                        {
                            "Device":"/dev/xvda",
                            "Size":"20"
                        }
                    ]
                },
                "ImageOwnerAlias":"system",
                "Progress":"100%",
                "Usage":"instance",
                "CreationTime":"2015-05-06T09:01:32Z",
                "Status":"Available",
                "ImageVersion":"1",
                "ImageName":"suse11sp3_64_20G_aliaegis_20150428.vhd",
                "IsCopied":false,
                "IsSubscribed":false,
                "Platform":"SUSE",
                "Size":20
            }
        ]
    }
}

jq语法手册

基础用法

通过Key筛选

jq表达式:
".foo"
input:

{"foo": 42, "bar": "less interesting data"}

output:
42

通过Index筛选

jq表达式:
".[0]"
input:

[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]

output:

{
  "name": "JSON",
  "good": true
}

通过Iterator筛选

jq表达式:
".[]"
input:

["hello","world"]

output:

"hello"  
"world"

jq表达式:
".[]"
input:

[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]

output:

{
  "name": "JSON",
  "good": true
}
{
  "name": "XML",
  "good": false
}

通过Pipe("|")筛选

jq表达式:
".[]|.name"
input:

[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]

output:  

"JSON"
"XML"

构造Array

jq表达式:
"[.user, .projects[]]"
input:

{"user":"stedolan", "projects": ["jq", "wikiflow"]}

output:

[
  "stedolan",
  "jq",
  "wikiflow"
]

构造Mapping

jq表达式:
"{user, title: .titles}"
input:

{"user":"stedolan","titles":["JQ Primer", "More JQ"]}

output:

{
  "user": "stedolan",
  "title": [
    "JQ Primer",
    "More JQ"
  ]
}

jq表达式:
"{(.user): .titles}"
input:

{"user":"stedolan","titles":["JQ Primer", "More JQ"]}

output:

{
  "stedolan": [
    "JQ Primer",
    "More JQ"
  ]
}

内建操作符和函数

Addition(+)

jq表达式:
".a + 1"
input:

{"a": 7}

output:

8

jq表达式:
".a + .b"
input:

{"a": [1,2], "b": [3,4]}

output:

[
  1,
  2,
  3,
  4
]

Subtraction(-)

jq表达式:
". - ["xml", "yaml"]"
input:

["xml", "yaml", "json"]

output:

[
  "json"
]

Length

jq表达式:
"[.[]| length]"
input:

[[1,2], "string", {"a":2}, null]

output:

[
  2,
  6,
  1,
  0
]

Keys

jq表达式:
"keys"
input:

{"abc": 1, "abcd": 2, "Foo": 3}

output:

[
  "Foo",
  "abc",
  "abcd"
]

In

jq表达式:
'.[] | in({"foo": 42})'
input:

["foo", "bar"]

output:

true
false

map

jq表达式:
"map(.+1)"
input:

[1,2,3]

output:

[
  2,
  3,
  4
]

map_values

jq表达式:
"map_values(.+1)"
input:

{"a": 1, "b": 2, "c": 3}

output:

{
  "a": 2,
  "b": 3,
  "c": 4
}

del

jq表达式:
"del(.foo)"
input:

{"foo": 42, "bar": 9001, "baz": 42}

output:

{
  "bar": 9001,
  "baz": 42
}

select

jq表达式:
'.[] | select(.id == "second")'
input:

[{"id": "first", "val": 1}, {"id": "second", "val": 2}]

output:

{
  "id": "second",
  "val": 2
}

jq表达式:
"map(select(. >= 2))"
input:

[1,5,3,0,7]

output:

[
  5,
  3,
  7
]

any

jq表达式:
"any"
input:

[true, false]

output:

true

jq表达式:
"any"
input:

[false, false]

output:

false

all

jq表达式:
"all"
input:

[true, false]

output:

false

jq表达式:
"all"
input:

[true, true]

output:

true

jq表达式:
"all"
input:

[]

output:

true

jq表达式:
"all"
input:

[]

output:

true

min、max

jq表达式:
"min"
input:

[5,4,2,7]

output:

2

sort、sort_by

jq表达式:
"sort"
input:

[8,3,null,6]

output:

[
  null,
  3,
  6,
  8
]

jq表达式:
"sort_by(.foo)"
input:

[{"foo":4, "bar":10}, {"foo":3, "bar":100}, {"foo":2, "bar":1}]

output:

[
  {
    "foo": 2,
    "bar": 1
  },
  {
    "foo": 3,
    "bar": 100
  },
  {
    "foo": 4,
    "bar": 10
  }
]

index

jq表达式:
'index(",  ")'
input:

"a,b,  cd, efg, hijk"

output:

3

split

jq表达式:
'split(", ")'
input:

"a, b,c,d, e, "

output:

[
  "a",
  "b,c,d",
  "e",
  ""
]

join

jq表达式:
'join(", ")'
input:

["a","b,c,d","e"]

output:

"a, b,c,d, e"

jq表达式:
'join("   ")'
input:

["a",1,2.3,true,null,false]

output:

"a   1   2.3   true      false"

条件判断

if-then-else

jq表达式:

 'if . == 0 then  "zero"elif . == 1 then "one"else  "many"end'     

input:

2

output:

"many"

>, <,>=, <=,==,!=

jq表达式:

 '.<5'     
input:

2

output:

true

jq表达式:
 '.==5'     
input:

2

output:

false

高级用法

变量

jq表达式:
".bar as $x | .foo | . + $x"
input:

{"foo":10, "bar":200}

output:

210

jq表达式:
". as $i|[(.*2|. as $i| $i), $i]"
input:

5

output:

[
  10,
  5
]

自定义函数

jq表达式:
"def addvalue(f): . + [f]; map(addvalue(.[0]))"
input:

[[1,2],[10,20]]

output:

[
  [
    1,
    2,
    1
  ],
  [
    10,
    20,
    10
  ]
]

jq表达式:
"def addvalue(f): f as $x | map(. + $x); addvalue(.[0])"
input:

[[1,2],[10,20]]

output:

[
  [
    1,
    2,
    1,
    2
  ],
  [
    10,
    20,
    1,
    2
  ]
]

 

目录
相关文章
|
16天前
|
运维 自然语言处理 开发者
作为一名运维人员,使用通义灵码个人版处理日常工作中的代码相关任务,极大地提升了我的工作效率。以下是我使用通义灵码的具体实践场景、效果和心得,以及相应的截图。
作为一名运维人员,我使用通义灵码处理日常工作中的代码任务,效率提升了30%。通义灵码帮助我快速理解复杂代码、生成准确的代码注释,并能从自然语言生成代码示例,大幅减少了代码编写和理解的时间。
49 3
|
2月前
|
机器学习/深度学习 人工智能 运维
|
3月前
|
存储 边缘计算 运维
边缘计算问题之OpenYurt 对边缘计算场景中的运维难题如何解决
边缘计算问题之OpenYurt 对边缘计算场景中的运维难题如何解决
38 1
|
3月前
|
运维 监控 测试技术
5个常见运维场景,用这几个Python脚本就够了!
5个常见运维场景,用这几个Python脚本就够了!
|
4月前
|
运维 监控 容灾
智能化运维场景分析
【7月更文挑战第12天】智能运维目标是解放运维人员,提高效率,确保业务连续性和优化资源利用。
|
6月前
|
运维 算法 物联网
五大智能运维场景
【5月更文挑战第3天】智能运维场景分5类:异常检测、根因诊断、故障自愈、事件预警、效能优化。
|
6月前
|
弹性计算 运维 应用服务中间件
带你读《云上自动化运维宝典》——ECS多场景迁移上云最佳实践(1)
带你读《云上自动化运维宝典》——ECS多场景迁移上云最佳实践(1)
280 1
|
6月前
|
弹性计算 运维 应用服务中间件
带你读《云上自动化运维宝典》——ECS多场景迁移上云最佳实践(2)
带你读《云上自动化运维宝典》——ECS多场景迁移上云最佳实践(2)
251 1
|
6月前
|
弹性计算 运维 Linux
带你读《云上自动化运维宝典》——ECS多场景迁移上云最佳实践(3)
带你读《云上自动化运维宝典》——ECS多场景迁移上云最佳实践(3)
247 1
|
NoSQL 测试技术 API
从程序员到架构师开发运维场景实战篇:一人一套测试环境
一人一套测试环境 本篇开始讲第16次架构经历:一人一套测试环境。同样,先介绍业务场景。 业务场景:测试环境何时能释放出来使用 当时,公司的基础设施使用的是虚拟机,而且还未迁移到容器。