Skip to content

feat(gcp): add parameter manager support#1808

Open
mahpatil wants to merge 12 commits into
open-feature:mainfrom
mahpatil:feat/gcp-parameter-manager
Open

feat(gcp): add parameter manager support#1808
mahpatil wants to merge 12 commits into
open-feature:mainfrom
mahpatil:feat/gcp-parameter-manager

Conversation

@mahpatil
Copy link
Copy Markdown
Contributor

@mahpatil mahpatil commented Jun 5, 2026

This PR

Adds a GCP Parameter Manager support to the GCP Provider that reads feature flags from GCP Parameter Manager config.
Includes updates to sample application under samples/gcp/ with setup/teardown script
Package: providers/gcp
Class: GcpParameterManagerProvider
Supports all OpenFeature flag types via structured or plain-text secret values
Configurable poll interval and GCP project settings

Related Issues

Fixes GCP services covered under #1420

Notes

This builds on top of the previous PR that added support for GCP Secret manager.

Follow-up Tasks

How to test

  • Unit tests for GcpParameterManagerProvider
  • Integration test (GcpParameterManagerProviderIntegrationTest) requires a real GCP project — set GCP_PROJECT_ID env var to run
  • Sample app: follow samples/gcp/README.md to run end-to-end

mahpatil added 9 commits June 3, 2026 13:48
Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
…til/java-sdk-contrib into feat/gcp-parameter-manager

Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
1. add location id to options as gcp params require location
2. fixed parameter manager classes to use common gcp classes post refactor

Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
@mahpatil mahpatil requested a review from a team as a code owner June 5, 2026 18:21
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for Google Cloud Parameter Manager by refactoring the existing GCP provider into a shared AbstractGcpProvider base class and adding the new GcpParameterManagerProvider. It also updates the sample applications, setup/teardown scripts, and tests to support both Secret Manager and Parameter Manager backends. Feedback on these changes highlights several issues: locationId in GcpProviderOptions needs a default value of "global" to prevent null pointer exceptions; the Parameter Manager setup script contains broken update and versioning logic; redundant log statements should be cleaned up; synchronizing on this during blocking network calls creates a performance bottleneck; and the teardown script's deletion chaining is risky and should be decoupled.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +45 to +48
/**
* Optional location required for ParameterManager, ignored by SecretManager
*/
private final String locationId;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The locationId field is currently not defaulted or validated. If it is left null, GcpParameterManagerProvider will throw a NullPointerException when building ParameterVersionName because ParameterVersionName.of requires a non-null location. This also causes the sample application ParameterManagerSampleApp to crash out-of-the-box because it does not configure locationId when building GcpProviderOptions.

Defaulting locationId to "global" using @Builder.Default ensures robust out-of-the-box behavior for both the sample app and general usage.

Suggested change
/**
* Optional location required for ParameterManager, ignored by SecretManager
*/
private final String locationId;
/**
* Optional location required for ParameterManager, ignored by SecretManager
*/
@Builder.Default
private final String locationId = "global";
References
  1. Breaking changes to default behavior can be acceptable for very recently introduced features to allow for architectural improvements before the behavior is widely adopted.

Comment thread samples/gcp/setup.sh
Comment on lines +50 to +76
elif [[ "$BACKEND" == "parameter-manager" ]]; then
# Create or update Parameter Manager parameter
if gcloud parametermanager parameters describe "${full_name}" --location=global --project="${PROJECT}" &>/dev/null 2>&1; then
echo " [EXISTS] ${full_name} — updating value"
gcloud parametermanager parameters update "${full_name}" \
--location=global \
--project="${PROJECT}" \
--data="${value}" \
--quiet || echo " [WARN] Could not update parameter (may require gcloud alpha components)"
else
gcloud parametermanager parameters create "${full_name}" \
--location=global \
--project="${PROJECT}" \
--parameter-format=UNFORMATTED \
--quiet || echo " [WARN] Could not create parameter (may require gcloud alpha components)"
echo " [CREATED] ${full_name}"

gcloud parametermanager parameters versions create VERSION_ID \
--parameter="${full_name}" \
--project="${PROJECT}" \
--location=global \
--payload-data="${value}"

# Add a secret version with the flag value
echo -n "${value}" | gcloud secrets versions add "${full_name}" \
--project="${PROJECT}" \
--data-file=- \
--quiet
echo " [VERSION] ${full_name} → ${value}"

fi
echo " [SET] ${full_name} → ${value}"
fi
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The update and creation logic for Parameter Manager is broken:

  1. gcloud parametermanager parameters update does not support a --data flag, so the update command will fail with an error.
  2. Parameter Manager parameters are versioned, and the provider fetches the payload from a parameter version (defaulting to "latest"). Simply updating the parameter metadata (even if it worked) does not create a new version, so the provider would never see the updated value.
  3. The creation logic hardcodes the version ID to VERSION_ID. If the script is run multiple times, it will fail or fail to update.

