diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fb2350b555..1cc60dfcdf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -47,7 +47,7 @@ repos: files: salt/tests/unit/formulas/.*\.py additional_dependencies: - 'pyenchant~=3.2' - - 'salt==3002.6' + - 'salt==3002.7' - pytest - Jinja2 diff --git a/CHANGELOG.md b/CHANGELOG.md index ef7814a53f..d814449518 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,29 @@ # CHANGELOG ## Release 2.11.0 (in development) ### Enhancements + +- Bump Kubernetes version to 1.22.1 + (PR[#3525](https://github.com/scality/metalk8s/pull/3525)) + +- Bump etcd version to 3.5.0-0 + (PR[#3525](https://github.com/scality/metalk8s/pull/3525)) + +- Bump CoreDNS version to v1.8.4 + (PR[#3525](https://github.com/scality/metalk8s/pull/3525)) + - Bump `containerd` version to 1.4.8 (PR [#3466](https://github.com/scality/metalk8s/pull/3466)). +- Bump Calico version to 3.20.0 + (PR[#3527](https://github.com/scality/metalk8s/pull/3527)) + +- Bump ingress-nginx chart version to 4.0.1 + nginx-ingress-controller image has been bumped accordingly to v1.0.0 + (PR[#3518](https://github.com/scality/metalk8s/pull/3518)) + +- Bump Dex chart version to v0.6.3, Dex image has been bumped accordingly + to v2.30.0 + (PR[#3519](https://github.com/scality/metalk8s/pull/3519)) + - [#3487](https://github.com/scality/metalk8s/issues/3487) - Make Salt Kubernetes execution module more flexible relying on `DynamicClient` from `python-kubernetes` @@ -14,6 +35,20 @@ - Bump Kubernetes version to 1.21.4 (PR[#3495](https://github.com/scality/metalk8s/pull/3495)) +- Bump Salt version to 3002.7 + (PR [#3524](https://github.com/scality/metalk8s/pull/3524)) + +- Improve UI metrics charts (cursor synchronisation when hovering a chart, better tooltip with coloured legend and unit, lot of bug fixes when data is missing, symmetrical charts to compare read/write in/out metrics) (PR[#3529](https://github.com/scality/metalk8s/pull/3529)) + +## Bug fixes + +- Enforce a single subnet for control plane when using a + MetalLB-managed VIP for Ingress + (PR [#3533](https://github.com/scality/metalk8s/pull/3533)) + +- Fix UI issues in multi nodes environment when a node + is unavailable (PR[#3521](https://github.com/scality/metalk8s/pull/3521)) + ## Release 2.10.2 ### Bug fixes - Fix the link to documentation from the UI navigation bar diff --git a/buildchain/buildchain/versions.py b/buildchain/buildchain/versions.py index ea8549773d..a9055734c6 100644 --- a/buildchain/buildchain/versions.py +++ b/buildchain/buildchain/versions.py @@ -18,9 +18,9 @@ # Project-wide versions {{{ -CALICO_VERSION: str = "3.19.1" -K8S_VERSION: str = "1.21.4" -SALT_VERSION: str = "3002.6" +CALICO_VERSION: str = "3.20.0" +K8S_VERSION: str = "1.22.1" +SALT_VERSION: str = "3002.7" CONTAINERD_VERSION: str = "1.4.8" SOS_VERSION: str = "< 4.0" @@ -98,27 +98,27 @@ def _version_prefix(version: str, prefix: str = "v") -> str: Image( name="calico-node", version=_version_prefix(CALICO_VERSION), - digest="sha256:bc4a631d553b38fdc169ea4cb8027fa894a656e80d68d513359a4b9d46836b55", + digest="sha256:7f9aa7e31fbcea7be64b153f8bcfd494de023679ec10d851a05667f0adb42650", ), Image( name="calico-kube-controllers", version=_version_prefix(CALICO_VERSION), - digest="sha256:904458fe1bd56f995ef76e2c4d9a6831c506cc80f79e8fc0182dc059b1db25a4", + digest="sha256:a850ce8daa84433a5641900693b0f8bc8e5177a4aa4cac8cf4b6cd8c24fd9886", ), Image( name="coredns", - version="v1.8.0", - digest="sha256:cc8fb77bc2a0541949d1d9320a641b82fd392b0d3d8145469ca4709ae769980e", + version="v1.8.4", + digest="sha256:6e5a02c21641597998b4be7cb5eb1e7b02c0d8d23cce4dd09f4682d463798890", ), Image( name="dex", - version="v2.28.1", - digest="sha256:5e88f2205de172b60fd7af23ac92f34321688a83de9f7de7c9a6f394f6950877", + version="v2.30.0", + digest="sha256:63fc6ee14bcf1868ebfba90885aec76597e0f27bc8e89d1fd238b1f2ee3dea6e", ), Image( name="etcd", - version="3.4.13-0", - digest="sha256:4ad90a11b55313b182afc186b9876c8e891531b8db4c9bf1541953021618d0e2", + version="3.5.0-0", + digest="sha256:9ce33ba33d8e738a5b85ed50b5080ac746deceed4a7496c550927a7a19ca3b6d", ), Image( name="grafana", @@ -133,22 +133,22 @@ def _version_prefix(version: str, prefix: str = "v") -> str: Image( name="kube-apiserver", version=_version_prefix(K8S_VERSION), - digest="sha256:a12a4347573b16ba925bf3c2154b9155faa7796d35016d0e194f3ffce93435dc", + digest="sha256:6862d5a70cea8f3ef49213d6a36b7bfbbf90f99fb37f7124505be55f0ef51364", ), Image( name="kube-controller-manager", version=_version_prefix(K8S_VERSION), - digest="sha256:2f8234e1d386faa415090c381edc55a473b355ba79ef71c7851f89041b294d56", + digest="sha256:3e4274dee8a122bdd5e3f3db6b1eb8db59404deda2bf1adb0fec1da5dd95400a", ), Image( name="kube-proxy", version=_version_prefix(K8S_VERSION), - digest="sha256:bebf88332fc0e5648795fd7f0b57c4d39b901878e56d7cbd940ff9bd20d2a027", + digest="sha256:efcf1d5fb2fc95d28841f534f1385a4884230c7c876fb1b7cf66d2777ad6dc56", ), Image( name="kube-scheduler", version=_version_prefix(K8S_VERSION), - digest="sha256:650b648d881c672e7541227ad3ef9ff107e24d565a66feec95ca089b027c0b18", + digest="sha256:e1a999694bf4b9198bc220216680ef651fabe406445a93c2d354f9dd7e53c1fd", ), Image( name="kube-state-metrics", @@ -162,8 +162,8 @@ def _version_prefix(version: str, prefix: str = "v") -> str: ), Image( name="nginx-ingress-controller", - version="v0.47.0", - digest="sha256:a1e4efc107be0bb78f32eaec37bef17d7a0c81bec8066cdf2572508d21351d0b", + version="v1.0.0", + digest="sha256:0851b34f69f69352bf168e6ccf30e1e20714a264ab1ecd1933e4d8c0fc3215c6", ), Image( name="nginx-ingress-defaultbackend-amd64", diff --git a/charts/dex.yaml b/charts/dex.yaml index 57e436e11f..5e0a1bbe20 100644 --- a/charts/dex.yaml +++ b/charts/dex.yaml @@ -51,7 +51,7 @@ volumeMounts: - name: https-tls mountPath: /etc/dex/tls/https/server - name: dex-login - mountPath: /web/themes/scality + mountPath: /srv/dex/web/themes/scality - name: nginx-ingress-ca-cert mountPath: /etc/ssl/certs/nginx-ingress-ca.crt subPath: ca.crt diff --git a/charts/dex/Chart.yaml b/charts/dex/Chart.yaml index 8b0584fdad..0c913eec47 100644 --- a/charts/dex/Chart.yaml +++ b/charts/dex/Chart.yaml @@ -1,11 +1,12 @@ annotations: artifacthub.io/changes: | - - Added support for priority class name + - kind: added + description: "`clusterIP` value to control the IP when using ClusterIP service type" artifacthub.io/images: | - name: dex - image: ghcr.io/dexidp/dex:v2.28.1 + image: ghcr.io/dexidp/dex:v2.30.0 apiVersion: v2 -appVersion: 2.28.1 +appVersion: 2.30.0 description: OpenID Connect (OIDC) identity and OAuth 2.0 provider with pluggable connectors. home: https://dexidp.io/ @@ -25,4 +26,4 @@ sources: - https://github.com/dexidp/dex - https://github.com/dexidp/helm-charts/tree/master/charts/dex type: application -version: 0.4.0 +version: 0.6.3 diff --git a/charts/dex/README.md b/charts/dex/README.md index cccd01fe4d..34cd919d53 100644 --- a/charts/dex/README.md +++ b/charts/dex/README.md @@ -1,6 +1,6 @@ # dex -![version: 0.4.0](https://img.shields.io/badge/version-0.4.0-informational?style=flat-square) ![type: application](https://img.shields.io/badge/type-application-informational?style=flat-square) ![app version: 2.28.1](https://img.shields.io/badge/app%20version-2.28.1-informational?style=flat-square) ![kube version: >=1.14.0-0](https://img.shields.io/badge/kube%20version->=1.14.0--0-informational?style=flat-square) [![artifact hub](https://img.shields.io/badge/artifact%20hub-dex-informational?style=flat-square)](https://artifacthub.io/packages/helm/dex/dex) +![version: 0.6.3](https://img.shields.io/badge/version-0.6.3-informational?style=flat-square) ![type: application](https://img.shields.io/badge/type-application-informational?style=flat-square) ![app version: 2.30.0](https://img.shields.io/badge/app%20version-2.30.0-informational?style=flat-square) ![kube version: >=1.14.0-0](https://img.shields.io/badge/kube%20version->=1.14.0--0-informational?style=flat-square) [![artifact hub](https://img.shields.io/badge/artifact%20hub-dex-informational?style=flat-square)](https://artifacthub.io/packages/helm/dex/dex) OpenID Connect (OIDC) identity and OAuth 2.0 provider with pluggable connectors. @@ -140,6 +140,7 @@ ingress: | securityContext | object | `{}` | Container [security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container). See the [API reference](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context-1) for details. | | service.annotations | object | `{}` | Annotations to be added to the service. | | service.type | string | `"ClusterIP"` | Kubernetes [service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types). | +| service.clusterIP | string | `""` | Internal cluster service IP (when applicable) | | service.ports.http.port | int | `5556` | HTTP service port | | service.ports.http.nodePort | int | `nil` | HTTP node port (when applicable) | | service.ports.https.port | int | `5554` | HTTPS service port | diff --git a/charts/dex/ci/no-config-secret.yaml b/charts/dex/ci/no-config-secret.yaml new file mode 100644 index 0000000000..8e667025fb --- /dev/null +++ b/charts/dex/ci/no-config-secret.yaml @@ -0,0 +1,10 @@ +config: + issuer: https://my-issuer.com + + storage: + type: memory + + enablePasswordDB: true + +configSecret: + create: false diff --git a/charts/dex/templates/deployment.yaml b/charts/dex/templates/deployment.yaml index 6cacfbf244..386a43f7ea 100644 --- a/charts/dex/templates/deployment.yaml +++ b/charts/dex/templates/deployment.yaml @@ -14,10 +14,12 @@ spec: template: metadata: annotations: - checksum/config: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} + {{ if .Values.configSecret.create }} + checksum/config: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + {{- end }} labels: {{- include "dex.selectorLabels" . | nindent 8 }} spec: diff --git a/charts/dex/templates/secret.yaml b/charts/dex/templates/secret.yaml index 72e17ac8df..27d39546ed 100644 --- a/charts/dex/templates/secret.yaml +++ b/charts/dex/templates/secret.yaml @@ -1,4 +1,4 @@ -{{ if .Values.configSecret.create }} +{{- if .Values.configSecret.create -}} apiVersion: v1 kind: Secret metadata: @@ -8,4 +8,4 @@ metadata: type: Opaque data: config.yaml: {{ .Values.config | toYaml | b64enc | quote }} -{{ end }} +{{- end }} diff --git a/charts/dex/templates/service.yaml b/charts/dex/templates/service.yaml index 9803239431..8114e8d59e 100644 --- a/charts/dex/templates/service.yaml +++ b/charts/dex/templates/service.yaml @@ -10,8 +10,8 @@ metadata: {{- end }} spec: type: {{ .Values.service.type }} - {{- if hasKey .Values.service "clusterIP" }} - clusterIP: {{ .Values.service.clusterIP | quote }} + {{- with .Values.service.clusterIP }} + clusterIP: {{ . }} {{- end }} ports: - name: http diff --git a/charts/dex/templates/tests/no-config-secret.yaml b/charts/dex/templates/tests/no-config-secret.yaml new file mode 100644 index 0000000000..4b7804f540 --- /dev/null +++ b/charts/dex/templates/tests/no-config-secret.yaml @@ -0,0 +1,13 @@ +{{- if not .Values.configSecret.create -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "dex.configSecretName" . }}-test-no-create + labels: + {{- include "dex.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +type: Opaque +data: + config.yaml: {{ .Values.config | toYaml | b64enc | quote }} +{{- end }} diff --git a/charts/dex/values.yaml b/charts/dex/values.yaml index 1e0004adf9..11cfb15aae 100644 --- a/charts/dex/values.yaml +++ b/charts/dex/values.yaml @@ -122,6 +122,9 @@ service: # -- Kubernetes [service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types). type: ClusterIP + # -- Internal cluster service IP (when applicable) + clusterIP: "" + ports: http: # -- HTTP service port diff --git a/charts/ingress-nginx-control-plane-daemonset.yaml b/charts/ingress-nginx-control-plane-daemonset.yaml index e3b586f3cf..336230be4c 100644 --- a/charts/ingress-nginx-control-plane-daemonset.yaml +++ b/charts/ingress-nginx-control-plane-daemonset.yaml @@ -7,7 +7,9 @@ controller: electionID: ingress-control-plane-controller-leader - ingressClass: nginx-control-plane + ingressClassResource: + name: nginx-control-plane + controllerValue: "k8s.io/ingress-nginx-control-plane" admissionWebhooks: enabled: false diff --git a/charts/ingress-nginx-control-plane-deployment.yaml b/charts/ingress-nginx-control-plane-deployment.yaml index 2e464c0a73..fb52236843 100644 --- a/charts/ingress-nginx-control-plane-deployment.yaml +++ b/charts/ingress-nginx-control-plane-deployment.yaml @@ -7,7 +7,9 @@ controller: electionID: ingress-control-plane-controller-leader - ingressClass: nginx-control-plane + ingressClassResource: + name: nginx-control-plane + controllerValue: "k8s.io/ingress-nginx-control-plane" admissionWebhooks: enabled: false diff --git a/charts/ingress-nginx.yaml b/charts/ingress-nginx.yaml index 2c8e15ecd3..c58f8b9614 100644 --- a/charts/ingress-nginx.yaml +++ b/charts/ingress-nginx.yaml @@ -6,6 +6,9 @@ controller: hostPort: enabled: true + ingressClassResource: + default: true + admissionWebhooks: enabled: false diff --git a/charts/ingress-nginx/Chart.yaml b/charts/ingress-nginx/Chart.yaml index 8aa300b551..5a82314b79 100644 --- a/charts/ingress-nginx/Chart.yaml +++ b/charts/ingress-nginx/Chart.yaml @@ -1,19 +1,22 @@ annotations: artifacthub.io/changes: | - - Add namespace field in the namespace scoped resource templates + - Support for Ingress object v1 and drop support for v1beta1 + - Update to go 1.17 + - Fix some bugs + artifacthub.io/prerelease: "false" apiVersion: v2 -appVersion: 0.47.0 +appVersion: 1.0.0 description: Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer home: https://github.com/kubernetes/ingress-nginx icon: https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Nginx_logo.svg/500px-Nginx_logo.svg.png keywords: - ingress - nginx -kubeVersion: '>=1.16.0-0' +kubeVersion: '>=1.19.0-0' maintainers: - name: ChiefAlexander name: ingress-nginx sources: - https://github.com/kubernetes/ingress-nginx type: application -version: 3.34.0 +version: 4.0.1 diff --git a/charts/ingress-nginx/OWNERS b/charts/ingress-nginx/OWNERS index 7aadb8dc29..6b7e049ca8 100644 --- a/charts/ingress-nginx/OWNERS +++ b/charts/ingress-nginx/OWNERS @@ -1,5 +1,10 @@ +# See the OWNERS docs: https://github.com/kubernetes/community/blob/master/contributors/guide/owners.md + approvers: - - ChiefAlexander +- ingress-nginx-helm-maintainers reviewers: - - ChiefAlexander +- ingress-nginx-helm-reviewers + +labels: +- area/helm diff --git a/charts/ingress-nginx/README.md b/charts/ingress-nginx/README.md index 53657e56ff..22e66fb853 100644 --- a/charts/ingress-nginx/README.md +++ b/charts/ingress-nginx/README.md @@ -90,9 +90,9 @@ You can add Prometheus annotations to the metrics service using `controller.metr Previous versions of this chart had a `controller.stats.*` configuration block, which is now obsolete due to the following changes in nginx ingress controller: -- In [0.16.1](https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md#0161), the vts (virtual host traffic status) dashboard was removed -- In [0.23.0](https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md#0230), the status page at port 18080 is now a unix socket webserver only available at localhost. - You can use `curl --unix-socket /tmp/nginx-status-server.sock http://localhost/nginx_status` inside the controller container to access it locally, or use the snippet from [nginx-ingress changelog](https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md#0230) to re-enable the http server +- In [0.16.1](https://github.com/kubernetes/ingress-nginx/blob/main/Changelog.md#0161), the vts (virtual host traffic status) dashboard was removed +- In [0.23.0](https://github.com/kubernetes/ingress-nginx/blob/main/Changelog.md#0230), the status page at port 18080 is now a unix socket webserver only available at localhost. + You can use `curl --unix-socket /tmp/nginx-status-server.sock http://localhost/nginx_status` inside the controller container to access it locally, or use the snippet from [nginx-ingress changelog](https://github.com/kubernetes/ingress-nginx/blob/main/Changelog.md#0230) to re-enable the http server ### ExternalDNS Service Configuration @@ -107,7 +107,7 @@ controller: ### AWS L7 ELB with SSL Termination -Annotate the controller as shown in the [nginx-ingress l7 patch](https://github.com/kubernetes/ingress-nginx/blob/master/deploy/aws/l7/service-l7.yaml): +Annotate the controller as shown in the [nginx-ingress l7 patch](https://github.com/kubernetes/ingress-nginx/blob/main/deploy/aws/l7/service-l7.yaml): ```yaml controller: @@ -159,7 +159,7 @@ controller: enabled: true annotations: # Create internal ELB - service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0 + service.beta.kubernetes.io/aws-load-balancer-internal: "true" # Any other annotation can be declared here. ``` diff --git a/charts/ingress-nginx/ci/controller-custom-ingressclass-flags.yaml b/charts/ingress-nginx/ci/controller-custom-ingressclass-flags.yaml new file mode 100644 index 0000000000..b28a2326ee --- /dev/null +++ b/charts/ingress-nginx/ci/controller-custom-ingressclass-flags.yaml @@ -0,0 +1,7 @@ +controller: + watchIngressWithoutClass: true + ingressClassResource: + name: custom-nginx + enabled: true + default: true + controllerValue: "k8s.io/custom-nginx" diff --git a/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml b/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml index e12b53421b..43dd2b2ac9 100644 --- a/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml +++ b/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null kind: DaemonSet admissionWebhooks: enabled: false diff --git a/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml b/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml index cfc545f69f..1d94be219b 100644 --- a/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml +++ b/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml @@ -1,5 +1,9 @@ controller: kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false diff --git a/charts/ingress-nginx/ci/daemonset-headers-values.yaml b/charts/ingress-nginx/ci/daemonset-headers-values.yaml index ff82cd9c70..ab7d47bd4d 100644 --- a/charts/ingress-nginx/ci/daemonset-headers-values.yaml +++ b/charts/ingress-nginx/ci/daemonset-headers-values.yaml @@ -1,5 +1,9 @@ controller: kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false addHeaders: diff --git a/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml b/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml index 443e39d8ba..0a200a7460 100644 --- a/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml +++ b/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml @@ -1,5 +1,9 @@ controller: kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false service: @@ -7,4 +11,4 @@ controller: internal: enabled: true annotations: - service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0 + service.beta.kubernetes.io/aws-load-balancer-internal: "true" diff --git a/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml b/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml index 6d6605f0e1..3b7aa2fcd2 100644 --- a/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml +++ b/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml @@ -1,5 +1,9 @@ controller: kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false service: diff --git a/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml b/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml index 04ac58dbd8..0b55306a10 100644 --- a/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml +++ b/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml @@ -1,5 +1,9 @@ controller: kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false metrics: diff --git a/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml b/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml index afb5487c57..acd86a77ab 100644 --- a/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml +++ b/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml @@ -1,5 +1,9 @@ controller: kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false service: diff --git a/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml b/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml index 7b4d7cbe7d..25ee64d856 100644 --- a/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml +++ b/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml @@ -1,5 +1,9 @@ controller: kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false service: diff --git a/charts/ingress-nginx/ci/daemonset-tcp-values.yaml b/charts/ingress-nginx/ci/daemonset-tcp-values.yaml index a359a6a401..380c8b4b13 100644 --- a/charts/ingress-nginx/ci/daemonset-tcp-values.yaml +++ b/charts/ingress-nginx/ci/daemonset-tcp-values.yaml @@ -1,5 +1,9 @@ controller: kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false service: diff --git a/charts/ingress-nginx/ci/deamonset-default-values.yaml b/charts/ingress-nginx/ci/deamonset-default-values.yaml index e63a7f5db3..82fa23e854 100644 --- a/charts/ingress-nginx/ci/deamonset-default-values.yaml +++ b/charts/ingress-nginx/ci/deamonset-default-values.yaml @@ -1,5 +1,9 @@ controller: kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false service: diff --git a/charts/ingress-nginx/ci/deamonset-metrics-values.yaml b/charts/ingress-nginx/ci/deamonset-metrics-values.yaml index 1e5190afc0..cb3cb54be2 100644 --- a/charts/ingress-nginx/ci/deamonset-metrics-values.yaml +++ b/charts/ingress-nginx/ci/deamonset-metrics-values.yaml @@ -1,5 +1,9 @@ controller: kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false metrics: diff --git a/charts/ingress-nginx/ci/deamonset-psp-values.yaml b/charts/ingress-nginx/ci/deamonset-psp-values.yaml index 017b60a9c6..8026a6356f 100644 --- a/charts/ingress-nginx/ci/deamonset-psp-values.yaml +++ b/charts/ingress-nginx/ci/deamonset-psp-values.yaml @@ -1,5 +1,9 @@ controller: kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false service: diff --git a/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml b/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml index 88aafc66fd..fccdb134cf 100644 --- a/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml +++ b/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml @@ -1,5 +1,9 @@ controller: kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: true service: diff --git a/charts/ingress-nginx/ci/deamonset-webhook-values.yaml b/charts/ingress-nginx/ci/deamonset-webhook-values.yaml index 6e3b371da6..54d364df11 100644 --- a/charts/ingress-nginx/ci/deamonset-webhook-values.yaml +++ b/charts/ingress-nginx/ci/deamonset-webhook-values.yaml @@ -1,5 +1,9 @@ controller: kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: true service: diff --git a/charts/ingress-nginx/ci/deployment-autoscaling-behavior-values.yaml b/charts/ingress-nginx/ci/deployment-autoscaling-behavior-values.yaml new file mode 100644 index 0000000000..dca3f35f83 --- /dev/null +++ b/charts/ingress-nginx/ci/deployment-autoscaling-behavior-values.yaml @@ -0,0 +1,14 @@ +controller: + autoscaling: + enabled: true + behavior: + scaleDown: + stabilizationWindowSeconds: 300 + policies: + - type: Pods + value: 1 + periodSeconds: 180 + admissionWebhooks: + enabled: false + service: + type: ClusterIP diff --git a/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml b/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml index 5314cecb38..b8b3ac6862 100644 --- a/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml +++ b/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null autoscaling: enabled: true admissionWebhooks: diff --git a/charts/ingress-nginx/ci/deployment-customconfig-values.yaml b/charts/ingress-nginx/ci/deployment-customconfig-values.yaml index f232531acb..85715ddb76 100644 --- a/charts/ingress-nginx/ci/deployment-customconfig-values.yaml +++ b/charts/ingress-nginx/ci/deployment-customconfig-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null config: use-proxy-protocol: "true" admissionWebhooks: diff --git a/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml b/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml index 9eda282b13..a564eaf931 100644 --- a/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml +++ b/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false service: diff --git a/charts/ingress-nginx/ci/deployment-default-values.yaml b/charts/ingress-nginx/ci/deployment-default-values.yaml index 93a393c975..9f46b4e7e9 100644 --- a/charts/ingress-nginx/ci/deployment-default-values.yaml +++ b/charts/ingress-nginx/ci/deployment-default-values.yaml @@ -1,4 +1,8 @@ # Left blank to test default values controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null service: type: ClusterIP diff --git a/charts/ingress-nginx/ci/deployment-headers-values.yaml b/charts/ingress-nginx/ci/deployment-headers-values.yaml index 665fd48d35..17a11ac370 100644 --- a/charts/ingress-nginx/ci/deployment-headers-values.yaml +++ b/charts/ingress-nginx/ci/deployment-headers-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false addHeaders: diff --git a/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml b/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml index 892f6de3f0..fd8df8de5d 100644 --- a/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml +++ b/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false service: @@ -6,4 +10,4 @@ controller: internal: enabled: true annotations: - service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0 + service.beta.kubernetes.io/aws-load-balancer-internal: "true" diff --git a/charts/ingress-nginx/ci/deployment-metrics-values.yaml b/charts/ingress-nginx/ci/deployment-metrics-values.yaml index 887ed0f620..9209ad5a6f 100644 --- a/charts/ingress-nginx/ci/deployment-metrics-values.yaml +++ b/charts/ingress-nginx/ci/deployment-metrics-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false metrics: diff --git a/charts/ingress-nginx/ci/deployment-nodeport-values.yaml b/charts/ingress-nginx/ci/deployment-nodeport-values.yaml index 84f1f7582e..cd9b323528 100644 --- a/charts/ingress-nginx/ci/deployment-nodeport-values.yaml +++ b/charts/ingress-nginx/ci/deployment-nodeport-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false service: diff --git a/charts/ingress-nginx/ci/deployment-podannotations-values.yaml b/charts/ingress-nginx/ci/deployment-podannotations-values.yaml index b65a0910b3..b48d93c46a 100644 --- a/charts/ingress-nginx/ci/deployment-podannotations-values.yaml +++ b/charts/ingress-nginx/ci/deployment-podannotations-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false metrics: diff --git a/charts/ingress-nginx/ci/deployment-psp-values.yaml b/charts/ingress-nginx/ci/deployment-psp-values.yaml index e339c69c32..2f332a7b20 100644 --- a/charts/ingress-nginx/ci/deployment-psp-values.yaml +++ b/charts/ingress-nginx/ci/deployment-psp-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null service: type: ClusterIP diff --git a/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml b/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml index 141e06b687..c51a4e91fa 100644 --- a/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml +++ b/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false service: diff --git a/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml b/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml index bc29abeba7..5b45b69dcc 100644 --- a/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml +++ b/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: false service: diff --git a/charts/ingress-nginx/ci/deployment-tcp-values.yaml b/charts/ingress-nginx/ci/deployment-tcp-values.yaml index b7f54c09fa..ac0b6e60eb 100644 --- a/charts/ingress-nginx/ci/deployment-tcp-values.yaml +++ b/charts/ingress-nginx/ci/deployment-tcp-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null service: type: ClusterIP diff --git a/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml b/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml index a829c36144..6195bb3391 100644 --- a/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml +++ b/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: true service: diff --git a/charts/ingress-nginx/ci/deployment-webhook-resources-values.yaml b/charts/ingress-nginx/ci/deployment-webhook-resources-values.yaml new file mode 100644 index 0000000000..49ebbb02c8 --- /dev/null +++ b/charts/ingress-nginx/ci/deployment-webhook-resources-values.yaml @@ -0,0 +1,23 @@ +controller: + service: + type: ClusterIP + admissionWebhooks: + enabled: true + createSecretJob: + resources: + limits: + cpu: 10m + memory: 20Mi + requests: + cpu: 10m + memory: 20Mi + patchWebhookJob: + resources: + limits: + cpu: 10m + memory: 20Mi + requests: + cpu: 10m + memory: 20Mi + patch: + enabled: true diff --git a/charts/ingress-nginx/ci/deployment-webhook-values.yaml b/charts/ingress-nginx/ci/deployment-webhook-values.yaml index 4f18a70b9f..76669a5300 100644 --- a/charts/ingress-nginx/ci/deployment-webhook-values.yaml +++ b/charts/ingress-nginx/ci/deployment-webhook-values.yaml @@ -1,4 +1,8 @@ controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null admissionWebhooks: enabled: true service: diff --git a/charts/ingress-nginx/templates/NOTES.txt b/charts/ingress-nginx/templates/NOTES.txt index 60fb2c1f62..2eebba3686 100644 --- a/charts/ingress-nginx/templates/NOTES.txt +++ b/charts/ingress-nginx/templates/NOTES.txt @@ -29,7 +29,7 @@ Get the application URL by running these commands: An example Ingress that makes use of the controller: - apiVersion: networking.k8s.io/v1beta1 + apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: diff --git a/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml b/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml index 3656be4876..1f58bdce7b 100644 --- a/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml +++ b/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml @@ -47,6 +47,9 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + {{- if .Values.controller.admissionWebhooks.createSecretJob.resources }} + resources: {{ toYaml .Values.controller.admissionWebhooks.createSecretJob.resources | nindent 12 }} + {{- end }} restartPolicy: OnFailure serviceAccountName: {{ include "ingress-nginx.fullname" . }}-admission {{- if .Values.controller.admissionWebhooks.patch.nodeSelector }} diff --git a/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml b/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml index 9e9bd0138d..6d01ad2304 100644 --- a/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml +++ b/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml @@ -49,6 +49,9 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + {{- if .Values.controller.admissionWebhooks.patchWebhookJob.resources }} + resources: {{ toYaml .Values.controller.admissionWebhooks.patchWebhookJob.resources | nindent 12 }} + {{- end }} restartPolicy: OnFailure serviceAccountName: {{ include "ingress-nginx.fullname" . }}-admission {{- if .Values.controller.admissionWebhooks.patch.nodeSelector }} diff --git a/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml b/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml index 2f3dd77848..712f74fdd3 100644 --- a/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml +++ b/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml @@ -18,7 +18,7 @@ webhooks: - apiGroups: - networking.k8s.io apiVersions: - - v1beta1 + - v1 operations: - CREATE - UPDATE @@ -28,12 +28,11 @@ webhooks: sideEffects: None admissionReviewVersions: - v1 - - v1beta1 clientConfig: service: namespace: {{ .Release.Namespace | quote }} name: {{ include "ingress-nginx.controller.fullname" . }}-admission - path: /networking/v1beta1/ingresses + path: /networking/v1/ingresses {{- if .Values.controller.admissionWebhooks.timeoutSeconds }} timeoutSeconds: {{ .Values.controller.admissionWebhooks.timeoutSeconds }} {{- end }} diff --git a/charts/ingress-nginx/templates/clusterrole.yaml b/charts/ingress-nginx/templates/clusterrole.yaml index 8ec5f49fa4..c1f901d50c 100644 --- a/charts/ingress-nginx/templates/clusterrole.yaml +++ b/charts/ingress-nginx/templates/clusterrole.yaml @@ -1,4 +1,10 @@ -{{- if and .Values.rbac.create (not .Values.rbac.scope) -}} +{{- if .Values.rbac.create }} + +{{- if and .Values.rbac.scope (not .Values.controller.scope.enabled) -}} + {{ required "Invalid configuration: 'rbac.scope' should be equal to 'controller.scope.enabled' (true/false)." (index (dict) ".") }} +{{- end }} + +{{- if not .Values.rbac.scope -}} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: @@ -42,8 +48,7 @@ rules: - list - watch - apiGroups: - - extensions - - "networking.k8s.io" # k8s 1.14+ + - networking.k8s.io resources: - ingresses verbs: @@ -58,14 +63,13 @@ rules: - create - patch - apiGroups: - - extensions - - "networking.k8s.io" # k8s 1.14+ + - networking.k8s.io resources: - ingresses/status verbs: - update - apiGroups: - - "networking.k8s.io" # k8s 1.14+ + - networking.k8s.io resources: - ingressclasses verbs: @@ -73,3 +77,5 @@ rules: - list - watch {{- end }} + +{{- end }} diff --git a/charts/ingress-nginx/templates/controller-daemonset.yaml b/charts/ingress-nginx/templates/controller-daemonset.yaml index 2f6def5897..34986e568e 100644 --- a/charts/ingress-nginx/templates/controller-daemonset.yaml +++ b/charts/ingress-nginx/templates/controller-daemonset.yaml @@ -41,6 +41,9 @@ spec: spec: {{- if .Values.controller.dnsConfig }} dnsConfig: {{ toYaml .Values.controller.dnsConfig | nindent 8 }} + {{- end }} + {{- if .Values.controller.hostname }} + hostname: {{ toYaml .Values.controller.hostname | nindent 8 }} {{- end }} dnsPolicy: {{ .Values.controller.dnsPolicy }} {{- if .Values.imagePullSecrets }} @@ -74,22 +77,22 @@ spec: args: - /nginx-ingress-controller {{- if .Values.defaultBackend.enabled }} - - --default-backend-service={{ .Release.Namespace }}/{{ include "ingress-nginx.defaultBackend.fullname" . }} + - --default-backend-service=$(POD_NAMESPACE)/{{ include "ingress-nginx.defaultBackend.fullname" . }} {{- end }} {{- if .Values.controller.publishService.enabled }} - --publish-service={{ template "ingress-nginx.controller.publishServicePath" . }} {{- end }} - --election-id={{ .Values.controller.electionID }} - - --ingress-class={{ .Values.controller.ingressClass }} - - --configmap={{ .Release.Namespace }}/{{ include "ingress-nginx.controller.fullname" . }} + - --controller-class={{ .Values.controller.ingressClassResource.controllerValue }} + - --configmap={{ default "$(POD_NAMESPACE)" .Values.controller.configMapNamespace }}/{{ include "ingress-nginx.controller.fullname" . }} {{- if .Values.tcp }} - - --tcp-services-configmap={{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-tcp + - --tcp-services-configmap={{ default "$(POD_NAMESPACE)" .Values.controller.tcp.configMapNamespace }}/{{ include "ingress-nginx.fullname" . }}-tcp {{- end }} {{- if .Values.udp }} - - --udp-services-configmap={{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-udp + - --udp-services-configmap={{ default "$(POD_NAMESPACE)" .Values.controller.udp.configMapNamespace }}/{{ include "ingress-nginx.fullname" . }}-udp {{- end }} {{- if .Values.controller.scope.enabled }} - - --watch-namespace={{ default .Release.Namespace .Values.controller.scope.namespace }} + - --watch-namespace={{ default "$(POD_NAMESPACE)" .Values.controller.scope.namespace }} {{- end }} {{- if and .Values.controller.reportNodeInternalIp .Values.controller.hostNetwork }} - --report-node-internal-ip-address={{ .Values.controller.reportNodeInternalIp }} @@ -108,6 +111,9 @@ spec: {{- if not (eq .Values.controller.healthCheckPath "/healthz") }} - --health-check-path={{ .Values.controller.healthCheckPath }} {{- end }} + {{- if .Values.controller.watchIngressWithoutClass }} + - --watch-ingress-without-class=true + {{- end }} {{- range $key, $value := .Values.controller.extraArgs }} {{- /* Accept keys without values or with false as value */}} {{- if eq ($value | quote | len) 2 }} diff --git a/charts/ingress-nginx/templates/controller-deployment.yaml b/charts/ingress-nginx/templates/controller-deployment.yaml index 7e2d223a99..f17975de33 100644 --- a/charts/ingress-nginx/templates/controller-deployment.yaml +++ b/charts/ingress-nginx/templates/controller-deployment.yaml @@ -45,6 +45,9 @@ spec: spec: {{- if .Values.controller.dnsConfig }} dnsConfig: {{ toYaml .Values.controller.dnsConfig | nindent 8 }} + {{- end }} + {{- if .Values.controller.hostname }} + hostname: {{ toYaml .Values.controller.hostname | nindent 8 }} {{- end }} dnsPolicy: {{ .Values.controller.dnsPolicy }} {{- if .Values.imagePullSecrets }} @@ -84,13 +87,13 @@ spec: - --publish-service={{ template "ingress-nginx.controller.publishServicePath" . }} {{- end }} - --election-id={{ .Values.controller.electionID }} - - --ingress-class={{ .Values.controller.ingressClass }} - - --configmap=$(POD_NAMESPACE)/{{ include "ingress-nginx.controller.fullname" . }} + - --controller-class={{ .Values.controller.ingressClassResource.controllerValue }} + - --configmap={{ default "$(POD_NAMESPACE)" .Values.controller.configMapNamespace }}/{{ include "ingress-nginx.controller.fullname" . }} {{- if .Values.tcp }} - - --tcp-services-configmap=$(POD_NAMESPACE)/{{ include "ingress-nginx.fullname" . }}-tcp + - --tcp-services-configmap={{ default "$(POD_NAMESPACE)" .Values.controller.tcp.configMapNamespace }}/{{ include "ingress-nginx.fullname" . }}-tcp {{- end }} {{- if .Values.udp }} - - --udp-services-configmap=$(POD_NAMESPACE)/{{ include "ingress-nginx.fullname" . }}-udp + - --udp-services-configmap={{ default "$(POD_NAMESPACE)" .Values.controller.udp.configMapNamespace }}/{{ include "ingress-nginx.fullname" . }}-udp {{- end }} {{- if .Values.controller.scope.enabled }} - --watch-namespace={{ default "$(POD_NAMESPACE)" .Values.controller.scope.namespace }} @@ -109,6 +112,9 @@ spec: {{- if not (eq .Values.controller.healthCheckPath "/healthz") }} - --health-check-path={{ .Values.controller.healthCheckPath }} {{- end }} + {{- if .Values.controller.watchIngressWithoutClass }} + - --watch-ingress-without-class=true + {{- end }} {{- range $key, $value := .Values.controller.extraArgs }} {{- /* Accept keys without values or with false as value */}} {{- if eq ($value | quote | len) 2 }} @@ -140,7 +146,7 @@ spec: {{- end }} {{- if .Values.controller.extraEnvs }} {{- toYaml .Values.controller.extraEnvs | nindent 12 }} - {{- end }} + {{- end }} {{- if .Values.controller.startupProbe }} startupProbe: {{ toYaml .Values.controller.startupProbe | nindent 12 }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-hpa.yaml b/charts/ingress-nginx/templates/controller-hpa.yaml index fb14bdf6a7..876315f333 100644 --- a/charts/ingress-nginx/templates/controller-hpa.yaml +++ b/charts/ingress-nginx/templates/controller-hpa.yaml @@ -22,9 +22,9 @@ spec: maxReplicas: {{ .Values.controller.autoscaling.maxReplicas }} metrics: {{- with .Values.controller.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory + - type: Resource + resource: + name: memory target: type: Utilization averageUtilization: {{ . }} @@ -38,7 +38,11 @@ spec: averageUtilization: {{ . }} {{- end }} {{- with .Values.controller.autoscalingTemplate }} -{{- toYaml . | nindent 2 }} + {{- toYaml . | nindent 2 }} + {{- end }} + {{- with .Values.controller.autoscaling.behavior }} + behavior: + {{- toYaml . | nindent 4 }} {{- end }} {{- end }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-ingressclass.yaml b/charts/ingress-nginx/templates/controller-ingressclass.yaml index f94b9590de..9492784a28 100644 --- a/charts/ingress-nginx/templates/controller-ingressclass.yaml +++ b/charts/ingress-nginx/templates/controller-ingressclass.yaml @@ -1,9 +1,7 @@ -{{- if and (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) (.Values.controller.ingressClassResource.enabled) -}} -{{- if and (semverCompare "=1.18-0" .Capabilities.KubeVersion.GitVersion) }} -apiVersion: networking.k8s.io/v1beta1 -{{- else }} +{{- if .Values.controller.ingressClassResource.enabled -}} +# We don't support namespaced ingressClass yet +# So a ClusterRole and a ClusterRoleBinding is required apiVersion: networking.k8s.io/v1 -{{- end }} kind: IngressClass metadata: labels: @@ -12,12 +10,12 @@ metadata: {{- with .Values.controller.labels }} {{- toYaml . | nindent 4 }} {{- end }} - name: {{ .Values.controller.ingressClass }} + name: {{ .Values.controller.ingressClassResource.name }} {{- if .Values.controller.ingressClassResource.default }} annotations: ingressclass.kubernetes.io/is-default-class: "true" {{- end }} spec: - controller: k8s.io/ingress-nginx + controller: {{ .Values.controller.ingressClassResource.controllerValue }} {{ template "ingressClass.parameters" . }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml b/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml index a5a425f74b..9556f58631 100644 --- a/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml +++ b/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml @@ -1,5 +1,5 @@ {{- if or (and .Values.controller.autoscaling.enabled (gt (.Values.controller.autoscaling.minReplicas | int) 1)) (and (not .Values.controller.autoscaling.enabled) (gt (.Values.controller.replicaCount | int) 1)) }} -apiVersion: policy/v1beta1 +apiVersion: {{ ternary "policy/v1" "policy/v1beta1" (semverCompare ">=1.21.0-0" .Capabilities.KubeVersion.Version) }} kind: PodDisruptionBudget metadata: labels: diff --git a/charts/ingress-nginx/templates/controller-role.yaml b/charts/ingress-nginx/templates/controller-role.yaml index 1a5ccd29bf..97c627dacb 100644 --- a/charts/ingress-nginx/templates/controller-role.yaml +++ b/charts/ingress-nginx/templates/controller-role.yaml @@ -34,8 +34,7 @@ rules: - list - watch - apiGroups: - - extensions - - "networking.k8s.io" # k8s 1.14+ + - networking.k8s.io resources: - ingresses verbs: @@ -43,14 +42,13 @@ rules: - list - watch - apiGroups: - - extensions - - "networking.k8s.io" # k8s 1.14+ + - networking.k8s.io resources: - ingresses/status verbs: - update - apiGroups: - - "networking.k8s.io" # k8s 1.14+ + - networking.k8s.io resources: - ingressclasses verbs: @@ -62,7 +60,7 @@ rules: resources: - configmaps resourceNames: - - {{ .Values.controller.electionID }}-{{ .Values.controller.ingressClass }} + - {{ .Values.controller.electionID }} verbs: - get - update diff --git a/charts/ingress-nginx/templates/controller-service-internal.yaml b/charts/ingress-nginx/templates/controller-service-internal.yaml index 0bb9661274..09c942eb23 100644 --- a/charts/ingress-nginx/templates/controller-service-internal.yaml +++ b/charts/ingress-nginx/templates/controller-service-internal.yaml @@ -32,6 +32,9 @@ spec: port: {{ .Values.controller.service.ports.http }} protocol: TCP targetPort: {{ .Values.controller.service.targetPorts.http }} + {{- if semverCompare ">=1.20" .Capabilities.KubeVersion.Version }} + appProtocol: http + {{- end }} {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.http))) }} nodePort: {{ .Values.controller.service.nodePorts.http }} {{- end }} @@ -41,6 +44,9 @@ spec: port: {{ .Values.controller.service.ports.https }} protocol: TCP targetPort: {{ .Values.controller.service.targetPorts.https }} + {{- if semverCompare ">=1.20" .Capabilities.KubeVersion.Version }} + appProtocol: https + {{- end }} {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.https))) }} nodePort: {{ .Values.controller.service.nodePorts.https }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-service-webhook.yaml b/charts/ingress-nginx/templates/controller-service-webhook.yaml index 228cb59d88..ae3b1fc922 100644 --- a/charts/ingress-nginx/templates/controller-service-webhook.yaml +++ b/charts/ingress-nginx/templates/controller-service-webhook.yaml @@ -28,6 +28,9 @@ spec: - name: https-webhook port: 443 targetPort: webhook + {{- if semverCompare ">=1.20" .Capabilities.KubeVersion.Version }} + appProtocol: https + {{- end }} selector: {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} app.kubernetes.io/component: controller diff --git a/charts/ingress-nginx/templates/controller-service.yaml b/charts/ingress-nginx/templates/controller-service.yaml index 908291cfff..9248818457 100644 --- a/charts/ingress-nginx/templates/controller-service.yaml +++ b/charts/ingress-nginx/templates/controller-service.yaml @@ -44,6 +44,9 @@ spec: port: {{ .Values.controller.service.ports.http }} protocol: TCP targetPort: {{ .Values.controller.service.targetPorts.http }} + {{- if semverCompare ">=1.20" .Capabilities.KubeVersion.Version }} + appProtocol: http + {{- end }} {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.http))) }} nodePort: {{ .Values.controller.service.nodePorts.http }} {{- end }} @@ -53,6 +56,9 @@ spec: port: {{ .Values.controller.service.ports.https }} protocol: TCP targetPort: {{ .Values.controller.service.targetPorts.https }} + {{- if semverCompare ">=1.20" .Capabilities.KubeVersion.Version }} + appProtocol: https + {{- end }} {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.https))) }} nodePort: {{ .Values.controller.service.nodePorts.https }} {{- end }} diff --git a/charts/ingress-nginx/templates/controller-servicemonitor.yaml b/charts/ingress-nginx/templates/controller-servicemonitor.yaml index 066488a040..17894c8be2 100644 --- a/charts/ingress-nginx/templates/controller-servicemonitor.yaml +++ b/charts/ingress-nginx/templates/controller-servicemonitor.yaml @@ -1,4 +1,4 @@ -{{- if and .Values.controller.metrics.enabled .Values.controller.metrics.serviceMonitor.enabled -}} +{{- if and ( .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" ) .Values.controller.metrics.enabled .Values.controller.metrics.serviceMonitor.enabled -}} apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: diff --git a/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml b/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml index 153f005e25..9e586aa210 100644 --- a/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml +++ b/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml @@ -1,5 +1,5 @@ {{- if or (gt (.Values.defaultBackend.replicaCount | int) 1) (gt (.Values.defaultBackend.autoscaling.minReplicas | int) 1) }} -apiVersion: policy/v1beta1 +apiVersion: {{ ternary "policy/v1" "policy/v1beta1" (semverCompare ">=1.21.0-0" .Capabilities.KubeVersion.Version) }} kind: PodDisruptionBudget metadata: labels: diff --git a/charts/ingress-nginx/templates/default-backend-service.yaml b/charts/ingress-nginx/templates/default-backend-service.yaml index 7624ab36c4..f59eb1e7cc 100644 --- a/charts/ingress-nginx/templates/default-backend-service.yaml +++ b/charts/ingress-nginx/templates/default-backend-service.yaml @@ -29,6 +29,9 @@ spec: port: {{ .Values.defaultBackend.service.servicePort }} protocol: TCP targetPort: http + {{- if semverCompare ">=1.20" .Capabilities.KubeVersion.Version }} + appProtocol: http + {{- end }} selector: {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} app.kubernetes.io/component: default-backend diff --git a/charts/ingress-nginx/values.yaml b/charts/ingress-nginx/values.yaml index 460b19884a..e64e4ca2f6 100644 --- a/charts/ingress-nginx/values.yaml +++ b/charts/ingress-nginx/values.yaml @@ -1,5 +1,5 @@ ## nginx configuration -## Ref: https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/index.md +## Ref: https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/index.md ## ## Overrides for generated resource names @@ -15,8 +15,8 @@ controller: # for backwards compatibility consider setting the full image url via the repository value below # use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail # repository: - tag: "v0.47.0" - digest: sha256:a1e4efc107be0bb78f32eaec37bef17d7a0c81bec8066cdf2572508d21351d0b + tag: "v1.0.0" + digest: sha256:0851b34f69f69352bf168e6ccf30e1e20714a264ab1ecd1933e4d8c0fc3215c6 pullPolicy: IfNotPresent # www-data -> uid 101 runAsUser: 101 @@ -40,7 +40,7 @@ controller: ## configAnnotations: {} - # Will add custom headers before sending traffic to backends according to https://github.com/kubernetes/ingress-nginx/tree/master/docs/examples/customization/custom-headers + # Will add custom headers before sending traffic to backends according to https://github.com/kubernetes/ingress-nginx/tree/main/docs/examples/customization/custom-headers proxySetHeaders: {} # Will add custom headers before sending response traffic to the client according to: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#add-headers @@ -49,6 +49,9 @@ controller: # Optionally customize the pod dnsConfig. dnsConfig: {} + # Optionally customize the pod hostname. + hostname: {} + # Optionally change this to ClusterFirstWithHostNet in case you have 'hostNetwork: true'. # By default, while using host network, name resolution uses the host's DNS. If you wish nginx-controller # to keep resolving names inside the k8s network, use ClusterFirstWithHostNet. @@ -58,6 +61,11 @@ controller: # Ingress status was blank because there is no Service exposing the NGINX Ingress controller in a configuration using the host network, the default --publish-service flag used in standard cloud setups does not apply reportNodeInternalIp: false + # Process Ingress objects without ingressClass annotation/ingressClassName field + # Overrides value for --watch-ingress-without-class flag of the controller binary + # Defaults to false + watchIngressWithoutClass: false + # Required for use with CNI based kubernetes installations (such as ones set up by kubeadm), # since CNI and hostport don't mix yet. Can be deprecated once https://github.com/kubernetes/kubernetes/issues/23920 # is merged @@ -76,15 +84,13 @@ controller: ## electionID: ingress-controller-leader - ## Name of the ingress class to route through this controller - ## - ingressClass: nginx - # This section refers to the creation of the IngressClass resource - # IngressClass resources are supported since k8s >= 1.18 + # IngressClass resources are supported since k8s >= 1.18 and required since k8s >= 1.19 ingressClassResource: - enabled: false + name: nginx + enabled: true default: false + controllerValue: "k8s.io/ingress-nginx" # Parameters is a link to a custom resource containing additional # configuration for the controller. This is optional if the controller @@ -121,23 +127,23 @@ controller: ## scope: enabled: false - namespace: "" # defaults to .Release.Namespace + namespace: "" # defaults to $(POD_NAMESPACE) ## Allows customization of the configmap / nginx-configmap namespace ## - configMapNamespace: "" # defaults to .Release.Namespace + configMapNamespace: "" # defaults to $(POD_NAMESPACE) ## Allows customization of the tcp-services-configmap ## tcp: - configMapNamespace: "" # defaults to .Release.Namespace + configMapNamespace: "" # defaults to $(POD_NAMESPACE) ## Annotations to be added to the tcp config configmap annotations: {} ## Allows customization of the udp-services-configmap ## udp: - configMapNamespace: "" # defaults to .Release.Namespace + configMapNamespace: "" # defaults to $(POD_NAMESPACE) ## Annotations to be added to the udp config configmap annotations: {} @@ -332,6 +338,19 @@ controller: maxReplicas: 11 targetCPUUtilizationPercentage: 50 targetMemoryUtilizationPercentage: 50 + behavior: {} + # scaleDown: + # stabilizationWindowSeconds: 300 + # policies: + # - type: Pods + # value: 1 + # periodSeconds: 180 + # scaleUp: + # stabilizationWindowSeconds: 300 + # policies: + # - type: Pods + # value: 2 + # periodSeconds: 60 autoscalingTemplate: [] # Custom or additional autoscaling metrics @@ -526,21 +545,35 @@ controller: servicePort: 443 type: ClusterIP + createSecretJob: + resources: {} + # limits: + # cpu: 10m + # memory: 20Mi + # requests: + # cpu: 10m + # memory: 20Mi + + patchWebhookJob: + resources: {} + patch: enabled: true image: - registry: docker.io - image: jettech/kube-webhook-certgen + registry: k8s.gcr.io + image: ingress-nginx/kube-webhook-certgen # for backwards compatibility consider setting the full image url via the repository value below # use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail # repository: - tag: v1.5.1 + tag: v1.0 + digest: sha256:f3b6b39a6062328c095337b4cadcefd1612348fdd5190b1dcbcb9b9e90bd8068 pullPolicy: IfNotPresent ## Provide a priority class name to the webhook patching job ## priorityClassName: "" podAnnotations: {} - nodeSelector: {} + nodeSelector: + kubernetes.io/os: linux tolerations: [] runAsUser: 2000 @@ -717,7 +750,8 @@ defaultBackend: ## Node labels for default backend pod assignment ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ ## - nodeSelector: {} + nodeSelector: + kubernetes.io/os: linux ## Annotations to be added to default backend pods ## @@ -770,7 +804,7 @@ defaultBackend: priorityClassName: "" -## Enable RBAC as per https://github.com/kubernetes/ingress/tree/master/examples/rbac/nginx and https://github.com/kubernetes/ingress/issues/266 +## Enable RBAC as per https://github.com/kubernetes/ingress-nginx/blob/main/docs/deploy/rbac.md and https://github.com/kubernetes/ingress-nginx/issues/266 rbac: create: true scope: false @@ -791,18 +825,18 @@ imagePullSecrets: [] # - name: secretName # TCP service key:value pairs -# Ref: https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx/examples/tcp +# Ref: https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/exposing-tcp-udp-services.md ## tcp: {} # 8080: "default/example-tcp-svc:9000" # UDP service key:value pairs -# Ref: https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx/examples/udp +# Ref: https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/exposing-tcp-udp-services.md ## udp: {} # 53: "kube-system/kube-dns:53" # A base64ed Diffie-Hellman parameter # This can be generated with: openssl dhparam 4096 2> /dev/null | base64 -# Ref: https://github.com/krmichel/ingress-nginx/blob/master/docs/examples/customization/ssl-dh-param +# Ref: https://github.com/kubernetes/ingress-nginx/tree/main/docs/examples/customization/ssl-dh-param dhParam: diff --git a/charts/render.py b/charts/render.py index d991539774..d308102c59 100755 --- a/charts/render.py +++ b/charts/render.py @@ -333,6 +333,8 @@ def __call__(self, parser, args, values, option_string=None): "apiregistration.k8s.io/v1", # Available since Kubernetes 1.19 "networking.k8s.io/v1/Ingress", + # Used by ServiceMonitor and other monitoring objects + "monitoring.coreos.com/v1", ] command = [ @@ -344,6 +346,7 @@ def __call__(self, parser, args, values, option_string=None): "--values", args.values, "--include-crds", + "--skip-tests", args.path, ] diff --git a/eve/main.yml b/eve/main.yml index 5d7ee7cfc3..c5ed7ab77d 100644 --- a/eve/main.yml +++ b/eve/main.yml @@ -18,7 +18,7 @@ models: name: Git pull terraform snapshot command: > git clone --depth 1 "git@github.com:scality/terraform-snapshot.git" - --branch "0.2.0" + --branch "0.3.0" haltOnFailure: true - ShellCommand: &git_pull_ssh name: Git pull on bastion @@ -655,28 +655,28 @@ models: # OR if solution_base_url not set # "//" # default to: - # "https://github.com/scality/metalk8s-solution-example/releases/download/1.0.0/example-solution-1.0.0.iso" + # "https://github.com/scality/metalk8s-solution-example/releases/download/1.0.0/example-solution-1.0.2.iso" <<: *retrieve_iso doStepIf: "%(prop:install_solution:-false)s" name: Retrieve Solution archive env: <<: *_env_retrieve_artifact_retry BASE_URL: >- - %(prop:solution_base_url:-%(prop:solution_repo:-https://github.com/scality/metalk8s-solution-example/releases/download)s/%(prop:solution_version:-1.0.0)s)s - FILE_SOURCE: "%(prop:solution_archive:-example-solution-1.0.0.iso)s" - FILE_DEST: "%(prop:solution_archive:-example-solution-1.0.0.iso)s" + %(prop:solution_base_url:-%(prop:solution_repo:-https://github.com/scality/metalk8s-solution-example/releases/download)s/%(prop:solution_version:-1.0.2)s)s + FILE_SOURCE: "%(prop:solution_archive:-example-solution-1.0.2.iso)s" + FILE_DEST: "%(prop:solution_archive:-example-solution-1.0.2.iso)s" - ShellCommand: ©_solution_archive_bootstrap_ssh doStepIf: "%(prop:install_solution:-false)s" name: Copy Solution archive to bootstrap <<: *copy_iso_bootstrap_ssh env: &_env_copy_solution_archive_bootstrap_ssh <<: *_env_copy_iso_bootstrap_ssh - ARCHIVE: "%(prop:solution_archive:-example-solution-1.0.0.iso)s" + ARCHIVE: "%(prop:solution_archive:-example-solution-1.0.2.iso)s" - ShellCommand: &import_solution doStepIf: "%(prop:install_solution:-false)s" name: Import Solution archive env: - SOLUTION_ARCHIVE: "%(prop:solution_archive:-example-solution-1.0.0.iso)s" + SOLUTION_ARCHIVE: "%(prop:solution_archive:-example-solution-1.0.2.iso)s" METALK8S_MOUNTPOINT: "/srv/scality/metalk8s-%(prop:metalk8s_version)s" command: > ssh -F ssh_config bootstrap @@ -689,7 +689,7 @@ models: name: Activate Solution env: SOLUTION_NAME: "%(prop:solution_name:-example-solution)s" - SOLUTION_VERSION: "%(prop:solution_version:-1.0.0)s" + SOLUTION_VERSION: "%(prop:solution_version:-1.0.2)s" METALK8S_MOUNTPOINT: "/srv/scality/metalk8s-%(prop:metalk8s_version)s" command: > ssh -F ssh_config bootstrap @@ -730,7 +730,7 @@ models: env: ENVIRONMENT_NAME: "%(prop:solution_env_name:-example-environment)s" SOLUTION_NAME: "%(prop:solution_name:-example-solution)s" - SOLUTION_VERSION: "%(prop:solution_version:-1.0.0)s" + SOLUTION_VERSION: "%(prop:solution_version:-1.0.2)s" METALK8S_MOUNTPOINT: "/srv/scality/metalk8s-%(prop:metalk8s_version)s" command: > ssh -F ssh_config bootstrap @@ -908,11 +908,11 @@ stages: - SetProperty: name: Set Example Solution version property property: example_solution_version - value: 1.0.0 + value: 1.0.2 - SetProperty: name: Set Example Solution version next property property: example_solution_version_next - value: 1.0.1 + value: 1.0.3 - TriggerStages: name: Trigger Solutions framework tests stage_names: @@ -945,7 +945,9 @@ stages: name: Trigger upgrade and downgrade test stages simultaneously stage_names: - single-node-upgrade-centos - - single-node-downgrade-centos + # NOTE: Deactivate minor downgrade on this branch + # See: https://github.com/scality/metalk8s/issues/1750 + #- single-node-downgrade-centos waitForFinish: true create-upload-testrail-objects: @@ -1900,7 +1902,9 @@ stages: stage_names: - snapshot-single-node-upgrades - snapshot-multi-node-upgrades - - single-node-downgrade-promoted-centos + # NOTE: Deactivate minor downgrade on this branch + # See: https://github.com/scality/metalk8s/issues/1750 + #- single-node-downgrade-promoted-centos snapshot-single-node-upgrades: # NOTE: This stage just set `environment_type` property to diff --git a/packages/redhat/common/calico-cni-plugin.spec b/packages/redhat/common/calico-cni-plugin.spec index c96769bd93..2416240839 100644 --- a/packages/redhat/common/calico-cni-plugin.spec +++ b/packages/redhat/common/calico-cni-plugin.spec @@ -6,12 +6,12 @@ %ifarch x86_64 %global built_arch amd64 -%global calico_sha256 08786c58ad05bdd274ac4433e522853a08ade0cf2ca6e3cbfb5b4adb52bd303a -%global calico_ipam_sha256 08786c58ad05bdd274ac4433e522853a08ade0cf2ca6e3cbfb5b4adb52bd303a +%global calico_sha256 3a8b8e80599597bed8a4c10a43bad6424c2f3d8c00b3bb26f1268a0cae2a60da +%global calico_ipam_sha256 3a8b8e80599597bed8a4c10a43bad6424c2f3d8c00b3bb26f1268a0cae2a60da %endif Name: calico-cni-plugin -Version: 3.19.1 +Version: 3.20.0 Release: 1%{?dist} Summary: Calico CNI plugin @@ -49,6 +49,9 @@ install -p -m 755 %{SOURCE2} %{buildroot}/opt/cni/bin/calico-ipam %doc README.md %changelog +* Wed Sep 8 2021 Teddy Andrieux - 3.20.0.1-1 +- Version bump + * Tue Jun 29 2021 Teddy Andrieux - 3.19.1-1 - Version bump diff --git a/salt/_pillar/metalk8s.py b/salt/_pillar/metalk8s.py index b70d877f6a..303029564a 100644 --- a/salt/_pillar/metalk8s.py +++ b/salt/_pillar/metalk8s.py @@ -94,13 +94,18 @@ def _load_networks(config_data): # MetalLB disabled by default networks_data["controlPlane"].setdefault("metalLB", {}).setdefault("enabled", False) - if networks_data["controlPlane"]["metalLB"]["enabled"] and not networks_data[ - "controlPlane" - ].get("ingress", {}).get("ip"): - errors.append( - "'ip' for 'ingress' in 'controlPlane' network is mandatory when 'metalLB'" - "is enabled" - ) + if networks_data["controlPlane"]["metalLB"]["enabled"]: + if not networks_data["controlPlane"].get("ingress", {}).get("ip"): + errors.append( + "'ip' for 'ingress' in 'controlPlane' network is mandatory when " + "'metalLB' is enabled" + ) + if len(networks_data["controlPlane"]["cidr"]) > 1: + errors.append( + "Enabling 'metalLB' requires a single 'cidr' in " + "'controlPlane' network, see " + "https://github.com/scality/metalk8s/issues/3502" + ) if errors: return __utils__["pillar_utils.errors_to_dict"](errors) diff --git a/salt/metalk8s/addons/dex/config/dex.yaml.j2 b/salt/metalk8s/addons/dex/config/dex.yaml.j2 index f5f9cff540..3acb71d177 100644 --- a/salt/metalk8s/addons/dex/config/dex.yaml.j2 +++ b/salt/metalk8s/addons/dex/config/dex.yaml.j2 @@ -39,6 +39,7 @@ spec: tlsKey: /etc/dex/tls/https/server/tls.key frontend: + dir: /srv/dex/web/ theme: scality issuer: MetalK8s diff --git a/salt/metalk8s/addons/dex/deployed/chart.sls b/salt/metalk8s/addons/dex/deployed/chart.sls index eb9a13945a..db4c7a4042 100644 --- a/salt/metalk8s/addons/dex/deployed/chart.sls +++ b/salt/metalk8s/addons/dex/deployed/chart.sls @@ -14,8 +14,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: dex app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 2.28.1 - helm.sh/chart: dex-0.4.0 + app.kubernetes.io/version: 2.30.0 + helm.sh/chart: dex-0.6.3 heritage: metalk8s name: dex namespace: metalk8s-auth @@ -28,8 +28,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: dex app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 2.28.1 - helm.sh/chart: dex-0.4.0 + app.kubernetes.io/version: 2.30.0 + helm.sh/chart: dex-0.6.3 heritage: metalk8s name: dex namespace: metalk8s-auth @@ -55,8 +55,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: dex app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 2.28.1 - helm.sh/chart: dex-0.4.0 + app.kubernetes.io/version: 2.30.0 + helm.sh/chart: dex-0.6.3 heritage: metalk8s name: dex namespace: metalk8s-auth @@ -77,8 +77,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: dex app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 2.28.1 - helm.sh/chart: dex-0.4.0 + app.kubernetes.io/version: 2.30.0 + helm.sh/chart: dex-0.6.3 heritage: metalk8s name: dex namespace: metalk8s-auth @@ -113,8 +113,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: dex app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 2.28.1 - helm.sh/chart: dex-0.4.0 + app.kubernetes.io/version: 2.30.0 + helm.sh/chart: dex-0.6.3 heritage: metalk8s name: dex namespace: metalk8s-auth @@ -147,7 +147,7 @@ spec: env: - name: KUBERNETES_POD_NAMESPACE value: metalk8s-auth - image: {% endraw -%}{{ build_image_name("dex", False) }}{%- raw %}:v2.28.1 + image: {% endraw -%}{{ build_image_name("dex", False) }}{%- raw %}:v2.30.0 imagePullPolicy: IfNotPresent livenessProbe: httpGet: @@ -176,7 +176,7 @@ spec: readOnly: true - mountPath: /etc/dex/tls/https/server name: https-tls - - mountPath: /web/themes/scality + - mountPath: /srv/dex/web/themes/scality name: dex-login - mountPath: /etc/ssl/certs/nginx-ingress-ca.crt name: nginx-ingress-ca-cert @@ -217,8 +217,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: dex app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 2.28.1 - helm.sh/chart: dex-0.4.0 + app.kubernetes.io/version: 2.30.0 + helm.sh/chart: dex-0.6.3 heritage: metalk8s name: dex namespace: metalk8s-auth diff --git a/salt/metalk8s/addons/logging/fluent-bit/deployed/configmap.sls b/salt/metalk8s/addons/logging/fluent-bit/deployed/configmap.sls index aef457d365..e5fe96574d 100644 --- a/salt/metalk8s/addons/logging/fluent-bit/deployed/configmap.sls +++ b/salt/metalk8s/addons/logging/fluent-bit/deployed/configmap.sls @@ -28,7 +28,7 @@ Create fluent-bit ConfigMap: data: fluent-bit.conf: |- [SERVICE] - HTTP_Server On + HTTP_Server Off HTTP_Listen 0.0.0.0 HTTP_PORT 2020 Flush 1 diff --git a/salt/metalk8s/addons/nginx-ingress-control-plane/deployed/chart-daemonset.sls b/salt/metalk8s/addons/nginx-ingress-control-plane/deployed/chart-daemonset.sls index ccd978ba4b..6670c342e6 100644 --- a/salt/metalk8s/addons/nginx-ingress-control-plane/deployed/chart-daemonset.sls +++ b/salt/metalk8s/addons/nginx-ingress-control-plane/deployed/chart-daemonset.sls @@ -16,8 +16,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane namespace: metalk8s-ingress @@ -32,8 +32,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane-controller namespace: metalk8s-ingress @@ -46,8 +46,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane namespace: metalk8s-ingress @@ -78,7 +78,6 @@ rules: - list - watch - apiGroups: - - extensions - networking.k8s.io resources: - ingresses @@ -94,7 +93,6 @@ rules: - create - patch - apiGroups: - - extensions - networking.k8s.io resources: - ingresses/status @@ -117,8 +115,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane namespace: metalk8s-ingress @@ -140,8 +138,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane namespace: metalk8s-ingress @@ -172,7 +170,6 @@ rules: - list - watch - apiGroups: - - extensions - networking.k8s.io resources: - ingresses @@ -181,7 +178,6 @@ rules: - list - watch - apiGroups: - - extensions - networking.k8s.io resources: - ingresses/status @@ -198,7 +194,7 @@ rules: - apiGroups: - '' resourceNames: - - ingress-control-plane-controller-leader-nginx-control-plane + - ingress-control-plane-controller-leader resources: - configmaps verbs: @@ -227,8 +223,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane namespace: metalk8s-ingress @@ -250,8 +246,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane-controller-metrics namespace: metalk8s-ingress @@ -276,8 +272,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane-controller namespace: metalk8s-ingress @@ -286,7 +282,8 @@ spec: - '{%- endraw -%}{{ salt.metalk8s_network.get_control_plane_ingress_ip() }}{%- raw -%}' ports: - - name: https + - appProtocol: https + name: https port: 8443 protocol: TCP targetPort: https @@ -305,8 +302,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane-controller namespace: metalk8s-ingress @@ -330,8 +327,8 @@ spec: - /nginx-ingress-controller - --publish-service=$(POD_NAMESPACE)/ingress-nginx-control-plane-controller - --election-id=ingress-control-plane-controller-leader - - --ingress-class=nginx-control-plane - - --configmap=metalk8s-ingress/ingress-nginx-control-plane-controller + - --controller-class=k8s.io/ingress-nginx-control-plane + - --configmap=$(POD_NAMESPACE)/ingress-nginx-control-plane-controller - --default-ssl-certificate=metalk8s-ingress/ingress-control-plane-default-certificate - --metrics-per-host=false env: @@ -346,7 +343,7 @@ spec: - name: LD_PRELOAD value: /usr/local/lib/libmimalloc.so image: '{%- endraw -%}{{ build_image_name("nginx-ingress-controller", False) - }}{%- raw -%}:v0.47.0' + }}{%- raw -%}:v1.0.0' imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -415,6 +412,23 @@ spec: updateStrategy: type: RollingUpdate --- +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx-control-plane + app.kubernetes.io/managed-by: salt + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: metalk8s + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 + heritage: metalk8s + name: nginx-control-plane + namespace: metalk8s-ingress +spec: + controller: k8s.io/ingress-nginx-control-plane +--- apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: @@ -424,8 +438,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s metalk8s.scality.com/monitor: '' name: ingress-nginx-control-plane-controller diff --git a/salt/metalk8s/addons/nginx-ingress-control-plane/deployed/chart-deployment.sls b/salt/metalk8s/addons/nginx-ingress-control-plane/deployed/chart-deployment.sls index 2c1ae0b315..63fb1ca03d 100644 --- a/salt/metalk8s/addons/nginx-ingress-control-plane/deployed/chart-deployment.sls +++ b/salt/metalk8s/addons/nginx-ingress-control-plane/deployed/chart-deployment.sls @@ -15,8 +15,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane-controller namespace: metalk8s-ingress @@ -38,8 +38,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane namespace: metalk8s-ingress @@ -54,8 +54,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane-backend namespace: metalk8s-ingress @@ -70,8 +70,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane-controller namespace: metalk8s-ingress @@ -84,8 +84,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane namespace: metalk8s-ingress @@ -116,7 +116,6 @@ rules: - list - watch - apiGroups: - - extensions - networking.k8s.io resources: - ingresses @@ -132,7 +131,6 @@ rules: - create - patch - apiGroups: - - extensions - networking.k8s.io resources: - ingresses/status @@ -155,8 +153,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane namespace: metalk8s-ingress @@ -178,8 +176,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane namespace: metalk8s-ingress @@ -210,7 +208,6 @@ rules: - list - watch - apiGroups: - - extensions - networking.k8s.io resources: - ingresses @@ -219,7 +216,6 @@ rules: - list - watch - apiGroups: - - extensions - networking.k8s.io resources: - ingresses/status @@ -236,7 +232,7 @@ rules: - apiGroups: - '' resourceNames: - - ingress-control-plane-controller-leader-nginx-control-plane + - ingress-control-plane-controller-leader resources: - configmaps verbs: @@ -265,8 +261,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane namespace: metalk8s-ingress @@ -288,8 +284,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane-controller-metrics namespace: metalk8s-ingress @@ -314,8 +310,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane-controller namespace: metalk8s-ingress @@ -323,7 +319,8 @@ spec: externalTrafficPolicy: Local loadBalancerIP: {% endraw -%}{{ salt.metalk8s_network.get_control_plane_ingress_ip() }}{%- raw %} ports: - - name: https + - appProtocol: https + name: https port: 8443 protocol: TCP targetPort: https @@ -342,14 +339,15 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane-defaultbackend namespace: metalk8s-ingress spec: ports: - - name: http + - appProtocol: http + name: http port: 80 protocol: TCP targetPort: http @@ -368,8 +366,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane-controller namespace: metalk8s-ingress @@ -397,7 +395,7 @@ spec: - --default-backend-service=$(POD_NAMESPACE)/ingress-nginx-control-plane-defaultbackend - --publish-service=$(POD_NAMESPACE)/ingress-nginx-control-plane-controller - --election-id=ingress-control-plane-controller-leader - - --ingress-class=nginx-control-plane + - --controller-class=k8s.io/ingress-nginx-control-plane - --configmap=$(POD_NAMESPACE)/ingress-nginx-control-plane-controller - --default-ssl-certificate=metalk8s-ingress/ingress-control-plane-default-certificate - --metrics-per-host=false @@ -412,7 +410,7 @@ spec: fieldPath: metadata.namespace - name: LD_PRELOAD value: /usr/local/lib/libmimalloc.so - image: {% endraw -%}{{ build_image_name("nginx-ingress-controller", False) }}{%- raw %}:v0.47.0 + image: {% endraw -%}{{ build_image_name("nginx-ingress-controller", False) }}{%- raw %}:v1.0.0 imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -488,8 +486,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-control-plane-defaultbackend namespace: metalk8s-ingress @@ -545,6 +543,7 @@ spec: runAsNonRoot: true runAsUser: 65534 nodeSelector: + kubernetes.io/os: linux node-role.kubernetes.io/master: '' serviceAccountName: ingress-nginx-control-plane-backend terminationGracePeriodSeconds: 60 @@ -559,6 +558,23 @@ spec: key: node-role.kubernetes.io/infra operator: Exists --- +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx-control-plane + app.kubernetes.io/managed-by: salt + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: metalk8s + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 + heritage: metalk8s + name: nginx-control-plane + namespace: metalk8s-ingress +spec: + controller: k8s.io/ingress-nginx-control-plane +--- apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: @@ -568,8 +584,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s metalk8s.scality.com/monitor: '' name: ingress-nginx-control-plane-controller diff --git a/salt/metalk8s/addons/nginx-ingress/deployed/chart.sls b/salt/metalk8s/addons/nginx-ingress/deployed/chart.sls index 5aaeb0c6bd..4e96158bc9 100644 --- a/salt/metalk8s/addons/nginx-ingress/deployed/chart.sls +++ b/salt/metalk8s/addons/nginx-ingress/deployed/chart.sls @@ -16,8 +16,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx namespace: metalk8s-ingress @@ -32,8 +32,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-backend namespace: metalk8s-ingress @@ -48,8 +48,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-controller namespace: metalk8s-ingress @@ -62,8 +62,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx namespace: metalk8s-ingress @@ -94,7 +94,6 @@ rules: - list - watch - apiGroups: - - extensions - networking.k8s.io resources: - ingresses @@ -110,7 +109,6 @@ rules: - create - patch - apiGroups: - - extensions - networking.k8s.io resources: - ingresses/status @@ -133,8 +131,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx namespace: metalk8s-ingress @@ -156,8 +154,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx namespace: metalk8s-ingress @@ -188,7 +186,6 @@ rules: - list - watch - apiGroups: - - extensions - networking.k8s.io resources: - ingresses @@ -197,7 +194,6 @@ rules: - list - watch - apiGroups: - - extensions - networking.k8s.io resources: - ingresses/status @@ -214,7 +210,7 @@ rules: - apiGroups: - '' resourceNames: - - ingress-controller-leader-nginx + - ingress-controller-leader resources: - configmaps verbs: @@ -243,8 +239,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx namespace: metalk8s-ingress @@ -266,8 +262,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-controller-metrics namespace: metalk8s-ingress @@ -292,18 +288,20 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-controller namespace: metalk8s-ingress spec: ports: - - name: http + - appProtocol: http + name: http port: 80 protocol: TCP targetPort: http - - name: https + - appProtocol: https + name: https port: 443 protocol: TCP targetPort: https @@ -322,14 +320,15 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-defaultbackend namespace: metalk8s-ingress spec: ports: - - name: http + - appProtocol: http + name: http port: 80 protocol: TCP targetPort: http @@ -348,8 +347,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-controller namespace: metalk8s-ingress @@ -371,11 +370,11 @@ spec: containers: - args: - /nginx-ingress-controller - - --default-backend-service=metalk8s-ingress/ingress-nginx-defaultbackend + - --default-backend-service=$(POD_NAMESPACE)/ingress-nginx-defaultbackend - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller - --election-id=ingress-controller-leader - - --ingress-class=nginx - - --configmap=metalk8s-ingress/ingress-nginx-controller + - --controller-class=k8s.io/ingress-nginx + - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller - --default-ssl-certificate=metalk8s-ingress/ingress-workload-plane-default-certificate - --metrics-per-host=false env: @@ -390,7 +389,7 @@ spec: - name: LD_PRELOAD value: /usr/local/lib/libmimalloc.so image: '{%- endraw -%}{{ build_image_name("nginx-ingress-controller", False) - }}{%- raw -%}:v0.47.0' + }}{%- raw -%}:v1.0.0' imagePullPolicy: IfNotPresent lifecycle: preStop: @@ -464,8 +463,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s name: ingress-nginx-defaultbackend namespace: metalk8s-ingress @@ -522,6 +521,7 @@ spec: runAsNonRoot: true runAsUser: 65534 nodeSelector: + kubernetes.io/os: linux node-role.kubernetes.io/infra: '' serviceAccountName: ingress-nginx-backend terminationGracePeriodSeconds: 60 @@ -533,6 +533,25 @@ spec: key: node-role.kubernetes.io/infra operator: Exists --- +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + annotations: + ingressclass.kubernetes.io/is-default-class: 'true' + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: ingress-nginx + app.kubernetes.io/managed-by: salt + app.kubernetes.io/name: ingress-nginx + app.kubernetes.io/part-of: metalk8s + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 + heritage: metalk8s + name: nginx + namespace: metalk8s-ingress +spec: + controller: k8s.io/ingress-nginx +--- apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: @@ -542,8 +561,8 @@ metadata: app.kubernetes.io/managed-by: salt app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: metalk8s - app.kubernetes.io/version: 0.47.0 - helm.sh/chart: ingress-nginx-3.34.0 + app.kubernetes.io/version: 1.0.0 + helm.sh/chart: ingress-nginx-4.0.1 heritage: metalk8s metalk8s.scality.com/monitor: '' name: ingress-nginx-controller diff --git a/salt/metalk8s/defaults.yaml b/salt/metalk8s/defaults.yaml index 5ac1bec4df..7d4e412599 100644 --- a/salt/metalk8s/defaults.yaml +++ b/salt/metalk8s/defaults.yaml @@ -7,7 +7,7 @@ metalk8s: # (e.g. downgrade of etcd), prior to downgrading the cluster. # The downgrade can still be forced setting `metalk8s.downgrade.enabled` # to `True` in the pillar. - enabled: true + enabled: false kubernetes: cluster: kubernetes diff --git a/salt/metalk8s/kubernetes/apiserver/installed.sls b/salt/metalk8s/kubernetes/apiserver/installed.sls index 9684cc6d7b..a1f1655ef2 100644 --- a/salt/metalk8s/kubernetes/apiserver/installed.sls +++ b/salt/metalk8s/kubernetes/apiserver/installed.sls @@ -74,7 +74,6 @@ Create kube-apiserver Pod manifest: - --etcd-certfile={{ certificates.client.files['apiserver-etcd'].path }} - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key - --etcd-servers={{ etcd_servers | join(",") }} - - --insecure-port=0 - --kubelet-client-certificate={{ certificates.client.files['apiserver-kubelet'].path }} - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname diff --git a/salt/metalk8s/kubernetes/cni/calico/deployed.sls b/salt/metalk8s/kubernetes/cni/calico/deployed.sls index fac2af6719..2e05d4dea9 100644 --- a/salt/metalk8s/kubernetes/cni/calico/deployed.sls +++ b/salt/metalk8s/kubernetes/cni/calico/deployed.sls @@ -256,6 +256,11 @@ spec: Peers node to use the "next hop keep;" instead of "next hop self;"(default) in the specific branch of the Node on "bird.cfg". type: boolean + maxRestartTime: + description: Time to allow for software restart. When specified, this + is configured as the graceful restart timeout. When not specified, + the BIRD default of 120s is used. + type: string node: description: The node name identifying the Calico node instance that is targeted by this peer. If this is not set, and no nodeSelector @@ -526,13 +531,6 @@ spec: description: 'BPFEnabled, if enabled Felix will use the BPF dataplane. [Default: false]' type: boolean - bpfExtToServiceConnmark: - description: 'BPFExtToServiceConnmark in BPF mode, control a 32bit - mark that is set on connections from an external client to a local - service. This mark allows us to control how packets of that connection - are routed within the host and how is routing intepreted by RPF - check. [Default: 0]' - type: integer bpfExternalServiceMode: description: 'BPFExternalServiceMode in BPF mode, controls how connections from outside the cluster to services (node ports and cluster IPs) @@ -543,6 +541,14 @@ spec: node appears to use the IP of the ingress node; this requires a permissive L2 network. [Default: Tunnel]' type: string + bpfExtToServiceConnmark: + description: 'BPFExtToServiceConnmark in BPF mode, controls a + 32bit mark that is set on connections from an external client to + a local service. This mark allows us to control how packets of + that connection are routed within the host and how is routing + intepreted by RPF check. [Default: 0]' + type: integer + bpfKubeProxyEndpointSlicesEnabled: description: BPFKubeProxyEndpointSlicesEnabled in BPF mode, controls whether Felix's embedded kube-proxy accepts EndpointSlices or not. @@ -1076,16 +1082,17 @@ spec: contains a selector expression. Only traffic that originates from (or terminates at) endpoints within the selected namespaces will be matched. When both NamespaceSelector - and Selector are defined on the same rule, then only workload - endpoints that are matched by both selectors will be selected - by the rule. \n For NetworkPolicy, an empty NamespaceSelector - implies that the Selector is limited to selecting only - workload endpoints in the same namespace as the NetworkPolicy. - \n For NetworkPolicy, `global()` NamespaceSelector implies - that the Selector is limited to selecting only GlobalNetworkSet - or HostEndpoint. \n For GlobalNetworkPolicy, an empty - NamespaceSelector implies the Selector applies to workload - endpoints across all namespaces." + and another selector are defined on the same rule, then + only workload endpoints that are matched by both selectors + will be selected by the rule. \n For NetworkPolicy, an + empty NamespaceSelector implies that the Selector is limited + to selecting only workload endpoints in the same namespace + as the NetworkPolicy. \n For NetworkPolicy, `global()` + NamespaceSelector implies that the Selector is limited + to selecting only GlobalNetworkSet or HostEndpoint. \n + For GlobalNetworkPolicy, an empty NamespaceSelector implies + the Selector applies to workload endpoints across all + namespaces." type: string nets: description: Nets is an optional field that restricts the @@ -1173,6 +1180,26 @@ spec: AND'ed. type: string type: object + services: + description: "Services is an optional field that contains + options for matching Kubernetes Services. If specified, + only traffic that originates from or terminates at endpoints + within the selected service(s) will be matched, and only + to/from each endpoint's port. \n Services cannot be specified + on the same rule as Selector, NotSelector, NamespaceSelector, + Ports, NotPorts, Nets, NotNets or ServiceAccounts. \n + Only valid on egress rules." + properties: + name: + description: Name specifies the name of a Kubernetes + Service to match. + type: string + namespace: + description: Namespace specifies the namespace of the + given Service. If left empty, the rule will match + within this policy's namespace. + type: string + type: object type: object http: description: HTTP contains match criteria that apply to HTTP @@ -1281,16 +1308,17 @@ spec: contains a selector expression. Only traffic that originates from (or terminates at) endpoints within the selected namespaces will be matched. When both NamespaceSelector - and Selector are defined on the same rule, then only workload - endpoints that are matched by both selectors will be selected - by the rule. \n For NetworkPolicy, an empty NamespaceSelector - implies that the Selector is limited to selecting only - workload endpoints in the same namespace as the NetworkPolicy. - \n For NetworkPolicy, `global()` NamespaceSelector implies - that the Selector is limited to selecting only GlobalNetworkSet - or HostEndpoint. \n For GlobalNetworkPolicy, an empty - NamespaceSelector implies the Selector applies to workload - endpoints across all namespaces." + and another selector are defined on the same rule, then + only workload endpoints that are matched by both selectors + will be selected by the rule. \n For NetworkPolicy, an + empty NamespaceSelector implies that the Selector is limited + to selecting only workload endpoints in the same namespace + as the NetworkPolicy. \n For NetworkPolicy, `global()` + NamespaceSelector implies that the Selector is limited + to selecting only GlobalNetworkSet or HostEndpoint. \n + For GlobalNetworkPolicy, an empty NamespaceSelector implies + the Selector applies to workload endpoints across all + namespaces." type: string nets: description: Nets is an optional field that restricts the @@ -1378,6 +1406,26 @@ spec: AND'ed. type: string type: object + services: + description: "Services is an optional field that contains + options for matching Kubernetes Services. If specified, + only traffic that originates from or terminates at endpoints + within the selected service(s) will be matched, and only + to/from each endpoint's port. \n Services cannot be specified + on the same rule as Selector, NotSelector, NamespaceSelector, + Ports, NotPorts, Nets, NotNets or ServiceAccounts. \n + Only valid on egress rules." + properties: + name: + description: Name specifies the name of a Kubernetes + Service to match. + type: string + namespace: + description: Namespace specifies the namespace of the + given Service. If left empty, the rule will match + within this policy's namespace. + type: string + type: object type: object required: - action @@ -1407,16 +1455,17 @@ spec: contains a selector expression. Only traffic that originates from (or terminates at) endpoints within the selected namespaces will be matched. When both NamespaceSelector - and Selector are defined on the same rule, then only workload - endpoints that are matched by both selectors will be selected - by the rule. \n For NetworkPolicy, an empty NamespaceSelector - implies that the Selector is limited to selecting only - workload endpoints in the same namespace as the NetworkPolicy. - \n For NetworkPolicy, `global()` NamespaceSelector implies - that the Selector is limited to selecting only GlobalNetworkSet - or HostEndpoint. \n For GlobalNetworkPolicy, an empty - NamespaceSelector implies the Selector applies to workload - endpoints across all namespaces." + and another selector are defined on the same rule, then + only workload endpoints that are matched by both selectors + will be selected by the rule. \n For NetworkPolicy, an + empty NamespaceSelector implies that the Selector is limited + to selecting only workload endpoints in the same namespace + as the NetworkPolicy. \n For NetworkPolicy, `global()` + NamespaceSelector implies that the Selector is limited + to selecting only GlobalNetworkSet or HostEndpoint. \n + For GlobalNetworkPolicy, an empty NamespaceSelector implies + the Selector applies to workload endpoints across all + namespaces." type: string nets: description: Nets is an optional field that restricts the @@ -1504,6 +1553,26 @@ spec: AND'ed. type: string type: object + services: + description: "Services is an optional field that contains + options for matching Kubernetes Services. If specified, + only traffic that originates from or terminates at endpoints + within the selected service(s) will be matched, and only + to/from each endpoint's port. \n Services cannot be specified + on the same rule as Selector, NotSelector, NamespaceSelector, + Ports, NotPorts, Nets, NotNets or ServiceAccounts. \n + Only valid on egress rules." + properties: + name: + description: Name specifies the name of a Kubernetes + Service to match. + type: string + namespace: + description: Namespace specifies the namespace of the + given Service. If left empty, the rule will match + within this policy's namespace. + type: string + type: object type: object http: description: HTTP contains match criteria that apply to HTTP @@ -1612,16 +1681,17 @@ spec: contains a selector expression. Only traffic that originates from (or terminates at) endpoints within the selected namespaces will be matched. When both NamespaceSelector - and Selector are defined on the same rule, then only workload - endpoints that are matched by both selectors will be selected - by the rule. \n For NetworkPolicy, an empty NamespaceSelector - implies that the Selector is limited to selecting only - workload endpoints in the same namespace as the NetworkPolicy. - \n For NetworkPolicy, `global()` NamespaceSelector implies - that the Selector is limited to selecting only GlobalNetworkSet - or HostEndpoint. \n For GlobalNetworkPolicy, an empty - NamespaceSelector implies the Selector applies to workload - endpoints across all namespaces." + and another selector are defined on the same rule, then + only workload endpoints that are matched by both selectors + will be selected by the rule. \n For NetworkPolicy, an + empty NamespaceSelector implies that the Selector is limited + to selecting only workload endpoints in the same namespace + as the NetworkPolicy. \n For NetworkPolicy, `global()` + NamespaceSelector implies that the Selector is limited + to selecting only GlobalNetworkSet or HostEndpoint. \n + For GlobalNetworkPolicy, an empty NamespaceSelector implies + the Selector applies to workload endpoints across all + namespaces." type: string nets: description: Nets is an optional field that restricts the @@ -1709,6 +1779,26 @@ spec: AND'ed. type: string type: object + services: + description: "Services is an optional field that contains + options for matching Kubernetes Services. If specified, + only traffic that originates from or terminates at endpoints + within the selected service(s) will be matched, and only + to/from each endpoint's port. \n Services cannot be specified + on the same rule as Selector, NotSelector, NamespaceSelector, + Ports, NotPorts, Nets, NotNets or ServiceAccounts. \n + Only valid on egress rules." + properties: + name: + description: Name specifies the name of a Kubernetes + Service to match. + type: string + namespace: + description: Namespace specifies the namespace of the + given Service. If left empty, the rule will match + within this policy's namespace. + type: string + type: object type: object required: - action @@ -2293,6 +2383,11 @@ spec: host endpoints for every node. [Default: Disabled]' type: string type: object + leakGracePeriod: + description: 'LeakGracePeriod is the period used by the controller + to determine if an IP address has been leaked. Set to 0 + to disable IP garbage collection. [Default: 15m]' + type: string reconcilerPeriod: description: 'ReconcilerPeriod is the period to perform reconciliation with the Calico datastore. [Default: 5m]' @@ -2393,6 +2488,12 @@ spec: of host endpoints for every node. [Default: Disabled]' type: string type: object + leakGracePeriod: + description: 'LeakGracePeriod is the period used by the + controller to determine if an IP address has been leaked. + Set to 0 to disable IP garbage collection. [Default: + 15m]' + type: string reconcilerPeriod: description: 'ReconcilerPeriod is the period to perform reconciliation with the Calico datastore. [Default: @@ -2521,16 +2622,17 @@ spec: contains a selector expression. Only traffic that originates from (or terminates at) endpoints within the selected namespaces will be matched. When both NamespaceSelector - and Selector are defined on the same rule, then only workload - endpoints that are matched by both selectors will be selected - by the rule. \n For NetworkPolicy, an empty NamespaceSelector - implies that the Selector is limited to selecting only - workload endpoints in the same namespace as the NetworkPolicy. - \n For NetworkPolicy, `global()` NamespaceSelector implies - that the Selector is limited to selecting only GlobalNetworkSet - or HostEndpoint. \n For GlobalNetworkPolicy, an empty - NamespaceSelector implies the Selector applies to workload - endpoints across all namespaces." + and another selector are defined on the same rule, then + only workload endpoints that are matched by both selectors + will be selected by the rule. \n For NetworkPolicy, an + empty NamespaceSelector implies that the Selector is limited + to selecting only workload endpoints in the same namespace + as the NetworkPolicy. \n For NetworkPolicy, `global()` + NamespaceSelector implies that the Selector is limited + to selecting only GlobalNetworkSet or HostEndpoint. \n + For GlobalNetworkPolicy, an empty NamespaceSelector implies + the Selector applies to workload endpoints across all + namespaces." type: string nets: description: Nets is an optional field that restricts the @@ -2618,6 +2720,26 @@ spec: AND'ed. type: string type: object + services: + description: "Services is an optional field that contains + options for matching Kubernetes Services. If specified, + only traffic that originates from or terminates at endpoints + within the selected service(s) will be matched, and only + to/from each endpoint's port. \n Services cannot be specified + on the same rule as Selector, NotSelector, NamespaceSelector, + Ports, NotPorts, Nets, NotNets or ServiceAccounts. \n + Only valid on egress rules." + properties: + name: + description: Name specifies the name of a Kubernetes + Service to match. + type: string + namespace: + description: Namespace specifies the namespace of the + given Service. If left empty, the rule will match + within this policy's namespace. + type: string + type: object type: object http: description: HTTP contains match criteria that apply to HTTP @@ -2726,16 +2848,17 @@ spec: contains a selector expression. Only traffic that originates from (or terminates at) endpoints within the selected namespaces will be matched. When both NamespaceSelector - and Selector are defined on the same rule, then only workload - endpoints that are matched by both selectors will be selected - by the rule. \n For NetworkPolicy, an empty NamespaceSelector - implies that the Selector is limited to selecting only - workload endpoints in the same namespace as the NetworkPolicy. - \n For NetworkPolicy, `global()` NamespaceSelector implies - that the Selector is limited to selecting only GlobalNetworkSet - or HostEndpoint. \n For GlobalNetworkPolicy, an empty - NamespaceSelector implies the Selector applies to workload - endpoints across all namespaces." + and another selector are defined on the same rule, then + only workload endpoints that are matched by both selectors + will be selected by the rule. \n For NetworkPolicy, an + empty NamespaceSelector implies that the Selector is limited + to selecting only workload endpoints in the same namespace + as the NetworkPolicy. \n For NetworkPolicy, `global()` + NamespaceSelector implies that the Selector is limited + to selecting only GlobalNetworkSet or HostEndpoint. \n + For GlobalNetworkPolicy, an empty NamespaceSelector implies + the Selector applies to workload endpoints across all + namespaces." type: string nets: description: Nets is an optional field that restricts the @@ -2823,6 +2946,26 @@ spec: AND'ed. type: string type: object + services: + description: "Services is an optional field that contains + options for matching Kubernetes Services. If specified, + only traffic that originates from or terminates at endpoints + within the selected service(s) will be matched, and only + to/from each endpoint's port. \n Services cannot be specified + on the same rule as Selector, NotSelector, NamespaceSelector, + Ports, NotPorts, Nets, NotNets or ServiceAccounts. \n + Only valid on egress rules." + properties: + name: + description: Name specifies the name of a Kubernetes + Service to match. + type: string + namespace: + description: Namespace specifies the namespace of the + given Service. If left empty, the rule will match + within this policy's namespace. + type: string + type: object type: object required: - action @@ -2852,16 +2995,17 @@ spec: contains a selector expression. Only traffic that originates from (or terminates at) endpoints within the selected namespaces will be matched. When both NamespaceSelector - and Selector are defined on the same rule, then only workload - endpoints that are matched by both selectors will be selected - by the rule. \n For NetworkPolicy, an empty NamespaceSelector - implies that the Selector is limited to selecting only - workload endpoints in the same namespace as the NetworkPolicy. - \n For NetworkPolicy, `global()` NamespaceSelector implies - that the Selector is limited to selecting only GlobalNetworkSet - or HostEndpoint. \n For GlobalNetworkPolicy, an empty - NamespaceSelector implies the Selector applies to workload - endpoints across all namespaces." + and another selector are defined on the same rule, then + only workload endpoints that are matched by both selectors + will be selected by the rule. \n For NetworkPolicy, an + empty NamespaceSelector implies that the Selector is limited + to selecting only workload endpoints in the same namespace + as the NetworkPolicy. \n For NetworkPolicy, `global()` + NamespaceSelector implies that the Selector is limited + to selecting only GlobalNetworkSet or HostEndpoint. \n + For GlobalNetworkPolicy, an empty NamespaceSelector implies + the Selector applies to workload endpoints across all + namespaces." type: string nets: description: Nets is an optional field that restricts the @@ -2949,6 +3093,26 @@ spec: AND'ed. type: string type: object + services: + description: "Services is an optional field that contains + options for matching Kubernetes Services. If specified, + only traffic that originates from or terminates at endpoints + within the selected service(s) will be matched, and only + to/from each endpoint's port. \n Services cannot be specified + on the same rule as Selector, NotSelector, NamespaceSelector, + Ports, NotPorts, Nets, NotNets or ServiceAccounts. \n + Only valid on egress rules." + properties: + name: + description: Name specifies the name of a Kubernetes + Service to match. + type: string + namespace: + description: Namespace specifies the namespace of the + given Service. If left empty, the rule will match + within this policy's namespace. + type: string + type: object type: object http: description: HTTP contains match criteria that apply to HTTP @@ -3057,16 +3221,17 @@ spec: contains a selector expression. Only traffic that originates from (or terminates at) endpoints within the selected namespaces will be matched. When both NamespaceSelector - and Selector are defined on the same rule, then only workload - endpoints that are matched by both selectors will be selected - by the rule. \n For NetworkPolicy, an empty NamespaceSelector - implies that the Selector is limited to selecting only - workload endpoints in the same namespace as the NetworkPolicy. - \n For NetworkPolicy, `global()` NamespaceSelector implies - that the Selector is limited to selecting only GlobalNetworkSet - or HostEndpoint. \n For GlobalNetworkPolicy, an empty - NamespaceSelector implies the Selector applies to workload - endpoints across all namespaces." + and another selector are defined on the same rule, then + only workload endpoints that are matched by both selectors + will be selected by the rule. \n For NetworkPolicy, an + empty NamespaceSelector implies that the Selector is limited + to selecting only workload endpoints in the same namespace + as the NetworkPolicy. \n For NetworkPolicy, `global()` + NamespaceSelector implies that the Selector is limited + to selecting only GlobalNetworkSet or HostEndpoint. \n + For GlobalNetworkPolicy, an empty NamespaceSelector implies + the Selector applies to workload endpoints across all + namespaces." type: string nets: description: Nets is an optional field that restricts the @@ -3154,6 +3319,26 @@ spec: AND'ed. type: string type: object + services: + description: "Services is an optional field that contains + options for matching Kubernetes Services. If specified, + only traffic that originates from or terminates at endpoints + within the selected service(s) will be matched, and only + to/from each endpoint's port. \n Services cannot be specified + on the same rule as Selector, NotSelector, NamespaceSelector, + Ports, NotPorts, Nets, NotNets or ServiceAccounts. \n + Only valid on egress rules." + properties: + name: + description: Name specifies the name of a Kubernetes + Service to match. + type: string + namespace: + description: Namespace specifies the namespace of the + given Service. If left empty, the rule will match + within this policy's namespace. + type: string + type: object type: object required: - action @@ -3287,12 +3472,14 @@ rules: - watch - list - get - # Pods are queried to check for existence. + # Pods are watched to check for existence as part of IPAM controller. - apiGroups: [""] resources: - pods verbs: - get + - list + - watch # IPAM resources are manipulated when nodes are deleted. - apiGroups: ["crd.projectcalico.org"] resources: @@ -3374,6 +3561,14 @@ rules: - namespaces verbs: - get + # EndpointSlices are used for Service-based network policy rule + # enforcement. + - apiGroups: ["discovery.k8s.io"] + resources: + - endpointslices + verbs: + - watch + - list - apiGroups: [""] resources: - endpoints @@ -3567,7 +3762,7 @@ spec: # install of CNI and upgrade of ipam are handled in the dedicated # salt states #- name: upgrade-ipam - # image: calico/cni:v3.16.1 + # image: docker.io/calico/cni:v3.20.0 # command: ["/opt/cni/bin/calico-ipam", "-upgrade"] # envFrom: # - configMapRef: @@ -3594,7 +3789,7 @@ spec: # This container installs the CNI binaries # and CNI network config file on each node. #- name: install-cni - # image: calico/cni:v3.16.1 + # image: docker.io/calico/cni:v3.20.0 # command: ["/opt/cni/bin/install"] # envFrom: # - configMapRef: @@ -3636,7 +3831,7 @@ spec: # to communicate with Felix over the Policy Sync API. # Note: In MetalK8s, we have no support for Dikastes (yet). #- name: flexvol-driver - # image: calico/pod2daemon-flexvol:v3.16.1 + # image: docker.io/calico/pod2daemon-flexvol:v3.20.0 # volumeMounts: # - name: flexvol-driver-host # mountPath: /host/driver @@ -3711,6 +3906,9 @@ spec: # no effect. This should fall within `--cluster-cidr`. - name: CALICO_IPV4POOL_CIDR value: "{{ networks.pod }}" + # Note: We disable Calico CNI management as it's managed by MetalK8s with Salt + - name: CALICO_MANAGE_CNI + value: "false" # Disable file logging so `kubectl logs` works. - name: CALICO_DISABLE_FILE_LOGGING value: "true" @@ -3743,6 +3941,7 @@ spec: periodSeconds: 10 initialDelaySeconds: 10 failureThreshold: 6 + timeoutSeconds: 10 readinessProbe: exec: command: @@ -3750,7 +3949,13 @@ spec: - -felix-ready - -bird-ready periodSeconds: 10 + timeoutSeconds: 10 volumeMounts: + # For maintaining CNI plugin API credentials. + # Note: Not used in MetalK8s + #- mountPath: /host/etc/cni/net.d + # name: cni-net-dir + # readOnly: false - mountPath: /lib/modules name: lib-modules readOnly: true @@ -3796,9 +4001,10 @@ spec: path: /sys/fs/ type: DirectoryOrCreate # Used to install CNI. - - name: cni-bin-dir - hostPath: - path: /opt/cni/bin + # Note: Not used in MetalK8s + #- name: cni-bin-dir + # hostPath: + # path: /opt/cni/bin # Note: Not used in MetalK8s #- name: cni-net-dir # hostPath: @@ -3893,6 +4099,7 @@ spec: periodSeconds: 10 initialDelaySeconds: 10 failureThreshold: 6 + timeoutSeconds: 10 readinessProbe: exec: command: diff --git a/salt/metalk8s/kubernetes/coredns/deployed.sls b/salt/metalk8s/kubernetes/coredns/deployed.sls index 974921f241..a42586ae66 100644 --- a/salt/metalk8s/kubernetes/coredns/deployed.sls +++ b/salt/metalk8s/kubernetes/coredns/deployed.sls @@ -25,7 +25,9 @@ Create coredns ConfigMap: ttl 30 } prometheus :9153 - forward . /etc/resolv.conf + forward . /etc/resolv.conf { + max_concurrent 1000 + } cache 30 loop reload @@ -65,12 +67,15 @@ Create coredns service: - name: dns port: 53 protocol: UDP + targetPort: 53 - name: dns-tcp port: 53 protocol: TCP + targetPort: 53 - name: metrics port: 9153 protocol: TCP + targetPort: 9153 - require: - metalk8s_kubernetes: Create coredns deployment @@ -107,6 +112,13 @@ Create coredns cluster role: - nodes verbs: - get + - apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch Create coredns cluster role binding: metalk8s_kubernetes.object_present: diff --git a/salt/metalk8s/kubernetes/etcd/installed.sls b/salt/metalk8s/kubernetes/etcd/installed.sls index bc3ac96733..b287a652cd 100644 --- a/salt/metalk8s/kubernetes/etcd/installed.sls +++ b/salt/metalk8s/kubernetes/etcd/installed.sls @@ -112,7 +112,7 @@ Waiting for etcd running: - {{ certificates.server.files.etcd.path }} - /etc/kubernetes/pki/etcd/server.key - status: 200 - - match: '{"health":"true"}' + - match: '{"health":"true","reason":""}' - require: - module: Delay after etcd pod deployment diff --git a/salt/metalk8s/kubernetes/scheduler/installed.sls b/salt/metalk8s/kubernetes/scheduler/installed.sls index 4822ce17c6..543f4e871a 100644 --- a/salt/metalk8s/kubernetes/scheduler/installed.sls +++ b/salt/metalk8s/kubernetes/scheduler/installed.sls @@ -26,7 +26,6 @@ Create kube-scheduler Pod manifest: - --leader-elect=true - --port=0 # } - - --bind-address={{ grains['metalk8s']['control_plane_ip'] }} - --v={{ 2 if metalk8s.debug else 0 }} requested_cpu: 100m ports: diff --git a/salt/tests/requirements.in b/salt/tests/requirements.in index dfbf7e0994..04e0ee9154 100644 --- a/salt/tests/requirements.in +++ b/salt/tests/requirements.in @@ -1,6 +1,6 @@ pytest pytest-cov -salt == 3002.6 +salt == 3002.7 mock == 3.0.5 parameterized == 0.7.4 etcd3 != 0.11.0 diff --git a/salt/tests/requirements.txt b/salt/tests/requirements.txt index 6efc0abb16..4d5eb04cc0 100644 --- a/salt/tests/requirements.txt +++ b/salt/tests/requirements.txt @@ -81,9 +81,9 @@ distro==1.6.0 \ etcd3==0.12.0 \ --hash=sha256:89a704cb389bf0a010a1fa050ce19342d23bf6371ebda1c21cfe8ff3ed488726 \ # via -r salt/tests/requirements.in -google-auth==2.0.1 \ - --hash=sha256:c012c8be7c442c8309ca8fa0876fef33f5fd977c467be1e1c1c2f721e8ebd73c \ - --hash=sha256:ea1af050b3e06eb73e4470f704d23007307bc0e87c13e015f6b90460f1407bd3 \ +google-auth==2.0.2 \ + --hash=sha256:104475dc4d57bbae49017aea16fffbb763204fa2d6a70f1f3cc79962c1a383a4 \ + --hash=sha256:cde472372e030e1e0bc64dac00fb53e6c095d7ab641f4281e2c995e85e205d8b \ # via kubernetes grpcio==1.39.0 \ --hash=sha256:02e8a8b41db8e13df53078355b439363e4ac46d0ac9a8a461a39e42829e2bcf8 \ @@ -142,9 +142,9 @@ idna==3.2 \ --hash=sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a \ --hash=sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3 \ # via requests -importlib-metadata==4.6.4 \ - --hash=sha256:7b30a78db2922d78a6f47fb30683156a14f3c6aa5cc23f77cc8967e9ab2d002f \ - --hash=sha256:ed5157fef23a4bc4594615a0dd8eba94b2bb36bf2a343fa3d8bb2fa0a62a99d5 \ +importlib-metadata==4.8.1 \ + --hash=sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15 \ + --hash=sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1 \ # via pluggy, pytest iniconfig==1.1.1 \ --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \ @@ -260,9 +260,9 @@ parameterized==0.7.4 \ --hash=sha256:190f8cc7230eee0b56b30d7f074fd4d165f7c45e6077582d0813c8557e738490 \ --hash=sha256:59ab908e31c01505a987a2be78854e19cb1630c047bbab7848169c371d614d56 \ # via -r salt/tests/requirements.in -pluggy==0.13.1 \ - --hash=sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0 \ - --hash=sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d \ +pluggy==1.0.0 \ + --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \ + --hash=sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3 \ # via pytest protobuf==3.17.3 \ --hash=sha256:13ee7be3c2d9a5d2b42a1030976f760f28755fcf5863c55b1460fd205e6cd637 \ @@ -367,9 +367,9 @@ pycryptodomex==3.10.1 \ --hash=sha256:f933ecf4cb736c7af60a6a533db2bf569717f2318b265f92907acff1db43bc34 \ --hash=sha256:fc9c55dc1ed57db76595f2d19a479fc1c3a1be2c9da8de798a93d286c5f65f38 \ # via salt -pyfakefs==4.5.0 \ - --hash=sha256:46234fa2dbce8ffb693ec906638449342afe83e45fa832932351d5050048159b \ - --hash=sha256:58b017b3437bbe97803a23755876c6d6aeb5aea37e52cec15e5d86b59c4c7295 \ +pyfakefs==4.5.1 \ + --hash=sha256:002a065dcbf59c2caa039e4fc4ba01d1d636aa63ee9c794d4c9fc01f0e2d6dc0 \ + --hash=sha256:03a0e8e34e7250a458a640ca72c4c2c569bafc7e51a4c2d6c4ac62a426f60301 \ # via -r salt/tests/requirements.in pyparsing==2.4.7 \ --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \ @@ -379,9 +379,9 @@ pytest-cov==2.12.1 \ --hash=sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a \ --hash=sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7 \ # via -r salt/tests/requirements.in -pytest==6.2.4 \ - --hash=sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b \ - --hash=sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890 \ +pytest==6.2.5 \ + --hash=sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89 \ + --hash=sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134 \ # via -r salt/tests/requirements.in, pytest-cov python-dateutil==2.8.2 \ --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ @@ -469,8 +469,8 @@ rsa==4.7.2 \ --hash=sha256:78f9a9bf4e7be0c5ded4583326e7461e3a3c5aae24073648b4bdfa797d78c9d2 \ --hash=sha256:9d689e6ca1b3038bc82bf8d23e944b6b6037bc02301a574935b2dd946e0353b9 \ # via google-auth -salt==3002.6 \ - --hash=sha256:ffc478569363e1d17b6a3a0c421eaae9c079bbeabc4c7725a222d0fbf903a0a5 \ +salt==3002.7 \ + --hash=sha256:575a69fc6c798c1635fdd5b070c5fff83cdaadf8a68e3732d31368b4957e6adf \ # via -r salt/tests/requirements.in six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ @@ -484,10 +484,10 @@ toml==0.10.2 \ --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f \ # via pytest, pytest-cov -typing-extensions==3.10.0.0 \ - --hash=sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497 \ - --hash=sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342 \ - --hash=sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84 \ +typing-extensions==3.10.0.2 \ + --hash=sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e \ + --hash=sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7 \ + --hash=sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34 \ # via importlib-metadata urllib3==1.26.6 \ --hash=sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4 \ @@ -503,7 +503,7 @@ zipp==3.5.0 \ # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -setuptools==57.4.0 \ - --hash=sha256:6bac238ffdf24e8806c61440e755192470352850f3419a52f26ffe0a1a64f465 \ - --hash=sha256:a49230977aa6cfb9d933614d2f7b79036e9945c4cdd7583163f4e920b83418d6 \ +setuptools==58.0.0 \ + --hash=sha256:ee45f4d69c4bbd21a27b60a7f1f32cbdcd34755cb613e5c4edcb58145394a0a3 \ + --hash=sha256:fa14855ea8332dc12cae21b8e18cf55573eeaabad9fe3b8b787e6a74e01885d3 \ # via google-auth, kubernetes diff --git a/shell-ui/jest.config.js b/shell-ui/jest.config.js index 712c708e56..780d7fbf2f 100644 --- a/shell-ui/jest.config.js +++ b/shell-ui/jest.config.js @@ -1,5 +1,5 @@ module.exports = { - transformIgnorePatterns: ['/node_modules/(?!react-to-webcomponent)'], + transformIgnorePatterns: ['/node_modules/(?!vega-lite|react-to-webcomponent)'], setupFilesAfterEnv: ['./src/setupTests.js'], clearMocks: true, moduleNameMapper: { diff --git a/shell-ui/package-lock.json b/shell-ui/package-lock.json index 6e01c3db1a..97d1e58a8d 100644 --- a/shell-ui/package-lock.json +++ b/shell-ui/package-lock.json @@ -1958,8 +1958,8 @@ "dev": true }, "@scality/core-ui": { - "version": "github:scality/core-ui#d2c02f9cd9c24fe46a364f64d53b4984017c3199", - "from": "github:scality/core-ui#v0.19.3" + "version": "github:scality/core-ui#d477bb304427361d83726c634fb7aca3f02f89bf", + "from": "github:scality/core-ui#v0.21.1" }, "@scality/module-federation": { "version": "github:scality/module-federation#a4f1cf882646c8d859b9081dc8b66e5647962456", @@ -1971,9 +1971,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz", - "integrity": "sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==", + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.3.tgz", + "integrity": "sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -2253,9 +2253,9 @@ } }, "@types/clone": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/clone/-/clone-2.1.0.tgz", - "integrity": "sha512-d/aS/lPOnUSruPhgNtT8jW39fHRVTLQy9sodysP1kkG8EdAtdZu1vt8NJaYA8w/6Z9j8izkAsx1A/yJhcYR1CA==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/clone/-/clone-2.1.1.tgz", + "integrity": "sha512-BZIU34bSYye0j/BFcPraiDZ5ka6MJADjcDVELGf7glr9K+iE8NYVjFslJFVWzskSxkLLyCrSPScE82/UUoBSvg==" }, "@types/cookie": { "version": "0.4.0", @@ -2289,11 +2289,6 @@ "integrity": "sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg==", "dev": true }, - "@types/fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha512-mky/O83TXmGY39P1H9YbUpjV6l6voRYlufqfFCvel8l1phuy8HRjdWc1rrPuN53ITBJlbyMSV6z3niOySO5pgQ==" - }, "@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -2763,6 +2758,12 @@ "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", "dev": true }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -2883,6 +2884,48 @@ } } }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3578,6 +3621,17 @@ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, + "canvas": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.6.1.tgz", + "integrity": "sha512-S98rKsPcuhfTcYbtF53UIJhcbgIAK533d1kJKMwsMwAIFgfd58MOyxRud3kktlzWiEkFliaJtvyZCBtud/XVEA==", + "dev": true, + "requires": { + "nan": "^2.14.0", + "node-pre-gyp": "^0.11.0", + "simple-get": "^3.0.3" + } + }, "capture-exit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", @@ -3653,6 +3707,12 @@ "upath": "^1.1.1" } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, "chrome-trace-event": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", @@ -3822,6 +3882,12 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, "collapse-white-space": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", @@ -3948,6 +4014,12 @@ "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", "dev": true }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -4386,6 +4458,15 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "dev": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, "deep-equal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", @@ -4400,6 +4481,12 @@ "regexp.prototype.flags": "^1.2.0" } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -4551,6 +4638,12 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -4572,6 +4665,12 @@ "repeat-string": "^1.5.4" } }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -5448,6 +5547,15 @@ "universalify": "^2.0.0" } }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dev": true, + "requires": { + "minipass": "^2.6.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -5469,6 +5577,44 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -5720,6 +5866,12 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -6113,6 +6265,15 @@ "harmony-reflect": "^1.4.6" } }, + "ignore-walk": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", + "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -6165,6 +6326,12 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, "inline-style-parser": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", @@ -8738,6 +8905,12 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "dev": true + }, "min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -8773,6 +8946,33 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "dev": true, + "requires": { + "minipass": "^2.9.0" + } + }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -9100,8 +9300,7 @@ "version": "2.14.2", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", - "dev": true, - "optional": true + "dev": true }, "nano-time": { "version": "1.0.0", @@ -9169,6 +9368,28 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "needle": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "dev": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -9270,6 +9491,24 @@ } } }, + "node-pre-gyp": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", + "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", + "dev": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, "node-request-interceptor": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/node-request-interceptor/-/node-request-interceptor-0.6.3.tgz", @@ -9282,6 +9521,16 @@ "strict-event-emitter": "^0.1.0" } }, + "nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "dev": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -9300,6 +9549,32 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "npm-packlist": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", + "dev": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -9309,6 +9584,18 @@ "path-key": "^3.0.0" } }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, "nth-check": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", @@ -9318,6 +9605,12 @@ "boolbase": "^1.0.0" } }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, "nwsapi": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", @@ -9567,6 +9860,28 @@ "url-parse": "^1.4.3" } }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, "p-each-series": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", @@ -10091,6 +10406,18 @@ } } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, "react": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", @@ -10932,6 +11259,12 @@ } } }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, "saxes": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", @@ -11179,6 +11512,23 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true + }, + "simple-get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", + "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", + "dev": true, + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -11736,6 +12086,12 @@ "min-indent": "^1.0.0" } }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, "style-loader": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", @@ -11832,6 +12188,35 @@ "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true }, + "tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "dev": true, + "requires": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, "terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -12422,9 +12807,9 @@ } }, "url-parse": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", - "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz", + "integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==", "dev": true, "requires": { "querystringify": "^2.1.1", @@ -12596,6 +12981,14 @@ "requires": { "lru-cache": "^6.0.0" } + }, + "vega-tooltip": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/vega-tooltip/-/vega-tooltip-0.25.1.tgz", + "integrity": "sha512-ugGwGi2/p3OpB8N15xieuzP8DyV5DreqMWcmJ9zpWT8GlkyKtef4dGRXnvHeHQ+iJFmWrq4oZJ+kLTrdiECjAg==", + "requires": { + "vega-util": "^1.16.0" + } } } }, @@ -12701,22 +13094,21 @@ } }, "vega-lite": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/vega-lite/-/vega-lite-4.17.0.tgz", - "integrity": "sha512-MO2XsaVZqx6iWWmVA5vwYFamvhRUsKfVp7n0pNlkZ2/21cuxelSl92EePZ2YGmzL6z4/3K7r/45zaG8p+qNHeg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/vega-lite/-/vega-lite-5.1.1.tgz", + "integrity": "sha512-V085gNkbgbmcVC/Q3dJjmIioxcDicxMHvH0FIKOPxdplzt+qU9xGIhQy7scj0tSMYnmAPCayB5oLkkQXFb6w1w==", "requires": { - "@types/clone": "~2.1.0", - "@types/fast-json-stable-stringify": "^2.0.0", + "@types/clone": "~2.1.1", "array-flat-polyfill": "^1.0.1", "clone": "~2.1.2", "fast-deep-equal": "~3.1.3", "fast-json-stable-stringify": "~2.1.0", - "json-stringify-pretty-compact": "~2.0.0", - "tslib": "~2.0.3", + "json-stringify-pretty-compact": "~3.0.0", + "tslib": "~2.3.1", "vega-event-selector": "~2.0.6", - "vega-expression": "~3.0.0", - "vega-util": "~1.16.0", - "yargs": "~16.0.3" + "vega-expression": "~4.0.1", + "vega-util": "~1.16.1", + "yargs": "~17.1.1" }, "dependencies": { "ansi-regex": { @@ -12765,10 +13157,15 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, + "json-stringify-pretty-compact": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz", + "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA==" + }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -12784,17 +13181,14 @@ } }, "tslib": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", - "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, - "vega-expression": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-3.0.1.tgz", - "integrity": "sha512-+UwOFEkBnAWo8Zud6i8O4Pd2W6QqmPUOaAhjNtj0OxRL+d+Duoy7M4edUDZ+YuoUcMnjjBFfDQu7oRAA1fIMEQ==", - "requires": { - "vega-util": "^1.15.2" - } + "vega-util": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/vega-util/-/vega-util-1.16.1.tgz", + "integrity": "sha512-FdgD72fmZMPJE99FxvFXth0IL4BbLA93WmBg/lvcJmfkK4Uf90WIlvGwaIUdSePIsdpkZjBPyQcHMQ8OcS8Smg==" }, "wrap-ansi": { "version": "7.0.0", @@ -12807,28 +13201,28 @@ } }, "y18n": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", - "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==" + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, "yargs": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.0.3.tgz", - "integrity": "sha512-6+nLw8xa9uK1BOEOykaiYAJVh6/CjxWXK/q9b5FpRgNslt8s22F2xMBqVIKgCRjNgGvGPBy8Vog7WN7yh4amtA==", + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.1.1.tgz", + "integrity": "sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==", "requires": { - "cliui": "^7.0.0", - "escalade": "^3.0.2", + "cliui": "^7.0.2", + "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", - "y18n": "^5.0.1", - "yargs-parser": "^20.0.0" + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" } } }, @@ -12948,11 +13342,11 @@ } }, "vega-tooltip": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/vega-tooltip/-/vega-tooltip-0.25.0.tgz", - "integrity": "sha512-S48d/eP6WfieLmUvFEjd+raHWKKeK/RfTlwLa3zGcBULDHJY2NU2vRfjC1x33G6Y7eKeAfqGpM6ER5Qt1nf8tA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/vega-tooltip/-/vega-tooltip-0.27.0.tgz", + "integrity": "sha512-FRcHNfMNo9D/7an5nZuP6JC2JGEsc85qcGjyMU7VlPpjQj9eBj1P+sZSNbb54Z20g7inVSBRyd8qgNn5EYTxJA==", "requires": { - "vega-util": "^1.15.2" + "vega-util": "^1.16.0" } }, "vega-transforms": { @@ -13522,6 +13916,42 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, "wildcard": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", diff --git a/shell-ui/package.json b/shell-ui/package.json index 0534c98ee4..813b3db294 100644 --- a/shell-ui/package.json +++ b/shell-ui/package.json @@ -24,6 +24,7 @@ "babel-jest": "^26.6.3", "babel-loader": "^8.2.2", "babel-plugin-styled-components": "^1.12.0", + "canvas": "2.6.1", "css-loader": "^5.2.6", "file-loader": "^6.2.0", "flow-bin": "^0.143.1", @@ -46,7 +47,7 @@ "webpack-dev-server": "^3.11.2" }, "dependencies": { - "@scality/core-ui": "github:scality/core-ui.git#v0.19.3", + "@scality/core-ui": "github:scality/core-ui.git#v0.21.1", "@scality/module-federation": "github:scality/module-federation.git#1.0.0", "oidc-client": "^1.11.3", "oidc-react": "^1.1.5", @@ -65,7 +66,8 @@ "react-virtualized": "^9.22.3", "styled-components": "^5.2.1", "vega": "^5.17.3", - "vega-embed": "^6.15.0", - "vega-lite": "^4.17.0" + "vega-embed": "^6.0.0", + "vega-lite": "^5.0.0", + "vega-tooltip": "^0.27.0" } } diff --git a/shell-ui/src/auth/AuthProvider.js b/shell-ui/src/auth/AuthProvider.js index 6928b13faa..6207ed1988 100644 --- a/shell-ui/src/auth/AuthProvider.js +++ b/shell-ui/src/auth/AuthProvider.js @@ -9,7 +9,7 @@ import { } from 'oidc-react'; import { useShellConfig } from '../initFederation/ShellConfigProvider'; import { getUserGroups } from '../navbar/auth/permissionUtils'; -import { WebStorageStateStore } from 'oidc-client'; +import { MetadataService, WebStorageStateStore } from 'oidc-client'; export function AuthProvider({ children }: { children: Node }) { const { authConfig } = useAuthConfig(); @@ -25,6 +25,24 @@ export function AuthProvider({ children }: { children: Node }) { return {children}; } +function defaultDexConnectorMetadataService(connectorId: string) { + class DexDefaultConnectorMetadataService extends MetadataService { + getAuthorizationEndpoint() { + return this._getMetadataProperty('authorization_endpoint').then( + (authorizationEndpoint) => { + const queryParamas = new URLSearchParams(window.location.search); + if (!queryParamas.has('displayLoginChoice')) { + return authorizationEndpoint + '?connector_id=' + connectorId; + } + return authorizationEndpoint; + }, + ); + } + } + + return DexDefaultConnectorMetadataService; +} + function OAuth2AuthProvider({ children }: { children: Node }) { const { authConfig } = useAuthConfig(); @@ -39,6 +57,9 @@ function OAuth2AuthProvider({ children }: { children: Node }) { loadUserInfo: true, automaticSilentRenew: true, monitorSession: false, + MetadataServiceCtor: authConfig.defaultDexConnector + ? defaultDexConnectorMetadataService(authConfig.defaultDexConnector) + : MetadataService, userStore: new WebStorageStateStore({ store: localStorage }), }); @@ -95,28 +116,45 @@ export function useLogOut() { let auth; try { auth = useOauth2Auth(); - } catch(e) { + } catch (e) { //If an exception is raised here it is likely because the app is not using OIDC auth kind, so we can ignore this - console.log('Failed to retrieve auth informations for OIDC auth kind', e) + console.log('Failed to retrieve auth informations for OIDC auth kind', e); } - return {logOut: useCallback(() => { - if (!authConfig) { - return; - } - - if (authConfig.kind === 'OAuth2Proxy') { - throw new Error('OAuth2Proxy authentication kind is not yet supported'); - } - if (auth && auth.userManager) { - auth.userManager.revokeAccessToken(); - if (authConfig.providerLogout) { - auth.userManager.signoutRedirect(); - } else { - auth.userManager.removeUser(); - location.reload(); + return { + logOut: useCallback(() => { + if (!authConfig) { + return; } - } - }, [JSON.stringify(authConfig), auth])} - + + if (authConfig.kind === 'OAuth2Proxy') { + throw new Error('OAuth2Proxy authentication kind is not yet supported'); + } + if (auth && auth.userManager) { + auth.userManager.revokeAccessToken(); + if (authConfig.providerLogout) { + auth.userManager.signoutRedirect().catch((e) => { + if (e.message === 'no end session endpoint') { + console.log( + "OIDC provider doesn't support end session endpoint, fallback to clearing document cookies", + ); + document.cookie.split(';').forEach(function (c) { + document.cookie = + c.trim().split('=')[0] + + '=;' + + 'expires=Thu, 01 Jan 1970 00:00:00 UTC;'; + }); + } else { + console.error(e); + } + auth.userManager.removeUser(); + location.reload(); + }); + } else { + auth.userManager.removeUser(); + location.reload(); + } + } + }, [JSON.stringify(authConfig), auth]), + }; } diff --git a/shell-ui/src/initFederation/ConfigurationProviders.js b/shell-ui/src/initFederation/ConfigurationProviders.js index ddee95775a..8012482220 100644 --- a/shell-ui/src/initFederation/ConfigurationProviders.js +++ b/shell-ui/src/initFederation/ConfigurationProviders.js @@ -28,7 +28,8 @@ export type OIDCConfig = { clientId: string, responseType: string, scopes: string, - providerLogout?: boolean + providerLogout?: boolean, + defaultDexConnector?: string, }; type RuntimeWebFinger = { diff --git a/tests/conftest.py b/tests/conftest.py index 879064b958..e239713d08 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -365,9 +365,9 @@ def dex_login(username, password, should_fail, control_plane_ingress_ep): ) get_auth_start = time.time() try: - auth_page = session.post( - control_plane_ingress_ep + "/oidc/auth?", - data={ + auth_page = session.get( + control_plane_ingress_ep + "/oidc/auth/local?", + params={ "response_type": "id_token", "client_id": "metalk8s-ui", "scope": "openid audience:server:client_id:oidc-auth-client", @@ -390,11 +390,11 @@ def dex_login(username, password, should_fail, control_plane_ingress_ep): auth_form = auth_page.text # The form action looks like: - # - next_path_match = re.search(r'href=[\'"](?P/oidc/\S+)[\'"] ', auth_form) + #
+ next_path_match = re.search(r'action=[\'"](?P/oidc/\S+)[\'"]', auth_form) assert ( next_path_match is not None - ), "Could not find an anchor with `href='/oidc/...'` in Dex response:\n{}".format( + ), "Could not find an anchor with `action='/oidc/...'` in Dex response:\n{}".format( auth_form ) next_path = next_path_match.group("next_path") diff --git a/tests/post/features/solutions.feature b/tests/post/features/solutions.feature index 68b7413905..af4acf5f0c 100644 --- a/tests/post/features/solutions.feature +++ b/tests/post/features/solutions.feature @@ -7,15 +7,15 @@ Feature: Solutions And the Solution Configuration file is absent When we import a Solution archive '/var/tmp/example-solution.iso' Then Solution archive 'example-solution' is imported correctly - And Solution 'example-solution' version '1.0.0' is available - When we activate Solution 'example-solution' version '1.0.0' - Then Solution 'example-solution' version '1.0.0' is activated + And Solution 'example-solution' version '1.0.2' is available + When we activate Solution 'example-solution' version '1.0.2' + Then Solution 'example-solution' version '1.0.2' is activated And CRD 'versionservers.example-solution.metalk8s.scality.com' exists in Kubernetes API And CRD 'clockservers.example-solution.metalk8s.scality.com' exists in Kubernetes API When we create a solution environment 'example-environment' Then solution environment 'example-environment' is available When we remove Taints on node 'bootstrap' before deployment - And we deploy Solution 'example-solution' in environment 'example-environment' with version '1.0.0' + And we deploy Solution 'example-solution' in environment 'example-environment' with version '1.0.2' Then we have 1 running pod labeled 'app=example-solution-operator' in namespace 'example-environment' When we deactivate Solution 'example-solution' And we delete Solution 'example-solution' in environment 'example-environment' diff --git a/tools/crd-client-generator-js/package-lock.json b/tools/crd-client-generator-js/package-lock.json index 8e4b7313be..edba93d2b9 100644 --- a/tools/crd-client-generator-js/package-lock.json +++ b/tools/crd-client-generator-js/package-lock.json @@ -5926,9 +5926,9 @@ "dev": true }, "tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, "requires": { "chownr": "^2.0.0", diff --git a/ui/package-lock.json b/ui/package-lock.json index ec62b4ae8e..0a157d4f07 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -3004,8 +3004,8 @@ } }, "@scality/core-ui": { - "version": "github:scality/core-ui#d2c02f9cd9c24fe46a364f64d53b4984017c3199", - "from": "github:scality/core-ui#v0.19.3" + "version": "github:scality/core-ui#d477bb304427361d83726c634fb7aca3f02f89bf", + "from": "github:scality/core-ui#v0.21.1" }, "@scality/module-federation": { "version": "github:scality/module-federation#a4f1cf882646c8d859b9081dc8b66e5647962456", @@ -3017,9 +3017,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz", - "integrity": "sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==", + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.3.tgz", + "integrity": "sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -3042,9 +3042,9 @@ } }, "styled-components": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.0.tgz", - "integrity": "sha512-bPJKwZCHjJPf/hwTJl6TbkSZg/3evha+XPEizrZUGb535jLImwDUdjTNxXqjjaASt2M4qO4AVfoHJNe3XB/tpQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.1.tgz", + "integrity": "sha512-JThv2JRzyH0NOIURrk9iskdxMSAAtCfj/b2Sf1WJaCUsloQkblepy1jaCLX/bYE+mhYo3unmwVSI9I5d9ncSiQ==", "requires": { "@babel/helper-module-imports": "^7.0.0", "@babel/traverse": "^7.4.5", @@ -3295,9 +3295,9 @@ "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" }, "@types/clone": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/clone/-/clone-2.1.0.tgz", - "integrity": "sha512-d/aS/lPOnUSruPhgNtT8jW39fHRVTLQy9sodysP1kkG8EdAtdZu1vt8NJaYA8w/6Z9j8izkAsx1A/yJhcYR1CA==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/clone/-/clone-2.1.1.tgz", + "integrity": "sha512-BZIU34bSYye0j/BFcPraiDZ5ka6MJADjcDVELGf7glr9K+iE8NYVjFslJFVWzskSxkLLyCrSPScE82/UUoBSvg==" }, "@types/cookie": { "version": "0.4.0", @@ -3331,11 +3331,6 @@ "integrity": "sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==", "dev": true }, - "@types/fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha512-mky/O83TXmGY39P1H9YbUpjV6l6voRYlufqfFCvel8l1phuy8HRjdWc1rrPuN53ITBJlbyMSV6z3niOySO5pgQ==" - }, "@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -3553,9 +3548,9 @@ } }, "@types/request": { - "version": "2.48.5", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz", - "integrity": "sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ==", + "version": "2.48.7", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.7.tgz", + "integrity": "sha512-GWP9AZW7foLd4YQxyFZDBepl0lPsWLMEXDZUjQ/c1gqVPDPECrRZyEzuhJdnPWioFCq3Tv0qoGpMD6U+ygd4ZA==", "requires": { "@types/caseless": "*", "@types/node": "*", @@ -4272,9 +4267,9 @@ "dev": true }, "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", "dev": true, "requires": { "delegates": "^1.0.0", @@ -5825,13 +5820,13 @@ "dev": true }, "canvas": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.7.0.tgz", - "integrity": "sha512-pzCxtkHb+5su5MQjTtepMDlIOtaXo277x0C0u3nMOxtkhTyQ+h2yNKhlROAaDllWgRyePAUitC08sXw26Eb6aw==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.6.1.tgz", + "integrity": "sha512-S98rKsPcuhfTcYbtF53UIJhcbgIAK533d1kJKMwsMwAIFgfd58MOyxRud3kktlzWiEkFliaJtvyZCBtud/XVEA==", "dev": true, "requires": { "nan": "^2.14.0", - "node-pre-gyp": "^0.15.0", + "node-pre-gyp": "^0.11.0", "simple-get": "^3.0.3" } }, @@ -6436,9 +6431,9 @@ } }, "tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, "requires": { "chownr": "^2.0.0", @@ -10621,9 +10616,9 @@ "dev": true }, "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", + "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", "dev": true, "requires": { "minimatch": "^3.0.4" @@ -14676,9 +14671,9 @@ "dev": true }, "needle": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz", - "integrity": "sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", "dev": true, "requires": { "debug": "^3.2.6", @@ -14825,21 +14820,21 @@ } }, "node-pre-gyp": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz", - "integrity": "sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", + "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", "dev": true, "requires": { "detect-libc": "^1.0.2", - "mkdirp": "^0.5.3", - "needle": "^2.5.0", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", "nopt": "^4.0.1", "npm-packlist": "^1.1.6", "npmlog": "^4.0.2", "rc": "^1.2.7", "rimraf": "^2.6.1", "semver": "^5.3.0", - "tar": "^4.4.2" + "tar": "^4" }, "dependencies": { "rimraf": { @@ -14903,9 +14898,9 @@ } }, "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", "dev": true, "requires": { "npm-normalize-package-bin": "^1.0.1" @@ -18088,7 +18083,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, "requires": { "ansi-regex": "^5.0.0" } @@ -18278,18 +18272,18 @@ "dev": true }, "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "dev": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "dev": true, + "requires": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" }, "dependencies": { "fs-minipass": { @@ -19157,6 +19151,14 @@ "requires": { "lru-cache": "^6.0.0" } + }, + "vega-tooltip": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/vega-tooltip/-/vega-tooltip-0.25.1.tgz", + "integrity": "sha512-ugGwGi2/p3OpB8N15xieuzP8DyV5DreqMWcmJ9zpWT8GlkyKtef4dGRXnvHeHQ+iJFmWrq4oZJ+kLTrdiECjAg==", + "requires": { + "vega-util": "^1.16.0" + } } } }, @@ -19262,22 +19264,21 @@ } }, "vega-lite": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/vega-lite/-/vega-lite-4.17.0.tgz", - "integrity": "sha512-MO2XsaVZqx6iWWmVA5vwYFamvhRUsKfVp7n0pNlkZ2/21cuxelSl92EePZ2YGmzL6z4/3K7r/45zaG8p+qNHeg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/vega-lite/-/vega-lite-5.1.1.tgz", + "integrity": "sha512-V085gNkbgbmcVC/Q3dJjmIioxcDicxMHvH0FIKOPxdplzt+qU9xGIhQy7scj0tSMYnmAPCayB5oLkkQXFb6w1w==", "requires": { - "@types/clone": "~2.1.0", - "@types/fast-json-stable-stringify": "^2.0.0", + "@types/clone": "~2.1.1", "array-flat-polyfill": "^1.0.1", "clone": "~2.1.2", "fast-deep-equal": "~3.1.3", "fast-json-stable-stringify": "~2.1.0", - "json-stringify-pretty-compact": "~2.0.0", - "tslib": "~2.0.3", + "json-stringify-pretty-compact": "~3.0.0", + "tslib": "~2.3.1", "vega-event-selector": "~2.0.6", - "vega-expression": "~3.0.0", - "vega-util": "~1.16.0", - "yargs": "~16.0.3" + "vega-expression": "~4.0.1", + "vega-util": "~1.16.1", + "yargs": "~17.1.1" }, "dependencies": { "cliui": { @@ -19290,31 +19291,10 @@ "wrap-ansi": "^7.0.0" } }, - "json-stringify-pretty-compact": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz", - "integrity": "sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ==" - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - }, "tslib": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", - "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" - }, - "vega-expression": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-3.0.1.tgz", - "integrity": "sha512-+UwOFEkBnAWo8Zud6i8O4Pd2W6QqmPUOaAhjNtj0OxRL+d+Duoy7M4edUDZ+YuoUcMnjjBFfDQu7oRAA1fIMEQ==", - "requires": { - "vega-util": "^1.15.2" - } + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, "wrap-ansi": { "version": "7.0.0", @@ -19327,28 +19307,28 @@ } }, "y18n": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", - "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==" + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, "yargs": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.0.3.tgz", - "integrity": "sha512-6+nLw8xa9uK1BOEOykaiYAJVh6/CjxWXK/q9b5FpRgNslt8s22F2xMBqVIKgCRjNgGvGPBy8Vog7WN7yh4amtA==", + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.1.1.tgz", + "integrity": "sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==", "requires": { - "cliui": "^7.0.0", - "escalade": "^3.0.2", + "cliui": "^7.0.2", + "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", - "y18n": "^5.0.1", - "yargs-parser": "^20.0.0" + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" } }, "yargs-parser": { - "version": "20.2.7", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", - "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==" + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" } } }, @@ -19475,9 +19455,9 @@ } }, "vega-tooltip": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/vega-tooltip/-/vega-tooltip-0.25.1.tgz", - "integrity": "sha512-ugGwGi2/p3OpB8N15xieuzP8DyV5DreqMWcmJ9zpWT8GlkyKtef4dGRXnvHeHQ+iJFmWrq4oZJ+kLTrdiECjAg==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/vega-tooltip/-/vega-tooltip-0.27.0.tgz", + "integrity": "sha512-FRcHNfMNo9D/7an5nZuP6JC2JGEsc85qcGjyMU7VlPpjQj9eBj1P+sZSNbb54Z20g7inVSBRyd8qgNn5EYTxJA==", "requires": { "vega-util": "^1.16.0" } diff --git a/ui/package.json b/ui/package.json index 3524637fd0..f49f2d5087 100644 --- a/ui/package.json +++ b/ui/package.json @@ -10,7 +10,7 @@ "@fortawesome/react-fontawesome": "^0.1.14", "@kubernetes/client-node": "github:scality/kubernetes-client-javascript.git#browser-0.10.2-63-g579d066", "@scality/module-federation": "github:scality/module-federation.git#1.0.0", - "@scality/core-ui": "github:scality/core-ui.git#v0.19.3", + "@scality/core-ui": "github:scality/core-ui.git#v0.21.1", "axios": "^0.21.1", "formik": "2.2.5", "jest-environment-jsdom-sixteen": "^1.0.3", @@ -39,7 +39,8 @@ "uuid": "3.3.2", "vega": "^5.17.3", "vega-embed": "^6.0.0", - "vega-lite": "^4.17.0", + "vega-lite": "^5.0.0", + "vega-tooltip": "^0.27.0", "yup": "^0.32.9" }, "scripts": { @@ -91,7 +92,7 @@ "babel-jest": "^26.6.3", "babel-loader": "^8.2.2", "babel-polyfill": "^6.26.0", - "canvas": "^2.6.1", + "canvas": "2.6.1", "compression-webpack-plugin": "^6.0.0", "css-loader": "^5.1.3", "cypress": "^5.6.0", @@ -134,6 +135,7 @@ "stepDefinitions": "cypress/integration/e2e" }, "jest": { + "transformIgnorePatterns": ["/node_modules/(?!vega-lite)"], "testMatch": [ "/src/**/__tests__/**/*.[jt]s?(x)", "/src/**/?(*.)+(spec|test).[jt]s?(x)" diff --git a/ui/src/components/DashboardAlerts.js b/ui/src/components/DashboardAlerts.js index 88b1ed5b34..4ecf0ecbd9 100644 --- a/ui/src/components/DashboardAlerts.js +++ b/ui/src/components/DashboardAlerts.js @@ -8,7 +8,10 @@ import { useDiscoveredViews, } from '../containers/ConfigProvider'; import { useHistory } from 'react-router'; -import { EmphaseText, SecondaryText } from '@scality/core-ui'; +import { + EmphaseText, + SecondaryText, +} from '@scality/core-ui/dist/components/text/Text.component'; import { useAlerts } from '../containers/AlertProvider'; const AlertsContainer = styled.div` diff --git a/ui/src/components/DashboardInventory.js b/ui/src/components/DashboardInventory.js index 0e4d0bbc20..5ad8e3c1c0 100644 --- a/ui/src/components/DashboardInventory.js +++ b/ui/src/components/DashboardInventory.js @@ -3,7 +3,8 @@ import React from 'react'; import { useQuery } from 'react-query'; import styled, { css } from 'styled-components'; -import { Card, Loader } from '@scality/core-ui'; +import Card from '@scality/core-ui/dist/components/card/Card.component'; +import Loader from '@scality/core-ui/dist/components/loader/Loader.component'; import { spacing, fontSize, diff --git a/ui/src/components/MetricChart.js b/ui/src/components/MetricChart.js new file mode 100644 index 0000000000..e55e42ff49 --- /dev/null +++ b/ui/src/components/MetricChart.js @@ -0,0 +1,99 @@ +import React, { useRef, useEffect } from 'react'; +import { useQuery, type UseQueryOptions } from 'react-query'; +import { + LineTemporalChart, + useMetricsTimeSpan, +} from '@scality/core-ui/dist/next'; +import { useStartingTimeStamp } from '../containers/StartTimeProvider'; +import { getSingleResourceSerie } from '../services/graphUtils'; +import { HEIGHT_DEFAULT_CHART } from '../constants'; + +const MetricChart = ({ + title, + yAxisType, + nodeName, + instanceIP, + showAvg, + getMetricQuery, + getMetricAvgQuery, + unitRange, +}: { + title: string, + yAxisType: 'default' | 'percentage', + nodeName: string, + instanceIP: string, + showAvg: boolean, + getMetricQuery: UseQueryOptions, + getMetricAvgQuery: UseQueryOptions, + unitRange?: { threshold: number, label: string }[], +}) => { + const { startingTimeISO, currentTimeISO } = useStartingTimeStamp(); + const { frequency } = useMetricsTimeSpan(); + + const startTimeRef = useRef(startingTimeISO); + const chartStartTimeRef = useRef(startingTimeISO); //IMPORTANT: the ref of the previous start time + const seriesRef = useRef(); + startTimeRef.current = startingTimeISO; + + const metricQuery = useQuery( + getMetricQuery(instanceIP, { + startingTimeISO, + currentTimeISO, + frequency, + }), + ); + + const metricAvgQuery = useQuery( + getMetricAvgQuery( + { + startingTimeISO, + currentTimeISO, + frequency, + }, + showAvg, + ), + ); + + const nodeIPAddress = { internalIP: instanceIP, name: nodeName }; + + const isMetricDataLoading = metricQuery.isLoading; + const isMetricAvgDataLoading = metricAvgQuery.isLoading; + + useEffect(() => { + if (!isMetricDataLoading && !showAvg) { + // single node metrics + chartStartTimeRef.current = startTimeRef.current; + seriesRef.current = getSingleResourceSerie( + metricQuery.data, + nodeIPAddress.name, + ); + } else if (!isMetricAvgDataLoading && !isMetricDataLoading && showAvg) { + // show cluster average + chartStartTimeRef.current = startTimeRef.current; + seriesRef.current = getSingleResourceSerie( + metricQuery.data, + nodeIPAddress.name, + metricAvgQuery.data, + ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isMetricDataLoading, isMetricAvgDataLoading, showAvg]); + + return ( + + ); +}; + +export default MetricChart; diff --git a/ui/src/components/MetricSymmetricalChart.js b/ui/src/components/MetricSymmetricalChart.js new file mode 100644 index 0000000000..f647e97c12 --- /dev/null +++ b/ui/src/components/MetricSymmetricalChart.js @@ -0,0 +1,153 @@ +import React, { useRef, useEffect } from 'react'; +import { useQuery, type UseQueryOptions } from 'react-query'; +import { + LineTemporalChart, + useMetricsTimeSpan, +} from '@scality/core-ui/dist/next'; +import { useStartingTimeStamp } from '../containers/StartTimeProvider'; +import { getSeriesForSymmetricalChart } from '../services/graphUtils'; +import { HEIGHT_SYMMETRICAL_CHART } from '../constants'; +import type { NodesState } from '../ducks/app/nodes'; + +const MetricSymmetricalChart = ({ + title, + yAxisTitle, + nodeName, + instanceIP, + showAvg, + nodesIPsInfo, + getMetricAboveQuery, + getMetricBelowQuery, + getMetricAboveAvgQuery, + getMetricBelowAvgQuery, + metricPrefixAbove, + metricPrefixBelow, + unitRange, + planeInterface, +}: { + title: string, + yAxisTitle: string, + nodeName: string, + instanceIP: string, + showAvg: boolean, + nodesIPsInfo: $PropertyType, + getMetricAboveQuery: UseQueryOptions, + getMetricBelowQuery: UseQueryOptions, + getMetricAboveAvgQuery: UseQueryOptions, + getMetricBelowAvgQuery: UseQueryOptions, + metricPrefixAbove: string, + metricPrefixBelow: string, + unitRange?: { threshold: number, label: string }[], + planeInterface?: string, +}) => { + const { startingTimeISO, currentTimeISO } = useStartingTimeStamp(); + const { frequency } = useMetricsTimeSpan(); + + const startTimeRef = useRef(startingTimeISO); + const chartStartTimeRef = useRef(startingTimeISO); + const seriesRef = useRef(); + + startTimeRef.current = startingTimeISO; + + const metricAboveQuery = useQuery( + getMetricAboveQuery( + instanceIP, + { + startingTimeISO, + currentTimeISO, + frequency, + }, + planeInterface, + ), + ); + const metricBelowQuery = useQuery( + getMetricBelowQuery( + instanceIP, + { + startingTimeISO, + currentTimeISO, + frequency, + }, + planeInterface, + ), + ); + + const metricAboveAvgQuery = useQuery( + getMetricAboveAvgQuery( + { + startingTimeISO, + currentTimeISO, + frequency, + }, + showAvg, + instanceIP, + nodesIPsInfo, + ), + ); + + const metricBelowAvgQuery = useQuery( + getMetricBelowAvgQuery( + { + startingTimeISO, + currentTimeISO, + frequency, + }, + showAvg, + instanceIP, + nodesIPsInfo, + ), + ); + + const nodeIPAddress = { internalIP: instanceIP, name: nodeName }; + + const isMetricsDataLoading = + metricAboveQuery.isLoading || metricBelowQuery.isLoading; + const isMetricsAvgDataLoading = + metricAboveQuery.isLoading || + metricBelowQuery.isLoading || + metricAboveAvgQuery.isLoading || + metricBelowAvgQuery.isLoading; + + useEffect(() => { + if (!isMetricsDataLoading && !showAvg) { + // disable avg + chartStartTimeRef.current = startTimeRef.current; + seriesRef.current = getSeriesForSymmetricalChart( + metricAboveQuery.data, + metricBelowQuery.data, + nodeIPAddress.name, + metricPrefixAbove, + metricPrefixBelow, + ); + } else if (!isMetricsAvgDataLoading && !isMetricsDataLoading && showAvg) { + // enable cluster avg + chartStartTimeRef.current = startTimeRef.current; + seriesRef.current = getSeriesForSymmetricalChart( + metricAboveQuery.data, + metricBelowQuery.data, + nodeIPAddress.name, + metricPrefixAbove, + metricPrefixBelow, + metricAboveAvgQuery.data, + metricBelowAvgQuery.data, + ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isMetricsDataLoading, isMetricsAvgDataLoading, showAvg]); + + return ( + + ); +}; + +export default MetricSymmetricalChart; diff --git a/ui/src/components/NodePageOverviewTab.js b/ui/src/components/NodePageOverviewTab.js index ba474a23fe..cc452e95b8 100644 --- a/ui/src/components/NodePageOverviewTab.js +++ b/ui/src/components/NodePageOverviewTab.js @@ -195,7 +195,7 @@ const NodePageOverviewTab = (props) => { {nodeName} - {currentNodeReturnByK8S?.status === API_STATUS_UNKNOWN ? ( + {currentNodeReturnByK8S?.status === API_STATUS_UNKNOWN && !currentNodeReturnByK8S.internalIP ? ( !currentNodeReturnByK8S?.deploying ? ( { const { nodeName } = props; const dispatch = useDispatch(); - const volumeListData = useVolumesWithAlerts(); + const volumeListData = useVolumesWithAlerts(nodeName); useRefreshEffect(refreshVolumesAction, stopRefreshVolumesAction); useRefreshEffect( diff --git a/ui/src/components/NodePartitionTable.js b/ui/src/components/NodePartitionTable.js index 2867a56e17..bf43453ef2 100644 --- a/ui/src/components/NodePartitionTable.js +++ b/ui/src/components/NodePartitionTable.js @@ -3,7 +3,9 @@ import React, { useCallback } from 'react'; import styled, { useTheme } from 'styled-components'; import { useTable } from 'react-table'; import { useQuery } from 'react-query'; -import { ProgressBar, Loader, EmptyTable } from '@scality/core-ui'; +import ProgressBar from '@scality/core-ui/dist/components/progressbar/ProgressBar.component'; +import Loader from '@scality/core-ui/dist/components/loader/Loader.component'; +import EmptyTable from '@scality/core-ui/dist/components/emptytable/Emptytable.component'; import { fontSize, padding } from '@scality/core-ui/dist/style/theme'; import { queryNodeFSUsage, diff --git a/ui/src/components/VolumeCharts.js b/ui/src/components/VolumeCharts.js new file mode 100644 index 0000000000..8b47b13789 --- /dev/null +++ b/ui/src/components/VolumeCharts.js @@ -0,0 +1,265 @@ +import React, { useRef, useEffect } from 'react'; +import { useQuery } from 'react-query'; +import { + LineTemporalChart, + useMetricsTimeSpan, +} from '@scality/core-ui/dist/next'; +import { useStartingTimeStamp } from '../containers/StartTimeProvider'; +import { getSeriesForSymmetricalChart, getSingleResourceSerie } from '../services/graphUtils'; +import { + getVolumeIOPSReadQuery, + getVolumeIOPSWriteQuery, + getVolumeLatencyReadQuery, + getVolumeLatencyWriteQuery, + getVolumeThroughputReadQuery, + getVolumeThroughputWriteQuery, + getVolumeUsageQuery, +} from '../services/platformlibrary/metrics'; +import type { UseQueryResult } from 'react-query'; +import type { TimeSpanProps } from '../services/platformlibrary/metrics'; +import { YAXIS_TITLE_READ_WRITE } from '@scality/core-ui/dist/components/linetemporalchart/LineTemporalChart.component'; + +const useSingleChartSerie = ({ + getQuery, + resourceName, +}: { + getQuery: (timeSpanProps: TimeSpanProps) => UseQueryResult, + resourceName: string, +}) => { + const { startingTimeISO, currentTimeISO } = useStartingTimeStamp(); + const { frequency } = useMetricsTimeSpan(); + + const startTimeRef = useRef(startingTimeISO); + const chartStartTimeRef = useRef(startingTimeISO); + const seriesRef = useRef(); + + startTimeRef.current = startingTimeISO; + + const query = useQuery( + getQuery({ + startingTimeISO, + currentTimeISO, + frequency, + }), + ); + + const isLoading = query.isLoading; + + useEffect(() => { + if (!isLoading) { + chartStartTimeRef.current = startTimeRef.current; + seriesRef.current = getSingleResourceSerie( + query.data, + resourceName, + ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isLoading]); + + return { + series: seriesRef.current || [], + startingTimeStamp: Date.parse(chartStartTimeRef.current) / 1000, + isLoading, + }; +}; + +const useSymetricalChartSeries = ({ + getQueryAbove, + getQueryBelow, + aboveQueryPrefix, + belowQueryPrefix, + resourceName, +}: { + getQueryAbove: (timeSpanProps: TimeSpanProps) => UseQueryResult, + getQueryBelow: (timeSpanProps: TimeSpanProps) => UseQueryResult, + resourceName: string, + aboveQueryPrefix: string, + belowQueryPrefix: string, +}) => { + const { startingTimeISO, currentTimeISO } = useStartingTimeStamp(); + const { frequency } = useMetricsTimeSpan(); + + const startTimeRef = useRef(startingTimeISO); + const chartStartTimeRef = useRef(startingTimeISO); + const seriesRef = useRef(); + + startTimeRef.current = startingTimeISO; + + const aboveQuery = useQuery( + getQueryAbove({ + startingTimeISO, + currentTimeISO, + frequency, + }), + ); + + const belowQuery = useQuery( + getQueryBelow({ + startingTimeISO, + currentTimeISO, + frequency, + }), + ); + + const isLoading = aboveQuery.isLoading || belowQuery.isLoading; + + useEffect(() => { + if (!isLoading) { + chartStartTimeRef.current = startTimeRef.current; + seriesRef.current = getSeriesForSymmetricalChart( + belowQuery.data, + aboveQuery.data, + resourceName, + aboveQueryPrefix, + belowQueryPrefix, + ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isLoading]); + + return { + series: seriesRef.current || [], + startingTimeStamp: Date.parse(chartStartTimeRef.current) / 1000, + isLoading, + }; +}; + +export const VolumeThroughputChart = ({ + instanceIp, + deviceName, + volumeName, +}: { + instanceIp: string, + deviceName: string, + volumeName: string, +}) => { + const { series, startingTimeStamp, isLoading } = useSymetricalChartSeries({ + getQueryAbove: (timeSpanProps: TimeSpanProps) => + getVolumeThroughputWriteQuery(instanceIp, deviceName, timeSpanProps), + getQueryBelow: (timeSpanProps: TimeSpanProps) => + getVolumeThroughputReadQuery(instanceIp, deviceName, timeSpanProps), + aboveQueryPrefix: 'write', + belowQueryPrefix: 'read', + resourceName: volumeName, + }); + + return ( + + ); +}; + +export const VolumeLatencyChart = ({ + instanceIp, + deviceName, + volumeName, +}: { + instanceIp: string, + deviceName: string, + volumeName: string, +}) => { + const { series, startingTimeStamp, isLoading } = useSymetricalChartSeries({ + getQueryAbove: (timeSpanProps: TimeSpanProps) => + getVolumeLatencyWriteQuery(instanceIp, deviceName, timeSpanProps), + getQueryBelow: (timeSpanProps: TimeSpanProps) => + getVolumeLatencyReadQuery(instanceIp, deviceName, timeSpanProps), + aboveQueryPrefix: 'write', + belowQueryPrefix: 'read', + resourceName: volumeName, + }); + + return ( + + ); +}; + +export const VolumeIOPSChart = ({ + instanceIp, + deviceName, + volumeName, +}: { + instanceIp: string, + deviceName: string, + volumeName: string, +}) => { + const { series, startingTimeStamp, isLoading } = useSymetricalChartSeries({ + getQueryAbove: (timeSpanProps: TimeSpanProps) => + getVolumeIOPSWriteQuery(instanceIp, deviceName, timeSpanProps), + getQueryBelow: (timeSpanProps: TimeSpanProps) => + getVolumeIOPSReadQuery(instanceIp, deviceName, timeSpanProps), + aboveQueryPrefix: 'write', + belowQueryPrefix: 'read', + resourceName: volumeName, + }); + + return ( + + ); +}; + +export const VolumeUsageChart = ({ + pvcName, + namespace, + volumeName, +}: { + pvcName: string, + namespace: string, + volumeName: string, +}) => { + const { series, startingTimeStamp, isLoading } = useSingleChartSerie({ + getQuery: (timeSpanProps: TimeSpanProps) => getVolumeUsageQuery(pvcName, namespace, timeSpanProps), + resourceName: volumeName + }) + + return ( + + ); +}; + diff --git a/ui/src/components/VolumeMetricsTab.js b/ui/src/components/VolumeMetricsTab.js index dce0fde20b..94484feda4 100644 --- a/ui/src/components/VolumeMetricsTab.js +++ b/ui/src/components/VolumeMetricsTab.js @@ -1,31 +1,19 @@ import React, { useEffect } from 'react'; import { useHistory } from 'react-router'; import { useDispatch, useSelector } from 'react-redux'; -import styled, { useTheme } from 'styled-components'; -import { LineChart, Dropdown } from '@scality/core-ui'; +import styled from 'styled-components'; +import { Dropdown } from '@scality/core-ui'; import { Button } from '@scality/core-ui/dist/next'; import { fetchVolumeStatsAction, updateVolumeStatsAction, } from '../ducks/app/monitoring'; -import { fontSize, padding } from '@scality/core-ui/dist/style/theme'; -import { - addMissingDataPoint, - fromUnixTimestampToDate, - useDynamicChartSize, -} from '../services/utils'; -import { yAxisUsage, yAxisWriteRead } from './LinechartSpec'; +import { padding, fontSize, spacing } from '@scality/core-ui/dist/style/theme'; import { VOLUME_CONDITION_LINK, LAST_SEVEN_DAYS, LAST_TWENTY_FOUR_HOURS, LAST_ONE_HOUR, - SAMPLE_DURATION_LAST_SEVEN_DAYS, - SAMPLE_DURATION_LAST_TWENTY_FOUR_HOURS, - SAMPLE_DURATION_LAST_ONE_HOUR, - SAMPLE_FREQUENCY_LAST_SEVEN_DAYS, - SAMPLE_FREQUENCY_LAST_TWENTY_FOUR_HOURS, - SAMPLE_FREQUENCY_LAST_ONE_HOUR, queryTimeSpansCodes, GRAFANA_DASHBOARDS, } from '../constants'; @@ -33,16 +21,48 @@ import { useIntl } from 'react-intl'; import { VolumeTab, MetricsActionContainer, - GraphsContainer, - RowGraphContainer, - GraphTitle, GraphWrapper, } from './style/CommonLayoutStyle'; +import { + VolumeIOPSChart, + VolumeLatencyChart, + VolumeThroughputChart, + VolumeUsageChart, +} from './VolumeCharts'; +import { SyncedCursorCharts } from '@scality/core-ui/dist/components/vegachartv2/SyncedCursorCharts'; const MetricGraphCardContainer = styled.div` min-height: 270px; `; +const GraphGrid = styled.div` + display: grid; + gap: 8px; + grid-template: + 'usage latency' 1fr + 'throughput iops' 1fr + / 1fr 1fr; + .sc-vegachart svg { + background-color: inherit !important; + } + .usage { + grid-area: usage; + } + .latency { + grid-area: latency; + } + .throughput { + grid-area: throughput; + } + .iops { + grid-area: iops; + } + padding-left: ${spacing.sp12}; + .sc-tabs-item-content { + overflow: scroll; + } +`; + // No data rendering should be extracted to an common style const NoMetricsText = styled.div` color: ${(props) => props.theme.textPrimary}; @@ -50,16 +70,11 @@ const NoMetricsText = styled.div` padding: ${padding.small} 0 0 ${padding.larger}; `; -const NoDataGraphText = styled.div` - color: ${(props) => props.theme.textPrimary}; - font-size: ${fontSize.small}; - padding: ${padding.small} 0 0 ${padding.larger}; -`; - const MetricsTab = (props) => { const { volumeCondition, - volumeMetricGraphData, + deviceName, + instanceIp, volumeName, volumeNamespace, volumePVCName, @@ -68,12 +83,10 @@ const MetricsTab = (props) => { const history = useHistory(); const intl = useIntl(); const query = new URLSearchParams(history?.location?.search); - const theme = useTheme(); const metricsTimeSpan = useSelector( (state) => state.app.monitoring.volumeStats.metricsTimeSpan, ); const config = useSelector((state) => state.config); - const [graphWidth, graphHeight] = useDynamicChartSize('graph_container'); // write the selected timespan in URL const writeUrlTimeSpan = (timespan: string) => { @@ -107,170 +120,6 @@ const MetricsTab = (props) => { const updateMetricsGraph = () => dispatch(fetchVolumeStatsAction()); - const queryStartingTime = volumeMetricGraphData?.queryStartingTime; - // the item chosed by the metrics time span dropdown - // we should have a unified unit, second, for all the props related to prometheus - let sampleDuration = null; - let sampleFrequency = null; - - if (metricsTimeSpan === LAST_SEVEN_DAYS) { - // do the query every 1 hour - sampleDuration = SAMPLE_DURATION_LAST_SEVEN_DAYS; - sampleFrequency = SAMPLE_FREQUENCY_LAST_SEVEN_DAYS; - } else if (metricsTimeSpan === LAST_TWENTY_FOUR_HOURS) { - // do the query every 1 minute - sampleDuration = SAMPLE_DURATION_LAST_TWENTY_FOUR_HOURS; - sampleFrequency = SAMPLE_FREQUENCY_LAST_TWENTY_FOUR_HOURS; - } else if (metricsTimeSpan === LAST_ONE_HOUR) { - // do the query every 1 second - sampleDuration = SAMPLE_DURATION_LAST_ONE_HOUR; - sampleFrequency = SAMPLE_FREQUENCY_LAST_ONE_HOUR; - } - - // We need to manually add the missing data points due to the shutdown of VM - const operateMetricRawData = (metricRawData) => - addMissingDataPoint( - metricRawData, - queryStartingTime, - sampleDuration, - sampleFrequency, - ); - - const volumeUsageOperated = operateMetricRawData( - volumeMetricGraphData?.volumeUsage, - ); - const volumeLatencyWriteOperated = operateMetricRawData( - volumeMetricGraphData?.volumeLatencyWrite, - ); - const volumeLatencyReadOperated = operateMetricRawData( - volumeMetricGraphData?.volumeLatencyRead, - ); - const volumeThroughputWriteOperated = operateMetricRawData( - volumeMetricGraphData?.volumeThroughputWrite, - ); - const volumeThroughputReadOperated = operateMetricRawData( - volumeMetricGraphData?.volumeThroughputRead, - ); - const volumeIOPSReadOperated = operateMetricRawData( - volumeMetricGraphData?.volumeIOPSRead, - ); - const volumeIOPSWriteOperated = operateMetricRawData( - volumeMetricGraphData?.volumeIOPSWrite, - ); - - // slot[0] => timestamp - // slot[1] => value - - const volumeUsageData = volumeUsageOperated?.map((slot) => { - return { - date: fromUnixTimestampToDate(slot[0]), - y: slot[1] === null ? null : Math.round(slot[1] * 100), - }; - }); - - const volumeLatencyWriteData = volumeLatencyWriteOperated?.map((slot) => { - return { - date: fromUnixTimestampToDate(slot[0]), - write: slot[1], - type: 'write', - }; - }); - - const volumeLatencyReadData = volumeLatencyReadOperated?.map((slot) => { - return { - date: fromUnixTimestampToDate(slot[0]), - read: slot[1], - type: 'read', - }; - }); - - const volumeThroughputWriteData = volumeThroughputWriteOperated?.map( - (slot) => { - return { - date: fromUnixTimestampToDate(slot[0]), - write: slot[1], - type: 'write', - }; - }, - ); - - const volumeThroughtReadData = volumeThroughputReadOperated?.map((slot) => { - return { - date: fromUnixTimestampToDate(slot[0]), - read: slot[1], - type: 'read', - }; - }); - - const volumeIOPSReadData = volumeIOPSReadOperated?.map((slot) => { - return { - date: fromUnixTimestampToDate(slot[0]), - read: slot[1] === null ? null : slot[1], - type: 'read', - }; - }); - - const volumeIOPSWriteData = volumeIOPSWriteOperated?.map((slot) => { - return { - date: fromUnixTimestampToDate(slot[0]), - write: slot[1] === null ? null : slot[1], - type: 'write', - }; - }); - - const volumeThroughputData = volumeThroughputWriteData?.concat( - volumeThroughtReadData, - ); - - const volumeIOPSData = volumeIOPSWriteData?.concat(volumeIOPSReadData); - - const volumeLatencyData = volumeLatencyWriteData?.concat( - volumeLatencyReadData, - ); - - const xAxis = { - field: 'date', - type: 'temporal', - axis: { - // Refer to all the available time format: https://github.com/d3/d3-time-format#locale_format - format: '%m/%d %H:%M', - // Boolean value that determines whether the axis should include ticks. - ticks: true, - tickCount: 4, - labelAngle: -50, - labelColor: theme.textSecondary, - }, - title: null, - }; - - const colorUsage = { - field: 'type', - type: 'nominal', - legend: null, - domain: ['y'], - scale: { - range: ['#6ED0E0'], - }, - }; - - const colors = { - field: 'type', - type: 'nominal', - legend: { - direction: 'horizontal', - orient: 'bottom', - title: null, - symbolType: 'stroke', - labelFontSize: 15, - columnPadding: 50, - symbolStrokeWidth: 5, - }, - domain: ['write', 'read'], - scale: { - range: ['#73BF69', '#E0B400'], - }, - }; - // the time span of metrics graph, by default is `Last 24 hours` const metricsTimeSpanItems = [ { @@ -339,82 +188,38 @@ const MetricsTab = (props) => { )} {volumeCondition === VOLUME_CONDITION_LINK ? ( - - - - Usage (%) - {volumeUsageData?.length > 0 && graphWidth ? ( - - ) : ( - No available usage data - )} + + + + - - Latency (µs) - {volumeLatencyData?.length > 0 && graphWidth ? ( - - ) : ( - No available latency data - )} + + - - - - Throughput (MB/s) - {volumeThroughputData?.length > 0 && graphWidth ? ( - - ) : ( - - No available throughput data - - )} + + - - IOPS - {volumeIOPSData?.length > 0 && graphWidth ? ( - - ) : ( - No available IOPS data - )} + + - - + + ) : ( {intl.formatMessage({ id: 'volume_is_not_bound' })} diff --git a/ui/src/constants.js b/ui/src/constants.js index 9db4daa42a..049714c922 100644 --- a/ui/src/constants.js +++ b/ui/src/constants.js @@ -54,6 +54,7 @@ export const QUERY_LAST_ONE_HOUR = 'now-1h'; export const PORT_NODE_EXPORTER = '9100'; +// deprecated should retrieve from core-ui export const queryTimeSpansCodes = [ { label: QUERY_LAST_SEVEN_DAYS, @@ -120,10 +121,10 @@ export const CIRCLE_BASE_SIZE = 'CIRCLE_BASE_SIZE'; export const CIRCLE_DOUBLE_SIZE = 'CIRCLE_DOUBLE_SIZE'; // LineChart colors -export const lineColor1 = '#245A83'; -export const lineColor2 = '#808080'; -export const lineColor3 = '#A04EC9'; -export const lineColor4 = '#C6B38A'; +export const lineColor1 = '#A14FBF'; +export const lineColor2 = '#BE9A40'; +export const lineColor3 = '#4BE4E2'; +export const lineColor4 = '#DC90F1'; // Grafana dashboard UIDs (for stable links) export const GRAFANA_DASHBOARDS = { @@ -131,3 +132,7 @@ export const GRAFANA_DASHBOARDS = { nodes: "node-exporter-full", volumes: "919b92a8e8041bd567af9edab12c840c", }; + +// Height of the charts +export const HEIGHT_DEFAULT_CHART = 120; +export const HEIGHT_SYMMETRICAL_CHART = 160; diff --git a/ui/src/containers/App.js b/ui/src/containers/App.js index 50d9370378..97a45d39a0 100755 --- a/ui/src/containers/App.js +++ b/ui/src/containers/App.js @@ -1,20 +1,23 @@ //@flow import React from 'react'; +import { MetricsTimeSpanProvider } from '@scality/core-ui/dist/next'; import Layout from './Layout'; import AlertProvider from './AlertProvider'; import ConfigProvider from './ConfigProvider'; -import { MetricsTimeSpanProvider } from '../hooks'; import FederatedIntlProvider from './IntlProvider'; +import StartTimeProvider from './StartTimeProvider'; const App = () => { return ( - - - + + + + + diff --git a/ui/src/containers/NodePageMetricsTab.js b/ui/src/containers/NodePageMetricsTab.js index 7e6f7fc248..868c74781b 100644 --- a/ui/src/containers/NodePageMetricsTab.js +++ b/ui/src/containers/NodePageMetricsTab.js @@ -2,54 +2,60 @@ import React from 'react'; import { useDispatch } from 'react-redux'; import { useHistory } from 'react-router'; -import styled, { useTheme } from 'styled-components'; -import { LineChart, Loader, Dropdown, Toggle } from '@scality/core-ui'; +import styled from 'styled-components'; +import { Dropdown, Toggle, BasicText } from '@scality/core-ui'; import { Button } from '@scality/core-ui/dist/next'; -import { padding } from '@scality/core-ui/dist/style/theme'; -import { - updateNodeStatsFetchArgumentAction, - MonitoringMetrics, -} from '../ducks/app/monitoring'; +import { spacing } from '@scality/core-ui/dist/style/theme'; +import { useIntl } from 'react-intl'; +import { queryTimeSpansCodes } from '@scality/core-ui/dist/components/constants'; import { - yAxisUsage, - yAxis, - getTooltipConfig, -} from '../components/LinechartSpec'; + useMetricsTimeSpan, + SyncedCursorCharts, +} from '@scality/core-ui/dist/next'; +import { updateNodeStatsFetchArgumentAction } from '../ducks/app/monitoring'; import { NodeTab, MetricsActionContainer, - GraphTitle, GraphWrapper, } from '../components/style/CommonLayoutStyle'; -import { - addMissingDataPoint, - fromUnixTimestampToDate, - useURLQuery, - useDynamicChartSize, -} from '../services/utils'; +import { useURLQuery } from '../services/utils'; import { LAST_SEVEN_DAYS, LAST_TWENTY_FOUR_HOURS, LAST_ONE_HOUR, - SAMPLE_DURATION_LAST_SEVEN_DAYS, - SAMPLE_DURATION_LAST_TWENTY_FOUR_HOURS, - SAMPLE_DURATION_LAST_ONE_HOUR, - SAMPLE_FREQUENCY_LAST_SEVEN_DAYS, - SAMPLE_FREQUENCY_LAST_TWENTY_FOUR_HOURS, - SAMPLE_FREQUENCY_LAST_ONE_HOUR, - queryTimeSpansCodes, PORT_NODE_EXPORTER, GRAFANA_DASHBOARDS, } from '../constants'; -import { useIntl } from 'react-intl'; import { useTypedSelector } from '../hooks'; +import { + getCPUUsageQuery, + getCPUUsageAvgQuery, + getIOPSWriteQuery, + getIOPSReadQuery, + getIOPSWriteAvgQuery, + getIOPSReadAvgQuery, + getSystemLoadQuery, + getSystemLoadAvgQuery, + getMemoryQuery, + getMemoryAvgQuery, + getControlPlaneBandWidthInQuery, + getControlPlaneBandWidthOutQuery, + getControlPlaneBandWidthAvgInQuery, + getControlPlaneBandWidthAvgOutQuery, + getWorkloadPlaneBandWidthInQuery, + getWorkloadPlaneBandWidthOutQuery, + getWorkloadPlaneBandWidthAvgInQuery, + getWorkloadPlaneBandWidthAvgOutQuery, +} from '../services/platformlibrary/metrics'; +import MetricChart from '../components/MetricChart'; +import MetricSymmetricalChart from '../components/MetricSymmetricalChart'; const GraphGrid = styled.div` display: grid; gap: 8px; grid-template: 'cpuusage systemload' 1fr - 'memory iops ' 1fr + 'memory iops' 1fr 'cpbandwidth wpbandwidth' 1fr / 1fr 1fr; .sc-vegachart svg { @@ -58,8 +64,8 @@ const GraphGrid = styled.div` .cpuusage { grid-area: cpuusage; } - .systemLoad { - grid-area: systemLoad; + .systemload { + grid-area: systemload; } .memory { grid-area: memory; @@ -73,7 +79,10 @@ const GraphGrid = styled.div` .wpbandwidth { grid-area: wpbandwidth; } - padding-left: ${padding.small}; + padding-left: ${spacing.sp12}; + .sc-tabs-item-content { + overflow: scroll; + } `; const MetricsToggleWrapper = styled.div` @@ -82,36 +91,40 @@ const MetricsToggleWrapper = styled.div` flex: 1; .sc-toggle { - margin-right: ${padding.small}; + margin-right: ${spacing.sp8}; } `; +const NoDataAvailable = styled.div` + display: flex; + padding-top: ${spacing.sp40}; + justify-content: center; +`; + const NodePageMetricsTab = ({ nodeName, - nodeStats, instanceIP, - avgStats, + controlPlaneInterface, + workloadPlaneInterface, + nodesIPsInfo, }: { nodeName: string, - nodeStats: MonitoringMetrics, instanceIP: string, - avgStats: MonitoringMetrics, + controlPlaneInterface: string, + workloadPlaneInterface: string, + nodesIPsInfo: [], }) => { const dispatch = useDispatch(); - const theme = useTheme(); const history = useHistory(); const query = useURLQuery(); const intl = useIntl(); const api = useTypedSelector((state) => state.config.api); - const metricsTimeSpan = useTypedSelector( - (state) => state.app.monitoring.nodeStats.metricsTimeSpan, - ); + const { label } = useMetricsTimeSpan(); + const showAvg = useTypedSelector( (state) => state.app.monitoring.nodeStats.showAvg, ); - const [graphWidth, graphHeight] = useDynamicChartSize('graph_container'); - // To redirect to the right Node(Detailed) dashboard in Grafana const unameInfos = useTypedSelector( (state) => state.app.monitoring.unameInfo, @@ -121,363 +134,12 @@ const NodePageMetricsTab = ({ unameInfo?.metric?.instance === `${instanceIP}:${PORT_NODE_EXPORTER}`, )?.metric?.nodename; - let sampleDuration = null; - let sampleFrequency = null; - if (metricsTimeSpan === LAST_SEVEN_DAYS) { - // do the query every 1 hour - sampleDuration = SAMPLE_DURATION_LAST_SEVEN_DAYS; - sampleFrequency = SAMPLE_FREQUENCY_LAST_SEVEN_DAYS; - } else if (metricsTimeSpan === LAST_TWENTY_FOUR_HOURS) { - // do the query every 1 minute - sampleDuration = SAMPLE_DURATION_LAST_TWENTY_FOUR_HOURS; - sampleFrequency = SAMPLE_FREQUENCY_LAST_TWENTY_FOUR_HOURS; - } else if (metricsTimeSpan === LAST_ONE_HOUR) { - // do the query every 1 second - sampleDuration = SAMPLE_DURATION_LAST_ONE_HOUR; - sampleFrequency = SAMPLE_FREQUENCY_LAST_ONE_HOUR; - } - - // slot[0] => timestamp - // slot[1] => value - // We need to manually add the missing data points due to the shutdown of VM - const queryStartingTime = nodeStats?.queryStartingTime; - const operateMetricRawData = (metricRawData) => - addMissingDataPoint( - metricRawData, - queryStartingTime, - sampleDuration, - sampleFrequency, - ); - - const typedMetrics = { - cpuUsage: 'CPU Usage', - systemLoad: 'System Load', - memory: 'Memory', - iopsRead: 'Read', - iopsWrite: 'Write', - controlPlaneNetworkBandwidthIn: 'In', - controlPlaneNetworkBandwidthOut: 'Out', - workloadPlaneNetworkBandwidthIn: 'In', - workloadPlaneNetworkBandwidthOut: 'Out', - }; - - const typedAvgMetrics = { - cpuUsage: 'cluster avg', - systemLoad: 'cluster avg', - memory: 'cluster avg', - iopsRead: 'read avg', - iopsWrite: 'write avg', - controlPlaneNetworkBandwidthIn: 'in avg', - controlPlaneNetworkBandwidthOut: 'out avg', - workloadPlaneNetworkBandwidthIn: 'in avg', - workloadPlaneNetworkBandwidthOut: 'out avg', - }; - - const nodeStatsData = Object.keys(nodeStats).reduce((acc, metricName) => { - const data = operateMetricRawData(nodeStats[metricName][0]?.values); - - let extra = {}; - const metricType = typedMetrics[metricName]; - if (metricType !== undefined) extra.type = metricType; - - /* - ** Using 'symbol': 'A' because vega-lite internally assign the plain line - ** to the first alphabetical item when strokeDash is used - */ - acc[metricName] = data.map((slot) => ({ - date: fromUnixTimestampToDate(slot[0]), - y: slot[1], - symbol: 'A', - ...extra, - })); - return acc; - }, {}); - - const avgStatsData = Object.keys(avgStats).reduce((acc, metricName) => { - const data = operateMetricRawData(avgStats[metricName][0]?.values); - - let extra = {}; - const metricType = typedAvgMetrics[metricName]; - if (metricType !== undefined) extra.type = metricType; - - acc[metricName] = data.map((slot) => ({ - date: fromUnixTimestampToDate(slot[0]), - y: slot[1], - symbol: 'Cluster avg', - ...extra, - })); - return acc; - }, {}); - - let cpuData = nodeStatsData['cpuUsage']; - let systemLoadData = nodeStatsData['systemLoad']; - let memoryData = nodeStatsData['memory']; - // Combine the read/write, in/out into one dataset - let iopsData = nodeStatsData['iopsRead'].concat(nodeStatsData['iopsWrite']); - let controlPlaneNetworkBandwidthData = nodeStatsData[ - 'controlPlaneNetworkBandwidthIn' - ].concat(nodeStatsData['controlPlaneNetworkBandwidthOut']); - let workloadPlaneNetworkBandwidthData = nodeStatsData[ - 'workloadPlaneNetworkBandwidthIn' - ].concat(nodeStatsData['workloadPlaneNetworkBandwidthOut']); - - if (showAvg) { - cpuData = cpuData.concat(avgStatsData['cpuUsage']); - systemLoadData = systemLoadData.concat(avgStatsData['systemLoad']); - memoryData = memoryData.concat(avgStatsData['memory']); - iopsData = iopsData - .concat(avgStatsData['iopsRead']) - .concat(avgStatsData['iopsWrite']); - controlPlaneNetworkBandwidthData = controlPlaneNetworkBandwidthData - .concat(avgStatsData['controlPlaneNetworkBandwidthIn']) - .concat(avgStatsData['controlPlaneNetworkBandwidthOut']); - workloadPlaneNetworkBandwidthData = workloadPlaneNetworkBandwidthData - .concat(avgStatsData['workloadPlaneNetworkBandwidthIn']) - .concat(avgStatsData['workloadPlaneNetworkBandwidthOut']); - } - - // Tooltip Custom Spec - const ttpCPUSpec = [ - { - field: 'CPU Usage', - type: 'quantitative', - title: `CPU Usage - ${nodeName}`, - format: '.1f', - }, - ]; - const ttpSystemLoadSpec = [ - { - field: 'System Load', - type: 'quantitative', - title: `System Load - ${nodeName}`, - format: '.1f', - }, - ]; - const ttpMemorySpec = [ - { - field: 'Memory', - type: 'quantitative', - title: `Memory - ${nodeName}`, - format: '.1f', - }, - ]; - const ttpIOPSSpec = [ - { - field: `Read`, - type: 'quantitative', - title: `Read - ${nodeName}`, - format: '.1f', - }, - { - field: 'Write', - type: 'quantitative', - title: `Write - ${nodeName}`, - format: '.1f', - }, - ]; - const ttpInOutSpec = [ - { - field: 'In', - type: 'quantitative', - title: `In - ${nodeName}`, - format: '.2f', - }, - { - field: 'Out', - type: 'quantitative', - title: `Out - ${nodeName}`, - format: '.2f', - }, - ]; - - const clusterAvgLocale = intl.formatMessage({ id: 'cluster_avg' }); - if (showAvg) { - ttpCPUSpec.push({ - field: 'cluster avg', - type: 'quantitative', - title: `CPU Usage - ${clusterAvgLocale}`, - format: '.1f', - }); - ttpSystemLoadSpec.push({ - field: 'cluster avg', - type: 'quantitative', - title: `System Load - ${clusterAvgLocale}`, - format: '.1f', - }); - ttpMemorySpec.push({ - field: 'cluster avg', - type: 'quantitative', - title: `Memory - ${clusterAvgLocale}`, - format: '.1f', - }); - ttpIOPSSpec.push( - { - field: 'read avg', - type: 'quantitative', - title: `Read - ${clusterAvgLocale}`, - format: '.1f', - }, - { - field: 'write avg', - type: 'quantitative', - title: `Write - ${clusterAvgLocale}`, - format: '.1f', - }, - ); - ttpInOutSpec.push( - { - field: 'in avg', - type: 'quantitative', - title: `In - ${clusterAvgLocale}`, - format: '.2f', - }, - { - field: 'out avg', - type: 'quantitative', - title: `Out - ${clusterAvgLocale}`, - format: '.2f', - }, - ); - } - const tooltipConfigCPU = getTooltipConfig(ttpCPUSpec); - const tooltipConfigSystemLoad = getTooltipConfig(ttpSystemLoadSpec); - const tooltipConfigMemory = getTooltipConfig(ttpMemorySpec); - const tooltipConfigIops = getTooltipConfig(ttpIOPSSpec); - const tooltipConfigInOut = getTooltipConfig(ttpInOutSpec); - - const xAxis = { - field: 'date', - type: 'temporal', - axis: { - // Refer to all the available time format: https://github.com/d3/d3-time-format#locale_format - format: - metricsTimeSpan === (LAST_ONE_HOUR || LAST_TWENTY_FOUR_HOURS) - ? '%H:%M' - : '%m/%d', - // Boolean value that determines whether the axis should include ticks. - ticks: true, - tickCount: 4, - labelColor: theme.textSecondary, - }, - title: null, - }; - - const strokeDashConfig = { - field: 'symbol', - type: 'nominal', - legend: { - direction: 'horizontal', - orient: 'bottom', - title: null, - values: [`${clusterAvgLocale}.`], - symbolSize: 300, - labelFontSize: 12, - }, - }; - - const opacityConfig = { - condition: { - test: 'datum.symbol == "Cluster avg"', - value: 0.5, - }, - value: 1, - }; - - // the `read` and `out` should be the same color - // the `write` and `in` should be the same color - const colorCPU = { - field: 'type', - type: 'nominal', - scale: { range: ['#9645c2'] }, - legend: { - direction: 'horizontal', - orient: 'bottom', - title: null, - values: showAvg ? ['CPU Usage'] : [''], - labelFontSize: 12, - symbolSize: 300, - }, - }; - - const colorSystemLoad = { - field: 'type', - type: 'nominal', - scale: { range: ['#9645c2'] }, - legend: { - direction: 'horizontal', - orient: 'bottom', - title: null, - values: showAvg ? ['System Load'] : [''], - labelFontSize: 12, - symbolSize: 300, - }, - }; - - const colorMemory = { - field: 'type', - type: 'nominal', - scale: { range: ['#9645c2'] }, - legend: { - direction: 'horizontal', - orient: 'bottom', - title: null, - values: showAvg ? ['Memory'] : [''], - labelFontSize: 12, - symbolSize: 300, - }, - }; - - const colorsWriteRead = { - field: 'type', - type: 'nominal', - legend: { - direction: 'horizontal', - orient: 'bottom', - title: null, - symbolType: 'stroke', - symbolSize: 300, - labelFontSize: 12, - columnPadding: 15, - symbolStrokeWidth: 2, - values: ['Read', 'Write'], - }, - domain: ['Read', 'Write'], - scale: { - range: showAvg - ? ['#9645c2', '#bfaa7f', '#9645c2', '#bfaa7f'] - : ['#9645c2', '#bfaa7f'], - }, - }; - - const colorsInOut = { - field: 'type', - type: 'nominal', - legend: { - direction: 'horizontal', - orient: 'bottom', - title: null, - symbolType: 'stroke', - symbolSize: 300, - labelFontSize: 12, - columnPadding: 15, - symbolStrokeWidth: 2, - values: ['In', 'Out'], - }, - domain: ['In', 'Out'], - scale: { - range: showAvg - ? ['#bfaa7f', '#9645c2', '#bfaa7f', '#9645c2'] - : ['#bfaa7f', '#9645c2'], - }, - }; - const lineConfig = { strokeWidth: 1.5 }; - // write the selected timespan in URL - const writeUrlTimeSpan = (timespan) => { - let formatted = queryTimeSpansCodes.find((item) => item.value === timespan); + const writeUrlTimeSpan = (label) => { + let formatted = queryTimeSpansCodes.find((item) => item.label === label); if (formatted) { - query.set('from', formatted.label); + query.set('from', formatted.query); history.push({ search: query.toString() }); } }; @@ -497,35 +159,34 @@ const NodePageMetricsTab = ({ label: option, 'data-cy': option, onClick: () => { - dispatch(updateNodeStatsFetchArgumentAction({ metricsTimeSpan: option })); + //dispatch(updateNodeStatsFetchArgumentAction({ metricsTimeSpan: option })); writeUrlTimeSpan(option); }, - selected: metricsTimeSpan === option, + selected: label === option, })); - const metricsTimeSpanDropdownItems = metricsTimeSpanItems.filter( - (mTS) => mTS.label !== metricsTimeSpan, + (mTS) => mTS.label !== label, ); - return ( - ) => { - writeShowAvg(e.currentTarget.checked); - dispatch( - updateNodeStatsFetchArgumentAction({ - showAvg: e.currentTarget.checked, - }), - ); - }} - /> - {showAvg && !avgStatsData['cpuUsage'].length ? : null} + {instanceIP && ( + ) => { + writeShowAvg(e.currentTarget.checked); + dispatch( + updateNodeStatsFetchArgumentAction({ + showAvg: e.currentTarget.checked, + }), + ); + }} + /> + )} {api && api.url_grafana && ( } + disabled={instanceIP === ''} /> )} - + {instanceIP && ( + + )} - - - -
CPU Usage (%)
- {!cpuData.length && } -
- {graphWidth !== 0 && ( - - )} -
- - -
CPU System Load (%)
- {!systemLoadData.length && } -
- {graphWidth !== 0 && ( - - )} -
- - - -
Memory (%)
- {!memoryData.length && } -
- {graphWidth !== 0 && ( - - )} -
- - -
IOPS
- {!iopsData.length && } -
- {graphWidth !== 0 && ( - - )} -
- - -
Control Plane Bandwidth (MB/s)
- {!controlPlaneNetworkBandwidthData.length && } -
- {graphWidth !== 0 && ( - - )} -
- - -
Workload Plane Bandwidth (MB/s)
- {!workloadPlaneNetworkBandwidthData.length && } -
- {graphWidth !== 0 && ( - - )} -
-
+ {instanceIP ? ( + + + + + + + + + + + + + + + + + + + + + + + ) : ( + + + + {intl.formatMessage({ id: 'no_data_available_for_metrics' })} + + + )} ); }; diff --git a/ui/src/containers/NodePageRSP.js b/ui/src/containers/NodePageRSP.js index 89bdb5a48b..4a724338bb 100644 --- a/ui/src/containers/NodePageRSP.js +++ b/ui/src/containers/NodePageRSP.js @@ -8,8 +8,6 @@ import { getPodsListData } from '../services/PodUtils'; import { useURLQuery, useRefreshEffect } from '../services/utils'; import { updateNodeStatsFetchArgumentAction, - refreshNodeStatsAction, - stopRefreshNodeStatsAction, fetchNodeUNameInfoAction, } from '../ducks/app/monitoring'; import { @@ -35,7 +33,6 @@ import { } from '../constants'; import { useAlerts } from './AlertProvider'; import { useIntl } from 'react-intl'; -import { useTypedSelector } from '../hooks'; // fetches the data for all the tabs given the current selected Node // handles the refresh for the metrics tab @@ -70,7 +67,6 @@ const NodePageRSP = (props) => { const showAvg = queryShowAvg === 'true' ? true : nodeMetricsShowAvg; - useRefreshEffect(refreshNodeStatsAction, stopRefreshNodeStatsAction); useRefreshEffect(refreshVolumesAction, stopRefreshVolumesAction); // Retrieve the podlist data @@ -78,18 +74,14 @@ const NodePageRSP = (props) => { const podsListData = getPodsListData(name, pods); const nodes = useSelector((state) => state.app.nodes.list); const volumes = useSelector((state) => state.app.volumes.list); - const nodeStats = useTypedSelector( - (state) => state.app.monitoring.nodeStats.metrics, - ); - const avgStats = useTypedSelector( - (state) => state.app.monitoring.nodeStats.metricsAvg, - ); const nodesIPsInfo = useSelector((state) => state.app.nodes.IPsInfo); const instanceIP = nodes?.find((node) => node.name === name)?.internalIP ?? ''; - const controlPlaneInterface = nodesIPsInfo[name]?.controlPlane?.interface; - const workloadPlaneInterface = nodesIPsInfo[name]?.workloadPlane?.interface; + const controlPlaneInterface = + nodesIPsInfo[name]?.controlPlane?.interface ?? ''; + const workloadPlaneInterface = + nodesIPsInfo[name]?.workloadPlane?.interface ?? ''; const currentNode = nodeTableData?.find((node) => node.name.name === name); useEffect(() => { @@ -117,10 +109,13 @@ const NodePageRSP = (props) => { const alertList = useAlerts({ alertname: NODE_ALERTS_GROUP, - instance: `${instanceIP}:${PORT_NODE_EXPORTER}`, }); - const alertsNode = (alertList && alertList.alerts) || []; + const alertsNode = ((alertList && alertList.alerts) || []).filter( + (alert) => + alert.labels.instance === `${instanceIP}:${PORT_NODE_EXPORTER}` || + alert.labels.node === name, + ); return name && currentNode ? ( @@ -156,9 +151,10 @@ const NodePageRSP = (props) => { > (null); + +export const useStartingTimeStamp = (): { + startingTimeISO: string, + currentTimeISO: string, +} => { + const { startingTimeISO, currentTimeISO } = useContext(StartTimeContext); + if (!startingTimeISO || !currentTimeISO) { + throw new Error( + 'The useStartingTimeStamp hook can only be used within StartTimeProvider.', + ); + } + return { startingTimeISO, currentTimeISO }; +}; + +const StartTimeProvider = ({ children }: { children: Node }) => { + const { duration } = useMetricsTimeSpan(); + + const [currentTime, setCurrentTime] = useState(new Date()); + + const [startingTimeISO, setStartingTimeISO] = useState( + new Date((currentTime / 1000 - duration) * 1000).toISOString(), + ); + + const updateCurrentTime = useCallback(() => { + const newCurrentDate = new Date(); + setCurrentTime(newCurrentDate); + setStartingTimeISO( + new Date((newCurrentDate / 1000 - duration) * 1000).toISOString(), + ); + }, [duration]); + + useMemo(() => { + updateCurrentTime(); + }, [updateCurrentTime]); + + useEffect(() => { + const refreshStartTimeInterval = setInterval(() => { + updateCurrentTime(); + }, REFRESH_METRICS_GRAPH); + + return () => { + clearInterval(refreshStartTimeInterval); + }; + }, [updateCurrentTime]); + + return ( + + {children} + + ); +}; +export default StartTimeProvider; diff --git a/ui/src/containers/VolumePageContent.js b/ui/src/containers/VolumePageContent.js index 71f1493ccb..fd30ae1918 100644 --- a/ui/src/containers/VolumePageContent.js +++ b/ui/src/containers/VolumePageContent.js @@ -12,7 +12,6 @@ import { SPARSE_LOOP_DEVICE, RAW_BLOCK_DEVICE, LVM_LOGICAL_VOLUME, - PORT_NODE_EXPORTER, } from '../constants'; import { computeVolumeGlobalStatus } from '../services/NodeVolumesUtils'; import { useAlerts } from './AlertProvider'; @@ -46,7 +45,6 @@ const VolumePageContent = (props) => { pVList, pVCList, pods, - volumeStats, currentVolumeObject, loading, } = props; @@ -105,56 +103,17 @@ const VolumePageContent = (props) => { // prepare the data for const deviceName = volume?.status?.deviceName; - let instance; + let instanceIp; if (!node.internalIP) { // find the node name of this volume const nodeName = volume?.spec?.nodeName; const currentNode = nodes.find((node) => node.name === nodeName); - instance = `${currentNode?.internalIP}:${PORT_NODE_EXPORTER}`; + instanceIp = currentNode?.internalIP; } else { - instance = `${node?.internalIP}:${PORT_NODE_EXPORTER}`; + instanceIp = node?.internalIP; } - const queryStartingTime = volumeStats?.queryStartingTime; - const volumeUsage = volumeStats?.volumeUsage?.find( - (vU) => vU.metric.persistentvolumeclaim === PVCName, - )?.values; - const volumeThroughputWrite = volumeStats?.volumeThroughputWrite?.find( - (vTW) => - vTW.metric.instance === instance && vTW.metric.device === deviceName, - )?.values; - const volumeThroughputRead = volumeStats?.volumeThroughputRead?.find( - (vTR) => - vTR.metric.instance === instance && vTR.metric.device === deviceName, - )?.values; - const volumeLatencyWrite = volumeStats?.volumeLatencyWrite?.find( - (vL) => vL.metric.instance === instance && vL.metric.device === deviceName, - )?.values; - const volumeLatencyRead = volumeStats?.volumeLatencyRead?.find( - (vL) => vL.metric.instance === instance && vL.metric.device === deviceName, - )?.values; - const volumeIOPSRead = volumeStats?.volumeIOPSRead?.find( - (vIOPSR) => - vIOPSR.metric.instance === instance && - vIOPSR.metric.device === deviceName, - )?.values; - const volumeIOPSWrite = volumeStats?.volumeIOPSWrite?.find( - (vIOPSW) => - vIOPSW.metric.instance === instance && - vIOPSW.metric.device === deviceName, - )?.values; - const volumeMetricGraphData = { - volumeUsage, - volumeThroughputWrite, - volumeThroughputRead, - volumeLatencyWrite, - volumeLatencyRead, - volumeIOPSRead, - volumeIOPSWrite, - queryStartingTime, - }; - /* ** Used to determine if a first loading has happened ** This allow us to check if we need to display EmptyState or not @@ -273,7 +232,8 @@ const VolumePageContent = (props) => { > { export const updateCurrentVolumeStatsAction = (payload) => { return { type: UPDATE_CURRENT_VOLUMESTATS, payload }; }; -export const fetchNodeStatsAction = () => { - return { type: FETCH_NODESTATS }; -}; -export const updateNodeStatsAction = (payload) => { - return { type: UPDATE_NODESTATS, payload }; -}; -export const refreshNodeStatsAction = () => { - return { type: REFRESH_NODESTATS }; -}; -export const stopRefreshNodeStatsAction = () => { - return { type: STOP_REFRESH_NODESTATS }; -}; export const updateNodeStatsFetchArgumentAction = (payload) => { return { type: UPDATE_NODESTATS_FETCH_ARG, payload }; }; @@ -319,8 +257,6 @@ export const updateNodeUNameInfoAction = (payload) => { }; // Selectors -export const isAlertRefreshing = (state) => - state.app.monitoring.alert.isRefreshing; export const isClusterRefreshing = (state) => state.app.monitoring.cluster.isRefreshing; export const isVolumeStatsRefreshing = (state) => @@ -329,17 +265,6 @@ export const isCurrentVolumeStatsRefresh = (state) => state.app.monitoring.volumeCurrentStats.isRefreshing; const volumeMetricsTimeSpan = (state) => state.app.monitoring.volumeStats.metricsTimeSpan; -const nodeMetricsTimeSpan = (state) => - state.app.monitoring.nodeStats.metricsTimeSpan; -export const isNodeStatsRefreshing = (state) => - state.app.monitoring.nodeStats.isRefreshing; -const instanceIPSelector = (state) => state.app.monitoring.nodeStats.instanceIP; -const controlPlaneInterfaceSelector = (state) => - state.app.monitoring.nodeStats.controlPlaneInterface; -const workloadPlaneInterfaceSelector = (state) => - state.app.monitoring.nodeStats.workloadPlaneInterface; -export const nodeMetricsShowAvgSelector = (state) => - state.app.monitoring.nodeStats.showAvg; // Sagas function getClusterQueryStatus(result) { @@ -689,347 +614,6 @@ export function* stopRefreshCurrentStats() { yield put(updateCurrentVolumeStatsAction({ isRefreshing: false })); } -export function* fetchNodeStats() { - const instanceIP = yield select(instanceIPSelector); - const controlPlaneInterface = yield select(controlPlaneInterfaceSelector); - const workloadPlaneInterface = yield select(workloadPlaneInterfaceSelector); - const showAvg = yield select(nodeMetricsShowAvgSelector); - - let cpuUsage = []; - let systemLoad = []; - let memory = []; - let iopsRead = []; - let iopsWrite = []; - let controlPlaneNetworkBandwidthIn = []; - let controlPlaneNetworkBandwidthOut = []; - let workloadPlaneNetworkBandwidthIn = []; - let workloadPlaneNetworkBandwidthOut = []; - let cpuUsageAvg = []; - let systemLoadAvg = []; - let memoryAvg = []; - let iopsReadAvg = []; - let iopsWriteAvg = []; - let controlPlaneNetworkBandwidthInAvg = []; - let controlPlaneNetworkBandwidthOutAvg = []; - let workloadPlaneNetworkBandwidthInAvg = []; - let workloadPlaneNetworkBandwidthOutAvg = []; - - let sampleDuration; - let sampleFrequency; - - const timeSpan = yield select(nodeMetricsTimeSpan); - if (timeSpan === LAST_TWENTY_FOUR_HOURS) { - sampleDuration = SAMPLE_DURATION_LAST_TWENTY_FOUR_HOURS; - sampleFrequency = SAMPLE_FREQUENCY_LAST_TWENTY_FOUR_HOURS; - } else if (timeSpan === LAST_SEVEN_DAYS) { - sampleDuration = SAMPLE_DURATION_LAST_SEVEN_DAYS; - sampleFrequency = SAMPLE_FREQUENCY_LAST_SEVEN_DAYS; - } else if (timeSpan === LAST_ONE_HOUR) { - sampleDuration = SAMPLE_DURATION_LAST_ONE_HOUR; - sampleFrequency = SAMPLE_FREQUENCY_LAST_ONE_HOUR; - } - const currentTime = new Date(); - const currentTimeISO = currentTime.toISOString(); // To query Prometheus the date should follow `RFC3339` format - const startingTimestamp = - Math.round(currentTime.getTime() / 1000) - sampleDuration; - const startingTimeISO = new Date(startingTimestamp * 1000).toISOString(); - - const cpuUsageQuery = `100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle",instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}[5m])) * 100)`; - const systemLoadQuery = `avg(node_load1{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}) / count(count(node_cpu_seconds_total{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}) by (cpu)) * 100`; - const memoryQuery = `sum(100 - ((node_memory_MemAvailable_bytes{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"} * 100) / node_memory_MemTotal_bytes{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}))`; - const iopsReadQuery = `sum(irate(node_disk_reads_completed_total{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}[5m])) by (instance)`; - const iopsWriteQuery = `sum(irate(node_disk_writes_completed_total{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}[5m])) by (instance)`; - const controlPlaneNetworkBandwidthInQuery = `sum(irate(node_network_receive_bytes_total{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}",device="${controlPlaneInterface}"}[5m])) * 0.000001`; - const controlPlaneNetworkBandwidthOutQuery = `sum(irate(node_network_transmit_bytes_total{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}",device="${controlPlaneInterface}"}[5m])) * 0.000001`; - const workloadPlaneNetworkBandwidthInQuery = `sum(irate(node_network_receive_bytes_total{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}",device="${workloadPlaneInterface}"}[5m])) * 0.000001`; - const workloadPlaneNetworkBandwidthOutQuery = `sum(irate(node_network_transmit_bytes_total{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}",device="${workloadPlaneInterface}"}[5m])) * 0.000001`; - - const cpuUsageAvgQuery = `avg(100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100))`; - const systemLoadAvgQuery = `avg(node_load1/count without(cpu, mode) (node_cpu_seconds_total{mode="idle"})) * 100`; - const memoryAvgQuery = `avg(100 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100)`; - const iopsWriteAvgQuery = `avg(sum(irate(node_disk_writes_completed_total[5m])) by (instance))`; - const iopsReadAvgQuery = `avg(sum(irate(node_disk_reads_completed_total[5m])) by (instance))`; - const controlPlaneNetworkBandwidthInAvgQuery = `avg(irate(node_network_receive_bytes_total{job=~"node-exporter",device=~"${controlPlaneInterface}"}[5m]))* 0.000001`; - const controlPlaneNetworkBandwidthOutAvgQuery = `avg(irate(node_network_transmit_bytes_total{job=~"node-exporter",device=~"${controlPlaneInterface}"}[5m]))* 0.000001`; - const workloadPlaneNetworkBandwidthInAvgQuery = `avg(irate(node_network_receive_bytes_total{job=~"node-exporter",device=~"${workloadPlaneInterface}"}[5m]))* 0.000001`; - const workloadPlaneNetworkBandwidthOutAvgQuery = `avg(irate(node_network_transmit_bytes_total{job=~"node-exporter",device=~"${workloadPlaneInterface}"}[5m]))* 0.000001`; - - // Make sure the props are ready before sending the requests. - if (instanceIP && controlPlaneInterface && workloadPlaneInterface) { - // Running Tasks In Parallel - const [ - cpuUsageQueryResult, - systemLoadQueryResult, - memoryQueryResult, - iopsReadQueryResult, - iopsWriteQueryResult, - controlPlaneNetworkBandwidthInQueryResult, - controlPlaneNetworkBandwidthOutQueryResult, - workloadPlaneNetworkBandwidthInQueryResult, - workloadPlaneNetworkBandwidthOutQueryResult, - ] = yield all([ - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - cpuUsageQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - systemLoadQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - memoryQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - iopsReadQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - iopsWriteQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - controlPlaneNetworkBandwidthInQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - controlPlaneNetworkBandwidthOutQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - workloadPlaneNetworkBandwidthInQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - workloadPlaneNetworkBandwidthOutQuery, - ), - ]); - - if (showAvg) { - const [ - cpuUsageAvgQueryResult, - systemLoadAvgQueryResult, - memoryAvgQueryResult, - iopsReadAvgQueryResult, - iopsWriteAvgQueryResult, - controlPlaneNetworkBandwidthInAvgQueryResult, - controlPlaneNetworkBandwidthOutAvgQueryResult, - workloadPlaneNetworkBandwidthInAvgQueryResult, - workloadPlaneNetworkBandwidthOutAvgQueryResult, - ] = yield all([ - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - cpuUsageAvgQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - systemLoadAvgQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - memoryAvgQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - iopsReadAvgQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - iopsWriteAvgQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - controlPlaneNetworkBandwidthInAvgQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - controlPlaneNetworkBandwidthOutAvgQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - workloadPlaneNetworkBandwidthInAvgQuery, - ), - call( - queryPrometheusRange, - startingTimeISO, - currentTimeISO, - sampleFrequency, - workloadPlaneNetworkBandwidthOutAvgQuery, - ), - ]); - - if (!cpuUsageAvgQueryResult.error) { - cpuUsageAvg = cpuUsageAvgQueryResult.data.result; - } - if (!systemLoadAvgQueryResult.error) { - systemLoadAvg = systemLoadAvgQueryResult.data.result; - } - if (!memoryAvgQueryResult.error) { - memoryAvg = memoryAvgQueryResult.data.result; - } - if (!iopsReadAvgQueryResult.error) { - iopsReadAvg = iopsReadAvgQueryResult.data.result; - } - if (!iopsWriteAvgQueryResult.error) { - iopsWriteAvg = iopsWriteAvgQueryResult.data.result; - } - if (!controlPlaneNetworkBandwidthInAvgQueryResult.error) { - controlPlaneNetworkBandwidthInAvg = - controlPlaneNetworkBandwidthInAvgQueryResult.data.result; - } - if (!controlPlaneNetworkBandwidthOutAvgQueryResult.error) { - controlPlaneNetworkBandwidthOutAvg = - controlPlaneNetworkBandwidthOutAvgQueryResult.data.result; - } - if (!workloadPlaneNetworkBandwidthInAvgQueryResult.error) { - workloadPlaneNetworkBandwidthInAvg = - workloadPlaneNetworkBandwidthInAvgQueryResult.data.result; - } - if (!workloadPlaneNetworkBandwidthOutAvgQueryResult.error) { - workloadPlaneNetworkBandwidthOutAvg = - workloadPlaneNetworkBandwidthOutAvgQueryResult.data.result; - } - } - // Running Average Tasks In Parallel - - if (!cpuUsageQueryResult.error) { - cpuUsage = cpuUsageQueryResult.data.result; - } - if (!systemLoadQueryResult.error) { - systemLoad = systemLoadQueryResult.data.result; - } - if (!memoryQueryResult.error) { - memory = memoryQueryResult.data.result; - } - if (!iopsReadQueryResult.error) { - iopsRead = iopsReadQueryResult.data.result; - } - if (!iopsWriteQueryResult.error) { - iopsWrite = iopsWriteQueryResult.data.result; - } - if (!controlPlaneNetworkBandwidthInQueryResult.error) { - controlPlaneNetworkBandwidthIn = - controlPlaneNetworkBandwidthInQueryResult.data.result; - } - if (!controlPlaneNetworkBandwidthOutQueryResult.error) { - controlPlaneNetworkBandwidthOut = - controlPlaneNetworkBandwidthOutQueryResult.data.result; - } - if (!workloadPlaneNetworkBandwidthInQueryResult.error) { - workloadPlaneNetworkBandwidthIn = - workloadPlaneNetworkBandwidthInQueryResult.data.result; - } - if (!workloadPlaneNetworkBandwidthOutQueryResult.error) { - workloadPlaneNetworkBandwidthOut = - workloadPlaneNetworkBandwidthOutQueryResult.data.result; - } - - const metrics = { - cpuUsage, - systemLoad, - memory, - iopsRead, - iopsWrite, - controlPlaneNetworkBandwidthIn, - controlPlaneNetworkBandwidthOut, - workloadPlaneNetworkBandwidthIn, - workloadPlaneNetworkBandwidthOut, - queryStartingTime: startingTimestamp, - }; - - const metricsAvg = { - cpuUsage: cpuUsageAvg, - systemLoad: systemLoadAvg, - memory: memoryAvg, - iopsRead: iopsReadAvg, - iopsWrite: iopsWriteAvg, - controlPlaneNetworkBandwidthIn: controlPlaneNetworkBandwidthInAvg, - controlPlaneNetworkBandwidthOut: controlPlaneNetworkBandwidthOutAvg, - workloadPlaneNetworkBandwidthIn: workloadPlaneNetworkBandwidthInAvg, - workloadPlaneNetworkBandwidthOut: workloadPlaneNetworkBandwidthOutAvg, - queryStartingTime: startingTimestamp, - }; - - yield put(updateNodeStatsAction({ metrics: metrics, metricsAvg })); - } -} - -// A long-running saga to handle the refresh, we should launch this saga as part of the root saga. -// Avoid starting it manually to make sure there is only one loop that exists. -export function* watchRefreshNodeStats() { - while (true) { - yield take(REFRESH_NODESTATS); - while (true) { - const fetchNodeStatsTask = yield fork(fetchNodeStats); - const { interrupt } = yield race({ - interrupt: take(STOP_REFRESH_NODESTATS), - // If the refresh period expires before we receive a halt, - // we can refresh the stats - requeue: delay(REFRESH_METRICS_GRAPH), - // whenever we change one of the parameters for "fetchNodeStats", - // it gets triggered again - update: take(UPDATE_NODESTATS_FETCH_ARG), - }); - if (interrupt) { - yield cancel(fetchNodeStatsTask); - break; - } - } - } -} - export function* fetchNodeUNameInfo() { const fetchNodeUNameInfoQuery = 'node_uname_info'; const result = yield call(queryPrometheus, fetchNodeUNameInfoQuery); @@ -1039,7 +623,6 @@ export function* fetchNodeUNameInfo() { } export function* monitoringSaga() { - yield fork(watchRefreshNodeStats); yield takeLatest(FETCH_VOLUMESTATS, fetchVolumeStats); yield takeEvery(REFRESH_VOLUMESTATS, refreshVolumeStats); yield takeEvery(STOP_REFRESH_VOLUMESTATS, stopRefreshVolumeStats); @@ -1050,6 +633,5 @@ export function* monitoringSaga() { yield takeEvery(REFRESH_ALERTS, refreshAlerts); yield takeEvery(STOP_REFRESH_ALERTS, stopRefreshAlerts); yield takeEvery(STOP_REFRESH_CLUSTER_STATUS, stopRefreshClusterStatus); - yield takeEvery(FETCH_NODESTATS, fetchNodeStats); yield takeEvery(FETCH_NODE_UNAME_INFO, fetchNodeUNameInfo); } diff --git a/ui/src/ducks/app/nodes.js b/ui/src/ducks/app/nodes.js index b0f55bc90f..9be78f1646 100644 --- a/ui/src/ducks/app/nodes.js +++ b/ui/src/ducks/app/nodes.js @@ -164,7 +164,12 @@ export type NodesState = { clusterVersion: string, isRefreshing: boolean, isLoading: boolean, - IPsInfo: any, // TODO: define the type of this + IPsInfo: { + [nodeName: string]: { + controlPlane?: { interface?: string, ip?: string }, + workloadPlane?: { interface?: string, ip?: string }, + }, + }, currentNodeObject: any, // TODO: define the type of this list: { name: string, diff --git a/ui/src/services/NodeUtils.js b/ui/src/services/NodeUtils.js index bb7f8ebe20..cf219bb8bb 100644 --- a/ui/src/services/NodeUtils.js +++ b/ui/src/services/NodeUtils.js @@ -10,13 +10,18 @@ import { API_STATUS_NOT_READY, API_STATUS_UNKNOWN, API_STATUS_DEPLOYING, + STATUS_HEALTH, } from '../constants'; import { compareHealth } from './utils'; import type { IPInterfaces } from './salt/api'; import type { RootState } from '../ducks/reducer'; import type { NodesState } from '../ducks/app/nodes'; import type { Brand } from '../services/api'; -import { getHealthStatus, filterAlerts, type Alert } from '../services/alertUtils'; +import { + getHealthStatus, + filterAlerts, + type Alert, +} from '../services/alertUtils'; const METALK8S_CONTROL_PLANE_IP = 'metalk8s:control_plane_ip'; const METALK8S_WORKLOAD_PLANE_IP = 'metalk8s:workload_plane_ip'; @@ -56,33 +61,35 @@ const IPsInfoSelector = (state) => state.app.nodes.IPsInfo; const nodesSelector = (state) => state.app.nodes.list; // Return the data used by the Node list table -export const getNodeListData = (alerts: Array, brand: Brand) => createTypedSelector( - ( - nodes: $PropertyType, - nodeIPsInfo: NodesState - ) => { - const mapped = - nodes.map((node) => { - const conditions = node.conditions; - const IPsInfo = nodeIPsInfo[node.name]; - let statusTextColor, health; +export const getNodeListData = (alerts: Array, brand: Brand) => + createTypedSelector( + (nodes: $PropertyType, nodeIPsInfo: NodesState) => { + const mapped = + nodes.map((node) => { + const conditions = node.conditions; + const IPsInfo = nodeIPsInfo[node.name]; + let statusTextColor, health; - const alertsNode = filterAlerts(alerts, { - alertname: NODE_ALERTS_GROUP, - instance: `${node.internalIP}:${PORT_NODE_EXPORTER}`, - }); + const alertsNode = filterAlerts(alerts, { + alertname: NODE_ALERTS_GROUP, + }).filter( + (alert) => + alert.labels.instance === + `${node.internalIP}:${PORT_NODE_EXPORTER}` || + alert.labels.node === node.name, + ); - const totalAlertsCounter = alertsNode.length; - const criticalAlertsCounter = alertsNode.filter( - (alert) => alert.labels.severity === STATUS_CRITICAL, - ).length; - const warningAlertsCounter = alertsNode.filter( - (alert) => alert.labels.severity === STATUS_WARNING, - ).length; + const totalAlertsCounter = alertsNode.length; + const criticalAlertsCounter = alertsNode.filter( + (alert) => alert.labels.severity === STATUS_CRITICAL, + ).length; + const warningAlertsCounter = alertsNode.filter( + (alert) => alert.labels.severity === STATUS_WARNING, + ).length; - health = getHealthStatus(alertsNode); - const computedStatus = []; - /* The rules of the color of the node status + health = getHealthStatus(alertsNode); + const computedStatus = []; + /* The rules of the color of the node status when status.conditions['Ready'] == True and all other conditions are false when status.conditions['Ready'] == True and some other conditions are true when status.conditions['Ready'] == False @@ -105,43 +112,48 @@ export const getNodeListData = (alerts: Array, brand: Brand) => createTyp } else if (node.status !== API_STATUS_READY) { statusTextColor = brand.statusCritical; computedStatus.push(API_STATUS_NOT_READY); - health = STATUS_NONE; + //If no alert is raised on the node but kubernetes + //report a non-ready node we set the node health to NONE + //else we set it to the highest alert status raised on the node. + if (health === STATUS_HEALTH) { + health = STATUS_NONE; + } } else { statusTextColor = brand.textSecondary; computedStatus.push(API_STATUS_UNKNOWN); health = STATUS_NONE; } - return { - // According to the design, the IPs of Control Plane and Workload Plane are in the same Cell with Name - name: { - name: node.name, - controlPlaneIP: IPsInfo?.controlPlane?.ip, - workloadPlaneIP: IPsInfo?.workloadPlane?.ip, - }, - status: { - status: node.status, - conditions: node.conditions, - statusTextColor, - computedStatus, - }, - roles: node.roles, - health: { - health, - totalAlertsCounter, - criticalAlertsCounter, - warningAlertsCounter, - }, - }; - }) || []; + return { + // According to the design, the IPs of Control Plane and Workload Plane are in the same Cell with Name + name: { + name: node.name, + controlPlaneIP: IPsInfo?.controlPlane?.ip, + workloadPlaneIP: IPsInfo?.workloadPlane?.ip, + }, + status: { + status: node.status, + conditions: node.conditions, + statusTextColor, + computedStatus, + }, + roles: node.roles, + health: { + health, + totalAlertsCounter, + criticalAlertsCounter, + warningAlertsCounter, + }, + }; + }) || []; - return mapped.sort((a, b) => - compareHealth(b.health.health, a.health.health), - ); - }, - nodesSelector, - IPsInfoSelector, -); + return mapped.sort((a, b) => + compareHealth(b.health.health, a.health.health), + ); + }, + nodesSelector, + IPsInfoSelector, + ); /* This function returns the IP and interface of Control Plane and Workload Plane for each Node diff --git a/ui/src/services/graphUtils.js b/ui/src/services/graphUtils.js index 7e2489c50c..bf30eeaac6 100644 --- a/ui/src/services/graphUtils.js +++ b/ui/src/services/graphUtils.js @@ -3,7 +3,8 @@ import type { PrometheusQueryResult, RangeMatrixResult, } from './prometheus/api'; - +import { lineColor1 } from '../constants'; +import { type Serie } from '@scality/core-ui/dist/components/linetemporalchart/LineTemporalChart.component'; export type FormattedChartNodePromRange = { date: Date, type: string, @@ -78,3 +79,123 @@ export const formatNodesThroughputPromRangeForChart = ( return null; }; + +export const getSingleResourceSerie = ( + result: PrometheusQueryResult, + resource: string, + resultAvg?: PrometheusQueryResult, +): Serie[] => { + const series = []; + + if (result && result.status === 'success') { + const matrixResult: RangeMatrixResult = result?.data?.result[0]; + const prometheusData = matrixResult?.values ?? []; + const singleSerie = { + data: prometheusData, + resource, + getTooltipLabel: (_, resource) => { + return resource; + }, + getLegendLabel: (_, resource) => { + return resource; + }, + isLineDashed: false, + color: lineColor1, + }; + series.push(singleSerie); + } + + if (resultAvg && resultAvg.status === 'success') { + const avgMatrixResult = resultAvg?.data?.result[0]; + const prometheusAvgData = avgMatrixResult?.values ?? []; + series.push({ + data: prometheusAvgData, + resource: 'Cluster Avg.', + getTooltipLabel: (_, resource) => { + return resource; + }, + getLegendLabel: (_, resource) => { + return resource; + }, + isLineDashed: true, + color: lineColor1, + }); + } + return series; +}; + +// get the series for symmetrical charts +export const getSeriesForSymmetricalChart = ( + resultAbove: PrometheusQueryResult, + resultBelow: PrometheusQueryResult, + resource: string, + metricPrefixAbove: string, + metricPrefixBelow: string, + resultAvgAbove?: PrometheusQueryResult, + resultAvgBelow?: PrometheusQueryResult, +): Serie[] => { + const series = []; + + if (resultAbove && resultAbove.status === 'success') { + const serieAbove = { + metricPrefix: metricPrefixAbove, + data: resultAbove?.data?.result[0]?.values || [], + resource, + getTooltipLabel: (metricPrefix, resource) => { + return `${resource}-${metricPrefix}`; + }, + color: lineColor1, + }; + series.push(serieAbove); + } + if (resultBelow && resultBelow.status === 'success') { + const serieBelow = { + metricPrefix: metricPrefixBelow, + data: resultBelow?.data?.result[0]?.values || [], + resource, + getTooltipLabel: (metricPrefix, resource) => { + return `${resource}-${metricPrefix}`; + }, + // For the legend, we display only two labels for the symmetrical chart: One is the `${node_name}`, the other is `Cluster Avg.` + getLegendLabel: (_, resource) => { + return `${resource}`; + }, + color: lineColor1, + }; + series.push(serieBelow); + } + + // show cluster average is activated + if (resultAvgAbove && resultAvgAbove.status === 'success') { + const serieAvgAbove = { + metricPrefix: metricPrefixAbove, + data: resultAvgAbove?.data?.result[0]?.values || [], + resource: 'Cluster Avg.', + getTooltipLabel: (metricPrefix, resource) => { + return `${resource}-${metricPrefix}`; + }, + getLegendLabel: (_, resource) => { + return `${resource}`; + }, + color: lineColor1, + isLineDashed: true, + }; + series.push(serieAvgAbove); + } + if (resultAvgBelow && resultAvgBelow.status === 'success') { + // the negative value + const serieAvgBelow = { + metricPrefix: metricPrefixBelow, + data: resultAvgBelow?.data?.result[0]?.values || [], + resource: 'Cluster Avg.', + getTooltipLabel: (metricPrefix, resource) => { + return `${resource}-${metricPrefix}`; + }, + color: lineColor1, + isLineDashed: true, + }; + + series.push(serieAvgBelow); + } + return series; +}; diff --git a/ui/src/services/platformlibrary/metrics.js b/ui/src/services/platformlibrary/metrics.js new file mode 100644 index 0000000000..6abfa835cb --- /dev/null +++ b/ui/src/services/platformlibrary/metrics.js @@ -0,0 +1,662 @@ +import { PORT_NODE_EXPORTER } from '../../constants'; +import { queryPromtheusMetrics } from '../prometheus/fetchMetrics'; +import type { NodesState } from '../../ducks/app/nodes'; + +export type TimeSpanProps = { + startingTimeISO: string, + currentTimeISO: string, + frequency: number, +}; + +const getPrometheusQuery = ( + queryKey: string[], + prometheusQuery: string, + { startingTimeISO, currentTimeISO, frequency }: TimeSpanProps, +): typeof useQuery => { + queryKey.push(startingTimeISO); + return { + queryKey, + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + encodeURIComponent(prometheusQuery), + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + }; +}; + +// the queries for the metrics +// TODO: we may want to merge useStartingTimeStamp() and useMetricsTimeSpan(), so the all props related to timespan will be returned in one object +export const getCPUUsageQuery = ( + instanceIP: string, + timespanProps: TimeSpanProps, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + const cpuUsagePrometheusQuery = `100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle",instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}[5m])) * 100)`; + + return { + queryKey: ['CpuUsage', instanceIP, startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + cpuUsagePrometheusQuery, + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: instanceIP !== '', + }; +}; + +export const getCPUUsageAvgQuery = ( + timespanProps: TimeSpanProps, + showAvg: boolean, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + + const cpuUsageAvgPrometheusQuery = `avg(100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100))`; + + return { + queryKey: ['CpuUsageAvg', startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + cpuUsageAvgPrometheusQuery, + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: showAvg, + }; +}; + +export const getSystemLoadQuery = ( + instanceIP: string, + timespanProps: TimeSpanProps, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + const systemLoadPrometheusQuery = `avg(node_load1{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}) / count(count(node_cpu_seconds_total{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}) by (cpu)) * 100`; + + return { + queryKey: ['SystemLoad', instanceIP, startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + systemLoadPrometheusQuery, + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + }; +}; + +export const getSystemLoadAvgQuery = ( + timespanProps: TimeSpanProps, + showAvg: boolean, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + const systemLoadAvgPrometheusQuery = `avg(node_load1/count without(cpu, mode) (node_cpu_seconds_total{mode="idle"})) * 100`; + + return { + queryKey: ['SystemLoadAvg', startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + systemLoadAvgPrometheusQuery, + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + }; +}; + +export const getMemoryQuery = ( + instanceIP: string, + timespanProps: TimeSpanProps, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + const memoryPrometheusQuery = `sum(100 - ((node_memory_MemAvailable_bytes{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"} * 100) / node_memory_MemTotal_bytes{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}))`; + + return { + queryKey: ['Memory', instanceIP, startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + memoryPrometheusQuery, + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + }; +}; + +export const getMemoryAvgQuery = ( + timespanProps: TimeSpanProps, + showAvg: boolean, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + + const memoryAvgPrometheusQuery = `avg(100 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100)`; + + return { + queryKey: ['MemoryAvg', startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + memoryAvgPrometheusQuery, + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: showAvg, + }; +}; + +const getPlaneBandWidthInPromQuery = ( + instanceIP: string, + planeInterface: string, +) => { + return `sum(irate(node_network_receive_bytes_total{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}",device="${planeInterface}"}[5m]))`; +}; + +const getPlaneBandWidthOutPromQuery = ( + instanceIP: string, + planeInterface: string, +) => { + return `sum(irate(node_network_transmit_bytes_total{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}",device="${planeInterface}"}[5m]))`; +}; + +export const getControlPlaneBandWidthInQuery = ( + instanceIP: string, + timespanProps: TimeSpanProps, + planeInterface: string, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + + return { + queryKey: ['ControlPlaneBandwidthIn', instanceIP, startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + getPlaneBandWidthInPromQuery(instanceIP, planeInterface), + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: planeInterface !== '', + }; +}; + +export const getControlPlaneBandWidthOutQuery = ( + instanceIP: string, + timespanProps: TimeSpanProps, + planeInterface: string, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + + return { + queryKey: ['ControlPlaneBandwidthOut', instanceIP, startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + getPlaneBandWidthOutPromQuery(instanceIP, planeInterface), + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: planeInterface !== '', + }; +}; + +export const getControlPlaneBandWidthAvgInQuery = ( + timespanProps: TimeSpanProps, + showAvg: boolean, + instanceIP: string, + nodesIPsInfo: $PropertyType, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + const nodesCPBandwidthInPrometheusQuery = []; + for (let nodeIPsInfo of Object.values(nodesIPsInfo)) { + const controlPlaneInterface = nodeIPsInfo?.controlPlane?.interface; + const instanceIP = nodeIPsInfo?.controlPlane?.ip; + + if (controlPlaneInterface) { + nodesCPBandwidthInPrometheusQuery.push( + getPlaneBandWidthInPromQuery(instanceIP, controlPlaneInterface), + ); + } + } + + const nodeCPBandwithInAvgPrometheusQuery = encodeURIComponent( + `${nodesCPBandwidthInPrometheusQuery.join('+')} / ${ + nodesCPBandwidthInPrometheusQuery.length + }`, + ); + + return { + queryKey: ['ControlPlaneBandwidthAvgIn', instanceIP, startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + nodeCPBandwithInAvgPrometheusQuery, + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: showAvg, + }; +}; + +export const getControlPlaneBandWidthAvgOutQuery = ( + timespanProps: TimeSpanProps, + showAvg: boolean, + instanceIP: string, + nodesIPsInfo: $PropertyType, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + const nodesCPBandwidthOutPrometheusQuery = []; + + for (let nodeIPsInfo of Object.values(nodesIPsInfo)) { + const controlPlaneInterface = nodeIPsInfo?.controlPlane?.interface; + const instanceIP = nodeIPsInfo?.controlPlane?.ip; + + if (controlPlaneInterface) { + nodesCPBandwidthOutPrometheusQuery.push( + getPlaneBandWidthOutPromQuery(instanceIP, controlPlaneInterface), + ); + } + } + + const nodeCPBandwithAvgOutPrometheusQuery = encodeURIComponent( + `${nodesCPBandwidthOutPrometheusQuery.join('+')} / ${ + nodesCPBandwidthOutPrometheusQuery.length + }`, + ); + + return { + queryKey: ['ControlPlaneBandwidthAvgOut', instanceIP, startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + nodeCPBandwithAvgOutPrometheusQuery, + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: showAvg, + }; +}; + +export const getWorkloadPlaneBandWidthInQuery = ( + instanceIP: string, + timespanProps: TimeSpanProps, + planeInterface: string, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + + return { + queryKey: ['WorkloadPlaneBandwidthIn', instanceIP, startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + getPlaneBandWidthInPromQuery(instanceIP, planeInterface), + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: planeInterface !== '', + }; +}; + +export const getWorkloadPlaneBandWidthOutQuery = ( + instanceIP: string, + timespanProps: TimeSpanProps, + planeInterface: string, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + + return { + queryKey: ['WorkloadPlaneBandwidthOut', instanceIP, startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + getPlaneBandWidthOutPromQuery(instanceIP, planeInterface), + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: planeInterface !== '', + }; +}; + +export const getWorkloadPlaneBandWidthAvgInQuery = ( + timespanProps: TimeSpanProps, + showAvg: boolean, + instanceIP: string, + nodesIPsInfo: $PropertyType, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + const nodesWPBandwidthInPrometheusQuery = []; + for (let nodeIPsInfo of Object.values(nodesIPsInfo)) { + const instanceIP = nodeIPsInfo?.controlPlane?.ip; + const workloadPlaneInterface = nodeIPsInfo?.workloadPlane?.interface; + + if (workloadPlaneInterface) { + nodesWPBandwidthInPrometheusQuery.push( + getPlaneBandWidthInPromQuery(instanceIP, workloadPlaneInterface), + ); + } + } + + const nodeWPBandwithInAvgPrometheusQuery = encodeURIComponent( + `${nodesWPBandwidthInPrometheusQuery.join('+')} / ${ + nodesWPBandwidthInPrometheusQuery.length + }`, + ); + + return { + queryKey: ['WorkloadPlaneBandwidthAvgIn', instanceIP, startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + nodeWPBandwithInAvgPrometheusQuery, + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: showAvg, + }; +}; + +export const getWorkloadPlaneBandWidthAvgOutQuery = ( + timespanProps: TimeSpanProps, + showAvg: boolean, + instanceIP: string, + nodesIPsInfo: $PropertyType, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + const nodesWPBandwidthOutPrometheusQuery = []; + + for (let nodeIPsInfo of Object.values(nodesIPsInfo)) { + const controlPlaneInterface = nodeIPsInfo?.controlPlane?.interface; + const instanceIP = nodeIPsInfo?.controlPlane?.ip; + + if (controlPlaneInterface) { + nodesWPBandwidthOutPrometheusQuery.push( + getPlaneBandWidthOutPromQuery(instanceIP, controlPlaneInterface), + ); + } + } + + const nodeWPBandwithAvgOutPrometheusQuery = encodeURIComponent( + `${nodesWPBandwidthOutPrometheusQuery.join('+')} / ${ + nodesWPBandwidthOutPrometheusQuery.length + }`, + ); + + return { + queryKey: ['WorkloadPlaneBandwidthAvgOut', instanceIP, startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + nodeWPBandwithAvgOutPrometheusQuery, + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: showAvg, + }; +}; + +export const getIOPSWriteQuery = ( + instanceIP: string, + timespanProps: TimeSpanProps, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + const iopsWritePrometheusQuery = `sum(irate(node_disk_writes_completed_total{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}[5m])) by (instance)`; + + return { + queryKey: ['iopsWrite', instanceIP, startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + iopsWritePrometheusQuery, + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + }; +}; + +export const getIOPSReadQuery = ( + instanceIP: string, + timespanProps: TimeSpanProps, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + const iopsReadPrometheusQuery = `sum(irate(node_disk_reads_completed_total{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}[5m])) by (instance)`; + + return { + queryKey: ['iopsRead', instanceIP, startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + iopsReadPrometheusQuery, + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + }; +}; + +// IOPS Write Average (+) +export const getIOPSWriteAvgQuery = ( + timespanProps: TimeSpanProps, + showAvg: boolean, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + const iopsWriteAvgPrometheusQuery = `avg(sum(irate(node_disk_writes_completed_total[5m])) by (instance))`; + + return { + queryKey: ['iopsWriteAvg', startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + iopsWriteAvgPrometheusQuery, + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: showAvg, + }; +}; + +// IOPS Read Average (-) +export const getIOPSReadAvgQuery = ( + timespanProps: TimeSpanProps, + showAvg: boolean, +) => { + const { startingTimeISO, currentTimeISO, frequency } = timespanProps; + const iopsReadAvgPrometheusQuery = `avg(sum(irate(node_disk_reads_completed_total[5m])) by (instance))`; + + return { + queryKey: ['iopsReadAvg', startingTimeISO], + queryFn: () => { + return queryPromtheusMetrics( + frequency, + startingTimeISO, + currentTimeISO, + iopsReadAvgPrometheusQuery, + ); + }, + refetchOnMount: false, + refetchOnWindowFocus: false, + enabled: showAvg, + }; +}; + +export const getVolumeUsageQuery = ( + pvcName: string, + namespace: string, + timespanProps: TimeSpanProps, +): typeof useQuery => { + const prometheusFilters = `{namespace="${namespace}",persistentvolumeclaim="${pvcName}"}`; + const volumeUsageQuery = `kubelet_volume_stats_used_bytes${prometheusFilters} / kubelet_volume_stats_capacity_bytes${prometheusFilters} * 100`; + return getPrometheusQuery( + ['volumeUsage', pvcName, namespace], + volumeUsageQuery, + timespanProps, + ); +}; + +const getNodeDevicePrometheusFilter = ( + instanceIp: string, + deviceName: string, +) => { + return `{instance="${instanceIp}:${PORT_NODE_EXPORTER}",device="${deviceName}"}`; +}; + +export const getVolumeThroughputReadQuery = ( + instanceIp: string, + deviceName: string, + timespanProps: TimeSpanProps, +): typeof useQuery => { + const prometheusFilters = getNodeDevicePrometheusFilter( + instanceIp, + deviceName, + ); + const volumeThroughputReadQuery = `sum(irate(node_disk_read_bytes_total${prometheusFilters}[1m]))`; + return getPrometheusQuery( + ['volumeThroughputRead', instanceIp, deviceName], + volumeThroughputReadQuery, + timespanProps, + ); +}; + +export const getVolumeThroughputWriteQuery = ( + instanceIp: string, + deviceName: string, + timespanProps: TimeSpanProps, +): typeof useQuery => { + const prometheusFilters = getNodeDevicePrometheusFilter( + instanceIp, + deviceName, + ); + const volumeThroughputWriteQuery = `sum(irate(node_disk_written_bytes_total${prometheusFilters}[1m]))`; + return getPrometheusQuery( + ['volumeThroughputWrite', instanceIp, deviceName], + volumeThroughputWriteQuery, + timespanProps, + ); +}; + +export const getVolumeIOPSReadQuery = ( + instanceIp: string, + deviceName: string, + timespanProps: TimeSpanProps, +): typeof useQuery => { + const prometheusFilters = getNodeDevicePrometheusFilter( + instanceIp, + deviceName, + ); + const volumeIOPSReadQuery = `sum(irate(node_disk_reads_completed_total${prometheusFilters}[5m]))`; + return getPrometheusQuery( + ['volumeIOPSRead', instanceIp, deviceName], + volumeIOPSReadQuery, + timespanProps, + ); +}; + +export const getVolumeIOPSWriteQuery = ( + instanceIp: string, + deviceName: string, + timespanProps: TimeSpanProps, +): typeof useQuery => { + const prometheusFilters = getNodeDevicePrometheusFilter( + instanceIp, + deviceName, + ); + const volumeIOPSWriteQuery = `sum(irate(node_disk_writes_completed_total${prometheusFilters}[5m]))`; + return getPrometheusQuery( + ['volumeIOPSWrite', instanceIp, deviceName], + volumeIOPSWriteQuery, + timespanProps, + ); +}; + +export const getVolumeLatencyWriteQuery = ( + instanceIp: string, + deviceName: string, + timespanProps: TimeSpanProps, +): typeof useQuery => { + const prometheusFilters = getNodeDevicePrometheusFilter( + instanceIp, + deviceName, + ); + const volumeLatencyWriteQuery = `sum( + irate(node_disk_write_time_seconds_total${prometheusFilters}[5m]) / + (irate(node_disk_writes_completed_total${prometheusFilters}[5m]) > 0) or + irate(node_disk_write_time_seconds_total${prometheusFilters}[5m]) > bool 0) * 1000000`; + return getPrometheusQuery( + ['volumeLatencyWrite', instanceIp, deviceName], + volumeLatencyWriteQuery, + timespanProps, + ); +}; + +export const getVolumeLatencyReadQuery = ( + instanceIp: string, + deviceName: string, + timespanProps: TimeSpanProps, +): typeof useQuery => { + const prometheusFilters = getNodeDevicePrometheusFilter( + instanceIp, + deviceName, + ); + const volumeLatencyReadQuery = `sum( + irate(node_disk_read_time_seconds_total${prometheusFilters}[5m]) / + (irate(node_disk_reads_completed_total${prometheusFilters}[5m]) > 0) or + irate(node_disk_read_time_seconds_total${prometheusFilters}[5m]) > bool 0) * 1000000`; + return getPrometheusQuery( + ['volumeLatencyRead', instanceIp, deviceName], + volumeLatencyReadQuery, + timespanProps, + ); +}; diff --git a/ui/src/services/prometheus/fetchMetrics.js b/ui/src/services/prometheus/fetchMetrics.js index f7bed73be2..ccb4d4aeb5 100644 --- a/ui/src/services/prometheus/fetchMetrics.js +++ b/ui/src/services/prometheus/fetchMetrics.js @@ -33,6 +33,7 @@ export function queryNodeFSSize( }); } +//TODO: To be removed const getMetricsTimeValues = ( timeSpan: MetricsTimeSpan, ): { @@ -57,11 +58,8 @@ export function queryNodeCPUMetrics( timeSpan: MetricsTimeSpan, ): Promise { const cpuUsageQuery = `100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle",instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}[5m])) * 100)`; - const { - startingTimeISO, - currentTimeISO, - sampleFrequency, - } = getMetricsTimeValues(timeSpan); + const { startingTimeISO, currentTimeISO, sampleFrequency } = + getMetricsTimeValues(timeSpan); return queryPrometheusRange( startingTimeISO, @@ -81,11 +79,8 @@ export function queryNodeMemoryMetrics( timeSpan: MetricsTimeSpan, ): ?Promise { const memoryQuery = `sum(100 - ((node_memory_MemAvailable_bytes{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"} * 100) / node_memory_MemTotal_bytes{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}))`; - const { - startingTimeISO, - currentTimeISO, - sampleFrequency, - } = getMetricsTimeValues(timeSpan); + const { startingTimeISO, currentTimeISO, sampleFrequency } = + getMetricsTimeValues(timeSpan); return queryPrometheusRange( startingTimeISO, currentTimeISO, @@ -104,11 +99,8 @@ export function queryNodeLoadMetrics( timeSpan: MetricsTimeSpan, ): ?Promise { const systemLoadQuery = `avg(node_load1{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}) / count(count(node_cpu_seconds_total{instance=~"${instanceIP}:${PORT_NODE_EXPORTER}"}) by (cpu)) * 100`; - const { - startingTimeISO, - currentTimeISO, - sampleFrequency, - } = getMetricsTimeValues(timeSpan); + const { startingTimeISO, currentTimeISO, sampleFrequency } = + getMetricsTimeValues(timeSpan); return queryPrometheusRange( startingTimeISO, @@ -127,11 +119,8 @@ export function queryThroughputRead( timeSpan: MetricsTimeSpan, ): Promise { const nodeThroughputReadQuery = `sum(sum(irate(node_disk_read_bytes_total[1m])) by (instance, device) * 0.000001) by(instance)`; - const { - startingTimeISO, - currentTimeISO, - sampleFrequency, - } = getMetricsTimeValues(timeSpan); + const { startingTimeISO, currentTimeISO, sampleFrequency } = + getMetricsTimeValues(timeSpan); return queryPrometheusRange( startingTimeISO, @@ -150,17 +139,33 @@ export function queryThroughputWrite( timeSpan: MetricsTimeSpan, ): Promise { const nodeThroughputWriteQuery = `sum(sum(irate(node_disk_written_bytes_total[1m])) by (instance, device) * 0.000001)by(instance)`; - const { + const { startingTimeISO, currentTimeISO, sampleFrequency } = + getMetricsTimeValues(timeSpan); + + return queryPrometheusRange( startingTimeISO, currentTimeISO, sampleFrequency, - } = getMetricsTimeValues(timeSpan); + nodeThroughputWriteQuery, + )?.then((resolve) => { + if (resolve.error) { + throw resolve.error; + } + return resolve; + }); +} +export function queryPromtheusMetrics( + frequency: number, + startingTimeISO: string, + currentTimeISO: string, + promQuery: string, +): PrometheusQueryResult { return queryPrometheusRange( startingTimeISO, currentTimeISO, - sampleFrequency, - nodeThroughputWriteQuery, + frequency, + promQuery, )?.then((resolve) => { if (resolve.error) { throw resolve.error; diff --git a/ui/src/services/utils.test.js b/ui/src/services/utils.test.js index 54625d6118..b695b36f6e 100644 --- a/ui/src/services/utils.test.js +++ b/ui/src/services/utils.test.js @@ -1,20 +1,11 @@ -import { renderHook, act } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react-hooks'; import { sortCapacity, - addMissingDataPoint, fromMilliSectoAge, useTableSortURLSync, linuxDrivesNamingIncrement, formatDateToMid1, } from './utils'; -import { MetricsTimeSpanProvider, useMetricsTimeSpan } from '../hooks'; -import { - QUERY_LAST_ONE_HOUR, - QUERY_LAST_SEVEN_DAYS, - SAMPLE_DURATION_LAST_ONE_HOUR, - SAMPLE_DURATION_LAST_SEVEN_DAYS, - SAMPLE_DURATION_LAST_TWENTY_FOUR_HOURS, -} from '../constants'; const testcases = [ { storageCapacity: '1Ki' }, @@ -119,118 +110,6 @@ it('test the sort with a custom sortBy', () => { ]); }); -// test for addMissingDataPoint function -const originalValue = [ - [0, 0], - [1, 1], - [2, 2], - [3, 3], - [4, 4], - [5, 5], - [6, 6], - [8, 8], - [9, 9], - [10, 10], -]; -const startingTimeStamp = 0; -const sampleDuration = 11; -const sampleFrequency = 1; -const newValues = [ - [0, 0], - [1, 1], - [2, 2], - [3, 3], - [4, 4], - [5, 5], - [6, 6], - [7, null], - [8, 8], - [9, 9], - [10, 10], -]; -it('should add missing data point with null', () => { - const result = addMissingDataPoint( - originalValue, - startingTimeStamp, - sampleDuration, - sampleFrequency, - ); - expect(result).toEqual(newValues); -}); - -it('should return an empty array when the original dataset is empty', () => { - const result = addMissingDataPoint( - [], - startingTimeStamp, - sampleDuration, - sampleFrequency, - ); - expect(result).toEqual([]); -}); - -it('should return an empty array when the starting timestamp is undefined', () => { - const result = addMissingDataPoint( - originalValue, - undefined, - sampleDuration, - sampleFrequency, - ); - expect(result).toEqual([]); -}); - -it('should return an empty array when sample duration is less than or equal to zero', () => { - const result = addMissingDataPoint( - originalValue, - startingTimeStamp, - 0, - sampleFrequency, - ); - expect(result).toEqual([]); -}); - -it('should return an empty array when sample frequency is less than or equal to zero', () => { - const result = addMissingDataPoint( - originalValue, - startingTimeStamp, - sampleDuration, - -1, - ); - expect(result).toEqual([]); -}); - -it('should return an empty array when sample frequency is undefined', () => { - const result = addMissingDataPoint( - originalValue, - startingTimeStamp, - sampleDuration, - undefined, - ); - expect(result).toEqual([]); -}); - -const originalValueWithAllZero = [ - [0, 0], - [1, 0], - [2, 0], - [3, 0], - [4, 0], - [5, 0], - [6, 0], - [7, 0], - [8, 0], - [9, 0], - [10, 0], -]; -it('should return all zero when the original dataset is all zero', () => { - const result = addMissingDataPoint( - originalValueWithAllZero, - startingTimeStamp, - sampleDuration, - sampleFrequency, - ); - expect(result).toEqual(originalValueWithAllZero); -}); - // test for fromMilliSectoAge it('should return undefined if {milliSecTime} is zero or negative number', () => { const result = fromMilliSectoAge(0); @@ -268,62 +147,6 @@ jest.mock('react-router-dom', () => { }; }); -describe('useMetricsTimeSpan hook', () => { - it('should render properly with the provider', () => { - const wrapper = ({ children }) => ( - {children} - ); - - const { result } = renderHook(() => useMetricsTimeSpan(), { wrapper }); - expect(result.error).not.toEqual( - Error( - "useMetricsTimeSpan hook can't be use outside ", - ), - ); - }); - - it('should throw an error if no provider', () => { - const { result } = renderHook(() => useMetricsTimeSpan()); - expect(result.error).toEqual( - Error( - "useMetricsTimeSpan hook can't be use outside ", - ), - ); - }); - - it('setter/getter should set/get context value', () => { - const wrapper = ({ children }) => ( - {children} - ); - const { result } = renderHook(() => useMetricsTimeSpan(), { wrapper }); - expect(typeof result.current[1]).toBe('function'); - expect(result.current[0]).toEqual(SAMPLE_DURATION_LAST_TWENTY_FOUR_HOURS); - act(() => result.current[1](2000)); - expect(result.current[0]).toEqual(2000); - }); - - it('should modify urlQuery if set context value', () => { - const wrapper = ({ children }) => ( - {children} - ); - jest - .spyOn(URLSearchParams.prototype, 'get') - .mockReturnValue(QUERY_LAST_SEVEN_DAYS); - const metricHook = renderHook(() => useMetricsTimeSpan(), { - wrapper, - }); - expect(metricHook.result.current[0]).toBe(SAMPLE_DURATION_LAST_SEVEN_DAYS); - - jest - .spyOn(URLSearchParams.prototype, 'get') - .mockReturnValue(QUERY_LAST_ONE_HOUR); - const metricHook2 = renderHook(() => useMetricsTimeSpan(), { - wrapper, - }); - expect(metricHook2.result.current[0]).toBe(SAMPLE_DURATION_LAST_ONE_HOUR); - }); -}); - describe('useTableSortURLSync hook', () => { it('should not set anything in the URL if data is not ready', () => { renderHook(() => useTableSortURLSync('name', false, [], 'key')); diff --git a/ui/src/translations/en.json b/ui/src/translations/en.json index c60ba01fe8..6a80453f08 100644 --- a/ui/src/translations/en.json +++ b/ui/src/translations/en.json @@ -176,5 +176,6 @@ "observability": "Observability", "core": "Core", "view_details": "View details", - "start": "Start" + "start": "Start", + "no_data_available_for_metrics": "No data available for displaying the Metrics" } diff --git a/ui/src/translations/fr.json b/ui/src/translations/fr.json index 751d006841..e983852251 100644 --- a/ui/src/translations/fr.json +++ b/ui/src/translations/fr.json @@ -176,5 +176,6 @@ "observability": "Observabilité", "core": "Noyau", "view_details": "Voir détails", - "start": "Début" + "start": "Début", + "no_data_available_for_metrics": "Aucune donnée disponible pour afficher les Métriques" } diff --git a/ui/webpack.common.js b/ui/webpack.common.js index 6660e8a586..50042e70a1 100644 --- a/ui/webpack.common.js +++ b/ui/webpack.common.js @@ -28,8 +28,8 @@ module.exports = (env) => ({ performance: { hints: 'error', // ~732 KiB for production - // ~4.06 MiB for development because flow increase the size of assets. - maxAssetSize: process.env.NODE_ENV === 'production' ? 750000 : 5000000, + // ~10.5 MiB for development because flow increase the size of assets. + maxAssetSize: process.env.NODE_ENV === 'production' ? 750000 : 11000000, assetFilter: (assetFilename) => { return ( !assetFilename.endsWith('.map.gz') && assetFilename.endsWith('.gz')