[#3] Added generate proto script create container method
Signed-off-by: Ilyas Niyazov <i.niyazov@yadro.com>
This commit is contained in:
parent
f8465e5b99
commit
fba6eaaa9c
34 changed files with 547 additions and 108 deletions
10
frostfs_sdk/client/__init__.py
Normal file
10
frostfs_sdk/client/__init__.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from frostfs_sdk.client.frostfs_client import FrostfsClient
|
||||
|
||||
from frostfs_sdk.client.models.client_environment import ClientEnvironment
|
||||
from frostfs_sdk.client.models.client_settings import ClientSettings
|
||||
from frostfs_sdk.client.models.ecdsa_model import ECDSA
|
||||
|
||||
from frostfs_sdk.client.parameters.container_param import ContainerCreateParam
|
||||
from frostfs_sdk.client.parameters.wait_param import WaitParam
|
||||
|
||||
from frostfs_sdk.client.services.container import ContainerClient
|
|
@ -1,17 +1,20 @@
|
|||
# Create channel and Stubs
|
||||
import grpc
|
||||
|
||||
from frostfs_sdk.client.services.session import SessionCache
|
||||
from frostfs_sdk.client.models.client_environment import ClientEnvironment
|
||||
from frostfs_sdk.client.models.client_settings import ClientSettings
|
||||
from frostfs_sdk.client.models.ecdsa import ECDSA
|
||||
from frostfs_sdk.client.models.ecdsa_model import ECDSA
|
||||
from frostfs_sdk.client.services.container import ContainerClient
|
||||
from frostfs_sdk.models.dto.version import Version
|
||||
|
||||
|
||||
class FrostfsClient:
|
||||
def __init__(self, client_settings: ClientSettings):
|
||||
self.channel = grpc.insecure_channel(f"{client_settings.host}:{client_settings.port}")
|
||||
self.channel = grpc.insecure_channel(client_settings.address)
|
||||
self.ecdsa: ECDSA = ECDSA(wif=client_settings.wif)
|
||||
|
||||
client_environment = ClientEnvironment(self.ecdsa, self.channel)
|
||||
client_environment = ClientEnvironment(self.ecdsa, self.channel, client_settings.address, Version(), SessionCache(0))
|
||||
self.container = ContainerClient(client_environment)
|
||||
|
||||
def close(self):
|
||||
|
|
|
@ -1,8 +1,22 @@
|
|||
import grpc
|
||||
from frostfs_sdk.client.models.ecdsa import ECDSA
|
||||
from frostfs_sdk.cryptography.key_extension import KeyExtension
|
||||
from frostfs_sdk.client.models.ecdsa_model import ECDSA
|
||||
from frostfs_sdk.models.dto.version import Version
|
||||
from frostfs_sdk.models.dto.owner_id import OwnerId
|
||||
from frostfs_sdk.client.services.session import SessionCache
|
||||
|
||||
|
||||
class ClientEnvironment:
|
||||
def __init__(self, ecdsa: ECDSA, channel: grpc.Channel):
|
||||
def __init__(self, ecdsa: ECDSA, channel: grpc.Channel, address: str, version: Version, session_cache: SessionCache):
|
||||
self.ecdsa = ecdsa
|
||||
self.channel = channel
|
||||
self.version = version
|
||||
# self.owner_id = OwnerId(KeyExtension.get_owner_id_by_wif(ecdsa.wif))
|
||||
self.owner_id = "11"
|
||||
self.session_cache = session_cache
|
||||
self.address = address
|
||||
|
||||
def get_session_key(self):
|
||||
if not self._session_key:
|
||||
self._session_key = SessionCache.form_cache_key(self.address, KeyExtension.get_hex_string(self.ecdsa.public_key))
|
||||
return self._session_key
|
||||
|
|
21
frostfs_sdk/client/parameters/call_context_param.py
Normal file
21
frostfs_sdk/client/parameters/call_context_param.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
|
||||
DEFAULT_GRPC_TIMEOUT = 5
|
||||
|
||||
|
||||
class TimeUnit(Enum):
|
||||
MINUTES = "MINUTES"
|
||||
SECONDS = "SECONDS"
|
||||
MILLISECONDS = "MILLISECONDS"
|
||||
|
||||
@dataclass
|
||||
class CallContextParam:
|
||||
timeout: int = DEFAULT_GRPC_TIMEOUT
|
||||
time_unit: TimeUnit = TimeUnit.SECONDS
|
||||
|
||||
@classmethod
|
||||
def default(cls):
|
||||
return cls()
|
|
@ -2,17 +2,17 @@ from dataclasses import dataclass, field
|
|||
from typing import Optional, Dict
|
||||
|
||||
from frostfs_sdk.models.dto.container import Container
|
||||
from frostfs_sdk.models.dto.session_token import SessionToken
|
||||
from frostfs_sdk.client.parameters.wait import PrmWait
|
||||
from frostfs_sdk.client.services.session import SessionToken
|
||||
from frostfs_sdk.client.parameters.wait_param import WaitParam
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PrmContainerCreate:
|
||||
class ContainerCreateParam:
|
||||
container: Container
|
||||
wait_params: Optional[PrmWait] = None
|
||||
wait_params: Optional[WaitParam] = None
|
||||
session_token: Optional[SessionToken] = None
|
||||
x_headers: Dict[str, str] = field(default_factory=dict)
|
||||
|
||||
def __post_init__(self):
|
||||
if self.wait_params is None:
|
||||
object.__setattr__(self, 'wait_params', PrmWait())
|
||||
object.__setattr__(self, 'wait_params', WaitParam())
|
|
@ -4,7 +4,7 @@ from typing import Optional
|
|||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PrmWait:
|
||||
class WaitParam:
|
||||
DEFAULT_TIMEOUT: timedelta = field(default=timedelta(seconds=120), init=False)
|
||||
DEFAULT_POLL_INTERVAL: timedelta = field(default=timedelta(seconds=5), init=False)
|
||||
|
|
@ -1,30 +1,41 @@
|
|||
# implementation Conainer methods
|
||||
from frostfs_sdk.client.models.client_environment import ClientEnvironment
|
||||
from frostfs_sdk.client.services.context_accessor import ContextAccessor
|
||||
from frostfs_sdk.client.parameters.container_param import ContainerCreateParam
|
||||
from frostfs_sdk.client.parameters.call_context_param import CallContextParam
|
||||
from frostfs_sdk.cryptography.signer import Signer
|
||||
from frostfs_sdk.models.dto.container import ContainerId
|
||||
import protos.models.container.service_pb2_grpc as service_pb2_grpc_container
|
||||
import protos.models.container.service_pb2 as service_pb2_container
|
||||
|
||||
from frostfs_sdk.client.parameters.container_create import PrmContainerCreate
|
||||
from frostfs_sdk.models.dto.container import ContainerId, Container
|
||||
from frostfs_sdk.models.mappers.container_mapper import ContainerMapper
|
||||
from frostfs_sdk.models.mappers.owner_id_mapper import OwnerIdMapper
|
||||
from frostfs_sdk.models.mappers.version_mapper import VersionMapper
|
||||
from frostfs_sdk.protos.models.container import service_pb2 as service_pb2_container
|
||||
from frostfs_sdk.protos.models.container import service_pb2_grpc as service_pb2_grpc_container
|
||||
|
||||
|
||||
|
||||
class ContainerClient:
|
||||
class ContainerClient(ContextAccessor):
|
||||
def __init__(self, client_environment: ClientEnvironment):
|
||||
super().__init__(client_environment)
|
||||
self.container_stub = service_pb2_grpc_container.ContainerServiceStub(client_environment.channel)
|
||||
self.ecdsa = client_environment.ecdsa
|
||||
|
||||
def create(self, prm_container_create: PrmContainerCreate) -> ContainerId:
|
||||
request = self.create_put_request(prm_container_create)
|
||||
def create_container(self, container_create_param: ContainerCreateParam, ctx: CallContextParam) -> ContainerId:
|
||||
request = self._create_put_request(container_create_param, ctx)
|
||||
response: service_pb2_container.PutResponse = self.container_stub.Put(request)
|
||||
return ContainerId(value=response.body.container_id)
|
||||
return ContainerId(value=response.body.container_id.value)
|
||||
|
||||
def create_put_request(self, prm: PrmContainerCreate):
|
||||
grpc_container=ContainerMapper().to_grpc_message(prm.container)
|
||||
def _create_put_request(self, param: ContainerCreateParam, ctx: CallContextParam) -> service_pb2_container.PutRequest:
|
||||
"""
|
||||
Creates a PUT request for creating a container.
|
||||
"""
|
||||
grpc_container=ContainerMapper().to_grpc_message(param.container)
|
||||
if not grpc_container.owner_id:
|
||||
grpc_container.owner_id = OwnerIdMapper.to_grpc_message(self.get_context.owner_id)
|
||||
if not grpc_container.version:
|
||||
grpc_container.version = VersionMapper.to_grpc_message(self.get_context.version)
|
||||
|
||||
body = service_pb2_container.PutRequest.Body(
|
||||
container=grpc_container,
|
||||
signature=Signer.sign_rfc6979(self.ecdsa.private_key, grpc_container)
|
||||
signature=Signer.sign_message_rfc_6979(self.get_context.ecdsa, grpc_container)
|
||||
)
|
||||
|
||||
request = service_pb2_container.PutRequest(body=body)
|
||||
|
|
12
frostfs_sdk/client/services/context_accessor.py
Normal file
12
frostfs_sdk/client/services/context_accessor.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from frostfs_sdk.client.models.client_environment import ClientEnvironment
|
||||
|
||||
class ContextAccessor:
|
||||
def __init__(self, context: ClientEnvironment):
|
||||
"""
|
||||
Initializes a ContextAccessor with a given ClientEnvironment.
|
||||
"""
|
||||
self.context: ClientEnvironment = context
|
||||
|
||||
@property
|
||||
def get_context(self) -> ClientEnvironment:
|
||||
return self.context
|
37
frostfs_sdk/client/services/session.py
Normal file
37
frostfs_sdk/client/services/session.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SessionToken:
|
||||
token: bytes
|
||||
|
||||
|
||||
class SessionCache:
|
||||
def __init__(self, session_expiration_duration):
|
||||
self.cache = {}
|
||||
self.token_duration = session_expiration_duration
|
||||
self.current_epoch = 0
|
||||
|
||||
|
||||
def contains(self, key):
|
||||
return key in self.cache
|
||||
|
||||
def try_get_value(self, key):
|
||||
if not key:
|
||||
return None
|
||||
return self.cache.get(key)
|
||||
|
||||
|
||||
def set_value(self, key, value):
|
||||
if key is not None:
|
||||
self.cache[key] = value
|
||||
|
||||
def delete_by_prefix(self, prefix):
|
||||
# Collect keys to avoid modifying dictionary during iteration
|
||||
keys_to_delete = [key for key in self.cache if key.startswith(prefix)]
|
||||
for key in keys_to_delete:
|
||||
del self.cache[key]
|
||||
|
||||
@staticmethod
|
||||
def form_cache_key(address: str, key: str):
|
||||
return address + key
|
30
frostfs_sdk/client/utils/message_helper.py
Normal file
30
frostfs_sdk/client/utils/message_helper.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
from typing import Any
|
||||
from google.protobuf.message import Message
|
||||
|
||||
class MessageHelper:
|
||||
@staticmethod
|
||||
def get_field(message: Message, field_name: str):
|
||||
"""
|
||||
Retrieves the value of a field from a Protobuf message.
|
||||
|
||||
:param message: A Protobuf Message object.
|
||||
:param field_name: The name of the field to retrieve.
|
||||
:return: The value of the specified field.
|
||||
:raises ValueError: If the input parameters are invalid.
|
||||
"""
|
||||
if not message or not field_name or not isinstance(field_name, str) or not field_name.strip():
|
||||
raise ValueError("Some parameter is missing")
|
||||
|
||||
descriptor = message.DESCRIPTOR
|
||||
field_descriptor = descriptor.fields[field_name]
|
||||
if not field_descriptor:
|
||||
raise ValueError(f"Field '{field_name}' not found in message descriptor")
|
||||
|
||||
return message.GetField(field_descriptor[field_name])
|
||||
|
||||
@staticmethod
|
||||
def set_field(message: Message, field_name: str, value: Any) -> None:
|
||||
if message is None or not field_name.strip() or value is None:
|
||||
raise ValueError("Some parameter is missing")
|
||||
|
||||
message.SetField(message.DESCRIPTOR.fields[field_name], value)
|
Loading…
Add table
Add a link
Reference in a new issue