Skip to content

Commit

Permalink
feat: add meta flags and options for common logic and data
Browse files Browse the repository at this point in the history
  • Loading branch information
adohe committed Apr 6, 2024
1 parent c21eacc commit e0c58d7
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 42 deletions.
2 changes: 1 addition & 1 deletion pkg/cmd/apply/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (o *Options) Run() error {
}

// parse project and stack of work directory
currentProject, currentStack, err := project.DetectProjectAndStack(o.Options.WorkDir)
currentProject, currentStack, err := project.DetectProjectAndStackFrom(o.Options.WorkDir)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/apply/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ var (
)

func mockPatchDetectProjectAndStack() *mockey.Mocker {
return mockey.Mock(project.DetectProjectAndStack).To(func(stackDir string) (*apiv1.Project, *apiv1.Stack, error) {
return mockey.Mock(project.DetectProjectAndStackFrom).To(func(stackDir string) (*apiv1.Project, *apiv1.Stack, error) {
proj.Path = stackDir
stack.Path = stackDir
return proj, stack, nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/destroy/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (o *Options) Run() error {
// listen for interrupts or the SIGTERM signal
signals.HandleInterrupt()
// parse project and stack of work directory
proj, stack, err := project.DetectProjectAndStack(o.Options.WorkDir)
proj, stack, err := project.DetectProjectAndStackFrom(o.Options.WorkDir)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/destroy/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ var (
)

func mockDetectProjectAndStack() {
mockey.Mock(project.DetectProjectAndStack).To(func(stackDir string) (*apiv1.Project, *apiv1.Stack, error) {
mockey.Mock(project.DetectProjectAndStackFrom).To(func(stackDir string) (*apiv1.Project, *apiv1.Stack, error) {
proj.Path = stackDir
stack.Path = stackDir
return proj, stack, nil
Expand Down
55 changes: 25 additions & 30 deletions pkg/cmd/generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import (
"kusionstack.io/kusion/pkg/backend"
"kusionstack.io/kusion/pkg/cmd/generate/generator"
"kusionstack.io/kusion/pkg/cmd/generate/run"
"kusionstack.io/kusion/pkg/cmd/meta"
cmdutil "kusionstack.io/kusion/pkg/cmd/util"
"kusionstack.io/kusion/pkg/engine/spec"
"kusionstack.io/kusion/pkg/project"
"kusionstack.io/kusion/pkg/util/i18n"
"kusionstack.io/kusion/pkg/util/pretty"
)
Expand Down Expand Up @@ -46,8 +46,7 @@ var (
// This structure reduces the transformation to wiring and makes the logic itself easy to unit test.
type GenerateFlags struct {
WorkDir string
Backend string
Workspace string
MetaFlags *meta.MetaFlags

genericiooptions.IOStreams
}
Expand All @@ -56,17 +55,15 @@ type GenerateFlags struct {
type GenerateOptions struct {
WorkDir string

Project *v1.Project
Stack *v1.Stack
Workspace *v1.Workspace
MetaOptions *meta.MetaOptions

SpecStorage spec.Storage
Generator generator.Generator
}

// NewGenerateFlags returns a default GenerateFlags
func NewGenerateFlags(streams genericiooptions.IOStreams) *GenerateFlags {
return &GenerateFlags{
MetaFlags: meta.NewMetaFlags(),
IOStreams: streams,
}
}
Expand Down Expand Up @@ -97,9 +94,10 @@ func NewCmdGenerate(ioStreams genericiooptions.IOStreams) *cobra.Command {

// AddFlags registers flags for a cli.
func (flags *GenerateFlags) AddFlags(cmd *cobra.Command) {
// bind flag structs
flags.MetaFlags.AddFlags(cmd)

cmd.Flags().StringVarP(&flags.WorkDir, "workdir", "w", flags.WorkDir, i18n.T("The working directory for generate (default is current dir where executed)."))
cmd.Flags().StringVarP(&flags.Backend, "backend", "", flags.Backend, i18n.T("The backend to use, supports 'local', 'oss' and 's3'."))
cmd.Flags().StringVarP(&flags.Workspace, "workspace", "", flags.Workspace, i18n.T("The name of target workspace to operate in."))
}

// ToOptions converts from CLI inputs to runtime inputs.
Expand All @@ -110,35 +108,27 @@ func (flags *GenerateFlags) ToOptions() (*GenerateOptions, error) {
workDir, _ = os.Getwd()
}

// Parse project and currentStack of work directory
currentProject, currentStack, err := project.DetectProjectAndStack(workDir)
if err != nil {
return nil, err
}

// Get current workspace from backend
workspaceStorage, err := backend.NewWorkspaceStorage(flags.Backend)
if err != nil {
return nil, err
}
currentWorkspace, err := workspaceStorage.Get(flags.Workspace)
// Convert meta options
metaOptions, err := flags.MetaFlags.ToOptions()
if err != nil {
return nil, err
}

// Get target spec storage
specStorage, err := backend.NewSpecStorage(flags.Backend, currentProject.Name, currentStack.Name, flags.Workspace)
specStorage, err := backend.NewSpecStorage(
*flags.MetaFlags.Backend,
metaOptions.RefProject.Name,
metaOptions.RefStack.Name,
metaOptions.RefWorkspace.Name,
)
if err != nil {
return nil, err
}

o := &GenerateOptions{
WorkDir: workDir,
Project: currentProject,
Stack: currentStack,
Workspace: currentWorkspace,
MetaOptions: metaOptions,
SpecStorage: specStorage,
Generator: nil,
}

return o, nil
Expand All @@ -155,11 +145,16 @@ func (o *GenerateOptions) Validate(cmd *cobra.Command, args []string) error {

// Run executes the `generate` command.
func (o *GenerateOptions) Run() error {
spec, err := GenerateSpecWithSpinner(o.Project, o.Stack, o.Workspace, true)
versionedSpec, err := GenerateSpecWithSpinner(
o.MetaOptions.RefProject,
o.MetaOptions.RefStack,
o.MetaOptions.RefWorkspace,
true,
)
if err != nil {
return err
}
return o.SpecStorage.Apply(spec)
return o.SpecStorage.Apply(versionedSpec)
}

// GenerateSpecWithSpinner calls generator to generate versioned Spec. Add a method wrapper for testing purposes.
Expand Down Expand Up @@ -189,7 +184,7 @@ func GenerateSpecWithSpinner(project *v1.Project, stack *v1.Stack, workspace *v1
// style means color and prompt here. Currently, sp will be nil only when o.NoStyle is true
style := !noStyle && sp != nil

spec, err := defaultGenerator.Generate(stack.Path, nil)
versionedSpec, err := defaultGenerator.Generate(stack.Path, nil)
if err != nil {
if style {
sp.Fail()
Expand All @@ -206,7 +201,7 @@ func GenerateSpecWithSpinner(project *v1.Project, stack *v1.Stack, workspace *v1
fmt.Println()
}

return spec, nil
return versionedSpec, nil
}

func SpecFromFile(filePath string) (*v1.Spec, error) {
Expand Down
99 changes: 99 additions & 0 deletions pkg/cmd/meta/meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2024 KusionStack 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 meta

import (
"github.com/spf13/cobra"

v1 "kusionstack.io/kusion/pkg/apis/api.kusion.io/v1"
"kusionstack.io/kusion/pkg/backend"
"kusionstack.io/kusion/pkg/project"
"kusionstack.io/kusion/pkg/util/i18n"
)

// MetaFlags directly reflect the information that CLI is gathering via flags. They will be converted to
// MetaOptions, which reflect the runtime requirements for the command.
//
// This structure reduces the transformation to wiring and makes the logic itself easy to unit test.
type MetaFlags struct {
Application *string
Workspace *string

Backend *string
}

// MetaOptions are the meta-options that are available on all or most commands.
type MetaOptions struct {
// RefProject references the project for this CLI invocation.
RefProject *v1.Project

// RefStack referenced the stack for this CLI invocation
RefStack *v1.Stack

// RefWorkspace referenced the workspace for this CLI invocation
RefWorkspace *v1.Workspace
}

// NewMetaFlags provides default flags and values for use in other commands.
func NewMetaFlags() *MetaFlags {
application := ""
workspace := ""
backend := ""

return &MetaFlags{
Application: &application,
Workspace: &workspace,
Backend: &backend,
}
}

// AddFlags registers flags for a cli.
func (f *MetaFlags) AddFlags(cmd *cobra.Command) {
if f.Workspace != nil {
cmd.Flags().StringVarP(f.Workspace, "workspace", "", *f.Workspace, i18n.T("The name of target workspace to operate in."))
}
if f.Backend != nil {
cmd.Flags().StringVarP(f.Backend, "backend", "", *f.Backend, i18n.T("The backend to use, supports 'local', 'oss' and 's3'."))
}
}

// ToOptions converts MetaFlags to MetaOptions.
func (f *MetaFlags) ToOptions() (*MetaOptions, error) {
opts := &MetaOptions{}

// Parse project and currentStack of work directory
refProject, refStack, err := project.DetectProjectAndStacks()
if err != nil {
return nil, err
}

opts.RefProject = refProject
opts.RefStack = refStack

// Get current workspace from backend
if f.Backend != nil && f.Workspace != nil {
workspaceStorage, err := backend.NewWorkspaceStorage(*f.Backend)
if err != nil {
return nil, err
}
refWorkspace, err := workspaceStorage.Get(*f.Workspace)
if err != nil {
return nil, err
}
opts.RefWorkspace = refWorkspace
}

return opts, nil
}
2 changes: 1 addition & 1 deletion pkg/cmd/preview/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func (o *Options) Run() error {
}

// Parse project and currentStack of work directory
currentProject, currentStack, err := project.DetectProjectAndStack(o.WorkDir)
currentProject, currentStack, err := project.DetectProjectAndStackFrom(o.WorkDir)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/preview/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func newSA(name string) apiv1.Resource {
}

func mockDetectProjectAndStack() {
mockey.Mock(project.DetectProjectAndStack).To(func(stackDir string) (*apiv1.Project, *apiv1.Stack, error) {
mockey.Mock(project.DetectProjectAndStackFrom).To(func(stackDir string) (*apiv1.Project, *apiv1.Stack, error) {
proj.Path = stackDir
stack.Path = stackDir
return proj, stack, nil
Expand Down
15 changes: 13 additions & 2 deletions pkg/project/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,19 @@ const (
StackFile = "stack.yaml"
)

// DetectProjectAndStack try to get stack and project from given path
func DetectProjectAndStack(stackDir string) (p *v1.Project, s *v1.Stack, err error) {
// DetectProjectAndStacks locates the closest project and stack from the current working directory,
// or an error if not found.
func DetectProjectAndStacks() (*v1.Project, *v1.Stack, error) {
dir, err := os.Getwd()
if err != nil {
return nil, nil, err
}

return DetectProjectAndStackFrom(dir)
}

// DetectProjectAndStackFrom try to get stack and project from given path
func DetectProjectAndStackFrom(stackDir string) (p *v1.Project, s *v1.Stack, err error) {
stackDir, err = filepath.Abs(stackDir)
if err != nil {
return nil, nil, err
Expand Down
8 changes: 4 additions & 4 deletions pkg/project/paths_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,17 +205,17 @@ func TestDetectProjectAndStack(t *testing.T) {
for _, tt := range tests {
mockey.PatchConvey(tt.name, t, func() {
tt.preRun()
project, stack, err := DetectProjectAndStack(tt.args.stackDir)
project, stack, err := DetectProjectAndStackFrom(tt.args.stackDir)
tt.postRun()
if (err != nil) != tt.wantErr {
t.Errorf("DetectProjectAndStack() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("DetectProjectAndStackFrom() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(project, tt.project) {
t.Errorf("DetectProjectAndStack() got = %v, want %v", project, tt.project)
t.Errorf("DetectProjectAndStackFrom() got = %v, want %v", project, tt.project)
}
if !reflect.DeepEqual(stack, tt.stack) {
t.Errorf("DetectProjectAndStack() gosuccess = %v, want %v", stack, tt.stack)
t.Errorf("DetectProjectAndStackFrom() gosuccess = %v, want %v", stack, tt.stack)
}
})
}
Expand Down

0 comments on commit e0c58d7

Please sign in to comment.