20191026のNode.jsに関する記事は7件です。

react-dropzone、Express、Firebaseを使った画像のアップロード

components/FileUpload.ts
const onDrop = (acceptedFiles: File[]) => {
     console.log(formData)
 }

return (
     <div>
         <DropZone  onDrop={onDrop}>
             {({getRootProps, isDragActive}) => (
                 <div {...getRootProps()}>
                     {isDragActive ? "Drop it like it's hot!" : 'Drag a file to upload!'}
                 </div>
             )}
         </DropZone>
     </div>
 )

react-dropzoneを使ってこんな感じに書いて、描画された部分に画像をドロップしてみると

lastModified: 1571463132902
lastModifiedDate: Sat Oct 19 2019 14:32:12 GMT+0900 (日本標準時) {}
name: "hoge.PNG"
path: "hoge.PNG"
size: 353348
type: "image/png"
webkitRelativePath: ""

こんな感じのFileっていう型の配列がもらえる。
んじゃあこのデータ配列そのままサーバ側にぶん投げて、サーバ側でfirebase storageにpath指定してあげりゃあ画像のアップロード実装できんじゃん!
って思ってたんだけど、見てわかる通りpathがfullpath(?)になってない。どうしよう...

最終的に、サーバ側に一時的に画像を保存して、そこで生成されたpathをfirebase側に指定してあげたらうまく行った。

そのために、まずはreact-dropzoneから受け取ったFile配列をFormDataとやらに変換して、それをPOSTデータとしてぶん投げた。

components/FileUpload.ts
 const onDrop = (acceptedFiles: File[]) => {
     const formData = new FormData();
     acceptedFiles.forEach(file => {
         formData.append('Files', file)
     })
     dispatch(postFiles.request(formData))
 }

参考にさせていただいたサイト様様 → https://qiita.com/uryyyyyyy/items/9954205a620b6f3c1f24

んで、これをExpress側で受け取るんだけど、このデータはmultipart/form-dataというものらしく、普通にやったらうまく受け取れなかった。なんやそれ。知らんがな。

app.ts
import multer from 'multer';

const upload = multer({ dest: './uploads/' });

