Goalist Developers Blog

AWS LambdaをJavaで書いていた人がPythonで書くためにすること

こんにちは。ゴーリスト開発の盛次です。

Javaが大好きだけどPythonでもAWS Lambdaを書いてみるかー、と思った人のための記事です。

開発環境

Windows10
PyCharm
Python3.6

開発環境を作る

とりあえずインストールが楽そうなのでAnacondaを利用します。
Anacondaのインストール方法は世の中に優れた記事がたくさんあるのでそちらにお任せします。
JetBrains製品が大好きなのでIDEはPyCharmを使います。
CommunityとProfessionalの2種類がありますが、無料のCommunityで問題ないです。リモートデバッグを使う場合はProfessionalが必要です。
こちらもインストール方法は世の中の優れた記事にお任せします。

ライブラリまわりについて

Javaをeclipseとかで開発するときのプロジェクトに相当するのはPythonでは仮想環境だと思います。
従って、まずは仮想環境を作ります。私の環境ではPyCharmを立ち上げたままAnacondaを入れると、再起動しないとcondaにパスが通ってない状態でした。

conda create -n my_env python numpy scipy

my_envが仮想環境の名前になります。python numpy scipyなどは最初からinstallしておきたいパッケージを指定します。
scipy=0.12.0のようにすることでバージョン指定も可能です。

仮想環境の一覧と仮想環境の切り替えは以下です。

conda info -e # 利用できる仮想環境の一覧を表示
activate my_env # Windows
source activate my_env # Mac/Linux

基本的に「conda insall hogepackage」でinstallしますが、見つからない場合があるので、良く見るパッケージ管理ツールpipもインストールします。

conda install pip

awsまわりのライブラリはboto3を利用します

pip install boto3

利用ライブラリの書き出しは以下です

pip freeze > requirements.txt

lambda-uploaderの利用

Lambdaへのアップロードにはlambda-uploaderを使います。

pip install lambda-uploader

50MB以下なら直接アップロードして適用できます。それ以上はlambda-uploaderでzipにして後は自分でS3にアップして適用するいつものやつになります。

AWSの認証情報との紐づけはprofileを作り、アップロード時に指定します。
とりあえずaws-cliが必要なので

pip install awscli

hoge-profileという名前でプロファイルを作成

aws configure --profile hoge-profile

AWS Access Key ID [None]: {Your Access Key Id}
AWS Secret Access Key [None]: {Your Secret Access Key}
Default region name [None]: ap-northeast-1
Default output format [None]: json

アップロード時のコマンド。

del *.zip
lambda-uploader --virtualenv=C:\hoge\my_env --profile=hoge-profile

コマンドの実行はlambda-uploaderの設定ファイルであるlambda.jsonが置かれているディレクトリで打つ前提です。
ビルドにミスるとゴミzipが出来上がって、次に実行する時にこのzipが含まれて大きなzipになるのでdelで削除しています。Linuxの場合は読み替えてください。
lambda-uploaderを使うとlambda.jsonとこのコマンドだけでアップロードが完了します。凄く便利。

lambda.jsonについて

lambda-uploaderの設定ファイルのlambda.jsonの内容は見れば分かるものばかりですが、注意すべきなのはname、handler、roleの3つだと思います。

{
  "name": "hogeFunc",
  "description": "write description",
  "region": "ap-northeast-1",
  "handler": "hoge.lambda_handler",
  "role": "arn:aws:iam::xxxxxxxxxxxx:role/hoge_lambda_exec_role",
  "timeout": 300,
  "memory": 128
}

nameはアップロード先のLambda Functionの名前と一致させる必要があります。
handlerはhoge.pyというファイルにlambda_handlerというメソッドを定義している場合です。Lambdaで実行されるメソッドになります。
roleはhogeFunc実行用のroleの値を入れる必要があります。

API GatewayのLambdaプロキシ統合を使う場合

API Gatewayの設定でLambdaプロキシ統合(Lambda Proxy Integration)を使うと普通に文字列をreturnするとinternal server errorで怒られます。 以下のようにstatusCode body が必須、必要な情報はbodyに入れます。 'Access-Control-Allow-Origin': '*'を設定したheadersも入れることになると思います。

return { 'statusCode': 200, 'body': 'Hello from Lambda' }

Javaぽく書きたい!

Python3.5からType Hintsという機能が導入されました。
実行時には完全にコメント扱いなので好きなだけ書いていいです。
変数宣言の型と関数の戻り値の型などを明示できます。
Type Hintsの静的なチェックにはmypyを使います。

pip install mypy
mypy hoge.py 

以下は私がLambdaのレスポンスをクラス化したいと思い、Pythonの命名規則の規約をガン無視してJavaぽく書いたコードになります。
自分で定義したclassに対してはjson.dumpsが使えないので、EntityBaseクラスにtoJson()メソッドをもたせ、継承してゴニョゴニョしています。
Lambdaで実行するメソッドの戻り値はDict[str, Any]でとりあえずは大丈夫でした。

