Repository: coryodaniel/bonny
Pull request #167 (2022-10-29 13:58:46)

Prepare 1.0.0-rc.0


Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 540a7ae..174c72e 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -74,7 +74,7 @@ jobs:
         run: |
           MIX_ENV=test mix compile
           MIX_ENV=test mix bonny.gen.manifest -o - | kubectl apply -f -
-          TEST_WAIT_TIMEOUT=50000 TEST_KUBECONFIG=$KUBECONFIG mix coveralls.github --include integration --timeout 60000
+          TEST_WAIT_TIMEOUT=10000 TEST_KUBECONFIG=$KUBECONFIG mix coveralls.github --include integration --timeout 12000
 
       # Create PLTs if no cache was found
       - name: Create PLTs
diff --git a/.github/workflows/k8s_matrix.yaml b/.github/workflows/k8s_matrix.yaml
index d53c550..a0789e2 100644
--- a/.github/workflows/k8s_matrix.yaml
+++ b/.github/workflows/k8s_matrix.yaml
@@ -44,4 +44,4 @@ jobs:
         run: |
           MIX_ENV=test mix compile
           MIX_ENV=test mix bonny.gen.manifest -o - | kubectl apply -f -
-          TEST_WAIT_TIMEOUT=50000 TEST_KUBECONFIG=$KUBECONFIG mix test --only integration --timeout 60000
+          TEST_WAIT_TIMEOUT=10000 TEST_KUBECONFIG=$KUBECONFIG mix test --only integration --timeout 12000
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0d7b9b4..d438209 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,20 +11,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 <!-- ### Added | Changed | Deprecated | Removed | Fixed | Security -->
 
