Skip to content

Commit 5ec2c4f

Browse files
committed
add bundle entity
Signed-off-by: perdasilva <[email protected]>
1 parent 19161a1 commit 5ec2c4f

File tree

2 files changed

+488
-0
lines changed

2 files changed

+488
-0
lines changed
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
package entity
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"sync"
7+
8+
"github.com/blang/semver/v4"
9+
"github.com/operator-framework/deppy/pkg/deppy/input"
10+
"github.com/operator-framework/operator-registry/alpha/property"
11+
)
12+
13+
const PropertyBundlePath = "olm.bundle.path"
14+
15+
type ChannelProperties struct {
16+
property.Channel
17+
Replaces string `json:"replaces,omitempty"`
18+
Skips []string `json:"skips,omitempty"`
19+
SkipRange string `json:"skipRange,omitempty"`
20+
}
21+
22+
type PackageRequired struct {
23+
property.PackageRequired
24+
SemverRange *semver.Range `json:"-"`
25+
}
26+
27+
type GVK property.GVK
28+
29+
func (g GVK) String() string {
30+
return fmt.Sprintf(`group:"%s" version:"%s" kind:"%s"`, g.Group, g.Version, g.Kind)
31+
}
32+
33+
type GVKRequired property.GVKRequired
34+
35+
func (g GVKRequired) String() string {
36+
return fmt.Sprintf(`group:"%s" version:"%s" kind:"%s"`, g.Group, g.Version, g.Kind)
37+
}
38+
39+
type BundleEntity struct {
40+
*input.Entity
41+
42+
// these properties are lazy loaded as they are requested
43+
bundlePackage *property.Package
44+
providedGVKs []GVK
45+
requiredGVKs []GVKRequired
46+
requiredPackages []PackageRequired
47+
channelProperties *ChannelProperties
48+
semVersion *semver.Version
49+
bundlePath string
50+
mu sync.RWMutex
51+
}
52+
53+
func NewBundleEntity(entity *input.Entity) *BundleEntity {
54+
return &BundleEntity{
55+
Entity: entity,
56+
mu: sync.RWMutex{},
57+
}
58+
}
59+
60+
func (b *BundleEntity) PackageName() (string, error) {
61+
if err := b.loadPackage(); err != nil {
62+
return "", err
63+
}
64+
return b.bundlePackage.PackageName, nil
65+
}
66+
67+
func (b *BundleEntity) Version() (*semver.Version, error) {
68+
if err := b.loadPackage(); err != nil {
69+
return nil, err
70+
}
71+
return b.semVersion, nil
72+
}
73+
74+
func (b *BundleEntity) ProvidedGVKs() ([]GVK, error) {
75+
if err := b.loadProvidedGVKs(); err != nil {
76+
return nil, err
77+
}
78+
return b.providedGVKs, nil
79+
}
80+
81+
func (b *BundleEntity) RequiredGVKs() ([]GVKRequired, error) {
82+
if err := b.loadRequiredGVKs(); err != nil {
83+
return nil, err
84+
}
85+
return b.requiredGVKs, nil
86+
}
87+
88+
func (b *BundleEntity) RequiredPackages() ([]PackageRequired, error) {
89+
if err := b.loadRequiredPackages(); err != nil {
90+
return nil, err
91+
}
92+
return b.requiredPackages, nil
93+
}
94+
95+
func (b *BundleEntity) ChannelName() (string, error) {
96+
if err := b.loadChannelProperties(); err != nil {
97+
return "", err
98+
}
99+
return b.channelProperties.ChannelName, nil
100+
}
101+
102+
func (b *BundleEntity) ChannelProperties() (*ChannelProperties, error) {
103+
if err := b.loadChannelProperties(); err != nil {
104+
return nil, err
105+
}
106+
return b.channelProperties, nil
107+
}
108+
109+
func (b *BundleEntity) BundlePath() (string, error) {
110+
if err := b.loadBundlePath(); err != nil {
111+
return "", err
112+
}
113+
return b.bundlePath, nil
114+
}
115+
116+
func (b *BundleEntity) loadPackage() error {
117+
if b.bundlePackage == nil {
118+
bundlePackage, err := loadFromEntity[property.Package](b.Entity, property.TypePackage)
119+
if err != nil {
120+
return fmt.Errorf("error determining package for entity '%s': %s", b.ID, err)
121+
}
122+
b.bundlePackage = &bundlePackage
123+
if b.semVersion == nil {
124+
semVer, err := semver.Parse(b.bundlePackage.Version)
125+
if err != nil {
126+
return fmt.Errorf("could not parse semver (%s) for entity '%s': %s", b.bundlePackage.Version, b.ID, err)
127+
}
128+
b.semVersion = &semVer
129+
}
130+
}
131+
return nil
132+
}
133+
134+
func (b *BundleEntity) loadProvidedGVKs() error {
135+
b.mu.Lock()
136+
defer b.mu.Unlock()
137+
if b.providedGVKs == nil {
138+
providedGVKs, err := loadFromEntity[[]GVK](b.Entity, property.TypeGVK)
139+
if err != nil {
140+
return fmt.Errorf("error determining bundle provided gvks for entity '%s': %s", b.ID, err)
141+
}
142+
b.providedGVKs = providedGVKs
143+
}
144+
return nil
145+
}
146+
147+
func (b *BundleEntity) loadRequiredGVKs() error {
148+
b.mu.Lock()
149+
defer b.mu.Unlock()
150+
if b.requiredGVKs == nil {
151+
requiredGVKs, err := loadFromEntity[[]GVKRequired](b.Entity, property.TypeGVKRequired)
152+
if err != nil {
153+
return fmt.Errorf("error determining bundle required gvks for entity '%s': %s", b.ID, err)
154+
}
155+
b.requiredGVKs = requiredGVKs
156+
}
157+
return nil
158+
}
159+
160+
func (b *BundleEntity) loadRequiredPackages() error {
161+
b.mu.Lock()
162+
defer b.mu.Unlock()
163+
if b.requiredPackages == nil {
164+
requiredPackages, err := loadFromEntity[[]PackageRequired](b.Entity, property.TypePackageRequired)
165+
if err != nil {
166+
return fmt.Errorf("error determining bundle required packages for entity '%s': %s", b.ID, err)
167+
}
168+
for _, requiredPackage := range requiredPackages {
169+
semverRange, err := semver.ParseRange(requiredPackage.VersionRange)
170+
if err != nil {
171+
return fmt.Errorf("error determining bundle required package semver range for entity '%s': '%s'", b.ID, err)
172+
}
173+
requiredPackage.SemverRange = &semverRange
174+
}
175+
b.requiredPackages = requiredPackages
176+
}
177+
return nil
178+
}
179+
180+
func (b *BundleEntity) loadChannelProperties() error {
181+
b.mu.Lock()
182+
defer b.mu.Unlock()
183+
if b.channelProperties == nil {
184+
channel, err := loadFromEntity[ChannelProperties](b.Entity, property.TypeChannel)
185+
if err != nil {
186+
return fmt.Errorf("error determining bundle channel properties for entity '%s': %s", b.ID, err)
187+
}
188+
b.channelProperties = &channel
189+
}
190+
return nil
191+
}
192+
193+
func (b *BundleEntity) loadBundlePath() error {
194+
b.mu.Lock()
195+
defer b.mu.Unlock()
196+
if b.bundlePath == "" {
197+
bundlePath, err := loadFromEntity[string](b.Entity, PropertyBundlePath)
198+
if err != nil {
199+
return fmt.Errorf("error determining bundle path for entity '%s': %s", b.ID, err)
200+
}
201+
b.bundlePath = bundlePath
202+
}
203+
return nil
204+
}
205+
206+
func loadFromEntity[T interface{}](entity *input.Entity, propertyName string) (T, error) {
207+
deserializedProperty := *new(T)
208+
propertyValue, ok := entity.Properties[propertyName]
209+
if !ok {
210+
return deserializedProperty, fmt.Errorf("property '%s' not found", propertyName)
211+
}
212+
213+
if err := json.Unmarshal([]byte(propertyValue), &deserializedProperty); err != nil {
214+
return deserializedProperty, fmt.Errorf("property '%s' ('%s') could not be parsed: %s", propertyName, propertyValue, err)
215+
}
216+
return deserializedProperty, nil
217+
}

0 commit comments

Comments
 (0)