From 29e5e941c8e44dfdbde64d86da7c7c1ea784ae84 Mon Sep 17 00:00:00 2001 From: Kim Tsao Date: Wed, 6 Oct 2021 14:27:13 -0400 Subject: [PATCH 1/5] add new generator and helpers to return values for boolean pointers --- build.sh | 4 + generator/helpers/gen.go | 134 ++++++++++++++++++ generator/helpers/zz_generated.markerhelp.go | 22 +++ generator/main.go | 5 + generator/overrides/gen.go | 13 ++ pkg/apis/workspaces/v1alpha2/commands.go | 6 + .../v1alpha2/component_container.go | 3 + .../v1alpha2/component_image_dockerfile.go | 2 + .../workspaces/v1alpha2/component_volume.go | 2 + pkg/apis/workspaces/v1alpha2/endpoint.go | 6 +- .../v1alpha2/zz_generated.helpers.go | 61 ++++++++ 11 files changed, 255 insertions(+), 3 deletions(-) create mode 100644 generator/helpers/gen.go create mode 100644 generator/helpers/zz_generated.markerhelp.go create mode 100644 pkg/apis/workspaces/v1alpha2/zz_generated.helpers.go diff --git a/build.sh b/build.sh index da5f79dbd..38fce7787 100755 --- a/build.sh +++ b/build.sh @@ -61,4 +61,8 @@ echo "Generating JsonSchemas" generator/build/generator "schemas" "output:schemas:artifacts:config=schemas" "paths=./pkg/apis/workspaces/v1alpha2" +echo "Generating Helper Implementations" + +generator/build/generator "helpers" "paths=./pkg/apis/workspaces/v1alpha2" + echo "Finished generation of required GO sources, K8S CRDs, and Json Schemas" diff --git a/generator/helpers/gen.go b/generator/helpers/gen.go new file mode 100644 index 000000000..c8e4043f1 --- /dev/null +++ b/generator/helpers/gen.go @@ -0,0 +1,134 @@ +package helpers + +import ( + "bytes" + "fmt" + "github.com/devfile/api/generator/genutils" + "github.com/elliotchance/orderedmap" + "go/ast" + "sigs.k8s.io/controller-tools/pkg/genall" + "sigs.k8s.io/controller-tools/pkg/loader" + "sigs.k8s.io/controller-tools/pkg/markers" +) + +//go:generate go run sigs.k8s.io/controller-tools/cmd/helpgen generate:headerFile=../header.go.txt,year=2020 paths=. + +var ( + // HelperTypeMarker is associated with a type that's used as a parameter to the helper function + HelperTypeMarker = markers.Must(markers.MakeDefinition("devfile:helper:generate", markers.DescribesType, struct{}{})) + // DefaultFieldMarker is associated with a boolean pointer field to indicate what the default value should be + DefaultFieldMarker = markers.Must(markers.MakeDefinition("devfile:default:value", markers.DescribesField, "")) +) + +// +controllertools:marker:generateHelp + +// Generator generates helper functions that are used to return values for boolean pointer fields. +// +// The helper function takes as a parameter, the `devfile:default:generate` annotated type and returns the value of the +// field if it's been set, otherwise it will return the default value specified by the devfile:default:value annotation. +type Generator struct{} + +// RegisterMarkers registers the markers of the Generator +func (Generator) RegisterMarkers(into *markers.Registry) error { + if err := markers.RegisterAll(into, HelperTypeMarker, DefaultFieldMarker); err != nil { + return err + } + into.AddHelp(HelperTypeMarker, + markers.SimpleHelp("Devfile", "indicates the type that's passed in as a parameter to the generated helper functions")) + into.AddHelp(DefaultFieldMarker, + markers.SimpleHelp("Devfile", "indicates the default value of a boolean pointer field")) + return genutils.RegisterUnionMarkers(into) + +} + +func (Generator) CheckFilter() loader.NodeFilter { + return func(node ast.Node) bool { + // ignore interfaces + _, isIface := node.(*ast.InterfaceType) + return !isIface + } +} + +// helperInfo stores the info to generate the helper function +type helperInfo struct { + funcName string + defaultVal string + returnType string +} + +// Generate generates the artifacts +func (g Generator) Generate(ctx *genall.GenerationContext) error { + for _, root := range ctx.Roots { + ctx.Checker.Check(root) + root.NeedTypesInfo() + + typesToProcess := orderedmap.NewOrderedMap() + if err := markers.EachType(ctx.Collector, root, func(info *markers.TypeInfo) { + if info.Markers.Get(HelperTypeMarker.Name) != nil { + var helpers []helperInfo + for _, field := range info.Fields { + defaultVal := field.Markers.Get(DefaultFieldMarker.Name) + if defaultVal != nil { + //look for boolean pointers + if ptr, isPtr := field.RawField.Type.(*ast.StarExpr); isPtr { + if ident, ok := ptr.X.(*ast.Ident); ok { + if ident.Name == "bool" { + helpers = append(helpers, helperInfo{ + field.Name, + defaultVal.(string), + ident.Name, + }) + } else { + root.AddError(fmt.Errorf("devfile:default:value marker is specified on %s/%s which is not a boolean pointer", info.Name, field.Name)) + } + } + } else { + root.AddError(fmt.Errorf("devfile:default:value marker is specified on %s/%s which is not a boolean pointer", info.Name, field.Name)) + } + + } + } + if len(helpers) > 0 { + typesToProcess.Set(info, helpers) + } else { + root.AddError(fmt.Errorf("type %s does not have the field marker, devfile:default:value specified on a boolean pointer field", info.Name)) + } + return + } + + }); err != nil { + root.AddError(err) + return nil + } + + genutils.WriteFormattedSourceFile("helpers", ctx, root, func(buf *bytes.Buffer) { + for elt := typesToProcess.Front(); elt != nil; elt = elt.Next() { + param := elt.Key.(*markers.TypeInfo) + fields := elt.Value.([]helperInfo) + for _, helper := range fields { + fName := helper.funcName + returnType := helper.returnType + + helperFunction := ` +//` + fName + ` returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker` + ` +func ` + fName + `(in *` + param.Name + `) ` + returnType + ` { + if in.` + fName + ` !=nil{ + return *in.` + fName + `}` + + defaultVal := helper.defaultVal + if fName != "MountSources" { + buf.WriteString(helperFunction + ` + return ` + defaultVal + ` }`) + } else { + buf.WriteString(helperFunction + `else { + if DedicatedPod(in) { return false } + return ` + defaultVal + ` }}`) + } + } + } + }) + + } + + return nil +} diff --git a/generator/helpers/zz_generated.markerhelp.go b/generator/helpers/zz_generated.markerhelp.go new file mode 100644 index 000000000..8c70743bf --- /dev/null +++ b/generator/helpers/zz_generated.markerhelp.go @@ -0,0 +1,22 @@ +// +build !ignore_autogenerated + +// Generated for the devfile generator + +// Code generated by helpgen. DO NOT EDIT. + +package helpers + +import ( + "sigs.k8s.io/controller-tools/pkg/markers" +) + +func (Generator) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "", + DetailedHelp: markers.DetailedHelp{ + Summary: "generates helper functions that are used to return values for boolean pointer fields. ", + Details: "The helper function takes as a parameter, the `devfile:default:generate` annotated type and returns the value of the field if it's been set, otherwise it will return the default value specified by the devfile:default:value annotation.", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} diff --git a/generator/main.go b/generator/main.go index d4c2a77d3..d8fa6ee93 100644 --- a/generator/main.go +++ b/generator/main.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "fmt" + "github.com/devfile/api/generator/helpers" "io" "os" "strings" @@ -34,6 +35,7 @@ var ( "deepcopy": deepcopy.Generator{}, "schemas": schemas.Generator{}, "validate": validate.Generator{}, + "helpers": helpers.Generator{}, } // allOutputRules defines the list of all known output rules, giving @@ -122,6 +124,9 @@ generator overrides:isForPluginOverrides=false paths=./pkg/apis/workspaces/v1alp # Generate Interface Implementations based on the workspaces/v1alpha2 K8S API generator interfaces paths=./pkg/apis/workspaces/v1alpha2 +# Generate Default value implementations based on the workspaces/v1alpha2 K8S API +generator helpers paths=./pkg/apis/workspaces/v1alpha2 + # Generate K8S CRDs based on the workspaces/v1alpha2 K8S API generator crds output:crds:artifacts:config=crds paths=./pkg/apis/workspaces/v1alpha2 diff --git a/generator/overrides/gen.go b/generator/overrides/gen.go index 422a55f7f..7b7a7bfcb 100644 --- a/generator/overrides/gen.go +++ b/generator/overrides/gen.go @@ -227,6 +227,12 @@ func (g Generator) createOverride(newTypeToProcess typeToProcess, packageTypes m ) } + overrideGenDecl.Doc = updateComments( + overrideGenDecl, overrideGenDecl.Doc, + `.*`, + ` *`+regexp.QuoteMeta("+devfile:helper:generate")+`.*`, + ) + overrideGenDecl.Doc = updateComments( overrideGenDecl, overrideGenDecl.Doc, `.*`, @@ -318,6 +324,13 @@ func (g Generator) createOverride(newTypeToProcess typeToProcess, packageTypes m ` *`+regexp.QuoteMeta("+kubebuilder:default")+` *=.*`, ) + //remove the +devfile:default:values for overrides + astField.Doc = updateComments( + astField, astField.Doc, + `.*`, + ` *`+regexp.QuoteMeta("+devfile:default:value")+` *=.*`, + ) + processFieldType := func(ident *ast.Ident) *typeToProcess { typeToOverride, existsInPackage := packageTypes[ident.Name] if !existsInPackage { diff --git a/pkg/apis/workspaces/v1alpha2/commands.go b/pkg/apis/workspaces/v1alpha2/commands.go index 6698a1d30..a334b59b1 100644 --- a/pkg/apis/workspaces/v1alpha2/commands.go +++ b/pkg/apis/workspaces/v1alpha2/commands.go @@ -29,12 +29,14 @@ const ( DeployCommandGroupKind CommandGroupKind = "deploy" ) +// +devfile:helper:generate type CommandGroup struct { // Kind of group the command is part of Kind CommandGroupKind `json:"kind"` // +optional // Identifies the default command for a given group kind + // +devfile:default:value=false IsDefault *bool `json:"isDefault,omitempty"` } @@ -107,6 +109,7 @@ type CommandUnion struct { Custom *CustomCommand `json:"custom,omitempty"` } +// +devfile:helper:generate type ExecCommand struct { LabeledCommand `json:",inline"` @@ -145,6 +148,7 @@ type ExecCommand struct { // If set to `true` the command won't be restarted and it is expected to handle file changes on its own. // // Default value is `false` + // +devfile:default:value=false HotReloadCapable *bool `json:"hotReloadCapable,omitempty"` } @@ -156,6 +160,7 @@ type ApplyCommand struct { Component string `json:"component"` } +// +devfile:default:generate type CompositeCommand struct { LabeledCommand `json:",inline"` @@ -164,6 +169,7 @@ type CompositeCommand struct { // Indicates if the sub-commands should be executed concurrently // +optional + // +devfile:default:value=false Parallel *bool `json:"parallel,omitempty"` } diff --git a/pkg/apis/workspaces/v1alpha2/component_container.go b/pkg/apis/workspaces/v1alpha2/component_container.go index f6168c356..1d374c25a 100644 --- a/pkg/apis/workspaces/v1alpha2/component_container.go +++ b/pkg/apis/workspaces/v1alpha2/component_container.go @@ -7,6 +7,7 @@ type ContainerComponent struct { Endpoints []Endpoint `json:"endpoints,omitempty" patchStrategy:"merge" patchMergeKey:"name"` } +// +devfile:helper:generate type Container struct { Image string `json:"image"` @@ -55,6 +56,7 @@ type Container struct { // // Defaults to true for all component types except plugins and components that set `dedicatedPod` to true. // +optional + // +devfile:default:value=true MountSources *bool `json:"mountSources,omitempty"` // Optional specification of the path in the container where @@ -69,6 +71,7 @@ type Container struct { // // Default value is `false` // +optional + // +devfile:default:value=false DedicatedPod *bool `json:"dedicatedPod,omitempty"` } diff --git a/pkg/apis/workspaces/v1alpha2/component_image_dockerfile.go b/pkg/apis/workspaces/v1alpha2/component_image_dockerfile.go index 4743825e2..05eb2d47a 100644 --- a/pkg/apis/workspaces/v1alpha2/component_image_dockerfile.go +++ b/pkg/apis/workspaces/v1alpha2/component_image_dockerfile.go @@ -41,6 +41,7 @@ type DockerfileSrc struct { Git *DockerfileGitProjectSource `json:"git,omitempty"` } +// +devfile:helper:generate type Dockerfile struct { // Path of source directory to establish build context. Defaults to ${PROJECT_ROOT} in the container // +optional @@ -54,6 +55,7 @@ type Dockerfile struct { // // Default value is `false` // +optional + // +devfile:default:value=false RootRequired *bool `json:"rootRequired,omitempty"` } diff --git a/pkg/apis/workspaces/v1alpha2/component_volume.go b/pkg/apis/workspaces/v1alpha2/component_volume.go index b2517499d..6585617b0 100644 --- a/pkg/apis/workspaces/v1alpha2/component_volume.go +++ b/pkg/apis/workspaces/v1alpha2/component_volume.go @@ -7,6 +7,7 @@ type VolumeComponent struct { } // Volume that should be mounted to a component container +// +devfile:helper:generate type Volume struct { // +optional // Size of the volume @@ -15,5 +16,6 @@ type Volume struct { // +optional // Ephemeral volumes are not stored persistently across restarts. Defaults // to false + // +devfile:default:value=false Ephemeral *bool `json:"ephemeral,omitempty"` } diff --git a/pkg/apis/workspaces/v1alpha2/endpoint.go b/pkg/apis/workspaces/v1alpha2/endpoint.go index 8599a6e4f..e38185d47 100644 --- a/pkg/apis/workspaces/v1alpha2/endpoint.go +++ b/pkg/apis/workspaces/v1alpha2/endpoint.go @@ -1,8 +1,6 @@ package v1alpha2 -import ( - attributes "github.com/devfile/api/v2/pkg/attributes" -) +import "github.com/devfile/api/v2/pkg/attributes" // EndpointProtocol defines the application and transport protocols of the traffic that will go through this endpoint. // Only one of the following protocols may be specified: http, ws, tcp, udp. @@ -46,6 +44,7 @@ const ( NoneEndpointExposure EndpointExposure = "none" ) +// +devfile:helper:generate type Endpoint struct { // +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ // +kubebuilder:validation:MaxLength=63 @@ -94,6 +93,7 @@ type Endpoint struct { // Describes whether the endpoint should be secured and protected by some // authentication process. This requires a protocol of `https` or `wss`. // +optional + // +devfile:default:value=false Secure *bool `json:"secure,omitempty"` // Path of the endpoint URL diff --git a/pkg/apis/workspaces/v1alpha2/zz_generated.helpers.go b/pkg/apis/workspaces/v1alpha2/zz_generated.helpers.go new file mode 100644 index 000000000..86dde0f82 --- /dev/null +++ b/pkg/apis/workspaces/v1alpha2/zz_generated.helpers.go @@ -0,0 +1,61 @@ +package v1alpha2 + +//IsDefault returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func IsDefault(in *CommandGroup) bool { + if in.IsDefault != nil { + return *in.IsDefault + } + return false +} + +//HotReloadCapable returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func HotReloadCapable(in *ExecCommand) bool { + if in.HotReloadCapable != nil { + return *in.HotReloadCapable + } + return false +} + +//MountSources returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func MountSources(in *Container) bool { + if in.MountSources != nil { + return *in.MountSources + } else { + if DedicatedPod(in) { + return false + } + return true + } +} + +//DedicatedPod returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func DedicatedPod(in *Container) bool { + if in.DedicatedPod != nil { + return *in.DedicatedPod + } + return false +} + +//RootRequired returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func RootRequired(in *Dockerfile) bool { + if in.RootRequired != nil { + return *in.RootRequired + } + return false +} + +//Ephemeral returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func Ephemeral(in *Volume) bool { + if in.Ephemeral != nil { + return *in.Ephemeral + } + return false +} + +//Secure returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func Secure(in *Endpoint) bool { + if in.Secure != nil { + return *in.Secure + } + return false +} From bff902a39dc244d8ecdd92636929be637be3fe71 Mon Sep 17 00:00:00 2001 From: Kim Tsao Date: Wed, 6 Oct 2021 14:44:52 -0400 Subject: [PATCH 2/5] update type marker --- pkg/apis/workspaces/v1alpha2/commands.go | 2 +- pkg/apis/workspaces/v1alpha2/zz_generated.helpers.go | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/apis/workspaces/v1alpha2/commands.go b/pkg/apis/workspaces/v1alpha2/commands.go index a334b59b1..8d5e3e7ae 100644 --- a/pkg/apis/workspaces/v1alpha2/commands.go +++ b/pkg/apis/workspaces/v1alpha2/commands.go @@ -160,7 +160,7 @@ type ApplyCommand struct { Component string `json:"component"` } -// +devfile:default:generate +// +devfile:helper:generate type CompositeCommand struct { LabeledCommand `json:",inline"` diff --git a/pkg/apis/workspaces/v1alpha2/zz_generated.helpers.go b/pkg/apis/workspaces/v1alpha2/zz_generated.helpers.go index 86dde0f82..c36ed9713 100644 --- a/pkg/apis/workspaces/v1alpha2/zz_generated.helpers.go +++ b/pkg/apis/workspaces/v1alpha2/zz_generated.helpers.go @@ -16,6 +16,14 @@ func HotReloadCapable(in *ExecCommand) bool { return false } +//Parallel returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func Parallel(in *CompositeCommand) bool { + if in.Parallel != nil { + return *in.Parallel + } + return false +} + //MountSources returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker func MountSources(in *Container) bool { if in.MountSources != nil { From 7fa72f83a7919f7b1108fb0ac4aeb7c17ac7fc11 Mon Sep 17 00:00:00 2001 From: Kim Tsao Date: Thu, 7 Oct 2021 11:43:05 -0400 Subject: [PATCH 3/5] updated type marker name in marker help --- generator/helpers/gen.go | 2 +- generator/helpers/zz_generated.markerhelp.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/generator/helpers/gen.go b/generator/helpers/gen.go index c8e4043f1..6add99e9e 100644 --- a/generator/helpers/gen.go +++ b/generator/helpers/gen.go @@ -24,7 +24,7 @@ var ( // Generator generates helper functions that are used to return values for boolean pointer fields. // -// The helper function takes as a parameter, the `devfile:default:generate` annotated type and returns the value of the +// The helper function takes as a parameter, the `devfile:helper:generate` annotated type and returns the value of the // field if it's been set, otherwise it will return the default value specified by the devfile:default:value annotation. type Generator struct{} diff --git a/generator/helpers/zz_generated.markerhelp.go b/generator/helpers/zz_generated.markerhelp.go index 8c70743bf..16ec41a61 100644 --- a/generator/helpers/zz_generated.markerhelp.go +++ b/generator/helpers/zz_generated.markerhelp.go @@ -15,7 +15,7 @@ func (Generator) Help() *markers.DefinitionHelp { Category: "", DetailedHelp: markers.DetailedHelp{ Summary: "generates helper functions that are used to return values for boolean pointer fields. ", - Details: "The helper function takes as a parameter, the `devfile:default:generate` annotated type and returns the value of the field if it's been set, otherwise it will return the default value specified by the devfile:default:value annotation.", + Details: "The helper function takes as a parameter, the `devfile:helper:generate` annotated type and returns the value of the field if it's been set, otherwise it will return the default value specified by the devfile:default:value annotation.", }, FieldHelp: map[string]markers.DetailedHelp{}, } From 77db450b4801c65fcba5a54d90467af8fa3bfb49 Mon Sep 17 00:00:00 2001 From: Kim Tsao Date: Wed, 13 Oct 2021 14:31:59 -0400 Subject: [PATCH 4/5] update to use getter methods based on PR review --- build.sh | 4 +- generator/{helpers => getters}/gen.go | 83 +++++++++---------- generator/getters/zz_generated.markerhelp.go | 22 +++++ generator/helpers/zz_generated.markerhelp.go | 22 ----- generator/main.go | 8 +- generator/overrides/gen.go | 2 +- pkg/apis/workspaces/v1alpha2/commands.go | 6 +- .../v1alpha2/component_container.go | 15 +++- .../v1alpha2/component_image_dockerfile.go | 2 +- .../workspaces/v1alpha2/component_volume.go | 2 +- pkg/apis/workspaces/v1alpha2/endpoint.go | 2 +- .../v1alpha2/zz_generated.getters.go | 42 ++++++++++ .../v1alpha2/zz_generated.helpers.go | 69 --------------- 13 files changed, 131 insertions(+), 148 deletions(-) rename generator/{helpers => getters}/gen.go (56%) create mode 100644 generator/getters/zz_generated.markerhelp.go delete mode 100644 generator/helpers/zz_generated.markerhelp.go create mode 100644 pkg/apis/workspaces/v1alpha2/zz_generated.getters.go delete mode 100644 pkg/apis/workspaces/v1alpha2/zz_generated.helpers.go diff --git a/build.sh b/build.sh index 38fce7787..e50006f21 100755 --- a/build.sh +++ b/build.sh @@ -61,8 +61,8 @@ echo "Generating JsonSchemas" generator/build/generator "schemas" "output:schemas:artifacts:config=schemas" "paths=./pkg/apis/workspaces/v1alpha2" -echo "Generating Helper Implementations" +echo "Generating Getter Implementations" -generator/build/generator "helpers" "paths=./pkg/apis/workspaces/v1alpha2" +generator/build/generator "getters" "paths=./pkg/apis/workspaces/v1alpha2" echo "Finished generation of required GO sources, K8S CRDs, and Json Schemas" diff --git a/generator/helpers/gen.go b/generator/getters/gen.go similarity index 56% rename from generator/helpers/gen.go rename to generator/getters/gen.go index 6add99e9e..9d13fb32d 100644 --- a/generator/helpers/gen.go +++ b/generator/getters/gen.go @@ -1,4 +1,4 @@ -package helpers +package getters import ( "bytes" @@ -9,32 +9,33 @@ import ( "sigs.k8s.io/controller-tools/pkg/genall" "sigs.k8s.io/controller-tools/pkg/loader" "sigs.k8s.io/controller-tools/pkg/markers" + "strconv" ) -//go:generate go run sigs.k8s.io/controller-tools/cmd/helpgen generate:headerFile=../header.go.txt,year=2020 paths=. +//go:generate go run sigs.k8s.io/controller-tools/cmd/helpgen generate:headerFile=../header.go.txt,year=2021 paths=. var ( - // HelperTypeMarker is associated with a type that's used as a parameter to the helper function - HelperTypeMarker = markers.Must(markers.MakeDefinition("devfile:helper:generate", markers.DescribesType, struct{}{})) - // DefaultFieldMarker is associated with a boolean pointer field to indicate what the default value should be + // GetterTypeMarker is associated with a type that's used as the pointer receiver of the getter method + GetterTypeMarker = markers.Must(markers.MakeDefinition("devfile:getter:generate", markers.DescribesType, struct{}{})) + // DefaultFieldMarker is associated with a boolean pointer field to indicate the default boolean value DefaultFieldMarker = markers.Must(markers.MakeDefinition("devfile:default:value", markers.DescribesField, "")) ) // +controllertools:marker:generateHelp -// Generator generates helper functions that are used to return values for boolean pointer fields. +// Generator generates getter methods that are used to return values for the boolean pointer fields. // -// The helper function takes as a parameter, the `devfile:helper:generate` annotated type and returns the value of the +// The pointer receiver is determined from the `devfile:getter:generate` annotated type. The method will return the value of the // field if it's been set, otherwise it will return the default value specified by the devfile:default:value annotation. type Generator struct{} // RegisterMarkers registers the markers of the Generator func (Generator) RegisterMarkers(into *markers.Registry) error { - if err := markers.RegisterAll(into, HelperTypeMarker, DefaultFieldMarker); err != nil { + if err := markers.RegisterAll(into, GetterTypeMarker, DefaultFieldMarker); err != nil { return err } - into.AddHelp(HelperTypeMarker, - markers.SimpleHelp("Devfile", "indicates the type that's passed in as a parameter to the generated helper functions")) + into.AddHelp(GetterTypeMarker, + markers.SimpleHelp("Devfile", "indicates the type that's used as the pointer receiver of the getter method")) into.AddHelp(DefaultFieldMarker, markers.SimpleHelp("Devfile", "indicates the default value of a boolean pointer field")) return genutils.RegisterUnionMarkers(into) @@ -49,11 +50,10 @@ func (Generator) CheckFilter() loader.NodeFilter { } } -// helperInfo stores the info to generate the helper function -type helperInfo struct { +// getterInfo stores the info to generate the getter method +type getterInfo struct { funcName string defaultVal string - returnType string } // Generate generates the artifacts @@ -64,19 +64,22 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error { typesToProcess := orderedmap.NewOrderedMap() if err := markers.EachType(ctx.Collector, root, func(info *markers.TypeInfo) { - if info.Markers.Get(HelperTypeMarker.Name) != nil { - var helpers []helperInfo + if info.Markers.Get(GetterTypeMarker.Name) != nil { + var getters []getterInfo for _, field := range info.Fields { defaultVal := field.Markers.Get(DefaultFieldMarker.Name) if defaultVal != nil { + if _, err := strconv.ParseBool(defaultVal.(string)); err != nil { + root.AddError(fmt.Errorf("devfile:default:value marker specified on %s/%s does not have a true or false value. Value is %s", info.Name, field.Name, defaultVal.(string))) + } + //look for boolean pointers if ptr, isPtr := field.RawField.Type.(*ast.StarExpr); isPtr { if ident, ok := ptr.X.(*ast.Ident); ok { if ident.Name == "bool" { - helpers = append(helpers, helperInfo{ + getters = append(getters, getterInfo{ field.Name, defaultVal.(string), - ident.Name, }) } else { root.AddError(fmt.Errorf("devfile:default:value marker is specified on %s/%s which is not a boolean pointer", info.Name, field.Name)) @@ -88,8 +91,8 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error { } } - if len(helpers) > 0 { - typesToProcess.Set(info, helpers) + if len(getters) > 0 { + typesToProcess.Set(info, getters) } else { root.AddError(fmt.Errorf("type %s does not have the field marker, devfile:default:value specified on a boolean pointer field", info.Name)) } @@ -101,33 +104,29 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error { return nil } - genutils.WriteFormattedSourceFile("helpers", ctx, root, func(buf *bytes.Buffer) { + genutils.WriteFormattedSourceFile("getters", ctx, root, func(buf *bytes.Buffer) { for elt := typesToProcess.Front(); elt != nil; elt = elt.Next() { - param := elt.Key.(*markers.TypeInfo) - fields := elt.Value.([]helperInfo) - for _, helper := range fields { - fName := helper.funcName - returnType := helper.returnType - - helperFunction := ` -//` + fName + ` returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker` + ` -func ` + fName + `(in *` + param.Name + `) ` + returnType + ` { - if in.` + fName + ` !=nil{ - return *in.` + fName + `}` - - defaultVal := helper.defaultVal - if fName != "MountSources" { - buf.WriteString(helperFunction + ` - return ` + defaultVal + ` }`) - } else { - buf.WriteString(helperFunction + `else { - if DedicatedPod(in) { return false } - return ` + defaultVal + ` }}`) - } + cmd := elt.Key.(*markers.TypeInfo) + fields := elt.Value.([]getterInfo) + for _, getter := range fields { + fName := getter.funcName + defaultVal := getter.defaultVal + getterMethod := ` +// Get` + fName + ` returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker` + ` +func (in *` + cmd.Name + `) Get` + fName + `() bool { + return getBoolOrDefault(in.` + fName + `,` + defaultVal + `)}` + buf.WriteString(getterMethod) } } - }) + internalHelper := ` +func getBoolOrDefault(input *bool, defaultVal bool) bool { + if input != nil { + return *input + } + return defaultVal }` + buf.WriteString(internalHelper) + }) } return nil diff --git a/generator/getters/zz_generated.markerhelp.go b/generator/getters/zz_generated.markerhelp.go new file mode 100644 index 000000000..98f022020 --- /dev/null +++ b/generator/getters/zz_generated.markerhelp.go @@ -0,0 +1,22 @@ +// +build !ignore_autogenerated + +// Generated for the devfile generator + +// Code generated by helpgen. DO NOT EDIT. + +package getters + +import ( + "sigs.k8s.io/controller-tools/pkg/markers" +) + +func (Generator) Help() *markers.DefinitionHelp { + return &markers.DefinitionHelp{ + Category: "", + DetailedHelp: markers.DetailedHelp{ + Summary: "generates getter methods that are used to return values for the boolean pointer fields. ", + Details: "The pointer receiver is determined from the `devfile:getter:generate` annotated type. The method will return the value of the field if it's been set, otherwise it will return the default value specified by the devfile:default:value annotation.", + }, + FieldHelp: map[string]markers.DetailedHelp{}, + } +} diff --git a/generator/helpers/zz_generated.markerhelp.go b/generator/helpers/zz_generated.markerhelp.go deleted file mode 100644 index 16ec41a61..000000000 --- a/generator/helpers/zz_generated.markerhelp.go +++ /dev/null @@ -1,22 +0,0 @@ -// +build !ignore_autogenerated - -// Generated for the devfile generator - -// Code generated by helpgen. DO NOT EDIT. - -package helpers - -import ( - "sigs.k8s.io/controller-tools/pkg/markers" -) - -func (Generator) Help() *markers.DefinitionHelp { - return &markers.DefinitionHelp{ - Category: "", - DetailedHelp: markers.DetailedHelp{ - Summary: "generates helper functions that are used to return values for boolean pointer fields. ", - Details: "The helper function takes as a parameter, the `devfile:helper:generate` annotated type and returns the value of the field if it's been set, otherwise it will return the default value specified by the devfile:default:value annotation.", - }, - FieldHelp: map[string]markers.DetailedHelp{}, - } -} diff --git a/generator/main.go b/generator/main.go index d8fa6ee93..27d72a289 100644 --- a/generator/main.go +++ b/generator/main.go @@ -3,7 +3,7 @@ package main import ( "encoding/json" "fmt" - "github.com/devfile/api/generator/helpers" + "github.com/devfile/api/generator/getters" "io" "os" "strings" @@ -35,7 +35,7 @@ var ( "deepcopy": deepcopy.Generator{}, "schemas": schemas.Generator{}, "validate": validate.Generator{}, - "helpers": helpers.Generator{}, + "getters": getters.Generator{}, } // allOutputRules defines the list of all known output rules, giving @@ -124,8 +124,8 @@ generator overrides:isForPluginOverrides=false paths=./pkg/apis/workspaces/v1alp # Generate Interface Implementations based on the workspaces/v1alpha2 K8S API generator interfaces paths=./pkg/apis/workspaces/v1alpha2 -# Generate Default value implementations based on the workspaces/v1alpha2 K8S API -generator helpers paths=./pkg/apis/workspaces/v1alpha2 +# Generate Boolean Getter implementations based on the workspaces/v1alpha2 K8S API +generator getters paths=./pkg/apis/workspaces/v1alpha2 # Generate K8S CRDs based on the workspaces/v1alpha2 K8S API generator crds output:crds:artifacts:config=crds paths=./pkg/apis/workspaces/v1alpha2 diff --git a/generator/overrides/gen.go b/generator/overrides/gen.go index 7b7a7bfcb..283ee72ee 100644 --- a/generator/overrides/gen.go +++ b/generator/overrides/gen.go @@ -230,7 +230,7 @@ func (g Generator) createOverride(newTypeToProcess typeToProcess, packageTypes m overrideGenDecl.Doc = updateComments( overrideGenDecl, overrideGenDecl.Doc, `.*`, - ` *`+regexp.QuoteMeta("+devfile:helper:generate")+`.*`, + ` *`+regexp.QuoteMeta("+devfile:getter:generate")+`.*`, ) overrideGenDecl.Doc = updateComments( diff --git a/pkg/apis/workspaces/v1alpha2/commands.go b/pkg/apis/workspaces/v1alpha2/commands.go index 8d5e3e7ae..e99a2dfa9 100644 --- a/pkg/apis/workspaces/v1alpha2/commands.go +++ b/pkg/apis/workspaces/v1alpha2/commands.go @@ -29,7 +29,7 @@ const ( DeployCommandGroupKind CommandGroupKind = "deploy" ) -// +devfile:helper:generate +// +devfile:getter:generate type CommandGroup struct { // Kind of group the command is part of Kind CommandGroupKind `json:"kind"` @@ -109,7 +109,7 @@ type CommandUnion struct { Custom *CustomCommand `json:"custom,omitempty"` } -// +devfile:helper:generate +// +devfile:getter:generate type ExecCommand struct { LabeledCommand `json:",inline"` @@ -160,7 +160,7 @@ type ApplyCommand struct { Component string `json:"component"` } -// +devfile:helper:generate +// +devfile:getter:generate type CompositeCommand struct { LabeledCommand `json:",inline"` diff --git a/pkg/apis/workspaces/v1alpha2/component_container.go b/pkg/apis/workspaces/v1alpha2/component_container.go index 1d374c25a..08ded6bb2 100644 --- a/pkg/apis/workspaces/v1alpha2/component_container.go +++ b/pkg/apis/workspaces/v1alpha2/component_container.go @@ -7,7 +7,7 @@ type ContainerComponent struct { Endpoints []Endpoint `json:"endpoints,omitempty" patchStrategy:"merge" patchMergeKey:"name"` } -// +devfile:helper:generate +// +devfile:getter:generate type Container struct { Image string `json:"image"` @@ -56,7 +56,6 @@ type Container struct { // // Defaults to true for all component types except plugins and components that set `dedicatedPod` to true. // +optional - // +devfile:default:value=true MountSources *bool `json:"mountSources,omitempty"` // Optional specification of the path in the container where @@ -75,6 +74,18 @@ type Container struct { DedicatedPod *bool `json:"dedicatedPod,omitempty"` } +//GetMountSources returns the value of the boolean property. If it's unset, the default value is true for all component types except plugins and components that set `dedicatedPod` to true. +func (in *Container) GetMountSources() bool { + if in.MountSources != nil { + return *in.MountSources + } else { + if in.GetDedicatedPod() { + return false + } + return true + } +} + type EnvVar struct { Name string `json:"name" yaml:"name"` Value string `json:"value" yaml:"value"` diff --git a/pkg/apis/workspaces/v1alpha2/component_image_dockerfile.go b/pkg/apis/workspaces/v1alpha2/component_image_dockerfile.go index 05eb2d47a..a200ba137 100644 --- a/pkg/apis/workspaces/v1alpha2/component_image_dockerfile.go +++ b/pkg/apis/workspaces/v1alpha2/component_image_dockerfile.go @@ -41,7 +41,7 @@ type DockerfileSrc struct { Git *DockerfileGitProjectSource `json:"git,omitempty"` } -// +devfile:helper:generate +// +devfile:getter:generate type Dockerfile struct { // Path of source directory to establish build context. Defaults to ${PROJECT_ROOT} in the container // +optional diff --git a/pkg/apis/workspaces/v1alpha2/component_volume.go b/pkg/apis/workspaces/v1alpha2/component_volume.go index 6585617b0..c3bd69280 100644 --- a/pkg/apis/workspaces/v1alpha2/component_volume.go +++ b/pkg/apis/workspaces/v1alpha2/component_volume.go @@ -7,7 +7,7 @@ type VolumeComponent struct { } // Volume that should be mounted to a component container -// +devfile:helper:generate +// +devfile:getter:generate type Volume struct { // +optional // Size of the volume diff --git a/pkg/apis/workspaces/v1alpha2/endpoint.go b/pkg/apis/workspaces/v1alpha2/endpoint.go index e38185d47..3cf10d8ea 100644 --- a/pkg/apis/workspaces/v1alpha2/endpoint.go +++ b/pkg/apis/workspaces/v1alpha2/endpoint.go @@ -44,7 +44,7 @@ const ( NoneEndpointExposure EndpointExposure = "none" ) -// +devfile:helper:generate +// +devfile:getter:generate type Endpoint struct { // +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ // +kubebuilder:validation:MaxLength=63 diff --git a/pkg/apis/workspaces/v1alpha2/zz_generated.getters.go b/pkg/apis/workspaces/v1alpha2/zz_generated.getters.go new file mode 100644 index 000000000..952b408f0 --- /dev/null +++ b/pkg/apis/workspaces/v1alpha2/zz_generated.getters.go @@ -0,0 +1,42 @@ +package v1alpha2 + +// GetIsDefault returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func (in *CommandGroup) GetIsDefault() bool { + return getBoolOrDefault(in.IsDefault, false) +} + +// GetHotReloadCapable returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func (in *ExecCommand) GetHotReloadCapable() bool { + return getBoolOrDefault(in.HotReloadCapable, false) +} + +// GetParallel returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func (in *CompositeCommand) GetParallel() bool { + return getBoolOrDefault(in.Parallel, false) +} + +// GetDedicatedPod returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func (in *Container) GetDedicatedPod() bool { + return getBoolOrDefault(in.DedicatedPod, false) +} + +// GetRootRequired returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func (in *Dockerfile) GetRootRequired() bool { + return getBoolOrDefault(in.RootRequired, false) +} + +// GetEphemeral returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func (in *Volume) GetEphemeral() bool { + return getBoolOrDefault(in.Ephemeral, false) +} + +// GetSecure returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func (in *Endpoint) GetSecure() bool { + return getBoolOrDefault(in.Secure, false) +} +func getBoolOrDefault(input *bool, defaultVal bool) bool { + if input != nil { + return *input + } + return defaultVal +} diff --git a/pkg/apis/workspaces/v1alpha2/zz_generated.helpers.go b/pkg/apis/workspaces/v1alpha2/zz_generated.helpers.go deleted file mode 100644 index c36ed9713..000000000 --- a/pkg/apis/workspaces/v1alpha2/zz_generated.helpers.go +++ /dev/null @@ -1,69 +0,0 @@ -package v1alpha2 - -//IsDefault returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker -func IsDefault(in *CommandGroup) bool { - if in.IsDefault != nil { - return *in.IsDefault - } - return false -} - -//HotReloadCapable returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker -func HotReloadCapable(in *ExecCommand) bool { - if in.HotReloadCapable != nil { - return *in.HotReloadCapable - } - return false -} - -//Parallel returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker -func Parallel(in *CompositeCommand) bool { - if in.Parallel != nil { - return *in.Parallel - } - return false -} - -//MountSources returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker -func MountSources(in *Container) bool { - if in.MountSources != nil { - return *in.MountSources - } else { - if DedicatedPod(in) { - return false - } - return true - } -} - -//DedicatedPod returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker -func DedicatedPod(in *Container) bool { - if in.DedicatedPod != nil { - return *in.DedicatedPod - } - return false -} - -//RootRequired returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker -func RootRequired(in *Dockerfile) bool { - if in.RootRequired != nil { - return *in.RootRequired - } - return false -} - -//Ephemeral returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker -func Ephemeral(in *Volume) bool { - if in.Ephemeral != nil { - return *in.Ephemeral - } - return false -} - -//Secure returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker -func Secure(in *Endpoint) bool { - if in.Secure != nil { - return *in.Secure - } - return false -} From fe380d0fc0ae97fe0adf346330410e8f54ded1bb Mon Sep 17 00:00:00 2001 From: Kim Tsao Date: Thu, 14 Oct 2021 10:47:31 -0400 Subject: [PATCH 5/5] update formatting per review --- generator/getters/gen.go | 9 +++++---- pkg/apis/workspaces/v1alpha2/zz_generated.getters.go | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/generator/getters/gen.go b/generator/getters/gen.go index 9d13fb32d..2c70b5d5f 100644 --- a/generator/getters/gen.go +++ b/generator/getters/gen.go @@ -111,15 +111,16 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error { for _, getter := range fields { fName := getter.funcName defaultVal := getter.defaultVal - getterMethod := ` -// Get` + fName + ` returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker` + ` -func (in *` + cmd.Name + `) Get` + fName + `() bool { - return getBoolOrDefault(in.` + fName + `,` + defaultVal + `)}` + getterMethod := fmt.Sprintf(` +// Get%[1]s returns the value of the boolean property. If unset, it's the default value specified in the devfile:default:value marker +func (in *%[2]s) Get%[1]s() bool { +return getBoolOrDefault(in.%[1]s, %[3]s)}`, fName, cmd.Name, defaultVal) buf.WriteString(getterMethod) } } internalHelper := ` + func getBoolOrDefault(input *bool, defaultVal bool) bool { if input != nil { return *input diff --git a/pkg/apis/workspaces/v1alpha2/zz_generated.getters.go b/pkg/apis/workspaces/v1alpha2/zz_generated.getters.go index 952b408f0..4c7c36a30 100644 --- a/pkg/apis/workspaces/v1alpha2/zz_generated.getters.go +++ b/pkg/apis/workspaces/v1alpha2/zz_generated.getters.go @@ -34,6 +34,7 @@ func (in *Volume) GetEphemeral() bool { func (in *Endpoint) GetSecure() bool { return getBoolOrDefault(in.Secure, false) } + func getBoolOrDefault(input *bool, defaultVal bool) bool { if input != nil { return *input