From bf1d3f3a271ac5ef191a34dc75e06e7a5e772d6a Mon Sep 17 00:00:00 2001 From: Yury Kovalev <8366110+kovayur@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:06:03 +0100 Subject: [PATCH] ROX-27176: Remove observability dead code from acs-fleet-manager (#2103) --- .github/workflows/ci.yaml | 2 +- .pre-commit-config.yaml | 2 +- .secrets.baseline | 22 +- Makefile | 60 - cmd/fleet-manager/main.go | 1 - dev/env/defaults/00-defaults.env | 1 - dev/env/scripts/lib.sh | 2 - docs/development/populating-configuration.md | 8 - docs/legacy/feature-flags.md | 23 - docs/legacy/observability/README.md | 35 +- ...figuring-metrics-federate-scrape-target.md | 61 - ...nning-local-observatorium-token-refresh.md | 19 - go.mod | 1 - go.sum | 6 - internal/dinosaur/constants/central.go | 12 - .../api/dbapi/data_plane_cluster_status.go | 9 - .../dinosaur/pkg/api/private/api/openapi.yaml | 38 +- .../model_dataplane_cluster_agent_config.go | 3 +- ...del_dataplane_cluster_agent_config_spec.go | 16 - ...cluster_agent_config_spec_observability.go | 19 - .../dinosaur/pkg/api/public/api/openapi.yaml | 192 --- .../dinosaur/pkg/api/public/api_default.go | 364 ------ .../dinosaur/pkg/environments/development.go | 61 +- .../dinosaur/pkg/environments/integration.go | 5 +- internal/dinosaur/pkg/generated/bindata.go | 4 +- internal/dinosaur/pkg/handlers/metrics.go | 163 --- .../pkg/metrics/federate_user_metrics.go | 82 -- .../pkg/metrics/federate_user_metrics_test.go | 110 -- .../presenters/data_plane_cluster_status.go | 14 +- internal/dinosaur/pkg/presenters/metric.go | 107 -- internal/dinosaur/pkg/routes/route_loader.go | 23 - .../pkg/services/data_plane_cluster.go | 12 +- .../pkg/services/observatorium_service.go | 56 - .../pkg/services/observatorium_service_moq.go | 145 --- internal/dinosaur/pkg/workers/clusters_mgr.go | 119 +- .../provisioning_dinosaurs_mgr.go | 4 +- internal/dinosaur/providers.go | 3 - internal/dinosaur/test/helper.go | 5 +- openapi/fleet-manager-private.yaml | 14 - openapi/fleet-manager.yaml | 124 -- pkg/api/observability/v1/types.go | 78 -- pkg/client/observatorium/api.go | 146 --- pkg/client/observatorium/api_mock.go | 177 --- pkg/client/observatorium/api_test.go | 86 -- pkg/client/observatorium/client.go | 229 ---- pkg/client/observatorium/config.go | 16 - pkg/client/observatorium/mocks/api_moq.go | 1126 ----------------- pkg/client/observatorium/mocks/gen.go | 8 - .../observatorium/observability_config.go | 129 -- pkg/client/observatorium/types.go | 52 - pkg/metrics/metrics.go | 84 -- pkg/providers/core.go | 2 - templates/observatorium-token-refresher.yml | 123 -- templates/secrets-template.yml | 34 - templates/service-template.yml | 84 -- 55 files changed, 66 insertions(+), 4255 deletions(-) delete mode 100644 docs/legacy/observability/configuring-metrics-federate-scrape-target.md delete mode 100644 docs/legacy/observability/running-local-observatorium-token-refresh.md delete mode 100644 internal/dinosaur/pkg/api/private/model_dataplane_cluster_agent_config_spec.go delete mode 100644 internal/dinosaur/pkg/api/private/model_dataplane_cluster_agent_config_spec_observability.go delete mode 100644 internal/dinosaur/pkg/handlers/metrics.go delete mode 100644 internal/dinosaur/pkg/metrics/federate_user_metrics.go delete mode 100644 internal/dinosaur/pkg/metrics/federate_user_metrics_test.go delete mode 100644 internal/dinosaur/pkg/presenters/metric.go delete mode 100644 internal/dinosaur/pkg/services/observatorium_service.go delete mode 100644 internal/dinosaur/pkg/services/observatorium_service_moq.go delete mode 100644 pkg/api/observability/v1/types.go delete mode 100644 pkg/client/observatorium/api.go delete mode 100644 pkg/client/observatorium/api_mock.go delete mode 100644 pkg/client/observatorium/api_test.go delete mode 100644 pkg/client/observatorium/client.go delete mode 100644 pkg/client/observatorium/config.go delete mode 100644 pkg/client/observatorium/mocks/api_moq.go delete mode 100644 pkg/client/observatorium/mocks/gen.go delete mode 100644 pkg/client/observatorium/observability_config.go delete mode 100644 pkg/client/observatorium/types.go delete mode 100644 templates/observatorium-token-refresher.yml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 44be794144..5a0f3d5157 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -105,7 +105,7 @@ jobs: git diff --exit-code - name: Setup tests secrets run: | - make ocm/setup aws/setup redhatsso/setup centralcert/setup observatorium/setup secrets/touch + make ocm/setup aws/setup redhatsso/setup centralcert/setup secrets/touch - name: Run Migration Script run: make db/migrate - name: Verify & Test diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 35b1d74e7c..87fdc1f428 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,7 @@ repos: require_serial: true pass_filenames: true stages: [push, manual] - files: '(openapi/.*|pkg/workers/worker_interface.go|pkg/client/ocm/id.go|pkg/client/aws/client.go|pkg/client/ocm/client.go|pkg/client/iam/client.go|pkg/services/authorization/authorization.go|pkg/services/sso/iam_service.go|pkg/client/redhatsso/client.go|pkg/auth/auth_agent_service.go|internal/dinosaur/pkg/services/observatorium_service.go|internal/dinosaur/pkg/services/cluster_placement_strategy.go|internal/dinosaur/pkg/services/cloud_providers.go|internal/dinosaur/pkg/services/clusters.go|internal/dinosaur/pkg/services/quota.go|internal/dinosaur/pkg/services/fleetshard_operator_addon.go|internal/dinosaur/pkg/services/quota_service_factory.go|internal/dinosaur/pkg/clusters/cluster_builder.go|internal/dinosaur/pkg/clusters/provider.go|internal/dinosaur/pkg/services/dinosaur.go)' + files: '(openapi/.*|pkg/workers/worker_interface.go|pkg/client/ocm/id.go|pkg/client/aws/client.go|pkg/client/ocm/client.go|pkg/client/iam/client.go|pkg/services/authorization/authorization.go|pkg/services/sso/iam_service.go|pkg/client/redhatsso/client.go|pkg/auth/auth_agent_service.go|internal/dinosaur/pkg/services/cluster_placement_strategy.go|internal/dinosaur/pkg/services/cloud_providers.go|internal/dinosaur/pkg/services/clusters.go|internal/dinosaur/pkg/services/quota.go|internal/dinosaur/pkg/services/fleetshard_operator_addon.go|internal/dinosaur/pkg/services/quota_service_factory.go|internal/dinosaur/pkg/clusters/cluster_builder.go|internal/dinosaur/pkg/clusters/provider.go|internal/dinosaur/pkg/services/dinosaur.go)' - repo: https://github.com/Yelp/detect-secrets rev: v1.5.0 hooks: diff --git a/.secrets.baseline b/.secrets.baseline index 0b7f5a65e4..0a4175243b 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -268,7 +268,7 @@ "filename": "internal/dinosaur/pkg/api/public/api/openapi.yaml", "hashed_secret": "5b455797b93de5b6a19633ba22127c8a610f5c1b", "is_verified": false, - "line_number": 1535 + "line_number": 1343 } ], "internal/dinosaur/pkg/presenters/managedcentral.go": [ @@ -330,63 +330,63 @@ "filename": "templates/service-template.yml", "hashed_secret": "13032f402fed753c2248419ea4f69f99931f6dbc", "is_verified": false, - "line_number": 524 + "line_number": 471 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "30025f80f6e22cdafb85db387d50f90ea884576a", "is_verified": false, - "line_number": 524 + "line_number": 471 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "355f24fd038bcaf85617abdcaa64af51ed19bbcf", "is_verified": false, - "line_number": 524 + "line_number": 471 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "3d8a1dcd2c3c765ce35c9a9552d23273cc4ddace", "is_verified": false, - "line_number": 524 + "line_number": 471 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "4ac7b0522761eba972467942cd5cd7499dd2c361", "is_verified": false, - "line_number": 524 + "line_number": 471 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "7639ab2a6bcf2ea30a055a99468c9cd844d4c22a", "is_verified": false, - "line_number": 524 + "line_number": 471 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "b56360daf4793d2a74991a972b34d95bc00fb2da", "is_verified": false, - "line_number": 524 + "line_number": 471 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "c9a73ef9ee8ce9f38437227801c70bcc6740d1a1", "is_verified": false, - "line_number": 524 + "line_number": 471 }, { "type": "Secret Keyword", "filename": "templates/service-template.yml", "hashed_secret": "4e199b4a1c40b497a95fcd1cd896351733849949", "is_verified": false, - "line_number": 707, + "line_number": 654, "is_secret": false } ], @@ -416,5 +416,5 @@ } ] }, - "generated_at": "2024-10-17T08:34:41Z" + "generated_at": "2024-11-26T16:50:48Z" } diff --git a/Makefile b/Makefile index 2f68479be4..80da5bc969 100644 --- a/Makefile +++ b/Makefile @@ -215,8 +215,6 @@ help: @echo "make setup/git/hooks setup git hooks" @echo "make secrets/touch touch all required secret files" @echo "make centralcert/setup setup the central TLS certificate used for Managed Central Service" - @echo "make observatorium/setup setup observatorium secrets used by CI" - @echo "make observatorium/token-refresher/setup" setup a local observatorium token refresher @echo "make docker/login/internal login to an openshift cluster image registry" @echo "make image/push/internal push image to an openshift cluster image registry." @echo "make deploy/project deploy the service via templates to an openshift cluster" @@ -634,7 +632,6 @@ secrets/touch: secrets/central-tls.crt \ secrets/central-tls.key \ secrets/central.idp-client-secret \ - secrets/observability-config-access.token \ secrets/ocm-service.clientId \ secrets/ocm-service.clientSecret \ secrets/ocm-service.token \ @@ -675,31 +672,6 @@ centralcert/setup: @echo -n "$(CENTRAL_TLS_KEY)" > secrets/central-tls.key .PHONY:centralcert/setup -observatorium/setup: - @echo -n "$(OBSERVATORIUM_CONFIG_ACCESS_TOKEN)" > secrets/observability-config-access.token; - @echo -n "$(RHSSO_LOGS_CLIENT_ID)" > secrets/rhsso-logs.clientId; - @echo -n "$(RHSSO_LOGS_CLIENT_SECRET)" > secrets/rhsso-logs.clientSecret; - @echo -n "$(RHSSO_METRICS_CLIENT_ID)" > secrets/rhsso-metrics.clientId; - @echo -n "$(RHSSO_METRICS_CLIENT_SECRET)" > secrets/rhsso-metrics.clientSecret; -.PHONY:observatorium/setup - -observatorium/token-refresher/setup: PORT ?= 8085 -observatorium/token-refresher/setup: IMAGE_TAG ?= latest -observatorium/token-refresher/setup: ISSUER_URL ?= https://sso.redhat.com/auth/realms/redhat-external -observatorium/token-refresher/setup: OBSERVATORIUM_URL ?= https://observatorium-mst.api.stage.openshift.com/api/metrics/v1/manageddinosaur -observatorium/token-refresher/setup: - @$(DOCKER) run -d -p ${PORT}:${PORT} \ - --restart always \ - --name observatorium-token-refresher quay.io/rhoas/mk-token-refresher:${IMAGE_TAG} \ - /bin/token-refresher \ - --oidc.issuer-url="${ISSUER_URL}" \ - --url="${OBSERVATORIUM_URL}" \ - --oidc.client-id="${CLIENT_ID}" \ - --oidc.client-secret="${CLIENT_SECRET}" \ - --web.listen=":${PORT}" - @echo The Observatorium token refresher is now running on 'http://localhost:${PORT}' -.PHONY: observatorium/token-refresher/setup - # Setup dummy OCM_OFFLINE_TOKEN for integration testing ocm/setup: OCM_OFFLINE_TOKEN ?= "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" # pragma: allowlist secret ocm/setup: @@ -743,13 +715,6 @@ deploy/secrets: -p CENTRAL_IDP_CLIENT_SECRET="$(shell ([ -s './secrets/central.idp-client-secret' ] && [ -z '${CENTRAL_IDP_CLIENT_SECRET}' ]) && cat ./secrets/central.idp-client-secret || echo '${CENTRAL_IDP_CLIENT_SECRET}')" \ -p CENTRAL_TLS_CERT="$(shell ([ -s './secrets/central-tls.crt' ] && [ -z '${CENTRAL_TLS_CERT}' ]) && cat ./secrets/central-tls.crt || echo '${CENTRAL_TLS_CERT}')" \ -p CENTRAL_TLS_KEY="$(shell ([ -s './secrets/central-tls.key' ] && [ -z '${CENTRAL_TLS_KEY}' ]) && cat ./secrets/central-tls.key || echo '${CENTRAL_TLS_KEY}')" \ - -p OBSERVABILITY_CONFIG_ACCESS_TOKEN="$(shell ([ -s './secrets/observability-config-access.token' ] && [ -z '${OBSERVABILITY_CONFIG_ACCESS_TOKEN}' ]) && cat ./secrets/observability-config-access.token || echo '${OBSERVABILITY_CONFIG_ACCESS_TOKEN}')" \ - -p OBSERVABILITY_RHSSO_LOGS_CLIENT_ID="$(shell ([ -s './secrets/rhsso-logs.clientId' ] && [ -z '${OBSERVABILITY_RHSSO_LOGS_CLIENT_ID}' ]) && cat ./secrets/rhsso-logs.clientId || echo '${OBSERVABILITY_RHSSO_LOGS_CLIENT_ID}')" \ - -p OBSERVABILITY_RHSSO_LOGS_SECRET="$(shell ([ -s './secrets/rhsso-logs.clientSecret' ] && [ -z '${OBSERVABILITY_RHSSO_LOGS_SECRET}' ]) && cat ./secrets/rhsso-logs.clientSecret || echo '${OBSERVABILITY_RHSSO_LOGS_SECRET}')" \ - -p OBSERVABILITY_RHSSO_METRICS_CLIENT_ID="$(shell ([ -s './secrets/rhsso-metrics.clientId' ] && [ -z '${OBSERVABILITY_RHSSO_METRICS_CLIENT_ID}' ]) && cat ./secrets/rhsso-metrics.clientId || echo '${OBSERVABILITY_RHSSO_METRICS_CLIENT_ID}')" \ - -p OBSERVABILITY_RHSSO_METRICS_SECRET="$(shell ([ -s './secrets/rhsso-metrics.clientSecret' ] && [ -z '${OBSERVABILITY_RHSSO_METRICS_SECRET}' ]) && cat ./secrets/rhsso-metrics.clientSecret || echo '${OBSERVABILITY_RHSSO_METRICS_SECRET}')" \ - -p OBSERVABILITY_RHSSO_GRAFANA_CLIENT_ID="${OBSERVABILITY_RHSSO_GRAFANA_CLIENT_ID}" \ - -p OBSERVABILITY_RHSSO_GRAFANA_CLIENT_SECRET="${OBSERVABILITY_RHSSO_GRAFANA_CLIENT_SECRET}" \ | oc apply -f - -n $(NAMESPACE) .PHONY: deploy/secrets @@ -794,9 +759,6 @@ deploy/service: ENABLE_TERMS_ACCEPTANCE ?= "false" deploy/service: ENABLE_DENY_LIST ?= "false" deploy/service: ALLOW_EVALUATOR_INSTANCE ?= "true" deploy/service: QUOTA_TYPE ?= "quota-management-list" -deploy/service: OBSERVABILITY_CONFIG_REPO ?= "https://api.github.com/repos/bf2fc6cc711aee1a0c2a/observability-resources-mk/contents" -deploy/service: OBSERVABILITY_CONFIG_CHANNEL ?= "resources" -deploy/service: OBSERVABILITY_CONFIG_TAG ?= "main" deploy/service: DATAPLANE_CLUSTER_SCALING_TYPE ?= "manual" deploy/service: CENTRAL_IDP_ISSUER ?= "https://sso.stage.redhat.com/auth/realms/redhat-external" deploy/service: CENTRAL_IDP_CLIENT_ID ?= "rhacs-ms-dev" @@ -830,13 +792,6 @@ endif -p OCM_ADDON_SERVICE_URL="$(OCM_ADDON_SERVICE_URL)" \ -p AMS_URL="${AMS_URL}" \ -p SERVICE_PUBLIC_HOST_URL="https://$(shell oc get routes/fleet-manager -o jsonpath="{.spec.host}" -n $(NAMESPACE))" \ - -p OBSERVATORIUM_RHSSO_GATEWAY="${OBSERVATORIUM_RHSSO_GATEWAY}" \ - -p OBSERVATORIUM_RHSSO_REALM="${OBSERVATORIUM_RHSSO_REALM}" \ - -p OBSERVATORIUM_RHSSO_TENANT="${OBSERVATORIUM_RHSSO_TENANT}" \ - -p OBSERVATORIUM_RHSSO_AUTH_SERVER_URL="${OBSERVATORIUM_RHSSO_AUTH_SERVER_URL}" \ - -p OBSERVATORIUM_TOKEN_REFRESHER_URL="http://token-refresher.$(NAMESPACE).svc.cluster.local" \ - -p OBSERVABILITY_CONFIG_REPO="${OBSERVABILITY_CONFIG_REPO}" \ - -p OBSERVABILITY_CONFIG_TAG="${OBSERVABILITY_CONFIG_TAG}" \ -p ENABLE_TERMS_ACCEPTANCE="${ENABLE_TERMS_ACCEPTANCE}" \ -p ALLOW_EVALUATOR_INSTANCE="${ALLOW_EVALUATOR_INSTANCE}" \ -p QUOTA_TYPE="${QUOTA_TYPE}" \ @@ -861,7 +816,6 @@ endif # remove service deployments from an OpenShift cluster undeploy: FLEET_MANAGER_IMAGE ?= $(SHORT_IMAGE_REF) undeploy: - @-oc process -f ./templates/observatorium-token-refresher.yml --local | oc delete -f - -n $(NAMESPACE) @-oc process -f ./templates/db-template.yml --local | oc delete -f - -n $(NAMESPACE) @-oc process -f ./templates/secrets-template.yml --local | oc delete -f - -n $(NAMESPACE) @-oc process -f ./templates/route-template.yml --local | oc delete -f - -n $(NAMESPACE) @@ -871,20 +825,6 @@ undeploy: | oc delete -f - -n $(NAMESPACE) .PHONY: undeploy -# Deploys an Observatorium token refresher on an OpenShift cluster -deploy/token-refresher: ISSUER_URL ?= "https://sso.redhat.com/auth/realms/redhat-external" -deploy/token-refresher: OBSERVATORIUM_TOKEN_REFRESHER_IMAGE ?= "quay.io/rhoas/mk-token-refresher" -deploy/token-refresher: OBSERVATORIUM_TOKEN_REFRESHER_IMAGE_TAG ?= "latest" -deploy/token-refresher: OBSERVATORIUM_URL ?= "https://observatorium-mst.api.stage.openshift.com/api/metrics/v1/manageddinosaur" -deploy/token-refresher: - @-oc process -f ./templates/observatorium-token-refresher.yml \ - -p ISSUER_URL=${ISSUER_URL} \ - -p OBSERVATORIUM_URL=${OBSERVATORIUM_URL} \ - -p OBSERVATORIUM_TOKEN_REFRESHER_IMAGE=${OBSERVATORIUM_TOKEN_REFRESHER_IMAGE} \ - -p OBSERVATORIUM_TOKEN_REFRESHER_IMAGE_TAG=${OBSERVATORIUM_TOKEN_REFRESHER_IMAGE_TAG} \ - | oc apply -f - -n $(NAMESPACE) -.PHONY: deploy/token-refresher - # Deploys OpenShift ingress router on a k8s cluster deploy/openshift-router: ./scripts/openshift-router.sh deploy diff --git a/cmd/fleet-manager/main.go b/cmd/fleet-manager/main.go index 657c5b03d9..2b143637d9 100644 --- a/cmd/fleet-manager/main.go +++ b/cmd/fleet-manager/main.go @@ -49,7 +49,6 @@ func main() { // Unsupported CLI commands. Eventually some of them can be removed. // rootCmd.AddCommand(cluster.NewClusterCommand(env)) // rootCmd.AddCommand(cloudprovider.NewCloudProviderCommand(env)) - // rootCmd.AddCommand(observatorium.NewRunObservatoriumCommand(env)) // rootCmd.AddCommand(errors.NewErrorsCommand(env)) if err := rootCmd.Execute(); err != nil { diff --git a/dev/env/defaults/00-defaults.env b/dev/env/defaults/00-defaults.env index 1136af2c1a..83e2e4b49a 100644 --- a/dev/env/defaults/00-defaults.env +++ b/dev/env/defaults/00-defaults.env @@ -43,7 +43,6 @@ export OSD_IDP_SSO_CLIENT_ID_DEFAULT="" export OSD_IDP_SSO_CLIENT_SECRET_DEFAULT="" export ROUTE53_ACCESS_KEY_DEFAULT="" export ROUTE53_SECRET_ACCESS_KEY_DEFAULT="" -export OBSERVABILITY_CONFIG_ACCESS_TOKEN_DEFAULT="" export SPAWN_LOGGER_DEFAULT="false" export DUMP_LOGS_DEFAULT="false" export OPERATOR_SOURCE_DEFAULT="" diff --git a/dev/env/scripts/lib.sh b/dev/env/scripts/lib.sh index 863aedc2f0..a026f46827 100644 --- a/dev/env/scripts/lib.sh +++ b/dev/env/scripts/lib.sh @@ -107,7 +107,6 @@ init() { export OSD_IDP_SSO_CLIENT_SECRET=${OSD_IDP_SSO_CLIENT_SECRET:-$OSD_IDP_SSO_CLIENT_SECRET_DEFAULT} export ROUTE53_ACCESS_KEY=${ROUTE53_ACCESS_KEY:-$ROUTE53_ACCESS_KEY_DEFAULT} export ROUTE53_SECRET_ACCESS_KEY=${ROUTE53_SECRET_ACCESS_KEY:-$ROUTE53_SECRET_ACCESS_KEY_DEFAULT} - export OBSERVABILITY_CONFIG_ACCESS_TOKEN=${OBSERVABILITY_CONFIG_ACCESS_TOKEN:-$OBSERVABILITY_CONFIG_ACCESS_TOKEN_DEFAULT} export INHERIT_IMAGEPULLSECRETS=${INHERIT_IMAGEPULLSECRETS:-$INHERIT_IMAGEPULLSECRETS_DEFAULT} export SPAWN_LOGGER=${SPAWN_LOGGER:-$SPAWN_LOGGER_DEFAULT} export DUMP_LOGS=${DUMP_LOGS:-$DUMP_LOGS_DEFAULT} @@ -190,7 +189,6 @@ OSD_IDP_SSO_CLIENT_ID: ******** OSD_IDP_SSO_CLIENT_SECRET: ******** ROUTE53_ACCESS_KEY: ******** ROUTE53_SECRET_ACCESS_KEY: ******** -OBSERVABILITY_CONFIG_ACCESS_TOKEN: ******** INHERIT_IMAGEPULLSECRETS: ${INHERIT_IMAGEPULLSECRETS} SPAWN_LOGGER: ${SPAWN_LOGGER} DUMP_LOGS: ${DUMP_LOGS} diff --git a/docs/development/populating-configuration.md b/docs/development/populating-configuration.md index 85c16a3293..7cf5c81fb9 100644 --- a/docs/development/populating-configuration.md +++ b/docs/development/populating-configuration.md @@ -115,14 +115,6 @@ In the Data Plane cluster, the Central Operator and the FleetShard Deployments might reference container images that are located in authenticated container image registries. -## Setup the Observability stack secrets -See [Obsevability](./observability/README.md) to learn more about Observatorium and the observability stack. -The following command is used to setup the various secrets needed by the Observability stack. - -``` -make observatorium/setup -``` - ## Setup a custom TLS certificate for Central Host URLs When Fleet Manager creates Central instances, it can be configured to diff --git a/docs/legacy/feature-flags.md b/docs/legacy/feature-flags.md index 66b099fbaf..02146a4785 100644 --- a/docs/legacy/feature-flags.md +++ b/docs/legacy/feature-flags.md @@ -13,7 +13,6 @@ This lists the feature flags and their sub-configurations to enable/disable and - [Central](#central) - [IAM](#iam) - [Metrics Server](#metrics-server) - - [Observability](#observability) - [OpenShift Cluster Manager](#openshift-cluster-manager) - [Dataplane Cluster Management](#dataplane-cluster-management) - [Sentry](#sentry) @@ -76,28 +75,6 @@ This lists the feature flags and their sub-configurations to enable/disable and - `https-cert-file` [Required]: The path to the file containing the TLS certificate. - `https-key-file` [Required]: The path to the file containing the TLS private key. -## Observability -- **enable-observatorium-mock**: Enables use of a mock Observatorium client. - - `observatorium-timeout` [Optional]: Timeout to be used for Observatorium requests (default: `240s`). -- **observatorium-debug**: Enables Observatorium debug logging. -- **observatorium-ignore-ssl**: Disables Observatorium TLS verification. - -### Red Hat SSO Authentication -- The '[Required]' in the following denotes that these flags are required to use Red Hat SSO Authentication with the service. - - `observability-red-hat-sso-auth-server-url`[Required]: Red Hat SSO authentication server URL (default: `https://sso.redhat.com/auth`). - - `observability-red-hat-sso-realm`[Required]: Red Hat SSO realm (default: `redhat-external`). - - `observability-red-hat-sso-token-refresher-url`[Required]: Red Hat SSO token refresher URL (default: `www.test.com`). - - `observability-red-hat-sso-observatorium-gateway`[Required]: Red Hat SSO observatorium gateway (default: `https://observatorium-mst.api.stage.openshift.com`). - - `observability-red-hat-sso-tenant`[Required]: Red Hat SSO tenant (default: `managedCentral`). - - `observability-red-hat-sso-logs-client-id-file`[Required]: The path to the file containing the client - ID for the logs service account for use with Red Hat SSO. - - `observability-red-hat-sso-logs-secret-file`[Required]: The path to the file containing the client - secret for the logs service account for use with Red Hat SSO. - - `observability-red-hat-sso-metrics-client-id-file`[Required]: The path to the file containing the client - ID for the metrics service account for use with Red Hat SSO. - - `observability-red-hat-sso-metrics-secret-file`[Required]: The path to the file containing the client - secret for the metrics service account for use with Red Hat SSO. - ## OpenShift Cluster Manager - **enable-ocm-mock**: Enables use of a mock OCM client. - `ocm-mock-mode` [Optional]: Sets the ocm client mock type (default: `stub-server`). diff --git a/docs/legacy/observability/README.md b/docs/legacy/observability/README.md index d82d4a1fc5..77a11d5b38 100644 --- a/docs/legacy/observability/README.md +++ b/docs/legacy/observability/README.md @@ -5,9 +5,7 @@ This README will outline the adaptations and modifications that need to be made The file [metrics.go](../../pkg/metrics/metrics.go) creates Prometheus metrics of differing types. These are the metrics which are then reported and visualised in each Grafana dashboard. See [here](https://prometheus.io/docs/concepts/metric_types/) for more info about Prometheus metric types -These metrics are grouped by metric subject: data plane clusters, service ('dinosaur' for this template) and reconcilers. These metrics need to be updated with service name (ie 'dinosaur' replaced and service name included). - -There is also a section of metrics regarding Observatorium API. These metrics are not reported to a Grafana dashboard by this template. See [kas-fleet-manager metrics configmap](https://gitlab.cee.redhat.com/service/app-interface/-/blob/master/resources/observability/grafana/grafana-dashboard-kas-fleet-manager-metrics.configmap.yaml#L5460-7247) for an example of a Grafana dashboard reporting these metrics. +These metrics are grouped by metric subject: data plane clusters, service ('central' for this template) and reconcilers. These metrics need to be updated with service name (ie 'dinosaur' replaced and service name included). The file [metrics_middleware.go](../../pkg/handlers/metrics_middleware.go) creates metrics concerned with incoming API requests. This file contains useful and important information about how these metrics are written and reported. @@ -42,37 +40,12 @@ See [SLOs README](../slos/README.md) for more informtion about metrics and their See [here](https://gitlab.cee.redhat.com/service/app-interface#add-a-grafana-dashboard) for information about adding Grafana dashboards in App-Sre -## Observatorium - -The service that the Fleet Manager manages (Dinosaur in the case of this template) -can send metrics to a [Observatorium](https://github.com/observatorium/observatorium) -instance from the data plane. Fleet Manager is also able to interact directly -with Observatorium to retrieve the metrics sent by the managed -service (Dinosaur) from the data plane. - -### Configuring Observatorium - -To configure a new managed service to use Observatorium a Red Hat managed -Observatorium service can be used. For that, a new `Observatorium Tenant` has -to be created and configured in that Red Hat managed Observatorium service. That -task is done by the Red Hat Observability team. To do so there's -an Onboarding process. See the[Onboarding a Tenant into Red Hat’s Observatorium Instance](https://docs.google.com/document/d/1pjM9RRvij-IgwqQMt5q798B_4k4A9Y16uT2oV9sxN3g) document on how to do it. - -If you have any doubts about the onboarding process, The Red Hat Observability -team can be contacted on the #forum-observatorium Slack channel. - ## Observability stack -When a data plane cluster is created/assigned in Fleet Manager, the Observability stack is installed as part of the [cluster -Terraforming process](../implementation.md). +When a data plane cluster is created the Observability stack is installed as part of the cluster Terraforming process. The observability stack includes: -* [Observability Operator](https://github.com/redhat-developer/observability-operator): The Observability Operator deploys & maintains a common platform for Application Services to share and utilize to aid in monitoring & reporting on their service components. It integrates with the Observatorium project for pushing metrics and logs to a central location. See the linked repository on details about what is deployed / configured by the operator +* [Observability Operator](https://github.com/rhobs/observability-operator): The Observability Operator deploys & maintains a common platform for Application Services to share and utilize to aid in monitoring & reporting on their service components. It integrates with the Observatorium project for pushing metrics and logs to a central location. See the linked repository on details about what is deployed / configured by the operator * Configuration to set up the Observability stack through Observability Operator. This configuration is done by hosting a set of configuration files in a git remote repository that has to be provided as part of - the Fleet Manager configuration. An example of a git remote repository containing the observability stack configuration in a git remote - repository for the Managed Kafka service can be found in the [observability-resources-mk](https://github.com/bf2fc6cc711aee1a0c2a/observability-resources-mk) git repository - -To provide the parameters to set up the observability stack in fleet manager those are set as -CLI flags to the kas-fleet-manager code. See the [Observability section in the feature-flags documentation file](../feature-flags.md#Observability) -for details. + the Fleet Manager configuration. ACSCS gitops configuration can be found in the [acscs-manifests](https://github.com/stackrox/acscs-manifests) git repository diff --git a/docs/legacy/observability/configuring-metrics-federate-scrape-target.md b/docs/legacy/observability/configuring-metrics-federate-scrape-target.md deleted file mode 100644 index 39058a0278..0000000000 --- a/docs/legacy/observability/configuring-metrics-federate-scrape-target.md +++ /dev/null @@ -1,61 +0,0 @@ -# Configuring Dinosaur /metrics/federate Endpoint as a Prometheus Scrape Target - -The **/dinosaurs/{id}/metrics/federate** endpoint returns Dinosaur metrics in a Prometheus Text Format. This can be configured as a scrape target which allows you to easily collect your Dinosaur metrics and integrate them with your own metrics platform. - -This document will guide you on how to set up a Prometheus scrape target to collect metrics of your Dinosaur instances in OpenShift Streams for Apache Dinosaur. - -## Pre-requisites -- Prometheus instance. -- RHOSAK service account (create one [here](https://console.redhat.com/application-services/service-accounts)). - -### Configuring via Prometheus CR -> The following steps are based on this [guide](https://github.com/prometheus-operator/prometheus-operator/blob/master/Documentation/additional-scrape-config.md#additional-scrape-configuration) from Prometheus. - -1. Create a file called `dinosaur-federate.yaml` with the following content: - ``` - - job_name: "dinosaur-federate" - static_configs: - - targets: ["api.openshift.com"] - scheme: "https" - metrics_path: "/api/dinosaurs_mgmt/v1/dinosaurs//metrics/federate" - oauth2: - client_id: "" - client_secret: "" - token_url: "https://identity.api.openshift.com/auth/realms/rhoas/protocol/openid-connect/token" - ``` -2. Create a secret which has the configuration specified in step 1. - ``` - kubectl create secret generic additional-scrape-configs --from-file=dinosaur-federate.yaml --dry-run -o yaml | kubectl apply -f - -n - ``` -3. Reference this secret in your Prometheus CR - ``` - apiVersion: monitoring.coreos.com/v1 - kind: Prometheus - metadata: - ... - spec: - ... - additionalScrapeConfigs: - name: additional-scrape-configs - key: dinosaur-federate.yaml - ``` -4. The scrape target should be available once the configuration has been reloaded. - -### Configuring via a Configuration File - -1. Add the following to your Prometheus configuration file (see this [documentation](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#configuration-file) for more information about the Prometheus configuration file) - ``` - ... - scrape_configs: - - job_name: "dinosaur-federate" - static_configs: - - targets: ["api.openshift.com"] - scheme: "https" - metrics_path: "/api/dinosaurs_mgmt/v1/dinosaurs//metrics/federate" - oauth2: - client_id: "" - client_secret: "" - token_url: "https://identity.api.openshift.com/auth/realms/rhoas/protocol/openid-connect/token" - ... - ``` -2. The scrape target should be available once the configuration has been reloaded. diff --git a/docs/legacy/observability/running-local-observatorium-token-refresh.md b/docs/legacy/observability/running-local-observatorium-token-refresh.md deleted file mode 100644 index 6fac2c7a49..0000000000 --- a/docs/legacy/observability/running-local-observatorium-token-refresh.md +++ /dev/null @@ -1,19 +0,0 @@ -# Running a Local Observatorium Token Refresher -> NOTE: This is only required if your Observatorium instance is authenticated using sso.redhat.com. - -Run the following make target: -``` -make observatorium/token-refresher/setup CLIENT_ID= CLIENT_SECRET= [OPTIONAL PARAMETERS] -``` - -**Required Parameters**: -- CLIENT_ID: The client id of a service account that has, at least, permissions to read metrics. -- ClIENT_SECRET: The client secret of a service account that has, at least, permissions to read metrics. - -**Optional Parameters**: -- PORT: Port for running the token refresher on. Defaults to `8085` -- IMAGE_TAG: Image tag of the [token-refresher image](https://quay.io/repository/rhoas/mk-token-refresher?tab=tags). Defaults to `latest` -- ISSUER_URL: URL of your auth issuer. Defaults to `https://sso.redhat.com/auth/realms/redhat-external` -- OBSERVATORIUM_URL: URL of your Observatorium instance. Defaults to `https://observatorium-mst.api.stage.openshift.com/api/metrics/v1/manageddinosaur` - ->NOTE: Here the tenant used is a dummy one `manageddinosaur`. Once you have your application setup, you will need to have your own tenant created in the Red Hat obsevatorium instance. Optionally, you can deploy a local observatorium instance by following the [observatorium guide](https://github.com/observatorium/observatorium/blob/main/docs/usage/getting-started.md) diff --git a/go.mod b/go.mod index 674ad26737..4dbb95c1a7 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,6 @@ require ( github.com/openshift-online/ocm-sdk-go v0.1.439 github.com/openshift/addon-operator/apis v0.0.0-20231110045543-dd01f2f5c184 github.com/openshift/api v0.0.0-20231117201702-2ea16bbab164 - github.com/operator-framework/api v0.22.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 diff --git a/go.sum b/go.sum index e5525ba080..4f320ed121 100644 --- a/go.sum +++ b/go.sum @@ -1131,8 +1131,6 @@ github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -1238,8 +1236,6 @@ github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d/go.mod h1 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -1307,8 +1303,6 @@ github.com/openshift/client-go v0.0.0-20230926161409-848405da69e1 h1:W1N/3nVciqm github.com/openshift/client-go v0.0.0-20230926161409-848405da69e1/go.mod h1:ihUJrhBcYAGYQrJu/gP2OMgfVds5f5z5kbeLNBqjHLo= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/operator-framework/api v0.22.0 h1:UZSn+iaQih4rCReezOnWTTJkMyawwV5iLnIItaOzytY= -github.com/operator-framework/api v0.22.0/go.mod h1:p/7YDbr+n4fmESfZ47yLAV1SvkfE6NU2aX8KhcfI0GA= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= diff --git a/internal/dinosaur/constants/central.go b/internal/dinosaur/constants/central.go index aebb512ba2..0de2996d2c 100644 --- a/internal/dinosaur/constants/central.go +++ b/internal/dinosaur/constants/central.go @@ -34,12 +34,6 @@ const ( // CentralOperationDeprovision = Central cluster deprovision operations CentralOperationDeprovision CentralOperation = "deprovision" - // ObservabilityCanaryPodLabelKey that will be used by the observability operator to scrap metrics - ObservabilityCanaryPodLabelKey = "managed-central-canary" - - // ObservabilityCanaryPodLabelValue the value for ObservabilityCanaryPodLabelKey - ObservabilityCanaryPodLabelValue = "true" - // CentralMaxDurationWithProvisioningErrs the maximum duration a Central request // might be in provisioning state while receiving 5XX errors CentralMaxDurationWithProvisioningErrs = 5 * time.Minute @@ -60,12 +54,6 @@ var ordinals = map[string]int{ CentralRequestStatusFailed.String(): 500, } -// NamespaceLabels contains labels that indicates if a namespace is a managed application services namespace. -// A namespace with these labels will be scrapped by the Observability operator to retrieve metrics -var NamespaceLabels = map[string]string{ - "mas-managed": "true", -} - // String ... func (k CentralOperation) String() string { return string(k) diff --git a/internal/dinosaur/pkg/api/dbapi/data_plane_cluster_status.go b/internal/dinosaur/pkg/api/dbapi/data_plane_cluster_status.go index 0bc54f6d4b..65cab5c4b5 100644 --- a/internal/dinosaur/pkg/api/dbapi/data_plane_cluster_status.go +++ b/internal/dinosaur/pkg/api/dbapi/data_plane_cluster_status.go @@ -14,15 +14,6 @@ type DataPlaneClusterStatus struct { Addons []AddonInstallation } -// DataPlaneClusterConfigObservability ... -type DataPlaneClusterConfigObservability struct { - AccessToken string - Channel string - Repository string - Tag string -} - // DataPlaneClusterConfig ... type DataPlaneClusterConfig struct { - Observability DataPlaneClusterConfigObservability } diff --git a/internal/dinosaur/pkg/api/private/api/openapi.yaml b/internal/dinosaur/pkg/api/private/api/openapi.yaml index ba85ccc411..7825cc2caf 100644 --- a/internal/dinosaur/pkg/api/private/api/openapi.yaml +++ b/internal/dinosaur/pkg/api/private/api/openapi.yaml @@ -573,15 +573,11 @@ components: DataplaneClusterAgentConfig: description: Configuration for the data plane cluster agent example: - spec: - observability: - channel: channel - tag: tag - accessToken: accessToken - repository: repository + spec: '{}' properties: spec: - $ref: '#/components/schemas/DataplaneClusterAgentConfig_spec' + description: Data plane cluster agent spec + type: object type: object Error: allOf: @@ -790,34 +786,6 @@ components: type: string router: type: string - DataplaneClusterAgentConfig_spec_observability: - description: Observability configurations - example: - channel: channel - tag: tag - accessToken: accessToken - repository: repository - properties: - accessToken: - nullable: true - type: string - channel: - type: string - repository: - type: string - tag: - type: string - DataplaneClusterAgentConfig_spec: - description: Data plane cluster agent spec - example: - observability: - channel: channel - tag: tag - accessToken: accessToken - repository: repository - properties: - observability: - $ref: '#/components/schemas/DataplaneClusterAgentConfig_spec_observability' Error_allOf: properties: code: diff --git a/internal/dinosaur/pkg/api/private/model_dataplane_cluster_agent_config.go b/internal/dinosaur/pkg/api/private/model_dataplane_cluster_agent_config.go index 82e4be2f5a..3721ec4350 100644 --- a/internal/dinosaur/pkg/api/private/model_dataplane_cluster_agent_config.go +++ b/internal/dinosaur/pkg/api/private/model_dataplane_cluster_agent_config.go @@ -12,5 +12,6 @@ package private // DataplaneClusterAgentConfig Configuration for the data plane cluster agent type DataplaneClusterAgentConfig struct { - Spec DataplaneClusterAgentConfigSpec `json:"spec,omitempty"` + // Data plane cluster agent spec + Spec map[string]interface{} `json:"spec,omitempty"` } diff --git a/internal/dinosaur/pkg/api/private/model_dataplane_cluster_agent_config_spec.go b/internal/dinosaur/pkg/api/private/model_dataplane_cluster_agent_config_spec.go deleted file mode 100644 index a2455ec201..0000000000 --- a/internal/dinosaur/pkg/api/private/model_dataplane_cluster_agent_config_spec.go +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Red Hat Advanced Cluster Security Service Fleet Manager - * - * Red Hat Advanced Cluster Security (RHACS) Service Fleet Manager APIs that are used by internal services e.g fleetshard operators. - * - * API version: 1.4.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech). DO NOT EDIT. -package private - -// DataplaneClusterAgentConfigSpec Data plane cluster agent spec -type DataplaneClusterAgentConfigSpec struct { - Observability DataplaneClusterAgentConfigSpecObservability `json:"observability,omitempty"` -} diff --git a/internal/dinosaur/pkg/api/private/model_dataplane_cluster_agent_config_spec_observability.go b/internal/dinosaur/pkg/api/private/model_dataplane_cluster_agent_config_spec_observability.go deleted file mode 100644 index c1ffb2aab0..0000000000 --- a/internal/dinosaur/pkg/api/private/model_dataplane_cluster_agent_config_spec_observability.go +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Red Hat Advanced Cluster Security Service Fleet Manager - * - * Red Hat Advanced Cluster Security (RHACS) Service Fleet Manager APIs that are used by internal services e.g fleetshard operators. - * - * API version: 1.4.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech). DO NOT EDIT. -package private - -// DataplaneClusterAgentConfigSpecObservability Observability configurations -type DataplaneClusterAgentConfigSpecObservability struct { - AccessToken *string `json:"accessToken,omitempty"` - Channel string `json:"channel,omitempty"` - Repository string `json:"repository,omitempty"` - Tag string `json:"tag,omitempty"` -} diff --git a/internal/dinosaur/pkg/api/public/api/openapi.yaml b/internal/dinosaur/pkg/api/public/api/openapi.yaml index 92b0bc534d..1e9de56134 100644 --- a/internal/dinosaur/pkg/api/public/api/openapi.yaml +++ b/internal/dinosaur/pkg/api/public/api/openapi.yaml @@ -639,198 +639,6 @@ paths: security: - Bearer: [] summary: Returns the list of cloud accounts which belong to user's organization - /api/rhacs/v1/centrals/{id}/metrics/query_range: - get: - operationId: getMetricsByRangeQuery - parameters: - - description: The ID of record - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - - description: The length of time in minutes for which to return the metrics - examples: - duration: - value: 5 - explode: true - in: query - name: duration - required: true - schema: - default: 5 - format: int64 - maximum: 4320 - minimum: 1 - type: integer - style: form - - description: The interval in seconds between data points - examples: - interval: - value: 30 - explode: true - in: query - name: interval - required: true - schema: - default: 30 - format: int64 - maximum: 10800 - minimum: 1 - type: integer - style: form - - description: List of metrics to fetch. Fetch all metrics when empty. List - entries are Central internal metric names. - explode: true - in: query - name: filters - required: false - schema: - default: [] - items: - type: string - type: array - style: form - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/MetricsRangeQueryList' - description: Returned JSON array of Prometheus metrics objects from observatorium - "401": - content: - application/json: - examples: - "401Example": - $ref: '#/components/examples/401Example' - schema: - $ref: '#/components/schemas/Error' - description: Auth token is invalid - "500": - content: - application/json: - examples: - "500Example": - $ref: '#/components/examples/500Example' - schema: - $ref: '#/components/schemas/Error' - description: Unexpected error occurred - security: - - Bearer: [] - summary: Returns metrics with timeseries range query by Central ID - /api/rhacs/v1/centrals/{id}/metrics/query: - get: - operationId: getMetricsByInstantQuery - parameters: - - description: The ID of record - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - - description: List of metrics to fetch. Fetch all metrics when empty. List - entries are Central internal metric names. - explode: true - in: query - name: filters - required: false - schema: - default: [] - items: - type: string - type: array - style: form - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/MetricsInstantQueryList' - description: Returned JSON array of Prometheus metrics objects from observatorium - "401": - content: - application/json: - examples: - "401Example": - $ref: '#/components/examples/401Example' - schema: - $ref: '#/components/schemas/Error' - description: Auth token is invalid - "500": - content: - application/json: - examples: - "500Example": - $ref: '#/components/examples/500Example' - schema: - $ref: '#/components/schemas/Error' - description: Unexpected error occurred - security: - - Bearer: [] - summary: Returns metrics with instant query by Central ID - /api/rhacs/v1/centrals/{id}/metrics/federate: - get: - operationId: federateMetrics - parameters: - - description: The ID of record - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - responses: - "200": - content: - text/plain: - schema: - type: string - description: Returned Central metrics in a Prometheus text format - "400": - content: - application/json: - examples: - MissingParameterExample: - $ref: '#/components/examples/400MissingParameterExample' - schema: - $ref: '#/components/schemas/Error' - description: Bad request - "401": - content: - application/json: - examples: - "401Example": - $ref: '#/components/examples/401Example' - schema: - $ref: '#/components/schemas/Error' - description: Auth token is invalid - "404": - content: - application/json: - examples: - "404Example": - $ref: '#/components/examples/404Example' - schema: - $ref: '#/components/schemas/Error' - description: Central ID not found - "500": - content: - application/json: - examples: - "500Example": - $ref: '#/components/examples/500Example' - schema: - $ref: '#/components/schemas/Error' - description: Unexpected error occurred - security: - - Bearer: [] - summary: Returns all metrics in scrapeable format for a given Central ID components: examples: USRegionExample: diff --git a/internal/dinosaur/pkg/api/public/api_default.go b/internal/dinosaur/pkg/api/public/api_default.go index be5339da49..2d338e738b 100644 --- a/internal/dinosaur/pkg/api/public/api_default.go +++ b/internal/dinosaur/pkg/api/public/api_default.go @@ -16,7 +16,6 @@ import ( _ioutil "io/ioutil" _nethttp "net/http" _neturl "net/url" - "reflect" "strings" ) @@ -286,123 +285,6 @@ func (a *DefaultApiService) DeleteCentralById(ctx _context.Context, id string, a return localVarHTTPResponse, nil } -/* -FederateMetrics Returns all metrics in scrapeable format for a given Central ID - - @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - - @param id The ID of record - -@return string -*/ -func (a *DefaultApiService) FederateMetrics(ctx _context.Context, id string) (string, *_nethttp.Response, error) { - var ( - localVarHTTPMethod = _nethttp.MethodGet - localVarPostBody interface{} - localVarFormFileName string - localVarFileName string - localVarFileBytes []byte - localVarReturnValue string - ) - - // create path and map variables - localVarPath := a.client.cfg.BasePath + "/api/rhacs/v1/centrals/{id}/metrics/federate" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", _neturl.QueryEscape(parameterToString(id, "")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := _neturl.Values{} - localVarFormParams := _neturl.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"text/plain", "application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(r) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - if localVarHTTPResponse.StatusCode == 400 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 401 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 404 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 500 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - /* GetCentralById Returns a Central request by ID This operation is only authorized to users in the same organisation as the owner organisation of the specified Central. @@ -981,252 +863,6 @@ func (a *DefaultApiService) GetCloudProviders(ctx _context.Context, localVarOpti return localVarReturnValue, localVarHTTPResponse, nil } -// GetMetricsByInstantQueryOpts Optional parameters for the method 'GetMetricsByInstantQuery' -type GetMetricsByInstantQueryOpts struct { - Filters optional.Interface -} - -/* -GetMetricsByInstantQuery Returns metrics with instant query by Central ID - - @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - - @param id The ID of record - - @param optional nil or *GetMetricsByInstantQueryOpts - Optional Parameters: - - @param "Filters" (optional.Interface of []string) - List of metrics to fetch. Fetch all metrics when empty. List entries are Central internal metric names. - -@return MetricsInstantQueryList -*/ -func (a *DefaultApiService) GetMetricsByInstantQuery(ctx _context.Context, id string, localVarOptionals *GetMetricsByInstantQueryOpts) (MetricsInstantQueryList, *_nethttp.Response, error) { - var ( - localVarHTTPMethod = _nethttp.MethodGet - localVarPostBody interface{} - localVarFormFileName string - localVarFileName string - localVarFileBytes []byte - localVarReturnValue MetricsInstantQueryList - ) - - // create path and map variables - localVarPath := a.client.cfg.BasePath + "/api/rhacs/v1/centrals/{id}/metrics/query" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", _neturl.QueryEscape(parameterToString(id, "")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := _neturl.Values{} - localVarFormParams := _neturl.Values{} - - if localVarOptionals != nil && localVarOptionals.Filters.IsSet() { - t := localVarOptionals.Filters.Value() - if reflect.TypeOf(t).Kind() == reflect.Slice { - s := reflect.ValueOf(t) - for i := 0; i < s.Len(); i++ { - localVarQueryParams.Add("filters", parameterToString(s.Index(i), "multi")) - } - } else { - localVarQueryParams.Add("filters", parameterToString(t, "multi")) - } - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(r) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - if localVarHTTPResponse.StatusCode == 401 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 500 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -// GetMetricsByRangeQueryOpts Optional parameters for the method 'GetMetricsByRangeQuery' -type GetMetricsByRangeQueryOpts struct { - Filters optional.Interface -} - -/* -GetMetricsByRangeQuery Returns metrics with timeseries range query by Central ID - - @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - - @param id The ID of record - - @param duration The length of time in minutes for which to return the metrics - - @param interval The interval in seconds between data points - - @param optional nil or *GetMetricsByRangeQueryOpts - Optional Parameters: - - @param "Filters" (optional.Interface of []string) - List of metrics to fetch. Fetch all metrics when empty. List entries are Central internal metric names. - -@return MetricsRangeQueryList -*/ -func (a *DefaultApiService) GetMetricsByRangeQuery(ctx _context.Context, id string, duration int64, interval int64, localVarOptionals *GetMetricsByRangeQueryOpts) (MetricsRangeQueryList, *_nethttp.Response, error) { - var ( - localVarHTTPMethod = _nethttp.MethodGet - localVarPostBody interface{} - localVarFormFileName string - localVarFileName string - localVarFileBytes []byte - localVarReturnValue MetricsRangeQueryList - ) - - // create path and map variables - localVarPath := a.client.cfg.BasePath + "/api/rhacs/v1/centrals/{id}/metrics/query_range" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", _neturl.QueryEscape(parameterToString(id, "")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := _neturl.Values{} - localVarFormParams := _neturl.Values{} - if duration < 1 { - return localVarReturnValue, nil, reportError("duration must be greater than 1") - } - if duration > 4320 { - return localVarReturnValue, nil, reportError("duration must be less than 4320") - } - if interval < 1 { - return localVarReturnValue, nil, reportError("interval must be greater than 1") - } - if interval > 10800 { - return localVarReturnValue, nil, reportError("interval must be less than 10800") - } - - localVarQueryParams.Add("duration", parameterToString(duration, "")) - localVarQueryParams.Add("interval", parameterToString(interval, "")) - if localVarOptionals != nil && localVarOptionals.Filters.IsSet() { - t := localVarOptionals.Filters.Value() - if reflect.TypeOf(t).Kind() == reflect.Slice { - s := reflect.ValueOf(t) - for i := 0; i < s.Len(); i++ { - localVarQueryParams.Add("filters", parameterToString(s.Index(i), "multi")) - } - } else { - localVarQueryParams.Add("filters", parameterToString(t, "multi")) - } - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(r) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - if localVarHTTPResponse.StatusCode == 401 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - return localVarReturnValue, localVarHTTPResponse, newErr - } - if localVarHTTPResponse.StatusCode == 500 { - var v Error - err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr.error = err.Error() - return localVarReturnValue, localVarHTTPResponse, newErr - } - newErr.model = v - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - /* GetServiceStatus Returns the status of resources, such as whether maximum service capacity has been reached - @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). diff --git a/internal/dinosaur/pkg/environments/development.go b/internal/dinosaur/pkg/environments/development.go index a79c36d5c3..b176917574 100644 --- a/internal/dinosaur/pkg/environments/development.go +++ b/internal/dinosaur/pkg/environments/development.go @@ -6,38 +6,33 @@ import "github.com/stackrox/acs-fleet-manager/pkg/environments" // NewDevelopmentEnvLoader The development environment is intended for use while developing features, requiring manual verification func NewDevelopmentEnvLoader() environments.EnvLoader { return environments.SimpleEnvLoader{ - "v": "10", - "ocm-debug": "false", - "ams-base-url": "https://api.stage.openshift.com", - "ocm-base-url": "https://api.stage.openshift.com", - "enable-ocm-mock": "true", - "enable-https": "false", - "enable-metrics-https": "false", - "enable-terms-acceptance": "false", - "api-server-bindaddress": "localhost:8000", - "enable-sentry": "false", - "enable-deny-list": "true", - "enable-instance-limit-control": "false", - "enable-central-external-certificate": "false", - "cluster-compute-machine-type": "m5.2xlarge", - "allow-evaluator-instance": "true", - "quota-type": "quota-management-list", - "enable-deletion-of-expired-central": "true", - "dataplane-cluster-scaling-type": "manual", - "observability-red-hat-sso-auth-server-url": "https://sso.redhat.com/auth", - "observability-red-hat-sso-realm": "redhat-external", - "observability-red-hat-sso-token-refresher-url": "http://localhost:8085", - "observability-red-hat-sso-observatorium-gateway": "https://observatorium-mst.api.stage.openshift.com", - "observability-red-hat-sso-tenant": "manageddinosaur", - "enable-additional-sso-issuers": "true", - "additional-sso-issuers-file": "config/additional-sso-issuers.yaml", - "jwks-file": "config/jwks-file-static.json", - "fleetshard-authz-config-file": "config/fleetshard-authz-development.yaml", - "central-idp-client-id": "rhacs-ms-dev", - "central-idp-issuer": "https://sso.stage.redhat.com/auth/realms/redhat-external", - "admin-authz-config-file": "config/admin-authz-roles-dev.yaml", - "enable-leader-election": "false", - "kubernetes-issuer-enabled": "true", - "kubernetes-issuer-uri": "https://127.0.0.1:6443", + "v": "10", + "ocm-debug": "false", + "ams-base-url": "https://api.stage.openshift.com", + "ocm-base-url": "https://api.stage.openshift.com", + "enable-ocm-mock": "true", + "enable-https": "false", + "enable-metrics-https": "false", + "enable-terms-acceptance": "false", + "api-server-bindaddress": "localhost:8000", + "enable-sentry": "false", + "enable-deny-list": "true", + "enable-instance-limit-control": "false", + "enable-central-external-certificate": "false", + "cluster-compute-machine-type": "m5.2xlarge", + "allow-evaluator-instance": "true", + "quota-type": "quota-management-list", + "enable-deletion-of-expired-central": "true", + "dataplane-cluster-scaling-type": "manual", + "enable-additional-sso-issuers": "true", + "additional-sso-issuers-file": "config/additional-sso-issuers.yaml", + "jwks-file": "config/jwks-file-static.json", + "fleetshard-authz-config-file": "config/fleetshard-authz-development.yaml", + "central-idp-client-id": "rhacs-ms-dev", + "central-idp-issuer": "https://sso.stage.redhat.com/auth/realms/redhat-external", + "admin-authz-config-file": "config/admin-authz-roles-dev.yaml", + "enable-leader-election": "false", + "kubernetes-issuer-enabled": "true", + "kubernetes-issuer-uri": "https://127.0.0.1:6443", } } diff --git a/internal/dinosaur/pkg/environments/integration.go b/internal/dinosaur/pkg/environments/integration.go index ca3b9f20f5..c03604e019 100644 --- a/internal/dinosaur/pkg/environments/integration.go +++ b/internal/dinosaur/pkg/environments/integration.go @@ -3,7 +3,6 @@ package environments import ( "os" - "github.com/stackrox/acs-fleet-manager/pkg/client/observatorium" ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/db" "github.com/stackrox/acs-fleet-manager/pkg/environments" @@ -53,12 +52,10 @@ func (b IntegrationEnvLoader) Defaults() map[string]string { func (b IntegrationEnvLoader) ModifyConfiguration(env *environments.Env) error { // Support a one-off env to allow enabling db debug in testing var databaseConfig *db.DatabaseConfig - var observabilityConfiguration *observatorium.ObservabilityConfiguration - env.MustResolveAll(&databaseConfig, &observabilityConfiguration) + env.MustResolveAll(&databaseConfig) if os.Getenv("DB_DEBUG") == "true" { databaseConfig.Debug = true } - observabilityConfiguration.EnableMock = true return nil } diff --git a/internal/dinosaur/pkg/generated/bindata.go b/internal/dinosaur/pkg/generated/bindata.go index 77d2688b13..ff36834557 100644 --- a/internal/dinosaur/pkg/generated/bindata.go +++ b/internal/dinosaur/pkg/generated/bindata.go @@ -79,7 +79,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _fleetManagerYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x7b\x53\x1b\xb9\xb2\xf8\xff\x7c\x8a\xfe\x39\xbf\x53\x9c\xdd\xc2\xc6\x36\x6f\xd7\xcd\xad\x22\x81\x24\xec\x49\x08\xcb\x63\xb3\xd9\xad\x53\x46\x9e\x91\x6d\x85\x19\x69\x90\x34\x06\xe7\x9e\xfb\xdd\x6f\x49\x9a\x87\xe6\xe9\x31\x84\x04\x76\xa1\x6a\x6b\xe3\x19\xa9\xa7\xdf\x6a\x49\xad\x16\x0b\x30\x45\x01\x19\xc0\x46\xa7\xdb\xe9\xc2\x0b\xa0\x18\xbb\x20\xa7\x44\x00\x12\x30\x26\x5c\x48\xf0\x08\xc5\x20\x19\x20\xcf\x63\x37\x20\x98\x8f\xe1\xe8\xe0\x50\xa8\x47\x57\x94\xdd\x98\xd6\xaa\x03\x85\x08\x1c\xb8\xcc\x09\x7d\x4c\x65\x67\xe5\x05\xec\x7b\x1e\x60\xea\x06\x8c\x50\x29\xc0\xc5\x63\x42\xb1\x0b\x53\xcc\x31\xdc\x10\xcf\x83\x11\x06\x97\x08\x87\xcd\x30\x47\x23\x0f\xc3\x68\xae\xbe\x04\xa1\xc0\x5c\x74\xe0\x68\x0c\x52\xb7\x55\x1f\x88\xb0\x63\x70\x85\x71\x60\x30\x49\x21\xb7\x02\x4e\x66\x48\xe2\xd6\x1a\x20\x57\xd1\x80\x7d\xd5\x54\x4e\x31\xb4\x7c\x44\xd1\x04\xbb\x6d\x81\xf9\x8c\x38\x58\xb4\x51\x40\xda\x51\xfb\xce\x1c\xf9\x5e\x0b\xc6\xc4\xc3\x2b\x84\x8e\xd9\x60\x05\x40\x12\xe9\xe1\x01\x9c\x62\x17\xde\x21\x09\xfb\xee\x0c\x51\x07\xbb\xf0\xda\x0b\x85\xc4\x1c\xce\xb0\x13\x72\x22\xe7\x70\x66\x00\xc2\x1b\x0f\x63\x09\x1f\xf4\x67\xf8\x0a\xc0\x0c\x73\x41\x18\x1d\x40\xaf\xd3\xef\x74\x57\x00\x5c\x2c\x1c\x4e\x02\xa9\x1f\x2e\x86\xfb\xcf\xd3\x77\xfb\xaf\xcf\x7e\x2a\x87\x6f\x78\x71\x8a\x85\x84\xfd\x93\x23\x45\xa4\xa1\x0f\x08\x15\x52\x01\x14\xc0\xc6\xb0\xff\xfa\x0c\x1c\xe6\x07\x8c\x62\x2a\x45\x67\x45\xd1\x8e\xb9\x50\xe4\xb5\x21\xe4\xde\x00\xa6\x52\x06\x62\xb0\xbe\x8e\x02\xd2\x51\x92\x13\x53\x32\x96\x1d\x87\xf9\x2b\x00\x39\x8c\x3f\x20\x42\xe1\x9f\x01\x67\x6e\xe8\xa8\x27\x3f\x81\x01\x57\x0e\x4c\x48\x34\xc1\x8b\x40\x9e\x49\x34\x21\x74\x52\x0a\x68\xb0\xbe\xee\x31\x07\x79\x53\x26\xe4\x60\xb7\xdb\xed\x16\xbb\x27\xef\xd3\x9e\xeb\xc5\x56\x4e\xc8\x39\xa6\x12\x5c\xe6\x23\x42\x57\x02\x24\xa7\x9a\x03\x0a\xcd\x75\x3e\x45\x8e\x58\x9f\xf5\x06\xba\xdf\x04\x4b\xf3\x0f\x50\x6a\xcc\x91\x02\x70\xe4\x0e\xd4\xf3\xdf\x8c\x34\x3f\x60\x89\x5c\x24\x51\xd4\x8a\x63\x11\x30\x2a\xb0\x88\xbb\x01\xb4\xfa\xdd\x6e\x2b\xfd\x09\xe0\x30\x2a\x31\x95\xf6\x23\x00\x14\x04\x1e\x71\xf4\x07\xd6\xbf\x08\x46\xb3\x6f\x01\x84\x33\xc5\x3e\xca\x3f\x05\xf8\xff\x1c\x8f\x07\xd0\x7a\xb1\x9e\x8a\x75\xdd\xb4\x15\xeb\x39\x14\x5b\x56\xe7\x0c\x43\xa2\x76\xe0\x67\x69\x11\xa1\xef\x23\x3e\x57\xaa\x29\x43\x4e\x85\x36\x9b\x59\xbe\x6d\x9e\x71\xeb\x98\x73\xc6\xc5\xfa\xff\x10\xf7\x7f\x17\x32\xf1\x50\xb5\x7d\x35\x3f\x72\x1f\x23\xfb\x34\x72\x95\x4c\x7b\x8b\x25\x68\x52\x95\x73\x4a\x08\x28\xe5\x59\xd2\x8c\xc4\xcd\x24\x9a\x58\x24\xb6\x4d\x0b\x11\x3d\x08\x10\x47\x3e\x96\x91\x5d\xc6\x4d\xca\x30\x4d\x5b\xae\x13\xb7\x55\x25\x8a\x66\x52\x10\x8f\x56\x04\xef\x89\x90\x95\x62\x50\x2f\x95\x67\x0b\x98\x10\x44\x0d\x15\x19\x56\x96\x8a\xc3\xcb\x77\x51\x0e\x33\xd3\xad\x42\x3c\x05\xfe\x0a\x89\x64\xb8\x98\xbf\x91\xc3\x3e\xd3\xad\x1f\x23\x9b\x33\x08\x56\xb2\xfa\xe3\x55\x8a\xea\x56\x0e\xd5\x4c\xc3\x0b\x8a\x6f\x03\xec\x48\xec\x46\xaa\xcf\x1c\xed\x73\xdd\x47\x61\xc5\xea\x0f\xdf\x22\x3f\xf0\x6c\xe6\xc7\x7f\x5b\xdd\xee\xa1\x79\x59\x7c\x57\xfe\xa1\x18\xd6\x7a\xda\xb5\x55\xa7\x7e\x46\x69\x94\x02\x72\x2c\x58\xc8\x1d\x2c\xd6\x40\x84\xce\x54\x45\x57\x37\x53\xac\x42\x1b\xf0\xd1\x2d\xf1\x43\x1f\xa2\xe0\x04\x1c\x14\x20\x47\x05\x01\x53\x24\x60\x84\x31\x05\x8e\x91\x33\x4d\x58\x2a\xa2\x20\xc1\xd6\xda\x57\x18\x71\xcc\x07\xf0\xe7\xbf\x0b\x8a\xeb\x60\x2a\x39\xf2\x1a\x7a\xe9\xd7\xa6\xb5\xe5\xa7\x33\xe2\x3e\x57\xb1\x5e\xd2\x47\x05\x22\x8c\x7a\x73\x40\xa1\x9c\x32\x4e\xbe\x9a\xe8\x4c\x87\x6e\x40\xa8\x61\x01\xf2\x31\x30\x3e\x41\x94\x08\xd3\x09\x19\xde\xb0\x1b\x8a\x79\xf6\x0d\x1b\x9b\x2e\x01\x76\xc8\x98\xa8\xb8\xc8\x60\xd3\x79\x8c\x86\x14\xe1\x76\x8a\xaf\x43\x9c\x75\x5a\xf5\x5a\x97\xed\xf7\x16\xcb\xd3\x88\xaa\xbb\xea\x62\x16\x60\x4e\x2d\x1b\x7c\xf7\x13\x91\xd3\x37\x88\x78\xd8\x7d\xcd\xb1\xe6\x91\x71\x0e\xdf\x06\x9f\x1a\xc8\x95\xde\x27\x82\x00\xdc\x80\x80\x31\x0b\xa9\xab\xc7\xde\x83\x54\xf0\x9b\xdd\xde\x23\x89\x15\xea\xe5\xbd\xd9\xed\xdd\x95\x93\x69\xd7\x4a\x56\xed\x87\x72\x0a\x92\x5d\x61\x6d\x8c\x84\xce\x90\x47\x5c\x9b\x49\x1b\x4f\x84\x49\x1b\x77\x67\xd2\xc6\x22\x26\x5d\x08\xcc\x81\x32\x99\xf3\x53\xc8\x71\xb0\x88\x1c\xb5\xf1\xbd\x36\xe3\x36\x9f\x08\xe3\x36\xef\xce\xb8\xcd\x45\x8c\x3b\x66\x05\x5b\xbc\x21\x72\x6a\x79\xe8\xa3\x03\xc0\xb7\x44\x48\x51\x1d\x2f\x3c\x56\xd6\x7d\xd3\xe1\xbf\xc0\xba\x45\x81\xd1\xc2\x51\x1c\xca\x82\x0a\x54\x90\x47\xea\x15\x5d\xec\x61\x89\x4b\x07\x76\xf3\x6a\xc1\xd8\xfe\x9f\x04\x93\x73\x35\x3c\xab\x71\xdd\x8c\xe4\x96\xd5\x8c\x19\x37\xeb\x3d\x69\x0c\x80\xb8\xc5\xbf\xde\x4f\xba\x33\x72\x7d\x42\x89\x90\x1c\x49\x45\xf9\xf8\xae\x03\x3e\x40\xdf\x00\x34\x7d\x15\x3a\x6b\x80\xa8\x6b\xb0\x23\x63\x20\x52\x2f\x86\x78\x82\xa9\xa9\x94\xbc\xc7\xa7\xca\x67\x62\x84\x0e\xe0\x3a\xc4\x7c\x6e\x89\x99\x22\x1f\x0f\x00\x89\x39\x75\xaa\x84\x7f\x82\xf9\x98\x71\x5f\x7f\x11\x39\x26\x54\xa2\x80\xa8\xe9\x35\xe5\x8c\xb2\x50\x80\x8f\x28\xd5\x2b\x1f\x75\x4a\x2f\xe7\x01\x1e\xc0\x88\x31\x0f\x23\x6a\xbd\x51\xf2\x27\x1c\xbb\x03\x90\x3c\xc4\xb5\x01\x52\xbf\x3a\x7c\x3f\xd0\x8a\x91\x19\x30\x9e\x86\xf1\x6e\x76\xbb\x1a\x77\xc2\xe8\xdd\xfd\x5f\x1e\x44\xf5\xaa\x89\x1a\x55\x8d\x1e\x99\xf9\x61\x71\x9a\xf3\x1c\x8f\x3c\xc7\x23\xcf\xf1\x88\x8a\x47\x8c\x4f\xb9\x47\x54\x92\x01\xf0\xb7\x8d\x4d\xee\xc7\xc6\x3c\x80\xbb\xc7\x29\x71\x08\x62\xc0\xd5\x87\x20\x8d\xc2\x9a\xe2\x48\xdb\x68\xc5\xb3\x6a\x5d\xc3\x00\x09\x98\x28\x5f\xd3\x70\xd4\xcc\x33\x0e\x7d\xca\xc2\x9e\x43\xe4\x4c\x21\x02\xa6\x97\x5c\x10\x08\x42\x27\x5e\x69\x14\xa1\x62\x8f\xdc\x7b\x15\x94\x74\x40\x4f\x70\xb1\xd9\xa3\xba\x49\x38\x24\xa7\x48\x07\x28\xaa\xa5\x9e\xc0\x2a\xdb\x56\x1d\x4c\x10\x93\x81\x1c\xca\x29\xa6\x52\xe9\x5d\x12\x67\xe1\x98\xc5\x7f\xb1\x20\x45\xd3\xf4\x8a\xb9\x96\x96\x94\xce\xff\xad\x0d\x8a\x52\x53\xad\x37\xd4\x72\x33\x6d\xbe\xa4\x73\x82\xe6\x1e\x43\x6e\xd6\x68\xab\x4c\xf6\xe2\xec\x14\x4f\x48\xd1\x57\x2c\x30\xd3\xb8\x5b\xc5\xaa\xcd\xe1\xc5\x9d\xa0\xc6\xdd\x0a\x50\xef\x1c\x34\x3e\xb1\x55\xb5\x13\x26\x1e\x7e\x59\x2d\x1b\xf7\x38\x0e\x0e\x9e\x6a\x24\x1d\xaf\xce\xdd\x23\x92\xce\x81\x78\x8e\xa4\x9f\x23\xe9\x98\x49\xdf\x38\x92\x4e\xc0\x7e\x40\xb7\xfb\x9e\xc7\x6e\xb0\x7b\x14\xe5\x3d\x9c\x9a\x7d\x92\x7b\x7c\x6f\x11\xcc\x52\x44\xce\x31\xf7\xc5\x31\x93\xb1\x0f\xb8\xc7\xf7\x2b\x40\xd5\xcf\x24\xc6\x8c\x8f\x88\xeb\x62\x0a\x98\xe8\x1d\xa5\x11\x76\x50\x28\x70\x1a\x6d\x10\xd1\x68\xba\x01\x2c\xdb\x37\xde\x99\xa2\xa1\x3f\xc2\x7a\x1d\x27\xcd\x30\xd1\xa1\x8d\x83\x28\x8c\x70\x14\x63\x45\x01\x0e\x11\xe6\x9b\xf9\xdd\xab\xce\x93\x9c\xcc\x3c\xe0\xe2\xea\x79\x1a\xdf\x61\x37\xd9\x20\x04\x97\x61\x41\x57\xa5\x99\xba\xd8\x3c\xdb\x7b\x22\x3c\xdb\x3b\x46\x3e\x7e\xcd\xe8\xd8\x23\x8e\xbc\x3b\xff\xca\xc0\x54\x3b\x4b\xc5\x0f\xdd\x32\xd5\x3b\x17\x4b\x33\xaf\x89\x76\x22\x9d\x68\x88\x32\x4b\x81\x44\x24\x2c\x7f\x92\xd3\xc3\x07\x5c\xba\xde\xa7\x10\x56\xcd\x0a\xe1\x66\x4a\xbc\x98\x97\x74\xa2\x19\x9b\x9b\x0f\x36\x9f\x09\x5a\xb3\xcb\x74\xfe\x54\x06\xcd\xda\xb0\x2e\x59\x12\x8f\x93\x3c\x72\x3d\x45\xd9\x6c\xef\x23\xf5\xe6\xc0\x93\x2d\x7a\x26\x70\x3c\xf7\x8b\x5c\x1a\xe2\x38\x3b\x5d\x2b\x5b\x45\x36\x53\xb8\x26\x33\xb6\x8a\xfd\x75\xb1\x0c\x93\x9a\x6c\x7b\xe7\xac\x61\x01\x4b\xe0\xc7\x85\xf4\xf9\x0c\x1f\x58\x22\xac\x57\x7d\xbf\x4d\x38\x6f\x41\x6a\x55\x87\xec\x19\xa6\xbe\x42\x6e\x4e\xc3\xbf\x2b\x17\x97\xf4\x10\x47\x26\x5e\xfc\x35\xc4\x7c\x7e\x8f\xb0\xbe\x04\x4c\xab\x3a\x50\x5f\x22\x7e\x7d\xbc\x9c\xfb\xc6\x51\xfd\xdf\x37\x50\xbf\xef\x92\xf7\x73\xde\x59\x93\xd1\xfb\x4e\x19\xa4\x01\x9a\x58\xa2\x5a\xd8\x5c\x90\xaf\xcb\x34\x67\xdc\xc5\xfc\xd5\x7c\x99\x0f\x60\xc4\x9d\x69\xc9\x1a\xaf\xc7\x42\x77\x18\x70\x36\x23\x2e\x2e\xc9\x6e\xad\xcd\xf9\x14\x61\x10\x30\xae\x34\x44\x83\x81\x04\x4c\xd5\xd0\xac\x5a\x9d\xe4\x1a\x3d\xcc\x00\x6d\xd0\xc5\x6e\x63\x5c\xe1\xbb\x0e\xd8\x36\x23\xb2\xe3\xf5\xb3\xcb\x6f\xe2\xf2\x9f\x3d\xd7\x63\xf3\x5c\xb5\x6e\x45\x67\xc6\xae\x73\xbd\x64\x7e\x67\x1f\x13\x75\x4f\xf2\x4c\x2a\x0c\xba\x89\xef\x31\x8b\xf7\x8f\xc4\x03\xc5\x84\xfd\x30\x47\x64\xb8\xf1\xec\x86\x9e\xdd\x50\xf2\xf7\xe3\xdd\x10\x71\x97\x70\x42\x0f\x1b\x6d\xc5\x4b\xb2\x43\x39\x0f\x2a\x7d\x1d\x72\x1c\x16\x52\xb9\xa4\x77\x33\x9e\x20\xee\x0b\x37\x53\xe2\x4c\x61\x84\x3d\x46\x27\x71\x9e\xff\xaa\x88\x16\x48\xbe\x6a\x8d\xa8\x73\x6f\xfb\x11\x9c\x26\x7e\x0d\xfe\x06\x8e\x2d\xe6\xc7\xb3\x6b\x7b\x76\x6d\xc9\xdf\x37\x73\x6d\x2f\xd4\x7f\x70\x3e\xc5\x02\xeb\x35\xcd\x78\xd9\xb2\x3d\x46\x0e\xa1\x13\xe0\xd8\xd3\xeb\x96\xc9\x01\xea\xa8\x4f\xcd\xe1\xa1\x75\x1f\x4b\x4e\x1c\xb1\xae\x93\x4d\x86\x1c\xd1\x09\x5e\xec\x50\xa2\x4e\x26\x39\x4b\x12\x1f\x0b\xcc\x09\x16\xa0\xbb\x9b\xbc\x15\x18\xcd\x93\x95\xca\x24\x93\x28\xef\x43\x3e\x18\x38\xaf\xe6\xa7\xaa\xe3\xaf\x56\xbe\xcb\x03\x47\x48\xbf\x9c\x7d\x3c\x06\xc4\x39\x9a\x2b\x77\x72\xc2\x99\x8f\xe5\x14\x87\x29\x65\x6c\xf4\x05\x3b\x52\xc0\x98\x33\x1f\xd8\x48\x60\x3e\x43\x92\x71\x12\xfa\x3f\x42\xe1\x22\x3e\xa5\x5c\x7a\xf6\x2f\xcf\xfe\x25\xf9\x7b\x62\xa1\x93\x1b\x1a\x1f\xb0\x54\x3c\x24\x95\x01\x7a\x4b\x74\x19\x13\x4f\xfd\xbf\x3a\xc7\xb0\xc4\xfd\x2d\xe9\xf8\x4c\x94\x26\xef\xe2\xef\x4c\x26\x82\x7c\xf6\x78\x0b\x3c\x9e\xcd\xa7\x67\x9f\xf7\xec\xf3\x92\xbf\x27\xe6\xf3\x96\xf4\x46\x63\xec\x2a\xc7\xd1\x20\x12\x43\x9e\x97\x58\x30\xa1\x20\x1c\x8e\x02\xac\x6b\xee\x8c\x19\xf7\x91\xd4\x7b\xd8\x08\x26\x64\x86\xe9\x02\xff\x14\x7f\x34\x32\xbd\xef\xe3\x96\x62\x94\x2c\x1a\x90\xed\x9d\x24\xbe\x95\x11\x29\x8b\xb4\x52\x35\x5d\x0f\x3c\x44\x1a\xeb\xa3\x49\x71\x16\x92\x13\x3a\xb1\x1d\xcb\x5f\x66\xc7\xf8\x03\x11\x82\xd0\xc9\x49\xac\x88\xf7\xd8\x35\xae\x00\xf5\xec\x90\x97\xdd\x39\xde\xac\x66\x52\x6a\x9f\x7a\x5b\x55\x1f\x91\x7f\x12\x3c\xfa\xa6\x29\x75\xcf\x83\xd6\xc3\x0e\x5a\x2b\xe9\x2b\xd5\x33\xa2\xc5\x00\xf9\xa8\x83\xc0\x53\x3c\xc6\x1c\x53\x27\x41\xd3\x38\x4a\x13\x21\xc6\x9f\xe7\x6a\xf0\x90\xc4\xa6\x93\xb8\x36\x5d\xa5\xde\xf5\x8a\xd0\xc5\x8d\xa6\x8a\x88\xba\x46\x2a\x14\x1c\x24\x63\x4f\x74\xfe\xc1\xe2\x82\xfa\x8a\xf5\x33\x40\x13\x6c\xfd\x14\xe4\xab\xfd\x53\x32\x99\x9c\x22\xd2\x47\x6f\x24\xf6\xc5\x72\x84\x37\xa2\x4a\x61\x51\x6c\xa4\xa6\x36\x13\xeb\x30\x8e\x42\x6e\x71\x2b\x8d\x73\x7d\x33\xad\x9b\x71\x13\xe4\x79\x1f\xc7\x8b\xf4\x24\xd6\xea\x9c\x12\xd8\x61\x4e\x09\x3f\xaa\x78\x02\xda\x10\xdd\x82\xaa\x97\xf2\x06\xb4\x20\x51\x89\x59\x56\x36\x4f\x62\x97\x61\x56\xed\x4a\x3b\x25\x85\xa8\xee\xc4\x90\xec\xcc\x63\x69\x2e\x68\x85\x2a\x47\x51\x4f\xc8\x72\x6f\x4a\x9b\x37\xf6\x43\xd9\x9c\xb8\xef\x24\xff\xa2\x0d\x9a\xe6\x7e\xe8\x49\x32\x44\x5f\x1b\x30\xc9\xae\xc5\x95\xfe\x65\x5c\x7f\xeb\x37\xe4\x85\x58\x0c\xe0\x4f\x14\xa5\xd2\xaf\x41\xc0\x71\x80\x94\x98\xd7\xcc\x26\x80\x20\x8c\xea\x5f\x1c\x23\x77\xbe\x06\x63\x5d\xaa\x66\x0d\x5c\x9c\xbc\x5e\x33\xa5\x13\x08\x9d\xfc\x1b\xf2\xbe\xbb\x52\xdb\xb2\xdb\xcb\xf5\x68\x1e\xeb\xf2\x48\x63\xd0\x3b\x02\x10\x0a\x93\x16\xe5\xe2\xc0\x63\xf3\x0e\xbc\x61\x3c\x1e\x22\x60\xff\xd3\xd9\x92\x18\x44\x1b\x37\x25\x3a\x9f\xc5\xc1\x7c\x3b\xda\x8e\x80\xa3\x83\xc6\x9f\x89\x45\x56\xae\xaf\xc5\xe3\x80\x10\xed\xb9\x34\x91\x5c\x52\x1c\xd4\xda\x58\x8f\x8e\xf6\x3a\xb9\x9d\x9c\x0c\x9f\x06\x10\x8a\x36\x46\x42\xb6\x7b\x7a\x3e\xb3\x0c\xdb\xf4\x81\xce\xc6\x4e\x45\x1f\xb1\x6c\x2c\x11\x63\x6a\x17\x47\x17\xa7\xef\x97\xed\x74\x80\x24\x5a\xaa\x9b\x39\x8e\x31\x44\x32\xdf\xc5\x4c\x8e\x06\xe0\x22\x89\xdb\x92\xf8\xb8\x29\xc8\x30\x70\xbf\x35\x48\x63\x6d\xc3\x25\x3d\x79\x5c\x58\xb5\x69\xfb\xcc\xee\x68\xe3\x5e\x92\x23\x22\xef\xef\x86\x2b\xe0\xe3\xb2\xb8\xee\xae\x67\x11\x8b\x79\xcd\x0f\x39\x6c\x95\xa2\xae\x23\x1a\x68\x2d\xca\xf3\xd6\x31\x0d\xb4\x7a\xd9\xa7\x3a\x86\x29\x3c\x35\x31\x4b\xe1\xb1\xe2\x73\x9e\xcd\xf7\x3b\xc4\xf9\xb0\xe3\x70\x4e\x04\xe9\x5f\xbd\x30\xca\x0e\xc1\xe6\x0a\xbc\x7e\xa7\xc1\xba\x4e\xde\xfb\x27\x47\x11\x52\x39\x21\xa9\x97\xb3\x9c\xe4\xa6\x06\xad\xcc\x82\x56\x2b\x17\xff\x79\x1e\xd6\xc7\xd0\x0b\x8c\x6c\x1b\x98\xf1\xfa\x57\xf1\x10\x5e\x19\xf4\xf5\xea\xe6\x59\x85\xcd\x6b\x6a\x75\x80\x5a\x89\xe0\xf7\x52\x8c\x52\x01\x66\x2a\x7a\xc6\x30\xb3\xa5\x96\x75\xf7\xe4\x88\x4a\x7c\xda\x2f\xaa\x4e\x19\x2f\xc8\xc1\x88\xb9\x31\xfa\x05\xb9\x67\xab\x2d\x98\x3f\x1f\xdd\x0e\xe3\x62\x95\xc3\xe8\x94\x5f\xe6\x64\x79\xc3\x19\x51\x19\xec\xc2\x49\x39\x5d\x10\x3b\x3d\x25\x87\x02\x12\xe1\x5f\x98\xd8\x34\x8f\x38\x4b\xf0\x6f\xa0\x07\xa5\x64\xd7\x45\x34\x47\xd4\xd5\x87\x84\xee\x5e\xf2\x33\x4b\xa0\x1d\x58\x95\x96\x2c\x68\xa2\x04\x49\xf1\x0e\xe6\xce\x41\x60\x2a\x55\xdc\x99\xd8\x0c\x9c\x7c\x3c\x3b\x5f\xa9\x62\x5f\x5b\xc7\x3e\x4b\x0a\xb9\x32\x28\x2e\x88\x3a\x97\xa7\x73\xa3\xeb\xbf\xdb\x07\xcd\x12\xcb\x48\x82\xc4\xf8\x04\x2a\xa1\x05\x75\xc8\x0d\xbf\x75\xa1\x71\x05\x26\x28\x89\x8c\x93\x82\x1e\x1e\xa1\x57\x26\x54\x57\x78\x29\xcd\x8c\x03\x8d\x45\xdf\x2f\x8b\x99\xb3\x72\xc2\xd2\x1c\x4f\x54\xc0\x79\xa8\x4b\xef\x3b\x8c\x8e\xc9\x24\xac\x64\x83\x64\x8a\x09\x1a\xf4\xfe\x1f\x05\x0c\xf2\x81\x78\x3e\x70\xcd\x86\xdf\x8a\x6e\x1a\xcd\x4a\x4a\xbf\xd6\x81\x23\x09\x7e\x28\xa4\x42\x4b\x44\x59\x56\x1e\xbb\xc1\xbc\xed\x20\x81\x01\x79\xc1\x14\xd1\xd0\xc7\x5c\x45\xea\x53\xc4\x91\x23\x31\x17\xc0\x38\xac\xb6\x57\xd7\x94\xd9\xf2\xa8\x56\x0f\xa2\xa6\xf5\x08\x4b\xbb\xad\xa9\xf1\x86\xa9\x9b\x6d\x55\x80\x69\xda\x39\x88\xea\x25\xd0\x11\x06\x8f\xd1\x09\x56\xfa\x8d\x28\x6c\xf4\xad\x8f\x77\x5a\x8b\xe4\x52\x9c\x99\x94\x1c\xd6\x55\x4d\x6a\xf5\xd1\x89\x0a\xf9\x2f\xa7\x97\x85\x84\xfb\x1f\x16\xc2\x55\x67\xfe\xc3\x8f\x8a\xe0\x6c\x94\x9e\x4c\x00\x67\x23\xdd\x4a\x65\x9c\xe6\x32\xff\x50\x09\x97\xa5\x54\xc3\x8f\x94\x6f\x65\x11\x9c\xc7\x2b\x5d\x83\x72\xab\x68\xbf\xa5\x83\x6f\xb4\xc6\x92\xac\x58\xb4\x6a\x46\xca\xfc\x02\x71\x16\x50\x1a\x4c\x28\x0f\xa4\x48\x4e\xce\xb3\x1b\x45\xe8\xc0\xa7\xc8\xf9\xac\x66\xf0\x5a\xd5\x83\xd6\x62\x47\x58\x33\x24\xb6\x2e\x28\xb9\x0e\x31\x10\x17\x53\x49\xc6\x04\xa7\xb5\x3d\xcd\xa7\x17\x02\x77\x89\x08\x3c\x34\x1f\xd6\x0f\x41\xc7\xd6\xf0\x93\x0b\x06\x54\xf8\x12\x01\x81\x20\xe4\x01\x13\xb8\x81\x73\xaf\xff\xdc\xbb\xd0\x47\x14\xc6\x9c\x60\xea\x7a\xf3\x12\xea\xb2\x38\xac\x69\x24\xe2\x65\xb9\x4b\x74\x23\x2e\x17\x63\x80\x29\x1a\x79\xb8\x86\xb5\x9f\xa2\xd0\xb0\x84\x66\x22\xe2\xee\x86\x7c\xbd\x38\x48\xe8\x44\x0d\x8c\x1f\xcf\x0e\xe2\x41\xa7\x04\x89\xec\xc8\x5f\x16\xc8\x45\x80\xf3\x2e\xaa\x5c\x89\x0f\xd2\x5f\x8a\x35\x28\x1e\x11\xf5\xbf\x9d\x1f\xa6\xe1\x06\xe5\xd5\x27\xa7\xda\x11\xf7\xca\x54\x3a\xa7\x63\xc7\x1d\xf8\x8d\xf0\x09\xa1\x04\x7d\x6b\x5d\x8b\x90\xf8\x56\x3a\x66\x3e\x36\x46\xa1\x27\x07\x30\x46\x9e\x48\x03\xe2\x24\x11\x7f\x98\x59\x94\xab\x9e\xf6\xe9\x50\xb4\x18\x63\xc5\xbd\xf5\xf7\x85\x95\xdf\x1f\x97\x9f\x31\x24\x95\xa0\x9a\x1f\x1a\x4a\x86\x85\x12\x86\x2e\x32\x1b\xf3\xa4\x8a\x3a\xdd\xe4\x45\x26\xf5\x3a\xce\x5f\x89\x53\xb0\x5f\xe8\x36\xa5\x79\xbb\x95\x61\xc2\x1d\x86\xfe\x05\x89\xc1\x10\x2f\xdd\xf4\xe8\x28\x38\xdb\xe9\xbe\x73\xc3\x13\xbc\xe9\x75\x25\xdb\xfd\x72\x36\xe9\xbf\x7e\xff\x75\x1c\x16\xc7\xfa\xc2\xa8\x5a\x3b\xd8\x17\x50\x58\x62\xc8\xcf\x3b\x8d\x0a\x69\x25\x84\x34\x6e\xfa\x23\x43\x89\x94\x13\x86\x05\xe9\xef\x25\xb7\xd3\x8d\x4e\x15\x77\x79\x0b\x1a\x82\x5c\x97\x28\xf3\x42\xde\x49\x05\xa3\x4b\x39\x35\x33\xdb\x69\x77\x31\xa9\x3a\xfa\x0d\x58\x43\x7b\xf6\x13\x0d\xe9\xd6\x07\x0a\x24\xf2\x83\xc5\xfb\xe0\xe9\x5e\x05\xa1\x72\x7b\x33\x4b\x5a\xb1\xbb\x29\x7a\x55\xd2\xdb\x65\xe1\xc8\xc3\x35\xce\x41\x03\xb4\x6d\x3a\x9f\x99\xfa\x00\x56\x5d\x9d\xfc\x0a\xdf\xcf\xae\x6d\x24\xfe\xee\x96\x6d\xf3\xa2\x65\x2b\xc3\x1b\x93\x3a\x49\x18\x3d\xc5\x42\x0d\x93\x2b\x15\x64\xd8\x10\x1e\x99\x37\x78\xdc\x56\x57\x38\x5d\xb7\x24\xf7\x1c\xbb\xff\x9d\x5c\x5e\xa9\xce\x34\x98\x54\x46\x1f\x6d\x15\xc8\xb8\x07\x05\x47\x8b\xb3\x8d\x1c\x7b\x9e\x58\xd3\x7e\xa5\x98\x49\x96\x9a\xa3\xa9\x38\x4c\x2a\xae\x8d\xc2\x70\x74\x60\xee\xc1\x72\x18\x4f\x6a\x4b\xe7\xd2\xe8\x4a\x90\x23\x74\x00\x01\x92\xd3\xbc\xe0\xd3\x6d\x86\xf8\x88\x48\x16\x8f\xf8\xa9\x05\xc6\x2e\x91\x5c\xc0\xce\xc3\x74\x22\xa7\x3a\x32\x27\x3e\x06\x42\xc1\x27\x34\x54\x93\x10\x15\x0e\x9b\x93\xaf\x92\x45\x95\xc3\x4c\x71\xc4\x4c\xea\x73\x69\x65\xdd\x72\xfa\xf2\x16\x52\x6e\x1f\x49\x10\xbd\x95\x5a\x35\xa1\xc4\x0f\xfd\x01\xf4\xd2\x47\x66\x23\x61\x00\x9b\x1b\xfd\x6e\xf4\xb4\x98\x53\x98\x67\x11\x24\xf6\x17\x41\x8f\xcf\xcc\xe4\x64\x19\x3d\x6d\xca\xc3\xb8\xbd\x4e\x2e\xc7\x0e\xa3\xae\x80\x11\x96\x37\xba\x18\x1f\x92\x08\x92\x83\x86\x0f\xcb\xb1\x8d\x6e\x23\x96\xf5\xba\xbb\xdd\x6a\x9e\xe5\x59\x62\xf1\x2c\x82\x1f\xe5\xe9\x67\x79\x16\x3d\x6c\xc2\xb2\xf8\x3a\xc3\x78\x3a\x20\x19\x8c\xb1\x74\xa6\x1d\x78\xa3\xfe\x97\x49\xd7\xbf\x99\x62\x0a\xd8\x0f\xe4\xbc\x63\xfa\xa9\x59\x11\xc1\x42\x1f\xed\x8c\xa7\x48\x1a\x65\x9a\x24\xc8\x6b\x8c\x44\xa7\x96\xb3\x59\x5f\x56\xf0\x64\xe5\x13\xdd\x98\xcf\x49\x3d\xf7\x34\x57\xd1\x30\xc1\xca\xa1\xac\xe5\xc0\x89\xb9\xb4\xd6\xc5\xb7\x05\x9d\xb0\xa7\x8e\x0d\xdc\x44\x51\x7e\xf9\x0c\xca\x48\x76\xf1\x7a\xa5\x9d\x3a\x69\x90\xb6\x32\x3d\x6b\x91\x3e\x4e\x8b\xa1\x2a\x76\x29\x65\xc7\xc8\x99\xda\x44\x7f\x43\x32\xf2\x29\x9e\x09\x19\xdd\xae\x21\x24\x2a\xb6\x54\xba\x60\xf3\x9f\x76\xd2\xf3\x2c\xba\x15\x21\xba\x7b\x4f\x75\x52\xf3\x65\x87\x13\x89\x39\x41\x1d\x6d\xc0\x62\x4e\x25\xba\x4d\x56\x5b\x12\x5f\x0f\x44\x58\x08\xf9\xc4\x43\x3c\xde\xfa\xb2\xbb\x60\xb8\x8c\x01\x5f\x82\xe3\xe9\x32\xb2\x6c\x0c\x88\xc2\xd9\xaf\xef\xf5\x6e\x2d\x36\x97\x42\xc7\xb0\x74\xe9\x7d\x73\x22\x2e\xaa\x24\xab\xfb\x9b\xa9\x3c\xa2\xf3\x18\xec\x98\x79\x1e\xbb\x51\x53\xe7\x4b\x27\xb3\xdf\x29\x2e\x61\x4c\xb0\xe7\x8a\xc1\x4a\x02\xf4\xe7\x4c\xae\x55\xf1\x71\x94\x4d\x65\xbf\xc8\x6c\x4d\x66\x5e\xe8\x45\x8f\xa1\x75\xfa\xe0\x67\x2b\xbb\xca\x7a\x38\xe5\x78\x6c\xfd\xcc\x74\xc8\xac\x07\x58\xcf\x0b\x09\x8f\x3f\xdb\xdb\xaa\xea\xa7\x5d\x7a\x32\x8b\x84\xce\x54\xb3\x7e\x9b\x25\x0f\xeb\x41\x6e\x6f\xfc\x67\x2b\x83\xcb\x7a\x18\x65\x53\xa5\xcc\xb3\x12\xe9\xd6\xac\xf1\x4e\xb9\xa2\xd4\xcb\x24\x97\x4c\xa7\xc2\x92\x53\x4c\xb8\x46\x7f\x0d\xe2\xea\xc1\xa9\xd4\x8c\x92\x58\x32\xba\xbc\xbc\x14\xd7\x69\x1e\xb5\xde\x6e\x44\xc2\xb1\xdf\xa7\x8d\xcf\xef\x82\x06\x0c\x11\x75\x87\xc9\xfe\x9b\xa2\xfd\x3e\x98\xad\x59\x62\xaf\xc6\xd4\x5c\x56\x9e\xb1\x1b\xba\x2a\xe3\x35\x51\x77\x0d\x18\x07\x62\xda\x68\x3b\xd6\xab\x6e\xca\xa9\xaf\xa9\x67\xa9\xf8\xcc\xba\x9c\x9a\x1c\x18\x07\x6f\x51\xa8\x10\xea\x24\xde\x22\xf0\x98\x8b\x33\x03\x68\xd1\x83\xe4\x1c\x04\x58\x4e\x24\xa6\xae\x55\xe1\xf7\x8c\x63\x8c\x00\xdc\xd7\xb7\x09\x39\xf7\xd4\x00\xc9\xb8\x39\x24\x6a\x6a\xbe\x95\xfb\xad\xd4\x6d\xe9\x46\xa9\x9b\xb2\xb4\xa2\xde\x5f\x2d\xf0\x53\x7a\x0f\x37\xeb\xa4\xd2\x6f\x66\x9c\x15\x44\xf5\xc0\x23\x47\x93\x5c\x6b\x6a\x10\x53\xd2\xb9\xcc\xfa\x8f\xcb\x35\xb8\x54\x8c\x53\xff\xd7\x66\xaa\xfe\x61\xec\xf3\xd2\x6c\x58\x5f\x1a\xe3\xbc\x4c\x61\xab\xf9\x00\xd2\x97\xa5\x19\x81\x5f\xfe\xd7\x7f\xab\x5e\x2f\x2f\xb5\xca\x5c\xbe\x3f\xfa\xd7\xe1\x65\xea\x36\xe3\x5e\x5f\x18\xa1\x51\xfb\xfd\xe3\x83\x4b\x03\xfb\xe3\xe9\x65\x07\xde\xb1\x1b\x3c\xc3\x7c\x0d\xe6\x2c\xd4\xae\x55\x51\x89\x92\xb4\x13\x36\x86\x5e\x37\xea\xae\x0f\xd1\x45\xd4\x68\xd9\x5b\x3c\x3e\x4c\x94\xa9\xcc\x18\x0b\xa6\x18\x95\x78\x88\xf3\x07\x2e\xfd\x79\x3b\xf2\xb9\x06\x37\x6b\xb9\x59\xef\x9a\x34\x35\xc8\xac\x35\xbe\x84\x14\xae\xd9\xff\xcf\xb0\x1f\x5e\x02\xba\x11\x76\xf7\x3f\x83\xf6\xbf\x97\x21\x00\x99\xef\xe8\x54\x0f\x9d\xab\x10\x1d\xe1\xbe\xf4\xe7\x77\x44\xd9\x23\x57\x18\xfc\xf9\x3f\xfa\x5b\x0f\xe2\x37\xb4\x5f\xb4\xf3\x11\x12\xdf\x68\x79\x14\x24\xd3\xca\xc5\x53\x24\x20\xc0\xdc\x27\x42\xdf\xef\x2e\x19\x08\x6c\xaa\x84\xf0\xe8\xac\xa5\xa5\x04\xc7\x4c\xe2\x4e\x8c\xa2\x19\xac\xd3\x53\x79\x4a\xa1\xa3\xb3\x55\x7a\xe1\x3d\xee\x5d\xed\xa0\xa2\x60\x4b\x2b\x5c\x85\xdb\x29\x77\x31\x25\xb1\x51\xc6\x83\x40\xde\xb1\x35\x52\x94\xd6\xdd\x1d\x58\x69\x6a\x71\x3c\x73\x2a\x0e\xf9\x85\xe9\x52\x41\xfb\xf4\x56\x97\x9a\x05\xe8\x39\x44\x66\x14\x18\xcd\x2b\x78\xd5\x00\xef\xa6\xec\xc4\x33\xe4\x0d\x2b\xf3\xa5\x63\xd6\xe2\x4c\x69\x05\xd5\xd8\x45\xdc\x5d\xdc\x2f\x6e\xa9\xfa\xc6\xe7\x84\x75\x4a\x5a\x8c\x42\x74\x50\xd8\xa6\x0b\x0f\x60\xa4\x9f\x46\x0f\xcd\x8f\x37\xd1\xec\xef\x97\x4f\x71\x86\x9a\x21\x7a\x2a\x65\xb0\x92\x27\x2c\x77\xc1\x4e\x0c\x3e\xb7\xfa\x14\x25\xff\x40\x2b\x39\x3b\x90\x92\x98\x4b\x5c\x83\x96\xa5\x35\xb1\xbc\x5b\x51\x22\x1f\x0a\x88\x4c\x32\x76\x73\xb7\xf0\x2c\xfa\x34\x0e\xdb\x37\xf8\x1b\x7d\xba\x34\xed\xb9\x02\x01\xbd\x36\x8c\x36\xd0\x9e\xb3\x35\xda\x6b\x77\xfb\xbb\x1b\xed\xcd\xf1\x78\xb7\xbd\x37\xda\xc3\x6d\x17\xf5\xfb\xdd\x3d\x17\xf5\x76\x9c\x8d\xf4\xcb\xd1\xd2\x73\x64\x5b\xe9\xf3\xda\x34\xdc\xf5\x46\xdf\x80\x17\x10\x70\x34\xf1\xd1\x40\x79\x35\x76\xa3\xab\x78\x09\xec\x70\x2c\x6d\xa5\x93\xa1\x18\x40\x4b\x9f\xdb\x69\xca\xae\x24\xf1\xce\xf6\x46\xf5\xa2\x37\xe7\x41\xa0\x85\x02\x32\x8c\xc8\x18\x46\xec\xae\x11\x83\x85\x90\x7d\xe8\x03\x5a\x4a\x41\xc5\x60\xdd\xb0\xa6\xdd\x84\x1d\x9d\x48\x99\x3b\xba\x4b\xc7\x61\x7e\x01\x78\x7c\x38\x24\x0f\xde\x45\x12\xdd\xff\x1b\xe9\x49\x12\x7d\x27\x54\xb7\xdd\xeb\xb6\xbb\x5b\xe7\xbd\xfe\x60\xab\x37\xe8\x6f\x76\xba\x5b\x1b\xbd\xcd\xfe\x1f\x69\x0f\xeb\xa0\x48\xa1\xc7\xf6\x60\x63\xbb\xb3\xb1\xdd\xef\x77\x77\xad\x1e\xf1\x89\x0e\x68\xf5\x3b\xdb\x9d\x6e\xfa\x22\xeb\x52\x12\x57\x53\xa2\xe0\x0d\x6e\x2f\xff\xeb\x28\xbd\x39\x37\xf3\xac\xf5\x4f\x5b\xeb\xb3\xa7\x9f\xa0\x85\xa2\x23\xad\x65\x17\x6a\xa4\x37\x97\xe4\x65\xdf\xdc\x44\x4a\xae\x4e\xc8\x99\x44\x83\x53\x3c\xc5\x4c\xc0\x62\xc6\x60\x49\x5e\x60\x61\x59\xb1\x7d\x17\xfb\xab\xb3\xc1\xef\x68\x87\x50\x33\x00\x2d\x36\xc7\x1a\x93\x84\x05\x66\x09\xb6\x69\x7a\x19\x63\x84\x7a\x83\x84\x87\x36\x4a\xf8\x4e\x86\x09\x77\x32\x4e\xb8\x93\x81\x42\xdd\xd0\x04\x8b\x6c\xaf\x24\x69\x79\x81\xd9\x15\x33\x86\x21\x19\xa7\x4a\xa2\xbe\xcc\xb3\x4c\xea\x19\xb4\xf6\x7d\xf4\x95\x51\xf8\x84\x47\xf1\xd9\x20\xab\x6d\x9c\x1c\x96\x2a\x5f\x31\x03\xb7\x01\xaa\x76\xfa\x6b\x82\x68\x89\xd6\xe6\x50\xbb\x38\x83\x43\x24\xe4\x1a\x58\x19\x6d\x75\xb8\x41\x5d\xde\x18\xfc\x99\xce\x2a\xd6\xa2\x99\x89\xd9\x92\xa8\xca\x33\xaa\x20\xac\xb8\x5d\x3e\xd4\x08\x0f\x87\x03\xb0\x47\x40\xcc\x87\x23\xce\xae\x30\x97\x2c\x20\x4e\xb4\x37\x33\x1c\xcd\x25\x16\x43\x42\x87\xd9\xe2\x10\x89\x49\x0c\x4d\xf5\x01\xc6\x87\x84\x0d\xa3\x25\xe5\x04\x6e\x3b\x7f\xed\x85\xf2\xa0\x01\x71\x06\x30\x1c\x3a\x8c\x8a\xd0\xc7\x7c\xc8\xc6\x63\x81\xad\xcb\x7b\x8a\xc9\x38\x6d\x6b\x4b\x1e\x7a\xdb\xbd\xde\xf6\x4e\xb7\xbf\xd1\xed\x76\xbb\x59\x85\x36\x93\xb1\xdd\xcd\xde\xd6\xe6\xa2\xde\xdb\x95\xbd\xb7\x76\x77\x77\x17\xf5\xde\xab\xec\xbd\xb3\xdd\xef\xdb\x42\x2a\x49\x1a\xf9\x8b\x88\x69\xa1\x48\x0a\xe2\xa8\xcc\x5c\xc8\x71\xa2\x32\x43\xa1\x9d\xdf\xfa\x87\x56\xf6\x41\xc9\x60\x65\x6d\xfc\x47\xad\xd3\x27\xa6\x79\xf5\x65\xfb\x65\x61\x75\xaf\x5b\x8c\x9a\x73\xc5\x6f\x4a\xc7\x6a\x73\x1f\xe8\x7a\xa6\xbb\x2e\x0a\x02\x2d\x7d\xf8\xaf\xfd\xe1\xed\x87\xf3\x76\xe6\x75\x12\x3e\x9d\x59\x37\x16\xc7\x77\x19\x9b\x3b\x0f\x13\xef\x61\x96\xee\xf4\xdd\xc6\x2f\xf5\xb9\xaa\x64\xb9\xcd\x0a\x7d\xed\x3a\x21\xd0\xea\x91\x4f\x47\xc4\xbf\x7e\xeb\xf0\x83\xf0\xfd\x76\x0f\x5d\xdc\x1e\xfd\x71\xfd\xea\xfc\xfa\xf8\x14\x25\x8c\xa9\xb8\x3b\xf5\x99\x31\x35\x37\x50\x95\x31\xa7\x7f\x2f\xde\xf4\x6b\x59\xd3\x2f\xe3\x8c\x99\x34\x82\x64\x8a\x5e\x81\x33\x6b\xe0\x03\xb8\xd0\x43\x91\x7a\xab\x83\xc0\xfc\x3d\x6a\xa6\x54\x5e\x71\x82\x34\x80\xcc\x67\x07\xb0\xe8\x2b\x56\x75\x72\xe6\x85\x3e\x35\x0b\xc6\x0a\x7a\xb4\xba\x09\xab\xc4\x5d\xed\xc0\x59\x59\x3b\xbd\xf8\x3f\x88\xa2\xc6\xb5\x68\xfb\x2d\x1b\x80\xc6\x4f\x4d\xc8\xda\x81\x5f\xcd\x02\xae\x91\xcd\x00\x88\x0b\x2f\xa1\x67\xf3\x27\x2f\x69\xef\xd3\xc1\xdb\x70\x3e\x3a\xe2\x87\xf4\x96\xef\x63\x7f\xa7\xbf\x39\xb9\xbe\xba\x22\x07\xb3\x44\xd2\x0b\x2a\xc7\x95\x4a\xbb\x77\x2f\x69\xf7\x6a\xa5\xdd\x2b\x91\xb6\x6f\x70\xd4\xb9\x44\xa9\x82\x27\xfe\x1d\xec\x0a\x8d\xcb\xb3\x20\x5f\xd3\xac\x8c\xe4\x9d\xfb\x50\xbc\x53\x47\xf0\x4e\x09\xbd\x4d\x2e\x17\x4d\xb0\x37\x57\xfb\x3f\x3a\x1a\x92\xa9\x68\x84\xbc\x29\xa4\xeb\xbe\x5c\xed\x91\x7f\x6d\xb8\xe1\x6f\x9f\x8f\x66\xb3\xad\xcf\xb3\xf7\xde\xfc\x6b\xcf\x7f\x7b\xba\xf1\xcb\xfc\xfa\x78\x35\xad\x86\x57\xe3\xbd\x3e\x7f\xdc\x99\xf4\x27\xdb\xef\xce\xdd\x8b\x7f\x5d\xa0\xfe\x95\x78\xb7\xdb\xbf\xfa\xf5\x60\x63\x1e\xb3\x24\x5f\xc8\xaf\xd4\x9d\xdf\x4b\x87\x7b\xb5\x3a\xdc\x2b\xd3\xe1\xd4\x27\xcd\x30\x27\xe3\x39\xfc\xf2\xe9\xdc\xd4\x49\x1c\xc0\x69\xb4\xc8\x9f\xdc\xaf\x66\x16\x0c\x4c\x15\xc5\x46\x2c\xd9\xb8\x98\x1e\x4e\x6f\xfc\xdf\x5f\x05\x9f\x4e\xc6\x47\x7d\xef\x18\x5f\x05\xee\xe6\x1f\x07\x31\x4b\xf2\xb7\xc3\x95\xb1\x64\xf3\x3e\x1c\xd9\xac\x63\xc8\x66\x19\x3f\x04\xe6\xb0\x3a\x66\xac\x3d\x42\x7c\xb5\xe9\x25\xc7\x9d\x1a\x43\xff\xbc\x71\x41\x0e\xa7\x5f\xa9\xc5\x84\x2f\x81\xbb\xf9\xf9\x75\xc2\x84\xa6\x97\x4e\x97\x71\x67\xeb\x3e\xdc\xd9\xaa\xe3\xce\xd6\x62\xee\x4c\x91\x88\x8b\x0a\x58\x3b\xc1\xe9\xcd\xce\xdb\x66\xc9\x43\x9f\x55\x8e\x76\x0f\x17\x72\xea\xea\x56\x71\xea\xb7\x13\x7c\xd4\x67\xc7\xf8\x8b\xbb\xf1\xfb\xab\x84\x51\x0b\x2e\xc5\x2e\x35\xa8\xfe\xbd\x0c\xaa\x5f\x6b\x50\xfd\x12\x16\x25\x46\x23\x15\xb2\x30\x45\x33\x1c\x9d\x24\xc7\x14\xe2\xba\x61\x95\x4c\xb8\xfa\xfd\xf5\xd7\x4f\x9a\xf6\x98\x09\xef\x67\x6f\xf6\xbe\x7c\xf8\xf5\x73\xcc\x84\xba\x9b\x90\xcb\x18\xb0\xb1\x7d\x1f\x06\xd8\xbd\x8b\x0c\xb0\xdf\x66\x3c\x2c\xf2\x4c\xcc\x41\x04\x20\x4f\x2f\x68\xe9\x32\x64\x95\x64\x6f\x5f\x7d\xee\x2a\xd9\x7f\x4d\xe9\xff\x8c\xa7\xee\xc6\x61\xe4\x29\x8a\x85\x2b\xcb\x48\xdd\xbb\x0f\xa5\x7b\x75\x84\xee\x95\x7a\xce\xa8\x74\x58\x5c\x09\xb4\xc6\x11\xe2\xc3\x58\x8c\xdb\x9f\x27\xd3\xf1\x87\xbd\xc9\xdb\x53\xf1\x6e\x76\xf8\x29\x21\xaf\xf1\x70\xf9\x3d\x89\xb4\xe6\x75\x2d\xb3\x21\x1f\x57\xae\x03\x15\xd9\x0b\x2c\x07\xf0\xf1\xf5\x87\xf6\xe1\xef\xed\xbd\x41\xb4\x90\x6c\x4a\xcd\x29\x62\xd2\x36\xf8\x56\xb6\xd3\x45\xc4\x76\x8f\xdc\x76\x37\x3c\xea\x7a\xfe\x75\xf7\x7a\xec\xec\x08\x22\xd1\x96\xf0\xbe\xcc\x76\xed\xb9\xb0\x8a\x57\xe3\xd2\x07\x8a\xf2\xde\x64\xcb\xdd\xdd\xbd\xee\x7a\xdc\x71\x67\x9b\x93\x1d\xe4\x8d\x76\x84\x37\x9e\xd0\x2f\x1b\xee\x74\x24\xbe\xfc\xe3\xff\xfd\xf3\xf0\xf7\xf3\xd3\x7d\xf8\xd9\x10\xdb\xd1\x7c\x79\x99\x1e\xbe\xb4\x60\x13\x01\xab\x9b\xdd\xcd\xd5\x35\xcd\x06\xfd\xf3\xf5\xfb\x8b\xb3\xf3\xc3\xd3\x78\x68\xe8\x6e\xae\xea\xfd\xfd\x44\x94\xf6\x29\x4e\xd5\xbe\x37\xd9\x62\x7c\xab\x3b\x23\x61\x77\x87\x61\x25\xa8\x29\xbf\x72\xfa\xdb\xee\x64\x2c\xbf\xf4\x90\xb3\x6a\x73\xef\x75\x44\xc7\xea\x22\x22\xac\x50\xe3\xa7\xba\x81\xf5\x5c\x7c\xe2\xf3\x6d\x2a\xae\x47\x7d\x71\xec\xbf\xf9\xb2\x35\xfa\x3d\x38\xd8\x79\x8d\x5a\x2b\xff\x17\x00\x00\xff\xff\x84\x79\x99\x0f\x7e\xa3\x00\x00") +var _fleetManagerYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x6b\x73\xdb\x38\xb2\xf6\x77\xff\x8a\x7e\x35\xef\x96\x77\xa6\x2c\x59\x92\xef\xaa\x93\x53\xe5\xc4\x4e\xe2\xdd\xc4\xc9\xf8\x32\x99\xcc\xd4\x96\x0c\x91\x90\x84\x98\x04\x68\x00\xb4\xad\x9c\x3d\xff\xfd\x14\x00\x5e\x40\x12\xa4\x68\x3b\x17\x67\xc7\xfe\x92\x88\x04\x1a\x8d\xa7\x2f\x68\x00\x0d\x90\x45\x98\xa2\x88\x8c\x60\xa3\xd7\xef\xf5\xe1\x27\xa0\x18\xfb\x20\xe7\x44\x00\x12\x30\x25\x5c\x48\x08\x08\xc5\x20\x19\xa0\x20\x60\x37\x20\x58\x88\xe1\xe8\xe0\x50\xa8\x47\x97\x94\xdd\x98\xd2\xaa\x02\x85\x84\x1c\xf8\xcc\x8b\x43\x4c\x65\x6f\xe5\x27\xd8\x0f\x02\xc0\xd4\x8f\x18\xa1\x52\x80\x8f\xa7\x84\x62\x1f\xe6\x98\x63\xb8\x21\x41\x00\x13\x0c\x3e\x11\x1e\xbb\xc6\x1c\x4d\x02\x0c\x93\x85\x6a\x09\x62\x81\xb9\xe8\xc1\xd1\x14\xa4\x2e\xab\x1a\x48\xb8\x63\x70\x89\x71\x64\x38\xc9\x29\x77\x22\x4e\xae\x91\xc4\x9d\x35\x40\xbe\xea\x03\x0e\x55\x51\x39\xc7\xd0\x09\x11\x45\x33\xec\x77\x05\xe6\xd7\xc4\xc3\xa2\x8b\x22\xd2\x4d\xca\xf7\x16\x28\x0c\x3a\x30\x25\x01\x5e\x21\x74\xca\x46\x2b\x00\x92\xc8\x00\x8f\xe0\x04\xfb\xf0\x1a\x49\xd8\xf7\xaf\x11\xf5\xb0\x0f\x2f\x82\x58\x48\xcc\xe1\x14\x7b\x31\x27\x72\x01\xa7\x86\x20\xbc\x0c\x30\x96\xf0\x56\x37\xc3\x57\x00\xae\x31\x17\x84\xd1\x11\x0c\x7a\xc3\x5e\x7f\x05\xc0\xc7\xc2\xe3\x24\x92\xfa\xe1\x72\xba\x7f\x3f\x79\xbd\xff\xe2\xf4\x67\x37\x7d\x83\xc5\x09\x16\x12\xf6\xdf\x1f\xa9\x4e\x9a\xfe\x01\xa1\x42\x2a\x82\x02\xd8\x14\xf6\x5f\x9c\x82\xc7\xc2\x88\x51\x4c\xa5\xe8\xad\xa8\xbe\x63\x2e\x54\xf7\xba\x10\xf3\x60\x04\x73\x29\x23\x31\x5a\x5f\x47\x11\xe9\x29\xc9\x89\x39\x99\xca\x9e\xc7\xc2\x15\x80\x12\xc7\x6f\x11\xa1\xf0\xf7\x88\x33\x3f\xf6\xd4\x93\x9f\xc1\x90\x73\x13\x13\x12\xcd\xf0\x32\x92\xa7\x12\xcd\x08\x9d\x39\x09\x8d\xd6\xd7\x03\xe6\xa1\x60\xce\x84\x1c\xed\xf6\xfb\xfd\x6a\xf5\xec\x7d\x5e\x73\xbd\x5a\xca\x8b\x39\xc7\x54\x82\xcf\x42\x44\xe8\x4a\x84\xe4\x5c\x23\xa0\xd8\x5c\xe7\x73\xe4\x89\xf5\xeb\xc1\x48\xd7\x9b\x61\x69\xfe\x03\x4a\x8d\x39\x52\x04\x8e\xfc\x91\x7a\xfe\x9b\x91\xe6\x5b\x2c\x91\x8f\x24\x4a\x4a\x71\x2c\x22\x46\x05\x16\x69\x35\x80\xce\xb0\xdf\xef\xe4\x3f\x01\x3c\x46\x25\xa6\xd2\x7e\x04\x80\xa2\x28\x20\x9e\x6e\x60\xfd\x93\x60\xb4\xf8\x16\x40\x78\x73\x1c\xa2\xf2\x53\x80\xff\xcf\xf1\x74\x04\x9d\x9f\xd6\x73\xb1\xae\x9b\xb2\x62\xbd\xc4\x62\xc7\xaa\x5c\x00\x24\x29\x07\x61\xb1\x2f\x22\x0e\x43\xc4\x17\x4a\x35\x65\xcc\xa9\xd0\x66\x73\x5d\x2e\x5b\x06\x6e\x1d\x73\xce\xb8\x58\xff\x1f\xe2\xff\xef\x52\x10\x0f\x55\xd9\xe7\x8b\x23\xff\x31\xc2\xa7\x99\xab\x05\xed\x15\x96\xa0\xbb\xaa\x9c\x53\xd6\x01\x27\x66\x59\x31\x92\x16\x93\x68\x66\x75\xb1\x6b\x4a\x88\xe4\x41\x84\x38\x0a\xb1\x4c\xec\x32\x2d\xe2\xe2\x34\x2f\xb9\x4e\xfc\x4e\x9d\x28\xda\x49\x41\x3c\x5a\x11\xbc\x21\x42\xd6\x8a\x41\xbd\x54\x9e\x2d\x62\x42\x10\x35\x54\x14\xa0\x74\x8a\x23\x28\x57\x51\x0e\xb3\x50\xad\x46\x3c\x15\x7c\x85\x44\x32\x5e\x8e\x6f\xe2\xb0\x4f\x75\xe9\xc7\x08\x73\x81\xc1\x5a\xa8\xdf\x5d\xe6\xac\x6e\x95\x58\x2d\x14\x3c\xa7\xf8\x36\xc2\x9e\xc4\x7e\xa2\xfa\xcc\xd3\x3e\xd7\x7f\x14\x56\xac\xfe\xf0\x2d\x0a\xa3\xc0\x06\x3f\xfd\xdb\xea\xf7\x0f\xcd\xcb\xea\x3b\x77\x43\x29\xad\xf5\xbc\x6a\xa7\x49\xfd\x8c\xd2\x28\x05\xe4\x58\xb0\x98\x7b\x58\xac\x81\x88\xbd\xb9\x8a\xae\x6e\xe6\x58\x85\x36\x10\xa2\x5b\x12\xc6\x21\x24\xc1\x09\x78\x28\x42\x9e\x0a\x02\xe6\x48\xc0\x04\x63\x0a\x1c\x23\x6f\x9e\x41\x2a\x92\x20\xc1\xd6\xda\xe7\x18\x71\xcc\x47\xf0\xe7\xbf\x2a\x8a\xeb\x61\x2a\x39\x0a\x5a\x7a\xe9\x17\xa6\xb4\xe5\xa7\x0b\xe2\x3e\x53\xb1\x5e\x56\x47\x05\x22\x8c\x06\x0b\x40\xb1\x9c\x33\x4e\x3e\x9b\xe8\x4c\x87\x6e\x40\xa8\x81\x00\x85\x18\x18\x9f\x21\x4a\x84\xa9\x84\x0c\x36\xec\x86\x62\x5e\x7c\xc3\xa6\xa6\x4a\x84\x3d\x32\x25\x2a\x2e\x32\xdc\xf4\x1e\xa3\x21\x25\xbc\x9d\xe0\xab\x18\x17\x9d\x56\xb3\xd6\x15\xeb\xbd\xc2\xf2\x24\xe9\xd5\x7d\x75\xb1\x48\xb0\xa4\x96\x2d\xda\xfd\x40\xe4\xfc\x25\x22\x01\xf6\x5f\x70\xac\x31\x32\xce\xe1\xcb\xf0\xd3\x40\xb9\xd6\xfb\x24\x14\x80\x1b\x12\x30\x65\x31\xf5\xf5\xd8\x7b\x90\x0b\x7e\xb3\x3f\x78\x24\xb1\x42\xb3\xbc\x37\xfb\x83\xfb\x22\x99\x57\xad\x85\x6a\x3f\x96\x73\x90\xec\x12\x6b\x63\x24\xf4\x1a\x05\xc4\xb7\x41\xda\xf8\x41\x40\xda\xb8\x3f\x48\x1b\xcb\x40\x3a\x17\x98\x03\x65\xb2\xe4\xa7\x90\xe7\x61\x91\x38\x6a\xe3\x7b\x6d\xe0\x36\x7f\x10\xe0\x36\xef\x0f\xdc\xe6\x32\xe0\x8e\x59\xc5\x16\x6f\x88\x9c\x5b\x1e\xfa\xe8\x00\xf0\x2d\x11\x52\xd4\xc7\x0b\x8f\x15\xba\x2f\x3a\xfc\x57\xa0\x5b\x16\x18\x2d\x1d\xc5\xc1\x15\x54\xa0\x8a\x3c\x72\xaf\xe8\xe3\x00\x4b\xec\x1c\xd8\xcd\xab\x25\x63\xfb\xbf\x33\x4e\xce\xd4\xf0\xac\xc6\x75\x33\x92\x5b\x56\x33\x65\xdc\xac\xf7\xe4\x31\x00\xe2\x16\x7e\x83\x9f\x75\x65\xe4\x87\x84\x12\x21\x39\x92\xaa\xe7\xd3\xfb\x0e\xf8\x00\x43\x43\xd0\xd4\x55\xec\xac\x01\xa2\xbe\xe1\x8e\x4c\x81\x48\xbd\x18\x12\x08\xa6\xa6\x52\xf2\x01\x4d\xb9\x67\x62\x84\x8e\xe0\x2a\xc6\x7c\x61\x89\x99\xa2\x10\x8f\x00\x89\x05\xf5\xea\x84\xff\x1e\xf3\x29\xe3\xa1\x6e\x11\x79\x26\x54\xa2\x80\xa8\xa9\x35\xe7\x8c\xb2\x58\x40\x88\x28\xd5\x2b\x1f\x4d\x4a\x2f\x17\x11\x1e\xc1\x84\xb1\x00\x23\x6a\xbd\x51\xf2\x27\x1c\xfb\x23\x90\x3c\xc6\x8d\x01\xd2\xb0\x3e\x7c\x3f\xd0\x8a\x51\x18\x30\x7e\x0c\xe3\xdd\xec\xf7\x35\xef\x84\xd1\xfb\xfb\xbf\x32\x89\xfa\x55\x13\x35\xaa\x1a\x3d\x32\xf3\xc3\xea\x34\xe7\x29\x1e\x79\x8a\x47\x9e\xe2\x11\x15\x8f\x18\x9f\xf2\x80\xa8\xa4\x40\xe0\x2f\x1b\x9b\x3c\x0c\xc6\x32\x81\xfb\xc7\x29\x69\x08\x62\xc8\x35\x87\x20\xad\xc2\x9a\xea\x48\xdb\x6a\xc5\xb3\x6e\x5d\xc3\x10\x89\x98\x70\xaf\x69\x78\x6a\xe6\x99\x86\x3e\xae\xb0\xe7\x10\x79\x73\x48\x88\xe9\x25\x17\x04\x82\xd0\x59\xe0\x8c\x22\x54\xec\x51\x7a\xaf\x82\x92\x1e\xe8\x09\x2e\x36\x7b\x54\x37\x19\x42\x72\x8e\x74\x80\xa2\x4a\xea\x09\xac\xb2\x6d\x55\xc1\x04\x31\x05\xca\xb1\x9c\x63\x2a\x95\xde\x65\x71\x16\x4e\x21\xfe\x0f\x0b\x52\x74\x9f\x9e\x33\xdf\xd2\x12\xe7\xfc\xdf\xda\xa0\x70\x9a\x6a\xb3\xa1\xba\xcd\xb4\xfd\x92\xce\x7b\xb4\x08\x18\xf2\x8b\x46\x5b\x67\xb2\xe7\xa7\x27\x78\x46\xaa\xbe\x62\x89\x99\xa6\xd5\x6a\x56\x6d\x0e\xcf\xef\x45\x35\xad\x56\xa1\x7a\xef\xa0\xf1\x07\x5b\x55\x7b\xcf\xc4\xd7\x5f\x56\x2b\xc6\x3d\x9e\x87\xa3\x1f\x35\x92\x4e\x57\xe7\x1e\x10\x49\x97\x48\x3c\x45\xd2\x4f\x91\x74\x0a\xd2\x17\x8e\xa4\x33\xb2\x6f\xd1\xed\x7e\x10\xb0\x1b\xec\x1f\x25\x79\x0f\x27\x66\x9f\xe4\x01\xed\x2d\xa3\xe9\x64\xe4\x0c\xf3\x50\x1c\x33\x99\xfa\x80\x07\xb4\x5f\x43\xaa\x79\x26\x31\x65\x7c\x42\x7c\x1f\x53\xc0\x44\xef\x28\x4d\xb0\x87\x62\x81\xf3\x68\x83\x88\x56\xd3\x0d\x60\xc5\xba\xe9\xce\x14\x8d\xc3\x09\xd6\xeb\x38\x79\x86\x89\x0e\x6d\x3c\x44\x61\x82\x93\x18\x2b\x09\x70\x88\x30\x6d\x96\x77\xaf\x7a\x3f\xe4\x64\xe6\x2b\x2e\xae\x9e\xe5\xf1\x1d\xf6\xb3\x0d\x42\xf0\x19\x16\x74\x55\x9a\xa9\x8b\x8d\xd9\xde\x0f\x82\xd9\xde\x31\x0a\xf1\x0b\x46\xa7\x01\xf1\xe4\xfd\xf1\x73\x91\xa9\x77\x96\x0a\x0f\x5d\x32\xd7\x3b\x1f\x4b\x33\xaf\x49\x76\x22\xbd\x64\x88\x32\x4b\x81\x44\x64\x90\xff\x90\xd3\xc3\xaf\xb8\x74\xbd\x4f\x21\xae\x9b\x15\xc2\xcd\x9c\x04\x29\x96\x74\xa6\x81\x2d\xcd\x07\xdb\xcf\x04\xad\xd9\x65\x3e\x7f\x72\x51\xb3\x36\xac\x1d\x4b\xe2\x69\x92\x47\xa9\xa6\x70\xcd\xf6\xde\xd1\x60\x01\x3c\xdb\xa2\x67\x02\xa7\x73\xbf\xc4\xa5\x21\x8e\x8b\xd3\x35\xd7\x2a\xb2\x99\xc2\xb5\x99\xb1\xd5\xec\xaf\x8b\xbb\x80\xd4\x66\xdb\xbb\x64\x0d\x4b\x20\x81\xef\x17\xd2\x97\x33\x7c\xe0\x0e\x61\xbd\xaa\xfb\x65\xc2\x79\x8b\x52\xa7\x3e\x64\x2f\x80\xfa\x1c\xf9\x25\x0d\xff\xa6\x28\xde\xd1\x43\x1c\x99\x78\xf1\xd7\x18\xf3\xc5\x03\xc2\x7a\x07\x99\x4e\x7d\xa0\x7e\x87\xf8\xf5\xf1\x22\xf7\x85\xa3\xfa\xbf\x6e\xa0\xfe\xd0\x25\xef\xa7\xbc\xb3\x36\xa3\xf7\xbd\x32\x48\x23\x34\xb3\x44\xb5\xb4\xb8\x20\x9f\xef\x52\x9c\x71\x1f\xf3\xe7\x8b\xbb\x34\x80\x11\xf7\xe6\x8e\x35\xde\x80\xc5\xfe\x38\xe2\xec\x9a\xf8\xd8\x91\xdd\xda\x98\xf3\x29\xe2\x28\x62\x5c\x69\x88\x26\x03\x19\x99\xba\xa1\x59\x95\x7a\x5f\x2a\xf4\x75\x06\x68\xc3\x2e\xf6\x5b\xf3\x0a\xdf\x74\xc0\xb6\x81\x28\x8e\xd7\x4f\x2e\xbf\x8d\xcb\x7f\xf2\x5c\x8f\xcd\x73\x35\xba\x15\x9d\x19\xbb\xce\xf5\x92\xf9\xbd\x7d\x4c\x52\x3d\xcb\x33\xa9\x31\xe8\x36\xbe\xc7\x2c\xde\x3f\x12\x0f\x94\x76\xec\xbb\x39\x22\x83\xc6\x93\x1b\x7a\x72\x43\xd9\xdf\xf7\x77\x43\xc4\xbf\x83\x13\xfa\xba\xd1\x56\xba\x24\x3b\x96\x8b\xa8\xd6\xd7\x21\xcf\x63\x31\x95\x77\xf4\x6e\xc6\x13\xa4\x75\xe1\x66\x4e\xbc\x39\x4c\x70\xc0\xe8\x2c\xcd\xf3\x5f\x15\xc9\x02\xc9\x67\xad\x11\x4d\xee\x6d\x3f\xa1\xd3\xc6\xaf\xc1\x5f\xc0\xb1\xa5\x78\x3c\xb9\xb6\x27\xd7\x96\xfd\x7d\x21\xd7\xb6\x92\x17\x50\x74\x12\x66\x0c\xc9\x77\x93\x4f\xd8\x93\x27\x78\x8a\x39\xa6\x5e\xd6\x8e\xc9\xe1\x60\xfa\x65\xea\x1a\xb9\x32\x64\x49\x6c\x46\x89\x6f\x33\x66\x2a\x09\xc9\x09\x9d\x65\x8f\x2f\x09\x5d\x5e\x68\xae\xba\xd2\x54\x48\x99\xc5\x28\xf3\x02\x49\xde\x82\xe5\x2b\x54\x2b\xd6\x4f\xe5\x64\xad\x9f\xca\x89\x5a\x3f\x25\x93\x59\xf6\x8f\x4e\x99\x91\x38\x14\x77\xeb\x78\xab\x5e\x29\x2e\xaa\x85\x08\x95\x78\x66\x25\xd1\x28\xe6\x96\x97\xd2\x3c\x37\x17\xd3\xca\x95\x16\x41\x41\xf0\x6e\xba\x6c\x0c\x4b\xd5\xb2\xa4\x04\xf6\x78\xe3\xc0\xa3\x0e\x13\xd0\x96\xe4\x57\x74\xd5\x89\x0d\x68\x41\x22\x87\x5d\xd5\x16\xcf\xc6\x91\x71\x51\xed\x9c\x95\xb2\x03\xa4\xf7\x02\xa4\xe8\x85\xef\x8c\x82\x56\x28\x37\x8b\x88\x73\xb4\x28\xbd\x71\x16\x6f\xed\x48\x8a\x6b\xd9\xdf\x48\xfe\x55\x1b\x34\xc5\xc3\x38\x90\x64\x8c\x3e\xb7\x00\xc9\x3e\x43\x9b\xff\x15\x7c\x77\xe7\x37\x14\xc4\x58\x8c\xe0\x4f\x94\x6c\x81\xaf\x41\xc4\x71\x84\x94\x98\xd7\xcc\xe0\x2d\x08\xa3\xfa\x17\xc7\xc8\x5f\xac\xc1\x54\x1f\x31\x5b\x03\x1f\x67\xaf\xd7\xcc\x91\x07\x42\x67\xff\x82\xb2\xf3\xad\xd5\xb6\xe2\xb4\xb0\x99\xcd\x63\x7d\xac\x71\x0a\x7a\x24\x57\x91\x90\x5e\xce\xf4\x71\x14\xb0\x45\x0f\x5e\x32\x9e\xfa\x78\xd8\xff\x70\x7a\x47\x0e\x92\x80\xcb\xa1\xf3\x45\x1e\x4c\xdb\x49\x18\x01\x47\x07\xad\x9b\x49\x45\xe6\xd6\xd7\x6a\x1a\x1f\x24\xb1\x52\x1b\xc9\x65\x97\x7a\x58\x13\xe2\x24\x25\xd7\x2b\x45\x60\x05\x9c\x46\x10\x8b\x2e\x46\x42\x76\x07\x7a\x2f\xed\x2e\xb0\xe9\x44\xcc\xd6\x4e\x45\xa7\x46\xb6\x96\x88\x31\xb5\xf3\xa3\xf3\x93\x37\x77\xad\x74\x80\x24\xba\x53\x35\x93\x46\x31\x46\xb2\x5c\x65\xca\x78\x88\xe4\x08\x7c\x24\x71\x57\x92\x10\xb7\x25\x19\x47\xfe\x97\x26\x69\xac\x6d\x7c\x47\x4f\x9e\x5e\x88\xd2\xb6\x7c\x61\x56\xd3\xba\x96\xe4\x88\xc8\x87\xbb\xe1\x1a\xfa\xd8\x15\x98\xdd\x37\x87\xb0\xba\x1f\xf9\x35\x87\x2d\x27\xeb\x3a\xa2\x81\xce\xb2\xfd\x59\x1d\xd3\x40\x67\x50\x7c\xaa\x63\x98\xca\x53\x13\xb3\x54\x1e\x2b\x9c\xcb\x30\x3f\x2c\xf9\xf2\xeb\x8e\xc3\x25\x11\xe4\x7f\xcd\xc2\x70\x25\xaf\x96\x2e\x66\xf9\x46\x83\x75\x93\xbc\xf7\xdf\x1f\x25\x4c\x95\x84\xa4\x5e\x5e\x97\x24\x37\x37\x6c\x15\xd6\x14\x3a\xa5\xf8\x2f\x08\xb0\x4e\x1f\xaf\x00\xd9\x35\x34\xd3\x04\x8b\x6a\xf2\x9c\x8b\xfa\x7a\x7d\xf1\xa2\xc2\x96\x35\xb5\x3e\x40\xad\x65\xf0\x5b\x29\x86\x53\x80\x85\x9b\x38\x52\x9a\xc5\x2b\x92\x74\xf5\x2c\xb5\x24\xcd\xd2\x4b\x6e\x95\x48\x97\x46\x60\xc2\xfc\x94\xfd\x8a\xdc\x8b\xa7\x24\xcc\x5f\x88\x6e\xc7\xe9\x25\x13\xe3\x24\x3b\xaf\x90\x11\xde\x72\x46\xe4\xa2\x5d\xc9\x70\xd3\x17\x59\xe5\xd9\x6d\x28\x22\x09\xff\x95\x89\x4d\xfb\x88\xd3\xc1\x7f\x0b\x3d\x70\x76\xbb\x29\xa2\x39\xa2\xbe\x4e\xee\xb9\xff\x55\x1d\xc5\x0e\xda\x81\x95\xf3\xa8\x41\x1b\x25\xc8\x0e\xdd\x30\x7f\x01\x02\x53\xa9\xe2\xce\xcc\x66\xe0\xfd\xbb\xd3\xb3\x95\x3a\xf8\xba\x3a\xf6\xb9\xa3\x90\x6b\x83\xe2\x8a\xa8\x4b\xeb\x6b\x37\xfa\xde\x36\x3b\x41\x2c\xb3\x8c\x2c\x48\x4c\x33\x47\x09\xad\xa8\x43\x69\xf8\x6d\x0a\x8d\x6b\x38\x41\x59\x64\x9c\x1d\xc4\x09\x08\xbd\x34\xa1\xba\xe2\x4b\x69\x66\x1a\x68\x2c\x6b\xdf\x15\x33\x17\xe5\x84\xa5\x49\x2b\x54\xc4\x79\xac\xaf\xcc\xf3\x18\x9d\x92\x59\x5c\x0b\x83\x64\x0a\x04\x4d\x7a\xff\x8f\x0a\x07\xe5\x40\xbc\x1c\xb8\x16\xc3\x6f\xd5\x6f\x9a\xcc\x4a\x9c\xad\xf5\xe0\x48\x42\x18\x0b\xa9\xd8\x12\xc9\xea\x68\xc0\x6e\x30\xef\x7a\x48\x60\x40\x41\x34\x47\x34\x0e\x31\x57\x91\xfa\x1c\x71\xe4\x49\xcc\x05\x30\x0e\xab\xdd\xd5\x35\x65\xb6\x3c\x39\x63\x87\xa8\x29\x3d\xc1\xd2\x2e\x6b\xce\x66\x63\xea\x17\x4b\x55\x68\x9a\x72\x1e\xa2\x3a\x23\x64\x82\x21\x60\x74\x86\x95\x7e\x23\x0a\x1b\x43\xab\xf1\x5e\x67\x99\x5c\xaa\x33\x13\x47\x92\xad\x2a\xd2\xa8\x8f\x5e\x72\x01\xdf\xdd\xf4\xb2\xb2\x51\xfe\xdd\x42\xb8\xfa\x1d\x7b\xf8\x5e\x11\x9c\xcd\xd2\x0f\x13\xc0\xd9\x4c\x77\x72\x19\xe7\x7b\x90\xdf\x55\xc2\xae\xad\x50\xf8\x9e\xf2\xad\x3d\xbc\xf6\x78\xa5\x6b\x58\xee\x54\xed\xd7\x39\xf8\x26\x6b\x2c\xd9\x8a\x45\xa7\x61\xa4\x2c\x2f\x10\x17\x09\xe5\xc1\x84\xf2\x40\xaa\xcb\x59\x1e\xba\x51\x84\x1e\x7c\x48\x9c\xcf\x6a\x81\xaf\x55\x3d\x68\x2d\x77\x84\x0d\x43\x62\xe7\x9c\x92\xab\x18\x03\xf1\x31\x95\x64\x4a\x70\x7e\x27\x87\x69\x7a\x29\x71\x9f\x88\x28\x40\x8b\x71\xf3\x10\x74\x6c\x0d\x3f\xa5\x60\x40\x85\x2f\x09\x11\x88\x62\x1e\x31\x81\x5b\x38\xf7\xe6\xe6\x5e\xc7\x21\xa2\x30\xe5\x04\x53\x3f\x58\x38\x7a\x57\xe4\x61\x4d\x33\x91\x2e\xcb\x5d\xa0\x1b\x71\xb1\x9c\x03\x4c\xd1\x24\xc0\x0d\xd0\x7e\x48\x42\x43\x47\x9f\x89\x48\xab\x9b\xee\xeb\xc5\x41\x42\x67\x6a\x60\x7c\x77\x7a\x90\x0e\x3a\x0e\x26\x8a\x23\xbf\x2b\x90\x4b\x08\x97\x5d\x94\x5b\x89\x0f\xf2\x5f\x0a\x1a\x94\x8e\x88\xfa\xff\xde\x77\xd3\x70\xc3\xf2\xea\x0f\xa7\xda\x09\x7a\x2e\x95\x2e\xe9\xd8\x71\x0f\x7e\x23\x7c\x46\x28\x41\x5f\x5a\xd7\x12\x26\xbe\x94\x8e\x99\xc6\xa6\x28\x0e\xe4\x08\xa6\x28\x10\x79\x40\x9c\x6d\xa0\x8f\x0b\x8b\x72\xf5\xd3\x3e\x1d\x8a\x56\x63\xac\xb4\xb6\x6e\x5f\x58\xfb\xf2\xe9\xb1\x31\xd3\x25\x07\xab\xe5\xa1\xc1\x31\x2c\x38\x00\x5d\x66\x36\xe6\x49\x5d\xef\x74\x91\x9f\x74\x8e\x43\x77\x8a\x3c\x05\x69\x88\x25\x27\x9e\xe2\x33\xd0\xc1\xe1\x4f\xba\xcc\x5b\xf3\xf4\x04\xd1\x19\xd6\x79\xf2\x8d\x61\xc2\x3d\x86\x7e\x67\x03\x8e\xa5\x9b\x01\x9d\x44\xa7\x3b\xfd\xd7\x7e\xfc\x1e\x6f\x06\x7d\xc9\x76\x3f\x9d\xce\x86\x2f\xde\x7c\x9e\xc6\xd5\xb1\xbe\x32\xaa\x36\x0e\xf6\x15\x16\xee\x30\xe4\x97\x9d\x46\x8d\xb4\xb2\x8e\xb4\x2e\xfa\x3d\x43\x89\x1c\x09\x03\x41\xfe\xfb\x8e\xdb\xe9\x46\xa7\xaa\xbb\xbc\x15\x0d\x41\xbe\x4f\x94\x79\xa1\xe0\x7d\x0d\xd0\x4e\xa4\xae\xcd\x76\xda\x7d\x4c\xaa\xa9\xff\x86\xac\xe9\x7b\xb1\x89\x96\xfd\x96\x24\xc4\x42\xa2\x30\x5a\xbe\x0f\x9e\xef\x55\x10\x2a\xb7\x37\x8b\x5d\xab\x56\x37\x87\x55\x1d\xb5\x7d\x16\x4f\x02\xdc\xe0\x1c\x34\x41\xdb\xa6\xcd\x19\x60\xf9\x35\xad\xba\xdc\xc4\x77\xb1\x6b\x9b\x89\xbf\xba\x65\xdb\x58\x74\x6c\x65\x78\x89\xfd\x24\x19\xe1\x04\x0b\x35\x4c\xae\xd4\x74\xc3\xa6\xf0\xc8\xbc\xc1\xe3\xb6\xba\x4a\x56\xdc\x1d\xd1\xf3\xec\xfa\xf7\x72\x79\x4e\x9d\x69\x31\xa9\x4c\x1a\xed\x54\xba\xf1\x80\x1e\x1c\x2d\xcf\x36\xf2\xec\x79\x62\x43\xf9\x95\x6a\x96\x6b\x6e\x8e\xe6\xa6\x20\x52\x73\xdd\x33\x86\xa3\x03\x73\x7f\xb5\xc7\x78\x76\x27\x54\x29\x0f\xce\xc1\x1c\xa1\x23\x88\x90\x9c\x97\x05\x9f\x6f\x33\xf8\xb1\xb1\xa6\x22\x1f\xe9\x53\x8b\x8c\x7d\xb5\x51\x85\xbb\x00\xd3\x99\x9c\xeb\xc8\x9c\x84\x18\x08\x85\x90\xd0\x58\x4d\x42\x54\x38\x6c\x32\x56\x25\x4b\x4e\xfc\x9a\x4b\x0d\x8c\x39\xd7\x33\x56\xd7\xbf\xb2\x85\xb8\xed\x23\x0b\xa2\xb7\x72\xab\x26\x94\x84\x71\x38\x82\x41\xfe\xc8\x6c\x24\x8c\x60\x73\x63\xd8\x4f\x9e\x56\x93\x02\xcb\x10\x41\x66\x7f\x09\x75\xc5\x12\xbf\xce\xb3\xc4\x12\x59\x26\x4f\xdb\x62\x98\x96\x57\xe8\x09\xec\x31\xea\x0b\x98\x60\x79\xa3\x0f\xd1\x23\x89\xc0\x7c\x61\xe5\xab\x23\xb6\xd1\x6f\x05\xd9\xa0\xbf\xdb\xaf\xc7\xac\x0c\x89\x85\x59\x42\x7f\x4a\x02\x3b\xdb\xdb\x60\x96\x3c\x6c\x03\x59\xfa\x19\x82\x74\x3a\x20\x19\x4c\xb1\xf4\xe6\x3d\x78\xa9\xfe\xd1\x5f\xaf\x49\xdf\xdd\xcc\x31\x05\x1c\x46\x72\xd1\x33\xf5\xd4\xac\x88\x60\xa1\x8f\x99\xa7\x53\x24\xcd\x32\x45\x69\x2d\xcd\x91\xe8\x35\x22\x5b\xf4\x65\x15\x4f\xe6\x9e\xe8\xa6\x38\x67\xf7\xb0\xe5\xb9\x8a\x06\x04\x2b\x87\xb2\x11\x81\xf7\xe6\x63\x33\x3e\xbe\xad\xe8\x84\x3d\x75\x6c\xe1\x26\xaa\xf2\x2b\x67\x50\x26\xb2\x4b\xd7\x2b\xed\xd4\x49\xc3\xb4\x95\xe9\xd9\xc8\xf4\x71\x7e\x89\x89\x82\x4b\x29\x3b\x46\xde\xdc\xee\xf4\x17\xec\x46\x39\xc5\x33\xeb\x46\xbf\x6f\x3a\x92\x1c\x92\x74\x2e\xd8\xfc\xbb\x9b\xd5\x3c\x4d\x6e\x33\x4c\xee\xcc\x57\x95\xd4\x7c\xd9\xe3\x44\x62\x4e\x50\x4f\x1b\xb0\x58\x50\x89\x6e\xb3\xd5\x96\xcc\xd7\x03\x11\x16\x43\x21\x09\x10\x4f\xb7\xbe\xec\x2a\x18\x2e\x52\xc2\x17\xe0\x05\xfa\xfa\x17\x36\x05\x44\xe1\xf4\xd7\x37\x7a\xb7\x16\x9b\x8f\x39\xa5\xb4\xf4\x95\x79\x1a\xe8\xf4\x06\x18\x5d\xdf\x4c\xe5\x11\x5d\xa4\x64\xa7\x2c\x08\xd8\x8d\x9a\x3a\x5f\x78\x85\xfd\x4e\x71\x01\x53\x82\x03\x5f\x8c\x56\x32\xa2\xbf\x14\x72\xad\xaa\x8f\x93\x6c\x2a\xfb\x45\x61\x6b\xb2\xf0\x42\x2f\x7a\x8c\xad\xd4\xf8\x5f\xac\xec\x2a\xeb\xe1\x9c\xe3\xa9\xf5\xb3\x50\xa1\xb0\x1e\x60\x3d\xaf\x24\x3c\xfe\x62\x6f\xab\xaa\x9f\xf6\x95\x11\x45\x26\x74\xa6\x9a\xf5\xdb\x2c\x79\x58\x0f\x4a\x7b\xe3\xbf\x58\x19\x5c\xd6\xc3\x24\x9b\x2a\x07\xcf\x4a\xa4\x5b\xb3\xc6\x3b\xe5\x8a\x72\x2f\x93\x7d\x1c\x2a\x17\x96\x9c\x63\xc2\x35\xfb\x6b\x90\xde\xfa\x93\x4b\xcd\x28\x89\x25\xa3\x8b\x8b\x0b\x71\x95\xe7\x51\xeb\xed\x46\x24\x3c\xfb\x7d\x5e\xf8\xec\x3e\x6c\xc0\x18\x51\x7f\x9c\xed\xbf\xa9\xbe\x3f\x84\xb3\x35\x4b\xec\xf5\x9c\x9a\x8f\x8c\x15\xec\x86\xae\xca\x74\x4d\xd4\x5f\x03\xc6\x81\x98\x32\xda\x8e\xf5\xaa\x9b\x72\xea\x6b\xea\x59\x2e\x3e\xb3\x2e\xa7\x26\x07\xc6\xc1\x5b\x3d\x54\x0c\xf5\x32\x6f\x11\x05\xcc\xc7\x85\x01\xb4\xea\x41\x4a\x0e\x02\x2c\x27\x92\xf6\xae\x53\xe3\xf7\x8c\x63\x4c\x08\x3c\xd4\xb7\x09\xb9\x08\xd4\x00\xc9\xb8\xf9\x88\x97\x39\xab\xed\xf6\x5b\xb9\xdb\xd2\x85\x72\x37\x65\x69\x45\xb3\xbf\x5a\xe2\xa7\xf4\x1e\x6e\xd1\x49\xe5\x6d\x16\x9c\x15\x24\xf7\x78\x25\x8e\x26\xfb\x1c\x89\x61\x4c\x49\xe7\xa2\xe8\x3f\x2e\xd6\xe0\x42\x01\xa7\xfe\xd5\x66\xaa\xfe\x63\xec\xf3\xc2\x6c\x58\x5f\x18\xe3\xbc\xc8\x69\xab\xf9\x00\xd2\x97\x9c\x1b\x81\x5f\xfc\xd7\x7f\xab\x5a\xcf\x2e\xb4\xca\x5c\xbc\x39\xfa\xe7\xe1\x45\xee\x36\xd3\x5a\x9f\x18\xa1\x49\xf9\xfd\xe3\x83\x0b\x43\xfb\xdd\xc9\x45\x0f\x5e\xb3\x1b\x7c\x8d\xf9\x1a\x2c\x58\xac\x5d\xab\xea\x25\xca\xd2\x4e\xd8\x14\x06\xfd\xa4\x3a\xa1\x80\xd2\xde\x68\xd9\x5b\x18\x1f\x66\xca\xe4\x32\xc6\x8a\x29\x9a\xdd\x7a\x99\xe6\x0f\x5c\x84\x8b\x6e\xe2\x73\x0d\x6f\xd6\x72\xb3\xde\x35\x69\x6b\x90\x45\x6b\x7c\x06\x39\x5d\xb3\xff\x5f\x80\x1f\x9e\x01\xba\x11\x76\xf5\x3f\xa3\xee\xbf\xee\xd2\x01\x64\xda\xd1\xa9\x1e\x3a\x57\x41\x98\xe7\x17\xe1\xe2\x9e\x2c\x07\xe4\x12\x43\xb8\xf8\xdb\x70\xeb\xab\xf8\x0d\xed\x17\xed\x7c\x84\xcc\x37\x5a\x1e\x05\xc9\xfc\xc6\xa1\x39\x12\x10\x61\x1e\x12\xa1\xbf\xcb\x26\x19\x08\x8c\xb5\x22\xf1\xe4\xd4\x9b\xa5\x04\xc7\x4c\xe2\xf4\xfb\x89\xc9\x60\x9d\x1f\x19\x53\x0a\x9d\x1c\x8e\xd2\x0b\xef\x69\xed\x7a\x07\x95\x04\x5b\x5a\xe1\x6a\xdc\x8e\xdb\xc5\x38\x62\xa3\x82\x07\x81\xb2\x63\x6b\xa5\x28\x9d\xfb\x3b\x30\x67\x6a\x71\x3a\x73\xaa\x0e\xf9\x95\xe9\x52\x45\xfb\xf4\x56\x97\x9a\x05\xe8\x39\x44\x61\x14\x98\x2c\x6a\xb0\x6a\xc1\x77\x5b\x38\xf1\x35\x0a\xc6\xb5\xf9\xd2\x29\xb4\xaa\x54\xbe\xa8\xa7\x0a\xfb\x88\xfb\xcb\xeb\xa5\x25\x55\xdd\xf4\xc8\xa6\x4e\x49\x4b\x59\x48\xce\x6c\xda\xfd\xc2\x23\x98\xe8\xa7\xc9\x43\xf3\xe3\x65\x32\xfb\xfb\xc7\x87\x34\x43\xcd\x74\x7a\x2e\x65\xb4\x52\xee\x58\xe9\x62\xdc\x94\x7c\x69\xf5\x29\x49\xfe\x81\x4e\x76\x76\x20\xef\x62\x29\x71\x0d\x3a\x96\xd6\xa4\xf2\xee\x24\x89\x7c\x28\x22\x32\xcb\xd8\x2d\xdd\x9e\xbb\xac\x69\x1c\x77\x6f\xf0\x17\x6a\xda\x99\xf6\x5c\xc3\x80\x5e\x1b\x46\x1b\x68\xcf\xdb\x9a\xec\x75\xfb\xc3\xdd\x8d\xee\xe6\x74\xba\xdb\xdd\x9b\xec\xe1\xae\x8f\x86\xc3\xfe\x9e\x8f\x06\x3b\xde\x46\xde\x72\xb2\xf4\x9c\xd8\x56\xfe\xbc\x31\x0d\x77\xbd\x55\x1b\xf0\x13\x44\x1c\xcd\x42\x34\x32\xdf\x89\xd5\xa7\x6f\x05\xf6\x38\x96\xb6\xd2\xc9\x58\x8c\xa0\xa3\xcf\xed\xb4\x85\x2b\x4b\xbc\xb3\xbd\x51\xb3\xe8\xcd\x79\x10\xe8\xa0\x88\x8c\x93\x6e\x8c\x13\xb8\x1b\xc4\x60\x31\x64\x1f\xfa\x80\x4e\xfa\x69\x53\x0d\x4d\xb7\x0d\x1c\xbd\x44\x99\x7b\xba\x4a\xcf\x63\x61\x85\x78\x7a\x38\xa4\x4c\xde\x47\x12\x3d\xbc\x8d\xfc\x24\x89\xbe\xcb\xb9\xdf\x1d\xf4\xbb\xfd\xad\xb3\xc1\x70\xb4\x35\x18\x0d\x37\x7b\xfd\xad\x8d\xc1\xe6\xf0\x8f\xbc\x86\x75\x50\xa4\x52\x63\x7b\xb4\xb1\xdd\xdb\xd8\x1e\x0e\xfb\xbb\x56\x8d\xec\x13\xb7\x9d\x61\x6f\xbb\xd7\xcf\x5f\x14\x5d\x4a\xe6\x6a\x1c\x0a\xde\xe2\xab\x63\xff\x39\x4a\x6f\xce\xcd\x3c\x69\xfd\x8f\xad\xf5\xc5\xd3\x4f\xd0\x41\xc9\x91\x56\xd7\x45\x98\xf9\x8d\xa3\x65\xd9\xb7\x37\x11\xc7\x95\x87\x25\x93\x68\x71\x8a\xa7\x9a\x09\x58\xcd\x18\x74\xe4\x05\x56\x96\x15\xbb\xf7\xb1\xbf\x26\x1b\xfc\x86\x76\x08\x0d\x03\xd0\x72\x73\x6c\x30\x49\x58\x62\x96\x60\x9b\x66\x50\x30\x46\x68\x36\x48\xf8\xda\x46\x09\xdf\xc8\x30\xe1\x5e\xc6\x09\xf7\x32\x50\x68\x1a\x9a\x60\x99\xed\x39\x92\x96\x97\x98\x5d\x35\x63\x18\xb2\x71\xca\x11\xf5\x15\x9e\x15\x52\xcf\xa0\xb3\x1f\xa2\xcf\x8c\xc2\x07\x3c\x49\xcf\x06\x59\x65\xd3\xe4\xb0\x5c\xf9\xaa\x19\xb8\x2d\x58\xb5\xd3\x5f\x33\x46\x1d\x5a\x5b\x62\xed\xfc\x14\x0e\x91\x90\x6b\x60\x65\xb4\x35\xf1\x06\x4d\x79\x63\xf0\x67\x3e\xab\x58\x4b\x66\x26\x66\x4b\xa2\x2e\xcf\xa8\xa6\x63\xd5\xed\xf2\xb1\x66\x78\x3c\x1e\x81\x3d\x02\x62\x3e\x9e\x70\x76\x89\xb9\x64\x11\xf1\x92\xbd\x99\xf1\x64\x21\xb1\x18\x13\x3a\x2e\x5e\x0e\x91\x99\xc4\xd8\xdc\x3e\xc0\xf8\x98\xb0\x71\xb2\xa4\x9c\xd1\xed\x96\xaf\xab\x54\x1e\x34\x22\xde\x08\xc6\x63\x8f\x51\x11\x87\x98\x8f\xd9\x74\x2a\xb0\x75\xe9\x6e\x35\x19\xa7\x6b\x6d\xc9\xc3\x60\x7b\x30\xd8\xde\xe9\x0f\x37\xfa\xfd\x7e\xbf\xa8\xd0\x66\x32\xb6\xbb\x39\xd8\xda\x5c\x56\x7b\xbb\xb6\xf6\xd6\xee\xee\xee\xb2\xda\x7b\xb5\xb5\x77\xb6\x87\x43\x5b\x48\x8e\xa4\x91\xff\x10\x31\x2d\x15\x49\x45\x1c\xb5\x99\x0b\x25\x24\x6a\x33\x14\xba\xe5\xad\x7f\xe8\x14\x1f\x38\x06\x2b\x6b\xe3\x3f\x29\x9d\x3f\x31\xc5\xeb\x3f\x92\xe7\x0a\xab\x07\xfd\x6a\xd4\x5c\xba\xbd\xc6\x39\x56\x9b\xef\x78\xac\x17\xaa\xeb\x4b\x41\xa0\xa3\x0f\xff\x75\xdf\xbe\x7a\x7b\xd6\x2d\xbc\xce\xc2\xa7\x53\xeb\x4b\x43\xe9\x37\x88\xcc\xb7\x0a\x32\xef\x61\x96\xee\xf4\x37\x89\x9e\xe9\x73\x55\xd9\x72\x9b\x15\xfa\xda\xf7\x84\x40\x67\x40\x3e\x1c\x91\xf0\xea\x95\xc7\x0f\xe2\x37\xdb\x03\x74\x7e\x7b\xf4\xc7\xd5\xf3\xb3\xab\xe3\x13\x94\x01\x53\xf3\xcd\x93\x27\x60\x1a\x6e\x8e\x76\x81\x33\x7c\x10\x36\xc3\x46\x68\x86\x2e\x64\xcc\xa4\x11\xa4\xfe\xe2\xa7\xc0\x85\x35\xf0\x11\x9c\xeb\xa1\x48\xbd\xd5\x41\x60\xf9\xfe\x73\x9d\x15\xe2\x98\x20\x8d\xa0\xd0\xec\x08\x96\xb5\x62\xdd\x2a\xc6\x82\x38\xa4\x66\xc1\x58\x51\x4f\x56\x37\x61\x95\xf8\xab\x3d\x38\x75\x95\xd3\x8b\xff\xa3\x24\x6a\x5c\x4b\xb6\xdf\x8a\x01\x68\xfa\xd4\x84\xac\x3d\xf8\xd5\x2c\xe0\x1a\xd9\x8c\x80\xf8\xf0\x0c\x06\x36\x3e\x65\x49\x07\x1f\x0e\x5e\xc5\x8b\xc9\x11\x3f\xa4\xb7\x7c\x1f\x87\x3b\xc3\xcd\xd9\xd5\xe5\x25\x39\xb8\xce\x24\xfd\x96\x08\x41\xe8\xec\x7d\xaa\x36\x6d\xa4\x3d\x78\x90\xb4\x07\x8d\xd2\x1e\x38\xa4\x1d\x1a\x1e\x75\x2e\x51\xae\xe0\x99\x7f\x07\xfb\x22\xbe\xbb\x43\x50\xfe\xbc\x87\xab\xcb\x3b\x0f\xe9\xf1\x4e\x53\x87\x77\x1c\xfd\x6d\xf3\x51\x90\x8c\x7b\xe7\x27\x02\xbf\x7f\x1f\xb2\xa9\x68\xc2\xbc\xde\x85\x21\xfe\xb3\xd5\x01\xf9\xe7\x86\x1f\xff\xf6\xf1\xe8\xfa\x7a\xeb\xe3\xf5\x9b\x60\xf1\x79\x10\xbe\x3a\xd9\xf8\xc7\xe2\xea\x78\x55\xfb\x36\xfd\xa1\xf7\x06\xef\xf5\xf1\xdd\xce\x6c\x38\xdb\x7e\x7d\xe6\x9f\xff\xf3\x1c\x0d\x2f\xc5\xeb\xdd\xe1\xe5\xaf\x07\x1b\x8b\x14\x92\xf2\x2d\x73\x4e\x77\xfe\x20\x1d\x1e\x34\xea\xf0\xc0\xa5\xc3\xb9\x4f\xba\xc6\x9c\x4c\x17\xf0\x8f\x0f\x67\xe6\x12\xbf\x11\x9c\x24\x8b\xfc\xd9\xbd\xe8\x66\xc1\xc0\x5c\xf1\xd7\x0a\x92\x8d\xf3\xf9\xe1\xfc\x26\xfc\xfd\x79\xf4\xe1\xfd\xf4\x68\x18\x1c\xe3\xcb\xc8\xdf\xfc\xe3\x20\x85\xa4\x7c\xab\xbb\x0b\x92\xcd\x87\x20\xb2\xd9\x04\xc8\xa6\x0b\x0f\x81\x39\xac\x4e\x19\xeb\x4e\x10\x5f\x6d\xfb\x71\xa2\x5e\x83\xa1\x7f\xdc\x38\x27\x87\xf3\xcf\xd4\x02\xe1\x53\xe4\x6f\x7e\x7c\x91\x81\xd0\xf6\x63\x51\x2e\x74\xb6\x1e\x82\xce\x56\x13\x3a\x5b\xcb\xd1\x99\x23\x91\x5e\x2a\x60\xed\x04\xe7\x5f\x64\xda\x36\x4b\x1e\xfa\xac\x72\xb2\x7b\xb8\x14\xa9\xcb\x5b\x85\xd4\x6f\xef\xf1\xd1\x90\x1d\xe3\x4f\xfe\xc6\xef\xcf\x33\xa0\x96\x7c\xcc\xca\x69\x50\xc3\x07\x19\xd4\xb0\xd1\xa0\x86\x0e\x88\x32\xa3\x91\x8a\x59\x98\xa3\x6b\x9c\x9c\x24\xc7\x14\xd2\x7b\xc3\x6a\x41\xb8\xfc\xfd\xc5\xe7\x0f\xba\xef\x29\x08\x6f\xae\x5f\xee\x7d\x7a\xfb\xeb\xc7\x14\x84\xa6\x2f\x18\xb9\x00\xd8\xd8\x7e\x08\x00\x76\xed\x2a\x00\xf6\xdb\x82\x87\x45\x81\x89\x39\xf4\xf7\xcf\xf5\x82\x96\xbe\x86\xac\xb6\xdb\xdb\x97\x1f\xfb\x4a\xf6\x9f\xf3\xfe\x7f\xc4\x73\x7f\xe3\x30\xf1\x14\xd5\x9b\x27\x5d\x5d\xdd\x7b\x48\x4f\xf7\x9a\x3a\xba\xe7\xf4\x9c\xf9\xd7\x7c\x71\xb1\x9d\x8a\x23\xc4\x87\xa9\x18\xb7\x3f\xce\xe6\xd3\xb7\x7b\xb3\x57\x27\xe2\xf5\xf5\xe1\x87\xac\x7b\xad\x87\xcb\x6f\xd9\x49\x6b\x5e\xd7\x31\x1b\xf2\xe9\xcd\x75\xa0\x22\x7b\x81\xe5\x08\xde\xbd\x78\xdb\x3d\xfc\xbd\xbb\x37\x4a\x16\x92\xcd\x55\x73\xaa\x33\x79\x19\x7c\x2b\xbb\xf9\x22\x62\x77\x40\x6e\xfb\x1b\x01\xf5\x83\xf0\xaa\x7f\x35\xf5\x76\x04\x91\x68\x4b\x04\x9f\xae\x77\xed\xb9\xb0\x8a\x57\xd3\xab\x0f\x54\xcf\x07\xb3\x2d\x7f\x77\xf7\xaa\x1f\x70\xcf\xbf\xde\x9c\xed\xa0\x60\xb2\x23\x82\xe9\x8c\x7e\xda\xf0\xe7\x13\xf1\xe9\x6f\xff\xef\xef\x87\xbf\x9f\x9d\xec\xc3\x2f\xa6\xb3\x3d\x8d\xcb\xb3\xfc\xf0\xa5\x45\x9b\x08\x58\xdd\xec\x6f\xae\xae\x69\x18\xf4\xcf\x17\x6f\xce\x4f\xcf\x0e\x4f\xd2\xa1\xa1\xbf\xb9\x6a\xbe\x83\x9b\x8a\xd2\x3e\xc5\xa9\xca\x0f\x66\x5b\x8c\x6f\xf5\xaf\x49\xdc\xdf\x61\x58\x09\x6a\xce\x2f\xbd\xe1\xb6\x3f\x9b\xca\x4f\x03\xe4\xad\xda\xe8\xbd\x48\xfa\xb1\xba\xac\x13\x56\xa8\xf1\x73\xd3\xc0\x7a\x26\x3e\xf0\xc5\x36\x15\x57\x93\xa1\x38\x0e\x5f\x7e\xda\x9a\xfc\x1e\x1d\xec\xbc\x40\x9d\x95\xff\x0b\x00\x00\xff\xff\xbb\x7e\xcb\xdf\x36\x93\x00\x00") func fleetManagerYamlBytes() ([]byte, error) { return bindataRead( @@ -94,7 +94,7 @@ func fleetManagerYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "fleet-manager.yaml", size: 41854, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + info := bindataFileInfo{name: "fleet-manager.yaml", size: 37686, mode: os.FileMode(420), modTime: time.Unix(1, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/internal/dinosaur/pkg/handlers/metrics.go b/internal/dinosaur/pkg/handlers/metrics.go deleted file mode 100644 index 60ca90de73..0000000000 --- a/internal/dinosaur/pkg/handlers/metrics.go +++ /dev/null @@ -1,163 +0,0 @@ -package handlers - -import ( - "net/http" - "strconv" - "strings" - "time" - - "github.com/getsentry/sentry-go" - "github.com/golang/glog" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/public" - "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/metrics" - "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/presenters" - "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/services" - "github.com/stackrox/acs-fleet-manager/pkg/handlers" - "github.com/stackrox/acs-fleet-manager/pkg/shared" - - "github.com/gorilla/mux" - "github.com/stackrox/acs-fleet-manager/pkg/client/observatorium" - "github.com/stackrox/acs-fleet-manager/pkg/errors" -) - -type metricsHandler struct { - service services.ObservatoriumService -} - -// NewMetricsHandler ... -func NewMetricsHandler(service services.ObservatoriumService) *metricsHandler { - return &metricsHandler{ - service: service, - } -} - -// FederateMetrics ... -func (h metricsHandler) FederateMetrics(w http.ResponseWriter, r *http.Request) { - dinosaurID := strings.TrimSpace(mux.Vars(r)["id"]) - if dinosaurID == "" { - shared.HandleError(r, w, &errors.ServiceError{ - Code: errors.ErrorBadRequest, - Reason: "missing path parameter: central id", - HTTPCode: http.StatusBadRequest, - }) - return - } - - dinosaurMetrics := &observatorium.DinosaurMetrics{} - params := observatorium.MetricsReqParams{ - ResultType: observatorium.Query, - } - - _, err := h.service.GetMetricsByDinosaurID(r.Context(), dinosaurMetrics, dinosaurID, params) - if err != nil { - if err.Code == errors.ErrorNotFound { - shared.HandleError(r, w, err) - } else { - glog.Errorf("error getting metrics: %v", err) - sentry.CaptureException(err) - shared.HandleError(r, w, &errors.ServiceError{ - Code: err.Code, - Reason: "error getting metrics", - HTTPCode: http.StatusInternalServerError, - }) - } - return - } - - // Define metric collector - collector := metrics.NewFederatedUserMetricsCollector(dinosaurMetrics) - registry := prometheus.NewPedanticRegistry() - registry.MustRegister(collector) - - promHandler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{ - ErrorHandling: promhttp.HTTPErrorOnError, - }) - promHandler.ServeHTTP(w, r) -} - -// GetMetricsByRangeQuery ... -func (h metricsHandler) GetMetricsByRangeQuery(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - params := observatorium.MetricsReqParams{} - query := r.URL.Query() - cfg := &handlers.HandlerConfig{ - Validate: []handlers.Validate{ - handlers.ValidatQueryParam(query, "duration"), - handlers.ValidatQueryParam(query, "interval"), - }, - Action: func() (i interface{}, serviceError *errors.ServiceError) { - ctx := r.Context() - params.ResultType = observatorium.RangeQuery - extractMetricsQueryParams(r, ¶ms) - dinosaurMetrics := &observatorium.DinosaurMetrics{} - foundDinosaurID, err := h.service.GetMetricsByDinosaurID(ctx, dinosaurMetrics, id, params) - if err != nil { - return nil, err - } - metricList := public.MetricsRangeQueryList{ - Kind: "MetricsRangeQueryList", - Id: foundDinosaurID, - } - metricsResult, err := presenters.PresentMetricsByRangeQuery(dinosaurMetrics) - if err != nil { - return nil, err - } - metricList.Items = metricsResult - - return metricList, nil - }, - } - handlers.HandleGet(w, r, cfg) -} - -// GetMetricsByInstantQuery ... -func (h metricsHandler) GetMetricsByInstantQuery(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - params := observatorium.MetricsReqParams{} - cfg := &handlers.HandlerConfig{ - Action: func() (i interface{}, serviceError *errors.ServiceError) { - ctx := r.Context() - params.ResultType = observatorium.Query - extractMetricsQueryParams(r, ¶ms) - dinosaurMetrics := &observatorium.DinosaurMetrics{} - foundDinosaurID, err := h.service.GetMetricsByDinosaurID(ctx, dinosaurMetrics, id, params) - if err != nil { - return nil, err - } - metricList := public.MetricsInstantQueryList{ - Kind: "MetricsInstantQueryList", - Id: foundDinosaurID, - } - metricsResult, err := presenters.PresentMetricsByInstantQuery(dinosaurMetrics) - if err != nil { - return nil, err - } - metricList.Items = metricsResult - - return metricList, nil - }, - } - handlers.HandleGet(w, r, cfg) -} - -func extractMetricsQueryParams(r *http.Request, q *observatorium.MetricsReqParams) { - q.FillDefaults() - queryParams := r.URL.Query() - if dur := queryParams.Get("duration"); dur != "" { - if num, err := strconv.ParseInt(dur, 10, 64); err == nil { - duration := time.Duration(num) * time.Minute - q.Start = q.End.Add(-duration) - } - } - if step := queryParams.Get("interval"); step != "" { - if num, err := strconv.Atoi(step); err == nil { - q.Step = time.Duration(num) * time.Second - } - } - if filters, ok := queryParams["filters"]; ok && len(filters) > 0 { - q.Filters = filters - } - -} diff --git a/internal/dinosaur/pkg/metrics/federate_user_metrics.go b/internal/dinosaur/pkg/metrics/federate_user_metrics.go deleted file mode 100644 index 7e22ca990b..0000000000 --- a/internal/dinosaur/pkg/metrics/federate_user_metrics.go +++ /dev/null @@ -1,82 +0,0 @@ -package metrics - -import ( - "github.com/golang/glog" - "github.com/stackrox/acs-fleet-manager/internal/dinosaur/constants" - - "github.com/prometheus/client_golang/prometheus" - pModel "github.com/prometheus/common/model" - "github.com/stackrox/acs-fleet-manager/pkg/client/observatorium" -) - -// FederatedUserMetricsCollector ... -type FederatedUserMetricsCollector struct { - DinosaurMetricsMetadata map[string]constants.MetricsMetadata - DinosaurMetrics *observatorium.DinosaurMetrics -} - -// NewFederatedUserMetricsCollector ... -func NewFederatedUserMetricsCollector(dinosaurMetrics *observatorium.DinosaurMetrics) *FederatedUserMetricsCollector { - return &FederatedUserMetricsCollector{ - DinosaurMetricsMetadata: constants.GetMetricsMetaData(), - DinosaurMetrics: dinosaurMetrics, - } -} - -// Describe ... -func (f FederatedUserMetricsCollector) Describe(ch chan<- *prometheus.Desc) { - for _, metricMetadata := range f.DinosaurMetricsMetadata { - ch <- f.buildMetricDesc(metricMetadata) - } -} - -// Collect ... -func (f FederatedUserMetricsCollector) Collect(ch chan<- prometheus.Metric) { - // collect metric - for _, m := range *f.DinosaurMetrics { - if m.Vector != nil { - for _, v := range m.Vector { - name := string(v.Metric["__name__"]) - - // Check if we have metadata for the given metric - if metadata, ok := f.DinosaurMetricsMetadata[name]; ok { - switch metadata.Type { - case prometheus.GaugeValue, prometheus.CounterValue: - ch <- prometheus.MustNewConstMetric( - f.buildMetricDesc(metadata), - metadata.Type, - float64(v.Value), - f.extractLabelValues(v.Metric)..., - ) - default: - glog.Infof("skipping unsupported federated metric: %v (%v)", name, metadata.Type) - } - } - } - } - } -} - -// buildMetricDesc returns the metric description based on the metricMetadata passed in -func (f FederatedUserMetricsCollector) buildMetricDesc(metricMetadata constants.MetricsMetadata) *prometheus.Desc { - return prometheus.NewDesc( - metricMetadata.Name, - metricMetadata.Help, - metricMetadata.VariableLabels, - metricMetadata.ConstantLabels, - ) -} - -// extractLabelValues gets values of the labels from the given metric -// metricLabels is a label set with the following type map[LabelName]LabelValue -// -// The label values returned needs to be in the order of the variable labels that's specified in the metric description -func (f FederatedUserMetricsCollector) extractLabelValues(metricLabels pModel.Metric) []string { - labelValues := []string{} - metric := f.DinosaurMetricsMetadata[string(metricLabels["__name__"])] - for _, label := range metric.VariableLabels { - label := pModel.LabelName(label) - labelValues = append(labelValues, string(metricLabels[label])) - } - return labelValues -} diff --git a/internal/dinosaur/pkg/metrics/federate_user_metrics_test.go b/internal/dinosaur/pkg/metrics/federate_user_metrics_test.go deleted file mode 100644 index 0fff143bb0..0000000000 --- a/internal/dinosaur/pkg/metrics/federate_user_metrics_test.go +++ /dev/null @@ -1,110 +0,0 @@ -package metrics - -import ( - "testing" - - "github.com/prometheus/client_golang/prometheus" - io_prometheus_client "github.com/prometheus/client_model/go" - pModel "github.com/prometheus/common/model" - "github.com/stackrox/acs-fleet-manager/internal/dinosaur/constants" - "github.com/stackrox/acs-fleet-manager/pkg/client/observatorium" -) - -func TestFederateMetrics_Collect(t *testing.T) { - tests := []struct { - name string - metrics observatorium.DinosaurMetrics - }{ - { - name: "test if correct number of metrics are gathered", - metrics: observatorium.DinosaurMetrics{ - { - Vector: []*pModel.Sample{ - { - Metric: map[pModel.LabelName]pModel.LabelValue{ - "__name__": "kubelet_volume_stats_available_bytes", - }, - }, - }, - }, - { - Vector: []*pModel.Sample{ - { - Metric: map[pModel.LabelName]pModel.LabelValue{ - "__name__": "kubelet_volume_stats_used_bytes", - }, - }, - }, - }, - { - Vector: []*pModel.Sample{ - { - Metric: map[pModel.LabelName]pModel.LabelValue{ - "__name__": "dinosaur_namespace:haproxy_server_bytes_in_total:rate5m", - }, - }, - }, - }, - { - Vector: []*pModel.Sample{ - { - Metric: map[pModel.LabelName]pModel.LabelValue{ - "__name__": "dinosaur_namespace:haproxy_server_bytes_out_total:rate5m", - }, - }, - }, - }, - { - Vector: []*pModel.Sample{ - { - Metric: map[pModel.LabelName]pModel.LabelValue{ - "__name__": "dinosaur_namespace:dinosaur_server_socket_server_metrics_connection_count:sum", - }, - }, - }, - }, - { - Vector: []*pModel.Sample{ - { - Metric: map[pModel.LabelName]pModel.LabelValue{ - "__name__": "dinosaur_namespace:dinosaur_server_socket_server_metrics_connection_creation_rate:sum", - }, - }, - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - collector := NewFederatedUserMetricsCollector(&tt.metrics) - registry := prometheus.NewPedanticRegistry() - registry.MustRegister(collector) - federatedMetrics, err := registry.Gather() - metadata := constants.GetMetricsMetaData() - - if err != nil { - t.Error(err) - } - - if len(federatedMetrics) != len(tt.metrics) { - t.Errorf("expected %v metrics to be gathered", len(tt.metrics)) - } - - for _, metric := range federatedMetrics { - name := *metric.Name - metricMetadata := metadata[name] - var metricType = int32(*metric.Type) - - if metricMetadata.Help != *metric.Help { - t.Errorf("unexpected help '%v' for metric %v", *metric.Help, name) - } - - if metricMetadata.TypeName != io_prometheus_client.MetricType_name[metricType] { - t.Errorf("unexpected type '%v' for metric %v", *metric.Type, name) - } - } - }) - } -} diff --git a/internal/dinosaur/pkg/presenters/data_plane_cluster_status.go b/internal/dinosaur/pkg/presenters/data_plane_cluster_status.go index 3a6becb1ad..3acd05ef92 100644 --- a/internal/dinosaur/pkg/presenters/data_plane_cluster_status.go +++ b/internal/dinosaur/pkg/presenters/data_plane_cluster_status.go @@ -24,16 +24,6 @@ func ConvertDataPlaneClusterStatus(status private.DataPlaneClusterUpdateStatusRe } // PresentDataPlaneClusterConfig ... -func PresentDataPlaneClusterConfig(config *dbapi.DataPlaneClusterConfig) private.DataplaneClusterAgentConfig { - res := private.DataplaneClusterAgentConfig{ - Spec: private.DataplaneClusterAgentConfigSpec{ - Observability: private.DataplaneClusterAgentConfigSpecObservability{ - AccessToken: &config.Observability.AccessToken, - Channel: config.Observability.Channel, - Repository: config.Observability.Repository, - Tag: config.Observability.Tag, - }, - }, - } - return res +func PresentDataPlaneClusterConfig(_ *dbapi.DataPlaneClusterConfig) private.DataplaneClusterAgentConfig { + return private.DataplaneClusterAgentConfig{} } diff --git a/internal/dinosaur/pkg/presenters/metric.go b/internal/dinosaur/pkg/presenters/metric.go deleted file mode 100644 index 9063bbb2b9..0000000000 --- a/internal/dinosaur/pkg/presenters/metric.go +++ /dev/null @@ -1,107 +0,0 @@ -package presenters - -import ( - pmod "github.com/prometheus/common/model" - "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/public" - "github.com/stackrox/acs-fleet-manager/pkg/client/observatorium" - "github.com/stackrox/acs-fleet-manager/pkg/errors" -) - -func convertMatrix(from pmod.Matrix) []public.RangeQuery { - series := make([]public.RangeQuery, len(from)) - - for i, s := range from { - series[i] = convertSampleStream(s) - } - return series -} -func convertVector(from pmod.Vector) []public.InstantQuery { - series := make([]public.InstantQuery, len(from)) - - for i, s := range from { - series[i] = convertSample(s) - } - return series -} - -func convertSampleStream(from *pmod.SampleStream) public.RangeQuery { - labelSet := make(map[string]string, len(from.Metric)) - for k, v := range from.Metric { - if !isAllowedLabel(string(k)) { - // Do not add these labels - continue - } - labelSet[string(k)] = string(v) - } - values := make([]public.Values, len(from.Values)) - for i := range from.Values { - values[i] = convertSamplePair(&from.Values[i]) - } - return public.RangeQuery{ - Metric: labelSet, - Values: values, - } -} -func convertSample(from *pmod.Sample) public.InstantQuery { - labelSet := make(map[string]string, len(from.Metric)) - for k, v := range from.Metric { - if !isAllowedLabel(string(k)) { - // Do not add these labels - continue - } - labelSet[string(k)] = string(v) - } - return public.InstantQuery{ - Metric: labelSet, - Timestamp: int64(from.Timestamp), - Value: float64(from.Value), - } -} - -func convertSamplePair(from *pmod.SamplePair) public.Values { - return public.Values{ - Timestamp: int64(from.Timestamp), - Value: float64(from.Value), - } -} - -// PresentMetricsByRangeQuery ... -func PresentMetricsByRangeQuery(metrics *observatorium.DinosaurMetrics) ([]public.RangeQuery, *errors.ServiceError) { - out := []public.RangeQuery{} - for _, m := range *metrics { - if m.Err != nil { - return nil, errors.GeneralError("error in metric %s: %v", m.Matrix, m.Err) - } - metric := convertMatrix(m.Matrix) - out = append(out, metric...) - } - return out, nil -} - -// PresentMetricsByInstantQuery ... -func PresentMetricsByInstantQuery(metrics *observatorium.DinosaurMetrics) ([]public.InstantQuery, *errors.ServiceError) { - out := []public.InstantQuery{} - for _, m := range *metrics { - if m.Err != nil { - return nil, errors.GeneralError("error in metric %s: %v", m.Matrix, m.Err) - } - metric := convertVector(m.Vector) - out = append(out, metric...) - } - return out, nil -} - -func isAllowedLabel(lable string) bool { - for _, labelName := range getSupportedLables() { - if lable == labelName { - return true - } - } - - return false -} - -// TODO change supported labels to match the metrics labels supported for your service -func getSupportedLables() []string { - return []string{"__name__", "dinosaur_operator_io_cluster", "topic", "persistentvolumeclaim", "statefulset_kubernetes_io_pod_name", "exported_service", "exported_pod", "route"} -} diff --git a/internal/dinosaur/pkg/routes/route_loader.go b/internal/dinosaur/pkg/routes/route_loader.go index 54f32c6286..efbaa3f027 100644 --- a/internal/dinosaur/pkg/routes/route_loader.go +++ b/internal/dinosaur/pkg/routes/route_loader.go @@ -45,7 +45,6 @@ type options struct { Central services.DinosaurService ClusterService services.ClusterService CloudProviders services.CloudProvidersService - Observatorium services.ObservatoriumService DataPlaneCluster services.DataPlaneClusterService DataPlaneCentralService services.DataPlaneCentralService AccountService account.AccountService @@ -88,7 +87,6 @@ func (s *options) buildAPIBaseRouter(mainRouter *mux.Router, basePath string, op s.CentralRequestConfig) cloudProvidersHandler := handlers.NewCloudProviderHandler(s.CloudProviders, s.ProviderConfig) errorsHandler := coreHandlers.NewErrorsHandler() - metricsHandler := handlers.NewMetricsHandler(s.Observatorium) serviceStatusHandler := handlers.NewServiceStatusHandler(s.Central, s.AccessControlListConfig) cloudAccountsHandler := handlers.NewCloudAccountsHandler(s.AMSClient) @@ -142,27 +140,6 @@ func (s *options) buildAPIBaseRouter(mainRouter *mux.Router, basePath string, op apiV1CentralsCreateRouter.HandleFunc("", centralHandler.Create).Methods(http.MethodPost) apiV1CentralsCreateRouter.Use(requireTermsAcceptance) - // /centrals/{id}/metrics - apiV1MetricsRouter := apiV1CentralsRouter.PathPrefix("/{id}/metrics").Subrouter() - apiV1MetricsRouter.HandleFunc("/query_range", metricsHandler.GetMetricsByRangeQuery). - Name(logger.NewLogEvent("get-metrics", "list metrics by range").ToString()). - Methods(http.MethodGet) - apiV1MetricsRouter.HandleFunc("/query", metricsHandler.GetMetricsByInstantQuery). - Name(logger.NewLogEvent("get-metrics-instant", "get metrics by instant").ToString()). - Methods(http.MethodGet) - - // /centrals/{id}/metrics/federate - // federate endpoint separated from the rest of the /centrals endpoints as it needs to support auth from both sso.redhat.com and mas-sso - // NOTE: this is only a temporary solution. MAS SSO auth support should be removed once we migrate to sso.redhat.com (TODO: to be done as part of MGDSTRM-6159) - apiV1MetricsFederateRouter := apiV1Router.PathPrefix("/centrals/{id}/metrics/federate").Subrouter() - apiV1MetricsFederateRouter.HandleFunc("", metricsHandler.FederateMetrics). - Name(logger.NewLogEvent("get-federate-metrics", "get federate metrics by id").ToString()). - Methods(http.MethodGet) - apiV1MetricsFederateRouter.Use(auth.NewRequireIssuerMiddleware().RequireIssuer( - append(s.IAMConfig.AdditionalSSOIssuers.GetURIs(), s.IAMConfig.RedhatSSORealm.ValidIssuerURI), errors.ErrorUnauthenticated)) - apiV1MetricsFederateRouter.Use(requireOrgID) - apiV1MetricsFederateRouter.Use(authorizeMiddleware) - // /cloud_providers v1Collections = append(v1Collections, api.CollectionMetadata{ ID: "cloud_providers", diff --git a/internal/dinosaur/pkg/services/data_plane_cluster.go b/internal/dinosaur/pkg/services/data_plane_cluster.go index 9e8fb02cff..9b93cfd0e2 100644 --- a/internal/dinosaur/pkg/services/data_plane_cluster.go +++ b/internal/dinosaur/pkg/services/data_plane_cluster.go @@ -8,8 +8,6 @@ import ( "github.com/goava/di" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/dbapi" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/config" - "github.com/stackrox/acs-fleet-manager/pkg/client/observatorium" - "github.com/stackrox/acs-fleet-manager/pkg/api" "github.com/stackrox/acs-fleet-manager/pkg/errors" ) @@ -26,7 +24,6 @@ type dataPlaneClusterService struct { di.Inject ClusterService ClusterService CentralConfig *config.CentralConfig - ObservabilityConfig *observatorium.ObservabilityConfiguration DataplaneClusterConfig *config.DataplaneClusterConfig } @@ -46,14 +43,7 @@ func (d *dataPlaneClusterService) GetDataPlaneClusterConfig(ctx context.Context, return nil, errors.BadRequest("Cluster agent with ID '%s' not found", clusterID) } - return &dbapi.DataPlaneClusterConfig{ - Observability: dbapi.DataPlaneClusterConfigObservability{ - AccessToken: d.ObservabilityConfig.ObservabilityConfigAccessToken, - Channel: d.ObservabilityConfig.ObservabilityConfigChannel, - Repository: d.ObservabilityConfig.ObservabilityConfigRepo, - Tag: d.ObservabilityConfig.ObservabilityConfigTag, - }, - }, nil + return &dbapi.DataPlaneClusterConfig{}, nil } // UpdateDataPlaneClusterStatus ... diff --git a/internal/dinosaur/pkg/services/observatorium_service.go b/internal/dinosaur/pkg/services/observatorium_service.go deleted file mode 100644 index a279462a7f..0000000000 --- a/internal/dinosaur/pkg/services/observatorium_service.go +++ /dev/null @@ -1,56 +0,0 @@ -package services - -import ( - "context" - "fmt" - - "github.com/stackrox/acs-fleet-manager/pkg/client/observatorium" - "github.com/stackrox/acs-fleet-manager/pkg/errors" -) - -var _ ObservatoriumService = &observatoriumService{} - -type observatoriumService struct { - observatorium *observatorium.Client - dinosaurService DinosaurService -} - -// NewObservatoriumService ... -func NewObservatoriumService(observatorium *observatorium.Client, dinosaurService DinosaurService) ObservatoriumService { - return &observatoriumService{ - observatorium: observatorium, - dinosaurService: dinosaurService, - } -} - -// ObservatoriumService ... -// -//go:generate moq -out observatorium_service_moq.go . ObservatoriumService -type ObservatoriumService interface { - GetDinosaurState(name string, namespaceName string) (observatorium.DinosaurState, error) - GetMetricsByDinosaurID(ctx context.Context, csMetrics *observatorium.DinosaurMetrics, id string, query observatorium.MetricsReqParams) (string, *errors.ServiceError) -} - -// GetDinosaurState ... -func (obs observatoriumService) GetDinosaurState(name string, namespaceName string) (observatorium.DinosaurState, error) { - state, err := obs.observatorium.Service.GetDinosaurState(name, namespaceName) - if err != nil { - return state, fmt.Errorf("getting central state for %q in namespace %q: %w", name, namespaceName, err) - } - return state, nil -} - -// GetMetricsByDinosaurID ... -func (obs observatoriumService) GetMetricsByDinosaurID(ctx context.Context, dinosaursMetrics *observatorium.DinosaurMetrics, id string, query observatorium.MetricsReqParams) (string, *errors.ServiceError) { - dinosaurRequest, err := obs.dinosaurService.Get(ctx, id) - if err != nil { - return "", err - } - - getErr := obs.observatorium.Service.GetMetrics(dinosaursMetrics, dinosaurRequest.Namespace, &query) - if getErr != nil { - return dinosaurRequest.ID, errors.NewWithCause(errors.ErrorGeneral, getErr, "failed to retrieve metrics") - } - - return dinosaurRequest.ID, nil -} diff --git a/internal/dinosaur/pkg/services/observatorium_service_moq.go b/internal/dinosaur/pkg/services/observatorium_service_moq.go deleted file mode 100644 index 47030a5a3b..0000000000 --- a/internal/dinosaur/pkg/services/observatorium_service_moq.go +++ /dev/null @@ -1,145 +0,0 @@ -// Code generated by moq; DO NOT EDIT. -// github.com/matryer/moq - -package services - -import ( - "context" - "github.com/stackrox/acs-fleet-manager/pkg/client/observatorium" - serviceError "github.com/stackrox/acs-fleet-manager/pkg/errors" - "sync" -) - -// Ensure, that ObservatoriumServiceMock does implement ObservatoriumService. -// If this is not the case, regenerate this file with moq. -var _ ObservatoriumService = &ObservatoriumServiceMock{} - -// ObservatoriumServiceMock is a mock implementation of ObservatoriumService. -// -// func TestSomethingThatUsesObservatoriumService(t *testing.T) { -// -// // make and configure a mocked ObservatoriumService -// mockedObservatoriumService := &ObservatoriumServiceMock{ -// GetDinosaurStateFunc: func(name string, namespaceName string) (observatorium.DinosaurState, error) { -// panic("mock out the GetDinosaurState method") -// }, -// GetMetricsByDinosaurIDFunc: func(ctx context.Context, csMetrics *observatorium.DinosaurMetrics, id string, query observatorium.MetricsReqParams) (string, *serviceError.ServiceError) { -// panic("mock out the GetMetricsByDinosaurID method") -// }, -// } -// -// // use mockedObservatoriumService in code that requires ObservatoriumService -// // and then make assertions. -// -// } -type ObservatoriumServiceMock struct { - // GetDinosaurStateFunc mocks the GetDinosaurState method. - GetDinosaurStateFunc func(name string, namespaceName string) (observatorium.DinosaurState, error) - - // GetMetricsByDinosaurIDFunc mocks the GetMetricsByDinosaurID method. - GetMetricsByDinosaurIDFunc func(ctx context.Context, csMetrics *observatorium.DinosaurMetrics, id string, query observatorium.MetricsReqParams) (string, *serviceError.ServiceError) - - // calls tracks calls to the methods. - calls struct { - // GetDinosaurState holds details about calls to the GetDinosaurState method. - GetDinosaurState []struct { - // Name is the name argument value. - Name string - // NamespaceName is the namespaceName argument value. - NamespaceName string - } - // GetMetricsByDinosaurID holds details about calls to the GetMetricsByDinosaurID method. - GetMetricsByDinosaurID []struct { - // Ctx is the ctx argument value. - Ctx context.Context - // CsMetrics is the csMetrics argument value. - CsMetrics *observatorium.DinosaurMetrics - // ID is the id argument value. - ID string - // Query is the query argument value. - Query observatorium.MetricsReqParams - } - } - lockGetDinosaurState sync.RWMutex - lockGetMetricsByDinosaurID sync.RWMutex -} - -// GetDinosaurState calls GetDinosaurStateFunc. -func (mock *ObservatoriumServiceMock) GetDinosaurState(name string, namespaceName string) (observatorium.DinosaurState, error) { - if mock.GetDinosaurStateFunc == nil { - panic("ObservatoriumServiceMock.GetDinosaurStateFunc: method is nil but ObservatoriumService.GetDinosaurState was just called") - } - callInfo := struct { - Name string - NamespaceName string - }{ - Name: name, - NamespaceName: namespaceName, - } - mock.lockGetDinosaurState.Lock() - mock.calls.GetDinosaurState = append(mock.calls.GetDinosaurState, callInfo) - mock.lockGetDinosaurState.Unlock() - return mock.GetDinosaurStateFunc(name, namespaceName) -} - -// GetDinosaurStateCalls gets all the calls that were made to GetDinosaurState. -// Check the length with: -// -// len(mockedObservatoriumService.GetDinosaurStateCalls()) -func (mock *ObservatoriumServiceMock) GetDinosaurStateCalls() []struct { - Name string - NamespaceName string -} { - var calls []struct { - Name string - NamespaceName string - } - mock.lockGetDinosaurState.RLock() - calls = mock.calls.GetDinosaurState - mock.lockGetDinosaurState.RUnlock() - return calls -} - -// GetMetricsByDinosaurID calls GetMetricsByDinosaurIDFunc. -func (mock *ObservatoriumServiceMock) GetMetricsByDinosaurID(ctx context.Context, csMetrics *observatorium.DinosaurMetrics, id string, query observatorium.MetricsReqParams) (string, *serviceError.ServiceError) { - if mock.GetMetricsByDinosaurIDFunc == nil { - panic("ObservatoriumServiceMock.GetMetricsByDinosaurIDFunc: method is nil but ObservatoriumService.GetMetricsByDinosaurID was just called") - } - callInfo := struct { - Ctx context.Context - CsMetrics *observatorium.DinosaurMetrics - ID string - Query observatorium.MetricsReqParams - }{ - Ctx: ctx, - CsMetrics: csMetrics, - ID: id, - Query: query, - } - mock.lockGetMetricsByDinosaurID.Lock() - mock.calls.GetMetricsByDinosaurID = append(mock.calls.GetMetricsByDinosaurID, callInfo) - mock.lockGetMetricsByDinosaurID.Unlock() - return mock.GetMetricsByDinosaurIDFunc(ctx, csMetrics, id, query) -} - -// GetMetricsByDinosaurIDCalls gets all the calls that were made to GetMetricsByDinosaurID. -// Check the length with: -// -// len(mockedObservatoriumService.GetMetricsByDinosaurIDCalls()) -func (mock *ObservatoriumServiceMock) GetMetricsByDinosaurIDCalls() []struct { - Ctx context.Context - CsMetrics *observatorium.DinosaurMetrics - ID string - Query observatorium.MetricsReqParams -} { - var calls []struct { - Ctx context.Context - CsMetrics *observatorium.DinosaurMetrics - ID string - Query observatorium.MetricsReqParams - } - mock.lockGetMetricsByDinosaurID.RLock() - calls = mock.calls.GetMetricsByDinosaurID - mock.lockGetMetricsByDinosaurID.RUnlock() - return calls -} diff --git a/internal/dinosaur/pkg/workers/clusters_mgr.go b/internal/dinosaur/pkg/workers/clusters_mgr.go index b8453cb0e7..fe37584cba 100644 --- a/internal/dinosaur/pkg/workers/clusters_mgr.go +++ b/internal/dinosaur/pkg/workers/clusters_mgr.go @@ -10,7 +10,6 @@ import ( dinosaurConstants "github.com/stackrox/acs-fleet-manager/internal/dinosaur/constants" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/config" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/gitops" - "github.com/stackrox/acs-fleet-manager/pkg/client/observatorium" ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/logger" @@ -26,8 +25,6 @@ import ( authv1 "github.com/openshift/api/authorization/v1" userv1 "github.com/openshift/api/user/v1" - "github.com/operator-framework/api/pkg/operators/v1alpha1" - "github.com/operator-framework/api/pkg/operators/v1alpha2" "github.com/pkg/errors" k8sCoreV1 "k8s.io/api/core/v1" @@ -36,13 +33,6 @@ import ( // TODO change these constants to match your own const ( - observabilityNamespace = "managed-application-services-observability" - observabilityCatalogSourceImage = "quay.io/rhoas/observability-operator-index:v3.0.8" - observabilityOperatorGroupName = "observability-operator-group-name" - observabilityCatalogSourceName = "observability-operator-manifests" - observabilitySubscriptionName = "observability-operator" - observatoriumSSOSecretName = "observatorium-configuration-red-hat-sso" // pragma: allowlist secret - observatoriumAuthType = "redhat" mkReadOnlyGroupName = "mk-readonly-access" mkSREGroupName = "dinosaur-sre" mkReadOnlyRoleBindingName = "mk-dedicated-readers" @@ -85,15 +75,14 @@ type ClusterManager struct { // ClusterManagerOptions ... type ClusterManagerOptions struct { di.Inject - Reconciler workers.Reconciler - OCMConfig *ocm.OCMConfig - ObservabilityConfiguration *observatorium.ObservabilityConfiguration - DataplaneClusterConfig *config.DataplaneClusterConfig - SupportedProviders *config.ProviderConfig - ClusterService services.ClusterService - CloudProvidersService services.CloudProvidersService - AddonProvisioner *services.AddonProvisioner - GitOpsConfigProvider gitops.ConfigProvider + Reconciler workers.Reconciler + OCMConfig *ocm.OCMConfig + DataplaneClusterConfig *config.DataplaneClusterConfig + SupportedProviders *config.ProviderConfig + ClusterService services.ClusterService + CloudProvidersService services.CloudProvidersService + AddonProvisioner *services.AddonProvisioner + GitOpsConfigProvider gitops.ConfigProvider } type processor func() []error @@ -717,98 +706,6 @@ func (c *ClusterManager) reconcileClustersForRegions() []error { return errs } -func (c *ClusterManager) buildObservabilityNamespaceResource() *k8sCoreV1.Namespace { - return &k8sCoreV1.Namespace{ - TypeMeta: metav1.TypeMeta{ - APIVersion: k8sCoreV1.SchemeGroupVersion.String(), - Kind: "Namespace", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: observabilityNamespace, - }, - } -} - -func (c *ClusterManager) buildObservatoriumSSOSecretResource() *k8sCoreV1.Secret { - observabilityConfig := c.ObservabilityConfiguration - stringDataMap := map[string]string{ - "authType": observatoriumAuthType, - "gateway": observabilityConfig.RedHatSSOGatewayURL, - "tenant": observabilityConfig.RedHatSSOTenant, - "redHatSsoAuthServerUrl": observabilityConfig.RedHatSSOAuthServerURL, - "redHatSsoRealm": observabilityConfig.RedHatSSORealm, - "metricsClientId": observabilityConfig.MetricsClientID, - "metricsSecret": observabilityConfig.MetricsSecret, // pragma: allowlist secret - "logsClientId": observabilityConfig.LogsClientID, - "logsSecret": observabilityConfig.LogsSecret, // pragma: allowlist secret - } - return &k8sCoreV1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: metav1.SchemeGroupVersion.Version, - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: observatoriumSSOSecretName, - Namespace: observabilityNamespace, - }, - Type: k8sCoreV1.SecretTypeOpaque, - StringData: stringDataMap, - } -} -func (c *ClusterManager) buildObservabilityCatalogSourceResource() *v1alpha1.CatalogSource { - return &v1alpha1.CatalogSource{ - TypeMeta: metav1.TypeMeta{ - APIVersion: v1alpha1.SchemeGroupVersion.String(), - Kind: "CatalogSource", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: observabilityCatalogSourceName, - Namespace: observabilityNamespace, - }, - Spec: v1alpha1.CatalogSourceSpec{ - SourceType: v1alpha1.SourceTypeGrpc, - Image: observabilityCatalogSourceImage, - }, - } -} - -func (c *ClusterManager) buildObservabilityOperatorGroupResource() *v1alpha2.OperatorGroup { - return &v1alpha2.OperatorGroup{ - TypeMeta: metav1.TypeMeta{ - APIVersion: v1alpha2.SchemeGroupVersion.String(), - Kind: "OperatorGroup", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: observabilityOperatorGroupName, - Namespace: observabilityNamespace, - }, - Spec: v1alpha2.OperatorGroupSpec{ - TargetNamespaces: []string{observabilityNamespace}, - }, - } -} - -func (c *ClusterManager) buildObservabilitySubscriptionResource() *v1alpha1.Subscription { - return &v1alpha1.Subscription{ - TypeMeta: metav1.TypeMeta{ - APIVersion: v1alpha1.SchemeGroupVersion.String(), - Kind: "Subscription", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: observabilitySubscriptionName, - Namespace: observabilityNamespace, - }, - Spec: &v1alpha1.SubscriptionSpec{ - CatalogSource: observabilityCatalogSourceName, - Channel: "alpha", - CatalogSourceNamespace: observabilityNamespace, - StartingCSV: "observability-operator.v3.0.8", - InstallPlanApproval: v1alpha1.ApprovalAutomatic, - Package: observabilitySubscriptionName, - }, - } -} - // buildReadOnlyGroupResource creates a group to which read-only cluster users are added. func (c *ClusterManager) buildReadOnlyGroupResource() *userv1.Group { return &userv1.Group{ diff --git a/internal/dinosaur/pkg/workers/dinosaurmgrs/provisioning_dinosaurs_mgr.go b/internal/dinosaur/pkg/workers/dinosaurmgrs/provisioning_dinosaurs_mgr.go index e14c51027d..67b0cfd13e 100644 --- a/internal/dinosaur/pkg/workers/dinosaurmgrs/provisioning_dinosaurs_mgr.go +++ b/internal/dinosaur/pkg/workers/dinosaurmgrs/provisioning_dinosaurs_mgr.go @@ -22,12 +22,11 @@ const provisioningCentralWorkerType = "provisioning_dinosaur" type ProvisioningDinosaurManager struct { workers.BaseWorker dinosaurService services.DinosaurService - observatoriumService services.ObservatoriumService centralRequestTimeout time.Duration } // NewProvisioningDinosaurManager creates a new dinosaur manager -func NewProvisioningDinosaurManager(dinosaurService services.DinosaurService, observatoriumService services.ObservatoriumService, centralRequestConfig *config.CentralRequestConfig) *ProvisioningDinosaurManager { +func NewProvisioningDinosaurManager(dinosaurService services.DinosaurService, centralRequestConfig *config.CentralRequestConfig) *ProvisioningDinosaurManager { metrics.InitReconcilerMetricsForType(provisioningCentralWorkerType) return &ProvisioningDinosaurManager{ BaseWorker: workers.BaseWorker{ @@ -36,7 +35,6 @@ func NewProvisioningDinosaurManager(dinosaurService services.DinosaurService, ob Reconciler: workers.Reconciler{}, }, dinosaurService: dinosaurService, - observatoriumService: observatoriumService, centralRequestTimeout: centralRequestConfig.ExpirationTimeout, } } diff --git a/internal/dinosaur/providers.go b/internal/dinosaur/providers.go index 99afb931c8..c1063905a2 100644 --- a/internal/dinosaur/providers.go +++ b/internal/dinosaur/providers.go @@ -14,7 +14,6 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/services/quota" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/workers" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/workers/dinosaurmgrs" - observatoriumClient "github.com/stackrox/acs-fleet-manager/pkg/client/observatorium" environments2 "github.com/stackrox/acs-fleet-manager/pkg/environments" "github.com/stackrox/acs-fleet-manager/pkg/providers" ) @@ -40,7 +39,6 @@ func ConfigProviders() di.Option { // Configuration for the Dinosaur service... di.Provide(config.NewAWSConfig, di.As(new(environments2.ConfigModule))), di.Provide(config.NewSupportedProvidersConfig, di.As(new(environments2.ConfigModule)), di.As(new(environments2.ServiceValidator))), - di.Provide(observatoriumClient.NewObservabilityConfigurationConfig, di.As(new(environments2.ConfigModule))), di.Provide(config.NewCentralConfig, di.As(new(environments2.ConfigModule))), di.Provide(config.NewDataplaneClusterConfig, di.As(new(environments2.ConfigModule))), di.Provide(config.NewFleetshardConfig, di.As(new(environments2.ConfigModule))), @@ -57,7 +55,6 @@ func ServiceProviders() di.Option { di.Provide(services.NewClusterService), di.Provide(services.NewDinosaurService), di.Provide(services.NewCloudProvidersService), - di.Provide(services.NewObservatoriumService), di.Provide(services.NewAddonProvisioner), di.Provide(services.NewClusterPlacementStrategy), di.Provide(services.NewDataPlaneClusterService), diff --git a/internal/dinosaur/test/helper.go b/internal/dinosaur/test/helper.go index ae5118850a..66c1245253 100644 --- a/internal/dinosaur/test/helper.go +++ b/internal/dinosaur/test/helper.go @@ -19,7 +19,6 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/services" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/workers" "github.com/stackrox/acs-fleet-manager/pkg/client/iam" - "github.com/stackrox/acs-fleet-manager/pkg/client/observatorium" ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/db" "github.com/stackrox/acs-fleet-manager/pkg/environments" @@ -44,7 +43,6 @@ type Services struct { OCMClient ocm.ClusterManagementClient OCMConfig *ocm.OCMConfig DinosaurService services.DinosaurService - ObservatoriumClient *observatorium.Client ClusterManager *workers.ClusterManager ServerConfig *server.ServerConfig } @@ -85,9 +83,8 @@ func NewAdminHelperWithHooks(t *testing.T, server *httptest.Server, configuratio func newCentralHelperWithHooks(t *testing.T, server *httptest.Server, configurationHook interface{}) (*test.Helper, func()) { return test.NewHelperWithHooks(t, server, configurationHook, dinosaur.ConfigProviders(), di.ProvideValue(environments.BeforeCreateServicesHook{ - Func: func(dataplaneClusterConfig *config.DataplaneClusterConfig, dinosaurConfig *config.CentralConfig, observabilityConfiguration *observatorium.ObservabilityConfiguration, fleetshardConfig *config.FleetshardConfig, ocmConfig *ocm.OCMConfig) { + Func: func(dataplaneClusterConfig *config.DataplaneClusterConfig, dinosaurConfig *config.CentralConfig, fleetshardConfig *config.FleetshardConfig, ocmConfig *ocm.OCMConfig) { dinosaurConfig.CentralLifespan.EnableDeletionOfExpiredCentral = true - observabilityConfiguration.EnableMock = true dataplaneClusterConfig.DataPlaneClusterScalingType = config.NoScaling // disable scaling by default as it will be activated in specific tests // Integration tests require a valid OCM client. This requires OCM service account credentials to be set. ocmConfig.EnableMock = false diff --git a/openapi/fleet-manager-private.yaml b/openapi/fleet-manager-private.yaml index 2663ea3440..68d83aa332 100644 --- a/openapi/fleet-manager-private.yaml +++ b/openapi/fleet-manager-private.yaml @@ -661,20 +661,6 @@ components: spec: description: "Data plane cluster agent spec" type: object - properties: - observability: - description: "Observability configurations" - type: object - properties: - accessToken: - type: string - nullable: true - channel: - type: string - repository: - type: string - tag: - type: string securitySchemes: Bearer: diff --git a/openapi/fleet-manager.yaml b/openapi/fleet-manager.yaml index 746d6fb921..340f10425f 100644 --- a/openapi/fleet-manager.yaml +++ b/openapi/fleet-manager.yaml @@ -451,130 +451,6 @@ paths: examples: 500Example: $ref: "#/components/examples/500Example" - # - # These are the user-facing related endpoints - # - /api/rhacs/v1/centrals/{id}/metrics/query_range: - get: - summary: Returns metrics with timeseries range query by Central ID - operationId: getMetricsByRangeQuery - security: - - Bearer: [] - responses: - "200": - description: Returned JSON array of Prometheus metrics objects from observatorium - content: - application/json: - schema: - $ref: "#/components/schemas/MetricsRangeQueryList" - "401": - description: Auth token is invalid - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - examples: - 401Example: - $ref: "#/components/examples/401Example" - "500": - description: Unexpected error occurred - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - examples: - 500Example: - $ref: "#/components/examples/500Example" - parameters: - - $ref: "#/components/parameters/id" - - $ref: "#/components/parameters/duration" - - $ref: "#/components/parameters/interval" - - $ref: "#/components/parameters/filters" - /api/rhacs/v1/centrals/{id}/metrics/query: - get: - summary: Returns metrics with instant query by Central ID - operationId: getMetricsByInstantQuery - security: - - Bearer: [] - responses: - "200": - description: Returned JSON array of Prometheus metrics objects from observatorium - content: - application/json: - schema: - $ref: "#/components/schemas/MetricsInstantQueryList" - "401": - description: Auth token is invalid - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - examples: - 401Example: - $ref: "#/components/examples/401Example" - "500": - description: Unexpected error occurred - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - examples: - 500Example: - $ref: "#/components/examples/500Example" - parameters: - - $ref: "#/components/parameters/id" - - $ref: "#/components/parameters/filters" - /api/rhacs/v1/centrals/{id}/metrics/federate: - get: - summary: Returns all metrics in scrapeable format for a given Central ID - operationId: federateMetrics - security: - - Bearer: [] - responses: - "200": - description: Returned Central metrics in a Prometheus text format - content: - text/plain: - schema: - type: string - "400": - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - examples: - MissingParameterExample: - $ref: "#/components/examples/400MissingParameterExample" - "401": - description: Auth token is invalid - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - examples: - 401Example: - $ref: "#/components/examples/401Example" - "404": - description: Central ID not found - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - examples: - 404Example: - $ref: "#/components/examples/404Example" - "500": - description: Unexpected error occurred - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - examples: - 500Example: - $ref: "#/components/examples/500Example" - parameters: - - $ref: "#/components/parameters/id" components: schemas: diff --git a/pkg/api/observability/v1/types.go b/pkg/api/observability/v1/types.go deleted file mode 100644 index bc0b145bbc..0000000000 --- a/pkg/api/observability/v1/types.go +++ /dev/null @@ -1,78 +0,0 @@ -// Package v1 ... -package v1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// ObservabilityAuthType ... -type ObservabilityAuthType string - -// AuthTypeDex ... -const ( - AuthTypeDex ObservabilityAuthType = "dex" -) - -// DexConfig ... -type DexConfig struct { - URL string `json:"url" yaml:"url"` - CredentialSecretNamespace string `json:"credentialSecretNamespace" yaml:"credentialSecretNamespace"` - CredentialSecretName string `json:"credentialSecretName" yaml:"credentialSecretName"` -} - -// GrafanaConfig ... -type GrafanaConfig struct { - // If false, the operator will install default dashboards and ignore list - Managed bool `json:"managed" yaml:"managed"` -} - -// ObservatoriumConfig ... -type ObservatoriumConfig struct { - // Observatorium Gateway API URL - Gateway string `json:"gateway" yaml:"gateway"` - // Observatorium tenant name - Tenant string `json:"tenant" yaml:"tenant"` - - // Auth type. Currently only dex is supported - AuthType ObservabilityAuthType `json:"authType,omitempty" yaml:"authType,omitempty"` - - // Dex configuration - AuthDex *DexConfig `json:"dexConfig,omitempty" yaml:"dexConfig,omitempty"` -} - -// AlertmanagerConfig ... -type AlertmanagerConfig struct { - PagerDutySecretName string `json:"pagerDutySecretName"` - PagerDutySecretNamespace string `json:"pagerDutySecretNamespace,omitempty"` - DeadMansSnitchSecretName string `json:"deadMansSnitchSecretName"` - DeadMansSnitchSecretNamespace string `json:"deadMansSnitchSecretNamespace,omitempty"` -} - -// ObservabilitySpec defines the desired state of Observability -type ObservabilitySpec struct { - // Observatorium config - Observatorium ObservatoriumConfig `json:"observatorium"` - - // Grafana config - Grafana GrafanaConfig `json:"grafana"` - - // Alertmanager config - Alertmanager AlertmanagerConfig `json:"alertmanager,omitempty"` - - // Selector for all namespaces that should be scraped - DinosaurNamespaceSelector metav1.LabelSelector `json:"dinosaurNamespaceSelector"` - - // Selector for all canary pods that should be scraped - CanaryPodSelector metav1.LabelSelector `json:"canaryPodSelector,omitempty"` -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status - -// Observability is the Schema for the observabilities API -type Observability struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec ObservabilitySpec `json:"spec,omitempty"` -} diff --git a/pkg/client/observatorium/api.go b/pkg/client/observatorium/api.go deleted file mode 100644 index a4bf71e629..0000000000 --- a/pkg/client/observatorium/api.go +++ /dev/null @@ -1,146 +0,0 @@ -// Package observatorium ... -package observatorium - -import ( - "fmt" - "strings" - - "github.com/golang/glog" - "github.com/pkg/errors" -) - -// APIObservatoriumService ... -type APIObservatoriumService interface { - GetDinosaurState(name string, namespaceName string) (DinosaurState, error) - GetMetrics(csMetrics *DinosaurMetrics, resourceNamespace string, rq *MetricsReqParams) error -} -type fetcher struct { - metric string - labels string - callback callback -} -type callback func(m Metric) - -// ServiceObservatorium ... -type ServiceObservatorium struct { - client *Client -} - -// GetDinosaurState ... -func (obs *ServiceObservatorium) GetDinosaurState(name string, resourceNamespace string) (DinosaurState, error) { - DinosaurState := DinosaurState{} - c := obs.client - metric := `dinosaur_operator_resource_state{%s}` // TODO change this to reflect your app specific readness state metric - labels := fmt.Sprintf(`kind=~'Dinosaur', name=~'%s',resource_namespace=~'%s'`, name, resourceNamespace) - result := c.Query(metric, labels) - if result.Err != nil { - return DinosaurState, result.Err - } - - for _, s := range result.Vector { - - if s.Value == 1 { - DinosaurState.State = ClusterStateReady - } else { - DinosaurState.State = ClusterStateUnknown - } - } - return DinosaurState, nil -} - -// GetMetrics ... -func (obs *ServiceObservatorium) GetMetrics(metrics *DinosaurMetrics, namespace string, rq *MetricsReqParams) error { - failedMetrics := []string{} - // TODO update metrics names and add more specifics metrics for your service - fetchers := map[string]fetcher{ - // Check metrics for available disk space per broker - "kubelet_volume_stats_available_bytes": { - `kubelet_volume_stats_available_bytes{%s}`, - fmt.Sprintf(`persistentvolumeclaim=~"data-.*-dinosaur-[0-9]*$", namespace=~'%s'`, namespace), - func(m Metric) { - *metrics = append(*metrics, m) - }, - }, - // Check metrics for used disk space per broker - "kubelet_volume_stats_used_bytes": { - `kubelet_volume_stats_used_bytes{%s}`, - fmt.Sprintf(`persistentvolumeclaim=~"data-.*-dinosaur-[0-9]*$", namespace=~'%s'`, namespace), - func(m Metric) { - *metrics = append(*metrics, m) - }, - }, - // Check metrics for all traffic in/out - "dinosaur_namespace:haproxy_server_bytes_in_total:rate5m": { - `dinosaur_namespace:haproxy_server_bytes_in_total:rate5m{%s}`, - fmt.Sprintf(`exported_namespace=~'%s'`, namespace), - func(m Metric) { - *metrics = append(*metrics, m) - }, - }, - "dinosaur_namespace:haproxy_server_bytes_out_total:rate5m": { - `dinosaur_namespace:haproxy_server_bytes_out_total:rate5m{%s}`, - fmt.Sprintf(`exported_namespace=~'%s'`, namespace), - func(m Metric) { - *metrics = append(*metrics, m) - }, - }, - "dinosaur_namespace:dinosaur_server_socket_server_metrics_connection_count:sum": { - `dinosaur_namespace:dinosaur_server_socket_server_metrics_connection_count:sum{%s}`, - fmt.Sprintf(`namespace=~'%s'`, namespace), - func(m Metric) { - *metrics = append(*metrics, m) - }, - }, - "dinosaur_namespace:dinosaur_server_socket_server_metrics_connection_creation_rate:sum": { - `dinosaur_namespace:dinosaur_server_socket_server_metrics_connection_creation_rate:sum{%s}`, - fmt.Sprintf(`namespace=~'%s'`, namespace), - func(m Metric) { - *metrics = append(*metrics, m) - }, - }, - } - - for msg, f := range fetchers { - fetchAll := len(rq.Filters) == 0 - if fetchAll { - result := obs.fetchMetricsResult(rq, f) - if result.Err != nil { - glog.Error("error from metric ", result.Err) - failedMetrics = append(failedMetrics, fmt.Sprintf("%s: %s", msg, result.Err)) - } - f.callback(result) - } - if !fetchAll { - for _, filter := range rq.Filters { - if filter == msg { - result := obs.fetchMetricsResult(rq, f) - if result.Err != nil { - glog.Error("error from metric ", result.Err) - failedMetrics = append(failedMetrics, fmt.Sprintf("%s: %s", msg, result.Err)) - } - f.callback(result) - } - } - - } - - } - if len(failedMetrics) > 0 { - return errors.Errorf("Failed to fetch metrics data [%s]", strings.Join(failedMetrics, ",")) - } - return nil -} - -func (obs *ServiceObservatorium) fetchMetricsResult(rq *MetricsReqParams, f fetcher) Metric { - c := obs.client - var result Metric - switch rq.ResultType { - case RangeQuery: - result = c.QueryRange(f.metric, f.labels, rq.Range) - case Query: - result = c.Query(f.metric, f.labels) - default: - result = Metric{Err: errors.Errorf("Unsupported Result Type %q", rq.ResultType)} - } - return result -} diff --git a/pkg/client/observatorium/api_mock.go b/pkg/client/observatorium/api_mock.go deleted file mode 100644 index ea97ababea..0000000000 --- a/pkg/client/observatorium/api_mock.go +++ /dev/null @@ -1,177 +0,0 @@ -package observatorium - -import ( - "context" - "strings" - "time" - - pV1 "github.com/prometheus/client_golang/api/prometheus/v1" - pModel "github.com/prometheus/common/model" - mocks "github.com/stackrox/acs-fleet-manager/pkg/client/observatorium/mocks" -) - -// MockAPI returns a mocked instance of pV1.API -func (c *Client) MockAPI() pV1.API { - return &mocks.APIMock{ - QueryFunc: func(ctx context.Context, query string, ts time.Time, opts ...pV1.Option) (pModel.Value, pV1.Warnings, error) { - values := getMockQueryData(query) - return values, []string{}, nil - }, - QueryRangeFunc: func(ctx context.Context, query string, r pV1.Range, opts ...pV1.Option) (pModel.Value, pV1.Warnings, error) { - values := getMockQueryRangeData(query) - return values, []string{}, nil - }, - } -} - -// getMockQueryData -func getMockQueryData(query string) pModel.Vector { - for key, values := range queryData { - if strings.Contains(query, key) { - return values - } - } - return pModel.Vector{} - -} - -// getMockQueryRangeData -func getMockQueryRangeData(query string) pModel.Matrix { - for key, values := range rangeQuerydata { - if strings.Contains(query, key) { - return values - } - } - return pModel.Matrix{} - -} - -var rangeQuerydata = map[string]pModel.Matrix{ - "kubelet_volume_stats_available_bytes": { - fakeMetricData("kubelet_volume_stats_available_bytes", 220792516608), - }, - "dinosaur_server_brokertopicmetrics_messages_in_total": { - fakeMetricData("dinosaur_server_brokertopicmetrics_messages_in_total", 3040), - }, - "dinosaur_server_brokertopicmetrics_bytes_in_total": { - fakeMetricData("dinosaur_server_brokertopicmetrics_bytes_in_total", 293617), - }, - "dinosaur_server_brokertopicmetrics_bytes_out_total": { - fakeMetricData("dinosaur_server_brokertopicmetrics_bytes_out_total", 152751), - }, - "dinosaur_controller_dinosaurcontroller_offline_partitions_count": { - fakeMetricData("dinosaur_controller_dinosaurcontroller_offline_partitions_count", 0), - }, - "dinosaur_controller_dinosaurcontroller_global_partition_count": { - fakeMetricData("dinosaur_controller_dinosaurcontroller_global_partition_count", 0), - }, - "dinosaur_broker_quota_softlimitbytes": { - fakeMetricData("dinosaur_broker_quota_softlimitbytes", 10000), - }, - "dinosaur_broker_quota_totalstorageusedbytes": { - fakeMetricData("dinosaur_broker_quota_totalstorageusedbytes", 1237582), - }, - "dinosaur_topic:dinosaur_log_log_size:sum": { - fakeMetricData("dinosaur_topic:dinosaur_log_log_size:sum", 220), - }, - "dinosaur_namespace:dinosaur_server_socket_server_metrics_connection_creation_rate:sum": { - fakeMetricData("dinosaur_namespace:dinosaur_server_socket_server_metrics_connection_creation_rate:sum", 20), - }, - "dinosaur_topic:dinosaur_topic_partitions:sum": { - fakeMetricData("dinosaur_topic:dinosaur_topic_partitions:sum", 20), - }, - "dinosaur_topic:dinosaur_topic_partitions:count": { - fakeMetricData("dinosaur_topic:dinosaur_topic_partitions:count", 20), - }, - "consumergroup:dinosaur_consumergroup_members:count": { - fakeMetricData("consumergroup:dinosaur_consumergroup_members:count", 20), - }, - "dinosaur_namespace:dinosaur_server_socket_server_metrics_connection_count:sum": { - fakeMetricData("dinosaur_namespace:dinosaur_server_socket_server_metrics_connection_count:sum", 20), - }, -} - -func fakeMetricData(name string, value int) *pModel.SampleStream { - return &pModel.SampleStream{ - Metric: pModel.Metric{ - "__name__": pModel.LabelValue(name), - "pod": "whatever", - "dinosaur_operator_io_cluster": "whatever", - }, - Values: []pModel.SamplePair{{Timestamp: 0, Value: pModel.SampleValue(value)}, - {Timestamp: 0, Value: pModel.SampleValue(value)}}, - } -} - -var queryData = map[string]pModel.Vector{ - "dinosaur_operator_resource_state": { - &pModel.Sample{ - Metric: pModel.Metric{ - "dinosaur_operator_io_kind": "Dinosaur", - "dinosaur_operator_io_name": "test-dinosaur", - "namespace": "my-dinosaur-namespace", - }, - Timestamp: pModel.Time(1607506882175), - Value: 1, - }, - }, - - "dinosaur_server_brokertopicmetrics_bytes_in_total": { - &pModel.Sample{ - Metric: pModel.Metric{ - "__name__": "dinosaur_server_brokertopicmetrics_bytes_in_total", - "pod": "whatever", - "dinosaur_operator_io_cluster": "whatever", - "topic": "whatever", - }, - Timestamp: pModel.Time(1607506882175), - Value: 293617, - }, - }, - "dinosaur_server_brokertopicmetrics_messages_in_total": { - &pModel.Sample{ - Metric: pModel.Metric{ - "__name__": "dinosaur_server_brokertopicmetrics_messages_in_total", - "pod": "whatever", - "dinosaur_operator_io_cluster": "whatever", - "topic": "whatever", - }, - Timestamp: pModel.Time(1607506882175), - Value: 1016, - }, - }, - "dinosaur_broker_quota_softlimitbytes": { - &pModel.Sample{ - Metric: pModel.Metric{ - "__name__": "dinosaur_broker_quota_softlimitbytes", - "pod": "whatever", - "dinosaur_operator_io_cluster": "whatever", - "topic": "whatever", - }, - Timestamp: pModel.Time(1607506882175), - Value: 30000, - }, - }, - "dinosaur_broker_quota_totalstorageusedbytes": { - &pModel.Sample{ - Metric: pModel.Metric{ - "__name__": "dinosaur_broker_quota_totalstorageusedbytes", - "pod": "whatever", - "dinosaur_operator_io_cluster": "whatever", - "topic": "whatever", - }, - Timestamp: pModel.Time(1607506882175), - Value: 2207924332, - }, - }, - "kubelet_volume_stats_available_bytes": { - &pModel.Sample{ - Metric: pModel.Metric{ - "__name__": "kubelet_volume_stats_available_bytes", - "persistentvolumeclaim": "whatever", - }, - Timestamp: pModel.Time(1607506882175), - Value: 220792492032, - }, - }, -} diff --git a/pkg/client/observatorium/api_test.go b/pkg/client/observatorium/api_test.go deleted file mode 100644 index aba8dfba1b..0000000000 --- a/pkg/client/observatorium/api_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package observatorium - -import ( - "testing" -) - -func TestServiceObservatorium_GetMetrics(t *testing.T) { - type fields struct { - client *Client - } - - type args struct { - metrics *DinosaurMetrics - namespace string - rq *MetricsReqParams - } - - obsClientMock, err := NewClientMock(&Configuration{}) - if err != nil { - t.Fatal("failed to create a mock observatorium client") - } - - tests := []struct { - name string - fields fields - args args - wantErr bool - }{ - { - name: "Return metrics successfully for Query result type", - fields: fields{ - client: obsClientMock, - }, - args: args{ - metrics: &DinosaurMetrics{}, - namespace: "dinosaur-test", - rq: &MetricsReqParams{ - ResultType: Query, - }, - }, - wantErr: false, - }, - { - name: "Return metrics successfully for Query result type", - fields: fields{ - client: obsClientMock, - }, - args: args{ - metrics: &DinosaurMetrics{}, - namespace: "dinosaur-test", - rq: &MetricsReqParams{ - ResultType: RangeQuery, - }, - }, - wantErr: false, - }, - { - name: "Return an error if result type is not supported", - fields: fields{ - client: obsClientMock, - }, - args: args{ - metrics: &DinosaurMetrics{}, - namespace: "dinosaur-test", - rq: &MetricsReqParams{ - ResultType: "unsupported", - }, - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - obs := &ServiceObservatorium{ - client: tt.fields.client, - } - if err := obs.GetMetrics(tt.args.metrics, tt.args.namespace, tt.args.rq); (err != nil) != tt.wantErr { - t.Errorf("ServiceObservatorium.GetMetrics() error = %v, wantErr %v", err, tt.wantErr) - } - - if tt.args.metrics == nil { - t.Errorf("metrics list should not be empty") - } - }) - } -} diff --git a/pkg/client/observatorium/client.go b/pkg/client/observatorium/client.go deleted file mode 100644 index 2d8e197d1d..0000000000 --- a/pkg/client/observatorium/client.go +++ /dev/null @@ -1,229 +0,0 @@ -package observatorium - -import ( - "context" - "crypto/tls" - "fmt" - "net/http" - "strings" - "time" - - "github.com/golang/glog" - - "github.com/pkg/errors" - pAPI "github.com/prometheus/client_golang/api" - pV1 "github.com/prometheus/client_golang/api/prometheus/v1" - pModel "github.com/prometheus/common/model" - "github.com/stackrox/acs-fleet-manager/pkg/logger" - "github.com/stackrox/acs-fleet-manager/pkg/metrics" -) - -// ClientConfiguration ... -type ClientConfiguration struct { - BaseURL string - Timeout time.Duration - Debug bool - EnableMock bool - Insecure bool -} - -// Client ... -type Client struct { - // Configuration - Config *ClientConfiguration - connection pV1.API - Service APIObservatoriumService -} - -// NewObservatoriumClient ... -func NewObservatoriumClient(c *ObservabilityConfiguration) (client *Client, err error) { - // Create Observatorium client - observatoriumConfig := &Configuration{ - Timeout: c.Timeout, - Debug: c.Debug, - Insecure: c.Insecure, - } - - observatoriumConfig.BaseURL = c.RedHatSSOTokenRefresherURL - - if c.EnableMock { - glog.Infof("Using Mock Observatorium Client") - client, err = NewClientMock(observatoriumConfig) - } else { - client, err = NewClient(observatoriumConfig) - } - if err != nil { - glog.Errorf("Unable to create Observatorium client: %s", err) - } - return -} - -// NewClient ... -func NewClient(config *Configuration) (*Client, error) { - client := &Client{ - Config: &ClientConfiguration{ - Timeout: config.Timeout, - Debug: config.Debug, - EnableMock: false, - Insecure: config.Insecure, - }, - } - - // Ensure baseURL has a trailing slash - baseURL := strings.TrimSuffix(config.BaseURL, "/") - client.Config.BaseURL = baseURL + "/" - client.Service = &ServiceObservatorium{client: client} - if config.Insecure { - pAPI.DefaultRoundTripper.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - } - - apiClient, err := pAPI.NewClient(pAPI.Config{ - Address: client.Config.BaseURL, - RoundTripper: observatoriumRoundTripper{ - config: *client.Config, - wrapped: pAPI.DefaultRoundTripper, - }, - }) - if err != nil { - return nil, fmt.Errorf("creating new client: %w", err) - } - client.connection = pV1.NewAPI(apiClient) - client.Service = &ServiceObservatorium{client: client} - return client, nil -} - -// NewClientMock ... -func NewClientMock(config *Configuration) (*Client, error) { - client := &Client{ - Config: &ClientConfiguration{ - Timeout: config.Timeout, - Debug: false, - EnableMock: true, - Insecure: config.Insecure, - }, - } - - client.Service = &ServiceObservatorium{client: client} - client.connection = client.MockAPI() - - return client, nil -} - -type observatoriumRoundTripper struct { - config ClientConfiguration - wrapped http.RoundTripper -} - -// RoundTrip ... -func (p observatoriumRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { - var statusCode int - path := strings.TrimPrefix(request.URL.String(), p.config.BaseURL) - if !strings.HasPrefix(path, "/") { - path = "/" + path - } - - start := time.Now() - resp, err := p.wrapped.RoundTrip(request) - elapsedTime := time.Since(start) - - if resp != nil { - statusCode = resp.StatusCode - } - metrics.IncreaseObservatoriumRequestCount(statusCode, path, request.Method) - metrics.UpdateObservatoriumRequestDurationMetric(statusCode, path, request.Method, elapsedTime) - - if err != nil { - return resp, fmt.Errorf("executing round trip: %w", err) - } - return resp, nil -} - -// Send a POST request to server. -func (c *Client) send(query string) (pModel.Value, pV1.Warnings, error) { - ctx, cancel := context.WithTimeout(context.Background(), c.Config.Timeout) - defer cancel() - - v, w, err := c.connection.Query(ctx, query, time.Now()) - if err != nil { - return v, w, fmt.Errorf("executing POST request: %w", err) - } - return v, w, nil -} - -// Send a POST request to server. -func (c *Client) sendRange(query string, bounds pV1.Range) (pModel.Value, pV1.Warnings, error) { - ctx, cancel := context.WithTimeout(context.Background(), c.Config.Timeout) - defer cancel() - - v, w, err := c.connection.QueryRange(ctx, query, bounds) - if err != nil { - return v, w, fmt.Errorf("executing range POST request: %w", err) - } - return v, w, nil - -} - -// Query sends a metrics request to server and returns unmashalled Vector response. -// The VectorResult(s) inside will contain either .Value for queries resulting in instant vector, -// or .Values for queries resulting in a range vector. -// -// queryTemplate must contain one %s for labels, e.g. `some_metric{%s}` or `count(some_metric{state="up",%s})`. -// (Undocumented PromQL: empty labels some_metric{} are OK - https://github.com/prometheus/prometheus/issues/3697) -// labels 0 or more constraints separated by comma e.g. “ or `foo="bar",quux="baz"`. -func (c *Client) Query(queryTemplate string, label string) Metric { - - queryString := fmt.Sprintf(queryTemplate, label) - values, warnings, err := c.send(queryString) - - if len(warnings) > 0 { - logger.Logger.Warningf("Prometheus client got warnings %s", all(warnings, "and")) - } - if err != nil { - return Metric{Err: err} - } - - v, ok := values.(pModel.Vector) - if !ok { - logger.Logger.Errorf("Prometheus client got data of type %T, but expected model.Vector", values) - return Metric{Err: errors.Errorf("Prometheus client got data of type %T, but expected model.Vector", values)} - } - return Metric{Vector: v} -} - -// QueryRange ... -func (c *Client) QueryRange(queryTemplate string, label string, bounds pV1.Range) Metric { - - queryString := fmt.Sprintf(queryTemplate, label) - values, warnings, err := c.sendRange(queryString, bounds) - if len(warnings) > 0 { - logger.Logger.Warningf("Prometheus client got warnings %s", all(warnings, "and")) - } - if err != nil { - return Metric{Err: err} - } - - m, ok := values.(pModel.Matrix) - if !ok { - logger.Logger.Errorf("Prometheus client got data of type %T, but expected model.Matrix", values) - return Metric{Err: errors.Errorf("Prometheus client got data of type %T, but expected model.Matrix", values)} - - } - return Metric{Matrix: m} -} - -func all(items []string, conjunction string) string { - count := len(items) - if count == 0 { - return "" - } - quoted := make([]string, len(items)) - for i, item := range items { - quoted[i] = fmt.Sprintf("'%s'", item) - } - if count == 1 { - return quoted[0] - } - head := quoted[0 : count-1] - tail := quoted[count-1] - return fmt.Sprintf("%s %s %s", strings.Join(head, ", "), conjunction, tail) -} diff --git a/pkg/client/observatorium/config.go b/pkg/client/observatorium/config.go deleted file mode 100644 index 8d90d62863..0000000000 --- a/pkg/client/observatorium/config.go +++ /dev/null @@ -1,16 +0,0 @@ -package observatorium - -import ( - "time" -) - -// Configuration ... -type Configuration struct { - BaseURL string - AuthToken string - Cookie string - Timeout time.Duration - Debug bool - Insecure bool - AuthType string -} diff --git a/pkg/client/observatorium/mocks/api_moq.go b/pkg/client/observatorium/mocks/api_moq.go deleted file mode 100644 index a48eaf95c0..0000000000 --- a/pkg/client/observatorium/mocks/api_moq.go +++ /dev/null @@ -1,1126 +0,0 @@ -// Code generated by moq; DO NOT EDIT. -// github.com/matryer/moq - -package mocks - -import ( - "context" - pV1 "github.com/prometheus/client_golang/api/prometheus/v1" - "github.com/prometheus/common/model" - "sync" - "time" -) - -// Ensure, that APIMock does implement API. -// If this is not the case, regenerate this file with moq. -var _ API = &APIMock{} - -// APIMock is a mock implementation of API. -// -// func TestSomethingThatUsesAPI(t *testing.T) { -// -// // make and configure a mocked API -// mockedAPI := &APIMock{ -// AlertManagersFunc: func(ctx context.Context) (pV1.AlertManagersResult, error) { -// panic("mock out the AlertManagers method") -// }, -// AlertsFunc: func(ctx context.Context) (pV1.AlertsResult, error) { -// panic("mock out the Alerts method") -// }, -// BuildinfoFunc: func(ctx context.Context) (pV1.BuildinfoResult, error) { -// panic("mock out the Buildinfo method") -// }, -// CleanTombstonesFunc: func(ctx context.Context) error { -// panic("mock out the CleanTombstones method") -// }, -// ConfigFunc: func(ctx context.Context) (pV1.ConfigResult, error) { -// panic("mock out the Config method") -// }, -// DeleteSeriesFunc: func(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error { -// panic("mock out the DeleteSeries method") -// }, -// FlagsFunc: func(ctx context.Context) (pV1.FlagsResult, error) { -// panic("mock out the Flags method") -// }, -// LabelNamesFunc: func(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]string, pV1.Warnings, error) { -// panic("mock out the LabelNames method") -// }, -// LabelValuesFunc: func(ctx context.Context, label string, matches []string, startTime time.Time, endTime time.Time) (model.LabelValues, pV1.Warnings, error) { -// panic("mock out the LabelValues method") -// }, -// MetadataFunc: func(ctx context.Context, metric string, limit string) (map[string][]pV1.Metadata, error) { -// panic("mock out the Metadata method") -// }, -// QueryFunc: func(ctx context.Context, query string, ts time.Time, opts ...pV1.Option) (model.Value, pV1.Warnings, error) { -// panic("mock out the Query method") -// }, -// QueryExemplarsFunc: func(ctx context.Context, query string, startTime time.Time, endTime time.Time) ([]pV1.ExemplarQueryResult, error) { -// panic("mock out the QueryExemplars method") -// }, -// QueryRangeFunc: func(ctx context.Context, query string, r pV1.Range, opts ...pV1.Option) (model.Value, pV1.Warnings, error) { -// panic("mock out the QueryRange method") -// }, -// RulesFunc: func(ctx context.Context) (pV1.RulesResult, error) { -// panic("mock out the Rules method") -// }, -// RuntimeinfoFunc: func(ctx context.Context) (pV1.RuntimeinfoResult, error) { -// panic("mock out the Runtimeinfo method") -// }, -// SeriesFunc: func(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, pV1.Warnings, error) { -// panic("mock out the Series method") -// }, -// SnapshotFunc: func(ctx context.Context, skipHead bool) (pV1.SnapshotResult, error) { -// panic("mock out the Snapshot method") -// }, -// TSDBFunc: func(ctx context.Context) (pV1.TSDBResult, error) { -// panic("mock out the TSDB method") -// }, -// TargetsFunc: func(ctx context.Context) (pV1.TargetsResult, error) { -// panic("mock out the Targets method") -// }, -// TargetsMetadataFunc: func(ctx context.Context, matchTarget string, metric string, limit string) ([]pV1.MetricMetadata, error) { -// panic("mock out the TargetsMetadata method") -// }, -// WalReplayFunc: func(ctx context.Context) (pV1.WalReplayStatus, error) { -// panic("mock out the WalReplay method") -// }, -// } -// -// // use mockedAPI in code that requires API -// // and then make assertions. -// -// } -type APIMock struct { - // AlertManagersFunc mocks the AlertManagers method. - AlertManagersFunc func(ctx context.Context) (pV1.AlertManagersResult, error) - - // AlertsFunc mocks the Alerts method. - AlertsFunc func(ctx context.Context) (pV1.AlertsResult, error) - - // BuildinfoFunc mocks the Buildinfo method. - BuildinfoFunc func(ctx context.Context) (pV1.BuildinfoResult, error) - - // CleanTombstonesFunc mocks the CleanTombstones method. - CleanTombstonesFunc func(ctx context.Context) error - - // ConfigFunc mocks the Config method. - ConfigFunc func(ctx context.Context) (pV1.ConfigResult, error) - - // DeleteSeriesFunc mocks the DeleteSeries method. - DeleteSeriesFunc func(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error - - // FlagsFunc mocks the Flags method. - FlagsFunc func(ctx context.Context) (pV1.FlagsResult, error) - - // LabelNamesFunc mocks the LabelNames method. - LabelNamesFunc func(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]string, pV1.Warnings, error) - - // LabelValuesFunc mocks the LabelValues method. - LabelValuesFunc func(ctx context.Context, label string, matches []string, startTime time.Time, endTime time.Time) (model.LabelValues, pV1.Warnings, error) - - // MetadataFunc mocks the Metadata method. - MetadataFunc func(ctx context.Context, metric string, limit string) (map[string][]pV1.Metadata, error) - - // QueryFunc mocks the Query method. - QueryFunc func(ctx context.Context, query string, ts time.Time, opts ...pV1.Option) (model.Value, pV1.Warnings, error) - - // QueryExemplarsFunc mocks the QueryExemplars method. - QueryExemplarsFunc func(ctx context.Context, query string, startTime time.Time, endTime time.Time) ([]pV1.ExemplarQueryResult, error) - - // QueryRangeFunc mocks the QueryRange method. - QueryRangeFunc func(ctx context.Context, query string, r pV1.Range, opts ...pV1.Option) (model.Value, pV1.Warnings, error) - - // RulesFunc mocks the Rules method. - RulesFunc func(ctx context.Context) (pV1.RulesResult, error) - - // RuntimeinfoFunc mocks the Runtimeinfo method. - RuntimeinfoFunc func(ctx context.Context) (pV1.RuntimeinfoResult, error) - - // SeriesFunc mocks the Series method. - SeriesFunc func(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, pV1.Warnings, error) - - // SnapshotFunc mocks the Snapshot method. - SnapshotFunc func(ctx context.Context, skipHead bool) (pV1.SnapshotResult, error) - - // TSDBFunc mocks the TSDB method. - TSDBFunc func(ctx context.Context) (pV1.TSDBResult, error) - - // TargetsFunc mocks the Targets method. - TargetsFunc func(ctx context.Context) (pV1.TargetsResult, error) - - // TargetsMetadataFunc mocks the TargetsMetadata method. - TargetsMetadataFunc func(ctx context.Context, matchTarget string, metric string, limit string) ([]pV1.MetricMetadata, error) - - // WalReplayFunc mocks the WalReplay method. - WalReplayFunc func(ctx context.Context) (pV1.WalReplayStatus, error) - - // calls tracks calls to the methods. - calls struct { - // AlertManagers holds details about calls to the AlertManagers method. - AlertManagers []struct { - // Ctx is the ctx argument value. - Ctx context.Context - } - // Alerts holds details about calls to the Alerts method. - Alerts []struct { - // Ctx is the ctx argument value. - Ctx context.Context - } - // Buildinfo holds details about calls to the Buildinfo method. - Buildinfo []struct { - // Ctx is the ctx argument value. - Ctx context.Context - } - // CleanTombstones holds details about calls to the CleanTombstones method. - CleanTombstones []struct { - // Ctx is the ctx argument value. - Ctx context.Context - } - // Config holds details about calls to the Config method. - Config []struct { - // Ctx is the ctx argument value. - Ctx context.Context - } - // DeleteSeries holds details about calls to the DeleteSeries method. - DeleteSeries []struct { - // Ctx is the ctx argument value. - Ctx context.Context - // Matches is the matches argument value. - Matches []string - // StartTime is the startTime argument value. - StartTime time.Time - // EndTime is the endTime argument value. - EndTime time.Time - } - // Flags holds details about calls to the Flags method. - Flags []struct { - // Ctx is the ctx argument value. - Ctx context.Context - } - // LabelNames holds details about calls to the LabelNames method. - LabelNames []struct { - // Ctx is the ctx argument value. - Ctx context.Context - // Matches is the matches argument value. - Matches []string - // StartTime is the startTime argument value. - StartTime time.Time - // EndTime is the endTime argument value. - EndTime time.Time - } - // LabelValues holds details about calls to the LabelValues method. - LabelValues []struct { - // Ctx is the ctx argument value. - Ctx context.Context - // Label is the label argument value. - Label string - // Matches is the matches argument value. - Matches []string - // StartTime is the startTime argument value. - StartTime time.Time - // EndTime is the endTime argument value. - EndTime time.Time - } - // Metadata holds details about calls to the Metadata method. - Metadata []struct { - // Ctx is the ctx argument value. - Ctx context.Context - // Metric is the metric argument value. - Metric string - // Limit is the limit argument value. - Limit string - } - // Query holds details about calls to the Query method. - Query []struct { - // Ctx is the ctx argument value. - Ctx context.Context - // Query is the query argument value. - Query string - // Ts is the ts argument value. - Ts time.Time - // Opts is the opts argument value. - Opts []pV1.Option - } - // QueryExemplars holds details about calls to the QueryExemplars method. - QueryExemplars []struct { - // Ctx is the ctx argument value. - Ctx context.Context - // Query is the query argument value. - Query string - // StartTime is the startTime argument value. - StartTime time.Time - // EndTime is the endTime argument value. - EndTime time.Time - } - // QueryRange holds details about calls to the QueryRange method. - QueryRange []struct { - // Ctx is the ctx argument value. - Ctx context.Context - // Query is the query argument value. - Query string - // R is the r argument value. - R pV1.Range - // Opts is the opts argument value. - Opts []pV1.Option - } - // Rules holds details about calls to the Rules method. - Rules []struct { - // Ctx is the ctx argument value. - Ctx context.Context - } - // Runtimeinfo holds details about calls to the Runtimeinfo method. - Runtimeinfo []struct { - // Ctx is the ctx argument value. - Ctx context.Context - } - // Series holds details about calls to the Series method. - Series []struct { - // Ctx is the ctx argument value. - Ctx context.Context - // Matches is the matches argument value. - Matches []string - // StartTime is the startTime argument value. - StartTime time.Time - // EndTime is the endTime argument value. - EndTime time.Time - } - // Snapshot holds details about calls to the Snapshot method. - Snapshot []struct { - // Ctx is the ctx argument value. - Ctx context.Context - // SkipHead is the skipHead argument value. - SkipHead bool - } - // TSDB holds details about calls to the TSDB method. - TSDB []struct { - // Ctx is the ctx argument value. - Ctx context.Context - } - // Targets holds details about calls to the Targets method. - Targets []struct { - // Ctx is the ctx argument value. - Ctx context.Context - } - // TargetsMetadata holds details about calls to the TargetsMetadata method. - TargetsMetadata []struct { - // Ctx is the ctx argument value. - Ctx context.Context - // MatchTarget is the matchTarget argument value. - MatchTarget string - // Metric is the metric argument value. - Metric string - // Limit is the limit argument value. - Limit string - } - // WalReplay holds details about calls to the WalReplay method. - WalReplay []struct { - // Ctx is the ctx argument value. - Ctx context.Context - } - } - lockAlertManagers sync.RWMutex - lockAlerts sync.RWMutex - lockBuildinfo sync.RWMutex - lockCleanTombstones sync.RWMutex - lockConfig sync.RWMutex - lockDeleteSeries sync.RWMutex - lockFlags sync.RWMutex - lockLabelNames sync.RWMutex - lockLabelValues sync.RWMutex - lockMetadata sync.RWMutex - lockQuery sync.RWMutex - lockQueryExemplars sync.RWMutex - lockQueryRange sync.RWMutex - lockRules sync.RWMutex - lockRuntimeinfo sync.RWMutex - lockSeries sync.RWMutex - lockSnapshot sync.RWMutex - lockTSDB sync.RWMutex - lockTargets sync.RWMutex - lockTargetsMetadata sync.RWMutex - lockWalReplay sync.RWMutex -} - -// AlertManagers calls AlertManagersFunc. -func (mock *APIMock) AlertManagers(ctx context.Context) (pV1.AlertManagersResult, error) { - if mock.AlertManagersFunc == nil { - panic("APIMock.AlertManagersFunc: method is nil but API.AlertManagers was just called") - } - callInfo := struct { - Ctx context.Context - }{ - Ctx: ctx, - } - mock.lockAlertManagers.Lock() - mock.calls.AlertManagers = append(mock.calls.AlertManagers, callInfo) - mock.lockAlertManagers.Unlock() - return mock.AlertManagersFunc(ctx) -} - -// AlertManagersCalls gets all the calls that were made to AlertManagers. -// Check the length with: -// -// len(mockedAPI.AlertManagersCalls()) -func (mock *APIMock) AlertManagersCalls() []struct { - Ctx context.Context -} { - var calls []struct { - Ctx context.Context - } - mock.lockAlertManagers.RLock() - calls = mock.calls.AlertManagers - mock.lockAlertManagers.RUnlock() - return calls -} - -// Alerts calls AlertsFunc. -func (mock *APIMock) Alerts(ctx context.Context) (pV1.AlertsResult, error) { - if mock.AlertsFunc == nil { - panic("APIMock.AlertsFunc: method is nil but API.Alerts was just called") - } - callInfo := struct { - Ctx context.Context - }{ - Ctx: ctx, - } - mock.lockAlerts.Lock() - mock.calls.Alerts = append(mock.calls.Alerts, callInfo) - mock.lockAlerts.Unlock() - return mock.AlertsFunc(ctx) -} - -// AlertsCalls gets all the calls that were made to Alerts. -// Check the length with: -// -// len(mockedAPI.AlertsCalls()) -func (mock *APIMock) AlertsCalls() []struct { - Ctx context.Context -} { - var calls []struct { - Ctx context.Context - } - mock.lockAlerts.RLock() - calls = mock.calls.Alerts - mock.lockAlerts.RUnlock() - return calls -} - -// Buildinfo calls BuildinfoFunc. -func (mock *APIMock) Buildinfo(ctx context.Context) (pV1.BuildinfoResult, error) { - if mock.BuildinfoFunc == nil { - panic("APIMock.BuildinfoFunc: method is nil but API.Buildinfo was just called") - } - callInfo := struct { - Ctx context.Context - }{ - Ctx: ctx, - } - mock.lockBuildinfo.Lock() - mock.calls.Buildinfo = append(mock.calls.Buildinfo, callInfo) - mock.lockBuildinfo.Unlock() - return mock.BuildinfoFunc(ctx) -} - -// BuildinfoCalls gets all the calls that were made to Buildinfo. -// Check the length with: -// -// len(mockedAPI.BuildinfoCalls()) -func (mock *APIMock) BuildinfoCalls() []struct { - Ctx context.Context -} { - var calls []struct { - Ctx context.Context - } - mock.lockBuildinfo.RLock() - calls = mock.calls.Buildinfo - mock.lockBuildinfo.RUnlock() - return calls -} - -// CleanTombstones calls CleanTombstonesFunc. -func (mock *APIMock) CleanTombstones(ctx context.Context) error { - if mock.CleanTombstonesFunc == nil { - panic("APIMock.CleanTombstonesFunc: method is nil but API.CleanTombstones was just called") - } - callInfo := struct { - Ctx context.Context - }{ - Ctx: ctx, - } - mock.lockCleanTombstones.Lock() - mock.calls.CleanTombstones = append(mock.calls.CleanTombstones, callInfo) - mock.lockCleanTombstones.Unlock() - return mock.CleanTombstonesFunc(ctx) -} - -// CleanTombstonesCalls gets all the calls that were made to CleanTombstones. -// Check the length with: -// -// len(mockedAPI.CleanTombstonesCalls()) -func (mock *APIMock) CleanTombstonesCalls() []struct { - Ctx context.Context -} { - var calls []struct { - Ctx context.Context - } - mock.lockCleanTombstones.RLock() - calls = mock.calls.CleanTombstones - mock.lockCleanTombstones.RUnlock() - return calls -} - -// Config calls ConfigFunc. -func (mock *APIMock) Config(ctx context.Context) (pV1.ConfigResult, error) { - if mock.ConfigFunc == nil { - panic("APIMock.ConfigFunc: method is nil but API.Config was just called") - } - callInfo := struct { - Ctx context.Context - }{ - Ctx: ctx, - } - mock.lockConfig.Lock() - mock.calls.Config = append(mock.calls.Config, callInfo) - mock.lockConfig.Unlock() - return mock.ConfigFunc(ctx) -} - -// ConfigCalls gets all the calls that were made to Config. -// Check the length with: -// -// len(mockedAPI.ConfigCalls()) -func (mock *APIMock) ConfigCalls() []struct { - Ctx context.Context -} { - var calls []struct { - Ctx context.Context - } - mock.lockConfig.RLock() - calls = mock.calls.Config - mock.lockConfig.RUnlock() - return calls -} - -// DeleteSeries calls DeleteSeriesFunc. -func (mock *APIMock) DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error { - if mock.DeleteSeriesFunc == nil { - panic("APIMock.DeleteSeriesFunc: method is nil but API.DeleteSeries was just called") - } - callInfo := struct { - Ctx context.Context - Matches []string - StartTime time.Time - EndTime time.Time - }{ - Ctx: ctx, - Matches: matches, - StartTime: startTime, - EndTime: endTime, - } - mock.lockDeleteSeries.Lock() - mock.calls.DeleteSeries = append(mock.calls.DeleteSeries, callInfo) - mock.lockDeleteSeries.Unlock() - return mock.DeleteSeriesFunc(ctx, matches, startTime, endTime) -} - -// DeleteSeriesCalls gets all the calls that were made to DeleteSeries. -// Check the length with: -// -// len(mockedAPI.DeleteSeriesCalls()) -func (mock *APIMock) DeleteSeriesCalls() []struct { - Ctx context.Context - Matches []string - StartTime time.Time - EndTime time.Time -} { - var calls []struct { - Ctx context.Context - Matches []string - StartTime time.Time - EndTime time.Time - } - mock.lockDeleteSeries.RLock() - calls = mock.calls.DeleteSeries - mock.lockDeleteSeries.RUnlock() - return calls -} - -// Flags calls FlagsFunc. -func (mock *APIMock) Flags(ctx context.Context) (pV1.FlagsResult, error) { - if mock.FlagsFunc == nil { - panic("APIMock.FlagsFunc: method is nil but API.Flags was just called") - } - callInfo := struct { - Ctx context.Context - }{ - Ctx: ctx, - } - mock.lockFlags.Lock() - mock.calls.Flags = append(mock.calls.Flags, callInfo) - mock.lockFlags.Unlock() - return mock.FlagsFunc(ctx) -} - -// FlagsCalls gets all the calls that were made to Flags. -// Check the length with: -// -// len(mockedAPI.FlagsCalls()) -func (mock *APIMock) FlagsCalls() []struct { - Ctx context.Context -} { - var calls []struct { - Ctx context.Context - } - mock.lockFlags.RLock() - calls = mock.calls.Flags - mock.lockFlags.RUnlock() - return calls -} - -// LabelNames calls LabelNamesFunc. -func (mock *APIMock) LabelNames(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]string, pV1.Warnings, error) { - if mock.LabelNamesFunc == nil { - panic("APIMock.LabelNamesFunc: method is nil but API.LabelNames was just called") - } - callInfo := struct { - Ctx context.Context - Matches []string - StartTime time.Time - EndTime time.Time - }{ - Ctx: ctx, - Matches: matches, - StartTime: startTime, - EndTime: endTime, - } - mock.lockLabelNames.Lock() - mock.calls.LabelNames = append(mock.calls.LabelNames, callInfo) - mock.lockLabelNames.Unlock() - return mock.LabelNamesFunc(ctx, matches, startTime, endTime) -} - -// LabelNamesCalls gets all the calls that were made to LabelNames. -// Check the length with: -// -// len(mockedAPI.LabelNamesCalls()) -func (mock *APIMock) LabelNamesCalls() []struct { - Ctx context.Context - Matches []string - StartTime time.Time - EndTime time.Time -} { - var calls []struct { - Ctx context.Context - Matches []string - StartTime time.Time - EndTime time.Time - } - mock.lockLabelNames.RLock() - calls = mock.calls.LabelNames - mock.lockLabelNames.RUnlock() - return calls -} - -// LabelValues calls LabelValuesFunc. -func (mock *APIMock) LabelValues(ctx context.Context, label string, matches []string, startTime time.Time, endTime time.Time) (model.LabelValues, pV1.Warnings, error) { - if mock.LabelValuesFunc == nil { - panic("APIMock.LabelValuesFunc: method is nil but API.LabelValues was just called") - } - callInfo := struct { - Ctx context.Context - Label string - Matches []string - StartTime time.Time - EndTime time.Time - }{ - Ctx: ctx, - Label: label, - Matches: matches, - StartTime: startTime, - EndTime: endTime, - } - mock.lockLabelValues.Lock() - mock.calls.LabelValues = append(mock.calls.LabelValues, callInfo) - mock.lockLabelValues.Unlock() - return mock.LabelValuesFunc(ctx, label, matches, startTime, endTime) -} - -// LabelValuesCalls gets all the calls that were made to LabelValues. -// Check the length with: -// -// len(mockedAPI.LabelValuesCalls()) -func (mock *APIMock) LabelValuesCalls() []struct { - Ctx context.Context - Label string - Matches []string - StartTime time.Time - EndTime time.Time -} { - var calls []struct { - Ctx context.Context - Label string - Matches []string - StartTime time.Time - EndTime time.Time - } - mock.lockLabelValues.RLock() - calls = mock.calls.LabelValues - mock.lockLabelValues.RUnlock() - return calls -} - -// Metadata calls MetadataFunc. -func (mock *APIMock) Metadata(ctx context.Context, metric string, limit string) (map[string][]pV1.Metadata, error) { - if mock.MetadataFunc == nil { - panic("APIMock.MetadataFunc: method is nil but API.Metadata was just called") - } - callInfo := struct { - Ctx context.Context - Metric string - Limit string - }{ - Ctx: ctx, - Metric: metric, - Limit: limit, - } - mock.lockMetadata.Lock() - mock.calls.Metadata = append(mock.calls.Metadata, callInfo) - mock.lockMetadata.Unlock() - return mock.MetadataFunc(ctx, metric, limit) -} - -// MetadataCalls gets all the calls that were made to Metadata. -// Check the length with: -// -// len(mockedAPI.MetadataCalls()) -func (mock *APIMock) MetadataCalls() []struct { - Ctx context.Context - Metric string - Limit string -} { - var calls []struct { - Ctx context.Context - Metric string - Limit string - } - mock.lockMetadata.RLock() - calls = mock.calls.Metadata - mock.lockMetadata.RUnlock() - return calls -} - -// Query calls QueryFunc. -func (mock *APIMock) Query(ctx context.Context, query string, ts time.Time, opts ...pV1.Option) (model.Value, pV1.Warnings, error) { - if mock.QueryFunc == nil { - panic("APIMock.QueryFunc: method is nil but API.Query was just called") - } - callInfo := struct { - Ctx context.Context - Query string - Ts time.Time - Opts []pV1.Option - }{ - Ctx: ctx, - Query: query, - Ts: ts, - Opts: opts, - } - mock.lockQuery.Lock() - mock.calls.Query = append(mock.calls.Query, callInfo) - mock.lockQuery.Unlock() - return mock.QueryFunc(ctx, query, ts, opts...) -} - -// QueryCalls gets all the calls that were made to Query. -// Check the length with: -// -// len(mockedAPI.QueryCalls()) -func (mock *APIMock) QueryCalls() []struct { - Ctx context.Context - Query string - Ts time.Time - Opts []pV1.Option -} { - var calls []struct { - Ctx context.Context - Query string - Ts time.Time - Opts []pV1.Option - } - mock.lockQuery.RLock() - calls = mock.calls.Query - mock.lockQuery.RUnlock() - return calls -} - -// QueryExemplars calls QueryExemplarsFunc. -func (mock *APIMock) QueryExemplars(ctx context.Context, query string, startTime time.Time, endTime time.Time) ([]pV1.ExemplarQueryResult, error) { - if mock.QueryExemplarsFunc == nil { - panic("APIMock.QueryExemplarsFunc: method is nil but API.QueryExemplars was just called") - } - callInfo := struct { - Ctx context.Context - Query string - StartTime time.Time - EndTime time.Time - }{ - Ctx: ctx, - Query: query, - StartTime: startTime, - EndTime: endTime, - } - mock.lockQueryExemplars.Lock() - mock.calls.QueryExemplars = append(mock.calls.QueryExemplars, callInfo) - mock.lockQueryExemplars.Unlock() - return mock.QueryExemplarsFunc(ctx, query, startTime, endTime) -} - -// QueryExemplarsCalls gets all the calls that were made to QueryExemplars. -// Check the length with: -// -// len(mockedAPI.QueryExemplarsCalls()) -func (mock *APIMock) QueryExemplarsCalls() []struct { - Ctx context.Context - Query string - StartTime time.Time - EndTime time.Time -} { - var calls []struct { - Ctx context.Context - Query string - StartTime time.Time - EndTime time.Time - } - mock.lockQueryExemplars.RLock() - calls = mock.calls.QueryExemplars - mock.lockQueryExemplars.RUnlock() - return calls -} - -// QueryRange calls QueryRangeFunc. -func (mock *APIMock) QueryRange(ctx context.Context, query string, r pV1.Range, opts ...pV1.Option) (model.Value, pV1.Warnings, error) { - if mock.QueryRangeFunc == nil { - panic("APIMock.QueryRangeFunc: method is nil but API.QueryRange was just called") - } - callInfo := struct { - Ctx context.Context - Query string - R pV1.Range - Opts []pV1.Option - }{ - Ctx: ctx, - Query: query, - R: r, - Opts: opts, - } - mock.lockQueryRange.Lock() - mock.calls.QueryRange = append(mock.calls.QueryRange, callInfo) - mock.lockQueryRange.Unlock() - return mock.QueryRangeFunc(ctx, query, r, opts...) -} - -// QueryRangeCalls gets all the calls that were made to QueryRange. -// Check the length with: -// -// len(mockedAPI.QueryRangeCalls()) -func (mock *APIMock) QueryRangeCalls() []struct { - Ctx context.Context - Query string - R pV1.Range - Opts []pV1.Option -} { - var calls []struct { - Ctx context.Context - Query string - R pV1.Range - Opts []pV1.Option - } - mock.lockQueryRange.RLock() - calls = mock.calls.QueryRange - mock.lockQueryRange.RUnlock() - return calls -} - -// Rules calls RulesFunc. -func (mock *APIMock) Rules(ctx context.Context) (pV1.RulesResult, error) { - if mock.RulesFunc == nil { - panic("APIMock.RulesFunc: method is nil but API.Rules was just called") - } - callInfo := struct { - Ctx context.Context - }{ - Ctx: ctx, - } - mock.lockRules.Lock() - mock.calls.Rules = append(mock.calls.Rules, callInfo) - mock.lockRules.Unlock() - return mock.RulesFunc(ctx) -} - -// RulesCalls gets all the calls that were made to Rules. -// Check the length with: -// -// len(mockedAPI.RulesCalls()) -func (mock *APIMock) RulesCalls() []struct { - Ctx context.Context -} { - var calls []struct { - Ctx context.Context - } - mock.lockRules.RLock() - calls = mock.calls.Rules - mock.lockRules.RUnlock() - return calls -} - -// Runtimeinfo calls RuntimeinfoFunc. -func (mock *APIMock) Runtimeinfo(ctx context.Context) (pV1.RuntimeinfoResult, error) { - if mock.RuntimeinfoFunc == nil { - panic("APIMock.RuntimeinfoFunc: method is nil but API.Runtimeinfo was just called") - } - callInfo := struct { - Ctx context.Context - }{ - Ctx: ctx, - } - mock.lockRuntimeinfo.Lock() - mock.calls.Runtimeinfo = append(mock.calls.Runtimeinfo, callInfo) - mock.lockRuntimeinfo.Unlock() - return mock.RuntimeinfoFunc(ctx) -} - -// RuntimeinfoCalls gets all the calls that were made to Runtimeinfo. -// Check the length with: -// -// len(mockedAPI.RuntimeinfoCalls()) -func (mock *APIMock) RuntimeinfoCalls() []struct { - Ctx context.Context -} { - var calls []struct { - Ctx context.Context - } - mock.lockRuntimeinfo.RLock() - calls = mock.calls.Runtimeinfo - mock.lockRuntimeinfo.RUnlock() - return calls -} - -// Series calls SeriesFunc. -func (mock *APIMock) Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, pV1.Warnings, error) { - if mock.SeriesFunc == nil { - panic("APIMock.SeriesFunc: method is nil but API.Series was just called") - } - callInfo := struct { - Ctx context.Context - Matches []string - StartTime time.Time - EndTime time.Time - }{ - Ctx: ctx, - Matches: matches, - StartTime: startTime, - EndTime: endTime, - } - mock.lockSeries.Lock() - mock.calls.Series = append(mock.calls.Series, callInfo) - mock.lockSeries.Unlock() - return mock.SeriesFunc(ctx, matches, startTime, endTime) -} - -// SeriesCalls gets all the calls that were made to Series. -// Check the length with: -// -// len(mockedAPI.SeriesCalls()) -func (mock *APIMock) SeriesCalls() []struct { - Ctx context.Context - Matches []string - StartTime time.Time - EndTime time.Time -} { - var calls []struct { - Ctx context.Context - Matches []string - StartTime time.Time - EndTime time.Time - } - mock.lockSeries.RLock() - calls = mock.calls.Series - mock.lockSeries.RUnlock() - return calls -} - -// Snapshot calls SnapshotFunc. -func (mock *APIMock) Snapshot(ctx context.Context, skipHead bool) (pV1.SnapshotResult, error) { - if mock.SnapshotFunc == nil { - panic("APIMock.SnapshotFunc: method is nil but API.Snapshot was just called") - } - callInfo := struct { - Ctx context.Context - SkipHead bool - }{ - Ctx: ctx, - SkipHead: skipHead, - } - mock.lockSnapshot.Lock() - mock.calls.Snapshot = append(mock.calls.Snapshot, callInfo) - mock.lockSnapshot.Unlock() - return mock.SnapshotFunc(ctx, skipHead) -} - -// SnapshotCalls gets all the calls that were made to Snapshot. -// Check the length with: -// -// len(mockedAPI.SnapshotCalls()) -func (mock *APIMock) SnapshotCalls() []struct { - Ctx context.Context - SkipHead bool -} { - var calls []struct { - Ctx context.Context - SkipHead bool - } - mock.lockSnapshot.RLock() - calls = mock.calls.Snapshot - mock.lockSnapshot.RUnlock() - return calls -} - -// TSDB calls TSDBFunc. -func (mock *APIMock) TSDB(ctx context.Context) (pV1.TSDBResult, error) { - if mock.TSDBFunc == nil { - panic("APIMock.TSDBFunc: method is nil but API.TSDB was just called") - } - callInfo := struct { - Ctx context.Context - }{ - Ctx: ctx, - } - mock.lockTSDB.Lock() - mock.calls.TSDB = append(mock.calls.TSDB, callInfo) - mock.lockTSDB.Unlock() - return mock.TSDBFunc(ctx) -} - -// TSDBCalls gets all the calls that were made to TSDB. -// Check the length with: -// -// len(mockedAPI.TSDBCalls()) -func (mock *APIMock) TSDBCalls() []struct { - Ctx context.Context -} { - var calls []struct { - Ctx context.Context - } - mock.lockTSDB.RLock() - calls = mock.calls.TSDB - mock.lockTSDB.RUnlock() - return calls -} - -// Targets calls TargetsFunc. -func (mock *APIMock) Targets(ctx context.Context) (pV1.TargetsResult, error) { - if mock.TargetsFunc == nil { - panic("APIMock.TargetsFunc: method is nil but API.Targets was just called") - } - callInfo := struct { - Ctx context.Context - }{ - Ctx: ctx, - } - mock.lockTargets.Lock() - mock.calls.Targets = append(mock.calls.Targets, callInfo) - mock.lockTargets.Unlock() - return mock.TargetsFunc(ctx) -} - -// TargetsCalls gets all the calls that were made to Targets. -// Check the length with: -// -// len(mockedAPI.TargetsCalls()) -func (mock *APIMock) TargetsCalls() []struct { - Ctx context.Context -} { - var calls []struct { - Ctx context.Context - } - mock.lockTargets.RLock() - calls = mock.calls.Targets - mock.lockTargets.RUnlock() - return calls -} - -// TargetsMetadata calls TargetsMetadataFunc. -func (mock *APIMock) TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]pV1.MetricMetadata, error) { - if mock.TargetsMetadataFunc == nil { - panic("APIMock.TargetsMetadataFunc: method is nil but API.TargetsMetadata was just called") - } - callInfo := struct { - Ctx context.Context - MatchTarget string - Metric string - Limit string - }{ - Ctx: ctx, - MatchTarget: matchTarget, - Metric: metric, - Limit: limit, - } - mock.lockTargetsMetadata.Lock() - mock.calls.TargetsMetadata = append(mock.calls.TargetsMetadata, callInfo) - mock.lockTargetsMetadata.Unlock() - return mock.TargetsMetadataFunc(ctx, matchTarget, metric, limit) -} - -// TargetsMetadataCalls gets all the calls that were made to TargetsMetadata. -// Check the length with: -// -// len(mockedAPI.TargetsMetadataCalls()) -func (mock *APIMock) TargetsMetadataCalls() []struct { - Ctx context.Context - MatchTarget string - Metric string - Limit string -} { - var calls []struct { - Ctx context.Context - MatchTarget string - Metric string - Limit string - } - mock.lockTargetsMetadata.RLock() - calls = mock.calls.TargetsMetadata - mock.lockTargetsMetadata.RUnlock() - return calls -} - -// WalReplay calls WalReplayFunc. -func (mock *APIMock) WalReplay(ctx context.Context) (pV1.WalReplayStatus, error) { - if mock.WalReplayFunc == nil { - panic("APIMock.WalReplayFunc: method is nil but API.WalReplay was just called") - } - callInfo := struct { - Ctx context.Context - }{ - Ctx: ctx, - } - mock.lockWalReplay.Lock() - mock.calls.WalReplay = append(mock.calls.WalReplay, callInfo) - mock.lockWalReplay.Unlock() - return mock.WalReplayFunc(ctx) -} - -// WalReplayCalls gets all the calls that were made to WalReplay. -// Check the length with: -// -// len(mockedAPI.WalReplayCalls()) -func (mock *APIMock) WalReplayCalls() []struct { - Ctx context.Context -} { - var calls []struct { - Ctx context.Context - } - mock.lockWalReplay.RLock() - calls = mock.calls.WalReplay - mock.lockWalReplay.RUnlock() - return calls -} diff --git a/pkg/client/observatorium/mocks/gen.go b/pkg/client/observatorium/mocks/gen.go deleted file mode 100644 index e7db2afd39..0000000000 --- a/pkg/client/observatorium/mocks/gen.go +++ /dev/null @@ -1,8 +0,0 @@ -package mocks - -import pV1 "github.com/prometheus/client_golang/api/prometheus/v1" - -// API an alias for pV1.API -// -//go:generate moq -rm -out api_moq.go . API -type API = pV1.API diff --git a/pkg/client/observatorium/observability_config.go b/pkg/client/observatorium/observability_config.go deleted file mode 100644 index 9f333cdca9..0000000000 --- a/pkg/client/observatorium/observability_config.go +++ /dev/null @@ -1,129 +0,0 @@ -package observatorium - -import ( - "fmt" - "time" - - "github.com/spf13/pflag" - "github.com/stackrox/acs-fleet-manager/pkg/shared" -) - -// ObservabilityConfiguration ... -type ObservabilityConfiguration struct { - // Red Hat SSO configuration - RedHatSSOGatewayURL string `json:"redhat_sso_gateway_url" yaml:"redhat_sso_gateway_url"` - RedHatSSOAuthServerURL string `json:"redhat_sso_auth_server_url" yaml:"redhat_sso_auth_server_url"` - RedHatSSORealm string `json:"redhat_sso_realm" yaml:"redhat_sso_realm"` - RedHatSSOTenant string `json:"redhat_sso_tenant" yaml:"redhat_sso_tenant"` - RedHatSSOTokenRefresherURL string `json:"redhat_sso_token_refresher_url" yaml:"redhat_sso_token_refresher_url"` - MetricsClientID string `json:"redhat_sso_metrics_client_id" yaml:"redhat_sso_metrics_client_id"` - MetricsClientIDFile string `json:"redhat_sso_metrics_client_id_file" yaml:"redhat_sso_metrics_client_id_file"` - MetricsSecret string `json:"redhat_sso_metrics_secret" yaml:"redhat_sso_metrics_secret"` - MetricsSecretFile string `json:"redhat_sso_metrics_secret_file" yaml:"redhat_sso_metrics_secret_file"` - LogsClientID string `json:"redhat_sso_logs_client_id" yaml:"redhat_sso_logs_client_id"` - LogsClientIDFile string `json:"redhat_sso_logs_client_id_file" yaml:"redhat_sso_logs_client_id_file"` - LogsSecret string `json:"redhat_sso_logs_secret" yaml:"redhat_sso_logs_secret"` - LogsSecretFile string `json:"redhat_sso_logs_secret_file" yaml:"redhat_sso_logs_secret_file"` - - // Observatorium configuration - Timeout time.Duration `json:"timeout"` - Insecure bool `json:"insecure"` - Debug bool `json:"debug"` - EnableMock bool `json:"enable_mock"` - - // Configuration repo for the Observability operator - ObservabilityConfigTag string `json:"observability_config_tag"` - ObservabilityConfigRepo string `json:"observability_config_repo"` - ObservabilityConfigChannel string `json:"observability_config_channel"` - ObservabilityConfigAccessToken string `json:"observability_config_access_token"` - ObservabilityConfigAccessTokenFile string `json:"observability_config_access_token_file"` -} - -// NewObservabilityConfigurationConfig ... -func NewObservabilityConfigurationConfig() *ObservabilityConfiguration { - return &ObservabilityConfiguration{ - Timeout: 240 * time.Second, - Debug: true, // TODO: false - EnableMock: false, - Insecure: true, // TODO: false - ObservabilityConfigRepo: "https://api.github.com/repos/bf2fc6cc711aee1a0c2a/observability-resources-mk/contents", - ObservabilityConfigChannel: "resources", // Pointing to resources as the individual directories for prod and staging are no longer needed - ObservabilityConfigAccessToken: "", - ObservabilityConfigAccessTokenFile: "secrets/observability-config-access.token", - ObservabilityConfigTag: "main", - MetricsClientIDFile: "secrets/rhsso-metrics.clientId", - MetricsSecretFile: "secrets/rhsso-metrics.clientSecret", // pragma: allowlist secret - LogsClientIDFile: "secrets/rhsso-logs.clientId", - LogsSecretFile: "secrets/rhsso-logs.clientSecret", // pragma: allowlist secret - RedHatSSOTenant: "", - RedHatSSOAuthServerURL: "", - RedHatSSORealm: "", - RedHatSSOTokenRefresherURL: "", - RedHatSSOGatewayURL: "", - } -} - -// AddFlags ... -func (c *ObservabilityConfiguration) AddFlags(fs *pflag.FlagSet) { - fs.StringVar(&c.RedHatSSOTenant, "observability-red-hat-sso-tenant", c.RedHatSSOTenant, "Red Hat SSO tenant") - fs.StringVar(&c.RedHatSSOAuthServerURL, "observability-red-hat-sso-auth-server-url", c.RedHatSSOAuthServerURL, "Red Hat SSO auth server URL") - fs.StringVar(&c.RedHatSSOGatewayURL, "observability-red-hat-sso-observatorium-gateway", c.RedHatSSOGatewayURL, "Red Hat SSO gateway URL") - fs.StringVar(&c.RedHatSSOTokenRefresherURL, "observability-red-hat-sso-token-refresher-url", c.RedHatSSOTokenRefresherURL, "Red Hat SSO token refresher URL") - fs.StringVar(&c.LogsClientIDFile, "observability-red-hat-sso-logs-client-id-file", c.LogsClientIDFile, "Red Hat SSO logs client id file") - fs.StringVar(&c.MetricsClientIDFile, "observability-red-hat-sso-metrics-client-id-file", c.MetricsClientIDFile, "Red Hat SSO metrics client id file") - fs.StringVar(&c.LogsSecretFile, "observability-red-hat-sso-logs-secret-file", c.LogsSecretFile, "Red Hat SSO logs secret file") - fs.StringVar(&c.MetricsSecretFile, "observability-red-hat-sso-metrics-secret-file", c.MetricsSecretFile, "Red Hat SSO metrics secret file") - fs.StringVar(&c.RedHatSSORealm, "observability-red-hat-sso-realm", c.RedHatSSORealm, "Red Hat SSO realm") - - fs.DurationVar(&c.Timeout, "observatorium-timeout", c.Timeout, "Timeout for Observatorium client") - fs.BoolVar(&c.Insecure, "observatorium-ignore-ssl", c.Insecure, "ignore SSL Observatorium certificate") - fs.BoolVar(&c.EnableMock, "enable-observatorium-mock", c.EnableMock, "Enable mock Observatorium client") - fs.BoolVar(&c.Debug, "observatorium-debug", c.Debug, "Debug flag for Observatorium client") - - fs.StringVar(&c.ObservabilityConfigRepo, "observability-config-repo", c.ObservabilityConfigRepo, "Repo for the observability operator configuration repo") - fs.StringVar(&c.ObservabilityConfigChannel, "observability-config-channel", c.ObservabilityConfigChannel, "Channel for the observability operator configuration repo") - fs.StringVar(&c.ObservabilityConfigAccessTokenFile, "observability-config-access-token-file", c.ObservabilityConfigAccessTokenFile, "File contains the access token to the observability operator configuration repo") - fs.StringVar(&c.ObservabilityConfigTag, "observability-config-tag", c.ObservabilityConfigTag, "Tag or branch to use inside the observability configuration repo") -} - -// ReadFiles ... -func (c *ObservabilityConfiguration) ReadFiles() error { - configFileError := c.ReadObservatoriumConfigFiles() - if configFileError != nil { - return configFileError - } - - if c.ObservabilityConfigAccessToken == "" && c.ObservabilityConfigAccessTokenFile != "" { - err := shared.ReadFileValueString(c.ObservabilityConfigAccessTokenFile, &c.ObservabilityConfigAccessToken) - if err != nil { - return fmt.Errorf("reading observability config access token file: %w", err) - } - return nil - } - return nil -} - -// ReadObservatoriumConfigFiles ... -func (c *ObservabilityConfiguration) ReadObservatoriumConfigFiles() error { - logsClientIDErr := shared.ReadFileValueString(c.LogsClientIDFile, &c.LogsClientID) - if logsClientIDErr != nil { - return fmt.Errorf("reading observatorium config client ID file: %w", logsClientIDErr) - } - - logsSecretErr := shared.ReadFileValueString(c.LogsSecretFile, &c.LogsSecret) - if logsSecretErr != nil { // pragma: allowlist secret - return fmt.Errorf("reading observatorium config secret file: %w", logsSecretErr) - } - - metricsClientIDErr := shared.ReadFileValueString(c.MetricsClientIDFile, &c.MetricsClientID) - if metricsClientIDErr != nil { - return fmt.Errorf("reading observatorium config metrics client ID file: %w", metricsClientIDErr) - } - - metricsSecretErr := shared.ReadFileValueString(c.MetricsSecretFile, &c.MetricsSecret) - if metricsSecretErr != nil { // pragma: allowlist secret - return fmt.Errorf("reading observatorium config metrics secret file: %w", metricsSecretErr) - } - - return nil -} diff --git a/pkg/client/observatorium/types.go b/pkg/client/observatorium/types.go deleted file mode 100644 index dcfec43b10..0000000000 --- a/pkg/client/observatorium/types.go +++ /dev/null @@ -1,52 +0,0 @@ -package observatorium - -import ( - prom_v1 "github.com/prometheus/client_golang/api/prometheus/v1" - pModel "github.com/prometheus/common/model" - - "time" -) - -// State ... -type State string - -// ResultType ... -type ResultType string - -// ClusterStateUnknown ... -const ( - ClusterStateUnknown State = "unknown" - ClusterStateReady State = "ready" - RangeQuery ResultType = "query_range" - Query ResultType = "query" -) - -// DinosaurState ... -type DinosaurState struct { - State State `json:",omitempty"` -} - -// DinosaurMetrics ... -type DinosaurMetrics []Metric - -// Metric holds the Prometheus Matrix or Vector model, which contains instant vector or range vector with time series (depending on result type) -type Metric struct { - Matrix pModel.Matrix `json:"matrix"` - Vector pModel.Vector `json:"vector"` - Err error `json:"-"` -} - -// MetricsReqParams holds common parameters for all kinds of range queries and instant quries -type MetricsReqParams struct { - Filters []string - ResultType ResultType - prom_v1.Range -} - -// FillDefaults fills the struct with default parameters -func (q *MetricsReqParams) FillDefaults() { - q.End = time.Now() - q.Start = q.End.Add(-5 * time.Minute) - q.Step = 30 * time.Second - -} diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 087b56dfc1..e344425731 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -2,7 +2,6 @@ package metrics import ( - "strconv" "time" constants2 "github.com/stackrox/acs-fleet-manager/internal/dinosaur/constants" @@ -58,11 +57,6 @@ const ( LeaderWorker = "leader_worker" - // ObservatoriumRequestCount - metric name for the number of observatorium requests sent - ObservatoriumRequestCount = "observatorium_request_count" - // ObservatoriumRequestDuration - metric name for observatorium request duration in seconds - ObservatoriumRequestDuration = "observatorium_request_duration" - // DatabaseQueryCount - metric name for the number of database query sent DatabaseQueryCount = "database_query_count" // DatabaseQueryDuration - metric name for database query duration in milliseconds @@ -156,12 +150,6 @@ var ReconcilerMetricsLabels = []string{ labelWorkerType, } -var observatoriumRequestMetricsLabels = []string{ - LabelStatusCode, - LabelMethod, - LabelPath, -} - // DatabaseMetricsLabels ... var DatabaseMetricsLabels = []string{ LabelDatabaseQueryStatus, @@ -580,65 +568,6 @@ func SetLeaderWorkerMetric(workerType string, leader bool) { // #### Metrics for Reconcilers - End #### -// #### Metrics for Observatorium #### - -// register observatorium request count metric -// -// observatorium_request_count - Number of Observatorium requests sent partitioned by http status code, method and url path -var observatoriumRequestCountMetric = prometheus.NewCounterVec(prometheus.CounterOpts{ - Subsystem: FleetManager, - Name: ObservatoriumRequestCount, - Help: "number of requests sent to Observatorium. If no response was received, the value of code should be '0' (this can happen on request timeout or failure to connect to Observatorium).", -}, observatoriumRequestMetricsLabels) - -// IncreaseObservatoriumRequestCount Increase the observatorium request count metric with the following labels: -// - code: HTTP Status code (i.e. 200 or 500) -// - path: Request URL path (i.e. /api/v1/query) -// - method: HTTP Method (i.e. GET or POST) -func IncreaseObservatoriumRequestCount(code int, path, method string) { - labels := prometheus.Labels{ - LabelStatusCode: strconv.Itoa(code), - LabelPath: path, - LabelMethod: method, - } - observatoriumRequestCountMetric.With(labels).Inc() -} - -// register observatorium request duration metric. Each metric is partitioned by http status code, method and url path -// -// observatorium_request_duration_sum - Total time to send requests to Observatorium in seconds. -// observatorium_request_duration_count - Total number of Observatorium requests measured. -// observatorium_request_duration_bucket - Number of Observatorium requests organized in buckets. -var observatoriumRequestDurationMetric = prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Subsystem: FleetManager, - Name: ObservatoriumRequestDuration, - Help: `Observatorium request duration in seconds. If no response was received, the value of code should be '0' (this can happen on request timeout or failure to connect to Observatorium).`, - Buckets: []float64{ - 0.1, - 1.0, - 10.0, - 30.0, - }, - }, - observatoriumRequestMetricsLabels, -) - -// UpdateObservatoriumRequestDurationMetric Update the observatorium request duration metric with the following labels: -// - code: HTTP Status code (i.e. 200 or 500) -// - path: Request url path (i.e. /api/v1/query) -// - method: HTTP Method (i.e. GET or POST) -func UpdateObservatoriumRequestDurationMetric(code int, path, method string, elapsed time.Duration) { - labels := prometheus.Labels{ - LabelStatusCode: strconv.Itoa(code), - LabelPath: path, - LabelMethod: method, - } - observatoriumRequestDurationMetric.With(labels).Observe(elapsed.Seconds()) -} - -// #### Metrics for Observatorium - End #### - // #### Metrics for Database #### // register database query count metric @@ -789,10 +718,6 @@ func init() { prometheus.MustRegister(leaderWorkerMetric) prometheus.MustRegister(centralTimeoutCountMetric) - // metrics for observatorium - prometheus.MustRegister(observatoriumRequestCountMetric) - prometheus.MustRegister(observatoriumRequestDurationMetric) - // metrics for database prometheus.MustRegister(databaseRequestCountMetric) prometheus.MustRegister(databaseQueryDurationMetric) @@ -824,13 +749,6 @@ func ResetMetricsForReconcilers() { reconcilerErrorsCountMetric.Reset() } -// ResetMetricsForObservatorium will reset the metrics related to Observatorium requests -// This is needed because if current process is not the leader anymore, the metrics need to be reset otherwise staled data will be scraped -func ResetMetricsForObservatorium() { - observatoriumRequestCountMetric.Reset() - observatoriumRequestDurationMetric.Reset() -} - // Reset the metrics we have defined. It is mainly used for testing. func Reset() { requestClusterCreationDurationMetric.Reset() @@ -858,8 +776,6 @@ func Reset() { leaderWorkerMetric.Reset() centralTimeoutCountMetric.Reset() - ResetMetricsForObservatorium() - databaseRequestCountMetric.Reset() databaseQueryDurationMetric.Reset() } diff --git a/pkg/providers/core.go b/pkg/providers/core.go index b65ff5236f..c2ae5eacc9 100644 --- a/pkg/providers/core.go +++ b/pkg/providers/core.go @@ -8,7 +8,6 @@ import ( "github.com/stackrox/acs-fleet-manager/pkg/auth" "github.com/stackrox/acs-fleet-manager/pkg/client/aws" "github.com/stackrox/acs-fleet-manager/pkg/client/iam" - "github.com/stackrox/acs-fleet-manager/pkg/client/observatorium" ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/client/telemetry" "github.com/stackrox/acs-fleet-manager/pkg/db" @@ -61,7 +60,6 @@ func ServiceProviders() di.Option { // provide the service constructors di.Provide(db.NewConnectionFactory), - di.Provide(observatorium.NewObservatoriumClient), di.Provide(func(config *ocm.OCMConfig) ocm.ClusterManagementClient { if config.EnableMock { diff --git a/templates/observatorium-token-refresher.yml b/templates/observatorium-token-refresher.yml deleted file mode 100644 index 84dc9fd36b..0000000000 --- a/templates/observatorium-token-refresher.yml +++ /dev/null @@ -1,123 +0,0 @@ ---- -apiVersion: template.openshift.io/v1 -kind: Template -metadata: - name: observatorium-token-refresher - annotations: - description: "Token refresher for Observatorium" -parameters: - -- name: ISSUER_URL - description: URL of the token issuer - -- name: OBSERVATORIUM_URL - description: URL of the observatorium instance - -- name: OBSERVATORIUM_TOKEN_REFRESHER_IMAGE - description: Observatorium token refresher image - value: quay.io/rhoas/mk-token-refresher - -- name: OBSERVATORIUM_TOKEN_REFRESHER_IMAGE_TAG - description: Observatorium token refresher image tag - value: latest - -objects: - - kind: Service - apiVersion: v1 - metadata: - labels: - app.kubernetes.io/component: authentication-proxy - app.kubernetes.io/name: token-refresher - app.kubernetes.io/version: ${OBSERVATORIUM_TOKEN_REFRESHER_IMAGE_TAG} - name: token-refresher - spec: - ports: - - name: http - port: 80 - targetPort: 8080 - - name: internal - port: 8081 - targetPort: 8081 - protocol: TCP - selector: - app.kubernetes.io/component: authentication-proxy - app.kubernetes.io/name: token-refresher - - kind: Deployment - apiVersion: apps/v1 - metadata: - labels: - app.kubernetes.io/component: authentication-proxy - app.kubernetes.io/name: token-refresher - app.kubernetes.io/version: ${OBSERVATORIUM_TOKEN_REFRESHER_IMAGE_TAG} - name: token-refresher - spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/component: authentication-proxy - app.kubernetes.io/name: token-refresher - template: - metadata: - labels: - app.kubernetes.io/component: authentication-proxy - app.kubernetes.io/name: token-refresher - app.kubernetes.io/version: master-2020-12-04-5504078 - spec: - containers: - - name: token-refresher - image: ${OBSERVATORIUM_TOKEN_REFRESHER_IMAGE}:${OBSERVATORIUM_TOKEN_REFRESHER_IMAGE_TAG} - imagePullPolicy: Always - ports: - - containerPort: 8080 - name: http - - containerPort: 8081 - name: internal - env: - - name: CLIENT_ID - valueFrom: - secretKeyRef: - name: fleet-manager-observatorium-configuration-red-hat-sso - key: grafana.clientId - - name: CLIENT_SECRET - valueFrom: - secretKeyRef: - name: fleet-manager-observatorium-configuration-red-hat-sso - key: grafana.clientSecret - - name: ISSUER_URL - value: ${ISSUER_URL} - - name: OBSERVATORIUM_URL - value: ${OBSERVATORIUM_URL} - command: - - /bin/token-refresher - - --web.listen=0.0.0.0:8080 - - --web.internal.listen=0.0.0.0:8081 - - --oidc.audience=observatorium-telemeter - - --oidc.client-id=$(CLIENT_ID) - - --oidc.client-secret=$(CLIENT_SECRET) - - --oidc.issuer-url=$(ISSUER_URL) - - --url=$(OBSERVATORIUM_URL) - - kind: NetworkPolicy - apiVersion: networking.k8s.io/v1 - metadata: - labels: - app.kubernetes.io/component: authentication-proxy - app.kubernetes.io/name: token-refresher - name: token-refresher - spec: - podSelector: - matchLabels: - app.kubernetes.io/component: authentication-proxy - app.kubernetes.io/name: token-refresher - policyTypes: - - Ingress - ingress: - - from: - - podSelector: - matchLabels: - app: fleet-manager - - podSelector: - matchLabels: - prometheus: dinosaur-prometheus - namespaceSelector: - matchLabels: - name: managed-application-services-observability diff --git a/templates/secrets-template.yml b/templates/secrets-template.yml index 8ee245b76c..e47e688f55 100644 --- a/templates/secrets-template.yml +++ b/templates/secrets-template.yml @@ -80,27 +80,6 @@ parameters: - name: CENTRAL_TLS_KEY description: Central TLS certificate private key -- name: OBSERVABILITY_CONFIG_ACCESS_TOKEN - description: Access token for the observability configuration repo - -- name: OBSERVABILITY_RHSSO_LOGS_CLIENT_ID - description: Red Hat SSO Logs client id for observability stack - -- name: OBSERVABILITY_RHSSO_METRICS_CLIENT_ID - description: Red Hat SSO Metrics client id for the Observability stack - -- name: OBSERVABILITY_RHSSO_LOGS_SECRET - description: Red Hat SSO Logs secret for the Observability stack - -- name: OBSERVABILITY_RHSSO_METRICS_SECRET - description: Red Hat SSO Metrics secret for the Observability stack - -- name: OBSERVABILITY_RHSSO_GRAFANA_CLIENT_ID - description: Red Hat SSO Grafana client id for the Observability stack - -- name: OBSERVABILITY_RHSSO_GRAFANA_CLIENT_SECRET - description: Red Hat SSO Grafana secret for the Observability stack - objects: - apiVersion: v1 @@ -135,7 +114,6 @@ objects: redhatsso-service.clientSecret: ${SSO_CLIENT_SECRET} aws.route53accesskey: ${ROUTE53_ACCESS_KEY} aws.route53secretaccesskey: ${ROUTE53_SECRET_ACCESS_KEY} - observability-config-access.token: ${OBSERVABILITY_CONFIG_ACCESS_TOKEN} - apiVersion: v1 kind: Secret @@ -144,15 +122,3 @@ objects: stringData: tls.crt: ${CENTRAL_TLS_CERT} tls.key: ${CENTRAL_TLS_KEY} - -- apiVersion: v1 - kind: Secret - metadata: - name: fleet-manager-observatorium-configuration-red-hat-sso - stringData: - logs.clientId: ${OBSERVABILITY_RHSSO_LOGS_CLIENT_ID} - logs.clientSecret : ${OBSERVABILITY_RHSSO_LOGS_SECRET} - metrics.clientId: ${OBSERVABILITY_RHSSO_METRICS_CLIENT_ID} - metrics.clientSecret: ${OBSERVABILITY_RHSSO_METRICS_SECRET} - grafana.clientId: ${OBSERVABILITY_RHSSO_GRAFANA_CLIENT_ID} - grafana.clientSecret: ${OBSERVABILITY_RHSSO_GRAFANA_CLIENT_SECRET} diff --git a/templates/service-template.yml b/templates/service-template.yml index 8b42a4b217..f4a8c8019b 100644 --- a/templates/service-template.yml +++ b/templates/service-template.yml @@ -187,44 +187,6 @@ parameters: description: Enable the Central TLS certificate value: "true" -- name: OBSERVATORIUM_RHSSO_GATEWAY - displayName: Observatorium Red Hat SSO gateway - description: A URL to an Observatorium instance where observability metrics will sent to. - value: "" - -- name: OBSERVATORIUM_RHSSO_REALM - displayName: Observatorium Red Hat SSO realm - description: Observatorium Red Hat SSO realm for observability stack. - value: "" - -- name: OBSERVATORIUM_RHSSO_TENANT - displayName: Observatorium Red Hat SSO tenant - description: Observatorium Red Hat SSO tenant for observability stack. - value: "" - -- name: OBSERVATORIUM_RHSSO_AUTH_SERVER_URL - displayName: Observatorium Red Hat SSO auth server URL - description: Observatorium Red Hat SSO auth server URL for observability stack. - value: "" - -- name: OBSERVATORIUM_TOKEN_REFRESHER_URL - displayName: Observatorium Red Hat SSO token refresher URL - description: Observatorium Red Hat SSO token refresher URL for observability stack. - value: "" - -- name: ENABLE_OBSERVATORIUM_DEBUG - displayName: Enable Observatorium Debug Logging - value: "false" - -- name: OBSERVATORIUM_TIMEOUT - displayName: observatorium Request Timeout (seconds) - description: Timeout duration for all requests made to Observatorium - value: "120s" - -- name: OBSERVATORIUM_INSECURE - displayName: Observatorium ssl mode (disable) - value: "true" - - name: ENABLE_TERMS_ACCEPTANCE displayName: Enable terms acceptance description: If enabled, centrals can't be created unless required terms are accepted @@ -295,21 +257,6 @@ parameters: description: Interval defining how often the synchronizer reports back status changes to the control plane value: "60s" -- name: OBSERVABILITY_CONFIG_REPO - displayName: Observability configuration repo URL - description: URL of the observability configuration repo - value: "https://api.github.com/repos/bf2fc6cc711aee1a0c2a/observability-resources-mk/contents" - -- name: OBSERVABILITY_CONFIG_CHANNEL - displayName: Observability configuration channel - description: Channel of the observability configuration - value: "resources" - -- name: OBSERVABILITY_CONFIG_TAG - displayName: Observability configuration tag - description: Tag or branch to use inside the configuration repo - value: "main" - - name: SERVICE_PUBLIC_HOST_URL displayName: The public HTTP host URL of the service description: The public HTTP host URL of the service @@ -716,16 +663,6 @@ objects: redhatsso-service.clientSecret: badger aws.route53accesskey: badger aws.route53secretaccesskey: badger - observability-config-access.token: badger - - apiVersion: v1 - kind: ConfigMap - metadata: - name: fake-fleet-manager-observatorium-secret - data: - logs.clientId: badger - logs.clientSecret: badger - metrics.clientId: badger - metrics.clientSecret: badger - apiVersion: v1 kind: ConfigMap metadata: @@ -834,9 +771,6 @@ objects: - name: envoy-unix-sockets emptyDir: medium: Memory - - name: fleet-manager-observatorium-configuration-red-hat-sso - configMap: - name: fake-fleet-manager-observatorium-secret - name: fleet-manager-dataplane-cluster-configuration-staging configMap: name: fleet-manager-dataplane-cluster-configuration-staging @@ -897,8 +831,6 @@ objects: mountPath: /secrets/service - name: dataplane-certificate mountPath: /secrets/dataplane-certificate - - name: fleet-manager-observatorium-configuration-red-hat-sso - mountPath: /secrets/observatorium - name: rds mountPath: /secrets/rds - name: fleet-manager-providers-config @@ -979,18 +911,6 @@ objects: - --aws-route53-secret-access-key-file=/secrets/fleet-manager-credentials/aws.route53secretaccesskey - --central-idp-client-secret-file=/secrets/fleet-manager-credentials/central.idp-client-secret - --central-idp-client-id=${CENTRAL_IDP_CLIENT_ID} - - --observatorium-debug=${ENABLE_OBSERVATORIUM_DEBUG} - - --observatorium-ignore-ssl=${OBSERVATORIUM_INSECURE} - - --observatorium-timeout=${OBSERVATORIUM_TIMEOUT} - - --observability-red-hat-sso-logs-client-id-file=/secrets/observatorium/logs.clientId - - --observability-red-hat-sso-logs-secret-file=/secrets/observatorium/logs.clientSecret - - --observability-red-hat-sso-metrics-client-id-file=/secrets/observatorium/metrics.clientId - - --observability-red-hat-sso-metrics-secret-file=/secrets/observatorium/metrics.clientSecret - - --observability-red-hat-sso-auth-server-url=${OBSERVATORIUM_RHSSO_AUTH_SERVER_URL} - - --observability-red-hat-sso-realm=${OBSERVATORIUM_RHSSO_REALM} - - --observability-red-hat-sso-token-refresher-url=${OBSERVATORIUM_TOKEN_REFRESHER_URL} - - --observability-red-hat-sso-observatorium-gateway=${OBSERVATORIUM_RHSSO_GATEWAY} - - --observability-red-hat-sso-tenant=${OBSERVATORIUM_RHSSO_TENANT} - --db-host-file=/secrets/rds/db.host - --db-port-file=/secrets/rds/db.port - --db-user-file=/secrets/rds/db.user @@ -1043,10 +963,6 @@ objects: - --fleetshard-resync-interval=${FLEETSHARD_RESYNC_INTERVAL} - --allow-evaluator-instance=${ALLOW_EVALUATOR_INSTANCE} - --quota-type=${QUOTA_TYPE} - - --observability-config-repo=${OBSERVABILITY_CONFIG_REPO} - - --observability-config-channel=${OBSERVABILITY_CONFIG_CHANNEL} - - --observability-config-access-token-file=/secrets/service/observability-config-access.token - - --observability-config-tag=${OBSERVABILITY_CONFIG_TAG} - --public-host-url=${SERVICE_PUBLIC_HOST_URL} - --dataplane-cluster-scaling-type=${DATAPLANE_CLUSTER_SCALING_TYPE} - --central-domain-name=${CENTRAL_DOMAIN_NAME}