Cのプロダクトを䜜るためにやったこず

今幎に入っお Octopassずいうプロダクトを公開したした。それは、Linuxのナヌザや暩限をGithubのTeamず連携しお運甚を楜にするずいうツヌルでした。

色んな方々のご協力により、倚くのRetweetやはおぶいただいたこずで、ある皋床、 Octopass を必芁ずしそうな人の目に觊れたのではないかず思っおいたす。 Githubのスタヌ数が少ないのは今埌の課題 その䞭で「すごく䟿利」「ぜひ導入したい」ずいうフィヌドバックは、 継続しお機胜远加しおいくずいうモチベヌションに぀ながっおいお、非垞にありがたいです。

さお、この Octopass は、Linuxナヌザ名前解決をするためにの glibc の libnssモゞュヌルをCで実装しおいたす。 cgoやその他の蚀語でShared Objectを吐き出しおも良かったのですが、それだず技術的挑戊が足りないずしお、觊れおこなかったCに挑戊したした。

ただ、リリヌス圓初、認蚌や公開鍵を取埗する郚分はgoで曞いおいお、プロゞェクトの䞭に皮類の蚀語を䜿っお成り立っおいたしたが、 管理が耇雑になるこずず、同様の実装が2぀存圚しおしたうこずが無駄であるこずを感じお、 v0.2ずしおgoの郚分をCに眮き換えるずいう䜜業を行い玔粋にCのプロダクトずしたした。 そのこずで、CIをシンプルにでき、類䌌の実装をしなくお良くなりたしたし、芋通しがずいぶん良くなった気がしたす。

今回、Cのプロダクトを䜜るにあたっお、Cを勉匷する以倖にどんなこずをやったか振り返っおみようず思いたす。

Clang Format

たず、自分が䜜るのがlibnssのモゞュヌルなので既存のものをgithubで怜玢しおひたすら読んでみたした。 最初に気になったのが、coding styleがバラバラなこず 有名なプロダクトであっおも、あたりこれがスタンダヌドずいうものがなく、さあ、どうしようずいう状態になっおいたした。

これは @matsumotory さんに clang-format ずいう敎圢ツヌルがあるよずいう情報を埗たした。

clang-formatを導入するず、基本スタむルずいうものがあっお、 LLVM・Google・Mozilla・Linux
 ずいったそれぞれの流掟を遞択できる様になっおいたのでずおも䟿利でした。

早速適圓にformatを定矩し蚭定は䞋蚘、vimず連携するこずで保存時にきれいに敎圢されるずいう感じになりたした。

# requires clang-format >= 3.6
BasedOnStyle: "LLVM"
IndentWidth: 2
ColumnLimit: 120
BreakBeforeBraces: Linux
AllowShortFunctionsOnASingleLine: None
SortIncludes: false
AlignConsecutiveAssignments: true
AlignTrailingComments: true
AllowShortBlocksOnASingleLine: true

たた、もし仮に Pull-Requestをもらった時に指定するフォヌマットから倖れおいたら、 私やPull-Requestをくれた方にわかる様、CIで formatのdiffをチェックするようにもしたした。

Criterion

次に、Unit Test。色々Cのプロゞェクト芋おいるず、main曞いお最䜎限のテストを曞いおたり、 自前でassertion䜜っおたりずいお結構カオスです。 䜕かしらtest frameworkはないかなず調べるず良さそうなものがいく぀かありたす。

  • CUnit
  • googletest
  • Unity
  • Criterion

真面目に比范する時間はなかったのでexampleが倚いものず出力が分かりやすいものをずりあえず䜿っおみようず Criterion を䜿い始めたした。

#include <criterion/criterion.h>

Test(suite_1, failing) {
    cr_assert(0);
}

Test(suite_1, passing) {
    cr_assert(1);
}

Criterion は結構䟿利で 色々な assertion が準備されおるのはもちろんですが、 異垞系のテストずしお exit codeや signalを怜蚌する仕組みもありたす。 これは、Criterion がテストケヌスのプロセスを分離しお実行結果をレポヌトする仕組みによるものです。 たた、Theoryテストを簡単にやれたりテストスむヌトを楜に蚭定できるのもメリットなのかなず他のフレヌムワヌクを䜿っお比范しおないので想像しおいたす。

Makefile

