Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a pluggable step for adding missing gvk information to resources #184

Merged
merged 6 commits into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

<!-- ### Added | Changed | Deprecated | Removed | Fixed | Security -->

### Added

- A pluggable step for adding missing GVK information to resources - [#184](https://github.com/coryodaniel/bonny/pull/184), [#183](https://github.com/coryodaniel/bonny/issues/183)

<!-- No new entries below this line! -->

## [1.0.1] - 2023-02-04
Expand Down
23 changes: 12 additions & 11 deletions guides/the_operator.livemd
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ The operator defines custom resources, watch queries and their controllers and s

Overall, an operator has the following responsibilities:

* to provide a wrapper for starting and stopping the
- to provide a wrapper for starting and stopping the
operator as part of a supervision tree
* To define the resources to be watched together with the
- To define the resources to be watched together with the
controllers which handle action events on those resources.
* to define an initial pluggable pipeline containing the step `:delegate_to_controller` for all action events
- to define an initial pluggable pipeline containing the step `:delegate_to_controller` for all action events
to pass through
* To define any custom resources ending up in the manifest
- To define any custom resources ending up in the manifest
generated by `mix bonny.gen.manifest`

```elixir
Expand Down Expand Up @@ -104,8 +104,8 @@ crds

In `controllers/2` we define the queries and their event handlers, i.e. controllers. function should return a list where each element of the list is a map with these 2 keys:

* `:query` - A list operation of type `K8s.Operation.t()`. Bonny will watch the cluster with this operation and forward all events to the `:controller`.
* `:controller` - A controller (See the [controller guide](controllers.livemd)) or any other `Pluggable` step. Accepts a module or a `{controller :: module(), init_opts :: keyword()}` tuple. If a tuple is given, the init_opts are passed to the controller's `init/1` function.
- `:query` - A list operation of type `K8s.Operation.t()`. Bonny will watch the cluster with this operation and forward all events to the `:controller`.
- `:controller` - A controller (See the [controller guide](controllers.livemd)) or any other `Pluggable` step. Accepts a module or a `{controller :: module(), init_opts :: keyword()}` tuple. If a tuple is given, the init_opts are passed to the controller's `init/1` function.

If you managed to define a valid `conn` above, you can now run the operator defined above in this livebook. The code below starts the operator and shows the supervision tree. Note how the operator starts an `EventRecorder` and two proceses for each controller defined in `controllers/2`. These two processes are the `Watcher` and the `Reconciler`. The `Watcher` watches for `ADD`, `MODIFY` and `DELETE` events in the cluster. The `Reconciler` regularly creates `:reconcile` events for each resource found in the cluster.

Expand Down Expand Up @@ -139,8 +139,9 @@ The operator implements a [Pluggable](https://hex.pm/packages/pluggable) pipelin

Bonny comes with a few steps to your convenience. In most caes it makes sense to add at least `Bonny.Pluggable.ApplyDescendants` and `Bonny.Pluggable.ApplyStatus` to the end of your operator pipeline.

* `Bonny.Pluggable.AddManagedByLabelToDescendants` - Adds the `app.kubernetes.io/managed-by` label to all descendants registered within the pipeline.
* `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.Pluggable.SkipObservedGenerations` - halts the pipelines for a defined list of actions if the observed generation equals the resource's generation. You'll find further documentation on this module in the [Controller](controllers.livemd) guide.
- `Bonny.Pluggable.AddManagedByLabelToDescendants` - Adds the `app.kubernetes.io/managed-by` label to all descendants registered within the pipeline.
- `Bonny.Pluggable.AddMissingGVK` - Add fields `apiVersion` and `kind` to the resource if they are missing.
- `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.Pluggable.SkipObservedGenerations` - halts the pipelines for a defined list of actions if the observed generation equals the resource's generation. You'll find further documentation on this module in the [Controller](controllers.livemd) guide.
37 changes: 37 additions & 0 deletions lib/bonny/pluggable/add_missing_gvk.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
defmodule Bonny.Pluggable.AddMissingGVK do
@moduledoc """
The Kubernetes API sometimes doesn't set the fields `apiVersion` and `kind` on
items of a list operation. E.g. if you use `K8s.Client` to get a list of
deployments, the deployments won't contains those two fields. This is being
discussed on https://github.com/kubernetes/kubernetes/issues/3030.

Bonny sometimes depends on `apiVersion` and `kind` to be defined on the
resource being handled. This is the case for setting the status, e.g. when
using `Bonny.Pluggable.SkipObservedGenerations` or `Bonny.Axn.update_status/2`.

Add this step to your controller in order to set those values on all resources
being handled.


## Examples

step Bonny.Pluggable.AddMissingGVK,
apiVersion: "apps/v1",
kind: "Deployment"
"""

@behaviour Pluggable

@impl true
def init(opts),
do: %{
"apiVersion" => Keyword.fetch!(opts, :apiVersion),
"kind" => Keyword.fetch!(opts, :kind)
}

@impl true
def call(axn, config) do
resource = Map.merge(config, axn.resource)
struct!(axn, resource: resource)
end
end
26 changes: 26 additions & 0 deletions test/bonny/pluggable/add_missing_gvk_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
defmodule Bonny.Pluggable.AddMissingGVKTest do
use ExUnit.Case, async: true
use Bonny.Axn.Test

import YamlElixir.Sigil

alias Bonny.Pluggable.AddMissingGVK, as: MUT

test "adds apiVersion and kind fields to the resource" do
opts = MUT.init(apiVersion: "v1", kind: "ConfigMap")

resource = ~y"""
metadata:
name: foo
namespace: default
data:
key: value
"""

result = MUT.call(axn(:add, resource: resource), opts)

# generation already observed
assert "v1" == result.resource["apiVersion"]
assert "ConfigMap" == result.resource["kind"]
end
end
2 changes: 1 addition & 1 deletion test/bonny/pluggable/skip_observed_generations_test.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
defmodule Bonny.Pluggable.SkipObservedGenerationsTest do
use ExUnit.Case
use ExUnit.Case, async: true
use Bonny.Axn.Test

alias Bonny.Pluggable.SkipObservedGenerations, as: MUT
Expand Down