OmniAICreator/Qiita-1.07M huggingface dataset:
APIGatewayでlambdaを呼び出してAPIする

自分用メモ。
サーバレスやAPIベースでアプリを作る時の基礎。

# 1：lambda作る
作る。

# 2：APIを作る
①APIGatewayでAPIの作成をクリック。
②API名をAPI群の総称（システム名とか）で設定。

# 3：リソースを作る
API作ったら画面遷移するので、
①[アクション]⇒[リソースの作成]
②リソース名をメソッド名（getRoomEnvとか）で設定
※この名前がAPIエンドポイントのURL末尾になる。
　http://[固有].execute-api.[リージョン].amazonaws.com/[ステージ]/[リソース]


# 4：メソッドを作る
①[アクション]⇒[メソッドの作成]
②POSTとか好きなのを選ぶ。
③各種設定　
統合タイプ　　　　　　　 ：lambda関数
lambdaプロキシ統合の使用：チェックしない
lambdaリージョン　　　　：呼び出すlambdaのリージョン
lambda関数　　　　　　　：呼び出すlambdaの関数名

# 5-1：GETでパラメータを渡す場合
URL末尾にくっつける[param1="hoge"]をlambdaに渡す設定をする。
①メソッドの実行画面からメソッドリクエストを開く
②URLクエリ文字列パラメータでクエリ文字列の追加をクリック
③[param1]を追加。必須なら必須にチェック
④メソッドの実行画面に戻り統合リクエストを開く
⑤本文マッピングテンプレートをクリック
⑥[テンプレートが定義されていない場合]を選択
⑦マッピングテンプレートの追加：application/json

```bash
{
  "param1": "$input.params('param1')"
}
```
# 5-2：POSTでパラメータを渡す場合
Request bodyをlambdaに渡す設定をする。
①メソッドの実行画面から統合リクエストを開く
②本文マッピングテンプレートをクリック
③[テンプレートが定義されていない場合]を選択
④マッピングテンプレートの追加：application/json

```bash
##  See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
##  This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload
#set($allParams = $input.params())
{
"body-json" : $input.json('$'),
"params" : {
#foreach($type in $allParams.keySet())
    #set($params = $allParams.get($type))
"$type" : {
    #foreach($paramName in $params.keySet())
    "$paramName" : "$util.escapeJavaScript($params.get($paramName))"
        #if($foreach.hasNext),#end
    #end
}
    #if($foreach.hasNext),#end
#end
},
"stage-variables" : {
#foreach($key in $stageVariables.keySet())
"$key" : "$util.escapeJavaScript($stageVariables.get($key))"
    #if($foreach.hasNext),#end
#end
},
"context" : {
    "account-id" : "$context.identity.accountId",
    "api-id" : "$context.apiId",
    "api-key" : "$context.identity.apiKey",
    "authorizer-principal-id" : "$context.authorizer.principalId",
    "caller" : "$context.identity.caller",
    "cognito-authentication-provider" : "$context.identity.cognitoAuthenticationProvider",
    "cognito-authentication-type" : "$context.identity.cognitoAuthenticationType",
    "cognito-identity-id" : "$context.identity.cognitoIdentityId",
    "cognito-identity-pool-id" : "$context.identity.cognitoIdentityPoolId",
    "http-method" : "$context.httpMethod",
    "stage" : "$context.stage",
    "source-ip" : "$context.identity.sourceIp",
    "user" : "$context.identity.user",
    "user-agent" : "$context.identity.userAgent",
    "user-arn" : "$context.identity.userArn",
    "request-id" : "$context.requestId",
    "resource-id" : "$context.resourceId",
    "resource-path" : "$context.resourcePath"
    }
}

```

# 6：テスト
①メソッドの実行画面に戻り左端の[テスト⚡]をクリック
②必要パラメータを入れてテスト

# 7：デプロイ
①リソースツリーで[アクション]⇒[APIのデプロイ]
②ステージはprodとか。URLにくっつく。
　http://[固有].execute-api.[リージョン].amazonaws.com/[ステージ]/[リソース]

# 8：確認
①画面左の[ステージ]⇒[prod]⇒[リソース]⇒[メソッド]
②URLを確認してブラウザやPOSTMANで確認

# 9：お疲れ様でした。
terraformで、他のtfstateファイルのリソース情報を参照する

## 前提
- terraform v0.10.3
- tfstateファイルはS3に配置


## 同じtfstateファイルのリソースを参照する場合
- ディレクトリ構成

```directory
sample/
　├ config.tf
　├ subnet.tf
　└ vpc.tf
```


- tfstateファイルをS3で管理

```sample/config.tf
terraform {
  backend "s3" {
    bucket = "[バケット名]"
    key = "sample/terraform.tfstate"
    region = "ap-northeast-1"
  }
}
```

- VPC作成

```sample/vpc.tf
resource "aws_vpc" "main" {
  cidr_block       = "10.0.0.0/16"
  instance_tenancy = "dedicated"

  tags {
    Name = "main_vpc"
  }
}
```

- Subnet作成時、`sample/subnet.tf`から`sample/vpc.tf`で作成するVPCのIDを`"${aws_vpc.main.id}"`で参照できる

```sample/subnet.tf
resource "aws_subnet" "1a" {
  vpc_id     = "${aws_vpc.main.id}"
  cidr_block = "10.0.1.0/24"

  tags {
    Name = "1a_subnet"
  }
}

```



## 異なるtfstateファイルのリソースを参照する場合
### 参照元のリソース情報をtfstateファイルにoutput
- ディレクトリ構成

```
sample/
　├ config.tf
　├ output.tf ←追加
　├ subnet.tf
　└ vpc.tf
```

- 必要なリソース情報のoutputしておく
  - ここでは作成したVPCのIDを`vpc_id`という名前でoutputしておく

```sample/output.tf
output "vpc_id" {
  value = "${aws_vpc.main.id}"
}
```

- terraform applyしてtfstateファイルにoutputする

```shell
terraform apply
aws_vpc.main: Refreshing state... (ID: vpc-xxxxxxxx)
aws_subnet.1a: Refreshing state... (ID: subnet-xxxxxxx)

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

vpc_id = vpc-xxxxxxxx
```

- S3のtfstateファイル上ではこんな感じでoutputされる(`outputs`部分)

```S3のterraform.tfstate
{
    "version": 3,
    "terraform_version": "0.10.3",
    "serial": 6,
    "lineage": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {
                "vpc_id": {
                    "sensitive": false,
                    "type": "string",
                    "value": "vpc-xxxxxxxx"
                }
            },
            "resources": {
                "aws_subnet.1a": {

...
``` 


### tfstateファイルからoutputしたリソース情報を参照

- ディレクトリ構成(other/で作業)


```directory
sample/
　├ other/ ←追加
　│　├ config.tf ←追加
　│　├ remote_state.tf ←追加
　│　└ subnet.tf ←追加
　├ config.tf
　├ subnet.tf
　└ vpc.tf
```

- sample/other/配下のtfstateファイルをS3で管理

```sample/other/config.tf
terraform {
  backend "s3" {
    bucket = "[バケット名]"
    key = "sample/other/terraform.tfstate"
    region = "ap-northeast-1"
  }
}
```


- S3にあるtfstateファイルを取得する

```sample/other/remote_state.tf
data "terraform_remote_state" "vpc" {
  backend = "s3"

  config {
    bucket = "[バケット名]"
    key    = "sample/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

```


- tfstateファイルから、リソース情報を参照する

```sample/other/subnet.tf
resource "aws_subnet" "1c" {
  vpc_id     = "${data.terraform_remote_state.vpc.vpc_id}"
  cidr_block = "10.0.2.0/24"

  tags {
    Name = "1c_subnet"
  }
}
```
- `sample/output.tf`で定義した`vpc_id`という変数名で参照することができる
GitHub で公開されているドキュメントを継続的に翻訳する


オープンソースに関わっている関係で、これまでいろいろな GitHub 上のドキュメントを翻訳してきました。

たとえば、[WP-CLIのドキュメンテーション](http://wp-cli.org/ja/) とか、[WP-APIのドキュメンテーション](http://ja.wp-api.org/) とか、[ベクトルタイルの仕様書](https://github.com/madefor/vector-tile-spec)などなどです。

多くの場合、オリジナルのドキュメントがそれほど頻繁にアップデートされることがないため、一度翻訳を行えばそれっきりなことが多いのですが、WP-CLIのドキュメントのように頻繁に更新され、それを追いかける必要があるケースがあります。

ところがこれ結構大変で、オリジナルで軽微な修正がはいっても、その修正箇所を探すために全文を確認する必要がでてきてしまいます。

そんなわけで、WP-CLIでは、メンテナーに頼んで変更の度にタグをつけてもらい、追従するときにはタグ間の差分を確認することで、更新箇所を確認するというワークフローをとっています。

例: WP-CLI v1.3.0とv1.4.0の間に更新された差分
https://github.com/wp-cli/wp-cli.github.com/compare/v1.3.0...v1.4.0

いまのところまあまあ上手く機能しているのですが、これをやるにはタグをつけるという新たなワークフローを理解してもらわないといけないので、知人とかじゃないとなかなかうまくいきません。

## Front-End Checklistというドキュメントを翻訳した

数日前に GitHub で Front-End Checklist というドキュメントが GitHub 上で公開されていて、6,000以上のスターを集めていることに気がつきました。

https://github.com/miya0001/Front-End-Checklist

実際、内容を見たらなかなかすばらしいリストだったので翻訳してみたのですが、今回は従来とはちょっとちがう工夫をしてみました。

## 原文はコメントアウトした上で残す

これまで翻訳する際には、元の原文を消して、その上に日本語訳をごりごり書いていましたが、今回は以下のように原文をコメントアウトして残し、その下の行に日本語訳を追記する形にしました。

```
<!--
The **Front-End Checklist** is an exhaustive list of all elements you need to have / to test before launching your site / page HTML to production.
-->

**フロントエンドチェックリスト**は、みなさんがウェブサイトや HTML ページを本番環境で公開する前に確認するべきあらゆる項目を網羅したリストです。

<!--
It is based on Front-End developers' years of experience, with the additions coming from some other open-source checklists.
-->

これは、フロントエンド開発者たちの数年に及ぶ経験にもとづいており、さらに他のオープンソースのチェックリストの内容も追加されています。

<!--
## Table of Contents
-->
```

はい、これ大成功でした。

## オリジナル版の変更をマージする

今回翻訳した Frontend Checklistはかなり頻繁に更新されていて、僕が翻訳している間にも3回ほどアップデートがありました。

この際に、原文が残ってるおかげで Git コマンドでいい感じに差分を抽出できるようになりました。

マージするためのコマンドの手順は以下の通りです。

```
$ git remote add upstream xxxx.git
$ git fetch upstream
$ git merge upstream/master
```

最後のコマンドを実行するとコンフリクトしてるよって言われてしまいますが慌てる必要はありません。

以下は翻訳したドキュメントにオリジナル側の差分をマージした結果を、Visual Studio Codeで開いたスクリーンショットです。

![](https://www.evernote.com/l/ABVNDfbz76NJeo2NwyRXJH1Bpk9pT416U_cB/image.png)

この修正ではオリジナル版に新しい行が追加されたようなんですが、差分を見るとその追加された行がきれいに翻訳版のコメントアウトの中に挿入されていることが分かると思います。

Visual Studio Code にはコンフリクトを解消するための機能があるので、"Accept Both Changes" というボタンをクリックするだけでまあまあうまいことマージしてくれます。

あとは、追加された原文の部分をコメントアウトし（時には移動する必要があることもある）、その下に翻訳を追加していくことで従来よりもはるかに簡単に翻訳を追従させることができます。
Solarisのコマンド（tarのみ）

仕事でSolarisを使用する機会があり、Linuxとはコマンドが微妙に違っていて難しかったので少しずつメモしていこうと思います。

#Solarisとは
サン・マイクロシステムズによって開発されたOSでUNIXとして認定を受けています。
オラクルに買収されて以降、オラクルによって開発されています。
ただ、2017年9月にオラクルが Solaris 開発の大半の社員を解雇したという報道がされており、先があるOSかというと微妙なきがします。

#コマンドの違い
##tar
Unix系のOSを使用するときに、必ず使用するといっても過言ではない書庫を作成するコマンド。
使用することにより、複数のファイルを１つにまとめることができ便利です。必要ないファイルをまとめて保存したり、バックアップしたりするために使用されると思います。
gzip圧縮と同時しているのが多い。

RedHat等では「-z」オプションを使用すると書庫化と同時にgzip圧縮してくれるのだが、Solaris10未満では対応していませんでした。
※Solaris11では「-z」オプションに対応しているみたいなので安心してください。

RedHat、Solaris11では、以下のコマンドで書庫を作成して圧縮できます。

```shell-session
$ tar -zcf dir.tar.gz /dir
```

では、「-z」オプションのないSolaris10未満ではどうするのか。

■gzipコマンドを使用
tarコマンドで書庫化してから、gzipコマンドで圧縮する

```shell-session
$ tar -cf dir.tar ./dir
$ gzip dir.tar
#こっちでもできる
$ tar cvf - ./dir | gzip -c > dir.tar.gz
```
めんどうだが、確実

■gtarを使用する
LinuxのtarコマンドはGNU tarである。そのGNU tarがSolarisには標準でインストールされているのでこちらを使用すると同じように使用できる。
「/usr/sfw/bin/gtar」にインストールされているはずである。

```shell-session
$ /usr/sfw/bin/gtar -zcf dir.tar.gz ./dir
```


また、違いといえば、ファイル名の長さの上限もある。
Solarisのtarでは、ファイル名は100文字までなら対応しているが、100文字を超えるとエラーとなり書庫の対象外となる。
これもgtarを使用すると回避できる。
もしくは、「-E」オプションを使用することでファイル名の制限なしで書庫化することができる。
ただし、「-E」オプションを使用すると拡張ヘッダーが追記されてしまう。そのため、展開する側のOSもその拡張ヘッダに対応していないと展開できない。
そんなことをいちいち調査しないといけないぐらいなら、gtarを使用すればよいのではないかと思います。


また、なにか気づけば書いていこうと思います。
VBAのCall文

VBA(Visual Basic for Applications)はMicrosoft Officeに搭載されている言語（DSL？）である．
VBAの情報源として，[VBA言語リファレンス](https://msdn.microsoft.com/vba/vba-language-reference)と[VBA Language Specification](https://msdn.microsoft.com/en-us/library/dd361851.aspx)という文書がある．

ここではCall文(Call Statement)の書式について解説します．
以下の定義が同じモジュールにあるものとします．

```
Sub test0()
End Sub

Sub test1(a)
End Sub

Sub test2(a, b)
End Sub
```

次の12通りの呼び出しが有効であるかどうかを考えてみます．

1. `test0`
2. `test0()`
3. `Call test0`
4. `Call test0()`
5. `test1 1`
6. `test1(1)`
7. `Call test1 1`
8. `Call test1(1)`
9. `test2 1, 2`
10. `test2(1, 2)`
11. `Call test2 1, 2`
12. `Call test2(1, 2)`

この内の4.はVBEでは入力できない．イミディエイトを使うと入力でき，実行もできることがわかる．

# 各パターン解説

[言語リファレンスのCallステートメントの説明](https://msdn.microsoft.com/ja-jp/vba/language-reference-vba/articles/call-statement)によると，次のようにわかる．

### Callキーワードを省略したら()を付けてはいけない．

1と5と9はCallキーワードと()がないので，実行できる．
2と10はコンパイルエラー．
意外なことに，6は実行できる．

### Callキーワードがある場合は()が必要

4は入力できないので，8と12は実行できる．
3 7 11は全てコンパイルエラーになる．

## 6はなぜ大丈夫なのか？

```
test1(1)
```

引数が1つのため，第1引数の式が`(1)`と解釈されているようです．

# Language Specificationよりわかること

> call-statement = "Call" (simple-name-expression / member-access-expression / index-expression / with-expression)  
> call-statement =/ (simple-name-expression / member-access-expression / with-expression) argument-list 
> [5.4.2.1 Call Statement](https://msdn.microsoft.com/en-us/library/ee200263.aspx)

Callをつけた場合はindex-expressionが使えることになっています．index-expressionでは()に引数を記入します．
一方，Callをつけない場合はargument-listを使って引数を指定しています．
ReactNativeのアップグレード手順

ReactNativeは現在も2週間後ごとにバージョンアップしており新機能の追加やバグフィックスなども盛んに行われています。
ReactNativeで開発していると、リリースされた最新バージョンだと直面していたバグが直っているということもあり、そのときに必要になるのがReactNative自体のバージョンのアップグレードです。
公式にも手順は記されているのですが行った際に詰まったことも多かったため、手順として記載します。

# 前提

* 対象は`react-native init XX` で作成したプロジェクトになります。
* `create-react-native-app`を使用して作成したプロジェクトは別の方法になるようです。(あんまり調べてません)

# 事前準備

ReactNativeの公式を参考にreact-native-git-upgradeをインストールします。
install実施後にバージョン確認ができたら準備完了です。

```
npm install -g react-native-git-upgrade
react-native-git-upgrade -v
```
[Upgrading to new React Native versions](https://facebook.github.io/react-native/docs/upgrading.html)

# アップグレード実施

## ライブラリのunlink
react-native-git-upgradeを実施する前に各ライブラリのlinkを解除しておきます。
理由としてはこの後にあるコンフリクトの解消をしやすくするためです。
プロジェクト配下のpackage.jsonのdependenciesからlinkしているライブラリを見つけて解除します。(どれがlinkしているか分からなかったら、dependenciesの配下を全てlink解除しても空振りするだけなので問題ありません。)
link解除は以下のコマンドで行います。
`react-native unlink パッケージ名`

## バージョンアップ
react-native-git-upgradeを使用してバージョンを上げていきます。

```
react-native-git-upgrade #最新版にする場合はとくにオプションをつけない
react-native-git-upgrade 0.48.4 #特定のバージョンにする場合はバージョンを続けて書く
```

## コンフリクト解消(iOS)
`react-native-git-upgrade`を実施すると、ios配下にあるproject.pbxprojがコンフリクトを起こします。
pbxprojはiosのプロジェクト構成を管理しているファイルであり、基本的にはxcodeに管理を任せておけばいいものですが、たまにコンフリクトして大変なことになります。
ただ、`react-native-git-upgrade`でのpbxprojのコンフリクトの解消はそんなに大変ではありません。
基本的に以下の点に気をつければ、コンフリクトを解消してxcodeでプロジェクトを開けるようになります。

* `<<<<<<< ours` を削除
* `=======`　を削除
* `>>>>>>> theirs` を削除
* pbxprojの,や};などの構造がおかしくなっている部分がないか確認(大体、git差分が出ている部分を見ると何箇所がある)

## ライブラリをlink

xcodeでiosのプロジェクトが開けたら、unlinkコマンドを全てlinkにしてライブラリをlinkし直します。unlinkを全てlinkに置換してしまえばいいと思います。
`react-native link パッケージ名`

## コンフリクト解消(Android)
linkをするとAndroid側で差分が発生しており正常に動かなくなります。
私の環境で発生したのは以下でしたが簡単に解消することができました。

* Androidのsrc配下のMainApplication.javaのgetPackagesでlinkしているパッケージがダブっているので1つにする

## 動作確認
ios、Androidで動作確認ができたらGitにコミットしてアップグレード完了です。

もっと簡単にやる方法も探したいところですが、一旦これで対応できているのでここまでとします。
Visual Studio デバッグモードが止まらない

■現象
csファイル上にブレークポイントを設置してデバッグ実行したのにプログラムが止まらない...
Visual Studioには以下エラー文が書かれている
「ブレークポイントは現在の設定ではヒットしません。このドキュメントのシンボルが読み込まれていません。」


■調査内容１
以下を実行してもダメ...

・「ビルド」→「構成マネージャ」でソリューション構成「Debug」がアクティブになっている
・管理者権限で実行している
Googleのガチセキュリティ「高度な保護機能プログラム」を試してみる，あと仕組みとか

Googleアカウントのセキュリティ，耐不正アクセス性を高められるオプション「[高度な保護機能プログラム](https://landing.google.com/advancedprotection/)」が提供され始めたので，試してみる．

すなわち，この記事は以下の内容ではない；
・自社サーバのセキュリティを高める
・PCのセキュリティを高める
・Google内部のセキュリティを詳解する
・気になる人のGoogleアカウントをクラッキングする

それから，スクリーンショットを貼るつもりが紛失したので，貴方がたの優秀な心の目で見て欲しい．

# tl;dr
まだ早い（Bluetooth周りがいまいち）

#設定
全く難しくない．
[高度な保護機能プログラム](https://landing.google.com/advancedprotection/)
上記リンク先より設定開始．

まずは物理的な認証用のキーが必要となる．
下図の通りリンクで誘導してくれているが，リンク先が米アマゾンである．
日アマゾンで探し直したほうが良い（配送に時間がかかる，あるいはそもそも配送非対応）．

（スクリーンショットがここに貼られるはずだった）

手元に2つとも揃ったら，次画面へ進む．
指示に従いキーを登録．
**Bluetoothキーも登録はUSB経由**で行う．

（スクリーンショットがここに貼られるはずだった）

2キーとも登録が済むと，次画面で警告がでる．
内容は以下の通り（これは別途コピペしておいたので紛失を免れた）．

```
高度な保護機能による変更内容

・ログインには物理セキュリティ キーとパスワードが必要です
　セキュリティ キーが必要なのは、新しいパソコン、ブラウザ、端末で初めてログインするときだけです

・メールを読むには、Gmail か Inbox by Gmail が必要になります
　Gmail と Inbox by Gmail にはどちらもモバイルアプリが用意されています。Chrome でウェブ バージョンも利用できます

・Apple のメールアプリ、連絡先アプリ、カレンダー アプリから Google データにアクセスできなくなります
　iOS アプリから Gmail、コンタクト、カレンダーにはアクセスできなくなります。メール アプリを使用してメールを確認することができなくなります。

・サードパーティ アプリは Gmail、Google ドライブ、コンタクトにアクセスできなくなります
　フィッシングからデータを保護するため、サードパーティのアプリによる Google ドライブ、Gmail、コンタクトへのアクセスはすべて拒否されます

・アカウント復元では、本人確認の特別な手順を行います
　セキュリティ キーを紛失して、アカウントにアクセスできなくなった場合には、アカウントの復元に数日かかります

・2 段階認証プロセスに対応していないアプリではアカウントを使用できなくなります
　一部のアプリでは、2 段階認証プロセスの代わりにアプリ パスワードが使用されます。これらのアプリは引き続き使用できますが、Google アカウントでは使用できません
```
誤解を招くような表現だが，iOSから全く使えなくなるという意味ではないはずだ．
Google製のiOSアプリからは問題ないはずである（推定なのは後述）．
iOSデフォルトのアプリを連携させるユーザが多いため，こういった表記になったのだろう．



上記の警告に同意した上で次画面に行けば，晴れて有効化となる．

（スクリーンショットがここに貼られるはずだった）

#実際どうだったか
Chromebook内のAndroidサイドでの認証に失敗．
iOSでの認証に失敗．

いずれもBluetooth接続時に認識されずタイムアウト．
他の認証手段（SMSで認証コード送信など）は，そもそもこの「[高度な保護機能プログラム](https://landing.google.com/advancedprotection/)」によって選択できなくなっている．
検証機種はChromebook Pixel(2015), iPad Proである．

# 仕組み
折角なので，中心となる物理キー周りの仕組みについても俯瞰的に触れておく．

FIDO(Fast IDentity Online)という仕様が採用されている[^1]．
[^1]: 更に細かく言うと，「UAF(Universal Authentication Framework)」「U2F(Universal 2nd Factor)」という仕様がある．Googleアカウントで現在使われているのは後者．前者はまるまるパスワードを置き換え，後者は二段階認証の代替に留まる．


##なぜよりセキュアなのか
ざっくり言えば，パスワード（に相当するもの）を**サーバ-クライアント間で受け渡さない**からである[^2]．
[^2]: 公開鍵暗号方式を利用し，都度サーバから送られるチャレンジを元に生成するトークンを受け渡すのである．
　
パスワード相当を受け渡ししないため，生体認証にも適している．
（生体認証は変更不可能のため，殊更に漏洩に注意する必要があった）

##問題点
クライアント側でパスワード相当を保存するため，クライアント自体のセキュリティは必要．
（しかし保存しない場合でも必要なのは確か）

##ちなみに
上記の仕組みを理解すれば明らかだが，生体認証や物理キーであること自体にはあまり意味がない．
実際のところ，FIDOとしてはPIN入力も想定されている．
Googleが物理キーを採用したのは，利便性などによるものだろう．

# 雑感


##文字が入力される
認証でも何でもない場面でキーのボタンを押すと，一見ランダムな文字が入力される．
おそらくキーに保存されている，パスワードに相当するものだろうと思われる[^3]．
こういった**不注意による漏洩リスク**はあるかもしれない．
[^3]: 仮にトークンなのであれば，チャレンジなしに何を元に生成しているのか，ということになる．また，あの物理キーにトークンを出力できるほどの処理ができる，とは少し考えにくい．

##もはやパスワード要らないのでは
実はFIDOのうち二段階認証としてのキーなのである．
パスワードを丸々置き換えるためのキーは，現在のところ発売されていないようである．

##セキュリティ高めるなら毎回でいいんじゃないの，手軽だし
上記に同じ．


#おしまい
何か間違いなどあったらマサカってください．

# 参考文献
[FIDO とは](https://k-tai.watch.impress.co.jp/docs/column/keyword/759872.html)
[FIDOの仕組み](https://fidoalliance.org/fido%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF/?lang=ja)（公式サイト）
golang製gtop "gotop"を作ってみました。

# gotop
[https://github.com/bunbunjp/gotop](https://github.com/bunbunjp/gotop)

![gotop_movie.gif](https://qiita-image-store.s3.amazonaws.com/0/30738/730846ab-cb72-27fa-e622-82b21f64792e.gif)


## これはなに？
nodejs製のシステムモニタリングツール[gtop](https://github.com/aksakalli/gtop)をより軽量化する狙いで
golangを使って再実装しました。

コアごとのCPU利用率とメモリの利用率遷移
メモリスワップとネットワーク通信量、ディスク使用量、プロセス一覧の表示を実装しています。
マシンリソースのモニタリングに使ってもらえると幸いです。

## どのぐらい軽くなったのか
CPU利用率が1.7 GHz Intel Core i7のMBAで
gtop 6-7% -> gotop 2-4%

メモリ利用量が
gtop 80-90MB -> gotop 7-8MB

## インストール
```shell-session
$ go get github.com/bunbunjp/gotop
```

## どんな感じで実装してるか
もともとgolangの勉強も兼ねて実装しましたので、実装でイケてない所が多々あるかと思います。
お気づきの点があれば、ご指摘やContributeしてもらえるとうれしいです。
UIの実装は[termui](https://github.com/gizak/termui)を使っています。
システム情報の取得には[gopsutil](https://github.com/shirou/gopsutil)を一部使っています。
パッケージ管理は[glide](https://github.com/Masterminds/glide)を使いました。

正直、見た目はかなりgtopのほうがいい感じですし、操作性もgtopのほうが。。。

## 今後対応したいと思ってること
- Windows対応したい
- process listで選んでkill実行したい

あとUIもgtopに負けないようにいろんなTUIライブラリを試してみたいなと思っています。
TDEを使用したテーブルの.ibdファイルを移行する方法

最近、透過的テーブルスペース暗号化（TDE）を使用したテーブルの.ibdファイルを、
別インスタンスに移行する時の注意点を知ったので、メモしておきます。

#### 【 前提 】
TDEを使用したテーブルであっても、mysqldumpを利用した移行は可能です。
ただし、dumpファイルは復号化された状態（平文）で出力されます。

「平文のファイルは扱いたくない！」という場合は、テーブルスペース（.ibdファイル）を
暗号化された状態のまま、EXPORT / IMPORT する必要があります。

#### 【 やりたいこと 】
1. 暗号化テーブルに対して、「FLUSH TABLES  ... FOR EXPORT」を実行し.ibdファイルなどをコピー可能な状態にする
→ [MySQL5.6 リファレンスマニュアル](https://dev.mysql.com/doc/refman/5.6/ja/flush.html)

2. 各種ファイルを移行先サーバに転送

3. 移行先MySQLで、「ALTER TABLE tbl_name IMPORT TABLESPACE;」を実行し、.ibdファイルなどをインポート
→ [MySQL5.6 リファレンスマニュアル](http://dev.mysql.com/doc/refman/5.6/ja/alter-table.html)

#### 【 注意点 】
TDEを使用したテーブルに対して「FLUSH TABLES  ... FOR EXPORT」を実行すると、
*".cfpファイル"* が生成されます。
このファイルには「TDEの暗号キー2種」の情報が含まれているようで、これを使用することで
移行先MySQLでも既存のテーブルスペースを復号化できるようです。

→ [MySQL5.7 リファレンスマニュアル](http://dev.mysql.com/doc/refman/5.7/en/tablespace-copying.html)
> When exporting a tablespace that is encrypted using the InnoDB tablespace encryption feature, InnoDB generates a .cfp file in addition to a .cfg metadata file. The .cfp file must be copied to the destination server together with the .cfg file and tablespace file before performing the ALTER TABLE ... IMPORT TABLESPACE operation on the destination server. The .cfp file contains a transfer key and an encrypted tablespace key. On import, InnoDB uses the transfer key to decrypt the tablespace key.

#### 【 検証 】
```
mysql> CREATE TABLE enc_t1 (id int, body text) ENCRYPTION = 'Y';
mysql> INSERT INTO enc_t1 VALUES (1, "This is test!"),(5,"MySQL5.7"),(10,"1+1=2");
mysql> FLUSH TABLES enc_t1 FOR EXPORT;

# 別コンソールを立ち上げる
[root@test-server ~]# ls /var/lib/mysql/test | grep enc
enc_t1.cfg
enc_t1.cfp
enc_t1.frm
enc_t1.ibd
[root@test-server ~]# scp /var/lib/mysql/test/enc_t1.* <移行先サーバ>

# 移行先サーバに移動
mysql> CREATE TABLE enc_t1 (id int, body text) ENCRYPTION = 'Y';
mysql> INSERT INTO enc_t1 VALUES (1000, "New server");
mysql> ALTER TABLE enc_t1 DISCARD TABLE SPACE;

[root@test-server2 ~]# cp enc_t1.ibd/.cfp/.cfg/.frm <スキーマのディレクトリ>
[root@test-server2 ~]# chown -R mysql:mysql <スキーマのディレクトリ>

mysql> SELECT * FROM enc_t1\G
*************************** 1. row ***************************
  id: 1
body: This is test!
*************************** 2. row ***************************
  id: 5
body: MySQL5.7
*************************** 3. row ***************************
  id: 10
body: 1+1=2
```

#### 【 補足 】
移行先サーバの鍵（keyringファイル）自体は更新されず、従来のままのようです。

``` 
# 移行元サーバ
$ strings /tmp/keyring
INNODBKey-63ff5824-5411-11e6-b70c-0800272020f4-1AESw
# 移行先サーバ（IMPORT前）
$ strings /tmp/keyring2
INNODBKey-a39cafed-bcfb-11e6-82f3-0800272020f4-1AES
# 移行先サーバ（IMPORT後）
$ strings /tmp/keyring2
INNODBKey-a39cafed-bcfb-11e6-82f3-0800272020f4-1AES
```
[金融機関編]委託先審査で確認されるAWS関連とかその他のあれこれ

## TL;DR

金融機関などへサービス提供する際に業務委託先としてふさわしいかの審査が入ります。
その際によく聞かれるAWSにまつわるあれこれ。
追記していく。。と思う。きっと。


## FISC対応状況

https://aws.amazon.com/jp/compliance/fisc/

FISC各項目に対するAWSの見解が記載されている。
基本的に最新版のFISCに関するリファレンスのみしか記載されていないため、
旧版のFISCに関するリファレンスは必要に応じて保存しておく必要がある。

某DさんやAさんのようなコンサルティングを生業にする企業や、Cloud Integraterと呼ばれるCloudpackさん、Serverworksさん、Class methodさんに問い合わせてみると、旧版FISCについてのリファレンスも入手できるかもしれない。


## データセンター所在地

リージョン毎に都市までは確認可能
https://aws.amazon.com/jp/about-aws/global-infrastructure/

## データセンター入館管理
AWS リスクおよびコンプライアンスホワイトペーパー（日本語） P60より抜粋
https://s3.amazonaws.com/awsmedia/jp/wp/AWS_Risk_and_Compliance_Whitepaper.pdf

```
AWSは、データセンターへのアクセスのための多要素認証メカニズム、および権限のある関係者のみがAWSデータセンターに入場するための追加のセキュリティメカニズムを利用しています。
権限のある関係者は、施設への入場および許可された部屋への入室には、カードリーダーでバッジを使用し、一意のPINを入力する必要があります。
データセンターへの物理的なアクセスは、AWSの電子アクセス制御システムに基づいて行われます。
このシステムの構成では、建物や部屋の入り口ではカードリーダーとPINパッドを、出口ではカードリーダーのみを使用します。
建物や部屋の出口でカードリーダーを使用することで、パスバック防止機能を提供し、許可のない人が許可のある人の後にぴったりついて、バッジなしで入場できないようにします。
```

下記資料の17Pにもデータセンターセキュリティについての情報あり。
http://d36cz9buwru1tt.cloudfront.net/jp/documentation/Security_Compliancev2_2_201612.pdf


## 適用される法律
13.4 準拠法
https://d1.awsstatic.com/legal/aws-customer-agreement/AWS_Customer_Agreement_Japanese.f9491bf0bf362b619a4fc5939c92702edf80bbce.pdf


## フォレンジック調査のサポートについて
PCI-DSS A 1.4に則り、サポートされている。

https://aws.amazon.com/jp/compliance/pci-dss-level-1-faqs/

```
AWSは必要に応じてフォレンジック調査をサポートしますか？

はい。AWS は、DSS 要件の A 1.4 に従い、フォレンジック調査を管理しています。お客様、またはお客様が指定した Qualified Incident Response Assessors（QIRA）は、フォレンジック調査の実行が必要な場合に AWS に問い合わせることができます。
```
【PHP】SSL証明書の有効期限をチェックして、SlackにWebhookで通知

# 概要

PHPで、ウェブサイトのSSL証明書の有効期限をチェックして、有効期限が近いサイトがあれば、Slackに通知したい。

「ツール入れればいいじゃん」という話は、無しでお願いします・・・。お手軽重視！！

# しらべた

自分が使うだけのものなので、あんまり時間をかけずにサクッと作りたかった。ので、とりあえずググりました。

参考にさせていただいたのは、以下の2記事。というか、ほとんどそのまま・・・。

* [PHP で外部サーバーの SSL サーバー証明書の有効期限を取得する](https://qiita.com/suzuki86/items/a971a5cbbcd6179a2695 "PHP で外部サーバーの SSL サーバー証明書の有効期限を取得する")
* [PHPでSlackにメッセージをポストするクラスのサンプル](https://qiita.com/monhan/items/d95ad6e4e698da9e6593 "PHPでSlackにメッセージをポストするクラスのサンプル")

# 実装

サクッと組み合わせたものを、そのまま ~~恥ずかしげもなく~~ 公開。定数の定義位置が微妙なのは、非エンジニアでもドメイン追加しやすいように、という余計な配慮です。

```ssl-watch.php
<?php

/**
 * SSL証明書期限切れ通知用プログラム
 */

// ここに、監視対象のドメインを追加（FQDN）
$domain_names = array(
	'example.com',
	);

// テストしたい場合は1にして手動実行すると、全て通知される。
define('IS_TEST', 0);

// 通知先のチャンネル名。@始まりでDM。
define('SLACK_CHANNEL_NAME', '#ssl_watch');

// Slack Incoming WebHooksのURL
define('SLACK_WEBHOOK_URL', 'https://hooks.slack.com/services/hogehogehogehogehogehoge');

$warning_messages = array();
foreach($domain_names as $domain_name){
	$stream_context = stream_context_create(array(
		'ssl' => array('capture_peer_cert' => true)
		));
	$resource = stream_socket_client(
		'ssl://' . $domain_name . ':443',
		$errno,
		$errstr,
		30,
		STREAM_CLIENT_CONNECT,
		$stream_context
		);
	$cont = stream_context_get_params($resource);
	$parsed = openssl_x509_parse($cont['options']['ssl']['peer_certificate']);

	if(strpos($parsed['subject']['CN'], $domain_name) !== false){

		// 1ヶ月以内の場合は、通知
		$_ssl_limit = date('Y/m/d', $parsed['validTo_time_t']);
		if(IS_TEST || strtotime($_ssl_limit) < mktime(0, 0, 0, date('m') + 1, date('d'), date('Y'))){
			$_warning_message = "【SSL証明書期限】{$domain_name} ({$_ssl_limit})";
		}
	}else{
		// 取得できなかったドメインも通知
		$_warning_message = "【SSL証明書期限】{$domain_name} (取得失敗)";
	}

	if(!empty($_warning_message)){
		if(IS_TEST) $_warning_message = 'TEST '.$_warning_message;

		$warning_messages[] = $_warning_message;
		echo $_warning_message."\n";
	}
}

if(!empty($warning_messages)) SendSlack::send(implode("\n", $warning_messages));
return;

class SendSlack
{
	public static function send($text){
		$webhook_url = SLACK_WEBHOOK_URL;

		$message = array(
			'username' => 'Bot',
			"channel" => SLACK_CHANNEL_NAME,
			'text' => $text,
			"icon_emoji" => ":ghost:",
			);

		$options = array(
			'http' => array(
				'method' => 'POST',
				'header' => 'Content-Type: application/json',
				'content' => json_encode($message),
				),
			'ssl' => array(
				'verify_peer' => false,
				'verify_peer_name' => false,
				),
			);
		$response = file_get_contents($webhook_url, false, stream_context_create($options));
		$ret = $response === 'ok';
	}
}
```

# 実行

日次バッチなどでphpを実行すると、期限が1ヶ月以内に迫った場合は、以下のような感じでSlackに通知される。

```
【SSL証明書期限】example.com (2018/12/31)
```
Enterキー無効化

# 概要
自社サイトを何気なくポチポチしていたら、テキストボックスがフォーカスされている状態で、
Enterキーを押すとサブミットされてしまう場所があったので、その原因と修正方法を記載します。

# 無効化されていなかった項目
パスワードを入力する項目だけが無効化されていませんでした。

![b.jpg](https://qiita-image-store.s3.amazonaws.com/0/206289/7871f4e8-c674-6c77-2770-cd5052407ab5.jpeg)

# どんなコードになってた？

```js
// Enterキー無効化
document.onkeypress = function(e){ // <== receive e
    //check if we have "e" or "window.event" and use them as "evt"
    var evt = e || window.event;
    var keyCode = evt.which || evt.keyCode; //key property also different
    var ref=evt.target||evt.srcElement;
    if(keyCode == 13 && (ref.type == 'text' || ref.type == 'radio' || ref.type == 'checkbox')){
        return false;
    }
}
```

社内に英語でコメント書く人いないから、どこかのサイトからコピペしてきたのかな。

# 原因
`ref.type`に`password`が設定されていないため、`type="password"`なテキストボックスではEnterキーが有効になっていました。

# 対応方法
if文のところを下記のようにしました。

```js
if(keyCode == 13 && (ref.type == 'text' || ref.type == 'radio' || ref.type == 'checkbox' || ref.type == 'password')){
        return false;
    }
```

とりあえず、`type="password"`なテキストボックスがフォーカスされている状態で、
Enterキーを押してもサブミットされなくなりました。
でも、この方法だと問題がありそうです。

## 問題点
1. KeyboardEvent.keyCodeは、Deprecatedになっている。
2. KeyboardEvent.whichは、Deprecatedになっている。

## 解決方法
`KeyboardEvent.key`プロパティを使えば良さそうです。
[KeybordEvent](https://developer.mozilla.org/ja/docs/Web/API/KeyboardEvent)

```js
document.onkeypress = function(e){
    var ref=e.target;
    if(e.key === 'Enter' && (ref.type == 'text' || ref.type == 'radio' || ref.type == 'checkbox' || ref.type == 'password')){
        return false;
    }
}
```

FireFoxは、`Event.srcElement`をサポートしていないようだったので削除しました。
[React Native]react-native-orientationのlandscapeがandroidとiosで逆向きになる

## 背景

- react-native-orientationを使っていたらiosとandroidで動きが違った

## 事象

- iosとandroidでlandscapeLeft、landscapeRightの動きが反対

## サンプル

### ios

- landscapeLeftで端末の右辺が下になる
- landscapeRightで端末の左辺が下になる
- ![ios.gif](https://qiita-image-store.s3.amazonaws.com/0/175213/16e8f923-922f-b9c4-9714-eea21ce61583.gif)

- landscapeLeftした状態
- <img width="287" alt="スクリーンショット 2017-10-23 23.28.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/175213/88f3657b-bbe7-15f0-42c4-2eae1296cd9c.png">

### android

- landscapeLeftで端末の左辺が下になる
- landscapeRightで端末の右辺が下になる
- ![android.gif](https://qiita-image-store.s3.amazonaws.com/0/175213/f5b29f8b-7ad5-1fa6-6a53-94c5b21bf83f.gif)

- landscapeLeftした状態
- <img width="319" alt="スクリーンショット 2017-10-23 23.29.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/175213/0917f24a-a4fd-c7e1-e830-3c8ed93691c4.png">


### ソースコード

```js:App.js
import React, { Component } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import Orientation from 'react-native-orientation';

export default class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <TouchableOpacity onPress={() => Orientation.lockToPortrait() } style={[styles.panel, { backgroundColor: 'skyblue' }]}>
          <Text>lockToPortrait</Text>
        </TouchableOpacity>
        <TouchableOpacity onPress={() => Orientation.lockToLandscapeLeft() } style={[styles.panel, { backgroundColor: 'lightblue' }]}>
          <Text>lockToLandscapeLeft</Text>
        </TouchableOpacity>
        <TouchableOpacity onPress={() => Orientation.lockToLandscapeRight() } style={[styles.panel, { backgroundColor: 'lightgreen' }]}>
          <Text>lockToLandscapeRight</Text>
        </TouchableOpacity>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  panel: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
});
```
```js:index.ios.js/index.android.js
import React from 'react';
import { AppRegistry } from 'react-native';
import App from './App';

AppRegistry.registerComponent('ReactNativeSample', () => App);
```

## 追記

- プルリクを遡ったらios側のLeftとRightを入れ替える怪しげなプルリク発見
- つまりこの動きが意図したものということなのか
- https://github.com/yamill/react-native-orientation/pull/122/files
IoTシステム技術検定(中級)合格に向けて

第0回は試験の概要をまとめておこうと思う。

# 受験の動機

本業はWebエンジニアだが、
工場である程度自由に実験できる環境が周りにあるので、
ちょっくらIoTでも勉強してみるか、ということで受験を決意。

IoTはハードウェアからソフトウェアまで、広範囲な知識が必要となるので、自己研鑽の課題としてはもってこいだとも思う。

# 試験概要

項目|内容
---|---
実施日|第3回 2017年12月2日（土）
実施時間|9:50～11:20
開催場所|東京、横浜、名古屋、大阪、札幌、仙台、さいたま、広島、福岡
受検料|15,100円（税込）<br />会員、協力団体の方は割引がございます。こちらをご覧ください。
試験問題|マークシート80問<br />対応テキスト発売中
問題数|マークシート80問
試験時間|90分
申込期間|2017年9月25日（月）～11月13日（月）

2016/12/10に第1回目が開催されて、今回が第3回目となる。
マークシート80問の出題で、記述試験がないので合格するのは簡単か？
でも合格基準が未公開らしい。珍しい！
単純に正答割合なのか、合格受験者全体偏差値とかなのかも不明。

## 試験の目的

> IoTシステム構築・活用に関する知識を中心に、その習熟度を検定することで、IoTシステム構築に関係する技術者の対応力向上

## 試験レベル

### 上級
+ 高度なIoTシステム、業界固有または業界をまたがるサービスを構築する実践的な専門技術を認定

### 中級(自分が受けるレベル)
+ IoTシステム構築に取り組むための基本技術を認定

### 基礎
+ IoTに関する基礎知識を保持していることを認定

![試験概要](https://qiita-image-store.s3.amazonaws.com/0/172314/3d08db65-ad3c-a154-ed2d-502842f233fd.jpeg)

上図のように基礎・中級・上級とあるらしいが、現在は基礎・中級のみが開催されているっぽい？
私がいきなり中級を受けるのは、基礎は情報処理試験でいうITパスポート的なものだと思ったから。実際はどうか知らん。
工場をスマート化するにあたって基礎だけ知っててもしょうがないと思ったのもある。

### 必要とするレベル、実務の適応

公式によると以下の通り。

資格の種類|必要とするレベル|適用可能な実務レベル
---|---|---
上級|IoTのシステム構築・活用に関する、より実践的な専門技術|IoTシステムについて顧客の要求を理解し、課題の整理のうえ、システムの企画、計画し戦略的提案をおこないます。また、IoTシステム構築のリーダとして活動できます。
中級|IoTシステムを構成する基本技術習得|①IoTシステム構成と構築技術<br />②センサ／アクチュエータ技術と通信方式<br />③IoTデータ活用技術<br />④IoT情報セキュリティ対策技術<br />⑤IoTシステムのプロトタイピング技術<br />IoTシステム全体を俯瞰することができ顧客の要求または提案の要点を的確に把握でき、システム構成の概要が描けます。
基礎|IoTに関する基礎知識の理解|IoT、BD（ビッグデータ）、AI(人工知能）

うーん、基礎のざっくり感。

## 中級のカリキュラム

[受験要項](http://www.mcpc-jp.org/iotkentei/kentei_msg/index.html)の画像を拝借。

![wariai.gif](https://qiita-image-store.s3.amazonaws.com/0/172314/f6ee6a9c-a1ea-ec46-556b-38a0840666ef.gif)

Webエンジニアの私としては、「センサ/アクチュエータ技術と通信方式」あたりが鬼門かなと思う。がんばろ。

# 主催

MCPC(モバイルコンピューティング推進コンソーシアム)

> ワイヤレス通信とコンピュータシステムの連携を図り、モバイルコンピューティングシステムの普及を推進するために、1997年にわが国を代表する移動体通信会社、コンピュータハード・ソフトメーカ、携帯電話・PHSメーカ、システムインテグレータなどで組織されたものです。(2016年9月現在、会員会社数162社)
React NavigationのnavigationOptionsで、ESLintがエラー吐いた

```JavaScript
class ResultScreen extends Component {
  static navigationOptions = {
    title: 'Photo Result Log'
  };

  render() {
    // 省略
  }
}
```

React Navigationを使うにあたって、こんな感じのコードを書いたら`Parsing error: unexpected token = (Fatal)`の表記が。
エディタはAtomを使っています。

[full classes compatibility (with static properties) · Issue #43 · yannickcr/eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react/issues/43)
こちらを参考にして、下記の対応をしました。

1. `babel-eslint`を導入
2. .eslintrcに`"parser": "babel-eslint"`と`"classes": true`の表記を追加。

```
(抜粋)
"parser": "babel-eslint",
"parserOptions": {
    "ecmaFeatures": {
        "experimentalObjectRestSpread": true,
        "jsx": true,
        "classes": true
    },
    "sourceType": "module"
},
```
PythonでローカルデータをEC2インスタンスにコピーする方法

# 概要
EC2インスタンスにファイルやフォルダをごとコピーしたい時のプログラムを作りました。

```python:sftp_to_ec2.py
import os
import paramiko

def _path_join(path, paths):
    """
    パスのジョイン対応（Windows対応版）
    :param path:
    :param paths:
    :return:
    """
    return os.path.join(path, paths).replace(os.sep, '/')


def put_data_to_ec2(local_path, remote_dir,  host, port, user, private_key):
    """
    EC2へデータを格納する処理
    :param local_path:ローカルデータ（転送したいデータ）
    :param remote_dir:リモート保存先パス（ディレクトリ名)
    :param host:　ホスト
    :param port:　サーバポート
    :param user: リモートユーザ
    :param private_key: pemファイルパス
    :return:
    """
    if local_path[-1]==os.sep or local_path[-1]=="/" :
        local_path = local_path[:-1]

    is_dir = os.path.isdir(local_path)
    is_file = os.path.isfile(local_path)

    if not is_file and not is_dir:
        raise FileNotFoundError

    if not os.path.isfile(private_key) :
        raise FileNotFoundError

    pk = paramiko.RSAKey.from_private_key_file(private_key)
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(host, port=port, username=user, pkey=pk)

    sftp = ssh.open_sftp()
    local_dir_name = os.path.dirname(local_path) + "/"

    if is_dir:
        for file_path in get_file_path(local_path):
            file_name = os.path.basename(file_path)
            dir_name = os.path.dirname(file_path)
            dir_name = dir_name.split(local_dir_name)[1]
            remote_dir_tmp = _path_join(remote_dir, dir_name)
            remote_file_path = _path_join(remote_dir_tmp, file_name)
            # サーバのディレクトリ作成
            print("mkdir -p {}".format(remote_dir_tmp))
            ssh.exec_command("mkdir -p {}".format(remote_dir_tmp))
            # サーバにデータをコピー
            print("sftp {} {}".format(file_path, remote_file_path))
            sftp.put(file_path, remote_file_path)
    elif is_file:
        remote_file_path = _path_join(remote_dir, os.path.basename(local_path))
        sftp.put(local_path, remote_file_path)
    sftp.close()
    ssh.close()
```
OpenBSD 6.2 のデスクトップに Mate を導入

# はじめに
Xfce4 をデスクトップ環境として使ってますが、他の環境も試してみたいと考えていましたが、MATE（マテ）を設定してみることにしました。
Mate は GNOME2 からフォークしたデスクトップ環境です。

今回は、OpenBSD 6.2 をインストールして、Xfce4 で日本語デスクトップ環境を構築した環境に、追加で Mate をインストールしてみます。

*OpenBSD 6.2 のインストール方法については以下を参照ください。
[OpenBSD 6.2 のインストール](https://qiita.com/kinichiro/items/728d61ce75c93b3e4041)*

# Mate をインストール

## (1) パッケージのインストール

今回、[OpenBSD で Mate が使えるという情報](https://github.com/jasperla/openbsd-wip/issues/69) を見て試してみることにしたのですが、そこに書かれていたのが以下のように必要なパッケージを全部指定して pkg_add する、というものでした。

以下のコマンドを実行してインストールを行います。

```shell-session
# pkg_add atril caja caja-extensions engrampa eom marco
# pkg_add mate-calc mate-control-center mate-desktop mate-icon-theme mate-media
# pkg_add mate-menus mate-notification-daemon mate-panel mate-power-manager
# pkg_add mate-screensaver mate-session-manager mate-settings-daemon
# pkg_add mate-terminal mate-themes mate-utils
# pkg_add pluma
```

日本語のための scim やフォントはインストール済みなので今回は以下のコマンドは実施済みの想定です。

```shell-session
# pkg_add scim-anthy ja-mplus-ttf-20060520p5
```

## (2) .xsession を作成

ログインディレクトリに Mate 用の .xsession を作成します。

```bash:$HOME/.xsession
export LANG=ja_JP.UTF-8
export XMODIFIERS="@im=SCIM"
export GTK_IM_MODULE=xim
export QT_IM_MODULE=xim
scim -d
exec ck-launch-session mate-session
```

## (3) ログイン

ログインしてみると Mate のデスクトップ環境が起動しました。
日本語入力もできました。
![62-14.png](https://qiita-image-store.s3.amazonaws.com/0/103043/a916bf7b-26d4-5b87-2894-00b88e2b40cc.png)

# まとめ

OpenBSD 6.2 のデスクトップ環境として Mate をインストールしました。
とても簡単にインストールできました。やはりパッケージは偉大です。
使ってみた感想としては、私の1GBしかメモリを割り当てていない貧弱な仮想サーバ環境でも問題ないなあ、という感じです。
Xfce4 だけでなく Mate も使えるという具合に、デスクトップ環境に選択肢があるのは良いことだと思います。
しばらくは Mate を使ってみようと思ってます。

OpenBSD プロジェクトは寄付を募ってます。
メールアドレス指定で日本からでもPayPalでの寄付ができます。
詳しくはこちら： https://www.openbsd.org/donations.html
mac OS XへのPostgresインストール

macへのpostgres install
いろいろと忘れたのでメモ。


##install確認
```
$ brew search postgres
==> Searching local taps...
postgresql ✔        postgres-xc         postgresql@9.5      postgrest
check_postgres      postgresql@9.4      postgresql@9.6
```

installしていない場合は

```
$ brew install postgresql
```

##brew link
#####link
```
$ brew link postgresql
Linking /usr/local/Cellar/postgresql/10.0... 666 symlinks created
```

#####initdb確認

```
$ which initdb | xargs readlink
../Cellar/postgresql/10.0/bin/initdb
```


ちなみにunlinkは

```
$ brew unlink postgresql
```


## version
```
$ postgres --version
postgres (PostgreSQL) 10.0
```
```
$ psql --version
psql (PostgreSQL) 10.0
```

##データベース初期化
```
$ initdb /usr/local/var/postgres -E utf8
The files belonging to this database system will be owned by user <USERNAME>.
This user must also own the server process.

The database cluster will be initialized with locale "ja_JP.UTF-8".
initdb: could not find suitable text search configuration for locale "ja_JP.UTF-8"
The default text search configuration will be set to "simple".

Data page checksums are disabled.

creating directory /usr/local/var/postgres ... ok
creating subdirectories ... ok
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting dynamic shared memory implementation ... posix
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok
```

##起動
```
$ pg_ctl -D /usr/local/var/postgres -l logfile start
```

または

```
$ postgres -D /usr/local/var/postgres
```

停止

```
$ pg_ctl stop -D /usr/local/var/postgres
```


##services起動
#####確認
```
$ brew services list
Name         Status  User Plist
postgresql   stopped
```

#####起動
```
$ brew services start postgresql
==> Successfully started `postgresql` (label: homebrew.mxcl.postgresql)
```

#####停止
```
$ brew services stop postgresql
Stopping `postgresql`... (might take a while)
==> Successfully stopped `postgresql` (label: homebrew.mxcl.postgresql)
```

#####再起動
```
$ brew services restart postgresql
Stopping `postgresql`... (might take a while)
==> Successfully stopped `postgresql` (label: homebrew.mxcl.postgresql)
==> Successfully started `postgresql` (label: homebrew.mxcl.postgresql)
```


##バージョン切り替え

```
$ pg_ctl stop -D /usr/local/var/postgres
$ brew switch postgres 9.6.3
$ initdb /usr/local/var/postgres96 -E utf8
$ brew services restart postgresql
$ pg_ctl -D /usr/local/var/postgres96 -l logfile start
```
anaconda3でTkinterを使ったときにハマった件

Anaconda3をインストールした環境でTkinterを使ってGUIのプログラムを書いて、png画像を読み込もうとしたら、

_tkinter.TclError: couldn't recognize data in image file "xxx.png"

なるエラーが出てハマりました。
コードを見てもおかしなところはなさそうですし、pngファイルが壊れているわけでもない。

Google先生でいろいろ調べると、どうやらAnaconda3でインストールされたTkのバージョンが古いみたい。

conda update tk

をしたら、新しいバージョンのTkがインストールされて、エラーが出なくなりました。
高専セキュコンに参加した話

# はじめに
今日は休校だったので暴風雨を横目にミニキャンプの応募書類とかと戦ってました。devkarasuです。
高専セキュコンに参加したのでWriteup書きます。
とはいえKoHの攻略しようと唸ってる間にチームメイトがJeopardyはほとんど解いてくれたので、私がWriteup書けるのとかほとんどない……。
非常に不甲斐ないので次回は頑張ります。

# Writeup
## 02 ファイル名を探せ
とりあえず`file q`で落としたファイルを調べるとgzipで圧縮されてたので解凍するとtarが出てきたんで更に解凍。
大量のファイルが出てきました。今思えばこの時点で遠回りしていた。
さて、まずはファイル名がBase64っぽかったのでエンコードしました。
文字は出てきませんでした。
ここからドブ沼にハマりました。
ファイル名くっつけてエンコードすればいけんじゃね？って発想のもと、ファイル名をスキャンして連結するシェルスクリプト書きました。これ人生で初めて書いたシェルスクリプトです。ついに手を出してしまった。しぇるげぃきゎめょ。
まあ1文字目同じものだけ連結とかいろいろやったけど出なかったですね。この時点で1日目の深夜27時です。
次の日、眠みが残る頭でおもむろにファイル名のリスト（前日の残骸）からgrep。ふらぐでてきた。

`SCKOSEN{ki_ha_mori_ni_kakuse}`

tarの時点で`strings q | grep SCKOSEN`するのが一番早かったと思います。
思い込みって良くないね(´・ω・｀)

## 06 解凍して解答せよ
flag.zipを解凍するとmasks.pngとxor.pngっていういかにもなファイル。
合成するとフラグがでました。
最初はOpenCVで演算するようワクワクPに頼んだんですが、GIMPで適当にレイヤー重ねるだけでよかった。

`SCKOSEN{simple_visual_cryptgraphy}`

## 17 ファイル送信
落としてきたpcapファイルをWiresharkで開くと、Lenna.pngをPOSTしてlock.zipを貰ってる通信のようでした。
HTTPオブジェクトをエクスポートして、lock.zipを覗くとflag.txtとLenna.pngが。
適当にぐぐってみると既知平文攻撃が使えそうなことがわかりました。
早速PkCrackを落としてきて試してみたんですがセグフォ。
これもワクワクPに投げたら普通に出てきました。ありがとうワクワクP。ありがとうBash on Windows。

~~`フラグ保存してないし忘れた`~~

# おわりに
あとはKoHの80番ポート叩いたりポートノッキングの痕跡探したりファイルシステムを言われるまま構築しようとしたり英語の論文読んだりしてました。ポートノッキング、443の前で探せばよかったのね……。80の前のポートknockしてた。
なんだかんだ楽しかったしモチベも上がったので次回はトップを目指したいですね。

__それでは皆様、お疲れ様でした！__
PHPのarray_filterとin_arrayを使って、特定のキーを持つ要素のみを抽出して新しい配列を作る

#やりたい事

ある配列の中から、特定のキーを持つ要素(複数)のみ抽出して新しい配列として取得したい。
`array_column()`でやろうとしたが、単一のカラムの値しか取得できなかったので、`array_filter`の中で`in_array`を使って特定のキーを持っているか判定するようにした。


#コード
`$baseArray`の中で`$needleArray`のキーを持つ要素のみで構成される配列を作る。

```php
$baseArray = [
'ゴミ処理' => 1500,
'コタツ(布団付き)' => 3000,
'電気グリル鍋' => 2000,
'大きな鍋' => 1000,
'カセットコンロ' => 1500,
'たこ焼き器' => 1500,
'大人数レイアウト' => 4000,
'寿司桶' => 500,
'プロジェクター' => 1500,
'深夜利用' => 5000,
'宿泊(1〜3名様)' => 6000,
'宿泊(4〜5名様)' => 8000,
'コテ' => 1000,
'撮影用ミニライト' => 1000,
'姿見鏡' => 1000,
'サプライズ装飾' => 4000,
'炊飯器' => 1500,
'トースター' => 1500,
'ミキサー' => 1000,
'付けない' => 0,
];

$needleArray = ['ゴミ処理','宿泊(1〜3名様)','電気グリル鍋'];

//ここから処理
$newArray = array_filter($baseArray,function($k) use ($needleArray) {
                return in_array($k,$needleArray,true);
            },ARRAY_FILTER_USE_KEY);

//追記 指摘がありました。こっちの方がスッキリしてる！
$newArray = array_intersect_key($baseArray, array_flip($needleArray));


```




#説明
###`array_filter`
・第１引数・・フィルタリングの対象になる配列
・第２引数
　・・フィルタリング処理。第３引数に`ARRAY_FILTER_USE_KEY`を設定することで、クロージャの中で`$baseArray`のキーを使えるようになります。キーはクロージャの引数に`$k`で渡す。この処理でtrueの場合、該当する要素が結果の配列に格納される。
・第３引数・・オプション

###フィルタリング処理内容
第２引数の処理の中では、`in_array`によって、`$needleArray`の中に`$k`が存在するかをチェックしている。存在する場合のみ該当要素が結果の配列に格納される。

以上。

参考
array_filter()
http://php.net/manual/ja/function.array-filter.php
in_array()
http://php.net/manual/ja/function.in-array.php
SeleniumとPythonで自作サイトをテストする①

#まえがき
テスト、大事な工程なのはわかっているのですが同じ作業の繰り返しになることが多くあんまり楽しくない。。。ただJUnitでテストソースを書いてちまちまカバレッジを上げていくのは楽しかったので、テストソースを書けるようなテストなら少しは前向きに実施できるのかなあと思い、やってみることにしました。

#Seleniumの準備
言語はPythonとJava、どっちもやってみようと思っていますがひとまずPythonで。。。コマンドプロンプトで以下のコマンドを打てばSeleniumをインストールできるみたいです。

`pip install selenium`

早速やってみたけど、エラーで黒い画面が真っ赤に…；
PermissionErrorが出て怒られちゃいました。

コマンドプロンプトは管理者として実行しないといけなかったみたい。
もう一度やり直すと一瞬でインストールできました。
インストールはできた、これを使うにはどうすればいいのかな～＾＾
[ノート]『PHP Cookbook 』と『初めてのPHP』を読む（配列　基礎編）


#基礎編

配列の生成、配列に要素を増加

```php
$array=array($e1,$e2,$e3)
$array=[]
array_push($array,$var)
```

配列のサイズの洗い出し

```php
count($array)
```

特定のキーを持つ要素の調査

```php
array_key_exists($key,$array)　 //返却値の型がboolean
```
特定の値を持つ要素の調査

```php
in_array($value,$array),   　 //返却値の型がboolean
```

特定の値を持つ要素の検索

```php
array_search($value,$array) 　 //返却値がkey
```

配列から文字列を作成

```php
implode($s,$array)
```

文字列から配列へ転換

```php
explode($s,$str)

```
配列を個々の変数に：

```php
list($a,$b,$c,$d)=$array
```


配列のソート
昇順：

```php
sort($array)   //ソート後keyはなくなり、数字に転換
asort($array)　 //ソート後keyは保持
ksort($array)　  //keyでソートする
```

降順

```php
rsort($array)  //ソート後keyはなくなり、数字に転換
arsort($array)　 //ソート後keyは保持
krsort($array)　  //keyでソートする
```
WordpressをAWSの無料証明書でHTTPS化する。

# WordpressをAWS無料証明書でHTTPS化する方法

Wordpressのメディアファイルを、CloudFrontとS3を使って連携する構成です。
ご参考になればと思い書き留めます。

## Wordpressのメディアファイル以外をHTTPS化する。
### 証明書を作成する
まず、Wordpressが置いてあるEC２が属しているリージョンで、Certificate Managerを立ち上げます。
![スクリーンショット 2017-10-23 21.08.28.png](https://qiita-image-store.s3.amazonaws.com/0/52386/590ae2d1-c77a-5b10-8c85-0fa9c263e0cd.png)
こんな感じで作成します。
メールでアクティベートをして、有効化します。

### ロードバランサーに証明書を設定する
ロードバランサーをHTTPSをリスナーに設定して作成します。
![スクリーンショット 2017-10-23 21.11.58.png](https://qiita-image-store.s3.amazonaws.com/0/52386/12ad010c-d053-8eba-2312-0d36b7b0ef81.png)

![スクリーンショット 2017-10-23 21.12.56.png](https://qiita-image-store.s3.amazonaws.com/0/52386/080a1fd7-d702-65f9-2a60-c6f05401e39f.png)

### Route53にロードバランサーを設定する
Aレコードに、AliasをYesにして、表示されたロードバランサーを設定します。

### WordpressにReally Simple SSLを入れます。
プラグインの新規追加から、Really Simple SSLを選択してインストール。
その後、有効化します。
設定は何も変更しないでも動作しました。
これで、メディアファイル以外のHTTPS化が終わりです。

## S3+CloudFrontのCDN構成でのメディアファイルをHTTPS化する
### S3にバケットを作成する
バケットポリシーはいかに設定します。

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::domainname/*"
        }
    ]
}
```

### CloudFront用の証明書をバージニア北部のリージョンに作成する

ここが一番大事です。
<font color="red">バージニア北部に作成しないと、CloudFrontの証明書として使用できません！！</font>

![スクリーンショット 2017-10-23 21.26.44.png](https://qiita-image-store.s3.amazonaws.com/0/52386/e90c4286-d55a-69f7-172a-46e15cfd70f4.png)

### CloudFrontに証明書を登録する
先ほど作ったS3を指定します。
![スクリーンショット 2017-10-23 21.29.10.png](https://qiita-image-store.s3.amazonaws.com/0/52386/17816912-72de-beb4-b751-aee58c54a433.png)

今回は、cdn.domainname.comとしたので、証明書作成のときに、
*.domainname.comとしました。
![スクリーンショット 2017-10-23 21.31.33.png](https://qiita-image-store.s3.amazonaws.com/0/52386/9ad41dc0-3e15-bd0c-1822-86531650e5ea.png)

### Route53に、CloudFrontを登録する
cdn.domainname.comのところに、CNAMEのAlias=Noで、xxxxxxx.cloudfront.netを登録する。

### Wordpressに、Amazon Web Servicesプラグインを入れる
プラグインの新規作成からインストールし、有効化する。
アクセスキーと、シークレットキーを設定しますが、これは、サーバ上のWordpressのフォルダの中にある、wp-config.phpに設定します。

```wp-config.php
define( ‘AWS_ACCESS_KEY_ID’, ‘********************’ );
define( ‘AWS_SECRET_ACCESS_KEY’, ‘********************************’ );
```

### Wordpressに、WP Offload S3 Liteを入れる
プラグインの新規作成からインストールし、有効化する。
プラグイン一覧から、Settingsを選んで、以下のような設定にする。
変更したのは、Copy Files to S3を有効にしたのと、CloudFront or Custom Domainを設定した。

![スクリーンショット 2017-10-23 21.42.27.png](https://qiita-image-store.s3.amazonaws.com/0/52386/907e4b7d-afac-d4fe-7a70-1d62d51b527d.png)

![スクリーンショット 2017-10-23 21.42.21.png](https://qiita-image-store.s3.amazonaws.com/0/52386/a48564be-081b-9e97-1344-8af48848bc43.png)

##　これで完成！！
Rancher や k8s (kubernetes)で使えそうな分散ストレージについて調べてみた

# 動機

* RancherOS を使って、k8s (kubernetes) 環境が簡単に構築できた
* けど、ストレージ周りはどうしよう
    * 手元では NFS を提供するための VM を立てたけど、負けた気がする (:3」∠)_
* 分散ストレージで組めたら、かっこいいなぁ
    * 調査前は Glusterfs くらいしかクラスタ組んだことがなかった

と、思って調べてみたのでアウトプットです。
試したわけではなく、まだ俯瞰しただけです(´・ω・｀)

# k8s (kubernetes) で利用可能なストレージ

* まずは、何が使えて、何が使えないのかを調べます
* 調査対象にする条件
    * 手軽に試せそう(OSS とかわざわざ購入しなくても良さそうなもの)
    * オンプレ (クラウドは手軽に試せるし、動かしても「ふーん」で終わってしまうので)
* で、公式ドキュメントを見てみました
    * https://kubernetes.io/docs/concepts/storage/persistent-volumes/#provisioner

|Provisioner|コメント(小並感)|
|-----------|---|
|AWSElasticBlockStore| 皆様ご存知 AWS の EBS ですね。|
|AzureFile| Microsoft Azure らしい。Azure 詳しくない(´・ω・｀)|
|AzureDisk| 上記と同じ |
|CephFS| OSS の分散ストレージソフトウェア |
|Cinder|OpenStack のブロックストレージ管理を担うものらしい。Nova の後継で今後は Cinder になっていくらしい|
|FC (Fibre Channel)| ストレージとしての FC だそうで。 |
|FlexVolume|こちらは NetApp?|
|Flocker|ClusterHQからリリースされた OSS だそうで。 https://clusterhq.com/flocker/introduction/|
|GCEPersistentDisk| Google Compute Engine ですね。|
|Glusterfs| こちらも OSS の分散ストレージソフトウェア|
|iSCSI| Fibre Channel が対応してるなら、まぁ iSCSI も対応してるよね|
|PhotonPersistentDisk|VMware の　Photon Platform　というものらしいです|
|Quobyte| Quobyte さんが作っているものらしい。詳しくは https://www.quobyte.com/ |
|NFS| 皆さんご存知 NFS|
|RBD| Ceph RBD。 CephFS との違いは後述 |
|VsphereVolume| VMware vSphere Cloud ですかね。VMDK を事前に作っておく必要アリ? |
|PortworxVolume| Portworx さんが作っているストレージソリューションだそうで。詳しくは https://portworx.com/|
|ScaleIO| DELL EMC SCALE IO。詳しくは https://www.dellemc.com/ja-jp/storage/scaleio/index.htm|

# GlusterFS, Ceph とはなんぞや？
* こちらの SlideShare が大変詳しく解説されていたので、そのまま引用させていただきます。
* Ceph と書いてありますが、GlusterFS との比較も書いてあります
* <strong> <a href="//www.slideshare.net/Yuryu/ceph-42632689" title="分散ストレージ技術Cephの最新情報" target="_blank">分散ストレージ技術Cephの最新情報</a> </strong>

また、他にもコンテナのための分散ストレージがないか調べてみました。

# 他にもこんなものがありました
## OpenEBS

> OpenEBS enables the use of containers for mission critical, persistent workloads. OpenEBS is containerized storage and related storage services.

* 公式 URL: http://www.openebs.io/
* レポジトリ: https://github.com/openebs/openebs
* 参考スライド: http://storageconference.us/2017/Presentations/Powell.pdf
* ど ... どれくらい使えるんだろう(´-﹏-`；) EBS と同じように使えるなら、かなり嬉しいとは思うけど。

## Longhorn

> Longhorn is a distributed block storage system built using containers and microservices.

* 公式 URL: http://rancher.com/microservices-block-storage/
* レポジトリ: https://github.com/rancher/longhorn
* 参考スライド: https://www.slideshare.net/ShannonWilliams14/introducing-project-longhorn-april-2016-rancher-online-meetup
* rancher さんが発表した分散ストレージシステム。期待しているけど、k8s には対応していないらしい。
    * swarm には対応予定とのこと
* 2017/10/24 コメントからの追記(ありがとうございます！)
    * どうやら、OpenEBS をベースに利用しているとのこと
    * https://blog.openebs.io/ranchers-longhorn-announcement-solidifies-the-openebs-story-a68f49ef830e

## Rook
> File, Block, and Object Storage Services for your Cloud-Native Environment

* 公式 URL: https://rook.io/
* レポジトリ: https://github.com/rook/rook
* 参考スライド: http://schd.ws/hosted_files/cloudnativeeu2017/98/KubeCon-Berlin-2017-Talk.pdf
* 新進気鋭のプロジェクト、という感じがする。ドキュメントを見たところ、Helm を使ってインストールする感じでしょうか。

# まとまらないまとめ

* 昨今のコンテナのためのストレージシステムは色々と考えられて作られているんだなぁと思いました
* Longhorn は面白そうですが、k8s で利用できるのかまだまだ調査が必要そうです
* Rook あたりを検証できれば、やってみようかなぁと
JavaScriptステップアップリンク集

jsは初心者向けの体系的な学習方法は揃ってるけれども、それ以上のレベルを目指すときの情報が少ない気がしたのでまとめてみる。

## ドキュメント

現代の JavaScript チュートリアル
https://ja.javascript.info/
モダンなjsといえばここ（らしい）

JavaScript Primer
https://jsprimer.net/
こちらもモダンjsの定番（らしい）

はてな教科書 JavaScript 編
https://hatena.github.io/Hatena-Textbook-JavaScript/
入門的な内容だけども他言語経験者向けに要点だけをまとめて記述されているので話が早くて良い。基礎のおさらいに。

GMOペパボ フロントエンド・ SPA 研修
https://github.com/pepabo/frontend-training/
こちらの方が新し目の研修

JavaScript初級者から中級者になろう
http://uhyohyo.net/javascript/index.html
定番

## レベル別の書籍紹介

これらの記事に共通で登場するような本は良書だろう、たぶん。

JavaScriptのレベル別書籍のまとめ
https://gist.github.com/azu/027859e08e284cb8dfe7

JavaScriptのレベル別おすすめ書籍まとめ！初級から上級まで全8冊
https://blog.codecamp.jp/post-31442

【レベル別に紹介！】JavaScriptを勉強できるおすすめの本10選
https://techacademy.jp/magazine/11894

## あわせて読みたい

JavaScript初心者脱却への道のりを考える
https://qiita.com/stkdev/items/cfe4678ac437cb2fdc9f

中上級者になるためのJavaScript【知識編】
https://qiita.com/kenju/items/c7fad62a12cc2809b507