2020年7月7日火曜日

AWS CDKで立てたEC2インスタンスのTimeZoneとかいじりたかった話

EC2を立てることはできたけど、立てたインスタンスは UTCのままだし設定ファイルとかいちいちscpしてくるのはダルい。

当初UserDataでなんとかしようとしたものの、「書く量がヤバいしメンテしにくい」と悩んでいたところ見かけたのが AWS::CloudFormation::Init 
調べたら YAMLやJSONでの書き方ばかりだったけど、CDKも結局最後は CloudFormationになるはず!とツッコんでみた。

■ どんなことができるのか


使った機能だけだけど

package    : yum install できるものはここで宣言しておくのが楽っぽい。
files          : その名の通りファイルの配置。インスタンス作った直後にscpで送るのか…とか頭抱えてた課題の救世主。
command : その名の通りコマンド実行。ただし実行ユーザーはrootユーザーの模様。

サーバ作った後、最初にやりたいセットアップまでコード化できるのは色々嬉しい。(最悪 README.mdにインスタンス立てた後のコマンドつらつら書くことを覚悟してた。)

■ どんな風に書くのか

 instance.addOverride('Metadata', {
  'AWS::CloudFormation::Init': {
    'config': {
      'packages': {
        'yum': {
          'git': []
        }
      },
      'files': {
        '/home/ec2-user/.ssh/config': {
          'content': fs.readFileSync(path.resolve(__dirname, "../files/config")).toString(),
          'mode': '600',
          'owner': 'ec2-user',
          'group': 'ec2-user'
        },
      },
      'commands': {
        '01_set_timezone': {
          'command': 'timedatectl set-timezone Asia/Tokyo',
          'ignoreErrors': false
        }
      },
    }
  }
});

let userData = ec2.UserData.forLinux({ shebang: "#!/bin/bash" });
userData.addCommands(
  `/opt/aws/bin/cfn-init --region ${this.region} --stack ${this.stackName} --resource ${instance.logicalId}`
);
instance.userData = cdk.Fn.base64(userData.render());

前回 のものに追加した。
config の子要素としてそれぞれやりたいことを書いていく感じ。

