Skip to content

Commit

Permalink
Initial LXD backend discovery support
Browse files Browse the repository at this point in the history
  • Loading branch information
jtopjian committed Apr 14, 2017
1 parent 2f0ecb3 commit a280a7e
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 4 deletions.
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ deps: clean-deps
github.com/hashicorp/consul/api \
github.com/spf13/cobra \
github.com/laher/goxc \
github.com/gin-contrib/cors
github.com/gin-contrib/cors \
github.com/lxc/lxd

clean-dist:
rm -rf ./dist/${VERSION}
Expand All @@ -84,10 +85,10 @@ dist:
rm ./debian -rf
@echo Done.

build-container-latest: build
build-container-latest: build
@echo Building docker container LATEST
docker build -t yyyar/gobetween .

build-container-tagged: build
build-container-tagged: build
@echo Building docker container ${VERSION}
docker build -t yyyar/gobetween:${VERSION} .
5 changes: 5 additions & 0 deletions config/gobetween.toml
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,8 @@ protocol = "udp"
# consul_tls_key_path = "/path/to/key.pem"
# consul_tls_cacert_path = "/path/to/cacert.pem"
#
# # -- lxd -- #
# kind = "lxd"
# lxd_container_label = "myservice" # (required) Label to filter containers
# lxd_container_private_port = "80" # (required) Private port of container to use
# lxd_container_interface = "eth1" # (optional) Container interface to communicate on. by default "eth0"
9 changes: 8 additions & 1 deletion src/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type ApiConfig struct {
Bind string `toml:"bind" json:"bind"`
BasicAuth *ApiBasicAuthConfig `toml:"basic_auth" json:"basic_auth"`
Tls *ApiTlsConfig `toml:"tls" json:"tls"`
Cors bool `toml:"cors" json:"cors"`
Cors bool `toml:"cors" json:"cors"`
}

/**
Expand Down Expand Up @@ -170,6 +170,7 @@ type DiscoveryConfig struct {
*ExecDiscoveryConfig
*PlaintextDiscoveryConfig
*ConsulDiscoveryConfig
*LXDDiscoveryConfig
}

type StaticDiscoveryConfig struct {
Expand Down Expand Up @@ -228,6 +229,12 @@ type ConsulDiscoveryConfig struct {
ConsulTlsCacertPath string `toml:"consul_tls_cacert_path" json:"consul_tls_cacert_path"`
}

type LXDDiscoveryConfig struct {
LXDContainerLabel string `toml:"lxd_container_label" json:"lxd_container_label"`
LXDContainerPrivatePort string `toml:"lxd_container_private_port" json:"lxd_container_private_port"`
LXDContainerInterface string `toml:"lxd_container_interface" json:"lxd_container_interface"`
}

/**
* Healthcheck configuration
*/
Expand Down
1 change: 1 addition & 0 deletions src/discovery/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func init() {
registry["exec"] = NewExecDiscovery
registry["plaintext"] = NewPlaintextDiscovery
registry["consul"] = NewConsulDiscovery
registry["lxd"] = NewLXDDiscovery
}

/**
Expand Down
155 changes: 155 additions & 0 deletions src/discovery/lxd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/**
* lxd.go - LXD API discovery implementation
*
*/

package discovery

import (
"../config"
"../core"
"../logging"
"../utils"
"fmt"
"time"

"github.com/lxc/lxd"
)

const (
lxdRetryWaitDuration = 2 * time.Second
lxdTimeout = 5 * time.Second
)

/**
* Create new Discovery with LXD fetch func
*/
func NewLXDDiscovery(cfg config.DiscoveryConfig) interface{} {

d := Discovery{
opts: DiscoveryOpts{lxdRetryWaitDuration},
fetch: lxdFetch,
cfg: cfg,
}

return &d
}

/**
* Fetch backends from LXD API
*/
func lxdFetch(cfg config.DiscoveryConfig) (*[]core.Backend, error) {

log := logging.For("lxdFetch")

log.Info("Fetching unix.socket")

// Create a simple LXD client that connects to the LXD server over the local
// unix socket.
config := lxd.Config{
Remotes: map[string]lxd.RemoteConfig{
"local": lxd.RemoteConfig{
Addr: "unix:/var/lib/lxd/unix.socket",
},
},
}
client, err := lxd.NewClient(&config, "local")
if err != nil {
return nil, err
}

// Validate the client config and connectivity
if _, err := client.GetServerConfig(); err != nil {
return nil, err
}

client.Http.Timeout = utils.ParseDurationOrDefault(cfg.Timeout, lxdTimeout)

/* Fetch containers */
containers, err := client.ListContainers()
if err != nil {
return nil, err
}

/* Create backends from response */

backends := []core.Backend{}

for _, container := range containers {
config := container.Config
if v, ok := config["user.gobetween.label"]; ok {
// label is an arbitrary label to describe the service.
label := v

// Don't add the backend if the label wasn't requested
// for this service.
if cfg.LXDContainerLabel != "" && label != cfg.LXDContainerLabel {
continue
}

// private_port is the port on the LXD container.
private_port, ok := config["user.gobetween.private_port"]
if !ok {
msg := fmt.Sprintf("No user.gobetween.private_port set for container %s. Skipping",
container.Name)
log.Warn(msg)
continue
}

// iface is the container interface to get an IP address.
iface := "eth0"
if v, ok := config["user.gobetween.interface"]; ok {
iface = v
}

ip, err := lxdDetermineContainerIP(client, container.Name, iface)
if err != nil {
return nil, err
}

var sni string
if v, ok := config["user.gobetween.sni"]; ok {
sni = v
}

backends = append(backends, core.Backend{
Target: core.Target{
Host: ip,
Port: private_port,
},
Priority: 1,
Weight: 1,
Stats: core.BackendStats{
Live: true,
},
Sni: sni,
})
}
}

return &backends, nil
}

func lxdDetermineContainerIP(client *lxd.Client, container string, iface string) (string, error) {
var containerIP string
cstate, err := client.ContainerState(container)
if err != nil {
return "", err
}

for i, network := range cstate.Network {
if i == iface {
for _, ip := range network.Addresses {
if ip.Family == "inet" {
containerIP = ip.Address
}
}
}
}

if containerIP == "" {
return "", fmt.Errorf("Unable to determine IP address for LXD container %s", container)
}

return containerIP, nil
}

0 comments on commit a280a7e

Please sign in to comment.