CLOVER🍀

That was when it all began.

TerratestでTerraformのテストを書いてみる

これは、なにをしたくて書いたもの?

Terraformに対するテストコードを書くのに、Terratestというものが使えるというようなので。

こちらを試してみようかな、と。

Terratest

Terratestとは、インフラストラクチャーをテストするためのパターン、ヘルパー関数を提供するGoのライブラリです。

Terratest | Automated tests for your infrastructure code.

GitHub - gruntwork-io/terratest: Terratest is a Go library that makes it easier to write automated tests for your infrastructure code.

ドキュメントとGoDdocは、こちら。

Documentation

terratest · pkg.go.dev

ちゃんと見るまで、ずっとTerraform向けのテストライブラリだと思っていたのですが、こんな感じでけっこういろんな
テストができるようです。
※ドキュメント等に記載されている順番から、少し入れ替えています

Introduction

  • Testing Terraform code
  • Testing Packer templates
  • Testing Docker images
  • Testing Helm Charts
  • Working with AWS APIs
  • Working with Azure APIs
  • Working with GCP APIs
  • Working with Kubernetes APIs
  • Executing commands on servers over SSH
  • Making HTTP requests
  • Running shell commands
  • And much more

Package by package overview

https://github.com/gruntwork-io/terratest/tree/v0.35.1/modules

基本的な使い方は、こちらに載っています。

Quick start / Setting up your project

Quick start / Terratest intro

テストコードは通常のGoのテストコードとして書き、go testコマンドで実行することになります。

テストコードの実行時に、

  • Terratestを使用してIaCツール(Terraform、Packer、Dockerなど)を実行して、実際の環境を構築
  • Terratestが提供するライブラリを使用して、HTTPリクエストの実行、API呼び出しなどを行って確認

というステップを踏み、最後にテスト中に構築した環境(リソース)を破棄します。

各テストのサンプルは、Quick startやExamplesにあるのですが

Quick start

Examples

GitHubリポジトリには、さらに多数のサンプルがあります。

https://github.com/gruntwork-io/terratest/tree/v0.35.1/examples

インフラをコードでテストする際の考え方は、こちらを見るとよいようです。

How to test infrastructure code: automated testing for Terraform, Kub…

今回は、Terraformを使ったテストコードを書いてみたいと思います。

環境

今回の環境は、こちら。

$ go version
go version go1.16.4 linux/amd64


$ terraform version
Terraform v0.15.5
on linux_amd64

Terratest(0.35.1時点)では、Terratestが要求しているのはGo 1.13以上、のみです。

Quick start / Requirements

ですが、TerratestでTerraformのテストを書こうとすると、内部的にterraformコマンドを実行するため、結局Terraformも
必要になります。

また、TerraformではMySQL Providerを使うことにします。

Provider: MySQL - Terraform by HashiCorp

ここで使用するMySQLは8.0.25で、172.17.0.2でアクセスできるものとします。

お題

今回は、TerraformでMySQLに関するリソースを構築し、それに対するテストを書きます。

テストは、Quick startに習って単純にTerraformのoutputを確認するもののと、構築されたリソースの確認を含めて
行うものの2つを行いましょう。

準備

まずは、ディレクトリを作成します。

Quick startを見ていると、Terratestを使ったテストコードはtestディレクトリに置き、またGoモジュールのルートディレクトリと
した方が良さそうですね。

Quick start / Setting up your project

terraformtestディレクトリをそれぞれ作成することにしました。

$ mkdir terraform test

Terratest側のテストコードも、そのようになっています(Goモジュールのルートディレクトリではないですが)。

https://github.com/gruntwork-io/terratest/tree/v0.35.1/test

今回作成するディレクトリやファイルは、最終的にこんな感じになりました。

.
├── terraform
│   ├── complex
│   │   └── main.tf
│   └── simple
│       └── main.tf
└── test
    ├── go.mod
    ├── go.sum
    └── mysql_test.go

では、Goモジュール側のセットアップをしましょう。testディレクトリへ移動し、

$ cd test

Goモジュールの初期化。

$ go mod init getting-started
go: creating new go.mod: module getting-started

Terratestをgo getします。

$ go get github.com/gruntwork-io/terratest/modules/terraform@v0.35.1

go get github.com/gruntwork-io/terratest@v0.35.1だと、ライブラリが足りない状態になりました…。

