/* Copyright 2015 The Kubernetes Authors All rights reserved. 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" ) // 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.company.com:5050", "id": "20150419-081501-16777343-5050-16383-S2", "hostname": "mesos1.internal.company.com", "attributes": {}, "active": true }, { "resources": { "ports": "[31000-32000]", "mem": 15360, "disk": 470842, "cpus": 8 }, "registered_time": 1429456502.4144, "pid": "slave(1)@mesos2.internal.company.com:5050", "id": "20150419-081501-16777343-5050-16383-S1", "hostname": "mesos2.internal.company.com", "attributes": {}, "active": true }, { "resources": { "ports": "[31000-32000]", "mem": 15360, "disk": 470842, "cpus": 8 }, "registered_time": 1429456502.02879, "pid": "slave(1)@mesos3.internal.company.com:5050", "id": "20150419-081501-16777343-5050-16383-S0", "hostname": "mesos3.internal.company.com", "attributes": {}, "active": true } ], "pid": "master@mesos-master0.internal.company.com:5050", "orphan_tasks": [], "lost_tasks": 0, "leader": "master@mesos-master0.internal.company.com: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": "/tmp/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 := &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{} // TODO: Uncomment next two lines and remove third line when fix #19254 // defer httpServer.Close() // httpServer, httpClient, httpTransport := makeHttpMocks() _, httpClient, httpTransport := makeHttpMocks() 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.company.com": {}, "mesos2.internal.company.com": {}, "mesos3.internal.company.com": {}, } 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{} // TODO: Uncomment next two lines and remove third line when fix #19254 // defer httpServer.Close() // httpServer, httpClient, httpTransport := makeHttpMocks() _, httpClient, httpTransport := makeHttpMocks() 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") } }