Node.jsからIBM Cloud Object Storageへ接続する際のメモ

# はじめに

Node-REDからIBM Cloud Object Storageへ接続する際にエンドポイントの設定でハマったので、原因調査のためnode-red-contrib-cosモジュールで利用しているibm-cos-sdk (npmパッケージ)を使用した際のメモを残しておきます。

https://qiita.com/kitazaki/items/d8ac26a60ac25d58f188

# 準備

## 1. ibm-cos-sdk (npmパッケージ)のインストール

適当のディレクトリを作成して、npmパッケージをインストールします。(例えば、testというディレクトリを作成します)

```bash
$ mkdir test
$ cd test
$ npm init -y
$ npm i ibm-cos-sdk
```

## 2. サンプルコードを作成

IBM Cloudのリファレンスページを参照して、サンプルコードを作成します。

https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-node

```javascript:client.js
var ibm = require('ibm-cos-sdk');

var config = {
 endpoint: '<エンドポイント>',
 ibmAuthEndpoint: 'https://iam.cloud.ibm.com/identity/token',
 apiKeyId: '<APIキーID>',
 serviceInstanceId: '<サービスインスタンスID>',
};

var cos = new ibm.S3(config);

function getBuckets() {
    console.log('Retrieving list of buckets');
    return cos.listBuckets()
    .promise()
    .then((data) => {
        if (data.Buckets != null) {
            console.log(data);
        }
    })
    .catch((e) => {
        console.error(`ERROR: ${e.code} - ${e.message}\n`);
    });
}

function getBucketContents(bucketName) {
    console.log(`Retrieving bucket contents from: ${bucketName}`);
    return cos.listObjects(
        {Bucket: bucketName},
    ).promise()
    .then((data) => {
        if (data != null && data.Contents != null) {
            for (var i = 0; i < data.Contents.length; i++) {
                var itemKey = data.Contents[i].Key;
                var itemSize = data.Contents[i].Size;
                console.log(`Item: ${itemKey} (${itemSize} bytes).`)
            }
        }    
    })
    .catch((e) => {
        console.error(`ERROR: ${e.code} - ${e.message}\n`);
    });
}

function getItem(bucketName, itemName) {
    console.log(`Retrieving item from bucket: ${bucketName}, key: ${itemName}`);
    return cos.getObject({
        Bucket: bucketName, 
        Key: itemName
    }).promise()
    .then((data) => {
        if (data != null) {
            console.log('File Contents: ' + Buffer.from(data.Body).toString());
        }    
    })
    .catch((e) => {
        console.error(`ERROR: ${e.code} - ${e.message}\n`);
    });
}

getBuckets();
getBucketContents('cloud-object-storage-82-cos-standard-1o6');
getItem('cloud-object-storage-82-cos-standard-1o6', 'test.txt');
```

### config

ここで重要になるのはconfigに設定するendpoint、apiKeyId、serviceInstanceIdの値です。

apiKeyID と serviceInstanceId は、Cloud Object Storageの「サービス資格情報」の"apikey"と"resource_instance_id"を値を設定するのが正解です。
endpoint は バケットの設定(回復力・ロケーション)によって値が異なります。

![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f37323437392f61393633323632332d303634662d623436302d306436392d3561333139366562383761312e6a706567.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/799bcdcb-4e98-90a3-75d6-3aed0e8e91e6.jpeg)

### 間違いやすい点①

apiKeyIDと"apikey"で名称が微妙に違うことと、serviceInstanceIdは"resource_instance_id"で名称が異なっているのが間違いやすい点です。
「サービス資格情報」には iam_apikey_description や iam_apikey_name など、apikeyを含む似たような名称があったり、
serviceInstanceIdは、「サービス資格情報」のiam_serviceid_crnと混同しました。(serviceとidを含む名称のため)

### 間違いやすい点②

<b><font color=#FF0000>endpointは「サービス資格情報」の"endpoints"ではありません。</font></b>

エンドポイントの情報は以下にあります。

https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-endpoints#endpoints

例えば、アクセス先のバケットの回復力がRegional、ロケーションがjp-tokの場合、エンドポイント(パブリック接続)は
s3.jp-tok.cloud-object-storage.appdomain.cloud
になります。

# サンプルコードの実行例

### configに誤りがある場合

エンドポイントが間違っていてもバケットの一覧は表示されます。
バケットの中身や指定したファイル(オブジェクト)は表示されません。
(ERROR: NoSuchBucketやERROR: NoSuchKeyになります)

```bash
$ node client.js

Retrieving list of buckets
{
  Buckets: [
    {
      Name: 'cloud-object-storage-82-cos-standard-001',
      CreationDate: 2021-05-07T08:08:36.589Z
    },
    {
      Name: 'cloud-object-storage-82-cos-standard-1o6',
      CreationDate: 2021-05-07T03:31:45.802Z
    },
    {
      Name: 'cloud-object-storage-82-cos-standard-257',
      CreationDate: 2021-05-07T11:37:55.016Z
    },
    {
      Name: 'cloud-object-storage-82-cos-standard-ztk',
      CreationDate: 2021-05-06T07:51:49.717Z
    }
  ],
  Owner: {
    DisplayName: '77c04f4b-8e89-4636-be0c-be8eb1331f15',
    ID: '77c04f4b-8e89-4636-be0c-be8eb1331f15'
  }
}

Retrieving bucket contents from: cloud-object-storage-82-cos-standard-1o6
ERROR: NoSuchBucket - The specified bucket does not exist.

Retrieving item from bucket: cloud-object-storage-82-cos-standard-1o6, key: test.txt
ERROR: NoSuchKey - The specified key does not exist.
```

### configが正しい場合

```bash
$ node client.js

Retrieving list of buckets
{
  Buckets: [
    {
      Name: 'cloud-object-storage-82-cos-standard-001',
      CreationDate: 2021-05-07T08:08:36.589Z
    },
    {
      Name: 'cloud-object-storage-82-cos-standard-1o6',
      CreationDate: 2021-05-07T03:31:45.802Z
    },
    {
      Name: 'cloud-object-storage-82-cos-standard-257',
      CreationDate: 2021-05-07T11:37:55.016Z
    },
    {
      Name: 'cloud-object-storage-82-cos-standard-ztk',
      CreationDate: 2021-05-06T07:51:49.717Z
    }
  ],
  Owner: {
    DisplayName: '77c04f4b-8e89-4636-be0c-be8eb1331f15',
    ID: '77c04f4b-8e89-4636-be0c-be8eb1331f15'
  }
}

Retrieving bucket contents from: cloud-object-storage-82-cos-standard-1o6
Item: test.txt (22 bytes).

Retrieving item from bucket: cloud-object-storage-82-cos-standard-1o6, key: test.txt
File Contents: This is a test file.
```

# まとめ

みなさま、IBM CloudのLiteアカウントで良きNode-REDライフを！

### 参考

https://stackoverflow.com/questions/64053759/service-amazon-s3-status-code-404-error-code-nosuchbucket-trouble-getting