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 endpoint for uploading docker image #232

Merged
merged 13 commits into from
Dec 1, 2023
Merged

Add endpoint for uploading docker image #232

merged 13 commits into from
Dec 1, 2023

Conversation

evanyeyeye
Copy link
Member

@evanyeyeye evanyeyeye commented Apr 24, 2023

Description

  • Added endpoint for uploading a docker image tar created using docker save.
  • The maximum file upload size was increased from 250 MB to 10 GB, which may be a concern for assignment uploads.
  • Updated tango client script to utilize the new endpoint.
  • Corresponding Autolab PR.

Testing

  • Used the updated clients/tango-cli.py to upload and build a new docker image.
  • Verified that bad file submissions are error reported properly, and do not crash Tango.

@evanyeyeye evanyeyeye requested a review from damianhxy April 24, 2023 21:27
@evanyeyeye
Copy link
Member Author

This PR is updated and ready for review.

@damianhxy
Copy link
Member

@CodeRabbit review

Copy link
Member

@damianhxy damianhxy left a comment

Choose a reason for hiding this comment

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

Screenshot 2023-11-30 at 15 52 40

Successfully used tango-cli to upload an image. LGTM

@cg2v
Copy link
Member

cg2v commented Dec 1, 2023

At the very least, this operation should verify that it's using the localDocker backend, since it can only possibly work with that backend.

Copy link

coderabbitai bot commented Dec 1, 2023

Walkthrough

Walkthrough

The recent updates involve enhancing a system with Docker capabilities, including building Docker images and handling larger input files. A new .gitignore entry ensures temporary Docker files are excluded. The tango-cli script now supports a build command, and the server-side code has been updated to handle build requests and manage Docker operations. Additionally, the Python environment has been adjusted with package version changes, including a downgrade of the requests package and the addition of the docker package.

Changes

