Terraformでランダムな文字列を生成したい

今日は小ネタ。

Terraformで何かのリソースを作ろうとしたとき、ランダムな文字列を生成したいということがよくあります。例えばAzureのストレージアカウント名はグローバルで一意である必要があるのですが、それを生成するのはちょっと面倒くさいです。

docs.microsoft.com

何かいい方法ないかなと思ったら、random providerのrandom_stringリソースを見つけました。

registry.terraform.io

使い方は上記のページを参照すれば概ね理解できるかなと思います。

lengthで文字数を設定し、大文字・小文字・記号などを使うか使わないかを設定します。こんな感じで記述します(4文字で小文字を使う設定。デフォルトで数字(numeric)も使う)。

resource "random_string" "name" {
  length  = 4
  upper   = false
  lower   = true
  special = false
}

生成された値は、random_string.name.resultで得られます。

同様にランダムなパスワードを生成するrandom_passwordやランダムな数値を取得するrandom_integerなどがあるようです。

registry.terraform.io random_passwordのマニュアル

registry.terraform.io random_integerのマニュアル

Azure Container InstanceでAzure Database for PostgreSQL flexible serverに対してpg_dumpを実行してみる

はじめに

Azure Database for PostgreSQL flexible serverを使っているとき、用意されているバックアップ機能は最大35日しか取れず、あとは手動で実行しなければなりません。

docs.microsoft.com

バックアップのために仮想マシンを立てるのもなあ...と思い、Azure Container InstanceでPostgreSQLのバックアップコマンドであるpg_dumpが実行できないか試してみました。なお、この内容は不完全版です。

pg_dumpコマンド

PostgreSQLのバックアップを取るpg_dumpコマンドをドキュメントで確認します。

www.postgresql.jp

-hオプションでホスト名、-Uでユーザー名、-dでデータベース名、-fで出力ファイル名を指定できます。実際には以下のようなコマンドになるでしょう。

$ pg_dump -h xxxxxxxxxx.postgres.database.azure.com -U ユーザー名 -d データベース名 -f /home/azureuser/pg_dump001
Password:
$

パスワードを入力しなければいけないので、バッチ的な使い方をするには一工夫必要です。色々と調べると、環境変数PGPASSWORDに値を設定すれば良いようです。

www.postgresql.jp

ただし、この環境変数はセキュリティ上非推奨のようです。が、今回はこの環境変数を使ってみることにします。

Dockerイメージ

DockerイメージはDocker Official Imageのpostgresを使い、コマンドでpg_dumpを実行してみます。

hub.docker.com

実行

Azure Container Instanceで各種環境変数値を設定して実行します。環境変数の設定は「安全としてマーク」に「はい」を設定しておくと良いかと思います。

あとはAzure Container Instanceで設定して実行します。細かい設定は以下の感じで。

今回は確認のためファイルに出力はせず標準出力に出してみることにします。

無事実行することができているようです。

あとはこれをファイル出力したいのですがPortalでお気軽に設定できなかったので、次週にということで。

Apple Silicon(M2 MacBook Air)でAzure Functions Core Tools+Java言語でうまく動かなかった(未解決)

はじめに

先日、M2 MacBook Airを買い、色々とセットアップしました。 Apple Sillicon搭載マシンがでたのが2020年11月で、1年半以上経っていることもあって多くのツールはApple Silliconに対応していましたが、Azure Functionsのローカル実行用として使われるAzure Functions Core ToolsJavaではまだ未対応ということではまったので、ここでその内容を記します。

サンプル

Durable Functionsの動作検証のため、Quick Startにあるコードを実行しようとしました。すると、以下のエラーメッセージが出て起動できませんでした。

Azure Functions Core Tools
Core Tools Version:       4.0.4736 Commit hash: N/A  (64-bit)
Function Runtime Version: 4.8.1.18957

[2022-08-21T05:41:47.806Z] A host error has occurred during startup operation 'e87c7af2-7243-4df8-b9cd-142cac9bc1fb'.
[2022-08-21T05:41:47.806Z] Grpc.Core: Error loading native library. Not found in any of the possible locations: /Users/miyohide/.azure-functions-core-tools/Functions/ExtensionBundles/Microsoft.Azure.Functions.ExtensionBundle.Preview/4.2.1/bin/libgrpc_csharp_ext.arm64.dylib,/Users/miyohide/.azure-functions-core-tools/Functions/ExtensionBundles/Microsoft.Azure.Functions.ExtensionBundle.Preview/4.2.1/bin/runtimes/osx-arm64/native/libgrpc_csharp_ext.arm64.dylib,/Users/miyohide/.azure-functions-core-tools/Functions/ExtensionBundles/Microsoft.Azure.Functions.ExtensionBundle.Preview/4.2.1/bin/../../runtimes/osx-arm64/native/libgrpc_csharp_ext.arm64.dylib.
Value cannot be null. (Parameter 'provider')

