diff --git a/dbrepo-auth-service/listeners/target/create-event-listener.jar b/dbrepo-auth-service/listeners/target/create-event-listener.jar index 9fa1288598118b348a3341e591558b66152c43b3..9defdad744bc4cb391b2ea104502106d0011f6af 100644 Binary files a/dbrepo-auth-service/listeners/target/create-event-listener.jar and b/dbrepo-auth-service/listeners/target/create-event-listener.jar differ diff --git a/dbrepo-dashboard-service/app.py b/dbrepo-dashboard-service/app.py index 94f838e2b4ea6a8da071c2e7f18c2a2805d28a39..fe5d8218b009503afad67c45d8d7d69df7616322 100644 --- a/dbrepo-dashboard-service/app.py +++ b/dbrepo-dashboard-service/app.py @@ -216,7 +216,7 @@ def create_dashboard(): logging.debug( f"endpoint create dashboard, is_public={is_public}, is_schema_public={is_schema_public}, owner_username={owner_username}") try: - db = dashboard.create(request.json['database_name'], request.json['owner_username']) + db = dashboard.create(request.json['database_name']) access.update_anonymous_read_access(db['uid'], is_public, is_schema_public) return Response(dumps(db)), 201, headers except GrafanaClientError as e: diff --git a/dbrepo-dashboard-service/dashboard.py b/dbrepo-dashboard-service/dashboard.py index a170e63dbb445dc5f7ef943fb7ed9f424c10bf1b..8ff4b47c49ba74f3ee27efb478767254f01fdba5 100644 --- a/dbrepo-dashboard-service/dashboard.py +++ b/dbrepo-dashboard-service/dashboard.py @@ -1,37 +1,30 @@ import logging import os -from dbrepo.api.dto import Database, View +from dbrepo.api.dto import Database +from dbrepo.api.exceptions import MalformedError +from grafana_client.client import GrafanaClientError +from api.exceptions import DashboardNotFound from clients.grafana_client import GrafanaClient - -statistics_row_title = '${view_id}' +from panel import get_panels base_url = os.getenv('BASE_URL', 'http://localhost') datasource_uid = os.getenv('JSON_DATASOURCE_NAME', 'dbrepojson0') -def map_link(title: str, url: str) -> dict: +def map_link(title: str, url: str, icon: str = 'info') -> dict: return dict(targetBlank=True, asDropdown=False, includeVars=False, keepTime=False, tags=[], type='link', - icon='info', + icon=icon, title=title, url=url) -def map_statistics_row(dashboard: dict) -> dict | None: - filtered_panels = [panel for panel in dashboard['panels'] if - panel['type'] == 'row' and panel['title'] == statistics_row_title] - if len(filtered_panels) == 0: - logging.warning(f"Failed to find statistics row title {statistics_row_title} in: {filtered_panels}") - return None - return filtered_panels[0] - - def map_links(database: Database) -> [dict]: links = [] if len(database.identifiers) > 0: @@ -41,275 +34,30 @@ def map_links(database: Database) -> [dict]: return links -def map_templating(database: Database) -> dict: - options = [dict(selected=False, - text=view.name, - value=str(view.id)) for view in database.views] - selected = dict(selected=True, - text=[view.name for view in database.views], - value=[str(view.id) for view in database.views]) - datasource = dict(uid=datasource_uid, - type='yesoreyeram-infinity-datasource') - return dict(list=[dict(description='', - name='view_id', - hide=0, - includeAll=True, - multi=True, - datasource=datasource, - refresh=1, - regex='', - sort=0, - definition='dbrepo-json- (infinity) json', - query=dict(queryType='infinity', - query='', - infinityQuery=dict(format='table', - filters=[], - parser='backend', - refId='variable', - root_selector='', - source='url', - type='json', - url=f"/api/database/{database.id}/view", - columns=[dict(selector='id', - text='value', - type='string'), - dict( - selector='internal_name', - text='name', - type='string')], - url_options=dict(data='', - method='GET'))), - label='Datasource', - skipUrlSync=False, - type='query', - current=selected, - options=options)]) - - -def map_timeseries_panel(database: Database, view: View) -> dict: - datasource = dict(uid=datasource_uid, - type='yesoreyeram-infinity-datasource') - return dict( - title=view['name'], - type='timeseries', - datasource=datasource, - targets=[dict(datasource=datasource, - format='table', - global_query_id='', - hide=False, - refId='A', - root_selector='', - source='url', - type='json', - url=f"/api/database/{database['id']}/view/{view['id']}", - url_options=dict(data='', - method='GET'))], - gridPos=dict(h=8, - w=12, - x=0, - y=0), - options=dict(legend=dict(displayMode='list', - placement='bottom', - showLegend=True), - tooltip=dict(mode='single', - sort='none')), - fieldConfig=dict( - defaults=dict(color=dict(mode='palette-classic'), - custom=dict( - axisBorderShow=False, - axisCenteredZero=False, - axisColorMode='text', - axisLabel='', - axisPlacement='auto', - barAlignment=0, - drawStyle='line', - fillOpacity=0, - gradientMode='none', - hideFrom=dict(legend=False, - tooltip=False, - viz=False), - insertNulls=False, - lineInterpolation='linear', - lineWidth=1, - pointSize=5, - scaleDistribution=dict(type='linear'), - showPoints='auto', - spanNulls=False, - stacking=dict(group='A', - mode='none'), - thresholdsStyle=dict(mode='absolute'))))) - - -def map_statistics_panel(database_id: str, view: View) -> dict: - datasource = dict(uid=datasource_uid, - type='yesoreyeram-infinity-datasource') - return dict( - title=view.name, - type='table', - datasource=datasource, - targets=[dict(datasource=datasource, - columns=[], - filters=[], - format='table', - global_query_id='', - hide=False, - refId='A', - root_selector='', - source='url', - type='json', - url=f"/api/database/{database_id}/view/{view.id}/data", - url_options=dict(data='', - method='GET'))], - options=dict(cellHeight="sm", - showHeader=True, - footer=dict(countRows=False, - fields="", - reducer=["sum"], - show=False)), - gridPos=dict(h=8, - w=12, - x=12, - y=0), - transformations=dict(id="organize", - options=dict(excludeByName=dict(), - includeByName=dict(), - indexByName=dict( - HEADER_AVG=3, - HEADER_COL=0, - HEADER_STDDEV=4, - HEADER_MAX=2, - HEADER_MIN=1))), - fieldConfig=dict(defaults=dict(custom=dict(align="auto", - filterable="true", - cellOptions=dict(type="auto"), - inspect=False), - mappings=[], - thresholds=dict(mode="absolute", - steps=[dict(color="green", - value=None), - dict(color="red", - value=80) - ])), - overrides=[dict(matcher=dict(id="byName", - options="HEADER_COL"), - properties=[dict(id="custom.align", - value="center")]), - dict(matcher=dict(id="byName", - options="HEADER_MIN"), - properties=[dict(id="custom.width", - value=115)]), - dict(matcher=dict(id="byName", - options="HEADER_MAX"), - properties=[dict(id="custom.width", - value=115)]), - dict(matcher=dict(id="byName", - options="HEADER_AVG"), - properties=[dict(id="custom.width", - value=115)]), - dict(matcher=dict(id="byName", - options="HEADER_STDDEV"), - properties=[dict(id="custom.width", - value=115)]) - ])) - - -def map_overview_panel(database_id: str) -> dict: - datasource = dict(uid=datasource_uid, - type='yesoreyeram-infinity-datasource') - return dict(title='Preview', - type='table', - fieldConfig=dict( - defaults=dict( - color=dict(mode='palette-classic'), - custom=dict(axisBorderShow=False, - axisCenteredZero=False, - axisColorMode='text', - axisLabel='', - axisPlacement='auto', - barAlignment=0, - drawStyle='line', - fillOpacity=0, - gradientMode='none', - hideFrom=dict( - legend=False, - tooltip=False, - viz=False), - insertNulls=False, - lineInterpolation='linear', - lineWidth=1, - pointSize=5, - scaleDistribution=dict( - type='linear'), - showPoints='auto', - spanNulls=False, - stacking=dict(group='A', - mode='none'), - thresholdsStyle=dict( - mode='off'))), - overrides=[]), - options=dict(legend=dict(displayMode='list', - placement='bottom', - showLegend=True, - calcs=[]), - tooltip=dict(mode='single', - sort='none')), - targets=[dict(format='json', - columns=[], - datasource=datasource, - filters=[], - global_query_id='', - refId='A', - root_selector='', - source='url', - type='json', - url='/api/database/' + database_id + '/view/${view_id}/data', - url_options=dict(data='', - method='GET'))], - datasource=datasource, - gridPos=dict(h=4, - w=12, - x=0, - y=0)) - - -def map_row() -> dict: - datasource = dict(uid=datasource_uid, - type='yesoreyeram-infinity-datasource') - return dict(collapsed=False, - repeat='view_id', - repeatDirection='h', - title=statistics_row_title, - type='row', - panels=[], - targets=[dict(refId='A', - datasource=datasource)], - gridPos=dict(h=1, - w=24, - x=0, - y=0)) - - -def map_panels(dashboard: dict, database: Database) -> [dict]: - if map_statistics_row(dashboard) is None: - dashboard['panels'].append(map_row()) - dashboard['panels'].append(map_overview_panel(database.id)) - for view in database.views: - dashboard['panels'].append(map_statistics_panel(database.id, view)) - return dashboard['panels'] - - def find(uid: str): + """ + Finds a dashboard with the given uid. + + @return The dashboard, if successful. Otherwise, `None`. + """ + if uid is None: + return None grafana = GrafanaClient().connect() - return grafana.dashboard.get_dashboard(uid) + try: + return grafana.dashboard.get_dashboard(uid) + except GrafanaClientError: + logging.warning(f"Failed to find dashboard with uid: {uid}") + return None def create(database_name: str, uid: str = '') -> dict: grafana = GrafanaClient().connect() dashboard = dict(uid=uid, title=f'{database_name} Overview', - tags=['dbrepo'], + tags=['managed'], timezone='browser', - fiscalYearStartMonth=1, + refresh='30m', + preload=False, panels=[]) dashboard['panels'] = [] payload = dict(folderUid='', @@ -327,16 +75,20 @@ def delete(uid: str) -> None: def update(database: Database) -> None: grafana = GrafanaClient().connect() - dashboard = find(database.dashboard_uid)['dashboard'] + dashboard = find(database.dashboard_uid) + if dashboard is None: + raise DashboardNotFound(f'Dashboard {database.dashboard_uid} not found') + dashboard = dashboard['dashboard'] # update metadata + if not database.is_dashboard_enabled and 'managed' in dashboard['tags']: + dashboard['tags'].remove('managed') if len(database.identifiers) > 0 and len(database.identifiers[0].titles) > 0: dashboard['title'] = database.identifiers[0].titles[0].title if len(database.identifiers) > 0 and len(database.identifiers[0].descriptions) > 0: dashboard['description'] = database.identifiers[0].descriptions[0].description dashboard['links'] = map_links(database) - dashboard['templating'] = map_templating(database) # update panels - dashboard['panels'] = map_panels(dashboard, database) + dashboard['panels'] = get_panels(dashboard, database) payload = dict(folderUid='', overwrite=True, dashboard=dashboard) diff --git a/dbrepo-dashboard-service/init/dashboard.py b/dbrepo-dashboard-service/init/dashboard.py index a7c03894e4a2194f23800d4284323254c233a93e..8ff4b47c49ba74f3ee27efb478767254f01fdba5 100644 --- a/dbrepo-dashboard-service/init/dashboard.py +++ b/dbrepo-dashboard-service/init/dashboard.py @@ -50,7 +50,7 @@ def find(uid: str): return None -def create(database_name: str, uid: str = '') -> str: +def create(database_name: str, uid: str = '') -> dict: grafana = GrafanaClient().connect() dashboard = dict(uid=uid, title=f'{database_name} Overview', @@ -65,7 +65,7 @@ def create(database_name: str, uid: str = '') -> str: dashboard=dashboard) dashboard = grafana.dashboard.update_dashboard(payload) logging.info(f"Created dashboard with uid: {dashboard['uid']}") - return dashboard['uid'] + return dashboard def delete(uid: str) -> None: diff --git a/dbrepo-dashboard-service/init/tests/test_integration_dashboard.py b/dbrepo-dashboard-service/init/tests/test_integration_dashboard.py index 1ae27faae084df1f8ad9463d410ff244022fd8d9..da6071f13bf6a056c4cd910a3ff63a4444521446 100644 --- a/dbrepo-dashboard-service/init/tests/test_integration_dashboard.py +++ b/dbrepo-dashboard-service/init/tests/test_integration_dashboard.py @@ -108,7 +108,7 @@ class DashboardIntegrationTest(unittest.TestCase): def test_create_succeeds(self): # test - dashboard.create('some_database', 'foobar') + dashboard.create('some_database') def test_create_with_uid_succeeds(self): # test diff --git a/dbrepo-data-service/Dockerfile b/dbrepo-data-service/Dockerfile index 9edf1375fb6c47f63dbd45f26bba0b3a6fe15255..233e1f43c49d648c0bc562b6be406fe860871e41 100644 --- a/dbrepo-data-service/Dockerfile +++ b/dbrepo-data-service/Dockerfile @@ -1,5 +1,5 @@ ###### FIRST STAGE ###### -FROM dbrepo-metadata-service:build AS dependency +FROM dbrepo-core:build AS dependency LABEL org.opencontainers.image.authors="martin.weise@tuwien.ac.at" ###### SECOND STAGE ###### @@ -10,7 +10,7 @@ COPY ./pom.xml ./ RUN mvn -fn dependency:go-offline -COPY --from=dependency /root/.m2/repository/at/tuwien /root/.m2/repository/at/tuwien +COPY --from=dependency /root/.m2/repository/at/ac/tuwien/ifs/dbrepo /root/.m2/repository/at/ac/tuwien/ifs/dbrepo COPY ./querystore ./querystore COPY ./report ./report @@ -28,7 +28,7 @@ RUN apk add --no-cache curl bash jq WORKDIR /app -RUN adduser -S -u 1001 data-service +RUN adduser -S -u 1001 dbrepo USER 1001 diff --git a/dbrepo-metadata-service/Dockerfile b/dbrepo-metadata-service/Dockerfile index fa92b799eeaac75f9daea7b5a1eec11560b04647..9bcf0d75c75ec6cfba7d49a05b08a48448e0064d 100644 --- a/dbrepo-metadata-service/Dockerfile +++ b/dbrepo-metadata-service/Dockerfile @@ -1,32 +1,27 @@ ###### FIRST STAGE ###### +FROM dbrepo-core:build AS dependency +LABEL org.opencontainers.image.authors="martin.weise@tuwien.ac.at" + +###### SECOND STAGE ###### FROM maven:3-amazoncorretto-17 AS build LABEL org.opencontainers.image.authors="martin.weise@tuwien.ac.at" COPY ./pom.xml ./ -COPY ./api/pom.xml ./api/ -COPY ./entities/pom.xml ./entities/ -COPY ./oai/pom.xml ./oai/ -COPY ./report/pom.xml ./report/ -COPY ./repositories/pom.xml ./repositories/ -COPY ./rest-service/pom.xml ./rest-service/ -COPY ./services/pom.xml ./services/ -COPY ./test/pom.xml ./test/ - -RUN mvn dependency:go-offline - -COPY ./api ./api -COPY ./entities ./entities + +RUN mvn -fn dependency:go-offline + +COPY --from=dependency /root/.m2/repository/at/ac/tuwien/ifs/dbrepo /root/.m2/repository/at/ac/tuwien/ifs/dbrepo + COPY ./oai ./oai COPY ./report ./report COPY ./repositories ./repositories COPY ./rest-service ./rest-service COPY ./services ./services -COPY ./test ./test # Make sure it compiles -RUN mvn clean install -DskipTests +RUN mvn -fn clean package -DskipTests -###### SECOND STAGE ###### +###### THIRD STAGE ###### FROM amazoncorretto:17-alpine3.19 AS runtime LABEL org.opencontainers.image.authors="martin.weise@tuwien.ac.at" @@ -34,6 +29,8 @@ RUN apk add --no-cache curl bash jq WORKDIR /app +RUN adduser -S -u 1001 dbrepo + USER 1001 COPY --from=build --chown=1001 ./rest-service/target/dbrepo-metadata-service-rest-service-*.jar ./metadata-service.jar @@ -41,4 +38,6 @@ COPY --from=build --chown=1001 ./rest-service/target/dbrepo-metadata-service-res # non-root port EXPOSE 8080 -ENTRYPOINT ["java", "-Dlog4j2.formatMsgNoLookups=true", "-jar", "./metadata-service.jar"] +ENV JAVA_OPTS="-Dlog4j2.formatMsgNoLookups=true" + +ENTRYPOINT exec java $JAVA_OPTS -jar ./metadata-service.jar \ No newline at end of file diff --git a/helm/dbrepo/files/create-event-listener.jar b/helm/dbrepo/files/create-event-listener.jar index 9fa1288598118b348a3341e591558b66152c43b3..9defdad744bc4cb391b2ea104502106d0011f6af 100644 Binary files a/helm/dbrepo/files/create-event-listener.jar and b/helm/dbrepo/files/create-event-listener.jar differ diff --git a/lib/java/dbrepo-core/Dockerfile b/lib/java/dbrepo-core/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..8a6348bbcef25836c318e41ae226295f90549a66 --- /dev/null +++ b/lib/java/dbrepo-core/Dockerfile @@ -0,0 +1,12 @@ +###### FIRST STAGE ###### +FROM maven:3-amazoncorretto-17 AS build +LABEL org.opencontainers.image.authors="martin.weise@tuwien.ac.at" + +COPY ./pom.xml ./ + +RUN mvn dependency:go-offline + +COPY ./src/ ./src/ + +# Make sure it compiles +RUN mvn clean install -DskipTests \ No newline at end of file diff --git a/make/build.mk b/make/build.mk index 96cd6392d504c519061e5eb9d6f43b8e0ff57921..48e9212a6ac44f9d64c93fb981fa197bc407cd4e 100644 --- a/make/build.mk +++ b/make/build.mk @@ -2,8 +2,9 @@ .PHONY: build-images build-images: ## Build Docker images. - docker build --network=host -t dbrepo-metadata-service:build --target build dbrepo-metadata-service + docker build --network=host -t dbrepo-core:build --target build ./lib/java/dbrepo-core docker build --network=host -t dbrepo-data-service:build --target build dbrepo-data-service + docker build --network=host -t dbrepo-metadata-service:build --target build dbrepo-metadata-service docker compose build --parallel .PHONY: build-data-service