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

feature: add container's network files #1403

Merged
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 apis/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1787,6 +1787,11 @@ definitions:
type: "boolean"
x-nullable: false
default: true
DisableNetworkFiles:
description: "Whether to generate the network files(/etc/hostname, /etc/hosts and /etc/resolv.conf) for container."
type: "boolean"
x-nullable: false
default: false
ExposedPorts:
description: "An object mapping ports to an empty object in the form:`{<port>/<tcp|udp>: {}}`"
type: "object"
Expand Down
5 changes: 5 additions & 0 deletions apis/types/container_config.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/common_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func addCommonFlags(flagSet *pflag.FlagSet) *container {
flagSet.StringVar(&c.entrypoint, "entrypoint", "", "Overwrite the default ENTRYPOINT of the image")
flagSet.StringSliceVarP(&c.env, "env", "e", nil, "Set environment variables for container")
flagSet.StringVar(&c.hostname, "hostname", "", "Set container's hostname")
flagSet.BoolVar(&c.disableNetworkFiles, "disable-network-files", false, "Disable the generation of network files(/etc/hostname, /etc/hosts and /etc/resolv.conf) for container. If true, no network files will be generated. Default false")

// Intel RDT
flagSet.StringVar(&c.IntelRdtL3Cbm, "intel-rdt-l3-cbm", "", "Limit container resource for Intel RDT/CAT which introduced in Linux 4.10 kernel")
Expand Down
56 changes: 29 additions & 27 deletions cli/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@ import (
)

type container struct {
labels []string
name string
tty bool
volume []string
volumesFrom []string
runtime string
env []string
entrypoint string
workdir string
user string
groupAdd []string
hostname string
rm bool
labels []string
name string
tty bool
volume []string
volumesFrom []string
runtime string
env []string
entrypoint string
workdir string
user string
groupAdd []string
hostname string
rm bool
disableNetworkFiles bool

blkioWeight uint16
blkioWeightDevice WeightDevice
Expand Down Expand Up @@ -173,20 +174,21 @@ func (c *container) config() (*types.ContainerCreateConfig, error) {

config := &types.ContainerCreateConfig{
ContainerConfig: types.ContainerConfig{
Tty: c.tty,
Env: c.env,
Entrypoint: strings.Fields(c.entrypoint),
WorkingDir: c.workdir,
User: c.user,
Hostname: strfmt.Hostname(c.hostname),
Labels: labels,
Rich: c.rich,
RichMode: c.richMode,
InitScript: c.initScript,
ExposedPorts: ports,
DiskQuota: diskQuota,
QuotaID: c.quotaID,
SpecAnnotation: specAnnotation,
Tty: c.tty,
Env: c.env,
Entrypoint: strings.Fields(c.entrypoint),
WorkingDir: c.workdir,
User: c.user,
Hostname: strfmt.Hostname(c.hostname),
DisableNetworkFiles: c.disableNetworkFiles,
Labels: labels,
Rich: c.rich,
RichMode: c.richMode,
InitScript: c.initScript,
ExposedPorts: ports,
DiskQuota: diskQuota,
QuotaID: c.quotaID,
SpecAnnotation: specAnnotation,
},

HostConfig: &types.HostConfig{
Expand Down
3 changes: 3 additions & 0 deletions cri/v1alpha1/cri_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ func applySandboxSecurityContext(lc *runtime.LinuxPodSandboxConfig, config *apit

// applySandboxLinuxOptions applies LinuxPodSandboxConfig to pouch's HostConfig and ContainerCreateConfig.
func applySandboxLinuxOptions(hc *apitypes.HostConfig, lc *runtime.LinuxPodSandboxConfig, createConfig *apitypes.ContainerCreateConfig, image string) error {
// apply the sandbox network_mode, "none" is default.
hc.NetworkMode = namespaceModeNone

if lc == nil {
return nil
}
Expand Down
3 changes: 3 additions & 0 deletions cri/v1alpha2/cri_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ func applySandboxSecurityContext(lc *runtime.LinuxPodSandboxConfig, config *apit

// applySandboxLinuxOptions applies LinuxPodSandboxConfig to pouch's HostConfig and ContainerCreateConfig.
func applySandboxLinuxOptions(hc *apitypes.HostConfig, lc *runtime.LinuxPodSandboxConfig, createConfig *apitypes.ContainerCreateConfig, image string) error {
// apply the sandbox network_mode, "none" is default.
hc.NetworkMode = namespaceModeNone

if lc == nil {
return nil
}
Expand Down
61 changes: 43 additions & 18 deletions daemon/mgr/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,12 @@ func (mgr *ContainerManager) Create(ctx context.Context, name string, config *ty
return nil, errors.Wrap(errtypes.ErrAlreadyExisted, "container name: "+name)
}

// set hostname.
if config.Hostname.String() == "" {
// if hostname is empty, take the part of id as the hostname
config.Hostname = strfmt.Hostname(id[:12])
}

// set container runtime
if config.HostConfig.Runtime == "" {
config.HostConfig.Runtime = mgr.Config.DefaultRuntime
Expand Down Expand Up @@ -322,13 +328,12 @@ func (mgr *ContainerManager) Create(ctx context.Context, name string, config *ty
networkMode := config.HostConfig.NetworkMode
if networkMode == "" {
config.HostConfig.NetworkMode = "bridge"
container.Config.NetworkDisabled = true
}
container.NetworkSettings = new(types.NetworkSettings)
if len(config.NetworkingConfig.EndpointsConfig) > 0 {
container.NetworkSettings.Networks = config.NetworkingConfig.EndpointsConfig
}
if container.NetworkSettings.Networks == nil && networkMode != "" && !IsContainer(networkMode) {
if container.NetworkSettings.Networks == nil && !IsContainer(config.HostConfig.NetworkMode) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I found that here we have so much blocks about network handling in this Create function. And this make the code much much longer and a little bit difficult to read. Is there any way to encapsulate these blocks into a single function? @shaloulcy
Maybe in another pull request.

container.NetworkSettings.Networks = make(map[string]*types.EndpointSettings)
container.NetworkSettings.Networks[config.HostConfig.NetworkMode] = new(types.EndpointSettings)
}
Expand Down Expand Up @@ -454,33 +459,53 @@ func (mgr *ContainerManager) start(ctx context.Context, c *Container, detachKeys
c.ResolvConfPath = origContainer.ResolvConfPath
c.Config.Hostname = origContainer.Config.Hostname
c.Config.Domainname = origContainer.Config.Domainname
}
} else {
// initialise host network mode
if IsHost(networkMode) {
hostname, err := os.Hostname()
if err != nil {
return err
}
c.Config.Hostname = strfmt.Hostname(hostname)
}

// initialise host network mode
if IsHost(networkMode) {
hostname, err := os.Hostname()
if err != nil {
// build the network related path.
if err := mgr.buildNetworkRelatedPath(c); err != nil {
return err
}
c.Config.Hostname = strfmt.Hostname(hostname)
}

// initialise network endpoint
if c.NetworkSettings != nil {
for name, endpointSetting := range c.NetworkSettings.Networks {
endpoint := mgr.buildContainerEndpoint(c)
endpoint.Name = name
endpoint.EndpointConfig = endpointSetting
if _, err := mgr.NetworkMgr.EndpointCreate(ctx, endpoint); err != nil {
logrus.Errorf("failed to create endpoint: %v", err)
return err
// initialise network endpoint
if c.NetworkSettings != nil {
for name, endpointSetting := range c.NetworkSettings.Networks {
endpoint := mgr.buildContainerEndpoint(c)
endpoint.Name = name
endpoint.EndpointConfig = endpointSetting
if _, err := mgr.NetworkMgr.EndpointCreate(ctx, endpoint); err != nil {
logrus.Errorf("failed to create endpoint: %v", err)
return err
}
}
}
}

return mgr.createContainerdContainer(ctx, c)
}

// buildNetworkRelatedPath builds the network related path.
func (mgr *ContainerManager) buildNetworkRelatedPath(c *Container) error {
// set the hosts file path.
c.HostsPath = path.Join(mgr.Store.Path(c.ID), "hosts")

// set the resolv.conf file path.
c.ResolvConfPath = path.Join(mgr.Store.Path(c.ID), "resolv.conf")

// set the hostname file path.
c.HostnamePath = path.Join(mgr.Store.Path(c.ID), "hostname")

// write the hostname file, other files are filled by libnetwork.
return ioutil.WriteFile(c.HostnamePath, []byte(c.Config.Hostname+"\n"), 0644)
}

func (mgr *ContainerManager) createContainerdContainer(ctx context.Context, c *Container) error {
// CgroupParent from HostConfig will be first priority to use,
// then will be value from mgr.Config.CgroupParent
Expand Down
68 changes: 68 additions & 0 deletions daemon/mgr/spec_mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ package mgr
import (
"context"
"fmt"
"os"

"github.com/alibaba/pouch/apis/types"

specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
)

func clearReadonly(m *specs.Mount) {
Expand All @@ -31,6 +35,11 @@ func setupMounts(ctx context.Context, c *Container, s *specs.Spec) error {
return nil
}
for _, mp := range c.Mounts {
if trySetupNetworkMount(mp, c) {
// ignore the network mount, we will handle it later.
continue
}

// check duplicate mountpoint
for _, sm := range mounts {
if sm.Destination == mp.Destination {
Expand Down Expand Up @@ -69,6 +78,12 @@ func setupMounts(ctx context.Context, c *Container, s *specs.Spec) error {
Options: opts,
})
}

// if disable hostfiles, we will not mount the hosts files into container.
if !c.Config.DisableNetworkFiles {
mounts = append(mounts, generateNetworkMounts(c)...)
}

s.Mounts = mounts

if c.HostConfig.Privileged {
Expand All @@ -83,3 +98,56 @@ func setupMounts(ctx context.Context, c *Container, s *specs.Spec) error {
}
return nil
}

// generateNetworkMounts will generate network mounts.
func generateNetworkMounts(c *Container) []specs.Mount {
mounts := make([]specs.Mount, 0)

fileBinds := []struct {
Name string
Source string
Dest string
}{
{"HostnamePath", c.HostnamePath, "/etc/hostname"},
{"HostsPath", c.HostsPath, "/etc/hosts"},
{"ResolvConfPath", c.ResolvConfPath, "/etc/resolv.conf"},
}

for _, bind := range fileBinds {
if bind.Source != "" {
_, err := os.Stat(bind.Source)
if err != nil {
logrus.Warnf("%s set to %s, but stat error: %v, skip it", bind.Name, bind.Source, err)
} else {
mounts = append(mounts, specs.Mount{
Source: bind.Source,
Destination: bind.Dest,
Type: "bind",
Options: []string{"rbind", "rprivate"},
})
}
}
}

return mounts
}

// trySetupNetworkMount will try to set network mount.
func trySetupNetworkMount(mount *types.MountPoint, c *Container) bool {
if mount.Destination == "/etc/hostname" {
c.HostnamePath = mount.Source
return true
}

if mount.Destination == "/etc/hosts" {
c.HostsPath = mount.Source
return true
}

if mount.Destination == "/etc/resolv.conf" {
c.ResolvConfPath = mount.Source
return true
}

return false
}
43 changes: 43 additions & 0 deletions test/cli_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,3 +332,46 @@ func (suite *PouchRunSuite) TestRunWithRM(c *check.C) {
output := command.PouchRun("inspect", cname).Stderr()
c.Assert(util.PartialEqual(output, cname+": not found"), check.IsNil)
}

// TestRunWithDisableNetworkFiles is to verify running container with disable-network-files flag.
func (suite *PouchRunSuite) TestRunWithDisableNetworkFiles(c *check.C) {
// Run a container with disable-network-files flag
cname1 := "RunWithDisableNetworkFiles"
res := command.PouchRun("run", "--disable-network-files", "--name", cname1,
busyboxImage, "ls", "/etc")
defer DelContainerForceMultyTime(c, cname1)

res.Assert(c, icmd.Success)
output := res.Stdout()
if strings.Contains(output, "hostname") {
Copy link
Contributor

Choose a reason for hiding this comment

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

we may make sure the test image never contains this three files, just comment.

c.Fatal("expected no /etc/hostname, but the file exists")
}

if strings.Contains(output, "hosts") {
c.Fatal("expected no /etc/hosts, but the file exists")
}

if strings.Contains(output, "resolv.conf") {
// ignore checking the existence of /etc/resolv.conf, because the busybox
// contains the file.
}

// Run a container without disable-network-files flag
cname2 := "RunWithoutDisableNetworkFiles"
res = command.PouchRun("run", "--name", cname2, busyboxImage, "ls", "/etc")
defer DelContainerForceMultyTime(c, cname2)

res.Assert(c, icmd.Success)
output = res.Stdout()
if !strings.Contains(output, "hostname") {
c.Fatal("expected /etc/hostname, but the file does not exist")
}

if !strings.Contains(output, "hosts") {
c.Fatal("expected /etc/hosts, but the file does not exist")
}

if !strings.Contains(output, "resolv.conf") {
c.Fatal("expected /etc/resolv.conf, but the file does not exist")
}
}