u-ryo's blog

various information for coding...

Markdown to Pdf on CLI

| Comments

markdownで快調に書いたものを、「印刷して提出せよ」ということになっていたので、「へ?」とか思って。今時ねぇ、印刷なんてねぇ、何の意味があるのか。一応evidenceってことなんだろうけど。

調べると、意外とないんですよね日本語markdown to pdf。popularなのはMS Visual Studio Codeの拡張機能Markdown PDF使うものですけど、たったそれだけのためにVisual Studio Code入れたかぁないですし。 nodeのmarkdown-pdfmd-to-pdfとかサクッと出来そうだったんですけど、古いのか、どうしようもないエラーが出ます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
docker run --rm -it -v $PWD:/work -w /work -e https_proxy=http://192.168.120.1:3128 node:alpine sh
# npm i -g markdown-pdf
# markdown-pdf some.md
node:events:497
      throw er; // Unhandled 'error' event
      ^

Error: spawn /usr/local/lib/node_modules/markdown-pdf/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs ENOENT
  :
# npm i -g md-to-pdf
# md-to-pdf some.md
(node:29) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)

  Puppeteer old Headless deprecation warning:
    :

  ✖ generating PDF from some.md
    → TROUBLESHOOTING: https://pptr.dev/troubleshooting
ListrError: Something went wrong
    at /usr/local/lib/node_modules/md-to-pdf/node_modules/listr/index.js:102:18
     :

pandocもいいんですが、texの環境色々入れるのがかったるいなー。 というわけで、docker baseで。 探すと、参考文献がありました。proxyも使わないとならないので。

1
2
3
4
5
6
7
8
9
10
$ PROXY=http://192.168.120.1:3128
$ docker run --rm -it -e https_proxy=$PROXY -e http_proxy=$PROXY -e ftp_proxy=$PROXY -e use_proxy=yes -v $PWD:/data --entrypoint sh pandoc/latex
/data # tlmgr install collection-langjapanese
   :
tlmgr: package repository ftp://tug.org/historic/systems/texlive/2022/tlnet-final (not verified: valid signature with expired key)
   :
[108/108, 22:16/22:16] install: collection-langjapanese [1k]
running mktexlsr ...
   :
/data # pandoc some.md -o some.pdf -C --pdf-engine=lualatex -V linkcolor=blue -V documentclass=ltjsarticle -V luatexjapresetoptions=fonts-noto-cjk
  • 何か今どきftpで取りに行くので、ftp_proxyが必要
  • use_proxy=yesもないとproxy経由と認識してくれなかった
  • working directoryはdefaultで/dataなのでそこにmountする
  • entrypointが入っているので、optionで上書きしないとtlmgrを動かせない
  • pandoc/coreではなくpandoc/latexであることに留意
    • 500MB超とsizeも結構ある
  • tlmgr install collection-langjapaneseは22分かかった
    • 総計30分弱はかかる
  • 基本的にはpandoc md_file -o pdf_fileだが、--pdf-enginelualatexを指定したりdocumentclassltjsarticleを指定したり、日本語特有の指定が必要
  • ###が本文と同じ大きさの文字になるので、思っているよりlevelを1段階上げとかないと、思ったような出力にならない

Get Refresh Token for Google API

| Comments

何かいつの間にかGoogle APIの認証方法が変わってて、security上の理由からというので仕方ないんでしょうけど。

AUTHORIZATION_CODEを得ようと、以前のように↓のURLにaccessすると、 https://accounts.google.com/o/oauth2/v2/auth?responsetype=code&clientid=...apps.googleusercontent.com&redirecturi=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/photoslibrary&accesstype=offline アクセスをブロック: ... のリクエストは無効です

と出て、あれ?!と。

エラー 400: invalid_request The out-of-band (OOB) flow has been blocked in order to keep users secure. Follow the Out-of-Band (OOB) flow migration guide linked in the developer docs below to migrate your app to an alternative method.

で、帯域外(OOB)フロー移行ガイドへ誘導され。

このoobフローはなくなったと!? んじゃどうせぃっていうんじゃねん。

「デスクトップクライアント」に相当するから、ループバック IP アドレス(localhost または 127.0.0.1)フローに飛ばされたものの、こっちもなくなってて、ループバック IP アドレスフローの移行ガイドに飛ばされて、結局library使う方法しか書いてなくて。こちとら、shell scriptで使いたいんですけど。refresh_token欲しいだけなのに、なんでこんなに苦労せなかんの??🤔

...って嘆いても仕方ないので、library使ってrefresh_token取得だけします。

ちょっとめんどくさいんですけど、local環境を汚染しない形で。

参考:

Google APIの認証情報のpageから「OAuthクライアントをダウンロード」してclient_secret.jsonとして保存。

