github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/server/components/components.go (about)

     1  // Copyright 2019 Google Inc.
     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  //     https://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  // Package components defines and instantiates the components needed by a
    16  // generic Fleetspeak server.
    17  //
    18  // Installations requiring specialized components should branch this, or
    19  // otherwise create a server.Components according to their needs.
    20  package components
    21  
    22  import (
    23  	"database/sql"
    24  	"errors"
    25  	"fmt"
    26  	"net"
    27  	"net/http"
    28  
    29  	"google.golang.org/grpc"
    30  
    31  	"github.com/google/fleetspeak/fleetspeak/src/server"
    32  	"github.com/google/fleetspeak/fleetspeak/src/server/admin"
    33  	"github.com/google/fleetspeak/fleetspeak/src/server/authorizer"
    34  	"github.com/google/fleetspeak/fleetspeak/src/server/comms"
    35  	cauthorizer "github.com/google/fleetspeak/fleetspeak/src/server/components/authorizer"
    36  	chttps "github.com/google/fleetspeak/fleetspeak/src/server/components/https"
    37  	cnotifications "github.com/google/fleetspeak/fleetspeak/src/server/components/notifications"
    38  	"github.com/google/fleetspeak/fleetspeak/src/server/components/prometheus"
    39  	"github.com/google/fleetspeak/fleetspeak/src/server/grpcservice"
    40  	"github.com/google/fleetspeak/fleetspeak/src/server/https"
    41  	inotifications "github.com/google/fleetspeak/fleetspeak/src/server/internal/notifications"
    42  	"github.com/google/fleetspeak/fleetspeak/src/server/mysql"
    43  	"github.com/google/fleetspeak/fleetspeak/src/server/notifications"
    44  	"github.com/google/fleetspeak/fleetspeak/src/server/service"
    45  	"github.com/google/fleetspeak/fleetspeak/src/server/stats"
    46  
    47  	"github.com/prometheus/client_golang/prometheus/promhttp"
    48  
    49  	cpb "github.com/google/fleetspeak/fleetspeak/src/server/components/proto/fleetspeak_components"
    50  	sgrpc "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server"
    51  )
    52  
    53  // MakeComponents creates server components from a given config.
    54  func MakeComponents(cfg *cpb.Config) (*server.Components, error) {
    55  	if cfg.MysqlDataSourceName == "" {
    56  		return nil, errors.New("mysql_data_source_name is required")
    57  	}
    58  	hcfg := cfg.HttpsConfig
    59  	if hcfg != nil {
    60  		switch {
    61  		case hcfg.GetFrontendConfig().GetCleartextHeaderConfig() != nil,
    62  			hcfg.GetFrontendConfig().GetCleartextHeaderChecksumConfig() != nil,
    63  			hcfg.GetFrontendConfig().GetCleartextXfccConfig() != nil:
    64  			if hcfg.ListenAddress == "" {
    65  				return nil, errors.New("http_config requires listen_address")
    66  			}
    67  		default:
    68  			if hcfg.ListenAddress == "" || hcfg.Certificates == "" || hcfg.Key == "" {
    69  				return nil, errors.New("https_config requires listen_address, certificates and key")
    70  			}
    71  		}
    72  	}
    73  
    74  	acfg := cfg.AdminConfig
    75  	if acfg != nil && acfg.ListenAddress == "" {
    76  		return nil, errors.New("admin_config.listen_address can't be empty")
    77  	}
    78  
    79  	// Database setup
    80  	con, err := sql.Open("mysql", cfg.MysqlDataSourceName)
    81  	if err != nil {
    82  		return nil, fmt.Errorf("failed to open database: %v", err)
    83  	}
    84  
    85  	db, err := mysql.MakeDatastore(con)
    86  	if err != nil {
    87  		return nil, fmt.Errorf("failed to create datastore: %v", err)
    88  	}
    89  
    90  	// Authorizer setup
    91  	var auth authorizer.Authorizer
    92  	if cfg.RequiredLabel == "" {
    93  		auth = authorizer.PermissiveAuthorizer{}
    94  	} else {
    95  		auth = cauthorizer.LabelFilter{Label: cfg.RequiredLabel}
    96  	}
    97  
    98  	// HTTPS setup
    99  	var comm comms.Communicator
   100  	if hcfg != nil {
   101  		l, err := net.Listen("tcp", hcfg.ListenAddress)
   102  		if err != nil {
   103  			return nil, fmt.Errorf("failed to listen on [%v]: %v", cfg.HttpsConfig.ListenAddress, err)
   104  		}
   105  		if cfg.ProxyProtocol {
   106  			l = &chttps.ProxyListener{Listener: l}
   107  		}
   108  		comm, err = https.NewCommunicator(https.Params{
   109  			Listener:       l,
   110  			Cert:           []byte(hcfg.Certificates),
   111  			FrontendConfig: hcfg.GetFrontendConfig(),
   112  			Key:            []byte(hcfg.Key),
   113  			Streaming:      !hcfg.DisableStreaming,
   114  		})
   115  		if err != nil {
   116  			return nil, fmt.Errorf("failed to create communicator: %v", err)
   117  		}
   118  	}
   119  	// Notification setup.
   120  	var nn notifications.Notifier
   121  	var nl notifications.Listener
   122  	if cfg.NotificationListenAddress != "" {
   123  		nn = &cnotifications.HttpNotifier{}
   124  		nl = &cnotifications.HttpListener{
   125  			BindAddress:       cfg.NotificationListenAddress,
   126  			AdvertisedAddress: cfg.NotificationPublicAddress,
   127  		}
   128  	} else {
   129  		llc := inotifications.LocalListenerNotifier{}
   130  		if cfg.NotificationUseHttpNotifier {
   131  			nn = &cnotifications.HttpNotifier{}
   132  		} else {
   133  			nn = &llc
   134  		}
   135  		nl = &llc
   136  	}
   137  
   138  	var admSrv *grpc.Server
   139  	if acfg != nil {
   140  		as := admin.NewServer(db, nn)
   141  		admSrv := grpc.NewServer()
   142  		sgrpc.RegisterAdminServer(admSrv, as)
   143  		aas, err := net.ResolveTCPAddr("tcp", acfg.ListenAddress)
   144  		if err != nil {
   145  			return nil, fmt.Errorf("failed to initialize admin server: %v", err)
   146  		}
   147  		asl, err := net.ListenTCP("tcp", aas)
   148  		if err != nil {
   149  			return nil, fmt.Errorf("failed to initialize admin server: %v", err)
   150  		}
   151  		go func() {
   152  			admSrv.Serve(asl)
   153  		}()
   154  	}
   155  
   156  	// Stats setup
   157  	scfg := cfg.StatsConfig
   158  	var statsCollector stats.Collector
   159  	if scfg != nil {
   160  		addressToExportStats := scfg.Address
   161  		if addressToExportStats != "" {
   162  			statsCollector = prometheus.StatsCollector{}
   163  			statsMux := http.NewServeMux()
   164  			statsMux.Handle("/metrics", promhttp.Handler())
   165  			go http.ListenAndServe(addressToExportStats, statsMux)
   166  		}
   167  	}
   168  
   169  	// Health check setup
   170  	hccfg := cfg.HealthCheckConfig
   171  	var healthCheck *http.Server
   172  	if hccfg != nil {
   173  		healthCheckListener, err := net.Listen("tcp", hccfg.ListenAddress)
   174  		if err != nil {
   175  			return nil, fmt.Errorf("failed to initialize health check service: %v", err)
   176  		}
   177  		healthCheck = &http.Server{
   178  			Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
   179  		}
   180  		go healthCheck.Serve(healthCheckListener)
   181  	}
   182  
   183  	var communicators []comms.Communicator
   184  	if comm != nil {
   185  		communicators = append(communicators, comm)
   186  	}
   187  
   188  	// Final assembly
   189  	return &server.Components{
   190  		Datastore: db,
   191  		ServiceFactories: map[string]service.Factory{
   192  			"GRPC": grpcservice.Factory,
   193  			"NOOP": service.NOOPFactory,
   194  		},
   195  		Communicators: communicators,
   196  		Authorizer:    auth,
   197  		Stats:         statsCollector,
   198  		Notifier:      nn,
   199  		Listener:      nl,
   200  		Admin:         admSrv,
   201  		HealthCheck:   healthCheck,
   202  	}, nil
   203  }