Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TERM iff TERM not defined in container when podman exec -t #20357

Merged
merged 1 commit into from
Oct 18, 2023

Conversation

rhatdan
Copy link
Member

@rhatdan rhatdan commented Oct 13, 2023

Fixes: #20334

Does this PR introduce a user-facing change?

Podman exec -t will define TERM environment variable even if container is not running with a terminal.

@openshift-ci openshift-ci bot added release-note approved Indicates a PR has been approved by an approver from all required OWNERS files. labels Oct 13, 2023
@github-actions github-actions bot added the kind/api-change Change to remote API; merits scrutiny label Oct 13, 2023
@rhatdan
Copy link
Member Author

rhatdan commented Oct 13, 2023

@edsantiago Not sure what is going on, but the out put of env from inside of container seems to have a ^M associated with it, not sure how to remove it nicely.

@@ -713,6 +713,13 @@ func (c *Container) LinuxResources() *spec.LinuxResources {
return nil
}

func (c *Container) Env() []string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a doc comment.

@@ -1219,3 +1219,28 @@ func ConvertTimeout(timeout int) uint {
}
return uint(timeout)
}

func ExecAddTERM(useTTY bool, existingEnv []string, execEnvs map[string]string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there's only one caller and no uni tests, can we move it directly to pkg/domain/infra/abi/containers.go?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two callers, one from api/handlers an one from domain/infra/abi


@test "podman exec --tty" {
run_podman run -d --name test $IMAGE top
run_podman 1 exec test printenv echo $TERM
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The printenv echo $TERM confuses me. Do you want to echo or printenv?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not only that, the echo $TERM (without single quotes) will interpret $TERM in the context of the running shell (BATS), not the container. This should be printenv TERM, as it is below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this was a leak, attempting to get this, should all be printenv TERM.

fi
run_podman rm -f -t 0 test

run_podman run -t -d --name test $IMAGE top
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire block looks a duplicate of the previous one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First one is a container run without a -t
Second one is a container run with -t
Third is container run with -t --env TERM=banana

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Way too many confusing combinations. Might I make a suggestion?

@test "podman exec --tty" {
    # Outer loops: different variations on the RUN container
    for run_opt_t in "" "-t"; do
        for run_term_env in "" "explicit_RUN_term"; do
            local run_opt_env=
            if [[ -n "$run_term_env" ]]; then
                run_opt_env="--env=TERM=$run_term_env"
            fi
            run_podman run -d $run_opt_t $run_opt_env --name test $IMAGE top

            # Inner loops: different variations on EXEC
            for exec_opt_t in "" "-t"; do
                for exec_term_env in "" "explicit_EXEC_term"; do
                    # What to expect.
                    local expected=
                    # "exec -t" defaults to current $TERM
                    if [[ -n "$exec_opt_t" ]]; then
                        expected="$TERM"
                    fi
                    # ...unless "run -t", which forces xterm (always xterm)
                    if [[ -n "$run_opt_t" ]]; then
                        expected="xterm"
                    fi
                    # ...unless overridden by explicit --env
                    if [[ -n "$run_term_env$exec_term_env" ]]; then
                        # (exec overrides run)
                        expected="${exec_term_env:-$run_term_env}"
                    fi

                    local exec_opt_env=
                    if [[ -n "$exec_term_env" ]]; then
                        exec_opt_env="--env=TERM=$exec_term_env"
                    fi

                    run_podman exec $exec_opt_t $exec_opt_env test sh -c 'echo -n $TERM'
                    assert "$output" = "$expected" "run $run_opt_t $run_opt_env, exec $exec_opt_t $exec_opt_env"
                done
            done

            run_podman rm -f -t0 test
        done
    done

This very clearly demonstrates what I consider a confusing aspect of run -t with exec -t. If it's deliberate, fine, but to me it violates POLA that exec -t depends on whether the container was run with/without -t.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine, it does loose the test to make sure TERM is not set in the non run -t and non exec -t case, since theoretically TERM="" versus TERM not set. But really not important.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be fairly easy to fix:

    local expect_rc=0
    if [[ -z "$expect_output" ]]; then
         expect_rc=1
    fi
    ...
    run_podman $expect_rc exec [...] printenv TERM

...but that then requires defining cr=$'\r' and requiring it in the -t cases. Which I think might be fine, but it makes the test more complicated, and the loop is already too unwieldy. LMK if you'd like the full \r set of changes.

@@ -58,6 +59,7 @@ func ExecCreateHandler(w http.ResponseWriter, r *http.Request) {
libpodConfig.WorkDir = input.WorkingDir
libpodConfig.Privileged = input.Privileged
libpodConfig.User = input.User
util.ExecAddTERM(input.Tty && !ctr.Terminal(), ctr.Env(), libpodConfig.Environment)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why this should ever depend on ctr.Terminal()? Either exec is started with tty or not, I don't see why the container option matters in this case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you are right, originally I was checking that to look for TERM, but now, I just can use TERM if it is defined within the container and only force it if it is not.

Copy link
Member

@edsantiago edsantiago left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^M is the result of -t. For a tighter test, I recommend:

    cr=$'\r'
    ...
    assert "$output" = "whatever$cr" "..."


@test "podman exec --tty" {
run_podman run -d --name test $IMAGE top
run_podman 1 exec test printenv echo $TERM
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not only that, the echo $TERM (without single quotes) will interpret $TERM in the context of the running shell (BATS), not the container. This should be printenv TERM, as it is below.

@rhatdan rhatdan force-pushed the TERM branch 2 times, most recently from 3947965 to 25bf9c1 Compare October 16, 2023 18:15
@rhatdan
Copy link
Member Author

rhatdan commented Oct 16, 2023

@edsantiago it is blowing up with an unexpected terminal name dumb?

@edsantiago
Copy link
Member

Argh! I can reproduce via env -u TERM hack/bats --rootless 075:tty </dev/null. (I had only tested via TERM= hack/bats, which is obviously not the same). Now looking for a fix.

@edsantiago
Copy link
Member

This fixes the tests..... but only for local podman. For remote, podman run (no opts) with exec -t gets the podman-system-service $TERM, not the invoking client's. My gut tells me that's wrong, but it's too late in my day right now for me to fully think it through.

diff --git a/test/system/075-exec.bats b/test/system/075-exec.bats
index ec7d59536..324c9bbfe 100644
--- a/test/system/075-exec.bats
+++ b/test/system/075-exec.bats
@@ -177,3 +177,3 @@ load helpers
             fi
-            run_podman run -d $run_opt_t $run_opt_env --name test $IMAGE top
+            TERM=run-term run_podman run -d $run_opt_t $run_opt_env --name test $IMAGE top
 
@@ -184,5 +184,5 @@ load helpers
                     local expected=
-                    # "exec -t" defaults to current $TERM
+                    # "exec -t" defaults to the process TERM
                     if [[ -n "$exec_opt_t" ]]; then
-                        expected="$TERM"
+                        expected="exec-term"
                     fi
@@ -203,3 +203,3 @@ load helpers
 
-                    run_podman exec $exec_opt_t $exec_opt_env test sh -c 'echo -n $TERM'
+                    TERM=exec-term run_podman exec $exec_opt_t $exec_opt_env test sh -c 'echo -n $TERM'
                     assert "$output" = "$expected" "run $run_opt_t $run_opt_env, exec $exec_opt_t $exec_opt_env"

Note that this does not test the env -u TERM run_podman case, because we can't do that. Can't do env with run_podman. This keep getting trickier. I'm sorry to have introduced so much hassle, but am slightly glad that we're giving attention to the corner cases.

@edsantiago
Copy link
Member

@rhatdan this is going to fail. I guess I wasn't clear in my comments, sorry.

The podman-remote case uses $TERM on the server. I think this is wrong:

$ TERM=sdfsdf bin/podman system service --timeout=0
$ bin/podman-remote run -d --name foo quay.io/libpod/testimage:20221018 top
<sha>
$ bin/podman-remote exec -t foo printenv TERM
sdfsdf

Note that podman-remote run -t shows xterm, not sdf.

Anyhow, tests will fail until this is resolved. Either skip part of the tests under remote, or fix the underlying code, and I would prefer to fix the underlying code. I think podman-remote exec should use client TERM, not server. But I may be wrong.

@rhatdan
Copy link
Member Author

rhatdan commented Oct 17, 2023

@edsantiago I changed the podman run -t behavior to do a Getenv("TERM") before failing over to "xterm"
Now it should work on --remote.

@edsantiago
Copy link
Member

I'm not sure this is better? It still fails remote (no change), and now I think the exec behavior is worse: exec -t, on a container with run -t, now gets the run TERM, not the exec one. I think that's a step in the opposite direction than we want?

Anyhow, this is going to fail all system tests, not just remote.

@edsantiago
Copy link
Member

My bad. This is actually a very small net gain (run -t now honors TERM) but I still think that exec -t should win. This patch makes it work under podman-local:

diff --git a/test/system/075-exec.bats b/test/system/075-exec.bats
index 324c9bbfe..14f965063 100644
--- a/test/system/075-exec.bats
+++ b/test/system/075-exec.bats
@@ -188,5 +188,5 @@ load helpers
                     fi
-                    # ...unless "run -t", which forces xterm (always xterm)
+                    # ...unless "run -t", which forces the run-process TERM
                     if [[ -n "$run_opt_t" ]]; then
-                        expected="xterm"
+                        expected="run-term"
                     fi

...but remote is still 100% broken and I still lean toward thinking that it should be fixed in the code, not the tests.

What I can do, though, is kludge up the tests so they pass under remote, then sweep the podman-remote-exec problem under the rug for another bug week. Should I do that?

@edsantiago
Copy link
Member

copy-paste this, it will work under local and remote. I've made the warning messages very obnoxious as an incentive to getting that bug fixed (or at least getting the issue resolved one way or another).

@test "podman exec --tty" {
    # Outer loops: different variations on the RUN container
    for run_opt_t in "" "-t"; do
        for run_term_env in "" "explicit_RUN_term"; do
            local run_opt_env=
            if [[ -n "$run_term_env" ]]; then
                run_opt_env="--env=TERM=$run_term_env"
            fi
            TERM=run-term run_podman run -d $run_opt_t $run_opt_env --name test $IMAGE top

            # Inner loops: different variations on EXEC
            for exec_opt_t in "" "-t"; do
                for exec_term_env in "" "explicit_EXEC_term"; do
                    # What to expect.
                    local expected=
                    # "exec -t" defaults to the process TERM
                    if [[ -n "$exec_opt_t" ]]; then
                        expected="exec-term"
                    fi
                    # ...unless "run -t", which forces the run-process TERM
                    if [[ -n "$run_opt_t" ]]; then
                        expected="run-term"
                    fi
                    # ...unless overridden by explicit --env
                    if [[ -n "$run_term_env$exec_term_env" ]]; then
                        # (exec overrides run)
                        expected="${exec_term_env:-$run_term_env}"
                    fi

                    local exec_opt_env=
                    if [[ -n "$exec_term_env" ]]; then
                        exec_opt_env="--env=TERM=$exec_term_env"
                    fi

                    local desc="run $run_opt_t $run_opt_env, exec $exec_opt_t $exec_opt_env"
                    TERM=exec-term run_podman exec $exec_opt_t $exec_opt_env test sh -c 'echo -n $TERM'
                    # FIXME FIXME FIXME! #xxxxx: podman-remote run/exec -t use server TERM, not client
                    if is_remote && [[ -n "$run_opt_t$exec_opt_t" ]] && [[ -z "$run_term_env$exec_term_env" ]]; then
                        echo "# [[ FIXME: podman-remote -t uses server TERM, not client : $desc ]]" >&3
                    else
                        # The normal case
                        assert "$output" = "$expected" "$desc"
                    fi
                done
            done

            run_podman rm -f -t0 test
        done
    done
}

@rhatdan
Copy link
Member Author

rhatdan commented Oct 18, 2023

Switching to TERM on the client side is a potential breaking change, but might make the most sense.

Up til now, exec used TERM from the Container, the change that triggered this PR was that podman run --tty=false, no longer sets a TERM environment variable. This was causing podman exec --tty=true to said terminals to not work correctly without a TERM set.

This PR still takes the TERM from the container if it is set, and then takes if from the client, when it is not set.

Changing to always take TERM from the client for podman exec, might make the most sense, although in the server case, you could have a TERM on the client side that is not understood on the server side. Perhaps a TERM from a Mac or Windows is not understood in linux. (The current code is susceptible to the same issue).

@vrothberg @Luap99 @edsantiago @baude Thoughts?

Copy link
Member

@vrothberg vrothberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK with the change
LGTM

@Luap99
Copy link
Member

Luap99 commented Oct 18, 2023

Why are we looking up the TERM from the host environment exactly? This seems to be a new change made by this PR so I don't see how this effects any kind of backwards compatibility. More the other way around using the TERM env from the environment might be breaking (although I don't see how but I don't really understand how $TERM is used by applications so I wouldn't really comment on that)

I don't see why we should make such change, nobody seems to have asked for that. The old logic is use --env TERM when set otherwise default to xterm. I see no reason to switch the behavior right now, the bug is about exec -t not setting TERM so I don't see why we should just do the same thing as done by podman run right now.

@Luap99
Copy link
Member

Luap99 commented Oct 18, 2023

Also worth pointing out before committing to such changes we should verify what docker does. If docker doesn't read from the environment neither should we IMO.

@edsantiago
Copy link
Member

Why are we looking up the TERM from the host environment exactly?

My fault: I got sick of all the churn on this, and added tests, and my new test found what I consider to be weird behavior. But yeah, I guess we should figure out what docker does and copy it.

@rhatdan
Copy link
Member Author

rhatdan commented Oct 18, 2023

Basically docker sets the TERM=xterm if it not defined in the container image on docker run.
Docker sets the TERM=xterm if TERM is not defined in the container on docker exec.

@edsantiago
Copy link
Member

Something changed in defaultenv, xterm vs xterm-256color.

And, I'm ultra-concerned that we have a brand-new test, exec leak something added in #20394, that is failing hard (not flaking) on podman-remote.

@edsantiago
Copy link
Member

exec something leak, @giuseppe PTAL

Copy link
Member

@edsantiago edsantiago left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have to repush (once #20404 merges) to fix CI anyway, so, some fix requests

for exec_term_env in "" "explicit_EXEC_term"; do
# What to expect.
local expected=
# "exec -t" defaults to the process xterm
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just "xterm", not "the process xterm". (You global-replaced TERM, that yielded some nonsense results)

if [[ -n "$exec_opt_t" ]]; then
expected="xterm"
fi
# ...unless "run -t", which forces the run-process xterm
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here too

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this block is now absurd. It should be collapsed, both ifs should become:

    # if -t is set anywhere, either run or exec, go with xterm
    if [[ -n "$run_opt_t$exec_opt_t" ]]; then


local desc="run $run_opt_t $run_opt_env, exec $exec_opt_t $exec_opt_env"
TERM=exec-term run_podman exec $exec_opt_t $exec_opt_env test sh -c 'echo -n $TERM'
# The normal case
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this comment any more, please remove

@edsantiago
Copy link
Member

20404 (CI fix) is merged. You can now rebase. Please do fix the issues I reported above.

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Oct 18, 2023

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: edsantiago, rhatdan, vrothberg

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:
  • OWNERS [edsantiago,rhatdan,vrothberg]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@edsantiago
Copy link
Member

Flake is new to me. Restarted.

/lgtm
/hold

This has been quite the ordeal. Thank you @rhatdan for your patience with my obnoxious insistence on testing.

@openshift-ci openshift-ci bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Oct 18, 2023
@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Oct 18, 2023
@edsantiago
Copy link
Member

/hold cancel

@openshift-ci openshift-ci bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Oct 18, 2023
@openshift-ci openshift-ci bot merged commit 6863641 into containers:main Oct 18, 2023
98 checks passed
@github-actions github-actions bot added the locked - please file new issue/PR Assist humans wanting to comment on an old issue or PR with locked comments. label Jan 17, 2024
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 17, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. kind/api-change Change to remote API; merits scrutiny lgtm Indicates that a PR is ready to be merged. locked - please file new issue/PR Assist humans wanting to comment on an old issue or PR with locked comments. release-note
Projects
None yet
Development

Successfully merging this pull request may close these issues.

tput: No value for $TERM and no -T specified error when exec -it into container
4 participants