Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/persist #4

Merged
merged 3 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion arlas/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from arlas.cli.collections import collections
from arlas.cli.configurations import configurations
from arlas.cli.persist import persist
from arlas.cli.index import indices
from arlas.cli.variables import variables
from arlas.cli.settings import ARLAS, Configuration, Resource, Settings
Expand Down Expand Up @@ -36,7 +37,8 @@ def init(
arlas={
"demo": ARLAS(server=Resource(location="https://demo.cloud.arlas.io/arlas/server", headers={"Content-Type": "application/json"})),
"local": ARLAS(
server=Resource(location="http://localhost:9999/arlas", headers={"Content-Type": "application/json"}),
server=Resource(location="http://localhost/arlas", headers={"Content-Type": "application/json"}),
persistence=Resource(location="http://localhost/persist", headers={"Content-Type": "application/json"}),
elastic=Resource(location="http://localhost:9200", headers={"Content-Type": "application/json"}),
allow_delete=True
)
Expand All @@ -56,6 +58,7 @@ def init(
def main():
app.add_typer(collections, name="collections")
app.add_typer(indices, name="indices")
app.add_typer(persist, name="persist")
app.add_typer(configurations, name="confs")
app()

Expand Down
2 changes: 1 addition & 1 deletion arlas/cli/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

@collections.callback()
def configuration(config: str = typer.Option(help="Name of the ARLAS configuration to use from your configuration file ({}).".format(variables["configuration_file"]))):
variables["arlas"] = config
variables["arlas"] = config

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trailing spaces



@collections.command(help="List collections", name="list")
Expand Down
4 changes: 4 additions & 0 deletions arlas/cli/configurations.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def list_configurations():
def create_configuration(
name: str = typer.Argument(help="Name of the configuration"),
server: str = typer.Option(help="ARLAS Server url"),
persistence: str = typer.Option(default=None, help="ARLAS Persistence url"),
headers: list[str] = typer.Option([], help="header (name:value)"),
elastic: str = typer.Option(default=None, help="dictionary of name/es resources"),
elastic_headers: list[str] = typer.Option([], help="header (name:value)"),
Expand All @@ -42,6 +43,9 @@ def create_configuration(
conf = ARLAS(
server=Resource(location=server, headers=dict(map(lambda h: (h.split(":")[0], h.split(":")[1]), headers))),
allow_delete=allow_delete)
if persistence:
conf.persistence = Resource(location=persistence, headers=dict(map(lambda h: (h.split(":")[0], h.split(":")[1]), headers)))

if auth_token_url:
conf.authorization = AuthorizationService(
token_url=Resource(location=auth_token_url, headers=dict(map(lambda h: (h.split(":")[0], h.split(":")[1]), auth_headers))),
Expand Down
4 changes: 2 additions & 2 deletions arlas/cli/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,10 @@ def delete(
):
config = variables["arlas"]
if not Configuration.settings.arlas.get(config).allow_delete:
print("Error: delete on \"{}\" is not allowed. To allow delete, change your configuration file ({}).".format(config, configuration_file), file=sys.stderr)
print("Error: delete on \"{}\" is not allowed. To allow delete, change your configuration file ({}).".format(config, variables["configuration_file"]), file=sys.stderr)
exit(1)

if typer.confirm("You are about to delete the index '{}' on the '{}' configuration.\n".format(index, config),
if typer.confirm("You are about to delete the index '{}' on '{}' configuration.\n".format(index, config),
prompt_suffix="Do you want to continue (del {} on {})?".format(index, config),
default=False, ):
if config != "local" and config.find("test") < 0:
Expand Down
92 changes: 92 additions & 0 deletions arlas/cli/persist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import json
import typer
import os
import sys
from prettytable import PrettyTable

from arlas.cli.settings import Configuration, Resource
from arlas.cli.service import Service
from arlas.cli.variables import variables

persist = typer.Typer()


@persist.callback()
def configuration(config: str = typer.Option(help="Name of the ARLAS configuration to use from your configuration file ({}).".format(variables["configuration_file"]))):
variables["arlas"] = config


@persist.command(help="Add an entry, returns its ID", name="add")
def add(
file: str = typer.Argument(help="File path"),
zone: str = typer.Argument(help="zone"),
name: str = typer.Option(help="name", default="none"),
reader: list[str] = typer.Option(help="Readers", default=[]),
writer: list[str] = typer.Option(help="writers", default=[]),
encode: bool = typer.Option(help="Encode in BASE64", default=False)
):
config = variables["arlas"]
id = Service.persistence_add_file(config, Resource(location=file), zone=zone, name=name, readers=reader, encode=encode)
print(id)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to add a more meaningful message

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually it is the id to be used for different usages. I thought that if I do not put text around, it would be easier to use in a script (e.g. to get the ID and put it in a different command line). I can add more details, but it will make the work of the developper a bit more complex to extract the id. Your opinion?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't have it in mind, I think it is a good idea to do it like you proposed, otherwise it would have to be on multiple lines to be able to grab the id from the last line maybe, but it makes things complicated. If other methods are to be integrated in scripts such as this one and they have a more complicated output, a "dev" or "short answer" mode with a flag could maybe allow for a more verbose answer as well as a direct one for scripts

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes this (short answer mode flag) is a good idea. For now, I release.



@persist.command(help="Delete an entry", name="delete")
def delete(
id: str = typer.Argument(help="entry identifier")
):
config = variables["arlas"]
if not Configuration.settings.arlas.get(config).allow_delete:
print("Error: delete on \"{}\" is not allowed. To allow delete, change your configuration file ({}).".format(config, variables["configuration_file"]), file=sys.stderr)
exit(1)

if typer.confirm("You are about to delete the entry '{}' on '{}' configuration.\n".format(id, config),
prompt_suffix="Do you want to continue (del {} on {})?".format(id, config),
default=False, ):
if config != "local" and config.find("test") < 0:
if typer.prompt("WARNING: You are not on a test environment. To delete {} on {}, type the name of the configuration ({})".format(id, config, config)) != config:
print("Error: delete on \"{}\" cancelled.".format(config), file=sys.stderr)
exit(1)

Service.persistence_delete(config, id=id)
print("Resource {} deleted.".format(id))


@persist.command(help="Retrieve an entry", name="get")
def get(
id: str = typer.Argument(help="entry identifier")
):
config = variables["arlas"]
print(Service.persistence_get(config, id=id).get("doc_value"))


@persist.command(help="List entries within a zone", name="zone")
def zone(
zone: str = typer.Argument(help="Zone name")
):
config = variables["arlas"]
table = Service.persistence_zone(config, zone=zone)
tab = PrettyTable(table[0], sortby="name", align="l")
tab.add_rows(table[1:])
print(tab)


@persist.command(help="List groups allowed to access a zone", name="groups")
def groups(
zone: str = typer.Argument(help="Zone name")
):
config = variables["arlas"]
table = Service.persistence_groups(config, zone=zone)
tab = PrettyTable(table[0], sortby="group", align="l")
tab.add_rows(table[1:])
print(tab)


@persist.command(help="Describe an entry", name="describe")
def describe(
id: str = typer.Argument(help="entry identifier")
):
config = variables["arlas"]
table = Service.persistence_describe(config, id=id)
tab = PrettyTable(table[0], sortby="metadata", align="l")
tab.add_rows(table[1:])
print(tab)
98 changes: 78 additions & 20 deletions arlas/cli/service.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from enum import Enum
import json
import os
import sys
from alive_progress import alive_bar
import requests

from arlas.cli.settings import Configuration, Resource
from arlas.cli.settings import ARLAS, Configuration, Resource


class RequestException(Exception):
Expand All @@ -13,6 +14,11 @@ def __init__(self, code, message):
self.message = message


class Services(Enum):
arlas_server = "server"
persistence_server = "persistence"


class Service:

def list_collections(arlas: str) -> list[list[str]]:
Expand Down Expand Up @@ -134,7 +140,50 @@ def count_hits(file_path: str) -> int:
with open(file_path) as f:
for line in f:
line_number = line_number + 1
return line_number
return line_number

def persistence_add_file(arlas: str, file: Resource, zone: str, name: str, encode: bool = False, readers: list[str] = [], writers: list[str] = []):
content = Service.__fetch__(file)
url = "/".join(["persist", "resource", zone, name]) + "?" + "&readers=".join(readers) + "&writers=".join(writers)
return Service.__arlas__(arlas, url, post=content, service=Services.persistence_server).get("id")

def persistence_delete(arlas: str, id: str):
url = "/".join(["persist", "resource", "id", id])
return Service.__arlas__(arlas, url, delete=True, service=Services.persistence_server)

def persistence_get(arlas: str, id: str):
url = "/".join(["persist", "resource", "id", id])
return Service.__arlas__(arlas, url, service=Services.persistence_server)

def persistence_zone(arlas: str, zone: str):
url = "/".join(["persist", "resources", zone]) + "?size=10&page=1&order=desc&pretty=false"
table = [["id", "name", "zone", "last_update_date", "owner"]]
entries = Service.__arlas__(arlas, url, service=Services.persistence_server).get("data", [])
for entry in entries:
table.append([entry["id"], entry["doc_key"], entry["doc_zone"], entry["last_update_date"], entry["doc_owner"]])
return table

def persistence_groups(arlas: str, zone: str):
url = "/".join(["persist", "groups", zone])
table = [["group"]]
groups = Service.__arlas__(arlas, url, service=Services.persistence_server)
for group in groups:
table.append([group])
return table

def persistence_describe(arlas: str, id: str):
url = "/".join(["persist", "resource", "id", id])
r = Service.__arlas__(arlas, url, service=Services.persistence_server)
table = [["metadata", "value"]]
table.append(["ID", r.get("id")])
table.append(["name", r.get("doc_key")])
table.append(["zone", r.get("doc_zone")])
table.append(["last_update_date", r.get("last_update_date")])
table.append(["owner", r.get("doc_owner")])
table.append(["organization", r.get("doc_organization")])
table.append(["ispublic", r.get("ispublic")])
table.append(["updatable", r.get("updatable")])
return table

def __index_bulk__(arlas: str, index: str, bulk: []):
data = os.linesep.join([json.dumps(line) for line in bulk]) + os.linesep
Expand Down Expand Up @@ -185,29 +234,38 @@ def __get_fields__(origin: list[str], properties: dict[str:dict]):
fields.append([".".join(o), type])
return fields

def __arlas__(arlas: str, suffix, post=None, put=None, delete=None):
def __arlas__(arlas: str, suffix, post=None, put=None, delete=None, service=Services.arlas_server):
configuration: ARLAS = Configuration.settings.arlas.get(arlas, {})
if configuration is None:
print("Error: arlas configuration ({}) not found among [{}] for {}.".format(arlas, ", ".join(Configuration.settings.arlas.keys()), service.name), file=sys.stderr)
exit(1)
if service == Services.arlas_server:
endpoint: Resource = configuration.server
else:
endpoint: Resource = configuration.persistence
__headers__ = Configuration.settings.arlas.get(arlas).server.headers.copy()
if Configuration.settings.arlas.get(arlas).authorization is not None:
__headers__["Authorization"] = "Bearer " + Service.__get_token__(arlas)
endpoint = Configuration.settings.arlas.get(arlas)
if endpoint is None:
print("Error: arlas configuration ({}) not found among [{}].".format(arlas, ", ".join(Configuration.settings.arlas.keys())), file=sys.stderr)
exit(1)
url = "/".join([endpoint.server.location, suffix])
if post:
r = requests.post(url, data=post, headers=__headers__)
else:
if put:
r = requests.put(url, data=put, headers=__headers__)
url = "/".join([endpoint.location, suffix])
try:
if post:
r = requests.post(url, data=post, headers=__headers__)
else:
if delete:
r = requests.delete(url, headers=__headers__)
if put:
r = requests.put(url, data=put, headers=__headers__)
else:
r = requests.get(url, headers=__headers__)
if r.ok:
return r.json()
else:
print("Error: request failed with status {}: {}".format(str(r.status_code), r.content), file=sys.stderr)
if delete:
r = requests.delete(url, headers=__headers__)
else:
r = requests.get(url, headers=__headers__)
if r.ok:
return r.json()
else:
print("Error: request failed with status {}: {}".format(str(r.status_code), r.content), file=sys.stderr)
print(" url: {}".format(url), file=sys.stderr)
exit(1)
except Exception as e:
print("Error: request failed: {}".format(e), file=sys.stderr)
print(" url: {}".format(url), file=sys.stderr)
exit(1)

Expand Down
1 change: 1 addition & 0 deletions arlas/cli/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class AuthorizationService(BaseModel):


class ARLAS(BaseModel):
persistence: Resource | None = Field(title="ARLAS Persistence Server", default=None)
server: Resource = Field(title="ARLAS Server")
authorization: AuthorizationService | None = Field(default=None, title="Keycloak URL")
elastic: Resource | None = Field(default=None, title="dictionary of name/es resources")
Expand Down
27 changes: 22 additions & 5 deletions docker/dc-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ services:
- arlas-net
logging:
driver: ${DOCKER_LOGGING_DRIVER:-json-file}
options:
tag: "urms"
healthcheck:
test: ["CMD","java","HttpHealthcheck.java","http://localhost:9999/admin/healthcheck"]
interval: 5s
Expand All @@ -51,7 +49,7 @@ services:
- ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
- discovery.type=single-node
- cluster.name=arlas-es-cluster
- node.name=urms-data-node-1
- node.name=data-node-1
- "ES_JAVA_OPTS=-Xms1g -Xmx1g"
- xpack.security.enabled=false
- tracing.apm.enabled=false
Expand All @@ -68,10 +66,29 @@ services:
- arlas-net
logging:
driver: ${DOCKER_LOGGING_DRIVER:-json-file}
options:
tag: "urms"
healthcheck:
test: "curl -s --user ${ELASTIC_USER}:${ELASTIC_PASSWORD} -X GET http://localhost:9200/_cluster/health?pretty | grep status | grep -q '\\(green\\|yellow\\)'"
interval: 10s
timeout: 10s
retries: 24

arlas-persistence:
image: ${ARLAS_PERSISTENCE_SERVER_VERSION}
container_name: arlas-persistence
restart: always
environment:
- ARLAS_PERSISTENCE_HIBERNATE_DRIVER=org.postgresql.Driver
- ARLAS_PERSISTENCE_LOCAL_FOLDER=/tmp/
ports:
- "9997:9997"
expose:
- "9997"
networks:
- arlas-net
logging:
driver: ${DOCKER_LOGGING_DRIVER:-json-file}
healthcheck:
test: ["CMD","java","HttpHealthcheck.java","http://localhost:9997/arlas_persistence_server/swagger.json"]
interval: 5s
timeout: 10s
retries: 3
1 change: 1 addition & 0 deletions docs/docs/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Whether you are at the top level:
│ collections │
│ confs │
│ indices │
│ persist │
╰────────────────────────────────────────────────────────────────────╯
```

Expand Down
6 changes: 6 additions & 0 deletions docs/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ __ARLAS Exploration__ is an Open Source software for exploring and analysing Geo
- list collections
- describe a collection
- delete a collection
- managing ARLAS persistence
- create a entry in a zone
- list entries within a zone
- describe an entry
- delete an entry
- get the groups accessing a zone
- managing configurations
- register an ARLAS/elasticsearch configuration, with headers and authentication parameters
- delete a configuration
Expand Down
Loading
Loading