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  }