Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CORE-60] Schema Registry: Support /mode #17952

Merged
merged 18 commits into from
May 14, 2024

Conversation

BenPope
Copy link
Member

@BenPope BenPope commented Apr 18, 2024

Support READONLY /mode

Documentation

Schema registry supports a read-only mode, which is useful when it is undesirable for modifications to be made to either the whole Schema Registry, or particular subjects.

To allow the mode of the Schema Registry to be changed, the node-level property schema_registry.mode_mutability must be set to true in redpanda.yaml (this is now the default):

schema_registry:
  mode_mutability: true

If authentication is enabled on Schema Registry, then the global or subject-level mode can only be changed by superusers.

To get the global mode:

curl http://localhost:$PORT/mode

To set the global mode to read only:

curl -X PUT -H "Content-Type: application/vnd.schemaregistry.v1+json" --data '{"mode": "READONLY"}' http://localhost:$PORT/mode

To revert the global mode to read-write:

curl -X PUT -H "Content-Type: application/vnd.schemaregistry.v1+json" --data '{"mode": "READWRITE"}' http://localhost:$PORT/mode

To query the mode for a particular subject:
If there is no specific mode set for the subject an error shall be returned.

curl http://localhost:$PORT/mode/subject

To query the mode for a particular subject, regardless of whether it has a specific override

curl http://localhost:$PORT/mode/subject?defaultToGlobal=true

To override the global mode for a particular subject:

curl -X PUT -H "Content-Type: application/vnd.schemaregistry.v1+json" --data '{"mode": "READONLY"}' http://localhost:$PORT/mode/<subject>

To clear the override for a particular subject:

curl -X DELETE http://localhost:$PORT/mode/<subject>

New swagger:

    "/mode": {
      "get": {
        "summary": "Get the global mode.",
        "operationId": "get_mode",
        "consumes": [
          "application/vnd.schemaregistry.v1+json",
          "application/vnd.schemaregistry+json",
          "application/json"
        ],
        "parameters": [],
        "produces": [
          "application/vnd.schemaregistry.v1+json",
          "application/vnd.schemaregistry+json",
          "application/json"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/mode"
            }
          },
          "500": {
            "description": "Internal Server error",
            "schema": {
              "$ref": "#/definitions/error_body"
            }
          }
        }
      },
      "put": {
        "summary": "Set the global mode.",
        "operationId": "put_mode",
        "consumes": [
          "application/vnd.schemaregistry.v1+json",
          "application/vnd.schemaregistry+json",
          "application/json"
        ],
        "parameters": [
          {
            "name": "mode",
            "in": "body",
            "schema": {
              "$ref": "#/definitions/mode"
            }
          }
        ],
        "produces": [
          "application/vnd.schemaregistry.v1+json",
          "application/vnd.schemaregistry+json",
          "application/json"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/mode"
            }
          },
          "422": {
            "description": "Unprocessable Entity",
            "schema": {
              "$ref": "#/definitions/error_body"
            }
          },
          "500": {
            "description": "Internal Server error",
            "schema": {
              "$ref": "#/definitions/error_body"
            }
          }
        }
      }
    },
    "/mode/{subject}": {
      "get": {
        "summary": "Get the mode for a subject.",
        "operationId": "get_mode_subject",
        "consumes": [
          "application/vnd.schemaregistry.v1+json",
          "application/vnd.schemaregistry+json",
          "application/json"
        ],
        "parameters": [
          {
            "name": "subject",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "defaultToGlobal",
            "in": "query",
            "required": false,
            "type": "boolean"
          }
        ],
        "produces": [
          "application/vnd.schemaregistry.v1+json",
          "application/vnd.schemaregistry+json",
          "application/json"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/mode"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "$ref": "#/definitions/error_body"
            }
          },
          "500": {
            "description": "Internal Server error",
            "schema": {
              "$ref": "#/definitions/error_body"
            }
          }
        }
      },
      "put": {
        "summary": "Set the mode for a subject.",
        "operationId": "put_mode_subject",
        "consumes": [
          "application/vnd.schemaregistry.v1+json",
          "application/vnd.schemaregistry+json",
          "application/json"
        ],
        "parameters": [
          {
            "name": "subject",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "mode",
            "in": "body",
            "schema": {
              "$ref": "#/definitions/mode"
            }
          }
        ],
        "produces": [
          "application/vnd.schemaregistry.v1+json",
          "application/vnd.schemaregistry+json",
          "application/json"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/mode"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "$ref": "#/definitions/error_body"
            }
          },
          "422": {
            "description": "Unprocessable Entity",
            "schema": {
              "$ref": "#/definitions/error_body"
            }
          },
          "500": {
            "description": "Internal Server error",
            "schema": {
              "$ref": "#/definitions/error_body"
            }
          }
        }
      },
      "delete": {
        "summary": "Delete the mode for a subject.",
        "operationId": "delete_mode_subject",
        "consumes": [
          "application/vnd.schemaregistry.v1+json",
          "application/vnd.schemaregistry+json",
          "application/json"
        ],
        "parameters": [
          {
            "name": "subject",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "produces": [
          "application/vnd.schemaregistry.v1+json",
          "application/vnd.schemaregistry+json",
          "application/json"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/mode"
            }
          },
          "404": {
            "description": "Not Found",
            "schema": {
              "$ref": "#/definitions/error_body"
            }
          },
          "500": {
            "description": "Internal Server error",
            "schema": {
              "$ref": "#/definitions/error_body"
            }
          }
        }
      }
    },

