diff options
-rw-r--r-- | cmd/podman/shared/create.go | 50 | ||||
-rw-r--r-- | libpod/container_internal.go | 5 | ||||
-rw-r--r-- | libpod/image/image.go | 130 | ||||
-rw-r--r-- | pkg/adapter/pods.go | 12 | ||||
-rw-r--r-- | test/e2e/images_test.go | 1 |
5 files changed, 98 insertions, 100 deletions
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index 2fcec6deb..bb4e9cd12 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -7,6 +7,7 @@ import ( "io" "os" "path/filepath" + goruntime "runtime" "strconv" "strings" "syscall" @@ -88,9 +89,11 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. return nil, nil, err } + overrideOS := c.String("override-os") + overrideArch := c.String("override-arch") dockerRegistryOptions := image.DockerRegistryOptions{ - OSChoice: c.String("override-os"), - ArchitectureChoice: c.String("override-arch"), + OSChoice: overrideOS, + ArchitectureChoice: overrideArch, } newImage, err := runtime.ImageRuntime().New(ctx, name, rtc.SignaturePolicyPath, c.String("authfile"), writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullType) @@ -101,6 +104,15 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. if err != nil { return nil, nil, err } + + if overrideOS == "" && data.Os != goruntime.GOOS { + return nil, nil, errors.Errorf("incompatible image OS %q on %q host", data.Os, goruntime.GOOS) + } + + if overrideArch == "" && data.Architecture != goruntime.GOARCH { + return nil, nil, errors.Errorf("incompatible image architecture %q on %q host", data.Architecture, goruntime.GOARCH) + } + names := newImage.Names() if len(names) > 0 { imageName = names[0] @@ -214,24 +226,24 @@ func configureEntrypoint(c *GenericCLIResults, data *inspect.ImageData) []string return entrypoint } -func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, error) { +func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, string, error) { pod, err := runtime.LookupPod(podName) if err != nil { - return namespaces, err + return namespaces, "", err } podInfraID, err := pod.InfraContainerID() if err != nil { - return namespaces, err + return namespaces, "", err } hasUserns := false if podInfraID != "" { podCtr, err := runtime.GetContainer(podInfraID) if err != nil { - return namespaces, err + return namespaces, "", err } mappings, err := podCtr.IDMappings() if err != nil { - return namespaces, err + return namespaces, "", err } hasUserns = len(mappings.UIDMap) > 0 } @@ -251,7 +263,7 @@ func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[ if (namespaces["uts"] == cc.Pod) || (!c.IsSet("uts") && pod.SharesUTS()) { namespaces["uts"] = fmt.Sprintf("container:%s", podInfraID) } - return namespaces, nil + return namespaces, podInfraID, nil } // Parses CLI options related to container creation into a config which can be @@ -359,6 +371,10 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. if len(podName) < 1 && c.IsSet("pod") { return nil, errors.Errorf("new pod name must be at least one character") } + + // If we are adding a container to a pod, we would like to add an annotation for the infra ID + // so kata containers can share VMs inside the pod + var podInfraID string if c.IsSet("pod") { if strings.HasPrefix(originalPodName, "new:") { // pod does not exist; lets make it @@ -387,7 +403,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. // The container now cannot have port bindings; so we reset the map portBindings = make(map[nat.Port][]nat.PortBinding) } - namespaces, err = configurePod(c, runtime, namespaces, podName) + namespaces, podInfraID, err = configurePod(c, runtime, namespaces, podName) if err != nil { return nil, err } @@ -485,12 +501,26 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. // ANNOTATIONS annotations := make(map[string]string) + // First, add our default annotations - annotations[ann.ContainerType] = "sandbox" annotations[ann.TTY] = "false" if tty { annotations[ann.TTY] = "true" } + + // in the event this container is in a pod, and the pod has an infra container + // we will want to configure it as a type "container" instead defaulting to + // the behavior of a "sandbox" container + // In Kata containers: + // - "sandbox" is the annotation that denotes the container should use its own + // VM, which is the default behavior + // - "container" denotes the container should join the VM of the SandboxID + // (the infra container) + if podInfraID != "" { + annotations[ann.SandboxID] = podInfraID + annotations[ann.ContainerType] = ann.ContainerTypeContainer + } + if data != nil { // Next, add annotations from the image for key, value := range data.Annotations { diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 028d7601d..4ff1913b5 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -652,6 +652,11 @@ func (c *Container) removeConmonFiles() error { return errors.Wrapf(err, "error removing container %s ctl file", c.ID()) } + winszFile := filepath.Join(c.bundlePath(), "winsz") + if err := os.Remove(winszFile); err != nil && !os.IsNotExist(err) { + return errors.Wrapf(err, "error removing container %s winsz file", c.ID()) + } + oomFile := filepath.Join(c.bundlePath(), "oom") if err := os.Remove(oomFile); err != nil && !os.IsNotExist(err) { return errors.Wrapf(err, "error removing container %s OOM file", c.ID()) diff --git a/libpod/image/image.go b/libpod/image/image.go index c912ac2ca..75ac85311 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -765,109 +765,65 @@ func (i *Image) History(ctx context.Context) ([]*History, error) { return nil, err } - // Use our layers list to find images that use any of them (or no - // layer, since every base layer is derived from an empty layer) as its - // topmost layer. - interestingLayers := make(map[string]bool) - var layer *storage.Layer - if i.TopLayer() != "" { - if layer, err = i.imageruntime.store.Layer(i.TopLayer()); err != nil { - return nil, err - } + // Build a mapping from top-layer to image ID. + images, err := i.imageruntime.GetImages() + if err != nil { + return nil, err } - interestingLayers[""] = true - for layer != nil { - interestingLayers[layer.ID] = true - if layer.Parent == "" { - break + topLayerMap := make(map[string]string) + for _, image := range images { + if _, exists := topLayerMap[image.TopLayer()]; !exists { + topLayerMap[image.TopLayer()] = image.ID() } - layer, err = i.imageruntime.store.Layer(layer.Parent) + } + + var allHistory []*History + var layer *storage.Layer + + // Check if we have an actual top layer to prevent lookup errors. + if i.TopLayer() != "" { + layer, err = i.imageruntime.store.Layer(i.TopLayer()) if err != nil { return nil, err } } - // Get the IDs of the images that share some of our layers. Hopefully - // this step means that we'll be able to avoid reading the - // configuration of every single image in local storage later on. - images, err := i.imageruntime.GetImages() - if err != nil { - return nil, errors.Wrapf(err, "error getting images from store") - } - interestingImages := make([]*Image, 0, len(images)) - for i := range images { - if interestingLayers[images[i].TopLayer()] { - interestingImages = append(interestingImages, images[i]) - } - } + // Iterate in reverse order over the history entries, and lookup the + // corresponding image ID, size and get the next later if needed. + numHistories := len(oci.History) - 1 + for x := numHistories; x >= 0; x-- { + var size int64 - // Build a list of image IDs that correspond to our history entries. - historyImages := make([]*Image, len(oci.History)) - if len(oci.History) > 0 { - // The starting image shares its whole history with itself. - historyImages[len(historyImages)-1] = i - for i := range interestingImages { - image, err := images[i].ociv1Image(ctx) - if err != nil { - return nil, errors.Wrapf(err, "error getting image configuration for image %q", images[i].ID()) + id := "<missing>" + if x == numHistories { + id = i.ID() + } else if layer != nil { + if !oci.History[x].EmptyLayer { + size = layer.UncompressedSize } - // If the candidate has a longer history or no history - // at all, then it doesn't share the portion of our - // history that we're interested in matching with other - // images. - if len(image.History) == 0 || len(image.History) > len(historyImages) { - continue - } - // If we don't include all of the layers that the - // candidate image does (i.e., our rootfs didn't look - // like its rootfs at any point), then it can't be part - // of our history. - if len(image.RootFS.DiffIDs) > len(oci.RootFS.DiffIDs) { - continue - } - candidateLayersAreUsed := true - for i := range image.RootFS.DiffIDs { - if image.RootFS.DiffIDs[i] != oci.RootFS.DiffIDs[i] { - candidateLayersAreUsed = false - break - } - } - if !candidateLayersAreUsed { - continue - } - // If the candidate's entire history is an initial - // portion of our history, then we're based on it, - // either directly or indirectly. - sharedHistory := historiesMatch(oci.History, image.History) - if sharedHistory == len(image.History) { - historyImages[sharedHistory-1] = images[i] + if imageID, exists := topLayerMap[layer.ID]; exists { + id = imageID + // Delete the entry to avoid reusing it for following history items. + delete(topLayerMap, layer.ID) } } - } - var ( - size int64 - sizeCount = 1 - allHistory []*History - ) - - for i := len(oci.History) - 1; i >= 0; i-- { - imageID := "<missing>" - if historyImages[i] != nil { - imageID = historyImages[i].ID() - } - if !oci.History[i].EmptyLayer { - size = img.LayerInfos()[len(img.LayerInfos())-sizeCount].Size - sizeCount++ - } allHistory = append(allHistory, &History{ - ID: imageID, - Created: oci.History[i].Created, - CreatedBy: oci.History[i].CreatedBy, + ID: id, + Created: oci.History[x].Created, + CreatedBy: oci.History[x].CreatedBy, Size: size, - Comment: oci.History[i].Comment, + Comment: oci.History[x].Comment, }) + + if layer != nil && layer.Parent != "" && !oci.History[x].EmptyLayer { + layer, err = i.imageruntime.store.Layer(layer.Parent) + if err != nil { + return nil, err + } + } } + return allHistory, nil } diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go index eafcc5e9b..2003b55ad 100644 --- a/pkg/adapter/pods.go +++ b/pkg/adapter/pods.go @@ -17,6 +17,7 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/adapter/shortcuts" + ann "github.com/containers/libpod/pkg/annotations" ns "github.com/containers/libpod/pkg/namespaces" createconfig "github.com/containers/libpod/pkg/spec" "github.com/containers/libpod/pkg/util" @@ -600,7 +601,7 @@ func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayVa if err != nil { return nil, err } - createConfig, err := kubeContainerToCreateConfig(ctx, container, r.Runtime, newImage, namespaces, volumes, pod.ID()) + createConfig, err := kubeContainerToCreateConfig(ctx, container, r.Runtime, newImage, namespaces, volumes, pod.ID(), podInfraID) if err != nil { return nil, err } @@ -719,7 +720,7 @@ func setupSecurityContext(securityConfig *createconfig.SecurityConfig, userConfi } // kubeContainerToCreateConfig takes a v1.Container and returns a createconfig describing a container -func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, runtime *libpod.Runtime, newImage *image.Image, namespaces map[string]string, volumes map[string]string, podID string) (*createconfig.CreateConfig, error) { +func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, runtime *libpod.Runtime, newImage *image.Image, namespaces map[string]string, volumes map[string]string, podID, infraID string) (*createconfig.CreateConfig, error) { var ( containerConfig createconfig.CreateConfig pidConfig createconfig.PidConfig @@ -800,6 +801,13 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container // Set default environment variables and incorporate data from image, if necessary envs := shared.EnvVariablesFromData(imageData) + annotations := make(map[string]string) + if infraID != "" { + annotations[ann.SandboxID] = infraID + annotations[ann.ContainerType] = ann.ContainerTypeContainer + } + containerConfig.Annotations = annotations + // Environment Variables for _, e := range containerYAML.Env { envs[e.Name] = e.Value diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index e125c62b4..80e6d4444 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -360,7 +360,6 @@ LABEL "com.example.vendor"="Example Vendor" session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) output = session.OutputToString() - Expect(output).To(Not(MatchRegexp("<missing>"))) Expect(output).To(Not(MatchRegexp("error"))) session = podmanTest.Podman([]string{"history", "--quiet", "foo"}) |