def lambda_handler(event:Dict[str, Any], context) -> Dict[str, Any]:
    resultResponse:ResultResponse = ResultResponse(...)
    # 省略
    return resultResponse.toDict()

class ResultResponseBody(EntityBase, Generic[T]):
    def __init__(self:'ResultResponseBody', code:int, msg:str, data: object)  -> None:
        self.code:int = code
        self.msg:str = msg
        self.data:object = data

class ResultResponse(EntityBase, Generic[T]):
    def __init__(self:'ResultResponse', status:int, code:int, msg:str, data:object=None) -> None:
        # data:object={} とすると怒られるのでこの形が正しいらしい
        # https://docs.quantifiedcode.com/python-anti-patterns/correctness/mutable_default_value_as_argument.html
        if data is None:
            data = {}

        self.statusCode:int = status
        self.headers:Dict[str, str] = {  # Lambda_Proxyを有効にするのでCORSはAPI Gatewayではなくここで何とかする必要がある
            'Access-Control-Allow-Origin': '*'
        }
        #self.body:str = json.dumps(ResultResponseBody(code, msg, data)) # 自作クラスに対しては無力な模様
        self.body: str = ResultResponseBody(code, msg, data).toJson()

class EntityBase(Generic[T]):
    # from collections import namedtuple を使う方法もあるけど、、、
    def jsonToEntity(self:T, jsonString:str) -> T:
        dictObj = json.loads(jsonString)  # ファイルオブジェクトからのときはload 文字列からのときはloads
        for key in dictObj.keys():
            setattr(self, key, dictObj[key])
        return self

    # object的な感じで来たものを対象クラスに入れていく、keyはObjectの方をベースにする
    def translateTo(self:T, obj:Dict[str, Any]) -> T:
        for key in obj.keys():
            value:Any = obj[key]
            if value is None and isinstance(getattr(self, key), str):
                value = ''
            setattr(self, key, value)
        return self

    # なんか良い感じにjson.dumpsが動くかと思いきや、自作クラスは全く動かないという悲しい現実
    # relation等があって内部に別クラスを持つ場合はそのクラスがEntityBaseを継承していれば再帰的にうまくいくはず
    # それ以外の場合は。。。
    def toJson(self) -> str:
        res:Dict[str, Any] = {}
        for key in self.__dict__.keys():
            target:Any = getattr(self, key)

            if isinstance(target, EntityBase):
                res[key] = target.toJson()
                continue
            if isinstance(target, Decimal):
                decimalVal:Decimal = target
                strDecimalVal:str = str(decimalVal)
                if '.' not in strDecimalVal: 
                    res[key] = int(strDecimalVal)
                    continue
                else:
                    raise TypeError(key + strDecimalVal + " is invalid at base.py toJson")

            if isinstance(target, List):  # Listの中がentityの場合はここ
                listJson:str = ''
                for eachTarget in target:
                    if len(listJson) > 0:
                        listJson += ','
                    if isinstance(eachTarget, EntityBase):
                        listJson += eachTarget.toJson()
                    else:
                        listJson += json.dumps(eachTarget)
                res[key] = '[' + listJson + ']'
                continue

            if isinstance(target, Dict): # dataにEntityBaseを継承しないで直接Dictを突っ込んだ場合
                res[key] = json.dumps(target)
                continue

            res[key] = target

        return json.dumps(res)

    def toDict(self) -> Dict[str, Any]:
        ret:Dict[str, Any] = {}
        for key in self.__dict__.keys():
            ret[key] = getattr(self, key)
        return ret

まとめ

アップロード周りはlambda-uploaderの使い勝手が非常に良いです。
Type Hintsはかなり良いと思います。Pythonでも型に基づいてIDEのサポートをフルに受けたいという人にはお勧めです。

【2017年】マテリアルデザインのライブラリをかんたんに比較

こんにちは。ゴーリスト開発の飯尾です。

今回のお題は
マテリアルデザイン

material.io

現代っぽくスマートですね。
Googleが推奨しているというのだから乗るしかない、このビッグウェーブに
最近ではGoogleカレンダーのUIもマテリアルデザインに変わりましたね

GoogleカレンダーのWeb版がデザインを一新 | TechCrunch Japan

フレームワークどれ使う?

さて、このマテリアルデザインを実現するためのCSSフレームワーク
いろいろあります

おなじみBootstrapのCSSをマテリアルっぽくしたものとか

JSフレームワーク別に最適化されたものも

何使ったらいいんだ!とおもったのでちょっと調べてみました。

1. Material Design Lite

getmdl.io

  • Googleの開発者がメンテ
  • GitHub Star 20,006(ユーザー多め)
  • 開発がすでに「Material Components for the Web」に移行しているためサポートは限定的(ぐぬぬ)

2. Material Components for the Web

material.io

  • Material Design Liteの後続
  • ド公式のGoogleが管理してる最新版(安心)
  • GitHub Star 6,613(まだあまり普及してないのかね)

3. Materialize

materializecss.com

  • GiuHub Star 30,010(一番人気)
  • 充実のコンポーネント(マジパネッス)(リッチなWebサイト作るならこれ)
  • サイズはデカイ

