AWS LambdaでGmail APIの呼び出し 第1回【cloudpack 大阪 BLOG】

 AWS LambdaからGmailAPIを呼び出したいと思い方法を調べてみました。当初はGmailAPIにアクセスするためのQuickstartが用意されていたため簡単にできるかと思っていました。が、サンプルがOAuth認証のみとなっていました。OAuthではプログラム実行時に手動で認証になるためLambdaではサービスアカウントを使用する必要があります。このあたりの使い方がよくわからずがっつりハマりました。また、Lambdaでもしっかり躓きなかなか大変でしたが、何とかAPIを呼び出すところまで漕ぎ着けたので手順をまとめておきたいと思います。今回は長くなりそうなので2回に分けたいと思います。

手順

1.GmailAPIの設定(第1回)

 ① プロジェクトの作成
 ② APIの有効化
 ③ サービスアカウントの作成

2.Lambdaの設定(第2回)

 ① Lambda用プロジェクトの作成
 ② 秘密鍵をp12からpemに変換
 ③ Lambdaの作成
 ④ 動作確認

開発環境

 Amazon Linux AMI release 2015.09
 Python v2.7.10
 google-api-python-client v1.4.2
 pycrypto v2.6.1
 lambda-uploader v0.5.0

1.GmailAPIの設定

① プロジェクトの作成

前提としてGmailの2段階認証を有効にしておいてください。まずはGoogle Developer Consoleにアクセスします。
HOME画面にアクセスし、「プロジェクトの作成」を選択。初めての場合はプロジェクトの作成画面になるかもしれません。
f:id:cloudfish:20160208013126p:plain

プロジェクト名に任意の名前を入力し、作成をクリック。
f:id:cloudfish:20160208013131p:plain

APIの有効化

デフォルトではGMailAPIは無効化されていますので有効にします。
f:id:cloudfish:20160208013134p:plain

GmailAPIをクリック
f:id:cloudfish:20160208013158p:plain

APIを有効にするをクリック。これでGmailAPIが有効化されました。
f:id:cloudfish:20160208013210p:plain

③ サービスアカウントの作成

Lambdaからアクセスするためのアカウントを作成します。サーバからアクセスする場合はOAuthクライアントIDでも可能ですが、認証が必要となるためサービスアカウントを作成します。左ペインの認証情報を選択し、新しい認証情報クリック。
f:id:cloudfish:20160208013219p:plain

サービスアカウントキーを選択。
f:id:cloudfish:20160208013224p:plain

任意の名前を入力します。キーのタイプP12を選択し作成をクリック。
f:id:cloudfish:20160208013229p:plain

鍵が自動的にダウンロードされますので保管しておいてください。またパスワードも控えておいてください。
余談ですが、たまたまSafariでこのページを開いていたのですが自動でダウンロードされませんでした。
f:id:cloudfish:20160208013233p:plain

サービスアカウントの管理をクリック
f:id:cloudfish:20160208013236p:plain

GmailAPIにアクセスする際はこのメールアドレス(サービスアカウントID)とダウンロードした鍵を使用して認証を行います。
f:id:cloudfish:20160208013240p:plain

これで準備が整いましたので、次回はLambdaでGmailアクセスを行いたいと思います。

AWS IOTでDynamoDBへ登録する【cloudpack 大阪 BLOG】

仕事でAWS IOTを使って簡単なデモを作成しました。センサーから送信されたデータは、IOTからDynamoDBへ直接連携できる機能が用意されていたため、DynamoDBへ登録することにしました。今回はその手順を残しておきます。

手順(概要)

1.テーブル作成
2. AWS IOTの設定
3. 動作確認

1.テーブル作成

AWS IOT連携する場合はソートキーが必須になるのでソートキーも作成してください。

f:id:cloudfish:20160124160343p:plain

2.AWS IOTの設定

①thingの作成

AWS IOT上で管理するデバイスの名前を作成します。
f:id:cloudfish:20160121115311p:plain

②ruleの作成

