Skip to content

Commit 5dc2082

Browse files
authored
Merge pull request #1540 from allencloud/refactor-container
refactor: make code more encapsulate and logic simple
2 parents 1ac9207 + b126b02 commit 5dc2082

File tree

3 files changed

+173
-122
lines changed

3 files changed

+173
-122
lines changed

daemon/mgr/container.go

Lines changed: 157 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -451,24 +451,16 @@ func (mgr *ContainerManager) Start(ctx context.Context, id, detachKeys string) (
451451

452452
func (mgr *ContainerManager) start(ctx context.Context, c *Container, detachKeys string) error {
453453
var err error
454-
455-
c.Lock()
456-
if c.Config == nil || c.State == nil {
457-
c.Unlock()
458-
return errors.Wrapf(errtypes.ErrNotfound, "container %s", c.ID)
459-
}
460454
c.DetachKeys = detachKeys
461455

462-
// attach volume
463456
attachedVolumes := map[string]struct{}{}
464457
defer func() {
465-
if err != nil {
466-
for name := range attachedVolumes {
467-
_, err = mgr.VolumeMgr.Detach(ctx, name, map[string]string{volumetypes.OptionRef: c.ID})
468-
if err != nil {
469-
logrus.Errorf("failed to detach volume(%s) when start container(%s) rollback",
470-
name, c.ID)
471-
}
458+
if err == nil {
459+
return
460+
}
461+
for name := range attachedVolumes {
462+
if _, err = mgr.VolumeMgr.Detach(ctx, name, map[string]string{volumetypes.OptionRef: c.ID}); err != nil {
463+
logrus.Errorf("failed to detach volume(%s) when start container(%s) rollback: %v", name, c.ID, err)
472464
}
473465
}
474466
}()
@@ -477,23 +469,33 @@ func (mgr *ContainerManager) start(ctx context.Context, c *Container, detachKeys
477469
if mp.Name == "" {
478470
continue
479471
}
480-
481-
_, err = mgr.VolumeMgr.Attach(ctx, mp.Name, map[string]string{volumetypes.OptionRef: c.ID})
482-
if err != nil {
483-
c.Unlock()
472+
if _, err = mgr.VolumeMgr.Attach(ctx, mp.Name, map[string]string{volumetypes.OptionRef: c.ID}); err != nil {
484473
return errors.Wrapf(err, "failed to attach volume(%s)", mp.Name)
485474
}
486475
attachedVolumes[mp.Name] = struct{}{}
487476
}
488477

489-
// initialise container network mode
478+
if err = mgr.prepareContainerNetwork(ctx, c); err != nil {
479+
return err
480+
}
481+
482+
if err = mgr.createContainerdContainer(ctx, c); err != nil {
483+
return errors.Wrapf(err, "failed to create container(%s) on containerd", c.ID)
484+
}
485+
486+
return nil
487+
}
488+
489+
func (mgr *ContainerManager) prepareContainerNetwork(ctx context.Context, c *Container) error {
490+
c.Lock()
491+
defer c.Unlock()
492+
490493
networkMode := c.HostConfig.NetworkMode
491494

492495
if IsContainer(networkMode) {
493496
var origContainer *Container
494-
origContainer, err = mgr.Get(ctx, strings.SplitN(networkMode, ":", 2)[1])
497+
origContainer, err := mgr.Get(ctx, strings.SplitN(networkMode, ":", 2)[1])
495498
if err != nil {
496-
c.Unlock()
497499
return err
498500
}
499501

@@ -502,42 +504,37 @@ func (mgr *ContainerManager) start(ctx context.Context, c *Container, detachKeys
502504
c.ResolvConfPath = origContainer.ResolvConfPath
503505
c.Config.Hostname = origContainer.Config.Hostname
504506
c.Config.Domainname = origContainer.Config.Domainname
505-
} else {
506-
// initialise host network mode
507-
if IsHost(networkMode) {
508-
var hostname string
509-
hostname, err = os.Hostname()
510-
if err != nil {
511-
c.Unlock()
512-
return err
513-
}
514-
c.Config.Hostname = strfmt.Hostname(hostname)
515-
}
516507

517-
// build the network related path.
518-
if err = mgr.buildNetworkRelatedPath(c); err != nil {
519-
c.Unlock()
508+
return nil
509+
}
510+
511+
// initialise host network mode
512+
if IsHost(networkMode) {
513+
hostname, err := os.Hostname()
514+
if err != nil {
520515
return err
521516
}
517+
c.Config.Hostname = strfmt.Hostname(hostname)
518+
}
522519

523-
// initialise network endpoint
524-
if c.NetworkSettings != nil {
525-
for name, endpointSetting := range c.NetworkSettings.Networks {
526-
endpoint := mgr.buildContainerEndpoint(c)
527-
endpoint.Name = name
528-
endpoint.EndpointConfig = endpointSetting
529-
if _, err = mgr.NetworkMgr.EndpointCreate(ctx, endpoint); err != nil {
530-
logrus.Errorf("failed to create endpoint: %v", err)
531-
c.Unlock()
532-
return err
533-
}
534-
}
535-
}
520+
// build the network related path.
521+
if err := mgr.buildNetworkRelatedPath(c); err != nil {
522+
return err
536523
}
537-
c.Unlock()
538524

539-
if err = mgr.createContainerdContainer(ctx, c); err != nil {
540-
return errors.Wrapf(err, "failed to create container(%s) on containerd", c.ID)
525+
// initialise network endpoint
526+
if c.NetworkSettings == nil {
527+
return nil
528+
}
529+
530+
for name, endpointSetting := range c.NetworkSettings.Networks {
531+
endpoint := mgr.buildContainerEndpoint(c)
532+
endpoint.Name = name
533+
endpoint.EndpointConfig = endpointSetting
534+
if _, err := mgr.NetworkMgr.EndpointCreate(ctx, endpoint); err != nil {
535+
logrus.Errorf("failed to create endpoint: %v", err)
536+
return err
537+
}
541538
}
542539

543540
return nil
@@ -937,35 +934,38 @@ func (mgr *ContainerManager) Remove(ctx context.Context, name string, options *t
937934

938935
// if the container is running, force to stop it.
939936
if c.IsRunning() && options.Force {
940-
msg, err := mgr.Client.DestroyContainer(ctx, c.ID, c.StopTimeout())
937+
_, err := mgr.Client.DestroyContainer(ctx, c.ID, c.StopTimeout())
941938
if err != nil && !errtypes.IsNotfound(err) {
942-
return errors.Wrapf(err, "failed to destroy container %s", c.ID)
939+
return errors.Wrapf(err, "failed to destroy container %s when removing", c.ID)
943940
}
944-
if err := mgr.markStoppedAndRelease(c, msg); err != nil {
945-
return errors.Wrapf(err, "failed to mark container %s stop status", c.ID)
941+
// After stopping a running container, we should release container resource
942+
c.UnsetMergedDir()
943+
if err := mgr.releaseContainerResources(c); err != nil {
944+
logrus.Errorf("failed to release container %s resources when removing: %v", c.ID, err)
946945
}
947946
}
948947

949948
if err := mgr.detachVolumes(ctx, c, options.Volumes); err != nil {
950949
logrus.Errorf("failed to detach volume: %v", err)
951950
}
952951

953-
// remove name
954-
c.Lock()
955-
mgr.NameToID.Remove(c.Name)
956-
c.Unlock()
957-
958-
// remove meta data
959-
if err := mgr.Store.Remove(c.Key()); err != nil {
960-
logrus.Errorf("failed to remove container %s from meta store: %v", c.ID, err)
952+
// remove snapshot
953+
if err := mgr.Client.RemoveSnapshot(ctx, c.ID); err != nil {
954+
logrus.Errorf("failed to remove snapshot of container %s: %v", c.ID, err)
961955
}
962956

957+
// When removing a container, we have set up such rule for object removing sequences:
958+
// 1. container object in pouchd's memory;
959+
// 2. meta.json for container in local disk.
960+
961+
// remove name
962+
mgr.NameToID.Remove(c.Name)
963963
// remove container cache
964964
mgr.cache.Remove(c.ID)
965965

966-
// remove snapshot
967-
if err := mgr.Client.RemoveSnapshot(ctx, c.ID); err != nil {
968-
logrus.Errorf("failed to remove snapshot of container %s: %v", c.ID, err)
966+
// remove meta.json for container in local disk
967+
if err := mgr.Store.Remove(c.Key()); err != nil {
968+
logrus.Errorf("failed to remove container %s from meta store: %v", c.ID, err)
969969
}
970970

971971
return nil
@@ -1718,14 +1718,6 @@ func (mgr *ContainerManager) markStoppedAndRelease(c *Container, m *ctrd.Message
17181718

17191719
c.SetStatusStopped(code, errMsg)
17201720

1721-
// unset Snapshot MergedDir. Stop a container will
1722-
// delete the containerd container, the merged dir
1723-
// will also be deleted, so we should unset the
1724-
// container's MergedDir.
1725-
if c.Snapshotter != nil && c.Snapshotter.Data != nil {
1726-
c.Snapshotter.Data["MergedDir"] = ""
1727-
}
1728-
17291721
// Action Container Remove and function markStoppedAndRelease are conflict.
17301722
// If a container has been removed and the corresponding meta.json will be removed as well.
17311723
// However, when this function markStoppedAndRelease still keeps the container instance,
@@ -1738,47 +1730,52 @@ func (mgr *ContainerManager) markStoppedAndRelease(c *Container, m *ctrd.Message
17381730

17391731
// Remove io and network config may occur error, so we should update
17401732
// container's status on disk as soon as possible.
1741-
if err := c.Write(mgr.Store); err != nil {
1742-
logrus.Errorf("failed to update meta: %v", err)
1743-
return err
1744-
}
1733+
defer func() {
1734+
if err := c.Write(mgr.Store); err != nil {
1735+
logrus.Errorf("failed to update meta: %v", err)
1736+
}
1737+
}()
17451738

1746-
// release resource
1747-
if io := mgr.IOs.Get(c.ID); io != nil {
1748-
io.Close()
1749-
mgr.IOs.Remove(c.ID)
1739+
c.UnsetMergedDir()
1740+
1741+
return mgr.releaseContainerResources(c)
1742+
}
1743+
1744+
func (mgr *ContainerManager) markExitedAndRelease(c *Container, m *ctrd.Message) error {
1745+
var (
1746+
exitCode int64 // container exit code used for container state setting
1747+
errMsg string // container exit error message used for container state setting
1748+
)
1749+
if m != nil {
1750+
exitCode = int64(m.ExitCode())
1751+
if err := m.RawError(); err != nil {
1752+
errMsg = err.Error()
1753+
}
17501754
}
17511755

1752-
// No network binded, just return
1753-
c.Lock()
1754-
if c.NetworkSettings == nil {
1755-
c.Unlock()
1756+
c.SetStatusExited(exitCode, errMsg)
1757+
1758+
// Action Container Remove and function markStoppedAndRelease are conflict.
1759+
// If a container has been removed and the corresponding meta.json will be removed as well.
1760+
// However, when this function markStoppedAndRelease still keeps the container instance,
1761+
// there will be possibility that in markStoppedAndRelease, code calls c.Write(mgr.Store) to
1762+
// write the removed meta.json again. If that, incompatibilty happens.
1763+
// As a result, we check whether this container is still in the meta store.
1764+
if container, err := mgr.container(c.Name); err != nil || container == nil {
17561765
return nil
17571766
}
17581767

1759-
for name, epConfig := range c.NetworkSettings.Networks {
1760-
endpoint := mgr.buildContainerEndpoint(c)
1761-
endpoint.Name = name
1762-
endpoint.EndpointConfig = epConfig
1763-
if err := mgr.NetworkMgr.EndpointRemove(context.Background(), endpoint); err != nil {
1764-
// TODO(ziren): it is a trick, we should wrapper "sanbox
1765-
// not found"" as an error type
1766-
if !strings.Contains(err.Error(), "not found") {
1767-
logrus.Errorf("failed to remove endpoint: %v", err)
1768-
c.Unlock()
1769-
return err
1770-
}
1768+
// Remove io and network config may occur error, so we should update
1769+
// container's status on disk as soon as possible.
1770+
defer func() {
1771+
if err := c.Write(mgr.Store); err != nil {
1772+
logrus.Errorf("failed to update meta: %v", err)
17711773
}
1772-
}
1773-
c.Unlock()
1774+
}()
17741775

1775-
// update meta
1776-
if err := c.Write(mgr.Store); err != nil {
1777-
logrus.Errorf("failed to update meta of container %s: %v", c.ID, err)
1778-
return err
1779-
}
1776+
c.UnsetMergedDir()
17801777

1781-
return nil
1778+
return mgr.releaseContainerResources(c)
17821779
}
17831780

17841781
// exitedAndRelease be register into ctrd as a callback function, when the running container suddenly
@@ -1789,12 +1786,10 @@ func (mgr *ContainerManager) exitedAndRelease(id string, m *ctrd.Message) error
17891786
return err
17901787
}
17911788

1792-
if err := mgr.markStoppedAndRelease(c, m); err != nil {
1789+
if err := mgr.markExitedAndRelease(c, m); err != nil {
17931790
return err
17941791
}
17951792

1796-
c.SetStatusExited()
1797-
17981793
// Action Container Remove and function exitedAndRelease are conflict.
17991794
// If a container has been removed and the corresponding meta.json will be removed as well.
18001795
// However, when this function exitedAndRelease still keeps the container instance,
@@ -1805,11 +1800,6 @@ func (mgr *ContainerManager) exitedAndRelease(id string, m *ctrd.Message) error
18051800
return nil
18061801
}
18071802

1808-
if err := c.Write(mgr.Store); err != nil {
1809-
logrus.Errorf("failed to update meta: %v", err)
1810-
return err
1811-
}
1812-
18131803
// send exit event to monitor
18141804
mgr.monitor.PostEvent(ContainerExitEvent(c).WithHandle(func(c *Container) error {
18151805
// check status and restart policy
@@ -1847,19 +1837,65 @@ func (mgr *ContainerManager) execExitedAndRelease(id string, m *ctrd.Message) er
18471837
execConfig.Running = false
18481838
execConfig.Error = m.RawError()
18491839

1850-
if io := mgr.IOs.Get(id); io != nil {
1851-
if err := m.RawError(); err != nil {
1852-
fmt.Fprintf(io.Stdout, "%v\n", err)
1853-
}
1840+
io := mgr.IOs.Get(id)
1841+
if io == nil {
1842+
return nil
1843+
}
1844+
1845+
if err := m.RawError(); err != nil {
1846+
fmt.Fprintf(io.Stdout, "%v\n", err)
1847+
}
1848+
1849+
// close io
1850+
io.Close()
1851+
mgr.IOs.Remove(id)
1852+
1853+
return nil
1854+
}
1855+
1856+
func (mgr *ContainerManager) releaseContainerResources(c *Container) error {
1857+
mgr.releaseContainerIOs(c.ID)
1858+
return mgr.releaseContainerNetwork(c)
1859+
}
1860+
1861+
// releaseContainerNetwork release container network when container exits or is stopped.
1862+
func (mgr *ContainerManager) releaseContainerNetwork(c *Container) error {
1863+
c.Lock()
1864+
defer c.Unlock()
1865+
if c.NetworkSettings == nil {
1866+
return nil
1867+
}
18541868

1855-
// close io
1856-
io.Close()
1857-
mgr.IOs.Remove(id)
1869+
for name, epConfig := range c.NetworkSettings.Networks {
1870+
endpoint := mgr.buildContainerEndpoint(c)
1871+
endpoint.Name = name
1872+
endpoint.EndpointConfig = epConfig
1873+
if err := mgr.NetworkMgr.EndpointRemove(context.Background(), endpoint); err != nil {
1874+
// TODO(ziren): it is a trick, we should wrapper "sanbox
1875+
// not found"" as an error type
1876+
if !strings.Contains(err.Error(), "not found") {
1877+
logrus.Errorf("failed to remove endpoint: %v", err)
1878+
return err
1879+
}
1880+
}
18581881
}
18591882

18601883
return nil
18611884
}
18621885

1886+
// releaseContainerIOs releases container IO resources.
1887+
func (mgr *ContainerManager) releaseContainerIOs(containerID string) {
1888+
// release resource
1889+
io := mgr.IOs.Get(containerID)
1890+
if io == nil {
1891+
return
1892+
}
1893+
1894+
io.Close()
1895+
mgr.IOs.Remove(containerID)
1896+
return
1897+
}
1898+
18631899
func (mgr *ContainerManager) attachVolume(ctx context.Context, name string, c *Container) (string, string, error) {
18641900
driver := volumetypes.DefaultBackend
18651901
v, err := mgr.VolumeMgr.Get(ctx, name)

0 commit comments

Comments
 (0)