聞こえないJavaエンジニアが適当に書き連ねていく

つらつらとメモしたり日頃の溜まっている想いを吐き出す場所です。

Eclipse + checkStyle の設定メモ

前提

ベースにするのはGooglecheckStyle設定。

いつかリンクが切れる可能性はあるけど直リンクも貼っておく。 checkstyle/google_checks.xml at master · checkstyle/checkstyle

困ること

日本人なので、JavaDocのコメントは「。」終わりにしたいのに、「.」で終わらないとダメだよと怒られる。

エラーメッセージ:Javadocの最初の文に末尾のピリオドがありません。

対処方法

注意:checkStyle バージョン8.43の時の対処法なので、バージョンアップで設定方法が変わる可能性はある。

SummaryJavadocのプロパティ、periodを定義する。

<module name="SummaryJavadoc">
  <property name="forbiddenSummaryFragments"
           value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
  <property name="period" value="。"/>   <!-- この行を追加 -->      
</module>

参考

SummaryJavadocCheck (checkstyle 8.43 API)

少し在宅勤務を経験した思い出

これは何

ちょっとだけ在宅勤務を経験したのであとから振り返れるように。

前提

  • ブログ主は聴覚障害があるので音声コミュニケーションが不可能。
  • ブログ主は関東圏に住んでいる。
  • 期間限定だろうなと察していたので在宅勤務用に新たに何か買うということはしなかった。(家に元々あった環境で対応した)

在宅勤務の期間について

緊急事態宣言が発令されたことに伴い、 2021/1/12~2021/3/2までの基本、月曜日と火曜日が在宅勤務になった。 水曜日~金曜日は内部/外部打ち合わせがあったので会社に出勤していた。

出勤時も時差勤務は認められていたので、9:00~17:30が通常勤務時間帯のところを、8:30~17:00勤務にした。

緊急事態宣言が明けることで、在宅勤務も終了というお達しが出た。 (これを書いているのが2/26なのでまだ明けていないが、今の状況だと2021/3/7で終了でしょ?)

在宅勤務をやってみて思ったこと

よかった点

  • 始業時間ギリギリまで家のことが出来るのは楽だった。
  • 犬を飼っているので、朝の散歩に行けたのはちょっとした運動になってよかった。
    • 暗くなるのが早いので、2回目の散歩は日中に妻に行ってもらった。
  • 子どもが学校から帰ってきたときに、「おかえり」と言えたのは嬉しかった。
  • 終業時間になったらすぐにOFFモードになれるのは楽。

これは厳しいかなという点

対面で相談したいときに困る

今の業務ではSlackを使っているのでテキストコミュニケーションのため、さほど困らなかったが、 もし、プログラミングしていて、プログラムについて相談したいときに対面でないと相談し辛いことがあるかもしれない。

聞こえる人同士なら、画面を共有して、ボイスチャットとかできるのかもしれないが、その辺はよくわからない。

運動不足になる

歩数を確認したところ、通勤って意外とエネルギーを使っているのだなということがはっきりした。 ちょっと寒い時期だったので仕事後にウォーキングやランニングをする気力がわかなかったが、暖かい時期なら 仕事後に少し運動すべきかもしれない。

通勤時間帯のスイッチの切り替えが出来ない

片道1時間半以上かけて通勤しているので、通勤時間帯にスマホゲームをしたりTwitterを読んだりYahooニュースを見たりして なんとなくスイッチを切り替えていた。

在宅だと、この時間が無くなるのでスイッチの切り替え時間がなくなる、というのはある。 仕事に行き詰まる時期ではなかったのでさほど支障はなかったが、仕事に行き詰まっている時はこの気分転換の時間は必要だろうなと思う。

まとめると

在宅勤務のいいところ、会社に出勤のいいところ、両方それぞれ存在する。

社員が自分で選択できる勤務体制になるのがいいのだろうな、と思う。

単純なSpring Applicationで、コマンドラインから動かしたい処理を指定する方法

これは何

単純なSpring Applicationで、コマンドラインから動かしたい処理を指定する方法のメモ。

やりたいこと

  • Spring Batchではない、通常のコマンドラインアプリケーション。
  • Spring資源を使いたい。
  • 起動する処理は引数で指定したい。

方法

  • SpringApplication.runの戻り値でApplicationContextが取得できる。
  • ApplicationContext.getBean(bean名称)で動かしたいBeanが取得できる。

ソース

Gist多田さんに教えてもらった記述をほぼそのままコピペしている。

Componentの名前を指定しない時

package ta.su.simple;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);

    //context.containsBean(bean名称) でそのBeanが存在するか事前にチェックが出来る。
        if(!context.containsBean("cBean")) {
            throw new IllegalArgumentException("不正なBean名を指定した");
        }

    //Bean名称はクラス名称と同一になる。
        Bean bean = context.getBean("ABean", Bean.class);
        bean.run();
    }

}

interface Bean {
    public void run();
}

@Component
class ABean implements Bean {

    @Override
    public void run() {
        System.out.println("aBeanが呼ばれた");
    }
}

@Component
class BBean implements Bean {

    @Override
    public void run() {
        System.out.println("bBeanが呼ばれた");
    }
}

