Synthetic CI/CD テスト

Synthetic CI/CD テスト

事前定義された間隔でテストを実行するだけでなく、API エンドポイントを使用して Datadog Synthetic テストをオンデマンドで実行することもできます。Datadog Synthetic テストを継続的インテグレーション (CI) パイプラインで実行して、製品を破壊する恐れのあるブランチのデプロイをブロックできます。

Synthetic CI/CD テストを使用して、CD プロセスの一部としてテストを実行し、デプロイが完了した直後に本番アプリケーションの状態を評価することもできます。これにより、ユーザーに影響を与える可能性のある潜在的な回帰を検出し、重要なテストが失敗したときに自動的にロールバックをトリガーできます。

この関数により、本番システムの問題修正に時間を割く必要がなくなり、プロセスの早期でバグと回帰を検出できます。

これらの API エンドポイントに加え、Datadog ではコマンドラインインターフェース (CLI) を提供および管理しているため、Datadog Synthetic テストを CI ツールに容易に統合できます。Synthetic CI/CD テストはオープンソースで、そのソースコードは GitHub の DataDog/datadog-ci から入手できます。

API の使用

トリガーエンドポイントから、トリガーされたチェックのリストとその結果識別子が提供されます。完全なテスト結果が入手可能な場合は、ポーリングエンドポイントから入手できます。

トリガーテストエンドポイント

エンドポイントをトリガーするテストは、1 回のリクエストで最大 50 件のテスト実行に対応します。

  • Endpoint: https://api./api/v1/synthetics/tests/trigger/ci
  • Method: POST
  • Argument: トリガーする全テストのリストと各テストのコンフィギュレーションオーバーライドを含む JSON オブジェクト。

リクエストデータの構造

{
    "tests": [TEST_TO_TRIGGER, TEST_TO_TRIGGER, ...]
}

TEST_TO_TRIGGER オブジェクトは、トリガーするテストの public_id (必須) と任意のコンフィギュレーションオーバーライドから構成されます (各フィールドの説明については下記を参照してください)。

テストの公開識別子は、テスト詳細ページの URL の一部であるテストの識別子 (https://app.datadoghq.com/synthetics/details/abc-def-ghi の場合は abc-def-ghi) か、詳細ページへの URL 全体 (https://app.datadoghq.com/synthetics/details/abc-def-ghi) のいずれかになります。

リクエスト例

#!/bin/sh

api_key="<DATADOG_API_KEY>"
app_key="<DATADOG_APPLICATION_KEY>"

curl -X POST \
-H 'Content-Type: application/json' \
-H "DD-API-KEY: ${api_key}" \
-H "DD-APPLICATION-KEY: ${app_key}" \
-d '{
    "tests": [
        {
            "public_id": "abc-def-ghi",
            "allowInsecureCertificates": true,
            "basicAuth": { "username": "test", "password": "test" },
            "body": "{\"fakeContent\":true}",
            "bodyType": "application/json",
            "cookies": "name1=value1;name2=value2;",
            "deviceIds": ["laptop_large"],
            "followRedirects": true,
            "headers": { "NEW_HEADER": "NEW VALUE" },
            "locations": ["aws:us-west-1"],
            "retry": { "count": 2, "interval": 300 },
            "startUrl": "http://new.url/",
            "variables": { "titleVariable": "new value" }
        }
    ]
}' "https://api./api/v1/synthetics/tests/trigger/ci"

応答例

{
  "batch_id": null,
  "results": [
    {
      "result_id": "0123456789012345678",
      "public_id": "abc-def-ghi",
      "location": 30019
    }
  ],
  "triggered_check_ids": [
    "abc-def-ghi"
  ],
  "locations": [
    {
      "display_name": "N. California (AWS)",
      "name": "aws:us-west-1",
      "region": "Americas",
      "is_active": true,
      "is_public": true,
      "id": 30019
    }
  ]
}

ポーリング結果のエンドポイント

  • Endpoint: https://api./api/v1/synthetics/tests/poll_results
  • Method: GET
  • Parameters: 結果の入手元となる結果識別子のリストを含む JSON 配列。

リクエスト例

#!/bin/sh

api_key="<DATADOG_API_KEY>"
app_key="<DATADOG_APPLICATION_KEY>"

curl -G \
    "https://api./api/v1/synthetics/tests/poll_results" \
    -H "DD-API-KEY: ${api_key}" \
    -H "DD-APPLICATION-KEY: ${app_key}" \
    -d "result_ids=[220123456789012345678]"

応答例

{
  "results": [
    {
      "check": {
        "config": {
          "assertions": [
            {
              "operator": "lessThan",
              "target": 2000,
              "type": "responseTime"
            }
          ],
          "configVariables": [],
          "request": {
            "basicAuth": {
              "password": "test",
              "username": "test"
            },
            "body": "{\"fakeContent\":true}",
            "headers": {
              "Content-Type": "application/json",
              "Cookie": "name1=value1;name2=value2;",
              "NEW_HEADER": "NEW VALUE"
            },
            "method": "GET",
            "timeout": 30,
            "url": "http://new.url/"
          }
        },
        "locations": [
          30019
        ],
        "options": {
          "allow_insecure": true,
          "follow_redirects": true,
          "min_failure_duration": 0,
          "min_location_failed": 1,
          "monitor_options": {
            "include_tags": true,
            "locked": false,
            "new_host_delay": 300,
            "notify_audit": false,
            "notify_no_data": false,
            "renotify_interval": 0
          },
          "retry": {
            "count": 2,
            "interval": 300
          },
          "tick_every": 60
        },
        "subtype": "http",
        "type": "api"
      },
      "check_id": "7654321",
      "check_version": 2,
      "config_override": {
        "allowInsecureCertificates": true,
        "basicAuth": {
          "password": "test",
          "username": "test"
        },
        "body": "{\"fakeContent\":true}",
        "bodyType": "application/json",
        "cookies": "name1=value1;name2=value2;",
        "deviceIds": [
          "laptop_large"
        ],
        "followRedirects": true,
        "headers": {
          "Content-Type": "application/json",
          "Cookie": "name1=value1;name2=value2;",
          "NEW_HEADER": "NEW VALUE"
        },
        "locations": [
          "aws:us-west-1"
        ],
        "public_id": "abc-def-ghi",
        "retry": {
          "count": 2,
          "interval": 300
        },
        "startUrl": "http://example.org/",
        "variables": {
          "titleVariable": "new value"
        }
      },
      "dc_id": 30019,
      "orgID": 2,
      "result": {
        "assertionResults": [
          {
            "actual": 27.92,
            "valid": true
          }
        ],
        "dnsServer": "8.8.8.8",
        "eventType": "finished",
        "healthCheckRatio": 1,
        "httpStatusCode": 400,
        "mainDC": "us1.prod",
        "passed": true,
        "resolvedIp": "93.184.216.34",
        "responseSize": 349,
        "runType": 2,
        "subtype": "http",
        "timings": {
          "dns": 24.6,
          "download": 0.1,
          "firstByte": 1.4,
          "tcp": 1.8,
          "total": 27.9
        },
        "unhealthy": false
      },
      "resultID": "220123456789012345678",
      "timestamp": 1612404331304
    }
  ]
}
{
  "results": [
    {
      "check_id": "123456",
      "timestamp": 1601639904704,
      "orgID": 2,
      "result": {
        "runType": 2,
        "artifactsBucketKey": "2/e2e-tests/abc-def-ghi/results/17221670732431167/chrome.laptop_large/artifacts__1601639913277.json",
        "browserType": "chrome",
        "eventType": "finished",
        "stepDetails": [
          {
            "browserErrors": [],
            "skipped": false,
            "description": "Navigate to start URL",
            "warnings": [],
            "url": "about:blank",
            "value": "https://example.com",
            "duration": 1002,
            "allowFailure": false,
            "screenshotBucketKey": "2/e2e-tests/abc-def-ghi/results/17221670732431167/chrome.laptop_large/step-0__1601639913294.jpeg",
            "type": "goToUrlAndMeasureTti",
            "stepId": -1
          },
          {
            "browserErrors": [],
            "stepElementUpdates": {
              "version": 1,
              "multiLocator": {
                "ab": "/*[local-name()=\"html\"][1]/*[local-name()=\"body\"][1]/*[local-name()=\"div\"][1]/*[local-name()=\"h1\"][1]",
                "co": "[{\"text\":\"example domain\",\"textType\":\"directText\"}]",
                "cl": "/*[local-name()=\"html\"]/*[local-name()=\"body\"]/*[local-name()=\"div\"][1]/*[local-name()=\"h1\"][1]",
                "at": "/*[local-name()=\"html\"]/*[local-name()=\"body\"]/*[local-name()=\"div\"][1]/*[local-name()=\"h1\"][1]",
                "clt": "/descendant::*[text()[normalize-space(translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ', 'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ')) = \"example domain\"]]",
                "ro": "//*[local-name()=\"h1\"]"
              }
            },
            "skipped": false,
            "description": "Test heading \"Example Domain\" content",
            "url": "https://www.example.com/",
            "checkType": "contains",
            "value": "Example Domain",
            "duration": 204,
            "allowFailure": false,
            "screenshotBucketKey": "2/e2e-tests/abc-def-ghi/results/17221670732431167/chrome.laptop_large/step-1__1601639913410.jpeg",
            "type": "assertElementContent",
            "stepId": 2275176
          }
        ],
        "browserVersion": "84.0.4147.135",
        "mainDC": "us1.prod",
        "timeToInteractive": 269,
        "device": {
          "name": "Laptop Large",
          "height": 1100,
          "width": 1440,
          "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36",
          "id": "chrome.laptop_large",
          "isMobile": false,
          "browser": "chrome"
        },
        "passed": true,
        "duration": 1206,
        "startUrl": "https://www.example.com",
        "metadata": {}
      },
      "dc_id": 30005,
      "resultID": "17221670732431167",
      "metadata": {}
    }
  ]
}

