2022年6月30日木曜日
2022年6月28日火曜日
MinIO の gateway s3 を docker-compose で動かす
概要
この記事の docker-compose 版を紹介します
環境
- macOS 11.6.7
- docker 20.10.12
- minio RELEASE.2022-06-25T15-50-16Z.fips
docker-compose.yml
version: '3'
services:
minio:
image: quay.io/minio/minio:latest
ports:
- "9100:9100"
- "9000:9000"
container_name: minio
command:
'gateway s3 https://jp-east-1.storage.api.nifcloud.com:443 --console-address ":9100"'
environment:
MINIO_ROOT_USER: 'AKIxxx'
MINIO_ROOT_PASSWORD: 'xxx'
2022年6月27日月曜日
Flask-SQLAlchemy で外部キー制約のある親レコードが削除されたときに参照元の子レコードも削除する方法
概要
cascade delete を使います
環境
- macOS 11.6.7
- Python 3.10.2
- Flask-SQLAlchemy 2.5.1
サンプルコード
user = db.relationship("User", backref="address", cascade="delete")
user 側 (親) のレコードを削除した場合に address 側 (子) のレコードも削除してくれます
参考サイト
2022年6月24日金曜日
Flask-SQLAlchemy でテーブルを結合して検索する方法
概要
テーブル間は外部キー制約が必要です
環境
- macOS 11.6.7
- Python 3.10.2
- Flask-SQLAlchemy 2.5.1
サンプルコード
def join_test(self,
name: str,
city: str):
return User.query.join(Address).filter(Address.city == city,
User.name == name).one()
join 構文を使うことで外部キー制約のあるテーブル同士を結合して検索することができます
filter 内ではどちらのテーブルに対しても条件を記載することができるようになります
最後に
クエリが複雑になるとスロークエリの原因にもなるので複雑な join は避けるようにしましょう
参考サイト
2022年6月23日木曜日
間違って実行したコマンドを訂正して実行してくれる thefuck を試す
概要
タイポなどでコマンドミスしたり権限がなくてコマンドエラーになった場合に再度コマンドを実行せず fuck と実行すると正しいコマンドを実行してくれるツールです
今回は実際にどんな感じで動作するのか試してみました
環境
- macOS 11.6.7
- Python 3.10.2
- thefuck
インストール
- brew install thefuck
-
eval $(thefuck --alias)
とりあえず試す
puthon とタイポしたあとに fuck するとコマンドを正しく作成し直してから実行してくれます
% puthon
zsh: command not found: puthon
% fuck
python [enter/↑/↓/ctrl+c]
Python 3.9.13 (main, May 24 2022, 21:28:44)
[Clang 13.0.0 (clang-1300.0.29.30)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
どうやって動作しているのか
ルールを記載することでルールに習って新しいコマンドを生成してくれます
なので対応していないコマンドや独自のコマンドがある場合は自分で追加することができます
独自のルールを作成してみる
~/.config/thefuck/rules
配下にルールファイルを作成することで独自のルールを作成できます
今回は hoge という存在しないコマンドをタイプしたあとに fuck すると ls コマンドの結果を返すルールを作成してみます
-
mkdir -p ~/.config/thefuck/rules
-
vim ~/.config/thefuck/rules/hoge_ls.py
import re
from thefuck.shells import shell
# enabled_by_default = True
# requires_output = True
# priority = 1
regex = re.compile(r'fuga')
def match(command):
return bool(regex.findall(command.script))
def get_new_command(command):
return "ls"
独自ルールを作成するときのポイント
match でなかなか目的の結果を補足できないケースが多かったので一旦 True を必ず返却するようにして print デバッグなど使ってみましょう
もし他のルールで補足されると独自ルールに回ってこないので priority を小さくすることでルールの評価を優先できるので優先度を上げてみましょう
参考サイト
最後に
パッケージの名前があれですが便利に使えるツールかなと思います
リバースサーチに慣れている場合は fuck に切り替えるのが大変かもしれません
あとはプロダクション環境などでは逆に誤って実行してしまうケースもあるので使わないほうがいいかもしれません
2022年6月22日水曜日
Gmail で独自ドメインを使ってメールを送受信する方法
概要
Gmail で独自ドメインを使ってメールを送受信する方法を紹介します
基本的には自分のドメインの DNS サーバの MX レコードを登録するだけです
事前に GoogleWorkspace の申し込みと管理者ユーザの作成が必要になります (参考)
環境
- macOS 11.6.7
- Google Workspace
コンソールにログイン
まずは GoogleWorkspace のコンソールにログインしましょう
MXレコードを設定する
まだの場合はクイックスタート用のリンクがあるのでここから設定するのが簡単です
MX レコードを DNS に設定する前に注意文が表示されます
いろいろ書いてありますが既存の MX レコードを書き換える場合にはタイミングによってメールが送受信できないので注意してくださいということが書いてあります
新規で MX レコードを登録する場合は問題ありません
DNSの管理画面の一般的な操作方法が記載されています
DNSレコードの設定はお使いのDNSサービスによって異なるので各自で設定画面に移動してください
「次へ: 手順2に移動」を選択します
下にスクロールするとレコード情報があるのでここに記載されている5つのMXレコードを登録します
自分はお名前comのDNSサーバを使っているので以下のような感じで登録しました
設定を反映して以下のような完了画面になれば OK です
GoogleWorkspace が MX レコードを引けるようになれば OK です
うまく設定が完了しない場合は dig などでレコードが引けるようになっているか確認しましょう
-
dig your-domain.com mx
動作確認
GoogleWorkspace に登録したユーザに適当にメールを送信してみましょう
ちゃんと Gmail でメールが確認できれば OK です
注意事項
今回は新規で MX レコードを登録しましたがすでに使用しているメールサーバなどがあり MX レコードがある場合は切り替え時の注意が必要です
具体的には参考サイトにある Google の公式サイトを見てほしいのですが切り替えのタイミングによってはメールが届かないことがあります
参考サイト
2022年6月21日火曜日
個人で Google Workspace に申し込んでみた
概要
申し込んでみました
1アカウントのみで使用するのでドメイン保護まで行っています
環境
- macOS 11.6.7
- Chrome 102.0.5005.115
申し込みの流れ
スクリーンショットベースで簡単な流れだけ紹介します
ビジネス名は何でもOKです
今回は個人利用なので個人向けを選択します
Google アカウントでログインしているはメールアドレスは補完されます
今回はドメインを保有しているので「使用できるドメインがある」を選択しました
保有しているドメインを入力します
あとでこのドメインは Google Workspace に保護されます (他の人がこのドメインで申し込めなくなる)
管理者アカウントを設定します
Google Workspace 専用の管理者アカウントになります
Google アカウントとは違うので注意です
これで Google Workspace の基本的な申込みは完了です
ドメインの保護
テキストレコード認証で保護することができます
自分はお名前comで取得したドメインなのでお名前comのDNSにレコードを登録しました
ホスト名はアットマークでOKです
レコードが登録されて Google Workspace 側で引けるようになると保護が完了します
あとは別アカウントの作成やメールの転送設定があります
今回は個人アカウントなので別アカウントは登録しません
またメールサーバもないのでメール設定もパスします
最後に
個人利用で料金がいくらかわかったら追記したいと思います
2022年6月20日月曜日
Python で datetime からエポックタイムを取得する方法
概要
タイトルの通りです
複数あるので紹介します
環境
- macOS 11.6.7
- Python 3.10.2
strftime を使う場合
from datetime import datetime
datetime.now().strftime("%s")
この方法は unix 系のみ実行可能です
また返り値は文字列になります
timestamp を使う
from datetime import datetime
datetime.now().timestamp()
小数点以下まで取得できます
float として取得できます
total_seconds を使う
from datetime import datetime
(datetime.now() - datetime(1970,1,1)).total_seconds()
基準時間からの引き算でも算出できます
float としてミリ秒まで取得できます
timege を使う
from datetime import datetime
import calendar
d = datetime(2019, 12, 1, 0, 0)
calendar.timegm(d.timetuple())
int として取得できます
ミリ秒以下は取得できません
最後に
いろいろありますが timestamp を使うのが良さそうです
2022年6月17日金曜日
botocore でバケット配下のオブジェクトをすべて削除する方法
概要
botocore だけ使って削除する場合はループさせる方法しかなさそうです
boto3 が使える場合は bucket.object_versions.delete()
が使えるようです
環境
- macOS 11.6.6
- Python 3.10.2
- botocore 1.24.11
サンプルコード
from botocore import session
import sys
class TestClient():
def __init__(self):
self.client = session.get_session().create_client(
's3',
region_name='us-east-1',
endpoint_url='https://s3.compatible.api',
aws_access_key_id='xxx',
aws_secret_access_key='xxx',
)
def delete_object(self, bucket, key):
return self.client.delete_object(
Bucket=bucket,
Key=key,
)
def delete_all_objects(self, bucket):
objs = self.list_objects(bucket)
for obj in objs['Contents']:
response = self.delete_object(bucket, obj["Key"])
return response
def list_objects(self, bucket):
result = self.client.list_objects(
Bucket=bucket
)
return result
if __name__ == '__main__':
cli = TestClient()
print(cli.delete_all_objects(sys.argv[1]))
注意事項
list_objects が最大 1000 オブジェクトまでしか取得できないのでそれ以上の場合はページングなどの考慮が必要になります
参考サイト
2022年6月16日木曜日
docker swarm で crazymax swarm-cronjob を使って定期処理をしている場合にスケジュールを変更する場合は label の張替えをしなければならない
概要
過去に crazymax/swarm-cronjob を使って swarm 上で定期実行する方法を紹介しました
もしスケジュールを変更する場合は stack deploy し直せば良いのですが stack deploy ができなく service update などを使う場合はラベルの張替えが必要になるのでその手順を紹介します
環境
- Ubuntu 18.04
- docker 19.03.13
service update でラベルを張り替える
-
docker service update cronjob1 --force --label-rm "swarm.cronjob.schedule"
-
docker service update cronjob1 --force --label-add "swarm.cronjob.schedule=5 10 * * *"
削除 -> 追加の手順でやっていますが追加の手順だけでも上書きしてくれるので大丈夫だと思います
イメージの変更をしない場合は --force
オプションがないとターミナルが戻ってこないので付与します
crazymax/swarm-cronjob のコンテナは再起動しなくても良さそう
自分はしばらく待っていると勝手に再スケジュールしてくれました
もし即時反映させたい場合はコンテナを再起動すれば良さそうです
参考サイト
2022年6月15日水曜日
バケット配下に存在する最後に追加されたファイルを取得する方法
概要
botocore を使ってバケット配下にあるオブジェクトで最後に追加されたオブジェクトを取得する方法を紹介します
環境
- macOS 11.6.6
- Python 3.10.2
- botocore 1.24.11
サンプルコード
import boto3
import sys
class TestClient():
def __init__(self):
self.client = boto3.client(
's3',
region_name='us-east-1',
endpoint_url='https://s3.compatible.api',
aws_access_key_id='xxx',
aws_secret_access_key='xxx',
)
def get_latest_object(self, bucket):
result = self.client.list_objects(
Bucket=bucket
)
# sorted用のラムダの作成
get_last_modified = lambda obj: int(obj['LastModified'].strftime('%s'))
objs = result['Contents']
# リスト内包を使ってdictをソートする、ソートするキーはラムダを使用する
last_added = [obj for obj in sorted(objs, key=get_last_modified, reverse=True)][0]
return last_added
if __name__ == '__main__':
cli = TestClient()
print(cli.get_latest_object(sys.argv[1]))
Do not assign a lambda expression, use a def (E731) で怒られる場合はこちら
import boto3
import sys
class TestClient():
def __init__(self):
self.client = boto3.client(
's3',
region_name='us-east-1',
endpoint_url='https://s3.compatible.api',
aws_access_key_id='xxx',
aws_secret_access_key='xxx',
)
def get_latest_object(self, bucket):
result = self.client.list_objects(
Bucket=bucket
)
# sorted用の関数の作成
def get_last_modified(obj):
return int(obj['LastModified'].strftime('%s'))
objs = result['Contents']
# リスト内包を使ってdictをソートする、ソートするキーはラムダを使用する
last_added = [obj for obj in sorted(objs, key=get_last_modified, reverse=True)][0]
return last_added
if __name__ == '__main__':
cli = TestClient()
print(cli.get_latest_object(sys.argv[1]))
参考サイト
2022年6月14日火曜日
Python の datetime.now で秒以下を切り捨てる方法
概要
datetime.now() はミリ秒まで算出します
秒とミリ秒が不要な場合 0 で設定することができます
環境
- macOS 11.6.6
- Python 3.10.2
結論: replace(second=0, microsecond=0) を使う
Python 3.10.2 (main, Feb 4 2022, 17:19:26) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from datetime import datetime
>>> without_sec_now = datetime.now().replace(second=0, microsecond=0)
>>> print(without_sec_now)
2022-06-14 10:52:00
2022年6月13日月曜日
(Ruby) falcon を試す
概要
falcon は Ruby の Webサーバです
thin の代わりに使ってみました
アプリケーションフレームワークは sinatra を使います
環境
- macOS 11.6.6
- Ruby 3.1.1p18
- falcon 0.39.2
インストール
- bundle init
- vim Gemfile
gem "sinatra"
gem "falcon"
- bundle install
Getting Started
- vim app.rb
require 'sinatra'
class WebApp < Sinatra::Base
get '/' do
'ok'
end
end
-
bundle exec falcon --port 9292
-
curl -k https://localhost:9292
デフォルトは 9292 ポートです
https で起動するのでとりあえず警告を無視するため -k を付与しています
rackup 経由で起動する
-
vim config.ru
require './app'
run WebApp
-
bundle exec rackup config.ru
- curl localhost:9292
Rack 経由の場合は http で動作します
falcon のオプションはすべて使うことはできないので注意してください
公式だと rackup 経由の動作ではシングルスレッド+HTTP1 になってしまいパフォーマンスが低下するので直接 falcon を動かすのを推奨しています
https://socketry.github.io/falcon/guides/getting-started/index.html
最後に
thin -> falcon の乗り換え自体は Gemfile を書き換えるだけなので簡単にできます
しかし falcon は単体で動作することを想定しているためアプリケーションが Rack の機能を多く使っている場合にはその辺りの移植が必要になりそうです
2022年6月10日金曜日
Gitlab の issue のメッセージを slack に通知すると attachments になるので注意
環境
過去に Slack + Ruby でメッセージを補足する方法を紹介しました
Incomming を使って Gitlab からの issue のコメントメッセージを補足したい場合は attachments を使います
環境
- macOS 11.6.6
- Ruby 3.1.1p18
- slack-ruby-client 1.0.0
サンプルコード
slack.client.on :message do |data|
if data.has_key?('attachments') && data['attachments'].first['text']
puts "Message from gitlab"
end
end
slack.client.start!
2022年6月9日木曜日
celery の chain でタスクごとにキューをルーティングする方法
概要
過去にキューをルーティングする方法を紹介しました
今回は chain と組み合わせてルーティングする方法を紹介します
環境
- macOS 11.6.6
- Python 3.10.2
- celery 5.2.7
タスク
まずは普通にタスクを定義します
ここではキューは指定しません
-
vim tasks.py
from celery import Celery
app = Celery('sub_tasks', backend='redis://localhost', broker='redis://localhost')
@app.task
def add(x, y):
return x + y
@app.task
def multi(x, y):
return x * y
chain
次に chain を使ってタスク同士をつなげます
このときにタスクがエンキューされるキューを指定します
apply_async では指定せずにタスクに対して set をコールすることでタスクに紐付けるキューを指定できます
-
vim app.py
from celery import chain
from tasks import add, multi
tasks = chain(add.s(1, 2),
multi.s(10).set(queue='multi'),
multi.s(2)).apply_async()
print(tasks.get())
上記の場合は 1 つ目と 3 つ目のタスクはデフォルトのキューで 2 つ目のタスクは「multi」というキューを使います
ワーカーをそれぞれ起動
デフォルトのキューに入ったタスクを処理するワーカーと multi キューに入ったタスクを処理するワーカーを起動します
- pipenv run celery -A tasks worker -l info
- pipenv run celery -A tasks worker -Q multi -l info
動作確認
エンキューして動作確認します
-
pipenv run python app.py
これでキューを指定したそれぞれのワーカーでタスクが処理されていることが確認できると思います
apply_async 時に指定するキューの意味
デフォルトのキューになります
タスクそれぞれでキューを set していない場合に使用されるキューになります
2022年6月8日水曜日
botocore でバケット配下の容量を取得する方法
概要
前回は AWS CLI を使って取得する方法を紹介しました
今回は Python を使って取得する方法を紹介します
環境
- Ubuntu 18.04
- Python 3.10.2
- botocore 1.24.11
サンプルコード
import boto3
import sys
class TestClient():
def __init__(self):
self.client = boto3.client(
's3',
region_name='us-east-1',
endpoint_url='https://s3.compatible.api',
aws_access_key_id='xxxx',
aws_secret_access_key='xxx',
)
def list_objects(self, bucket):
result = self.client.list_objects(
Bucket=bucket
)
return sum([c['Size'] for c in result['Contents']])
if __name__ == '__main__':
cli = TestClient()
print(cli.list_objects(sys.argv[1]))
リスト内包記法を使っていますが Contents 内にある Size の値を取得して合算する感じになります
2022年6月7日火曜日
AWS CLI (s3api) でバケット配下の容量を取得する方法
概要
list-objects + query オプションを使って取得できます
環境
- Ubuntu 18.04
- aws-cli 2.4.23
バケット配下のオブジェクトのサイズの合計を取得
aws s3api list-objects \
--bucket bucket_name \
--query "[sum(Contents[].Size), length(Contents[])]" \
--region us-east-1 \
--endpoint-url https://s3.compatible.api
1つのオブジェクトのサイズを取得
上記に合わせて --prefix オプションを使います
aws s3api list-objects \
--prefix hogeobj \
--bucket bucket_name \
--query "[sum(Contents[].Size), length(Contents[])]" \
--region us-east-1 \
--endpoint-url https://s3.compatible.api
参考サイト
2022年6月4日土曜日
2022年6月3日金曜日
GrapesJS のデモをローカルで起動する方法
概要
デモ用のプラグインで grapesjs-preset-webpage というのがあるのでこれを使います
大抵のコンポーネントが含まれているのでこれを使えばデモと同じ UI が起動できます
docker はなぜか対応していないのでシングルページアプリケーションとしてローカルで起動します
環境
- Chrome 102.0.5005.61
- GrapesJS 0.18.4
- grapesjs-preset-webpage 0.1.11
サンプルコード
すべて CDN から取得できます
HTML ファイルを一つ作成すれば完了です
- vim index.html
<html>
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/grapesjs/0.18.4/css/grapes.min.css" integrity="sha512-3SojaDAbWw8RPDimU3ix1LUfttuUbIvBJeEaDzkJL7LUoffq/FkZsAuaYwFYG9OdKQRPexGhcbmS+eIjaFcbzg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/grapesjs-preset-webpage@0.1.11/dist/grapesjs-preset-webpage.min.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/grapesjs/0.18.4/grapes.min.js" integrity="sha512-pDjtzJzYuUo7iyB5QDzOE4d7sKzB1fhb+5iNkdhycJK81uoa8oQYK9c5BLQJzjFTFSWHgIrycEvGHMO2HT3KSw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.jsdelivr.net/npm/grapesjs-preset-webpage@0.1.11/dist/grapesjs-preset-webpage.min.js"></script>
</head>
<body>
<div id="gjs"></div>
<script type="text/javascript">
var editor = grapesjs.init({
container : '#gjs',
plugins: ['gjs-preset-webpage'],
pluginsOpts: {
'gjs-preset-webpage': {
}
}
});
</script>
</body>
動作確認
- open index.html
最後に
開発したければソースコードを持ってきて npm から起動すれば簡単にできそうです
2022年6月2日木曜日
Heroku CLI でセッショントークン切れが嫌な場合は Long-lived トークンを作成しよう
概要
heroku login などでログインした場合に取得できるアクセストークンには期限があり 1 ヶ月で期限切れになります
期限切れになった場合は再度 heroku login を実行すれば良いのですがスクリプトなどで heroku コマンドを使っている場合は面倒です (ログインにブラウザを立ち上げる必要があるので)
その場合には期限切れのないトークンを生成しそのトークンを使って CLI を叩けるように設定します
環境
- macOS 11.6.6
- Heroku CLI 7.60.2
Long Lived Token の作成
以下で生成できます
- heroku authorizations:create
Long-lived user authorization xxx-xxx-xxx-xxx-xxx global
こんな感じで生成できれば OK です
生成されたトークンの ID (上記の xxx-xxx-xxx-xxx-xxx の部分) はメモしておきます
Long Lived Token のアクセストークンを取得する
ID から CLI をコールするためのアクセストークンを取得します
- heroku authorizations:info xxx-xxx-xxx-xxx-xxx
で確認できます
Client: <none>
ID: xxx-xxx-xxx-xxx-xxx
Description: Long-lived user authorization
Scope: global
Token: yyy-yyy-yyy-yyy-yyy
Updated at: Thu Jun 02 2022 08:26:43 GMT+0900 (日本標準時) (about 1 hour ago)
上記の yyy-yyy-yyy-yyy-yyy の部分がアクセスに必要なアクセストークンになります
このアクセストークンを使って CLI をコールするようにします
.netrc の編集
heroku コマンドの認証情報は ~/.netrc
に格納されています
vim ~/.netrc
machine api.heroku.com
login name1@example.com
password yyy-yyy-yyy-yyy-yyy
machine git.heroku.com
login name1@example.com
password yyy-yyy-yyy-yyy-yyy
上記のパスワードの部分を先程メモしたアクセストークンに書き換えます
書き換えたら保存します
動作確認
本当に期限のないトークンでコールできるか確認しましょう
- heroku auto:token
で問題なくアクセスできることと期限切れの警告が表示されないことを確認しましょう
もしまだうまく設定できていない場合は以下のように表示されるはずです
› Warning: token will expire tomorrow at 9:13 AM
› Use heroku authorizations:create to generate a long-term token
zzz-zzz-zzz-zzz-zzz
注意事項
理由は不明なのですが Long Lived Token を設定後に再度 heroku login すると設定を上書きしてしまうようです
また Long Lived Token のクライアントトークン自体も削除してしまうのでできれば設定後は heroku login しないほうが良いかもしれません
おまけ: 発行している OAuth クライアントの一覧を確認する方法
- heroku authorizations
最後に
Heroku CLI で期限切れにならないアクセストークンを設定する方法を紹介しました
使用していないクライアントトークンは heroku authorizations:revoke
コマンドで削除しても良いかなと思います
また revoke は Heroku のダッシュボード上からもできるのでどちらでも大丈夫です