Add service based network_mode

Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
This commit is contained in:
Ulysses Souza 2021-02-09 10:22:04 -03:00
parent ea24e499e6
commit 83cc63c8ae
9 changed files with 208 additions and 32 deletions

View File

@ -69,7 +69,7 @@ func (o *projectOptions) toProject(services []string) (*types.Project, error) {
} }
if len(services) != 0 { if len(services) != 0 {
s, err := project.GetServices(services) s, err := project.GetServices(services...)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -32,7 +32,7 @@ func TestFilterServices(t *testing.T) {
}, },
{ {
Name: "bar", Name: "bar",
NetworkMode: "service:zot", NetworkMode: types.NetworkModeServicePrefix + "zot",
}, },
{ {
Name: "zot", Name: "zot",

2
go.mod
View File

@ -17,7 +17,7 @@ require (
github.com/awslabs/goformation/v4 v4.15.6 github.com/awslabs/goformation/v4 v4.15.6
github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129
github.com/cnabio/cnab-to-oci v0.3.1-beta1 github.com/cnabio/cnab-to-oci v0.3.1-beta1
github.com/compose-spec/compose-go v0.0.0-20210217144939-9f2c61fe6b14 github.com/compose-spec/compose-go v0.0.0-20210218184709-a75bbdcff7f3
github.com/containerd/console v1.0.1 github.com/containerd/console v1.0.1
github.com/containerd/containerd v1.4.3 github.com/containerd/containerd v1.4.3
github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a // indirect github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a // indirect

4
go.sum
View File

@ -302,8 +302,8 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/compose-spec/compose-go v0.0.0-20210217144939-9f2c61fe6b14 h1:ezR3VeA9GLPRMXYC0JzuHOM6c9yHMbc6EHaZy6lEKRA= github.com/compose-spec/compose-go v0.0.0-20210218184709-a75bbdcff7f3 h1:cGJa3EMDcclDU21e/CVQJnDf3ZjnB6HN9TvxkFHpGq8=
github.com/compose-spec/compose-go v0.0.0-20210217144939-9f2c61fe6b14/go.mod h1:flNthwF3kg+JioxATZWSsuuA2N3zGGwggDNNGoE9PHA= github.com/compose-spec/compose-go v0.0.0-20210218184709-a75bbdcff7f3/go.mod h1:flNthwF3kg+JioxATZWSsuuA2N3zGGwggDNNGoE9PHA=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340 h1:9atoWyI9RtXFwf7UDbme/6M8Ud0rFrx+Q3ZWgSnsxtw= github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340 h1:9atoWyI9RtXFwf7UDbme/6M8Ud0rFrx+Q3ZWgSnsxtw=

View File

@ -42,7 +42,17 @@ const (
"Remove the custom name to scale the service.\n" "Remove the custom name to scale the service.\n"
) )
func (s *composeService) ensureScale(ctx context.Context, actual []moby.Container, scale int, project *types.Project, service types.ServiceConfig) (*errgroup.Group, []moby.Container, error) { func (s *composeService) ensureScale(ctx context.Context, project *types.Project, service types.ServiceConfig) (*errgroup.Group, []moby.Container, error) {
cState, err := GetContextContainerState(ctx)
if err != nil {
return nil, nil, err
}
observedState := cState.GetContainers()
actual := observedState.filter(isService(service.Name))
scale, err := getScale(service)
if err != nil {
return nil, nil, err
}
eg, _ := errgroup.WithContext(ctx) eg, _ := errgroup.WithContext(ctx)
if len(actual) < scale { if len(actual) < scale {
next, err := nextContainerNumber(actual) next, err := nextContainerNumber(actual)
@ -75,15 +85,8 @@ func (s *composeService) ensureScale(ctx context.Context, actual []moby.Containe
return eg, actual, nil return eg, actual, nil
} }
func (s *composeService) ensureService(ctx context.Context, observedState Containers, project *types.Project, service types.ServiceConfig, recreate string) error { func (s *composeService) ensureService(ctx context.Context, project *types.Project, service types.ServiceConfig, recreate string) error {
actual := observedState.filter(isService(service.Name)) eg, actual, err := s.ensureScale(ctx, project, service)
scale, err := getScale(service)
if err != nil {
return err
}
eg, actual, err := s.ensureScale(ctx, actual, scale, project, service)
if err != nil { if err != nil {
return err return err
} }
@ -260,7 +263,12 @@ func (s *composeService) restartContainer(ctx context.Context, container moby.Co
return nil return nil
} }
func (s *composeService) createMobyContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, name string, number int, container *moby.Container, autoRemove bool) error { func (s *composeService) createMobyContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, name string, number int, container *moby.Container,
autoRemove bool) error {
cState, err := GetContextContainerState(ctx)
if err != nil {
return err
}
containerConfig, hostConfig, networkingConfig, err := s.getCreateOptions(ctx, project, service, number, container, autoRemove) containerConfig, hostConfig, networkingConfig, err := s.getCreateOptions(ctx, project, service, number, container, autoRemove)
if err != nil { if err != nil {
return err return err
@ -269,10 +277,14 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
if err != nil { if err != nil {
return err return err
} }
id := created.ID createdContainer := moby.Container{
ID: created.ID,
Labels: containerConfig.Labels,
}
cState.Add(createdContainer)
for netName := range service.Networks { for netName := range service.Networks {
netwrk := project.Networks[netName] netwrk := project.Networks[netName]
err = s.connectContainerToNetwork(ctx, id, netwrk.Name, service.Name, getContainerName(project.Name, service, number)) err = s.connectContainerToNetwork(ctx, created.ID, netwrk.Name, service.Name, getContainerName(project.Name, service, number))
if err != nil { if err != nil {
return err return err
} }

View File

@ -70,6 +70,8 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt
if err != nil { if err != nil {
return err return err
} }
containerState := NewContainersState(observedState)
ctx = context.WithValue(ctx, ContainersKey{}, containerState)
allServices := project.AllServices() allServices := project.AllServices()
allServiceNames := []string{} allServiceNames := []string{}
@ -92,8 +94,10 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt
} }
} }
prepareNetworkMode(project)
return InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error { return InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
return s.ensureService(c, observedState, project, service, opts.Recreate) return s.ensureService(c, project, service, opts.Recreate)
}) })
} }
@ -129,6 +133,27 @@ func prepareNetworks(project *types.Project) {
} }
} }
func prepareNetworkMode(p *types.Project) {
outLoop:
for i := range p.Services {
dependency := getDependentServiceByNetwork(p.Services[i].NetworkMode)
if dependency == "" {
continue
}
if p.Services[i].DependsOn == nil {
p.Services[i].DependsOn = make(types.DependsOnConfig)
}
for _, service := range p.Services {
if service.Name == dependency {
p.Services[i].DependsOn[service.Name] = types.ServiceDependency{
Condition: types.ServiceConditionStarted,
}
continue outLoop
}
}
}
}
func (s *composeService) ensureNetworks(ctx context.Context, networks types.Networks) error { func (s *composeService) ensureNetworks(ctx context.Context, networks types.Networks) error {
for _, network := range networks { for _, network := range networks {
err := s.ensureNetwork(ctx, network) err := s.ensureNetwork(ctx, network)
@ -235,7 +260,11 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
portBindings := buildContainerPortBindingOptions(service) portBindings := buildContainerPortBindingOptions(service)
resources := getDeployResources(service) resources := getDeployResources(service)
networkMode := getNetworkMode(p, service)
networkMode, err := getNetworkMode(ctx, p, service)
if err != nil {
return nil, nil, nil, err
}
hostConfig := container.HostConfig{ hostConfig := container.HostConfig{
AutoRemove: autoRemove, AutoRemove: autoRemove,
Binds: binds, Binds: binds,
@ -335,6 +364,14 @@ func getVolumesFrom(project *types.Project, volumesFrom []string) ([]string, []s
} }
func getDependentServiceByNetwork(networkMode string) string {
baseService := ""
if strings.HasPrefix(networkMode, types.NetworkModeServicePrefix) {
return networkMode[len(types.NetworkModeServicePrefix):]
}
return baseService
}
func (s *composeService) buildContainerVolumes(ctx context.Context, p types.Project, service types.ServiceConfig, func (s *composeService) buildContainerVolumes(ctx context.Context, p types.Project, service types.ServiceConfig,
inherit *moby.Container) (map[string]struct{}, []string, []mount.Mount, error) { inherit *moby.Container) (map[string]struct{}, []string, []mount.Mount, error) {
var mounts = []mount.Mount{} var mounts = []mount.Mount{}
@ -602,32 +639,42 @@ func getAliases(s types.ServiceConfig, c *types.ServiceNetworkConfig) []string {
return aliases return aliases
} }
func getNetworkMode(p *types.Project, service types.ServiceConfig) container.NetworkMode { func getNetworkMode(ctx context.Context, p *types.Project, service types.ServiceConfig) (container.NetworkMode, error) {
cState, err := GetContextContainerState(ctx)
if err != nil {
return container.NetworkMode("none"), nil
}
observedState := cState.GetContainers()
mode := service.NetworkMode mode := service.NetworkMode
if mode == "" { if mode == "" {
if len(p.Networks) > 0 { if len(p.Networks) > 0 {
for name := range getNetworksForService(service) { for name := range getNetworksForService(service) {
return container.NetworkMode(p.Networks[name].Name) return container.NetworkMode(p.Networks[name].Name), nil
} }
} }
return container.NetworkMode("none") return container.NetworkMode("none"), nil
} }
depServiceNetworkMode := getDependentServiceByNetwork(service.NetworkMode)
// FIXME incomplete implementation if depServiceNetworkMode != "" {
if strings.HasPrefix(mode, "service:") { depServiceContainers := observedState.filter(isService(depServiceNetworkMode))
panic("Not yet implemented") if len(depServiceContainers) > 0 {
return container.NetworkMode(types.NetworkModeContainerPrefix + depServiceContainers[0].ID), nil
} }
if strings.HasPrefix(mode, "container:") { return container.NetworkMode("none"),
panic("Not yet implemented") fmt.Errorf(`no containers started for network_mode %q in service %q -> %v`,
mode, service.Name, observedState)
} }
return container.NetworkMode(mode), nil
return container.NetworkMode(mode)
} }
func getNetworksForService(s types.ServiceConfig) map[string]*types.ServiceNetworkConfig { func getNetworksForService(s types.ServiceConfig) map[string]*types.ServiceNetworkConfig {
if len(s.Networks) > 0 { if len(s.Networks) > 0 {
return s.Networks return s.Networks
} }
if s.NetworkMode != "" {
return nil
}
return map[string]*types.ServiceNetworkConfig{"default": nil} return map[string]*types.ServiceNetworkConfig{"default": nil}
} }

View File

@ -30,6 +30,16 @@ import (
) )
func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) { func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) (int, error) {
observedState, err := s.apiClient.ContainerList(ctx, apitypes.ContainerListOptions{
Filters: filters.NewArgs(projectFilter(project.Name)),
All: true,
})
if err != nil {
return 0, err
}
containerState := NewContainersState(observedState)
ctx = context.WithValue(ctx, ContainersKey{}, containerState)
service, err := project.GetService(opts.Service) service, err := project.GetService(opts.Service)
if err != nil { if err != nil {
return 0, err return 0, err

102
local/compose/status.go Normal file
View File

@ -0,0 +1,102 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"context"
"github.com/docker/docker/api/types"
"github.com/pkg/errors"
)
// ContainersKey is the context key to access context value os a ContainersStatus
type ContainersKey struct{}
// ContainersState state management interface
type ContainersState interface {
Get(string) *types.Container
GetContainers() Containers
Add(c types.Container)
AddAll(cs Containers)
Remove(string) types.Container
}
// NewContainersState creates a new container state manager
func NewContainersState(cs Containers) ContainersState {
s := containersState{
observedContainers: &cs,
}
return &s
}
// ContainersStatus works as a collection container for the observed containers
type containersState struct {
observedContainers *Containers
}
func (s *containersState) AddAll(cs Containers) {
for _, c := range cs {
lValue := append(*s.observedContainers, c)
s.observedContainers = &lValue
}
}
func (s *containersState) Add(c types.Container) {
if s.Get(c.ID) == nil {
lValue := append(*s.observedContainers, c)
s.observedContainers = &lValue
}
}
func (s *containersState) Remove(id string) types.Container {
var c types.Container
var newObserved Containers
for _, o := range *s.observedContainers {
if o.ID != id {
c = o
continue
}
newObserved = append(newObserved, o)
}
s.observedContainers = &newObserved
return c
}
func (s *containersState) Get(id string) *types.Container {
for _, o := range *s.observedContainers {
if id == o.ID {
return &o
}
}
return nil
}
func (s *containersState) GetContainers() Containers {
if s.observedContainers != nil && *s.observedContainers != nil {
return *s.observedContainers
}
return make(Containers, 0)
}
// GetContextContainerState gets the container state manager
func GetContextContainerState(ctx context.Context) (ContainersState, error) {
cState, ok := ctx.Value(ContainersKey{}).(*containersState)
if !ok {
return nil, errors.New("containers' containersState not available in context")
}
return cState, nil
}

View File

@ -1,4 +1,9 @@
services: services:
mydb:
image: mysql
network_mode: "service:db"
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
db: db:
image: gtardif/sentences-db image: gtardif/sentences-db
networks: networks: