mirror of https://github.com/containers/podman.git
				
				
				
			
		
			
				
	
	
		
			414 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Bash
		
	
	
	
			
		
		
	
	
			414 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Bash
		
	
	
	
#!/usr/bin/env bats   -*- bats -*-
 | 
						|
#
 | 
						|
# Test podman shell completion
 | 
						|
#
 | 
						|
# Shell completion is provided via the cobra library
 | 
						|
# It is implement by calling a hidden subcommand called "__complete"
 | 
						|
#
 | 
						|
 | 
						|
load helpers
 | 
						|
 | 
						|
function setup() {
 | 
						|
    # $PODMAN may be a space-separated string, e.g. if we include a --url.
 | 
						|
    local -a podman_as_array=($PODMAN)
 | 
						|
    # __completeNoDesc must be the first arg if we running the completion cmd
 | 
						|
    # set the var for the run_completion function
 | 
						|
    PODMAN_COMPLETION="${podman_as_array[0]} __completeNoDesc ${podman_as_array[@]:1}"
 | 
						|
 | 
						|
    basic_setup
 | 
						|
}
 | 
						|
 | 
						|
# Returns true if we are able to podman-pause
 | 
						|
function _can_pause() {
 | 
						|
    # Even though we're just trying completion, not an actual unpause,
 | 
						|
    # podman barfs with:
 | 
						|
    #    Error: unpause is not supported for cgroupv1 rootless containers
 | 
						|
    if is_rootless && is_cgroupsv1; then
 | 
						|
        return 1
 | 
						|
    fi
 | 
						|
    return 0
 | 
						|
}
 | 
						|
 | 
						|