> Task :azureFunctionsRun FAILED

Issue

色々と検索してみると、以下のIssueを見つけました。

github.com

このIssueに書かれているものによると、Javaに関しては以下の記述がありました。

Java: blocked because the Java worker only supports Java 8 & 11 and AFAIK Java itself doesn't support Apple Silicon until at least Java 17

JavaApple Siliconサポートは少なくともJava 17以降ということなので、Java版の直近の対応はちょっと難しそうですね...

やるとすれば、Azure上にWindows/Linux仮想マシンを立てて開発するか、言語を変えるかかと。

Azure SDK for JavaでAzure Container Instanceを操作する

ちょっと機会があってJavaでAzure Container Instanceを操作する実装方法について調査する機会がありました。備忘録としても記しておきます。

SDK

AzureにはSDKが用意されており、プログラミング言語で各サービスを操作することが可能です。用意されているSDKは以下のページに記されています。

azure.microsoft.com

簡単な使い方については、以前ブログも書いていたようです(これを書く上で検索してはじめて思い出しました)。

miyohide.hatenablog.com

今回は、Azure Container Instanceを操作するためには、以下の記述をbuild.gradleに記述しました。

dependencies {
    implementation 'com.azure.resourcemanager:azure-resourcemanager-containerinstance:2.17.0'
    implementation 'com.azure.resourcemanager:azure-resourcemanager:2.17.0'
    implementation 'com.azure:azure-identity:1.5.3'
}

サンプル

SDKAPIを色々と観てもいいんですが、コードサンプルがMicrosoftから公開されているのでそれを参考にします。コードサンプルは、以下のページから検索して探してみます。

docs.microsoft.com

今回は以下のサンプルが見つかったので、このサンプルをもとに実装します。

github.com

実装

実装自体はそんなに難しいものではなく、サンプルを横目に実装できます。細かいメソッドとして、Utils.javaが公開されているのでここから必要なメソッドを抜き出すと便利かなと思います。

github.com

今回は、以下のようなクラスを作り...

package com.github.miyohide;

import com.azure.core.management.Region;
import com.azure.resourcemanager.AzureResourceManager;
import com.azure.resourcemanager.containerinstance.models.ContainerGroup;
import com.azure.resourcemanager.containerinstance.models.ContainerGroupRestartPolicy;
import com.azure.resourcemanager.resources.fluentcore.utils.ResourceManagerUtils;

import java.time.Duration;

public class ContainerInstanceService {
    private String resourceGroupName;
    private Region region;
    private String containerImage;
    private String aciName;

    public ContainerInstanceService(String resourceGroupName, Region region, String containerImage, String aciName) {
        this.resourceGroupName = resourceGroupName;
        this.region = region;
        this.containerImage = containerImage;
        this.aciName = aciName;
    }

    public boolean runContainerInstance(AzureResourceManager azureResourceManager) {
        ContainerGroup containerGroup = azureResourceManager.containerGroups().define(aciName)
                .withRegion(this.region)
                .withNewResourceGroup(this.resourceGroupName)
                .withLinux()
                .withPublicImageRegistryOnly()
                .withoutVolume()
                .defineContainerInstance(aciName)
                .withImage(this.containerImage)
                .withExternalTcpPort(80)
                .attach()
                .withRestartPolicy(ContainerGroupRestartPolicy.NEVER)
                .withDnsPrefix(aciName)
                .create();

        // 起動中
        System.out.println("起動中 ..." + containerGroup.ipAddress());
        Utils.sendGetRequest("http://" + containerGroup.ipAddress());
        ResourceManagerUtils.sleep(Duration.ofSeconds(15));
        System.out.println("CURLing " + containerGroup.ipAddress());
        System.out.println(Utils.sendGetRequest("http://" + containerGroup.ipAddress()));

        return true;
    }
}

上記のクラス・メソッドを以下のように呼び出します。