あとは、テストライブラリとしてtestifyを、MySQLへアクセスするためにMySQLのドライバーをインストールします。

$ go get github.com/stretchr/testify@v1.7.0
$ go get github.com/go-sql-driver/mysql@v1.6.0

go.modは、こうなりました。

go.mod

module getting-started

go 1.16

require (
    github.com/go-sql-driver/mysql v1.6.0 // indirect
    github.com/gruntwork-io/terratest v0.35.1 // indirect
    github.com/stretchr/testify v1.7.0 // indirect
)

テストコードの雛形

最初に、テストコードの雛形を書いておきましょう。

mysql_test.go

package test

import (
    "database/sql"
    "fmt"
    "testing"

    _ "github.com/go-sql-driver/mysql"
    "github.com/gruntwork-io/terratest/modules/terraform"
    "github.com/stretchr/testify/assert"
)

// ここに、テストを書く!

Terratestを使って、Terraformを実行する

最初に、Terraformの構成ファイルを書きましょう。先ほど作成したterraformディレクトリ内に、ディレクトリを追加します。

$ mkdir terraform/simple
$ cd terraform/simple

こんな感じに、データベースを作成するだけのmain.tfファイルを作成。

main.tf

terraform {
  required_version = "0.15.5"

  required_providers {

    mysql = {
      source  = "terraform-providers/mysql"
      version = "1.9.0"
    }
  }
}

provider "mysql" {
  endpoint = "172.17.0.2:3306"
  username = "root"
  password = "password"
}

resource "mysql_database" "app" {
  name = "my_database"
}

output "database_name" {
  value = mysql_database.app.name
}

outputは、データベース名のみです。

一応、単体でも確認しておきましょう。

$ terraform init
$ terraform fmt -diff -recursive
$ terraform apply
$ terraform destroy

次に、テストコードにテストを追加します。

こちらを参考に。

Quick start / Example #1: Terraform “Hello, World”

func TestTerraformMySqlHelloWorld(t *testing.T) {
    terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
        TerraformDir: "../terraform/simple",
    })

    defer terraform.Destroy(t, terraformOptions)

    terraform.InitAndApply(t, terraformOptions)

    databaseName := terraform.Output(t, terraformOptions, "database_name")

    assert.Equal(t, "my_database", databaseName)
}

こちらで、実行するTerraformの構成ファイルが配置されているディレクトリを指定。

   terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
        TerraformDir: "../terraform/simple",
    })

WithDefaultRetryableErrorsという関数は、Optionsさらにリトライ設定を追加するものになります。

type Options

func WithDefaultRetryableErrors

戻り値は、Optionsのままです。

テストの成功/失敗に関わらず、最後にterraform destroyするようにしています。

func Destroy

   defer terraform.Destroy(t, terraformOptions)

あとはterraform initterraform applyを行い

func InitAndApply

   terraform.InitAndApply(t, terraformOptions)

outputを取得してアサーションして完了です。

func Output

   databaseName := terraform.Output(t, terraformOptions, "database_name")

    assert.Equal(t, "my_database", databaseName)

outputを扱う関数は他にもあり、全部のoutputを取得したりもできそうですが、型アサーションが必要そうなので、
それなりに型を合わせたものを使うのが良さそうです。

では、実行してみましょう。

$ go test -v

実行ログは、こんな感じになりました。