4. MUI - Material Design CSS Framework

www.muicss.com

  • 軽量!簡素!
  • GitHub Star 3,679(う〜ん)
  • Angular対応してるのか〜とおもったらAngularJSだった(がっかりさせおって)



だいたいわかってきたぜえ

まあ私が個人的に使いたいのはAngularで組むWebアプリなんで
Angular Material 一択ですが!フハハ!ハハ!

皆さまも素敵なビッグウェ〜ブライフをお楽しみください🍌

Angular 4 から 5 にアップデートしてみた

こんにちは、ゴーリスト開発の飯尾です。
みなさんも大好きなAngular、ほんの一週間ほど前にバージョン5が出ましたね。

blog.angular.io

というわけで

  • 羽のように軽い気持ちで
  • Angular4で作ったアプリを5に上げたら
  • 一体どうなるのか!?

試してみます

互換性の確認

angular/CHANGELOG.md at master · angular/angular · GitHub

BREAKING CHANGES をチェケです
関係ありそうなのはこのあたりでしょうか…

Angular now requires TypeScript 2.4.x.

@angular/platform-server now additionally depends on @angular/platform-browser-dynamic as a peer dependency.

ncu でパッケージの更新確認してみます

$ ncu
 @angular/common                     ^4.0.0  →   ^5.0.0 
 @angular/compiler                   ^4.0.0  →   ^5.0.0 
 @angular/core                       ^4.0.0  →   ^5.0.0 
 @angular/forms                      ^4.0.0  →   ^5.0.0 
 @angular/http                       ^4.0.0  →   ^5.0.0 
 @angular/platform-browser           ^4.0.0  →   ^5.0.0 
 @angular/platform-browser-dynamic   ^4.0.0  →   ^5.0.0 
 @angular/router                     ^4.0.0  →   ^5.0.0 
 @angular/compiler-cli               ^4.0.0  →   ^5.0.0 
 @types/jasmine                      2.5.38  →    2.6.3 
 @types/node                        ~6.0.60  →  ~8.0.49 
 codelyzer                           ~2.0.0  →   ~4.0.1 
 jasmine-core                        ~2.5.2  →   ~2.8.0 
 jasmine-spec-reporter               ~3.2.0  →   ~4.2.1 
 karma                               ~1.4.1  →   ~1.7.1 
 karma-chrome-launcher               ~2.0.0  →   ~2.2.0 
 karma-coverage-istanbul-reporter    ^0.2.0  →   ^1.3.0 
 protractor                          ~5.1.0  →   ~5.2.0 
 ts-node                             ~2.0.0  →   ~3.3.0 
 tslint                              ~4.5.0  →   ~5.8.0 
 typescript                          ~2.2.0  →   ~2.6.1 

The following dependencies are satisfied by their declared version range, but the installed versions are behind. You can install the latest versions without modifying your package file by using npm update. If you want to update the dependencies in your package file anyway, run ncu -a.

 @agm/core          ^1.0.0-beta.0  →  ^1.0.0-beta.2 
 @types/googlemaps       ^3.26.16  →        ^3.29.2 
 core-js                   ^2.4.1  →         ^2.5.1 
 rxjs                      ^5.1.0  →         ^5.5.2 
 zone.js                   ^0.8.4  →        ^0.8.18 

Run ncu with -u to upgrade package.json

TypeScriptのバージョンは上がりますね
@angular/platform-browser-dynamic は入れてあったのでOK

Angular CLI のアップデート

とりあえずCLIの方から上げていきます

$ ng -v
    _                      _                 ____ _     ___
   / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
  / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
 / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
/_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
               |___/
@angular/cli: 1.1.3
node: 6.10.2
os: darwin x64
@angular/common: 4.2.4
@angular/compiler: 4.2.4
@angular/core: 4.2.4
@angular/forms: 4.2.4
@angular/http: 4.2.4
@angular/platform-browser: 4.2.4
@angular/platform-browser-dynamic: 4.2.4
@angular/router: 4.2.4
@angular/cli: 1.1.3
@angular/compiler-cli: 4.2.4

$ npm update @angular/cli

$ ng -v

    _                      _                 ____ _     ___
   / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
  / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
 / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
/_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
               |___/
    
Angular CLI: 1.5.0
Node: 6.10.2
OS: darwin x64
Angular: 4.2.4
... common, compiler, compiler-cli, core, forms, http
... platform-browser, platform-browser-dynamic, router
... tsc-wrapped

@angular/cli: 1.5.0
@angular-devkit/build-optimizer: 0.0.32
@angular-devkit/core: 0.0.20
@angular-devkit/schematics: 0.0.35
@ngtools/json-schema: 1.1.0
@ngtools/webpack: 1.8.0
@schematics/angular: 0.1.1
typescript: 2.2.2
webpack: 3.8.1

ng buildもまだまだふつうにできました

Angular 4 to 5

パッケージ更新

まずはncu -uでpackage.jsonの更新

