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

2014年11月24日月曜日

RobolectricでActivityのテストをやってみる

このエントリーをはてなブックマークに追加 はてなブックマーク - RobolectricでActivityのテストをやってみる

RobolectricでどのようなActivityのテストが出来るか確認してみたのでメモ。

Github

作ってみたプロジェクトはGithubに置きました。

toshihirock/RobolectricSample

Androidアプリ側の実装について本記事には書いていないので実際のコードをご確認いただければ幸いです。

出来ること

  • Activityのライフサイクル(onCreate,onResumeなど)が呼ばれた際の挙動
  • ある操作においてどのようなIntentが投げられているかの確認(他のActivityを呼ぶ側)
  • 特定のIntentが投げられた場合のAcitvityの挙動の確認(他のActivityから呼ばれる側)

出来ないこと

  • 本当に画面遷移をしているかの確認

単体テストなので、当たり前ですが、画面遷移が本当にできているかの確認は出来ません。しかし、「特定の操作でIntentが正しく設定されること(Aボタンが押下された時にhogeというIntentを設定すること)」、「特定のIntentが投げされた時にhogeとなること」はそれぞれ確認できるので、画面遷移に近い確認はできると思います。

Activityのライフサイクル

テストでActivityのライフサイクルを変更できるので、ライフサイクルが変わった時に発生するはずの処理が正しく動作しているか確認できます。

Activity activity = Robolectric.buildActivity(HogeActivity.class).create().get();

以上の記述でonCreateが呼ばれます。 また、以下のようにすることでonResumeを発生させることもできます。

Activity activity = Robolectric.buildActivity(SubActivity.class).create().start().resume().pause().get();

例えばonPauseになった時にあるTextViewの内容が「onPause」になるコードがあった場合には以下のようにテストを書くことができます。

@Test
public void onPauseメソッドが呼ばれたタイミングでTextViewが変更できていること() throws Exception {
    Activity activity = Robolectric.buildActivity(SubActivity.class).create().start().resume().pause().get();

    TextView text = (TextView) activity.findViewById(R.id.textview);
    String actual = text.getText().toString();
    assertThat(actual, is("onPause"));
}

ある操作においてどのようなIntentが投げられているかの確認(他のActivityを呼ぶ側)

あるボタンを押下した時にIntentにputExtraが正しく設定されているかは以下のように確認できます。

@Test
public void ボタン押下時にmessageというIntentでFromDeckardActivityと設定されている事() throws Exception {
    Activity activity = Robolectric.buildActivity(DeckardActivity.class).create().get();

    // click button
    activity.findViewById(R.id.button).performClick();
    ShadowActivity shadowActivity = Robolectric.shadowOf(activity);
    Intent intent = shadowActivity.peekNextStartedActivity();

    String actual = intent.getStringExtra("message");
    assertThat(actual, is("FromDeckardActivity"));
}

peekNextStartedActivityメソッドで起動した(はず)のIntentを取得できるので、そのIntentを確認することで設定が合っているか確認できます。また、peekNextStartedServiceメソッド及びpeekNextStartedActivityForResultメソッドを利用することでサービスの起動やStartActivityForResultを使って起動する際の挙動確認もできます。

なお、Intentに設定したクラス名の確認は以下のようにすることで可能です。

@Test
public void ボタン押下時にIntentにSubActivityクラスが設定されている事() throws Exception {
    Activity activity = Robolectric.buildActivity(DeckardActivity.class).create().get();

    // click button
    activity.findViewById(R.id.button).performClick();
    ShadowActivity shadowActivity = Robolectric.shadowOf(activity);
    Intent intent = shadowActivity.peekNextStartedActivity();

    ShadowIntent shadowIntent = Robolectric.shadowOf(intent);
    String actualClassName = shadowIntent.getComponent().getClassName();
    assertThat(actualClassName, is(SubActivity.class.getName()));
}

特定のIntentが投げられた場合のAcitvityの挙動の確認(他のActivityから呼ばれる側)

呼ばれる側のActivityも挙動を確認できます。

@Test
public void messageというIntentが設定された場合にTextviewに内容を表示すること() throws Exception {
    Intent intent = new Intent();
    intent.putExtra("message", "FromDeckardActivity");

    Activity activity = Robolectric.buildActivity(SubActivity.class).withIntent(intent).create().get();

    TextView text = (TextView) activity.findViewById(R.id.textview);
    String actual = text.getText().toString();
    assertThat(actual, is("FromDeckardActivity"));
}

@Test
public void messageというIntentが設定されていない場合にTextViewに設定されていない旨が表示されること() throws Exception {
    Activity activity = Robolectric.buildActivity(SubActivity.class).create().visible().get();

    TextView text = (TextView) activity.findViewById(R.id.textview);
    String actual = text.getText().toString();
    assertThat(actual, is("not found message extra"));
}

設定されるはずのIntentを作成し、withIntentメソッドを利用することで挙動の確認ができます。

参考

画面遷移をrobolectricでテストする

Driving the Activity Lifecycle

2014年11月16日日曜日

Deckard (for Gradle)を使ってRobolectricとEspressoが使えるAndroidプロジェクト環境をサクッと作る

このエントリーをはてなブックマークに追加 はてなブックマーク - Deckard (for Gradle)を使ってRobolectricとEspressoが使えるAndroidプロジェクト環境をサクッと作る

Androidの単体テスト用フレームワークRobolectricとUIテストフレームワークEspressoが使える環境をDeckard (for Gradle)というテンプレートプロジェクトを利用して簡単に作成してみたのでその時のメモ。(といってもほとんどREADMEの内容そのままだが。)

上記テンプレートプロジェクトでは最初からRobolectrci、Espressoの実行環境(及びそれぞれの試験が一つずつ)あるので、まずは、これを使って環境を作り、その後は自分の好きなように編集していくと環境構築で手間取ることはないかと思います。

実行環境前提

  • AndroidSDKをインストール済み。また、AndroidSDKのAPI19をインストール済み(READMEではAPI18と書いてありますが、 compileSdkVersionが19なので19が必要です)
  • エミュレーターでAPI19のものが存在し、起動済み。また、adb devicesコマンドで認識できていること(Espressoを動かす場合のみ)

Espressoを動かさない場合にはエミュレーターの起動は必要ありません。 余談ですが、AndoidSDK同梱のエミュレーターは遅いのでGenymotionを使って、エミュレーターの起動を高速化すると作業が捗ります。

CUI環境で動かす

Deckard (for Gradle)

README.mdの通りですが、やっていきます。

1.ANDOID_HOMEの設定

GradleのAndroidプラグインにAndoidSDKのパスを教える必要があるため、環境変数ANDROID_HOMEの設定を.bashrc.zshrcなどお使いのシェル環境に設定します。

$echo "export ANDROID_HOME=/usr/local/android-sdk-macosx" >> ~/.zshrc
$source ~/.zshrc

パスの部分は各PCで配置しているAndoroidSDKのパスを設定してください。

設定が正しいか確認します。

$echo $ANDROID_HOME
/usr/local/android-sdk-macosx

2.テンプレートのダウンロード、プロジェクト名の変更

プロジェクトテンプートをダウンロードして解凍します。

$wget https://github.com/robolectric/deckard-gradle/archive/master.zip
$unzip master.zip

プロジェクト名は任意の名前に変更します。

$mv deckard-master my-new-project

3.Robolectricの実行

この状態ですでにRobolectricの実行は可能です。以下で実行が出来きます。

$cd my-new-project
$.gradlew clean test

ビルドが成功するとテストレポートも出力されています。

$open build/test-report/index.html

4.Espressoの実行

私の環境でEspressoを実行した際にはcom.android.dex.DexException: Cannot merge new index 65576 into a non-jumbo instruction!とか表示されてエラーになってしまいました。

対応方法ですが、以下のサイトに記載してありました。

Android Studioでビルド時にCannot merge new index xxxx into a non-jumbo instruction!

おそらく、Espressoはかなり多くのライブラリに依存しており、そのためにjumboModeにしないとエラーになるのではないかと思います。

jumboModeを有効にするためにbuild.gradleに以下の記述を追記します。

android {
    dexOptions {
       jumboMode true
    }
}

追記後、AndoridSDKのAPIが19のエミュレーターを起動し、adbコマンドも認識できることを確認します。 (AndoridSDKのAPIがbuild.gradleのものと同様でないとEspressoのテストが実行されないようです)

準備ができたら、以下のコマンドで実行します。

$./gradlew clean connectedAndroidTest

RobolectrciはJVMで動くので高速ですが、Espressoはエミュレータや実機が必要なため、少し時間がかかります。

こちらもビルドが成功すればテストレポートが出力されています。

$open build/outputs/reports/androidTests/connected/index.html

AndroidStudioで動かす

AndroidStudioではこのプロジェクトをそのままimportできるので、ここまでCUIで確認してからAndoridStudioに取り込み、自分の作成するプロジェクトに応じてAndoridManifest.xmlやソースコードを変えていき、自分のリポジトリにpushしていけば良いと思います。

テストの実行などはAndroidStudioのTerminalからでも実行できますし、Run/Debug ConfigurationでGradleのタスクを追加することでも可能です。

2014年10月24日金曜日

Gitlabで大文字小文字のリポジトリを正しく取り込む方法

このエントリーをはてなブックマークに追加 はてなブックマーク - Gitlabで大文字小文字のリポジトリを正しく取り込む方法

既存リポジトリを取り込むとき、リポジトリ名に大文字小文字がある場合にGitlabは勝手にに大文字も小文字にしてしまう。

これは困ったと思っていたら、実は直す方法が書いてあった。

Allow camelCase / PascalCase repo names

Settings→Rename repositoryで好きな名称に変更出来る。(ただし、危険な変更ではある旨が書いてあるので念のため、実行前にはリポジトリのバックアップなどしておくと良いと思います。)

2014年10月23日木曜日

[Git]過去のリビジョンに戻る場合でのgit checkoutとgit reset --hardの違い

このエントリーをはてなブックマークに追加 はてなブックマーク - [Git]過去のリビジョンに戻る場合でのgit checkoutとgit reset --hardの違い

