jar版Rundeckからメール送信を行う

タスクスケジューラの1種であるRundeckは
javaベースで動いており,Windows上でも動かすことができます.
Windows上でRundeckを動かし,ジョブの開始/成功/失敗時に
メールを送るために設定したことをメモしておきます.

何故記事にするのか

Rundeckはパッケージとしても配布しており,ubuntuなどのLinuxOSを使用している場合は
通常そちらを使うケースが多く,jarファイルを使用している人が少ないためかネット上にも
あまり記事を見かけません.また,jarファイルでRundeckの環境を構築した場合は
設定ファイルの場所など,仕様が異なるため今回記事を書きました.

Rundeckをインストールする

コマンドプロンプト上で

java -jar rundeck-***.jar (***にはバージョンなど)

を実行します.
初回起動時にフォルダや設定ファイルなどが自動で作成されるはずです.
(以下インストールしたjarファイルが存在するフォルダを$rundeckとする)
【生成されるフォルダ構成一覧(一部)】

$rundeck
-etc
-libext
-projects
-rundeck
-server
  -config
  -data
  -exp
  -lib
  -logs
  -sbin
  -work
-tools
-var
(などなど.全ては書きません)

groovyファイルを作成する

$rundeck/server/config
の中にあるrundeck-config.propertiesのバックアップを取り(例えばrundeck-config.properties.bakにリネーム),
rundeck-config.propertiesはrundeck-config.groovyに名前を変えて編集する.

今後,設定ファイルとしてgroovyフォーマットのファイルを使用するため,
メールの設定に加え,いくつかgroovyフォーマットに対応した記述に変更する必要があります.
以下が私の設定ファイルの中身(私はgmailを使用しています).

loglevel.default=INFO
rdeck.base="C:/workspace/rundeck"
rss.enabled=false

dataSource {
        dbCreate="update"
        url = "jdbc:h2:file:C:/workspace/rundeck/server/data/grailsdb;MVCC=true;TRACE_LEVEL_FILE=4"

}

grails {
        serverURL="http://<**localhost_hostname***>:4440"
        mail {
                host="smtp.gmail.com"
                port = 465
                username="<***your_mailaddress***>@gmail.com"
                password="<your_mail_password(ログイン時に使用するもの)>"
                props = ["mail.smtp.auth":"true",
                      "mail.smtp.socketFactory.port":"465",
                      "mail.smtp.socketFactory.class":"javax.net.ssl.SSLSocketFactory",
                      "mail.smtp.socketFactory.fallback":"false"]
        }
}

この時に大切なことは以下の3つ
- 必ずgroovy形式にする(ファイル名も).{}はgroovy形式のフォーマットなので,これでしか読めない
- 元のpropertyファイルに残っていた情報も記載しなければならない
- rdeck.base= "..."の部分からわかるように元ファイルと違いダブルクオーテーション""が必要になるので注意

保存後,Rundeckを以下のオプションをつけて起動(再起動)します.

java -jar -Drundeck.config.name=rundeck-config.groovy *****.jar

ファイル名を直接指定していますが,この記述で
$rundeck/server/config内のファイルを読み込んでくれます.
また,Linuxにインストールしたパッケージ版では
groovyファイルを読み込むようにするために
/etc/rundeck/profileを編集していましたが,jar版では違うみたいです.

この状態でジョブを作成/編集する際に"send notification?"をYesにチェックし,
送信先メールアドレスを入力すれば,メールが届くようになります.


参考にしたサイト:
- Rundeck公式
http://rundeck.org/docs/administration/configuration-file-reference.html
- RundeckからGmailを使ってメール送信する http://soymsk.hatenablog.com/entry/2015/09/17/222110
- メール送信設定後,証明書関係で例外が
出るようになった際に参考にしたページ(設定後システムの再起動が必要)
https://groups.google.com/forum/#!topic/rundeck-discuss/3vJyPpvqjVs

boost::coroutineを使ってみる

Boost.Coroutineを使うとC++でもコルーチンを使用することができます.
C++14になってからはラムダ式の引数に型推論が使えるようになり,
コルーチンの引数にラムダ式を使うことで,とても楽に書けるようになりました.

