Oracle FunctionsでOCI Go SDKを使いインスタンス操作

　Oracle FunctionsでOCI Go SDKを使用してインスタンスを操作します。
操作といってもインスタンス名を変更するだけですが、それを発展させてOCI Go SDKを使おうという説明も最後に記載しています。

　Oracle Functionsって？という方は[こちら](https://qiita.com/y-araki-qiita/items/f73f815ea60a93978cac) をご覧下さい。

　まずインスタンスを建てた時、このインスタンス名ってコンソール上で変更できないですよね？
<img width="420" alt="キャプチャ.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131627/f7c50cbe-34ce-1285-83ea-7774ca77c8f0.png">
何でやねんと思いますが。。今回この「araki-test01」というインスタンス名をOracle Functionsで「araki-test02」に変更します。


※Oracle Functions開発サーバ内で作業。セットアップ方法は[こちら](https://qiita.com/y-araki-qiita/items/d1ed0551d8943adc3bb5)。

今回こちらのソースコードを使います。
https://github.com/abhirockzz/fn-oci-compute

　インスタンスのリストを表示するファンクションと、インスタンスの表示名を変更するファンクションに分けて説明します。

# インスタンスのリスト表示 

## ソースコード用意
```
クローン
git clone https://github.com/abhirockzz/fn-oci-compute.git

移動
cd fn-oci-compute/list
```

## SDK用クレデンシャル設定
　クレデンシャルを設定する方法は複数ありますが、今回はDockerfileに記載する方法でやります。
　ちなみにOCI SDKを使うのに必要なクレデンシャル情報は以下です。
・Tenancy OCID
・User OCID
・KeyFingerprint
・Passphrase(鍵に設定していれば)
・Region

```
vi Dockerfile
※ ENV OCI_PRIVATE_KEY_FILE_NAME=${PRIVATE_KEY_NAME}の下に以下追記

ENV TENANT_OCID=テナンシーのOCID
ENV USER_OCID=Oracle Functions用ユーザのOCID
ENV REGION=ap-tokyo-1 (環境に合わせて)
ENV FINGERPRINT=Oracle Functions用用ユーザのフィンガープリント
```
## デプロイ
Oracle Functions用ユーザで作成したプライベートキーを使用してデプロイします。

```
鍵を移動
cp ~/.oci/<プライベートキー名> .

一時的に権限変更(デプロイ後権限を戻すか削除)
chmod 644 ./<プライベートキー名>

fn -v deploy --app fn-compute-app --build-arg PRIVATE_KEY_NAME=<プライベートキー名>
```

## ファンクション実行
インスタンスリストを表示させたいコンパートメントのOCIDを渡して実行します。  

```
実行(とりあえずjqで形整えてます。)
echo -n '{"CompartmentIDFilter":"コンパートメントID"}' | fn invoke fn-compute-app list | jq

※以下のように表示されます。
[
  {
    "OCID": "インスタンスOCID",
    "DisplayName": "araki-test01"
  }
]
```
# インスタンス名変更
今までと全く同じような内容、流れでやります。

```
場所を移動
 cd ../update/

クレデンシャル情報記載
vi Dockerfile
※ ENV OCI_PRIVATE_KEY_FILE_NAME=${PRIVATE_KEY_NAME}の下に以下追記

ENV TENANT_OCID=テナンシーのOCID
ENV USER_OCID=Oracle Functions用ユーザのOCID
ENV REGION=ap-tokyo-1 (環境に合わせて)
ENV FINGERPRINT=Oracle Functions用用ユーザのフィンガープリント

鍵を移動
cp ~/.oci/<プライベートキー名> .

一時的に権限変更(デプロイ後権限を戻すか削除)
chmod 644 ./<プライベートキー名>

fn -v deploy --app fn-compute-app --build-arg PRIVATE_KEY_NAME=<プライベートキー名>
```

## インスタンス名変更ファンクション実行
先程表示されたインスタンスのOCIDを使いインスタンス名を変更します。

```
echo -n '{"OCID":"先ほど表示されたaraki-test01のOCID":"新しいインスタンス名"}' | fn invoke fn-compute-app update
```

すると以下のようにインスタンス名が変更されていることがわかります。
<img width="421" alt="キャプチャ2.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131627/e49e2c73-2acc-8157-c803-ef97846549de.png">


## OCI Go SDKについて

クレデンシャル情報を設定するだけでOCI SDKを使うことでき、
様々なOCIリソースを操作することができます。

OCIは現在、以下4つの言語でSDKが使用できるようですが、今回はGoの話をします。
Java,Python,Ruby,Go

　[こちらのサイト](https://github.com/oracle/oci-go-sdk)を表示するとOCI Go SDKに用意されているコードを確認することができます。この中で、今回はcommonとcoreというSDKプログラムを使っているので、インスタンス名変更の実行ファイルfunc.goを簡単に解説したいと思います。

まずパッケージのインポート部分です。

```go
package main

import (
	"context"
	"encoding/json"
	"io"
	"io/ioutil"
	"log"
	"os"
        // ↓GoでFnのコードを書くための開発キット、fdkを使うと明示
        // リンク: https://github.com/fnproject/fdk-go
	fdk "github.com/fnproject/fdk-go"
        // ↓認証設定のためoci-go-sdkのcommonを使うと明示
	"github.com/oracle/oci-go-sdk/common"
        // ↓インスタンス名変更のためoci-go-sdkのcoreを使うと明示
	"github.com/oracle/oci-go-sdk/core"
)
```
　oci-sdk-goのcommonを使っていますが、これはクレデンシャルの認証情報を設定するのにcommonが必要だからです。
　プログラムの途中に以下のコードがあります。

```go
rawConfigProvider := common.NewRawConfigurationProvider(tenancy, user, region, fingerprint, string(privateKey), common.String(passphrase))
```
　これはクレデンシャル情報をcommonのNewRawConfigurationProvider関数に渡すことで、Go SDKツールを使う設定をしているからです。

　それではコードの続きを一気にざっと見ていきます。

```go
// ファンクションを実行するメイン関数。fdk-goの関数で実行している。
// ociComputeEventHandlerには以下のコードのメイン処理が格納されている。
func main() {
	fdk.Handle(fdk.HandlerFunc(ociComputeEventHandler))
}

const privateKeyFolder string = "/function"
const successMsg string = "Updated Compute Instance information successfully"

func ociComputeEventHandler(ctx context.Context, in io.Reader, out io.Writer) {

     // クレデンシャル情報の格納
	tenancy := os.Getenv("TENANT_OCID")
	user := os.Getenv("USER_OCID")
	region := os.Getenv("REGION")
	fingerprint := os.Getenv("FINGERPRINT")
	privateKeyName := os.Getenv("OCI_PRIVATE_KEY_FILE_NAME")
	privateKeyLocation := privateKeyFolder + "/" + privateKeyName
	passphrase := os.Getenv("PASSPHRASE")

     // ログ出力。ファンクション失敗時確認するので必ず出力する。
	log.Println("TENANT_OCID ", tenancy)
	log.Println("USER_OCID ", user)
	log.Println("REGION ", region)
	log.Println("FINGERPRINT ", fingerprint)
	log.Println("OCI_PRIVATE_KEY_FILE_NAME ", privateKeyName)
	log.Println("PRIVATE_KEY_LOCATION ", privateKeyLocation)

     // プライベートキー格納とエラー検知
	privateKey, err := ioutil.ReadFile(privateKeyLocation)
	if err == nil {
		log.Println("read private key from ", privateKeyLocation)
	} else {
		resp := FailedResponse{Message: "Unable to read private Key", Error: err.Error()}
		log.Println(resp.toString())
		json.NewEncoder(out).Encode(resp)
		return
	}

     // 先程説明したクレデンシャル情報をcommonのNewRawConfigurationProviderに渡す
	rawConfigProvider := common.NewRawConfigurationProvider(tenancy, user, region, fingerprint, string(privateKey), common.String(passphrase))

    // 設定したクレデンシャル情報をcoreのインスタンスコンフィグを扱う関数を使って、
    //"cc"という変数に渡している。
	cc, err := core.NewComputeClientWithConfigurationProvider(rawConfigProvider)

    // エラー検知
	if err != nil {
		resp := FailedResponse{Message: "Problem getting Compute Client handle", Error: err.Error()}
		log.Println(resp.toString())
		json.NewEncoder(out).Encode(resp)
		return
	}

     // インスタンス名変更のための変数を設定。
     // UpdateInfoには変更するインスタンスOCID、インスタンス名が渡されている(一番下の構造体より)
	var updateInfo UpdateInfo
	json.NewDecoder(in).Decode(&updateInfo)
	log.Println("UpdateInfo ", updateInfo)

       // 以下インスタンス名変更部分。
       // coreのUpdateInstanceRequest関数を使っている。
	_, updateErr := cc.UpdateInstance(context.Background(), core.UpdateInstanceRequest{InstanceId: common.String(updateInfo.OCID), UpdateInstanceDetails: core.UpdateInstanceDetails{DisplayName: common.String(updateInfo.NewDisplayName)}})
       // エラー検知
	if updateErr != nil {
		resp := FailedResponse{Message: "Problem updating instance", Error: updateErr.Error()}
		log.Println(resp.toString())
		json.NewEncoder(out).Encode(resp)
		return
	}

	log.Println(successMsg)

	out.Write([]byte(successMsg))
}

// echo -n でjson形式で渡すインスタンスID,新しいインスタンス名を
//↓の構造体に格納する。
type UpdateInfo struct {
	OCID           string
	NewDisplayName string
}

//FailedResponse ...
type FailedResponse struct {
	Message string
	Error   string
}

func (response FailedResponse) toString() string {
	return response.Message + " due to " + response.Error
}
```

以上です。ざっくりな流れとしては、、、
・必要なパッケージ、oci-go-sdkのパッケージを最初にインポート
・クレデンシャル情報をcommonの関数を使い格納
・それをインスタンス名変更するcoreの関数に渡して実行

　クレデンシャル情報を設定し、後は先程紹介した、[こちらのサイト](https://github.com/oracle/oci-go-sdk)にたくさんのGo SDKプログラムが格納されているので、
こちらを使いこなせばOracle Functions上でどんなリソースも操作できることになります。

　今回の記事を発展させてOCI SDKでいろんなファンクションを作成してみようと思います。