1
2
3
4
5
6
7
$ docker run --rm -it -v $PWD/client_secret_....json:/root/client_secret.json python:alpine sh
# apk add w3m screen
# pip install google-api-python-client google-auth-oauthlib
# screen
# python
>>> from google_auth_oauthlib.flow import InstalledAppFlow
>>> credential=InstalledAppFlow.from_client_secrets_file('client_secret.json',["https://www.googleapis.com/auth/photoslibrary.appendonly"]).run_local_server()

ここでw3mが開くので、Qを押して閉じるとURLが表示され、そこをChromeなりで開いて、突き進んで承認します。

そうすると、http://localhost:8080/?state=.... へ回されて止まるので、C-aで別screenを開いて、そこでw3m 'http://localhost:8080/?state=...'とすると、The authentication flow has completed. You may close this window.と言われます。徐に元のscreenに戻るとcredential objectができているので、credential.refresh_tokenとすると、漸くREFRESH_TOKENを得られます。1時間有効なACCESS_TOKENcredentail.tokenで得られます。

これらを使うと、やっと以前のようにshell scriptで回せるようになります。ふぅ。

NHK Taiga Staff Reflection

| Comments

大河ドラマ『鎌倉殿の13人』13日放送分に一眼カメラを構えたマスクおじさんスタッフ映り込み放送事故で公式が謝罪、視聴者「タイムスクープハンターの撮影班だ」#鎌倉殿の13人 たまたまぼくも本放送、再放送どちらも録画してたので、よーく見てみました。

kamakuradonono13nin

12fps で 3コマ約0.2秒、確かによく見付けたなぁと思います。 再放送では編集されるとの由、どうなるんだろうと注視していましたら、 北条義時の走るシーンが例の場面直前まででカットされ、 その分佐竹側の武者達が降りてくる次のシーンが若干長くなっている、 というものでした。 両者足したものを作ってみたら確かに45分を0.2秒程越えてました。

Gradlew Stopped

| Comments

実に久し振りにこのblogを書こうとして、

1
./grainw create-post 'some title'

としたら、gradle-1.8.jarを取りに行って404 not found食らって動きません。 このgrainって古いしもう更新止まってるし、先見の明なかったなぁということなんですが、もうちょっと本気で原因究明です。 https://services.gradle.org/distributions/ はあるのになぜ404? と思ったら、http://...で取りに行ってました。gradlewのgradleが古すぎる? grainwのどこで止まってるか、debug print入れて割り出すと、./gradlew gendepsでした。これをversion 6.8.1のgradleで手動でやってみる(gradle gendeps)と、build.gradleline: 78で止まります。なんで?と思って調べると、「Cannot add task 'wrapper' as a task with that name already exists.」が出たにあった通り、でした。

