diff --git a/modules/default.nix b/modules/default.nix index 25678e6..6162323 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -4,6 +4,7 @@ submodules = ./submodules.nix; helm = ./helm.nix; docker = ./docker.nix; + metacontroller = ./metacontroller; testing = ./testing.nix; test = ./test.nix; module = ./module.nix; diff --git a/modules/metacontroller/compositecontroller.nix b/modules/metacontroller/compositecontroller.nix new file mode 100644 index 0000000..ef30574 --- /dev/null +++ b/modules/metacontroller/compositecontroller.nix @@ -0,0 +1,139 @@ +{ config, lib, ... }: + +with lib; + +{ + options = { + parentResource = { + apiVersion = mkOption { + description = "Parent resource apiVersion"; + type = types.str; + example = "apps/v1"; + }; + + resource = mkOption { + description = "The canonical, lowercase, plural name of the parent resource"; + type = types.str; + example = "deployments"; + }; + + revisionHistory = mkOption { + description = "A list of field path strings specifying which parent fields trigger rolling updates of children"; + type = types.listOf types.str; + default = ["spec"]; + example = ["spec.template"]; + }; + }; + + childResources = mkOption { + description = "A list of resource rules specifying the child resources"; + type = types.listOf (types.submodule ({ config, ... }: { + options = { + apiVersion = mkOption { + description = "The API group/version of the child resource, or just version for core APIs"; + type = types.str; + example = "apps/v1"; + }; + + resource = mkOption { + description = "The canonical, lowercase, plural name of the child resource"; + type = types.str; + example = "deployments"; + }; + + updateStrategy = { + method = mkOption { + description = '' + A string indicating the overall method that should be used for updating this type of child resource. + The default is OnDelete, which means don't try to update children that already exist. + + - OnDelete: Don't update existing children unless they get deleted by some other agent. + - Recreate: Immediately delete any children that differ from the desired state, and recreate them in the desired state. + - InPlace: Immediately update any children that differ from the desired state. + - RollingRecreate: Delete each child that differs from the desired state, one at a time, + and recreate each child before moving on to the next one. + Pause the rollout if at any time one of the children that have already been updated fails one or more status checks. + - RollingInPlace: Update each child that differs from the desired state, one at a time. Pause the rollout if at any time + one of the children that have already been updated fails one or more status checks. + ''; + type = types.enum [ + "OnDelete" + "Recreate" + "InPlace" + "RollingRecreate" + "RollingInPlace" + ]; + default = "OnDelete"; + }; + + statusChecks.conditions = mkOption { + description = '' + A list of status condition checks that must all pass on already-updated + children for the rollout to continue. + ''; + type = types.listOf (types.submodule ({ config, ... }: { + options = { + type = mkOption { + description = "A string specifying the status condition type to check."; + type = types.str; + }; + + status = mkOption { + description = '' + A string specifying the required status of the given status condition. + If none is specified, the condition's status is not checked. + ''; + type = types.str; + default = ""; + }; + + reason = mkOption { + description = '' + A string specifying the required reason of the given status condition. + If none is specified, the condition's reason is not checked. + ''; + type = types.str; + default = ""; + }; + }; + })); + default = []; + }; + }; + }; + })); + default = []; + }; + + resyncPeriodSeconds = mkOption { + description = '' + How often, in seconds, you want every parent object to be resynced, + even if no changes are detected. + ''; + type = types.int; + default = 0; + }; + + generateSelector = mkOption { + description = '' + If true, ignore the selector in each parent object and instead generate + a unique selector that prevents overlap with other objects. + ''; + type = types.bool; + default = false; + }; + + hooks = { + sync.webhook.url = mkOption { + description = "Webhook URL where to send sync request"; + type = types.str; + }; + + finalize.webhook.url = mkOption { + description = "Webhook URL where to send finalize request"; + type = types.str; + default = ""; + }; + }; + }; +} diff --git a/modules/metacontroller/default.nix b/modules/metacontroller/default.nix new file mode 100644 index 0000000..4ddd28e --- /dev/null +++ b/modules/metacontroller/default.nix @@ -0,0 +1,36 @@ +{ config, lib, ... }: + +with lib; + +{ + imports = [ ../k8s.nix ]; + + options.metacontroller = { + compositeControllers = mkOption { + type = types.attrsOf (types.submodule ({ name, config, ... }: { + imports = [ ./compositecontroller.nix ]; + + options = { + name = mkOption { + description = "Name of the composite controller"; + type = types.str; + default = name; + }; + }; + })); + default = {}; + }; + }; + + config = { + kubernetes.customResources = [{ + group = "metacontroller.k8s.io"; + version = "v1alpha1"; + kind = "CompositeController"; + resource = "compositecontrollers"; + description = "Composite controller"; + alias = "compositecontrollers"; + module.imports = [ ./compositecontroller.nix ]; + }]; + }; +} diff --git a/tests/default.nix b/tests/default.nix index 2634e15..56f3ce7 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -32,6 +32,7 @@ let ./submodules/defaults.nix ./submodules/versioning.nix ./module.nix + ./metacontroller/compositecontroller.nix ]; testing.defaults = ({kubenix, ...}: { imports = [kubenix.modules.k8s]; diff --git a/tests/metacontroller/compositecontroller.nix b/tests/metacontroller/compositecontroller.nix new file mode 100644 index 0000000..f45c52d --- /dev/null +++ b/tests/metacontroller/compositecontroller.nix @@ -0,0 +1,25 @@ +{ config, kubenix, ... }: + +{ + imports = [ kubenix.modules.test kubenix.modules.metacontroller ]; + + test = { + name = "metacontroller-controllers"; + description = "Testing metacontroller custom resources"; + }; + + kubernetes.api.compositecontrollers.test = { + spec = { + generateSelector = true; + parentResource = { + apiVersion = "ctl.enisoc.com/v1"; + resource = "things"; + }; + childResources = [{ + apiVersion = "v1"; + resource = "pods"; + }]; + hooks.sync.webhook.url = "http://thing-controller.metacontroller/sync"; + }; + }; +}