function check_shell_completion() {
 | 
						|
    local count=0
 | 
						|
 | 
						|
    # Newline character; used for confirming string output
 | 
						|
    local nl="
 | 
						|
"
 | 
						|
 | 
						|
    for cmd in $(_podman_commands "$@"); do
 | 
						|
        # Skip the compose command which is calling `docker-compose --help`
 | 
						|
        # and hence won't match the assumptions made below.
 | 
						|
        if [[ "$cmd" == "compose" ]]; then
 | 
						|
            continue
 | 
						|
        fi
 | 
						|
        # Human-readable podman command string, with multiple spaces collapsed
 | 
						|
        name="podman"
 | 
						|
        if is_remote; then
 | 
						|
            name="podman-remote"
 | 
						|
        fi
 | 
						|
        command_string="$name $* $cmd"
 | 
						|
        command_string=${command_string//  / } # 'podman  x' -> 'podman x'
 | 
						|
 | 
						|
        run_podman "$@" $cmd --help
 | 
						|
        local full_help="$output"
 | 
						|
 | 
						|
        # The line immediately after 'Usage:' gives us a 1-line synopsis
 | 
						|
        usage=$(echo "$full_help" | grep -A1 '^Usage:' | tail -1)
 | 
						|
        assert "$usage" != "" "podman $cmd: no Usage message found"
 | 
						|
 | 
						|
        # If usage ends in '[command]', recurse into subcommands
 | 
						|
        if expr "$usage" : '.*\[command\]$' >/dev/null; then
 | 
						|
            check_shell_completion "$@" $cmd
 | 
						|
            continue
 | 
						|
        fi
 | 
						|
 | 
						|
        # Trim to command path so we only have the args
 | 
						|
        args="${usage/$command_string/}"
 | 
						|
        # Trim leading whitespaces
 | 
						|
        args="${args#"${args%%[![:space:]]*}"}"
 | 
						|
 | 
						|
        # Extra args is used to match the correct argument number for the command
 | 
						|
        # This is important because some commands provide different suggestions based
 | 
						|
        # on the number of arguments.
 | 
						|
        extra_args=()
 | 
						|
 | 
						|
        for arg in $args; do
 | 
						|
 | 
						|
            match=false
 | 
						|
            i=0
 | 
						|
            while true; do
 | 
						|
 | 
						|
                case $arg in
 | 
						|
 | 
						|
                # If we have options than we need to check if we are getting flag completion
 | 
						|
                "[options]")
 | 
						|
                    # skip this for remote it fails if a command only has the latest flag e.g podman top
 | 
						|
                    if ! is_remote; then
 | 
						|
                        run_completion "$@" $cmd "--"
 | 
						|
                        # If this fails there is most likely a problem with the cobra library
 | 
						|
                        is "${lines[0]}" "--.*" \
 | 
						|
                           "$* $cmd: flag(s) listed in suggestions"
 | 
						|
                        assert "${#lines[@]}" -gt 2 \
 | 
						|
                               "$* $cmd: No flag suggestions"
 | 
						|
                        _check_completion_end NoFileComp
 | 
						|
                    fi
 | 
						|
                    # continue the outer for args loop
 | 
						|
                    continue 2
 | 
						|
                    ;;
 | 
						|
 | 
						|
                *CONTAINER*)
 | 
						|
                    # podman unpause fails early on rootless cgroupsv1
 | 
						|
                    if [[ $cmd = "unpause" ]] && ! _can_pause; then
 | 
						|
                        continue 2
 | 
						|
                    fi
 | 
						|
 | 
						|
                    name=$random_container_name
 | 
						|
                    # special case podman cp suggest containers names with a colon
 | 
						|
                    if [[ $cmd = "cp" ]]; then
 | 
						|
                        name="$name:"
 | 
						|
                    fi
 | 
						|
 | 
						|
                    run_completion "$@" $cmd "${extra_args[@]}" ""
 | 
						|
                    is "$output" ".*-$name${nl}" \
 | 
						|
                       "$* $cmd: actual container listed in suggestions"
 | 
						|
 | 
						|
                    match=true
 | 
						|
                    # resume
 | 
						|
                    ;;&
 | 
						|
 | 
						|
                *POD*)
 | 
						|
                    run_completion "$@" $cmd "${extra_args[@]}" ""
 | 
						|
                    is "$output" ".*-$random_pod_name${nl}" \
 | 
						|
                       "$* $cmd: actual pod listed in suggestions"
 | 
						|
                    _check_completion_end NoFileComp
 | 
						|
 | 
						|
                    match=true
 | 
						|
                    # resume
 | 
						|
                    ;;&
 | 
						|
 | 
						|
                *IMAGE*)
 | 
						|
                    run_completion "$@" $cmd "${extra_args[@]}" ""
 | 
						|
                    is "$output" ".*localhost/$random_image_name:$random_image_tag${nl}" \
 | 
						|
                       "$* $cmd: actual image listed in suggestions"
 | 
						|
 | 
						|
                    # check that we complete the image with tag after at least one char is typed
 | 
						|
                    run_completion "$@" $cmd "${extra_args[@]}" "${random_image_name:0:1}"
 | 
						|
                    is "$output" ".*$random_image_name:$random_image_tag${nl}" \
 | 
						|
                       "$* $cmd: image name:tag included in suggestions"
 | 
						|
 | 
						|
                    # check that we complete the image id after at least two chars are typed
 | 
						|
                    run_completion "$@" $cmd "${extra_args[@]}" "${random_image_id:0:2}"
 | 
						|
                    is "$output" ".*$random_image_id${nl}" \
 | 
						|
                       "$* $cmd: image id included in suggestions when two leading characters present in command line"
 | 
						|
 | 
						|
                    match=true
 | 
						|
                    # resume
 | 
						|
                    ;;&
 | 
						|
 | 
						|
                *NETWORK*)
 | 
						|
                    run_completion "$@" $cmd "${extra_args[@]}" ""
 | 
						|
                    is "$output" ".*$random_network_name${nl}" \
 | 
						|
                       "$* $cmd: actual network listed in suggestions"
 | 
						|
                    _check_completion_end NoFileComp
 | 
						|
 | 
						|
                    match=true
 | 
						|
                    # resume
 | 
						|
                    ;;&
 | 
						|
 | 
						|
                *VOLUME*)
 | 
						|
                    run_completion "$@" $cmd "${extra_args[@]}" ""
 | 
						|
                    is "$output" ".*$random_volume_name${nl}" \
 | 
						|
                       "$* $cmd: actual volume listed in suggestions"
 | 
						|
                    _check_completion_end NoFileComp
 | 
						|
 | 
						|
                    match=true
 | 
						|
                    # resume
 | 
						|
                    ;;&
 | 
						|
 | 
						|
                *REGISTRY*)
 | 
						|
                    run_completion "$@" $cmd "${extra_args[@]}" ""
 | 
						|
                    _check_completion_end NoFileComp
 | 
						|
                    assert "${#lines[@]}" -gt 2 "$* $cmd: No REGISTRIES found in suggestions"
 | 
						|
                    # We can assume quay.io as we force our own CONTAINERS_REGISTRIES_CONF below.
 | 
						|
                    assert "${lines[0]}" == "quay.io" "unqualified-search-registries from registries.conf listed"
 | 
						|
 | 
						|
                    match=true
 | 
						|
                    # resume
 | 
						|
                    ;;&
 | 
						|
 | 
						|
                *SECRET*)
 | 
						|
                    run_completion "$@" $cmd "${extra_args[@]}" ""
 | 
						|
                    is "$output" ".*$random_secret_name${nl}" \
 | 
						|
                       "$* $cmd: actual secret listed in suggestions"
 | 
						|
                    _check_completion_end NoFileComp
 | 
						|
 | 
						|
                    match=true
 | 
						|
                    # resume
 | 
						|
                    ;;&
 | 
						|
 | 
						|
                *PATH* | *CONTEXT* | *FILE* | *COMMAND* | *ARG...* | *URI*)
 | 
						|
                    # default shell completion should be done for everything which accepts a path
 | 
						|
                    run_completion "$@" $cmd "${extra_args[@]}" ""
 | 
						|
 | 
						|
                    # cp is a special case it returns ShellCompDirectiveNoSpace
 | 
						|
                    if [[ "$cmd" == "cp" ]]; then
 | 
						|
                        _check_completion_end NoSpace
 | 
						|
                    else
 | 
						|
                        _check_completion_end Default
 | 
						|
                        _check_no_suggestions
 | 
						|
                    fi
 | 
						|
                    ;;
 | 
						|
 | 
						|
                *)
 | 
						|
                    if [[ "$match" == "false" ]]; then
 | 
						|
                        dprint "UNKNOWN arg: $arg for $command_string ${extra_args[*]}"
 | 
						|
                    fi
 | 
						|
                    ;;
 | 
						|
 | 
						|
                esac
 | 
						|
 | 
						|
                # Increment the argument array
 | 
						|
                extra_args+=("arg")
 | 
						|
 | 
						|
                i=$(($i + 1))
 | 
						|
                # If the argument ends with ...] than we accept 0...n args
 | 
						|
                # Loop two times to make sure we are not only completing the first arg
 | 
						|
                if [[ ! ${arg} =~ "..." ]] || [[ i -gt 1 ]]; then
 | 
						|
                    break
 | 
						|
                fi
 | 
						|
 | 
						|
            done
 | 
						|
 | 
						|
        done
 | 
						|
 | 
						|
        # If the command takes no more parameters make sure we are getting no completion
 | 
						|
        if [[ ! ${args##* } =~ "..." ]]; then
 | 
						|
            run_completion "$@" $cmd "${extra_args[@]}" ""
 | 
						|
            _check_completion_end NoFileComp
 | 
						|
            _check_no_suggestions
 | 
						|
        fi
 | 
						|
 | 
						|
    done
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
# run the completion cmd
 | 
						|
function run_completion() {
 | 
						|
    PODMAN="$PODMAN_COMPLETION" run_podman "$@"
 | 
						|
}
 | 
						|
 | 
						|
# check for the given ShellCompDirective (always last line)
 | 
						|
function _check_completion_end() {
 | 
						|
    is "${lines[-1]}" "Completion ended with directive: ShellCompDirective$1" "Completion has wrong ShellCompDirective set"
 | 
						|
}
 | 
						|
 | 
						|
# Check that there are no suggestions in the output.
 | 
						|
# We could only check stdout and not stderr but this is not possible with bats.
 | 
						|
# By default we always have two extra lines at the end for the ShellCompDirective.
 | 
						|
# Then we could also have other extra lines for debugging, they will always start
 | 
						|
# with [Debug], e.g. `[Debug] [Error] no container with name or ID "t12" found: no such container`.
 | 
						|
function _check_no_suggestions() {
 | 
						|
    if [ ${#lines[@]} -gt 2 ]; then
 | 
						|
        # Checking for line count is not enough since we may include additional debug output.
 | 
						|
        # Lines starting with [Debug] are allowed.
 | 
						|
        local i=0
 | 
						|
        length=$((${#lines[@]} - 2))
 | 
						|
        while [[ i -lt length ]]; do
 | 
						|
            assert "${lines[$i]:0:7}" == "[Debug]"  "Unexpected non-Debug output line: ${lines[$i]}"
 | 
						|
            i=$((i + 1))
 | 
						|
        done
 | 
						|
    fi
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
# bats test_tags=ci:parallel
 | 
						|
@test "podman shell completion test" {
 | 
						|
 | 
						|
    random_container_name="c-$(safename)"
 | 
						|
    random_pod_name="p-$(safename)"
 | 
						|
    random_image_name="i-$(safename)"
 | 
						|
    random_image_tag=$(random_string 5)
 | 
						|
    random_network_name="n-$(safename)"
 | 
						|
    # Do not change the suffix, it is special debug for #23913
 | 
						|
    random_volume_name="v-$(safename)-ebpf-debug-23913"
 | 
						|
    random_secret_name="s-$(safename)"
 | 
						|
    random_secret_content=$(random_string 30)
 | 
						|
    secret_file=$PODMAN_TMPDIR/$(random_string 10)
 | 
						|
 | 
						|
    echo $random_secret_content > $secret_file
 | 
						|
 | 
						|
    # create a container for each state since some commands are only suggesting running container for example
 | 
						|
    run_podman create --name created-$random_container_name $IMAGE
 | 
						|
    run_podman run --name running-$random_container_name -d $IMAGE top
 | 
						|
    run_podman run --name pause-$random_container_name -d $IMAGE top
 | 
						|
    if _can_pause; then
 | 
						|
        run_podman pause pause-$random_container_name
 | 
						|
    fi
 | 
						|
    run_podman run --name exited-$random_container_name -d $IMAGE echo exited
 | 
						|
 | 
						|
    # create pods for each state
 | 
						|
    run_podman pod create --name created-$random_pod_name
 | 
						|
    run_podman pod create --name running-$random_pod_name
 | 
						|
    run_podman pod create --name degraded-$random_pod_name
 | 
						|
    run_podman pod create --name exited-$random_pod_name
 | 
						|
    run_podman run -d --name running-$random_pod_name-con --pod running-$random_pod_name $IMAGE top
 | 
						|
    run_podman run -d --name degraded-$random_pod_name-con --pod degraded-$random_pod_name $IMAGE echo degraded
 | 
						|
    run_podman run -d --name exited-$random_pod_name-con --pod exited-$random_pod_name $IMAGE echo exited
 | 
						|
    run_podman pod stop exited-$random_pod_name
 | 
						|
 | 
						|
    # create image name (just tag with new names no need to pull)
 | 
						|
    run_podman image tag $IMAGE $random_image_name:$random_image_tag
 | 
						|
    run_podman image list --format '{{.ID}}' --filter reference=$random_image_name
 | 
						|
    random_image_id="${lines[0]}"
 | 
						|
 | 
						|
    # create network
 | 
						|
    run_podman network create $random_network_name
 | 
						|
 | 
						|
    # create volume
 | 
						|
    run_podman volume create $random_volume_name
 | 
						|
 | 
						|
    # create secret
 | 
						|
    run_podman secret create $random_secret_name $secret_file
 | 
						|
 | 
						|
    # create our own registries.conf so we know what registry is set
 | 
						|
    local CONTAINERS_REGISTRIES_CONF="$PODMAN_TMPDIR/registries.conf"
 | 
						|
    echo 'unqualified-search-registries = ["quay.io"]' > "$CONTAINERS_REGISTRIES_CONF"
 | 
						|
    export CONTAINERS_REGISTRIES_CONF
 | 
						|
 | 
						|
    # Called with no args -- start with 'podman --help'. check_shell_completion() will
 | 
						|
    # recurse for any subcommands.
 | 
						|
    check_shell_completion
 | 
						|
 | 
						|
    # check inspect with format flag
 | 
						|
    run_completion inspect -f "{{."
 | 
						|
    assert "$output" =~ ".*^\{\{\.Args\}\}\$.*" "Defaulting to container type is completed"
 | 
						|
 | 
						|
    run_completion inspect created-$random_container_name -f "{{."
 | 
						|
    assert "$output" =~ ".*^\{\{\.Args\}\}\$.*" "Container type is completed"
 | 
						|
 | 
						|
    run_completion inspect $random_image_name -f "{{."
 | 
						|
    assert "$output" =~ ".*^\{\{\.Digest\}\}\$.*" "Image type is completed"
 | 
						|
 | 
						|
    run_completion inspect $random_volume_name -f "{{."
 | 
						|
    assert "$output" =~ ".*^\{\{\.Anonymous\}\}\$.*" "Volume type is completed"
 | 
						|
 | 
						|
    run_completion inspect created-$random_pod_name -f "{{."
 | 
						|
    assert "$output" =~ ".*^\{\{\.BlkioDeviceReadBps\}\}\$.*" "Pod type is completed"
 | 
						|
 | 
						|
    run_completion inspect $random_network_name -f "{{."
 | 
						|
    assert "$output" =~ ".*^\{\{\.DNSEnabled\}\}\$.*" "Network type is completed"
 | 
						|
 | 
						|
    # cleanup
 | 
						|
    run_podman secret rm $random_secret_name
 | 
						|
    rm -f $secret_file
 | 
						|
 | 
						|
    run_podman volume rm $random_volume_name
 | 
						|
 | 
						|
    run_podman network rm $random_network_name
 | 
						|
 | 
						|
    run_podman image untag $IMAGE $random_image_name:$random_image_tag
 | 
						|
 | 
						|
    for state in created running degraded exited; do
 | 
						|
        run_podman pod rm -t 0 --force $state-$random_pod_name
 | 
						|
    done
 | 
						|
 | 
						|
    for state in created running pause exited; do
 | 
						|
        run_podman rm --force $state-$random_container_name
 | 
						|
    done
 | 
						|
}
 | 
						|
 | 
						|
# bats test_tags=ci:parallel
 | 
						|
@test "podman shell completion for paths in container/image" {
 | 
						|
    skip_if_remote "mounting via remote does not work"
 | 
						|
    for cmd in create run; do
 | 
						|
        run_completion $cmd $IMAGE ""
 | 
						|
        assert "$output" =~ ".*^/etc/\$.*" "etc directory suggested (cmd: podman $cmd)"
 | 
						|
        assert "$output" =~ ".*^/home/\$.*" "home directory suggested (cmd: podman $cmd)"
 | 
						|
        assert "$output" =~ ".*^/root/\$.*" "root directory suggested (cmd: podman $cmd)"
 | 
						|
 | 
						|
        # check completion for subdirectory
 | 
						|
        run_completion $cmd $IMAGE "/etc"
 | 
						|
        # It should be safe to assume the os-release file always exists in $IMAGE
 | 
						|
        assert "$output" =~ ".*^/etc/os-release\$.*" "/etc files suggested (cmd: podman $cmd /etc)"
 | 
						|
        # check completion for partial file name
 | 
						|
        run_completion $cmd $IMAGE "/etc/os-"
 | 
						|
        assert "$output" =~ ".*^/etc/os-release\$.*" "/etc files suggested (cmd: podman $cmd /etc/os-)"
 | 
						|
 | 
						|
        # regression check for https://bugzilla.redhat.com/show_bug.cgi?id=2209809
 | 
						|
        # check for relative directory without slash in path.
 | 
						|
        run_completion $cmd $IMAGE "e"
 | 
						|
        assert "$output" =~ ".*^etc/\$.*" "etc dir suggested (cmd: podman $cmd e)"
 | 
						|
 | 
						|
        # check completion with relative path components
 | 
						|
        # It is important the we will still use the image root and not escape to the host
 | 
						|
        run_completion $cmd $IMAGE "../../"
 | 
						|
        assert "$output" =~ ".*^../../etc/\$.*" "relative etc directory suggested (cmd: podman $cmd ../../)"
 | 
						|
        assert "$output" =~ ".*^../../home/\$.*" "relative home directory suggested (cmd: podman $cmd ../../)"
 | 
						|
    done
 | 
						|
 | 
						|
    ctrname="c-$(safename)"
 | 
						|
    random_file=$(random_string 30)
 | 
						|
    run_podman run --name $ctrname $IMAGE sh -c "touch /tmp/$random_file && touch /tmp/${random_file}2 && mkdir /emptydir"
 | 
						|
 | 
						|
    # check completion for podman cp
 | 
						|
    run_completion cp ""
 | 
						|
    assert "$output" =~ ".*^$ctrname\:\$.*" "podman cp suggest container names"
 | 
						|
 | 
						|
    run_completion cp "$ctrname:"
 | 
						|
    assert "$output" =~ ".*^$ctrname\:/etc/\$.*" "podman cp suggest paths in container"
 | 
						|
 | 
						|
    run_completion cp "$ctrname:/tmp"
 | 
						|
    assert "$output" =~ ".*^$ctrname\:/tmp/$random_file\$.*" "podman cp suggest custom file in container"
 | 
						|
 | 
						|
    run_completion cp "$ctrname:/tmp/$random_file"
 | 
						|
    assert "$output" =~ ".*^$ctrname\:/tmp/$random_file\$.*" "podman cp suggest /tmp/$random_file file in container"
 | 
						|
    assert "$output" =~ ".*^$ctrname\:/tmp/${random_file}2\$.*" "podman cp suggest /tmp/${random_file}2 file in container"
 | 
						|
 | 
						|
    run_completion cp "$ctrname:/emptydir"
 | 
						|
    assert "$output" =~ ".*^$ctrname\:/emptydir/\$.*ShellCompDirectiveNoSpace" "podman cp suggest empty dir with no space directive (:2)"
 | 
						|
 | 
						|
    # cleanup container
 | 
						|
    run_podman rm $ctrname
 | 
						|
}
 |