$ ncu -u
 @angular/common                     ^4.0.0  →   ^5.0.0 
 @angular/compiler                   ^4.0.0  →   ^5.0.0 
 @angular/core                       ^4.0.0  →   ^5.0.0 
 @angular/forms                      ^4.0.0  →   ^5.0.0 
 @angular/http                       ^4.0.0  →   ^5.0.0 
 @angular/platform-browser           ^4.0.0  →   ^5.0.0 
 @angular/platform-browser-dynamic   ^4.0.0  →   ^5.0.0 
 @angular/router                     ^4.0.0  →   ^5.0.0 
 @angular/compiler-cli               ^4.0.0  →   ^5.0.0 
 @types/jasmine                      2.5.38  →    2.6.3 
 @types/node                        ~6.0.60  →  ~8.0.49 
 codelyzer                           ~2.0.0  →   ~4.0.1 
 jasmine-core                        ~2.5.2  →   ~2.8.0 
 jasmine-spec-reporter               ~3.2.0  →   ~4.2.1 
 karma                               ~1.4.1  →   ~1.7.1 
 karma-chrome-launcher               ~2.0.0  →   ~2.2.0 
 karma-coverage-istanbul-reporter    ^0.2.0  →   ^1.3.0 
 protractor                          ~5.1.0  →   ~5.2.0 
 ts-node                             ~2.0.0  →   ~3.3.0 
 tslint                              ~4.5.0  →   ~5.8.0 
 typescript                          ~2.2.0  →   ~2.6.1 

The following dependencies are satisfied by their declared version range, but the installed versions are behind. You can install the latest versions without modifying your package file by using npm update. If you want to update the dependencies in your package file anyway, run ncu -a.

 @agm/core          ^1.0.0-beta.0  →  ^1.0.0-beta.2 
 @types/googlemaps       ^3.26.16  →        ^3.29.2 
 core-js                   ^2.4.1  →         ^2.5.1 
 rxjs                      ^5.1.0  →         ^5.5.2 
 zone.js                   ^0.8.4  →        ^0.8.18 

Upgraded /path/to/app/package.json

アップデート実行!

$ npm update
my-app
├── @agm/core@1.0.0-beta.2 
├── UNMET PEER DEPENDENCY @angular/common@5.0.0
├── @angular/compiler@5.0.0 
├── @angular/compiler-cli@5.0.0 
├── UNMET PEER DEPENDENCY @angular/core@5.0.0
├── UNMET PEER DEPENDENCY @angular/forms@5.0.0
├── @angular/http@5.0.0 
├── @angular/platform-browser@5.0.0 
├── @angular/platform-browser-dynamic@5.0.0 
├── @angular/router@5.0.0 
├── @types/googlemaps@3.29.2 
├── @types/jasmine@2.6.3 
├── @types/node@8.0.49 
├── codelyzer@4.0.1 
├── core-js@2.5.1 
├── jasmine-core@2.8.0 
├── jasmine-spec-reporter@4.2.1 
├── karma@1.7.1 
├── karma-chrome-launcher@2.2.0 
├── karma-coverage-istanbul-reporter@1.3.0 
├── protractor@5.2.0 
├── rxjs@5.5.2 
├── ts-node@3.3.0 
├── tslint@5.8.0 
├── UNMET PEER DEPENDENCY typescript@2.6.1
└── zone.js@0.8.18 

npm WARN @angular-mdl/core@4.0.8 requires a peer of @angular/core@>=4.0.0 <5.0.0 but none was installed.
npm WARN @angular-mdl/core@4.0.8 requires a peer of @angular/common@>=4.0.0 <5.0.0 but none was installed.
npm WARN @angular-mdl/core@4.0.8 requires a peer of @angular/forms@>=4.0.0 <5.0.0 but none was installed.
npm WARN @angular/compiler-cli@5.0.0 requires a peer of typescript@>=2.4.2 <2.5 but none was installed.
npm WARN tsickle@0.24.1 requires a peer of typescript@2.4.2 but none was installed.

怒られているゥ…

@angular-mdl/core はまだ5に未対応、放置するとして

TypeScriptは2.4.x だったので
package.jsonを"typescript": "~2.4"に書き換える

npmのキャッシュクリーンからのインストール

$ rm -r node_modules
$ npm cache clean
$ npm install

ぶじtypescript@2.4.2 になりました

ビルドしてみる

コンパイル通ってくれ…
祈りのng serve

ERROR in src/app/service/request.service.ts(34,5): error TS2322: Type 'Observable<{} | Response>' is not assignable to type 'Observable<Response>'.
  Type '{} | Response' is not assignable to type 'Response'.
    Type '{}' is not assignable to type 'Response'.
      Property 'type' is missing in type '{}'.

赤字エラ〜〜〜〜

対処

これを

public post(params: string): Observable<Response> {
  ・・・
  return this.http.post(url, params)
    .catch((error) => {
      throw new Error(error);
    });
}

こう!

public post(params: string): Observable<Response> {
  ・・・
  return this.http.post(url, params)
    .catch((error) => {
      Observable.throw(error);
    });
}


