Blog | ytyng.comhttps://b.ytyng.com/blog/2024-03-19T05:46:01+00:00BlogKubernetes Ingress で任意のレスポンスヘッダーを返す2024-02-23T03:38:57+00:002024-03-19T03:00:01+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/kubernetes-ingress-add-response-header/Kubernetes の Nginx Ingress で任意のレスポンスヘッダーを返す方法。
複数の Ingress があってどれが反応しているか確認したい時に使う。
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ...
namespace: ...
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Ingress-Name: my-awesome-ingress";
more_set_headers "X-Other-header: other-header";
spec:
```Kubernetes Ingress でパスのリライトをする2024-01-01T04:50:47+00:002024-03-14T19:52:34+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/kubernetes-ingress-%E3%81%A7%E3%83%91%E3%82%B9%E3%81%AE%E3%83%AA%E3%83%A9%E3%82%A4%E3%83%88%E3%82%92%E3%81%99%E3%82%8B/`rules`.`http`.`paths`.`path` で正規表現でマッチさせたものが
`metadata`.`annotations`.`nginx.ingress.kubernetes.io/rewrite-target` に入ります。
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
namespace: my-namespace
annotations:
ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/rewrite-target: /api/apppath/$1
spec:
tls:
- secretName: my-http-cert
rules:
- host: log.ytyng.com
http:
paths:
- path: /(.*)
pathType: Prefix
backend:
service:
name: my-service-service
port:
number: 8000
```PyCharm (およびそのほかの JetBrains のエディター) で、フォルダツリー(Project View) でシングルクリックでファイルが開かれるようになったのをダブルクリックに戻す設定2023-12-31T06:02:48+00:002024-03-19T03:00:49+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/pycharm-jetbrains-editor-no-single-click-open-file-in-folder-tree-project-view/![画像](https://media.ytyng.com/20231231/4036b954380743eca1e3e810cbae55c6.png)
Preferences → Editor → General → Editor Tabs → Opening policy → Enable preview tabs
を無効にする
![画像](https://media.ytyng.com/20231231/ca5ca15ba6ce464eb84fe8bf113d1a21.png)
デフォルトで有効になるようになったっぽい。
[How to disable single-click "open file" from Project view – IDEs Support (IntelliJ Platform) | JetBrains](https://intellij-support.jetbrains.com/hc/en-us/community/posts/9020214641554-How-to-disable-single-click-open-file-from-Project-view)HW-394 と書いてある ESP-WROOM-32 (ESP32) 開発ボードで CircuitPython をする2023-12-24T09:20:13+00:002024-03-19T02:59:27+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/esp32-hw-394-wroom-32-circuit-python/AliExpress で、ESP32 で検索すると出てくる ESP-WROOM-32 搭載の開発ボードで、[基盤に「HW-394」と書いてあるもの](https://www.aliexpress.com/item/1005005495948290.html)を買いました。
4個入で16ドルでした。
![画像](https://media.ytyng.com/20231224/5e52364af9784cf392024a67df0e7ad3.jpg)
USBシリアルコンソールのチップで CH340C が実装されているものです。[HW-394 という印字が無く、パターンが同じタイプ](https://www.aliexpress.com/item/1005005335283537.html)もあるようです。
日本の Amazon でも、ESP-WROOM-32 + CH340C (もしくは別のシリアルコンソールチップ)の(謎メーカーの)同等商品が数多く売られていました。
HW-394 という印字が開発ボードの名前かどうかもわかりませんが、正式名称がわからないため、今後、この製品は HW-394 と呼びます。
後記:
後からわかったのですが、ESP32 DevKitC という規格でいろんなメーカーが作っているらしい
# 選定にあたっての注意
アリエク以外で売ってないですし、型番で検索しても公式ページなどは無いので怪しい商品です。
よく使う小さめのブレッドボードを使った時、基盤が大きすぎるため片側のピンしかアクセスできません。
そのためかなりおすすめしません。
![画像](https://media.ytyng.com/20231224/f98e176df26f4d9e8c76630f34a53cdc.jpg)
(写真では見えにくいですが、左側のピンしか使えません)
ESP32を使う場合、この商品は使わずに、CircuitPython の公式サイトで「ESP32」で検索すると出てくるボードを買うのが良いと思います。
https://circuitpython.org/downloads?q=ESP32
[Seeed Studio XIAO ESP32C3](https://circuitpython.org/board/seeed_xiao_esp32c3/) が、日本でも入手しやすく比較的安価で小さくて良いと思います。
# 開発方法
Mac (Apple Silicon M2) を使って開発します。
ESP32 は、USBシリアル接続を使わずに、ブラウザを使ってファイルを転送したりブラウザ内で開発したりできるようですが、今回はそれは行いません。Python ファイルの転送も USB シリアルポート経由で行います。
PyCharm でコードを書いて、シェルスクリプトでフラッシュの書き込みを行います。
# USB シリアルの接続ポートを確認する
HW-394 を Mac に USB-C-C のケーブルで接続します。
ドライバのインストール等は不要です。
接続後、以下のコマンドでポートを確認します。
```shell
python -m serial.tools.list_ports
```
結果
```
/dev/cu.Bluetooth-Incoming-Port
/dev/cu.usbserial-110
2 ports found
```
`Bluetooth-Incoming-Port` は除外し、検出された `/dev/cu.usbserial-110` が、今回作られた USB シリアルポートです。
ここで、シリアルポートが見えないようであれば、ケーブルを変えて、ハブを通さず直接接続したほうが良いです。途中で USB ハブをはさんでいるとうまく認識されないことがあります。
シェルスクリプト内でポートを扱う際、110 という数字は変動しそうなので、シェルスクリプトから取得する場合は私はこんな感じで探すようにしています。
```shell
port=$(ls -1 /dev/cu.usbserial* |head -n 1)
```
# CircuitPython のファームウェアを書き込む
RP2040(RaspberryPi Pico 等) のように、uf2 をドラッグアンドドロップして書き込むことはできません。ESP32はストレージデバイスとして認識されないためです。
ファームウェア書き込みのために、esptool というライブラリを使います。
## esptool のインストール方法
```shell
python3 -m pip install esptool
```
Pipenv を使う場合、以下のような Pipfile が良いと思います。
#### Pipfile
```
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
esptool = "*"
# 不具合を修正したフォークを使う
adafruit-ampy = {git = "git+ssh://git@github.com/ytyng/ampy.git"}
[requires]
python_version = "3.9"
```
```shell
pipenv install
```
adafruit-ampy については後ほど説明します。ファイルの転送に使います。
## CircuitPython のファームウェアの取得
HW-394 用の CircuitPython のファームウェアは用意されていません。そのため、構成の似ている
[DOIT ESP32 Development Board](https://circuitpython.org/board/doit_esp32_devkit_v1/) のファームウェアを使います。
本来の製品用のものではないため、自己責任での使用をお願いします。
[上記ページ](https://circuitpython.org/board/doit_esp32_devkit_v1/)から DOWNLOAD .BIN NOW を押して、ダウンロードします。
## ファームウェアの書き込み
esptool の `write_flash` を使って書き込みます。
```shell
#!/usr/bin/env zsh
port=$(ls -1 /dev/cu.usbserial* |head -n 1)
esptool.py --chip esp32 --port $port \
--before default_reset --after hard_reset --no-stub \
write_flash --flash_mode dio 0x0 \
adafruit-circuitpython-doit_esp32_devkit_v1-en_US-8.2.9.bin
```
# Python の REPL に接続する
シリアルポートに接続することで、ボード上の Python コンソール (REPL) が使えます。
screen コマンドで接続します。
```shell
#!/usr/bin/env zsh
port=$(ls -1 /dev/cu.usbserial* |head -n 1)
screen $port 115200
```
```
Adafruit CircuitPython 8.2.9 on 2023-12-06; ESP32 Devkit V1 with ESP32
>>> import board
>>> dir(board)
['__class__', '__name__', 'D1', 'D12', 'D13', 'D14', 'D15', 'D16', 'D17', 'D18', 'D19', 'D2', 'D21', 'D22', 'D23', 'D25', 'D26', 'D27', 'D3', 'D32', 'D33', 'D34', 'D35', 'D4', 'D5', 'I2C', 'LED', 'MISO', 'MOSI', 'RX', 'RX0', 'RX2', 'SCK', 'SCL', 'SDA', 'SPI', 'TX', 'TX0', 'TX2', 'UART', 'VN', 'VP', 'board_id']
```
# Mac で書いた Python コードをフラッシュに書き込む
シリアルポートからファイルを書き込むには、 [rshell](https://github.com/dhylands/rshell) ,[mpfshell](https://github.com/wendlers/mpfshell), [ampy](https://pypi.org/project/adafruit-ampy/), および [upydev](https://pypi.org/project/upydev/0.0.9/) といったツールが使えそうですが、どれも開発ボードが MicroPython でないと使えないようです。
例えば、MicroPython では ubinascii というモジュール名なのが、CircuitPython では binascii となっていたりと、モジュール名が違うものがいくつかあるため、MicroPython 用のライブラリはそのまま CircuitPython では使えない場合が多いです。多くは、MicroPython 用のものは先頭に「u」がつく場合が多いです。
この中では ampy が一番シンプルなツールで CircuitPython への対応が容易そうなのでフォークして修正して使いました。本家では既にプルリクエストが上がっていますが、メンテが放置されているらしくマージされていません。
[修正済みampy](https://github.com/ytyng/ampy)
## 修正済みの ampy をインストールする
上で書いた Pipfile でインストールするか、
```shell
python3 -m pip install git+https://github.com/ytyng/ampy.git
```
でインストールしてください。
## Python コードを書く
基盤に実装されている D2 LEDを点滅させるスクリプトを書きます。
### blink_led_d2.py
```
import digitalio
import time
import board
print('brink_led.py loaded.')
led_pin = digitalio.DigitalInOut(board.D2)
led_pin.direction = digitalio.Direction.OUTPUT
while True:
led_pin.value = True
time.sleep(0.1)
led_pin.value = False
time.sleep(1.9)
```
## フラッシュに書き込む
### deploy.sh
```shell
#!/usr/bin/env zsh
export AMPY_PORT=$(ls -1 /dev/cu.usbserial* |head -n 1)
cd $(dirname $0)
ampy put blink_led_d21.py /code.py
````
フラッシュの `/code.py` としてコピーしした後、EN と書いてある基板上のボタン(リセットボタン)を押すと実行され、Lチカします。
![画像](https://media.ytyng.com/20231224/40ae557718e944328b5c2e506f91b701.jpg)
ピン番号を変更することで、他のピンでもLチカできることを確認しました。
![画像](https://media.ytyng.com/20231224/fe1a7bd34cc040288a30179f41c266a8.jpg)mac で、コマンドラインからChrome をウインドウサイズを指定して特定のURLを開く方法2023-12-12T00:22:07+00:002024-03-19T03:00:19+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/mac-chrome-open-from-commandline-fixed-size/mac で、コマンドラインからウインドウサイズを指定して Chrome を開く方法です。
メモの Webアプリ等をランチャーから起動する場合に便利です。
```shell
open -na "Google Chrome" --args --new-window --app="data:text/html,<html><body><script>window.moveTo(0,0);window.resizeTo(500,800);window.location='https://www.example.com';</script></body></html>"
```
### 参考
[bash - Launch Google Chrome from the command line with specific window coordinates - Stack Overflow](https://stackoverflow.com/questions/13436855/launch-google-chrome-from-the-command-line-with-specific-window-coordinates)Raspberry PI Pico や RP2040 に Mac OS Sonoma でファイルをコピーすると Input/output error になる場合2023-11-21T02:51:56+00:002024-03-19T03:00:55+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/raspberry-pi-pico-rp2040-mac-os-sonoma-copy-input-output-error/# 問題
Mac OS Sononoma で、 Rasberry PI Pico や RP2040 を載せてるマイコンボードの開発をしようとして、
ファイルを RP2040 にコピーした時に、
```
cp: code.py: could not copy extended attributes to /Volumes/CIRCUITPY/code.py: Input/output error
```
となって 0バイトでコピーされる。(中身がコピーされていない)
また、Finder でドラッグアンドドロップした場合は
```
The Finder can’t complete the operation because some data in “” can’t be read or written.
(Error code -36)
```
のエラーダイアログが表示される。
![画像](https://media.ytyng.com/20231120/8d15e80161bc4c3d87b1c48be6f19188.png)
# 解決方法
ボリュームを synchronous でマウントしなおせば良い。
以下のスクリプトでマウントしなおしができる
```shell
#!/usr/bin/env zsh
m=$(mount | grep /Volumes/CIRCUITPY | grep synchronous)
if [ ! "$m" ]; then
devname=$(df | grep CIRCUITPY | cut -d" " -f1)
sudo umount /Volumes/CIRCUITPY
sudo mkdir /Volumes/CIRCUITPY
sleep 2
sudo mount -v -o sync -t msdos $devname /Volumes/CIRCUITPY
fi
```
# 参考情報
[OSError: [Errno 5] Input/output: macOS Sonoma is delaying writes on small filesystems · Issue #8449 · adafruit/circuitpython](https://github.com/adafruit/circuitpython/issues/8449)
# 原因
Sonoma では、ファイルのコピーをする際、まず仮想ファイルを作って、ファイルシステムへのページアウトがトリガーされる。
非同期でマウントされている場合、FAT とメタデータへの更新はフラッシュされるまで遅延されるため、0バイトファイルとなる。
8MB以下、FAT16、uf2、Python ファイルの書き込み の条件下で問題となる。
https://github.com/adafruit/circuitpython/issues/8449#issuecomment-1745372269
# 私の解決策
下記のようなデプロイスクリプトを作りました。
## remount.sh
```shell
#!/usr/bin/env zsh
ret=$(mount | grep /Volumes/CIRCUITPY | grep synchronous)
if [ ! "$ret" ]; then
devname=$(df | grep CIRCUITPY | cut -d" " -f1)
sudo umount /Volumes/CIRCUITPY
sudo mkdir /Volumes/CIRCUITPY
sleep 2
sudo mount -v -o sync -t msdos $devname /Volumes/CIRCUITPY
fi
```
## deploy.sh
```shell
#!/usr/bin/env zsh
cd $(dirname $0)
# Sonoma以降、ファイルのコピーで Input/output error になる問題の対応
./remount.sh
files=(
file_1.py
file_2.py
)
for file in $files; do
echo $file
cp $file /Volumes/CIRCUITPY/
done
# ゴミファイルを消す
dot_clean /Volumes/CIRCUITPY
ls -lhatr /Volumes/CIRCUITPY
```Python の MySQLdb を使う時に symbol not found in flat namespace '_mysql_affected_rows' が出た場合の対応2023-11-09T03:39:23+00:002024-03-19T03:00:51+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/python3-symbol-not-found-in-flat-namespace-_mysql_affected_rows/Apple silicon の Python で MySQL を操作しようとして
```
ImportError: dlopen(/.../site-packages/MySQLdb/_mysql.cpython-310-darwin.so, 0x0002): symbol not found in flat namespace '_mysql_affected_rows'
```
が出た。
[Macos M1 mysqlclient Symbol not found: _mysql_affected_rows ERROR · Issue #496 · PyMySQL/mysqlclient](https://github.com/PyMySQL/mysqlclient/issues/496#issuecomment-1614688099)
上記のコメントを参考にする。
私の環境では
```shell
% echo $MYSQLCLIENT_LDFLAGS
-L/opt/homebrew/opt/openssl/lib -L/opt/homebrew/opt/mysql/lib -L/opt/homebrew/opt/zlib/lib -L/opt/homebrew/lib
```
となっていたので、
```shell
% echo $MYSQLCLIENT_LDFLAGS
-L/opt/homebrew/opt/openssl/lib -L/opt/homebrew/opt/mysql/lib -L/opt/homebrew/opt/zlib/lib -L/opt/homebrew/lib -lmysqlclient -rpath /opt/homebrew/opt/mysql/lib
```
となるように修正した。
その後、
```shell
pip uninstall mysqlclient
pip cache purge
pip install mysqlclient
```
を行った。
```shell
python3
>>> import MySQLdb
>>>
```
でエラーが出なければOKDjango のログインユーザーを django shell を使って切り替える2023-10-12T12:41:35+00:002024-03-19T02:59:22+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/django-change-user-on-session/問い合わせ対応などで、他のユーザーとして強制的なログインを行う方法。
操作を間違えたり、他のユーザーとしてログインしたまま放置して操作を間違えると危険なので、使用には十分注意すること。本番環境で行ってはいけません。
この操作方法を行って発生した影響・損害・問題に対して著者は一切の責任を負いません。
1. まず、ブラウザで Django のアプリにログインする
2. クッキーの session_id を記録しておく
3. サーバで ./manage.py shell を実行する
```python
# 自分の session_id
session_id = 'abcd1234xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
# 代わりたいユーザーのID
user_id = 1
from django.conf import settings
from user.models import User
engine = __import__(settings.SESSION_ENGINE, {}, {}, [''])
session = engine.SessionStore(session_id)
dict(session) # 自分のユーザーIDか確認
user = User.objects.get(id=user_id)
user # 内容確認
session['_auth_user_id'] = user.id
session['_auth_user_hash'] = user.get_session_auth_hash()
session.save()
```
ブラウザをリロードすればユーザーが変わっている。
確認作業が終わったら、事故らないようクッキーの消去と正規ユーザーでの再ログインをしておく。Kubernetes で他の Pod と通信する2023-10-07T09:46:45+00:002024-03-19T03:00:16+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/kubernetes-name-resolv-dns-another-pod/同じ namespace 内であれば、 Service の `metadata.name` で名前解決ができる。
## Service
```yaml
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: airflow
spec:
ports:
- name: "6379"
port: 6379
targetPort: 6379
```
とすると、他の Pod から `redis` で名前解決できる。
```
redis://:@redis:6379/0
```
同様に
```yaml
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: airflow
spec:
ports:
- name: "5432"
port: 5432
targetPort: 5432
```
とすれば
```
db+postgresql://username:password@postgres/airflow
postgresql+psycopg2://username:password@postgres/airflow
```
とできるKubernetes Cronjob の登録で no matches for kind "CronJob" in version "batch/v1beta1" となった場合の対応2023-09-28T00:31:09+00:002024-03-19T05:04:07+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/kubernetes-cronjob-apply-no-matches-for-kind-cronjob-in-version-batchv1beta1/Kubernetes 1.25 で,
```yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: my-awesome-product-extreme-job
namespace: ytyng
spec:
...
```
このような CronJob のマニフェストを Apply したところ、下記のエラーが出た。
```
resource mapping not found for name: "my-awesome-product-extreme-job" namespace: "ytyng" from "sync.cronjob.yml": no matches for kind "CronJob" in version "batch/v1beta1"
ensure CRDs are installed first
```
Kubernetes 1.25 では、 batch/v1beta1 は使えないため、batch/v1 に修正すると登録できる。
```yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: my-awesome-product-extreme-job
namespace: ytyng
spec:
...
```Microk8s でSSL証明書の期限が切れた時の更新方法2023-08-21T01:08:59+00:002024-03-19T03:01:21+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/update-certificates-in-microk8s-kubernbetes/Micro k8s ( Kubernetes )を操作しようとして
```
Unable to connect to the server: tls: failed to verify certificate: x509: certificate has expired or is not yet valid: current time 2023-08-21T09:52:50+09:00 is after 2023-08-19T07:31:15Z
```
が出た時の対応方法です。
まず、Kubernetes ノードで `sudo microk8s.refresh-certs -c` をする
```
$ sudo microk8s.refresh-certs -c
The CA certificate will expire in 3273 days.
The server certificate will expire in -2 days.
The front proxy client certificate will expire in -2 days.
```
期限切れの証明書がわかるので、それぞれの証明書を `sudo microk8s.refresh-certs` で更新する。
```
$ sudo microk8s.refresh-certs --cert=front-proxy-client.crt
Taking a backup of the current certificates under /var/snap/microk8s/5625/certs-backup/
Creating new certificates
Signature ok
subject=CN = front-proxy-client
Getting CA Private Key
Restarting service kubelite.
```
```
$ sudo microk8s.refresh-certs --cert=server.crt
Taking a backup of the current certificates under /var/snap/microk8s/5625/certs-backup/
Creating new certificates
Signature ok
subject=C = GB, ST = Canonical, L = Canonical, O = Canonical, OU = Canonical, CN = 127.0.0.1
Getting CA Private Key
Restarting service kubelite.
Restarting service cluster-agent.
```
結果の確認
```
$ sudo microk8s.refresh-certs -c
The CA certificate will expire in 3273 days.
The server certificate will expire in 364 days.
The front proxy client certificate will expire in 364 days.
```
期限が364days なので、1年に1回実行する必要がありそうだ。Mac で Selenium + Chrome を使っていて、Chromeバージョンが 115 以上になって chromedriver がインストールできない場合2023-08-17T09:31:33+00:002024-03-19T02:59:19+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/chromedriver-over-115-mac-selenium-install/Mac で Selenium を使っていて、下記のようなエラーが出て Selenium 経由で Chrome が使えない場合の対応
```
This version of ChromeDriver only supports Chrome version 114
Current browser version is 116.0.5845.96 with binary path /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
```
または、最新の chromedriver をインストールしても下記エラーが出てしまう場合の対応。
```
selenium.common.exceptions.WebDriverException: Message: unknown error: cannot find Chrome binary
```
chromedriver のダウンロードページを見る
https://chromedriver.chromium.org/downloads
すると、
If you are using Chrome version 115 or newer, please consult the Chrome for Testing availability dashboard. This page provides convenient JSON endpoints for specific ChromeDriver version downloading.
と書いてある。
なので、json endpoint を見る。
https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json
リリース情報が json になっているので、本来はスクリプトで自動処理を作るのが良いが、今回は急ぎだったので手作業で対応する。
```bash
curl 'https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json' | jq | less
```
で、json の内容を見て、 116 を検索し、さらにその周辺の mac-arm64 を探すと、
```json
{
"platform": "mac-arm64",
"url": "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/116.0.5791.0/mac-arm64/chrome-mac-arm64.zip"
},
```
```json
{
"platform": "mac-arm64",
"url": "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/116.0.5791.0/mac-arm64/chromedriver-mac-
arm64.zip"
},
```
この2つの行が見つかる。
それぞれの URL から、chromedriver と 自動操作用の Chrome ( Google Chrome for Testing )をダウンロードする。
chromedriver はパスの通ったディレクトリに入れる。
既に過去に chromedriver を使ったことがある場合、 which chromedriver で場所を特定する。
私は homebrew で入れてたので /opt/homebrew/bin/chromedriver にあったので、上書きした。
Google Chrome for Testing は、Mac のアプリケーションディレクトリに入れる
それで Selenium の chromedriver が使えるようになる。
追記:
自動更新するスクリプトを作りました。
https://www.ytyng.com/blog/download-chromedriver-and-deploy-to-path/Djangoで、テンプレート内でモデルに対してのパーミッションがあるかを確認する2023-08-08T11:52:38+00:002024-03-19T03:00:46+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/permission-test-for-model-in-django-template/Django のテンプレート言語で、ログインユーザーがモデルに対してのパーミッションを持っているかを判定する例です。
perms というテンプレート変数がデフォルトで存在しており、それを使うことで判定できます。
```html
{% if perms.myapp.view_mymodel %}
<li class="nav-item">
<a class="nav-link" href="https://www.ytyng.com/blog/feeds/atom/%7B%%20url%20'admin:myapp_mymodel_changelist'%20%%7D">管理ページ</a>
</li>
{% endif %}
```シェルスクリプトで2ループ目以降 sleep する2023-07-20T11:48:32+00:002024-03-19T03:01:11+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/shell-script-sleep-in-loop-after-first/冗長化してあるものに対して1つずつ順番に処理をする際、一気にやってしまわないように 2回ループ名以降 sleep する場合に使う
```shell
#!/usr/bin/env zsh
sleep_command=""
for i in {1..3} ; do
eval ${sleep_command}
echo ${i}
sleep_command="sleep 10"
done
```iOS Safari でソフトウェアキーボードが出た時にブラウザの有効な天地サイズを取得し、レイアウトを整える2023-07-16T11:02:53+00:002024-03-19T05:46:01+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/ios-safari-software-keyboard-refresh-valid-window-height/固定フッターがある場合のページレイアウトにしていると、iOS でソフトウェアキーボードが表示された時にレイアウトが破綻する場合がある。
レイアウトの最大天地の高さを CSS 変数にして、iOS の場合 は更新し続けるようなコードにして解決した。
```css
:root {
--visual-viewport-height: 100%;
}
body {
height: var(--visual-viewport-height);
}
```
```javascript
const w = window as any;
const refreshSize = () => {
w.document.documentElement.style.setProperty('--visual-viewport-height', `${w.visualViewport.height}px`)
w.scrollTo(0, 0)
}
w.mobileUIRefreshInterval = setInterval(refreshSize, 300)
refreshSize()
// 条件を解除したい場合
...
clearInterval(w.mobileUIRefreshInterval)
```
# 補足事項
## iOS Safari で、100vh を正しく計算するための -webkit-fill-available というオプションがある
今回の用途には使えなかったが、別の用途でうまく使える場合もありそうなので書いておく。
```css
body {
height: 100vh;
}
@supports (-webkit-touch-callout: none) {
body {
/* The hack for Safari */
height: -webkit-fill-available;
}
}
```
参考: [iOSでも100vhをいい具合に調整して画面の高さいっぱいに要素を表示させる](https://zenn.dev/tak_dcxi/articles/2ac77656aa94c2cd40bf)
## ソフトウェアキーボードが表示されている時は、強制的にスクロールが有効になる
`body` に `overflow: fixed` をつけていても、iOS でソフトウェアキーボードが表示されると強制的にスクロールができるようになる。
この場合、固定フッターがあるレイアウトの Web アプリの場合、使用感が良くない。
いくつか対策はあるけど、SetInterval で `window.scrollTo(0, 0)`` を定期的に実行してしまうのが良いと思った。
実際に window のスクロールがされた時はちょっとちらついてしまう問題はあるけど、実用的には十分で安定している。Kubernetes (Microk8s) の中から名前解決ができなくなったので dns プラグインを入れ直した2023-07-03T02:04:10+00:002024-03-19T03:00:23+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/microk8s-re-install-dns-plugin/MicroK8s を使っていたら、Pod から外部の通信ができなくなっていた。
SSH で外部に通信するコード処理があったが、
```
ssh: Could not resolve hostname server.example.com: Try again
```
というエラーが出ていた。
他の Pod でも名前解決ができないか、遅い状態だった。
microk8s の DNS プラグインの入れ直し(DNSサーバの再設定)で直った。
```shell
$ microk8s disable dns
$ microk8s enable dns:1.1.1.1
```
補足: Kubernetes の DNS のデバッグ方法
[Debugging DNS Resolution | Kubernetes](https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution/)Docker イメージのタグをビルドのコミット数から設定する方法2023-06-16T04:09:27+00:002024-03-19T02:59:48+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/how-to-create-image-tag-number-from-commit-number/簡易的にバージョン番号をつけたい時に、コミット数からバージョン番号をつける
### docker/config.sh
```sh
#!/usr/bin/env zsh
image_name=ytyng/my-docker-image
image_tag=1.11.$(git rev-list --count HEAD)
```
### docker/build.sh
```sh
#!/usr/bin/env zsh
cd "$(dirname $0)" || exit
. ./config.sh
cd ..
docker build --platform linux/amd64 \
--ssh default -t ${image_name}:${image_tag} \
--build-arg IMAGE_TAG=${image_tag} \
-f docker/Dockerfile .
docker tag ${image_name}:${image_tag} ${image_name}:latest
```
### docker/Dockerfile
```dockerfile
FROM ...
...
ARG IMAGE_TAG='-'
RUN echo ${IMAGE_TAG} > /app/VERSION.txt
```Docker build で ホストのSSHキーを使う2023-05-23T09:55:58+00:002024-03-19T02:59:24+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/docker-builduse-host-ssh-key/# 今までやっていた方法
### Dockerfile
```dockerfile
# syntax=docker/dockerfile:1.0.0-experimental
FROM python:3.10-bullseye
RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
RUN --mount=type=ssh ssh git@github.com
```
### sh
```sh
DOCKER_BUILDKIT=1 docker build . --platform linux/amd64 --ssh default -t test-build -f Dockerfile
```
# これでよかった
### Dockerfile
```dockerfile
FROM python:3.10-bullseye
RUN --mount=type=ssh ssh -o StrictHostKeyChecking=no git@github.com
```
### sh
```sh
docker build . --platform linux/amd64 --ssh default -t test-build -f Dockerfile
```
ただし pip install などで gitプライベートリポジトリからインストールするならこれは入れとく
```dockerfile
RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
```MySQL で、AUTO_INCREMENT の値を設定して取得する2023-05-18T00:31:20+00:002024-03-19T01:46:18+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/mysql-auto-increment-refresh-information-schema-caching/MySQL で、テーブルの auto_increment を設定したり取得したりする時、
MySQL8以降は、information_schema がキャッシュされるので、連続で AUTO_INCREMENT を取得すると、更新されない値が取得できることがある。
その場合の対策方法
## 設定
```mysql
ALTER TABLE my_table_name AUTO_INCREMENT = 1
```
## 取得
```mysql
SELECT AUTO_INCREMENT
FROM information_schema.tables
WHERE TABLE_NAME = 'my_table_name'
```
ただし、キャッシュされている値が取得されることがある。
以下のどちらかの方法で対策できる。
### 対策A: セッション内で information_schema のキャッシュを無効化する
```mysql
SET information_schema_stats_expiry = 0
```
### 対策B: テーブルの information_schema のキャッシュを強制的に更新する
```mysql
ANALYZE TABLE my_table_name
```
# 参考ページ
- [[MySQL8.0] InnoDB でinformation_schema.TABLESのAUTO_INCREMENTが古いとき - Qiita](https://qiita.com/lofi/items/3a12f69081c73f81945a)
- [MySQL :: MySQL 8.0 リファレンスマニュアル :: 8.2.3 INFORMATION_SCHEMA クエリーの最適化](https://dev.mysql.com/doc/refman/8.0/ja/information-schema-optimization.html)BitLocker の復号化キーのファイルは utf-16のため grep -R で検索できないのでスクリプトで検索する2023-05-14T03:09:02+00:002024-03-19T02:59:41+00:00ytynghttps://b.ytyng.com/blog/author/ytyng/https://b.ytyng.com/blog/find-bitlocker-file-like-grep-r-in-python/Windows のストレージを BitLocker で暗号化する時、そのキーファイルをファイルとして保存できる。
ファイルが複数ある場合、内容の一致するファイルを検索したくて
```shell
grep -R 'AABBCC' .
```
とかしても、ファイルエンコーディングが UTF-16 なのでマッチしない。
マッチさせるテクニックは、下記 StackOverflow に書いてある
https://stackoverflow.com/questions/3752913/grepping-binary-files-and-utf16
これによると、おそらく
```shell
grep -Ra 'A.A.B.B.C.C.' .
```
とするとで実現可能そうだが、私は今回はこの方法を行わず Python スクリプトを書いて行った。
python では、 decode('utf16', errors='ignore') して str にすると内容を読める。
```python
import glob
import os
bitlocker_key_save_dir = '<my-mounted-bitlocker-key-save-dir>'
needle = 'AABBCC'
def main():
# bitlocker_key_save_dir 以下のファイルをサブディレクトリも含めてすべて取得
for file_path in glob.glob(
f'{bitlocker_key_save_dir}/**/*', recursive=True
):
# file_path がファイルでなければスキップ
if not os.path.isfile(file_path):
continue
print(f'\r{file_path}', end='', flush=True)
# 内容を print
content_bytes = open(file_path, 'rb').read()
content = content_bytes.decode('utf16', errors='ignore')
if needle in content:
print('\n')
print(content)
if __name__ == '__main__':
main()
```</my-mounted-bitlocker-key-save-dir>