Ansibleにおいて、リストの各要素をシングルクォートで囲んでからカンマで連結

はじめに

諸般の事情により、以下のようなリストについて、

terms:
  - AI
  - Artificial Intelligence

以下のように加工する必要に迫られた。

"'AI', 'Artificial Intelligence'"

つまり、それぞれの要素をシングルクォートで囲って、カンマで連結するとともに、全体をダブルクォートで囲むという加工を行う。 フィルタの使い方がややこしいのに加えて、ハマりどころもあるので、調査記録を残しておく。

最終的に出来上がったPlaybook

試行錯誤した結果、Playbookの内容は以下に落ち着いた。

- set_fact:
    single_quote: "'"

- shell: echo "{{ terms | map('regex_replace', '(^|$)', single_quote) | join(', ') }}"

モジュールdebugを利用して出力した結果の抜粋は以下。

ok: [192.168.8.8] => {
    "msg": "'AI', 'Artificial Intelligence'"
}

フィルタmapを利用することで、リストの各要素にフィルタregex_replaceを適用してシングルクォートで囲む加工を実現している。

また、置換後の文字列であるシングルクォートは、変数を利用して間接的に指定している。

ハマりどころ1 - シングルクォートを即値で表現できない

シングルクォートを即値で表現できないか色々試したが、どれもダメだった。 例えば、以下のようにシングルクォートを指定すると、文法エラーが発生する。

- shell: echo "{{ terms | map('regex_replace', '(^|$)', "'") | join(', ') }}"

全体をダブルクォートで囲まなければ文法エラーにはならないのだが、それでは望んだ結果は得られない。 バックスラッシュでエスケープできないか試したが、これもダメだった。 このもどかしさは、シェルスクリプトでありがちな、クォートやエスケープがネストすると訳が分からなくなるパターンと同じ。 一気に全部作ろうとせずに、タスクを分割して部分的に組み上げていくと、即値で書けるかもしれない。 しかし、中間的な作業変数は作りたくないので、シングルクォートの定数(みたいなもの)を準備することにした。

ハマりどころ2 - 標準のフィルタquote

標準でquoteというフィルタがあり、ぱっと見はこれで良さそうに思える。 フィルタquoteを利用したPlaybookは以下のようになる。

- shell: echo "{{ terms | map('quote') | join(', ') }}"

モジュールdebugを利用して出力した結果の抜粋は以下。

ok: [192.168.8.8] => {
    "msg": "AI, 'Artificial Intelligence'"
}

Artificial Intelligenceはシングルクォートで囲まれているのに、AIは囲まれていない。 調べてみると、Ansibleの問題報告に同じような事例Possible quote filter issue #28084を発見。 Ansibleの中の人のコメントは以下。

So the quote filter decides that a string with an asterisk needs to be quoted, but "/mnt" is fine without quotes. Which is correct for shell usage.

I don't think the quote-filter is what you need to use for your use-case as your intended output is not specific to the shell.

自分なりに意訳すると以下。

そのため、フィルタquoteは、アスタリスクを含む文字列についてはクォートが必要と判断しているが、"/mnt"についてはクォートがなくても問題は起きないと判断している。 フィルタquoteはシェルのために利用するので、これは正しい動作だ。

あなたのユースケースでは、出力結果はシェルでの利用を意図していないので、フィルタquoteは望んでいるものではないと思う。

ということで、フィルタquoteはシェルが解釈することを前提に、クォートするかどうかを独自に判断している。 そのため、フィルタquoteが利用できる状況はかなり限定される。 せめて強制的にクォートするようなフラグがあれば、使いようがあるのだが。

参考

Ubuntu 18.04でコマンドラインからUbuntu Dock (ランチャー)に登録しているアプリを変更

はじめに

やりたいことはタイトル通り。 Ubuntu 18.04上での開発環境を自動構築したかったのが動機。 デフォルトで登録されているRhythmboxやAmazonへのリンクは開発環境には不要だし、追加でGoogle ChromeVS Codeも登録しておきたい。

Dockに登録しているアプリを変更

Dockに登録しているアプリをコマンドラインから確認するには、以下のようにする。

$ gsettings get org.gnome.shell favorite-apps
['ubiquity.desktop', 'firefox.desktop', 'thunderbird.desktop', 'org.gnome.Nautilus.desktop', 'rhythmbox.desktop', 'libreoffice-writer.desktop', 'org.gnome.Software.desktop', 'yelp.desktop', 'ubuntu-amazon-default.desktop']

