diff --git a/k8s/lifemonitor-web/templates/configmap.yaml b/k8s/lifemonitor-web/templates/configmap.yaml
index 2e83aea4..50ed5aef 100644
--- a/k8s/lifemonitor-web/templates/configmap.yaml
+++ b/k8s/lifemonitor-web/templates/configmap.yaml
@@ -12,6 +12,8 @@ data:
{
"apiBaseUrl": "https://{{ .Values.externalServerName }}/api",
"appDomain": "{{ .Values.externalServerName }}",
- "socketBaseUrl": "https://{{ .Values.externalServerName }}"{{ if .Values.backend.clientId }},{{ end }}
- {{ if .Values.backend.clientId }}"clientId": "{{ .Values.backend.clientId }}"{{ end }}
+ "socketBaseUrl": "https://{{ .Values.externalServerName }}"
+ {{- if .Values.backend.clientId }}, "clientId": "{{ .Values.backend.clientId }}"{{ end }}
+ {{- if .Values.maintenanceMode.enabled }}, "maintenanceMode": "true"{{ end }}
+ {{- if .Values.maintenanceMode.message }}, "maintenanceMessage":"{{.Values.maintenanceMode.message}}"{{ end }}
}
diff --git a/k8s/lifemonitor-web/templates/deployment.yaml b/k8s/lifemonitor-web/templates/deployment.yaml
index 2160716f..2d8ab7dd 100644
--- a/k8s/lifemonitor-web/templates/deployment.yaml
+++ b/k8s/lifemonitor-web/templates/deployment.yaml
@@ -17,6 +17,8 @@ spec:
helm.sh/chart: {{ include "lifemonitor-web.chart" . }}
template:
metadata:
+ annotations:
+ checksum/frontend-config: {{ include (print $.Template.BasePath "/nginx.configmap.yml") . | sha256sum }}
labels:
app.kubernetes.io/name: {{ include "lifemonitor-web.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
@@ -31,7 +33,7 @@ spec:
- name: http
containerPort: 4200
protocol: TCP
- {{ if .Values.monitoring.enabled }}
+ {{- if .Values.monitoring.enabled }}
- name: metrics
containerPort: 9090
protocol: TCP
@@ -46,8 +48,10 @@ spec:
- name: nginx-config
mountPath: /etc/nginx/conf.d/webapp.prod.conf
subPath: app.conf
- - name: logs-storage
+ - name: nginx-logs
mountPath: /var/log/nginx
+ - name: nginx-run
+ mountPath: /var/run/nginx
{{ if .Values.extraVolumeMounts }}
{{- toYaml .Values.extraVolumeMounts | nindent 12 }}
{{- end }}
@@ -62,6 +66,24 @@ spec:
# port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
+ {{- if .Values.monitoring.enabled }}
+ - name: promtail
+ image: grafana/promtail:master
+ args:
+ - "-config.file=/etc/promtail/promtail.yaml" # Found in the ConfigMap
+ resources:
+ requests:
+ memory: "128Mi"
+ cpu: "100m"
+ limits:
+ memory: "256Mi"
+ cpu: "200m"
+ volumeMounts:
+ - name: promtail-config
+ mountPath: /etc/promtail
+ - name: nginx-logs
+ mountPath: /var/log/nginx
+ {{- end }}
volumes:
- name: frontend-config
configMap:
@@ -71,8 +93,16 @@ spec:
configMap:
name: {{ include "lifemonitor-web.fullname" . }}-nginx-config
defaultMode: 0644
- - name: logs-storage
+ - name: nginx-logs
emptyDir: {}
+ - name: nginx-run
+ emptyDir: {}
+ {{- if .Values.monitoring.enabled }}
+ - name: promtail-config
+ configMap:
+ name: {{ include "lifemonitor-web.fullname" . }}-promtail-configmap
+ defaultMode: 0644
+ {{- end }}
{{ if .Values.extraVolumes }}
{{- toYaml .Values.extraVolumes | nindent 8 }}
{{- end }}
diff --git a/k8s/lifemonitor-web/templates/nginx.configmap.yml b/k8s/lifemonitor-web/templates/nginx.configmap.yml
index a1281307..839aa16c 100644
--- a/k8s/lifemonitor-web/templates/nginx.configmap.yml
+++ b/k8s/lifemonitor-web/templates/nginx.configmap.yml
@@ -47,10 +47,6 @@ data:
server {
server_name {{ .Values.externalServerName }} {{ include "lifemonitor-web.name" . }}-metrics.default {{ include "lifemonitor-web.name" . }}-metrics.default.svc.cluster.local;
- # save logs here
- access_log /var/log/nginx/metrics.access.log extended;
- error_log /var/log/nginx/metrics.error.log;
-
proxy_read_timeout 600;
proxy_connect_timeout 600;
proxy_send_timeout 600;
@@ -69,9 +65,12 @@ data:
# force HTTP traffic to HTTPS
# error_page 497 http://$host:4200$request_uri;
- # expose extended metrics
+ # expose standard nginx metrics
location = /metrics {
stub_status on;
+
+ access_log off;
+ log_not_found off;
}
}
{{- end -}}
@@ -97,8 +96,46 @@ data:
# force HTTP traffic to HTTPS
error_page 497 http://$host:4200$request_uri;
+ # define error pages
+ error_page 404 /error/404;
+ error_page 405 /error/405;
+ error_page 429 /error/429;
+ error_page 500 /error/500;
+ error_page 502 /error/502;
+
+ # location for error pages
+ location ~ ^/error {
+ # rewrite request uri to point to the api
+ rewrite /error/(.*) /api/error/$1 break;
+
+ # disable redirects
+ proxy_redirect off;
+
+ # rewrite headers
+ proxy_pass_header Server;
+ proxy_set_header X-Real-IP $http_x_forwarded_for;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Scheme $scheme;
+ proxy_set_header Host $http_host;
+ proxy_set_header Cookie $http_cookie;
+ proxy_set_header X-Requested-With $http_x_requested_with;
+
+ # various proxy settings
+ proxy_connect_timeout 600;
+ proxy_read_timeout 600;
+ proxy_send_timeout 600;
+ #proxy_intercept_errors on;
+
+ # set uppstream
+ proxy_pass https://api;
+
+ # log errors using the extended format
+ access_log /var/log/nginx/http.error.log custom_format;
+ }
+
# disable standard nginx metrics
location = /nginx-metrics {
+ # stub_status on;
deny all;
}
@@ -155,7 +192,6 @@ data:
proxy_connect_timeout 600;
proxy_read_timeout 600;
proxy_send_timeout 600;
- #proxy_intercept_errors on;
}
location ~ ^/(account|oauth2|jobs|github|integrations) {
@@ -177,7 +213,6 @@ data:
proxy_connect_timeout 600;
proxy_read_timeout 600;
proxy_send_timeout 600;
- #proxy_intercept_errors on;
}
location /socket.io/ {
@@ -223,15 +258,15 @@ data:
pcre_jit on;
# logs
- pid /var/run/openresty/nginx.pid;
- error_log /var/log/nginx/nginx.error.log crit;
+ pid /var/log/nginx/nginx.pid;
+ error_log /var/log/nginx/nginx.error.log warn;
events {
worker_connections 1024;
}
http {
-
+
include mime.types;
default_type application/octet-stream;
@@ -240,27 +275,35 @@ data:
# When the use of underscores is disabled, request header fields whose names contain underscores are marked as invalid and become subject to the ignore_invalid_headers directive.
# underscores_in_headers off;
- server_names_hash_bucket_size 128;
- server_names_hash_max_size 512;
-
+ # fix issues with large client headers
proxy_headers_hash_max_size 512;
proxy_headers_hash_bucket_size 128;
- # Define a verbose log format
- log_format extended
- '[$http_x_forwarded_for] '
- '"$request" $status ($body_bytes_sent bytes) - "$http_referer", "$http_user_agent", "$http_x_request_domain"';
-
+ # Define the log format
+ log_format custom_format '$http_x_client_ip - $remote_user [$time_local] '
+ '"$request" $status $body_bytes_sent '
+ '"$http_referer" "$http_user_agent" - '
+ '$http_x_forwarded_for $request_time $request_length - '
+ '$connection $connection_requests $pipe - '
+ '$upstream_addr $upstream_status $upstream_cache_status '
+ '$upstream_response_time $upstream_response_length';
+
# Configure Log files
- access_log /usr/local/openresty/nginx/logs/access.log extended;
- # error_log /usr/local/openresty/nginx/logs/error.log warn;
+ access_log /var/log/nginx/access.log custom_format;
+ error_log /var/log/nginx/error.log warn;
+
+
+ # See Move default writable paths to a dedicated directory (#119)
+ # https://github.com/openresty/docker-openresty/issues/119
+ client_body_temp_path /var/run/nginx/nginx-client-body;
+ proxy_temp_path /var/run/nginx/nginx-proxy;
+ fastcgi_temp_path /var/run/nginx/nginx-fastcgi;
+ uwsgi_temp_path /var/run/nginx/nginx-uwsgi;
+ scgi_temp_path /var/run/nginx/nginx-scgi;
+
- # Extra settings
- client_body_temp_path /var/run/openresty/nginx-client-body;
- proxy_temp_path /var/run/openresty/nginx-proxy;
- fastcgi_temp_path /var/run/openresty/nginx-fastcgi;
- uwsgi_temp_path /var/run/openresty/nginx-uwsgi;
- scgi_temp_path /var/run/openresty/nginx-scgi;
+ # Defines a file that will store the process ID of the main process.
+ server_names_hash_bucket_size 128;
sendfile on;
#tcp_nopush on;
@@ -275,4 +318,3 @@ data:
# Don't reveal OpenResty version to clients.
# server_tokens off;
}
-
diff --git a/k8s/lifemonitor-web/templates/promtail.configmap.yaml b/k8s/lifemonitor-web/templates/promtail.configmap.yaml
new file mode 100644
index 00000000..02ed4e86
--- /dev/null
+++ b/k8s/lifemonitor-web/templates/promtail.configmap.yaml
@@ -0,0 +1,41 @@
+{{- if .Values.monitoring.loki.enabled }}
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: "{{ include "lifemonitor-web.fullname" . }}-promtail-configmap"
+data:
+ promtail.yaml: |
+ server:
+ http_listen_port: 9080
+ grpc_listen_port: 0
+ log_level: "debug"
+ positions:
+ filename: /tmp/positions.yaml
+ clients: # Specify target
+ - url: {{ $.Values.monitoring.loki.url }}/loki/api/v1/push
+ scrape_configs:
+ - job_name: "lifemonitor-app-proxy-logger"
+ static_configs:
+ - targets:
+ - localhost
+ labels:
+ app: "lifemonitor-webapp"
+ component: "{{$.Release.Name}}-proxy"
+ environment: "{{$.Release.Namespace}}"
+ format: "extended"
+ level: "INFO"
+ __path__: /var/log/nginx/access.log
+ - targets:
+ - localhost
+ labels:
+ app: "lifemonitor-webapp"
+ component: "{{ $.Release.Name }}-proxy"
+ environment: "{{ $.Release.Namespace }}"
+ format: "extended"
+ level: "ERROR"
+ __path__: /var/log/nginx/*error.log
+ pipeline_stages:
+ - drop:
+ expression: ".*(DEBUG|health|heartbeat).*"
+{{- end }}
\ No newline at end of file
diff --git a/k8s/lifemonitor-web/values.yaml b/k8s/lifemonitor-web/values.yaml
index 098541ad..c8018403 100644
--- a/k8s/lifemonitor-web/values.yaml
+++ b/k8s/lifemonitor-web/values.yaml
@@ -9,15 +9,20 @@ image:
tag: latest
pullPolicy: IfNotPresent
-nameOverride: ''
-fullnameOverride: ''
+nameOverride: ""
+fullnameOverride: ""
-externalServerName: 'localhost'
+externalServerName: "localhost"
+
+# Manage maintenance mode
+maintenanceMode:
+ enabled: false
+ # message: "The service is currently under maintenance. Please try again later."
# Setting for the LifeMonitor Backend API
backend:
- apiUrl: ''
- clientId: ''
+ apiUrl: ""
+ clientId: ""
service:
type: NodePort
@@ -29,6 +34,19 @@ monitoring:
enabled: false
prometheus:
namespace: kube-prometheus-stack
+ loki:
+ enabled: false
+ url: http://loki:3100
+ exporter:
+ image: grafana/promtail:main-60ea954
+ imagePullPolicy: IfNotPresent
+ resources:
+ requests:
+ memory: 128Mi
+ cpu: 0.1
+ limits:
+ memory: 256Mi
+ cpu: 0.2
ingress:
enabled: false
@@ -39,24 +57,30 @@ ingress:
hosts:
- host: localhost
paths:
- - '/'
+ - "/"
# configure TLS for the ingress
tls:
- secretName: lifemonitor-web-tls
hosts:
- localhost
-resources: {}
-# We usually recommend not to specify default resources and to leave this as a conscious
-# choice for the user. This also increases chances charts run on environments with little
-# resources, such as Minikube. If you do want to specify resources, uncomment the following
-# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
-# limits:
-# cpu: 100m
-# memory: 128Mi
-# requests:
-# cpu: 100m
-# memory: 128Mi
+resources:
+ # We usually recommend not to specify default resources and to leave this as a conscious
+ # choice for the user. This also increases chances charts run on environments with little
+ # resources, such as Minikube. If you do want to specify resources, uncomment the following
+ # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+ # limits:
+ # cpu: 100m
+ # memory: 128Mi
+ # requests:
+ # cpu: 100m
+ # memory: 128Mi
+ requests:
+ memory: "4096Mi"
+ cpu: "1000m"
+ limits:
+ memory: "7680Mi"
+ cpu: "1800m"
nodeSelector: {}
diff --git a/k8s/values.yaml b/k8s/values.yaml
index 89fed179..4e117107 100644
--- a/k8s/values.yaml
+++ b/k8s/values.yaml
@@ -14,6 +14,11 @@ fullnameOverride: ''
externalServerName: 'localhost'
+# Manage maintenance mode
+maintenanceMode:
+ enabled: false
+ # message: "The service is currently under maintenance. Please try again later."
+
# Setting for the LifeMonitor Backend API
backend:
apiUrl: 'https://api.lifemonitor.eu'
diff --git a/ngsw-config.json b/ngsw-config.json
index cfcf80ae..4afb4224 100644
--- a/ngsw-config.json
+++ b/ngsw-config.json
@@ -33,14 +33,14 @@
"name": "api",
"urls": [
"/api/**",
+ "/error/**",
"/account/**",
"/oauth2/**",
"/jobs/**",
"/socket.io/**",
"/github/**",
"/integrations/**",
- "/openapi.*",
- "/metrics"
+ "/openapi.*"
],
"cacheConfig": {
"strategy": "freshness",
diff --git a/package-lock.json b/package-lock.json
index 516c8b14..1a4b05d7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "lifemonitor",
- "version": "0.5.5",
+ "version": "0.5.6",
"lockfileVersion": 2,
"requires": true,
"packages": {
diff --git a/package.json b/package.json
index 0662c5be..2e218ba5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "lifemonitor",
- "version": "0.5.5",
+ "version": "0.5.6",
"scripts": {
"start": "ng build --configuration production && http-server -p 4202 --ssl -C ./certs/lm.crt -K ./certs/lm.key --host lm dist/lifemonitor",
"start:dev": "ng serve --port 4200 --ssl --ssl-key ./certs/lm.key --ssl-cert ./certs/lm.crt --host lm",
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index 802e25ee..11d5d814 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -16,6 +16,7 @@ import { InputDialogService } from './utils/services/input-dialog.service';
import { DashboardComponent } from './views/dashboard/dashboard.component';
import { SuiteComponent } from './views/suite/suite.component';
import { WorkflowComponent } from './views/workflow/workflow.component';
+import { MaintenanceComponent } from './pages/maintenance/maintenance.component';
const routes: Routes = [
{
@@ -28,6 +29,11 @@ const routes: Routes = [
// canActivate: [AuthGuard],
// canActivateChild: [AuthGuard],
children: [
+ {
+ path: 'maintenance',
+ component: MaintenanceComponent,
+ pathMatch: 'full',
+ },
{
path: 'dashboard',
component: DashboardComponent,
@@ -51,7 +57,7 @@ const routes: Routes = [
{
path: 'logout',
component: LogoutComponent,
- },
+ },
// {
// path: 'register',
// component: RegisterComponent,
@@ -83,7 +89,7 @@ export class AppRoutingModule implements OnInit {
private router: Router,
private inputDialogService: InputDialogService,
private toastr: ToastrService
- ) {}
+ ) { }
ngOnInit() {
this.logger.debug('Initializing app routing module');
@@ -133,7 +139,7 @@ export class AppRoutingModule implements OnInit {
cancelText: 'Close',
iconClass: 'fas fa-exclamation-triangle',
enableCancel: false,
- onCancel: () => {},
+ onCancel: () => { },
});
}
}
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index c8eaaaae..f258e621 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,10 +1,12 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { SwUpdate, UpdateAvailableEvent } from '@angular/service-worker';
+import { Router } from '@angular/router';
import { Observable, Subscription } from 'rxjs';
import { Logger, LoggerManager } from './utils/logging/index';
import { CacheRefreshStatus } from './utils/services/cache/cache.model';
import { CachedHttpClientService } from './utils/services/cache/cachedhttpclient.service';
+import { AppConfigService } from './utils/services/config.service';
import { InputDialogService } from './utils/services/input-dialog.service';
import {
@@ -42,7 +44,9 @@ export class AppComponent implements OnInit, OnDestroy {
private inputDialog: InputDialogService,
private swUpdate: SwUpdate,
private cachedHttpClient: CachedHttpClientService,
- private ccService: NgcCookieConsentService
+ private ccService: NgcCookieConsentService,
+ private config: AppConfigService,
+ private router: Router
) {
this.refreshStatus$ = this.cachedHttpClient.refreshProgressStatus$;
}
@@ -51,7 +55,20 @@ export class AppComponent implements OnInit, OnDestroy {
return this.swUpdate.available;
}
+ get maintenanceModeEnabled(): boolean {
+ return this.config.maintenanceMode;
+ }
+
ngOnInit() {
+
+ if (this.maintenanceModeEnabled) {
+ console.log("Current route: " + this.router.url);
+ if (this.router.url !== '/maintenance'
+ && !this.router.url.startsWith('/static')) {
+ return this.router.navigateByUrl('/maintenance');
+ }
+ }
+
if (this.swUpdate.isEnabled) {
this.checkVersionSubscription = this.updateAvailable.subscribe(() => {
this.inputDialog.show({
@@ -73,30 +90,30 @@ export class AppComponent implements OnInit, OnDestroy {
}
// subscribe to cookieconsent observables to react to main events
- this.popupOpenSubscription = this.ccService.popupOpen$.subscribe(() => {});
+ this.popupOpenSubscription = this.ccService.popupOpen$.subscribe(() => { });
this.popupCloseSubscription = this.ccService.popupClose$.subscribe(
- () => {}
+ () => { }
);
this.initializeSubscription = this.ccService.initializing$.subscribe(
- (event: NgcInitializingEvent) => {}
+ (event: NgcInitializingEvent) => { }
);
this.statusChangeSubscription = this.ccService.statusChange$.subscribe(
- (event: NgcStatusChangeEvent) => {}
+ (event: NgcStatusChangeEvent) => { }
);
this.revokeChoiceSubscription = this.ccService.revokeChoice$.subscribe(
- () => {}
+ () => { }
);
this.noCookieLawSubscription = this.ccService.noCookieLaw$.subscribe(
- (event: NgcNoCookieLawEvent) => {}
+ (event: NgcNoCookieLawEvent) => { }
);
}
- ngAfterViewInit() {}
+ ngAfterViewInit() { }
ngOnDestroy() {
if (this.checkVersionSubscription) {
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 89950721..709eb2c5 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -55,6 +55,7 @@ import { BlankComponent } from './views/blank/blank.component';
import { DashboardComponent } from './views/dashboard/dashboard.component';
import { SuiteComponent } from './views/suite/suite.component';
import { WorkflowComponent } from './views/workflow/workflow.component';
+import { MaintenanceComponent } from './pages/maintenance/maintenance.component';
// PrimeNG Modules
// import { ChartModule } from 'primeng/chart';
@@ -156,6 +157,7 @@ export function initConfigService(
LogoutComponent,
ScrollComponent,
BaseDataViewComponent,
+ MaintenanceComponent,
],
imports: [
// PrimeNg Modules
diff --git a/src/app/pages/main/header/header.component.html b/src/app/pages/main/header/header.component.html
index d89bb121..4f189489 100644
--- a/src/app/pages/main/header/header.component.html
+++ b/src/app/pages/main/header/header.component.html
@@ -35,7 +35,8 @@
/> -->
-
+
Sign In
@@ -58,4 +59,4 @@