-
Notifications
You must be signed in to change notification settings - Fork 1.6k
✨re-scaffold the project with a new command #3431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
Copyright 2023 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 alpha | ||
|
||
import ( | ||
"log" | ||
|
||
"github.com/spf13/cobra" | ||
"sigs.k8s.io/kubebuilder/v3/pkg/rescaffold" | ||
) | ||
|
||
// NewScaffoldCommand return a new scaffold command | ||
func NewScaffoldCommand() *cobra.Command { | ||
opts := rescaffold.MigrateOptions{} | ||
scaffoldCmd := &cobra.Command{ | ||
Use: "generate", | ||
Short: "Re-scaffold an existing Kuberbuilder project", | ||
Long: `It's an experimental feature that has the purpose of re-scaffolding the whole project from the scratch | ||
using the current version of KubeBuilder binary available. | ||
# make sure the PROJECT file is in the 'input-dir' argument, the default is the current directory. | ||
$ kubebuilder alpha generate --input-dir="./test" --output-dir="./my-output" | ||
Then we will re-scaffold the project by Kubebuilder in the directory specified by 'output-dir'. | ||
`, | ||
PreRunE: func(cmd *cobra.Command, _ []string) error { | ||
return opts.Validate() | ||
}, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
if err := opts.Rescaffold(); err != nil { | ||
log.Fatalf("Failed to rescaffold %s", err) | ||
} | ||
}, | ||
} | ||
scaffoldCmd.Flags().StringVar(&opts.InputDir, "input-dir", "", | ||
"path to a Kubebuilder project file if not in the current working directory") | ||
scaffoldCmd.Flags().StringVar(&opts.OutputDir, "output-dir", "", | ||
"path to output the scaffolding. defaults a directory in the current working directory") | ||
|
||
return scaffoldCmd | ||
} |
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,107 @@ | ||||||||
/* | ||||||||
Copyright 2023 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 rescaffold | ||||||||
|
||||||||
import ( | ||||||||
"fmt" | ||||||||
"log" | ||||||||
"os" | ||||||||
"os/exec" | ||||||||
|
||||||||
"github.com/spf13/afero" | ||||||||
"sigs.k8s.io/kubebuilder/v3/pkg/config/store" | ||||||||
"sigs.k8s.io/kubebuilder/v3/pkg/config/store/yaml" | ||||||||
"sigs.k8s.io/kubebuilder/v3/pkg/machinery" | ||||||||
"sigs.k8s.io/kubebuilder/v3/pkg/plugin/util" | ||||||||
) | ||||||||
|
||||||||
type MigrateOptions struct { | ||||||||
InputDir string | ||||||||
OutputDir string | ||||||||
} | ||||||||
|
||||||||
const DefaultOutputDir = "output-dir" | ||||||||
|
||||||||
func (opts *MigrateOptions) Rescaffold() error { | ||||||||
config := yaml.New(machinery.Filesystem{FS: afero.NewOsFs()}) | ||||||||
if err := config.LoadFrom(opts.InputDir); err != nil { | ||||||||
log.Fatal(err) | ||||||||
} | ||||||||
// create output directory | ||||||||
// nolint: gosec | ||||||||
if err := os.MkdirAll(opts.OutputDir, 0755); err != nil { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yyy1000 this lint issue we can ignore in this case
Suggested change
So that we will ignore this specific check in this specfic line then we can get this one merged There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added and squashed. |
||||||||
log.Fatal(err) | ||||||||
} | ||||||||
// use the new directory to set up the new project | ||||||||
if err := os.Chdir(opts.OutputDir); err != nil { | ||||||||
log.Fatal(err) | ||||||||
} | ||||||||
// init project with plugins | ||||||||
if err := kubebuilderInit(config); err != nil { | ||||||||
log.Fatal(err) | ||||||||
} | ||||||||
return nil | ||||||||
} | ||||||||
|
||||||||
func (opts *MigrateOptions) Validate() error { | ||||||||
cwd, err := os.Getwd() | ||||||||
if err != nil { | ||||||||
log.Fatal(err) | ||||||||
} | ||||||||
// get PROJECT path from command args | ||||||||
inputPath, err := getInputPath(cwd, opts.InputDir) | ||||||||
if err != nil { | ||||||||
log.Fatal(err) | ||||||||
} | ||||||||
opts.InputDir = inputPath | ||||||||
// get output path from command args | ||||||||
opts.OutputDir, err = getOutputPath(cwd, opts.OutputDir) | ||||||||
if err != nil { | ||||||||
log.Fatal(err) | ||||||||
} | ||||||||
// check whether the kubebuilder binary is accessible | ||||||||
_, err = exec.LookPath("kubebuilder") | ||||||||
return err | ||||||||
} | ||||||||
|
||||||||
func getInputPath(currentWorkingDirectory string, inputPath string) (string, error) { | ||||||||
if inputPath == "" { | ||||||||
inputPath = currentWorkingDirectory | ||||||||
} | ||||||||
projectPath := fmt.Sprintf("%s/%s", inputPath, yaml.DefaultPath) | ||||||||
if _, err := os.Stat(projectPath); os.IsNotExist(err) { | ||||||||
return "", fmt.Errorf("PROJECT path: %s does not exist. %v", projectPath, err) | ||||||||
} | ||||||||
return projectPath, nil | ||||||||
} | ||||||||
|
||||||||
func getOutputPath(currentWorkingDirectory, outputPath string) (string, error) { | ||||||||
if outputPath == "" { | ||||||||
outputPath = fmt.Sprintf("%s/%s", currentWorkingDirectory, DefaultOutputDir) | ||||||||
} | ||||||||
_, err := os.Stat(outputPath) | ||||||||
if err == nil { | ||||||||
return "", fmt.Errorf("Output path: %s already exists. %v", outputPath, err) | ||||||||
} | ||||||||
if os.IsNotExist(err) { | ||||||||
return outputPath, nil | ||||||||
} | ||||||||
return "", err | ||||||||
} | ||||||||
|
||||||||
func kubebuilderInit(_ store.Store) error { | ||||||||
var args []string | ||||||||
args = append(args, "init") | ||||||||
return util.RunCmd("kubebuilder init", "kubebuilder", args...) | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this ready in use OR we will have future development for this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, will we call Kubebuilder executable to re-scaffold the project in the future? It feels hacky to me... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this is in use. We will call that to init the project from 'PROJECT file' |
||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need a func like setDefaults which will set the defaults (here you can find a nice example)
Also, one validate() which we should call before do anything and has the purpose to validate if ALL is OK prior run the command or fail. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool! I used to have a validate() in it, but I don't know what it should check so I delete it. Maybe it can check 'whether it can find the kubebuilder binary'. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you must ensure that has a PROJECT file
Otherwise, how will you do the re-scaffold?
For now, I think just that. But we will need also do checks like
If the Project file has layout == go/v2 or go/v3 AND if the Project file is the old config version than we say that the command is not supported. but we can do that in a follow up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! So far my code will ensure the PROJECT file.
I will add other check in a follow up. :)