ページ

2016年12月17日土曜日

DockerのテストをServerSpec+CircleCIでやる



Dockerfileで作成したimageをテストしてみたいと思います。
ServerSpecとCircleCIを使います。
サンプルとして作成するimageはAndroidがビルドできる環境のimageを作成します。

最終的な構成


↓の構成でリポジトリを作成します。
.
├── circle.yml
├── run-tests.sh   # 各imageのテストを実行
├── android        # image毎にサブディレクトリを作成
│   ├── Dockerfile
│   ├── docker-build.sh # docker build用のスクリプト
│   └── spec
│       ├── Gemfile
│       └── spec
│           ├── android_docker_spec.rb
│           └── spec_helper.rb
├── xxxxxxx
│
リポジトリのRootにcircle.ymlがあり、各image毎にサブディレクトリを
作成し、テストの時はリポジトリ配下にある全imageをテストします。

Androidビルド環境image


まずはAndroidビルド環境用のDockerfileを作成
# Image for android dev.
#
FROM java:openjdk-8-jdk
ENV HOME_DIR /usr/local
WORKDIR $HOME_DIR

# Update packages.
RUN apt-get update && apt-get -y install sudo

# Install wget for download android sdk.
RUN sudo apt-get -y install wget

RUN sudo apt-get -y install lib32stdc++6 lib32z1

# Download android sdk.
RUN wget http://dl.google.com/android/android-sdk_r24.4.1-linux.tgz
RUN tar zxvfo android-sdk_r24.4.1-linux.tgz
RUN rm -rf android-sdk_r24.4.1-linux.tgz

# Environment variables
ENV ANDROID_HOME /usr/local/android-sdk-linux
ENV PATH $ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$PATH

RUN echo y | android update sdk --no-ui --all --filter "android-23,build-tools-25.0.0"
RUN echo y | android update sdk --no-ui --all --filter "extra-android-m2repository"
やってる事はAndroid SDKをダウンロードし、ANDROID_HOMEの環境変数を設定
最後にplatformとbuild-tools、supportライブラリをダウンロードしています。
ビルド用のスクリプトを用意しておきます。
#!/usr/bin/env bash
# Build docker image for Circle CI

docker build -t slowhand/android:1.0 .
imageの名前は好きな名前で。
試しにスクリプトを実行させてみてimageができていればOKです。

Spec


ここからテストを作成していきます。
今回はserverspecを使うのでGemfileを用意します。
source "https://rubygems.org"

gem "docker-api"

group :test do
  gem 'specinfra', '2.12.7'
  gem 'serverspec'
end
serverspecでDockerを扱うdocker-apiも入れておきます。
次にspec_helper.rbと実際のテストコードを記述するandroid_docker_spec.rbです
  • spec_helper.rb
    # encoding: utf-8
    require 'docker'
    require 'serverspec'
    
    set :backend, :docker
    set :docker_url, ENV['DOCKER_HOST']
    set :docker_image, 'slowhand/android:1.0'
    
    Excon.defaults[:ssl_verify_peer] = false
  • android_docker_spec.rb
    # encoding: utf-8
    require 'spec_helper'
    
    if ENV['CIRCLECI']
      class Docker::Container
        def remove(options={}); end
        alias_method :delete, :remove
      end
    end
    
    %w(wget lib32stdc++6 lib32z1).each do |pkg|
      describe package(pkg) do
        it { should be_installed }
      end
    end
    
    describe file('/usr/local/android-sdk-linux') do
      it { should be_directory }
    end
    
    describe command('env') do
      its(:stdout) { should match /ANDROID_HOME/ }
    end
    ↑の通りですが、 wget lib32stdc++6 lib32z1のインストール確認と
    $ANDROID_HOMEのパスと環境変数の確認をテストしています。

CircleCI