仕事で過去のリビジョンに戻る方法はどうすれば良いのか?という質問があったのだが、git checkoutとgit reset –hardを使う場合の違いについてよく分かってなかったので調べてみた。

指定リビジョンに戻す

既に記載の通り、2つやり方がある。

$git checkout <commit>

もしくは

$git reset --hard <commit>

である。

ただし、二つは大きな違いがある。

git checkout <commit>

指定されたコミットIDのリビジョンに作業ディレクトリ内のファイルが変更される。ブランチは detached HEAD状態となり、この状態ではコミットなどを行ってもリポジトリに保存されない。(厳密には少し違うが) つまり、read only状態で指定リビジョンの状態確認が出来る。

元のブランチに戻る場合、以下のように元々のブランチ名を指定すれば良い。

$git checkout master

基本的にread onlyであるが、別途ブランチを作成する事で、リポジトリへの追記も可能。

git reset --hard <commit>

現在のブランチのまま、指定されたリビジョンに作業ディレクトリ、ステージング環境、HEADが変わる。もし作業ディレクトリに編集中のファイルがあったという場合にもその情報は失われてしまうので、実行の際には注意が必要。

また、元々のHEADの状態に戻る場合、git reflogを使うかORGI_HEADを使う必要がある。

$git reset --hard ORIG_HEAD

もしくは

$git reflog
$git reset --hard <commit>

結論

大半の場合、checkoutによる操作で事足りそう。そして、checkoutの方が安全。

参考

The git checkout Command

The git reset Command

detached HEAD状態から元に戻すコマンド (git, checkout, fix a detached HEAD, .git/HEAD, refs/heads/master)

git reset についてもまとめてみる

2014年10月14日火曜日

[Git]ローカルのコミットは入れず、リモートリポジトリの更新内容をブランチに取り込む

このエントリーをはてなブックマークに追加 はてなブックマーク - [Git]ローカルのコミットは入れず、リモートリポジトリの更新内容をブランチに取り込む

状態

  • masterブランチ。ローカルで1回コミットを行った。その後、リモートリポジトリが他のメンバーによって更新された。
  • devブランチ。ローカルで1回コミットをする前の状態のmasterからブランチを作成している。

やりたいこと

masterブランチのリモートリポジトリで行われた変更のみをdevブランチに反映したい。 ただし、masterブランチでのローカルで行われたコミット内容はdevブランチに反映したくない。

結論

$git fetch
$git checkout dev
$git merge origin/master

fetchでorigin/masterリモートブランチを最新化し、devブランチにorigin/masterをmergeする。

本作業と同時にmasterブランチにリモートリブランチ(origin/master)のコミットを反映しても良い場合には以下でもOK。

$git pull
$git checkout dev
$git merge origin/master

git pullはgit fetchgit mergeを実行するので、上記だとmasterブランチも変更される。 単純にやりたいことだけやるのであれば、git fetchの方が合っていると思われる。

追記(11/23) トピックブランチにマスターの変更を反映することを2回以上行うと大量のコンフリクトが発生する。

gitで双方向にmergeしているとひどいはまり方をすることがある件

上記より、トピックブランチへマスターの変更を反映するのはマスターへマージする直前のみとし、それ以外の場合にはマスターからブランチを再作成し、cherry-pickなどを利用するのが良いと思われる。

これは駄目

$git pull
$git checkout dev
$git merge master

これを実行するとローカルのコミットもdevブランチに反映されてしまうのでNG。

大切な事

  • git pullとはgit fetchとgit mergeが行われている事を意識する
  • git fetchが実行されると作業ディレクトリの内容はは変更されず、リモートブランチ(orgin/masterなど)のみが変更される
  • masterブランチとリモートブランチ(origin/master)ブランチは別のブランチでである

参考にさせて頂きました

2014年10月7日火曜日

EclipseのプロジェクトをGradleでビルドし、JenkinsでもRobolectricを動くようにする

このエントリーをはてなブックマークに追加 はてなブックマーク - EclipseのプロジェクトをGradleでビルドし、JenkinsでもRobolectricを動くようにする

最近のAndroid開発環境はAndroidStudioがデファクトスタンダードっぽいのですが、関わっているプロジェクトがNDKを利用しており、まだAndroidStudioでは未対応なので、Eclipseを使っております。

そんなプロジェクトなのですが、Robolectricを使ってテストしており、Jenkinsで実行させる為にコマンドラインで実行するまでにした事をメモしておきます。

どうやったか

Antでやる方法もあると思うのですが、Gradleだとそれ用のプラグインがあるのでGradleを使いました。

Github

とりあえず使える状態のサンプルをGithubにおいてあります。

RobolectricGradleSample

READMEに書いてあるような状態であれば以下で実行出来るかと思います。

$gradle clean test

EclipseでRobolectricを実行させる

以下の公式サイトに丁寧に記載があるのでこちらを参照。

Eclipse Quick Start

Gradleのビルドで必要なファイルを作る

Eclipseから簡単にGradleでビルドする時に必要なファイルを出力する事が出来るので、利用します。Eclipseを起動し、以下で完了です。

File→Export→Android→Generate Gradle build files

完了すると色々ファイルが出来ていると思うので、バージョン管理に登録しておきます。

サーバー環境でAndroidをGradleでビルドする

Androidをビルド出来る環境を作る

AndroidSDKをサーバーにインストールします。自分の場合、Linuxだったので対応するSDKをダウンロードして、後は自分のビルドしたいAndoridバージョンのSDKをインストールしておきます。

この時、 AndroidSDK Build-toolsAndroid Support Repositoryもインストールしておいてください。

AndoridSDK Build-toolsはGradleのビルド時にバージョンの指定が必要なので、インストールしたバージョンも確認しておいて下さい。

なお、CUIでAndoridSDKなどのインストール方法はこちらを参照。

特定のAndroid SDKをCUIでインストールする方法

GradleをGVMを使ってインストールする

サーバーにGradleをインストールしますが、普通にインストールするとGradleのバージョン切り替えが面倒なのでGVMを使います。

GVM

インストールします。

$curl -s get.gvmtool.net | bash

現在、利用可能なGradleのバージョンを確認します。

$gvm list gradle

1.12をインストールして利用します。(Androidをビルドするためのプラグインが1.1x系を要求するため)

$gvm install gradle 1.12
$gvm use gradle 1.12

環境変数ANDROID_HOMEを設定する

GradleのAndroidプラグインにて環境変数のANDROID_HOMEを指定する必要があるので、設定します。

$vi ~/.bashrc

追加します。

# Andorid_HOME
$export ANDROID_HOME=/Applications/adt-bundle-mac-x86_64-20140624/sdk/

適用させます。

$source ~/.bashrc

Jenkinsで実行する場合、Jenkinsの環境変数を設定する箇所で上記を設定してください。

build.gradleを編集する

Eclipseでプロジェクトを新規作成するとappcompat_v7とかもコンパイルしなければいけなかったり、lib配下にandroid-support-v4.jarとかが存在します。

Eclipseでビルドする時は良いのですが、サーバーでビルドする時に上記も用意したりするのは面倒なので、Gradleを使って依存を解決します。

という事で作成されたbuild.gradleを一部編集します。 自分の場合、以下のようにしました。

// Gradle自身の環境を設定
buildscript {

    // mavenCentralリポジトリを利用する
    repositories {
        mavenCentral()
    }

    // 利用するGradleプラグインは0.13を利用
    dependencies {
        classpath 'com.android.tools.build:gradle:0.13.0'
    }
}

// すべてでmavenCentralのリポジトリを追加
allprojects {
    repositories {
        mavenCentral()
    }
}

apply plugin: 'android'

dependencies {

    compile fileTree(dir: 'libs', include: '*.jar')

    // 削除。下記のように記載することでGradleより取得
    //compile project(':appcompat_v7')

    // 追加。Gradleによってappcompatの依存関係を解決。
    // excludeの記述によってlib配下のsupport-v4との競合を避ける。
    compile ('com.android.support:appcompat-v7:18.0+') {
        exclude module: 'support-v4'
    }
}

android {
    compileSdkVersion 20
    buildToolsVersion "20.0.0"

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }

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

        // Move the tests to tests/java, tests/res, etc...
        instrumentTest.setRoot('tests')

        // Move the build types to build-types/<type>
        // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
        // This moves them out of them default location under src/<type>/... which would
        // conflict with src/ being used by the main source set.
        // Adding new build types or product flavors should be accompanied
        // by a similar customization.
        debug.setRoot('build-types/debug')
        release.setRoot('build-types/release')
    }
}

変更点は以下の通りです。

  • buildscriptを追加(2~13行目)
  • allprojectsを追加(16~20行目)
  • dependenciesの一部を変更(24~36行目)

buildscriptと記述されている場所はGradle自身の依存関係や利用するプラグインの情報を指定します。 今回の指定では以下2点を指定しています。

  1. buildscriptでの依存解決ではmavenCentralリポジトリを利用利用する
  2. com.android.tools.build:gradleの0.13を利用する

allprojectsと記述されている場所では以下を指定しています。

  1. 依存解決ではmavenCentralリポジトリを利用する(buildscript)

depnedenciesでは以下を指定、変更しました。

  1. appcompat_v7プロジェクトのビルドをする、という命令の箇所を削除
  2. コンパイル時に’com.android.support:appcompat-v7:18.0’以上が必要である事を記述
  3. appcompat-v7の依存を解決する時にsupport-v4に関する依存は解決しない

1点目のappcompatについてですが、Eclipseだとプロジェクトを追加した時にappcompat_v7も一緒に作成されて、ビルドが必要となります。ただし、サーバーでは面倒なので、この役目はGradleに任せる事としてコメントアウトしています。

2点目は1点目でコメントアウトしたappcompatについてGradleで依存解決する為の記述です。コンパイルする時にこのプロジェクトはappcompat-v7:18以上に依存しているよという事を記述しています。

3点目ではsupport-v4に関しての依存部分は解決しなくてよい、という事を指定しています。Gradleで依存解決を行う時は対象のものが依存しているものも一緒に依存解決しようとします。 試しにexcludeを消して、gradle dependenciesというコマンドをタイプして各ライブラリの依存関係を表示します。