+<!-- No new entries below this line! -->
+
+## [1.0.0-rc.0] - 2022-10-29
+
+### Changed
+
+With Version 1.0.0, Bonny got a thorough refactoring. Besides this changelog,
+you might consider the several guides (e.g. the [migration guide](./guides/migration.mid))
+
+- `Bonny.Operator` was introduced as an entry point to the watching and handling
+  of processes. Your controllers are not more added to the supervision tree by
+  bonny. Instead you must create an operator and add that to your application's
+  supervision tree.
+- The [`Pluggable`](https://hex.pm/packages/pluggable) (think [`Plug`](https://hex.pm/packages/plug))
+  library is used with `Bonny.Axn` as token to process `ADDED`, `MODIFIED`,
+  `DELETED` and reconciliation events by `Pluggable` pipelines.
+- `Bonny.ControllerV2` was introduced as a successor to `Bonny.Controller`. It leverages
+  `Pluggable.StepBuilder` (think `Plug.Builder`) to build a pluggable pipeline.
+- `Bonny.Event` and `Bonny.EventRecorder` were introducd for Kubernetes
+  event creation ([#156](https://github.com/coryodaniel/bonny/pull/156), [#5](https://github.com/coryodaniel/bonny/issues/5))
+
+Why this refactoring?
+
+- Allows for better CRD and API version definitions
+- With a `Pluggable` architecture, controllers are much easier to test (Think of `Plug.Conn` tests)
+- With a `Pluggable` architecture, your processing pipelines are easier to customize/extend
+- manifest generation and event processing were decoupled
+- Internally, the amount of macros was reduced which makes Bonny easier to maintain
+
 ### Added
 
-- Added option `skip_observed_generations` to `Bonny.ControllerV2` ([151](https://github.com/coryodaniel/bonny/issues/151), [#153](https://github.com/coryodaniel/bonny/pull/153))
-- `Bonny.ControllerV2` as a successor to `Bonny.Controller`
-  - Allows for better CRD definition ([#149](https://github.com/coryodaniel/bonny/pull/149))
-  - Decouples CRD and controller ([#158](https://github.com/coryodaniel/bonny/pull/158))
-  - Operator wide API version configuration ([#159](https://github.com/coryodaniel/bonny/pull/159))
-  - Adds `Bonny.EventRecorder` for creating Kubernetes events ([#156](https://github.com/coryodaniel/bonny/pull/156), [#5](https://github.com/coryodaniel/bonny/issues/5))
-  - Allows skipping observed generations ([#153](https://github.com/coryodaniel/bonny/pull/153))
+- `Bonny.Pluggable.SkipObservedGenerations` - halts the pipelines for a defined list of actions if the observed generation equals the resource's generation.
+- `Bonny.Pluggable.ApplyDescendants` - applies all the descendants added to the `%Bonny.Axn{}` struct.
+- `Bonny.Pluggable.ApplyStatus` - applies the status of the given `%Bonny.Axn{}` struct to the status subresource.
+- `Bonny.Pluggable.Logger`- logs an action event and when status, descendants and events are applied to the cluster. If desired, it makes sense to be placed as first step in your operator pipeline but can also be added to a controller pipeline.
 - `Bonny.Resource.add_owner_reference/3` used to add the owner reference to resources created by the controller. ([#147](https://github.com/coryodaniel/bonny/pull/147))
-- An integration test suite was added that runs tests agains a "real" kubernetes cluster on the CI pipeline ([#146](https://github.com/coryodaniel/bonny/pull/146), [#84](https://github.com/coryodaniel/bonny/issues/84))
+- An integration test suite was added that runs tests against a "real" kubernetes cluster on the CI pipeline ([#146](https://github.com/coryodaniel/bonny/pull/146), [#84](https://github.com/coryodaniel/bonny/issues/84))
 - Mix task for initializing a new operator `mix bonny.init` ([#160](https://github.com/coryodaniel/bonny/pull/160), [#67](https://github.com/coryodaniel/bonny/issues/67))
 
-<!-- No new entries below this line! -->
+### Deprecated
+
+- `Bonny.Controller` was deprecated in favor of the new design with
+  `Bonny.Operator` and `Bonny.ControllerV2`
 
 ## [0.5.2] - 2022-08-31
 
@@ -44,7 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [0.5.0] - 2022-04-23
 
-Version 0.5.0 comes with some major changes. Please read through the [migration guide](guides/migrations.md) before upgrading.
+Version 0.5.0 comes with some major changes. Please read through the [migration guide](./guides/migrations.md) before upgrading.
 
 ## Added
 
diff --git a/guides/controllers.livemd b/guides/controllers.livemd
index f448967..14f2a2e 100644
--- a/guides/controllers.livemd
+++ b/guides/controllers.livemd
@@ -47,10 +47,10 @@ end
 
 The step(s) you implement are called with a `%Bonny.Axn{}` token. It contains the `action` which triggered this event, the `resource` the event regards and other fields. Use pattern matching or a adispatch mechanism to handle the four different action types:
 
-* `add/1` - resource was created in the cluster.
-* `delete/1` - resource was deleted from the cluster.
-* `modify/1` - resource was modified in the cluster.
-* `reconcile/1` - Called on a regular basis in case we missed an action or to fix diverged state.
+- `add/1` - resource was created in the cluster.
+- `delete/1` - resource was deleted from the cluster.
+- `modify/1` - resource was modified in the cluster.
+- `reconcile/1` - Called on a regular basis in case we missed an action or to fix diverged state.
 
 Your event handlers should return the struct it received as first parameter. However, your controller can use helper functions from the `Bonny.Axn` module to modify it before returning it.
 
@@ -163,7 +163,7 @@ Bonny.Axn.new!(action: :add, conn: nil, resource: resource)
 
 Kubernetes events provide a way to report back to the client. A Kubernetes event always references the object to which the event relates. For a controller the regarding object would be the handled resource. The user can then use `kubectl describe` on the custom resource to see the events.
 
-Use `Bonny.Axn.success_event/2`, `Bonny.Axn.failure_event/2` or `Bonny.Axn.add_event/2` do register events in the `%Bonny.Axn{}` struct. Events are going to be applied to the cluster at the end of the Operator pipeline. There is no need to register a `step` for that.
+Use `Bonny.Axn.success_event/2`, `Bonny.Axn.failure_event/2` or `Bonny.Axn.register_event/6` do register events in the `%Bonny.Axn{}` struct. Events are going to be applied to the cluster at the end of the Operator pipeline. There is no need to register a `step` for that.
 
 ```elixir
 defmodule MyResourceController do
@@ -209,8 +209,8 @@ You can define such rules one by defining the `rbac_rules/0` callback. This call
 
 ```elixir
 @type rbac_rule :: %{
-  apiGroups: list(binary()), 
-  resources: list(binary()), 
+  apiGroups: list(binary()),
+  resources: list(binary()),
   binary()s: list(binary())}
 ```
 
@@ -220,8 +220,8 @@ You can use the helper function `to_rbac_rule/1` to convert a tuple to an rbac r
 
 ```elixir
   @spec to_rbac_rule({
-    binary() | list(binary()), 
-    binary() | list(binary()), 
+    binary() | list(binary()),
+    binary() | list(binary()),
     binary() | list(binary())
   }) :: rbac_rule
 ```
diff --git a/guides/migrations.md b/guides/migrations.md
index 8455544..604083a 100644
--- a/guides/migrations.md
+++ b/guides/migrations.md
@@ -18,7 +18,7 @@ controllers and bring it into a `Pluggable` form.
 #### Step 1: Create your Opeator
 
 Create a new module (e.g. `YourProject.Operator`) which uses `use Bonny.Operator`,
-implement `crds/0` and `controllers/2` according to the [Operator Guide](./operators.livemd)
+implement `crds/0` and `controllers/2` according to the [Operator Guide](./the_operator.livemd)
 and add it to your supervision tree.
 
 #### Step 2: Create your API versions
diff --git a/lib/bonny/api/crd.ex b/lib/bonny/api/crd.ex
index d2bbc9c..ac6f7ac 100644
--- a/lib/bonny/api/crd.ex
+++ b/lib/bonny/api/crd.ex
@@ -2,10 +2,9 @@ defmodule Bonny.API.CRD do
   @moduledoc """
   A Custom Resource Definition.
 
-  The %Bonny.API.CRD{} struct contains the fields `group`, `resource_type`,
+  The `%Bonny.API.CRD{}` struct contains the fields `group`, `resource_type`,
   `scope` and `version`. New definitions can be created directly, using the
-  `new!/1` function or from inside a controller using the
-  `build_for_controller!/1` macro.
+  `new!/1` function.
   """
 
   @kind "CustomResourceDefinition"
diff --git a/lib/bonny/api/version.ex b/lib/bonny/api/version.ex
index a23297e..63264f0 100644
--- a/lib/bonny/api/version.ex
+++ b/lib/bonny/api/version.ex
@@ -1,9 +1,9 @@
 defmodule Bonny.API.Version do
-  @moduledoc """
+  @moduledoc ~S"""
   Describes an API version of a custom resource.
 
-  The %Bonny.API.Version{} struct contains the fields required to build the manifest
-  for this version.
+  The `%Bonny.API.Version{}` struct contains the fields required to build the
+  manifest for this version.
 
   This module is meant to be `use`d by a module representing the
   API version of a custom resource. The using module has to define
@@ -14,50 +14,44 @@ defmodule Bonny.API.Version do
   If no name is passed, The macro will use the using module's name as the
   version name.
 
-  The Option `hub` has to be `true` for exactly one version of your CRD.
-  The `defaults/1` macro will set the `storage` flag to the value passed via the
-  `hub` option.
+  **Note: The `:storage` flag has to be `true` for exactly one version of a
+  CRD.**
 
-  ```
-  defmodule MyOperator.API.V1.CronTab do
-    use Bonny.API.Version
+      defmodule MyOperator.API.V1.CronTab do
+        use Bonny.API.Version
 
-    def manifest() do
-      struct!(defaults(), storage: true)
-    end
-  ```
-
-  ### Use the `manifest/0` callback to override the defaults, e.g. add a schema:
+        def manifest() do
+          struct!(defaults(), storage: true)
+        end
 
+  Use the `manifest/0` callback to override the defaults, e.g. add a schema.
+  Pipe your struct into `add_observed_generation_status/1` - which is imported
+  into the using module - if you use the
+  `Bonny.Pluggable.SkipObservedGenerations` step in your controller
 
-  ```
-  defmodule MyOperator.API.V1.CronTab do
-    use Bonny.API.Version
+      defmodule MyOperator.API.V1.CronTab do
+        use Bonny.API.Version
 
-    def manifest() do
-      struct!(
-        defaults(),
-        storage: true,
-        schema: %{
-          openAPIV3Schema: %{
-            type: :object,
-            properties: %{
-              spec: %{
+        def manifest() do
+          struct!(
+            defaults(),
+            storage: true,
+            schema: %{
+              openAPIV3Schema: %{
+                type: :object,
+                properties: %{
+                  spec: %{
+                }
+              }
             }
-          }
-        }
-      )
-    end
-  ```
-
+          )
+        end
   """
 
-  # Use this behviour when implementing conversion webhooks
-  # defmodule NonHub do
-  #   @callback convert_to(Bonny.Resource.t()) :: Bonny.Resource.t()
-  #   @callback convert_from(Bonny.Resource.t()) :: Bonny.Resource.t()
-  # end
-
+  @doc """
+  Return a `%Bonny.API.Version{}` struct representing the manifest for this
+  version of the CRD API.
+  """
   @callback manifest() :: Bonny.API.Version.t()
 
   @typedoc """
@@ -152,6 +146,10 @@ defmodule Bonny.API.Version do
     end
   end
 
+  @doc """
+  Returns a `Bonny.API.Version` struct with default values. Use this and pipe
+  it into `struct!()` to override the defaults in your `manifest/0` callback.
+  """
   defmacro defaults() do
     name = __extract_version__(__CALLER__.module)
 
@@ -178,7 +176,9 @@ defmodule Bonny.API.Version do
 
   ### Example
 
-      iex> Bonny.API.Version.add_observed_generation_status(%{})
+      iex> %Bonny.API.Version{}
+      ...> |> Bonny.API.Version.add_observed_generation_status()
+      ...> |> Map.take([:subresources, :schema])
       %{
         subresources: %{status: %{}},
         schema: %{
@@ -191,7 +191,8 @@ defmodule Bonny.API.Version do
                   observedGeneration: %{type: :integer}
                 }
               }
-            }
+            },
+            "x-kubernetes-preserve-unknown-fields": true,
           }
         }
       }
diff --git a/lib/bonny/axn.ex b/lib/bonny/axn.ex
index 3a0fbce..a236156 100644
--- a/lib/bonny/axn.ex
+++ b/lib/bonny/axn.ex
@@ -5,6 +5,16 @@ defmodule Bonny.Axn do
   This is the token passed to all steps of your operator and controller
   pipeline.
 
+  This module gets imported to your controllers where you should use the
+  functions `register_descendant/3`, `update_status/2` and the ones to register
+  events: `success_event/2`, `failure_event/2` and/or `register_event/6`. Note
+  that these functions raise exceptions if those resources have already been
+  applied to the cluster.
+
+  The `register_before_*` functions can be used in `Pluggable` steps in order
+  to register callbacks that are called before applying resources to the
+  cluster. Have a look at `Bonny.Pluggable.Logger` for a use case.
+
   ## Action event fields
 
   These fields contain information on the action event that occurred.
@@ -114,7 +124,10 @@ defmodule Bonny.Axn do
     """
   end
 
-  @spec event(
+  @doc """
+  Registers a Kubernetes event to the `%Axn{}` token to be emitted by Bonny.
+  """
+  @spec register_event(
           t(),
           Resource.t() | nil,
           Event.event_type(),
@@ -122,7 +135,7 @@ defmodule Bonny.Axn do
           binary(),
           binary()
         ) :: t()
-  def event(
+  def register_event(
         axn,
         related \\ nil,
         event_type,
diff --git a/lib/bonny/controller_v2.ex b/lib/bonny/controller_v2.ex
index 82eeeb3..7ac1ec3 100644
--- a/lib/bonny/controller_v2.ex
+++ b/lib/bonny/controller_v2.ex
@@ -11,6 +11,13 @@ defmodule Bonny.ControllerV2 do
 
       step_name(Bonny.Axn.t(), keyword()) :: Bonny.Axn.t()
 
+  The modules `Bonny.Axn` module is imported to your controller. In your event
+  handler step you should use the functions `Bonny.Axn.register_descendant/3`,
+  `Bonny.Axn.update_status/2` and the ones to  register events:
+  `Bonny.Axn.success_event/2`, `Bonny.Axn.failure_event/2` and/or
+  `Bonny.Axn.register_event/6`. Note that these functions raise
+  exceptions if those resources have already been applied to the cluster.
+
   ## Example
 
   Match against the struct's `:action` field which is one of `:add`, `:modify`,
@@ -33,6 +40,49 @@ defmodule Bonny.ControllerV2 do
           axn
         end
       end
+
+  Registering your descendants with the `%Bonny.Axn{}` token makes your
+  controller easier to test. Be sure to add `Bonny.Pluggable.ApplyDescendants`
+  as step to your operator in order for the descendants to be applied to the
+  cluster.
+
+      defmodule MyOperator.Controller.CronTabController do
+
+        # other steps
+        step :handle_event
+        # other steps
+
+        # apply the resource
+        def handle_event(axn, _opts) do
+          deployment = generate_deployment(axn.resource)
+
+          axn
+          |> register_descendant(deployment)
+          |> success_event()
+        end
+      end
+
+  Use `Bonny.Axn.update_status/2` to store API responses or other status data in the
+  resource status. Be sure to enable the status subresource in your CRD version
+  module.
+
+      defmodule MyOperator.Controller.CronTabController do
+
+        # other steps
+        step :handle_event
+        # other steps
+
+        # apply the resource
+        def handle_event(axn, _opts) do
+          response = apply_state(axn.resource)
+
+          axn
+          |> update_status(fn status ->
+            Map.put(status, "response", response)
+          end)
+          |> success_event()
+        end
+      end
   """
 
   use Supervisor
@@ -48,7 +98,9 @@ defmodule Bonny.ControllerV2 do
   defmacro __using__(_opts) do
     quote do
       use Pluggable.StepBuilder
+
       import Bonny.Axn
+
       import Bonny.ControllerV2, only: [to_rbac_rule: 1]
       @behaviour Bonny.ControllerV2
 
diff --git a/lib/bonny/operator.ex b/lib/bonny/operator.ex
index e13dec8..610f628 100644
--- a/lib/bonny/operator.ex
+++ b/lib/bonny/operator.ex
@@ -3,7 +3,7 @@ defmodule Bonny.Operator do
   Defines a Bonny operator.
 
   The operator defines custom resources, watch queries and their
-  controllers and serves as the entry point to the watching and handling
+  controllers and serves as the entry point to the watching and handling of
   processes.
 
   Overall, an operator has the following responsibilities:
diff --git a/lib/bonny/periodic_task.ex b/lib/bonny/periodic_task.ex
index a02279d..9d4fb6a 100644
--- a/lib/bonny/periodic_task.ex
+++ b/lib/bonny/periodic_task.ex
@@ -60,7 +60,7 @@ defmodule Bonny.PeriodicTask do
   @doc "Registers and starts a new `Bonny.PeriodicTask`"
   @spec register(t()) :: {:ok, pid} | {:error, term()}
   def register(%__MODULE__{id: id} = task) do
-    Logger.info("Task registered", %{id: id})
+    Logger.info("Task registered", %{id: id, library: :bonny})
     DynamicSupervisor.start_child(__MODULE__, {Bonny.PeriodicTask.Runner, task})
   end
 
@@ -69,7 +69,7 @@ defmodule Bonny.PeriodicTask do
   def unregister(%__MODULE__{id: id}), do: unregister(id)
 
   def unregister(id) when is_atom(id) do
-    Logger.info("Task unregistered", %{id: id})
+    Logger.info("Task unregistered", %{id: id, library: :bonny})
 
     case Process.whereis(id) do
       nil ->
diff --git a/lib/bonny/periodic_task/runner.ex b/lib/bonny/periodic_task/runner.ex
index f0add09..8c14734 100644
--- a/lib/bonny/periodic_task/runner.ex
+++ b/lib/bonny/periodic_task/runner.ex
@@ -11,14 +11,14 @@ defmodule Bonny.PeriodicTask.Runner do
 
   @impl true
   def init(%PeriodicTask{} = task) do
-    Logger.info("Task initialized", %{id: task.id})
+    Logger.info("Task initialized", %{id: task.id, library: :bonny})
     Process.send_after(self(), :run, calc_offset(task))
     {:ok, task}
   end
 
   @impl true
   def handle_info(:run, %PeriodicTask{} = task) do
-    metadata = %{id: task.id}
+    metadata = %{id: task.id, library: :bonny}
 
     :telemetry.span([:task, :execution], metadata, fn ->
       Logger.info("Task execution started", metadata)
diff --git a/lib/bonny/pluggable/skip_observed_generations.ex b/lib/bonny/pluggable/skip_observed_generations.ex
index cdc7158..19c8287 100644
--- a/lib/bonny/pluggable/skip_observed_generations.ex
+++ b/lib/bonny/pluggable/skip_observed_generations.ex
@@ -6,8 +6,8 @@ defmodule Bonny.Pluggable.SkipObservedGenerations do
 
   ## Options
 
-  `:actions` - The actions for which this rule applies. Defaults to `[:add, :modify]`.
-  `:observed_generation_key` - The resource status key where the observed generation is stored. This will be passed to `Kernel.get_in()`. Defaults to `["status", "observedGeneration"]`.
+  * `:actions` - The actions for which this rule applies. Defaults to `[:add, :modify]`.
+  * `:observed_generation_key` - The resource status key where the observed generation is stored. This will be passed to `Kernel.get_in()`. Defaults to `["status", "observedGeneration"]`.
 
   ## Usage
 
diff --git a/lib/bonny/server/async_stream_runner.ex b/lib/bonny/server/async_stream_runner.ex
index 0c1d458..f7b761b 100644
--- a/lib/bonny/server/async_stream_runner.ex
+++ b/lib/bonny/server/async_stream_runner.ex
@@ -49,7 +49,7 @@ defmodule Bonny.Server.AsyncStreamRunner do
     stream = Keyword.fetch!(args, :stream)
     termination_delay = Keyword.get(args, :termination_delay)
 
-    Logger.debug("Starting #{name}")
+    Logger.debug("AsyncStreamRunner - Starting #{name}", library: :bonny)
 
     {:ok, pid} = Task.start_link(__MODULE__, :run, [stream, termination_delay])
 
@@ -62,7 +62,7 @@ defmodule Bonny.Server.AsyncStreamRunner do
   def run(stream, termination_delay) do
     Stream.run(stream)
 
-    Logger.debug("Stream terminated")
+    Logger.debug("AsyncStreamRunner - Stream terminated", library: :bonny)
 
     if !is_nil(termination_delay), do: Process.sleep(termination_delay)
   end
diff --git a/lib/bonny/server/reconciler.ex b/lib/bonny/server/reconciler.ex
index 1c70004..5259b90 100644
--- a/lib/bonny/server/reconciler.ex
+++ b/lib/bonny/server/reconciler.ex
@@ -37,12 +37,12 @@ defmodule Bonny.Server.Reconciler do
   end
 
   defp fetch_succeeded?({:error, error}) do
-    Logger.debug("Reconciler fetch failed", %{error: error})
+    Logger.debug("Reconciler fetch failed", %{error: error, library: :bonny})
     false
   end
 
   defp fetch_succeeded?(resource) when is_map(resource) do
-    Logger.debug("Reconciler fetch succeeded")
+    Logger.debug("Reconciler fetch succeeded", library: :bonny)
     true
   end
 
@@ -52,7 +52,8 @@ defmodule Bonny.Server.Reconciler do
       name: K8s.Resource.name(resource),
       namespace: K8s.Resource.namespace(resource),
       kind: K8s.Resource.kind(resource),
-      api_version: resource["apiVersion"]
+      api_version: resource["apiVersion"],
+      library: :bonny
     }
 
     :telemetry.span([:reconciler, :reconcile], metadata, fn ->
diff --git a/lib/bonny/server/scheduler.ex b/lib/bonny/server/scheduler.ex
index e76a0d8..27e9b6d 100644
--- a/lib/bonny/server/scheduler.ex
+++ b/lib/bonny/server/scheduler.ex
@@ -173,7 +173,7 @@ defmodule Bonny.Server.Scheduler do
     op = K8s.Client.list("v1", :nodes)
 
     response = K8s.Client.stream(conn, op)
-    metadata = %{operation: op}
+    metadata = %{operation: op, library: :bonny}
 
     case response do
       {:ok, stream} ->
diff --git a/lib/bonny/server/scheduler/binding.ex b/lib/bonny/server/scheduler/binding.ex
index 3400df4..0a7777d 100644
--- a/lib/bonny/server/scheduler/binding.ex
+++ b/lib/bonny/server/scheduler/binding.ex
@@ -50,7 +50,7 @@ defmodule Bonny.Server.Scheduler.Binding do
   def create(conn, pod, node) do
     binding = new(pod, node)
     operation = K8s.Client.create(pod, binding)
-    metadata = %{operation: operation}
+    metadata = %{operation: operation, library: :bonny}
 
     :telemetry.span([:scheduler, :binding], metadata, fn ->
       case K8s.Client.run(conn, operation) do
diff --git a/mix.exs b/mix.exs
index ca7ca08..1a7fb55 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,6 +1,6 @@
 defmodule Bonny.MixProject do
   use Mix.Project
-  @version "0.5.2"
+  @version "1.0.0-rc.0"
   @source_url "https://github.com/coryodaniel/bonny"
 
   def project do
@@ -98,9 +98,13 @@ defmodule Bonny.MixProject do
       extras: [
         "README.md",
         "CHANGELOG.md",
+        "guides/contributing.md",
+        "guides/controllers.livemd",
+        "guides/crd_versions.livemd",
         "guides/migrations.md",
-        "guides/testing.md",
-        "guides/controllers.livemd"
+        "guides/mix_tasks.md",
+        "guides/testing.livemd",
+        "guides/the_operator.livemd"
       ],
       groups_for_extras: [
         Guides: Path.wildcard("guides/*.md")


Test patch:
diff --git a/lib/bonny/axn/test.ex b/lib/bonny/axn/test.ex
index aa241ef..0c039b0 100644
--- a/lib/bonny/axn/test.ex
+++ b/lib/bonny/axn/test.ex
@@ -5,7 +5,7 @@ defmodule Bonny.Axn.Test do
   This module can be used in your test cases, like this:
 
       use ExUnit.Case, async: true
-      use Plug.Test
+      use Bonny.Axn.Test
 
   Using this module will:
 
diff --git a/test/bonny/axn_test.exs b/test/bonny/axn_test.exs
index 95acd64..fc49bff 100644
--- a/test/bonny/axn_test.exs
+++ b/test/bonny/axn_test.exs
@@ -47,7 +47,8 @@ defmodule Bonny.AxnTest do
 
   describe "event/5 resp. event/6" do
     test "adds an without related object to an empty list of events", %{axn: axn} do
-      result_axn = MUT.event(axn, :Normal, "Reason for the action", "add", "Custom Event Message")
+      result_axn =
+        MUT.register_event(axn, :Normal, "Reason for the action", "add", "Custom Event Message")
 
       assert 1 == length(result_axn.events)
 
@@ -76,7 +77,14 @@ defmodule Bonny.AxnTest do
       related: related
     } do
       result_axn =
-        MUT.event(axn, related, :Normal, "Reason for the action", "add", "Custom Event Message")
+        MUT.register_event(
+          axn,
+          related,
+          :Normal,
+          "Reason for the action",
+          "add",
+          "Custom Event Message"
+        )
 
       assert 1 == length(result_axn.events)
 
@@ -117,7 +125,7 @@ defmodule Bonny.AxnTest do
         )
 
       result_axn =
-        MUT.event(
+        MUT.register_event(
           struct!(axn, events: [dummy_event]),
           :Normal,
           "Reason for the action",
diff --git a/test/bonny/controller_v2_integration_test.exs b/test/bonny/controller_v2_integration_test.exs
index 1e81c5b..c7e01b2 100644
--- a/test/bonny/controller_v2_integration_test.exs
+++ b/test/bonny/controller_v2_integration_test.exs
@@ -28,6 +28,9 @@ defmodule Bonny.ControllerV2IntegrationTest do
 
     {:ok, _} = Supervisor.start_link([{Bonny.Test.Operator, conn: conn}], strategy: :one_for_one)
 
+    # Give watch process some time to start.
+    Process.sleep(600)
+
     [timeout: timeout]
   end
 
diff --git a/test/bonny/periodic_task_test.exs b/test/bonny/periodic_task_test.exs
index c61a014..dbff14b 100644
--- a/test/bonny/periodic_task_test.exs
+++ b/test/bonny/periodic_task_test.exs
@@ -103,7 +103,7 @@ defmodule Bonny.PeriodicTaskTest do
       {_task_pid, agent_name2} = make_increment_task(:task2, 25)
 
       :timer.sleep(50)
-      assert Agent.get(agent_name1, & &1) in 8..10
+      assert Agent.get(agent_name1, & &1) in 7..10
       assert Agent.get(agent_name2, & &1) in 1..2
       DynamicSupervisor.stop(PeriodicTask)
     end