はじめに
先日、SAP Build Process AutomationでBTPのユーザ登録を自動化するという記事を書きました。記事の中で紹介したのは、Process Automationのワークフローをベースとして、申請されたユーザをBTPのサブアカウントに登録したり、ロールコレクションを付与したりするフローでした。
改善ポイントとして残っていたのが、Cloud Foundryのスペースへのユーザ登録でした。スペースへのユーザ登録ができるHTTPベースのAPIが見つからなかったため、上記の記事ではシステム管理者がマニュアルで行うこととしていました。
ワークフローからユーザ登録をするためにはCloud Foundry CLIをラップするAPIが必要になります。そこで今回は以下のような構成でAPIを作成することにしました。
- CFコマンドを実行するためのスクリプトを作成
- スクリプトを実行するNode.jsアプリを作成し、APIを公開
Cloud Foundry CLIを実行環境にインストールする必要があるため、Kyma runtimeを利用します。
ステップ
- スクリプトの作成
- Node.jsアプリの作成
- Dockerイメージの作成
- シークレットの作成
- Deploymentの作成
1. スクリプトの作成
以下のスクリプトを作成しました。
#!/bin/bash
if [ -z "$1" ] || [ -z "$2" ]; then
echo "Usage: $0 USER ROLE"
exit 1
fi
# Assign positional arguments to variables
USER=$1
ROLE=$2
# login
cf login -a $CF_API_URL -u $CF_USERNAME -p $CF_PASSWORD -o $CF_ORG -s $CF_SPACE
# add a user to space
result=$(cf set-space-role $USER $CF_ORG $CF_SPACE $ROLE | awk '{print $NF}')
echo $result
if [[ "$result" == *OK ]]; then
echo "OK"
else
echo "Error"
exit 1
fi
このスクリプトは引数にユーザと割り当てたいスペースのロールを受け取ります。
./script.sh user1@gmail.com SpaceDeveloper
スクリプトで行っているのは、Cloud Foundryにログインしてユーザにロールを割り当てることです。スペースにユーザが未登録の場合は、cf set-space-role
コマンドによりユーザが追加されます。$CF_API_URL
、$CF_USERNAME
等のパラメータは環境変数として定義します。
cf set-space-role
コマンドは、以下のような結果を返します。
Assigning role SpaceAuditor to user xxx@gmail.com in org f83f5860trial / space dev as xxx@gmail.com...
OK
Assigning role SpaceAuditor to user mio.fujita.01@gmail.com in org f83f5860trial / space dev as xxx@gmail.com...
User 'xxx@gmail.com' already has role 'SpaceAuditor' in org 'f83f5860trial' / space 'dev'.
OK
Assigning role SpaceAuditor to user mio.fujita.02@gmail.com in org f83f5860trial / space dev as xxx@gmail.com...
No user exists with the username 'xxx@gmail.com'.
FAILED
よって、結果の末尾が"OK"か否かによって、スクリプトを正常終了するか異常終了するかを判断します。
2. Node.jsアプリの作成
Node.jsアプリは/spaceuser
というエンドポイントを公開します。ボディ(JSON)に設定されたuser
, role
という2つの項目を先ほどのスクリプトに渡します。
const express = require('express')
const { execSync } = require('child_process')
const app = express()
const port = process.env.PORT || 8080
app.use(express.json())
app.post('/spaceuser', (req, res) => {
console.log('Received data:', req.body)
const user = sanitize(req.body.user)
const role= sanitize(req.body.role)
try {
execSync(`./script.sh ${user} ${role}`)
} catch (error) {
return res.status(400).send(error.message);
}
res.send('Space user was addedded successfully.')
})
function sanitize (input) {
return input.split("|")[0]
}
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
sanitize
というファンクションは、例えば"role": "SpaceDeveloper" | ls"
のように任意のコマンドを渡されても後ろのコマンドが実行されないように、 パイプの前の項目のみを取得します。
3. Dockerイメージの作成
以下のDockerfileを作成します。
# syntax=docker/dockerfile:1
FROM node:latest
# install cf cli
RUN wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | apt-key add -
RUN echo "deb https://packages.cloudfoundry.org/debian stable main" | tee /etc/apt/sources.list.d/cloudfoundry-cli.list
RUN apt-get update
RUN apt-get install cf8-cli
WORKDIR /app
COPY ["src/package.json", "src/package-lock.json*", "./"]
RUN npm install
COPY src/ .
# remove windows style carriage returns
RUN sed -i -e 's/\r$//' script.sh
CMD [ "node", "server.js" ]
nodeをベースイメージとして、そこにCLoud Foundry CLIをインストールします。インストール方法は以下のページを参考にしました。
https://docs.cloudfoundry.org/cf-cli/install-go-cli.html#pkg-linux
以下の部分は、私がWindowsでDockerイメージを作成していることに起因する問題への対応です。
# remove windows style carriage returns
RUN sed -i -e 's/\r$//' script.sh
私はスクリプトやNode.jsアプリの開発はBusiness Application Studio(Linux環境)で行い、Dockerイメージを作成するためにWindowsの端末にGit経由で引っ張ってくるという開発を行っていました。するとWindowsに持ってきたときにスクリプトファイルにWindowsスタイルの改行コードが入り込み、Kyma runtimeでスクリプトを実行しようとすると./script.sh: not found
というエラーになりました。対応として、イメージ作成時にWindowsスタイルの改行コードを除去することにしました。
APIを実行したときのエラー内容(Windowsスタイルの改行コードが含まれている場合)
以下のコマンドでイメージを作成し、Docker Hubにプッシュします。
docker build -t <ユーザ名>/<イメージ>:<タグ> .
docker image push <ユーザ名>/<イメージ>:<タグ>
4. シークレットの作成
スクリプトの中で環境変数として使いたいパラメータをシークレットとして登録します。
今回はUIから作成しましたが、kubectlのコマンドを使って以下のように登録することもできます。(改行はPower Shell向けになっているので、Linux, Macの場合は\に変更)
kubectl create secret generic env-cfcommand `
--namespace default `
--from-literal=CF_API_URL=apiUrl `
--from-literal=CF_USERNAME=userName `
--from-literal=CF_PASSWORD=password `
--from-literal=CF_ORG=org `
--from-literal=CF_SPACE=space
5. Deploymentの作成
deployment.yamlを以下のように設定します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: cfcommand
labels:
app: cfcommand
spec:
replicas: 1
selector:
matchLabels:
app: cfcommand
template:
metadata:
labels:
app: cfcommand
spec:
containers:
- name: cfcommand
image: docker.io/miyasuta/cfcommand:0.0.2
imagePullPolicy: Always
ports:
- containerPort: 8080
resources:
limits:
ephemeral-storage: 256M
memory: 256M
cpu: 100m
requests:
cpu: 100m
ephemeral-storage: 256M
memory: 256M
env:
- name: CF_USERNAME
valueFrom:
secretKeyRef:
name: env-cfcommand
key: CF_USERNAME
- name: CF_PASSWORD
valueFrom:
secretKeyRef:
name: env-cfcommand
key: CF_PASSWORD
- name: CF_API_URL
valueFrom:
secretKeyRef:
name: env-cfcommand
key: CF_API_URL
- name: CF_ORG
valueFrom:
secretKeyRef:
name: env-cfcommand
key: CF_ORG
- name: CF_SPACE
valueFrom:
secretKeyRef:
name: env-cfcommand
key: CF_SPACE
status: {}
---
apiVersion: v1
kind: Service
metadata:
name: cfcommand
spec:
selector:
app: cfcommand
ports:
- port: 8080
targetPort: 8080
protocol: TCP
---
apiVersion: gateway.kyma-project.io/v1alpha1
kind: APIRule
metadata:
name: cfcommand
spec:
gateway: kyma-gateway.kyma-system.svc.cluster.local
service:
name: cfcommand
port: 8080
host: cfcommand
rules:
- path: /.*
methods: ["GET", "POST"]
accessStrategies:
- handler: oauth2_introspection
config: {}
ポイントは以下の2点です。
①シークレットを環境変数にバインドする
以下のセクションでenv-cfcommand
というシークレットの各項目を、アプリケーションで使う環境変数にバインドしています。
spec:
containers:
#...
env:
- name: CF_USERNAME
valueFrom:
secretKeyRef:
name: env-cfcommand
key: CF_USERNAME
参考:Define container environment variables using Secret data
②認証の設定
APIRuleの設定で、accessStrategies.handlerにoauth2_introspection
を設定します。これにより、APIにアクセスする際にKyma runtimeで作成したOAuthクライアントを使った認証が必要になります。
apiVersion: gateway.kyma-project.io/v1alpha1
kind: APIRule
metadata:
name: cfcommand
spec:
#...
rules:
#...
accessStrategies:
- handler: oauth2_introspection
config: {}
OAuthクライアントの作成方法については、以下の記事をご参照ください。
※2023/3/15追記
OAuthクライアントを使用した認証はBTPのDestinationで使用することができなかった(認証エラーになる)ため、XSUAAを使用した認証に変更しました。
accessStrategies:
# - handler: oauth2_introspection
# config: {}
- handler: jwt
config:
jwks_urls:
- https://f83f5860trial.authentication.us10.hana.ondemand.com/token_keys
動作確認
Rest Clientを使ってAPIのテストを行います。一つ目のリクエストで認証用のトークンを取得し、二つ目のリクエストでCloud Foundry CLIを実行するAPIを呼び、ユーザにCloud Foundryのスペースロールを割り当てます。
@server=https://cfcommand.<クラスタードメイン>.kyma.ondemand.com
@oauthserver=https://oauth2.<クラスタードメイン>.kyma.ondemand.com
@user=OAuth2Clientのclient_id
@password=OAuth2Clientのclient_secret
### 1. Get Token
# @name client_credentials
POST {{oauthserver}}/oauth2/token
Authorization: Basic {{user}}:{{password}}
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
### 2. Add user to space
POST {{server}}/spaceuser
Content-Type: application/json
Authorization: Bearer {{client_credentials.response.body.access_token}}
{
"user": "user",
"role": "SpaceAuditor"
}
おわりに
これでCloud Foundryスペースへのユーザ登録がAPIでできるようになりました。次回はこのAPIをSAP Build Process Automationのフローに組み込んでいきます。