aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml18
-rw-r--r--cmd/podman/generate/systemd.go71
-rw-r--r--docs/source/markdown/podman-generate-systemd.1.md6
-rw-r--r--pkg/api/handlers/libpod/generate.go45
-rw-r--r--pkg/api/server/register_generate.go62
-rw-r--r--pkg/bindings/generate/generate.go27
-rw-r--r--pkg/domain/entities/generate.go7
-rw-r--r--pkg/domain/infra/abi/generate.go8
-rw-r--r--pkg/domain/infra/tunnel/generate.go3
-rw-r--r--pkg/specgen/generate/security.go2
-rw-r--r--pkg/systemd/generate/containers.go30
-rw-r--r--pkg/systemd/generate/containers_test.go27
-rw-r--r--pkg/systemd/generate/pods.go49
-rw-r--r--pkg/systemd/generate/pods_test.go9
-rw-r--r--test/e2e/generate_systemd_test.go15
-rw-r--r--test/e2e/run_apparmor_test.go13
-rw-r--r--test/system/helpers.bash2
17 files changed, 307 insertions, 87 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 1bf35e142..367d9e770 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -154,7 +154,7 @@ container_image_build_task:
depends_on:
- "gating"
- # Only run for PRs, quay.io will automatically build after bramch-push
+ # Only run for PRs, quay.io will automatically build after branch-push
only_if: $CIRRUS_BRANCH != $DEST_BRANCH
matrix:
@@ -736,6 +736,7 @@ success_task:
- "verify_test_built_images"
- "docs"
- "static_build"
+ - "darwin_build"
env:
CIRRUS_WORKING_DIR: "/usr/src/libpod"
@@ -784,3 +785,18 @@ static_build_task:
binaries_artifacts:
path: "result/bin/podman"
+
+
+darwin_build_task:
+ depends_on:
+ - "gating"
+ osx_instance:
+ image: catalina-base
+ setup-script:
+ - brew install go
+ - brew install go-md2man
+ build-script:
+ - make podman-remote-darwin
+ - make install-podman-remote-darwin-docs
+ binaries_artifacts:
+ path: "bin/podman-remote-darwin"
diff --git a/cmd/podman/generate/systemd.go b/cmd/podman/generate/systemd.go
index 851a104bc..f690836a4 100644
--- a/cmd/podman/generate/systemd.go
+++ b/cmd/podman/generate/systemd.go
@@ -1,15 +1,22 @@
package pods
import (
+ "encoding/json"
"fmt"
+ "os"
+ "path/filepath"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/utils"
"github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
var (
+ files bool
+ format string
systemdTimeout uint
systemdOptions = entities.GenerateSystemdOptions{}
systemdDescription = `Generate systemd units for a pod or container.
@@ -29,19 +36,20 @@ var (
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
- Mode: []entities.EngineMode{entities.ABIMode},
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: systemdCmd,
Parent: generateCmd,
})
flags := systemdCmd.Flags()
flags.BoolVarP(&systemdOptions.Name, "name", "n", false, "Use container/pod names instead of IDs")
- flags.BoolVarP(&systemdOptions.Files, "files", "f", false, "Generate .service files instead of printing to stdout")
+ flags.BoolVarP(&files, "files", "f", false, "Generate .service files instead of printing to stdout")
flags.UintVarP(&systemdTimeout, "time", "t", containerConfig.Engine.StopTimeout, "Stop timeout override")
flags.StringVar(&systemdOptions.RestartPolicy, "restart-policy", "on-failure", "Systemd restart-policy")
flags.BoolVarP(&systemdOptions.New, "new", "", false, "Create a new container instead of starting an existing one")
flags.StringVar(&systemdOptions.ContainerPrefix, "container-prefix", "container", "Systemd unit name prefix for containers")
flags.StringVar(&systemdOptions.PodPrefix, "pod-prefix", "pod", "Systemd unit name prefix for pods")
flags.StringVar(&systemdOptions.Separator, "separator", "-", "Systemd unit name separator between name/id and prefix")
+ flags.StringVar(&format, "format", "", "Print the created units in specified format (json)")
flags.SetNormalizeFunc(utils.AliasFlags)
}
@@ -50,11 +58,68 @@ func systemd(cmd *cobra.Command, args []string) error {
systemdOptions.StopTimeout = &systemdTimeout
}
+ if registry.IsRemote() {
+ logrus.Warnln("The generated units should be placed on your remote system")
+ }
+
report, err := registry.ContainerEngine().GenerateSystemd(registry.GetContext(), args[0], systemdOptions)
if err != nil {
return err
}
- fmt.Println(report.Output)
+ if files {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return errors.Wrap(err, "error getting current working directory")
+ }
+ for name, content := range report.Units {
+ path := filepath.Join(cwd, fmt.Sprintf("%s.service", name))
+ f, err := os.Create(path)
+ if err != nil {
+ return err
+ }
+ _, err = f.WriteString(content)
+ if err != nil {
+ return err
+ }
+ err = f.Close()
+ if err != nil {
+ return err
+ }
+
+ // add newline if default format is given
+ if format == "" {
+ path += "\n"
+ }
+ // modify in place so we can print the
+ // paths when --files is set
+ report.Units[name] = path
+ }
+ }
+
+ switch format {
+ case "json":
+ return printJSON(report.Units)
+ case "":
+ return printDefault(report.Units)
+ default:
+ return errors.Errorf("unknown --format argument: %s", format)
+ }
+
+}
+
+func printDefault(units map[string]string) error {
+ for _, content := range units {
+ fmt.Print(content)
+ }
+ return nil
+}
+
+func printJSON(units map[string]string) error {
+ b, err := json.MarshalIndent(units, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
return nil
}
diff --git a/docs/source/markdown/podman-generate-systemd.1.md b/docs/source/markdown/podman-generate-systemd.1.md
index d0b1b3588..2ee290f0f 100644
--- a/docs/source/markdown/podman-generate-systemd.1.md
+++ b/docs/source/markdown/podman-generate-systemd.1.md
@@ -10,7 +10,7 @@ podman\-generate\-systemd - Generate systemd unit file(s) for a container or pod
**podman generate systemd** will create a systemd unit file that can be used to control a container or pod.
By default, the command will print the content of the unit files to stdout.
-Note that this command is not supported for the remote client.
+_Note: If you use this command with the remote client, you would still have to place the generated units on the remote system._
## OPTIONS:
@@ -20,6 +20,10 @@ Generate files instead of printing to stdout. The generated files are named {co
Note: On a system with SELinux enabled, the generated files will inherit contexts from the current working directory. Depending on the SELinux setup, changes to the generated files using `restorecon`, `chcon`, or `semanage` may be required to allow systemd to access these files. Alternatively, use the `-Z` option when running `mv` or `cp`.
+**--format**=*format*
+
+Print the created units in specified format (json). If `--files` is specified the paths to the created files will be printed instead of the unit content.
+
**--name**, **-n**
Use the name of the container for the start, stop, and description in the unit file
diff --git a/pkg/api/handlers/libpod/generate.go b/pkg/api/handlers/libpod/generate.go
index 966874a2b..33bb75391 100644
--- a/pkg/api/handlers/libpod/generate.go
+++ b/pkg/api/handlers/libpod/generate.go
@@ -7,10 +7,55 @@ import (
"github.com/containers/podman/v2/pkg/api/handlers/utils"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/domain/infra/abi"
+ "github.com/containers/podman/v2/pkg/util"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
+func GenerateSystemd(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Name bool `schema:"useName"`
+ New bool `schema:"new"`
+ RestartPolicy string `schema:"restartPolicy"`
+ StopTimeout uint `schema:"stopTimeout"`
+ ContainerPrefix string `schema:"containerPrefix"`
+ PodPrefix string `schema:"podPrefix"`
+ Separator string `schema:"separator"`
+ }{
+ RestartPolicy: "on-failure",
+ StopTimeout: util.DefaultContainerConfig().Engine.StopTimeout,
+ ContainerPrefix: "container",
+ PodPrefix: "pod",
+ Separator: "-",
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ containerEngine := abi.ContainerEngine{Libpod: runtime}
+ options := entities.GenerateSystemdOptions{
+ Name: query.Name,
+ New: query.New,
+ RestartPolicy: query.RestartPolicy,
+ StopTimeout: &query.StopTimeout,
+ ContainerPrefix: query.ContainerPrefix,
+ PodPrefix: query.PodPrefix,
+ Separator: query.Separator,
+ }
+ report, err := containerEngine.GenerateSystemd(r.Context(), utils.GetName(r), options)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error generating systemd units"))
+ return
+ }
+
+ utils.WriteResponse(w, http.StatusOK, report.Units)
+}
+
func GenerateKube(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
diff --git a/pkg/api/server/register_generate.go b/pkg/api/server/register_generate.go
index 7db8ee387..60e5b03f7 100644
--- a/pkg/api/server/register_generate.go
+++ b/pkg/api/server/register_generate.go
@@ -8,6 +8,68 @@ import (
)
func (s *APIServer) registerGenerateHandlers(r *mux.Router) error {
+ // swagger:operation GET /libpod/generate/{name:.*}/systemd libpod libpodGenerateSystemd
+ // ---
+ // tags:
+ // - containers
+ // - pods
+ // summary: Generate Systemd Units
+ // description: Generate Systemd Units based on a pod or container.
+ // parameters:
+ // - in: path
+ // name: name:.*
+ // type: string
+ // required: true
+ // description: Name or ID of the container or pod.
+ // - in: query
+ // name: useName
+ // type: boolean
+ // default: false
+ // description: Use container/pod names instead of IDs.
+ // - in: query
+ // name: new
+ // type: boolean
+ // default: false
+ // description: Create a new container instead of starting an existing one.
+ // - in: query
+ // name: time
+ // type: integer
+ // default: 10
+ // description: Stop timeout override.
+ // - in: query
+ // name: restartPolicy
+ // default: on-failure
+ // type: string
+ // enum: ["no", on-success, on-failure, on-abnormal, on-watchdog, on-abort, always]
+ // description: Systemd restart-policy.
+ // - in: query
+ // name: containerPrefix
+ // type: string
+ // default: container
+ // description: Systemd unit name prefix for containers.
+ // - in: query
+ // name: podPrefix
+ // type: string
+ // default: pod
+ // description: Systemd unit name prefix for pods.
+ // - in: query
+ // name: separator
+ // type: string
+ // default: "-"
+ // description: Systemd unit name separator between name/id and prefix.
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: no error
+ // schema:
+ // type: object
+ // additionalProperties:
+ // type: string
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/generate/{name:.*}/systemd"), s.APIHandler(libpod.GenerateSystemd)).Methods(http.MethodGet)
+
// swagger:operation GET /libpod/generate/{name:.*}/kube libpod libpodGenerateKube
// ---
// tags:
diff --git a/pkg/bindings/generate/generate.go b/pkg/bindings/generate/generate.go
index b02221765..dde1cc29c 100644
--- a/pkg/bindings/generate/generate.go
+++ b/pkg/bindings/generate/generate.go
@@ -10,6 +10,33 @@ import (
"github.com/containers/podman/v2/pkg/domain/entities"
)
+func Systemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := url.Values{}
+
+ params.Set("useName", strconv.FormatBool(options.Name))
+ params.Set("new", strconv.FormatBool(options.New))
+ if options.RestartPolicy != "" {
+ params.Set("restartPolicy", options.RestartPolicy)
+ }
+ if options.StopTimeout != nil {
+ params.Set("stopTimeout", strconv.FormatUint(uint64(*options.StopTimeout), 10))
+ }
+ params.Set("containerPrefix", options.ContainerPrefix)
+ params.Set("podPrefix", options.PodPrefix)
+ params.Set("separator", options.Separator)
+
+ response, err := conn.DoRequest(nil, http.MethodGet, "/generate/%s/systemd", params, nil, nameOrID)
+ if err != nil {
+ return nil, err
+ }
+ report := &entities.GenerateSystemdReport{}
+ return report, response.Process(&report.Units)
+}
+
func Kube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
conn, err := bindings.GetClient(ctx)
if err != nil {
diff --git a/pkg/domain/entities/generate.go b/pkg/domain/entities/generate.go
index a8ad13705..4a0d7537e 100644
--- a/pkg/domain/entities/generate.go
+++ b/pkg/domain/entities/generate.go
@@ -4,8 +4,6 @@ import "io"
// 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.
@@ -24,9 +22,8 @@ type GenerateSystemdOptions struct {
// GenerateSystemdReport
type GenerateSystemdReport struct {
- // Output of the generate process. Either the generated files or their
- // entire content.
- Output string
+ // Units of the generate process. key = unit name -> value = unit content
+ Units map[string]string
}
// GenerateKubeOptions control the generation of Kubernetes YAML files.
diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go
index 0b73ddd7e..79bf2291e 100644
--- a/pkg/domain/infra/abi/generate.go
+++ b/pkg/domain/infra/abi/generate.go
@@ -19,11 +19,11 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string,
ctr, ctrErr := ic.Libpod.LookupContainer(nameOrID)
if ctrErr == nil {
// Generate the unit for the container.
- s, err := generate.ContainerUnit(ctr, options)
+ name, content, err := generate.ContainerUnit(ctr, options)
if err != nil {
return nil, err
}
- return &entities.GenerateSystemdReport{Output: s}, nil
+ return &entities.GenerateSystemdReport{Units: map[string]string{name: content}}, nil
}
// If it's not a container, we either have a pod or garbage.
@@ -34,11 +34,11 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string,
}
// Generate the units for the pod and all its containers.
- s, err := generate.PodUnits(pod, options)
+ units, err := generate.PodUnits(pod, options)
if err != nil {
return nil, err
}
- return &entities.GenerateSystemdReport{Output: s}, nil
+ return &entities.GenerateSystemdReport{Units: units}, nil
}
func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
diff --git a/pkg/domain/infra/tunnel/generate.go b/pkg/domain/infra/tunnel/generate.go
index c7d5cd9e2..966f707b1 100644
--- a/pkg/domain/infra/tunnel/generate.go
+++ b/pkg/domain/infra/tunnel/generate.go
@@ -5,11 +5,10 @@ import (
"github.com/containers/podman/v2/pkg/bindings/generate"
"github.com/containers/podman/v2/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")
+ return generate.Systemd(ic.ClientCxt, nameOrID, options)
}
func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go
index d3e3d9278..87e8029a7 100644
--- a/pkg/specgen/generate/security.go
+++ b/pkg/specgen/generate/security.go
@@ -60,7 +60,7 @@ func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig s
func setupApparmor(s *specgen.SpecGenerator, rtc *config.Config, g *generate.Generator) error {
hasProfile := len(s.ApparmorProfile) > 0
if !apparmor.IsEnabled() {
- if hasProfile {
+ if hasProfile && s.ApparmorProfile != "unconfined" {
return errors.Errorf("Apparmor profile %q specified, but Apparmor is not enabled on this system", s.ApparmorProfile)
}
return nil
diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go
index 5f6376977..caf5de357 100644
--- a/pkg/systemd/generate/containers.go
+++ b/pkg/systemd/generate/containers.go
@@ -3,9 +3,7 @@ package generate
import (
"bytes"
"fmt"
- "io/ioutil"
"os"
- "path/filepath"
"sort"
"strings"
"text/template"
@@ -87,17 +85,22 @@ KillMode=none
Type=forking
[Install]
-WantedBy=multi-user.target default.target`
+WantedBy=multi-user.target default.target
+`
// ContainerUnit generates a systemd unit for the specified container. Based
// on the options, the return value might be the entire unit or a file it has
// been written to.
-func ContainerUnit(ctr *libpod.Container, options entities.GenerateSystemdOptions) (string, error) {
+func ContainerUnit(ctr *libpod.Container, options entities.GenerateSystemdOptions) (string, string, error) {
info, err := generateContainerInfo(ctr, options)
if err != nil {
- return "", err
+ return "", "", err
+ }
+ content, err := executeContainerTemplate(info, options)
+ if err != nil {
+ return "", "", err
}
- return executeContainerTemplate(info, options)
+ return info.ServiceName, content, nil
}
func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSystemdOptions) (*containerInfo, error) {
@@ -288,18 +291,5 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst
return "", err
}
- if !options.Files {
- return buf.String(), nil
- }
-
- buf.WriteByte('\n')
- cwd, err := os.Getwd()
- if err != nil {
- return "", errors.Wrap(err, "error getting current working directory")
- }
- path := filepath.Join(cwd, fmt.Sprintf("%s.service", info.ServiceName))
- if err := ioutil.WriteFile(path, buf.Bytes(), 0644); err != nil {
- return "", errors.Wrap(err, "error generating systemd unit")
- }
- return path, nil
+ return buf.String(), nil
}
diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go
index b5c736c5a..d27062ef3 100644
--- a/pkg/systemd/generate/containers_test.go
+++ b/pkg/systemd/generate/containers_test.go
@@ -56,7 +56,8 @@ KillMode=none
Type=forking
[Install]
-WantedBy=multi-user.target default.target`
+WantedBy=multi-user.target default.target
+`
goodName := `# container-foobar.service
# autogenerated by Podman CI
@@ -78,7 +79,8 @@ KillMode=none
Type=forking
[Install]
-WantedBy=multi-user.target default.target`
+WantedBy=multi-user.target default.target
+`
goodNameBoundTo := `# container-foobar.service
# autogenerated by Podman CI
@@ -102,7 +104,8 @@ KillMode=none
Type=forking
[Install]
-WantedBy=multi-user.target default.target`
+WantedBy=multi-user.target default.target
+`
goodWithNameAndGeneric := `# jadda-jadda.service
# autogenerated by Podman CI
@@ -125,7 +128,8 @@ KillMode=none
Type=forking
[Install]
-WantedBy=multi-user.target default.target`
+WantedBy=multi-user.target default.target
+`
goodWithExplicitShortDetachParam := `# jadda-jadda.service
# autogenerated by Podman CI
@@ -148,7 +152,8 @@ KillMode=none
Type=forking
[Install]
-WantedBy=multi-user.target default.target`
+WantedBy=multi-user.target default.target
+`
goodNameNewWithPodFile := `# jadda-jadda.service
# autogenerated by Podman CI
@@ -171,7 +176,8 @@ KillMode=none
Type=forking
[Install]
-WantedBy=multi-user.target default.target`
+WantedBy=multi-user.target default.target
+`
goodNameNewDetach := `# jadda-jadda.service
# autogenerated by Podman CI
@@ -194,7 +200,8 @@ KillMode=none
Type=forking
[Install]
-WantedBy=multi-user.target default.target`
+WantedBy=multi-user.target default.target
+`
goodIDNew := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
# autogenerated by Podman CI
@@ -217,7 +224,8 @@ KillMode=none
Type=forking
[Install]
-WantedBy=multi-user.target default.target`
+WantedBy=multi-user.target default.target
+`
tests := []struct {
name string
@@ -375,8 +383,7 @@ WantedBy=multi-user.target default.target`
test := tt
t.Run(tt.name, func(t *testing.T) {
opts := entities.GenerateSystemdOptions{
- Files: false,
- New: test.new,
+ New: test.new,
}
got, err := executeContainerTemplate(&test.info, opts)
if (err != nil) != test.wantErr {
diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go
index dec9587d9..c41eedd17 100644
--- a/pkg/systemd/generate/pods.go
+++ b/pkg/systemd/generate/pods.go
@@ -3,9 +3,7 @@ package generate
import (
"bytes"
"fmt"
- "io/ioutil"
"os"
- "path/filepath"
"sort"
"strings"
"text/template"
@@ -88,39 +86,40 @@ KillMode=none
Type=forking
[Install]
-WantedBy=multi-user.target default.target`
+WantedBy=multi-user.target default.target
+`
// PodUnits generates systemd units for the specified pod and its containers.
// Based on the options, the return value might be the content of all units or
// the files they been written to.
-func PodUnits(pod *libpod.Pod, options entities.GenerateSystemdOptions) (string, error) {
+func PodUnits(pod *libpod.Pod, options entities.GenerateSystemdOptions) (map[string]string, error) {
// Error out if the pod has no infra container, which we require to be the
// main service.
if !pod.HasInfraContainer() {
- return "", errors.Errorf("error generating systemd unit files: Pod %q has no infra container", pod.Name())
+ return nil, errors.Errorf("error generating systemd unit files: Pod %q has no infra container", pod.Name())
}
podInfo, err := generatePodInfo(pod, options)
if err != nil {
- return "", err
+ return nil, err
}
infraID, err := pod.InfraContainerID()
if err != nil {
- return "", err
+ return nil, err
}
// Compute the container-dependency graph for the Pod.
containers, err := pod.AllContainers()
if err != nil {
- return "", err
+ return nil, err
}
if len(containers) == 0 {
- return "", errors.Errorf("error generating systemd unit files: Pod %q has no containers", pod.Name())
+ return nil, errors.Errorf("error generating systemd unit files: Pod %q has no containers", pod.Name())
}
graph, err := libpod.BuildContainerGraph(containers)
if err != nil {
- return "", err
+ return nil, err
}
// Traverse the dependency graph and create systemdgen.containerInfo's for
@@ -133,7 +132,7 @@ func PodUnits(pod *libpod.Pod, options entities.GenerateSystemdOptions) (string,
}
ctrInfo, err := generateContainerInfo(ctr, options)
if err != nil {
- return "", err
+ return nil, err
}
// Now add the container's dependencies and at the container as a
// required service of the infra container.
@@ -149,24 +148,23 @@ func PodUnits(pod *libpod.Pod, options entities.GenerateSystemdOptions) (string,
containerInfos = append(containerInfos, ctrInfo)
}
+ units := map[string]string{}
// Now generate the systemd service for all containers.
- builder := strings.Builder{}
out, err := executePodTemplate(podInfo, options)
if err != nil {
- return "", err
+ return nil, err
}
- builder.WriteString(out)
+ units[podInfo.ServiceName] = out
for _, info := range containerInfos {
info.pod = podInfo
- builder.WriteByte('\n')
out, err := executeContainerTemplate(info, options)
if err != nil {
- return "", err
+ return nil, err
}
- builder.WriteString(out)
+ units[info.ServiceName] = out
}
- return builder.String(), nil
+ return units, nil
}
func generatePodInfo(pod *libpod.Pod, options entities.GenerateSystemdOptions) (*podInfo, error) {
@@ -339,18 +337,5 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions)
return "", err
}
- if !options.Files {
- return buf.String(), nil
- }
-
- buf.WriteByte('\n')
- cwd, err := os.Getwd()
- if err != nil {
- return "", errors.Wrap(err, "error getting current working directory")
- }
- path := filepath.Join(cwd, fmt.Sprintf("%s.service", info.ServiceName))
- if err := ioutil.WriteFile(path, buf.Bytes(), 0644); err != nil {
- return "", errors.Wrap(err, "error generating systemd unit")
- }
- return path, nil
+ return buf.String(), nil
}
diff --git a/pkg/systemd/generate/pods_test.go b/pkg/systemd/generate/pods_test.go
index 8bf4705a7..7f1f63b7e 100644
--- a/pkg/systemd/generate/pods_test.go
+++ b/pkg/systemd/generate/pods_test.go
@@ -58,7 +58,8 @@ KillMode=none
Type=forking
[Install]
-WantedBy=multi-user.target default.target`
+WantedBy=multi-user.target default.target
+`
podGoodNamedNew := `# pod-123abc.service
# autogenerated by Podman CI
@@ -84,7 +85,8 @@ KillMode=none
Type=forking
[Install]
-WantedBy=multi-user.target default.target`
+WantedBy=multi-user.target default.target
+`
tests := []struct {
name string
@@ -130,8 +132,7 @@ WantedBy=multi-user.target default.target`
test := tt
t.Run(tt.name, func(t *testing.T) {
opts := entities.GenerateSystemdOptions{
- Files: false,
- New: test.new,
+ New: test.new,
}
got, err := executePodTemplate(&test.info, opts)
if (err != nil) != test.wantErr {
diff --git a/test/e2e/generate_systemd_test.go b/test/e2e/generate_systemd_test.go
index 60d9162d1..cd3ee6e0a 100644
--- a/test/e2e/generate_systemd_test.go
+++ b/test/e2e/generate_systemd_test.go
@@ -1,5 +1,3 @@
-// +build !remote
-
package integration
import (
@@ -61,7 +59,7 @@ var _ = Describe("Podman generate systemd", func() {
session = podmanTest.Podman([]string{"generate", "systemd", "--restart-policy", "bogus", "foobar"})
session.WaitWithDefaultTimeout()
Expect(session).To(ExitWithError())
- found, _ := session.ErrorGrepString("Error: bogus is not a valid restart policy")
+ found, _ := session.ErrorGrepString("bogus is not a valid restart policy")
Expect(found).Should(BeTrue())
})
@@ -383,4 +381,15 @@ var _ = Describe("Podman generate systemd", func() {
found, _ = session.GrepString("pod rm --ignore -f --pod-id-file %t/pod-foo.pod-id")
Expect(found).To(BeTrue())
})
+
+ It("podman generate systemd --format json", func() {
+ n := podmanTest.Podman([]string{"create", "--name", "foo", ALPINE})
+ n.WaitWithDefaultTimeout()
+ Expect(n.ExitCode()).To(Equal(0))
+
+ session := podmanTest.Podman([]string{"generate", "systemd", "--format", "json", "foo"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.IsJSONOutputValid()).To(BeTrue())
+ })
})
diff --git a/test/e2e/run_apparmor_test.go b/test/e2e/run_apparmor_test.go
index 53cac9529..7d522a752 100644
--- a/test/e2e/run_apparmor_test.go
+++ b/test/e2e/run_apparmor_test.go
@@ -155,4 +155,17 @@ profile aa-test-profile flags=(attach_disconnected,mediate_deleted) {
inspect := podmanTest.InspectContainer(cid)
Expect(inspect[0].AppArmorProfile).To(Equal(""))
})
+
+ It("podman run apparmor disabled unconfined", func() {
+ skipIfAppArmorEnabled()
+
+ session := podmanTest.Podman([]string{"create", "--security-opt", "apparmor=unconfined", ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ cid := session.OutputToString()
+ // Verify that apparmor.Profile is being set
+ inspect := podmanTest.InspectContainer(cid)
+ Expect(inspect[0].AppArmorProfile).To(Equal(""))
+ })
})
diff --git a/test/system/helpers.bash b/test/system/helpers.bash
index a6414344e..6c24b24b3 100644
--- a/test/system/helpers.bash
+++ b/test/system/helpers.bash
@@ -67,7 +67,7 @@ function basic_teardown() {
run_podman '?' pod rm --all --force
run_podman '?' rm --all --force
- /bin/rm -rf $PODMAN_TMPDIR
+ command rm -rf $PODMAN_TMPDIR
}