Skip to content

Commit 99fce72

Browse files
committed
wip: registry+v1 to helm chart
- handling for all install mode combinations is working - subscription.spec.config support
1 parent 87dfebe commit 99fce72

File tree

11 files changed

+1546
-9
lines changed

11 files changed

+1546
-9
lines changed

cmd/registryv1-to-helm/main.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
7+
"github.com/spf13/cobra"
8+
"helm.sh/helm/v3/pkg/chartutil"
9+
10+
v2 "github.com/operator-framework/operator-controller/internal/rukpak/convert/v2"
11+
)
12+
13+
func main() {
14+
if err := rootCmd().Execute(); err != nil {
15+
os.Exit(1)
16+
}
17+
}
18+
19+
func rootCmd() *cobra.Command {
20+
cmd := &cobra.Command{
21+
Use: "registryv1-to-helm <registry+v1-directory-path> [output-path]",
22+
Args: cobra.RangeArgs(1, 2),
23+
Run: func(cmd *cobra.Command, args []string) {
24+
registryv1Path := args[0]
25+
26+
saveDir := "."
27+
if len(args) == 2 {
28+
saveDir = args[1]
29+
}
30+
31+
chrt, err := v2.RegistryV1ToHelmChart(cmd.Context(), os.DirFS(registryv1Path))
32+
if err != nil {
33+
cmd.PrintErr(err)
34+
os.Exit(1)
35+
}
36+
37+
if err := chartutil.SaveDir(chrt, saveDir); err != nil {
38+
cmd.PrintErr(err)
39+
os.Exit(1)
40+
}
41+
cmd.Printf("Chart saved to %s\n", filepath.Join(saveDir, chrt.Metadata.Name))
42+
},
43+
}
44+
return cmd
45+
}

cmd/registryv1-to-helm/values.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
affinity:
2+
nodeAffinity:
3+
requiredDuringSchedulingIgnoredDuringExecution:
4+
nodeSelectorTerms:
5+
- matchExpressions:
6+
- key: node-role.kubernetes.io/master
7+
operator: Exists
8+
nodeSelector:
9+
overrideKey1: overrideValue1
10+
11+
selector:
12+
overrideKey2: overrideValue2
13+
14+
tolerations:
15+
- effect: NoSchedule
16+
key: node-role.kubernetes.io/master2
17+
18+
volumes:
19+
- name: argocd-operator-token-5z5z2
20+
emptyDir: {}
21+
22+
env:
23+
- name: WATCH_NAMESPACE
24+
value: BAR
25+
26+
envFrom:
27+
- configMapRef:
28+
name: my-configmap
29+
- secretRef:
30+
name: my-secret
31+
32+
resources:
33+
requests:
34+
cpu: 100m
35+
memory: 100Mi
36+
37+
volumeMounts:
38+
- name: tmp
39+
mountPath: override

