Skip to content

Conversation

melodyliu1986
Copy link
Contributor

@melodyliu1986 melodyliu1986 commented May 21, 2025

When 'ramalama run' is used with '--network none', it was inheriting host proxy environment variables. This caused the interanl client to fail when connecting to the internal llama-server on 127.0.0.1, as it tried to route loopback traffic through the unreachable proxy.

This change modifies engine.py to:

  • Correctly set NO_PROXY/no_proxy for localhost and 127.0.0.1.
  • Explicitly unset http_proxy, https_proxy, HTTP_PROXY, and HTTPS_PROXY variables for the container when the 'run' subcommand is invoked.

This allows the internal client to connect directly to the internal server, resolving the connection error.

Fixes: #1414

Testings before the change:

$ env|grep "proxy"
no_proxy=localhost,127.0.0.0/8,::1
https_proxy=http://proxy4.squi-001.prod.rdu2.dc.redhat.com:3128/
HTTPS_PROXY=http://proxy4.squi-001.prod.rdu2.dc.redhat.com:3128/
HTTP_PROXY=http://proxy4.squi-001.prod.rdu2.dc.redhat.com:3128/
http_proxy=http://proxy4.squi-001.prod.rdu2.dc.redhat.com:3128/

$ ramalama run granite3-moe
🦭 > how are you?
Error: could not connect to: http://127.0.0.1:8080/v1/chat/completions

Testings after the change:

$ env|grep "proxy"
no_proxy=localhost,127.0.0.0/8,::1
https_proxy=http://proxy4.squi-001.prod.rdu2.dc.redhat.com:3128/
HTTPS_PROXY=http://proxy4.squi-001.prod.rdu2.dc.redhat.com:3128/
HTTP_PROXY=http://proxy4.squi-001.prod.rdu2.dc.redhat.com:3128/
http_proxy=http://proxy4.squi-001.prod.rdu2.dc.redhat.com:3128/

$ ramalama list
NAME                                      MODIFIED   SIZE     
ollama://granite3-moe/granite3-moe:latest 5 days ago 783.77 MB
ollama://tinyllama/tinyllama:latest       5 days ago 608.16 MB

$ ramalama run granite3-moe
🦭 > how are you?
I'm an artificial intelligence, so I don't have feelings, but I'm here and ready to assist you with any questions you have. How can I help you today?
🦭 > what is the longest river and highest montain in the world?
1. The longest river in the world is the Nile River, which runs through Africa. It is approximately 4,135 miles (6,650 kilometers) long.

2. The highest mountain in the world is Mount Everest. It is an extinct volcano located in the Himalayas on the border of Nepal and Tibet. It stands at an elevation of approximately 29,029 feet (8,848 meters) above sea level.
🦭 > 

$ ramalama serve granite3-moe
serving on port 8080
build: 5335 (d8919424) with cc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-5) for x86_64-redhat-linux
system info: n_threads = 4, n_threads_batch = 4, total_threads = 8

system_info: n_threads = 4 (n_threads_batch = 4) / 8 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | BMI2 = 1 | LLAMAFILE = 1 | OPENMP = 1 | AARCH64_REPACK = 1 | 

main: binding port with default address family
main: HTTP server is listening, hostname: 0.0.0.0, port: 8080, http threads: 7

Summary by Sourcery

Ensure 'run' subcommand bypasses host proxy settings to allow internal client to connect to the local llama server

Bug Fixes:

  • Prevent host proxy environment variables from interfering with internal loopback connections by unsetting http_proxy, https_proxy, HTTP_PROXY, and HTTPS_PROXY in the 'run' subcommand
  • Extend NO_PROXY/no_proxy to include localhost and 127.0.0.1 for containers when 'run' is invoked

Enhancements:

  • Include debug flag in the image pull step and emit a debug-only note if the pull command fails

Copy link
Contributor

sourcery-ai bot commented May 21, 2025