definitions:

      "mode": {
      "type": "object",
      "properties": {
        "mode": {
          "type": "string",
          "enum": [
            "READWRITE",
            "READONLY"
          ]
        },

Notes for the reviewer

A read-only Schema Registry may receive updates directly to its topic, and those changes should become visible in a timely fashion. This will often require a rq.service().writer().read_sync().

Backports Required

  • none - not a bug fix
  • none - this is a backport
  • none - issue does not exist in previous branches
  • none - papercut/not impactful enough to backport
  • v24.1.x
  • v23.3.x
  • v23.2.x

There are a couple of commits that may need to be backported.

Release Notes

Features

  • Schema Registry: Support /mode endpoints for READONLY

@BenPope BenPope requested a review from a team as a code owner April 18, 2024 21:19
@BenPope BenPope self-assigned this Apr 18, 2024
@BenPope BenPope requested review from oleiman and michael-redpanda and removed request for a team and oleiman April 18, 2024 21:19
@BenPope BenPope added area/schema-registry Schema Registry service within Redpanda and removed area/redpanda labels Apr 18, 2024
@BenPope BenPope changed the title Schema Registry: Support /mode [CORE-60] Schema Registry: Support /mode Apr 18, 2024
Copy link
Contributor

@michael-redpanda michael-redpanda left a comment

Choose a reason for hiding this comment

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

looks nice, a few questions

src/v/pandaproxy/schema_registry/store.h Show resolved Hide resolved
src/v/pandaproxy/schema_registry/store.h Show resolved Hide resolved
Comment on lines 718 to 719
result<void> check_mutable(force f) const {
if (!_mutable && !f) {
Copy link
Contributor

Choose a reason for hiding this comment

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

question: Isn't the force flag for if there are already registered schemas and the mode is going into IMPORT?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, but it's also used when applying records from the topic onto the store.

src/v/pandaproxy/api/api-doc/schema_registry.json Outdated Show resolved Hide resolved
oleiman
oleiman previously approved these changes Apr 22, 2024
Copy link
Member

@oleiman oleiman left a comment

Choose a reason for hiding this comment

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

lgtm. few questions mostly for my own benefit.

src/v/pandaproxy/schema_registry/seq_writer.cc Outdated Show resolved Hide resolved
src/v/pandaproxy/schema_registry/seq_writer.cc Outdated Show resolved Hide resolved
src/v/pandaproxy/schema_registry/seq_writer.cc Outdated Show resolved Hide resolved
@pgellert pgellert self-requested a review May 8, 2024 13:36
Comment on lines +70 to 77
rq.user = maybe_authorize_request(
rq.authn_method,
_auth_level,
rq.service().authenticator(),
*rq.req);
} catch (unauthorized_user_exception& e) {
audit_authn_failure(rq, e.get_username(), e.what());
throw;
Copy link
Member Author

Choose a reason for hiding this comment

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

@pgellert Looks like if this fails AuthZ (c.f. AuthN) this needs something like audit_authz for failure

@BenPope
Copy link
Member Author

BenPope commented May 9, 2024

Changes in force-push

  • More tests
  • More compatibility
  • Properly wired-up is_mutable
  • Add support for mode READONLY
  • Drop support for mode IMPORT

@BenPope BenPope requested a review from dotnwat May 9, 2024 15:24
"type": "object",
"properties": {
"mode": {
"type": "string"
Copy link
Member Author

@BenPope BenPope May 9, 2024

Choose a reason for hiding this comment

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

Like this, I believe so!

Suggested change
"type": "string"
"type": "string",
"enum": [
"READWRITE",
"READONLY"
]

@BenPope BenPope requested review from a team and michael-redpanda May 9, 2024 19:15
"type": "string"
},
{
"name": "defaultToGlobal",
Copy link

@kbatuigas kbatuigas May 9, 2024

Choose a reason for hiding this comment

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

From the description for the query param

query the mode for a particular subject, regardless of whether it has a specific override

and the endpoint (without the query param)

If there is no specific mode set for the subject an error shall be returned.

Does defaultToGlobal mean, if there is no specific mode set for the subject, return the global mode instead? I wasn't sure what "specific override" was referring to

Copy link
Member Author

@BenPope BenPope May 9, 2024

Choose a reason for hiding this comment

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

From the description for the query param

query the mode for a particular subject, regardless of whether it has a specific override

and the endpoint (without the query param)

If there is no specific mode set for the subject an error shall be returned.

Does defaultToGlobal mean, if there is no specific mode set for the subject, return the global mode instead? I wasn't sure what "specific override" was referring to

Yeah, so the global mode is in force for all subjects, unless a subject has a mode set, in which case that will override whatever is global, for the subject.

To query whether a subject has a mode set, either don't specify defaultToGlobal=true, or specify defaultToGlobal=false, and detect the success or error.

But if you're just querying what mode is in force, specify defaultToGlobal=true

Copy link
Member Author

Choose a reason for hiding this comment

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

@kbatuigas I added some short descriptions for the parameters.

@oleiman oleiman self-requested a review May 9, 2024 23:11
Signed-off-by: Ben Pope <ben@redpanda.com>
BenPope added 6 commits May 13, 2024 15:43
Signed-off-by: Ben Pope <ben@redpanda.com>
Signed-off-by: Ben Pope <ben@redpanda.com>
No functional changes

Signed-off-by: Ben Pope <ben@redpanda.com>
Signed-off-by: Ben Pope <ben@redpanda.com>
Signed-off-by: Ben Pope <ben@redpanda.com>
Signed-off-by: Ben Pope <ben@redpanda.com>
@BenPope BenPope dismissed stale reviews from oleiman and kbatuigas via 19e51c9 May 13, 2024 14:54
@BenPope BenPope force-pushed the schema-registry-mode branch from 0347421 to 19e51c9 Compare May 13, 2024 14:54
@BenPope
Copy link
Member Author

BenPope commented May 13, 2024

Changes in force-push:

  • Address review comments
  • Return 40401 when deleting a mode for a subject, but it isn't set.

@BenPope BenPope requested review from pgellert and oleiman May 13, 2024 15:59
Copy link
Contributor

@pgellert pgellert left a comment

Choose a reason for hiding this comment

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

lgtm

Copy link
Member

@oleiman oleiman left a comment

Choose a reason for hiding this comment

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

lgtm

@BenPope
Copy link
Member Author

BenPope commented May 14, 2024

We don't do this in the swagger specs currently, but I think our doc readers could benefit from additional explanations added to the description for an operation or parameter.

Do you think it would be useful to add docs as code reviewers for Swagger?

@BenPope BenPope merged commit c1ec803 into redpanda-data:dev May 14, 2024
18 checks passed
@Feediver1
Copy link

We don't do this in the swagger specs currently, but I think our doc readers could benefit from additional explanations added to the description for an operation or parameter.

Do you think it would be useful to add docs as code reviewers for Swagger?

Yes--would mean these updates are reviewed before any customers ever see them. Perfect.

@michael-redpanda
Copy link
Contributor

We don't do this in the swagger specs currently, but I think our doc readers could benefit from additional explanations added to the description for an operation or parameter.

Do you think it would be useful to add docs as code reviewers for Swagger?

Yes--would mean these updates are reviewed before any customers ever see them. Perfect.

https://redpandadata.atlassian.net/browse/CORE-2952

@michael-redpanda
Copy link
Contributor

@Feediver1 done (#18473)

@BenPope
Copy link
Member Author

BenPope commented May 22, 2024

/backport v24.1.x

@BenPope
Copy link
Member Author

BenPope commented May 31, 2024

/backport v23.3.x

@vbotbuildovich
Copy link
Collaborator

Failed to create a backport PR to v23.3.x branch. I tried:

git remote add upstream https://github.com/redpanda-data/redpanda.git
git fetch --all
git checkout -b backport-pr-17952-v23.3.x-326 remotes/upstream/v23.3.x
git cherry-pick -x c99992877894ada0f7a69878b4c42ab8ec4f4519 5fb72fcf3d840e4290417d62ed0bfb9db7c508f9 452556b74f8d3afd29198308c9c60414a87d1df4 88e1993bb1c6c0677668cbc0134de489b65d1eed ba05b97ce676bf276cd25b0eee16bbc03b2b9360 390fc71805c0b131f5d3154af4b508e730303827 7aa34ffccee787e6d725b621ebe7fdd77befe1f5 71c5fef6fd374ddba669fde6edad32e1318cd607 6ce966f23e086138f6f18368f1ef17799fb374fa 2776733646c541d335085e36485c993a797a0f0b 282854b2223cd8b423318dac62367e5520b41ba9 8b0915f8e22d9768c5f0458c80048ee4f847af3f b55d09ed7a031345182efe5f45272d118cea0fe8 ccdb7d6f060a66417f6cae7c5828d33e60a67f78 6f2f894891c146fc56d7f57bce868b7930db13ad dc8c2e429a1c23db2d87eaa834ff67d6faebb468 dd6c8312fa26a237af40e5d447ea35d2367474fa 19e51c9b6695f9a81fb777d297e511ec3095ced6

Workflow run logs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/redpanda area/schema-registry Schema Registry service within Redpanda doc-needed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants