OmniAICreator/Qiita-1.07M huggingface dataset:
GCP - gcloud コマンド

## 認証情報を確認

```
gcloud auth list
```

## k8s関連で使うコマンド

```
kubectl config get-contexts

gcloud container clusters list --project=PROJECT_ID
gcloud container clusters get-credentials CLUDTER_NAME --project=PROJECT_ID --zone=ZONE_NAME
```

## GCP - アカウント管理

### 設定リストを確認

```
% gcloud config configurations list
NAME     IS_ACTIVE  ACCOUNT                 PROJECT      COMPUTE_DEFAULT_ZONE  COMPUTE_DEFAULT_REGION
default  True       アカウント@gmail.com  プロジェクトID
```

### 設定の作成/切り替え

```
# 新しい設定を作成
% gcloud config configurations create setting-name --activate
# アカウントを切り替えるとき
% gcloud config configurations activate setting-name
```

### 設定を追加

```
% gcloud config set core/project プロジェクトID
% gcloud config set core/account サービスアカウント
% gcloud config set compute/region asia-northeast1
% gcloud config set compute/zone asia-northeast1-a
```

### アカウント認証

```
% gcloud auth activate-service-account --key-file ~/Documents/gcp/key/pub-sub-key.json
```

### docker認証

```
gcloud auth configure-docker
```
セッション管理とスレッドモデル(ASP.NET編)

# セッション管理とスレッドモデル(ASP.NET編)

# 結論

複数のセッションの処理はマルチスレッド処理(平行処理)が行われているかもしれないが、同一セッションの内部では処理は一つずつシリアルで処理されているということ。

# 実験

親のaspxでセッションを作成、その後、ko0.aspxとko1.aspxに対して同時にアクセスされるはずなので、その様子を見てみよう。

```vb:oya.aspx
<%@ Page Language="C#" %>

<script runat="server">
</script>

<html>
 <head>
 </head>
 <body>
 <form id="form1" runat="server">
SessionID= <%= this.Session.SessionID %><br><hr>
  <iframe src="ko0.aspx"></iframe><br><hr>
  <iframe src="ko1.aspx"></iframe>
 </form>
 </body>
</html>
```

ko0.phpとko1.phpは同じ内容で、10秒間スリープするだけの処理

```vb:ko0.aspx
<%@ Page Language="C#" %>

<script runat="server">
</script>

<html>
<head runat="server">
</head>
<body>
<form id="form1" runat="server">
<%
 Response.Write("SessionID=" + this.Session.SessionID + "<br>");
 System.DateTime dateTime1 = System.DateTime.Now;
 Response.Write(dateTime1.Hour.ToString() + ":" + dateTime1.Minute.ToString() + ":" + dateTime1.Second.ToString()+ "<br>");
 Session["aaa"] += "abc";
 Response.Write(Session["aaa"] + "<br>");
 System.Threading.Thread.Sleep(10000);
 System.DateTime dateTime2 = System.DateTime.Now;
 Response.Write(dateTime2.Hour.ToString() + ":" + dateTime2.Minute.ToString() + ":" + dateTime2.Second.ToString()+ "<br>");
%>
</form>
</body>
</html>
```

# 結果

![04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182822/fdbda3c4-4269-0302-d55c-e7e52e4e8ab0.jpeg)

ko0.aspx が処理されてから、ko1.aspxが処理されているのがわかる。

時々、セッションがko[0|1].aspxに引き継がれない場合もある...(その時は、個別のセッションなので、ko[0|1].aspxは同時並行に処理される)

# つまり

同一セッションで大量にアクセスしても・・・早く終わるわけではない、ということ

# 考えてみたら・・・

Sessionオブジェクトに複数スレッドから同時アクセスを制御するようなロック処理がないのだから当然といえば当然か・・・

このあたりはClassicASPと互換性(sessionオブジェクトには排他制御がないけど、Applicationオブジェクトにはある・・・とか)を維持しているのかもしれない。

# 戻る

