diff --git a/Makefile b/Makefile index f6fc9e7a..d21b2c80 100644 --- a/Makefile +++ b/Makefile @@ -1,202 +1,181 @@ -SHELL := /bin/bash - -.PHONY: install fmt vet test devchecknotest devcheck importfmtlint golangcilint starttestcontainer removetestcontainer spincontainer openlocalwebapi openapp - -default: install - -install: - @echo -n "Running 'go mod tidy' to ensure all dependencies are up to date..." - @if go mod tidy; then \ - echo " SUCCESS"; \ - else \ - echo " FAILED"; \ - exit 1; \ - fi - - @echo -n "Running 'go install' to install pingcli..." - @if go install .; then \ - echo " SUCCESS"; \ - else \ - echo " FAILED"; \ - exit 1; \ - fi - -fmt: - @echo -n "Running 'go fmt' to format the code..." - @if go fmt ./...; then \ - echo " SUCCESS"; \ - else \ - echo " FAILED"; \ - exit 1; \ - fi - -vet: - @echo -n "Running 'go vet' to check for potential issues..." - @if go vet ./...; then \ - echo " SUCCESS"; \ - else \ - echo " FAILED"; \ - exit 1; \ - fi - -test: --checktestenvvars --test-cmd --test-internal-commands --test-internal-configuration --test-internal-connector --test-internal-customtypes --test-internal-input --test-internal-profiles - ---checktestenvvars: - @echo -n "Checking for required environment variables to run pingcli tests..." - @test -n "$$TEST_PINGONE_ENVIRONMENT_ID" || { echo " FAILED"; echo "TEST_PINGONE_ENVIRONMENT_ID environment variable is not set.\n\nCreate/Specify an unconfigured PingOne environment to test Ping CLI with. The following services are required: PingOne SSO, PingOne MFA, PingOne Protect, PingOne DaVinci, PingOne Authorize, and PingFederate"; exit 1; } - @test -n "$$TEST_PINGONE_REGION_CODE" || { echo " FAILED"; echo "TEST_PINGONE_REGION_CODE environment variable is not set.\n\nCreate/Specify an unconfigured PingOne environment to test Ping CLI with. The following services are required: PingOne SSO, PingOne MFA, PingOne Protect, PingOne DaVinci, PingOne Authorize, and PingFederate"; exit 1; } - @test -n "$$TEST_PINGONE_WORKER_CLIENT_ID" || { echo " FAILED"; echo "TEST_PINGONE_WORKER_CLIENT_ID environment variable is not set.\n\nCreate/Specify a worker applicaiton in the unconfigured PingOne environment with all admin roles to test Ping CLI with"; exit 1; } - @test -n "$$TEST_PINGONE_WORKER_CLIENT_SECRET" || { echo " FAILED"; echo "TEST_PINGONE_WORKER_CLIENT_SECRET environment variable is not set.\n\nCreate/Specify a worker applicaiton in an unconfigured PingOne environment with all admin roles to test Ping CLI with"; exit 1; } - @echo " SUCCESS" - ---test-cmd: - @echo "Running tests for cmd..." - @go test -count=1 ./cmd/... - ---test-internal-commands: - @echo "Running tests for internal/commands..." - @go test -count=1 ./internal/commands/... - ---test-internal-configuration: - @echo "Running tests for internal/configuration..." - @go test -count=1 ./internal/configuration/... - ---test-internal-connector: - @echo "Running tests for internal/connector..." - - @# Test each connector package separately to avoid configuration collision - @go test -count=1 ./internal/connector - - @# Test the resources within each connector first - @go test -count=1 ./internal/connector/pingfederate/resources - @go test -count=1 ./internal/connector/pingone/authorize/resources - @go test -count=1 ./internal/connector/pingone/mfa/resources - @go test -count=1 ./internal/connector/pingone/platform/resources - @go test -count=1 ./internal/connector/pingone/protect/resources - @go test -count=1 ./internal/connector/pingone/sso/resources - - @# Test the connectors itegration terraform plan tests - @go test -count=1 ./internal/connector/pingfederate - @go test -count=1 ./internal/connector/pingone/authorize - @go test -count=1 ./internal/connector/pingone/mfa - @go test -count=1 ./internal/connector/pingone/platform - @go test -count=1 ./internal/connector/pingone/protect - @go test -count=1 ./internal/connector/pingone/sso - ---test-internal-customtypes: - @echo "Running tests for internal/customtypes..." - @go test -count=1 ./internal/customtypes/... - ---test-internal-input: - @echo "Running tests for internal/input..." - @go test -count=1 ./internal/input/... - ---test-internal-profiles: - @echo "Running tests for internal/profiles..." - @go test -count=1 ./internal/profiles/... - -devchecknotest: install importfmtlint fmt vet golangcilint - -devcheck: devchecknotest spincontainer test removetestcontainer - -importfmtlint: - @echo -n "Running 'impi' to format import ordering..." - @if go tool impi --skip internal/proto/pingcli_command --local . --scheme stdThirdPartyLocal ./...; then \ - echo " SUCCESS"; \ - else \ - echo " FAILED"; \ - exit 1; \ - fi - -golangcilint: - @echo -n "Running 'golangci-lint' to check for code quality issues... " - @# Clear the cache for every run, so that the linter outputs the same results as the GH Actions workflow - @if go tool golangci-lint cache clean && go tool golangci-lint run --timeout 5m ./...; then \ - echo "SUCCESS"; \ - else \ - echo "FAILED"; \ - exit 1; \ - fi - -starttestcontainer: --checkpfcontainerenvvars --checkdocker --dockerrunpf --waitforpfhealthy - ---checkpfcontainerenvvars: - @echo -n "Checking for required environment variables to run PingFederate container..." - @test -n "$$TEST_PING_IDENTITY_DEVOPS_USER" || { echo " FAILED"; echo "TEST_PING_IDENTITY_DEVOPS_USER environment variable is not set.\n\nNot Registered? Register for the DevOps Program at https://devops.pingidentity.com/how-to/devopsRegistration/."; exit 1; } - @test -n "$$TEST_PING_IDENTITY_DEVOPS_KEY" || { echo " FAILED"; echo "TEST_PING_IDENTITY_DEVOPS_KEY environment variable is not set.\n\nNot Registered? Register for the DevOps Program at https://devops.pingidentity.com/how-to/devopsRegistration/."; exit 1; } - @test "YES" = "$$TEST_PING_IDENTITY_ACCEPT_EULA" || { echo " FAILED"; echo "You must accept the EULA to use the PingFederate container. Set TEST_PING_IDENTITY_ACCEPT_EULA=YES to continue."; exit 1; } - @echo " SUCCESS" - ---checkdocker: - @echo -n "Checking if Docker is running..." - @docker info > /dev/null 2>&1 || { echo " FAILED"; echo "Docker is not running. Please start Docker and try again."; exit 1; } - @echo " SUCCESS" - ---dockerrunpf: - @echo -n "Starting the PingFederate container..." - @docker run --name pingcli_test_pingfederate_container \ - -d -p 9031:9031 \ - -p 9999:9999 \ - --env PING_IDENTITY_DEVOPS_USER="$${TEST_PING_IDENTITY_DEVOPS_USER}" \ - --env PING_IDENTITY_DEVOPS_KEY="$${TEST_PING_IDENTITY_DEVOPS_KEY}" \ - --env PING_IDENTITY_ACCEPT_EULA="$${TEST_PING_IDENTITY_ACCEPT_EULA}" \ +# Use .ONESHELL to treat each recipe as a single shell script. +# This simplifies complex commands and makes the file more readable. +.ONESHELL: +SHELL := $(shell which bash || which sh) + +# ==================================================================================== +# VARIABLES +# ==================================================================================== + +# Go variables +GOCMD := go +GOTIDY := $(GOCMD) mod tidy +GOINSTALL := $(GOCMD) install . +GOFMT := $(GOCMD) fmt ./... +GOVET := $(GOCMD) vet ./... +GOTEST := $(GOCMD) test -count=1 +GOLANGCI_LINT := $(GOCMD) tool golangci-lint +IMPI := $(GOCMD) tool impi + +# Find all directories containing Go test files. +TEST_DIRS := $(shell find . -type f -name '*_test.go' -print0 | xargs -0 -n1 dirname | sort -u) + +# Docker variables +DOCKER := docker +CONTAINER_NAME := pingcli_test_pingfederate_container + +# Cross-platform 'open' command. +OPEN_CMD := xdg-open +ifeq ($(shell uname), Darwin) + OPEN_CMD := open +endif + +# Helper to check for required environment variables. +# If not set, it stops execution with an error. +define check_env + $(if $(value $1),,$(error ERROR: Environment variable '$1' is not set. $(2))) +endef + +# ==================================================================================== +# PHONY TARGETS +# ==================================================================================== + +.PHONY: help default install fmt vet test importfmtlint golangcilint devcheck devchecknotest +.PHONY: starttestcontainer removetestcontainer spincontainer openlocalwebapi openapp protogen +.PHONY: _check_env _check_ping_env _check_docker _run_pf_container _wait_for_pf _stop_pf_container + +# ==================================================================================== +# USER-FACING COMMANDS +# ==================================================================================== + +# Set the default goal to 'help'. +default: help + +help: ## Display this help message + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) + +install: ## Install the application binaries + @echo " > Tidy: Ensuring dependencies are up to date..." + $(GOTIDY) + echo "✅ Dependencies are up to date." + echo " > Install: Building and installing application..." + $(GOINSTALL) + echo "✅ Application installed." + +fmt: ## Format Go source code + @echo " > Fmt: Formatting Go code..." + $(GOFMT) + echo "✅ Go code formatted." + +vet: ## Run go vet to catch suspicious constructs + @echo " > Vet: Analyzing source code for potential issues..." + $(GOVET) + echo "✅ No issues found." + +importfmtlint: ## Format Go import ordering + @echo " > ImportFmt: Formatting import statements..." + $(IMPI) --skip internal/proto/pingcli_command --local . --scheme stdThirdPartyLocal ./... + echo "✅ Import statements formatted." + +golangcilint: ## Run golangci-lint for comprehensive code analysis + @echo " > Lint: Running golangci-lint..." + $(GOLANGCI_LINT) cache clean + $(GOLANGCI_LINT) run --timeout 5m ./... + echo "✅ No linting issues found." + +protogen: ## Generate Go code from .proto files + @echo " > Protogen: Generating gRPC code from proto files..." + protoc --proto_path=./internal/proto --go_out=./internal --go-grpc_out=./internal ./internal/proto/*.proto + echo "✅ gRPC code generated." + +test: _check_ping_env ## Run all tests + @echo " > Test: Running all Go tests..." + set -e + for dir in $(TEST_DIRS); do + echo " -> $$dir" + $(GOTEST) $$dir + done + echo "✅ All tests passed." + +devcheck: install importfmtlint fmt vet golangcilint spincontainer test removetestcontainer ## Run the full suite of development checks and tests + @echo "✅ All development checks passed successfully." + +devchecknotest: install importfmtlint fmt vet golangcilint ## Run all development checks except tests + @echo "✅ All development checks (no tests) passed successfully." + +# ==================================================================================== +# DOCKER & CONTAINER COMMANDS +# ==================================================================================== + +starttestcontainer: _check_docker _check_env _run_pf_container _wait_for_pf ## Start the PingFederate test container +removetestcontainer: _check_docker _stop_pf_container ## Stop and remove the PingFederate test container +spincontainer: removetestcontainer starttestcontainer ## Re-spin the test container (remove and start) + +openlocalwebapi: ## Open the PingFederate Admin API docs in a browser + @echo " > Browser: Opening PingFederate Admin API docs..." + $(OPEN_CMD) "https://localhost:9999/pf-admin-api/api-docs/#/" + echo "✅ Opened PingFederate Admin API docs." + +openapp: ## Open the PingFederate Admin Console in a browser + @echo " > Browser: Opening PingFederate Admin Console..." + $(OPEN_CMD) "https://localhost:9999/pingfederate/app" + echo "✅ Opened PingFederate Admin Console." + +# ==================================================================================== +# INTERNAL HELPER TARGETS (Not intended for direct use) +# ==================================================================================== + +_check_env: + @echo " > Env: Checking Docker container variables..." + $(call check_env,TEST_PING_IDENTITY_DEVOPS_USER,See https://devops.pingidentity.com/how-to/devopsRegistration/) + $(call check_env,TEST_PING_IDENTITY_DEVOPS_KEY,See https://devops.pingidentity.com/how-to/devopsRegistration/) + $(call check_env,TEST_PING_IDENTITY_ACCEPT_EULA,Set to 'YES' to accept the EULA.) + echo "✅ Required Docker variables are set." + +_check_ping_env: + @echo " > Env: Checking PingOne test variables..." + $(call check_env,TEST_PINGONE_ENVIRONMENT_ID,Specify an unconfigured PingOne environment.) + $(call check_env,TEST_PINGONE_REGION_CODE,Specify the region for the PingOne environment.) + $(call check_env,TEST_PINGONE_WORKER_CLIENT_ID,Specify a worker app client ID with admin roles.) + $(call check_env,TEST_PINGONE_WORKER_CLIENT_SECRET,Specify the secret for the worker app.) + echo "✅ Required PingOne test variables are set." + +_check_docker: + @echo " > Docker: Checking if the Docker daemon is running..." + $(DOCKER) info > /dev/null 2>&1 + echo "✅ Docker daemon is running." + +_run_pf_container: + @echo " > Docker: Starting the PingFederate container..." + $(DOCKER) run --name $(CONTAINER_NAME) \ + -d -p 9031:9031 -p 9999:9999 \ + --env PING_IDENTITY_DEVOPS_USER="$(TEST_PING_IDENTITY_DEVOPS_USER)" \ + --env PING_IDENTITY_DEVOPS_KEY="$(TEST_PING_IDENTITY_DEVOPS_KEY)" \ + --env PING_IDENTITY_ACCEPT_EULA="$(TEST_PING_IDENTITY_ACCEPT_EULA)" \ --env CREATE_INITIAL_ADMIN_USER="true" \ -v $$(pwd)/internal/testing/pingfederate_container_files/deploy:/opt/in/instance/server/default/deploy \ - pingidentity/pingfederate:latest > /dev/null 2>&1 || { echo " FAILED"; echo "Failed to start the PingFederate container. Please check your Docker setup."; exit 1; } - @echo " SUCCESS" - ---waitforpfhealthy: - @echo -n "Waiting for the PingFederate container to become healthy..." - @timeout=240; \ - while test $$timeout -gt 0; do \ - status=$$(docker inspect --format='{{json .State.Health.Status}}' pingcli_test_pingfederate_container 2>/dev/null || echo ""); \ - if test "$$status" = '"healthy"'; then \ - echo " SUCCESS"; \ - exit 0; \ - fi; \ - sleep 1; \ - timeout=$$((timeout - 1)); \ - done; \ - echo " FAILED"; \ - echo "PingFederate container did not become healthy within the timeout period."; \ - echo "Current status: $$status"; \ - docker logs pingcli_test_pingfederate_container || echo "No logs available."; \ + pingidentity/pingfederate:latest + echo "✅ PingFederate container started." + +_wait_for_pf: + @echo " > Docker: Waiting for container to become healthy (up to 4 minutes)..." + set -e + timeout=240 + while test $$timeout -gt 0; do + status=$$(docker inspect --format='{{json .State.Health.Status}}' $(CONTAINER_NAME) 2>/dev/null || echo "") + if test "$$status" = '"healthy"'; then + echo "✅ Docker: Container is healthy." + exit 0 + fi + sleep 1 + timeout=$$((timeout - 1)) + done + echo "Error: Container did not become healthy within the timeout period." + $(DOCKER) logs $(CONTAINER_NAME) || echo "No logs available." exit 1 -removetestcontainer: --checkdocker - @echo -n "Stopping and removing the PingFederate container..." - @if docker rm -f pingcli_test_pingfederate_container > /dev/null 2>&1; then \ - echo " SUCCESS"; \ - else \ - echo " FAILED"; \ - exit 1; \ - fi - -spincontainer: removetestcontainer starttestcontainer - -openlocalwebapi: - @echo -n "Opening the PingFederate Admin API documentation in the default web browser..." - @if open "https://localhost:9999/pf-admin-api/api-docs/#/"; then \ - echo " SUCCESS"; \ - else \ - echo " FAILED"; \ - exit 1; \ - fi - -openapp: - @echo -n "Opening the PingFederate Admin Console in the default web browser..." - @if open "https://localhost:9999/pingfederate/app"; then \ - echo " SUCCESS"; \ - else \ - echo " FAILED"; \ - exit 1; \ - fi - -protogen: - @echo -n "Running 'protoc' to generate Go code from proto files..." - @if protoc --proto_path=./internal/proto --go_out=./internal --go-grpc_out=./internal ./internal/proto/*.proto; then \ - echo " SUCCESS"; \ - else \ - echo " FAILED"; \ - exit 1; \ - fi +_stop_pf_container: + @echo " > Docker: Stopping and removing previous container..." + # Using '|| true' correctly prevents an error if the container doesn't exist. + $(DOCKER) rm -f $(CONTAINER_NAME) 2>/dev/null || true + echo "✅ Previous container removed." \ No newline at end of file diff --git a/internal/commands/license/license_internal_test.go b/internal/commands/license/license_internal_test.go index fa3a2282..9d5bb51f 100644 --- a/internal/commands/license/license_internal_test.go +++ b/internal/commands/license/license_internal_test.go @@ -103,8 +103,8 @@ func Test_runLicenseRequest_Success(t *testing.T) { t.Context(), customtypes.ENUM_LICENSE_PRODUCT_PING_FEDERATE, "13.0", - os.Getenv("TEST_PINGCLI_DEVOPS_USER"), - os.Getenv("TEST_PINGCLI_DEVOPS_KEY")) + os.Getenv("TEST_PING_IDENTITY_DEVOPS_USER"), + os.Getenv("TEST_PING_IDENTITY_DEVOPS_KEY")) testutils.CheckExpectedError(t, err, nil) if licenseData == "" { diff --git a/internal/connector/pingone/platform/pingone_platform_connector_test.go b/internal/connector/pingone/platform/pingone_platform_connector_test.go index e11a7f7a..91b72b87 100644 --- a/internal/connector/pingone/platform/pingone_platform_connector_test.go +++ b/internal/connector/pingone/platform/pingone_platform_connector_test.go @@ -178,6 +178,7 @@ func TestPlatformTerraformPlan(t *testing.T) { testableResource: pingone_platform_testable_resources.NotificationSettingsEmail(t, clientInfo), ignoredErrors: []string{ "Error: Missing Configuration for Required Attribute", + "Error: Invalid Attribute Combination", }, }, { diff --git a/internal/testing/testutils_koanf/koanf_utils.go b/internal/testing/testutils_koanf/koanf_utils.go index 93318192..a4e0ef56 100644 --- a/internal/testing/testutils_koanf/koanf_utils.go +++ b/internal/testing/testutils_koanf/koanf_utils.go @@ -146,8 +146,8 @@ func getDefaultConfigFileContents() string { return fmt.Sprintf(defaultConfigFileContentsPattern, outputDirectoryReplacement, customtypes.ENUM_EXPORT_SERVICE_PINGONE_PROTECT, - os.Getenv("TEST_PINGCLI_DEVOPS_USER"), - os.Getenv("TEST_PINGCLI_DEVOPS_KEY"), + os.Getenv("TEST_PING_IDENTITY_DEVOPS_USER"), + os.Getenv("TEST_PING_IDENTITY_DEVOPS_KEY"), os.Getenv("TEST_PINGONE_REGION_CODE"), os.Getenv("TEST_PINGONE_WORKER_CLIENT_ID"), os.Getenv("TEST_PINGONE_WORKER_CLIENT_SECRET"), diff --git a/internal/testing/testutils_terraform/terraform_utils.go b/internal/testing/testutils_terraform/terraform_utils.go index 31da9849..ebd34939 100644 --- a/internal/testing/testutils_terraform/terraform_utils.go +++ b/internal/testing/testutils_terraform/terraform_utils.go @@ -162,7 +162,7 @@ func InitPingOneTerraform(t *testing.T) { required_providers { pingone = { source = "pingidentity/pingone" - version = "1.8.0" + version = "1.12.0" } } } @@ -211,7 +211,7 @@ func InitPingFederateTerraform(t *testing.T) { required_providers { pingfederate = { source = "pingidentity/pingfederate" - version = "1.5.0" + version = "1.6.1" } } } @@ -221,7 +221,7 @@ provider "pingfederate" { password = "2FederateM0re" https_host = "https://localhost:9999" admin_api_path = "/pf-admin-api/v1" - product_version = "12.2" + product_version = "12.3" insecure_trust_all_tls = true x_bypass_external_validation_header = true }