Componentの名前を指定する時

package ta.su.simple;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        
        if(!context.containsBean("cBean")) {
            throw new IllegalArgumentException("不正なBean名を指定した");
        }
        
    //Bean名称はComponentアノテーションで指定した名称になる。
        Bean bean = context.getBean("aBean", Bean.class);
        bean.run();
    }

}

interface Bean {
    public void run();
}

@Component("aBean")
class ABean implements Bean {

    @Override
    public void run() {
        System.out.println("aBeanが呼ばれた");
    }
}

@Component("bBean")
class BBean implements Bean {

    @Override
    public void run() {
        System.out.println("bBeanが呼ばれた");
    }
}

COVID-19のワクチン接種でマイナンバーはどう使われる想定なのか?

ニュースや河野大臣のtweetを見て、こういう感じなのかなとざっくりフローを作ってみた。

処理順は省いてしまった。書いたほうがよかったか…?

f:id:su_zu_ki_1010:20210127083422p:plain
ワクチン接種フロー

接種実績登録や接種実績照会のキーとしてマイナンバーを使うことを想定されているのだろうと思う。 マイナンバーは重複して割り振られることは無いので、登録漏れが無ければ確実に結果が分かるはず。

plantumlのソースコードは以下。

@startuml

actor 自治体A
actor 自治体B
actor 住民
actor 接種会場
database 専用システム
database マイナンバー

自治体A --> (クーポン券発行)
(クーポン券発行) <-- (専用システム)

(専用システム) <- (マイナンバー)

自治体A --> (クーポン券支給)
(クーポン券支給) --> (住民)

(住民) --> (ワクチン接種):クーポンを\n提示
(ワクチン接種) --> (接種会場)
(接種会場) -> (接種報告):クーポンを\n提出?
(接種報告) -> (自治体A)

(自治体A) --> (接種実績登録):この人は\n接種済と登録
(接種実績登録) --> (専用システム)

(自治体A) --> (接種実績照会)
(接種実績照会) --> (専用システム)

(住民) -> (転居)
(転居) -> (自治体B)

(自治体B)-> (接種実績照会)

note bottom of (自治体B)
転居してきた住民が
接種済か確認する。
end note

@enduml

「ユーザのための要件定義ガイド 第2版 要件定義を成功に導く128の勘どころ」を読んだ

これは何

要件定義フェーズに関わりそうなので、要件定義について調べようと検索したらヒットした ユーザのための要件定義ガイド 第2版 要件定義を成功に導く128の勘どころ:IPA 独立行政法人 情報処理推進機構を読んだので感想をつらつらと書いておく。

独立行政法人情報処理推進機構IPA)社会基盤センターが2019年12月20日にに発行したもので、 書籍版とPDF版があるが、内容は同じ。書籍版は2500円するが、PDF版は無料で読める。

全部で498ページあり、PDF版は14.6MBある。

ユーザーのための、と書いてあるが、ITベンダも読んで理解すべき内容が多く載っていた。

構成

章立ては以下の通り。

  • はじめに
  • 第1章 背景
  • 第2章 要件定義の問題認識
  • 第3章 要件定義の全体像
  • 第4章 ビジネス要求定義(BR)における問題と解決の勘どころ
  • 第5章 システム化要求定義(SR)における問題と解決の勘どころ
  • 第6章 要件定義マネジメント(RM)における問題と解決の勘どころ
  • 第7章 要件定義の主要ドキュメント作成(DD)の勘どころ

はじめに

最後の方に、章ごとに想定している読者層の表が載っている。自分がどの層に当てはまるのか確認すると 自分が特に重視して読むべき章はどれかが分かる。

自分はITベンダという立場で読んだ。

第1章 背景

現状のシステム開発の背景が載っている章。 特に関心が無ければ、流し読みでいいかな、と思う。

第2章 要件定義の問題認識

要件定義を行うにあたって、それぞれの立場で意識しないといけないことが書かれている。 この章はある程度しっかり読みこむことで、要件定義を行う際に自分が意識しないといけないことを改めて認識出来る。

一度でも要件定義をしたことがある人は、前回の要件定義でここがダメだったな、という振り返りにもなる。

第3章 要件定義の全体像

要件定義の全体像がまとめられている。自分がこれからやること、作るものについてざっくり把握するのに役立つ。

第4章 ビジネス要求定義(BR)における問題と解決の勘どころ

ITベンダの立場で読むと、お客様に何をやっていただきたいか、何をお願いしたほうがいいのか、を整理できる。 お客様の話を聞いていて、なんかはっきりしないな?と思った時に確認すべき事項が載っている、ともいえる。

自分はこの章は真剣に読んだ。

第5章 システム化要求定義(SR)における問題と解決の勘どころ

お客様の要望をシステム化するにあたって何を考える必要があるのか、を整理できる。

自分はこの章は真剣に読んだ。

特に5.1 システム化要求の仕様化 の章に書いてある内容は読んでいて 自分が今までに書いたドキュメントについてとても反省した。

第6章 要件定義マネジメント(RM)における問題と解決の勘どころ

