Refuse a different handle when an Artifact Registry is already activated

What does this MR do and why?

An organization that already holds an Artifact Registry could call artifactRegistryActivate again with any handle and get errors: [] plus the handle it really holds. The same request racing the original claim gets Artifact Registry's 409. monolith/S10 criterion 3 was amended in gitlab-org/ops/artifact-registry!2946 (merged) so the sequential case answers the same way. This MR implements that in the monolith.

With a mapping row present, ArtifactRegistry::ProvisionNamespaceService now reads the namespace live (GET /api/gitlab/v1/namespaces/:uuid, never the render-path cache) and:

Live read says Result Provision call
same handle success, existing mapping + the live namespace on the payload no
different handle (even one that also breaks a syntactic rule) error, reason: :already_activated, message names only the claimed handle, payload carries mapping + namespace no
status purged error, reason: :reonboarding_unavailable no
AR 404 for the UUID error, reason: :service_unavailable (the mutation renders its own service-unavailable message) no
Client::UnavailableError error, reason: :service_unavailable no
Client::AuthorizationError propagates; the mutation's shared RendersErrors maps it to not-available, as on the provision path no

Mutations::ArtifactRegistry::Activate returns the claimed registry alongside the payload errors for :already_activated (so the setup page can send the owner there); every other refusal keeps registry: null. The submitted handle is never persisted, logged, forwarded, or echoed. After a successful live read the service expires the render-path cache, as the condition mutations do, so the settings page does not show a stale status for up to five minutes.

Because every success payload now carries the live namespace, the mutation's cache-reading fallback (resolved_registry) became unreachable and its comment false, so it is removed.

Changes

  • ee/app/services/artifact_registry/provision_namespace_service.rb — resolve_existing replaces the blanket early return; two new refusals; class doc reworded.
  • ee/app/graphql/mutations/artifact_registry/activate.rb — :already_activated branch returns the registry; dead cache fallback removed; registry field description updated (docs + introspection regenerated).
  • ee/lib/artifact_registry/namespace.rb — PURGED_STATUS, on the value object that owns status.
  • locale/gitlab.pot — two new ArtifactRegistry| strings.
  • Specs: one case per arm at the service and request layer, each asserting no provisioning call; a cached-entry-disagrees-with-live case; a logger spy proving no log line carries the submitted handle; response-body checks proving the submitted handle is never echoed.

No changelog: artifact_registry_ui is default_enabled: false. No frontend change here; the setup-page state the spec adds follows in a stacked MR.

Known carry-over, not changed here: the :service_unavailable ServiceResponse drops the AR request_id on both the provision and the repeat path; the mutation re-raises with the message only.

Changes since the first review (4ead4adb)

  • registry_for coerces a missing live status to unknown (the null: false field), as ConditionMutation does.
  • The render-path cache is expired after every successful live read, wrapped so a cache failure cannot fail the claim.
  • PURGED_STATUS moved to ArtifactRegistry::Namespace (ee/lib/artifact_registry/namespace.rb); the dead 404-arm s_() string is gone; the registry field description no longer says "provisioned" for the repeat arms.
  • Specs: negatively-cached-entry arm, stale-cache examples assert the refresh, row asserted by attributes, request-spec fixture status is suspended, stubs constrained by argument, request-layer timeout duplicate removed.

Changes since the second review (c3755ee4)

  • The render-path cache is expired after every live read that answered, including an AR 404, so the settings page stops serving a vanished registry as active. Only the raised-exception arms leave it alone.
  • registry_for returns a null registry for a payload without a namespace instead of raising (unreachable today; matches the field description).
  • The doubled render-path client is released after each stale-cache example.
  • Externalizing the pre-existing sibling refusal messages is tracked in #631281.

References

Screenshots or screen recordings

Backend only. Verified live against a GDK organization activated as gdk with a local Artifact Registry:

Call Before After
artifactRegistryActivate(slug: "totally-different-slug") errors: [], registry.slug: "gdk" errors: ["Artifact Registry is already activated for this organization with the handle 'gdk'."], registry.slug: "gdk"; submitted slug absent from the response
artifactRegistryActivate(slug: "gdk") errors: [], registry.slug: "gdk" unchanged
AR namespace row + mapping row unchanged unchanged

How to set up and validate locally

Needs an organization with exactly one top-level group that is already activated, and a reachable Artifact Registry.

  1. Run the specs:

    VITE_ENABLED=true bin/rspec ee/spec/services/artifact_registry/provision_namespace_service_spec.rb \
      ee/spec/requests/api/graphql/mutations/artifact_registry/activate_spec.rb
  2. In rails console, with org an activated organization and root an owner:

    q = 'mutation($slug: String!) { artifactRegistryActivate(input: { slug: $slug }) { errors registry { slug status } } }'
    GitlabSchema.execute(q, variables: { slug: 'some-other-handle' },
      context: { current_user: root, current_organization: org }).to_h
    # => errors names the claimed handle, registry carries it, 'some-other-handle' appears nowhere
    GitlabSchema.execute(q, variables: { slug: org.artifact_registry_namespace_mapping.registry.slug },
      context: { current_user: root, current_organization: org }).to_h
    # => errors: [], registry unchanged
  3. Confirm org.artifact_registry_namespace_mapping is the same row and the AR namespace is unchanged.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Narendran

Merge request reports

Loading
Loading