次はキサマだ

ERROR in src/app/conponents/popover/popover.component.ts(69,11): error TS2322: Type 'Observable<{} | Event>' is not assignable to type 'Observable<Event>'.
  Type '{} | Event' is not assignable to type 'Event'.
    Type '{}' is not assignable to type 'Event'.
      Property 'bubbles' is missing in type '{}'.

これを

const stream: Observable<Event> = Observable
      .merge(Observable.fromEvent(document, 'click'), Observable.fromEvent(document, 'touchend'));

こう!

const stream: Observable<Event> = Observable
      .merge(Observable.fromEvent(document, 'click'), Observable.fromEvent(document, 'touchend'))
      .catch(e => Observable.throw(e));


ウオオアアアーーー

qiita.com

そういえば

こんなこと書いてあったので

Developers appreciated the new HttpClient (introduced in version 4.3) so the team is now recommending HttpClient for all applications, and deprecating the previous @angular/http library.

jaxenter.com

Httpクライアントモジュールは
@angular/http が非推奨になって
@angular/common/http を使うのが主流になっていくみたいです

こちらの解説が詳しい…アリガトウゴザイマス

qiita.com

めっちゃ推されてるビルド速度について

Angular 4.2.4 / Angular CLI 1.1.3

$ ng build
Time: 20935ms
chunk    {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 283 kB {4} [initial] [rendered]
chunk    {1} main.bundle.js, main.bundle.js.map (main) 103 kB {3} [initial] [rendered]
chunk    {2} styles.bundle.js, styles.bundle.js.map (styles) 45.6 kB {4} [initial] [rendered]
chunk    {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 3.36 MB [initial] [rendered]
chunk    {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] [rendered]

Angular 5.0.0 / Angular CLI 1.5.0

$ ng build
Time: 19193ms
chunk {inline} inline.bundle.js, inline.bundle.js.map (inline) 5.83 kB [entry] [rendered]
chunk {main} main.bundle.js, main.bundle.js.map (main) 129 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 383 kB [initial] [rendered]
chunk {styles} styles.bundle.js, styles.bundle.js.map (styles) 41.8 kB [initial] [rendered]
chunk {vendor} vendor.bundle.js, vendor.bundle.js.map (vendor) 4.45 MB [initial] [rendered]

$ ng build --aot
Time: 23034ms
chunk {inline} inline.bundle.js, inline.bundle.js.map (inline) 5.83 kB [entry] [rendered]
chunk {main} main.bundle.js, main.bundle.js.map (main) 338 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 383 kB [initial] [rendered]
chunk {styles} styles.bundle.js, styles.bundle.js.map (styles) 41.8 kB [initial] [rendered]
chunk {vendor} vendor.bundle.js, vendor.bundle.js.map (vendor) 3.33 MB [initial] [rendered]

あんまり違いが…

感想

Observableの処理をしっかりやる以外は特に変更なしでいけました〜
あとは非推奨になってしまった通信部分の書き換えとかですね

年明けてバグとかライブラリ周り落ち着いてきたら
本番アプリでもあてていくつもりです( ˘ω˘)

Terraformに足を誘われて...

どうも,新卒エンジニアのナカノです.

今回から,技術ブログらしい記事を投稿していこうと思います.

最近までは「AWS,CloudFormation,Terraform,暗号化実装」などを学習してきたので,今後はこれらに関する記事を書いていく予定です.

そして初回は,Terraformに関するintroductionの記事となります.

  

目 次
  • Terraformって何なの?   
    • 概要
    • メリット
  • どうやって使うの?   
    • 環境構築
    • 利用方法
  • 締め括り
  • 参考文献

  

Terraformって何なの?

概要

Terraformとは,HashiCorpという会社がつくったinfrastructure as code softwareです.つまり,コードを書いてインフラリソースの作成や管理を行うことが可能な,インフラの構成管理ツールです.

コードを作成する場合,HashiCorp Configuration Language(通称,HCL)というJSON-likeな表記記法を使います.これはJSON-likeなので,JSONに慣れている方は割と直ぐに馴染むかもしれません.

メリット

Infrastructure as Codeの実現

まず,Terraformはinfrastructure as code softwareなので,インフラの作成や管理を効率的に行うことが出来るという利点があります.

hardwareの様な場合だとインフラ管理が非常に面倒であり,またインフラ構成を変更することになれば都度に機器を変えることが求められます.

一方で,構成管理ツールを用いれば,インフラ管理を行う際には設定ファイルのコードを編集するだけで済むので作業が非常に効率的になります.

また,インフラ構成の変更に対しても柔軟に対応することが出来るため,この様なツールを用いた作業方法はアジャイル開発に非常に適していると言えます.

対応するProviderの種類が多い

Terraformが対応出来るProviderはAWS以外に多くのものがあります.ここではそれらを列挙するのは面倒なので,気になる方はTerraformの公式ドキュメントを参照なさってください.

AWSに限って言えばインフラの構成管理ツールとしてはCloudFormationでも良さそうな気がしますが,何らかのタイミングでProviderが変わる場合も考えるとTerraformには柔軟性が非常にあると思います.

  

どうやって使うの?

環境構築

一番簡単な方法は,自分が使用しているOSに対応するzipファイルをTerraformの公式サイトからダウンロードするというものです.

ダウンロードのページは,Terraformの公式サイトのページ右上部にある"Download"の項目をクリックすれば開くことが出来ます.

f:id:r-nakano:20171102144615p:plain

ページを見るとlatest versionしか見当たりませんが,older versionをダウンロードしたい方はそのページにある"download older versions of Terraform"の部分をクリックしていただくとよいです.

f:id:r-nakano:20171102144625p:plain

ダウンロード後,Terraformが使える場所を確認しておきましょう.例えば,"terraform version"というversion確認用のコマンドを打ってうまく実行されればOKです.

利用方法

Terraformを使ってインフラリソースを作成するには,tfファイルをつくってそこに設定用のコードを書く必要があります.基本的には,tfファイルに書くべき設定はresourseやdataに関するもので大丈夫です.

f:id:r-nakano:20171102144950p:plain

そして設定ファイルの作成完了後に"terraform plan"というDry-run用のコマンドを実行してエラーのチェックを行います.

このコマンドの実行後にエラーがないことが確認出来たならば,その次に"terraform apply"というdeploy用のコマンドを実行してリソースを作成します.

この際に注意すべきことが2点あります.1点は,"terraform plan"を実行してエラーが出なかったとしても"terraform apply"の実行時にエラーが出ることがあるということです.そのため,planのコマンドの実行に成功したとしても油断は禁物です.

もう1点は,"terraform apply"の実行に失敗した場合は作成状況がロールバックしないという点です.よって,リソースの作成が正常に行われるまではエラーの原因を分析し,解決のためにplanとapplyのコマンドの実行の繰り返しを行うことが求められます.

  

締め括り

今回はintroductiveな記事ということで,この辺りで一旦筆を置かせていただこうかと思います.

次回からは具体的なインフラリソースの作成,簡単なインフラのアーキテクチャーの構成などの記事を書いていくつもりです.

2ヶ月前ぐらいから使い始めたばかりではありますが,技術ブログを通じて皆様とTerraformに関する知識を少しでも共有出来ればこの上ないことだと思っております.

それでは,次回以降にご期待ください.

  

参考文献

以下、Terraformの公式サイトのURLです: www.terraform.io

食欲の秋をしのぐためのアプリを作ってみた

Trick or Treat!渡部です。

f:id:watabe1028:20171031123524p:plain

最近食欲がやばいです。
栗、芋、南瓜の魅力に勝てません。
かといって運動もしたくありません。
 
このままでは太ってしまいます。
DevからDebuになったらおしまいです。

 
せめて夕飯だけでも抑えねば!と思い
何か役立つアプリを作ってみようと考えました。

毎日食べたものを記録するアプリは作るのも
使うのも面倒です。

 
何もしなくても戒めてくれるアプリ・・・

 
そうや!飯時に通知がくるアプリ作ったろ!
ってことで夕飯時に戒めの言葉を送ってくるアプリを作ろうと思います。
割と忙しいし面倒なので最低限の簡単な構成で作ろう!

 
手順
・適当に画面を作る
・ローカル通知を実装
・実行

サクッといきます!

 

適当に画面を作る

今回は画面は必要ないので弊社サービスを牛耳るふろぐん(4回目)の
ハロウィンバージョンを表示させます。
特に意味はありません。

f:id:watabe1028:20171031120621p:plain

 
 

ローカル通知を実装

次に通知の実装です。
前回にリモート通知の実装をしたので詳しくはそちらを参照ください。
まずは3秒後に通知がくるように実装します。
以下のコードを丸々コピペすればおけ!です。
AppDelegate.swiftに書きます。

import UIKit
import UserNotifications    // 追加
import NotificationCenter   // 追加

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        // 全ての通知を一括で削除
        UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
        
        // 通知許可ダイアログ
        UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in if granted {print("通知許可")}
        }
        
        // 通知の設定
        let notificationContent = UNMutableNotificationContent()
        notificationContent.title = "よう、デブ!"
        notificationContent.body = "飯食ってる場合じゃねーぞ!"
        notificationContent.sound = UNNotificationSound.default()
        
        // 3秒後に通知
        let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 3, repeats: false)
        let request = UNNotificationRequest.init(identifier: "YoDebuNotification", content: notificationContent, trigger: trigger)
        
        let notificationCenter = UNUserNotificationCenter.current()
        notificationCenter.add(request)
        notificationCenter.delegate = self
        
        return true
    }
    
    // 通知の受信
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.alert,.sound])
    }
    
    // 通知に対する操作
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        
        // alertで表示
        let contentBody = response.notification.request.content.body
        let alert:UIAlertController = UIAlertController(title: "何も食べません!", message: contentBody, preferredStyle: UIAlertControllerStyle.alert)
        alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {
            (action:UIAlertAction!) -> Void in
            print("食べないことを誓いました。")
        }))
        self.window?.rootViewController?.present(alert, animated: true, completion: nil)
        
        completionHandler()
    }
}

 
 

