diff options
Diffstat (limited to 'pkg/domain/infra')
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 100 | ||||
-rw-r--r-- | pkg/domain/infra/abi/cp.go | 6 | ||||
-rw-r--r-- | pkg/domain/infra/abi/images.go | 10 | ||||
-rw-r--r-- | pkg/domain/infra/abi/manifest.go | 23 | ||||
-rw-r--r-- | pkg/domain/infra/abi/network.go | 17 | ||||
-rw-r--r-- | pkg/domain/infra/abi/play.go | 558 | ||||
-rw-r--r-- | pkg/domain/infra/abi/play_test.go | 169 | ||||
-rw-r--r-- | pkg/domain/infra/abi/pods.go | 17 | ||||
-rw-r--r-- | pkg/domain/infra/abi/system.go | 21 | ||||
-rw-r--r-- | pkg/domain/infra/abi/terminal/sigproxy_linux.go | 8 | ||||
-rw-r--r-- | pkg/domain/infra/runtime_libpod.go | 24 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/containers.go | 25 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/network.go | 10 |
13 files changed, 242 insertions, 746 deletions
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 98b886845..ff4277a2e 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -205,15 +205,13 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin } func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) (*entities.ContainerPruneReport, error) { - var filterFuncs []libpod.ContainerFilter + filterFuncs := make([]libpod.ContainerFilter, 0, len(options.Filters)) for k, v := range options.Filters { - for _, val := range v { - generatedFunc, err := lpfilters.GenerateContainerFilterFuncs(k, val, ic.Libpod) - if err != nil { - return nil, err - } - filterFuncs = append(filterFuncs, generatedFunc) + generatedFunc, err := lpfilters.GenerateContainerFilterFuncs(k, v, ic.Libpod) + if err != nil { + return nil, err } + filterFuncs = append(filterFuncs, generatedFunc) } return ic.pruneContainersHelper(filterFuncs) } @@ -913,7 +911,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } else { report.ExitCode = int(ecode) } - if opts.Rm { + if opts.Rm && !ctr.ShouldRestart(ctx) { if err := ic.Libpod.RemoveContainer(ctx, ctr, false, true); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == define.ErrCtrRemoved { @@ -994,7 +992,7 @@ func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []st return []*entities.ContainerCleanupReport{}, nil } - if options.Remove { + if options.Remove && !ctr.ShouldRestart(ctx) { err = ic.Libpod.RemoveContainer(ctx, ctr, false, true) if err != nil { report.RmErr = errors.Wrapf(err, "failed to cleanup and remove container %v", ctr.ID()) @@ -1017,6 +1015,7 @@ func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []st _, err = ic.Libpod.RemoveImage(ctx, ctrImage, false) report.RmiErr = err } + reports = append(reports, &report) } return reports, nil @@ -1058,11 +1057,23 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []strin os.Exit(ret) } } - ctrs, err := getContainersByContext(options.All, options.Latest, nameOrIDs, ic.Libpod) + reports := []*entities.ContainerMountReport{} + // Attempt to mount named containers directly from storage, + // this will fail and code will fall through to removing the container from libpod.` + names := []string{} + for _, ctr := range nameOrIDs { + report := entities.ContainerMountReport{Id: ctr} + if report.Path, report.Err = ic.Libpod.MountStorageContainer(ctr); report.Err != nil { + names = append(names, ctr) + } else { + reports = append(reports, &report) + } + } + + ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod) if err != nil { return nil, err } - reports := make([]*entities.ContainerMountReport, 0, len(ctrs)) for _, ctr := range ctrs { report := entities.ContainerMountReport{Id: ctr.ID()} report.Path, report.Err = ctr.Mount() @@ -1072,6 +1083,30 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []strin return reports, nil } + storageCtrs, err := ic.Libpod.StorageContainers() + if err != nil { + return nil, err + } + + for _, sctr := range storageCtrs { + mounted, path, err := ic.Libpod.IsStorageContainerMounted(sctr.ID) + if err != nil { + return nil, err + } + + var name string + if len(sctr.Names) > 0 { + name = sctr.Names[0] + } + if mounted { + reports = append(reports, &entities.ContainerMountReport{ + Id: sctr.ID, + Name: name, + Path: path, + }) + } + } + // No containers were passed, so we send back what is mounted ctrs, err = getContainersByContext(true, false, []string{}, ic.Libpod) if err != nil { @@ -1091,15 +1126,44 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []strin }) } } + return reports, nil } func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIDs []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) { - ctrs, err := getContainersByContext(options.All, options.Latest, nameOrIDs, ic.Libpod) + reports := []*entities.ContainerUnmountReport{} + names := []string{} + if options.All { + storageCtrs, err := ic.Libpod.StorageContainers() + if err != nil { + return nil, err + } + for _, sctr := range storageCtrs { + mounted, _, _ := ic.Libpod.IsStorageContainerMounted(sctr.ID) + if mounted { + report := entities.ContainerUnmountReport{Id: sctr.ID} + if _, report.Err = ic.Libpod.UnmountStorageContainer(sctr.ID, options.Force); report.Err != nil { + if errors.Cause(report.Err) != define.ErrCtrExists { + reports = append(reports, &report) + } + } else { + reports = append(reports, &report) + } + } + } + } + for _, ctr := range nameOrIDs { + report := entities.ContainerUnmountReport{Id: ctr} + if _, report.Err = ic.Libpod.UnmountStorageContainer(ctr, options.Force); report.Err != nil { + names = append(names, ctr) + } else { + reports = append(reports, &report) + } + } + ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod) if err != nil { return nil, err } - reports := []*entities.ContainerUnmountReport{} for _, ctr := range ctrs { state, err := ctr.State() if err != nil { @@ -1251,3 +1315,13 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri return statsChan, nil } + +// ShouldRestart returns whether the container should be restarted +func (ic *ContainerEngine) ShouldRestart(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { + ctr, err := ic.Libpod.LookupContainer(nameOrID) + if err != nil { + return nil, err + } + + return &entities.BoolReport{Value: ctr.ShouldRestart(ctx)}, nil +} diff --git a/pkg/domain/infra/abi/cp.go b/pkg/domain/infra/abi/cp.go index ab90c8183..8f4f5d3d7 100644 --- a/pkg/domain/infra/abi/cp.go +++ b/pkg/domain/infra/abi/cp.go @@ -214,7 +214,7 @@ func getPathInfo(path string) (string, os.FileInfo, error) { } srcfi, err := os.Stat(path) if err != nil { - return "", nil, errors.Wrapf(err, "error reading path %q", path) + return "", nil, err } return path, srcfi, nil } @@ -245,7 +245,7 @@ func containerCopy(srcPath, destPath, src, dest string, idMappingOpts storage.ID } _, err = os.Stat(destdir) if err != nil && !os.IsNotExist(err) { - return errors.Wrapf(err, "error checking directory %q", destdir) + return err } destDirIsExist := err == nil if err = os.MkdirAll(destdir, 0755); err != nil { @@ -292,7 +292,7 @@ func containerCopy(srcPath, destPath, src, dest string, idMappingOpts storage.ID destfi, err := os.Stat(destPath) if err != nil { if !os.IsNotExist(err) || strings.HasSuffix(dest, string(os.PathSeparator)) { - return errors.Wrapf(err, "failed to get stat of dest path %s", destPath) + return err } } if destfi != nil && destfi.IsDir() { diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 25335cf11..ef0e15264 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -39,8 +39,14 @@ const SignatureStoreDir = "/var/lib/containers/sigstore" func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.BoolReport, error) { _, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) - if err != nil && errors.Cause(err) != define.ErrNoSuchImage { - return nil, err + if err != nil { + if errors.Cause(err) == define.ErrMultipleImages { + return &entities.BoolReport{Value: true}, nil + } else { + if errors.Cause(err) != define.ErrNoSuchImage { + return nil, err + } + } } return &entities.BoolReport{Value: err == nil}, nil } diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index 6c518e678..ad7128b42 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -25,6 +25,7 @@ import ( "github.com/containers/podman/v2/pkg/domain/entities" "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/sirupsen/logrus" "github.com/pkg/errors" ) @@ -90,10 +91,6 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte continue } - if !manifest.MIMETypeIsMultiImage(manifestType) { - appendErr(errors.Errorf("manifest is of type %s (not a list type)", manifestType)) - continue - } result = manifestBytes manType = manifestType break @@ -101,7 +98,18 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte if len(result) == 0 && latestErr != nil { return nil, latestErr } - if manType != manifest.DockerV2ListMediaType { + + switch manType { + case manifest.DockerV2Schema2MediaType: + logrus.Warnf("Warning! The manifest type %s is not a manifest list but a single image.", manType) + schema2Manifest, err := manifest.Schema2FromManifest(result) + if err != nil { + return nil, errors.Wrapf(err, "error parsing manifest blob %q as a %q", string(result), manType) + } + if result, err = schema2Manifest.Serialize(); err != nil { + return nil, err + } + default: listBlob, err := manifest.ListFromBlob(result, manType) if err != nil { return nil, errors.Wrapf(err, "error parsing manifest blob %q as a %q", string(result), manType) @@ -113,10 +121,9 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte if result, err = list.Serialize(); err != nil { return nil, err } - } - err = json.Indent(&b, result, "", " ") - if err != nil { + + if err = json.Indent(&b, result, "", " "); err != nil { return nil, errors.Wrapf(err, "error rendering manifest %s for display", name) } return b.Bytes(), nil diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go index 4f572fb88..c52584565 100644 --- a/pkg/domain/infra/abi/network.go +++ b/pkg/domain/infra/abi/network.go @@ -96,7 +96,7 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o if err := ic.Libpod.RemovePod(ctx, pod, true, true); err != nil { return reports, err } - } else if err := ic.Libpod.RemoveContainer(ctx, c, true, true); err != nil { + } else if err := ic.Libpod.RemoveContainer(ctx, c, true, true); err != nil && errors.Cause(err) != define.ErrNoSuchCtr { return reports, err } } @@ -110,7 +110,11 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o } func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) { - return network.Create(name, options, ic.Libpod) + runtimeConfig, err := ic.Libpod.GetConfig() + if err != nil { + return nil, err + } + return network.Create(name, options, runtimeConfig) } func ifPassesFilterTest(netconf *libcni.NetworkConfigList, filter []string) bool { @@ -134,3 +138,12 @@ func ifPassesFilterTest(netconf *libcni.NetworkConfigList, filter []string) bool } return result } + +// NetworkDisconnect removes a container from a given network +func (ic *ContainerEngine) NetworkDisconnect(ctx context.Context, networkname string, options entities.NetworkDisconnectOptions) error { + return ic.Libpod.DisconnectContainerFromNetwork(options.Container, networkname, options.Force) +} + +func (ic *ContainerEngine) NetworkConnect(ctx context.Context, networkname string, options entities.NetworkConnectOptions) error { + return ic.Libpod.ConnectContainerToNetwork(options.Container, networkname, options.Aliases) +} diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 348570a20..3aeb6a2ee 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -6,38 +6,21 @@ import ( "io" "io/ioutil" "os" - "path/filepath" "strings" - "github.com/containers/buildah/pkg/parse" "github.com/containers/image/v5/types" "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/image" - ann "github.com/containers/podman/v2/pkg/annotations" "github.com/containers/podman/v2/pkg/domain/entities" - envLib "github.com/containers/podman/v2/pkg/env" - ns "github.com/containers/podman/v2/pkg/namespaces" - createconfig "github.com/containers/podman/v2/pkg/spec" "github.com/containers/podman/v2/pkg/specgen/generate" + "github.com/containers/podman/v2/pkg/specgen/generate/kube" "github.com/containers/podman/v2/pkg/util" - "github.com/containers/storage" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/distribution/reference" "github.com/ghodss/yaml" "github.com/pkg/errors" "github.com/sirupsen/logrus" v1apps "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" -) - -const ( - // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath - kubeDirectoryPermission = 0755 - // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath - kubeFilePermission = 0644 - // Kubernetes sets CPUPeriod to 100000us (100ms): https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/ - defaultCPUPeriod = 100000 ) func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { @@ -112,7 +95,6 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { var ( - pod *libpod.Pod registryCreds *types.DockerAuthConfig writer io.Writer playKubePod entities.PlayKubePod @@ -131,49 +113,10 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } } - podOptions := []libpod.PodCreateOption{ - libpod.WithInfraContainer(), - libpod.WithPodName(podName), - } - - if podYAML.ObjectMeta.Labels != nil { - podOptions = append(podOptions, libpod.WithPodLabels(podYAML.ObjectMeta.Labels)) - } - - // TODO we only configure Process namespace. We also need to account for Host{IPC,Network,PID} - // which is not currently possible with pod create - if podYAML.Spec.ShareProcessNamespace != nil && *podYAML.Spec.ShareProcessNamespace { - podOptions = append(podOptions, libpod.WithPodPID()) - } - - hostname := podYAML.Spec.Hostname - if hostname == "" { - hostname = podName - } - podOptions = append(podOptions, libpod.WithPodHostname(hostname)) - - if podYAML.Spec.HostNetwork { - podOptions = append(podOptions, libpod.WithPodHostNetwork()) - } - - if podYAML.Spec.HostAliases != nil { - hosts := make([]string, 0, len(podYAML.Spec.HostAliases)) - for _, hostAlias := range podYAML.Spec.HostAliases { - for _, host := range hostAlias.Hostnames { - hosts = append(hosts, host+":"+hostAlias.IP) - } - } - podOptions = append(podOptions, libpod.WithPodHosts(hosts)) - } - - nsOptions, err := generate.GetNamespaceOptions(strings.Split(createconfig.DefaultKernelNamespaces, ",")) + p, err := kube.ToPodGen(ctx, podName, podYAML) if err != nil { return nil, err } - podOptions = append(podOptions, nsOptions...) - podPorts := getPodPorts(podYAML.Spec.Containers) - podOptions = append(podOptions, libpod.WithInfraContainerPorts(podPorts)) - if options.Network != "" { switch strings.ToLower(options.Network) { case "bridge", "host": @@ -185,12 +128,12 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY // networks. networks := strings.Split(options.Network, ",") logrus.Debugf("Pod joining CNI networks: %v", networks) - podOptions = append(podOptions, libpod.WithPodNetworks(networks)) + p.CNINetworks = append(p.CNINetworks, networks...) } } // Create the Pod - pod, err = ic.Libpod.NewPod(ctx, podOptions...) + pod, err := generate.MakePod(p, ic.Libpod) if err != nil { return nil, err } @@ -199,29 +142,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY if err != nil { return nil, err } - hasUserns := false - if podInfraID != "" { - podCtr, err := ic.Libpod.GetContainer(podInfraID) - if err != nil { - return nil, err - } - mappings, err := podCtr.IDMappings() - if err != nil { - return nil, err - } - hasUserns = len(mappings.UIDMap) > 0 - } - namespaces := map[string]string{ - // Disabled during code review per mheon - //"pid": fmt.Sprintf("container:%s", podInfraID), - "net": fmt.Sprintf("container:%s", podInfraID), - "ipc": fmt.Sprintf("container:%s", podInfraID), - "uts": fmt.Sprintf("container:%s", podInfraID), - } - if hasUserns { - namespaces["user"] = fmt.Sprintf("container:%s", podInfraID) - } if !options.Quiet { writer = os.Stderr } @@ -239,65 +160,12 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY DockerInsecureSkipTLSVerify: options.SkipTLSVerify, } - // map from name to mount point - volumes := make(map[string]string) - for _, volume := range podYAML.Spec.Volumes { - hostPath := volume.VolumeSource.HostPath - if hostPath == nil { - return nil, errors.Errorf("HostPath is currently the only supported VolumeSource") - } - if hostPath.Type != nil { - switch *hostPath.Type { - case v1.HostPathDirectoryOrCreate: - if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { - if err := os.Mkdir(hostPath.Path, kubeDirectoryPermission); err != nil { - return nil, errors.Errorf("error creating HostPath %s", volume.Name) - } - } - // Label a newly created volume - if err := libpod.LabelVolumePath(hostPath.Path); err != nil { - return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path) - } - case v1.HostPathFileOrCreate: - if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { - f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, kubeFilePermission) - if err != nil { - return nil, errors.Errorf("error creating HostPath %s", volume.Name) - } - if err := f.Close(); err != nil { - logrus.Warnf("Error in closing newly created HostPath file: %v", err) - } - } - // unconditionally label a newly created volume - if err := libpod.LabelVolumePath(hostPath.Path); err != nil { - return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path) - } - case v1.HostPathSocket: - st, err := os.Stat(hostPath.Path) - if err != nil { - return nil, errors.Wrap(err, "error checking HostPathSocket") - } - if st.Mode()&os.ModeSocket != os.ModeSocket { - return nil, errors.Errorf("error checking HostPathSocket: path %s is not a socket", hostPath.Path) - } - - case v1.HostPathDirectory: - case v1.HostPathFile: - case v1.HostPathUnset: - // do nothing here because we will verify the path exists in validateVolumeHostDir - break - default: - return nil, errors.Errorf("Invalid HostPath type %v", hostPath.Type) - } - } - - if err := parse.ValidateVolumeHostDir(hostPath.Path); err != nil { - return nil, errors.Wrapf(err, "error in parsing HostPath in YAML") - } - volumes[volume.Name] = hostPath.Path + volumes, err := kube.InitializeVolumes(podYAML.Spec.Volumes) + if err != nil { + return nil, err } - seccompPaths, err := initializeSeccompPaths(podYAML.ObjectMeta.Annotations, options.SeccompProfileRoot) + seccompPaths, err := kube.InitializeSeccompPaths(podYAML.ObjectMeta.Annotations, options.SeccompProfileRoot) if err != nil { return nil, err } @@ -349,29 +217,42 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY pullPolicy = util.PullImageAlways } } + + // This ensures the image is the image store newImage, err := ic.Libpod.ImageRuntime().New(ctx, container.Image, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullPolicy) if err != nil { return nil, err } - conf, err := kubeContainerToCreateConfig(ctx, container, newImage, namespaces, volumes, pod.ID(), podName, podInfraID, configMaps, seccompPaths) + + specGen, err := kube.ToSpecGen(ctx, container, container.Image, newImage, volumes, pod.ID(), podName, podInfraID, configMaps, seccompPaths, ctrRestartPolicy) if err != nil { return nil, err } - conf.RestartPolicy = ctrRestartPolicy - ctr, err := createconfig.CreateContainerFromCreateConfig(ctx, ic.Libpod, conf, pod) + + ctr, err := generate.MakeContainer(ctx, ic.Libpod, specGen) if err != nil { return nil, err } containers = append(containers, ctr) } - // start the containers - for _, ctr := range containers { - if err := ctr.Start(ctx, true); err != nil { - // Making this a hard failure here to avoid a mess - // the other containers are in created status + if options.Start != types.OptionalBoolFalse { + //start the containers + podStartErrors, err := pod.Start(ctx) + if err != nil { return nil, err } + + // Previous versions of playkube started containers individually and then + // looked for errors. Because we now use the uber-Pod start call, we should + // iterate the map of possible errors and return one if there is a problem. This + // keeps the behavior the same + + for _, e := range podStartErrors { + if e != nil { + return nil, e + } + } } playKubePod.ID = pod.ID() @@ -384,265 +265,6 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY return &report, nil } -// getPodPorts converts a slice of kube container descriptions to an -// array of ocicni portmapping descriptions usable in libpod -func getPodPorts(containers []v1.Container) []ocicni.PortMapping { - var infraPorts []ocicni.PortMapping - for _, container := range containers { - for _, p := range container.Ports { - if p.HostPort != 0 && p.ContainerPort == 0 { - p.ContainerPort = p.HostPort - } - if p.Protocol == "" { - p.Protocol = "tcp" - } - portBinding := ocicni.PortMapping{ - HostPort: p.HostPort, - ContainerPort: p.ContainerPort, - Protocol: strings.ToLower(string(p.Protocol)), - HostIP: p.HostIP, - } - // only hostPort is utilized in podman context, all container ports - // are accessible inside the shared network namespace - if p.HostPort != 0 { - infraPorts = append(infraPorts, portBinding) - } - - } - } - return infraPorts -} - -func setupSecurityContext(securityConfig *createconfig.SecurityConfig, userConfig *createconfig.UserConfig, containerYAML v1.Container) { - if containerYAML.SecurityContext == nil { - return - } - if containerYAML.SecurityContext.ReadOnlyRootFilesystem != nil { - securityConfig.ReadOnlyRootfs = *containerYAML.SecurityContext.ReadOnlyRootFilesystem - } - if containerYAML.SecurityContext.Privileged != nil { - securityConfig.Privileged = *containerYAML.SecurityContext.Privileged - } - - if containerYAML.SecurityContext.AllowPrivilegeEscalation != nil { - securityConfig.NoNewPrivs = !*containerYAML.SecurityContext.AllowPrivilegeEscalation - } - - if seopt := containerYAML.SecurityContext.SELinuxOptions; seopt != nil { - if seopt.User != "" { - securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=user:%s", seopt.User)) - securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("user:%s", seopt.User)) - } - if seopt.Role != "" { - securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=role:%s", seopt.Role)) - securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("role:%s", seopt.Role)) - } - if seopt.Type != "" { - securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=type:%s", seopt.Type)) - securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("type:%s", seopt.Type)) - } - if seopt.Level != "" { - securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=level:%s", seopt.Level)) - securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("level:%s", seopt.Level)) - } - } - if caps := containerYAML.SecurityContext.Capabilities; caps != nil { - for _, capability := range caps.Add { - securityConfig.CapAdd = append(securityConfig.CapAdd, string(capability)) - } - for _, capability := range caps.Drop { - securityConfig.CapDrop = append(securityConfig.CapDrop, string(capability)) - } - } - if containerYAML.SecurityContext.RunAsUser != nil { - userConfig.User = fmt.Sprintf("%d", *containerYAML.SecurityContext.RunAsUser) - } - if containerYAML.SecurityContext.RunAsGroup != nil { - if userConfig.User == "" { - userConfig.User = "0" - } - userConfig.User = fmt.Sprintf("%s:%d", userConfig.User, *containerYAML.SecurityContext.RunAsGroup) - } -} - -// kubeContainerToCreateConfig takes a v1.Container and returns a createconfig describing a container -func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, newImage *image.Image, namespaces map[string]string, volumes map[string]string, podID, podName, infraID string, configMaps []v1.ConfigMap, seccompPaths *kubeSeccompPaths) (*createconfig.CreateConfig, error) { - var ( - containerConfig createconfig.CreateConfig - pidConfig createconfig.PidConfig - networkConfig createconfig.NetworkConfig - cgroupConfig createconfig.CgroupConfig - utsConfig createconfig.UtsConfig - ipcConfig createconfig.IpcConfig - userConfig createconfig.UserConfig - securityConfig createconfig.SecurityConfig - ) - - // The default for MemorySwappiness is -1, not 0 - containerConfig.Resources.MemorySwappiness = -1 - - containerConfig.Image = containerYAML.Image - containerConfig.ImageID = newImage.ID() - - // podName should be non-empty for Deployment objects to be able to create - // multiple pods having containers with unique names - if podName == "" { - return nil, errors.Errorf("kubeContainerToCreateConfig got empty podName") - } - containerConfig.Name = fmt.Sprintf("%s-%s", podName, containerYAML.Name) - - containerConfig.Tty = containerYAML.TTY - - containerConfig.Pod = podID - - imageData, _ := newImage.Inspect(ctx) - - userConfig.User = "0" - if imageData != nil { - userConfig.User = imageData.Config.User - } - - setupSecurityContext(&securityConfig, &userConfig, containerYAML) - - // Since we prefix the container name with pod name to work-around the uniqueness requirement, - // the seccom profile should reference the actual container name from the YAML - // but apply to the containers with the prefixed name - securityConfig.SeccompProfilePath = seccompPaths.findForContainer(containerYAML.Name) - - var err error - milliCPU, err := quantityToInt64(containerYAML.Resources.Limits.Cpu()) - if err != nil { - return nil, errors.Wrap(err, "Failed to set CPU quota") - } - if milliCPU > 0 { - containerConfig.Resources.CPUPeriod = defaultCPUPeriod - // CPU quota is a fraction of the period: milliCPU / 1000.0 * period - // Or, without floating point math: - containerConfig.Resources.CPUQuota = milliCPU * defaultCPUPeriod / 1000 - } - - containerConfig.Resources.Memory, err = quantityToInt64(containerYAML.Resources.Limits.Memory()) - if err != nil { - return nil, errors.Wrap(err, "Failed to set memory limit") - } - containerConfig.Resources.MemoryReservation, err = quantityToInt64(containerYAML.Resources.Requests.Memory()) - if err != nil { - return nil, errors.Wrap(err, "Failed to set memory reservation") - } - - containerConfig.Command = []string{} - if imageData != nil && imageData.Config != nil { - containerConfig.Command = imageData.Config.Entrypoint - } - if len(containerYAML.Command) != 0 { - containerConfig.Command = containerYAML.Command - } - // doc https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes - if len(containerYAML.Args) != 0 { - containerConfig.Command = append(containerConfig.Command, containerYAML.Args...) - } else if len(containerYAML.Command) == 0 { - // Add the Cmd from the image config only if containerYAML.Command and containerYAML.Args are empty - containerConfig.Command = append(containerConfig.Command, imageData.Config.Cmd...) - } - if imageData != nil && len(containerConfig.Command) == 0 { - return nil, errors.Errorf("No command specified in container YAML or as CMD or ENTRYPOINT in this image for %s", containerConfig.Name) - } - - containerConfig.UserCommand = containerConfig.Command - - containerConfig.StopSignal = 15 - - containerConfig.WorkDir = "/" - if imageData != nil { - // FIXME, - // we are currently ignoring imageData.Config.ExposedPorts - containerConfig.BuiltinImgVolumes = imageData.Config.Volumes - if imageData.Config.WorkingDir != "" { - containerConfig.WorkDir = imageData.Config.WorkingDir - } - containerConfig.Labels = imageData.Config.Labels - if imageData.Config.StopSignal != "" { - stopSignal, err := util.ParseSignal(imageData.Config.StopSignal) - if err != nil { - return nil, err - } - containerConfig.StopSignal = stopSignal - } - } - - if containerYAML.WorkingDir != "" { - containerConfig.WorkDir = containerYAML.WorkingDir - } - // If the user does not pass in ID mappings, just set to basics - if userConfig.IDMappings == nil { - userConfig.IDMappings = &storage.IDMappingOptions{} - } - - networkConfig.NetMode = ns.NetworkMode(namespaces["net"]) - ipcConfig.IpcMode = ns.IpcMode(namespaces["ipc"]) - utsConfig.UtsMode = ns.UTSMode(namespaces["uts"]) - // disabled in code review per mheon - //containerConfig.PidMode = ns.PidMode(namespaces["pid"]) - userConfig.UsernsMode = ns.UsernsMode(namespaces["user"]) - if len(containerConfig.WorkDir) == 0 { - containerConfig.WorkDir = "/" - } - - containerConfig.Pid = pidConfig - containerConfig.Network = networkConfig - containerConfig.Uts = utsConfig - containerConfig.Ipc = ipcConfig - containerConfig.Cgroup = cgroupConfig - containerConfig.User = userConfig - containerConfig.Security = securityConfig - - annotations := make(map[string]string) - if infraID != "" { - annotations[ann.SandboxID] = infraID - annotations[ann.ContainerType] = ann.ContainerTypeContainer - } - containerConfig.Annotations = annotations - - // Environment Variables - envs := map[string]string{} - if imageData != nil { - imageEnv, err := envLib.ParseSlice(imageData.Config.Env) - if err != nil { - return nil, errors.Wrap(err, "error parsing image environment variables") - } - envs = imageEnv - } - for _, env := range containerYAML.Env { - value := envVarValue(env, configMaps) - - envs[env.Name] = value - } - for _, envFrom := range containerYAML.EnvFrom { - cmEnvs := envVarsFromConfigMap(envFrom, configMaps) - - for k, v := range cmEnvs { - envs[k] = v - } - } - containerConfig.Env = envs - - for _, volume := range containerYAML.VolumeMounts { - var readonly string - hostPath, exists := volumes[volume.Name] - if !exists { - return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name) - } - if err := parse.ValidateVolumeCtrDir(volume.MountPath); err != nil { - return nil, errors.Wrapf(err, "error in parsing MountPath") - } - if volume.ReadOnly { - readonly = ":ro" - } - containerConfig.Volumes = append(containerConfig.Volumes, fmt.Sprintf("%s:%s%s", hostPath, volume.MountPath, readonly)) - } - return &containerConfig, nil -} - // readConfigMapFromFile returns a kubernetes configMap obtained from --configmap flag func readConfigMapFromFile(r io.Reader) (v1.ConfigMap, error) { var cm v1.ConfigMap @@ -662,125 +284,3 @@ func readConfigMapFromFile(r io.Reader) (v1.ConfigMap, error) { return cm, nil } - -// envVarsFromConfigMap returns all key-value pairs as env vars from a configMap that matches the envFrom setting of a container -func envVarsFromConfigMap(envFrom v1.EnvFromSource, configMaps []v1.ConfigMap) map[string]string { - envs := map[string]string{} - - if envFrom.ConfigMapRef != nil { - cmName := envFrom.ConfigMapRef.Name - - for _, c := range configMaps { - if cmName == c.Name { - envs = c.Data - break - } - } - } - - return envs -} - -// envVarValue returns the environment variable value configured within the container's env setting. -// It gets the value from a configMap if specified, otherwise returns env.Value -func envVarValue(env v1.EnvVar, configMaps []v1.ConfigMap) string { - for _, c := range configMaps { - if env.ValueFrom != nil { - if env.ValueFrom.ConfigMapKeyRef != nil { - if env.ValueFrom.ConfigMapKeyRef.Name == c.Name { - if value, ok := c.Data[env.ValueFrom.ConfigMapKeyRef.Key]; ok { - return value - } - } - } - } - } - - return env.Value -} - -// kubeSeccompPaths holds information about a pod YAML's seccomp configuration -// it holds both container and pod seccomp paths -type kubeSeccompPaths struct { - containerPaths map[string]string - podPath string -} - -// findForContainer checks whether a container has a seccomp path configured for it -// if not, it returns the podPath, which should always have a value -func (k *kubeSeccompPaths) findForContainer(ctrName string) string { - if path, ok := k.containerPaths[ctrName]; ok { - return path - } - return k.podPath -} - -// initializeSeccompPaths takes annotations from the pod object metadata and finds annotations pertaining to seccomp -// it parses both pod and container level -// if the annotation is of the form "localhost/%s", the seccomp profile will be set to profileRoot/%s -func initializeSeccompPaths(annotations map[string]string, profileRoot string) (*kubeSeccompPaths, error) { - seccompPaths := &kubeSeccompPaths{containerPaths: make(map[string]string)} - var err error - if annotations != nil { - for annKeyValue, seccomp := range annotations { - // check if it is prefaced with container.seccomp.security.alpha.kubernetes.io/ - prefixAndCtr := strings.Split(annKeyValue, "/") - if prefixAndCtr[0]+"/" != v1.SeccompContainerAnnotationKeyPrefix { - continue - } else if len(prefixAndCtr) != 2 { - // this could be caused by a user inputting either of - // container.seccomp.security.alpha.kubernetes.io{,/} - // both of which are invalid - return nil, errors.Errorf("Invalid seccomp path: %s", prefixAndCtr[0]) - } - - path, err := verifySeccompPath(seccomp, profileRoot) - if err != nil { - return nil, err - } - seccompPaths.containerPaths[prefixAndCtr[1]] = path - } - - podSeccomp, ok := annotations[v1.SeccompPodAnnotationKey] - if ok { - seccompPaths.podPath, err = verifySeccompPath(podSeccomp, profileRoot) - } else { - seccompPaths.podPath, err = libpod.DefaultSeccompPath() - } - if err != nil { - return nil, err - } - } - return seccompPaths, nil -} - -// verifySeccompPath takes a path and checks whether it is a default, unconfined, or a path -// the available options are parsed as defined in https://kubernetes.io/docs/concepts/policy/pod-security-policy/#seccomp -func verifySeccompPath(path string, profileRoot string) (string, error) { - switch path { - case v1.DeprecatedSeccompProfileDockerDefault: - fallthrough - case v1.SeccompProfileRuntimeDefault: - return libpod.DefaultSeccompPath() - case "unconfined": - return path, nil - default: - parts := strings.Split(path, "/") - if parts[0] == "localhost" { - return filepath.Join(profileRoot, parts[1]), nil - } - return "", errors.Errorf("invalid seccomp path: %s", path) - } -} - -func quantityToInt64(quantity *resource.Quantity) (int64, error) { - if i, ok := quantity.AsInt64(); ok { - return i, nil - } - - if i, ok := quantity.AsDec().Unscaled(); ok { - return i, nil - } - - return 0, errors.Errorf("Quantity cannot be represented as int64: %v", quantity) -} diff --git a/pkg/domain/infra/abi/play_test.go b/pkg/domain/infra/abi/play_test.go index 5595476c3..4354a3835 100644 --- a/pkg/domain/infra/abi/play_test.go +++ b/pkg/domain/infra/abi/play_test.go @@ -6,34 +6,9 @@ import ( "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v12 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -var configMapList = []v1.ConfigMap{ - { - TypeMeta: metav1.TypeMeta{ - Kind: "ConfigMap", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "bar", - }, - Data: map[string]string{ - "myvar": "bar", - }, - }, - { - TypeMeta: metav1.TypeMeta{ - Kind: "ConfigMap", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - }, - Data: map[string]string{ - "myvar": "foo", - }, - }, -} - func TestReadConfigMapFromFile(t *testing.T) { tests := []struct { name string @@ -55,11 +30,11 @@ data: false, "", v1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ + TypeMeta: v12.TypeMeta{ Kind: "ConfigMap", APIVersion: "v1", }, - ObjectMeta: metav1.ObjectMeta{ + ObjectMeta: v12.ObjectMeta{ Name: "foo", }, Data: map[string]string{ @@ -114,141 +89,3 @@ data: }) } } - -func TestEnvVarsFromConfigMap(t *testing.T) { - tests := []struct { - name string - envFrom v1.EnvFromSource - configMapList []v1.ConfigMap - expected map[string]string - }{ - { - "ConfigMapExists", - v1.EnvFromSource{ - ConfigMapRef: &v1.ConfigMapEnvSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: "foo", - }, - }, - }, - configMapList, - map[string]string{ - "myvar": "foo", - }, - }, - { - "ConfigMapDoesNotExist", - v1.EnvFromSource{ - ConfigMapRef: &v1.ConfigMapEnvSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: "doesnotexist", - }, - }, - }, - configMapList, - map[string]string{}, - }, - { - "EmptyConfigMapList", - v1.EnvFromSource{ - ConfigMapRef: &v1.ConfigMapEnvSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: "foo", - }, - }, - }, - []v1.ConfigMap{}, - map[string]string{}, - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - result := envVarsFromConfigMap(test.envFrom, test.configMapList) - assert.Equal(t, test.expected, result) - }) - } -} - -func TestEnvVarValue(t *testing.T) { - tests := []struct { - name string - envVar v1.EnvVar - configMapList []v1.ConfigMap - expected string - }{ - { - "ConfigMapExists", - v1.EnvVar{ - Name: "FOO", - ValueFrom: &v1.EnvVarSource{ - ConfigMapKeyRef: &v1.ConfigMapKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ - Name: "foo", - }, - Key: "myvar", - }, - }, - }, - configMapList, - "foo", - }, - { - "ContainerKeyDoesNotExistInConfigMap", - v1.EnvVar{ - Name: "FOO", - ValueFrom: &v1.EnvVarSource{ - ConfigMapKeyRef: &v1.ConfigMapKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ - Name: "foo", - }, - Key: "doesnotexist", - }, - }, - }, - configMapList, - "", - }, - { - "ConfigMapDoesNotExist", - v1.EnvVar{ - Name: "FOO", - ValueFrom: &v1.EnvVarSource{ - ConfigMapKeyRef: &v1.ConfigMapKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ - Name: "doesnotexist", - }, - Key: "myvar", - }, - }, - }, - configMapList, - "", - }, - { - "EmptyConfigMapList", - v1.EnvVar{ - Name: "FOO", - ValueFrom: &v1.EnvVarSource{ - ConfigMapKeyRef: &v1.ConfigMapKeySelector{ - LocalObjectReference: v1.LocalObjectReference{ - Name: "foo", - }, - Key: "myvar", - }, - }, - }, - []v1.ConfigMap{}, - "", - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - result := envVarValue(test.envVar, test.configMapList) - assert.Equal(t, test.expected, result) - }) - } -} diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index 258640a81..11374e513 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -282,20 +282,17 @@ func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOp func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOptions) ([]*entities.ListPodsReport, error) { var ( - err error - filters = []libpod.PodFilter{} - pds = []*libpod.Pod{} + err error + pds = []*libpod.Pod{} ) + filters := make([]libpod.PodFilter, 0, len(options.Filters)) for k, v := range options.Filters { - for _, filter := range v { - f, err := lpfilters.GeneratePodFilterFunc(k, filter) - if err != nil { - return nil, err - } - filters = append(filters, f) - + f, err := lpfilters.GeneratePodFilterFunc(k, v) + if err != nil { + return nil, err } + filters = append(filters, f) } if options.Latest { pod, err := ic.Libpod.GetLatestPod() diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 57c098166..72fd98ac1 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -17,6 +17,7 @@ import ( "github.com/containers/podman/v2/pkg/rootless" "github.com/containers/podman/v2/pkg/util" "github.com/containers/podman/v2/utils" + "github.com/containers/storage" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -123,7 +124,7 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) } } if err != nil { - logrus.Error(err) + logrus.Error(errors.Wrapf(err, "invalid internal status, try resetting the pause process with %q", os.Args[0]+" system migrate")) os.Exit(1) } if became { @@ -231,17 +232,25 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System dfContainers := make([]*entities.SystemDfContainerReport, 0, len(cons)) for _, c := range cons { iid, _ := c.Image() - conSize, err := c.RootFsSize() + state, err := c.State() if err != nil { - return nil, err + return nil, errors.Wrapf(err, "Failed to get state of container %s", c.ID()) } - state, err := c.State() + conSize, err := c.RootFsSize() if err != nil { - return nil, err + if errors.Cause(err) == storage.ErrContainerUnknown { + logrus.Error(errors.Wrapf(err, "Failed to get root file system size of container %s", c.ID())) + } else { + return nil, errors.Wrapf(err, "Failed to get root file system size of container %s", c.ID()) + } } rwsize, err := c.RWSize() if err != nil { - return nil, err + if errors.Cause(err) == storage.ErrContainerUnknown { + logrus.Error(errors.Wrapf(err, "Failed to get read/write size of container %s", c.ID())) + } else { + return nil, errors.Wrapf(err, "Failed to get read/write size of container %s", c.ID()) + } } report := entities.SystemDfContainerReport{ ContainerID: c.ID(), diff --git a/pkg/domain/infra/abi/terminal/sigproxy_linux.go b/pkg/domain/infra/abi/terminal/sigproxy_linux.go index 0c586cf5c..2aca8f22d 100644 --- a/pkg/domain/infra/abi/terminal/sigproxy_linux.go +++ b/pkg/domain/infra/abi/terminal/sigproxy_linux.go @@ -5,8 +5,10 @@ import ( "syscall" "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/shutdown" "github.com/containers/podman/v2/pkg/signal" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -33,12 +35,16 @@ func ProxySignals(ctr *libpod.Container) { } if err := ctr.Kill(uint(s.(syscall.Signal))); err != nil { + if errors.Cause(err) == define.ErrCtrStateInvalid { + logrus.Infof("Ceasing signal forwarding to container %s as it has stopped", ctr.ID()) + } else { + logrus.Errorf("Error forwarding signal %d to container %s: %v", s, ctr.ID(), err) + } // If the container dies, and we find out here, // we need to forward that one signal to // ourselves so that it is not lost, and then // we terminate the proxy and let the defaults // play out. - logrus.Errorf("Error forwarding signal %d to container %s: %v", s, ctr.ID(), err) signal.StopCatch(sigBuffer) if err := syscall.Kill(syscall.Getpid(), s.(syscall.Signal)); err != nil { logrus.Errorf("failed to kill pid %d", syscall.Getpid()) diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go index 26c9c7e2e..b786a5fbf 100644 --- a/pkg/domain/infra/runtime_libpod.go +++ b/pkg/domain/infra/runtime_libpod.go @@ -6,8 +6,10 @@ import ( "context" "fmt" "os" + "os/signal" "sync" + "github.com/containers/podman/v2/cmd/podman/utils" "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/domain/entities" @@ -16,6 +18,7 @@ import ( "github.com/containers/storage" "github.com/containers/storage/pkg/idtools" "github.com/pkg/errors" + "github.com/sirupsen/logrus" flag "github.com/spf13/pflag" ) @@ -348,3 +351,24 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin } return &options, nil } + +// StartWatcher starts a new SIGHUP go routine for the current config. +func StartWatcher(rt *libpod.Runtime) { + // Setup the signal notifier + ch := make(chan os.Signal, 1) + signal.Notify(ch, utils.SIGHUP) + + go func() { + for { + // Block until the signal is received + logrus.Debugf("waiting for SIGHUP to reload configuration") + <-ch + if err := rt.Reload(); err != nil { + logrus.Errorf("unable to reload configuration: %v", err) + continue + } + } + }() + + logrus.Debugf("registered SIGHUP watcher for config") +} diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 8066e1c00..1aa5afbe7 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -595,12 +595,20 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta // Defer the removal, so we can return early if needed and // de-spaghetti the code. defer func() { - if err := containers.Remove(ic.ClientCxt, con.ID, bindings.PFalse, bindings.PTrue); err != nil { - if errorhandling.Contains(err, define.ErrNoSuchCtr) || - errorhandling.Contains(err, define.ErrCtrRemoved) { - logrus.Warnf("Container %s does not exist: %v", con.ID, err) - } else { - logrus.Errorf("Error removing container %s: %v", con.ID, err) + shouldRestart, err := containers.ShouldRestart(ic.ClientCxt, con.ID) + if err != nil { + logrus.Errorf("Failed to check if %s should restart: %v", con.ID, err) + return + } + + if !shouldRestart { + if err := containers.Remove(ic.ClientCxt, con.ID, bindings.PFalse, bindings.PTrue); err != nil { + if errorhandling.Contains(err, define.ErrNoSuchCtr) || + errorhandling.Contains(err, define.ErrCtrRemoved) { + logrus.Warnf("Container %s does not exist: %v", con.ID, err) + } else { + logrus.Errorf("Error removing container %s: %v", con.ID, err) + } } } }() @@ -737,3 +745,8 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri } return containers.Stats(ic.ClientCxt, namesOrIds, &options.Stream) } + +// ShouldRestart reports back whether the containre will restart +func (ic *ContainerEngine) ShouldRestart(_ context.Context, id string) (bool, error) { + return containers.ShouldRestart(ic.ClientCxt, id) +} diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go index 15527e02c..10ae03045 100644 --- a/pkg/domain/infra/tunnel/network.go +++ b/pkg/domain/infra/tunnel/network.go @@ -55,3 +55,13 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) { return network.Create(ic.ClientCxt, options, &name) } + +// NetworkDisconnect removes a container from a given network +func (ic *ContainerEngine) NetworkDisconnect(ctx context.Context, networkname string, options entities.NetworkDisconnectOptions) error { + return network.Disconnect(ic.ClientCxt, networkname, options) +} + +// NetworkConnect removes a container from a given network +func (ic *ContainerEngine) NetworkConnect(ctx context.Context, networkname string, options entities.NetworkConnectOptions) error { + return network.Connect(ic.ClientCxt, networkname, options) +} |