app.post('/files', upload.fields([ { name: 'Files' } ]), postStorageController);
controller/postStorageController.ts
export function postStorageController(req: Request, res: Response, next: NextFunction) {
    const files: Express.Multer.File[] = req.files['Files'];

こういう風に書いてあげると、filesの部分で以下のような感じで値がもらえる

[Object: null prototype] {
  Files:
   [ { fieldname: 'Files',
       originalname: 'hoge.PNG',
       encoding: '7bit',
       mimetype: 'image/png',
       destination: 'uploads/',
       filename: 'd18eb9674f13a81898e7c25bb0c3bda6',
       path: 'uploads/d18eb9674f13a81898e7c25bb0c3bda6',
       size: 353348 } ] }

参考にさせていただいたサイト様様 → https://qiita.com/uryyyyyyy/items/9954205a620b6f3c1f24
(さっきと同じ記事。圧倒的感謝)

あとは、Google先生の解説を見ながらfirebase storageにあげれば終わり!

controller/postStorageController.ts
export function postStorageController(req: Request, res: Response, next: NextFunction) {
    const files: Express.Multer.File[] = req.files['Files'];

    files.forEach(fileInfo => {
        const uploadFilePath = `files/${fileInfo.originalname}`
        bucket.upload(fileInfo.path, { destination: uploadFilePath, contentType: fileInfo.mimetype }, error => {
            if (error) {
                console.log(`failed storage post ${fileInfo.originalname}`);
                console.log(error.message);
                return res.sendStatus(404);
            } else {
                console.log(`success storage post ${fileInfo.originalname}`);
                return res.sendStatus(200);
            }
    }
}

↓↓↓今回書いたソースコード

(フロント)
https://github.com/ShotaroOkada/fileupload_react_client

(サーバ)
https://github.com/ShotaroOkada/fileupload_express_api

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

react-dropzone、Express、firebaseを使った画像のアップロード

components/FileUpload.ts
const onDrop = (acceptedFiles: File[]) => {
     console.log(formData)
 }

return (
     <div>
         <DropZone  onDrop={onDrop}>
             {({getRootProps, isDragActive}) => (
                 <div {...getRootProps()}>
                     {isDragActive ? "Drop it like it's hot!" : 'Drag a file to upload!'}
                 </div>
             )}
         </DropZone>
     </div>
 )

react-dropzoneを使ってこんな感じに書いて、描画された部分に画像をドロップしてみると

lastModified: 1571463132902
lastModifiedDate: Sat Oct 19 2019 14:32:12 GMT+0900 (日本標準時) {}
name: "hoge.PNG"
path: "hoge.PNG"
size: 353348
type: "image/png"
webkitRelativePath: ""

こんな感じのFileっていう型の配列がもらえる。
んじゃあこのデータ配列そのままサーバ側にぶん投げて、サーバ側でfirebase storageにpath指定してあげりゃあ画像のアップロード実装できんじゃん!
って思ってたんだけど、見てわかる通りpathがfullpath(?)になってない。どうしよう...

最終的に、サーバ側に一時的に画像を保存して、そこで生成されたpathをfirebase側に指定してあげたらうまく行った。

そのために、まずはreact-dropzoneから受け取ったFile配列をFormDataとやらに変換して、それをPOSTデータとしてぶん投げた。

components/FileUpload.ts
 const onDrop = (acceptedFiles: File[]) => {
     const formData = new FormData();
     acceptedFiles.forEach(file => {
         formData.append('Files', file)
     })
     dispatch(postFiles.request(formData))
 }

参考にさせていただいたサイト様様 → https://qiita.com/uryyyyyyy/items/9954205a620b6f3c1f24

んで、これをExpress側で受け取るんだけど、このデータはmultipart/form-dataというものらしく、普通にやったらうまく受け取れなかった。なんやそれ。知らんがな。

app.ts
import multer from 'multer';

const upload = multer({ dest: './uploads/' });

app.post('/files', upload.fields([ { name: 'Files' } ]), postStorageController);
controller/postStorageController.ts
export function postStorageController(req: Request, res: Response, next: NextFunction) {
    const files: Express.Multer.File[] = req.files['Files'];

こういう風に書いてあげると、filesの部分で以下のような感じで値がもらえる

[Object: null prototype] {
  Files:
   [ { fieldname: 'Files',
       originalname: 'hoge.PNG',
       encoding: '7bit',
       mimetype: 'image/png',
       destination: 'uploads/',
       filename: 'd18eb9674f13a81898e7c25bb0c3bda6',
       path: 'uploads/d18eb9674f13a81898e7c25bb0c3bda6',
       size: 353348 } ] }

参考にさせていただいたサイト様様 → https://qiita.com/uryyyyyyy/items/9954205a620b6f3c1f24
(さっきと同じ記事。圧倒的感謝)

あとは、Google先生の解説を見ながらfirebase storageにあげれば終わり!

controller/postStorageController.ts
export function postStorageController(req: Request, res: Response, next: NextFunction) {
    const files: Express.Multer.File[] = req.files['Files'];

    files.forEach(fileInfo => {
        const uploadFilePath = `files/${fileInfo.originalname}`
        bucket.upload(fileInfo.path, { destination: uploadFilePath, contentType: fileInfo.mimetype }, error => {
            if (error) {
                console.log(`failed storage post ${fileInfo.originalname}`);
                console.log(error.message);
                return res.sendStatus(404);
            } else {
                console.log(`success storage post ${fileInfo.originalname}`);
                return res.sendStatus(200);
            }
    }
}

↓↓↓今回書いたソースコード

(フロント)
https://github.com/ShotaroOkada/fileupload_react_client

(サーバ)
https://github.com/ShotaroOkada/fileupload_express_api

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JSON形式のAPIレスポンス構造をNode.jsのREPLで理解する

この記事の対象者

JSON形式のレスポンスをJavaSciptで処理する際に、

  • どういう書き方で書けば欲しい情報が取り出せるのか知りたい
  • JSONレスポンスが大きすぎて構造がよくわからない
  • APIリクエスト制限が厳しくて何度もリクエストを投げられない
  • 参考記事には結果のコードしか載ってなくて初見のAPIをどうやればいいのか

ということがある場合。JSON形式のAPIの扱いに慣れていない人向けです。

どうやって理解するのか

一旦APIレスポンスをJSONファイルとして出力した後にNode.jsのREPLで読み込んで、対話形式で解析します。
一度JSONファイルとして書き込むことで何度もAPIリクエストを投げる必要がないのでAPIリクエスト制限に引っかかる心配がありません。

やり方

例としてGitHub APIにて react で検索した時の結果をAPIで取得します。
https://developer.github.com/v3/search/#search-repositories

ターミナルで下記のコマンドを入力します。
JSON形式のデータ整形に jq というツールを使っているので無い場合はコマンド brew install jq で入れてください。

$ curl https://api.github.com/search/repositories?q=react | jq .

ターミナルを埋め尽くすほどのAPIレスポンスが帰ってきました。
この状態だと何が何だか分からないので一回JSONファイルに出力します。

$ curl https://api.github.com/search/repositories?q=react | jq . > result.json
result.json
{
  "total_count": 1054700,
  "incomplete_results": false,
  "items": [
    {
      "id": 10270250,
      "node_id": "MDEwOlJlcG9zaXRvcnkxMDI3MDI1MA==",
      "name": "react",
      "full_name": "facebook/react",
      "private": false,
      "owner": {
        "login": "facebook",
        "id": 69631,
        ...
      }
      ...
    }
  ]
}

ざっくり構造が見えてきました。まずトップ階層に検索件数が表示され、具体的な中身は items の中に Object[] として格納されているので、items[n] または map 関数で取り出せそうだなという目安がつきます。

これからNode.jsのREPLでデータを取り出してみます。

$ node
Welcome to Node.js v12.3.0.
Type ".help" for more information.
> const data = require("./result")
> data
... // Object形式で出力

JSONファイルは require で読み込むことでJavaScriptの Object として読み込まれます。

Object形式なので下記のように入力するとデータを取り出すことができます。

> data.total_count
1054700

ここで items の中から full_name のみ取り出してみます。

> data.items.map(item => item.full_name)
[
  'facebook/react',
  'duxianwei520/react',
  'reactphp/react',
  ...
  'reduxjs/react-redux'
]

条件を絞ってみます。 獲得スターが30k以上 のリポジトリの full_name のみ取り出してみます。

> data.items.filter(item => item.stargazers_count >= 30000).map(item => item.full_name)
[
  'facebook/react',
  'facebook/react-native',
  'ReactTraining/react-router',
  'zeit/next.js',
  'facebook/create-react-app',
  'enaqx/awesome-react'
]

data.items.filter(item => item.stargazers_count >= 30000) では条件に合致する Object[] が帰ってくるのでそれを map 関数で取り出しました。

これを実際のAPIレスポンスを扱うスクリプトに適応するとこんな感じです。

const axios = require("axios");

const http = axios.create({
  baseURL: "https://api.github.com"
});

async function main() {
  try {
    const response = await http.get("/search/repositories", {
      params: {
        q: "react"
      }
    });
    const data = await response.data
    const result = await data.items.filter(item => item.stargazers_count >= 30000)
    console.log(`30000 over stars Repository search by 'react'`);
    result.map(item => console.log(item.full_name))
  } catch (error) {
    console.log(error)
  }
}

main();

$ node index.js 
30000 over stars Repository search by 'react'
facebook/react
facebook/react-native
ReactTraining/react-router
zeit/next.js
facebook/create-react-app
enaqx/awesome-react

まとめ

これで初見のJSON形式のAPIでも対応できると思います。
REPLはレスポンス構造を解析するだけでなく構文のチェックも簡単にできるのでド忘れしやすい僕は多用してます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React+Firebase+material-UIでWebアプリを作成する①

初投稿です。
身内用にReact+firebaseの環境作成方法を共有するために投稿してみました。

要するに、
Node.jsでサーバーを動かせるようにして、
Reactでフロントの動きを作って、
material-UIで画面をお洒落に仕上げて、
firebaseのデータべースや、認証機能サービスなどを使い、
世界中に公開できるようにしましょうということです。

実行環境はwindows10です。

①まず下記URLからnode.jsのインストーラーをダウンロードしましょう。
今回は左の推奨版でOKです。
https://nodejs.org/ja/

インストーラーを実行したら、ひとまず全てデフォルトの設定でインストールを完了させましょう。
コマンドプロンプトを開いて、

node -v

でバージョンが表示されていればインストールは成功です。

zoom command node ver.jpg

次にそのままコマンドで下記コマンドを実行します。

npx create-react-app my-app

(my-appの部分は任意のディレクトリ名でOKです)
happy hackingができれば完了です!

reactapp.jpg

するとReactの実行環境が作成されます。

そのまま

cd my-app
npm start

で下記画面が表示されればReactの導入も成功です。

reacthome.jpg

※パッケージマネージャーとしてyarnを導入しておくと、npmよりも早く、エラーも発生しづらいので色々便利です。

npm install -g yarn

コマンドでyarnを導入しておきましょう。

②次にFirebaseで自分のプロジェクト作成します。
下記のリンクで使ってみるを選択します。
https://firebase.google.com/?hl=ja

プロジェクト名はなんでもOKです。
アナリティクスはひとまず不要で大丈夫です。
そうすると下記のメイン画面に入ります。

firebase.jpg

ここから3つのことをやっておきましょう。

 1.中央にある</>マークを選ぶとwebアプリの登録に進めます。適当な名前をつけて、登録しましょう。
 2.Databaseを選択してcloudfirestoreを使えるようにしておきましょう。こちらも適当な名前で。
 3.任意の名前で作成したReactのディレクトリの中の「src」ディレクトリの中に「plugins」ディレクトリを作成し、その中に「firebase.js」というjavascriptのファイルを作成します。
「firebase.js」にProject Overviewの横にある歯車(設定)アイコンをクリックし、「プロジェクトの設定」の全般の一番下にあるコードをコピーしてapiKeyからappIdを丸ごと貼り付けます。

【任意のReactディレクトリ】⇒【srcディレクトリ】⇒【pluginsディレクトリ】⇒【firebase.js】※このファイルに下記コードをコピーして保存

firebase.js
import firebase from 'firebase';

const config = {
  var firebaseConfig = {
//ここから
    apiKey: "****************************",
    authDomain: "****************************",
    databaseURL: "****************************",
    projectId: "****************************",
    storageBucket: "****************************",
    messagingSenderId: "****************************",
    appId: "****************************",
//ここまでコピーして書き換え
  };
const firebaseApp = firebase.initializeApp(config);
export const firestore = firebaseApp.firestore();

そしたらsrcディレクトリの中のApp.jsというファイルでインポートさせます。
後々、使う認証機能も一緒にインポートしておきましょう。

App.js
import firebase from "firebase/app"
import "firebase/auth"
import "firebase/firestore"
import { firestore } from './plugins/firebase';

これでfirestoreというデータベースのサービスを利用することができます。
※注意
firebaseの料金プランは無料プランのままにしておいて下さい。
誤って従量課金プランに変えてしまうと、
とんでもない請求がいくかもしれません…

③FirebaseとReactを接続する。

FirebaseCLIを使うことで、ローカルからfirebaseプロジェクト更新できるようにします。
下記コマンドを入力しましょう。

npm install -g firebase-tools

もしくは 

yarn add -g firebase -tools

CLIが導入できたら、

firebase login

firebase init

とコマンドを続けましょう。
そうすると選択項目が出てきます。

ここはキーボードの方向キーで指定して、スペースキーで選択、enterで決定できます。
下記の順に選びましょう。

Hosting

Use an existing project (先ほど名前を付けたFirebaseのアプリの名前を選択)

What do you want to use as your public directory?(ディレクトリを選択します。buildと入力しておきましょう)

あとはすべてyesで問題ないです。 

終了したら

npm run build
firebase deploy

(もしくはyarn build && firebase deploy)

deployが完了したら表示されたURLにアクセスできます

あとは更新するたびに、buildとfirebase deployを行えば更新することができます。

あとはついでにフロント用にmaterial-UIをインポートしておきましょう。

https://material-ui.com/getting-started/installation/

1.上記のリンクの上部にあるコマンドでインストール

npm install @material-ui/core

もしくは

yarn add @material-ui/core

2.任意の名前で作成したReactディレクトリの「public」の中の「index.html」のhead内に、RobotoFontを読み込むコードを張り付けます。
 

3.必要なライブラリだけimportとしていきます。あとはコードで囲えば使用できます。

App.js
//例
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';

<TextField value="入力"/>
<Button>登録</Button>

↓こんな感じで表示されます。
materialUI.png

これで準備はだいたいOKです。
続きはまた次回に投稿いたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js+React+Firebase+Material-UIでWebアプリを作成する①

初投稿です。
React+Firebase+Material-UIの環境作成方法に関する記事です。

実行環境はwindows10です。

①Reactを使うにはNode.jsが必要です。
まず下記URLからNode.jsのインストーラーをダウンロードしましょう。
今回は左の推奨版でOKです。
https://nodejs.org/ja/

インストーラーを実行したら、ひとまず全てデフォルトの設定でインストールを完了させましょう。
コマンドプロンプトを開いて、

node -v

でバージョンが表示されていればインストールは成功です。

zoom command node ver.jpg

次にそのままコマンドで下記コマンドを実行します。

npx create-react-app my-app

(my-appの部分は任意のディレクトリ名でOKです)
happy hackingができれば完了です!

reactapp.jpg

するとReactの実行環境が作成されます。

そのまま

cd my-app
npm start

で下記画面が表示されればReactの導入も成功です。

reacthome.jpg

※パッケージマネージャーとしてyarnを導入しておくと、npmよりも早く、エラーも発生しづらいので色々便利です。

npm install -g yarn

コマンドでyarnを導入しておきましょう。

②次にFirebaseで自分のプロジェクト作成します。
下記のリンクで使ってみるを選択します。
https://firebase.google.com/?hl=ja

プロジェクト名はなんでもOKです。
アナリティクスはひとまず不要で大丈夫です。
そうすると下記のメイン画面に入ります。

firebase.jpg

ここから3つのことをやっておきましょう。

 1.中央にある</>マークを選ぶとwebアプリの登録に進めます。適当な名前をつけて、登録しましょう。
 2.Databaseを選択してcloudfirestoreを使えるようにしておきましょう。こちらも適当な名前で。モードはテストモード。ロケーションはとりあえずasia-northeast1で。

 3.任意の名前で作成したReactのディレクトリの中の「src」ディレクトリの中に「plugins」ディレクトリを作成し、その中に「firebase.js」というjavascriptのファイルを作成します。
「firebase.js」にProject Overviewの横にある歯車(設定)アイコンをクリックし、「プロジェクトの設定」の全般の一番下にあるコードをコピーしてapiKeyからappIdを丸ごと貼り付けます。

【任意のReactディレクトリ】⇒【srcディレクトリ】⇒【pluginsディレクトリ】⇒【firebase.js】※このファイルに下記コードをコピーして保存

firebase.js
import firebase from 'firebase';

const config = {
  var firebaseConfig = {
//ここから
    apiKey: "****************************",
    authDomain: "****************************",
    databaseURL: "****************************",
    projectId: "****************************",
    storageBucket: "****************************",
    messagingSenderId: "****************************",
    appId: "****************************",
//ここまでコピーして書き換え
  };
const firebaseApp = firebase.initializeApp(config);
export const firestore = firebaseApp.firestore();

そしたらsrcディレクトリの中のApp.jsというファイルでインポートさせます。
後々、使う認証機能も一緒にインポートしておきましょう。

App.js
import firebase from "firebase/app"
import "firebase/auth"
import "firebase/firestore"
import { firestore } from './plugins/firebase';

これでfirestoreというデータベースのサービスを利用することができます。
※注意
firebaseの料金プランは無料プランのままにしておいて下さい。
誤って従量課金プランに変えてしまうと、
とんでもない請求がいくかもしれません…

③FirebaseとReactを接続する。

FirebaseCLIを使うことで、ローカルからfirebaseプロジェクト更新できるようにします。
下記コマンドを入力しましょう。

npm install -g firebase-tools

もしくは 

yarn add -g firebase -tools

CLIが導入できたら、

firebase login

firebase init

とコマンドを続けましょう。
そうすると選択項目が出てきます。

ここはキーボードの方向キーで指定して、スペースキーで選択、enterで決定できます。
下記の順に選びましょう。

Hosting

Use an existing project (先ほど名前を付けたFirebaseのアプリの名前を選択)

What do you want to use as your public directory?(ディレクトリを選択します。buildと入力しておきましょう)

あとはyesで問題ないです。 

そうすると「build」というディレクトリが生成されているので、その中のindex.htmlをごっそり書き換えましょう。

index.html
<!doctype html>
    <html lang="ja">
    <head>
        <meta charset="utf-8"/>
        <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/>
        <meta name="viewport" content="width=device-width,initial-scale=1"/>
        <meta name="theme-color" content="#000000"/>
        <meta name="description" content="Test"/>
        <title>React Sample</title>
    </head>
    <body>
        <div id="root">
        </div>
    </body>

    </html>

App.jsのreturn()の記述が、最終的にindex.htmlのid ="root"に渡ってきます。
App.jsも書き換えてみましょう。

App.js
import React from 'react';
import './App.css';
import firebase from "firebase/app"
import "firebase/auth"
import "firebase/firestore"
import { firestore } from './plugins/firebase';

class App extends React.Component{
  constructor(props){
    super(props);
      }

      render(){
        return(
          <h1>Hello React</h1>
        );
      }

}


export default App;

完了したら

npm run build
firebase deploy

(もしくはyarn build && firebase deploy)

deployが完了したら表示されたURLにアクセスできます.

下記のように表示されたら成功です。
HelloReact.png

あとは更新するたびに、buildとfirebase deployを行えば更新することができます。

④最後にmaterial-UIをインポートしておきましょう。

https://material-ui.com/getting-started/installation/

1.上記のリンクの上部にあるコマンドでインストール

npm install @material-ui/core

もしくは

yarn add @material-ui/core

2.任意の名前で作成したReactディレクトリの「build」の中の「index.html」のhead内に、RobotoFontを読み込むコードを張り付けます。
 
※先ほどApp.jsを書き換えた際、じつはもう記載してあります。

3.必要なライブラリだけimportとしていきます。あとはコードで囲えば使用できます。

App.js
//例
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';

<TextField value="入力"/>
<Button>登録</Button>

↓こんな感じで表示されます。
materialUI.png

これで準備はだいたいOKです。
続きはまた次回に投稿いたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React+FirebaseでWebアプリを作成する①

※この記事は編集中です。

初投稿です。
身内用にReact+Firebaseの環境の作成方法を共有するための投稿です。

実行環境はwindows10のものです。

①まず下記URLからnode.jsのインストーラーをダウンロードしましょう。
今回は左の推奨版でOKです。
https://nodejs.org/ja/

インストーラーを実行したら、ひとまず全てデフォルトの設定でインストールを完了させましょう。
コマンドプロンプトを開いて、

code - node -v

↑が実行できればインストールは成功です。

node-v.png

次にそのままコマンドで下記コマンドを実行します。

npx create-react-app my-app

(my-appの部分は任意のディレクトリ名でOKです)

するとReactの実行環境が作成されます。

そのまま

cd my-app

npm start

で下記画面が表示されればReactの導入も成功です。

※パッケージマネージャーとしてyarnを導入しておくと、npmよりも早く、エラーも発生しづらいので後々便利です。

npm install -g yarn

コマンドで導入しておきましょう。

②次にFirebaseで自分のプロジェクト作成します。
下記のリンクで使ってみるを選択します。
https://firebase.google.com/?hl=ja

プロジェクト名はなんでもOKです。
アナリティクスはひとまず有効にしないで大丈夫です。

プロジェクトが作成されたら</>マークを選ぶとwebアプリの登録に進めます。

こちらも適当な名前をつけて、登録しましょう。

③FirebaseとReactを接続する。

FirebaseCLIを使うことで、ローカルからfirebaseプロジェクト更新できるようにします。
下記コマンドを入力しましょう。

npm install -g firebase-tools

もしくは ####yarn add -g firebase -tools


firebase login

firebase init

https://material-ui.com/getting-started/installation/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

インフラ自動テストツール ServerSpec 個人メモ

Ansibleを使用し、Node.jsのインストールを行なった以下記事の続きです。

[初心者向け] Ansible を体験してみよう

ServerSpecはサーバテストを自動化することができるフレームワークです。

今回は、前回の記事でインストールしたNode.jsが正常に入っているかどうかServerSpecを使用し、確認したいと思います。

構成の確認

前回に引き続き、MacOSに仮想マシンを二つ立てている構成です。

  • Vagrantfile
Vagrant.configure(2) do |config|
 config.vm.define "ConfigurationManagementServer" do |node|
    node.vm.box = "bento/ubuntu-18.04"
    node.vm.hostname = "ConfigurationManagementServer"
    node.vm.network :private_network, ip:"192.168.7.7"
    node.vm.provider "virtualbox" do |vb|
      vb.customize ["modifyvm", :id, "--memory", "1024"]
    end
    node.vm.provider "virtualbox" do |vb|
      vb.gui = false
    end
  end
  config.vm.define "AnsibleClient" do |node|
    node.vm.box = "bento/ubuntu-18.04"
    node.vm.hostname = "AnsibleClient"
    node.vm.network :private_network, ip:"192.168.8.8"
    node.vm.provider "virtualbox" do |vb|
      vb.customize ["modifyvm", :id, "--memory", "1024"]
    end
    node.vm.provider "virtualbox" do |vb|
      vb.gui = false
    end
  end
end

192.168.8.8が割り当てられているAnsibleClientには既にNode.jsがインストールされています。

$ vagrant ssh AnsibleClient
vagrant@AnsibleClient:~$ node -v
v10.17.0

上記では、手動でコマンドを打ちversionを確認しましたが、もう一つの仮想マシンからServerSpecを使用しテストを行います。

ServerSpecインストール・ファイルの編集

AnsibleClient環境から抜け、ConfigurationManagementServer環境に入ります。

$ vagrant ssh ConfigurationManagementServer
vagrant@ConfigurationManagementServer:~$

Rubyのインストールを行います。
今回はrbenvなどは使用せず、aptでインストールします。

vagrant@ConfigurationManagementServer:~$ sudo apt-get install ruby
vagrant@ConfigurationManagementServer:~$ ruby --version
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu] 

必要なライブラリをインストールします。

vagrant@ConfigurationManagementServer:~$ sudo gem install serverspec rake

ファイル群を作成します。
target host nameでは、テストを行う対象のIPアドレスを指定しますが、ここで入力された値がそのままディレクトリ名になるみたいです。

vagrant@ConfigurationManagementServer:~$ serverspec-init
Select OS type:

  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1

Vagrant instance y/n: n
Input target host name: 192.168.8.8
 + spec/
 + spec/192.168.8.8/
 + spec/192.168.8.8/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec

テスト項目を記述するファイルはspec/192.168.8.8/sample_spec.rb です。
ファイルを以下のように編集します。
node -vというコマンドを実行し、特定のバージョンがインストールされているか確認するテスト項目です。

spec/192.168.8.8/sample_spec.rb
require 'spec_helper'

describe command('node -v') do
  its(:stdout) { should match /v10.17.0/ }
end

ファイル編集完了後、Rakefileがあるディレクトリで以下コマンドを実行します。
パスワードを聞かれるので、接続先のパスワードを入力します。今回はvagrantです。

vagrant@ConfigurationManagementServer:~$ rake

vagrant@192.168.8.8's password: 
Command "node -v"
stdout is expected to match /v10.17.0/

Finished in 48.27 seconds (files took 1.33 seconds to load)
1 example, 0 failures

上記結果から、AnsibleClientサーバには Node.js v10.17.0 がインストールされていることがわかりました。
テスト方法は他にも様々存在するため、詳細は以下を参照してください。

https://serverspec.org/

ServerSpecはデフォルトで一つのサーバしか実行できない仕様なので、以下に複数サーバ対応方法を記載します。

複数サーバ対応方法

上記で記述した通り、デフォルトでは一つのサーバしかテストを行えないので、以下方法で複数対応を行います。
ファイルの編集を行う前に、仮想マシンをもう一つ立ち上げ、Node.js v10.17.0 のインストールを予め行なっています。
今回は192.168.9.9を割り当てたAnsibleClient2を立ち上げています。

$ vagrant ssh AnsibleClient2
vagrant@AnsibleClient2:~$ node -v
v10.17.0

次に、ServerSpecを実行するサーバに戻り、以下ディレクトリ構成に変更します。

.
├── Rakefile
├── properties.yml       # 新規作成
└── spec
    ├── ansibleclient    # 192.168.8.8というディレクトリ名を変更
    │   └── node_ver_spec.rb  # sample_spec.rbというファイル名を変更
    ├── ansibleclient2   # 新規作成
    │   └── node_ver_spec.rb  # 新規作成
    └── spec_helper.rb

spec_helper.rbファイルにライブラリの読み込みを追記します。

spec/spec_helper.rb
require 'serverspec'
require 'net/ssh'
require 'pathname'       # 追記
require 'rspec/its'      # 追記

set :backend, :ssh
set :disable_sudo, true  # 追記

次に、Rakefileを以下のように編集します。
どこかのサイトから参照しています。

require 'rake'
require 'rspec/core/rake_task'
require 'yaml'

properties = YAML.load_file('properties.yml')

task :spec    => 'spec:all'
task :default => :spec
namespace :spec do
  task :all => properties.keys.map {|key| 'spec:' + key.split('.')[0] }
  properties.keys.each do |key|
    desc "Run spec to #{key}"
    RSpec::Core::RakeTask.new(key.split('.')[0].to_sym) do |t|
      ENV['TARGET_HOST'] = key
      puts "\n========================================"
      puts "HOSTNAME: #{key}"
      t.fail_on_error = false
      t.pattern = 'spec/{' + properties[key][:roles].join(',') + '}/*_spec.rb'
    end
  end
end

ファイル編集完了後、Rakefileと同じディレクトリに properties.ymlを作成し、本ファイルに接続先の定義を行います。
properties.ymlでは、テストを実行するファイル内で使用できる変数を定義することが可能です。
今回は、ansibleclient2のテストで変数 node_verを定義しています。

properties.yml
192.168.8.8:          # 接続先を指定
  :roles:             # 実行するroleを指定
    - ansibleclient

192.168.9.9:
  :roles:
    - ansibleclient2
  :node_ver: "v10.17.0"

ansibleclient2のテストは以下の通りです。
こちらもnode.jsのversionを確認しているテスト項目です。

spec/ansibleclient2/node_ver_spec.rb
require 'spec_helper'

describe command('node -v') do
  its(:stdout) { should match /#{property[:node_ver]}/ }
end

ファイル編集は上記で完了です。
テストを再度実行します。

$ rake

1回目のテストが終了後、ansibleclient2のテストが開始されたでしょうか。
特に問題がなければ、両方のサーバ共にテストは成功していると思います。

おわりに

個人的なメモとして残しているため、わかりにくい箇所があるかもしれません。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む