diff options
author | Valentin Rothberg <rothberg@redhat.com> | 2020-04-27 14:23:23 +0200 |
---|---|---|
committer | Valentin Rothberg <rothberg@redhat.com> | 2020-04-29 07:22:18 +0200 |
commit | b2414b580e236e011d60028e645ab41096035ee3 (patch) | |
tree | 7e639a4c40d728c3be4e65e2870f785ced13665e /pkg | |
parent | bf4efc1953467907ae7d75d5f3ef3cd41505ee24 (diff) | |
download | podman-b2414b580e236e011d60028e645ab41096035ee3.tar.gz podman-b2414b580e236e011d60028e645ab41096035ee3.tar.bz2 podman-b2414b580e236e011d60028e645ab41096035ee3.zip |
generate systemd
Implement `podman generate systemd` for Podman v2 and enable associated
tests.
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/domain/entities/engine_container.go | 1 | ||||
-rw-r--r-- | pkg/domain/entities/generate.go | 22 | ||||
-rw-r--r-- | pkg/domain/infra/abi/generate.go | 174 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/generate.go | 12 | ||||
-rw-r--r-- | pkg/specgen/generate/container_create.go | 1 |
5 files changed, 210 insertions, 0 deletions
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 502279bcf..eebf4c033 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -41,6 +41,7 @@ type ContainerEngine interface { ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error) Events(ctx context.Context, opts EventsOptions) error + GenerateSystemd(ctx context.Context, nameOrID string, opts GenerateSystemdOptions) (*GenerateSystemdReport, error) HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error) Info(ctx context.Context) (*define.Info, error) PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error) diff --git a/pkg/domain/entities/generate.go b/pkg/domain/entities/generate.go new file mode 100644 index 000000000..6d65b52f8 --- /dev/null +++ b/pkg/domain/entities/generate.go @@ -0,0 +1,22 @@ +package entities + +// GenerateSystemdOptions control the generation of systemd unit files. +type GenerateSystemdOptions struct { + // Files - generate files instead of printing to stdout. + Files bool + // Name - use container/pod name instead of its ID. + Name bool + // New - create a new container instead of starting a new one. + New bool + // RestartPolicy - systemd restart policy. + RestartPolicy string + // StopTimeout - time when stopping the container. + StopTimeout *uint +} + +// GenerateSystemdReport +type GenerateSystemdReport struct { + // Output of the generate process. Either the generated files or their + // entire content. + Output string +} diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go new file mode 100644 index 000000000..f69ba560e --- /dev/null +++ b/pkg/domain/infra/abi/generate.go @@ -0,0 +1,174 @@ +package abi + +import ( + "context" + "fmt" + "strings" + + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/systemd/generate" + "github.com/pkg/errors" +) + +func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) { + opts := generate.Options{ + Files: options.Files, + New: options.New, + } + + // First assume it's a container. + if info, found, err := ic.generateSystemdgenContainerInfo(nameOrID, nil, options); found && err != nil { + return nil, err + } else if found && err == nil { + output, err := generate.CreateContainerSystemdUnit(info, opts) + if err != nil { + return nil, err + } + return &entities.GenerateSystemdReport{Output: output}, nil + } + + // --new does not support pods. + if options.New { + return nil, errors.Errorf("error generating systemd unit files: cannot generate generic files for a pod") + } + + // We're either having a pod or garbage. + pod, err := ic.Libpod.LookupPod(nameOrID) + if err != nil { + return nil, err + } + + // Error out if the pod has no infra container, which we require to be the + // main service. + if !pod.HasInfraContainer() { + return nil, fmt.Errorf("error generating systemd unit files: Pod %q has no infra container", pod.Name()) + } + + // Generate a systemdgen.ContainerInfo for the infra container. This + // ContainerInfo acts as the main service of the pod. + infraID, err := pod.InfraContainerID() + if err != nil { + return nil, nil + } + podInfo, _, err := ic.generateSystemdgenContainerInfo(infraID, pod, options) + if err != nil { + return nil, err + } + + // Compute the container-dependency graph for the Pod. + containers, err := pod.AllContainers() + if err != nil { + return nil, err + } + if len(containers) == 0 { + return nil, fmt.Errorf("error generating systemd unit files: Pod %q has no containers", pod.Name()) + } + graph, err := libpod.BuildContainerGraph(containers) + if err != nil { + return nil, err + } + + // Traverse the dependency graph and create systemdgen.ContainerInfo's for + // each container. + containerInfos := []*generate.ContainerInfo{podInfo} + for ctr, dependencies := range graph.DependencyMap() { + // Skip the infra container as we already generated it. + if ctr.ID() == infraID { + continue + } + ctrInfo, _, err := ic.generateSystemdgenContainerInfo(ctr.ID(), nil, options) + if err != nil { + return nil, err + } + // Now add the container's dependencies and at the container as a + // required service of the infra container. + for _, dep := range dependencies { + if dep.ID() == infraID { + ctrInfo.BoundToServices = append(ctrInfo.BoundToServices, podInfo.ServiceName) + } else { + _, serviceName := generateServiceName(dep, nil, options) + ctrInfo.BoundToServices = append(ctrInfo.BoundToServices, serviceName) + } + } + podInfo.RequiredServices = append(podInfo.RequiredServices, ctrInfo.ServiceName) + containerInfos = append(containerInfos, ctrInfo) + } + + // Now generate the systemd service for all containers. + builder := strings.Builder{} + for i, info := range containerInfos { + if i > 0 { + builder.WriteByte('\n') + } + out, err := generate.CreateContainerSystemdUnit(info, opts) + if err != nil { + return nil, err + } + builder.WriteString(out) + } + + return &entities.GenerateSystemdReport{Output: builder.String()}, nil +} + +// generateSystemdgenContainerInfo is a helper to generate a +// systemdgen.ContainerInfo for `GenerateSystemd`. +func (ic *ContainerEngine) generateSystemdgenContainerInfo(nameOrID string, pod *libpod.Pod, options entities.GenerateSystemdOptions) (*generate.ContainerInfo, bool, error) { + ctr, err := ic.Libpod.LookupContainer(nameOrID) + if err != nil { + return nil, false, err + } + + timeout := ctr.StopTimeout() + if options.StopTimeout != nil { + timeout = *options.StopTimeout + } + + config := ctr.Config() + conmonPidFile := config.ConmonPidFile + if conmonPidFile == "" { + return nil, true, errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag") + } + + createCommand := []string{} + if config.CreateCommand != nil { + createCommand = config.CreateCommand + } else if options.New { + return nil, true, errors.Errorf("cannot use --new on container %q: no create command found", nameOrID) + } + + name, serviceName := generateServiceName(ctr, pod, options) + info := &generate.ContainerInfo{ + ServiceName: serviceName, + ContainerName: name, + RestartPolicy: options.RestartPolicy, + PIDFile: conmonPidFile, + StopTimeout: timeout, + GenerateTimestamp: true, + CreateCommand: createCommand, + } + + return info, true, nil +} + +// generateServiceName generates the container name and the service name for systemd service. +func generateServiceName(ctr *libpod.Container, pod *libpod.Pod, options entities.GenerateSystemdOptions) (string, string) { + var kind, name, ctrName string + if pod == nil { + kind = "container" + name = ctr.ID() + if options.Name { + name = ctr.Name() + } + ctrName = name + } else { + kind = "pod" + name = pod.ID() + ctrName = ctr.ID() + if options.Name { + name = pod.Name() + ctrName = ctr.Name() + } + } + return ctrName, fmt.Sprintf("%s-%s", kind, name) +} diff --git a/pkg/domain/infra/tunnel/generate.go b/pkg/domain/infra/tunnel/generate.go new file mode 100644 index 000000000..3cd483053 --- /dev/null +++ b/pkg/domain/infra/tunnel/generate.go @@ -0,0 +1,12 @@ +package tunnel + +import ( + "context" + + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" +) + +func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) { + return nil, errors.New("not implemented for tunnel") +} diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index bb84f0618..01ddcf9c8 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -76,6 +76,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener } options := []libpod.CtrCreateOption{} + options = append(options, libpod.WithCreateCommand()) var newImage *image.Image if s.Rootfs != "" { |