Allow changing of port forward rules on restore

Restored containers, until now, had the same port mappings as the
original started container. This commit adds the parameter '--publish'
to 'podman container restore' with the same semantic as during
create/run.

With this change it is possible to create a copy from a container with a
'--publish' rule and replace the original '--publish' setting with a new
one.

 # podman run -p 2345:8080 container
 # podman container checkpoint -l --export=dump.tar
 # podman container restore -p 5432:8080 --import=dump.tar

The restored container will now listen on localhost:5432 instead of
localhost:2345 as the original created container.

Signed-off-by: Adrian Reber <areber@redhat.com>
This commit is contained in:
Adrian Reber
2021-05-18 11:31:30 +00:00
committed by Adrian Reber
parent f7233a2da7
commit 1ac9198d75
7 changed files with 36 additions and 10 deletions

View File

@ -170,7 +170,7 @@ func NetFlagsToNetOptions(cmd *cobra.Command, netnsFromConfig bool) (*entities.N
return nil, err return nil, err
} }
if len(inputPorts) > 0 { if len(inputPorts) > 0 {
opts.PublishPorts, err = createPortBindings(inputPorts) opts.PublishPorts, err = CreatePortBindings(inputPorts)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -89,8 +89,8 @@ func createExpose(expose []string) (map[uint16]string, error) {
return toReturn, nil return toReturn, nil
} }
// createPortBindings iterates ports mappings into SpecGen format. // CreatePortBindings iterates ports mappings into SpecGen format.
func createPortBindings(ports []string) ([]specgen.PortMapping, error) { func CreatePortBindings(ports []string) ([]specgen.PortMapping, error) {
// --publish is formatted as follows: // --publish is formatted as follows:
// [[hostip:]hostport[-endPort]:]containerport[-endPort][/protocol] // [[hostip:]hostport[-endPort]:]containerport[-endPort][/protocol]
toReturn := make([]specgen.PortMapping, 0, len(ports)) toReturn := make([]specgen.PortMapping, 0, len(ports))

View File

@ -36,9 +36,7 @@ var (
} }
) )
var ( var restoreOptions entities.RestoreOptions
restoreOptions entities.RestoreOptions
)
func init() { func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{ registry.Commands = append(registry.Commands, registry.CliCommand{
@ -66,10 +64,17 @@ func init() {
flags.BoolVar(&restoreOptions.IgnoreStaticIP, "ignore-static-ip", false, "Ignore IP address set via --static-ip") flags.BoolVar(&restoreOptions.IgnoreStaticIP, "ignore-static-ip", false, "Ignore IP address set via --static-ip")
flags.BoolVar(&restoreOptions.IgnoreStaticMAC, "ignore-static-mac", false, "Ignore MAC address set via --mac-address") flags.BoolVar(&restoreOptions.IgnoreStaticMAC, "ignore-static-mac", false, "Ignore MAC address set via --mac-address")
flags.BoolVar(&restoreOptions.IgnoreVolumes, "ignore-volumes", false, "Do not export volumes associated with container") flags.BoolVar(&restoreOptions.IgnoreVolumes, "ignore-volumes", false, "Do not export volumes associated with container")
flags.StringSliceP(
"publish", "p", []string{},
"Publish a container's port, or a range of ports, to the host (default [])",
)
_ = restoreCommand.RegisterFlagCompletionFunc("publish", completion.AutocompleteNone)
validate.AddLatestFlag(restoreCommand, &restoreOptions.Latest) validate.AddLatestFlag(restoreCommand, &restoreOptions.Latest)
} }
func restore(_ *cobra.Command, args []string) error { func restore(cmd *cobra.Command, args []string) error {
var errs utils.OutputErrors var errs utils.OutputErrors
if rootless.IsRootless() { if rootless.IsRootless() {
return errors.New("restoring a container requires root") return errors.New("restoring a container requires root")
@ -90,6 +95,17 @@ func restore(_ *cobra.Command, args []string) error {
return errors.Errorf("--tcp-established cannot be used with --name") return errors.Errorf("--tcp-established cannot be used with --name")
} }
inputPorts, err := cmd.Flags().GetStringSlice("publish")
if err != nil {
return err
}
if len(inputPorts) > 0 {
restoreOptions.PublishPorts, err = common.CreatePortBindings(inputPorts)
if err != nil {
return err
}
}
argLen := len(args) argLen := len(args)
if restoreOptions.Import != "" { if restoreOptions.Import != "" {
if restoreOptions.All || restoreOptions.Latest { if restoreOptions.All || restoreOptions.Latest {

View File

@ -11,6 +11,7 @@ import (
"github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/podman/v3/pkg/errorhandling"
"github.com/containers/podman/v3/pkg/specgen/generate"
"github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/archive"
spec "github.com/opencontainers/runtime-spec/specs-go" spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -95,6 +96,14 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt
newName = true newName = true
} }
if len(restoreOptions.PublishPorts) > 0 {
ports, _, _, err := generate.ParsePortMapping(restoreOptions.PublishPorts)
if err != nil {
return nil, err
}
ctrConfig.PortMappings = ports
}
pullOptions := &libimage.PullOptions{} pullOptions := &libimage.PullOptions{}
pullOptions.Writer = os.Stderr pullOptions.Writer = os.Stderr
if _, err := runtime.LibimageRuntime().Pull(ctx, ctrConfig.RootfsImageName, config.PullPolicyMissing, pullOptions); err != nil { if _, err := runtime.LibimageRuntime().Pull(ctx, ctrConfig.RootfsImageName, config.PullPolicyMissing, pullOptions); err != nil {

View File

@ -197,6 +197,7 @@ type RestoreOptions struct {
Name string Name string
TCPEstablished bool TCPEstablished bool
ImportPrevious string ImportPrevious string
PublishPorts []specgen.PortMapping
} }
type RestoreReport struct { type RestoreReport struct {

View File

@ -125,7 +125,7 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod
options = append(options, libpod.WithPodUseImageHosts()) options = append(options, libpod.WithPodUseImageHosts())
} }
if len(p.PortMappings) > 0 { if len(p.PortMappings) > 0 {
ports, _, _, err := parsePortMapping(p.PortMappings) ports, _, _, err := ParsePortMapping(p.PortMappings)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -24,7 +24,7 @@ const (
// Parse port maps to OCICNI port mappings. // Parse port maps to OCICNI port mappings.
// Returns a set of OCICNI port mappings, and maps of utilized container and // Returns a set of OCICNI port mappings, and maps of utilized container and
// host ports. // host ports.
func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, map[string]map[string]map[uint16]uint16, map[string]map[string]map[uint16]uint16, error) { func ParsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, map[string]map[string]map[uint16]uint16, map[string]map[string]map[uint16]uint16, error) {
// First, we need to validate the ports passed in the specgen, and then // First, we need to validate the ports passed in the specgen, and then
// convert them into CNI port mappings. // convert them into CNI port mappings.
type tempMapping struct { type tempMapping struct {
@ -254,7 +254,7 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping,
// Make final port mappings for the container // Make final port mappings for the container
func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData *libimage.ImageData) ([]ocicni.PortMapping, error) { func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData *libimage.ImageData) ([]ocicni.PortMapping, error) {
finalMappings, containerPortValidate, hostPortValidate, err := parsePortMapping(s.PortMappings) finalMappings, containerPortValidate, hostPortValidate, err := ParsePortMapping(s.PortMappings)
if err != nil { if err != nil {
return nil, err return nil, err
} }