1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

人に運勢があるなら、HTTPリクエストにも運勢があるはず(謎

Last updated at Posted at 2023-12-21

本記事は富士通クラウドテクノロジーズ Advent Calendar 2023 21日目の記事です。
昨日の記事は@ometora_sateyanPython初学者がToDoリストリマインダーbotを作ってみたでした。
個人的にこういったBotのトリガーにGitLabのPipeline Scheduleを利用するのはおもしろい発想だなーと思いました。軽いBotであれば、Pipeline Schedule -> Gitlab-CI -> Incoming Webhook -> Slackでシュッと作れてますね。


TL;DR

HTTPリクエストのヘッダーに一定の確率でGoodを設定するTraefikのPluginをシュッと作りました。

おわり

はじめに

TraefikはEdge Routerと呼ばれるものであり、リバースプロキシーやロードバランサーとして利用することができるOSSです。詳細は下記を参照してください。

また、Traefikでは、ユーザの要件に合わせてPluginを作成し組み込むことも可能です。
いくつかPlugin Catalogで公開されているので、眺めてみると役に立つPluginがあるかもしれません。

今回はこのPluginをシュッと作りたいと思います。

Pluginの作成

HTTPリクエストの運勢をHeaderに差し込むPluginを作りたいと思います。
作成するPluginの処理としては以下になります。

  • HeaderにKey:X-FORTUNEで運勢を追加する
  • 運勢はGoodまたはBadになる
  • 確率RateでGoodになる。それ以外はBadになる

はい、シュッと

// Config the plugin configuration.
type Config struct {
	Rate int `json:"rate,omitempty"`
}

// CreateConfig creates the default plugin configuration.
func CreateConfig() *Config {
	return &Config{
		Rate: 0,
	}
}

// Lottery.
type Lottery struct {
	next     http.Handler
	rate     int
	name     string
	template *template.Template
}

// New created a new Demo plugin.
func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
	return &Lottery{
		rate:     config.Rate,
		next:     next,
		name:     name,
		template: template.New("lottery").Delims("[[", "]]"),
	}, nil
}

func (l *Lottery) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	rand.Seed(time.Now().UnixNano())

	fortune := rand.Intn(100) + 1
	os.Stdout.WriteString(fmt.Sprintf("fortune/Rate: %v/%v \n", fortune, l.rate))
	if l.rate < fortune {
		req.Header.Set("X-FORTUNE", "Bad")
	} else {
		req.Header.Set("X-FORTUNE", "Good")
	}

	l.next.ServeHTTP(rw, req)
}

Defining a Pluginを参照するにPluginで作成する必要があるものは下記の3種類見たいです。

  • type Config struct { ... }
    • Dynamic Configurationで設定したい項目(後述)
  • func CreateConfig() *Config
    • Configの作成
  • func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error)
    • Pluginで行う処理
    • X-FORTUNEヘッダーを挿入する処理

ログ出力に関して

Logs曰く、Log出力する方法はos.Stdout.WriteString("...")またはos.Stderr.WriteString("...")を使用するしかないみたいです。

traefik.yaml

Pluginの実態以外に、.traefik.yamlが必要になります。
作成しない場合、TraefikでPluginを読み込む際に下記のエラーが発生します。

msg="Plugins are disabled because an error has occurred." error="1 error occurred:\n\t* failed to open the plugin manifest plugins-local/src/github.com/ystkfujii/traefik-plugin-fortune/.traefik.yml: open plugins-local/src/github.com/ystkfujii/traefik-plugin-fortune/.traefik.yml: no such file or directory\n\n"

.traefik.yamlの設定項目は下記になります。
内容はManifestの内容をコピペして日本語訳したものになります。

#【必須】Plugins Catalog web UIで表示する名前
displayName: Name of your plugin

#【必須】現在は`middleware`のみ設定可能
type: middleware

#【必須】PluginのImport Path
import: github.com/username/my-plugin

#【必須】Pluginの簡単な説明
summary: Description of what my plugin is doing

#【任意】Pluginに関連するメディア
iconPath: foo/icon.png
bannerPath: foo/banner.png

# 【必須】
# Pluginの確認用のデータ
# Plugins Catalogは起動時の有効性のテストのひとつとして、下記のデータでPluginをテストする
testData:
  Headers:
    Foo: Bar

pluginのパッケージ名について

最初はパッケージ名をmainに設定して作成してたんですが、TraefikでPluginを読み込む際に下記エラーが発生してうまく読み込めませんでした。

Plugins are disabled because an error has occurred." error="failed to eval New: 1:28: undefined: traefik_plugin_fortune"

下記を参照しパッケージ名をPlugin名にすることで解決しました。

https://github.com/traefik/plugindemo/issues/15

Dockerfile

上記で作成したPluginを読み込んで動作確認するためのdocker composeファイルは以下になります。
今回はRateを30に設定しているので、30%でX-FORTUNEヘッダーが"Good"になります。

services:
  traefik:
    image: "traefik:v2.10"
    container_name: "traefik"
    command:
      #- "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      # Plugin Catalogから読み込む際にコメントアウト
      #- "--experimental.plugins.lottery.modulename=github.com/ystkfujii/traefik-plugin-fortune"
      #- "--experimental.plugins.lottery.version=v0.0.1"
      # ローカルから読み込む
      - "--experimental.localPlugins.lottery.moduleName=github.com/ystkfujii/traefik-plugin-fortune"
    ports:
      - "80:80"
    volumes:
      - ".:/plugins-local/src/github.com/ystkfujii/traefik-plugin-fortune"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  whoami:
    image: "traefik/whoami"
    container_name: "simple-service"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=PathPrefix(`/who`)"
      - "traefik.http.routers.whoami.entrypoints=web"
      # traefik-plugin-fortuneの設定
      - "traefik.http.routers.whoami.middlewares=my-plugin"
      - "traefik.http.middlewares.my-plugin.plugin.lottery.Rate=30"

動作確認

dockerイメージの起動

docker compose up

アクセス確認!
30%の確率でX-Fortune: Goodになります。

$ curl localhost/who
Hostname: a3c72290d619
IP: 127.0.0.1
IP: 192.168.144.3
RemoteAddr: 192.168.144.2:39244
GET /who HTTP/1.1
Host: localhost
User-Agent: curl/7.81.0
Accept: */*
Accept-Encoding: gzip
X-Fortune: Good
X-Forwarded-For: 192.168.144.1
X-Forwarded-Host: localhost
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: 6733240255a7
X-Real-Ip: 192.168.144.1

おわり!


明日は@unecchiの「Notion APIでWeb API操作を勉強してみた」です。
お楽しみに!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?