EnvInfo returns information about the environment of a container.
(ctx context.Context, execer agentexec.Execer, container, containerUser string)
| 36 | |
| 37 | // EnvInfo returns information about the environment of a container. |
| 38 | func EnvInfo(ctx context.Context, execer agentexec.Execer, container, containerUser string) (*DockerEnvInfoer, error) { |
| 39 | var dei DockerEnvInfoer |
| 40 | dei.container = container |
| 41 | |
| 42 | if containerUser == "" { |
| 43 | // Get the "default" user of the container if no user is specified. |
| 44 | // TODO: handle different container runtimes. |
| 45 | cmd, args := wrapDockerExec(container, "", "whoami") |
| 46 | stdout, stderr, err := run(ctx, execer, cmd, args...) |
| 47 | if err != nil { |
| 48 | return nil, xerrors.Errorf("get container user: run whoami: %w: %s", err, stderr) |
| 49 | } |
| 50 | if len(stdout) == 0 { |
| 51 | return nil, xerrors.Errorf("get container user: run whoami: empty output") |
| 52 | } |
| 53 | containerUser = stdout |
| 54 | } |
| 55 | // Now that we know the username, get the required info from the container. |
| 56 | // We can't assume the presence of `getent` so we'll just have to sniff /etc/passwd. |
| 57 | cmd, args := wrapDockerExec(container, containerUser, "cat", "/etc/passwd") |
| 58 | stdout, stderr, err := run(ctx, execer, cmd, args...) |
| 59 | if err != nil { |
| 60 | return nil, xerrors.Errorf("get container user: read /etc/passwd: %w: %q", err, stderr) |
| 61 | } |
| 62 | |
| 63 | scanner := bufio.NewScanner(strings.NewReader(stdout)) |
| 64 | var foundLine string |
| 65 | for scanner.Scan() { |
| 66 | line := strings.TrimSpace(scanner.Text()) |
| 67 | if !strings.HasPrefix(line, containerUser+":") { |
| 68 | continue |
| 69 | } |
| 70 | foundLine = line |
| 71 | break |
| 72 | } |
| 73 | if err := scanner.Err(); err != nil { |
| 74 | return nil, xerrors.Errorf("get container user: scan /etc/passwd: %w", err) |
| 75 | } |
| 76 | if foundLine == "" { |
| 77 | return nil, xerrors.Errorf("get container user: no matching entry for %q found in /etc/passwd", containerUser) |
| 78 | } |
| 79 | |
| 80 | // Parse the output of /etc/passwd. It looks like this: |
| 81 | // postgres:x:999:999::/var/lib/postgresql:/bin/bash |
| 82 | passwdFields := strings.Split(foundLine, ":") |
| 83 | if len(passwdFields) != 7 { |
| 84 | return nil, xerrors.Errorf("get container user: invalid line in /etc/passwd: %q", foundLine) |
| 85 | } |
| 86 | |
| 87 | // The fifth entry in /etc/passwd contains GECOS information, which is a |
| 88 | // comma-separated list of fields. The first field is the user's full name. |
| 89 | gecos := strings.Split(passwdFields[4], ",") |
| 90 | fullName := "" |
| 91 | if len(gecos) > 1 { |
| 92 | fullName = gecos[0] |
| 93 | } |
| 94 | |
| 95 | dei.user = &user.User{ |