go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/explorer/services.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package explorer 5 6 import ( 7 "net/http" 8 9 llx "go.mondoo.com/cnquery/llx" 10 "go.mondoo.com/ranger-rpc" 11 "golang.org/x/sync/semaphore" 12 ) 13 14 type ResolvedVersion string 15 16 const ( 17 V2Code ResolvedVersion = "v2" 18 ) 19 20 var globalEmpty = &Empty{} 21 22 type Services struct { 23 QueryHub 24 QueryConductor 25 } 26 27 // LocalServices is an implementation of the explorer for a local execution. 28 // It has an optional upstream-handler embedded. If a local service does not 29 // yield results for a request, and the upstream handler is defined, it will 30 // be used instead. 31 type LocalServices struct { 32 DataLake DataLake 33 Upstream *Services 34 Incognito bool 35 runtime llx.Runtime 36 } 37 38 // NewLocalServices initializes a reasonably configured local services struct 39 func NewLocalServices(datalake DataLake, uuid string, runtime llx.Runtime) *LocalServices { 40 return &LocalServices{ 41 DataLake: datalake, 42 Upstream: nil, 43 Incognito: false, 44 runtime: runtime, 45 } 46 } 47 48 // NewRemoteServices initializes a services struct with a remote endpoint 49 func NewRemoteServices(addr string, auth []ranger.ClientPlugin, httpClient *http.Client) (*Services, error) { 50 if httpClient == nil { 51 httpClient = ranger.DefaultHttpClient() 52 } 53 // restrict parallel upstream connections to two connections 54 httpClient.Transport = NewMaxParallelConnTransport(httpClient.Transport, 2) 55 56 queryHub, err := NewQueryHubClient(addr, httpClient, auth...) 57 if err != nil { 58 return nil, err 59 } 60 61 queryConductor, err := NewQueryConductorClient(addr, httpClient, auth...) 62 if err != nil { 63 return nil, err 64 } 65 66 return &Services{ 67 QueryHub: queryHub, 68 QueryConductor: queryConductor, 69 }, nil 70 } 71 72 // MaxParallelConnHTTPTransport restricts the parallel connections that the client is doing upstream. 73 // This has many advantages: 74 // - we do not run into max ulimit issues because of parallel execution 75 // - we do not ddos our server in case something is wrong upstream 76 // - implementing this as http.RoundTripper has the advantage that the http timeout still applies and calls are canceled properly on the client-side 77 type MaxParallelConnHTTPTransport struct { 78 transport http.RoundTripper 79 parallelConns *semaphore.Weighted 80 } 81 82 // RoundTrip executes a single HTTP transaction, returning 83 // a Response for the provided Request. 84 func (t *MaxParallelConnHTTPTransport) RoundTrip(r *http.Request) (*http.Response, error) { 85 err := t.parallelConns.Acquire(r.Context(), 1) 86 if err != nil { 87 return nil, err 88 } 89 defer t.parallelConns.Release(1) 90 return t.transport.RoundTrip(r) 91 } 92 93 // NewMaxParallelConnTransport creates a transport with parallel HTTP connections 94 func NewMaxParallelConnTransport(transport http.RoundTripper, parallel int64) *MaxParallelConnHTTPTransport { 95 return &MaxParallelConnHTTPTransport{ 96 transport: transport, 97 parallelConns: semaphore.NewWeighted(parallel), 98 } 99 }