diff --git a/crossplane/function/proto/v1/run_function.proto b/crossplane/function/proto/v1/run_function.proto index a6ca33a..e58a4b5 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. @@ -156,6 +163,44 @@ message RequestMeta { // An opaque string identifying a request. Requests with identical tags will // be otherwise identical. string tag = 1; + + // Capabilities supported by this version of Crossplane. Functions may use + // this to determine whether Crossplane will honor certain fields in their + // response, or populate certain fields in their request. + repeated Capability capabilities = 2; +} + +// Capability indicates that Crossplane supports a particular feature. +// Functions can check for capabilities to determine whether Crossplane will +// honor a particular request or response field. +enum Capability { + CAPABILITY_UNSPECIFIED = 0; + + // Crossplane sends capabilities in RequestMeta. If this capability is + // present, the function knows that if another capability is absent, it's + // because Crossplane doesn't support it (not because Crossplane predates + // capability advertisement). Added in Crossplane v2.2. + CAPABILITY_CAPABILITIES = 1; + + // Crossplane supports the requirements.resources field. Functions can return + // resource requirements and Crossplane will fetch the requested resources and + // return them in required_resources. Added in Crossplane v1.15. + CAPABILITY_REQUIRED_RESOURCES = 2; + + // Crossplane supports the credentials field. Functions can receive + // credentials from secrets specified in the Composition. Added in Crossplane + // v1.16. + CAPABILITY_CREDENTIALS = 3; + + // Crossplane supports the conditions field. Functions can return status + // conditions to be applied to the XR and optionally its claim. Added in + // Crossplane v1.17. + CAPABILITY_CONDITIONS = 4; + + // Crossplane supports the requirements.schemas field. Functions can request + // OpenAPI schemas and Crossplane will return them in required_schemas. Added + // in Crossplane v2.2. + CAPABILITY_REQUIRED_SCHEMAS = 5; } // Requirements that must be satisfied for a function to run successfully. @@ -169,6 +214,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. @@ -267,7 +333,7 @@ message Resource { // * A function should set this field to READY_TRUE in a RunFunctionResponse // to indicate that a desired XR is ready. This overwrites the standard // readiness detection that determines the ready state of the composite by the - // ready state of the the composed resources. + // ready state of the composed resources. // // Ready is only used for composition. It's ignored by Operations. Ready ready = 3; @@ -367,4 +433,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..5fe00b0 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\"W\n\x0bRequestMeta\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12;\n\x0c\x63\x61pabilities\x18\x02 \x03(\x0e\x32%.apiextensions.fn.proto.v1.Capability\"\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*\xc0\x01\n\nCapability\x12\x1a\n\x16\x43\x41PABILITY_UNSPECIFIED\x10\x00\x12\x1b\n\x17\x43\x41PABILITY_CAPABILITIES\x10\x01\x12!\n\x1d\x43\x41PABILITY_REQUIRED_RESOURCES\x10\x02\x12\x1a\n\x16\x43\x41PABILITY_CREDENTIALS\x10\x03\x12\x19\n\x15\x43\x41PABILITY_CONDITIONS\x10\x04\x12\x1f\n\x1b\x43\x41PABILITY_REQUIRED_SCHEMAS\x10\x05*?\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,70 @@ _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['_CAPABILITY']._serialized_start=3898 + _globals['_CAPABILITY']._serialized_end=4090 + _globals['_READY']._serialized_start=4092 + _globals['_READY']._serialized_end=4155 + _globals['_SEVERITY']._serialized_start=4157 + _globals['_SEVERITY']._serialized_end=4256 + _globals['_TARGET']._serialized_start=4258 + _globals['_TARGET']._serialized_end=4344 + _globals['_STATUS']._serialized_start=4346 + _globals['_STATUS']._serialized_end=4473 _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=1976 + _globals['_REQUIREMENTS']._serialized_start=1979 + _globals['_REQUIREMENTS']._serialized_end=2515 + _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_start=2231 + _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_end=2329 + _globals['_REQUIREMENTS_RESOURCESENTRY']._serialized_start=2331 + _globals['_REQUIREMENTS_RESOURCESENTRY']._serialized_end=2424 + _globals['_REQUIREMENTS_SCHEMASENTRY']._serialized_start=2426 + _globals['_REQUIREMENTS_SCHEMASENTRY']._serialized_end=2515 + _globals['_SCHEMASELECTOR']._serialized_start=2517 + _globals['_SCHEMASELECTOR']._serialized_end=2568 + _globals['_SCHEMA']._serialized_start=2570 + _globals['_SCHEMA']._serialized_end=2643 + _globals['_RESOURCESELECTOR']._serialized_start=2646 + _globals['_RESOURCESELECTOR']._serialized_end=2832 + _globals['_MATCHLABELS']._serialized_start=2835 + _globals['_MATCHLABELS']._serialized_end=2963 + _globals['_MATCHLABELS_LABELSENTRY']._serialized_start=2918 + _globals['_MATCHLABELS_LABELSENTRY']._serialized_end=2963 + _globals['_RESPONSEMETA']._serialized_start=2965 + _globals['_RESPONSEMETA']._serialized_end=3045 + _globals['_STATE']._serialized_start=3048 + _globals['_STATE']._serialized_end=3266 + _globals['_STATE_RESOURCESENTRY']._serialized_start=3181 + _globals['_STATE_RESOURCESENTRY']._serialized_end=3266 + _globals['_RESOURCE']._serialized_start=3269 + _globals['_RESOURCE']._serialized_end=3517 + _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_start=3461 + _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_end=3517 + _globals['_RESULT']._serialized_start=3520 + _globals['_RESULT']._serialized_end=3699 + _globals['_CONDITION']._serialized_start=3702 + _globals['_CONDITION']._serialized_end=3895 + _globals['_FUNCTIONRUNNERSERVICE']._serialized_start=4476 + _globals['_FUNCTIONRUNNERSERVICE']._serialized_end=4611 # @@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..d0d29ca 100644 --- a/crossplane/function/proto/v1/run_function_pb2.pyi +++ b/crossplane/function/proto/v1/run_function_pb2.pyi @@ -11,6 +11,15 @@ from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor +class Capability(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + CAPABILITY_UNSPECIFIED: _ClassVar[Capability] + CAPABILITY_CAPABILITIES: _ClassVar[Capability] + CAPABILITY_REQUIRED_RESOURCES: _ClassVar[Capability] + CAPABILITY_CREDENTIALS: _ClassVar[Capability] + CAPABILITY_CONDITIONS: _ClassVar[Capability] + CAPABILITY_REQUIRED_SCHEMAS: _ClassVar[Capability] + class Ready(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () READY_UNSPECIFIED: _ClassVar[Ready] @@ -36,6 +45,12 @@ class Status(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): STATUS_CONDITION_UNKNOWN: _ClassVar[Status] STATUS_CONDITION_TRUE: _ClassVar[Status] STATUS_CONDITION_FALSE: _ClassVar[Status] +CAPABILITY_UNSPECIFIED: Capability +CAPABILITY_CAPABILITIES: Capability +CAPABILITY_REQUIRED_RESOURCES: Capability +CAPABILITY_CREDENTIALS: Capability +CAPABILITY_CONDITIONS: Capability +CAPABILITY_REQUIRED_SCHEMAS: Capability READY_UNSPECIFIED: Ready READY_TRUE: Ready READY_FALSE: Ready @@ -52,7 +67,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 +89,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 +104,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 +113,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",) @@ -136,13 +160,15 @@ class RunFunctionResponse(_message.Message): def __init__(self, meta: _Optional[_Union[ResponseMeta, _Mapping]] = ..., desired: _Optional[_Union[State, _Mapping]] = ..., results: _Optional[_Iterable[_Union[Result, _Mapping]]] = ..., context: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., requirements: _Optional[_Union[Requirements, _Mapping]] = ..., conditions: _Optional[_Iterable[_Union[Condition, _Mapping]]] = ..., output: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ...) -> None: ... class RequestMeta(_message.Message): - __slots__ = ("tag",) + __slots__ = ("tag", "capabilities") TAG_FIELD_NUMBER: _ClassVar[int] + CAPABILITIES_FIELD_NUMBER: _ClassVar[int] tag: str - def __init__(self, tag: _Optional[str] = ...) -> None: ... + capabilities: _containers.RepeatedScalarFieldContainer[Capability] + def __init__(self, tag: _Optional[str] = ..., capabilities: _Optional[_Iterable[_Union[Capability, 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 +183,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..667c740 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. @@ -158,6 +165,44 @@ message RequestMeta { // An opaque string identifying a request. Requests with identical tags will // be otherwise identical. string tag = 1; + + // Capabilities supported by this version of Crossplane. Functions may use + // this to determine whether Crossplane will honor certain fields in their + // response, or populate certain fields in their request. + repeated Capability capabilities = 2; +} + +// Capability indicates that Crossplane supports a particular feature. +// Functions can check for capabilities to determine whether Crossplane will +// honor a particular request or response field. +enum Capability { + CAPABILITY_UNSPECIFIED = 0; + + // Crossplane sends capabilities in RequestMeta. If this capability is + // present, the function knows that if another capability is absent, it's + // because Crossplane doesn't support it (not because Crossplane predates + // capability advertisement). Added in Crossplane v2.2. + CAPABILITY_CAPABILITIES = 1; + + // Crossplane supports the requirements.resources field. Functions can return + // resource requirements and Crossplane will fetch the requested resources and + // return them in required_resources. Added in Crossplane v1.15. + CAPABILITY_REQUIRED_RESOURCES = 2; + + // Crossplane supports the credentials field. Functions can receive + // credentials from secrets specified in the Composition. Added in Crossplane + // v1.16. + CAPABILITY_CREDENTIALS = 3; + + // Crossplane supports the conditions field. Functions can return status + // conditions to be applied to the XR and optionally its claim. Added in + // Crossplane v1.17. + CAPABILITY_CONDITIONS = 4; + + // Crossplane supports the requirements.schemas field. Functions can request + // OpenAPI schemas and Crossplane will return them in required_schemas. Added + // in Crossplane v2.2. + CAPABILITY_REQUIRED_SCHEMAS = 5; } // Requirements that must be satisfied for a function to run successfully. @@ -171,6 +216,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. @@ -269,7 +335,7 @@ message Resource { // * A function should set this field to READY_TRUE in a RunFunctionResponse // to indicate that a desired XR is ready. This overwrites the standard // readiness detection that determines the ready state of the composite by the - // ready state of the the composed resources. + // ready state of the composed resources. // // Ready is only used for composition. It's ignored by Operations. Ready ready = 3; diff --git a/crossplane/function/proto/v1beta1/run_function_pb2.py b/crossplane/function/proto/v1beta1/run_function_pb2.py index 9907f35..3f3ebd4 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\"\\\n\x0bRequestMeta\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12@\n\x0c\x63\x61pabilities\x18\x02 \x03(\x0e\x32*.apiextensions.fn.proto.v1beta1.Capability\"\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*\xc0\x01\n\nCapability\x12\x1a\n\x16\x43\x41PABILITY_UNSPECIFIED\x10\x00\x12\x1b\n\x17\x43\x41PABILITY_CAPABILITIES\x10\x01\x12!\n\x1d\x43\x41PABILITY_REQUIRED_RESOURCES\x10\x02\x12\x1a\n\x16\x43\x41PABILITY_CREDENTIALS\x10\x03\x12\x19\n\x15\x43\x41PABILITY_CONDITIONS\x10\x04\x12\x1f\n\x1b\x43\x41PABILITY_REQUIRED_SCHEMAS\x10\x05*?\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,70 @@ _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['_CAPABILITY']._serialized_start=4093 + _globals['_CAPABILITY']._serialized_end=4285 + _globals['_READY']._serialized_start=4287 + _globals['_READY']._serialized_end=4350 + _globals['_SEVERITY']._serialized_start=4352 + _globals['_SEVERITY']._serialized_end=4451 + _globals['_TARGET']._serialized_start=4453 + _globals['_TARGET']._serialized_end=4539 + _globals['_STATUS']._serialized_start=4541 + _globals['_STATUS']._serialized_end=4668 _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=2086 + _globals['_REQUIREMENTS']._serialized_start=2089 + _globals['_REQUIREMENTS']._serialized_end=2655 + _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_start=2356 + _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_end=2459 + _globals['_REQUIREMENTS_RESOURCESENTRY']._serialized_start=2461 + _globals['_REQUIREMENTS_RESOURCESENTRY']._serialized_end=2559 + _globals['_REQUIREMENTS_SCHEMASENTRY']._serialized_start=2561 + _globals['_REQUIREMENTS_SCHEMASENTRY']._serialized_end=2655 + _globals['_SCHEMASELECTOR']._serialized_start=2657 + _globals['_SCHEMASELECTOR']._serialized_end=2708 + _globals['_SCHEMA']._serialized_start=2710 + _globals['_SCHEMA']._serialized_end=2783 + _globals['_RESOURCESELECTOR']._serialized_start=2786 + _globals['_RESOURCESELECTOR']._serialized_end=2977 + _globals['_MATCHLABELS']._serialized_start=2980 + _globals['_MATCHLABELS']._serialized_end=3113 + _globals['_MATCHLABELS_LABELSENTRY']._serialized_start=3068 + _globals['_MATCHLABELS_LABELSENTRY']._serialized_end=3113 + _globals['_RESPONSEMETA']._serialized_start=3115 + _globals['_RESPONSEMETA']._serialized_end=3195 + _globals['_STATE']._serialized_start=3198 + _globals['_STATE']._serialized_end=3431 + _globals['_STATE_RESOURCESENTRY']._serialized_start=3341 + _globals['_STATE_RESOURCESENTRY']._serialized_end=3431 + _globals['_RESOURCE']._serialized_start=3434 + _globals['_RESOURCE']._serialized_end=3692 + _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_start=3636 + _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_end=3692 + _globals['_RESULT']._serialized_start=3695 + _globals['_RESULT']._serialized_end=3884 + _globals['_CONDITION']._serialized_start=3887 + _globals['_CONDITION']._serialized_end=4090 + _globals['_FUNCTIONRUNNERSERVICE']._serialized_start=4671 + _globals['_FUNCTIONRUNNERSERVICE']._serialized_end=4816 # @@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..d0d29ca 100644 --- a/crossplane/function/proto/v1beta1/run_function_pb2.pyi +++ b/crossplane/function/proto/v1beta1/run_function_pb2.pyi @@ -11,6 +11,15 @@ from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor +class Capability(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + CAPABILITY_UNSPECIFIED: _ClassVar[Capability] + CAPABILITY_CAPABILITIES: _ClassVar[Capability] + CAPABILITY_REQUIRED_RESOURCES: _ClassVar[Capability] + CAPABILITY_CREDENTIALS: _ClassVar[Capability] + CAPABILITY_CONDITIONS: _ClassVar[Capability] + CAPABILITY_REQUIRED_SCHEMAS: _ClassVar[Capability] + class Ready(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () READY_UNSPECIFIED: _ClassVar[Ready] @@ -36,6 +45,12 @@ class Status(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): STATUS_CONDITION_UNKNOWN: _ClassVar[Status] STATUS_CONDITION_TRUE: _ClassVar[Status] STATUS_CONDITION_FALSE: _ClassVar[Status] +CAPABILITY_UNSPECIFIED: Capability +CAPABILITY_CAPABILITIES: Capability +CAPABILITY_REQUIRED_RESOURCES: Capability +CAPABILITY_CREDENTIALS: Capability +CAPABILITY_CONDITIONS: Capability +CAPABILITY_REQUIRED_SCHEMAS: Capability READY_UNSPECIFIED: Ready READY_TRUE: Ready READY_FALSE: Ready @@ -52,7 +67,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 +89,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 +104,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 +113,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",) @@ -136,13 +160,15 @@ class RunFunctionResponse(_message.Message): def __init__(self, meta: _Optional[_Union[ResponseMeta, _Mapping]] = ..., desired: _Optional[_Union[State, _Mapping]] = ..., results: _Optional[_Iterable[_Union[Result, _Mapping]]] = ..., context: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., requirements: _Optional[_Union[Requirements, _Mapping]] = ..., conditions: _Optional[_Iterable[_Union[Condition, _Mapping]]] = ..., output: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ...) -> None: ... class RequestMeta(_message.Message): - __slots__ = ("tag",) + __slots__ = ("tag", "capabilities") TAG_FIELD_NUMBER: _ClassVar[int] + CAPABILITIES_FIELD_NUMBER: _ClassVar[int] tag: str - def __init__(self, tag: _Optional[str] = ...) -> None: ... + capabilities: _containers.RepeatedScalarFieldContainer[Capability] + def __init__(self, tag: _Optional[str] = ..., capabilities: _Optional[_Iterable[_Union[Capability, 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 +183,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..e059f16 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,96 @@ def get_credentials(req: fnv1.RunFunctionRequest, name: str) -> Credentials: # If no recognized source type is set, return empty return empty + + +def advertises_capabilities(req: fnv1.RunFunctionRequest) -> bool: + """Check whether Crossplane advertises its capabilities. + + Args: + req: The RunFunctionRequest to check. + + Returns: + True if Crossplane advertises its capabilities. + + Crossplane v2.2 and later advertise their capabilities in the request + metadata. If this returns False, the calling Crossplane predates capability + advertisement and has_capability will always return False, even for features + the older Crossplane does support. + + if not request.advertises_capabilities(req): + # Pre-v2.2 Crossplane, capabilities are unknown. + ... + elif request.has_capability(req, fnv1.CAPABILITY_REQUIRED_SCHEMAS): + response.require_schema(rsp, "xr", xr_api_version, xr_kind) + """ + return fnv1.CAPABILITY_CAPABILITIES in req.meta.capabilities + + +def has_capability( + req: fnv1.RunFunctionRequest, + cap: fnv1.Capability.ValueType, +) -> bool: + """Check whether Crossplane advertises a particular capability. + + Args: + req: The RunFunctionRequest to check. + cap: The capability to check for, e.g. fnv1.CAPABILITY_REQUIRED_SCHEMAS. + + Returns: + True if the capability is present in the request metadata. + + Crossplane sends its capabilities in the request metadata. Functions can use + this to determine whether Crossplane will honor certain fields in their + response, or populate certain fields in their request. + + Use advertises_capabilities to check whether Crossplane advertises its + capabilities at all. If it doesn't, has_capability always returns False even + for features the older Crossplane does support. + + if request.has_capability(req, fnv1.CAPABILITY_REQUIRED_SCHEMAS): + response.require_schema(rsp, "xr", xr_api_version, xr_kind) + """ + return cap in req.meta.capabilities + + +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..f7fdf00 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -264,6 +264,149 @@ class TestCase: dataclasses.asdict(case.want), dataclasses.asdict(got), case.reason ) + def test_advertises_capabilities(self) -> None: + @dataclasses.dataclass + class TestCase: + reason: str + req: fnv1.RunFunctionRequest + want: bool + + cases = [ + TestCase( + reason="Should return False when no capabilities are advertised.", + req=fnv1.RunFunctionRequest(), + want=False, + ), + TestCase( + reason="Should return True when CAPABILITY_CAPABILITIES is present.", + req=fnv1.RunFunctionRequest( + meta=fnv1.RequestMeta( + capabilities=[ + fnv1.CAPABILITY_CAPABILITIES, + fnv1.CAPABILITY_REQUIRED_SCHEMAS, + ], + ), + ), + want=True, + ), + ] + + for case in cases: + got = request.advertises_capabilities(case.req) + self.assertEqual(case.want, got, case.reason) + + def test_has_capability(self) -> None: + @dataclasses.dataclass + class TestCase: + reason: str + req: fnv1.RunFunctionRequest + cap: fnv1.Capability.ValueType + want: bool + + cases = [ + TestCase( + reason="Should return False when no capabilities are advertised.", + req=fnv1.RunFunctionRequest(), + cap=fnv1.CAPABILITY_REQUIRED_SCHEMAS, + want=False, + ), + TestCase( + reason="Should return True when the capability is present.", + req=fnv1.RunFunctionRequest( + meta=fnv1.RequestMeta( + capabilities=[ + fnv1.CAPABILITY_CAPABILITIES, + fnv1.CAPABILITY_REQUIRED_SCHEMAS, + ], + ), + ), + cap=fnv1.CAPABILITY_REQUIRED_SCHEMAS, + want=True, + ), + TestCase( + reason="Should return False when a different capability is present.", + req=fnv1.RunFunctionRequest( + meta=fnv1.RequestMeta( + capabilities=[ + fnv1.CAPABILITY_CAPABILITIES, + fnv1.CAPABILITY_CREDENTIALS, + ], + ), + ), + cap=fnv1.CAPABILITY_REQUIRED_SCHEMAS, + want=False, + ), + ] + + for case in cases: + got = request.has_capability(case.req, case.cap) + self.assertEqual(case.want, 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()