実行

これを実行するとこんな感じで表示されます。

f:id:watabe1028:20171031122628p:plain

アプリを閉じていても
f:id:watabe1028:20171031122735p:plain

 
ふろぐんがにくたらしいですね!

 
 

次に時間を指定します。
だいたい21時ごろが夕飯の時間帯なので
21時に通知がくるように設定します。

    // 21時に設定
    let notificationContent = UNMutableNotificationContent()
    notificationContent.title = "よう、デブ!"
    notificationContent.body = "飯食ってる場合じゃねーぞ!"
    notificationContent.sound = UNNotificationSound.default()
        
    let date = DateComponents(hour:21)
    let trigger = UNCalendarNotificationTrigger.init(dateMatching: date, repeats: true)
    let request = UNNotificationRequest.init(identifier: "YoDebuNotification", content: notificationContent, trigger: trigger)
        
    let notificationCenter = UNUserNotificationCenter.current()
    notificationCenter.add(request)
    notificationCenter.delegate = self

以上で完了です。
UNCalendarNotificationTrigger.init(dateMatching: date, repeats: true)
のrepeatsをtrueにしとけば毎日繰り返し通知がくるようになります。

 
 

おわり

モンブラン、かぼちゃの煮物、焼き芋に秋刀魚。。。
なかなか食欲を抑えるのは厳しいですが
こういったアプリで自分を律することにします!
できれば・・・ですが・・・

