From 20084cc8c63196cbea32c403804b9bafa06ac729 Mon Sep 17 00:00:00 2001 From: Yury Kovalev <8366110+kovayur@users.noreply.github.com> Date: Thu, 18 Jan 2024 12:20:37 +0100 Subject: [PATCH 01/16] Fix integration tests and enable them back on CI (#1594) --- .github/pull_request_template.md | 2 +- .github/workflows/ci.yaml | 4 +--- Makefile | 14 ++------------ 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 99b533d6eb..85c675f6e8 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -20,6 +20,6 @@ ``` # To run tests locally run: make db/teardown db/setup db/migrate -make ocm/setup OCM_OFFLINE_TOKEN= OCM_ENV=development +make ocm/setup make verify lint binary test test/integration ``` diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ba66eb886a..3aebbb57fb 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -71,8 +71,6 @@ jobs: # Dummy Central TLS env variables CENTRAL_TLS_CERT: central_tls_cert # pragma: allowlist secret - dummy value CENTRAL_TLS_KEY: central_tls_key # pragma: allowlist secret - dummy value - # So that OCM secrets are initialised - DOCKER_PR_CHECK: true TEST_TIMEOUT: 30m services: postgres: @@ -118,7 +116,7 @@ jobs: GOPATH=$(go env GOPATH) export GOPATH export PATH=${PATH}:$GOPATH/bin - make verify binary test + make verify binary test test/integration timeout-minutes: 14 build-push-images: name: "Build and push fleet* images to quay.io" diff --git a/Makefile b/Makefile index 7700300681..913273f458 100644 --- a/Makefile +++ b/Makefile @@ -660,22 +660,12 @@ observatorium/token-refresher/setup: @echo The Observatorium token refresher is now running on 'http://localhost:${PORT}' .PHONY: observatorium/token-refresher/setup -# OCM login -ocm/login: - @ocm login --url="$(SERVER_URL)" --token="$(OCM_OFFLINE_TOKEN)" -.PHONY: ocm/login - -# Setup OCM_OFFLINE_TOKEN and -# OCM Client ID and Secret should be set only when running inside docker in integration ENV) -ocm/setup: OCM_CLIENT_ID ?= badger -ocm/setup: OCM_CLIENT_SECRET ?= badger +# Setup dummy OCM_OFFLINE_TOKEN for integration testing +ocm/setup: OCM_OFFLINE_TOKEN ?= "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" # pragma: allowlist secret ocm/setup: @echo -n "$(OCM_OFFLINE_TOKEN)" > secrets/ocm-service.token @echo -n "" > secrets/ocm-service.clientId @echo -n "" > secrets/ocm-service.clientSecret -ifeq ($(OCM_ENV), integration) - @if [[ -n "$(DOCKER_PR_CHECK)" ]]; then echo -n "$(OCM_CLIENT_ID)" > secrets/ocm-service.clientId; echo -n "$(OCM_CLIENT_SECRET)" > secrets/ocm-service.clientSecret; fi; -endif .PHONY: ocm/setup # create project where the service will be deployed in an OpenShift cluster From a332cab9d873813db1f48c7639ac89d20668ebe1 Mon Sep 17 00:00:00 2001 From: Ludovic Cleroux Date: Thu, 18 Jan 2024 14:25:41 +0100 Subject: [PATCH 02/16] ROX-21779 Try app-protocol annotation (#1603) --- templates/service-template.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/templates/service-template.yml b/templates/service-template.yml index 8afefec639..8422f67953 100644 --- a/templates/service-template.yml +++ b/templates/service-template.yml @@ -1485,6 +1485,7 @@ objects: annotations: description: Exposes and load balances the fleet-manager pods going through envoy beforehand service.alpha.openshift.io/serving-cert-secret-name: fleet-manager-envoy-tls + service.alpha.kubernetes.io/app-protocols: '{"https":"HTTPS"}' spec: selector: app: fleet-manager @@ -1492,6 +1493,7 @@ objects: - port: 9001 targetPort: 9001 protocol: TCP + name: https - kind: Service apiVersion: v1 @@ -1503,6 +1505,7 @@ objects: annotations: description: Exposes and load balances the fleet-manager pods. Only targets the active fleet-manager pod. service.alpha.openshift.io/serving-cert-secret-name: fleet-manager-active-tls + service.alpha.kubernetes.io/app-protocols: '{"https":"HTTPS"}' spec: selector: app: fleet-manager @@ -1511,6 +1514,7 @@ objects: - port: 9001 targetPort: 9001 protocol: TCP + name: https # Services for diagnostic ports (not part of main service because we # don't want exposing them externally through same route). From cdb2c6afaf7bf7271b810615fca498d61c9df2c9 Mon Sep 17 00:00:00 2001 From: Ludovic Cleroux Date: Thu, 18 Jan 2024 14:32:33 +0100 Subject: [PATCH 03/16] Revert "ROX-21779 Try app-protocol annotation (#1603)" (#1604) This reverts commit a332cab9d873813db1f48c7639ac89d20668ebe1. --- templates/service-template.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/templates/service-template.yml b/templates/service-template.yml index 8422f67953..8afefec639 100644 --- a/templates/service-template.yml +++ b/templates/service-template.yml @@ -1485,7 +1485,6 @@ objects: annotations: description: Exposes and load balances the fleet-manager pods going through envoy beforehand service.alpha.openshift.io/serving-cert-secret-name: fleet-manager-envoy-tls - service.alpha.kubernetes.io/app-protocols: '{"https":"HTTPS"}' spec: selector: app: fleet-manager @@ -1493,7 +1492,6 @@ objects: - port: 9001 targetPort: 9001 protocol: TCP - name: https - kind: Service apiVersion: v1 @@ -1505,7 +1503,6 @@ objects: annotations: description: Exposes and load balances the fleet-manager pods. Only targets the active fleet-manager pod. service.alpha.openshift.io/serving-cert-secret-name: fleet-manager-active-tls - service.alpha.kubernetes.io/app-protocols: '{"https":"HTTPS"}' spec: selector: app: fleet-manager @@ -1514,7 +1511,6 @@ objects: - port: 9001 targetPort: 9001 protocol: TCP - name: https # Services for diagnostic ports (not part of main service because we # don't want exposing them externally through same route). From 1d5f45a3839b1422ea2e9bed0f43ff46c8aaf5af Mon Sep 17 00:00:00 2001 From: Ludovic Cleroux Date: Thu, 18 Jan 2024 14:33:38 +0100 Subject: [PATCH 04/16] ROX-21779 Add envoy active listener (#1602) ROX-21779 Add envoy active route --- templates/service-template.yml | 146 ++++++++++++++++----------------- 1 file changed, 72 insertions(+), 74 deletions(-) diff --git a/templates/service-template.yml b/templates/service-template.yml index 8afefec639..ed20ac7eb8 100644 --- a/templates/service-template.yml +++ b/templates/service-template.yml @@ -859,87 +859,15 @@ objects: address: 0.0.0.0 port_value: 9001 listener_filters: - - name: tls_inspector - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector filter_chains: - - filter_chain_match: - server_names: - - "fleet-manager-active" - - "fleet-manager-active.${NAMESPACE}" - - "fleet-manager-active.${NAMESPACE}.svc" - - "fleet-manager-active.${NAMESPACE}.svc.cluster" - - "fleet-manager-active.${NAMESPACE}.svc.cluster.local" - - "romndkjdq62p7sr.api.integration.openshift.com" - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - full_scan_certs_on_sni_mismatch: true - common_tls_context: - tls_certificates: - - certificate_chain: {filename: "/secrets/active-tls/tls.crt"} - private_key: {filename: "/secrets/active-tls/tls.key"} - - certificate_chain: {filename: "/secrets/active-tls/tls.crt"} - private_key: {filename: "/secrets/active-tls/tls.key"} - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - access_log: - - name: envoy.access_loggers.file - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog - path: /dev/stdout - stat_prefix: ingress - route_config: - name: backend - virtual_hosts: - - name: all - domains: - - "*" - routes: - - name: default - match: - prefix: / - route: - cluster: backend - # Add security headers. - typed_per_filter_config: - lua_security_headers: - "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute - source_code: - inline_string: | - function envoy_on_response(response_handle) - contentSecurityPolicy = "default-src 'none'; script-src 'self'; img-src 'self'; style-src 'self'; connect-src 'self'"; - response_handle:headers():add("Content-Security-Policy", contentSecurityPolicy); - response_handle:headers():add("X-Frame-Options", "deny"); - response_handle:headers():add("X-XSS-Protection", "1; mode=block"); - response_handle:headers():add("X-Content-Type-Options", "nosniff"); - response_handle:headers():add("Referrer-Policy", "no-referrer"); - response_handle:headers():add("X-Download-Options", "noopen"); - response_handle:headers():add("X-DNS-Prefetch-Control", "off"); - response_handle:headers():add("Strict-Transport-Security", "max-age=31536000; includeSubDomains"); - end - http_filters: - - name: lua_security_headers - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - transport_socket: name: envoy.transport_sockets.tls typed_config: "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - full_scan_certs_on_sni_mismatch: true common_tls_context: tls_certificates: - certificate_chain: {filename: "/secrets/tls/tls.crt"} private_key: {filename: "/secrets/tls/tls.key"} - - certificate_chain: {filename: "/secrets/active-tls/tls.crt"} - private_key: {filename: "/secrets/active-tls/tls.key"} filters: - name: envoy.filters.network.http_connection_manager typed_config: @@ -1060,6 +988,73 @@ objects: # code: "CENTRALS-MGMT-429" # reason: "Too Many Requests" + # We need a second listener on a different port, because the openshift ingress does + # not forward SNI headers, and envoy is not able to determine which certificate to use + # for TLS termination. So we need a whole new listener with a different port. + # This has the same config as the 9001 listener, but listens on port 9002 and uses + # the fleet-manager-active..svc certificate. + - name: ingress-active + address: + socket_address: + address: 0.0.0.0 + port_value: 9002 + listener_filters: + filter_chains: + - transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + common_tls_context: + tls_certificates: + - certificate_chain: {filename: "/secrets/active-tls/tls.crt"} + private_key: {filename: "/secrets/active-tls/tls.key"} + filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/stdout + stat_prefix: ingress + route_config: + name: backend + virtual_hosts: + - name: all + domains: + - "*" + routes: + - name: default + match: + prefix: / + route: + cluster: backend + # Add security headers. + typed_per_filter_config: + lua_security_headers: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute + source_code: + inline_string: | + function envoy_on_response(response_handle) + contentSecurityPolicy = "default-src 'none'; script-src 'self'; img-src 'self'; style-src 'self'; connect-src 'self'"; + response_handle:headers():add("Content-Security-Policy", contentSecurityPolicy); + response_handle:headers():add("X-Frame-Options", "deny"); + response_handle:headers():add("X-XSS-Protection", "1; mode=block"); + response_handle:headers():add("X-Content-Type-Options", "nosniff"); + response_handle:headers():add("Referrer-Policy", "no-referrer"); + response_handle:headers():add("X-Download-Options", "noopen"); + response_handle:headers():add("X-DNS-Prefetch-Control", "off"); + response_handle:headers():add("Strict-Transport-Security", "max-age=31536000; includeSubDomains"); + end + http_filters: + - name: lua_security_headers + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + - kind: Deployment apiVersion: apps/v1 metadata: @@ -1409,6 +1404,9 @@ objects: - name: api-envoy protocol: TCP containerPort: 9001 + - name: api-envoy-active + protocol: TCP + containerPort: 9002 - name: metrics-envoy protocol: TCP containerPort: 9000 @@ -1508,8 +1506,8 @@ objects: app: fleet-manager fleet-manager-active: "true" ports: - - port: 9001 - targetPort: 9001 + - port: 9002 + targetPort: 9002 protocol: TCP # Services for diagnostic ports (not part of main service because we From 87b3b4f62b5d0396aa09d8fe75440154521b139f Mon Sep 17 00:00:00 2001 From: Ludovic Cleroux Date: Thu, 18 Jan 2024 14:59:35 +0100 Subject: [PATCH 05/16] ROX-21779 Set new FM_ENDPOINT for integration (#1605) --- dp-terraform/helm/rhacs-terraform/terraform_cluster.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dp-terraform/helm/rhacs-terraform/terraform_cluster.sh b/dp-terraform/helm/rhacs-terraform/terraform_cluster.sh index cd8c60538e..8c0fd8c791 100755 --- a/dp-terraform/helm/rhacs-terraform/terraform_cluster.sh +++ b/dp-terraform/helm/rhacs-terraform/terraform_cluster.sh @@ -50,7 +50,7 @@ case $ENVIRONMENT in ;; integration) - FM_ENDPOINT="https://qj3layty4dynlnz.api.integration.openshift.com" + FM_ENDPOINT="https://romndkjdq62p7sr.api.integration.openshift.com" OBSERVABILITY_GITHUB_TAG="master" OBSERVABILITY_OBSERVATORIUM_GATEWAY="https://observatorium-mst.api.stage.openshift.com" OBSERVABILITY_OPERATOR_VERSION="v4.2.1" From 24eb4814d2ddbea77ea50a5801c0a4a322e88c0b Mon Sep 17 00:00:00 2001 From: Yury Kovalev <8366110+kovayur@users.noreply.github.com> Date: Thu, 18 Jan 2024 16:24:46 +0100 Subject: [PATCH 06/16] Fixed race condition in integration tests (#1595) --- .github/workflows/ci.yaml | 2 - .openshift-ci/tests/e2e-test.sh | 5 -- .secrets.baseline | 22 +++---- dev/env/scripts/lib.sh | 2 +- .../dinosaur/pkg/environments/development.go | 1 + .../dinosaur/pkg/environments/integration.go | 1 + pkg/db/dinosaur.go | 1 + pkg/server/server_config.go | 22 +++---- pkg/serviceregistration/leader.go | 60 +++++++++++++------ pkg/serviceregistration/service.go | 50 ++++++---------- templates/service-template.yml | 5 ++ test/helper.go | 2 - 12 files changed, 92 insertions(+), 81 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3aebbb57fb..95593ea2b0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -54,9 +54,7 @@ jobs: name: "Verify & Test" runs-on: ubuntu-latest env: - # TODO make sure that the secrets are configured for your repository OCM_ENV: integration - E2E: "true" # Dummy SSO variables SSO_CLIENT_ID: ${{ secrets.SSO_CLIENT_ID }} SSO_CLIENT_SECRET: ${{ secrets.SSO_CLIENT_SECRET }} diff --git a/.openshift-ci/tests/e2e-test.sh b/.openshift-ci/tests/e2e-test.sh index 2582372ec1..c79ce9ad61 100755 --- a/.openshift-ci/tests/e2e-test.sh +++ b/.openshift-ci/tests/e2e-test.sh @@ -19,11 +19,6 @@ fi up.sh log "Environment up and running" -log "Waiting for fleet-manager to complete leader election..." -# Don't have a better way yet to wait until fleet-manager has completed the leader election. -$KUBECTL -n "$ACSCS_NAMESPACE" logs -l application=fleet-manager -c fleet-manager -f --tail=-1 | - grep -q --line-buffered --max-count=1 'started leading' || true -sleep 1 FAIL=0 if [[ "$SKIP_TESTS" == "true" ]]; then diff --git a/.secrets.baseline b/.secrets.baseline index 0f50573ab0..b053b13799 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -493,70 +493,70 @@ "filename": "templates/service-template.yml", "hashed_secret": "13032f402fed753c2248419ea4f69f99931f6dbc", "is_verified": false, - "line_number": 522 + "line_number": 526 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "30025f80f6e22cdafb85db387d50f90ea884576a", "is_verified": false, - "line_number": 522 + "line_number": 526 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "355f24fd038bcaf85617abdcaa64af51ed19bbcf", "is_verified": false, - "line_number": 522 + "line_number": 526 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "3d8a1dcd2c3c765ce35c9a9552d23273cc4ddace", "is_verified": false, - "line_number": 522 + "line_number": 526 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "4ac7b0522761eba972467942cd5cd7499dd2c361", "is_verified": false, - "line_number": 522 + "line_number": 526 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "7639ab2a6bcf2ea30a055a99468c9cd844d4c22a", "is_verified": false, - "line_number": 522 + "line_number": 526 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "b56360daf4793d2a74991a972b34d95bc00fb2da", "is_verified": false, - "line_number": 522 + "line_number": 526 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "c9a73ef9ee8ce9f38437227801c70bcc6740d1a1", "is_verified": false, - "line_number": 522 + "line_number": 526 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "14736999d9940728c5294277831a702f7882dece", "is_verified": false, - "line_number": 559 + "line_number": 563 }, { "type": "Secret Keyword", "filename": "templates/service-template.yml", "hashed_secret": "4e199b4a1c40b497a95fcd1cd896351733849949", "is_verified": false, - "line_number": 706, + "line_number": 710, "is_secret": false } ], @@ -586,5 +586,5 @@ } ] }, - "generated_at": "2024-01-17T10:24:51Z" + "generated_at": "2024-01-17T16:46:11Z" } diff --git a/dev/env/scripts/lib.sh b/dev/env/scripts/lib.sh index 487243e085..e2b676d425 100644 --- a/dev/env/scripts/lib.sh +++ b/dev/env/scripts/lib.sh @@ -142,7 +142,7 @@ init() { export RHACS_TARGETED_OPERATOR_UPGRADES=${RHACS_TARGETED_OPERATOR_UPGRADES:-$RHACS_TARGETED_OPERATOR_UPGRADES_DEFAULT} export RHACS_GITOPS_ENABLED=${RHACS_GITOPS_ENABLED:-$RHACS_GITOPS_ENABLED_DEFAULT} - local fleet_manager_command="/usr/local/bin/fleet-manager serve --force-leader --api-server-bindaddress=0.0.0.0:8000 --health-check-server-bindaddress=0.0.0.0:8083 --enable-central-external-certificate=$ENABLE_CENTRAL_EXTERNAL_CERTIFICATE --central-domain-name='$CENTRAL_DOMAIN_NAME'" + local fleet_manager_command="/usr/local/bin/fleet-manager serve --api-server-bindaddress=0.0.0.0:8000 --health-check-server-bindaddress=0.0.0.0:8083 --enable-central-external-certificate=$ENABLE_CENTRAL_EXTERNAL_CERTIFICATE --central-domain-name='$CENTRAL_DOMAIN_NAME'" FLEET_MANAGER_CONTAINER_COMMAND_DEFAULT="${fleet_manager_command} || { sleep 120; false; }" FLEETSHARD_SYNC_CONTAINER_COMMAND_DEFAULT="/usr/local/bin/fleetshard-sync" export FLEET_MANAGER_CONTAINER_COMMAND=${FLEET_MANAGER_CONTAINER_COMMAND:-$FLEET_MANAGER_CONTAINER_COMMAND_DEFAULT} diff --git a/internal/dinosaur/pkg/environments/development.go b/internal/dinosaur/pkg/environments/development.go index 24ee1d63d6..2ae94b35c8 100644 --- a/internal/dinosaur/pkg/environments/development.go +++ b/internal/dinosaur/pkg/environments/development.go @@ -39,5 +39,6 @@ func NewDevelopmentEnvLoader() environments.EnvLoader { "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", } } diff --git a/internal/dinosaur/pkg/environments/integration.go b/internal/dinosaur/pkg/environments/integration.go index 7897e4a1b0..5c7ec2f3f2 100644 --- a/internal/dinosaur/pkg/environments/integration.go +++ b/internal/dinosaur/pkg/environments/integration.go @@ -29,6 +29,7 @@ func (b IntegrationEnvLoader) Defaults() map[string]string { "enable-https": "false", "enable-metrics-https": "false", "enable-terms-acceptance": "false", + "enable-leader-election": "false", "ocm-debug": "false", "enable-ocm-mock": "true", "ocm-mock-mode": ocm.MockModeEmulateServer, diff --git a/pkg/db/dinosaur.go b/pkg/db/dinosaur.go index 479208cb7f..0c192b5d33 100644 --- a/pkg/db/dinosaur.go +++ b/pkg/db/dinosaur.go @@ -4,4 +4,5 @@ import "time" // DinosaurAdditionalLeasesExpireTime Set new additional leases expire time to a minute later from now so that the old "dinosaur" leases finishes // its execution before the new jobs kicks in. +// Not used in leader election anymore, kept for database migrations var DinosaurAdditionalLeasesExpireTime = time.Now().Add(1 * time.Minute) diff --git a/pkg/server/server_config.go b/pkg/server/server_config.go index efe77b0da2..70a45cc4cd 100644 --- a/pkg/server/server_config.go +++ b/pkg/server/server_config.go @@ -19,20 +19,21 @@ type ServerConfig struct { // For production it is "https://api.openshift.com" PublicHostURL string `json:"public_url"` EnableTermsAcceptance bool `json:"enable_terms_acceptance"` - ForceLeader bool `json:"force_leader"` + EnableLeaderElection bool `json:"enable_leader_election"` } // NewServerConfig ... func NewServerConfig() *ServerConfig { return &ServerConfig{ - BindAddress: "localhost:8000", - EnableHTTPS: false, - JwksURL: "https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/certs", - JwksFile: "config/jwks-file.json", - TokenIssuerURL: "https://sso.redhat.com/auth/realms/redhat-external", - HTTPSCertFile: "", - HTTPSKeyFile: "", - PublicHostURL: "http://localhost", + BindAddress: "localhost:8000", + EnableHTTPS: false, + JwksURL: "https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/certs", + JwksFile: "config/jwks-file.json", + TokenIssuerURL: "https://sso.redhat.com/auth/realms/redhat-external", + HTTPSCertFile: "", + HTTPSKeyFile: "", + PublicHostURL: "http://localhost", + EnableLeaderElection: true, } } @@ -47,8 +48,7 @@ func (s *ServerConfig) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&s.JwksFile, "jwks-file", s.JwksFile, "File containing the the JSON web token signing certificates.") fs.StringVar(&s.TokenIssuerURL, "token-issuer-url", s.TokenIssuerURL, "A token issuer URL. Used to validate if a JWT token used for public endpoints was issued from the given URL.") fs.StringVar(&s.PublicHostURL, "public-host-url", s.PublicHostURL, "Public http host URL of the service") - fs.BoolVar(&s.ForceLeader, "force-leader", s.ForceLeader, "Disable leader election (for testing)") - fs.MarkHidden("force-leader") + fs.BoolVar(&s.EnableLeaderElection, "enable-leader-election", s.EnableLeaderElection, "Enable leader election") } // ReadFiles ... diff --git a/pkg/serviceregistration/leader.go b/pkg/serviceregistration/leader.go index 616734f281..3fe0c2b9c9 100644 --- a/pkg/serviceregistration/leader.go +++ b/pkg/serviceregistration/leader.go @@ -4,8 +4,12 @@ package serviceregistration import ( "context" "fmt" + "github.com/stackrox/acs-fleet-manager/pkg/workers" + "k8s.io/client-go/rest" + "math" + "os" "strconv" "sync/atomic" "time" @@ -36,7 +40,21 @@ type leaderWorker struct { } // newWorker creates a new leaderWorker -func newWorker(namespaceName, podName string, client kubernetes.Interface, workers []workers.Worker) *leaderWorker { +func newWorker(workers []workers.Worker) *leaderWorker { + config, err := rest.InClusterConfig() + if err != nil { + panic(err) + } + var ok bool + client := kubernetes.NewForConfigOrDie(config) + namespaceName, ok := os.LookupEnv("NAMESPACE_NAME") + if !ok { + panic("NAMESPACE_NAME not set") + } + podName, ok := os.LookupEnv("POD_NAME") + if !ok { + panic("POD_NAME not set") + } return &leaderWorker{ namespaceName: namespaceName, podName: podName, @@ -74,27 +92,13 @@ func (l *leaderWorker) run(ctx context.Context) error { glog.Info("[serviceregistration] started leading") l.isLeader.Store(true) l.updateWithRetry(ctx) - for _, worker := range l.workers { - if !worker.IsRunning() { - glog.V(1).Infoln(fmt.Sprintf("[serviceregistration] starting worker %q with id %q", worker.GetWorkerType(), worker.GetID())) - worker.Start() - } else { - glog.V(1).Infoln(fmt.Sprintf("[serviceregistration] worker %q with id %q already running", worker.GetWorkerType(), worker.GetID())) - } - } + startWorkers(l.workers) }, OnStoppedLeading: func() { glog.Info("[serviceregistration] stopped leading") l.isLeader.Store(false) l.updateWithRetry(ctx) - for _, worker := range l.workers { - if worker.IsRunning() { - glog.V(1).Infoln(fmt.Sprintf("[serviceregistration] stopping worker %q with id %q", worker.GetWorkerType(), worker.GetID())) - worker.Stop() - } else { - glog.V(1).Infoln(fmt.Sprintf("[serviceregistration] worker %q with id %q already stopped", worker.GetWorkerType(), worker.GetID())) - } - } + stopWorkers(l.workers) }, }, }) @@ -173,3 +177,25 @@ func (l *leaderWorker) update(ctx context.Context) error { return nil } } + +func startWorkers(workers []workers.Worker) { + for _, worker := range workers { + if !worker.IsRunning() { + glog.V(1).Infoln(fmt.Sprintf("[serviceregistration] starting worker %q with id %q", worker.GetWorkerType(), worker.GetID())) + worker.Start() + } else { + glog.V(1).Infoln(fmt.Sprintf("[serviceregistration] worker %q with id %q already running", worker.GetWorkerType(), worker.GetID())) + } + } +} + +func stopWorkers(workers []workers.Worker) { + for _, worker := range workers { + if worker.IsRunning() { + glog.V(1).Infoln(fmt.Sprintf("[serviceregistration] stopping worker %q with id %q", worker.GetWorkerType(), worker.GetID())) + worker.Stop() + } else { + glog.V(1).Infoln(fmt.Sprintf("[serviceregistration] worker %q with id %q already stopped", worker.GetWorkerType(), worker.GetID())) + } + } +} diff --git a/pkg/serviceregistration/service.go b/pkg/serviceregistration/service.go index d0f76be506..53e88f4dc7 100644 --- a/pkg/serviceregistration/service.go +++ b/pkg/serviceregistration/service.go @@ -2,56 +2,42 @@ package serviceregistration import ( "context" + "github.com/golang/glog" + "github.com/stackrox/acs-fleet-manager/pkg/server" "github.com/stackrox/acs-fleet-manager/pkg/workers" - "k8s.io/client-go/kubernetes/fake" - "os" - - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" ) // Service is the service for service registration type Service struct { - ctx context.Context - cancel context.CancelFunc - workers []workers.Worker + ctx context.Context + cancel func() + workers []workers.Worker + serverConfig *server.ServerConfig } // NewService returns a new Service. -func NewService(workers []workers.Worker) *Service { +func NewService(workers []workers.Worker, serverConfig *server.ServerConfig) *Service { return &Service{ - workers: workers, + workers: workers, + serverConfig: serverConfig, } } // Start implements Service.Start func (s *Service) Start() { glog.Info("starting service registration") - s.ctx, s.cancel = context.WithCancel(context.Background()) - var client kubernetes.Interface - var namespaceName, podName string - if e2e, _ := os.LookupEnv("E2E"); e2e == "true" { - client = fake.NewSimpleClientset() - namespaceName = "e2e-namespace" - podName = "e2e-test-pod" - } else { - config, err := rest.InClusterConfig() - if err != nil { - panic(err) - } - var ok bool - client = kubernetes.NewForConfigOrDie(config) - namespaceName, ok = os.LookupEnv("NAMESPACE_NAME") - if !ok { - panic("NAMESPACE_NAME not set") - } - podName, ok = os.LookupEnv("POD_NAME") - if !ok { - panic("POD_NAME not set") + if !s.serverConfig.EnableLeaderElection { + glog.Info("leader election disabled") + s.cancel = func() { + stopWorkers(s.workers) } + startWorkers(s.workers) + return } - l := newWorker(namespaceName, podName, client, s.workers) + + s.ctx, s.cancel = context.WithCancel(context.Background()) + l := newWorker(s.workers) if err := l.run(s.ctx); err != nil { glog.Errorf("error running leader election: %v", err) panic(err) diff --git a/templates/service-template.yml b/templates/service-template.yml index ed20ac7eb8..a1c77b5102 100644 --- a/templates/service-template.yml +++ b/templates/service-template.yml @@ -422,6 +422,10 @@ parameters: description: Whether to enable targeted operator upgrades value: "false" +- name: ENABLE_LEADER_ELECTION + description: Enable leader election + value: "true" + objects: - kind: ConfigMap apiVersion: v1 @@ -1363,6 +1367,7 @@ objects: - --central-request-internal-user-agents=${CENTRAL_REQUEST_INTERNAL_USER_AGENTS} - --telemetry-endpoint=${TELEMETRY_ENDPONT} - --telemetry-storage-key-secret-file=/secrets/fleet-manager-credentials/telemetry.storageKey + - --enable-leader-election=${ENABLE_LEADER_ELECTION} - -v=${GLOG_V} resources: requests: diff --git a/test/helper.go b/test/helper.go index de6f993bd1..ecb7457e69 100644 --- a/test/helper.go +++ b/test/helper.go @@ -114,8 +114,6 @@ func NewHelperWithHooks(t *testing.T, httpServer *httptest.Server, configuration env.MustResolveAll(&ocmConfig, &serverConfig, &iamConfig, ¢ralConfig) - db.DinosaurAdditionalLeasesExpireTime = time.Now().Add(-time.Minute) // set dinosaurs lease as expired so that a new leader is elected for each of the leases - // Create a new helper authHelper, err := auth.NewAuthHelper(jwtKeyFile, jwtCAFile, serverConfig.TokenIssuerURL) if err != nil { From 0e84778905d50e23b48b5fe98f52f150d6921602 Mon Sep 17 00:00:00 2001 From: Yury Kovalev <8366110+kovayur@users.noreply.github.com> Date: Thu, 18 Jan 2024 16:57:09 +0100 Subject: [PATCH 07/16] Native arm64 docker build with buildkit (#1601) --- .github/workflows/ci.yaml | 4 +++- .openshift-ci/e2e-runtime/Dockerfile | 2 +- .openshift-ci/e2e-runtime/e2e_dockerized.sh | 1 - Dockerfile | 12 ++++++++---- Dockerfile.tools | 4 ++-- Makefile | 19 +++++++------------ dev/env/scripts/docker.sh | 2 +- probe/Dockerfile | 4 ++-- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 95593ea2b0..2f9429181b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -134,6 +134,8 @@ jobs: uses: actions/checkout@v2 with: fetch-depth: 0 # Critical for correct image detection in Makefile + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - name: Build and push fleet-manager-tools image to quay.io if: github.event_name == 'push' env: @@ -142,4 +144,4 @@ jobs: - name: Build and push fleetshard-operator image to quay.io run: make image/push/fleetshard-operator - name: Build and push fleet-manager image to quay.io - run: make image/push/fleet-manager + run: make image/push/fleet-manager IMAGE_PLATFORM=linux/amd64,linux/arm64 diff --git a/.openshift-ci/e2e-runtime/Dockerfile b/.openshift-ci/e2e-runtime/Dockerfile index f209ba0eec..27cd1e06be 100644 --- a/.openshift-ci/e2e-runtime/Dockerfile +++ b/.openshift-ci/e2e-runtime/Dockerfile @@ -11,7 +11,7 @@ COPY --from=quay.io/operator-framework/operator-sdk:v1.25 /usr/local/bin/operato ENV GOPATH=/go ENV GOROOT=/usr/local/go -ENV PATH="/usr/local/go/bin:${PATH}" +ENV PATH="${GOROOT}/bin:${PATH}" RUN ln -s /usr/bin/oc /usr/bin/kubectl diff --git a/.openshift-ci/e2e-runtime/e2e_dockerized.sh b/.openshift-ci/e2e-runtime/e2e_dockerized.sh index c7e1f1f8e3..9d9c366790 100755 --- a/.openshift-ci/e2e-runtime/e2e_dockerized.sh +++ b/.openshift-ci/e2e-runtime/e2e_dockerized.sh @@ -22,7 +22,6 @@ AWS_SESSION_TOKEN=$(aws configure get aws_session_token --profile=saml) FLEET_MANAGER_IMAGE=$(make -s -C "$GITROOT" full-image-tag) # Run the necessary docker actions out of the container -preload_dependency_images ensure_fleet_manager_image_exists docker build -t acscs-e2e -f "$GITROOT/.openshift-ci/e2e-runtime/Dockerfile" "${GITROOT}" diff --git a/Dockerfile b/Dockerfile index 9f23f00b1c..3a364f03f9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,13 @@ -FROM registry.ci.openshift.org/openshift/release:golang-1.20 AS build +FROM --platform=$BUILDPLATFORM registry.access.redhat.com/ubi8/go-toolset:1.20 AS build +USER root RUN mkdir /src /rds_ca ADD https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem /rds_ca/aws-rds-ca-global-bundle.pem - WORKDIR /src + +RUN go env -w GOCACHE=/go/.cache; \ + go env -w GOMODCACHE=/go/pkg/mod + RUN --mount=type=cache,target=/go/pkg/mod/ \ --mount=type=bind,source=go.sum,target=go.sum \ --mount=type=bind,source=go.mod,target=go.mod \ @@ -11,11 +15,11 @@ RUN --mount=type=cache,target=/go/pkg/mod/ \ COPY . ./ -ARG GOARCH +ARG TARGETARCH RUN --mount=type=cache,target=/go/pkg/mod/ \ --mount=type=cache,target=/go/.cache/ \ - make binary GOOS=linux GOARCH=${GOARCH} + make binary GOOS=linux GOARCH=${TARGETARCH} FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9 as standard diff --git a/Dockerfile.tools b/Dockerfile.tools index 869862d297..56dfc32a22 100644 --- a/Dockerfile.tools +++ b/Dockerfile.tools @@ -1,5 +1,5 @@ -FROM registry.ci.openshift.org/openshift/release:golang-1.20 AS build - +FROM registry.access.redhat.com/ubi8/go-toolset:1.20 AS build +USER root RUN mkdir /src WORKDIR /src COPY . ./ diff --git a/Makefile b/Makefile index 913273f458..e5ad58d630 100644 --- a/Makefile +++ b/Makefile @@ -62,8 +62,9 @@ else GOBIN=$(shell $(GO) env GOBIN) endif -# Used for local builds to support arm64 -goarch=$(shell go env GOARCH) +ifeq ($(IMAGE_PLATFORM),) +IMAGE_PLATFORM=linux/$(shell $(GO) env GOARCH) +endif LOCAL_BIN_PATH := ${PROJECT_PATH}/bin # Add the project-level bin directory into PATH. Needed in order @@ -496,12 +497,9 @@ docker/login/internal: $(DOCKER) login -u kubeadmin --password-stdin <<< $(shell oc whoami -t) $(shell oc get route default-route -n openshift-image-registry -o jsonpath="{.spec.host}") .PHONY: docker/login/internal -# Build the image using by specifying a specific image target within the Dockerfile. -image/build: GOARCH?=amd64 -image/build: IMAGE_REF="$(external_image_registry)/$(image_repository):$(image_tag)" +# Build the image image/build: - DOCKER_CONFIG=${DOCKER_CONFIG} $(DOCKER) build -t $(IMAGE_REF) --build-arg GOARCH=$(GOARCH) . - DOCKER_CONFIG=${DOCKER_CONFIG} $(DOCKER) tag $(IMAGE_REF) $(SHORT_IMAGE_REF) + DOCKER_CONFIG=${DOCKER_CONFIG} DOCKER_BUILDKIT=1 $(DOCKER) build -t $(SHORT_IMAGE_REF) . @echo "New image tag: $(SHORT_IMAGE_REF). You might want to" @echo "export FLEET_MANAGER_IMAGE=$(SHORT_IMAGE_REF)" ifeq ("$(CLUSTER_TYPE)","kind") @@ -536,8 +534,8 @@ image/push: image/push/fleet-manager image/push/probe .PHONY: image/push image/push/fleet-manager: IMAGE_REF="$(external_image_registry)/$(image_repository):$(image_tag)" -image/push/fleet-manager: image/build - DOCKER_CONFIG=${DOCKER_CONFIG} $(DOCKER) push $(IMAGE_REF) +image/push/fleet-manager: + DOCKER_CONFIG=${DOCKER_CONFIG} $(DOCKER) buildx build -t $(IMAGE_REF) --platform $(IMAGE_PLATFORM) --push . @echo @echo "Image was pushed as $(IMAGE_REF). You might want to" @echo "export FLEET_MANAGER_IMAGE=$(IMAGE_REF)" @@ -846,15 +844,12 @@ deploy/bootstrap: .PHONY: deploy/bootstrap # Deploy local images fast for development -deploy/dev-fast: GOARCH=$(goarch) deploy/dev-fast: image/build deploy/dev-fast/fleet-manager deploy/dev-fast/fleetshard-sync -deploy/dev-fast/fleet-manager: GOARCH=$(goarch) deploy/dev-fast/fleet-manager: image/build kubectl -n $(ACSCS_NAMESPACE) set image deploy/fleet-manager fleet-manager=$(SHORT_IMAGE_REF) db-migrate=$(SHORT_IMAGE_REF) kubectl -n $(ACSCS_NAMESPACE) delete pod -l application=fleet-manager -deploy/dev-fast/fleetshard-sync: GOARCH=$(goarch) deploy/dev-fast/fleetshard-sync: image/build kubectl -n $(ACSCS_NAMESPACE) set image deploy/fleetshard-sync fleetshard-sync=$(SHORT_IMAGE_REF) kubectl -n $(ACSCS_NAMESPACE) delete pod -l application=fleetshard-sync diff --git a/dev/env/scripts/docker.sh b/dev/env/scripts/docker.sh index 060b4ceb60..5d67f8d3a1 100644 --- a/dev/env/scripts/docker.sh +++ b/dev/env/scripts/docker.sh @@ -61,7 +61,7 @@ ensure_fleet_manager_image_exists() { # Attempt to build this image. if [[ "$FLEET_MANAGER_IMAGE" == "$(make -s -C "${GITROOT}" full-image-tag)" ]]; then log "Building local image..." - make -C "${GITROOT}" image/build GOARCH="$(go env GOARCH)" # supports arm64 + make -C "${GITROOT}" image/build else die "Cannot find image '${FLEET_MANAGER_IMAGE}' and don't know how to build it" fi diff --git a/probe/Dockerfile b/probe/Dockerfile index 630dc818f1..bea2b46a1a 100644 --- a/probe/Dockerfile +++ b/probe/Dockerfile @@ -1,5 +1,5 @@ -FROM registry.ci.openshift.org/openshift/release:golang-1.20 AS build - +FROM registry.access.redhat.com/ubi8/go-toolset:1.20 AS build +USER root ENV GOFLAGS="-mod=mod" RUN mkdir /src From fc9b30caae051dd20b437caffcc04906caf71fc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 09:06:11 +0100 Subject: [PATCH 08/16] Bump github.com/go-resty/resty/v2 from 2.10.0 to 2.11.0 (#1606) Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.10.0 to 2.11.0. - [Release notes](https://github.com/go-resty/resty/releases) - [Commits](https://github.com/go-resty/resty/compare/v2.10.0...v2.11.0) --- updated-dependencies: - dependency-name: github.com/go-resty/resty/v2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 369866f855..5f8339cdb2 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/ghodss/yaml v1.0.0 github.com/go-gormigrate/gormigrate/v2 v2.1.1 github.com/go-logr/logr v1.4.1 - github.com/go-resty/resty/v2 v2.10.0 + github.com/go-resty/resty/v2 v2.11.0 github.com/goava/di v1.11.1 github.com/gogo/protobuf v1.3.2 github.com/golang-jwt/jwt/v4 v4.5.0 diff --git a/go.sum b/go.sum index 634dfce7ba..6f5582a9df 100644 --- a/go.sum +++ b/go.sum @@ -184,8 +184,8 @@ github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+ github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= -github.com/go-resty/resty/v2 v2.10.0 h1:Qla4W/+TMmv0fOeeRqzEpXPLfTUnR5HZ1+lGs+CkiCo= -github.com/go-resty/resty/v2 v2.10.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= +github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= +github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v2.20.0+incompatible h1:4Xh3bDzO29j4TWNOI+24ubc0vbVFMg2PMnXKxK54/CA= From 90f5715889f18611864c0bd238c245affc5c6311 Mon Sep 17 00:00:00 2001 From: Ludovic Cleroux Date: Fri, 19 Jan 2024 13:00:18 +0100 Subject: [PATCH 09/16] ROX-21779 Fix api-envoy-active port name too long error (#1609) --- templates/service-template.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/service-template.yml b/templates/service-template.yml index a1c77b5102..8e7f9c75c0 100644 --- a/templates/service-template.yml +++ b/templates/service-template.yml @@ -1409,7 +1409,7 @@ objects: - name: api-envoy protocol: TCP containerPort: 9001 - - name: api-envoy-active + - name: api-active protocol: TCP containerPort: 9002 - name: metrics-envoy @@ -1502,7 +1502,7 @@ objects: name: fleet-manager-active labels: app: fleet-manager - port: api-envoy + port: api-active annotations: description: Exposes and load balances the fleet-manager pods. Only targets the active fleet-manager pod. service.alpha.openshift.io/serving-cert-secret-name: fleet-manager-active-tls From 11fb07a398c540aed13625cb70b6ed6641718099 Mon Sep 17 00:00:00 2001 From: Mladen Todorovic Date: Fri, 19 Jan 2024 13:41:04 +0100 Subject: [PATCH 10/16] ROX-21695: Increase collector memory usage for CO-RE BPF collection method (#1607) --- .../helm/rhacs-terraform/charts/secured-cluster/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dp-terraform/helm/rhacs-terraform/charts/secured-cluster/values.yaml b/dp-terraform/helm/rhacs-terraform/charts/secured-cluster/values.yaml index 9aef521780..1e358fa909 100644 --- a/dp-terraform/helm/rhacs-terraform/charts/secured-cluster/values.yaml +++ b/dp-terraform/helm/rhacs-terraform/charts/secured-cluster/values.yaml @@ -16,10 +16,10 @@ admissionControl: collector: resources: requests: - memory: 140Mi + memory: 200Mi cpu: 10m limits: - memory: 140Mi + memory: 200Mi collection: "CORE_BPF" compliance: resources: From b1000cb65c61460e0e332619b0bcb8766fceb77e Mon Sep 17 00:00:00 2001 From: Yury Kovalev <8366110+kovayur@users.noreply.github.com> Date: Tue, 23 Jan 2024 16:14:25 +0100 Subject: [PATCH 11/16] ROX-21070: Set fleetshard sync image from Fleet Manager (#1596) --- .secrets.baseline | 22 +- Makefile | 13 +- dev/env/defaults/00-defaults.env | 3 + .../01-fleet-manager-secrets.yaml | 3 + dev/env/scripts/lib.sh | 3 + .../dinosaur/pkg/environments/development.go | 2 - .../dinosaur/pkg/environments/integration.go | 2 - internal/dinosaur/pkg/gitops/provider.go | 7 +- internal/dinosaur/pkg/services/addon.go | 189 +++++++++++------- internal/dinosaur/pkg/services/addon_test.go | 119 +++++++++++ pkg/client/ocm/addon_config.go | 60 ++++++ pkg/client/ocm/client.go | 19 +- pkg/client/ocm/client_moq.go | 44 ++++ pkg/client/ocm/config.go | 55 +++-- pkg/providers/core.go | 1 + templates/secrets-template.yml | 14 +- templates/service-template.yml | 27 +-- 17 files changed, 450 insertions(+), 133 deletions(-) create mode 100644 pkg/client/ocm/addon_config.go diff --git a/.secrets.baseline b/.secrets.baseline index b053b13799..962020ff27 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -493,70 +493,70 @@ "filename": "templates/service-template.yml", "hashed_secret": "13032f402fed753c2248419ea4f69f99931f6dbc", "is_verified": false, - "line_number": 526 + "line_number": 524 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "30025f80f6e22cdafb85db387d50f90ea884576a", "is_verified": false, - "line_number": 526 + "line_number": 524 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "355f24fd038bcaf85617abdcaa64af51ed19bbcf", "is_verified": false, - "line_number": 526 + "line_number": 524 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "3d8a1dcd2c3c765ce35c9a9552d23273cc4ddace", "is_verified": false, - "line_number": 526 + "line_number": 524 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "4ac7b0522761eba972467942cd5cd7499dd2c361", "is_verified": false, - "line_number": 526 + "line_number": 524 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "7639ab2a6bcf2ea30a055a99468c9cd844d4c22a", "is_verified": false, - "line_number": 526 + "line_number": 524 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "b56360daf4793d2a74991a972b34d95bc00fb2da", "is_verified": false, - "line_number": 526 + "line_number": 524 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "c9a73ef9ee8ce9f38437227801c70bcc6740d1a1", "is_verified": false, - "line_number": 526 + "line_number": 524 }, { "type": "Base64 High Entropy String", "filename": "templates/service-template.yml", "hashed_secret": "14736999d9940728c5294277831a702f7882dece", "is_verified": false, - "line_number": 563 + "line_number": 561 }, { "type": "Secret Keyword", "filename": "templates/service-template.yml", "hashed_secret": "4e199b4a1c40b497a95fcd1cd896351733849949", "is_verified": false, - "line_number": 710, + "line_number": 708, "is_secret": false } ], @@ -586,5 +586,5 @@ } ] }, - "generated_at": "2024-01-17T16:46:11Z" + "generated_at": "2024-01-18T15:43:17Z" } diff --git a/Makefile b/Makefile index e5ad58d630..4541517580 100644 --- a/Makefile +++ b/Makefile @@ -599,6 +599,9 @@ secrets/touch: secrets/ocm-service.clientId \ secrets/ocm-service.clientSecret \ secrets/ocm-service.token \ + secrets/ocm-addon-service.clientId \ + secrets/ocm-addon-service.clientSecret \ + secrets/ocm-addon-service.token \ secrets/rhsso-logs.clientId \ secrets/rhsso-logs.clientSecret \ secrets/rhsso-metrics.clientId \ @@ -662,8 +665,11 @@ observatorium/token-refresher/setup: ocm/setup: OCM_OFFLINE_TOKEN ?= "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" # pragma: allowlist secret ocm/setup: @echo -n "$(OCM_OFFLINE_TOKEN)" > secrets/ocm-service.token + @echo -n "$(OCM_OFFLINE_TOKEN)" > secrets/ocm-addon-service.token @echo -n "" > secrets/ocm-service.clientId @echo -n "" > secrets/ocm-service.clientSecret + @echo -n "" > secrets/ocm-addon-service.clientId + @echo -n "" > secrets/ocm-addon-service.clientSecret .PHONY: ocm/setup # create project where the service will be deployed in an OpenShift cluster @@ -685,6 +691,9 @@ deploy/secrets: -p OCM_SERVICE_CLIENT_ID="$(shell ([ -s './secrets/ocm-service.clientId' ] && [ -z '${OCM_SERVICE_CLIENT_ID}' ]) && cat ./secrets/ocm-service.clientId || echo '${OCM_SERVICE_CLIENT_ID}')" \ -p OCM_SERVICE_CLIENT_SECRET="$(shell ([ -s './secrets/ocm-service.clientSecret' ] && [ -z '${OCM_SERVICE_CLIENT_SECRET}' ]) && cat ./secrets/ocm-service.clientSecret || echo '${OCM_SERVICE_CLIENT_SECRET}')" \ -p OCM_SERVICE_TOKEN="$(shell ([ -s './secrets/ocm-service.token' ] && [ -z '${OCM_SERVICE_TOKEN}' ]) && cat ./secrets/ocm-service.token || echo '${OCM_SERVICE_TOKEN}')" \ + -p OCM_ADDON_SERVICE_CLIENT_ID="$(shell ([ -s './secrets/ocm-addon-service.clientId' ] && [ -z '${OCM_ADDON_SERVICE_CLIENT_ID}' ]) && cat ./secrets/ocm-addon-service.clientId || echo '${OCM_ADDON_SERVICE_CLIENT_ID}')" \ + -p OCM_ADDON_SERVICE_CLIENT_SECRET="$(shell ([ -s './secrets/ocm-addon-service.clientSecret' ] && [ -z '${OCM_ADDON_SERVICE_CLIENT_SECRET}' ]) && cat ./secrets/ocm-addon-service.clientSecret || echo '${OCM_ADDON_SERVICE_CLIENT_SECRET}')" \ + -p OCM_ADDON_SERVICE_TOKEN="$(shell ([ -s './secrets/ocm-addon-service.token' ] && [ -z '${OCM_ADDON_SERVICE_TOKEN}' ]) && cat ./secrets/ocm-addon-service.token || echo '${OCM_ADDON_SERVICE_TOKEN}')" \ -p SENTRY_KEY="$(shell ([ -s './secrets/sentry.key' ] && [ -z '${SENTRY_KEY}' ]) && cat ./secrets/sentry.key || echo '${SENTRY_KEY}')" \ -p AWS_ACCESS_KEY="$(shell ([ -s './secrets/aws.accesskey' ] && [ -z '${AWS_ACCESS_KEY}' ]) && cat ./secrets/aws.accesskey || echo '${AWS_ACCESS_KEY}')" \ -p AWS_ACCOUNT_ID="$(shell ([ -s './secrets/aws.accountid' ] && [ -z '${AWS_ACCOUNT_ID}' ]) && cat ./secrets/aws.accountid || echo '${AWS_ACCOUNT_ID}')" \ @@ -738,8 +747,6 @@ deploy/service: OBSERVABILITY_CONFIG_REPO ?= "https://api.github.com/repos/bf2fc deploy/service: OBSERVABILITY_CONFIG_CHANNEL ?= "resources" deploy/service: OBSERVABILITY_CONFIG_TAG ?= "main" deploy/service: DATAPLANE_CLUSTER_SCALING_TYPE ?= "manual" -deploy/service: CENTRAL_OPERATOR_OPERATOR_ADDON_ID ?= "managed-central-qe" -deploy/service: FLEETSHARD_ADDON_ID ?= "fleetshard-operator-qe" deploy/service: CENTRAL_IDP_ISSUER ?= "https://sso.stage.redhat.com/auth/realms/redhat-external" deploy/service: CENTRAL_IDP_CLIENT_ID ?= "rhacs-ms-dev" deploy/service: CENTRAL_REQUEST_EXPIRATION_TIMEOUT ?= "1h" @@ -779,8 +786,6 @@ deploy/service: deploy/envoy deploy/route -p QUOTA_TYPE="${QUOTA_TYPE}" \ -p FLEETSHARD_OLM_INDEX_IMAGE="${FLEETSHARD_OLM_INDEX_IMAGE}" \ -p CENTRAL_OPERATOR_OLM_INDEX_IMAGE="${CENTRAL_OPERATOR_OLM_INDEX_IMAGE}" \ - -p CENTRAL_OPERATOR_OPERATOR_ADDON_ID="${CENTRAL_OPERATOR_OPERATOR_ADDON_ID}" \ - -p FLEETSHARD_ADDON_ID="${FLEETSHARD_ADDON_ID}" \ -p DATAPLANE_CLUSTER_SCALING_TYPE="${DATAPLANE_CLUSTER_SCALING_TYPE}" \ -p CENTRAL_REQUEST_EXPIRATION_TIMEOUT="${CENTRAL_REQUEST_EXPIRATION_TIMEOUT}" \ | oc apply -f - -n $(NAMESPACE) diff --git a/dev/env/defaults/00-defaults.env b/dev/env/defaults/00-defaults.env index 22a2950eb9..deeb1b4d70 100644 --- a/dev/env/defaults/00-defaults.env +++ b/dev/env/defaults/00-defaults.env @@ -30,6 +30,9 @@ export DATABASE_TLS_CERT_DEFAULT="" export OCM_SERVICE_CLIENT_ID_DEFAULT="" export OCM_SERVICE_CLIENT_SECRET_DEFAULT="" export OCM_SERVICE_TOKEN_DEFAULT="" +export OCM_ADDON_SERVICE_CLIENT_ID_DEFAULT="" +export OCM_ADDON_SERVICE_CLIENT_SECRET_DEFAULT="" +export OCM_ADDON_SERVICE_TOKEN_DEFAULT="" export SENTRY_KEY_DEFAULT="" export AWS_ACCESS_KEY_DEFAULT="" export AWS_ACCOUNT_ID_DEFAULT="" diff --git a/dev/env/manifests/fleet-manager/01-fleet-manager-secrets.yaml b/dev/env/manifests/fleet-manager/01-fleet-manager-secrets.yaml index 04f9755508..85b3c80b72 100644 --- a/dev/env/manifests/fleet-manager/01-fleet-manager-secrets.yaml +++ b/dev/env/manifests/fleet-manager/01-fleet-manager-secrets.yaml @@ -13,6 +13,9 @@ stringData: ocm-service.clientId: "${OCM_SERVICE_CLIENT_ID}" ocm-service.clientSecret: "${OCM_SERVICE_CLIENT_SECRET}" ocm-service.token: "${OCM_SERVICE_TOKEN}" + ocm-addon-service.clientId: "${OCM_ADDON_SERVICE_CLIENT_ID}" + ocm-addon-service.clientSecret: "${OCM_ADDON_SERVICE_CLIENT_SECRET}" + ocm-addon-service.token: "${OCM_ADDON_SERVICE_TOKEN}" sentry.key: "${SENTRY_KEY}" aws.accesskey: "${AWS_ACCESS_KEY}" aws.accountid: "${AWS_ACCOUNT_ID}" diff --git a/dev/env/scripts/lib.sh b/dev/env/scripts/lib.sh index e2b676d425..fbc157e3c6 100644 --- a/dev/env/scripts/lib.sh +++ b/dev/env/scripts/lib.sh @@ -111,6 +111,9 @@ init() { export OCM_SERVICE_CLIENT_ID=${OCM_SERVICE_CLIENT_ID:-$OCM_SERVICE_CLIENT_ID_DEFAULT} export OCM_SERVICE_CLIENT_SECRET=${OCM_SERVICE_CLIENT_SECRET:-$OCM_SERVICE_CLIENT_SECRET_DEFAULT} export OCM_SERVICE_TOKEN=${OCM_SERVICE_TOKEN:-$OCM_SERVICE_TOKEN_DEFAULT} + export OCM_ADDON_SERVICE_CLIENT_ID=${OCM_ADDON_SERVICE_CLIENT_ID:-$OCM_ADDON_SERVICE_CLIENT_ID_DEFAULT} + export OCM_ADDON_SERVICE_CLIENT_SECRET=${OCM_ADDON_SERVICE_CLIENT_SECRET:-$OCM_ADDON_SERVICE_CLIENT_SECRET_DEFAULT} + export OCM_ADDON_SERVICE_TOKEN=${OCM_ADDON_SERVICE_TOKEN:-$OCM_ADDON_SERVICE_TOKEN_DEFAULT} export SENTRY_KEY=${SENTRY_KEY:-$SENTRY_KEY_DEFAULT} export AWS_ACCESS_KEY=${AWS_ACCESS_KEY:-$AWS_ACCESS_KEY_DEFAULT} export AWS_ACCOUNT_ID=${AWS_ACCOUNT_ID:-$AWS_ACCOUNT_ID_DEFAULT} diff --git a/internal/dinosaur/pkg/environments/development.go b/internal/dinosaur/pkg/environments/development.go index 2ae94b35c8..c3bfde829e 100644 --- a/internal/dinosaur/pkg/environments/development.go +++ b/internal/dinosaur/pkg/environments/development.go @@ -25,8 +25,6 @@ func NewDevelopmentEnvLoader() environments.EnvLoader { "quota-type": "quota-management-list", "enable-deletion-of-expired-central": "true", "dataplane-cluster-scaling-type": "manual", - "central-operator-addon-id": "managed-central-qe", - "fleetshard-addon-id": "fleetshard-operator-qe", "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", diff --git a/internal/dinosaur/pkg/environments/integration.go b/internal/dinosaur/pkg/environments/integration.go index 5c7ec2f3f2..b44f42873b 100644 --- a/internal/dinosaur/pkg/environments/integration.go +++ b/internal/dinosaur/pkg/environments/integration.go @@ -44,8 +44,6 @@ func (b IntegrationEnvLoader) Defaults() map[string]string { "quota-type": "quota-management-list", "enable-deletion-of-expired-central": "true", "dataplane-cluster-scaling-type": "auto", // need to set this to 'auto' for integration environment as some tests rely on this - "central-operator-addon-id": "managed-central-qe", - "fleetshard-addon-id": "fleetshard-operator-qe", } } diff --git a/internal/dinosaur/pkg/gitops/provider.go b/internal/dinosaur/pkg/gitops/provider.go index 02da84fb58..026671490c 100644 --- a/internal/dinosaur/pkg/gitops/provider.go +++ b/internal/dinosaur/pkg/gitops/provider.go @@ -1,6 +1,7 @@ package gitops import ( + "os" "reflect" "sync" "sync/atomic" @@ -31,7 +32,11 @@ func NewProvider() ConfigProvider { var reader Reader if features.GitOpsCentrals.Enabled() { - reader = NewFileReader(configPath) + path, exists := os.LookupEnv("GITOPS_CONFIG_PATH") + if !exists { + path = configPath + } + reader = NewFileReader(path) } else { reader = NewEmptyReader() } diff --git a/internal/dinosaur/pkg/services/addon.go b/internal/dinosaur/pkg/services/addon.go index 72a00b20cf..b521e0c38f 100644 --- a/internal/dinosaur/pkg/services/addon.go +++ b/internal/dinosaur/pkg/services/addon.go @@ -6,6 +6,7 @@ import ( "github.com/golang/glog" "github.com/hashicorp/go-multierror" + addonsmgmtv1 "github.com/openshift-online/ocm-sdk-go/addonsmgmt/v1" clustersmgmtv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/dbapi" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/gitops" @@ -16,70 +17,115 @@ import ( "golang.org/x/exp/maps" ) +const fleetshardImageTagParameter = "fleetshardSyncImageTag" + // AddonProvisioner keeps addon installations on the data plane clusters up-to-date type AddonProvisioner struct { - ocmClient ocm.Client + ocmClient ocm.Client + customizations []addonCustomization } // NewAddonProvisioner creates a new instance of AddonProvisioner -func NewAddonProvisioner(ocmClient ocm.ClusterManagementClient) *AddonProvisioner { - return &AddonProvisioner{ - ocmClient: ocmClient, +func NewAddonProvisioner(addonConfig *ocm.AddonConfig, baseConfig *ocm.OCMConfig) (*AddonProvisioner, error) { + addonOCMConfig := *baseConfig + + addonOCMConfig.BaseURL = addonConfig.URL + addonOCMConfig.ClientID = addonConfig.ClientID + addonOCMConfig.ClientSecret = addonConfig.ClientSecret // pragma: allowlist secret + addonOCMConfig.SelfToken = addonConfig.SelfToken + + conn, _, err := ocm.NewOCMConnection(&addonOCMConfig, addonOCMConfig.BaseURL) + if err != nil { + return nil, fmt.Errorf("addon service ocm connection: %w", err) } + return &AddonProvisioner{ + ocmClient: ocm.NewClient(conn), + customizations: initCustomizations(*addonConfig), + }, nil } -type updateDecision struct { - installedInOCM *clustersmgmtv1.AddOnInstallation - expectedConfig gitops.AddonConfig - ocmClient ocm.Client - multiErr *multierror.Error +func initCustomizations(config ocm.AddonConfig) []addonCustomization { + var customizations []addonCustomization + + if config.InheritFleetshardSyncImageTag { + if config.FleetshardSyncImageTag == "" { + glog.Error("fleetshard image tag should not be empty when inherit customization is enabled") + } else { + customizations = append(customizations, inheritFleetshardImageTag(config.FleetshardSyncImageTag)) + } + } + return customizations } +type addonCustomization func(gitops.AddonConfig) gitops.AddonConfig + // Provision installs, upgrades or uninstalls the addons based on a given config -func (p *AddonProvisioner) Provision(cluster api.Cluster, addons []gitops.AddonConfig) error { +func (p *AddonProvisioner) Provision(cluster api.Cluster, expectedConfigs []gitops.AddonConfig) error { var multiErr *multierror.Error clusterID := cluster.ClusterID - updateDecisions := make(map[string]*updateDecision) - for _, addon := range addons { - addonInstallation, addonErr := p.ocmClient.GetAddonInstallation(clusterID, addon.ID) + installedAddons, err := p.getInstalledAddons(cluster) + if err != nil { + multiErr = multierror.Append(multiErr, err) + } + + for _, expectedConfig := range expectedConfigs { + for _, customization := range p.customizations { + expectedConfig = customization(expectedConfig) + } + installedInOCM, addonErr := p.ocmClient.GetAddonInstallation(clusterID, expectedConfig.ID) + installedOnCluster, existOnCluster := installedAddons[expectedConfig.ID] + if existOnCluster { + delete(installedAddons, expectedConfig.ID) // retained installations are absent in GitOps - we need to uninstall them + } if addonErr != nil { if addonErr.Is404() { // addon does not exist, install it - multiErr = multierror.Append(multiErr, p.installAddon(clusterID, addon)) + multiErr = multierror.Append(multiErr, p.installAddon(clusterID, expectedConfig)) } else { - multiErr = multierror.Append(multiErr, fmt.Errorf("failed to get addon %s: %w", addon.ID, addonErr)) + multiErr = multierror.Append(multiErr, fmt.Errorf("failed to get addon %s: %w", expectedConfig.ID, addonErr)) } - } else { - updateDecisions[addonInstallation.ID()] = &updateDecision{ - installedInOCM: addonInstallation, - expectedConfig: addon, - ocmClient: p.ocmClient, - multiErr: multiErr, + continue + } + if updateInProgress(installedInOCM) { + glog.V(10).Infof("Addon %s is not in a final state: %s, skip until the next worker iteration", installedInOCM.ID(), installedInOCM.State()) + continue + } + if expectedConfig.Version == "" { + addon, err := p.ocmClient.GetAddon(expectedConfig.ID) + if err != nil { + multiErr = multierror.Append(multiErr, fmt.Errorf("get addon %s with the latest version: %w", expectedConfig.ID, err)) + continue } + expectedConfig.Version = addon.Version().ID() } - } - installedAddons, err := p.getInstalledAddons(cluster) - if err != nil { - multiErr = multierror.Append(multiErr, err) - } - for _, installedAddon := range installedAddons { - decision, exists := updateDecisions[installedAddon.ID] - if !exists { - // addon is installed on the cluster but not present in gitops config - uninstall it - multiErr = multierror.Append(multiErr, p.uninstallAddon(clusterID, installedAddon.ID)) + if gitOpsConfigDifferent(expectedConfig, installedInOCM) { + multiErr = multierror.Append(multiErr, p.updateAddon(clusterID, expectedConfig)) + continue + } + versionInstalledInOCM, err := p.ocmClient.GetAddonVersion(installedInOCM.ID(), installedInOCM.AddonVersion().ID()) + if err != nil { + multiErr = multierror.Append(multiErr, fmt.Errorf("get addon version object for addon %s with version %s: %w", + installedInOCM.ID(), installedInOCM.AddonVersion().ID(), err)) + continue + } + if !existOnCluster { + glog.V(10).Infof("Addon %s is not installed on the data plane", installedInOCM.ID()) + continue + } + if clusterInstallationDifferent(installedOnCluster, versionInstalledInOCM) { + multiErr = multierror.Append(multiErr, p.updateAddon(clusterID, expectedConfig)) } else { - if decision.updateInProgress() { - glog.V(10).Infof("Addon %s is not in a final state: %s, skip until the next worker iteration", - decision.installedInOCM.ID(), decision.installedInOCM.State()) - } else if decision.needsUpdate(installedAddon) { - multiErr = multierror.Append(multiErr, p.updateAddon(clusterID, decision.expectedConfig)) - } else { - glog.V(10).Infof("Addon %s is already up-to-date", installedAddon.ID) - multiErr = validateUpToDateAddon(multiErr, decision.installedInOCM, installedAddon) - } + glog.V(10).Infof("Addon %s is already up-to-date", installedOnCluster.ID) + multiErr = validateUpToDateAddon(multiErr, installedInOCM, installedOnCluster) } } + + for _, installedAddon := range installedAddons { + // addon is installed on the cluster but not present in gitops config - uninstall it + multiErr = multierror.Append(multiErr, p.uninstallAddon(clusterID, installedAddon.ID)) + } + return errorOrNil(multiErr) } @@ -99,20 +145,23 @@ func validateUpToDateAddon(multiErr *multierror.Error, ocmInstallation *clusters return multiErr } -func (p *AddonProvisioner) getInstalledAddons(cluster api.Cluster) ([]dbapi.AddonInstallation, error) { +func (p *AddonProvisioner) getInstalledAddons(cluster api.Cluster) (map[string]dbapi.AddonInstallation, error) { if !features.AddonAutoUpgrade.Enabled() { glog.V(10).Info("Addon auto upgrade feature is disabled, the existing addon installations will NOT be updated") - return []dbapi.AddonInstallation{}, nil + return map[string]dbapi.AddonInstallation{}, nil } if len(cluster.Addons) == 0 { - glog.V(10).Info("No addons installed on the cluster, skipping") - return []dbapi.AddonInstallation{}, nil + return map[string]dbapi.AddonInstallation{}, nil } var installedAddons []dbapi.AddonInstallation if err := json.Unmarshal(cluster.Addons, &installedAddons); err != nil { - return []dbapi.AddonInstallation{}, fmt.Errorf("unmarshal installed addons: %w", err) + return map[string]dbapi.AddonInstallation{}, fmt.Errorf("unmarshal installed addons: %w", err) + } + result := make(map[string]dbapi.AddonInstallation) + for _, addon := range installedAddons { + result[addon.ID] = addon } - return installedAddons, nil + return result, nil } func errorOrNil(multiErr *multierror.Error) error { @@ -123,7 +172,7 @@ func errorOrNil(multiErr *multierror.Error) error { } func (p *AddonProvisioner) installAddon(clusterID string, config gitops.AddonConfig) error { - addonInstallation, err := newInstallation(config) + addonInstallation, err := p.newInstallation(config) if err != nil { return err } @@ -134,12 +183,17 @@ func (p *AddonProvisioner) installAddon(clusterID string, config gitops.AddonCon return nil } -func newInstallation(config gitops.AddonConfig) (*clustersmgmtv1.AddOnInstallation, error) { - installation, err := clustersmgmtv1.NewAddOnInstallation(). +func (p *AddonProvisioner) newInstallation(config gitops.AddonConfig) (*clustersmgmtv1.AddOnInstallation, error) { + builder := clustersmgmtv1.NewAddOnInstallation(). + ID(config.ID). Addon(clustersmgmtv1.NewAddOn().ID(config.ID)). - AddonVersion(clustersmgmtv1.NewAddOnVersion().ID(config.Version)). - Parameters(convertParametersToOCMAPI(config.Parameters)). - Build() + Parameters(convertParametersToOCMAPI(config.Parameters)) + + if config.Version != "" { + builder = builder.AddonVersion(clustersmgmtv1.NewAddOnVersion().ID(config.Version)) + } + + installation, err := builder.Build() if err != nil { return nil, fmt.Errorf("build new addon installation %s: %w", config.ID, err) @@ -149,7 +203,7 @@ func newInstallation(config gitops.AddonConfig) (*clustersmgmtv1.AddOnInstallati } func (p *AddonProvisioner) updateAddon(clusterID string, config gitops.AddonConfig) error { - update, err := newInstallation(config) + update, err := p.newInstallation(config) if err != nil { return err } @@ -172,23 +226,15 @@ func isFinalState(state clustersmgmtv1.AddOnInstallationState) bool { return state == clustersmgmtv1.AddOnInstallationStateFailed || state == clustersmgmtv1.AddOnInstallationStateReady } -func (c *updateDecision) updateInProgress() bool { - return !isFinalState(c.installedInOCM.State()) +func updateInProgress(installedInOCM *clustersmgmtv1.AddOnInstallation) bool { + return !isFinalState(installedInOCM.State()) } -func (c *updateDecision) needsUpdate(current dbapi.AddonInstallation) bool { - if c.installedInOCM.AddonVersion().ID() != c.expectedConfig.Version || - !maps.Equal(convertParametersFromOCMAPI(c.installedInOCM.Parameters()), c.expectedConfig.Parameters) { - return true - } - - addonVersion, err := c.ocmClient.GetAddonVersion(c.expectedConfig.ID, c.expectedConfig.Version) - if err != nil { - c.multiErr = multierror.Append(c.multiErr, fmt.Errorf("get addon version object for addon %s with version %s: %w", - c.expectedConfig.ID, c.expectedConfig.Version, err)) - return false - } +func gitOpsConfigDifferent(expectedConfig gitops.AddonConfig, installedInOCM *clustersmgmtv1.AddOnInstallation) bool { + return installedInOCM.AddonVersion().ID() != expectedConfig.Version || !maps.Equal(convertParametersFromOCMAPI(installedInOCM.Parameters()), expectedConfig.Parameters) +} +func clusterInstallationDifferent(current dbapi.AddonInstallation, addonVersion *addonsmgmtv1.AddonVersion) bool { return current.SourceImage != addonVersion.SourceImage() || current.PackageImage != addonVersion.PackageImage() } @@ -208,3 +254,12 @@ func convertParametersFromOCMAPI(parameters *clustersmgmtv1.AddOnInstallationPar }) return result } + +func inheritFleetshardImageTag(imageTag string) addonCustomization { + return func(addon gitops.AddonConfig) gitops.AddonConfig { + if param := addon.Parameters[fleetshardImageTagParameter]; param == "inherit" { + addon.Parameters[fleetshardImageTagParameter] = imageTag + } + return addon + } +} diff --git a/internal/dinosaur/pkg/services/addon_test.go b/internal/dinosaur/pkg/services/addon_test.go index fa40566773..978fdb6bc0 100644 --- a/internal/dinosaur/pkg/services/addon_test.go +++ b/internal/dinosaur/pkg/services/addon_test.go @@ -653,8 +653,127 @@ func TestAddonProvisioner_Provision_AutoUpgradeDisabled(t *testing.T) { }) } +func TestAddonProvisioner_Provision_InheritFleetshardImageTag_Install(t *testing.T) { + RegisterTestingT(t) + + ocmMock := &ocm.ClientMock{ + GetAddonInstallationFunc: func(clusterID string, addonID string) (*clustersmgmtv1.AddOnInstallation, *errors.ServiceError) { + return nil, errors.NotFound("") + }, + CreateAddonInstallationFunc: func(clusterID string, addon *clustersmgmtv1.AddOnInstallation) error { + return nil + }, + } + addonConfig := ocm.AddonConfig{ + FleetshardSyncImageTag: "0307e03", + InheritFleetshardSyncImageTag: true, + } + p := &AddonProvisioner{ + ocmClient: ocmMock, + customizations: initCustomizations(addonConfig), + } + err := p.Provision(api.Cluster{}, []gitops.AddonConfig{ + { + ID: "acs-fleetshard-dev", + Parameters: map[string]string{ + "fleetshardSyncImageTag": "inherit", + }, + }, + }) + Expect(err).To(Not(HaveOccurred())) + Expect(len(ocmMock.CreateAddonInstallationCalls())).To(Equal(1)) + Expect(ocmMock.CreateAddonInstallationCalls()[0].Addon.Parameters().Len()).To(Equal(1)) + Expect(ocmMock.CreateAddonInstallationCalls()[0].Addon.Parameters().Get(0).ID()).To(Equal("fleetshardSyncImageTag")) + Expect(ocmMock.CreateAddonInstallationCalls()[0].Addon.Parameters().Get(0).Value()).To(Equal("0307e03")) +} + +func TestAddonProvisioner_Provision_InheritFleetshardImageTag_Upgrade(t *testing.T) { + RegisterTestingT(t) + + ocmMock := &ocm.ClientMock{ + GetAddonInstallationFunc: func(clusterID string, addonID string) (*clustersmgmtv1.AddOnInstallation, *errors.ServiceError) { + object, err := clustersmgmtv1.NewAddOnInstallation(). + ID(addonID). + Addon(clustersmgmtv1.NewAddOn().ID(addonID)). + AddonVersion(clustersmgmtv1.NewAddOnVersion().ID("0.2.0")). + State(clustersmgmtv1.AddOnInstallationStateReady). + Build() + Expect(err).To(Not(HaveOccurred())) + return object, nil + }, + GetAddonVersionFunc: func(addonID string, version string) (*addonsmgmtv1.AddonVersion, error) { + return addonsmgmtv1.NewAddonVersion(). + ID("0.2.0"). + SourceImage("quay.io/osd-addons/acs-fleetshard-index@sha256:71eaaccb4d3962043eac953fb3c19a6cc6a88b18c472dd264efc5eb3da4960ac"). + PackageImage("quay.io/osd-addons/acs-fleetshard-package@sha256:3e4fc039662b876c83dd4b48a9608d6867a12ab4932c5b7297bfbe50ba8ee61c"). + Build() + }, + UpdateAddonInstallationFunc: func(clusterID string, addon *clustersmgmtv1.AddOnInstallation) error { + return nil + }, + } + addonConfig := ocm.AddonConfig{ + FleetshardSyncImageTag: "0307e03", + InheritFleetshardSyncImageTag: true, + } + p := &AddonProvisioner{ + ocmClient: ocmMock, + customizations: initCustomizations(addonConfig), + } + err := p.Provision(api.Cluster{ + Addons: addonsJSON([]dbapi.AddonInstallation{ + { + ID: "acs-fleetshard", + Version: "0.2.0", + SourceImage: "quay.io/osd-addons/acs-fleetshard-index@sha256:71eaaccb4d3962043eac953fb3c19a6cc6a88b18c472dd264efc5eb3da4960ac", + PackageImage: "quay.io/osd-addons/acs-fleetshard-package@sha256:3e4fc039662b876c83dd4b48a9608d6867a12ab4932c5b7297bfbe50ba8ee61c", + ParametersSHA256Sum: "3e4fc039662b876c83dd4b48a9608d6867a12ab4932c5b7297bfbe50ba8ee61c", // pragma: allowlist secret + }, + }), + }, + []gitops.AddonConfig{ + { + ID: "acs-fleetshard", + Version: "0.3.0", + Parameters: map[string]string{ + "fleetshardSyncImageTag": "inherit", + }, + }, + }) + Expect(err).To(Not(HaveOccurred())) + Expect(len(ocmMock.UpdateAddonInstallationCalls())).To(Equal(1)) + Expect(ocmMock.UpdateAddonInstallationCalls()[0].Addon.Parameters().Len()).To(Equal(1)) + Expect(ocmMock.UpdateAddonInstallationCalls()[0].Addon.Parameters().Get(0).ID()).To(Equal("fleetshardSyncImageTag")) + Expect(ocmMock.UpdateAddonInstallationCalls()[0].Addon.Parameters().Get(0).Value()).To(Equal("0307e03")) +} + func addonsJSON(addons []dbapi.AddonInstallation) api.JSON { result, err := json.Marshal(addons) Expect(err).To(Not(HaveOccurred())) return result } + +func TestAddonProvisioner_NewAddonProvisioner(t *testing.T) { + RegisterTestingT(t) + + addonConfigPtr := &ocm.AddonConfig{ + URL: "https://addon-service.test", + ClientID: "addon-client-id", + ClientSecret: "addon-client-secret", // pragma: allowlist secret + SelfToken: "addon-token", + } + + baseConfigPtr := &ocm.OCMConfig{ + BaseURL: "https://base.test", + ClientID: "base-client-id", + ClientSecret: "base-client-secret", // pragma: allowlist secret + SelfToken: "base-token", + } + _, err := NewAddonProvisioner(addonConfigPtr, baseConfigPtr) + + Expect(err).To(Not(HaveOccurred())) + Expect(baseConfigPtr.BaseURL).To(Equal("https://base.test")) + Expect(baseConfigPtr.ClientID).To(Equal("base-client-id")) + Expect(baseConfigPtr.ClientSecret).To(Equal("base-client-secret")) + Expect(baseConfigPtr.SelfToken).To(Equal("base-token")) +} diff --git a/pkg/client/ocm/addon_config.go b/pkg/client/ocm/addon_config.go new file mode 100644 index 0000000000..6d63b66b4e --- /dev/null +++ b/pkg/client/ocm/addon_config.go @@ -0,0 +1,60 @@ +package ocm + +import ( + "fmt" + + "github.com/spf13/pflag" + "github.com/stackrox/acs-fleet-manager/pkg/shared" +) + +// AddonConfig addon service configuration +type AddonConfig struct { + URL string + ClientID string + ClientIDFile string + ClientSecret string + ClientSecretFile string + SelfToken string + SelfTokenFile string + InheritFleetshardSyncImageTag bool + FleetshardSyncImageTag string +} + +// NewAddonConfig creates a new instance of AddonConfig +func NewAddonConfig() *AddonConfig { + return &AddonConfig{ + URL: "https://api.openshift.com", + ClientIDFile: "secrets/ocm-addon-service.clientId", + ClientSecretFile: "secrets/ocm-addon-service.clientSecret", // pragma: allowlist secret + SelfTokenFile: "secrets/ocm-addon-service.token", + InheritFleetshardSyncImageTag: true, + } +} + +// AddFlags ... +func (c *AddonConfig) AddFlags(fs *pflag.FlagSet) { + fs.StringVar(&c.ClientIDFile, "ocm-addon-client-id-file", c.ClientIDFile, "File containing OCM API privileged account client-id") + fs.StringVar(&c.ClientSecretFile, "ocm-addon-client-secret-file", c.ClientSecretFile, "File containing OCM API privileged account client-secret") + fs.StringVar(&c.SelfTokenFile, "addon-self-token-file", c.SelfTokenFile, "File containing OCM API privileged offline SSO token") + fs.StringVar(&c.URL, "ocm-addon-url", c.URL, "The base URL of the OCM API, integration by default") + fs.BoolVar(&c.InheritFleetshardSyncImageTag, "inherit-fleetshard-sync-image-tag", c.InheritFleetshardSyncImageTag, "Enable fleetshard-sync image tag") + fs.StringVar(&c.FleetshardSyncImageTag, "fleetshard-sync-image-tag", c.FleetshardSyncImageTag, "Fleetshard-sync image tag") +} + +// ReadFiles ... +func (c *AddonConfig) ReadFiles() error { + err := shared.ReadFileValueString(c.ClientIDFile, &c.ClientID) + if err != nil { + return fmt.Errorf("reading client ID file: %w", err) + } + err = shared.ReadFileValueString(c.ClientSecretFile, &c.ClientSecret) + if err != nil { + return fmt.Errorf("reading client secret file: %w", err) + } + err = shared.ReadFileValueString(c.SelfTokenFile, &c.SelfToken) + if err != nil && (c.ClientSecret == "" || c.ClientID == "") { + return fmt.Errorf("reading self token file: %w", err) + } + + return nil +} diff --git a/pkg/client/ocm/client.go b/pkg/client/ocm/client.go index 2105e71522..43c901d74d 100644 --- a/pkg/client/ocm/client.go +++ b/pkg/client/ocm/client.go @@ -39,6 +39,7 @@ type Client interface { CreateAddonInstallation(clusterID string, addon *clustersmgmtv1.AddOnInstallation) error UpdateAddonInstallation(clusterID string, addon *clustersmgmtv1.AddOnInstallation) error DeleteAddonInstallation(clusterID string, addonID string) error + GetAddon(addonID string) (*addonsmgmtv1.Addon, error) GetAddonVersion(addonID string, version string) (*addonsmgmtv1.AddonVersion, error) GetClusterDNS(clusterID string) (string, error) CreateIdentityProvider(clusterID string, identityProvider *clustersmgmtv1.IdentityProvider) (*clustersmgmtv1.IdentityProvider, error) @@ -312,6 +313,22 @@ func (c *client) GetRegions(provider *clustersmgmtv1.CloudProvider) (*clustersmg return regionList, nil } +func (c *client) GetAddon(addonID string) (*addonsmgmtv1.Addon, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + resp, err := c.connection.AddonsMgmt().V1().Addons().Addon(addonID).Get().Send() + if err != nil { + if resp != nil && resp.Status() == http.StatusNotFound { + return nil, serviceErrors.NotFound("") + } + return nil, serviceErrors.GeneralError("sending GetAddon request: %v", err) + } + + return resp.Body(), nil +} + func (c *client) GetAddonVersion(addonID string, versionID string) (*addonsmgmtv1.AddonVersion, error) { if c.connection == nil { return nil, serviceErrors.InvalidOCMConnection() @@ -322,7 +339,7 @@ func (c *client) GetAddonVersion(addonID string, versionID string) (*addonsmgmtv if resp != nil && resp.Status() == http.StatusNotFound { return nil, serviceErrors.NotFound("") } - return nil, serviceErrors.GeneralError("sending GetAddon request: %v", err) + return nil, serviceErrors.GeneralError("sending GetAddonVersion request: %v", err) } return resp.Body(), nil diff --git a/pkg/client/ocm/client_moq.go b/pkg/client/ocm/client_moq.go index d3351ca867..ea302d7a25 100644 --- a/pkg/client/ocm/client_moq.go +++ b/pkg/client/ocm/client_moq.go @@ -49,6 +49,9 @@ var _ Client = &ClientMock{} // FindSubscriptionsFunc: func(query string) (*amsv1.SubscriptionsListResponse, error) { // panic("mock out the FindSubscriptions method") // }, +// GetAddonFunc: func(addonID string) (*addonsmgmtv1.Addon, error) { +// panic("mock out the GetAddon method") +// }, // GetAddonInstallationFunc: func(clusterID string, addonID string) (*clustersmgmtv1.AddOnInstallation, *serviceErrors.ServiceError) { // panic("mock out the GetAddonInstallation method") // }, @@ -128,6 +131,9 @@ type ClientMock struct { // FindSubscriptionsFunc mocks the FindSubscriptions method. FindSubscriptionsFunc func(query string) (*amsv1.SubscriptionsListResponse, error) + // GetAddonFunc mocks the GetAddon method. + GetAddonFunc func(addonID string) (*addonsmgmtv1.Addon, error) + // GetAddonInstallationFunc mocks the GetAddonInstallation method. GetAddonInstallationFunc func(clusterID string, addonID string) (*clustersmgmtv1.AddOnInstallation, *serviceErrors.ServiceError) @@ -224,6 +230,11 @@ type ClientMock struct { // Query is the query argument value. Query string } + // GetAddon holds details about calls to the GetAddon method. + GetAddon []struct { + // AddonID is the addonID argument value. + AddonID string + } // GetAddonInstallation holds details about calls to the GetAddonInstallation method. GetAddonInstallation []struct { // ClusterID is the clusterID argument value. @@ -319,6 +330,7 @@ type ClientMock struct { lockDeleteCluster sync.RWMutex lockDeleteSubscription sync.RWMutex lockFindSubscriptions sync.RWMutex + lockGetAddon sync.RWMutex lockGetAddonInstallation sync.RWMutex lockGetAddonVersion sync.RWMutex lockGetCloudProviders sync.RWMutex @@ -631,6 +643,38 @@ func (mock *ClientMock) FindSubscriptionsCalls() []struct { return calls } +// GetAddon calls GetAddonFunc. +func (mock *ClientMock) GetAddon(addonID string) (*addonsmgmtv1.Addon, error) { + if mock.GetAddonFunc == nil { + panic("ClientMock.GetAddonFunc: method is nil but Client.GetAddon was just called") + } + callInfo := struct { + AddonID string + }{ + AddonID: addonID, + } + mock.lockGetAddon.Lock() + mock.calls.GetAddon = append(mock.calls.GetAddon, callInfo) + mock.lockGetAddon.Unlock() + return mock.GetAddonFunc(addonID) +} + +// GetAddonCalls gets all the calls that were made to GetAddon. +// Check the length with: +// +// len(mockedClient.GetAddonCalls()) +func (mock *ClientMock) GetAddonCalls() []struct { + AddonID string +} { + var calls []struct { + AddonID string + } + mock.lockGetAddon.RLock() + calls = mock.calls.GetAddon + mock.lockGetAddon.RUnlock() + return calls +} + // GetAddonInstallation calls GetAddonInstallationFunc. func (mock *ClientMock) GetAddonInstallation(clusterID string, addonID string) (*clustersmgmtv1.AddOnInstallation, *serviceErrors.ServiceError) { if mock.GetAddonInstallationFunc == nil { diff --git a/pkg/client/ocm/config.go b/pkg/client/ocm/config.go index 7e93ded571..9086dff40d 100644 --- a/pkg/client/ocm/config.go +++ b/pkg/client/ocm/config.go @@ -9,45 +9,38 @@ import ( // MockModeStubServer ... const ( - MockModeStubServer = "stub-server" - MockModeEmulateServer = "emulate-server" - centralOperatorAddonID = "managed-central" - fleetshardAddonID = "fleetshard-operator" - ClusterLoggingOperatorAddonID = "cluster-logging-operator" + MockModeStubServer = "stub-server" + MockModeEmulateServer = "emulate-server" ) // OCMConfig ... type OCMConfig struct { - BaseURL string `json:"base_url"` - AmsURL string `json:"ams_url"` - ClientID string `json:"client-id"` - ClientIDFile string `json:"client-id_file"` - ClientSecret string `json:"client-secret"` - ClientSecretFile string `json:"client-secret_file"` - SelfToken string `json:"self_token"` - SelfTokenFile string `json:"self_token_file"` - TokenURL string `json:"token_url"` - Debug bool `json:"debug"` - EnableMock bool `json:"enable_mock"` - MockMode string `json:"mock_type"` - CentralOperatorAddonID string `json:"central_operator_addon_id"` - FleetshardAddonID string `json:"fleetshard_addon_id"` + BaseURL string `json:"base_url"` + AmsURL string `json:"ams_url"` + ClientID string `json:"client-id"` + ClientIDFile string `json:"client-id_file"` + ClientSecret string `json:"client-secret"` + ClientSecretFile string `json:"client-secret_file"` + SelfToken string `json:"self_token"` + SelfTokenFile string `json:"self_token_file"` + TokenURL string `json:"token_url"` + Debug bool `json:"debug"` + EnableMock bool `json:"enable_mock"` + MockMode string `json:"mock_type"` } // NewOCMConfig ... func NewOCMConfig() *OCMConfig { return &OCMConfig{ - BaseURL: "https://api-integration.6943.hive-integration.openshiftapps.com", - AmsURL: "https://api.stage.openshift.com", - TokenURL: "https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token", - ClientIDFile: "secrets/ocm-service.clientId", - ClientSecretFile: "secrets/ocm-service.clientSecret", // pragma: allowlist secret - SelfTokenFile: "secrets/ocm-service.token", - Debug: false, - EnableMock: false, - MockMode: MockModeStubServer, - CentralOperatorAddonID: centralOperatorAddonID, - FleetshardAddonID: fleetshardAddonID, + BaseURL: "https://api-integration.6943.hive-integration.openshiftapps.com", + AmsURL: "https://api.stage.openshift.com", + TokenURL: "https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token", + ClientIDFile: "secrets/ocm-service.clientId", + ClientSecretFile: "secrets/ocm-service.clientSecret", // pragma: allowlist secret + SelfTokenFile: "secrets/ocm-service.token", + Debug: false, + EnableMock: false, + MockMode: MockModeStubServer, } } @@ -62,8 +55,6 @@ func (c *OCMConfig) AddFlags(fs *pflag.FlagSet) { fs.BoolVar(&c.Debug, "ocm-debug", c.Debug, "Debug flag for OCM API") fs.BoolVar(&c.EnableMock, "enable-ocm-mock", c.EnableMock, "Enable mock ocm clients") fs.StringVar(&c.MockMode, "ocm-mock-mode", c.MockMode, "Set mock type") - fs.StringVar(&c.CentralOperatorAddonID, "central-operator-addon-id", c.CentralOperatorAddonID, "The name of the Central operator addon") - fs.StringVar(&c.FleetshardAddonID, "fleetshard-addon-id", c.FleetshardAddonID, "The name of the fleetshard operator addon") } // ReadFiles ... diff --git a/pkg/providers/core.go b/pkg/providers/core.go index be6a5ba0fd..ba49e6cdbb 100644 --- a/pkg/providers/core.go +++ b/pkg/providers/core.go @@ -37,6 +37,7 @@ func CoreConfigProviders() di.Option { di.Provide(db.NewDatabaseConfig, di.As(new(environments.ConfigModule))), di.Provide(server.NewServerConfig, di.As(new(environments.ConfigModule))), di.Provide(ocm.NewOCMConfig, di.As(new(environments.ConfigModule))), + di.Provide(ocm.NewAddonConfig, di.As(new(environments.ConfigModule))), di.Provide(iam.NewIAMConfig, di.As(new(environments.ConfigModule))), di.Provide(acl.NewAccessControlListConfig, di.As(new(environments.ConfigModule))), di.Provide(quotamanagement.NewQuotaManagementListConfig, di.As(new(environments.ConfigModule))), diff --git a/templates/secrets-template.yml b/templates/secrets-template.yml index 4e8c742fa3..8ee245b76c 100644 --- a/templates/secrets-template.yml +++ b/templates/secrets-template.yml @@ -41,6 +41,15 @@ parameters: - name: OCM_SERVICE_TOKEN description: Offline token used to interact with other UHC services +- name: OCM_ADDON_SERVICE_CLIENT_ID + description: Client id used to interact with OCM Addon Service + +- name: OCM_ADDON_SERVICE_CLIENT_SECRET + description: Client secret used to interact with OCM Addon Service + +- name: OCM_ADDON_SERVICE_TOKEN + description: Offline token used to interact with OCM Addon Service + - name: SENTRY_KEY description: Private key used in Sentry DSN @@ -109,12 +118,15 @@ objects: - apiVersion: v1 kind: Secret metadata: - name: fleet-manager + name: fleet-manager-credentials stringData: central.idp-client-secret: "${CENTRAL_IDP_CLIENT_SECRET}" ocm-service.clientId: ${OCM_SERVICE_CLIENT_ID} ocm-service.clientSecret: ${OCM_SERVICE_CLIENT_SECRET} ocm-service.token: ${OCM_SERVICE_TOKEN} + ocm-addon-service.clientId: ${OCM_ADDON_SERVICE_CLIENT_ID} + ocm-addon-service.clientSecret: ${OCM_ADDON_SERVICE_CLIENT_SECRET} + ocm-addon-service.token: ${OCM_ADDON_SERVICE_TOKEN} sentry.key: ${SENTRY_KEY} aws.accesskey: ${AWS_ACCESS_KEY} aws.accountid: ${AWS_ACCOUNT_ID} diff --git a/templates/service-template.yml b/templates/service-template.yml index 8e7f9c75c0..a9a1161e6c 100644 --- a/templates/service-template.yml +++ b/templates/service-template.yml @@ -353,16 +353,6 @@ parameters: description: The domain name to use for Central instances value: acs-stage.rhcloud.com -- name: CENTRAL_OPERATOR_OPERATOR_ADDON_ID - displayName: Central operator addon ID - description: ID of the Central operator addon - value: "managed-central-qe" - -- name: FLEETSHARD_ADDON_ID - displayName: fleetshard addon ID - description: ID of the fleetshard addon - value: "fleetshard-operator-qe" - - name: ENABLE_READY_DATA_PLANE_CLUSTERS_RECONCILE description: Enables reconciliation for data plane clusters in the 'Ready' state value: "true" @@ -426,6 +416,14 @@ parameters: description: Enable leader election value: "true" +- name: OCM_ADDON_SERVICE_URL + displayName: The base URL of the OCM API for the addon service + value: "https://api.openshift.com" + +- name: OCM_ADDON_SERVICE_INHERIT_FLEETSHARD_SYNC_IMAGE_TAG + description: Sets the provided fleetshard image tag if the addon parameter value is 'inherit'" + value: "true" + objects: - kind: ConfigMap apiVersion: v1 @@ -1314,6 +1312,13 @@ objects: - --central-idp-issuer=${CENTRAL_IDP_ISSUER} - --ocm-client-id-file=/secrets/fleet-manager-credentials/ocm-service.clientId - --ocm-client-secret-file=/secrets/fleet-manager-credentials/ocm-service.clientSecret + - --ocm-addon-url=${OCM_ADDON_SERVICE_URL} + - --ocm-addon-client-id-file=/secrets/fleet-manager-credentials/ocm-addon-service.clientId + - --ocm-addon-client-secret-file=/secrets/fleet-manager-credentials/ocm-addon-service.clientSecret + - --ocm-addon-client-secret-file=/secrets/fleet-manager-credentials/ocm-addon-service.clientSecret + - --inherit-fleetshard-sync-image-tag=${OCM_ADDON_SERVICE_INHERIT_FLEETSHARD_SYNC_IMAGE_TAG} + - --addon-self-token-file=/secrets/fleet-manager-credentials/ocm-addon-service.token + - --fleetshard-sync-image-tag=${IMAGE_TAG} - --sso-base-url=${SSO_BASE_URL} - --max-limit-for-sso-get-clients=${MAX_LIMIT_FOR_SSO_GET_CLIENTS} - --sso-debug=${SSO_DEBUG} @@ -1360,8 +1365,6 @@ objects: - --fleetshard-operator-index-image=${FLEETSHARD_OLM_INDEX_IMAGE} - --dataplane-cluster-scaling-type=${DATAPLANE_CLUSTER_SCALING_TYPE} - --central-domain-name=${CENTRAL_DOMAIN_NAME} - - --central-operator-addon-id=${CENTRAL_OPERATOR_OPERATOR_ADDON_ID} - - --fleetshard-addon-id=${FLEETSHARD_ADDON_ID} - --alsologtostderr - --central-request-expiration-timeout=${CENTRAL_REQUEST_EXPIRATION_TIMEOUT} - --central-request-internal-user-agents=${CENTRAL_REQUEST_INTERNAL_USER_AGENTS} From 6b013f973d9edcc320deeacfd1fbaf4fb23c4ecd Mon Sep 17 00:00:00 2001 From: Ivan Degtiarenko <78353299+ivan-degtiarenko@users.noreply.github.com> Date: Thu, 25 Jan 2024 19:13:47 +0100 Subject: [PATCH 12/16] Allow for both "sub" claim formats to be used in login (#1616) * Extract alternative user id into central request and add Admin role mapping for it * Address feedback; use deprecated_sub claim * Fix migration script * Fix alternative->alternate * Add comment regarding deprecated_sub --- .../pkg/central/reconciler/reconciler.go | 42 +++++++++++-------- .../pkg/api/dbapi/central_request_types.go | 3 ++ .../dinosaur/pkg/api/private/api/openapi.yaml | 2 + .../model_managed_central_all_of_spec_auth.go | 15 +++---- internal/dinosaur/pkg/handlers/validation.go | 1 + .../20240122180000_add_alternate_user_id.go | 34 +++++++++++++++ .../dinosaur/pkg/migrations/migrations.go | 1 + .../dinosaur/pkg/presenters/managedcentral.go | 15 +++---- openapi/fleet-manager-private.yaml | 2 + pkg/auth/acs_claims.go | 8 ++++ pkg/auth/context_config.go | 3 ++ 11 files changed, 95 insertions(+), 31 deletions(-) create mode 100644 internal/dinosaur/pkg/migrations/20240122180000_add_alternate_user_id.go diff --git a/fleetshard/pkg/central/reconciler/reconciler.go b/fleetshard/pkg/central/reconciler/reconciler.go index 4b0872faed..dc36172b83 100644 --- a/fleetshard/pkg/central/reconciler/reconciler.go +++ b/fleetshard/pkg/central/reconciler/reconciler.go @@ -529,27 +529,35 @@ func (r *CentralReconciler) configureAuditLogNotifier(secret *corev1.Secret, nam } func getAuthProviderConfig(remoteCentral private.ManagedCentral) *declarativeconfig.AuthProvider { + groups := []declarativeconfig.Group{ + { + AttributeKey: "userid", + AttributeValue: remoteCentral.Spec.Auth.OwnerUserId, + RoleName: "Admin", + }, + { + AttributeKey: "groups", + AttributeValue: "admin:org:all", + RoleName: "Admin", + }, + { + AttributeKey: "rh_is_org_admin", + AttributeValue: "true", + RoleName: "Admin", + }, + } + if remoteCentral.Spec.Auth.OwnerAlternateUserId != "" { + groups = append(groups, declarativeconfig.Group{ + AttributeKey: "userid", + AttributeValue: remoteCentral.Spec.Auth.OwnerAlternateUserId, + RoleName: "Admin", + }) + } return &declarativeconfig.AuthProvider{ Name: authProviderName(remoteCentral), UIEndpoint: remoteCentral.Spec.UiEndpoint.Host, ExtraUIEndpoints: []string{"localhost:8443"}, - Groups: []declarativeconfig.Group{ - { - AttributeKey: "userid", - AttributeValue: remoteCentral.Spec.Auth.OwnerUserId, - RoleName: "Admin", - }, - { - AttributeKey: "groups", - AttributeValue: "admin:org:all", - RoleName: "Admin", - }, - { - AttributeKey: "rh_is_org_admin", - AttributeValue: "true", - RoleName: "Admin", - }, - }, + Groups: groups, RequiredAttributes: []declarativeconfig.RequiredAttribute{ { AttributeKey: "rh_org_id", diff --git a/internal/dinosaur/pkg/api/dbapi/central_request_types.go b/internal/dinosaur/pkg/api/dbapi/central_request_types.go index 8551a41ecf..46fcf2b05c 100644 --- a/internal/dinosaur/pkg/api/dbapi/central_request_types.go +++ b/internal/dinosaur/pkg/api/dbapi/central_request_types.go @@ -44,6 +44,9 @@ type CentralRequest struct { OwnerAccountID string `json:"owner_account_id"` // OwnerUserID is the subject claim (confusingly it is NOT the user_id claim) of the Red Hat SSO token. OwnerUserID string `json:"owner_user_id"` + // OwnerAlternateUserID is the user_id claim of the Red Hat SSO token. + // It is introduced to allow for a seamless migration of the subject claim format. + OwnerAlternateUserID string `json:"owner_alternate_user_id"` // Instance-independent part of the Central's hostname. For example, this // can be `rhacs-dev.com`, `acs-stage.rhcloud.com`, etc. diff --git a/internal/dinosaur/pkg/api/private/api/openapi.yaml b/internal/dinosaur/pkg/api/private/api/openapi.yaml index ba988d80ad..6f653f67e1 100644 --- a/internal/dinosaur/pkg/api/private/api/openapi.yaml +++ b/internal/dinosaur/pkg/api/private/api/openapi.yaml @@ -454,6 +454,8 @@ components: type: string ownerUserId: type: string + ownerAlternateUserId: + type: string ownerOrgId: type: string ownerOrgName: diff --git a/internal/dinosaur/pkg/api/private/model_managed_central_all_of_spec_auth.go b/internal/dinosaur/pkg/api/private/model_managed_central_all_of_spec_auth.go index 4604762138..bffd114ad4 100644 --- a/internal/dinosaur/pkg/api/private/model_managed_central_all_of_spec_auth.go +++ b/internal/dinosaur/pkg/api/private/model_managed_central_all_of_spec_auth.go @@ -12,11 +12,12 @@ package private // ManagedCentralAllOfSpecAuth struct for ManagedCentralAllOfSpecAuth type ManagedCentralAllOfSpecAuth struct { - ClientSecret string `json:"clientSecret,omitempty"` - ClientId string `json:"clientId,omitempty"` - ClientOrigin string `json:"clientOrigin,omitempty"` - OwnerUserId string `json:"ownerUserId,omitempty"` - OwnerOrgId string `json:"ownerOrgId,omitempty"` - OwnerOrgName string `json:"ownerOrgName,omitempty"` - Issuer string `json:"issuer,omitempty"` + ClientSecret string `json:"clientSecret,omitempty"` + ClientId string `json:"clientId,omitempty"` + ClientOrigin string `json:"clientOrigin,omitempty"` + OwnerUserId string `json:"ownerUserId,omitempty"` + OwnerAlternateUserId string `json:"ownerAlternateUserId,omitempty"` + OwnerOrgId string `json:"ownerOrgId,omitempty"` + OwnerOrgName string `json:"ownerOrgName,omitempty"` + Issuer string `json:"issuer,omitempty"` } diff --git a/internal/dinosaur/pkg/handlers/validation.go b/internal/dinosaur/pkg/handlers/validation.go index 595cc31395..92c1f5281a 100644 --- a/internal/dinosaur/pkg/handlers/validation.go +++ b/internal/dinosaur/pkg/handlers/validation.go @@ -110,6 +110,7 @@ func ValidateDinosaurClaims(ctx context.Context, dinosaurRequestPayload *public. dinosaurRequest.OrganisationID, _ = claims.GetOrgID() dinosaurRequest.OwnerAccountID, _ = claims.GetAccountID() dinosaurRequest.OwnerUserID, _ = claims.GetSubject() + dinosaurRequest.OwnerAlternateUserID, _ = claims.GetAlternateUserID() return nil } diff --git a/internal/dinosaur/pkg/migrations/20240122180000_add_alternate_user_id.go b/internal/dinosaur/pkg/migrations/20240122180000_add_alternate_user_id.go new file mode 100644 index 0000000000..999cce708d --- /dev/null +++ b/internal/dinosaur/pkg/migrations/20240122180000_add_alternate_user_id.go @@ -0,0 +1,34 @@ +package migrations + +// Migrations should NEVER use types from other packages. Types can change +// and then migrations run on a _new_ database will fail or behave unexpectedly. +// Instead of importing types, always re-create the type in the migration, as +// is done here, even though the same type is defined in pkg/api + +import ( + "github.com/go-gormigrate/gormigrate/v2" + "github.com/pkg/errors" + "github.com/stackrox/acs-fleet-manager/pkg/db" + "gorm.io/gorm" +) + +func addAlternateUserIDFieldToCentralRequests() *gormigrate.Migration { + type CentralRequest struct { + db.Model + OwnerAlternateUserID string `json:"owner_alternate_user_id"` + } + migrationID := "20240122180000" + + return &gormigrate.Migration{ + ID: migrationID, + Migrate: func(tx *gorm.DB) error { + return addColumnIfNotExists(tx, &CentralRequest{}, "owner_alternate_user_id") + }, + Rollback: func(tx *gorm.DB) error { + return errors.Wrap( + tx.Migrator().DropColumn(&CentralRequest{}, "owner_alternate_user_id"), + "failed to drop owner_alternate_user_id column", + ) + }, + } +} diff --git a/internal/dinosaur/pkg/migrations/migrations.go b/internal/dinosaur/pkg/migrations/migrations.go index 1da85cff79..a288bf1bb8 100644 --- a/internal/dinosaur/pkg/migrations/migrations.go +++ b/internal/dinosaur/pkg/migrations/migrations.go @@ -52,6 +52,7 @@ func getMigrations() []*gormigrate.Migration { addExpiredAtFieldToCentralRequests(), addExpirationLeaseType(), addClusterAddons(), + addAlternateUserIDFieldToCentralRequests(), } } diff --git a/internal/dinosaur/pkg/presenters/managedcentral.go b/internal/dinosaur/pkg/presenters/managedcentral.go index e4a39166a0..df321ab66b 100644 --- a/internal/dinosaur/pkg/presenters/managedcentral.go +++ b/internal/dinosaur/pkg/presenters/managedcentral.go @@ -125,13 +125,14 @@ func (c *ManagedCentralPresenter) presentManagedCentral(gitopsConfig gitops.Conf from.Owner, }, Auth: private.ManagedCentralAllOfSpecAuth{ - ClientId: from.AuthConfig.ClientID, - ClientSecret: from.AuthConfig.ClientSecret, // pragma: allowlist secret - ClientOrigin: from.AuthConfig.ClientOrigin, - OwnerOrgId: from.OrganisationID, - OwnerOrgName: from.OrganisationName, - OwnerUserId: from.OwnerUserID, - Issuer: from.AuthConfig.Issuer, + ClientId: from.AuthConfig.ClientID, + ClientSecret: from.AuthConfig.ClientSecret, // pragma: allowlist secret + ClientOrigin: from.AuthConfig.ClientOrigin, + OwnerOrgId: from.OrganisationID, + OwnerOrgName: from.OrganisationName, + OwnerUserId: from.OwnerUserID, + OwnerAlternateUserId: from.OwnerAlternateUserID, + Issuer: from.AuthConfig.Issuer, }, UiEndpoint: private.ManagedCentralAllOfSpecUiEndpoint{ Host: from.GetUIHost(), diff --git a/openapi/fleet-manager-private.yaml b/openapi/fleet-manager-private.yaml index 7251a289a5..a53d7c1daa 100644 --- a/openapi/fleet-manager-private.yaml +++ b/openapi/fleet-manager-private.yaml @@ -292,6 +292,8 @@ components: type: string ownerUserId: type: string + ownerAlternateUserId: + type: string ownerOrgId: type: string ownerOrgName: diff --git a/pkg/auth/acs_claims.go b/pkg/auth/acs_claims.go index 9807093040..f9e38e385d 100644 --- a/pkg/auth/acs_claims.go +++ b/pkg/auth/acs_claims.go @@ -49,6 +49,14 @@ func (c *ACSClaims) GetUserID() (string, error) { tenantUserIDClaim, alternateTenantUserIDClaim) } +// GetAlternateUserID ... +func (c *ACSClaims) GetAlternateUserID() (string, error) { + if alternateSub, ok := (*c)[alternateSubClaim].(string); ok { + return alternateSub, nil + } + return "", fmt.Errorf("can't find %q attribute in claims", alternateSubClaim) +} + // GetOrgID ... func (c *ACSClaims) GetOrgID() (string, error) { if idx, val := arrays.FindFirst(func(x interface{}) bool { return x != nil }, diff --git a/pkg/auth/context_config.go b/pkg/auth/context_config.go index 947872889a..7d48e7e64e 100644 --- a/pkg/auth/context_config.go +++ b/pkg/auth/context_config.go @@ -21,6 +21,9 @@ var ( // The claims relate to the Red Hat organisation and user that created the service account. alternateTenantIDClaim = "rh-org-id" alternateTenantUserIDClaim = "rh-user-id" + + // Sub claim in old format. + alternateSubClaim = "deprecated_sub" ) // ContextConfig ... From 61218cbab7c97484b62814f8d88ce6809e7f4713 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 03:38:17 +0100 Subject: [PATCH 13/16] Bump github.com/prometheus/common from 0.45.0 to 0.46.0 (#1615) Bumps [github.com/prometheus/common](https://github.com/prometheus/common) from 0.45.0 to 0.46.0. - [Release notes](https://github.com/prometheus/common/releases) - [Commits](https://github.com/prometheus/common/compare/v0.45.0...v0.46.0) --- updated-dependencies: - dependency-name: github.com/prometheus/common dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 15 +++++++-------- go.sum | 30 ++++++++++++++---------------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 5f8339cdb2..8448a5f1d3 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 github.com/prometheus/client_golang v1.18.0 github.com/prometheus/client_model v0.5.0 - github.com/prometheus/common v0.45.0 + github.com/prometheus/common v0.46.0 github.com/redhat-developer/app-services-sdk-core/app-services-sdk-go/serviceaccountmgmt v0.0.0-20230323122535-49460b57cc45 github.com/rs/xid v1.5.0 github.com/santhosh-tekuri/jsonschema/v3 v3.1.0 @@ -55,10 +55,10 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 github.com/zgalor/weberr v0.8.2 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - golang.org/x/net v0.19.0 - golang.org/x/oauth2 v0.15.0 + golang.org/x/net v0.20.0 + golang.org/x/oauth2 v0.16.0 golang.org/x/sync v0.6.0 - golang.org/x/sys v0.15.0 + golang.org/x/sys v0.16.0 gopkg.in/resty.v1 v1.12.0 gopkg.in/yaml.v2 v2.4.0 gorm.io/driver/postgres v1.5.4 @@ -131,7 +131,6 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/microcosm-cc/bluemonday v1.0.23 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect @@ -163,8 +162,8 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/term v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.14.0 // indirect @@ -174,7 +173,7 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect google.golang.org/grpc v1.58.3 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 6f5582a9df..821632485a 100644 --- a/go.sum +++ b/go.sum @@ -430,8 +430,6 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103 h1:Z/i1e+gTZrmcGeZyWckaLfucYG6KYOXLWo4co8pZYNY= github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103/go.mod h1:o9YPB5aGP8ob35Vy6+vyq3P3bWe7NQWzf+JLiXCiMaE= github.com/microcosm-cc/bluemonday v1.0.23 h1:SMZe2IGa0NuHvnVNAZ+6B38gsTbi5e4sViiWJyDDqFY= @@ -491,8 +489,8 @@ github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlk github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y= +github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/redhat-developer/app-services-sdk-core/app-services-sdk-go v0.1.0/go.mod h1:JPNDOitDoHoHk5ZPRjfOxHQhE4Br0WtiyV8m43E0rso= @@ -635,8 +633,8 @@ golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -730,8 +728,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -753,8 +751,8 @@ golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220630143837-2104d58473e0/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= -golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -845,8 +843,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -854,8 +852,8 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1130,8 +1128,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From 93d1bfa708cc52523b33bc508bf251260fcf84f5 Mon Sep 17 00:00:00 2001 From: Johannes Malsam <60240743+johannes94@users.noreply.github.com> Date: Fri, 26 Jan 2024 06:15:55 +0100 Subject: [PATCH 14/16] fix: update for secret backup for tenants (#1617) --- fleetshard/pkg/central/reconciler/reconciler.go | 2 +- internal/dinosaur/pkg/services/data_plane_dinosaur.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fleetshard/pkg/central/reconciler/reconciler.go b/fleetshard/pkg/central/reconciler/reconciler.go index dc36172b83..c1dd7dd4ac 100644 --- a/fleetshard/pkg/central/reconciler/reconciler.go +++ b/fleetshard/pkg/central/reconciler/reconciler.go @@ -794,7 +794,7 @@ func (r *CentralReconciler) collectReconciliationStatus(ctx context.Context, rem } // Only report secrets if Central is ready, to ensure we're not trying to get secrets before they are created. - // Only report secrets once. Ensures we don't overwrite initial secrets with corrupted secrets + // Only report secrets if not all secrets are already stored to ensure we don't overwrite initial secrets with corrupted secrets // from the cluster state. if isRemoteCentralReady(remoteCentral) && !r.areSecretsStored(remoteCentral.Metadata.SecretsStored) { secrets, err := r.collectSecretsEncrypted(ctx, remoteCentral) diff --git a/internal/dinosaur/pkg/services/data_plane_dinosaur.go b/internal/dinosaur/pkg/services/data_plane_dinosaur.go index a7c0357a88..aed10b8dc1 100644 --- a/internal/dinosaur/pkg/services/data_plane_dinosaur.go +++ b/internal/dinosaur/pkg/services/data_plane_dinosaur.go @@ -276,8 +276,8 @@ func (s *dataPlaneCentralService) addRoutesToRequest(centralRequest *dbapi.Centr } func (d *dataPlaneCentralService) addSecretsToRequest(centralRequest *dbapi.CentralRequest, centralStatus *dbapi.DataPlaneCentralStatus, cluster *api.Cluster) *serviceError.ServiceError { - if centralRequest.Secrets != nil { // pragma: allowlist secret - logger.Logger.V(10).Infof("skip persisting secrets for Central %s as they are already stored", centralRequest.ID) + if centralStatus.Secrets == nil || len(centralStatus.Secrets) == 0 { // pragma: allowlist secret + logger.Logger.V(10).Infof("skip persisting secrets for Central %s, report is empty or nil", centralRequest.ID) return nil } logger.Logger.Infof("store secret information for central %s", centralRequest.ID) From 6bc66b74cbdee83ea0bb3aec31bcb3e06ef2ec06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl?= Date: Mon, 29 Jan 2024 12:22:12 +0100 Subject: [PATCH 15/16] refactor: allow for clean-up and regenerating go files (#1620) --- .github/workflows/ci.yaml | 9 + .secrets.baseline | 18 +- Makefile | 5 + e2e/e2e_auth_test.go | 15 +- e2e/e2e_canary_upgrade_test.go | 7 +- e2e/e2e_test.go | 11 +- .../pkg/central/reconciler/reconciler_test.go | 9 +- fleetshard/pkg/runtime/runtime.go | 5 +- .../dinosaur/pkg/clusters/cluster_builder.go | 3 +- .../dinosaur/pkg/clusters/cluster_test.go | 8 +- .../dinosaur/pkg/clusters/ocm_provider.go | 9 +- .../pkg/clusters/ocm_provider_test.go | 48 +- internal/dinosaur/pkg/clusters/provider.go | 2 +- internal/dinosaur/pkg/clusters/types/types.go | 2 +- .../pkg/cmd/fleetmanagerclient/client.go | 13 +- .../dinosaur/pkg/dinosaurs/types/types.go | 4 +- .../dinosaur/pkg/environments/integration.go | 2 +- .../dinosaur/pkg/handlers/cloud_accounts.go | 2 +- .../pkg/handlers/cloud_accounts_test.go | 2 +- internal/dinosaur/pkg/routes/route_loader.go | 2 +- internal/dinosaur/pkg/services/addon.go | 9 +- internal/dinosaur/pkg/services/addon_test.go | 11 +- .../dinosaur/pkg/services/data_migration.go | 2 +- .../pkg/services/data_migration_test.go | 4 +- internal/dinosaur/pkg/services/dinosaur.go | 2 +- .../pkg/services/quota/ams_quota_service.go | 2 +- .../services/quota/ams_quota_service_test.go | 126 ++-- .../quota/default_quota_service_factory.go | 2 +- internal/dinosaur/pkg/workers/clusters_mgr.go | 2 +- internal/dinosaur/test/common/util.go | 2 +- internal/dinosaur/test/helper.go | 2 +- ...equire_terms_acceptance_middleware_test.go | 2 +- .../require_terms_acceptance_middleware.go | 2 +- ...equire_terms_acceptance_middleware_test.go | 7 +- pkg/client/fleetmanager/client.go | 123 +--- pkg/client/fleetmanager/{ => impl}/auth.go | 2 +- .../fleetmanager/{ => impl}/auth_ocm.go | 2 +- .../fleetmanager/{ => impl}/auth_rhsso.go | 2 +- .../{ => impl}/auth_static_token.go | 2 +- .../fleetmanager/{ => impl}/auth_test.go | 2 +- .../{ => impl}/auth_token_common.go | 2 +- pkg/client/fleetmanager/impl/client.go | 127 ++++ .../fleetmanager/{ => mocks}/client_mock.go | 14 +- .../{api_moq.go => mocks/client_moq.go} | 33 +- pkg/client/observatorium/api_mock.go | 8 +- .../observatorium/{ => mocks}/api_moq.go | 144 ++--- pkg/client/observatorium/mocks/gen.go | 8 + pkg/client/ocm/client.go | 552 +---------------- pkg/client/ocm/id.go | 36 +- pkg/client/ocm/{ => impl}/addon_config.go | 2 +- pkg/client/ocm/impl/client_impl.go | 559 ++++++++++++++++++ pkg/client/ocm/{ => impl}/client_test.go | 2 +- pkg/client/ocm/{ => impl}/config.go | 2 +- pkg/client/ocm/impl/id.go | 35 ++ pkg/client/ocm/{ => impl}/id_test.go | 2 +- pkg/client/ocm/{ => impl}/paginated_moq.go | 2 +- .../ocm/{ => impl}/paginated_moq_test.go | 2 +- .../ocm/{ => impl}/paginated_request.go | 2 +- pkg/client/ocm/{ => impl}/types.go | 2 +- pkg/client/ocm/{ => mocks}/client_moq.go | 13 +- .../id_generator_moq.go} | 13 +- pkg/providers/core.go | 2 +- pkg/services/account/provider.go | 2 +- pkg/services/authorization/provider.go | 2 +- probe/pkg/fleetmanager/client.go | 5 +- probe/pkg/probe/probe_test.go | 2 +- test/helper.go | 2 +- 67 files changed, 1063 insertions(+), 996 deletions(-) rename pkg/client/fleetmanager/{ => impl}/auth.go (99%) rename pkg/client/fleetmanager/{ => impl}/auth_ocm.go (98%) rename pkg/client/fleetmanager/{ => impl}/auth_rhsso.go (98%) rename pkg/client/fleetmanager/{ => impl}/auth_static_token.go (98%) rename pkg/client/fleetmanager/{ => impl}/auth_test.go (95%) rename pkg/client/fleetmanager/{ => impl}/auth_token_common.go (92%) create mode 100644 pkg/client/fleetmanager/impl/client.go rename pkg/client/fleetmanager/{ => mocks}/client_mock.go (63%) rename pkg/client/fleetmanager/{api_moq.go => mocks/client_moq.go} (96%) rename pkg/client/observatorium/{ => mocks}/api_moq.go (85%) create mode 100644 pkg/client/observatorium/mocks/gen.go rename pkg/client/ocm/{ => impl}/addon_config.go (99%) create mode 100644 pkg/client/ocm/impl/client_impl.go rename pkg/client/ocm/{ => impl}/client_test.go (98%) rename pkg/client/ocm/{ => impl}/config.go (99%) create mode 100644 pkg/client/ocm/impl/id.go rename pkg/client/ocm/{ => impl}/id_test.go (98%) rename pkg/client/ocm/{ => impl}/paginated_moq.go (99%) rename pkg/client/ocm/{ => impl}/paginated_moq_test.go (99%) rename pkg/client/ocm/{ => impl}/paginated_request.go (99%) rename pkg/client/ocm/{ => impl}/types.go (98%) rename pkg/client/ocm/{ => mocks}/client_moq.go (99%) rename pkg/client/ocm/{idgenerator_moq.go => mocks/id_generator_moq.go} (79%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2f9429181b..178bae23a0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -104,6 +104,15 @@ jobs: key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }} restore-keys: | ${{ runner.os }}-go- + - name: Go mod tidy + run: | + go mod tidy + - name: Clean and generate files + run: | + make clean/go-generated && make generate + - name: Test that there were no changes + run: | + git diff --exit-code - name: Setup tests secrets run: | make ocm/setup aws/setup redhatsso/setup centralcert/setup observatorium/setup secrets/touch diff --git a/.secrets.baseline b/.secrets.baseline index 962020ff27..cce6547bce 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -333,7 +333,7 @@ "filename": "e2e/e2e_test.go", "hashed_secret": "7f38822bc2b03e97325ff310099f457f6f788daf", "is_verified": false, - "line_number": 267 + "line_number": 268 } ], "fleetshard/pkg/central/cloudprovider/dbclient_moq.go": [ @@ -370,27 +370,27 @@ "line_number": 983 } ], - "pkg/client/fleetmanager/api_moq.go": [ + "pkg/client/fleetmanager/mocks/client_moq.go": [ { "type": "Secret Keyword", - "filename": "pkg/client/fleetmanager/api_moq.go", + "filename": "pkg/client/fleetmanager/mocks/client_moq.go", "hashed_secret": "44e17306b837162269a410204daaa5ecee4ec22c", "is_verified": false, - "line_number": 583 + "line_number": 584 }, { "type": "Secret Keyword", - "filename": "pkg/client/fleetmanager/api_moq.go", + "filename": "pkg/client/fleetmanager/mocks/client_moq.go", "hashed_secret": "0ff50155b4f57adeccae93f27dc23efe2a8b7824", "is_verified": false, - "line_number": 584 + "line_number": 585 }, { "type": "Secret Keyword", - "filename": "pkg/client/fleetmanager/api_moq.go", + "filename": "pkg/client/fleetmanager/mocks/client_moq.go", "hashed_secret": "5ce1b8d4fb9dae5c02b2017e39e7267a21cea37f", "is_verified": false, - "line_number": 593 + "line_number": 594 } ], "pkg/client/iam/client_moq.go": [ @@ -586,5 +586,5 @@ } ] }, - "generated_at": "2024-01-18T15:43:17Z" + "generated_at": "2024-01-25T17:36:32Z" } diff --git a/Makefile b/Makefile index 4541517580..f94c305974 100644 --- a/Makefile +++ b/Makefile @@ -876,3 +876,8 @@ tag: full-image-tag: @echo "$(IMAGE_NAME):$(image_tag)" .PHONY: full-image-tag + +clean/go-generated: + @echo "Cleaning generated .go files..." + @find . -name '*.go' | xargs grep -l '// Code generated by .*; DO NOT EDIT.$$' | while read -r file; do echo ""$$file""; rm -f "$$file"; done +.PHONY: clean/go-generated diff --git a/e2e/e2e_auth_test.go b/e2e/e2e_auth_test.go index 0786e67696..82a917ec92 100644 --- a/e2e/e2e_auth_test.go +++ b/e2e/e2e_auth_test.go @@ -10,6 +10,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager" + fmImpl "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager/impl" ) const ( @@ -40,7 +41,7 @@ var _ = Describe("AuthN/Z Fleet* components", Ordered, func() { skipOnProd := env == "production" skipOnNonProd := env != "production" - authOption := fleetmanager.OptionFromEnv() + authOption := fmImpl.OptionFromEnv() var client *fleetmanager.Client @@ -72,9 +73,9 @@ var _ = Describe("AuthN/Z Fleet* components", Ordered, func() { Describe("OCM auth type", func() { BeforeEach(func() { - auth, err := fleetmanager.NewOCMAuth(context.Background(), authOption.Ocm) + auth, err := fmImpl.NewOCMAuth(context.Background(), authOption.Ocm) Expect(err).ToNot(HaveOccurred()) - fmClient, err := fleetmanager.NewClient(fleetManagerEndpoint, auth) + fmClient, err := fmImpl.NewClient(fleetManagerEndpoint, auth) Expect(err).ToNot(HaveOccurred()) client = fmClient }) @@ -94,9 +95,9 @@ var _ = Describe("AuthN/Z Fleet* components", Ordered, func() { Describe("Static token auth type", func() { BeforeEach(func() { - auth, err := fleetmanager.NewStaticAuth(context.Background(), authOption.Static) + auth, err := fmImpl.NewStaticAuth(context.Background(), authOption.Static) Expect(err).ToNot(HaveOccurred()) - fmClient, err := fleetmanager.NewClient(fleetManagerEndpoint, auth) + fmClient, err := fmImpl.NewClient(fleetManagerEndpoint, auth) Expect(err).ToNot(HaveOccurred()) client = fmClient }) @@ -122,9 +123,9 @@ var _ = Describe("AuthN/Z Fleet* components", Ordered, func() { Skip("RHSSO_SERVICE_ACCOUNT_CLIENT_ID / RHSSO_SERVICE_ACCOUNT_CLIENT_SECRET not set, cannot initialize auth type") } // Create the auth type for RH SSO. - auth, err := fleetmanager.NewRHSSOAuth(context.Background(), rhSSOOpt) + auth, err := fmImpl.NewRHSSOAuth(context.Background(), rhSSOOpt) Expect(err).ToNot(HaveOccurred()) - fmClient, err := fleetmanager.NewClient(fleetManagerEndpoint, auth) + fmClient, err := fmImpl.NewClient(fleetManagerEndpoint, auth) Expect(err).ToNot(HaveOccurred()) client = fmClient }) diff --git a/e2e/e2e_canary_upgrade_test.go b/e2e/e2e_canary_upgrade_test.go index 33bfb2892c..fb6786a78f 100644 --- a/e2e/e2e_canary_upgrade_test.go +++ b/e2e/e2e_canary_upgrade_test.go @@ -14,6 +14,7 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/gitops" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/services" "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager" + fmImpl "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager/impl" "github.com/stackrox/acs-fleet-manager/pkg/features" "github.com/stackrox/rox/operator/apis/platform/v1alpha1" appsv1 "k8s.io/api/apps/v1" @@ -60,10 +61,10 @@ var _ = Describe("Fleetshard-sync Targeted Upgrade", Ordered, func() { BeforeEach(func() { SkipIf(!features.TargetedOperatorUpgrades.Enabled() || !runCanaryUpgradeTests, "Skipping canary upgrade test") - option := fleetmanager.OptionFromEnv() - auth, err := fleetmanager.NewStaticAuth(ctx, fleetmanager.StaticOption{StaticToken: option.Static.StaticToken}) + option := fmImpl.OptionFromEnv() + auth, err := fmImpl.NewStaticAuth(ctx, fmImpl.StaticOption{StaticToken: option.Static.StaticToken}) Expect(err).ToNot(HaveOccurred()) - client, err = fleetmanager.NewClient(fleetManagerEndpoint, auth) + client, err = fmImpl.NewClient(fleetManagerEndpoint, auth) Expect(err).ToNot(HaveOccurred()) }) diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 5fb39ec3e7..92cc27a6fe 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -21,6 +21,7 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/public" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/services" "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager" + fmImpl "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager/impl" "github.com/stackrox/rox/operator/apis/platform/v1alpha1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -62,16 +63,16 @@ var _ = Describe("Central", Ordered, func() { BeforeEach(func() { SkipIf(!runCentralTests, "Skipping Central tests") - option := fleetmanager.OptionFromEnv() - auth, err := fleetmanager.NewStaticAuth(context.Background(), fleetmanager.StaticOption{StaticToken: option.Static.StaticToken}) + option := fmImpl.OptionFromEnv() + auth, err := fmImpl.NewStaticAuth(context.Background(), fmImpl.StaticOption{StaticToken: option.Static.StaticToken}) Expect(err).ToNot(HaveOccurred()) - client, err = fleetmanager.NewClient(fleetManagerEndpoint, auth) + client, err = fmImpl.NewClient(fleetManagerEndpoint, auth) Expect(err).ToNot(HaveOccurred()) adminStaticToken := os.Getenv("STATIC_TOKEN_ADMIN") - adminAuth, err := fleetmanager.NewStaticAuth(context.Background(), fleetmanager.StaticOption{StaticToken: adminStaticToken}) + adminAuth, err := fmImpl.NewStaticAuth(context.Background(), fmImpl.StaticOption{StaticToken: adminStaticToken}) Expect(err).ToNot(HaveOccurred()) - adminClient, err := fleetmanager.NewClient(fleetManagerEndpoint, adminAuth) + adminClient, err := fmImpl.NewClient(fleetManagerEndpoint, adminAuth) Expect(err).ToNot(HaveOccurred()) adminAPI = adminClient.AdminAPI() diff --git a/fleetshard/pkg/central/reconciler/reconciler_test.go b/fleetshard/pkg/central/reconciler/reconciler_test.go index ce56d59a80..2100f5b03f 100644 --- a/fleetshard/pkg/central/reconciler/reconciler_test.go +++ b/fleetshard/pkg/central/reconciler/reconciler_test.go @@ -27,6 +27,7 @@ import ( centralConstants "github.com/stackrox/acs-fleet-manager/internal/dinosaur/constants" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/private" "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager" + fmMocks "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager/mocks" "github.com/stackrox/rox/operator/apis/platform/v1alpha1" "github.com/stackrox/rox/pkg/declarativeconfig" "github.com/stackrox/rox/pkg/utils" @@ -144,7 +145,7 @@ func getClientTrackerAndReconciler( fakeClient, tracker := testutils.NewFakeClientWithTracker(t, k8sObjects...) reconciler := NewCentralReconciler( fakeClient, - fleetmanager.NewClientMock().Client(), + fmMocks.NewClientMock().Client(), centralConfig, managedDBClient, centralDBInitFunc, @@ -1872,7 +1873,7 @@ func TestRestoreCentralSecrets(t *testing.T) { centralTLSSecretObject(), }, buildFMClient: func() *fleetmanager.Client { - mockClient := fleetmanager.NewClientMock() + mockClient := fmMocks.NewClientMock() mockClient.PrivateAPIMock.GetCentralFunc = func(ctx context.Context, centralID string) (private.ManagedCentral, *http.Response, error) { return private.ManagedCentral{}, nil, errors.New("test error") } @@ -1892,7 +1893,7 @@ func TestRestoreCentralSecrets(t *testing.T) { centralTLSSecretObject(), }, buildFMClient: func() *fleetmanager.Client { - mockClient := fleetmanager.NewClientMock() + mockClient := fmMocks.NewClientMock() mockClient.PrivateAPIMock.GetCentralFunc = func(ctx context.Context, centralID string) (private.ManagedCentral, *http.Response, error) { returnCentral := simpleManagedCentral returnCentral.Metadata.Secrets = map[string]string{"central-db-password": "testpw"} @@ -1910,7 +1911,7 @@ func TestRestoreCentralSecrets(t *testing.T) { return newCentral }, buildFMClient: func() *fleetmanager.Client { - mockClient := fleetmanager.NewClientMock() + mockClient := fmMocks.NewClientMock() mockClient.PrivateAPIMock.GetCentralFunc = func(ctx context.Context, centralID string) (private.ManagedCentral, *http.Response, error) { returnCentral := simpleManagedCentral centralTLS := `{"metadata":{"name":"central-tls","namespace":"rhacs-cb45idheg5ip6dq1jo4g","creationTimestamp":null}}` diff --git a/fleetshard/pkg/runtime/runtime.go b/fleetshard/pkg/runtime/runtime.go index 77047d42ef..4aa0454c72 100644 --- a/fleetshard/pkg/runtime/runtime.go +++ b/fleetshard/pkg/runtime/runtime.go @@ -20,7 +20,8 @@ import ( "github.com/stackrox/acs-fleet-manager/fleetshard/pkg/k8s" "github.com/stackrox/acs-fleet-manager/fleetshard/pkg/util" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/private" - "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager" + fmAPI "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager" + fleetmanager "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager/impl" "github.com/stackrox/acs-fleet-manager/pkg/features" "github.com/stackrox/acs-fleet-manager/pkg/logger" "github.com/stackrox/rox/operator/apis/platform/v1alpha1" @@ -50,7 +51,7 @@ var backoff = wait.Backoff{ // Runtime represents the runtime to reconcile all centrals associated with the given cluster. type Runtime struct { config *config.Config - client *fleetmanager.Client + client *fmAPI.Client clusterID string reconcilers reconcilerRegistry k8sClient ctrlClient.Client diff --git a/internal/dinosaur/pkg/clusters/cluster_builder.go b/internal/dinosaur/pkg/clusters/cluster_builder.go index 404c078a67..02761113ce 100644 --- a/internal/dinosaur/pkg/clusters/cluster_builder.go +++ b/internal/dinosaur/pkg/clusters/cluster_builder.go @@ -9,6 +9,7 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/clusters/types" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/config" "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocmImpl "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" ) // ClusterNamePrefix a prefix used for new OCM cluster names @@ -45,7 +46,7 @@ type clusterBuilder struct { // NewClusterBuilder create a new default implementation of ClusterBuilder. func NewClusterBuilder(awsConfig *config.AWSConfig, dataplaneClusterConfig *config.DataplaneClusterConfig) ClusterBuilder { return &clusterBuilder{ - idGenerator: ocm.NewIDGenerator(ClusterNamePrefix), + idGenerator: ocmImpl.NewIDGenerator(ClusterNamePrefix), awsConfig: awsConfig, dataplaneClusterConfig: dataplaneClusterConfig, } diff --git a/internal/dinosaur/pkg/clusters/cluster_test.go b/internal/dinosaur/pkg/clusters/cluster_test.go index 0a15463a91..d8cc3af64e 100644 --- a/internal/dinosaur/pkg/clusters/cluster_test.go +++ b/internal/dinosaur/pkg/clusters/cluster_test.go @@ -6,7 +6,9 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/clusters/types" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/config" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocmAPI "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" + ocmMocks "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/mocks" clustersmgmtv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" ) @@ -35,7 +37,7 @@ func Test_clusterBuilder_NewOCMClusterFromCluster(t *testing.T) { SecretAccessKey(awsConfig.SecretAccessKey) type fields struct { - idGenerator ocm.IDGenerator + idGenerator ocmAPI.IDGenerator awsConfig *config.AWSConfig dataplaneClusterConfig *config.DataplaneClusterConfig } @@ -101,7 +103,7 @@ func Test_clusterBuilder_NewOCMClusterFromCluster(t *testing.T) { { name: "successful conversion of all supported provided values", fields: fields{ - idGenerator: &ocm.IDGeneratorMock{ + idGenerator: &ocmMocks.IDGeneratorMock{ GenerateFunc: func() string { return "" }, diff --git a/internal/dinosaur/pkg/clusters/ocm_provider.go b/internal/dinosaur/pkg/clusters/ocm_provider.go index 14236206f4..33cce271ae 100644 --- a/internal/dinosaur/pkg/clusters/ocm_provider.go +++ b/internal/dinosaur/pkg/clusters/ocm_provider.go @@ -5,21 +5,18 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/clusters/types" "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocmImpl "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" clustersmgmtv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" "github.com/pkg/errors" "github.com/stackrox/acs-fleet-manager/pkg/api" ) -const ( - ipdAlreadyCreatedErrorToCheck = "already exists" -) - // OCMProvider ... type OCMProvider struct { ocmClient ocm.Client clusterBuilder ClusterBuilder - ocmConfig *ocm.OCMConfig + ocmConfig *ocmImpl.OCMConfig } // blank assignment to verify that OCMProvider implements Provider @@ -150,7 +147,7 @@ func (o *OCMProvider) GetCloudProviderRegions(providerInfo types.CloudProviderIn // ensure OCMProvider implements Provider interface var _ Provider = &OCMProvider{} -func newOCMProvider(ocmClient ocm.ClusterManagementClient, clusterBuilder ClusterBuilder, ocmConfig *ocm.OCMConfig) *OCMProvider { +func newOCMProvider(ocmClient ocmImpl.ClusterManagementClient, clusterBuilder ClusterBuilder, ocmConfig *ocmImpl.OCMConfig) *OCMProvider { return &OCMProvider{ ocmClient: ocmClient, clusterBuilder: clusterBuilder, diff --git a/internal/dinosaur/pkg/clusters/ocm_provider_test.go b/internal/dinosaur/pkg/clusters/ocm_provider_test.go index ea1dac26aa..dab81700d2 100644 --- a/internal/dinosaur/pkg/clusters/ocm_provider_test.go +++ b/internal/dinosaur/pkg/clusters/ocm_provider_test.go @@ -6,7 +6,9 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/clusters/types" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/config" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocmAPI "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" + ocmMocks "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/mocks" . "github.com/onsi/gomega" clustersmgmtv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" @@ -16,7 +18,7 @@ import ( func TestOCMProvider_Create(t *testing.T) { type fields struct { - ocmClient ocm.Client + ocmClient ocmAPI.Client } type args struct { clusterReq types.ClusterRequest @@ -50,7 +52,7 @@ func TestOCMProvider_Create(t *testing.T) { { name: "should return created cluster", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ CreateClusterFunc: func(cluster *clustersmgmtv1.Cluster) (*clustersmgmtv1.Cluster, error) { return clustersmgmtv1.NewCluster().ID(internalID).ExternalID(externalID).Build() }, @@ -70,7 +72,7 @@ func TestOCMProvider_Create(t *testing.T) { { name: "should return error when create cluster failed from OCM", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ CreateClusterFunc: func(cluster *clustersmgmtv1.Cluster) (*clustersmgmtv1.Cluster, error) { return nil, errors.Errorf("failed to create cluster") }, @@ -97,7 +99,7 @@ func TestOCMProvider_Create(t *testing.T) { func TestOCMProvider_CheckClusterStatus(t *testing.T) { type fields struct { - ocmClient ocm.Client + ocmClient ocmAPI.Client } type args struct { clusterSpec *types.ClusterSpec @@ -125,7 +127,7 @@ func TestOCMProvider_CheckClusterStatus(t *testing.T) { { name: "should return cluster status ready", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ GetClusterFunc: func(clusterID string) (*clustersmgmtv1.Cluster, error) { sb := clustersmgmtv1.NewClusterStatus().State(clustersmgmtv1.ClusterStateReady) return clustersmgmtv1.NewCluster().Status(sb).ExternalID(externalID).Build() @@ -146,7 +148,7 @@ func TestOCMProvider_CheckClusterStatus(t *testing.T) { { name: "should return cluster status failed", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ GetClusterFunc: func(clusterID string) (*clustersmgmtv1.Cluster, error) { sb := clustersmgmtv1.NewClusterStatus().State(clustersmgmtv1.ClusterStateError).ProvisionErrorMessage(clusterFailedProvisioningErrorText) return clustersmgmtv1.NewCluster().Status(sb).ExternalID(externalID).Build() @@ -168,7 +170,7 @@ func TestOCMProvider_CheckClusterStatus(t *testing.T) { { name: "should return error when failed to get cluster from OCM", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ GetClusterFunc: func(clusterID string) (*clustersmgmtv1.Cluster, error) { return nil, errors.Errorf("failed to get cluster") }, @@ -197,7 +199,7 @@ func TestOCMProvider_CheckClusterStatus(t *testing.T) { func TestOCMProvider_Delete(t *testing.T) { type fields struct { - ocmClient ocm.Client + ocmClient ocmAPI.Client } type args struct { clusterSpec *types.ClusterSpec @@ -222,7 +224,7 @@ func TestOCMProvider_Delete(t *testing.T) { { name: "should return true if cluster is not found from OCM", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ DeleteClusterFunc: func(clusterID string) (int, error) { return http.StatusNotFound, nil }, @@ -237,7 +239,7 @@ func TestOCMProvider_Delete(t *testing.T) { { name: "should return false if the cluster still exists in OCM", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ DeleteClusterFunc: func(clusterID string) (int, error) { return http.StatusConflict, nil }, @@ -252,7 +254,7 @@ func TestOCMProvider_Delete(t *testing.T) { { name: "should return error", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ DeleteClusterFunc: func(clusterID string) (int, error) { return 0, errors.Errorf("failed to delete cluster from OCM") }, @@ -281,7 +283,7 @@ func TestOCMProvider_Delete(t *testing.T) { func TestOCMProvider_GetClusterDNS(t *testing.T) { type fields struct { - ocmClient ocm.Client + ocmClient ocmAPI.Client } type args struct { clusterSpec *types.ClusterSpec @@ -308,7 +310,7 @@ func TestOCMProvider_GetClusterDNS(t *testing.T) { { name: "should return dns value from OCM", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ GetClusterDNSFunc: func(clusterID string) (string, error) { return dns, nil }, @@ -323,7 +325,7 @@ func TestOCMProvider_GetClusterDNS(t *testing.T) { { name: "should return error", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ GetClusterDNSFunc: func(clusterID string) (string, error) { return "", errors.Errorf("failed to get dns value from OCM") }, @@ -352,7 +354,7 @@ func TestOCMProvider_GetClusterDNS(t *testing.T) { func TestOCMProvider_GetCloudProviders(t *testing.T) { type fields struct { - ocmClient ocm.Client + ocmClient ocmAPI.Client } providerID1 := "provider-id-1" @@ -368,7 +370,7 @@ func TestOCMProvider_GetCloudProviders(t *testing.T) { { name: "should return cloud providers when there are no cloud providers returned from ocm", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ GetCloudProvidersFunc: func() (*clustersmgmtv1.CloudProviderList, error) { return clustersmgmtv1.NewCloudProviderList().Build() }, @@ -380,7 +382,7 @@ func TestOCMProvider_GetCloudProviders(t *testing.T) { { name: "should return cloud providers when there are cloud providers returned from ocm", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ GetCloudProvidersFunc: func() (*clustersmgmtv1.CloudProviderList, error) { p := clustersmgmtv1.NewCloudProvider().ID(providerID1).Name(providerName1).DisplayName(providerDisplayName1) return clustersmgmtv1.NewCloudProviderList().Items(p).Build() @@ -397,7 +399,7 @@ func TestOCMProvider_GetCloudProviders(t *testing.T) { { name: "should return error when failed to get cloud providers", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ GetCloudProvidersFunc: func() (*clustersmgmtv1.CloudProviderList, error) { return nil, errors.Errorf("failed to get cloud providers") }, @@ -423,7 +425,7 @@ func TestOCMProvider_GetCloudProviders(t *testing.T) { func TestOCMProvider_GetCloudProviderRegions(t *testing.T) { type fields struct { - ocmClient ocm.Client + ocmClient ocmAPI.Client } type args struct { @@ -449,7 +451,7 @@ func TestOCMProvider_GetCloudProviderRegions(t *testing.T) { { name: "should return cloud providers when there are no cloud providers returned from ocm", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ GetRegionsFunc: func(provider *clustersmgmtv1.CloudProvider) (*clustersmgmtv1.CloudRegionList, error) { Expect(provider.ID()).To(Equal(providerID1)) Expect(provider.Name()).To(Equal(providerName1)) @@ -469,7 +471,7 @@ func TestOCMProvider_GetCloudProviderRegions(t *testing.T) { { name: "should return cloud providers when there are cloud providers returned from ocm", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ GetRegionsFunc: func(provider *clustersmgmtv1.CloudProvider) (*clustersmgmtv1.CloudRegionList, error) { Expect(provider.ID()).To(Equal(providerID1)) Expect(provider.Name()).To(Equal(providerName1)) @@ -501,7 +503,7 @@ func TestOCMProvider_GetCloudProviderRegions(t *testing.T) { { name: "should return error when failed to get cloud provider regions", fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmMocks.ClientMock{ GetRegionsFunc: func(provider *clustersmgmtv1.CloudProvider) (*clustersmgmtv1.CloudRegionList, error) { return nil, errors.Errorf("failed get cloud provider regions") }, diff --git a/internal/dinosaur/pkg/clusters/provider.go b/internal/dinosaur/pkg/clusters/provider.go index cfb7292fc1..98de12993d 100644 --- a/internal/dinosaur/pkg/clusters/provider.go +++ b/internal/dinosaur/pkg/clusters/provider.go @@ -5,7 +5,7 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/clusters/types" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/config" "github.com/stackrox/acs-fleet-manager/pkg/api" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/db" ) diff --git a/internal/dinosaur/pkg/clusters/types/types.go b/internal/dinosaur/pkg/clusters/types/types.go index 514cca1e65..512d3df7ba 100644 --- a/internal/dinosaur/pkg/clusters/types/types.go +++ b/internal/dinosaur/pkg/clusters/types/types.go @@ -3,7 +3,7 @@ package types import ( "github.com/stackrox/acs-fleet-manager/pkg/api" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" ) // Parameter Specify the parameters for an installation diff --git a/internal/dinosaur/pkg/cmd/fleetmanagerclient/client.go b/internal/dinosaur/pkg/cmd/fleetmanagerclient/client.go index 7f2c78ef0a..21c3188eeb 100644 --- a/internal/dinosaur/pkg/cmd/fleetmanagerclient/client.go +++ b/internal/dinosaur/pkg/cmd/fleetmanagerclient/client.go @@ -10,6 +10,7 @@ import ( "github.com/golang/glog" "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager" + impl "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager/impl" ) var ( @@ -41,8 +42,8 @@ func AuthenticatedClientWithRHOASToken(ctx context.Context) *fleetmanager.Client } singletonRHOASTokenInstance.Do(func() { - auth, err := fleetmanager.NewAuth(ctx, fleetmanager.StaticTokenAuthName, fleetmanager.Option{ - Static: fleetmanager.StaticOption{ + auth, err := impl.NewAuth(ctx, impl.StaticTokenAuthName, impl.Option{ + Static: impl.StaticOption{ StaticToken: rhoasToken, }, }) @@ -51,7 +52,7 @@ func AuthenticatedClientWithRHOASToken(ctx context.Context) *fleetmanager.Client return } - fmClientAuthWithRHOASToken, err = fleetmanager.NewClient(fleetManagerEndpoint, auth) + fmClientAuthWithRHOASToken, err = impl.NewClient(fleetManagerEndpoint, auth) if err != nil { glog.Fatalf("Failed to create connection: %s", err) return @@ -80,8 +81,8 @@ func AuthenticatedClientWithOCM(ctx context.Context) *fleetmanager.Client { } singletonOCMRefreshTokenInstance.Do(func() { - auth, err := fleetmanager.NewAuth(ctx, fleetmanager.OCMAuthName, fleetmanager.Option{ - Ocm: fleetmanager.OCMOption{ + auth, err := impl.NewAuth(ctx, impl.OCMAuthName, impl.Option{ + Ocm: impl.OCMOption{ RefreshToken: ocmRefreshToken, }, }) @@ -90,7 +91,7 @@ func AuthenticatedClientWithOCM(ctx context.Context) *fleetmanager.Client { return } - fmClientAuthWithOCMRefreshToken, err = fleetmanager.NewClient(fleetManagerEndpoint, auth) + fmClientAuthWithOCMRefreshToken, err = impl.NewClient(fleetManagerEndpoint, auth) if err != nil { glog.Fatalf("Failed to create connection: %s", err) return diff --git a/internal/dinosaur/pkg/dinosaurs/types/types.go b/internal/dinosaur/pkg/dinosaurs/types/types.go index ddbca90c0e..db4b9976e5 100644 --- a/internal/dinosaur/pkg/dinosaurs/types/types.go +++ b/internal/dinosaur/pkg/dinosaurs/types/types.go @@ -1,7 +1,9 @@ // Package types ... package types -import "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" +import ( + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" +) // DinosaurInstanceType ... type DinosaurInstanceType string diff --git a/internal/dinosaur/pkg/environments/integration.go b/internal/dinosaur/pkg/environments/integration.go index b44f42873b..e0141639dc 100644 --- a/internal/dinosaur/pkg/environments/integration.go +++ b/internal/dinosaur/pkg/environments/integration.go @@ -4,7 +4,7 @@ import ( "os" "github.com/stackrox/acs-fleet-manager/pkg/client/observatorium" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + 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" ) diff --git a/internal/dinosaur/pkg/handlers/cloud_accounts.go b/internal/dinosaur/pkg/handlers/cloud_accounts.go index 05a33d4826..82d2066376 100644 --- a/internal/dinosaur/pkg/handlers/cloud_accounts.go +++ b/internal/dinosaur/pkg/handlers/cloud_accounts.go @@ -7,7 +7,7 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/presenters" "github.com/stackrox/acs-fleet-manager/pkg/auth" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/errors" "github.com/stackrox/acs-fleet-manager/pkg/handlers" ) diff --git a/internal/dinosaur/pkg/handlers/cloud_accounts_test.go b/internal/dinosaur/pkg/handlers/cloud_accounts_test.go index 9c02d0b786..e949075377 100644 --- a/internal/dinosaur/pkg/handlers/cloud_accounts_test.go +++ b/internal/dinosaur/pkg/handlers/cloud_accounts_test.go @@ -13,7 +13,7 @@ import ( v1 "github.com/openshift-online/ocm-sdk-go/accountsmgmt/v1" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/public" "github.com/stackrox/acs-fleet-manager/pkg/auth" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/mocks" "github.com/stretchr/testify/assert" "github.com/pkg/errors" diff --git a/internal/dinosaur/pkg/routes/route_loader.go b/internal/dinosaur/pkg/routes/route_loader.go index ca9ffa8df9..44ead3601c 100644 --- a/internal/dinosaur/pkg/routes/route_loader.go +++ b/internal/dinosaur/pkg/routes/route_loader.go @@ -20,7 +20,7 @@ import ( "github.com/stackrox/acs-fleet-manager/pkg/api" "github.com/stackrox/acs-fleet-manager/pkg/auth" "github.com/stackrox/acs-fleet-manager/pkg/client/iam" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + 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" "github.com/stackrox/acs-fleet-manager/pkg/errors" diff --git a/internal/dinosaur/pkg/services/addon.go b/internal/dinosaur/pkg/services/addon.go index b521e0c38f..03fc0f7891 100644 --- a/internal/dinosaur/pkg/services/addon.go +++ b/internal/dinosaur/pkg/services/addon.go @@ -12,6 +12,7 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/gitops" "github.com/stackrox/acs-fleet-manager/pkg/api" "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocmImpl "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/features" "github.com/stackrox/acs-fleet-manager/pkg/shared" "golang.org/x/exp/maps" @@ -26,7 +27,7 @@ type AddonProvisioner struct { } // NewAddonProvisioner creates a new instance of AddonProvisioner -func NewAddonProvisioner(addonConfig *ocm.AddonConfig, baseConfig *ocm.OCMConfig) (*AddonProvisioner, error) { +func NewAddonProvisioner(addonConfig *ocmImpl.AddonConfig, baseConfig *ocmImpl.OCMConfig) (*AddonProvisioner, error) { addonOCMConfig := *baseConfig addonOCMConfig.BaseURL = addonConfig.URL @@ -34,17 +35,17 @@ func NewAddonProvisioner(addonConfig *ocm.AddonConfig, baseConfig *ocm.OCMConfig addonOCMConfig.ClientSecret = addonConfig.ClientSecret // pragma: allowlist secret addonOCMConfig.SelfToken = addonConfig.SelfToken - conn, _, err := ocm.NewOCMConnection(&addonOCMConfig, addonOCMConfig.BaseURL) + conn, _, err := ocmImpl.NewOCMConnection(&addonOCMConfig, addonOCMConfig.BaseURL) if err != nil { return nil, fmt.Errorf("addon service ocm connection: %w", err) } return &AddonProvisioner{ - ocmClient: ocm.NewClient(conn), + ocmClient: ocmImpl.NewClient(conn), customizations: initCustomizations(*addonConfig), }, nil } -func initCustomizations(config ocm.AddonConfig) []addonCustomization { +func initCustomizations(config ocmImpl.AddonConfig) []addonCustomization { var customizations []addonCustomization if config.InheritFleetshardSyncImageTag { diff --git a/internal/dinosaur/pkg/services/addon_test.go b/internal/dinosaur/pkg/services/addon_test.go index 978fdb6bc0..ed386b578c 100644 --- a/internal/dinosaur/pkg/services/addon_test.go +++ b/internal/dinosaur/pkg/services/addon_test.go @@ -10,7 +10,8 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/dbapi" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/gitops" "github.com/stackrox/acs-fleet-manager/pkg/api" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocmImpl "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/mocks" "github.com/stackrox/acs-fleet-manager/pkg/errors" ) @@ -664,7 +665,7 @@ func TestAddonProvisioner_Provision_InheritFleetshardImageTag_Install(t *testing return nil }, } - addonConfig := ocm.AddonConfig{ + addonConfig := ocmImpl.AddonConfig{ FleetshardSyncImageTag: "0307e03", InheritFleetshardSyncImageTag: true, } @@ -712,7 +713,7 @@ func TestAddonProvisioner_Provision_InheritFleetshardImageTag_Upgrade(t *testing return nil }, } - addonConfig := ocm.AddonConfig{ + addonConfig := ocmImpl.AddonConfig{ FleetshardSyncImageTag: "0307e03", InheritFleetshardSyncImageTag: true, } @@ -756,14 +757,14 @@ func addonsJSON(addons []dbapi.AddonInstallation) api.JSON { func TestAddonProvisioner_NewAddonProvisioner(t *testing.T) { RegisterTestingT(t) - addonConfigPtr := &ocm.AddonConfig{ + addonConfigPtr := &ocmImpl.AddonConfig{ URL: "https://addon-service.test", ClientID: "addon-client-id", ClientSecret: "addon-client-secret", // pragma: allowlist secret SelfToken: "addon-token", } - baseConfigPtr := &ocm.OCMConfig{ + baseConfigPtr := &ocmImpl.OCMConfig{ BaseURL: "https://base.test", ClientID: "base-client-id", ClientSecret: "base-client-secret", // pragma: allowlist secret diff --git a/internal/dinosaur/pkg/services/data_migration.go b/internal/dinosaur/pkg/services/data_migration.go index 83b3b10f81..e13779b4e9 100644 --- a/internal/dinosaur/pkg/services/data_migration.go +++ b/internal/dinosaur/pkg/services/data_migration.go @@ -5,7 +5,7 @@ import ( "github.com/pkg/errors" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/dbapi" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/db" ) diff --git a/internal/dinosaur/pkg/services/data_migration_test.go b/internal/dinosaur/pkg/services/data_migration_test.go index d07d20d091..b02d8539f7 100644 --- a/internal/dinosaur/pkg/services/data_migration_test.go +++ b/internal/dinosaur/pkg/services/data_migration_test.go @@ -6,7 +6,7 @@ import ( amsv1 "github.com/openshift-online/ocm-sdk-go/accountsmgmt/v1" "github.com/pkg/errors" mocket "github.com/selvatico/go-mocket" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocmClientMock "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/mocks" "github.com/stackrox/acs-fleet-manager/pkg/db" "github.com/stretchr/testify/assert" ) @@ -67,7 +67,7 @@ func TestDataMigration(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { connectionFactory := db.NewMockConnectionFactory(nil) - amsClient := ocm.ClientMock{GetOrganisationFromExternalIDFunc: func(externalID string) (*amsv1.Organization, error) { + amsClient := ocmClientMock.ClientMock{GetOrganisationFromExternalIDFunc: func(externalID string) (*amsv1.Organization, error) { org, err := amsv1.NewOrganization(). ID("12345678"). Name("dummy-org"). diff --git a/internal/dinosaur/pkg/services/dinosaur.go b/internal/dinosaur/pkg/services/dinosaur.go index 6a9d418e00..e6b39c2b01 100644 --- a/internal/dinosaur/pkg/services/dinosaur.go +++ b/internal/dinosaur/pkg/services/dinosaur.go @@ -17,7 +17,7 @@ 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/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" dynamicClientAPI "github.com/stackrox/acs-fleet-manager/pkg/client/redhatsso/api" "github.com/stackrox/acs-fleet-manager/pkg/client/redhatsso/dynamicclients" "github.com/stackrox/acs-fleet-manager/pkg/db" diff --git a/internal/dinosaur/pkg/services/quota/ams_quota_service.go b/internal/dinosaur/pkg/services/quota/ams_quota_service.go index 1514fc9802..5b9df89903 100644 --- a/internal/dinosaur/pkg/services/quota/ams_quota_service.go +++ b/internal/dinosaur/pkg/services/quota/ams_quota_service.go @@ -11,7 +11,7 @@ import ( "github.com/openshift-online/ocm-sdk-go/authentication" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/dbapi" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/dinosaurs/types" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/errors" ) diff --git a/internal/dinosaur/pkg/services/quota/ams_quota_service_test.go b/internal/dinosaur/pkg/services/quota/ams_quota_service_test.go index 7bee0dd3ae..a957ec6693 100644 --- a/internal/dinosaur/pkg/services/quota/ams_quota_service_test.go +++ b/internal/dinosaur/pkg/services/quota/ams_quota_service_test.go @@ -12,6 +12,8 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/dbapi" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/dinosaurs/types" "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocmImpl "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" + ocmClientMock "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/mocks" "github.com/onsi/gomega" v1 "github.com/openshift-online/ocm-sdk-go/accountsmgmt/v1" @@ -53,7 +55,7 @@ func Test_AMSCheckQuota(t *testing.T) { false, }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { cloudAuthorizationResp, _ := v1.NewClusterAuthorizationResponse().Allowed(true).Build() return cloudAuthorizationResp, nil @@ -63,10 +65,10 @@ func Test_AMSCheckQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - if product != string(ocm.RHACSProduct) { + if product != string(ocmImpl.RHACSProduct) { return []*v1.QuotaCost{}, nil } - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb, err := v1.NewQuotaCost().Allowed(1).Consumed(0).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb}, nil @@ -89,9 +91,9 @@ func Test_AMSCheckQuota(t *testing.T) { false, }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { - if cb.ProductID() == string(ocm.RHACSProduct) { + if cb.ProductID() == string(ocmImpl.RHACSProduct) { cloudAuthorizationResp, _ := v1.NewClusterAuthorizationResponse().Allowed(true).Build() return cloudAuthorizationResp, nil } @@ -103,10 +105,10 @@ func Test_AMSCheckQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - if product != string(ocm.RHACSProduct) { + if product != string(ocmImpl.RHACSProduct) { return []*v1.QuotaCost{}, nil } - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb, err := v1.NewQuotaCost().Allowed(1).Consumed(0).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb}, nil @@ -129,7 +131,7 @@ func Test_AMSCheckQuota(t *testing.T) { false, }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { cloudAuthorizationResp, _ := v1.NewClusterAuthorizationResponse().Allowed(false).Build() return cloudAuthorizationResp, nil @@ -159,7 +161,7 @@ func Test_AMSCheckQuota(t *testing.T) { false, }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { return nil, fmt.Errorf("some errors") }, @@ -168,10 +170,10 @@ func Test_AMSCheckQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - if product != string(ocm.RHACSProduct) { + if product != string(ocmImpl.RHACSProduct) { return []*v1.QuotaCost{}, nil } - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb, err := v1.NewQuotaCost().Allowed(1).Consumed(0).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb}, nil @@ -235,7 +237,7 @@ func Test_AMSReserveQuota(t *testing.T) { owner: "testUser", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { return mockClusterAuthorizationResponse(), nil }, @@ -244,7 +246,7 @@ func Test_AMSReserveQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb, err := v1.NewQuotaCost().Allowed(1).Consumed(0).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb}, nil @@ -265,7 +267,7 @@ func Test_AMSReserveQuota(t *testing.T) { owner: "testUser", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { return mockClusterAuthorizationResponse(), nil }, @@ -274,10 +276,10 @@ func Test_AMSReserveQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb1, err := v1.NewQuotaCost().Allowed(1).Consumed(0).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) - rrbq2 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq2 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb2, err := v1.NewQuotaCost().Allowed(1).Consumed(0).OrganizationID(organizationID).RelatedResources(rrbq2).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb1, qcb2}, nil @@ -298,7 +300,7 @@ func Test_AMSReserveQuota(t *testing.T) { owner: "testUser", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { return mockClusterAuthorizationResponse(), nil }, @@ -307,10 +309,10 @@ func Test_AMSReserveQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb1, err := v1.NewQuotaCost().Allowed(1).Consumed(0).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) - rrbq2 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq2 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb2, err := v1.NewQuotaCost().Allowed(1).Consumed(1).OrganizationID(organizationID).RelatedResources(rrbq2).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb2, qcb1}, nil @@ -331,7 +333,7 @@ func Test_AMSReserveQuota(t *testing.T) { owner: "testUser", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { return mockClusterAuthorizationResponse(), nil }, @@ -340,7 +342,7 @@ func Test_AMSReserveQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocm.RHACSTrialProduct)).ResourceName(resourceName).Cost(0) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocmImpl.RHACSTrialProduct)).ResourceName(resourceName).Cost(0) qcb1, err := v1.NewQuotaCost().Allowed(0).Consumed(2).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb1}, nil @@ -361,7 +363,7 @@ func Test_AMSReserveQuota(t *testing.T) { owner: "testUser", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { return mockClusterAuthorizationResponse(), nil }, @@ -370,10 +372,10 @@ func Test_AMSReserveQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb1, err := v1.NewQuotaCost().Allowed(1).Consumed(1).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) - rrbq2 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq2 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb2, err := v1.NewQuotaCost().Allowed(1).Consumed(1).OrganizationID(organizationID).RelatedResources(rrbq2).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb2, qcb1}, nil @@ -392,7 +394,7 @@ func Test_AMSReserveQuota(t *testing.T) { owner: "testUser", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { return mockClusterAuthorizationResponse(), nil }, @@ -417,7 +419,7 @@ func Test_AMSReserveQuota(t *testing.T) { owner: "testUser", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { return mockClusterAuthorizationResponse(), nil }, @@ -426,10 +428,10 @@ func Test_AMSReserveQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel(string("unknownbillingmodelone")).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq1 := v1.NewRelatedResource().BillingModel(string("unknownbillingmodelone")).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb1, err := v1.NewQuotaCost().Allowed(1).Consumed(1).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) - rrbq2 := v1.NewRelatedResource().BillingModel(string("unknownbillingmodeltwo")).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq2 := v1.NewRelatedResource().BillingModel(string("unknownbillingmodeltwo")).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb2, err := v1.NewQuotaCost().Allowed(1).Consumed(1).OrganizationID(organizationID).RelatedResources(rrbq2).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb1, qcb2}, nil @@ -448,7 +450,7 @@ func Test_AMSReserveQuota(t *testing.T) { owner: "testUser", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { cloudAuthorizationResp, _ := v1.NewClusterAuthorizationResponse().Allowed(false).Build() return cloudAuthorizationResp, nil @@ -458,7 +460,7 @@ func Test_AMSReserveQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb, err := v1.NewQuotaCost().Allowed(1).Consumed(0).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb}, nil @@ -477,7 +479,7 @@ func Test_AMSReserveQuota(t *testing.T) { owner: "testUser", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { return mockClusterAuthorizationResponse(), nil }, @@ -486,7 +488,7 @@ func Test_AMSReserveQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocm.RHACSTrialProduct)).ResourceName(resourceName).Cost(0) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocmImpl.RHACSTrialProduct)).ResourceName(resourceName).Cost(0) qcb1, err := v1.NewQuotaCost().Allowed(0).Consumed(2).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb1}, nil @@ -505,7 +507,7 @@ func Test_AMSReserveQuota(t *testing.T) { owner: "testUser", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { return mockClusterAuthorizationResponse(), nil }, @@ -514,7 +516,7 @@ func Test_AMSReserveQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocm.RHACSTrialProduct)).ResourceName(resourceName).Cost(0) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocmImpl.RHACSTrialProduct)).ResourceName(resourceName).Cost(0) qcb1, err := v1.NewQuotaCost().Allowed(0).Consumed(2).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb1}, nil @@ -540,7 +542,7 @@ func Test_AMSReserveQuota(t *testing.T) { cloudAccountID: "different cloudAccountID", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { return mockClusterAuthorizationResponse(), nil }, @@ -549,7 +551,7 @@ func Test_AMSReserveQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocm.RHACSTrialProduct)).ResourceName(resourceName).Cost(0) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocmImpl.RHACSTrialProduct)).ResourceName(resourceName).Cost(0) qcb1, err := v1.NewQuotaCost().Allowed(0).Consumed(2).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb1}, nil @@ -575,7 +577,7 @@ func Test_AMSReserveQuota(t *testing.T) { cloudAccountID: "cloudAccountID", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { return mockClusterAuthorizationResponse(), nil }, @@ -584,7 +586,7 @@ func Test_AMSReserveQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocm.RHACSTrialProduct)).ResourceName(resourceName).Cost(0) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocmImpl.RHACSTrialProduct)).ResourceName(resourceName).Cost(0) qcb1, err := v1.NewQuotaCost().Allowed(0).Consumed(2).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb1}, nil @@ -614,7 +616,7 @@ func Test_AMSReserveQuota(t *testing.T) { cloudProviderID: "aws", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ ClusterAuthorizationFunc: func(cb *v1.ClusterAuthorizationRequest) (*v1.ClusterAuthorizationResponse, error) { return mockClusterAuthorizationResponse(), nil }, @@ -623,7 +625,7 @@ func Test_AMSReserveQuota(t *testing.T) { return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocm.RHACSTrialProduct)).ResourceName(resourceName).Cost(0) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocmImpl.RHACSTrialProduct)).ResourceName(resourceName).Cost(0) qcb1, err := v1.NewQuotaCost().Allowed(0).Consumed(2).OrganizationID(organizationID).RelatedResources(rrbq1).Build() require.NoError(t, err) return []*v1.QuotaCost{qcb1}, nil @@ -664,7 +666,7 @@ func Test_AMSReserveQuota(t *testing.T) { gomega.Expect(err != nil).To(gomega.Equal(tt.wantErr)) if tt.wantBillingModel != "" || tt.wantBillingMarketplaceAccount != "" { - ocmClientMock := tt.fields.ocmClient.(*ocm.ClientMock) + ocmClientMock := tt.fields.ocmClient.(*ocmClientMock.ClientMock) clusterAuthorizationCalls := ocmClientMock.ClusterAuthorizationCalls() gomega.Expect(len(clusterAuthorizationCalls)).To(gomega.Equal(1)) clusterAuthorizationResources := clusterAuthorizationCalls[0].Cb.Resources() @@ -713,7 +715,7 @@ func Test_Delete_Quota(t *testing.T) { subscriptionID: "1223", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ DeleteSubscriptionFunc: func(id string) (int, error) { return 1, nil }, @@ -727,7 +729,7 @@ func Test_Delete_Quota(t *testing.T) { subscriptionID: "1223", }, fields: fields{ - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ DeleteSubscriptionFunc: func(id string) (int, error) { return 0, serviceErrors.GeneralError("failed to delete subscription") }, @@ -764,7 +766,7 @@ func Test_amsQuotaService_HasQuotaAllowance(t *testing.T) { }{ { name: "returns false if no quota cost exists for the dinosaur's organization", - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ GetOrganisationFromExternalIDFunc: func(externalId string) (*v1.Organization, error) { org, _ := v1.NewOrganization().ID(fmt.Sprintf("fake-org-id-%s", externalId)).Build() return org, nil @@ -782,14 +784,14 @@ func Test_amsQuotaService_HasQuotaAllowance(t *testing.T) { }, { name: "returns false if the quota cost billing model is not among the supported ones", - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ GetOrganisationFromExternalIDFunc: func(externalId string) (*v1.Organization, error) { org, _ := v1.NewOrganization().ID(fmt.Sprintf("fake-org-id-%s", externalId)).Build() return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel("unknownbillingmodel").Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) - rrbq2 := v1.NewRelatedResource().BillingModel("unknownbillingmodel2").Product(string(ocm.RHACSTrialProduct)).ResourceName(resourceName).Cost(1) + rrbq1 := v1.NewRelatedResource().BillingModel("unknownbillingmodel").Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq2 := v1.NewRelatedResource().BillingModel("unknownbillingmodel2").Product(string(ocmImpl.RHACSTrialProduct)).ResourceName(resourceName).Cost(1) qcb, err := v1.NewQuotaCost().Allowed(1).Consumed(0).OrganizationID(organizationID).RelatedResources(rrbq1, rrbq2).Build() if err != nil { panic("unexpected error") @@ -806,14 +808,14 @@ func Test_amsQuotaService_HasQuotaAllowance(t *testing.T) { }, { name: "returns true if there is at least a 'standard' quota cost billing model", - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ GetOrganisationFromExternalIDFunc: func(externalId string) (*v1.Organization, error) { org, _ := v1.NewOrganization().ID(fmt.Sprintf("fake-org-id-%s", externalId)).Build() return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) - rrbq2 := v1.NewRelatedResource().BillingModel("unknownbillingmodel2").Product(string(ocm.RHACSTrialProduct)).ResourceName(resourceName).Cost(1) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq2 := v1.NewRelatedResource().BillingModel("unknownbillingmodel2").Product(string(ocmImpl.RHACSTrialProduct)).ResourceName(resourceName).Cost(1) qcb, err := v1.NewQuotaCost().Allowed(1).Consumed(0).OrganizationID(organizationID).RelatedResources(rrbq1, rrbq2).Build() if err != nil { panic("unexpected error") @@ -830,18 +832,18 @@ func Test_amsQuotaService_HasQuotaAllowance(t *testing.T) { }, { name: "returns true if there is at least a 'marketplace' quota cost billing model", - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ GetOrganisationFromExternalIDFunc: func(externalId string) (*v1.Organization, error) { org, _ := v1.NewOrganization().ID(fmt.Sprintf("fake-org-id-%s", externalId)).Build() return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel("unknownbillingmodel").Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq1 := v1.NewRelatedResource().BillingModel("unknownbillingmodel").Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb, err := v1.NewQuotaCost().Allowed(1).Consumed(1).OrganizationID(organizationID).RelatedResources(rrbq1).Build() if err != nil { panic("unexpected error") } - rrbq2 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq2 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) cloudAccount := v1.NewCloudAccount().CloudAccountID("cloudAccountID").CloudProviderID(awsCloudProvider) qcb2, err := v1.NewQuotaCost().Allowed(1).Consumed(2).OrganizationID(organizationID).RelatedResources(rrbq2).CloudAccounts(cloudAccount).Build() if err != nil { @@ -863,18 +865,18 @@ func Test_amsQuotaService_HasQuotaAllowance(t *testing.T) { }, { name: "returns false if there is no supported billing model with an 'allowed' value greater than 0", - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ GetOrganisationFromExternalIDFunc: func(externalId string) (*v1.Organization, error) { org, _ := v1.NewOrganization().ID(fmt.Sprintf("fake-org-id-%s", externalId)).Build() return org, nil }, GetQuotaCostsForProductFunc: func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) { - rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq1 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplace)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb, err := v1.NewQuotaCost().Allowed(0).Consumed(0).OrganizationID(organizationID).RelatedResources(rrbq1).Build() if err != nil { panic("unexpected error") } - rrbq2 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq2 := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb2, err := v1.NewQuotaCost().Allowed(0).Consumed(0).OrganizationID(organizationID).RelatedResources(rrbq2).Build() if err != nil { panic("unexpected error") @@ -891,7 +893,7 @@ func Test_amsQuotaService_HasQuotaAllowance(t *testing.T) { }, { name: "returns an error if it fails retrieving the organization ID", - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ GetOrganisationFromExternalIDFunc: func(externalId string) (*v1.Organization, error) { return nil, fmt.Errorf("error getting org") }, @@ -907,7 +909,7 @@ func Test_amsQuotaService_HasQuotaAllowance(t *testing.T) { }, { name: "returns an error if it fails retrieving quota costs", - ocmClient: &ocm.ClientMock{ + ocmClient: &ocmClientMock.ClientMock{ GetOrganisationFromExternalIDFunc: func(externalId string) (*v1.Organization, error) { org, _ := v1.NewOrganization().ID(fmt.Sprintf("fake-org-id-%s", externalId)).Build() return org, nil @@ -953,7 +955,7 @@ func Test_amsQuotaService_HasQuotaAllowance_Extra(t *testing.T) { tests := []struct { name string - amsClient ocm.AMSClient + amsClient ocmImpl.AMSClient getQuotaFunc func(organizationID, resourceName, product string) ([]*v1.QuotaCost, error) central *dbapi.CentralRequest @@ -1134,7 +1136,7 @@ func Test_amsQuotaService_HasQuotaAllowance_Extra(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := gomega.NewWithT(t) - var amsClient ocm.AMSClient = &ocm.ClientMock{ + var amsClient ocmImpl.AMSClient = &ocmClientMock.ClientMock{ GetOrganisationFromExternalIDFunc: makeOrganizationFromExternalID, GetQuotaCostsForProductFunc: tt.getQuotaFunc, } @@ -1153,7 +1155,7 @@ func Test_amsQuotaService_HasQuotaAllowance_Extra(t *testing.T) { } func makeStandardTestQuotaCost(resourceName string, organizationID string, allowed int, consumed int, t *testing.T) *v1.QuotaCost { - rrbq := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq := v1.NewRelatedResource().BillingModel(string(v1.BillingModelStandard)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb, err := v1.NewQuotaCost().Allowed(allowed).Consumed(consumed).OrganizationID(organizationID).RelatedResources(rrbq).Build() require.NoError(t, err) return qcb @@ -1161,7 +1163,7 @@ func makeStandardTestQuotaCost(resourceName string, organizationID string, allow func makeCloudTestQuotaCost(resourceName string, organizationID string, allowed int, consumed int, t *testing.T) *v1.QuotaCost { cloudAccount := v1.NewCloudAccount().CloudAccountID("cloudAccountID").CloudProviderID(awsCloudProvider) - rrbq := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplaceAWS)).Product(string(ocm.RHACSProduct)).ResourceName(resourceName).Cost(1) + rrbq := v1.NewRelatedResource().BillingModel(string(v1.BillingModelMarketplaceAWS)).Product(string(ocmImpl.RHACSProduct)).ResourceName(resourceName).Cost(1) qcb, err := v1.NewQuotaCost().Allowed(allowed).Consumed(consumed).OrganizationID(organizationID).RelatedResources(rrbq).CloudAccounts(cloudAccount).Build() require.NoError(t, err) return qcb diff --git a/internal/dinosaur/pkg/services/quota/default_quota_service_factory.go b/internal/dinosaur/pkg/services/quota/default_quota_service_factory.go index 621f80c5b4..2eafaf9a81 100644 --- a/internal/dinosaur/pkg/services/quota/default_quota_service_factory.go +++ b/internal/dinosaur/pkg/services/quota/default_quota_service_factory.go @@ -3,7 +3,7 @@ package quota import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/services" "github.com/stackrox/acs-fleet-manager/pkg/api" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + 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/errors" "github.com/stackrox/acs-fleet-manager/pkg/quotamanagement" diff --git a/internal/dinosaur/pkg/workers/clusters_mgr.go b/internal/dinosaur/pkg/workers/clusters_mgr.go index cedd9a786f..98af551efc 100644 --- a/internal/dinosaur/pkg/workers/clusters_mgr.go +++ b/internal/dinosaur/pkg/workers/clusters_mgr.go @@ -11,7 +11,7 @@ import ( "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" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/logger" "github.com/goava/di" diff --git a/internal/dinosaur/test/common/util.go b/internal/dinosaur/test/common/util.go index 57617d0658..8bb3a41a35 100644 --- a/internal/dinosaur/test/common/util.go +++ b/internal/dinosaur/test/common/util.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/server" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/services" diff --git a/internal/dinosaur/test/helper.go b/internal/dinosaur/test/helper.go index 1f1f56ba73..fc846934fc 100644 --- a/internal/dinosaur/test/helper.go +++ b/internal/dinosaur/test/helper.go @@ -20,7 +20,7 @@ import ( "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" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + 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" "github.com/stackrox/acs-fleet-manager/pkg/server" diff --git a/internal/dinosaur/test/integration/require_terms_acceptance_middleware_test.go b/internal/dinosaur/test/integration/require_terms_acceptance_middleware_test.go index 2bfcb81198..e69095ac52 100644 --- a/internal/dinosaur/test/integration/require_terms_acceptance_middleware_test.go +++ b/internal/dinosaur/test/integration/require_terms_acceptance_middleware_test.go @@ -7,7 +7,7 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/public" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/config" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/test" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/server" . "github.com/onsi/gomega" diff --git a/pkg/auth/require_terms_acceptance_middleware.go b/pkg/auth/require_terms_acceptance_middleware.go index 1b38f1d15a..6d470ea18a 100644 --- a/pkg/auth/require_terms_acceptance_middleware.go +++ b/pkg/auth/require_terms_acceptance_middleware.go @@ -4,7 +4,7 @@ import ( "net/http" "time" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/patrickmn/go-cache" diff --git a/pkg/auth/require_terms_acceptance_middleware_test.go b/pkg/auth/require_terms_acceptance_middleware_test.go index 856d30c4e4..c473ae3fb3 100644 --- a/pkg/auth/require_terms_acceptance_middleware_test.go +++ b/pkg/auth/require_terms_acceptance_middleware_test.go @@ -8,6 +8,7 @@ import ( . "github.com/onsi/gomega" "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocmMocks "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/mocks" "github.com/stackrox/acs-fleet-manager/pkg/errors" "github.com/stackrox/acs-fleet-manager/pkg/shared" ) @@ -23,7 +24,7 @@ func TestRequireTermsAcceptanceMiddleware(t *testing.T) { { name: "should fail if terms checks is enabled and terms are required", enabled: true, - client: &ocm.ClientMock{ + client: &ocmMocks.ClientMock{ GetRequiresTermsAcceptanceFunc: func(username string) (bool, string, error) { return true, "", nil }, @@ -36,7 +37,7 @@ func TestRequireTermsAcceptanceMiddleware(t *testing.T) { { name: "should succeed if terms check is not a enabled even and terms are required", enabled: false, - client: &ocm.ClientMock{ + client: &ocmMocks.ClientMock{ GetRequiresTermsAcceptanceFunc: func(username string) (bool, string, error) { return true, "", nil }, @@ -49,7 +50,7 @@ func TestRequireTermsAcceptanceMiddleware(t *testing.T) { { name: "should succeed if terms checks is enabled and terms are not required", enabled: true, - client: &ocm.ClientMock{ + client: &ocmMocks.ClientMock{ GetRequiresTermsAcceptanceFunc: func(username string) (bool, string, error) { return false, "", nil }, diff --git a/pkg/client/fleetmanager/client.go b/pkg/client/fleetmanager/client.go index daaca50193..f50ee9ef79 100644 --- a/pkg/client/fleetmanager/client.go +++ b/pkg/client/fleetmanager/client.go @@ -3,15 +3,13 @@ package fleetmanager import ( "context" "net/http" - "net/url" - "github.com/pkg/errors" admin "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/admin/private" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/private" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/public" ) -//go:generate moq -out api_moq.go . PublicAPI PrivateAPI AdminAPI +//go:generate moq -rm -out mocks/client_moq.go -pkg mocks . PublicAPI PrivateAPI AdminAPI // PublicAPI is a wrapper interface for the fleetmanager client public API. type PublicAPI interface { @@ -38,46 +36,6 @@ type AdminAPI interface { UpdateCentralNameById(ctx context.Context, id string, centralUpdateNameRequest admin.CentralUpdateNameRequest) (admin.Central, *http.Response, error) } -var ( - _ http.RoundTripper = (*authTransport)(nil) - _ PublicAPI = (*publicAPIDelegate)(nil) - _ PrivateAPI = (*privateAPIDelegate)(nil) - _ AdminAPI = (*adminAPIDelegate)(nil) -) - -type publicAPIDelegate struct { - *public.DefaultApiService -} - -type privateAPIDelegate struct { - *private.AgentClustersApiService -} - -type adminAPIDelegate struct { - *admin.DefaultApiService -} - -type authTransport struct { - transport http.RoundTripper - auth Auth -} - -func (c *authTransport) RoundTrip(req *http.Request) (*http.Response, error) { - if err := c.auth.AddAuth(req); err != nil { - return nil, errors.Wrapf(err, "setting auth on req %+v", req) - } - return c.transport.RoundTrip(req) -} - -// newAuthTransport creates a http.RoundTripper that wraps http.DefaultTransport and injects -// the authorization header from Auth into any request. -func newAuthTransport(auth Auth) *authTransport { - return &authTransport{ - transport: http.DefaultTransport, - auth: auth, - } -} - // Client is a helper struct that wraps around the API clients generated from // OpenAPI spec for the three different API groups of fleet manager: public, private, admin. type Client struct { @@ -86,83 +44,14 @@ type Client struct { adminAPI AdminAPI } -// ClientOption to configure the Client. -type ClientOption func(*options) - -// WithDebugEnabled enables the debug logging for API request sent and received from fleet manager. -// Internally, this will use httputil.DumpRequestOut/DumpResponse. -func WithDebugEnabled() ClientOption { - return func(o *options) { - o.debug = true - } -} - -// WithUserAgent allows to set a custom value that shall be used as the User-Agent header -// when sending requests. -func WithUserAgent(userAgent string) ClientOption { - return func(o *options) { - o.userAgent = userAgent - } -} - -type options struct { - debug bool - userAgent string -} - -func defaultOptions() *options { - return &options{ - debug: false, - userAgent: "OpenAPI-Generator/1.0.0/go", +func MakeClient(publicAPI PublicAPI, privateAPI PrivateAPI, adminAPI AdminAPI) *Client { + return &Client{ + publicAPI: publicAPI, + privateAPI: privateAPI, + adminAPI: adminAPI, } } -// NewClient creates a new fleet manager client with the specified auth type. -// The client will be able to talk to the three different API groups of fleet manager: public, private, admin. -func NewClient(endpoint string, auth Auth, opts ...ClientOption) (*Client, error) { - if _, err := url.Parse(endpoint); err != nil { - return nil, errors.Wrapf(err, "parsing endpoint %q as URL", endpoint) - } - - o := defaultOptions() - for _, opt := range opts { - opt(o) - } - - client := &Client{} - - httpClient := &http.Client{ - Transport: newAuthTransport(auth), - } - - client.publicAPI = &publicAPIDelegate{ - DefaultApiService: public.NewAPIClient(&public.Configuration{ - BasePath: endpoint, - UserAgent: o.userAgent, - Debug: o.debug, - HTTPClient: httpClient, - }).DefaultApi, - } - client.privateAPI = &privateAPIDelegate{ - AgentClustersApiService: private.NewAPIClient(&private.Configuration{ - BasePath: endpoint, - UserAgent: o.userAgent, - Debug: o.debug, - HTTPClient: httpClient, - }).AgentClustersApi, - } - client.adminAPI = &adminAPIDelegate{ - DefaultApiService: admin.NewAPIClient(&admin.Configuration{ - BasePath: endpoint, - UserAgent: o.userAgent, - Debug: o.debug, - HTTPClient: httpClient, - }).DefaultApi, - } - - return client, nil -} - // PublicAPI returns the service to interact with fleet manager's public API. func (c *Client) PublicAPI() PublicAPI { return c.publicAPI diff --git a/pkg/client/fleetmanager/auth.go b/pkg/client/fleetmanager/impl/auth.go similarity index 99% rename from pkg/client/fleetmanager/auth.go rename to pkg/client/fleetmanager/impl/auth.go index ae2e1b0e42..8d4202a41e 100644 --- a/pkg/client/fleetmanager/auth.go +++ b/pkg/client/fleetmanager/impl/auth.go @@ -1,5 +1,5 @@ // Package fleetmanager ... -package fleetmanager +package impl import ( "context" diff --git a/pkg/client/fleetmanager/auth_ocm.go b/pkg/client/fleetmanager/impl/auth_ocm.go similarity index 98% rename from pkg/client/fleetmanager/auth_ocm.go rename to pkg/client/fleetmanager/impl/auth_ocm.go index ef4aec7c3c..17b238c8c7 100644 --- a/pkg/client/fleetmanager/auth_ocm.go +++ b/pkg/client/fleetmanager/impl/auth_ocm.go @@ -1,4 +1,4 @@ -package fleetmanager +package impl import ( "context" diff --git a/pkg/client/fleetmanager/auth_rhsso.go b/pkg/client/fleetmanager/impl/auth_rhsso.go similarity index 98% rename from pkg/client/fleetmanager/auth_rhsso.go rename to pkg/client/fleetmanager/impl/auth_rhsso.go index fb7e835921..52765d1e9a 100644 --- a/pkg/client/fleetmanager/auth_rhsso.go +++ b/pkg/client/fleetmanager/impl/auth_rhsso.go @@ -1,4 +1,4 @@ -package fleetmanager +package impl import ( "context" diff --git a/pkg/client/fleetmanager/auth_static_token.go b/pkg/client/fleetmanager/impl/auth_static_token.go similarity index 98% rename from pkg/client/fleetmanager/auth_static_token.go rename to pkg/client/fleetmanager/impl/auth_static_token.go index ff91ee1601..a9961097d0 100644 --- a/pkg/client/fleetmanager/auth_static_token.go +++ b/pkg/client/fleetmanager/impl/auth_static_token.go @@ -1,4 +1,4 @@ -package fleetmanager +package impl import ( "context" diff --git a/pkg/client/fleetmanager/auth_test.go b/pkg/client/fleetmanager/impl/auth_test.go similarity index 95% rename from pkg/client/fleetmanager/auth_test.go rename to pkg/client/fleetmanager/impl/auth_test.go index 43d4957a98..e0a3957fb0 100644 --- a/pkg/client/fleetmanager/auth_test.go +++ b/pkg/client/fleetmanager/impl/auth_test.go @@ -1,4 +1,4 @@ -package fleetmanager +package impl import ( "testing" diff --git a/pkg/client/fleetmanager/auth_token_common.go b/pkg/client/fleetmanager/impl/auth_token_common.go similarity index 92% rename from pkg/client/fleetmanager/auth_token_common.go rename to pkg/client/fleetmanager/impl/auth_token_common.go index 6e08594d19..70c9cf2f62 100644 --- a/pkg/client/fleetmanager/auth_token_common.go +++ b/pkg/client/fleetmanager/impl/auth_token_common.go @@ -1,4 +1,4 @@ -package fleetmanager +package impl import ( "fmt" diff --git a/pkg/client/fleetmanager/impl/client.go b/pkg/client/fleetmanager/impl/client.go new file mode 100644 index 0000000000..2a468ee36e --- /dev/null +++ b/pkg/client/fleetmanager/impl/client.go @@ -0,0 +1,127 @@ +package impl + +import ( + "net/http" + "net/url" + + "github.com/pkg/errors" + admin "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/admin/private" + "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/private" + "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/public" + fleetmanager "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager" +) + +var ( + _ http.RoundTripper = (*authTransport)(nil) + _ fleetmanager.PublicAPI = (*publicAPIDelegate)(nil) + _ fleetmanager.PrivateAPI = (*privateAPIDelegate)(nil) + _ fleetmanager.AdminAPI = (*adminAPIDelegate)(nil) +) + +type publicAPIDelegate struct { + *public.DefaultApiService +} + +type privateAPIDelegate struct { + *private.AgentClustersApiService +} + +type adminAPIDelegate struct { + *admin.DefaultApiService +} + +type authTransport struct { + transport http.RoundTripper + auth Auth +} + +func (c *authTransport) RoundTrip(req *http.Request) (*http.Response, error) { + if err := c.auth.AddAuth(req); err != nil { + return nil, errors.Wrapf(err, "setting auth on req %+v", req) + } + return c.transport.RoundTrip(req) +} + +// newAuthTransport creates a http.RoundTripper that wraps http.DefaultTransport and injects +// the authorization header from Auth into any request. +func newAuthTransport(auth Auth) *authTransport { + return &authTransport{ + transport: http.DefaultTransport, + auth: auth, + } +} + +// ClientOption to configure the Client. +type ClientOption func(*options) + +// WithDebugEnabled enables the debug logging for API request sent and received from fleet manager. +// Internally, this will use httputil.DumpRequestOut/DumpResponse. +func WithDebugEnabled() ClientOption { + return func(o *options) { + o.debug = true + } +} + +// WithUserAgent allows to set a custom value that shall be used as the User-Agent header +// when sending requests. +func WithUserAgent(userAgent string) ClientOption { + return func(o *options) { + o.userAgent = userAgent + } +} + +type options struct { + debug bool + userAgent string +} + +func defaultOptions() *options { + return &options{ + debug: false, + userAgent: "OpenAPI-Generator/1.0.0/go", + } +} + +// NewClient creates a new fleet manager client with the specified auth type. +// The client will be able to talk to the three different API groups of fleet manager: public, private, admin. +func NewClient(endpoint string, auth Auth, opts ...ClientOption) (*fleetmanager.Client, error) { + if _, err := url.Parse(endpoint); err != nil { + return nil, errors.Wrapf(err, "parsing endpoint %q as URL", endpoint) + } + + o := defaultOptions() + for _, opt := range opts { + opt(o) + } + + httpClient := &http.Client{ + Transport: newAuthTransport(auth), + } + + publicAPI := &publicAPIDelegate{ + DefaultApiService: public.NewAPIClient(&public.Configuration{ + BasePath: endpoint, + UserAgent: o.userAgent, + Debug: o.debug, + HTTPClient: httpClient, + }).DefaultApi, + } + privateAPI := &privateAPIDelegate{ + AgentClustersApiService: private.NewAPIClient(&private.Configuration{ + BasePath: endpoint, + UserAgent: o.userAgent, + Debug: o.debug, + HTTPClient: httpClient, + }).AgentClustersApi, + } + adminAPI := &adminAPIDelegate{ + DefaultApiService: admin.NewAPIClient(&admin.Configuration{ + BasePath: endpoint, + UserAgent: o.userAgent, + Debug: o.debug, + HTTPClient: httpClient, + }).DefaultApi, + } + + return fleetmanager.MakeClient(publicAPI, privateAPI, adminAPI), nil +} diff --git a/pkg/client/fleetmanager/client_mock.go b/pkg/client/fleetmanager/mocks/client_mock.go similarity index 63% rename from pkg/client/fleetmanager/client_mock.go rename to pkg/client/fleetmanager/mocks/client_mock.go index cb66ccd105..a672a27421 100644 --- a/pkg/client/fleetmanager/client_mock.go +++ b/pkg/client/fleetmanager/mocks/client_mock.go @@ -1,4 +1,8 @@ -package fleetmanager +package mocks + +import ( + fleetmanager "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager" +) // ClientMock API mocks holder. type ClientMock struct { @@ -17,10 +21,6 @@ func NewClientMock() *ClientMock { } // Client returns new Client instance -func (m *ClientMock) Client() *Client { - return &Client{ - privateAPI: m.PrivateAPIMock, - publicAPI: m.PublicAPIMock, - adminAPI: m.AdminAPIMock, - } +func (m *ClientMock) Client() *fleetmanager.Client { + return fleetmanager.MakeClient(m.PublicAPIMock, m.PrivateAPIMock, m.AdminAPIMock) } diff --git a/pkg/client/fleetmanager/api_moq.go b/pkg/client/fleetmanager/mocks/client_moq.go similarity index 96% rename from pkg/client/fleetmanager/api_moq.go rename to pkg/client/fleetmanager/mocks/client_moq.go index 508c8d48f4..a867684f31 100644 --- a/pkg/client/fleetmanager/api_moq.go +++ b/pkg/client/fleetmanager/mocks/client_moq.go @@ -1,26 +1,27 @@ // Code generated by moq; DO NOT EDIT. // github.com/matryer/moq -package fleetmanager +package mocks import ( "context" admin "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/admin/private" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/private" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/public" + "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager" "net/http" "sync" ) -// Ensure, that PublicAPIMock does implement PublicAPI. +// Ensure, that PublicAPIMock does implement fleetmanager.PublicAPI. // If this is not the case, regenerate this file with moq. -var _ PublicAPI = &PublicAPIMock{} +var _ fleetmanager.PublicAPI = &PublicAPIMock{} -// PublicAPIMock is a mock implementation of PublicAPI. +// PublicAPIMock is a mock implementation of fleetmanager.PublicAPI. // // func TestSomethingThatUsesPublicAPI(t *testing.T) { // -// // make and configure a mocked PublicAPI +// // make and configure a mocked fleetmanager.PublicAPI // mockedPublicAPI := &PublicAPIMock{ // CreateCentralFunc: func(ctx context.Context, async bool, request public.CentralRequestPayload) (public.CentralRequest, *http.Response, error) { // panic("mock out the CreateCentral method") @@ -36,7 +37,7 @@ var _ PublicAPI = &PublicAPIMock{} // }, // } // -// // use mockedPublicAPI in code that requires PublicAPI +// // use mockedPublicAPI in code that requires fleetmanager.PublicAPI // // and then make assertions. // // } @@ -246,15 +247,15 @@ func (mock *PublicAPIMock) GetCentralsCalls() []struct { return calls } -// Ensure, that PrivateAPIMock does implement PrivateAPI. +// Ensure, that PrivateAPIMock does implement fleetmanager.PrivateAPI. // If this is not the case, regenerate this file with moq. -var _ PrivateAPI = &PrivateAPIMock{} +var _ fleetmanager.PrivateAPI = &PrivateAPIMock{} -// PrivateAPIMock is a mock implementation of PrivateAPI. +// PrivateAPIMock is a mock implementation of fleetmanager.PrivateAPI. // // func TestSomethingThatUsesPrivateAPI(t *testing.T) { // -// // make and configure a mocked PrivateAPI +// // make and configure a mocked fleetmanager.PrivateAPI // mockedPrivateAPI := &PrivateAPIMock{ // GetCentralFunc: func(ctx context.Context, centralID string) (private.ManagedCentral, *http.Response, error) { // panic("mock out the GetCentral method") @@ -270,7 +271,7 @@ var _ PrivateAPI = &PrivateAPIMock{} // }, // } // -// // use mockedPrivateAPI in code that requires PrivateAPI +// // use mockedPrivateAPI in code that requires fleetmanager.PrivateAPI // // and then make assertions. // // } @@ -480,15 +481,15 @@ func (mock *PrivateAPIMock) UpdateCentralClusterStatusCalls() []struct { return calls } -// Ensure, that AdminAPIMock does implement AdminAPI. +// Ensure, that AdminAPIMock does implement fleetmanager.AdminAPI. // If this is not the case, regenerate this file with moq. -var _ AdminAPI = &AdminAPIMock{} +var _ fleetmanager.AdminAPI = &AdminAPIMock{} -// AdminAPIMock is a mock implementation of AdminAPI. +// AdminAPIMock is a mock implementation of fleetmanager.AdminAPI. // // func TestSomethingThatUsesAdminAPI(t *testing.T) { // -// // make and configure a mocked AdminAPI +// // make and configure a mocked fleetmanager.AdminAPI // mockedAdminAPI := &AdminAPIMock{ // CentralRotateSecretsFunc: func(ctx context.Context, id string, centralRotateSecretsRequest admin.CentralRotateSecretsRequest) (*http.Response, error) { // panic("mock out the CentralRotateSecrets method") @@ -507,7 +508,7 @@ var _ AdminAPI = &AdminAPIMock{} // }, // } // -// // use mockedAdminAPI in code that requires AdminAPI +// // use mockedAdminAPI in code that requires fleetmanager.AdminAPI // // and then make assertions. // // } diff --git a/pkg/client/observatorium/api_mock.go b/pkg/client/observatorium/api_mock.go index ee0b7c5205..ea97ababea 100644 --- a/pkg/client/observatorium/api_mock.go +++ b/pkg/client/observatorium/api_mock.go @@ -7,16 +7,12 @@ import ( 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" ) -// API an alias for pV1.API -// -//go:generate moq -out api_moq.go . API -type API = pV1.API - // MockAPI returns a mocked instance of pV1.API func (c *Client) MockAPI() pV1.API { - return &APIMock{ + 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 diff --git a/pkg/client/observatorium/api_moq.go b/pkg/client/observatorium/mocks/api_moq.go similarity index 85% rename from pkg/client/observatorium/api_moq.go rename to pkg/client/observatorium/mocks/api_moq.go index 639511e360..a48eaf95c0 100644 --- a/pkg/client/observatorium/api_moq.go +++ b/pkg/client/observatorium/mocks/api_moq.go @@ -1,12 +1,12 @@ // Code generated by moq; DO NOT EDIT. // github.com/matryer/moq -package observatorium +package mocks import ( "context" - prom_v1 "github.com/prometheus/client_golang/api/prometheus/v1" - pModel "github.com/prometheus/common/model" + pV1 "github.com/prometheus/client_golang/api/prometheus/v1" + "github.com/prometheus/common/model" "sync" "time" ) @@ -21,67 +21,67 @@ var _ API = &APIMock{} // // // make and configure a mocked API // mockedAPI := &APIMock{ -// AlertManagersFunc: func(ctx context.Context) (prom_v1.AlertManagersResult, error) { +// AlertManagersFunc: func(ctx context.Context) (pV1.AlertManagersResult, error) { // panic("mock out the AlertManagers method") // }, -// AlertsFunc: func(ctx context.Context) (prom_v1.AlertsResult, error) { +// AlertsFunc: func(ctx context.Context) (pV1.AlertsResult, error) { // panic("mock out the Alerts method") // }, -// BuildinfoFunc: func(ctx context.Context) (prom_v1.BuildinfoResult, error) { +// 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) (prom_v1.ConfigResult, error) { +// 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) (prom_v1.FlagsResult, error) { +// 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, prom_v1.Warnings, error) { +// 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) (pModel.LabelValues, prom_v1.Warnings, error) { +// 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][]prom_v1.Metadata, error) { +// 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 ...prom_v1.Option) (pModel.Value, prom_v1.Warnings, error) { +// 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) ([]prom_v1.ExemplarQueryResult, error) { +// 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 prom_v1.Range, opts ...prom_v1.Option) (pModel.Value, prom_v1.Warnings, error) { +// 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) (prom_v1.RulesResult, error) { +// RulesFunc: func(ctx context.Context) (pV1.RulesResult, error) { // panic("mock out the Rules method") // }, -// RuntimeinfoFunc: func(ctx context.Context) (prom_v1.RuntimeinfoResult, error) { +// 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) ([]pModel.LabelSet, prom_v1.Warnings, error) { +// 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) (prom_v1.SnapshotResult, error) { +// SnapshotFunc: func(ctx context.Context, skipHead bool) (pV1.SnapshotResult, error) { // panic("mock out the Snapshot method") // }, -// TSDBFunc: func(ctx context.Context) (prom_v1.TSDBResult, error) { +// TSDBFunc: func(ctx context.Context) (pV1.TSDBResult, error) { // panic("mock out the TSDB method") // }, -// TargetsFunc: func(ctx context.Context) (prom_v1.TargetsResult, error) { +// 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) ([]prom_v1.MetricMetadata, error) { +// 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) (prom_v1.WalReplayStatus, error) { +// WalReplayFunc: func(ctx context.Context) (pV1.WalReplayStatus, error) { // panic("mock out the WalReplay method") // }, // } @@ -92,67 +92,67 @@ var _ API = &APIMock{} // } type APIMock struct { // AlertManagersFunc mocks the AlertManagers method. - AlertManagersFunc func(ctx context.Context) (prom_v1.AlertManagersResult, error) + AlertManagersFunc func(ctx context.Context) (pV1.AlertManagersResult, error) // AlertsFunc mocks the Alerts method. - AlertsFunc func(ctx context.Context) (prom_v1.AlertsResult, error) + AlertsFunc func(ctx context.Context) (pV1.AlertsResult, error) // BuildinfoFunc mocks the Buildinfo method. - BuildinfoFunc func(ctx context.Context) (prom_v1.BuildinfoResult, error) + 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) (prom_v1.ConfigResult, error) + 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) (prom_v1.FlagsResult, error) + 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, prom_v1.Warnings, error) + 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) (pModel.LabelValues, prom_v1.Warnings, error) + 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][]prom_v1.Metadata, error) + 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 ...prom_v1.Option) (pModel.Value, prom_v1.Warnings, error) + 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) ([]prom_v1.ExemplarQueryResult, error) + 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 prom_v1.Range, opts ...prom_v1.Option) (pModel.Value, prom_v1.Warnings, error) + 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) (prom_v1.RulesResult, error) + RulesFunc func(ctx context.Context) (pV1.RulesResult, error) // RuntimeinfoFunc mocks the Runtimeinfo method. - RuntimeinfoFunc func(ctx context.Context) (prom_v1.RuntimeinfoResult, error) + 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) ([]pModel.LabelSet, prom_v1.Warnings, error) + 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) (prom_v1.SnapshotResult, error) + SnapshotFunc func(ctx context.Context, skipHead bool) (pV1.SnapshotResult, error) // TSDBFunc mocks the TSDB method. - TSDBFunc func(ctx context.Context) (prom_v1.TSDBResult, error) + TSDBFunc func(ctx context.Context) (pV1.TSDBResult, error) // TargetsFunc mocks the Targets method. - TargetsFunc func(ctx context.Context) (prom_v1.TargetsResult, error) + TargetsFunc func(ctx context.Context) (pV1.TargetsResult, error) // TargetsMetadataFunc mocks the TargetsMetadata method. - TargetsMetadataFunc func(ctx context.Context, matchTarget string, metric string, limit string) ([]prom_v1.MetricMetadata, error) + TargetsMetadataFunc func(ctx context.Context, matchTarget string, metric string, limit string) ([]pV1.MetricMetadata, error) // WalReplayFunc mocks the WalReplay method. - WalReplayFunc func(ctx context.Context) (prom_v1.WalReplayStatus, error) + WalReplayFunc func(ctx context.Context) (pV1.WalReplayStatus, error) // calls tracks calls to the methods. calls struct { @@ -239,7 +239,7 @@ type APIMock struct { // Ts is the ts argument value. Ts time.Time // Opts is the opts argument value. - Opts []prom_v1.Option + Opts []pV1.Option } // QueryExemplars holds details about calls to the QueryExemplars method. QueryExemplars []struct { @@ -259,9 +259,9 @@ type APIMock struct { // Query is the query argument value. Query string // R is the r argument value. - R prom_v1.Range + R pV1.Range // Opts is the opts argument value. - Opts []prom_v1.Option + Opts []pV1.Option } // Rules holds details about calls to the Rules method. Rules []struct { @@ -342,7 +342,7 @@ type APIMock struct { } // AlertManagers calls AlertManagersFunc. -func (mock *APIMock) AlertManagers(ctx context.Context) (prom_v1.AlertManagersResult, error) { +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") } @@ -374,7 +374,7 @@ func (mock *APIMock) AlertManagersCalls() []struct { } // Alerts calls AlertsFunc. -func (mock *APIMock) Alerts(ctx context.Context) (prom_v1.AlertsResult, error) { +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") } @@ -406,7 +406,7 @@ func (mock *APIMock) AlertsCalls() []struct { } // Buildinfo calls BuildinfoFunc. -func (mock *APIMock) Buildinfo(ctx context.Context) (prom_v1.BuildinfoResult, error) { +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") } @@ -470,7 +470,7 @@ func (mock *APIMock) CleanTombstonesCalls() []struct { } // Config calls ConfigFunc. -func (mock *APIMock) Config(ctx context.Context) (prom_v1.ConfigResult, error) { +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") } @@ -546,7 +546,7 @@ func (mock *APIMock) DeleteSeriesCalls() []struct { } // Flags calls FlagsFunc. -func (mock *APIMock) Flags(ctx context.Context) (prom_v1.FlagsResult, error) { +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") } @@ -578,7 +578,7 @@ func (mock *APIMock) FlagsCalls() []struct { } // LabelNames calls LabelNamesFunc. -func (mock *APIMock) LabelNames(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]string, prom_v1.Warnings, error) { +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") } @@ -622,7 +622,7 @@ func (mock *APIMock) LabelNamesCalls() []struct { } // LabelValues calls LabelValuesFunc. -func (mock *APIMock) LabelValues(ctx context.Context, label string, matches []string, startTime time.Time, endTime time.Time) (pModel.LabelValues, prom_v1.Warnings, error) { +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") } @@ -670,7 +670,7 @@ func (mock *APIMock) LabelValuesCalls() []struct { } // Metadata calls MetadataFunc. -func (mock *APIMock) Metadata(ctx context.Context, metric string, limit string) (map[string][]prom_v1.Metadata, error) { +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") } @@ -710,7 +710,7 @@ func (mock *APIMock) MetadataCalls() []struct { } // Query calls QueryFunc. -func (mock *APIMock) Query(ctx context.Context, query string, ts time.Time, opts ...prom_v1.Option) (pModel.Value, prom_v1.Warnings, error) { +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") } @@ -718,7 +718,7 @@ func (mock *APIMock) Query(ctx context.Context, query string, ts time.Time, opts Ctx context.Context Query string Ts time.Time - Opts []prom_v1.Option + Opts []pV1.Option }{ Ctx: ctx, Query: query, @@ -739,13 +739,13 @@ func (mock *APIMock) QueryCalls() []struct { Ctx context.Context Query string Ts time.Time - Opts []prom_v1.Option + Opts []pV1.Option } { var calls []struct { Ctx context.Context Query string Ts time.Time - Opts []prom_v1.Option + Opts []pV1.Option } mock.lockQuery.RLock() calls = mock.calls.Query @@ -754,7 +754,7 @@ func (mock *APIMock) QueryCalls() []struct { } // QueryExemplars calls QueryExemplarsFunc. -func (mock *APIMock) QueryExemplars(ctx context.Context, query string, startTime time.Time, endTime time.Time) ([]prom_v1.ExemplarQueryResult, error) { +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") } @@ -798,15 +798,15 @@ func (mock *APIMock) QueryExemplarsCalls() []struct { } // QueryRange calls QueryRangeFunc. -func (mock *APIMock) QueryRange(ctx context.Context, query string, r prom_v1.Range, opts ...prom_v1.Option) (pModel.Value, prom_v1.Warnings, error) { +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 prom_v1.Range - Opts []prom_v1.Option + R pV1.Range + Opts []pV1.Option }{ Ctx: ctx, Query: query, @@ -826,14 +826,14 @@ func (mock *APIMock) QueryRange(ctx context.Context, query string, r prom_v1.Ran func (mock *APIMock) QueryRangeCalls() []struct { Ctx context.Context Query string - R prom_v1.Range - Opts []prom_v1.Option + R pV1.Range + Opts []pV1.Option } { var calls []struct { Ctx context.Context Query string - R prom_v1.Range - Opts []prom_v1.Option + R pV1.Range + Opts []pV1.Option } mock.lockQueryRange.RLock() calls = mock.calls.QueryRange @@ -842,7 +842,7 @@ func (mock *APIMock) QueryRangeCalls() []struct { } // Rules calls RulesFunc. -func (mock *APIMock) Rules(ctx context.Context) (prom_v1.RulesResult, error) { +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") } @@ -874,7 +874,7 @@ func (mock *APIMock) RulesCalls() []struct { } // Runtimeinfo calls RuntimeinfoFunc. -func (mock *APIMock) Runtimeinfo(ctx context.Context) (prom_v1.RuntimeinfoResult, error) { +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") } @@ -906,7 +906,7 @@ func (mock *APIMock) RuntimeinfoCalls() []struct { } // Series calls SeriesFunc. -func (mock *APIMock) Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]pModel.LabelSet, prom_v1.Warnings, error) { +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") } @@ -950,7 +950,7 @@ func (mock *APIMock) SeriesCalls() []struct { } // Snapshot calls SnapshotFunc. -func (mock *APIMock) Snapshot(ctx context.Context, skipHead bool) (prom_v1.SnapshotResult, error) { +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") } @@ -986,7 +986,7 @@ func (mock *APIMock) SnapshotCalls() []struct { } // TSDB calls TSDBFunc. -func (mock *APIMock) TSDB(ctx context.Context) (prom_v1.TSDBResult, error) { +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") } @@ -1018,7 +1018,7 @@ func (mock *APIMock) TSDBCalls() []struct { } // Targets calls TargetsFunc. -func (mock *APIMock) Targets(ctx context.Context) (prom_v1.TargetsResult, error) { +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") } @@ -1050,7 +1050,7 @@ func (mock *APIMock) TargetsCalls() []struct { } // TargetsMetadata calls TargetsMetadataFunc. -func (mock *APIMock) TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]prom_v1.MetricMetadata, error) { +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") } @@ -1094,7 +1094,7 @@ func (mock *APIMock) TargetsMetadataCalls() []struct { } // WalReplay calls WalReplayFunc. -func (mock *APIMock) WalReplay(ctx context.Context) (prom_v1.WalReplayStatus, error) { +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") } diff --git a/pkg/client/observatorium/mocks/gen.go b/pkg/client/observatorium/mocks/gen.go new file mode 100644 index 0000000000..e7db2afd39 --- /dev/null +++ b/pkg/client/observatorium/mocks/gen.go @@ -0,0 +1,8 @@ +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/ocm/client.go b/pkg/client/ocm/client.go index 43c901d74d..b18043ef82 100644 --- a/pkg/client/ocm/client.go +++ b/pkg/client/ocm/client.go @@ -1,33 +1,16 @@ -// Package ocm ... package ocm import ( - "fmt" - "net/http" - - "github.com/golang/glog" sdkClient "github.com/openshift-online/ocm-sdk-go" amsv1 "github.com/openshift-online/ocm-sdk-go/accountsmgmt/v1" addonsmgmtv1 "github.com/openshift-online/ocm-sdk-go/addonsmgmt/v1" - v1 "github.com/openshift-online/ocm-sdk-go/authorizations/v1" clustersmgmtv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" - "github.com/openshift-online/ocm-sdk-go/logging" - pkgerrors "github.com/pkg/errors" serviceErrors "github.com/stackrox/acs-fleet-manager/pkg/errors" ) -// TermsSitecode ... -const TermsSitecode = "OCM" - -// TermsEventcodeOnlineService ... -const TermsEventcodeOnlineService = "onlineService" +//go:generate moq -rm -out mocks/client_moq.go -pkg mocks . Client -// TermsEventcodeRegister ... -const TermsEventcodeRegister = "register" - -// Client ... -// -//go:generate moq -out client_moq.go . Client +// Client is an interface to OCM type Client interface { CreateCluster(cluster *clustersmgmtv1.Cluster) (*clustersmgmtv1.Cluster, error) GetClusterIngresses(clusterID string) (*clustersmgmtv1.IngressesListResponse, error) @@ -56,534 +39,3 @@ type Client interface { // GetCurrentAccount returns the account information of the user to whom belongs the token GetCurrentAccount(userToken string) (int, *amsv1.Account, error) } - -var _ Client = &client{} - -type client struct { - connection *sdkClient.Connection -} - -// AMSClient ... -type AMSClient Client - -// ClusterManagementClient ... -type ClusterManagementClient Client - -// NewOCMConnection ... -func NewOCMConnection(ocmConfig *OCMConfig, baseURL string) (*sdkClient.Connection, func(), error) { - if ocmConfig.EnableMock && ocmConfig.MockMode != MockModeEmulateServer { - return nil, func() {}, nil - } - - builder := getBaseConnectionBuilder(baseURL) - if !ocmConfig.EnableMock { - // Create a logger that has the debug level enabled: - logger, err := getLogger(ocmConfig.Debug) - if err != nil { - return nil, nil, err - } - builder = builder.Logger(logger) - } - - if ocmConfig.ClientID != "" && ocmConfig.ClientSecret != "" { - builder = builder.Client(ocmConfig.ClientID, ocmConfig.ClientSecret) - } else if ocmConfig.SelfToken != "" { - builder = builder.Tokens(ocmConfig.SelfToken) - } else { - return nil, nil, pkgerrors.New("Can't build OCM client connection. No Client/Secret or Token has been provided.") - } - - connection, err := builder.Build() - if err != nil { - return nil, nil, fmt.Errorf("building OCM client connection: %w", err) - } - return connection, func() { - _ = connection.Close() - }, nil -} - -func getBaseConnectionBuilder(baseURL string) *sdkClient.ConnectionBuilder { - return sdkClient.NewConnectionBuilder(). - URL(baseURL). - MetricsSubsystem("api_outbound") -} - -func getLogger(isDebugEnabled bool) (*logging.GoLogger, error) { - logger, err := sdkClient.NewGoLoggerBuilder(). - Debug(isDebugEnabled). - Build() - if err != nil { - return nil, fmt.Errorf("creating logger for OCM client connection: %w", err) - } - return logger, nil -} - -// NewClient ... -func NewClient(connection *sdkClient.Connection) Client { - return &client{connection: connection} -} - -// NewMockClient returns a new OCM client with stubbed responses. -func NewMockClient() Client { - return &ClientMock{ - GetOrganisationFromExternalIDFunc: func(externalID string) (*amsv1.Organization, error) { - org, err := amsv1.NewOrganization(). - ID("12345678"). - Name("stubbed-name"). - Build() - return org, pkgerrors.Wrap(err, "failed to build organisation") - }, - } -} - -// Connection ... -func (c *client) Connection() *sdkClient.Connection { - return c.connection -} - -// Close ... -func (c *client) Close() { - if c.connection != nil { - _ = c.connection.Close() - } -} - -// CreateCluster ... -func (c *client) CreateCluster(cluster *clustersmgmtv1.Cluster) (*clustersmgmtv1.Cluster, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - clusterResource := c.connection.ClustersMgmt().V1().Clusters() - response, err := clusterResource.Add().Body(cluster).Send() - if err != nil { - return &clustersmgmtv1.Cluster{}, serviceErrors.New(serviceErrors.ErrorGeneral, err.Error()) - } - createdCluster := response.Body() - - return createdCluster, nil -} - -// GetExistingClusterMetrics ... -func (c *client) GetExistingClusterMetrics(clusterID string) (*amsv1.SubscriptionMetrics, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - subscriptions, err := c.connection.AccountsMgmt().V1().Subscriptions().List().Search(fmt.Sprintf("cluster_id='%s'", clusterID)).Send() - if err != nil { - return nil, fmt.Errorf("retrieving subscriptions: %w", err) - } - items := subscriptions.Items() - if items == nil || items.Len() == 0 { - return nil, nil - } - - if items.Len() > 1 { - return nil, fmt.Errorf("expected 1 subscription item, found %d", items.Len()) - } - subscriptionsMetrics := subscriptions.Items().Get(0).Metrics() - if len(subscriptionsMetrics) > 1 { - // this should never happen: https://github.com/openshift-online/ocm-api-model/blob/9ca12df7763723903c0d1cd87e993995a2acda5f/model/accounts_mgmt/v1/subscription_type.model#L49-L50 - return nil, fmt.Errorf("expected 1 subscription metric, found %d", len(subscriptionsMetrics)) - } - - if len(subscriptionsMetrics) == 0 { - return nil, nil - } - - return subscriptionsMetrics[0], nil -} - -// GetOrganisationFromExternalID takes the external org id as input, and returns the OCM org. -func (c *client) GetOrganisationFromExternalID(externalID string) (*amsv1.Organization, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - request := c.connection.AccountsMgmt().V1().Organizations().List().Search(fmt.Sprintf("external_id='%s'", externalID)) - res, err := request.Send() - if err != nil { - return nil, fmt.Errorf("retrieving organizations: %w", err) - } - - items := res.Items() - if items.Len() < 1 { - return nil, serviceErrors.New(serviceErrors.ErrorNotFound, "organisation with external id '%s' not found", externalID) - } - - return items.Get(0), nil -} - -// GetRequiresTermsAcceptance ... -func (c *client) GetRequiresTermsAcceptance(username string) (termsRequired bool, redirectURL string, err error) { - if c.connection == nil { - return false, "", serviceErrors.InvalidOCMConnection() - } - - // Check for Appendix 4 Terms - request, err := v1.NewTermsReviewRequest().AccountUsername(username).SiteCode(TermsSitecode).EventCode(TermsEventcodeRegister).Build() - if err != nil { - return false, "", fmt.Errorf("creating terms review request: %w", err) - } - selfTermsReview := c.connection.Authorizations().V1().TermsReview() - postResp, err := selfTermsReview.Post().Request(request).Send() - if err != nil { - return false, "", fmt.Errorf("getting terms review: %w", err) - } - response, ok := postResp.GetResponse() - if !ok { - return false, "", fmt.Errorf("empty response from authorization post request") - } - - redirectURL, _ = response.GetRedirectUrl() - - return response.TermsRequired(), redirectURL, nil -} - -// GetClusterIngresses sends a GET request to ocm to retrieve the ingresses of an OSD cluster -func (c *client) GetClusterIngresses(clusterID string) (*clustersmgmtv1.IngressesListResponse, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - clusterIngresses := c.connection.ClustersMgmt().V1().Clusters().Cluster(clusterID).Ingresses() - ingressList, err := clusterIngresses.List().Send() - if err != nil { - return nil, fmt.Errorf("sending cluster ingresses list request: %w", err) - } - - return ingressList, nil -} - -// GetCluster ... -func (c *client) GetCluster(clusterID string) (*clustersmgmtv1.Cluster, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - resp, err := c.connection.ClustersMgmt().V1().Clusters().Cluster(clusterID).Get().Send() - if err != nil { - return nil, fmt.Errorf("sending get cluster request: %w", err) - } - return resp.Body(), nil -} - -// GetClusterStatus ... -func (c *client) GetClusterStatus(id string) (*clustersmgmtv1.ClusterStatus, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - resp, err := c.connection.ClustersMgmt().V1().Clusters().Cluster(id).Status().Get().Send() - if err != nil { - return nil, fmt.Errorf("sending cluster status request: %w", err) - } - return resp.Body(), nil -} - -// GetCloudProviders ... -func (c *client) GetCloudProviders() (*clustersmgmtv1.CloudProviderList, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - providersCollection := c.connection.ClustersMgmt().V1().CloudProviders() - providersResponse, err := providersCollection.List().Send() - if err != nil { - return nil, pkgerrors.Wrap(err, "error retrieving cloud provider list") - } - cloudProviderList := providersResponse.Items() - return cloudProviderList, nil -} - -// GetRegions ... -func (c *client) GetRegions(provider *clustersmgmtv1.CloudProvider) (*clustersmgmtv1.CloudRegionList, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - regionsCollection := c.connection.ClustersMgmt().V1().CloudProviders().CloudProvider(provider.ID()).Regions() - regionsResponse, err := regionsCollection.List().Send() - if err != nil { - return nil, pkgerrors.Wrap(err, "error retrieving cloud region list") - } - - regionList := regionsResponse.Items() - return regionList, nil -} - -func (c *client) GetAddon(addonID string) (*addonsmgmtv1.Addon, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - resp, err := c.connection.AddonsMgmt().V1().Addons().Addon(addonID).Get().Send() - if err != nil { - if resp != nil && resp.Status() == http.StatusNotFound { - return nil, serviceErrors.NotFound("") - } - return nil, serviceErrors.GeneralError("sending GetAddon request: %v", err) - } - - return resp.Body(), nil -} - -func (c *client) GetAddonVersion(addonID string, versionID string) (*addonsmgmtv1.AddonVersion, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - resp, err := c.connection.AddonsMgmt().V1().Addons().Addon(addonID).Versions().Version(versionID).Get().Send() - if err != nil { - if resp != nil && resp.Status() == http.StatusNotFound { - return nil, serviceErrors.NotFound("") - } - return nil, serviceErrors.GeneralError("sending GetAddonVersion request: %v", err) - } - - return resp.Body(), nil -} - -// CreateAddonInstallation creates a new addon for a cluster with given ID -func (c *client) CreateAddonInstallation(clusterID string, addon *clustersmgmtv1.AddOnInstallation) error { - if c.connection == nil { - return serviceErrors.InvalidOCMConnection() - } - _, err := c.connection.ClustersMgmt().V1().Clusters().Cluster(clusterID).Addons().Add().Body(addon).Send() - if err != nil { - return fmt.Errorf("sending CreateAddonInstallation request: %w", err) - } - return nil -} - -// GetAddonInstallation returns the addon installed on a cluster with given ID -func (c *client) GetAddonInstallation(clusterID string, addonID string) (*clustersmgmtv1.AddOnInstallation, *serviceErrors.ServiceError) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - resp, err := c.connection.ClustersMgmt().V1().Clusters().Cluster(clusterID).Addons().Addoninstallation(addonID).Get().Send() - if err != nil { - if resp != nil && resp.Status() == http.StatusNotFound { - return nil, serviceErrors.NotFound("") - } - return nil, serviceErrors.GeneralError("sending GetAddonInstallation request: %v", err) - } - - return resp.Body(), nil -} - -// UpdateAddonInstallation updates the existing addon on a cluster with given ID -func (c *client) UpdateAddonInstallation(clusterID string, addonInstallation *clustersmgmtv1.AddOnInstallation) error { - if c.connection == nil { - return serviceErrors.InvalidOCMConnection() - } - _, err := c.connection.ClustersMgmt().V1().Clusters().Cluster(clusterID).Addons().Addoninstallation(addonInstallation.ID()).Update().Body(addonInstallation).Send() - if err != nil { - return fmt.Errorf("sending UpdateAddonInstallation request: %w", err) - } - return nil -} - -// DeleteAddonInstallation deletes the addon on a cluster with given ID -func (c *client) DeleteAddonInstallation(clusterID string, addonInstallationID string) error { - if c.connection == nil { - return serviceErrors.InvalidOCMConnection() - } - _, err := c.connection.ClustersMgmt().V1().Clusters().Cluster(clusterID).Addons().Addoninstallation(addonInstallationID).Delete().Send() - if err != nil { - return fmt.Errorf("sending DeleteAddonInstallation request: %w", err) - } - return nil -} - -// GetClusterDNS ... -func (c *client) GetClusterDNS(clusterID string) (string, error) { - if clusterID == "" { - return "", serviceErrors.Validation("clusterID cannot be empty") - } - ingresses, err := c.GetClusterIngresses(clusterID) - if err != nil { - return "", err - } - - var clusterDNS string - ingresses.Items().Each(func(ingress *clustersmgmtv1.Ingress) bool { - if ingress.Default() { - clusterDNS = ingress.DNSName() - return false - } - return true - }) - - if clusterDNS == "" { - return "", serviceErrors.NotFound("Cluster %s: DNS is empty", clusterID) - } - - return clusterDNS, nil -} - -// CreateIdentityProvider ... -func (c *client) CreateIdentityProvider(clusterID string, identityProvider *clustersmgmtv1.IdentityProvider) (*clustersmgmtv1.IdentityProvider, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - clustersResource := c.connection.ClustersMgmt().V1().Clusters() - response, identityProviderErr := clustersResource.Cluster(clusterID). - IdentityProviders(). - Add(). - Body(identityProvider). - Send() - var err error - if identityProviderErr != nil { - err = serviceErrors.NewErrorFromHTTPStatusCode(response.Status(), "ocm client failed to create identity provider: %s", identityProviderErr) - } - return response.Body(), err -} - -// DeleteCluster ... -func (c *client) DeleteCluster(clusterID string) (int, error) { - if c.connection == nil { - return 0, serviceErrors.InvalidOCMConnection() - } - - clustersResource := c.connection.ClustersMgmt().V1().Clusters() - response, deleteClusterError := clustersResource.Cluster(clusterID).Delete().Send() - - var err error - if deleteClusterError != nil { - err = serviceErrors.NewErrorFromHTTPStatusCode(response.Status(), "OCM client failed to delete cluster '%s': %s", clusterID, deleteClusterError) - } - return response.Status(), err -} - -// ClusterAuthorization ... -func (c *client) ClusterAuthorization(cb *amsv1.ClusterAuthorizationRequest) (*amsv1.ClusterAuthorizationResponse, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - glog.V(10).Infof("Sending request to OCM '%v'", *cb) - - r, err := c.connection.AccountsMgmt().V1(). - ClusterAuthorizations(). - Post().Request(cb).Send() - if err != nil && r.Status() != http.StatusTooManyRequests { - glog.Warningf("OCM client responded with '%v: %v' for request '%v'", r.Status(), err, *cb) - return nil, serviceErrors.NewErrorFromHTTPStatusCode(r.Status(), "OCM client failed to create cluster authorization") - } - resp, _ := r.GetResponse() - return resp, nil -} - -// DeleteSubscription ... -func (c *client) DeleteSubscription(id string) (int, error) { - if c.connection == nil { - return 0, serviceErrors.InvalidOCMConnection() - } - - r := c.connection.AccountsMgmt().V1().Subscriptions().Subscription(id).Delete() - resp, err := r.Send() - return resp.Status(), err -} - -// FindSubscriptions ... -func (c *client) FindSubscriptions(query string) (*amsv1.SubscriptionsListResponse, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - r, err := c.connection.AccountsMgmt().V1().Subscriptions().List().Search(query).Send() - if err != nil { - return nil, fmt.Errorf("querying the accounts management service for subscriptions: %w", err) - } - return r, nil -} - -// GetQuotaCostsForProduct gets the AMS QuotaCosts in the given organizationID -// whose relatedResources contains at least a relatedResource that has the -// given resourceName and product -func (c *client) GetQuotaCostsForProduct(organizationID, resourceName, product string) ([]*amsv1.QuotaCost, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - var res []*amsv1.QuotaCost - organizationClient := c.connection.AccountsMgmt().V1().Organizations() - quotaCostClient := organizationClient.Organization(organizationID).QuotaCost() - - req := quotaCostClient.List().Parameter("fetchRelatedResources", true).Parameter("fetchCloudAccounts", true) - - // TODO: go 1.21 can infer the following generic arguments from req - // automatically, so this indirection becomes unnecessary. - fetchQuotaCosts := fetchPages[*amsv1.QuotaCostListRequest, - *amsv1.QuotaCostListResponse, - *amsv1.QuotaCostList, - *amsv1.QuotaCost, - ] - err := fetchQuotaCosts(req, 100, 1000, func(qc *amsv1.QuotaCost) bool { - relatedResourcesList := qc.RelatedResources() - for _, relatedResource := range relatedResourcesList { - if relatedResource.ResourceName() == resourceName && relatedResource.Product() == product { - res = append(res, qc) - break - } - } - return true - }) - if err != nil { - return nil, pkgerrors.Wrap(err, "error listing QuotaCosts") - } - return res, nil -} - -func (c *client) GetCustomerCloudAccounts(organizationID string, quotaIDs []string) ([]*amsv1.CloudAccount, error) { - if c.connection == nil { - return nil, serviceErrors.InvalidOCMConnection() - } - - var res []*amsv1.CloudAccount - organizationClient := c.connection.AccountsMgmt().V1().Organizations() - quotaCostClient := organizationClient.Organization(organizationID).QuotaCost() - - quotaCostList, err := quotaCostClient.List().Parameter("fetchCloudAccounts", true).Send() - if err != nil { - return nil, fmt.Errorf("error getting cloud accounts: %w", err) - } - - quotaCostList.Items().Each(func(qc *amsv1.QuotaCost) bool { - for _, quotaID := range quotaIDs { - if qc.QuotaID() == quotaID { - res = append(res, qc.CloudAccounts()...) - break - } - } - return true - }) - - return res, nil -} - -// GetCurrentAccount returns the account information of the user to whom belongs the token -func (c *client) GetCurrentAccount(userToken string) (int, *amsv1.Account, error) { - logger, err := getLogger(c.connection.Logger().DebugEnabled()) - if err != nil { - return 0, nil, fmt.Errorf("couldn't create logger for modified OCM connection: %w", err) - } - modifiedConnection, err := getBaseConnectionBuilder(c.connection.URL()). - Logger(logger). - Tokens(userToken). - Build() - if err != nil { - return 0, nil, fmt.Errorf("couldn't build modified OCM connection: %w", err) - } - defer modifiedConnection.Close() - response, err := modifiedConnection.AccountsMgmt().V1().CurrentAccount().Get().Send() - if err != nil { - return response.Status(), nil, fmt.Errorf("unsuccessful call to current account endpoint: %w", err) - } - - currentAccount := response.Body() - return response.Status(), currentAccount, nil -} diff --git a/pkg/client/ocm/id.go b/pkg/client/ocm/id.go index 0bfe5a2a47..c471cd8c14 100644 --- a/pkg/client/ocm/id.go +++ b/pkg/client/ocm/id.go @@ -1,44 +1,12 @@ package ocm -import ( - "fmt" - - "github.com/rs/xid" -) - -// MaxClusterNameLength ... -const ( - // MaxClusterNameLength - defines maximum length of an OSD cluster name - MaxClusterNameLength = 15 -) - // IDGenerator NOTE: the current mock generation exports to a _test file, if in the future this should be made public, consider // moving the type into a ocmtest package. + // IDGenerator interface for string ID generators. // -//go:generate moq -out idgenerator_moq.go . IDGenerator +//go:generate moq -rm -out mocks/id_generator_moq.go -pkg mocks . IDGenerator type IDGenerator interface { // Generate create a new string ID. Generate() string } - -var _ IDGenerator = idGenerator{} - -// idGenerator internal implementation of IDGenerator. -type idGenerator struct { - // prefix a string to prefix to any generated ID. - prefix string -} - -// NewIDGenerator create a new default implementation of IDGenerator. -func NewIDGenerator(prefix string) IDGenerator { - return idGenerator{ - prefix: prefix, - } -} - -// Generate It is not allowed for the cluster name to be longer than 15 characters, hence -// the truncation -func (i idGenerator) Generate() string { - return fmt.Sprintf("%s%s", i.prefix, xid.New().String())[0:MaxClusterNameLength] -} diff --git a/pkg/client/ocm/addon_config.go b/pkg/client/ocm/impl/addon_config.go similarity index 99% rename from pkg/client/ocm/addon_config.go rename to pkg/client/ocm/impl/addon_config.go index 6d63b66b4e..6d4ea239ff 100644 --- a/pkg/client/ocm/addon_config.go +++ b/pkg/client/ocm/impl/addon_config.go @@ -1,4 +1,4 @@ -package ocm +package impl import ( "fmt" diff --git a/pkg/client/ocm/impl/client_impl.go b/pkg/client/ocm/impl/client_impl.go new file mode 100644 index 0000000000..f8c0ca062c --- /dev/null +++ b/pkg/client/ocm/impl/client_impl.go @@ -0,0 +1,559 @@ +// Package ocm ... +package impl + +import ( + "fmt" + "net/http" + + "github.com/golang/glog" + sdkClient "github.com/openshift-online/ocm-sdk-go" + amsv1 "github.com/openshift-online/ocm-sdk-go/accountsmgmt/v1" + addonsmgmtv1 "github.com/openshift-online/ocm-sdk-go/addonsmgmt/v1" + v1 "github.com/openshift-online/ocm-sdk-go/authorizations/v1" + clustersmgmtv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" + "github.com/openshift-online/ocm-sdk-go/logging" + pkgerrors "github.com/pkg/errors" + "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/mocks" + serviceErrors "github.com/stackrox/acs-fleet-manager/pkg/errors" +) + +// TermsSitecode ... +const TermsSitecode = "OCM" + +// TermsEventcodeOnlineService ... +const TermsEventcodeOnlineService = "onlineService" + +// TermsEventcodeRegister ... +const TermsEventcodeRegister = "register" + +var _ ocm.Client = &client{} + +type client struct { + connection *sdkClient.Connection +} + +// AMSClient ... +type AMSClient ocm.Client + +// ClusterManagementClient ... +type ClusterManagementClient ocm.Client + +// NewOCMConnection ... +func NewOCMConnection(ocmConfig *OCMConfig, baseURL string) (*sdkClient.Connection, func(), error) { + if ocmConfig.EnableMock && ocmConfig.MockMode != MockModeEmulateServer { + return nil, func() {}, nil + } + + builder := getBaseConnectionBuilder(baseURL) + if !ocmConfig.EnableMock { + // Create a logger that has the debug level enabled: + logger, err := getLogger(ocmConfig.Debug) + if err != nil { + return nil, nil, err + } + builder = builder.Logger(logger) + } + + if ocmConfig.ClientID != "" && ocmConfig.ClientSecret != "" { + builder = builder.Client(ocmConfig.ClientID, ocmConfig.ClientSecret) + } else if ocmConfig.SelfToken != "" { + builder = builder.Tokens(ocmConfig.SelfToken) + } else { + return nil, nil, pkgerrors.New("Can't build OCM client connection. No Client/Secret or Token has been provided.") + } + + connection, err := builder.Build() + if err != nil { + return nil, nil, fmt.Errorf("building OCM client connection: %w", err) + } + return connection, func() { + _ = connection.Close() + }, nil +} + +func getBaseConnectionBuilder(baseURL string) *sdkClient.ConnectionBuilder { + return sdkClient.NewConnectionBuilder(). + URL(baseURL). + MetricsSubsystem("api_outbound") +} + +func getLogger(isDebugEnabled bool) (*logging.GoLogger, error) { + logger, err := sdkClient.NewGoLoggerBuilder(). + Debug(isDebugEnabled). + Build() + if err != nil { + return nil, fmt.Errorf("creating logger for OCM client connection: %w", err) + } + return logger, nil +} + +// NewClient ... +func NewClient(connection *sdkClient.Connection) ocm.Client { + return &client{connection: connection} +} + +// NewMockClient returns a new OCM client with stubbed responses. +func NewMockClient() ocm.Client { + return &mocks.ClientMock{ + GetOrganisationFromExternalIDFunc: func(externalID string) (*amsv1.Organization, error) { + org, err := amsv1.NewOrganization(). + ID("12345678"). + Name("stubbed-name"). + Build() + return org, pkgerrors.Wrap(err, "failed to build organisation") + }, + } +} + +// Connection ... +func (c *client) Connection() *sdkClient.Connection { + return c.connection +} + +// Close ... +func (c *client) Close() { + if c.connection != nil { + _ = c.connection.Close() + } +} + +// CreateCluster ... +func (c *client) CreateCluster(cluster *clustersmgmtv1.Cluster) (*clustersmgmtv1.Cluster, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + clusterResource := c.connection.ClustersMgmt().V1().Clusters() + response, err := clusterResource.Add().Body(cluster).Send() + if err != nil { + return &clustersmgmtv1.Cluster{}, serviceErrors.New(serviceErrors.ErrorGeneral, err.Error()) + } + createdCluster := response.Body() + + return createdCluster, nil +} + +// GetExistingClusterMetrics ... +func (c *client) GetExistingClusterMetrics(clusterID string) (*amsv1.SubscriptionMetrics, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + subscriptions, err := c.connection.AccountsMgmt().V1().Subscriptions().List().Search(fmt.Sprintf("cluster_id='%s'", clusterID)).Send() + if err != nil { + return nil, fmt.Errorf("retrieving subscriptions: %w", err) + } + items := subscriptions.Items() + if items == nil || items.Len() == 0 { + return nil, nil + } + + if items.Len() > 1 { + return nil, fmt.Errorf("expected 1 subscription item, found %d", items.Len()) + } + subscriptionsMetrics := subscriptions.Items().Get(0).Metrics() + if len(subscriptionsMetrics) > 1 { + // this should never happen: https://github.com/openshift-online/ocm-api-model/blob/9ca12df7763723903c0d1cd87e993995a2acda5f/model/accounts_mgmt/v1/subscription_type.model#L49-L50 + return nil, fmt.Errorf("expected 1 subscription metric, found %d", len(subscriptionsMetrics)) + } + + if len(subscriptionsMetrics) == 0 { + return nil, nil + } + + return subscriptionsMetrics[0], nil +} + +// GetOrganisationFromExternalID takes the external org id as input, and returns the OCM org. +func (c *client) GetOrganisationFromExternalID(externalID string) (*amsv1.Organization, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + request := c.connection.AccountsMgmt().V1().Organizations().List().Search(fmt.Sprintf("external_id='%s'", externalID)) + res, err := request.Send() + if err != nil { + return nil, fmt.Errorf("retrieving organizations: %w", err) + } + + items := res.Items() + if items.Len() < 1 { + return nil, serviceErrors.New(serviceErrors.ErrorNotFound, "organisation with external id '%s' not found", externalID) + } + + return items.Get(0), nil +} + +// GetRequiresTermsAcceptance ... +func (c *client) GetRequiresTermsAcceptance(username string) (termsRequired bool, redirectURL string, err error) { + if c.connection == nil { + return false, "", serviceErrors.InvalidOCMConnection() + } + + // Check for Appendix 4 Terms + request, err := v1.NewTermsReviewRequest().AccountUsername(username).SiteCode(TermsSitecode).EventCode(TermsEventcodeRegister).Build() + if err != nil { + return false, "", fmt.Errorf("creating terms review request: %w", err) + } + selfTermsReview := c.connection.Authorizations().V1().TermsReview() + postResp, err := selfTermsReview.Post().Request(request).Send() + if err != nil { + return false, "", fmt.Errorf("getting terms review: %w", err) + } + response, ok := postResp.GetResponse() + if !ok { + return false, "", fmt.Errorf("empty response from authorization post request") + } + + redirectURL, _ = response.GetRedirectUrl() + + return response.TermsRequired(), redirectURL, nil +} + +// GetClusterIngresses sends a GET request to ocm to retrieve the ingresses of an OSD cluster +func (c *client) GetClusterIngresses(clusterID string) (*clustersmgmtv1.IngressesListResponse, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + clusterIngresses := c.connection.ClustersMgmt().V1().Clusters().Cluster(clusterID).Ingresses() + ingressList, err := clusterIngresses.List().Send() + if err != nil { + return nil, fmt.Errorf("sending cluster ingresses list request: %w", err) + } + + return ingressList, nil +} + +// GetCluster ... +func (c *client) GetCluster(clusterID string) (*clustersmgmtv1.Cluster, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + resp, err := c.connection.ClustersMgmt().V1().Clusters().Cluster(clusterID).Get().Send() + if err != nil { + return nil, fmt.Errorf("sending get cluster request: %w", err) + } + return resp.Body(), nil +} + +// GetClusterStatus ... +func (c *client) GetClusterStatus(id string) (*clustersmgmtv1.ClusterStatus, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + resp, err := c.connection.ClustersMgmt().V1().Clusters().Cluster(id).Status().Get().Send() + if err != nil { + return nil, fmt.Errorf("sending cluster status request: %w", err) + } + return resp.Body(), nil +} + +// GetCloudProviders ... +func (c *client) GetCloudProviders() (*clustersmgmtv1.CloudProviderList, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + providersCollection := c.connection.ClustersMgmt().V1().CloudProviders() + providersResponse, err := providersCollection.List().Send() + if err != nil { + return nil, pkgerrors.Wrap(err, "error retrieving cloud provider list") + } + cloudProviderList := providersResponse.Items() + return cloudProviderList, nil +} + +// GetRegions ... +func (c *client) GetRegions(provider *clustersmgmtv1.CloudProvider) (*clustersmgmtv1.CloudRegionList, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + regionsCollection := c.connection.ClustersMgmt().V1().CloudProviders().CloudProvider(provider.ID()).Regions() + regionsResponse, err := regionsCollection.List().Send() + if err != nil { + return nil, pkgerrors.Wrap(err, "error retrieving cloud region list") + } + + regionList := regionsResponse.Items() + return regionList, nil +} + +func (c *client) GetAddon(addonID string) (*addonsmgmtv1.Addon, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + resp, err := c.connection.AddonsMgmt().V1().Addons().Addon(addonID).Get().Send() + if err != nil { + if resp != nil && resp.Status() == http.StatusNotFound { + return nil, serviceErrors.NotFound("") + } + return nil, serviceErrors.GeneralError("sending GetAddon request: %v", err) + } + + return resp.Body(), nil +} + +func (c *client) GetAddonVersion(addonID string, versionID string) (*addonsmgmtv1.AddonVersion, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + resp, err := c.connection.AddonsMgmt().V1().Addons().Addon(addonID).Versions().Version(versionID).Get().Send() + if err != nil { + if resp != nil && resp.Status() == http.StatusNotFound { + return nil, serviceErrors.NotFound("") + } + return nil, serviceErrors.GeneralError("sending GetAddonVersion request: %v", err) + } + + return resp.Body(), nil +} + +// CreateAddonInstallation creates a new addon for a cluster with given ID +func (c *client) CreateAddonInstallation(clusterID string, addon *clustersmgmtv1.AddOnInstallation) error { + if c.connection == nil { + return serviceErrors.InvalidOCMConnection() + } + _, err := c.connection.ClustersMgmt().V1().Clusters().Cluster(clusterID).Addons().Add().Body(addon).Send() + if err != nil { + return fmt.Errorf("sending CreateAddonInstallation request: %w", err) + } + return nil +} + +// GetAddonInstallation returns the addon installed on a cluster with given ID +func (c *client) GetAddonInstallation(clusterID string, addonID string) (*clustersmgmtv1.AddOnInstallation, *serviceErrors.ServiceError) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + resp, err := c.connection.ClustersMgmt().V1().Clusters().Cluster(clusterID).Addons().Addoninstallation(addonID).Get().Send() + if err != nil { + if resp != nil && resp.Status() == http.StatusNotFound { + return nil, serviceErrors.NotFound("") + } + return nil, serviceErrors.GeneralError("sending GetAddonInstallation request: %v", err) + } + + return resp.Body(), nil +} + +// UpdateAddonInstallation updates the existing addon on a cluster with given ID +func (c *client) UpdateAddonInstallation(clusterID string, addonInstallation *clustersmgmtv1.AddOnInstallation) error { + if c.connection == nil { + return serviceErrors.InvalidOCMConnection() + } + _, err := c.connection.ClustersMgmt().V1().Clusters().Cluster(clusterID).Addons().Addoninstallation(addonInstallation.ID()).Update().Body(addonInstallation).Send() + if err != nil { + return fmt.Errorf("sending UpdateAddonInstallation request: %w", err) + } + return nil +} + +// DeleteAddonInstallation deletes the addon on a cluster with given ID +func (c *client) DeleteAddonInstallation(clusterID string, addonInstallationID string) error { + if c.connection == nil { + return serviceErrors.InvalidOCMConnection() + } + _, err := c.connection.ClustersMgmt().V1().Clusters().Cluster(clusterID).Addons().Addoninstallation(addonInstallationID).Delete().Send() + if err != nil { + return fmt.Errorf("sending DeleteAddonInstallation request: %w", err) + } + return nil +} + +// GetClusterDNS ... +func (c *client) GetClusterDNS(clusterID string) (string, error) { + if clusterID == "" { + return "", serviceErrors.Validation("clusterID cannot be empty") + } + ingresses, err := c.GetClusterIngresses(clusterID) + if err != nil { + return "", err + } + + var clusterDNS string + ingresses.Items().Each(func(ingress *clustersmgmtv1.Ingress) bool { + if ingress.Default() { + clusterDNS = ingress.DNSName() + return false + } + return true + }) + + if clusterDNS == "" { + return "", serviceErrors.NotFound("Cluster %s: DNS is empty", clusterID) + } + + return clusterDNS, nil +} + +// CreateIdentityProvider ... +func (c *client) CreateIdentityProvider(clusterID string, identityProvider *clustersmgmtv1.IdentityProvider) (*clustersmgmtv1.IdentityProvider, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + clustersResource := c.connection.ClustersMgmt().V1().Clusters() + response, identityProviderErr := clustersResource.Cluster(clusterID). + IdentityProviders(). + Add(). + Body(identityProvider). + Send() + var err error + if identityProviderErr != nil { + err = serviceErrors.NewErrorFromHTTPStatusCode(response.Status(), "ocm client failed to create identity provider: %s", identityProviderErr) + } + return response.Body(), err +} + +// DeleteCluster ... +func (c *client) DeleteCluster(clusterID string) (int, error) { + if c.connection == nil { + return 0, serviceErrors.InvalidOCMConnection() + } + + clustersResource := c.connection.ClustersMgmt().V1().Clusters() + response, deleteClusterError := clustersResource.Cluster(clusterID).Delete().Send() + + var err error + if deleteClusterError != nil { + err = serviceErrors.NewErrorFromHTTPStatusCode(response.Status(), "OCM client failed to delete cluster '%s': %s", clusterID, deleteClusterError) + } + return response.Status(), err +} + +// ClusterAuthorization ... +func (c *client) ClusterAuthorization(cb *amsv1.ClusterAuthorizationRequest) (*amsv1.ClusterAuthorizationResponse, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + glog.V(10).Infof("Sending request to OCM '%v'", *cb) + + r, err := c.connection.AccountsMgmt().V1(). + ClusterAuthorizations(). + Post().Request(cb).Send() + if err != nil && r.Status() != http.StatusTooManyRequests { + glog.Warningf("OCM client responded with '%v: %v' for request '%v'", r.Status(), err, *cb) + return nil, serviceErrors.NewErrorFromHTTPStatusCode(r.Status(), "OCM client failed to create cluster authorization") + } + resp, _ := r.GetResponse() + return resp, nil +} + +// DeleteSubscription ... +func (c *client) DeleteSubscription(id string) (int, error) { + if c.connection == nil { + return 0, serviceErrors.InvalidOCMConnection() + } + + r := c.connection.AccountsMgmt().V1().Subscriptions().Subscription(id).Delete() + resp, err := r.Send() + return resp.Status(), err +} + +// FindSubscriptions ... +func (c *client) FindSubscriptions(query string) (*amsv1.SubscriptionsListResponse, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + r, err := c.connection.AccountsMgmt().V1().Subscriptions().List().Search(query).Send() + if err != nil { + return nil, fmt.Errorf("querying the accounts management service for subscriptions: %w", err) + } + return r, nil +} + +// GetQuotaCostsForProduct gets the AMS QuotaCosts in the given organizationID +// whose relatedResources contains at least a relatedResource that has the +// given resourceName and product +func (c *client) GetQuotaCostsForProduct(organizationID, resourceName, product string) ([]*amsv1.QuotaCost, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + var res []*amsv1.QuotaCost + organizationClient := c.connection.AccountsMgmt().V1().Organizations() + quotaCostClient := organizationClient.Organization(organizationID).QuotaCost() + + req := quotaCostClient.List().Parameter("fetchRelatedResources", true).Parameter("fetchCloudAccounts", true) + + // TODO: go 1.21 can infer the following generic arguments from req + // automatically, so this indirection becomes unnecessary. + fetchQuotaCosts := fetchPages[*amsv1.QuotaCostListRequest, + *amsv1.QuotaCostListResponse, + *amsv1.QuotaCostList, + *amsv1.QuotaCost, + ] + err := fetchQuotaCosts(req, 100, 1000, func(qc *amsv1.QuotaCost) bool { + relatedResourcesList := qc.RelatedResources() + for _, relatedResource := range relatedResourcesList { + if relatedResource.ResourceName() == resourceName && relatedResource.Product() == product { + res = append(res, qc) + break + } + } + return true + }) + if err != nil { + return nil, pkgerrors.Wrap(err, "error listing QuotaCosts") + } + return res, nil +} + +func (c *client) GetCustomerCloudAccounts(organizationID string, quotaIDs []string) ([]*amsv1.CloudAccount, error) { + if c.connection == nil { + return nil, serviceErrors.InvalidOCMConnection() + } + + var res []*amsv1.CloudAccount + organizationClient := c.connection.AccountsMgmt().V1().Organizations() + quotaCostClient := organizationClient.Organization(organizationID).QuotaCost() + + quotaCostList, err := quotaCostClient.List().Parameter("fetchCloudAccounts", true).Send() + if err != nil { + return nil, fmt.Errorf("error getting cloud accounts: %w", err) + } + + quotaCostList.Items().Each(func(qc *amsv1.QuotaCost) bool { + for _, quotaID := range quotaIDs { + if qc.QuotaID() == quotaID { + res = append(res, qc.CloudAccounts()...) + break + } + } + return true + }) + + return res, nil +} + +// GetCurrentAccount returns the account information of the user to whom belongs the token +func (c *client) GetCurrentAccount(userToken string) (int, *amsv1.Account, error) { + logger, err := getLogger(c.connection.Logger().DebugEnabled()) + if err != nil { + return 0, nil, fmt.Errorf("couldn't create logger for modified OCM connection: %w", err) + } + modifiedConnection, err := getBaseConnectionBuilder(c.connection.URL()). + Logger(logger). + Tokens(userToken). + Build() + if err != nil { + return 0, nil, fmt.Errorf("couldn't build modified OCM connection: %w", err) + } + defer modifiedConnection.Close() + response, err := modifiedConnection.AccountsMgmt().V1().CurrentAccount().Get().Send() + if err != nil { + return response.Status(), nil, fmt.Errorf("unsuccessful call to current account endpoint: %w", err) + } + + currentAccount := response.Body() + return response.Status(), currentAccount, nil +} diff --git a/pkg/client/ocm/client_test.go b/pkg/client/ocm/impl/client_test.go similarity index 98% rename from pkg/client/ocm/client_test.go rename to pkg/client/ocm/impl/client_test.go index 92fc3a3334..e0ffb3e81b 100644 --- a/pkg/client/ocm/client_test.go +++ b/pkg/client/ocm/impl/client_test.go @@ -1,4 +1,4 @@ -package ocm +package impl import ( "testing" diff --git a/pkg/client/ocm/config.go b/pkg/client/ocm/impl/config.go similarity index 99% rename from pkg/client/ocm/config.go rename to pkg/client/ocm/impl/config.go index 9086dff40d..560702f066 100644 --- a/pkg/client/ocm/config.go +++ b/pkg/client/ocm/impl/config.go @@ -1,4 +1,4 @@ -package ocm +package impl import ( "fmt" diff --git a/pkg/client/ocm/impl/id.go b/pkg/client/ocm/impl/id.go new file mode 100644 index 0000000000..3c42183943 --- /dev/null +++ b/pkg/client/ocm/impl/id.go @@ -0,0 +1,35 @@ +package impl + +import ( + "fmt" + + "github.com/rs/xid" + "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" +) + +// MaxClusterNameLength ... +const ( + // MaxClusterNameLength - defines maximum length of an OSD cluster name + MaxClusterNameLength = 15 +) + +var _ ocm.IDGenerator = idGenerator{} + +// idGenerator internal implementation of IDGenerator. +type idGenerator struct { + // prefix a string to prefix to any generated ID. + prefix string +} + +// NewIDGenerator create a new default implementation of IDGenerator. +func NewIDGenerator(prefix string) ocm.IDGenerator { + return idGenerator{ + prefix: prefix, + } +} + +// Generate It is not allowed for the cluster name to be longer than 15 characters, hence +// the truncation +func (i idGenerator) Generate() string { + return fmt.Sprintf("%s%s", i.prefix, xid.New().String())[0:MaxClusterNameLength] +} diff --git a/pkg/client/ocm/id_test.go b/pkg/client/ocm/impl/id_test.go similarity index 98% rename from pkg/client/ocm/id_test.go rename to pkg/client/ocm/impl/id_test.go index c337abd65c..db2afbe675 100644 --- a/pkg/client/ocm/id_test.go +++ b/pkg/client/ocm/impl/id_test.go @@ -1,4 +1,4 @@ -package ocm +package impl import ( "fmt" diff --git a/pkg/client/ocm/paginated_moq.go b/pkg/client/ocm/impl/paginated_moq.go similarity index 99% rename from pkg/client/ocm/paginated_moq.go rename to pkg/client/ocm/impl/paginated_moq.go index edb9bc952d..04998c5d2f 100644 --- a/pkg/client/ocm/paginated_moq.go +++ b/pkg/client/ocm/impl/paginated_moq.go @@ -1,4 +1,4 @@ -package ocm +package impl // Here goes implementation of paginatedResponse and paginatedRequest interfaces // for testing purposes. They provide paged access to an array of testDataType. diff --git a/pkg/client/ocm/paginated_moq_test.go b/pkg/client/ocm/impl/paginated_moq_test.go similarity index 99% rename from pkg/client/ocm/paginated_moq_test.go rename to pkg/client/ocm/impl/paginated_moq_test.go index e1f1b47226..029f5d1b21 100644 --- a/pkg/client/ocm/paginated_moq_test.go +++ b/pkg/client/ocm/impl/paginated_moq_test.go @@ -1,4 +1,4 @@ -package ocm +package impl import ( "testing" diff --git a/pkg/client/ocm/paginated_request.go b/pkg/client/ocm/impl/paginated_request.go similarity index 99% rename from pkg/client/ocm/paginated_request.go rename to pkg/client/ocm/impl/paginated_request.go index e01675300e..82b6081f71 100644 --- a/pkg/client/ocm/paginated_request.go +++ b/pkg/client/ocm/impl/paginated_request.go @@ -1,4 +1,4 @@ -package ocm +package impl import pkgerrors "github.com/pkg/errors" diff --git a/pkg/client/ocm/types.go b/pkg/client/ocm/impl/types.go similarity index 98% rename from pkg/client/ocm/types.go rename to pkg/client/ocm/impl/types.go index c0d6562d77..892e79ada9 100644 --- a/pkg/client/ocm/types.go +++ b/pkg/client/ocm/impl/types.go @@ -1,4 +1,4 @@ -package ocm +package impl // Parameter ... type Parameter struct { diff --git a/pkg/client/ocm/client_moq.go b/pkg/client/ocm/mocks/client_moq.go similarity index 99% rename from pkg/client/ocm/client_moq.go rename to pkg/client/ocm/mocks/client_moq.go index ea302d7a25..94264f2574 100644 --- a/pkg/client/ocm/client_moq.go +++ b/pkg/client/ocm/mocks/client_moq.go @@ -1,26 +1,27 @@ // Code generated by moq; DO NOT EDIT. // github.com/matryer/moq -package ocm +package mocks import ( sdkClient "github.com/openshift-online/ocm-sdk-go" amsv1 "github.com/openshift-online/ocm-sdk-go/accountsmgmt/v1" addonsmgmtv1 "github.com/openshift-online/ocm-sdk-go/addonsmgmt/v1" clustersmgmtv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" + "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" serviceErrors "github.com/stackrox/acs-fleet-manager/pkg/errors" "sync" ) -// Ensure, that ClientMock does implement Client. +// Ensure, that ClientMock does implement ocm.Client. // If this is not the case, regenerate this file with moq. -var _ Client = &ClientMock{} +var _ ocm.Client = &ClientMock{} -// ClientMock is a mock implementation of Client. +// ClientMock is a mock implementation of ocm.Client. // // func TestSomethingThatUsesClient(t *testing.T) { // -// // make and configure a mocked Client +// // make and configure a mocked ocm.Client // mockedClient := &ClientMock{ // ClusterAuthorizationFunc: func(cb *amsv1.ClusterAuthorizationRequest) (*amsv1.ClusterAuthorizationResponse, error) { // panic("mock out the ClusterAuthorization method") @@ -99,7 +100,7 @@ var _ Client = &ClientMock{} // }, // } // -// // use mockedClient in code that requires Client +// // use mockedClient in code that requires ocm.Client // // and then make assertions. // // } diff --git a/pkg/client/ocm/idgenerator_moq.go b/pkg/client/ocm/mocks/id_generator_moq.go similarity index 79% rename from pkg/client/ocm/idgenerator_moq.go rename to pkg/client/ocm/mocks/id_generator_moq.go index 978895cde3..d817e3de9c 100644 --- a/pkg/client/ocm/idgenerator_moq.go +++ b/pkg/client/ocm/mocks/id_generator_moq.go @@ -1,28 +1,29 @@ // Code generated by moq; DO NOT EDIT. // github.com/matryer/moq -package ocm +package mocks import ( + "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" "sync" ) -// Ensure, that IDGeneratorMock does implement IDGenerator. +// Ensure, that IDGeneratorMock does implement ocm.IDGenerator. // If this is not the case, regenerate this file with moq. -var _ IDGenerator = &IDGeneratorMock{} +var _ ocm.IDGenerator = &IDGeneratorMock{} -// IDGeneratorMock is a mock implementation of IDGenerator. +// IDGeneratorMock is a mock implementation of ocm.IDGenerator. // // func TestSomethingThatUsesIDGenerator(t *testing.T) { // -// // make and configure a mocked IDGenerator +// // make and configure a mocked ocm.IDGenerator // mockedIDGenerator := &IDGeneratorMock{ // GenerateFunc: func() string { // panic("mock out the Generate method") // }, // } // -// // use mockedIDGenerator in code that requires IDGenerator +// // use mockedIDGenerator in code that requires ocm.IDGenerator // // and then make assertions. // // } diff --git a/pkg/providers/core.go b/pkg/providers/core.go index ba49e6cdbb..77e3aa3b63 100644 --- a/pkg/providers/core.go +++ b/pkg/providers/core.go @@ -9,7 +9,7 @@ import ( "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" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + 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" "github.com/stackrox/acs-fleet-manager/pkg/environments" diff --git a/pkg/services/account/provider.go b/pkg/services/account/provider.go index 56dd602c0f..08a7049ebe 100644 --- a/pkg/services/account/provider.go +++ b/pkg/services/account/provider.go @@ -2,7 +2,7 @@ package account import ( "github.com/goava/di" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/environments" "github.com/stackrox/acs-fleet-manager/pkg/logger" ) diff --git a/pkg/services/authorization/provider.go b/pkg/services/authorization/provider.go index 0dbb018b3d..2f489357cf 100644 --- a/pkg/services/authorization/provider.go +++ b/pkg/services/authorization/provider.go @@ -2,7 +2,7 @@ package authorization import ( "github.com/goava/di" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/environments" "github.com/stackrox/acs-fleet-manager/pkg/logger" ) diff --git a/probe/pkg/fleetmanager/client.go b/probe/pkg/fleetmanager/client.go index 15e6ef9f5a..a6a28844e6 100644 --- a/probe/pkg/fleetmanager/client.go +++ b/probe/pkg/fleetmanager/client.go @@ -6,17 +6,18 @@ import ( "github.com/pkg/errors" "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager" + "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager/impl" "github.com/stackrox/acs-fleet-manager/probe/config" ) // New creates a new fleet manager client. func New(ctx context.Context, config *config.Config) (fleetmanager.PublicAPI, error) { - auth, err := fleetmanager.NewAuth(ctx, config.AuthType, fleetmanager.OptionFromEnv()) + auth, err := impl.NewAuth(ctx, config.AuthType, impl.OptionFromEnv()) if err != nil { return nil, errors.Wrap(err, "failed to create fleet manager authentication") } - client, err := fleetmanager.NewClient(config.FleetManagerEndpoint, auth, fleetmanager.WithUserAgent("fleet-manager-probe-service")) + client, err := impl.NewClient(config.FleetManagerEndpoint, auth, impl.WithUserAgent("fleet-manager-probe-service")) if err != nil { return nil, errors.Wrap(err, "failed to create fleet manager client") } diff --git a/probe/pkg/probe/probe_test.go b/probe/pkg/probe/probe_test.go index b4b01bb591..3463c9f95f 100644 --- a/probe/pkg/probe/probe_test.go +++ b/probe/pkg/probe/probe_test.go @@ -12,7 +12,7 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/constants" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/api/public" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/dinosaurs/types" - "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager" + fleetmanager "github.com/stackrox/acs-fleet-manager/pkg/client/fleetmanager/mocks" "github.com/stackrox/acs-fleet-manager/probe/config" "github.com/stackrox/rox/pkg/concurrency" "github.com/stackrox/rox/pkg/httputil" diff --git a/test/helper.go b/test/helper.go index ecb7457e69..5a70a9b676 100644 --- a/test/helper.go +++ b/test/helper.go @@ -16,7 +16,7 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/config" "github.com/stackrox/acs-fleet-manager/pkg/client/iam" - "github.com/stackrox/acs-fleet-manager/pkg/client/ocm" + ocm "github.com/stackrox/acs-fleet-manager/pkg/client/ocm/impl" "github.com/stackrox/acs-fleet-manager/pkg/server" "github.com/stackrox/acs-fleet-manager/internal/dinosaur/compat" From a092092e4cdc4f80ea2389ab6a2c19d829b695aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl?= Date: Tue, 30 Jan 2024 10:45:48 +0100 Subject: [PATCH 16/16] chore: enable unused and ginkgo linters (#1623) --- .golangci.yml | 7 +++---- .../pkg/services/cluster_placement_strategy_test.go | 5 ++--- .../dinosaur/pkg/services/data_plane_cluster.go | 8 -------- internal/dinosaur/pkg/services/dinosaur.go | 2 +- internal/dinosaur/pkg/services/util.go | 2 +- internal/dinosaur/pkg/services/util_test.go | 13 ++++++------- 6 files changed, 13 insertions(+), 24 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 6a066d1f8b..c61986ee44 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -42,7 +42,6 @@ linters-settings: includes: - G601 revive: - min-confidence: 0 rules: - name: blank-imports - name: context-as-argument @@ -87,14 +86,13 @@ linters-settings: enabled-checks: - commentFormatting nolintlint: - allow-leading-space: false # require machine-readable nolint directives (i.e. with no leading space) allow-unused: false # report any unused nolint directives require-explanation: false # don't require an explanation for nolint directives require-specific: true # require nolint directives to be specific about which linter is being skipped staticcheck: go: "1.20" checks: [ all,-ST1000,-ST1001,-ST1003,-ST1005,-SA1019,-SA4001,-ST1016 ] - wrapcheck: + wrapcheck: {} # ignoreSigRegexps: uncomment to add ignore rules linters: @@ -114,6 +112,7 @@ linters: # - gochecknoinits # - gocognit # - goconst + - ginkgolinter - exportloopref - gocritic # - gocyclo @@ -146,7 +145,7 @@ linters: # - typecheck # - unconvert # - unparam - # - unused + - unused # - varcheck # - whitespace - wrapcheck diff --git a/internal/dinosaur/pkg/services/cluster_placement_strategy_test.go b/internal/dinosaur/pkg/services/cluster_placement_strategy_test.go index 7c99a7b80a..b5262680fd 100644 --- a/internal/dinosaur/pkg/services/cluster_placement_strategy_test.go +++ b/internal/dinosaur/pkg/services/cluster_placement_strategy_test.go @@ -7,7 +7,6 @@ import ( "github.com/stackrox/acs-fleet-manager/internal/dinosaur/pkg/config" "github.com/stackrox/acs-fleet-manager/pkg/api" - apiErrors "github.com/stackrox/acs-fleet-manager/pkg/errors" "github.com/stretchr/testify/require" serviceErrors "github.com/stackrox/acs-fleet-manager/pkg/errors" @@ -77,12 +76,12 @@ func TestFirstClusterPlacementStrategy(t *testing.T) { newClusterServiceMock: func() ClusterService { return &ClusterServiceMock{ FindAllClustersFunc: func(criteria FindClusterCriteria) ([]*api.Cluster, *serviceErrors.ServiceError) { - return nil, serviceErrors.New(apiErrors.ErrorGeneral, "error in FindAllClusters") + return nil, serviceErrors.New(serviceErrors.ErrorGeneral, "error in FindAllClusters") }, } }, central: centralRequest, - expectedError: serviceErrors.New(apiErrors.ErrorGeneral, "error in FindAllClusters"), + expectedError: serviceErrors.New(serviceErrors.ErrorGeneral, "error in FindAllClusters"), expectedCluster: nil, }, { diff --git a/internal/dinosaur/pkg/services/data_plane_cluster.go b/internal/dinosaur/pkg/services/data_plane_cluster.go index a891481131..9e8fb02cff 100644 --- a/internal/dinosaur/pkg/services/data_plane_cluster.go +++ b/internal/dinosaur/pkg/services/data_plane_cluster.go @@ -22,8 +22,6 @@ type DataPlaneClusterService interface { var _ DataPlaneClusterService = &dataPlaneClusterService{} -const dataPlaneClusterStatusCondReadyName = "Ready" - type dataPlaneClusterService struct { di.Inject ClusterService ClusterService @@ -88,9 +86,3 @@ func (d *dataPlaneClusterService) setClusterStatus(cluster *api.Cluster, status } return nil } - -func (d *dataPlaneClusterService) clusterCanProcessStatusReports(cluster *api.Cluster) bool { - return cluster.Status == api.ClusterReady || - cluster.Status == api.ClusterComputeNodeScalingUp || - cluster.Status == api.ClusterFull -} diff --git a/internal/dinosaur/pkg/services/dinosaur.go b/internal/dinosaur/pkg/services/dinosaur.go index e6b39c2b01..24e57b029e 100644 --- a/internal/dinosaur/pkg/services/dinosaur.go +++ b/internal/dinosaur/pkg/services/dinosaur.go @@ -229,7 +229,7 @@ func (k *dinosaurService) DetectInstanceType(dinosaurRequest *dbapi.CentralReque // reserveQuota - reserves quota for the given dinosaur request. If a RHACS quota has been assigned, it will try to reserve RHACS quota, otherwise it will try with RHACSTrial func (k *dinosaurService) reserveQuota(ctx context.Context, dinosaurRequest *dbapi.CentralRequest) (subscriptionID string, err *errors.ServiceError) { if dinosaurRequest.InstanceType == types.EVAL.String() && - (environments.GetEnvironmentStrFromEnv() == environments.DevelopmentEnv || environments.GetEnvironmentStrFromEnv() == environments.TestingEnv) == false { + !(environments.GetEnvironmentStrFromEnv() == environments.DevelopmentEnv || environments.GetEnvironmentStrFromEnv() == environments.TestingEnv) { if !k.dinosaurConfig.Quota.AllowEvaluatorInstance { return "", errors.NewWithCause(errors.ErrorForbidden, err, "central eval instances are not allowed") } diff --git a/internal/dinosaur/pkg/services/util.go b/internal/dinosaur/pkg/services/util.go index 0ec9751779..4148d90ea9 100644 --- a/internal/dinosaur/pkg/services/util.go +++ b/internal/dinosaur/pkg/services/util.go @@ -54,7 +54,7 @@ func replaceHostSpecialChar(name string) (string, error) { // This should never fail based on above replacement of invalid characters. validationErrors := validation.IsDNS1035Label(replacedName) if len(validationErrors) > 0 { - return replacedName, fmt.Errorf("Host name is not valid: %s. A DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character, regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?'", strings.Join(validationErrors[:], ",")) + return replacedName, fmt.Errorf("host name %q is not valid: a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character, regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?'", strings.Join(validationErrors[:], ",")) } return replacedName, nil diff --git a/internal/dinosaur/pkg/services/util_test.go b/internal/dinosaur/pkg/services/util_test.go index b4cc4e05f0..9bb51a53bc 100644 --- a/internal/dinosaur/pkg/services/util_test.go +++ b/internal/dinosaur/pkg/services/util_test.go @@ -11,7 +11,6 @@ import ( "github.com/stackrox/acs-fleet-manager/pkg/shared" pkgErr "github.com/pkg/errors" - "github.com/stackrox/acs-fleet-manager/pkg/errors" serviceError "github.com/stackrox/acs-fleet-manager/pkg/errors" "gorm.io/gorm" ) @@ -83,7 +82,7 @@ func Test_handleCreateError(t *testing.T) { tests := []struct { name string args args - want *errors.ServiceError + want *serviceError.ServiceError }{ { name: "Handler should return a general error for any other errors than violating unique constraints", @@ -91,7 +90,7 @@ func Test_handleCreateError(t *testing.T) { resourceType: resourceType, err: gorm.ErrInvalidField, }, - want: errors.GeneralError("Unable to create %s: %s", resourceType, gorm.ErrInvalidField.Error()), + want: serviceError.GeneralError("Unable to create %s: %s", resourceType, gorm.ErrInvalidField.Error()), }, { name: "Handler should return a conflict error if creation error is due to violating unique constraints", @@ -99,7 +98,7 @@ func Test_handleCreateError(t *testing.T) { resourceType: resourceType, err: fmt.Errorf("transaction violates unique constraints"), }, - want: errors.Conflict("This %s already exists", resourceType), + want: serviceError.Conflict("This %s already exists", resourceType), }, } for _, tt := range tests { @@ -119,7 +118,7 @@ func Test_handleUpdateError(t *testing.T) { tests := []struct { name string args args - want *errors.ServiceError + want *serviceError.ServiceError }{ { name: "Handler should return a general error for any other errors than violating unique constraints", @@ -127,7 +126,7 @@ func Test_handleUpdateError(t *testing.T) { resourceType: resourceType, err: gorm.ErrInvalidData, }, - want: errors.GeneralError("Unable to update %s: %s", resourceType, gorm.ErrInvalidData.Error()), + want: serviceError.GeneralError("Unable to update %s: %s", resourceType, gorm.ErrInvalidData.Error()), }, { name: "Handler should return a conflict error if update error is due to violating unique constraints", @@ -135,7 +134,7 @@ func Test_handleUpdateError(t *testing.T) { resourceType: resourceType, err: fmt.Errorf("transaction violates unique constraints"), }, - want: errors.Conflict("Changes to %s conflict with existing records", resourceType), + want: serviceError.Conflict("Changes to %s conflict with existing records", resourceType), }, } for _, tt := range tests {