Telegram 憑證監控機器人實作 EP1 — 讀取 MongoDB Domain Info

更新於 發佈於 閱讀時間約 27 分鐘
raw-image

👨‍💻 簡介

上次做的憑證監控已經可以正常運作了,但這次希望能夠不從 yaml 讀取 domain info,而是從 MongoDB 進行讀取,方便未來的擴充性。

這次的重點是要透過 Python 連接 MongoDB,並且透過 Python 讀取 MongoDB,最後透過 Python 寫入 MongoDB。

🛠️ 使用工具

  • Python 3.9.6
  • MongoDB
  • Mongoshell
  • Docker
  • Docker-Compose

📝 功能需求

  • 建立 MongoDB docker-compose
  • 透過 Python 連接 MongoDB
  • 透過 Python 讀取 yaml 並寫入 MongoDB
  • 透過 Python 傳入 env 以及 domain 寫入 MongoDB
  • 透過 Python 讀取 MongoDB
  • 透過 Python 修改 MongoDB
  • 透過 Python 刪除 MongoDB

🎯Setup

  1. 建立 MongoDB docker-compose

要簡單使用 MongoDB 可以用 docker-compose 快速拉起:

version: "3.1"

services:
mongodb:
image: mongo:latest
container_name: mongodb_container
environment:
MONGO_INITDB_ROOT_USERNAME: rootuser
MONGO_INITDB_ROOT_PASSWORD: rootpass
ports:
- "27017:27017"
volumes:
- mongodb_data_container:/data/db
volumes:
mongodb_data_container:
docker-compose up -d

2. 透過 Python 連接 MongoDB

在 Python Library 中,可以使用 PyMongo操作 MongoDB。 要使用這個 Library 要先透過安裝:

python3 -m pip install pymongo

接著可以簡單操作一筆資料看是否正常:

# 導入 pymongo 模塊中的 MongoClient 類,用於與 MongoDB 資料庫進行連接
from pymongo import MongoClient
from pymongo.errors import ConnectionFailure

# 定義 MongoDB 連接 URI,包含用戶名、密碼、服務器地址、端口和資料庫名
mongodb_uri = "mongodb://rootuser:rootpass@localhost:27017/mydatabase?authSource=admin"

try:
# 嘗試連接 MongoDB
client = MongoClient(mongodb_uri)
# 嘗試獲取服務器訊息,以確認連接
info = client.server_info() # 會在連接失敗時拋出 ConnectionFailure 異常
print("MongoDB 連接成功。Server Info:", info)
except ConnectionFailure:
print("MongoDB 連接失敗。請檢查您的連接設置和Server狀態。")

輸出如下:

