diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4533d8f..c03d08e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -114,7 +114,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Download Sdist and Wheel from GitHub - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: dist path: "dist" diff --git a/.gitignore b/.gitignore index 9527b70..9c08e8b 100644 --- a/.gitignore +++ b/.gitignore @@ -161,3 +161,6 @@ cython_debug/ # We don't commit our docs - instead we generate them and upload to GitHub pages. docs + +# AIs +.claude/ diff --git a/crossplane/function/proto/v1/run_function.proto b/crossplane/function/proto/v1/run_function.proto index b66e970..557fd34 100644 --- a/crossplane/function/proto/v1/run_function.proto +++ b/crossplane/function/proto/v1/run_function.proto @@ -1,80 +1,98 @@ /* -Copyright 2022 The Crossplane Authors. + Copyright 2022 The Crossplane Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ syntax = "proto3"; -import "google/protobuf/struct.proto"; -import "google/protobuf/duration.proto"; +// This package defines the RPC for both composition and operation functions. +// Only composition functions are part of the 'apiextensions' API group. In +// retrospect this package should've been crossplane.proto.fn.v1, but it's too +// late to change it now. +//buf:lint:ignore PACKAGE_DIRECTORY_MATCH package apiextensions.fn.proto.v1; -option go_package = "github.com/crossplane/crossplane/apis/apiextensions/fn/proto/v1"; +import "google/protobuf/duration.proto"; +import "google/protobuf/struct.proto"; + +option go_package = "github.com/crossplane/crossplane/v2/proto/fn/v1"; -// A FunctionRunnerService is a Composition Function. +// A FunctionRunnerService is a function. service FunctionRunnerService { - // RunFunction runs the Composition Function. + // RunFunction runs the function. rpc RunFunction(RunFunctionRequest) returns (RunFunctionResponse) {} } -// A RunFunctionRequest requests that the Composition Function be run. +// A RunFunctionRequest requests that the function be run. message RunFunctionRequest { // Metadata pertaining to this request. RequestMeta meta = 1; - // The observed state prior to invocation of a Function pipeline. State passed - // to each Function is fresh as of the time the pipeline was invoked, not as - // of the time each Function was invoked. + // The observed state prior to invocation of a function pipeline. State passed + // to each function is fresh as of the time the pipeline was invoked, not as + // of the time each function was invoked. State observed = 2; - // Desired state according to a Function pipeline. The state passed to a - // particular Function may have been accumulated by previous Functions in the + // Desired state according to a function pipeline. The state passed to a + // particular function may have been accumulated by previous functions in the // pipeline. // // Note that the desired state must be a partial object with only the fields - // that this function (and its predecessors in the pipeline) wants to have - // set in the object. Copying a non-partial observed state to desired is most + // that this function (and its predecessors in the pipeline) wants to have set + // in the object. Copying a non-partial observed state to desired is most // likely not what you want to do. Leaving out fields that had been returned // as desired before will result in them being deleted from the objects in the // cluster. State desired = 3; - // Optional input specific to this Function invocation. A JSON representation - // of the 'input' block of the relevant entry in a Composition's pipeline. + // Optional input specific to this function invocation. A JSON representation + // of the 'input' block of the relevant entry in a function pipeline. optional google.protobuf.Struct input = 4; - // Optional context. Crossplane may pass arbitary contextual information to a - // Function. A Function may also return context in its RunFunctionResponse, - // and that context will be passed to subsequent Functions. Crossplane - // discards all context returned by the last Function in the pipeline. + // Optional context. Crossplane may pass arbitrary contextual information to a + // function. A function may also return context in its RunFunctionResponse, + // and that context will be passed to subsequent functions. Crossplane + // discards all context returned by the last function in the pipeline. optional google.protobuf.Struct context = 5; - // Optional extra resources that the Function required. - // Note that extra resources is a map to Resources, plural. - // The map key corresponds to the key in a RunFunctionResponse's - // extra_resources field. If a Function requested extra resources that - // did not exist, Crossplane sets the map key to an empty Resources message to - // indicate that it attempted to satisfy the request. - map extra_resources = 6; + // Optional resources that the function specified in its requirements. Note + // that resources is a map to Resources, plural. The map key corresponds to + // the key in a RunFunctionResponse's requirements.extra_resources field. If a + // function requested extra resources that did not exist, Crossplane sets + // the map key to an empty Resources message to indicate that it attempted to + // satisfy the request. This field is only populated when the function uses + // extra_resources in its requirements. + // + // Deprecated: Use required_resources instead. + map extra_resources = 6 [deprecated = true]; - // Optional credentials that this Function may use to communicate with an + // Optional credentials that this function may use to communicate with an // external system. - map credentials = 7; + map credentials = 7; + + // Optional resources that the function specified in its requirements. Note + // that resources is a map to Resources, plural. The map key corresponds to + // the key in a RunFunctionResponse's requirements.resources field. If a + // function requested required resources that did not exist, Crossplane sets + // the map key to an empty Resources message to indicate that it attempted to + // satisfy the request. This field is only populated when the function uses + // resources in its requirements. + map required_resources = 8; } -// Credentials that a Function may use to communicate with an external system. +// Credentials that a function may use to communicate with an external system. message Credentials { // Source of the credentials. oneof source { @@ -93,52 +111,64 @@ message Resources { repeated Resource items = 1; } -// A RunFunctionResponse contains the result of a Composition Function run. +// A RunFunctionResponse contains the result of a function run. message RunFunctionResponse { // Metadata pertaining to this response. ResponseMeta meta = 1; - // Desired state according to a Function pipeline. Functions may add desired + // Desired state according to a function pipeline. functions may add desired // state, and may mutate or delete any part of the desired state they are - // concerned with. A Function must pass through any part of the desired state + // concerned with. A function must pass through any part of the desired state // that it is not concerned with. // - // // Note that the desired state must be a partial object with only the fields - // that this function (and its predecessors in the pipeline) wants to have - // set in the object. Copying a non-partial observed state to desired is most + // that this function (and its predecessors in the pipeline) wants to have set + // in the object. Copying a non-partial observed state to desired is most // likely not what you want to do. Leaving out fields that had been returned // as desired before will result in them being deleted from the objects in the // cluster. State desired = 2; - // Results of the Function run. Results are used for observability purposes. + // Results of the function run. Results are used for observability purposes. repeated Result results = 3; - // Optional context to be passed to the next Function in the pipeline as part + // Optional context to be passed to the next function in the pipeline as part // of the RunFunctionRequest. Dropped on the last function in the pipeline. optional google.protobuf.Struct context = 4; - // Requirements that must be satisfied for this Function to run successfully. + // Requirements that must be satisfied for this function to run successfully. Requirements requirements = 5; - // Status conditions to be applied to the composite resource. Conditions may also - // optionally be applied to the composite resource's associated claim. + // Status conditions to be applied to the XR. Conditions may also optionally + // be applied to the XR's associated claim. + // + // Conditions are only used for composition. They're ignored by Operations. repeated Condition conditions = 6; + + // Optional output specific to this function invocation. + // + // Only Operations use function output. XRs will discard any function output. + optional google.protobuf.Struct output = 7; } // RequestMeta contains metadata pertaining to a RunFunctionRequest. message RequestMeta { - // An opaque string identifying the content of the request. Two identical - // requests should have the same tag. + // An opaque string identifying a request. Requests with identical tags will + // be otherwise identical. string tag = 1; } -// Requirements that must be satisfied for a Function to run successfully. +// Requirements that must be satisfied for a function to run successfully. message Requirements { - // Extra resources that this Function requires. - // The map key uniquely identifies the group of resources. - map extra_resources = 1; + // Resources that this function requires. The map key uniquely identifies the + // group of resources. + // + // Deprecated: Use resources instead. + map extra_resources = 1 [deprecated = true]; + + // Resources that this function requires. The map key uniquely identifies the + // group of resources. + map resources = 2; } // ResourceSelector selects a group of resources, either by name or by label. @@ -157,6 +187,11 @@ message ResourceSelector { // Match all resources with these labels. MatchLabels match_labels = 4; } + + // Match resources in this namespace. Omit namespace to match cluster scoped + // resources, or to match namespaced resources by labels across all + // namespaces. + optional string namespace = 5; } // MatchLabels defines a set of labels to match resources against. @@ -170,77 +205,86 @@ message ResponseMeta { // meta.tag of the corresponding RunFunctionRequest. string tag = 1; - // Time-to-live of this response. Deterministic Functions with no side-effects - // (e.g. simple templating Functions) may specify a TTL. Crossplane may choose - // to cache responses until the TTL expires. + // Time-to-live of this response. Crossplane will call the function again when + // the TTL expires. Crossplane may cache the response to avoid calling the + // function again until the TTL expires. optional google.protobuf.Duration ttl = 2; } -// State of the composite resource (XR) and any composed resources. +// State of the XR (XR) and any resources. message State { - // The state of the composite resource (XR). + // The state of the XR (XR). Resource composite = 1; - // The state of any composed resources. + // The state of any other resources. In composition functions these are the + // composed resources. In operation functions they're arbitrary resources that + // the operation wants to create or update. map resources = 2; } -// A Resource represents the state of a composite or composed resource. +// A Resource represents the state of a Kubernetes resource. message Resource { // The JSON representation of the resource. // // * Crossplane will set this field in a RunFunctionRequest to the entire - // observed state of a resource - including its metadata, spec, and status. + // observed state of a resource - including its metadata, spec, and status. // - // * A Function should set this field in a RunFunctionRequest to communicate - // the desired state of a composite or composed resource. + // * A function should set this field in a RunFunctionRequest to communicate + // the desired state of the resource. // - // * A Function may only specify the desired status of a composite resource - - // not its metadata or spec. A Function should not return desired metadata - // or spec for a composite resource. This will be ignored. + // * A function may only specify the desired status of a XR - not its metadata + // or spec. A function should not return desired metadata or spec for a XR. + // This will be ignored. // - // * A Function may not specify the desired status of a composed resource - - // only its metadata and spec. A Function should not return desired status - // for a composed resource. This will be ignored. + // * A function may not specify the desired status of any other resource - + // e.g. composed resources. It may only specify their metadata and spec. + // Status will be ignored. google.protobuf.Struct resource = 1; // The resource's connection details. - // + // // * Crossplane will set this field in a RunFunctionRequest to communicate the - // the observed connection details of a composite or composed resource. + // the observed connection details of a composite or composed resource. // - // * A Function should set this field in a RunFunctionResponse to indicate the - // desired connection details of the composite resource. + // * A function should set this field in a RunFunctionResponse to indicate the + // desired connection details of the XR. // - // * A Function should not set this field in a RunFunctionResponse to indicate - // the desired connection details of a composed resource. This will be - // ignored. + // * A function should not set this field in a RunFunctionResponse to indicate + // the desired connection details of a composed resource. This will be + // ignored. + // + // Connection details are only used for composition. They're ignored by + // Operations. map connection_details = 2; // Ready indicates whether the resource should be considered ready. - // + // // * Crossplane will never set this field in a RunFunctionRequest. // - // * A Function should set this field to READY_TRUE in a RunFunctionResponse - // to indicate that a desired composed resource is ready. + // * A function should set this field to READY_TRUE in a RunFunctionResponse + // to indicate that a desired resource is ready. // - // * A Function should not set this field in a RunFunctionResponse to indicate - // that the desired composite resource is ready. This will be ignored. + // * 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 is only used for composition. It's ignored by Operations. Ready ready = 3; } -// Ready indicates whether a composed resource should be considered ready. +// Ready indicates whether a resource should be considered ready. enum Ready { READY_UNSPECIFIED = 0; - // True means the composed resource has been observed to be ready. + // True means the resource has been observed to be ready. READY_TRUE = 1; - // False means the composed resource has not been observed to be ready. + // False means the resource has not been observed to be ready. READY_FALSE = 2; } -// A Result of running a Function. +// A Result of running a function. message Result { // Severity of this result. Severity severity = 1; @@ -256,42 +300,42 @@ message Result { optional Target target = 4; } -// Severity of Function results. +// Severity of function results. enum Severity { SEVERITY_UNSPECIFIED = 0; - // Fatal results are fatal; subsequent Composition Functions may run, but - // the Composition Function pipeline run will be considered a failure and - // the first fatal result will be returned as an error. + // Fatal results are fatal; subsequent functions may run, but the function + // pipeline run will be considered a failure and the first fatal result will + // be returned as an error. SEVERITY_FATAL = 1; - // Warning results are non-fatal; the entire Composition will run to - // completion but warning events and debug logs associated with the - // composite resource will be emitted. + // Warning results are non-fatal; the entire pipeline will run to completion + // but warning events and debug logs associated with the XR or Operation will + // be emitted. SEVERITY_WARNING = 2; - // Normal results are emitted as normal events and debug logs associated - // with the composite resource. + // Normal results are emitted as normal events and debug logs associated with + // the XR or operation. SEVERITY_NORMAL = 3; } -// Target of Function results and conditions. +// Target of function results and conditions. enum Target { - // If the target is unspecified, the result targets the composite resource. + // If the target is unspecified, the result targets the XR. TARGET_UNSPECIFIED = 0; - // Target the composite resource. Results that target the composite resource - // should include detailed, advanced information. + // Target the XR. Results that target the XR should include detailed, advanced + // information. TARGET_COMPOSITE = 1; - // Target the composite and the claim. Results that target the composite and - // the claim should include only end-user friendly information. + // Target the XR and the claim. Results that target the XR and the claim + // should include only end-user friendly information. TARGET_COMPOSITE_AND_CLAIM = 2; } -// Status condition to be applied to the composite resource. Condition may also -// optionally be applied to the composite resource's associated claim. For -// detailed information on proper usage of status conditions, please see +// Status condition to be applied to the XR. Condition may also optionally be +// applied to the XR's associated claim. For detailed information on proper +// usage of status conditions, please see // https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties. message Condition { // Type of condition in PascalCase. @@ -323,4 +367,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 8a3947d..17eb5d0 100644 --- a/crossplane/function/proto/v1/run_function_pb2.py +++ b/crossplane/function/proto/v1/run_function_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: crossplane/function/proto/v1/run_function.proto -# Protobuf Python Version: 5.29.0 +# Protobuf Python Version: 6.31.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -11,8 +11,8 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, - 5, - 29, + 6, + 31, 0, '', 'crossplane/function/proto/v1/run_function.proto' @@ -22,82 +22,94 @@ _sym_db = _symbol_database.Default() -from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +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\x1cgoogle/protobuf/struct.proto\x1a\x1egoogle/protobuf/duration.proto\"\x8d\x05\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\x12Z\n\x0f\x65xtra_resources\x18\x06 \x03(\x0b\x32\x41.apiextensions.fn.proto.v1.RunFunctionRequest.ExtraResourcesEntry\x12S\n\x0b\x63redentials\x18\x07 \x03(\x0b\x32>.apiextensions.fn.proto.v1.RunFunctionRequest.CredentialsEntry\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\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\"\xe7\x02\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.ConditionB\n\n\x08_context\"\x1a\n\x0bRequestMeta\x12\x0b\n\x03tag\x18\x01 \x01(\t\"\xc8\x01\n\x0cRequirements\x12T\n\x0f\x65xtra_resources\x18\x01 \x03(\x0b\x32;.apiextensions.fn.proto.v1.Requirements.ExtraResourcesEntry\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\"\x94\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\x42\x07\n\x05match\"\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\x41Z?github.com/crossplane/crossplane/apis/apiextensions/fn/proto/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\"\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') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'crossplane.function.proto.v1.run_function_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'Z?github.com/crossplane/crossplane/apis/apiextensions/fn/proto/v1' + _globals['DESCRIPTOR']._serialized_options = b'Z/github.com/crossplane/crossplane/v2/proto/fn/v1' _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._loaded_options = None _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._serialized_options = b'8\001' _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._loaded_options = None _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_options = b'8\001' + _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._loaded_options = None + _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._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 _globals['_CREDENTIALDATA_DATAENTRY']._serialized_options = b'8\001' _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._loaded_options = None _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_options = b'8\001' + _globals['_REQUIREMENTS_RESOURCESENTRY']._loaded_options = None + _globals['_REQUIREMENTS_RESOURCESENTRY']._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 _globals['_MATCHLABELS_LABELSENTRY']._serialized_options = b'8\001' _globals['_STATE_RESOURCESENTRY']._loaded_options = None _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=2894 - _globals['_READY']._serialized_end=2957 - _globals['_SEVERITY']._serialized_start=2959 - _globals['_SEVERITY']._serialized_end=3058 - _globals['_TARGET']._serialized_start=3060 - _globals['_TARGET']._serialized_end=3146 - _globals['_STATUS']._serialized_start=3148 - _globals['_STATUS']._serialized_end=3275 + _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['_RUNFUNCTIONREQUEST']._serialized_start=141 - _globals['_RUNFUNCTIONREQUEST']._serialized_end=794 - _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._serialized_start=589 - _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._serialized_end=680 - _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_start=682 - _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_end=772 - _globals['_CREDENTIALS']._serialized_start=796 - _globals['_CREDENTIALS']._serialized_end=889 - _globals['_CREDENTIALDATA']._serialized_start=892 - _globals['_CREDENTIALDATA']._serialized_end=1020 - _globals['_CREDENTIALDATA_DATAENTRY']._serialized_start=977 - _globals['_CREDENTIALDATA_DATAENTRY']._serialized_end=1020 - _globals['_RESOURCES']._serialized_start=1022 - _globals['_RESOURCES']._serialized_end=1085 - _globals['_RUNFUNCTIONRESPONSE']._serialized_start=1088 - _globals['_RUNFUNCTIONRESPONSE']._serialized_end=1447 - _globals['_REQUESTMETA']._serialized_start=1449 - _globals['_REQUESTMETA']._serialized_end=1475 - _globals['_REQUIREMENTS']._serialized_start=1478 - _globals['_REQUIREMENTS']._serialized_end=1678 - _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_start=1580 - _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_end=1678 - _globals['_RESOURCESELECTOR']._serialized_start=1681 - _globals['_RESOURCESELECTOR']._serialized_end=1829 - _globals['_MATCHLABELS']._serialized_start=1832 - _globals['_MATCHLABELS']._serialized_end=1960 - _globals['_MATCHLABELS_LABELSENTRY']._serialized_start=1915 - _globals['_MATCHLABELS_LABELSENTRY']._serialized_end=1960 - _globals['_RESPONSEMETA']._serialized_start=1962 - _globals['_RESPONSEMETA']._serialized_end=2042 - _globals['_STATE']._serialized_start=2045 - _globals['_STATE']._serialized_end=2263 - _globals['_STATE_RESOURCESENTRY']._serialized_start=2178 - _globals['_STATE_RESOURCESENTRY']._serialized_end=2263 - _globals['_RESOURCE']._serialized_start=2266 - _globals['_RESOURCE']._serialized_end=2514 - _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_start=2458 - _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_end=2514 - _globals['_RESULT']._serialized_start=2517 - _globals['_RESULT']._serialized_end=2696 - _globals['_CONDITION']._serialized_start=2699 - _globals['_CONDITION']._serialized_end=2892 - _globals['_FUNCTIONRUNNERSERVICE']._serialized_start=3278 - _globals['_FUNCTIONRUNNERSERVICE']._serialized_end=3413 + _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 # @@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 5ba6180..ed96678 100644 --- a/crossplane/function/proto/v1/run_function_pb2.pyi +++ b/crossplane/function/proto/v1/run_function_pb2.pyi @@ -1,10 +1,11 @@ -from google.protobuf import struct_pb2 as _struct_pb2 from google.protobuf import duration_pb2 as _duration_pb2 +from google.protobuf import struct_pb2 as _struct_pb2 from google.protobuf.internal import containers as _containers from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message -from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -49,7 +50,7 @@ STATUS_CONDITION_TRUE: Status STATUS_CONDITION_FALSE: Status class RunFunctionRequest(_message.Message): - __slots__ = ("meta", "observed", "desired", "input", "context", "extra_resources", "credentials") + __slots__ = ("meta", "observed", "desired", "input", "context", "extra_resources", "credentials", "required_resources") class ExtraResourcesEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -64,6 +65,13 @@ class RunFunctionRequest(_message.Message): key: str value: Credentials def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[Credentials, _Mapping]] = ...) -> None: ... + class RequiredResourcesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: Resources + def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[Resources, _Mapping]] = ...) -> None: ... META_FIELD_NUMBER: _ClassVar[int] OBSERVED_FIELD_NUMBER: _ClassVar[int] DESIRED_FIELD_NUMBER: _ClassVar[int] @@ -71,6 +79,7 @@ class RunFunctionRequest(_message.Message): CONTEXT_FIELD_NUMBER: _ClassVar[int] EXTRA_RESOURCES_FIELD_NUMBER: _ClassVar[int] CREDENTIALS_FIELD_NUMBER: _ClassVar[int] + REQUIRED_RESOURCES_FIELD_NUMBER: _ClassVar[int] meta: RequestMeta observed: State desired: State @@ -78,7 +87,8 @@ class RunFunctionRequest(_message.Message): context: _struct_pb2.Struct extra_resources: _containers.MessageMap[str, Resources] credentials: _containers.MessageMap[str, Credentials] - 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]] = ...) -> None: ... + 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: ... class Credentials(_message.Message): __slots__ = ("credential_data",) @@ -106,20 +116,22 @@ class Resources(_message.Message): def __init__(self, items: _Optional[_Iterable[_Union[Resource, _Mapping]]] = ...) -> None: ... class RunFunctionResponse(_message.Message): - __slots__ = ("meta", "desired", "results", "context", "requirements", "conditions") + __slots__ = ("meta", "desired", "results", "context", "requirements", "conditions", "output") META_FIELD_NUMBER: _ClassVar[int] DESIRED_FIELD_NUMBER: _ClassVar[int] RESULTS_FIELD_NUMBER: _ClassVar[int] CONTEXT_FIELD_NUMBER: _ClassVar[int] REQUIREMENTS_FIELD_NUMBER: _ClassVar[int] CONDITIONS_FIELD_NUMBER: _ClassVar[int] + OUTPUT_FIELD_NUMBER: _ClassVar[int] meta: ResponseMeta desired: State results: _containers.RepeatedCompositeFieldContainer[Result] context: _struct_pb2.Struct requirements: Requirements conditions: _containers.RepeatedCompositeFieldContainer[Condition] - 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]]] = ...) -> None: ... + output: _struct_pb2.Struct + 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",) @@ -128,7 +140,7 @@ class RequestMeta(_message.Message): def __init__(self, tag: _Optional[str] = ...) -> None: ... class Requirements(_message.Message): - __slots__ = ("extra_resources",) + __slots__ = ("extra_resources", "resources") class ExtraResourcesEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -136,21 +148,32 @@ class Requirements(_message.Message): key: str value: ResourceSelector def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[ResourceSelector, _Mapping]] = ...) -> None: ... + class ResourcesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: ResourceSelector + def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[ResourceSelector, _Mapping]] = ...) -> None: ... EXTRA_RESOURCES_FIELD_NUMBER: _ClassVar[int] + RESOURCES_FIELD_NUMBER: _ClassVar[int] extra_resources: _containers.MessageMap[str, ResourceSelector] - def __init__(self, extra_resources: _Optional[_Mapping[str, ResourceSelector]] = ...) -> None: ... + resources: _containers.MessageMap[str, ResourceSelector] + def __init__(self, extra_resources: _Optional[_Mapping[str, ResourceSelector]] = ..., resources: _Optional[_Mapping[str, ResourceSelector]] = ...) -> None: ... class ResourceSelector(_message.Message): - __slots__ = ("api_version", "kind", "match_name", "match_labels") + __slots__ = ("api_version", "kind", "match_name", "match_labels", "namespace") API_VERSION_FIELD_NUMBER: _ClassVar[int] KIND_FIELD_NUMBER: _ClassVar[int] MATCH_NAME_FIELD_NUMBER: _ClassVar[int] MATCH_LABELS_FIELD_NUMBER: _ClassVar[int] + NAMESPACE_FIELD_NUMBER: _ClassVar[int] api_version: str kind: str match_name: str match_labels: MatchLabels - def __init__(self, api_version: _Optional[str] = ..., kind: _Optional[str] = ..., match_name: _Optional[str] = ..., match_labels: _Optional[_Union[MatchLabels, _Mapping]] = ...) -> None: ... + namespace: str + def __init__(self, api_version: _Optional[str] = ..., kind: _Optional[str] = ..., match_name: _Optional[str] = ..., match_labels: _Optional[_Union[MatchLabels, _Mapping]] = ..., namespace: _Optional[str] = ...) -> None: ... class MatchLabels(_message.Message): __slots__ = ("labels",) @@ -171,7 +194,7 @@ class ResponseMeta(_message.Message): TTL_FIELD_NUMBER: _ClassVar[int] tag: str ttl: _duration_pb2.Duration - def __init__(self, tag: _Optional[str] = ..., ttl: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ...) -> None: ... + def __init__(self, tag: _Optional[str] = ..., ttl: _Optional[_Union[datetime.timedelta, _duration_pb2.Duration, _Mapping]] = ...) -> None: ... class State(_message.Message): __slots__ = ("composite", "resources") diff --git a/crossplane/function/proto/v1/run_function_pb2_grpc.py b/crossplane/function/proto/v1/run_function_pb2_grpc.py index bc3a415..ecc0ec8 100644 --- a/crossplane/function/proto/v1/run_function_pb2_grpc.py +++ b/crossplane/function/proto/v1/run_function_pb2_grpc.py @@ -5,7 +5,7 @@ from crossplane.function.proto.v1 import run_function_pb2 as crossplane_dot_function_dot_proto_dot_v1_dot_run__function__pb2 -GRPC_GENERATED_VERSION = '1.71.0' +GRPC_GENERATED_VERSION = '1.73.1' GRPC_VERSION = grpc.__version__ _version_not_supported = False @@ -26,7 +26,7 @@ class FunctionRunnerServiceStub(object): - """A FunctionRunnerService is a Composition Function. + """A FunctionRunnerService is a function. """ def __init__(self, channel): @@ -43,11 +43,11 @@ def __init__(self, channel): class FunctionRunnerServiceServicer(object): - """A FunctionRunnerService is a Composition Function. + """A FunctionRunnerService is a function. """ def RunFunction(self, request, context): - """RunFunction runs the Composition Function. + """RunFunction runs the function. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -70,7 +70,7 @@ def add_FunctionRunnerServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. class FunctionRunnerService(object): - """A FunctionRunnerService is a Composition Function. + """A FunctionRunnerService is a function. """ @staticmethod diff --git a/crossplane/function/proto/v1beta1/run_function.proto b/crossplane/function/proto/v1beta1/run_function.proto index 52ab926..f0ab5b6 100644 --- a/crossplane/function/proto/v1beta1/run_function.proto +++ b/crossplane/function/proto/v1beta1/run_function.proto @@ -1,82 +1,100 @@ /* -Copyright 2022 The Crossplane Authors. + Copyright 2022 The Crossplane Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ -syntax = "proto3"; +// Generated from proto/fn/v1/run_function.proto by ./hack/duplicate_proto_type.sh. DO NOT EDIT. -import "google/protobuf/struct.proto"; -import "google/protobuf/duration.proto"; +syntax = "proto3"; -// Generated from apiextensions/fn/proto/v1/run_function.proto by ../hack/duplicate_proto_type.sh. DO NOT EDIT. +// This package defines the RPC for both composition and operation functions. +// Only composition functions are part of the 'apiextensions' API group. In +// retrospect this package should've been crossplane.proto.fn.v1, but it's too +// late to change it now. +//buf:lint:ignore PACKAGE_DIRECTORY_MATCH package apiextensions.fn.proto.v1beta1; -option go_package = "github.com/crossplane/crossplane/apis/apiextensions/fn/proto/v1beta1"; +import "google/protobuf/duration.proto"; +import "google/protobuf/struct.proto"; + +option go_package = "github.com/crossplane/crossplane/v2/proto/fn/v1beta1"; -// A FunctionRunnerService is a Composition Function. +// A FunctionRunnerService is a function. service FunctionRunnerService { - // RunFunction runs the Composition Function. + // RunFunction runs the function. rpc RunFunction(RunFunctionRequest) returns (RunFunctionResponse) {} } -// A RunFunctionRequest requests that the Composition Function be run. +// A RunFunctionRequest requests that the function be run. message RunFunctionRequest { // Metadata pertaining to this request. RequestMeta meta = 1; - // The observed state prior to invocation of a Function pipeline. State passed - // to each Function is fresh as of the time the pipeline was invoked, not as - // of the time each Function was invoked. + // The observed state prior to invocation of a function pipeline. State passed + // to each function is fresh as of the time the pipeline was invoked, not as + // of the time each function was invoked. State observed = 2; - // Desired state according to a Function pipeline. The state passed to a - // particular Function may have been accumulated by previous Functions in the + // Desired state according to a function pipeline. The state passed to a + // particular function may have been accumulated by previous functions in the // pipeline. // // Note that the desired state must be a partial object with only the fields - // that this function (and its predecessors in the pipeline) wants to have - // set in the object. Copying a non-partial observed state to desired is most + // that this function (and its predecessors in the pipeline) wants to have set + // in the object. Copying a non-partial observed state to desired is most // likely not what you want to do. Leaving out fields that had been returned // as desired before will result in them being deleted from the objects in the // cluster. State desired = 3; - // Optional input specific to this Function invocation. A JSON representation - // of the 'input' block of the relevant entry in a Composition's pipeline. + // Optional input specific to this function invocation. A JSON representation + // of the 'input' block of the relevant entry in a function pipeline. optional google.protobuf.Struct input = 4; - // Optional context. Crossplane may pass arbitary contextual information to a - // Function. A Function may also return context in its RunFunctionResponse, - // and that context will be passed to subsequent Functions. Crossplane - // discards all context returned by the last Function in the pipeline. + // Optional context. Crossplane may pass arbitrary contextual information to a + // function. A function may also return context in its RunFunctionResponse, + // and that context will be passed to subsequent functions. Crossplane + // discards all context returned by the last function in the pipeline. optional google.protobuf.Struct context = 5; - // Optional extra resources that the Function required. - // Note that extra resources is a map to Resources, plural. - // The map key corresponds to the key in a RunFunctionResponse's - // extra_resources field. If a Function requested extra resources that - // did not exist, Crossplane sets the map key to an empty Resources message to - // indicate that it attempted to satisfy the request. - map extra_resources = 6; + // Optional resources that the function specified in its requirements. Note + // that resources is a map to Resources, plural. The map key corresponds to + // the key in a RunFunctionResponse's requirements.extra_resources field. If a + // function requested extra resources that did not exist, Crossplane sets + // the map key to an empty Resources message to indicate that it attempted to + // satisfy the request. This field is only populated when the function uses + // extra_resources in its requirements. + // + // Deprecated: Use required_resources instead. + map extra_resources = 6 [deprecated = true]; - // Optional credentials that this Function may use to communicate with an + // Optional credentials that this function may use to communicate with an // external system. - map credentials = 7; + map credentials = 7; + + // Optional resources that the function specified in its requirements. Note + // that resources is a map to Resources, plural. The map key corresponds to + // the key in a RunFunctionResponse's requirements.resources field. If a + // function requested required resources that did not exist, Crossplane sets + // the map key to an empty Resources message to indicate that it attempted to + // satisfy the request. This field is only populated when the function uses + // resources in its requirements. + map required_resources = 8; } -// Credentials that a Function may use to communicate with an external system. +// Credentials that a function may use to communicate with an external system. message Credentials { // Source of the credentials. oneof source { @@ -95,52 +113,64 @@ message Resources { repeated Resource items = 1; } -// A RunFunctionResponse contains the result of a Composition Function run. +// A RunFunctionResponse contains the result of a function run. message RunFunctionResponse { // Metadata pertaining to this response. ResponseMeta meta = 1; - // Desired state according to a Function pipeline. Functions may add desired + // Desired state according to a function pipeline. functions may add desired // state, and may mutate or delete any part of the desired state they are - // concerned with. A Function must pass through any part of the desired state + // concerned with. A function must pass through any part of the desired state // that it is not concerned with. // - // // Note that the desired state must be a partial object with only the fields - // that this function (and its predecessors in the pipeline) wants to have - // set in the object. Copying a non-partial observed state to desired is most + // that this function (and its predecessors in the pipeline) wants to have set + // in the object. Copying a non-partial observed state to desired is most // likely not what you want to do. Leaving out fields that had been returned // as desired before will result in them being deleted from the objects in the // cluster. State desired = 2; - // Results of the Function run. Results are used for observability purposes. + // Results of the function run. Results are used for observability purposes. repeated Result results = 3; - // Optional context to be passed to the next Function in the pipeline as part + // Optional context to be passed to the next function in the pipeline as part // of the RunFunctionRequest. Dropped on the last function in the pipeline. optional google.protobuf.Struct context = 4; - // Requirements that must be satisfied for this Function to run successfully. + // Requirements that must be satisfied for this function to run successfully. Requirements requirements = 5; - // Status conditions to be applied to the composite resource. Conditions may also - // optionally be applied to the composite resource's associated claim. + // Status conditions to be applied to the XR. Conditions may also optionally + // be applied to the XR's associated claim. + // + // Conditions are only used for composition. They're ignored by Operations. repeated Condition conditions = 6; + + // Optional output specific to this function invocation. + // + // Only Operations use function output. XRs will discard any function output. + optional google.protobuf.Struct output = 7; } // RequestMeta contains metadata pertaining to a RunFunctionRequest. message RequestMeta { - // An opaque string identifying the content of the request. Two identical - // requests should have the same tag. + // An opaque string identifying a request. Requests with identical tags will + // be otherwise identical. string tag = 1; } -// Requirements that must be satisfied for a Function to run successfully. +// Requirements that must be satisfied for a function to run successfully. message Requirements { - // Extra resources that this Function requires. - // The map key uniquely identifies the group of resources. - map extra_resources = 1; + // Resources that this function requires. The map key uniquely identifies the + // group of resources. + // + // Deprecated: Use resources instead. + map extra_resources = 1 [deprecated = true]; + + // Resources that this function requires. The map key uniquely identifies the + // group of resources. + map resources = 2; } // ResourceSelector selects a group of resources, either by name or by label. @@ -159,6 +189,11 @@ message ResourceSelector { // Match all resources with these labels. MatchLabels match_labels = 4; } + + // Match resources in this namespace. Omit namespace to match cluster scoped + // resources, or to match namespaced resources by labels across all + // namespaces. + optional string namespace = 5; } // MatchLabels defines a set of labels to match resources against. @@ -172,77 +207,86 @@ message ResponseMeta { // meta.tag of the corresponding RunFunctionRequest. string tag = 1; - // Time-to-live of this response. Deterministic Functions with no side-effects - // (e.g. simple templating Functions) may specify a TTL. Crossplane may choose - // to cache responses until the TTL expires. + // Time-to-live of this response. Crossplane will call the function again when + // the TTL expires. Crossplane may cache the response to avoid calling the + // function again until the TTL expires. optional google.protobuf.Duration ttl = 2; } -// State of the composite resource (XR) and any composed resources. +// State of the XR (XR) and any resources. message State { - // The state of the composite resource (XR). + // The state of the XR (XR). Resource composite = 1; - // The state of any composed resources. + // The state of any other resources. In composition functions these are the + // composed resources. In operation functions they're arbitrary resources that + // the operation wants to create or update. map resources = 2; } -// A Resource represents the state of a composite or composed resource. +// A Resource represents the state of a Kubernetes resource. message Resource { // The JSON representation of the resource. // // * Crossplane will set this field in a RunFunctionRequest to the entire - // observed state of a resource - including its metadata, spec, and status. + // observed state of a resource - including its metadata, spec, and status. // - // * A Function should set this field in a RunFunctionRequest to communicate - // the desired state of a composite or composed resource. + // * A function should set this field in a RunFunctionRequest to communicate + // the desired state of the resource. // - // * A Function may only specify the desired status of a composite resource - - // not its metadata or spec. A Function should not return desired metadata - // or spec for a composite resource. This will be ignored. + // * A function may only specify the desired status of a XR - not its metadata + // or spec. A function should not return desired metadata or spec for a XR. + // This will be ignored. // - // * A Function may not specify the desired status of a composed resource - - // only its metadata and spec. A Function should not return desired status - // for a composed resource. This will be ignored. + // * A function may not specify the desired status of any other resource - + // e.g. composed resources. It may only specify their metadata and spec. + // Status will be ignored. google.protobuf.Struct resource = 1; // The resource's connection details. - // + // // * Crossplane will set this field in a RunFunctionRequest to communicate the - // the observed connection details of a composite or composed resource. + // the observed connection details of a composite or composed resource. + // + // * A function should set this field in a RunFunctionResponse to indicate the + // desired connection details of the XR. // - // * A Function should set this field in a RunFunctionResponse to indicate the - // desired connection details of the composite resource. + // * A function should not set this field in a RunFunctionResponse to indicate + // the desired connection details of a composed resource. This will be + // ignored. // - // * A Function should not set this field in a RunFunctionResponse to indicate - // the desired connection details of a composed resource. This will be - // ignored. + // Connection details are only used for composition. They're ignored by + // Operations. map connection_details = 2; // Ready indicates whether the resource should be considered ready. - // + // // * Crossplane will never set this field in a RunFunctionRequest. // - // * A Function should set this field to READY_TRUE in a RunFunctionResponse - // to indicate that a desired composed resource is ready. + // * A function should set this field to READY_TRUE in a RunFunctionResponse + // to indicate that a desired resource is ready. + // + // * 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. // - // * A Function should not set this field in a RunFunctionResponse to indicate - // that the desired composite resource is ready. This will be ignored. + // Ready is only used for composition. It's ignored by Operations. Ready ready = 3; } -// Ready indicates whether a composed resource should be considered ready. +// Ready indicates whether a resource should be considered ready. enum Ready { READY_UNSPECIFIED = 0; - // True means the composed resource has been observed to be ready. + // True means the resource has been observed to be ready. READY_TRUE = 1; - // False means the composed resource has not been observed to be ready. + // False means the resource has not been observed to be ready. READY_FALSE = 2; } -// A Result of running a Function. +// A Result of running a function. message Result { // Severity of this result. Severity severity = 1; @@ -258,42 +302,42 @@ message Result { optional Target target = 4; } -// Severity of Function results. +// Severity of function results. enum Severity { SEVERITY_UNSPECIFIED = 0; - // Fatal results are fatal; subsequent Composition Functions may run, but - // the Composition Function pipeline run will be considered a failure and - // the first fatal result will be returned as an error. + // Fatal results are fatal; subsequent functions may run, but the function + // pipeline run will be considered a failure and the first fatal result will + // be returned as an error. SEVERITY_FATAL = 1; - // Warning results are non-fatal; the entire Composition will run to - // completion but warning events and debug logs associated with the - // composite resource will be emitted. + // Warning results are non-fatal; the entire pipeline will run to completion + // but warning events and debug logs associated with the XR or Operation will + // be emitted. SEVERITY_WARNING = 2; - // Normal results are emitted as normal events and debug logs associated - // with the composite resource. + // Normal results are emitted as normal events and debug logs associated with + // the XR or operation. SEVERITY_NORMAL = 3; } -// Target of Function results and conditions. +// Target of function results and conditions. enum Target { - // If the target is unspecified, the result targets the composite resource. + // If the target is unspecified, the result targets the XR. TARGET_UNSPECIFIED = 0; - // Target the composite resource. Results that target the composite resource - // should include detailed, advanced information. + // Target the XR. Results that target the XR should include detailed, advanced + // information. TARGET_COMPOSITE = 1; - // Target the composite and the claim. Results that target the composite and - // the claim should include only end-user friendly information. + // Target the XR and the claim. Results that target the XR and the claim + // should include only end-user friendly information. TARGET_COMPOSITE_AND_CLAIM = 2; } -// Status condition to be applied to the composite resource. Condition may also -// optionally be applied to the composite resource's associated claim. For -// detailed information on proper usage of status conditions, please see +// Status condition to be applied to the XR. Condition may also optionally be +// applied to the XR's associated claim. For detailed information on proper +// usage of status conditions, please see // https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties. message Condition { // Type of condition in PascalCase. diff --git a/crossplane/function/proto/v1beta1/run_function_pb2.py b/crossplane/function/proto/v1beta1/run_function_pb2.py index dcd2eb0..4a6f64b 100644 --- a/crossplane/function/proto/v1beta1/run_function_pb2.py +++ b/crossplane/function/proto/v1beta1/run_function_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: crossplane/function/proto/v1beta1/run_function.proto -# Protobuf Python Version: 5.29.0 +# Protobuf Python Version: 6.31.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -11,8 +11,8 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, - 5, - 29, + 6, + 31, 0, '', 'crossplane/function/proto/v1beta1/run_function.proto' @@ -22,82 +22,94 @@ _sym_db = _symbol_database.Default() -from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +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\x1cgoogle/protobuf/struct.proto\x1a\x1egoogle/protobuf/duration.proto\"\xb0\x05\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_\n\x0f\x65xtra_resources\x18\x06 \x03(\x0b\x32\x46.apiextensions.fn.proto.v1beta1.RunFunctionRequest.ExtraResourcesEntry\x12X\n\x0b\x63redentials\x18\x07 \x03(\x0b\x32\x43.apiextensions.fn.proto.v1beta1.RunFunctionRequest.CredentialsEntry\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\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\"\x80\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.ConditionB\n\n\x08_context\"\x1a\n\x0bRequestMeta\x12\x0b\n\x03tag\x18\x01 \x01(\t\"\xd2\x01\n\x0cRequirements\x12Y\n\x0f\x65xtra_resources\x18\x01 \x03(\x0b\x32@.apiextensions.fn.proto.v1beta1.Requirements.ExtraResourcesEntry\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\"\x99\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\x42\x07\n\x05match\"\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\x46ZDgithub.com/crossplane/crossplane/apis/apiextensions/fn/proto/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\"\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') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'crossplane.function.proto.v1beta1.run_function_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'ZDgithub.com/crossplane/crossplane/apis/apiextensions/fn/proto/v1beta1' + _globals['DESCRIPTOR']._serialized_options = b'Z4github.com/crossplane/crossplane/v2/proto/fn/v1beta1' _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._loaded_options = None _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._serialized_options = b'8\001' _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._loaded_options = None _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_options = b'8\001' + _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._loaded_options = None + _globals['_RUNFUNCTIONREQUEST_REQUIREDRESOURCESENTRY']._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 _globals['_CREDENTIALDATA_DATAENTRY']._serialized_options = b'8\001' _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._loaded_options = None _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_options = b'8\001' + _globals['_REQUIREMENTS_RESOURCESENTRY']._loaded_options = None + _globals['_REQUIREMENTS_RESOURCESENTRY']._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 _globals['_MATCHLABELS_LABELSENTRY']._serialized_options = b'8\001' _globals['_STATE_RESOURCESENTRY']._loaded_options = None _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=3044 - _globals['_READY']._serialized_end=3107 - _globals['_SEVERITY']._serialized_start=3109 - _globals['_SEVERITY']._serialized_end=3208 - _globals['_TARGET']._serialized_start=3210 - _globals['_TARGET']._serialized_end=3296 - _globals['_STATUS']._serialized_start=3298 - _globals['_STATUS']._serialized_end=3425 + _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['_RUNFUNCTIONREQUEST']._serialized_start=151 - _globals['_RUNFUNCTIONREQUEST']._serialized_end=839 - _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._serialized_start=624 - _globals['_RUNFUNCTIONREQUEST_EXTRARESOURCESENTRY']._serialized_end=720 - _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_start=722 - _globals['_RUNFUNCTIONREQUEST_CREDENTIALSENTRY']._serialized_end=817 - _globals['_CREDENTIALS']._serialized_start=841 - _globals['_CREDENTIALS']._serialized_end=939 - _globals['_CREDENTIALDATA']._serialized_start=942 - _globals['_CREDENTIALDATA']._serialized_end=1075 - _globals['_CREDENTIALDATA_DATAENTRY']._serialized_start=1032 - _globals['_CREDENTIALDATA_DATAENTRY']._serialized_end=1075 - _globals['_RESOURCES']._serialized_start=1077 - _globals['_RESOURCES']._serialized_end=1145 - _globals['_RUNFUNCTIONRESPONSE']._serialized_start=1148 - _globals['_RUNFUNCTIONRESPONSE']._serialized_end=1532 - _globals['_REQUESTMETA']._serialized_start=1534 - _globals['_REQUESTMETA']._serialized_end=1560 - _globals['_REQUIREMENTS']._serialized_start=1563 - _globals['_REQUIREMENTS']._serialized_end=1773 - _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_start=1670 - _globals['_REQUIREMENTS_EXTRARESOURCESENTRY']._serialized_end=1773 - _globals['_RESOURCESELECTOR']._serialized_start=1776 - _globals['_RESOURCESELECTOR']._serialized_end=1929 - _globals['_MATCHLABELS']._serialized_start=1932 - _globals['_MATCHLABELS']._serialized_end=2065 - _globals['_MATCHLABELS_LABELSENTRY']._serialized_start=2020 - _globals['_MATCHLABELS_LABELSENTRY']._serialized_end=2065 - _globals['_RESPONSEMETA']._serialized_start=2067 - _globals['_RESPONSEMETA']._serialized_end=2147 - _globals['_STATE']._serialized_start=2150 - _globals['_STATE']._serialized_end=2383 - _globals['_STATE_RESOURCESENTRY']._serialized_start=2293 - _globals['_STATE_RESOURCESENTRY']._serialized_end=2383 - _globals['_RESOURCE']._serialized_start=2386 - _globals['_RESOURCE']._serialized_end=2644 - _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_start=2588 - _globals['_RESOURCE_CONNECTIONDETAILSENTRY']._serialized_end=2644 - _globals['_RESULT']._serialized_start=2647 - _globals['_RESULT']._serialized_end=2836 - _globals['_CONDITION']._serialized_start=2839 - _globals['_CONDITION']._serialized_end=3042 - _globals['_FUNCTIONRUNNERSERVICE']._serialized_start=3428 - _globals['_FUNCTIONRUNNERSERVICE']._serialized_end=3573 + _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 # @@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 5ba6180..ed96678 100644 --- a/crossplane/function/proto/v1beta1/run_function_pb2.pyi +++ b/crossplane/function/proto/v1beta1/run_function_pb2.pyi @@ -1,10 +1,11 @@ -from google.protobuf import struct_pb2 as _struct_pb2 from google.protobuf import duration_pb2 as _duration_pb2 +from google.protobuf import struct_pb2 as _struct_pb2 from google.protobuf.internal import containers as _containers from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message -from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -49,7 +50,7 @@ STATUS_CONDITION_TRUE: Status STATUS_CONDITION_FALSE: Status class RunFunctionRequest(_message.Message): - __slots__ = ("meta", "observed", "desired", "input", "context", "extra_resources", "credentials") + __slots__ = ("meta", "observed", "desired", "input", "context", "extra_resources", "credentials", "required_resources") class ExtraResourcesEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -64,6 +65,13 @@ class RunFunctionRequest(_message.Message): key: str value: Credentials def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[Credentials, _Mapping]] = ...) -> None: ... + class RequiredResourcesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: Resources + def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[Resources, _Mapping]] = ...) -> None: ... META_FIELD_NUMBER: _ClassVar[int] OBSERVED_FIELD_NUMBER: _ClassVar[int] DESIRED_FIELD_NUMBER: _ClassVar[int] @@ -71,6 +79,7 @@ class RunFunctionRequest(_message.Message): CONTEXT_FIELD_NUMBER: _ClassVar[int] EXTRA_RESOURCES_FIELD_NUMBER: _ClassVar[int] CREDENTIALS_FIELD_NUMBER: _ClassVar[int] + REQUIRED_RESOURCES_FIELD_NUMBER: _ClassVar[int] meta: RequestMeta observed: State desired: State @@ -78,7 +87,8 @@ class RunFunctionRequest(_message.Message): context: _struct_pb2.Struct extra_resources: _containers.MessageMap[str, Resources] credentials: _containers.MessageMap[str, Credentials] - 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]] = ...) -> None: ... + 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: ... class Credentials(_message.Message): __slots__ = ("credential_data",) @@ -106,20 +116,22 @@ class Resources(_message.Message): def __init__(self, items: _Optional[_Iterable[_Union[Resource, _Mapping]]] = ...) -> None: ... class RunFunctionResponse(_message.Message): - __slots__ = ("meta", "desired", "results", "context", "requirements", "conditions") + __slots__ = ("meta", "desired", "results", "context", "requirements", "conditions", "output") META_FIELD_NUMBER: _ClassVar[int] DESIRED_FIELD_NUMBER: _ClassVar[int] RESULTS_FIELD_NUMBER: _ClassVar[int] CONTEXT_FIELD_NUMBER: _ClassVar[int] REQUIREMENTS_FIELD_NUMBER: _ClassVar[int] CONDITIONS_FIELD_NUMBER: _ClassVar[int] + OUTPUT_FIELD_NUMBER: _ClassVar[int] meta: ResponseMeta desired: State results: _containers.RepeatedCompositeFieldContainer[Result] context: _struct_pb2.Struct requirements: Requirements conditions: _containers.RepeatedCompositeFieldContainer[Condition] - 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]]] = ...) -> None: ... + output: _struct_pb2.Struct + 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",) @@ -128,7 +140,7 @@ class RequestMeta(_message.Message): def __init__(self, tag: _Optional[str] = ...) -> None: ... class Requirements(_message.Message): - __slots__ = ("extra_resources",) + __slots__ = ("extra_resources", "resources") class ExtraResourcesEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -136,21 +148,32 @@ class Requirements(_message.Message): key: str value: ResourceSelector def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[ResourceSelector, _Mapping]] = ...) -> None: ... + class ResourcesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: ResourceSelector + def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[ResourceSelector, _Mapping]] = ...) -> None: ... EXTRA_RESOURCES_FIELD_NUMBER: _ClassVar[int] + RESOURCES_FIELD_NUMBER: _ClassVar[int] extra_resources: _containers.MessageMap[str, ResourceSelector] - def __init__(self, extra_resources: _Optional[_Mapping[str, ResourceSelector]] = ...) -> None: ... + resources: _containers.MessageMap[str, ResourceSelector] + def __init__(self, extra_resources: _Optional[_Mapping[str, ResourceSelector]] = ..., resources: _Optional[_Mapping[str, ResourceSelector]] = ...) -> None: ... class ResourceSelector(_message.Message): - __slots__ = ("api_version", "kind", "match_name", "match_labels") + __slots__ = ("api_version", "kind", "match_name", "match_labels", "namespace") API_VERSION_FIELD_NUMBER: _ClassVar[int] KIND_FIELD_NUMBER: _ClassVar[int] MATCH_NAME_FIELD_NUMBER: _ClassVar[int] MATCH_LABELS_FIELD_NUMBER: _ClassVar[int] + NAMESPACE_FIELD_NUMBER: _ClassVar[int] api_version: str kind: str match_name: str match_labels: MatchLabels - def __init__(self, api_version: _Optional[str] = ..., kind: _Optional[str] = ..., match_name: _Optional[str] = ..., match_labels: _Optional[_Union[MatchLabels, _Mapping]] = ...) -> None: ... + namespace: str + def __init__(self, api_version: _Optional[str] = ..., kind: _Optional[str] = ..., match_name: _Optional[str] = ..., match_labels: _Optional[_Union[MatchLabels, _Mapping]] = ..., namespace: _Optional[str] = ...) -> None: ... class MatchLabels(_message.Message): __slots__ = ("labels",) @@ -171,7 +194,7 @@ class ResponseMeta(_message.Message): TTL_FIELD_NUMBER: _ClassVar[int] tag: str ttl: _duration_pb2.Duration - def __init__(self, tag: _Optional[str] = ..., ttl: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ...) -> None: ... + def __init__(self, tag: _Optional[str] = ..., ttl: _Optional[_Union[datetime.timedelta, _duration_pb2.Duration, _Mapping]] = ...) -> None: ... class State(_message.Message): __slots__ = ("composite", "resources") diff --git a/crossplane/function/proto/v1beta1/run_function_pb2_grpc.py b/crossplane/function/proto/v1beta1/run_function_pb2_grpc.py index 1bd4f5d..281cd23 100644 --- a/crossplane/function/proto/v1beta1/run_function_pb2_grpc.py +++ b/crossplane/function/proto/v1beta1/run_function_pb2_grpc.py @@ -5,7 +5,7 @@ from crossplane.function.proto.v1beta1 import run_function_pb2 as crossplane_dot_function_dot_proto_dot_v1beta1_dot_run__function__pb2 -GRPC_GENERATED_VERSION = '1.71.0' +GRPC_GENERATED_VERSION = '1.73.1' GRPC_VERSION = grpc.__version__ _version_not_supported = False @@ -26,7 +26,7 @@ class FunctionRunnerServiceStub(object): - """A FunctionRunnerService is a Composition Function. + """A FunctionRunnerService is a function. """ def __init__(self, channel): @@ -43,11 +43,11 @@ def __init__(self, channel): class FunctionRunnerServiceServicer(object): - """A FunctionRunnerService is a Composition Function. + """A FunctionRunnerService is a function. """ def RunFunction(self, request, context): - """RunFunction runs the Composition Function. + """RunFunction runs the function. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -70,7 +70,7 @@ def add_FunctionRunnerServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. class FunctionRunnerService(object): - """A FunctionRunnerService is a Composition Function. + """A FunctionRunnerService is a function. """ @staticmethod diff --git a/crossplane/function/request.py b/crossplane/function/request.py new file mode 100644 index 0000000..ad408c9 --- /dev/null +++ b/crossplane/function/request.py @@ -0,0 +1,118 @@ +# Copyright 2025 The Crossplane Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utilities for working with RunFunctionRequests.""" + +import dataclasses + +import crossplane.function.proto.v1.run_function_pb2 as fnv1 +from crossplane.function import resource + + +@dataclasses.dataclass +class Credentials: + """Credentials.""" + + type: str + data: dict + + +def get_required_resources(req: fnv1.RunFunctionRequest, name: str) -> list[dict]: + """Get required resources by name from the request. + + Args: + req: The RunFunctionRequest containing required resources. + name: The name of the required resource set to get. + + Returns: + A list of resources as dictionaries. Empty list if not found. + + 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. + """ + if name not in req.required_resources: + return [] + + return [ + resource.struct_to_dict(item.resource) + for item in req.required_resources[name].items + ] + + +def get_watched_resource(req: fnv1.RunFunctionRequest) -> dict | None: + """Get the watched resource that triggered this operation. + + Args: + req: The RunFunctionRequest to check for a watched resource. + + Returns: + The watched resource as a dictionary, or None if not found. + + When a WatchOperation creates an Operation, it injects the resource that + changed using the special requirement name 'ops.crossplane.io/watched-resource'. + This helper makes it easy to access that resource. + """ + watched = get_required_resources(req, "ops.crossplane.io/watched-resource") + return watched[0] if watched else None + + +def get_required_resource(req: fnv1.RunFunctionRequest, name: str) -> dict | None: + """Get a single required resource by name from the request. + + Args: + req: The RunFunctionRequest containing required resources. + name: The name of the required resource to get. + + Returns: + The first resource as a dictionary, or None if not found. + + This is a convenience function for when you know there should be exactly + one resource with the given requirement name. + """ + resources = get_required_resources(req, name) + return resources[0] if resources else None + + +def get_credentials(req: fnv1.RunFunctionRequest, name: str) -> Credentials: + """Get the supplied credentials from the request. + + Args: + req: The RunFunctionRequest containing credentials. + name: The name of the credentials to get. + + Returns: + The requested credentials with type and data. + + If the credentials don't exist, returns empty credentials with type "data" + and empty data dictionary. + """ + empty = Credentials(type="data", data={}) + + if not req or name not in req.credentials: + return empty + + cred = req.credentials[name] + + # Use WhichOneof to determine which field in the oneof is set + source_type = cred.WhichOneof("source") + if source_type == "credential_data": + # Convert bytes data to string data for backward compatibility + data = {} + for key, value in cred.credential_data.data.items(): + data[key] = value.decode("utf-8") + return Credentials(type="credential_data", data=data) + + # If no recognized source type is set, return empty + return empty diff --git a/crossplane/function/resource.py b/crossplane/function/resource.py index c9bfc69..fae7be6 100644 --- a/crossplane/function/resource.py +++ b/crossplane/function/resource.py @@ -140,24 +140,3 @@ def get_condition(resource: structpb.Struct, typ: str) -> Condition: return condition return unknown - - -@dataclasses.dataclass -class Credentials: - """Credentials.""" - - type: str - data: dict - - -def get_credentials(req: structpb.Struct, name: str) -> Credentials: - """Get the supplied credentials.""" - empty = Credentials(type="data", data={}) - if not req or "credentials" not in req: - return empty - if not req["credentials"] or name not in req["credentials"]: - return empty - return Credentials( - type=req["credentials"][name]["type"], - data=struct_to_dict(req["credentials"][name]["data"]), - ) diff --git a/crossplane/function/response.py b/crossplane/function/response.py index b37fd8d..72222b6 100644 --- a/crossplane/function/response.py +++ b/crossplane/function/response.py @@ -17,8 +17,10 @@ import datetime from google.protobuf import duration_pb2 as durationpb +from google.protobuf import struct_pb2 as structpb import crossplane.function.proto.v1.run_function_pb2 as fnv1 +from crossplane.function import resource """The default TTL for which a RunFunctionResponse may be cached.""" DEFAULT_TTL = datetime.timedelta(minutes=1) @@ -77,3 +79,73 @@ def fatal(rsp: fnv1.RunFunctionResponse, message: str) -> None: message=message, ) ) + + +def set_output(rsp: fnv1.RunFunctionResponse, output: dict | structpb.Struct) -> None: + """Set the output field in a RunFunctionResponse for operation functions. + + Args: + rsp: The RunFunctionResponse to update. + output: The output data as a dictionary or protobuf Struct. + + Operation functions can return arbitrary output data that will be written + to the Operation's status.pipeline field. This function sets that output + on the response. + """ + match output: + case dict(): + rsp.output.CopyFrom(resource.dict_to_struct(output)) + case structpb.Struct(): + rsp.output.CopyFrom(output) + case _: + t = type(output) + msg = f"Unsupported output type: {t}" + raise TypeError(msg) + + +def require_resources( # noqa: PLR0913 + rsp: fnv1.RunFunctionResponse, + name: str, + api_version: str, + kind: str, + *, + match_name: str | None = None, + match_labels: dict[str, str] | None = None, + namespace: str | None = None, +) -> None: + """Add a resource requirement to the response. + + Args: + rsp: The RunFunctionResponse to update. + name: The name to use for this requirement. + api_version: The API version of resources to require. + kind: The kind of resources to require. + match_name: Match a resource by name (mutually exclusive with match_labels). + match_labels: Match resources by labels (mutually exclusive with match_name). + namespace: The namespace to search in (optional). + + Raises: + ValueError: If both match_name and match_labels are provided, or neither. + + This tells Crossplane to fetch the specified resources and include them + in the next call to the function in req.required_resources[name]. + """ + if (match_name is None) == (match_labels is None): + msg = "Exactly one of match_name or match_labels must be provided" + raise ValueError(msg) + + selector = fnv1.ResourceSelector( + api_version=api_version, + kind=kind, + ) + + if match_name is not None: + selector.match_name = match_name + + if match_labels is not None: + selector.match_labels.labels.update(match_labels) + + if namespace is not None: + selector.namespace = namespace + + rsp.requirements.resources[name].CopyFrom(selector) diff --git a/pyproject.toml b/pyproject.toml index 6b2e071..baad132 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ classifiers = [ ] dependencies = [ - "grpcio==1.73.1", + "grpcio==1.74.0", "grpcio-reflection==1.*", "protobuf==6.31.1", # Must be compatible with grpcio-tools. "pydantic==2.*", @@ -46,7 +46,7 @@ type = "virtual" detached = true path = ".venv-generate" dependencies = [ - "grpcio-tools==1.73.1", + "grpcio-tools==1.74.0", "protobuf==6.31.1", ] @@ -66,7 +66,7 @@ packages = ["crossplane"] # This special environment is used by hatch fmt. [tool.hatch.envs.hatch-static-analysis] -dependencies = ["ruff==0.12.3"] +dependencies = ["ruff==0.12.7"] config-path = "none" # Disable Hatch's default Ruff config. [tool.ruff] diff --git a/tests/test_request.py b/tests/test_request.py new file mode 100644 index 0000000..a517814 --- /dev/null +++ b/tests/test_request.py @@ -0,0 +1,269 @@ +# Copyright 2025 The Crossplane Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import dataclasses +import unittest + +import crossplane.function.proto.v1.run_function_pb2 as fnv1 +from crossplane.function import logging, request, resource + + +class TestRequest(unittest.TestCase): + def setUp(self) -> None: + logging.configure(level=logging.Level.DISABLED) + + def test_get_required_resources(self) -> None: + @dataclasses.dataclass + class TestCase: + reason: str + req: fnv1.RunFunctionRequest + name: str + want: list[dict] + + cases = [ + TestCase( + reason="Should return empty list when requirement name not found.", + req=fnv1.RunFunctionRequest(), + name="non-existent", + want=[], + ), + TestCase( + reason="Should return resources when requirement name exists.", + req=fnv1.RunFunctionRequest( + required_resources={ + "test-resources": fnv1.Resources( + items=[ + fnv1.Resource( + resource=resource.dict_to_struct( + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": {"name": "test-pod"}, + } + ) + ), + fnv1.Resource( + resource=resource.dict_to_struct( + { + "apiVersion": "v1", + "kind": "Service", + "metadata": {"name": "test-svc"}, + } + ) + ), + ] + ) + } + ), + name="test-resources", + want=[ + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": {"name": "test-pod"}, + }, + { + "apiVersion": "v1", + "kind": "Service", + "metadata": {"name": "test-svc"}, + }, + ], + ), + ] + + for case in cases: + got = request.get_required_resources(case.req, case.name) + self.assertEqual(case.want, got, case.reason) + + def test_get_watched_resource(self) -> None: + @dataclasses.dataclass + class TestCase: + reason: str + req: fnv1.RunFunctionRequest + want: dict | None + + cases = [ + TestCase( + reason="Should return None when no watched resource exists.", + req=fnv1.RunFunctionRequest(), + want=None, + ), + TestCase( + reason="Should return watched resource when it exists.", + req=fnv1.RunFunctionRequest( + required_resources={ + "ops.crossplane.io/watched-resource": fnv1.Resources( + items=[ + fnv1.Resource( + resource=resource.dict_to_struct( + { + "apiVersion": "example.org/v1", + "kind": "App", + "metadata": {"name": "watched-app"}, + } + ) + ) + ] + ) + } + ), + want={ + "apiVersion": "example.org/v1", + "kind": "App", + "metadata": {"name": "watched-app"}, + }, + ), + ] + + for case in cases: + got = request.get_watched_resource(case.req) + self.assertEqual(case.want, got, case.reason) + + def test_get_required_resource(self) -> None: + @dataclasses.dataclass + class TestCase: + reason: str + req: fnv1.RunFunctionRequest + name: str + want: dict | None + + cases = [ + TestCase( + reason="Should return None when requirement name not found.", + req=fnv1.RunFunctionRequest(), + name="non-existent", + want=None, + ), + TestCase( + reason="Should return first resource when requirement name exists.", + req=fnv1.RunFunctionRequest( + required_resources={ + "single-resource": fnv1.Resources( + items=[ + fnv1.Resource( + resource=resource.dict_to_struct( + { + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": {"name": "test-cm"}, + } + ) + ) + ] + ) + } + ), + name="single-resource", + want={ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": {"name": "test-cm"}, + }, + ), + TestCase( + reason="Should return first resource when multiple resources exist.", + req=fnv1.RunFunctionRequest( + required_resources={ + "multi-resource": fnv1.Resources( + items=[ + fnv1.Resource( + resource=resource.dict_to_struct( + { + "apiVersion": "v1", + "kind": "Secret", + "metadata": {"name": "first-secret"}, + } + ) + ), + fnv1.Resource( + resource=resource.dict_to_struct( + { + "apiVersion": "v1", + "kind": "Secret", + "metadata": {"name": "second-secret"}, + } + ) + ), + ] + ) + } + ), + name="multi-resource", + want={ + "apiVersion": "v1", + "kind": "Secret", + "metadata": {"name": "first-secret"}, + }, + ), + ] + + for case in cases: + got = request.get_required_resource(case.req, case.name) + self.assertEqual(case.want, got, case.reason) + + def test_get_credentials(self) -> None: + @dataclasses.dataclass + class TestCase: + reason: str + req: fnv1.RunFunctionRequest + name: str + want: request.Credentials + + cases = [ + TestCase( + reason="Should return empty credentials when no credentials exist.", + req=fnv1.RunFunctionRequest(), + name="test", + want=request.Credentials(type="data", data={}), + ), + TestCase( + reason="Should return empty credentials when specified name not found.", + req=fnv1.RunFunctionRequest( + credentials={ + "other-cred": fnv1.Credentials( + credential_data=fnv1.CredentialData(data={"key": b"value"}) + ) + } + ), + name="test", + want=request.Credentials(type="data", data={}), + ), + TestCase( + reason="Should return credentials when they exist.", + req=fnv1.RunFunctionRequest( + credentials={ + "test": fnv1.Credentials( + credential_data=fnv1.CredentialData( + data={"username": b"admin", "password": b"secret"} + ) + ) + } + ), + name="test", + want=request.Credentials( + type="credential_data", + data={"username": "admin", "password": "secret"}, + ), + ), + ] + + for case in cases: + got = request.get_credentials(case.req, case.name) + self.assertEqual( + dataclasses.asdict(case.want), dataclasses.asdict(got), case.reason + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_resource.py b/tests/test_resource.py index 010fed8..004295f 100644 --- a/tests/test_resource.py +++ b/tests/test_resource.py @@ -205,49 +205,6 @@ class TestCase: dataclasses.asdict(case.want), dataclasses.asdict(got), "-want, +got" ) - def test_get_credentials(self) -> None: - @dataclasses.dataclass - class TestCase: - reason: str - req: structpb.Struct - name: str - want: resource.Credentials - - cases = [ - TestCase( - reason="Return the specified credentials if they exist.", - req=resource.dict_to_struct( - {"credentials": {"test": {"type": "data", "data": {"foo": "bar"}}}} - ), - name="test", - want=resource.Credentials(type="data", data={"foo": "bar"}), - ), - TestCase( - reason="Return empty credentials if no credentials section exists.", - req=resource.dict_to_struct({}), - name="test", - want=resource.Credentials(type="data", data={}), - ), - TestCase( - reason="Return empty credentials if the specified name does not exist.", - req=resource.dict_to_struct( - { - "credentials": { - "nottest": {"type": "data", "data": {"foo": "bar"}} - } - } - ), - name="test", - want=resource.Credentials(type="data", data={}), - ), - ] - - for case in cases: - got = resource.get_credentials(case.req, case.name) - self.assertEqual( - dataclasses.asdict(case.want), dataclasses.asdict(got), "-want, +got" - ) - def test_dict_to_struct(self) -> None: @dataclasses.dataclass class TestCase: diff --git a/tests/test_response.py b/tests/test_response.py index 8273c50..9e33153 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -18,6 +18,7 @@ from google.protobuf import duration_pb2 as durationpb from google.protobuf import json_format +from google.protobuf import struct_pb2 as structpb from crossplane.function import logging, resource, response from crossplane.function.proto.v1 import run_function_pb2 as fnv1 @@ -65,11 +66,155 @@ class TestCase: for case in cases: got = response.to(case.req, case.ttl) self.assertEqual( - json_format.MessageToJson(case.want), - json_format.MessageToJson(got), + json_format.MessageToJson(case.want, sort_keys=True), + json_format.MessageToJson(got, sort_keys=True), "-want, +got", ) + def test_set_output(self) -> None: + @dataclasses.dataclass + class TestCase: + reason: str + rsp: fnv1.RunFunctionResponse + output: dict | structpb.Struct + want_output: dict + + cases = [ + TestCase( + reason="Setting output from dict should work.", + rsp=fnv1.RunFunctionResponse(), + output={"status": "success", "processed": 42}, + want_output={"status": "success", "processed": 42}, + ), + TestCase( + reason="Setting output from Struct should work.", + rsp=fnv1.RunFunctionResponse(), + output=resource.dict_to_struct({"result": "completed", "count": 3}), + want_output={"result": "completed", "count": 3}, + ), + ] + + for case in cases: + response.set_output(case.rsp, case.output) + got_output = resource.struct_to_dict(case.rsp.output) + self.assertEqual(case.want_output, got_output, case.reason) + + def test_set_output_invalid_type(self) -> None: + rsp = fnv1.RunFunctionResponse() + with self.assertRaises(TypeError): + response.set_output(rsp, "invalid-string-type") + + def test_require_resources(self) -> None: + @dataclasses.dataclass + class TestCase: + reason: str + rsp: fnv1.RunFunctionResponse + name: str + api_version: str + kind: str + match_name: str | None + match_labels: dict[str, str] | None + namespace: str | None + want_selector: fnv1.ResourceSelector + + cases = [ + TestCase( + reason="Should create requirement with match_name.", + rsp=fnv1.RunFunctionResponse(), + name="test-pods", + api_version="v1", + kind="Pod", + match_name="my-pod", + match_labels=None, + namespace="default", + want_selector=fnv1.ResourceSelector( + api_version="v1", + kind="Pod", + match_name="my-pod", + namespace="default", + ), + ), + TestCase( + reason="Should create requirement with match_labels.", + rsp=fnv1.RunFunctionResponse(), + name="app-pods", + api_version="v1", + kind="Pod", + match_name=None, + match_labels={"app": "web", "version": "v1.2.3"}, + namespace="production", + want_selector=fnv1.ResourceSelector( + api_version="v1", + kind="Pod", + match_labels=fnv1.MatchLabels( + labels={"app": "web", "version": "v1.2.3"} + ), + namespace="production", + ), + ), + TestCase( + reason="Should create requirement without namespace.", + rsp=fnv1.RunFunctionResponse(), + name="cluster-resources", + api_version="v1", + kind="Node", + match_name="worker-1", + match_labels=None, + namespace=None, + want_selector=fnv1.ResourceSelector( + api_version="v1", + kind="Node", + match_name="worker-1", + ), + ), + ] + + for case in cases: + response.require_resources( + case.rsp, + case.name, + case.api_version, + case.kind, + match_name=case.match_name, + match_labels=case.match_labels, + namespace=case.namespace, + ) + + # Check that the requirement was added + self.assertIn(case.name, case.rsp.requirements.resources, case.reason) + got_selector = case.rsp.requirements.resources[case.name] + + self.assertEqual( + json_format.MessageToJson(case.want_selector, sort_keys=True), + json_format.MessageToJson(got_selector, sort_keys=True), + case.reason, + ) + + def test_require_resources_invalid_args(self) -> None: + rsp = fnv1.RunFunctionResponse() + + # Should raise ValueError if both match_name and match_labels are provided + with self.assertRaises(ValueError): + response.require_resources( + rsp, + "test", + "v1", + "Pod", + match_name="pod-name", + match_labels={"app": "test"}, + ) + + # Should raise ValueError if neither match_name nor match_labels are provided + with self.assertRaises(ValueError): + response.require_resources( + rsp, + "test", + "v1", + "Pod", + match_name=None, + match_labels=None, + ) + if __name__ == "__main__": unittest.main()