mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-08-28 12:31:04 +00:00
Merge pull request #136 from sboeuf/fix_rollback
virtcontainers: Properly rollback mounts and hotplugs when container creation failed
This commit is contained in:
commit
e75713fffb
@ -416,63 +416,78 @@ func newContainer(pod *Pod, contConfig ContainerConfig) (*Container, error) {
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rollbackFailingContainerCreation rolls back important steps that might have
|
||||||
|
// been performed before the container creation failed.
|
||||||
|
// - Unplug CPU and memory resources from the VM.
|
||||||
|
// - Unplug devices from the VM.
|
||||||
|
func rollbackFailingContainerCreation(c *Container, err error) {
|
||||||
|
if err != nil && c != nil {
|
||||||
|
if err2 := c.removeResources(); err2 != nil {
|
||||||
|
c.Logger().WithError(err2).Error("rollback failed removeResources()")
|
||||||
|
}
|
||||||
|
if err2 := c.detachDevices(); err2 != nil {
|
||||||
|
c.Logger().WithError(err2).Error("rollback failed detachDevices()")
|
||||||
|
}
|
||||||
|
if err2 := c.removeDrive(); err2 != nil {
|
||||||
|
c.Logger().WithError(err2).Error("rollback failed removeDrive()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// createContainer creates and start a container inside a Pod. It has to be
|
// createContainer creates and start a container inside a Pod. It has to be
|
||||||
// called only when a new container, not known by the pod, has to be created.
|
// called only when a new container, not known by the pod, has to be created.
|
||||||
func createContainer(pod *Pod, contConfig ContainerConfig) (*Container, error) {
|
func createContainer(pod *Pod, contConfig ContainerConfig) (c *Container, err error) {
|
||||||
if pod == nil {
|
if pod == nil {
|
||||||
return nil, errNeedPod
|
return nil, errNeedPod
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := newContainer(pod, contConfig)
|
c, err = newContainer(pod, contConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.createContainersDirs(); err != nil {
|
if err = c.createContainersDirs(); err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.pod.config.HypervisorConfig.DisableBlockDeviceUse {
|
// In case the container creation fails, the following takes care
|
||||||
agentCaps := c.pod.agent.capabilities()
|
// of rolling back all the actions previously performed.
|
||||||
hypervisorCaps := c.pod.hypervisor.capabilities()
|
defer rollbackFailingContainerCreation(c, err)
|
||||||
|
|
||||||
if agentCaps.isBlockDeviceSupported() && hypervisorCaps.isBlockDeviceHotplugSupported() {
|
if err = c.hotplugDrive(); err != nil {
|
||||||
if err := c.hotplugDrive(); err != nil {
|
return
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach devices
|
// Attach devices
|
||||||
if err := c.attachDevices(); err != nil {
|
if err = c.attachDevices(); err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.addResources(); err != nil {
|
if err = c.addResources(); err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deduce additional system mount info that should be handled by the agent
|
// Deduce additional system mount info that should be handled by the agent
|
||||||
// inside the VM
|
// inside the VM
|
||||||
c.getSystemMountInfo()
|
c.getSystemMountInfo()
|
||||||
|
|
||||||
if err := c.storeDevices(); err != nil {
|
if err = c.storeDevices(); err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
process, err := pod.agent.createContainer(c.pod, c)
|
process, err := pod.agent.createContainer(c.pod, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return c, err
|
||||||
}
|
}
|
||||||
c.process = *process
|
c.process = *process
|
||||||
|
|
||||||
// Store the container process returned by the agent.
|
// Store the container process returned by the agent.
|
||||||
if err := c.storeProcess(); err != nil {
|
if err = c.storeProcess(); err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.setContainerState(StateReady); err != nil {
|
if err = c.setContainerState(StateReady); err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
@ -666,6 +681,15 @@ func (c *Container) processList(options ProcessListOptions) (ProcessList, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) hotplugDrive() error {
|
func (c *Container) hotplugDrive() error {
|
||||||
|
agentCaps := c.pod.agent.capabilities()
|
||||||
|
hypervisorCaps := c.pod.hypervisor.capabilities()
|
||||||
|
|
||||||
|
if c.pod.config.HypervisorConfig.DisableBlockDeviceUse ||
|
||||||
|
!agentCaps.isBlockDeviceSupported() ||
|
||||||
|
!hypervisorCaps.isBlockDeviceHotplugSupported() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
dev, err := getDeviceForPath(c.rootFs)
|
dev, err := getDeviceForPath(c.rootFs)
|
||||||
|
|
||||||
if err == errMountPointNotFound {
|
if err == errMountPointNotFound {
|
||||||
|
@ -599,7 +599,25 @@ func (k *kataAgent) appendDevices(deviceList []*grpc.Device, devices []Device) [
|
|||||||
return deviceList
|
return deviceList
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *kataAgent) createContainer(pod *Pod, c *Container) (*Process, error) {
|
// rollbackFailingContainerCreation rolls back important steps that might have
|
||||||
|
// been performed before the container creation failed.
|
||||||
|
// - Unmount container volumes.
|
||||||
|
// - Unmount container rootfs.
|
||||||
|
func (k *kataAgent) rollbackFailingContainerCreation(c *Container, err error) {
|
||||||
|
if err != nil {
|
||||||
|
if c != nil {
|
||||||
|
if err2 := c.unmountHostMounts(); err2 != nil {
|
||||||
|
k.Logger().WithError(err2).Error("rollback failed unmountHostMounts()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err2 := bindUnmountContainerRootfs(kataHostSharedDir, c.pod.id, c.id); err2 != nil {
|
||||||
|
k.Logger().WithError(err2).Error("rollback failed bindUnmountContainerRootfs()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *kataAgent) createContainer(pod *Pod, c *Container) (p *Process, err error) {
|
||||||
ociSpecJSON, ok := c.config.Annotations[vcAnnotations.ConfigJSONKey]
|
ociSpecJSON, ok := c.config.Annotations[vcAnnotations.ConfigJSONKey]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errorMissingOCISpec
|
return nil, errorMissingOCISpec
|
||||||
@ -619,6 +637,10 @@ func (k *kataAgent) createContainer(pod *Pod, c *Container) (*Process, error) {
|
|||||||
rootPathParent := filepath.Join(kataGuestSharedDir, c.id)
|
rootPathParent := filepath.Join(kataGuestSharedDir, c.id)
|
||||||
rootPath := filepath.Join(rootPathParent, rootfsDir)
|
rootPath := filepath.Join(rootPathParent, rootfsDir)
|
||||||
|
|
||||||
|
// In case the container creation fails, the following defer statement
|
||||||
|
// takes care of rolling back actions previously performed.
|
||||||
|
defer k.rollbackFailingContainerCreation(c, err)
|
||||||
|
|
||||||
if c.state.Fstype != "" {
|
if c.state.Fstype != "" {
|
||||||
// This is a block based device rootfs.
|
// This is a block based device rootfs.
|
||||||
|
|
||||||
@ -667,27 +689,25 @@ func (k *kataAgent) createContainer(pod *Pod, c *Container) (*Process, error) {
|
|||||||
// (kataGuestSharedDir) is already mounted in the
|
// (kataGuestSharedDir) is already mounted in the
|
||||||
// guest. We only need to mount the rootfs from
|
// guest. We only need to mount the rootfs from
|
||||||
// the host and it will show up in the guest.
|
// the host and it will show up in the guest.
|
||||||
if err := bindMountContainerRootfs(kataHostSharedDir, pod.id, c.id, c.rootFs, false); err != nil {
|
if err = bindMountContainerRootfs(kataHostSharedDir, pod.id, c.id, c.rootFs, false); err != nil {
|
||||||
bindUnmountAllRootfs(kataHostSharedDir, *pod)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ociSpec := &specs.Spec{}
|
ociSpec := &specs.Spec{}
|
||||||
if err := json.Unmarshal([]byte(ociSpecJSON), ociSpec); err != nil {
|
if err = json.Unmarshal([]byte(ociSpecJSON), ociSpec); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle container mounts
|
// Handle container mounts
|
||||||
newMounts, err := c.mountSharedDirMounts(kataHostSharedDir, kataGuestSharedDir)
|
newMounts, err := c.mountSharedDirMounts(kataHostSharedDir, kataGuestSharedDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bindUnmountAllRootfs(kataHostSharedDir, *pod)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// We replace all OCI mount sources that match our container mount
|
// We replace all OCI mount sources that match our container mount
|
||||||
// with the right source path (The guest one).
|
// with the right source path (The guest one).
|
||||||
if err := k.replaceOCIMountSource(ociSpec, newMounts); err != nil {
|
if err = k.replaceOCIMountSource(ociSpec, newMounts); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -714,7 +734,7 @@ func (k *kataAgent) createContainer(pod *Pod, c *Container) (*Process, error) {
|
|||||||
OCI: grpcSpec,
|
OCI: grpcSpec,
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := k.sendReq(req); err != nil {
|
if _, err = k.sendReq(req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user