2014年12月29日月曜日

AWS SDK for Javaとpercolを使ってEC2のインスタンスにログインする

このエントリーをはてなブックマークに追加 はてなブックマーク - AWS SDK for Javaとpercolを使ってEC2のインスタンスにログインする

既にRubyだとやってる人がいて便利そうだったので、Groovy(Java)でも書いてみた。

gist

使うとき

  • ~/.ssh/configにログイン対象のEC2のユーザー名、KeyPair、ホスト名を予め指定しておくこと
  • ~/.aws/credentialsの設定をしておくこと。AWS CLIでconfigure設定をすればOKだと思われる。
  • groovy ec2list.groovy| percol | cut -f 3 | xargs -o -n 1 ssh
  • 上記コマンドで対象のEC2にSSHログインできる
  • いちいちコマンドを打つのが面倒なので適当にalias設定をしておくととても便利

参考資料

2014年12月23日火曜日

AWSセキュリティメモ

このエントリーをはてなブックマークに追加 はてなブックマーク - AWSセキュリティメモ

書いていること

個人的に最低限、知っておいた方が良いと思うこと

  1. アカウントの設定(IAM)
  2. VPC
  3. SecurityGroup

以下の資料に詳しく書いてあります。

AWSのセキュリティについて

書かないこと

  • アプリケーション上のセキュリティとか
  • これだけやればいいということではないです

1.アカウント設定

  • AWS利用時に作成されるアカウント(以降Rootアカウント)は何でも出来て権限が強すぎるのでなるべく使わないようにする
  • IAM(Identity and Access Management)を使って別途アカウントを作成して運用する

Rootアカウントはなるべく使わない

  • Rootアカウントのアクセスキーがあれば削除する。AWSはアクセスキーを使ってAPIを介した各サービスの操作が可能だが、Rootアカウントのアクセスキーは何でも出来てしまうので利用しない。
  • 初期設定時にはRootアカウントはパスワードのみとなっているのでMFAを用いた2段階認証を利用する

MFA?

IAM(Identitiy and Access Management)

  • Rootアカウントとは別に個別にアカウントを作成する事ができるのがIAM
  • 基本的にはIAMで作った個別アカウントのみを使うようにする
  • 権限を細かく設定することができる(S3のReadOnlyユーザーなど)
  • IAM Groupを使うとグループごとに権限を設定できる
  • 個別のアカウントにもMFAが利用できるので特に権限レベルが強いものはMFAをかけておくとより安全

2.VPC(Virtual Private Cloud)

  • AWSはパブリッククラウドだが、AWS内にプライベートネットワークを構築する事ができる
  • VPC内でサブネットを区切ることもできる。VPC領域を192.168.0.0/16とし、その中に192.168.0.1/24192.168.2.0という別のサブネットを作ることもできる
  • AWS Direct Connectでの専用線接続やインターネットVPNを利用することで直接AWSのVPCにアクセスすることも可能(やったことはない)

3層(Web,AP,DB)モデルの場合

  • Amazon Web Services 基礎からのネットワーク&サーバー構築が詳しい。一度これ見て組んでみることを推奨
  • Webサーバーはインターネットと接続が可能なサブネット内に配置し、APサーバーやDBサーバーは内部のみと通信可能なサブネットとすることでセキュリティを高める
  • APサーバーなどのプライベートネットワークからインターネットへ接続する(OSアップデートなど)の場合にはNATを起動して接続する。NATのAMI(AmazonMachineImage)を利用することで簡単に構築できる

3.SecurityGroup

  • InboundとOutboundのファイアウォールの設定が可能
  • 名称の指定も出来るのでGroupNameに予めWebAPなど作っておいてルールを設定しておくと良い
  • 書いてない設定はDenyされるので許可させたいネットワークを書く
  • デフォルトでは22ポート(SSH)がどこからでも接続できるできるポリシーが適用されるので固定IPに変更する。
  • HTTP、HTTPSなどの場合でも極力どこからでも接続できることを意味する0.0.0.0/0anywhereは指定せず固定IPなどの指定を推奨

