Merge commit 'efcfdcc'
This commit is contained in:
commit
a873dc6608
11 changed files with 289 additions and 11 deletions
68
THOUGHTS.txt
68
THOUGHTS.txt
|
@ -4322,6 +4322,16 @@ set_link virtio-net-pci.0 on
|
||||||
|
|
||||||
See if both devices are bridge members
|
See if both devices are bridge members
|
||||||
|
|
||||||
|
disable again,check if back to starting position
|
||||||
|
|
||||||
|
|
||||||
|
Wed Mar 13 00:00:16 GMT 2024
|
||||||
|
|
||||||
|
aside: "trigger" is the least bad word I've thought of so far for
|
||||||
|
these services that stop/start other services
|
||||||
|
|
||||||
|
telent: yeah, in general 'ps afuxww' (or s6-ps -H :)) is the way to solve this, look for hung s6-rc processes and in particular their s6-svlisten1 children, where the command line will show what service is still waiting for readiness
|
||||||
|
|
||||||
Wed Mar 20 19:34:36 GMT 2024
|
Wed Mar 20 19:34:36 GMT 2024
|
||||||
|
|
||||||
Because I forgot hoe to rebuild rotuer, I tihnk it is time to improve
|
Because I forgot hoe to rebuild rotuer, I tihnk it is time to improve
|
||||||
|
@ -4344,3 +4354,61 @@ just now but it does mean we can punt on specifying the device inside the
|
||||||
liminix-config which is unreasonably circular.
|
liminix-config which is unreasonably circular.
|
||||||
|
|
||||||
Maybe we'll just chuck a makefile in telent-nixos-config
|
Maybe we'll just chuck a makefile in telent-nixos-config
|
||||||
|
|
||||||
|
Fri Mar 22 22:14:32 GMT 2024
|
||||||
|
|
||||||
|
For the service failover milestone we said
|
||||||
|
|
||||||
|
a. A configuration demonstrating a service which is restarted when it crashes
|
||||||
|
b. A failover config where service B runs iff service A is unavailable
|
||||||
|
c. A config showing different pppd behaviour when interface is flakey (retry) vs ppp password is wrong (report error, wait for resolution)
|
||||||
|
|
||||||
|
Sun Mar 24 23:41:27 GMT 2024
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
1) make liminix-rebuild bounce only affected services instead of
|
||||||
|
full reboot (what does it do about triggered services?)
|
||||||
|
2) sniproxy
|
||||||
|
|
||||||
|
3) see if arhcive still works. usb disk hotplug would be a good candidate for
|
||||||
|
switching to triggers
|
||||||
|
|
||||||
|
Mon Mar 25 19:35:47 GMT 2024
|
||||||
|
|
||||||
|
to make the liminix-rebuild thing restart only affected services, it needs to
|
||||||
|
know when the new service is not like the old one. By default it does not
|
||||||
|
restart a service with a changed up/down/run script unless the name has
|
||||||
|
also changed, so we need to figure out how to generate a "conversion"
|
||||||
|
file with the services that are different
|
||||||
|
|
||||||
|
pkgs/s6-rc-database/default.nix creates $out/compiled, we could add
|
||||||
|
$out/hashes to this
|
||||||
|
|
||||||
|
the other thing making this fun is that we will need to run `activate`
|
||||||
|
(which is usually done in preinit) otherwise the new configuration's
|
||||||
|
fhs directories won't exist.
|
||||||
|
|
||||||
|
so the plan woyuld be
|
||||||
|
|
||||||
|
in liminix-rebuild, when reboot was not chosen,
|
||||||
|
|
||||||
|
- run activate
|
||||||
|
- compare /run/s6-rc/compiled/hashes (old services) with
|
||||||
|
/etc/s6-rc/compiled/hashes (new services)
|
||||||
|
|
||||||
|
- whenever both files have the same column 1 and different
|
||||||
|
column 2, add that name to restart list
|
||||||
|
|
||||||
|
(need to turn restarts.fnl into a lua script)
|
||||||
|
|
||||||
|
s6-rc-update /etc/s6-rc/compiled/hashes restarts
|
||||||
|
|
||||||
|
Tue Mar 26 23:18:53 GMT 2024
|
||||||
|
|
||||||
|
activate overwrites /etc/s6-rc/compiled, which is a problem because
|
||||||
|
s6-rc-update expects to find the old compiled database here so that
|
||||||
|
it can know what to update
|
||||||
|
|
||||||
|
Maybe config.filesystem should specify /etc/s6-rc/compiled.new
|
||||||
|
and something in early boot could symlink /etc/s6-rc/compiled to it
|
||||||
|
|
|
@ -29,6 +29,10 @@ in {
|
||||||
services = mkOption {
|
services = mkOption {
|
||||||
type = types.attrsOf type_service;
|
type = types.attrsOf type_service;
|
||||||
};
|
};
|
||||||
|
system.callService = mkOption {
|
||||||
|
type = types.functionTo (types.functionTo types.anything);
|
||||||
|
};
|
||||||
|
|
||||||
filesystem = mkOption {
|
filesystem = mkOption {
|
||||||
type = types.anything;
|
type = types.anything;
|
||||||
description = ''
|
description = ''
|
||||||
|
@ -111,6 +115,31 @@ in {
|
||||||
"fw_devlink=off"
|
"fw_devlink=off"
|
||||||
] ++ lib.optional (config.rootOptions != null) "rootflags=${config.rootOptions}";
|
] ++ lib.optional (config.rootOptions != null) "rootflags=${config.rootOptions}";
|
||||||
|
|
||||||
|
system.callService = path : parameters :
|
||||||
|
let
|
||||||
|
typeChecked = caller: type: value:
|
||||||
|
let
|
||||||
|
inherit (lib) types mergeDefinitions;
|
||||||
|
defs = [{ file = caller; inherit value; }];
|
||||||
|
type' = types.submodule { options = type; };
|
||||||
|
in (mergeDefinitions [] type' defs).mergedValue;
|
||||||
|
cp = lib.callPackageWith(pkgs // { svc = config.system.service; });
|
||||||
|
pkg = cp path {};
|
||||||
|
checkTypes = t : p : typeChecked (builtins.toString path) t p;
|
||||||
|
in {
|
||||||
|
inherit parameters;
|
||||||
|
build = { dependencies ? [], ... } @ args :
|
||||||
|
let
|
||||||
|
s = pkg (checkTypes parameters
|
||||||
|
(builtins.removeAttrs args ["dependencies"]));
|
||||||
|
in s.overrideAttrs (o: {
|
||||||
|
dependencies = (builtins.map (d: d.name) dependencies) ++ o.dependencies;
|
||||||
|
buildInputs = dependencies ++ o.buildInputs;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
users.root = {
|
users.root = {
|
||||||
uid = 0; gid= 0; gecos = "Root of all evaluation";
|
uid = 0; gid= 0; gecos = "Root of all evaluation";
|
||||||
dir = "/home/root/";
|
dir = "/home/root/";
|
||||||
|
|
|
@ -14,6 +14,8 @@ let
|
||||||
inherit (pkgs) liminix;
|
inherit (pkgs) liminix;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
imports = [ ../ifwait ];
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
system.service.bridge = {
|
system.service.bridge = {
|
||||||
primary = mkOption { type = liminix.lib.types.serviceDefn; };
|
primary = mkOption { type = liminix.lib.types.serviceDefn; };
|
||||||
|
@ -27,7 +29,7 @@ in
|
||||||
description = "bridge interface name to create";
|
description = "bridge interface name to create";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
members = liminix.callService ./members.nix {
|
members = config.system.callService ./members.nix {
|
||||||
primary = mkOption {
|
primary = mkOption {
|
||||||
type = liminix.lib.types.interface;
|
type = liminix.lib.types.interface;
|
||||||
description = "primary bridge interface";
|
description = "primary bridge interface";
|
||||||
|
@ -47,5 +49,5 @@ in
|
||||||
# a better way to test for the existence of vlan config:
|
# a better way to test for the existence of vlan config:
|
||||||
# maybe the module should set an `enabled` attribute?
|
# maybe the module should set an `enabled` attribute?
|
||||||
BRIDGE_VLAN_FILTERING = "y";
|
BRIDGE_VLAN_FILTERING = "y";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
liminix
|
liminix
|
||||||
, ifwait
|
, ifwait
|
||||||
, lib
|
, lib
|
||||||
|
, svc
|
||||||
}:
|
}:
|
||||||
{ members, primary } :
|
{ members, primary } :
|
||||||
|
|
||||||
|
@ -10,14 +11,20 @@ let
|
||||||
inherit (liminix.services) bundle oneshot;
|
inherit (liminix.services) bundle oneshot;
|
||||||
inherit (lib) mkOption types;
|
inherit (lib) mkOption types;
|
||||||
addif = member :
|
addif = member :
|
||||||
oneshot {
|
# how do we get sight of services from here? maybe we need to
|
||||||
name = "${primary.name}.member.${member.name}";
|
# implement ifwait as a regualr derivation instead of a
|
||||||
up = ''
|
# servicedefinition
|
||||||
dev=$(output ${member} ifname)
|
svc.ifwait.build {
|
||||||
${ifwait}/bin/ifwait $dev running && ip link set dev $dev master $(output ${primary} ifname)
|
state = "running";
|
||||||
'';
|
interface = member;
|
||||||
down = "ip link set dev $(output ${member} ifname) nomaster";
|
|
||||||
dependencies = [ primary member ];
|
dependencies = [ primary member ];
|
||||||
|
service = oneshot {
|
||||||
|
name = "${primary.name}.member.${member.name}";
|
||||||
|
up = ''
|
||||||
|
ip link set dev $(output ${member} ifname) master $(output ${primary} ifname)
|
||||||
|
'';
|
||||||
|
down = "ip link set dev $(output ${member} ifname) nomaster";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
in bundle {
|
in bundle {
|
||||||
name = "${primary.name}.members";
|
name = "${primary.name}.members";
|
||||||
|
|
18
modules/ifwait/default.nix
Normal file
18
modules/ifwait/default.nix
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{ config, pkgs, lib, ... } :
|
||||||
|
let
|
||||||
|
inherit (pkgs) liminix;
|
||||||
|
inherit (lib) mkOption types;
|
||||||
|
in {
|
||||||
|
options.system.service.ifwait =
|
||||||
|
mkOption { type = liminix.lib.types.serviceDefn; };
|
||||||
|
|
||||||
|
config.system.service.ifwait = config.system.callService ./ifwait.nix {
|
||||||
|
state = mkOption { type = types.str; };
|
||||||
|
interface = mkOption {
|
||||||
|
type = liminix.lib.types.interface;
|
||||||
|
};
|
||||||
|
service = mkOption {
|
||||||
|
type = liminix.lib.types.service;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
15
modules/ifwait/ifwait.nix
Normal file
15
modules/ifwait/ifwait.nix
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{ ifwait, liminix } :
|
||||||
|
{
|
||||||
|
state
|
||||||
|
, interface
|
||||||
|
, service
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (liminix.services) longrun;
|
||||||
|
in longrun {
|
||||||
|
name = "ifwait.${interface.name}";
|
||||||
|
buildInputs = [ service ];
|
||||||
|
run = ''
|
||||||
|
${ifwait}/bin/ifwait -s ${service.name} $(output ${interface} ifname) ${state}
|
||||||
|
'';
|
||||||
|
}
|
|
@ -37,8 +37,8 @@
|
||||||
(when (not (= up wanted?))
|
(when (not (= up wanted?))
|
||||||
(set up
|
(set up
|
||||||
(if wanted?
|
(if wanted?
|
||||||
(pcall system (.. "s6-rc -u change " service))
|
(pcall system (.. "s6-rc -b -u change " service))
|
||||||
(not (pcall system (.. "s6-rc -d change " service)))))
|
(not (pcall system (.. "s6-rc -b -d change " service)))))
|
||||||
))
|
))
|
||||||
|
|
||||||
(fn run [args event-fn]
|
(fn run [args event-fn]
|
||||||
|
|
|
@ -8,4 +8,5 @@
|
||||||
min-copy-closure = import ./min-copy-closure/test.nix;
|
min-copy-closure = import ./min-copy-closure/test.nix;
|
||||||
fennel = import ./fennel/test.nix;
|
fennel = import ./fennel/test.nix;
|
||||||
tftpboot = import ./tftpboot/test.nix;
|
tftpboot = import ./tftpboot/test.nix;
|
||||||
|
updown = import ./updown/test.nix;
|
||||||
}
|
}
|
||||||
|
|
55
tests/updown/configuration.nix
Normal file
55
tests/updown/configuration.nix
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
{ config, pkgs, lib, ... } :
|
||||||
|
let
|
||||||
|
inherit (pkgs.liminix.services) bundle oneshot longrun;
|
||||||
|
inherit (pkgs) serviceFns;
|
||||||
|
# EDIT: you can pick your preferred RFC1918 address space
|
||||||
|
# for NATted connections, if you don't like this one.
|
||||||
|
ipv4LocalNet = "10.8.0";
|
||||||
|
svc = config.system.service;
|
||||||
|
|
||||||
|
in rec {
|
||||||
|
imports = [
|
||||||
|
../../modules/bridge
|
||||||
|
../../modules/dhcp6c
|
||||||
|
../../modules/dnsmasq
|
||||||
|
../../modules/firewall
|
||||||
|
../../modules/hostapd
|
||||||
|
../../modules/network
|
||||||
|
../../modules/ssh
|
||||||
|
../../modules/vlan
|
||||||
|
../../modules/wlan.nix
|
||||||
|
];
|
||||||
|
rootfsType = "jffs2";
|
||||||
|
hostname = "updown";
|
||||||
|
|
||||||
|
services.int = svc.network.address.build {
|
||||||
|
interface = svc.bridge.primary.build { ifname = "int"; };
|
||||||
|
family = "inet"; address = "${ipv4LocalNet}.1"; prefixLength = 16;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.bridge = svc.bridge.members.build {
|
||||||
|
primary = services.int;
|
||||||
|
members = with config.hardware.networkInterfaces;
|
||||||
|
[ lan ];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.sshd = svc.ssh.build { };
|
||||||
|
|
||||||
|
# users.root = {
|
||||||
|
# # EDIT: choose a root password and then use
|
||||||
|
# # "mkpasswd -m sha512crypt" to determine the hash.
|
||||||
|
# # It should start wirh $6$.
|
||||||
|
# passwd = "$6$6HG7WALLQQY1LQDE$428cnouMJ7wVmyK9.dF1uWs7t0z9ztgp3MHvN5bbeo0M4Kqg/u2ThjoSHIjCEJQlnVpDOaEKcOjXAlIClHWN21";
|
||||||
|
# openssh.authorizedKeys.keys = [
|
||||||
|
# # EDIT: you can add your ssh pubkey here
|
||||||
|
# # "ssh-rsa AAAAB3NzaC1....H6hKd user@example.com";
|
||||||
|
# ];
|
||||||
|
# };
|
||||||
|
|
||||||
|
defaultProfile.packages = with pkgs; [
|
||||||
|
min-collect-garbage
|
||||||
|
# strace
|
||||||
|
# ethtool
|
||||||
|
tcpdump
|
||||||
|
];
|
||||||
|
}
|
64
tests/updown/script.expect
Normal file
64
tests/updown/script.expect
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
set timeout 10
|
||||||
|
|
||||||
|
spawn socat unix-connect:vm/monitor -
|
||||||
|
set monitor_id $spawn_id
|
||||||
|
|
||||||
|
expect "(qemu)"
|
||||||
|
send "set_link virtio-net-pci.1 off\n"
|
||||||
|
expect "(qemu)"
|
||||||
|
send "set_link virtio-net-pci.0 off\n"
|
||||||
|
expect "(qemu)"
|
||||||
|
send "c\r\n"
|
||||||
|
spawn socat unix-connect:vm/console -
|
||||||
|
set console_id $spawn_id
|
||||||
|
|
||||||
|
expect "BusyBox"
|
||||||
|
expect "#" { send "PS1=RE\\ADY_\\ ; stty -echo \r" }
|
||||||
|
expect "READY_" { send "s6-rc -b -a list\r" } ; # -b waits for s6-rc lock
|
||||||
|
expect "READY_" { send "ls /sys/class/net/lan/master\r" }
|
||||||
|
expect {
|
||||||
|
"No such file or directory" { }
|
||||||
|
timeout { exit 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
expect "READY_" { send "cat /sys/class/net/lan/operstate\r" }
|
||||||
|
expect {
|
||||||
|
"down" { }
|
||||||
|
"up" { exit 1 }
|
||||||
|
}
|
||||||
|
expect "READY_" { send "s6-rc -a -u change\r" }
|
||||||
|
expect {
|
||||||
|
"unable to take locks" { exit 1 }
|
||||||
|
"READY_" { send "\r" }
|
||||||
|
}
|
||||||
|
|
||||||
|
set spawn_id $monitor_id
|
||||||
|
send "\r"
|
||||||
|
expect "(qemu)"
|
||||||
|
send "set_link virtio-net-pci.1 on\n"
|
||||||
|
expect "(qemu)"
|
||||||
|
send "set_link virtio-net-pci.0 on\n"
|
||||||
|
expect "(qemu)"
|
||||||
|
set spawn_id $console_id
|
||||||
|
|
||||||
|
expect "entered forwarding state"
|
||||||
|
send "\r"
|
||||||
|
expect "READY_" { send "cat /sys/class/net/lan/operstate\r" }
|
||||||
|
expect {
|
||||||
|
"down" { exit 1 }
|
||||||
|
"up" { }
|
||||||
|
}
|
||||||
|
|
||||||
|
expect "READY_" { send "cat /sys/class/net/lan/master/uevent\r" }
|
||||||
|
expect {
|
||||||
|
"INTERFACE=int" { }
|
||||||
|
timeout { exit 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
expect "READY_" { send "s6-rc listall int.link.a.10.8.0.1.member.lan.link ; hostname\r" }
|
||||||
|
|
||||||
|
expect {
|
||||||
|
"updown" {}
|
||||||
|
timeout { exit 1 }
|
||||||
|
}
|
||||||
|
|
19
tests/updown/test.nix
Normal file
19
tests/updown/test.nix
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
liminix
|
||||||
|
, nixpkgs
|
||||||
|
}:
|
||||||
|
let img = (import liminix {
|
||||||
|
device = import "${liminix}/devices/qemu/";
|
||||||
|
liminix-config = ./configuration.nix;
|
||||||
|
}).outputs.vmroot;
|
||||||
|
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; };
|
||||||
|
in pkgs.runCommand "check" {
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
expect
|
||||||
|
socat
|
||||||
|
] ;
|
||||||
|
} ''
|
||||||
|
mkdir vm
|
||||||
|
${img}/run.sh --flag -S --background ./vm
|
||||||
|
expect ${./script.expect} | tee $out
|
||||||
|
''
|
Loading…
Reference in a new issue