MongoDB 連接成功。Server Info: {'version': '7.0.8', 'gitVersion':
'c5d33e55ba38d98e2f48765ec4e55338d67a4a64', 'modules': [], 'allocator': 'tcmalloc',
'javascriptEngine': 'mozjs', 'sysInfo': 'deprecated', 'versionArray': [7, 0, 8, 0], 'openssl':
{'running': 'OpenSSL 3.0.2 15 Mar 2022', 'compiled': 'OpenSSL 3.0.2 15 Mar 2022'},
'buildEnvironment': {'distmod': 'ubuntu2204', 'distarch': 'aarch64', 'cc': '/opt/mongodbtoolchain/v4/
bin/gcc: gcc (GCC) 11.3.0', 'ccflags': '-Werror -include mongo/platform/basic.h -ffp-contract=off
...

稍微包裝一下:

from pymongo import MongoClient
from pymongo.errors import ConnectionFailure

mongodb_uri = "mongodb://rootuser:rootpass@localhost:27017/mydatabase?authSource=admin"

def init_mongo_client(mongodb_uri):
try:
# 嘗試連接 MongoDB
client = MongoClient(mongodb_uri)
# 嘗試獲取服務器訊息,以確認連接
info = client.server_info() # 會在連接失敗時拋出 ConnectionFailure 異常
mongodb_version = info['version']
print("MongoDB 連接成功。Mongo 版本為", mongodb_version)
return client
except ConnectionFailure:
print("MongoDB 連接失敗。請檢查您的連接設置和Server狀態。")

client = init_mongo_client(mongodb_uri)

這樣就代表我們成功連線到 MongoDB 了。

3. 透過 Python 讀取 yaml 並寫入 MongoDB

接下來我想要將讀取的 domains.yaml 寫入 M ongo ,會用到之前的 load_data_from_yaml

client = init_mongo_client(mongodb_uri)
db = client.get_default_database()

# 定義要操作的集合名稱
collection_name = "domains"
collection = db[collection_name]
yaml_file_path = "domains.yaml"
domain_data = load_data_from_yaml(yaml_file_path, "domain_envs")
# 每次執行迴圈都會取得一個鍵值對,env 是 key,格式為 string,
# 而 value 則會是 domains,格式是 list
for env, domains in domain_data.items():
print(f"Writing domains for env {env}: {domains}")
# 這裡組成一個 document,方便儲存到 Mongo 裡
document = {
"env": env,
"domains": domains
}
# 更新條件,匹配那些 env 字段等於當前 env 值的 document
query = {"env": env}
# 將匹配的 document 的內容設為 `document` 字典中的內容
# 使用 upsert=True,如果不存在則插入,存在則更新
collection.update_one(query, {"$set": document}, upsert=True)
print("資料已成功寫入 MongoDB。")
在 Mongo 中,如果指定的集合 (collection) 不存在,當你進行第一次寫入操作(如插入文檔)時,MongoDB 會自動創建這個集合。

集合的創建是懶惰的(lazy),意味著直到你對集合進行了第一次寫入操作(例如使用 `insert_one``insert_many``update_one` 等方法),集合才會被實際創建。

這種設計使得在 MongoDB 中處理集合非常靈活,你不需要事先創建集合就可以開始開發和測試你的應用程序。MongoDB 會根據需要自動管理集合的創建。

儲存要注意的地方是格式,這裡因為使用的是 Mongo,所以我們這邊將讀取的格式組成一個 document。

在 Mongo 中,每個 document 都是以 BSON(Binary JSON) 格式儲存,這是一種類似於 JSON 的格式。

每個文檔都是由鍵值對(key-value pairs)組成的資料結構,其中每個鍵(key)是一個字符串,而值(value)可以是不同類型的資料類型,包括但不限於字符串、數字、布爾值、列表(在 BSON 中稱為)、甚至是嵌套的文檔。

可參考官方文檔[官方文檔](https://www.mongodb.com/docs/manual/reference/bson-types/#bson-types)

update_one基本語法如下:

collection.update_one(filter, update, upsert=False)
  • filter:一個字典,用於指定查詢條件,以匹配需要更新的文檔。
  • update:一個字典,用於指定如何更新匹配的文檔。這通常涉及到 MongoDB 的更新操作符,如 $set$unset 等。
  • upsert:一個可選的 boolean 值,默認為 False。如果設置為 True,當沒有文檔匹配 filter 查詢條件時,update 操作將會作為一個新的 doc 被插入到集合中。

寫入成功後可以透過 MongoShell 或是 Mongo Compass,這邊使用 MongoShell,在 docker-compose.yaml 添加 MongoShell 的 service:

services:
mongoshell:
image: mongo:latest
container_name: mongodb_mongoshell
depends_on:
- mongodb
entrypoint:
[
"mongosh",
"--host",
"mongodb",
"--username",
"rootuser",
"--password",
"rootpass",
"--authenticationDatabase",
"admin",
]
stdin_open: true
tty: true

接著透過以下指令進入 mongoshell:

docker-compose run mongoshell

要查看 collection 需執行以下 query:

use mydatabase
db.domains.find({})

這段代碼用意如下:

  • 選擇 mydatabase db
  • 查詢 domains collection 中所有的 document

輸出類似以下訊息:

[
{
_id: ObjectId('66150a22e1a8ac17b898a2f0'),
env: 'live',
domains: [ 'google.com', 'en.wikipedia.org' ]
}
]

讓我們稍微優化一下程式碼:

def write_domain_data_to_mongodb(mongo_client, collection_name, domain_data):
db = mongo_client.get_default_database()
collection = db[collection_name]

for env, domains in domain_data.items():
document = {
"env": env,
"domains": domains
}
# 更新條件,這裡假設 env 是唯一的
query = {"env": env}
# 使用 upsert=True,如果不存在則插入,存在則更新
collection.update_one(query, {"$set": document}, upsert=True)
print("數據已成功寫入 MongoDB。")

client = init_mongo_client(mongodb_uri)
collection_name = "domains"
yaml_file_path = "domains.yaml"
domain_data = load_data_from_yaml(yaml_file_path, "domain_envs")
write_domain_data_to_mongodb(client, collection_name, domain_data)

這樣就完成了從 yaml 進行寫入,接著需要撰寫另一套,透過傳入 env 以及 domain 資訊來進行寫入。

4. 透過 Python 傳入 env 以及 domain 寫入 MongoDB

會需要這個功能是因為之後要透過 TG Bot 傳入 env 以及 domain 進行寫入的操作:

def add_domain_to_mongodb(collection, env, domain):
# 嘗試添加或更新該 env 的域名
result = collection.update_one(
{"env": env}, {"$addToSet": {"domains": domain}}, upsert=True
)
if result.matched_count > 0 or result.upserted_id is not None:
print("域名已成功添加或更新。")
return True
else:
print("域名添加或更新失敗。")
return False

client = init_mongo_client(mongodb_uri)
db = client.get_default_database()
collection_name = "domains"
collection = db[collection_name]
add_env = "dev"
add_domain = "test.com"
add_domain_to_mongodb(collection, add_env, add_domain)

使用 addToSet 是為了確保新增時,如果已存在不會重複新增,確保每個 domain 的唯一性。

新增的部分告一段落,接著來進行讀取的部分。

5. 透過 Python 讀取 MongoDB

讀取可以透過 collection.find({}) 進行查詢:

db = client.get_default_database()
collection = db[collection_name]
try:
domain_envs = {}
data = collection.find({})
for item in data:
env = item.get("env")
domains = item.get("domains", [])
if env and domains:
domain_envs[env] = domains
print(domain_envs)
except Exception as e:
print(f"從 MongoDB 讀取數據失敗: {e}")
finally:
client.close()

接著優化一下代碼:

def load_domain_envs_from_mongodb(mongo_client, collection_name):
db = mongo_client.get_default_database()
collection = db[collection_name]
try:
domain_envs = {}
data = collection.find({})
for item in data:
env = item.get("env")
domains = item.get("domains", [])
if env and domains:
domain_envs[env] = domains
return domain_envs
except Exception as e:
print(f"從 MongoDB 讀取數據失敗: {e}")
return {}
finally:
client.close()

這樣就算是完成了基本的讀取,接下來要做取得單一 domain 資訊:

def get_domain_from_mongodb(collection, env, domain):
# 構造查詢條件
query = {"env": env, "domains": domain}
# 執行查詢操作
result = collection.find_one(query)
print("get result",result)
if result:
# 找到了相應的文檔,返回域名訊息
print(f"在環境 '{env}' 下找到域名 '{domain}' 的訊息。")
return result
else:
# 沒有找到相應的文檔
print(f"在環境 '{env}' 下未找到域名 '{domain}' 的訊息。")
return None

client = init_mongo_client(mongodb_uri)
db = client.get_default_database()
collection_name = "domains"
collection = db[collection_name]
get_env = "live"
get_domain = "google.com"
get_domain_from_mongodb(collection, get_env, get_domain)

讀取的部分告一段落,接著來進行修改的部分。

6. 透過 Python 修改 MongoDB

假設我們目前的 MongoDB 資料如下:

[
{
_id: ObjectId('66150a22e1a8ac17b898a2f0'),
env: 'live',
domains: [ 'google.com', 'en.wikipedia.org' ]
}
]

我打算將 google.com 改成 github.com,只需要將原本用來新增的 update_one 裡的 query 多一個 domains 欄位:

def update_domain_in_mongodb(collection, env_value, old_domain, new_domain):
# 建立查詢條件和更新動作
query = {"env": env_value, "domains": old_domain}
update_action = {"$set": {"domains.$": new_domain}}

# 執行更新操作
update_result = collection.update_many(query, update_action)
if update_result.matched_count > 0:
print(
f"成功更新文檔。匹配數量: {update_result.matched_count}, 修改數量: {update_result.modified_count}."
)
else:
print("未找到匹配的文檔或域名,更新未執行。")

db = client.get_default_database()
collection = db[collection_name]
env_value = "live"
origin_domain = "google.com"
new_domain = "github.com"

update_domain_in_mongodb(collection, env_value, origin_domain, new_domain)

這裡有用到 $ 佔位符,主要是將查詢語句的第一個值來做 update, 會先查找 env 等於 env_value 並且 domains 等於 old_domain, 接著將 domains list 中第一個匹配的 old_domain 元素更新為 new_domain

可參考官網的操作符文檔

7. 透過 Python 刪除 MongoDB

最後一步要做的是刪除,能夠在指定的 env 刪除 domain

def delete_domain_in_mongodb(collection, env_value, domain_to_delete):
query = {"env": env_value}
delete_action = {"$pull": {"domains": domain_to_delete}}
collection.update_one(query, delete_action)

db = client.get_default_database()
collection = db[collection_name]
env_value = "live"
delete_domain = "github.com"
delete_domain_in_mongodb(collection, env_value, delete_domain)

刪除一樣用 update 方法,然後使用 $pull 操作符,刪除指定的項目,官方文檔在這。

MongoDB 操作的部分就先告一段落,下篇會教如何打造屬於自己的 Telegram Bot,讓我的機器人能夠接收指令。

如果想看完整程式碼可以參考這裡 🔗 專案 repo –> ep1-mongo-setup

📚Reference

avatar-img
17會員
83內容數
golang
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Alan的開發者天地 的其他內容
👨‍💻簡介 最近因為憑證越來越多,需要監控什麼時候到期,當到期時發送到期通知,因此撰寫一個簡單的小程式來完成。 這次使用Python和Telegram Bot來監控SSL證書的到期時間並發送通知。並使用GCP工具,如CloudFunction和CloudScheduler做部署平台。
引言 在當今的技術世界中,Kubernetes 已成為容器化應用的領導平台。作為一個高效的容器編排系統,它不僅管理著容器的部署和擴展,還提供了必要的自動化支持,以保證應用的高可用性和性能。在這個框架中,自動擴展功能起著至關重要的作用,特別是在面對不斷變化的負載和需求時。
👨‍💻簡介 在當今的雲計算時代,容器化和微服務架構成為了重要趨勢。Kubernetes,作為領先的容器編排平台,提供了強大的功能來管理和部署應用程式。然而,隨著應用程式和用戶的增加,有效管理誰可以對 Kubernetes 集群執行何種操作變得至關重要。
👨‍💻簡介 terraform在每次執行terraform plan或terraform apply時,是如何知道應該要管理哪些資源? 其實就是透過在每次執行terraform時,將建立或要變更的資源都記錄在terraform.state這份狀態檔,預設檔案使用JSON格式。
📔心得 最近,我在探索 Ansible 自動化工具的過程中,決定運用它來建立 ELK Stack,這是我之前使用 Docker 建立的經驗的延伸。在這個過程中,我想分享一下我的學習心得。
在 Kubernetes 裡,Secret 就像是一個保險箱,可以放你任何不想公開的東西。比如說密碼、API 金鑰、憑證等,這樣的資料可能會被放在 Pod 裡,但你可以用 Secret 來避免直接在應用程式的程式碼中暴露這些機密資料。
👨‍💻簡介 最近因為憑證越來越多,需要監控什麼時候到期,當到期時發送到期通知,因此撰寫一個簡單的小程式來完成。 這次使用Python和Telegram Bot來監控SSL證書的到期時間並發送通知。並使用GCP工具,如CloudFunction和CloudScheduler做部署平台。
引言 在當今的技術世界中,Kubernetes 已成為容器化應用的領導平台。作為一個高效的容器編排系統,它不僅管理著容器的部署和擴展,還提供了必要的自動化支持,以保證應用的高可用性和性能。在這個框架中,自動擴展功能起著至關重要的作用,特別是在面對不斷變化的負載和需求時。
👨‍💻簡介 在當今的雲計算時代,容器化和微服務架構成為了重要趨勢。Kubernetes,作為領先的容器編排平台,提供了強大的功能來管理和部署應用程式。然而,隨著應用程式和用戶的增加,有效管理誰可以對 Kubernetes 集群執行何種操作變得至關重要。
👨‍💻簡介 terraform在每次執行terraform plan或terraform apply時,是如何知道應該要管理哪些資源? 其實就是透過在每次執行terraform時,將建立或要變更的資源都記錄在terraform.state這份狀態檔,預設檔案使用JSON格式。
📔心得 最近,我在探索 Ansible 自動化工具的過程中,決定運用它來建立 ELK Stack,這是我之前使用 Docker 建立的經驗的延伸。在這個過程中,我想分享一下我的學習心得。
在 Kubernetes 裡,Secret 就像是一個保險箱,可以放你任何不想公開的東西。比如說密碼、API 金鑰、憑證等,這樣的資料可能會被放在 Pod 裡,但你可以用 Secret 來避免直接在應用程式的程式碼中暴露這些機密資料。
你可能也想看
Google News 追蹤
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
https://x.com/i/web/status/1780213284769337813
Thumbnail
本篇文章將教你如何在Kubernetes cluster內部署一個MongoDB,包括取得Manifests、建立Volume、部署實務、基本操作和結論。透過操作演示,讓你瞭解在實務上如何成功建立MongoDB,並進行基本操作。
Thumbnail
前言 大家好上次我們教了如何建立自己的 Docker Image,今天我們要來教怎麼把自己做好的 Docker Image 上傳到 Docker Hub,如果以後自己在公司有搭建 Docker Registry,也可以用同樣方式上傳到公司的 Docker Registry 私有倉庫 登入 Doc
Thumbnail
詳細解說如何在 Mac 上透過 Docker 安裝 MongoDB 社群版。包括 MongoDB Compass 的安裝與配置,以及 MongoDB Shell 的使用方法,為開發者提供 MongoDB 學習資源。
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
https://x.com/i/web/status/1780213284769337813
Thumbnail
本篇文章將教你如何在Kubernetes cluster內部署一個MongoDB,包括取得Manifests、建立Volume、部署實務、基本操作和結論。透過操作演示,讓你瞭解在實務上如何成功建立MongoDB,並進行基本操作。
Thumbnail
前言 大家好上次我們教了如何建立自己的 Docker Image,今天我們要來教怎麼把自己做好的 Docker Image 上傳到 Docker Hub,如果以後自己在公司有搭建 Docker Registry,也可以用同樣方式上傳到公司的 Docker Registry 私有倉庫 登入 Doc
Thumbnail
詳細解說如何在 Mac 上透過 Docker 安裝 MongoDB 社群版。包括 MongoDB Compass 的安裝與配置,以及 MongoDB Shell 的使用方法,為開發者提供 MongoDB 學習資源。