2024-07-05 09:54:09 -04:00
package container
import (
2024-09-26 07:35:46 -04:00
"strings"
"sync"
2024-07-05 09:54:09 -04:00
"github.com/docker/cli/cli/command/completion"
2024-07-05 18:48:51 -04:00
"github.com/docker/docker/api/types/container"
2024-09-26 07:35:46 -04:00
"github.com/moby/sys/capability"
2024-07-05 19:46:47 -04:00
"github.com/moby/sys/signal"
2024-07-05 09:54:09 -04:00
"github.com/spf13/cobra"
)
2024-09-26 07:35:46 -04:00
// allCaps is the magic value for "all capabilities".
const allCaps = "ALL"
2024-07-05 09:54:09 -04:00
// allLinuxCapabilities is a list of all known Linux capabilities.
//
// TODO(thaJeztah): add descriptions, and enable descriptions for our completion scripts (cobra.CompletionOptions.DisableDescriptions is currently set to "true")
2024-09-26 07:35:46 -04:00
// TODO(thaJeztah): consider what casing we want to use for completion (see below);
//
// We need to consider what format is most convenient; currently we use the
// canonical name (uppercase and "CAP_" prefix), however, tab-completion is
// case-sensitive by default, so requires the user to type uppercase letters
// to filter the list of options.
//
// Bash completion provides a `completion-ignore-case on` option to make completion
// case-insensitive (https://askubuntu.com/a/87066), but it looks to be a global
// option; the current cobra.CompletionOptions also don't provide this as an option
// to be used in the generated completion-script.
//
// Fish completion has `smartcase` (by default?) which matches any case if
// all of the input is lowercase.
//
// Zsh does not appear have a dedicated option, but allows setting matching-rules
// (see https://superuser.com/a/1092328).
var allLinuxCapabilities = sync . OnceValue ( func ( ) [ ] string {
caps := capability . ListKnown ( )
out := make ( [ ] string , 0 , len ( caps ) + 1 )
out = append ( out , allCaps )
for _ , c := range caps {
out = append ( out , "CAP_" + strings . ToUpper ( c . String ( ) ) )
}
return out
} )
2024-07-05 09:54:09 -04:00
2024-07-05 18:48:51 -04:00
// restartPolicies is a list of all valid restart-policies..
//
// TODO(thaJeztah): add descriptions, and enable descriptions for our completion scripts (cobra.CompletionOptions.DisableDescriptions is currently set to "true")
var restartPolicies = [ ] string {
string ( container . RestartPolicyDisabled ) ,
string ( container . RestartPolicyAlways ) ,
string ( container . RestartPolicyOnFailure ) ,
string ( container . RestartPolicyUnlessStopped ) ,
}
2024-10-22 15:40:31 -04:00
// addCompletions adds the completions that `run` and `create` have in common.
func addCompletions ( cmd * cobra . Command , dockerCLI completion . APIClientProvider ) {
2024-10-23 09:09:13 -04:00
_ = cmd . RegisterFlagCompletionFunc ( "attach" , completion . FromList ( "stderr" , "stdin" , "stdout" ) )
2024-10-22 15:40:31 -04:00
_ = cmd . RegisterFlagCompletionFunc ( "cap-add" , completeLinuxCapabilityNames )
_ = cmd . RegisterFlagCompletionFunc ( "cap-drop" , completeLinuxCapabilityNames )
_ = cmd . RegisterFlagCompletionFunc ( "env" , completion . EnvVarNames )
_ = cmd . RegisterFlagCompletionFunc ( "env-file" , completion . FileNames )
2024-10-24 07:08:41 -04:00
_ = cmd . RegisterFlagCompletionFunc ( "ipc" , completeIpc ( dockerCLI ) )
2024-10-24 07:31:25 -04:00
_ = cmd . RegisterFlagCompletionFunc ( "link" , completeLink ( dockerCLI ) )
2024-10-22 15:40:31 -04:00
_ = cmd . RegisterFlagCompletionFunc ( "network" , completion . NetworkNames ( dockerCLI ) )
2024-10-24 09:12:51 -04:00
_ = cmd . RegisterFlagCompletionFunc ( "pid" , completePid ( dockerCLI ) )
2024-10-22 15:40:31 -04:00
_ = cmd . RegisterFlagCompletionFunc ( "platform" , completion . Platforms )
_ = cmd . RegisterFlagCompletionFunc ( "pull" , completion . FromList ( PullImageAlways , PullImageMissing , PullImageNever ) )
_ = cmd . RegisterFlagCompletionFunc ( "restart" , completeRestartPolicies )
_ = cmd . RegisterFlagCompletionFunc ( "stop-signal" , completeSignals )
2024-10-24 09:45:23 -04:00
_ = cmd . RegisterFlagCompletionFunc ( "storage-opt" , completeStorageOpt )
2024-10-24 10:04:15 -04:00
_ = cmd . RegisterFlagCompletionFunc ( "ulimit" , completeUlimit )
2024-10-24 10:09:32 -04:00
_ = cmd . RegisterFlagCompletionFunc ( "userns" , completion . FromList ( "host" ) )
2024-10-22 15:40:31 -04:00
_ = cmd . RegisterFlagCompletionFunc ( "volumes-from" , completion . ContainerNames ( dockerCLI , true ) )
}
2024-10-24 07:08:41 -04:00
// completeIpc implements shell completion for the `--ipc` option of `run` and `create`.
// The completion is partly composite.
func completeIpc ( dockerCLI completion . APIClientProvider ) func ( cmd * cobra . Command , args [ ] string , toComplete string ) ( [ ] string , cobra . ShellCompDirective ) {
return func ( cmd * cobra . Command , args [ ] string , toComplete string ) ( [ ] string , cobra . ShellCompDirective ) {
if len ( toComplete ) > 0 && strings . HasPrefix ( "container" , toComplete ) { //nolint:gocritic // not swapped, matches partly typed "container"
return [ ] string { "container:" } , cobra . ShellCompDirectiveNoSpace
}
if strings . HasPrefix ( toComplete , "container:" ) {
names , _ := completion . ContainerNames ( dockerCLI , true ) ( cmd , args , toComplete )
return prefixWith ( "container:" , names ) , cobra . ShellCompDirectiveNoFileComp
}
return [ ] string {
string ( container . IPCModeContainer + ":" ) ,
string ( container . IPCModeHost ) ,
string ( container . IPCModeNone ) ,
string ( container . IPCModePrivate ) ,
string ( container . IPCModeShareable ) ,
} , cobra . ShellCompDirectiveNoFileComp
}
}
2024-10-24 07:31:25 -04:00
// completeLink implements shell completion for the `--link` option of `run` and `create`.
func completeLink ( dockerCLI completion . APIClientProvider ) func ( cmd * cobra . Command , args [ ] string , toComplete string ) ( [ ] string , cobra . ShellCompDirective ) {
return func ( cmd * cobra . Command , args [ ] string , toComplete string ) ( [ ] string , cobra . ShellCompDirective ) {
return postfixWith ( ":" , containerNames ( dockerCLI , cmd , args , toComplete ) ) , cobra . ShellCompDirectiveNoSpace
}
}
2024-10-24 09:12:51 -04:00
// completePid implements shell completion for the `--pid` option of `run` and `create`.
func completePid ( dockerCLI completion . APIClientProvider ) func ( cmd * cobra . Command , args [ ] string , toComplete string ) ( [ ] string , cobra . ShellCompDirective ) {
return func ( cmd * cobra . Command , args [ ] string , toComplete string ) ( [ ] string , cobra . ShellCompDirective ) {
if len ( toComplete ) > 0 && strings . HasPrefix ( "container" , toComplete ) { //nolint:gocritic // not swapped, matches partly typed "container"
return [ ] string { "container:" } , cobra . ShellCompDirectiveNoSpace
}
if strings . HasPrefix ( toComplete , "container:" ) {
names , _ := completion . ContainerNames ( dockerCLI , true ) ( cmd , args , toComplete )
return prefixWith ( "container:" , names ) , cobra . ShellCompDirectiveNoFileComp
}
return [ ] string { "container:" , "host" } , cobra . ShellCompDirectiveNoFileComp
}
}
2024-10-24 09:45:23 -04:00
// completeStorageOpt implements shell completion for the `--storage-opt` option of `run` and `create`.
func completeStorageOpt ( _ * cobra . Command , _ [ ] string , _ string ) ( [ ] string , cobra . ShellCompDirective ) {
return [ ] string { "size=" } , cobra . ShellCompDirectiveNoSpace
}
2024-10-24 10:04:15 -04:00
// completeUlimit implements shell completion for the `--ulimit` option of `run` and `create`.
func completeUlimit ( _ * cobra . Command , _ [ ] string , _ string ) ( [ ] string , cobra . ShellCompDirective ) {
limits := [ ] string {
"as" ,
"chroot" ,
"core" ,
"cpu" ,
"data" ,
"fsize" ,
"locks" ,
"maxlogins" ,
"maxsyslogins" ,
"memlock" ,
"msgqueue" ,
"nice" ,
"nofile" ,
"nproc" ,
"priority" ,
"rss" ,
"rtprio" ,
"sigpending" ,
"stack" ,
}
return postfixWith ( "=" , limits ) , cobra . ShellCompDirectiveNoSpace
}
2024-10-24 07:31:25 -04:00
// containerNames contacts the API to get names and optionally IDs of containers.
// In case of an error, an empty list is returned.
func containerNames ( dockerCLI completion . APIClientProvider , cmd * cobra . Command , args [ ] string , toComplete string ) [ ] string {
names , _ := completion . ContainerNames ( dockerCLI , true ) ( cmd , args , toComplete )
if names == nil {
return [ ] string { }
}
return names
}
2024-10-24 07:08:41 -04:00
// prefixWith prefixes every element in the slice with the given prefix.
func prefixWith ( prefix string , values [ ] string ) [ ] string {
result := make ( [ ] string , len ( values ) )
for i , v := range values {
result [ i ] = prefix + v
}
return result
}
2024-10-24 07:31:25 -04:00
// postfixWith appends postfix to every element in the slice.
func postfixWith ( postfix string , values [ ] string ) [ ] string {
result := make ( [ ] string , len ( values ) )
for i , v := range values {
result [ i ] = v + postfix
}
return result
}
2024-07-05 09:54:09 -04:00
func completeLinuxCapabilityNames ( cmd * cobra . Command , args [ ] string , toComplete string ) ( names [ ] string , _ cobra . ShellCompDirective ) {
2024-09-26 07:35:46 -04:00
return completion . FromList ( allLinuxCapabilities ( ) ... ) ( cmd , args , toComplete )
2024-07-05 09:54:09 -04:00
}
2024-07-05 18:48:51 -04:00
func completeRestartPolicies ( cmd * cobra . Command , args [ ] string , toComplete string ) ( names [ ] string , _ cobra . ShellCompDirective ) {
return completion . FromList ( restartPolicies ... ) ( cmd , args , toComplete )
}
2024-07-05 19:46:47 -04:00
func completeSignals ( cmd * cobra . Command , args [ ] string , toComplete string ) ( names [ ] string , _ cobra . ShellCompDirective ) {
// TODO(thaJeztah): do we want to provide the full list here, or a subset?
signalNames := make ( [ ] string , 0 , len ( signal . SignalMap ) )
for k := range signal . SignalMap {
signalNames = append ( signalNames , k )
}
return completion . FromList ( signalNames ... ) ( cmd , args , toComplete )
}