blob: 290dadfde77155e7e28963c15e3901a1a5fb981d [file] [log] [blame]
Marek Szwalkiewiczbe4c4642020-01-30 13:49:18 +00001# CDS Artifact Manager
2
3Artifact 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`.
4
5## Configuration
6Configuration is stored in `.ini` file, you can specify a path and name of a file using `CONFIGURATION` env variable.
7For possible variables please see example below (with inline comments):
8```
9[artifactManagerServer]
10port=50052 # A port on which the server will be listening
11logFile=server.log # Path to a log file
12maxWorkers=20 # Max number of concurent workers
13debug=true # Debug flag
14logConfig=logging.yaml # A special MDC logger config
15fileRepositoryBasePath=/tmp/ # A FS path where we should store CBA files
16```
17
18## Methods
19Below is a list of gRPC methods handled by the service. The `proto` files are available in `artifact-manager/manager/proto` directory.
20
21All methods expect `CommonHeader` with:
22* `timestamp` - datetime in UTC with this: `%Y-%m-%dT%H:%M:%S.%fZ` format
23* `originatorId` - name of the component (eg. `CDS`)
24* `requestId` - ID of the request
25* `subRequestId` - Sub ID of the request
26* `flag` - TBD
27
28and an `ActionIdentifiers` with following fields:
29* `blueprintName` - name of the blueprint
30* `blueprintVersion` - version number of blueprint (as string)
31* `actionName` - TBD
32* `mode` - TBD
33
34### Upload
35
36Upload `CBA.zip` file for storage in artifact manager. File needs to be sent as a binary data in `fileChunk` field.
37
38#### Example
39
40```
41stub: BluePrintManagementServiceStub = BluePrintManagementServiceStub(channel)
42with open(file_path, "rb") as cba_file:
43 msg: BluePrintUploadInput = BluePrintUploadInput()
44 msg.actionIdentifiers.blueprintName = "Test"
45 msg.actionIdentifiers.blueprintVersion = "0.0.1"
46 msg.fileChunk.chunk = cba_file.read()
47return stub.uploadBlueprint(msg)
48```
49
50### Download
51
52Download existing `CBA.zip` file.
53
54#### Example
55
56```
57stub: BluePrintManagementServiceStub = BluePrintManagementServiceStub(channel)
58msg: BluePrintDownloadInput = BluePrintDownloadInput()
59msg.actionIdentifiers.blueprintName = "Test"
60msg.actionIdentifiers.blueprintVersion = "0.0.1"
61return stub.downloadBlueprint(msg)
62```
63### Remove
64
65Delete existing `CBA.zip` file.
66
67#### Example
68
69```
70stub: BluePrintManagementServiceStub = BluePrintManagementServiceStub(channel)
71msg: BluePrintRemoveInput = BluePrintRemoveInput()
72msg.actionIdentifiers.blueprintName = "Test"
73msg.actionIdentifiers.blueprintVersion = "0.0.1"
74return stub.removeBlueprint(msg)
75```
76
77## Full gRPC Client Example
78
79```
80import logging
81import sys
82from argparse import ArgumentParser, FileType, Namespace
83from configparser import ConfigParser
84from datetime import datetime
85from pathlib import Path
86
87import zipfile
88
89from grpc import Channel, ChannelCredentials, insecure_channel, secure_channel, ssl_channel_credentials
90
91from proto.BluePrintManagement_pb2 import (
92 BluePrintDownloadInput,
93 BluePrintRemoveInput,
94 BluePrintUploadInput,
95 BluePrintManagementOutput,
96)
97from proto.BluePrintManagement_pb2_grpc import BluePrintManagementServiceStub
98
99
100logging.basicConfig(level=logging.DEBUG)
101
102
103class ClientArgumentParser(ArgumentParser):
104 """Client argument parser.
105
106 It has two arguments:
107 - config_file - provide a path to configuration file. Default is ./configuration-local.ini
108 - actions - list of actions to do by client. It have to be a list of given values: upload, download, remove.
109 """
110
111 DEFAULT_CONFIG_PATH: str = str(Path(__file__).resolve().with_name("configuration-local.ini"))
112
113 def __init__(self, *args, **kwargs):
114 """Initialize argument parser."""
115 super().__init__(*args, **kwargs)
116 self.description: str = "Artifact Manager client example"
117
118 self.add_argument(
119 "--config_file",
120 type=FileType("r"),
121 default=self.DEFAULT_CONFIG_PATH,
122 help="Path to the client configuration file. By default it's `configuration-local.ini` file from Artifact Manager directory",
123 )
124 self.add_argument(
125 "--actions", nargs="+", default=["upload", "download", "remove"], choices=["upload", "download", "remove"]
126 )
127
128
129class Client:
130 """Client class.
131
132 Implements methods which can be called to server.
133 """
134
135 def __init__(self, channel: Channel, config: ConfigParser) -> None:
136 """Initialize client class.
137
138 :param channel: gprc channel object
139 :param config: ConfigParser object with "client" section
140 """
141 self.channel: Channel = channel
142 self.stub: BluePrintManagementServiceStub = BluePrintManagementServiceStub(self.channel)
143 self.config = config
144
145 def upload(self) -> BluePrintManagementOutput:
146 """Prepare upload message and send it to server."""
147 logging.info("Call upload client method")
148 with open(self.config.get("client", "cba_file"), "rb") as cba_file:
149 msg: BluePrintUploadInput = BluePrintUploadInput()
150 msg.actionIdentifiers.blueprintName = "Test"
151 msg.actionIdentifiers.blueprintVersion = "0.0.1"
152 msg.fileChunk.chunk = cba_file.read()
153 return self.stub.uploadBlueprint(msg)
154
155 def download(self) -> BluePrintManagementOutput:
156 """Prepare download message and send it to server."""
157 logging.info("Call download client method")
158 msg: BluePrintDownloadInput = BluePrintDownloadInput()
159 msg.actionIdentifiers.blueprintName = "Test"
160 msg.actionIdentifiers.blueprintVersion = "0.0.1"
161 return self.stub.downloadBlueprint(msg)
162
163 def remove(self) -> BluePrintManagementOutput:
164 """Prepare remove message and send it to server."""
165 logging.info("Call remove client method")
166 msg: BluePrintRemoveInput = BluePrintRemoveInput()
167 msg.actionIdentifiers.blueprintName = "Test"
168 msg.actionIdentifiers.blueprintVersion = "0.0.1"
169 return self.stub.removeBlueprint(msg)
170
171
172if __name__ == "__main__":
173 arg_parser: ClientArgumentParser = ClientArgumentParser()
174 args: Namespace = arg_parser.parse_args()
175
176 config_parser: ConfigParser = ConfigParser()
177 config_parser.read_file(args.config_file)
178
179 server_address: str = f"{config_parser.get('client', 'address')}:{config_parser.get('client', 'port')}"
180 if config_parser.getboolean("client", "use_ssl", fallback=False):
181 logging.info(f"Create secure connection on {server_address}")
182 with open(config_parser.get("client", "private_key_file"), "rb") as private_key_file, open(
183 config_parser.get("client", "certificate_chain_file"), "rb"
184 ) as certificate_chain_file:
185 ssl_credentials: ChannelCredentials = ssl_channel_credentials(
186 private_key=private_key_file.read(), certificate_chain=certificate_chain_file.read()
187 )
188 channel: Channel = secure_channel(server_address, ssl_credentials)
189 else:
190 logging.info(f"Create insecure connection on {server_address}")
191 channel: Channel = insecure_channel(server_address)
192
193 with channel:
194 client: Client = Client(channel, config_parser)
195 for action in args.actions:
196 logging.info("Get response")
197 logging.info(getattr(client, action)())
198
199```