···597- New options for the declarative configuration of the user space part of ALSA have been introduced under [hardware.alsa](options.html#opt-hardware.alsa.enable), including setting the default capture and playback device, defining sound card aliases and volume controls.
598 Note: these are intended for users not running a sound server like PulseAudio or PipeWire, but having ALSA as their only sound system.
59900600- Caddy can now be built with plugins by using `caddy.withPlugins`, a `passthru` function that accepts an attribute set as a parameter. The `plugins` argument represents a list of Caddy plugins, with each Caddy plugin being a versioned module. The `hash` argument represents the `vendorHash` of the resulting Caddy source code with the plugins added.
601602 Example:
···597- New options for the declarative configuration of the user space part of ALSA have been introduced under [hardware.alsa](options.html#opt-hardware.alsa.enable), including setting the default capture and playback device, defining sound card aliases and volume controls.
598 Note: these are intended for users not running a sound server like PulseAudio or PipeWire, but having ALSA as their only sound system.
599600+- `services.k3s` now provides the `autoDeployCharts` option that allows to automatically deploy Helm charts via the k3s Helm controller.
601+602- Caddy can now be built with plugins by using `caddy.withPlugins`, a `passthru` function that accepts an attribute set as a parameter. The `plugins` argument represents a list of Caddy plugins, with each Caddy plugin being a versioned module. The `hash` argument represents the `vendorHash` of the resulting Caddy source code with the plugins added.
603604 Example:
+506-161
nixos/modules/services/cluster/k3s/default.nix
···20 chartDir = "/var/lib/rancher/k3s/server/static/charts";
21 imageDir = "/var/lib/rancher/k3s/agent/images";
22 containerdConfigTemplateFile = "/var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl";
00000000000002324- manifestModule =
00000000000000000000000000000000000000000000000000000000000000025 let
26- mkTarget =
27- name: if (lib.hasSuffix ".yaml" name || lib.hasSuffix ".yml" name) then name else name + ".yaml";
028 in
29- lib.types.submodule (
30- {
31- name,
32- config,
33- options,
34- ...
35- }:
36- {
37- options = {
38- enable = lib.mkOption {
39- type = lib.types.bool;
40- default = true;
41- description = "Whether this manifest file should be generated.";
42- };
00000000000000000000004344- target = lib.mkOption {
45- type = lib.types.nonEmptyStr;
46- example = lib.literalExpression "manifest.yaml";
47- description = ''
48- Name of the symlink (relative to {file}`${manifestDir}`).
49- Defaults to the attribute name.
50- '';
51- };
000000005253- content = lib.mkOption {
54- type = with lib.types; nullOr (either attrs (listOf attrs));
55- default = null;
56- description = ''
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
60- YAML file.
61- '';
000000000000000000000000000000000000000000000000000000000000000062 };
0006364- source = lib.mkOption {
65- type = lib.types.path;
66- example = lib.literalExpression "./manifests/app.yaml";
67- description = ''
68- Path of the source `.yaml` file.
69- '';
000000000000000000000000000000000000000070 };
00000071 };
07273- config = {
74- target = lib.mkDefault (mkTarget name);
75- source = lib.mkIf (config.content != null) (
76- let
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;
81- mkSource =
82- value:
83- if builtins.isList value then
84- pkgs.concatText name' (
85- lib.concatMap (x: [
86- yamlDocSeparator
87- (mkYaml docName x)
88- ]) value
89- )
90- else
91- mkYaml name' value;
92- in
93- lib.mkDerivedConfig options.content mkSource
94- );
0095 };
96- }
97- );
9899- enabledManifests = lib.filter (m: m.enable) (lib.attrValues cfg.manifests);
100- linkManifestEntry = m: "${pkgs.coreutils-full}/bin/ln -sfn ${m.source} ${manifestDir}/${m.target}";
101- linkImageEntry = image: "${pkgs.coreutils-full}/bin/ln -sfn ${image} ${imageDir}/${image.name}";
102- linkChartEntry =
103- let
104- mkTarget = name: if (lib.hasSuffix ".tgz" name) then name else name + ".tgz";
105- in
106- name: value:
107- "${pkgs.coreutils-full}/bin/ln -sfn ${value} ${chartDir}/${mkTarget (builtins.baseNameOf name)}";
108109- activateK3sContent = pkgs.writeShellScript "activate-k3s-content" ''
110- ${lib.optionalString (
111- builtins.length enabledManifests > 0
112- ) "${pkgs.coreutils-full}/bin/mkdir -p ${manifestDir}"}
113- ${lib.optionalString (cfg.charts != { }) "${pkgs.coreutils-full}/bin/mkdir -p ${chartDir}"}
114- ${lib.optionalString (
115- builtins.length cfg.images > 0
116- ) "${pkgs.coreutils-full}/bin/mkdir -p ${imageDir}"}
00117118- ${builtins.concatStringsSep "\n" (map linkManifestEntry enabledManifests)}
119- ${builtins.concatStringsSep "\n" (lib.mapAttrsToList linkChartEntry cfg.charts)}
120- ${builtins.concatStringsSep "\n" (map linkImageEntry cfg.images)}
00000121122- ${lib.optionalString (cfg.containerdConfigTemplate != null) ''
123- mkdir -p $(dirname ${containerdConfigTemplateFile})
124- ${pkgs.coreutils-full}/bin/ln -sfn ${pkgs.writeText "config.toml.tmpl" cfg.containerdConfigTemplate} ${containerdConfigTemplateFile}
125- ''}
126- '';
000000000000000000127in
128{
129 imports = [ (removeOption [ "docker" ] "k3s docker option is no longer supported.") ];
···242 type = lib.types.attrsOf manifestModule;
243 default = { };
244 example = lib.literalExpression ''
245- deployment.source = ../manifests/deployment.yaml;
246- my-service = {
247- enable = false;
248- target = "app-service.yaml";
249- content = {
250- apiVersion = "v1";
251- kind = "Service";
252- metadata = {
253- name = "app-service";
254- };
255- spec = {
256- selector = {
257- "app.kubernetes.io/name" = "MyApp";
258 };
259- ports = [
260- {
261- name = "name-of-service-port";
262- protocol = "TCP";
263- port = 80;
264- targetPort = "http-web-svc";
265- }
266- ];
00000267 };
268- }
269- };
270271- nginx.content = [
272- {
273- apiVersion = "v1";
274- kind = "Pod";
275- metadata = {
276- name = "nginx";
277- labels = {
278- "app.kubernetes.io/name" = "MyApp";
0000000000000000000000279 };
280- };
281- spec = {
282- containers = [
283- {
284- name = "nginx";
285- image = "nginx:1.14.2";
286- ports = [
287- {
288- containerPort = 80;
289- name = "http-web-svc";
290- }
291- ];
292- }
293- ];
294- };
295- }
296- {
297- apiVersion = "v1";
298- kind = "Service";
299- metadata = {
300- name = "nginx-service";
301- };
302- spec = {
303- selector = {
304- "app.kubernetes.io/name" = "MyApp";
305 };
306- ports = [
307- {
308- name = "name-of-service-port";
309- protocol = "TCP";
310- port = 80;
311- targetPort = "http-web-svc";
312- }
313- ];
314- };
315- }
316- ];
317 '';
318 description = ''
319 Auto-deploying manifests that are linked to {file}`${manifestDir}` before k3s starts.
···337 Packaged Helm charts that are linked to {file}`${chartDir}` before k3s starts.
338 The attribute name will be used as the link target (relative to {file}`${chartDir}`).
339 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
343- (`role = server`).
344 '';
345 };
346···450 set the `clientConnection.kubeconfig` if you want to use `extraKubeProxyConfig`.
451 '';
452 };
00000000000000000000000000000000000000000000000453 };
454455 # implementation
···462 ++ (lib.optional (cfg.role != "server" && cfg.charts != { })
463 "k3s: Helm charts are only made available to the cluster on server nodes (role == server), they will be ignored by this node."
464 )
000000000465 ++ (lib.optional (
466 cfg.disableAgent && cfg.images != [ ]
467 ) "k3s: Images are only imported on nodes with an enabled agent, they will be ignored by this node")
···486487 environment.systemPackages = [ config.services.k3s.package ];
48800000000000000000000000000000000000000000000489 systemd.services.k3s =
490 let
491 kubeletParams =
···533 LimitCORE = "infinity";
534 TasksMax = "infinity";
535 EnvironmentFile = cfg.environmentFile;
536- ExecStartPre = activateK3sContent;
537 ExecStart = lib.concatStringsSep " \\\n " (
538 [ "${cfg.package}/bin/k3s ${cfg.role}" ]
539 ++ (lib.optional cfg.clusterInit "--cluster-init")
···20 chartDir = "/var/lib/rancher/k3s/server/static/charts";
21 imageDir = "/var/lib/rancher/k3s/agent/images";
22 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
26+ mkManifestTarget =
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 =
30+ with builtins;
31+ lib.intersectLists (attrNames cfg.autoDeployCharts) (attrNames cfg.manifests);
32+ # Produces a list containing all duplicate chart names
33+ duplicateCharts =
34+ with builtins;
35+ lib.intersectLists (attrNames cfg.autoDeployCharts) (attrNames cfg.charts);
3637+ # Converts YAML -> JSON -> Nix
38+ fromYaml =
39+ path:
40+ with builtins;
41+ fromJSON (
42+ readFile (
43+ pkgs.runCommand "${path}-converted.json" { nativeBuildInputs = [ yq-go ]; } ''
44+ yq --no-colors --output-format json ${path} > $out
45+ ''
46+ )
47+ );
48+49+ # Replace characters that are problematic in file names
50+ cleanHelmChartName =
51+ lib.replaceStrings
52+ [
53+ "/"
54+ ":"
55+ ]
56+ [
57+ "-"
58+ "-"
59+ ];
60+61+ # Fetch a Helm chart from a public registry. This only supports a basic Helm pull.
62+ fetchHelm =
63+ {
64+ name,
65+ repo,
66+ version,
67+ hash ? lib.fakeHash,
68+ }:
69+ pkgs.runCommand (cleanHelmChartName "${lib.removePrefix "https://" repo}-${name}-${version}.tgz")
70+ {
71+ inherit (lib.fetchers.normalizeHash { } { inherit hash; }) outputHash outputHashAlgo;
72+ impureEnvVars = lib.fetchers.proxyImpureEnvVars;
73+ nativeBuildInputs = with pkgs; [
74+ kubernetes-helm
75+ cacert
76+ ];
77+ }
78+ ''
79+ export HOME="$PWD"
80+ helm repo add repository ${repo}
81+ helm pull repository/${name} --version ${version}
82+ mv ./*.tgz $out
83+ '';
84+85+ # Returns the path to a YAML manifest file
86+ mkExtraDeployManifest =
87+ x:
88+ # x is a derivation that provides a YAML file
89+ if lib.isDerivation x then
90+ x.outPath
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
95+ else
96+ x;
97+98+ # Generate a HelmChart custom resource.
99+ mkHelmChartCR =
100+ name: value:
101 let
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;
105 in
106+ # merge with extraFieldDefinitions to allow setting advanced values and overwrite generated
107+ # values
108+ lib.recursiveUpdate {
109+ apiVersion = "helm.cattle.io/v1";
110+ kind = "HelmChart";
111+ metadata = {
112+ inherit name;
113+ namespace = "kube-system";
114+ };
115+ spec = {
116+ inherit valuesContent;
117+ inherit (value) targetNamespace createNamespace;
118+ chart = "https://%{KUBERNETES_API}%/static/charts/${name}.tgz";
119+ };
120+ } value.extraFieldDefinitions;
121+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
124+ # deploys.
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" (
131+ [
132+ (yamlFormat.generate "helm-chart-manifest-${name}.yaml" (mkHelmChartCR name value))
133+ ]
134+ # alternate the YAML doc seperator (---) and extraDeploy manifests to create
135+ # multi document YAMLs
136+ ++ (lib.concatMap (x: [
137+ yamlDocSeparator
138+ (mkExtraDeployManifest x)
139+ ]) value.extraDeploy)
140+ );
141+ };
142143+ autoDeployChartsModule = lib.types.submodule (
144+ { config, ... }:
145+ {
146+ options = {
147+ enable = lib.mkOption {
148+ type = lib.types.bool;
149+ default = true;
150+ example = false;
151+ description = ''
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).
157+ '';
158+ };
159160+ repo = lib.mkOption {
161+ type = lib.types.nonEmptyStr;
162+ example = "https://kubernetes.github.io/ingress-nginx";
163+ description = ''
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
166+ filesystem.
167+ '';
168+ };
169+170+ name = lib.mkOption {
171+ type = lib.types.nonEmptyStr;
172+ example = "ingress-nginx";
173+ description = ''
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
176+ filesystem.
177+ '';
178+ };
179+180+ version = lib.mkOption {
181+ type = lib.types.nonEmptyStr;
182+ example = "4.7.0";
183+ description = ''
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
186+ filesystem.
187+ '';
188+ };
189+190+ hash = lib.mkOption {
191+ type = lib.types.str;
192+ example = "sha256-ej+vpPNdiOoXsaj1jyRpWLisJgWo8EqX+Z5VbpSjsPA=";
193+ description = ''
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
196+ filesystem.
197+ '';
198+ };
199+200+ package = lib.mkOption {
201+ type = with lib.types; either path package;
202+ example = lib.literalExpression "../my-helm-chart.tgz";
203+ description = ''
204+ The packaged Helm chart. Overwrites the options `repo`, `name`, `version`
205+ and `hash` in case of conflicts.
206+ '';
207+ };
208+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.";
214+ };
215+216+ createNamespace = lib.mkOption {
217+ type = lib.types.bool;
218+ default = false;
219+ example = true;
220+ description = "Whether to create the target namespace if not present.";
221+ };
222+223+ values = lib.mkOption {
224+ type = with lib.types; either path attrs;
225+ default = { };
226+ example = {
227+ replicaCount = 3;
228+ hostName = "my-host";
229+ server = {
230+ name = "nginx";
231+ port = 80;
232+ };
233 };
234+ description = ''
235+ Override default chart values via Nix expressions. This is equivalent to setting
236+ values in a `values.yaml` file.
237238+ WARNING: The values (including secrets!) specified here are exposed unencrypted
239+ in the world-readable nix store.
240+ '';
241+ };
242+243+ extraDeploy = lib.mkOption {
244+ type = with lib.types; listOf (either path attrs);
245+ default = [ ];
246+ example = lib.literalExpression ''
247+ [
248+ ../manifests/my-extra-deployment.yaml
249+ {
250+ apiVersion = "v1";
251+ kind = "Service";
252+ metadata = {
253+ name = "app-service";
254+ };
255+ spec = {
256+ selector = {
257+ "app.kubernetes.io/name" = "MyApp";
258+ };
259+ ports = [
260+ {
261+ name = "name-of-service-port";
262+ protocol = "TCP";
263+ port = 80;
264+ targetPort = "http-web-svc";
265+ }
266+ ];
267+ };
268+ }
269+ ];
270+ '';
271+ description = "List of extra Kubernetes manifests to deploy with this Helm chart.";
272+ };
273+274+ extraFieldDefinitions = lib.mkOption {
275+ inherit (yamlFormat) type;
276+ default = { };
277+ example = {
278+ spec = {
279+ bootstrap = true;
280+ helmVersion = "v2";
281+ backOffLimit = 3;
282+ jobImage = "custom-helm-controller:v0.0.1";
283+ };
284 };
285+ description = ''
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.
290+ '';
291 };
292+ };
293294+ config.package = lib.mkDefault (fetchHelm {
295+ inherit (config)
296+ repo
297+ name
298+ version
299+ hash
300+ ;
301+ });
302+ }
303+ );
304+305+ manifestModule = lib.types.submodule (
306+ {
307+ name,
308+ config,
309+ options,
310+ ...
311+ }:
312+ {
313+ options = {
314+ enable = lib.mkOption {
315+ type = lib.types.bool;
316+ default = true;
317+ description = "Whether this manifest file should be generated.";
318 };
00319320+ target = lib.mkOption {
321+ type = lib.types.nonEmptyStr;
322+ example = "manifest.yaml";
323+ description = ''
324+ Name of the symlink (relative to {file}`${manifestDir}`).
325+ Defaults to the attribute name.
326+ '';
327+ };
0328329+ content = lib.mkOption {
330+ type = with lib.types; nullOr (either attrs (listOf attrs));
331+ default = null;
332+ description = ''
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
336+ YAML file.
337+ '';
338+ };
339340+ source = lib.mkOption {
341+ type = lib.types.path;
342+ example = lib.literalExpression "./manifests/app.yaml";
343+ description = ''
344+ Path of the source `.yaml` file.
345+ '';
346+ };
347+ };
348349+ config = {
350+ target = lib.mkDefault (mkManifestTarget name);
351+ source = lib.mkIf (config.content != null) (
352+ let
353+ name' = "k3s-manifest-" + builtins.baseNameOf name;
354+ docName = "k3s-manifest-doc-" + builtins.baseNameOf name;
355+ mkSource =
356+ value:
357+ if builtins.isList value then
358+ pkgs.concatText name' (
359+ lib.concatMap (x: [
360+ yamlDocSeparator
361+ (yamlFormat.generate docName x)
362+ ]) value
363+ )
364+ else
365+ yamlFormat.generate name' value;
366+ in
367+ lib.mkDerivedConfig options.content mkSource
368+ );
369+ };
370+ }
371+ );
372in
373{
374 imports = [ (removeOption [ "docker" ] "k3s docker option is no longer supported.") ];
···487 type = lib.types.attrsOf manifestModule;
488 default = { };
489 example = lib.literalExpression ''
490+ {
491+ deployment.source = ../manifests/deployment.yaml;
492+ my-service = {
493+ enable = false;
494+ target = "app-service.yaml";
495+ content = {
496+ apiVersion = "v1";
497+ kind = "Service";
498+ metadata = {
499+ name = "app-service";
000500 };
501+ spec = {
502+ selector = {
503+ "app.kubernetes.io/name" = "MyApp";
504+ };
505+ ports = [
506+ {
507+ name = "name-of-service-port";
508+ protocol = "TCP";
509+ port = 80;
510+ targetPort = "http-web-svc";
511+ }
512+ ];
513+ };
514 };
515+ };
0516517+ nginx.content = [
518+ {
519+ apiVersion = "v1";
520+ kind = "Pod";
521+ metadata = {
522+ name = "nginx";
523+ labels = {
524+ "app.kubernetes.io/name" = "MyApp";
525+ };
526+ };
527+ spec = {
528+ containers = [
529+ {
530+ name = "nginx";
531+ image = "nginx:1.14.2";
532+ ports = [
533+ {
534+ containerPort = 80;
535+ name = "http-web-svc";
536+ }
537+ ];
538+ }
539+ ];
540+ };
541+ }
542+ {
543+ apiVersion = "v1";
544+ kind = "Service";
545+ metadata = {
546+ name = "nginx-service";
547 };
548+ spec = {
549+ selector = {
550+ "app.kubernetes.io/name" = "MyApp";
551+ };
552+ ports = [
553+ {
554+ name = "name-of-service-port";
555+ protocol = "TCP";
556+ port = 80;
557+ targetPort = "http-web-svc";
558+ }
559+ ];
0000000000000560 };
561+ }
562+ ];
563+ };
00000000564 '';
565 description = ''
566 Auto-deploying manifests that are linked to {file}`${manifestDir}` before k3s starts.
···584 Packaged Helm charts that are linked to {file}`${chartDir}` before k3s starts.
585 The attribute name will be used as the link target (relative to {file}`${chartDir}`).
586 The specified charts will only be placed on the file system and made available to the
587+ Kubernetes APIServer from within the cluster. See the [](#opt-services.k3s.autoDeployCharts)
588+ option and the [k3s Helm controller docs](https://docs.k3s.io/helm#using-the-helm-controller)
589+ to deploy Helm charts. This option only makes sense on server nodes (`role = server`).
0590 '';
591 };
592···696 set the `clientConnection.kubeconfig` if you want to use `extraKubeProxyConfig`.
697 '';
698 };
699+700+ autoDeployCharts = lib.mkOption {
701+ type = lib.types.attrsOf autoDeployChartsModule;
702+ apply = lib.mapAttrs mkAutoDeployChartManifest;
703+ default = { };
704+ example = lib.literalExpression ''
705+ {
706+ harbor = {
707+ name = "harbor";
708+ repo = "https://helm.goharbor.io";
709+ version = "1.14.0";
710+ hash = "sha256-fMP7q1MIbvzPGS9My91vbQ1d3OJMjwc+o8YE/BXZaYU=";
711+ values = {
712+ existingSecretAdminPassword = "harbor-admin";
713+ expose = {
714+ tls = {
715+ enabled = true;
716+ certSource = "secret";
717+ secret.secretName = "my-tls-secret";
718+ };
719+ ingress = {
720+ hosts.core = "example.com";
721+ className = "nginx";
722+ };
723+ };
724+ };
725+ };
726+727+ custom-chart = {
728+ package = ../charts/my-chart.tgz;
729+ values = ../values/my-values.yaml;
730+ extraFieldDefinitions = {
731+ spec.timeout = "60s";
732+ };
733+ };
734+ }
735+ '';
736+ description = ''
737+ Auto deploying Helm charts that are installed by the k3s Helm controller. Avoid to use
738+ attribute names that are also used in the [](#opt-services.k3s.manifests) and
739+ [](#opt-services.k3s.charts) options. Manifests with the same name will override
740+ auto deploying charts with the same name. Similiarly, charts with the same name will
741+ overwrite the Helm chart contained in auto deploying charts. This option only makes
742+ sense on server nodes (`role = server`). See the
743+ [k3s Helm documentation](https://docs.k3s.io/helm) for further information.
744+ '';
745+ };
746 };
747748 # implementation
···755 ++ (lib.optional (cfg.role != "server" && cfg.charts != { })
756 "k3s: Helm charts are only made available to the cluster on server nodes (role == server), they will be ignored by this node."
757 )
758+ ++ (lib.optional (cfg.role != "server" && cfg.autoDeployCharts != { })
759+ "k3s: Auto deploying Helm charts are only installed on server nodes (role == server), they will be ignored by this node."
760+ )
761+ ++ (lib.optional (duplicateManifests != [ ])
762+ "k3s: The following auto deploying charts are overriden by manifests of the same name: ${toString duplicateManifests}."
763+ )
764+ ++ (lib.optional (duplicateCharts != [ ])
765+ "k3s: The following auto deploying charts are overriden by charts of the same name: ${toString duplicateCharts}."
766+ )
767 ++ (lib.optional (
768 cfg.disableAgent && cfg.images != [ ]
769 ) "k3s: Images are only imported on nodes with an enabled agent, they will be ignored by this node")
···788789 environment.systemPackages = [ config.services.k3s.package ];
790791+ # Use systemd-tmpfiles to activate k3s content
792+ systemd.tmpfiles.settings."10-k3s" =
793+ let
794+ # Merge manifest with manifests generated from auto deploying charts, keep only enabled manifests
795+ enabledManifests = lib.filterAttrs (_: v: v.enable) (cfg.autoDeployCharts // cfg.manifests);
796+ # Merge charts with charts contained in enabled auto deploying charts
797+ helmCharts =
798+ (lib.concatMapAttrs (n: v: { ${n} = v.package; }) (
799+ lib.filterAttrs (_: v: v.enable) cfg.autoDeployCharts
800+ ))
801+ // cfg.charts;
802+ # Make a systemd-tmpfiles rule for a manifest
803+ mkManifestRule = manifest: {
804+ name = "${manifestDir}/${manifest.target}";
805+ value = {
806+ "L+".argument = "${manifest.source}";
807+ };
808+ };
809+ # Ensure that all chart targets have a .tgz suffix
810+ mkChartTarget = name: if (lib.hasSuffix ".tgz" name) then name else name + ".tgz";
811+ # Make a systemd-tmpfiles rule for a chart
812+ mkChartRule = target: source: {
813+ name = "${chartDir}/${mkChartTarget target}";
814+ value = {
815+ "L+".argument = "${source}";
816+ };
817+ };
818+ # Make a systemd-tmpfiles rule for a container image
819+ mkImageRule = image: {
820+ name = "${imageDir}/${image.name}";
821+ value = {
822+ "L+".argument = "${image}";
823+ };
824+ };
825+ in
826+ (lib.mapAttrs' (_: v: mkManifestRule v) enabledManifests)
827+ // (lib.mapAttrs' (n: v: mkChartRule n v) helmCharts)
828+ // (builtins.listToAttrs (map mkImageRule cfg.images))
829+ // (lib.optionalAttrs (cfg.containerdConfigTemplate != null) {
830+ ${containerdConfigTemplateFile} = {
831+ "L+".argument = "${pkgs.writeText "config.toml.tmpl" cfg.containerdConfigTemplate}";
832+ };
833+ });
834+835 systemd.services.k3s =
836 let
837 kubeletParams =
···879 LimitCORE = "infinity";
880 TasksMax = "infinity";
881 EnvironmentFile = cfg.environmentFile;
0882 ExecStart = lib.concatStringsSep " \\\n " (
883 [ "${cfg.package}/bin/k3s ${cfg.role}" ]
884 ++ (lib.optional cfg.clusterInit "--cluster-init")
···1+apiVersion: v2
2+name: k3s-test-chart
3+description: A Helm chart that is used in k3s NixOS tests.
4+5+# A chart can be either an 'application' or a 'library' chart.
6+#
7+# Application charts are a collection of templates that can be packaged into versioned archives
8+# to be deployed.
9+#
10+# Library charts provide useful utilities or functions for the chart developer. They're included as
11+# a dependency of application charts to inject those utilities and functions into the rendering
12+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
13+type: application
14+15+# This is the chart version. This version number should be incremented each time you make changes
16+# to the chart and its templates, including the app version.
17+# Versions are expected to follow Semantic Versioning (https://semver.org/)
18+version: 0.1.0
19+20+# This is the version number of the application being deployed. This version number should be
21+# incremented each time you make changes to the application. Versions are not expected to
22+# follow Semantic Versioning. They should reflect the version the application is using.
23+# It is recommended to use it with quotes.
24+appVersion: "1.16.0"