Helix Coreサーバ管理者ガイド (2020.1)

トリガの基本

このセクションにはトリガの使用方法に関する情報が含まれています。 それぞれのタイプのトリガの実装方法については、以下の各セクションを参照してください。 このセクションでは、すべてのタイプのトリガに関する情報を提供します。

トリガのデバッグについては、サポートナレッジベースの記事「トリガをデバッグする」を参照してください。

トリガとサーバ間の通信

トリガは、2つの方法のうちいずれか1つでサーバと通信します。すなわち、トリガスクリプト変数で説明している変数を使用するか、もしくはSTDINおよびSTDOUTを介してキー/値のペアのディクショナリーを使用します。 triggers.io構成変数の設定内容に応じて、いずれかの方法が使用されます。 また選択された方法によって、STDINおよびSTDOUTの内容や、トリガ失敗の処理方法が異なります。 以下の表では、これらの設定による結果を要約しています。 クライアントは、トリガを実行するサーバに接続されているクライアントアプリケーション(SwarmP4V、P4など)を指します。

  triggers.io = 0 triggers.io = 1

トリガ成功

トリガはサーバとの通信にトリガ変数を使用します。

STDINを使用するのはアーカイブトリガまたは認証トリガに限られます。 アーカイブトリガではSTDINはファイルコンテンツに使用され、認証トリガではSTDINはパスワードに使用されます。

アーカイブトリガを除くすべてのトリガのSTDOUTは、未修飾のメッセージとしてクライアントに送信されます。アーカイブトリガの場合、コマンドの標準出力はファイルコンテンツです。

トリガは0の値で終了する必要があります。

トリガはサーバとの通信にSTDINおよびSTDOUTを使用します。

STDINは、%clienthost%および%peerhost%を除くすべてのトリガ変数の名前と値のペアから成るテキストディクショナリーです。

この設定は、アーカイブトリガまたは認証トリガに関しては、STDINの値に影響を及ぼしません。

トリガは0の値で終了する必要があります。

トリガ失敗

トリガのSTDOUTおよびSTDERRは、トリガ失敗エラーメッセージの文字列として、クライアントに送信されます。

トリガは0以外の値で終了する必要があります。

STDOUTはエラー情報を含むテキストディクショナリーです。 STDERRはSTDOUTとマージされます。

トリガ失敗ということは、すなわち、トリガスクリプトが実行できない、出力ディクショナリーに失敗メッセージが含まれている、出力の形式が不適切であるということです。 実行エラーはサーバによって記録され、サーバはSTDOUTによって指定された情報をクライアントに送信します。 ディクショナリーが提供されていない場合、サーバは、何らかの問題が発生したことを示す一般的なメッセージをクライアントに送信します。

ディクショナリーの形式は、キー/値のペアから成る複数の行の連続です。 印字不能文字は、パーセントエンコーディングで符号化される必要があります。 データは、Unicode対応サーバ上では、UTF-8でエンコードされているものとして扱われます。 %dictionaryにおける%client%、%clientprog%、%command%、%user%変数の記述例を以下に列挙します。

client:jgibson-aaaatchoooo
clientprog:P4/LINUX45X86_128/2017.9.MAIN/1773263782 (2017/OCT/09).
command:user-dwim
user:jgibson

上記の例では、ディクショナリーの一部のみを示しています。 このようにして変数が渡される場合、トリガスクリプト変数で説明しているすべての変数はSTDINに渡されます。たとえスクリプトがこれらの変数の一部しか参照しないとしても、トリガスクリプトはSTDINのすべてを読み取る必要があります。 スクリプトがSTDINのすべてを読み取らない場合、スクリプトは失敗し、サーバで以下のようなエラーが記録されます。

write: yourTriggerScript: Broken pipe

トリガはSTDOUTを介してディクショナリーをサーバに送り返す必要があります。 ディクショナリーには、少なくとも1つのアクションが含まれている必要があります(アクションは任意で1つのメッセージを伴うことが可能です)。 アクションはpassまたはfailのいずれかです。 印字不能文字は、パーセントエンコーディングで符号化される必要があります。 以下に例を示します。

action:fail
message:too bad!

欠陥のあるトリガがディクショナリーに応答し、実行時に発生した問題が一般的なエラーとともにクライアントに報告されます。 詳細なメッセージはサーバログに記録されます。