$ go test -v
=== RUN   TestTerraformMySqlHelloWorld
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 retry.go:91: terraform [init -upgrade=false]
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: Running command terraform with args [init -upgrade=false]
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: Initializing the backend...
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: Initializing provider plugins...
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: - Reusing previous version of terraform-providers/mysql from the dependency lock file
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: - Using previously-installed terraform-providers/mysql v1.9.0
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: Terraform has been successfully initialized!
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: You may now begin working with Terraform. Try running "terraform plan" to see
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: any changes that are required for your infrastructure. All Terraform commands
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: should now work.
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: If you ever set or change modules or backend configuration for Terraform,
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: rerun this command to reinitialize your working directory. If you forget, other
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: commands will detect it and remind you to do so if necessary.
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 retry.go:91: terraform [apply -input=false -auto-approve -lock=false]
TestTerraformMySqlHelloWorld 2021-06-05T22:02:09+09:00 logger.go:66: Running command terraform with args [apply -input=false -auto-approve -lock=false]
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: Terraform used the selected providers to generate the following execution
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: plan. Resource actions are indicated with the following symbols:
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66:   + create
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: Terraform will perform the following actions:
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66:   # mysql_database.app will be created
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66:   + resource "mysql_database" "app" {
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66:       + default_character_set = "utf8"
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66:       + default_collation     = "utf8_general_ci"
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66:       + id                    = (known after apply)
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66:       + name                  = "my_database"
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66:     }
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: Plan: 1 to add, 0 to change, 0 to destroy.
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: Changes to Outputs:
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66:   + database_name = "my_database"
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: mysql_database.app: Creating...
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: mysql_database.app: Creation complete after 0s [id=my_database]
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: Outputs:
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: database_name = "my_database"
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 retry.go:91: terraform [output -no-color -json database_name]
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: Running command terraform with args [output -no-color -json database_name]
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: "my_database"
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 retry.go:91: terraform [destroy -auto-approve -input=false -lock=false]
TestTerraformMySqlHelloWorld 2021-06-05T22:02:10+09:00 logger.go:66: Running command terraform with args [destroy -auto-approve -input=false -lock=false]
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: mysql_database.app: Refreshing state... [id=my_database]
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: Terraform used the selected providers to generate the following execution
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: plan. Resource actions are indicated with the following symbols:
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66:   - destroy
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: Terraform will perform the following actions:
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66:   # mysql_database.app will be destroyed
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66:   - resource "mysql_database" "app" {
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66:       - default_character_set = "utf8" -> null
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66:       - default_collation     = "utf8_general_ci" -> null
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66:       - id                    = "my_database" -> null
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66:       - name                  = "my_database" -> null
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66:     }
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: Plan: 0 to add, 0 to change, 1 to destroy.
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: Changes to Outputs:
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66:   - database_name = "my_database" -> null
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: mysql_database.app: Destroying... [id=my_database]
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: mysql_database.app: Destruction complete after 1s
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: 
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: Destroy complete! Resources: 1 destroyed.
TestTerraformMySqlHelloWorld 2021-06-05T22:02:11+09:00 logger.go:66: 
--- PASS: TestTerraformMySqlHelloWorld (2.46s)
PASS
ok      getting-started 2.462s

よくよく見ると、terraformコマンドの実行内容もログに含まれています。

terraform [init -upgrade=false]
terraform [apply -input=false -auto-approve -lock=false]
terraform [output -no-color -json database_name]
terraform [destroy -auto-approve -input=false -lock=false]

なので、terraformコマンドがインストールされていない場合は、このテストは実行に失敗します。

TestTerraformMySqlHelloWorld 2021-06-05T22:05:01+09:00 retry.go:99: Returning due to fatal error: FatalError{Underlying: error while running command: exec: "terraform": executable file not found in $PATH; }
    apply.go:15: 
            Error Trace:    apply.go:15
                                        mysql_test.go:20
            Error:          Received unexpected error:
                            FatalError{Underlying: error while running command: exec: "terraform": executable file not found in $PATH; }
            Test:           TestTerraformMySqlHelloWorld

なんとなく、基本的な使い方はわかった気がします。

あとは、その他のTerratestが提供するIaCツールサポート、ライブラリの使い方を覚えたり、
ドキュメントに「テストのベストプラクティス」が載っているので、こちらを見たりして慣れていくのかな、と
思います。

Testing best practices

もう少し複雑な例を

続いて、先ほどのシンプルな例にもう少し要素を加えたものをやってみましょう。

ディレクトリを追加。

$ mkdir terraform/complex
$ cd terraform/complex

Terraformの構成ファイルは、こちら。

main.tf

terraform {
  required_version = "0.15.5"

  required_providers {

    mysql = {
      source  = "terraform-providers/mysql"
      version = "1.9.0"
    }
  }
}

variable "database_name" {
  type = string
}

variable "app_user_name" {
  type = string
  default = "app_user"
}

variable "app_user_password" {
  type = string
}

provider "mysql" {
  endpoint = "172.17.0.2:3306"
  username = "root"
  password = "password"
}

resource "mysql_database" "app" {
  name = var.database_name
}

resource "mysql_user" "app_user" {
  user               = var.app_user_name
  plaintext_password = var.app_user_password
  host               = "%"
}

