github.com/google/cloudprober@v0.11.3/cloudprober.go (about) 1 // Copyright 2017-2019 The Cloudprober 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 15 /* 16 Package cloudprober provides a prober for running a set of probes. 17 18 Cloudprober takes in a config proto which dictates what probes should be created 19 with what configuration, and manages the asynchronous fan-in/fan-out of the 20 metrics data from these probes. 21 */ 22 package cloudprober 23 24 import ( 25 "context" 26 "crypto/tls" 27 "fmt" 28 "net" 29 "net/http" 30 "os" 31 "strconv" 32 "strings" 33 "sync" 34 35 "github.com/golang/protobuf/proto" 36 "github.com/google/cloudprober/common/tlsconfig" 37 "github.com/google/cloudprober/config" 38 configpb "github.com/google/cloudprober/config/proto" 39 "github.com/google/cloudprober/config/runconfig" 40 "github.com/google/cloudprober/logger" 41 "github.com/google/cloudprober/prober" 42 "github.com/google/cloudprober/probes" 43 "github.com/google/cloudprober/servers" 44 "github.com/google/cloudprober/surfacers" 45 "github.com/google/cloudprober/sysvars" 46 "google.golang.org/grpc" 47 "google.golang.org/grpc/channelz/service" 48 "google.golang.org/grpc/credentials" 49 ) 50 51 const ( 52 sysvarsModuleName = "sysvars" 53 ) 54 55 // Constants defining the default server host and port. 56 const ( 57 DefaultServerHost = "" 58 DefaultServerPort = 9313 59 ServerHostEnvVar = "CLOUDPROBER_HOST" 60 ServerPortEnvVar = "CLOUDPROBER_PORT" 61 ) 62 63 // Global prober.Prober instance protected by a mutex. 64 var cloudProber struct { 65 prober *prober.Prober 66 defaultServerLn net.Listener 67 defaultGRPCLn net.Listener 68 textConfig string 69 config *configpb.ProberConfig 70 cancelInitCtx context.CancelFunc 71 sync.Mutex 72 } 73 74 func getServerHost(c *configpb.ProberConfig) string { 75 serverHost := c.GetHost() 76 if serverHost == "" { 77 serverHost = DefaultServerHost 78 // If ServerHostEnvVar is defined, it will override the default 79 // server host. 80 if host := os.Getenv(ServerHostEnvVar); host != "" { 81 serverHost = host 82 } 83 } 84 return serverHost 85 } 86 87 func getDefaultServerPort(c *configpb.ProberConfig, l *logger.Logger) (int, error) { 88 if c.GetPort() != 0 { 89 return int(c.GetPort()), nil 90 } 91 92 // If ServerPortEnvVar is defined, it will override the default 93 // server port. 94 portStr := os.Getenv(ServerPortEnvVar) 95 if portStr == "" { 96 return DefaultServerPort, nil 97 } 98 99 if strings.HasPrefix(portStr, "tcp://") { 100 l.Warningf("%s environment variable likely set by Kubernetes (to %s), ignoring it", ServerPortEnvVar, portStr) 101 return DefaultServerPort, nil 102 } 103 104 port, err := strconv.ParseInt(portStr, 10, 32) 105 if err != nil { 106 return 0, fmt.Errorf("failed to parse default port from the env var: %s=%s", ServerPortEnvVar, portStr) 107 } 108 109 return int(port), nil 110 } 111 112 func initDefaultServer(c *configpb.ProberConfig, l *logger.Logger) (net.Listener, error) { 113 serverHost := getServerHost(c) 114 serverPort, err := getDefaultServerPort(c, l) 115 116 if err != nil { 117 return nil, err 118 } 119 120 ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", serverHost, serverPort)) 121 if err != nil { 122 return nil, fmt.Errorf("error while creating listener for default HTTP server: %v", err) 123 } 124 125 return ln, nil 126 } 127 128 // InitFromConfig initializes Cloudprober using the provided config. 129 func InitFromConfig(configFile string) error { 130 // Return immediately if prober is already initialized. 131 cloudProber.Lock() 132 defer cloudProber.Unlock() 133 134 if cloudProber.prober != nil { 135 return nil 136 } 137 138 // Initialize sysvars module 139 l, err := logger.NewCloudproberLog(sysvarsModuleName) 140 if err != nil { 141 return err 142 } 143 144 if err := sysvars.Init(l, nil); err != nil { 145 return err 146 } 147 148 configStr, err := config.ParseTemplate(configFile, sysvars.Vars()) 149 if err != nil { 150 return err 151 } 152 153 cfg := &configpb.ProberConfig{} 154 if err := proto.UnmarshalText(configStr, cfg); err != nil { 155 return err 156 } 157 158 globalLogger, err := logger.NewCloudproberLog("global") 159 if err != nil { 160 return fmt.Errorf("error in initializing global logger: %v", err) 161 } 162 163 // Start default HTTP server. It's used for profile handlers and 164 // prometheus exporter. 165 ln, err := initDefaultServer(cfg, l) 166 if err != nil { 167 return err 168 } 169 170 var grpcLn net.Listener 171 if cfg.GetGrpcPort() != 0 { 172 serverHost := getServerHost(cfg) 173 174 grpcLn, err = net.Listen("tcp", fmt.Sprintf("%s:%d", serverHost, cfg.GetGrpcPort())) 175 if err != nil { 176 return fmt.Errorf("error while creating listener for default gRPC server: %v", err) 177 } 178 179 // Create the default gRPC server now, so that other modules can register 180 // their services with it in the prober.Init() phase. 181 var serverOpts []grpc.ServerOption 182 183 if cfg.GetGrpcTlsConfig() != nil { 184 tlsConfig := &tls.Config{} 185 if err := tlsconfig.UpdateTLSConfig(tlsConfig, cfg.GetGrpcTlsConfig(), true); err != nil { 186 return err 187 } 188 serverOpts = append(serverOpts, grpc.Creds(credentials.NewTLS(tlsConfig))) 189 } 190 191 s := grpc.NewServer(serverOpts...) 192 // register channelz service to the default grpc server port 193 service.RegisterChannelzServiceToServer(s) 194 runconfig.SetDefaultGRPCServer(s) 195 } 196 197 pr := &prober.Prober{} 198 199 // initCtx is used to clean up in case of partial initialization failures. For 200 // example, user-configured servers open listeners during initialization and 201 // if initialization fails at a later stage, say in probers or surfacers, 202 // pr.Init returns an error and we cancel the initCtx, which makes servers 203 // close their listeners. 204 // TODO(manugarg): Plumb init context from cmd/cloudprober. 205 initCtx, cancelFunc := context.WithCancel(context.TODO()) 206 if err := pr.Init(initCtx, cfg, globalLogger); err != nil { 207 cancelFunc() 208 ln.Close() 209 return err 210 } 211 212 cloudProber.prober = pr 213 cloudProber.config = cfg 214 cloudProber.textConfig = configStr 215 cloudProber.defaultServerLn = ln 216 cloudProber.defaultGRPCLn = grpcLn 217 cloudProber.cancelInitCtx = cancelFunc 218 return nil 219 } 220 221 // Start starts a previously initialized Cloudprober. 222 func Start(ctx context.Context) { 223 cloudProber.Lock() 224 defer cloudProber.Unlock() 225 226 // Default servers 227 srv := &http.Server{} 228 grpcSrv := runconfig.DefaultGRPCServer() 229 230 // Set up a goroutine to cleanup if context ends. 231 go func() { 232 <-ctx.Done() 233 srv.Close() // This will close the listener as well. 234 if grpcSrv != nil { 235 grpcSrv.Stop() 236 } 237 cloudProber.cancelInitCtx() 238 }() 239 240 go srv.Serve(cloudProber.defaultServerLn) 241 if grpcSrv != nil && cloudProber.defaultGRPCLn != nil { 242 go grpcSrv.Serve(cloudProber.defaultGRPCLn) 243 } 244 245 if cloudProber.prober == nil { 246 panic("Prober is not initialized. Did you call cloudprober.InitFromConfig first?") 247 } 248 249 cloudProber.prober.Start(ctx) 250 } 251 252 // GetConfig returns the prober config. 253 func GetConfig() *configpb.ProberConfig { 254 cloudProber.Lock() 255 defer cloudProber.Unlock() 256 return cloudProber.config 257 } 258 259 // GetTextConfig returns the prober config in text proto format. 260 func GetTextConfig() string { 261 cloudProber.Lock() 262 defer cloudProber.Unlock() 263 return cloudProber.textConfig 264 } 265 266 // GetInfo returns information on all the probes, servers and surfacers. 267 func GetInfo() (map[string]*probes.ProbeInfo, []*surfacers.SurfacerInfo, []*servers.ServerInfo) { 268 cloudProber.Lock() 269 defer cloudProber.Unlock() 270 return cloudProber.prober.Probes, cloudProber.prober.Surfacers, cloudProber.prober.Servers 271 }