#include <iostream>
#include <boost/coroutine/all.hpp>

template< class T >
using coroutine = boost::coroutines::coroutine< T >;

//Coroutineで呼び出される関数
void cooperative( coroutine< int >::push_type & yield )
{
	int counter = 0;
	while( 1 )
	{
		yield( ++counter );
	}

}

int main()
{
	coroutine< int >::pull_type generator( cooperative );
	
	for( int i = 0; i < 10; ++i )
	{
		std::cout << generator().get() << std::endl;
	}
        // 1 2 3 4 ... が出力

	//**********************************************************//
	//ラムダ式バージョン
	coroutine< int >::pull_type double_gen(
		[]( auto & yield )
	{
		int counter = 0;
		while( 1 )
		{
			yield( ++counter * 2 );
		}
	}

	);

	for( int i = 0; i < 10; ++i )
	{
		std::cout << double_gen().get() << std::endl;
	}
        //2 4 6 8 ... が出力
	//**********************************************************//

	return 0;
}

Boost::coroutineはv1とv2の2つのバージョンが存在しており(2016年6月現在),v2ではv1のcaller_typeがpull_typeになっていたりと互換性が無いことに注意が必要です.

boost::zip_iteratorを使ってみる

C++でzip iteratorを使えると聞いて.

2つのイテレータを並列に進めて2倍した値をv2に格納する

#include <iostream>
#include <vector>
#include <numeric>

#include <boost/iterator/zip_iterator.hpp>

int main()
{
	std::vector< int > v( 5 );
	std::vector< int > v2( 5 );
	std::iota( v.begin(), v.end(), 1 );


	auto it = boost::make_zip_iterator( 
		boost::make_tuple( v.begin(), v2.begin() ) );

	auto it2 = boost::make_zip_iterator(
		boost::make_tuple( v.end(), v2.end() ) );

	std::for_each( it, it2, []( auto a )
	{
		a.get<1>() = a.get<0>() * 2;
		//***iteratorのコピーaから参照にアクセスしているので,値が書き換わる***
	} );

	for( auto const & i : v2 )
	{
		std::cout << i << std::endl;
	}

        /*output*
        2
        4
        6
        8
        10
        ********/

	return 0;
}

writableじゃないのでstd::sortは使えないなどの問題はあるんだけれど,まぁ,それなりには使えそう.

C++のためのAPIデザイン2章(2)

日が空いてしまいましたが,続き.

2.3 最小限の完全性

APIは最小限に完全であるべき.すなわち,可能な限り小さくするが,
必要以上に小さくはしないこと,とこの本では主張している.
APIは将来の保守性,完全性,使用者との約束,これらを維持するためには
必要な物だけを実装し,できるだけ最小限の公開インタフェースを設けることが大切である.
過度に一般性や将来性を見込んで実装したAPIは,将来の変更の足枷になる
(変更すればユーザのコードに影響が出るため,変更できない)上に,将来不要になるケースも多い.
APIは一度実装してしまえば削除できない,という大前提を肝に銘じて置かなければならない.
"""不確かなときは書かずにおけ"""である.

仮想関数の追加について

最小限の完全性を実現する際には仮想関数の追加には慎重にしないといけない.
基本クラスへの実装変更がユーザのコードへ有害な結果をもたらしたり,
ユーザが基本クラスの仕様を把握せずにAPIを拡張したりすることによる
弊害が大きくなったりする.

コアAPIとコンビニエンスAPIの分離

APIの利便性を増すためには,基本機能をまとめた
(複数のAPI操作を行い,より高度な操作を提供する)
APIを提供することは有用である.
しかし,一方でそのようなAPIを提供すると,デバッグや保守性に負担が生じる.
そこで,この本ではコアAPIと高度な操作を提供するAPIを分離して
別ラッパークラス・ライブラリとして提供することを提案している.
確かに,1つのクラスの中に基本操作と高度な操作を提供するAPIの両方を実装すれば,
APIの複雑性が増し保守などの負担が増加する.
また,使用者はコアAPIを使えば良いのか,高度なAPIを使えば良いのかわからなくなるだろう.