この章はITベンダ側のプロジェクトマネージャという立場で読むときは重要だと思う。 でも、ITベンダの一介の設計担当として読む分には、あまり重要ではないかなと感じた。 (意識しすぎると却って疲れてしまうので、この章の部分はマネジメント担当にお願いしたほうが楽)

第7章 要件定義の主要ドキュメント作成(DD)の勘どころ

要件定義を行い、最終的にどのようなシステムにするのかを決めるにはドキュメント作成は避けて通れない。 後々困らないためのドキュメントとはどういうものなのか、がこの章にまとまっている。

作る必要があるドキュメントの量が多くて気が滅入りそうになる。 でも、各ドキュメントのサンプルも載っているのでイメージはしやすい。

自分がこれからどんなドキュメントを作るのか、を改めて意識することが出来た。

まとめ

要件定義という一連の作業についてざっくり把握する入門のドキュメントとしては非常にお勧め出来る。 ただし、一気に読むのは疲れてしまうので、他の技術文書同様に段階を踏んで読むのがいい。

  1. 全体をざっと流し読みして自分に特に関係ありそうな章をまず把握する。
  2. 自分に関係ありそうな章を読み、わからないキーワードをメモする。
  3. わからないキーワードを調べて意味を理解して、再度関係ありそうな章を読む。

2と3を繰り返すことで理解が深まると思うし、その上で詳細をもっと勉強したくなったら 詳細が知りたいキーワードで検索して出てきたページや本を読む、という流れがよさそう。

LocalStack + Spring Boot でAWS SQSの開発環境を構築する際のメモ

これは何

動くようになるまでにドはまりしたのであとあと確認出来るようにメモ

前提

関係ありそうなバージョン情報は以下の通り

  • Java:11
  • Spring Boot:2.3.7
  • Spring Cloud:Hoxton.SR9
  • aws-java-sdk-core:1.11.792

AWS SDKはVersion2が出ているようだが、Spring Boot側がそれに対応したバージョンがまだリリースされていない模様。

build.gradleの定義

spring initializr でweb、aws core、aws simple queue serviceを選択している。

plugins {
    id 'org.springframework.boot' version '2.3.7.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
    mavenCentral()
}

ext {
    set('springCloudVersion', "Hoxton.SR9")
}

