From c2c3af344e2b1ff3f6824a8bc5f773b99b651418 Mon Sep 17 00:00:00 2001 From: Nic Cope Date: Thu, 15 Jan 2026 15:23:54 -0800 Subject: [PATCH] Add support for requesting OpenAPI schemas Crossplane PR #7022 adds support for composition functions to request OpenAPI schemas for resource kinds. When a function needs to validate or introspect a resource's schema, it can request the schema from Crossplane and receive it in subsequent invocations. This commit updates the protobuf definitions from PR #7022 and adds helper functions to work with schema requirements: - response.require_schema() declares a schema requirement - request.get_required_schema() retrieves the resolved schema The schema is returned as a plain dict, allowing callers to use it directly with validation libraries like openapi-schema-validator or jsonschema. Both get_required_resources() and get_required_schema() docstrings now document how to distinguish between "not yet resolved" and "resolved but not found" using the `name in req.required_*` pattern. Ref: https://github.com/crossplane/crossplane/pull/7022 Signed-off-by: Nic Cope --- .../function/proto/v1/run_function.proto | 30 ++++- .../function/proto/v1/run_function_pb2.py | 124 ++++++++++-------- .../function/proto/v1/run_function_pb2.pyi | 40 +++++- .../function/proto/v1beta1/run_function.proto | 28 ++++ .../proto/v1beta1/run_function_pb2.py | 124 ++++++++++-------- .../proto/v1beta1/run_function_pb2.pyi | 40 +++++- crossplane/function/request.py | 58 ++++++++ crossplane/function/response.py | 29 ++++ tests/test_request.py | 65 +++++++++ tests/test_response.py | 53 ++++++++ 10 files changed, 470 insertions(+), 121 deletions(-) diff --git a/crossplane/function/proto/v1/run_function.proto b/crossplane/function/proto/v1/run_function.proto index a6ca33a..4c3d092 100644 --- a/crossplane/function/proto/v1/run_function.proto +++ b/crossplane/function/proto/v1/run_function.proto @@ -90,6 +90,13 @@ message RunFunctionRequest { // satisfy the request. This field is only populated when the function uses // resources in its requirements. map required_resources = 8; + + // Optional schemas that the function specified in its requirements. The map + // key corresponds to the key in a RunFunctionResponse's requirements.schemas + // field. If a function requested a schema that could not be found, Crossplane + // sets the map key to an empty Schema message to indicate that it attempted + // to satisfy the request. + map required_schemas = 9; } // Credentials that a function may use to communicate with an external system. @@ -169,6 +176,27 @@ message Requirements { // Resources that this function requires. The map key uniquely identifies the // group of resources. map resources = 2; + + // Schemas that this function requires. The map key uniquely identifies the + // schema request. + map schemas = 3; +} + +// SchemaSelector identifies a resource kind whose OpenAPI schema is requested. +message SchemaSelector { + // API version of the resource kind, e.g. "example.org/v1". + string api_version = 1; + + // Kind of resource, e.g. "MyResource". + string kind = 2; +} + +// Schema represents the OpenAPI schema for a resource kind. +message Schema { + // The OpenAPI v3 schema of the resource kind as unstructured JSON. + // For CRDs this is the spec.versions[].schema.openAPIV3Schema field. + // Empty if Crossplane could not find a schema for the requested kind. + optional google.protobuf.Struct openapi_v3 = 1; } // ResourceSelector selects a group of resources, either by name or by label. @@ -367,4 +395,4 @@ enum Status { STATUS_CONDITION_TRUE = 2; STATUS_CONDITION_FALSE = 3; -} \ No newline at end of file +} diff --git a/crossplane/function/proto/v1/run_function_pb2.py b/crossplane/function/proto/v1/run_function_pb2.py index 9c51896..27506ae 100644 --- a/crossplane/function/proto/v1/run_function_pb2.py +++ b/crossplane/function/proto/v1/run_function_pb2.py @@ -26,7 +26,7 @@ from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n/crossplane/function/proto/v1/run_function.proto\x12\x19\x61piextensions.fn.proto.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\"\xd3\x06\n\x12RunFunctionRequest\x12\x34\n\x04meta\x18\x01 \x01(\x0b\x32&.apiextensions.fn.proto.v1.RequestMeta\x12\x32\n\x08observed\x18\x02 \x01(\x0b\x32 .apiextensions.fn.proto.v1.State\x12\x31\n\x07\x64\x65sired\x18\x03 \x01(\x0b\x32 .apiextensions.fn.proto.v1.State\x12+\n\x05input\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x12-\n\x07\x63ontext\x18\x05 \x01(\x0b\x32\x17.google.protobuf.StructH\x01\x88\x01\x01\x12^\n\x0f\x65xtra_resources\x18\x06 \x03(\x0b\x32\x41.apiextensions.fn.proto.v1.RunFunctionRequest.ExtraResourcesEntryB\x02\x18\x01\x12S\n\x0b\x63redentials\x18\x07 \x03(\x0b\x32>.apiextensions.fn.proto.v1.RunFunctionRequest.CredentialsEntry\x12`\n\x12required_resources\x18\x08 \x03(\x0b\x32\x44.apiextensions.fn.proto.v1.RunFunctionRequest.RequiredResourcesEntry\x1a[\n\x13\x45xtraResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x33\n\x05value\x18\x02 \x01(\x0b\x32$.apiextensions.fn.proto.v1.Resources:\x02\x38\x01\x1aZ\n\x10\x43redentialsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x35\n\x05value\x18\x02 \x01(\x0b\x32&.apiextensions.fn.proto.v1.Credentials:\x02\x38\x01\x1a^\n\x16RequiredResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x33\n\x05value\x18\x02 \x01(\x0b\x32$.apiextensions.fn.proto.v1.Resources:\x02\x38\x01\x42\x08\n\x06_inputB\n\n\x08_context\"]\n\x0b\x43redentials\x12\x44\n\x0f\x63redential_data\x18\x01 \x01(\x0b\x32).apiextensions.fn.proto.v1.CredentialDataH\x00\x42\x08\n\x06source\"\x80\x01\n\x0e\x43redentialData\x12\x41\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\x33.apiextensions.fn.proto.v1.CredentialData.DataEntry\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"?\n\tResources\x12\x32\n\x05items\x18\x01 \x03(\x0b\x32#.apiextensions.fn.proto.v1.Resource\"\xa0\x03\n\x13RunFunctionResponse\x12\x35\n\x04meta\x18\x01 \x01(\x0b\x32\'.apiextensions.fn.proto.v1.ResponseMeta\x12\x31\n\x07\x64\x65sired\x18\x02 \x01(\x0b\x32 .apiextensions.fn.proto.v1.State\x12\x32\n\x07results\x18\x03 \x03(\x0b\x32!.apiextensions.fn.proto.v1.Result\x12-\n\x07\x63ontext\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x12=\n\x0crequirements\x18\x05 \x01(\x0b\x32\'.apiextensions.fn.proto.v1.Requirements\x12\x38\n\nconditions\x18\x06 \x03(\x0b\x32$.apiextensions.fn.proto.v1.Condition\x12,\n\x06output\x18\x07 \x01(\x0b\x32\x17.google.protobuf.StructH\x01\x88\x01\x01\x42\n\n\x08_contextB\t\n\x07_output\"\x1a\n\x0bRequestMeta\x12\x0b\n\x03tag\x18\x01 \x01(\t\"\xf6\x02\n\x0cRequirements\x12X\n\x0f\x65xtra_resources\x18\x01 \x03(\x0b\x32;.apiextensions.fn.proto.v1.Requirements.ExtraResourcesEntryB\x02\x18\x01\x12I\n\tresources\x18\x02 \x03(\x0b\x32\x36.apiextensions.fn.proto.v1.Requirements.ResourcesEntry\x1a\x62\n\x13\x45xtraResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12:\n\x05value\x18\x02 \x01(\x0b\x32+.apiextensions.fn.proto.v1.ResourceSelector:\x02\x38\x01\x1a]\n\x0eResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12:\n\x05value\x18\x02 \x01(\x0b\x32+.apiextensions.fn.proto.v1.ResourceSelector:\x02\x38\x01\"\xba\x01\n\x10ResourceSelector\x12\x13\n\x0b\x61pi_version\x18\x01 \x01(\t\x12\x0c\n\x04kind\x18\x02 \x01(\t\x12\x14\n\nmatch_name\x18\x03 \x01(\tH\x00\x12>\n\x0cmatch_labels\x18\x04 \x01(\x0b\x32&.apiextensions.fn.proto.v1.MatchLabelsH\x00\x12\x16\n\tnamespace\x18\x05 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05matchB\x0c\n\n_namespace\"\x80\x01\n\x0bMatchLabels\x12\x42\n\x06labels\x18\x01 \x03(\x0b\x32\x32.apiextensions.fn.proto.v1.MatchLabels.LabelsEntry\x1a-\n\x0bLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"P\n\x0cResponseMeta\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12+\n\x03ttl\x18\x02 \x01(\x0b\x32\x19.google.protobuf.DurationH\x00\x88\x01\x01\x42\x06\n\x04_ttl\"\xda\x01\n\x05State\x12\x36\n\tcomposite\x18\x01 \x01(\x0b\x32#.apiextensions.fn.proto.v1.Resource\x12\x42\n\tresources\x18\x02 \x03(\x0b\x32/.apiextensions.fn.proto.v1.State.ResourcesEntry\x1aU\n\x0eResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x32\n\x05value\x18\x02 \x01(\x0b\x32#.apiextensions.fn.proto.v1.Resource:\x02\x38\x01\"\xf8\x01\n\x08Resource\x12)\n\x08resource\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\x12V\n\x12\x63onnection_details\x18\x02 \x03(\x0b\x32:.apiextensions.fn.proto.v1.Resource.ConnectionDetailsEntry\x12/\n\x05ready\x18\x03 \x01(\x0e\x32 .apiextensions.fn.proto.v1.Ready\x1a\x38\n\x16\x43onnectionDetailsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"\xb3\x01\n\x06Result\x12\x35\n\x08severity\x18\x01 \x01(\x0e\x32#.apiextensions.fn.proto.v1.Severity\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x13\n\x06reason\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x36\n\x06target\x18\x04 \x01(\x0e\x32!.apiextensions.fn.proto.v1.TargetH\x01\x88\x01\x01\x42\t\n\x07_reasonB\t\n\x07_target\"\xc1\x01\n\tCondition\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x31\n\x06status\x18\x02 \x01(\x0e\x32!.apiextensions.fn.proto.v1.Status\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x14\n\x07message\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x36\n\x06target\x18\x05 \x01(\x0e\x32!.apiextensions.fn.proto.v1.TargetH\x01\x88\x01\x01\x42\n\n\x08_messageB\t\n\x07_target*?\n\x05Ready\x12\x15\n\x11READY_UNSPECIFIED\x10\x00\x12\x0e\n\nREADY_TRUE\x10\x01\x12\x0f\n\x0bREADY_FALSE\x10\x02*c\n\x08Severity\x12\x18\n\x14SEVERITY_UNSPECIFIED\x10\x00\x12\x12\n\x0eSEVERITY_FATAL\x10\x01\x12\x14\n\x10SEVERITY_WARNING\x10\x02\x12\x13\n\x0fSEVERITY_NORMAL\x10\x03*V\n\x06Target\x12\x16\n\x12TARGET_UNSPECIFIED\x10\x00\x12\x14\n\x10TARGET_COMPOSITE\x10\x01\x12\x1e\n\x1aTARGET_COMPOSITE_AND_CLAIM\x10\x02*\x7f\n\x06Status\x12 \n\x1cSTATUS_CONDITION_UNSPECIFIED\x10\x00\x12\x1c\n\x18STATUS_CONDITION_UNKNOWN\x10\x01\x12\x19\n\x15STATUS_CONDITION_TRUE\x10\x02\x12\x1a\n\x16STATUS_CONDITION_FALSE\x10\x03\x32\x87\x01\n\x15\x46unctionRunnerService\x12n\n\x0bRunFunction\x12-.apiextensions.fn.proto.v1.RunFunctionRequest\x1a..apiextensions.fn.proto.v1.RunFunctionResponse\"\x00\x42\x31Z/github.com/crossplane/crossplane/v2/proto/fn/v1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n/crossplane/function/proto/v1/run_function.proto\x12\x19\x61piextensions.fn.proto.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\"\x8c\x08\n\x12RunFunctionRequest\x12\x34\n\x04meta\x18\x01 \x01(\x0b\x32&.apiextensions.fn.proto.v1.RequestMeta\x12\x32\n\x08observed\x18\x02 \x01(\x0b\x32 .apiextensions.fn.proto.v1.State\x12\x31\n\x07\x64\x65sired\x18\x03 \x01(\x0b\x32 .apiextensions.fn.proto.v1.State\x12+\n\x05input\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x12-\n\x07\x63ontext\x18\x05 \x01(\x0b\x32\x17.google.protobuf.StructH\x01\x88\x01\x01\x12^\n\x0f\x65xtra_resources\x18\x06 \x03(\x0b\x32\x41.apiextensions.fn.proto.v1.RunFunctionRequest.ExtraResourcesEntryB\x02\x18\x01\x12S\n\x0b\x63redentials\x18\x07 \x03(\x0b\x32>.apiextensions.fn.proto.v1.RunFunctionRequest.CredentialsEntry\x12`\n\x12required_resources\x18\x08 \x03(\x0b\x32\x44.apiextensions.fn.proto.v1.RunFunctionRequest.RequiredResourcesEntry\x12\\\n\x10required_schemas\x18\t \x03(\x0b\x32\x42.apiextensions.fn.proto.v1.RunFunctionRequest.RequiredSchemasEntry\x1a[\n\x13\x45xtraResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x33\n\x05value\x18\x02 \x01(\x0b\x32$.apiextensions.fn.proto.v1.Resources:\x02\x38\x01\x1aZ\n\x10\x43redentialsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x35\n\x05value\x18\x02 \x01(\x0b\x32&.apiextensions.fn.proto.v1.Credentials:\x02\x38\x01\x1a^\n\x16RequiredResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x33\n\x05value\x18\x02 \x01(\x0b\x32$.apiextensions.fn.proto.v1.Resources:\x02\x38\x01\x1aY\n\x14RequiredSchemasEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x30\n\x05value\x18\x02 \x01(\x0b\x32!.apiextensions.fn.proto.v1.Schema:\x02\x38\x01\x42\x08\n\x06_inputB\n\n\x08_context\"]\n\x0b\x43redentials\x12\x44\n\x0f\x63redential_data\x18\x01 \x01(\x0b\x32).apiextensions.fn.proto.v1.CredentialDataH\x00\x42\x08\n\x06source\"\x80\x01\n\x0e\x43redentialData\x12\x41\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\x33.apiextensions.fn.proto.v1.CredentialData.DataEntry\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"?\n\tResources\x12\x32\n\x05items\x18\x01 \x03(\x0b\x32#.apiextensions.fn.proto.v1.Resource\"\xa0\x03\n\x13RunFunctionResponse\x12\x35\n\x04meta\x18\x01 \x01(\x0b\x32\'.apiextensions.fn.proto.v1.ResponseMeta\x12\x31\n\x07\x64\x65sired\x18\x02 \x01(\x0b\x32 .apiextensions.fn.proto.v1.State\x12\x32\n\x07results\x18\x03 \x03(\x0b\x32!.apiextensions.fn.proto.v1.Result\x12-\n\x07\x63ontext\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x12=\n\x0crequirements\x18\x05 \x01(\x0b\x32\'.apiextensions.fn.proto.v1.Requirements\x12\x38\n\nconditions\x18\x06 \x03(\x0b\x32$.apiextensions.fn.proto.v1.Condition\x12,\n\x06output\x18\x07 \x01(\x0b\x32\x17.google.protobuf.StructH\x01\x88\x01\x01\x42\n\n\x08_contextB\t\n\x07_output\"\x1a\n\x0bRequestMeta\x12\x0b\n\x03tag\x18\x01 \x01(\t\"\x98\x04\n\x0cRequirements\x12X\n\x0f\x65xtra_resources\x18\x01 \x03(\x0b\x32;.apiextensions.fn.proto.v1.Requirements.ExtraResourcesEntryB\x02\x18\x01\x12I\n\tresources\x18\x02 \x03(\x0b\x32\x36.apiextensions.fn.proto.v1.Requirements.ResourcesEntry\x12\x45\n\x07schemas\x18\x03 \x03(\x0b\x32\x34.apiextensions.fn.proto.v1.Requirements.SchemasEntry\x1a\x62\n\x13\x45xtraResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12:\n\x05value\x18\x02 \x01(\x0b\x32+.apiextensions.fn.proto.v1.ResourceSelector:\x02\x38\x01\x1a]\n\x0eResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12:\n\x05value\x18\x02 \x01(\x0b\x32+.apiextensions.fn.proto.v1.ResourceSelector:\x02\x38\x01\x1aY\n\x0cSchemasEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x38\n\x05value\x18\x02 \x01(\x0b\x32).apiextensions.fn.proto.v1.SchemaSelector:\x02\x38\x01\"3\n\x0eSchemaSelector\x12\x13\n\x0b\x61pi_version\x18\x01 \x01(\t\x12\x0c\n\x04kind\x18\x02 \x01(\t\"I\n\x06Schema\x12\x30\n\nopenapi_v3\x18\x01 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x42\r\n\x0b_openapi_v3\"\xba\x01\n\x10ResourceSelector\x12\x13\n\x0b\x61pi_version\x18\x01 \x01(\t\x12\x0c\n\x04kind\x18\x02 \x01(\t\x12\x14\n\nmatch_name\x18\x03 \x01(\tH\x00\x12>\n\x0cmatch_labels\x18\x04 \x01(\x0b\x32&.apiextensions.fn.proto.v1.MatchLabelsH\x00\x12\x16\n\tnamespace\x18\x05 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05matchB\x0c\n\n_namespace\"\x80\x01\n\x0bMatchLabels\x12\x42\n\x06labels\x18\x01 \x03(\x0b\x32\x32.apiextensions.fn.proto.v1.MatchLabels.LabelsEntry\x1a-\n\x0bLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"P\n\x0cResponseMeta\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12+\n\x03ttl\x18\x02 \x01(\x0b\x32\x19.google.protobuf.DurationH\x00\x88\x01\x01\x42\x06\n\x04_ttl\"\xda\x01\n\x05State\x12\x36\n\tcomposite\x18\x01 \x01(\x0b\x32#.apiextensions.fn.proto.v1.Resource\x12\x42\n\tresources\x18\x02 \x03(\x0b\x32/.apiextensions.fn.proto.v1.State.ResourcesEntry\x1aU\n\x0eResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x32\n\x05value\x18\x02 \x01(\x0b\x32#.apiextensions.fn.proto.v1.Resource:\x02\x38\x01\"\xf8\x01\n\x08Resource\x12)\n\x08resource\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\x12V\n\x12\x63onnection_details\x18\x02 \x03(\x0b\x32:.apiextensions.fn.proto.v1.Resource.ConnectionDetailsEntry\x12/\n\x05ready\x18\x03 \x01(\x0e\x32 .apiextensions.fn.proto.v1.Ready\x1a\x38\n\x16\x43onnectionDetailsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"\xb3\x01\n\x06Result\x12\x35\n\x08severity\x18\x01 \x01(\x0e\x32#.apiextensions.fn.proto.v1.Severity\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x13\n\x06reason\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x36\n\x06target\x18\x04 \x01(\x0e\x32!.apiextensions.fn.proto.v1.TargetH\x01\x88\x01\x01\x42\t\n\x07_reasonB\t\n\x07_target\"\xc1\x01\n\tCondition\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x31\n\x06status\x18\x02 \x01(\x0e\x32!.apiextensions.fn.proto.v1.Status\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x14\n\x07message\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x36\n\x06target\x18\x05 \x01(\x0e\x32!.apiextensions.fn.proto.v1.TargetH\x01\x88\x01\x01\x42\n\n\x08_messageB\t\n\x07_target*?\n\x05Ready\x12\x15\n\x11READY_UNSPECIFIED\x10\x00\x12\x0e\n\nREADY_TRUE\x10\x01\x12\x0f\n\x0bREADY_FALSE\x10\x02*c\n\x08Severity\x12\x18\n\x14SEVERITY_UNSPECIFIED\x10\x00\x12\x12\n\x0eSEVERITY_FATAL\x10\x01\x12\x14\n\x10SEVERITY_WARNING\x10\x02\x12\x13\n\x0fSEVERITY_NORMAL\x10\x03*V\n\x06Target\x12\x16\n\x12TARGET_UNSPECIFIED\x10\x00\x12\x14\n\x10TARGET_COMPOSITE\x10\x01\x12\x1e\n\x1aTARGET_COMPOSITE_AND_CLAIM\x10\x02*\x7f\n\x06Status\x12 \n\x1cSTATUS_CONDITION_UNSPECIFIED\x10\x00\x12\x1c\n\x18STATUS_CONDITION_UNKNOWN\x10\x01\x12\x19\n\x15STATUS_CONDITION_TRUE\x10\x02\x12\x1a\n\x16STATUS_CONDITION_FALSE\x10\x03\x32\x87\x01\n\x15\x46unctionRunnerService\x12n\n\x0bRunFunction\x12-.apiextensions.fn.proto.v1.RunFunctionRequest\x1a..apiextensions.fn.proto.v1.RunFunctionResponse\"\x00\x42\x31Z/github.com/crossplane/crossplane/v2/proto/fn/v1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -40,6 +40,8 @@ _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_options = b'8\001' _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._loaded_options = None _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._serialized_options = b'8\001' + _globals['_RUNFUNCTIONREQUEST_REQUIREDSCHEMASENTRY']._loaded_options = None + _globals['_RUNFUNCTIONREQUEST_REQUIREDSCHEMASENTRY']._serialized_options = b'8\001' _globals['_RUNFUNCTIONREQUEST'].fields_by_name['extra_resources']._loaded_options = None _globals['_RUNFUNCTIONREQUEST'].fields_by_name['extra_resources']._serialized_options = b'\030\001' _globals['_CREDENTIALDATA_DATAENTRY']._loaded_options = None @@ -48,6 +50,8 @@ _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_options = b'8\001' _globals['_REQUIREMENTS_RESOURCESENTRY']._loaded_options = None _globals['_REQUIREMENTS_RESOURCESENTRY']._serialized_options = b'8\001' + _globals['_REQUIREMENTS_SCHEMASENTRY']._loaded_options = None + _globals['_REQUIREMENTS_SCHEMASENTRY']._serialized_options = b'8\001' _globals['_REQUIREMENTS'].fields_by_name['extra_resources']._loaded_options = None _globals['_REQUIREMENTS'].fields_by_name['extra_resources']._serialized_options = b'\030\001' _globals['_MATCHLABELS_LABELSENTRY']._loaded_options = None @@ -56,60 +60,68 @@ _globals['_STATE_RESOURCESENTRY']._serialized_options = b'8\001' _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._loaded_options = None _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_options = b'8\001' - _globals['_READY']._serialized_start=3361 - _globals['_READY']._serialized_end=3424 - _globals['_SEVERITY']._serialized_start=3426 - _globals['_SEVERITY']._serialized_end=3525 - _globals['_TARGET']._serialized_start=3527 - _globals['_TARGET']._serialized_end=3613 - _globals['_STATUS']._serialized_start=3615 - _globals['_STATUS']._serialized_end=3742 + _globals['_READY']._serialized_start=3836 + _globals['_READY']._serialized_end=3899 + _globals['_SEVERITY']._serialized_start=3901 + _globals['_SEVERITY']._serialized_end=4000 + _globals['_TARGET']._serialized_start=4002 + _globals['_TARGET']._serialized_end=4088 + _globals['_STATUS']._serialized_start=4090 + _globals['_STATUS']._serialized_end=4217 _globals['_RUNFUNCTIONREQUEST']._serialized_start=141 - _globals['_RUNFUNCTIONREQUEST']._serialized_end=992 - _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._serialized_start=691 - _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._serialized_end=782 - _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_start=784 - _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_end=874 - _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._serialized_start=876 - _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._serialized_end=970 - _globals['_CREDENTIALS']._serialized_start=994 - _globals['_CREDENTIALS']._serialized_end=1087 - _globals['_CREDENTIALDATA']._serialized_start=1090 - _globals['_CREDENTIALDATA']._serialized_end=1218 - _globals['_CREDENTIALDATA_DATAENTRY']._serialized_start=1175 - _globals['_CREDENTIALDATA_DATAENTRY']._serialized_end=1218 - _globals['_RESOURCES']._serialized_start=1220 - _globals['_RESOURCES']._serialized_end=1283 - _globals['_RUNFUNCTIONRESPONSE']._serialized_start=1286 - _globals['_RUNFUNCTIONRESPONSE']._serialized_end=1702 - _globals['_REQUESTMETA']._serialized_start=1704 - _globals['_REQUESTMETA']._serialized_end=1730 - _globals['_REQUIREMENTS']._serialized_start=1733 - _globals['_REQUIREMENTS']._serialized_end=2107 - _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_start=1914 - _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_end=2012 - _globals['_REQUIREMENTS_RESOURCESENTRY']._serialized_start=2014 - _globals['_REQUIREMENTS_RESOURCESENTRY']._serialized_end=2107 - _globals['_RESOURCESELECTOR']._serialized_start=2110 - _globals['_RESOURCESELECTOR']._serialized_end=2296 - _globals['_MATCHLABELS']._serialized_start=2299 - _globals['_MATCHLABELS']._serialized_end=2427 - _globals['_MATCHLABELS_LABELSENTRY']._serialized_start=2382 - _globals['_MATCHLABELS_LABELSENTRY']._serialized_end=2427 - _globals['_RESPONSEMETA']._serialized_start=2429 - _globals['_RESPONSEMETA']._serialized_end=2509 - _globals['_STATE']._serialized_start=2512 - _globals['_STATE']._serialized_end=2730 - _globals['_STATE_RESOURCESENTRY']._serialized_start=2645 - _globals['_STATE_RESOURCESENTRY']._serialized_end=2730 - _globals['_RESOURCE']._serialized_start=2733 - _globals['_RESOURCE']._serialized_end=2981 - _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_start=2925 - _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_end=2981 - _globals['_RESULT']._serialized_start=2984 - _globals['_RESULT']._serialized_end=3163 - _globals['_CONDITION']._serialized_start=3166 - _globals['_CONDITION']._serialized_end=3359 - _globals['_FUNCTIONRUNNERSERVICE']._serialized_start=3745 - _globals['_FUNCTIONRUNNERSERVICE']._serialized_end=3880 + _globals['_RUNFUNCTIONREQUEST']._serialized_end=1177 + _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._serialized_start=785 + _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._serialized_end=876 + _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_start=878 + _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_end=968 + _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._serialized_start=970 + _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._serialized_end=1064 + _globals['_RUNFUNCTIONREQUEST_REQUIREDSCHEMASENTRY']._serialized_start=1066 + _globals['_RUNFUNCTIONREQUEST_REQUIREDSCHEMASENTRY']._serialized_end=1155 + _globals['_CREDENTIALS']._serialized_start=1179 + _globals['_CREDENTIALS']._serialized_end=1272 + _globals['_CREDENTIALDATA']._serialized_start=1275 + _globals['_CREDENTIALDATA']._serialized_end=1403 + _globals['_CREDENTIALDATA_DATAENTRY']._serialized_start=1360 + _globals['_CREDENTIALDATA_DATAENTRY']._serialized_end=1403 + _globals['_RESOURCES']._serialized_start=1405 + _globals['_RESOURCES']._serialized_end=1468 + _globals['_RUNFUNCTIONRESPONSE']._serialized_start=1471 + _globals['_RUNFUNCTIONRESPONSE']._serialized_end=1887 + _globals['_REQUESTMETA']._serialized_start=1889 + _globals['_REQUESTMETA']._serialized_end=1915 + _globals['_REQUIREMENTS']._serialized_start=1918 + _globals['_REQUIREMENTS']._serialized_end=2454 + _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_start=2170 + _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_end=2268 + _globals['_REQUIREMENTS_RESOURCESENTRY']._serialized_start=2270 + _globals['_REQUIREMENTS_RESOURCESENTRY']._serialized_end=2363 + _globals['_REQUIREMENTS_SCHEMASENTRY']._serialized_start=2365 + _globals['_REQUIREMENTS_SCHEMASENTRY']._serialized_end=2454 + _globals['_SCHEMASELECTOR']._serialized_start=2456 + _globals['_SCHEMASELECTOR']._serialized_end=2507 + _globals['_SCHEMA']._serialized_start=2509 + _globals['_SCHEMA']._serialized_end=2582 + _globals['_RESOURCESELECTOR']._serialized_start=2585 + _globals['_RESOURCESELECTOR']._serialized_end=2771 + _globals['_MATCHLABELS']._serialized_start=2774 + _globals['_MATCHLABELS']._serialized_end=2902 + _globals['_MATCHLABELS_LABELSENTRY']._serialized_start=2857 + _globals['_MATCHLABELS_LABELSENTRY']._serialized_end=2902 + _globals['_RESPONSEMETA']._serialized_start=2904 + _globals['_RESPONSEMETA']._serialized_end=2984 + _globals['_STATE']._serialized_start=2987 + _globals['_STATE']._serialized_end=3205 + _globals['_STATE_RESOURCESENTRY']._serialized_start=3120 + _globals['_STATE_RESOURCESENTRY']._serialized_end=3205 + _globals['_RESOURCE']._serialized_start=3208 + _globals['_RESOURCE']._serialized_end=3456 + _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_start=3400 + _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_end=3456 + _globals['_RESULT']._serialized_start=3459 + _globals['_RESULT']._serialized_end=3638 + _globals['_CONDITION']._serialized_start=3641 + _globals['_CONDITION']._serialized_end=3834 + _globals['_FUNCTIONRUNNERSERVICE']._serialized_start=4220 + _globals['_FUNCTIONRUNNERSERVICE']._serialized_end=4355 # @@protoc_insertion_point(module_scope) diff --git a/crossplane/function/proto/v1/run_function_pb2.pyi b/crossplane/function/proto/v1/run_function_pb2.pyi index 534fe30..30ad04f 100644 --- a/crossplane/function/proto/v1/run_function_pb2.pyi +++ b/crossplane/function/proto/v1/run_function_pb2.pyi @@ -52,7 +52,7 @@ STATUS_CONDITION_TRUE: Status STATUS_CONDITION_FALSE: Status class RunFunctionRequest(_message.Message): - __slots__ = ("meta", "observed", "desired", "input", "context", "extra_resources", "credentials", "required_resources") + __slots__ = ("meta", "observed", "desired", "input", "context", "extra_resources", "credentials", "required_resources", "required_schemas") class ExtraResourcesEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -74,6 +74,13 @@ class RunFunctionRequest(_message.Message): key: str value: Resources def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[Resources, _Mapping]] = ...) -> None: ... + class RequiredSchemasEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: Schema + def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[Schema, _Mapping]] = ...) -> None: ... META_FIELD_NUMBER: _ClassVar[int] OBSERVED_FIELD_NUMBER: _ClassVar[int] DESIRED_FIELD_NUMBER: _ClassVar[int] @@ -82,6 +89,7 @@ class RunFunctionRequest(_message.Message): EXTRA_RESOURCES_FIELD_NUMBER: _ClassVar[int] CREDENTIALS_FIELD_NUMBER: _ClassVar[int] REQUIRED_RESOURCES_FIELD_NUMBER: _ClassVar[int] + REQUIRED_SCHEMAS_FIELD_NUMBER: _ClassVar[int] meta: RequestMeta observed: State desired: State @@ -90,7 +98,8 @@ class RunFunctionRequest(_message.Message): extra_resources: _containers.MessageMap[str, Resources] credentials: _containers.MessageMap[str, Credentials] required_resources: _containers.MessageMap[str, Resources] - def __init__(self, meta: _Optional[_Union[RequestMeta, _Mapping]] = ..., observed: _Optional[_Union[State, _Mapping]] = ..., desired: _Optional[_Union[State, _Mapping]] = ..., input: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., context: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., extra_resources: _Optional[_Mapping[str, Resources]] = ..., credentials: _Optional[_Mapping[str, Credentials]] = ..., required_resources: _Optional[_Mapping[str, Resources]] = ...) -> None: ... + required_schemas: _containers.MessageMap[str, Schema] + def __init__(self, meta: _Optional[_Union[RequestMeta, _Mapping]] = ..., observed: _Optional[_Union[State, _Mapping]] = ..., desired: _Optional[_Union[State, _Mapping]] = ..., input: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., context: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., extra_resources: _Optional[_Mapping[str, Resources]] = ..., credentials: _Optional[_Mapping[str, Credentials]] = ..., required_resources: _Optional[_Mapping[str, Resources]] = ..., required_schemas: _Optional[_Mapping[str, Schema]] = ...) -> None: ... class Credentials(_message.Message): __slots__ = ("credential_data",) @@ -142,7 +151,7 @@ class RequestMeta(_message.Message): def __init__(self, tag: _Optional[str] = ...) -> None: ... class Requirements(_message.Message): - __slots__ = ("extra_resources", "resources") + __slots__ = ("extra_resources", "resources", "schemas") class ExtraResourcesEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -157,11 +166,34 @@ class Requirements(_message.Message): key: str value: ResourceSelector def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[ResourceSelector, _Mapping]] = ...) -> None: ... + class SchemasEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: SchemaSelector + def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[SchemaSelector, _Mapping]] = ...) -> None: ... EXTRA_RESOURCES_FIELD_NUMBER: _ClassVar[int] RESOURCES_FIELD_NUMBER: _ClassVar[int] + SCHEMAS_FIELD_NUMBER: _ClassVar[int] extra_resources: _containers.MessageMap[str, ResourceSelector] resources: _containers.MessageMap[str, ResourceSelector] - def __init__(self, extra_resources: _Optional[_Mapping[str, ResourceSelector]] = ..., resources: _Optional[_Mapping[str, ResourceSelector]] = ...) -> None: ... + schemas: _containers.MessageMap[str, SchemaSelector] + def __init__(self, extra_resources: _Optional[_Mapping[str, ResourceSelector]] = ..., resources: _Optional[_Mapping[str, ResourceSelector]] = ..., schemas: _Optional[_Mapping[str, SchemaSelector]] = ...) -> None: ... + +class SchemaSelector(_message.Message): + __slots__ = ("api_version", "kind") + API_VERSION_FIELD_NUMBER: _ClassVar[int] + KIND_FIELD_NUMBER: _ClassVar[int] + api_version: str + kind: str + def __init__(self, api_version: _Optional[str] = ..., kind: _Optional[str] = ...) -> None: ... + +class Schema(_message.Message): + __slots__ = ("openapi_v3",) + OPENAPI_V3_FIELD_NUMBER: _ClassVar[int] + openapi_v3: _struct_pb2.Struct + def __init__(self, openapi_v3: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ...) -> None: ... class ResourceSelector(_message.Message): __slots__ = ("api_version", "kind", "match_name", "match_labels", "namespace") diff --git a/crossplane/function/proto/v1beta1/run_function.proto b/crossplane/function/proto/v1beta1/run_function.proto index e53ded4..2815d5b 100644 --- a/crossplane/function/proto/v1beta1/run_function.proto +++ b/crossplane/function/proto/v1beta1/run_function.proto @@ -92,6 +92,13 @@ message RunFunctionRequest { // satisfy the request. This field is only populated when the function uses // resources in its requirements. map required_resources = 8; + + // Optional schemas that the function specified in its requirements. The map + // key corresponds to the key in a RunFunctionResponse's requirements.schemas + // field. If a function requested a schema that could not be found, Crossplane + // sets the map key to an empty Schema message to indicate that it attempted + // to satisfy the request. + map required_schemas = 9; } // Credentials that a function may use to communicate with an external system. @@ -171,6 +178,27 @@ message Requirements { // Resources that this function requires. The map key uniquely identifies the // group of resources. map resources = 2; + + // Schemas that this function requires. The map key uniquely identifies the + // schema request. + map schemas = 3; +} + +// SchemaSelector identifies a resource kind whose OpenAPI schema is requested. +message SchemaSelector { + // API version of the resource kind, e.g. "example.org/v1". + string api_version = 1; + + // Kind of resource, e.g. "MyResource". + string kind = 2; +} + +// Schema represents the OpenAPI schema for a resource kind. +message Schema { + // The OpenAPI v3 schema of the resource kind as unstructured JSON. + // For CRDs this is the spec.versions[].schema.openAPIV3Schema field. + // Empty if Crossplane could not find a schema for the requested kind. + optional google.protobuf.Struct openapi_v3 = 1; } // ResourceSelector selects a group of resources, either by name or by label. diff --git a/crossplane/function/proto/v1beta1/run_function_pb2.py b/crossplane/function/proto/v1beta1/run_function_pb2.py index 9907f35..8e23ac4 100644 --- a/crossplane/function/proto/v1beta1/run_function_pb2.py +++ b/crossplane/function/proto/v1beta1/run_function_pb2.py @@ -26,7 +26,7 @@ from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n4crossplane/function/proto/v1beta1/run_function.proto\x12\x1e\x61piextensions.fn.proto.v1beta1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\"\x80\x07\n\x12RunFunctionRequest\x12\x39\n\x04meta\x18\x01 \x01(\x0b\x32+.apiextensions.fn.proto.v1beta1.RequestMeta\x12\x37\n\x08observed\x18\x02 \x01(\x0b\x32%.apiextensions.fn.proto.v1beta1.State\x12\x36\n\x07\x64\x65sired\x18\x03 \x01(\x0b\x32%.apiextensions.fn.proto.v1beta1.State\x12+\n\x05input\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x12-\n\x07\x63ontext\x18\x05 \x01(\x0b\x32\x17.google.protobuf.StructH\x01\x88\x01\x01\x12\x63\n\x0f\x65xtra_resources\x18\x06 \x03(\x0b\x32\x46.apiextensions.fn.proto.v1beta1.RunFunctionRequest.ExtraResourcesEntryB\x02\x18\x01\x12X\n\x0b\x63redentials\x18\x07 \x03(\x0b\x32\x43.apiextensions.fn.proto.v1beta1.RunFunctionRequest.CredentialsEntry\x12\x65\n\x12required_resources\x18\x08 \x03(\x0b\x32I.apiextensions.fn.proto.v1beta1.RunFunctionRequest.RequiredResourcesEntry\x1a`\n\x13\x45xtraResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x38\n\x05value\x18\x02 \x01(\x0b\x32).apiextensions.fn.proto.v1beta1.Resources:\x02\x38\x01\x1a_\n\x10\x43redentialsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12:\n\x05value\x18\x02 \x01(\x0b\x32+.apiextensions.fn.proto.v1beta1.Credentials:\x02\x38\x01\x1a\x63\n\x16RequiredResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x38\n\x05value\x18\x02 \x01(\x0b\x32).apiextensions.fn.proto.v1beta1.Resources:\x02\x38\x01\x42\x08\n\x06_inputB\n\n\x08_context\"b\n\x0b\x43redentials\x12I\n\x0f\x63redential_data\x18\x01 \x01(\x0b\x32..apiextensions.fn.proto.v1beta1.CredentialDataH\x00\x42\x08\n\x06source\"\x85\x01\n\x0e\x43redentialData\x12\x46\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\x38.apiextensions.fn.proto.v1beta1.CredentialData.DataEntry\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"D\n\tResources\x12\x37\n\x05items\x18\x01 \x03(\x0b\x32(.apiextensions.fn.proto.v1beta1.Resource\"\xb9\x03\n\x13RunFunctionResponse\x12:\n\x04meta\x18\x01 \x01(\x0b\x32,.apiextensions.fn.proto.v1beta1.ResponseMeta\x12\x36\n\x07\x64\x65sired\x18\x02 \x01(\x0b\x32%.apiextensions.fn.proto.v1beta1.State\x12\x37\n\x07results\x18\x03 \x03(\x0b\x32&.apiextensions.fn.proto.v1beta1.Result\x12-\n\x07\x63ontext\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x12\x42\n\x0crequirements\x18\x05 \x01(\x0b\x32,.apiextensions.fn.proto.v1beta1.Requirements\x12=\n\nconditions\x18\x06 \x03(\x0b\x32).apiextensions.fn.proto.v1beta1.Condition\x12,\n\x06output\x18\x07 \x01(\x0b\x32\x17.google.protobuf.StructH\x01\x88\x01\x01\x42\n\n\x08_contextB\t\n\x07_output\"\x1a\n\x0bRequestMeta\x12\x0b\n\x03tag\x18\x01 \x01(\t\"\x8a\x03\n\x0cRequirements\x12]\n\x0f\x65xtra_resources\x18\x01 \x03(\x0b\x32@.apiextensions.fn.proto.v1beta1.Requirements.ExtraResourcesEntryB\x02\x18\x01\x12N\n\tresources\x18\x02 \x03(\x0b\x32;.apiextensions.fn.proto.v1beta1.Requirements.ResourcesEntry\x1ag\n\x13\x45xtraResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12?\n\x05value\x18\x02 \x01(\x0b\x32\x30.apiextensions.fn.proto.v1beta1.ResourceSelector:\x02\x38\x01\x1a\x62\n\x0eResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12?\n\x05value\x18\x02 \x01(\x0b\x32\x30.apiextensions.fn.proto.v1beta1.ResourceSelector:\x02\x38\x01\"\xbf\x01\n\x10ResourceSelector\x12\x13\n\x0b\x61pi_version\x18\x01 \x01(\t\x12\x0c\n\x04kind\x18\x02 \x01(\t\x12\x14\n\nmatch_name\x18\x03 \x01(\tH\x00\x12\x43\n\x0cmatch_labels\x18\x04 \x01(\x0b\x32+.apiextensions.fn.proto.v1beta1.MatchLabelsH\x00\x12\x16\n\tnamespace\x18\x05 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05matchB\x0c\n\n_namespace\"\x85\x01\n\x0bMatchLabels\x12G\n\x06labels\x18\x01 \x03(\x0b\x32\x37.apiextensions.fn.proto.v1beta1.MatchLabels.LabelsEntry\x1a-\n\x0bLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"P\n\x0cResponseMeta\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12+\n\x03ttl\x18\x02 \x01(\x0b\x32\x19.google.protobuf.DurationH\x00\x88\x01\x01\x42\x06\n\x04_ttl\"\xe9\x01\n\x05State\x12;\n\tcomposite\x18\x01 \x01(\x0b\x32(.apiextensions.fn.proto.v1beta1.Resource\x12G\n\tresources\x18\x02 \x03(\x0b\x32\x34.apiextensions.fn.proto.v1beta1.State.ResourcesEntry\x1aZ\n\x0eResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x37\n\x05value\x18\x02 \x01(\x0b\x32(.apiextensions.fn.proto.v1beta1.Resource:\x02\x38\x01\"\x82\x02\n\x08Resource\x12)\n\x08resource\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\x12[\n\x12\x63onnection_details\x18\x02 \x03(\x0b\x32?.apiextensions.fn.proto.v1beta1.Resource.ConnectionDetailsEntry\x12\x34\n\x05ready\x18\x03 \x01(\x0e\x32%.apiextensions.fn.proto.v1beta1.Ready\x1a\x38\n\x16\x43onnectionDetailsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"\xbd\x01\n\x06Result\x12:\n\x08severity\x18\x01 \x01(\x0e\x32(.apiextensions.fn.proto.v1beta1.Severity\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x13\n\x06reason\x18\x03 \x01(\tH\x00\x88\x01\x01\x12;\n\x06target\x18\x04 \x01(\x0e\x32&.apiextensions.fn.proto.v1beta1.TargetH\x01\x88\x01\x01\x42\t\n\x07_reasonB\t\n\x07_target\"\xcb\x01\n\tCondition\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x36\n\x06status\x18\x02 \x01(\x0e\x32&.apiextensions.fn.proto.v1beta1.Status\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x14\n\x07message\x18\x04 \x01(\tH\x00\x88\x01\x01\x12;\n\x06target\x18\x05 \x01(\x0e\x32&.apiextensions.fn.proto.v1beta1.TargetH\x01\x88\x01\x01\x42\n\n\x08_messageB\t\n\x07_target*?\n\x05Ready\x12\x15\n\x11READY_UNSPECIFIED\x10\x00\x12\x0e\n\nREADY_TRUE\x10\x01\x12\x0f\n\x0bREADY_FALSE\x10\x02*c\n\x08Severity\x12\x18\n\x14SEVERITY_UNSPECIFIED\x10\x00\x12\x12\n\x0eSEVERITY_FATAL\x10\x01\x12\x14\n\x10SEVERITY_WARNING\x10\x02\x12\x13\n\x0fSEVERITY_NORMAL\x10\x03*V\n\x06Target\x12\x16\n\x12TARGET_UNSPECIFIED\x10\x00\x12\x14\n\x10TARGET_COMPOSITE\x10\x01\x12\x1e\n\x1aTARGET_COMPOSITE_AND_CLAIM\x10\x02*\x7f\n\x06Status\x12 \n\x1cSTATUS_CONDITION_UNSPECIFIED\x10\x00\x12\x1c\n\x18STATUS_CONDITION_UNKNOWN\x10\x01\x12\x19\n\x15STATUS_CONDITION_TRUE\x10\x02\x12\x1a\n\x16STATUS_CONDITION_FALSE\x10\x03\x32\x91\x01\n\x15\x46unctionRunnerService\x12x\n\x0bRunFunction\x12\x32.apiextensions.fn.proto.v1beta1.RunFunctionRequest\x1a\x33.apiextensions.fn.proto.v1beta1.RunFunctionResponse\"\x00\x42\x36Z4github.com/crossplane/crossplane/v2/proto/fn/v1beta1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n4crossplane/function/proto/v1beta1/run_function.proto\x12\x1e\x61piextensions.fn.proto.v1beta1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\"\xc3\x08\n\x12RunFunctionRequest\x12\x39\n\x04meta\x18\x01 \x01(\x0b\x32+.apiextensions.fn.proto.v1beta1.RequestMeta\x12\x37\n\x08observed\x18\x02 \x01(\x0b\x32%.apiextensions.fn.proto.v1beta1.State\x12\x36\n\x07\x64\x65sired\x18\x03 \x01(\x0b\x32%.apiextensions.fn.proto.v1beta1.State\x12+\n\x05input\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x12-\n\x07\x63ontext\x18\x05 \x01(\x0b\x32\x17.google.protobuf.StructH\x01\x88\x01\x01\x12\x63\n\x0f\x65xtra_resources\x18\x06 \x03(\x0b\x32\x46.apiextensions.fn.proto.v1beta1.RunFunctionRequest.ExtraResourcesEntryB\x02\x18\x01\x12X\n\x0b\x63redentials\x18\x07 \x03(\x0b\x32\x43.apiextensions.fn.proto.v1beta1.RunFunctionRequest.CredentialsEntry\x12\x65\n\x12required_resources\x18\x08 \x03(\x0b\x32I.apiextensions.fn.proto.v1beta1.RunFunctionRequest.RequiredResourcesEntry\x12\x61\n\x10required_schemas\x18\t \x03(\x0b\x32G.apiextensions.fn.proto.v1beta1.RunFunctionRequest.RequiredSchemasEntry\x1a`\n\x13\x45xtraResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x38\n\x05value\x18\x02 \x01(\x0b\x32).apiextensions.fn.proto.v1beta1.Resources:\x02\x38\x01\x1a_\n\x10\x43redentialsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12:\n\x05value\x18\x02 \x01(\x0b\x32+.apiextensions.fn.proto.v1beta1.Credentials:\x02\x38\x01\x1a\x63\n\x16RequiredResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x38\n\x05value\x18\x02 \x01(\x0b\x32).apiextensions.fn.proto.v1beta1.Resources:\x02\x38\x01\x1a^\n\x14RequiredSchemasEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x35\n\x05value\x18\x02 \x01(\x0b\x32&.apiextensions.fn.proto.v1beta1.Schema:\x02\x38\x01\x42\x08\n\x06_inputB\n\n\x08_context\"b\n\x0b\x43redentials\x12I\n\x0f\x63redential_data\x18\x01 \x01(\x0b\x32..apiextensions.fn.proto.v1beta1.CredentialDataH\x00\x42\x08\n\x06source\"\x85\x01\n\x0e\x43redentialData\x12\x46\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\x38.apiextensions.fn.proto.v1beta1.CredentialData.DataEntry\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"D\n\tResources\x12\x37\n\x05items\x18\x01 \x03(\x0b\x32(.apiextensions.fn.proto.v1beta1.Resource\"\xb9\x03\n\x13RunFunctionResponse\x12:\n\x04meta\x18\x01 \x01(\x0b\x32,.apiextensions.fn.proto.v1beta1.ResponseMeta\x12\x36\n\x07\x64\x65sired\x18\x02 \x01(\x0b\x32%.apiextensions.fn.proto.v1beta1.State\x12\x37\n\x07results\x18\x03 \x03(\x0b\x32&.apiextensions.fn.proto.v1beta1.Result\x12-\n\x07\x63ontext\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x12\x42\n\x0crequirements\x18\x05 \x01(\x0b\x32,.apiextensions.fn.proto.v1beta1.Requirements\x12=\n\nconditions\x18\x06 \x03(\x0b\x32).apiextensions.fn.proto.v1beta1.Condition\x12,\n\x06output\x18\x07 \x01(\x0b\x32\x17.google.protobuf.StructH\x01\x88\x01\x01\x42\n\n\x08_contextB\t\n\x07_output\"\x1a\n\x0bRequestMeta\x12\x0b\n\x03tag\x18\x01 \x01(\t\"\xb6\x04\n\x0cRequirements\x12]\n\x0f\x65xtra_resources\x18\x01 \x03(\x0b\x32@.apiextensions.fn.proto.v1beta1.Requirements.ExtraResourcesEntryB\x02\x18\x01\x12N\n\tresources\x18\x02 \x03(\x0b\x32;.apiextensions.fn.proto.v1beta1.Requirements.ResourcesEntry\x12J\n\x07schemas\x18\x03 \x03(\x0b\x32\x39.apiextensions.fn.proto.v1beta1.Requirements.SchemasEntry\x1ag\n\x13\x45xtraResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12?\n\x05value\x18\x02 \x01(\x0b\x32\x30.apiextensions.fn.proto.v1beta1.ResourceSelector:\x02\x38\x01\x1a\x62\n\x0eResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12?\n\x05value\x18\x02 \x01(\x0b\x32\x30.apiextensions.fn.proto.v1beta1.ResourceSelector:\x02\x38\x01\x1a^\n\x0cSchemasEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12=\n\x05value\x18\x02 \x01(\x0b\x32..apiextensions.fn.proto.v1beta1.SchemaSelector:\x02\x38\x01\"3\n\x0eSchemaSelector\x12\x13\n\x0b\x61pi_version\x18\x01 \x01(\t\x12\x0c\n\x04kind\x18\x02 \x01(\t\"I\n\x06Schema\x12\x30\n\nopenapi_v3\x18\x01 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x88\x01\x01\x42\r\n\x0b_openapi_v3\"\xbf\x01\n\x10ResourceSelector\x12\x13\n\x0b\x61pi_version\x18\x01 \x01(\t\x12\x0c\n\x04kind\x18\x02 \x01(\t\x12\x14\n\nmatch_name\x18\x03 \x01(\tH\x00\x12\x43\n\x0cmatch_labels\x18\x04 \x01(\x0b\x32+.apiextensions.fn.proto.v1beta1.MatchLabelsH\x00\x12\x16\n\tnamespace\x18\x05 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05matchB\x0c\n\n_namespace\"\x85\x01\n\x0bMatchLabels\x12G\n\x06labels\x18\x01 \x03(\x0b\x32\x37.apiextensions.fn.proto.v1beta1.MatchLabels.LabelsEntry\x1a-\n\x0bLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"P\n\x0cResponseMeta\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12+\n\x03ttl\x18\x02 \x01(\x0b\x32\x19.google.protobuf.DurationH\x00\x88\x01\x01\x42\x06\n\x04_ttl\"\xe9\x01\n\x05State\x12;\n\tcomposite\x18\x01 \x01(\x0b\x32(.apiextensions.fn.proto.v1beta1.Resource\x12G\n\tresources\x18\x02 \x03(\x0b\x32\x34.apiextensions.fn.proto.v1beta1.State.ResourcesEntry\x1aZ\n\x0eResourcesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x37\n\x05value\x18\x02 \x01(\x0b\x32(.apiextensions.fn.proto.v1beta1.Resource:\x02\x38\x01\"\x82\x02\n\x08Resource\x12)\n\x08resource\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\x12[\n\x12\x63onnection_details\x18\x02 \x03(\x0b\x32?.apiextensions.fn.proto.v1beta1.Resource.ConnectionDetailsEntry\x12\x34\n\x05ready\x18\x03 \x01(\x0e\x32%.apiextensions.fn.proto.v1beta1.Ready\x1a\x38\n\x16\x43onnectionDetailsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"\xbd\x01\n\x06Result\x12:\n\x08severity\x18\x01 \x01(\x0e\x32(.apiextensions.fn.proto.v1beta1.Severity\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x13\n\x06reason\x18\x03 \x01(\tH\x00\x88\x01\x01\x12;\n\x06target\x18\x04 \x01(\x0e\x32&.apiextensions.fn.proto.v1beta1.TargetH\x01\x88\x01\x01\x42\t\n\x07_reasonB\t\n\x07_target\"\xcb\x01\n\tCondition\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x36\n\x06status\x18\x02 \x01(\x0e\x32&.apiextensions.fn.proto.v1beta1.Status\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x14\n\x07message\x18\x04 \x01(\tH\x00\x88\x01\x01\x12;\n\x06target\x18\x05 \x01(\x0e\x32&.apiextensions.fn.proto.v1beta1.TargetH\x01\x88\x01\x01\x42\n\n\x08_messageB\t\n\x07_target*?\n\x05Ready\x12\x15\n\x11READY_UNSPECIFIED\x10\x00\x12\x0e\n\nREADY_TRUE\x10\x01\x12\x0f\n\x0bREADY_FALSE\x10\x02*c\n\x08Severity\x12\x18\n\x14SEVERITY_UNSPECIFIED\x10\x00\x12\x12\n\x0eSEVERITY_FATAL\x10\x01\x12\x14\n\x10SEVERITY_WARNING\x10\x02\x12\x13\n\x0fSEVERITY_NORMAL\x10\x03*V\n\x06Target\x12\x16\n\x12TARGET_UNSPECIFIED\x10\x00\x12\x14\n\x10TARGET_COMPOSITE\x10\x01\x12\x1e\n\x1aTARGET_COMPOSITE_AND_CLAIM\x10\x02*\x7f\n\x06Status\x12 \n\x1cSTATUS_CONDITION_UNSPECIFIED\x10\x00\x12\x1c\n\x18STATUS_CONDITION_UNKNOWN\x10\x01\x12\x19\n\x15STATUS_CONDITION_TRUE\x10\x02\x12\x1a\n\x16STATUS_CONDITION_FALSE\x10\x03\x32\x91\x01\n\x15\x46unctionRunnerService\x12x\n\x0bRunFunction\x12\x32.apiextensions.fn.proto.v1beta1.RunFunctionRequest\x1a\x33.apiextensions.fn.proto.v1beta1.RunFunctionResponse\"\x00\x42\x36Z4github.com/crossplane/crossplane/v2/proto/fn/v1beta1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -40,6 +40,8 @@ _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_options = b'8\001' _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._loaded_options = None _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._serialized_options = b'8\001' + _globals['_RUNFUNCTIONREQUEST_REQUIREDSCHEMASENTRY']._loaded_options = None + _globals['_RUNFUNCTIONREQUEST_REQUIREDSCHEMASENTRY']._serialized_options = b'8\001' _globals['_RUNFUNCTIONREQUEST'].fields_by_name['extra_resources']._loaded_options = None _globals['_RUNFUNCTIONREQUEST'].fields_by_name['extra_resources']._serialized_options = b'\030\001' _globals['_CREDENTIALDATA_DATAENTRY']._loaded_options = None @@ -48,6 +50,8 @@ _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_options = b'8\001' _globals['_REQUIREMENTS_RESOURCESENTRY']._loaded_options = None _globals['_REQUIREMENTS_RESOURCESENTRY']._serialized_options = b'8\001' + _globals['_REQUIREMENTS_SCHEMASENTRY']._loaded_options = None + _globals['_REQUIREMENTS_SCHEMASENTRY']._serialized_options = b'8\001' _globals['_REQUIREMENTS'].fields_by_name['extra_resources']._loaded_options = None _globals['_REQUIREMENTS'].fields_by_name['extra_resources']._serialized_options = b'\030\001' _globals['_MATCHLABELS_LABELSENTRY']._loaded_options = None @@ -56,60 +60,68 @@ _globals['_STATE_RESOURCESENTRY']._serialized_options = b'8\001' _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._loaded_options = None _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_options = b'8\001' - _globals['_READY']._serialized_start=3531 - _globals['_READY']._serialized_end=3594 - _globals['_SEVERITY']._serialized_start=3596 - _globals['_SEVERITY']._serialized_end=3695 - _globals['_TARGET']._serialized_start=3697 - _globals['_TARGET']._serialized_end=3783 - _globals['_STATUS']._serialized_start=3785 - _globals['_STATUS']._serialized_end=3912 + _globals['_READY']._serialized_start=4026 + _globals['_READY']._serialized_end=4089 + _globals['_SEVERITY']._serialized_start=4091 + _globals['_SEVERITY']._serialized_end=4190 + _globals['_TARGET']._serialized_start=4192 + _globals['_TARGET']._serialized_end=4278 + _globals['_STATUS']._serialized_start=4280 + _globals['_STATUS']._serialized_end=4407 _globals['_RUNFUNCTIONREQUEST']._serialized_start=151 - _globals['_RUNFUNCTIONREQUEST']._serialized_end=1047 - _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._serialized_start=731 - _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._serialized_end=827 - _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_start=829 - _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_end=924 - _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._serialized_start=926 - _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._serialized_end=1025 - _globals['_CREDENTIALS']._serialized_start=1049 - _globals['_CREDENTIALS']._serialized_end=1147 - _globals['_CREDENTIALDATA']._serialized_start=1150 - _globals['_CREDENTIALDATA']._serialized_end=1283 - _globals['_CREDENTIALDATA_DATAENTRY']._serialized_start=1240 - _globals['_CREDENTIALDATA_DATAENTRY']._serialized_end=1283 - _globals['_RESOURCES']._serialized_start=1285 - _globals['_RESOURCES']._serialized_end=1353 - _globals['_RUNFUNCTIONRESPONSE']._serialized_start=1356 - _globals['_RUNFUNCTIONRESPONSE']._serialized_end=1797 - _globals['_REQUESTMETA']._serialized_start=1799 - _globals['_REQUESTMETA']._serialized_end=1825 - _globals['_REQUIREMENTS']._serialized_start=1828 - _globals['_REQUIREMENTS']._serialized_end=2222 - _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_start=2019 - _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_end=2122 - _globals['_REQUIREMENTS_RESOURCESENTRY']._serialized_start=2124 - _globals['_REQUIREMENTS_RESOURCESENTRY']._serialized_end=2222 - _globals['_RESOURCESELECTOR']._serialized_start=2225 - _globals['_RESOURCESELECTOR']._serialized_end=2416 - _globals['_MATCHLABELS']._serialized_start=2419 - _globals['_MATCHLABELS']._serialized_end=2552 - _globals['_MATCHLABELS_LABELSENTRY']._serialized_start=2507 - _globals['_MATCHLABELS_LABELSENTRY']._serialized_end=2552 - _globals['_RESPONSEMETA']._serialized_start=2554 - _globals['_RESPONSEMETA']._serialized_end=2634 - _globals['_STATE']._serialized_start=2637 - _globals['_STATE']._serialized_end=2870 - _globals['_STATE_RESOURCESENTRY']._serialized_start=2780 - _globals['_STATE_RESOURCESENTRY']._serialized_end=2870 - _globals['_RESOURCE']._serialized_start=2873 - _globals['_RESOURCE']._serialized_end=3131 - _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_start=3075 - _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_end=3131 - _globals['_RESULT']._serialized_start=3134 - _globals['_RESULT']._serialized_end=3323 - _globals['_CONDITION']._serialized_start=3326 - _globals['_CONDITION']._serialized_end=3529 - _globals['_FUNCTIONRUNNERSERVICE']._serialized_start=3915 - _globals['_FUNCTIONRUNNERSERVICE']._serialized_end=4060 + _globals['_RUNFUNCTIONREQUEST']._serialized_end=1242 + _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._serialized_start=830 + _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._serialized_end=926 + _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_start=928 + _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_end=1023 + _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._serialized_start=1025 + _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._serialized_end=1124 + _globals['_RUNFUNCTIONREQUEST_REQUIREDSCHEMASENTRY']._serialized_start=1126 + _globals['_RUNFUNCTIONREQUEST_REQUIREDSCHEMASENTRY']._serialized_end=1220 + _globals['_CREDENTIALS']._serialized_start=1244 + _globals['_CREDENTIALS']._serialized_end=1342 + _globals['_CREDENTIALDATA']._serialized_start=1345 + _globals['_CREDENTIALDATA']._serialized_end=1478 + _globals['_CREDENTIALDATA_DATAENTRY']._serialized_start=1435 + _globals['_CREDENTIALDATA_DATAENTRY']._serialized_end=1478 + _globals['_RESOURCES']._serialized_start=1480 + _globals['_RESOURCES']._serialized_end=1548 + _globals['_RUNFUNCTIONRESPONSE']._serialized_start=1551 + _globals['_RUNFUNCTIONRESPONSE']._serialized_end=1992 + _globals['_REQUESTMETA']._serialized_start=1994 + _globals['_REQUESTMETA']._serialized_end=2020 + _globals['_REQUIREMENTS']._serialized_start=2023 + _globals['_REQUIREMENTS']._serialized_end=2589 + _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_start=2290 + _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_end=2393 + _globals['_REQUIREMENTS_RESOURCESENTRY']._serialized_start=2395 + _globals['_REQUIREMENTS_RESOURCESENTRY']._serialized_end=2493 + _globals['_REQUIREMENTS_SCHEMASENTRY']._serialized_start=2495 + _globals['_REQUIREMENTS_SCHEMASENTRY']._serialized_end=2589 + _globals['_SCHEMASELECTOR']._serialized_start=2591 + _globals['_SCHEMASELECTOR']._serialized_end=2642 + _globals['_SCHEMA']._serialized_start=2644 + _globals['_SCHEMA']._serialized_end=2717 + _globals['_RESOURCESELECTOR']._serialized_start=2720 + _globals['_RESOURCESELECTOR']._serialized_end=2911 + _globals['_MATCHLABELS']._serialized_start=2914 + _globals['_MATCHLABELS']._serialized_end=3047 + _globals['_MATCHLABELS_LABELSENTRY']._serialized_start=3002 + _globals['_MATCHLABELS_LABELSENTRY']._serialized_end=3047 + _globals['_RESPONSEMETA']._serialized_start=3049 + _globals['_RESPONSEMETA']._serialized_end=3129 + _globals['_STATE']._serialized_start=3132 + _globals['_STATE']._serialized_end=3365 + _globals['_STATE_RESOURCESENTRY']._serialized_start=3275 + _globals['_STATE_RESOURCESENTRY']._serialized_end=3365 + _globals['_RESOURCE']._serialized_start=3368 + _globals['_RESOURCE']._serialized_end=3626 + _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_start=3570 + _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_end=3626 + _globals['_RESULT']._serialized_start=3629 + _globals['_RESULT']._serialized_end=3818 + _globals['_CONDITION']._serialized_start=3821 + _globals['_CONDITION']._serialized_end=4024 + _globals['_FUNCTIONRUNNERSERVICE']._serialized_start=4410 + _globals['_FUNCTIONRUNNERSERVICE']._serialized_end=4555 # @@protoc_insertion_point(module_scope) diff --git a/crossplane/function/proto/v1beta1/run_function_pb2.pyi b/crossplane/function/proto/v1beta1/run_function_pb2.pyi index 534fe30..30ad04f 100644 --- a/crossplane/function/proto/v1beta1/run_function_pb2.pyi +++ b/crossplane/function/proto/v1beta1/run_function_pb2.pyi @@ -52,7 +52,7 @@ STATUS_CONDITION_TRUE: Status STATUS_CONDITION_FALSE: Status class RunFunctionRequest(_message.Message): - __slots__ = ("meta", "observed", "desired", "input", "context", "extra_resources", "credentials", "required_resources") + __slots__ = ("meta", "observed", "desired", "input", "context", "extra_resources", "credentials", "required_resources", "required_schemas") class ExtraResourcesEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -74,6 +74,13 @@ class RunFunctionRequest(_message.Message): key: str value: Resources def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[Resources, _Mapping]] = ...) -> None: ... + class RequiredSchemasEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: Schema + def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[Schema, _Mapping]] = ...) -> None: ... META_FIELD_NUMBER: _ClassVar[int] OBSERVED_FIELD_NUMBER: _ClassVar[int] DESIRED_FIELD_NUMBER: _ClassVar[int] @@ -82,6 +89,7 @@ class RunFunctionRequest(_message.Message): EXTRA_RESOURCES_FIELD_NUMBER: _ClassVar[int] CREDENTIALS_FIELD_NUMBER: _ClassVar[int] REQUIRED_RESOURCES_FIELD_NUMBER: _ClassVar[int] + REQUIRED_SCHEMAS_FIELD_NUMBER: _ClassVar[int] meta: RequestMeta observed: State desired: State @@ -90,7 +98,8 @@ class RunFunctionRequest(_message.Message): extra_resources: _containers.MessageMap[str, Resources] credentials: _containers.MessageMap[str, Credentials] required_resources: _containers.MessageMap[str, Resources] - def __init__(self, meta: _Optional[_Union[RequestMeta, _Mapping]] = ..., observed: _Optional[_Union[State, _Mapping]] = ..., desired: _Optional[_Union[State, _Mapping]] = ..., input: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., context: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., extra_resources: _Optional[_Mapping[str, Resources]] = ..., credentials: _Optional[_Mapping[str, Credentials]] = ..., required_resources: _Optional[_Mapping[str, Resources]] = ...) -> None: ... + required_schemas: _containers.MessageMap[str, Schema] + def __init__(self, meta: _Optional[_Union[RequestMeta, _Mapping]] = ..., observed: _Optional[_Union[State, _Mapping]] = ..., desired: _Optional[_Union[State, _Mapping]] = ..., input: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., context: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., extra_resources: _Optional[_Mapping[str, Resources]] = ..., credentials: _Optional[_Mapping[str, Credentials]] = ..., required_resources: _Optional[_Mapping[str, Resources]] = ..., required_schemas: _Optional[_Mapping[str, Schema]] = ...) -> None: ... class Credentials(_message.Message): __slots__ = ("credential_data",) @@ -142,7 +151,7 @@ class RequestMeta(_message.Message): def __init__(self, tag: _Optional[str] = ...) -> None: ... class Requirements(_message.Message): - __slots__ = ("extra_resources", "resources") + __slots__ = ("extra_resources", "resources", "schemas") class ExtraResourcesEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -157,11 +166,34 @@ class Requirements(_message.Message): key: str value: ResourceSelector def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[ResourceSelector, _Mapping]] = ...) -> None: ... + class SchemasEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: SchemaSelector + def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[SchemaSelector, _Mapping]] = ...) -> None: ... EXTRA_RESOURCES_FIELD_NUMBER: _ClassVar[int] RESOURCES_FIELD_NUMBER: _ClassVar[int] + SCHEMAS_FIELD_NUMBER: _ClassVar[int] extra_resources: _containers.MessageMap[str, ResourceSelector] resources: _containers.MessageMap[str, ResourceSelector] - def __init__(self, extra_resources: _Optional[_Mapping[str, ResourceSelector]] = ..., resources: _Optional[_Mapping[str, ResourceSelector]] = ...) -> None: ... + schemas: _containers.MessageMap[str, SchemaSelector] + def __init__(self, extra_resources: _Optional[_Mapping[str, ResourceSelector]] = ..., resources: _Optional[_Mapping[str, ResourceSelector]] = ..., schemas: _Optional[_Mapping[str, SchemaSelector]] = ...) -> None: ... + +class SchemaSelector(_message.Message): + __slots__ = ("api_version", "kind") + API_VERSION_FIELD_NUMBER: _ClassVar[int] + KIND_FIELD_NUMBER: _ClassVar[int] + api_version: str + kind: str + def __init__(self, api_version: _Optional[str] = ..., kind: _Optional[str] = ...) -> None: ... + +class Schema(_message.Message): + __slots__ = ("openapi_v3",) + OPENAPI_V3_FIELD_NUMBER: _ClassVar[int] + openapi_v3: _struct_pb2.Struct + def __init__(self, openapi_v3: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ...) -> None: ... class ResourceSelector(_message.Message): __slots__ = ("api_version", "kind", "match_name", "match_labels", "namespace") diff --git a/crossplane/function/request.py b/crossplane/function/request.py index ad408c9..59c7695 100644 --- a/crossplane/function/request.py +++ b/crossplane/function/request.py @@ -41,6 +41,21 @@ def get_required_resources(req: fnv1.RunFunctionRequest, name: str) -> list[dict Required resources are previously called "extra resources" in composition functions. For operation functions, there are no observed resources, so all resources are "required" resources that the function requested. + + Note: This returns an empty list both when the requirement hasn't been + resolved yet, and when it was resolved but no resources matched. To + distinguish between these cases, check whether Crossplane resolved the + requirement using `name in req.required_resources`: + + # Always declare requirements - Crossplane considers them satisfied + # when they stabilize across calls. + response.require_resources(rsp, name, ...) + + if name in req.required_resources: + resources = request.get_required_resources(req, name) + if not resources: + # Crossplane resolved the requirement, but found no matches + response.fatal(rsp, "no matching resources found") """ if name not in req.required_resources: return [] @@ -116,3 +131,46 @@ def get_credentials(req: fnv1.RunFunctionRequest, name: str) -> Credentials: # If no recognized source type is set, return empty return empty + + +def get_required_schema(req: fnv1.RunFunctionRequest, name: str) -> dict | None: + """Get a required OpenAPI schema by name from the request. + + Args: + req: The RunFunctionRequest containing required schemas. + name: The name of the required schema to get. + + Returns: + The OpenAPI v3 schema as a dictionary, or None if not found. + + Note: This returns None both when the requirement hasn't been resolved yet, + and when it was resolved but the schema wasn't found. To distinguish between + these cases, check whether Crossplane resolved the requirement using + `name in req.required_schemas`: + + # Always declare requirements - Crossplane considers them satisfied + # when they stabilize across calls. + response.require_schema(rsp, name, "example.org/v1", "MyKind") + + if name in req.required_schemas: + schema = request.get_required_schema(req, name) + if schema is None: + # Crossplane resolved the requirement, but couldn't find it + response.fatal(rsp, "schema not found") + + The returned schema can be used with libraries like openapi-schema-validator + or jsonschema for validation: + + schema = request.get_required_schema(req, "my-schema") + if schema: + from openapi_schema_validator import validate + validate(resource, schema) + """ + if name not in req.required_schemas: + return None + + schema = req.required_schemas[name] + if not schema.HasField("openapi_v3"): + return None + + return resource.struct_to_dict(schema.openapi_v3) diff --git a/crossplane/function/response.py b/crossplane/function/response.py index 72222b6..5392d36 100644 --- a/crossplane/function/response.py +++ b/crossplane/function/response.py @@ -149,3 +149,32 @@ def require_resources( # noqa: PLR0913 selector.namespace = namespace rsp.requirements.resources[name].CopyFrom(selector) + + +def require_schema( + rsp: fnv1.RunFunctionResponse, + name: str, + api_version: str, + kind: str, +) -> None: + """Add a schema requirement to the response. + + Args: + rsp: The RunFunctionResponse to update. + name: The name to use for this requirement. + api_version: The API version of the resource kind, e.g. "example.org/v1". + kind: The kind of resource, e.g. "MyResource". + + This tells Crossplane to fetch the OpenAPI schema for the specified resource + kind and include it in the next call to the function in + req.required_schemas[name]. Use request.get_required_schema to retrieve it. + + For CRDs, Crossplane returns the spec.versions[].schema.openAPIV3Schema field. + If Crossplane cannot find a schema for the requested kind, the schema will be + empty (get_required_schema will return None). + """ + selector = fnv1.SchemaSelector( + api_version=api_version, + kind=kind, + ) + rsp.requirements.schemas[name].CopyFrom(selector) diff --git a/tests/test_request.py b/tests/test_request.py index a517814..0e497ba 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -264,6 +264,71 @@ class TestCase: dataclasses.asdict(case.want), dataclasses.asdict(got), case.reason ) + def test_get_required_schema(self) -> None: + @dataclasses.dataclass + class TestCase: + reason: str + req: fnv1.RunFunctionRequest + name: str + want: dict | None + + cases = [ + TestCase( + reason="Should return None when schema name not found.", + req=fnv1.RunFunctionRequest(), + name="non-existent", + want=None, + ), + TestCase( + reason="Should return None when schema exists but is empty.", + req=fnv1.RunFunctionRequest( + required_schemas={ + "empty-schema": fnv1.Schema(), + } + ), + name="empty-schema", + want=None, + ), + TestCase( + reason="Should return schema when it exists.", + req=fnv1.RunFunctionRequest( + required_schemas={ + "my-schema": fnv1.Schema( + openapi_v3=resource.dict_to_struct( + { + "type": "object", + "properties": { + "spec": { + "type": "object", + "properties": { + "replicas": {"type": "integer"}, + }, + }, + }, + } + ) + ), + } + ), + name="my-schema", + want={ + "type": "object", + "properties": { + "spec": { + "type": "object", + "properties": { + "replicas": {"type": "integer"}, + }, + }, + }, + }, + ), + ] + + for case in cases: + got = request.get_required_schema(case.req, case.name) + self.assertEqual(case.want, got, case.reason) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_response.py b/tests/test_response.py index 9e33153..763a37f 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -215,6 +215,59 @@ def test_require_resources_invalid_args(self) -> None: match_labels=None, ) + def test_require_schema(self) -> None: + @dataclasses.dataclass + class TestCase: + reason: str + rsp: fnv1.RunFunctionResponse + name: str + api_version: str + kind: str + want_selector: fnv1.SchemaSelector + + cases = [ + TestCase( + reason="Should create schema requirement.", + rsp=fnv1.RunFunctionResponse(), + name="bucket-schema", + api_version="s3.aws.upbound.io/v1beta2", + kind="Bucket", + want_selector=fnv1.SchemaSelector( + api_version="s3.aws.upbound.io/v1beta2", + kind="Bucket", + ), + ), + TestCase( + reason="Should create schema requirement for core types.", + rsp=fnv1.RunFunctionResponse(), + name="pod-schema", + api_version="v1", + kind="Pod", + want_selector=fnv1.SchemaSelector( + api_version="v1", + kind="Pod", + ), + ), + ] + + for case in cases: + response.require_schema( + case.rsp, + case.name, + case.api_version, + case.kind, + ) + + # Check that the requirement was added + self.assertIn(case.name, case.rsp.requirements.schemas, case.reason) + got_selector = case.rsp.requirements.schemas[case.name] + + self.assertEqual( + json_format.MessageToJson(case.want_selector, sort_keys=True), + json_format.MessageToJson(got_selector, sort_keys=True), + case.reason, + ) + if __name__ == "__main__": unittest.main()