From 696b3dbfcb55b45753fbaa99e83eb4d9f8a3de07 Mon Sep 17 00:00:00 2001 From: Michael Ruoss Date: Sat, 1 Apr 2023 10:14:10 +0200 Subject: [PATCH] next approach --- .../admission_control/admission_review.ex | 12 ++- lib/bonny/admission_control/plug.ex | 82 +++++++++++++++++++ lib/bonny/admission_control/webhook_router.ex | 77 ----------------- 3 files changed, 90 insertions(+), 81 deletions(-) create mode 100644 lib/bonny/admission_control/plug.ex delete mode 100644 lib/bonny/admission_control/webhook_router.ex diff --git a/lib/bonny/admission_control/admission_review.ex b/lib/bonny/admission_control/admission_review.ex index a0fdd2d..2def083 100644 --- a/lib/bonny/admission_control/admission_review.ex +++ b/lib/bonny/admission_control/admission_review.ex @@ -5,15 +5,19 @@ defmodule Bonny.AdmissionControl.AdmissionReview do require Logger + @derive Pluggable.Token + + @type webhook_type :: :mutating | :validating @type t :: %__MODULE__{ request: map(), response: map(), - webhook_type: :mutating | :validating + webhook_type: webhook_type(), + halted: boolean(), + assigns: map() } - @fields [:request, :response, :webhook_type] - @enforce_keys @fields - defstruct @fields + @enforce_keys [:request, :response, :webhook_type] + defstruct [:request, :response, :webhook_type, halted: false, assigns: %{}] def new(%{"kind" => "AdmissionReview", "request" => request}, webhook_type) do struct!(__MODULE__, diff --git a/lib/bonny/admission_control/plug.ex b/lib/bonny/admission_control/plug.ex new file mode 100644 index 0000000..17c6e5f --- /dev/null +++ b/lib/bonny/admission_control/plug.ex @@ -0,0 +1,82 @@ +defmodule Bonny.AdmissionControl.Plug do + use Plug.Builder + + alias Bonny.AdmissionControl.AdmissionReview + + require Logger + + @api_version "admission.k8s.io/v1" + @kind "AdmissionReview" + + plug(Plug.Parsers, + parsers: [:urlencoded, :json], + json_decoder: Jason + ) + + @impl true + def init(opts) do + if opts[:webhook_type] not in [:mutating, :validating] do + raise(CompileError, + file: __ENV__.file, + line: __ENV__.line, + description: + "#{__MODULE__} requires you to define the :webhook_type option as :mutating or :validating when plugged." + ) + end + + webhook_handler = + case opts[:webhook_handler] do + {module, init_opts} -> + {module, module.init(init_opts)} + + nil -> + raise(CompileError, + file: __ENV__.file, + line: __ENV__.line, + description: "#{__MODULE__} requires you to set the :webhook_handler option." + ) + + module -> + {module, []} + end + + %{ + webhook_type: opts[:webhook_type], + webhook_handler: webhook_handler + } + end + + @doc false + @impl true + def call(conn, webhook_config) do + %{ + webhook_type: webhook_type, + webhook_handler: {webhook_handler, opts} + } = webhook_config + + conn + |> super([]) + |> Map.get(:body_params) + |> AdmissionReview.new(webhook_type) + |> tap(fn review -> + Logger.debug("Processing Admission Review Request", library: :bonny, review: review) + end) + |> AdmissionReview.allow() + |> webhook_handler.call(opts) + |> encode_response() + |> send_response(conn) + end + + defp send_response(response_body, conn) do + conn + |> put_resp_content_type("application/json") + |> send_resp(200, response_body) + end + + @spec encode_response(AdmissionReview.t()) :: binary() + defp encode_response(admission_review) do + %{"apiVersion" => @api_version, "kind" => @kind} + |> Map.put("response", admission_review.response) + |> Jason.encode!() + end +end diff --git a/lib/bonny/admission_control/webhook_router.ex b/lib/bonny/admission_control/webhook_router.ex deleted file mode 100644 index 6c665c5..0000000 --- a/lib/bonny/admission_control/webhook_router.ex +++ /dev/null @@ -1,77 +0,0 @@ -defmodule Bonny.AdmissionControl.WebhookRouter do - import Plug.Conn - - alias Bonny.AdmissionControl.AdmissionReview - - require Logger - - @api_version "admission.k8s.io/v1" - @kind "AdmissionReview" - - @callback process(AdmissionReview.t()) :: AdmissionReview.t() - - defmacro __using__(_opts) do - quote do - use Plug.Builder - - alias Bonny.AdmissionControl.WebhookRouter - - @behaviour WebhookRouter - - plug(Plug.Parsers, - parsers: [:urlencoded, :json], - json_decoder: Jason - ) - - def init(opts) do - WebhookRouter.plug_init(opts, __MODULE__) - end - - def call(%Plug.Conn{} = conn, webhook_type) do - conn - |> super([]) - |> WebhookRouter.process_request(__MODULE__, webhook_type) - end - end - end - - @doc false - def plug_init(opts, module) do - if opts[:webhook_type] not in [:mutating, :validating] do - raise(CompileError, - file: __ENV__.file, - line: __ENV__.line, - description: - "#{module} requires you to define the :webhook_type as :mutating or :validating when plugged." - ) - end - - opts[:webhook_type] - end - - @doc false - def process_request(conn, router, webhook_type) do - conn.body_params - |> AdmissionReview.new(webhook_type) - |> tap(fn review -> - Logger.debug("Processing Admission Review Request", library: :bonny, review: review) - end) - |> AdmissionReview.allow() - |> router.process() - |> encode_response() - |> send_response(conn) - end - - defp send_response(response_body, conn) do - conn - |> put_resp_content_type("application/json") - |> send_resp(200, response_body) - end - - @spec encode_response(AdmissionReview.t()) :: binary() - defp encode_response(admission_review) do - %{"apiVersion" => @api_version, "kind" => @kind} - |> Map.put("response", admission_review.response) - |> Jason.encode!() - end -end