dependencies {

    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.cloud:spring-cloud-starter-aws'
    implementation 'org.springframework.cloud:spring-cloud-starter-aws-messaging'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

test {
    useJUnitPlatform()
}

依存関係

compileClasspathのみ記載。

compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-starter-web -> 2.3.7.RELEASE
|    +--- org.springframework.boot:spring-boot-starter:2.3.7.RELEASE
|    |    +--- org.springframework.boot:spring-boot:2.3.7.RELEASE
|    |    |    +--- org.springframework:spring-core:5.2.12.RELEASE
|    |    |    |    \--- org.springframework:spring-jcl:5.2.12.RELEASE
|    |    |    \--- org.springframework:spring-context:5.2.12.RELEASE
|    |    |         +--- org.springframework:spring-aop:5.2.12.RELEASE
|    |    |         |    +--- org.springframework:spring-beans:5.2.12.RELEASE
|    |    |         |    |    \--- org.springframework:spring-core:5.2.12.RELEASE (*)
|    |    |         |    \--- org.springframework:spring-core:5.2.12.RELEASE (*)
|    |    |         +--- org.springframework:spring-beans:5.2.12.RELEASE (*)
|    |    |         +--- org.springframework:spring-core:5.2.12.RELEASE (*)
|    |    |         \--- org.springframework:spring-expression:5.2.12.RELEASE
|    |    |              \--- org.springframework:spring-core:5.2.12.RELEASE (*)
|    |    +--- org.springframework.boot:spring-boot-autoconfigure:2.3.7.RELEASE
|    |    |    \--- org.springframework.boot:spring-boot:2.3.7.RELEASE (*)
|    |    +--- org.springframework.boot:spring-boot-starter-logging:2.3.7.RELEASE
|    |    |    +--- ch.qos.logback:logback-classic:1.2.3
|    |    |    |    +--- ch.qos.logback:logback-core:1.2.3
|    |    |    |    \--- org.slf4j:slf4j-api:1.7.25 -> 1.7.30
|    |    |    +--- org.apache.logging.log4j:log4j-to-slf4j:2.13.3
|    |    |    |    +--- org.slf4j:slf4j-api:1.7.25 -> 1.7.30
|    |    |    |    \--- org.apache.logging.log4j:log4j-api:2.13.3
|    |    |    \--- org.slf4j:jul-to-slf4j:1.7.30
|    |    |         \--- org.slf4j:slf4j-api:1.7.30
|    |    +--- jakarta.annotation:jakarta.annotation-api:1.3.5
|    |    +--- org.springframework:spring-core:5.2.12.RELEASE (*)
|    |    \--- org.yaml:snakeyaml:1.26
|    +--- org.springframework.boot:spring-boot-starter-json:2.3.7.RELEASE
|    |    +--- org.springframework.boot:spring-boot-starter:2.3.7.RELEASE (*)
|    |    +--- org.springframework:spring-web:5.2.12.RELEASE
|    |    |    +--- org.springframework:spring-beans:5.2.12.RELEASE (*)
|    |    |    \--- org.springframework:spring-core:5.2.12.RELEASE (*)
|    |    +--- com.fasterxml.jackson.core:jackson-databind:2.11.3
|    |    |    +--- com.fasterxml.jackson.core:jackson-annotations:2.11.3
|    |    |    \--- com.fasterxml.jackson.core:jackson-core:2.11.3
|    |    +--- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.3
|    |    |    +--- com.fasterxml.jackson.core:jackson-core:2.11.3
|    |    |    \--- com.fasterxml.jackson.core:jackson-databind:2.11.3 (*)
|    |    +--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.3
|    |    |    +--- com.fasterxml.jackson.core:jackson-annotations:2.11.3
|    |    |    +--- com.fasterxml.jackson.core:jackson-core:2.11.3
|    |    |    \--- com.fasterxml.jackson.core:jackson-databind:2.11.3 (*)
|    |    \--- com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.3
|    |         +--- com.fasterxml.jackson.core:jackson-core:2.11.3
|    |         \--- com.fasterxml.jackson.core:jackson-databind:2.11.3 (*)
|    +--- org.springframework.boot:spring-boot-starter-tomcat:2.3.7.RELEASE
|    |    +--- jakarta.annotation:jakarta.annotation-api:1.3.5
|    |    +--- org.apache.tomcat.embed:tomcat-embed-core:9.0.41
|    |    +--- org.glassfish:jakarta.el:3.0.3
|    |    \--- org.apache.tomcat.embed:tomcat-embed-websocket:9.0.41
|    |         \--- org.apache.tomcat.embed:tomcat-embed-core:9.0.41
|    +--- org.springframework:spring-web:5.2.12.RELEASE (*)
|    \--- org.springframework:spring-webmvc:5.2.12.RELEASE
|         +--- org.springframework:spring-aop:5.2.12.RELEASE (*)
|         +--- org.springframework:spring-beans:5.2.12.RELEASE (*)
|         +--- org.springframework:spring-context:5.2.12.RELEASE (*)
|         +--- org.springframework:spring-core:5.2.12.RELEASE (*)
|         +--- org.springframework:spring-expression:5.2.12.RELEASE (*)
|         \--- org.springframework:spring-web:5.2.12.RELEASE (*)
+--- org.springframework.cloud:spring-cloud-starter-aws -> 2.2.5.RELEASE
|    +--- org.springframework.cloud:spring-cloud-aws-context:2.2.5.RELEASE
|    |    +--- org.springframework.cloud:spring-cloud-aws-core:2.2.5.RELEASE
|    |    |    +--- org.springframework:spring-beans:5.2.8.RELEASE -> 5.2.12.RELEASE (*)
|    |    |    +--- org.springframework:spring-aop:5.2.8.RELEASE -> 5.2.12.RELEASE (*)
|    |    |    +--- com.amazonaws:aws-java-sdk-core:1.11.792
|    |    |    |    +--- org.apache.httpcomponents:httpclient:4.5.9 -> 4.5.13
|    |    |    |    |    +--- org.apache.httpcomponents:httpcore:4.4.13 -> 4.4.14
|    |    |    |    |    \--- commons-codec:commons-codec:1.11 -> 1.14
|    |    |    |    +--- software.amazon.ion:ion-java:1.0.2
|    |    |    |    +--- com.fasterxml.jackson.core:jackson-databind:2.6.7.3 -> 2.11.3 (*)
|    |    |    |    +--- com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.6.7 -> 2.11.3
|    |    |    |    |    +--- com.fasterxml.jackson.core:jackson-databind:2.11.3 (*)
|    |    |    |    |    \--- com.fasterxml.jackson.core:jackson-core:2.11.3
|    |    |    |    \--- joda-time:joda-time:2.8.1
|    |    |    +--- com.amazonaws:aws-java-sdk-s3:1.11.792
|    |    |    |    +--- com.amazonaws:aws-java-sdk-kms:1.11.792
|    |    |    |    |    +--- com.amazonaws:aws-java-sdk-core:1.11.792 (*)
|    |    |    |    |    \--- com.amazonaws:jmespath-java:1.11.792
|    |    |    |    |         \--- com.fasterxml.jackson.core:jackson-databind:2.6.7.3 -> 2.11.3 (*)
|    |    |    |    +--- com.amazonaws:aws-java-sdk-core:1.11.792 (*)
|    |    |    |    \--- com.amazonaws:jmespath-java:1.11.792 (*)
|    |    |    +--- com.amazonaws:aws-java-sdk-ec2:1.11.792
|    |    |    |    +--- com.amazonaws:aws-java-sdk-core:1.11.792 (*)
|    |    |    |    \--- com.amazonaws:jmespath-java:1.11.792 (*)
|    |    |    +--- com.amazonaws:aws-java-sdk-cloudformation:1.11.792
|    |    |    |    +--- com.amazonaws:aws-java-sdk-core:1.11.792 (*)
|    |    |    |    \--- com.amazonaws:jmespath-java:1.11.792 (*)
|    |    |    \--- org.slf4j:slf4j-api:1.7.30
|    |    +--- org.springframework:spring-context:5.2.8.RELEASE -> 5.2.12.RELEASE (*)
|    |    \--- org.slf4j:slf4j-api:1.7.30
|    +--- org.springframework.cloud:spring-cloud-aws-autoconfigure:2.2.5.RELEASE
|    |    +--- org.springframework.cloud:spring-cloud-aws-context:2.2.5.RELEASE (*)
|    |    +--- org.springframework.boot:spring-boot-autoconfigure:2.3.2.RELEASE -> 2.3.7.RELEASE (*)
|    |    \--- org.slf4j:slf4j-api:1.7.30
|    \--- org.slf4j:slf4j-api:1.7.30
\--- org.springframework.cloud:spring-cloud-starter-aws-messaging -> 2.2.5.RELEASE
     +--- org.springframework.cloud:spring-cloud-starter-aws:2.2.5.RELEASE (*)
     +--- org.springframework.cloud:spring-cloud-aws-messaging:2.2.5.RELEASE
     |    +--- org.springframework.cloud:spring-cloud-aws-context:2.2.5.RELEASE (*)
     |    +--- com.amazonaws:aws-java-sdk-sns:1.11.792
     |    |    +--- com.amazonaws:aws-java-sdk-sqs:1.11.792
     |    |    |    +--- com.amazonaws:aws-java-sdk-core:1.11.792 (*)
     |    |    |    \--- com.amazonaws:jmespath-java:1.11.792 (*)
     |    |    +--- com.amazonaws:aws-java-sdk-core:1.11.792 (*)
     |    |    \--- com.amazonaws:jmespath-java:1.11.792 (*)
     |    +--- com.amazonaws:aws-java-sdk-sqs:1.11.792 (*)
     |    +--- org.springframework:spring-messaging:5.2.8.RELEASE -> 5.2.12.RELEASE
     |    |    +--- org.springframework:spring-beans:5.2.12.RELEASE (*)
     |    |    \--- org.springframework:spring-core:5.2.12.RELEASE (*)
     |    \--- org.slf4j:slf4j-api:1.7.30
     \--- org.slf4j:slf4j-api:1.7.30

application.propertiesの定義

AWSにアクセスするのではなく、開発端末でLocalStackを使って疑似サーバーを立ててテストするための設定になっている。

cloud.aws.stack.auto=false
cloud.aws.region.auto=false
cloud.aws.region.static=ap-northeast-1
cloud.aws.credentials.access-key=dummy
cloud.aws.credentials.secret-key=dummy

logging.level.com.amazonaws.util.EC2MetadataUtils=error

aws.endpoint=http://localhost:4566

LocalStackについて

localstack/localstack: 💻 A fully functional local AWS cloud stack. Develop and test your cloud & Serverless apps offline!

  • AWSだと課金が怖いので、まずは開発環境で色々試すためのツール。
  • Dockerで動かす。
  • 今回はWindows 10 Pro の端末上にDockerをインストールして、そのうえで動かした。

docker-compose.yml の内容

初期値のままでも大丈夫だったかもしれないけど、とりあえず動いたときのものを貼り付けておく。

version: '2.1'

services:
  localstack:
    container_name: "${LOCALSTACK_DOCKER_NAME-localstack_main}"
    image: localstack/localstack
    network_mode: bridge
    ports:
      - "4566:4566"
      - "4571:4571"
      - "18080:18080"
    environment:
      - SERVICES=${SERVICES- }
      - DEBUG=${DEBUG- }
      - DATA_DIR=${DATA_DIR- }
      - PORT_WEB_UI=${PORT_WEB_UI- }
      - LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR- }
      - KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
      - DOCKER_HOST=unix:///var/run/docker.sock
      - HOST_TMP_FOLDER=${TMPDIR}
      - LAMBDA_DOCKER_NETWORK=host
      - AWS_CBOR_DISABLE=true
      - USE_SSL=false
      - AWS_ACCESS_KEY_ID=dummy
      - AWS_SECRET_ACCESS_KEY=dummy
    volumes:
      - "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"

