mirror of
https://github.com/nix-community/home-manager.git
synced 2025-12-01 14:41:03 +01:00
podman: link dependent quadlets during build
podman's systemd generator can automatically resolve unit dependencies, so instead of us guessing these links to create them, we provide the sources during generation
This commit is contained in:
parent
4108ec3aa8
commit
81bf639da7
10 changed files with 96 additions and 91 deletions
|
|
@ -7,7 +7,7 @@ let
|
||||||
|
|
||||||
createQuadletSource = name: buildDef:
|
createQuadletSource = name: buildDef:
|
||||||
let
|
let
|
||||||
buildConfig = podman-lib.deepMerge {
|
quadlet = podman-lib.deepMerge {
|
||||||
Build = {
|
Build = {
|
||||||
AuthFile = buildDef.authFile;
|
AuthFile = buildDef.authFile;
|
||||||
Environment = buildDef.environment;
|
Environment = buildDef.environment;
|
||||||
|
|
@ -30,21 +30,26 @@ let
|
||||||
};
|
};
|
||||||
Unit = { Description = buildDef.description; };
|
Unit = { Description = buildDef.description; };
|
||||||
} buildDef.extraConfig;
|
} buildDef.extraConfig;
|
||||||
in ''
|
in {
|
||||||
# Automatically generated by home-manager for podman build configuration
|
attrs = quadlet;
|
||||||
# DO NOT EDIT THIS FILE DIRECTLY
|
text = ''
|
||||||
#
|
# Automatically generated by home-manager for podman build configuration
|
||||||
# ${name}.build
|
# DO NOT EDIT THIS FILE DIRECTLY
|
||||||
${podman-lib.toQuadletIni buildConfig}
|
#
|
||||||
'';
|
# ${name}.build
|
||||||
|
${podman-lib.toQuadletIni quadlet}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
toQuadletInternal = name: buildDef: {
|
toQuadletInternal = name: buildDef:
|
||||||
assertions = podman-lib.buildConfigAsserts name buildDef.extraConfig;
|
let src = createQuadletSource name buildDef;
|
||||||
serviceName =
|
in {
|
||||||
"podman-${name}"; # quadlet service name: 'podman-<name>-build.service
|
assertions = podman-lib.buildConfigAsserts name buildDef.extraConfig;
|
||||||
source = podman-lib.removeBlankLines (createQuadletSource name buildDef);
|
serviceName =
|
||||||
resourceType = "build";
|
"podman-${name}"; # generated service name: 'podman-<name>-build.service
|
||||||
};
|
source = podman-lib.removeBlankLines src.text;
|
||||||
|
resourceType = "build";
|
||||||
|
};
|
||||||
in let
|
in let
|
||||||
buildDefinitionType = types.submodule ({ name, ... }: {
|
buildDefinitionType = types.submodule ({ name, ... }: {
|
||||||
options = {
|
options = {
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,10 @@ let
|
||||||
|
|
||||||
createQuadletSource = name: containerDef:
|
createQuadletSource = name: containerDef:
|
||||||
let
|
let
|
||||||
# formatServiceNameForType = type: name:
|
|
||||||
# {
|
|
||||||
# image = "${name}-image.service";
|
|
||||||
# build = "${name}-build.service";
|
|
||||||
# network = "${name}-network.service";
|
|
||||||
# volume = "${name}-volume.service";
|
|
||||||
# }."${type}";
|
|
||||||
|
|
||||||
dependencyBySuffix = type: name:
|
dependencyBySuffix = type: name:
|
||||||
if (hasInfix ".${type}" name) then
|
if (hasInfix ".${type}" name) then
|
||||||
let
|
let baseName = elemAt (splitString ".${type}" name) 0;
|
||||||
baseName = elemAt (splitString ".${type}" name) 0;
|
in if (hasAttr baseName cfg.internal.builtQuadlets) then
|
||||||
in
|
|
||||||
if (hasAttr (builtins.trace (baseName) baseName) cfg.internal.builtQuadlets) then
|
|
||||||
[ (cfg.internal.builtQuadlets.${baseName}) ]
|
[ (cfg.internal.builtQuadlets.${baseName}) ]
|
||||||
else
|
else
|
||||||
[ ]
|
[ ]
|
||||||
|
|
@ -30,25 +20,34 @@ let
|
||||||
[ ];
|
[ ];
|
||||||
|
|
||||||
withResolverFor = type: value:
|
withResolverFor = type: value:
|
||||||
let
|
let resolve = v: dependencyBySuffix type v;
|
||||||
resolve = v: dependencyBySuffix type v;
|
in if builtins.isList value then
|
||||||
in
|
builtins.concatLists (map resolve value) # Flatten list of lists
|
||||||
if builtins.isList value
|
|
||||||
then builtins.concatLists (map resolve value) # Flatten list of lists
|
|
||||||
else resolve value;
|
|
||||||
|
|
||||||
dependencyServices = (withResolverFor "image" containerDef.image) ++
|
|
||||||
(withResolverFor "build" containerDef.image) ++
|
|
||||||
(withResolverFor "network" containerDef.network) ++
|
|
||||||
(withResolverFor "volume" containerDef.volumes);
|
|
||||||
|
|
||||||
getActualImage =
|
|
||||||
if (builtins.hasAttr containerDef.image cfg.images) then
|
|
||||||
cfg.images."${containerDef.image}".image
|
|
||||||
else if (builtins.hasAttr containerDef.image cfg.builds) then
|
|
||||||
"localhost/homemanager/${containerDef.image}"
|
|
||||||
else
|
else
|
||||||
containerDef.image;
|
resolve value;
|
||||||
|
|
||||||
|
dependencyServices = (withResolverFor "image" containerDef.image)
|
||||||
|
++ (withResolverFor "build" containerDef.image)
|
||||||
|
++ (withResolverFor "network" containerDef.network)
|
||||||
|
++ (withResolverFor "volume" containerDef.volumes);
|
||||||
|
|
||||||
|
checkQuadletReference = types: value:
|
||||||
|
if builtins.isList value then
|
||||||
|
builtins.concatLists (map (checkQuadletReference types) value)
|
||||||
|
else
|
||||||
|
let type = findFirst (t: hasInfix ".${t}" value) null types;
|
||||||
|
in if (type != null) then
|
||||||
|
let
|
||||||
|
baseName = elemAt (splitString ".${type}" value) 0;
|
||||||
|
quadletsOfType =
|
||||||
|
filterAttrs (n: v: v.quadletData.resourceType == type)
|
||||||
|
cfg.internal.builtQuadlets;
|
||||||
|
in if (hasAttr baseName quadletsOfType) then
|
||||||
|
[ (replaceStrings [ baseName ] [ "podman-${baseName}" ] value) ]
|
||||||
|
else
|
||||||
|
[ value ]
|
||||||
|
else
|
||||||
|
[ value ];
|
||||||
|
|
||||||
quadlet = (podman-lib.deepMerge {
|
quadlet = (podman-lib.deepMerge {
|
||||||
Container = {
|
Container = {
|
||||||
|
|
@ -62,18 +61,18 @@ let
|
||||||
EnvironmentFile = containerDef.environmentFile;
|
EnvironmentFile = containerDef.environmentFile;
|
||||||
Exec = containerDef.exec;
|
Exec = containerDef.exec;
|
||||||
Group = containerDef.group;
|
Group = containerDef.group;
|
||||||
Image = resolvedImage;
|
Image = checkQuadletReference [ "build" "image" ] containerDef.image;
|
||||||
IP = containerDef.ip4;
|
IP = containerDef.ip4;
|
||||||
IP6 = containerDef.ip6;
|
IP6 = containerDef.ip6;
|
||||||
Label =
|
Label =
|
||||||
(containerDef.labels // { "nix.home-manager.managed" = true; });
|
(containerDef.labels // { "nix.home-manager.managed" = true; });
|
||||||
Network = containerDef.network;
|
Network = checkQuadletReference [ "network" ] containerDef.network;
|
||||||
NetworkAlias = containerDef.networkAlias;
|
NetworkAlias = containerDef.networkAlias;
|
||||||
PodmanArgs = containerDef.extraPodmanArgs;
|
PodmanArgs = containerDef.extraPodmanArgs;
|
||||||
PublishPort = containerDef.ports;
|
PublishPort = containerDef.ports;
|
||||||
UserNS = containerDef.userNS;
|
UserNS = containerDef.userNS;
|
||||||
User = containerDef.user;
|
User = containerDef.user;
|
||||||
Volume = containerDef.volumes;
|
Volume = checkQuadletReference [ "volume" ] containerDef.volumes;
|
||||||
};
|
};
|
||||||
Install = {
|
Install = {
|
||||||
WantedBy = optionals containerDef.autoStart [
|
WantedBy = optionals containerDef.autoStart [
|
||||||
|
|
@ -99,9 +98,9 @@ let
|
||||||
"Service for container ${name}");
|
"Service for container ${name}");
|
||||||
};
|
};
|
||||||
} containerDef.extraConfig);
|
} containerDef.extraConfig);
|
||||||
in
|
in {
|
||||||
{
|
|
||||||
dependencies = dependencyServices;
|
dependencies = dependencyServices;
|
||||||
|
attrs = quadlet;
|
||||||
text = ''
|
text = ''
|
||||||
# Automatically generated by home-manager podman container configuration
|
# Automatically generated by home-manager podman container configuration
|
||||||
# DO NOT EDIT THIS FILE DIRECTLY
|
# DO NOT EDIT THIS FILE DIRECTLY
|
||||||
|
|
@ -111,17 +110,16 @@ let
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
toQuadletInternal = name: containerDef: let
|
toQuadletInternal = name: containerDef:
|
||||||
quadletSrc = createQuadletSource name containerDef;
|
let src = createQuadletSource name containerDef;
|
||||||
in {
|
in {
|
||||||
assertions = podman-lib.buildConfigAsserts name containerDef.extraConfig;
|
assertions = podman-lib.buildConfigAsserts name containerDef.extraConfig;
|
||||||
dependencies = quadletSrc.dependencies;
|
dependencies = src.dependencies;
|
||||||
resourceType = "container";
|
resourceType = "container";
|
||||||
serviceName =
|
serviceName =
|
||||||
"podman-${name}"; # quadlet service name: 'podman-<name>.service'
|
"podman-${src.attrs.Container.ContainerName}"; # generated service name: 'podman-<name>.service'
|
||||||
source =
|
source = podman-lib.removeBlankLines src.text;
|
||||||
podman-lib.removeBlankLines quadletSrc.text;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
# Define the container user type as the user interface
|
# Define the container user type as the user interface
|
||||||
containerDefinitionType = types.submodule {
|
containerDefinitionType = types.submodule {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ let
|
||||||
(if imageDef.username != null then imageDef.username else "")
|
(if imageDef.username != null then imageDef.username else "")
|
||||||
+ (if imageDef.password != null then ":${imageDef.password}" else "");
|
+ (if imageDef.password != null then ":${imageDef.password}" else "");
|
||||||
|
|
||||||
imageConfig = podman-lib.deepMerge {
|
quadlet = podman-lib.deepMerge {
|
||||||
Image = {
|
Image = {
|
||||||
AuthFile = imageDef.authFile;
|
AuthFile = imageDef.authFile;
|
||||||
CertDir = imageDef.certDir;
|
CertDir = imageDef.certDir;
|
||||||
|
|
@ -40,13 +40,13 @@ let
|
||||||
# DO NOT EDIT THIS FILE DIRECTLY
|
# DO NOT EDIT THIS FILE DIRECTLY
|
||||||
#
|
#
|
||||||
# ${name}.image
|
# ${name}.image
|
||||||
${podman-lib.toQuadletIni imageConfig}
|
${podman-lib.toQuadletIni quadlet}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
toQuadletInternal = name: imageDef: {
|
toQuadletInternal = name: imageDef: {
|
||||||
assertions = podman-lib.buildConfigAsserts name imageDef.extraConfig;
|
assertions = podman-lib.buildConfigAsserts name imageDef.extraConfig;
|
||||||
serviceName =
|
serviceName =
|
||||||
"podman-${name}"; # quadlet service name: 'podman-<name>-image.service
|
"podman-${name}"; # generated service name: 'podman-<name>-image.service
|
||||||
source = podman-lib.removeBlankLines (createQuadletSource name imageDef);
|
source = podman-lib.removeBlankLines (createQuadletSource name imageDef);
|
||||||
resourceType = "image";
|
resourceType = "image";
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,13 @@ let
|
||||||
pkgs.stdenv.mkDerivation {
|
pkgs.stdenv.mkDerivation {
|
||||||
name = "home-${quadlet.resourceType}-${quadlet.serviceName}";
|
name = "home-${quadlet.resourceType}-${quadlet.serviceName}";
|
||||||
|
|
||||||
buildInputs = [ cfg.package ];
|
buildInputs = [ cfg.package ] ++ quadlet.dependencies;
|
||||||
|
|
||||||
# dontUnpack = true;
|
|
||||||
|
|
||||||
unpackPhase = ''
|
unpackPhase = ''
|
||||||
mkdir -p $out/quadlets
|
mkdir -p $out/quadlets
|
||||||
${concatStringsSep "\n" (map (v: "cp ${v.out}/quadlets/${v.quadletData.serviceName}.${v.quadletData.resourceType} $out/quadlets") quadlet.dependencies)}
|
${concatStringsSep "\n" (map (v:
|
||||||
|
"ln -s ${v.out}/quadlets/${v.quadletData.serviceName}.${v.quadletData.resourceType} $out/quadlets")
|
||||||
|
quadlet.dependencies)}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
|
|
@ -42,7 +42,6 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Create a derivation for each quadlet spec
|
|
||||||
builtQuadlets = map buildPodmanQuadlet cfg.internal.quadletDefinitions;
|
builtQuadlets = map buildPodmanQuadlet cfg.internal.quadletDefinitions;
|
||||||
|
|
||||||
accumulateUnitFiles = prefix: path: quadlet:
|
accumulateUnitFiles = prefix: path: quadlet:
|
||||||
|
|
@ -87,6 +86,9 @@ in {
|
||||||
lib.mkIf (lib.length builtQuadlets >= 1)
|
lib.mkIf (lib.length builtQuadlets >= 1)
|
||||||
(lib.hm.dag.entryAfter [ "reloadSystemd" ] activationCleanupScript);
|
(lib.hm.dag.entryAfter [ "reloadSystemd" ] activationCleanupScript);
|
||||||
|
|
||||||
services.podman.internal.builtQuadlets = listToAttrs (map (pkg: { name = removePrefix "podman-" pkg.passthru.quadletData.serviceName; value = pkg; }) builtQuadlets);
|
services.podman.internal.builtQuadlets = listToAttrs (map (pkg: {
|
||||||
|
name = removePrefix "podman-" pkg.passthru.quadletData.serviceName;
|
||||||
|
value = pkg;
|
||||||
|
}) builtQuadlets);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ let
|
||||||
|
|
||||||
createQuadletSource = name: networkDef:
|
createQuadletSource = name: networkDef:
|
||||||
let
|
let
|
||||||
cfg = (podman-lib.deepMerge {
|
quadlet = (podman-lib.deepMerge {
|
||||||
Install = {
|
Install = {
|
||||||
WantedBy = (if networkDef.autoStart then [
|
WantedBy = (if networkDef.autoStart then [
|
||||||
"default.target"
|
"default.target"
|
||||||
|
|
@ -50,13 +50,13 @@ let
|
||||||
# DO NOT EDIT THIS FILE DIRECTLY
|
# DO NOT EDIT THIS FILE DIRECTLY
|
||||||
#
|
#
|
||||||
# ${name}.network
|
# ${name}.network
|
||||||
${podman-lib.toQuadletIni cfg}
|
${podman-lib.toQuadletIni quadlet}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
toQuadletInternal = name: networkDef: {
|
toQuadletInternal = name: networkDef: {
|
||||||
assertions = podman-lib.buildConfigAsserts name networkDef.extraConfig;
|
assertions = podman-lib.buildConfigAsserts name networkDef.extraConfig;
|
||||||
serviceName =
|
serviceName =
|
||||||
"podman-${name}"; # quadlet service name: 'podman-<name>-network.service'
|
"podman-${name}"; # generated service name: 'podman-<name>-network.service'
|
||||||
source = podman-lib.removeBlankLines (createQuadletSource name networkDef);
|
source = podman-lib.removeBlankLines (createQuadletSource name networkDef);
|
||||||
resourceType = "network";
|
resourceType = "network";
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ let
|
||||||
|
|
||||||
createQuadletSource = name: volumeDef:
|
createQuadletSource = name: volumeDef:
|
||||||
let
|
let
|
||||||
volumeConfig = podman-lib.deepMerge {
|
quadlet = podman-lib.deepMerge {
|
||||||
Install = {
|
Install = {
|
||||||
WantedBy = optionals volumeDef.autoStart [
|
WantedBy = optionals volumeDef.autoStart [
|
||||||
"default.target"
|
"default.target"
|
||||||
|
|
@ -49,13 +49,13 @@ let
|
||||||
# DO NOT EDIT THIS FILE DIRECTLY
|
# DO NOT EDIT THIS FILE DIRECTLY
|
||||||
#
|
#
|
||||||
# ${name}.volume
|
# ${name}.volume
|
||||||
${podman-lib.toQuadletIni volumeConfig}
|
${podman-lib.toQuadletIni quadlet}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
toQuadletInternal = name: volumeDef: {
|
toQuadletInternal = name: volumeDef: {
|
||||||
assertions = podman-lib.buildConfigAsserts name volumeDef.extraConfig;
|
assertions = podman-lib.buildConfigAsserts name volumeDef.extraConfig;
|
||||||
serviceName =
|
serviceName =
|
||||||
"podman-${name}"; # quadlet service name: 'podman-<name>-volume.service'
|
"podman-${name}"; # generated service name: 'podman-<name>-volume.service'
|
||||||
source = podman-lib.removeBlankLines (createQuadletSource name volumeDef);
|
source = podman-lib.removeBlankLines (createQuadletSource name volumeDef);
|
||||||
resourceType = "volume";
|
resourceType = "volume";
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -27,4 +27,4 @@ Wants=podman-user-wait-network-online.service
|
||||||
After=podman-user-wait-network-online.service
|
After=podman-user-wait-network-online.service
|
||||||
Description=Service for build my-bld
|
Description=Service for build my-bld
|
||||||
RequiresMountsFor=%t/containers
|
RequiresMountsFor=%t/containers
|
||||||
SourcePath=/nix/store/00000000000000000000000000000000-home-build-podman-my-bld/quadlets/podman-my-bld.build
|
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container-bld/quadlets/podman-my-bld.build
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
[X-Container]
|
[X-Container]
|
||||||
ContainerName=my-container-bld
|
ContainerName=my-container-bld
|
||||||
Environment=
|
Environment=
|
||||||
Image=localhost/homemanager/my-bld
|
Image=podman-my-bld.build
|
||||||
Label=nix.home-manager.managed=true
|
Label=nix.home-manager.managed=true
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
|
@ -26,13 +26,13 @@ Delegate=yes
|
||||||
Type=notify
|
Type=notify
|
||||||
NotifyAccess=all
|
NotifyAccess=all
|
||||||
SyslogIdentifier=%N
|
SyslogIdentifier=%N
|
||||||
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --name my-container-bld --cidfile=%t/%N.cid --replace --rm --cgroups=split --sdnotify=conmon -d --label nix.home-manager.managed=true localhost/homemanager/my-bld
|
ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --name my-container-bld --cidfile=%t/%N.cid --replace --rm --cgroups=split --sdnotify=conmon -d --label nix.home-manager.managed=true homemanager/my-bld
|
||||||
|
|
||||||
[Unit]
|
[Unit]
|
||||||
Wants=podman-user-wait-network-online.service
|
Wants=podman-user-wait-network-online.service
|
||||||
After=podman-user-wait-network-online.service
|
After=podman-user-wait-network-online.service
|
||||||
After=podman-my-bld-build.service
|
|
||||||
Description=Service for container my-container-bld
|
Description=Service for container my-container-bld
|
||||||
Requires=podman-my-bld-build.service
|
|
||||||
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container-bld/quadlets/podman-my-container-bld.container
|
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container-bld/quadlets/podman-my-container-bld.container
|
||||||
|
Requires=podman-my-bld-build.service
|
||||||
|
After=podman-my-bld-build.service
|
||||||
RequiresMountsFor=%t/containers
|
RequiresMountsFor=%t/containers
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@
|
||||||
[X-Container]
|
[X-Container]
|
||||||
ContainerName=my-container
|
ContainerName=my-container
|
||||||
Environment=
|
Environment=
|
||||||
Image=docker.io/alpine:latest
|
Image=podman-my-img.image
|
||||||
Label=nix.home-manager.managed=true
|
Label=nix.home-manager.managed=true
|
||||||
Network=my-net
|
Network=podman-my-net.network
|
||||||
Network=externalnet
|
Network=externalnet
|
||||||
Volume=my-vol:/data
|
Volume=podman-my-vol.volume:/data
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=default.target
|
WantedBy=default.target
|
||||||
|
|
@ -34,12 +34,12 @@ ExecStart=/nix/store/00000000000000000000000000000000-podman/bin/podman run --na
|
||||||
[Unit]
|
[Unit]
|
||||||
Wants=podman-user-wait-network-online.service
|
Wants=podman-user-wait-network-online.service
|
||||||
After=podman-user-wait-network-online.service
|
After=podman-user-wait-network-online.service
|
||||||
After=podman-my-img-image.service
|
|
||||||
After=podman-my-net-network.service
|
|
||||||
After=podman-my-vol-volume.service
|
|
||||||
Description=Service for container my-container
|
Description=Service for container my-container
|
||||||
Requires=podman-my-img-image.service
|
|
||||||
Requires=podman-my-net-network.service
|
|
||||||
Requires=podman-my-vol-volume.service
|
|
||||||
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container/quadlets/podman-my-container.container
|
SourcePath=/nix/store/00000000000000000000000000000000-home-container-podman-my-container/quadlets/podman-my-container.container
|
||||||
|
Requires=podman-my-img-image.service
|
||||||
|
After=podman-my-img-image.service
|
||||||
RequiresMountsFor=%t/containers
|
RequiresMountsFor=%t/containers
|
||||||
|
Requires=podman-my-net-network.service
|
||||||
|
After=podman-my-net-network.service
|
||||||
|
Requires=podman-my-vol-volume.service
|
||||||
|
After=podman-my-vol-volume.service
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
network = [ "my-net.network" "externalnet" ];
|
network = [ "my-net.network" "externalnet" ];
|
||||||
volumes = [ "my-vol.volume:/data" ];
|
volumes = [ "my-vol.volume:/data" ];
|
||||||
};
|
};
|
||||||
"my-container-bld" = { image = "my-bld"; };
|
"my-container-bld" = { image = "my-bld.build"; };
|
||||||
};
|
};
|
||||||
images."my-img" = { image = "docker.io/alpine:latest"; };
|
images."my-img" = { image = "docker.io/alpine:latest"; };
|
||||||
networks."my-net" = {
|
networks."my-net" = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue