Library Service¤
The ASAB Library is the concept of shared data content across microservices in the cluster. In the cluster/cloud microservice architectures, all microservices must have access to unified resources. The Library provides a read-only interface for listing and reading this content.
The Library is designed to be read-only. It also allows to "stack" various libraries into one view (overlayed), merging the content of each library into one united space.
The library can also notify the ASAB microservice about changes, e.g. for automated update/reload.
Library structure¤
The library content is organized in a simplified file system manner, with directories and files.
Example of the library structure
Library path rules
- Any path must start with "/", including the root path.
- The directory path must end with "/".
- The directory name cannot contain ".".
- The item path must end with a file extension (e.g. ".txt", ".json", ...).
Layers¤
The library content can be organized into an unlimited number of layers. Each layer is represented by a provider (e.g. filesystem, zookeeper, git, ...) with a specific configuration. Two layers can have the same provider but different base paths.
The layers of the library are like slices of Swiss cheese layered on top of each other. Only if there is a hole in the top layer can you see the layer that shows through underneath. It means that files of the upper layer overwrite files with the same path in the lower layers.
Tip
In the most applications, it is common to create the first layer with Zookeeper provider and the layers beneath with git or libsreg provider. This allows you to quickly modify items of the Library on the first layer.
Disabling files¤
The library concept supports multi-tenancy. By default, all items of the library are visible for everyone, but you can disable some of them for specific tenants.
In order to disable some items of the library, create a file /.disabled.yaml
. This file must be created on the first layer.
In the following example, file1.txt
is disabled for tenant-1 and tenant-2, file2.txt
for tenant-1 and file3.txt
for every tenant.
Warning
When disabling a file for all tenants with a star, don't forget to close it in quotation marks. Otherwise, YAML would interpret star as an alias. Read more about anchors and aliases.
Library service¤
The library service may exist in multiple instances, with different paths
setups.
For that reason, you have to provide a unique service_name
and there is no default value for that.
Each Library item is represented by LibraryItem
dataclass. Read more in the reference section.
Example of the use:
import asab
import asab.library
class MyApplication(asab.Application):
async def initialize(self):
self.LibraryService = asab.library.LibraryService(self, "LibraryService") #(1)!
self.PubSub.subscribe("Library.ready!", self.on_library_ready) #(2)!
async def on_library_ready(self, event_name, library): #(3)!
for item in await self.LibraryService.list("/", recursive=True): #(4)!
print("*", item)
if item.type == 'item': #(5)!
itemio = await self.LibraryService.read(item.name) #(6)!
if itemio is not None:
with itemio: #(7)!
content = itemio.read()
print("- content: {} bytes".format(len(content)))
else:
print(" - (DISABLED)")
if __name__ == '__main__':
app = MyApplication()
app.run()
- Initializes the Library Service. Remember to specify a unique
service_name
. - When the Library is initialized,
Library.ready!
PubSub message is emitted. - The callback has to possess two arguments.
event_name
is the message "Library.ready!",library
is the specific provider with which is the Library initialized. list()
method returns list ofLibraryItem
s. For more information, see the reference section.item.type
can be either 'item' or 'dir'.read()
coroutine returns item IO object or None if the file is disabled.- Item IO object is used as a context manager.
Example of the library configuration:
PubSub messages¤
The Library is created in not-ready
state. After the connection with the technologies behind is established, every library provider changes its state to ready
.
The Library switches to ready
state after all its providers are ready.
If some of the providers is disconnected, the Library switches to not-ready
state again till the connection is reestablished.
Every time the Library changes its state, PubSub
message is published, with the arguments provider
and path
.
Message | Published when... |
---|---|
Library.not_ready! |
at least one provider is not ready. |
Library.ready! |
all of the providers are ready. |
Library.change! |
the content of the Library has changed. |
Notification on changes¤
Some providers are able to detect changes of the library items.
Example
class MyApplication(asab.Application):
async def initialize(self):
self.PubSub.subscribe("Library.ready!", self.on_library_ready
self.PubSub.subscribe("Library.change!", self.on_library_change)
async def on_library_ready(self, event_name, library=None):
await self.LibraryService.subscribe(["/asab"]) #(1)!
def on_library_change(self, message, provider, path): #(2)!
print("New changes in the library found by provider: '{}'".format(provider))
self.LibraryService.subscribe()
method takes either a single path as a string or multiple paths in list and watches for changes in them.- This coroutine takes three arguments:
message
(Library.change!
in this case),provider
(name of the provider that has detected changes) andpath
(the path where changes were made).
Info
Note that the some of the providers detect changes immediately while others detect them periodically. For example, git provider pulls the repository every minute, only after that the changes can be detected.
Providers¤
The list of available providers:
Provider | Read the content | Notify on changes |
---|---|---|
Filesystem | ||
Apache Zookeeper | ||
Microsoft Azure Storage | ||
Git | ||
Libraries repository |
Filesystem¤
The most basic provider that reads data from the local filesystem. The notification on changes functionality is available only for Linux systems, as it uses inotify.
Configuration examples:
Apache Zookeeper¤
ZooKeeper as a consensus technology is vital for microservices in the cluster.
There are several configuration strategies:
1) Configuration from [zookeeper]
section.
[zookeeper]
servers=zookeeper-1:2181,zookeeper-2:2181,zookeeper-3:2181
path=/library
[library]
providers:
zk://
2) Specify a path of a ZooKeeper node where only library lives.
[zookeeper]
servers=zookeeper-1:2181,zookeeper-2:2181,zookeeper-3:2181
path=/else
[library]
providers:
zk:///library
[zookeeper]
servers=zookeeper-1:2181,zookeeper-2:2181,zookeeper-3:2181
path=/else
[library]
providers:
zk:///
3) Configuration from the URL in the [library]
section.
4) Configuration from [zookeeper]
section and joined
[path]{.title-ref} from [zookeeper]
and [library]
sections.
> The resulting path will be [/else/library]{.title-ref}.
[zookeeper]
servers=zookeeper-1:2181,zookeeper-2:2181,zookeeper-3:2181
path=/else
[library]
providers:
zk://./library
If a path
from the [zookeeper]
section is missing, an application class name will be used, e.g.
/BSQueryApp/library
.
Microsoft Azure Storage¤
You can configure the microservice to read from the Microsoft Azure Storage container.
Configuration:
If Container Public Access Level is not set to "Public access", then "Access Policy" must be created with "Read" and "List" permissions and "Shared Access Signature" (SAS) query string must be added to a URL in a configuration:
[library]
providers: azure+https://ACCOUNT-NAME.blob.core.windows.net/BLOB-CONTAINER?sv=2020-10-02&si=XXXX&sr=c&sig=XXXXXXXXXXXXXX
Git repository¤
Please follow this format in the configuration:
Cloning from GitHub repository:
Using a public repository from GitHub, the configuration may look like this:
Using custom branch:
Use hash #<branch-name>
to clone a repository from a
selected branch:
Deploy tokens in GitLab¤
GitLab uses deploy tokens to enable authentication of deployment tasks, independent of a user account. Authentication through deploy tokens is the only supported option for now.
If you want to create a deploy token for your GitLab repository, follow these steps from the manual:
- Go to Settings > Repository > Deploy tokens section in your repository. (Note that you have to possess a "Maintainer" or "Owner" role for the repository.)
- Expand the "Deploy tokens" section. The list of current Active Deploy Tokens will be displayed.
- Complete the fields and scopes. We recommend a custom "username", as you will need it later for the URL in the configuration.
- Record the deploy token's values before leaving or refreshing the page! After that, you cannot access it again.
After the deploy token is created, use the URL for the repository in the following format:
[library]
providers: git+https://<username>:<deploy_token>@gitlab.example.com/john/awesome_project.git
Where does the repository clone?¤
The git provider clones the repository into a temporary directory. The
default path for the cloned repository is
/tmp/asab.library.git/
and it can be changed manually:
Libraries repository¤
The libsreg
provider downloads the content from the distribution URL.
The distribution URL points to HTTP(S) server where content archives are published.
More than one distribution server can be specified:
This variant provides more resiliency against a distribution server unavailability.
A structure of the distribution server filesystem:
+ /my-library/
- my-library-master.tar.xz
- my-library-master.tar.xz.sha256
- my-library-production.tar.xz
- my-library-production.tar.xz.sha256
- my-library-v43.41.tar.xz
- my-library-v43.41.tar.xz.sha256
...
*.tar.xz
: This is the TAR/XZ archive of the actual content*.tar.xz.sha256
: SHA256 checksum of the archive
The structure of the distribution is as follows:
/{archname}/{archname}-{version}.tar.xz
archname
: A name of the distribution archive,my-library
in the example aboveversion
: A version of the distribution archive,master
,production
are typically GIT branches,v43.41
is a GIT tag.
Tip
This provider is designed to use Microsoft Azure Storage as a distribution point. Is is assumed that the content archives are uploaded to the distribution point using CI/CD.
Reference¤
asab.library.LibraryService
¤
Bases: Service
Configuration:
The order of providers is important, the priority (or layering) is top-down.
Each library provider is specified by URL/URI schema:
zk://
orzookeeper://
for ZooKeeper providerfile://
or local path for FileSystem providerazure+https://
for Microsoft Azure Storage provider.git+https://
for Git provider.libsreg+https://
for Libraries provider.
The first provider is responsible for providing /.disabled.yaml
.
A library is created in “not ready” state, each provider then informs the library when it is ready
(eg. Zookeeper provider needs to connect to Zookeeper servers). Only after all providers are ready, the library itself becomes ready.
The library indicates that by the PubSub event Library.ready!
.
Source code in asab/library/service.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 |
|
__init__(app, service_name, paths=None)
¤
Initialize the LibraryService.
The library service is designed to "exist" in multiple instances,
with different paths
setups.
For that reason, you have to provide unique service_name
and there is no default value for that.
If paths
are not provided, they are fetched from [library]providers
configuration.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
app |
Application
|
The ASAB Application. |
required |
service_name |
str
|
A unique name of the service. |
required |
paths |
str | list[str] | None
|
Either single path or list of paths with which LibraryService is connected. |
None
|
Source code in asab/library/service.py
check_disabled(path, tenant=None)
¤
Check if the item specified in path is disabled, either globally or for the specified tenant.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
path |
str
|
Path to the item to be checked. |
required |
tenant |
str | None
|
The tenant to apply. If not specified, the global access is assumed. |
None
|
Returns:
Type | Description |
---|---|
bool
|
|
Source code in asab/library/service.py
export(path='/', tenant=None, remove_path=False)
async
¤
Return a file-like stream containing a gzipped tar archive of the library contents of the path.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
path |
str
|
The path to export. |
'/'
|
tenant |
str | None
|
The tenant to use for the operation. |
None
|
remove_path |
bool
|
If |
False
|
Returns:
Type | Description |
---|---|
IO
|
A file object containing a gzipped tar archive. |
Source code in asab/library/service.py
find(path)
async
¤
Searches for files with a specific name within a library, using the provided path.
The method traverses the library directories, looking for files that match the given filename.
It returns a list of paths leading to these files, or None
if no such files are found.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
path |
str
|
The path specifying the filename and its location within the library. The path should start with a forward slash and include the filename. Example: '/library/Templates/.setup.yaml' |
required |
Returns:
Type | Description |
---|---|
Optional[List[str]]
|
typing.Optional[typing.List[str]]: A list containing the paths to the found files,
|
Source code in asab/library/service.py
is_ready()
¤
Check if all the libraries are ready.
Returns:
Type | Description |
---|---|
bool
|
True if all libraries are ready, otherwise False. |
Source code in asab/library/service.py
list(path='/', tenant=None, recursive=False)
async
¤
List the directory of the library specified by the path that are enabled for the specified tenant. This method can be used only after the Library is ready.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
path |
str
|
Path to the directory. |
'/'
|
tenant |
str | None
|
If specified, items that are enabled for the tenant are filtered. |
None
|
recursive |
bool
|
If |
False
|
Returns:
Type | Description |
---|---|
List[LibraryItem]
|
List of items that are enabled for the tenant. |
Source code in asab/library/service.py
open(path, tenant=None)
async
¤
Read the content of the library item specified by path
in a SAFE way, protected by a context manager/with statement.
This method can be used only after the Library is ready.
Example:
async with self.App.LibraryService.open(path) as b:
if b is None:
return None
text = b.read().decode("utf-8")
Source code in asab/library/service.py
read(path, tenant=None)
async
¤
THIS IS OBSOLETED METHOD, USE open(...)
!!!
Read the content of the library item specified by path
. This method can be used only after the Library is ready.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
path |
str
|
Path to the file, |
required |
tenant |
str | None
|
The tenant to apply. If not specified, the global access is assumed. |
None
|
Returns:
Type | Description |
---|---|
IO | None
|
Readable stream with the content of the library item. |
Example:
itemio = await library.read('/path', 'tenant')
if itemio is not None:
with itemio:
return itemio.read()
Source code in asab/library/service.py
subscribe(paths)
async
¤
Subscribe to changes for specified paths of the library.
In order to notify on changes in the Library, this method must be used after the Library is ready.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
paths |
str | list[str]
|
Either single path or list of paths to be subscribed. All the paths must be absolute (start with '/'). |
required |
Examples:
class MyApplication(asab.Application):
async def initialize(self):
self.PubSub.subscribe("Library.ready!", self.on_library_ready
self.PubSub.subscribe("Library.change!", self.on_library_change)
async def on_library_ready(self, event_name, library=None):
await self.LibraryService.subscribe(["/alpha","/beta"])
def on_library_change(self, message, provider, path):
print("New changes in the library found by provider: '{}'".format(provider))
Source code in asab/library/service.py
asab.library.item.LibraryItem
dataclass
¤
The data class that contains the info about a specific item in the library.
Attributes:
Name | Type | Description |
---|---|---|
name |
str
|
The absolute path of the Item. It can be directly fed into |
type |
str
|
Can be either |
layer |
int
|
The number of highest layer in which this Item is found. The higher the number, the lower the layer is. |
providers |
list
|
List of |
disabled |
bool
|
|
override |
int
|
If |