やはりお前らのCFデプロイはダサい  

※[前回記事](https://qiita.com/tnmrknm/items/d76349a7ae0731b809d3)同様、これはよくある煽り系釣りタイトルみたいなものです。怒らないでください。
煽ると軽バズりすることがわかったので2回目です。(安直)
タイトルの割には丁寧めに、なぜダサいのかをこんこんと解説し改善を目指していきます。

## 前回のおさらいと今回の目的
[前回記事](https://qiita.com/tnmrknm/items/d76349a7ae0731b809d3)では「みなさん、まさか**手動でポチポチとビルド/デプロイ**なんてしてないですよね？」と切り出しまして、MTA(後述)を使ったプロジェクトのビルドとデプロイを自動化しました。
今回はさらに自動テスト実行プロセスをパイプラインに加えていきます。
また、その他のAzure DevOpsの機能を活用することでSAP Cloud Platformでの開発におけるDevOps化実現のTipsを示していければと思っております。

前回のおさらいをしつつ、今回は中身の要素技術についても軽く解説していきます。

### 1.SAP Cloud Platform CF環境とMTA

[SAP Cloud Platform](https://www.sap.com/japan/products/cloud-platform.html)とはSAPが提供するクラウドPaaSサービスです。
S/4HANA拡張のためのSDKや、S/4HANAへの接続設定といった、SAPが提供するサービスを活用しながらアプリケーションの構築/実行ができるサービスです。

アプリケーション実行環境の1つである、Cloud Foundry(CF)環境ではアプリケーションはそれぞれコンテナ上で起動するのですが、コンテナオーケストレーションツールには2020年2月現在、Multi Target Application(MTA)というSAP独自の仕組みが採用されています。
JavaやSAPUI5などで作成されたプロジェクトはMTAプロジェクトフォルダ直下に配置され、MTAのビルド時にmtarファイルにまとめられます。
一般的なコンテナオーケストレーションツールのデファクトスタンダードである、Kubernetesと同様に、MTAもyaml形式のファイルを使ってプロジェクト配下のオブジェクトの挙動を表現することができます。


### 2.CI/CD
「CI」とは「Continuous Integration(継続的インテグレーション)」の略です。
テストやビルドを自動化することで継続的に行えるようにするアプローチのことを指しています。
また「CD」とは「Continuous Delivery(継続的デリバリ)」の略です。
本番環境にデプロイが可能な状態を整えるプロセスのことであり、「Continuous Deployment(継続的デプロイ)」の略として使われることもあります。
ではCI/CDを実現することにどんな意味があるのか？というと、多くの理由が挙げられますが、

- 開発中も常に動く状態のアプリケーションが開発環境に上げられること
- 壊れたときにすぐに気がつけること
- テスト実行自体の工数削減

などがメリットとして上げられます。
なので~~いつまでも手動でダサいことやらないで~~ぜひSAP Cloud Platformでの開発でも取り入れて自動化しちまおう、というお話です。

### 3.Azure DevOps

2020年2月現在、筆者がSAP Cloud Platformの開発でDevOps環境を導入するのに最適なツールだと考えているのが、Microsoftが提供するSaaSのCI/CDツールであるAzure DevOpsです。
前回記事だとJenkinsコンテナをCF環境に建てるのが良いと書きましたが、あれ以来すっかりAzure DevOpsの魅力にハマり、
今では寝返ってこちらがベストだと思っています。すいません。。ですが最高です...//

Jenkinsに代表される他のCI/CDツールと比較して以下のようなメリットが挙げられます。

- 自前のデプロイサーバが不要なので導入コストが低い
- yamlの形式の設定ファイルでパイプライン処理を制御しているので、プロジェクトと一緒にGitでバージョン管理ができる
- [データセキュリティ](https://docs.microsoft.com/ja-jp/azure/devops/organizations/security/data-protection?view=azure-devops)はShare Pointと同レベルの堅牢性が担保されている
- **チケット管理サービスや、Gitサービスが同じサービス群の中に含まれている**

最後に上げた、CI/CDツール以外の機能も備わっているという部分が特に他のCI/CDツールにはない特徴でありメリットだと思っています。
Boards(チケット管理), Repos(Git), Pipelines(CI/CD), Test Plans(テスト計画作成), Artifacts(パッケージリポジトリ管理)という5つが主な機能です。

サービス詳細は[公式ドキュメント](https://docs.microsoft.com/ja-jp/azure/devops/user-guide/what-is-azure-devops?view=azure-devops)を参照してください。


## Azure DevOpsの活用方法
というわけでどのようにしてSAP Cloud Platformの開発でCI/CDやDevOps化を実現しているのか見ていきます。

### MTAプロジェクトの準備

今回は[こんな構成](https://github.com/kanamen10/uglyMTA.git)でプロジェクトを作成しました。

```text
/
  └ MTA Project
    ├ appRouter
    ├ ui_deployer
    ├ UI5 Project
    ├ hdi module
    ├ Java Project
      └ src
      └ project-checkstyle.xml
      └ pom.xml
    └ xs-security.json
    └ azure-pipelines.yml
    └ mta.yaml
```

UI5+Javaでフロントとバックを構成するMTAプロジェクトになっています。
Pipelineの中でMTAの自動ビルド/デプロイに加えて自動テストも実行したいので、Javaプロジェクトの中に簡単なJUnitのテストコードも作成しておきます。
今回はこのように作成しました。

```java:Calc.class
public class Calc {
	public static int divide(int x, int y) {
		return x / Math.abs(y);
	}
}
```

```java:CalcTest.class
@RunWith(Enclosed.class)
public class CalcTest {
	@RunWith(Parameterized.class)
	public static class DevideTest {
		@Parameters
		public static Iterable<Fixture> data() {
			List<Fixture> fixtures = new ArrayList<>();
			fixtures.add(new Fixture(4, 2, 2, null));
			fixtures.add(new Fixture(4, 0, 0, ArithmeticException.class));
			fixtures.add(new Fixture(4, -2, -2, null));
			return fixtures;
		}

		@Parameter
		public Fixture fixture;

		@Rule
		public ExpectedException expectedException = ExpectedException.none();

		@Before
		public void setUp() {
			if (fixture.exceptionClass != null) {
				expectedException.expect(fixture.exceptionClass);
			}
		}

		@Test
		public void test() {
			int actual = Calc2.divide(fixture.x, fixture.y);
			assertThat(actual, is(fixture.expected));
		}

		public static class Fixture {
			public final int x;
			public final int y;
			public final int expected;
			public final Class<? extends Throwable> exceptionClass;
			public Fixture(int x, int y, int expected, Class<? extends Throwable> exceptionClass) {
				this.x = x;
				this.y = y;
				this.expected = expected;
				this.exceptionClass = exceptionClass;
			}
		}
	}
}
```

また、自動テストに加えて静的コード解析もCheckstyleで行っています。
Checkstyleの設定内容に関しては[こちらのリポジトリ](https://github.com/kanamen10/uglyMTA)の内容を参照してください。
Versionは3.1.0を使用しているので、それに合わせてスタイル定義を以下のリンクからダウンロードしてproject-checkstyle.xmlとしてJavaプロジェクト直下に配置します。

- [Google Check Style XML定義](https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml)



セッティングが終わったら、念の為手動でテストを実行して出力される結果を確認しておきます。
(ダサくてすいません)

```terminal
$ mvn test
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/b90d359f-f435-a556-0ef7-33440bf16c77.png)


5つのテストケースを実行し、全て正常に終了することがわかりました。


続いて、このテストを自動的に実行してくれる処理を加えたyamlファイルの構築をしていきます。

###Pipelines設定
Azure DevOpsでは、自動化したい一連の流れをMTAプロジェクトフォルダ直下に配置したazure-pipeline.ymlの中で定義します。
このyamlを実行するのがAzure pipelineです。
今回は以下のように設定しました。

```yaml:azure-pipelines.yml
name: not-ugly-MTA
resources:
  containers:
  - container: mta
    image: 'ppiper/mta-archive-builder:latest'
    options: --user 0:0

    # In case use MTA Build Tools, below.
    # image: devxci/mbtci
    # options: --user 0:0
  - container: cfcli
    image: 'ppiper/cf-cli'
    options: --user 0:0 --privileged
  - container: node
    image: 'geekykaran/headless-chrome-node-docker:latest'
    options: --privileged

trigger:
- master

stages:
- stage: test
  displayName: Java Test
  jobs:
    - job: test
      pool:
        vmImage: 'ubuntu-latest'
      steps:
        - task: Maven@2
          inputs:
            #Require Change Pom.xml Path.  
            mavenPomFile: 'ugly-java/pom.xml'
            mavenOptions: '-Xmx3072m'
            javaHomeOption: 'JDKVersion'
            jdkVersionOption: '1.8'
            jdkArchitectureOption: 'x64'
            publishJUnitResults: true
            testResultsFiles: '**/surefire-reports/TEST-*.xml'
            goals: 'test'

- stage: build
  displayName: Build MTA for SAP Cloud Platform
  jobs:
    - job: build
      pool:
        vmImage: 'ubuntu-latest'
      container: mta
      steps:        
        - bash: 'mtaBuild --build-target CF --mtar nameraka.mtar build'
        - publish: $(System.DefaultWorkingDirectory)/.

      # In case use MTA Build Tools, below.
        # - bash: wget https://github.com/SAP/cloud-mta-build-tool/releases/download/v1.0.5/cloud-mta-build-tool_1.0.5_Linux_amd64.tar.gz
        # - bash: tar xvzf cloud-mta-build-tool_1.0.5_Linux_amd64.tar.gz
        # - bash: sudo cp mbt /usr/local/bin/
        # - bash: npm config set @sap:registry https://npm.sap.com
        # - bash: 'mbt build -p=cf -m=verbose --mtar=MySampleApp.mtar'
          artifact: WebApp


## CF Deploy
- stage: deploy
  displayName: Deployment to SAP Cloud Platform (cf)
  jobs:
    - job: deploy
      pool:
        vmImage: 'ubuntu-latest'
      container: cfcli
      steps:
        - download: current
          artifact: WebApp
        - bash: 'cf login -u "$(CF-USER)" -p "$(CF-PASSWORD)" -a "$(CF-API)" -o "$(CF-ORG)" -s "$(CF-SPACE)" && cf deploy $(Pipeline.Workspace)/WebApp/nameraka.mtar -f'
```

大きく分けて
Javaの自動テスト→MTAのビルド→MTAのデプロイ
の順に実行しています。
MTAのビルド/デプロイに関しては、SAPが公開している[リポジトリ](https://github.com/SAP/devops-docker-images)から、mta-archive-builderとcf-cliのDocker Imageを使用しています。


### ReposとPipelinesの紐付け
上記のようにプロジェクトを作成したら、Azure Reposにソースコードをコミットし、リポジトリをPipelinesと紐づけていきます。
紐づけ方に関しては下記を参照してください。
[前回記事](https://qiita.com/tnmrknm/items/d76349a7ae0731b809d3)

### チケットの作成

Azure Boardsというサービスからでチケットを作成することで、プロジェクトの進捗や障害管理をすることができます。
今回は以下のようにチケットを作成しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/df128f4c-a5f0-5acc-dedc-c126f307c4e5.png)


Taskを選択して作成します。
タスクに対して人をアサインしたり、詳細を記述することができたり、キャプチャ画面添付することもドラッグ&ドロップで簡単にできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/625897ad-9aad-3bee-bcd3-654bbc30795a.png)


作成したチケットは、Azure Repos(Git)上のコミットと連携することも可能です。
赤枠で囲ったDevelopmentという部分に、コミットが紐づいて表示されます。
手動でコミットを選択して紐づけることも可能ですが、基本的にはコミットメッセージにチケット番号を入れて自動的に紐づけていきます。

今回作成したチケットは#16番で作成されました。


### チケットとコミットの紐づけ

Pipelineの実行タイミングをmasterブランチへのコミット時に設定したので、ローカルGitからorigin/masterへプッシュしていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/739b99d6-30bb-ac00-765d-4e1e3ee2e0d6.png)

コミットメッセージ内の"#16"という文字を認識して、該当する番号のチケットにコミットを紐づけることができています。
めちゃくちゃ簡単です！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/9360f1f9-6888-6efd-c1eb-2ce06b101fff.png)

チケット内のリンクから、変更内容を確認することもできます。
今回は、mta.yaml内の余分な改行を削除する対応を行いました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/8d0f5b74-05c4-1fd2-2daa-853d7c05eb7b.png)


Azure DevOpsのOverview画面では、作成したチケットを見ることでプロジェクト全体の進捗状況を確認することもできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/480b9e55-14e1-1c2a-10ed-b1f9e0aae77b.png)

今回は個人開発なので全くアジャイルっぽくないですが、本来はSprintのバーンダウンチャートをこの画面から確認したり、
アサインされているタスクを見たりすることができます。

### テスト実行結果

では実行したPipelineの結果を見てみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/2c66c843-7854-55bd-ae37-1aca5273b183.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/ac77d662-4f61-44e5-116b-427a91b9c213.png)


yamlの内容が全て正常に成功していることがわかります。
さらに実行結果を詳しく見てみると、自動テストの結果を参照することも可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/687fbd68-4fe0-cbc7-aa10-fcc4959713a3.png)


Mavenのログを確認すると、checkstyleも実行されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/6c58ed48-4e37-371b-b5c3-8857343bad7b.png)

ただし今回はただCheckstyleを実行しただけなので、かなりのWarningが出ています。
この辺りのルールは見直す必要がありそうです。

また、ビルドの結果作成されたmtarファイルはPipeline内のArtifactsからダウンロード可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/0ff19930-83ac-d495-27d6-56090e7a9bcb.png)

##今後の課題
以下のような課題を解決したい所存。

- CheckStyleレポートの出力とAzure DevOps上での参照
    - 今回はログ上で確認してしまっているので結果を通知する形式など、考える必要がある。
- コンテナ内でCacheのリストア
    - Azure Pipelineの無料枠では実行環境のスペックが低いのでビルド/デプロイに時間がかかる。Cacheを活かす方法でyamlを書く必要あり。
- Key Vaultを使用してデプロイ先の動的切替
    - デプロイ先環境は環境変数化して固定で持っているので、Azure Key Vaultなどのサービスを利用して動的に変更するようにしたい。
- SAPUI5の自動テスト
    - バックエンドのテスト実行はできたので、フロントエンドの自動テストを導入したい。