diff --git a/pkg/cmdutil/create.go b/pkg/cmdutil/create.go index 4b42d84..7d5ec42 100644 --- a/pkg/cmdutil/create.go +++ b/pkg/cmdutil/create.go @@ -10,6 +10,8 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/record/util" + "os" + "path/filepath" "time" ) @@ -19,11 +21,13 @@ type Creator interface { Runnable GeneratePrefix() string Build() runtime.Object + Set(entity runtime.Object) } type CreateOptions struct { *GenericOperationOptions - Delegate Creator + Delegate Creator + fromDescriptor bool } func NewCreateOptions(resourceType ResourceType, client HalkyonEntity) *CreateOptions { @@ -37,14 +41,74 @@ func NewCreateOptions(resourceType ResourceType, client HalkyonEntity) *CreateOp return c } -func (o *CreateOptions) Complete(name string, cmd *cobra.Command, args []string) error { - if err := o.Delegate.Complete(name, cmd, args); err != nil { - return err +func entityNames(registry entitiesRegistry) []string { + names := make([]string, 0, len(registry)) + for _, entity := range registry { + names = append(names, entity.Name) } + return names +} +func (o *CreateOptions) Complete(name string, cmd *cobra.Command, args []string) error { if len(args) == 1 { o.Name = args[0] } + + // look for locally existing components that also don't already exist on the remote cluster + currentDir, err := os.Getwd() + if err != nil { + return err + } + hd := LoadAvailableHalkyonEntities(currentDir) + entities := hd.GetDefinedEntitiesWith(o.ResourceType) + size := len(entities) + t := o.ResourceType.String() + if size > 0 { + // if we have an entity with the same name as the current directory, use it by default + currentDirName := filepath.Base(currentDir) + if entity, ok := entities[currentDirName]; ok { + o.Name = entity.Name + o.fromDescriptor = true + o.Delegate.Set(entity.Entity) + } else { + names := entityNames(entities) + if size == 1 { + if o.Name == names[0] { + entity := entities[o.Name] + if IsInteractive(cmd) && ui.Proceed(fmt.Sprintf("Found %s named %s in %s, use it", t, o.Name, entity.Path)) { + o.Name = entity.Name + o.fromDescriptor = true + o.Delegate.Set(entity.Entity) + } + } + } else if IsInteractive(cmd) && ui.Proceed(fmt.Sprintf("Found %d %s(s), do you want to %s from them", size, t, o.operationName)) { + o.Name = ui.Select(t, names, o.Name) + } + } + + } + + entity, ok := entities[o.Name] + if ok { + ui.OutputSelection(fmt.Sprintf("Selected %s from %s", t, entity.Path), o.Name) + o.fromDescriptor = true + // set the component on the delegate so it uses it when we want ask to create it + o.Delegate.Set(entity.Entity) + exists, err := o.Exists() + if err != nil { + return err + } + if exists { + return fmt.Errorf("a %s named '%s' already exists, please use update instead (NOT YET IMPLEMENTED)", o.ResourceType, o.Name) + } + } + + if !o.fromDescriptor { + if err := o.Delegate.Complete(name, cmd, args); err != nil { + return err + } + } + for { o.Name = ui.Ask("Name", o.Name, o.generateName()) err := validation.NameValidator(o.Name) @@ -68,9 +132,23 @@ func (o *CreateOptions) Complete(name string, cmd *cobra.Command, args []string) return nil } +func (o *CreateOptions) Exists() (bool, error) { + err := o.Client.Get(o.Name, v1.GetOptions{}) + if err != nil { + if util.IsKeyNotFoundError(errors.Cause(err)) { + return false, nil + } else { + return false, err + } + } + return true, nil +} + func (o *CreateOptions) Validate() error { - if err := o.Delegate.Validate(); err != nil { - return err + if !o.fromDescriptor { + if err := o.Delegate.Validate(); err != nil { + return err + } } return nil } diff --git a/pkg/hal/cli/capability/create.go b/pkg/hal/cli/capability/create.go index 733726c..64e46b1 100644 --- a/pkg/hal/cli/capability/create.go +++ b/pkg/hal/cli/capability/create.go @@ -22,6 +22,11 @@ type createOptions struct { paramPairs []string parameters []halkyon.NameValuePair *cmdutil.CreateOptions + target *v1beta1.Capability +} + +func (o *createOptions) Set(entity runtime.Object) { + o.target = entity.(*v1beta1.Capability) } var ( diff --git a/pkg/hal/cli/component/create.go b/pkg/hal/cli/component/create.go index c430fab..fe09dca 100644 --- a/pkg/hal/cli/component/create.go +++ b/pkg/hal/cli/component/create.go @@ -64,6 +64,7 @@ type createOptions struct { Template string P string scaffoldP string + target *v1beta1.Component } func (o *createOptions) GeneratePrefix() string { @@ -92,6 +93,10 @@ func (o *createOptions) Build() runtime.Object { } } +func (o *createOptions) Set(entity runtime.Object) { + o.target = entity.(*v1beta1.Component) +} + var ( createExample = ktemplates.Examples(` # Create a new Halkyon component located in the 'foo' child directory of the current directory %[1]s foo`) diff --git a/pkg/hal/cli/link/create.go b/pkg/hal/cli/link/create.go index 55d0c10..f89d551 100644 --- a/pkg/hal/cli/link/create.go +++ b/pkg/hal/cli/link/create.go @@ -24,6 +24,7 @@ type createOptions struct { linkType link.LinkType *cmdutil.CreateOptions *cmdutil.EnvOptions + target *link.Link } func (o *createOptions) SetEnvOptions(env *cmdutil.EnvOptions) { @@ -106,6 +107,10 @@ func (o *createOptions) GeneratePrefix() string { return o.targetName } +func (o *createOptions) Set(entity runtime.Object) { + o.target = entity.(*link.Link) +} + func NewCmdCreate(parent string) *cobra.Command { c := k8s.GetClient() o := &createOptions{}