Skip to content

Commit 3a2c3fb

Browse files
authored
Add functionality to inspect nodes (#104)
- Updated the README to reflect changes - Removed the output dir from query custom, and moved it to its own command - We can either see the node in stdout or write it to a file Signed-off-by: neilnaveen <[email protected]>
1 parent 8ea49f4 commit 3a2c3fb

File tree

9 files changed

+115
-48
lines changed

9 files changed

+115
-48
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,36 +59,36 @@
5959
_Redis must be running at `localhost:6379`. If not, please use `make docker-up` to start Redis._
6060

6161
1. **Start the API server:**
62-
```shell
62+
```sh
6363
minefield start-service
6464
```
6565

66-
2. **Ingest the `test` SBOM directory:**
66+
2. **Ingest the `testdata/small` SBOM directory:**
6767
```sh
68-
minefield ingest sbom testdata
68+
minefield ingest sbom testdata/small
6969
```
7070
3. **Cache the data:**
7171
```sh
7272
minefield cache
7373
```
74-
4. **Run the leaderboard custom with "dependents PACKAGE":**
74+
4. **Run the leaderboard custom with "dependents library":**
7575
- This command generates a ranked list of packages, ordered by the number of other packages that depend on them.
7676
```sh
77-
minefield leaderboard custom "dependents PACKAGE"
77+
minefield leaderboard custom "dependents library"
7878
```
7979
5. **Run a query on the top value from the leaderboard:**
8080
- This command queries the dependents for a specific package, in this case `dep2`.
8181
```sh
82-
minefield query "dependents PACKAGE dep2"
82+
minefield query custom "dependents library pkg:dep2@1.0.0"
8383
```
8484
6. **Run queries to see the shared dependencies of `lib-A` and `dep1`, and `lib-A` and `lib-B`:**
8585
- These queries output the intersection of two queries, finding package dependencies shared between each pair.
8686
```sh
87-
minefield query "dependencies PACKAGE pkg:generic/[email protected] and dependencies PACKAGE pkg:generic/[email protected]"
87+
minefield query custom "dependencies library pkg:[email protected] and dependencies library pkg:[email protected]"
8888
```
8989
7. **Run queries with the visualizer:**
9090
```sh
91-
minefield query "dependents PACKAGE pkg:generic/[email protected]" --visualize
91+
minefield query custom "dependents library pkg:[email protected]" --visualize
9292
```
9393

9494
## To Start Using Minefield

cmd/query/custom/custom.go

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
package query
1+
package custom
22

33
import (
44
"bufio"
55
"fmt"
66
"net/http"
77
"os"
8-
"path/filepath"
98
"strconv"
109
"strings"
1110

@@ -14,22 +13,18 @@ import (
1413
apiv1 "github.com/bit-bom/minefield/gen/api/v1"
1514
"github.com/bit-bom/minefield/gen/api/v1/apiv1connect"
1615
"github.com/bit-bom/minefield/pkg/graph"
17-
"github.com/bit-bom/minefield/pkg/tools"
18-
"github.com/goccy/go-json"
1916
"github.com/olekukonko/tablewriter"
2017
"github.com/spf13/cobra"
2118
)
2219

2320
type options struct {
2421
storage graph.Storage
25-
outputdir string
2622
visualizerAddr string
2723
maxOutput int
2824
visualize bool
2925
}
3026

3127
func (o *options) AddFlags(cmd *cobra.Command) {
32-
cmd.Flags().StringVar(&o.outputdir, "output-dir", "", "specify dir to write the output to")
3328
cmd.Flags().IntVar(&o.maxOutput, "max-output", 10, "max output length")
3429
cmd.Flags().BoolVar(&o.visualize, "visualize", false, "visualize the query")
3530
cmd.Flags().StringVar(&o.visualizerAddr, "addr", "8081", "address to run the visualizer on")
@@ -73,32 +68,6 @@ func (o *options) Run(cmd *cobra.Command, args []string) error {
7368
}
7469

7570
table.Append([]string{node.Name, node.Type, strconv.Itoa(int(node.Id))})
76-
77-
if o.outputdir != "" {
78-
data, err := json.MarshalIndent(node.Metadata, "", " ")
79-
if err != nil {
80-
return fmt.Errorf("failed to marshal node metadata: %w", err)
81-
}
82-
if _, err := os.Stat(o.outputdir); os.IsNotExist(err) {
83-
if err := os.MkdirAll(o.outputdir, os.ModePerm); err != nil {
84-
return fmt.Errorf("failed to create output directory: %w", err)
85-
}
86-
} else if err != nil {
87-
return fmt.Errorf("failed to check output directory: %w", err)
88-
}
89-
90-
filePath := filepath.Join(o.outputdir, tools.SanitizeFilename(node.Name)+".json")
91-
file, err := os.Create(filePath)
92-
if err != nil {
93-
return fmt.Errorf("failed to create file: %w", err)
94-
}
95-
defer file.Close()
96-
97-
_, err = file.Write(data)
98-
if err != nil {
99-
return fmt.Errorf("failed to write data to file: %w", err)
100-
}
101-
}
10271
count++
10372
}
10473

cmd/query/globsearch/globsearch.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package query
1+
package globsearch
22

33
import (
44
"fmt"

cmd/query/output/output.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package output
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
"os"
8+
"strconv"
9+
10+
"connectrpc.com/connect"
11+
apiv1 "github.com/bit-bom/minefield/gen/api/v1"
12+
"github.com/bit-bom/minefield/gen/api/v1/apiv1connect"
13+
"github.com/bit-bom/minefield/pkg/graph"
14+
"github.com/spf13/cobra"
15+
)
16+
17+
type options struct {
18+
storage graph.Storage
19+
outputFile string
20+
}
21+
22+
func (o *options) AddFlags(cmd *cobra.Command) {
23+
24+
cmd.Flags().StringVar(&o.outputFile, "output-file", "", "output file")
25+
}
26+
27+
func (o *options) Run(cmd *cobra.Command, args []string) error {
28+
httpClient := &http.Client{}
29+
addr := os.Getenv("BITBOMDEV_ADDR")
30+
if addr == "" {
31+
addr = "http://localhost:8089"
32+
}
33+
client := apiv1connect.NewGraphServiceClient(httpClient, addr)
34+
35+
// Create a new context
36+
ctx := cmd.Context()
37+
38+
// Create a new QueryRequest
39+
req := connect.NewRequest(&apiv1.GetNodeByNameRequest{
40+
Name: args[0],
41+
})
42+
43+
res, err := client.GetNodeByName(ctx, req)
44+
if err != nil {
45+
return fmt.Errorf("query failed: %v", err)
46+
}
47+
48+
node := res.Msg.Node
49+
if node == nil {
50+
return fmt.Errorf("node not found: %s", args[0])
51+
}
52+
53+
// Unmarshal the metadata JSON string into a map
54+
var metadata map[string]interface{}
55+
if err := json.Unmarshal([]byte(node.Metadata), &metadata); err != nil {
56+
return fmt.Errorf("failed to unmarshal metadata: %v", err)
57+
}
58+
59+
output := struct {
60+
Name string `json:"name"`
61+
Type string `json:"type"`
62+
ID string `json:"id"`
63+
Metadata map[string]interface{} `json:"metadata"` // Change type to map
64+
}{
65+
Name: node.Name,
66+
Type: node.Type,
67+
ID: strconv.Itoa(int(node.Id)),
68+
Metadata: metadata, // Use the unmarshaled map
69+
}
70+
71+
jsonOutput, err := json.MarshalIndent(output, "", " ")
72+
if err != nil {
73+
return fmt.Errorf("failed to marshal json: %v", err)
74+
}
75+
if o.outputFile != "" {
76+
err = os.WriteFile(o.outputFile, jsonOutput, 0644)
77+
if err != nil {
78+
return fmt.Errorf("failed to write output file: %v", err)
79+
}
80+
} else {
81+
fmt.Println(string(jsonOutput))
82+
}
83+
84+
return nil
85+
}
86+
87+
func New(storage graph.Storage) *cobra.Command {
88+
o := &options{
89+
storage: storage,
90+
}
91+
cmd := &cobra.Command{
92+
Use: "output [node name]",
93+
Short: "Output the node, with its metadata",
94+
Args: cobra.ExactArgs(1),
95+
RunE: o.Run,
96+
DisableAutoGenTag: true,
97+
}
98+
o.AddFlags(cmd)
99+
100+
return cmd
101+
}

cmd/query/query.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package query
33
import (
44
custom "github.com/bit-bom/minefield/cmd/query/custom"
55
globsearch "github.com/bit-bom/minefield/cmd/query/globsearch"
6+
output "github.com/bit-bom/minefield/cmd/query/output"
67
"github.com/bit-bom/minefield/pkg/graph"
78
"github.com/spf13/cobra"
89
)
@@ -16,6 +17,6 @@ func New(storage graph.Storage) *cobra.Command {
1617

1718
cmd.AddCommand(custom.New(storage))
1819
cmd.AddCommand(globsearch.New(storage))
19-
20+
cmd.AddCommand(output.New(storage))
2021
return cmd
2122
}

pkg/tools/ingest/sbom.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,6 @@ func processSBOMFile(filePath string, storage graph.Storage) error {
5959
return fmt.Errorf("file path is empty")
6060
}
6161

62-
file, err := os.Open(filePath)
63-
if err != nil {
64-
return fmt.Errorf("failed to open file %s: %w", filePath, err)
65-
}
66-
6762
// Create a new protobom reader
6863
r := reader.New()
6964

@@ -88,7 +83,8 @@ func processSBOMFile(filePath string, storage graph.Storage) error {
8883
if purl == "" {
8984
purl = fmt.Sprintf("pkg:%s@%s", node.GetName(), node.GetVersion())
9085
}
91-
graphNode, err := graph.AddNode(storage, "library", file, purl)
86+
87+
graphNode, err := graph.AddNode(storage, "library", node, purl)
9288
if err != nil {
9389
if errors.Is(err, graph.ErrNodeAlreadyExists) {
9490
// log.Printf("Skipping node %s: %s\n", node.GetName(), err)
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)