Skip to content

Commit

Permalink
Merge pull request #101 from pmorie/comment-fixes
Browse files Browse the repository at this point in the history
Comment fixes for code generation
  • Loading branch information
droot authored Apr 30, 2018
2 parents 5c2f3d8 + c98652c commit 4987c4d
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 82 deletions.
2 changes: 2 additions & 0 deletions cmd/internal/codegen/parse/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type controllerTags struct {
resource string
}

// parseControllers populates the list of controllers to generate code from the
// list of annotated types.
func (b *APIs) parseControllers() {
for _, c := range b.context.Order {
if IsController(c) {
Expand Down
6 changes: 3 additions & 3 deletions cmd/internal/codegen/parse/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ import (
// parseIndex indexes all types with the comment "// +resource=RESOURCE" by GroupVersionKind and
// GroupKindVersion
func (b *APIs) parseIndex() {
// Index resource by group version kind
// Index resource by group, version, kind
b.ByGroupVersionKind = map[string]map[string]map[string]*codegen.APIResource{}

// Index resources by group, kind, version
b.ByGroupKindVersion = map[string]map[string]map[string]*codegen.APIResource{}

// Index subresources
// Index subresources by group, version, kind
b.SubByGroupVersionKind = map[string]map[string]map[string]*types.Type{}

for _, c := range b.context.Order {
// The type is a subresource, add it to the
// The type is a subresource, add it to the subresource index
if IsAPISubresource(c) {
group := GetGroup(c)
version := GetVersion(c, group)
Expand Down
144 changes: 73 additions & 71 deletions cmd/internal/codegen/parse/rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ import (
"strings"

rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/gengo/types"
"k8s.io/apimachinery/pkg/apis/meta/v1"
)

// parseRBAC populates the RBAC rules for each annotated type.
func (b *APIs) parseRBAC() {
for _, c := range b.context.Order {
if IsRBAC(c) {
Expand All @@ -37,85 +38,86 @@ func (b *APIs) parseRBAC() {
}

func (b *APIs) getRBACTag(c *types.Type) []string {
comments := Comments(c.CommentLines)
resource := comments.getTags("rbac", ":")
resource = append(resource, comments.getTags("kubebuilder:rbac", ":")...)
if len(resource) == 0 {
panic(fmt.Errorf("Must specify +kubebuilder:rbac comment for type %v", c.Name))
}
return resource
comments := Comments(c.CommentLines)
resource := comments.getTags("rbac", ":")
resource = append(resource, comments.getTags("kubebuilder:rbac", ":")...)
if len(resource) == 0 {
panic(fmt.Errorf("Must specify +kubebuilder:rbac comment for type %v", c.Name))
}
return resource
}

func parseRBACTag(tag string) rbacv1.PolicyRule {
result := rbacv1.PolicyRule{}
for _, elem := range strings.Split(tag, ",") {
kv := strings.Split(elem, "=")
if len(kv) != 2 {
log.Fatalf("// +kubebuilder:rbac: tags must be key value pairs. Expected "+
"keys [groups=<group1;group2>,resources=<resource1;resource2>,verbs=<verb1;verb2>] "+
"Got string: [%s]", tag)
}
value := kv[1]
values := []string{}
if strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"") {
value = value[1 : len(value)-1]
}
values = strings.Split(value, ";")
switch kv[0] {
case "groups":
result.APIGroups = values
case "resources":
result.Resources = values
case "verbs":
result.Verbs = values
case "urls":
result.NonResourceURLs = values
}
}
return result
result := rbacv1.PolicyRule{}
for _, elem := range strings.Split(tag, ",") {
kv := strings.Split(elem, "=")
if len(kv) != 2 {
log.Fatalf("// +kubebuilder:rbac: tags must be key value pairs. Expected "+
"keys [groups=<group1;group2>,resources=<resource1;resource2>,verbs=<verb1;verb2>] "+
"Got string: [%s]", tag)
}
value := kv[1]
values := []string{}
if strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"") {
value = value[1 : len(value)-1]
}
values = strings.Split(value, ";")
switch kv[0] {
case "groups":
result.APIGroups = values
case "resources":
result.Resources = values
case "verbs":
result.Verbs = values
case "urls":
result.NonResourceURLs = values
}
}
return result
}

// parseInfomers populates the informers to generate on each annotated type.
func (b *APIs) parseInformers() {
for _, c := range b.context.Order {
if IsInformer(c) {
for _, tag := range b.getInformerTag(c) {
if b.Informers == nil {
b.Informers = map[v1.GroupVersionKind]bool{}
}
b.Informers[parseInformerTag(tag)] = true
}
}
}
for _, c := range b.context.Order {
if IsInformer(c) {
for _, tag := range b.getInformerTag(c) {
if b.Informers == nil {
b.Informers = map[v1.GroupVersionKind]bool{}
}
b.Informers[parseInformerTag(tag)] = true
}
}
}
}

func (b *APIs) getInformerTag(c *types.Type) []string {
comments := Comments(c.CommentLines)
resource := comments.getTags("informers", ":")
resource = append(resource, comments.getTags("kubebuilder:informers", ":")...)
if len(resource) == 0 {
panic(fmt.Errorf("Must specify +kubebuilder:informers comment for type %v", c.Name))
}
return resource
comments := Comments(c.CommentLines)
resource := comments.getTags("informers", ":")
resource = append(resource, comments.getTags("kubebuilder:informers", ":")...)
if len(resource) == 0 {
panic(fmt.Errorf("Must specify +kubebuilder:informers comment for type %v", c.Name))
}
return resource
}

func parseInformerTag(tag string) v1.GroupVersionKind {
result := v1.GroupVersionKind{}
for _, elem := range strings.Split(tag, ",") {
kv := strings.Split(elem, "=")
if len(kv) != 2 {
log.Fatalf("// +kubebuilder:informers: tags must be key value pairs. Expected "+
"keys [group=core,version=v1,kind=Pod] "+
"Got string: [%s]", tag)
}
value := kv[1]
switch kv[0] {
case "group":
result.Group = value
case "version":
result.Version = value
case "kind":
result.Kind = value
}
}
return result
}
result := v1.GroupVersionKind{}
for _, elem := range strings.Split(tag, ",") {
kv := strings.Split(elem, "=")
if len(kv) != 2 {
log.Fatalf("// +kubebuilder:informers: tags must be key value pairs. Expected "+
"keys [group=core,version=v1,kind=Pod] "+
"Got string: [%s]", tag)
}
value := kv[1]
switch kv[0] {
case "group":
result.Group = value
case "version":
result.Version = value
case "kind":
result.Kind = value
}
}
return result
}
19 changes: 11 additions & 8 deletions cmd/internal/codegen/parse/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
// IsAPIResource returns true if t has a +resource/+kubebuilder:resource comment tag
func IsAPIResource(t *types.Type) bool {
for _, c := range t.CommentLines {
if strings.Contains(c, "+resource") || strings.Contains(c, "+kubebuilder:resource"){
if strings.Contains(c, "+resource") || strings.Contains(c, "+kubebuilder:resource") {
return true
}
}
Expand Down Expand Up @@ -57,6 +57,7 @@ func IsNonNamespaced(t *types.Type) bool {
return false
}

// IsController returns true if t has a +controller or +kubebuilder:controller tag
func IsController(t *types.Type) bool {
for _, c := range t.CommentLines {
if strings.Contains(c, "+controller") || strings.Contains(c, "+kubebuilder:controller") {
Expand All @@ -66,6 +67,7 @@ func IsController(t *types.Type) bool {
return false
}

// IsRBAC returns true if t has a +rbac or +kubebuilder:rbac tag
func IsRBAC(t *types.Type) bool {
for _, c := range t.CommentLines {
if strings.Contains(c, "+rbac") || strings.Contains(c, "+kubebuilder:rbac") {
Expand All @@ -75,16 +77,16 @@ func IsRBAC(t *types.Type) bool {
return false
}

// IsInformer returns true if t has a +informers or +kubebuilder:informers tag
func IsInformer(t *types.Type) bool {
for _, c := range t.CommentLines {
if strings.Contains(c, "+informers") || strings.Contains(c, "+kubebuilder:informers") {
return true
}
}
return false
for _, c := range t.CommentLines {
if strings.Contains(c, "+informers") || strings.Contains(c, "+kubebuilder:informers") {
return true
}
}
return false
}


// IsAPISubresource returns true if t has a +subresource-request comment tag
func IsAPISubresource(t *types.Type) bool {
for _, c := range t.CommentLines {
Expand Down Expand Up @@ -162,6 +164,7 @@ func (c Comments) getTag(name, sep string) string {
return ""
}

// hasTag returns true if the Comments has a tag with the given name
func (c Comments) hasTag(name string) bool {
for _, c := range c {
prefix := fmt.Sprintf("+%s", name)
Expand Down
17 changes: 17 additions & 0 deletions cmd/internal/codegen/parse/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import (
"text/template"
)

// parseJSONSchemaProps populates the CRD field of each Group.Version.Resource,
// creating validations using the annotations on type fields.
func (b *APIs) parseJSONSchemaProps() {
for _, group := range b.APIs.Groups {
for _, version := range group.Versions {
Expand Down Expand Up @@ -94,6 +96,8 @@ func (b *APIs) getMeta() string {
}`
}

// typeToJSONSchemaProps returns a JSONSchemaProps object and its serialization
// in Go that describe the JSONSchema validations for the given type.
func (b *APIs) typeToJSONSchemaProps(t *types.Type, found sets.String, comments []string) (v1beta1.JSONSchemaProps, string) {
// Special cases
time := types.Name{Name: "Time", Package: "k8s.io/apimachinery/pkg/apis/meta/v1"}
Expand Down Expand Up @@ -165,6 +169,9 @@ var primitiveTemplate = template.Must(template.New("map-template").Parse(
{{ end -}}
}`))

// parsePrimitiveValidation returns a JSONSchemaProps object and its
// serialization in Go that describe the validations for the given primitive
// type.
func (b *APIs) parsePrimitiveValidation(t *types.Type, found sets.String, comments []string) (v1beta1.JSONSchemaProps, string) {
props := v1beta1.JSONSchemaProps{Type: string(t.Name.Name)}

Expand Down Expand Up @@ -211,6 +218,8 @@ var mapTemplate = template.Must(template.New("map-template").Parse(
},
}`))

// parseMapValidation returns a JSONSchemaProps object and its serialization in
// Go that describe the validations for the given map type.
func (b *APIs) parseMapValidation(t *types.Type, found sets.String, comments []string) (v1beta1.JSONSchemaProps, string) {
additionalProps, _ := b.typeToJSONSchemaProps(t.Elem, found, comments)
props := v1beta1.JSONSchemaProps{
Expand All @@ -235,6 +244,8 @@ var arrayTemplate = template.Must(template.New("array-template").Parse(
},
}`))

// parseArrayValidation returns a JSONSchemaProps object and its serialization in
// Go that describe the validations for the given array type.
func (b *APIs) parseArrayValidation(t *types.Type, found sets.String, comments []string) (v1beta1.JSONSchemaProps, string) {
items, result := b.typeToJSONSchemaProps(t.Elem, found, comments)
props := v1beta1.JSONSchemaProps{
Expand Down Expand Up @@ -264,6 +275,8 @@ var objectTemplate = template.Must(template.New("object-template").Parse(
},
}`))

// parseObjectValidation returns a JSONSchemaProps object and its serialization in
// Go that describe the validations for the given object type.
func (b *APIs) parseObjectValidation(t *types.Type, found sets.String, comments []string) (v1beta1.JSONSchemaProps, string) {
buff := &bytes.Buffer{}
props := v1beta1.JSONSchemaProps{
Expand All @@ -290,6 +303,8 @@ func (b *APIs) parseObjectValidation(t *types.Type, found sets.String, comments
return props, buff.String()
}

// getValidation parses the validation tags from the comment and sets the
// validation rules on the given JSONSchemaProps.
func getValidation(comment string, props *v1beta1.JSONSchemaProps) {
comment = strings.TrimLeft(comment, " ")
if !strings.HasPrefix(comment, "+kubebuilder:validation:") {
Expand Down Expand Up @@ -392,6 +407,8 @@ func getValidation(comment string, props *v1beta1.JSONSchemaProps) {
}
}

// getMembers builds maps by field name of the JSONSchemaProps and their Go
// serializations.
func (b *APIs) getMembers(t *types.Type, found sets.String) (map[string]v1beta1.JSONSchemaProps, map[string]string) {
members := map[string]v1beta1.JSONSchemaProps{}
result := map[string]string{}
Expand Down

0 comments on commit 4987c4d

Please sign in to comment.