Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 49 additions & 24 deletions pkg/karmadactl/util/completion/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/util"
)

const completionRequestTimeout = 5 * time.Second

var factory util.Factory

// SetFactoryForCompletion Store the factory which is needed by the completion functions.
Expand Down Expand Up @@ -256,35 +258,58 @@ func compGetResourceList(restClientGetter genericclioptions.RESTClientGetter, cm
o.PrintFlags.OutputFormat = ptr.To("name")
o.Cached = true
o.Verbs = []string{"get"}
// TODO: Should set --request-timeout=5s

if err := o.Complete(restClientGetter, cmd, nil); err != nil {
return nil
}
// Use a context with timeout to prevent indefinite waiting during shell completion
ctx, cancel := context.WithTimeout(context.Background(), completionRequestTimeout)
defer cancel()

// Ignore errors as the output may still be valid
if err := o.RunAPIResources(); err != nil {
return nil
}
// Run the completion operations with timeout protection
resultChan := make(chan []string, 1)
errorChan := make(chan error, 1)

// Resources can be a comma-separated list. The last element is then
// the one we should complete. For example if toComplete=="pods,secre"
// we should return "pods,secrets"
prefix := ""
suffix := toComplete
lastIdx := strings.LastIndex(toComplete, ",")
if lastIdx != -1 {
prefix = toComplete[0 : lastIdx+1]
suffix = toComplete[lastIdx+1:]
}
var comps []string
resources := strings.SplitSeq(buf.String(), "\n")
for res := range resources {
if res != "" && strings.HasPrefix(res, suffix) {
comps = append(comps, fmt.Sprintf("%s%s", prefix, res))
go func() {
if err := o.Complete(restClientGetter, cmd, nil); err != nil {
errorChan <- err
return
}

// Ignore errors as the output may still be valid
if err := o.RunAPIResources(); err != nil {
errorChan <- err
return
}

// Resources can be a comma-separated list. The last element is then
// the one we should complete. For example if toComplete=="pods,secre"
// we should return "pods,secrets"
prefix := ""
suffix := toComplete
lastIdx := strings.LastIndex(toComplete, ",")
if lastIdx != -1 {
prefix = toComplete[0 : lastIdx+1]
suffix = toComplete[lastIdx+1:]
}
var comps []string
resources := strings.SplitSeq(buf.String(), "\n")
for res := range resources {
if res != "" && strings.HasPrefix(res, suffix) {
comps = append(comps, fmt.Sprintf("%s%s", prefix, res))
}
}
resultChan <- comps
}()

// Wait for either completion or timeout
select {
case comps := <-resultChan:
return comps
case <-errorChan:
// Error occurred, return nil to fail gracefully during completion
return nil
case <-ctx.Done():
// Timeout occurred, return empty completion list to prevent shell hang
return nil
}
return comps
}

// resourceTypeAndNameCompletionFunc Returns a completion function that completes resource types
Expand Down