File(s) Summary
.gitignore Excluded dockerTmp/* to prevent tracking temporary Docker files.
clients/.../tango-cli.py Added --build argument and tango_build function for Docker image building.
config.template.py Increased MAX_INPUT_FILE_SIZE from 250MB to 10GB.
requirements.txt Downgraded requests to 2.28.0 and added docker 5.0.3.
restful_tango/server.py Added BuildHandler class for handling Docker build requests.
restful_tango/tangoREST.py (3 entries) Imported docker module, added Docker-related status codes, and a new build method for image building.

Tips

Chat with CodeRabbit Bot (@coderabbitai)

  • If you reply to a review comment from CodeRabbit, the bot will automatically respond.
  • To engage with CodeRabbit bot directly around the specific lines of code in the PR, mention @coderabbitai in your review comment
  • Note: Review comments are made on code diffs or files, not on the PR overview.
  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Note: For conversation with the bot, please use the review comments on code diffs or files.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

@@ -87,7 +87,7 @@ class Config(object):
DOCKER_HOST_USER = ""

# Maximum size for input files in bytes
MAX_INPUT_FILE_SIZE = 250 * 1024 * 1024 # 250MB
MAX_INPUT_FILE_SIZE = 10 * 1024 * 1024 * 1024 # 10GB
Copy link

Choose a reason for hiding this comment

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

The increase in MAX_INPUT_FILE_SIZE to 10GB is a significant change that could impact system resources. Ensure that the system has been tested to handle such large uploads without running into issues like disk space or memory constraints. Additionally, consider the potential impact on dependents and whether the MAX_OUTPUT_FILE_SIZE should also be adjusted to accommodate larger outputs.

Comment on lines 442 to 451
else:
self.log.info("Key not recognized: %s" % key)
return self.status.wrong_key

def build(self, key, tempfile):
self.log.debug("Received build request(%s)" % (key))
if Config.VMMS_NAME != "localDocker":
self.log.error("Not using Docker backend, so cannot build image")
os.unlink(tempfile)
return "test"
Copy link

Choose a reason for hiding this comment

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

The hardcoded return value "test" in the build method is likely a mistake and should be replaced with an appropriate status object or message.

-            return "test"
+            return self.status.create(-1, "Docker backend not in use")

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
else:
self.log.info("Key not recognized: %s" % key)
return self.status.wrong_key
def build(self, key, tempfile):
self.log.debug("Received build request(%s)" % (key))
if Config.VMMS_NAME != "localDocker":
self.log.error("Not using Docker backend, so cannot build image")
os.unlink(tempfile)
return "test"
else:
self.log.info("Key not recognized: %s" % key)
return self.status.wrong_key
def build(self, key, tempfile):
self.log.debug("Received build request(%s)" % (key))
if Config.VMMS_NAME != "localDocker":
self.log.error("Not using Docker backend, so cannot build image")
os.unlink(tempfile)
return self.status.create(-1, "Docker backend not in use")

Comment on lines +454 to +455
client = docker.from_env()
imageTarStr = open(tempfile, "rb").read()
Copy link

Choose a reason for hiding this comment

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

Use a context manager to ensure the temporary file is properly closed.

-                imageTarStr = open(tempfile, "rb").read()
+                with open(tempfile, "rb") as file:
+                    imageTarStr = file.read()

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
client = docker.from_env()
imageTarStr = open(tempfile, "rb").read()
client = docker.from_env()
with open(tempfile, "rb") as file:
imageTarStr = file.read()

Comment on lines 448 to 452
if Config.VMMS_NAME != "localDocker":
self.log.error("Not using Docker backend, so cannot build image")
os.unlink(tempfile)
return "test"
if self.validateKey(key):
Copy link

Choose a reason for hiding this comment

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

The key validation should occur before checking the VMMS_NAME to avoid unnecessary processing for invalid keys.

-        if Config.VMMS_NAME != "localDocker":
-            self.log.error("Not using Docker backend, so cannot build image")
-            os.unlink(tempfile)
-            return "test"
-        if self.validateKey(key):
+        if self.validateKey(key):
+            if Config.VMMS_NAME != "localDocker":
+                self.log.error("Not using Docker backend, so cannot build image")
+                os.unlink(tempfile)
+                return self.status.create(-1, "Docker backend not in use")

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
if Config.VMMS_NAME != "localDocker":
self.log.error("Not using Docker backend, so cannot build image")
os.unlink(tempfile)
return "test"
if self.validateKey(key):
if self.validateKey(key):
if Config.VMMS_NAME != "localDocker":
self.log.error("Not using Docker backend, so cannot build image")
os.unlink(tempfile)
return self.status.create(-1, "Docker backend not in use")

Comment on lines +456 to +457
images = client.images.load(imageTarStr)
id_list = ", ".join(image.short_id for image in images)
Copy link

Choose a reason for hiding this comment

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

Handle the case where no images are loaded from the tar file by checking the length of the images list and returning an appropriate status message if it is empty.

+                if not images:
+                    self.log.error("No images were loaded from the tar file")
+                    os.unlink(tempfile)
+                    return self.status.create(-1, "No images loaded from file")

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
images = client.images.load(imageTarStr)
id_list = ", ".join(image.short_id for image in images)
images = client.images.load(imageTarStr)
if not images:
self.log.error("No images were loaded from the tar file")
os.unlink(tempfile)
return self.status.create(-1, "No images loaded from file")
id_list = ", ".join(image.short_id for image in images)

Comment on lines +128 to +147
@tornado.web.stream_request_body
class BuildHandler(tornado.web.RequestHandler):
def prepare(self):
"""set up the temporary file"""
tempdir = "dockerTmp"
if not os.path.exists(tempdir):
os.mkdir(tempdir, 0o700)
if os.path.exists(tempdir) and not os.path.isdir(tempdir):
tangoREST.log("Cannot process uploads, %s is not a directory" % (tempdir,))
return self.send_error()
self.tempfile = NamedTemporaryFile(prefix="docker", dir=tempdir, delete=False)

def data_received(self, chunk):
self.tempfile.write(chunk)

def post(self, key):
"""post - Handles the post request to build."""
name = self.tempfile.name
self.tempfile.close()
self.write(tangoREST.build(key, name))
Copy link

Choose a reason for hiding this comment

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

The BuildHandler class correctly implements the streaming of request bodies to a temporary file. However, there are a few points to consider:

  • The prepare method creates a temporary directory dockerTmp without checking for permissions or handling potential exceptions that could occur during directory creation.
  • The post method does not perform any cleanup of the temporary file after the build process, which could lead to storage issues if not handled elsewhere.
  • There is no error handling for the build method call in the post method, which could lead to unhandled exceptions if the build process fails.

Comment on lines +132 to +134
tempdir = "dockerTmp"
if not os.path.exists(tempdir):
os.mkdir(tempdir, 0o700)
Copy link

Choose a reason for hiding this comment

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

The tempdir is hardcoded as dockerTmp. Consider making this a configurable value in the Config class to improve maintainability and flexibility.

Comment on lines +140 to +141
def data_received(self, chunk):
self.tempfile.write(chunk)
Copy link

Choose a reason for hiding this comment

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

The data_received method does not update a hash or checksum for the received data. It might be beneficial to calculate a checksum to verify the integrity of the uploaded file.

Comment on lines +143 to +147
def post(self, key):
"""post - Handles the post request to build."""
name = self.tempfile.name
self.tempfile.close()
self.write(tangoREST.build(key, name))
Copy link

Choose a reason for hiding this comment

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

The key parameter in the post method is not sanitized or validated before being passed to the build method. Ensure that the key is properly validated to prevent potential security issues.

"""post - Handles the post request to build."""
name = self.tempfile.name
self.tempfile.close()
self.write(tangoREST.build(key, name))
Copy link

Choose a reason for hiding this comment

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

The post method does not check the result of the tangoREST.build call before writing it to the response. Consider adding error handling to provide appropriate feedback to the client in case of a build failure.

@evanyeyeye evanyeyeye merged commit 8348ab5 into master Dec 1, 2023
1 check passed
@evanyeyeye evanyeyeye deleted the add-build branch December 1, 2023 18:20
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.

3 participants