このセクションの冒頭で示唆したように、サーバとの通信に使用される2つの方法は互いに排他的な関係にあります。 両者は概して両立しません。 ただし、1つだけ例外があり、その場合にはたとえtriggers.ioを1に設定していたとしても、コマンドラインで変数を指定する必要があります。 その例外とは、%peerhost%変数または%clienthost%変数を参照する場合です。 こうした変数を渡すことは非常に非効率的です。 ディクショナリーに含まれるべきそれらの値については、コマンドライン上で1つまたは両方を指定する必要があります。

以下のサンプルは、入力ディクショナリーをユーザにエコーするPerlプログラムです。

use strict;
use warnings FATAL=>"all";
use open qw/ :std :utf8 /;
use Data::Dumper;
use URI::Escape;

$Data::Dumper::Quotekeys = 0;
$Data::Dumper::Sortkeys  = 1;

my %keys = map { /(.*):(.*)/ } <STDIN>;

print "action:pass\nmessage:" . uri_escape Dumper \ %keys;

ソースリストの冒頭では、いくつかのコードによって基本のUnicodeサポートのためにPerlを設定し、さらに少々のエラー処理を加えています。 プログラムの核心は第8行にあります。 <STDIN>は、mapが入力から1行ずつ取得して内の関数を実行する際に、map{}に適用されるファイルハンドルです。 (.*):(.*)という式は、コロンで区切られたキャプチャグループのペアから成る正規表現です。 サーバから送信されるキーそれ自体の中にコロンが含まれることはないので、前方の.*のみでは一致しません。 ほとんどの印字不能文字(改行文字など)は、ディクショナリー内ではパーセントエンコーディングで符号化されているため、トリガは1組のキー/値が1行で記述されることを前提とします。したがって単一の正規表現は、キーと値の両方を抽出します。 正規表現の戻り値は、文字列のリストであり、マップ関数の戻り値として扱われます。 リストがハッシュに割り当てられると、Perlはそこからキー/値のペアのリストの作成を試みます。 ご存知のようにそのリストは偶数項目から成っているので、この試行は成功し、データを取得できました。 printコマンドが結果ディクショナリーを作成し、それをサーバに送信します。 そのコマンドを呼び出すと、パスアクションによって、サーバは、コマンド続行を許可し、トリガの入力ディクショナリーのフォーマット済みハッシュをメッセージとしてユーザに送信するようにという指示を受け取ります。

例外

triggers.ioに1を指定したとしても、認証トリガやアーカイブトリガには影響しません。これらのトリガは、実際に設定されている値とは無関係に、あたかもtriggers.ioに0が指定されているかのように動作します。

古いトリガとの互換性

triggers.io変数に1を設定すると、サーバで実行されるすべての新旧のスクリプトの動作に影響を及ぼします。 古いトリガスクリプトの書き直しを望まない場合、シムを、トリガテーブルと古いトリガスクリプトの間に挿入することにより、トリガ出力を収集し、それをサーバで現在取り扱っているとおりにフォーマットするとよいでしょう。 すなわち、シムが古いトリガを実行し、その出力とリターンコードを取得した後で、適切なディクショナリーをサーバに送り返します。 以下のPerlスクリプトで、そのようなシムの具体例を示します。

t form-out label unset "perl shim.pl original_trigger.exe orig_args..."

shim.plプログラムは、以下のように表示されます。

use strict;
use warnings FATAL => "all";
use open qw/ :std :utf8 /;
use URI::Escape;
use IPC::Run3;

@_=<STDIN>;
run3 \@ARGV, undef, \$_, \$_;
print 'action:' . (? ? 'fail' : 'pass' ) . "\nmessage:" . uri_escape $_;

ディポ内にトリガを保存する

トリガをディポ内に保存することができます。 これには次のようなメリットがあります。

  • トリガをバージョンアップし、必要に応じて過去のバージョンにもアクセスすることができるようになる。
  • 分散構造で、サーバごとのファイルシステム内のファイルを手動で更新することなく、Helixサーバがレプリカごとに最新のトリガスクリプトを自動で入力することができるようになる。
注意

ディポから実行されるトリガは、メタデータのみが含まれているレプリカでは機能しません。

詳細については、「メタデータとディポへのアクセスを制御するサーバオプション」と「メタデータのみの複製を設定する構成可能変数」を参照してください。

