> ## Environment utilities

> Fetch the complete documentation index at: https://antithesis.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

---

The **Antithesis environment** is the virtual system where we run your containers -- it's the *universe* in some branch of the testing *multiverse*.

> **Note**
>
> You can read more about the specifics of the system in our [Antithesis environment documentation](/docs/configuration/the_antithesis_environment/), but for our purposes, you can think of it as a Linux system that's been customized for testing.

Inside the multiverse debugging notebook, you can use the [`environment` object](#the-environment-object) to access the following features that relate to the Antithesis environment:

- [getting system logs](#getting-system-logs) with environment event sets
- [listing containers](#listing-containers)
- [extracting files](#extracting-files) to view locally
- [injecting files](#injecting-files) in a container after it starts
- [configuring fault injection](#configuring-fault-injection)
- [profiling CPU usage](#profiling-cpu-usage)

You can also [run commands](#running-commands) on your containers with [bash fragments](#bash-fragments).

## The environment object

Inside a multiverse debugging session, the `environment` object represents the system as a whole. In each [moment](/docs/product/debugging/advanced_multiverse_debugging/moment_branch/), the Antithesis environment is in some state. There are probably containers running your software, there may be Antithesis faults in progress, and there are certainly some background services supporting the system.

When you begin your session, the `environment` object is already defined for you:

```js
// `environment` here is your environment
[environment, moment] = prepare_multiverse_debugging()
```

As a rule of thumb, you'll typically use the environment when you're inspecting and configuring the system as a whole.

### Getting system logs

Sometimes, when you're diagnosing a problem, it's helpful to see what's going on with the system as a whole. System logs are available through the `environment` object as curated [event sets](/docs/product/debugging/advanced_multiverse_debugging/event_sets/).

> **Note**
>
> When events are the result of readable (non-binary) program output, an event is sent each time the output contains a newline character.

Provided event sets include:

- `environment.containers.events`: output from all of the containers
- `environment.containers.meta_events`: changes to containers (create, start, stop, etc) as seen by the container management system
- `environment.sdk.all_events`: all of the events that have been processed through the [Antithesis SDK](/docs/reference/sdk/)
- `environment.sdk.assertions`: assertions defined with the [Antithesis SDK](/docs/reference/sdk/) which have been encountered
- `environment.events`: the main event source, including the sources above
- `environment.fault_injector.events`: high level information about the status of the fault injector
- `environment.fault_injector.faults`: information about each of the discrete faults introduced by the fault injector (this does not include background chaos like thread interleavings or baseline packet drop rates)
- `environment.background_monitor.events`: raw output from the Antithesis system utilization monitor (Warning: this is a new feature and its format may be unusually unstable!)
- `environment.journal.events` : the main logger on the system, including for various daemons. It can be verbose, but it is the right place to notice (for instance) the system OOM-killer. (read more at [man journald](https://man7.org/linux/man-pages/man5/journald.conf.5.html))

### Listing containers

> **Warning**
>
> Container commands currently do not work if you are using Kubernetes. You can use `kubectl` to interact with the kubernetes cluster:
>
> ```js
> print(bash`kubectl get pod`.run({branch: guest_branch, container: env.host}))
> ```

You can list all of your containers as of some moment.

```js
[environment, moment] = prepare_multiverse_debugging()
print(environment.containers.list({moment}))
```

```js
[
    {
        name: "myworkload",
        id: "5bf984f4b13cdb1aad334c80113d72b8b480984c4ae3d98da988c70975ca4af8",
        state: "running",
        image: "us-central1-docker.pkg.dev/molten-verve-216720/my-company-repository/my_db:latest",
        image_id: "20a003596f19ddcd16bd1c3ce30e0c3dedda40f959ead53e46a6e45e3452fa18"
    }
]
```

### Extracting files

Sometimes, the best tool for inspecting a file is one you have locally. In cases like that, you can extract the file and download it.

```js
// To extract a file, use the container name and the path in the container
link = environment.extract_file({moment, path: "/run/myservice.db", container: "myworkload"})

// You can also extract a file from an image if you need to.
link = environment.extract_file({moment, path: "/run/myservice.db", image: "my_db"})

// Print the result to get a download link
print(link)
```

[Download myservice.log (808712 bytes)](https://youtu.be/dQw4w9WgXcQ)

### Injecting files

Sometimes, you may want to inject a file in your container after it's started. In cases like that, use `inject_file()`.

```js
    // To inject a file, use the container name, the path in the container, and the file_data you want to inject.
    stat = environment.inject_file({
                        branch,
                        path: "/run/testing.sh",
                        container: "mycontainer",
                        file_data: bash`echo hello`,
                        permissions: "0755"
                    })

    // Print the result to see the stat results
    print(stat)
```

### Configuring fault injection

If you're exploring a moment from the middle of a test, Antithesis might have been disrupting the network or other containers. If you want to stop these faults, either to see whether your software recovers or to make other debugging clearer, you can use `environment.fault_injector.pause`, and if you want to resume, you can use `environment.fault_injector.unpause`.

### Profiling CPU usage

The Environment includes a CPU profiler, which is a powerful way to investigate what's happening during some part of a branch. Here's what it looks like to use the profiler.

```js
// Start the profiler on some branch
background_profiler = environment.profiler.start({branch})

// You can optionally supply a PID if you want to look at a particular process.
// background_profiler = environment.profiler.start({branch, pid: 1})

// Advance time on the branch
// Note that instead of waiting, you could run commands here.
// This is especially helpful for investigating the performance of a series of commands
branch.wait(Time.seconds(10))

// Stop the profiler on the branch
environment.profiler.stop({branch, background_profiler})

// View the results as of the end of the branch
print(environment.profiler.report({moment: branch.end}))
```

## Running commands

To run a command in one of your containers, you can use a [bash fragment](#bash-fragments).

Commands return a [process object](#process-objects).

> **Note**
>
> Since we know that many lightweight containers don't include bash, Antithesis mounts a shell and assorted shell tools into every container at runtime, making it possible to run bash commands even in minimal containers.

There are two main ways to run a bash fragment: in the foreground and the background. Either way, you need to supply a [container](#listing-containers), so we know where to run a command, and a [branch](/docs/product/debugging/advanced_multiverse_debugging/moment_branch/), so we know when in the multiverse to run it. Running a bash command will return a [process](#process-objects) object.

### Running a command with `.run()`

Most of the time, when you want to run a command, you want to run it to completion. If you call `.run()` on a fragment, it will run until it finishes, advancing the branch until the command has exited.

If the command *doesn't* exit, `.run()` will fail after 30 virtual minutes have passed. You can supply an optional timeout to change how long it waits.

```js
print(
  bash`echo 'hello world!'`.run({
    container: "myworkload",
    timeout: Time.seconds(2),
    branch
  })
)
```

```sh
78.513  info    hello world
```

### Starting a command with `.run_in_background()`

Sometimes, you want to start a long-running command and *not* wait for it to complete. When that happens, you can use `.run_in_background()`. Unlike `.run()`, `.run_in_background()` only advances time on the branch until after the command has been *delivered*. It might not even be started until time has advanced on the branch!

If you print the result, you'll *still* see the output as it comes in (see the [process object notes](#process-objects) for more of an explanation).

```js
print(bash`while true ; do du -hs /* && echo "" ; sleep 2 ; done`.run_in_background({container: "myworkload", branch}))
branch.wait(Time.seconds(10))
```

```sh
73.938  info    17404	/
73.938  info
76.111  info    17408	/
76.111  info
78.283  info    17413	/
78.283  info
80.455  info    17413	/
80.455  info
82.626  info    17420	/
82.627  info
```

### Bash fragments

The multiverse debugging environment supports running shell commands with *bash fragments*. A bash fragment is a [JavaScript tagged template literal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) containing the command that you want to run.

```js
bash`echo 'hello world!'`
```

Like regular JavaScript templates, you can interpolate, but bash fragments escape intelligently.

```js
s = 'This is a multicharacter string'
print(bash`echo ${s}`)
```

```js
bash`echo "This is a multicharacter string"`
```

```js
b = bash`ls /`
print(bash`MY_VARIABLE="foo" ${b}`)
// equivalent to: bash`MY_VARIABLE="foo" ls /`
```

```js
b = bash`ls /`
bash`MY_VARIABLE="foo" ls /`
```

### Process objects

When you run a command in our environment, the result is a **process** object representing the execution of that process on the branch.

You can print a process to view its output.

```js
proc = bash`ps`.run({container: "myworkload", branch})
print(proc)
```

```sh
73.446  info    Every 2.0s: ps
                2024-08-19 19:57:56
73.446  info
73.447  info    PID     USER    TIME    COMMAND
73.447  info      1     root    0:00    sleep infinity
73.447  info      2     root    0:00    watch ps
73.447  info      4     root    0:00    timeout 10 watch ps
73.447  info      5     root    0:00    ps
```

You can also call `help(proc)` for more details on what you can do with a **process** object.

#### Highlights

- Process-specific [event sets](/docs/product/debugging/advanced_multiverse_debugging/event_sets/), including `proc.events`, `proc.stdout`, `proc.stderr`, and `proc.exits`.
- `proc.pid` is the PID of the process inside our virtual system.
- `proc.exited_by(moment)` will return `true` if the process existed in the branch that ends with the supplied moment, and had exited.
- `proc.exit_code` is the exit code that the process returned.

#### Background processes

Whether you use `.run()` or `.run_in_background()`, the **process** will have the same shape, but the behavior may be slightly different.

In particular, when you get a **Process** object from `.run_in_background()`, its properties will be true as of the *current* state of the branch it was run on.

- `proc.exit_code` will be `undefined` if the process hasn't returned yet.
- `proc.pid` will be `undefined` if the process hasn't forked yet.

*However*, when you `print()` a **process** that results from `.run_in_background()`, you will see the events associated with the process on the branch that it was constructed from, *as though the print were written below the last update to the branch in the notebook.*