2014年12月21日日曜日

AndroidUIテストフレームワークのEspresso2.0が出たので試してみた

このエントリーをはてなブックマークに追加 はてなブックマーク - AndroidUIテストフレームワークのEspresso2.0が出たので試してみた

Android UIテストフレームワークのひとつEspressoは今年の1月からずーっとコミットがなさそうで、これはもうお亡くなりになったのか。。。と思っていたら突如2.0が発表されました。

ReleaseNotes

Android Developers

ということで早速試してみたのでメモ。 (細かい所までは見れてないです)

そもそもEspressoとは?

上記に記載したようにGoogleが2013年に発表したUIテストフレームワークのひとつです。

AndroidのUIテストフレームワークはrobotiumuiautomatorなんかがありますね。 calabash-androidというのもあります。

その中でEspressoの特徴ですが、とにかく実行速度がめちゃめちゃ早いことだと思います。

robotiumとの実行速度比較は以下のスライドで見れます。

UI Testing On Android(スライド17)

作成するアプリの画面が少なければ問題ないかもしれませんが、そうでない場合に相当数のUIテストが必要で、そのUIテストに何時間も掛かるようでは継続的インテグレーションへの導入も難しいと思います。そういった意味でEspressoはとても魅力的なテストフレームワークの一つではないかと思います。

Espresso2.0で何が変わった?

詳細はリリースノートを見ていただくとして以下が気になった変更点です。

  • Android Support Repositoryの一部になった
  • Espressoのドキュメントは2015年の早いタイミングでAndorid Developersへ移動する
  • テストランナーがGoogleInstrumentationTestRunneからAndroidJUnitRunnerになった。また、AndroidJUnitRunnerはJUnit4を含むのでJUnit4のスタイルでも記述できる
  • Lollipop対応
  • パッケージ名がcom.google.android.apps.common.testing.ui.espressoからandroid.support.test.espressoになった

最初に書いたようにEspressoはGoogle製のフレームワークにも関わらず、今年に入って更新がほぼなくオワコン感があったのですが、今回の2.0はAndroid Support Repositoryの一つとなり、ドキュメントがAndroidDevelopersサイトへ移行予定とのことなのでAndoridとして推奨のUIテストフレームワークの一つに昇格したのではないかと思います。

1.Espresso1から2への移行をやってみる

Espresso1から2への移行を行ってみます。手順はこちらに記載があります。

EspressoSetupInstructions

最初から2を試したい人は以下に既にGoogleがサンプルプロジェクトを用意してるのでこちらを利用するのが手っ取り早いと思います。(ただし、AndoridSupportRepository最新版のダウンロードは必要)

Android-Testing Github Samples

最終的に変更したものはGithubに配置しています。(robolectric/deckard-gradleを利用して変更しました)

AndroidEspresso2Sample

なお、こちらにあるように実機かEmulatorで開発者メニューの一部は変更済みの想定です。

1–1.Espresso2.0を利用するように変更する

Espresssoに関連するjarファイルはAndroidSupportRepositoryから取得できるようになったので、lib配下のものは参照せず、パッケージ名を明記する方法にbuild.gradleを変更します。

-    androidTestCompile files('lib/espresso-1.1.jar', 'lib/testrunner-1.1.jar', 'lib/testrunner-runtime-1.1.jar')
+    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
+    androidTestCompile 'com.android.support.test:testing-support-lib:0.1'

lib配下のjarファイルが不要になるので削除します。

$rm lib/espresso-1.1.jar
$rm lib/testrunner-1.1.jar

1–2.Espressoテストコードのパッケージ名を変更する

Espressoテストコードのパッケージ名をcom.google.android.apps.common.testing.ui.espressoからandroid.support.test.espressoに変更します。

