Skip to content

Commit b662638

Browse files
committed
Add type spec
2 parents 17dfc0c + cfae593 commit b662638

File tree

13 files changed

+420
-18
lines changed

13 files changed

+420
-18
lines changed

cmd/helm2go-operator-sdk/new/chart.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,12 @@ func scaffoldOverwrite(outputDir, kind, apiVersion string, rcache *resourcecache
149149
ok = templating.ResourceFileStructure(rcache, resDir)
150150
ok = templating.TemplatesToFiles(templates, resDir)
151151
if !ok {
152-
return fmt.Errorf("Writing to File Error")
152+
return fmt.Errorf("error writing to file")
153+
}
154+
155+
//begin overwritting the types file
156+
ok = templating.OverwriteKindTypes(outputDir, kind, apiVersion)
157+
if !ok {
158+
return fmt.Errorf("error overwriting kind types")
153159
}
154-
return nil
155160
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/Masterminds/sprig v0.0.0-20190301161902-9f8fceff796f // indirect
99
github.com/cyphar/filepath-securejoin v0.2.2 // indirect
1010
github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4 // indirect
11+
github.com/go-openapi/inflect v0.19.0 // indirect
1112
github.com/gobwas/glob v0.2.3 // indirect
1213
github.com/huandu/xstrings v1.2.0 // indirect
1314
github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
2323
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
2424
github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4 h1:bRzFpEzvausOAt4va+I/22BZ1vXDtERngp0BNYDKej0=
2525
github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
26+
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
27+
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
2628
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
2729
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
2830
github.com/gogo/protobuf v1.0.0 h1:2jyBKDKU/8v3v2xVR2PtiWQviFUyiaGk2rpfyFT8rTM=

internal/pkg/load/validation_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package load
22

33
import (
4+
"os"
5+
"path/filepath"
46
"testing"
57

68
. "github.com/onsi/ginkgo"
@@ -14,7 +16,8 @@ func TestValidation(t *testing.T) {
1416

1517
var _ = Describe("Resource Validation", func() {
1618
It("Validates Resources", func() {
17-
_, err := PerformResourceValidation("/home/sjakati/go/src/github.com/redhat-nfvpe/helm2go-operator-sdk/test/resources")
19+
gopath := os.Getenv("GOPATH")
20+
_, err := PerformResourceValidation(filepath.Join(gopath, "src/github.com/redhat-nfvpe/helm2go-operator-sdk/test/resources"))
1821
Expect(err).ToNot(HaveOccurred())
1922
})
2023
})
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package templating
2+
3+
import (
4+
"bytes"
5+
"text/template"
6+
)
7+
8+
// CRTemplateConfig is used to template the appropriate CR Scaffold
9+
type CRTemplateConfig struct {
10+
APIVersion string
11+
Kind string
12+
LowerKind string
13+
Spec CRSpec
14+
}
15+
16+
// CRSpec contains the specific values needed for the CRTemplate
17+
type CRSpec struct {
18+
placeholder string
19+
}
20+
21+
// NewCRTemplateConfig ...
22+
// TODO : need to implement the function to create an actual spec struct
23+
func NewCRTemplateConfig(apiVersion, kind, lowerKind string, spec CRSpec) *CRTemplateConfig {
24+
return &CRTemplateConfig{
25+
APIVersion: apiVersion,
26+
Kind: kind,
27+
LowerKind: lowerKind,
28+
Spec: spec,
29+
}
30+
}
31+
32+
// Execute renders the template and returns the templated string
33+
func (c *CRTemplateConfig) Execute() (string, error) {
34+
temp, err := template.New("CRTemplate").Parse(c.GetTemplate())
35+
if err != nil {
36+
return "", err
37+
}
38+
39+
var wr bytes.Buffer
40+
err = temp.Execute(&wr, c)
41+
if err != nil {
42+
return "", err
43+
}
44+
return wr.String(), nil
45+
}
46+
47+
// GetTemplate returns the necessary template for the CR
48+
func (c *CRTemplateConfig) GetTemplate() string {
49+
50+
return `apiVersion: {{ .Resource.APIVersion }}
51+
kind: {{ .Resource.Kind }}
52+
metadata:
53+
name: example-{{ .Resource.LowerKind }}
54+
spec:
55+
{{- with .Spec }}
56+
{{ . | indent 2 }}
57+
{{- else }}
58+
# Add fields here
59+
size: 3
60+
{{- end }}
61+
`
62+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package templating
2+
3+
import (
4+
"bytes"
5+
"io/ioutil"
6+
"reflect"
7+
"text/template"
8+
9+
"gopkg.in/yaml.v2"
10+
)
11+
12+
// NewSpecConfig returns a spec config
13+
func NewSpecConfig(name, valuesPath string) *SpecConfig {
14+
yamlMap, err := getYAMLMap(valuesPath)
15+
if err != nil {
16+
panic(err)
17+
}
18+
return &SpecConfig{
19+
Name: name,
20+
InstanceValue: expandInstanceValue(yamlMap),
21+
}
22+
}
23+
24+
// SpecConfig is the YAML spec config
25+
type SpecConfig struct {
26+
Name string
27+
InstanceValue string
28+
}
29+
30+
// GetTemplate returns the necessary template
31+
func (s *SpecConfig) GetTemplate() string {
32+
structTemplate := `
33+
type {{ .Name }} struct {
34+
{{ .InstanceValue }}
35+
}
36+
`
37+
return structTemplate
38+
}
39+
40+
// Execute renders the template and returns the templated string
41+
func (s *SpecConfig) Execute() (string, error) {
42+
temp, err := template.New("TypeSpec").Parse(s.GetTemplate())
43+
if err != nil {
44+
return "", err
45+
}
46+
47+
s.Name = s.Name + "Type"
48+
49+
var wr bytes.Buffer
50+
err = temp.Execute(&wr, s)
51+
if err != nil {
52+
return "", err
53+
}
54+
return wr.String(), nil
55+
}
56+
57+
func getYAMLMap(yamlInput string) (map[interface{}]interface{}, error) {
58+
var output map[interface{}]interface{}
59+
//read bytes yaml
60+
bytes, err := ioutil.ReadFile(yamlInput)
61+
if err != nil {
62+
return nil, err
63+
}
64+
err = yaml.Unmarshal(bytes, &output)
65+
if err != nil {
66+
return nil, err
67+
}
68+
return output, nil
69+
}
70+
71+
func expandInstanceValue(instance map[interface{}]interface{}) string {
72+
var result string
73+
for k, v := range instance {
74+
strK, _ := k.(string)
75+
if reflect.TypeOf(v).Kind() != reflect.Map {
76+
result = result + "\t" + strK + " string " + getJSONTag(strK) + "\n"
77+
} else {
78+
mapV := v.(map[interface{}]interface{})
79+
result = result + "\t" + strK + " struct { " + "\n" + "\t" + expandInstanceValue(mapV) + "}" + "\n"
80+
}
81+
}
82+
return result
83+
}
84+
85+
func getJSONTag(input string) string {
86+
return "`" + "json:" + `"` + input + `,omitempty"` + "`"
87+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package templating
2+
3+
import (
4+
"bytes"
5+
"text/template"
6+
)
7+
8+
// KindTypesTemplateConfig is used to overwrite the generated kind_types.go file
9+
type KindTypesTemplateConfig struct {
10+
Kind string
11+
Version string
12+
LowerPlural string
13+
KindSpec string
14+
SpecConfigurationName string
15+
SpecConfigurationTypeName string
16+
LowerCamelSpecConfigurationName string
17+
}
18+
19+
// NewKindTypesTemplateConfig returns a new KindTypesConfig
20+
func NewKindTypesTemplateConfig(kind, apiVersion, kindSpec string) *KindTypesTemplateConfig {
21+
22+
lowerPlural := getLowerPlural(kind)
23+
components, _ := getAPIVersionComponents(apiVersion)
24+
version := components.Version
25+
kindSpecName := getKindSpecName(kind)
26+
kindSpecTypeName := getKindSpecTypeName(kind)
27+
kindSpecLowerCamel := kindToLowerCamel(kind)
28+
29+
// create config and return address
30+
return &KindTypesTemplateConfig{
31+
Kind: kind,
32+
Version: version,
33+
LowerPlural: lowerPlural,
34+
KindSpec: kindSpec,
35+
SpecConfigurationName: kindSpecName,
36+
SpecConfigurationTypeName: kindSpecTypeName,
37+
LowerCamelSpecConfigurationName: kindSpecLowerCamel,
38+
}
39+
}
40+
41+
// Execute renders the template and returns the templated string
42+
func (k *KindTypesTemplateConfig) Execute() (string, error) {
43+
temp, err := template.New("CRTemplate").Parse(k.GetTemplate())
44+
if err != nil {
45+
return "", err
46+
}
47+
48+
var wr bytes.Buffer
49+
err = temp.Execute(&wr, k)
50+
if err != nil {
51+
return "", err
52+
}
53+
return wr.String(), nil
54+
}
55+
56+
// GetTemplate returns the necessary template for the kind_types.go
57+
func (k *KindTypesTemplateConfig) GetTemplate() string {
58+
return `package {{ .Version }}
59+
import (
60+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
61+
)
62+
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
63+
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
64+
65+
{{ .KindSpec }}
66+
67+
68+
// {{.Kind}}Spec defines the desired state of {{.Kind}}
69+
// +k8s:openapi-gen=true
70+
type {{.Kind}}Spec struct {
71+
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
72+
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
73+
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
74+
75+
{{ .SpecConfigurationName }} {{ .SpecConfigurationTypeName }}` + "`" + `json:"{{ .LowerCamelSpecConfigurationName }},omitempty"` + "`" + `
76+
77+
}
78+
// {{.Kind}}Status defines the observed state of {{.Kind}}
79+
// +k8s:openapi-gen=true
80+
type {{.Kind}}Status struct {
81+
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
82+
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
83+
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
84+
}
85+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
86+
// {{.Kind}} is the Schema for the {{ .LowerPlural }} API
87+
// +k8s:openapi-gen=true
88+
// +kubebuilder:subresource:status
89+
type {{.Kind}} struct {
90+
metav1.TypeMeta ` + "`" + `json:",inline"` + "`" + `
91+
metav1.ObjectMeta ` + "`" + `json:"metadata,omitempty"` + "`" + `
92+
Spec {{.Kind}}Spec ` + "`" + `json:"spec,omitempty"` + "`" + `
93+
Status {{.Kind}}Status ` + "`" + `json:"status,omitempty"` + "`" + `
94+
}
95+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
96+
// {{.Kind}}List contains a list of {{.Kind}}
97+
type {{.Kind}}List struct {
98+
metav1.TypeMeta ` + "`" + `json:",inline"` + "`" + `
99+
metav1.ListMeta ` + "`" + `json:"metadata,omitempty"` + "`" + `
100+
Items []{{ .Kind }} ` + "`" + `json:"items"` + "`" + `
101+
}
102+
func init() {
103+
SchemeBuilder.Register(&{{.Kind}}{}, &{{.Kind}}List{})
104+
}
105+
`
106+
}

internal/pkg/templating/resources.go

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"regexp"
88
"strings"
99

10+
"github.com/go-openapi/inflect"
1011
"github.com/iancoleman/strcase"
1112

1213
"github.com/redhat-nfvpe/helm2go-operator-sdk/internal/resourcecache"
@@ -159,12 +160,13 @@ func getImportMap(outputDir, kind, apiVersion string) map[string]string {
159160
}
160161

161162
func getAppTypeImport(outputDir, apiVersion string) string {
162-
comps, err := getAPIVersionComponents(apiVersion)
163+
var comps []string
164+
components, err := getAPIVersionComponents(apiVersion)
163165
if err != nil {
164166
panic(err)
165167
}
166168
sp := strings.Split(outputDir, "src/")
167-
comps = append([]string{sp[len(sp)-1], "pkg", "apis"}, comps...)
169+
comps = append([]string{sp[len(sp)-1], "pkg", "apis"}, components.Subdomain, components.Version)
168170

169171
return filepath.Join(comps...)
170172

@@ -181,26 +183,52 @@ func getResourceCRImport(outputDir, resourceType string) string {
181183
return importString
182184
}
183185

184-
func getAPIVersionComponents(input string) ([]string, error) {
185-
186-
var group string
187-
var version string
186+
// APIComponents used to seperate api components
187+
type APIComponents struct {
188+
Subdomain string
189+
Domain string
190+
Version string
191+
}
188192

189-
fmt.Printf("Input: %v", input)
193+
func getAPIVersionComponents(input string) (*APIComponents, error) {
190194

191195
// matches the input string and returns the groups
192196
pattern := regexp.MustCompile(`(.*)\.(.*)\..*\/(.*)`)
193197
matches := pattern.FindStringSubmatch(input)
194198
if l := len(matches); l != 3+1 {
195-
return []string{}, fmt.Errorf("expected four matches, received %d instead", l)
199+
return nil, fmt.Errorf("expected four matches, received %d instead", l)
196200
}
197-
group = matches[1]
198-
version = matches[3]
199201

200-
var result = []string{
201-
group,
202-
version,
202+
var result = &APIComponents{
203+
matches[1],
204+
matches[2],
205+
matches[3],
203206
}
204207

205208
return result, nil
206209
}
210+
211+
func getKindSpecName(kind string) string {
212+
return kind + "Spec"
213+
}
214+
215+
func getKindSpecTypeName(kind string) string {
216+
return kind + "Spec" + "Type"
217+
}
218+
219+
func getLowerPlural(kind string) string {
220+
return inflect.Pluralize(inflect.CamelizeDownFirst(kind))
221+
}
222+
223+
func getSpecifiedOrDefault(attribute string, values map[string]string) string {
224+
// check if attribute is in the map
225+
val, ok := values[attribute]
226+
if !ok {
227+
return getDefault(attribute)
228+
}
229+
return val
230+
}
231+
232+
func getDefault(attribute string) string {
233+
return "placeholder"
234+
}

0 commit comments

Comments
 (0)