何?Swaggerって?

お仕事で「APIの仕様書をすわっがーで作ってね」って言われました、既存の仕様書はYAMLファイルになっているのですが・・・何?Swaggerって?

Swaggerは、OpenAPI仕様に基づいてREST APIの仕様書作成から構築を助けてくれるツールです。

Swagger は RESTful APIを構築するためのオープンソースフレームワークのことです。「Open API Initiative」という団体がRESTful APIインターフェイスの記述をするための標準フォーマットを推進していて、その標準フォーマットがSwaggerです。Swaggerには多くの便利なツールが提供されていることもあり、多くのメリットを享受できそうです。

Swaggerの概要をまとめてみた。 - Qiita

仕様書のフォーマット?

Swaggerは、OpenAPI仕様(以下OAS)と言われる、REST APIを定義するための標準仕様にもとづいて構築された一連のオープンソースツールです。REST APIの設計、構築、文書化、および使用に役立つ機能を提供します。

本当に使ってよかったOpenAPI (Swagger) ツール | フューチャー技術ブログ

フォーマットだけじゃなくて、仕様書から構築までできるらしいです。

OpenAPIは、REST APIの記述フォーマットです。

What Is OpenAPI?

OpenAPI Specification (formerly Swagger Specification) is an API description format for REST APIs.

(省略)

What Is Swagger?

Swagger is a set of open-source tools built around the OpenAPI Specification that can help you design, build, document and consume REST APIs.

About Swagger Specification | Documentation | Swagger - swagger.io

弱い英語力から以下程度に解釈しました。

Swagger3.0は、OpenAPI?

OpenAPI は Swagger 3.0

Swagger 3.0 から OpenAPI に名前が変わったため、 OpenAPI 3.0 は Swagger 3.0 でもあります。

もともと他にも API 周りのインターフェース定義ができるルールが存在してたのですが、 Swagger が晴れて標準となったようです。 他には API Blueprint などがあるようですが、僕もそこまで詳しくはありません。 OpenAPI の仕様の、現時点での最新は 3.0.2 です。 仕様書はこちらにあります。

https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md

OpenAPI (Swagger) の基本的なあれこれ - ばうあーろぐ

Swaggerは、2から3でなにやらずいぶん変わったようですが・・・初めてなのでとにかくSwagger3.0を使えるようになることを優先します。

ブラウザで仕様書を見て書けます。

ツールをインストールしたり環境を構築したりしなくてもすぐに使えそうです。

  1. Swagger Editorをブラウザで表示する
    • f:id:ponsuke_tarou:20210121141523p:plain
  2. [File] > [Import file] > 既存の仕様書をインポートする
    • f:id:ponsuke_tarou:20210121141129p:plain
  3. 仕様書に定義されたインターフェースが横に見やすく表示された!
  4. 左のYMLを修正すると右側に反映される

実行もできます。

呼出し先が起動していれば、必要な情報を入力して実行することもできました。

  1. 仕様書のAPIを起動して接続できるようにする
  2. f:id:ponsuke_tarou:20210121094641p:plain
    このボタンを押下すると入力できるようになる
  3. ヘッダやパラメータなど必須になっている項目を入力する
  4. f:id:ponsuke_tarou:20210121094746p:plain
    このボタンで実行できる
  5. 「TypeError」になったけれどAPIのログを見てみるとちゃんとAPIを呼び出していた
    • f:id:ponsuke_tarou:20210121142535p:plain
      Server responseにAPIからのレスポンスが表示される

GitにあるSwaggerのファイルをプレビューできるChrome拡張機能があるらしいです。

qiita.com

f:id:ponsuke_tarou:20210122223424j:plain
板橋区の梅の湯

Serversを書く

APIのサーバを定義します。 サーバには、本番環境やらテスト環境など複数を定義できます。

認証を書く

試しに「OAuth2.0で取得したBearerのトークンをヘッダにAuthorizationで設定する」みたいに書こうとした・・・ら、なんか言われました。

Header parameters name "Authorization" are ignore. Use the `securitySchemes` and `security` sections instead to define authorization.

f:id:ponsuke_tarou:20210121161236p:plain どうやら、ヘッダやパラメータに「Authorization」を設定しても無視されるようです。

You have used a restricted value as the name of a header parameter. The values Accept, Content-Type, and Authorization are restricted values and should not be used as the header name. A header with any of these values as the header name is ignored.

Header parameter with the name 'Authorization' is ignored - apisecurity.io

では、どう書くのか?調べながら書いてみました。

Bearer スキームを書く

トークンを利用した認証・認可 API を実装するとき Authorization: Bearer ヘッダを使っていいのか調べた - Qiitaを読むとやりたいことは「Bearer スキーム」というものでした。

共通で使うオブジェクトや構造の定義を書く

APIのパラメータやレスポンスで共通のオブジェクトや構造を使うことがあります。 そんな共通のオブジェクトや構造は、components配下に定義して$refを使うことで参照できます。

Components Section

Often, multiple API operations have some common parameters or return the same response structure. To avoid code duplication, you can place the common definitions in the global components section and reference them using $ref.

Components Section - swagger.io

リクエストを書く

各リクエストのエンドポイントは、paths配下に定義していきます。

paths:
  /{エンドポイント}:
    summary: {短い説明文}
    description: >
      {長い説明文、
        ここにはMarkdownで複数行にわたって書くことができます。}

typeに定義するデータ型は、Data Typesに記載されているものを使用します。

type 意味 参考になりそうなサイト
string 文字列
number 整数と浮動小数点付き数値 浮動小数点って何? - Qiita
integer 整数
boolean 真偽値
array 配列
object オブジェクト

パラメータを定義する

Describing Parameters

In OpenAPI 3.0, parameters are defined in the parameters section of an operation or path. To describe a parameter, you specify its name, location (in), data type (defined by either schema or content) and other attributes, such as description or required.

Describing Parameters - swagger.io

パラメータといっても種類があります。基本の書き方は同じで- inに指定する値を変えて書き分けます。各パラメータがどんな感じのものかはSwaggerの上記ドキュメントページにサンプルがあってわかりやすいです。

パラメータの種類 inに指定する値 参考になりそうなサイト
パスパラメータ path
クエリパラメータ query
ヘッダパラメータ header HTTP ヘッダー - HTTP | MDN
Cookieパラメータ cookie

フォーマットはこんな感じです。

parameters:
  - in: pathかqueryかheaderかcookie
    name: パラメータ名やヘッダのフィールド名
    schema:
      type: パラメータの型
      enum:
        - パラメータの値が特定の値しか受付ない場合に指定する
      example: パラメータの例
    required: 必須かどうかをboolで指定、パスパラメータは省略できないのでtrueを指定する
    description: パラメータの説明文
    allowEmptyValue: パラメータ名の指定のみで値がなくてもいいかどうかをbookで指定する

レスポンスを書く

Describing Responses - swagger.ioを参照すると、 各レスポンスは以下のようにresponses配下にHTTPステータスコード毎に定義していくようです。

responses:
  {ステータスコード}:
    description: {説明}
    content:
      {メディアタイプ}:
        schema:
          # ここからレスポンスボディを定義する
          type: {レスポンスボディのタイプ「object」「array」}
          properties:
            {プロパティ名}:
              type: {プロパティのタイプ}
              description: {プロパティの説明}

レスポンスのexampleを複数定義する

APIのレスポンスで同じHTTPステータスにexampleを複数定義したいということがあります。そんな場合の書き方です。

Adding Examples - swagger.ioを参考にするとフォーマットはこんな感じです。

paths:
  #...省略...
  responses:
    'HTTPステータスコード':
      #...省略...
      examples: # <<<複数定義する場合は「example」ではなく「s」をつけて「examples」になります。
        examples1:
          summary: 説明文、例えば「データがある場合」
          value:
            # ここ以降にレスポンスの例を記載します。
        examples2:
          summary: 説明文、例えば「データがない場合」
          value:
            # ここ以降にレスポンスの例を記載します。

同じHTTPステータスで、exampleだけではなく返却するオブジェクトを複数定義することもできるようです。

Swaggerで、とあるapiのレスポンスにおいて、「同じステータスコードを返すんだけれど、bodyの内容が違う場合がある」時、SwaggerのoneOfという書き方で対応できます。(swagger3.0以上だったはず)

【Swagger3.0】1つのステータスコードに対して複数のレスポンスを定義 (oneOf) - 196Log

YAMLファイルに保存する

ブラウザ上で書いたものをローカルにダウンロードしてYAMLファイルとして保存します。

f:id:ponsuke_tarou:20210121171039p:plain
[File] > [Save as YAML]から簡単に保存できる

f:id:ponsuke_tarou:20210122222954j:plain
新宿区の弁天湯

はまゆう日記

基本情報

我が家に来た経緯

お向かいさんがはまゆうの種を玄関先で配っていたのでたくさんいただいてみた。

f:id:ponsuke_tarou:20210110102433j:plain
2021-01-10:玄関で管理中

分類

  • 学名:Crinum asiaticum L.
    • 日本に自生するのは亜種のCrinum asiaticum var. japonicum
  • APG体系 : キジカクシ目Asparagales > ヒガンバナ科Amaryllidaceae > ハマオモト属Crinum > ハマユウC. asiaticum
  • 別名:浜木綿(ハマユウ)、浜万年青(ハマオモト)
  • 常緑 / 多年草 / 半耐寒性

生態 : 鉢植えにして寒くなったら家に入れる

  1. 温暖な海岸砂地に自生する大形常緑の多年草
  2. 花茎:約50~100cm(葉:50~80cm)
    • でかい・・・うちのぷてぃーとな花壇には入らない・・・
  3. 葉の間の真ん中から太くてまっすぐな茎を上に伸ばし、先端に白い花弁が25~40個ほど集まって、散形花序を作る
  4. 根(鱗茎)に有毒なリコリン(アルカロイド)を含む
    • 食べるとよだれが出て、吐き気、下痢、血圧低下、中枢神経の麻痺などの症状が現れる
    • しかし、ハマオモトヨトウはハマユウを食べる
時期 補足
播種 種を採取した後すぐ 種から花が立派につくようになるまで数年かかる
肥料 4月(発芽) 緩効性化成肥料
苗植え 4~8月 or 11月
花期 7~9月 花は夕方から開き始め深夜に満開になる。よい香りを放つ
肥料 9月(花の終わり) 緩効性化成肥料

https://sakata-tsushin.com/oyakudachi/lesson/flower/assets_c/2016/05/crinum_calendar-thumb-960x450-4840.jpg リナムの育て方・栽培方法|失敗しない栽培レッスン(花の育て方)|サカタのタネ 家庭菜園・園芸情報サイト 園芸通信

(土)赤玉土7:腐葉土3

日当たり 水はけ 乾燥 寒さ 暑さ
朝から晩まで日陰にならない場所が 良い 弱い 弱い

https://www.sakataseed.co.jp/product/topics/file/image/%E7%90%83%E6%A0%B9/%E3%82%A4%E3%83%B3%E3%83%89%E3%81%AF%E3%81%BE%E3%82%86%E3%81%86%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88.gif

クリナム (プレミアム野放し球根)|商品情報いろいろ検索|タネ・苗・園芸用品・農業用資材の総合案内:サカタのタネ

f:id:ponsuke_tarou:20220315200739j:plain
2021-07-27

MacでJSFのプロジェクトを作る

  • 環境
    • macOS Big Sur バージョン11.0.1
    • openjdk version "11.0.8" 2020-07-14

以前、CentOSJSFのプロジェクトを作ったので今回はMacでつくる

ponsuke-tarou.hatenablog.com

CentOSでせっかく作っても・・・Dockerイメージを取らずに、Gitにコミットせずに・・・うっかりEC2インスタンスもろとも削除してしまいました。 というわけでMacで再び作ります。

f:id:ponsuke_tarou:20201221231926j:plain
豊島区の目白庭園

Eclipseを配置する

  1. Mac 版 Eclipse Pleiades All in One リリース - Qiitaを参考にEclipseを配置する
  2. eclipse.iniで以前インストールしたJava11を設定する
オプション 意味 参考
-vm JVM(Javaプログラムを動かすためのソフトウェア)のパスを設定 eclipse.iniに-vmを指定する方法 - Qiita
--illegal-access=deny コマンドで起動するときに出るワーニングを抑制 警告を出ないようにする - Qiita
#...省略...以下追記箇所...
-vm
/usr/local/opt/openjdk@11/bin/java
-vmargs
--illegal-access=deny
#...省略...

