Skip to content

Commit

Permalink
⭐ add internal mock provider
Browse files Browse the repository at this point in the history
This allows us to use a recording **without connecting to any actual asset**.

Assuming you have made a previous recording via e.g.

```bash
cnquery scan local -f examples/os.mql.yaml --record recording.json
```

You can now use that recording with the `mock` provider in a few
different ways:

**1. Executing scans:**

```bash
cnquery scan mock -f examples/os.mql.yaml --use-recording recording.json
```

**2. Running commands:**

```bash
cnquery run mock --use-recording recording.json -c sshd.config.params
```

**3. Opening a shell:**

```bash
cnquery shell mock --use-recording recording.json
```

A few follow-ups and notes:
1. The current `providers/mock` will be removed. It is now replaced by this runner. A few tests still depend on it.
2. This was only implemented for a few providers (`core` and `os`), more will be migrated to support this.
3. No provider can claim the mock connector or connection type. Mock is a builtin functionality that goes hand in hand with recording.

Signed-off-by: Dominik Richter <[email protected]>
  • Loading branch information
arlimus committed Sep 17, 2023
1 parent 7907256 commit 5c11108
Show file tree
Hide file tree
Showing 18 changed files with 363 additions and 94 deletions.
6 changes: 4 additions & 2 deletions cli/providers/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,14 +307,16 @@ func setConnector(provider *plugin.Provider, connector *plugin.Connector, run fu
if recordingPath == "" {
recordingPath = useRecording
}
doRecord := record != ""

runtime.Recording, err = providers.NewRecording(recordingPath, providers.RecordingOptions{
DoRecord: record != "",
recording, err := providers.NewRecording(recordingPath, providers.RecordingOptions{
DoRecord: doRecord,
PrettyPrintJSON: pretty,
})
if err != nil {
log.Fatal().Msg(err.Error())
}
runtime.SetRecording(recording)

cliRes, err := runtime.Provider.Instance.Plugin.ParseCLI(&plugin.ParseCLIReq{
Connector: connector.Name,
Expand Down
3 changes: 2 additions & 1 deletion explorer/scan/local_scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ func (s *LocalScanner) distributeJob(job *Job, ctx context.Context, upstream *up
log.Error().Err(err).Msg("unable to detect provider for asset")
continue
}
runtime.SetRecording(s.recording)

if err := runtime.Connect(&plugin.ConnectReq{
Features: cnquery.GetFeatures(ctx),
Expand Down Expand Up @@ -214,7 +215,7 @@ func (s *LocalScanner) distributeJob(job *Job, ctx context.Context, upstream *up
}

// attach recording before connect, so it is tied to the asset
runtime.Recording = s.recording
runtime.SetRecording(s.recording)

err := runtime.Connect(&plugin.ConnectReq{
Features: config.Features,
Expand Down
15 changes: 10 additions & 5 deletions providers-sdk/v1/plugin/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,35 @@ func (m *GRPCClient) ParseCLI(req *ParseCLIReq) (*ParseCLIRes, error) {
return m.client.ParseCLI(context.Background(), req)
}

func (m *GRPCClient) Connect(req *ConnectReq, callback ProviderCallback) (*ConnectRes, error) {
func (m *GRPCClient) connect(req *ConnectReq, callback ProviderCallback) {
helper := &GRPCProviderCallbackServer{Impl: callback}

var s *grpc.Server
serverFunc := func(opts []grpc.ServerOption) *grpc.Server {
s = grpc.NewServer(opts...)
RegisterProviderCallbackServer(s, helper)

return s
}

brokerID := m.broker.NextId()
req.CallbackServer = brokerID
go m.broker.AcceptAndServe(brokerID, serverFunc)

res, err := m.client.Connect(context.Background(), req)

// Note: the reverse connection is not closed explicitly. It stays open
// until the process is eventually stopped. Connect should only be called
// once per connected asset, thus the reverse connection is also only
// open for the duration of said connection.
// In the future, we may want to explicitly disconnect and re-use providers.
}

func (m *GRPCClient) Connect(req *ConnectReq, callback ProviderCallback) (*ConnectRes, error) {
m.connect(req, callback)
return m.client.Connect(context.Background(), req)
}

return res, err
func (m *GRPCClient) MockConnect(req *ConnectReq, callback ProviderCallback) (*ConnectRes, error) {
m.connect(req, callback)
return m.client.MockConnect(context.Background(), req)
}

func (m *GRPCClient) Shutdown(req *ShutdownReq) (*ShutdownRes, error) {
Expand Down
1 change: 1 addition & 0 deletions providers-sdk/v1/plugin/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type ProviderCallback interface {
type ProviderPlugin interface {
ParseCLI(req *ParseCLIReq) (*ParseCLIRes, error)
Connect(req *ConnectReq, callback ProviderCallback) (*ConnectRes, error)
MockConnect(req *ConnectReq, callback ProviderCallback) (*ConnectRes, error)
Shutdown(req *ShutdownReq) (*ShutdownRes, error)
GetData(req *DataReq) (*DataRes, error)
StoreData(req *StoreReq) (*StoreRes, error)
Expand Down
107 changes: 57 additions & 50 deletions providers-sdk/v1/plugin/plugin.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions providers-sdk/v1/plugin/plugin.proto
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ message StoreRes {}
service ProviderPlugin {
rpc ParseCLI(ParseCLIReq) returns (ParseCLIRes);
rpc Connect(ConnectReq) returns (ConnectRes);
rpc MockConnect(ConnectReq) returns (ConnectRes);
rpc Shutdown(ShutdownReq) returns (ShutdownRes);
rpc GetData(DataReq) returns (DataRes);
rpc StoreData(StoreReq) returns (StoreRes);
Expand Down
Loading

0 comments on commit 5c11108

Please sign in to comment.