$gradle dependencies 
・・・・
_debugCompile - ## Internal use, do not manually configure ##
\--- com.android.support:appcompat-v7:18.0+ -> 18.0.0
     \--- com.android.support:support-v4:18.0.0

上記のようにappcompat-v7:18はsupport-v4に依存している事が分かります。

依存解決は通常ありがたいことなのですが、今回作成したプロジェクトではlibsフォルダ配下に既にsupport-v4が存在しています。そのため、excludeしないとビルドエラーとなってしまいます。(Multiple dex files define Landroid/support/v4/accessibilityservice/AccessibilityServiceInfoCompatとか出ます。)

libs配下のsupport-v4を削除するとEclipseのビルドができなくなってしまいますので、今回は上記のように書いてありますが、Gradleでのビルド前提の場合にはGradleで依存解決は解決する方が妥当だと思います。

AndoridをGradleでビルドする

まず、自分がビルドしたいAndoridプロジェクトをサーバーに任意の場所にcloneします。

そして、build.gradleがある場所(対象のAndroidプロジェクトのRoot)に移動します。

そこで、build.gradleを開き、buildToolsVersionがサーバーでインストールしたバージョンと合っているか確認して下さい。もし、違う場合にはエラーとなるので、書き換えてください。

ANDROID_BUILD_TOOLSというような変数にしておいて、gradle.propertiesなどに設定しておく方法がおすすめです。

さて、ビルドしてみます。

$gradle build 

もし、特にエラーがなければ、build/outputs/apk配下にapkが作成されているはずです。

build.gradldを編集する2

buidl.gradleを編集してRobolectricでテスト出来るようにします。

各変更点は後述しますが、全体で以下のようになりました。

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:0.13.0'
        classpath 'org.robolectric:robolectric-gradle-plugin:0.13.0'
    }
}

allprojects {
    repositories {
        mavenCentral()
    }
}

apply plugin: 'android'
apply plugin: 'robolectric'

android {
    compileSdkVersion 17
    buildToolsVersion "20.0.0"

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }
        androidTest {
            setRoot('test')
        }

        // Move the tests to tests/java, tests/res, etc...
        //instrumentTest.setRoot('tests')

        // Move the build types to build-types/<type>
        // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
        // This moves them out of them default location under src/<type>/... which would
        // conflict with src/ being used by the main source set.
        // Adding new build types or product flavors should be accompanied
        // by a similar customization.
        debug.setRoot('build-types/debug')
        //release.setRoot('build-types/release')
    }
}

robolectric {
    include '**/*Test.class'
}

dependencies {

    compile fileTree(dir: 'libs', include: '*.jar')
    compile ('com.android.support:appcompat-v7:18.0+') {
      exclude module: 'support-v4'
    }

    // test
    androidTestCompile('junit:junit:4.11') 
    androidTestCompile('org.robolectric:robolectric:2.3')
}

変更点は以下の通りです。

  • 依存するプラグインとしてrobolectricを追加(8,19行目)
  • テストフォルダの位置を指定(35~37行目)
  • robolectricで実行するテストクラスを指定(53~55行目)
  • テストのコンパイル時に利用するライブラリを記述(65,66行目)

自分がこの中でも特にはまったのがテストフォルダの位置を指定する箇所です。

これについてなのですが、robolectric-gradle-plugin:0.12.0までは本設定をしてもまったく有効化せず、必ずsrc/test/java配下にファイルを配置しないといけませんでした。

ただ、0.13からは任意のディレクトリの指定が可能となりました。ただし、必ず指定したディレクトリ配下にjavaというフォルダがある必要はありますので、注意が必要です。

GradleからRobolectricを実行する

早速やってみます。

$gradle clean test

gradle1.12では下記エラーになってしまいます。。。

* What went wrong:
A problem occurred evaluating root project 'RobolectricGradleSample'.
> Could not create plugin of type 'AppPlugin'.

ただし、Gradle2.1ではうまくできます。

$gvm install gradle 2.1
$gvm use gradle 2.1
$gradle clean test

こんな感じで結果がbuild/test-report/index.htmlに出力されています。

また、build/test-result/にXMLも出力されるのでこれを利用してJenkinsで実行結果のグラフ表示も可能です。

RobolectricのPluginが1.x系に対応してないからなのでしょうか。。。ちょっといまいち理由が分かっていません。知っている人がいらっしゃったら教えて頂けるとありがたいです。

2014年7月2日水曜日

[Android]CLIからインテントの送信、ブロードキャストの送信を行ってみた

このエントリーをはてなブックマークに追加 はてなブックマーク - [Android]CLIからインテントの送信、ブロードキャストの送信を行ってみた

Androidのアプリで違うアプリから自分のアプリがkickされることって良くあると思います。

で、その時の動作を確認する為に、スタブアプリを作ってーみたいな事を今までやっていたのですが、実はCLIでインテントの送信やブロードキャストの送信も出来る事が分かり、とても便利なのでそのメモ。

参考

ターミナルからIntentを投げる

Broadcastをエミュレートする

前提

  • テストするアプリのパッケージ名はcom.example.toshihirock.myapplicationとしています。適宜自分の環境に置き換えて読んでください。
  • MyActivityというActivityが存在するものとします。
  • adb devicesで端末が認識出来る状態にしておきます。

明示的インテント

$adb shell am start -n 'com.example.toshihirock.myapplication/.MyActivity'

com.example.toshihirock.myapplicationというパッケージ名のMyActivityを起動させます。

アプリ終了

$adb shell am force-stop com.example.toshihirock.myapplication

パッケージ名を指定します。

暗黙的インテント

$adb shell am start -a 'android.intent.action.VIEW' -d 'http://google.co.jp'

-dではDATA_URIを指定しています。

ブロードキャストの送信

今回はom.example.toshihirock.testというアクションでtestという文字列の変数を送る事を確認してみます。

以下のようなクラスがあったとしてます。

MyBroadCastReceiver.java

package com.example.toshihirock.myapplication;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

/**
 * Created by toshihirock on 7/2/14.
 */
public class MyBroadCastReceiver extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        String str = intent.getExtras().getString("test");
        Log.i("tag",str);
    }

}

また、AndroidManifest.xmlでreceiverに上記クラスを設定します。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.toshihirock.myapplication" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MyActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name="MyBroadCastReceiver">
            <intent-filter>
                <action android:name="com.example.toshihirock.test"></action>
            </intent-filter>
        </receiver>
    </application>

</manifest>

上記作成後、以下のコマンドで該当のクラスへのBroadcastが確認出来ます。

$adb shell am broadcast -a 'com.example.toshihirock.test' -e 'test' 'aaaa'

Logcatには以下のように出力されるはずです。

07-01 11:40:37.621    3144-3144/com.example.toshihirock.myapplication I/tag﹕ aaaa

なお、文字列に"などを指定したい場合には、以下のようにエスケープする事で正しく内容が送信出来ます。

$adb shell am broadcast -a 'com.example.toshihirock.test' -e 'test' '\"aaaa\"'

Logcatで確認出来ます。

07-01 12:02:30.007    3144-3144/com.example.toshihirock.myapplication I/tag﹕ "aaaa"

その他

以下のコマンドでヘルプが見れるので詳細はこちらを見ると良いかと思います。

$adb shell am

他にも色々出来るっぽいです。

2014年6月27日金曜日

[AWS]EC2のロードバランサを設定した時のメモ

このエントリーをはてなブックマークに追加 はてなブックマーク - [AWS]EC2のロードバランサを設定した時のメモ

ドットインストールのAmazon Web Services入門を参考にロードバランサの設定をした時のメモ

EC2のインスタンスの準備

  • EC2のインスタンスを作成する
  • 適用させるSecurity Groupsで2280ポートを解放する。
  • sshログインしてnginxなどのWebサーバーを起動させる
  • Webサイトにアクセス出来る事を確認

AMI(Amazon Machine Image)の作成

  • 作成したインスタンスを複製するのためにAMIを作成
  • 作成までは少し時間が必要
  • sshログイン、Webサイトにアクセス出来る事を確認

ロードバランサの作成

  • 作成時に80ポートを転送するように設定
  • 作成した二つのインスタンスを適用
  • 作成後表示されるDNS NameのURLにアクセス(自分の場合、作成後すぐにアクセスするとエラーとなってしまった。数分後、アクセスすると問題なくサイトが表示された)
  • アクセスログを確認し、それぞれのインスタンスにアクセスが出来ていることを確認

2014年6月25日水曜日

ADT(Android Developer Tools)の更新がNo repository found containingとかで出来ない時の解決法

このエントリーをはてなブックマークに追加 はてなブックマーク - ADT(Android Developer Tools)の更新がNo repository found containingとかで出来ない時の解決法

ADT(Android Developer Tools)の更新がうまく出来ない事があって、その時の対応メモ。

AndroidSDK自体の更新は行ったが、ADTの更新がされていない場合、以下のようなダイアログがEclipse起動の際に表示された。

でもChek for updateをクリックしてもNo updates were foundとか言われます。

手動で更新しようとしてHelp→Install New Software→Android Developer Tools Update Site - http://dl-ssl.google.com/android/eclipse/とか選択して次へ次へとやっていくも最終的にNo repository found containingとかでエラーとなる。