public class Main {
    public static void main(String[] args) {
        AzureProfile profile = new AzureProfile(AzureEnvironment.AZURE);
        TokenCredential credential = new DefaultAzureCredentialBuilder()
                .authorityHost(profile.getEnvironment().getActiveDirectoryEndpoint())
                .build();
        AzureResourceManager azure = AzureResourceManager
                .authenticate(credential, profile)
                .withDefaultSubscription();
        ContainerInstanceService containerInstanceService
                = new ContainerInstanceService(
                        Utils.randomResourceName(azure, "rgaci", 15),
                Region.JAPAN_EAST,
                "mcr.microsoft.com/azuredocs/aci-helloworld",
                Utils.randomResourceName(azure, "acisample", 20));

        containerInstanceService.runContainerInstance(azure);
    }
}

詳細は末尾のソースを参照してください。

実行

実行すると、ランダムなリソースグループ名とコンテナインスタンス名でAzure Container Instanceがつくられます。

Azure Container Instanceの概要ページ

今回はMicrosoftが公開しているサンプルのイメージであるmcr.microsoft.com/azuredocs/aci-helloworldを使ったので、URLアクセスすると以下の画面が表示されます。

aci-helloworldページ

ちなみにUtils.randomResourceNameの第二引数に指定している長さを短くすると、prefixがつかないことがありました。

実装に使っているメソッドのドキュメントにもprefixの部分にはif possibleと書かれているので、短いとprefixがつかないのでしょう。

docs.microsoft.com

ソース

ここまでのソースです。

github.com

Apple Watch Series 6が突然動かなくなったのでAppleに修理を依頼した

始まりはいつも突然。7月31日(日)にApple Watch Series 6が突然動かなくなりました。ここではその顛末を記します。

その日は7月最終日なので月間チャレンジのアクティビティを達成しようと思い朝も早くから運動しようかなと思った矢先、Apple Watch Series 6が突然「バッテリーが残り10%です」という警告が。「あれ?充電はたっぷりあったような気がするけれど」と思いつつ、充電器に乗せたのですがさっぱり充電されない。画面を触ってもボタンを押しても画面に何もつかない。いよいよ焦り始めました。これが朝8時。

とりあえずまずはAppleのサポートページを確認。

support.apple.com

対処方法が記されているので試してみますが、一向に改善されず。困ったなあと思い、関連情報にあったAppleサポートへの問い合わせを実施しました。

問い合わせは電話で。すぐさま電話がかかってきて担当者さんとお話しすることができました。待つことなくすぐさまサポートに繋がるのは素晴らしい。

あらかじめ問題の切り分けができているということから、Apple正規サービスプロバイダへの持ち込みや配送修理、エクスプレス交換サービスのご案内(AppleCare+特典)をいただきました。Apple KawasakiのGenius Barに空きがたまたまあったことからそこで相談することにし、現地へ。

Apple Kawasakiでは事象を確認していただき、工場での診断・修理対応と判断。AppleCare+に入っていたので料金は0円と見積もられましたが、工場での診断の結果変わることもあるという説明を受けました。8月3日(水)には配送予定となったので、数日間Apple Watchなしの生活になることが確定。この瞬間、月間チャレンジのアクティビティの達成は途切れることになりました。残念無念。

ただApple Kawasakiでの対応はものすごく丁寧かつ迅速で、20分以内にすべての手続きが完了しました。ありがたい。

さて、Apple Watchが動かなくなって一番困ったのはSuicaです。JR東日本のページにはFAQが用意されていますが、結局はAppleのサポートにサポートを依頼することに。

appsuica.okbiz.okwave.jp

Apple Kawasakiで相談したときには「ペアリングを解除して24時間以内には再度Walletから登録できるようになる」という説明でしたので、律儀に待ちましたが結局復旧しなかったので再度Appleに問い合わせ。今度はApple IDから該当のApple Watchにある「ウォレットとApple Pay」の「項目を削除」をタップするように言われて実施。再度24時間以内には登録できるようになるということから待って翌日の6時、復旧しました。復旧については以下のFAQが参考になりました。

appsuica.okbiz.okwave.jp

工場に行ったApple Watchは8月3日(水)に「お客様の製品を受領いたしました。」というメールが届き8月4日(木)には「交換品の発送についてのお知らせ」というメールが届きました。結局交換機への交換となったようです。8月5日(金)には手元に届き、一週間以内でご対応いただけました。

届いたのはApple Watch本体だけが入った箱。

結局料金は0円のまま。AppleCare+に入っていなかったら4万円以上の修理代金が必要だったことを考えると入っていて良かったのかなと思います。