CLI の使用

パッケージのインストール

パッケージは NPM レジストリの @datadog/datadog-ci で公開されています。

NPM からパッケージをインストールします。

npm install --save-dev @datadog/datadog-ci

Yarn からパッケージをインストールします。

yarn add --dev @datadog/datadog-ci

クライアントのセットアップ

クライアントをセットアップするには、Datadog API キーとアプリケーションキーを構成する必要があります。これらのキーは次の 3 つの方法で定義できます。

  1. 環境変数として定義

    export DATADOG_API_KEY="<API_KEY>"
    export DATADOG_APP_KEY="<APPLICATION_KEY>"
    
  2. テストの実行中に CLI に渡す

    datadog-ci synthetics run-tests --apiKey "<API_KEY>" --appKey "<APPLICATION_KEY>"
    
  3. グローバルコンフィギュレーションファイルで定義

    グローバル JSON コンフィギュレーションファイルでは、追加の詳細オプションを指定できます。テストを起動するときにフラグ --config を使用して、このファイルへのパスを指定します。グローバルコンフィギュレーションファイルの名前が datadog-ci.json に設定されている場合、デフォルトでその名前になります。

グローバルコンフィギュレーションファイルでは、次のオプションを構成できます。

apiKey
Datadog API にクエリーを送信する際に使用される API キー。
appKey
Datadog API にクエリーを送信する際に使用されるアプリケーションキー。
datadogSite
リクエストの送信先となる Datadog インスタンス。デフォルトは datadoghq.com。Datadog サイトは です。
files
Synthetic テスト用コンフィギュレーションファイルを検出するグロブパターン。
global
すべてのテストに適用される Synthetic テストのオーバーライド (各フィールドの説明については下記を参照してください)。
proxy
Datadog への発信接続に使用されるプロキシ。hostport キーは必須の引数で、protocol キーの初期値は http です。サポートされる protocol キーの値は、httphttpssockssocks4socks4asocks5socks5hpac+datapac+filepac+ftppac+httppac+https です。プロキシの構成に使用されるライブラリは、proxy-agent ライブラリです。
subdomain
Datadog アプリケーションにアクセスするために設定されたカスタムサブドメインの名前。Datadog へのアクセスに使用する URL が myorg.datadoghq.com の場合、subdomain の値は myorg にする必要があります。

グローバルコンフィギュレーションファイルの例

