diff --git a/.github/workflows/acceptance_tests_mta.yaml b/.github/workflows/acceptance_tests_mta.yaml index 53e8fa38f..c059709a9 100644 --- a/.github/workflows/acceptance_tests_mta.yaml +++ b/.github/workflows/acceptance_tests_mta.yaml @@ -18,5 +18,14 @@ jobs: with: deployment_name: "autoscaler-mta-${{ github.event.pull_request.number || 'main' }}" use_metricsgateway: ${{ github.event_name == 'pull_request' }} + system_domain: "${{ vars.SYSTEM_DOMAIN }}" + autoscaler_org: "${{ vars.ACCEPTANCE_TESTS_EXISTING_ORG }}" + autoscaler_org_manager_user: "${{ vars.AUTOSCALER_ORG_MANAGER_USER }}" + autoscaler_other_user: "${{ vars.AUTOSCALER_OTHER_USER }}" + infrastructure: "${{ vars.INFRASTRUCTURE }}" + postgres_service_offering: "${{ vars.POSTGRES_SERVICE_OFFERING }}" + postgres_service_plan: "${{ vars.POSTGRES_SERVICE_PLAN }}" secrets: bbl_ssh_key: "${{ secrets.BBL_SSH_KEY }}" + autoscaler_org_manager_password: "${{ secrets.AUTOSCALER_ORG_MANAGER_PASSWORD }}" + autoscaler_other_user_password: "${{ secrets.AUTOSCALER_OTHER_USER_PASSWORD }}" diff --git a/.github/workflows/acceptance_tests_reusable.yaml b/.github/workflows/acceptance_tests_reusable.yaml index b8a32effb..3f7e00c87 100644 --- a/.github/workflows/acceptance_tests_reusable.yaml +++ b/.github/workflows/acceptance_tests_reusable.yaml @@ -13,9 +13,41 @@ on: required: false type: boolean default: false + system_domain: + required: false + type: string + default: "" + autoscaler_org: + required: false + type: string + default: "" + autoscaler_org_manager_user: + required: false + type: string + default: "" + autoscaler_other_user: + required: false + type: string + default: "" + infrastructure: + required: false + type: string + default: "OSS" + postgres_service_offering: + required: false + type: string + default: "" + postgres_service_plan: + required: false + type: string + default: "" secrets: bbl_ssh_key: required: true + autoscaler_org_manager_password: + required: false + autoscaler_other_user_password: + required: false defaults: run: @@ -54,14 +86,44 @@ jobs: with: ssh-key: ${{ secrets.bbl_ssh_key}} + - name: Create Acceptance Space + shell: bash + env: + SYSTEM_DOMAIN: ${{ inputs.system_domain }} + AUTOSCALER_ORG: ${{ inputs.autoscaler_org }} + AUTOSCALER_ORG_MANAGER_USER: ${{ inputs.autoscaler_org_manager_user }} + AUTOSCALER_ORG_MANAGER_PASSWORD: ${{ secrets.autoscaler_org_manager_password }} + INFRASTRUCTURE: ${{ inputs.infrastructure }} + run: make --directory="${AUTOSCALER_DIR}" create-acceptance-space + - name: Provision DB + if: inputs.infrastructure == 'OSS' shell: bash run: make --directory="${AUTOSCALER_DIR}" provision-db + - name: Provision DB Service + if: inputs.infrastructure != 'OSS' + shell: bash + env: + SYSTEM_DOMAIN: ${{ inputs.system_domain }} + AUTOSCALER_ORG: ${{ inputs.autoscaler_org }} + AUTOSCALER_ORG_MANAGER_USER: ${{ inputs.autoscaler_org_manager_user }} + AUTOSCALER_ORG_MANAGER_PASSWORD: ${{ secrets.autoscaler_org_manager_password }} + POSTGRES_SERVICE_OFFERING: ${{ inputs.postgres_service_offering }} + POSTGRES_SERVICE_PLAN: ${{ inputs.postgres_service_plan }} + run: make --directory="${AUTOSCALER_DIR}" provision-db-service + - name: Deploy Apps shell: bash env: USE_METRICSGATEWAY: ${{ inputs.use_metricsgateway }} + SYSTEM_DOMAIN: ${{ inputs.system_domain }} + AUTOSCALER_ORG: ${{ inputs.autoscaler_org }} + AUTOSCALER_ORG_MANAGER_USER: ${{ inputs.autoscaler_org_manager_user }} + AUTOSCALER_ORG_MANAGER_PASSWORD: ${{ secrets.autoscaler_org_manager_password }} + AUTOSCALER_OTHER_USER: ${{ inputs.autoscaler_other_user }} + AUTOSCALER_OTHER_USER_PASSWORD: ${{ secrets.autoscaler_other_user_password }} + INFRASTRUCTURE: ${{ inputs.infrastructure }} run: | make --directory="${AUTOSCALER_DIR}" build-extension-file make --directory="${AUTOSCALER_DIR}" mta-build @@ -98,6 +160,11 @@ jobs: shell: bash env: SUITES: ${{ matrix.suite }} + SYSTEM_DOMAIN: ${{ inputs.system_domain }} + AUTOSCALER_ORG_MANAGER_USER: ${{ inputs.autoscaler_org_manager_user }} + AUTOSCALER_ORG_MANAGER_PASSWORD: ${{ secrets.autoscaler_org_manager_password }} + AUTOSCALER_OTHER_USER: ${{ inputs.autoscaler_other_user }} + AUTOSCALER_OTHER_USER_PASSWORD: ${{ secrets.autoscaler_other_user_password }} run: make --directory="${AUTOSCALER_DIR}" mta-acceptance-tests deployment_cleanup: @@ -125,6 +192,12 @@ jobs: with: ssh-key: ${{ secrets.bbl_ssh_key}} - name: Perform deployment cleanup + env: + SYSTEM_DOMAIN: ${{ inputs.system_domain }} + AUTOSCALER_ORG: ${{ inputs.autoscaler_org }} + AUTOSCALER_ORG_MANAGER_USER: ${{ inputs.autoscaler_org_manager_user }} + AUTOSCALER_ORG_MANAGER_PASSWORD: ${{ secrets.autoscaler_org_manager_password }} + INFRASTRUCTURE: ${{ inputs.infrastructure }} run: | make --directory="${AUTOSCALER_DIR}" deploy-cleanup diff --git a/.github/workflows/create-org-manager-user.yaml b/.github/workflows/setup-acceptance-tests.yaml similarity index 57% rename from .github/workflows/create-org-manager-user.yaml rename to .github/workflows/setup-acceptance-tests.yaml index 0699432dc..f31ad5323 100644 --- a/.github/workflows/create-org-manager-user.yaml +++ b/.github/workflows/setup-acceptance-tests.yaml @@ -1,13 +1,17 @@ -name: Create CI Test Users +name: Setup Acceptance Tests on: workflow_dispatch: inputs: - org: + autoscaler_org: + required: true + type: string + description: "CF org to grant OrgManager role in" + existing_organization: required: false type: string - default: "SAP_autoscaler_tests_OSS" - description: "CF org to create the CI test users in" + default: "" + description: "Additional CF org to grant OrgManager role in (if different from autoscaler_org)" defaults: run: @@ -16,12 +20,11 @@ defaults: env: AUTOSCALER_DIR: "${{ github.workspace }}/app-autoscaler" BBL_STATE_PATH: "${{ github.workspace }}/bbl/bbl-state" - AUTOSCALER_ORG: "${{ inputs.org }}" - DEPLOYMENT_NAME: "autoscaler-mta-main" + AUTOSCALER_ORG: "${{ inputs.autoscaler_org }}" jobs: - create_ci_test_users: - name: Create CI Test Users + setup_acceptance_tests: + name: Setup Acceptance Tests runs-on: ubuntu-latest steps: - name: Checkout @@ -39,15 +42,16 @@ jobs: run: | cd "${AUTOSCALER_DIR}" eval "$(devbox shellenv)" - printenv >> "$GITHUB_ENV" + printenv | grep -v '^GH_TOKEN=' >> "$GITHUB_ENV" - name: Setup environment uses: ./app-autoscaler/.github/actions/setup-environment with: ssh-key: ${{ secrets.BBL_SSH_KEY }} - - name: Create CI test users and store credentials + - name: Setup acceptance tests (create users, org, space) env: - GH_TOKEN: ${{ secrets.APP_AUTOSCALER_CI_TOKEN }} + EXISTING_ORGANIZATION: ${{ inputs.existing_organization }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - make --directory="${AUTOSCALER_DIR}" create-org-manager-user + make --directory="${AUTOSCALER_DIR}" setup-acceptance-tests diff --git a/Makefile b/Makefile index 4b210e9c3..af096503d 100644 --- a/Makefile +++ b/Makefile @@ -230,10 +230,18 @@ target/init-db-${db_type}: @mkdir -p target @touch $@ +.PHONY: create-acceptance-space +create-acceptance-space: ## Create the CF org/space for acceptance tests + @./scripts/create-acceptance-space.sh + .PHONY: provision-db provision-db: ## Provision a database on a remote Postgres server (requires POSTGRES_IP) @./scripts/provision_db.sh +.PHONY: provision-db-service +provision-db-service: ## Provision a CF service instance as the database (non-OSS) + @./scripts/provision-db-service.sh + .PHONY: deprovision-db deprovision-db: ## Deprovision a database on a remote Postgres server (requires POSTGRES_IP) @./scripts/deprovision_db.sh @@ -382,6 +390,10 @@ cf-login: 'The necessary changes to the environment get lost when make exits its process.' @${MAKEFILE_DIR}/scripts/os-infrastructure-login.sh +.PHONY: deploy-cleanup +deploy-cleanup: + DEBUG="${DEBUG}" ./scripts/cleanup-autoscaler.sh + .PHONY: start-db start-db: check-db_type target/start-db-${db_type}_CI_${CI} waitfor_${db_type}_CI_${CI} @@ -495,9 +507,6 @@ build-test-app: acceptance.build_tests: @make --directory=acceptance build_tests -.PHONY: acceptance.tests-cleanup -acceptance.tests-cleanup: - @make --directory=acceptance acceptance-tests-cleanup # This target is defined here rather than directly in the component “scheduler” itself, because it depends on targets outside that component. In the future, it will be moved back to that component and reference a dependency to a Makefile on the same level – the one for the component it depends on. .PHONY: scheduler.test @@ -537,9 +546,6 @@ build-acceptance-tests: acceptance-tests: build-test-app acceptance-tests-config ## Run acceptance tests against OSS dev environment (requrires a previous deployment of the autoscaler) @make --directory='acceptance' run-acceptance-tests -.PHONY: acceptance-cleanup -acceptance-cleanup: - @make --directory='acceptance' acceptance-tests-cleanup .PHONY: acceptance-tests-config acceptance-tests-config: @@ -556,9 +562,9 @@ mta-acceptance-tests: ## Run MTA acceptance tests in parallel via CF tasks deploy-cleanup: DEBUG="${DEBUG}" ./scripts/cleanup-autoscaler.sh -.PHONY: create-org-manager-user -create-org-manager-user: - DEBUG="${DEBUG}" ./scripts/create-org-manager-user.sh +.PHONY: setup-acceptance-tests +setup-acceptance-tests: + DEBUG="${DEBUG}" ./scripts/setup-acceptance-tests.sh .PHONY: deploy-apps deploy-apps: diff --git a/acceptance/Makefile b/acceptance/Makefile index b5e5e1c26..f9a6bc608 100644 --- a/acceptance/Makefile +++ b/acceptance/Makefile @@ -20,8 +20,7 @@ export GOWORK := off .PHONY: clean clean: - rm --recursive --force './build' './vendor' './ginkgo_v2' './results' - @make --directory='./assets/app/go_app' clean + rm --recursive --force './vendor' .PHONY: go-mod-tidy @@ -86,6 +85,3 @@ run-acceptance-tests: acceptance-tests-config @${MAKEFILE_DIR}/../scripts/run-acceptance-tests.sh -.PHONY: acceptance-tests-cleanup -acceptance-tests-cleanup: acceptance-tests-config - @${MAKEFILE_DIR}/../scripts/cleanup-acceptance.sh diff --git a/acceptance/assets/app/go_app/internal/app/disk_test.go b/acceptance/assets/app/go_app/internal/app/disk_test.go index 8bad02337..7a4f6866e 100644 --- a/acceptance/assets/app/go_app/internal/app/disk_test.go +++ b/acceptance/assets/app/go_app/internal/app/disk_test.go @@ -247,7 +247,6 @@ var _ = Describe("Disk write timing", func() { It("completes within 25 seconds", func() { filePath := filepath.Join(GinkgoT().TempDir(), "disk-timing-test") occupier := app.NewDefaultDiskOccupier(filePath) - DeferCleanup(occupier.Stop) spaceInBytes := int64(650) * 1000 * 1000 // matches acceptance test: 650 * 1000 * 1000 @@ -263,6 +262,7 @@ var _ = Describe("Disk write timing", func() { Expect(err).ToNot(HaveOccurred()) Expect(fStat.Size()).To(Equal(spaceInBytes)) + occupier.Stop() GinkgoWriter.Printf("650MB write completed in %v\n", elapsed) }) }) diff --git a/acceptance/broker/broker_test.go b/acceptance/broker/broker_test.go index 9e71e011f..839eb941c 100644 --- a/acceptance/broker/broker_test.go +++ b/acceptance/broker/broker_test.go @@ -352,21 +352,19 @@ type ServicePlans []ServicePlan type BoolOrInt bool func (b *BoolOrInt) UnmarshalJSON(data []byte) error { - if len(data) > 0 && (data[0] == 't' || data[0] == 'f') { - var boolVal bool - if err := json.Unmarshal(data, &boolVal); err != nil { - return err - } + var boolVal bool + if err := json.Unmarshal(data, &boolVal); err == nil { *b = BoolOrInt(boolVal) return nil } var intVal int - if err := json.Unmarshal(data, &intVal); err != nil { - return fmt.Errorf("cannot unmarshal %s into BoolOrInt", string(data)) + if err := json.Unmarshal(data, &intVal); err == nil { + *b = BoolOrInt(intVal != 0) + return nil } - *b = BoolOrInt(intVal != 0) - return nil + + return fmt.Errorf("cannot unmarshal %s into BoolOrInt", string(data)) } type ( @@ -411,7 +409,7 @@ func GetServicePlans(cfg *config.Config) ServicePlans { } func (p ServicePlan) isUpdatable() bool { - return p.BrokerCatalog.Features.PlanUpdateable.Bool() + return bool(p.BrokerCatalog.Features.PlanUpdateable) } func (b BoolOrInt) Bool() bool { return bool(b) } diff --git a/cf/cfclient_wrapper.go b/cf/cfclient_wrapper.go index 67ef8e3da..2ae092ce4 100644 --- a/cf/cfclient_wrapper.go +++ b/cf/cfclient_wrapper.go @@ -114,6 +114,13 @@ func (w *CFClientWrapper) Login(ctx context.Context) error { } func (w *CFClientWrapper) IsUserAdmin(ctx context.Context, userToken string) (bool, error) { + // Password grant uses the public "cf" client (no secret). UAA /introspect requires + // confidential client credentials, so it would always fail. On password-grant deployments + // (PR environments) the caller is an OrgManager, never a CC admin, so returning false is correct. + if w.conf.IsPasswordGrant() { + return false, nil + } + resp, err := w.introspectToken(ctx, userToken) if err != nil { return false, err diff --git a/cf/cfclient_wrapper_test.go b/cf/cfclient_wrapper_test.go index d76fd93ca..660e132d2 100644 --- a/cf/cfclient_wrapper_test.go +++ b/cf/cfclient_wrapper_test.go @@ -203,6 +203,22 @@ var _ = Describe("CFClientWrapper", func() { Expect(err).NotTo(HaveOccurred()) Expect(isAdmin).To(BeFalse()) }) + + Context("when using password grant", func() { + BeforeEach(func() { + conf.GrantType = cf.GrantTypePassword + conf.Username = "test-user" + conf.Password = "test-password" + conf.Secret = "" + }) + + It("returns false without calling introspect", func() { + // No mockServer.Add().Introspect() — any introspect call would fail the test + isAdmin, err := client.IsUserAdmin(ctx, "user-token") + Expect(err).NotTo(HaveOccurred()) + Expect(isAdmin).To(BeFalse()) + }) + }) }) Describe("IsUserSpaceDeveloper", func() { diff --git a/ci/infrastructure/pipeline.yml b/ci/infrastructure/pipeline.yml index a625499cb..1a7a2b127 100644 --- a/ci/infrastructure/pipeline.yml +++ b/ci/infrastructure/pipeline.yml @@ -52,7 +52,7 @@ resources: source: uri: git@github.com:cloudfoundry/app-autoscaler private_key: ((autoscaler-deploy-key-private)) - branch: worktree-APPAUTOSCALER-1033-move-ci-pipelines + branch: main fetch_tags: true paths: - ci/infrastructure diff --git a/ci/infrastructure/scripts/deploy-metricsgateway.sh b/ci/infrastructure/scripts/deploy-metricsgateway.sh index b1653c593..38506cab9 100755 --- a/ci/infrastructure/scripts/deploy-metricsgateway.sh +++ b/ci/infrastructure/scripts/deploy-metricsgateway.sh @@ -75,6 +75,15 @@ EOF echo "${ext_file}" } +function setup_security_group() { + local sg_file + sg_file="$(dirname "${BASH_SOURCE[0]}")/../security-groups/metricsgateway.json" + log "Binding metricsgateway security group to org '${cf_org}'" + cf create-security-group metricsgateway "${sg_file}" || true + cf update-security-group metricsgateway "${sg_file}" + cf bind-security-group metricsgateway "${cf_org}" +} + function deploy_metricsgateway() { local ext_file ext_file="$(build_extension_file)" @@ -94,6 +103,7 @@ cf_login "${system_domain}" cf target -o "${cf_org}" -s "${cf_space}" generate_secrets deploy_metricsgateway +setup_security_group log "metricsgateway deployed:" cf app metricsgateway diff --git a/ci/infrastructure/scripts/deploy-postgres.sh b/ci/infrastructure/scripts/deploy-postgres.sh index b7eb07344..26f8a66e4 100755 --- a/ci/infrastructure/scripts/deploy-postgres.sh +++ b/ci/infrastructure/scripts/deploy-postgres.sh @@ -43,23 +43,10 @@ function deploy () { } function setup_postgres_security_group() { - postgres_ip="$(credhub get -n /bosh-autoscaler/postgres/postgres_host_or_ip --quiet)" - - security_group_json_path="$(mktemp)" - cat < "${security_group_json_path}" -[ - { - "protocol": "tcp", - "destination": "${postgres_ip}/32", - "ports": "5524", - "description": "allow egress to the internal postgres IP" - } -] -EOF - + local sg_file="${script_dir}/../security-groups/postgres.json" cf_login "${system_domain}" - cf create-security-group postgres "${security_group_json_path}" || true - cf update-security-group postgres "${security_group_json_path}" + cf create-security-group postgres "${sg_file}" || true + cf update-security-group postgres "${sg_file}" cf bind-running-security-group postgres } diff --git a/metricsgateway/security-group.json b/ci/infrastructure/security-groups/metricsgateway.json similarity index 100% rename from metricsgateway/security-group.json rename to ci/infrastructure/security-groups/metricsgateway.json diff --git a/metricsforwarder/security-group.json b/ci/infrastructure/security-groups/postgres.json similarity index 56% rename from metricsforwarder/security-group.json rename to ci/infrastructure/security-groups/postgres.json index 8faf51f75..d4518f9cb 100644 --- a/metricsforwarder/security-group.json +++ b/ci/infrastructure/security-groups/postgres.json @@ -1,15 +1,9 @@ [ - { - "protocol": "tcp", - "destination": "10.0.1.0/24", - "ports": "6067", - "description": "Allow syslog traffic from" - }, { "protocol": "tcp", "destination": "10.0.1.0/24", "ports": "5432", - "description": "Allow postgres traffic from" + "description": "Allow postgres traffic from SAP_autoscaler_tests_OSS" }, { "protocol": "tcp", diff --git a/ci/infrastructure/tasks/deploy-metricsgateway.yml b/ci/infrastructure/tasks/deploy-metricsgateway.yml index 21de65ced..f79b4726c 100644 --- a/ci/infrastructure/tasks/deploy-metricsgateway.yml +++ b/ci/infrastructure/tasks/deploy-metricsgateway.yml @@ -13,8 +13,7 @@ inputs: - name: app-autoscaler-mtar params: - CF_ORG: SAP_autoscaler_tests_OSS - CF_SPACE: SAP_autoscaler_tests_OSS + CF_ORG: system run: path: ci/ci/infrastructure/scripts/deploy-metricsgateway.sh diff --git a/metricsforwarder/Makefile b/metricsforwarder/Makefile deleted file mode 100644 index 02a7cdf4b..000000000 --- a/metricsforwarder/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -MAKEFILE_DIR := $(dir $(lastword $(MAKEFILE_LIST))) - -PHONY: set-security-group -set-security-group: - $(eval ORG := $(shell cf target |grep "org:" |cut -d':' -f2 | xargs)) - $(eval SPACE := $(shell cf target |grep "space:" |cut -d':' -f2 | xargs)) - - cf create-security-group metricsforwarder $(MAKEFILE_DIR)/security-group.json - cf update-security-group metricsforwarder $(MAKEFILE_DIR)/security-group.json - cf bind-security-group metricsforwarder $(ORG) diff --git a/metricsgateway/Makefile b/metricsgateway/Makefile deleted file mode 100644 index 31c3a5ae5..000000000 --- a/metricsgateway/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -MAKEFILE_DIR := $(dir $(lastword $(MAKEFILE_LIST))) - -PHONY: set-security-group -set-security-group: - $(eval ORG := $(shell cf target |grep "org:" |cut -d':' -f2 | xargs)) - $(eval SPACE := $(shell cf target |grep "space:" |cut -d':' -f2 | xargs)) - - cf create-security-group metricsgateway $(MAKEFILE_DIR)/security-group.json - cf update-security-group metricsgateway $(MAKEFILE_DIR)/security-group.json - cf bind-security-group metricsgateway $(ORG) diff --git a/mta.tpl.yaml b/mta.tpl.yaml index 773510a2f..ed2278ec3 100644 --- a/mta.tpl.yaml +++ b/mta.tpl.yaml @@ -11,7 +11,7 @@ copyright: Apache License 2.0 version: MTA_VERSION parameters: enable-parallel-deployments: true - java-buildpack: java_buildpack_v5 + java-buildpack: java_buildpack modules: - name: dbtasks type: java diff --git a/scripts/acceptance-tests-config.sh b/scripts/acceptance-tests-config.sh index cc95158b6..3f5f880c7 100755 --- a/scripts/acceptance-tests-config.sh +++ b/scripts/acceptance-tests-config.sh @@ -27,18 +27,38 @@ then cf_admin_password="$(credhub get --quiet --name='/bosh-autoscaler/cf/cf_admin_password')" fi +# Use admin user for main branch, org-manager user for PRs +if is_main_deployment; then + autoscaler_org_manager_user="admin" + autoscaler_org_manager_password="${cf_admin_password}" + skip_service_access_management="false" +else + # For PRs, use dedicated org manager user (password from GH secret) + if [[ -z "${AUTOSCALER_ORG_MANAGER_PASSWORD:-}" ]]; then + echo "ERROR: AUTOSCALER_ORG_MANAGER_PASSWORD is not set (required for PR deployments)" >&2 + exit 1 + fi + autoscaler_org_manager_user="${AUTOSCALER_ORG_MANAGER_USER}" + autoscaler_org_manager_password="${AUTOSCALER_ORG_MANAGER_PASSWORD}" + skip_service_access_management="${SKIP_SERVICE_ACCESS_MANAGEMENT:-true}" +fi + function write_app_config() { local -r config_path="$1" local -r use_existing_organization="$2" local -r use_existing_space="$3" local -r existing_org="$4" local -r existing_space="$5" + local -r existing_user="$6" + local -r existing_user_password="$7" + local -r other_existing_user="$8" + local -r other_existing_user_password="$9" cat > "${config_path}" << EOF { "api": "api.${system_domain}", - "admin_user": "admin", - "admin_password": "${cf_admin_password}", + "admin_user": "${autoscaler_org_manager_user}", + "admin_password": "${autoscaler_org_manager_password}", "apps_domain": "${system_domain}", "skip_ssl_validation": ${skip_ssl_validation}, "use_http": false, @@ -49,6 +69,11 @@ function write_app_config() { "existing_organization": "${existing_org}", "use_existing_space": ${use_existing_space}, "existing_space": "${existing_space}", + "existing_user": "${existing_user}", + "existing_user_password": "${existing_user_password}", + "other_existing_user": "${other_existing_user}", + "other_existing_user_password": "${other_existing_user_password}", + "skip_service_access_management": ${skip_service_access_management}, "aggregate_interval": 120, "default_timeout": 60, "cpu_upper_threshold": ${cpu_upper_threshold}, @@ -67,4 +92,6 @@ EOF write_app_config \ "${ACCEPTANCE_CONFIG_PATH}" \ - "${use_existing_organization}" "${use_existing_space}" "${existing_organization}" "${existing_space}" + "${use_existing_organization}" "${use_existing_space}" "${existing_organization}" "${existing_space}" \ + "${AUTOSCALER_ORG_MANAGER_USER:-}" "${AUTOSCALER_ORG_MANAGER_PASSWORD:-}" \ + "${AUTOSCALER_OTHER_USER:-}" "${AUTOSCALER_OTHER_USER_PASSWORD:-}" diff --git a/scripts/build-extension-file.sh b/scripts/build-extension-file.sh index 82f45118b..e4a1bd4a5 100755 --- a/scripts/build-extension-file.sh +++ b/scripts/build-extension-file.sh @@ -25,11 +25,14 @@ if [ -z "${DEPLOYMENT_NAME}" ]; then exit 1 fi -bbl_login -cf_login +if is_oss_infrastructure; then + bbl_login +fi +cf_deployment_login + cf_target "${AUTOSCALER_ORG}" "${AUTOSCALER_SPACE}" -export SYSTEM_DOMAIN="autoscaler.app-runtime-interfaces.ci.cloudfoundry.org" +export SYSTEM_DOMAIN="${SYSTEM_DOMAIN:-autoscaler.app-runtime-interfaces.ci.cloudfoundry.org}" export CPU_LOWER_THRESHOLD="${CPU_LOWER_THRESHOLD:-"100"}" generate_deployment_secrets() { @@ -50,8 +53,6 @@ load_secrets() { # Map YAML keys → shell variable names, emitting `export VAR=value` lines local exports exports="$(yq ' - "export EVENTGENERATOR_LOG_CACHE_UAA_CLIENT_ID=" + (.eventgenerator_log_cache_uaa_client_id | @sh), - "export EVENTGENERATOR_LOG_CACHE_UAA_CLIENT_SECRET=" + (.eventgenerator_log_cache_uaa_client_secret | @sh), "export CF_ADMIN_PASSWORD=" + (.cf_admin_password | @sh), "export POSTGRES_IP=" + (.postgres_ip | @sh), "export DATABASE_DB_USERNAME=" + (.database_username | @sh), @@ -72,7 +73,8 @@ escape_newlines() { printf '%s' "${1//$'\n'/\\n}"; return; } generate_deployment_secrets -cat << EOF > /tmp/extension-file-secrets.yml.tpl +if is_oss_infrastructure; then + cat << EOF > /tmp/extension-file-secrets.yml.tpl postgres_ip: ((/bosh-autoscaler/postgres/postgres_host_or_ip)) eventgenerator_log_cache_uaa_client_id: eventgenerator_log_cache @@ -91,14 +93,41 @@ database_client_key: ((/bosh-autoscaler/postgres/postgres_server.private_key)) cf_admin_password: ((/bosh-autoscaler/cf/cf_admin_password)) EOF -credhub interpolate -f "/tmp/extension-file-secrets.yml.tpl" > /tmp/mtar-secrets.yml -load_secrets /tmp/mtar-secrets.yml + credhub interpolate -f "/tmp/extension-file-secrets.yml.tpl" > /tmp/mtar-secrets.yml + load_secrets /tmp/mtar-secrets.yml +fi # --- API server & broker --- export APISERVER_HOST="${APISERVER_HOST:-"${DEPLOYMENT_NAME}"}" export APISERVER_INSTANCES="${APISERVER_INSTANCES:-2}" export SERVICEBROKER_HOST="${SERVICEBROKER_HOST:-"${DEPLOYMENT_NAME}servicebroker"}" +# --- CF credentials for components --- +# PR deployments use password grant with org-manager user. +# Main deployments use client_credentials. +if is_pr_deployment; then + if [[ -z "${AUTOSCALER_ORG_MANAGER_PASSWORD:-}" ]]; then + echo "ERROR: AUTOSCALER_ORG_MANAGER_PASSWORD is required for component CF credentials" >&2 + exit 1 + fi + if [[ -z "${AUTOSCALER_OTHER_USER_PASSWORD:-}" ]]; then + echo "ERROR: AUTOSCALER_OTHER_USER_PASSWORD is required for acceptance test credentials" >&2 + exit 1 + fi + cf_grant_type="password" cf_client_id="cf" cf_secret="" + cf_username="${AUTOSCALER_ORG_MANAGER_USER}" cf_password="${AUTOSCALER_ORG_MANAGER_PASSWORD}" +else + cf_grant_type="client_credentials" cf_client_id="autoscaler_client_id" cf_secret="autoscaler_client_secret" + cf_username="" cf_password="" +fi +for component in APISERVER EVENTGENERATOR SCALINGENGINE OPERATOR; do + export "${component}_CF_GRANT_TYPE=${cf_grant_type}" + export "${component}_CF_CLIENT_ID=${cf_client_id}" + export "${component}_CF_SECRET=${cf_secret}" + export "${component}_CF_USERNAME=${cf_username}" + export "${component}_CF_PASSWORD=${cf_password}" +done + # --- Event generator --- export EVENTGENERATOR_CF_HOST="${EVENTGENERATOR_CF_HOST:-"${DEPLOYMENT_NAME}-cf-eventgenerator"}" export EVENTGENERATOR_HOST="${EVENTGENERATOR_HOST:-"${DEPLOYMENT_NAME}-eventgenerator"}" @@ -113,12 +142,11 @@ export METRICSFORWARDER_INSTANCES="${METRICSFORWARDER_INSTANCES:-2}" export USE_METRICSGATEWAY="${USE_METRICSGATEWAY:-true}" export METRICSGATEWAY_HOST="${METRICSGATEWAY_HOST:-"${DEPLOYMENT_NAME}-metricsgateway"}" export METRICSGATEWAY_INSTANCES="${METRICSGATEWAY_INSTANCES:-2}" +export METRICSFORWARDER_METRICS_GATEWAY_URL="${METRICSFORWARDER_METRICS_GATEWAY_URL:-}" AUTOSCALER_ORG_GUID="$(cf org "${AUTOSCALER_ORG}" --guid)" export AUTOSCALER_ORG_GUID # --- Scaling engine --- -export SCALINGENGINE_CF_CLIENT_ID="autoscaler_client_id" -export SCALINGENGINE_CF_CLIENT_SECRET="autoscaler_client_secret" export SCALINGENGINE_CF_HOST="${SCALINGENGINE_CF_HOST:-"${DEPLOYMENT_NAME}-cf-scalingengine"}" export SCALINGENGINE_HOST="${SCALINGENGINE_HOST:-"${DEPLOYMENT_NAME}-scalingengine"}" export SCALINGENGINE_INSTANCES="${SCALINGENGINE_INSTANCES:-2}" @@ -129,22 +157,22 @@ export SCHEDULER_CF_HOST="${SCHEDULER_CF_HOST:-"${DEPLOYMENT_NAME}-cf-scheduler" export SCHEDULER_INSTANCES="${SCHEDULER_INSTANCES:-2}" # --- Operator --- -export OPERATOR_CF_CLIENT_ID="autoscaler_client_id" -export OPERATOR_CF_CLIENT_SECRET="autoscaler_client_secret" export OPERATOR_HOST="${OPERATOR_HOST:-"${DEPLOYMENT_NAME}-operator"}" export OPERATOR_INSTANCES="${OPERATOR_INSTANCES:-2}" # --- Database --- -# Port 5524 is the bosh-deployed postgres proxy port (not the default 5432) -export POSTGRES_URI="postgres://${DATABASE_DB_USERNAME}:${DATABASE_DB_PASSWORD}@${POSTGRES_IP}:5524/${DEPLOYMENT_NAME}?sslmode=verify-ca" -DATABASE_DB_CLIENT_CERT="$(escape_newlines "${DATABASE_DB_CLIENT_CERT}")"; export DATABASE_DB_CLIENT_CERT -DATABASE_DB_CLIENT_KEY="$(escape_newlines "${DATABASE_DB_CLIENT_KEY}")"; export DATABASE_DB_CLIENT_KEY -DATABASE_DB_SERVER_CA="$(escape_newlines "${DATABASE_DB_SERVER_CA}")"; export DATABASE_DB_SERVER_CA - -# --- Syslog client --- -SYSLOG_CLIENT_CERT="$(escape_newlines "${SYSLOG_CLIENT_CERT}")"; export SYSLOG_CLIENT_CERT -SYSLOG_CLIENT_KEY="$(escape_newlines "${SYSLOG_CLIENT_KEY}")"; export SYSLOG_CLIENT_KEY -SYSLOG_CLIENT_CA="$(escape_newlines "${SYSLOG_CLIENT_CA}")"; export SYSLOG_CLIENT_CA +if is_oss_infrastructure; then + # Port 5524 is the bosh-deployed postgres proxy port (not the default 5432) + export POSTGRES_URI="postgres://${DATABASE_DB_USERNAME}:${DATABASE_DB_PASSWORD}@${POSTGRES_IP}:5524/${DEPLOYMENT_NAME}?sslmode=verify-ca" + DATABASE_DB_CLIENT_CERT="$(escape_newlines "${DATABASE_DB_CLIENT_CERT}")"; export DATABASE_DB_CLIENT_CERT + DATABASE_DB_CLIENT_KEY="$(escape_newlines "${DATABASE_DB_CLIENT_KEY}")"; export DATABASE_DB_CLIENT_KEY + DATABASE_DB_SERVER_CA="$(escape_newlines "${DATABASE_DB_SERVER_CA}")"; export DATABASE_DB_SERVER_CA + + # --- Syslog client --- + SYSLOG_CLIENT_CERT="$(escape_newlines "${SYSLOG_CLIENT_CERT}")"; export SYSLOG_CLIENT_CERT + SYSLOG_CLIENT_KEY="$(escape_newlines "${SYSLOG_CLIENT_KEY}")"; export SYSLOG_CLIENT_KEY + SYSLOG_CLIENT_CA="$(escape_newlines "${SYSLOG_CLIENT_CA}")"; export SYSLOG_CLIENT_CA +fi # --- Acceptance tests --- export SKIP_SSL_VALIDATION="${SKIP_SSL_VALIDATION:-true}" @@ -154,10 +182,41 @@ export PERFORMANCE_APP_COUNT="${PERFORMANCE_APP_COUNT:-100}" export PERFORMANCE_APP_PERCENTAGE_TO_SCALE="${PERFORMANCE_APP_PERCENTAGE_TO_SCALE:-30}" export PERFORMANCE_SETUP_WORKERS="${PERFORMANCE_SETUP_WORKERS:-50}" export PERFORMANCE_UPDATE_EXISTING_ORG_QUOTA="${PERFORMANCE_UPDATE_EXISTING_ORG_QUOTA:-true}" +export USE_EXISTING_ORGANIZATION="${USE_EXISTING_ORGANIZATION:-true}" +export EXISTING_ORGANIZATION="${EXISTING_ORGANIZATION:-${AUTOSCALER_ORG}}" +export SKIP_SERVICE_ACCESS_MANAGEMENT="${SKIP_SERVICE_ACCESS_MANAGEMENT:-true}" +export USE_EXISTING_USER="${USE_EXISTING_USER:-true}" +export EXISTING_USER="${EXISTING_USER:-${AUTOSCALER_ORG_MANAGER_USER}}" +export EXISTING_USER_PASSWORD="${EXISTING_USER_PASSWORD:-${AUTOSCALER_ORG_MANAGER_PASSWORD}}" +export KEEP_USER_AT_SUITE_END="${KEEP_USER_AT_SUITE_END:-true}" +# When using a shared existing org (not the deployment org), don't reuse a space from the +# deployment org — let cf-test-helpers create a fresh space in the existing org instead. +if [[ "${EXISTING_ORGANIZATION}" != "${AUTOSCALER_ORG}" ]]; then + export ADD_EXISTING_USER_TO_EXISTING_SPACE="${ADD_EXISTING_USER_TO_EXISTING_SPACE:-false}" + export USE_EXISTING_SPACE="${USE_EXISTING_SPACE:-false}" + export EXISTING_SPACE="${EXISTING_SPACE:-}" +else + export ADD_EXISTING_USER_TO_EXISTING_SPACE="${ADD_EXISTING_USER_TO_EXISTING_SPACE:-true}" + export USE_EXISTING_SPACE="${USE_EXISTING_SPACE:-true}" + export EXISTING_SPACE="${EXISTING_SPACE:-${AUTOSCALER_SPACE}}" +fi # ${default-domain} contains a hyphen so envsubst leaves it untouched (hyphens are invalid in shell variable names) envsubst < "${script_dir}/extension-file.tpl.yaml" > "${extension_file_path}" +# For non-OSS, replace the database UPS with an existing managed CF service +if ! is_oss_infrastructure; then + yq --inplace ' + (.resources[] | select(.name == "database")) = { + "name": "database", + "type": "org.cloudfoundry.existing-service", + "parameters": { + "service-name": env(DEPLOYMENT_NAME) + } + } + ' "${extension_file_path}" +fi + # When not using the metricsgateway, patch the generated file: # - metricsforwarder gets syslog-client binding (direct syslog path) # - metricsgateway is set to 0 instances and its config resource is removed diff --git a/scripts/cleanup-autoscaler-deployments.sh b/scripts/cleanup-autoscaler-deployments.sh index 1d0251aaf..a7e1bb5af 100755 --- a/scripts/cleanup-autoscaler-deployments.sh +++ b/scripts/cleanup-autoscaler-deployments.sh @@ -9,8 +9,24 @@ source "${script_dir}/vars.source.sh" # shellcheck source=scripts/common.sh source "${script_dir}/common.sh" +# Shared orgs used by multiple PRs — must never be cleaned up by this script. +# These orgs are long-lived and other deployments depend on them existing. +PROTECTED_ORGS=("SAP_autoscaler_tests_OSS") + +function is_protected_org(){ + local name="$1" + local org + for org in "${PROTECTED_ORGS[@]}"; do + [[ "${name}" == "${org}" ]] && return 0 + done + return 1 +} + function get_autoscaler_deployments(){ - cf curl /v3/organizations | jq -r '.resources[] | select(.name | contains("autoscaler")) | .name' + cf curl /v3/organizations | jq -r '.resources[] | select(.name | contains("autoscaler")) | .name' \ + | while IFS= read -r org; do + is_protected_org "${org}" || echo "${org}" + done } function main(){ diff --git a/scripts/cleanup-autoscaler.sh b/scripts/cleanup-autoscaler.sh index a2999c23f..6d9884379 100755 --- a/scripts/cleanup-autoscaler.sh +++ b/scripts/cleanup-autoscaler.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 set -euo pipefail @@ -10,14 +11,24 @@ source "${script_dir}/common.sh" function main() { step "cleaning up deployment ${DEPLOYMENT_NAME}" - bbl_login - cf_login + + if is_oss_infrastructure; then + bbl_login + fi + cf_deployment_login cleanup_apps cleanup_acceptance_run cleanup_service_broker - cleanup_credhub - cleanup_db + + if is_oss_infrastructure; then + cleanup_credhub + cleanup_db + else + step "deleting CF service '${DEPLOYMENT_NAME}'" + cf_target "${AUTOSCALER_ORG}" "${AUTOSCALER_SPACE}" + cf delete-service -f "${DEPLOYMENT_NAME}" || echo " - could not delete service '${DEPLOYMENT_NAME}'" + fi } [ "${BASH_SOURCE[0]}" == "${0}" ] && main "$@" diff --git a/scripts/common.sh b/scripts/common.sh index e601de9db..0a771e9ae 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -3,6 +3,23 @@ # # This file is intended to be loaded via the source command. +# Enable debug output if DEBUG=true +if [[ "${DEBUG:-false}" == "true" ]]; then + set -x +fi + +function is_pr_deployment() { + [[ -n "${PR_NUMBER:-}" && "${PR_NUMBER}" != "main" ]] +} + +function is_main_deployment() { + [[ -z "${PR_NUMBER:-}" || "${PR_NUMBER}" == "main" ]] +} + +function is_oss_infrastructure() { + [[ "${INFRASTRUCTURE:-OSS}" == "OSS" ]] +} + function step(){ echo "# $1" } @@ -41,18 +58,30 @@ function bbl_login() { } function cf_login(){ - step 'login to cf' + step 'login to cf as admin' cf api "https://api.${system_domain}" --skip-ssl-validation cf_admin_password="$(credhub get --quiet --name='/bosh-autoscaler/cf/cf_admin_password')" cf auth admin "$cf_admin_password" } -function uaa_login(){ - step "login to uaa" - local uaa_client_secret - uaa_client_secret="$(credhub get --quiet --name='/bosh-autoscaler/cf/uaa_admin_client_secret')" - uaa target "https://uaa.${system_domain}" --skip-ssl-validation - uaa get-client-credentials-token admin -s "${uaa_client_secret}" +# Login to CF with appropriate credentials for deployment operations +# Uses admin on main branch, org-manager on PR branches +function cf_deployment_login(){ + step 'login to cf for deployment operations' + if is_main_deployment; then + cf_login + else + if [[ -z "${AUTOSCALER_ORG_MANAGER_USER:-}" ]]; then + echo "ERROR: AUTOSCALER_ORG_MANAGER_USER is not set" >&2 + return 1 + fi + if [[ -z "${AUTOSCALER_ORG_MANAGER_PASSWORD:-}" ]]; then + echo "ERROR: AUTOSCALER_ORG_MANAGER_PASSWORD is not set" >&2 + return 1 + fi + cf api "https://api.${system_domain}" --skip-ssl-validation + cf auth "${AUTOSCALER_ORG_MANAGER_USER}" "${AUTOSCALER_ORG_MANAGER_PASSWORD}" --origin uaa + fi } function cleanup_acceptance_run(){ @@ -71,24 +100,6 @@ function cleanup_service_broker(){ fi } -function cleanup_bosh_deployment(){ - step "deleting bosh deployment '${deployment_name}'" - retry 3 bosh delete-deployment -d "${deployment_name}" -n -} - -function delete_releases(){ - step "deleting releases" - if [ -n "${deployment_name}" ] - then - for release in $(bosh releases | grep -E "${deployment_name}\s+" | awk '{print $2}') - do - echo "- Deleting bosh release '${release}'" - bosh delete-release -n "app-autoscaler/${release}" & - done - wait - fi -} - function cleanup_db(){ local script_dir script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) @@ -97,31 +108,50 @@ function cleanup_db(){ } -function cleanup_bosh(){ - step "cleaning up bosh" - retry 3 bosh clean-up --all -n -} - function cleanup_credhub(){ step "cleaning up credhub: '/bosh-autoscaler/${deployment_name}/*'" retry 3 credhub delete --path="/bosh-autoscaler/${deployment_name}" } + function cleanup_apps(){ step "cleaning up apps" local mtar_app local space_guid - cf_target "${autoscaler_org}" "${autoscaler_space}" - - space_guid="$(cf space --guid "${autoscaler_space}")" - - local deploy_service_url="https://deploy-service.${system_domain}" + # Don't use cf_target() here — it errors if the space doesn't exist, and during + # cleanup the space may already be deleted. Use v3 API for safe existence check. + local org_guid + local org_output + if ! org_output=$(cf org "${autoscaler_org}" --guid 2>&1); then + if echo "${org_output}" | grep -qi "not found"; then + echo "Org ${autoscaler_org} does not exist, nothing to clean up" + return 0 + fi + echo "WARNING: Failed to query org '${autoscaler_org}': ${org_output}" >&2 + return 0 + fi + org_guid="${org_output}" + local spaces_response + if ! spaces_response=$(cf curl "/v3/spaces?names=${autoscaler_space}&organization_guids=${org_guid}" 2>&1); then + echo "WARNING: Failed to query spaces API: ${spaces_response}" >&2 + return 0 + fi + if echo "${spaces_response}" | jq -e '.errors' >/dev/null 2>&1; then + echo "WARNING: CF API error querying spaces: $(echo "${spaces_response}" | jq -r '.errors[0].detail // "unknown"')" >&2 + return 0 + fi + space_guid=$(echo "${spaces_response}" | jq -r '.resources[0].guid // empty') + if [[ -z "${space_guid}" ]]; then + echo "Space ${autoscaler_space} does not exist, nothing to clean up" + return 0 + fi + if ! cf target -o "${autoscaler_org}" 2>/dev/null; then + echo "WARNING: Could not target org '${autoscaler_org}' — cleanup may be incomplete" >&2 + fi local mtas_response - - # Fetch MTAs from deploy-service (--insecure for self-signed certs) - if mtas_response="$(curl --silent --fail --insecure --header "Authorization: $(cf oauth-token)" "${deploy_service_url}/api/v2/spaces/${space_guid}/mtas" 2>/dev/null)"; then - mtar_app="$(jq -r '.[] | .metadata.id' <<< "${mtas_response}" 2>/dev/null)" || true + if mtas_response="$(curl --silent --fail --insecure --header "Authorization: $(cf oauth-token)" "https://deploy-service.${system_domain}/api/v2/spaces/${space_guid}/mtas" 2>/dev/null)"; then + mtar_app="$(jq -r '.[] | .metadata.id' <<< "${mtas_response}")" || true else echo "Warning: Failed to fetch MTAs from deploy-service, skipping MTA cleanup" fi @@ -180,34 +210,11 @@ function unset_vars() { unset GINKGO_OPTS } -function find_or_create_org(){ - step "finding or creating org" - local org_name="$1" - if ! cf orgs | grep --quiet --regexp="^${org_name}$" - then - cf create-org "${org_name}" - fi - echo "targeting org ${org_name}" - cf target -o "${org_name}" -} - -function find_or_create_space(){ - step "finding or creating space" - local space_name="$1" - if ! cf spaces | grep --quiet --regexp="^${space_name}$" - then - cf create-space "${space_name}" - fi - echo "targeting space ${space_name}" - cf target -s "${space_name}" -} - function cf_target(){ local org_name="$1" local space_name="$2" - find_or_create_org "${org_name}" - find_or_create_space "${space_name}" + cf target -o "${org_name}" -s "${space_name}" } function check_database_exists(){ diff --git a/scripts/cleanup-acceptance.sh b/scripts/create-acceptance-space.sh similarity index 51% rename from scripts/cleanup-acceptance.sh rename to scripts/create-acceptance-space.sh index 2c09f4354..93e0d508e 100755 --- a/scripts/cleanup-acceptance.sh +++ b/scripts/create-acceptance-space.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# shellcheck disable=SC1091 set -euo pipefail @@ -8,10 +9,10 @@ source "${script_dir}/vars.source.sh" # shellcheck source=scripts/common.sh source "${script_dir}/common.sh" -function main() { - bbl_login - cf_login - cleanup_acceptance_run -} +if is_oss_infrastructure; then + bbl_login +fi +cf_deployment_login -[ "${BASH_SOURCE[0]}" == "${0}" ] && main "$@" +cf create-space -o "${AUTOSCALER_ORG}" "${AUTOSCALER_SPACE}" || cf space "${AUTOSCALER_SPACE}" --guid >/dev/null +cf_target "${AUTOSCALER_ORG}" "${AUTOSCALER_SPACE}" diff --git a/scripts/extension-file.tpl.yaml b/scripts/extension-file.tpl.yaml index 6dacf1149..5e875e7f2 100644 --- a/scripts/extension-file.tpl.yaml +++ b/scripts/extension-file.tpl.yaml @@ -79,11 +79,23 @@ modules: ACCEPTANCE_CONFIG_JSON: | { "api": "api.${SYSTEM_DOMAIN}", - "admin_user": "admin", - "admin_password": "${CF_ADMIN_PASSWORD}", + "admin_user": "${EXISTING_USER}", + "admin_password": "${EXISTING_USER_PASSWORD}", "apps_domain": "${SYSTEM_DOMAIN}", "skip_ssl_validation": ${SKIP_SSL_VALIDATION}, "use_http": false, + "use_existing_user": ${USE_EXISTING_USER}, + "existing_user": "${EXISTING_USER}", + "existing_user_password": "${EXISTING_USER_PASSWORD}", + "other_existing_user": "${AUTOSCALER_OTHER_USER}", + "other_existing_user_password": "${AUTOSCALER_OTHER_USER_PASSWORD}", + "keep_user_at_suite_end": ${KEEP_USER_AT_SUITE_END}, + "add_existing_user_to_existing_space": ${ADD_EXISTING_USER_TO_EXISTING_SPACE}, + "use_existing_organization": ${USE_EXISTING_ORGANIZATION}, + "existing_organization": "${EXISTING_ORGANIZATION}", + "use_existing_space": ${USE_EXISTING_SPACE}, + "existing_space": "${EXISTING_SPACE}", + "skip_service_access_management": ${SKIP_SERVICE_ACCESS_MANAGEMENT}, "service_name": "${DEPLOYMENT_NAME}", "service_plan": "autoscaler-free-plan", "service_broker": "${DEPLOYMENT_NAME}", @@ -106,7 +118,7 @@ resources: config: metricsforwarder-config: metrics_gateway: - url: https://${METRICSGATEWAY_HOST}.${default-domain} + url: ${METRICSFORWARDER_METRICS_GATEWAY_URL} health: basic_auth: password: ${METRICSFORWARDER_HEALTH_PASSWORD} @@ -129,8 +141,11 @@ resources: metric_collector_url: https://log-cache.${default-domain} port: "" uaa: - client_id: ${EVENTGENERATOR_LOG_CACHE_UAA_CLIENT_ID} - client_secret: ${EVENTGENERATOR_LOG_CACHE_UAA_CLIENT_SECRET} + client_id: ${EVENTGENERATOR_CF_CLIENT_ID} + client_secret: ${EVENTGENERATOR_CF_SECRET} + grant_type: ${EVENTGENERATOR_CF_GRANT_TYPE} + username: ${EVENTGENERATOR_CF_USERNAME} + password: ${EVENTGENERATOR_CF_PASSWORD} url: https://uaa.${default-domain} skip_ssl_validation: true pool: @@ -150,9 +165,11 @@ resources: upper_threshold: ${CPU_LOWER_THRESHOLD} cf: api: https://api.${SYSTEM_DOMAIN} - grant_type: client_credentials - client_id: autoscaler_client_id - secret: autoscaler_client_secret + grant_type: ${APISERVER_CF_GRANT_TYPE} + client_id: ${APISERVER_CF_CLIENT_ID} + secret: ${APISERVER_CF_SECRET} + username: ${APISERVER_CF_USERNAME} + password: ${APISERVER_CF_PASSWORD} scheduler: scheduler_url: https://${SCHEDULER_CF_HOST}.${default-domain} metrics_forwarder: @@ -188,8 +205,11 @@ resources: password: ${OPERATOR_HEALTH_PASSWORD} cf: api: https://api.${default-domain} + grant_type: ${OPERATOR_CF_GRANT_TYPE} client_id: ${OPERATOR_CF_CLIENT_ID} - secret: ${OPERATOR_CF_CLIENT_SECRET} + secret: ${OPERATOR_CF_SECRET} + username: ${OPERATOR_CF_USERNAME} + password: ${OPERATOR_CF_PASSWORD} scaling_engine: scaling_engine_url: https://${SCALINGENGINE_CF_HOST}.${default-domain} scheduler: @@ -204,8 +224,11 @@ resources: password: ${SCALINGENGINE_HEALTH_PASSWORD} cf: api: https://api.${default-domain} + grant_type: ${SCALINGENGINE_CF_GRANT_TYPE} client_id: ${SCALINGENGINE_CF_CLIENT_ID} - secret: ${SCALINGENGINE_CF_CLIENT_SECRET} + secret: ${SCALINGENGINE_CF_SECRET} + username: ${SCALINGENGINE_CF_USERNAME} + password: ${SCALINGENGINE_CF_PASSWORD} - name: database parameters: diff --git a/scripts/mta-deploy.sh b/scripts/mta-deploy.sh index ef70fd3f4..608e9fda2 100755 --- a/scripts/mta-deploy.sh +++ b/scripts/mta-deploy.sh @@ -35,8 +35,9 @@ fi pushd "${autoscaler_dir}" > /dev/null bbl_login - make -f metricsforwarder/Makefile set-security-group - make -f metricsgateway/Makefile set-security-group + cf_deployment_login + cf_target "${AUTOSCALER_ORG}" "${AUTOSCALER_SPACE}" + echo "Deploying as user: $(cf target | grep 'user:' | awk '{print $2}')" echo "Deploying with extension file: ${EXTENSION_FILE}" cf deploy "${DEST}/${MTAR_FILENAME}" --version-rule ALL -f --delete-services -e "${EXTENSION_FILE}" -m "${MODULES}" @@ -46,13 +47,17 @@ popd > /dev/null # Extract broker password from the generated extension file (baked in by build-extension-file.sh) SERVICE_BROKER_PASSWORD="$(yq '.resources[] | select(.name == "apiserver-config") | .parameters.config."apiserver-config".broker_credentials[0].broker_password' "${EXTENSION_FILE}")" -cf_login - -set +e -existing_service_broker="$(cf curl v3/service_brokers | jq --raw-output \ +broker_response="$(cf curl v3/service_brokers 2>&1)" || { + echo "ERROR: Failed to query service brokers: ${broker_response}" >&2 + exit 1 +} +existing_service_broker="$(echo "${broker_response}" | jq --raw-output \ --arg service_broker_name "${deployment_name:-}" \ - '.resources[] | select(.name == $service_broker_name) | .name')" -set -e + '.resources[] | select(.name == $service_broker_name) | .name')" || { + echo "ERROR: Failed to parse service broker response" >&2 + echo " Response was: ${broker_response}" >&2 + exit 1 +} if [[ -n "${existing_service_broker}" ]]; then echo "Service Broker ${existing_service_broker} already exists" @@ -65,6 +70,10 @@ if [[ -n "${existing_service_broker}" ]]; then fi echo "Creating service broker ${deployment_name:-} at 'https://${service_broker_name:-}.${system_domain:-}'" -cf create-service-broker "${deployment_name:-}" autoscaler-broker-user "${SERVICE_BROKER_PASSWORD}" "https://${service_broker_name:-}.${system_domain:-}" +space_scoped_flag="" +if is_pr_deployment; then + space_scoped_flag="--space-scoped" +fi +cf create-service-broker "${deployment_name:-}" autoscaler-broker-user "${SERVICE_BROKER_PASSWORD}" "https://${service_broker_name:-}.${system_domain:-}" ${space_scoped_flag} cf logout diff --git a/scripts/provision-db-service.sh b/scripts/provision-db-service.sh new file mode 100755 index 000000000..281395975 --- /dev/null +++ b/scripts/provision-db-service.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 + +set -euo pipefail + +script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)" +# shellcheck source=scripts/vars.source.sh +source "${script_dir}/vars.source.sh" +# shellcheck source=scripts/common.sh +source "${script_dir}/common.sh" + +if [[ -z "${POSTGRES_SERVICE_OFFERING:-}" ]]; then + echo "ERROR: POSTGRES_SERVICE_OFFERING is not set" >&2 + exit 1 +fi +if [[ -z "${POSTGRES_SERVICE_PLAN:-}" ]]; then + echo "ERROR: POSTGRES_SERVICE_PLAN is not set" >&2 + exit 1 +fi + +cf_deployment_login +cf_target "${AUTOSCALER_ORG}" "${AUTOSCALER_SPACE}" + +echo "Creating service '${DEPLOYMENT_NAME}' (${POSTGRES_SERVICE_OFFERING}/${POSTGRES_SERVICE_PLAN})" +cf create-service "${POSTGRES_SERVICE_OFFERING}" "${POSTGRES_SERVICE_PLAN}" "${DEPLOYMENT_NAME}" diff --git a/scripts/provision_db.sh b/scripts/provision_db.sh index f1a0700c4..97872ee81 100755 --- a/scripts/provision_db.sh +++ b/scripts/provision_db.sh @@ -16,9 +16,7 @@ DEPLOYMENT_NAME="${DEPLOYMENT_NAME:-autoscaler}" DB_USER="${DB_USER:-vcap}" APP_DB_USER="${APP_DB_USER:-pgadmin}" -if [ -n "${BBL_STATE_PATH:-}" ]; then - bbl_login -fi +bbl_login echo "Provisioning database '${DEPLOYMENT_NAME}' on Postgres instance ${POSTGRES_INSTANCE} in deployment ${BOSH_DEPLOYMENT}" @@ -55,4 +53,4 @@ else echo "You may need to grant permissions manually" >&2 fi -echo "Done" \ No newline at end of file +echo "Done" diff --git a/scripts/run-mta-acceptance-tests.sh b/scripts/run-mta-acceptance-tests.sh index b520ab199..3dc39628b 100755 --- a/scripts/run-mta-acceptance-tests.sh +++ b/scripts/run-mta-acceptance-tests.sh @@ -44,6 +44,20 @@ validate_app() { [[ -n "${APP_GUID}" ]] || { echo "ERROR: acceptance-tests app not found. Run: make mta-deploy"; exit 1; } echo " ✓ App found (GUID: ${APP_GUID:0:8})" + + if [[ "${DEBUG:-false}" == "true" ]]; then + local app_state + app_state=$(cf curl "/v3/apps/${APP_GUID}" 2>/dev/null | jq -r '.state // "UNKNOWN"') + echo " App state: ${app_state}" + + local droplet + droplet=$(cf curl "/v3/apps/${APP_GUID}/droplets/current" 2>/dev/null | jq -r '.guid // empty') + if [[ -n "${droplet}" ]]; then + echo " ✓ Current droplet: ${droplet:0:8}" + else + echo " ✗ WARNING: No current droplet assigned" + fi + fi } launch_task() { @@ -55,13 +69,18 @@ launch_task() { local guid local cf_output - cf_output=$(cf curl "/v3/apps/${APP_GUID}/tasks" -X POST -d "$(jq -n --arg n "${task_name}" --arg c "${cmd}" \ - '{name:$n,command:$c,memory_in_mb:2048,disk_in_mb:2048}')" 2>&1) - guid=$(echo "$cf_output" | jq -r '.guid // empty') + local payload + payload=$(jq -n --arg n "${task_name}" --arg c "${cmd}" \ + '{name:$n,command:$c,memory_in_mb:2048,disk_in_mb:2048}') + cf_output=$(cf curl "/v3/apps/${APP_GUID}/tasks" -X POST -d "${payload}" 2>&1) + guid=$(echo "$cf_output" | jq -r '.guid // empty' 2>/dev/null) if [[ -n "${guid}" ]]; then echo "${guid}" else echo "ERROR: Failed to launch task for ${suite}" >&2 + echo " API response: ${cf_output}" >&2 + echo " APP_GUID: ${APP_GUID}" >&2 + echo " Payload: ${payload}" >&2 echo "FAILED" fi } @@ -75,17 +94,12 @@ get_pr_tasks() { guid_list="${LAUNCHED_TASK_GUIDS[*]}" local tasks_json - if ! cf curl "/v3/tasks?guids=${guid_list}" >/dev/null 2>&1; then - return 0 - fi - - tasks_json=$(cf curl "/v3/tasks?guids=${guid_list}" 2>/dev/null) + tasks_json=$(cf curl "/v3/tasks?guids=${guid_list}" 2>/dev/null) || return 0 echo "$tasks_json" | jq -r '.resources[]? | "\(.name):\(.state)"' 2>/dev/null || true } has_unfinished_tasks() { - local tasks - tasks=$(get_pr_tasks) + local tasks="${1:-$(get_pr_tasks)}" if echo "$tasks" | grep -qE ":(RUNNING|PENDING)$"; then return 0 fi @@ -96,18 +110,16 @@ has_unfinished_tasks() { has_failed_tasks() { local tasks="${FINAL_TASK_STATE:-$(get_pr_tasks)}" - [[ -n "$tasks" ]] && echo "$tasks" | grep -qvE ":SUCCEEDED$" + echo "$tasks" | grep -v '^[[:space:]]*$' | grep -qvE ":SUCCEEDED$" } format_time() { printf "%dm%02ds" $(($1/60)) $(($1%60)); } show_status() { local poll=$1 + local tasks="${2:-$(get_pr_tasks)}" local elapsed=$(($(date +%s) - script_start_time)) - local tasks - tasks=$(get_pr_tasks) - # Build compact status line local running=0 pending=0 succeeded=0 failed=0 while IFS=: read -r name state; do @@ -174,8 +186,10 @@ main() { step "Running MTA acceptance tests: ${SUITES}" validate bbl_login - cf_login + + cf_deployment_login cf_target "${autoscaler_org}" "${autoscaler_space}" + validate_app # Launch tasks @@ -220,14 +234,17 @@ main() { # Poll until all tasks are finished step "Monitoring tasks" local poll=0 - while has_unfinished_tasks; do + local current_tasks + current_tasks=$(get_pr_tasks) + while has_unfinished_tasks "${current_tasks}"; do poll=$((poll + 1)) [[ $(($(date +%s) - script_start_time)) -gt ${MAX_WAIT_TIME} ]] && { echo "ERROR: Timeout after ${MAX_WAIT_TIME}s" break } - show_status ${poll} + show_status ${poll} "${current_tasks}" sleep ${POLL_INTERVAL} + current_tasks=$(get_pr_tasks) done # Final summary diff --git a/scripts/create-org-manager-user.sh b/scripts/setup-acceptance-tests.sh similarity index 67% rename from scripts/create-org-manager-user.sh rename to scripts/setup-acceptance-tests.sh index b3f94ca72..e6433d800 100755 --- a/scripts/create-org-manager-user.sh +++ b/scripts/setup-acceptance-tests.sh @@ -21,7 +21,12 @@ function create_cf_test_user() { cf delete-user -f "${username}" || true cf create-user "${username}" "${password}" cf set-org-role "${username}" "${AUTOSCALER_ORG}" OrgManager - cf set-space-role "${username}" "${AUTOSCALER_ORG}" "${AUTOSCALER_SPACE}" SpaceDeveloper + + local existing_org="${EXISTING_ORGANIZATION:-}" + if [[ -n "${existing_org}" && "${existing_org}" != "${AUTOSCALER_ORG}" ]]; then + log "Granting OrgManager role in existing org: ${existing_org}" + cf set-org-role "${username}" "${existing_org}" OrgManager + fi log "Writing username to GitHub repo variable ${var_name}" gh variable set "${var_name}" --body "${username}" --repo "${repo}" @@ -35,11 +40,21 @@ function create_cf_test_user() { function main() { bbl_login cf_login + + # Ensure org and space exist (first-time setup for main deployments) + cf create-org "${AUTOSCALER_ORG}" || cf org "${AUTOSCALER_ORG}" --guid >/dev/null + cf target -o "${AUTOSCALER_ORG}" + cf create-space "${AUTOSCALER_SPACE}" || cf space "${AUTOSCALER_SPACE}" --guid >/dev/null cf_target "${AUTOSCALER_ORG}" "${AUTOSCALER_SPACE}" local repo repo="$(gh repo view --json nameWithOwner --jq '.nameWithOwner')" create_cf_test_user "${repo}" "${AUTOSCALER_ORG_MANAGER_USER}" AUTOSCALER_ORG_MANAGER_USER AUTOSCALER_ORG_MANAGER_PASSWORD "Creating org manager CF user" create_cf_test_user "${repo}" "${AUTOSCALER_OTHER_USER}" AUTOSCALER_OTHER_USER AUTOSCALER_OTHER_USER_PASSWORD "Creating other-user for acceptance tests" + + step "Setting METRICSFORWARDER_METRICS_GATEWAY_URL GitHub variable" + gh variable set METRICSFORWARDER_METRICS_GATEWAY_URL \ + --body "https://system-production-metricsgateway.autoscaler.app-runtime-interfaces.ci.cloudfoundry.org" \ + --repo "${repo}" } [[ "${BASH_SOURCE[0]}" == "${0}" ]] && main "$@" diff --git a/scripts/vars.source.sh b/scripts/vars.source.sh index 479ed3ad6..8c609dd0d 100644 --- a/scripts/vars.source.sh +++ b/scripts/vars.source.sh @@ -71,6 +71,17 @@ debug "AUTOSCALER_SPACE: ${AUTOSCALER_SPACE}" log "set up vars: AUTOSCALER_SPACE=${AUTOSCALER_SPACE}" autoscaler_space="${AUTOSCALER_SPACE}" +export AUTOSCALER_ORG_MANAGER_USER="${AUTOSCALER_ORG_MANAGER_USER:-org-manager-user}" +debug "AUTOSCALER_ORG_MANAGER_USER: ${AUTOSCALER_ORG_MANAGER_USER}" +log "set up vars: AUTOSCALER_ORG_MANAGER_USER=${AUTOSCALER_ORG_MANAGER_USER}" + +export AUTOSCALER_OTHER_USER="${AUTOSCALER_OTHER_USER:-other-user}" +debug "AUTOSCALER_OTHER_USER: ${AUTOSCALER_OTHER_USER}" + +export INFRASTRUCTURE="${INFRASTRUCTURE:-OSS}" +debug "INFRASTRUCTURE: ${INFRASTRUCTURE}" +log "set up vars: INFRASTRUCTURE=${INFRASTRUCTURE}" + export SYSTEM_DOMAIN="${SYSTEM_DOMAIN:-"autoscaler.app-runtime-interfaces.ci.cloudfoundry.org"}" debug "SYSTEM_DOMAIN: ${SYSTEM_DOMAIN}" system_domain="${SYSTEM_DOMAIN}"