support.apple.com

後から振り返るとエクスプレス交換サービスを利用しても良かったのかなと思いますが、Apple Kawasakiでなんとか対応してくれるかもという思いもあったので結果論かなと思います。

Azureの予算を作成する

今日は小ネタ。

クラウドは従量課金制であるサービスが多く、うっかりするとびっくりする請求を受けることがあります。そこで予算をシステムに登録し、一定金額を越えるようならアラートを出すということを設定することがWell Architectedでも記されています。

docs.microsoft.com

今回はその設定を行ってみます。

やり方はチュートリアルがあるのでそれに沿って。

docs.microsoft.com

特に難しいところはなく、名前と予算額を定めて...

通知の設定をすればOKです。

予算の有効期限を無期限にできないのかなと色々と試してみましたが、できませんでした。

個人的には、以下のようなことをやっています。

  • アラートが動いていることを確認するために、予算の割合が極端に低いものを一つ設定する
  • アラートの受信者(メール)を複数個設定する

Rails 7 + ReactにおけるJestを使ったテストの書き方

はじめに

Rails 7とReactを使ったサンプルアプリがあったので勉強がてら実装してみました。

techracho.bpsinc.jp

コンパクトにまとまったいい記事なのですが、テストが書かれていなかったので実装してみることにしました。なお、ここに書いているのはいわゆるベストプラクティスではなく、こうしたらとりあえず動いたというレベルで見ていただけると幸いです。

セットアップ

Jestを含めた各種ライブラリをインストールすることから始めます。Jestのサイトに以下の記事があったのでそれを参考にします。

jestjs.io

package.jsonにて以下の記述を追加しておくとnpm run testでテストが実行できるので追記しておきます。

  "scripts": {
    // ↓を追記
    "test": "jest"
  },

自分はテストをapp/javascript/__tests__に置くことにしたので、package.jsonにjestの設定を記述します。

  "jest": {
    "roots": [
      "<rootDir>/app/javascript"
    ]
  }

package.jsonではなくjestの設定ファイルとしてjest.config.jsを作成しても良いかと思います。

テストの書き方

各componentsに対してテストを書きます。DOMを操作するもののテストについては、React Testing Libraryを使うようです。

testing-library.com

他にもJest 28からの仕様変更によりjest-environment-jsdomの設定が必要でした。

testing-library.com

React Testing Libraryを使ったテストを書く場合、以下のドキュメントに書かれているクエリを発行してtesting-library/jest-domでのマッチャーを使ってテストを実装するようです。

testing-library.com

github.com

一番簡単なテストとしては以下のような実装となりました。

/**
 * @jest-environment jsdom
 */
import {render, screen} from "@testing-library/react";
import EventNotFound from "../components/EventNotFound";
import '@testing-library/jest-dom/extend-expect';

describe('EventNotFound', () => {
  test('renders EventNotFound component', () => {
    render(<EventNotFound />);
    expect(screen.getByText('Event not found!')).toBeInTheDocument();
  })
});

テストが失敗する例

context of a componentが出る

上記のような書き方でテストを実装すると、context of a <Router> componentというエラーが出てしまうケースがあります。この時は、MemoryRouterというものを使ってテストを書きます。

reactrouter.com

以下のようなテストになりました。

/**
 * @jest-environment jsdom
 */
import Header from "../components/Header";
import {render, screen} from "@testing-library/react";
import { MemoryRouter as Router } from "react-router-dom";
import '@testing-library/jest-dom/extend-expect';

test('renders Header', () => {
  render(
    <Router>
      <Header />
    </Router>
  );
  expect(screen.getByText('Event Manager')).toBeInTheDocument();
})

cssのimportにてSyntaxErrorが出る

テスト対象のcomponentにてimport 'pikaday/css/pikaday.css';というようにCSSを読み込んでいるとSyntaxErrorが出ました。この事象に対してはstackoverflowに類似の記事があったので、それを参考に解決します。

stackoverflow.com

具体的には、package.jsonにてJestの設定を追記します。

  "jest": {
    "roots": [
      "<rootDir>/app/javascript"
    ],
    "moduleNameMapper": {
      "\\.(css|less|sass|scss)$": "<rootDir>/app/javascript/__mocks__/styleMock.js"
    }
  }

その後、app/javascript/__mocks__/styleMock.jsというファイルを作成します。中身は以下の通りほぼ空です。

module.exports = {};

これでSyntaxErrorは出なくなりました。