コマンド集

キューを作成

aws --region ap-northeast-1 --endpoint-url http://localhost:4566 sqs create-queue --queue-name 'foo-queue'

メッセージ送信

aws --region ap-northeast-1 --endpoint-url http://localhost:4566 sqs send-message --queue-url 'http://localhost:4566/000000000000/foo-queue' --message-body 'hogehoge'

動くようになるまでに悩まされたものその1

アプリ起動時にcom.amazonaws.SdkClientException: Failed to connect to service endpoint:エラーが出る

2020-12-15 15:08:00.739  WARN 18360 --- [           main] i.InstanceMetadataServiceResourceFetcher : Fail to retrieve token 

com.amazonaws.SdkClientException: Failed to connect to service endpoint: 
    at com.amazonaws.internal.EC2ResourceFetcher.doReadResource(EC2ResourceFetcher.java:100) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.internal.InstanceMetadataServiceResourceFetcher.getToken(InstanceMetadataServiceResourceFetcher.java:91) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.internal.InstanceMetadataServiceResourceFetcher.readResource(InstanceMetadataServiceResourceFetcher.java:69) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.internal.EC2ResourceFetcher.readResource(EC2ResourceFetcher.java:66) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.util.EC2MetadataUtils.getItems(EC2MetadataUtils.java:402) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.util.EC2MetadataUtils.getData(EC2MetadataUtils.java:371) ~[aws-java-sdk-core-1.11.792.jar:na]
    at org.springframework.cloud.aws.context.support.env.AwsCloudEnvironmentCheckUtils.isRunningOnCloudEnvironment(AwsCloudEnvironmentCheckUtils.java:38) ~[spring-cloud-aws-context-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.cloud.aws.context.annotation.OnAwsCloudEnvironmentCondition.matches(OnAwsCloudEnvironmentCondition.java:38) ~[spring-cloud-aws-context-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:108) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader$TrackedConditionEvaluator.shouldSkip(ConfigurationClassBeanDefinitionReader.java:477) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:131) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:331) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:236) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:280) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:96) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:707) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:533) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:405) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at com.example.demo.AaaaApplication.main(AaaaApplication.java:21) ~[main/:na]
