mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	Removed mesos as cloud provider from Kubernetes.
This commit is contained in:
		@@ -43,7 +43,6 @@ var cloudproviders = []string{
 | 
			
		||||
	"azure",
 | 
			
		||||
	"cloudstack",
 | 
			
		||||
	"gce",
 | 
			
		||||
	"mesos",
 | 
			
		||||
	"openstack",
 | 
			
		||||
	"ovirt",
 | 
			
		||||
	"photon",
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,6 @@ go_library(
 | 
			
		||||
        "//pkg/cloudprovider/providers/azure:go_default_library",
 | 
			
		||||
        "//pkg/cloudprovider/providers/cloudstack:go_default_library",
 | 
			
		||||
        "//pkg/cloudprovider/providers/gce:go_default_library",
 | 
			
		||||
        "//pkg/cloudprovider/providers/mesos:go_default_library",
 | 
			
		||||
        "//pkg/cloudprovider/providers/openstack:go_default_library",
 | 
			
		||||
        "//pkg/cloudprovider/providers/ovirt:go_default_library",
 | 
			
		||||
        "//pkg/cloudprovider/providers/photon:go_default_library",
 | 
			
		||||
@@ -41,7 +40,6 @@ filegroup(
 | 
			
		||||
        "//pkg/cloudprovider/providers/cloudstack:all-srcs",
 | 
			
		||||
        "//pkg/cloudprovider/providers/fake:all-srcs",
 | 
			
		||||
        "//pkg/cloudprovider/providers/gce:all-srcs",
 | 
			
		||||
        "//pkg/cloudprovider/providers/mesos:all-srcs",
 | 
			
		||||
        "//pkg/cloudprovider/providers/openstack:all-srcs",
 | 
			
		||||
        "//pkg/cloudprovider/providers/ovirt:all-srcs",
 | 
			
		||||
        "//pkg/cloudprovider/providers/photon:all-srcs",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,67 +0,0 @@
 | 
			
		||||
package(default_visibility = ["//visibility:public"])
 | 
			
		||||
 | 
			
		||||
licenses(["notice"])
 | 
			
		||||
 | 
			
		||||
load(
 | 
			
		||||
    "@io_bazel_rules_go//go:def.bzl",
 | 
			
		||||
    "go_library",
 | 
			
		||||
    "go_test",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
go_library(
 | 
			
		||||
    name = "go_default_library",
 | 
			
		||||
    srcs = [
 | 
			
		||||
        "client.go",
 | 
			
		||||
        "config.go",
 | 
			
		||||
        "mesos.go",
 | 
			
		||||
        "plugins.go",
 | 
			
		||||
    ],
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//pkg/cloudprovider:go_default_library",
 | 
			
		||||
        "//pkg/controller:go_default_library",
 | 
			
		||||
        "//vendor/github.com/golang/glog:go_default_library",
 | 
			
		||||
        "//vendor/github.com/mesos/mesos-go/detector:go_default_library",
 | 
			
		||||
        "//vendor/github.com/mesos/mesos-go/detector/zoo:go_default_library",
 | 
			
		||||
        "//vendor/github.com/mesos/mesos-go/mesosproto:go_default_library",
 | 
			
		||||
        "//vendor/golang.org/x/net/context:go_default_library",
 | 
			
		||||
        "//vendor/gopkg.in/gcfg.v1:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/api/core/v1:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
go_test(
 | 
			
		||||
    name = "go_default_test",
 | 
			
		||||
    srcs = [
 | 
			
		||||
        "client_test.go",
 | 
			
		||||
        "config_test.go",
 | 
			
		||||
        "mesos_test.go",
 | 
			
		||||
    ],
 | 
			
		||||
    library = ":go_default_library",
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//pkg/cloudprovider:go_default_library",
 | 
			
		||||
        "//vendor/github.com/golang/glog:go_default_library",
 | 
			
		||||
        "//vendor/github.com/mesos/mesos-go/detector:go_default_library",
 | 
			
		||||
        "//vendor/github.com/mesos/mesos-go/mesosutil:go_default_library",
 | 
			
		||||
        "//vendor/golang.org/x/net/context:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
 | 
			
		||||
        "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
filegroup(
 | 
			
		||||
    name = "package-srcs",
 | 
			
		||||
    srcs = glob(["**"]),
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
    visibility = ["//visibility:private"],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
filegroup(
 | 
			
		||||
    name = "all-srcs",
 | 
			
		||||
    srcs = [":package-srcs"],
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
)
 | 
			
		||||
@@ -1,375 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015 The Kubernetes 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 mesos
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	log "github.com/golang/glog"
 | 
			
		||||
	"github.com/mesos/mesos-go/detector"
 | 
			
		||||
	mesos "github.com/mesos/mesos-go/mesosproto"
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
			
		||||
	utilnet "k8s.io/apimachinery/pkg/util/net"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const defaultClusterName = "mesos"
 | 
			
		||||
 | 
			
		||||
var noLeadingMasterError = errors.New("there is no current leading master available to query")
 | 
			
		||||
 | 
			
		||||
type mesosClient struct {
 | 
			
		||||
	masterLock    sync.RWMutex
 | 
			
		||||
	master        string // host:port formatted address
 | 
			
		||||
	httpClient    *http.Client
 | 
			
		||||
	tr            *http.Transport
 | 
			
		||||
	initialMaster <-chan struct{} // signal chan, closes once an initial, non-nil master is found
 | 
			
		||||
	state         *stateCache
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type slaveNode struct {
 | 
			
		||||
	hostname       string
 | 
			
		||||
	kubeletRunning bool
 | 
			
		||||
	resources      *v1.NodeResources
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type mesosState struct {
 | 
			
		||||
	clusterName string
 | 
			
		||||
	nodes       map[string]*slaveNode // by hostname
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type stateCache struct {
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
	expiresAt time.Time
 | 
			
		||||
	cached    *mesosState
 | 
			
		||||
	err       error
 | 
			
		||||
	ttl       time.Duration
 | 
			
		||||
	refill    func(context.Context) (*mesosState, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// reloadCache reloads the state cache if it has expired.
 | 
			
		||||
func (c *stateCache) reloadCache(ctx context.Context) {
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	c.Lock()
 | 
			
		||||
	defer c.Unlock()
 | 
			
		||||
	if c.expiresAt.Before(now) {
 | 
			
		||||
		log.V(4).Infof("Reloading cached Mesos state")
 | 
			
		||||
		c.cached, c.err = c.refill(ctx)
 | 
			
		||||
		c.expiresAt = now.Add(c.ttl)
 | 
			
		||||
	} else {
 | 
			
		||||
		log.V(4).Infof("Using cached Mesos state")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// cachedState returns the cached Mesos state.
 | 
			
		||||
func (c *stateCache) cachedState(ctx context.Context) (*mesosState, error) {
 | 
			
		||||
	c.reloadCache(ctx)
 | 
			
		||||
	return c.cached, c.err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clusterName returns the cached Mesos cluster name.
 | 
			
		||||
func (c *stateCache) clusterName(ctx context.Context) (string, error) {
 | 
			
		||||
	cached, err := c.cachedState(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return cached.clusterName, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// nodes returns the cached list of slave nodes.
 | 
			
		||||
func (c *stateCache) nodes(ctx context.Context) (map[string]*slaveNode, error) {
 | 
			
		||||
	cached, err := c.cachedState(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return cached.nodes, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newMesosClient(
 | 
			
		||||
	md detector.Master,
 | 
			
		||||
	mesosHttpClientTimeout, stateCacheTTL time.Duration) (*mesosClient, error) {
 | 
			
		||||
 | 
			
		||||
	tr := utilnet.SetTransportDefaults(&http.Transport{})
 | 
			
		||||
	httpClient := &http.Client{
 | 
			
		||||
		Transport: tr,
 | 
			
		||||
		Timeout:   mesosHttpClientTimeout,
 | 
			
		||||
	}
 | 
			
		||||
	return createMesosClient(md, httpClient, tr, stateCacheTTL)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createMesosClient(
 | 
			
		||||
	md detector.Master,
 | 
			
		||||
	httpClient *http.Client,
 | 
			
		||||
	tr *http.Transport,
 | 
			
		||||
	stateCacheTTL time.Duration) (*mesosClient, error) {
 | 
			
		||||
 | 
			
		||||
	initialMaster := make(chan struct{})
 | 
			
		||||
	client := &mesosClient{
 | 
			
		||||
		httpClient:    httpClient,
 | 
			
		||||
		tr:            tr,
 | 
			
		||||
		initialMaster: initialMaster,
 | 
			
		||||
		state: &stateCache{
 | 
			
		||||
			ttl: stateCacheTTL,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	client.state.refill = client.pollMasterForState
 | 
			
		||||
	first := true
 | 
			
		||||
	if err := md.Detect(detector.OnMasterChanged(func(info *mesos.MasterInfo) {
 | 
			
		||||
		host, port := extractMasterAddress(info)
 | 
			
		||||
		if len(host) > 0 {
 | 
			
		||||
			client.masterLock.Lock()
 | 
			
		||||
			defer client.masterLock.Unlock()
 | 
			
		||||
			client.master = fmt.Sprintf("%s:%d", host, port)
 | 
			
		||||
			if first {
 | 
			
		||||
				first = false
 | 
			
		||||
				close(initialMaster)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		log.Infof("cloud master changed to '%v'", client.master)
 | 
			
		||||
	})); err != nil {
 | 
			
		||||
		log.V(1).Infof("detector initialization failed: %v", err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return client, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func extractMasterAddress(info *mesos.MasterInfo) (host string, port int) {
 | 
			
		||||
	if info != nil {
 | 
			
		||||
		host = info.GetAddress().GetHostname()
 | 
			
		||||
		if host == "" {
 | 
			
		||||
			host = info.GetAddress().GetIp()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if host != "" {
 | 
			
		||||
			// use port from Address
 | 
			
		||||
			port = int(info.GetAddress().GetPort())
 | 
			
		||||
		} else {
 | 
			
		||||
			// deprecated: get host and port directly from MasterInfo (and not Address)
 | 
			
		||||
			host = info.GetHostname()
 | 
			
		||||
			if host == "" {
 | 
			
		||||
				host = unpackIPv4(info.GetIp())
 | 
			
		||||
			}
 | 
			
		||||
			port = int(info.GetPort())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unpackIPv4(ip uint32) string {
 | 
			
		||||
	octets := make([]byte, 4, 4)
 | 
			
		||||
	binary.BigEndian.PutUint32(octets, ip)
 | 
			
		||||
	ipv4 := net.IP(octets)
 | 
			
		||||
	return ipv4.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// listSlaves returns a (possibly cached) map of slave nodes by hostname.
 | 
			
		||||
// Callers must not mutate the contents of the returned slice.
 | 
			
		||||
func (c *mesosClient) listSlaves(ctx context.Context) (map[string]*slaveNode, error) {
 | 
			
		||||
	return c.state.nodes(ctx)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clusterName returns a (possibly cached) cluster name.
 | 
			
		||||
func (c *mesosClient) clusterName(ctx context.Context) (string, error) {
 | 
			
		||||
	return c.state.clusterName(ctx)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pollMasterForState returns an array of slave nodes
 | 
			
		||||
func (c *mesosClient) pollMasterForState(ctx context.Context) (*mesosState, error) {
 | 
			
		||||
	// wait for initial master detection
 | 
			
		||||
	select {
 | 
			
		||||
	case <-c.initialMaster: // noop
 | 
			
		||||
	case <-ctx.Done():
 | 
			
		||||
		return nil, ctx.Err()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	master := func() string {
 | 
			
		||||
		c.masterLock.RLock()
 | 
			
		||||
		defer c.masterLock.RUnlock()
 | 
			
		||||
		return c.master
 | 
			
		||||
	}()
 | 
			
		||||
	if master == "" {
 | 
			
		||||
		return nil, noLeadingMasterError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//TODO(jdef) should not assume master uses http (what about https?)
 | 
			
		||||
 | 
			
		||||
	var state *mesosState
 | 
			
		||||
	successHandler := func(res *http.Response) error {
 | 
			
		||||
		blob, err1 := ioutil.ReadAll(res.Body)
 | 
			
		||||
		if err1 != nil {
 | 
			
		||||
			return err1
 | 
			
		||||
		}
 | 
			
		||||
		log.V(3).Infof("Got mesos state, content length %v", len(blob))
 | 
			
		||||
		state, err1 = parseMesosState(blob)
 | 
			
		||||
		return err1
 | 
			
		||||
	}
 | 
			
		||||
	// thinking here is that we may get some other status codes from mesos at some point:
 | 
			
		||||
	// - authentication
 | 
			
		||||
	// - redirection (possibly from http to https)
 | 
			
		||||
	// ...
 | 
			
		||||
	for _, tt := range []struct {
 | 
			
		||||
		uri      string
 | 
			
		||||
		handlers map[int]func(*http.Response) error
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			uri: fmt.Sprintf("http://%s/state", master),
 | 
			
		||||
			handlers: map[int]func(*http.Response) error{
 | 
			
		||||
				200: successHandler,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			uri: fmt.Sprintf("http://%s/state.json", master),
 | 
			
		||||
			handlers: map[int]func(*http.Response) error{
 | 
			
		||||
				200: successHandler,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	} {
 | 
			
		||||
		req, err := http.NewRequest("GET", tt.uri, nil)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		err = c.httpDo(ctx, req, func(res *http.Response, err error) error {
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			defer res.Body.Close()
 | 
			
		||||
			if handler, ok := tt.handlers[res.StatusCode]; ok {
 | 
			
		||||
				if err := handler(res); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			// no handler for this error code, proceed to the next connection type
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
		if state != nil || err != nil {
 | 
			
		||||
			return state, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, errors.New("failed to sync with Mesos master")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseMesosState(blob []byte) (*mesosState, error) {
 | 
			
		||||
	type State struct {
 | 
			
		||||
		ClusterName string `json:"cluster"`
 | 
			
		||||
		Slaves      []*struct {
 | 
			
		||||
			Id        string                 `json:"id"`        // ex: 20150106-162714-3815890698-5050-2453-S2
 | 
			
		||||
			Pid       string                 `json:"pid"`       // ex: slave(1)@10.22.211.18:5051
 | 
			
		||||
			Hostname  string                 `json:"hostname"`  // ex: 10.22.211.18, or slave-123.nowhere.com
 | 
			
		||||
			Resources map[string]interface{} `json:"resources"` // ex: {"mem": 123, "ports": "[31000-3200]"}
 | 
			
		||||
		} `json:"slaves"`
 | 
			
		||||
		Frameworks []*struct {
 | 
			
		||||
			Id        string `json:"id"`  // ex: 20151105-093752-3745622208-5050-1-0000
 | 
			
		||||
			Pid       string `json:"pid"` // ex: scheduler(1)@192.168.65.228:57124
 | 
			
		||||
			Executors []*struct {
 | 
			
		||||
				SlaveId    string `json:"slave_id"`    // ex: 20151105-093752-3745622208-5050-1-S1
 | 
			
		||||
				ExecutorId string `json:"executor_id"` // ex: 6704d375c68fee1e_k8sm-executor
 | 
			
		||||
				Name       string `json:"name"`        // ex: Kubelet-Executor
 | 
			
		||||
			} `json:"executors"`
 | 
			
		||||
		} `json:"frameworks"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state := &State{ClusterName: defaultClusterName}
 | 
			
		||||
	if err := json.Unmarshal(blob, state); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	executorSlaveIds := map[string]struct{}{}
 | 
			
		||||
	for _, f := range state.Frameworks {
 | 
			
		||||
		for _, e := range f.Executors {
 | 
			
		||||
			// Note that this simple comparison breaks when we support more than one
 | 
			
		||||
			// k8s instance in a cluster. At the moment this is not possible for
 | 
			
		||||
			// a number of reasons.
 | 
			
		||||
			// TODO(sttts): find way to detect executors of this k8s instance
 | 
			
		||||
			if e.Name == KubernetesExecutorName {
 | 
			
		||||
				executorSlaveIds[e.SlaveId] = struct{}{}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nodes := map[string]*slaveNode{} // by hostname
 | 
			
		||||
	for _, slave := range state.Slaves {
 | 
			
		||||
		if slave.Hostname == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		node := &slaveNode{hostname: slave.Hostname}
 | 
			
		||||
		cap := v1.ResourceList{}
 | 
			
		||||
		if slave.Resources != nil && len(slave.Resources) > 0 {
 | 
			
		||||
			// attempt to translate CPU (cores) and memory (MB) resources
 | 
			
		||||
			if cpu, found := slave.Resources["cpus"]; found {
 | 
			
		||||
				if cpuNum, ok := cpu.(float64); ok {
 | 
			
		||||
					cap[v1.ResourceCPU] = *resource.NewQuantity(int64(cpuNum), resource.DecimalSI)
 | 
			
		||||
				} else {
 | 
			
		||||
					log.Warningf("unexpected slave cpu resource type %T: %v", cpu, cpu)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				log.Warningf("slave failed to report cpu resource")
 | 
			
		||||
			}
 | 
			
		||||
			if mem, found := slave.Resources["mem"]; found {
 | 
			
		||||
				if memNum, ok := mem.(float64); ok {
 | 
			
		||||
					cap[v1.ResourceMemory] = *resource.NewQuantity(int64(memNum), resource.BinarySI)
 | 
			
		||||
				} else {
 | 
			
		||||
					log.Warningf("unexpected slave mem resource type %T: %v", mem, mem)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				log.Warningf("slave failed to report mem resource")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if len(cap) > 0 {
 | 
			
		||||
			node.resources = &v1.NodeResources{
 | 
			
		||||
				Capacity: cap,
 | 
			
		||||
			}
 | 
			
		||||
			log.V(4).Infof("node %q reporting capacity %v", node.hostname, cap)
 | 
			
		||||
		}
 | 
			
		||||
		if _, ok := executorSlaveIds[slave.Id]; ok {
 | 
			
		||||
			node.kubeletRunning = true
 | 
			
		||||
		}
 | 
			
		||||
		nodes[node.hostname] = node
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result := &mesosState{
 | 
			
		||||
		clusterName: state.ClusterName,
 | 
			
		||||
		nodes:       nodes,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type responseHandler func(*http.Response, error) error
 | 
			
		||||
 | 
			
		||||
// httpDo executes an HTTP request in the given context, canceling an ongoing request if the context
 | 
			
		||||
// is canceled prior to completion of the request. hacked from https://blog.golang.org/context
 | 
			
		||||
func (c *mesosClient) httpDo(ctx context.Context, req *http.Request, f responseHandler) error {
 | 
			
		||||
	// Run the HTTP request in a goroutine and pass the response to f.
 | 
			
		||||
	ch := make(chan error, 1)
 | 
			
		||||
	go func() { ch <- f(c.httpClient.Do(req)) }()
 | 
			
		||||
	select {
 | 
			
		||||
	case <-ctx.Done():
 | 
			
		||||
		c.tr.CancelRequest(req)
 | 
			
		||||
		<-ch // Wait for f to return.
 | 
			
		||||
		return ctx.Err()
 | 
			
		||||
	case err := <-ch:
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,269 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015 The Kubernetes 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 mesos
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	log "github.com/golang/glog"
 | 
			
		||||
	"github.com/mesos/mesos-go/detector"
 | 
			
		||||
	"github.com/mesos/mesos-go/mesosutil"
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
 | 
			
		||||
	utilnet "k8s.io/apimachinery/pkg/util/net"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Test data
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	TEST_MASTER_ID   = "master-12345"
 | 
			
		||||
	TEST_MASTER_IP   = 177048842 // 10.141.141.10
 | 
			
		||||
	TEST_MASTER_PORT = 5050
 | 
			
		||||
 | 
			
		||||
	TEST_STATE_JSON = `
 | 
			
		||||
	{
 | 
			
		||||
		"version": "0.22.0",
 | 
			
		||||
		"unregistered_frameworks": [],
 | 
			
		||||
		"started_tasks": 0,
 | 
			
		||||
		"start_time": 1429456501.61141,
 | 
			
		||||
		"staged_tasks": 0,
 | 
			
		||||
		"slaves": [
 | 
			
		||||
		{
 | 
			
		||||
			"resources": {
 | 
			
		||||
				"ports": "[31000-32000]",
 | 
			
		||||
				"mem": 15360,
 | 
			
		||||
				"disk": 470842,
 | 
			
		||||
				"cpus": 8
 | 
			
		||||
			},
 | 
			
		||||
			"registered_time": 1429456502.46999,
 | 
			
		||||
			"pid": "slave(1)@mesos1.internal.example.org.fail:5050",
 | 
			
		||||
			"id": "20150419-081501-16777343-5050-16383-S2",
 | 
			
		||||
			"hostname": "mesos1.internal.example.org.fail",
 | 
			
		||||
			"attributes": {},
 | 
			
		||||
			"active": true
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"resources": {
 | 
			
		||||
				"ports": "[31000-32000]",
 | 
			
		||||
				"mem": 15360,
 | 
			
		||||
				"disk": 470842,
 | 
			
		||||
				"cpus": 8
 | 
			
		||||
			},
 | 
			
		||||
			"registered_time": 1429456502.4144,
 | 
			
		||||
			"pid": "slave(1)@mesos2.internal.example.org.fail:5050",
 | 
			
		||||
			"id": "20150419-081501-16777343-5050-16383-S1",
 | 
			
		||||
			"hostname": "mesos2.internal.example.org.fail",
 | 
			
		||||
			"attributes": {},
 | 
			
		||||
			"active": true
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"resources": {
 | 
			
		||||
				"ports": "[31000-32000]",
 | 
			
		||||
				"mem": 15360,
 | 
			
		||||
				"disk": 470842,
 | 
			
		||||
				"cpus": 8
 | 
			
		||||
			},
 | 
			
		||||
			"registered_time": 1429456502.02879,
 | 
			
		||||
			"pid": "slave(1)@mesos3.internal.example.org.fail:5050",
 | 
			
		||||
			"id": "20150419-081501-16777343-5050-16383-S0",
 | 
			
		||||
			"hostname": "mesos3.internal.example.org.fail",
 | 
			
		||||
			"attributes": {},
 | 
			
		||||
			"active": true
 | 
			
		||||
		}
 | 
			
		||||
		],
 | 
			
		||||
		"pid": "master@mesos-master0.internal.example.org.fail:5050",
 | 
			
		||||
		"orphan_tasks": [],
 | 
			
		||||
		"lost_tasks": 0,
 | 
			
		||||
		"leader": "master@mesos-master0.internal.example.org.fail:5050",
 | 
			
		||||
		"killed_tasks": 0,
 | 
			
		||||
		"failed_tasks": 0,
 | 
			
		||||
		"elected_time": 1429456501.61638,
 | 
			
		||||
		"deactivated_slaves": 0,
 | 
			
		||||
		"completed_frameworks": [],
 | 
			
		||||
		"build_user": "buildbot",
 | 
			
		||||
		"build_time": 1425085311,
 | 
			
		||||
		"build_date": "2015-02-27 17:01:51",
 | 
			
		||||
		"activated_slaves": 3,
 | 
			
		||||
		"finished_tasks": 0,
 | 
			
		||||
		"flags": {
 | 
			
		||||
			"zk_session_timeout": "10secs",
 | 
			
		||||
			"work_dir": "/somepath/mesos/local/Lc9arz",
 | 
			
		||||
			"webui_dir": "/usr/local/share/mesos/webui",
 | 
			
		||||
			"version": "false",
 | 
			
		||||
			"user_sorter": "drf",
 | 
			
		||||
			"slave_reregister_timeout": "10mins",
 | 
			
		||||
			"logbufsecs": "0",
 | 
			
		||||
			"log_auto_initialize": "true",
 | 
			
		||||
			"initialize_driver_logging": "true",
 | 
			
		||||
			"framework_sorter": "drf",
 | 
			
		||||
			"authenticators": "crammd5",
 | 
			
		||||
			"authenticate_slaves": "false",
 | 
			
		||||
			"authenticate": "false",
 | 
			
		||||
			"allocation_interval": "1secs",
 | 
			
		||||
			"logging_level": "INFO",
 | 
			
		||||
			"quiet": "false",
 | 
			
		||||
			"recovery_slave_removal_limit": "100%",
 | 
			
		||||
			"registry": "replicated_log",
 | 
			
		||||
			"registry_fetch_timeout": "1mins",
 | 
			
		||||
			"registry_store_timeout": "5secs",
 | 
			
		||||
			"registry_strict": "false",
 | 
			
		||||
			"root_submissions": "true"
 | 
			
		||||
		},
 | 
			
		||||
		"frameworks": [],
 | 
			
		||||
		"git_branch": "refs/heads/0.22.0-rc1",
 | 
			
		||||
		"git_sha": "46834faca67f877631e1beb7d61be5c080ec3dc2",
 | 
			
		||||
		"git_tag": "0.22.0-rc1",
 | 
			
		||||
		"hostname": "localhost",
 | 
			
		||||
		"id": "20150419-081501-16777343-5050-16383"
 | 
			
		||||
	}`
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Mocks
 | 
			
		||||
 | 
			
		||||
type FakeMasterDetector struct {
 | 
			
		||||
	callback detector.MasterChanged
 | 
			
		||||
	done     chan struct{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newFakeMasterDetector() *FakeMasterDetector {
 | 
			
		||||
	return &FakeMasterDetector{
 | 
			
		||||
		done: make(chan struct{}),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (md FakeMasterDetector) Cancel() {
 | 
			
		||||
	close(md.done)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (md FakeMasterDetector) Detect(cb detector.MasterChanged) error {
 | 
			
		||||
	md.callback = cb
 | 
			
		||||
	leadingMaster := mesosutil.NewMasterInfo(TEST_MASTER_ID, TEST_MASTER_IP, TEST_MASTER_PORT)
 | 
			
		||||
	cb.OnMasterChanged(leadingMaster)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (md FakeMasterDetector) Done() <-chan struct{} {
 | 
			
		||||
	return md.done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Auxiliary functions
 | 
			
		||||
 | 
			
		||||
func makeHttpMocks() (*httptest.Server, *http.Client, *http.Transport) {
 | 
			
		||||
	httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		log.V(4).Infof("Mocking response for HTTP request: %#v", r)
 | 
			
		||||
		if r.URL.Path == "/state.json" {
 | 
			
		||||
			w.WriteHeader(200) // OK
 | 
			
		||||
			w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
			fmt.Fprintln(w, TEST_STATE_JSON)
 | 
			
		||||
		} else {
 | 
			
		||||
			w.WriteHeader(400)
 | 
			
		||||
			fmt.Fprintln(w, "Bad Request")
 | 
			
		||||
		}
 | 
			
		||||
	}))
 | 
			
		||||
 | 
			
		||||
	// Intercept all client requests and feed them to the test server
 | 
			
		||||
	transport := utilnet.SetTransportDefaults(&http.Transport{
 | 
			
		||||
		Proxy: func(req *http.Request) (*url.URL, error) {
 | 
			
		||||
			return url.Parse(httpServer.URL)
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	httpClient := &http.Client{Transport: transport}
 | 
			
		||||
 | 
			
		||||
	return httpServer, httpClient, transport
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Tests
 | 
			
		||||
 | 
			
		||||
// test mesos.parseMesosState
 | 
			
		||||
func Test_parseMesosState(t *testing.T) {
 | 
			
		||||
	state, err := parseMesosState([]byte(TEST_STATE_JSON))
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("parseMesosState does not yield an error")
 | 
			
		||||
	}
 | 
			
		||||
	if state == nil {
 | 
			
		||||
		t.Fatalf("parseMesosState yields a non-nil state")
 | 
			
		||||
	}
 | 
			
		||||
	if len(state.nodes) != 3 {
 | 
			
		||||
		t.Fatalf("parseMesosState yields a state with 3 nodes")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// test mesos.listSlaves
 | 
			
		||||
func Test_listSlaves(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
	md := FakeMasterDetector{}
 | 
			
		||||
	httpServer, httpClient, httpTransport := makeHttpMocks()
 | 
			
		||||
	defer httpServer.Close()
 | 
			
		||||
 | 
			
		||||
	cacheTTL := 500 * time.Millisecond
 | 
			
		||||
	mesosClient, err := createMesosClient(md, httpClient, httpTransport, cacheTTL)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("createMesosClient does not yield an error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	slaveNodes, err := mesosClient.listSlaves(context.TODO())
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("listSlaves does not yield an error")
 | 
			
		||||
	}
 | 
			
		||||
	if len(slaveNodes) != 3 {
 | 
			
		||||
		t.Fatalf("listSlaves yields a collection of size 3")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expectedHostnames := map[string]struct{}{
 | 
			
		||||
		"mesos1.internal.example.org.fail": {},
 | 
			
		||||
		"mesos2.internal.example.org.fail": {},
 | 
			
		||||
		"mesos3.internal.example.org.fail": {},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	actualHostnames := make(map[string]struct{})
 | 
			
		||||
	for _, node := range slaveNodes {
 | 
			
		||||
		actualHostnames[node.hostname] = struct{}{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !reflect.DeepEqual(expectedHostnames, actualHostnames) {
 | 
			
		||||
		t.Fatalf("listSlaves yields a collection with the expected hostnames")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// test mesos.clusterName
 | 
			
		||||
func Test_clusterName(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
	md := FakeMasterDetector{}
 | 
			
		||||
	httpServer, httpClient, httpTransport := makeHttpMocks()
 | 
			
		||||
	defer httpServer.Close()
 | 
			
		||||
	cacheTTL := 500 * time.Millisecond
 | 
			
		||||
	mesosClient, err := createMesosClient(md, httpClient, httpTransport, cacheTTL)
 | 
			
		||||
 | 
			
		||||
	name, err := mesosClient.clusterName(context.TODO())
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("clusterName does not yield an error")
 | 
			
		||||
	}
 | 
			
		||||
	if name != defaultClusterName {
 | 
			
		||||
		t.Fatalf("clusterName yields the expected (default) value")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,79 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015 The Kubernetes 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 mesos
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"gopkg.in/gcfg.v1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DefaultMesosMaster       = "localhost:5050"
 | 
			
		||||
	DefaultHttpClientTimeout = time.Duration(10) * time.Second
 | 
			
		||||
	DefaultStateCacheTTL     = time.Duration(5) * time.Second
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Example Mesos cloud provider configuration file:
 | 
			
		||||
//
 | 
			
		||||
// [mesos-cloud]
 | 
			
		||||
//  mesos-master        = leader.mesos:5050
 | 
			
		||||
//	http-client-timeout = 500ms
 | 
			
		||||
//	state-cache-ttl     = 1h
 | 
			
		||||
 | 
			
		||||
type ConfigWrapper struct {
 | 
			
		||||
	Mesos_Cloud Config
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Config struct {
 | 
			
		||||
	MesosMaster            string   `gcfg:"mesos-master"`
 | 
			
		||||
	MesosHttpClientTimeout Duration `gcfg:"http-client-timeout"`
 | 
			
		||||
	StateCacheTTL          Duration `gcfg:"state-cache-ttl"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Duration struct {
 | 
			
		||||
	Duration time.Duration `gcfg:"duration"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Duration) UnmarshalText(data []byte) error {
 | 
			
		||||
	underlying, err := time.ParseDuration(string(data))
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		d.Duration = underlying
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createDefaultConfig() *Config {
 | 
			
		||||
	return &Config{
 | 
			
		||||
		MesosMaster:            DefaultMesosMaster,
 | 
			
		||||
		MesosHttpClientTimeout: Duration{Duration: DefaultHttpClientTimeout},
 | 
			
		||||
		StateCacheTTL:          Duration{Duration: DefaultStateCacheTTL},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readConfig(configReader io.Reader) (*Config, error) {
 | 
			
		||||
	config := createDefaultConfig()
 | 
			
		||||
	wrapper := &ConfigWrapper{Mesos_Cloud: *config}
 | 
			
		||||
	if configReader != nil {
 | 
			
		||||
		if err := gcfg.ReadInto(wrapper, configReader); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		config = &(wrapper.Mesos_Cloud)
 | 
			
		||||
	}
 | 
			
		||||
	return config, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -1,75 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015 The Kubernetes 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 mesos
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	log "github.com/golang/glog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// test mesos.createDefaultConfig
 | 
			
		||||
func Test_createDefaultConfig(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
 | 
			
		||||
	config := createDefaultConfig()
 | 
			
		||||
 | 
			
		||||
	if config.MesosMaster != DefaultMesosMaster {
 | 
			
		||||
		t.Fatalf("Default config has the expected MesosMaster value")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if config.MesosHttpClientTimeout.Duration != DefaultHttpClientTimeout {
 | 
			
		||||
		t.Fatalf("Default config has the expected MesosHttpClientTimeout value")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if config.StateCacheTTL.Duration != DefaultStateCacheTTL {
 | 
			
		||||
		t.Fatalf("Default config has the expected StateCacheTTL value")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// test mesos.readConfig
 | 
			
		||||
func Test_readConfig(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
 | 
			
		||||
	configString := `
 | 
			
		||||
[mesos-cloud]
 | 
			
		||||
	mesos-master        = leader.mesos:5050
 | 
			
		||||
	http-client-timeout = 500ms
 | 
			
		||||
	state-cache-ttl     = 1h`
 | 
			
		||||
 | 
			
		||||
	reader := bytes.NewBufferString(configString)
 | 
			
		||||
 | 
			
		||||
	config, err := readConfig(reader)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Reading configuration does not yield an error: %#v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if config.MesosMaster != "leader.mesos:5050" {
 | 
			
		||||
		t.Fatalf("Parsed config has the expected MesosMaster value")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if config.MesosHttpClientTimeout.Duration != time.Duration(500)*time.Millisecond {
 | 
			
		||||
		t.Fatalf("Parsed config has the expected MesosHttpClientTimeout value")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if config.StateCacheTTL.Duration != time.Duration(1)*time.Hour {
 | 
			
		||||
		t.Fatalf("Parsed config has the expected StateCacheTTL value")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,315 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015 The Kubernetes 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 mesos
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"regexp"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
 | 
			
		||||
	log "github.com/golang/glog"
 | 
			
		||||
	"github.com/mesos/mesos-go/detector"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/types"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/cloudprovider"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/controller"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	ProviderName = "mesos"
 | 
			
		||||
 | 
			
		||||
	// KubernetesExecutorName is shared between contrib/mesos and Mesos cloud provider.
 | 
			
		||||
	// Because cloud provider -> contrib dependencies are forbidden, this constant
 | 
			
		||||
	// is defined here, not in contrib.
 | 
			
		||||
	KubernetesExecutorName = "Kubelet-Executor"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	CloudProvider *MesosCloud
 | 
			
		||||
 | 
			
		||||
	noHostNameSpecified = errors.New("No hostname specified")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	cloudprovider.RegisterCloudProvider(
 | 
			
		||||
		ProviderName,
 | 
			
		||||
		func(configReader io.Reader) (cloudprovider.Interface, error) {
 | 
			
		||||
			provider, err := newMesosCloud(configReader)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				CloudProvider = provider
 | 
			
		||||
			}
 | 
			
		||||
			return provider, err
 | 
			
		||||
		})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MesosCloud struct {
 | 
			
		||||
	client *mesosClient
 | 
			
		||||
	config *Config
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *MesosCloud) MasterURI() string {
 | 
			
		||||
	return c.config.MesosMaster
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newMesosCloud(configReader io.Reader) (*MesosCloud, error) {
 | 
			
		||||
	config, err := readConfig(configReader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.V(1).Infof("new mesos cloud, master='%v'", config.MesosMaster)
 | 
			
		||||
	if d, err := detector.New(config.MesosMaster); err != nil {
 | 
			
		||||
		log.V(1).Infof("failed to create master detector: %v", err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	} else if cl, err := newMesosClient(d,
 | 
			
		||||
		config.MesosHttpClientTimeout.Duration,
 | 
			
		||||
		config.StateCacheTTL.Duration); err != nil {
 | 
			
		||||
		log.V(1).Infof("failed to create mesos cloud client: %v", err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	} else {
 | 
			
		||||
		return &MesosCloud{client: cl, config: config}, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Initialize passes a Kubernetes clientBuilder interface to the cloud provider
 | 
			
		||||
func (c *MesosCloud) Initialize(clientBuilder controller.ControllerClientBuilder) {}
 | 
			
		||||
 | 
			
		||||
// Implementation of Instances.CurrentNodeName
 | 
			
		||||
func (c *MesosCloud) CurrentNodeName(hostname string) (types.NodeName, error) {
 | 
			
		||||
	return types.NodeName(hostname), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *MesosCloud) AddSSHKeyToAllInstances(user string, keyData []byte) error {
 | 
			
		||||
	return errors.New("unimplemented")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Instances returns a copy of the Mesos cloud Instances implementation.
 | 
			
		||||
// Mesos natively provides minimal cloud-type resources. More robust cloud
 | 
			
		||||
// support requires a combination of Mesos and cloud-specific knowledge.
 | 
			
		||||
func (c *MesosCloud) Instances() (cloudprovider.Instances, bool) {
 | 
			
		||||
	return c, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadBalancer always returns nil, false in this implementation.
 | 
			
		||||
// Mesos does not provide any type of native load balancing by default,
 | 
			
		||||
// so this implementation always returns (nil, false).
 | 
			
		||||
func (c *MesosCloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Zones always returns nil, false in this implementation.
 | 
			
		||||
// Mesos does not provide any type of native region or zone awareness,
 | 
			
		||||
// so this implementation always returns (nil, false).
 | 
			
		||||
func (c *MesosCloud) Zones() (cloudprovider.Zones, bool) {
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clusters returns a copy of the Mesos cloud Clusters implementation.
 | 
			
		||||
// Mesos does not provide support for multiple clusters.
 | 
			
		||||
func (c *MesosCloud) Clusters() (cloudprovider.Clusters, bool) {
 | 
			
		||||
	return c, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Routes always returns nil, false in this implementation.
 | 
			
		||||
func (c *MesosCloud) Routes() (cloudprovider.Routes, bool) {
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProviderName returns the cloud provider ID.
 | 
			
		||||
func (c *MesosCloud) ProviderName() string {
 | 
			
		||||
	return ProviderName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ScrubDNS filters DNS settings for pods.
 | 
			
		||||
func (c *MesosCloud) ScrubDNS(nameservers, searches []string) (nsOut, srchOut []string) {
 | 
			
		||||
	return nameservers, searches
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListClusters lists the names of the available Mesos clusters.
 | 
			
		||||
func (c *MesosCloud) ListClusters() ([]string, error) {
 | 
			
		||||
	// Always returns a single cluster (this one!)
 | 
			
		||||
	ctx, cancel := context.WithCancel(context.TODO())
 | 
			
		||||
	defer cancel()
 | 
			
		||||
	name, err := c.client.clusterName(ctx)
 | 
			
		||||
	return []string{name}, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Master gets back the address (either DNS name or IP address) of the leading Mesos master node for the cluster.
 | 
			
		||||
func (c *MesosCloud) Master(clusterName string) (string, error) {
 | 
			
		||||
	clusters, err := c.ListClusters()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	for _, name := range clusters {
 | 
			
		||||
		if name == clusterName {
 | 
			
		||||
			if c.client.master == "" {
 | 
			
		||||
				return "", errors.New("The currently leading master is unknown.")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			host, _, err := net.SplitHostPort(c.client.master)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return "", err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return host, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return "", fmt.Errorf("The supplied cluster '%v' does not exist", clusterName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ipAddress returns an IP address of the specified instance.
 | 
			
		||||
func ipAddress(name string) (net.IP, error) {
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		return nil, noHostNameSpecified
 | 
			
		||||
	}
 | 
			
		||||
	ipaddr := net.ParseIP(name)
 | 
			
		||||
	if ipaddr != nil {
 | 
			
		||||
		return ipaddr, nil
 | 
			
		||||
	}
 | 
			
		||||
	iplist, err := net.LookupIP(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.V(2).Infof("failed to resolve IP from host name '%v': %v", name, err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	ipaddr = iplist[0]
 | 
			
		||||
	log.V(2).Infof("resolved host '%v' to '%v'", name, ipaddr)
 | 
			
		||||
	return ipaddr, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// mapNodeNameToPrivateDNSName maps a k8s NodeName to an mesos hostname.
 | 
			
		||||
// This is a simple string cast
 | 
			
		||||
func mapNodeNameToHostname(nodeName types.NodeName) string {
 | 
			
		||||
	return string(nodeName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExternalID returns the cloud provider ID of the instance with the specified nodeName (deprecated).
 | 
			
		||||
func (c *MesosCloud) ExternalID(nodeName types.NodeName) (string, error) {
 | 
			
		||||
	hostname := mapNodeNameToHostname(nodeName)
 | 
			
		||||
	//TODO(jdef) use a timeout here? 15s?
 | 
			
		||||
	ctx, cancel := context.WithCancel(context.TODO())
 | 
			
		||||
	defer cancel()
 | 
			
		||||
 | 
			
		||||
	nodes, err := c.client.listSlaves(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	node := nodes[hostname]
 | 
			
		||||
	if node == nil {
 | 
			
		||||
		return "", cloudprovider.InstanceNotFound
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ip, err := ipAddress(node.hostname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return ip.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstanceID returns the cloud provider ID of the instance with the specified nodeName.
 | 
			
		||||
func (c *MesosCloud) InstanceID(nodeName types.NodeName) (string, error) {
 | 
			
		||||
	return "", nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstanceTypeByProviderID returns the cloudprovider instance type of the node with the specified unique providerID
 | 
			
		||||
// This method will not be called from the node that is requesting this ID. i.e. metadata service
 | 
			
		||||
// and other local methods cannot be used here
 | 
			
		||||
func (c *MesosCloud) InstanceTypeByProviderID(providerID string) (string, error) {
 | 
			
		||||
	return "", errors.New("unimplemented")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstanceType returns the type of the instance with the specified nodeName.
 | 
			
		||||
func (c *MesosCloud) InstanceType(nodeName types.NodeName) (string, error) {
 | 
			
		||||
	return "", nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *MesosCloud) listNodes() (map[string]*slaveNode, error) {
 | 
			
		||||
	//TODO(jdef) use a timeout here? 15s?
 | 
			
		||||
	ctx, cancel := context.WithCancel(context.TODO())
 | 
			
		||||
	defer cancel()
 | 
			
		||||
 | 
			
		||||
	nodes, err := c.client.listSlaves(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if len(nodes) == 0 {
 | 
			
		||||
		log.V(2).Info("no slaves found, are any running?")
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	return nodes, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// List lists instances that match 'filter' which is a regular expression
 | 
			
		||||
// which must match the entire instance name (fqdn).
 | 
			
		||||
func (c *MesosCloud) List(filter string) ([]types.NodeName, error) {
 | 
			
		||||
	nodes, err := c.listNodes()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	filterRegex, err := regexp.Compile(filter)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	names := []types.NodeName{}
 | 
			
		||||
	for _, node := range nodes {
 | 
			
		||||
		if filterRegex.MatchString(node.hostname) {
 | 
			
		||||
			names = append(names, types.NodeName(node.hostname))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return names, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListWithKubelet list those instance which have no running kubelet, i.e. the
 | 
			
		||||
// Kubernetes executor.
 | 
			
		||||
func (c *MesosCloud) ListWithoutKubelet() ([]string, error) {
 | 
			
		||||
	nodes, err := c.listNodes()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	addr := make([]string, 0, len(nodes))
 | 
			
		||||
	for _, n := range nodes {
 | 
			
		||||
		if !n.kubeletRunning {
 | 
			
		||||
			addr = append(addr, n.hostname)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return addr, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NodeAddresses returns the addresses of the instance with the specified nodeName.
 | 
			
		||||
func (c *MesosCloud) NodeAddresses(nodeName types.NodeName) ([]v1.NodeAddress, error) {
 | 
			
		||||
	name := mapNodeNameToHostname(nodeName)
 | 
			
		||||
	ip, err := ipAddress(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return []v1.NodeAddress{
 | 
			
		||||
		{Type: v1.NodeInternalIP, Address: ip.String()},
 | 
			
		||||
		{Type: v1.NodeExternalIP, Address: ip.String()},
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NodeAddressesByProviderID returns the node addresses of an instances with the specified unique providerID
 | 
			
		||||
// This method will not be called from the node that is requesting this ID. i.e. metadata service
 | 
			
		||||
// and other local methods cannot be used here
 | 
			
		||||
func (c *MesosCloud) NodeAddressesByProviderID(providerID string) ([]v1.NodeAddress, error) {
 | 
			
		||||
	return []v1.NodeAddress{}, errors.New("unimplemented")
 | 
			
		||||
}
 | 
			
		||||
@@ -1,280 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015 The Kubernetes 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 mesos
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"net"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	log "github.com/golang/glog"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/types"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/cloudprovider"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestIPAddress(t *testing.T) {
 | 
			
		||||
	expected4 := net.IPv4(127, 0, 0, 1)
 | 
			
		||||
	ip, err := ipAddress("127.0.0.1")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepEqual(ip, expected4) {
 | 
			
		||||
		t.Fatalf("expected %#v instead of %#v", expected4, ip)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected6 := net.ParseIP("::1")
 | 
			
		||||
	if expected6 == nil {
 | 
			
		||||
		t.Fatalf("failed to parse ipv6 ::1")
 | 
			
		||||
	}
 | 
			
		||||
	ip, err = ipAddress("::1")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepEqual(ip, expected6) {
 | 
			
		||||
		t.Fatalf("expected %#v instead of %#v", expected6, ip)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ip, err = ipAddress("localhost")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("unexpected error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepEqual(ip, expected4) && !reflect.DeepEqual(ip, expected6) {
 | 
			
		||||
		t.Fatalf("expected %#v or %#v instead of %#v", expected4, expected6, ip)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = ipAddress("")
 | 
			
		||||
	if err != noHostNameSpecified {
 | 
			
		||||
		t.Fatalf("expected error noHostNameSpecified but got none")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// test mesos.newMesosCloud with no config
 | 
			
		||||
func Test_newMesosCloud_NoConfig(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
	mesosCloud, err := newMesosCloud(nil)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Creating a new Mesos cloud provider without config does not yield an error: %#v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if mesosCloud.client.httpClient.Timeout != DefaultHttpClientTimeout {
 | 
			
		||||
		t.Fatalf("Creating a new Mesos cloud provider without config does not yield an error: %#v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if mesosCloud.client.state.ttl != DefaultStateCacheTTL {
 | 
			
		||||
		t.Fatalf("Mesos client with default config has the expected state cache TTL value")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// test mesos.newMesosCloud with custom config
 | 
			
		||||
func Test_newMesosCloud_WithConfig(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
 | 
			
		||||
	configString := `
 | 
			
		||||
[mesos-cloud]
 | 
			
		||||
	http-client-timeout = 500ms
 | 
			
		||||
	state-cache-ttl = 1h`
 | 
			
		||||
 | 
			
		||||
	reader := bytes.NewBufferString(configString)
 | 
			
		||||
 | 
			
		||||
	mesosCloud, err := newMesosCloud(reader)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Creating a new Mesos cloud provider with a custom config does not yield an error: %#v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if mesosCloud.client.httpClient.Timeout != time.Duration(500)*time.Millisecond {
 | 
			
		||||
		t.Fatalf("Mesos client with a custom config has the expected HTTP client timeout value")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if mesosCloud.client.state.ttl != time.Duration(1)*time.Hour {
 | 
			
		||||
		t.Fatalf("Mesos client with a custom config has the expected state cache TTL value")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// tests for capability reporting functions
 | 
			
		||||
 | 
			
		||||
// test mesos.Instances
 | 
			
		||||
func Test_Instances(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
	mesosCloud, _ := newMesosCloud(nil)
 | 
			
		||||
 | 
			
		||||
	instances, supports_instances := mesosCloud.Instances()
 | 
			
		||||
 | 
			
		||||
	if !supports_instances || instances == nil {
 | 
			
		||||
		t.Fatalf("MesosCloud provides an implementation of Instances")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// test mesos.LoadBalancer
 | 
			
		||||
func Test_TcpLoadBalancer(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
	mesosCloud, _ := newMesosCloud(nil)
 | 
			
		||||
 | 
			
		||||
	lb, supports_lb := mesosCloud.LoadBalancer()
 | 
			
		||||
 | 
			
		||||
	if supports_lb || lb != nil {
 | 
			
		||||
		t.Fatalf("MesosCloud does not provide an implementation of LoadBalancer")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// test mesos.Zones
 | 
			
		||||
func Test_Zones(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
	mesosCloud, _ := newMesosCloud(nil)
 | 
			
		||||
 | 
			
		||||
	zones, supports_zones := mesosCloud.Zones()
 | 
			
		||||
 | 
			
		||||
	if supports_zones || zones != nil {
 | 
			
		||||
		t.Fatalf("MesosCloud does not provide an implementation of Zones")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// test mesos.Clusters
 | 
			
		||||
func Test_Clusters(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
	mesosCloud, _ := newMesosCloud(nil)
 | 
			
		||||
 | 
			
		||||
	clusters, supports_clusters := mesosCloud.Clusters()
 | 
			
		||||
 | 
			
		||||
	if !supports_clusters || clusters == nil {
 | 
			
		||||
		t.Fatalf("MesosCloud does not provide an implementation of Clusters")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// test mesos.MasterURI
 | 
			
		||||
func Test_MasterURI(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
	mesosCloud, _ := newMesosCloud(nil)
 | 
			
		||||
 | 
			
		||||
	uri := mesosCloud.MasterURI()
 | 
			
		||||
 | 
			
		||||
	if uri != DefaultMesosMaster {
 | 
			
		||||
		t.Fatalf("MasterURI returns the expected master URI (expected \"localhost\", actual \"%s\"", uri)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// test mesos.ListClusters
 | 
			
		||||
func Test_ListClusters(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
	md := FakeMasterDetector{}
 | 
			
		||||
	httpServer, httpClient, httpTransport := makeHttpMocks()
 | 
			
		||||
	defer httpServer.Close()
 | 
			
		||||
	cacheTTL := 500 * time.Millisecond
 | 
			
		||||
	mesosClient, err := createMesosClient(md, httpClient, httpTransport, cacheTTL)
 | 
			
		||||
	mesosCloud := &MesosCloud{client: mesosClient, config: createDefaultConfig()}
 | 
			
		||||
 | 
			
		||||
	clusters, err := mesosCloud.ListClusters()
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("ListClusters does not yield an error: %#v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(clusters) != 1 {
 | 
			
		||||
		t.Fatalf("ListClusters should return a list of size 1: (actual: %#v)", clusters)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expectedClusterNames := []string{"mesos"}
 | 
			
		||||
 | 
			
		||||
	if !reflect.DeepEqual(clusters, expectedClusterNames) {
 | 
			
		||||
		t.Fatalf("ListClusters should return the expected list of names: (expected: %#v, actual: %#v)",
 | 
			
		||||
			expectedClusterNames,
 | 
			
		||||
			clusters)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// test mesos.Master
 | 
			
		||||
func Test_Master(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
	md := FakeMasterDetector{}
 | 
			
		||||
	httpServer, httpClient, httpTransport := makeHttpMocks()
 | 
			
		||||
	defer httpServer.Close()
 | 
			
		||||
	cacheTTL := 500 * time.Millisecond
 | 
			
		||||
	mesosClient, err := createMesosClient(md, httpClient, httpTransport, cacheTTL)
 | 
			
		||||
	mesosCloud := &MesosCloud{client: mesosClient, config: createDefaultConfig()}
 | 
			
		||||
 | 
			
		||||
	clusters, err := mesosCloud.ListClusters()
 | 
			
		||||
	clusterName := clusters[0]
 | 
			
		||||
	master, err := mesosCloud.Master(clusterName)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Master does not yield an error: %#v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expectedMaster := unpackIPv4(TEST_MASTER_IP)
 | 
			
		||||
 | 
			
		||||
	if master != expectedMaster {
 | 
			
		||||
		t.Fatalf("Master returns the unexpected value: (expected: %#v, actual: %#v", expectedMaster, master)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// test mesos.List
 | 
			
		||||
func Test_List(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
	md := FakeMasterDetector{}
 | 
			
		||||
	httpServer, httpClient, httpTransport := makeHttpMocks()
 | 
			
		||||
	defer httpServer.Close()
 | 
			
		||||
	cacheTTL := 500 * time.Millisecond
 | 
			
		||||
	mesosClient, err := createMesosClient(md, httpClient, httpTransport, cacheTTL)
 | 
			
		||||
	mesosCloud := &MesosCloud{client: mesosClient, config: createDefaultConfig()}
 | 
			
		||||
 | 
			
		||||
	clusters, err := mesosCloud.List(".*") // recognizes the language of all strings
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("List does not yield an error: %#v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(clusters) != 3 {
 | 
			
		||||
		t.Fatalf("List with a catch-all filter should return a list of size 3: (actual: %#v)", clusters)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clusters, err = mesosCloud.List("$^") // end-of-string followed by start-of-string: recognizes the empty language
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("List does not yield an error: %#v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(clusters) != 0 {
 | 
			
		||||
		t.Fatalf("List with a reject-all filter should return a list of size 0: (actual: %#v)", clusters)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_ExternalID(t *testing.T) {
 | 
			
		||||
	defer log.Flush()
 | 
			
		||||
	md := FakeMasterDetector{}
 | 
			
		||||
	httpServer, httpClient, httpTransport := makeHttpMocks()
 | 
			
		||||
	defer httpServer.Close()
 | 
			
		||||
	cacheTTL := 500 * time.Millisecond
 | 
			
		||||
	mesosClient, err := createMesosClient(md, httpClient, httpTransport, cacheTTL)
 | 
			
		||||
	mesosCloud := &MesosCloud{client: mesosClient, config: createDefaultConfig()}
 | 
			
		||||
 | 
			
		||||
	_, err = mesosCloud.ExternalID("unknown")
 | 
			
		||||
	if err != cloudprovider.InstanceNotFound {
 | 
			
		||||
		t.Fatalf("ExternalID did not return InstanceNotFound on an unknown instance")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	slaveName := types.NodeName("mesos3.internal.example.org.fail")
 | 
			
		||||
	id, err := mesosCloud.ExternalID(slaveName)
 | 
			
		||||
	if id != "" {
 | 
			
		||||
		t.Fatalf("ExternalID should not be able to resolve %q", slaveName)
 | 
			
		||||
	}
 | 
			
		||||
	if err == cloudprovider.InstanceNotFound {
 | 
			
		||||
		t.Fatalf("ExternalID should find %q", slaveName)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015 The Kubernetes 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 mesos
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	_ "github.com/mesos/mesos-go/detector/zoo"
 | 
			
		||||
)
 | 
			
		||||
@@ -22,7 +22,6 @@ import (
 | 
			
		||||
	_ "k8s.io/kubernetes/pkg/cloudprovider/providers/azure"
 | 
			
		||||
	_ "k8s.io/kubernetes/pkg/cloudprovider/providers/cloudstack"
 | 
			
		||||
	_ "k8s.io/kubernetes/pkg/cloudprovider/providers/gce"
 | 
			
		||||
	_ "k8s.io/kubernetes/pkg/cloudprovider/providers/mesos"
 | 
			
		||||
	_ "k8s.io/kubernetes/pkg/cloudprovider/providers/openstack"
 | 
			
		||||
	_ "k8s.io/kubernetes/pkg/cloudprovider/providers/ovirt"
 | 
			
		||||
	_ "k8s.io/kubernetes/pkg/cloudprovider/providers/photon"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user