sbtとLambdaを使ってCodeDeployを少しだけ自動化する

# 前提

- CodeDeploy側でApplication, DeploymentGroup, AutoScalingなどなどの設定が終わっていること
- Lambdaの設定は基本的には
http://aws.typepad.com/sajp/2015/04/automatically-deploy-from-amazon-s3-using-aws-codedeploy.html
と同じ（現在は不要になっていると思われる手順もある）

# 手順

- Lambda側
基本的にはAWSブログと同じなので差分だけ

	- 必要なRoleはExecution Roleだけ
	- Lambdaの設定は少し画面が変わっている

	- Lambda関数の設定
		- Select blueprintはSkip
		- Name, Descriptionは適当に。RuntimeはNode.js
		- CodeはAWSブログからコピー。必要に応じて修正する
		- Handlerはデフォルトのindex.handler
		- Roleは先ほど作ったRoleを設定
	- Event Sourceの設定
		- event sourceタブを選択
		- Event source typeをS3にして、Bucketを選ぶ
		- Event typeはObject Created

ここまでで、aws deploy pushすると勝手にデプロイしてくれるようになっているはず。

- sbt側
今回はsbtのタスクとして作成。

	- project/plugins.sbtに以下を追加
	
	```
	libraryDependencies += "com.amazonaws" % "aws-java-sdk" % "1.10.20"

	libraryDependencies += "com.amazonaws" % "aws-java-sdk-s3" % "1.10.20"
	```

	- build.sbtにタスクキーの設定を行う（SettingKeyは必要に応じて）
	
	```
	lazy val bucketName = settingKey[String]("S3 Bucket Name")

	lazy val key = settingKey[String]("S3 Key Name")

	lazy val appName = settingKey[String]("CodeDeploy Application Name")

	lazy val dirName = settingKey[String]("Source directory name")

	lazy val push = taskKey[Unit]("push zip to S3 for CodeDeploy")

	bucketName := <Bucket Name>

	key := <S3 Key Name>

	appName := <Application Name>

	dirName := <デプロイするアプリケーションがあるディレクトリ>
	
	lazy val push = taskKey[Unit]("push zip to S3 for CodeDeploy")
	
	push := {
  		DeployPush.push(dirName.value, bucketName.value, key.value, appName.value)
	}
	```
	
	- project/DeployPush.scalaにコードを設定
	
	```
	import com.amazonaws.regions._
	import com.amazonaws.services.codedeploy.AmazonCodeDeployClient
	import com.amazonaws.services.codedeploy.model._
	import com.amazonaws.services.s3.AmazonS3Client
	import com.amazonaws.services.s3.model.{PutObjectRequest, PutObjectResult}
	import sbt._

	object DeployPush {
  		def push(dir: String, bucketName: String, key: String, appName: String): Unit = {
    		val s3Client: AmazonS3Client = new AmazonS3Client()
    		s3Client.setRegion(Region.getRegion(Regions.AP_NORTHEAST_1))
    		val depClient: AmazonCodeDeployClient = new AmazonCodeDeployClient()
    		verifySetting(depClient, appName)

    		val zipFile: File = java.io.File.createTempFile(appName + "-", ".zip")
    		IO.zip(sbt.Path.allSubpaths(new File(dir)), zipFile)
    		val key: String = zipFile.getName

   	 		val req: PutObjectRequest = new PutObjectRequest(bucketName, key, zipFile)
    		println("put Object to S3 : " + zipFile.getName)
    		val res: PutObjectResult = s3Client.putObject(req)

    		val s3Loc: S3Location =
      			new S3Location().
        		withBucket(bucketName).
        		withKey(key).
        		withBundleType(BundleType.Zip).
        		withETag(res.getETag)
    		println("Location: " + s3Loc.getKey)
    		println("Version: " + s3Loc.getVersion)
    		println("ETag: " + s3Loc.getETag)

    		val revLoc: RevisionLocation =
      			new RevisionLocation().
        		withRevisionType(RevisionLocationType.S3).
        		withS3Location(s3Loc)

    		val revReq: RegisterApplicationRevisionRequest =
      			new RegisterApplicationRevisionRequest().
        		withApplicationName(appName).
        		withRevision(revLoc)
    		depClient.registerApplicationRevision(revReq)
  		}
  		
  		private def verifySetting(depClient: AmazonCodeDeployClient, appName: String) {
    		val applications: ListApplicationsResult = depClient.listApplications
    		if (!applications.getApplications.contains(appName)) {
      			println("applicationName(" + appName + ") not found")
      			throw new Exception()
    		}
  		}
	}
	```
	
# デプロイ手順

- sbt clean assembly
- こんな感じのディレクトリ階層を用意しておく(build.sbtでassemblyJarNameを設定して、該当ディレクトリに作成するようにすると少し幸せ）
	
	```
	├── appspec.yml
├── content
│   └── hogehoge.jar
└── scripts
    ├── start_server.sh
    └── stop_server.sh
	```
- sbt push

これでLambdaが気がついてデプロイされる

	
# きになるところ

- SettingKeyを"appName in Push"とかにしたかったんだけど、どうやって設定するのかよくわからない