mirror of
https://github.com/containers/podman.git
synced 2025-06-17 23:20:59 +08:00

When committing containers to create new images, accept a container config blob being passed in the body of the API request by adding a Config field to our API structures. Populate it from the body of requests that we receive, and use its contents as the body of requests that we make. Make the libpod commit endpoint split changes values at newlines, just like the compat endpoint does. Pass both the config blob and the "changes" slice to buildah's Commit() API, so that it can handle cases where they overlap or conflict. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
180 lines
5.1 KiB
Go
180 lines
5.1 KiB
Go
//go:build !remote
|
|
// +build !remote
|
|
|
|
package libpod
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/containers/buildah"
|
|
"github.com/containers/common/libimage"
|
|
is "github.com/containers/image/v5/storage"
|
|
"github.com/containers/image/v5/types"
|
|
"github.com/containers/podman/v4/libpod/define"
|
|
"github.com/containers/podman/v4/libpod/events"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// ContainerCommitOptions is a struct used to commit a container to an image
|
|
// It uses buildah's CommitOptions as a base. Long-term we might wish to
|
|
// decouple these because it includes duplicates of fields that are in, or
|
|
// could later be added, to buildah's CommitOptions, which gets confusing
|
|
type ContainerCommitOptions struct {
|
|
buildah.CommitOptions
|
|
Pause bool
|
|
IncludeVolumes bool
|
|
Author string
|
|
Message string
|
|
Changes []string // gets merged with CommitOptions.OverrideChanges
|
|
Squash bool // always used instead of CommitOptions.Squash
|
|
}
|
|
|
|
// Commit commits the changes between a container and its image, creating a new
|
|
// image
|
|
func (c *Container) Commit(ctx context.Context, destImage string, options ContainerCommitOptions) (*libimage.Image, error) {
|
|
if c.config.Rootfs != "" {
|
|
return nil, errors.New("cannot commit a container that uses an exploded rootfs")
|
|
}
|
|
|
|
if !c.batched {
|
|
c.lock.Lock()
|
|
defer c.lock.Unlock()
|
|
|
|
if err := c.syncContainer(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if c.state.State == define.ContainerStateRunning && options.Pause {
|
|
if err := c.pause(); err != nil {
|
|
return nil, fmt.Errorf("pausing container %q to commit: %w", c.ID(), err)
|
|
}
|
|
defer func() {
|
|
if err := c.unpause(); err != nil {
|
|
logrus.Errorf("Unpausing container %q: %v", c.ID(), err)
|
|
}
|
|
}()
|
|
}
|
|
|
|
builderOptions := buildah.ImportOptions{
|
|
Container: c.ID(),
|
|
SignaturePolicyPath: options.SignaturePolicyPath,
|
|
}
|
|
commitOptions := buildah.CommitOptions{
|
|
SignaturePolicyPath: options.SignaturePolicyPath,
|
|
ReportWriter: options.ReportWriter,
|
|
Squash: options.Squash,
|
|
SystemContext: c.runtime.imageContext,
|
|
PreferredManifestType: options.PreferredManifestType,
|
|
OverrideChanges: append(append([]string{}, options.Changes...), options.CommitOptions.OverrideChanges...),
|
|
OverrideConfig: options.CommitOptions.OverrideConfig,
|
|
}
|
|
importBuilder, err := buildah.ImportBuilder(ctx, c.runtime.store, builderOptions)
|
|
importBuilder.Format = options.PreferredManifestType
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if options.Author != "" {
|
|
importBuilder.SetMaintainer(options.Author)
|
|
}
|
|
if options.Message != "" {
|
|
importBuilder.SetComment(options.Message)
|
|
}
|
|
|
|
// We need to take meta we find in the current container and
|
|
// add it to the resulting image.
|
|
|
|
// Entrypoint - always set this first or cmd will get wiped out
|
|
importBuilder.SetEntrypoint(c.config.Entrypoint)
|
|
|
|
// Cmd
|
|
importBuilder.SetCmd(c.config.Command)
|
|
|
|
// Env
|
|
// TODO - this includes all the default environment vars as well
|
|
// Should we store the ENV we actually want in the spec separately?
|
|
if c.config.Spec.Process != nil {
|
|
for _, e := range c.config.Spec.Process.Env {
|
|
splitEnv := strings.SplitN(e, "=", 2)
|
|
importBuilder.SetEnv(splitEnv[0], splitEnv[1])
|
|
}
|
|
}
|
|
// Expose ports
|
|
for _, p := range c.config.PortMappings {
|
|
importBuilder.SetPort(fmt.Sprintf("%d/%s", p.ContainerPort, p.Protocol))
|
|
}
|
|
for port, protocols := range c.config.ExposedPorts {
|
|
for _, protocol := range protocols {
|
|
importBuilder.SetPort(fmt.Sprintf("%d/%s", port, protocol))
|
|
}
|
|
}
|
|
// Labels
|
|
for k, v := range c.Labels() {
|
|
importBuilder.SetLabel(k, v)
|
|
}
|
|
// No stop signal
|
|
// User
|
|
if c.config.User != "" {
|
|
importBuilder.SetUser(c.config.User)
|
|
}
|
|
// Volumes
|
|
if options.IncludeVolumes {
|
|
for _, v := range c.config.UserVolumes {
|
|
if v != "" {
|
|
importBuilder.AddVolume(v)
|
|
}
|
|
}
|
|
} else {
|
|
// Only include anonymous named volumes added by the user by
|
|
// default.
|
|
for _, v := range c.config.NamedVolumes {
|
|
include := false
|
|
for _, userVol := range c.config.UserVolumes {
|
|
if userVol == v.Dest {
|
|
include = true
|
|
break
|
|
}
|
|
}
|
|
if include {
|
|
vol, err := c.runtime.GetVolume(v.Name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("volume %s used in container %s has been removed: %w", v.Name, c.ID(), err)
|
|
}
|
|
if vol.Anonymous() {
|
|
importBuilder.AddVolume(v.Dest)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Workdir
|
|
importBuilder.SetWorkDir(c.config.Spec.Process.Cwd)
|
|
|
|
var commitRef types.ImageReference
|
|
if destImage != "" {
|
|
// Now resolve the name.
|
|
resolvedImageName, err := c.runtime.LibimageRuntime().ResolveName(destImage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
imageRef, err := is.Transport.ParseStoreReference(c.runtime.store, resolvedImageName)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parsing target image name %q: %w", destImage, err)
|
|
}
|
|
commitRef = imageRef
|
|
}
|
|
id, _, _, err := importBuilder.Commit(ctx, commitRef, commitOptions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer c.newContainerEvent(events.Commit)
|
|
img, _, err := c.runtime.libimageRuntime.LookupImage(id, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return img, nil
|
|
}
|