OmniAICreator/Qiita-1.07M huggingface dataset:
【Unity】iOSのカメラ使用時にiOS実機ビルドでクラッシュする時の対処法

UnityでiOSのカメラを使う際は、WebCamTextureクラスを使います。
以下のショートコードでプラットフォームを越えてカメラ情報を取得することが出来るので本当にUnityは便利です。

```WebCamTest.cs
[SerializeField] RawImage _rawImage;
void Start()
{
    var webCamTexture = new WebCamTexture();
    _rawImage.texture = webCamTexture;
    webCamTexture.Play();
}
```

UnityEditor上で実行する上では問題なく動くと思います。
しかし、XcodeでiOS端末で実行すると、Xcodeのコンソールで以下のエラーを吐いて落ちる時があります。

本記事はその対処法備忘録です。


>[access] This app has crashed because it attempted to access privacy-sensitive data without a usage description.  The app's Info.plist must contain an NSCameraUsageDescription key with a string value explaining to the user how the app uses this data.


## Camera Usage Descriptionへの入力必須

`PlayerSettings > OtherSettings > Camera Usage Description`
この項目に何かしらの文字列を入れる必要があります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328771/d2b83404-5060-5eae-abcd-9a62e3717fd8.png" width=450 />

Xcodeのinfo.plistの`Privacy - Camera Usage Description`に追加される事になります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328771/10d0d0c9-1043-0eb8-3b18-980045eee2cf.png" width=500 />

Camera Usage Descriptionを設定してiOS実機ビルドするとクラッシュしなくなると思います。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328771/639022f4-f62d-c36f-0cd1-8c3d1bd935c4.png" width=250 />
ちなみに、`Camera Usage Description`はカメラ使用時の許可ダイアログ内の説明文で使用されます。


## 環境

- iPhone6s iOS 13.6
- Unity2019.4.4f1
- Xcode 11.6
ダミー化はオープンクエスチョンをクローズドクエスチョンにする

機械学習の勉強をしていてモヤモヤしていた部分を解消したのでメモ
# ダミー化
機械学習に用いるデータセットで定性的な情報を数値化することと理解
何かのカテゴリーなどを、適当に数値化するにはどうしたらいいかわからなかった。

```python:sample
headers = ['country', 'salary', 'job']
rows = ['JP', '6000000', 'engineer']
```

これで例えばcountryが日本、中国、アメリカの場合に、下記のようにやってしまうのはNG

```py:sample

{'JP': 1, 'US': 2, 'CH': 3}

``` 
距離を考えて正しくはこう

``` py:sample
headers = ['is_jp', 'is_us', 'is_ch']
ch = [0,0,1]
jp = [1,0,0]
us = [0,1,0] 
```

# タイトルの意味
元のデータでは、お住まいの国はどこですか？といオープンクエスチョンに対して、回答を求めた結果をカラムにいれいてる形。
ダミー化した物は、お住まいの国は〇〇ですか？と言うクローズドクエスチョンに対しての回答結果をそれぞれ入力している形。

すごく基本的なことなのだろうが、へーとなったので備忘録として記録。
Snowflake の TIMEZONE と TIMESTAMP型(３種類)の違い

SnowflakeのTIMEZONE や TIMESTAMP型で 少しハマったので、メモしておきます。
あとで、もう少しキレイにするつもり。


# デフォルトTIMEZONE は America/Los_Angeles
SnowflakeのデフォルトのTimezoneは America/Los_Angeles になっています。次のSQLを実行すると確認できます。

```sql
SHOW PARAMETERS LIKE 'TIMEZONE';
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214131/f77dcaf3-4c87-df1b-634e-4ed10f976db9.png)

なので、CURRENT_TIMESTAMPを表示すると、米国カリフォルニア時間で返ってきます。

```sql
SELECT CURRENT_TIMESTAMP;
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214131/0a37f387-3fbd-206b-8a00-f3b660af476b.png)

とりいそぎ日本時間で表示したければ、SESSION の TIMEZONE を変更すればOKです。

```sql
ALTER SESSION SET TIMEZONE = 'Asia/Tokyo';
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214131/0f6b3b26-76d2-8a16-64ab-327a08e460f5.png)

```sql
SELECT CURRENT_TIMESTAMP;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214131/3b3afefc-d997-7b0f-b85e-a496976791ab.png)

# TIMESTAMP型は３種類。それぞれの違い。
あとで書く

## TIMESTAMP_LTZ
## TIMESTAMP_NTZ
## TIMESTAMP_TZ
【SVN,Git】TortoiseGitを用いてコミット履歴込みでSVNリポジトリをGitへ移行する。

メモとして残します。

わりと手間取ったのでメモ

[Git to Git ](https://qiita.com/msht0511/items/467a0cbffb4fed60f885)は記事にまとめましたが、SVN to Gitへのリポジトリ移行について今回はまとめます。

#■やり方
###0.[重要]Windows32bit版のGitをインストール
64bitバージョンだと既知の不具合か何かで、`git svn clone`コマンドが
こけるらしいので、Git32bitをインストール。
###1.任意のフォルダで右クリメニューGitクローン(複製)...をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213462/1d743838-5eff-50f6-f460-66494b5d4b77.png)
###2.SVNのURLをセット
URL・・・SVNのURLを入力
ディレクトリ・・・ダウンロード先フォルダを入力
SVNリポジトリからにチェック
**注意）**
・**トランク、タグ、ブランチがある場合は、URLもこれらがある階層を含むアドレスにして、チェックを入れる。**
・**最新のリジョンのみクローンする場合は`From`に最新のリビジョンNoの数字を入力して実行すること**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213462/a0b7cee2-8c18-77df-c08a-b24c47e133a7.png)



###3.SVNからダウンロード＆Git用に自動変換
完了すると下部に青地で成功と表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213462/14542abb-d2e5-32b6-fc0d-b078b0d109d1.png)

注意）リビジョンが多いほど時間がかかりますのでじっくり待つ。

###4.Originのリモートをセット
```bash
git remote add origin https://hogehoge.com/hoge.git
```

###5.ローカルの情報をリモートにpush
```bash
git push -u origin master
```

以上
playgroundでRxSwiftを試す

## 何をやりたいか

RxSwiftの勉強を始めたばかりで、
とりあえずplaygroundでサンプルコードを実行したいと思いますが、
ライブラリのimportがうまくいかなかったので、メモとして残します。
少しでも役に立てれば幸いです。

## 結論

playgroundでimportする前に、一回プロジェクトをビルドする必要があるかもしれないです。

## やり方

### 新規プロジェクトの作成

iOSのSingle View APPを選択して、適当な名前をつけて作成します。
そしてプロジェクトを閉じて、ターミナルでライブラリの導入をやります。

### ライブラリの導入

導入するのによく使われているツールはCocoaPods、Carthage、Swift Package Managerがありますが、今回はCocoaPodsで入れます。

* CocoaPodsをインストール
* プロジェクトのルートディレクトリに移動し、`pod init`でPodfileファイルを生成
* Podfileファイルに必要なライブラリを記入  

```shell
target 'LearnRxSwift' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!

# Pods for LearnRxSwift
pod 'RxSwift', '~> 5'
pod 'RxCocoa', '~> 5'

end
```

* `pod install`でインストール開始(２回目以降は`pod update`)
* エラーがなければこれでインストール完了

### playgroundファイルの作成

* CocoaPodsが作成してくれた`プロジェクト名.xcworkspace`ワークスペースを開く
* 一回ビルドします(デフォルトの設定でも良い)
* 図のようにルートにplaygroundを作りたいので、`command`キーを押しながら、選択中のものをクリックして、選択状態を解除します  
<img width="269" alt="スクリーンショット 2020-08-04 15.17.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594064/8f3fa0c5-f277-c03b-8595-f76703647089.png">
* メニューバーから、`File > New > File... > iOS `で一番下のBlankのplaygroundを選択して生成します。
* これでimportができるようなる


## 参考リンク