resource "mysql_grant" "user_grant" {
  user       = mysql_user.app_user.user
  host       = mysql_user.app_user.host
  database   = mysql_database.app.name
  privileges = ["ALL"]
}

output "database_name" {
  value = mysql_database.app.name
}

output "app_user_name" {
  value = mysql_user.app_user.user
}

今回は、variableも取るようにしました。

variable "database_name" {
  type = string
}

variable "app_user_name" {
  type = string
  default = "app_user"
}

variable "app_user_password" {
  type = string
}

ユーザーも作成するようにしています。

resource "mysql_user" "app_user" {
  user               = var.app_user_name
  plaintext_password = var.app_user_password
  host               = "%"
}

resource "mysql_grant" "user_grant" {
  user       = mysql_user.app_user.user
  host       = mysql_user.app_user.host
  database   = mysql_database.app.name
  privileges = ["ALL"]
}

テストコードは、こちら。

func TestTerraformMySqlWithValidation(t *testing.T) {
    appUserPassword := "app_password"

    terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
        TerraformDir: "../terraform/complex",
        Vars: map[string]interface{}{
            "database_name":     "practice",
            "app_user_password": appUserPassword,
        },
    })

    defer terraform.Destroy(t, terraformOptions)

    terraform.InitAndApply(t, terraformOptions)

    databaseName := terraform.Output(t, terraformOptions, "database_name")
    appUserName := terraform.Output(t, terraformOptions, "app_user_name")

    assert.Equal(t, "practice", databaseName)
    assert.Equal(t, "app_user", appUserName)

    // validation
    db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@(%s:%d)/%s", appUserName, appUserPassword, "172.17.0.2", 3306, databaseName))

    assert.Nil(t, err)
    assert.NotNil(t, db)

    defer db.Close()

    err = db.Ping()
    assert.Nil(t, err)
}

Optionsに、今回はVarの指定を追加しました。-varと同じですね。

   terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
        TerraformDir: "../terraform/complex",
        Vars: map[string]interface{}{
            "database_name":     "practice",
            "app_user_password": appUserPassword,
        },
    })

terraform destroyの仕込みや、terraform initterraform applyの実行、terraform outputからのアサーションを行いつつ

   defer terraform.Destroy(t, terraformOptions)

    terraform.InitAndApply(t, terraformOptions)

    databaseName := terraform.Output(t, terraformOptions, "database_name")
    appUserName := terraform.Output(t, terraformOptions, "app_user_name")

    assert.Equal(t, "practice", databaseName)
    assert.Equal(t, "app_user", appUserName)

最後に、作成したユーザーで、MySQLに接続できることを確認します。

   // validation
    db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@(%s:%d)/%s", appUserName, appUserPassword, "172.17.0.2", 3306, databaseName))

    assert.Nil(t, err)
    assert.NotNil(t, db)

    defer db.Close()

    err = db.Ping()
    assert.Nil(t, err)

このように、構築されたリソースに対して動作確認(HTTPリクエストを実行する、API呼び出しを行う、SSHでコマンドを
実行する、など)することを、バリデーションと呼んでいるみたいです。

https://www.slideshare.net/brikis98/how-to-test-infrastructure-code-automated-testing-for-terraform-kubernetes-docker-packer-and-more/53

https://www.slideshare.net/brikis98/how-to-test-infrastructure-code-automated-testing-for-terraform-kubernetes-docker-packer-and-more/67

https://www.slideshare.net/brikis98/how-to-test-infrastructure-code-automated-testing-for-terraform-kubernetes-docker-packer-and-more/69

テストで確認。

$ go test -v

実行ログ。

