前言
昨天收到一封用户邮件,提到CodeLab的IoT服务器证书过期了。
原因是Let’s Encrypt每三个月会更新一次证书(推荐使用acme.sh),而我的mqtt broker又要求直接把证书放在它的特定目录下。于是我此前的做法是,每次Let’s Encrypt更新证书,我就手动复制一份到mqtt broker配置目录里,然后重启它。
麻烦是,有时候我会忘掉这件事,这不,前头的用户邮件就是一个例子。
DRY(Don’t repeat yourself)是面向对象编程中的基本原则,程序员的行事准则。旨在软件开发中,减少重复的信息。 DRY的原则是“系统中的每一部分,都必须有一个单一的、明确的、权威的代表”,指的是(由人编写而非机器生成的)代码和测试所构成的系统,必须能够表达所应表达的内容,但是不能含有任何重复代码。
将DRY的概念延伸到日常工作上,会让我们视图将周期性/重复劳动自动化。
今年比较热门的RPA(机器人流程自动化), 这个想法便与此有关。
我挺喜欢将编程技能用于解决日常生活里的问题,此前做过一些有关笔记:
解决问题
为了避免每三个月手动更新一次证书,我准备将其自动化
思路
思路其实很简单,使用watchdog监控证书文件,当其更新时,触发一个脚本,该脚本会将证书复制到目标路径,并重启mqtt broker。犹豫服务器基本不会关机,所有整个程序运行在tmux里即可,而不必构建开机自启程序(supervisor).
我原先想采用schedule,定时
触发程序,但证书的确切更新时间我是不知的。所以采用watchdog会更合适,这是一种典型的事件驱动: 当发生x时触发y。
动手
首先安装watchdog:
pip install watchdog
会同时为我们装上watchdog(Python库)和watchmedo(命令行工具)
我们先来看看命令行工具的用法:
和watchmedo
1
2
3
4
5
|
watchmedo log \
--patterns="*.py;*.txt" \
--ignore-directories \
--recursive \
.
|
以上脚本的含义是: 递归记录与当前目录下*.py和*.txt文件有关的事件
使用shell-command
参数,来触发脚本
1
2
3
4
5
|
watchmedo shell-command \
--patterns="*.py;*.txt" \
--recursive \
--command='echo "${watch_event_type}"' \
.
|
我接下来想表达这个逻辑: 如果${watch_event_type}
是created
,则执行目标脚本,结果写bash if条件语句老是出错,原本以为是个hello world级的工作,bash语法真迷。
watchdog
watchdog是Python库!
上述逻辑我们可以轻松写出, 微调以下官方示例即可:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
# python3 mycertsdog.py .
import logging
import sys
import time
import subprocess
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
logging.basicConfig(level=logging.DEBUG)
class MyEventHandler(FileSystemEventHandler):
def my_task(self):
subprocess.call("cp /home/wwj/.acme.sh/codelab.club/codelab.club.key /home/wwj/mqtt/emqx/etc/certs/key.pem",shell=True)
subprocess.call("cp /home/wwj/.acme.sh/codelab.club/fullchain.cer /home/wwj/mqtt/emqx/etc/certs/cert.pem",shell=True)
subprocess.call("/home/wwj/mqtt/emqx/bin/emqx restart",shell=True)
def on_modified(self, event):
# test: cp ~/privkey.pem privkey.pem
# import IPython;IPython.embed()
if "codelab.club.key" in event.src_path:
logging.info(event)
time.sleep(1)
self.my_task()
path = sys.argv[1]
event_handler = MyEventHandler()
observer = Observer()
observer.schedule(event_handler, path, recursive=True)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
|
在tmux中运行
1
|
python3 mycertsdog.py /home/wwj/.acme.sh/codelab.club/
|
通知
我想让下次更新证书时,给我发个通知。
准备采用ifttt webhook。
1
|
curl -X POST https://maker.ifttt.com/trigger/cert_update/with/key/xxxxxx
|
塞到my_task
,使用subprocess调用它即可。
如果你准备给自己发邮件(SMTP)的话,可以试试notifiers
参考