mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00
Merge pull request #16237 from alexlarsson/quadlet-updates
Various quadlet updates
This commit is contained in:
@ -23,6 +23,7 @@ import (
|
||||
|
||||
var (
|
||||
verboseFlag bool // True if -v passed
|
||||
noKmsgFlag bool
|
||||
isUser bool // True if run as quadlet-user-generator executable
|
||||
)
|
||||
|
||||
@ -134,7 +135,7 @@ func generateServiceFile(service *parser.UnitFile) error {
|
||||
Debugf("writing '%s'", service.Path)
|
||||
|
||||
service.PrependComment("",
|
||||
"Automatically generated by quadlet-generator",
|
||||
fmt.Sprintf("Automatically generated by %s", os.Args[0]),
|
||||
"")
|
||||
|
||||
f, err := os.Create(service.Path)
|
||||
@ -219,6 +220,10 @@ func main() {
|
||||
enableDebug()
|
||||
}
|
||||
|
||||
if noKmsgFlag {
|
||||
noKmsg = true
|
||||
}
|
||||
|
||||
if flag.NArg() < 1 {
|
||||
Logf("Missing output directory argument")
|
||||
os.Exit(1)
|
||||
@ -270,4 +275,5 @@ func main() {
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&verboseFlag, "v", false, "Print debug information")
|
||||
flag.BoolVar(&noKmsgFlag, "no-kmsg-log", false, "Don't log to kmsg")
|
||||
}
|
||||
|
@ -15,7 +15,8 @@ Valid _mode_ values are:
|
||||
- **mac=MAC**: Specify a static mac address for this container.
|
||||
- **interface_name**: Specify a name for the created network interface inside the container.
|
||||
|
||||
For example to set a static ipv4 address and a static mac address, use `--network bridge:ip=10.88.0.10,mac=44:33:22:11:00:99`.
|
||||
For example to set a static ipv4 address and a static mac address, use `--network bridge:ip=10.88.0.10,mac=44:33:22:11:00:99`.
|
||||
|
||||
- \<network name or ID\>[:OPTIONS,...]: Connect to a user-defined network; this is the network name or ID from a network created by **[podman network create](podman-network-create.1.md)**. Using the network name implies the bridge network mode. It is possible to specify the same options described under the bridge mode above. You can use the **--network** option multiple times to specify additional networks.
|
||||
- **none**: Create a network namespace for the container but do not configure network interfaces for it, thus the container has no network connectivity.
|
||||
- **container:**_id_: Reuse another container's network stack.
|
||||
|
@ -126,22 +126,39 @@ setuid and file capabilities.
|
||||
|
||||
#### `DropCapability=` (defaults to `all`)
|
||||
|
||||
Drop these capabilities from the default container capability set. The default is `all`, allowing
|
||||
addition of capabilities with `AddCapability`. Set this to empty to drop no capabilities.
|
||||
This can be listed multiple times.
|
||||
Drop these capabilities from the default podman capability set, or `all` for all capabilities. The default if no
|
||||
`DropCapability` is set is `all`. Set this to empty (i.e. `DropCapability=`) to use the default podman capability set.
|
||||
|
||||
This is a space separated list of capabilities. This key can be listed multiple times.
|
||||
|
||||
For example:
|
||||
```
|
||||
DropCapability=CAP_DAC_OVERRIDE CAP_IPC_OWNER
|
||||
```
|
||||
|
||||
#### `AddCapability=`
|
||||
|
||||
By default, the container runs with no capabilities (due to DropCapabilities='all'). If any specific
|
||||
caps are needed, then add them with this key. For example using `AddCapability=CAP_DAC_OVERRIDE`.
|
||||
This can be listed multiple times.
|
||||
|
||||
#### `ReadOnly=` (defaults to `no`)
|
||||
This is a space separated list of capabilities. This key can be listed multiple times.
|
||||
|
||||
For example:
|
||||
```
|
||||
AddCapability=CAP_DAC_OVERRIDE CAP_IPC_OWNER
|
||||
```
|
||||
|
||||
#### `ReadOnly=` (defaults to `yes`)
|
||||
|
||||
If enabled, makes image read-only, with /var/tmp, /tmp and /run a tmpfs (unless disabled by `VolatileTmp=no`).
|
||||
|
||||
**NOTE:** Podman will automatically copy any content from the image onto the tmpfs
|
||||
|
||||
#### `SeccompProfile=`
|
||||
|
||||
Set the seccomp profile to use in the container. If unset, the default podman profile is used.
|
||||
Set to either the pathname of a json file, or `unconfined` to disable the seccomp filters.
|
||||
|
||||
#### `RemapUsers=` (defaults to `no`)
|
||||
|
||||
If this is enabled, then host user and group ids are remapped in the container, such that all the uids
|
||||
@ -217,6 +234,14 @@ created by using a `$name.volume` quadlet file.
|
||||
|
||||
This key can be listed multiple times.
|
||||
|
||||
#### `Network=`
|
||||
|
||||
Specify a custom network for the container. This has the same format as the `--network` option
|
||||
to `podman run`. For example, use `host` to use the host network in the container, or `none` to
|
||||
not set up networking in the container.
|
||||
|
||||
This key can be listed multiple times.
|
||||
|
||||
#### `ExposeHostPort=`
|
||||
|
||||
Exposes a port, or a range of ports (e.g. `50-59`), from the host to the container. Equivalent
|
||||
@ -241,6 +266,16 @@ allocated port can be found with the `podman port` command.
|
||||
|
||||
This key can be listed multiple times.
|
||||
|
||||
#### `AddDevice=`
|
||||
|
||||
Adds a device node from the host into the container. The format of this is
|
||||
`HOST-DEVICE[:CONTAINER-DEVICE][:PERMISSIONS]`, where `HOST-DEVICE` is the path of
|
||||
the device node on the host, `CONTAINER-DEVICE` is the path of the device node in
|
||||
the container, and `PERMISSIONS` is a list of permissions combining 'r' for read,
|
||||
'w' for write, and 'm' for mknod(2).
|
||||
|
||||
This key can be listed multiple times.
|
||||
|
||||
#### `PodmanArgs=`
|
||||
|
||||
This key contains a list of arguments passed directly to the end of the `podman run` command
|
||||
|
@ -56,7 +56,6 @@ const (
|
||||
KeyRemapUIDRanges = "RemapUidRanges"
|
||||
KeyRemapGIDRanges = "RemapGidRanges"
|
||||
KeyNotify = "Notify"
|
||||
KeySocketActivated = "SocketActivated"
|
||||
KeyExposeHostPort = "ExposeHostPort"
|
||||
KeyPublishPort = "PublishPort"
|
||||
KeyKeepID = "KeepId"
|
||||
@ -71,6 +70,9 @@ const (
|
||||
KeyRunInit = "RunInit"
|
||||
KeyVolatileTmp = "VolatileTmp"
|
||||
KeyTimezone = "Timezone"
|
||||
KeySeccompProfile = "SeccompProfile"
|
||||
KeyAddDevice = "AddDevice"
|
||||
KeyNetwork = "Network"
|
||||
)
|
||||
|
||||
// Supported keys in "Container" group
|
||||
@ -89,7 +91,6 @@ var supportedContainerKeys = map[string]bool{
|
||||
KeyRemapUIDRanges: true,
|
||||
KeyRemapGIDRanges: true,
|
||||
KeyNotify: true,
|
||||
KeySocketActivated: true,
|
||||
KeyExposeHostPort: true,
|
||||
KeyPublishPort: true,
|
||||
KeyKeepID: true,
|
||||
@ -104,6 +105,9 @@ var supportedContainerKeys = map[string]bool{
|
||||
KeyRunInit: true,
|
||||
KeyVolatileTmp: true,
|
||||
KeyTimezone: true,
|
||||
KeySeccompProfile: true,
|
||||
KeyAddDevice: true,
|
||||
KeyNetwork: true,
|
||||
}
|
||||
|
||||
// Supported keys in "Volume" group
|
||||
@ -353,8 +357,7 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile
|
||||
"-d",
|
||||
|
||||
// But we still want output to the journal, so use the log driver.
|
||||
// TODO: Once available we want to use the passthrough log-driver instead.
|
||||
"--log-driver", "journald",
|
||||
"--log-driver", "passthrough",
|
||||
|
||||
// Never try to pull the image during service start
|
||||
"--pull=never")
|
||||
@ -370,6 +373,13 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile
|
||||
podman.addf("--tz=%s", timezone)
|
||||
}
|
||||
|
||||
networks := container.LookupAll(ContainerGroup, KeyNetwork)
|
||||
for _, network := range networks {
|
||||
if len(network) > 0 {
|
||||
podman.addf("--network=%s", network)
|
||||
}
|
||||
}
|
||||
|
||||
// Run with a pid1 init to reap zombies by default (as most apps don't do that)
|
||||
runInit := container.LookupBoolean(ContainerGroup, KeyRunInit, true)
|
||||
if runInit {
|
||||
@ -397,9 +407,21 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile
|
||||
podman.add("--security-opt=no-new-privileges")
|
||||
}
|
||||
|
||||
// But allow overrides with AddCapability
|
||||
devices := container.LookupAllStrv(ContainerGroup, KeyAddDevice)
|
||||
for _, device := range devices {
|
||||
podman.addf("--device=%s", device)
|
||||
}
|
||||
|
||||
// Default to no higher level privileges or caps
|
||||
seccompProfile, hasSeccompProfile := container.Lookup(ContainerGroup, KeySeccompProfile)
|
||||
if hasSeccompProfile {
|
||||
podman.add("--security-opt", fmt.Sprintf("seccomp=%s", seccompProfile))
|
||||
}
|
||||
|
||||
dropCaps := []string{"all"} // Default
|
||||
if container.HasKey(ContainerGroup, KeyDropCapability) {
|
||||
dropCaps = container.LookupAll(ContainerGroup, KeyDropCapability)
|
||||
dropCaps = container.LookupAllStrv(ContainerGroup, KeyDropCapability)
|
||||
}
|
||||
|
||||
for _, caps := range dropCaps {
|
||||
@ -407,12 +429,12 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile
|
||||
}
|
||||
|
||||
// But allow overrides with AddCapability
|
||||
addCaps := container.LookupAll(ContainerGroup, KeyAddCapability)
|
||||
addCaps := container.LookupAllStrv(ContainerGroup, KeyAddCapability)
|
||||
for _, caps := range addCaps {
|
||||
podman.addf("--cap-add=%s", strings.ToLower(caps))
|
||||
}
|
||||
|
||||
readOnly := container.LookupBoolean(ContainerGroup, KeyReadOnly, false)
|
||||
readOnly := container.LookupBoolean(ContainerGroup, KeyReadOnly, true)
|
||||
if readOnly {
|
||||
podman.add("--read-only")
|
||||
}
|
||||
@ -429,18 +451,6 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile
|
||||
podman.add("--read-only-tmpfs=false")
|
||||
}
|
||||
|
||||
socketActivated := container.LookupBoolean(ContainerGroup, KeySocketActivated, false)
|
||||
if socketActivated {
|
||||
// TODO: This will not be needed with later podman versions that support activation directly:
|
||||
// https://github.com/containers/podman/pull/11316
|
||||
podman.add("--preserve-fds=1")
|
||||
podmanEnv["LISTEN_FDS"] = "1"
|
||||
|
||||
// TODO: This will not be 2 when catatonit forwards fds:
|
||||
// https://github.com/openSUSE/catatonit/pull/15
|
||||
podmanEnv["LISTEN_PID"] = "2"
|
||||
}
|
||||
|
||||
defaultContainerUID := uint32(0)
|
||||
defaultContainerGID := uint32(0)
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
## assert-podman-final-args run --name=systemd-%N --cidfile=%t/%N.cid --replace --rm -d --log-driver journald --pull=never --runtime /usr/bin/crun --cgroups=split --sdnotify=conmon imagename
|
||||
## assert-podman-final-args run --name=systemd-%N --cidfile=%t/%N.cid --replace --rm -d --log-driver passthrough --pull=never --runtime /usr/bin/crun --cgroups=split --sdnotify=conmon imagename
|
||||
|
||||
[Container]
|
||||
Image=imagename
|
||||
|
||||
# Disable all default features to get as empty podman run command as we can
|
||||
RemapUsers=no
|
||||
ReadOnly=no
|
||||
NoNewPrivileges=no
|
||||
DropCapability=
|
||||
RunInit=no
|
||||
|
@ -4,7 +4,7 @@
|
||||
## assert-podman-args "--rm"
|
||||
## assert-podman-args "--replace"
|
||||
## assert-podman-args "-d"
|
||||
## assert-podman-args "--log-driver" "journald"
|
||||
## assert-podman-args "--log-driver" "passthrough"
|
||||
## assert-podman-args "--pull=never"
|
||||
## assert-podman-args "--init"
|
||||
## assert-podman-args "--runtime" "/usr/bin/crun"
|
||||
@ -12,7 +12,8 @@
|
||||
## assert-podman-args "--sdnotify=conmon"
|
||||
## assert-podman-args "--security-opt=no-new-privileges"
|
||||
## assert-podman-args "--cap-drop=all"
|
||||
## assert-podman-args "--tmpfs" "/tmp:rw,size=512M,mode=1777"
|
||||
## assert-podman-args "--read-only"
|
||||
## !assert-podman-args "--read-only-tmpfs=false"
|
||||
## assert-key-is "Unit" "RequiresMountsFor" "%t/containers"
|
||||
## assert-key-is "Service" "KillMode" "mixed"
|
||||
## assert-key-is "Service" "Delegate" "yes"
|
||||
|
@ -1,8 +1,11 @@
|
||||
## assert-podman-args "--cap-drop=all"
|
||||
## !assert-podman-args "--cap-drop=all"
|
||||
## assert-podman-args "--cap-add=cap_dac_override"
|
||||
## assert-podman-args "--cap-add=cap_audit_write"
|
||||
## assert-podman-args "--cap-add=cap_ipc_owner"
|
||||
|
||||
[Container]
|
||||
Image=imagename
|
||||
AddCapability=CAP_DAC_OVERRIDE
|
||||
# Verify that we can reset to the default cap set
|
||||
DropCapability=
|
||||
AddCapability=CAP_DAC_OVERRIDE CAP_AUDIT_WRITE
|
||||
AddCapability=CAP_IPC_OWNER
|
||||
|
9
test/e2e/quadlet/capabilities2.container
Normal file
9
test/e2e/quadlet/capabilities2.container
Normal file
@ -0,0 +1,9 @@
|
||||
## !assert-podman-args "--cap-drop=all"
|
||||
## assert-podman-args "--cap-drop=cap_dac_override"
|
||||
## assert-podman-args "--cap-drop=cap_audit_write"
|
||||
## assert-podman-args "--cap-drop=cap_ipc_owner"
|
||||
|
||||
[Container]
|
||||
Image=localhost/imagename
|
||||
DropCapability=CAP_DAC_OVERRIDE CAP_AUDIT_WRITE
|
||||
DropCapability=CAP_IPC_OWNER
|
7
test/e2e/quadlet/devices.container
Normal file
7
test/e2e/quadlet/devices.container
Normal file
@ -0,0 +1,7 @@
|
||||
## assert-podman-args --device=/dev/fuse
|
||||
## assert-podman-args --device=/dev/loop0:r
|
||||
|
||||
[Container]
|
||||
Image=localhost/imagename
|
||||
AddDevice=/dev/fuse
|
||||
AddDevice=/dev/loop0:r
|
5
test/e2e/quadlet/network.container
Normal file
5
test/e2e/quadlet/network.container
Normal file
@ -0,0 +1,5 @@
|
||||
## assert-podman-args "--network=host"
|
||||
|
||||
[Container]
|
||||
Image=localhost/imagename
|
||||
Network=host
|
@ -1,4 +1,4 @@
|
||||
## assert-failed
|
||||
## assert-stderr-contains "No Image key specified"
|
||||
## assert-stderr-contains "no Image key specified"
|
||||
|
||||
[Container]
|
||||
|
6
test/e2e/quadlet/readonly-notmpfs.container
Normal file
6
test/e2e/quadlet/readonly-notmpfs.container
Normal file
@ -0,0 +1,6 @@
|
||||
## assert-podman-args "--read-only-tmpfs=false"
|
||||
## assert-podman-args "--read-only"
|
||||
|
||||
[Container]
|
||||
Image=localhost/imagename
|
||||
VolatileTmp=no
|
7
test/e2e/quadlet/readwrite-notmpfs.container
Normal file
7
test/e2e/quadlet/readwrite-notmpfs.container
Normal file
@ -0,0 +1,7 @@
|
||||
## !assert-podman-args "--read-only"
|
||||
## !assert-podman-args "--tmpfs" "/tmp:rw,size=512M,mode=1777"
|
||||
|
||||
[Container]
|
||||
Image=localhost/imagename
|
||||
VolatileTmp=no
|
||||
ReadOnly=no
|
6
test/e2e/quadlet/readwrite.container
Normal file
6
test/e2e/quadlet/readwrite.container
Normal file
@ -0,0 +1,6 @@
|
||||
## !assert-podman-args "--read-only"
|
||||
## assert-podman-args "--tmpfs" "/tmp:rw,size=512M,mode=1777"
|
||||
|
||||
[Container]
|
||||
Image=localhost/imagename
|
||||
ReadOnly=no
|
5
test/e2e/quadlet/seccomp.container
Normal file
5
test/e2e/quadlet/seccomp.container
Normal file
@ -0,0 +1,5 @@
|
||||
## assert-podman-args --security-opt seccomp=unconfined
|
||||
|
||||
[Container]
|
||||
Image=localhost/imagename
|
||||
SeccompProfile=unconfined
|
@ -80,7 +80,7 @@ func findSublist(full []string, sublist []string) int {
|
||||
}
|
||||
|
||||
func (t *quadletTestcase) assertStdErrContains(args []string, session *PodmanSessionIntegration) bool {
|
||||
return strings.Contains(session.OutputToString(), args[0])
|
||||
return strings.Contains(session.ErrorToString(), args[0])
|
||||
}
|
||||
|
||||
func (t *quadletTestcase) assertKeyIs(args []string, unit *parser.UnitFile) bool {
|
||||
@ -174,7 +174,10 @@ func (t *quadletTestcase) doAssert(check []string, unit *parser.UnitFile, sessio
|
||||
}
|
||||
|
||||
if !ok {
|
||||
s, _ := unit.ToString()
|
||||
s := "(nil)"
|
||||
if unit != nil {
|
||||
s, _ = unit.ToString()
|
||||
}
|
||||
return fmt.Errorf("Failed assertion for %s: %s\n\n%s", t.serviceName, strings.Join(check, " "), s)
|
||||
}
|
||||
return nil
|
||||
@ -189,12 +192,18 @@ func (t *quadletTestcase) check(generateDir string, session *PodmanSessionIntegr
|
||||
}
|
||||
|
||||
file := filepath.Join(generateDir, t.serviceName)
|
||||
if _, err := os.Stat(file); os.IsNotExist(err) && expectFail {
|
||||
return // Successful fail
|
||||
_, err := os.Stat(file)
|
||||
if expectFail {
|
||||
Expect(err).To(MatchError(os.ErrNotExist))
|
||||
} else {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
unit, err := parser.ParseUnitFile(file)
|
||||
Expect(err).To(BeNil())
|
||||
var unit *parser.UnitFile
|
||||
if !expectFail {
|
||||
unit, err = parser.ParseUnitFile(file)
|
||||
Expect(err).To(BeNil())
|
||||
}
|
||||
|
||||
for _, check := range t.checks {
|
||||
err := t.doAssert(check, unit, session)
|
||||
@ -244,7 +253,7 @@ var _ = Describe("quadlet system generator", func() {
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
// Run quadlet to convert the file
|
||||
session := podmanTest.Quadlet([]string{generatedDir}, quadletDir)
|
||||
session := podmanTest.Quadlet([]string{"-no-kmsg-log", generatedDir}, quadletDir)
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(0))
|
||||
|
||||
@ -260,6 +269,8 @@ var _ = Describe("quadlet system generator", func() {
|
||||
Entry("annotation.container", "annotation.container"),
|
||||
Entry("basepodman.container", "basepodman.container"),
|
||||
Entry("capabilities.container", "capabilities.container"),
|
||||
Entry("capabilities2.container", "capabilities2.container"),
|
||||
Entry("devices.container", "devices.container"),
|
||||
Entry("env.container", "env.container"),
|
||||
Entry("escapes.container", "escapes.container"),
|
||||
Entry("exec.container", "exec.container"),
|
||||
@ -267,6 +278,7 @@ var _ = Describe("quadlet system generator", func() {
|
||||
Entry("install.container", "install.container"),
|
||||
Entry("label.container", "label.container"),
|
||||
Entry("name.container", "name.container"),
|
||||
Entry("network.container", "network.container"),
|
||||
Entry("noimage.container", "noimage.container"),
|
||||
Entry("noremapuser2.container", "noremapuser2.container"),
|
||||
Entry("noremapuser.container", "noremapuser.container"),
|
||||
@ -275,7 +287,10 @@ var _ = Describe("quadlet system generator", func() {
|
||||
Entry("podmanargs.container", "podmanargs.container"),
|
||||
Entry("ports.container", "ports.container"),
|
||||
Entry("ports_ipv6.container", "ports_ipv6.container"),
|
||||
Entry("socketactivated.container", "socketactivated.container"),
|
||||
Entry("readonly-notmpfs.container", "readonly-notmpfs.container"),
|
||||
Entry("readwrite.container", "readwrite.container"),
|
||||
Entry("readwrite-notmpfs.container", "readwrite-notmpfs.container"),
|
||||
Entry("seccomp.container", "seccomp.container"),
|
||||
Entry("timezone.container", "timezone.container"),
|
||||
Entry("user.container", "user.container"),
|
||||
Entry("user-host.container", "user-host.container"),
|
||||
|
Reference in New Issue
Block a user