github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/server/grpcservice/service.go (about) 1 // Copyright 2017 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 grpcservice defines a service.Service which passes all received messages to 16 // a destination host using grpc. 17 package grpcservice 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "sync" 24 "time" 25 26 "google.golang.org/grpc" 27 "google.golang.org/grpc/codes" 28 "google.golang.org/grpc/credentials" 29 "google.golang.org/grpc/credentials/insecure" 30 "google.golang.org/grpc/status" 31 32 "github.com/google/fleetspeak/fleetspeak/src/server/service" 33 34 fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak" 35 ggrpc "github.com/google/fleetspeak/fleetspeak/src/server/grpcservice/proto/fleetspeak_grpcservice" 36 gpb "github.com/google/fleetspeak/fleetspeak/src/server/grpcservice/proto/fleetspeak_grpcservice" 37 spb "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server" 38 ) 39 40 // GRPCService is a service.Service which forwards all received 41 // messages to an implementation of ggrpc.Processor. 42 type GRPCService struct { 43 sctx service.Context 44 conn *grpc.ClientConn 45 client ggrpc.ProcessorClient 46 l sync.RWMutex 47 } 48 49 // NewGRPCService returns a service.Service which forwards received 50 // messages to c. Implementations which wish to implement transport 51 // security or otherwise control the connection used should define a 52 // service.Factory based on this. 53 func NewGRPCService(c *grpc.ClientConn) *GRPCService { 54 ret := &GRPCService{ 55 conn: c, 56 } 57 if c != nil { 58 ret.client = ggrpc.NewProcessorClient(c) 59 } 60 return ret 61 } 62 63 func (s *GRPCService) Start(sctx service.Context) error { 64 s.sctx = sctx 65 return nil 66 } 67 68 func (s *GRPCService) Stop() error { 69 s.l.Lock() 70 defer s.l.Unlock() 71 72 if s.conn != nil { 73 s.conn.Close() 74 } 75 s.conn = nil 76 s.client = nil 77 78 return nil 79 } 80 81 // Update replaces the current connection with the given one, may be nil to 82 // indicate that a connection is currently unavailable. 83 func (s *GRPCService) Update(c *grpc.ClientConn) { 84 s.l.Lock() 85 defer s.l.Unlock() 86 87 if s.conn != nil { 88 s.conn.Close() 89 } 90 if c == nil { 91 s.conn = nil 92 s.client = nil 93 } else { 94 s.conn = c 95 s.client = ggrpc.NewProcessorClient(c) 96 } 97 } 98 99 func (s *GRPCService) ProcessMessage(ctx context.Context, m *fspb.Message) error { 100 var err error 101 d := time.Second 102 103 s.l.RLock() 104 defer s.l.RUnlock() 105 if s.client == nil { 106 return service.TemporaryError{E: errors.New("connection unavailable")} 107 } 108 109 // TODO: Remove retry logic when possible. 110 L: 111 for { 112 _, err = s.client.Process(ctx, m) 113 if err == nil { 114 return nil 115 } 116 t := time.NewTimer(d) 117 select { 118 case <-ctx.Done(): 119 t.Stop() 120 break L 121 case <-t.C: 122 d = d * 2 123 } 124 } 125 // Tell Fleetspeak to retry (minutes later) in the most obviously retryable 126 // cases. Assume permission issues are configuration errors which will be 127 // fixed eventually. 128 if s, ok := status.FromError(err); ok { 129 c := s.Code() 130 if c == codes.DeadlineExceeded || 131 c == codes.Unavailable || 132 c == codes.Aborted || 133 c == codes.Canceled || 134 c == codes.Unauthenticated || 135 c == codes.PermissionDenied { 136 err = service.TemporaryError{E: err} 137 } 138 } 139 return err 140 } 141 142 // Factory is a server.ServiceFactory that creates a GRPCService. 143 // 144 // cfg must contain a fleetspeak.grpcservice.Config message describing 145 // how to dial the target grpc server. 146 func Factory(cfg *spb.ServiceConfig) (service.Service, error) { 147 conf := &gpb.Config{} 148 if err := cfg.Config.UnmarshalTo(conf); err != nil { 149 return nil, err 150 } 151 152 switch { 153 case conf.Insecure: 154 con, err := grpc.DialContext(context.Background(), conf.Target, grpc.WithTransportCredentials(insecure.NewCredentials())) 155 if err != nil { 156 return nil, err 157 } 158 return NewGRPCService(con), nil 159 case conf.CertFile != "": 160 cred, err := credentials.NewClientTLSFromFile(conf.CertFile, "") 161 if err != nil { 162 return nil, err 163 } 164 con, err := grpc.DialContext(context.Background(), conf.Target, grpc.WithTransportCredentials(cred)) 165 if err != nil { 166 return nil, err 167 } 168 return NewGRPCService(con), nil 169 default: 170 return nil, fmt.Errorf("GRPCService requires either insecure or cert_file to be set, got: %+v", conf.String()) 171 } 172 }