Skip to content

Commit

Permalink
lxc: Add completions for server keys and console (#14115)
Browse files Browse the repository at this point in the history
Resolves #14098.

All the improvements made to the `lxc config get/set` completions have
been tested locally. I made a minor change that deviates from the
requirements outlined in #14098.
Instead of typing `lxc config get foo:core.<tab>` to retrieve server
configuration options, typing `lxc config get foo: core.<tab>` (notice
the [SPACE]) will now return server configuration options. This change
was implemented to differentiate from the conventional usage of
`remote:instance` across the CLI. However, please let me know if you'd
prefer server configuration completions without the [SPACE] 😄.
  • Loading branch information
tomponline authored Sep 27, 2024
2 parents f1271f6 + 2b39578 commit b236cea
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 3 deletions.
1 change: 1 addition & 0 deletions client/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type InstanceServer interface {
ImageServer

// Server functions
GetMetadataConfiguration() (metadataConfiguration *api.MetadataConfiguration, err error)
GetMetrics() (metrics string, err error)
GetServer() (server *api.Server, ETag string, err error)
GetServerResources() (resources *api.Resources, err error)
Expand Down
18 changes: 18 additions & 0 deletions client/lxd_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,21 @@ func (r *ProtocolLXD) GetMetrics() (string, error) {

return string(content), nil
}

// GetMetadataConfiguration returns metadata configuration for a server.
func (r *ProtocolLXD) GetMetadataConfiguration() (*api.MetadataConfiguration, error) {
// Check that the server supports it.
err := r.CheckExtension("metadata_configuration")
if err != nil {
return nil, err
}

metadataConfiguration := api.MetadataConfiguration{}

_, err = r.queryStruct(http.MethodGet, api.NewURL().Path("metadata", "configuration").String(), nil, "", &metadataConfiguration)
if err != nil {
return nil, err
}

return &metadataConfiguration, err
}
58 changes: 55 additions & 3 deletions lxc/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,18 @@ func (g *cmdGlobal) cmpImages(toComplete string) ([]string, cobra.ShellCompDirec
}

func (g *cmdGlobal) cmpInstanceAllKeys(instanceName string) ([]string, cobra.ShellCompDirective) {
var keys []string
cmpDirectives := cobra.ShellCompDirectiveNoFileComp

_, instanceNameOnly, _ := strings.Cut(instanceName, ":")
if instanceNameOnly == "" {
serverKeys, directives := g.cmpServerAllKeys(instanceName)
keys = append(keys, serverKeys...)
cmpDirectives = directives

return keys, cmpDirectives
}

resources, err := g.ParseServers(instanceName)
if err != nil || len(resources) == 0 {
return nil, cobra.ShellCompDirectiveError
Expand All @@ -219,13 +231,12 @@ func (g *cmdGlobal) cmpInstanceAllKeys(instanceName string) ([]string, cobra.She
resource := resources[0]
client := resource.server

instanceNameOnly, _, err := client.GetInstance(instanceName)
instance, _, err := client.GetInstance(instanceNameOnly)
if err != nil {
return nil, cobra.ShellCompDirectiveError
}

var keys []string
instanceType := instanceNameOnly.Type
instanceType := instance.Type

if instanceType == "container" {
for k := range instancetype.InstanceConfigKeysContainer {
Expand All @@ -241,6 +252,43 @@ func (g *cmdGlobal) cmpInstanceAllKeys(instanceName string) ([]string, cobra.She
keys = append(keys, k)
}

return keys, cmpDirectives
}

func (g *cmdGlobal) cmpServerAllKeys(instanceName string) ([]string, cobra.ShellCompDirective) {
resources, err := g.ParseServers(instanceName)
if err != nil || len(resources) == 0 {
return nil, cobra.ShellCompDirectiveError
}

resource := resources[0]
client := resource.server

metadataConfiguration, err := client.GetMetadataConfiguration()
if err != nil {
return nil, cobra.ShellCompDirectiveError
}

server, ok := metadataConfiguration.Configs["server"]
if !ok {
return nil, cobra.ShellCompDirectiveError
}

keyCount := 0
for _, field := range server {
keyCount += len(field.Keys)
}

keys := make([]string, 0, keyCount)

for _, field := range server {
for _, keyMap := range field.Keys {
for key := range keyMap {
keys = append(keys, key)
}
}
}

return keys, cobra.ShellCompDirectiveNoFileComp
}

Expand Down Expand Up @@ -937,6 +985,10 @@ func (g *cmdGlobal) cmpRemotes(includeAll bool) ([]string, cobra.ShellCompDirect
continue
}

if remoteName == "local" || rc.Protocol == "simplestreams" {
continue
}

results = append(results, fmt.Sprintf("%s:", remoteName))
}

Expand Down
12 changes: 12 additions & 0 deletions lxc/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,10 @@ func (c *cmdConfigGet) command() *cobra.Command {

cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
if strings.Contains(toComplete, ".") {
return c.global.cmpServerAllKeys(toComplete)
}

return c.global.cmpInstances(toComplete)
}

Expand Down Expand Up @@ -553,6 +557,10 @@ lxc config set core.https_address=[::]:8443

cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
if strings.Contains(toComplete, ".") {
return c.global.cmpServerAllKeys(toComplete)
}

return c.global.cmpInstances(toComplete)
}

Expand Down Expand Up @@ -902,6 +910,10 @@ func (c *cmdConfigUnset) command() *cobra.Command {

cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
if strings.Contains(toComplete, ".") {
return c.global.cmpServerAllKeys(toComplete)
}

return c.global.cmpInstances(toComplete)
}

Expand Down
4 changes: 4 additions & 0 deletions lxc/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ as well as retrieve past log entries from it.`))
cmd.Flags().BoolVar(&c.flagShowLog, "show-log", false, i18n.G("Retrieve the container's console log"))
cmd.Flags().StringVarP(&c.flagType, "type", "t", "console", i18n.G("Type of connection to establish: 'console' for serial console, 'vga' for SPICE graphical output")+"``")

cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return c.global.cmpInstances(toComplete)
}

return cmd
}

Expand Down

0 comments on commit b236cea

Please sign in to comment.