Payaraをインストールする

  1. zipでインストールする - Qiita
  2. Eclipseに設定する - Qiita

Mavenプロジェクトを作成する

  1. [パッケージ・エクスプローラー]にカーソルを入れて「Ctrl + N」で新規作成ダイアログを表示する。
  2. [Maven] > [Mavenプロジェクト] > [次へ]ボタン
  3. [シンプルなプロジェクトの作成(アーキタイプ選択のスキップ)]チェックボックスをONにする > [次へ]ボタン
  4. 以下を設定して[完了]ボタンでプロジェクトを作成する

pom.xml文字コードUTF-8」を設定する

文字コードを設定しないと各OSの文字コードでコードがビルドされます。 そうなるとビルドする環境によって内容が変わってしまいます。

なので、プラットフォームのエンコーディング (実際は UTF-8) を使用してフィルターされたリソースをコピーします。つまり、ビルドはプラットフォームに依存します!というメッセージがログや[エラー・ログ]ビューに出力されます。

<!-- ...省略... -->
  <properties>
    <!-- ソースの文字コードを定義 -->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
</project>

コンパイラを設定する

  1. [パッケージ・エクスプローラー]でプロジェクトを選択 > 「Command + I」でプロパティダイアログを表示する
  2. [Java コンパイラー] > [Javaビルド・パス上の実行環境'J2SE-1.5'から準拠を使用]チェックボックスをOFFにする
  3. [コンパイラー準拠レベル]で「11」を選択
  4. [デフォルトの準拠設定の使用]チェックボックスをOFFにする
  5. [適用]ボタン > メッセージダイアログが表示されるので[はい]でビルドを行う f:id:ponsuke_tarou:20201229095524p:plain

pom.xmlMavenコンパイル用のJDKを定義する

pom.xmlを開いてJDKを以下のように定義します。

JDKを定義しないとMavenビルド後にJava compiler level does not match the version of the installed Java project facet.というエラーになることがあります。

<!-- ...省略... -->
  <properties>
    <!-- ソースの文字コードを定義 -->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!-- Mavenコンパイル用のJavaを定義 -->
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>
</project>

f:id:ponsuke_tarou:20201229115210j:plain
台東区の鶴の湯

JSFを設定する

  1. [パッケージ・エクスプローラー]でプロジェクトを選択 > 「Command + I」でプロパティダイアログを表示する
  2. [プロジェクト・ファセット] > [ファセット・フォームへ変換...]リンクから一覧を表示する
  3. [Java]をONにして[Version]を「11」にする
  4. [JavaServer Faces]をONにして[Version]を「2.2」にする
    • ここを「2.3」にすると後でコードをサーバで実行するときにUnable to find CDI BeanManager.となることがあるので注意してください
  5. [動的Webモジュール]をONにして[Version]を「3.1」にする
    1. [ランタイム]タブ > 表示されたPayaraをONにする
    2. 下の方に出てくる[より詳しい構成が必要...]リンクを押下して[ファセット・プロジェクトの変更]画面を表示する
      • f:id:ponsuke_tarou:20201229101314p:plain
    3. [コンテキストルート : ]を設定する(今回はデフォルトのまま)
      • Content directoryは、HTMLやCSSや画像ファイルなどのコンテンツを格納するディレクトリルート
    4. [web.xmlデプロイメント記述子の生成 : ]チェックボックスをONにする > [次へ]ボタン
      • f:id:ponsuke_tarou:20201216225927p:plain
    5. [URLマッピング・パターン:]で「/faces/」を[除去]して、「.jsf」「*.xhtml」を[追加...]する
      • f:id:ponsuke_tarou:20201229101140p:plain
    6. [OK]ボタンでダイアログを閉じる
  6. [適用して閉じる]ボタンでプロパティダイアログを閉じる
  7. メッセージダイアログが表示されるので[はい]でビルドを行う
    • f:id:ponsuke_tarou:20201216230210p:plain

f:id:ponsuke_tarou:20201229101623p:plain
この設定によりWebContentsディレクトリやweb.xml、faces-config.xmlが作成されます。

WebContentディレクトリは、コンテキストディレクトリともいいます。 コンテキストディレクトリは、任意のディレクトリに変更することもできます。

中身はこんな感じです。(まだないものもあります)

ディレクトリ/ファイル名 内容
WebContent/WEB-INF コンパイル済みのプログラムや各種のライブラリファイル、設定ファイルなどが入ります。
このディレクトリ配下のリソースは、クライアント(WEBブラウザ)からアクセスすることはできません。
WebContent/WEB-INF/lib 各種ライブラリのJARファイル。
ここに配置したJARファイル中のクラスファイルはWebアプリケーションから参照することができます。
WebContent/WEB-INF/classes 作成したプログラムのclassファイルやメッセージプロパティファイルが格納されます。
WebContent/WEB-INF/faces-config.xml JSF の構成ファイルです。エラー・メッセージの国際化などに使用するリソース・バンドルの情報が記述されています。
WebContent/WEB-INF/web.xml アプリケーションの動作を指定する必須ファイルです。
FacesServletの動作環境や条件を定義します。
WebContent/resources 画像やCSSなどWebページに読み込ませるデータを配置します。

出力されたweb.xmlはこんな感じです。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>tryJSF</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>
</web-app>

出力されたfaces-config.xmlはこんな感じです。

<?xml version="1.0" encoding="UTF-8"?>
<faces-config
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
    version="2.2">

</faces-config>

pom.xmlJSFのライブラリを定義する

  1. Maven Repository: org.glassfish » javax.facesから任意のバージョンのMaven用定義をコピーしてpom.xmlに定義を貼り付ける
    • 今回は作業時点で最新の「2.4」を使う
  2. Maven Repository: org.primefaces » primefacesから任意のバージョンのMaven用定義をコピーしてpom.xmlに定義を貼り付ける
    • 今回は作業時点で最新の「8.0」を使う
    • 参考 : Primefacesの紹介
  3. [Package Explorer]でプロジェクトを選択 > 「fn + option + F5」でダイアログを表示 > [OK]ボタンでMavenを更新する
<!-- ...省略... -->
  </properties>
  <dependencies>
    <!-- https://mvnrepository.com/artifact/org.glassfish/javax.faces -->
    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>javax.faces</artifactId>
        <version>2.4.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.primefaces/primefaces -->
    <dependency>
      <groupId>org.primefaces</groupId>
      <artifactId>primefaces</artifactId>
      <version>8.0</version>
    </dependency>
  </dependencies>
</project>

最初に表示されるページを作成する

JSFのページはWebContentディレクトリにXHTMLで作成します。

index.xhtmlを作成する

  1. [パッケージ・エクスプローラー]で[WebContent]を選択して「Command + N」で新規ダイアログを開く
  2. [Web] > [HTML ファイル] > [次へ]ボタン > [ファイル名:]に「index.xhtml」を入力し[次へ]ボタン
  3. [テンプレート:] > [新規Faceletテンプレート] > [完了]ボタンで新規ページを作成する f:id:ponsuke_tarou:20201221230531p:plain

今はとりあえずなのでindex.xhtmlはテンプレートのままにします(改行は調整済み)。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
  <head>
    <title><ui:insert name="title">Default title</ui:insert></title>
  </head>
  <body>
    <ui:debug hotkey="x" rendered="#{initParam['javax.faces.FACELETS_DEVELOPMENT']}"/>
    <div id="header">
      <ui:insert name="header">
        Header area. See comments below this line in the source.<!-- include your header file or uncomment the include below and create header.xhtml in this directory --> <!-- <ui:include src="header.xhtml"/> -->
      </ui:insert>
    </div>
    <div id="content">
      <ui:insert name="content">
        Content area. See comments below this line in the source. <!-- include your content file or uncomment the include below and create content.xhtml in this directory --> <!-- <div> --> <!-- <ui:include src="content.xhtml"/> --> <!-- </div> -->
      </ui:insert>
    </div>
    <div id="footer">
      <ui:insert name="footer">
        Footer area. See comments below this line in the source. <!-- include your header file or uncomment the include below and create footer.xhtml in this directory --> <!--<ui:include src="footer.xhtml"/> -->
      </ui:insert>
    </div>
  </body>
</html>

index.xhtmlをウェルカムページに設定する

ファイル名を指定しなかった場合に、既定で返されるドキュメントは設定ファイルで指定することが出来ます。 この既定のファイルのことを、ウェルカムページ (Welcome page) といいます。

JSP のウェルカムページ (デフォルトページ) の設定 - Java による Web アプリケーション開発 - Java の基本 - Java 入門

web.xmlにあるwelcome-file-listタグ配下を以下のように変更します。

<!-- ...省略... -->
  <welcome-file-list>
    <welcome-file>index.jsf</welcome-file>
  </welcome-file-list>
<!-- ...省略... -->

Payaraを起動する

  1. [パッケージ・エクスプローラー]でプロジェクトを選択 > Option + Shift + X + R(サーバーで実行)
  2. ダイアログでPayaraを選択 > [OK]ボタンで実行
  3. ブラウザでhttp://localhost:8080/tryJsf/にアクセスしてページが表示されたら動作確認完了

f:id:ponsuke_tarou:20201229114256p:plain

f:id:ponsuke_tarou:20201221232307j:plain
世田谷の用賀にある栄湯

Cloud9でLambdaを作ろうとして失敗した記録

残念ながら

このページは残念な記録しかないので、こういうことが起こるんだぁぐらいにしか役立ちません。 解決方法もありません。誰かに教えてほしい状態です。

Command failed: virtualenv venv -p python3.7

$ python -V
Python 3.6.12
$ python -m pip -V
pip 20.3.1 from /home/ec2-user/.local/lib/python3.6/site-packages/pip (python 3.6)

$ sudo python -V
Python 3.6.12
$ sudo pip -V
pip 9.0.3 from /usr/lib/python3.6/dist-packages (python 3.6)

作成内容

f:id:ponsuke_tarou:20201207204413p:plain

エラー

f:id:ponsuke_tarou:20201207204425p:plain

原因 : 不明

# インストールディレクトリを見てみると
$ which python
/usr/bin/python
# Pythonは「2.7」「3.6」はあるが「3.7」はない
$ ls -la /usr/bin/ | grep python
lrwxrwxrwx  1 root root          24 Dec  7 09:52 python -> /etc/alternatives/python
lrwxrwxrwx  1 root root          17 Dec  4 16:25 python2 -> /usr/bin/python27
-rwxr-xr-x  1 root root        5104 Nov  2 22:27 python27
-rwxr-xr-x  1 root root        5104 Nov  2 22:27 python2.7
-rwxr-xr-x  1 root root        1846 Nov  2 22:27 python2.7-config
lrwxrwxrwx  1 root root          25 Dec  4 16:26 python3 -> /etc/alternatives/python3
-rwxr-xr-x  3 root root        6872 Aug 31 18:58 python36
-rwxr-xr-x  3 root root        6872 Aug 31 18:58 python3.6
lrwxrwxrwx  1 root root          17 Dec  4 16:26 python3.6-config -> python3.6m-config
-rwxr-xr-x  3 root root        6872 Aug 31 18:58 python3.6m
-rwxr-xr-x  1 root root         173 Aug 31 18:57 python3.6m-config
-rwxr-xr-x  1 root root        3373 Aug 31 18:41 python3.6m-x86_64-config
lrwxrwxrwx  1 root root          32 Dec  4 16:26 python3-config -> /etc/alternatives/python3-config
lrwxrwxrwx  1 root root          31 Dec  7 09:52 python-config -> /etc/alternatives/python-config

Python3.7をインストールしてもダメだった

参考 : pyenvによる仮想Python環境をAWS Cloud9上で構築する | Developers.IO

#### pyenvをインストールする
# pyenvをGitHubからCloneする
$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
Cloning into '/home/ec2-user/.pyenv'...
remote: Enumerating objects: 18376, done.
remote: Total 18376 (delta 0), reused 0 (delta 0), pack-reused 18376
Receiving objects: 100% (18376/18376), 3.67 MiB | 2.61 MiB/s, done.
Resolving deltas: 100% (12514/12514), done.
# バージョンを確認する
$ ~/.pyenv/bin/pyenv --version
pyenv 1.2.21-1-g943015eb
# .bashrcに定義を書いて
$ vi ~/.bashrc
$ cat ~/.bashrc | grep pyenv
export PATH="$HOME/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
# 反映させてPATHを通す
$ source ~/.bashrc
$ printenv PATH | sed -e 's/:/:\n/g' | grep pyenv
/home/ec2-user/.pyenv/bin:
$ pyenv --version
pyenv 1.2.21-1-g943015eb