To fix this, we should create a new version with a unique ID (e.g., using a timestamp and random suffix) whenever setting or updating a parameter, so that the "latest" alias automatically points to the newest value.

    elif [[ "$BACKEND" == "parameter-manager" ]]; then
        # Create Parameter Manager parameter if it doesn't exist
        if ! gcloud parametermanager parameters describe "${full_name}" --location=global --project="${PROJECT}" &>/dev/null 2>&1; then
            gcloud parametermanager parameters create "${full_name}" \
                --location=global \
                --project="${PROJECT}" \
                --parameter-format=UNFORMATTED \
                --quiet || echo "  [WARN] Could not create parameter"
            echo "  [CREATED] ${full_name}"
        else
            echo "  [EXISTS] ${full_name}"
        fi

        # Create a new version with a unique ID so "latest" points to the new value
        local version_id="v$(date +%s)_$RANDOM"
        gcloud parametermanager parameters versions create "${version_id}" \
            --parameter="${full_name}" \
            --project="${PROJECT}" \
            --location=global \
            --payload-data="${value}" \
            --quiet || echo "  [WARN] Could not create parameter version"
        echo "  [VERSION] ${full_name}${value} (version: ${version_id})"
    fi

Comment on lines +94 to +97
log.info("Fetching parameter from GCP name '{}'", parameterName);
ParameterVersionName versionName = ParameterVersionName.of(
options.getProjectId(), options.getLocationId(), parameterName, options.getVersion());
log.info("Fetching parameter from GCP version {}", parameterName);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

These log statements are redundant and noisy. They log "Fetching parameter from GCP name..." and then immediately log "Fetching parameter from GCP version..." (which incorrectly prints the parameter name instead of the version).

Since GcpSecretManagerProvider does not log on every fetch, these should be removed to avoid polluting production logs and maintain consistency across providers.

            ParameterVersionName versionName = ParameterVersionName.of(
                    options.getProjectId(), options.getLocationId(), parameterName, options.getVersion());

Comment on lines +105 to +111
synchronized (this) {
return cache.get(name).orElseGet(() -> {
String value = fetchFromGcp(name);
cache.put(name, value);
return value;
});
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Synchronizing on this (the entire provider instance) during a blocking network call (fetchFromGcp) is a performance bottleneck. If multiple threads evaluate different feature flags concurrently, they will all block on each other, resulting in high latency and thread starvation.

Consider using a ConcurrentHashMap of locks or a striped lock to synchronize only on a per-key basis rather than globally.

Comment thread samples/gcp/teardown.sh
Comment on lines +31 to 48
elif [[ "$BACKEND" == "parameter-manager" ]]; then
if gcloud parametermanager parameters describe "${full_name}" --location=global --project="${PROJECT}" &>/dev/null 2>&1; then
for v in $(gcloud parametermanager parameters versions list \
--parameter="${full_name}" --location=global \
--format="value(name.basename())"); do
gcloud parametermanager parameters versions delete "$v" \
--parameter="${full_name}" --location=global --quiet
done && \

gcloud parametermanager parameters delete "${full_name}" \
--location=global \
--project="${PROJECT}" \
--quiet || echo " [WARN] Could not delete parameter (may require gcloud alpha components)"
echo " [DELETED] ${full_name}"
else
echo " [SKIP] ${full_name} (not found)"
fi
fi
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Chaining the loop with the parameter deletion using && \ is risky. If any version deletion fails (or if there are no versions and the loop exits with a non-zero status in some shells), the parameter itself will not be deleted. It is safer to run them as separate commands and add error tolerance. Additionally, the indentation inside the for loop should be corrected.

Suggested change
elif [[ "$BACKEND" == "parameter-manager" ]]; then
if gcloud parametermanager parameters describe "${full_name}" --location=global --project="${PROJECT}" &>/dev/null 2>&1; then
for v in $(gcloud parametermanager parameters versions list \
--parameter="${full_name}" --location=global \
--format="value(name.basename())"); do
gcloud parametermanager parameters versions delete "$v" \
--parameter="${full_name}" --location=global --quiet
done && \
gcloud parametermanager parameters delete "${full_name}" \
--location=global \
--project="${PROJECT}" \
--quiet || echo " [WARN] Could not delete parameter (may require gcloud alpha components)"
echo " [DELETED] ${full_name}"
else
echo " [SKIP] ${full_name} (not found)"
fi
fi
elif [[ "$BACKEND" == "parameter-manager" ]]; then
if gcloud parametermanager parameters describe "${full_name}" --location=global --project="${PROJECT}" &>/dev/null 2>&1; then
for v in $(gcloud parametermanager parameters versions list \
--parameter="${full_name}" --location=global \
--format="value(name.basename())" 2>/dev/null || true); do
gcloud parametermanager parameters versions delete "$v" \
--parameter="${full_name}" --location=global --quiet || true
done
gcloud parametermanager parameters delete "${full_name}" \
--location=global \
--project="${PROJECT}" \
--quiet || echo " [WARN] Could not delete parameter"
echo " [DELETED] ${full_name}"
else
echo " [SKIP] ${full_name} (not found)"
fi
fi

@mahpatil mahpatil changed the title Feat/gcp parameter manager feat/gcp parameter manager Jun 6, 2026
@mahpatil mahpatil changed the title feat/gcp parameter manager feat(gcp): parameter manager Jun 6, 2026
@mahpatil mahpatil changed the title feat(gcp): parameter manager feat(gcp): add parameter manager support Jun 6, 2026
mahpatil and others added 3 commits June 6, 2026 07:41
Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
… of creating new versoin2. locationId default to prevent null pointers3. update references to stale GcpParameterManagerOptions

Signed-off-by: Mahesh Patil <maheshfinity@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant