OmniAICreator/Qiita-1.07M huggingface dataset:
[失敗]OpenWRT on FreeBSD

ちょっと確認したい事があり、OpenWRTをFreeBSDでビルドできないか試してみました。

BastiileBSDを使いますが、FreeBSD/amd64で試してみます。

ネットで検索するといくつかパッケージをインストールしてちょっとパッチを宛てればビルドできるような情報が出てきます。

たしかに下記のパッチでmakeが実行できるようになります。

```diff
diff --git a/include/prereq-build.mk b/include/prereq-build.mk
index d28432f726..d82bd5e9f4 100644
--- a/include/prereq-build.mk
+++ b/include/prereq-build.mk
@@ -130,6 +130,7 @@ $(eval $(call SetupHostCommand,getopt, \
        Please install an extended getopt version that supports --long, \
        gnugetopt -o t --long test -- --test | grep '^ *--test *--', \
        getopt -o t --long test -- --test | grep '^ *--test *--', \
+       /usr/local/bin/getopt -o t --long test -- --test | grep '^ *--test *--', \
        /usr/local/opt/gnu-getopt/bin/getopt -o t --long test -- --test | grep '^ *--test *--'))
 
 $(eval $(call SetupHostCommand,stat,Cannot find a file stat utility, \
diff --git a/scripts/mkhash.c b/scripts/mkhash.c
index ed3d42d4c3..a28d5fd537 100644
--- a/scripts/mkhash.c
+++ b/scripts/mkhash.c
@@ -105,7 +105,6 @@ be32enc(void *buf, uint32_t u)
        p[2] = ((uint8_t) ((u >> 8) & 0xff));
        p[3] = ((uint8_t) (u & 0xff));
 }
-#endif
 
 static void
 be64enc(void *buf, uint64_t u)
@@ -132,6 +131,7 @@ be32dec(const void *buf)
 
        return (((uint32_t) be16dec(p)) << 16) | be16dec(p + 2);
 }
+#endif
 
 #define MD5_DIGEST_LENGTH      16
```

makeは実行できますが、toolsとかのビルドでエラーがつぎつぎ出て無理みたいです。

ツールのソース内にdevnameという変数があり、FreeBSDではシステムのヘッダーでこの名前を使っていて、エラーになるケースがありました。これはFreeBSD側が悪いような気もします。

先日twitterで見つけたBastiileBSDのDebian jailでも試してみました。

https://twitter.com/BastilleBSD/status/1477897516355964928

こちらは何もしないでmakeが実行できますがmakeしているとサポートされてないシステムコールがあり、止まってしまいます。

Linuxバイナリ互換環境はあまり意味がないので試してません。

Linux互換はOpenWatcomのインストーラーも動かなかったので、結構使えないことあるようです。ただOpenWatcomのツールやPlatformIOのツールなどは問題ないので、向き不向きがあるのかもしれません。

あきらめてceleron(32Bit)なDebian10でビルドしました。ほぼほぼ一日がかりでした。このマシン71Wくらいあって24時間だと1704whで40円くらいのお金がかかってしまったと思われます。
【Rust】UTAUプラグインを作ってみた話。

#はじめに
この記事は「【Rust】UTAUプラグインを作ってみる話。」シリーズの各リンクをまとめたものです。

<details><summary>成果物の配布はこちらです。</summary><div>