#### Python3.7をインストールする
# インストールできるPython3.7を確認する
$ pyenv install -l | grep 3.7
  2.3.7
  3.3.7
  3.7.0
  3.7-dev
  3.7.1
  3.7.2
  3.7.3
  3.7.4
  3.7.5
  3.7.6
  3.7.7
  3.7.8
  3.7.9
  miniconda-3.7.0
  miniconda3-3.7.0
  stackless-3.3.7
  stackless-3.7.5
# Python3.7.9をインストールする
$ pyenv install 3.7.9
Downloading Python-3.7.9.tar.xz...
-> https://www.python.org/ftp/python/3.7.9/Python-3.7.9.tar.xz
Installing Python-3.7.9...
python-build: use readline from homebrew
WARNING: The Python bz2 extension was not compiled. Missing the bzip2 lib?
WARNING: The Python readline extension was not compiled. Missing the GNU readline lib?
Installed Python-3.7.9 to /home/ec2-user/.pyenv/versions/3.7.9
# WARNINGで出ている不足したものをインストールする
$ sudo yum -y install bzip2 readline
Loaded plugins: priorities, update-motd, upgrade-helper
amzn-main                                                                                   | 2.1 kB  00:00:00     
amzn-updates                                                                                | 3.8 kB  00:00:00     
1067 packages excluded due to repository priority protections
Package bzip2-1.0.6-8.12.amzn1.x86_64 already installed and latest version
Package readline-6.2-9.14.amzn1.x86_64 already installed and latest version
Nothing to do
# Python3.7に切り替える
$ pyenv versions
* system (set by /home/ec2-user/.pyenv/version)
  3.7.9
$ pyenv global 3.7.9
$ python -V
Python 3.7.9


### ターミナルでエラーになったコマンドは実行できる・・・でもLambda関数が作れない・・・
$ virtualenv venv -p python3.7
Running virtualenv with interpreter /home/ec2-user/.pyenv/shims/python3.7
Using base prefix '/home/ec2-user/.pyenv/versions/3.7.9'
New python executable in /home/ec2-user/environment/venv/bin/python3.7
Also creating executable in /home/ec2-user/environment/venv/bin/python
Installing setuptools, pip, wheel...
done.

Sorry, IKPdb only supports Python 3.6.x for now.

  • 環境
    • EC2 instance / Amazon Linux 2
    • Python 3.7.9
    • pip 20.3(rootのpipはアップグレードする等々していて紛失中)
$ python -V
Python 3.7.9

$ pip -V
pip 20.3 from /home/ec2-user/.local/lib/python3.7/site-packages/pip (python 3.7)

$ sudo python -V
Python 2.7.18

$ sudo pip -V
sudo: pip: command not found

作成内容

f:id:ponsuke_tarou:20201207153031p:plain
ランタイムをPython3.7にしたくてblueprintで「microservice-http-endpoint」を選択した

エラー

The following error was encountered when attempting to create your serverless application
Command failed: venv/bin/pip install ikp3db==1.1.4
ERROR: Command errored out with exit status 1:
command: /home/ec2-user/environment/a/venv/bin/python3.7 -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-9w9degcv/ikp3db_0b4451da9ebd4366a50f7baed6b7014c/setup.py'"'"'; __file__='"'"'/tmp/pip-install-9w9degcv/ikp3db_0b4451da9ebd4366a50f7baed6b7014c/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-pip-egg-info-va_wyotl
cwd: /tmp/pip-install-9w9degcv/ikp3db_0b4451da9ebd4366a50f7baed6b7014c/
Complete output (1 lines):
Sorry, IKPdb only supports Python 3.6.x for now.
----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

f:id:ponsuke_tarou:20201207153233p:plain

原因 : 不明

そもそもIKPdbとはなんぞや?

これには ikpdb という名前のモジュールが含まれており、AWS Cloud9 はこれを使用して Python アプリケーションをデバッグします。

との共同作業 AWS Lambda の関数 AWS Cloud9 Integrated Development Environment (IDE) - AWS Cloud9

Python2.7しかサポートしないよ的なことを言っている気がします。

Please note that IKPdb supports only CPython 2.7, CPython 3 support is the next step.

Welcome to IKPdb’s documentation! — IKPdb 1.0.0 documentation

いや、Python3以降は「IKPdb」ではなく「IKP3db」を使うのか?エラーもikp3db==1.1.4になっている。

IKP3db is a Python 3 debugger. For Python 2 see the IKPdb project on github and pypi.

ikp3db · PyPI

「IKP3db」はバージョン「1.3」以降でPython3.7に対応している・・・のかな?

1.3

Add Python 3.7 support (debugger can now be invoked using the breakpoint() function).

Ikp3db Changelog - pyup.io

対応 : あきらめる

Lambdaの画面から関数を作成しようっと

Command failed: virtualenv venv -p python3.6

  • 環境
    • EC2 instance / Amazon Linux 2
    • Python 3.7.9
    • pip 20.3(rootのpipはアップグレードする等々していて紛失中)
$ python -V
Python 3.7.9

$ pip -V
pip 20.3 from /home/ec2-user/.local/lib/python3.7/site-packages/pip (python 3.7)

$ sudo python -V
Python 3.7.9

$ sudo pip -V
sudo: pip: command not found

作成内容

f:id:ponsuke_tarou:20201207142629p:plain
[Select runtime]にPythonが「3.6」しかないので選択Python3.6を指定した

エラー

f:id:ponsuke_tarou:20201207142743p:plain

原因 : 不明

# インストールディレクトリを見てみると
$ which python
alias python='python3'
        /usr/bin/python3
# Pythonは「2.7」「3.7」はあるが「3.6」はない
$ ls -la /usr/bin/ | grep python                                                                                                                                                      
lrwxrwxrwx  1 root root          24 Dec  7 05:07 python -> /etc/alternatives/python
lrwxrwxrwx  1 root root           9 Nov  6 19:45 python2 -> python2.7
-rwxr-xr-x  1 root root        7048 Aug 27 21:23 python2.7
-rwxr-xr-x  1 root root        1846 Aug 27 21:23 python2.7-config
lrwxrwxrwx  1 root root          16 Nov  6 19:45 python2-config -> python2.7-config
lrwxrwxrwx  1 root root           9 Nov  6 19:57 python3 -> python3.7
-rwxr-xr-x  2 root root        7048 Aug 27 22:02 python3.7
lrwxrwxrwx  1 root root          17 Nov  6 19:57 python3.7-config -> python3.7m-config
-rwxr-xr-x  2 root root        7048 Aug 27 22:02 python3.7m
-rwxr-xr-x  1 root root         173 Aug 27 22:02 python3.7m-config
-rwxr-xr-x  1 root root        3210 Aug 27 21:16 python3.7m-x86_64-config
lrwxrwxrwx  1 root root          16 Nov  6 19:57 python3-config -> python3.7-config
lrwxrwxrwx  1 root root          14 Nov  6 19:45 python-config -> python2-config

対応 : Python3.6にこだわりがないのであきらめる

  1. Python2からPython3へ自力でバージョンアップしてYumが壊れたので修理する
  2. yumをアップデート
  3. Python3.6を探す >> インストールできそうなものがわからないので面倒くさくてあきらめた
$ sudo yum -y update
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
amzn2-core                                                                                                                                                                             | 3.7 kB  00:00:00     
...
Complete!

$ yum search python36-dev
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
220 packages excluded due to repository priority protections
========================================================================================= N/S matched: python36-dev ==========================================================================================
boost-python36-devel.x86_64 : Shared object symbolic links for Boost.Python 3
shiboken-python36-devel.x86_64 : Development files for shiboken

  Name and summary matches only, use "search all" for everything.

Backlogの課題にGitHubのコミットを連携する方法

このブログはBacklog Advent Calendar 2020 の7日目の記事です。 はじめてのAdvent Calendar参加でドキドキです。 adventar.org

Backlogの課題にGitHubのコミットやプルリクをコメントとして入れたい!

やりたいことは、「Backlogの課題にGitHubのコミットやプルリクをコメントとして入れたい」です。 BacklogのGitを使っていればコミットコメントに課題キーがあればその課題のコメントにコミットが連携されます。 なのでGitHubを使っていても同じようにしたい!

[GithubとBacklogの連携] Backlogでissue管理して、Githubへのコミット内容をBacklogにも反映させる様に連携する方法 - Qiitaを見て簡単にできる!とおもったら・・・GitHubのServiceはWebhookに統合されて消えていました。

We have deprecated GitHub Services in favor of integrating with webhooks. Replacing GitHub Services | GitHub Developer Guide

似たようなことをやっている人は世の中にいるのでいろいろ調べながら手作りすることにしました。

f:id:ponsuke_tarou:20201130170903j:plain
流れはこんなイメージ

GitHubとBacklogを連携するLambda関数を作る

1. BacklogでAPIキーを発行する

BacklogではAWSからやってくる処理を受け取るためのAPIキーを発行します。

f:id:ponsuke_tarou:20201130170932j:plain
絵だとこの辺のことです

  1. [個人設定]の画面を開く
    • f:id:ponsuke_tarou:20201119132811p:plain
      [個人設定]は右上のメニューから
  2. [API] > コメントを入力 > [登録]ボタンでAPIキーを発行する
    • f:id:ponsuke_tarou:20201119133053p:plain
      [登録]ボタンで一覧にAPIキーが追加される

2. AWSでLambdaとAPI Gatewayを作成する

AWSではGitHubからやってくる情報をAPI Gatewayで受け取ってLambda関数を呼び出して処理できるようにします。

f:id:ponsuke_tarou:20201130171001j:plain
絵だとこの辺のことです

IAM ポリシーを作成する

Lambda関数で使用する権限を作成します。

  1. [AWS マネジメントコンソール]から[IAM]の画面を開く
  2. サイドメニューの[ポリシー] > [ポリシーの作成]ボタンで作成画面を表示
  3. [JSON]タブを開いて以下のJSONを設定 > [ポリシーの確認]ボタンで内容を確認
  4. [名前]に任意の値を設定 > [ポリシーの作成]ボタンで作成する
    • f:id:ponsuke_tarou:20201130154133p:plain
      Lambdaのログを作成する権限とSecrets Managerからシークレットを取得する権限を設定している
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:対象のリージョン:{アカウントID}:*"
        },
        {
            "Action": "secretsmanager:GetSecretValue",
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}

IAM ロールを作成する

Lambda関数にアタッチするロールを作成してさっき作ったポリシーを設定します。

  1. [AWS マネジメントコンソール]から[IAM]の画面を開く
  2. サイドメニューの[ロール] > [ロールの作成]ボタンで作成画面を表示
  3. [AWSサービス] > [Lambda]を選択後に[次のステップ: アクセス権限]ボタンで次の画面を表示
  4. [ポリシーのフィルタ]で作成したポリシーを検索して選択後に[次のステップ: タグ]ボタンで次の画面を表示
  5. [タグの追加 (オプション)]は任意なので設定せずに[次のステップ: 確認]ボタンで次の画面を表示
  6. [ロール名]を入力して[ロールの作成]ボタンでロールを作成する

Lambda関数をとりあえず作る

まずは、実装を後回しにして関数だけ作ります。

  1. [AWS マネジメントコンソール]から[Lambda]の画面を開く
  2. [関数の作成]ボタンで作成画面を表示する
  3. [一から作成]を選択して以下を設定して[関数の作成]ボタンで関数を作成する
    • 関数名 : 任意の名前(今回はgithub_to_backlog)
    • ランタイム : Python3.7
    • 実行ロール : 既存のロールを使用する
    • 既存のロール : 作成したロールを選択

コードには初期コードがあるのでそのまま。実装は後でやります。

import json

def lambda_handler(event, context):
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

API Gatewayを作成する

