···
chartDir = "/var/lib/rancher/k3s/server/static/charts";
imageDir = "/var/lib/rancher/k3s/agent/images";
containerdConfigTemplateFile = "/var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl";
23
+
yamlFormat = pkgs.formats.yaml { };
24
+
yamlDocSeparator = builtins.toFile "yaml-doc-separator" "\n---\n";
25
+
# Manifests need a valid YAML suffix to be respected by k3s
27
+
name: if (lib.hasSuffix ".yaml" name || lib.hasSuffix ".yml" name) then name else name + ".yaml";
28
+
# Produces a list containing all duplicate manifest names
29
+
duplicateManifests =
31
+
lib.intersectLists (attrNames cfg.autoDeployCharts) (attrNames cfg.manifests);
32
+
# Produces a list containing all duplicate chart names
35
+
lib.intersectLists (attrNames cfg.autoDeployCharts) (attrNames cfg.charts);
37
+
# Converts YAML -> JSON -> Nix
43
+
pkgs.runCommand "${path}-converted.json" { nativeBuildInputs = [ yq-go ]; } ''
44
+
yq --no-colors --output-format json ${path} > $out
49
+
# Replace characters that are problematic in file names
50
+
cleanHelmChartName =
61
+
# Fetch a Helm chart from a public registry. This only supports a basic Helm pull.
67
+
hash ? lib.fakeHash,
69
+
pkgs.runCommand (cleanHelmChartName "${lib.removePrefix "https://" repo}-${name}-${version}.tgz")
71
+
inherit (lib.fetchers.normalizeHash { } { inherit hash; }) outputHash outputHashAlgo;
72
+
impureEnvVars = lib.fetchers.proxyImpureEnvVars;
73
+
nativeBuildInputs = with pkgs; [
80
+
helm repo add repository ${repo}
81
+
helm pull repository/${name} --version ${version}
85
+
# Returns the path to a YAML manifest file
86
+
mkExtraDeployManifest =
88
+
# x is a derivation that provides a YAML file
89
+
if lib.isDerivation x then
91
+
# x is an attribute set that needs to be converted to a YAML file
92
+
else if builtins.isAttrs x then
93
+
(yamlFormat.generate "extra-deploy-chart-manifest" x)
94
+
# assume x is a path to a YAML file
98
+
# Generate a HelmChart custom resource.
27
-
name: if (lib.hasSuffix ".yaml" name || lib.hasSuffix ".yml" name) then name else name + ".yaml";
102
+
chartValues = if (lib.isPath value.values) then fromYaml value.values else value.values;
103
+
# use JSON for values as it's a subset of YAML and understood by the k3s Helm controller
104
+
valuesContent = builtins.toJSON chartValues;
29
-
lib.types.submodule (
38
-
enable = lib.mkOption {
39
-
type = lib.types.bool;
41
-
description = "Whether this manifest file should be generated.";
106
+
# merge with extraFieldDefinitions to allow setting advanced values and overwrite generated
108
+
lib.recursiveUpdate {
109
+
apiVersion = "helm.cattle.io/v1";
110
+
kind = "HelmChart";
113
+
namespace = "kube-system";
116
+
inherit valuesContent;
117
+
inherit (value) targetNamespace createNamespace;
118
+
chart = "https://%{KUBERNETES_API}%/static/charts/${name}.tgz";
120
+
} value.extraFieldDefinitions;
122
+
# Generate a HelmChart custom resource together with extraDeploy manifests. This
123
+
# generates possibly a multi document YAML file that the auto deploy mechanism of k3s
125
+
mkAutoDeployChartManifest = name: value: {
126
+
# target is the final name of the link created for the manifest file
127
+
target = mkManifestTarget name;
128
+
inherit (value) enable package;
129
+
# source is a store path containing the complete manifest file
130
+
source = pkgs.concatText "auto-deploy-chart-${name}.yaml" (
132
+
(yamlFormat.generate "helm-chart-manifest-${name}.yaml" (mkHelmChartCR name value))
134
+
# alternate the YAML doc seperator (---) and extraDeploy manifests to create
135
+
# multi document YAMLs
136
+
++ (lib.concatMap (x: [
138
+
(mkExtraDeployManifest x)
139
+
]) value.extraDeploy)
143
+
autoDeployChartsModule = lib.types.submodule (
147
+
enable = lib.mkOption {
148
+
type = lib.types.bool;
152
+
Whether to enable the installation of this Helm chart. Note that setting
153
+
this option to `false` will not uninstall the chart from the cluster, if
154
+
it was previously installed. Please use the the `--disable` flag or `.skip`
155
+
files to delete/disable Helm charts, as mentioned in the
156
+
[docs](https://docs.k3s.io/installation/packaged-components#disabling-manifests).
160
+
repo = lib.mkOption {
161
+
type = lib.types.nonEmptyStr;
162
+
example = "https://kubernetes.github.io/ingress-nginx";
164
+
The repo of the Helm chart. Only has an effect if `package` is not set.
165
+
The Helm chart is fetched during build time and placed as a `.tgz` archive on the
170
+
name = lib.mkOption {
171
+
type = lib.types.nonEmptyStr;
172
+
example = "ingress-nginx";
174
+
The name of the Helm chart. Only has an effect if `package` is not set.
175
+
The Helm chart is fetched during build time and placed as a `.tgz` archive on the
180
+
version = lib.mkOption {
181
+
type = lib.types.nonEmptyStr;
184
+
The version of the Helm chart. Only has an effect if `package` is not set.
185
+
The Helm chart is fetched during build time and placed as a `.tgz` archive on the
190
+
hash = lib.mkOption {
191
+
type = lib.types.str;
192
+
example = "sha256-ej+vpPNdiOoXsaj1jyRpWLisJgWo8EqX+Z5VbpSjsPA=";
194
+
The hash of the packaged Helm chart. Only has an effect if `package` is not set.
195
+
The Helm chart is fetched during build time and placed as a `.tgz` archive on the
200
+
package = lib.mkOption {
201
+
type = with lib.types; either path package;
202
+
example = lib.literalExpression "../my-helm-chart.tgz";
204
+
The packaged Helm chart. Overwrites the options `repo`, `name`, `version`
205
+
and `hash` in case of conflicts.
209
+
targetNamespace = lib.mkOption {
210
+
type = lib.types.nonEmptyStr;
211
+
default = "default";
212
+
example = "kube-system";
213
+
description = "The namespace in which the Helm chart gets installed.";
44
-
target = lib.mkOption {
45
-
type = lib.types.nonEmptyStr;
46
-
example = lib.literalExpression "manifest.yaml";
48
-
Name of the symlink (relative to {file}`${manifestDir}`).
49
-
Defaults to the attribute name.
216
+
createNamespace = lib.mkOption {
217
+
type = lib.types.bool;
220
+
description = "Whether to create the target namespace if not present.";
53
-
content = lib.mkOption {
54
-
type = with lib.types; nullOr (either attrs (listOf attrs));
57
-
Content of the manifest file. A single attribute set will
58
-
generate a single document YAML file. A list of attribute sets
59
-
will generate multiple documents separated by `---` in a single
223
+
values = lib.mkOption {
224
+
type = with lib.types; either path attrs;
228
+
hostName = "my-host";
235
+
Override default chart values via Nix expressions. This is equivalent to setting
236
+
values in a `values.yaml` file.
64
-
source = lib.mkOption {
65
-
type = lib.types.path;
66
-
example = lib.literalExpression "./manifests/app.yaml";
68
-
Path of the source `.yaml` file.
238
+
WARNING: The values (including secrets!) specified here are exposed unencrypted
239
+
in the world-readable nix store.
243
+
extraDeploy = lib.mkOption {
244
+
type = with lib.types; listOf (either path attrs);
246
+
example = lib.literalExpression ''
248
+
../manifests/my-extra-deployment.yaml
253
+
name = "app-service";
257
+
"app.kubernetes.io/name" = "MyApp";
261
+
name = "name-of-service-port";
264
+
targetPort = "http-web-svc";
271
+
description = "List of extra Kubernetes manifests to deploy with this Helm chart.";
274
+
extraFieldDefinitions = lib.mkOption {
275
+
inherit (yamlFormat) type;
280
+
helmVersion = "v2";
282
+
jobImage = "custom-helm-controller:v0.0.1";
286
+
Extra HelmChart field definitions that are merged with the rest of the HelmChart
287
+
custom resource. This can be used to set advanced fields or to overwrite
288
+
generated fields. See https://docs.k3s.io/helm#helmchart-field-definitions
289
+
for possible fields.
74
-
target = lib.mkDefault (mkTarget name);
75
-
source = lib.mkIf (config.content != null) (
77
-
name' = "k3s-manifest-" + builtins.baseNameOf name;
78
-
docName = "k3s-manifest-doc-" + builtins.baseNameOf name;
79
-
yamlDocSeparator = builtins.toFile "yaml-doc-separator" "\n---\n";
80
-
mkYaml = name: x: (pkgs.formats.yaml { }).generate name x;
83
-
if builtins.isList value then
84
-
pkgs.concatText name' (
93
-
lib.mkDerivedConfig options.content mkSource
294
+
config.package = lib.mkDefault (fetchHelm {
305
+
manifestModule = lib.types.submodule (
314
+
enable = lib.mkOption {
315
+
type = lib.types.bool;
317
+
description = "Whether this manifest file should be generated.";
320
+
target = lib.mkOption {
321
+
type = lib.types.nonEmptyStr;
322
+
example = "manifest.yaml";
324
+
Name of the symlink (relative to {file}`${manifestDir}`).
325
+
Defaults to the attribute name.
329
+
content = lib.mkOption {
330
+
type = with lib.types; nullOr (either attrs (listOf attrs));
333
+
Content of the manifest file. A single attribute set will
334
+
generate a single document YAML file. A list of attribute sets
335
+
will generate multiple documents separated by `---` in a single
340
+
source = lib.mkOption {
341
+
type = lib.types.path;
342
+
example = lib.literalExpression "./manifests/app.yaml";
344
+
Path of the source `.yaml` file.
350
+
target = lib.mkDefault (mkManifestTarget name);
351
+
source = lib.mkIf (config.content != null) (
353
+
name' = "k3s-manifest-" + builtins.baseNameOf name;
354
+
docName = "k3s-manifest-doc-" + builtins.baseNameOf name;
357
+
if builtins.isList value then
358
+
pkgs.concatText name' (
359
+
lib.concatMap (x: [
361
+
(yamlFormat.generate docName x)
365
+
yamlFormat.generate name' value;
367
+
lib.mkDerivedConfig options.content mkSource
373
+
# TODO: use tmpfiles
enabledManifests = lib.filter (m: m.enable) (lib.attrValues cfg.manifests);
375
+
enabledHelmManifests = lib.filter (m: m.enable) (lib.attrValues cfg.autoDeployCharts);
376
+
enabledAutoDeployCharts = lib.concatMapAttrs (n: v: { ${n} = v.package; }) (
377
+
lib.filterAttrs (_: v: v.enable) cfg.autoDeployCharts
linkManifestEntry = m: "${pkgs.coreutils-full}/bin/ln -sfn ${m.source} ${manifestDir}/${m.target}";
linkImageEntry = image: "${pkgs.coreutils-full}/bin/ln -sfn ${image} ${imageDir}/${image.name}";
104
-
mkTarget = name: if (lib.hasSuffix ".tgz" name) then name else name + ".tgz";
383
+
mkChartTarget = name: if (lib.hasSuffix ".tgz" name) then name else name + ".tgz";
107
-
"${pkgs.coreutils-full}/bin/ln -sfn ${value} ${chartDir}/${mkTarget (builtins.baseNameOf name)}";
386
+
"${pkgs.coreutils-full}/bin/ln -sfn ${value} ${chartDir}/${mkChartTarget (builtins.baseNameOf name)}";
activateK3sContent = pkgs.writeShellScript "activate-k3s-content" ''
111
-
builtins.length enabledManifests > 0
390
+
builtins.length (enabledManifests ++ enabledHelmManifests) > 0
) "${pkgs.coreutils-full}/bin/mkdir -p ${manifestDir}"}
113
-
${lib.optionalString (cfg.charts != { }) "${pkgs.coreutils-full}/bin/mkdir -p ${chartDir}"}
392
+
${lib.optionalString (
393
+
cfg.charts != { } || enabledAutoDeployCharts != { }
394
+
) "${pkgs.coreutils-full}/bin/mkdir -p ${chartDir}"}
builtins.length cfg.images > 0
) "${pkgs.coreutils-full}/bin/mkdir -p ${imageDir}"}
${builtins.concatStringsSep "\n" (map linkManifestEntry enabledManifests)}
400
+
${builtins.concatStringsSep "\n" (map linkManifestEntry enabledHelmManifests)}
${builtins.concatStringsSep "\n" (lib.mapAttrsToList linkChartEntry cfg.charts)}
402
+
${builtins.concatStringsSep "\n" (lib.mapAttrsToList linkChartEntry enabledAutoDeployCharts)}
${builtins.concatStringsSep "\n" (map linkImageEntry cfg.images)}
${lib.optionalString (cfg.containerdConfigTemplate != null) ''
···
type = lib.types.attrsOf manifestModule;
example = lib.literalExpression ''
245
-
deployment.source = ../manifests/deployment.yaml;
248
-
target = "app-service.yaml";
253
-
name = "app-service";
257
-
"app.kubernetes.io/name" = "MyApp";
529
+
deployment.source = ../manifests/deployment.yaml;
532
+
target = "app-service.yaml";
537
+
name = "app-service";
541
+
"app.kubernetes.io/name" = "MyApp";
545
+
name = "name-of-service-port";
548
+
targetPort = "http-web-svc";
261
-
name = "name-of-service-port";
264
-
targetPort = "http-web-svc";
278
-
"app.kubernetes.io/name" = "MyApp";
562
+
"app.kubernetes.io/name" = "MyApp";
569
+
image = "nginx:1.14.2";
572
+
containerPort = 80;
573
+
name = "http-web-svc";
285
-
image = "nginx:1.14.2";
288
-
containerPort = 80;
289
-
name = "http-web-svc";
300
-
name = "nginx-service";
304
-
"app.kubernetes.io/name" = "MyApp";
584
+
name = "nginx-service";
308
-
name = "name-of-service-port";
311
-
targetPort = "http-web-svc";
588
+
"app.kubernetes.io/name" = "MyApp";
592
+
name = "name-of-service-port";
595
+
targetPort = "http-web-svc";
Auto-deploying manifests that are linked to {file}`${manifestDir}` before k3s starts.
···
Packaged Helm charts that are linked to {file}`${chartDir}` before k3s starts.
The attribute name will be used as the link target (relative to {file}`${chartDir}`).
The specified charts will only be placed on the file system and made available to the
340
-
Kubernetes APIServer from within the cluster, you may use the
341
-
[k3s Helm controller](https://docs.k3s.io/helm#using-the-helm-controller)
342
-
to deploy the charts. This option only makes sense on server nodes
625
+
Kubernetes APIServer from within the cluster. See the [](#opt-services.k3s.autoDeployCharts)
626
+
option and the [k3s Helm controller docs](https://docs.k3s.io/helm#using-the-helm-controller)
627
+
to deploy Helm charts. This option only makes sense on server nodes (`role = server`).
···
set the `clientConnection.kubeconfig` if you want to use `extraKubeProxyConfig`.
738
+
autoDeployCharts = lib.mkOption {
739
+
type = lib.types.attrsOf autoDeployChartsModule;
740
+
apply = lib.mapAttrs mkAutoDeployChartManifest;
742
+
example = lib.literalExpression ''
746
+
repo = "https://helm.goharbor.io";
747
+
version = "1.14.0";
748
+
hash = "sha256-fMP7q1MIbvzPGS9My91vbQ1d3OJMjwc+o8YE/BXZaYU=";
750
+
existingSecretAdminPassword = "harbor-admin";
754
+
certSource = "secret";
755
+
secret.secretName = "my-tls-secret";
758
+
hosts.core = "example.com";
759
+
className = "nginx";
766
+
package = ../charts/my-chart.tgz;
767
+
values = ../values/my-values.yaml;
768
+
extraFieldDefinitions = {
769
+
spec.timeout = "60s";
775
+
Auto deploying Helm charts that are installed by the k3s Helm controller. Avoid to use
776
+
attribute names that are also used in the [](#opt-services.k3s.manifests) and
777
+
[](#opt-services.k3s.charts) options. Manifests with the same name will override
778
+
auto deploying charts with the same name. Similiarly, charts with the same name will
779
+
overwrite the Helm chart contained in auto deploying charts. This option only makes
780
+
sense on server nodes (`role = server`). See the
781
+
[k3s Helm documentation](https://docs.k3s.io/helm) for further information.
···
++ (lib.optional (cfg.role != "server" && cfg.charts != { })
"k3s: Helm charts are only made available to the cluster on server nodes (role == server), they will be ignored by this node."
796
+
++ (lib.optional (cfg.role != "server" && cfg.autoDeployCharts != { })
797
+
"k3s: Auto deploying Helm charts are only installed on server nodes (role == server), they will be ignored by this node."
799
+
++ (lib.optional (duplicateManifests != [ ])
800
+
"k3s: The following auto deploying charts are overriden by manifests of the same name: ${toString duplicateManifests}."
802
+
++ (lib.optional (duplicateCharts != [ ])
803
+
"k3s: The following auto deploying charts are overriden by charts of the same name: ${toString duplicateCharts}."
cfg.disableAgent && cfg.images != [ ]