IOTで受け取ったデータをどのように処理するかのルールを作成します。
f:id:cloudfish:20160124162649j:plain
Create a ruleをクリック

f:id:cloudfish:20160124164411j:plain
f:id:cloudfish:20160124164422j:plain


③証明書の取得

認証用の証明書を取得します
f:id:cloudfish:20160205233115p:plain

秘密鍵、証明書等をダウンロードします。
また使いたいSDKを選択し、「Confirm & start connect」を選択します。
f:id:cloudfish:20160205233131p:plain

以下のページが表示されSDKのダウンロードができます。
f:id:cloudfish:20160205234249p:plain

また、下記ページからroot証明書をダウンロードします。
https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem

3. 動作確認

動作確認はnodeで1回だけpublishするサンプルを作成しました。テストだけならaws cliでも確認可能です。
aws-iot-device-sdk-js/examples以下に適当な名前で下記プログラムを作成します。
また同じディレクトリにcertsディレクトリを作成し上記で取得した証明書を格納しておきます

const deviceModule   = require('..').device;
const cmdLineProcess = require('./lib/cmdline');

const device = deviceModule({
  keyPath: './certs/private.pem.key',
  certPath: './certs/certificate.pem.crt',
  caPath: './certs/root-CA.crt',
  clientId: '1',
  region: 'ap-northeast-1',
  reconnectPeriod:10
});

var thingName = 'iot_test'
device.subscribe(thingName);

var put_data = {};
var data = new Array();


// データ送信
var now = new Date();
var yyyymmdd = now.getFullYear()+
           ( "0"+( now.getMonth()+1 ) ).slice(-2)+
           ( "0"+now.getDate() ).slice(-2);

var hhmmss = ("0" + now.getHours()).slice(-2)  + ("0" + now.getMinutes()).slice(-2) + ("0" + now.getSeconds()).slice(-2) + ("00" + now.getMilliseconds()).slice(-3);
put_data = {
              date: yyyymmdd,
              time : hhmmss,
              value :Math.floor( Math.random() * 11 )
           };


device.publish(thingName, JSON.stringify(put_data));

console.log( JSON.stringify(put_data));
console.log( 'publish');


次のコマンドで実行します。

node put_data.js


publishしたデータ送信データが登録されましたね。
f:id:cloudfish:20160206000916p:plain

今回初めてDynamoDBを使ったのでよく分からず、IOTの設定のところでつまづきました。
また、ルール作成のところでPayload fieldにDynamoに登録したいフィールドを入力しましたが、
全項目が登録されてしまい特定のフィールドだけ選択して登録ということが出来ませんでした。ここはもう少し調べてみたいと思います。

VimでPythonのコード補完設定【cloudpack 大阪 BLOG】

最近はAWSのLambdaなどの開発でPythonを使うことが増えてきましたが、まだまだ慣れていないこともあり、ちょっとしたコードを書くにも関数名がわからないので都度ネットで調べないといけないのがすごくストレスでした。

開発環境としてはLinux上のVimで開発しているため、Vim上で動作するPluginがないか調べたところ「jedi-vim」というよさそうなpluginがあったため導入してみました。まだあまり使い込んでいませんが、コード補完だけでなく定義元への移動やPydocの閲覧もでき動作も軽いので結構気に入りました。
今回はjedi-vimの導入方法について紹介したいと思います。

環境

OS:Amazon linux
Vim 7.4

導入手順

1.Neobundleのインストール
2.jedi-vimのインストール
3. 動作確認

Neobundleのインストール

以下のコマンドを実行
mkdir -p ~/.vim/bundle
git clone https://github.com/Shougo/neobundle.vim ~/.vim/bundle/neobundle.vim
.vimrcに以下の設定を追加
if 0 | endif

if has('vim_starting')
   if &compatible
      set nocompatible               " Be iMproved
   endif

  " Required:
  set runtimepath+=~/.vim/bundle/neobundle.vim/
endif

" Required:
call neobundle#begin(expand('~/.vim/bundle/'))

" Let NeoBundle manage NeoBundle
" Required:
NeoBundleFetch 'Shougo/neobundle.vim'