今回は、[HTTP API]と[REST API]のどちらを使おうか迷ったけれど、「使ったことがない」「GitHubのWebhookを受け取りたいだけ」なんといっても「低コストらしいぞ」という理由で[HTTP API]にしました。

  1. [AWS マネジメントコンソール]から[API Gateway]の画面を開く
  2. [APIを作成]ボタンで[APIの作成]画面を表示する
  3. [HTTP API]の[構築]ボタンで次の画面へ
  4. [統合を追加] > [Lambda] > [Lambda 関数]で作成したLambda関数を選択
  5. [API 名]に任意の名前を設定して[次へ]ボタンで[ルートを設定]画面へ
    • f:id:ponsuke_tarou:20201119161251p:plain
  6. 以下を設定して[次へ]ボタンで[ステージを定義]画面へ
    • メソッド : POST
    • リソースパス : /{Lambda関数名}
    • 統合ターゲット : {Lambda関数名}
    • f:id:ponsuke_tarou:20201119161434p:plain
  7. [ステージを追加]ボタンで以下を追加して[次へ]ボタンで[確認して作成]画面へ
    • ステージ名 : $default
    • 自動デプロイ : ON
    • f:id:ponsuke_tarou:20201119161459p:plain
  8. [作成]ボタンで作成する
    • f:id:ponsuke_tarou:20201119161603p:plain
  9. 「{Lambda関数名}のステージ」一覧の[URLを呼び出す]列に「呼び出しURL」が表示される
  10. curlコマンドを使ってAPI Gatewayを呼び出してAPI GatewayがLambda関数を呼び出せることを確認する
curlコマンドのオプション 意味
-X HTTPメソッドを指定する
-H HTTPヘッダを指定する
# Lambda関数の初期コードに書いてある「Hello from Lambda!」が返却される
$ curl -X POST -H 'Content-Type:application/json' {呼び出しURL}/{リソースパス}
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
}
100    20  100    20    0     0     50      0 --:--:-- --:--:-- --:--:--    50"Hello from Lambda!"

# 失敗例) 「リソースパス」をくっつけ忘れると「Not Found」になるので注意
$ curl -X POST -H 'Content-Type:application/json' {呼び出しURL}
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    23  100    23    0     0    287      0 --:--:-- --:--:-- --:--:--   291{"message":"Not Found"}

3. GithubでWebhook設定する

GitHubでのプッシュやプルリクの情報がAPI Gatewayに送られるようにWebhookを設定します。

f:id:ponsuke_tarou:20201130171025j:plain
絵だとこの辺のことです

  1. ブラウザでGitHubリポジトリを表示する
  2. [Settings] > [Webhooks] > [Add webhook]ボタン
  3. 以下を設定して[Add webhook]ボタン
    • Payload URL : API Gatewayの「呼び出しURL/リソースパス
    • Content type : application/json
    • Secret : 推測されにくい任意の文字列
    • SSL verification : Enable SSL verification
    • Which events would you like... : 「Let me select individual events.」にして以下を選択
      • Pull requests
      • Pushes
    • f:id:ponsuke_tarou:20201119151909p:plain
  4. 表示されたWebhookの横につくマークが緑チェックになるのを確認する
    • URLが間違っていたりすると赤バツが付くので、その場合は内容を確認する
    • f:id:ponsuke_tarou:20201119164221p:plain

4. AWSでSecrets Managerに情報を登録する

BacklogのAPIキーGitHubのWebhookに設定したSecretは大切な情報なのでSecrets Managerに登録して、Lambda関数から取得して使うようにします。

f:id:ponsuke_tarou:20201130171045j:plain
絵だとこの辺のことです

  1. [新しいシークレットを保存する]ボタンから作成画面を表示して以下を設定して[次]ボタン
    • シークレットの種類: その他のシークレット
    • シークレットのペア: 下記表参照
      • f:id:ponsuke_tarou:20201130160504p:plain
        Backlogの情報は1つのシークレットに2つのキーを設定する
    • 暗号化キー: DefaultEncryptionKey
  2. 以下を設定して[次]ボタン
    • シークレットの名前: 下記表参照
    • 説明とタグ: 任意
  3. [自動ローテーションを無効にする]を設定して[次]ボタン
  4. [保存]ボタンでシークレットを作成する
シークレットの名前 シークレットキー シークレットの値 使うところ
github/to/backlog GITHUB_SECRET GitHubのWebhookに
設定したSecret
GitHubから来た情報
をHMAC認証する時に使う
backlog/{GitHubのusername} APIKEY BacklogのAPIキー Backlogに各ユーザで
コメント追加するのに使う
同上 MAIL Backlogに登録され
ているメールアドレス
(以下手順で確認)
1.Backlogの[個人設定]の画面を開く
2. [ユーザー情報] > [メールアドレス]
プルリクエストの通知を
つけるためのid取得に使う

5. Lambda関数を実装する

環境変数を設定する

Backlogの課題にコメント追加するにはBacklog APIを使用します。 そのためにBacklog APIの情報としてLambdaの環境変数に以下を設定して使用します。

環境変数のキー 説明
BACKLOG_ENDPOINT BacklogAPIのエンドポイント https://BacklogのURLと同じ値/api/v2/
参考:認証と認可 | Backlog Developer API | Nulab
PROJECT_KEY Backlogのプロジェクトキー 参考 : プロジェクトの追加 – Backlog ヘルプセンター

Secrets Managerからシークレットの値を取得する

登録したBacklogのAPIキーGitHubのWebhookに設定したSecretを取得できるようにします。 基本的な実装はシークレットを登録した際にSecrets Managerの画面に表示されるサンプルコードを使用しました。

def get_secrets_manager_dict(secret_name: str) -> dict:
    """Secrets Managerからシークレットのセットを辞書型で取得する"""
    secrets_dict = {}
    if not secret_name:
        print('シークレットの名前未設定')
    else:
        session = boto3.session.Session()
        client = session.client(
            service_name='secretsmanager',
            region_name='対象のリージョン'
        )
        try:
            get_secret_value_response = client.get_secret_value(
                SecretId=secret_name
            )
        except ClientError as e:
            print('シークレット取得失敗:シークレットの名前={}'.format(secret_name))
            print(e.response['Error'])
        else:
            if 'SecretString' in get_secret_value_response:
                secret = get_secret_value_response['SecretString']
            else:
                secret = base64.b64decode(get_secret_value_response['SecretBinary'])
            secrets_dict = ast.literal_eval(secret)
    return secrets_dict

GitHubのWebhookから送られてくる内容を取得する処理を作る

GitHubのWebhookから送られてくる内容は以下サイトに説明があります。

まずは、GitHubから送られてきた情報が本当に設定したWebhookからなのを確認するためにHMAC認証します。 GitHubのWebhookに設定したSecretGitHubから送られてきた情報で認証を行います。

参考 : GitHubのWebhookでプルリクエストをマージした際にツイートできるようしてみた - Qiita

def is_correct_signature(signature: str, body: dict) -> bool:
    """GitHubから送られてきた情報をHMAC認証する."""
    if signature and body:
        # GitHubのWebhookに設定したSecretをSecrets Managerから取得する
        secret = get_secrets_manager_key_value('github/to/backlog', 'GITHUB_SECRET')
        if secret:
            secret_bytes = bytes(secret, 'utf-8')
            body_bytes = bytes(body, 'utf-8')
            # Secretから16進数ダイジェストを作成する
            signedBody = "sha1=" + hmac.new(secret_bytes, body_bytes, hashlib.sha1).hexdigest()
            return signature == signedBody
    else:
        return False

プッシュとプルリクではGitHubからくる情報が異なるというのとBacklogの課題に追加するコメントをちょっと変えるために処理を切り分けます。

操作 action pull_request commits
Pull requests o o x
Pushes x x o
def lambda_handler(event, context):
    # GitHubから送られてきた情報をHMAC認証する
    if is_correct_signature(event['headers']['x-hub-signature'], event['body']):
        body = json.loads(event['body'])
        # プッシュとプルリクを識別して処理を切り分ける
        if 'pull_request' in body and 'action' in body:
            # Pull requestsの場合
            add_pull_request_comment(body['action'], body['pull_request'], body['sender']['login'])
        elif 'commits' in body:
            # Pushesの場合
            add_push_comment(body['commits'])
        else:
            print('処理対象外のリクエストなので処理しない' + json.dumps(body))
    else:
        print('認証できないGitHubのsignatureが送られてきた')

コメントから課題キーを検索する

コメント追加する課題を決めるためにコミットコメントやプルリクのコメントから課題キーを検索します。 複数の課題キーがあればそれぞれの課題にコメントが追加できるようにリストに課題キーを入れて返却します。

def get_issue_key(message: str) -> list:
    """コメントから課題キーを検索する."""
    issue_key = []
    # 「[プロジェクトキー] + [-] + [数字の繰り返し]を全て抜出
    key_format = '{}-[\d]+'.format(os.environ.get('PROJECT_KEY'))
    match_list = re.findall(key_format, message)
    if match_list:
        # 抜き出した課題キーのリストから重複を削除する
        issue_key = list(set(match_list))
    else:
        print('コメントにBacklogの課題キーが設定されていない')
    return issue_key

プルリクエストはレビューアーに通知をつけたいので通知リストを作る

通知リストは次の流れで作成します。

  1. プルリクエストに設定されたレビューアーのGitHubユーザー名でSecrets ManagerからBacklogのメールアドレスを取得する
  2. APIで取得したBacklogのユーザー一覧からメールアドレスでBacklogのユーザーIDを探す
  3. 通知リストへ追加する
def create_notified_list(requested_reviewers: list, backlog_users: list) -> list:
    """レビューアーにBacklogの通知をつけるため、にコメント登録の通知を受け取るユーザーIDリストを作成する"""
    notified_list = []
    if requested_reviewers and backlog_users:

        for reviewer in requested_reviewers:
            # Secrets ManagerからBacklogのメールアドレスを取得する
            backlog_mail = get_secrets_manager_key_value('backlog/' + reviewer['login'], 'MAIL')

            if backlog_mail:
                for user in backlog_users:
                    # 取得したメールアドレスと同じメールアドレスのユーザーをBacklogユーザー一覧から探す
                    if backlog_mail == user['mailAddress']:
                        # ユーザーのIDをリストへ追加する
                        notified_list.append(user['id'])

    return notified_list

Backlogにコメントを追加する

def add_backlog_comment(api_key: str, issue_keys: list, comment: str, notified_list: list):
    """Backlogの課題にコメントを追加する."""
    if not api_key or not issue_keys:
        print('BacklogのAPIキーまたは課題キー未設定')
    else:
        params = {'apiKey': api_key}
        payload = {'content': comment}

        # コメント登録の通知を受け取るユーザーIDがある場合は設定する
        if notified_list:
            payload['notifiedUserId[]'] = notified_list

        # コメントにある課題すべてにコメントを追加する
        for issue_key in issue_keys:
            header = {'Content-Type': 'application/x-www-form-urlencoded'}
            api_path = urllib.parse.urljoin(os.environ.get('BACKLOG_ENDPOINT'), '/'.join(['issues', issue_key, 'comments']))
            result = requests.post(api_path, headers=header, params=params, data=payload)

できた!!

f:id:ponsuke_tarou:20201130165240p:plain

今回はシンプルにコメント追加をしました。 今後、BacklogのGitみたいに課題のステータス変更をしたり、レビューコメントも追加したりしたら楽しそうです!

コードの全体

ここ以降は、コード全部を張っているだけなので興味のある人だけ見てください。

import json, os
import hmac, hashlib
import requests, urllib
import boto3
import base64
import ast, re
from botocore.exceptions import ClientError


def get_secrets_manager_dict(secret_name: str) -> dict:
    """Secrets Managerからシークレットのセットを辞書型で取得する"""
    secrets_dict = {}
    if not secret_name:
        print('シークレットの名前未設定')
    else:
        session = boto3.session.Session()
        client = session.client(
            service_name='secretsmanager',
            region_name='対象のリージョン'
        )
        try:
            get_secret_value_response = client.get_secret_value(
                SecretId=secret_name
            )
        except ClientError as e:
            print('シークレット取得失敗:シークレットの名前={}'.format(secret_name))
            print(e.response['Error'])
        else:
            if 'SecretString' in get_secret_value_response:
                secret = get_secret_value_response['SecretString']
            else:
                secret = base64.b64decode(get_secret_value_response['SecretBinary'])
            secrets_dict = ast.literal_eval(secret)
    return secrets_dict


def get_secrets_manager_key_value(secret_name: str, secret_key: str) -> str:
    """AWS Secrets Managerからシークレットキーの値を取得する."""
    value = ''
    secrets_dict = get_secrets_manager_dict(secret_name)
    if secrets_dict:
        if secret_key in secrets_dict:
            # secrets_dictが設定されていてsecret_keyがキーとして存在する場合
            value = secrets_dict[secret_key]
        else:
            print('シークレットキーの値取得失敗:シークレットの名前={}、シークレットキー={}'.format(secret_name, secret_key))
    return value