1
task wrapper(type: Wrapper) {

1
wrapper {

にすれば動きました。良かった。 gradle wrapper./gradlewを更新して、./grainwも復活しました。

Ansible for Docker

| Comments

ansibleのlocalでの開発環境といえばvagrant+virtualboxですが、dockerでいいんじゃねぇの?と思って色々試行錯誤してみました。何度も挫けて、もう駄目かと思いましたけど、幾つもの山を越え、ついに出来ましたですよ!

docker.ini
1
2
3
4
5
[docker_host]
localhost

[container]
eiger-local
playbooks/docker.yml
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
41
42
43
44
45
46
47
- hosts: localhost
  connection: local
  gather_facts: no
  vars:
    ansible_python_interpreter: /Users/ryo-umetsu/.anyenv/envs/pyenv/shims/python
    # ansible_python_interpreter: /usr/bin/python3
  tasks:
    - name: deploy amazonlinux docker
      docker_container:
        image: amazonlinux
        name: eiger-local
        volumes:
          - /sys/fs/cgroup:/sys/fs/cgroup:ro
        ports: 57017:27017
        expose: 57017
        tty: yes
        detach: yes
        privileged: yes
        # capabilities:
        #   - SYS_ADMIN
        # sysctl:
        #   vm.swappiness: 1
        command: sh -c 'yum -y install procps systemd-sysv file && exec /sbin/init && systemctl restart'
    - name: adding host
      add_host: name="eiger-local"
    - name: wait
      wait_for:
        delay: 10
        timeout: 10

- hosts: eiger-local
  connection: docker
  vars:
    setup_mongodb_password: !vault |
      $ANSIBLE_VAULT;1.1;AES256
          39356533306331333763353433363335343961653335373762623630653361623034646232633837
          6164306666656436653636643333363261646662356666640a636462346635323164633365376637
          39303832396638336664653439326536386235323262303733653563653766353263623032386538
          3135623166613363630a343734666666323830663838653962663032386337626635396563383230
          6266
    # setup_mongodb_password: "test"
    ansible_python_interpreter: /usr/bin/python
  roles:
    - role: "../roles/create_data_directory"
    - role: "../roles/setup_mongodb"
    - role: "../roles/configure_logrotate"
    - role: "../roles/configure_swap"

凄いknow-how詰まってます。

  • connection: localとするとssh経由じゃなく直接command叩いてくれる
  • connection: dockerとするとdocker execで叩いてくれる(のでtargetにauthorized_keysとかsshdとか不要)
  • localhostではgather_facts: noとする(これをしないとansible_swaptotal_mbとかansible_memtotal_mbといったシステムから検出される変数: Fact (ファクト)が埋められない)
  • ansible_python_interpreter: /usr/bin/python3python binaryを探す順序があって、/usr/bin/pythonを先に見つけてしまい、version 2を使ってしまうのを防ぐため。Macだと/usr/bin/pythonがpython2で、python3は/usr/bin/python3なので。ここのversionが合わないと、The error was: No module named 'requests'"と言われて何が悪いのかさっぱりだった。基本的にはpip3 install dockerが足りなかったのだが、その他に、このようにansibleが自分で見つけて使っているpythonのversionと自分がcommand lineで使っていてpip installしたpythonのversionがずれていると、このように言われる、ことがわかった。その他にもinventoryで指定することもcommand line optionで指定することも出来る。command line optionで-e ansible_python_interpreter=/usr/bin/python3とするのが最も柔軟だが、これはplaybooks中の値を全て上書きするので、やはり注意が必要だった。pythonってversionややこしくて...
  • setup_mongodb_password: !vault |Single Encrypted Variableecho -n test|ansible-vault encrypt_string --vault-password-file vault_password.txt --encrypt-vault-id default --stdin setup_mongodb_passwordのように使う(ansible-vaultで暗号化しよう。)
  • docker_container:はmodule名で、古い文献だとdocker:になっていて今は効かないので注意。中で指定できるものはcommunity.docker.docker_container – manage docker containersに挙がっている。privileged: yesは、container内でsysctl vm.swappiness=1(defaultは60)をされるので。sysctl:というoptionもあるのだけれど、そこで指定できる(whitelistにある)のはnet.*とかkernel.*の一部だけだそう(サポート中の sysctl)。
  • command: sh -c 'yum -y install procps systemd-sysv file && exec /sbin/init'は、playbook中でsystemctlを設定するものがあるから。Restart=alwaysにしたいんですよね要するに。でも、そうするとdockerにsystemd(initd)が動いていねばならず、そういえばdockerって普通はそんなのなかったな、どうやるんだ? 調べると、
  • systemd-sysvをinstallする必要
  • /sys/fs/cgroup:/sys/fs/cgroup:roのdevice mountが必要
    • macにはそんなdirectoryはないのに大丈夫なのが不思議
  • 最後は/sbin/init
  • ↑これではcontainerが数秒で倒れるので更に調べるとexec /sbin/init(これどこで見たんだっけなぁ感動したなぁStackOverflowだった気が。他では全然書いて無くて、よく見つけたもんだ)
  • wait_for:がないと、docker内にまだyum installとか終わりきってないのにどんどん次に進んでしまい、service.name:mongod,service.enabled:trueの時に「mongodというserviceはない」といって落ちてしまうので。ホントはちゃんと何かを待ちたかったんだけど、何を待ったらいいのか不明でした。processかなと思ったんですが、processのwaitってpid fileの存在有無を確認するみたいな。いゃー、systemdというかinitってpid fileなんてないんですけど。network port待つ例は数多あれど。仕方ないのでただ10秒待つように。timeout:10は不要かと思いきや、ないとずっと待ってました。
  • これをansible-playbook -vvv -i docker.ini playbooks/docker.ymlと実行するんですが、dockerでやる場合はansible/ansible-runner使って、docker run --rm --name=ansible -v $PWD:/work -w /work -v /var/run/docker.sock:/var/run/docker.sock ansible/ansible-runner sh -c 'pip3 install docker&yum install -y yum-utils;yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo;yum install -y docker-ce docker-ce-cli containerd.io;ansible-playbook -i docker.ini playbooks/docker.yml'と、CentOS8 baseのansible-runnerにdockerを入れねばならず、またunix socketを共有しないとなりませんでした。CentOS8にyum install dockerするとpodmanが入りやがるんですよね。podmanってMacじゃ動かねぇっつーの! podmanだとhosts:eiger-localgather_factsでDocker version checkに失敗して落ちるんですよね。Podman broken on Silverblue? Podman complains kernel not supporting overlay fs (backing file system is unsupported for this graph driver)見てsed -i "s/#mount_program/mount_program/" /etc/containers/storage.conf加えても、Error: mount /var/lib/containers/storage/overlay:/var/lib/containers/storage/overlay, flags: 0x1000: operation not permitted。うーん。逆に、docker containerにansibleを入れる方がsimpleでした。即ち、docker run --rm --name=ansible -v $PWD:/work -w /work -v /var/run/docker.sock:/var/run/docker.sock docker sh -c 'apk add ansible py3-pip;pip3 install docker;ansible-playbook -i docker.ini playbooks/docker.yml'ですね。

参考サイト