1// Copyright 2017 Istio Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package util 16 17import ( 18 "io/ioutil" 19 "net" 20 "net/http" 21 "os" 22 "strconv" 23 "time" 24 25 "istio.io/pkg/log" 26 27 "istio.io/istio/pilot/pkg/bootstrap" 28 "istio.io/istio/pkg/config/mesh" 29 "istio.io/istio/pkg/keepalive" 30 "istio.io/istio/pkg/test/env" 31 32 "k8s.io/apimachinery/pkg/util/wait" 33) 34 35var ( 36 // MockPilotGrpcAddr is the address to be used for grpc connections. 37 MockPilotGrpcAddr string 38 39 // MockPilotHTTPPort is the dynamic port for pilot http 40 MockPilotHTTPPort int 41 42 // MockPilotGrpcPort is the dynamic port for pilot grpc 43 MockPilotGrpcPort int 44) 45 46// TearDownFunc is to be called to tear down a test server. 47type TearDownFunc func() 48 49// EnsureTestServer will ensure a pilot server is running in process and initializes 50// the MockPilotUrl and MockPilotGrpcAddr to allow connections to the test pilot. 51func EnsureTestServer(args ...func(*bootstrap.PilotArgs)) (*bootstrap.Server, TearDownFunc) { 52 server, tearDown, err := setup(args...) 53 if err != nil { 54 log.Errora("Failed to start in-process server: ", err) 55 panic(err) 56 } 57 return server, tearDown 58} 59 60func setup(additionalArgs ...func(*bootstrap.PilotArgs)) (*bootstrap.Server, TearDownFunc, error) { 61 // TODO: point to test data directory 62 // Setting FileDir (--configDir) disables k8s client initialization, including for registries, 63 // and uses a 100ms scan. Must be used with the mock registry (or one of the others) 64 // This limits the options - 65 66 // When debugging a test or running locally it helps having a static port for /debug 67 // "0" is used on shared environment (it's not actually clear if such thing exists since 68 // we run the tests in isolated VMs) 69 pilotHTTP := os.Getenv("PILOT_HTTP") 70 if len(pilotHTTP) == 0 { 71 pilotHTTP = "0" 72 } 73 httpAddr := ":" + pilotHTTP 74 75 meshConfig := mesh.DefaultMeshConfig() 76 77 bootstrap.PilotCertDir = env.IstioSrc + "/tests/testdata/certs/pilot" 78 79 additionalArgs = append([]func(p *bootstrap.PilotArgs){func(p *bootstrap.PilotArgs) { 80 p.Namespace = "testing" 81 p.DiscoveryOptions = bootstrap.DiscoveryServiceOptions{ 82 HTTPAddr: httpAddr, 83 GrpcAddr: ":0", 84 EnableProfiling: true, 85 } 86 //TODO: start mixer first, get its address 87 p.Mesh = bootstrap.MeshArgs{ 88 MixerAddress: "istio-mixer.istio-system:9091", 89 } 90 p.Config = bootstrap.ConfigArgs{ 91 KubeConfig: env.IstioSrc + "/tests/util/kubeconfig", 92 // Static testdata, should include all configs we want to test. 93 FileDir: env.IstioSrc + "/tests/testdata/config", 94 } 95 p.MeshConfig = &meshConfig 96 p.MCPOptions.MaxMessageSize = 1024 * 1024 * 4 97 p.KeepaliveOptions = keepalive.DefaultOption() 98 p.ForceStop = true 99 100 // TODO: add the plugins, so local tests are closer to reality and test full generation 101 // Plugins: bootstrap.DefaultPlugins, 102 }}, additionalArgs...) 103 // Create a test pilot discovery service configured to watch the tempDir. 104 args := bootstrap.NewPilotArgs(additionalArgs...) 105 106 // Create and setup the controller. 107 s, err := bootstrap.NewServer(args) 108 if err != nil { 109 return nil, nil, err 110 } 111 112 stop := make(chan struct{}) 113 // Start the server. 114 if err := s.Start(stop); err != nil { 115 return nil, nil, err 116 } 117 118 // Extract the port from the network address. 119 _, port, err := net.SplitHostPort(s.HTTPListener.Addr().String()) 120 if err != nil { 121 return nil, nil, err 122 } 123 httpURL := "http://localhost:" + port 124 MockPilotHTTPPort, _ = strconv.Atoi(port) 125 126 _, port, err = net.SplitHostPort(s.GRPCListener.Addr().String()) 127 if err != nil { 128 return nil, nil, err 129 } 130 MockPilotGrpcAddr = "localhost:" + port 131 MockPilotGrpcPort, _ = strconv.Atoi(port) 132 133 // Wait a bit for the server to come up. 134 err = wait.Poll(500*time.Millisecond, 5*time.Second, func() (bool, error) { 135 client := &http.Client{Timeout: 1 * time.Second} 136 resp, err := client.Get(httpURL + "/ready") 137 if err != nil { 138 return false, nil 139 } 140 defer func() { _ = resp.Body.Close() }() 141 _, _ = ioutil.ReadAll(resp.Body) 142 if resp.StatusCode == http.StatusOK { 143 return true, nil 144 } 145 return false, nil 146 }) 147 return s, func() { 148 close(stop) 149 }, err 150} 151