go.mod

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,19 @@ require (
1111
github.com/containers/image/v5 v5.32.2
1212
github.com/fsnotify/fsnotify v1.7.0
1313
github.com/go-logr/logr v1.4.2
14+
github.com/go-openapi/loads v0.22.0
15+
github.com/go-openapi/spec v0.21.0
1416
github.com/google/go-cmp v0.6.0
1517
github.com/google/go-containerregistry v0.20.2
18+
github.com/joeycumines/go-dotnotation v0.0.0-20180131115956-2d3612e36c5d
1619
github.com/onsi/ginkgo/v2 v2.20.2
1720
github.com/onsi/gomega v1.34.2
1821
github.com/opencontainers/go-digest v1.0.0
1922
github.com/operator-framework/api v0.27.0
2023
github.com/operator-framework/catalogd v0.35.0
2124
github.com/operator-framework/helm-operator-plugins v0.7.0
2225
github.com/operator-framework/operator-registry v1.48.0
26+
github.com/spf13/cobra v1.8.1
2327
github.com/spf13/pflag v1.0.5
2428
github.com/stretchr/testify v1.9.0
2529
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
@@ -101,9 +105,7 @@ require (
101105
github.com/go-openapi/errors v0.22.0 // indirect
102106
github.com/go-openapi/jsonpointer v0.21.0 // indirect
103107
github.com/go-openapi/jsonreference v0.21.0 // indirect
104-
github.com/go-openapi/loads v0.22.0 // indirect
105108
github.com/go-openapi/runtime v0.28.0 // indirect
106-
github.com/go-openapi/spec v0.21.0 // indirect
107109
github.com/go-openapi/strfmt v0.23.0 // indirect
108110
github.com/go-openapi/swag v0.23.0 // indirect
109111
github.com/go-openapi/validate v0.24.0 // indirect
@@ -193,7 +195,6 @@ require (
193195
github.com/sigstore/sigstore v1.8.4 // indirect
194196
github.com/sirupsen/logrus v1.9.3 // indirect
195197
github.com/spf13/cast v1.7.0 // indirect
196-
github.com/spf13/cobra v1.8.1 // indirect
197198
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect
198199
github.com/stoewer/go-strcase v1.3.0 // indirect
199200
github.com/stretchr/objx v0.5.2 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,8 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
392392
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
393393
github.com/joelanford/ignore v0.1.0 h1:VawbTDeg5EL+PN7W8gxVzGerfGpVo3gFdR5ZAqnkYRk=
394394
github.com/joelanford/ignore v0.1.0/go.mod h1:Vb0PQMAQXK29fmiPjDukpO8I2NTcp1y8LbhFijD1/0o=
395+
github.com/joeycumines/go-dotnotation v0.0.0-20180131115956-2d3612e36c5d h1:ljoJyU5NEhe3LWXrRSHnYqWtQehQSJ+9d9tfIGNbpSw=
396+
github.com/joeycumines/go-dotnotation v0.0.0-20180131115956-2d3612e36c5d/go.mod h1:siHz7M0dAufA9aNRidkfckDSVB/S+Ld7allyOe5uxVg=
395397
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
396398
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
397399
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=

internal/rukpak/convert/registryv1.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ type Plain struct {
3939
Objects []client.Object
4040
}
4141

42-
func RegistryV1ToHelmChart(ctx context.Context, rv1 fs.FS, installNamespace string, watchNamespaces []string) (*chart.Chart, error) {
42+
func LoadRegistryV1(ctx context.Context, rv1 fs.FS) (*RegistryV1, error) {
4343
l := log.FromContext(ctx)
4444

4545
reg := RegistryV1{}
@@ -100,8 +100,16 @@ func RegistryV1ToHelmChart(ctx context.Context, rv1 fs.FS, installNamespace stri
100100
}); err != nil {
101101
return nil, err
102102
}
103+
return &reg, nil
104+
}
105+
106+
func RegistryV1ToHelmChart(ctx context.Context, rv1 fs.FS, installNamespace string, watchNamespaces []string) (*chart.Chart, error) {
107+
reg, err := LoadRegistryV1(ctx, rv1)
108+
if err != nil {
109+
return nil, err
110+
}
103111

104-
return toChart(reg, installNamespace, watchNamespaces)
112+
return toChart(*reg, installNamespace, watchNamespaces)
105113
}
106114

107115
func toChart(in RegistryV1, installNamespace string, watchNamespaces []string) (*chart.Chart, error) {
@@ -195,17 +203,17 @@ func Convert(in RegistryV1, installNamespace string, targetNamespaces []string)
195203
for _, depSpec := range in.CSV.Spec.InstallStrategy.StrategySpec.DeploymentSpecs {
196204
annotations := util.MergeMaps(in.CSV.Annotations, depSpec.Spec.Template.Annotations)
197205
annotations["olm.targetNamespaces"] = strings.Join(targetNamespaces, ",")
206+
depSpec.Spec.Template.Annotations = annotations
198207
deployments = append(deployments, appsv1.Deployment{
199208
TypeMeta: metav1.TypeMeta{
200209
Kind: "Deployment",
201210
APIVersion: appsv1.SchemeGroupVersion.String(),
202211
},
203212

204213
ObjectMeta: metav1.ObjectMeta{
205-
Namespace: installNamespace,
206-
Name: depSpec.Name,
207-
Labels: depSpec.Label,
208-
Annotations: annotations,
214+
Namespace: installNamespace,
215+
Name: depSpec.Name,
216+
Labels: depSpec.Label,
209217
},
210218
Spec: depSpec.Spec,
211219
})
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package parametrize
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"regexp"
7+
"strings"
8+
9+
"github.com/joeycumines/go-dotnotation/dotnotation"
10+
"k8s.io/apimachinery/pkg/util/uuid"
11+
"sigs.k8s.io/yaml"
12+
)
13+
14+
var _ Instruction = (*block)(nil)
15+
16+
// Wrap the given field path into a template block.
17+
func Block(exp string, fieldPath string) Instruction {
18+
return &block{
19+
marker: string(uuid.NewUUID()),
20+
pipeline: exp,
21+
fieldPath: fieldPath,
22+
}
23+
}
24+
25+
// block represents the instruction to wrap the YAML structure
26+
// at the given fieldPath into a template block.
27+
type block struct {
28+
marker string
29+
pipeline string
30+
fieldPath string
31+
32+
mapKey string // map key to merge structures on.
33+
originalValue interface{} // YAML object found at position.
34+
writeValue interface{} // YAML object to write out.
35+
}
36+
37+
func (b *block) Mark(obj map[string]interface{}) error {
38+
var parentValue interface{}
39+
lastDotIdx := strings.LastIndex(b.fieldPath, ".")
40+
if lastDotIdx != -1 {
41+
var err error
42+
parentPath := b.fieldPath[:lastDotIdx]
43+
parentValue, err = dotnotation.Get(obj, parentPath)
44+
if err != nil {
45+
return err
46+
}
47+
} else {
48+
parentValue = obj
49+
}
50+
51+
origValue, err := dotnotation.Get(obj, b.fieldPath)
52+
if err != nil {
53+
return err
54+
}
55+
b.originalValue = origValue
56+
if _, ok := parentValue.([]interface{}); ok {
57+
// wrap array items in an array again.
58+
b.mapKey = b.fieldPath
59+
if _, ok := origValue.([]interface{}); !ok {
60+
origValue = []interface{}{origValue}
61+
}
62+
} else {
63+
b.mapKey = b.fieldPath
64+
if lastDotIdx != -1 {
65+
b.mapKey = b.fieldPath[lastDotIdx+1:]
66+
}
67+
// wrap field values in map key.
68+
origValue = map[string]interface{}{
69+
b.mapKey: origValue,
70+
}
71+
}
72+
73+
b.writeValue = origValue
74+
return dotnotation.Set(obj, b.fieldPath, b.marker)
75+
}
76+
77+
func (b *block) Replace(in []byte) ([]byte, error) {
78+
re := regexp.MustCompile(`(?m).*` + b.marker + `\n*`)
79+
found := re.Find(in)
80+
if found == nil {
81+
// marker not found -> do nothing.
82+
return nil, nil
83+
}
84+
85+
il := indentLevel(found)
86+
origB, err := yaml.Marshal(b.writeValue)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
i := strings.Repeat(" ", il)
92+
var replacement string
93+
replacement += fmt.Sprintf("%s{{- %s }}\n", i, b.pipeline)
94+
for _, l := range bytes.Split(bytes.TrimSpace(origB), []byte("\n")) {
95+
replacement += fmt.Sprintf("%s%s\n", i, l)
96+
}
97+
replacement += i + "{{- end }}\n"
98+
99+
return re.ReplaceAll(in, []byte(replacement)), nil
100+
}
101+
102+
func (b *block) Priority() int {
103+
return 100
104+
}
105+
106+
func indentLevel(b []byte) int {
107+
var indentLevel int
108+
for _, c := range b {
109+
if rune(c) == rune(' ') {
110+
indentLevel++
111+
} else {
112+
break
113+
}
114+
}
115+
return indentLevel
116+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package parametrize
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"regexp"
7+
"strings"
8+
9+
"k8s.io/apimachinery/pkg/util/uuid"
10+
"sigs.k8s.io/yaml"
11+
)
12+
13+
var _ Instruction = (*mergeBlock)(nil)
14+
15+
// Wrap the given field path into a template block merging data structures.
16+
func MergeBlock(pipeline string, fieldPath string) Instruction {
17+
return &mergeBlock{
18+
block: block{
19+
marker: string(uuid.NewUUID()),
20+
pipeline: pipeline,
21+
fieldPath: fieldPath,
22+
},
23+
}
24+
}
25+
26+
// mergeBlock represents the instruction to wrap the YAML structure
27+
// at the given fieldPath into a template block merging data structures.
28+
type mergeBlock struct {
29+
block
30+
}
31+
32+
func (b *mergeBlock) Replace(in []byte) ([]byte, error) {
33+
re := regexp.MustCompile(`(?m).*` + b.marker + `\n*`)
34+
found := re.Find(in)
35+
if found == nil {
36+
// marker not found -> do nothing.
37+
return nil, nil
38+
}
39+
40+
il := indentLevel(found)
41+
origB, err := yaml.Marshal(b.writeValue)
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
i := strings.Repeat(" ", il)
47+
var replacement bytes.Buffer
48+
if _, err = fmt.Fprintf(&replacement, "%s{{- define %q }}\n", i, b.marker); err != nil {
49+
return nil, err
50+
}
51+
for _, l := range bytes.Split(bytes.TrimSpace(origB), []byte("\n")) {
52+
if _, err = fmt.Fprintf(&replacement, "%s%s\n", i, l); err != nil {
53+
return nil, err
54+
}
55+
}
56+
if _, err = fmt.Fprintf(&replacement, `%s{{- end }}{{"\n"}}`+"\n", i); err != nil {
57+
return nil, err
58+
}
59+
if _, isSlice := b.originalValue.([]interface{}); isSlice {
60+
// assume data is a slice.
61+
if _, err = fmt.Fprintf(&replacement,
62+
`%s{{- dict %q (concat (fromYaml (include %q .)).%s (%s)) | toYaml | indent %d }}`+"\n",
63+
i, b.mapKey, b.marker, b.mapKey, b.pipeline, len(i),
64+
); err != nil {
65+
return nil, err
66+
}
67+
return re.ReplaceAll(in, replacement.Bytes()), nil
68+
}
69+
// assume data is a map.
70+
if _, err = fmt.Fprintf(&replacement,
71+
`%s{{- mergeOverwrite (fromYaml (include %q .)) (%s) | toYaml | indent %d }}`+"\n",
72+
i, b.marker, b.pipeline, len(i),
73+
); err != nil {
74+
return nil, err
75+
}
76+
return re.ReplaceAll(in, replacement.Bytes()), nil
77+
}

0 commit comments

Comments
 (0)