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 {
s, err := project.GetServices(services)
s, err := project.GetServices(services...)
if err != nil {
return nil, err
}

View File

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

2
go.mod
View File

@ -17,7 +17,7 @@ require (
github.com/awslabs/goformation/v4 v4.15.6
github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129
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/containerd v1.4.3
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/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/compose-spec/compose-go v0.0.0-20210217144939-9f2c61fe6b14 h1:ezR3VeA9GLPRMXYC0JzuHOM6c9yHMbc6EHaZy6lEKRA=
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 h1:cGJa3EMDcclDU21e/CVQJnDf3ZjnB6HN9TvxkFHpGq8=
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-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
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"
)
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)
if len(actual) < scale {
next, err := nextContainerNumber(actual)
@ -75,15 +85,8 @@ func (s *composeService) ensureScale(ctx context.Context, actual []moby.Containe
return eg, actual, nil
}
func (s *composeService) ensureService(ctx context.Context, observedState Containers, project *types.Project, service types.ServiceConfig, recreate string) error {
actual := observedState.filter(isService(service.Name))
scale, err := getScale(service)
if err != nil {
return err
}
eg, actual, err := s.ensureScale(ctx, actual, scale, project, service)
func (s *composeService) ensureService(ctx context.Context, project *types.Project, service types.ServiceConfig, recreate string) error {
eg, actual, err := s.ensureScale(ctx, project, service)
if err != nil {
return err
}
@ -260,7 +263,12 @@ func (s *composeService) restartContainer(ctx context.Context, container moby.Co
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)
if err != nil {
return err
@ -269,10 +277,14 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
if err != nil {
return err
}
id := created.ID
createdContainer := moby.Container{
ID: created.ID,
Labels: containerConfig.Labels,
}
cState.Add(createdContainer)
for netName := range service.Networks {
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 {
return err
}

View File

@ -70,6 +70,8 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt
if err != nil {
return err
}
containerState := NewContainersState(observedState)
ctx = context.WithValue(ctx, ContainersKey{}, containerState)
allServices := project.AllServices()
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 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 {
for _, network := range networks {
err := s.ensureNetwork(ctx, network)
@ -235,7 +260,11 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
portBindings := buildContainerPortBindingOptions(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{
AutoRemove: autoRemove,
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,
inherit *moby.Container) (map[string]struct{}, []string, []mount.Mount, error) {
var mounts = []mount.Mount{}
@ -602,32 +639,42 @@ func getAliases(s types.ServiceConfig, c *types.ServiceNetworkConfig) []string {
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
if mode == "" {
if len(p.Networks) > 0 {
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
}
// FIXME incomplete implementation
if strings.HasPrefix(mode, "service:") {
panic("Not yet implemented")
depServiceNetworkMode := getDependentServiceByNetwork(service.NetworkMode)
if depServiceNetworkMode != "" {
depServiceContainers := observedState.filter(isService(depServiceNetworkMode))
if len(depServiceContainers) > 0 {
return container.NetworkMode(types.NetworkModeContainerPrefix + depServiceContainers[0].ID), nil
}
return container.NetworkMode("none"),
fmt.Errorf(`no containers started for network_mode %q in service %q -> %v`,
mode, service.Name, observedState)
}
if strings.HasPrefix(mode, "container:") {
panic("Not yet implemented")
}
return container.NetworkMode(mode)
return container.NetworkMode(mode), nil
}
func getNetworksForService(s types.ServiceConfig) map[string]*types.ServiceNetworkConfig {
if len(s.Networks) > 0 {
return s.Networks
}
if s.NetworkMode != "" {
return 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) {
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)
if err != nil {
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:
mydb:
image: mysql
network_mode: "service:db"
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
db:
image: gtardif/sentences-db
networks: