dev-tools/v2tool/server/tool.go (119 lines of code) (raw):

// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. package server import ( "crypto/tls" "fmt" "strings" "sync" "time" "github.com/elastic/elastic-agent-client/v7/dev-tools/v2tool/manager" "github.com/elastic/elastic-agent-client/v7/pkg/client/mock" "github.com/elastic/elastic-agent-client/v7/pkg/proto" "github.com/elastic/elastic-agent-libs/logp" "github.com/gofrs/uuid/v5" "google.golang.org/grpc" "google.golang.org/grpc/credentials" protobuf "google.golang.org/protobuf/proto" ) // Tool carries the main management state for the application, including both the V2 server runtime type Tool struct { ca *CertificateAuthority pair *Pair srv mock.StubServerV2 manager *manager.InputManager serverName string } // NewToolServer initializes a new tool server instance func NewToolServer(inputMgr *manager.InputManager) (*Tool, error) { serverName, err := genServerName() if err != nil { return nil, fmt.Errorf("error generating server name: %w", err) } ca, pair, err := generateCreds(serverName) if err != nil { return nil, fmt.Errorf("error generating credentials: %w", err) } var mut sync.Mutex start := time.Now() srv := mock.StubServerV2{ CheckinV2Impl: func(observed *proto.CheckinObserved) *proto.CheckinExpected { mut.Lock() defer mut.Unlock() return inputMgr.Checkin(observed, start) }, ActionImpl: func(response *proto.ActionResponse) error { return nil }, ActionsChan: make(chan *mock.PerformAction, 100), } tool := &Tool{ ca: ca, pair: pair, srv: srv, serverName: serverName, manager: inputMgr, } return tool, nil } // StartServer starts the V2 mock server and writes the connection info to the client func (tool *Tool) StartServer() error { log := logp.L() cert, err := tls.X509KeyPair(tool.pair.Crt, tool.pair.Key) if err != nil { return fmt.Errorf("error generating X509 keypair: %w", err) } creds := credentials.NewServerTLSFromCert(&cert) err = tool.srv.Start(grpc.Creds(creds)) if err != nil { return fmt.Errorf("Error starting server: %w", err) } log.Debugf("Started V2 server") err = tool.writeConnInfo() if err != nil { return fmt.Errorf("Error writing connection info for process: %w", err) } log.Debugf("Wrote config to client") return nil } // writeConnInfo writes the GRPC connection info to the running client func (tool *Tool) writeConnInfo() error { log := logp.L() services := []proto.ConnInfoServices{proto.ConnInfoServices_CheckinV2} token, err := uuid.NewV4() if err != nil { return fmt.Errorf("error generating token: %w", err) } addr := fmt.Sprintf(":%d", tool.srv.Port) connInfo := &proto.ConnInfo{ Addr: addr, ServerName: tool.serverName, Token: token.String(), CaCert: tool.ca.Crt(), PeerCert: tool.pair.Crt, PeerKey: tool.pair.Key, Services: services, } log.Debugf("Creating config for client with address %s and services: %v", connInfo.Addr, connInfo.Services) infoBytes, err := protobuf.Marshal(connInfo) if err != nil { return fmt.Errorf("failed to marshal connection information: %w", err) } err = tool.manager.WriteToClient(infoBytes) if err != nil { return fmt.Errorf("error writing connection info to client: %w", err) } return nil } func generateCreds(serverName string) (*CertificateAuthority, *Pair, error) { ca, err := NewCA() if err != nil { return nil, nil, fmt.Errorf("Error generatint CA certs: %w", err) } pair, err := ca.GeneratePairWithName(serverName) if err != nil { return nil, nil, fmt.Errorf("error generating cert pair: %w", err) } return ca, pair, nil } func genServerName() (string, error) { u, err := uuid.NewV4() if err != nil { return "", err } return strings.Replace(u.String(), "-", "", -1), nil }