上記の結果は、Ubuntu 18.04をインストールした直後のデフォルトの状態である。

Dockに登録しているアプリをコマンドラインから変更するには、以下のようにする。

$ gsettings set org.gnome.shell favorite-apps "['google-chrome.desktop', 'firefox.desktop', 'code.desktop', 'org.gnome.Terminal.desktop', 'meld.desktop', 'org.gnome.Nautilus.desktop']"

その結果、画面的には以下のようになる。

f:id:redj:20180831001342p:plain

参考

プロキシの環境下でのUbuntu 18.04のインストールが異常に遅い場合の対処

プロキシを利用している環境でUbuntu 18.04をインストールしたところ、1時間ぐらいかかった。 インストール中のコンソールの出力を見ていると、時刻同期で度々ntp.ubuntu.comに接続しようとしてタイムアウトを起こしていた。 そこで、インストールメディアから「インストールせずにUbuntuを試してみる」でブートした後、以下のように時刻同期をオフにしてからインストーラ(ubiquity)を実行したところ、インストール速度が改善した。

$ sudo su -
$ systemctl stop systemd-timesyncd.service
$ http_proxy=http://proxy:port/ ubiquity

インストール後は、設定ファイル/etc/systemd/timesyncd.confにアクセス可能なNTPサーバを設定しておくか、以下のように時刻同期を止めておいた方がいい。

$ sudo systemctl stop systemd-timesyncd.service
$ sudo systemctl disable systemd-timesyncd.service

参考

~/.ssh以下のパーミッションの一括設定

CentOS 7だと~/.ssh以下のパーミッションを適切に設定しておかないとsshでログインできなかった。 Ubuntu 18.04はそんなことはなかったので、Ubuntusshdの設定が少しゆるい? そこで、以下のように設定する。

  • ディレクトリはオーナーのみ読み書き閲覧可能
  • ファイルはオーナーのみ読み書き可能
  • 鍵はオーナーのみ読み込み可能

これがベストなのかは正直不明。 パーミッションの一括設定のコマンドをメモっておく。

$ find ~/.ssh -type d | xargs chmod 0700
$ find ~/.ssh -type f | xargs chmod 0600
$ find ~/.ssh -type f | grep -E '/id_rsa(\.pub)?$' | xargs chmod 0400

上記コマンド実行後のパーミッションの例は以下。

$ ls -la ~/.ssh
合計 32
drwx------  3 redj redj 4096  7月 10 05:32 .
drwxr-xr-x 26 redj redj 4096  7月 10 05:32 ..
-rw-------  1 redj redj  389  7月 10 05:21 authorized_keys
-rw-------  1 redj redj  279  7月 10 04:58 config
drwx------  2 redj redj 4096  7月 10 05:23 config.d
-r--------  1 redj redj 1675  7月 10 05:21 id_rsa
-r--------  1 redj redj  389  7月 10 05:21 id_rsa.pub
-rw-------  1 redj redj  444  6月 28 00:19 known_hosts

Linuxのユーザをコマンドラインから作成する

はじめに

Linuxのユーザをコマンドラインから作成する手順を記しておく。 useraddがあまりフレンドリーではないので。。。

一般的なユーザの追加

ユーザを追加し、同時にホームディレクトリも作成する。 オプション-mをつけないとホームディレクトリが作成されない点に注意。 また、パスワードはコマンドpasswdで別途設定する必要がある。

短いオプションの場合は以下。

$ useradd -m [ユーザ名]
$ passwd [ユーザ名]

長いオプションの場合は以下。

$ useradd --create-home [ユーザ名]
$ passwd [ユーザ名]

一般的なユーザの追加で、uidとgidを指定

時々、他のマシンとユーザIDやグループIDを揃える必要があるので、そのときのために。 useradd実行時にはそのグループは作成されないため、必要ならばgroupaddを利用して事前に作成しておく必要がある。

短いオプションの場合は以下。

$ groupadd -g [グループID] [グループ名]
$ useradd -m -u [ユーザID] -g [グループ名] [ユーザ名]
$ passwd [ユーザ名]

長いオプションの場合は以下。

$ groupadd --gid [グループID] [グループ名]
$ useradd --create-home --uid [ユーザID] --gid [グループ名] [ユーザ名]
$ passwd [ユーザ名]