" My Bundles here:
" Refer to |:NeoBundle-examples|.
" Note: You don't set neobundle setting in .gvimrc!

call neobundle#end()

" Required:
filetype plugin indent on

" If there are uninstalled bundles found on startup,
" this will conveniently prompt you to install them.
NeoBundleCheck
Vimを起動して以下を実行
:NeoBundleInstall

NeoBundleがインストールされます

jedi-vimのインストール

以下のコマンドを実行
cd ~/.vim/bundle/ 
git clone --recursive https://github.com/davidhalter/jedi-vim.git
.vimrcに以下の設定を追加
" Jedi for python
NeoBundleLazy "davidhalter/jedi-vim", {
    \ "autoload": { "filetypes": [ "python", "python3", "djangohtml"] }}

if ! empty(neobundle#get("jedi-vim"))
  let g:jedi#auto_initialization = 1
  let g:jedi#auto_vim_configuration = 1

  nnoremap [jedi] <Nop>
  xnoremap [jedi] <Nop>
  nmap <Leader>j [jedi]
  xmap <Leader>j [jedi]

  let g:jedi#completions_command = "<C-Space>"    # 補完キーの設定この場合はCtrl+Space
  let g:jedi#goto_assignments_command = "<C-g>"   # 変数の宣言場所へジャンプ(Ctrl + g)
  let g:jedi#goto_definitions_command = "<C-d>"   # クラス、関数定義にジャンプ(Gtrl + dlet g:jedi#documentation_command = "<C-k>"      # Pydocを表示(Ctrl + klet g:jedi#rename_command = "[jedi]r"
  let g:jedi#usages_command = "[jedi]n"
  let g:jedi#popup_select_first = 0
  let g:jedi#popup_on_dot = 0

  autocmd FileType python setlocal completeopt-=preview

  " for w/ neocomplete
    if ! empty(neobundle#get("neocomplete.vim"))
        autocmd FileType python setlocal omnifunc=jedi#completions
        let g:jedi#completions_enabled = 0
        let g:jedi#auto_vim_configuration = 0
        let g:neocomplete#force_omni_input_patterns.python =
                        \ '\%([^. \t]\.\|^\s*@\|^\s*from\s.\+import \|^\s*from \|^\s*import \)\w*'
    endif
endif

動作確認

vimを起動して「:help jedi」とタイプし下記画面が表示されれば正常にインストールされていることが確認できます。
f:id:cloudfish:20160107000811p:plain

まずはコード補完(今回の設定ではCtrl+Space)
f:id:cloudfish:20160107000818p:plain

関数の引数も表示してくれます
f:id:cloudfish:20160107000835p:plain:w320

関数の定義元にジャンプ。
f:id:cloudfish:20160107000846p:plain:w320

    ↓↓↓

f:id:cloudfish:20160107000852p:plain:w320

他にも色々機能があるので、もっと色々知りたい方はヘルプを見てみてください。
IDEを使うほどでもな~と思っている方にはピッタリかと思います。

chrootの設定でハマった話【cloudpack 大阪 BLOG】

chrootの設定でかなりハマったので、今回はその内容をまとめたいと思います。

実現したいこと

sftp_userの接続時のホームディレクトリを「/home/sftp_user/home_dir」に制限したい。
 意図は、bash_profile、bash_history、.sshなどをsftp接続ユーザーに見せたくないため

設定方法

ユーザー追加
useradd sftp_user
passwd sftp_user
/etc/ssh/sshd_configの設定変更
#Subsystem      sftp    /usr/libexec/openssh/sftp-server
Subsystem       sftp    internal-sftp

Match User sftp_user
    ChrootDirectory /home/sftp_user/home_dir
    ForceCommand internal-sftp
    PasswordAuthentication yes
sshd再起動
/usr/sbin/sshd -t    ← 構文チェック
service sshd restart

sshdサービス再起動は十分注意してください。再起動が失敗した場合、新たにssh接続ができなくなります。
クラウドサービスのように直接接続する手段がない場合に対応するときは、別でもう一つssh接続をしておくなど慎重に対応してください。
今回はAWSでの対応のため、起動失敗のままセッションを切ってしまうと接続できなくなってしまい、リカバリ対応がかなり大変になることが想定されました。

ディレクトリの設定
chown root:root /home/sftp_user/home_dir ←必須
chmod 755 /home/sftp_user/home_dir   ←必須

mkdir /home/sftp_user/home_dir/data
chmod 755 /home/sftp_user/home_dir/data

※「/home/sftp_user/home_dir/」にはsftp_userの書き込み権限がないため、書き込み可能なディレクトリを用意しておく必要があります。

接続確認
sftp sftp_user@IP_ADDR

でパスワードを入力して接続OK

これで対応完了!

としたかったのですが・・・

接続は鍵認証にしたかったようなので、接続用の鍵を作成し設定しました。
いざ接続確認してみるものの、Connection Refuse・・・
再度ユーザー作成し、sshで鍵認証ができることを確認してから上記対応を行うも接続出来ない状況でした。
/var/log/secureを確認しても芳しい情報は得られずでなかなか解決ができませんでした。

で、もう一度よく設定を見直すと今回chrootの設定先は「/home/sftp_user/home_dir」となります
そのため「/home/sftp_user」はroot権限に設定しているため、ssh接続時に鍵の参照ができてないため接続エラーとなっていることに気が付きました。

ということで上記のディレクトリ構成のままで鍵認証ができないことが分かったので、制限用のディレクトリは別途「/var/sftp/sftp_user」などにすることで解決しました。

貝塚 泉南 2015.11.29

場所  :貝塚
天候  :雲
釣行時間:5:30 - 09:00
釣果  :なし
 
場所  :泉南
天候  :雲
釣行時間:15:00 - 17:00
釣果  エソ 2匹
 
 一日に2回出撃してきました。
朝は貝塚に行きました。とりあえず開始はタチウオ狙いで明るくなることに青物狙いにチェンジしました。タチウオも全く当たりなしの状態でした。青物は周りで2匹くらいサゴシが上がっていましたが、全体的には釣れていない状況でした。とはいえ全く釣れていないわけではないのでもう少しチャンスはありそうです。残り少ないシーズンでもうワンチャンス狙いたいと思います。
 
昼からは、たまたま釣りに行くチャンスがあったので泉南に行ってきましたが、昼間なのでダメ元で行きました。アタリはぽつぽつあったものの上がってきたのは予想通りエソ。周りでは、カレイ狙いで少し釣れているようでした。一年中、青物狙えるフィールドがほしいですね。

f:id:cloudfish:20151129225825j:imagef:id:cloudfish:20151129225841j:image
 
 

EC2インスタンスにRaspberry Piで電源スイッチを付けてみた【cloudpack 大阪 BLOG】

 久しぶりにRaspberryPiを触ってみて思いついたので早速作ってみました。以前はLチカやったところで終わっていましたが、Pythonを少し覚えたので何かAPIと連携させてみようと考えていました。最近、EC2インスタンスを触ることが多くなりましたが、サーバの電源を入れるということを全てWeb上でやっているので物理スイッチでわざわざ起動、停止をやってみようと思い作ってみました。
今回やってみたのは以下のような内容です。

・RaspberryPiでスイッチを押すとEC2が起動もしくは停止
・起動中 or 停止中はLEDを点滅させる
・起動、停止が完了したらslackで自分に通知

準備

RaspberryPi B+ (OSはraspbian 今回使用したバージョンは「Linux raspberrypi 3.12.28+ 」)
ブレッドボード
LED
タクトスイッチ
抵抗 330Ω(LED用)、1kΩ(タクトスイッチ用)
ジャンパーワイヤ

AWSのアクセスキー、シークレットキーの取得しておく
SlackのAPIキーの取得しておく

モジュールのインストール

pipのインストール

apt-get install python-pip

boto3(AWS SDK FOR Python)のインストール

pip install boto3

部品の配置

下記の図のように部品を配置します。GPIO、GNDの配線は間違えないようにしてください。
f:id:cloudfish:20151117005956p:plain:w512
※タクトスイッチは1kΩ、LEDは330Ωの抵抗を配置

実際に配線すると下記のようになります。
f:id:cloudfish:20151117012549j:plain:w384

配線の確認

実際に部品が正しく配置されているか確認するため、下記のプログラムを実行して動作確認を行います。

LEDの動作確認

#! /usr/bin/env python
import RPi.GPIO as GPIO
import time

LEDPIN = 21

GPIO.setwarnings(False) # Suppress warnings

GPIO.setmode( GPIO.BCM )
GPIO.setup( LEDPIN, GPIO.OUT )

while 1:
        GPIO.output( LEDPIN, True )
        time.sleep( 1.0 )
        GPIO.output( LEDPIN, False )
        time.sleep( 1.0 )

実行すると一定間隔でLEDが点滅します。点滅しなければ部品の配置や配線が間違えてないか見直してください

タクトスイッチの動作確認

#! /usr/bin/env python

import RPi.GPIO as GPIO
import time

# define
INPUT_PIN = 4

# init
GPIO.setwarnings(False) # Suppress warnings
GPIO.cleanup()
GPIO.setmode(GPIO.BCM)
GPIO.setup(INPUT_PIN,GPIO.IN)

if __name__ == "__main__":
    Btn_Flag = False

    while True:
        print (GPIO.input(INPUT_PIN))
        time.sleep(0.2)

実行してボタンを押すとボタンが押されている間はコンソールに「1」が表示されます

EC2インスタンスの起動・停止プログラム

上記のテストプログラムが正常に実行されれば実際にボタンを押してEC2のストップ、スタートができるプログラムを作成していきましょう。
AWSのアクセスキー、シークレットキー及びSlackのAPIキーは取得したものに変更してください。また、Slackの送信先は自身のユーザー名(@ + ユーザー名)に変更してください。

#! /usr/bin/env python

import RPi.GPIO as GPIO
import time
import boto3
from boto3.session import Session
import sys
import urllib
import urllib2

# define
INPUT_PIN = 4
LEDPIN = 21

# init 
GPIO.setwarnings(False)	# Suppress warnings
GPIO.cleanup()
GPIO.setmode(GPIO.BCM)
GPIO.setup(INPUT_PIN,GPIO.IN)
GPIO.setup( LEDPIN, GPIO.OUT )

#################################################################
# Function 
#################################################################
def getResourceEC2 ():
    session = Session(aws_access_key_id='ACCESS_KEY',
                      aws_secret_access_key='SECRET_KEY',
                      region_name='ap-northeast-1')
     
    ec2 = session.resource('ec2')
    return ec2

def get_ec2_state(instanceId):
    ec2 = getResourceEC2()
    instance = ec2.Instance(instanceId)
    if(instance.state['Name']=='running'):
        return True
    else:
        return False

def start_ec2(instanceId):
    ec2 = getResourceEC2()
    instance = ec2.Instance(instanceId)
    if(instance.state['Name']=='stopped'):
        instance.start()
        # Flashing LED
        led_flag = False
        while instance.state['Name'] != 'running':
           GPIO.output( LEDPIN, not led_flag)
           led_flag = not led_flag
           instance.reload()
	   time.sleep( 1.0 )
    
        GPIO.output( LEDPIN, True)

        print('EC2 Start!')
        post_slack('EC2 Start!')
    else:
        print('EC2 already Started!')
        post_slack('EC2 already Started!')

def stop_ec2(instanceId):
    ec2 = getResourceEC2()
    instance = ec2.Instance(instanceId)

    if(instance.state['Name']=='running'):
        instance.stop()
        # Flashing LED
        led_flag = False
        while instance.state['Name'] != 'stopped':
           GPIO.output( LEDPIN, not led_flag)
           led_flag = not led_flag
           instance.reload()
	   time.sleep( 1.0 )
    
        GPIO.output( LEDPIN, False)

        print('EC2 Stop!')
        post_slack('EC2 Stop!')
    else:
        print('EC2 already Stopped!')
        post_slack('EC2 already Stopped!')

def post_slack(message):
    url = "https://slack.com/api/chat.postMessage"
    params = {'token'  :'SLACK_API_KEY',
              'channel':'@USER',
              'text'   : message
             }

    req = urllib2.Request(url)
    req.add_header('Content-Type', 'application/x-www-form-urlencoded')
    req.add_data(urllib.urlencode(params))

    res = urllib2.urlopen(req)


#################################################################
# Main Function 
#################################################################
if __name__ == "__main__":
    print('Switch Start')
    
    argvs = sys.argv
    Id = argvs[1]
    Btn_Flag = get_ec2_state(Id)
    GPIO.output( LEDPIN, Btn_Flag)

    while True:
        
        if(GPIO.input(INPUT_PIN)):
           Btn_Flag = not Btn_Flag

           if(Btn_Flag):
               start_ec2(Id)
           else:
               stop_ec2(Id)

        time.sleep(0.2)

※簡略化するため、ステータスチェックなど細かい処理は省いています。

実行

下記のコマンドで実行します。引数には起動させたいEC2のインスタンスIDをセットします。
Switch Startと表示されたら、タクトスイッチを押してみましょう。

root@raspberrypi:~# python input_switch.py [instanceid]
Switch Start

しばらくすると、RaspberryPiのLEDが点滅を始めます。

AWS Management Consoleを確認してみましょう。
おお、起動中になりましたね!
f:id:cloudfish:20151117020140p:plain

起動、停止完了後
Slackにも無事通知されました!
f:id:cloudfish:20151117020445p:plain

まとめ

今回はEC2の起動、停止を行ったため、起動後にLEDが光らないなどプログラムにバグがあった時にやり直しの時間が少しかかりました。RaspberryPiと何かを連携させる場合は、まずRaspberryPiの挙動部分と連携部分を分けて事前に動作確認したうえで、それぞれ統合を進めたほうがいいように思いました。EC2のCPU使用率などを監視してしきい値超えるとLEDで点滅表示など状態を表すこともできそうですね。
電子回路は詳しくないですが、RaspberryPiはすごく簡単に色々できるようになっているので、また何か思いついたら作ってみたいと思います。

泉南  2015.11.08

 

天候  :雨

釣行時間:16:30 - 18:30
釣果  :アジ  7匹
     メバル 3匹
   ウミタナゴ 多数
   タチウオ  3匹
 
泉南の方でイワシとタチウオが釣れてるという情報があったので行ってきました。あいにく雨が強くしばらく様子見でしたが、何とか16時半くらいからサビキを始めることができました。仕掛けを投入するとエサなしでも釣れるくらいウミタナゴがわいてました。時々、アジやメバルがまじって釣れました。20cm前後のメバルを1匹煮付け用に確保しました。サビキでこれだけアジが釣れるならもしかすると青物も周っていたかもしれないです。イワシは全く釣れませんでいした。
 
 サビキはほどほどに夕暮れ前にジグにチェンジしタチウオを狙いました。日が暮れたころに、ジグの着水から1,2回シャクったタイミングでヒット。かなり遠目でヒットしました。一瞬、エソかと思いましたがタチウオがスレで掛かっていました。しばらくして隣のアングラーがワインドでヒットしだしたのでこちらもワインドに。変更してすぐにヒットしました。時合ですね。かなり足下まで寄ってきてたようです。それで時合が終わったようで、ワインドではアタリはなくなりました。
 
釣れなくなってきたので、そろそろ終わろうかと思いましたが、最後にメタルバイブレーションに変更して、遠目から色んなレンジをスローピッチで探っていると、またも足下近くでヒット!
今度は結構重く抜きあげるのを少し躊躇しましたが無事確保。帰ってから測ってみましたが、90cm弱のF4くらいで厚みもしっかりありました。(写真なし)
 
f:id:cloudfish:20151110011656j:plainf:id:cloudfish:20151110010851j:image

今季初のタチウオでした。