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  }