blob: 290dadfde77155e7e28963c15e3901a1a5fb981d [file] [log] [blame]
# CDS Artifact Manager
Artifact Manager is a very simple gRPC service that lets you upload, download and delete CBA archives. It can be ran as a standalone micro service (using `server.py`) or you can include it's methods in a service like `py-executor`.
## Configuration
Configuration is stored in `.ini` file, you can specify a path and name of a file using `CONFIGURATION` env variable.
For possible variables please see example below (with inline comments):
```
[artifactManagerServer]
port=50052 # A port on which the server will be listening
logFile=server.log # Path to a log file
maxWorkers=20 # Max number of concurent workers
debug=true # Debug flag
logConfig=logging.yaml # A special MDC logger config
fileRepositoryBasePath=/tmp/ # A FS path where we should store CBA files
```
## Methods
Below is a list of gRPC methods handled by the service. The `proto` files are available in `artifact-manager/manager/proto` directory.
All methods expect `CommonHeader` with:
* `timestamp` - datetime in UTC with this: `%Y-%m-%dT%H:%M:%S.%fZ` format
* `originatorId` - name of the component (eg. `CDS`)
* `requestId` - ID of the request
* `subRequestId` - Sub ID of the request
* `flag` - TBD
and an `ActionIdentifiers` with following fields:
* `blueprintName` - name of the blueprint
* `blueprintVersion` - version number of blueprint (as string)
* `actionName` - TBD
* `mode` - TBD
### Upload
Upload `CBA.zip` file for storage in artifact manager. File needs to be sent as a binary data in `fileChunk` field.
#### Example
```
stub: BluePrintManagementServiceStub = BluePrintManagementServiceStub(channel)
with open(file_path, "rb") as cba_file:
msg: BluePrintUploadInput = BluePrintUploadInput()
msg.actionIdentifiers.blueprintName = "Test"
msg.actionIdentifiers.blueprintVersion = "0.0.1"
msg.fileChunk.chunk = cba_file.read()
return stub.uploadBlueprint(msg)
```
### Download
Download existing `CBA.zip` file.
#### Example
```
stub: BluePrintManagementServiceStub = BluePrintManagementServiceStub(channel)
msg: BluePrintDownloadInput = BluePrintDownloadInput()
msg.actionIdentifiers.blueprintName = "Test"
msg.actionIdentifiers.blueprintVersion = "0.0.1"
return stub.downloadBlueprint(msg)
```
### Remove
Delete existing `CBA.zip` file.
#### Example
```
stub: BluePrintManagementServiceStub = BluePrintManagementServiceStub(channel)
msg: BluePrintRemoveInput = BluePrintRemoveInput()
msg.actionIdentifiers.blueprintName = "Test"
msg.actionIdentifiers.blueprintVersion = "0.0.1"
return stub.removeBlueprint(msg)
```
## Full gRPC Client Example
```
import logging
import sys
from argparse import ArgumentParser, FileType, Namespace
from configparser import ConfigParser
from datetime import datetime
from pathlib import Path
import zipfile
from grpc import Channel, ChannelCredentials, insecure_channel, secure_channel, ssl_channel_credentials
from proto.BluePrintManagement_pb2 import (
BluePrintDownloadInput,
BluePrintRemoveInput,
BluePrintUploadInput,
BluePrintManagementOutput,
)
from proto.BluePrintManagement_pb2_grpc import BluePrintManagementServiceStub
logging.basicConfig(level=logging.DEBUG)
class ClientArgumentParser(ArgumentParser):
"""Client argument parser.
It has two arguments:
- config_file - provide a path to configuration file. Default is ./configuration-local.ini
- actions - list of actions to do by client. It have to be a list of given values: upload, download, remove.
"""
DEFAULT_CONFIG_PATH: str = str(Path(__file__).resolve().with_name("configuration-local.ini"))
def __init__(self, *args, **kwargs):
"""Initialize argument parser."""
super().__init__(*args, **kwargs)
self.description: str = "Artifact Manager client example"
self.add_argument(
"--config_file",
type=FileType("r"),
default=self.DEFAULT_CONFIG_PATH,
help="Path to the client configuration file. By default it's `configuration-local.ini` file from Artifact Manager directory",
)
self.add_argument(
"--actions", nargs="+", default=["upload", "download", "remove"], choices=["upload", "download", "remove"]
)
class Client:
"""Client class.
Implements methods which can be called to server.
"""
def __init__(self, channel: Channel, config: ConfigParser) -> None:
"""Initialize client class.
:param channel: gprc channel object
:param config: ConfigParser object with "client" section
"""
self.channel: Channel = channel
self.stub: BluePrintManagementServiceStub = BluePrintManagementServiceStub(self.channel)
self.config = config
def upload(self) -> BluePrintManagementOutput:
"""Prepare upload message and send it to server."""
logging.info("Call upload client method")
with open(self.config.get("client", "cba_file"), "rb") as cba_file:
msg: BluePrintUploadInput = BluePrintUploadInput()
msg.actionIdentifiers.blueprintName = "Test"
msg.actionIdentifiers.blueprintVersion = "0.0.1"
msg.fileChunk.chunk = cba_file.read()
return self.stub.uploadBlueprint(msg)
def download(self) -> BluePrintManagementOutput:
"""Prepare download message and send it to server."""
logging.info("Call download client method")
msg: BluePrintDownloadInput = BluePrintDownloadInput()
msg.actionIdentifiers.blueprintName = "Test"
msg.actionIdentifiers.blueprintVersion = "0.0.1"
return self.stub.downloadBlueprint(msg)
def remove(self) -> BluePrintManagementOutput:
"""Prepare remove message and send it to server."""
logging.info("Call remove client method")
msg: BluePrintRemoveInput = BluePrintRemoveInput()
msg.actionIdentifiers.blueprintName = "Test"
msg.actionIdentifiers.blueprintVersion = "0.0.1"
return self.stub.removeBlueprint(msg)
if __name__ == "__main__":
arg_parser: ClientArgumentParser = ClientArgumentParser()
args: Namespace = arg_parser.parse_args()
config_parser: ConfigParser = ConfigParser()
config_parser.read_file(args.config_file)
server_address: str = f"{config_parser.get('client', 'address')}:{config_parser.get('client', 'port')}"
if config_parser.getboolean("client", "use_ssl", fallback=False):
logging.info(f"Create secure connection on {server_address}")
with open(config_parser.get("client", "private_key_file"), "rb") as private_key_file, open(
config_parser.get("client", "certificate_chain_file"), "rb"
) as certificate_chain_file:
ssl_credentials: ChannelCredentials = ssl_channel_credentials(
private_key=private_key_file.read(), certificate_chain=certificate_chain_file.read()
)
channel: Channel = secure_channel(server_address, ssl_credentials)
else:
logging.info(f"Create insecure connection on {server_address}")
channel: Channel = insecure_channel(server_address)
with channel:
client: Client = Client(channel, config_parser)
for action in args.actions:
logging.info("Get response")
logging.info(getattr(client, action)())
```