Merge pull request #81848 from grahamc/nested-specialisation
specialisation: replace nesting with named configurations
This commit is contained in:
commit
35d8514a91
@ -40,7 +40,7 @@ networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
|
||||
<note>
|
||||
<para>
|
||||
If you are switching networks with different proxy configurations, use the
|
||||
<literal>nesting.clone</literal> option in
|
||||
<literal>specialisation</literal> option in
|
||||
<literal>configuration.nix</literal> to switch proxies at runtime. Refer to
|
||||
<xref linkend="ch-options" /> for more information.
|
||||
</para>
|
||||
|
@ -203,6 +203,50 @@ environment.systemPackages = [
|
||||
<link xlink:href="https://github.com/gollum/gollum/wiki/5.0-release-notes#migrating-your-wiki">here</link>.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
The NixOS options <literal>nesting.clone</literal> and
|
||||
<literal>nesting.children</literal> have been deleted, and
|
||||
replaced with named <xref linkend="opt-specialisation"/>
|
||||
configurations.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Replace a <literal>nesting.clone</literal> entry with:
|
||||
|
||||
<programlisting>{
|
||||
<link xlink:href="#opt-specialisation">specialisation.example-sub-configuration</link> = {
|
||||
<link xlink:href="#opt-specialisation._name_.configuration">configuration</link> = {
|
||||
...
|
||||
};
|
||||
};</programlisting>
|
||||
|
||||
</para>
|
||||
<para>
|
||||
Replace a <literal>nesting.children</literal> entry with:
|
||||
|
||||
<programlisting>{
|
||||
<link xlink:href="#opt-specialisation">specialisation.example-sub-configuration</link> = {
|
||||
<link xlink:href="#opt-specialisation._name_.inheritParentConfig">inheritParentConfig</link> = false;
|
||||
<link xlink:href="#opt-specialisation._name_.configuration">configuration</link> = {
|
||||
...
|
||||
};
|
||||
};</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To switch to a specialised configuration at runtime you need to
|
||||
run:
|
||||
<programlisting>
|
||||
# sudo /run/current-system/specialisation/example-sub-configuration/bin/switch-to-configuration test
|
||||
</programlisting>
|
||||
Before you would have used:
|
||||
<programlisting>
|
||||
# sudo /run/current-system/fine-tune/child-1/bin/switch-to-configuration test
|
||||
</programlisting>
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
|
||||
|
@ -4,6 +4,5 @@ with lib;
|
||||
|
||||
{
|
||||
boot.loader.grub.device = mkOverride 0 "nodev";
|
||||
nesting.children = mkOverride 0 [];
|
||||
nesting.clone = mkOverride 0 [];
|
||||
specialisation = mkOverride 0 {};
|
||||
}
|
||||
|
@ -11,21 +11,16 @@ let
|
||||
# you can provide an easy way to boot the same configuration
|
||||
# as you use, but with another kernel
|
||||
# !!! fix this
|
||||
cloner = inheritParent: list:
|
||||
map (childConfig:
|
||||
children = mapAttrs (childName: childConfig:
|
||||
(import ../../../lib/eval-config.nix {
|
||||
inherit baseModules;
|
||||
system = config.nixpkgs.initialSystem;
|
||||
modules =
|
||||
(optionals inheritParent modules)
|
||||
(optionals childConfig.inheritParentConfig modules)
|
||||
++ [ ./no-clone.nix ]
|
||||
++ [ childConfig ];
|
||||
++ [ childConfig.configuration ];
|
||||
}).config.system.build.toplevel
|
||||
) list;
|
||||
|
||||
children =
|
||||
cloner false config.nesting.children
|
||||
++ cloner true config.nesting.clone;
|
||||
) config.specialisation;
|
||||
|
||||
systemBuilder =
|
||||
let
|
||||
@ -77,12 +72,9 @@ let
|
||||
echo -n "$nixosLabel" > $out/nixos-version
|
||||
echo -n "${config.boot.kernelPackages.stdenv.hostPlatform.system}" > $out/system
|
||||
|
||||
mkdir $out/fine-tune
|
||||
childCount=0
|
||||
for i in $children; do
|
||||
childCount=$(( childCount + 1 ))
|
||||
ln -s $i $out/fine-tune/child-$childCount
|
||||
done
|
||||
mkdir $out/specialisation
|
||||
${concatStringsSep "\n"
|
||||
(mapAttrsToList (name: path: "ln -s ${path} $out/specialisation/${name}") children)}
|
||||
|
||||
mkdir $out/bin
|
||||
export localeArchive="${config.i18n.glibcLocales}/lib/locale/locale-archive"
|
||||
@ -112,7 +104,6 @@ let
|
||||
shell = "${pkgs.bash}/bin/sh";
|
||||
su = "${pkgs.shadow.su}/bin/su";
|
||||
|
||||
inherit children;
|
||||
kernelParams = config.boot.kernelParams;
|
||||
installBootLoader =
|
||||
config.system.build.installBootLoader
|
||||
@ -143,6 +134,11 @@ let
|
||||
in
|
||||
|
||||
{
|
||||
imports = [
|
||||
(mkRemovedOptionModule [ "nesting" "clone" ] "Use `specialisation.«name» = { inheritParentConfig = true; configuration = { ... }; }` instead.")
|
||||
(mkRemovedOptionModule [ "nesting" "children" ] "Use `specialisation.«name».configuration = { ... }` instead.")
|
||||
];
|
||||
|
||||
options = {
|
||||
|
||||
system.build = mkOption {
|
||||
@ -154,26 +150,35 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
nesting.children = mkOption {
|
||||
default = [];
|
||||
specialisation = mkOption {
|
||||
default = {};
|
||||
example = lib.literalExample "{ fewJobsManyCores.configuration = { nix.buildCores = 0; nix.maxJobs = 1; }; }";
|
||||
description = ''
|
||||
Additional configurations to build.
|
||||
'';
|
||||
};
|
||||
Additional configurations to build. If
|
||||
<literal>inheritParentConfig</literal> is true, the system
|
||||
will be based on the overall system configuration.
|
||||
|
||||
nesting.clone = mkOption {
|
||||
default = [];
|
||||
description = ''
|
||||
Additional configurations to build based on the current
|
||||
configuration which then has a lower priority.
|
||||
|
||||
To switch to a cloned configuration (e.g. <literal>child-1</literal>)
|
||||
at runtime, run
|
||||
To switch to a specialised configuration
|
||||
(e.g. <literal>fewJobsManyCores</literal>) at runtime, run:
|
||||
|
||||
<programlisting>
|
||||
# sudo /run/current-system/fine-tune/child-1/bin/switch-to-configuration test
|
||||
# sudo /run/current-system/specialisation/fewJobsManyCores/bin/switch-to-configuration test
|
||||
</programlisting>
|
||||
'';
|
||||
type = types.attrsOf (types.submodule (
|
||||
{ ... }: {
|
||||
options.inheritParentConfig = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Include the entire system's configuration. Set to false to make a completely differently configured system.";
|
||||
};
|
||||
|
||||
options.configuration = mkOption {
|
||||
default = {};
|
||||
description = "Arbitrary NixOS configuration options.";
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
system.boot.loader.id = mkOption {
|
||||
|
@ -409,7 +409,7 @@ $conf .= "$extraEntries\n" unless $extraEntriesBeforeNixOS;
|
||||
|
||||
# Find all the children of the current default configuration
|
||||
# Do not search for grand children
|
||||
my @links = sort (glob "$defaultConfig/fine-tune/*");
|
||||
my @links = sort (glob "$defaultConfig/specialisation/*");
|
||||
foreach my $link (@links) {
|
||||
|
||||
my $entryName = "";
|
||||
@ -425,7 +425,8 @@ foreach my $link (@links) {
|
||||
if ($cfgName) {
|
||||
$entryName = $cfgName;
|
||||
} else {
|
||||
$entryName = "($date - $version)";
|
||||
my $linkname = basename($link);
|
||||
$entryName = "($linkname - $date - $version)";
|
||||
}
|
||||
addEntry("NixOS - $entryName", $link);
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ addEntry "NixOS - Default" $defaultConfig ""
|
||||
|
||||
# Add all generations of the system profile to the menu, in reverse
|
||||
# (most recent to least recent) order.
|
||||
for link in $((ls -d $defaultConfig/fine-tune/* ) | sort -n); do
|
||||
for link in $((ls -d $defaultConfig/specialisation/* ) | sort -n); do
|
||||
date=$(stat --printf="%y\n" $link | sed 's/\..*//')
|
||||
addEntry "NixOS - variation" $link ""
|
||||
done
|
||||
|
@ -91,8 +91,7 @@ in import ./make-test-python.nix {
|
||||
|
||||
security.acme.server = "https://acme-v02.api.letsencrypt.org/dir";
|
||||
|
||||
nesting.clone = [
|
||||
({pkgs, ...}: {
|
||||
specialisation.second-cert.configuration = {pkgs, ...}: {
|
||||
systemd.targets."acme-finished-b.example.com" = {};
|
||||
systemd.services."acme-b.example.com" = {
|
||||
wants = [ "acme-finished-b.example.com.target" ];
|
||||
@ -107,8 +106,8 @@ in import ./make-test-python.nix {
|
||||
echo hello world > "$out/index.html"
|
||||
'';
|
||||
};
|
||||
})
|
||||
({pkgs, config, nodes, lib, ...}: {
|
||||
};
|
||||
specialisation.dns-01.configuration = {pkgs, config, nodes, lib, ...}: {
|
||||
security.acme.certs."example.com" = {
|
||||
domain = "*.example.com";
|
||||
dnsProvider = "exec";
|
||||
@ -135,8 +134,7 @@ in import ./make-test-python.nix {
|
||||
echo hello world > "$out/index.html"
|
||||
'';
|
||||
};
|
||||
})
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
client = {nodes, lib, ...}: {
|
||||
@ -196,7 +194,7 @@ in import ./make-test-python.nix {
|
||||
|
||||
with subtest("Can add another certificate for nginx service"):
|
||||
webserver.succeed(
|
||||
"/run/current-system/fine-tune/child-1/bin/switch-to-configuration test"
|
||||
"/run/current-system/specialisation/second-cert/bin/switch-to-configuration test"
|
||||
)
|
||||
webserver.wait_for_unit("acme-finished-b.example.com.target")
|
||||
client.succeed(
|
||||
@ -208,7 +206,7 @@ in import ./make-test-python.nix {
|
||||
"${switchToNewServer}"
|
||||
)
|
||||
webserver.succeed(
|
||||
"/run/current-system/fine-tune/child-2/bin/switch-to-configuration test"
|
||||
"/run/current-system/specialisation/dns-01/bin/switch-to-configuration test"
|
||||
)
|
||||
webserver.wait_for_unit("acme-finished-example.com.target")
|
||||
client.succeed(
|
||||
|
@ -202,7 +202,7 @@ in
|
||||
nat.standalone = handleTest ./nat.nix { withFirewall = false; };
|
||||
ndppd = handleTest ./ndppd.nix {};
|
||||
neo4j = handleTest ./neo4j.nix {};
|
||||
nesting = handleTest ./nesting.nix {};
|
||||
specialisation = handleTest ./specialisation.nix {};
|
||||
netdata = handleTest ./netdata.nix {};
|
||||
networking.networkd = handleTest ./networking.nix { networkd = true; };
|
||||
networking.scripted = handleTest ./networking.nix { networkd = false; };
|
||||
|
@ -20,8 +20,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
|
||||
}
|
||||
'';
|
||||
|
||||
nesting.clone = [
|
||||
{
|
||||
specialisation.etag.configuration = {
|
||||
services.caddy.config = lib.mkForce ''
|
||||
http://localhost {
|
||||
gzip
|
||||
@ -34,21 +33,20 @@ import ./make-test-python.nix ({ pkgs, ... }: {
|
||||
}
|
||||
}
|
||||
'';
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
specialisation.config-reload.configuration = {
|
||||
services.caddy.config = ''
|
||||
http://localhost:8080 {
|
||||
}
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = { nodes, ... }: let
|
||||
etagSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-1";
|
||||
justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-2";
|
||||
etagSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/etag";
|
||||
justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/config-reload";
|
||||
in ''
|
||||
url = "http://localhost/example.html"
|
||||
webserver.wait_for_unit("caddy")
|
||||
|
@ -65,7 +65,7 @@ let
|
||||
# partitions and filesystems.
|
||||
testScriptFun = { bootLoader, createPartitions, grubVersion, grubDevice, grubUseEfi
|
||||
, grubIdentifier, preBootCommands, extraConfig
|
||||
, testCloneConfig
|
||||
, testSpecialisationConfig
|
||||
}:
|
||||
let iface = if grubVersion == 1 then "ide" else "virtio";
|
||||
isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
|
||||
@ -220,7 +220,7 @@ let
|
||||
|
||||
# Tests for validating clone configuration entries in grub menu
|
||||
''
|
||||
+ optionalString testCloneConfig ''
|
||||
+ optionalString testSpecialisationConfig ''
|
||||
# Reboot Machine
|
||||
machine = create_machine_named("clone-default-config")
|
||||
${preBootCommands}
|
||||
@ -262,7 +262,7 @@ let
|
||||
, bootLoader ? "grub" # either "grub" or "systemd-boot"
|
||||
, grubVersion ? 2, grubDevice ? "/dev/vda", grubIdentifier ? "uuid", grubUseEfi ? false
|
||||
, enableOCR ? false, meta ? {}
|
||||
, testCloneConfig ? false
|
||||
, testSpecialisationConfig ? false
|
||||
}:
|
||||
makeTest {
|
||||
inherit enableOCR;
|
||||
@ -337,7 +337,7 @@ let
|
||||
testScript = testScriptFun {
|
||||
inherit bootLoader createPartitions preBootCommands
|
||||
grubVersion grubDevice grubIdentifier grubUseEfi extraConfig
|
||||
testCloneConfig;
|
||||
testSpecialisationConfig;
|
||||
};
|
||||
};
|
||||
|
||||
@ -411,11 +411,11 @@ let
|
||||
grubUseEfi = true;
|
||||
};
|
||||
|
||||
clone-test-extraconfig = {
|
||||
specialisation-test-extraconfig = {
|
||||
extraConfig = ''
|
||||
environment.systemPackages = [ pkgs.grub2 ];
|
||||
boot.loader.grub.configurationName = "Home";
|
||||
nesting.clone = [ {
|
||||
specialisation.work.configuration = {
|
||||
boot.loader.grub.configurationName = lib.mkForce "Work";
|
||||
|
||||
environment.etc = {
|
||||
@ -424,9 +424,9 @@ let
|
||||
gitproxy = none for work.com
|
||||
";
|
||||
};
|
||||
} ];
|
||||
};
|
||||
'';
|
||||
testCloneConfig = true;
|
||||
testSpecialisationConfig = true;
|
||||
};
|
||||
|
||||
|
||||
@ -440,7 +440,7 @@ in {
|
||||
simple = makeInstallerTest "simple" simple-test-config;
|
||||
|
||||
# Test cloned configurations with the simple grub configuration
|
||||
simpleClone = makeInstallerTest "simpleClone" (simple-test-config // clone-test-extraconfig);
|
||||
simpleSpecialised = makeInstallerTest "simpleSpecialised" (simple-test-config // specialisation-test-extraconfig);
|
||||
|
||||
# Simple GPT/UEFI configuration using systemd-boot with 3 partitions: ESP, swap & root filesystem
|
||||
simpleUefiSystemdBoot = makeInstallerTest "simpleUefiSystemdBoot" {
|
||||
@ -467,7 +467,7 @@ in {
|
||||
simpleUefiGrub = makeInstallerTest "simpleUefiGrub" simple-uefi-grub-config;
|
||||
|
||||
# Test cloned configurations with the uefi grub configuration
|
||||
simpleUefiGrubClone = makeInstallerTest "simpleUefiGrubClone" (simple-uefi-grub-config // clone-test-extraconfig);
|
||||
simpleUefiGrubSpecialisation = makeInstallerTest "simpleUefiGrubSpecialisation" (simple-uefi-grub-config // specialisation-test-extraconfig);
|
||||
|
||||
# Same as the previous, but now with a separate /boot partition.
|
||||
separateBoot = makeInstallerTest "separateBoot" {
|
||||
|
@ -1,44 +0,0 @@
|
||||
import ./make-test-python.nix {
|
||||
name = "nesting";
|
||||
nodes = {
|
||||
clone = { pkgs, ... }: {
|
||||
environment.systemPackages = [ pkgs.cowsay ];
|
||||
nesting.clone = [
|
||||
({ pkgs, ... }: {
|
||||
environment.systemPackages = [ pkgs.hello ];
|
||||
})
|
||||
];
|
||||
};
|
||||
children = { pkgs, ... }: {
|
||||
environment.systemPackages = [ pkgs.cowsay ];
|
||||
nesting.children = [
|
||||
({ pkgs, ... }: {
|
||||
environment.systemPackages = [ pkgs.hello ];
|
||||
})
|
||||
];
|
||||
};
|
||||
};
|
||||
testScript = ''
|
||||
clone.wait_for_unit("default.target")
|
||||
clone.succeed("cowsay hey")
|
||||
clone.fail("hello")
|
||||
|
||||
with subtest("Nested clones do inherit from parent"):
|
||||
clone.succeed(
|
||||
"/run/current-system/fine-tune/child-1/bin/switch-to-configuration test"
|
||||
)
|
||||
clone.succeed("cowsay hey")
|
||||
clone.succeed("hello")
|
||||
|
||||
children.wait_for_unit("default.target")
|
||||
children.succeed("cowsay hey")
|
||||
children.fail("hello")
|
||||
|
||||
with subtest("Nested children do not inherit from parent"):
|
||||
children.succeed(
|
||||
"/run/current-system/fine-tune/child-1/bin/switch-to-configuration test"
|
||||
)
|
||||
children.fail("cowsay hey")
|
||||
children.succeed("hello")
|
||||
'';
|
||||
}
|
@ -19,7 +19,7 @@ import ./make-test-python.nix {
|
||||
'';
|
||||
};
|
||||
|
||||
nesting.clone = lib.singleton {
|
||||
specialisation.pass-checks.configuration = {
|
||||
services.nginx.virtualHosts.server = {
|
||||
root = lib.mkForce (pkgs.runCommandLocal "testdir2" {} ''
|
||||
mkdir "$out"
|
||||
@ -70,7 +70,7 @@ import ./make-test-python.nix {
|
||||
|
||||
testScript = { nodes, ... }: let
|
||||
inherit (nodes.server.config.system.build) toplevel;
|
||||
newSystem = "${toplevel}/fine-tune/child-1";
|
||||
newSystem = "${toplevel}/specialisation/pass-checks";
|
||||
in ''
|
||||
start_all()
|
||||
|
||||
|
@ -42,38 +42,35 @@ import ./make-test-python.nix ({ pkgs, ... }: {
|
||||
|
||||
services.nginx.enableReload = true;
|
||||
|
||||
nesting.clone = [
|
||||
{
|
||||
specialisation.etagSystem.configuration = {
|
||||
services.nginx.virtualHosts.localhost = {
|
||||
root = lib.mkForce (pkgs.runCommand "testdir2" {} ''
|
||||
mkdir "$out"
|
||||
echo content changed > "$out/index.html"
|
||||
'');
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
services.nginx.virtualHosts."1.my.test".listen = [ { addr = "127.0.0.1"; port = 8080; }];
|
||||
}
|
||||
|
||||
{
|
||||
services.nginx.package = pkgs.nginxUnstable;
|
||||
}
|
||||
|
||||
{
|
||||
services.nginx.package = pkgs.nginxUnstable;
|
||||
services.nginx.virtualHosts."!@$$(#*%".locations."~@#*$*!)".proxyPass = ";;;";
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
specialisation.justReloadSystem.configuration = {
|
||||
services.nginx.virtualHosts."1.my.test".listen = [ { addr = "127.0.0.1"; port = 8080; }];
|
||||
};
|
||||
|
||||
specialisation.reloadRestartSystem.configuration = {
|
||||
services.nginx.package = pkgs.nginxUnstable;
|
||||
};
|
||||
|
||||
specialisation.reloadWithErrorsSystem.configuration = {
|
||||
services.nginx.package = pkgs.nginxUnstable;
|
||||
services.nginx.virtualHosts."!@$$(#*%".locations."~@#*$*!)".proxyPass = ";;;";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = { nodes, ... }: let
|
||||
etagSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-1";
|
||||
justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-2";
|
||||
reloadRestartSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-3";
|
||||
reloadWithErrorsSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-4";
|
||||
etagSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/etagSystem";
|
||||
justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/justReloadSystem";
|
||||
reloadRestartSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/reloadRestartSystem";
|
||||
reloadWithErrorsSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/reloadWithErrorsSystem";
|
||||
in ''
|
||||
url = "http://localhost/index.html"
|
||||
|
||||
|
43
nixos/tests/specialisation.nix
Normal file
43
nixos/tests/specialisation.nix
Normal file
@ -0,0 +1,43 @@
|
||||
import ./make-test-python.nix {
|
||||
name = "specialisation";
|
||||
nodes = {
|
||||
inheritconf = { pkgs, ... }: {
|
||||
environment.systemPackages = [ pkgs.cowsay ];
|
||||
specialisation.inheritconf.configuration = { pkgs, ... }: {
|
||||
environment.systemPackages = [ pkgs.hello ];
|
||||
};
|
||||
};
|
||||
noinheritconf = { pkgs, ... }: {
|
||||
environment.systemPackages = [ pkgs.cowsay ];
|
||||
specialisation.noinheritconf = {
|
||||
inheritParentConfig = false;
|
||||
configuration = { pkgs, ... }: {
|
||||
environment.systemPackages = [ pkgs.hello ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
testScript = ''
|
||||
inheritconf.wait_for_unit("default.target")
|
||||
inheritconf.succeed("cowsay hey")
|
||||
inheritconf.fail("hello")
|
||||
|
||||
with subtest("Nested clones do inherit from parent"):
|
||||
inheritconf.succeed(
|
||||
"/run/current-system/specialisation/inheritconf/bin/switch-to-configuration test"
|
||||
)
|
||||
inheritconf.succeed("cowsay hey")
|
||||
inheritconf.succeed("hello")
|
||||
|
||||
noinheritconf.wait_for_unit("default.target")
|
||||
noinheritconf.succeed("cowsay hey")
|
||||
noinheritconf.fail("hello")
|
||||
|
||||
with subtest("Nested children do not inherit from parent"):
|
||||
noinheritconf.succeed(
|
||||
"/run/current-system/specialisation/noinheritconf/bin/switch-to-configuration test"
|
||||
)
|
||||
noinheritconf.fail("cowsay hey")
|
||||
noinheritconf.succeed("hello")
|
||||
'';
|
||||
}
|
Loading…
Reference in New Issue
Block a user