-import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
+import static android.support.test.espresso.Espresso.onView;

上記のように関連箇所を置換します。

1–3.Espresso2.0のダウンロード

AndroidSDKManagerを開いてExtra配下のAndroid Support Repositoryを最新版(rev11以上)をダウンロードします。

ダウンロードが成功すれば$ANDROID_HOME/extras/andorid/m2repository/com/andorid/support/test配下にespressotesting-support-libフォルダが存在が確認できます。testing-support-lib配下のpomを見るとjunit4.10に依存している記述があり、このファイルからもJUnit4対応が確認できます。

1–4.テストランナーの変更

テストランナーがGoogleInstrumentationTestRunneからAndroidJUnitRunnerになったのでbuild.graleを変更します。

-        testInstrumentationRunner "com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner"
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

1–5.実行

エミュレーターや実機を繋いて以下を実行します。

$./gradlew  clean connectedAndroidTest

成功していればBUILD SUCCESSFULと表示されると思います。

最終的なbuild.gradleは以下のようになりました。

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:1.0.0-rc4'
    }
}

allprojects {
    repositories {
        mavenCentral()
    }
}

apply plugin: 'com.android.application'

android {
    packagingOptions {
        exclude 'LICENSE.txt'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/NOTICE'
    }
    compileSdkVersion 19
    buildToolsVersion "19.1.0"

    defaultConfig {
        minSdkVersion 18
        targetSdkVersion 18
        versionCode 2
        versionName "1.0.0-SNAPSHOT"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    sourceSets {
        androidTest {
            setRoot('src/test')
        }
    }
}

dependencies {
    repositories {
        mavenCentral()
    }
    compile 'com.android.support:support-annotations:20.0.0'
    // Espresso
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
    androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
}

2.JUnit4のスタイルで書いてみる

せっかくなのでテストコードをJUnit4で書いてみることにしました。 ドキュメントは以下を参照。

User guide for AndroidJUnitRunner

なお、ドキュメントにも書いてあるようにJUnit3でも動くので特に昔のコードを変更する必要はなさそうです。 対応する場合には以下が必要です。

2–1.import文の追加

import org.junit.Before;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.InstrumentationRegistry;

2–2.RunWithアノテーションの追加

@RunWith(AndroidJUnit4.class)
@LargeTest
public class DeckardEspressoTest extends ActivityInstrumentationTestCase2<DeckardActivity> {

2–3.Before,Test,Afterのアノテーション追加

@Before
public void setUp() throws Exception {
    ・・・
}

@Test
public void testHoge() {
    ・・・
}

@After
public void tearDown() {
    ・・・
}

2–4.InstrumentationRegistryの実行

ActivityInstrumentationTestCase2を利用する場合、@Before内で手動で以下を実行する必要があるようです。

injectInstrumentation(InstrumentationRegistry.getInstrumentation());

これを実行しないとgetActivityメソッドでnullが返却されてテスト結果がエラーとなってしまいました。

2–5.実行

エミュレーターや実機を繋いて以下を実行します。

$./gradlew clean connectedAndroidTest

今後の話

Espressoのリリースノートに

What about WebView support? While testing internally, we discovered some issues, which required major changes. We are putting the finishing touches on a new and improved API - it is coming shortly and it promises to be awesome.

とあるのでそのうちWebViewサポートがくるかもしれません。

また、Espressoではできないと思いますが、AndoridStudio1.1devでMockableJarタスクが追加されてているのでAndoridの単体テストは実機やエミュレーターががなくても実行できるようになりそうです。

android / platform/tools/base / e8f845b47459973838c99656ae98563b268f21ff^! / .

やってることとしてはASMを使ってandroid.jarのStubExceptionが発生させないように置き換えて各メソッドはデフォルト値を返すようにするようです。(挙動を変える場合にはmockitoを使うっぽい)

2014年12月14日日曜日

既にあるAndorid.mkを使ってNDKビルドをGradleで実行する

このエントリーをはてなブックマークに追加 はてなブックマーク - 既にあるAndorid.mkを使ってNDKビルドをGradleで実行する

GradleでNDKサポートも行っているようですが、基本的にAndroid.mkは既存のものは使わず、自動生成するようになったようです。

シンプルな構成ならば指定された場所にファイルを移動したりすれば良いのですが、現在自分が担当しているプロジェクトはAndorid.mkがとても複雑化しており、既存のAndorid.mkを利用したまま、Gradleによってビルドする方法を試してみたのでそのメモを書きます。

検証ではNDKに最初から入っているサンプルプログラム(hello-jni)を利用します。

成果

toshihirock/AndroidNdkGradleSample  

前提

  • NDKインストール済み
  • android-sdkインストール済み
  • 環境変数ANDROID_HOMEを設定済み
  • Gradleがなんとなくは分かっている(細かい説明を書いていません)

環境

  • MacOSX10.10.1
  • ADT 23.0.0.1245622(build.gradle生成のためだけに利用)
  • NDK-r10d

ndk-buildコマンドとAntでビルド

まずはいつも通りantでビルドしてみます。(ここは別に実施しなくても良いですが一応ビルドできることの確認)

$cd ${NDK_HOME}/samples/hello-jni

# targetIDを確認してから、antのビルドで必要なbuild.xmlを生成
$android list target |less
$android update -p . -t 1

# ndk-buildする
$ndk-build

# antでビルド
$ant debug

# アプリをインストール
$adb install bin/HelloJni-debug.apk

ndk-buildをコマンドラインで実施。それ以外はGradleで行う

手っ取り早くGradleでビルドするようにbuild.gradleを作りたいのでEclipse with ADTを使います。

  • Eclipse(ADT)を起動
  • File→Export
  • Android→Generate Gradle build files

作成後、build.gradleの以下の2箇所を変更します。

# andorid-gradle pluginを最新化
- classpath 'com.android.tools.build:gradle:0.12.+'
+ classpath 'com.android.tools.build:gradle:1.0.0-rc4'

# plugin正式名称が変わったので対応
- apply plugin: 'android'
+ apply plugin: 'com.android.application'

デフォルトのgradlewのバージョンが1.12となっているので新しくします。現在の1.0.0-rc4ではgradle2.2.1が必要なので変更します。

$vi gradle/wrapper/gradle-wrapper.properties

- distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
+ distributionUrl=http\://services.gradle.org/distributions/gradle-2.2.1-all.zip

とりあえず動くかタスクを見つつ確認。

$./gradlew tasks

ネイティブコードのビルドはCLIからndk-buildを実行します。

$ndk-build

上記状態でlibs/armeobiなどの配下に.soファイルが出来ていることが確認できます。

一度この状態でgradleだけでビルドしてみます。

$./gradlew assembleDebug

ビルドが終わってapkができます。パスはbuild/outputs/apk/hello-jni-debug.apkとなっております。

ただし、これにはNDKでビルドを行なった場合に必要な.soファイルが含まれていません。(unzip hello-jni-debug.apkで中身が確認できます。)起動しようとしても.soファイルがないのでエラーが発生し、起動できません。

上記で.soファイルがapkのパッケージングの際にされないのはAndroidGradleプラグインで想定されている箇所に.soファイルが存在しないためです。なお、想定されているパスはsrc/main/jniLibsとなります。

build.gradleを見ると分かるのですが、実はそもそもjavaファイルなどの場所もAndroidGradleプラグインで想定されているものとは違うため、明示的にbuild.gradleに記載されている事がわかります。

    main {
        manifest.srcFile 'AndroidManifest.xml'
        java.srcDirs = ['src']
        resources.srcDirs = ['src']
        aidl.srcDirs = ['src']
        renderscript.srcDirs = ['src']
        res.srcDirs = ['res']
        assets.srcDirs = ['assets']
    }

AndoridStuidoへの移行を契機にAndoridGradleプラグイン(mavenのプロジェクト)の想定するパスに変えた方が良いと思いますが、とりあえずは既存の状態で対応させていく方法をとります。

.soファイルの存在する場所をbuild.gradleに伝えるためにjniLibsプロパティを追加します。

    main {
        manifest.srcFile 'AndroidManifest.xml'
        java.srcDirs = ['src']
        resources.srcDirs = ['src']
        aidl.srcDirs = ['src']
        renderscript.srcDirs = ['src']
        res.srcDirs = ['res']
        assets.srcDirs = ['assets']
        jniLibs.srcDirs = ['libs']
    }

これで準備ができました。 既に.soファイルは出来ているのでgradleを使って再度ビルドします。

$./gradlew assembleDebug

同じようにapkをunzipすると今度はlibフォルダもパッケージングされていることが確認できます。 当然、アプリとしても動作が可能です。

ndk-buildもGradleで実行する

ndk-buildもgradleで実行したいのでタスクを追加します。

冒頭でも書いているようにAndoridGradleプラグインでもNDKのビルドをしようとするのですが、今回は必要ないのでjniプロパティに何も設定しないようにしておきます。(本構成の場合、規定されたパスにネイティブコードはないのでどうせビルドされないですが。。)

sourceSets {
    main {
        ・・・
        jni.srcDirs = []
    }

また、ndk-buildコマンドを実行するための追加のタスク及びcleanタスクやJavaCompileのタスク実行時にndk-buildが実行されるようにします。

// ndk
ext {
    ndkHome = '/Users/toshihirock/android-ndk-r10d/'
}

tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn ndkBuild
}

clean.dependsOn 'ndkClean'

task ndkBuild(type: Exec) {
    println "exec ndk-build"
    commandLine "${ndkHome}ndk-build"
}

task ndkClean(type: Exec) {
    println "exec ndk-build clean"
    commandLine "${ndkHome}ndk-build", 'clean'
}

自分の環境で試す場合にはndkHomeプロパティを変更するなり、build.propertiesに記載したり、環境変数から取得するなりしてください。

cleanタスクとassembleDebugタスクを実行すると一緒にndk-build cleanとndk-buildも実行されます。

$./gradlew clean assembleDebug

参考

2014年12月13日土曜日

GradleでJenkinsのメッセージを取得する

このエントリーをはてなブックマークに追加 はてなブックマーク - GradleでJenkinsのメッセージを取得する

GradleでJenkinsのコミットメッセージを取りたかった(deploygateのメッセージにJenkinsのコミットメッセージとビルド番号を入れたかった)のでGradleスクリプトを作ったのでメモ。

Gitsにも置きました。

toshihirock / JenkinsGradle

// something

// jenkins
import groovy.json.*

ext {
    jenkinsUrl = 'http://localhost:8080'
    buildNumber = System.getenv("BUILD_NUMBER");
    jobName = System.getenv("JOB_NAME");
    println "build number is ${buildNumber}"
    println "jobName is ${jobName}"
}

def getJson(url) {
    println "start getJson $url"
    def response = new URL(url).text
    return new JsonSlurper().parseText(response)
}

def getJenkinsMessage(message)  {
    def url = "${jenkinsUrl}/job/${jobName}/${buildNumber}/api/json"
    def items = getJson(url)['changeSet']['items']
    items.eachWithIndex() { obj, i ->
        message += obj['msg'] + ' '
    }

    return message
}

task jenkins << {
    String message = "B${buildNumber} "
    println getJenkinsMessage(message)
}

参考

【初心者でも】やろうぜGroovy!〜ファイル読み書きしたり、Web APIたたいたり、レスポンスの中身確認したり〜編【今すぐ使える】

Groovy-Looping