TFDrift-Falco v0.2.0-beta リリース後の品質改善作業【開発日記】
はじめに
TFDrift-Falco v0.2.0-betaを2025年12月5日にリリースした直後、品質とセキュリティをさらに向上させるため、追加の改善作業を実施しました。本記事では、その実装プロセスを開発日記形式で記録します。
背景と目的
v0.2.0-betaリリース後、以下の課題が残っていました:
- Backendパッケージのテストカバレッジが0% - 新規追加したTerraform backend抽象化層が未テスト
- ベンチマークテストが無効化されている - パフォーマンス測定基準がない
- Snykセキュリティスキャンの設定不備 - SARIF出力が正しく生成されない
- テストカバレッジの検証が不十分 - 全体的な品質指標の確認が必要
これらを解決し、OSSプロジェクトとしての完成度を高めることが今回の目標です。
開発日記
Day 1: Backend パッケージのテスト実装
現状分析
まず、backendパッケージの構成を確認:
pkg/terraform/backend/
├── factory.go # バックエンドファクトリー(未テスト)
├── local.go # ローカルファイルバックエンド(未テスト)
└── s3.go # S3バックエンド(未テスト)
カバレッジ確認:
$ go test -cover ./pkg/terraform/backend
coverage: 0.0% of statements
全く未テストの状態でした。
テスト設計
3つの責務に分けてテストを設計:
- Factory Pattern - バックエンド選択ロジック
- Local Backend - ファイル操作とバリデーション
- S3 Backend - 設定検証(実際のS3アクセスはIntegration Testで)
実装
local_test.go を作成:
- ファイル存在チェック
- パスのバリデーション
- 状態ファイルの読み込み
- エラーハンドリング(ファイル削除後のLoad)
func TestNewLocalBackend(t *testing.T) {
tests := []struct {
name string
path string
setup func(string) error
wantError bool
}{
{
name: "existing file",
path: "test.tfstate",
setup: func(path string) error {
return os.WriteFile(path, []byte(`{"version": 4}`), 0600)
},
wantError: false,
},
// ... more test cases
}
}
s3_test.go を作成:
- Bucket/Key必須チェック
- Region デフォルト値(us-east-1)
- クライアント初期化
factory_test.go を作成:
- Local/S3バックエンドの選択
- 未サポートバックエンドのエラーハンドリング
結果
$ go test -cover ./pkg/terraform/backend
ok github.com/keitahigaki/tfdrift-falco/pkg/terraform/backend 2.831s coverage: 67.4% of statements
カバレッジ: 0.0% → 67.4% 🎉
残りの32.6%は主にS3の実際のLoad処理(AWS SDK呼び出し)で、これはIntegration Testでカバーする方針です。
Day 2: ベンチマークテストの有効化
問題発見
ベンチマークテストは //go:build ignore タグで無効化されていました:
$ ls tests/benchmark/
event_processing_bench_test.go # //go:build ignore
memory_usage_test.go # //go:build ignore
タグを削除して実行すると、APIの互換性エラーが発生:
det.HandleEvent undefined (but does have unexported method handleEvent)
det.GetStateManager undefined
原因分析
Detectorのリファクタリングで、以下の変更がありました:
-
HandleEvent()→handleEvent()(非公開化) -
GetStateManager()→ 削除(直接アクセスに変更)
しかし、ベンチマークテストは古いAPIを使用していました。
解決策
テスト専用のヘルパーメソッドを作成:
pkg/detector/testing.go を新規作成:
package detector
import "github.com/keitahigaki/tfdrift-falco/pkg/types"
// HandleEventForTest is a test helper that exposes handleEvent
func (d *Detector) HandleEventForTest(event types.Event) {
d.handleEvent(event)
}
// GetStateManagerForTest exposes the state manager for testing
func (d *Detector) GetStateManagerForTest() interface{} {
return d.stateManager
}
これにより、本番コードのカプセル化を維持しつつ、テストからアクセス可能になります。
ベンチマークテストの修正
すべての HandleEvent 呼び出しを HandleEventForTest に変更:
func BenchmarkEventProcessing_Single(b *testing.B) {
det := setupBenchmarkDetector(b)
event := createBenchmarkEvent()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
det.HandleEventForTest(event) // ← 修正
}
}
メモリテストの修正
メモリ測定で uint64 アンダーフローが発生していました:
// 問題のコード
allocDiff := memAfter.Alloc - memBefore.Alloc // GCでmemAfter < memBeforeになると巨大な値に
原因:runtime.MemStats.Alloc はGCで減少するため、差分が負になることがあります。
解決策:TotalAlloc(累積割り当て量、単調増加)を使用:
// 修正後
totalAllocDiff := memAfter.TotalAlloc - memBefore.TotalAlloc
avgPerEvent := totalAllocDiff / uint64(numEvents)
ベンチマーク結果
すべてのテストが成功!パフォーマンスベースラインを確立:
BenchmarkEventProcessing_Single-14 4344 44507 ns/op 9564 B/op 117 allocs/op
BenchmarkEventProcessing_Batch-14 30 4456894 ns/op 956465 B/op 11704 allocs/op
BenchmarkEventParsing-14 24639178 5.048 ns/op 0 B/op 0 allocs/op
BenchmarkStateComparison-14 31610491 3.912 ns/op 0 B/op 0 allocs/op
BenchmarkDriftDetection_EC2-14 4927 45019 ns/op 9494 B/op 115 allocs/op
BenchmarkDriftDetection_IAM-14 3804 42841 ns/op 9505 B/op 118 allocs/op
BenchmarkDriftDetection_S3-14 3520 44181 ns/op 9437 B/op 115 allocs/op
BenchmarkConcurrentEvents-14 3886 45974 ns/op 9622 B/op 117 allocs/op
Key Insights:
- イベント処理: ~44μs/op(22,000 events/sec処理可能)
- メモリ効率: ~9.5KB/op(妥当な範囲)
- 状態比較: ~4ns/op(キャッシュ効果抜群)
Day 3: Snykセキュリティスキャンの改善
問題の特定
.github/workflows/security.yml のSnyk設定を確認:
- name: Run Snyk to check for vulnerabilities
uses: snyk/actions/golang@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
- name: Upload Snyk results to GitHub Code Scanning
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: snyk.sarif # ← ファイルが生成されていない!
Snykアクションは snyk.sarif を自動生成しませんでした。
解決策
SARIF出力を明示的に指定:
- name: Run Snyk to check for vulnerabilities
uses: snyk/actions/golang@master
continue-on-error: true
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high --sarif-file-output=snyk.sarif
command: test
- name: Upload Snyk results to GitHub Code Scanning
uses: github/codeql-action/upload-sarif@v3
if: always() # ← 失敗してもアップロード
with:
sarif_file: snyk.sarif
セキュリティドキュメントの整備
.github/SECURITY.md を作成:
- サポートバージョンの明示
- 脆弱性報告プロセス
- セキュリティツールの説明(Snyk、GoSec、Nancy)
- Snykトークンの設定手順
scripts/security-scan.sh を作成:
ローカルで3つのセキュリティツールを一括実行:
#!/bin/bash
# Security scanning script for local development
# Run gosec
gosec -fmt=text -exclude=G104 ./...
# Run nancy
go list -json -deps ./... | nancy sleuth
# Run govulncheck
go list -json -m all | govulncheck -mode=binary ./...
README更新
開発セットアップセクションにセキュリティスキャンを追加:
### Security Scanning
Multiple security tools run on every commit:
- **Snyk**: Dependency vulnerability scanning
- **GoSec**: Go code security audit
- **Nancy**: OSS dependency scanner
Run local security scans:
\`\`\`bash
./scripts/security-scan.sh
\`\`\`
Day 4: テストカバレッジの検証
全体カバレッジの確認
$ go test -cover ./...
ok github.com/keitahigaki/tfdrift-falco/pkg/cloudtrail coverage: 93.9% of statements
ok github.com/keitahigaki/tfdrift-falco/pkg/detector coverage: 88.7% of statements
ok github.com/keitahigaki/tfdrift-falco/pkg/diff coverage: 98.2% of statements
ok github.com/keitahigaki/tfdrift-falco/pkg/metrics coverage: 100.0% of statements
ok github.com/keitahigaki/tfdrift-falco/pkg/notifier coverage: 95.6% of statements
ok github.com/keitahigaki/tfdrift-falco/pkg/terraform coverage: 97.6% of statements
ok github.com/keitahigaki/tfdrift-falco/pkg/terraform/backend coverage: 67.4% of statements
ok github.com/keitahigaki/tfdrift-falco/tests/benchmark coverage: 71.4% of statements
total: (statements) 71.9%
パッケージ別分析
| パッケージ | カバレッジ | 評価 |
|---|---|---|
| pkg/metrics | 100.0% | ✅ 完璧 |
| pkg/diff | 98.2% | ✅ 優秀 |
| pkg/terraform | 97.6% | ✅ 優秀 |
| pkg/notifier | 95.6% | ✅ 優秀 |
| pkg/cloudtrail | 93.9% | ✅ 優秀 |
| pkg/detector | 88.7% | ✅ 良好 |
| tests/benchmark | 71.4% | ✅ 良好 |
| pkg/terraform/backend | 67.4% | ✅ 改善! |
カバレッジレポート生成
$ go tool cover -func=coverage.out | tail -20
主な未カバー箇所:
- E2E/Integration テストヘルパー(実際のAWS/Falco環境が必要)
- S3BackendのLoad実装(AWS SDK呼び出し)
- CLI エントリーポイント(統合テストでカバー)
これらは意図的に除外しており、問題ありません。
成果まとめ
📊 数値での成果
| 項目 | Before | After | 改善 |
|---|---|---|---|
| Backend カバレッジ | 0.0% | 67.4% | +67.4% |
| ベンチマークテスト | 無効 | 有効 | ✅ |
| メモリテスト | エラー | 成功 | ✅ |
| セキュリティスキャン | 不完全 | 完全 | ✅ |
| 全体カバレッジ | - | 71.9% | - |
🎯 確立したパフォーマンスベースライン
- イベント処理: 44μs/op → 22,000 events/sec処理可能
- メモリ効率: 9.5KB/event → 1万イベントで95MB
- 状態比較: 4ns/op → キャッシュ効率が高い
🔒 セキュリティ強化
- Snyk: 依存関係の脆弱性スキャン(SARIF対応)
- GoSec: Goコードのセキュリティ監査
- Nancy: OSS依存関係スキャナー
-
ローカル実行:
./scripts/security-scan.shで即座に確認可能
📝 ドキュメント整備
-
.github/SECURITY.md: セキュリティポリシー -
scripts/security-scan.sh: ローカルセキュリティチェック - README.md: セキュリティスキャンセクション追加
💾 コミット履歴
770d1e7 test: add backend tests and enable benchmark tests
044cc92 chore: improve Snyk security scanning setup
学んだこと
1. テストヘルパーメソッドの有用性
本番コードのカプセル化を維持しつつ、テストからアクセスを可能にする *ForTest() パターンは有効でした。
// 本番コードは非公開のまま
func (d *Detector) handleEvent(event types.Event) { ... }
// テスト用に公開
func (d *Detector) HandleEventForTest(event types.Event) {
d.handleEvent(event)
}
2. メモリ測定の落とし穴
runtime.MemStats.Alloc は現在の割り当て量(GCで減少)、TotalAlloc は累積割り当て量(単調増加)です。差分計算では必ず TotalAlloc を使うべきです。
3. SARIF出力の重要性
GitHub Code Scanningと統合するには、SARIF形式のセキュリティレポートが必須です。ツールごとに出力方法が異なるため、ドキュメント確認が重要です。
4. ベンチマークは定期実行すべき
パフォーマンス退行を防ぐため、ベンチマークテストは定期的に実行し、結果を記録すべきです。今回確立したベースラインを今後の指標とします。
今後の展望
短期(次のマイナーリリース)
- Snykトークンの設定(リポジトリシークレット)
- E2E/Integration テスト環境の構築
- パフォーマンス監視の自動化
中期(v0.3.0)
- より多くのAWSサービス対応
- マルチリージョン対応の強化
- カスタムルールエンジンの実装
長期(v1.0.0に向けて)
- エンタープライズ機能(RBAC、監査ログ)
- プラグインシステム
- Web UIダッシュボード
まとめ
v0.2.0-betaリリース後、わずか4日間で以下を達成しました:
✅ Backendパッケージのテストカバレッジ 0% → 67.4%
✅ ベンチマークテストの有効化とベースライン確立
✅ セキュリティスキャンの完全な設定
✅ 包括的なドキュメント整備
OSSプロジェクトとして、コードの品質だけでなく、セキュリティ、パフォーマンス、ドキュメントのすべてを向上させることができました。
これにより、TFDrift-Falcoは本番環境での利用により適した状態になりました 🎉