From be4c46420944531765ecc8bae7305086d71a36d0 Mon Sep 17 00:00:00 2001 From: Marek Szwalkiewicz Date: Thu, 30 Jan 2020 13:49:18 +0000 Subject: Add Artifact Manager service. Adds a micro service that offers gRPC interface for CBA artifacts manipulation. By default the service is attached to py-executor but can be ran as a separate service if needed in the future. Issue-ID: CCSDK-1988 Change-Id: I40e20f085ae1c1e81a48f76dbea181af28d9bd0d Signed-off-by: Marek Szwalkiewicz --- ms/artifact-manager/README | 199 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 ms/artifact-manager/README (limited to 'ms/artifact-manager/README') diff --git a/ms/artifact-manager/README b/ms/artifact-manager/README new file mode 100644 index 000000000..290dadfde --- /dev/null +++ b/ms/artifact-manager/README @@ -0,0 +1,199 @@ +# 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)()) + +``` \ No newline at end of file -- cgit 1.2.3-korg