$ go test -v
=== RUN   TestTerraformMySqlWithValidation
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 retry.go:91: terraform [init -upgrade=false]
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: Running command terraform with args [init -upgrade=false]
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: Initializing the backend...
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: Initializing provider plugins...
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: - Reusing previous version of terraform-providers/mysql from the dependency lock file
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: - Using previously-installed terraform-providers/mysql v1.9.0
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: Terraform has been successfully initialized!
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: You may now begin working with Terraform. Try running "terraform plan" to see
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: any changes that are required for your infrastructure. All Terraform commands
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: should now work.
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: If you ever set or change modules or backend configuration for Terraform,
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: rerun this command to reinitialize your working directory. If you forget, other
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: commands will detect it and remind you to do so if necessary.
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 retry.go:91: terraform [apply -input=false -auto-approve -var database_name=practice -var app_user_password=app_password -lock=false]
TestTerraformMySqlWithValidation 2021-06-05T22:37:52+09:00 logger.go:66: Running command terraform with args [apply -input=false -auto-approve -var database_name=practice -var app_user_password=app_password -lock=false]
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: Terraform used the selected providers to generate the following execution
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: plan. Resource actions are indicated with the following symbols:
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:   + create
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: Terraform will perform the following actions:
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:   # mysql_database.app will be created
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:   + resource "mysql_database" "app" {
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + default_character_set = "utf8"
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + default_collation     = "utf8_general_ci"
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + id                    = (known after apply)
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + name                  = "practice"
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:     }
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:   # mysql_grant.user_grant will be created
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:   + resource "mysql_grant" "user_grant" {
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + database   = "practice"
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + grant      = false
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + host       = "%!"(MISSING)
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + id         = (known after apply)
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + privileges = [
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:           + "ALL",
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:         ]
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + table      = "*"
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + tls_option = "NONE"
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + user       = "app_user"
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:     }
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:   # mysql_user.app_user will be created
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:   + resource "mysql_user" "app_user" {
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + host               = "%!"(MISSING)
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + id                 = (known after apply)
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + plaintext_password = (sensitive value)
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + tls_option         = "NONE"
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:       + user               = "app_user"
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:     }
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: Plan: 3 to add, 0 to change, 0 to destroy.
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: Changes to Outputs:
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:   + app_user_name = "app_user"
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66:   + database_name = "practice"
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: mysql_database.app: Creating...
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: mysql_user.app_user: Creating...
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: mysql_user.app_user: Creation complete after 1s [id=app_user@%!](MISSING)
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: mysql_database.app: Creation complete after 1s [id=practice]
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: mysql_grant.user_grant: Creating...
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: mysql_grant.user_grant: Creation complete after 0s [id=app_user@%!:(MISSING)`practice`]
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: Outputs:
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: app_user_name = "app_user"
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: database_name = "practice"
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 retry.go:91: terraform [output -no-color -json database_name]
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: Running command terraform with args [output -no-color -json database_name]
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: "practice"
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 retry.go:91: terraform [output -no-color -json app_user_name]
TestTerraformMySqlWithValidation 2021-06-05T22:37:53+09:00 logger.go:66: Running command terraform with args [output -no-color -json app_user_name]
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: "app_user"
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 retry.go:91: terraform [destroy -auto-approve -input=false -var app_user_password=app_password -var database_name=practice -lock=false]
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: Running command terraform with args [destroy -auto-approve -input=false -var app_user_password=app_password -var database_name=practice -lock=false]
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: mysql_database.app: Refreshing state... [id=practice]
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: mysql_user.app_user: Refreshing state... [id=app_user@%!](MISSING)
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: mysql_grant.user_grant: Refreshing state... [id=app_user@%!:(MISSING)`practice`]
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: Terraform used the selected providers to generate the following execution
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: plan. Resource actions are indicated with the following symbols:
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:   - destroy
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: Terraform will perform the following actions:
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:   # mysql_database.app will be destroyed
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:   - resource "mysql_database" "app" {
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - default_character_set = "utf8" -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - default_collation     = "utf8_general_ci" -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - id                    = "practice" -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - name                  = "practice" -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:     }
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:   # mysql_grant.user_grant will be destroyed
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:   - resource "mysql_grant" "user_grant" {
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - database   = "practice" -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - grant      = false -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - host       = "%!"(MISSING) -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - id         = "app_user@%!:(MISSING)`practice`" -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - privileges = [
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:           - "ALL",
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:         ] -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - table      = "*" -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - tls_option = "NONE" -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - user       = "app_user" -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:     }
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:   # mysql_user.app_user will be destroyed
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:   - resource "mysql_user" "app_user" {
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - host               = "%!"(MISSING) -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - id                 = "app_user@%!"(MISSING) -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - plaintext_password = (sensitive value)
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - tls_option         = "NONE" -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:       - user               = "app_user" -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:     }
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: Plan: 0 to add, 0 to change, 3 to destroy.
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66: Changes to Outputs:
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:   - app_user_name = "app_user" -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:54+09:00 logger.go:66:   - database_name = "practice" -> null
TestTerraformMySqlWithValidation 2021-06-05T22:37:55+09:00 logger.go:66: mysql_grant.user_grant: Destroying... [id=app_user@%!:(MISSING)`practice`]
TestTerraformMySqlWithValidation 2021-06-05T22:37:55+09:00 logger.go:66: mysql_grant.user_grant: Destruction complete after 0s
TestTerraformMySqlWithValidation 2021-06-05T22:37:55+09:00 logger.go:66: mysql_database.app: Destroying... [id=practice]
TestTerraformMySqlWithValidation 2021-06-05T22:37:55+09:00 logger.go:66: mysql_user.app_user: Destroying... [id=app_user@%!](MISSING)
TestTerraformMySqlWithValidation 2021-06-05T22:37:55+09:00 logger.go:66: mysql_user.app_user: Destruction complete after 0s
TestTerraformMySqlWithValidation 2021-06-05T22:37:55+09:00 logger.go:66: mysql_database.app: Destruction complete after 0s
TestTerraformMySqlWithValidation 2021-06-05T22:37:55+09:00 logger.go:66: 
TestTerraformMySqlWithValidation 2021-06-05T22:37:55+09:00 logger.go:66: Destroy complete! Resources: 3 destroyed.
TestTerraformMySqlWithValidation 2021-06-05T22:37:55+09:00 logger.go:66: 
--- PASS: TestTerraformMySqlWithValidation (3.12s)
PASS
ok      getting-started 3.127s

