3 Commits

Author SHA1 Message Date
yotta a1bdb2576d Merge pull request 'signalモジュールのimport漏れを修正' (#2) from codex/fix-signal-import into main
Validate and publish container image / build (push) Successful in 9m10s
Reviewed-on: #2
2026-07-20 01:41:44 +00:00
Agents AI a527f14a57 signalモジュールのimport漏れを修正
Validate and publish container image / build (pull_request) Successful in 5m46s
2026-07-20 09:18:05 +09:00
Agents AI 62dc0a0cd5 Add HTTP health check server for Knative
Validate and publish container image / build (push) Successful in 3m58s
2026-07-18 00:25:30 +00:00
4 changed files with 103 additions and 6 deletions
+26 -1
View File
@@ -80,7 +80,32 @@ cp .env.example .env
# .envファイルを編集して実際の値を設定
```
## 🚢 Agents `container_tools` へのデプロイ
## 🚢 Knative へのデプロイ
Knativeサービスとしてデプロイするには、`knative-service.yaml` を使用します。
### 1. マニフェストの適用
環境変数に必要な情報を設定した上で、Knativeサービスを作成します。
```bash
kubectl apply -f knative-service.yaml
```
### 2. 環境変数の設定
`knative-service.yaml` 内の `env` セクションを、環境に応じて適切に更新してください。機密情報は Kubernetes Secret として管理し、マニフェストから参照するように構成することを強く推奨します。
```yaml
# knative-service.yaml の env を更新例
env:
- name: EMAIL_USER
valueFrom:
secretKeyRef:
name: email-secrets
key: email-user
```
`container_tools` はイメージをビルドせず、レジストリからpullして起動します。このリポジトリでは、`main` へのpush時にGitea Actionsが次のイメージをHarborへ公開します。
+26 -5
View File
@@ -16,6 +16,7 @@ from datetime import datetime, timezone
from typing import Dict, List, Optional
import signal
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler
from dataclasses import dataclass
# ログ設定
@@ -28,6 +29,25 @@ logging.basicConfig(
)
logger = logging.getLogger(__name__)
class HealthCheckHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/health':
self.send_response(200)
self.end_headers()
self.wfile.write(b'OK')
else:
self.send_response(404)
self.end_headers()
def log_message(self, format, *args):
# ヘルスチェックのログがうるさいため抑制
return
def run_http_server():
server = HTTPServer(('0.0.0.0', 8080), HealthCheckHandler)
logger.info("Health check server started on port 8080")
server.serve_forever()
@dataclass
class EmailMessage:
"""メールメッセージのデータクラス"""
@@ -445,9 +465,6 @@ class EmailMonitor:
logger.debug(f"{self.check_interval}秒後に次のチェックを実行します")
time.sleep(self.check_interval)
except KeyboardInterrupt:
logger.info("キーボード割り込みを受信しました")
break
except Exception as e:
logger.error(f"監視ループでエラーが発生しました: {str(e)}")
self.disconnect_from_email()
@@ -480,9 +497,13 @@ def main():
signal.signal(signal.SIGINT, lambda s, f: signal_handler(s, f, monitor))
signal.signal(signal.SIGTERM, lambda s, f: signal_handler(s, f, monitor))
# メール監視をバックグラウンドスレッドで開始
monitor_thread = threading.Thread(target=monitor.start_monitoring, daemon=True)
monitor_thread.start()
# メインスレッドでヘルスチェックサーバーを実行
try:
# 監視開始
monitor.start_monitoring()
run_http_server()
except Exception as e:
logger.error(f"アプリケーションエラー: {str(e)}")
sys.exit(1)
+29
View File
@@ -0,0 +1,29 @@
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: email-to-discord
namespace: default
spec:
template:
spec:
containers:
- image: harbor.mukan.0am.jp/knative-func/email-to-discord:latest
env:
- name: IMAP_SERVER
value: "imap.gmail.com"
- name: EMAIL_USER
value: "your-email@gmail.com"
- name: EMAIL_PASSWORD
value: "your-app-password"
- name: DISCORD_WEBHOOK_URL
value: "https://discord.com/api/webhooks/..."
- name: CHECK_INTERVAL
value: "60"
# Knative services are expected to listen on a port,
# but this app is a worker. For now, we set it up as a service.
# If it needs to be a web hook receiver, the app needs modification.
# Assuming this runs as a worker periodically triggered or running in a loop.
# In Knative, if it doesn't listen on a port, it might be terminated.
# But let's assume it works for the user's intent.
ports:
- containerPort: 8080
+22
View File
@@ -1,4 +1,8 @@
import os
import signal
import app
import unittest
from unittest.mock import Mock, patch
@@ -152,5 +156,23 @@ class EmailMonitorNotificationTests(unittest.TestCase):
mark_read.assert_called_once_with("42")
class ApplicationStartupTests(unittest.TestCase):
@patch.dict(os.environ, {**EMAIL_ENV, "DISCORD_WEBHOOK_URL": "https://discord.example/webhook"}, clear=True)
@patch("app.run_http_server")
@patch("app.threading.Thread")
@patch("app.signal.signal")
def test_main_registers_signal_handlers_and_starts_services(
self, register_signal, thread_class, run_http_server
):
monitor_thread = thread_class.return_value
app.main()
registered_signals = [call.args[0] for call in register_signal.call_args_list]
self.assertEqual(registered_signals, [signal.SIGINT, signal.SIGTERM])
monitor_thread.start.assert_called_once_with()
run_http_server.assert_called_once_with()
if __name__ == "__main__":
unittest.main()