def is_correct_signature(signature: str, body: dict) -> bool:
    """GitHubから送られてきた情報をHMAC認証する."""
    if signature and body:
        # GitHubのWebhookに設定したSecretをSecrets Managerから取得する
        secret = get_secrets_manager_key_value('github/to/backlog', 'GITHUB_SECRET')
        if secret:
            secret_bytes = bytes(secret, 'utf-8')
            body_bytes = bytes(body, 'utf-8')
            # Secretから16進数ダイジェストを作成する
            signedBody = "sha1=" + hmac.new(secret_bytes, body_bytes, hashlib.sha1).hexdigest()
            return signature == signedBody
    else:
        return False


def get_backlog_api_key(github_username: str) -> str:
    """GitHubのユーザ名から該当ユーザのBacklogのAPIキーを取得する."""
    api_key = ''
    if github_username:
        api_key = get_secrets_manager_key_value('backlog/' + github_username, 'APIKEY')
    else:
        print('GitHubユーザー名未設定')
    return api_key


def add_backlog_comment(api_key: str, issue_keys: list, comment: str, notified_list: list):
    """Backlogの課題にコメントを追加する."""
    if not api_key or not issue_keys:
        print('BacklogのAPIキーまたは課題キー未設定')
    else:
        params = {'apiKey': api_key}
        payload = {'content': comment}

        # コメント登録の通知を受け取るユーザーIDがある場合は設定する
        if notified_list:
            payload['notifiedUserId[]'] = notified_list

        # コメントにある課題すべてにコメントを追加する
        for issue_key in issue_keys:
            header = {'Content-Type': 'application/x-www-form-urlencoded'}
            api_path = urllib.parse.urljoin(os.environ.get('BACKLOG_ENDPOINT'), '/'.join(['issues', issue_key, 'comments']))
            result = requests.post(api_path, headers=header, params=params, data=payload)


def get_backlog_users(api_key: str) -> list:
    """Backlogユーザー一覧の取得"""
    users = []

    if not api_key:
        print('BacklogのAPIキー未設定')
    else:
        # ユーザーの取得対象はプロジェクト内に設定する
        api_path = urllib.parse.urljoin(os.environ.get('BACKLOG_ENDPOINT'), '/'.join(['projects', os.environ.get('PROJECT_KEY'), 'users']))
        api_result = requests.get(api_path, params={'apiKey': api_key}).json()

        if type(api_result) == list and api_result:
            # Backlogのユーザー一覧を取得する
            users = api_result
        else:
            print('Backlogプロジェクトのユーザー一覧取得失敗:{}'.format(json.dumps(api_result)))

    return users


def create_notified_list(requested_reviewers: list, backlog_users: list) -> list:
    """レビューアーにBacklogの通知をつけるため、にコメント登録の通知を受け取るユーザーIDリストを作成する"""
    notified_list = []
    if requested_reviewers and backlog_users:

        for reviewer in requested_reviewers:
            # Secrets ManagerからBacklogのメールアドレスを取得する
            backlog_mail = get_secrets_manager_key_value('backlog/' + reviewer['login'], 'MAIL')

            if backlog_mail:
                for user in backlog_users:
                    # 取得したメールアドレスと同じメールアドレスのユーザーをBacklogユーザー一覧から探す
                    if backlog_mail == user['mailAddress']:
                        # ユーザーのIDをリストへ追加する
                        notified_list.append(user['id'])

    return notified_list


def get_issue_key(message: str) -> list:
    """コメントから課題キーを検索する."""
    issue_key = []
    # 「[プロジェクトキー] + [-] + [数字の繰り返し]を全て抜出
    key_format = '{}-[\d]+'.format(os.environ.get('PROJECT_KEY'))
    match_list = re.findall(key_format, message)
    if match_list:
        # 抜き出した課題キーのリストから重複を削除する
        issue_key = list(set(match_list))
    else:
        print('コメントにBacklogの課題キーが設定されていない')
    return issue_key


def add_push_comment(commits: list):
    print('プッシュの情報をBacklogの課題へコメント追加する')
    print(json.dumps(commits))
    # プッシュに含まれるコミットを1つずつ処理する
    for commit in commits:
        issue_key = []
        if 'message' in commit:
            issue_keys = get_issue_key(commit['message'])
        if issue_keys:
            backlog_api_key = get_backlog_api_key(commit['committer']['username'])
            if backlog_api_key:
                comment_format = '- URL:{}\n{}'
                comment = comment_format.format(commit['url'], commit['message'])
                add_backlog_comment(backlog_api_key, issue_keys, comment, [])


def add_pull_request_comment(action: str, pull_request: dict, username: str):
    print('プルリクエストの情報をBacklogの課題へコメント追加する\nアクション:{}\nマージ:{}'.format(action, str(pull_request['merged'])))
    print(json.dumps(pull_request))
    # プルリクのタイトルとコメントから課題キーを取得する
    issue_keys = get_issue_key(pull_request['title'] + pull_request['body'])
    if issue_keys:
        # BacklogのAPIキーを取得する
        backlog_api_key = get_backlog_api_key(username)

        if backlog_api_key:
            notified_list = []
            if 'requested_reviewers' in pull_request and pull_request['requested_reviewers']:
                # Backlogのユーザー一覧を取得する
                backlog_users = get_backlog_users(backlog_api_key)
                if backlog_users:
                    # レビューアーが設定されている場合は追加するコメント用の通知リストを作成する
                    notified_list = create_notified_list(pull_request['requested_reviewers'], backlog_users)

            # プルリクのアクションによってコメントを作成する
            comment = ''
            if action == 'opened' or action == 'reopened':
                comment = 'プルリクエストが作成されました'
            elif action == 'closed':
                if pull_request['merged']:
                    comment = 'プルリクエストがマージされました'
                else:
                    comment = 'プルリクエストが却下されました'
            else:
                comment = 'プルリクエストが変更されました'
            comment += '\n\n- URL:{}'.format(pull_request['html_url'])

            add_backlog_comment(backlog_api_key, issue_keys, comment, notified_list)


def lambda_handler(event, context):
    # GitHubから送られてきた情報をHMAC認証する
    if is_correct_signature(event['headers']['x-hub-signature'], event['body']):
        body = json.loads(event['body'])
        # プッシュとプルリクを識別して処理を切り分ける
        if 'pull_request' in body and 'action' in body:
            # Pull requestsの場合
            add_pull_request_comment(body['action'], body['pull_request'], body['sender']['login'])
        elif 'commits' in body:
            # Pushesの場合
            add_push_comment(body['commits'])
        else:
            print('処理対象外のリクエストなので処理しない' + json.dumps(body))
    else:
        print('認証できないGitHubのsignatureが送られてきた')

Kintoneの開発環境を作成する

Kintoneの開発環境って何?

kintone API を使った開発用に1年間Kintoneが使えるようになります。

kintone 開発者ライセンスは、kintoneのアプリケーション開発を目的として、ご利用いただける開発環境です。本運用のご利用はできません。

kintone 開発者ライセンス(開発環境) – cybozu developer network

開発環境を作成する

cybozu developer networkにアカウントを作成する

  1. cybozu developer networkを表示する
  2. 右上の[サインイン]ボタンでポップアップを表示する
  3. [cybozu developer network を初めてご利用の方: アカウント登録]リンクから登録用のポップアップを表示する
  4. 入力項目を入力して[アカウント登録]で仮登録する
  5. 入力したメールで件名が「cybozu developer networkへようこそ」のメールに書かれたURLからサイトを表示する
  6. パスワードを設定してログインする

kintone開発者ライセンスを取得する

  1. ログイン後の画面で[kintone開発者ライセンスを取得]ボタンでページを表示する
  2. 内容を確認した後で[開発者ライセンスを申し込む]ボタンで申込ページを表示する
    • f:id:ponsuke_tarou:20201201174417p:plain
  3. 申込みフォームを入力して[申込み]ボタンで申し込む
    • f:id:ponsuke_tarou:20201201174524p:plain
  4. 申し込み完了メッセージは画面上部に表示される
    • f:id:ponsuke_tarou:20201201174755p:plain
      画面が上部に移動しのでエラーがあったのかと思ったけど大丈夫だった。
  5. メールがやってくるのをしばし待つ > メールが来たら書いてある[アクセスURL][ログイン名][パスワード]でKintoneにログインする
    • f:id:ponsuke_tarou:20201201192409p:plain
      開発環境のKintoneが使えるようになった!

使ってみる

パスワード認証を使ってスペース情報取得してみる

  1. [Kintone] > [スペース]の右にある[+]ボタン > [スペースを作成]からポップアップを表示
  2. [はじめから作る] > [基本設定]タブの内容を入力([参加メンバー]タブは誰もいないので設定しない)
  3. [保存]ボタンでスペースを作成する
  4. 「ログイン名:パスワード」をbase64エンコードする
  5. 以下を実行してスペース情報を取得する
    • crul + -H X-Cybozu-Authorization:{base64エンコードしたもの} + https://{サブドメイン名}.cybozu.com/k/v1/space.json?id={スペースのID}
コード 意味 参考
-H X-Cybozu-Authorization:{base64エンコードしたもの} ユーザ認証用のリクエストヘッダ kintone REST APIの共通仕様 – cybozu developer network
https://{サブドメイン名}.cybozu.com/k/v1/space.json?id={スペースのID} スペース情報の取得用のURIとリクエストパラメータ スペース情報の取得 – cybozu developer network
# 最後の改行を出力しない(-n)ようにしてbase64エンコードする
% echo -n '{ログイン名}:{パスワード}' | base64
xxxxx==

