diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/adapter/containers.go | 145 | ||||
-rw-r--r-- | pkg/adapter/containers_remote.go | 2 | ||||
-rw-r--r-- | pkg/adapter/pods.go | 10 | ||||
-rw-r--r-- | pkg/adapter/runtime.go | 18 | ||||
-rw-r--r-- | pkg/adapter/runtime_remote.go | 7 | ||||
-rw-r--r-- | pkg/adapter/volumes_remote.go | 2 | ||||
-rw-r--r-- | pkg/cgroups/cgroups.go | 8 | ||||
-rw-r--r-- | pkg/firewall/common.go | 55 | ||||
-rw-r--r-- | pkg/firewall/firewall_linux.go | 47 | ||||
-rw-r--r-- | pkg/firewall/firewall_none.go | 43 | ||||
-rw-r--r-- | pkg/firewall/firewall_unsupported.go | 27 | ||||
-rw-r--r-- | pkg/firewall/firewalld.go | 122 | ||||
-rw-r--r-- | pkg/firewall/iptables.go | 195 | ||||
-rw-r--r-- | pkg/systemdgen/systemdgen.go | 147 | ||||
-rw-r--r-- | pkg/systemdgen/systemdgen_test.go | 180 | ||||
-rw-r--r-- | pkg/util/utils_supported.go | 6 | ||||
-rw-r--r-- | pkg/util/utils_windows.go | 8 | ||||
-rw-r--r-- | pkg/varlinkapi/containers.go | 15 | ||||
-rw-r--r-- | pkg/varlinkapi/generate.go | 23 | ||||
-rw-r--r-- | pkg/varlinkapi/images.go | 26 | ||||
-rw-r--r-- | pkg/varlinkapi/volumes.go | 1 |
21 files changed, 456 insertions, 631 deletions
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index 45a9a54a3..41607145d 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -342,7 +342,8 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode if err := ctr.Start(ctx, c.IsSet("pod")); err != nil { // This means the command did not exist exitCode = 127 - if strings.Contains(err.Error(), "permission denied") || strings.Contains(err.Error(), "file not found") { + e := strings.ToLower(err.Error()) + if strings.Contains(e, "permission denied") || strings.Contains(e, "operation not permitted") || strings.Contains(e, "file not found") || strings.Contains(e, "no such file or directory") { exitCode = 126 } return exitCode, err @@ -405,12 +406,13 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode } // This means the command did not exist exitCode = 127 - if strings.Contains(err.Error(), "permission denied") { + e := strings.ToLower(err.Error()) + if strings.Contains(e, "permission denied") || strings.Contains(e, "operation not permitted") { exitCode = 126 } if c.IsSet("rm") { if deleteError := r.Runtime.RemoveContainer(ctx, ctr, true, false); deleteError != nil { - logrus.Errorf("unable to remove container %s after failing to start and attach to it", ctr.ID()) + logrus.Debugf("unable to remove container %s after failing to start and attach to it", ctr.ID()) } } return exitCode, err @@ -1092,28 +1094,145 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) { return portContainers, nil } -// GenerateSystemd creates a unit file for a container -func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) { - ctr, err := r.Runtime.LookupContainer(c.InputArgs[0]) +// generateServiceName generates the container name and the service name for systemd service. +func generateServiceName(c *cliconfig.GenerateSystemdValues, ctr *libpod.Container, pod *libpod.Pod) (string, string) { + var kind, name, ctrName string + if pod == nil { + kind = "container" + name = ctr.ID() + if c.Name { + name = ctr.Name() + } + ctrName = name + } else { + kind = "pod" + name = pod.ID() + ctrName = ctr.ID() + if c.Name { + name = pod.Name() + ctrName = ctr.Name() + } + } + return ctrName, fmt.Sprintf("%s-%s", kind, name) +} + +// generateSystemdgenContainerInfo is a helper to generate a +// systemdgen.ContainerInfo for `GenerateSystemd`. +func (r *LocalRuntime) generateSystemdgenContainerInfo(c *cliconfig.GenerateSystemdValues, nameOrID string, pod *libpod.Pod) (*systemdgen.ContainerInfo, bool, error) { + ctr, err := r.Runtime.LookupContainer(nameOrID) if err != nil { - return "", err + return nil, false, err } + timeout := int(ctr.StopTimeout()) if c.StopTimeout >= 0 { timeout = c.StopTimeout } - name := ctr.ID() - if c.Name { - name = ctr.Name() - } config := ctr.Config() conmonPidFile := config.ConmonPidFile if conmonPidFile == "" { - return "", errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag") + return nil, true, errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag") + } + + name, serviceName := generateServiceName(c, ctr, pod) + info := &systemdgen.ContainerInfo{ + ServiceName: serviceName, + ContainerName: name, + RestartPolicy: c.RestartPolicy, + PIDFile: conmonPidFile, + StopTimeout: timeout, + GenerateTimestamp: true, + } + + return info, true, nil +} + +// GenerateSystemd creates a unit file for a container or pod. +func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) { + // First assume it's a container. + if info, found, err := r.generateSystemdgenContainerInfo(c, c.InputArgs[0], nil); found && err != nil { + return "", err + } else if found && err == nil { + return systemdgen.CreateContainerSystemdUnit(info, c.Files) + } + + // We're either having a pod or garbage. + pod, err := r.Runtime.LookupPod(c.InputArgs[0]) + if err != nil { + return "", err + } + + // Error out if the pod has no infra container, which we require to be the + // main service. + if !pod.HasInfraContainer() { + return "", 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 + } + podInfo, _, err := r.generateSystemdgenContainerInfo(c, infraID, pod) + if err != nil { + return "", nil + } + + // Compute the container-dependency graph for the Pod. + containers, err := pod.AllContainers() + if err != nil { + return "", err + } + if len(containers) == 0 { + return "", fmt.Errorf("error generating systemd unit files: Pod %q has no containers", pod.Name()) + } + graph, err := libpod.BuildContainerGraph(containers) + if err != nil { + return "", err + } + + // Traverse the dependency graph and create systemdgen.ContainerInfo's for + // each container. + containerInfos := []*systemdgen.ContainerInfo{podInfo} + for ctr, dependencies := range graph.DependencyMap() { + // Skip the infra container as we already generated it. + if ctr.ID() == infraID { + continue + } + ctrInfo, _, err := r.generateSystemdgenContainerInfo(c, ctr.ID(), nil) + if err != nil { + return "", 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(c, dep, nil) + 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 := systemdgen.CreateContainerSystemdUnit(info, c.Files) + if err != nil { + return "", err + } + builder.WriteString(out) } - return systemdgen.CreateSystemdUnitAsString(name, ctr.ID(), c.RestartPolicy, conmonPidFile, timeout) + return builder.String(), nil } // GetNamespaces returns namespace information about a container for PS diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index 5a26f537f..590fef43f 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -951,7 +951,7 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) { // GenerateSystemd creates a systemd until for a container func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) { - return iopodman.GenerateSystemd().Call(r.Conn, c.InputArgs[0], c.RestartPolicy, int64(c.StopTimeout), c.Name) + return "", errors.New("systemd generation not supported for remote clients") } // GetNamespaces returns namespace information about a container for PS diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go index 1dd72babf..ded805de2 100644 --- a/pkg/adapter/pods.go +++ b/pkg/adapter/pods.go @@ -256,6 +256,10 @@ func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateVa options = append(options, libpod.WithPodName(cli.Name)) } + if cli.Flag("hostname").Changed { + options = append(options, libpod.WithPodHostname(cli.Hostname)) + } + if cli.Infra { options = append(options, libpod.WithInfraContainer()) nsOptions, err := shared.GetNamespaceOptions(strings.Split(cli.Share, ",")) @@ -476,6 +480,12 @@ func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayVa podOptions = append(podOptions, libpod.WithPodName(podName)) // TODO for now we just used the default kernel namespaces; we need to add/subtract this from yaml + hostname := podYAML.Spec.Hostname + if hostname == "" { + hostname = podName + } + podOptions = append(podOptions, libpod.WithPodHostname(hostname)) + nsOptions, err := shared.GetNamespaceOptions(strings.Split(shared.DefaultKernelNamespaces, ",")) if err != nil { return nil, err diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index ba988aaf7..0537308f8 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -201,16 +201,16 @@ func (r *LocalRuntime) RemoveVolumes(ctx context.Context, c *cliconfig.VolumeRmV } // Push is a wrapper to push an image to a registry -func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { +func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, digestfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { newImage, err := r.ImageRuntime().NewFromLocal(srcName) if err != nil { return err } - return newImage.PushImageToHeuristicDestination(ctx, destination, manifestMIMEType, authfile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, nil) + return newImage.PushImageToHeuristicDestination(ctx, destination, manifestMIMEType, authfile, digestfile, signaturePolicyPath, writer, forceCompress, signingOptions, dockerRegistryOptions, nil) } // InspectVolumes returns a slice of volumes based on an arg list or --all -func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*Volume, error) { +func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*libpod.InspectVolumeData, error) { var ( volumes []*libpod.Volume err error @@ -230,7 +230,17 @@ func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeIn if err != nil { return nil, err } - return libpodVolumeToVolume(volumes), nil + + inspectVols := make([]*libpod.InspectVolumeData, 0, len(volumes)) + for _, vol := range volumes { + inspectOut, err := vol.Inspect() + if err != nil { + return nil, errors.Wrapf(err, "error inspecting volume %s", vol.Name()) + } + inspectVols = append(inspectVols, inspectOut) + } + + return inspectVols, nil } // Volumes returns a slice of localruntime volumes diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index 420c9d0bb..8588966b6 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -21,7 +21,7 @@ import ( "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/remoteclientconfig" - "github.com/containers/libpod/cmd/podman/varlink" + iopodman "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/events" @@ -619,7 +619,7 @@ func (r *LocalRuntime) RemoveVolumes(ctx context.Context, c *cliconfig.VolumeRmV return iopodman.VolumeRemove().Call(r.Conn, rmOpts) } -func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { +func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, digestfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { reply, err := iopodman.PushImage().Send(r.Conn, varlink.More, srcName, destination, forceCompress, manifestMIMEType, signingOptions.RemoveSignatures, signingOptions.SignBy) if err != nil { @@ -669,7 +669,6 @@ func varlinkVolumeToVolume(r *LocalRuntime, volumes []iopodman.Volume) []*Volume MountPoint: v.MountPoint, Driver: v.Driver, Options: v.Options, - Scope: v.Scope, } n := remoteVolume{ Runtime: r, @@ -813,7 +812,7 @@ func IsImageNotFound(err error) bool { // HealthCheck executes a container's healthcheck over a varlink connection func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (string, error) { - return "", define.ErrNotImplemented + return iopodman.HealthCheckRun().Call(r.Conn, c.InputArgs[0]) } // Events monitors libpod/podman events over a varlink connection diff --git a/pkg/adapter/volumes_remote.go b/pkg/adapter/volumes_remote.go index beacd943a..58f9ba625 100644 --- a/pkg/adapter/volumes_remote.go +++ b/pkg/adapter/volumes_remote.go @@ -29,5 +29,5 @@ func (v *Volume) MountPoint() string { // Scope returns the scope for an adapter.volume func (v *Volume) Scope() string { - return v.config.Scope + return "local" } diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index 085718855..9711e8120 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" + "github.com/containers/libpod/pkg/rootless" systemdDbus "github.com/coreos/go-systemd/dbus" "github.com/godbus/dbus" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -19,7 +20,9 @@ import ( var ( // ErrCgroupDeleted means the cgroup was deleted - ErrCgroupDeleted = errors.New("cgroups: cgroup deleted") + ErrCgroupDeleted = errors.New("cgroup deleted") + // ErrCgroupV1Rootless means the cgroup v1 were attempted to be used in rootless environmen + ErrCgroupV1Rootless = errors.New("no support for CGroups V1 in rootless environments") ) // CgroupControl controls a cgroup hierarchy @@ -339,6 +342,9 @@ func Load(path string) (*CgroupControl, error) { p := control.getCgroupv1Path(name) if _, err := os.Stat(p); err != nil { if os.IsNotExist(err) { + if rootless.IsRootless() { + return nil, ErrCgroupV1Rootless + } // compatible with the error code // used by containerd/cgroups return nil, ErrCgroupDeleted diff --git a/pkg/firewall/common.go b/pkg/firewall/common.go deleted file mode 100644 index a65d4f03d..000000000 --- a/pkg/firewall/common.go +++ /dev/null @@ -1,55 +0,0 @@ -package firewall - -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import ( - "net" - - "github.com/containernetworking/cni/pkg/types/current" -) - -// FirewallNetConf represents the firewall configuration. -// Nolint applied for firewall.Firewall... name duplication notice. -//nolint -type FirewallNetConf struct { - //types.NetConf - - // IptablesAdminChainName is an optional name to use instead of the default - // admin rules override chain name that includes the interface name. - IptablesAdminChainName string - - // FirewalldZone is an optional firewalld zone to place the interface into. If - // the firewalld backend is used but the zone is not given, it defaults - // to 'trusted' - FirewalldZone string - - PrevResult *current.Result -} - -// FirewallBackend is an interface to the system firewall, allowing addition and -// removal of firewall rules. -// Nolint applied for firewall.Firewall... name duplication notice. -//nolint -type FirewallBackend interface { - Add(*FirewallNetConf) error - Del(*FirewallNetConf) error -} - -func ipString(ip net.IPNet) string { - if ip.IP.To4() == nil { - return ip.IP.String() + "/128" - } - return ip.IP.String() + "/32" -} diff --git a/pkg/firewall/firewall_linux.go b/pkg/firewall/firewall_linux.go deleted file mode 100644 index 4ac45427b..000000000 --- a/pkg/firewall/firewall_linux.go +++ /dev/null @@ -1,47 +0,0 @@ -// +build linux - -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package firewall - -import ( - "fmt" -) - -// GetBackend retrieves a firewall backend for adding or removing firewall rules -// on the system. -// Valid backend names are firewalld, iptables, and none. -// If the empty string is given, a firewalld backend will be returned if -// firewalld is running, and an iptables backend will be returned otherwise. -func GetBackend(backend string) (FirewallBackend, error) { - switch backend { - case "firewalld": - return newFirewalldBackend() - case "iptables": - return newIptablesBackend() - case "none": - return newNoneBackend() - case "": - // Default to firewalld if it's running - if isFirewalldRunning() { - return newFirewalldBackend() - } - - // Otherwise iptables - return newIptablesBackend() - default: - return nil, fmt.Errorf("unrecognized firewall backend %q", backend) - } -} diff --git a/pkg/firewall/firewall_none.go b/pkg/firewall/firewall_none.go deleted file mode 100644 index 9add24842..000000000 --- a/pkg/firewall/firewall_none.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package firewall - -import ( - "fmt" -) - -// FirewallNone is a firewall backend for environments where manipulating the -// system firewall is unsupported (for example, when running without root). -// Nolint applied to avoid firewall.FirewallNone name duplication notes. -//nolint -type FirewallNone struct{} - -func newNoneBackend() (FirewallBackend, error) { - return &FirewallNone{}, nil -} - -// Add adds a rule to the system firewall. -// No action is taken and an error is unconditionally returned as this backend -// does not support manipulating the firewall. -func (f *FirewallNone) Add(conf *FirewallNetConf) error { - return fmt.Errorf("cannot modify system firewall rules") -} - -// Del deletes a rule from the system firewall. -// No action is taken and an error is unconditionally returned as this backend -// does not support manipulating the firewall. -func (f *FirewallNone) Del(conf *FirewallNetConf) error { - return fmt.Errorf("cannot modify system firewall rules") -} diff --git a/pkg/firewall/firewall_unsupported.go b/pkg/firewall/firewall_unsupported.go deleted file mode 100644 index 24c07a8a9..000000000 --- a/pkg/firewall/firewall_unsupported.go +++ /dev/null @@ -1,27 +0,0 @@ -// +build !linux - -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package firewall - -import ( - "fmt" -) - -// GetBackend retrieves a firewall backend for adding or removing firewall rules -// on the system. -func GetBackend(backend string) (FirewallBackend, error) { - return nil, fmt.Errorf("firewall backends are not presently supported on this OS") -} diff --git a/pkg/firewall/firewalld.go b/pkg/firewall/firewalld.go deleted file mode 100644 index 15e845cb7..000000000 --- a/pkg/firewall/firewalld.go +++ /dev/null @@ -1,122 +0,0 @@ -// +build linux - -// Copyright 2018 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package firewall - -import ( - "fmt" - "github.com/sirupsen/logrus" - "strings" - - "github.com/godbus/dbus" -) - -const ( - dbusName = "org.freedesktop.DBus" - dbusPath = "/org/freedesktop/DBus" - dbusGetNameOwnerMethod = "GetNameOwner" - - firewalldName = "org.fedoraproject.FirewallD1" - firewalldPath = "/org/fedoraproject/FirewallD1" - firewalldZoneInterface = "org.fedoraproject.FirewallD1.zone" - firewalldAddSourceMethod = "addSource" - firewalldRemoveSourceMethod = "removeSource" - - errZoneAlreadySet = "ZONE_ALREADY_SET" -) - -// Only used for testcases to override the D-Bus connection -var testConn *dbus.Conn - -type fwdBackend struct { - conn *dbus.Conn -} - -// fwdBackend implements the FirewallBackend interface -var _ FirewallBackend = &fwdBackend{} - -func getConn() (*dbus.Conn, error) { - if testConn != nil { - return testConn, nil - } - return dbus.SystemBus() -} - -// isFirewalldRunning checks whether firewalld is running. -func isFirewalldRunning() bool { - conn, err := getConn() - if err != nil { - return false - } - - dbusObj := conn.Object(dbusName, dbusPath) - var res string - if err := dbusObj.Call(dbusName+"."+dbusGetNameOwnerMethod, 0, firewalldName).Store(&res); err != nil { - return false - } - - return true -} - -func newFirewalldBackend() (FirewallBackend, error) { - conn, err := getConn() - if err != nil { - return nil, err - } - - backend := &fwdBackend{ - conn: conn, - } - return backend, nil -} - -func getFirewalldZone(conf *FirewallNetConf) string { - if conf.FirewalldZone != "" { - return conf.FirewalldZone - } - - return "trusted" -} - -func (fb *fwdBackend) Add(conf *FirewallNetConf) error { - zone := getFirewalldZone(conf) - - for _, ip := range conf.PrevResult.IPs { - ipStr := ipString(ip.Address) - // Add a firewalld rule which assigns the given source IP to the given zone - firewalldObj := fb.conn.Object(firewalldName, firewalldPath) - var res string - if err := firewalldObj.Call(firewalldZoneInterface+"."+firewalldAddSourceMethod, 0, zone, ipStr).Store(&res); err != nil { - if !strings.Contains(err.Error(), errZoneAlreadySet) { - return fmt.Errorf("failed to add the address %v to %v zone: %v", ipStr, zone, err) - } - } - } - return nil -} - -func (fb *fwdBackend) Del(conf *FirewallNetConf) error { - for _, ip := range conf.PrevResult.IPs { - ipStr := ipString(ip.Address) - // Remove firewalld rules which assigned the given source IP to the given zone - firewalldObj := fb.conn.Object(firewalldName, firewalldPath) - var res string - if err := firewalldObj.Call(firewalldZoneInterface+"."+firewalldRemoveSourceMethod, 0, getFirewalldZone(conf), ipStr).Store(&res); err != nil { - logrus.Errorf("unable to store firewallobj") - } - } - return nil -} diff --git a/pkg/firewall/iptables.go b/pkg/firewall/iptables.go deleted file mode 100644 index 169ddc1d7..000000000 --- a/pkg/firewall/iptables.go +++ /dev/null @@ -1,195 +0,0 @@ -// +build linux - -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This is a "meta-plugin". It reads in its own netconf, it does not create -// any network interface but just changes the network sysctl. - -package firewall - -import ( - "fmt" - "github.com/sirupsen/logrus" - "net" - - "github.com/coreos/go-iptables/iptables" -) - -func getPrivChainRules(ip string) [][]string { - var rules [][]string - rules = append(rules, []string{"-d", ip, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}) - rules = append(rules, []string{"-s", ip, "-j", "ACCEPT"}) - return rules -} - -func ensureChain(ipt *iptables.IPTables, table, chain string) error { - chains, err := ipt.ListChains(table) - if err != nil { - return fmt.Errorf("failed to list iptables chains: %v", err) - } - for _, ch := range chains { - if ch == chain { - return nil - } - } - - return ipt.NewChain(table, chain) -} - -func generateFilterRule(privChainName string) []string { - return []string{"-m", "comment", "--comment", "CNI firewall plugin rules", "-j", privChainName} -} - -func cleanupRules(ipt *iptables.IPTables, privChainName string, rules [][]string) { - for _, rule := range rules { - if err := ipt.Delete("filter", privChainName, rule...); err != nil { - logrus.Errorf("failed to delete iptables rule %s", privChainName) - } - } -} - -func ensureFirstChainRule(ipt *iptables.IPTables, chain string, rule []string) error { - exists, err := ipt.Exists("filter", chain, rule...) - if !exists && err == nil { - err = ipt.Insert("filter", chain, 1, rule...) - } - return err -} - -func (ib *iptablesBackend) setupChains(ipt *iptables.IPTables) error { - privRule := generateFilterRule(ib.privChainName) - adminRule := generateFilterRule(ib.adminChainName) - - // Ensure our private chains exist - if err := ensureChain(ipt, "filter", ib.privChainName); err != nil { - return err - } - if err := ensureChain(ipt, "filter", ib.adminChainName); err != nil { - return err - } - - // Ensure our filter rule exists in the forward chain - if err := ensureFirstChainRule(ipt, "FORWARD", privRule); err != nil { - return err - } - - // Ensure our admin override chain rule exists in our private chain - if err := ensureFirstChainRule(ipt, ib.privChainName, adminRule); err != nil { - return err - } - - return nil -} - -func protoForIP(ip net.IPNet) iptables.Protocol { - if ip.IP.To4() != nil { - return iptables.ProtocolIPv4 - } - return iptables.ProtocolIPv6 -} - -func (ib *iptablesBackend) addRules(conf *FirewallNetConf, ipt *iptables.IPTables, proto iptables.Protocol) error { - rules := make([][]string, 0) - for _, ip := range conf.PrevResult.IPs { - if protoForIP(ip.Address) == proto { - rules = append(rules, getPrivChainRules(ipString(ip.Address))...) - } - } - - if len(rules) > 0 { - if err := ib.setupChains(ipt); err != nil { - return err - } - - // Clean up on any errors - var err error - defer func() { - if err != nil { - cleanupRules(ipt, ib.privChainName, rules) - } - }() - - for _, rule := range rules { - err = ipt.AppendUnique("filter", ib.privChainName, rule...) - if err != nil { - return err - } - } - } - - return nil -} - -func (ib *iptablesBackend) delRules(conf *FirewallNetConf, ipt *iptables.IPTables, proto iptables.Protocol) error { - rules := make([][]string, 0) - for _, ip := range conf.PrevResult.IPs { - if protoForIP(ip.Address) == proto { - rules = append(rules, getPrivChainRules(ipString(ip.Address))...) - } - } - - if len(rules) > 0 { - cleanupRules(ipt, ib.privChainName, rules) - } - - return nil -} - -type iptablesBackend struct { - protos map[iptables.Protocol]*iptables.IPTables - privChainName string - adminChainName string -} - -// iptablesBackend implements the FirewallBackend interface -var _ FirewallBackend = &iptablesBackend{} - -func newIptablesBackend() (FirewallBackend, error) { - adminChainName := "CNI-ADMIN" - - backend := &iptablesBackend{ - privChainName: "CNI-FORWARD", - adminChainName: adminChainName, - protos: make(map[iptables.Protocol]*iptables.IPTables), - } - - for _, proto := range []iptables.Protocol{iptables.ProtocolIPv4, iptables.ProtocolIPv6} { - ipt, err := iptables.NewWithProtocol(proto) - if err != nil { - return nil, fmt.Errorf("could not initialize iptables protocol %v: %v", proto, err) - } - backend.protos[proto] = ipt - } - - return backend, nil -} - -func (ib *iptablesBackend) Add(conf *FirewallNetConf) error { - for proto, ipt := range ib.protos { - if err := ib.addRules(conf, ipt, proto); err != nil { - return err - } - } - return nil -} - -func (ib *iptablesBackend) Del(conf *FirewallNetConf) error { - for proto, ipt := range ib.protos { - if err := ib.delRules(conf, ipt, proto); err != nil { - logrus.Errorf("failed to delete iptables backend rule %s", conf.IptablesAdminChainName) - } - } - return nil -} diff --git a/pkg/systemdgen/systemdgen.go b/pkg/systemdgen/systemdgen.go index 06c5ebde5..09d3c6fd5 100644 --- a/pkg/systemdgen/systemdgen.go +++ b/pkg/systemdgen/systemdgen.go @@ -1,29 +1,59 @@ package systemdgen import ( + "bytes" "fmt" + "io/ioutil" "os" + "path/filepath" + "sort" + "text/template" + "time" + "github.com/containers/libpod/version" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) -var template = `[Unit] -Description=%s Podman Container -[Service] -Restart=%s -ExecStart=%s start %s -ExecStop=%s stop -t %d %s -KillMode=none -Type=forking -PIDFile=%s -[Install] -WantedBy=multi-user.target` +// ContainerInfo contains data required for generating a container's systemd +// unit file. +type ContainerInfo struct { + // ServiceName of the systemd service. + ServiceName string + // Name or ID of the container. + ContainerName string + // InfraContainer of the pod. + InfraContainer string + // StopTimeout sets the timeout Podman waits before killing the container + // during service stop. + StopTimeout int + // RestartPolicy of the systemd unit (e.g., no, on-failure, always). + RestartPolicy string + // PIDFile of the service. Required for forking services. Must point to the + // PID of the associated conmon process. + PIDFile string + // GenerateTimestamp, if set the generated unit file has a time stamp. + GenerateTimestamp bool + // BoundToServices are the services this service binds to. Note that this + // service runs after them. + BoundToServices []string + // RequiredServices are services this service requires. Note that this + // service runs before them. + RequiredServices []string + // PodmanVersion for the header. Will be set internally. Will be auto-filled + // if left empty. + PodmanVersion string + // Executable is the path to the podman executable. Will be auto-filled if + // left empty. + Executable string + // TimeStamp at the time of creating the unit file. Will be set internally. + TimeStamp string +} var restartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"} -// ValidateRestartPolicy checks that the user-provided policy is valid -func ValidateRestartPolicy(restart string) error { +// validateRestartPolicy checks that the user-provided policy is valid. +func validateRestartPolicy(restart string) error { for _, i := range restartPolicies { if i == restart { return nil @@ -32,28 +62,87 @@ func ValidateRestartPolicy(restart string) error { return errors.Errorf("%s is not a valid restart policy", restart) } -// CreateSystemdUnitAsString takes variables to create a systemd unit file used to control -// a libpod container -func CreateSystemdUnitAsString(name, cid, restart, pidFile string, stopTimeout int) (string, error) { - podmanExe := getPodmanExecutable() - return createSystemdUnitAsString(podmanExe, name, cid, restart, pidFile, stopTimeout) -} +const containerTemplate = `# {{.ServiceName}}.service +# autogenerated by Podman {{.PodmanVersion}} +{{- if .TimeStamp}} +# {{.TimeStamp}} +{{- end}} + +[Unit] +Description=Podman {{.ServiceName}}.service +Documentation=man:podman-generate-systemd(1) +{{- if .BoundToServices}} +RefuseManualStart=yes +RefuseManualStop=yes +BindsTo={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} +After={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} +{{- end}} +{{- if .RequiredServices}} +Requires={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} +Before={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} +{{- end}} + +[Service] +Restart={{.RestartPolicy}} +ExecStart={{.Executable}} start {{.ContainerName}} +ExecStop={{.Executable}} stop {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} {{.ContainerName}} +KillMode=none +Type=forking +PIDFile={{.PIDFile}} + +[Install] +WantedBy=multi-user.target` -func createSystemdUnitAsString(exe, name, cid, restart, pidFile string, stopTimeout int) (string, error) { - if err := ValidateRestartPolicy(restart); err != nil { +// CreateContainerSystemdUnit creates a systemd unit file for a container. +func CreateContainerSystemdUnit(info *ContainerInfo, generateFiles bool) (string, error) { + if err := validateRestartPolicy(info.RestartPolicy); err != nil { return "", err } - unit := fmt.Sprintf(template, name, restart, exe, name, exe, stopTimeout, name, pidFile) - return unit, nil -} + // Make sure the executable is set. + if info.Executable == "" { + executable, err := os.Executable() + if err != nil { + executable = "/usr/bin/podman" + logrus.Warnf("Could not obtain podman executable location, using default %s", executable) + } + info.Executable = executable + } + + if info.PodmanVersion == "" { + info.PodmanVersion = version.Version + } + if info.GenerateTimestamp { + info.TimeStamp = fmt.Sprintf("%v", time.Now().Format(time.UnixDate)) + } -func getPodmanExecutable() string { - podmanExe, err := os.Executable() + // Sort the slices to assure a deterministic output. + sort.Strings(info.RequiredServices) + sort.Strings(info.BoundToServices) + + // Generate the template and compile it. + templ, err := template.New("systemd_service_file").Parse(containerTemplate) if err != nil { - podmanExe = "/usr/bin/podman" - logrus.Warnf("Could not obtain podman executable location, using default %s", podmanExe) + return "", errors.Wrap(err, "error parsing systemd service template") } - return podmanExe + var buf bytes.Buffer + if err := templ.Execute(&buf, info); err != nil { + return "", err + } + + if !generateFiles { + 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 } diff --git a/pkg/systemdgen/systemdgen_test.go b/pkg/systemdgen/systemdgen_test.go index e413b24ce..1ddb0c514 100644 --- a/pkg/systemdgen/systemdgen_test.go +++ b/pkg/systemdgen/systemdgen_test.go @@ -5,36 +5,41 @@ import ( ) func TestValidateRestartPolicy(t *testing.T) { - type args struct { + type ContainerInfo struct { restart string } tests := []struct { - name string - args args - wantErr bool + name string + ContainerInfo ContainerInfo + wantErr bool }{ - {"good-on", args{restart: "no"}, false}, - {"good-on-success", args{restart: "on-success"}, false}, - {"good-on-failure", args{restart: "on-failure"}, false}, - {"good-on-abnormal", args{restart: "on-abnormal"}, false}, - {"good-on-watchdog", args{restart: "on-watchdog"}, false}, - {"good-on-abort", args{restart: "on-abort"}, false}, - {"good-always", args{restart: "always"}, false}, - {"fail", args{restart: "foobar"}, true}, - {"failblank", args{restart: ""}, true}, + {"good-on", ContainerInfo{restart: "no"}, false}, + {"good-on-success", ContainerInfo{restart: "on-success"}, false}, + {"good-on-failure", ContainerInfo{restart: "on-failure"}, false}, + {"good-on-abnormal", ContainerInfo{restart: "on-abnormal"}, false}, + {"good-on-watchdog", ContainerInfo{restart: "on-watchdog"}, false}, + {"good-on-abort", ContainerInfo{restart: "on-abort"}, false}, + {"good-always", ContainerInfo{restart: "always"}, false}, + {"fail", ContainerInfo{restart: "foobar"}, true}, + {"failblank", ContainerInfo{restart: ""}, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := ValidateRestartPolicy(tt.args.restart); (err != nil) != tt.wantErr { + if err := validateRestartPolicy(tt.ContainerInfo.restart); (err != nil) != tt.wantErr { t.Errorf("ValidateRestartPolicy() error = %v, wantErr %v", err, tt.wantErr) } }) } } -func TestCreateSystemdUnitAsString(t *testing.T) { - goodID := `[Unit] -Description=639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 Podman Container +func TestCreateContainerSystemdUnit(t *testing.T) { + goodID := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service +# autogenerated by Podman CI + +[Unit] +Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service +Documentation=man:podman-generate-systemd(1) + [Service] Restart=always ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 @@ -42,11 +47,17 @@ ExecStop=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6 KillMode=none Type=forking PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid + [Install] WantedBy=multi-user.target` - goodName := `[Unit] -Description=foobar Podman Container + goodName := `# container-foobar.service +# autogenerated by Podman CI + +[Unit] +Description=Podman container-foobar.service +Documentation=man:podman-generate-systemd(1) + [Service] Restart=always ExecStart=/usr/bin/podman start foobar @@ -54,56 +65,121 @@ ExecStop=/usr/bin/podman stop -t 10 foobar KillMode=none Type=forking PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid + +[Install] +WantedBy=multi-user.target` + + goodNameBoundTo := `# container-foobar.service +# autogenerated by Podman CI + +[Unit] +Description=Podman container-foobar.service +Documentation=man:podman-generate-systemd(1) +RefuseManualStart=yes +RefuseManualStop=yes +BindsTo=a.service b.service c.service pod.service +After=a.service b.service c.service pod.service + +[Service] +Restart=always +ExecStart=/usr/bin/podman start foobar +ExecStop=/usr/bin/podman stop -t 10 foobar +KillMode=none +Type=forking +PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid + +[Install] +WantedBy=multi-user.target` + + podGoodName := `# pod-123abc.service +# autogenerated by Podman CI + +[Unit] +Description=Podman pod-123abc.service +Documentation=man:podman-generate-systemd(1) +Requires=container-1.service container-2.service +Before=container-1.service container-2.service + +[Service] +Restart=always +ExecStart=/usr/bin/podman start jadda-jadda-infra +ExecStop=/usr/bin/podman stop -t 10 jadda-jadda-infra +KillMode=none +Type=forking +PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid + [Install] WantedBy=multi-user.target` - type args struct { - exe string - name string - cid string - restart string - pidFile string - stopTimeout int - } tests := []struct { name string - args args + info ContainerInfo want string wantErr bool }{ {"good with id", - args{ - "/usr/bin/podman", - "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - "always", - "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - 10, + ContainerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + ContainerName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", }, goodID, false, }, {"good with name", - args{ - "/usr/bin/podman", - "foobar", - "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - "always", - "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - 10, + ContainerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "container-foobar", + ContainerName: "foobar", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", }, goodName, false, }, + {"good with name and bound to", + ContainerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "container-foobar", + ContainerName: "foobar", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + BoundToServices: []string{"pod", "a", "b", "c"}, + }, + goodNameBoundTo, + false, + }, + {"pod", + ContainerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "pod-123abc", + ContainerName: "jadda-jadda-infra", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + RequiredServices: []string{"container-1", "container-2"}, + }, + podGoodName, + false, + }, {"bad restart policy", - args{ - "/usr/bin/podman", - "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - "never", - "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - 10, + ContainerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + RestartPolicy: "never", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", }, "", true, @@ -111,13 +187,13 @@ WantedBy=multi-user.target` } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := createSystemdUnitAsString(tt.args.exe, tt.args.name, tt.args.cid, tt.args.restart, tt.args.pidFile, tt.args.stopTimeout) + got, err := CreateContainerSystemdUnit(&tt.info, false) if (err != nil) != tt.wantErr { - t.Errorf("CreateSystemdUnitAsString() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("CreateContainerSystemdUnit() error = \n%v, wantErr \n%v", err, tt.wantErr) return } if got != tt.want { - t.Errorf("CreateSystemdUnitAsString() = %v, want %v", got, tt.want) + t.Errorf("CreateContainerSystemdUnit() = \n%v, want \n%v", got, tt.want) } }) } diff --git a/pkg/util/utils_supported.go b/pkg/util/utils_supported.go index c7c8787a0..707e193e9 100644 --- a/pkg/util/utils_supported.go +++ b/pkg/util/utils_supported.go @@ -16,8 +16,8 @@ import ( "github.com/sirupsen/logrus" ) -// GetRootlessRuntimeDir returns the runtime directory when running as non root -func GetRootlessRuntimeDir() (string, error) { +// GetRuntimeDir returns the runtime directory +func GetRuntimeDir() (string, error) { var rootlessRuntimeDirError error rootlessRuntimeDirOnce.Do(func() { @@ -100,7 +100,7 @@ func GetRootlessConfigHomeDir() (string, error) { // GetRootlessPauseProcessPidPath returns the path to the file that holds the pid for // the pause process func GetRootlessPauseProcessPidPath() (string, error) { - runtimeDir, err := GetRootlessRuntimeDir() + runtimeDir, err := GetRuntimeDir() if err != nil { return "", err } diff --git a/pkg/util/utils_windows.go b/pkg/util/utils_windows.go index e781e6717..9bba2d1ee 100644 --- a/pkg/util/utils_windows.go +++ b/pkg/util/utils_windows.go @@ -25,12 +25,12 @@ func GetRootlessPauseProcessPidPath() (string, error) { return "", errors.Wrap(errNotImplemented, "GetRootlessPauseProcessPidPath") } -// GetRootlessRuntimeDir returns the runtime directory when running as non root -func GetRootlessRuntimeDir() (string, error) { - return "", errors.Wrap(errNotImplemented, "GetRootlessRuntimeDir") +// GetRuntimeDir returns the runtime directory +func GetRuntimeDir() (string, error) { + return "", errors.New("this function is not implemented for windows") } // GetRootlessConfigHomeDir returns the config home directory when running as non root func GetRootlessConfigHomeDir() (string, error) { - return "", errors.Wrap(errNotImplemented, "GetRootlessConfigHomeDir") + return "", errors.New("this function is not implemented for windows") } diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go index c7aa5233f..2dcdbc089 100644 --- a/pkg/varlinkapi/containers.go +++ b/pkg/varlinkapi/containers.go @@ -14,7 +14,7 @@ import ( "time" "github.com/containers/libpod/cmd/podman/shared" - "github.com/containers/libpod/cmd/podman/varlink" + iopodman "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/logs" @@ -864,3 +864,16 @@ func (i *LibpodAPI) ExecContainer(call iopodman.VarlinkCall, opts iopodman.ExecO return ecErr.Error } + +//HealthCheckRun executes defined container's healthcheck command and returns the container's health status. +func (i *LibpodAPI) HealthCheckRun(call iopodman.VarlinkCall, nameOrID string) error { + hcStatus, err := i.Runtime.HealthCheck(nameOrID) + if err != nil && hcStatus != libpod.HealthCheckFailure { + return call.ReplyErrorOccurred(err.Error()) + } + status := libpod.HealthCheckUnhealthy + if hcStatus == libpod.HealthCheckSuccess { + status = libpod.HealthCheckHealthy + } + return call.ReplyHealthCheckRun(status) +} diff --git a/pkg/varlinkapi/generate.go b/pkg/varlinkapi/generate.go index 9dc20d582..19010097d 100644 --- a/pkg/varlinkapi/generate.go +++ b/pkg/varlinkapi/generate.go @@ -4,9 +4,9 @@ package varlinkapi import ( "encoding/json" + "github.com/containers/libpod/cmd/podman/shared" iopodman "github.com/containers/libpod/cmd/podman/varlink" - "github.com/containers/libpod/pkg/systemdgen" ) // GenerateKube ... @@ -29,24 +29,3 @@ func (i *LibpodAPI) GenerateKube(call iopodman.VarlinkCall, name string, service Service: string(servB), }) } - -// GenerateSystemd ... -func (i *LibpodAPI) GenerateSystemd(call iopodman.VarlinkCall, nameOrID, restart string, stopTimeout int64, useName bool) error { - ctr, err := i.Runtime.LookupContainer(nameOrID) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - timeout := int(ctr.StopTimeout()) - if stopTimeout >= 0 { - timeout = int(stopTimeout) - } - name := ctr.ID() - if useName { - name = ctr.Name() - } - unit, err := systemdgen.CreateSystemdUnitAsString(name, ctr.ID(), restart, ctr.Config().StaticDir, timeout) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - return call.ReplyGenerateSystemd(unit) -} diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index fe7f11b4d..c184155a9 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -142,7 +142,14 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI return call.ReplyErrorOccurred(fmt.Sprintf("unable to untar context dir %s", contextDir)) } logrus.Debugf("untar of %s successful", contextDir) - + defer func() { + if err := os.Remove(contextDir); err != nil { + logrus.Errorf("unable to delete file '%s': %q", contextDir, err) + } + if err := os.RemoveAll(newContextDir); err != nil { + logrus.Errorf("unable to delete directory '%s': %q", newContextDir, err) + } + }() // All output (stdout, stderr) is captured in output as well var output bytes.Buffer @@ -331,7 +338,7 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, compr dockerRegistryOptions := image.DockerRegistryOptions{} if format != "" { switch format { - case "oci": //nolint + case "oci": // nolint manifestType = v1.MediaTypeImageManifest case "v2s1": manifestType = manifest.DockerV2Schema1SignedMediaType @@ -353,7 +360,12 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, compr output := bytes.NewBuffer([]byte{}) c := make(chan error) go func() { - err := newImage.PushImageToHeuristicDestination(getContext(), destname, manifestType, "", "", output, compress, so, &dockerRegistryOptions, nil) + writer := bytes.NewBuffer([]byte{}) + err := newImage.PushImageToHeuristicDestination(getContext(), destname, manifestType, "", "", "", writer, compress, so, &dockerRegistryOptions, nil) + if err != nil { + c <- err + } + _, err = io.CopyBuffer(output, writer, nil) c <- err close(c) }() @@ -381,6 +393,7 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, compr } br := iopodman.MoreResponse{ Logs: log, + Id: newImage.ID(), } call.ReplyPushImage(br) log = []string{} @@ -396,6 +409,7 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, compr br := iopodman.MoreResponse{ Logs: log, + Id: newImage.ID(), } return call.ReplyPushImage(br) } @@ -523,7 +537,7 @@ func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, ch } sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false) switch manifestType { - case "oci", "": //nolint + case "oci", "": // nolint mimeType = buildah.OCIv1ImageManifest case "docker": mimeType = manifest.DockerV2Schema2MediaType @@ -615,7 +629,7 @@ func (i *LibpodAPI) ExportImage(call iopodman.VarlinkCall, name, destination str return err } - if err := newImage.PushImageToHeuristicDestination(getContext(), destination, "", "", "", nil, compress, image.SigningOptions{}, &image.DockerRegistryOptions{}, additionalTags); err != nil { + if err := newImage.PushImageToHeuristicDestination(getContext(), destination, "", "", "", "", nil, compress, image.SigningOptions{}, &image.DockerRegistryOptions{}, additionalTags); err != nil { return call.ReplyErrorOccurred(err.Error()) } return call.ReplyExportImage(newImage.ID()) @@ -814,7 +828,7 @@ func (i *LibpodAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.ImageS // Image has been saved to `output` if outputToDir { // If the output is a directory, we need to tar up the directory to send it back - //Create a tempfile for the directory tarball + // Create a tempfile for the directory tarball outputFile, err := ioutil.TempFile("", "varlink_save_dir") if err != nil { return err diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go index 19ba38e7c..6dd86d831 100644 --- a/pkg/varlinkapi/volumes.go +++ b/pkg/varlinkapi/volumes.go @@ -68,7 +68,6 @@ func (i *LibpodAPI) GetVolumes(call iopodman.VarlinkCall, args []string, all boo MountPoint: v.MountPoint(), Name: v.Name(), Options: v.Options(), - Scope: v.Scope(), } volumes = append(volumes, newVol) } |