最初に作成したテストと、terraformコマンドの実行バリエーションが異なるのは、-varが追加されたことですね。

terraform [apply -input=false -auto-approve -var database_name=practice -var app_user_password=app_password -lock=false]

これで、variableを渡したり、実際に構築したリソースを確認したりするテストができました、と。

まとめ

Terratestを使って、Terraformの構成ファイルに対するテストを行ってみました。

調べてみると、Terraform以外のインフラ関係のツールのテストもできそうなので、もうちょっと調べてみてもいいかもですね。

最後に、テストコード全体を載せておきます。

mysql_test.go

package test

import (
    "database/sql"
    "fmt"
    "testing"

    _ "github.com/go-sql-driver/mysql"
    "github.com/gruntwork-io/terratest/modules/terraform"
    "github.com/stretchr/testify/assert"
)

func TestTerraformMySqlHelloWorld(t *testing.T) {
    terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
        TerraformDir: "../terraform/simple",
    })

    defer terraform.Destroy(t, terraformOptions)

    terraform.InitAndApply(t, terraformOptions)

    databaseName := terraform.Output(t, terraformOptions, "database_name")

    assert.Equal(t, "my_database", databaseName)
}

func TestTerraformMySqlWithValidation(t *testing.T) {
    appUserPassword := "app_password"

    terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
        TerraformDir: "../terraform/complex",
        Vars: map[string]interface{}{
            "database_name":     "practice",
            "app_user_password": appUserPassword,
        },
    })

    defer terraform.Destroy(t, terraformOptions)

    terraform.InitAndApply(t, terraformOptions)

    databaseName := terraform.Output(t, terraformOptions, "database_name")
    appUserName := terraform.Output(t, terraformOptions, "app_user_name")

    assert.Equal(t, "practice", databaseName)
    assert.Equal(t, "app_user", appUserName)

    // validation
    db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@(%s:%d)/%s", appUserName, appUserPassword, "172.17.0.2", 3306, databaseName))

    assert.Nil(t, err)
    assert.NotNil(t, db)

    defer db.Close()

    err = db.Ping()
    assert.Nil(t, err)
}

ブログを書き始めてから、10年経ちました

なんと

このブログですが、書き始めてから10年経っていたようです。

最初に書いたのは2011年5月14日で、「今日から開設。」とまったくやる気がなさそうな1本目から始まっているのですが。

続けて10年経ったということに、今年になって「どれくらい続けてるの?」と聞かれて気づいてしまいました。

たぶん、聞かれてなかったら気づいてないですね。

このブログでは、実際の自分の勉強の記録以外ほとんど書いてきていないのですが、こういう区切りっぽい年くらい
たまにはいいかなぁということで。

振り返りを兼ねて、つらつらと書いてみましょう。自分が「どうしてだっけ?」というのを考えながら書いています。

