traefik/vendor/github.com/mesosphere/mesos-dns/records/state/state.go
2017-03-09 13:13:02 +01:00

271 lines
7.9 KiB
Go

package state
import (
"bytes"
"github.com/mesos/mesos-go/upid"
"net"
"strconv"
"strings"
)
// Resources holds resources as defined in the /state.json Mesos HTTP endpoint.
type Resources struct {
PortRanges string `json:"ports"`
}
// Ports returns a slice of individual ports expanded from PortRanges.
func (r Resources) Ports() []string {
if r.PortRanges == "" || r.PortRanges == "[]" {
return []string{}
}
rhs := strings.Split(r.PortRanges, "[")[1]
lhs := strings.Split(rhs, "]")[0]
yports := []string{}
mports := strings.Split(lhs, ",")
for _, port := range mports {
tmp := strings.TrimSpace(port)
pz := strings.Split(tmp, "-")
lo, _ := strconv.Atoi(pz[0])
hi, _ := strconv.Atoi(pz[1])
for t := lo; t <= hi; t++ {
yports = append(yports, strconv.Itoa(t))
}
}
return yports
}
// Label holds a label as defined in the /state.json Mesos HTTP endpoint.
type Label struct {
Key string `json:"key"`
Value string `json:"value"`
}
// Status holds a task status as defined in the /state.json Mesos HTTP endpoint.
type Status struct {
Timestamp float64 `json:"timestamp"`
State string `json:"state"`
Labels []Label `json:"labels,omitempty"`
ContainerStatus ContainerStatus `json:"container_status,omitempty"`
Healthy *bool `json:"healthy"`
}
// ContainerStatus holds container metadata as defined in the /state.json
// Mesos HTTP endpoint.
type ContainerStatus struct {
NetworkInfos []NetworkInfo `json:"network_infos,omitempty"`
}
// NetworkInfo holds the network configuration for a single interface
// as defined in the /state.json Mesos HTTP endpoint.
type NetworkInfo struct {
IPAddresses []IPAddress `json:"ip_addresses,omitempty"`
// back-compat with 0.25 IPAddress format
IPAddress string `json:"ip_address,omitempty"`
}
// IPAddress holds a single IP address configured on an interface,
// as defined in the /state.json Mesos HTTP endpoint.
type IPAddress struct {
IPAddress string `json:"ip_address,omitempty"`
}
// Task holds a task as defined in the /state.json Mesos HTTP endpoint.
type Task struct {
FrameworkID string `json:"framework_id"`
ID string `json:"id"`
Name string `json:"name"`
SlaveID string `json:"slave_id"`
State string `json:"state"`
Statuses []Status `json:"statuses"`
Resources `json:"resources"`
DiscoveryInfo DiscoveryInfo `json:"discovery"`
SlaveIP string `json:"-"`
Labels []Label `json:"labels,omitempty"`
}
// HasDiscoveryInfo return whether the DiscoveryInfo was provided in the state.json
func (t *Task) HasDiscoveryInfo() bool {
return t.DiscoveryInfo.Name != ""
}
// IP returns the first Task IP found in the given sources.
func (t *Task) IP(srcs ...string) string {
if ips := t.IPs(srcs...); len(ips) > 0 {
return ips[0].String()
}
return ""
}
// IPs returns a slice of IPs sourced from the given sources with ascending
// priority.
func (t *Task) IPs(srcs ...string) (ips []net.IP) {
if t == nil {
return nil
}
for i := range srcs {
if src, ok := sources[srcs[i]]; ok {
for _, srcIP := range src(t) {
if ip := net.ParseIP(srcIP); len(ip) > 0 {
ips = append(ips, ip)
}
}
}
}
return ips
}
// sources maps the string representation of IP sources to their functions.
var sources = map[string]func(*Task) []string{
"host": hostIPs,
"mesos": mesosIPs,
"docker": dockerIPs,
"netinfo": networkInfoIPs,
}
// hostIPs is an IPSource which returns the IP addresses of the slave a Task
// runs on.
func hostIPs(t *Task) []string { return []string{t.SlaveIP} }
// networkInfoIPs returns IP addresses from a given Task's
// []Status.ContainerStatus.[]NetworkInfos.[]IPAddresses.IPAddress
func networkInfoIPs(t *Task) []string {
return statusIPs(t.Statuses, func(s *Status) []string {
ips := make([]string, len(s.ContainerStatus.NetworkInfos))
for _, netinfo := range s.ContainerStatus.NetworkInfos {
if len(netinfo.IPAddresses) > 0 {
// In v0.26, we use the IPAddresses field.
for _, ipAddress := range netinfo.IPAddresses {
ips = append(ips, ipAddress.IPAddress)
}
} else {
// Fall back to v0.25 syntax of single IPAddress if that's being used.
if netinfo.IPAddress != "" {
ips = append(ips, netinfo.IPAddress)
}
}
}
return ips
})
}
const (
// DockerIPLabel is the key of the Label which holds the Docker containerizer IP value.
DockerIPLabel = "Docker.NetworkSettings.IPAddress"
// MesosIPLabel is the key of the label which holds the Mesos containerizer IP value.
MesosIPLabel = "MesosContainerizer.NetworkSettings.IPAddress"
)
// dockerIPs returns IP addresses from the values of all
// Task.[]Status.[]Labels whose keys are equal to "Docker.NetworkSettings.IPAddress".
func dockerIPs(t *Task) []string {
return statusIPs(t.Statuses, labels(DockerIPLabel))
}
// mesosIPs returns IP addresses from the values of all
// Task.[]Status.[]Labels whose keys are equal to
// "MesosContainerizer.NetworkSettings.IPAddress".
func mesosIPs(t *Task) []string {
return statusIPs(t.Statuses, labels(MesosIPLabel))
}
// statusIPs returns the latest running status IPs extracted with the given src
func statusIPs(st []Status, src func(*Status) []string) []string {
// the state.json we extract from mesos makes no guarantees re: the order
// of the task statuses so we should check the timestamps to avoid problems
// down the line. we can't rely on seeing the same sequence. (@joris)
// https://github.com/apache/mesos/blob/0.24.0/src/slave/slave.cpp#L5226-L5238
ts, j := -1.0, -1
for i := range st {
if st[i].State == "TASK_RUNNING" && st[i].Timestamp > ts {
ts, j = st[i].Timestamp, i
}
}
if j >= 0 {
return src(&st[j])
}
return nil
}
// labels returns all given Status.[]Labels' values whose keys are equal
// to the given key
func labels(key string) func(*Status) []string {
return func(s *Status) []string {
vs := make([]string, 0, len(s.Labels))
for _, l := range s.Labels {
if l.Key == key {
vs = append(vs, l.Value)
}
}
return vs
}
}
// Framework holds a framework as defined in the /state.json Mesos HTTP endpoint.
type Framework struct {
Tasks []Task `json:"tasks"`
PID PID `json:"pid"`
Name string `json:"name"`
Hostname string `json:"hostname"`
}
// HostPort returns the hostname and port where a framework's scheduler is
// listening on.
func (f Framework) HostPort() (string, string) {
if f.PID.UPID != nil {
return f.PID.Host, f.PID.Port
}
return f.Hostname, ""
}
// Slave holds a slave as defined in the /state.json Mesos HTTP endpoint.
type Slave struct {
ID string `json:"id"`
Hostname string `json:"hostname"`
PID PID `json:"pid"`
}
// PID holds a Mesos PID and implements the json.Unmarshaler interface.
type PID struct{ *upid.UPID }
// UnmarshalJSON implements the json.Unmarshaler interface for PIDs.
func (p *PID) UnmarshalJSON(data []byte) (err error) {
p.UPID, err = upid.Parse(string(bytes.Trim(data, `" `)))
return err
}
// State holds the state defined in the /state.json Mesos HTTP endpoint.
type State struct {
Frameworks []Framework `json:"frameworks"`
Slaves []Slave `json:"slaves"`
Leader string `json:"leader"`
}
// DiscoveryInfo holds the discovery meta data for a task defined in the /state.json Mesos HTTP endpoint.
type DiscoveryInfo struct {
Visibilty string `json:"visibility"`
Version string `json:"version,omitempty"`
Name string `json:"name,omitempty"`
Location string `json:"location,omitempty"`
Environment string `json:"environment,omitempty"`
Labels struct {
Labels []Label `json:"labels"`
} `json:"labels"`
Ports Ports `json:"ports"`
}
// Ports holds a list of DiscoveryPort
type Ports struct {
DiscoveryPorts []DiscoveryPort `json:"ports"`
}
// DiscoveryPort holds a port for a task defined in the /state.json Mesos HTTP endpoint.
type DiscoveryPort struct {
Protocol string `json:"protocol"`
Number int `json:"number"`
Name string `json:"name"`
}