# スペース情報を取得する
% curl -H 'X-Cybozu-Authorization:xxxxx==' https://hoge.cybozu.com/k/v1/space.json?id=1
{"id":"1","name":"はじめてのスペース","defaultThread":"1","isPrivate":true,"creator":{"code":"...

APIトークン認証を使ってアプリ情報取得してみる

  1. スペースのページを表示する
  2. [アプリ]の右にある[+]ボタンで作成画面を表示する
  3. [初めから作成] > 手頃にパーツを配置する > [アプリを公開] > [OK]ボタンでアプリを作成する
    • f:id:ponsuke_tarou:20201201210935p:plain
      手頃にこんな感じで作りました。
  4. APIトークンを生成するを参考にトークンを作成する
  5. 以下を実行してスペース情報を取得する
    • crul + -H X-Cybozu-API-Token:{APIトークン} + https://{サブドメイン名}.cybozu.com/k/v1/app/form/fields.json?app={アプリのID}\&lang=ja
コード 意味 参考
-H X-Cybozu-API-Token:{APIトークン} APIトークン認証用のリクエストヘッダ kintone REST APIの共通仕様 – cybozu developer network
https://{サブドメイン名}.cybozu.com/k/v1/app/form/fields.json?app={アプリのID}\&lang=ja フィールドの一覧を取得用のURIとリクエストパラメータ フォームの設定の取得 – cybozu developer network
% curl -H X-Cybozu-API-Token:XxxxXx https://hoge.cybozu.com/k/v1/app/form/fields.json?app={アプリのID}\&lang=ja
{"revision":"4","properties":{"カテゴリー":{"type":"CATEGORY","code":"カテゴリー","label":"カテゴリー","enabled":false},"テーブル":{"type":"SUBTABLE","code":"テーブル","noLabel":false,"label":"テーブル","fields":{"数値":{"type":"NUMBER","code":"数値","label":"数値","noLabel":false,...

f:id:ponsuke_tarou:20201201213930j:plain
明治通りで拾ったかりん、蜂蜜でつけてみた

万二郎岳と万三郎岳 in 天城山

先週は那須で登山をしました。

ponsuke-tarou.hatenablog.com

万二郎岳

f:id:ponsuke_tarou:20201124203809j:plain
天城高原ゴルフ場の横にあるハイカー専用駐車場に車を停めました。
この駐車場はおトイレだけでなく、登山靴の洗い場にブラシまで設置された至れり尽くせりな駐車場でした。 とてもありがたいです。
f:id:ponsuke_tarou:20201124202056j:plain
今回は、シャクナゲコースを回ります。
登山道は駐車場のすぐ横にあります。 バス停が登山道入り口横にあってとてもわかりやすかったです。
f:id:ponsuke_tarou:20201124202141j:plain
なだらかな道が続いてとても歩きやすいです。
道は1つなので迷いにくいのですが、水のない川や落ち葉の積もった平地などがあって「あれ?」っと思うところもあります。 登山道から外れないようにロープがしっかり張ってあるのですが、ときどきロープのどっち側が登山道か迷うことがありました。 気につけられたピンクや青のテープも参考にしながら気をつけて歩きます。
f:id:ponsuke_tarou:20201124202227j:plain
ついつい足元ばかり見てしまいますが、しっかり顔をあげて周りを見ながら歩きました。
山頂に近づいていくにつれてあせびの木がたくさん!あせびが咲くのは早春らしく、きっとまだ雪が残っているだろうからこられそうにはないけどきっとすごく綺麗なんだろうなぁと想像しながら進みました。 f:id:ponsuke_tarou:20201124202247j:plain

万三郎岳

f:id:ponsuke_tarou:20201124202330j:plain 万三郎岳が近づいてくるとコースの名前にもなっているアマギシャクナゲが登山道に増えてきます。 標識によると5~6月に見頃を迎えるそうなので次はぜひ咲いているときに来てみたいです。 f:id:ponsuke_tarou:20201124202432j:plain f:id:ponsuke_tarou:20201124202455j:plain

裸の木がいっぱい!です。木についているフダをみるとヒメシャラとリョウブなのですが・・・見分けがつきません。 すっかり落葉したこの時期に「リョウブ」「ヒメシャラ」「サルスベリ」を木の幹と樹形だけで見分けられるようになりたいです。 botanica-media.jp

f:id:ponsuke_tarou:20201124202537j:plain
登山道にはたくさんのヒメシャラとリョウブがたくさん
f:id:ponsuke_tarou:20201124203250j:plain f:id:ponsuke_tarou:20201124203633j:plain 基本的にシャクナゲコースは初心者向けらしいのですが山頂から戻る道はちょっと足場が不安定なところが多いです。
f:id:ponsuke_tarou:20201124203705j:plain
途中でお年寄りのご夫婦が「きつい!」と休憩しておりました。確かに何度かこけました。
岩場とまではいかないけれど石の多い場所や木の階段が崩れたところが続いているので気を張って進みます。 子供や若い人はぽんぽん進めるけれど、中年に差し掛かっている身としては膝にこないよう気を使います。 駐車場に戻って時間を見ると全体で5時間ちょっとの道のりでした。 私は歩くのが遅いので登山に慣れている人だときっと4時間くらいで回るのかもしれませんね。

登山の後はやっぱり温泉!

温泉だ!と検索して出てきたのは伊豆市冷川にある「源泉湯治の宿 ごぜんの湯」。早速向かってみると・・・休業のふだがかかっていた・・・ onsen.surugabank.co.jp というわけで道の駅伊東マリンタウンで温泉に入って帰りました。 ito-marinetown.co.jp

天城山(シャクナゲコース)| 山ガールのための山歩きガイド コースガイド 女性のための登山情報サイト 山ガールネットで紹介されている日帰り温泉「東海館」というところを次は狙うべし!

白笹山と南月山 in 那須

先週はわたらせ渓谷に行きました

ponsuke-tarou.hatenablog.com

白笹山

11月も2週目になり、紅葉もだんだん終わりに近づいてきました。でも今日は暖かいです。

f:id:ponsuke_tarou:20201124193029j:plain
沼ッ原湿原の駐車場に車を停めてスタートです!
まずは白笹山の山頂を目指します。
f:id:ponsuke_tarou:20201124193348j:plain
登山道の入り口はこんな感じです。
数日前に雪が降ったと地元の方が言っていました。 日陰になっているところにはところどころ雪が残っています。 ぬかるんだ所はあるけれど歩くのには問題なさそうです。
f:id:ponsuke_tarou:20201124193245j:plain
途中には見晴らしのいい場所がたくさんあります。
沼ッ原調整池とその隣にある駐車場、そこに停まっているうちの車まで見えました。 f:id:ponsuke_tarou:20201124193648j:plain ちょっと辛い場所は熊笹地獄。 1箇所だけだけど土のある所を踏むと熊笹の根を踏んでどんどん土のないところへ滑っていく・・・しかも戻ろうとすると熊笹の根が足に引っかかって足が上がらないという残念な状態。
f:id:ponsuke_tarou:20201124193756j:plain
山の陽があまり当たらない斜面に来るとビックリ!雪がいっぱい。
f:id:ponsuke_tarou:20201124193824j:plain
はじめて雪がある登山道を登りました。
f:id:ponsuke_tarou:20201124193850j:plain
日頃から雪が降るような季節は登山はしないので、なかなか怖い!慎重に慎重に進みます。
f:id:ponsuke_tarou:20201124193927j:plain
白笹山山頂は・・・びっくりするほど狭い・・・山頂でご飯を食べるスペースはなかったです。
次はお隣の南月山を回ります。

南月山

f:id:ponsuke_tarou:20201124194601j:plain f:id:ponsuke_tarou:20201124194118j:plain

f:id:ponsuke_tarou:20201124194153j:plain
笹の道をどんどん進んでいきます。ここもまた景色がすごくいい!
f:id:ponsuke_tarou:20201124194500j:plain f:id:ponsuke_tarou:20201124194236j:plain f:id:ponsuke_tarou:20201124194259j:plain
f:id:ponsuke_tarou:20201124194332j:plain
南月山と書いてミナミガッサンと読むそうです。
f:id:ponsuke_tarou:20201124194411j:plain
山頂からは何度か行った茶臼岳がよく見えます。
近く見えるのでいけそうな気がしますが実際はかなり遠いようです。低レベル登山者の私では到底いけませんが夢は広がります。 多くの人はここから黒尾谷岳へ回るコースへ行くようですが、疲れたし日没の時間もあるのでここから引き返します。

来週は伊豆半島で登山です!

ponsuke-tarou.hatenablog.com

わたらせ渓谷駅ハイと美登里湯

先週はよく歩きました。

ponsuke-tarou.hatenablog.com

先週の4連休は、ずっと歩き続けたので今週末はかるぅく行くことにしました。

紅葉映えるわたらせ渓谷散策と御朱印めぐり

www.jreast.co.jp

紅葉の渓谷を行くハイキングで、今日は紅葉狩りといきます。

f:id:ponsuke_tarou:20201108175233j:plain

f:id:ponsuke_tarou:20201108101058j:plain
わたらせ渓谷鐵道には初めて乗った

f:id:ponsuke_tarou:20201108101158j:plain
わたらせ渓谷鐵道大間々駅では駅員さんとわっしーがお出迎えをしてくれた。

f:id:ponsuke_tarou:20201108101336j:plain
昭和12年建築のながめ余興場はレトロなつくりらしいが、今日はパス。

御朱印集めはしていないけれど、神社やお寺を見るのは楽しいです。

f:id:ponsuke_tarou:20201108173851j:plain
りっぱな神明宮

f:id:ponsuke_tarou:20201108173923j:plain
紅葉に映えるはねたき橋

ここから、高津戸峡遊歩道にはいって渓谷沿いを歩きました。

f:id:ponsuke_tarou:20201108174413j:plain
澄み渡る青空に映えて本当に綺麗です。

先週秩父に行った時は、あとちょっとだった紅葉が最好調に色づいていました。 f:id:ponsuke_tarou:20201108174519j:plain

ひたすら県道沿いを歩きます。道路沿いは歩きにくいけれど秋の山々をみながら歩けるのはいいです。

f:id:ponsuke_tarou:20201108174540j:plain
お庭が美しい松源寺

f:id:ponsuke_tarou:20201108174609j:plain
京都の貴船神社にゆかりがある貴船神社

貴船神社でおトイレを借りたら、横にご飯屋さん的なところへの矢印があった。

f:id:ponsuke_tarou:20201108174644j:plain
酒樽が個室になっている道楽園

朝ごはんを食べ損ねてめっちゃ腹減りだったので早速行ってみたら、なかなか面白いところだった。 f:id:ponsuke_tarou:20201108174750j:plain

でっかい酒樽がたくさん並ぶ庭のある・・・と思ったら全部個室だった。 f:id:ponsuke_tarou:20201108174814j:plain

ご飯を食べ終わったら、猫がやってきて遊んでくれた。素敵なところだった。 f:id:ponsuke_tarou:20201108174833j:plain スタート地点にもどって駅ハイ終了。

f:id:ponsuke_tarou:20201108174858j:plain
わたらせ渓谷鐵道大間々駅は駅本屋及びプラットフォームが登録有形文化財に登録されているそうです。

足立区美登利湯

りょうもう何とか号に乗って到着した北千住駅から学園通りを突き当たるまで歩いて横に曲がるとすぐにある美登利湯。 去年の台風19号で煙突がなくなってしまったらしい、残念。 f:id:ponsuke_tarou:20201108175210j:plain 少々熱めのお湯、ちょっと我慢して入っていると慣れてきていい湯になる。

来週は那須へ行って登山です。

ponsuke-tarou.hatenablog.com

ネイチャーミュージアム!天空の石切り場を廻る鋸山絶景ハイキング

昨日はあるきたをしました。

ponsuke-tarou.hatenablog.com

ネイチャーミュージアム!天空の石切り場を廻る鋸山絶景ハイキング

www.jreast.co.jp

f:id:ponsuke_tarou:20201103214804j:plain
浜金谷駅は本数がすくないので帰りに気をつけねば。
f:id:ponsuke_tarou:20201103214926j:plain
お肉屋さんでメンチカツを買って食べながらスタート。
f:id:ponsuke_tarou:20201103215010j:plain
ヒカリモという単細胞生物の藻がいるらしい。
f:id:ponsuke_tarou:20201103215111j:plain
車力道という登山道の入り口
f:id:ponsuke_tarou:20201103215202j:plain
上り道が始まります。
f:id:ponsuke_tarou:20201103215301j:plain
だんだん上り道がきつくなってきます。
f:id:ponsuke_tarou:20201103215405j:plain
昔の石切場、神殿みたいだ!
f:id:ponsuke_tarou:20201103215452j:plain
駅ハイだと思ってなめていた・・・かなりかなり本格的な登山です。
f:id:ponsuke_tarou:20201103215603j:plain
急な階段の先には広がる絶景です。
f:id:ponsuke_tarou:20201103215714j:plain
急な階段が次々と現れるけれど、見ものな石切場がたくさんあります。
f:id:ponsuke_tarou:20201103215834j:plain
丸池
f:id:ponsuke_tarou:20201103215916j:plain
切り通し
f:id:ponsuke_tarou:20201103215952j:plain
三角池
f:id:ponsuke_tarou:20201103220033j:plain
結構疲れてきた、ぬかるんでいるところもあるので注意しながら歩きます。
f:id:ponsuke_tarou:20201103220124j:plain
舞台にしてコンサートをやることもあるそうな
f:id:ponsuke_tarou:20201103220227j:plain
大谷石石切場みたいな感じダァ
f:id:ponsuke_tarou:20201103220323j:plain
トレッキングシューズ履いてくればよかった・・・登山している人も多くいました。
f:id:ponsuke_tarou:20201103220412j:plain
見下ろしてもすごい!
f:id:ponsuke_tarou:20201103220454j:plain
東京湾がよく見えます。
f:id:ponsuke_tarou:20201103220535j:plain
日本寺に入ってみました、入り口にあるのは百尺観音様
f:id:ponsuke_tarou:20201103220624j:plain
いろんなパンフで見るアングルのやつですね。
f:id:ponsuke_tarou:20201103220704j:plain
今度は大仏様を目指してながぁいながぁい階段を降ります。戻るのが辛そう。
f:id:ponsuke_tarou:20201103220805j:plain
思ったより大きい大仏様でした。
f:id:ponsuke_tarou:20201103221046j:plain
鋸山をせっせと下山してやってきたのは鋸山美術館。さかなが・・・海辺だからか。
f:id:ponsuke_tarou:20201103221157j:plain
恋人の聖地で鐘を無駄に叩いて、ザ・フィッシュでご飯です。
f:id:ponsuke_tarou:20201103221257j:plain
なかなかおいしいぃぃぃ!
最後にかぢや旅館で日帰り温泉入浴をしました、気持ちよかった。
f:id:ponsuke_tarou:20201103221330j:plain
一日お世話になりました、楽しかった。

来週は、わたらせ渓谷で駅ハイです。

ponsuke-tarou.hatenablog.com

あるきたとテルメ末広

昨日は駅ハイと銭湯巡り

ponsuke-tarou.hatenablog.com

あるきた

お天気がいまいちなので朝から家でだらだらして、昼前からあるきた始めした。

www.city.kita.tokyo.jp

王子豊島コース

f:id:ponsuke_tarou:20201103071832j:plain
紀州神社
f:id:ponsuke_tarou:20201103071852j:plain
清光寺

豊島馬場遺跡公園、毎年来るけど何の遺跡か知りません。 自分が生きてないくらい昔にいまいち興味が持てません。

f:id:ponsuke_tarou:20201103071907j:plain
豊島馬場遺跡公園

はじめて入るお蕎麦屋さん、喉ごし味値段、バッチリです!

f:id:ponsuke_tarou:20201103071916j:plain
松の家の力そば
f:id:ponsuke_tarou:20201103071924j:plain
庚申観音堂

赤羽岩淵コース

豊島からとぼとぼ荒川までやって来ました。 何と、荒川知水資料館が予約制になっておりました。 この辺に来る時は、おトイレを借りたりジュース買ったりしていたのに、入ることができない。

f:id:ponsuke_tarou:20201103072053j:plain
荒川知水資料館
f:id:ponsuke_tarou:20201103072102j:plain
旧岩淵水門のあたりはすっかり紅葉していました

来る時期が遅すぎたのか、去年釣り人に沢山落ちていると教えてもらったクルミはありませんでした。 残念なので、これまた教えてもらったグレープフルーツを3つほどもいで行きました。 市販のものより苦味がなくて美味しいです。

f:id:ponsuke_tarou:20201103075358j:plain
岩淵水門を遠巻きにみて、
f:id:ponsuke_tarou:20201103075435j:plain
八雲神社を通り過ぎ、
f:id:ponsuke_tarou:20201103075839j:plain
宝なんとか院まできて完了!

ずっと行ってみたかった赤羽のダンボに入店。

f:id:ponsuke_tarou:20201103075853j:plain
まずは、カレードリア、は普通の大きさ。
タバスコのビンが小さく見える。 なかなか安定した美味しさです、残さず頂きました。
f:id:ponsuke_tarou:20201103075903j:plain
生で見たくてつい注文したエビとたらこのクリームパスタ大盛。

テルメ末広

名前からそんなに古くも新しくもない銭湯だと思っていたら、なかなか綺麗で新しいところでした。

f:id:ponsuke_tarou:20201103075913j:plain
北区のテルメ末広
f:id:ponsuke_tarou:20201103075924j:plain
フロントのおじちゃんはとても感じよく、湯温が丁度良くてジャグジーの泡が楽しかったです。

駅ハイと月見湯温泉と高砂湯

昨日は登山をしました。

ponsuke-tarou.hatenablog.com

今日は東中野スタートの駅ハイに参加するため、朝から東中野目指して歩きます。 なんとか受付開始の10:00過ぎに到着しました。

秋の神田川と神社・仏閣を巡り中野の自然を感じるウォーク

www.jreast.co.jp

f:id:ponsuke_tarou:20201102100643j:plain
駅ハイマップ
f:id:ponsuke_tarou:20201102083311j:plain
中野氷川神社
f:id:ponsuke_tarou:20201102083427j:plain
f:id:ponsuke_tarou:20201102083450j:plain
神田川歌碑
神田川沿いを歩く時間はちょうど日が差して暑い・・・11月なのに・・・。
f:id:ponsuke_tarou:20201102083511j:plain
成願寺
f:id:ponsuke_tarou:20201102083618j:plain
八津御嶽神社
f:id:ponsuke_tarou:20201102083852j:plain

本日2つめの氷川神社氷川神社って東京にいくつあるんだろう?

f:id:ponsuke_tarou:20201102083920j:plain
本郷氷川神社
f:id:ponsuke_tarou:20201102084314j:plain
宝仙寺
f:id:ponsuke_tarou:20201102084409j:plain 「こうようやま」ではなく「もみじやま」と読むらしい。 親子連れがたくさんいました。
f:id:ponsuke_tarou:20201102084436j:plain
中野区立 紅葉山公園
お昼は野菜ラーメンにしました、とってもおいしい!
f:id:ponsuke_tarou:20201102084524j:plain
らーめん 北国の野菜ラーメン

世田谷区月見湯温泉

中野駅から下高井戸を目指して歩きます。東高円寺駅を通り過ぎて妙法寺を通り過ぎるついでに

f:id:ponsuke_tarou:20201102084604j:plain
妙法寺
東高円寺駅を通り過ぎて妙法寺を通り過ぎるついでに中でお経を聞かせてもらいました。 f:id:ponsuke_tarou:20201102084658j:plain 妙法寺商店街を抜けて荒玉水道道路を進みます。大宮八幡あたりで水道道路を離れ永福町駅へ。
f:id:ponsuke_tarou:20201102084725j:plain
永福稲荷神社
神田川を越えて下高井戸駅を越えてとうとう到達! 湯あたりはまぁまぁ、「かけ流し」と「みどりの湯船」をみて勇んで入ったら水風呂だった。 お湯になると透明になる温泉のようですね、湯上りはお肌すべすべで流石温泉って感じでした。
f:id:ponsuke_tarou:20201102084845j:plain
月見湯温泉

足立区高砂

お夕飯を商店街で食べて帰路に着きます。 永福町駅まで戻って善福寺川を越えて大六天神社横を通り過ぎて再び妙法寺横を抜け蚕糸の森公園まで戻りました。

f:id:ponsuke_tarou:20201102084934j:plain
杉並区立蚕糸の森公園にいたさくらさん
東高円寺駅から斜めに住宅地へ突っ込みます。 煙突を目指せばなんとかなる!と思ったけれど夜でマンション群中は流石に苦しかった・・・。 とはいえ、やっと到着!空が見える露天風呂がありました。
f:id:ponsuke_tarou:20201102085110j:plain
足立区高砂
f:id:ponsuke_tarou:20201102085409j:plain 最後は高田馬場駅まで歩いて帰りました。 1日の歩数が50,000歩を初めて越えました。

明日は、あるきたと銭湯巡りです。

ponsuke-tarou.hatenablog.com

日向山と丸山登山

10月31日ハロウィンです、安全情報確保支援士の試験が終わりました。 ITワールドから自然ワールドへ引っ越します。

日向山と丸山登山

10時前に駐車場に到着、出遅れたので満車になっていないか心配でしたが余裕です。

f:id:ponsuke_tarou:20201102064837j:plain
道の駅果樹公園あしがくぼ第二駐車場
芦ヶ久保駅を見学して、いざ出発します。
f:id:ponsuke_tarou:20201102064946j:plain
西武鉄道芦ヶ久保
神社の横の道から登り始めます。 安全に登れるようお願いしてから進みます。
f:id:ponsuke_tarou:20201102065030j:plain
芦ケ久保白鬚神社
神社横の家かと思ったらお寺さんでした、この横の道を進みます。
f:id:ponsuke_tarou:20201102065253j:plain
茂林寺
舗装された道が続きますが、道沿いの木々が色づいてきて秋らしい光景が広がります。 f:id:ponsuke_tarou:20201102065513j:plain f:id:ponsuke_tarou:20201102065647j:plain f:id:ponsuke_tarou:20201102065718j:plain なかなか急な道が続いていて疲れたので、途中の東屋で一休憩。 f:id:ponsuke_tarou:20201102065740j:plain
f:id:ponsuke_tarou:20201102072336j:plain
山神祠
f:id:ponsuke_tarou:20201102072452j:plain
f:id:ponsuke_tarou:20201102072652j:plain
青い実のなる植物発見
f:id:ponsuke_tarou:20201102072743j:plain
見えつらいけど、奥には大きな柿の木があってたっくさんの柿がたわわに実っている。
あしがくぼ山の花道は紅葉がたくさん植樹されていました。 数年後の秋は素晴らしい景色が広がるでしょう、楽しみ楽しみ、と思ってふと振り返ると下りの山道が・・・あれ?車道じゃなくて登山道がちゃんとあった・・・。
f:id:ponsuke_tarou:20201102073022j:plain
あしがくぼ山の花道
日向山の山頂はどこかよくわからないけどきっとこの辺だろう、ということでベンチに座ってちょっと休憩。 f:id:ponsuke_tarou:20201102073431j:plain 道案内版は風化して文字が消えていますが「丸山」をどなたかが彫ってくれていました。ありがたい。
f:id:ponsuke_tarou:20201102073454j:plain
丸山への登山道への入り口
f:id:ponsuke_tarou:20201102074335j:plain
f:id:ponsuke_tarou:20201102074448j:plain
途中には神社がありました、早速お参りです。
f:id:ponsuke_tarou:20201102074850j:plain
道端に象さん発見!
f:id:ponsuke_tarou:20201102074931j:plain
f:id:ponsuke_tarou:20201102074958j:plain
どかーーんと木が折れていてその横から急な坂道が始まります。
f:id:ponsuke_tarou:20201102075104j:plain
f:id:ponsuke_tarou:20201102075139j:plain
紅葉が始まりつつあります。
f:id:ponsuke_tarou:20201102075231j:plain
丸山登頂!
f:id:ponsuke_tarou:20201102075640j:plain f:id:ponsuke_tarou:20201102075702j:plain
f:id:ponsuke_tarou:20201102075734j:plain
展望台からの景色はすごくよかった!登るのが面倒くさくて迷ったけど、登ってよかった。
f:id:ponsuke_tarou:20201102080003j:plain
f:id:ponsuke_tarou:20201102080032j:plain
大野峠に向かって歩きます。
f:id:ponsuke_tarou:20201102080117j:plain
パラグライダーの発射場があって、飛び上がるのは感動的です、見てるだけだけど。
f:id:ponsuke_tarou:20201102080441j:plain
f:id:ponsuke_tarou:20201102080534j:plain
ちょっと道が崩れたところがあります。
f:id:ponsuke_tarou:20201102080643j:plain
急な坂道が続いています
f:id:ponsuke_tarou:20201102080716j:plain
杉林が広がっていて、重度の花粉症としては春は避けたいところ。
f:id:ponsuke_tarou:20201102080816j:plain
綺麗な小川が登山道の横を流れています。
f:id:ponsuke_tarou:20201102080908j:plain
お地蔵様が道端で見守ってくれています。
f:id:ponsuke_tarou:20201102080950j:plain
f:id:ponsuke_tarou:20201102081012j:plain
登山道の最後には養蜂所がありました、蜂蜜も売っていたので試食させてもらって美味しかったから早速購入!
道の駅に戻ったらすっかりお腹が空いたのでおうどんをいただいて、定番の武甲温泉に行ってきました。 泉質はまぁまぁ、周囲にはいろんな温泉があるみたいなので次は他のところにも行ってみたいですね。

明日は駅からハイキングと世田谷の温泉銭湯です。

ponsuke-tarou.hatenablog.com

Docker DesktopをMacにインストールする方法

Docker for Macをインストールする

インストーラーでインストールする方法

  • 環境 : macOS Catalina バージョン10.15.7

matsuand.github.io

  1. Docker Hubを表示する
  2. [Get Docker]ボタンでdmgファイルをダウンロードする
  3. Docker.dmg をダブルクリックしてアプリケーション・フォルダに Docker アイコンをドラッグする
  4. Launchpadに追加されたDockerを起動する
    • f:id:ponsuke_tarou:20201029200427p:plain
  5. f:id:ponsuke_tarou:20201029200501p:plain
    ダイアログが表示されたら[開く]ボタンで進む
  6. f:id:ponsuke_tarou:20201029200625p:plain
    ダイアログが表示されたら[OK]ボタンで進む
  7. パスワードを入力して[ヘルパーをインストールする]ボタンで進むとダイアログが開いて[start]ボタンから使い方の説明が見られる
    • f:id:ponsuke_tarou:20201029201614p:plain
      ボタンをぽちぽちしてくとクローンとかビルドの説明が見られる
  8. f:id:ponsuke_tarou:20201029201703p:plain
    ウィンドウの上にはくじらマークが出る
  9. f:id:ponsuke_tarou:20201029210220p:plain
    くじらマーク > [About Docker Desktop]からバージョンを確認できる
# ターミナルでコマンドを使ってもバージョンを確認できる
% docker --version
Docker version 19.03.13, build 4484c46d9d

インストールできたか確認するためにhello-worldコンテナを作ってみる

  1. ターミナルを起動する
  2. docker run hello-worldを実行
    1. ローカルにはないよと表示される : Unable to find image 'hello-world:latest' locally
    2. 自動でDocker Hubからイメージがpullされる : latest: Pulling from library/hello-world
# hello-worldコンテナを作る
% docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete 
Digest: sha256:8c5aeeb6a5f3ba4883347d3747a7249f491766ca1caa47e5da5dfcf6b9b717c0
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

# コンテナができた
% docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
79377c1d5f44        hello-world         "/hello"            14 minutes ago      Exited (0) 14 minutes ago                       trusting_jang

f:id:ponsuke_tarou:20201029221756j:plain
荒川区のニュー恵美須湯

Homebewでインストールする方法

  • 環境 : macOS Monterey バージョン12.2.1

formulae.brew.sh

# 1. 使う前に更新しておく
$ brew update
Already up-to-date.

# 2. 使う前に健康診断しておく
$ brew doctor
Your system is ready to brew.

# 3. バージョンを見ておく
$ brew --version
Homebrew 3.3.16
Homebrew/homebrew-core (git revision 892f65b1c21; last commit 2022-02-28)
Homebrew/homebrew-cask (git revision 9cc168aba7; last commit 2022-02-28)

# 4. 「Casks」にあるのがDocker Desktopで、もう一つはCUIのDocker
$ brew search docker 
==> Formulae
docker                                 docker-ls                              docker-machine-parallels
docker-clean                           docker-machine                         docker-slim
docker-completion                      docker-machine-completion              docker-squash
docker-compose                         docker-machine-driver-hyperkit         docker-swarm
docker-compose-completion              docker-machine-driver-vmware           docker2aci
docker-credential-helper               docker-machine-driver-vultr            dockerize
docker-credential-helper-ecr           docker-machine-driver-xhyve            lazydocker
docker-gen                             docker-machine-nfs                     mockery

==> Casks
docker                                 docker-toolbox                         homebrew/cask-versions/docker-edge

# 5. インストールする
$ brew install --cask docker
Running `brew update --preinstall`...
==> Auto-updated Homebrew!
Updated 3 taps (homebrew/core, homebrew/cask and homebrew/cask-fonts).
==> New Formulae
aarch64-elf-binutils                                      quick-lint-js
==> Updated Formulae
Updated 6 formulae.
==> New Casks
font-lxgw-bright                       font-mengshen-regular                  iptvnator
==> Updated Casks
Updated 36 casks.

==> Downloading https://desktop.docker.com/mac/main/amd64/74594/Docker.dmg
######################################################################## 100.0%
==> Installing Cask docker
==> Moving App 'Docker.app' to '/Applications/Docker.app'
==> Linking Binary 'docker-compose.bash-completion' to '/usr/local/etc/bash_completion.d/docker-compose'
==> Linking Binary 'docker.zsh-completion' to '/usr/local/share/zsh/site-functions/_docker'
==> Linking Binary 'docker.fish-completion' to '/usr/local/share/fish/vendor_completions.d/docker.fish'
==> Linking Binary 'docker-compose.fish-completion' to '/usr/local/share/fish/vendor_completions.d/docker-compose.f'
==> Linking Binary 'docker-compose.zsh-completion' to '/usr/local/share/zsh/site-functions/_docker_compose'
==> Linking Binary 'docker.bash-completion' to '/usr/local/etc/bash_completion.d/docker'
🍺  docker was successfully installed!

# 6. Docker Desktopのアプリを起動するとdockerコマンドが使える
$ docker --version
Docker version 20.10.12, build e91ed57

f:id:ponsuke_tarou:20211028222834p:plain
アプリを起動するとターミナルでdockerコマンドが使えるようになります。

MySQLを作ってみる

  1. Docker Hubからイメージをpullする
    • docker pull {イメージ名}
  2. imageを確認する
  3. イメージをビルドして起動する
    • docker run -e MYSQL_ROOT_PASSWORD={rootのパスワード} -d -p 3306:3306 {イメージ名}
# イメージをpullする
% docker pull mysql
Using default tag: latest
latest: Pulling from library/mysql
bb79b6b2107f: Already exists 
49e22f6fb9f7: Pull complete 
842b1255668c: Pull complete 
9f48d1f43000: Pull complete 
c693f0615bce: Pull complete 
8a621b9dbed2: Pull complete 
0807d32aef13: Pull complete 
a56aca0feb17: Pull complete 
de9d45fd0f07: Pull complete 
1d68a49161cc: Pull complete 
d16d318b774e: Pull complete 
49e112c55976: Pull complete 
Digest: sha256:8c17271df53ee3b843d6e16d46cff13f22c9c04d6982eb15a9a47bd5c9ac7e2d
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest

# imageを確認する
% docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
mysql               latest              db2b37ec6181        6 days ago          545MB
nginx               latest              f35646e83998        2 weeks ago         133MB
hello-world         latest              bf756fb1ae65        10 months ago       13.3kB

# ビルドして起動する
% docker run -e MYSQL_ROOT_PASSWORD=root -d --name mysql80 -p 3306:3306 mysql
0e2b57b5753826e7d3921037f22665eb331f0861b489245cce27377bc666d0a1

# mysql80が作られて起動している
% docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
0e2b57b57538        mysql               "docker-entrypoint.s…"   5 seconds ago       Up 4 seconds        0.0.0.0:3306->3306/tcp, 33060/tcp   mysql80

docker run のオプション

参考 : run — Docker-docs-ja 19.03 ドキュメント

オプション 意味
-d, --detach コンテナをバックグラウンドで実行し、コンテナIDを表示
-e, --env= 環境変数を指定
--name コンテナに名前を割り当てる
-p, --publish= コンテナのポートをホスト側に公開
-p {ホスト側ポート}:{コンテナ側ポート}

MySQLにログインする

  1. ホストか作ったコンテナにログインする
  2. MySQLにログインする
# コンテナにログインする
% docker exec -it mysql80 bash

# MySQLにログインする
root@0e2b57b57538:/# mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.22 MySQL Community Server - GPL

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

# まだ初期DBしかない
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.01 sec)

# MySQLからログアウトする
mysql> quit
Bye

# コンテナからログアウトする
root@0e2b57b57538:/# exit
exit

%

f:id:ponsuke_tarou:20201029221904j:plain
ニュー恵美須湯には気さくなかわいいおばあちゃんが2人いる

JMeterを使ってみた記録

  • 環境
    • Windows10 Pro バージョン1909
    • java version "1.8.0_251"

JMeterで負荷をかけたいのです。

アプリケーションにおいて、パフォーマンスは重要な要素です。また、どの程度の負荷まで耐えられるのかも、同じく重要な要素となります。JMeterを使用して、このような重要な要素を計測し、より信頼性の高いアプリケーションの開発・改良に役立てましょう。

1. JMeterの基本 | TECHSCORE(テックスコア)

やりたいこと

ローカル環境で実行しているWebアプリケーションで任意の操作中に負荷をかけたい。

JMeterをインストールします。

  1. Apache JMeter - Download Apache JMeterを表示する
  2. [Binaries]からzipファイルをダウンロードする
    • f:id:ponsuke_tarou:20201021093922p:plain
    • 今回は「apache-jmeter-5.3.zip」をダウンロードした
  3. zipファイルを解凍して任意の場所に配置する
# zipファイルを解凍して「/c/apps/」に配置する
$ unzip apache-jmeter-5.3.zip -d /c/apps/
Archive:  apache-jmeter-5.3.zip
   creating: /c/apps/apache-jmeter-5.3/
  inflating: /c/apps/apache-jmeter-5.3/LICENSE
#...省略...
  inflating: /c/apps/apache-jmeter-5.3/docs/api/org/apache/log/package-summary.html
  inflating: /c/apps/apache-jmeter-5.3/docs/api/org/apache/log/package-tree.html

# 「apache-jmeter-5.3/bin/」配下のコマンドでバージョンを確認する
WARNING: package sun.awt.X11 not in java.desktop
    _    ____   _    ____ _   _ _____       _ __  __ _____ _____ _____ ____
   / \  |  _ \ / \  / ___| | | | ____|     | |  \/  | ____|_   _| ____|  _ \
  / _ \ | |_) / _ \| |   | |_| |  _|    _  | | |\/| |  _|   | | |  _| | |_) |
 / ___ \|  __/ ___ \ |___|  _  | |___  | |_| | |  | | |___  | | | |___|  _ <
/_/   \_\_| /_/   \_\____|_| |_|_____|  \___/|_|  |_|_____| |_| |_____|_| \_\ 5.3

Copyright (c) 1999-2020 The Apache Software Foundation

JMeterの画面を起動します。

# apache-jmeter-5.3/bin/jmeterを実行すると画面が起動する
$ /c/apps/apache-jmeter-5.3/bin/jmeter
WARNING: package sun.awt.X11 not in java.desktop
================================================================================
Don't use GUI mode for load testing !, only for Test creation and Test debugging.
For load testing, use CLI Mode (was NON GUI):
   jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]
& increase Java Heap to meet your test requirements:
   Modify current env variable HEAP="-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m" in the jmeter batch file
Check : https://jmeter.apache.org/usermanual/best-practices.html
================================================================================

f:id:ponsuke_tarou:20201021105049p:plain

メニューを日本語表示にする

初期段階ではメニューが英語になっているので日本語表示にします。

  1. [Option] > [Choose Language] > [Japanese]
  2. メニューが日本語表示になる f:id:ponsuke_tarou:20201021134355p:plain

画面の操作を記録する(書き途中)

Thread Groupを作成する

  1. 左に表示されている[Test Plan]を右クリック
  2. [追加] > [Thread(Users)] > [スレッドグループ]で追加する
  3. 表示された画面で以下を設定する
項目 意味 設定値
名前 任意、初めてなのでデフォルトのまま
コメント 任意の値、とりあえず未設定
サンプルエラー後のアクション テスト停止
スレッド数 1回の実行でのアクセス数 初めてなのでデフォルトのまま
Ramp-Up期間 全リクエストの作成時間 60秒
ループ回数 シナリオを繰り返す回数 初めてなのでデフォルトのまま

各項目の意味は、【図解】はじめてでもわかるJMeterの使い方 - RAKUS Developers Blog | ラクス エンジニアブログが図まであってとても分かりやすいです。

記録コントローラーを作成する

  1. スレッドグループを右クリック
  2. [追加] > [ロジックコントローラー] > [記録コントローラー]で追加する
  3. 表示された画面で以下を設定する
項目 設定値
名前 任意、初めてなのでデフォルトのまま
コメント 任意の値、とりあえず未設定

HTTPプロキシサーバを作成する

  1. TestPlanを右クリック
  2. [追加] > [Non-Test エレメント] > [HTTP プロキシサーバ]で追加する
  3. 表示された画面で以下を設定する
項目 設定値
名前 任意、初めてなのでデフォルトのまま
コメント 任意の値、とりあえず未設定
ポート 使用していないポート番号、デフォルトの8888はちょうど使っていないのでそのまま
対象となるコントローラ 作成した記録コントローラを選択

プロキシを設定する

今回使用するのはChromeです。

  1. Win + R > [ファイル名を指定して実行]ダイアログを表示
  2. control inetcpl.cplを入力して[OK]ボタンで[インターネットのプロパティ]ダイアログを開く
  3. [接続]タブ > [LANの設定]ボタンでダイアログを開く
  4. [LANにプロキシサーバーを使用する] > ON
  5. 以下を設定し、[OK]ボタンで保存する
項目 設定値
アドレス localhost
ポート JMeterでHTTPプロキシサーバのポートに設定した値

f:id:ponsuke_tarou:20201021145844p:plain

画面の操作を記録する

  1. HTTP プロキシサーバの画面にある[開始]ボタンで記録を開始する
  2. ダイアログが表示されるので[OK]ボタンで進める
    • f:id:ponsuke_tarou:20201021151653p:plain
  3. ダイアログがさらに表示されるけど気にせずブラウザ画面の操作を開始する
    • f:id:ponsuke_tarou:20201021151810p:plain
  4. 操作を終了する場合は上記ダイアログの[停止]ボタンで終了する

localhostで記録する場合の注意ポイント

localhost:{アプリサーバのポート}/{アプリのURL}で画面の操作を記録をしたところ記録されたポートがアプリサーバの管理画面のポート(Payaraを使ったので4848)の記録しか取れませんでした。アプリでの操作が記録されなかったのです。

f:id:ponsuke_tarou:20201021162659p:plain
右側の[記録コントローラ]にぶら下がったHTTPリクエストの画面

  • localhostで記録する場合
    1. ipconfigでPCのIPアドレスを確認する
    2. {PCのIPアドレス}:{アプリサーバのポート}/{アプリのURL}をブラウザで表示する
      • この時点では「このサイトにアクセスできません」となって画面が表示できません。
    3. 「プロキシを設定する」をする
    4. HTTP プロキシサーバの画面にある[開始]ボタンで記録を開始する
    5. ブラウザをリロードするとアプリケーションの画面が表示される
    6. 操作を記録する
  • 参考 : たぬきさんのメモ帳:【JMeter】HTTPプロキシサーバとアプリケーションのお話