Skip to content

Commit

Permalink
refactor: hoist oci_python_image example to root module (#400)
Browse files Browse the repository at this point in the history
* refactor: hoist oci_python_image example to root module

* fix assertion

* fix assertion

* cleanup

* chore: fmt

* chore: disable failing docker test on AW

---------

Co-authored-by: thesayyn <[email protected]>
  • Loading branch information
alexeagle and thesayyn authored Jan 24, 2025
1 parent b7d38d4 commit a236066
Show file tree
Hide file tree
Showing 20 changed files with 211 additions and 1,608 deletions.
4 changes: 4 additions & 0 deletions .aspect/workflows/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@ tasks:
- lint:
- format:
- test:
bazel:
flags:
- --test_tag_filters=-skip-on-aspect-workflows

notifications:
github: {}
1 change: 0 additions & 1 deletion .bazelignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ jest/
nestjs/
node_snapshot_flags/
oci_java_image/
oci_python_image/
pnpm-workspaces/
prisma/
rules_nodejs_to_rules_js_migration/
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ jobs:
go_workspaces/**
jest/**
nestjs/**
oci_python_image/**
pnpm-workspaces/**
prisma/**
bufbuild/**
Expand Down
13 changes: 12 additions & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ bazel_dep(name = "apple_support", version = "1.15.1")
bazel_dep(name = "aspect_bazel_lib", version = "2.10.0")
bazel_dep(name = "aspect_rules_js", version = "2.0.0")
bazel_dep(name = "aspect_rules_lint", version = "1.0.0-rc10")
bazel_dep(name = "aspect_rules_py", version = "1.0.0")
bazel_dep(name = "aspect_rules_swc", version = "2.0.0")
bazel_dep(name = "aspect_rules_ts", version = "3.0.0")
bazel_dep(name = "bazel_skylib", version = "1.7.1")
Expand Down Expand Up @@ -245,4 +246,14 @@ oci.pull(
"linux/arm64",
],
)
use_repo(oci, "distroless_base")
oci.pull(
name = "ubuntu",
digest = "sha256:80dd3c3b9c6cecb9f1667e9290b3bc61b78c2678c02cbdae5f0fea92cc6734ab",
image = "ubuntu",
platforms = [
"linux/arm64/v8",
"linux/amd64",
],
tag = "latest",
)
use_repo(oci, "distroless_base", "distroless_base_linux_amd64", "distroless_base_linux_arm64", "ubuntu", "ubuntu_linux_amd64", "ubuntu_linux_arm64_v8")
1 change: 0 additions & 1 deletion oci_python_image/.bazeliskrc

This file was deleted.

Empty file removed oci_python_image/.bazelrc
Empty file.
1 change: 0 additions & 1 deletion oci_python_image/.bazelversion

This file was deleted.

10 changes: 3 additions & 7 deletions oci_python_image/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
load("@rules_python//python:pip.bzl", "compile_pip_requirements")

compile_pip_requirements(
name = "requirements",
src = "requirements.in",
requirements_txt = "requirements.txt",
extra_args = ["--allow-unsafe"],
exports_files(
["requirements.txt"],
visibility = ["//requirements:__pkg__"],
)
35 changes: 0 additions & 35 deletions oci_python_image/MODULE.bazel

This file was deleted.

1,223 changes: 0 additions & 1,223 deletions oci_python_image/MODULE.bazel.lock

This file was deleted.

Empty file removed oci_python_image/WORKSPACE.bazel
Empty file.
27 changes: 18 additions & 9 deletions oci_python_image/hello_world/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
load("@aspect_bazel_lib//lib:transitions.bzl", "platform_transition_filegroup")
load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_library")
load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_image_layer", "py_library")
load("@container_structure_test//:defs.bzl", "container_structure_test")
load("@pip//:requirements.bzl", "requirement")
load("@rules_oci//oci:defs.bzl", "oci_load")
load("//:py_layer.bzl", "py_oci_image")
load("@rules_oci//oci:defs.bzl", "oci_image", "oci_load")

py_library(
name = "hello_world_lib",
Expand All @@ -12,7 +11,6 @@ py_library(
"app.py",
],
imports = [".."],
visibility = ["//:__subpackages__"],
deps = [
requirement("cowsay"),
],
Expand All @@ -23,7 +21,6 @@ py_binary(
srcs = ["__main__.py"],
imports = [".."],
main = "__main__.py",
visibility = ["//:__subpackages__"],
deps = [":hello_world_lib"],
)

Expand All @@ -34,13 +31,21 @@ py_binary(
#2f2353bd5bea: Loading layer [==================================================>] 47.13MB/47.13MB
#f02532d45017: Loading layer [==================================================>] 3.62MB/3.62MB
#9296e9071c11: Loading layer [==================================================>] 16.24kB/16.24kB
py_oci_image(
py_image_layer(
name = "layers",
binary = ":hello_world",
)

oci_image(
name = "image",
# This is defined by an oci.pull() call in /MODULE.bazel
base = "@ubuntu",
binary = "hello_world",
entrypoint = ["/hello_world/hello_world"],
entrypoint = ["/oci_python_image/hello_world/hello_world"],
tars = [":layers"],
)

# This is defined by an oci.pull() call in /MODULE.bazel

platform(
name = "aarch64_linux",
constraint_values = [
Expand Down Expand Up @@ -99,7 +104,11 @@ py_test(
# the test Setup has to do some sophisticated work to load each layer.
data = [":platform_image"],
main = "app_test.py",
tags = ["requires-docker"],
tags = [
"requires-docker",
# TODO(sahin/derek?): this test passes on GitHub Actions runners but fails on AW
"skip-on-aspect-workflows",
],
deps = [
requirement("testcontainers"),
],
Expand Down
98 changes: 61 additions & 37 deletions oci_python_image/hello_world/app_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,29 @@
import tempfile
import io


def add_json_file(tar, name, contents):
content = json.dumps(contents).encode('utf-8')
content = json.dumps(contents).encode("utf-8")
info = tarfile.TarInfo(name=name)
info.size = len(content)
tar.addfile(info, fileobj = io.BytesIO(content))
tar.addfile(info, fileobj=io.BytesIO(content))


def add_file(tar, name, fileobj):
info = tarfile.TarInfo(name=name)
info.size = os.fstat(fileobj.fileno()).st_size
tar.addfile(info, fileobj = fileobj)
tar.addfile(info, fileobj=fileobj)
fileobj.close()


def get_blob_path(image, digest):
return "%s/blobs/%s" % (image, digest.replace(":", "/"))


def open_blob(image, digest):
return open(get_blob_path(image, digest), "rb")


def OCIImageContainer(image):
with open("%s/index.json" % image) as indexp:
indexjson = json.load(indexp)
Expand All @@ -40,56 +45,75 @@ def OCIImageContainer(image):

client = docker.from_env()

# Probe and layer loading phase
# Probe and layer loading phase
layers = manifest["layers"]
needed = []
i=0
while i < len(layers):
layer = layers[i]
tmp = tempfile.NamedTemporaryFile(suffix='.tar')
tar = tarfile.open(fileobj=tmp, mode='w')

add_json_file(tar, name = "manifest.json", contents = [{
"Config": "config.json",
"RepoTags": [],
"Layers": list(map(lambda x: x["digest"], manifest["layers"][:i+1]))
}])
add_json_file(tar, name = "config.json", contents = {
"rootfs": {
"type": "layers",
"diff_ids": config["rootfs"]["diff_ids"][:i+1]
}
})

if layer["digest"] in needed:
add_file(tar, name = layer["digest"], fileobj = open_blob(image, layer["digest"]))
# Probing phase
for i, layer in enumerate(layers):
tmp = tempfile.NamedTemporaryFile(suffix=".tar")
tar = tarfile.open(fileobj=tmp, mode="w")
add_json_file(
tar,
name="manifest.json",
contents=[
{
"Config": "config.json",
"RepoTags": [],
"Layers": [layer["digest"]],
}
],
)
add_json_file(
tar,
name="config.json",
contents={
"rootfs": {
"type": "layers",
"diff_ids": [config["rootfs"]["diff_ids"][i]],
}
},
)

tar.close()

try:
r = client.images.load(open(tmp.name, "rb"))
i+=1
# print(r[0].id)
# os.system("tar -tvf %s" % tmp.name)
client.images.load(
open(tmp.name, "rb"),
)
except docker.errors.ImageLoadError as e:
needed.append(layer["digest"])

# Config loading phase
tmp = tempfile.NamedTemporaryFile(suffix='.tar')
tar = tarfile.open(fileobj=tmp, mode='w')
add_json_file(tar, name = "manifest.json", contents = [{
"Config": "config.json",
"RepoTags": [],
"Layers": list(map(lambda x: x["digest"], manifest["layers"]))
}])
add_file(tar, name = "config.json", fileobj = open_blob(image, manifest["config"]["digest"]))
# Loading phase
tmp = tempfile.NamedTemporaryFile(suffix=".tar")
tar = tarfile.open(fileobj=tmp, mode="w")
add_json_file(
tar,
name="manifest.json",
contents=[
{
"Config": "config.json",
"RepoTags": [],
"Layers": list(map(lambda x: x["digest"], manifest["layers"])),
}
],
)
add_file(
tar, name="config.json", fileobj=open_blob(image, manifest["config"]["digest"])
)
for layer in needed:
add_file(tar, name=layer, fileobj=open_blob(image, layer))

tar.close()
r = client.images.load(open(tmp.name, "rb"))
return DockerContainer(r[0].id)


def test_wait_for_hello():
with OCIImageContainer("hello_world/image") as container:
wait_for_logs(container, "hello py_image_layer!")
print("Starting container")
with OCIImageContainer("oci_python_image/hello_world/image") as container:
wait_for_logs(container, "hello py_image_layer!")


test_wait_for_hello()
4 changes: 2 additions & 2 deletions oci_python_image/hello_world/test.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# See https://github.com/GoogleContainerTools/container-structure-test#command-tests
schemaVersion: 2.0.0
metadataTest:
entrypoint: ['/hello_world/hello_world']
entrypoint: ['/oci_python_image/hello_world/hello_world']
commandTests:
- name: run
command: /hello_world/hello_world
command: /oci_python_image/hello_world/hello_world
expectedOutput: ['hello py_image_layer!']
Loading

0 comments on commit a236066

Please sign in to comment.