スマホアプリでAWS EC2インスタンスを制御してみた

スマホからAWS EC2インスタンスを制御するとき、公式アプリである「AWS Console」を使うのが一般的かと思います。
現在のEC2インスタンスの詳細な状態を見ることができますし、起動や停止も行うことができます。

ただ、個人的にはAWS Consoleは機能が多すぎると思っています。
私の考える理想のアプリは下記の通りです。

  • アプリを立ち上げたらすぐに現在のEC2インスタンスの状態が見られる
  • ワンタップで各EC2インスタンスの起動や停止ができる

「ないのなら作ってしまえばいい」ということで、パパッと作ってみました。(Android用)

大雑把な仕様

仕様は下記の通りです。

  • EC2インスタンスの情報を取得してListViewで表示
  • タップすると当該EC2インスタンスを起動・停止(トグル動作)

EC2インスタンスの操作

以前、「AWS SDK for Go V2」を使ってGo言語でコマンドラインツールを作成しました。

関連記事

AWS EC2インスタンスをいじる際、AWSコンソールを使うことが多いかと思います。 しかし、起動中/停止中の確認や、起動/停止をするためだけにAWSコンソールにアクセスするのもそれはそれで手間がかかります。 AWS CLIを使えば[…]

このコマンドラインツールをREST API化しました。
Go言語はhttpパッケージを使って簡単にWebサーバーを立ち上げることができます。
あるポートで待ち受けておいて、リソースに応じてaws ec2コマンドを叩いて結果をJSONで返すようにしました。

Nginxを通常のwebサーバー+リバースプロキシとして動かしておいて、特定のエンドポイントの時にGo言語の方にリクエストを渡すようにしました。

レスポンス例

[{"reservationId":"r-0b8xxxxxxxxxxxxxx","instanceStatus":[{"instanceId":"i-03axxxxxxxxxxxxxx","status":"stopped","name":"AlmaLinux OS 8.4 x86_64-stage2-c076b20a-2305-4771-823f-944909847a05","description":"Official AlmaLinux OS 8.4 x86_64 image"}]},{"reservationId":"r-0fcxxxxxxxxxxxxxx","instanceStatus":[{"instanceId":"i-083xxxxxxxxxxxxxx","status":"stopped","name":"CentOS Stream 8 x86_64","description":"CentOS Stream 8 x86_64"}]}]

アプリの処理

アプリは上記のエンドポイントを叩いて、結果をListViewで表示しているだけです。
アプリ側でEC2インスタンスの操作は一切行っていません。

現在の状態に応じてTextViewの文字色を変えたかったのでSimpleAdapterをコピペして独自adapterを定義しました。

public InstanceAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) {
    stopString = context.getString(R.string.status_stop);
    startString = context.getString(R.string.status_start);
    mData = data;
    mResource = resource;
    mFrom = from;
    mTo = to;
    mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

@Override
public int getCount() {
    return mData.size();
}

@Override
public Object getItem(int position) {
    return mData.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    return createViewFromResource(mInflater, position, convertView, parent, mResource);
}

private View createViewFromResource(LayoutInflater inflater, int position, View convertView, ViewGroup parent, int resource) {
    View v;

    if (convertView == null) {
        v = inflater.inflate(resource, parent, false);
    } else {
        v = convertView;
    }

    bindView(position, v);

    return v;
}

private void bindView(int position, View view) {
    final Map dataSet = mData.get(position);
    if (dataSet == null) {
        return;
    }

    final String[] from = mFrom;
    final int[] to = mTo;
    final int count = to.length;

    for (int i = 0; i < count; i++) {
        final TextView v = view.findViewById(to[i]);
        if (v != null) {
            final Object data = dataSet.get(from[i]);
            String text = data == null ? "" : data.toString();
            if (text == null) {
                text = "";
            }

            if (i == 4) {
                if (text.equals(stopString)) {
                    v.setTextColor(Color.RED);
                } else if (text.equals(startString)) {
                    v.setTextColor(Color.GREEN);
                } else {
                    v.setTextColor(Color.YELLOW);
                }
            }

            v.setText(text);
        }
    }
}

リストには載っていませんが、クラス変数もSimpleAdapterと同じものを定義しました。
継承元はBaseAdapterにしましたが、もしかしたらSimpleAdapterを継承すればもっと簡単だったのかも…。

とりあえず動けばいい、って感じで組んだのでいろいろ見苦しい点はありますがご容赦ください。

画面

状態表示

状態表示

現在のEC2インスタンスの状態が表示されます。

状態変更

状態変更

各EC2インスタンスのセルをタップすると確認メッセージが表示されます。
「はい」をタップすることで起動中であれば停止、停止中であれば起動します。


まとめ

実はListViewで文字色を変えたのは今回が初めてだったんですけど、これが一番面倒でした。
でもadapterの作り方がおぼろげながら分かったのでよかったと思います。

必要な機能だけが実装された、使いやすいアプリが作れたと自分では思っています。
画面のデザインセンスがもうちょっとあればもっといいんでしょうけど…。