/
bom.go
145 lines (119 loc) · 2.91 KB
/
bom.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package runner
import (
"errors"
"os"
"path/filepath"
"strings"
"sync"
"github.com/BurntSushi/toml"
plugin "github.com/gideaworx/terraform-exporter-plugin-go"
)
var ErrPluginNotFound = errors.New("plugin not found")
type PluginType string
const (
Native PluginType = "native"
NodeJS PluginType = "nodejs"
Python PluginType = "python"
Java PluginType = "java"
)
type PluginIntegrity struct {
Checksum string `toml:"checksum"`
Algorithm string `toml:"algorithm"`
}
type PluginSource struct {
Type string `toml:"type"`
Name string `toml:"name"`
URL string `toml:"url,omitempty"`
}
type BillOfMaterials struct {
Name string `toml:"name"`
Type PluginType `toml:"type"`
Source PluginSource `toml:"source"`
Version plugin.Version `toml:"version,omitempty"`
Integrity *PluginIntegrity `toml:"integrity,omitempty"`
Provides []plugin.CommandInfo `toml:"provides,omitempty"`
}
func FindPluginsForCommand(cmdName string) ([][2]string, error) {
boms, err := LoadInstalledBOMs()
if err != nil {
return nil, err
}
if strings.Contains(cmdName, "/") {
parts := strings.Split(cmdName, "/")
for i := 0; i < len(boms); i++ {
if parts[0] == boms[i].Name {
boms = []BillOfMaterials{boms[i]}
break
}
}
}
matching := [][2]string{}
cmdName = cmdName[strings.Index(cmdName, "/")+1:]
for _, bom := range boms {
for _, c := range bom.Provides {
if c.Name == cmdName {
matching = append(matching, [2]string{bom.Name, c.Name})
break
}
}
}
return matching, nil
}
func LoadPluginBOM(pluginName string) (BillOfMaterials, error) {
boms, err := loadBoms(pluginName)
if err != nil {
return BillOfMaterials{}, err
}
if len(boms) == 0 {
return BillOfMaterials{}, ErrPluginNotFound
}
return boms[0], nil
}
func LoadInstalledBOMs() ([]BillOfMaterials, error) {
return loadBoms("*")
}
func loadBoms(dir string) ([]BillOfMaterials, error) {
home, err := PluginHome()
if err != nil {
return nil, err
}
pattern := filepath.Join(home, dir, "export-plugin.bom")
matches, err := filepath.Glob(pattern)
if err != nil {
return nil, err
}
boms := make([]BillOfMaterials, 0, len(matches))
lock := &sync.Mutex{}
errs := []error{}
wg := &sync.WaitGroup{}
wg.Add(len(matches))
for _, match := range matches {
go func(bomFile string) {
defer wg.Done()
bomBytes, err := os.ReadFile(bomFile)
if err != nil {
errs = append(errs, err)
return
}
var bom BillOfMaterials
if err = toml.Unmarshal(bomBytes, &bom); err != nil {
errs = append(errs, err)
return
}
lock.Lock()
boms = append(boms, bom)
lock.Unlock()
}(match)
}
wg.Wait()
if len(errs) > 0 {
msg := "the following errors occurred loading bills of material: "
errstrs := []string{}
for _, e := range errs {
errstrs = append(errstrs, e.Error())
}
msg += strings.Join(errstrs, ", ")
return nil, errors.New(msg)
}
return boms, nil
}