Skip to content

Commit

Permalink
Template interface (phase 2) including insert code fragments logic
Browse files Browse the repository at this point in the history
Signed-off-by: Adrian Orive <[email protected]>
  • Loading branch information
Adirio committed Feb 20, 2020
1 parent e80f0a1 commit 6f191bf
Show file tree
Hide file tree
Showing 24 changed files with 1,457 additions and 814 deletions.
84 changes: 84 additions & 0 deletions pkg/model/file/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package file

import (
"sigs.k8s.io/kubebuilder/pkg/model/resource"
)

// Builder defines the basic methods that any file builder must implement
type Builder interface {
// GetPath returns the path to the file location
GetPath() string
// GetIfExistsAction returns the behavior when creating a file that already exists
GetIfExistsAction() IfExistsAction
}

// RequiresValidation is a file builder that requires validation
type RequiresValidation interface {
Builder
// Validate returns true if the template has valid values
Validate() error
}

// Template is file builder based on a file template
type Template interface {
Builder
// GetBody returns the template body
GetBody() string
// SetTemplateDefaults returns the TemplateMixin for creating a scaffold file
SetTemplateDefaults() error
}

// Inserter is a file builder that inserts code fragments in marked positions
type Inserter interface {
Builder
// GetMarkers returns the different markers where code fragments will be inserted
GetMarkers() []Marker
// GetCodeFragments returns a map that binds markers to code fragments
GetCodeFragments() CodeFragmentsMap
}

// HasDomain allows the domain to be used on a template
type HasDomain interface {
// InjectDomain sets the template domain
InjectDomain(string)
}

// HasRepository allows the repository to be used on a template
type HasRepository interface {
// InjectRepository sets the template repository
InjectRepository(string)
}

// HasMultiGroup allows the multi-group flag to be used on a template
type HasMultiGroup interface {
// InjectMultiGroup sets the template multi-group flag
InjectMultiGroup(bool)
}

// HasBoilerplate allows a boilerplate to be used on a template
type HasBoilerplate interface {
// InjectBoilerplate sets the template boilerplate
InjectBoilerplate(string)
}

// HasResource allows a resource to be used on a template
type HasResource interface {
// InjectResource sets the template resource
InjectResource(*resource.Resource)
}
56 changes: 56 additions & 0 deletions pkg/model/file/marker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package file

import (
"fmt"
"path/filepath"
)

const prefix = "+kubebuilder:scaffold:"

var commentsByExt = map[string]string{
// TODO(v3): machine-readable comments should not have spaces by Go convention. However,
// this is a backwards incompatible change, and thus should be done for next project version.
".go": "// ",
".yaml": "# ",
}

// Marker represents a comment LoC that will be used to insert code fragments by update operations
type Marker struct {
comment string
value string
}

// NewMarkerFor creates a new marker customized for the specific file
func NewMarkerFor(path string, value string) Marker {
ext := filepath.Ext(path)
if comment, found := commentsByExt[ext]; found {
return Marker{comment, value}
}

panic(fmt.Errorf("unknown file extension: '%s', expected '.go' or '.yaml'", ext))
}

// String implements Stringer
func (m Marker) String() string {
return m.comment + prefix + m.value
}

type CodeFragments []string

type CodeFragmentsMap map[Marker]CodeFragments
85 changes: 29 additions & 56 deletions pkg/model/file/template.go → pkg/model/file/mixins.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,46 +20,50 @@ import (
"sigs.k8s.io/kubebuilder/pkg/model/resource"
)

// Template is a scaffoldable file template
type Template interface {
// GetPath returns the path to the file location
GetPath() string
// GetBody returns the template body
GetBody() string
// GetIfExistsAction returns the behavior when creating a file that already exists
GetIfExistsAction() IfExistsAction
// SetTemplateDefaults returns the TemplateMixin for creating a scaffold file
SetTemplateDefaults() error
}