An error occurred while collecting items to be installed
session context was:(profile=profile, phase=org.eclipse.equinox.internal.p2.engine.phases.Collect, operand=, action=).
No repository found containing: osgi.bundle,com.android.ide.eclipse.adt,22.6.3.v201404151837-1123206
No repository found containing: osgi.bundle,com.android.ide.eclipse.adt.package,22.6.3.v201404151837-1123206
No repository found containing: osgi.bundle,com.android.ide.eclipse.base,22.6.3.v201404151837-1123206
No repository found containing: osgi.bundle,com.android.ide.eclipse.ddms,22.6.3.v201404151837-1123206
No repository found containing: osgi.bundle,com.android.ide.eclipse.gldebugger,22.6.3.v201404151837-1123206
No repository found containing: osgi.bundle,com.android.ide.eclipse.hierarchyviewer,22.6.3.v201404151837-1123206
No repository found containing: osgi.bundle,com.android.ide.eclipse.ndk,22.6.3.v201404151837-1123206
No repository found containing: osgi.bundle,com.android.ide.eclipse.traceview,22.6.3.v201404151837-1123206
No repository found containing: osgi.bundle,overlay.com.android.ide.eclipse.adt.overlay,22.6.3.v201404151837-1123206
No repository found containing: org.eclipse.update.feature,com.android.ide.eclipse.adt,22.6.3.v201404151837-1123206
No repository found containing: org.eclipse.update.feature,com.android.ide.eclipse.ddms,22.6.3.v201404151837-1123206
No repository found containing: org.eclipse.update.feature,com.android.ide.eclipse.gldebugger,22.6.3.v201404151837-1123206
No repository found containing: org.eclipse.update.feature,com.android.ide.eclipse.hierarchyviewer,22.6.3.v201404151837-1123206
No repository found containing: org.eclipse.update.feature,com.android.ide.eclipse.ndk,22.6.3.v201404151837-1123206
No repository found containing: org.eclipse.update.feature,com.android.ide.eclipse.traceview,22.6.3.v201404151837-1123206

ここでググってみたら、以下のような同じ事象の記載が。

No repository found error in Installing ADT in eclipse Indigo

ここに記載されているようにHelp→Install New Softwareの時の画面下部のContact all update sites during install to find required softwareのチェックを外したらうまくできました。

ちょっとはまった。。

2014年6月24日火曜日

[AWS]EC2及びローカル環境からRDSのMySQLに接続した時のメモ

このエントリーをはてなブックマークに追加 はてなブックマーク - [AWS]EC2及びローカル環境からRDSのMySQLに接続した時のメモ

EC2及びローカル環境からRDSのMySQLに接続した時のメモ なお、予めEC2は起動済みでSSH接続出来る事が前提。

EC2からRDSへの接続

RDSを以下の内容で作成する。

  • Public AccessibleはNOにする
  • EC2と同じSecurityGroupを設定する。

RDS作成後、以下の情報をメモしておく。

  1. エンドポイント(例:dbtest.hogefuga.ap-northeast–1.rds.amazonaws.comなど)
  2. ポート(例:3306)
  3. DBユーザー名(例:dbuser)
  4. DBパスワード

次に設定したSecurityGroupのInboundを確認する。 もし、DBのポート(上記の場合3306)の設定がされていない場合、以下のように同じSecurityGroup間の3306ポートの接続は可能なように設定する。

設定後、EC2にSSH接続し、以下のようなコマンドを実行。

$mysql -h dbtest.hogefuga.ap-northeast-1.rds.amazonaws.com -P 3306 -u dbuser -p

パスワードが聞かれるので入力すればRDSのmysqlへの接続が出来る。

ローカルマシンからRDSへの接続

  • Public AccessibleはYESにする
  • SecurityGroupの設定でRDSに割り当てているもののInboundにアクセス元のローカルのグローバルIPとDBのポートによる接続許可のルールを追加する。(自分は確認の為にアクセス元を0.0.0.0/0にしましたが、セキュリティ的に通常設定すべきではありません)

ローカルマシンからEC2と同様にmysqlコマンドで接続が可能である。

2014年6月10日火曜日

svnコマンドで証明書を無視する

このエントリーをはてなブックマークに追加 はてなブックマーク - svnコマンドで証明書を無視する

svn exportとかした時にこの証明書信用していいんですか、みたいなのが出て落とせない。

(R)eject, accept (t)emporarily or accept (p)ermanently? 

みたいな感じで。

普通にコマンドを打っている場合にはキーボードから入力すれば良い訳ですが、Chefとか使っている場合だと自動化したいのでどうしようかな、と思ったのですが、オプションを指定したら無視出来ました。

svn  checkout https://hogefuga --non-interactive --trust-server-cert --username "hoge" --password "fuga"

expectコマンドとかyesコマンドとか使うのも手かもしれませんが、こちらの方が楽。

[Chef]expectコマンドを使ってAndroidSDKをインストールするレシピを作成してみた

このエントリーをはてなブックマークに追加 はてなブックマーク - [Chef]expectコマンドを使ってAndroidSDKをインストールするレシピを作成してみた

Chefを利用してAndroidSDK本体のインストール及び特定AndroidSDKインストールを自動化させたいと思っていました。

が、指定したAndroidSDKのインストールの途中で利用許諾の承認が必要であり、単純にexecuteコマンドを利用してインストールが出来ませんでした。

調査するとexpectコマンドを使うと対話形式の入力が必要な処理を自動化出来るとのことで実際に実施する事が出来たいのでそのメモを記載します。

そもそもexpectコマンドとは?

  • telnetやsshなどパスワード入力を求められるような処理を自動化出来る
  • 同様にインストール時に許諾への同意などが必要なものも自動化させる事が出来る

前提

  • Chef、Berkshelfなどはインストール済み
  • ホストはMac OSX 10.9.3
  • test-kitchenを利用。ドライバはvagrantを利用。

Chefやtest-kitchenの詳細は過去のブログを参考の事。

Berkshelf version3+Knife soloでnginx環境をVagrantに作ってみた話

[Chef]Berkshelfを利用したJenkinsクックブックを作成してserverspec+Kitchenでテストした話

参考

こちらのクックブックを参考にさせて頂きました。

gildegoma/chef-android-sdk

Github

置きました。

toshihirock/AndroidSDK

  • Ubuntuのテストはしてません
  • URLやAndroidSDKのバージョンは適宜変更してください。(そのうちattribute化したい。。。)

準備

クックブック作成します。

$knife cookbook create android -o .

AndroidSDKを利用する為にJavaのインストールが必要ですが、サードパーティのクックブックを利用 します。 また、AndroidSDKのダウンロード、展開の為にopscodeのarkというクックブックを利用します。

サードパーティのクックブックを使う為にBerksheflを利用する準備をします。

$berks init .

利用するためにmetadata.rbに追記を行います。

$cd android
$vi metadata.rb

name             'android'
maintainer       'YOUR_COMPANY_NAME'
maintainer_email 'YOUR_EMAIL'
license          'All rights reserved'
description      'Installs/Configures android'
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version          '0.1.0'

depends "ark"
depends "java"

また、test-kitchenを使ってプロビジョニングを行うのでその為のファイルを作成します。

$kitchen init

既にBerkshlefで.kitchen.ymlが作成済みの場合にはコンフリクトしますが、yを入力して鵣川書きします。

今回、試験ははCentOSのみに今回はするため、.kitchen.ymlの一部をコメントアウトします。

$vi .kitchen.yml

編集。

platforms:
  #- name: ubuntu-12.04
  - name: centos-6.4

AndroidSDK本体のダウンロード、展開

まずはAndroidSDK本体のダウンロード、展開までのレシピを書きます。 本来はURLやパスはattributesの値とすべきですが、とりあえずベタ書きします。

$vi recipes/default.rb

編集。

include_recipe 'java'

%w{unzip expect}.each do |pkg|
  package pkg do
    action :install
  end
end

ark 'android' do
  url 'http://dl.google.com/android/adt/22.6.2/adt-bundle-linux-x86_64-20140321.zip'
  path '/usr/local/android'
end

URLはAndroidのサイトをみて確認してください。(ビット数に応じてダウンロードするzipが違いますが、今回は判定処理は割愛して64bitで確認)

試験環境の準備

VMの起動。

$kitchen create

VMにChefやBerkshelfのインストール、及びレシピの適用を行います。

$kitchen setup

状態を確認します。

$kitchne list

SSH接続して確認してみます。

$kitchen login
$ll /usr/local
lrwxrwxrwx. 1 root root   20 Jun  9 14:30 android -> /usr/local/android-1

expectコマンドを使ってみる

Chefのコードを書く前に正しく動作出来るかVMでコマンドを実行して確認してみます。

$kitchen login
$sudo su
$cd /usr/local/android/sdk/tools
$vi expectTest.sh

確認用のシェルスクリプトを編集します。

#!/bin/bash

expect -c '

spawn /usr/local/android/sdk/tools/android update sdk --no-ui --filter android-17
set timeout 1800
expect {
  -regexp "Do you accept the license.*" {
    exp_send "y\r"
    exp_continue
  }
}
'

ざっくり説明します。

  • spawn:実際に実行させたいコマンド
  • set timeout:expectの実行を許容する時間。短すぎるとコマンドの途中で強制終了してしまう。
  • regrexp:どの文字列が表示されたら入力を自動的に行うかの正規表現。今回の場合、利用規約のDo you accept …という文字列が表示された時に入力を行う事を示す。
  • exp_send:regrexpで合致する文字列が表示された時に自動入力する文字列。
  • exp_continue:これを指定しないと自動入力したあと復帰出来ないっぽいです。

実行権限を付与して、実行してみます。

$chmod a+x expectTest.sh
$./expectTest.sh

Chefのレシピでexpectコマンドを実行する

成功すればこれをChefのレシピに落とし込みます。

$exit
$vi recipes/default.rb

レシピに追記します。

script 'Install Android SDK' do
  android_version="android-17"
  interpreter 'expect'
  not_if { ::File.exists?("/usr/local/android/sdk/#{android_version}") }
  code <<-EOF
    spawn /usr/local/android/sdk/tools/android update sdk --no-ui --filter #{android_version}
    set timeout 1800
    expect {
      -regexp "Do you accept the license.*" {
        exp_send "y\r"
        exp_continue
      }
    }
  EOF
end

テストも書きます。今回はserverspecを利用します。 Gemfileに追加します。

$echo "gem \'serverspec\'" >> Gemfile

$mkdir -p test/integration/default/serverspec/localhost/
$vi test/integration/default/serverspec/localhost/default_spec.rb

テストを書きます。

require 'serverspec'
include Serverspec::Helper::Exec
include Serverspec::Helper::DetectOS

describe command('which java') do
  it { should return_exit_status 0 }
end

describe command('ls /usr/local/android/sdk/platforms/android-17') do
  it { should return_exit_status 0 }
