はじめに
AWX のユーザーインターフェイスを少し修正したい機会があって、どうやって試す環境を作れるのだろうと調べていました。
このあたりは公式ドキュメントがしっかりしていて、以下のページが非常に参考になりました。
- UI を用意: https://github.com/ansible/awx/blob/devel/awx/ui/README.md
- API を Docker Compose で用意: https://github.com/ansible/awx/blob/devel/tools/docker-compose/README.md
私の環境で、おそらく必要最低限の手順でやってみたので、まとめました。
大きく分けて API 側(バックエンド)と、UI 側(フロントエンド)の環境を作っていきます。
今回いじりたいのは UI 側だけです。現状 React 17 系で作られているようです。
環境
私の環境は以下のとおりです。開発イメージのビルドなどする際に ansible が利用されるので ansible もインストールしておきます。
% docker --version Docker version 20.10.17, build 100c701 % ansible --version ansible [core 2.13.2] % node --version v17.5.0 % npm --version 8.4.1
要件については、それぞれ以下のドキュメントにに記載があります。
- API: https://github.com/ansible/awx/blob/devel/CONTRIBUTING.md#prerequisites
- UI: https://github.com/ansible/awx/blob/devel/awx/ui/README.md#requirements
AWX は開発スピードが早いため、本ブログに書いたことが通用しなくなっているかもしれません。最新は公式ドキュメントを参照してください。
準備
まず GitHub の awx リポジトリから git clone
します。
今回はあとでプルリクを出す想定なこともあって、先に forks してそのリポジトリ clone しました。
% git clone https://github.com/akira6592/awx.git
(あとで気がついたのですが、AWX 16 の頃に fork したままで、 upstream を fetch 忘れていました。以降、手順は変わらないと思いますが最新版とログなどが微妙に異なるかもしれません。)
awx
というディレクトリが作成されるので移動します。
% cd awx
以降、このディレクトリで作業します。
■ API 側 (バックエンド)
バックエンドとして API の口を docker compose で作成します。
基本的には公式の案内通りにやっていきます。
ビルド
まずはビルド。make
コマンド経由でできるようになってます。いきなり Playbook が実行されます。
% make docker-compose-build ansible-playbook tools/ansible/dockerfile.yml -e build_dev=True -e receptor_image=quay.io/ansible/receptor:devel [WARNING]: No inventory was parsed, only implicit localhost is available [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' [WARNING]: An error occurred while calling ansible.utils.display.initialize_locale (unsupported locale setting). This may result in incorrectly calculated text widths that can cause Display to print incorrect line lengths PLAY [Render AWX Dockerfile and sources] ******************************************************************************************************************************************************************* TASK [Gathering Facts] ************************************************************************************************************************************************************************************* ok: [localhost] TASK [dockerfile : Create _build directory] **************************************************************************************************************************************************************** changed: [localhost] TASK [dockerfile : Render supervisor configs] ************************************************************************************************************************************************************** changed: [localhost] => (item=supervisor.conf) changed: [localhost] => (item=supervisor_task.conf) TASK [dockerfile : Render Dockerfile] ********************************************************************************************************************************************************************** changed: [localhost] PLAY RECAP ************************************************************************************************************************************************************************************************* localhost : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 DOCKER_BUILDKIT=1 docker build -t ghcr.io/ansible/awx_devel:devel \ --build-arg BUILDKIT_INLINE_CACHE=1 \ --cache-from=ghcr.io/ansible/awx_devel:devel . [+] Building 120.1s (51/51) FINISHED ...(略)... Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them %
この時点では、イメージは以下の1つがありました。
% docker images REPOSITORY TAG IMAGE ID CREATED SIZE ghcr.io/ansible/awx_devel devel c7035a7d68e8 15 hours ago 1.83GB
起動
続いて起動です。これも make
経由です。
Playbpook で docker-compose.yml
を生成して、docker-compose up
する流れのようです。
https://github.com/ansible/awx/blob/devel/tools/docker-compose/README.md#start-the-containers
数分かかります。途中 Admin password:
というログの後に、 admin
ユーザーの自動生成パスワードが表示されるのでメモっておきます。
(2022/08/27 後日のPR #12753、#12755 あたりで、admin
のパスワードを自分で指定できるようになったかもしれません。試せてはいません。)
% make docker-compose ansible-playbook -i tools/docker-compose/inventory tools/docker-compose/ansible/sources.yml \ -e awx_image=ghcr.io/ansible/awx_devel \ -e awx_image_tag=devel \ -e receptor_image=quay.io/ansible/receptor:devel \ -e control_plane_node_count=1 \ -e execution_node_count=2 \ -e minikube_container_group=false \ -e enable_keycloak=false \ -e enable_ldap=false \ -e enable_splunk=false \ -e enable_prometheus=false \ -e enable_grafana=false [WARNING]: An error occurred while calling ansible.utils.display.initialize_locale (unsupported locale setting). This may result in incorrectly calculated text widths that can cause Display to print incorrect line lengths PLAY [Render AWX Dockerfile and sources] ******************************************************************************************************************************************************************* TASK [Gathering Facts] ************************************************************************************************************************************************************************************* ok: [localhost] TASK [sources : Create _sources directories] *************************************************************************************************************************************************************** changed: [localhost] => (item=secrets) changed: [localhost] => (item=receptor) TASK [sources : Detect secrets] **************************************************************************************************************************************************************************** ok: [localhost] => (item=pg_password) ok: [localhost] => (item=secret_key) ok: [localhost] => (item=broadcast_websocket_secret) ...(略)... tools_awx_1 | Superuser created successfully. tools_awx_1 | Admin password: (ここにadminのパスワードが表示される) tools_receptor_hop | WARNING 2022/08/25 11:37:01 Backend connection failed (will retry): dial tcp 172.18.0.4:2222: connect: connection refused tools_awx_1 | System check identified some issues: tools_awx_1 | ...(略)...
数分立つと、以下のログくらいしか出力されない、落ち着いた状態になりました。
tools_redis_1 | 1:M 25 Aug 2022 12:30:20.082 * 100 changes in 300 seconds. Saving... tools_redis_1 | 1:M 25 Aug 2022 12:30:20.093 * Background saving started by pid 16 tools_redis_1 | 16:C 25 Aug 2022 12:30:20.117 * DB saved on disk tools_redis_1 | 16:C 25 Aug 2022 12:30:20.118 * Fork CoW for RDB: current 0 MB, peak 0 MB, average 0 MB tools_redis_1 | 1:M 25 Aug 2022 12:30:20.199 * Background saving terminated with success
フォアグラウンドで実行されてるので、このターミナルはそのままにしておきます。
なお、デフォルトでは git としてのブランチ名と同じ名前の ghcr.io/ansible/awx_devel:ブランチ名
を利用するようです。
もし、
Pulling awx_1 (ghcr.io/ansible/awx_devel:fix-awsome)... ERROR: manifest unknown make: *** [docker-compose] Error 1
のような、エラーになったら、COMPOSE_TAG=devel make docker-compose
のように、存在するイメージのタグ名を明示指定します(参考)。
別ターミナルを立ち上げて・・・イメージ一覧を確認すると以下のようになりました。
% docker images REPOSITORY TAG IMAGE ID CREATED SIZE ghcr.io/ansible/awx_devel devel c7035a7d68e8 15 hours ago 1.83GB quay.io/ansible/receptor devel 981db0fc0f90 40 hours ago 233MB postgres 12 f2f1f275f1a1 47 hours ago 373MB redis latest dc7b40a0b05d 2 days ago 117MB
この時点で、docker-compose ps
の表示は以下のようになりました。docker-compose.yml
が特定のディレクトリに生成されるので、-f
で指定するか、そこまで移動します。
% docker-compose -f tools/docker-compose/_sources/docker-compose.yml ps Name Command State Ports ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ tools_awx_1 /entrypoint.sh launch_awx.sh Up 22/tcp, 0.0.0.0:2222->2222/tcp, 0.0.0.0:3000->3001/tcp, 0.0.0.0:6899->6899/tcp, 0.0.0.0:7899->7899/tcp, 0.0.0.0:7900->7900/tcp, 0.0.0.0:7901->7901/tcp, 0.0.0.0:7902->7902/tcp, 0.0.0.0:7903->7903/tcp, 0.0.0.0:7904->7904/tcp, 0.0.0.0:7905->7905/tcp, 0.0.0.0:7906->7906/tcp, 0.0.0.0:7907->7907/tcp, 0.0.0.0:7908->7908/tcp, 0.0.0.0:7909->7909/tcp, 0.0.0.0:7910->7910/tcp, 0.0.0.0:7911->7911/tcp, 0.0.0.0:7912->7912/tcp, 0.0.0.0:7913->7913/tcp, 0.0.0.0:7914->7914/tcp, 0.0.0.0:7915->7915/tcp, 0.0.0.0:7916->7916/tcp, 0.0.0.0:7917->7917/tcp, 0.0.0.0:7918->7918/tcp, 0.0.0.0:7919->7919/tcp, 0.0.0.0:7920->7920/tcp, 0.0.0.0:7921->7921/tcp, 0.0.0.0:7922->7922/tcp, 0.0.0.0:7923->7923/tcp, 0.0.0.0:7924->7924/tcp, 0.0.0.0:7925->7925/tcp, 0.0.0.0:7926->7926/tcp, 0.0.0.0:7927->7927/tcp, 0.0.0.0:7928->7928/tcp, 0.0.0.0:7929->7929/tcp, 0.0.0.0:7930->7930/tcp, 0.0.0.0:7931->7931/tcp, 0.0.0.0:7932->7932/tcp, 0.0.0.0:7933->7933/tcp, 0.0.0.0:7934->7934/tcp, 0.0.0.0:7935->7935/tcp, 0.0.0.0:7936->7936/tcp, 0.0.0.0:7937->7937/tcp, 0.0.0.0:7938->7938/tcp, 0.0.0.0:7939->7939/tcp, 0.0.0.0:7940->7940/tcp, 0.0.0.0:7941->7941/tcp, 0.0.0.0:7942->7942/tcp, 0.0.0.0:7943->7943/tcp, 0.0.0.0:7944->7944/tcp, 0.0.0.0:7945->7945/tcp, 0.0.0.0:7946->7946/tcp, 0.0.0.0:7947->7947/tcp, 0.0.0.0:7948->7948/tcp, 0.0.0.0:7949->7949/tcp, 0.0.0.0:7950->7950/tcp, 0.0.0.0:7951->7951/tcp, 0.0.0.0:7952->7952/tcp, 0.0.0.0:7953->7953/tcp, 0.0.0.0:7954->7954/tcp, 0.0.0.0:7955->7955/tcp, 0.0.0.0:7956->7956/tcp, 0.0.0.0:7957->7957/tcp, 0.0.0.0:7958->7958/tcp, 0.0.0.0:7959->7959/tcp, 0.0.0.0:7960->7960/tcp, 0.0.0.0:7961->7961/tcp, 0.0.0.0:7962->7962/tcp, 0.0.0.0:7963->7963/tcp, 0.0.0.0:7964->7964/tcp, 0.0.0.0:7965->7965/tcp, 0.0.0.0:7966->7966/tcp, 0.0.0.0:7967->7967/tcp, 0.0.0.0:7968->7968/tcp, 0.0.0.0:7969->7969/tcp, 0.0.0.0:7970->7970/tcp, 0.0.0.0:7971->7971/tcp, 0.0.0.0:7972->7972/tcp, 0.0.0.0:7973->7973/tcp, 0.0.0.0:7974->7974/tcp, 0.0.0.0:7975->7975/tcp, 0.0.0.0:7976->7976/tcp, 0.0.0.0:7977->7977/tcp, 0.0.0.0:7978->7978/tcp, 0.0.0.0:7979->7979/tcp, 0.0.0.0:7980->7980/tcp, 0.0.0.0:7981->7981/tcp, 0.0.0.0:7982->7982/tcp, 0.0.0.0:7983->7983/tcp, 0.0.0.0:7984->7984/tcp, 0.0.0.0:7985->7985/tcp, 0.0.0.0:7986->7986/tcp, 0.0.0.0:7987->7987/tcp, 0.0.0.0:7988->7988/tcp, 0.0.0.0:7989->7989/tcp, 0.0.0.0:7990->7990/tcp, 0.0.0.0:7991->7991/tcp, 0.0.0.0:7992->7992/tcp, 0.0.0.0:7993->7993/tcp, 0.0.0.0:7994->7994/tcp, 0.0.0.0:7995->7995/tcp, 0.0.0.0:7996->7996/tcp, 0.0.0.0:7997->7997/tcp, 0.0.0.0:7998->7998/tcp, 0.0.0.0:7999->7999/tcp, 0.0.0.0:8013->8013/tcp, 0.0.0.0:8043->8043/tcp, 0.0.0.0:8080->8080/tcp, 0.0.0.0:8888->8888/tcp tools_postgres_1 docker-entrypoint.sh postg ... Up 5432/tcp tools_receptor_1 /entrypoint.sh receptor -- ... Up 22/tcp, 8013/tcp, 8043/tcp, 8080/tcp tools_receptor_2 /entrypoint.sh receptor -- ... Up 22/tcp, 8013/tcp, 8043/tcp, 8080/tcp tools_receptor_hop /usr/local/bin/dumb-init - ... Up 0.0.0.0:5555->5555/tcp, 7323/tcp tools_redis_1 redis-server /usr/local/et ... Up 6379/tcp %
パスワードについて
もし、パスワードが分からなくなってしまった場合でも、以下のコマンドで新しい管理者ユーザーを作成できます。
% docker exec -ti tools_awx_1 awx-manage createsuperuser
API の確認
できたかなー、ということで、さっそく API を叩きます。ポートは 8043
です。
試すエンドポイントは、認証いらずで便利な ping
を指定します。
% curl -ks https://localhost:8043/api/v2/ping/ | jq . { "ha": false, "version": "16.0.1.dev4198+g4fbf5e9e2f", "active_node": "awx_1", "install_uuid": "6d9c484a-47f9-45c5-ac97-71c53b889792", "instances": [ { "node": "awx_1", "node_type": "hybrid", "uuid": "00000000-0000-0000-0000-000000000000", "heartbeat": "2022-08-25T12:15:32.388568Z", "capacity": 59, "version": "16.0.1.dev4198+g4fbf5e9e2f" }, { "node": "receptor-1", "node_type": "execution", "uuid": "e4b8a674-eaf5-48e6-804c-32d448d67b6e", "heartbeat": "2022-08-25T12:15:27.896150Z", "capacity": 59, "version": "ansible-runner-2.1.1.dev58" }, ...(略)... } %
無事に返ってきました。これで API 側はできたようです。
(あとで色々試していたら "version": "21.5.1.dev3+g4fbf5e9e2f"
と最新になりました。ずっと ghcr.io/ansible/awx_devel:devel
を利用してたつもりではあるのですが・・)
コンテナ側の UI は特になにもせず
この時点で、localhost:8043
の通常の UI を見ても、以下のような文字列しか返ってきません。
<% if (process.env.NODE_ENV === 'production') { %> <% } %> <% if (process.env.NODE_ENV === 'production') { %> <% } else { %> <% } %> <% if (process.env.NODE_ENV === 'production') { %>
docker exec tools_awx_1 make clean-ui ui-devel
を実行すれば、コンテナ側の UI も構築されるようです(うまくいくまで試せていません)。ですが、今回は UI はホストマシンに構築していじりたいので、コンテナ側はこのままにしておきます。
■ UI 側 (フロントエンド)
続いて、UI 側です。API 側を起動したターミナルはそのままにして、別のターミナルで作業します。
パッケージのインストール
npm
で必要なパッケージをインストールします。コマンドはこちらに記載されています。
% npm --prefix=awx/ui install
UI 側の起動
次に起動します。
% npm --prefix=awx/ui start > prestart > lingui compile Compiling message catalogs… Done! > start > GENERATE_SOURCEMAP=false ESLINT_NO_DEV_ERRORS=true PORT=3001 HTTPS=true DANGEROUSLY_DISABLE_HOST_CHECK=true react-scripts start Browserslist: caniuse-lite is outdated. Please run: npx browserslist@latest --update-db Why you should do it regularly: https://github.com/browserslist/browserslist#browsers-data-updating (node:56871) [DEP_WEBPACK_DEV_SERVER_HTTPS] DeprecationWarning: 'https' option is deprecated. Please use the 'server' option. (Use `node --trace-deprecation ...` to show where the warning was created) [HPM] Proxy created: /api,/websocket,/sso -> https://localhost:8043 (node:56871) [DEP_WEBPACK_DEV_SERVER_ON_AFTER_SETUP_MIDDLEWARE] DeprecationWarning: 'onAfterSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option. (node:56871) [DEP_WEBPACK_DEV_SERVER_ON_BEFORE_SETUP_MIDDLEWARE] DeprecationWarning: 'onBeforeSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option. Starting the development server... (ここで数分かかる) You can now view ui in the browser. Local: https://localhost:3001 On Your Network: https://192.168.1.135:3001 Note that the development build is not optimized. To create a production build, use npm run build. assets by path static/ 23.7 MiB assets by path static/js/*.js 19.9 MiB assets by chunk 2.83 MiB (id hint: vendors) 50 assets 25 assets assets by path static/media/ 3.85 MiB assets by chunk 2.32 MiB (auxiliary name: main) assets by path static/media/*.woff2 1.22 MiB 38 assets assets by path static/media/*.woff 1.1 MiB 26 assets assets by path static/media/*.jpg 1.53 MiB 6 assets asset static/media/status-icon-sprite.4fee9fefc3971799d2dd.svg 3.44 KiB [emitted] [immutable] [from: node_modules/@patternfly/react-styles/css/assets/images/status-icon-sprite.svg] (auxiliary id hint: vendors) asset asset-manifest.json 36 KiB [emitted] asset index.html 1.27 KiB [emitted] orphan modules 3.02 MiB [orphan] 2074 modules runtime modules 59.8 KiB 30 modules javascript modules 14.2 MiB modules by path ./node_modules/ 8.17 MiB 1821 modules modules by path ./src/ 6.02 MiB 971 modules asset modules 8.32 KiB (javascript) 3.85 MiB (asset) modules by path ./node_modules/@patternfly/react-core/dist/styles/assets/fonts/ 2.54 KiB (javascript) 2.28 MiB (asset) 62 modules modules by path data:image/svg+xml;charset=utf8,%3Csvg xmlns=%27http://www.w3.org/2000/ 5.41 KiB 8 modules modules by path ./node_modules/@patternfly/react-styles/ 294 bytes (javascript) 1.54 MiB (asset) 7 modules modules by path ./node_modules/@patternfly/react-core/dist/styles/assets/pficon/ 84 bytes (javascript) 36.9 KiB (asset) 2 modules json modules 30 KiB ./node_modules/entities/lib/maps/entities.json 28.4 KiB [built] [code generated] ./node_modules/entities/lib/maps/legacy.json 1.24 KiB [built] [code generated] ./node_modules/entities/lib/maps/xml.json 62 bytes [built] [code generated] ./node_modules/entities/lib/maps/decode.json 308 bytes [built] [code generated] webpack 5.66.0 compiled successfully in 211217 ms ...(フォアグラウンド実行中)...
正常にアクセスできまで数分間かかりました。
https://localhost:3001/
を開いて、ログイン画面が表示され、admin
と先程メモったパスワードでログインできれば一区切りです。
いつものでデモデータもはいっていました。
admin
のパスワードは、覚えやすく安全なものに変えちゃったほうがいいと思います。
なお、途中のログにあるように、デフォルトでは https://localhost:8043
の API を叩きに行くようです。今回は、docker compose で構築した API の口が https://localhost:8043
で同じなので、デフォルトのままでOKです。
もし、別のポートや別のサーバーを相手にしたい場合は、ドキュメントに記載があるように、環境変数 TARGET
をセットして起動します。(おためし)
# 別のサーバーのAPIを使う例 TARGET='https://awx.local:8043' npm --prefix awx/ui start
UI側と機能の整合性があるのであれば、今回用にAPI側をわざわざ作るのではなく、既存のものを使ってもよいかと思います。
UI のコードがあるパス
ui/src
ディレクトリ配下に、コンポーネントや hook のコードがあります。このあたりを触っていけばいいわけですね。
ということで、ほんとにやりたかったちょっとした修正にとりかかりました。
おわりに
API 側をコンテナで用意し、ホスト側にUIを用意しました。
AWX の画面表示が、どのような仕組みで成り立っているのか少し理解するきっかけになりました。