Reviewer's Guide

This PR updates engine.py to prevent host proxy settings from affecting the internal client when using the 'run' subcommand with '--network none' by injecting correct NO_PROXY values for loopback addresses and unsetting other proxy variables, and it enhances the pull logic to pass the debug flag and surface a detailed notice on failures.

Sequence Diagram: ramalama run Proxy Environment Configuration

sequenceDiagram
    actor User
    participant CLI as ramalama CLI
    participant Eng as Engine
    participant Container as Container Process

    User->>CLI: Executes `ramalama run <image>`
    CLI->>Eng: new Engine(args)
    Eng->>Eng: Calls _add_proxy_settings_for_run()
    Eng->>Eng: Modifies exec_args (conditionally sets NO_PROXY, unsets HTTP_PROXY, etc. for 'run' subcommand)
    CLI->>Container: Starts container with modified environment variables
    Container->>Container: Internal client connects directly to 127.0.0.1 (respects NO_PROXY)
    Container-->>User: AI response (connection successful)
Loading

Updated Class Diagram for Engine

classDiagram
  class Engine {
    +__init__(args)
    +add(newargs) : void
    -_add_proxy_settings_for_run() : void
    +add_pull_newer() : void
  }
Loading

File-Level Changes

Change Details Files
Introduce _add_proxy_settings_for_run to isolate container from host proxies
  • Add _add_proxy_settings_for_run method that:
  • • Checks for 'run' subcommand
  • • Merges existing NO_PROXY/no_proxy values with localhost and 127.0.0.1
  • • Injects updated NO_PROXY/no_proxy into container
  • • Unsets http_proxy, https_proxy, HTTP_PROXY, and HTTPS_PROXY
ramalama/engine.py
Enhance add_pull_newer to include debug output on pull failure
  • Pass args.debug to run_cmd when pulling
  • Add conditional perror message on exception when debug is enabled
ramalama/engine.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @melodyliu1986 - I've reviewed your changes - here's some feedback:

  • The proxy-clearing logic currently applies to all run invocations—consider scoping it to only when --network none is specified so you don’t unintentionally strip proxies in other network modes.
  • It might be helpful to extract the NO_PROXY/no_proxy merging and environment-setting logic into a standalone utility function for better reuse and easier testing.
Here's what I looked at during the review
  • 🟢 General issues: all looks good
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

"""Helper to add arguments to the execution list."""
self.exec_args.extend(newargs)