もう数年経った時に、自分で見返したらどう思うでしょうね?

ブログを始めた理由

自分の勉強のために始めました。

当時、本を読んだり、本やWebを見ながらコードを書いていても、どうも身につかないことに悩んでいて。
文章で書いた方が覚えるだろう、という効果を狙って始めたのがきっかけです。

想定対象読者

対象読者は、自分自身です。

ごく稀に、読み手の存在を意識して書く記事もありますが(最近やってないですが、Advent Calendarなど)、基本的には
自分の勉強のため、未来の自分が見て読み返して、思い出せるために書いています。

1回ちゃんと書いておけば、あとで見ても書いた当時にどういうことをやったのか、意外と思い出せるものだったりします。

とはいえ、たまに書いたこと自体を忘れていて、同じような記事を書いてしまうこともありましたけどね。

けっこう冗長感のあるタイトルのものが多いと思うのですが、これはキーワードで自分が検索しやすくするためです。

自分以外の人が読むことはほとんど気にしていなくて、「こんなブログの記事でも、読めるようなら、なにか役に立つようで
あればどうぞ」といった スタンスでやっています。これは、このブログを始めた時からずっとそう思っています。

なので、「結論をわかりやすくまとめる」といったことはほとんどしていなくて、「これをやろう」、「そしてこうだった」
ということを淡々と書いています。

当初から、「こんなブログを見る奇特な人なんていないだろう」と思って書いているんですけどね。
それは、記事へのアクセス数からも言えると思うのですが。

そういえば、はてなブログに移行してから、読者の増え方が上がった気がします。どうしてなんでしょう?

なんで"CLOVER"?

"CLOVER"自体に意味は込めていません。

このブログの名前はある集合から音の響きで選んだもので、時々変えていたのですが、"CLOVER"に変えた後に意外と
見られていることがわかってしまい、変えるに変えられなくなって今に至ります。

現時点で、書いた記事数

単純な数なら、10年で1,432。

ほんの数行、みたいなものもありますけど。

他と比較することも、したいとも思ったことがないので、これが多い、少ないはよくわかりません。

続けている理由

自分の勉強のために書いているので、完全に趣味です。

やることがない時に、「そういえば、これ試してみたいな」くらいの動機で書いていることが多いです。

他の予定があったら、ふつうにそちらを優先してます。

ノルマとか

月に1回投稿すること。これだけです。

書くネタの決め方

このあたりから、「勉強したいリスト」を作って、ある程度優先順位をつけて決めています。

  • 年末の振り返りで年内にやったことと、来年に勉強してみたいことをリストアップしてみたもの
  • 日常の目に入った情報から、興味があったもの
  • 仕事で触れるもので、自分の興味と合致したもの

そのリストに沿って必ずやっているかというとそうでもなく、気づくと「けっこう違うことやっているな」ということも
多々あります。

ここ数年は仕事で扱うものについても書く割合が増えている気がしますが、仕事で使うものであっても
そもそも自分の興味のないものでないと、このブログには登場しません。

あと、このブログに書いている内容は、ほとんどがいわゆる「やってみた」という内容なので、キャッチーさが
ほぼありません。興味のある内容を試していっている履歴なので、まあ、そうなりますよね、と。
記事が注目されない一因でもある気はしますが。

この傾向が変わることは、ないと思います。今のところ、そういう感じになることはないかなぁと。

やりたいネタは、十分に扱えてる?

遊んでみたいこと、試してみたいことに追いつかないことが増えましたね。

他のものを扱っていたり、優先度を入れ替えたりした結果、時間が過ぎてしまってそのまま見送ってしまうものが
けっこうあります。

公式のGetting Startedとか、他の人が同じような情報を書いてることも書いているような?意味ないのでは?

ふつうに書いています。

他の方が書いていたとしても、自分で確認して、自分の言葉でこのブログで書けるくらいに理解してみる、納得してみる、が
このブログを書いている理由にもなっていますし。

それに、他の方の理解したことと、自分が確認して理解したことは同じではないと思いますし、どこか違いがあるでしょう。

もしかしたらまったく同じかもしれませんが、それを無意味だとは思いません。

他の方の記事を書き写しているようなら意味はないと思いますが、アクセス数を稼ぎたいわけでもないですし。

