Convert bind mounts to use DB field

Refactors creation of bind mounts into a separate function that
can be called from elsewhere (e.g. pod start or container
restart). This function stores the mounts in the DB using the
field established last commit.

Spec generation now relies upon this field in the DB instead of
manually enumerating files to be bind mounted in.

Signed-off-by: Matthew Heon <matthew.heon@gmail.com>

Closes: #462
Approved by: baude
This commit is contained in:
Matthew Heon
2018-03-07 14:10:19 -05:00
committed by Atomic Bot
parent c657511bce
commit 54f32f2cc0
5 changed files with 121 additions and 62 deletions

View File

@ -143,6 +143,7 @@ func (s *BoltState) Refresh() error {
state.ExecSessions = make(map[string]*ExecSession)
state.IPs = nil
state.Routes = nil
state.BindMounts = make(map[string]string)
newStateBytes, err := json.Marshal(state)
if err != nil {

View File

@ -710,6 +710,35 @@ func (c *Container) Routes() ([]types.Route, error) {
return routes, nil
}
// BindMounts retrieves bind mounts that were created by libpod and will be
// added to the container
// All these mounts except /dev/shm are ignored if a mount in the given spec has
// the same destination
// These mounts include /etc/resolv.conf, /etc/hosts, and /etc/hostname
// The return is formatted as a map from destination (mountpoint in the
// container) to source (path of the file that will be mounted into the
// container)
// If the container has not been started yet, an empty map will be returned, as
// the files in question are only created when the container is started.
func (c *Container) BindMounts() (map[string]string, error) {
if !c.locked {
c.lock.Lock()
defer c.lock.Unlock()
if err := c.syncContainer(); err != nil {
return nil, err
}
}
newMap := make(map[string]string, len(c.state.BindMounts))
for key, val := range c.state.BindMounts {
newMap[key] = val
}
return newMap, nil
}
// Misc Accessors
// Most will require locking

View File

@ -59,25 +59,12 @@ func (c *Container) Init() (err error) {
}
}()
// Copy /etc/resolv.conf to the container's rundir
runDirResolv, err := c.generateResolvConf()
if err != nil {
if err := c.makeBindMounts(); err != nil {
return err
}
// Copy /etc/hosts to the container's rundir
runDirHosts, err := c.generateHosts()
if err != nil {
return errors.Wrapf(err, "unable to copy /etc/hosts to container space")
}
runDirHostname, err := c.generateEtcHostname(c.Hostname())
if err != nil {
return errors.Wrapf(err, "unable to generate hostname file for container")
}
// Generate the OCI spec
spec, err := c.generateSpec(runDirResolv, runDirHosts, runDirHostname)
spec, err := c.generateSpec()
if err != nil {
return err
}

View File

@ -25,6 +25,25 @@ func (c *Container) getContainerInspectData(size bool, driverData *inspect.Data)
execIDs = append(execIDs, id)
}
if c.state.BindMounts == nil {
c.state.BindMounts = make(map[string]string)
}
resolvPath := ""
if path, ok := c.state.BindMounts["/etc/resolv.conf"]; ok {
resolvPath = path
}
hostsPath := ""
if path, ok := c.state.BindMounts["/etc/hosts"]; ok {
hostsPath = path
}
hostnamePath := ""
if path, ok := c.state.BindMounts["/etc/hostname"]; ok {
hostnamePath = path
}
data := &inspect.ContainerInspectData{
ID: config.ID,
Created: config.CreatedTime,
@ -45,9 +64,9 @@ func (c *Container) getContainerInspectData(size bool, driverData *inspect.Data)
},
ImageID: config.RootfsImageID,
ImageName: config.RootfsImageName,
ResolvConfPath: "", // TODO get from networking path
HostnamePath: spec.Annotations["io.kubernetes.cri-o.HostnamePath"], // not sure
HostsPath: "", // can't get yet
ResolvConfPath: resolvPath,
HostnamePath: hostnamePath,
HostsPath: hostsPath,
StaticDir: config.StaticDir,
LogPath: config.LogPath,
Name: config.Name,

View File

@ -162,6 +162,8 @@ func newContainer(rspec *spec.Spec, lockDir string) (*Container, error) {
ctr.config.ShmSize = DefaultShmSize
ctr.config.CgroupParent = DefaultCgroupParent
ctr.state.BindMounts = make(map[string]string)
// Path our lock file will reside at
lockPath := filepath.Join(lockDir, ctr.config.ID)
// Grab a lockfile at the given path
@ -444,6 +446,56 @@ func (c *Container) cleanupStorage() error {
return c.save()
}
// Make standard bind mounts to include in the container
func (c *Container) makeBindMounts() error {
if c.state.BindMounts == nil {
c.state.BindMounts = make(map[string]string)
}
// SHM is always added when we mount the container
c.state.BindMounts["/dev/shm"] = c.config.ShmDir
// Make /etc/resolv.conf
if path, ok := c.state.BindMounts["/etc/resolv.conf"]; ok {
// If it already exists, delete so we can recreate
if err := os.Remove(path); err != nil {
return errors.Wrapf(err, "error removing resolv.conf for container %s", c.ID())
}
delete(c.state.BindMounts, "/etc/resolv.conf")
}
newResolv, err := c.generateResolvConf()
if err != nil {
return errors.Wrapf(err, "error creating resolv.conf for container %s", c.ID())
}
c.state.BindMounts["/etc/resolv.conf"] = newResolv
// Make /etc/hosts
if path, ok := c.state.BindMounts["/etc/hosts"]; ok {
// If it already exists, delete so we can recreate
if err := os.Remove(path); err != nil {
return errors.Wrapf(err, "error removing hosts file for container %s", c.ID())
}
delete(c.state.BindMounts, "/etc/hosts")
}
newHosts, err := c.generateHosts()
if err != nil {
return errors.Wrapf(err, "error creating hosts file for container %s", c.ID())
}
c.state.BindMounts["/etc/hosts"] = newHosts
// Make /etc/hostname
// This should never change, so no need to recreate if it exists
if _, ok := c.state.BindMounts["/etc/hostname"]; !ok {
hostnamePath, err := c.writeStringToRundir("hostname", c.Hostname())
if err != nil {
return errors.Wrapf(err, "error creating hostname file for container %s", c.ID())
}
c.state.BindMounts["/etc/hostname"] = hostnamePath
}
return nil
}
// writeStringToRundir copies the provided file to the runtimedir
func (c *Container) writeStringToRundir(destFile, output string) (string, error) {
destFileName := filepath.Join(c.state.RunDir, destFile)
@ -579,58 +631,29 @@ func (c *Container) generateHosts() (string, error) {
return c.writeStringToRundir("hosts", hosts)
}
// generateEtcHostname creates a containers /etc/hostname
func (c *Container) generateEtcHostname(hostname string) (string, error) {
return c.writeStringToRundir("hostname", hostname)
}
// Generate spec for a container
func (c *Container) generateSpec(resolvPath, hostsPath, hostnamePath string) (*spec.Spec, error) {
func (c *Container) generateSpec() (*spec.Spec, error) {
g := generate.NewFromSpec(c.config.Spec)
// If network namespace was requested, add it now
if c.config.CreateNetNS {
g.AddOrReplaceLinuxNamespace(spec.NetworkNamespace, c.state.NetNS.Path())
}
// Remove default /etc/shm mount
// Remove the default /dev/shm mount to ensure we overwrite it
g.RemoveMount("/dev/shm")
// Mount ShmDir from host into container
shmMnt := spec.Mount{
Type: "bind",
Source: c.config.ShmDir,
Destination: "/dev/shm",
Options: []string{"rw", "bind"},
}
g.AddMount(shmMnt)
// Bind mount resolv.conf
resolvMnt := spec.Mount{
Type: "bind",
Source: resolvPath,
Destination: "/etc/resolv.conf",
Options: []string{"rw", "bind"},
}
if !MountExists(g.Mounts(), resolvMnt.Destination) {
g.AddMount(resolvMnt)
}
// Bind mount hosts
hostsMnt := spec.Mount{
Type: "bind",
Source: hostsPath,
Destination: "/etc/hosts",
Options: []string{"rw", "bind"},
}
if !MountExists(g.Mounts(), hostsMnt.Destination) {
g.AddMount(hostsMnt)
}
// Bind hostname
hostnameMnt := spec.Mount{
Type: "bind",
Source: hostnamePath,
Destination: "/etc/hostname",
Options: []string{"rw", "bind"},
}
if !MountExists(g.Mounts(), hostnameMnt.Destination) {
g.AddMount(hostnameMnt)
// Add bind mounts to container
for dstPath, srcPath := range c.state.BindMounts {
newMount := spec.Mount{
Type: "bind",
Source: srcPath,
Destination: dstPath,
Options: []string{"rw", "bind"},
}
if !MountExists(g.Mounts(), dstPath) {
g.AddMount(newMount)
}
}
// Bind builtin image volumes