|
@@ -102,71 +102,13 @@ func (c *convergence) apply(ctx context.Context, project *types.Project, options
|
|
|
if utils.StringContains(options.Services, name) {
|
|
if utils.StringContains(options.Services, name) {
|
|
|
strategy = options.Recreate
|
|
strategy = options.Recreate
|
|
|
}
|
|
}
|
|
|
- err = c.ensureService(ctx, project, service, strategy, options.Inherit, options.Timeout)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return err
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- c.updateProject(project, name)
|
|
|
|
|
- return nil
|
|
|
|
|
|
|
+ return c.ensureService(ctx, project, service, strategy, options.Inherit, options.Timeout)
|
|
|
})(ctx)
|
|
})(ctx)
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
var mu sync.Mutex
|
|
var mu sync.Mutex
|
|
|
|
|
|
|
|
-// updateProject updates project after service converged, so dependent services relying on `service:xx` can refer to actual containers.
|
|
|
|
|
-func (c *convergence) updateProject(project *types.Project, serviceName string) {
|
|
|
|
|
- // operation is protected by a Mutex so that we can safely update project.Services while running concurrent convergence on services
|
|
|
|
|
- mu.Lock()
|
|
|
|
|
- defer mu.Unlock()
|
|
|
|
|
-
|
|
|
|
|
- cnts := c.getObservedState(serviceName)
|
|
|
|
|
- for i, s := range project.Services {
|
|
|
|
|
- updateServices(&s, cnts)
|
|
|
|
|
- project.Services[i] = s
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-func updateServices(service *types.ServiceConfig, cnts Containers) {
|
|
|
|
|
- if len(cnts) == 0 {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- for _, str := range []*string{&service.NetworkMode, &service.Ipc, &service.Pid} {
|
|
|
|
|
- if d := getDependentServiceFromMode(*str); d != "" {
|
|
|
|
|
- if serviceContainers := cnts.filter(isService(d)); len(serviceContainers) > 0 {
|
|
|
|
|
- *str = types.NetworkModeContainerPrefix + serviceContainers[0].ID
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- var links []string
|
|
|
|
|
- for _, serviceLink := range service.Links {
|
|
|
|
|
- parts := strings.Split(serviceLink, ":")
|
|
|
|
|
- serviceName := serviceLink
|
|
|
|
|
- serviceAlias := ""
|
|
|
|
|
- if len(parts) == 2 {
|
|
|
|
|
- serviceName = parts[0]
|
|
|
|
|
- serviceAlias = parts[1]
|
|
|
|
|
- }
|
|
|
|
|
- if serviceName != service.Name {
|
|
|
|
|
- links = append(links, serviceLink)
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
- for _, container := range cnts {
|
|
|
|
|
- name := getCanonicalContainerName(container)
|
|
|
|
|
- if serviceAlias != "" {
|
|
|
|
|
- links = append(links,
|
|
|
|
|
- fmt.Sprintf("%s:%s", name, serviceAlias))
|
|
|
|
|
- }
|
|
|
|
|
- links = append(links,
|
|
|
|
|
- fmt.Sprintf("%s:%s", name, name),
|
|
|
|
|
- fmt.Sprintf("%s:%s", name, getContainerNameWithoutProject(container)))
|
|
|
|
|
- }
|
|
|
|
|
- service.Links = links
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
func (c *convergence) ensureService(ctx context.Context, project *types.Project, service types.ServiceConfig, recreate string, inherit bool, timeout *time.Duration) error {
|
|
func (c *convergence) ensureService(ctx context.Context, project *types.Project, service types.ServiceConfig, recreate string, inherit bool, timeout *time.Duration) error {
|
|
|
expected, err := getScale(service)
|
|
expected, err := getScale(service)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -178,7 +120,7 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
|
|
|
|
|
|
|
|
eg, _ := errgroup.WithContext(ctx)
|
|
eg, _ := errgroup.WithContext(ctx)
|
|
|
|
|
|
|
|
- err = c.resolveVolumeFrom(&service)
|
|
|
|
|
|
|
+ err = c.resolveServiceReferences(&service)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
@@ -263,6 +205,20 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// resolveServiceReferences replaces reference to another service with reference to an actual container
|
|
|
|
|
+func (c *convergence) resolveServiceReferences(service *types.ServiceConfig) error {
|
|
|
|
|
+ err := c.resolveVolumeFrom(service)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ err = c.resolveSharedNamespaces(service)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func (c *convergence) resolveVolumeFrom(service *types.ServiceConfig) error {
|
|
func (c *convergence) resolveVolumeFrom(service *types.ServiceConfig) error {
|
|
|
for i, vol := range service.VolumesFrom {
|
|
for i, vol := range service.VolumesFrom {
|
|
|
spec := strings.Split(vol, ":")
|
|
spec := strings.Split(vol, ":")
|
|
@@ -283,6 +239,37 @@ func (c *convergence) resolveVolumeFrom(service *types.ServiceConfig) error {
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func (c *convergence) resolveSharedNamespaces(service *types.ServiceConfig) error {
|
|
|
|
|
+ str := service.NetworkMode
|
|
|
|
|
+ if name := getDependentServiceFromMode(str); name != "" {
|
|
|
|
|
+ dependencies := c.getObservedState(name)
|
|
|
|
|
+ if len(dependencies) == 0 {
|
|
|
|
|
+ return fmt.Errorf("cannot share network namespace with service %s: container missing", name)
|
|
|
|
|
+ }
|
|
|
|
|
+ service.NetworkMode = types.ContainerPrefix + dependencies.sorted()[0].ID
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ str = service.Ipc
|
|
|
|
|
+ if name := getDependentServiceFromMode(str); name != "" {
|
|
|
|
|
+ dependencies := c.getObservedState(name)
|
|
|
|
|
+ if len(dependencies) == 0 {
|
|
|
|
|
+ return fmt.Errorf("cannot share IPC namespace with service %s: container missing", name)
|
|
|
|
|
+ }
|
|
|
|
|
+ service.Ipc = types.ContainerPrefix + dependencies.sorted()[0].ID
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ str = service.Pid
|
|
|
|
|
+ if name := getDependentServiceFromMode(str); name != "" {
|
|
|
|
|
+ dependencies := c.getObservedState(name)
|
|
|
|
|
+ if len(dependencies) == 0 {
|
|
|
|
|
+ return fmt.Errorf("cannot share PID namespace with service %s: container missing", name)
|
|
|
|
|
+ }
|
|
|
|
|
+ service.Pid = types.ContainerPrefix + dependencies.sorted()[0].ID
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func mustRecreate(expected types.ServiceConfig, actual moby.Container, policy string) (bool, error) {
|
|
func mustRecreate(expected types.ServiceConfig, actual moby.Container, policy string) (bool, error) {
|
|
|
if policy == api.RecreateNever {
|
|
if policy == api.RecreateNever {
|
|
|
return false, nil
|
|
return false, nil
|