Caused by: java.net.SocketException: Network is unreachable: connect
    at java.base/java.net.PlainSocketImpl.waitForConnect(Native Method) ~[na:na]
    at java.base/java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:107) ~[na:na]
    at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:399) ~[na:na]
    at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:242) ~[na:na]
    at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:224) ~[na:na]
    at java.base/java.net.Socket.connect(Socket.java:609) ~[na:na]
    at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:177) ~[na:na]
    at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:474) ~[na:na]
    at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:569) ~[na:na]
    at java.base/sun.net.www.http.HttpClient.<init>(HttpClient.java:242) ~[na:na]
    at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:341) ~[na:na]
    at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:362) ~[na:na]
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1253) ~[na:na]
    at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1232) ~[na:na]
    at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1081) ~[na:na]
    at java.base/sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:1015) ~[na:na]
    at com.amazonaws.internal.ConnectionUtils.connectToEndpoint(ConnectionUtils.java:52) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.internal.EC2ResourceFetcher.doReadResource(EC2ResourceFetcher.java:80) ~[aws-java-sdk-core-1.11.792.jar:na]
    ... 25 common frames omitted

検索しまくったところ、EC2 metadata resolution related exception thrown when running application locally · Issue #556 · spring-cloud/spring-cloud-aws を見つけた。

EC2 metadata resolution related exception thrown when running application locally · Issue #556 · spring-cloud/spring-cloud-aws によると、AWS SDK v1 (checked with 1.11.791)からこのエラーが出るようになったらしい。

application.propertiosに以下の記述を追加して回避する。

logging.level.com.amazonaws.util.EC2MetadataUtils=error

動くようになるまでに悩まされたものその2

トークンの認証なんたらとエラーが出る。

2020-12-15 15:23:48.243 ERROR 11644 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'simpleMessageListenerContainer' defined in class path resource [org/springframework/cloud/aws/messaging/config/annotation/SqsConfiguration.class]: Invocation of init method failed; nested exception is com.amazonaws.services.sqs.model.AmazonSQSException: The security token included in the request is invalid. (Service: AmazonSQS; Status Code: 403; Error Code: InvalidClientTokenId; Request ID: 806c2625-65f0-56f0-95a0-7e773d023d56; Proxy: null)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1794) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:897) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:405) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
    at com.example.demo.AaaaApplication.main(AaaaApplication.java:21) ~[main/:na]