トリガをディポに保存する際、トリガを含むファイルのファイルパスを%記号で囲む特殊な方法でトリガ定義のcommandフィールドにトリガ名を指定する必要があります。 追加の変数をトリガに渡す必要がある場合、通常どおりそれらをコマンドフィールドに追加します。 サーバが一時ファイルを作成します。そこには、コマンドフィールドに指定したファイルパス名の内容が記録されます。 (一時ファイルの使用が望ましい理由は、まずそれがセキュリティ上必要だからであり、またディポファイルは通常いくらか処理を経てからでなければ実行できないからです。)

複数のファイルをディポからロードすることができます。 次のトリガ定義では、ディポのパスが2つ提供されています。 トリガの実行時に複数のファイルをディポからロードするため、ディポのパスが複数使用されることがあります。 例えば、トリガスクリプトには、ディポ内のスクリプトの隣に保存されている構成ファイルが必要な場合があります。

lo form-out label  "perl %//admin/validate.pl% %//admin/validate.conf%"

トリガとして使用するディポファイルは、既に存在しているものでなければなりません。 内容が使用可能であれば、あらゆるファイルタイプが許容されます。 Unicode対応のサーバ上では、一時ファイルはUTF-8のテキストタイプになります。 ディポスクリプトファイルの内容は、信頼されているユーザのみが読み書きできるようにプロテクトしておく必要があります。

ファイルパス名にスペースが含まれる場合や、追加のパラメータを渡す必要がある場合は、commandフィールドを引用符で囲む必要があります。

次のトリガ定義では、インタープリタがトリガに指定されている点に注目してください。 オペレーティングシステムがトリガの実行方法を認識しないようなプラットフォームでは、インタープリタを指定する必要があります。 例えば、Windowsでは.plファイルが実行ファイルとして認識されません。

lo form-out label  "perl %//admin/validate.pl%"

次のトリガ定義では、ディポパスがリビジョン番号を含んでいるため引用符で囲まれています。 インタープリタ値が指定されていない場合、オペレーティングシステムがスクリプトを直接実行できるということを意味します。

lo form-out branch "%//depot/scripts/validate.exe#123%"
警告

ディポファイルパス名に予約文字を含めることはできません。 なぜなら、16進数変換後の予約文字には、%var%の区切り文字として使用されているパーセント記号が含まれるからです。 例えば、@myScriptというファイル名は使用できません。なぜなら、%40myScriptに変換されるため、これをvarに代入すると%%40myScript%となってしまうからです。

複数のトリガを使用する

サブミットトリガとフォームトリガは、トリガテーブルに登場する順序で実行されます。 同じタイプに属する複数のトリガが同じパスに対して実行される場合、各トリガはトリガテーブルに登場する順序で実行されます。

例   同じファイル上の複数のトリガ

すべての*.cファイルは、スクリプトcheck1.shcheck2.shcheck3.shを通じて渡される必要があります。

Triggers:
  check1 change-submit //depot/src/*.c "/usr/bin/check1.sh %change%"
  check2 change-submit //depot/src/*.c "/usr/bin/check2.sh %change%"
  check3 change-submit //depot/src/*.c "/usr/bin/check3.sh %change%"

1つでもトリガ(例えばcheck1.sh)が失敗した場合、サブミットはただちに失敗し、後続のすべてのトリガ(check2.shcheck3.sh)が呼び出されなくなります。 トリガが成功するたびに、次の一致するトリガが実行されます。

複数のファイル仕様を同じトリガ(およびトリガタイプ)にリンクするには、トリガテーブル中にトリガを複数回記入します。

例   複数のファイル仕様に対して同一のトリガをアクティベートする

Triggers:
  bugcheck change-submit //depot/*.c   "/usr/bin/check4.sh %change%"
  bugcheck change-submit //depot/*.h   "/usr/bin/check4.sh %change%"
  bugcheck change-submit //depot/*.cpp "/usr/bin/check4.sh %change%"

上記の例では、bugcheckトリガが、*.cファイル、*.hファイル、*.cppファイルに対して実行されます。

異なるタイプに属する複数のサブミットトリガを同一のパスに対して実行すると、以下の順序で実行されます。

  1. change-submit (ファイルの転送を行う前に、チェンジリストのサブミットを実行する時)
  2. change-contentトリガ(チェンジリストのサブミットとファイルの転送の後)
  3. change-commitトリガ(サーバによって、チェンジリストの番号が自動的に振り直された時)

