開発言語、フレームワークなどの End Of Life （サポート終了日）を API で取得し、一覧化する

## はじめに

どんなものにも終わりはあります

今は当たり前のように存在しているサービスも、開発言語も、いつかはサポート終了 = EOL = End Of Life を迎えるのです

endoflife.date　は、各種製品のバージョン毎の EOL をまとめてくれているサービスです

https://endoflife.date/

この世のすべての製品、というわけにはいきませんが、 270 の製品について EOL の情報を持っているので、およそメジャーなものは揃っています

:::note info
endoflife.date　はオープンソースで成り立っており、 GitHub から貢献できるようになっています

https://github.com/endoflife-date/endoflife.date
:::

例えば Elixir の 1.10 は 2023 年 6 月 19 日でセキュリティーサポートを終了しています

https://endoflife.date/elixir

サポートが終了した製品等を使い続けていると、様々なリスクを背負い込むことになります

- セキュリティホールを塞ぐことが困難になる
- 更新できないため、他の新しい製品との整合性が取れなくなる
- OS やブラウザの更新に対応しないため、ある日急に動かなくなり、二度と動かなくなる可能性がある

そのため、エンジニアは EOL を意識して技術選定し、運用保守計画にはバージョンアップを組み込む必要があります

本記事では endoflife.date の API を利用し、各種製品の EOL を一覧化したいと思います

https://endoflife.date/docs/api

使用するのはいつもの通り Livebook です

https://livebook.dev/

## セットアップ

API にアクセスするための Req と、データ分析用の KinoExplorer をインストールします

```elixir
Mix.install([
  {:req, "~> 0.3"},
  {:kino_explorer, "~> 0.1"}
])
```

Explorer について、詳しくは以下の記事を参照してください

https://qiita.com/RyoWakabayashi/items/dfb5495b9c25eff710a2

エイリアスとマクロの設定をします

```elixir
alias Explorer.DataFrame
alias Explorer.Series
require Explorer.DataFrame
```

## 製品一覧の取得

API の仕様に従って製品一覧を取得します

```elixir
all_products =
  "https://endoflife.date/api/all.json"
  |> Req.get!()
  |> Map.get(:body)
```

実行結果
```elixir
["akeneo-pim", "alibaba-dragonwell", "almalinux", "alpine", "amazon-cdk", "amazon-corretto",
 "amazon-eks", "amazon-glue", "amazon-linux", "amazon-neptune", "amazon-rds-mysql",
 "amazon-rds-postgresql", "android", "angular", "angularjs", "ansible", "ansible-core", "antix",
 "apache", "apache-activemq", "apache-airflow", "apache-camel", "apache-cassandra", "apache-groovy",
 "apache-hadoop", "apache-hop", "apache-kafka", "apache-spark", "api-platform", "apple-watch",
 "argo-cd", "artifactory", "azul-zulu", "azure-devops-server", "azure-kubernetes-service", "beats",
 "bellsoft-liberica", "blender", "bootstrap", "cakephp", "centos", "centos-stream", "cfengine",
 "citrix-vad", "clamav", "coldfusion", "composer", "confluence", "consul", "contao", ...]
```

アルファベット順に各種製品が取得できました

## 全製品、全バージョンの EOL 取得

API の仕様に従って製品毎に EOL 情報を取得します

product　に製品、 cycle にバージョン番号、 eol にサポート終了日が入るようにします

```elixir
all_eof =
  all_products
  |> Enum.map(fn product ->
    product
    |> then(&"https://endoflife.date/api/#{&1}.json")
    |> Req.get!()
    |> Map.get(:body)
    |> Enum.map(fn eof ->
      %{
        "product" => product,
        "cycle" => eof["cycle"],
        "eol" => eof["eol"]
      }
    end)
  end)
  |> Enum.concat()
```

実行結果
```elixir
[
  %{"cycle" => "7.0", "eol" => "2024-09-30", "product" => "akeneo-pim"},
  %{"cycle" => "6.0", "eol" => "2023-09-30", "product" => "akeneo-pim"},
  %{"cycle" => "5.0", "eol" => "2022-09-30", "product" => "akeneo-pim"},
  ...
  %{"cycle" => "1.27", "eol" => "2024-07-01", "product" => "amazon-eks"},
  %{"cycle" => "1.26", "eol" => "2024-06-01", "product" => "amazon-eks"},
  %{"cycle" => "1.25", "eol" => "2024-05-01", "product" => "amazon-eks"},
  %{"cycle" => "1.24", "eol" => "2024-01-31", ...},
  %{"cycle" => "1.23", ...},
  %{...},
  ...
]
```

## データフレームへの変換

データ分析したりテーブル表示したりするために、 API で取得したデータをデータフレームに変換します

eol は文字列として API から取得していますが、扱いやすいように日付に変換します

```elixir
eof_df =
  all_eof
  |> Enum.map(fn eof ->
    Map.update!(eof, "eol", fn value ->
      if is_nil(value) or is_boolean(value) do
        nil
      else
        NaiveDateTime.from_iso8601!("#{value}T00:00:00Z")
      end
    end)
  end)
  |> DataFrame.new()
  |> DataFrame.select(["product", "cycle", "eol"])
```

実行結果
![スクリーンショット 2023-11-22 16.19.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/71709b11-d455-f60f-8a08-76cbb87b45bf.png)

この状態まで持っていけば、検索や並べ替えもすぐにできます

![search.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/5192c388-790b-3786-2ad9-cab2708e5096.gif)

## 直近 EOL の検索

以下のようなコードで直近 180 日以内にサポート終了する製品、バージョンが取得できます

```elixir
range_start = NaiveDateTime.utc_now()
range_end = NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 60 * 24 * 180)

eof_df
|> DataFrame.filter(eol >= ^range_start and eol <= ^range_end)
|> DataFrame.arrange(eol)
```

![スクリーンショット 2023-11-22 16.23.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/92b4384c-84ee-fcda-0e62-3b1e50d4a505.png)

もしも自分が使っている製品、バージョンがここに出てきたら、何らかの対応を計画しましょう

## まとめ

昨今は非常に多くの開発言語、フレームワークが乱立しており、バージョンアップのサイクルもかなり速くなっています

そのためサポート期間もかなり短くなってしまい、それらを使ってシステム構築・運用するエンジニアは日々アップデートに追われることになります

対処が難しいこともありますが、少なくとも EOL がいつなのかは意識しておきましょう