Caused by: com.amazonaws.services.sqs.model.AmazonSQSException: The security token included in the request is invalid. (Service: AmazonSQS; Status Code: 403; Error Code: InvalidClientTokenId; Request ID: 806c2625-65f0-56f0-95a0-7e773d023d56; Proxy: null)
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleErrorResponse(AmazonHttpClient.java:1811) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleServiceErrorResponse(AmazonHttpClient.java:1395) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeOneRequest(AmazonHttpClient.java:1371) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeHelper(AmazonHttpClient.java:1145) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.doExecute(AmazonHttpClient.java:802) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeWithTimer(AmazonHttpClient.java:770) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.execute(AmazonHttpClient.java:744) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutor.access$500(AmazonHttpClient.java:704) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient$RequestExecutionBuilderImpl.execute(AmazonHttpClient.java:686) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:550) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:530) ~[aws-java-sdk-core-1.11.792.jar:na]
    at com.amazonaws.services.sqs.AmazonSQSClient.doInvoke(AmazonSQSClient.java:2207) ~[aws-java-sdk-sqs-1.11.792.jar:na]
    at com.amazonaws.services.sqs.AmazonSQSClient.invoke(AmazonSQSClient.java:2174) ~[aws-java-sdk-sqs-1.11.792.jar:na]
    at com.amazonaws.services.sqs.AmazonSQSClient.invoke(AmazonSQSClient.java:2163) ~[aws-java-sdk-sqs-1.11.792.jar:na]
    at com.amazonaws.services.sqs.AmazonSQSClient.executeGetQueueUrl(AmazonSQSClient.java:1201) ~[aws-java-sdk-sqs-1.11.792.jar:na]
    at com.amazonaws.services.sqs.AmazonSQSClient.getQueueUrl(AmazonSQSClient.java:1173) ~[aws-java-sdk-sqs-1.11.792.jar:na]
    at org.springframework.cloud.aws.messaging.support.destination.DynamicQueueUrlDestinationResolver.resolveDestination(DynamicQueueUrlDestinationResolver.java:94) ~[spring-cloud-aws-messaging-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.cloud.aws.messaging.support.destination.DynamicQueueUrlDestinationResolver.resolveDestination(DynamicQueueUrlDestinationResolver.java:38) ~[spring-cloud-aws-messaging-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.messaging.core.CachingDestinationResolverProxy.resolveDestination(CachingDestinationResolverProxy.java:92) ~[spring-messaging-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.cloud.aws.messaging.listener.AbstractMessageListenerContainer.queueAttributes(AbstractMessageListenerContainer.java:321) ~[spring-cloud-aws-messaging-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.cloud.aws.messaging.listener.AbstractMessageListenerContainer.initialize(AbstractMessageListenerContainer.java:293) ~[spring-cloud-aws-messaging-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer.initialize(SimpleMessageListenerContainer.java:112) ~[spring-cloud-aws-messaging-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.cloud.aws.messaging.listener.AbstractMessageListenerContainer.afterPropertiesSet(AbstractMessageListenerContainer.java:268) ~[spring-cloud-aws-messaging-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer.afterPropertiesSet(SimpleMessageListenerContainer.java:46) ~[spring-cloud-aws-messaging-2.2.5.RELEASE.jar:2.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1853) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1790) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
    ... 17 common frames omitted

このエラーが出たときのコードは以下の通り。

package com.example.demo.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.sqs.AmazonSQSAsync;
import com.amazonaws.services.sqs.AmazonSQSAsyncClientBuilder;

@Configuration
public class AwsSQSConfig {

    Logger logger = LoggerFactory.getLogger(AwsSQSConfig.class);

    @Value("${cloud.aws.region.static}")
    private String region;

    @Value("${cloud.aws.credentials.access-key}")
    private String awsAccessKey;

    @Value("${cloud.aws.credentials.secret-key}")
    private String awsSecretKey;

    @Value("${aws.endpoint}")
    private String endPoint;

    @Bean
    public QueueMessagingTemplate queueMessagingTemplate() {
        return new QueueMessagingTemplate(amazonSQSAsync());
    }

    @Primary
    @Bean
    public AmazonSQSAsync amazonSQSAsync() {

        logger.info("region:{}, awsAccessKey:{}, awsSecretKey:{}", region, awsAccessKey, awsSecretKey);

        AmazonSQSAsync async = AmazonSQSAsyncClientBuilder.standard()
                .withRegion(Regions.AP_NORTHEAST_1)
                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(awsAccessKey, awsSecretKey)))
                //.withEndpointConfiguration(new EndpointConfiguration(endPoint, region))
                .build();
        
        return async;
    }

}

デバッグで追いかけたところ、エンドポイントのURLがlocalhostになっていなかった。(指定していないから当たり前) 以下のように修正してみる。

   @Primary
    @Bean
    public AmazonSQSAsync amazonSQSAsync() {

        logger.info("region:{}, awsAccessKey:{}, awsSecretKey:{}", region, awsAccessKey, awsSecretKey);

        AmazonSQSAsync async = AmazonSQSAsyncClientBuilder.standard()
                .withRegion(Regions.AP_NORTHEAST_1)
                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(awsAccessKey, awsSecretKey)))
                .withEndpointConfiguration(new EndpointConfiguration(endPoint, region))
                .build();
        
        return async;
    }

今度は以下のエラーが出る。

Caused by: java.lang.IllegalStateException: Only one of Region or EndpointConfiguration may be set.
    at com.amazonaws.client.builder.AwsClientBuilder.setRegion(AwsClientBuilder.java:450) ~[aws-java-sdk-core-1.11.792.jar:na]

リージョンは複数セットできないよ、ということらしい。(それはそう)

‘withRegion()`の部分を削除することで正常に立ち上がった。

   @Primary
    @Bean
    public AmazonSQSAsync amazonSQSAsync() {

        logger.info("region:{}, awsAccessKey:{}, awsSecretKey:{}", region, awsAccessKey, awsSecretKey);

        AmazonSQSAsync async = AmazonSQSAsyncClientBuilder.standard()
                //.withRegion(Regions.AP_NORTHEAST_1)
                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(awsAccessKey, awsSecretKey)))
                .withEndpointConfiguration(new EndpointConfiguration(endPoint, region))
                .build();
        
        return async;
    }

SQSにプットされたら自動で受信処理が動くようにする

org.springframework.cloud.aws.messaging.listener.annotation.SqsListener を使う。

package com.example.demo.listener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.aws.messaging.listener.annotation.SqsListener;
import org.springframework.stereotype.Component;

@Component
public class AwsSQSListener {

    Logger logger= LoggerFactory.getLogger(AwsSQSListener.class);

    @SqsListener("foo-queue")
    public void loadMessageFromSQS(String message)  {
        logger.info("message from SQS Queue {}",message);
    }
}

aws lambdaを使うより、常駐のSpringアプリケーションを一つ用意して、このアプリ内でリスナーを立ち上げたほうが色々やりやすそうな感じを受けた。

Gitlabの変更内容をSlackに通知する方法

概要

Gitlabで変更があった時(issueを新規起票、issueにコメントがついた、など)にSlackに通知を飛ばすための手順メモ。

検索すると手順が載っているページがいくつかヒットするが、Slack側のUI変更が入っていてちょっと手順が違ったので2020/11/12時点の手順を載せておく。

Slackの設定

まず、Slack側で通知を受け取れるようにするための設定を行う。

SlackAPIにアクセス

Slack API | Slack にアクセスする。

Webhook設定画面を開く

Messaging > Managing messages の下にある、Using Webhooksをクリックする。

f:id:su_zu_ki_1010:20201112154422p:plain

新しくSlackアプリを作成する

Getting started with Incoming Webhooksの下に、Create your Slack appボタンがあるのでクリックする。

f:id:su_zu_ki_1010:20201112154438p:plain

すると、新規アプリの名前を入れる欄と、登録するSlackワークスペースを選択する画面に遷移する。

f:id:su_zu_ki_1010:20201112154451p:plain

実際に通知を飛ばすチャンネルを選択する

Slackアプリの作成ができると、Building Apps for Slack画面に遷移するので、 Incoming Webhooksをクリックする。

f:id:su_zu_ki_1010:20201112154509p:plain

遷移先の画面で、Activate Incoming WebhooksOffになっているのでOnに変更する。

f:id:su_zu_ki_1010:20201112154526p:plain

Onにすると、表示が切り替わるので下にスクロールする。

f:id:su_zu_ki_1010:20201112154542p:plain

Webhook URLs for Your Workspaceの欄があるが、初期状態だとURLが設定されていない。 Add New Webhook to Workspaceボタンをクリックする。

f:id:su_zu_ki_1010:20201112154600p:plain

通知を飛ばすチャンネル選択の画面になるので、通知を飛ばしたいチャンネルを選択する。 Gitlabを頻繁に更新すると、通知がどんどん届くので通知専用のチャンネルを用意するをお勧めする。

f:id:su_zu_ki_1010:20201112154615p:plain

Allowボタンをクリックで、専用のURLが割り振られる。 このURLを後述する、Gitlab側の設定画面に設定することでGitlabで変更があるとSlackに通知が飛ぶようになる。

f:id:su_zu_ki_1010:20201112154629p:plain

Slackに通知が飛ぶことの確認

Sample curl request to post to a channelに記載されているコマンドを実行すると通知が飛ぶテストが出来る。 しかし、ここに記載されているコマンドはLinux用なのでWindows端末で実行しようとしてもエラーになってしまう。

[slack]Incoming WebhooksのSampleをWindowsで実行時"curl: (6) Could not resolve host: application"エラー | akamist blog

に書いてあるように書き換える必要がある。

curl -X POST -H "Content-type: application/json" --data "{\"text\":\"Hello, World!\"}" https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/xxxxxxxxxxxxxxxxxxxxxxxx
  • スペースを含むパラメータはダブルクォート「"」でくくる。
  • ダブルクォートの中にあるダブルクォートは、バックスラッシュ・円記号「\」でエスケープする

以上でSlack側の設定は完了。次はGitlab側を設定する。

Webhook URLに表示されている値はGitlab側の設定で使うので、このページは閉じないでおくのを推奨する。

誤って閉じてしまった時は、Slack API | Slack にアクセスして、右上にあるYour Appsをクリックする。

f:id:su_zu_ki_1010:20201112154652p:plain

クリックして遷移した画面に、作成したアプリ一覧が表示されるので、Gitlab通知用のアプリをクリックする。

f:id:su_zu_ki_1010:20201112154706p:plain

遷移した画面では、Featuresの下にある、Incoming Webhooksをクリックする。

f:id:su_zu_ki_1010:20201112154723p:plain

すると、Incoming Webhooks設定画面に遷移するので、下にスクロールするとWebhook用のURLが確認出来る。

Gitlab側の設定

Gitlab側で変更が発生したときに、Slackに通知を飛ばすための設定をする。

Slack通知をする設定画面を開く

Gitlabのプロジェクトページを開き、左側にあるSetting > Integrationsをクリックする。

遷移した画面の下の方にSlack notificationsのリンクがあるので、これをクリックする。

f:id:su_zu_ki_1010:20201112154324p:plain

Slack通知を行う設定をしてテストする

Slack notifications画面に遷移するので、下へスクロールする。

f:id:su_zu_ki_1010:20201112154738p:plain

そうすると、WebhookのURLを入力する欄があるので、先ほどSlackで表示されたURLを貼り付ける。

f:id:su_zu_ki_1010:20201112154751p:plain

Slack API画面のCopyボタンを押すとURLがクリップボードにコピーされるので、これを貼り付ければよい。

f:id:su_zu_ki_1010:20201112154805p:plain

URLを貼り付けたら、Save Changesボタンを押して一度保存したあと、Test settingsボタンを押して通知が飛ぶことを確認する。

正しく設定出来ていれば、設定したSlackのチャンネルに通知が飛ぶ。

以上で設定完了。