プロキシサーバ利用の設定

自分がよく使う、OSインストール直後のプロキシの設定方法。 UbuntuでもCentOSでも使えるはず。

echo "Defaults env_keep+=\"http_proxy https_proxy ftp_proxy no_proxy\"" > /etc/sudoers.d/proxy

echo "export http_proxy=http://server:port/
export https_proxy=http://server:port/
export ftp_proxy=http://server:port/
export no_proxy=localhost,127.0.0.1" > /etc/profile.d/proxy.sh

Windowsの共有フォルダを常時マウントする

はじめに

自分の作業環境的に、ファイルサーバはWindowsの共有フォルダとして提供されていることが多い。 そのため、開発環境のLinuxWindowsの共有フォルダを常時マウントしておきたい。 当然ながら、WindowsLinuxではユーザ管理やアクセス制御の考え方が違うので、うまく運用できるような方針を立てる必要がある。 ここで、以下の前提をおく。

  • 共有フォルダは自分専用で、他の人はアクセスしない
  • マウントを実施するLinuxマシンは自分専用で、他の人はアクセスしない

つまり、環境は完全に自分専用なので、他の人の使い勝手や、他の人によるファイル更新は無視できるとする。 そして、以下の方法で共有フォルダをマウントすることにした。

  • 共有フォルダはOS起動時に/sharedにマウントする
  • 共有フォルダのオーナをrootに、グループをdeveloperにする
  • 共有フォルダにアクセスさせたいユーザは、グループdeveloperに所属させる

なぜ自分専用の環境なのにアクセス制御を気にしているのかというと、ユーザの切り替えることで、複数の開発環境を使い分けているため。

環境

$ uname -srvm
Linux 4.15.0-23-generic #25-Ubuntu SMP Wed May 23 18:02:16 UTC 2018 x86_64

設定手順

まずは、マウントのタイプとしてcifsを指定できるようにするために、cifs-utilsをインストールする。

$ sudo apt install cifs-utils

次に、マウントポイント/sharedを作成する。

$ sudo mkdir /shared

fstabに以下の行を追加する。 ここで、共有フォルダのUNC(っぽいもの)は、仮に//server/sharedとしておく。

//server/shared    /shared    cifs    _netdev,x-systemd.automount,credentials=/root/.smb-credentials.txt,uid=root,gid=developer,file_mode=0660,dir_mode=0770    0    0

共有フォルダはネットワークが有効になってからでないとマウントできないので、「遅延マウント」の仕組みが必要になる。 ここでは、マウントオプション_netdevによりOS起動時のマウントを取りやめて、マウントオプションx-systemd.automountによりsystemd制御によるオンデマンドでのマウントを有効化している。

マウントオプションcredentialsで指定している認証情報のファイルはどこに配置してもよいが、ここでは/rootに配置することにした。 root以外からは読み込めないようにしておくこと。 認証情報のファイルの内容は以下だが、共有フォルダがActive Directoryで管理されていない場合(なのか?)はdomainは省略できる。

username=[ユーザ名]
password=[パスワード]
domain=[ドメイン名]

マウントオプションuid、gid、file_mode、dir_modeにより、Linuxパーミッションへのマッピングを指定している。 これにより、共有フォルダ上のファイルとディレクトリは、一律に所有者はrootに、グループはdeveloperにマッピングされる。 また、一律にグループに対して読み書きの権限が付与される。 そのため、グループdeveloperに所属しているユーザは、共有フォルダに対して読み書きができるようになる。 ただし、共有フォルダへの実際のアクセスは、マウントオプションcredentialsによって指定されたアカウントが利用されるので、最終的に読み書きが成功するかどうかは共有フォルダの元々のアクセス権次第である。

最後に、リブート後に「/sharedにアクセスして」マウントされていることを確認する。 前述のとおりオンデマンドのマウントを行っているので、確認のためには一度アクセスが必要になる。

$ cd /shared

$ df -h
Filesystem       Size  Used Avail Use% Mounted on
(略)
//server/shared  687G  122G  566G  18% /shared

$ ls -l
drwxrwx--- 2 root developer 0 Jun 15 00:43 dir-a
drwxrwx--- 2 root developer 0 Jun 15 00:43 dir-b
-rw-rw---- 1 root developer 0 Jun 15 00:43 file-a
-rw-rw---- 1 root developer 0 Jun 15 00:43 file-b