Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

podman inspect show exposed ports #11314

Merged
merged 1 commit into from
Aug 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions libpod/container_commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai
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)
Expand Down
6 changes: 6 additions & 0 deletions libpod/container_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,12 @@ type ContainerNetworkConfig struct {
// namespace
// These are not used unless CreateNetNS is true
PortMappings []ocicni.PortMapping `json:"portMappings,omitempty"`
// ExposedPorts are the ports which are exposed but not forwarded
// into the container.
// The map key is the port and the string slice contains the protocols,
// e.g. tcp and udp
// These are only set when exposed ports are given but not published.
ExposedPorts map[uint16][]string `json:"exposedPorts,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably wire this into Commit, so the exposed ports are added to committed images. I'm actually kind of amazed this wasn't in before - had to double-check to make sure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, they get commited now.

// UseImageResolvConf indicates that resolv.conf should not be
// bind-mounted inside the container.
// Conflicts with DNSServer, DNSSearch, DNSOption.
Expand Down
2 changes: 1 addition & 1 deletion libpod/container_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
// Port bindings.
// Only populate if we're using CNI to configure the network.
if c.config.CreateNetNS {
hostConfig.PortBindings = makeInspectPortBindings(c.config.PortMappings)
hostConfig.PortBindings = makeInspectPortBindings(c.config.PortMappings, c.config.ExposedPorts)
} else {
hostConfig.PortBindings = make(map[string][]define.InspectHostPort)
}
Expand Down
2 changes: 1 addition & 1 deletion libpod/networking_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -1015,7 +1015,7 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
}

settings := new(define.InspectNetworkSettings)
settings.Ports = makeInspectPortBindings(c.config.PortMappings)
settings.Ports = makeInspectPortBindings(c.config.PortMappings, c.config.ExposedPorts)

networks, isDefault, err := c.networks()
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion libpod/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,7 @@ func WithDependencyCtrs(ctrs []*Container) CtrCreateOption {
// namespace with a minimal configuration.
// An optional array of port mappings can be provided.
// Conflicts with WithNetNSFrom().
func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, netmode string, networks []string) CtrCreateOption {
func WithNetNS(portMappings []ocicni.PortMapping, exposedPorts map[uint16][]string, postConfigureNetNS bool, netmode string, networks []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
Expand All @@ -1051,6 +1051,7 @@ func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, netmo
ctr.config.NetMode = namespaces.NetworkMode(netmode)
ctr.config.CreateNetNS = true
ctr.config.PortMappings = portMappings
ctr.config.ExposedPorts = exposedPorts

ctr.config.Networks = networks

Expand Down
2 changes: 1 addition & 1 deletion libpod/pod_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
infraConfig.Networks = append(infraConfig.Networks, p.config.InfraContainer.Networks...)
}
infraConfig.NetworkOptions = p.config.InfraContainer.NetworkOptions
infraConfig.PortBindings = makeInspectPortBindings(p.config.InfraContainer.PortBindings)
infraConfig.PortBindings = makeInspectPortBindings(p.config.InfraContainer.PortBindings, nil)
}

inspectData := define.InspectPodData{
Expand Down
3 changes: 2 additions & 1 deletion libpod/runtime_pod_infra_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm
options = append(options, WithNetworkOptions(p.config.InfraContainer.NetworkOptions))
}
}
options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, !p.config.InfraContainer.Userns.IsHost(), netmode, p.config.InfraContainer.Networks))
// FIXME allow pods to have exposed ports
options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, nil, !p.config.InfraContainer.Userns.IsHost(), netmode, p.config.InfraContainer.Networks))
}

// For each option in InfraContainerConfig - if set, pass into
Expand Down
13 changes: 11 additions & 2 deletions libpod/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,8 @@ func writeHijackHeader(r *http.Request, conn io.Writer) {
}

// Convert OCICNI port bindings into Inspect-formatted port bindings.
func makeInspectPortBindings(bindings []ocicni.PortMapping) map[string][]define.InspectHostPort {
portBindings := make(map[string][]define.InspectHostPort)
func makeInspectPortBindings(bindings []ocicni.PortMapping, expose map[uint16][]string) map[string][]define.InspectHostPort {
portBindings := make(map[string][]define.InspectHostPort, len(bindings))
for _, port := range bindings {
key := fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol)
hostPorts := portBindings[key]
Expand All @@ -309,6 +309,15 @@ func makeInspectPortBindings(bindings []ocicni.PortMapping) map[string][]define.
})
portBindings[key] = hostPorts
}
// add exposed ports without host port information to match docker
for port, protocols := range expose {
for _, protocol := range protocols {
key := fmt.Sprintf("%d/%s", port, protocol)
if _, ok := portBindings[key]; !ok {
portBindings[key] = nil
}
}
}
return portBindings
}

Expand Down
8 changes: 4 additions & 4 deletions pkg/specgen/generate/namespaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,23 +242,23 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.
}
toReturn = append(toReturn, libpod.WithNetNSFrom(netCtr))
case specgen.Slirp:
portMappings, err := createPortMappings(ctx, s, imageData)
portMappings, expose, err := createPortMappings(ctx, s, imageData)
if err != nil {
return nil, err
}
val := "slirp4netns"
if s.NetNS.Value != "" {
val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value)
}
toReturn = append(toReturn, libpod.WithNetNS(portMappings, postConfigureNetNS, val, nil))
toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, nil))
case specgen.Private:
fallthrough
case specgen.Bridge:
portMappings, err := createPortMappings(ctx, s, imageData)
portMappings, expose, err := createPortMappings(ctx, s, imageData)
if err != nil {
return nil, err
}
toReturn = append(toReturn, libpod.WithNetNS(portMappings, postConfigureNetNS, "bridge", s.CNINetworks))
toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, "bridge", s.CNINetworks))
}

if s.UseImageHosts {
Expand Down
29 changes: 16 additions & 13 deletions pkg/specgen/generate/ports.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,17 +253,15 @@ func ParsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping,
}

// 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, map[uint16][]string, error) {
finalMappings, containerPortValidate, hostPortValidate, err := ParsePortMapping(s.PortMappings)
if err != nil {
return nil, err
return nil, nil, err
}

// If not publishing exposed ports, or if we are publishing and there is
// nothing to publish - then just return the port mappings we've made so
// far.
if !s.PublishExposedPorts || (len(s.Expose) == 0 && imageData == nil) {
return finalMappings, nil
// No exposed ports so return the port mappings we've made so far.
if len(s.Expose) == 0 && imageData == nil {
return finalMappings, nil, nil
}

logrus.Debugf("Adding exposed ports")
Expand All @@ -272,7 +270,7 @@ func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData
if imageData != nil {
expose, err = GenExposedPorts(imageData.Config.ExposedPorts)
if err != nil {
return nil, err
return nil, nil, err
}
}

Expand All @@ -288,11 +286,11 @@ func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData
// Validate protocol first
protocols, err := checkProtocol(proto, false)
if err != nil {
return nil, errors.Wrapf(err, "error validating protocols for exposed port %d", port)
return nil, nil, errors.Wrapf(err, "error validating protocols for exposed port %d", port)
}

if port == 0 {
return nil, errors.Errorf("cannot expose 0 as it is not a valid port number")
return nil, nil, errors.Errorf("cannot expose 0 as it is not a valid port number")
}

// Check to see if the port is already present in existing
Expand All @@ -316,6 +314,11 @@ func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData
}
}

// If not publishing exposed ports return mappings and exposed ports.
if !s.PublishExposedPorts {
return finalMappings, toExpose, nil
}

// We now have a final list of ports that we want exposed.
// Let's find empty, unallocated host ports for them.
for port, protocols := range toExpose {
Expand All @@ -331,7 +334,7 @@ func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData
// unfortunate for the UDP case.
candidate, err := utils.GetRandomPort()
if err != nil {
return nil, err
return nil, nil, err
}

// Check if the host port is already bound
Expand Down Expand Up @@ -362,12 +365,12 @@ func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData
}
if tries == 0 && hostPort == 0 {
// We failed to find an open port.
return nil, errors.Errorf("failed to find an open port to expose container port %d on the host", port)
return nil, nil, errors.Errorf("failed to find an open port to expose container port %d on the host", port)
}
}
}

return finalMappings, nil
return finalMappings, nil, nil
}

// Check a string to ensure it is a comma-separated set of valid protocols
Expand Down
36 changes: 36 additions & 0 deletions test/e2e/commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,4 +329,40 @@ var _ = Describe("Podman commit", func() {
session.WaitWithDefaultTimeout()
Expect(session.OutputToString()).To(Not(ContainSubstring(secretsString)))
})

It("podman commit adds exposed ports", func() {
name := "testcon"
s := podmanTest.Podman([]string{"run", "--name", name, "-p", "8080:80", ALPINE, "true"})
s.WaitWithDefaultTimeout()
Expect(s).Should(Exit(0))

newImageName := "newimage"
c := podmanTest.Podman([]string{"commit", name, newImageName})
c.WaitWithDefaultTimeout()
Expect(c).Should(Exit(0))

inspect := podmanTest.Podman([]string{"inspect", newImageName})
inspect.WaitWithDefaultTimeout()
Expect(inspect).Should(Exit(0))
images := inspect.InspectImageJSON()
Expect(images).To(HaveLen(1))
Expect(images[0].Config.ExposedPorts).To(HaveKey("80/tcp"))

name = "testcon2"
s = podmanTest.Podman([]string{"run", "--name", name, "-d", nginx})
s.WaitWithDefaultTimeout()
Expect(s).Should(Exit(0))

newImageName = "newimage2"
c = podmanTest.Podman([]string{"commit", name, newImageName})
c.WaitWithDefaultTimeout()
Expect(c).Should(Exit(0))

inspect = podmanTest.Podman([]string{"inspect", newImageName})
inspect.WaitWithDefaultTimeout()
Expect(inspect).Should(Exit(0))
images = inspect.InspectImageJSON()
Expect(images).To(HaveLen(1))
Expect(images[0].Config.ExposedPorts).To(HaveKey("80/tcp"))
})
})
25 changes: 25 additions & 0 deletions test/e2e/container_inspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package integration
import (
"os"

"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/annotations"
. "github.com/containers/podman/v3/test/utils"
. "github.com/onsi/ginkgo"
Expand Down Expand Up @@ -43,4 +44,28 @@ var _ = Describe("Podman container inspect", func() {
Expect(data[0].Config.Annotations[annotations.ContainerManager]).
To(Equal(annotations.ContainerManagerLibpod))
})

It("podman inspect shows exposed ports", func() {
name := "testcon"
session := podmanTest.Podman([]string{"run", "-d", "--stop-timeout", "0", "--expose", "8080/udp", "--name", name, ALPINE, "sleep", "inf"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
data := podmanTest.InspectContainer(name)

Expect(data).To(HaveLen(1))
Expect(data[0].NetworkSettings.Ports).
To(Equal(map[string][]define.InspectHostPort{"8080/udp": nil}))
})

It("podman inspect shows exposed ports on image", func() {
name := "testcon"
session := podmanTest.Podman([]string{"run", "-d", "--expose", "8080", "--name", name, nginx})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))

data := podmanTest.InspectContainer(name)
Expect(data).To(HaveLen(1))
Expect(data[0].NetworkSettings.Ports).
To(Equal(map[string][]define.InspectHostPort{"80/tcp": nil, "8080/tcp": nil}))
})
})