Skip to content
This repository has been archived by the owner on Jun 27, 2024. It is now read-only.

Commit

Permalink
feat: First version (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
lholota committed Mar 13, 2020
1 parent a208264 commit e32ecc7
Show file tree
Hide file tree
Showing 26 changed files with 545 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .github/settings.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# https://developer.github.com/v3/repos/#edit
repository:
name: docker-$$IMAGE_NAME$$
name: docker-swarm-local-network-connector
description: ""
homepage: https://homecentr.github.io/
private: false
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
pull_request:

env:
IMAGE_NAME: "homecentr/$$IMAGE_NAME$$"
IMAGE_NAME: "homecentr/swarm-local-network-connector"

jobs:
build:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
- master

env:
IMAGE_NAME: "homecentr/$$IMAGE_NAME$$"
IMAGE_NAME: "homecentr/swarm-local-network-connector"

jobs:
build:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/regular_scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
- cron: '0 6 * * *'

env:
IMAGE_NAME: "homecentr/$$IMAGE_NAME$$"
IMAGE_NAME: "homecentr/swarm-local-network-connector"

jobs:
build:
Expand Down
19 changes: 18 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1 +1,18 @@
FROM alpine
FROM golang:1.14.0-alpine as build

RUN apk add --no-cache git=2.24.1-r0

COPY ./src /go/src/github.com/homecentr/docker-swarm-local-network-connector

WORKDIR /go/src/github.com/homecentr/docker-swarm-local-network-connector

RUN go get ./... && \
go build

FROM alpine:3.11.3

COPY --from=build /go/src/github.com/homecentr/docker-swarm-local-network-connector/docker-swarm-local-network-connector /swarm-local-network-connector

RUN chmod a+x /swarm-local-network-connector

ENTRYPOINT [ "/swarm-local-network-connector" ]
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# HomeCentr - $$IMAGE_NAME$$
Template repository for Docker container repositories
# HomeCentr - swarm-local-network-connector
Connector process which monitors for newly created containers and connectes them to the network specified in the label. This is a workaround to solve the problem when Docker Swarm does not pass explicit IP address when connecting container to a macvlan network or other local scoped network drivers.

## Project status

Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
version: "3.7"
services:
$$IMAGE_NAME$$:
swarm-local-network-connector:
build: .
image: homecentr/$$IMAGE_NAME$$
image: homecentr/swarm-local-network-connector
restart: unless-stopped
6 changes: 6 additions & 0 deletions example/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: "3.7"
services:
nginx:
image: nginx
labels:
io.homecentr.local-networks: "[{ \"Network\": \"macvlan\", \"Config\": { \"IPAMConfig\": { \"IPV4Address\": \"192.168.2.199\" } } }]"
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"name": "homecentr-$$IMAGE_NAME$$",
"name": "homecentr-swarm-local-network-connector",
"version": "1.0.0",
"description": "",
"repository": {
"type": "git",
"url": "git+https://github.com/homecentr/docker-$$IMAGE_NAME$$.git"
"url": "git+https://github.com/homecentr/docker-swarm-local-network-connector.git"
},
"author": "",
"license": "MIT",
"bugs": {
"url": "https://github.com/homecentr/docker-$$IMAGE_NAME$$/issues"
"url": "https://github.com/homecentr/docker-swarm-local-network-connector/issues"
},
"homepage": "https://github.com/homecentr/docker-$$IMAGE_NAME$$#readme"
"homepage": "https://github.com/homecentr/docker-swarm-local-network-connector#readme"
}
19 changes: 19 additions & 0 deletions src/Connector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"context"

"github.com/docker/docker/client"
)

type Connector struct {
cli *client.Client
eventsContextCancel *context.CancelFunc
}

func NewConnector(cli *client.Client) *Connector {
result := new(Connector)
result.cli = cli

return result
}
22 changes: 22 additions & 0 deletions src/Connector_Events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package main

import (
"context"

"github.com/docker/docker/api/types"
)

func (c *Connector) listenToContainerEvents(ctx context.Context) error {
msgs, errs := c.cli.Events(ctx, types.EventsOptions{})

for {
select {
case err := <-errs:
print(err)
case msg := <-msgs:
if msg.Type == "container" && msg.Action == "create" {
c.handleContainer(ctx, msg.ID)
}
}
}
}
25 changes: 25 additions & 0 deletions src/Connector_ExistingContainers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import (
"context"

"github.com/docker/docker/api/types"
)