// TemplateMixin is the input for scaffolding a file
type TemplateMixin struct {
// Path is the file to write
// PathMixin provides file builders with a path field
type PathMixin struct {
// Path is the of the file
Path string
}

// TemplateBody is the template body to execute
TemplateBody string
// GetPath implements Builder
func (t *PathMixin) GetPath() string {
return t.Path
}

// IfExistsActionMixin provides file builders with a if-exists-action field
type IfExistsActionMixin struct {
// IfExistsAction determines what to do if the file exists
IfExistsAction IfExistsAction
}

func (t *TemplateMixin) GetPath() string {
return t.Path
// GetIfExistsAction implements Builder
func (t *IfExistsActionMixin) GetIfExistsAction() IfExistsAction {
return t.IfExistsAction
}

// TemplateMixin is the mixin that should be embedded in Template builders
type TemplateMixin struct {
PathMixin
IfExistsActionMixin

// TemplateBody is the template body to execute
TemplateBody string
}

func (t *TemplateMixin) GetBody() string {
return t.TemplateBody
}

func (t *TemplateMixin) GetIfExistsAction() IfExistsAction {
return t.IfExistsAction
// InserterMixin is the mixin that should be embedded in Inserter builders
type InserterMixin struct {
PathMixin
}

// HasDomain allows the domain to be used on a template
type HasDomain interface {
// InjectDomain sets the template domain
InjectDomain(string)
// GetIfExistsAction implements Builder
func (t *InserterMixin) GetIfExistsAction() IfExistsAction {
// Inserter builders always need to overwrite previous files
return Overwrite
}

// DomainMixin provides templates with a injectable domain field
Expand All @@ -75,12 +79,6 @@ func (m *DomainMixin) InjectDomain(domain string) {
}
}

// HasRepository allows the repository to be used on a template
type HasRepository interface {
// InjectRepository sets the template repository
InjectRepository(string)
}

// RepositoryMixin provides templates with a injectable repository field
type RepositoryMixin struct {
// Repo is the go project package path
Expand All @@ -94,12 +92,6 @@ func (m *RepositoryMixin) InjectRepository(repository string) {
}
}

// HasMultiGroup allows the multi-group flag to be used on a template
type HasMultiGroup interface {
// InjectMultiGroup sets the template multi-group flag
InjectMultiGroup(bool)
}

// MultiGroupMixin provides templates with a injectable multi-group flag field
type MultiGroupMixin struct {
// MultiGroup is the multi-group flag
Expand All @@ -111,12 +103,6 @@ func (m *MultiGroupMixin) InjectMultiGroup(flag bool) {
m.MultiGroup = flag
}

// HasBoilerplate allows a boilerplate to be used on a template
type HasBoilerplate interface {
// InjectBoilerplate sets the template boilerplate
InjectBoilerplate(string)
}

// BoilerplateMixin provides templates with a injectable boilerplate field
type BoilerplateMixin struct {
// Boilerplate is the contents of a Boilerplate go header file
Expand All @@ -130,12 +116,6 @@ func (m *BoilerplateMixin) InjectBoilerplate(boilerplate string) {
}
}

// HasResource allows a resource to be used on a template
type HasResource interface {
// InjectResource sets the template resource
InjectResource(*resource.Resource)
}

// ResourceMixin provides templates with a injectable resource field
type ResourceMixin struct {
Resource *resource.Resource
Expand All @@ -147,10 +127,3 @@ func (m *ResourceMixin) InjectResource(res *resource.Resource) {
m.Resource = res
}
}

// RequiresValidation is a file that requires validation
type RequiresValidation interface {
Template
// Validate returns true if the template has valid values
Validate() error
}
4 changes: 2 additions & 2 deletions pkg/model/universe.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type Universe struct {
Resource *resource.Resource `json:"resource,omitempty"`

// Files contains the model of the files that are being scaffolded
Files []*file.File `json:"files,omitempty"`
Files map[string]*file.File `json:"files,omitempty"`
}

// NewUniverse creates a new Universe
Expand Down Expand Up @@ -78,7 +78,7 @@ func WithResource(resource *resource.Resource) UniverseOption {
}
}

func (u Universe) InjectInto(t file.Template) {
func (u Universe) InjectInto(t file.Builder) {
// Inject project configuration
if u.Config != nil {
if templateWithDomain, hasDomain := t.(file.HasDomain); hasDomain {
Expand Down
24 changes: 5 additions & 19 deletions pkg/scaffold/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,19 +162,14 @@ func (s *apiScaffolder) scaffoldV2() error {
return fmt.Errorf("error scaffolding APIs: %v", err)
}

kustomizationFile := &crdv2.Kustomization{}
if err := machinery.NewScaffold().Execute(
s.newUniverse(),
kustomizationFile,
&crdv2.Kustomization{},
&crdv2.KustomizeConfig{},
); err != nil {
return fmt.Errorf("error scaffolding kustomization: %v", err)
}

if err := kustomizationFile.Update(); err != nil {
return fmt.Errorf("error updating kustomization.yaml: %v", err)
}

} else {
// disable generation of example reconcile body if not scaffolding resource
// because this could result in a fork-bomb of k8s resources where watching a
Expand All @@ -192,27 +187,18 @@ func (s *apiScaffolder) scaffoldV2() error {
fmt.Sprintf("%s_controller.go", strings.ToLower(s.resource.Kind))))
}

suiteTestFile := &controllerv2.SuiteTest{}
if err := machinery.NewScaffold(s.plugins...).Execute(
s.newUniverse(),
suiteTestFile,
&controllerv2.SuiteTest{},
&controllerv2.Controller{},
); err != nil {
return fmt.Errorf("error scaffolding controller: %v", err)
}

if err := suiteTestFile.Update(); err != nil {
return fmt.Errorf("error updating suite_test.go under controllers pkg: %v", err)
}
}

if err := (&templatesv2.Main{}).Update(
&templatesv2.MainUpdateOptions{
Config: &s.config.Config,
WireResource: s.doResource,
WireController: s.doController,
Resource: s.resource,
},
if err := machinery.NewScaffold(s.plugins...).Execute(
s.newUniverse(),
&templatesv2.MainUpdater{WireResource: s.doResource, WireController: s.doController},
); err != nil {
return fmt.Errorf("error updating main.go: %v", err)
}
Expand Down
21 changes: 10 additions & 11 deletions pkg/scaffold/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (

"sigs.k8s.io/kubebuilder/internal/config"
"sigs.k8s.io/kubebuilder/pkg/model"
"sigs.k8s.io/kubebuilder/pkg/model/file"
"sigs.k8s.io/kubebuilder/pkg/scaffold/internal/machinery"
templatesv1 "sigs.k8s.io/kubebuilder/pkg/scaffold/internal/templates/v1"
managerv1 "sigs.k8s.io/kubebuilder/pkg/scaffold/internal/templates/v1/manager"
Expand Down Expand Up @@ -86,13 +85,13 @@ func (s *initScaffolder) Scaffold() error {
}

func (s *initScaffolder) scaffoldV1() error {
bpFile := &templatesv1.Boilerplate{}
bpFile.Path = s.boilerplatePath
bpFile.License = s.license
bpFile.Owner = s.owner
if err := machinery.NewScaffold().Execute(
s.newUniverse(""),
&templatesv1.Boilerplate{
TemplateMixin: file.TemplateMixin{Path: s.boilerplatePath},
License: s.license,
Owner: s.owner,
},
bpFile,
); err != nil {
return err
}
Expand Down Expand Up @@ -126,13 +125,13 @@ func (s *initScaffolder) scaffoldV1() error {
}

func (s *initScaffolder) scaffoldV2() error {
bpFile := &templatesv2.Boilerplate{}
bpFile.Path = s.boilerplatePath
bpFile.License = s.license
bpFile.Owner = s.owner
if err := machinery.NewScaffold().Execute(
s.newUniverse(""),
&templatesv2.Boilerplate{
TemplateMixin: file.TemplateMixin{Path: s.boilerplatePath},
License: s.license,
Owner: s.owner,
},
bpFile,
); err != nil {
return err
}
Expand Down
Loading

0 comments on commit 6f191bf

Please sign in to comment.