Skip to content

Commit

Permalink
Move scaffold machinery to its own internal package
Browse files Browse the repository at this point in the history
Signed-off-by: Adrian Orive <adrian.orive.oneca@gmail.com>
  • Loading branch information
Adirio committed Feb 3, 2020
1 parent c6e1ee1 commit f96d453
Show file tree
Hide file tree
Showing 14 changed files with 1,421 additions and 140 deletions.
3 changes: 2 additions & 1 deletion cmd/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (

"sigs.k8s.io/kubebuilder/cmd/internal"
"sigs.k8s.io/kubebuilder/internal/config"
"sigs.k8s.io/kubebuilder/pkg/model"
"sigs.k8s.io/kubebuilder/pkg/model/resource"
"sigs.k8s.io/kubebuilder/pkg/scaffold"
"sigs.k8s.io/kubebuilder/plugins/addon"
Expand Down Expand Up @@ -222,7 +223,7 @@ func (o *apiOptions) scaffolder(c *config.Config) (scaffold.Scaffolder, error) {
}

// Load the requested plugins
plugins := make([]scaffold.Plugin, 0)
plugins := make([]model.Plugin, 0)
switch strings.ToLower(o.pattern) {
case "":
// Default pattern
Expand Down
24 changes: 24 additions & 0 deletions pkg/model/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
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 model

// Plugin is the interface that a plugin must implement
// We will (later) have an ExecPlugin that implements this by exec-ing a binary
type Plugin interface {
// Pipe is the core plugin interface, that transforms a UniverseModel
Pipe(*Universe) error
}
15 changes: 8 additions & 7 deletions pkg/scaffold/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"sigs.k8s.io/kubebuilder/internal/config"
"sigs.k8s.io/kubebuilder/pkg/model"
"sigs.k8s.io/kubebuilder/pkg/model/resource"
"sigs.k8s.io/kubebuilder/pkg/scaffold/internal/machinery"
controllerv1 "sigs.k8s.io/kubebuilder/pkg/scaffold/v1/controller"
crdv1 "sigs.k8s.io/kubebuilder/pkg/scaffold/v1/crd"
scaffoldv2 "sigs.k8s.io/kubebuilder/pkg/scaffold/v2"
Expand All @@ -38,7 +39,7 @@ type apiScaffolder struct {
boilerplate string
resource *resource.Resource
// plugins is the list of plugins we should allow to transform our generated scaffolding
plugins []Plugin
plugins []model.Plugin
// doResource indicates whether to scaffold API Resource or not
doResource bool
// doController indicates whether to scaffold controller files or not
Expand All @@ -50,7 +51,7 @@ func NewAPIScaffolder(
boilerplate string,
res *resource.Resource,
doResource, doController bool,
plugins []Plugin,
plugins []model.Plugin,
) Scaffolder {
return &apiScaffolder{
config: config,
Expand Down Expand Up @@ -90,7 +91,7 @@ func (s *apiScaffolder) scaffoldV1() error {
fmt.Println(filepath.Join("pkg", "apis", s.resource.GroupPackageName, s.resource.Version,
fmt.Sprintf("%s_types_test.go", strings.ToLower(s.resource.Kind))))

if err := NewScaffold().Execute(
if err := machinery.NewScaffold().Execute(
s.newUniverse(),
&crdv1.Register{Resource: s.resource},
&crdv1.Types{Resource: s.resource},
Expand All @@ -117,7 +118,7 @@ func (s *apiScaffolder) scaffoldV1() error {
fmt.Println(filepath.Join("pkg", "controller", strings.ToLower(s.resource.Kind),
fmt.Sprintf("%s_controller_test.go", strings.ToLower(s.resource.Kind))))

if err := NewScaffold().Execute(
if err := machinery.NewScaffold().Execute(
s.newUniverse(),
&controllerv1.Controller{Resource: s.resource},
&controllerv1.AddController{Resource: s.resource},
Expand Down Expand Up @@ -148,7 +149,7 @@ func (s *apiScaffolder) scaffoldV2() error {
fmt.Sprintf("%s_types.go", strings.ToLower(s.resource.Kind))))
}

if err := NewScaffold(s.plugins...).Execute(
if err := machinery.NewScaffold(s.plugins...).Execute(
s.newUniverse(),
&scaffoldv2.Types{Resource: s.resource},
&scaffoldv2.Group{Resource: s.resource},
Expand All @@ -162,7 +163,7 @@ func (s *apiScaffolder) scaffoldV2() error {
}

kustomizationFile := &crdv2.Kustomization{Resource: s.resource}
if err := NewScaffold().Execute(
if err := machinery.NewScaffold().Execute(
s.newUniverse(),
kustomizationFile,
&crdv2.KustomizeConfig{},
Expand Down Expand Up @@ -192,7 +193,7 @@ func (s *apiScaffolder) scaffoldV2() error {
}

suiteTestFile := &controllerv2.SuiteTest{Resource: s.resource}
if err := NewScaffold(s.plugins...).Execute(
if err := machinery.NewScaffold(s.plugins...).Execute(
s.newUniverse(),
suiteTestFile,
&controllerv2.Controller{Resource: s.resource},
Expand Down
9 changes: 5 additions & 4 deletions pkg/scaffold/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ 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"
"sigs.k8s.io/kubebuilder/pkg/scaffold/project"
scaffoldv1 "sigs.k8s.io/kubebuilder/pkg/scaffold/v1"
managerv1 "sigs.k8s.io/kubebuilder/pkg/scaffold/v1/manager"
Expand Down Expand Up @@ -76,7 +77,7 @@ func (s *initScaffolder) Scaffold() error {
return err
}

if err := NewScaffold().Execute(
if err := machinery.NewScaffold().Execute(
s.newUniverse(), // Boilerplate is still empty by this call as desired
&project.Boilerplate{
Input: file.Input{Path: s.boilerplatePath},
Expand All @@ -93,7 +94,7 @@ func (s *initScaffolder) Scaffold() error {
}
s.boilerplate = string(boilerplateBytes)

if err := NewScaffold().Execute(
if err := machinery.NewScaffold().Execute(
s.newUniverse(),
&project.GitIgnore{},
&project.AuthProxyRole{},
Expand All @@ -113,7 +114,7 @@ func (s *initScaffolder) Scaffold() error {
}

func (s *initScaffolder) scaffoldV1() error {
return NewScaffold().Execute(
return machinery.NewScaffold().Execute(
s.newUniverse(),
&project.KustomizeRBAC{},
&scaffoldv1.KustomizeImagePatch{},
Expand All @@ -134,7 +135,7 @@ func (s *initScaffolder) scaffoldV1() error {
}

func (s *initScaffolder) scaffoldV2() error {
return NewScaffold().Execute(
return machinery.NewScaffold().Execute(
s.newUniverse(),
&metricsauthv2.AuthProxyPatch{},
&metricsauthv2.AuthProxyService{},
Expand Down
93 changes: 93 additions & 0 deletions pkg/scaffold/internal/filesystem/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
Copyright 2020 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 filesystem

import (
"fmt"
)

// This file contains the errors returned by the file system wrapper
// They are not exported as they should not be created outside of this package
// Exported functions are provided to check which kind of error was returned

// createDirectoryError is returned if the directory could not be created
type createDirectoryError struct {
path string
err error
}

func (e createDirectoryError) Error() string {
return fmt.Sprintf("failed to create directory for %s: %v", e.path, e.err)
}

// IsCreateDirectoryError checks if the returned error is because the directory
// could not be created
func IsCreateDirectoryError(e error) bool {
_, ok := e.(createDirectoryError)
return ok
}

// createFileError is returned if the file could not be created
type createFileError struct {
path string
err error
}

func (e createFileError) Error() string {
return fmt.Sprintf("failed to create %s: %v", e.path, e.err)
}

// IsCreateFileError checks if the returned error is because the file could not
// be created
func IsCreateFileError(e error) bool {
_, ok := e.(createFileError)
return ok
}

// writeFileError is returned if the filed could not be written to
type writeFileError struct {
path string
err error
}

func (e writeFileError) Error() string {
return fmt.Sprintf("failed to write to %s: %v", e.path, e.err)
}

// IsWriteFileError checks if the returned error is because the file could not
// be written to
func IsWriteFileError(e error) bool {
_, ok := e.(writeFileError)
return ok
}

// closeFileError is returned if the file could not be created
type closeFileError struct {
path string
err error
}

func (e closeFileError) Error() string {
return fmt.Sprintf("failed to close %s: %v", e.path, e.err)
}

// IsCloseFileError checks if the returned error is because the file could not
// be closed
func IsCloseFileError(e error) bool {
_, ok := e.(closeFileError)
return ok
}
131 changes: 131 additions & 0 deletions pkg/scaffold/internal/filesystem/filesystem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
Copyright 2020 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 filesystem

import (
"io"
"os"
"path/filepath"

"github.com/spf13/afero"
)

const (
createOrUpdate = os.O_WRONLY | os.O_CREATE | os.O_TRUNC

defaultDirectoryPermission os.FileMode = 0700
defaultFilePermission os.FileMode = 0600
)

// FileSystem is an IO wrapper to create files
type FileSystem interface {
// Exists checks if the file exists
Exists(path string) (bool, error)

// Create creates the directory and file and returns a self-closing
// io.Writer pointing to that file. If the file exists, it truncates it.
Create(path string) (io.Writer, error)
}

// fileSystem implements FileSystem
type fileSystem struct {
fs afero.Fs
dirPerm os.FileMode
filePerm os.FileMode
fileMode int
}

// New returns a new FileSystem
func New(options ...Options) FileSystem {
// Default values
fs := fileSystem{
fs: afero.NewOsFs(),
dirPerm: defaultDirectoryPermission,
filePerm: defaultFilePermission,
fileMode: createOrUpdate,
}

// Apply options
for _, option := range options {
option(&fs)
}

return fs
}

// Options configure FileSystem
type Options func(system *fileSystem)

// DirectoryPermissions makes FileSystem.Create use the provided directory
// permissions
func DirectoryPermissions(dirPerm os.FileMode) Options {
return func(fs *fileSystem) {
fs.dirPerm = dirPerm
}
}

// FilePermissions makes FileSystem.Create use the provided file permissions
func FilePermissions(filePerm os.FileMode) Options {
return func(fs *fileSystem) {
fs.filePerm = filePerm
}
}

// Exists implements FileSystem.Exists
func (fs fileSystem) Exists(path string) (bool, error) {
return afero.Exists(fs.fs, path)
}

// Create implements FileSystem.Create
func (fs fileSystem) Create(path string) (io.Writer, error) {
// Create the directory if needed
if err := fs.fs.MkdirAll(filepath.Dir(path), fs.dirPerm); err != nil {
return nil, createDirectoryError{path, err}
}

// Create or truncate the file
wc, err := fs.fs.OpenFile(path, fs.fileMode, fs.filePerm)
if err != nil {
return nil, createFileError{path, err}
}

return &file{path, wc}, nil
}

// file implements io.Writer
type file struct {
path string
io.WriteCloser
}

// Write implements io.Writer.Write
func (f *file) Write(content []byte) (n int, err error) {
// Close the file when we end writing
defer func() {
if closeErr := f.Close(); err == nil && closeErr != nil {
err = closeFileError{f.path, err}
}
}()

// Write the content
n, err = f.WriteCloser.Write(content)
if err != nil {
return n, writeFileError{f.path, err}
}

return n, nil
}
Loading

0 comments on commit f96d453

Please sign in to comment.