bash (and zsh without a matcher) only accept candidates that start with the typed text, so "fsn1-bx1<Tab>" found nothing when every candidate was qualified as "prod/…". Hostnames that exist in only one instance are now offered bare; names present in several instances stay qualified. Connecting to a bare name still checks every instance for ambiguity, so the connection behavior is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
70 lines
2.2 KiB
Go
70 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
|
|
"git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/cache"
|
|
"git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/config"
|
|
"git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/netbox"
|
|
"git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/shortcuts"
|
|
)
|
|
|
|
func newNetBoxClient(cfg *config.Config) *netbox.Client {
|
|
if len(cfg.NetBoxes) > 0 {
|
|
sources := make([]netbox.Source, 0, len(cfg.NetBoxes))
|
|
for _, source := range cfg.NetBoxes {
|
|
sources = append(sources, netbox.Source{Name: source.Name, URL: source.URL, Token: source.Token, TokenVersion: source.TokenVersion})
|
|
}
|
|
return netbox.NewMultiClient(sources)
|
|
}
|
|
if cfg.NetBox.URL == "" {
|
|
return nil
|
|
}
|
|
return netbox.NewClient(cfg.NetBox.URL, cfg.NetBox.Token, cfg.NetBox.TokenVersion)
|
|
}
|
|
|
|
func normalizeTarget(cfg config.ShortcutsConfig, qualified bool) func(string) string {
|
|
return func(target string) string {
|
|
source, name := netbox.SplitTarget(target)
|
|
name = shortcuts.Normalize(name, cfg)
|
|
if qualified && source != "" {
|
|
return source + "/" + name
|
|
}
|
|
return name
|
|
}
|
|
}
|
|
|
|
// hostCompletions returns the cached hosts matching toComplete in a form that
|
|
// starts with the typed text, because bash and fish only accept such
|
|
// candidates. Without an instance prefix, a hostname that is unique across
|
|
// all instances is offered bare ("web01"), so it completes like with a single
|
|
// NetBox; connecting to it still checks every instance for ambiguity.
|
|
// Names present in several instances stay qualified ("prod/web01").
|
|
func hostCompletions(entries []cache.Entry, toComplete string) []string {
|
|
prefix := strings.ToLower(toComplete)
|
|
qualifiedInput := strings.Contains(prefix, "/")
|
|
instances := make(map[string]int)
|
|
for _, e := range entries {
|
|
_, name := netbox.SplitTarget(e.Name)
|
|
instances[strings.ToLower(name)]++
|
|
}
|
|
var out []string
|
|
for _, e := range entries {
|
|
source, name := netbox.SplitTarget(e.Name)
|
|
target := strings.ToLower(e.Name)
|
|
switch {
|
|
case source != "" && !qualifiedInput && strings.HasPrefix(strings.ToLower(name), prefix):
|
|
if instances[strings.ToLower(name)] == 1 {
|
|
out = append(out, name)
|
|
} else {
|
|
out = append(out, e.Name)
|
|
}
|
|
case strings.HasPrefix(target, prefix):
|
|
out = append(out, e.Name)
|
|
}
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|