go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/apps/provider-scaffold/template/provider/provider.go.template (about) 1 package provider 2 3 import ( 4 "errors" 5 "strconv" 6 7 "go.mondoo.com/cnquery/llx" 8 "go.mondoo.com/cnquery/providers-sdk/v1/inventory" 9 "go.mondoo.com/cnquery/providers-sdk/v1/plugin" 10 "go.mondoo.com/cnquery/providers-sdk/v1/upstream" 11 "{{ .GoPackage }}/connection" 12 "{{ .GoPackage }}/resources" 13 ) 14 15 type Service struct { 16 runtimes map[uint32]*plugin.Runtime 17 lastConnectionID uint32 18 } 19 20 func Init() *Service { 21 return &Service{ 22 runtimes: map[uint32]*plugin.Runtime{}, 23 lastConnectionID: 0, 24 } 25 } 26 27 func (s *Service) ParseCLI(req *plugin.ParseCLIReq) (*plugin.ParseCLIRes, error) { 28 flags := req.Flags 29 if flags == nil { 30 flags = map[string]*llx.Primitive{} 31 } 32 33 conf := &inventory.Config{ 34 Type: req.Connector, 35 Options: map[string]string{}, 36 } 37 38 // Do custom flag parsing here 39 40 asset := inventory.Asset{ 41 Connections: []*inventory.Config{conf}, 42 } 43 44 return &plugin.ParseCLIRes{Asset: &asset}, nil 45 } 46 47 func (s *Service) Connect(req *plugin.ConnectReq, callback plugin.ProviderCallback) (*plugin.ConnectRes, error) { 48 if req == nil || req.Asset == nil { 49 return nil, errors.New("no connection data provided") 50 } 51 52 conn, err := s.connect(req, callback) 53 if err != nil { 54 return nil, err 55 } 56 57 // We only need to run the detection step when we don't have any asset information yet. 58 if req.Asset.Platform == nil { 59 if err := s.detect(req.Asset, conn); err != nil { 60 return nil, err 61 } 62 } 63 64 return &plugin.ConnectRes{ 65 Id: conn.ID(), 66 Name: conn.Name(), 67 Asset: req.Asset, 68 Inventory: nil, 69 }, nil 70 } 71 72 // Shutdown is automatically called when the shell closes. 73 // It is not necessary to implement this method. 74 // If you want to do some cleanup, you can do it here. 75 func (s *Service) Shutdown(req *plugin.ShutdownReq) (*plugin.ShutdownRes, error) { 76 return &plugin.ShutdownRes{}, nil 77 } 78 79 func (s *Service) connect(req *plugin.ConnectReq, callback plugin.ProviderCallback) (*connection.{{ .CamelcaseProviderID }}Connection, error) { 80 if len(req.Asset.Connections) == 0 { 81 return nil, errors.New("no connection options for asset") 82 } 83 84 asset := req.Asset 85 conf := asset.Connections[0] 86 var conn *connection.{{ .CamelcaseProviderID }}Connection 87 var err error 88 89 switch conf.Type { 90 default: 91 s.lastConnectionID++ 92 conn, err = connection.New{{ .CamelcaseProviderID }}Connection(s.lastConnectionID, asset, conf) 93 } 94 95 if err != nil { 96 return nil, err 97 } 98 99 var upstream *upstream.UpstreamClient 100 if req.Upstream != nil && !req.Upstream.Incognito { 101 upstream, err = req.Upstream.InitClient() 102 if err != nil { 103 return nil, err 104 } 105 } 106 107 asset.Connections[0].Id = conn.ID() 108 s.runtimes[conn.ID()] = &plugin.Runtime{ 109 Connection: conn, 110 Callback: callback, 111 HasRecording: req.HasRecording, 112 CreateResource: resources.CreateResource, 113 Upstream: upstream, 114 } 115 116 return conn, err 117 } 118 119 func (s *Service) detect(asset *inventory.Asset, conn *connection.{{ .CamelcaseProviderID }}Connection) error { 120 // TODO: adjust asset detection 121 asset.Id = conn.Conf.Type 122 asset.Name = conn.Conf.Host 123 124 asset.Platform = &inventory.Platform{ 125 Name: "{{ .ProviderID }}", 126 Family: []string{"{{ .ProviderID }}"}, 127 Kind: "api", 128 Title: "{{ .ProviderName }}", 129 } 130 131 // TODO: Add platform IDs 132 asset.PlatformIds = []string{"//platformid.api.mondoo.app/runtime/oci/"} 133 return nil 134 } 135 136 func (s *Service) GetData(req *plugin.DataReq) (*plugin.DataRes, error) { 137 runtime, ok := s.runtimes[req.Connection] 138 if !ok { 139 return nil, errors.New("connection " + strconv.FormatUint(uint64(req.Connection), 10) + " not found") 140 } 141 142 args := plugin.PrimitiveArgsToRawDataArgs(req.Args, runtime) 143 144 if req.ResourceId == "" && req.Field == "" { 145 res, err := resources.NewResource(runtime, req.Resource, args) 146 if err != nil { 147 return nil, err 148 } 149 150 rd := llx.ResourceData(res, res.MqlName()).Result() 151 return &plugin.DataRes{ 152 Data: rd.Data, 153 }, nil 154 } 155 156 resource, ok := runtime.Resources.Get(req.Resource + "\x00" + req.ResourceId) 157 if !ok { 158 // Note: Since resources are internally always created, there are only very 159 // few cases where we arrive here: 160 // 1. The caller is wrong. Possibly a mixup with IDs 161 // 2. The resource was loaded from a recording, but the field is not 162 // in the recording. Thus the resource was never created inside the 163 // plugin. We will attempt to create the resource and see if the field 164 // can be computed. 165 if !runtime.HasRecording { 166 return nil, errors.New("resource '" + req.Resource + "' (id: " + req.ResourceId + ") doesn't exist") 167 } 168 169 args, err := runtime.ResourceFromRecording(req.Resource, req.ResourceId) 170 if err != nil { 171 return nil, errors.New("attempted to load resource '" + req.Resource + "' (id: " + req.ResourceId + ") from recording failed: " + err.Error()) 172 } 173 174 resource, err = resources.CreateResource(runtime, req.Resource, args) 175 if err != nil { 176 return nil, errors.New("attempted to create resource '" + req.Resource + "' (id: " + req.ResourceId + ") from recording failed: " + err.Error()) 177 } 178 } 179 180 return resources.GetData(resource, req.Field, args), nil 181 } 182 183 func (s *Service) StoreData(req *plugin.StoreReq) (*plugin.StoreRes, error) { 184 return nil, errors.New("not yet implemented") 185 } 186 187 func (s *Service) MockConnect(req *plugin.ConnectReq, callback plugin.ProviderCallback) (*plugin.ConnectRes, error) { 188 return nil, errors.New("mock connect not yet implemented") 189 }