最後にcircle.ymlとテストを起動させるスクリプトです。
  • circle.yml
    machine:
      services:
        - docker
    
    dependencies:
      override:
        - docker info
    
    test:
      override:
        - ./run-tests.sh
    ↑ではテスト時にrun-tests.shを起動するだけのシンプルなものです。
  • run-tests.sh
    #!/usr/bin/env bash
    
    WORKDIR=$PWD
    
    # Find target directory.
    for entity in `find . -type d -mindepth 1 -maxdepth 1 -not -name ".git"`; do
      # Only exist spec directory.
      if [ -e $entity/spec ]; then
        # Build docker image.
        cd $entity
        ./docker-build.sh
    
        cd spec
        # Install gems via bundler.
        bundle install --path vendor/bundle
        bundle exec rspec
        cd $WORKDIR
      fi
    done
    少し込み入ってますが、image用のサブディレクトリ内にspecディレクトリがあれば
    中に入ってテストを実行しています。

ここまでくれば、あとはpushすればCIが動作しテストを行ってくれます。
こちらで公開してますので宜しかったらどうぞ。

2016年12月4日日曜日

Android画像ライブラリ-Picasso-を試す


今回はPicassoを試してみたいと思います。
画像のダウンロード先はDocker+Rackで画像サーバーを作成(ただやりたいだけ)
Picassoとは?
Androidで画像のダウンロードやキャッシュをいい感じにやってくれるライブラリ

画像サーバー


わざわざDocker+Rackで作成しなくてもいいとは思いますが、、💦
試しにやってみました。
  • Dockerfile
    FROM ruby:2.3.0
    ENV LANG C.UTF-8
    ENV HOME_DIR /usr/src/app
    WORKDIR $HOME_DIR
    
    RUN apt-get update -qq
    
    COPY Gemfile $HOME_DIR/
    
    RUN bundle install
    ADD . $HOME_DIR
    
  • Gemfile
    source 'https://rubygems.org'
    
    gem 'rack'
    
docker-cpmposeも使ってみます。
  • docker-cpmpose.yml
    version: '2'
    services:
      web:
        build: .
        command: rackup -p 9292 -o 0.0.0.0
        environment:
          RAILS_ENV: development
        ports:
          - '9292:9292'
        volumes:
          - .:/usr/src/app
    
  • config.ru
    # encoding: utf-8
    run Rack::Directory.new(File.dirname(__FILE__) + "/public")
    
    public配下をhttp://localhost:9292/でアクセスできるように公開してます。
画像はDockerfileと同じディレクトリ内に以下パスで画像を置きました
public/images/sample.jpg
早速実行
$ docker-cpmpose up -d
ブラウザでhttp://localhost:9292/images/sample.jpgにアクセスし画像が表示されたら成功です。
停止はdocker-compose stop

Androidアプリ作成


ようやくのアプリ作成です💧
まずはプロジェクトのbuild.gradleに以下を追加
android {
    ・・・
    dataBinding {
        enabled = true
    }
}

dependencies {
    ・・・
    compile 'com.squareup.picasso:picasso:2.5.2'
}
ついでにdatabindingも有効にしときます。
次にレイアウトファイルの修正
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" >

    <RelativeLayout
      ・・・ >

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button"
            android:layout_alignParentBottom="true"
            android:layout_alignParentEnd="true" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentStart="true"
            android:id="@+id/imageView"
            android:layout_above="@+id/button"
            android:layout_alignParentEnd="true"
            android:layout_alignParentTop="true" />
    </RelativeLayout>
</layout>
そしてActivity内でボタンが押されたら「画像をダウンロードしてImageViewに設定する」
という処理を実装して行きます。
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    Button button = mBinding.button;

    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          ImageView imageView = mBinding.imageView;
          Picasso.with(MainActivity.this)
                 .load("http://xxxx/images/sample.jpg")
                 .into(imageView);
        }
    });
}
↓実際にPicassoを使っている所ですがメソッドチェーンで分かり易いです
Picasso.with(MainActivity.this)
       .load("http://xxxx/images/sample.jpg")
       .into(imageView);
※ 自分の環境ではDocker for Mac + Android Emulatorで動作させようとしたらEmulatorが動作しなかったので
以前使ったngrokを使ってlocalhostを外部公開して使ってます。
  • 動作
gif
画像は宮島の鹿ですw
Picassoではその他リサイズ回転等色々できるようです。
Picasso.with(MainActivity.this)
       .load("http://xxxx/images/sample.jpg")
       .resize(50, 50) // リサイズ
       .rotate(90.0f)  // 回転
       .error(R.drawable.hoge) // エラー時の画像
       .into(imageView);