end

色々実験したので一度、VM環境を削除します。

$kitchen destroy

その後、VMの作成、Chefの適用、serverspecのテストを実施します。

$kitchen test

問題なく完了すればOKです。

2014年6月7日土曜日

Shell勉強会bashの話のみメモ

このエントリーをはてなブックマークに追加 はてなブックマーク - Shell勉強会bashの話のみメモ

Shell勉強会の私的メモ。 bashの話のみ抜き出し。

参考

nanapi 勉強会 vol2-shell勉強会

シェル苦手な人に、今日から始めて欲しい3つの考え方

基本

  • Ctrl+b 1文字戻る
  • Ctrl+f 1文字進む
  • Ctrl+a 先頭に移動
  • Ctrl+e 末尾に移動
  • Ctrl+d カーソル位置の1文字削除
  • Ctrl+h カーソル位置の1文字削除してカーソルが一つ前に進む
  • Ctrl+k 後ろを全部消す
  • Ctrl+u 全部消す
  • Ctrl+r 後方インクリメンタル検索
  • Ctrl+s 全方インクリメンタル検索

コピペとか

  • tmux
  • screen

ワンライナーでシェルスクリプト

  • わざわざシェルスクリプト書かなくても;を使えばコマンドラインで動作が可能
  • $for i in {1..10};do;echo ${i};done # 1 2 3 … 10
  • forコマンドがマジで使える
  • ブレース展開便利
  • $echo {1..10} # 1 2 3 ..10
  • $echo {10..1} # 10 9 8 ..1
  • $echo b{ed,ird} #bed bird
  • for i in $(ls); do; mv $i{,.bak}; done #カレントディレクトリのファイルを.bakに変更
  • seqコマンドでインクリメント数を指定する事も出来る
  • $echo $(seq 1 2 10) #1 3 5 7 9
  • seqコマンドでゼロ埋めも出来る
  • seq -w 0 0.5 1 #0.0 0.5 1.0

2014年6月5日木曜日

[chef]test-kitchenのテストをAWSのEC2で実施する

このエントリーをはてなブックマークに追加 はてなブックマーク - [chef]test-kitchenのテストをAWSのEC2で実施する

前回のブログでJenkinsのレシピを作成し、Vagrantを利用してローカル環境のVMでtest-kitchenを利用してserverspecのテストを実行するようにしました。

今回、kitchen-ec2を使ってその環境をローカルでなく、AWSのEC2を利用してテストするようにします。

参考

Chefのテストツール kitchen-ec2を使う – 導入、チュートリアル

test-kitchen/kitchen-ec2

前提

  • AWSのアカウントがあること
  • AWS のアクセスキー/シークレットアクセスキーが存在すること。
  • EC2のキーペアを作成済みである事
  • VagrantやChefなどがインストール済みである事

環境の準備

必要なものをインストールします。

$gem install test-kitchen
$gem install kitchen-ec2

対象レシピのディレクトリに移動し、.kitchen.ymlを変更します。 既に.kitchen.ymlが存在する場合には上書きするかの確認が表示されますが、必要に応じてバックアップを取得しておいてください。

$cd {repo_dir}
$kitchen init --driver=kitchen-ec2

.kitchen.ymlの編集

.kitchen.ymlを編集します。

---
driver:
  name: ec2
  aws_access_key_id: <%= ENV['AWS_ACCESS_KEY'] %>
  aws_secret_access_key: <%= ENV['AWS_SECRET_KEY'] %>
  aws_ssh_key_id: <%= ENV['AWS_SSH_KEY_ID'] %>
  ssh_key: <%= ENV['SSH_KEY'] %>
  security_group_ids: ["Vagrant"]
  region: ap-northeast-1
  availability_zone: ap-northeast-1a
  require_chef_omnibus: true

provisioner:
  name: chef_solo

platforms:
  - name: ubuntu-12.04
    driver:
      image_id: ami-0596ef04
      username: ubuntu
      flavor_id: t1.micro
  - name: centos-6.4
    driver:
      image_id: ami-99fa7098
      username: root
      flavor_id: t1.micro

suites:
  - name: default
    run_list:
      - recipe[jenkins::default]
    attributes:

ポイントだけ記載します。

  • aws_access_key_id:AWSのアクセスキー
  • aws_secret_access_key:AWSのシークレットアクセスキー
  • aws_ssh_key_id:キーペアの名称
  • ssh_key:キーペア(pem)のファイルパス
  • security_group_ids:設定するセキュリティグループ

詳細は test-kitchen/kitchen-ec2を確認ください。

上記のように環境変数から読み込む設定の場合、~/.bashrcなどに以下のように記載します。

export AWS_ACCESS_KEY="xxx"
export AWS_SECRET_KEY="xxxxxxxx" 
export AWS_SSH_KEY_ID="hogeKeyPair" 
export SSH_KEY="~/.ssh/hogeKeyPair.pem" 

image_idについては自分で作成したAMIでも大丈夫ですし、aws marketplaceのものも利用出来るようです。

aws marketplaceのものを利用する場合、利用承諾をしていないとインスタンス起動時にエラーとなります。その場合には、利用承諾用URLが表示されるのでリンクがエラー文に表示されるのでそこから利用承諾を行うと以降の起動が成功します。

テストの実行

テストの実行は以下のコマンドで可能です。

$kitchen test

上記で

  • インスタンスの作成
  • AWSへのChefのインストール、レシピの適用
  • テストの実行
  • インスタンスの削除

をplatformsに書かれたOSの数分実行されます。

それぞれを個別にやるには

  • kitchen create
  • kitchen coverage
  • kitche verify
  • kitchen destroy

というコマンドが対応しています。

インスタンスの各状態はkitchen listコマンドで確認が出来ます。

2014年5月23日金曜日

外部のbareリポジトリとオレオレGitlabを連携させてみる

このエントリーをはてなブックマークに追加 はてなブックマーク - 外部のbareリポジトリとオレオレGitlabを連携させてみる

オレオレGitlabを構築して、外部のbareリポジトリと連携してみたのでその時のメモ。 (メール機能については除外)

状況

  • 外部サーバーにGitのbareリポジトリがある
  • 外部サーバーにGitlab立てたいけど色々あって却下された
  • じゃあ社内ネットワークにGitlab立ててHookさせれば良いんじゃね?
  • VagrantでVM立ててそこにGitlab立てよう

完成イメージ

環境

  • ホストマシン:Mac OSX 10.9.3
  • VM:Ubuntu12.04(64bit)
  • Vagrant1.6.2
  • Gitlab 6.8.2

UbuntuのVMを起動する

Gitlabが簡単にインストール出来るOmnibusが利用出来るUbuntu12.04(64bit)のVMを立てます。

まずはVagrantfileを用意。大切な箇所だけ抜粋。

# ubuntu12.04 64bitのBOXを予めインストール
config.vm.box = "precise64"

# 80ポートへのフォワード設定
config.vm.network "forwarded_port", guest: 80, host: 1234

起動させます。

$vagrant up

GitLabのインストール

まずはsshアクセスします。

$vagrant ssh

GitlabをVMにインストールします。 取得するGitLabのOmnibusの最新版は こちらで確認し、各自変更してください。

$wget https://downloads-packages.s3.amazonaws.com/ubuntu-12.04/gitlab_6.8.2-omnibus-1_amd64.deb
$sudo dpkg -i gitlab_6.8.2-omnibus-1_amd64.deb
$sudo gitlab-ctl reconfigure

インストールが終わったか、確認します。

$wget http://localhost:8080/

外部からのアクセスする際のURLを設定します。

$sudo vi /etc/gitlab/gitlab.rb
external_url "http://localhost"
$sudo chmod 600 /etc/gitlab/gitlab.rb

再度設定をします。

$sudo gitlab-ctl reconfigure

本例の場合、ブラウザからhttp://localhost:1234/でアクセスします。 実際に会社などで利用する場合には自身のマシンに設定されているIPアドレスでも接続出来るはずです。なお、その場合、gitlab.rbのexternal_urlもマシンに降られているIPアドレスを記述する必要があります。

最初のログインはID「root」、パスワード「5iveL!fe」と入力します。

bareリポジトリをGitLabにも追加する

本例では確認他の為にGithubを利用します。 まず、Githubに適当なプロジェクトを作成します。

その後、該当のプロジェクトのgit cloneする時のURLをメモします。