同様に、異なるタイプに属する複数のフォームトリガは、以下の順序で実行されます。

  1. form-out(フォームが生成された時)
  2. form-in(変更されたフォームがサーバに転送された時)
  3. form-save(検証済みのフォームがHelixサーバデータベース内のストレージに保存される前)
  4. form-delete(検証済みのフォームがHelixサーバデータベース内のストレージに保存された後)

トリガの除外マッピング

例    

trig1 change-submit //depot/... "trig.pl %changelist%"
trig1 change-submit -//depot/products/doc/... "trig.pl %changelist%"

//depot/products/doc/...で変更をサブミットすると、 /usr/bin/trig.plスクリプトが実行されなくなります。

その他のディレクトリに変更をサブミットすると、trig1スクリプトの最初のインスタンスを実行します。これは、usr/bin/trig.plの2つ目のインスタンスを無視した最初のtrig1行のスクリプトです。

除外マッピングのルール

  1. 除外は最後である必要があります。
  2. 同じスクリプトまたはアクションは、同じトリガ名内のそれぞれの異なる行と関連する必要があります。 パスまたはファイルチェックがトリガ可能なパスやファイルとして失敗した場合は、最初のトリガ行と関連するスクリプトまたはアクションが実行されます。
  3. サブミットを失敗させる場合、exit(1)リターンコードを一致したパスまたはファイルと関連付けます。

複数のHelixサーバをサポートするトリガを作成する

同一のトリガスクリプトを2つ以上のHelix Coreサーバから呼び出すには、%serverhost%%serverip%%serverport%変数を使用して、トリガスクリプトを他のサーバでも使用できるようにします。

例えば、ハードコーディングされたポート番号とアドレスを使用する以下のようなスクリプトがあるとします。

#!/bin/sh
# Usage: jobcheck.sh changelist
CHANGE=$1
P4CMD="/usr/local/bin/p4 -p 192.168.0.12:1666"
$P4CMD describe -s $1 | grep "Jobs fixed...\n\n\t" > /dev/null

上記のスクリプトを、トリガテーブル内の以下の行で呼び出すとします。

jc1 change-submit //depot/qa/... "jobcheck.sh %change%"

ポータビリティを向上するには、スクリプトを以下のように変更します。

#!/bin/sh
# Usage: jobcheck.sh changelist server:port
CHANGE=$1
P4PORT=$2
P4CMD="/usr/local/bin/p4 -p $P4PORT"
$P4CMD describe -s $1 | grep "Jobs fixed...\n\n\t" > /dev/null

サーバ固有のデータを、引数としてトリガスクリプトに渡します。

jc2 change-submit //depot/qa/... "jobcheck.sh %change% %serverport%"

注意: %serverport%変数には、トランスポートプレフィックス(ssltcp6ssl6)を指定することができます。

トリガタイプごとに適用する変数を網羅したリストについては、トリガスクリプト変数を参照してください。

トリガとマルチサーバアーキテクチャ

マスターサーバにインストールされているトリガは、そのレプリカサーバにも存在している必要があります。

  • トリガ定義はすべてのレプリカに自動的に伝達されます。
  • トリガを実装するプログラムファイルが、トリガがアクティブ化される可能性があるすべてのレプリカ上に存在することを必ず確認してください。 各レプリカの場所は、トリガ定義のcommandフィールドで指定されている場所と対応している必要があります。

    それぞれのサーバ上のファイルシステム内の同じ場所にトリガスクリプトを保存するか、マスターサーバまたはコミットサーバ上のディポにトリガスクリプトを保存して、ディポシンタックスを使用してファイル名を指定することで場所を対応させることができます。 この場合、ファイルはすべてのレプリカに自動的に伝達されます。 詳細については、ディポ内にトリガを保存するを参照してください。

レプリカにインストールされているトリガは、そのトリガに対する同一の実行環境とトリガ本体を持っている必要があります。 トリガ本体には通常、トリガログインチケット、またはPerl、Pythonなどのトリガスクリプトランタイムが含まれています。

注意

エッジサーバには、クライアントとエッジサーバ、およびエッジサーバとコミットサーバ間で起動するトリガが用意されています。 詳細については、トリガとコミットエッジを参照してください。