go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/core/provider/provider.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package provider
     5  
     6  import (
     7  	"errors"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"go.mondoo.com/cnquery/llx"
    12  	"go.mondoo.com/cnquery/providers-sdk/v1/plugin"
    13  	"go.mondoo.com/cnquery/providers/core/resources"
    14  	"go.mondoo.com/cnquery/types"
    15  )
    16  
    17  const defaultConnection uint32 = 1
    18  
    19  type Service struct {
    20  	runtimes         map[uint32]*plugin.Runtime
    21  	lastConnectionID uint32
    22  }
    23  
    24  func Init() *Service {
    25  	return &Service{
    26  		runtimes: map[uint32]*plugin.Runtime{},
    27  	}
    28  }
    29  
    30  func (s *Service) ParseCLI(req *plugin.ParseCLIReq) (*plugin.ParseCLIRes, error) {
    31  	return nil, errors.New("core doesn't offer any connectors")
    32  }
    33  
    34  func (s *Service) Connect(req *plugin.ConnectReq, callback plugin.ProviderCallback) (*plugin.ConnectRes, error) {
    35  	if req == nil || req.Asset == nil {
    36  		return nil, errors.New("no connection data provided")
    37  	}
    38  
    39  	s.lastConnectionID++
    40  	connID := s.lastConnectionID
    41  	runtime := &plugin.Runtime{
    42  		Callback:     callback,
    43  		HasRecording: req.HasRecording,
    44  	}
    45  	s.runtimes[connID] = runtime
    46  
    47  	asset := req.Asset
    48  	_, err := resources.CreateResource(runtime, "asset", map[string]*llx.RawData{
    49  		"ids":      llx.ArrayData(llx.TArr2Raw(asset.PlatformIds), types.String),
    50  		"platform": llx.StringData(asset.Platform.Name),
    51  		"name":     llx.StringData(asset.Name),
    52  		"kind":     llx.StringData(asset.Platform.Kind),
    53  		"runtime":  llx.StringData(asset.Platform.Runtime),
    54  		"version":  llx.StringData(asset.Platform.Version),
    55  		"arch":     llx.StringData(asset.Platform.Arch),
    56  		"title":    llx.StringData(asset.Platform.PrettyTitle()),
    57  		"family":   llx.ArrayData(llx.TArr2Raw(asset.Platform.Family), types.String),
    58  		"build":    llx.StringData(asset.Platform.Build),
    59  		"labels":   llx.MapData(llx.TMap2Raw(asset.Platform.Labels), types.String),
    60  		"fqdn":     llx.StringData(""),
    61  	})
    62  	if err != nil {
    63  		return nil, errors.New("failed to init core, cannot set asset metadata")
    64  	}
    65  
    66  	if len(asset.Connections) > 0 {
    67  		_, err = resources.CreateResource(runtime, "mondoo", map[string]*llx.RawData{
    68  			"capabilities": llx.ArrayData(llx.TArr2Raw(asset.Connections[0].Capabilities), types.String),
    69  		})
    70  		if err != nil {
    71  			return nil, errors.New("failed to init core, cannot set connection metadata")
    72  		}
    73  	}
    74  
    75  	return &plugin.ConnectRes{
    76  		Id:   connID,
    77  		Name: "core",
    78  	}, nil
    79  }
    80  
    81  func (s *Service) MockConnect(req *plugin.ConnectReq, callback plugin.ProviderCallback) (*plugin.ConnectRes, error) {
    82  	return s.Connect(req, callback)
    83  }
    84  
    85  // Shutdown is automatically called when the shell closes.
    86  // It is not necessary to implement this method.
    87  // If you want to do some cleanup, you can do it here.
    88  func (s *Service) Shutdown(req *plugin.ShutdownReq) (*plugin.ShutdownRes, error) {
    89  	return &plugin.ShutdownRes{}, nil
    90  }
    91  
    92  func (s *Service) GetData(req *plugin.DataReq) (*plugin.DataRes, error) {
    93  	runtime, ok := s.runtimes[req.Connection]
    94  	if !ok {
    95  		return nil, errors.New("connection " + strconv.FormatUint(uint64(req.Connection), 10) + " not found")
    96  	}
    97  
    98  	args := plugin.PrimitiveArgsToRawDataArgs(req.Args, runtime)
    99  
   100  	if req.ResourceId == "" && req.Field == "" {
   101  		res, err := resources.CreateResource(runtime, req.Resource, args)
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  
   106  		rd := llx.ResourceData(res, req.Resource).Result()
   107  		return &plugin.DataRes{
   108  			Data: rd.Data,
   109  		}, nil
   110  	}
   111  
   112  	resource, ok := runtime.Resources.Get(req.Resource + "\x00" + req.ResourceId)
   113  	if !ok {
   114  		return nil, errors.New("resource '" + req.Resource + "' (id: " + req.ResourceId + ") doesn't exist")
   115  	}
   116  
   117  	return resources.GetData(resource, req.Field, args), nil
   118  }
   119  
   120  func (s *Service) StoreData(req *plugin.StoreReq) (*plugin.StoreRes, error) {
   121  	runtime, ok := s.runtimes[req.Connection]
   122  	if !ok {
   123  		return nil, errors.New("connection " + strconv.FormatUint(uint64(req.Connection), 10) + " not found")
   124  	}
   125  
   126  	var errs []string
   127  	for i := range req.Resources {
   128  		info := req.Resources[i]
   129  
   130  		args, err := plugin.ProtoArgsToRawDataArgs(info.Fields)
   131  		if err != nil {
   132  			errs = append(errs, "failed to add cached "+info.Name+" (id: "+info.Id+"), failed to parse arguments")
   133  			continue
   134  		}
   135  
   136  		resource, ok := runtime.Resources.Get(info.Name + "\x00" + info.Id)
   137  		if !ok {
   138  			resource, err = resources.CreateResource(runtime, info.Name, args)
   139  			if err != nil {
   140  				errs = append(errs, "failed to add cached "+info.Name+" (id: "+info.Id+"), creation failed: "+err.Error())
   141  				continue
   142  			}
   143  
   144  			runtime.Resources.Set(info.Name+"\x00"+info.Id, resource)
   145  		}
   146  
   147  		for k, v := range args {
   148  			if err := resources.SetData(resource, k, v); err != nil {
   149  				errs = append(errs, "failed to add cached "+info.Name+" (id: "+info.Id+"), field error: "+err.Error())
   150  			}
   151  		}
   152  	}
   153  
   154  	if len(errs) != 0 {
   155  		return nil, errors.New(strings.Join(errs, ", "))
   156  	}
   157  	return &plugin.StoreRes{}, nil
   158  }