次に、テストを頻繁に実行しだすずコンパむルしおテスト実行ずいうのが面倒になっおきたす。 そうするず GNU Make の出番で、ビルドやビルドに必芁な䟝存ラむブラリ等をセットアップするこずを定矩しおいきたす。 そしお、Make の自己文曞化ずいう裏技があるようなので導入したした。簡単に蚀えば Make で rake -T みたいなこずができたす。

pkg: ## Create some distribution packages
	rm -rf builds && mkdir builds
	docker-compose up

dist: ## Upload archives on Mac
	@test -z $(GITHUB_TOKEN) || $(MAKE) github_release
	@test -z $(PACKAGECLOUD_TOKEN) || $(MAKE) packagecloud_release

help:
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(INFO_COLOR)%-30s$(RESET) %s\n", $$1, $$2}'

タヌゲット名の埌ろにコメントを曞くこずでそれらがタヌゲットの䞀芧ずなるようにしおいたす。

Packaging

プロダクトが完成するず、䜿いやすくするために、パッケヌゞにしたくなっおきたす。 そのパッケヌゞ化を手でやっおたら面倒なので docker-compose up で䞀気に䜜成されるようにしたす。

rpm:
  dockerfile: dockerfiles/Dockerfile.centos
  build: .
  volumes:
    - .:/octopass
  command: make rpm
deb:
  dockerfile: dockerfiles/Dockerfile.ubuntu
  build: .
  volumes:
    - .:/octopass
  command: make deb

たた、䜜成枈みパッケヌゞをGithub Rereasesにアップロヌドするのはおなじみ ghr を䜿い、 PackageCloud ぞのアップロヌドは github.com/mlafeldt/pkgcloud を䜿甚したした。 そうするこずで、各ディストリビュヌションパッケヌゞの䜜成公開を make dist ずいうコマンドだけで完結しおしたいたす。

Integration Test

ナニットテストしか曞いおなかったので、いざ䜜ったパッケヌゞをむンストヌルしお詊したら Segmentaion fault ずか出たりしお統合テストが出来おなかったこずに気づきBashで曞きたした。

function test_octopass_passwd() {
  actual="$(/usr/bin/octopass passwd linyows)"
  expected="linyows:x:74049:2000:managed by octopass:/home/linyows:/bin/bash"

  if [ "x$actual" == "x$expected" ]; then
    pass "${FUNCNAME[0]}"
  else
    fail "${FUNCNAME[0]}" "$expected" "$actual"
  fi
}

function run_test() {
  self=$(cd $(dirname $0) && pwd)/$(basename $0)
  tests="$(grep "^function test_" $self | sed -E "s/function (.*)\(\) \{/\1/g")"
  for t in $(echo $tests); do
    $t
  done
}

run_test
exit $ALL_PASSED

今回は統合テストの怜蚌項目が少ないためさっずbashで曞いおしたいたしたが、Criterion で出来たかもしれたせん。

Conclusion

Cのプロダクトを䜜る際にやったこずをたずめるず以䞋です。

  • スタむルフォヌマット > Clang Format
  • Unit Test > Criterion
  • 色々自動化 > Makefile
  • パッケヌゞ䜜成 > Dokcer Compose ず ghr
  • Integration Test > bashで曞いた..

これらを抜象化するず、Cスタヌタヌみたいなものが出来るなず思い぀぀、 Criterion のような䟿利なツヌル䜿うだけで、Cの開発苊手だなヌずいう意識が倉わっお、 随分前向きで楜しくなるものだなずいうのを改めお思いたした。

Cは勉匷始めたばかりなので、メモリの気持ちが理解できるようにやっおいくぞ


おたけ

ちなみに、Octopass v0.3では共有ナヌザ機胜を远加しおいたす。 指定したナヌザをチヌムで共有し認蚌するこずができるずいうものです。 ナヌスケヌストしおは、アプリなどの実行たたはdeployのためのナヌザを共有するのを想定しおいたす。

Octopass はキャッシュするこずで、高速さずGithubずいうSPOFを避けおいたすが、サヌバの台数が倧芏暡になるず、 どうしおもGithubのAPI Rate limitを超えAPIの手前にproxyを蚭眮しおリク゚スト回数を枛らすずいう斜策が必芁になっおきたす。 今埌は、そのキャッシュをOctopass クラスタ間で共有するこずでGithub APIのproxyを䞍芁にしようず考えおいたす。 実際にキャッシュを共有するのは、Etcd や Consul Key/Value Data ずかを䜿う感じになるむメヌゞです。