GitlabのNew ProjectImport existing repository?を選択し、先ほどコピーしたURLにユーザー名とパスワードを設定して指定します。(https://username:password@gitlab.com/company/project.git. など)

上記によってGitLabにbareリポジトリを作成されます。

bareリポジトリは/var/opt/gitlab/git-data/repositories/{namespace}/配下に配置されています。

git configコマンドで確認すると以下のようになっており、originはGithubとなっている事が確認出来ます。

$cd /var/opt/gitlab/git-data/repositories/root/helloworld.git
$git config -l
core.repositoryformatversion=0
core.filemode=true
core.bare=true
remote.origin.url=https://username:password@github.com/toshihirock/Hello-World.git

Gitabへのpush時に取得元のリポジトリにもpushするようにする

Gitlabへのpushタイミングで取得元にもpushする為にHookスクリプトを利用します。

こちらは以下のサイトを参考にさせてもらいました。

git hooks を利用したデプロイを導入しました

gitでbareリポジトリを同期する方法

まず、直接取得元へpushするユーザーが居る事もあると思うので、push時に取得元とGitLabとの間で同期を取るようにします。 この方法を実現する為に、pushの取り込み前に実行されるpre-receiveスクリプトを利用します。

$su git
$cd hooks
$touch pre-receive
$chmod +x pre-receive
$vi pre-receive

編集します。

#!/bin/bash
echo "start pre-receive"
git fetch origin 'refs/heads/*:refs/heads/*'
echo "end pre-receive"

これで取得元の差分があれば取り込む事が出来ます。

また、post-updateスクリプトを利用してGitlabへのpushが完了したタイミングで、取得元にもpushするようにします。 post-receiveスクリプトといのもあるのですが、こちらは複数のブランチへのpushの際にも一度しか呼ばれないので、今回の用途ではブランチ毎に呼び出されるpost-updateスクリプトを利用します。

$touch post-update
$chmod +x post-update
$vi post-update

編集します。

#!/bin/bash
echo "start post-update"
BRANCH=$(git rev-parse --symbolic --abbrev-ref $1)
git push origin ${BRANCH}
echo "end post-update"

上記のようにすると変数BRANCHにpushしたブランチ名が入るので、対象のブランチを取得元にpushすることができます。 先ほどgit configで見たようにGitlabに作成されたbareリポジトリのoriginは取得元(本例だとGithub)となっているので上記のようにすることで取得元へのpushが出来ます。

Gitlabからのクローンを確認

Gitlabからクローン出来るか確認してみます。 ユーザー名、パスワードは置き換えて下さい。

$git clone http://username:password@localhost:1234/root/helloworld.git

Githubからのクローンとpush先の変更

Githubから対象のリポジトリをcloneします。

$git clone http://username:password@github.com/hogefuga.git

git remoteコマンドを利用してoriginの向き先をGitlabに変更します。

$git clone Github url
$git remote set-url origin http://username:password@localhost:1234/root/helloworld.git

originの向き先がGitlabとなっている事を確認します。

$git config remote.origin.url 

pushしてみる

ではcloneしてきたリポジトリを編集してGitlabにpushし、その後、Githubに反映されているか確認します。

$vi README
$git add .
$git commit -m "Github"
$git push origin master
Hello-World [master] git push origin master
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 262 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: start pre-receive
remote: end pre-receive
remote: start post-update
remote: To https://username:password8@github.com/toshihirock/Hello-World.git
remote:    e9c6f5f..35d1433  master -> master
remote: end post-update
To http://username:password@localhost:1234/root/helloworld.git
   e9c6f5f..35d1433  master -> master

上記のようにHookスクリプトで標準出力される内容ははpush時に確認出来ます。(ユーザー名、パスワードは変更しています)

上記実施後、Githubでもpushしたコメントが反映されている事を確認できました。

また、上記の場合には取得元のパスワードがバレバレになるのでその場合にはpost-updateを以下に変更すれば出力がされないようになります。

git push origin ${BRANCH} >/dev/null 2>&1

本例ではhttpプロトコルで確認しましたが、sshプロトコルでも可能です。

2014年5月12日月曜日

Vagrant1.6のDockerの機能使ってコンテナにSSH接続してWebサーバー立ててみたよ

このエントリーをはてなブックマークに追加 はてなブックマーク - Vagrant1.6のDockerの機能使ってコンテナにSSH接続してWebサーバー立ててみたよ

Vagrant1.6からDockerを利用できるという事で色々使ってみたのでそのメモ。

Feature Preview: Docker-Based Development Environments

  • Imageを指定してコンテナを起動
  • Dockerfileを利用してコンテナの起動
  • コンテナにSSH接続してみる
  • SSH接続してWebサーバー立ててみる

確認環境

  • MaxOSX 10.9.2
  • Vagrant 1.6.1

そもそもMacやWindowsだとDockerって動かないのでは?

DockerはMacWindowsのOSでは利用出来ません。 そのため、VagrantではDockerを動かす為に専用LinuxVM(boot2docker)を起動して、その上でDockerを動作させます。

コンテナを起動する

Vagrantでコンテナを起動する方法として以下の2つがあります。

  1. Imageを指定する
  2. Dockerfileを利用してコンテナを起動する

1.Imageを指定する

以下のようにVagrantfileを作成します。

VAGRANTFILE_API_VERSION = "2" 

Vagrant.configure("2") do |config|
    config.vm.provider "docker" do |d| 
        d.image = "paintedfox/postgresql"
    end 
end

上記の例では以下を設定しています。

  • 利用するProviderはdockerを指定
  • 利用するイメージはpaintedfox/postgresqlを指定

次に以下のコマンドでコンテナを起動させます。

$vagrant up --provider=docker

上記によって以下が順番に実行されます。

  1. VM(boot2docker)が起動する
  2. VM(boot2docker)で指定したイメージをpullする
  3. VM(boot2docker)上でdockerを実行し、指定したコンテナをrunする

初回実行時にはイメージのダウンロードを行うため、通信環境によっては多少時間が掛かりますが、辛抱強く待ちます。あまりにも遅いとVagrantのboot_wait時間を超えて起動に失敗してしまいますので、通信環境の良い所で実行するのをおすすめします。(一度pullが終われば、以降はダウンロードする処理は実行されません)

終わった後にVagrantの状態を1.6の新機能global-statusを利用して確認してみます。

$vagrant global-status
id       name    provider   state     directory                                      
-------------------------------------------------------------------------------------
2a00b3b  default virtualbox running   /Users/toshihirock/.vagrant.d/data/docker-host 
3b48b7b  default docker     preparing /Users/toshihirock/vagrant/docker              

providerがvirtualboxとなっているのが、コンテナを動かす為に起動されたVMで、dockerとなっているのが先ほどrunしたコンテナになります。

boot2dockerのVMにも接続する事ができるので、こちらからもコンテナの状態を確認してみます。 ログインする際にはglobal-statusで確認したidを指定します。

$vagrant ssh 2a00b3b 
                        ##        .
                  ## ## ##       ==
               ## ## ## ##      ===
           /""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~
           \______ o          __/
             \    \        __/
              \____\______/
 _                 _   ____     _            _
| |__   ___   ___ | |_|___ \ __| | ___   ___| | _____ _ __
| '_ \ / _ \ / _ \| __| __) / _` |/ _ \ / __| |/ / _ \ '__|
| |_) | (_) | (_) | |_ / __/ (_| | (_) | (__|   <  __/ |
|_.__/ \___/ \___/ \__|_____\__,_|\___/ \___|_|\_\___|_|
boot2docker: 0.8.0
$docker ps -a
CONTAINER ID        IMAGE                          COMMAND                CREATED             STATUS                     PORTS               NAMES
2dc46197e3be        paintedfox/postgresql:latest   /scripts/start.sh /s   7 minutes ago       Exited (1) 2 minutes ago                       docker_default_1399814767  
$exit

既に処理を実行し、終了している事が確認できました。

Vagrantで経由でDockerの操作を行った際に何か困った事があれば、上記VMにSSHログインして調査すれば問題が解決しやすいかと思います。

コンテナの削除はvagrant destoryでvagrant global-statusで確認したidを指定すればOKです。 idは全て入力する必要はなく、一意に指定できれば良いので以下のようにしても動作が可能です。

$vagrant destroy 3

2.Dockerfileを指定する

以下のようにVagrantfileを作成します。

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure("2") do |config|
    v.vm.provider "docker" do |d|
        d.build_dir = "."
    end
end

上記の例では以下を設定しています。

  • 利用するProviderはdockerを指定
  • 利用するDockerifleはVagrantfileと同じディレクトリに配置

Dockerfileは先ほどと同じイメージを利用するようにしました。

FROM aintedfox/postgresql

これで先ほど同様にvagrant upすればOKです。

$vagrant up --provider=docker

1.Imageを指定するの手順を実行した後、上記を実行するとかなり早くコンテナの起動が出来るかと思います。

VM(boot2docker)の起動処理の時間とイメージをpullしてくる時間が省略された為です。

なお、VM(boot2docker)を削除すると、イメージを再度pullしてくる必要があるので必要です。

終わった後はコンテナを削除します。

$vagrant destroy 3

コンテナにSSH接続してみる

sshdのサービスを起動させるイメージを利用する事でVMと同じようにコンテナにもSSH接続が可能なようで試してみます。

以下のようなVagrantfileを用意します。

VAGRANTFILE_API_VERSION = "2" 

Vagrant.configure("2") do |config|
    config.vm.define "phusion" do |v| 
        v.vm.provider "docker" do |d| 
            d.cmd = ["/sbin/my_init", "--enable-insecure-key"]
            d.image = "phusion/baseimage:latest"
            d.has_ssh = true
        end 
        v.ssh.username = "root"
        v.ssh.private_key_path= "insecure_key"
        v.vm.provision "shell", inline: "echo hello"
    end 
end

上記の例では以下を設定しています。

  • 利用するProviderはdockerを指定
  • 利用するイメージはphusion/baseimageの最新(latest)
  • /sbin/my_initというコマンドを引数–enable-insecure-keyを指定して実行。
  • VagrantでSSHを利用する事を指定
  • SSHでログインするユーザーはroot
  • SSHでログインする際に利用する鍵はinsecure_key(詳細は後述)
  • コンテナの処理が完了したらhelloと出力する。

詳細は公式ドキュメントをご確認頂きたいのですが、本例では指定してるイメージがphusion/baseimageである事が大切です。

このイメージのドキュメントに詳細が書いてありますが、恐らく初期化処理の中でssdhのサービスを起動させる処理があり、これによってコンテナにも関わらず、SSH接続を可能にしているようです。

つまり全てのイメージでSSH接続が出来るという訳ではないのです

また、上記コンテナへのSSH接続は必ず指定された鍵を利用して認証を行う必要があります。そのため、以下のコマンドでVagrantfileと同じディレクトリに予めSSHで利用する鍵を配置しておく必要があります。

$curl -o insecure_key -fSL https://github.com/phusion/baseimage-docker/raw/master/image/insecure_key
$chmod 600 insecure_key

上記準備ができたら今まで同様に起動させます。

$vagrant up --provider=docker
・・・・
==> phusion: hello

正しく処理が完了した場合には、helloという出力がされる事も確認出来るかと思います。 ではコンテナにSSH接続してみます。

$vagrant ssh
==> phusion: SSH will be proxied through the Docker virtual machine since we're
==> phusion: not running Docker natively. This is just a notice, and not an error.
Warning: Permanently added '172.17.0.2' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 12.04.4 LTS (GNU/Linux 3.8.0-35-generic x86_64)

 * Documentation:  https://help.ubuntu.com/
 root@cb4eb5281f86:~#

イメージのベースはUbuntu12.04であり、そのコンテナにVagrantを利用して簡単にSSH接続する事が出来ました!

確認後、コンテナは削除します。

$vagrant destroy 3

SSH接続してWebサーバー立ててみる

単純にSSH接続出来ただけだと意味が無いので、実際にローカルマシン(vagrantを動かしているマシン)からアクセス出来るようにしてみます。

上記については既にブログに記載している方がいらっしゃり、とても分かりやすく説明されています。

Vagrant1.6のDocker Provider

が、一応こちらでも記載します。(上記ブログの方の方が分かりやすいです!)

ローカルマシンからのアクセスを実現する為には、ローカルマシン→VM(boot2docker)→コンテナと接続しなければいけません。 つまり、ポートフォワードを2カ所(ローカルマシン→VM。VM→コンテナ。) で設定する必要があります。

VM→コンテナについては今まで編集してきたVagrantfileにポートフォワードの設定を記載すればOKです。

ローカルマシン→VM間のポートフォワードを指定する場合には、新しくVagrantfileを作成する必要があります。VM→コンテナ間のVagrantfileに以下のように追記を行います。

d.vagrant_vagrantfile = "host-vm/Vagrantfile"

指定した場所にVagrantfileを配置する必要があるのですが、デフォルトでVM(boot2docker)に適用されるVagrantfileがあるのでそれに追記を行います。

$mkdir host-vm && cd host-vm
$wget https://raw.githubusercontent.com/mitchellh/vagrant/master/plugins/providers/docker/hostmachine/Vagrantfile

配置したファイルにポートフォワードに関する追記を行うと以下のようになります。

Vagrant.configure("2") do |config|
  config.vm.box = "mitchellh/boot2docker"

  config.vm.provider "virtualbox" do |v|
    # On VirtualBox, we don't have guest additions or a functional vboxsf
    # in TinyCore Linux, so tell Vagrant that so it can be smarter.
    v.check_guest_additions = false
    v.functional_vboxsf     = false
  end

  ["vmware_fusion", "vmware_workstation"].each do |vmware|
    config.vm.provider vmware do |v|
      if v.respond_to?(:functional_hgfs=)
        v.functional_hgfs = false
      end
    end
  end

  # b2d doesn't support NFS
  config.nfs.functional = false
  config.vm.network :forwarded_port, guest: 8080, host: 8080
end

同じようにVM→コンテナ間のVagrantfileにもポートフォワードに関する追記を行います。最終的には以下のようになります。

VAGRANTFILE_API_VERSION = "2" 

Vagrant.configure("2") do |config|
    config.vm.define "phusion" do |v| 
        v.vm.provider "docker" do |d| 
           d.vagrant_vagrantfile = "host-vm/Vagrantfile"
                d.cmd = ["/sbin/my_init", "--enable-insecure-key"]
                d.image = "phusion/baseimage:latest"
                d.has_ssh = true
        end 
        v.ssh.username = "root"
        v.ssh.private_key_path= "insecure_key"
        v.vm.provision "shell", inline: "echo hello"
        v.vm.network :forwarded_port, guest: 80, host: 8080
    end 
end

準備ができたので、順番に操作をしていきます。

まず、VM(boot2docker)を一度停止します。 Vagrantfileを明示的に指定すると別のVM(boot2docker)が起動するのですが、その際に既に起動しているものとポートがバッティングしてエラーとなる為です。恐らく回避方法もありそうな気がしますが。。。

$vagrant halt {vm_id}

次にVM(boot2docker)とコンテナを起動します。

$vagrant up --provider=docker

次にコンテナにSSH接続してnginxをインストールし、サービスを起動します。

$vagrant ssh
$apt-get update
$apt-get install -y nginx
$service nginx start
$exit

これで準備完了です。

ローカルマシンからブラウザでhttp://localhost:8080/にアクセスしてみます。

コンテナのnginxのWebページを表示する事が出来ました。

最後に

実際に動いているのはコンテナでもVagrantを利用する事でさもVMを利用しているかのように操作を出来るこの機能はとても素晴らしいと思います。

Dockerにも任意のprovisonが適応できるようなので、Chefとか流したりもきっと出来そうな。今度時間があればやってみたい。。。

2014年5月10日土曜日

Vagrant1.6のGlobalStatus and Controlが地味に便利だった話

このエントリーをはてなブックマークに追加 はてなブックマーク - Vagrant1.6のGlobalStatus and Controlが地味に便利だった話

Vagrant1.6の大きな変更の一つ、GlobalStaus and Controlが地味に便利だったのでメモ。

この機能を使うと

  • Vagnratで管理しているVMの一覧とその状態などが確認出来る
  • いちいちVagrantfileのあるディレクトリに移動しなくてもVMの操作が可能。(起動、中断、SSH接続など)

という事が出来ます。

なお、公式のドキュメントは こちら参照のこと。

準備

Vagrant1.6をまだインストールしていない人はインストール(アップグレード)しましょう。

$vagrant -v
Vagrant 1.6.1

なお、アップグレードの場合のみだと思われますが、私の環境ではvagrant global-statusコマンドを初回実行時に以下のような事を言われました。

Vagrant is upgrading some internal state for the latest version. Please do not quit Vagrant at this time. While upgrading, Vagrant will need to copy all your boxes, so it will use a considerable amount of disk space. After it is done upgrading, the temporary disk space will be freed.

Press ctrl-c now to exit if you want to remove some boxes or free up some disk space.

Press any other key to continue.

vagrantのアップグレードで一時的にBoxのコピーが必要だからその間Vagrantを止めないでね、もし、ディスクスペースが少ない場合にはctrl-cで中断してBox削除してね、問題なければそれ以外のコマンド押してね、という感じっぽいです。(適当)

よっぽどディスクが逼迫していなければEnterキー押してしまって良い思います。

コマンド実行してみる

早速使ってみます。最初に書いたように実行はどこのディレクトリでもOKです。

$vagrant globa-status
id       name    provider   state    directory                              
----------------------------------------------------------------------------
4d3ea23  default virtualbox poweroff /Users/toshihirock/vagrant/ubuntu12_32 
96c9db6  default virtualbox running  /Users/toshihirock/vagrant/ubuntu14_64 

The above shows information about all known Vagrant environments
on this machine. This data is cached and may not be completely
up-to-date. To interact with any of the machines, you can go to
that directory and run Vagrant, or you can use the ID directly
with Vagrant commands from any directory. For example:
"vagrant destroy 1a2b3c4d"

上記のようにVagrantで管理しているVMの一覧とその状態及びVagrantfileの位置などが分かります。

個人的にあれVagrantfileどこだっけ??という事がたまにあるのでVagrantfileの配置されたディレクトリが分かるのが嬉しいです。

今まで無かった情報としてVMごとに一意に決められたIDについても表示がされています。 これを使う事でVagrantfileのある場所に移動しなくても指定VMへの操作が出来ます。

例えばIDが96c9db6となっているVMにSSH接続したい場合には以下のようにします。

$vagrant ssh 96c9db6

公式ドキュメントを見る限り、SSHは勿論、updestorysuspendなど基本的な操作も可能なようです。

また、こちらは公式には書いてませんでしたが、全てのIDを入力しなくても一意にIDできるまでの入力のみでもコマンド実行が可能なようです。

$vagrant ssh 9

これも覚えておくと地味に便利ですね。

2014年5月1日木曜日

Chefのレシピ管理ツールBerkshelfのversion3のドキュメントを和訳してみた

このエントリーをはてなブックマークに追加 はてなブックマーク - Chefのレシピ管理ツールBerkshelfのversion3のドキュメントを和訳してみた

Chefの管理ツールであるBerkshlefのversion3がやっとでリリースされ、しかもChef-DK(Chef Deploymant Kit)とかいうのSDKにも内包されるようになったようで、せっかくなのでversion3のドキュメントを非公式に和訳してみました。

今回、GithubのREADME.mdに書いてみました。

Github-TranslateBerkshelf

色々良い訳。

  • 本ページはBerkshelf version3のドキュメント非公式に和訳したものです。
  • 2014年5月1日時点のものを参照したため、今後変更となる箇所がある可能性もあります。
  • 意訳した箇所もあります。
  • 動作確認を行えなかった箇所も多い為、その点はご了承ください。
  • 英語が得意!という訳でもないので、すいません、その点もご了承ください。
  • 記述誤りなどは @toshihirockまでご連絡頂くか、pull requestして頂くなどして頂ければ幸いです。

なお、vagrant berkshelfプラグインですが、私の環境では正しく動きませんでした。。。

vagrant-berkshelfは非推奨になるとのこと

上記記事からも非推奨となるようで、Vagrantと組み合わせて使う場合でも 以前ブログに書いたように使った方が良さそうな気がしました。

2014年4月19日土曜日

Ubuntu64bitでAndroidのadbコマンド実行するとNo such file or directoryとか出る時の対応

このエントリーをはてなブックマークに追加 はてなブックマーク - Ubuntu64bitでAndroidのadbコマンド実行するとNo such file or directoryとか出る時の対応

Ubuntu13.10の64bitでAndroidのadbコマンドを実行するとNo such file or directoryでエラーとなってしまった

$ adt-bundle-linux-x86_64-20140321/sdk/platform-tools/adb 
-bash: adt-bundle-linux-x86_64-20140321/sdk/platform-tools/adb: No such file or directory

調べるとAndroid SDKのページに以下のような記述があり、Linuxの64bitマシンでも32bitアプリが動くような環境にする必要があるらしい。

64-bit distributions must be capable of running 32-bit applications.

こちらのChefのレシピを参考にするとUbuntuのバージョンによってインストールする必要があるものは違うようだが、Ubuntu13.10の場合、lib32stdc++6をインストールすれば動くようになった。

$sudo apt-get install lib32stdc++6
$adt-bundle-linux-x86_64-20140321/sdk/platform-tools/adb version
Android Debug Bridge version 1.0.31

Ubuntu13.04以上の場合にはlib32stdc++6、Ubuntu11.10以上の場合にはlibstdc++6:i386、それ以外の場合にはlib32z1が必要らしいがこちらは未確認。

2014年4月12日土曜日

VagrantでVirtualBoxを利用する場合にVMの設定をVagrantfileで色々変えてみた

このエントリーをはてなブックマークに追加 はてなブックマーク - VagrantでVirtualBoxを利用する場合にVMの設定をVagrantfileで色々変えてみた

VagrantでVirtualBoxを利用する場合にVMの設定をVagrantfileを利用して変更する場合のメモを記載 。

やってみたこと。

  • GUIモードの起動
  • VM名称変更
  • メモリ変更
  • CPU数
  • ホストOSに対してのCPU利用制限率
  • HDDの追加

なお、基本的な記述は公式のこちら を参照の事。

GUIモードにする

Vagrantfileに以下の記述を追記すればGUI用のウィンドウが利用出来る。

config.vm.provider "virtualbox" do |vb|
  vb.gui = true
end

ただし、BOXにGUI用のソフトがインストールされていない場合には別途インストールが必要。

Ubuntuの場合、VagrantをGUIで立ち上げる(Ubuntu)を参考にして実施出来る事を確認。

VMの名称を設定する

デフォルトだとVMの名称はVagrantfileのあるフォルダ名称とタイムスタンプを利用したものになるので、以下で任意に設定可能

config.vm.provider "virtualbox" do |vb|
  vb.name = "my_vm"
end

ちなみに後述するvb.customizeを利用した以下でも設定可能。

config.vm.provider "virtualbox" do |vb|
  vb.customize ["modifyvm", :id, "--name", "my_vm"]
end

その他色々もっと変更したい

上記以外でも色々設定したい場合、Virtualboxの操作をCUIで行えるVBoxManageコマンドをvb.customizeをというような記述で記載する。

VBoxMangeに関する詳細は公式のこちら を参照の事。

例えばVBoxManage modifyvmというコマンドを利用して名称変更する場合には、以下のような記述となる。

config.vm.provider "virtualbox" do |vb|
  vb.customize ["modifyvm", :id,
                "--name", "my_vm"]
end

上記はコマンドラインで

$VBoxManage modifyvm ubuntu64_default_1397309544936_13521 --name my_vm

というのを実行した場合と同義となる。

なお、ubuntu64_default_1397309544936_13521というようなどのVMに対して処理を実行するかをコマンドからは指定する必要があるが、Vagrantfileに記述する場合には:idと記載すればVM名称となるようにしてくれる。

デフォルトで既に記述があるが、メモリを変えたい場合には以下のようにする。

config.vm.provider "virtualbox" do |vb|
  vb.customize ["modifyvm", :id,
                "--memory", "1024"]
end

CPUの数を変えたい場合はこんな感じ。

config.vm.provider "virtualbox" do |vb|
  vb.customize ["modifyvm", :id,
                "--cpus", "2"]
end

ホストマシンのCPUをどれぐらいの利用制限率とするかも以下で変更出来る。

config.vm.provider "virtualbox" do |v|
  vb.customize ["modifyvm", :id,
                "--cpuexecutioncap", "50"]
end

また、こちらを参考に、以下のようにするとHDDの追加も可能だった。

config.vm.provider :virtualbox do |vb|
      file_to_disk = "./tmp/disk.vdi"
      if not File.exist?(file_to_disk) then
        vb.customize ["createhd",
                      "--filename", file_to_disk,
                      "--size", 100 * 1024]
      end
      vb.customize ['storageattach', :id,
                    '--storagectl', 'SATAController',
                    '--port', 1,
                    '--device', 0,
                    '--type', 'hdd',
                    '--medium', file_to_disk]
end

なお、上記のSATACOntrollerという箇所はSATA Controllerとなっている場合もあり、その場合には対応する記述にしないとエラーとなるので注意。

2014年4月2日水曜日

Vagrantを利用して作成したVMからVMへ.ssh/configを設定して簡単にログインする

このエントリーをはてなブックマークに追加 はてなブックマーク - Vagrantを利用して作成したVMからVMへ.ssh/configを設定して簡単にログインする

.ssh/configを利用して

$ssh hoge

みたいなやり方でログイン出来るようにする。

構成

  • Ubuntu12.04 32bit
  • ログインする方(192.168.33.20)、ログインされる方(192.168.33.21)
  • Vagrantにて上記VMを作成し、起動(vagrant up)済み
  • ホスト名はsound
  • ログインするアカウントはvagrantアカウント

ログインする方(vagrantユーザーでログイン)

# 秘密鍵、公開鍵を作成
$ssh-keygen -t rsa
$cd ~/.ssh

# 公開鍵を表示して何かにコピペ
$cat id_rsa.pub

# sshログインの情報設定
$vi config

Host sound
HostName 192.168.33.21
User vagrant
IdentityFile ~/.ssh/id_rsa

ログインされる方(vagrantユーザーでログイン)

# authorized_keysの末尾にid_rsa.pubの内容を追加
$vi ~/.ssh/authorized_keys

この状態でログインする方のVMで

$ssh sound

とするとパスワードなしでログインが可能となる。

参考

.ssh/configで設定すると接続が楽になる方法

2014年3月21日金曜日

JenkinsのGitPluginでコメントが文字化けした時の解消方法

このエントリーをはてなブックマークに追加 はてなブックマーク - JenkinsのGitPluginでコメントが文字化けした時の解消方法

UbuntuにJenkinsをインストールしてGitPluginを入れて連携させた時にコミットコメントが文字化けしたので解消方法をメモ。

原因はJenkinsのファイルエンコーディングがUTF–8になっていないためなので、これを直す。

パッケージインストール(apt-getなど)の場合、/etc/default/jenkinsにJAVA_ARGS=“-Dfile.encoding=utf–8”を追記してJenkinsをリスタートすればOK

$sudo vi /etc/default/jenkins
JAVA_ARGS="-Dfile.encoding=utf-8"
$sudo service jenkins restart

これで直るはず。

warを利用してtomcatで動かしている場合、CATALINA_OPTSDfile.encoding=utf–8を指定した後に起動すればOK。

Tomcatユーザーで起動している場合、

$vi ~/.bashrc
CATALINA_OPTS="-Dfile.encoding=utf-8"
export CATALINA_OPTS
$source ~/.bashrc

としてtomcatを再起動すれば問題ないはず。

参考

Ubuntu 12.04にJenkinsをインストールしてApacheでポート80で動かす

CATALINA_OPTSの指定

2014年3月19日水曜日

特定のAndroid SDKをCUIでインストールする方法

このエントリーをはてなブックマークに追加 はてなブックマーク - 特定のAndroid SDKをCUIでインストールする方法

AndroidサイトにあるAndroidSDKを落としきても最新のAndroidSDK(Andorid19)のみしかなく、昔のSDKでビルドする必要がある場合、別途ダウンロードが必要である。

GUIであれば簡単だが、XWindowsがないなどの場合にCUIのみで実施する必要があり、その場合のメモ。

また、全てのパッケージをダウンロードするとめちゃくちゃ時間が掛かるので特定の奴だけダウンロードする方法を記載する。

まず、現在利用可能なパッケージは以下のコマンドで確認する。

$android list sdk
Packages available for installation or update: 45
   1- Android SDK Tools, revision 22.6.1
   2- Documentation for Android SDK, API 19, revision 2
   3- SDK Platform Android 4.3, API 18, revision 2
   4- SDK Platform Android 4.2.2, API 17, revision 2
   5- SDK Platform Android 4.1.2, API 16, revision 4
   6- SDK Platform Android 4.0.3, API 15, revision 3
   7- SDK Platform Android 4.0, API 14, revision 3
   8- SDK Platform Android 3.2, API 13, revision 1
   9- SDK Platform Android 3.1, API 12, revision 3
  10- SDK Platform Android 3.0, API 11, revision 2
  11- SDK Platform Android 2.3.3, API 10, revision 2
  12- SDK Platform Android 2.2, API 8, revision 3
  13- SDK Platform Android 2.1, API 7, revision 3
  14- SDK Platform Android 1.6, API 4, revision 3
  15- SDK Platform Android 1.5, API 3, revision 4
  16- Android Wear ARM EABI v7a System Image, Android API 19, revision 1
  17- ARM EABI v7a System Image, Android API 19, revision 2
  18- Intel x86 Atom System Image, Android API 19, revision 2
  19- Google APIs (x86 System Image), Android API 19, revision 2
  20- Google APIs (ARM System Image), Android API 19, revision 3
  21- Google APIs, Android API 18, revision 3
  22- Google APIs, Android API 17, revision 3
  23- Google APIs, Android API 16, revision 3
  24- Google APIs, Android API 15, revision 2
  25- Glass Development Kit Sneak Peek, Android API 15, revision 2
  26- Google APIs, Android API 14, revision 2
  27- Google APIs, Android API 13, revision 1
  28- Google TV Addon, Android API 13, revision 1
  29- Google APIs, Android API 12, revision 1
  30- Google APIs, Android API 11, revision 1
  31- Google APIs, Android API 10, revision 2
  32- Google APIs, Android API 8, revision 2
  33- Google APIs, Android API 7, revision 1
  34- Google APIs, Android API 4, revision 2
  35- Google APIs, Android API 3, revision 3
  36- Android Support Repository, revision 4
  37- Google Analytics App Tracking SDK, revision 3
  38- Google Play services for Froyo, revision 12
  39- Google Play services, revision 15
  40- Google Repository, revision 6
  41- Google Play APK Expansion Library, revision 3
  42- Google Play Billing Library, revision 5
  43- Google Play Licensing Library, revision 2
  44- Google Web Driver, revision 2
  45- Intel x86 Emulator Accelerator (HAXM), revision 3

上記のようにリスト表示されるので、自分のインストールしたいパッケージの番号を確認する。 上記の例だとSDK Platform Android 4.0, API 14, revision 3をインストールしたい場合には7となる。

メモが終わったら、以下のように–filterオプションを付与して、updateを実施することで特定のパッケージのみインストールできる。

$android update sdk --no-ui --filter 7

なお、–filterオプションを指定しないと全てのパッケージをダウンロードするため、かなり時間がかかる。

参考

Updating SDK from command-line

2014/6/9追記

以下のような記述でもインストール可能でした。こちらだとリストを調べなくてもよいのでより便利ですね。

$android update sdk --no-ui --filter android-10