[How to import RxSwift in Playground of xcode?](https://stackoverflow.com/questions/42082864/how-to-import-rxswift-in-playground-of-xcode)
[Testing RxSwift code in Playgrounds using tappable buttons bound with RxCocoa](https://medium.com/@alexandrosbaramilis/testing-rxswift-code-in-playgrounds-using-tappable-buttons-bound-with-rxcocoa-b71310ba98e3)
textareaの高さを入力に合わせて自動調整する


地味に面倒くさい、textareaの高さ自動調整。

```js
$("textarea").height( $("textarea")[0].scrollHeight );
```
※ 適時イベントハンドリングすること。

これぐらい、デフォルト仕様になればいいのにね。
端末起動時のディスクの自動暗号化→自動マウント/Linux

# はじめに

- 端末を再起動するとLUKSでの暗号化・マウントが解除されてしまうので、端末の起動後、自動で暗号化→マウントする方法について書く。
- 環境は「CentOS7」で実施。
- LUKSによる暗号化については以下を参照。  
 ⇒　[cryptsetupコマンドを使用したディスクの暗号化](https://qiita.com/subretu/items/620058932e0db93c6604)


# １．キーファイルの作成・登録

- これまではパスワードで認証を行っていたが、自動化のためにキーファイルで認証を行えるようにする。

##  keyfileの作成
```
dd bs=512 count=4 if=/dev/urandom of=/etc/mykeyfile
```
## keyfileに600の権限を付与（所有者の読込･書込のみ）
```
chmod 600 /etc/mykeyfile
```
## keyfileの登録
```
cryptsetup luksAddKey /dev/sde /etc/mykeyfile

Enter any existing passphrase:  <- 既存のパスフレーズを入力
```
##  keyfileの登録を確認
````
cryptsetup luksDump /dev/sde

LUKS header information for /dev/sde

Version:        1
Cipher name:    aes
Cipher mode:    xts-plain64
Hash spec:      sha256
Payload offset: ＃＃＃
MK bits:        2＃＃
MK digest:      ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃
MK salt:        ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃
                ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃
MK iterations:  ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃
UUID:           ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃

Key Slot 0: ENABLED
        Iterations:             ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃
        Salt:                   ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃
                                ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃
        Key material offset:    ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃
        AF stripes:             ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃
Key Slot 1: ENABLED
        Iterations:             ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃
        Salt:                   ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃
                                ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃
        Key material offset:    ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃
        AF stripes:             ＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED
````


# ２．キーファイルによる暗号化の確認

- すでに暗号化・マウントされているなら、現在のマウント・暗号化デバイスの解除を行い、キーファイルによる暗号化、マウントまで完了できることを確認する。

``````
# keyfileによる暗号化
cryptsetup luksOpen /dev/sde hogehoge --key-file /etc/mykeyfile
# マウント
mount /dev/mapper/hogehoge /mnt/hogehoge/
``````

## ３．crypttabへの登録
- crpttabに設定を書くことで、OS起動時にLUKSへの接続を行い暗号化を行えるので、以下のように設定する。

``````
vi /etc/crypttab

# 以下を追記
# {target name} {source device} {key file} luks, {options}
# timeout=5･･･パスワードを入力しないまま5秒放置したら無視して続行

hogehoge /dev/sde /etc/mykeyfile luks,timeout=5

``````

## ４．rc.localへのデバイスのマウント処理追記（権限付与をすること）
- rc.localは起動プロセスの最後に実行されるシェルスクリプトであり、ここにマウント処理を記述し、起動の最後にマウントを行うようにする。

``````
vi /etc/rc.local

# 以下を追記
# 今まで手動で行っていたmountのコマンド
mount /dev/mapper/hogehoge /mnt/hogehoge/
``````

権限付与

``````
chmod 755 /etc/rc.d/rc.local
``````
Internet Explorerでは try { } catch { } は動かない

JavaScriptにて例外処理を書いていまして、ビックリしたんですけど、
この書き方（`try { } catch { }`の引数を省略した形）だと、EdgeとIEで動きませんでした。

```javascript
try {
	throw new Error('error.');
} catch {
	console.log('error.');
}
```

エラーが出ました。

```
SCRIPT1005: SCRIPT1005: Expected '('
```

## こう書かないと

```javascript
try {
	throw new Error('error.');
} catch(e) {
	console.log('error.');
}
```

ChromeやFireFoxでは大丈夫だったのですが、`catch`の引数`e`が必要でした。
できれば省略したかったのに・・・。

バージョン
Microsoft Edge 44.18362.449.0
Internet Explorer 11.0.195
Google スプレッドシートから複数のCSVをローカルに保存するスクリプト

Google スプレッドシートから複数の CSV を書き出す際、
シートごとに「ファイル」>「ダウンロード」>「カンマ区切りの値」とする必要があった。
まとめてダウンロードできるようにして手間を省くため書いたもの。

先行事例として下記の記事を参考にした。
[GoogleAppsScript JavaScriptを用いてCSVをローカルに書き出す実装 - Qiita](https://qiita.com/ayies128/items/315f161bc14c36327fcd)

参考記事のスクリプトだと GAS 側から関数一回呼び出して、文字列で返り値を受け取っていた。

```js
var content = <?= getData(); ?>;
```

配列を HTML 側で受け取りループ回して CSV 吐き出すほうがシンプルなため
`google.script.run.withSuccessHandler` を使用。

## 実装

```code.gs
// メニューバーにカスタムメニュー追加
function onOpen() {
  const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  const entries = [
    {
      name: "CSV出力",
      functionName: "csvDownload",
    },
  ];
  spreadsheet.addMenu("その他", entries);
}

// ポップアップ起動
function csvDownload() {
  const html = HtmlService.createTemplateFromFile("dialog").evaluate();
  SpreadsheetApp.getUi().showModalDialog(html, "ダウンロード");
}

// HTML側でスプレッドシートの中身取得＆整形
function getData() {
  // スプレッドシートの値を取得
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
  const values = sheet.getDataRange().getValues();

  // 整形処理
  const dataList = [{
    title: "CSVのファイル名",
    body: "CSVの中身文字列",
  },...]
  return dataList;
}
```

```dialog.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top" />
    <script>
      window.onload = () => {
        handleDownload();
      }
      function handleDownload() {
        google.script.run.withSuccessHandler((dataList) => {
          dataList.forEach(data => {
            const blob = new Blob([ table.body ], { "type" : "text/csv"});
            const link = document.createElement('a');
            link.download = `${table.title}.csv`;
            link.href = window.URL.createObjectURL(blob);
            link.click();
          });
          google.script.host.close();
        }).getData();
      }
    </script>
  </head>
</html>
```

## 参考
[Class google.script.run (Client-side API)  |  Apps Script](https://developers.google.com/apps-script/guides/html/reference/run)
はじめてのAngular入門ガイド

Angularプロジェクトの概要と始め方を記載します。Versionの変更がたびたびあるので、内容はリンクは最新のVersionのページを確認すること。

社内教育に使っている資料なのでメモ書きです。

## URL
[Angular ドキュメント](https://angular.io/docs)

[Angular 基本概念](https://angular.io/guide/architecture)

[Angular チュートリアル](https://angular.io/tutorial/toh-pt0)

# アプリケーション生成

```tsx
ng new application-nameでプロジェクト構成
- Routingの設定を聞かれるので > y
- cssの設定は scssで
```

起動コマンド

```tsx
ng serve
```

http://localhost:4200で画面


## 続き
自分のブログに詳細を書いています。
https://codenote.online/notebooks/16
SeoとSERPとは何ですか？ インターネットソリューション

当社のウェブサイトをGoogleに表示するにはどうすればよいですか？次に答えはSEOです。 SEOはSearch Engine Optimazionの略です。つまり、ウェブサイトを最適化すると、検索エンジンでランク付けされます。

デジタルマーケティングの世界でSEOは重要な役割を果たします。広告を使ったプロモーションに加えて、それさえ主要なプレーヤーと見なすことができます。

![Untitled-2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608552/fb955d40-a889-0308-0ca8-761d7a005c4a.jpeg)

今日のオンラインマーケティングの傾向は、SEOを最も話題になっているトピックの1つにしています。

SEOとは何ですか、メカニズム、機能、説明はどうですか？この記事では、SEOについての重要な概要を説明します。

SEO：Optimazion検索エンジン
SEOは一連のプロセスです。体系的に行われること。ウェブサイトへのアクセス数やトラフィックの増加を目指しています。検索エンジンから供給。または一般的に呼ばれるオーガニック検索結果！

どのように、検索エンジンのアルゴリズムシステム自体のメカニズムに従うことによって。 SEOの目的は、検索ページの最上部にWebサイトを配置することです。または、少なくとも検索結果の最初のページ。特定のキーワードについて。対象となるもの。

論理的には、検索結果の上位を占めるWebは、ユーザーがアクセスする機会が多くなります。

社会生活におけるインターネットの使用の増加に伴い。この場合、誰もがインターネットを利用して、情報、サービス、製品の両方の形でニーズを見つけています。

したがって、自動的に、インターネットネットワークの使用もビジネスメディアに発展しました。 SEOの必要性も高まっています。

本質的に、誰が検索エンジンのページになりたくないのか。何百万という目で見られます。それは何かです...広告メディアの訪問数を利用する製品/サービスまたはパブリッシャーを販売できます。

検索結果の上位に位置し、明らかに潜在的な新規顧客を追加します。具体的には、Webベースのマーケティング会社向けです。この機会は、SEOメカニズムを理解している人によって利用されます。システムの検索エンジンについて「決定する」ことに夢中です。ページの位置を取得するウェブサイト/コンテンツ1. google、bing、yandexなど。

多くの人がデジタルマーケティングの成功を成功させてきましたが、その堅牢性を誤解し、過小評価していない人々がまだいます。ここに人々がマーケティングのステップで間違いをする3つの事柄があります。デジタルマーケティングは潜在的な顧客へのリーチに非効率的であるため、意味がないと考えるビジネスオーナーはまだたくさんいます。あなたは間違っている！さらに、デジタルマーケティングは、ビジネスをコミュニティとリアルタイムで週24時間接続する上で大きな力になっています。インターネット上の業界のイメージは、ビジネス開発に大きな影響を与え、競合他社と区別したいと考えています。それだけでなく、デジタルマーケティングにより、業界はビジネス開発に使用できるいくつかの貴重な情報を取得したいと考えています。市民の間でのもう1つの普遍的な誤解は、デジタルマーケティングの使用には費用がかかるということです。業界が負担する支払いは、選択した手順によって異なります。しかし、もちろん、デジタルマーケティングの使用がコストをカバーし、多くの利益を生み出すため、売上は増加します。今日、多くの産業は、その優れた能力により、マーケティングへの投資を増やし続けています

通常、ウェブサイトには2つの目的があります。お金を重視するウェブサイトと、知識を提供することを目的としたウェブサイトです。ウェブサイトがユーザーにとってのメリットを共有するウェブサイトであるとよいでしょう。なぜなら、ユーザーフレンドリーなウェブサイトは、通常、それでも収益を上げることができるからです。ビジネスに適したSEOを作成し、使用後の使いやすさを向上させます。 SEOフレンドリーとは、ウェブサイトに表示されるコンテンツを準備する際に、ウェブサイトが実際にSEOを実装することを意味します。それだけでなく、SEOフレンドリーなリスト、ウェブサイトの読み込み速度、キ​​ーワードの配置、品質と関連コンテンツ、着信リンク、発信リンクを確実にすることには多くの側面があります。などなど。

また、他の記事もご覧ください。
<a href="https://nphlogistik.com/">Jasa Sewa Truk dan Angkut Barang</a> | <a href="https://ambisiscreenprinting.com/">Jasa Konveksi Sablon</a> | <a href="https://www.efisienbangunan.com/">Jasa Desain Arsitek</a> | <a href="https://www.bosindogroup.com/jasa-sablon-kaos-polo-kemeja-jaket/">Pembuatan Polo Shirt</a> | <a href="https://bossablon.com/">Pembuatan Kemeja Korsa</a> | <a href="https://www.iklankita.id/">Tempat Iklan Gratis</a> | <a href="https://halosisgan.blogspot.com/">Tutorial dan Tips Internet</a> | <a href="https://indominingkonveksi.com/">Pembuatan Wearpack Murah</a> | <a href="https://jasasablon.com/">Pembuatan Kaos Sablon</a> | <a href="https://www.jasasablonkaosmurah.com/">Rekomendasi Jasa Konveksi Jogja</a> |  <a href="https://jasasablon.id/">Vendor Sablon Kaos</a>  | <a href="https://www.heindonesia.com/"> Blog Budaya Indonesia </a>
centos7のkernelアップデート

centos7のkernelアップデート手順のメモです。

#現在のkernelバージョン確認
```
uname -a
```
出力例
>Linux XXXXXX 3.10.0-229.el7.x86_64 #1 SMP Fri Mar 6 11:36:42 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

#既存パッケージの確認
```
rpm -qa | grep "^kernel"
```
出力例
>kernel-headers-3.10.0-229.el7.x86_64
kernel-3.10.0-229.el7.x86_64
kernel-tools-3.10.0-229.el7.x86_64
kernel-devel-3.10.0-229.el7.x86_64
kernel-tools-libs-3.10.0-229.el7.x86_64

#ELRepoの導入
```
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
rpm -Uvh https://www.elrepo.org/elrepo-release-7.0-4.el7.elrepo.noarch.rpm
```

以下のメッセージが表示された場合はcurlのバージョンアップが必要です。

```
curl: (35) Peer reports incompatible or unsupported protocol version.
```
```
yum update curl
```
#最新版kernelのインストール
```
#最新安定版をインストールする場合
$ yum --enablerepo=elrepo-kernel install kernel-ml

#長期サポート版をインストールする場合
$ yum --enablerepo=elrepo-kernel install kernel-lt
```
#デフォルト起動kernelの変更
```
awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg
```
出力例
>0 : CentOS Linux (4.15.2-1.el7.elrepo.x86_64) 7 (Core)
>1 : CentOS Linux (3.10.0-693.17.1.el7.x86_64) 7 (Core)
>2 : CentOS Linux (3.10.0-693.el7.x86_64) 7 (Core)
>3 : CentOS Linux (0-rescue-c03640e46bfe44989f5aa1a4dcd56b35) 7 (Core)

0番目のkernelで起動するように変更する場合

```
grub2-set-default 0
```
#関連パッケージのインストール
```
yum --enablerepo=elrepo-kernel swap kernel-tools-libs kernel-ml-tools-libs
yum --enablerepo=elrepo-kernel install kernel-ml-tools
```
順番通りインストールしないとコンフリクトが発生して失敗します。
最新版kernelインストールで長期サポート版を選択した場合はmlをltに変えて実行してください。

#参考サイト
https://hackers-high.com/linux/update-centos-kernel/
https://kou.bz/blog/centos7-kernel-verup.html
[インフラ]ドメインとは？

#ドメインとは？
ドメイン（domain）は、IPアドレスをわかりやすい名前に変換したものです。
URLに「xxxxx.com」や「xxxxx.co.jp」のような表示を見たことがあると思いますが、これがドメインです。
IPアドレスと同様にドメインも、世界に1つしか存在しません。

ドメインは人間がわかりやすいようにIPアドレスを変換したものなので、
PC同士で通信をするにはドメインをIPアドレスに変換する必要があります。
それをしてくれるものが、DNS（Domain Name System)です。

#DNS(Domain Name System)
DNSはドメインをIPアドレス変換してくれるシステムです。
図のように一度DNSサーバーへ経由して、接続先のPCへ到達します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681053/d25150e4-5799-f48b-e22f-482133eaa5d2.png)

#参考サイト
https://www.soumu.go.jp/main_sosiki/joho_tsusin/security_previous/juyogijutsu/dnssec01.htm
Vuejs CLIにて一部を外部htmlファイルを読み込んで実装する方法

# Vuejs CLI環境下で一部をhtmlで実装したい時
Vuejs CLIでvue拡張子のファイル内で一部をhtmlファイルを読み込んで実装したい時がある方、
その方々に、試行錯誤を経て分かった方法を共有します。

## 参考
- [Vue.jsで外部HTMLファイルをinclude(っぽく)する](https://qiita.com/ProjectEuropa/items/27d8604b857fc44dcdca)

## 環境の整理
- 開発環境：Vuejs CLIでインストールしたばかりの環境

##方法
1. [Vuejs CLI](https://cli.vuejs.org/)をインストールする。
2. HelloWorld.vueが生成される、その中を以下のように変更。

    ```HelloWorld.vue
    
    <template>
      <div class="hello">
        <p>Practice</p>
        <div v-html="htmlContent"></div>
      </div>
    </template>

    <script>
    export default {
      name: "HelloWorld",
      data() {
        return {
          htmlContent: "",
        };
      },
      mounted: function () {
        this.loadHtml();
      },
      methods: {
        loadHtml() {
          fetch("./html/sample.html").then((res) => {
            res.text().then((html) => {
              this.htmlContent = html;
            });
          });
        },
      },
    };
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    h3 {
      margin: 40px 0 0;
    }
    ul {
      list-style-type: none;
      padding: 0;
    }
    li {
      display: inline-block;
      margin: 0 10px;
    }
    a {
      color: #42b983;
    }
    </style>
    ```
肝は
＊v-htmlを使用
＊dataにv-htmlで使用しているデータhtmlContentを紐づける
＊mountedにて、fetch使用のメソッドloadHtmlを読み込む
＊methodsにて、メソッドloadHtmlを定義し、fetchで外部htmlを読み込んで、
　htmlをデータhtmlContentに格納する。

3. fetchメソッドに書いたパス（./html/sample.html）に
sample.html（＝今回対象となる一部htmlファイル）を格納する。
＊publicフォルダの中となる。
    ![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676239/272e6bc1-1518-98b2-286a-cd8ac95e75a9.png)

4. sample.html中は以下のようにstyle, htmlタグを書くことができる。

    ```sample.html
    <style>
      .text {
        color: red;
      }
    </style>
    
    <div class="text">Hello ! External.htmlだよ！ </div>
    ```

5. ```npm run serve```コマンドを実行、ブラウザーで確認すると、以下が確認できる。
    ![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676239/3178f350-46ed-5ff5-0229-49e9c4180aab.png)


＊```npm run build```コマンドを実行しビルドした場合、
　distフォルダ内にhtmlはそのまま格納される。
　（このsample.htmlを直した場合もブラウザーに問題なく反映された。）
    ![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676239/94742cb4-bbc5-e64d-c7d5-20400fa41781.png)
PHPのSwitch文の厳密な比較について

PHPのSwitch文は厳密比較`===`ではなく`==`で比較される

```php
$param = "2";

switch ($param) {
  case 2:
    echo "paramはInteger型の2です";
    break;
  case "2":
    echo "paramはString型の2です";
    break;
}
```
これの出力結果は`paramはInteger型の2です`
String型なのにInteger型と判断されてしまう
コレを解決するには

```php
$param = "2";

switch (true) {
  case $param === 2:
    echo "paramはInteger型の2です";
    break;
  case $param === "2":
    echo "paramはString型の2です";
    break;
}
```

こうしてやる必要がある
最初は条件式にtrueを入れるなんて！！！！と思ったけど

条件式に "2" をいれるとPHPがその時点で自動的にInteger型に型変換を行ってしまい
厳密な比較ができなくなってしまうようです
既存ファイルをGitHubにアップロードする方法

# 既存ファイルをGitHubに追加する

Gitで既存のファイルをアップロードする方法について手順をご紹介したいと思います。
                                    
**1. GitBashを立ち上げ、GitHubにアップしたいファイルにcdコマンドで移動する**
※ちなみに、cdはcreate directoryの略

**2. git add . コマンドを打つ**
cdコマンドで希望ディレクトリまで移動できたら、ステージに内容を追加するために、以下のコマンドを打ってください。

```git:GitBush
$ git add .
```

**3. git commit コマンドを打つ**
git add .　コマンドでステージに追加できたら、git commit でリポジトリに内容を追加します。

```git:GitBush
$ git commit
```

**4. remote repository を作成する**
GitHubを開き、New Repositoryを開きます。
その後、repository nameに任意の名前を入れて、Create repositoryでrepositoryを作成します。

![493edf904c9094ef7c0c39c2e6a566b6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620968/1d7f4a9a-2abf-29f5-97e5-f058b1ede2d2.gif)


以上の作業が完了したら、次に、以下のコマンドを打ちます。

```git:GitBush
$ git remote add origin https://github.com/Gitのアカウント名/作成したリポジトリ名.git
$ git push -u origin master
```

以上で、ファイルをGitHub上にアップできたと思います。

Git操作に不安がある方は、私も受講したUdemyの講座を受講するとよいかと思います！

https://www.udemy.com/share/1021OECUYecFdTR3w=/

# 考えられるエラーについて

リモートリポジトリを作成した直後に、上記コマンドを実行せずに、以下のコマンドを実行すると、以下のエラーが発生するので、ご注意ください。

```git:失敗例
error: src refspec master does not match any.
```

```git:解決方法
$ git add .
$ git commit 
```

加えて、もしかすると、git add . コマンドを打った時に、

```git:エラー
warning: CRLF will be replaced by LF in .vscode/settings.json.
The file will have its original line endings in your working directory
```

このようなエラーが出るかもしれません。
この時は、以下のコマンドを試してみましょう。

```git:解決方法
$ git config --global core.autoCRLF false
```

ちなみに、「https://qiita.com/yukari-n/items/e6107fe52772ef6a2df4」
を参考にさせていただきました。
※作成者様、ありがとうございます。
Python　指定フォルダをエクスプローラで開く

ファイルが存在するフォルダ名と同じフォルダを、別のサーバで探したいことがあったので作りました。



```
import pathlib
import os
import os.path
import glob
import subprocess
import shutil


p_dir = pathlib.Path(os.getcwd())
print(p_dir.name)
# dir
area = p_dir.name
srctext = r'C:\aaa\bbb\ccc\{}'

path1 = srctext.format(area)

path = os.path.realpath(path1)
os.startfile(path)


```

合っているのかどうかわからないけれど、もっと短くしてもできました

```
import pathlib
import os
import os.path
import glob
import subprocess
import shutil


p_dir = pathlib.Path(os.getcwd())

print(p_dir.name)
# dir

area = p_dir.name
os.startfile(os.path.realpath(fr'C:\aaa\bbb\{area}'))


```
[インフラ]IPアドレスの確認方法

##IPアドレスとは？
「IPアドレス（Internet Protocol address）」は、インターネット接続中のコンピュータを識別する番号のことで、インターネット上の住所と言われています。
インターネットで使われるIPアドレスは、世界に1つしかなく、インダーネット上でのデータのやり取りはIPアドレスを使って行われています。

#IPアドレスの確認方法
ターミナルを起動して以下の方法で確認することが可能です。

```
$ curl -s ifconfig.me
```

すると、カンマできり分けられた4つの数字が出てきます。
それがIPアドレスです。
Karabiner complex modification: shift + esc -> ~

## 動機
60%英語配列キーボードでEscと`` `/~ ``（所謂逆クォート/チルダキー）が基本的に同じ位置である。
プログラミングに`~`が不可欠なので、キーボードが対応していなくても、Shift + Escを押すと普通に`~`を出したい。

## コンフィグ
`~/.config/karabiner/karabiner.json`を開いて、下記のコンフィグを`rules`下に入れる。

```json
{
    "description": "Change shift + esc to ~",
    "manipulators": [
        {
            "from": {
                "key_code": "escape",
                "modifiers": {
                    "mandatory": [
                        "left_shift"
                    ],
                    "optional": [
                        "any"
                    ]
                }
            },
            "to": [
                {
                    "key_code": "grave_accent_and_tilde",
                    "modifiers": [
                        "left_shift"
                    ]
                }
            ],
            "type": "basic"
        },
        {
            "from": {
                "key_code": "escape",
                "modifiers": {
                    "mandatory": [
                        "right_shift"
                    ],
                    "optional": [
                        "any"
                    ]
                }
            },
            "to": [
                {
                    "key_code": "grave_accent_and_tilde",
                    "modifiers": [
                        "left_shift"
                    ]
                }
            ],
            "type": "basic"
        }
    ]
}
```
RaspberryPiを踏み台にしてPCをリモート操作

#はじめに
出先でPCをリモート操作したいけど電源をつけっぱなしにするのは電気代が、、、
ということでRaspberryPiを踏み台にしてPCをリモート操作できるようにします。

#準備
・TeamViewerのアカウント登録
　https://www.teamviewer.com/ja/ よりアカウントを登録してください。

・スマートプラグ設定
　購入したスマートプラグのマニュアルを見てください。

・RaspberryPiの基本設定
　ググってください。**TeamViewerをインストールする前提なのでGUIを使えるように**

・操作先PCのWake-On-LAN有効化
　ググって、MACアドレスを控えておいてください。

#踏み台用RaspberryPiセッティング
1. スマートプラグに接続します。
RaspberryPiはコンセントに差すだけで起動してくれるのでスマートプラグでリモートで電源を入れられます。

2. TeamViewerをインストールします。
https://www.teamviewer.com/ja/download/raspberry-pi/ よりRaspberryPi用のインストーラーをダウンロードしインストール

3. wakeonelanをインストールします。
```$sudo apt-get install wakeonlan```

#操作元端末セッティング
・TeamViewerをインストールします。
https://www.teamviewer.com/ja/download/ より任意のインストーラーをダウンロードしインストール

#操作先PCセッティング
・TeamViewerをインストールします。
https://www.teamviewer.com/ja/download/ より任意のインストーラーをダウンロードしインストール

#リモート操作実行
1. スマートプラグの電源を入れRaspberryPiを起動します。

2. 操作元端末のTeamViewerでRaspberryPiに接続します。

3. RaspberryPiからWake-On-LANし操作先PCを起動します。
```$sudo wakeonlan xx:xx:xx:xx:xx:xx（操作先PCのMACアドレス）```

4. 操作元端末のTeamViewerで操作先PCに接続します。
Amplifyで、STSの認証を直接行う

Amazon CognitoのIDプールを使って外部プロバイダーがウェブベースの認証を提供する場合に、**Cognitoが発行する認証の有効期限を伸ばす**ことがそのままでは難しかったので、その調査の記録を書いておきます。

# そもそもの認証フロー

開発者認証or外部プロバイダーどちらを使ったとしても、Amplifyの認証を素直に使うと下の拡張認証フローになります。

<img width="747" alt="スクリーンショット 2020-07-20 17.48.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213719/f588b6f9-aa7e-0cf2-8ecf-29dd42841f75.png">

> 引用元: 
https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/authentication-flow.html

この図を見てもらうとわかるのですが、最終的な認証情報はAWS STSから渡ってきます。そして、AWS STSでは認証情報を発行する際にその有効期限を指定することができます。しかし、拡張認証フローだとAmazon CognitoがAWS STSとやり取りしてしまうため、有効期限を指定することが出来ず、デフォルトの有効期限（`3600`秒）が指定されてします。

> https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_temp_request.html#api_assumerolewithwebidentity

そこで、先程のページに有る、基本的な認証フローを使うことになります。

<img width="747" alt="スクリーンショット 2020-07-20 17.55.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213719/1176f172-d3ff-a241-67b6-aa31795d3477.png">

> 引用元: https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/authentication-flow.html

見ての通り、AWS STSへ直接アクセスすることが出来ます。

# 実際のやり方

AmplifyでのCognito認証はすでに済んでいるものとし、AWS STSの認証に必要な下記のパラメータはすべて準備出来ているものとします。

- **(A)** `sts:AssumeRoleWithWebIdentity`アクションが実行可能なIAM Role（そしてそのARN）
- **(B)** 64字以下のセッション名として使う任意の文字列
- **(C)** `GetOpenIdToken...`で発行されたトークン

上記がそろえば、あとはさっくりAWS SDKのAPIを叩くだけです。

```js
const stsParams = {
  DurationSeconds:  900, // 最小900秒(15分)〜最大43200秒(12時間)まで設定することが出来ます。
  RoleArn:          `(A)の値`,
  RoleSessionName:  `(B)の値`,
  WebIdentityToken: `(C)の値`,
};
const sts = new AWS.STS();
const stsResponse = await sts.assumeRoleWithWebIdentity(stsParams).promise();
AWS.config.update({
  credentials: new AWS.Credentials({
    accessKeyId:     stsResponse.Credentials.AccessKeyId,
    secretAccessKey: stsResponse.Credentials.SecretAccessKey,
    sessionToken:    stsResponse.Credentials.SessionToken,
 }),
});
```

Amplifyの認証設定はいっさいやらないんかい！って感じですが、Amplifyのコードを見る限り、Credentialsクラスなどから認証情報を設定する方法はなさそうです。AWSのグローバルの認証情報を更新しておくと、Amplifyのもろもろはそれを見て勝手に認証してくれるようなので、この書き方に落ち着きました。

- ※ Amplifyのバージョンによって、aws-sdkのバージョンが違ってくるので、この書き方はできない場合があります。
- ※ この書き方はaws-sdk v2系を想定しています。
- ※サーバー側のAPIを用意できるなら、CognitoのEPをそのままモックしてしまうほうが良いです。
Django on Dockerの環境づくりの記事を見ながら構築してたら途中でERROR: for django  Cannot start service django: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"/usr/src/app/entrypoint.sh\": permission denied": unknownと出たので解決した

# エラー内容

```
ERROR: for django  Cannot start service django: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"/usr/src/app/entrypoint.sh\": permission denied": unknown
```


# 環境
Mac OS Catalina 10.15.4

# 経緯

[DockerでDjangoの開発環境を再構築！！！！](https://qiita.com/nokonoko_1203/items/e345f899ac9ac700d6a8)

を参考にDjango REST Frameworkの環境を構築していた所、Djangoの環境をDocker上にcomposeしようとしたら以上のエラー内容が出てきたのでメモ用に記事を書いておきます。

状況としては、DjangoとpostgreSQL、nginxのDockerfileを書いた後に`entrypoinst.sh`を作成し、DjangoディレクトリのDockerfile

```Docker:backend/containers/django/Dockerfile
# これ
COPY ./entrypoint.sh /usr/src/app/entrypoint.sh

COPY . /usr/src/app/

# これ
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
```
のコメントアウトを消して再び

```terminal
$ docker-compose up -d --build
```

コマンドを叩くと冒頭のエラーが出ました。

手っ取り早い解決策としては

[Error: Starting container process caused “exec: \”/docker-entrypoint.sh\“: permission denied”](https://stackoverflow.com/questions/54336677/error-starting-container-process-caused-exec-docker-entrypoint-sh-permi)

この記事を参考にしました。
エラー内容的にも権限が与えられていないのが原因とはわかっていたのですが、早く解決するためにググりました。

結論としてはDjangoディレクトリで

```terminal
$ chmod 755 entrypoint.sh
```

で権限を与え、再び

```terminal
$ docker-compose up -d --build
```

してあげると無事にDockerfileの変更が反映されます。


Dockerはいざ自分で触ると分からないことが多くて触るだけでも勉強になりますね。
[メモ] bundle binstubsを設定してbundle exec を省略できるように

# やりたいこと

Bundlerを使っていて起動する時に

```shell-session
$ bundle exec fastlane --version
```

とするのが面倒なので
単に

```shell-session
$ fastlane --version
```

で起動させられるようにしたい。


# やったこと
## 1. rbenv-binstubsをインストールする

```shell-session
$ brew install rbenv-binstubs
```


## 2. 各プロジェクト(リポジトリ)にbinstubsを設定する

```shell-session
$ bundle binstubs fastlane --path .bundle/bin
$ rbenv rehash
```

以下でもうまくいったがdeprecatedであるため非推奨:

```shell-session
$ bundle install --binstubs .bundle/bin
$ rbenv rehash
```

## 3. 確認する

```shell-session
$ which fastlane
/Users/foobarbaz/.rbenv/shims/fastlane
$ rbenv which fastlane
/path/to/your/repos/.bundle/bin/fastlane
```

のようにpathが出力されればshimsとpathが通っている

```shell-session
$ fastlane --version
fastlane 2.154.0
```

このように `bundle exe` なしで起動したらOK。


# うまくいった環境
macOS 10.15.6
Ruby 2.7.1
rbenv 1.1.2
Homebrew 2.4.9
fastlane 2.154.0
iterm 3.3.12


# 参考資料

https://qiita.com/circled9/items/1a3c6c38ee40de4cf5e3
sshとtmux aをワンライナーで

# 引用元

> Reattach with:
> 
> ssh -t server tmux attach

https://superuser.com/questions/490652/tmux-how-to-prevent-broken-pipe

# 例
①mac -> ②踏み台（tmux実行する）サーバ -> ③いじりたいサーバ
とあった場合、
①macで、以下のコマンドたたけば良いです。

```bash
ssh -t user_name@踏み台サーバ tmux attach
```

もう、sshで②踏み台（tmux実行する）サーバにつないでからtmux a ってしなくていいです。

便利。。。
ReactiveProperty v7.2.0 をリリースしました

ReactiveProperty v7.2.0 をリリースしました。

NuGet パッケージはこちら

[ReactiveProperty v7.2.0 | NuGet Gallery](https://www.nuget.org/packages/ReactiveProperty/7.2.0)

### 追加

`DisposePreviousValue` 拡張メソッドが追加されています。
`IObservable` の最新より前の値に対して `Dispose` の呼び出しを行います。
`ReadOnlyReactiveProperty<T>` に対して `Dispose` の呼び出しが必要な型などを `Select` で生成して使う場合に便利です。


```csharp
var source = new Subject<string>();

var rp = source.Select(x => new SomeDisposableObject(x))
    .DisposePreviousValue()
    .ToReadOnlyReactivePropertySlim();

source.OnNext("aaa");
source.OnNext("bbb"); // aaa から作られたものは Dispose される
source.OnComplete(); // ソースが終了したタイミングで現在の値も Dispose される
```
std::stoll 文字列をlong longの型に変換した際のエラー

# はじめに
ABC174 CをC++で解いた時にstollを使った謎のエラーに出会ったので、それを共有します。

# 実装例

```cpp:
string seven;
    for(long long i = 1; i <= 100; i++){
        seven.push_back('7');
        //printf("i %lld a\n", i);//デバッグ用プリント
        if(stoll(seven)%k == 0){
            printf("%lld\n", i);
            return 0;
        }
    }
```
stollを使って文字列を数字に変換するプログラムです。
for文を使うことで桁がどんどん増えていきます。

# 吐かれたエラー
```
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: stoll: out of range
Abort trap: 6
```
どうやらstollの範囲を超えたみたいです。
では、stollできる範囲はどれくらいでしょうか？
さっきのコードのコメントアウトを解除して実行すると、i = 20でエラーが吐かれました。
どうやら、stollは20桁が限界なようです。

正確には、stollが20桁で限界というよりもlong longのオーバーフローが原因みたいです。

```
i 19 a
i 20 a
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: stoll: out of range
Abort trap: 6
```

# まとめ
stollは20桁が限界(long longがオーバーフローする)なので、そもそも今回の問題にはこのような解法は間違っている。
普通に値をlong longで定義して毎回10倍した方が良い。(普通に10倍するとオーバーフローするので、毎回modをとる)
ServiceNowでレコードを複製したい

最近ServiceNowを利用した運用ツール開発に関わってまして、その中で知っておくと便利な知見をまとめておこうかと思います。

今回はレコードを複製したい場合。

ちょいちょいケースとして存在するので調べてみるとかなり短いスクリプトでやれそうです。

```javascript
var sourceRecordSysID = "[sys_id of your record here]",
	tableName = "[your table name here]";

var gr = new GlideRecord(tableName);

gr.initialize();
gr.get(sourceRecordSysID);
gr.insert();
```

ではこれをどこに書くかというと色々考えられますが、私は `Scheduled Jobs` でやりました。

- Filter Navigator（左メニューの検索窓）で `Scheduled Jobs` と入力
- メニューをクリック後、Table上部の `New` をクリック
- `Automatically run a script of your choosing` を選択
- `Run` Fieldの値を `On Demand` に変更（必要に応じて実行するモード)
- `Run this script` Fieldに上記のスクリプトを貼り付けてSubmit

この状態で再度スクリプトを開いて、 `Execute Now` を選択すると、指定したTableのレコードが複製されると思います。
Kubernetes中級(2)：Pod-QoS classes

<img width="471" alt="q1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519254/b4d24448-23a7-7ba4-a7fb-93c025baa67b.png">
上記の図のようにNodeのResourceにPodが3つあり、均等にリソースを使用している状態であると仮定する。
<img width="482" alt="q2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519254/cd22bc50-5d8e-4a83-96b0-fc431801233d.png">
Pod1が追加でResourceが必要になった場合はどうなるか
NodeにはResourceがないため、Pod1はResource不足でダウンになってしまうか、それともKubernetesが他のPodをダウンさせてPod1に必要なResourceを割り当てるか
<img width="479" alt="q3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519254/20ded1aa-e504-dbb5-c310-2bf24e513d18.png">
KubernetesはAppの重要度に応じてこの種の管理できるように3つの段階にQulity of Serviceをサポートしている。
<img width="479" alt="q4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519254/f47e0b4e-e4d4-e779-7e38-1449a57fc6e3.png">
この状態では、BestEffortに設定されたPodが一番最初にダウンされてResourceが返却され、Pod1は必要なResourceを使用できるようになる。
<img width="484" alt="q5.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519254/db171a0b-a60e-93e7-db63-354e185f93a5.png">
次はNodeにある程度のResourceが残っているが、Pod2からそれよりも多くのResourceを必要とする場合、Burstableに設定されたPodが
ダウンされてResourceが返却される。
Guaranteedが最後までPodを安全に維持させてくれるClassである。

QoSは特定のプロパティがあって設定するのはなく、ContainerにResource設定があり、ここでRequestsとLimitのMemoryとCPUをどのように設定するかによって決定される
<img width="711" alt="q6.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519254/8ca47415-431a-83dd-03b2-a0a53dc7787a.png">
Guaranteedは、Podに複数のContainerがある場合、全てのContainerにRequestsとLimitの設定が必要である。
そしてRequestsとLimitのmemory、cpuも設定をする必要があり、各Containerに設定されたMemoryとcpuの値は、RequestsとLimitが同じではないといけない。
このルールに一致すると、KubernetesがこのPodをGuaranteed Classに判断をする。
<img width="219" alt="q7.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519254/8dc52296-49d9-4afb-9811-e1e0d378bc2b.png">
どのContainerにもRequestとLimitが設定されていない場合はBestEffortとして判断する。
<img width="501" alt="q8.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519254/1b7d8fae-b938-fc17-ab5e-e7b5c551e057.png">
BurstableはGuaranteedとBestEffortの特徴を持っていると考えれば良い。
すべてのContainerにRequestとLimitが設定されているが、Requestの値が小さいか、Requestだけ設定がされており、Limitは設定されていない場合、またはPodのContainerが2つなのに一つは、完全に設定されているが、他の1つが設定されていない場合はすべてBurstable Classになる。
**このように3つのClassは、ContainerのRequestとLimitをどのように設定するかによって決定される。**
<img width="502" alt="q9.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519254/62ba02d5-4430-1bf2-6c05-194613f05385.png">
Burstableに設定された複数Podの中でどのPodが最初に削除されるかと言うとOOM(Out Of Memory)Scoreによって決定される。
上の図の2つのPodのRequest Memoryが5Gと8Gに設定されているが、Appが実際に使っているMemoryが同じ4GだとするとPod2のMemory使用量は75％であり、Pod3は50％である。
OOM ScoreがPod2が75％で大きいので、KubernetesはPod2を最初に削除する。
[Next.js] Next-AuthのCustom ProviderでMicrosoft Graph Authを有効にする

## 概要

Next.jsを使って，OAuth認証をするには，Next-Authというパッケージの利用がとても便利なのですが，デフォルトでライブラリに同梱されている認証プロバイダーは，老舗の認証ライブラリであるPassportなどに比べると，まだまだ限定的です．

しかし，だからといってNext-Authが使えないわけではありません．本稿では，Next-AuthのCustom Providerという仕組みを使って，2020年8月現在Built-in Providerには含まれていない，Microsoft Graph Authを実現する方法を紹介します．

## アプリの登録

事前に，[Azure Portal](https://portal.azure.com/)の「**Azure Active Directory**」→「**App Registration**」から，新規アプリの追加を行います．この時点で，Application Id (Client Id)が発行されます．

Secretは，「**Certificates & Secrets**」メニューから生成します．

OauthのRedirect URLの設定が必要になりますので，「**Authentication**」メニューから，開発用のURLである`http://localhost:3000/api/auth/callback/microsoft`を追加しておきましょう．

Scopeは必要なものを追加すればいいのですが，ここでは，`User.Read`のみとして進めます．

## .env.localの準備

登録が終われば，認証のための準備として，`.env.local`ファイルに先ほどつくったAppのIDやSecretを書いておきます，

```
// .env.local
NEXTAUTH_URL=http://localhost:3000/
MS_CLIENT_ID=xxx
MS_CLIENT_SECRET=xxx
MS_OAUTH_SCOPE="user.read"
```

## ずばり設定

以下の設定を，`pages/api/auth/[...nextauth].js`の`options.providers`に追加します．

```js
{
    id: "microsoft",
    name: "Microsoft",
    type: "oauth",
    version: "2.0",
    scope: process.env.MS_OAUTH_SCOPE!,
    params: { grant_type: "authorization_code" },
    accessTokenUrl: `https://login.microsoftonline.com/common/oauth2/v2.0/token`,
    authorizationUrl: `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?response_type=code&response_mode=query`,
    profileUrl: "https://graph.microsoft.com/v1.0/me",
    profile: (profile: MicrosoftUser) => {
    return {
        id: profile.id ?? "",
        email: profile.userPrincipalName ?? "",
        name: profile.displayName ?? "",
        image: "https://graph.microsoft.com/v1.0/me/photo/$value",
    }
    },
    clientId: process.env.MS_CLIENT_ID!,
    clientSecret: process.env.MS_CLIENT_SECRET!,
}
```

少しだけ補足しておくと，今回は，tenantIDを`common`にすることで，MicrosoftアカウントおよびMicrosoft認証を使うすべてのwork/schoolアカウントでのログインを許可しています．詳しくは，[こちら](https://docs.microsoft.com/en-us/graph/auth-v2-user#authorization-request)．

## Reactでの利用

あとは，以下のようなボタンをどこかにおいておけば，

```jsx
<button onClick={signIn}>Sign in</button>
```

クリック後に，`/api/auth/signin`へと遷移し，プロバイダーとして，"Micosoft"の文字が出ているはずです．
無事に認証が終われば，Next-Authが提供する，`useSession`フックを使って，ログインしているユーザーの情報を取得できます．

```tsx
// pages/index.tsx
const Home: NextPage = () => {
  const [session, loading] = useSession()

  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>Welcome to Next-Auth!</h1>

        <>
          {session && (
            <>
              Signed in as {session.user.email} <br />
              <button onClick={signOut}>Sign out</button>
            </>
          )}
        </>
      </main>
    </div>
  )
}

export default Home
```

はい，それでは^^
Unity+Android で画像保存が出来ない。

Unity + Android で画像保存を確認しようとしたら
うまくいかなかった

外部保存には以下のものを使用
https://github.com/yasirkula/UnityNativeGallery

それでこの記事を見て解決。
どうやら、設定を何かしら変えないと保存できない模様。
細かいことは気にせず動いたので問題解決とする。
https://qiita.com/Katumadeyaruhiko/items/c2b9b4ccdfe51df4ad4a
  
    public void SaveImage()
    {
        Sprite backCameraOn = Resources.Load<Sprite>("適当な画像");
        Texture2D frontImage = createReadabeTexture2D( backCameraOn.texture );

        NativeGallery.SaveImageToGallery(
            frontImage,
            "album",
            "bodygramFront");
    }

    Texture2D createReadabeTexture2D(Texture2D texture2d)
    {
        RenderTexture renderTexture = RenderTexture.GetTemporary(
                    texture2d.width,
                    texture2d.height,
                    0,
                    RenderTextureFormat.Default,
                    RenderTextureReadWrite.Linear);

        Graphics.Blit(texture2d, renderTexture);
        RenderTexture previous = RenderTexture.active;
        RenderTexture.active = renderTexture;
        Texture2D readableTextur2D = new Texture2D(texture2d.width, texture2d.height);
        readableTextur2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
        readableTextur2D.Apply();
        RenderTexture.active = previous;
        RenderTexture.ReleaseTemporary(renderTexture);
        return readableTextur2D;
    }
‘‘‘
ログって削除してるのに何で取ってるんですか？

※この記事は新入社員の問いにあることないことないことないこと話した備忘録です。

# 本日の困りごと
ログって最終的に消してますけど、何で取ってるんですか？
というか、ログっていつ見るものなんですか？

…と聞かれた。平和なプロジェクトだなぁ

# 経緯
[ログが重くて見れない件](https://qiita.com/pnd75/items/5c16cdd5970f3aa595a4)を根本解決していなかったので、平和なうちに「どうにかしてー」と頼んだら返ってきた言葉。

# 今回の解決方法
うーん。なんか最近説明してばっかだな。。

## そもそもログってなんやねん
ログというものの役割がわかれば疑問は解決すると思う。

「このサーバでどのような処理がされたか？」
「誰がアクセスしたか？」
「どこの処理が上手く動かなかったか？」
などの情報を記録しているものだ。
役割によって記録するものは様々で、何か起きた時に確認するものだ。

何も起きていなくても、運用者がシステムの持ち主と異なる場合などは「ホントに何も起きてないよ」を証明するために提出することもある。
…厳密に言うとちょっとニュアンスが異なる気がするのだが、上手く言えないので参考サイトでも。

[監査証跡とは｜「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word16934.html)

参考サイトを間違ったような気がしなくもない。

「何か起きた時に確認」と言っているように、何も起きていない時には確認することはあまりない。
そして基本的にシステムやサービスが動いている限り出力され続けるものなので、サーバの容量を圧迫する要因ともなっている。

なので、
* 定期的にローテートをする(バックアップをとる)
* 定期的に削除する
といった処理をすることが多い。
今回、私が依頼した「どうにかして」とはこういった処理を作ることだ。

「ローテートってなんやねん」と思われていたらいけないので説明しておく。
ログは何らかの設定がされない限り1つのファイルに出力を続けることが多い。
「XXXX.logというファイルにログを吐く」と設定すると
これにずっとログがたまることで1GB以上のテキストファイル(.logはテキスト形式のファイル)が出来上がってしまう。
1つのファイル容量が大きくなるとテキストエディタでは開けなくなってしまい「何かあった時」に素早く確認ができなくなってしまう。
そのため、日毎や容量毎にファイルをバックアップし、出力ファイルを切り替えることを「ログローテート」という。

## ログって消していいものなの？
消して良いか、悪いか…というのは、システムがどのような制約を受けているか？によるものだと思う。
この制約の一つが、「契約」だろう。

例えば、クライアントの命によってシステムの構築や運用保守を行っているとする。
そうすると、先ほど出てきた**監査証跡**の提出を求められることがあるだろう。
「1年に1回決まった時期に提出」だったり、「求められたら提出できるよう○年保管」だったり、契約の内容によって様々だ。
監査用以外のログファイルも保管年数や保管世代が設定されていることもあったりするので、それぞれのプロジェクトの規約に則ってもらえればいい。
ログ設計はまた別のお話かなって思う。
Nest.jsのgRPCで複数ファイル(パッケージ)に分割されたprotoファイルを読み込む

# About

Nest.jsのgRPC Microserviceにおいて、複数のファイルやパッケージに分割されたprotoファイルを読み込む方法が[公式チュートリアル](https://docs.nestjs.com/microservices/grpc)では分からなかったので、調べた結果を残しておきます。

# 前提

protoファイルとパッケージを次のような構成とします。

```sh
.
├── animal
│   ├── animal.proto
│   └── cat
│       └── cat.proto
└── human
    └── human.proto
```

```proto:animal/animal.proto
package animal;

service AnimalService {
  rpc GetAnimal (GetAnimalRequest) returns (GetAnimalResponse) {}
}
```

```proto:animal/cat/cat.proto
package animal.cat;

service CatService {
  rpc GetCat (GetCatRequest) returns (GetCatResponse) {}
}
```

```proto:animal/human/human.proto
package human;

service HumanService {
  rpc GetHuman (GetHumanRequest) returns (GetHumanResponse) {}
}
```

まとめると、パッケージも含めたサービスのリストは下記のようになります。

```
animal.AnimalService
animal.dog.DogService
human.HumanService
```

# 複数ファイル

まず、`main.ts`で指定する`GrpcOptions`は次のようになります。

```typescript:src/main.ts
  const app = await NestFactory.createMicroservice<GrpcOptions>(AppModule, {
    transport: Transport.GRPC,
    options: {
      url: '0.0.0.0:6565',
      // ポイント: パッケージを配列で指定
      // ポイント: サブパッケージ(animal.dog)を含めると重複になるので含めない
      package: ['animal', 'human'],
      // ポイント: protoファイルを配列で指定
      protoPath: [
        join(__dirname, 'proto/animal/animal.proto'),
        join(__dirname, 'proto/animal/dog/dog.proto'),
        join(__dirname, 'proto/human/human.proto')
      ],
    }
  });
  app.listenAsync();
```

次に、`Controller`側ですが、最上位パッケージ直下のサービスなのか、サブパッケージのサービスなのかで、`@GrpcMethod`デコレーターのサービス名の指定方法が変わってきます。

```typescript:最上位パッケージ直下のサービス
  @GrpcMethod('AnimalService', 'GetAnimal')
  getAnimal(req: IGetAnimalRequest): Promise<IGetAnimalResponse> {
    ...
  }

  @GrpcMethod('HumanService', 'GetAnimal')
  getAnimal(req: IGetHumanRequest): Promise<IGetHumanResponse> {
    ...
  }
```

```typescript:サブパッケージのサービス
  // dog.が必要
  @GrpcMethod('dog.DogService', 'GetDog')
  getAnimal(req: IGetDogRequest): Promise<IGetDoglResponse> {
    ...
  }
```

# 参考: 直面したエラー

## ケース1: サブパッケージを重複読み込み

`GrpcOptions`のパッケージしていにサブパッケージも含めてしまうと、次のようなエラーが発生してしまいました。

> Method handler for /animal.dog.DogService/GetDog already provided

```typescript:src/main.ts
  const app = await NestFactory.createMicroservice<GrpcOptions>(AppModule, {
    transport: Transport.GRPC,
    options: {
      url: '0.0.0.0:6565',
      package: ['animal', 'animal.dog', 'human'],
      protoPath: [
        join(__dirname, 'proto/animal/animal.proto'),
        join(__dirname, 'proto/animal/dog/dog.proto'),
        join(__dirname, 'proto/human/human.proto')
      ],
    }
  });
  app.listenAsync();
```

## ケース2: サブパッケージのサービス名の名前解決が不正

サブパッケージのサービス名の名前解決

> Method handler GetDog for /animal.dog.DogService/GetDog expected but not provided

```typescript
  @GrpcMethod('DogService', 'GetDog')
  getAnimal(req: IGetDogRequest): Promise<IGetDoglResponse> {
    ...
  }
```
GoogleタグマネージャーでCSSセレクタを指定する時はドットを付ける

# Click Classesのときは.（ドット）を付けない

例えば「トリガーの設定」でClick Classesを指定する時は、.（ドット）無しのクラス名（`class-name`）を入力しますよね。

<img width="950" alt="スクリーンショット 2020-08-04 12.09.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621295/fe1092dc-ef50-fbd4-883a-dfe0862db2c3.png">


# CSSセレクタのときは.（ドット）を付ける

ところが、トリガーのタイプ「要素の表示」を設定しようとCSSセレクタに `class-name` と書いたら発火しません。
なぜ…？と焦りましたが、.（ドット）を付けたらあっさり発火しました。

<img width="1006" alt="スクリーンショット 2020-08-04 12.04.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621295/2897cd13-0cf8-67d8-d367-28772e459725.png">

分かってしまえば些細なことなのですが、微妙に詰みかけたので記録しておきます。
PythonのSliceにモヤっとした話　がスッキリした話



#slice処理の違和感
前記事です：[PythonのSliceにモヤっとした話](https://qiita.com/kznSk2/items/4fce66a65c29b27c7956)

殆どのプログラマはおそらく、配列からある要素を抜き出したい場合添字にIndexを指定するというのが意識に刷り込まれているのではないでしょうか。

~~~:Index
        +++++++++++++++++++++++++++++++++++++
element + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 
        +++++++++++++++++++++++++++++++++++++
index   + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 +
~~~
ある配列の7個目の要素を抜き出したい場合、以下の様なコードと結果になる事に違和感を覚えるプログラマはまずいないでしょう。

~~~python
l=[1,2,3,4,5,6,7,8,9]
print(l[6])
#7
~~~
そのため、配列に対してslice操作を行う場合も、当然のようにIndexで範囲を指定しようとします。

~~~python
l=[1,2,3,4,5,6,7,8,9]
print(l[6:7])
#[7]
~~~
oh…　私はIndexの6～7を指定したつもりだったので、予想した結果は

~~~python
#[7,8]
~~~
なのですが、現実は非情でした。
#違和感の正体
結論から言えばsliceの処理で配列の添字とするべきは、IndexでなくLengthでした。

~~~:Length
        +++++++++++++++++++++++++++++++++++++
element + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 
        +++++++++++++++++++++++++++++++++++++
length  0___1___2___3___4___5___6___7___8___9
~~~

~~~python
l=[1,2,3,4,5,6,7,8,9]
print(l[6:7])
#[7]
~~~
Lengthの図表と処理を見比べ、添字がIndexではなくLengthなんだと脳内で置き換えてみると、長さ6～7の間にある要素を取得する処理となるため、出力結果にも違和感がなくなりました、ああスッキリ。

#でもなんでLength？
判りません。
range関数のようにLengthを指定してIndexの範囲を取得する処理はたしかに直感的で便利ですが、だからといってIndex基準が主流の配列の添字に対してまでLength基準での添字記法を態々混在させる程のどんな素晴らしい理由があるのか、結局判りませんでした。

#実はわかりやすい（？）Lengthを使った配列操作
上では配列の添字にLengthを使うことに対し否定的でしたが、実は誰しもLengthを添字にしようとしたことがあるはずなんです（多分）、私はあります。

あれは幼い頃、お年玉をはたいて近所のLaoxでDelphi2.0とはじめてのDelphiみたいな参考書を購入し（何故Delphiを選んだのかは判りません、多分GUIプログラミング言語の中で一番安かったとかそんな理由です）、意気揚々とプログラムの勉強を始めた時です。
参考書のサンプルを見ながらコードを組んではエラーと修正を繰り返しつつ、配列のページに差し掛かりました。

参考書「配列の最初の要素を取得するには、添字に0を指定します。」
僕「なんで？」

当時、周囲にその疑問に答えられる人は誰もおらず、渋々最初の要素を取りたいときは0、次の要素を取りたいときは1、と呪いのように意識に刷り込みました。

なので今回私が抱いた違和感も、実は全くプログラミングを知らない人ならそもそも違和感すら感じず受け入れるのかもしれません。
仮にすべての配列操作がLength基準によって行われる言語が主流になったなら、

参考書「配列の最初の要素を取得するには、添字に1を指定します。」
僕「なるほど」

こんな世界もあり得るのかもしれません。
#というわけで
結論としては、納得は行かないが理解はした、という感じです、あんまりスッキリしませんでしたね。

とはいえ、今更Length基準で配列操作を行うのが世界の主流になられても困ってしまいますし、配列の添字はこれからもできる限りIndex基準で統一して欲しい、と言う希望を述べつつ終わりとさせていただきます。
【python】SSHトンネル経由でMySQL接続する方法

SSHトンネル経由でMySQL接続する方法を紹介したいと思います。
例えばAWSを使用していて、VPC内にあるMySQLへアクセスするには一度踏み台サーバー経由しないと行けず、ローカル環境から直接出来ないですよね。こういったネットワーク環境構成は多いのではないかと思いますが、ローカル環境から直接MySQLに接続したい場面もあります。
![名称未設定 (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/11936/ff259ec1-2aff-bd40-c3c1-d0c65f8f7c3d.jpeg)
そんな時にsshtunnelというライブラリで実現することが出来るんですが、SSHポートフォワーディングを利用して、処理を実行しています。簡単にSSHポートフォワーディングについて説明したいと思います。
#SSHポートフォワーディングとは
クライアントのポートを、直接アクセス出来ないサーバーのポートに転送してくれる仕組みのことです。
コードを実行する前にSSHコマンドを利用して、ローカルからリモートサーバ先にあるHTTPサーバやMySQLサーバにアクセスすると、コードをより理解出来ると思います。

例えば、リモートサーバのIPアドレスが「10.1.1.1」だったとします。通常のSSH接続であれば、
```ssh [ユーザ名]@10.1.1.1
```となりますが、ポートフォワーディングをする場合、オプション「L」を付与します。
```SSH -L ローカルポート番号:リモートホストアドレス:ホストポート番号
```

リモートサーバ「10.1.1.1」のローカルホストにあるポート「3306」の「MySQLサーバ」にポートフォーディングする場合は、次のようになります。
```SSH -L 10022:127.0.0.1:3306 [ユーザ名]@10.1.1.1
```
ローカルポート番号は自由に指定することが出来ますが、MAMPやDockerなどで動かしているポートと競合しないように指定してください。ターミナルの窓を二つ起動し、片方で上記コマンドを実行した後に、ローカルからリモートサーバ先のMySQLに接続できることを確かめてみてください。
```mysql -h 127.0.0.1:10022 -u root -p
```
#実行コード
```python
#! /usr/env/bin python
# coding: UTF-8

import pymysql
import sshtunnel

with sshtunnel.open_tunnel(
    ("111.111.111.11", 22), // (踏み台サーバのIPアドレス, SSHポート)
    ssh_username="root",
    ssh_pkey="~/.ssh/ida_rsa", // 秘密鍵認証の場合は秘密鍵のパスを記述
    remote_bind_address=("127.0.0.1", 3306), // MySQLが踏み台サーバのローカルにある場合
    local_bind_address=('0.0.0.0', 10022) 
) as tunnel:
        connection = pymysql.connect(host="127.0.0.1",
                                user="hoge",
                                password="hogehoge",
                                db="hogedb",
                                port=10022)

```
SSHトンネル経由でMySQL接続することが出来ます。
#図解
![名称未設定-Page-2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/11936/b53c2055-47e7-a238-2906-3adf8bd0a0bd.jpeg)
恥ずかしいことに最初ポストフォーディングがあまり理解できておらず、remote_bind_addressとlocal_bind_addressの値を設定するか分からなかったのですが、図解と実際にSSHコマンドで実行することで何を指定すべきかが分かりました。

Pythonコードを利用して、SSHトンネル経由でMySQL接続する際につまずいてしまっている方がいましたら、上記試してみてください。
SafariでDeveloperメニューに対象デバイスが表示されない

まず、以下を確認する。

* Safari > Preferences > Advanced > Show Developer menu in menu barにチェックが入っている
* Settings > Safari > Advanced > Web InspectorがONになっている

がよくサイトに書いてあることだが、これだけでは表示されないことがある。

そのときは

**Xcode > Devices and Simulatorsで対象のデバイスの"Connect via Network"をONにする**

と解決した。