アクセスの傾向とか、よく読まれている記事とか?

平日にアクセスが多く、休みの日はかなり下がります。

Linuxに関するものが多く見られている傾向にありますが、すごく突出したものはありませんし(アクセス解析でも1位が3%とか)、
はてブもあまりつきません。

なので、なにがよく読まれているとか自分でもよくわかりません。いろんなページに散らばっているんだろうな、とは
思っています。

なお、はてなブログに移行するまでアクセス解析を見たことがなかったので、そこで始めてアクセス傾向を知りましたし、
はてなダイアリーの頃にどうだったのかはまったくわかりません。

10年書いていて、書き方が変わったとかある?

書き始めのころからすると、記事の構成がだいぶ変わりました。

大きく変わったのは、確認した時の条件、環境と、情報源の残し方ですね。

自分が書き始めた頃と比べると、情報の陳腐化する速度が早まったこともあって、ここ数年は

  • ライブラリやミドルウェアを使う場合は、どのバージョンについての話なのかを明記する
  • 前提となる環境の情報を明記する

ということを、書きすぎない、ジャマにならない程度に書くことにしています。

参照する情報の引用元については、

にするように強く意識しています。

この時、可能な限りドキュメントもソースコードも、参照する時には使っているバージョンの指しているURLを使用するように
しています。

たまにドキュメントは latest しかなくて諦めるとか、リポジトリにタグがない時は仕方がないのでコミットハッシュを指すとか
困ることはありますが、可能な限り記事を書いた時に使ったバージョンを追えるように注意しています。

このあたりは書き始めた当初は緩くて、バージョンを明記していないとか、公式以外のブログ記事などをそのまま
参考先としていたりしましたが。

今は、公式の情報を必ず確認するようにしています。

たまに、公式のドキュメントに書かれていないかったり、記述が十分にない情報について足元をすくわれたりして、
「もうちょっと他も調べておけばよかったかも」と思うこともあるのですが。

最初に見つけたのが公式以外の情報でも、それが公式のドキュメントに載っているかどうかは確認するようにしています。

特に書いた時の条件や環境を書き残すことにしてから構成を見直すようになり、これらに加えて「そもそもなんでこの記事
書いたの?」というのを小さなことでも書くようにしたのが今の書き方です。

あとは、書いていて「これも気になる」と思ったことを深追いして、記事が長めになることが増えた気がします…。

他のブログサービスや、プラットフォームに移らないのは?

このブログは、はてなダイアリーで始めて、今は、はてなブログとして利用させていただいているわけですが。

ブログを書くことで自分のブランディングをしているわけでもないですし、記事を書いて注目を集めたいというわけでも
ないので、移る理由がなく。

なにより、自分の勉強の記録として書いているだけなので、もし移るとすれば記事ごと全部移らないと意味がありません。
それも手間ですしね。

気ままに、書きたいものが自由に書ける場所であればそれでいいです。

とはいえ、他の場所でこういう技術系のブログ、記事を書きたくない、というわけでもありません。

ブログを続けてきて良かった?

後悔みたいなものは、まったくしていなくて。

仕事に直接役に立ってますか?という話だとそうでもないものは多いですが、情報の調べ方、実際に動かして確認してみる、
という繰り返しの経験はそれなりに意味があるんじゃないかな、と思っています。

このブログをきっかけに、知り合いになった方もいらっしゃいますしね。

それに、幸い?記事が注目を集めることがまったくないので、非難?のようなものも受けてきませんでしたし。

まあ、まったくなにもないかというとそんなことはなく、微妙に思うコメントがつくとか、実際に見た人から「知ってることしか
書かれていない」とか言われたこととかもあったりしますが。

ごく少数ですし、「見なくていいですよ?」としか思わないので。

ブログに登場していないけれど、勉強してみたいものは?

このブログでは、ある条件に当てはまったものは、興味があってもテーマとしては取り扱わないことにしています。

それに当てはまるひとつとして、このブログにはクラウドが登場しません。
クラウド以外のものについては扱っていないことを気にしていなかったのですが、クラウドについてはそれでいいのかな?
とは時々思っています。

でも、今後も登場することはないんだろうな、とも思っています。

今後はどうする?

変わらず、マイペースに続けると思いますよ。

これからも見る方がいらっしゃるようであれば、なにか役に立つものがあれば良いな、と思います。