docs/cluster/config.go

149 lines
3.9 KiB
Go

package cluster
import (
"encoding/json"
"strings"
"github.com/samalba/dockerclient"
)
// SwarmLabelNamespace defines the key prefix in all custom labels
const SwarmLabelNamespace = "com.docker.swarm"
// ContainerConfig is exported
// TODO store affinities and constraints in their own fields
type ContainerConfig struct {
dockerclient.ContainerConfig
}
func parseEnv(e string) (bool, string, string) {
parts := strings.SplitN(e, ":", 2)
if len(parts) == 2 {
return true, parts[0], parts[1]
}
return false, "", ""
}
// FIXME: Temporary fix to handle forward/backward compatibility between Docker <1.6 and >=1.7
// ContainerConfig should be handling converting to/from different docker versions
func consolidateResourceFields(c *dockerclient.ContainerConfig) {
if c.Memory != c.HostConfig.Memory {
if c.Memory != 0 {
c.HostConfig.Memory = c.Memory
} else {
c.Memory = c.HostConfig.Memory
}
}
if c.MemorySwap != c.HostConfig.MemorySwap {
if c.MemorySwap != 0 {
c.HostConfig.MemorySwap = c.MemorySwap
} else {
c.MemorySwap = c.HostConfig.MemorySwap
}
}
if c.CpuShares != c.HostConfig.CpuShares {
if c.CpuShares != 0 {
c.HostConfig.CpuShares = c.CpuShares
} else {
c.CpuShares = c.HostConfig.CpuShares
}
}
if c.Cpuset != c.HostConfig.CpusetCpus {
if c.Cpuset != "" {
c.HostConfig.CpusetCpus = c.Cpuset
} else {
c.Cpuset = c.HostConfig.CpusetCpus
}
}
}
// BuildContainerConfig creates a cluster.ContainerConfig from a dockerclient.ContainerConfig
func BuildContainerConfig(c dockerclient.ContainerConfig) *ContainerConfig {
var (
affinities []string
constraints []string
env []string
)
// only for tests
if c.Labels == nil {
c.Labels = make(map[string]string)
}
// parse affinities from labels (ex. docker run --label 'com.docker.swarm.affinities=["container==redis","image==nginx"]')
if labels, ok := c.Labels[SwarmLabelNamespace+".affinities"]; ok {
json.Unmarshal([]byte(labels), &affinities)
}
// parse contraints from labels (ex. docker run --label 'com.docker.swarm.constraints=["region==us-east","storage==ssd"]')
if labels, ok := c.Labels[SwarmLabelNamespace+".constraints"]; ok {
json.Unmarshal([]byte(labels), &constraints)
}
// parse affinities/contraints from env (ex. docker run -e affinity:container==redis -e affinity:image==nginx -e constraint:region==us-east -e constraint:storage==ssd)
for _, e := range c.Env {
if ok, key, value := parseEnv(e); ok && key == "affinity" {
affinities = append(affinities, value)
} else if ok && key == "constraint" {
constraints = append(constraints, value)
} else {
env = append(env, e)
}
}
// remove affinities/contraints from env
c.Env = env
// store affinities in labels
if len(affinities) > 0 {
if labels, err := json.Marshal(affinities); err == nil {
c.Labels[SwarmLabelNamespace+".affinities"] = string(labels)
}
}
// store contraints in labels
if len(constraints) > 0 {
if labels, err := json.Marshal(constraints); err == nil {
c.Labels[SwarmLabelNamespace+".constraints"] = string(labels)
}
}
consolidateResourceFields(&c)
return &ContainerConfig{c}
}
func (c *ContainerConfig) extractExprs(key string) []string {
var exprs []string
if labels, ok := c.Labels[SwarmLabelNamespace+"."+key]; ok {
json.Unmarshal([]byte(labels), &exprs)
}
return exprs
}
// SwarmID extracts the Swarm ID from the Config.
// May return an empty string if not set.
func (c *ContainerConfig) SwarmID() string {
return c.Labels[SwarmLabelNamespace+".id"]
}
// SetSwarmID sets or overrides the Swarm ID in the Config.
func (c *ContainerConfig) SetSwarmID(id string) {
c.Labels[SwarmLabelNamespace+".id"] = id
}
// Affinities returns all the affinities from the ContainerConfig
func (c *ContainerConfig) Affinities() []string {
return c.extractExprs("affinities")
}
// Constraints returns all the constraints from the ContainerConfig
func (c *ContainerConfig) Constraints() []string {
return c.extractExprs("constraints")
}