今日はハロウィンのせいか
お菓子が賑わっています。
早速律することをやめます。

f:id:watabe1028:20171031162810j:plain

コトリン入門開始!

読者の皆さん、 こんにちは!

ゴーリストのティンカーです!

いよいよコトリンの入門シリーズスタートです!
ここでちょこちょこコトリンの世界で一緒に迷い、遊び、楽しい時間を過ごすつもりなので、よろしくお願いします!

ではでは、
本日のトピックに関してなんですが、なんと。。。
開発環境の立ち上げとなります。。。!

”え。。。開発に手を加えたいのに。。”
と思ってる方もいらっしゃると思うんですが、大丈夫です!
来週から毎週新しい記事を出すつもりなので、バリバリやりますのでご安心ください!!

では早速なんですが、環境を立ち上げましょう!

今回の流れ

開発環境の設定:

  • 統合開発環境(IDE)、Android Studio 3.0のインストール(Mac上)。
  • 仮想端末(バーチャルデバイス)の作成。

それだけです!!

まずはダウンロードから行きます!

こちらのURL(アンドロイドの公式サイト)にてインストールの指示に従い、ダウンロードし、インストールしてください!

https://developer.android.com/studio/install.html (画面の一番下から言語を日本語に変更できます)

インストールが終わったら、新しいプロジェクトを作成しましょう!

f:id:TinkerJack:20171027123425p:plain
プロジェクト作成画面

Android Studio 3.0以降、コトリンのサポートが入っています!(Android Studio 3.0以前はわざわざライブライのプラグインをインストールしなきゃいけなかった)、 なので "Include Kotlin Support"のチェックボックスにチェックを入れます。

f:id:TinkerJack:20171027123059p:plain
Kotlin対応の導入

Activityの種類を選びましょう!(ハローコトリンを画面に出すだけなので、"Empty Activity"を選択しましょう)

f:id:TinkerJack:20171027123357p:plain
Activity種別の選択

Activityとlayout名を設定しましょう。こちらでコトリンの処理を書きます!

f:id:TinkerJack:20171027123401p:plain
Activity名を設定

これでプロジェクトの作成が完了しました!

ただ、依存関係が足りていないためにエラーが現れる可能性があります。ですが、Android Studioが必要な依存関係を教えてくれるので、エラーが出た場合はそれらをダウンロードしてgradleのプロジェクトをSynchronizeすれば解決する筈です!

プロジェクトを作成出来ました!

Androidの端末があると便利なんですが、やはり最近の端末がなかなか買いられないものなのです。 でも大丈夫です!Android Studioで仮想端末(Virtual Device)を作ることが出来ます! なので次はバーチャルデバイスの作成です!

アプリを実行(Playボタンを押すことで)したら、現在使われているデバイスの画面が表示されます! その時は "Create a New Virtual Device"を選択します!

f:id:TinkerJack:20171027123406p:plain
バーチャルデバイスの作成

ここで作りたい仮想端末を選択できます!

f:id:TinkerJack:20171027144507p:plain
バーチャルデバイスの選択

アプリのためのデフォルト画像を選択しなきゃいけません。 そのために画像をダウンロードする必要があります!

f:id:TinkerJack:20171027123353p:plain
アプリ画像

こちらをダウンロードして "finish" を押したら、バーチャルデバイスの作成は完了します!

これで使えるスケレトンコードとバーチャルデバイスの準備が整ったので、アプリを実行してみましょう!

f:id:TinkerJack:20171027145831p:plain
ハローコトリン!

