Skip to content

Conversation

@Williangalvani
Copy link
Member

@Williangalvani Williangalvani commented Nov 14, 2025

This allows users to commit the current container state to a new image, to save changes, and then switch the extension to this new image.
In master, the newly commited image gets deleted after the pull fails and we cleanup.

Summary by Sourcery

Enable offline installation of extensions by verifying and reusing locally available Docker images when remote pulls fail, rather than aborting and deleting the image.

New Features:

  • Allow extensions to be installed from locally available Docker images when remote pulls fail

Enhancements:

  • Add a helper method to detect local Docker image availability
  • Modify install error handling to skip cleanup and reuse local image on pull failure

@sourcery-ai
Copy link

sourcery-ai bot commented Nov 14, 2025

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Introduce a local image availability check and adjust the install flow to retain and use user-committed images when a pull fails, instead of always cleaning up.

Sequence diagram for extension install flow with local image fallback

sequenceDiagram
    participant Kraken
    participant Docker
    participant Extension
    Kraken->>Extension: install(clear_remaining_tags, atomic)
    Extension->>Docker: pull_docker_image()
    alt Pull succeeds
        Extension->>Docker: Use pulled image
    else Pull fails
        Extension->>Docker: image_is_available_locally()
        alt Image available locally
            Extension->>Docker: Use local image
        else Image not available
            Extension->>Extension: uninstall()
            Extension->>Extension: enable() (if running_ext)
            Extension->>Kraken: Raise ExtensionPullFailed
        end
    end
Loading

Class diagram for updated Extension install logic

classDiagram
    class Extension {
        +install(clear_remaining_tags: bool, atomic: bool)
        +_pull_docker_image(docker_auth: Optional[str])
        +_image_is_available_locally() bool
    }
    Extension --> DockerCtx
Loading

File-Level Changes

Change Details Files
Add local Docker image availability check
  • Introduce async _image_is_available_locally method
  • Leverage DockerCtx to inspect image
  • Return boolean based on inspection success
core/services/kraken/extension/extension.py
Allow installing from offline image on pull failure
  • Pre-check local image in atomic install failure path
  • Log and bypass uninstall when image exists locally
  • Fallback to original uninstall/enable logic when image missing
core/services/kraken/extension/extension.py

Possibly linked issues

  • #Kraken-Issue-ID: PR prevents extension deletion on pull failure by checking for locally available image, directly addressing issue.

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

@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 there - I've reviewed your changes - here's some feedback:

  • After detecting the image locally, you might still need to call running_ext.enable() so the extension actually gets started instead of silently skipping enable.
  • In _image_is_available_locally, consider catching only the specific 'image not found' exception rather than swallowing all exceptions to avoid hiding other errors.
  • Double-check that no other cleanup path later in the code will delete the newly committed image after a failed pull, to prevent surprising user data loss.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- After detecting the image locally, you might still need to call `running_ext.enable()` so the extension actually gets started instead of silently skipping enable.
- In `_image_is_available_locally`, consider catching only the specific 'image not found' exception rather than swallowing all exceptions to avoid hiding other errors.
- Double-check that no other cleanup path later in the code will delete the newly committed image after a failed pull, to prevent surprising user data loss.

## Individual Comments

### Comment 1
<location> `core/services/kraken/extension/extension.py:180-187` </location>
<code_context>
         docker_auth = f"{self.source.auth.username}:{self.source.auth.password}"
         return base64.b64encode(docker_auth.encode("utf-8")).decode("utf-8")

+    async def _image_is_available_locally(self) -> bool:
+        """Check if the Docker image is available locally."""
+        try:
+            async with DockerCtx() as client:
+                await client.images.inspect(f"{self.source.docker}:{self.tag}")
+                return True
+        except Exception:
+            return False
+
     async def _pull_docker_image(self, docker_auth: Optional[str]) -> AsyncGenerator[bytes, None]:
</code_context>

<issue_to_address>
**issue (bug_risk):** Catching all exceptions may mask underlying issues.

Catch only the exception(s) that indicate the image is not found, rather than all exceptions, to prevent hiding unrelated errors.
</issue_to_address>

### Comment 2
<location> `core/services/kraken/extension/extension.py:182-187` </location>
<code_context>
+        """Check if the Docker image is available locally."""
+        try:
+            async with DockerCtx() as client:
+                await client.images.inspect(f"{self.source.docker}:{self.tag}")
+                return True
+        except Exception:
</code_context>

<issue_to_address>
**suggestion:** Image reference construction may not handle digests.

This check may fail for images pulled by digest. Align the reference format with the pull logic to ensure consistent identification.

```suggestion
        try:
            image_ref = f"{self.source.docker}:{self.tag}" + (f"@{self.digest}" if self.digest else "")
            async with DockerCtx() as client:
                await client.images.inspect(image_ref)
                return True
        except Exception:
            return False
```
</issue_to_address>

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.

@Williangalvani Williangalvani force-pushed the allow_local_extension branch 3 times, most recently from 79ff87e to 1543860 Compare November 14, 2025 15:04
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.

1 participant