func (c *Connector) verifyExistingContainersConnected(ctx context.Context) error {
containers, err := c.cli.ContainerList(ctx, types.ContainerListOptions{})

if err != nil {
return err
}

for _, container := range containers {
err = c.handleContainer(ctx, container.ID)

if err != nil {
return err
}
}

return nil
}
74 changes: 74 additions & 0 deletions src/Connector_HandleContainer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package main

import (
"context"

"github.com/sirupsen/logrus"

types "github.com/docker/docker/api/types"
)

func (c *Connector) handleContainer(ctx context.Context, containerId string) error {
logrus.Debugf("Checking container %s", containerId)

json, err := c.cli.ContainerInspect(ctx, containerId)

if err != nil {
return err
}

networkConfigs := getNetworkConfiguration(&json)

if networkConfigs == nil {
logrus.Debugf("The container %s does not have a valid label, skipping...", containerId)
return nil
}

logrus.Infof("Found valid label on container %s", containerId)

for _, networkConfig := range networkConfigs {
if networkConfig.NetworkName == "" {
logrus.Warnf("Skipping entry with empty network name on container %s.", containerId)
continue
}

networkId, err := getNetworkId(c.cli, ctx, networkConfig.NetworkName)

if err != nil {
logrus.Warnf("Network with name %s does not exist, skipping this network.", networkConfig.NetworkName)
continue
}

if isContainerConnectedToNetwork(&json, networkId) {
logrus.Debugf("Container %s is already connected to the network %s (%s).", containerId, networkConfig.NetworkName, networkId)

continue
}

logrus.Infof("Connecting container %s to the network %s (%s)...", containerId, networkConfig.NetworkName, networkId)
err = c.connectContainerToNetwork(ctx, containerId, networkId, &networkConfig)

if err != nil {
logrus.Warnf("Connecting container %s to the network %s (%s) has failed: %s.", containerId, networkConfig.NetworkName, networkId, err)
continue
}
}

return nil
}

func isContainerConnectedToNetwork(containerJson *types.ContainerJSON, networkId string) bool {
for _, network := range (*containerJson).NetworkSettings.Networks {
if network.NetworkID == networkId {
return true
}
}

return false
}

func (c *Connector) connectContainerToNetwork(ctx context.Context, containerId string, networkId string, config *networkConfiguration) error {
settings := (*config).EndpointSettings
c.cli.NetworkConnect(ctx, networkId, containerId, &settings)
return nil
}
24 changes: 24 additions & 0 deletions src/Connector_StartStop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package main

import (
"context"

"github.com/sirupsen/logrus"
)

func (c *Connector) Start() {
ctx := context.Background()
eventsContext, cancel := context.WithCancel(ctx)

c.eventsContextCancel = &cancel

logrus.Infof("Subscribing to container create events...")
go c.listenToContainerEvents(eventsContext)

logrus.Infof("Verifying existing containers are connected...")
c.verifyExistingContainersConnected(context.Background())
}

func (c *Connector) Stop() {
(*c.eventsContextCancel)()
}
37 changes: 37 additions & 0 deletions src/ContainerLabels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package main

import (
"encoding/json"

"github.com/sirupsen/logrus"

types "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/network"
)

const LabelName string = "io.homecentr.local-networks"

type networkConfiguration struct {
NetworkName string `json:"Network"`
EndpointSettings network.EndpointSettings `json:"Config"`
}

func getNetworkConfiguration(containerInfo *types.ContainerJSON) []networkConfiguration {
labels := (*containerInfo).Config.Labels

if value, exists := labels[LabelName]; exists {
var result []networkConfiguration

err := json.Unmarshal([]byte(value), &result)

if err != nil {
logrus.Warnf("The container %s has invalid label value which cannot be deserialized as json.", containerInfo.ID)

return nil
}

return result
}

return nil
}
35 changes: 35 additions & 0 deletions src/Networking.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import (
"context"
"errors"

"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
)

func getNetworkId(cli *client.Client, ctx context.Context, networkName string) (string, error) {
filterArgs := filters.NewArgs()
filterArgs.Add("name", networkName)

options := types.NetworkListOptions{
Filters: filterArgs,
}

matches, err := cli.NetworkList(ctx, options)

if err != nil {
return "", err
}

if len(matches) == 0 {
return "", errors.New("The network " + networkName + " could not be found.")
}

if len(matches) > 1 {
return "", errors.New("The are multiple networks with name " + networkName + ".")
}

return matches[0].ID, nil
}
Loading

0 comments on commit e32ecc7

Please sign in to comment.