2.4 使いやすさ

できるだけドキュメントを読まなくても使えるAPIがbetter.学習のしやすさも重要.
使いやすさ != 解明しやすさ,であることを覚えておこう.

間違いの防止

APIのパラメータに同じ型のものが並んでいるだけで間違いを誘発する.

bool checkFunc( bool isAlive, bool isHuman )
{ ... } 

上記のような関数があった時,使用者はどちらが何のフラグであるかをドキュメントで調べなければならない.また,間違えて実引数を渡し間違える可能性もある.

このような場合は

enum class eAlive
{
   EA_ALIVE,
   EA_DEAD
};
...
bool checkFunc( enum eAlive, enum eHuman );

のようにすると呼び出す方も引数の順番などを意識する必要がなくなる.

この手のことは先日読んだEffectivePythonにも書かれてたな.C++に名前付き引数があれば更にこの辺に強くなれるんだけど(一応Boostにはある).

相互独立性

APIの呼び出しによって他のAPIに副作用を与えてはならない.
APIの呼び出し順によって結果がことなるとか怖いな,と思う.

プラットフォーム独立性

公開インタフェースにプラットフォームごとに切り替わるAPIを書かないこと.
これをやると,ユーザコードで切り替え用のコードを書く必要があったり,プラットフォームの数だけユーザに
コードの追加を強制させることになったりと,いろいろと影響しそうだ.
基本的に統一されたAPIの中でプラットフォームごとの差分を吸収させるような実装であることが望ましい.

Python3のPickleでうまくunpickleできない問題

ネット上で落としてきたとあるpickleでシリアライズされたファイルをデシリアライズしようとしたらつまづいたのでメモ.

f = open(filename, 'rb')
d = pickle.load(f)
f.close()

#イカdを使って処理...

上記のコード,Python2なら動くのですが,
Python3だと文字コードがどうとか言われてエラーになる,という問題に引っかかった.

f = open(f, 'rb')
enc = pickle._Unpickler(f)
enc.encoding = 'latin1'
d = enc.load()

上記コードによりPython3で回避(´・ω・`)

C++のためのAPIデザイン2章(1)

読書メモメモ
2章は長いので複数回に分けます.

2.1 問題ドメインモデリング

APIは特定の問題解決,タスクの実行のために記述される.そのためにはAPIはその問題の明確なソリューションを提供するべきである.たとえば,問題ドメインの抽象概念化を行いドメインのモデル化を行う.モデルに対して実際の知識や経験が使用することができるため,ユーザフレンドリなAPIとなる.

抽象概念の提供

APIは対象の問題の論理レベルの抽象概念を提供する.つまり,APIが解決するのは概念レベルの課題であり,実装レベルの課題ではない.
APIドキュメントは,テクニカルな知識の無い非プログラマが読んでも理解できるインタフェースの概念と機能が書かれていなければならない.

APIが提供するのは,実装レベルの課題解決策ではなく,より一般的化された問題の解決策であるから,テクニカルな知識無しでドキュメントは書かれるべき,ということなのだろう(また,そうなっているかどうかの確認方法として,非プログラマに読んでもらう必要がある,と).
概念化が足りないということは,そのドキュメントを作成された時に解決を目的とした
問題以外を解くことができない(=過度に問題を具体化しすぎ,範囲を狭め過ぎている?)可能性も起こりえる気がする

2.2 実装詳細の隠蔽

APIの利点は,内部詳細を隠すことで後から使用者に影響を与えずにその詳細を変更できること.

C++の機能的な実装隠蔽方法について書かれていた.

  • 宣言と定義による物理的な隠蔽
  • 論理的な隠蔽(アクセスレベル(public, private, protected),カプセル化)
  • メンバ変数の隠蔽・クラスの隠蔽

→メンバ変数の隠蔽およびカプセル化は,入力値・出力値の論理的チェックを可能にする点でも有効.
また,相互に影響しあうメンバ変数が互いにユーザから自由に変更が可能(public)にされていた場合,
それらの関係性は即座に破綻するだろうから,そういった場面でも必要な手段だろう.

ちなみに,書籍には遅延評価(setterで値が設定されても,値の出力が必要な場面までは計算を行わない),
キャッシングなどについても説明されていた.
遅延評価の具体例は,簡単に例をあげればsetterと入力の値の計算がセットになっていた場合(遅延評価無し)

obj.set(a);//setと計算(1)
obj.set(b);//setと計算(2)
x=obj.get();//出力(3)

のように出力(3)の場面においてobj.set(b)の値が使用されるため,
(1)の計算が無駄になってしまう.
そこで,実際に値の計算が必要な時に行えるようにgetterの方に計算を実装(評価を遅延させる)すれば

obj.set(a);//setのみ
obj.set(b);//setのみ
x=obj.get();//計算と出力

のように計算回数は無駄なく1回で済む.
という理解でいいよね?

C++のためのAPIデザイン1章(2)

昨日の続き.

APIの必要性】
ソフトウェアプロジェクトにAPIを使うべきかどうかは次の2点を検討する
(1)自分でAPIを設計し,記述するべきかどうか
(2)自分のアプリケーションに他人のAPIを使うべきかどうか

APIを作成することの利点として"コードの堅牢性"が向上することがあげられる.利点は以下のとおり.
・実装の隠蔽
モジュールの実装の詳細を隠せば,将来,ユーザを混乱させることなく実装を変更できる.隠蔽がない場合,のデメリットは昨日あげた通り,APIの更新に苦労したり,APIの利用者側のコードの変更が必要になってしまう.

・長寿化
実装を詳細に開示したシステムは,時間が立つに連れ,システム内部の詳細と密結合するコードになりやすく,修正が難しくなる.
優れたAPI設計に投資を行って,その後は一貫したデザインをキープするように徐々に保守をしていけば,コストも抑えられ,ソフトウェアの長寿化につながる
→実装が隠蔽されたAPIを利用する/APIとして機能を切り離すことで,システムとは(少なくともAPI以下のレベルで)疎結合にできる.結果として保守へのコストが下がる,という理解で良いのかな?

・モジュール化の促進
APIは,特定のタスクやユースケースの達成のために作成されるため,結果として特定の分野に特化して機能がモジュールとして作成することができる.
APIの上集合上にアプリケーションを作成すると,内部のモジュールが別のモジュールと依存することが少なくなり,疎結合となる.

・コードの重複の削除
APIを使用することで,コードの重複を減らせる.また,更新の際もAPIを1箇所変更するだけで対応可能となる.
→重複する機能を見つけた場合,API化を検討してみると良いかもしれない

・ハードコードされた値を取り除く
→容易に想像がつく

・実装が隠蔽されているため,最適化・更新・リファクタリングが容易になる

APIを使用するべきではない場合】
[自分でAPIを書く場合]
他の開発者が使用することを想定した強力で安定したインタフェースを作成する必要があり,品質・設計・ドキュメント・テスト・サポートなどのレベルの高い保守が必要となる.
→多くのソフトから利用されるようになればなるほどAPIの保守は大変そうだ.APIの設計をきちんとしておかなければ,このコストが大きくなりそう.

[サードパーティAPIを利用する場合]
・ライセンスの制約
GPLなどの感染性のあるライセンスは,商用アプリケーションやソースを公開したくないプロジェクトでは使用できない.

・機能がかみあわない

ソースコードの欠落
APIにバグがあった場合でも,ソースコードを追うことでその回避策が見つかる可能性があるが,ソースコードが公開されていないAPIではそれを行うことができない.
→ソフトウェア開発において,潜在的なバグを含んだ状態で開発をすすめるリスクを追う.コードの"隠蔽"と"欠落"では大きく異るね

・ドキュメントの欠落
ソースコードの欠落と同様に,動作の制限事項などについてドキュメントがまとめられていない場合,ソフトウェア開発では不適切な場合がある

この章の残りはAPIの実例紹介など.