{
    "apiKey": "<DATADOG_API_KEY>",
    "appKey": "<DATADOG_APPLICATION_KEY>",
    "datadogSite": "datadoghq.com",
    "files": "{,!(node_modules)/**/}*.synthetics.json",
    "global": {
        "allowInsecureCertificates": true,
        "basicAuth": { "username": "test", "password": "test" },
        "body": "{\"fakeContent\":true}",
        "bodyType": "application/json",
        "cookies": "name1=value1;name2=value2;",
        "deviceIds": ["laptop_large"],
        "followRedirects": true,
        "headers": { "<NEW_HEADER>": "<NEW_VALUE>" },
            "locations": ["aws:us-west-1"],
        "retry": { "count": 2, "interval": 300 },
        "executionRule": "blocking",
        "startUrl": "{{URL}}?static_hash={{STATIC_HASH}}",
        "variables": { "titleVariable": "new value" },
        "pollingTimeout": 180000
    },
    "proxy": {
      "auth": {
        "username": "login",
        "password": "pwd"
      },
      "host": "127.0.0.1",
      "port": 3128,
      "protocol": "http"
    },
    "subdomain": "subdomainname"
}

テストの構成

デフォルトでは、クライアントは **/*.synthetics.json ファイルで指定されたすべてのテストを自動的に検出して実行します (パスはグローバルコンフィギュレーションファイルで構成できます。このファイルには、 実行するテストのオブジェクト配列と ID、および、このテストで考えられるコンフィギュレーションオーバーライドを含む tests キーが含まれています。

基本的なテストコンフィギュレーションファイルの例

{
    "tests": [
        {
            "id": "<TEST_PUBLIC_ID>"
        },
        {
            "id": "<TEST_PUBLIC_ID>"
        }
    ]
}

詳細なコンフィギュレーション

テストに使用されるデフォルトのコンフィギュレーションはオリジナルテストのコンフィギュレーションです (UI または [API からテスト用コンフィギュレーションを取得]する際に確認可能4)。

ただし、CI デプロイメントの際に、下記のオーバーライドを使用して、テストパラメーターの一部 (またはすべて) をオーバーライドするように任意で定めることもできます。テストすべてをオーバーライドするように定義する場合は、下記と同じパラメーターをグローバルコンフィギュレーションファイルごとに設定できます。

allowInsecureCertificates
タイプ: ブール値
HTTP テストの認証チェックを無効化します。
basicAuth
タイプ: オブジェクト
HTTP またはブラウザテストで基本認証を行う際に提供する認証情報。
  • username: 文字列。基本認証で使用するユーザー名。
  • password: 文字列。基本認証で使用するパスワード。
body
タイプ: 文字列
HTTP テストで送信するデータ。
bodyType
タイプ: 文字列
HTTP テストで送信するデータのタイプ。
cookies
タイプ: 文字列
HTTP またはブラウザテストのクッキーヘッダーとして提供された文字列を使用。
deviceIds
タイプ: 配列
ブラウザテストを実行するデバイスのリスト。
followRedirects
タイプ: ブール値
HTTP テストでリダイレクトに従うかどうかを示す。
headers
タイプ: オブジェクト
HTTP またはブラウザテストで置換するヘッダー。このオブジェクトには、置換するヘッダーの名前 (キー) と、ヘッダーの新しい値 (値) が含まれている必要がある。
locations
タイプ: 配列
テストの実行元となる場所のリスト。
retry
タイプ: オブジェクト
テストの再試行ポリシー。
  • count: 整数。テストが失敗した場合に再試行する回数。
  • interval: 整数。試行する間隔 (ミリ秒)。
executionRule
タイプ: 文字列
テストの実行規則。テストが失敗した場合の CLI の挙動を定義する。
  • blocking: テストが失敗した場合、CLI はエラーを返す。
  • non_blocking: テストが失敗した場合、CLI は警告のプリントのみを実施する。
  • skipped: テストを一切実行しない。
startUrl
タイプ: 文字列
HTTP またはブラウザテストに提供する新しい開始 URL。
variables
タイプ: オブジェクト
テストで置換する変数。このオブジェクトには、置換する変数の名前 (キー) と、変数の新しい値 (値) が含まれている必要がある。
pollingTimeout
タイプ: 整数
datadog-ci がテスト結果のポーリングを停止するまでのミリ秒単位の期間。デフォルトは 120,000 ミリ秒です。CI レベルでは、この期間の後に完了したテスト結果は失敗したと見なされます。

Note: グローバルオーバーライドに優先するテストのオーバーライド。

高度なテストコンフィギュレーションファイルの例

{
    "tests": [
        {
            "id": "<TEST_PUBLIC_ID>",
            "config": {
                "allowInsecureCertificates": true,
                "basicAuth": { "username": "test", "password": "test" },
                "body": "{\"fakeContent\":true}",
                "bodyType": "application/json",
                "cookies": "name1=value1;name2=value2;",
                "deviceIds": ["laptop_large"],
                "followRedirects": true,
                "headers": { "<NEW_HEADER>": "<NEW_VALUE>" },
            "locations": ["aws:us-west-1"],
                "retry": { "count": 2, "interval": 300 },
                "executionRule": "skipped",
                "startUrl": "{{URL}}?static_hash={{STATIC_HASH}}",
                "variables": { "titleVariable": "new value" },
                "pollingTimeout": 180000
            }
        }
    ]
}

実行規則

各テストの 実行規則 もまた、アプリケーション内でテストごとに定義できます。CI Execution 横のドロップダウンから選択します。

テストに関連付けられた実行規則は常に、コンフィギュレーションファイルで設定されている中で最も制限の厳しいものが適用されます。最も制限の厳しいものから緩いものに並べると skippednon_blockingblocking の順になります。たとえば、UI ではテストを skipped で構成し、コンフィギュレーションファイルでは blocking で構成している場合、テストを実行すると skipped が適用されます。

開始 URL

テストオブジェクトに startUrl を含めることで、テストを開始する URL を構成し、テストのオリジナルの開始 URL の一部と下記の環境変数を使用した独自の開始 URL を作成できます。

URL
テストのオリジナルの開始 URL
: https://www.example.org:81/path/to/something?abc=123#target
DOMAIN
テストのドメイン名
: example.org
HASH
テストのハッシュ
: #target
HOST
テストのホスト
: www.example.org:81
HOSTNAME
テストのホスト名
: www.example.org
ORIGIN
テストの起点
: https://www.example.org:81
PARAMS
テストのクエリパラメーター
: ?abc=123
PATHNAME
テストの URl パス
: /path/to/something
PORT
テストのホストのポート
: 81
PROTOCOL
テストのプロトコル
: https:
SUBDOMAIN
テストのサブドメイン
: www

たとえば、テストの開始 URL が https://www.example.org:81/path/to/something?abc=123#target なら、次のような記述になります。

  • {{PROTOCOL}}//{{SUBDOMAIN}}.{{DOMAIN}}:{{PORT}}{{PATHNAME}}{{PARAMS}}{{HASH}}
  • {{PROTOCOL}}//{{HOST}}{{PATHNAME}}{{PARAMS}}{{HASH}}
  • {{URL}}

テストの実行

CLI にすべての **/*.synthetics.json Synthetic テスト (またはグローバルコンフィギュレーションファイルで指定したパスに紐付いたすべてのテスト) を自動検知させるか、-p,--public-id フラグを使用して実行するテストを指定するか、定義できます。

CLI を実行してテストを実行する

yarn datadog-ci synthetics run-tests

: カスタムグローバルコンフィギュレーションファイルを使用してテストを起動している場合は、コマンドに --config <PATH_TO_GLOBAL_CONFIG_FILE を追加します。

package.json に下記を追加します。

{
  "scripts": {
    "datadog-ci-synthetics": "datadog-ci synthetics run-tests"
  }
}

次に、以下を実行します。

npm run datadog-ci-synthetics

: カスタムグローバルコンフィギュレーションファイルを使用してテストを起動している場合は、datadog-ci-synthetics スクリプトに紐付けられたコマンドに --config <PATH_TO_GLOBAL_CONFIG_FILE を追加します。

テスト結果の表示

CI の場合

テストの実行中に、テストの実行結果を CI 内で直接確認することができます。

実行ログを確認し、失敗したアサーションの原因を検索することで、テストが失敗した原因を特定できます。

Datadog アプリケーションの場合

Datadog のテスト詳細ページでも一覧表示されたテスト結果を確認することができます。

その他の参考資料