filesは当然権限やファイルのowner:group なんかを設定できる。
コード化しているので配置したいファイルそのままを fs.readFileSync().toString() してしまえば、ファイルの中身をベタ書きせずに済むのも嬉しい。(【追記】 ただしこの場合、readFileSyncするfileの中身が空だと実行時にエラーになる…(´・ω・`))

commandsには「このコマンド実行の失敗を無視するか」を設定できる。
手作業でリカバーできる内容なら無視してしまってもよい?
また commandsは(今回は一つしか無いけど) key名(例だと 01_set_timezone)のソート昇順で実行するらしい。
ので今回の様にprefix的に番号でも付けておくと実行順の管理もしやすそう。

なおcommands自体の実行前に packages、filesの処理は終わってる模様。
なので「filesでshellファイルを配置し、commandsでそれを実行する」なんてこともできる。(この後さらにコマンドを追加しようと思ったら実行ユーザーの関係でどうしてもうまく行かなかったので、妥協して上記の形にしたのは内緒)

ただ最終的には UserDataから cfn-init を実行する。 オプションは無いと動いてくれないので忘れずに。

■ ついでに

これ作ってて一番アホだったミスが「コードは書き換えたが生成物に反映されていなかった」問題。「あれれ〜?変更されないぞ〜?」とかアホ面下げて調査してたらよく見たらFormationの更新がされていないというオチ。
なので必ず clean build( tcs --build --clean ) をdeploy前に実施するようにした。

…ホントこれのせいで一日潰したことに気づいた時の脱力感よ…(´・ω・`)

■ 不満点


ここまで「スゴイベンリ!」「ヤッター!」と来たものの、ここに至るまでに何度インスタンスを立てたか…
もっとこう、テストがしたいです…

ちなみに /var/log/cfn-init.log に実行ログが出るので、「なんかコマンド効いてなくない?」と思ったらここを確認すると幸せになれるかも




2020年7月1日水曜日

AWS CDKでサクッとEC2インスタンスを立てたかった話

前回 ひとまずcdk の initまで完了してプロジェクトの用意ができたので、次は実際に構成したい内容を書いていく。

■ 最初の要件


今回立てたいEC2インスタンスの要件はこんな感じ。
  • NodeJSのツール(インターネットアクセス有り)を実行できる。
  • TimeZoneは JST
  • sshで入る(=22ポートを開けたい)
ひとまずこんなところ。他は随時拡張したくなったら。

■ 作るもの


今回明示的に用意するものは以下
  • VPC(CIDERは適当に既存のものに被らないものを)
  • Security-Group(22ポートだけひとまずフルオープンしておく)
  • EC2インスタンス(t2.small)
一旦まずはTimeZoneなどは置いておいてインスタンスにsshできればいいや、程度から作る。

■ コード

さっくり
    const prefix = 'Sample';

    // VPC 
    const vpc = new ec2.Vpc(this, `${prefix}Vpc`, {
      cidr: 'x.x.x.x/y', // 空いているところを適当に使用
      natGateways: 0, 
      subnetConfiguration: [
        { name: `${prefix}PublicSubnet`, cidrMask: 20, subnetType: ec2.SubnetType.PUBLIC }
      ]
    });
    
    // SecurityGroup
    const instanceSecurityGroup = new ec2.SecurityGroup(
      this,
      `${prefix}SecurityGroup`,
      {
        securityGroupName: `${prefix}SecurityGroup`,
        vpc: vpc
      }
    );
    
    instanceSecurityGroup.node.applyAspect(
      new cdk.Tag("Name", prefix)
    );
    
    instanceSecurityGroup.addIngressRule(
      ec2.Peer.anyIpv4(),
      ec2.Port.tcp(22)
    );
    
    // EC2
    const instance = new ec2.CfnInstance(this, `${prefix}Instance`, {
      imageId: new ec2.AmazonLinuxImage(
        {generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2}
      ).getImage(this).imageId,
      instanceType: new ec2.InstanceType("t2.small").toString(),
      keyName: "sample-key", // 鍵は別途作ってあるものを使う
      networkInterfaces: [{
        associatePublicIpAddress: true,
        deviceIndex: "0",
        groupSet: [instanceSecurityGroup.securityGroupId],
        subnetId: vpc.publicSubnets[0].subnetId
      }],
      tags: [
        {"key": "Name", "value": `${prefix}-A01`},
      ]
    });
  
主に悩んだのは「Tagの宣言の仕方色々あるんやなぁ(´・ω・`)」というくらい。
あとはどちらかと言うと AWSの知識(どんな設定項目が必要なのか)の方が大事でコード的には難しいことはなにもない。
このあたりは実際にAWSコンソールから対象をまず手作業で作ってみて、「どんな設定、入力項目があるのか」を見てきた方が早いし納得しやすいかと。

■ この後やりたいこと


このままだとサーバのTimeZoneが UTC のままなのでJSTにしたい。
ついでに設定ファイルとかわざわざ scpしたりするのめんどい。

ので AWS::CloudFormation::Init を設定して楽したい。

というのは長くなったので次で。

2020年6月24日水曜日

AWS CDKを使いたくなった話

めちゃめちゃ久々に書いてみようとしたらなんか色々死んでた orz

お仕事が平気で徹夜カマしたり同じことばっかやって書くネタなくなったり気力続かなかったりグラブったりといろいろ理由付けてサボってたらいつの間にかインフラ構築とかもやるようになったので備忘録φ(._. )メモメモ

■ まず AWS CDKってなんやねん


AWS CDK とは 「AWS Cloud Development Kit」の略で、
ざっくり言えば 【CloudFormationビルドツール】的なもの。
これまで(と言ってもCDKが出たのは結構前だが)は一生懸命 YAMLやJsonでつらつら長々と書いていたCloudFormationを
さっくりコードから生成してしまおう、というもの。
使える言語もこの記事を書いている時点では 
「The AWS CDK supports TypeScript, JavaScript, Python, Java, and C#/.Net.」
と人気どころは押さえてあるんじゃないですかね知らんけど。

自分はずっとJavaScript(NodeJS)ばっか触ってたのでJavaScript…ではなくTypeScriptメインでいじってます。タイプセーフって安心する。
ので NodeJS-TypeScript環境でつらつら書いていく。


■ セットアップ


あらかじめNodeをインストールしておき、かつ
npm i -g aws-cdk
して cdk コマンドを使えるようにしておく。
あとは
mkdir sample 
cd sample
cdk init --language typescript
とプロジェクト用のディレクトリを切ってinit コマンドを実行すれば 必要な一式を自動生成してくれる。
なお「cdkをグローバルインストールしたくない」と予め
npm init -y 
npm i -S aws-cdk
npx cdk init --language typescript
とやろうとすると、
`cdk init` cannot be run in a non-empty directory!
と怒られるので諦めた(一敗)。

あとは個人的なカスタムとして、 package.jsonの scriptsに
    "deploy": "npm run build & npx cdk deploy"
を追加。一応READMEにコマンドが色々載ってるが、
  • buildとdeployでコマンドが違う
  • build忘れてdeployする(というかして一日ほど無駄にした)
  • cdkのバージョンはglobalではなくプロダクトのものに合わせておきたい
等の理由からdeployスクリプトを用意しておきたかった。

|・ω・).。o0( なんか長くなってきたから一旦ここで切る )

2015年12月9日水曜日

javascriptのcanvas作成でやったくだらないミス

うわ
しばらくサボってたら2015終わるやん(´・ω・`)

タイトル通りなんてことはない話
通常javascriptでcanvasを描画する際は

    var canvas = document.createElement('canvas');
    canvas.width = 100;
    canvas.height = 100;
    var context = canvas.getContext('2d');
    context.font = "30px 'MS ゴシック'";
    context.textAlign = "left";
    context.textBaseline = "top";
    context.fillText("test", 0, 0);
    document.getElementById('canvas_target').appendChild(canvas);
単純にテキストを描画するだけのcanvas作成だが、
この width,heightは「数値で指定すること」というのがすっぽり抜け落ちてた、というだけの話。

なにをやったかというとオープンソースのライブラリをいじってた時、よくあるオプション設定中で
width: "480px"
とかやってるパラメータから流用しようと一生懸命引っ張ってきて設定しようとして
「あれれ〜?なんか設定されずにwidthが0になるぞ〜?(´・ω・`)」
とかアホ晒してたというだけ。
style属性に入るならよしなにやってくれるのに、直接width属性に当て込まれるせいかね…


久しぶりにjs触った結果起きたポカを晒して、ちゃんと書く習慣を取り戻したいというのが真の狙いだったりする(´・ω・`)


2014年4月25日金曜日

mavenを使ってpomからプロジェクトを作る in Eclipse

そういえばやったこと無かったので('A`)

mavenを使ってpomからプロジェクトを作る

1. mavenのインストール

こちら からファイルをもらってくる。 とりあえず Binary Zip を取得。
展開後、任意の場所に解凍でできたフォルダを格納。パスを通すのでわかりやすいところがよろしいかと。(自分はC: 直下に mvn フォルダを作成し、その下に格納)

環境変数を開き、
変数名: Path
変数: C:\mvn\apache-maven-3.2.1\bin;%Path%
※ ver 3.2.1の自分の環境の場合。もちろん格納場所が変わればパス部も変わるが、 
・ binまで
・ 後に ;%Path%
を付ける点に注意

で設定してやる。

適当な場所でコマンドプロンプトを開き、 
mvn --version
で確認するのが王道か。

2. プロジェクトの作成

mvnの準備ができたら、pom.xmlのあるフォルダでコマンドプロンプトを開き、
mvn eclipse:eclipse
と入力。するとあら不思議。ずらずらとコマンドが走り、
BUILD SUCCESS
のテキストが表示されたら完了。
もう一度フォルダの中を見ると、.classpasthなどそれっぽいファイルが生成されているはず。

あとはeclipseを開き、 import → Existing Project into Workspace で
上記フォルダを選択 → import
すれば完了。


簡単だけど備忘録的な意味で('A`)

2014年3月31日月曜日

poiを使ってみた

気が付きゃ3月も終わりだ('A`)

poiを使ってみた

javaで走らせたり、というかSeleniumでのテスト時に一緒にデータを取って、その結果がExcelとかに出力できたらなー、とか考えたのが始まり

1. poi

ここからダウンロードできるライブラリの一種。簡単に言うと、「Microsoft Documentsにアクセスしやすくするライブラリ」 とのこと。 今回はそのうちの一つであるExcelへのアクセスを手伝ってもらう。
今回は poi-bin-3.10-FINAL-20140208.tar.gz を使用。

2. 環境設定

とりあえずよくわからないので、 
docs以下
LICENCE
NOTICE
以外は全部プロジェクトに放り込んだうえでパスを通した。 examplesとかいるのかなぁ…

3. WorkBookの作成

一つずつ順に。WorkBookはその名の通り作業する場所。ここにExcelファイルの1シートをコピーして作業する。 あくまでコピーなので、最後に書き込まないとExcelファイル自体は更新、書き込みされない点に注意。
    try {

      /* エクセルファイルはどこですか */
      File f = new File(filepath);
      InputStream is = new FileInputStream(f);

      /* ワークブックにアクセス */
      XSSFWorkbook wb = new XSSFWorkbook(is);

    } catch (FileNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
filepathはExcelファイルの在り処。「ファイルが無ければ作る」処理を追加してもいいかも。 WorkBookの作成時には2つもExceptionが発生するので、きちんと捕まえるなど対処が必要。

4. シート、行、列からセルの指定

WorkBookにシートを読み込ませて、徐々に絞っていく感じ。
    try {

      /* エクセルファイルはどこですか */
      File f = new File(filepath);
      InputStream is = new FileInputStream(f);

      /* ワークブックにアクセス */
      XSSFWorkbook wb = new XSSFWorkbook(is);

      /* シートの指定 */
      XSSFSheet sheet = wb.getSheet(sheetname);

      /* シートの定義の最初の行 */
      int firstRow = sheet.getFirstRowNum() + 1;

      /* シートの定義の最終行 */
      int lastRow = sheet.getLastRowNum();

      /* 行を順に上から見ていく。 */
      for (int i = firstRow; i <= lastRow; i++) {

        /* 行情報を取得 */
        XSSFRow row = sheet.getRow(i);

        /* 列の読み込み。 とりあえず 1 で */
        XSSFCell cell = row.getCell(columnno);

      }

    } catch (FileNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
sheetnameとcolumnnoは別途指定。
sheetnameはそのままシートにつける名前。デフォルトは【Sheet1】とかなってるアレ。実はintで順番指定も可能らしい(未検証)。
columnnoは列の番号。 A列が0、B列が1、といった順で対応しているらしい。

作業の順番は、
シートの指定 → 行の指定 → 列の指定
行の指定の際に、シート中にて、【何かしらが列のどこかに記入されている行】の行数が取得できる。今回は事前に項番を振っておいたExcelを読み込んでいるため、項番の分だけ回ってくれる寸法。 【最初の行】の指定の際 +1 しているのは、各列のラベルを設定した結果、その行まで含めて【最初の行】とされてしまったのか、うまくいかなかったため。

もちろん行数をピンポイントに指定してやることも可能。

5. セルの内容の確認

既に記入されていないか確認。
 /* チェック。 いろいろステータスのサポートがあるらしい */
        if (cell != null && cell.getCellType() == Cell.CELL_TYPE_STRING) {

          /* セルの内容がStringなら */
          String cellval = cell.getStringCellValue();

          /* 取得してみる */
          System.out.println(cellval);

        } else if (cell == null || cell.getCellType() == Cell.CELL_TYPE_BLANK) {

          /* ブランクなら */
          System.out.println("からっぽ(・д・`)");
        }
セルの読み込み直後から
Cell.CELL_TYPE_STRING はいわゆるセルの型。数字や日付なんかの指定もあり、読み込み時などに型のチェックができる。「数値が取り出せた場合のみ」なんて条件も可能。

6. 書き込み

実際に書き込む。

/* 指定された列番号のセルを準備する */
    XSSFCell cell = row.getCell(columnNum, Row.CREATE_NULL_AS_BLANK);

    /* 値を適当に書き込む */
    /* TODO Stringじゃなくて数字でよくないかなこれ */
    cell.setCellValue(String.format(" %1$d", 12345));
少し巻き戻って、指定の行から書き込むセルの指定のし直し。columnNumはまた別の列番号。
Row.CREATE_NULL_AS_BLANK は【空のセルとする】的な宣言...?('A`)
setCellValueで値を書き込む。TODOにある通り、なぜ数値をStringで書き込んでいるかは謎(他のとこからコピペしてきたからなんだがな…('A`))

7. Excelに書き込み

先述の通り、ここまでは WorkBook上での出来事。きちんとExcelファイルに放り込んでやらないと更新されない。

      /* 最後にワークブックをファイルに書き込み */
      OutputStream os = new FileOutputStream(f);
      wb.write(os);
これだけ。だけどこれをやらないとExcelファイルには何も起きない…('A`)

8.番外編 -色の指定-

ちょっとだけ戻ってセルの取得あたり。フォントの色やセルの色を付けてみる。
 /* style準備 */
    XSSFCellStyle style = wb.createCellStyle();
    style.setFillPattern(XSSFCellStyle.SOLID_FOREGROUND);

    /* font準備 */
    XSSFFont font = wb.createFont();

    /* 背景色設定 */
    style.setFillForegroundColor(backgroundColor);

    /* フォント設定 */
    font.setColor(fontColor);
    style.setFont(font);

    /* セルにスタイルを反映 */
    cell.setCellStyle(style);
styleがセルの色やら、fontが文字周り。 styleの準備をして、 fontの準備をして、最後にセルにその設定を反映する、という手順。
backgroundColorやfontColorは別宣言。色の指定は HSSFColorクラスが使用可能。たとえば赤であれば、HSSFColor.RED.index で使用できる。
文字の書き込みなどとは別で宣言できるが、当然 7. Excelに書き込み の処理の前にやらないと反映されない。




|ω・`) |Ю .。o0 ( 次は型やら何やらをきれいにしないとな )

2014年2月17日月曜日

AndroidでもSelenium2

|ω・`)ノ |Ю AndroidでもSelenium2が使えると聞いて

AndroidでもSelenium2

うまくいったら万々歳ですよ

1. 環境の作成

まず必要なのが、AndroidにSeleniumServerを用意すること。事前に adbコマンド は打てるようにしておくが吉。adbの入れ方は気が向いたら(PC変えた等)書くかも。

adb -versionでも打ってコマンドが使えることを確認する。

続いてこちらから、
android-server-"ver".apk
をもらう。 例によって "ver"はバージョン。(2014/02/17 時点では 2.21.0)

貰って来たら、こいつを端末にインストールする。
コマンドを開き、
adb -s 入れたい端末ID -e install -r android-server-"ver".apk
コマンドを叩く。apkは貰ったapkの名前。 入れたい端末のID は、別途
adb devices

コマンドを叩けば見られます
入れたい端末のID device
こんな感じで表示されるかと。

2. 準備

インストールが終わったら、今度は下準備。
まずは入れたWebDriverを起動する。
実機上のアイコンをタップするか、コマンドから
adb -s 入れたい端末のID shell am start -a android.intent.action.MAIN -n org.openqa.selenium.android.app/.MainActivity -e debug true
のコマンドを起動。この辺もバッチとかにしても楽かも。これでWebDriverが起動。真っ暗な感じの表示になるはず。
続いてポートフォワード設定(というらしい)。要は接続?
コマンドから、
adb -s 入れたい端末のID forward tcp:8080 tcp:8080
を入力。
ここまでやって端末側の準備が完了。

ちなみにこのWebDriver、 Android標準ブラウザ でのシミュレータになる(らしい)。 Chromeブラウザとかはどうやるんだろ…

3. テストコード用意

明らかに順番おかしい気がするけど。 JUnitが使えるので、今までのテストも使える。
ポイントは WebDriverの宣言。通常Chromeでテストする場合、
WebDriver driver = new ChromeDriver();
と宣言するところを、
WebDriver driver = new AndroidDriver();
と宣言する。

これで動きがみられるはず。
こいつをEclipseからでも実行してやると、実機上で動きが見られ、かつテスト結果はEclipseの方に出てくる。
デザインのチェックなどは厳しいけど、機能チェック、特に登録系に対してAndroidのキーボードでいちいち入力する手間が省けると、工数がぐっと減る気がする。

問題は、「バックキー」 が使えないこと。 使うと WebDriver自体が終了します。






うん。Androidでやる意味が薄いな…('A`)
PCでええやん…


4. おまけ ~WebDriverでUA偽装

正直UA偽装でもいい気がしたので。
今回はChrome用。たぶん他にもあるかと

    /* クローム用オプション。 UA偽装しないと見れない時に */
    ChromeOptions options = new ChromeOptions();

    /* UA偽装 to iPhone */
    options.addArguments("--user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25");

    /* Chromeドライバのインスタンス。オプションセット版 */
    driver = new ChromeDriver(options);

要するにオプションで設定できますよ、ってお話。UAはぐーぐる先生に聞いてみるといろいろ出てくるので、お好きなものをセットしてみましょう。




正直これでいい気がした(・ω・`)

AWS CDKで立てたEC2インスタンスのTimeZoneとかいじりたかった話

EC2を立てることはできたけど、立てたインスタンスは UTCのままだし設定ファイルとかいちいちscpしてくるのはダルい。 当初UserDataでなんとかしようとしたものの、「書く量がヤバいしメンテしにくい」と悩んでいたところ見かけたのが  AWS::CloudFormation:...