・[GitHub](https://github.com/dino3616/even_scale)
・[Google Drive](https://bit.ly/3pSLzBb)

</div></details>

#【Rust】UTAUプラグインを作ってみる話。(1～3)

* [【Rust】UTAUプラグインを作ってみる話。(1/3) ～API作成編～](https://qiita.com/dino3616/items/b4054f9b3fc12f60a746)
* [【Rust】UTAUプラグインを作ってみる話。(2/3) ～CUI作成編～](https://qiita.com/dino3616/items/f6fa4460002915e4577a)
* [【Rust】UTAUプラグインを作ってみる話。(3/3) ～GUI作成編～](https://qiita.com/dino3616/items/b4054f9b3fc12f60a746)

#おわりに
初めての記事執筆だったので、拙い箇所が多々あると思います。
記事の書き方、内容、またUTAUプラグインの機能についてやソースコードについて、ご意見やご指摘を頂けると本当にありがたいです。

次回はまだ先になりそうですが、何かいいトピックスがあったら投稿させて頂きます。
そのときはまたよしなにお願いします。

またね。
キャラボードの作り方

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74655/54b36f29-3657-dcd9-e24a-e64082be96fb.png)

要件定義：
画面を見ると、Objectが多いです。
キャラボードはキャラ毎に一つのレイアウトを持っています。
なので、レイアウトの数は多いです。
新キャラを追加する場合、動的にレイアウトを追加した方がいいです。
（企画は、固定っていいって言われました、その理由はわかる、自分の作業を減らすだけだ。僕はエンジニアです、へんなシステムを作りたくない。マスターでキャラ毎のレイアウトデータを管理したほうがいいってアピールしました、最後は提案を採用されました。企画の作業は増えた、ごめん、よく考えたら、チーム全員の作業量をかなり減らされました。デザイナーの作業は発生しません、AssetBundleのリリースもなし、いいことですね。）

設計：
それぞれのパネルは下記の座標に置きます
1   2  3  4  5  6  7  8  9 10
11 12 13 14 15 16 17 18 19 20
~
~
~
~
キャラクターのマスターデータには
軽量化するため、最初はObjectをPrefabに置かない！！
キャラクターのマスターデータを読み込んで、それぞれの座標を作成して、パネルのPrefabを座標に置けば綺麗に作成されます。

実装：
皆さん、頑張ってくださいね！
argparse：ArgumentParserコンストラクタ引数parentsに、相互排他グループを含む引数グループを渡すと、正しくグループ化されない

# 環境
* Python 3.9.7 
    * argparse 1.1

# やりたいこと
[argparse](https://docs.python.org/ja/3/library/argparse.html#parents)モジュールを使ってCLIを作っています。

以下のように、相互排他グループ（`--foo` or `--bar`）を含む引数グループ"global options"を作成したいです。

https://docs.python.org/ja/3/library/argparse.html#mutual-exclusion


```python:foo.py
import argparse

def add_parser(parser):
    argument_group = parser.add_argument_group("global options")
    argument_group.add_argument("--xxx")
    argument_group.add_argument("--yyy")

    exclusive_group = argument_group.add_mutually_exclusive_group()
    exclusive_group.add_argument("--foo", action="store_true")
    exclusive_group.add_argument("--bar", action="store_true")

def main():
    parser = argparse.ArgumentParser(description="test")
    parser.add_argument("integers", type=int, nargs="+")
    add_parser(parser)
    args = parser.parse_args()


if __name__ == "__main__":
    main()
```

```
$ python foo.py -h
usage: foo.py [-h] [--xxx XXX] [--yyy YYY] [--foo | --bar] integers [integers ...]

test

positional arguments:
  integers

optional arguments:
  -h, --help  show this help message and exit

global options:
  --xxx XXX
  --yyy YYY
  --foo
  --bar
```

# 発生した問題
"global options"という引数グループは、他のコマンドでも利用するコマンドライン引数なので、ArgumentParserコンストラクタ引数`parents`に設定したいです。

https://docs.python.org/ja/3/library/argparse.html#parents

```python
def main():
    parent_parser = argparse.ArgumentParser(add_help=False)
    add_parser(parent_parser)
    parser = argparse.ArgumentParser(description="test", parents=[parent_parser])
    parser.add_argument("integers", type=int, nargs="+")

    args = parser.parse_args()
```

```
$ python foo.py -h
usage: foo.py [-h] [--xxx XXX] [--yyy YYY] [--foo | --bar] integers [integers ...]

test

positional arguments:
  integers

optional arguments:
  -h, --help  show this help message and exit
  --foo
  --bar

global options:
  --xxx XXX
  --yyy YYY
```

しかしヘルプを見ると、相互排他グループである`--foo`, `--bar`は、`global options`でなくデフォルトの`optional arguments`に含まれていました。

# 原因
分かりませんでした。
VSCodeからMayaにリモートデバックする(debugpy版)

## 概要
debugpyでMayaと接続する手順の紹介。

※あくまでもptvsdで既に接続している人向けに記載。
細かい手順は別のptvsdでの接続記事をご参照ください。

## 経緯
ptvsdでのMayaとの接続情報は巷に溢れているが、ptvsdは[リポジトリ](https://github.com/microsoft/ptvsd)にある様に非推奨。
しかし、新しい「debugpy」で接続しようとすると上手くいかない。
[issue](https://github.com/microsoft/debugpy/issues/262)にその解決方法が記載されていたので、解決方法を記載する。

## 環境
- Windows10
- Maya2022
- VSCode
  - 拡張機能のPython導入済

## 手順

### VSCodeのデバック構成を記載する
launch.jsonに下記を追加

``` json
        {
            "name": "Maya: Attach",
            "type": "python",
            "request": "attach",
            "port": 5678,
            "host": "127.0.0.1"
        }
```

### debugpyをインストールする
Mayaでパスが確認できる所にインストールする。

```python
python -m pip install debugpy

例) Maya2022のmayapyでscripts以下にパッケージを入れる
"C:\Program Files\Autodesk\Maya2022\bin\mayapy" -m pip install debugpy -t %USERPROFILE%\Documents\maya\2022\scripts
```

### Mayaのusersetup.pyに記載する
usersetup.py推奨だが、その都度コンソールで実行も可。
``` python:usersetup.py
import debugpy
debugpy.configure(python="mayapyへのパス") # 例 "C:/Program Files/Autodesk/Maya2022/bin/mayapy.exe"
debugpy.listen(5678)
```

### デバック実行
デバック構成から「Maya: Attach」を選択して実行
正常にアタッチされていたら成功です。

### 資料

* https://github.com/microsoft/debugpy/issues/334
* https://github.com/microsoft/debugpy/issues/262
GitHub Pagesを使ってUnity製ゲームをスマホのブラウザで動かす


　GitHub PagesでUnity製スマホ向けゲームを公開する時にやったこと

#環境
　Unity 2020.3.22f1
　Visual Studio Code 1.63.2
　git version 2.34.1.windows.1

#動かしたいゲーム
　スマホ向け2Dパズルゲーム
　スマホ縦画面を想定して制作(9:16)

# WebGLで書き出す
　[Unity：作ったゲームをWebGLビルドで公開する]
(https://dianxnao.com/unity%ef%bc%9a%e4%bd%9c%e3%81%a3%e3%81%9f%e3%82%b2%e3%83%bc%e3%83%a0%e3%82%92webgl%e3%83%93%e3%83%ab%e3%83%89%e3%81%a7%e5%85%ac%e9%96%8b%e3%81%99%e3%82%8b/)
　ここを参考にWebGL設定をする

　iOSで動かすには
![unity setting 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398204/bd2dc0f7-e4e3-6cf6-1774-44e3dd752d97.png)
　Graphics APIsをWebGL 1.0にしておく

![unity setting 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398204/540eb11a-e9e0-9a00-2b7b-dac169535355.png)
　Compression FormatをDisabledにしておく

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398204/b44b4cf2-0935-e662-fa21-37646b341acc.png)
　WebGL TemplateはMinimalで
　(後でいじるのでCanvas WidthとCanvas Heightは適当で)

#GitHubにアップロードする

　[自分で作ったWebページをインターネット上に公開しよう！](https://prog-8.com/docs/github-pages)
　[【Unity】GitHub PagesでWebGLを表示した話](https://note.com/kabineko/n/n74dfe29717a9)

　ここを参考に出力ファイルを丸ごとGitHubにアップロードする

　Webでプッシュできる容量なら良いのだがダメだったのでVScodeでgitを使った
　[VSCodeでGit・GitHubを使う方法を解説する【2021最新】](https://miya-system-works.com/blog/detail/128)

#index.htmlを改変して9:16固定にする

```index.html(一部)
<script>
      var canvas = document.getElementById("unity-canvas");

      function resize()
      {
        canvas.style.height = document.documentElement.clientHeight + "px";
        canvas.style.width = ((document.documentElement.clientHeight / 16 )* 9) + "px";
        if (((document.documentElement.clientHeight / 16 )* 9) > document.documentElement.clientWidth)
        {
          canvas.style.width = document.documentElement.clientWidth + "px";
          canvas.style.height = ((document.documentElement.clientWidth / 9 )* 16) + "px";
        }
      }
      
      resize();

      window.onresize = resize;
</script>
```
　ウィンドウがリサイズされたらcanvasの高さと幅を調節するjavascriptを追記します

#　まとめ

　WebGL1.0にする
　GitHubにアップロードしてGitHub Pagesで公開
　index.htmlにjavascriptを入れてレイアウト調節
「電話番号への転送」ブロックについて

#はじめに
[Amazon Connect](https://aws.amazon.com/jp/connect/)では
[「電話番号への転送」ブロック](https://docs.aws.amazon.com/ja_jp/connect/latest/adminguide/transfer-to-phone-number.html)を用いることで、インスタンス外部の電話番号に転送することができます。
この「電話番号への転送」ブロックにおいて、転送先の状態がどのような場合に「呼び出し失敗」へ遷移するのかについて検証しました。
![3_構成図.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266380/abecb93f-7a82-d625-9d8f-7d94e3c720da.png)


#調査を実施するための設定概要
ここで、「電話番号への転送」ブロックについて、簡単に説明します。
![「電話番号への転送」ブロック.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266380/3a7bb8fa-c36f-a4fe-c5b1-32f694962744.png)

この「電話番号への転送」ブロックは、
・着信問い合わせフロー
・[Customer Queue flow (お客様キューフロー)]
・[Transfer to Agent flow (エージェントフローへ転送)]
・[Transfer to Queue flow (キューフローへ転送)]
上記のフローにて使用することができます。

また、今回調査した「呼び出し失敗」の他に、
「成功」「タイムアウト」「エラー」の4つの種類の分岐があります。

#調査を実施するための設定詳細

では、「電話番号への転送」ブロックにおいて
実際にどのような設定が必要なのかを、詳しく説明します。

###■転送先
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266380/e1f0b630-3150-7c0b-cc3d-b75082368955.png" width=60%>
ここでは、どの電話番号に転送するかの設定を行います。
赤い枠で囲ったところに転送したい電話番号を入力すると、指定した電話番号へ転送されます。



###■任意指定のパラメータ
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266380/86ce5a5c-ab4d-7257-7c99-eba5ac7b3dd4.png" width=60%>
ここでは、転送先に通知させる電話番号の設定をすることができます。
赤い枠で囲ったところでインスタンスで取得した電話番号を選択します。選択した電話番号が、転送先に通知されます。

その他に、「タイムアウトの設定」、「切断後に問い合わせフローを再開」の部分も設定が必要になります。

#検証内容
それぞれの遷移先に、内容に合わせた「プロンプトの再生」ブロックを設定し、
架電者側が状況を判断できるようなフローを作成しました。
そこで、以下の①～⑤の架電先の状態により
どのような場合に「呼び出し失敗」に遷移するのかを確認しました。

![1フロー全体.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2266380/cd2c0a0c-9ba3-cc1b-6b4c-ea7589630c94.png)
①転送先が30秒以上、応答しなかった場合　※タイムアウトを30秒に設定
②転送先が通話中の場合
③転送先が電源を落とした場合
④転送先が機内モードの場合
⑤転送先が着信を拒否した場合　※携帯電話から拒否を押下した場合

※転送先をキャリアNTTdocomoのスマートフォンにて実施しました。

#検証結果
検証をした結果、
<b>②転送先が通話中の場合
③転送先が電源を落とした場合
④転送先が機内モードの場合
⑤転送先が着信を拒否した場合</b>
に「呼び出し失敗」に遷移した場合のアナウンスが再生されることを確認しました。

ちなみに
①転送先が30秒以上、応答しなかった場合
は、「タイムアウト」に遷移した場合のアナウンスが再生されることを確認しました。

#まとめ
・転送先が通話中である場合
・転送先が電源を落とした場合
・転送先が機内モードの場合
・転送先が着信を拒否した場合
上記の場合、「電話番号へ転送」ブロックの「呼び出し失敗」に遷移することが分かりました。
転送先の状態によっては呼び出し失敗となるため、その内容に合わせた「プロンプトの再生」ブロックを設定すると架電者側が状況を判断することができるため、分かりやすくて良いと思いました。

#備考
今回検証を行ったフローは、決まった番号に転送をする設定ですが
架電者が指定した任意の番号に外線転送することもできます。
こちらは、またの機会に記事にしようと思います。
MaterialButtonでテキストカラーが変更されない時の対処法

#初めに
最近のアプリでは、マテリアルデザインを基に作っているアプリが多いかと思いますが、そこで自分がつまづいたことをメモしておこうと思います。

##つまづいたこと
MaterialButtonに対してのテキストや、スタイルの設定は直接書き込むことができるが、あまり好ましくないので、皆さんは`styles.xml`や`textAppearance.xml`にまとめておいてあるかと思います。
そこで、今回つまづいたことはMaterialButtonのテキストに対して設定が反映されていないということでした。

###解決方法
解決策自体は、すごく単純なものでした。
普通、テキストに対しての設定なので`textAppearance.xml`に対して全ての設定を書いてしまうかと思いますが、テキストの色自体はMaterialButtonの管轄内なので`styles.xml`に対して記述をしないと意味がありません。
逆に、テキストの色以外はMaterialButtonが把握していなくてもいい内容なため、MaterialButtonの管轄外になります。
したがって、テキストカラーへの設定は`styles.xml`に、その他のテキスト自体への詳細な設定は`textAppearance.xml`に記述しないといけません
DoS(DDoS)攻撃とは何か？その対策方法

# DoS(Denial of Service)攻撃とは
サーバーに対して大量のアクセスを行い、サービスを停止させる攻撃

# DDoS(Distributed Denial of Service)攻撃とは
複数のコンピューターがDoS攻撃を行うこと

# DoS(DDoS)攻撃の具体例

### SYN Flood攻撃
TCPのやり取りの際のコネクション確立要求を行うSYNパケットのみを大量に送り続ける攻撃。ACKパケットを送らずにサーバーを待ち状態にすることで、他ユーザーからの接続を確立させなくする。

### F5攻撃
サーバーに大量のリクエストを送り、サーバーを過負荷の状態にして、リクエストに対応できなくさせる攻撃。

# DoS(DDoS)攻撃の対策方法
- 送信元のIPアドレスからのリクエストを遮断(DDoSには効きにくい)
- IDSの設置[^IDS]
- IPSの設置[^IPS]
- WAFの設置[^WAF]

# おわりに
間違い等があればご指摘いただけるとありがたいです。

[^IDS]: Intrusion Detection Systemの略で不正アクセスを管理者に通知するシステム
[^IPS]: Intrusion Prevention Systemの略で不正アクセスをブロックするシステム
[^WAF]: Web Application Firewallの略でアプリケーションレベルの攻撃をブロックする

# 参考文献
[マスタリングTCP/IP―入門編―(第6版)](https://www.amazon.co.jp/%E3%83%9E%E3%82%B9%E3%82%BF%E3%83%AA%E3%83%B3%E3%82%B0TCP-IP%E2%80%95%E5%85%A5%E9%96%80%E7%B7%A8%E2%80%95-%E7%AC%AC6%E7%89%88-%E4%BA%95%E4%B8%8A-%E7%9B%B4%E4%B9%9F/dp/4274224473)
[イラスト図解式 この一冊で全部わかるWeb技術の基本](https://www.amazon.co.jp/%E3%82%A4%E3%83%A9%E3%82%B9%E3%83%88%E5%9B%B3%E8%A7%A3%E5%BC%8F-%E3%81%93%E3%81%AE%E4%B8%80%E5%86%8A%E3%81%A7%E5%85%A8%E9%83%A8%E3%82%8F%E3%81%8B%E3%82%8BWeb%E6%8A%80%E8%A1%93%E3%81%AE%E5%9F%BA%E6%9C%AC-%E5%B0%8F%E6%9E%97-%E6%81%AD%E5%B9%B3-ebook/dp/B06XNMMC9S/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=116568KLNFXIB&keywords=web%E6%8A%80%E8%A1%93%E3%81%AE%E5%9F%BA%E6%9C%AC&qid=1641545388&s=books&sprefix=web%E6%8A%80%E8%A1%93%E3%81%AE%E5%9F%BA%E6%9C%AC%2Cstripbooks%2C251&sr=1-1)
ビューコンポーザ利用時に発生したMethod Illuminate\View\View::composer does not exist.の原因と解決策

#自己紹介
初めまして、kotobukiと申します。

以前PHPでwebアプリを初めて0から開発した際につまづいた点を残しておらず後悔したので、
今後そのようなことがないように始めてみました。
これからの学習や個人開発でのつまづきポイントや疑問などを投稿していこうと思います。
おそらく初学者の方々も同じような部分でつまづくと思うので、参考になれば幸いです。

・開発したアプリ
　[逆タン！！-タンパク質量から食材の量を逆算-](https://protain-backcalc-app.herokuapp.com/)


#概要
本記事は、[PHPフレームワークLaravel入門 第2版](https://www.amazon.co.jp/PHP%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AFLaravel%E5%85%A5%E9%96%80-%E7%AC%AC2%E7%89%88-%E6%8E%8C%E7%94%B0-%E6%B4%A5%E8%80%B6%E4%B9%83/dp/4798060992/ref=pd_lpo_1?pd_rd_i=4798060992&psc=1)で学習している中の疑問・つまづきの備忘録です。

今回はビューコンポーザの利用でつまづいた点について原因と解決策をまとめます。


#今回起こったエラー
今回は以下のエラーが発生しました。

Method Illuminate\View\View::composer does not exist.

- ソースコード


```HelloServiceProvider.php
<?php

namespace App\Providers;

use Facade\FlareClient\View;
use Illuminate\Support\Facades\App;
use Illuminate\Support\ServiceProvider;

class HelloServiceProvider extends ServiceProvider
{
  public function boot(){
    //このcomposerメソッドが存在しないエラー
    View::composer('hello.index', function ($view) {
            $view->with('view_message', 'composer message!');
    });
  }
//以下省略
}
```
##原因
原因はエラーの通り、Illuminate\View\Viewクラスにcomposerメソッドが存在しないことです。
これは、実際にViewクラスのソースコードを確認していただければわかります。

さらに以下のリファレンスによると、ここで使用しているView::composer()はViewファサードというものでした。
したがって、名前空間のエイリアスを新たに作成し参照する必要がありました。
>Viewファサードのcomposerメソッドを使用して、ビューコンポーザを登録します。
Laravelには、クラスベースのビューコンポーザーのデフォルトディレクトリが含まれていないため、自由に整理できます。たとえば、app/View/Composersディレクトリを作成して、アプリケーションのすべてのビューコンポーザを保存できます。

>[Laravel 8.x ビュー](https://readouble.com/laravel/8.x/ja/views.html)

##解決策

HelloServiceProvider.phpに「use Illuminate\Support\Facades\View」を追記します。
なぜIlluminate\Support\Facades名前空間のViewを指定するのかは、以下のリファレンスに記載があります。


>Laravelのファサードはすべて、Illuminate\Support\Facades名前空間で定義します。したがって、次のようなファサードに簡単にアクセスできます。
>[Laravel 8.x ファサード](https://readouble.com/laravel/8.x/ja/facades.html)

また、今回はすでにuse Facade\FlareClient\View;が存在するため別名として「FacadesView」を付けています。


```HelloServiceProvider.php
<?php

namespace App\Providers;

use Facade\FlareClient\View;
use Illuminate\Support\Facades\App;
use Illuminate\Support\ServiceProvider;

///////////この部分を追加
use Illuminate\Support\Facades\View as FacadesView;
///////////この部分を追加

class HelloServiceProvider extends ServiceProvider
{
  public function boot(){
    FacadesView::composer('hello.index', function ($view) {//別名であるFacadesViewを指定
            $view->with('view_message', 'composer message!');
    });
  }
//以下省略
}
```

#参考文献
>[PHPフレームワークLaravel入門 第2版](https://www.amazon.co.jp/PHP%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AFLaravel%E5%85%A5%E9%96%80-%E7%AC%AC2%E7%89%88-%E6%8E%8C%E7%94%B0-%E6%B4%A5%E8%80%B6%E4%B9%83/dp/4798060992/ref=pd_lpo_1?pd_rd_i=4798060992&psc=1)
>[Laravel 8.x ビュー](https://readouble.com/laravel/8.x/ja/views.html)
>[Laravel 8.x ファサード](https://readouble.com/laravel/8.x/ja/facades.html)
UE5(アーリーアクセス)のC++プロジェクト作成時にビルドエラーが発生する場合の回避策メモ

アーリーアクセスのUE5C++プロジェクト作成時にビルドエラーが発生した場合の回避策をメモ

:::note info
ある程度プログラム開発がわかる前提の書き方をしています。
:::

:::note info
製品版では変わる可能性があります。
:::

UE5はEpic Games Lancherからインストールができる、2022/01/07時点の最新版を使用しています。

# 〇〇　-WaitMutex -FromMsBuild" exited with code 6.が出た場合

## 1.XGE/Incredibuildを無効化する

開発環境にIncredibuildがインストールされてなければ、以下の方法に従って無効化する。

[【テレワーク】 XGE / Incredibuild を無効化する](https://forums.unrealengine.com/t/xge-incredibuild/142050)

XGEによるコンパイル無効は、以下のコマンドをビルドコマンドラインに追加しても有効になります

```
-NoXGE
```

# error: expected \uXXXX escape sequenceが出た場合

## 1. bAllowSNDBSにfalseを設定する

以下を参考にbAllowSNDBSにfalseを設定する。

[“error: expected \uXXXX escape sequence” when compiling in c++](https://forums.unrealengine.com/t/error-expected-uxxxx-escape-sequence-when-compiling-in-c/232006)

BuildConfiguration.xmlは以下のドキュメントを参照
[Build Configuration](https://docs.unrealengine.com/4.26/en-US/ProductionPipelines/BuildTools/UnrealBuildTool/BuildConfiguration/)

---

上記二点を行うことで無事にUE5プロジェクトのC++ビルドが通りました。
ご参考までに。
herokuの無料プランで独自ドメインのSSL化+サイト表示スピードの高速化を実現する


## 背景
AWSで仮想サーバーを立てて個人アプリを作ったはいいが、如何せんコストがかかりすぎている。（月5000円くらい）
そのため、herokuの使用を検討した。
しかし、herokuの無料プランだと、以下の問題点があった。

- 30分間アクセスがないと、herokuがスリープしてしまい、サイト表示スピードが著しく落ちる。
- 独自ドメインのSSL化は有料プランでないとできない。

上記の問題を解決できないかと検討していたところ、解決策を発見したので備忘録を兼ねて記事を書くことにした。

## 前提条件
- herokuでのデプロイが完了している。
- お名前ドットコムで独自ドメインを取得している。

## サイト表示スピードの高速化
→ GASを使用して、定期的にサイトにアクセスさせることで解決可能。

### GASのスクリプト

```portforio_access.gs
function portforio_site_access() 
{
  var response = UrlFetchApp.fetch('sleepさせたくないアプリのurl');
}
```

`UrlFetchApp`はGASに用意されているクラス。このクラスでfetchメソッドを使ってあげることで、URLにアクセスできる。今回はパラメータなどをつける必要がないので、fetchメソッドのみ使用したが、何か別の処理を自動化する際に、パラメータを付与してリクエストを行うことも可能らしい。
ref: https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app

## 独自ドメインのSSL化
→ Cloudflareをherokuアプリとの間にかませる。

### Cloudflareとは？
アクセス負荷の軽減や、ネットワーク保護を行ってくれるサービス。
ref: https://www.cloudflare.com/ja-jp/learning/what-is-cloudflare/

### Cloudflare上の設定
#### ドメインの追加
https://www.cloudflare.com にアクセスして、Get Startedを選択。
その後、取得した独自ドメインを入力して、Add siteを選択。![独自ドメインの登録.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/702700/44076651-3514-4056-ef16-0edc771ba1d9.png)

#### プランの選択
赤枠部分を選択する。
![プランの選択.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/702700/e0a5d77d-fb4e-0ac9-31ae-f0d09cf8d89a.png)

#### DNSの追加
下記画像のTypeにはCNAME、Nameには独自ドメイン、Targetにはherokuドメインを入力。
入力後、Saveをして、Continueをクリック。

![DNS追加.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/702700/bd3d6a9e-8a82-eb7d-4962-e55a18ff4d10.png)

#### ネームサーバーの確認
下記画像のAdd Cloudflare's nameserversをコピーする。（2つともコピーしてください。）

![check_name_server.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/702700/e1e9646b-bdbc-c349-e885-309b89ebd56a.png)TLS

#### SSL/TLSの確認
下記画像のようにfullになっていることを確認する。
![cloudflare ssl チェック.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/702700/8619811a-3490-a425-254c-e5aad54a29cb.png)


### お名前ドットコム側の設定変更
下記画像のネームサーバー１と２に先ほどコピーしたものを貼り付ける。
![nameserver_setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/702700/2a3f9767-27a9-e93a-1df6-2a48b7309b11.png)

### アプリ側の設定（今回はRailsで作ったアプリを想定しております。ここは適宜読み替えてください。）

```production.rb
config.force_ssl = true # デフォルトだとfalseになっているので、trueにする。
```

### heroku側の設定
herokuの`https://dashboard.heroku.com/apps/アプリ名/settings`に遷移して、下記画像の赤枠部分をクリックし、独自ドメインを入力する。
![heroku setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/702700/f8e5f330-5038-ac3f-8717-98d0ffab8901.png)

以上で設定完了になります。
設定してから反映されるまで、1~3日ほどかかるようなので気長に待ちましょう。
SuperMicro X10SBAにTrueNASをインストールするときのTips

SuperMIcro X10SBAにTrueNASをインストールするも、定期的に再起動してしまう現象が発生したので対応をTipsとして残す
もしかするとIntel Celeron J1900に共通する問題だったりするかもしれないので参考までに。

## 前提条件
SuperMicro X10SBA 1.3c 
TrueNAS 12.0-U7

## BIOSの設定
BIOSアップデートを行う際はドキュメントちゃんと読む。
JPME2のジャンパを2-3に設定しないとエラーになるの注意。

Advanced -> Boot Feature -> Power Configration -> Watch Dog Function
上記がDisableに設定されている事を確認

## TrueNAS
システム -> 調整

以下項目を追加

| Variable | Value | Type |
|:-----------|:------------:|:------------:|
| hint.wbwd.0.disabled| 1        | LOADER         |
| watchdogd_enable     | NO      | RC       |


![WS000001.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212702/d5d3e67e-fc27-4951-db9e-87b2d48aaa29.jpeg)


設定後再起動
CVE Data Feeds | CVE として発行された脆弱性の個数を年ごとにまとめてみた

## 概要

2002–2021年までの年ごとの CVE の個数をまとめました。

- 2002–2021年までに発行された CVE の合計は 177,398個
- 2015–2017年にかけて CVE の発行数が約2倍増
- 2016年以降は CVE の発行数が全て5桁

![figure_cve_number_history.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273914/5ba55f6d-a263-747b-8807-2bb4906f250f.png)

## 年表

| 年 | CVE の個数 |
| :-- | :-- |
| 2002 | 6769個 |
| 2003 | 1550個 |
| 2004 | 2707個 |
| 2005 | 4764個 |
| 2006 | 7140個 |
| 2007 | 6576個 |
| 2008 | 7170個 |
| 2009 | 5023個 |
| 2010 | 5190個 |
| 2011 | 4827個 |
| 2012 | 5835個 |
| 2013 | 6642個 |
| 2014 | 8846個 |
| 2015 | 8561個 |
| 2016 | 10471個 |
| 2017 | 16528個 |
| 2018 | 16715個 |
| 2019 | 16511個 |
| 2020 | 18959個 |
| 2021 | 16624個 |

*この年表に記載されているデータは [参考](#参考) の nvd.nist.gov の `JSON Feeds` から取得しました。

## 参考

https://nvd.nist.gov/vuln/data-feeds

## 作図用 Python コード

```python
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

year = np.arange(2002, 2022, 1)
cve_number = [
    6769, 1550, 2707, 4764, 7140,
    6576, 7170, 5023, 5190, 4827,
    5835, 6642, 8846, 8561, 10471,
    16528, 16715, 16511, 18959, 16624
]
plt.plot(year, cve_number)
plt.title("Total: 177,398")
plt.xlabel("Year")
plt.ylabel("CVE Numbers")
plt.axis([2000, 2025, 0, 20000])
plt.show()
```
【多対多】belongsToManyメソッドについて

#belongsToManyメソッドの引数
belongsToManyメソッドを使うパターンは、多対多の場合に使われます。
多対多にはテーブル構造として３テーブルが必要です。
リレーション元、中間テーブル、リレーション先の３テーブルです。

中間テーブルのカラム名には【リレーション元・先のテーブル名の単数形＿id】という規則性があります。
この規則性に当てはまる場合は第三、第四引数は省略可という決まりがあります。

SNSをのフォロー機能のリレーションを例に解説していきます。
※下記のUser.phpではfollowersメソッドのみ定義しております。フォロー機能の場合はもう１つメソッドを定義する必要があります。今回はbelongsToManyの引数の仕様について解説しております。
###データベース
・usersテーブル
カラム:id/name/password/created_at/updated_at
・followsテーブル
カラム:id/follower_id/followee_id\created_at/updated_at


##第三、第四引数が省略不可のパターン
先ずUserモデルにリレーションを記入。

```php:User.php

   public function followers(): BelongsToMany
    {
        return $this->belongsToMany('App\User', 'follows', 'followee_id', 'follower_id')->withTimestamps();
```







belongsToManyメソッドの第一引数には関係するモデルのモデル名を渡します。
第二引数には中間テーブルのテーブル名を渡します。

ちなみに、第二引数を省略すると、中間テーブル名は2つのモデル名の単数形をアルファベット順に結合した名前であるという前提で処理されます。つまり、user_followという中間テーブルが存在するという前提で処理がされます。

followsテーブルには、created_at、updated_atカラムが存在するので、withTimestampsメソッドを付けるようにします。

●リレーション元のusersテーブルのidと、followee_idが紐づいている
●リレーション先のusersテーブルとfollower_idが紐づいている
このような関係性の場合は第三、第四引数は省略せずに中間テーブルのカラム名の記述をします。
※中間テーブルカラム名【リレーション元・先のテーブル名の単数形＿id】という規則性に当てはまらない

##第三、第四引数が省略可のパターン
例えば、『いいね機能』のリレーションについて
###データベース
・postsテーブル
カラム:id/body/user_id/created_at/updated_at
・likesテーブル
カラム:id/user_id/post_id/created_at/updated_at
・usersテーブル
カラム:id/name/password/created_at/updated_at



```php:User.php

   public function followers(): BelongsToMany
    {
        return $this->belongsToMany('App\User', 'likes')->withTimestamps();
```

●リレーション元のpostsテーブルのidは、中間テーブルのpost_idに紐づいている。
●リレーション先のusersテーブルのidは、中間テーブルのuser_idと紐づいている。
この場合は、第三・第四引数の省略が可能です。
※中間テーブルカラム名【リレーション元・先のテーブル名の単数形＿id】という規則性に当てはまる


###図解
![My First Board.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2188773/b34fb436-da0e-9644-50fc-6eea7bde1830.jpeg)
Android API 31以降の自機IPの取得

API31からWifiManager経由の自分のAndroidのIPアドレス取得が非推奨になりました。そのままのコードでもとりあえず動作するようですけど、修正するとしたら次のようにConnectivityManagerを使うようにする必要があります。


# API 31以前のWifiManagerによるIPアドレスの取得

```kotlin
val manager: WifiManager =this.applicationContext.getSystemService(WIFI_SERVICE) as WifiManager
val ip = manager.connectionInfo.ipAddress
Log.i(TAG,arrayOf<Int>(ip,ip shr 8,ip shr 16,ip shr 24).map{it and 0xff}.joinToString("."))
```

IPはIntで取得されるので、1バイトずつ分割して表示しています。例えば192.168.1.1のように表示されます。

# API 31 以降の場合

build.gradle(app)のcompileSdkが31以上となると、

```gradle:build.gradle(app)
compileSdk 31
```

以下のように非推奨が表示されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275020/3f4a2994-eacb-454b-1596-a462c846dae0.png">

以下のように、ConnectivityManagerを取得してコールバックを設定し、コールバック関数のonLinkPropertiesChanged内でIPを取得します。以下のコードはIPv4の最初のアドレスを取得しています。

```kotlin
val manager:ConnectivityManager = this.applicationContext.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
val networkCallback = object: ConnectivityManager.NetworkCallback() {
    override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) {
        super.onLinkPropertiesChanged(network, linkProperties)
        Log.i(TAG,linkProperties.linkAddresses.filter{
            it.address is Inet4Address
        }[0].toString())
    }
}
manager.registerDefaultNetworkCallback(networkCallback)
```

LinkAddressを文字化しているので例えば192.168.1.1/16のようにサブネットマスクも表示されます。

# AndroidManifest.xmlでの許可項目の変更

またWifiManagerのときに以下のようにAndroidManifest.xmlでACCESS_WIFI_STATEに対してパーミッションを与えていたところを、ACCESS_NETWORK_STATEに変更する必要があります。

```xml:AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
```

↑を、↓に変更する。

```xml:AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
```

# 参考にさせて頂いたサイト

- <a href="https://stackoverflow.com/questions/68512057/getter-for-connectioninfo-wifiinfo-is-deprecated-deprecated-in-java-api-31">'getter for connectionInfo: WifiInfo!' is deprecated. Deprecated in Java (Api 31)</a>
WSL2上にDocker立ててPostgreSQLを動かしホストから接続する

n番煎じでも手順を纏めておかんと忘れるねんで

# なんでそうするの
Docker Desktopが業務で使用する場合に有償になったので（所属組織規模による）WSL2上だけで動かしたい。でもGUIなしでのDocker操作よくわかってないし……。

# 動作環境

* OS: Windows 10 Pro 21H2
* WSL2: Ubuntu 20.04
* アンインストールしたけどDocker Desktopを入れていたので何か影響あるかも？

# WSL2上にDockerを入れる

 [Ubuntu向けDocker構築手順の公式ドキュメント](https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository)通り

## リポジトリを設定する

必要なものをインストールする

``` bash
 sudo apt-get update

 sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
```

Dockerの公式GPGキーの追加

``` bash
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
```

dockerの安定板パッケージリポジトリをaptに追加

``` bash
 echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```

## Docker のインストール

``` bash
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io
```

## docker-composeのインストール

``` bash
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
```

## docker daemon の起動


``` bash
sudo service docker start
```

これはWSL2を起動するたびに必要なのでおいおい自動化したいな。ちょっと`.bashrc`に追記してやってみようと思ったけど動かなかった。知識が足りない。

# Dockerの準備

ホスト機の`D:\docker`に`postgresql14`というフォルダを作り、そこに以下の内容で`docker-compose.yml`を作成する。
今回は postgreSQL 14.xを使いたい。

``` yml
version: '3.8'
services:
  db:
    image: postgres:14
    restart: always
    volumes:
      - postgresql:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: postgres
    tty:
      true
volumes:
  postgresql:
    driver: local
```

`restart: always`で死んだら勝手に再起動するようにした。記述ミスったときに無限再起動に陥ってしまったが……。
あとは `tty:true`で勝手に正常終了してしまわないようにしたが、postgresqlでは不要かもしれない（未確認）

# Docker コンテナの起動

WSL2上で`docker-compose.yml`を配置したフォルダへ移動してから起動させる

``` bash
cd /mnt/d/docker/postgresql14
docker-compose up -d
```

-dオプションでDockerコンテナがバックグラウンドで起動するようにする。

# ホストから繋ぐ

ここでWindows上のクライアントから繋ぎたいのだが、普通に`localhost:5432`宛に`postgres/postgres`でつながってしまった。
なんか設定しないといけないと思っていたので拍子抜けである。


# おまけ

Docker Desktopを使っていた際、Visual Studio CodeにDocker拡張機能を入れてコンテナの起動や終了をらくらく行っていたがWSL2上に構築すると接続できなくて使えない……と思いきや、[Remote - WSL](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl) という拡張機能をインストールすると使えるようになった。
やったぜ。
SPIRE: v1.1.2 変更内容

_この記事は [Z Lab](https://qiita.com/organizations/zlab) の業務の一環として作成したものです。_

## はじめに

SPIRE v1.1.2 の変更内容を紹介します。 
https://github.com/spiffe/spire/releases/tag/v1.1.2

:pencil: がついた文章は、CHANGELOG の公式内容ではなく筆者の補足です。
前バージョン v1.1.1 の変更内容は[こちら](https://qiita.com/TakanariKo/items/a0be8e7660a1eed4743f)を参照してください。

## Added

- SPIRE Agent で SVID 管理を信頼できるプラットフォームに委譲するための Delegated Identity API がサポートされました。([#2481](https://github.com/spiffe/spire/pull/2481))
    - :pencil: この API は、特権を持つ Workload に同一ホスト上の他の Workload に代わって SVID と Trust Bundle にアクセスさせたいユースケース[^1]を想定したものになっています。設定方法は SPIRE Agent の[ドキュメント](https://github.com/spiffe/spire/blob/v1.1.2/doc/spire_agent.md#delegated-identity-api)を、API の詳細は [spiffe/spire-api-sdk](https://github.com/spiffe/spire-api-sdk/blob/v1.1.2/proto/spire/api/agent/delegatedidentity/v1/delegatedidentity.proto) をご参照ください。
- K8s Workload Registrar でエントリに登録する SVID 内の DNS 名をテンプレートで設定できる機能がサポートされました。([#2643](https://github.com/spiffe/spire/pull/2643))
    - :pencil: 詳細は K8s Workload Registrar (CRD Mode) の[ドキュメント](https://github.com/spiffe/spire/blob/v1.1.2/support/k8s/k8s-workload-registrar/mode-crd/README.md#additional-dns-names)をご参照ください。
- SPIRE Server が期限切れのエントリーを削除した際にメッセージをロギングするようになりました。([#2637](https://github.com/spiffe/spire/pull/2637))
- OIDC Discovery Provider から提供される JWKs に use プロパティを設定できるようになりました。([#2634](https://github.com/spiffe/spire/pull/2634))

## Fixed

- SPIRE Agent で、特定の種類の認証エラー時に失敗の理由を表示されるようになりました。([#2628](https://github.com/spiffe/spire/pull/2628))

[^1]: 具体的な事例として、Cilium では Delegated Identity API を使用した SPIFFE 統合が進められています。詳細は [cilium/cilium#17335](https://github.com/cilium/cilium/pull/17335) をご参照ください。
SVG素材の圧縮

# インストール

```shell
$ yarn global add svgo
```

# 設定ファイル作成
```js:myconfig.js
module.exports = {
  plugins: [
    {
      name: 'preset-default',
      params: {
        overrides: {
          removeViewBox: false,
        },
      },
    },
  ],
};
```

# 実行
```shell
$ svgo -f ./assets/image -r --config ./myconfig.js
```

`./assets/image`ディレクトリ内のSVGが圧縮される

https://github.com/svg/svgo
AWS LightsailでのWordPress環境構築でwp-config.phpが反映されない

ローカルで作成したWordpress環境をLightsailに載せて公開したい

テーマファイルはFTPソフトで簡単に反映されたが，

インポートしたSQLファイルが反映されない．．．
新たに入れたwp-config.phpの情報（DBの設定など）が反映されていないよう．
どうやら元から入っていたwp-config.phpの情報のままになっている

Apacheを再起動してみる

```
Apache 停止／起動
sudo /opt/bitnami/ctlscript.sh stop apache
sudo /opt/bitnami/ctlscript.sh start apache
```

変わらず．．．

ここでかなりつまづきましたが，
結論Apacheの停止だけでなく，bitnami全体の再起動をしないと適用されませんでした

```
bitnami全体的に停止／起動
sudo /opt/bitnami/ctlscript.sh stop
sudo /opt/bitnami/ctlscript.sh start

※restatでは何故か適用されず．．．
```
PynamoDB でローカルテーブルを指定する場合に Unable to describe table: Unable to locate credentials が発生する

# 問題

Python で DynamoDB を簡単に使うためのライブラリの１つに PynamoDB がある。

https://pynamodb.readthedocs.io/en/latest/index.html

このライブラリでは EndPoint を利用することで本番ではなくローカルの DynamoDB を利用することもできる。

https://pynamodb.readthedocs.io/en/latest/local.html

しかし、ローカルでは動いていたのだが、アプリケーションを Docker に持っていった瞬間に掲題の `Unable to describe table: Unable to locate credentials` というエラーが発生してしまったので、その原因を探る。

# 回答

PynamoDB をローカルで利用する場合であっても、botocore に何らかの初期化を行う必要がある。 これができない場合、上記のエラーが発生する。

具体的には、以下のページで定義されている環境変数の指定、あるいは、デフォルトで読み込むクレデンシャルファイルが存在している必要がある。

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-envvars.html

https://pynamodb.readthedocs.io/en/latest/awsaccess.html

そのため、例えば、以下のような dummy を作成して、これを Docker コンテナの実行ユーザー以下の `~/.aws/credentials` にマウントすれば動作するようになる。

```ini:dummy_credentials
[default]
aws_access_key_id = dummy
aws_secret_access_key = dummy
```
Vue-Bootstrapで動的にCollapseを生成する

##経緯
とあるサイト制作で`template`を使用してボタンで発火する`v-collapse`をたくさん用意したかったのだが、`id`を動的に`bind`してもうまく動かなかった

##解決方法
```vue:main_card.vue
<b-button v-on:click="expandcoll" variant="outline-info" :id="'btn'+collapseid"
  >Member</b-button
>

<b-collapse :visible="isexpand" class="mt-2">
  <!-- dosomething -->
</b-collapse>
```

`id`なんてもので制御するのではなく、`<b-collapse :visible="bool">`を指定してあげれば、ボタンでboolの値を変更するだけで簡単に制御することが出来た。
bootstrapのリファレンスにこだわりすぎた。
Laravel アプリにlaravel/sail を導入

# はじめに

昔作ったアプリの開発環境がdocker環境になっていなかったのでそれをsail使って実現したことを備忘録として残します。


# 導入

## の前に

sailを利用するにはlaravel, php ともにバージョン7.4以上必要との認識ですので、

PHPのバージョンを7.2 → 7.4

Laravelのバージョンを7.0 → 7.4

```json:composer.json
{
    "name": "laravel/laravel",
    "type": "project",
    "description": "The Laravel Framework.",
    "keywords": [
        "framework",
        "laravel"
    ],
    "license": "MIT",
    "require": {
//        "php": "^7.2",
        "php": ">=7.4",　ここ変更
        省略
//        "laravel/framework": "^7.0",
        "laravel/framework": ">=7.4",　ここ変更
    },
        省略
}

```





```terminal
$ composer update
```

8系にすると色々ややこしいんじゃないかということで今回は7.4にしました。

パッケージのバージョンエラーあれば随時更新してあげてください。


## sail インストール

ここからはこれ参考にやりました。7系の該当ページは見つかりませんでした。

https://readouble.com/laravel/8.x/ja/sail.html


```terminal
$ composer install laravel/sail --dev
```

```terminal
$ php artisan sail:install
```


デフォルトのmysql のサービスを選択すると下記のようなdocker-compose.ymlが生成されます。


```yml:docker-compose.yml
# For more information: https://laravel.com/docs/sail
version: '3'
services:
    laravel.test:
        build:
            context: ./vendor/laravel/sail/runtimes/8.1
            dockerfile: Dockerfile
            args:
                WWWGROUP: '${WWWGROUP}'
        image: sail-8.1/app
        extra_hosts:
            - 'host.docker.internal:host-gateway'
        ports:
            - '${APP_PORT:-80}:80'
        environment:
            WWWUSER: '${WWWUSER}'
            LARAVEL_SAIL: 1
            XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
            XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
        volumes:
            - '.:/var/www/html'
        networks:
            - sail
        depends_on:
            - mysql
    mysql:
        image: 'mysql/mysql-server:8.0'
        ports:
            - '${FORWARD_DB_PORT:-3306}:3306'
        environment:
            MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ROOT_HOST: "%"
            MYSQL_DATABASE: '${DB_DATABASE}'
            MYSQL_USER: '${DB_USERNAME}'
            MYSQL_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ALLOW_EMPTY_PASSWORD: 1
        volumes:
            - 'sailmysql:/var/lib/mysql'
        networks:
            - sail
        healthcheck:
            test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"]
            retries: 3
            timeout: 5s
networks:
    sail:
        driver: bridge
volumes:
    sailmysql:
        driver: local

```


Dockerfile も作成します。

```Dockerfile
FROM bref/php-74-fpm
COPY . /var/task
CMD [ "public/index.php" ]
```


これで

```terminal
$ ./vendor/bin/sail up
```

してコンテナが正常に立ち上がれば一旦終わりです。

## おまけ

laravel.testというサービス名は嫌だ。という方は、

```:.env
APP_SERVICE="yourServiceName"
```

を追記し、docker-compose.yml 内のサービス名も変更してください。



# おわりに

こちら作業しながら短時間で書き上げた記事で、荒い部分あるかと思いますが、ご参考にしていただけるとありがたいです。

## 手前味噌

https://qiita.com/soma_sekimoto/items/18b0b0d2579e0cf34bae
【Rails】vendor/bundle 配下にbundle installしたgemを確認する方法


インストールしたgemのリストを表示するコマンドは

```
$ gem list
```
ですが、これだとカレントのruby versionでローカルにインストールしたgemのリストが表示化されます。


アプリケーションディレクトリーの`vendor/bundle` 配下にbundle installしたgemのリストを表示させるには

```
$ bundle exec gem list
```
を入力します。
【AzureCli】割り当て済み状態のまま停止中のVMを割り当て解除する

普通にGUIから実施すればいいんですが、AzureCliでやる機会があったので備忘録に。
Azureの仮想マシンには「割り当て状態」と「割り当て解除」状態が存在し、
仮想マシンが停止していても、「割り当て状態」のままだと課金が発生します。
これをAzureCliから割り当て解除実施をします。

### 前提
AzureCliを導入して、az loginまではしておいてね。

### 仮想マシンの電源状態を確認する
```
az vm list --subscription [サブスクリプション名] --show-details --query "[].{Name:name, Id:id, PowerState:powerState}" --output table
```
https://docs.microsoft.com/ja-jp/cli/azure/vm?view=azure-cli-latest#az_vm_list

| 表示  |  状態  |
| ---- | ---- |
|  VM running  |  起動中  |
|   VM stopped |  停止中（割り当て状態）  |
|   VM deallocated |  停止中（割り当て解除）  |

--show-detailsを指定しないと電源状態（PowerState）が確認できないので注意
割り当て解除したいVMのIdを確認する

### 割り当て解除する
```
az vm deallocate --ids [仮想マシンのId]
```

これで割り当て解除状態になる
gemを使わず複数ワード・複数カラムでのAND検索

自分が複数ワードでの検索機能を実装する際に結構調べたので備忘録として。

## 目標
複数ワードかつ複数カラムを跨いだ検索を可能にする。

## 開発環境
Ruby2.6.3
Rails5.2.5

# 実装方法

## ルーティングを記述

```ruby:routes.rb
get '/search' => 'searches#search'
```

検索ボタンを押したら、srarchesコントローラーのsearchアクションが実行されます。

## 検索フォーム(部分テンプレートにしています)

```ruby:_form.html.erb
<%= form_with url: search_path, method: :get, local: true do |f| %>
  <%= f.text_field :keyword %>
  <%= f.submit "検索" %>
<% end %>
```

検索フォームに入れた内容をkeywordとしてコントローラーへ送ります。

## controller

```ruby:searches_controller.rb
def search
  @keywords = params[:keyword]
  @posts = Post.all
  split_keywords = @keywords.split(/[[:blank]]+/)
  split_keywords.each do |word|
    @posts = @posts.where(['カラム名 LIKE ? OR カラム名 LIKE ?・・・', ・・・"%#{word}%", "%#{word}%"])
  end
end
```

簡単に解説していきます。
@keywords = params[:keyword]
→検索フォームから入力されたkeywordを取得。

@posts = Post.all
→@postsオブジェクトを作成。
　kaminari使用時は@posts = Post.page(params[:page]).per(5)のように記述。

split_keywords = @keywords.split(/[[:blank]]+/)
→検索ワードを分割しています(splitメソッドは文字列を分割して配列として格納します)
 　.split(/[[:blank]]+/)の記述で半角、全角の空白どちらを入力しても分割してくれます。

split_keywords.each do |word|
→入力したキーワードを検索にかけていきます。

@posts = @posts.where(['カラム名 LIKE ? OR カラム名　LIKE ?・・・', ・・・"%#{word}%", "%#{word}%"])
→検索をかけたいカラムを記述していきます。カラムの数だけ"%#{word}%"の記述が必要になります。
"%#{word}%"で部分一致の検索になります。

## 最後に検索結果のview

```ruby:search.html.erb
<%= @keywords %>の検索結果

<% if @posts.empty? %>
  該当する投稿はありません。
<% else %>
  <%= @posts.each do |post| %>
    ・
    ・
  <% end %>
<% end %>
# kaminari使用時のみ
<%= paginate @posts %>
```

empty?はRubyのメソッドです。
空文字、空配列、空ハッシュの場合にtrueを返してくれます。
なので今回検索したキーワードに該当する投稿がない場合に「該当する投稿がありません」と表示されるようにしました。
該当する投稿があった場合はeach文で取り出します。

以上です。

間違っているところやもっと効率の良い書き方があれば教えていただけると助かります。
Script IncludeのApprovalUserFeedbackについてメモ

実装上のメモ書き

## 機能
対応するシステムプロパティglide.approvals.ui_feedback がTrueの場合、承認可否によって、ダイアログメッセージを出すみたいな機能。

```
gs.addInfoMessage(gs.getMessage("Approved {0}",
 current.getDisplayValue("document_id")));
```

## どこで見つかるか
CSMあたりのUI ActionのApproveボタンの動作に内部スクリプトとして存在する。社内のアクションにはボタンが二つ定義されていた。

## 用途
承認のたびにメッセージダイアログなので、アクションボタンからは削除してよいと思われる。ただ、OOTBなので、コピーしてから削除することがよい。

## 少し参考
[ApprovalUserFeedback Script include, how to co-opt?](https://tech-software-tokyo.blogspot.com/2022/01/approvaluserfeedback-script-include-how.html)
WPFアプリ終了時にAccess violationが発生する。

開発しているWPFアプリが、終了時にAccess Violationを投げて異常終了していることがわかりました。なんかGoogleIMEが関係しているようなのですが、本当のところはよく分かりません。
環境はWindows10,VisualStudio2019です。

#起こったこと
WPFアプリを開発しているとき、Debugモードで実行中にアプリ側で実行終了すると「プログラムはコード(0xC0000005) Access Violation で終了しました。」と出力されることに気づいた。必ずではなく、出ないときもある。
うううー、デバッグモードでしか出ないし、どっちにしろ終了時なのだから実行に問題ない！！　放っておいても良いかな・・・と頭をよぎったのだが、Windowsのイベントログを見てみたらリリースモードの実行でもバリバリにエラー出ていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112029/d87f4c45-6b92-7cd8-eb3d-068ddf24a3eb.png)


#やったこと
とりあえずググったが・・・、英語も含めて情報が少ない！（皆さんお困りではないのか？）
見つけたのがこの記事。
https://qiita.com/MiyakoG/items/9898c41c12cf948eda39

>「プロジェクトのプロパティ＞デバッグ＞ネイティブ コードのデバッグを有効にする(T)」にチェック

で実行したところ、GoogleIMEの名前がついたDLLが例外を投げているところまで分かった。が、それで行き止まり。

#さらにやったこと
えーい、力技じゃ、ということで、アプリを分解してエラーが出ないところまで戻り、１個づつ組み立てながら発火点を探すという作業・・・。
それで、TextBoxを使うとエラーが出ると分かった。
TextBox？IME？でひらめいたのでした。

#最後に分かったこと
原因はGoogle日本語入力（2.25.3700 現時点の最新）でした。IMEをMicrosoft IMEに切り替えたら直りました。おわり。実害はないけど前から出ていたのかなあ？不思議です。
なお、TextBoxのIME無効化スイッチは効果ありませんでした。

#やっぱりなんかおかしい
その後いろいろ試しているのですが、他のPCで実行するとAccess Violationではなく、0x4000001fで終了したり、そもそもWindow+TextBoxだけのアプリでは落ちなかったりするので、まだ調査いるみたいです。
今のところGoogleIMEを外すと問題なくなるという点だけは確実ですが。
dejirenからSPA Cloudへの接続手順　ファイル一覧の表示

#はじめに
2022年1月にバージョンアップしたdejirenからSPA Cloudに接続してファイル一覧を取得する手順のメモです。

#手順
1.接続設定からSPA Cloudへの接続設定を作成する
2.VAを作る
3.ルームを作成し動かす

#1.接続設定からSPA Cloudへの接続設定を作成する
dejirenへログインし「接続設定」を選択します。  
画面右上の「新しい接続設定を作る」をクリックします。

![dejiren_spa01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/43520032-8599-9f92-7382-92a95021e485.png)

SPAを選択し次へボタンをクリックします。
![dejiren_spa02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/9f10f7a5-4c23-f54e-0327-3d97f3e96347.png)

接続に必要な情報を入力し画面上部の接続確認を実行します。
![dejiren_spa03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/fb54584d-c7ca-0de3-c591-7f8b32e1deff.png)

接続成功のメッセージが表示されればOKです。
![dejiren_spa04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/294b590c-4d89-82e3-dffc-5dc26220c0c1.png)


画面右下の「作成する」ボタンをクリックし接続設定を作成します。  


#VAを作る
私のVAもしくはみんなのVAをクリックし画面右上の「新しいVAをつくる」をクリックします。
![dejiren_spa11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/72fc8de9-faea-131b-2890-3ca0a15df5dd.png)


必用事項を入力します。  
今回はわたしのVAが動くきっかけに「メッセージを受け取ったとき」を選択します。
「次へ」ボタンをクリックします。
![dejiren_spa12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/22ca372d-7b91-0312-ae70-cbe46bad27a5.png)

連携先に今回は「dejiren」を選択し、作成するをクリックします。
![dejiren_spa13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/635d6d1d-62a1-2f2c-c0ad-3831646d39a0.png)

手順書作成画面へ遷移します。
![dejiren_spa14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/7d01c4c6-d5c8-9be9-3ca1-7187fed242cc.png)


何かしらのメッセージをVAが受け取ったら特定のSPAフォルダ内にあるファイル一覧をチャットへ投稿する手順を作成します。  
完成した手順のイメージです。
![dejiren_spa15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/43f163d3-f0ee-e070-314e-bafcf0ca1196.png)

フォルダー情報では以下のように利用するSPAの接続と、フォルダーパスを指定します。
![dejiren_spa16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/0be1a10e-d785-c4a5-c51f-70c52101fc9b.png)

まとめて処理では対象に「ファイル一覧」を指定します。
![dejiren_spa17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/70245682-a774-1b20-50a9-24c9d5354834.png)

ファイル情報では「ファイルID」を指定します。
![dejiren_spa18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/87a99044-20a7-3f99-c086-c04d3dc0814c.png)

最後にファイル名を投稿する設定を行います。
![dejiren_spa19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/13e2387d-671e-3b02-450b-baf1a238c876.png)

VAの設定は以上になりますので、VAを稼働中にしてください。

#ルームを作成し動かす
WEBクライアントを開き、ルームを作成します。
参加メンバーと作成したVAをメンバーへ追加します。
![dejiren_spa21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/ac6da152-d9c1-caad-485e-1148e95b77b1.png)

作成したルームに何かメッセージを送信するとファイル名の一覧が表示されます。
![dejiren_spa22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46156/b6554533-4c2c-eb6c-b573-d43b7fd63b1a.png)

#最後に
この記事では接続設定から動作までの最小限の内容をご案内することを目的に、ファイル一覧の表示を行う手順をご紹介しました。
実務への適用を考えると検索やファイルの取得といった機能の追加が必要となると思いますが、そのあたりはまた別の記事でご紹介できればと思います。
@includeについて

## `@include` とは

bladeの共通箇所を子ファイルとして作成し、親ファイルで呼び出す時に@includeが使える

## 使い方

親ファイル`resorces/views/parent.blade.php`の中で、子ファイル`resorces/views/child.blade.php`を読み込む場合

### 親ファイル parent.blade.php 内での記述

```parent.blade.php
@include('child')
```

とすれば読み込める。

### 変数も渡せる

また、親ファイルで持っている変数を子ファイルに渡すこともできる。
例えば変数`$data`を渡したい時は、、

```parent.blade.php
@include('child', ['data' => $data])
```

とすれば子ファイルでも$dataを使用することが可能。
Route::matchについて

## 同じURLのルーティングをまとめる事ができるRoute::match

```php
Route::get ('sample/aaa',  'HogeController@index')->name('hoge.index');
Route::post('sample/aaa',  'HogeController@index')->name('hoge.index');
```

このように、リクエストが`get`なのか`post`なのか、が違うだけで２行書かなければいけないところを`Route::match`を使う事で、

```php
Route::match(['get', 'post'], 'foo','HogeController@index')->name('hoge.index');
```

とまとめることができる。

## getとpostで異なるアクションにしたい場合の使い方

```php
Route::get ('sample/aaa',  'HogeController@index')->name('hoge.index');
Route::post('sample/aaa',  'HogeController@postIndex')->name('hoge.index');
```
このようなpostとgetで呼び出すアクションが異なる処理を

```php
Route::match(['get', 'post'], 'foo','HogeController@index')->name('hoge.index');
```

このようにまとめてしまうと、当然アクションを分けることができません。
そのため以下のように、Controller内でリクエスト種別を判定して処理を分岐させる必要があります。

```php
    public function index(Request $request)
    {
        if ($request->method() == 'POST')    //POSTだったら
        {
            return $this->postIndex($request);    //postIndexへ
        }
 
        return view('hoge/index');
    }
 
    public function postIndex(Request $request)
    {
```
Google Drive内のデータを検索してコピーしたデータを任意のフォルダに置くGAS


事務業務をしていると
「たくさんのファイルを添付して申請しなきゃいけない」のような事象によく見舞われ
探しているファイルは特定の場所にあったり、なかったり、、、
そのファイルを集めるのが大変という困り事がありました

この困り事を少し解決してくれたGASのスクリプトを紹介します

# 機能紹介
特定の文字列でGoogleDrive内を検索
ヒットした１件目のファイルをコピーして指定のフォルダ内に置く
検索対象は複数個指定可能
ファイルの重複は無視する

## 補足
実際の運用状況が、検索対象であるファイルはファイル名に重複なしのIDが振ってあり
検索文字列をIDにすることで、希望のファイルがコピーされてきました。

すべての職場が↑の状況とも限らないのでこの点だけ留意して参考にしてください。



## 全体のコード
```js
LIST_SHEET_FILE_ID = '(検索文字列が記載されたシートID)';
LIST_SHEET_NAME = '(検索文字列が記載されたシート名)';
FOLDER_SHEET_NAME = '(保存先のフォルダURLが記載されたシート名)';
FOLDER_URL_CELL = '(保存先のフォルダURLが記載されたセル番号)'
// FOLDER_URL= '(保存先のフォルダURL)'

// 実行関数
function myFunction() {
  // ファイルの置き先のフォルダIDを取得
  const targetFolderId = getFolderIdByUrl();
  // 検索文字列を取得
  const sheetDataArray = getSheetData();
  // 検索文字列ごとに検索をして、対象フォルダに置く
  for (sheetData of sheetDataArray) {
    copyfile(searchFile(sheetData), targetFolderId);
  }
}

// URLをparseしてID部分を取得
function getFolderIdByUrl() { 
  const url = getTargetFolderUrl();
  try {
    const [regex, match] = url.match(/folders\/(.+)/);
    if (match === '') {
      throw new Error('folder idが取得できませんでした');
    }
    return match;
  } catch(e) {
    console.error('フォルダーIDの取得に失敗しました: ' + e);
  }
}

// シートからフォルダURLを抜いてくる
function getTargetFolderUrl() {
    try {
      const sheet = SpreadsheetApp.openById(LIST_SHEET_FILE_ID).getSheetByName(FOLDER_SHEET_NAME);
      // URLが記載されたセル番号を指定
      return sheet.getRange(FOLDER_URL_CELL).getValue();
      // return FOLDER_URL // シートから取得せずに環境変数から取得しても良さそう
    } catch(e) {
      console.error('シート取得に失敗しました: ' + e);
    }
}

// スプレッドシートから検索文字列一覧の取得
function getSheetData() {
  const rangeValuesArrayList = (() => {
    try {
      const sheet = SpreadsheetApp.openById(LIST_SHEET_FILE_ID).getSheetByName(LIST_SHEET_NAME);
      // A列で書かれた検索文字一覧を取得
      return sheet.getDataRange().getValues();
    } catch(e) {
      console.error('シート取得に失敗しました: ' + e);
    }
  })();

  const dataArray =[];
  for (rangeValueArray of rangeValuesArrayList) {
    if (rangeValueArray[0] === '') {
      break;
    }
    dataArray.push(rangeValueArray[0]);
  }

  return dataArray;
}

function searchFile(title) { // ファイル名をパラメータにしてファイルを取得(1件目)
  const searchParam = `title contains "`+ title + `"`;

  try {
    const searchedFiles = DriveApp.searchFiles(searchParam);
    const file = searchedFiles.next();
    const getFile = DriveApp.getFileById(file.getId());

    Logger.log('ファイル検索に成功しました: '+ getFile);
    
    return getFile;
  } catch(e) {
    console.error('ファイル検索に失敗しました: ' +e);
  }
}

// 渡されたファイルをコピーして指定フォルダに置く
function copyfile(file,tagetFolderId) {
  try {
    const setFolder = DriveApp.getFolderById(tagetFolderId); // コピーファイルの置き先
    const setFile = file.makeCopy(setFolder)
  
    Logger.log('ファイルコピーに成功しました: '+ setFile);
  
    return setFile;
  } catch(e) {
    console.error('ファイルのコピーに失敗しました: ' + e);
  }
}
AWS Backupスケジュール設定

# 経緯
EC2インスタンスのバックアップを定期的に取得したい。

Lifecycle Manager でも定期的にバックアップを取得できるがEBSボリュームのスナップショット取得のみなので、AMIで取得する設定をAWS Backupで行いたい。

参考：[AWS BackupとLifecycle Managerの違いってなに？使い分けを考えてみる](https://tech.katomoh.net/difference-between-awsbackup-and-dlm/)

# スケジュール設定

AWS Backupを使用する。
参考：[AWS Backup とは](https://docs.aws.amazon.com/ja_jp/aws-backup/latest/devguide/whatisbackup.html)


## バックアップボールドの作成

AWS Backup ＞ バックアップボールト　より　「バックアップボールドを作成」

* バックアップボールド名を入力
* 「バックアップボールドを作成」押下

## バックアッププランの作成
AWS Backup ＞ バックアッププラン　より　「バックアッププランを作成」

 **新しいプランを立てる** 

* バックアッププラン名を入力

 **バックアップルールの設定** 

* バックアップルール名を入力
* 「バックアップボールド」で先ほど作成したものを選択
* 「バックアップ頻度」で設定したいスケジュール（毎時・12時間ごと・毎日・毎週・毎月）を選択する。選択肢にない場合はカスタムcronを選び、式を入力する
* 「バックアップウィンドウ」で「バックアップウィンドウをカスタマイズ」を選択
* 「バックアップウィンドウの開始時間」でバックアップを行いたい時間をUTCで入力
* 「次の時間以内に開始」「次の時間以内に完了」にそれぞれ時間を設定
* 「保持期間」を設定　※これがLifecycle Managerでの世代管理
* 「プランを作成」押下

 **リソースを割り当てる** 

* 「リソース割り当て名」入力
* 「IAMロールを選択」でロールを選択
* 1．リソース選択を定義　で「特定のリソースタイプを含める」を選択：「EC2」選択
* 2・3・4でリソースを設定し、「リソースを割り当てる」：対象EC2のインスタンスIDを指定

* バックアッププランが作成されたら、必要分のリソースやプランの追加

# 履歴の確認 

* AWS Backup ＞ バックアップボールト　＞　該当のボールド名　より、復旧ポイントやステータスを確認
* AWS Backup ＞ ジョブ　より、時間になったらバックアップ履歴を確認

# 注意点
* バックアップルール以外は作成したら編集ができないため、名前変更などの際は再作成の必要がある。
* バックアップウィンドウの「次の時間以内に開始」では、設定した開始時間からどのくらいの時間内で開始するかの設定。「次の時間以内に開始」が1時間以内、「次の時間以内に完了」が2時間以内だと、バックアップが設定した開始時間から2時間以内に行われる。その時間以内に設定された別のスケジュールはスキップされる（保存期間が長いものが優先される）
* 複数のバックアッププランで時間が被ると、保存期間が長い方が保存される。
* EC2 のスナップショットの画面で AWS Backup で取得したかを確認するには、タグを aws:backup:source-resource で検索。
* ログの確認は　CloudTrail ＞ イベント履歴 「DescribeRecoveryPoint」を確認。
* バックアップウィンドウの開始・完了を最短（1時間以内に開始、2時間以内に完了）にしてもバックアップに20～30分ほどかかる場合が多いので注意。