mirror of
https://github.com/containers/podman.git
synced 2025-06-28 22:53:21 +08:00
Quadlet: Add support for .kube files
Get the path to the yaml file and call podman kube play Add tests Signed-off-by: Ygal Blum <ygal.blum@gmail.com>
This commit is contained in:
@ -34,6 +34,15 @@ var (
|
|||||||
kmsgFile *os.File
|
kmsgFile *os.File
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
void struct{}
|
||||||
|
supportedExtensions = map[string]struct{}{
|
||||||
|
".container": void,
|
||||||
|
".volume": void,
|
||||||
|
".kube": void,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// We log directly to /dev/kmsg, because that is the only way to get information out
|
// We log directly to /dev/kmsg, because that is the only way to get information out
|
||||||
// of the generator into the system logs.
|
// of the generator into the system logs.
|
||||||
func logToKmsg(s string) bool {
|
func logToKmsg(s string) bool {
|
||||||
@ -105,6 +114,12 @@ func getUnitDirs(user bool) []string {
|
|||||||
return dirs
|
return dirs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isExtSupported(filename string) bool {
|
||||||
|
ext := filepath.Ext(filename)
|
||||||
|
_, ok := supportedExtensions[ext]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func loadUnitsFromDir(sourcePath string, units map[string]*parser.UnitFile) {
|
func loadUnitsFromDir(sourcePath string, units map[string]*parser.UnitFile) {
|
||||||
files, err := os.ReadDir(sourcePath)
|
files, err := os.ReadDir(sourcePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -116,9 +131,7 @@ func loadUnitsFromDir(sourcePath string, units map[string]*parser.UnitFile) {
|
|||||||
|
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
name := file.Name()
|
name := file.Name()
|
||||||
if units[name] == nil &&
|
if units[name] == nil && isExtSupported(name) {
|
||||||
(strings.HasSuffix(name, ".container") ||
|
|
||||||
strings.HasSuffix(name, ".volume")) {
|
|
||||||
path := path.Join(sourcePath, name)
|
path := path.Join(sourcePath, name)
|
||||||
|
|
||||||
Debugf("Loading source unit file %s", path)
|
Debugf("Loading source unit file %s", path)
|
||||||
@ -322,6 +335,8 @@ func main() {
|
|||||||
service, err = quadlet.ConvertContainer(unit, isUser)
|
service, err = quadlet.ConvertContainer(unit, isUser)
|
||||||
case strings.HasSuffix(name, ".volume"):
|
case strings.HasSuffix(name, ".volume"):
|
||||||
service, err = quadlet.ConvertVolume(unit, name)
|
service, err = quadlet.ConvertVolume(unit, name)
|
||||||
|
case strings.HasSuffix(name, ".kube"):
|
||||||
|
service, err = quadlet.ConvertKube(unit)
|
||||||
default:
|
default:
|
||||||
Logf("Unsupported file type '%s'", name)
|
Logf("Unsupported file type '%s'", name)
|
||||||
continue
|
continue
|
||||||
|
@ -22,6 +22,8 @@ const (
|
|||||||
XContainerGroup = "X-Container"
|
XContainerGroup = "X-Container"
|
||||||
VolumeGroup = "Volume"
|
VolumeGroup = "Volume"
|
||||||
XVolumeGroup = "X-Volume"
|
XVolumeGroup = "X-Volume"
|
||||||
|
KubeGroup = "Kube"
|
||||||
|
XKubeGroup = "X-Kube"
|
||||||
)
|
)
|
||||||
|
|
||||||
var validPortRange = regexp.MustCompile(`\d+(-\d+)?(/udp|/tcp)?$`)
|
var validPortRange = regexp.MustCompile(`\d+(-\d+)?(/udp|/tcp)?$`)
|
||||||
@ -55,6 +57,7 @@ const (
|
|||||||
KeySeccompProfile = "SeccompProfile"
|
KeySeccompProfile = "SeccompProfile"
|
||||||
KeyAddDevice = "AddDevice"
|
KeyAddDevice = "AddDevice"
|
||||||
KeyNetwork = "Network"
|
KeyNetwork = "Network"
|
||||||
|
KeyYaml = "Yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Supported keys in "Container" group
|
// Supported keys in "Container" group
|
||||||
@ -95,6 +98,11 @@ var supportedVolumeKeys = map[string]bool{
|
|||||||
KeyLabel: true,
|
KeyLabel: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Supported keys in "Kube" group
|
||||||
|
var supportedKubeKeys = map[string]bool{
|
||||||
|
KeyYaml: true,
|
||||||
|
}
|
||||||
|
|
||||||
func replaceExtension(name string, extension string, extraPrefix string, extraSuffix string) string {
|
func replaceExtension(name string, extension string, extraPrefix string, extraSuffix string) string {
|
||||||
baseName := name
|
baseName := name
|
||||||
|
|
||||||
@ -593,3 +601,65 @@ func ConvertVolume(volume *parser.UnitFile, name string) (*parser.UnitFile, erro
|
|||||||
|
|
||||||
return service, nil
|
return service, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ConvertKube(kube *parser.UnitFile) (*parser.UnitFile, error) {
|
||||||
|
service := kube.Dup()
|
||||||
|
service.Filename = replaceExtension(kube.Filename, ".service", "", "")
|
||||||
|
|
||||||
|
if kube.Path != "" {
|
||||||
|
service.Add(UnitGroup, "SourcePath", kube.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := checkForUnknownKeys(kube, KubeGroup, supportedKubeKeys); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename old Kube group to x-Kube so that systemd ignores it
|
||||||
|
service.RenameGroup(KubeGroup, XKubeGroup)
|
||||||
|
|
||||||
|
yamlPath, ok := kube.Lookup(KubeGroup, KeyYaml)
|
||||||
|
if !ok || len(yamlPath) == 0 {
|
||||||
|
return nil, fmt.Errorf("no Yaml key specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow mixed or control-group, as nothing else works well
|
||||||
|
killMode, ok := service.Lookup(ServiceGroup, "KillMode")
|
||||||
|
if !ok || !(killMode == "mixed" || killMode == "control-group") {
|
||||||
|
if ok {
|
||||||
|
return nil, fmt.Errorf("invalid KillMode '%s'", killMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We default to mixed instead of control-group, because it lets conmon do its thing
|
||||||
|
service.Set(ServiceGroup, "KillMode", "mixed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set PODMAN_SYSTEMD_UNIT so that podman auto-update can restart the service.
|
||||||
|
service.Add(ServiceGroup, "Environment", "PODMAN_SYSTEMD_UNIT=%n")
|
||||||
|
|
||||||
|
// Need the containers filesystem mounted to start podman
|
||||||
|
service.Add(UnitGroup, "RequiresMountsFor", "%t/containers")
|
||||||
|
|
||||||
|
service.Setv(ServiceGroup,
|
||||||
|
"Type", "notify",
|
||||||
|
"NotifyAccess", "all")
|
||||||
|
|
||||||
|
execStart := NewPodmanCmdline("kube", "play")
|
||||||
|
|
||||||
|
execStart.add(
|
||||||
|
// Replace any previous container with the same name, not fail
|
||||||
|
"--replace",
|
||||||
|
|
||||||
|
// Use a service container
|
||||||
|
"--service-container=true",
|
||||||
|
)
|
||||||
|
|
||||||
|
execStart.add(yamlPath)
|
||||||
|
|
||||||
|
service.AddCmdline(ServiceGroup, "ExecStart", execStart.Args)
|
||||||
|
|
||||||
|
execStop := NewPodmanCmdline("kube", "down")
|
||||||
|
execStop.add(yamlPath)
|
||||||
|
service.AddCmdline(ServiceGroup, "ExecStop", execStop.Args)
|
||||||
|
|
||||||
|
return service, nil
|
||||||
|
}
|
||||||
|
17
test/e2e/quadlet/basic.kube
Normal file
17
test/e2e/quadlet/basic.kube
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
## assert-podman-args "kube"
|
||||||
|
## assert-podman-args "play"
|
||||||
|
## assert-podman-final-args deployment.yml
|
||||||
|
## assert-podman-args "--replace"
|
||||||
|
## assert-podman-args "--service-container=true"
|
||||||
|
## assert-podman-stop-args "kube"
|
||||||
|
## assert-podman-stop-args "down"
|
||||||
|
## assert-podman-stop-final-args deployment.yml
|
||||||
|
## assert-key-is "Unit" "RequiresMountsFor" "%t/containers"
|
||||||
|
## assert-key-is "Service" "KillMode" "mixed"
|
||||||
|
## assert-key-is "Service" "Type" "notify"
|
||||||
|
## assert-key-is "Service" "NotifyAccess" "all"
|
||||||
|
## assert-key-is "Service" "Environment" "PODMAN_SYSTEMD_UNIT=%n"
|
||||||
|
|
||||||
|
|
||||||
|
[Kube]
|
||||||
|
Yaml=deployment.yml
|
@ -110,19 +110,35 @@ func (t *quadletTestcase) assertKeyContains(args []string, unit *parser.UnitFile
|
|||||||
return ok && strings.Contains(realValue, value)
|
return ok && strings.Contains(realValue, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *quadletTestcase) assertPodmanArgs(args []string, unit *parser.UnitFile) bool {
|
func (t *quadletTestcase) assertPodmanArgs(args []string, unit *parser.UnitFile, key string) bool {
|
||||||
podmanArgs, _ := unit.LookupLastArgs("Service", "ExecStart")
|
podmanArgs, _ := unit.LookupLastArgs("Service", key)
|
||||||
return findSublist(podmanArgs, args) != -1
|
return findSublist(podmanArgs, args) != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *quadletTestcase) assertFinalArgs(args []string, unit *parser.UnitFile) bool {
|
func (t *quadletTestcase) assertPodmanFinalArgs(args []string, unit *parser.UnitFile, key string) bool {
|
||||||
podmanArgs, _ := unit.LookupLastArgs("Service", "ExecStart")
|
podmanArgs, _ := unit.LookupLastArgs("Service", key)
|
||||||
if len(podmanArgs) < len(args) {
|
if len(podmanArgs) < len(args) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return matchSublistAt(podmanArgs, len(podmanArgs)-len(args), args)
|
return matchSublistAt(podmanArgs, len(podmanArgs)-len(args), args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *quadletTestcase) assertStartPodmanArgs(args []string, unit *parser.UnitFile) bool {
|
||||||
|
return t.assertPodmanArgs(args, unit, "ExecStart")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *quadletTestcase) assertStartPodmanFinalArgs(args []string, unit *parser.UnitFile) bool {
|
||||||
|
return t.assertPodmanFinalArgs(args, unit, "ExecStart")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *quadletTestcase) assertStopPodmanArgs(args []string, unit *parser.UnitFile) bool {
|
||||||
|
return t.assertPodmanArgs(args, unit, "ExecStop")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *quadletTestcase) assertStopPodmanFinalArgs(args []string, unit *parser.UnitFile) bool {
|
||||||
|
return t.assertPodmanFinalArgs(args, unit, "ExecStop")
|
||||||
|
}
|
||||||
|
|
||||||
func (t *quadletTestcase) assertSymlink(args []string, unit *parser.UnitFile) bool {
|
func (t *quadletTestcase) assertSymlink(args []string, unit *parser.UnitFile) bool {
|
||||||
symlink := args[0]
|
symlink := args[0]
|
||||||
expectedTarget := args[1]
|
expectedTarget := args[1]
|
||||||
@ -161,11 +177,15 @@ func (t *quadletTestcase) doAssert(check []string, unit *parser.UnitFile, sessio
|
|||||||
case "assert-key-contains":
|
case "assert-key-contains":
|
||||||
ok = t.assertKeyContains(args, unit)
|
ok = t.assertKeyContains(args, unit)
|
||||||
case "assert-podman-args":
|
case "assert-podman-args":
|
||||||
ok = t.assertPodmanArgs(args, unit)
|
ok = t.assertStartPodmanArgs(args, unit)
|
||||||
case "assert-podman-final-args":
|
case "assert-podman-final-args":
|
||||||
ok = t.assertFinalArgs(args, unit)
|
ok = t.assertStartPodmanFinalArgs(args, unit)
|
||||||
case "assert-symlink":
|
case "assert-symlink":
|
||||||
ok = t.assertSymlink(args, unit)
|
ok = t.assertSymlink(args, unit)
|
||||||
|
case "assert-podman-stop-args":
|
||||||
|
ok = t.assertStopPodmanArgs(args, unit)
|
||||||
|
case "assert-podman-stop-final-args":
|
||||||
|
ok = t.assertStopPodmanFinalArgs(args, unit)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unsupported assertion %s", op)
|
return fmt.Errorf("Unsupported assertion %s", op)
|
||||||
}
|
}
|
||||||
@ -300,6 +320,8 @@ var _ = Describe("quadlet system generator", func() {
|
|||||||
Entry("basic.volume", "basic.volume"),
|
Entry("basic.volume", "basic.volume"),
|
||||||
Entry("label.volume", "label.volume"),
|
Entry("label.volume", "label.volume"),
|
||||||
Entry("uid.volume", "uid.volume"),
|
Entry("uid.volume", "uid.volume"),
|
||||||
|
|
||||||
|
Entry("Basic kube", "basic.kube"),
|
||||||
)
|
)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user