[セッション管理とスレッドモデル](https://qiita.com/tomoki0sanaki/items/b25c267180180e36bcd4)

# 以上
セッション管理とスレッドモデル(ClassicASP編)

# セッション管理とスレッドモデル(ClassicASP編)

# 結論

複数のセッションの処理はマルチスレッド処理(平行処理)が行われているかもしれないが、同一セッションの内部では処理は一つずつシリアルで処理されているということ。

# 実験

親のaspでセッションを作成、その後、ko0.aspとko1.aspに対して同時にアクセスされるはずなので、その様子を見てみよう。

```vb:oya.asp
<html>
 <head>
  <frameset rows="50%,*">
   <frame src="ko0.asp">
   <frame src="ko1.asp">
  </frameset>
 </head>
</html>
```

ko0.phpとko1.phpは同じ内容で、10秒間スリープする関数がないので、しばらく無駄な計算をする処理。
そして、インタプリタ/コンパイラに削除されないように無駄な計算結果を画面に表示するという内容

```vb:ko0.asp
<%
 Dim i
 Dim sum
 Response.Write("Start is " & Now() & "<br>")
 sum = 0.51
 For i=0 To 10000000
  sum = (1.0 - sum) * sum
 Next
 Response.Write("sum = " & sum & "<br>")
 Response.Write("  End is " & Now() & "<br>")
%>
```

# 結果

![02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182822/d9e03db6-4184-634d-6a2b-5be02da4a62b.jpeg)

ko0.asp が処理されてから、ko1.aspが処理されているのがわかる。

# つまり

同一セッションで大量にアクセスしても・・・早く終わるわけではない、ということ

# 考えてみたら・・・

Sessionオブジェクトに複数スレッドから同時アクセスを制御するようなロック処理がないのだから当然といえば当然か・・・

(ちなみにApplicationオブジェクトにはLockとUnLockがあるよ)

参考 : [Applicationオブジェクトのlockメソッド](http://asp.style-mods.net/ref_application/lock.htm)

# 戻る

[セッション管理とスレッドモデル](https://qiita.com/tomoki0sanaki/items/b25c267180180e36bcd4)

# 以上
セッション管理とスレッドモデル(PHP編)

# セッション管理とスレッドモデル(PHP編)

# 結論

複数のセッションの処理はマルチスレッド処理(平行処理)が行われているかもしれないが、同一セッションの内部では処理は一つずつシリアルで処理されているということ。

# 実験

親のphpでセッションを作成、その後、ko0.phpとko1.phpに対して同時にアクセスされるはずなので、その様子を見てみよう。

```php5:oya.php
<?php
echo "<html><head><body>";
echo "SessionId is ";
echo session_id();
echo "<br><hr>\n";
echo "<iframe src=\"ko0.php\"></iframe><br>";
echo "<hr>\n";
echo "<iframe src=\"ko1.php\"></iframe><br>\n";
echo "</body></html>";
?>
```

ko0.phpとko1.phpは同じ内容で、10秒間スリープするだけの処理

```php5:ko0.php
<?php
echo "SessionID is ";
echo session_id();
echo "<br>\n";
echo "start is ";
echo date("H:i:s");
echo "<br>\n";
sleep(10);
echo "  end is ";
echo date("H:i:s");
echo "<br>\n";
phpinfo();
?>
```

# 結果

![01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182822/021faee5-cf61-dc17-0733-3248da6e22cd.jpeg)

ko0.php が処理されてから、ko1.phpが処理されているのがわかる。

# つまり・・・

同一セッションで大量にアクセスしても・・・早く終わるわけではない、ということ

# 考えてみたら・・・

セッション・オブジェクトに複数スレッドから同時アクセスを制御するようなロック処理がないのだから当然といえば当然か・・・

# 戻る

[セッション管理とスレッドモデル](https://qiita.com/tomoki0sanaki/items/b25c267180180e36bcd4)

# 以上
【備忘録】pandas: 重複している値とそれに紐づく値を調べる

# やりたいこと
IDとNAME列のテーブルがあります。
NAMEがいくつか重複していて、どのNAMEでどのIDが重複しているかを知りたいです。

```

>>> df = pandas.DataFrame({"ID":["a","b","c","d","e","f","g","h"], 
"NAME":["Alice","Bob","Chris","Chris","Chris","Dave","Eve","Eve"]})

>>> df
  ID   NAME
0  a  Alice
1  b    Bob
2  c  Chris
3  d  Chris
4  e  Chris
5  f   Dave
6  g    Eve
7  h    Eve
```

# 書いたコード

[pandas.DataFrame.duplicated](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.duplicated.html) 関数で、重複しているデータを抽出します。

```
>>> df2 = df[df["NAME"].duplicated(keep=False)]

>>> df2
  ID   NAME
2  c  Chris
3  d  Chris
4  e  Chris
6  g    Eve
7  h    Eve
```

ChrisとEveが重複していることが分かりました。
ChrisとEveがどのIDなのかをより分かりやすくするため、IDを列方向に並べます。

まずは、[DataFrameGroupBy.cumcount](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.core.groupby.DataFrameGroupBy.cumcount.html) 関数で、NAMEごとに連番を振ります。

```
>>> df2["group_index"] = df2.groupby("NAME").cumcount()

>>> df2
  ID   NAME  group_index
2  c  Chris            0
3  d  Chris            1
4  e  Chris            2
6  g    Eve            0
7  h    Eve            1
```

そして、[pandas.DataFrame.unstack](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.unstack.html) 関数で、NAMEに対応するIDを列方向に並べました。


```
>>> df3 = df2.set_index(["NAME","group_index"])

>>> df3
                  ID
NAME  group_index   
Chris 0            c
      1            d
      2            e
Eve   0            g
      1            h

>>> df4 = df3.unstack()

>>> df4
            ID        
group_index  0  1    2
NAME                  
Chris        c  d    e
Eve          g  h  NaN

```
【秘密計算】Beaver Multiplication Triplesによる効率的な乗算の実現

## はじめに

この記事では、秘密計算(秘匿計算)の実現方法の一つであるMulti-party Computation(MPC)において、乗法を効率的に行うことができる「Beaver Multiplication Triples」について紹介します。ただし、秘密計算やMPC、秘密分散法についての基礎的な知識を前提としております。

秘密計算やMPC、秘密分散法については、以前の記事で解説しておりますので、まずはそちらを参照して下さい。

[https://acompany.tech/blog/technology-confidential-calculation/](https://acompany.tech/blog/technology-confidential-calculation/)

[https://acompany.tech/blog/technology-multi-party-computation/](https://acompany.tech/blog/technology-multi-party-computation/)

[https://acompany.tech/blog/technology-secrets-decentralization/](https://acompany.tech/blog/technology-secrets-decentralization/)

一般に、MPCを用いる方法では、加法と乗法が定義できれば任意の計算を実行することができます。そのため、「Beaver Multiplication Triples」を用いて効率的に乗法が実現できることは、大きなメリットとなります。また、その実現方法はシンプルであるため容易に実装することができます。

それでは詳しく紹介していきます。

## Beaver Multiplication Triples による乗法の手順

ある秘密情報$x$ が任意の線形秘密分散法によってシェアにされている状態を$[x]$ と書くことにします。

ここで、$c = ab$ となる乱数$(a, b, c)$ のシェア$[a],[b],[c]$ を各パーティが保持しているとします。このような乱数をBeaver Multiplication Triples と呼びます。

さらに、各パーティは秘密情報のシェア$[x], [y]$ を保持しています。この時、$z=xy$ となるような$z$ のシェア$[z]$ を各パーティが保持することを目指します。

以下の方法を用いることで計算することができます。

1. 各パーティがローカルで$[d] = [x]-[a],[e]= [y]-[b]$ を計算する
2. 全てのパーティが$[d],[e]$ を公開し、$d, e$ の値を復元する
3. 各パーティはローカルで$[z] = e[a] + d[b] + [c] + de$ を計算する

上記の方法により計算された$[z]$ が$z$ のシェアであることは、以下の式変形によって確認できます。

$$\begin{array}{ccl} z &=& ea + db + c + de \\ &=& (y - b)a + (x - a)b + ab + (x - a)(y - b) \\ &=& xy \end{array}$$

最終的に、$[a],[b],[c],[x],[y]$ を用いることで$[z]$ を各パーティが保持することができました。ここで、この方法はローカルでの加法と定数倍の乗法、通信を用いた2つのシェアの復元を行うだけで実現できることから、シンプルかつ効率の良い方法と言えます。

ただし、$c=ab$ となる乱数の組を用意する方法はシステムの目的や構成に依存するため、絶対的な解はありません。そのため、実装するMPCシステムに適合した方法を実装する必要があります。

## まとめ

この記事のまとめは以下になります。

- Beaver Multiplication Triples で効率的に乗算を実現できる
- 事前計算された3つの乱数のシェアを使用する
- ローカルでの加法と定数倍の乗法、通信を用いたシェアの復号処理だけで実現できる
- 3つの乱数の生成はシステムの構成に依存する

## 参考文献

[https://link.springer.com/content/pdf/10.1007/3-540-46766-1_34.pdf](https://link.springer.com/content/pdf/10.1007/3-540-46766-1_34.pdf)

[https://www.jstage.jst.go.jp/article/isciesci/63/2/63_71/_pdf](https://www.jstage.jst.go.jp/article/isciesci/63/2/63_71/_pdf)

[https://cyber.ee/research/theses/pille_pullonen_msc.pdf](https://cyber.ee/research/theses/pille_pullonen_msc.pdf)
[Cloudian #5] Python(boto3)で、バケットに格納されているオブジェクトをリスト表示してみる


## はじめに
[Cloudian](https://cloudian.com/jp/)は、AWSのS3完全互換のAPIを持ったオブジェクトストレージです。
[前回](https://qiita.com/yamahiro/items/18d6fe34539d78b706dd)は、Python(boto3)で、ファイル転送を行ってみました。

今回は、Python(boto3)で、オブジェクトストレージのバケットに格納されているオブジェクトをリスト表示してみたいと思います。


## バケットに格納されているオブジェクトのリスト表示 / list_objects()
Cloudianにアップロードされたファイル(オブジェクト)の存在を、list_objects()を使って確認します。
list_objects()は多くの情報を Pythonのdict型で返します。  
以下の例では、返された戻り値の中からオブジェクトのキーとサイズ、最終変更日時のみを表示しています。


```python:test.py
import boto3

client = boto3.client(
    's3',
    endpoint_url='https://xxx.yyy.com'
)


# バケット名:pythonbucket1のオブジェクトを全てリスト表示
for obj in client.list_objects(Bucket='pythonbucket1')['Contents']: 
    print(obj['Key'], obj['Size'],
    obj['LastModified'].strftime("%Y/%m/%d %H:%M:%S"))
```


```bash
10mb.dat 10485760 2020/12/14 03:12:38
```


## まとめ
Python(boto3)で、オブジェクトのリスト表示してみました。

[次回](https://qiita.com/yamahiro/items/3be0fe7787a099cf8826)も、Pythonでオブジェクトストレージ/Cloudianをいろいろ操作していきたいと思います。
Azure環境プリザンターでDBから日時を取得してみた

## はじめに
弊社でも利用させていただいているプリザンターの「Advent Calendar 2020」 が盛り上がっているのを拝見し、Qiita初投稿ながら参加させていただきました。

今回は、Azure環境のプリザンターで拡張SQLを実装したときのことを紹介します。
初めて拡張SQLを使ってみたとき少し躓いたので、これを読んでいる方の参考になれば幸いです。
## やりたいこと
Azure SQL Databaseから現在日時を取得し、プリザンター上の項目に出力する。


## ①拡張SQLを用意
[開発者向け機能：拡張機能：拡張SQL](https://pleasanter.net/fs/publishes/422350/edit)

まず、Azure SQL Databaseから日時を取得するための、以下２つのファイル「GetDateDB.json」と「GetDateDB.json.sql」を用意します。


```GetDateDB.json
{
    "Name": "GetDateDB",
    "Description": "日時を取得します。",
    "SiteIdList": null,
    "IdList": null,
    "Api": true,
    "OnCreating": false,
    "OnCreated": false,
    "OnUpdating": false,
    "OnUpdated": false,
    "OnDeleting": false,
    "OnDeleted": false,
    "OnBulkDeleting": false,
    "OnBulkDeleted": false,
    "OnImporting": false,
    "OnImported": false,
    "OnSelectingWhere": false,
    "CommandText": ""
}
```

Azure SQL DatabaseはUTC日時のため、日本時間に合わせるため+9時間してます。

```GetDateDB.json.sql
select dateadd(hour, 9, getdate());
```




## ②Azure上に拡張SQLファイルをアップロード

「App Service」→「高度なツール」→「移動」で Kudu を開く
![高度なツール.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/929469/949a5c12-f556-3387-546d-0570fb585e1e.png)

「Debug console」→「CMD」を開く
![CMD.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/929469/973b8db6-582a-ecbc-84e6-64878fc6471f.png)

以下のファイルパスへ移動
「D:\home\site\wwwroot\App_Data\Parameters\ExtendedSqls>」

①で作成した２つのファイルをアップロード。
![GetDateDB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/929469/9480e164-7b8b-fac1-289a-e2d54d2703ed.png)

これで拡張SQLの設定は整いました。
※一度App Serviceを再起動しないと設定ファイルが反映されないのでお忘れなく


## ③プリザンター上にスクリプトの登録
[開発者向け機能：APIから拡張SQLを実行する](https://pleasanter.net/fs/publishes/1073749/edit)

あとはAPI実行で、②の拡張SQLを実行するだけです。

```getdbDate.js

var DateData = {
    "ApiVersion": 1.1,
    "ApiKey": ApiKey,       //ApiKeyを設定
    "Name": "GetDateDB"
}

$.ajax({
            type:"post", 
            url:"/api/extended/sql",                    // APIのパス
            data:JSON.stringify(DateData),              // JSONデータ本体
            contentType: 'application/json',            // リクエストの Content-Type
            dataType: "json",                           // レスポンスをJSONとしてパースする
        }).done(function(DateData1) { 
            var DateData2 = JSON.stringify(DateData1);  //JavaScriptオブジェクトをJSONに変換
            var objData = JSON.parse(DateData2);

            if (objData.StatusCode == 200){
                var dbDate = objData.Response.Data.Table[0].Column1; 
                $p.set($('#Results_ClassA'), dbDate);   // 結果を出力
            }
        })
```

## ④実行結果
Azure SQL Databaseから取得した日時が、プリザンター上の項目（分類A）へ出力されることを確認！
![実行結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/929469/da4f24d8-2400-0430-5b65-b874e38a4141.png)



## おわり
今年はプリザンターに出会い学びの多い年となりました。
他サービスとの連携など色々試して、今後もQiitaに投稿していきたいと思います。

みなさま、良い年末年始をお過ごしください。
[小ネタ] AWS SAM で Parameter Store 見るときはポリシーテンプレートがいいよ

### ポリシーテンプレートとは

SAM にはポリシーテンプレートという、パラメータを渡すことで自動的に決められた IAM ポリシーを適用してくれる便利機能があります。

https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-policy-templates.html

Lambda 関数は ECS タスクと違って、Parameter Store を環境変数に設定する機能が（まだ）ないので、基本的にコード内で ssm API を使ってシークレット値を取得する必要があります。

Parametrer Store を読めるマネージドポリシー（`arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess`とか）をアタッチしてもいいけど、読める範囲が大きすぎるのでポリシーテンプレートを使ったほうがいいです。

```yml
  SampleFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: sample-function
      CodeUri: .
      Handler: index.handler
      Runtime: nodejs12.x
      Environment:
        Variables:
          PARAMETER_PATH: /my/secret/parameter_path
      Policies:
        # ポリシーテンプレートを使う
        # ParameterName は先頭にスラッシュをつけてはいけないので注意
        - SSMParameterReadPolicy:
            ParameterName: "my/secret/parameter_path"
```

公式ドキュメント見ると、`ParameterName` ではなく `parameterName` になってるんですけど、先頭大文字が正解です。（AWSにフィードバック済み）
Pulumi Automation API でSelf-serviceなPlatformを作ってみる

# はじめに

先日、Pulumiから[Automation API](https://www.pulumi.com/blog/automation-api/) がリリースされました。
Pulumiはこれまでも汎用プログラミング言語で宣言的な環境構成の定義ができていました（TerraformをHCLではなく汎用プログラミング言語で書けるイメージです）が、構成定義だけでなく、ステートの管理や環境への変更適用などこれまでCLIを使って実施していたタスクもプログラム言語内で完結させて実施できるようになりました。

これによって、Pulumiで行っていた環境構成の定義から、定義した構成の実環境への適用まですべてをバックエンドサーバと統合できるようになりました。こうした機能をAPIなどを通じて公開することで、開発者や運用者が簡単に利用できるようになります（PulumiではこのユースケースをPlatform APIと言う名前で呼んでいます）。

今回これを活用して、外部からAPIや簡単なGUIを使って、定義済みの環境を構築したり・変更したりできるもの作ってみたので紹介したいと思います。

ここで紹介しているものは、こちらで公開していますので是非手元で動かしてみてください
https://github.com/hayato1226/dev-platform

# そもそもPulumiって？

主に、Terraformのような構成の定義を汎用プログラム言語で行えるようにしたもの ＋ SaaS上のステート管理などの仕組み（SaaSは使わずに、S3などクラウドストレージ上でステートを管理する方法もあります）です。YAMLやDSLではなく、汎用プログラミング言語で環境構成が定義できるので、既存のプログラミング言語の持っているパッケージ管理の仕組みや、IDEの支援などを受けながら開発できる点に特色があります
。
https://www.pulumi.com/docs/intro/concepts/how-pulumi-works/


# Automation API って何ができるの？

ざっくりいうと、プログラム内から`pulumi up` （Terraformの `terraform apply`） を実行したり、ステートの参照したりと、pulumiコマンドで行っていたようなことが各種プログラム内から実施できるようになるもの。APIドキュメントはこちら https://www.pulumi.com/docs/reference/pkg/nodejs/pulumi/pulumi/x/automation/


# 作ったものの紹介

開発チームのメンバがが必要に応じて、APIやWeb画面を操作して、AWS上に開発用インスタンス群を作ったり消したりできるものです。サンプルとして、モバイル開発をイメージして、Android端末イメージの[Genymotion](https://www.genymotion.com/)をデプロイする構成としています。

## 全体像

Pulumiによる環境管理機能を内包したバックエンドサーバと、APIを通じて環境変更を依頼するフロントエンドサーバから構成されています。

![arc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88184/692de8a5-4761-17c0-78dd-cff3c2444bfa.png)


### 作成する構成
Pulumiで定義したAWS上の構成は下のような構成になります。専用のVPC上にPublicなSubnetを構成し、そこに開発用インスタンスを作成していきます。Security Groupで必要なポートを開けています。

![sample-pf-env.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88184/b8b03c4b-b3b2-699e-7abf-332b087b59c2.png)

## back-end

下の動画は、左ペインがバックエンドサーバ、右ペインの上がクライアント（curl コマンドでAPIを叩いています）、右ペインの下がAWS上のインスタンス状態を表示しています。
下の例では、クライアントの端末から公開されているAPIへ 環境の作成→環境構成変更（端末追加）→環境削除 を順に行っています。単にhttpリクエストを投げるだけで必要な環境が手に入ることが分かるかと思います。
バックエンド側では、ログに`pulumi up`コマンドを実行したときと同じようなログが出力されていることが分かると思います。

![Pulumi-Automation-API-CLI.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88184/50ca4cbd-422c-8176-a3a4-f1a8b9cd3c70.gif)

## front-end

下の動画は、左ペインがバックエンドサーバ、右ペインがブラウザ操作を表しています。
バックエンドのAPIができたので、簡易なWebのUIも作成しました。簡易なUIですが、画面上から環境を作って、端末台数を増減させたり、端末へアクセスしたりと言ったことが可能です。
また、環境のステートはSaaS上で管理されていますので、これまでの変更の変遷や、現在の環境の情報なども見ることができます。

![Pulumi-Automation-API-GUI.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88184/1b0a39bc-dbe2-9b87-19d8-e19ed9b39f30.gif)



# 今後に向けて

Pulumi Automation APIを使うことで、環境の構成をうまくサービス化する手段が実現されました。これは、書籍「[Team Topologies](https://teamtopologies.com/)」の中で言われている、Platformチームが機能の X-as-a-Service 化を実現する際に強力なツールとなるなと感じています。
もし、興味持たれる方がいたら、次は実装内容の解説もしてみたいと思います。


# 参考

* https://www.pulumi.com/blog/automation-api/
    * Pulumi の Automation API を紹介するブログ記事

* https://medium.com/@danfhern/self-service-platform-development-made-easy-with-pulumi-4e2cf150e93c
    * この記事を書くきっかけとなったPulumi Automation APIを使ったセルフサービスプラットフォームのプロトタイプに関する記事

* https://kari-marttila.medium.com/terraform-vs-pulumi-experiences-c41a8774b637
    * TerraformとPulumiの比較はこちら
データベースやSQLって何？【初学者用】

プログラマーが必ずといって理解しておくべき「データベース」の基本についてまとめてみました。

##データベースとは

__データを保存して管理する場所__です。複数のデータを管理し、検索し、編集ができます。

クライアント（検索してきた人、PC）がWebサーバーにリクエストしたら、Webサーバー内にあるApacheとPHPなどが処理してデータベースに接続し、必要なデータを取り出したり編集したりしてくれます。

##RDB（リレーショナルデータベース）とは

主に三つあります。

* MySQL（無料。主に使われている）
* PostgreSQL
* Oracle

##DBの中身

* テーブル（エクセルでいうシート）
* カラム（エクセルでいう列）
* レコード（エクセルでいう行）

##SQLとは

データベースを操作する言語。

DBを操作するための言語（コマンド）あり。

##データベースを操作してみよう

Macの場合ターミナルなどで操作します。

	MySQLにログイン
		mysql -u [ユーザー名] -p

	どのデータベースを使うか選択（use）
		USE [データベース名];

	選択したデータベースが持つテーブルを表示（show）
		SHOW DATABASES;

	テーブルの中身を見る
		SHOW TABLES;

##SQLの基本コマンド

* 検索 「select」
* 追加 「insert」
* 更新 「update」
* 削除 「delete」

他にもありますが、どんどん使って慣れましょう！
youtube-dl で 動画アーカイブをダウンロードして ffmpeg でタイムラプスを作成する

# はじめに
イベント配信後の作業を楽に(時間を短縮)するためのTIPSです。

最近はオンラインイベント一色から徐々にオフラインイベントも再開されつつあります。
オンラインとオフラインのハイブリッド配信も多くなり、配信に係る作業量も多く(倍とまでは言いませんが、1.5倍程度に)なりました。
オフラインイベントは移動や会場毎の配信準備が省略できませんので、少しでも作業時間を減らしたいというニーズがあります。

配信時は時間を掛けてデータをアップロードするのでネットワーク帯域は数Mbps程度あれば十分ですが、動画アーカイブのダウンロード時は自宅や外出先(オフラインイベント会場など)でネットワーク環境が良くない場合、数GBのデータをダウンロードするのに時間が掛かる場合があります。

### before
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/430b1257-e427-fd0e-f38e-53b11eb5de20.png" width="75%">

そこで、クラウド環境で動画アーカイブのダウンロードとタイムラプスの作成を行う方法を考えました。

### after
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/c61d6233-4c54-085c-46f4-8921d2f04a19.png" width="75%">

# インストール
さくらのクラウドサーバーでの使用例です。
Ubuntu 18.04.5 LTS (Bionic Beaver)

```bash
# youtube-dl のインストール
$ sudo apt install -y python
$ sudo curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl
$ sudo chmod a+rx /usr/local/bin/youtube-dl

# ffmpeg のインストール
$ sudo apt install -y ffmpeg
```

# 手順

```bash
# 動画アーカイブのフォーマットコードの一覧表示
$ youtube-dl -F <YouTube動画のURL>
```

```bash:例
$ youtube-dl -F https://youtu.be/iYySh5PkAHE
[youtube] iYySh5PkAHE: Downloading webpage
[info] Available formats for iYySh5PkAHE:
format code  extension  resolution note
249          webm       audio only tiny   62k , opus @ 50k (48000Hz), 53.26MiB
250          webm       audio only tiny   79k , opus @ 70k (48000Hz), 64.02MiB
140          m4a        audio only tiny  138k , m4a_dash container, mp4a.40.2@128k (44100Hz), 138.11MiB
251          webm       audio only tiny  147k , opus @160k (48000Hz), 115.31MiB
278          webm       256x144    144p   95k , webm container, vp9, 30fps, video only, 41.23MiB
160          mp4        256x144    144p  129k , avc1.4d400c, 30fps, video only, 37.92MiB
242          webm       426x240    240p  199k , vp9, 30fps, video only, 59.61MiB
133          mp4        426x240    240p  291k , avc1.4d4015, 30fps, video only, 74.82MiB
243          webm       640x360    360p  354k , vp9, 30fps, video only, 100.83MiB
244          webm       854x480    480p  587k , vp9, 30fps, video only, 151.26MiB
134          mp4        640x360    360p  636k , avc1.4d401e, 30fps, video only, 133.05MiB
247          webm       1280x720   720p 1025k , vp9, 30fps, video only, 313.22MiB
135          mp4        854x480    480p 1186k , avc1.4d401f, 30fps, video only, 218.19MiB
248          webm       1920x1080  1080p 1731k , vp9, 30fps, video only, 502.10MiB
136          mp4        1280x720   720p 2362k , avc1.64001f, 30fps, video only, 414.96MiB
137          mp4        1920x1080  1080p 4391k , avc1.640028, 30fps, video only, 825.61MiB
18           mp4        640x360    360p  224k , avc1.42001E, 30fps, mp4a.40.2@ 96k (44100Hz), 239.40MiB
22           mp4        1280x720   720p 1012k , avc1.64001F, 30fps, mp4a.40.2@192k (44100Hz) (best)
```

```bash
# 動画アーカイブのダウンロード (フォーマットコード: 22の場合)
$ youtube-dl -f 22 <YouTube動画のURL>

# タイムラプスの作成
$ ffmpeg -i <オリジナル動画> -filter:v "setpts=PTS/240" -r 15 -an <タイムラプス動画>
```

最後に作成されたタイムラプスをローカルPCにダウンロードすれば完了です。
RecycleView攻略 3日目 key_viewclassとver2.0.0の新機能

今回は`key_viewclass`とver2.0.0の新機能の二つについて触れたいと思います。といっても特に何か有用な発見をしたというわけではなく、自分でRVの[公式の使用例][examples]を覗いた事があるという人はこの記事からは何も得られないので注意して下さい。

# 環境

せっかくKivy2.0.0が正式にreleaseされたので前回まで使っていた2.0.0rc4ではなく2.0.0を使います。

# key_viewclass

RVは元々は拡張機能として提供されていたのがKivy1.10.0になった時にKivyの標準機能として取り込まれた歴史があるようです。不思議なのが`key_viewclass`は取り込まれた時点で既にあったはずなのに私はずっとそれに気付かず、つい数ヶ月前にようやく気付いた事です。RVのdocは何度も読んだ事があるのになんでだろうか...。

ともかくこの機能はなかなか強力です。`viewclass`を使ったやり方だと一種類の`view widget`しか使えないのがこれを使えば何種類でも使えるからです。

```python
from kivy.app import App
from kivy.lang import Builder


KV_CODE = '''
RecycleView:
    key_viewclass: 'widget'
    always_overscroll: False
    RecycleBoxLayout:
        default_size_hint: 1, 1
        spacing: 5
        padding: 5
        orientation: 'vertical'
'''


class SampleApp(App):
    def build(self):
        rv = Builder.load_string(KV_CODE)
        rv.data = [
            {'widget': 'Label', 'text': "I'm a Label."},
            {'widget': 'Button', 'text': "I'm a Button."},
            {'widget': 'TextInput', 'text': "I'm a TextInput."},
            {'widget': 'Image', 'source': r"data/logo/kivy-icon-64.png"},
        ]
        return rv

SampleApp().run()
```

![Screenshot at 2020-12-18 11-42-58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144673/5f3d7a29-86a5-e77c-cc8d-c420b2003fac.png)

このように`key_viewclass`に与えた文字列を辞書のkeyに、widget名を辞書の値にすることで好きなwidgetをviewclassとして利用できました。

# ver2.0.0の新機能

新機能はviewclassが複数のwidgetで構成されている時に使える"かも"しれない機能です。例えばデータ1件を以下のように横に並べた画像と文字列で表現したいとします。
![Screenshot at 2020-12-18 12-18-47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144673/cd9eb53e-72f9-3115-1ddb-c8be11235f8d.png)
従来のやり方だと例えば以下のようにviewclassを定義して

```yaml
<Row@BoxLayout>:
    image: ""  # A
    desc: ""   # A
    Image:
        source: root.image
    Label:
        text: root.desc
```

以下のような感じでデータを与えていたと思います。

```python
        rv.data = [
            {'desc': "huga", 'image': image_path},
            {'desc': "hoge", 'image': image_path},
            {'desc': "bluh", 'image': image_path},
            {'desc': "aaaa", 'image': image_path},
        ]
```

辞書の鍵と同名のpropertyを用意してあげないといけないのがこれまでのやり方でした(A行)。これが新機能を使うと

```yaml
<Row@RecycleKVIDsDataViewBehavior+BoxLayout>:
    Image:
        id: image
    Label:
        id: label
```

```python
        rv.data = [
            {'label.text': "huga", 'image.source': image_path},
            {'label.text': "hoge", 'image.source': image_path},
            {'label.text': "bluh", 'image.source': image_path},
            {'label.text': "aaaa", 'image.source': image_path},
        ]
```

という風に書き直せます。このようにデータ側から子widgetを参照できるのでpropertyを用意する必要がなくなるわけです。一見すると良い事のように見えますが、データ側がviewclassの構造に依存してしまうので使える状況は限られそうです。

[source code](https://gist.github.com/gottadiveintopython/d4d2882f6d42a075d2d1fe69d18220a3)
[examples]:https://github.com/kivy/kivy/tree/master/examples/widgets/recycleview
git init から git pushまでの流れ


いつも新しいアプリを作成した時に、Gitのリポジトリを作成からGit上でpushできるようにするまでの設定でつまずくので、整理していきたいと思います。
初心者なので、間違いがあったらご指摘いただけると幸いです！


## ①GitHub上で新しくリポジトリを作成
自分のGitHubページ→「Repositories」のページへ行き、右上の青いボタン「New」で新しくリポジトリが作成できます。
![WS000000.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/952495/ed292d8f-b8f3-8760-8395-04813d698343.jpeg)
「Repository name」の部分にリポジトリ名を入力し、下の「Create Repository」をクリック。

今回はほかの項目はとくに触らずにいきます。
他の人に見られたくなかったら「Private」にポチつけます。


## ②GitHubとローカルリポジトリを紐づける
![WS000000.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/952495/52026916-c99a-166f-381c-ea96af455959.jpeg)

こんな画面が出てくるので、Gitをローカルリポジトリのところで立ち上げて、「…or create a new repository on the command line」のところに書いている`git init`から始めて上からコマンドを入力していきます。
`echo "# test" >> README.md`のところは、やってもやらんでもどっちでも！って感じです。笑


```Git:Git
$ git init
```
```Git:Git
$ git add README.md
```
ここは`git add -A`にすれば一括でaddできます

```Git:Git
$ git commit -m "first commit"
```
ここもfirst commitの部分は自分の好きなコメントに変えてもOK

```Git:Git
$ git branch -M main
```
```Git:Git
$ git remote add origin https://github.com/GitHubのユーザー名/リポジトリ名.git
```
これでGitHubと、ローカルのGitを紐づける設定をするイメージです。

```Git:Git
$ git push -u origin main
```
これで設定が完了しました！


## ローカルリポジトリを更新したら
履歴を残すためにも、更新したらgit pushするというのを癖づけておきたいところです。

```Git:Git
$ git add 〇〇
```
〇〇の部分はpushしたいファイル名です。
先ほどと同じく、`git add -A`と打てば全ファイルが一括でaddされます。
（しかも変更がある分だけアップされるという。素晴らしい！！）

```Git:Git
$ git commit -m"コメント"
```
ダブルクォーテーションの中に自分の好きなコメントを入れます。
色々コメントの書き方とかあるみたいですが、ここでは割愛します。

```Git:Git
$ git push origin main
```
main のところは masterだったりするみたいです。
昔はmasterだったけど、色々あり　今はmainなのかな？


これでpushできました～！



## まとめ
私はやり方を教えていただいたのですが
教えていただく前に自分一人でやっても全くわかりませんでした。。
なんかどの記事みても、Git分かってる前提でお話されてるので（こらっ！）

私のような初心者の皆さんのお役に立てばいいな・・・！
【vue.js】TypeError: Cannot read property 'xxxx' of undefined

下記のエラーが出ています！

```vue.esm.js?efeb:628 [Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'index' of undefined"
```

```vue.esm.js?TypeError: Cannot read property 'index' of undefined
```

原因：　DOM はまだ更新されないです！！！！！！

解決方法： 「$nextTick」を使えばokです！

>callback の実行を遅延し、DOM の更新サイクル後に実行します。DOM の更新を待ち受けるためにいくつかのデータを更新した直後に使用してください。callback の this コンテキストは自動的にこのメソッドを呼びだすインスタンスに束縛されることを除いて、グローバルな Vue.nextTick と同じです。

```index.vue
methods:{
    click(){
　　　　　// DOM はまだ更新されない
        this.$nextTick(function(){
　　　　　　　// DOM が更新された
            console.log()
        });
    }
}
```

参考：
[API-Vue.nextTick( [callback, context] )](https://jp.vuejs.org/v2/api/index.html#vm-nextTick)
Rspec: subjectにメソッドを設定した場合に、実行後の状態をテストしたい

# 前提

```ruby
context "ユーザを生成" do
  subject { User.create(name: "John") }
  it "ユーザが1つ生成される" do
    is_expected.to change(User.count).by(1)
  end
end
```
subjectとして、ユーザ生成のためのメソッドを定義しました。
この状態で、生成されたユーザ名が"John"であることを、同じsubjectでテストしたいことがありました。

# うまくいかない場合
```ruby
it "ユーザ名がJohnである" do
  subject
  expect(User.last.name).to eq("John")
end
```
こうしてもUser.lastがnilですよ、というエラーが出て、どうやらsubjectの中身が実行されていない様子。

# 原因究明

```ruby
puts subject.class
```
としてクラスを調べると、

```ruby
Proc
```
と返ってくる。

rubyではProcクラスという、実行コード自体を変数に格納するクラスが存在しています。

したがって

```ruby
subject
```
として書いても

```ruby
"文字列"
```

とか書いているのと同じようなもので、何も起こりません。
したがってProcクラスのオブジェクトとして実行してやる必要があります。

# 解決方法

実行の方法は様々ですが、一例でいうと

```ruby
subject.call
```
があります

したがって

```ruby
it "ユーザ名がJohnである" do
  subject.call
  expect(User.last.name).to eq("John")
end
```
これでパスしました。

別のコンテキストにしろよ、とか、subjectの設定がいまいち、とかもあるかもしれませんが、同じ状況に直面したらぜひご一考をば。

# 全体のコード

```ruby
context "ユーザを生成" do
  subject { User.create(name: "John") }

  it "ユーザが1つ生成される" do
    is_expected.to change(User.count).by(1)
  end

  it "ユーザ名がJohnである" do
    subject.call
    expect(User.last.name).to eq("John")
  end
end

```

以上です。
code-serverをGoogle Colaboratory上で起動する

[前回](https://qiita.com/naru-T/items/2edcb73d68468b17ded7)はRstudio-serverをColab上で立ち上げたのですが今回はcode-serverを立ててみました。　　


[Google Colaboratory](https://colab.research.google.com/)は無償で使用可能なクラウドのNotebook環境です。Notebookも使いやすいのですが、VScodeもまた魅力的なIDEです。[localtunnel](https://github.com/localtunnel/localtunnel)を使うと、ローカルサーバーを外部公開できるということで、これを使ってColab上にcode-serverを立てることができるのでは？と思って作ってみました。

https://github.com/naru-T/VScode_on_Colab_for_geospatial/blob/master/VScode_server.ipynb

👆のnotebookをcolabで開き、一個ずつセルを実行していきます。流れとしては以下のとおりです。
1. 関連ツールのインストール。分析内容により追加、削除してください。自分は地理情報の分析をするので関連のツールを加えています。
2. パスワードの設定
3.localtunnelによるcode-serverの外部公開URLの発行  
  
https://XXXXXXXXXXX.loca.it
のようなURLが発行されるアクセスするとパスワードを求められます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224794/37964bc2-1bcf-6330-d0ad-5c50cfa4640a.png)


パスワードを入力すると、無事code-serverが立ち上がっているのがわかります。ここまで１分くらいですかね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224794/0e82326e-7d02-2ac4-6c4f-5bfdea411612.png)


クリーンな環境ですが、gitも使えるのでgithubやgitlabからデータの入出力を実施することももちろん可能です。
確認ダイアログ(confirm)でユーザー応答を待つ【Nuxt.js】

# はじめに
Webアプリを開発していて必ず欲しくなる確認ダイアログ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/436187/20e6a10c-9b64-39e6-30a4-3d1859da6b23.png)

```
window.confirm('こういうやつ！')
```
と書いてもいいのですが「デザインをこだわるために自作したい！」となって作成しました。


# 目標
作りたかったものは以下です。

- [ ]  テキストが変更できる
- [ ]  前ページ共通で使いまわせる（各ページに配置などしない）
- [ ]  `window.confirm`のように処理を中断して`true / false`を返せる


# ダイアログを作る
ダイアログコンポーネントを作ります(HTML部分は適当&CSSは省略)。

```vuejs:DialogConfirm.vue
<template>
  <div v-if="isShown" class="dialog">
    <p>
      {{ text }}
    </p>
    <div class="dialog__buttons">
      <button @click="ok()">
        OK
      </button>
      <button @click="cancel()">
        Cancel
      </button>
    </div>
  </div>
</template>

<script lang="ts">
import Vue from 'vue';

type Data = {
  isShown: boolean,
  text: string,
  resolve: (v: boolean) => void
}

export default Vue.extend({
  data (): Data {
    return {
      isShown: false,
      text: '',
      resolve: () => {}
    };
  },

  methods: {
    confirm (text: string): Promise<boolean> {
      this.text = text;
      this.isShown = true;
      return new Promise((resolve: (v: boolean) => void) => {
        this.resolve = resolve;
      });
    },

    ok () {
      this.reset();
      this.resolve(true);
    },

    cancel () {
      this.reset();
      this.resolve(false);
    },

    reset () {
      this.isShown = false;
      this.text = '';
    }
  }
});
</script>
```

`confirm`メソッドでpromiseの解決をせず、コンポーネント内のdataに`resolve`を逃がしています。

```
confirm (text: string): Promise<boolean> {
  this.text = text;
  this.isShown = true;
  return new Promise((resolve: (v: boolean) => void) => {
      this.resolve = resolve;
  });
}
```
そしてダイアログ内のボタン押下でresolveしPromiseでラッピングした`boolean`を返します。

```
ok () {
  this.reset();
  this.resolve(true);
},

cancel () {
  this.reset();
  this.resolve(false);
}
```

Vueは`refs`を使うことで子コンポーネントのメソッドを実行できます。
これを利用しダイアログコンポーネントの`confirm`メソッドを叩くことを想定しています。




# 各ページから呼び出せるようにする
作成したダイアログコンポーネントを適当な`layouts`に配置し、呼び出せるようにします。

共通でメソッドを扱いたいため、メソッドを`store`に格納することにしました。
`refs`はVueオブジェクトから取るため、`layouts`の`created`で`store`に格納します。
（もっといいやりかたありそう......）

```default.vue
created () {
  this.$store.dispatch('setConfirmMethod', this.confirm);  // ストアに格納
},

methods: {
  confirm(text: string): Promise<boolean> {
    return (this.$refs.dialog as InstanceType<typeof DialogConfirm>).confirm(text);
  }
}
```

# 使いかた
これで適当なif文内でストアの`confirm`を呼んでやると

```
async click() {
  if (await this.$store.state.confirm('こういうやつ！'))) {
    window.alert('confirm!')
  }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/436187/647e7cdc-5eb8-c058-866a-86a6bdcb6af6.png)

と出てきて確認してくれます。


# おわりに
目標としていた3点を満たすコンポーネントを実装できました。

- [x]  テキストが変更できる
- [x]  `window.confirm`のように処理を中断して`true / false`を返せる
- [x]  前ページ共通で使いまわせる（各ページに配置などしない）
組み込みエンジニアが転職を機にアプリ開発のスクラムマスターにジョブチェンジした話

## はじめに
組み込みソフトウェアエンジニアを10年以上続けてきた私が、スクラムに目覚め、エンタープライズ系のスクラムマスターにジョブチェンジしてみた振り返りをまとめてみました。

内容としては一般的でないものも多い気がしていますが、同じようなキャリアパスを描いている人もそうでない人も参考程度に見ていただければと思います。

## 転職前の状況
ソフトハウス、電機メーカーを転々とし、プログラマ5年程度、プロジェクトリーダ5年程度、何でも屋さんとして様々な~~炎上~~プロジェクトを飛び回る業務を5年程度を経験。
~~炎上~~プロジェクトを飛び回る中で、モチベーションの失われたエンジニアの生産性に限界を感じ、スクラム信奉者になる。

何でも屋さんの後半はモチベーションの高いチーム作りをモットーに、スクラムを勉強しつつ自分の権限範囲内でスクラム開発を導入するも、ウォーターフォールを基準に整理された社内プロセスの壁に跳ね返され、転職を決意する。

転職前時点での知識レベルは以下みたいな感じ。

- CSMの資格あり、スクラムマスター経験はそこそこ
- プロジェクトリーダーの経験はそこそこ 
- プログラミングはここ数年殆どしていない（経験あるのもアセンブラとかCとかJavaとか💦）
- クラウドに関する知識は「何それ？」レベル
- ピエロ適正あり

といった感じで、このレベルでよくエンタープライズ開発を行う部門で採用してもらえたな、というレベルです。（ピエロ適正があると面接での受けが良いんですよね☺️）

## 転職後の所属チームの状況
転職後の所属部門は社内でスクラムを推進する部門で、スクラム開発に集中しやすいよう報告等も最小限に抑えられていました。感激😭

私が配属されたプロダクトは、以下みたいな感じです。

- サービスインから数年経っており運用と開発を並行して実施。
- 全てAWS上でサービスを提供。
- 体制はScrum of Scrumsを採用し３チームで運営。
- プロパとパートナーさんが3:7くらいの割合。

AWSはほぼ知識なし、Scrum of Scrumsは知識としてのみ知っている、といった状態でどうなることかと思っていました。

が、なんとかなるものですね😅。
最初3ヶ月はネイティブエンジニア兼SM補佐として入り、4ヶ月目からはスクラムマスターとして半年以上楽しくやらせてもらっています。

以降で、半年間の間に感じたことをまとめて行きたいと思います。

## 良かったこと
### スクラムマスターに専念できた
自分も経験ありますが、エンジニアからスクラムマスターになると、なまじ知識がある分どうしても開発チームに混じって自分の開発タスクを持ちたくなるのですが、そうすると「スクラムマスターの仕事＜自分の開発タスク」となり失敗することが多いと思うんです。
最近読んだ[SCRUMMASTER THE BOOK](https://www.amazon.co.jp/SCRUMMASTER-BOOK-優れたスクラムマスターになるための極意――メタスキル、学習、心理、リーダーシップ-Zuzana-Sochova/dp/4798166855)にも書いてありました。

今回の私のケースでは、エンジニアとして役に立たない分、スクラムマスターの仕事に絞って取り組めたので、チームにとっても、自分にとっても良い方向に働いたように感じます。
（自慢出来ることではないですが、）開発チームの誰もが私にエンジニアとしての期待をしていない、ってのも大きかったです。

### プログラミング言語の壁は気にならない
私のプロダクトでは、私自身が未経験のVueやPythonだったり使うことが多いのですが、スクラムマスターがコード見ることって殆ど無いんですよね。
ただ、コードから改善のタネを見つけられたりするので、読めるに越した事はないとは思います。

上にも書いたのですが、チームの誰もが私にエンジニアとしての期待をしていない状態なので、私に意見を求める時は要点を整理してくれているので、プログラミングの知識のなさは全く気にならなかったです。

### 組み込み開発もエンタープライズ開発も変わらない
こちらもスクラムマスターの仕事を行う上では、あまり関係なかったです。

私自身が入社前に困るかなと思っていたのは、チームの抱える課題を抽出し適切な改善策が打てるか、という点だったのですが、以下に書いた感じでなんとかなりました。

* 広い視点（設計時の観点不足や試験の自動化の必要性など）で見た時には、課題の抽出方法もそれに対するアプローチも、組み込み開発もエンタープライズ開発も全く同じ。
* 狭い視点（Pythonライブラリの知識不足やリファクタリングの必要性）は、組み込みとエンタープライズで異なる点も出てきますが、その粒度になると振り返りで開発チームから問題点として出てくる。<br>それをきちんと拾い対策をチームで決めていくことで解決できる。

### プロジェクトリーダーの経験は役に立つ
全てのプロダクトに対して言えるわけではないと思っていて、私の担当するプロダクト固有のものだと思いますが、
Scrum of Scrumsを採用している私たちの場合、プロダクトオーナーが全てに対して目が届かず、スクラムマスターにある程度プロダクトオーナー的な視点も求められています（スクラムのアンチパターン😅）。

プロダクトオーナーならこう判断するだろうとか、このレベルならプロダクトオーナーの判断なしでチームで決めて良いはず、などの価値観を合わせることがスムーズに出来たのは、プロジェクトリーダーの経験が活きたなと思います。

## 苦労したこと

### AWSの知識が足りない
フルクラウドで動くプロダクトを担当する以上、最低限の知識がないと話題に付いていけないですね。最初は打合せで議論に付いていけず、知らない単語をメモするだけで精一杯でした。

これは入社前に課題として考えていた点で、会社から研修を受けさせてもらうなどし入社当初に比べると徐々に解消してきています。

### UXへの意識の違い
こちらは入社前には想像していなかった苦労した点です。

組み込み開発だと・・って一括りにしてしまうと怒られそうですが、組み込み開発の場合、製品を使う人が限られているので、作りやすさを優先しUXに対する意識が低くなってしまいがちでした。（ちょっと使いにくいけどマニュアルに書けば良いや的な）

エンタープライズ開発というかBtoCの場合は、使い勝手が悪いとお客さんが離れていってしまうので、その辺りの意識の違いに戸惑うことが多くありました。
UIの仕様だったり、不具合に対する重要度の認識差異だったり。

根本的な解決策は見当たらず、徐々に慣れていくしかないかなと思います。

## こうしとけば良かったなと思ったこと
考えてみたんですけど、浮かびませんでした。
向上心が足りないのかも。。。

自分のこれまでの経験をチームに還元することも、苦労したことに対する取り組みも、自分なりに出来る事は出来たかなと。

## 終わりに
思いのほか長くなってしまいましたが、同じようなことを考えている方の参考になれば嬉しいです。

最初からスクラムマスター目指す人っていないと思っていて、そう考えるとスクラムマスターへのキャリアパスって色んなルートがあると思います。
ちょっとでも興味ある方いれば、スクラムマスター面白いので是非挑戦してほしいです！！
現場で使われるディープラーニング手法8選を一切コードを使わず解説

こちらはまとめ記事になります。


###画像分類編：（12/6投稿）
https://qiita.com/kanicream/items/cd62a2db68531e53a5b4

###SSD編: （12/18投稿）
https://qiita.com/kanicream/items/6e1b44ea6ff90843aee7

###セマンティックセグメンテーション編:執筆中

###Open Pose編:執筆中

###GAN画像生成編:執筆中

###GAN異常検知編:執筆中

###Transformer編:執筆中

###Bert編:執筆中
普段のタスク管理にNotionを使ってみた

本記事は、[サムザップ Advent Calendar 2020 #2](https://qiita.com/advent-calendar/2020/sumzap2) の12/21の記事です。
昨日の記事は@dimetrodonさんの[『クリーンアーキテクチャ』を雑に読む](https://qiita.com/dimetrodon/private/bf76e0b6a85f98d1a3e1)でした。

# はじめに

チーム運営を効率化し、自然とノウハウが貯まる構造を作るためにNotionを利用し始め、
成果が出始めてきましたので、どういう風に普段Notionを使っているのか、
どういう点で良いのかを伝えられたらと思います。

# なぜNotionを使っているのか？

Notionについての詳細はあちこちに転がっているので特に言及しませんが、私達のチームでは下記に利点を感じて使用を開始しました。

1. カレンダー、テーブルやボードなど様々な表現方法を持っており、ノウハウを貯めて検索しやすい構造を持っている
2. テンプレートを簡単に作れる
3. リアルタイムに複数人で編集できる
4. ゲストユーザーを簡単に追加できる
5. 会社全体で使用するツールになる可能性を秘めている

特に個人的には5が大切で、会社全体で利用しないとノウハウが会社に貯まらず成果が限定的になってしまいます。

# 実際の使用例

## タスク管理

チーム関連のタスクをこなす時はたいていタスクを作っています。

タスクリストは下記のようなイメージです
![普段のタスク管理にNotionを使ってみた aa0b0aee05fb4b47932d90e550b36533.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893591/a1026ff2-1eda-18eb-e12f-62a6f566f2bf.png)
テンプレートも用意していて下記はなるべく書くようにしています。

---
### 概要
ページを作る際はタスクに限らず何を書くのかを必ず書くようにしています。
1,2行でまとめることで、何をかけば良いのか頭が整理されるのでオススメです。

### 要件整理
上記の概要をさらに具体化し要件として洗い出します。

### 実装
タスクの内容にもよるのですが、実装タスクの場合はどこをどう実装するのかを事前に文字で書き起こしておきます。
テーブル作成やマスタ作成も含む、実際に作業する内容を全部書き出します。
ここで書き出すことで、抜け漏れや考慮漏れを洗い出すことができます。

### 参考
このタスクに関係するチケットのURLや仕様書のURL、このタスク関連の話をしたslackのリンクなど、関係するリンクを貼って、悩んだらすぐに参照できる状態にしておきます。

---

（実装タスクではないですが、サンプルを添付しておきます）
![普段のタスク管理にNotionを使ってみた aa0b0aee05fb4b47932d90e550b36533 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893591/661526a7-1978-a328-8fd9-e9f3d018ccd0.png)

現時点では使い始めて一ヶ月くらいなのでタスクリストも20件くらいなのですが、将来的にはDB化して、他プロジェクトで同様な実装の時に参考にできるようなノウハウの溜まり場になればと思っています。


## チーム朝会
私のプロジェクトでは毎朝、朝会を実施してお互いのタスクについて共有しています。
Notionはカレンダービューがあるので毎日の定例MTGの管理には最適です。

（実際の使用例はこちら）
![普段のタスク管理にNotionを使ってみた aa0b0aee05fb4b47932d90e550b36533 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893591/2df935f3-057a-00f3-cbd5-8beb0d4f89f7.png)
下記のようなテンプレートを作っておけば、簡単にフォーマットが作成できて便利です。
![普段のタスク管理にNotionを使ってみた aa0b0aee05fb4b47932d90e550b36533 3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893591/47bf9b36-535e-d980-9ec3-f05662cf68b3.png)
朝会のテンプレートは毎日苦もなく書けるようにできるだけシンプルにしておき、
「ふりかえり」と「今日やること」が人数分並び、最後に「全体共有」という構成になります。

---

### ふりかえり
前の営業日に何をやっていたのかを書きます。
毎朝書くのも手間なので、前日にやったことを翌日の朝会のページに書いておきます。
（ですので毎朝翌日のテンプレートを作る運用になっています）

### 今日やること
基本的に毎朝この今日やることを書きます。
前日に書いた「ふりかえり」があるのでスムーズにその日やることを書き出せます。
また、明日やりたいことの備忘録として書くこともできます。

### 全体共有

最後にチーム全体に共有したいことを書きます。
普段の勤怠連絡からメンバーへのメッセージ、備忘録等自由に使っています。

---

こんな感じで朝会開催時にはすべて書き込んだ状態なので、パッと見でお互いが何をやっているのかがわかり、気になる点にすぐにツッコミをいれて共通の認識を抱けるのでだいぶ重宝しています。

毎朝メンバーがやっていることを話し、それを聞いてリーダーがメモをするだけの朝会（以前そういう時代もありました）をやっているような人には是非オススメです。

# さいごに

今回は紹介していませんが、MTG議事録としても重宝していますし、他にもアイデア次第で普段の業務を効率化することができると思いますので、いろいろ試していきたいです。

将来的には普段の業務を行うだけで自然とノウハウが貯まり、会社全体としてもメンバーの誰でも他のメンバーのノウハウを利用できる状態を作れればいいなと思っています。


明日は@ohbashunsukeさんの記事です。お楽しみに！！
Vue+Typescript 変更を追跡する（ちゃんとWatchする）ようなオブジェクトの扱い方

[前回、Vue + Typescriptで配列の変更を検知しない問題について書きました。](https://qiita.com/WitTarou/items/17d24b15c61fc4678e78)

今回はObjectの扱いについてですが、
Objectは配列と比べるとかなり気楽に扱えます。

というのも、Typescriptだと未定義・未initializeのオブジェクトを使うことはほぼないので、
公式に記載されているような以下のアウトな形式

>Vue はプロパティの追加または削除を検出できません。

```typescript

const vm = { a: 1 }
// `vm.a` は今リアクティブです

vm.b = 2
// `vm.b` はリアクティブでは"ありません"
```

ですが、これもVue + Typescriptではおそらく以下のように書く必要がありますので

```typescript
// 型を定義してあげる必要がある。ここで上の例のようにbが抜けていると、そもそもコンパイルが通らない
interface Vm {
  a: number;
  b: number;
}

@Component({
  components: {
  }
})

export default class Qiita extends Vue {
  // 初期化してあげないといけない（これはStateとかに持たせたほうが管理が楽です）
  private vm: Vm = {
    a: 0,
    b: 0
  }

```

これであれば、変更は正常に検知されます。
Typescriptの強みですね。

もちろん、状況によっては`any`型などでオブジェクトを定義しなければならない局面もあるでしょう。
その場合は、公式にあるように`Object.assign`を使えば解決することができます。

```typescript
this.vm = Object.assign({}, this.vm, { a: 1, b: 2 })
```

基本問題になるのは前回解説した配列で、
配列が空でv-forを回していたりすると、検知できない！といったケースが多いです。

オブジェクトに配列があるデータなどの扱いは注意しましょう！というお話しでした。
jQueryの.text()について

## はじめに
最近、業務で要素内にテキストを追加しなければいけないという状況がありました。1ヶ月前くらいにも同様のことをしたのに忘れてしまい再度調べることに。。。
次こそは忘れないようQiitaに投稿することにしました！
また、「.text()」は意外と便利なのでは？と思いまして投稿することで誰かの役に立てれば良いかと思います。

## .text()とは
HTML内の要素内にあるテキスト情報を追加、変更できるメソッド。

## 実行結果
```HTML
<body>
<h1></h1>
<p>Hello!</p>
 
<script>
$('h1').text('あいさつ');
const title = $('h1').text();
const message = $('p').text();

console.log(title);
console.log(message);
</script>
</body>
```

```console
あいさつ
Hello！
```

## 解説
- .text('あいさつ')：()内に追加したいテキストを記述します。
- $('p').text()：pタグ内のテキストを取得できます。

## .text()と.html()の違いについて
調べると.text()と.html()は似ているようでした。
大きな違いは以下かと思います！

- .text()：テキストの取得・変更
- .html()：HTMLの取得・変更

## 実行結果
```HTML
<body>
<div>
  <p>Hello!</p>
</div>
 
<script>
const textElement = $('div').text();
const htmlElement = $('div').html();

console.log(textElement);
console.log(htmlElement);
</script>
</body>
```

```console
Hello！
<p>Hello!</p>
```

## さいごに
まだ.text()について明確に理解はできていませんが、今後使用していきながら学んで行こうと思います！
π × π ÷ 12

ライプニッツの公式が、逆正接関数 $\left( tan^{-1}{x} \right)$ の $x=1$

```math:
\begin{eqnarray}
\tan^{-1}{x}
& = & \frac{x^1}{1} - \frac{x^3}{3}
 + \frac{x^5}{5} - \frac{x^7}{7}
 + \frac{x^9}{9} - \frac{x^11}{11}
 + \cdots
\\
\tan^{-1}{1}
& = & \sum_{n=0}^{\infty}{ \frac{ \left( -1 \right) ^n }{ 2n + 1 } }
 = \frac{1}{1} - \frac{1}{3}
 + \frac{1}{5} - \frac{1}{7}
 + \frac{1}{9} - \frac{1}{11}
 + \cdots
 = \frac{\pi}{4}
\end{eqnarray}
```

らしいので、偶数分母の式を作ってみたくなる。Mathematica に計算してもらうと

```math:
\sum_{n=1}^{\infty}{ \frac{ \left( -1 \right) ^{n+1} }{ 2n } }
 = \frac{1}{2} - \frac{1}{4}
 + \frac{1}{6} - \frac{1}{8}
 + \frac{1}{10} - \frac{1}{12}
 + \cdots
 = \frac{ \log{2} }{2}
```

になって、円周率が出てこない。試しに、分母を二乗してみると

```math:
\sum_{n=1}^{\infty}{ \frac{ \left( -1 \right) ^{n+1} }{(2n)^2} }
 = \frac{1}{2^2} - \frac{1}{4^2}
 + \frac{1}{6^2} - \frac{1}{8^2}
 + \frac{1}{10^2} - \frac{1}{12^2}
 + \cdots
 = \frac{\pi^2}{48}
```

円周率がでてきた。これは

```math:
\begin{eqnarray}
\sum_{n=1}^{\infty}{ \frac{ \left( -1 \right) ^{n+1} }{n^2} }
& = & \frac{1}{1^2} - \frac{1}{2^2}
 + \frac{1}{3^2} - \frac{1}{4^2}
 + \frac{1}{5^2} - \frac{1}{6^2}
 + \cdots \\
& = & \frac{1}{1^2} + \frac{1}{2^2}
 + \frac{1}{3^2} + \frac{1}{4^2}
 + \frac{1}{5^2} + \frac{1}{6^2}
 + \cdots \\ 
&  & - 2 \left(
   \frac{1}{2^2} + \frac{1}{4^2}
 + \frac{1}{6^2} + \frac{1}{8^2}
 + \frac{1}{10^2} + \frac{1}{12^2}
 + \cdots \right) \\
& = & \sum_{n=1}^{\infty}{ \frac{1}{n^2} } - 2 \sum_{n=1}^{\infty}{ \frac{1}{ \left( 2 n \right)^2 } } \\
& = & \sum_{n=1}^{\infty}{ \frac{1}{n^2} } - \frac{1}{2} \sum_{n=1}^{\infty}{ \frac{1}{n^2} } \\
& = & \zeta\left(2\right) - \frac{1}{2} \zeta\left(2\right) \\
& = & \frac{\pi^2}{6} - \frac{1}{2} \frac{\pi^2}{6} \\
& = & \frac{\pi^2}{12} \\
\end{eqnarray}
```

で、$\zeta\left(2\right)$ のお仲間でした。ついでに思いたものを Mathematica で試してみると

```math:
\begin{eqnarray}
\sum_{n=1}^{\infty}{ \frac{1}{n\left(n+1\right)} }
& = & \frac{1}{1\cdot2} + \frac{1}{2\cdot3} + \frac{1}{3\cdot4} + \cdots = 1 \\
\sum_{n=1}^{\infty}{ \frac{1}{n\left(n+1\right)\left(n+2\right)} }
& = & \frac{1}{1\cdot2\cdot3} + \frac{1}{2\cdot3\cdot4} + \frac{1}{3\cdot4\cdot5} + \cdots = \frac{1}{4} \\
\sum_{n=1}^{\infty}{ \frac{\left(n-1\right)!}{\left(n+k\right)!} }
& = & \frac{1}{k! k} \\
\sum_{n=1}^{\infty}{ \frac{1}{n^2\left(n+1\right)^2} }
& = & \frac{1}{1^2\cdot2^2} + \frac{1}{2^2\cdot3^2} + \frac{1}{3^2\cdot4^2} + \cdots = \frac{\pi^2}{3} - 3 \\
\end{eqnarray}
```

を返してくれた。
現場で使われるディープラーニング手法8選を一切コードを使わず解説 SSD編

# 概要
このpartでは物体検出の仕組みについて解説します。

## 物体検出
物体検出とは、1枚の画像内に含まれている複数の物体に対して、その位置とラベル、信頼度を計算するタスク。物体の位置を示す枠のことを「バウンディングボックス」という。

## SSDによる物体検出
SSDは物体検出用のモデルであり、入力が300*300のものと、512*512のものがある。SSDではまず、デフォルトボックスと呼ばれる大きさの決まった枠を一定数用意し、それをどう動かせば物体を囲めるかという情報である「オフセット」を学習の対象とする。その後、一定以上の信頼度がでた枠のみを最終出力としている。

## SSDモデル
![SSD_network.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935441/5aeac297-3bb3-f494-6ba8-8ff77bd6a492.jpeg)
前半はVGG16のモデルを流用しており、後半にextraと呼ばれるSSD特有の層が並ぶ。
SSDでは、ネットワーク途中の合計6箇所でさまざまな大きさの特徴量マップを抽出しており、それぞれはSorce1~6と呼ばれている。この大きさのことなる6枚の特徴量マップを、場所を特定するlocモジュールと信頼度を計算するconfモジュールに入力することになる。

## SSDの順伝搬関数
SSDの順伝搬関数では、Non-Maximum Supressionという関数を使用する。これは、同じ物体に対して複数のバウンディングボックスが囲みに行っている場合、最も信頼度の高いものだけを残して他のボックスを消すという作業である。

この順伝搬関数には、vggモジュール、locモジュール、confモジュールを伝搬して作成された
1.デフォルトボックス(画像数,位置情報)=(8732,4)
2.オフセット情報(batch_num,DBox数,位置情報)=(batch_num,8732,4)
3.信頼度情報(batch_num,DBox数,ラベルごとの信頼度)=(batch_num,8732,21)
が入力される。
(画像枚数8732やクラス数21はVOCデータセットを利用した場合の数値。）

## Detectクラス
上記1,2,3の情報とNon-Maximum Supression関数を利用し、所望の物体検出結果であるバウンディングボックスとそのラベル、信頼度が残る。これをDetectクラスとして実装する。

## SSDクラス
phase=train（学習）の時は上記1,2,3まで、phase=inference（推論）の時はDetectクラス全体までが実行されるように実装する。

## 損失関数の定義
match関数：
あるDBoxがあるBBoxにかぶっていた時、そのかぶっている割合をjaccard係数と呼ぶ。どのBBoxにもかぶっていないDboxはjaccard係数は0である。また、jaccard係数が0.5以上のDBoxはポジティブDBoxと呼び、かぶっているBBoxまでの移動情報であるオフセットと物体のクラスを正解ラベルとして学習する。一方、jaccard係数が0.5未満のDBoxはネガティブDBoxと呼び、ラベル情報:backgroundのみを正解ラベルとして学習する。
この関数により、DBoxと正解のBBoxから、
ポジティブDboxには教師データとして「オフセット」と、「確信度」の情報が、
ネガティブDboxには教師データとして「確信度」のみの情報が、
それぞれ与えられる。

Hard Negative Mining:
物体検出に用いられる画像のほとんどの面積は物体ではなく背景で構成されている。そのため、必然的にDBoxの大半はネガティブDBoxに割り振られ、学習時ほとんどが背景クラスを学習することになってしまい、非常にアンバランスである。そこでHard Negative Miningと呼ばれる操作によって、ネガティブDBoxの数をポジティブDBoxの数の定数倍にする。（3倍などが一般的。）この時、もっとも損失値の大きいものから残すようにする。

損失関数：
オフセット情報と信頼度について、損失関数を定義する必要がある。

オフセット情報…オフセット情報はDBoxからポジティブBBoxへの変換補正値の予測であるため、回帰問題で用いられる平均二乗誤差を利用する。ただしSSDではこれに少し工夫を加えたSmooth L1 Lossというものを利用する。![スクリーンショット 2020-12-18 12.02.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935441/e7fb5ec7-e518-5736-a187-e557ce6e9126.png)
これにより、予測誤差の絶対値が1より小さい場合は通常通りの二乗誤差を計算し、1以上の場合は予測誤差の絶対値から0.5を引いた値にする。　教師データと予測結果の差が大きい場合に二乗誤差を計算すると、学習が不安定になるためである。

信頼度…信頼度については通常通り交差エントロピー誤差を利用する。

以上のプログラムをまとめることで、SSDの学習と検証を実施することができる。
基本情報技術者 Chapter1~12 

右算術シフトは符号ビットで埋める
ビットの反転は排他的論理和
特定のビットを取り出すのは論理積

コンピュータの5大装置　制御　演算　記憶　入力　出力装置

第2正規形の表は部分関数従属している列を切り出した形
第３正規形の表は主キー以外の列に関数従属している列を切り出した形

OSI基本参照モデル
第７層　アプリケーション層
第６層　プレゼンテーション層
第５層　セッション層
第４層　トランスポート層
第３層　ネットワーク層　ルータ　IPアドレス
第２層　データリンク層　ブリッジ　MACアドレス　セグメント間
第１層　物理層　NIC LANケーブル　リピータ（拡声器）

MACアドレスは：で区切られたひとつが１バイトを１６進数で表したもの
先頭２４ビットが製造メーカ番号　後ろ２４ビットが製造番号

IPアドレスは３２ビットを８ビットずつに区切って、それぞれを１０進数で表し、.でつないだもの
Cluster API: v0.3.12 変更内容

## はじめに

ここでは Cluster API v0.3.12 の変更内容についてまとめています。Cluster API は複数の Kubernetes クラスタのプロビジョニングやアップグレード、オペレーションをするための宣言的な API とツールを提供する SIG Cluster Lifecycle のサブプロジェクトです。

- https://github.com/kubernetes-sigs/cluster-api/releases/tag/v0.3.12

## ✨ 新しい機能

- Secret と ConfigMap でキャッシュしないクライアントとメタデータのみを使用するように変更されました ([#3986](https://github.com/kubernetes-sigs/cluster-api/issues/3986)) — 影響: メモリ使用量の軽減。全監視対象の Namespace の全てのオブジェクトのローカルキャッシュを作成する代わりに、Secret と ConfigMap に対するクエリはコントローラが直接 API サーバにアクセスするようになりました。
    - :memo: Secret と ConfigMap を使うコントローラが増えてきたので、キャッシュの容量が多くなってきたことへの対策のようです。キャッシュするのがオブジェクトのメタデータのみに変更され、オブジェクトを変更する場合はキャッシュではなく直接 API サーバにアクセスするように変更されています。
- Cluster API GCP プロバイダが`clusterctl providers list` に追加されました ([#4008](https://github.com/kubernetes-sigs/cluster-api/issues/4008))

## 🐛 バグ修正

- MachineHealthCheck (MHC) は継続的なパッチを避けるために MHC `.status.targets` のリストをソートします ([#3998](https://github.com/kubernetes-sigs/cluster-api/issues/3998))
    - :memo: MHC の対象ノードのリストの並び順がソートされていなかったことで、リストに変更がなくても `status` フィールドが更新されてしまっていたようです
- KubeadmControlPlane のスケールイン時の確認では、削除されようとしているマシンが除外されるように修正されました ([#3984](https://github.com/kubernetes-sigs/cluster-api/issues/3984))
    - :memo: KubeAdmControlPlane のスケールアウト/イン時には全 ControlPlane マシンの preflightCheckCondition が `True` であるかどうかを確認して、問題のあるマシンがあれば処理が進まないようになっているのですが、スケールイン中の例えば etcd を削除している最中にコントローラがクラッシュすると、次の reconsile 時に Condition の確認に失敗するため、スケールイン処理がブロックされてしまっていました。
- KubeadmControlPlane (KCP) は、etcd メンバを reconsile する際に自身の NodeRefs 使用するように修正されました ([#3971](https://github.com/kubernetes-sigs/cluster-api/issues/3971)) - 影響: コントローラがクラスタと ControlPlane としてノードのリストにまだ登録されていない Kubernetes ノードをアップグレートしようとする間の競合状態が解決されます
    - :memo: これまで etcd メンバを reconsile するときにはワークロードクラスタから `node-role.kubernetes.io/master` ラベルを持つノードのリストを取得して対象を探していました。しかし、Machine オブジェクトの `status.nodeRef` フィールドで実際にどのノードなのかの情報は取得可能なため、この変更でワークロードクラスタへの問い合わせることをやめることで安定性が向上します。
- apierrors の際に `errors.Cause` が適切に確認されるようになりました ([#3969](https://github.com/kubernetes-sigs/cluster-api/issues/3969)) — 影響: マシンの削除がスタックしたり、何度も再試行した後にタイムアウトする問題が解決されます
- MachinePool のブートストラップトークンがローテーションされます ([#3965](https://github.com/kubernetes-sigs/cluster-api/issues/3965)) — 影響: MachinePool のブートストラップトークンは以前はローテーションされておらず、15分後に期限切れになっていました。この変更により、新しい MachinePool のインスタンスは、タイムアウト後にもクラスタに参加できるようになりました

## 📖 ドキュメンテーション

- クイックスタート内の Cluster API OpenStack の変数が更新されました ([#3952](https://github.com/kubernetes-sigs/cluster-api/issues/3952))

## 所感

このリリースは大きな機能追加が含まれていませんが、クラスタに対しての操作の安定性向上に取り組まれていて着実によくなってきているという印象を持ちました。また、MachinePool でのブートストラップの期限切れ問題で、私は MachinePool は使っていないのですが単にマシンの作成時時間がかかりすぎていてトークンの期限切れが起きているので対処を検討中です（このバージョンでうまくいったらここを更新します）。