Skip to content

Commit

Permalink
feat: simplify how subjects are created from existing manifest
Browse files Browse the repository at this point in the history
Signed-off-by: Tomas Coufal <[email protected]>
  • Loading branch information
tumido committed May 2, 2024
1 parent cb575ab commit 00c2de4
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 7 deletions.
22 changes: 22 additions & 0 deletions docs/getting_started/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,28 @@ def push(uri, root):

</details>

<details>

<summary>Example of basic artifact attachment</summary>

We are assuming an `derived-artifact.txt` in the present working directory and that there's already a `localhost:5000/dinosaur/artifact:v1` artifact present in the registry. Here is an example of how to [attach](https://oras.land/docs/concepts/reftypes/) a derived artifact to the existing artifact.

```python
import oras.client
import oras.oci

client = oras.client.OrasClient(insecure=True)

manifest = client.remote.get_manifest("localhost:5000/dinosaur/artifact:v1")
subject = oras.oci.Subject.from_manifest(manifest)

client.push(files=["derived-artifact.txt"], target="localhost:5000/dinosaur/artifact:v1-derived", subject=subject)
Successfully pushed localhost:5000/dinosaur/artifact:v1-derived
Out[4]: <Response [201]>
```

</details>

The above examples are just a start! See our [examples](https://github.com/oras-project/oras-py/tree/main/examples)
folder alongside the repository for more code examples and clients. If you would like help
for an example, or to contribute an example, [you know what to do](https://github.com/oras-project/oras-py/issues)!
Expand Down
26 changes: 26 additions & 0 deletions oras/oci.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
__license__ = "Apache-2.0"

import copy
import hashlib
import json
import os
from dataclasses import dataclass
from typing import Dict, Optional, Tuple

import jsonschema
Expand Down Expand Up @@ -151,3 +154,26 @@ def NewManifest() -> dict:
Get an empty manifest config.
"""
return copy.deepcopy(EmptyManifest)

@dataclass
class Subject:
mediaType: str
digest: str
size: int

@classmethod
def from_manifest(cls, manifest: dict) -> "Subject":
"""
Create a new Subject from a Manifest
:param manifest: manifest to convert to subject
"""
manifest_string = json.dumps(manifest).encode("utf-8")
digest = "sha256:" + hashlib.sha256(manifest_string).hexdigest()
size = len(manifest_string)

return cls(
manifest["mediaType"] or oras.defaults.default_manifest_media_type,
digest,
size,
)
9 changes: 2 additions & 7 deletions oras/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import os
import urllib
from contextlib import contextmanager, nullcontext
from dataclasses import asdict, dataclass
from dataclasses import asdict
from http.cookiejar import DefaultCookiePolicy
from tempfile import TemporaryDirectory
from typing import Callable, Generator, List, Optional, Tuple, Union
Expand Down Expand Up @@ -35,11 +35,6 @@ def temporary_empty_config() -> Generator[str, None, None]:
yield config_file


@dataclass
class Subject:
mediaType: str
digest: str
size: int


class Registry:
Expand Down Expand Up @@ -697,7 +692,7 @@ def push(self, *args, **kwargs) -> requests.Response:
:param refresh_headers: if true or None, headers are refreshed
:type refresh_headers: bool
:param subject: optional subject reference
:type subject: Subject
:type subject: oras.oci.Subject
"""
container = self.get_container(kwargs["target"])
self.load_configs(container, configs=kwargs.get("config_path"))
Expand Down
19 changes: 19 additions & 0 deletions oras/tests/test_oci.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import pytest

import oras.defaults
import oras.oci

@pytest.mark.with_auth(False)
def test_create_subject_from_manifest():
"""
Basic tests for oras Subject creation from empty manifest
"""
manifest = oras.oci.NewManifest()
subject = oras.oci.Subject.from_manifest(manifest)

assert subject.mediaType == oras.defaults.default_manifest_media_type
assert (
subject.digest
== "sha256:7a6f84d8c73a71bf9417c13f721ed102f74afac9e481f89e5a72d28954e7d0c5"
)
assert subject.size == 126
1 change: 1 addition & 0 deletions oras/tests/test_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import oras.client
import oras.defaults
import oras.oci
import oras.provider
import oras.utils

Expand Down

0 comments on commit 00c2de4

Please sign in to comment.