おめでとうございます!これで初めてのコトリンのAndroidアプリの作成が完了しました!

お楽しみいただけたでしょうか!

次回までkotlinの勉強や開発でお楽しみください!!

ゴーリストエンジニア
イムラーン

Angularでng2-chartsを使ってグラフを描画する

おはようございます。 ゴーリスト新卒一年目、見習い開発者のバンナイです。

このブログの背景

先日、弊社で、AngularJSを使って書かれていたWEBアプリケーションをAngular4で書き換えるというプロジェクトに携わらせてもらいました。

初めてのフロントコーディング、初めてのフロントでの外部ライブラリの使用、初めてのAngular、ということでいろいろなことがありました。

その中でも比較的時間が割かれたグラフ機能に関して色々やったことをブログに書きたいと思います。

  1. 最初のグラフが描画されるまで
  2. グラフの見た目を整える
  3. グラフを動的に描画する

の三本立てで書きたいと思います。

準備

使用ライブラリ

valor-software.com

というライブラリを使いました。

早速 https://valor-software.com/ng2-charts/ に書いてあるInstallationとやAPI等を参考にして最初のグラフを描画してみたいと思います!

インストール

まずAngularCLIで新しいAngularプロジェクトを作ります。

その後、

Installation

に従い

npm install ng2-charts --save
npm install chart.js --save

ng2-chartsとchart.jsをインストール。

ng2-chartsだけではなくchart.jsもインストールするんですね。

後はindex.htmlのheadタグの中に

<script src="../node_modules/chart.js/src/chart.js"></script>

を書き込みました。

弊社都合によりバージョンを固定します。 以下のようにpackage.jsonのバージョンの部分を書き変えます。

"chart.js": "~2.5.0
"ng2-charts": "~1.5.0"

この後バージョンを反映させるために

npm install

を使います。

import

APIのUsage

を参考に

import { ChartsModule } from 'ng2-charts';

// In your App's module:
imports: [
   ChartsModule
]

をここでも言われるがままに適宜書きます。 僕はapp.module.tsに書きました。

Canvasタグ

僕が描画したいのは棒グラフなので

Bar Chart

の部分にあるソースコードをコピります。

<div>
  <div style="display: block">
    <canvas baseChart
            [datasets]="barChartData"
            [labels]="barChartLabels"
            [options]="barChartOptions"
            [legend]="barChartLegend"
            [chartType]="barChartType"
            (chartHover)="chartHovered($event)"
            (chartClick)="chartClicked($event)"></canvas>
  </div>
  <button (click)="randomize()">Update</button>
</div>

をhtmlファイルに

import { Component } from '@angular/core';
 
@Component({
  selector: 'bar-chart-demo',
  templateUrl: './bar-chart-demo.html'
})
export class BarChartDemoComponent {
  public barChartOptions:any = {
    scaleShowVerticalLines: false,
    responsive: true
  };
  public barChartLabels:string[] = ['2006', '2007', '2008', '2009', '2010', '2011', '2012'];
  public barChartType:string = 'bar';
  public barChartLegend:boolean = true;
 
  public barChartData:any[] = [
    {data: [65, 59, 80, 81, 56, 55, 40], label: 'Series A'},
    {data: [28, 48, 40, 19, 86, 27, 90], label: 'Series B'}
  ];
 
  // events
  public chartClicked(e:any):void {
    console.log(e);
  }
 
  public chartHovered(e:any):void {
    console.log(e);
  }
 
  public randomize():void {
    // Only Change 3 values
    let data = [
      Math.round(Math.random() * 100),
      59,
      80,
      (Math.random() * 100),
      56,
      (Math.random() * 100),
      40];
    let clone = JSON.parse(JSON.stringify(this.barChartData));
    clone[0].data = data;
    this.barChartData = clone;
    /**
     * (My guess), for Angular to recognize the change in the dataset
     * it has to change the dataset variable directly,
     * so one way around it, is to clone the data, change it and then
     * assign it;
     */
  }
}

をtsファイルに貼り付けます。

バグ?

これでグラフが描画される…はずだったのですが描画されません。 コンソールを見てみると f:id:bbbbbbbbb9:20171019174912p:plain

なんだかこれが原因かなというエラーが出ていました。

まったく意味が不明だったのでこのメッセージでそのままググってみました。結果。

index.htmlのheadタグに貼り付けた

<script src="node_modules/chart.js/src/chart.js"></script>

を消して

angular-cli.jsonのscripts項目に

"scripts": [
        "../node_modules/chart.js/src/chart.js"
],

と書くと上手くいくみたいです。謎です。

他にもいろいろな解決策が書いてありましたが僕はこれでうまくいきました。

これでグラフが描画されました。

f:id:bbbbbbbbb9:20171019175454p:plain

よかった~。

次のブログではオプションなどをいじってグラフの見た目等を色々といじっていきます。

最終的にグラフが、様々な数値やグラフの本数で動的に描画されるようになるのですが、その際にどんな条件でも見た目が崩れないようにする等、予想外にやることが沢山ありました。