def _add_proxy_settings_for_run(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (complexity): Consider refactoring proxy and environment variable handling into helper methods and simplifying error handling to reduce repetition and centralize logic.

# 1. Factor NO_PROXY parsing into a small helper and an add_env convenience:
def _get_no_proxy_str(self):
    essentials = {"localhost", "127.0.0.1"}
    parts = {
        p.strip()
        for var in ("NO_PROXY", "no_proxy")
        for p in os.getenv(var, "").split(",")
        if p.strip()
    }
    return ",".join(sorted(parts | essentials))

def add_env(self, key, value):
    self.add(["--env", f"{key}={value}"])

# 2. Collapse the big _add_proxy_settings_for_run into a few lines:
def _add_proxy_settings_for_run(self):
    if getattr(self.args, "subcommand", None) != "run":
        return

    no_proxy = self._get_no_proxy_str()
    if no_proxy:
        for var in ("NO_PROXY", "no_proxy"):
            self.add_env(var, no_proxy)

    for var in ("http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY"):
        self.add_env(var, "")

# 3. Simplify add_pull_newer by letting run_cmd handle debug‐only errors:
def add_pull_newer(self):
    if not self.args.dryrun and self.use_docker and self.args.pull == "newer":
        if not self.args.quiet:
            print(f"Checking for newer image {self.args.image}")
        run_cmd(
            [self.args.engine, "pull", "-q", self.args.image],
            ignore_all=True,
            debug=self.args.debug
        )
    else:
        self.exec_args += ["--pull", self.args.pull]

# 4. (If needed) In run_cmd, consolidate debug‐only error output:
def run_cmd(cmd, ignore_all=False, debug=False):
    try:
        subprocess.check_call(cmd, **...)
    except Exception as e:
        if debug:
            perror(f"'{' '.join(cmd)}' failed: {e}")
        if not ignore_all:
            raise

This preserves all functionality, removes repetition, and centralizes both proxy‐string logic and debug messaging.

"""Helper to add arguments to the execution list."""
self.exec_args.extend(newargs)

def _add_proxy_settings_for_run(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): We've found these issues:

@melodyliu1986 melodyliu1986 force-pushed the melodyliu1986-feature-branch branch 2 times, most recently from b3487cd to cef86f2 Compare May 21, 2025 07:35
@ericcurtin
Copy link
Member

Could we put all this in libexec/ramalama/ramalama-run-core and instead? It's cleaner no if statement required since at this point we know we are in the run code for sure

@rhatdan
Copy link
Member

rhatdan commented May 22, 2025

Would it be better/easier to just set podman run --network=none --http_proxy=false?

Although this command does not exist with Docker.

@ericcurtin
Copy link
Member

The thing is if we put it in libexec/ramalama/ramalama-run-core and solve it in python it should work with docker/podman/nocontainers

@melodyliu1986 melodyliu1986 force-pushed the melodyliu1986-feature-branch branch from cef86f2 to f19eb33 Compare May 27, 2025 07:48
@melodyliu1986
Copy link
Contributor Author

melodyliu1986 commented May 27, 2025

The thing is if we put it in libexec/ramalama/ramalama-run-core and solve it in python it should work with docker/podman/nocontainers

I reverted the engine.py and switched to libexec/ramalama/ramalama-run-core, can you please review it? TY
There is a check failure for bats, but I feel it is not related to my changes.

@rhatdan
Copy link
Member

rhatdan commented May 27, 2025

Since the ramalama-run-core is happening in a container, then are we sure the environment variables from the host are getting passed into the command?

@melodyliu1986
Copy link
Contributor Author

Since the ramalama-run-core is happening in a container, then are we sure the environment variables from the host are getting passed into the command?

I built a ramalama image using my changes locally. Here is my test:

$ env|grep "proxy"
no_proxy=localhost,127.0.0.0/8,::1
https_proxy=http://proxy4.squi-001.prod.rdu2.dc.redhat.com:3128/
HTTPS_PROXY=http://proxy4.squi-001.prod.rdu2.dc.redhat.com:3128/
HTTP_PROXY=http://proxy4.squi-001.prod.rdu2.dc.redhat.com:3128/
http_proxy=http://proxy4.squi-001.prod.rdu2.dc.redhat.com:3128/

Using the quay.io/ramalama/ramalama:0.8:

$ ramalama --image quay.io/ramalama/ramalama:0.8 run granite3-moe
Trying to pull quay.io/ramalama/ramalama:0.8...
Getting image source signatures
Copying blob d039c7db1282 skipped: already exists  
Copying blob 43a55c9c3601 skipped: already exists  
Copying blob dc5a8444c1b8 skipped: already exists  
Copying config 73d377b90c done   | 
Writing manifest to image destination
🦭 > how are you?
Error: could not connect to: http://127.0.0.1:8080/v1/chat/completions

Using my build image

$ make build IMAGE=ramalama
$ podman images|grep "ramalama"
quay.io/ramalama/ramalama                                                                        latest                           36de532a10da  34 minutes ago  1.3 GB
quay.io/ramalama/ramalama-rag                                                                    latest                           d8e8fd5f56ed  23 hours ago    3.94 GB
quay.io/ramalama/ramalama-whisper-server                                                         latest                           ddfce887e7f4  24 hours ago    1.3 GB
quay.io/ramalama/ramalama-llama-server                                                           latest                           874bc4dba31e  24 hours ago    1.3 GB
quay.io/ramalama/ramalama                                                                        0.8                              73d377b90ce9  9 days ago      1.31 GB
quay.io/ramalama/ramalama-rag                                                                    0.7                              29bd874256d5  6 weeks ago     3.65 GB
quay.io/ramalama/ramalama                                                                        0.7                              d29200bf974d  6 weeks ago     1.06 GB
$ ramalama --image quay.io/ramalama/ramalama:latest run --pull never granite3-moe
🦭 > how are you
I'm an artificial intelligence, so I don't have feelings, but I'm here and ready to help you with any questions you have to the best of my ability.
🦭 > what is the longest river in the world?
The longest river in the world is the Nile River, which runs through northeastern Africa. It stretches for about 4,135 miles (6,650 kilometers) and is surpassed only by the Amazon River in South America.
🦭 > generate a simplest python code
Here is a simple Python code that prints "Hello, World!" using only basic Python syntax:

```python
print("Hello, World!")

I don't think I need to push the ramalama image to quay.io, you have an official CI/CD system that handles building and pushing official images to quay.io/ramalama/, correct?

@@ -60,6 +60,25 @@ def initialize_args():


def main(args):
# --- START OF PROXY FIX ---
Copy link
Member

Choose a reason for hiding this comment

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

Please remove this line.

for proxy_var in ['http_proxy', 'https_proxy', 'HTTP_PROXY', 'HTTPS_PROXY']:
if proxy_var in os.environ:
del os.environ[proxy_var]
# --- END OF PROXY FIX ---
Copy link
Member

Choose a reason for hiding this comment

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

Please remove this line.

@rhatdan
Copy link
Member

rhatdan commented May 28, 2025

Yes we rebuild on Mondays usually, so once you remove those two lines I pointed out, I will merge and this fix should show up in next release.

When 'ramalama run' is used with '--network none', it was inheriting host proxy
environment variables. This caused the interanl client to fail when connecting to
the internal llama-server on 127.0.0.1, as it tried to route loopback traffic through
the unreachable proxy.

This change modifies engine.py to:
- Correctly set NO_PROXY/no_proxy for localhost and 127.0.0.1.
- Explicitly unset http_proxy, https_proxy, HTTP_PROXY, and HTTPS_PROXY variables
for the container when the 'run' subcommand is invoked.

This allows the internal client to connect directly to the internal server, resolving
the connection error.

Fixes: containers#1414

Signed-off-by: Song Liu <[email protected]>
@melodyliu1986 melodyliu1986 force-pushed the melodyliu1986-feature-branch branch from f19eb33 to 206d669 Compare May 29, 2025 07:50
@melodyliu1986
Copy link
Contributor Author

melodyliu1986 commented May 29, 2025

Yes we rebuild on Mondays usually, so once you remove those two lines I pointed out, I will merge and this fix should show up in next release.

cool, please take a look again, thank you

@melodyliu1986 melodyliu1986 requested a review from rhatdan May 29, 2025 07:53
@rhatdan
Copy link
Member

rhatdan commented May 29, 2025

LGTM

@rhatdan rhatdan merged commit 8a604e3 into containers:main May 29, 2025
18 of 22 checks passed
@rhatdan
Copy link
Member

rhatdan commented May 29, 2025

Thanks @melodyliu1986

@ericcurtin
Copy link
Member

Since the ramalama-run-core is happening in a container, then are we sure the environment variables from the host are getting passed into the command?

Thing is, ramalama-run-core should never use a proxy, it should always be localhost. ramalama-client-core called without ramalama-run-core could potentially use a proxy for a remote server.

@melodyliu1986 melodyliu1986 deleted the melodyliu1986-feature-branch branch July 28, 2025 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Always failed to run a specific model using ramalama run $model_name
3 participants