From 229a0a598e188174bb554258e39f3881b2b172cd Mon Sep 17 00:00:00 2001 From: Steven Borrelli Date: Tue, 29 Sep 2026 09:39:36 +0200 Subject: [PATCH] Support the dependency messages in the proto Sync proto/v1 with the composed resource ordering prototype in crossplane/crossplane#7842 and regenerate, picking up Dependencies, Dependency, RequiredResourceDependency, the DependencyLifecycle enum, the dependencies field on the request and the response, and CAPABILITY_DEPENDENCIES. response.To carries the request's dependencies forward, as it does desired state and context, so a function that adds one keeps the ones earlier functions declared. It copies them only when the request has them: unset means "no opinion, carry mine forward" and empty means "drop every constraint", so turning one into the other would change the graph. It copies rather than shares them, so adding to the response doesn't add to the request. AddDependency declares a dependency between composed resources, with WithCreateBeforeDestroy for a replacement, AddRequiredResourceDependency one on a resource the function requires, and ClearDependencies drops them all. This mirrors crossplane/function-sdk-python#241, and like it can't land before the protocol change does. Co-Authored-By: Claude Opus 5.5 --- proto/v1/run_function.pb.go | 667 +++++++++++++++++++++++++++--------- proto/v1/run_function.proto | 89 +++++ response/response.go | 87 ++++- response/response_test.go | 131 +++++++ 4 files changed, 817 insertions(+), 157 deletions(-) diff --git a/proto/v1/run_function.pb.go b/proto/v1/run_function.pb.go index ce16c3e..5b69253 100644 --- a/proto/v1/run_function.pb.go +++ b/proto/v1/run_function.pb.go @@ -45,6 +45,63 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// A DependencyLifecycle is how a dependency constrains the order its endpoints +// are created and deleted. +type DependencyLifecycle int32 + +const ( + // Symmetric ordering: the resource is created only once what it depends on + // is ready, and what it depends on is deleted only once the resource is + // gone. This is the default, and the only valid value when depends_on is a + // required resource, because Crossplane never deletes a resource it didn't + // compose. + DependencyLifecycle_DEPENDENCY_LIFECYCLE_UNSPECIFIED DependencyLifecycle = 0 + // The resource may be created without waiting for what it depends on to be + // deleted. It must still exist and be ready before what it depends on is + // deleted. Use for a replacement that must exist before its predecessor is + // torn down. Only valid when depends_on is a composed resource. + DependencyLifecycle_DEPENDENCY_LIFECYCLE_CREATE_BEFORE_DESTROY DependencyLifecycle = 1 +) + +// Enum value maps for DependencyLifecycle. +var ( + DependencyLifecycle_name = map[int32]string{ + 0: "DEPENDENCY_LIFECYCLE_UNSPECIFIED", + 1: "DEPENDENCY_LIFECYCLE_CREATE_BEFORE_DESTROY", + } + DependencyLifecycle_value = map[string]int32{ + "DEPENDENCY_LIFECYCLE_UNSPECIFIED": 0, + "DEPENDENCY_LIFECYCLE_CREATE_BEFORE_DESTROY": 1, + } +) + +func (x DependencyLifecycle) Enum() *DependencyLifecycle { + p := new(DependencyLifecycle) + *p = x + return p +} + +func (x DependencyLifecycle) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (DependencyLifecycle) Descriptor() protoreflect.EnumDescriptor { + return file_v1_run_function_proto_enumTypes[0].Descriptor() +} + +func (DependencyLifecycle) Type() protoreflect.EnumType { + return &file_v1_run_function_proto_enumTypes[0] +} + +func (x DependencyLifecycle) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use DependencyLifecycle.Descriptor instead. +func (DependencyLifecycle) EnumDescriptor() ([]byte, []int) { + return file_v1_run_function_proto_rawDescGZIP(), []int{0} +} + // Capability indicates that Crossplane supports a particular feature. // Functions can check for capabilities to determine whether Crossplane will // honor a particular request or response field. @@ -73,6 +130,10 @@ const ( // OpenAPI schemas and Crossplane will return them in required_schemas. Added // in Crossplane v2.2. Capability_CAPABILITY_REQUIRED_SCHEMAS Capability = 5 + // Crossplane supports the dependencies field. Functions can declare ordering + // constraints over composed resources, and Crossplane will sequence the + // resources it creates, updates and deletes accordingly. + Capability_CAPABILITY_DEPENDENCIES Capability = 6 ) // Enum value maps for Capability. @@ -84,6 +145,7 @@ var ( 3: "CAPABILITY_CREDENTIALS", 4: "CAPABILITY_CONDITIONS", 5: "CAPABILITY_REQUIRED_SCHEMAS", + 6: "CAPABILITY_DEPENDENCIES", } Capability_value = map[string]int32{ "CAPABILITY_UNSPECIFIED": 0, @@ -92,6 +154,7 @@ var ( "CAPABILITY_CREDENTIALS": 3, "CAPABILITY_CONDITIONS": 4, "CAPABILITY_REQUIRED_SCHEMAS": 5, + "CAPABILITY_DEPENDENCIES": 6, } ) @@ -106,11 +169,11 @@ func (x Capability) String() string { } func (Capability) Descriptor() protoreflect.EnumDescriptor { - return file_v1_run_function_proto_enumTypes[0].Descriptor() + return file_v1_run_function_proto_enumTypes[1].Descriptor() } func (Capability) Type() protoreflect.EnumType { - return &file_v1_run_function_proto_enumTypes[0] + return &file_v1_run_function_proto_enumTypes[1] } func (x Capability) Number() protoreflect.EnumNumber { @@ -119,7 +182,7 @@ func (x Capability) Number() protoreflect.EnumNumber { // Deprecated: Use Capability.Descriptor instead. func (Capability) EnumDescriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{0} + return file_v1_run_function_proto_rawDescGZIP(), []int{1} } // Ready indicates whether a resource should be considered ready. @@ -158,11 +221,11 @@ func (x Ready) String() string { } func (Ready) Descriptor() protoreflect.EnumDescriptor { - return file_v1_run_function_proto_enumTypes[1].Descriptor() + return file_v1_run_function_proto_enumTypes[2].Descriptor() } func (Ready) Type() protoreflect.EnumType { - return &file_v1_run_function_proto_enumTypes[1] + return &file_v1_run_function_proto_enumTypes[2] } func (x Ready) Number() protoreflect.EnumNumber { @@ -171,7 +234,7 @@ func (x Ready) Number() protoreflect.EnumNumber { // Deprecated: Use Ready.Descriptor instead. func (Ready) EnumDescriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{1} + return file_v1_run_function_proto_rawDescGZIP(), []int{2} } // Severity of function results. @@ -219,11 +282,11 @@ func (x Severity) String() string { } func (Severity) Descriptor() protoreflect.EnumDescriptor { - return file_v1_run_function_proto_enumTypes[2].Descriptor() + return file_v1_run_function_proto_enumTypes[3].Descriptor() } func (Severity) Type() protoreflect.EnumType { - return &file_v1_run_function_proto_enumTypes[2] + return &file_v1_run_function_proto_enumTypes[3] } func (x Severity) Number() protoreflect.EnumNumber { @@ -232,7 +295,7 @@ func (x Severity) Number() protoreflect.EnumNumber { // Deprecated: Use Severity.Descriptor instead. func (Severity) EnumDescriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{2} + return file_v1_run_function_proto_rawDescGZIP(), []int{3} } // Target of function results and conditions. @@ -274,11 +337,11 @@ func (x Target) String() string { } func (Target) Descriptor() protoreflect.EnumDescriptor { - return file_v1_run_function_proto_enumTypes[3].Descriptor() + return file_v1_run_function_proto_enumTypes[4].Descriptor() } func (Target) Type() protoreflect.EnumType { - return &file_v1_run_function_proto_enumTypes[3] + return &file_v1_run_function_proto_enumTypes[4] } func (x Target) Number() protoreflect.EnumNumber { @@ -287,7 +350,7 @@ func (x Target) Number() protoreflect.EnumNumber { // Deprecated: Use Target.Descriptor instead. func (Target) EnumDescriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{3} + return file_v1_run_function_proto_rawDescGZIP(), []int{4} } type Status int32 @@ -326,11 +389,11 @@ func (x Status) String() string { } func (Status) Descriptor() protoreflect.EnumDescriptor { - return file_v1_run_function_proto_enumTypes[4].Descriptor() + return file_v1_run_function_proto_enumTypes[5].Descriptor() } func (Status) Type() protoreflect.EnumType { - return &file_v1_run_function_proto_enumTypes[4] + return &file_v1_run_function_proto_enumTypes[5] } func (x Status) Number() protoreflect.EnumNumber { @@ -339,7 +402,7 @@ func (x Status) Number() protoreflect.EnumNumber { // Deprecated: Use Status.Descriptor instead. func (Status) EnumDescriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{4} + return file_v1_run_function_proto_rawDescGZIP(), []int{5} } // A RunFunctionRequest requests that the function be run. @@ -399,8 +462,11 @@ type RunFunctionRequest struct { // sets the map key to an empty Schema message to indicate that it attempted // to satisfy the request. RequiredSchemas map[string]*Schema `protobuf:"bytes,9,rep,name=required_schemas,json=requiredSchemas,proto3" json:"required_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Optional ordering constraints over composed resources, accumulated by the + // functions that ran before this one. + Dependencies *Dependencies `protobuf:"bytes,10,opt,name=dependencies,proto3" json:"dependencies,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RunFunctionRequest) Reset() { @@ -497,6 +563,13 @@ func (x *RunFunctionRequest) GetRequiredSchemas() map[string]*Schema { return nil } +func (x *RunFunctionRequest) GetDependencies() *Dependencies { + if x != nil { + return x.Dependencies + } + return nil +} + // Credentials that a function may use to communicate with an external system. type Credentials struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -657,6 +730,240 @@ func (x *Resources) GetItems() []*Resource { return nil } +// Dependencies is a set of ordering constraints over composed resources. +// +// This is a message wrapping a repeated field, rather than a bare repeated +// field, so that an unset value can be told apart from an empty one. A bare +// repeated field can't express that difference: proto3 has no presence for +// repeated fields, and an empty list and an unset field are both zero bytes on +// the wire. Crossplane relies on the difference, because an unset value means +// "this function has no opinion, carry my constraints forward" while an empty +// one means "this function wants no constraints at all." State wraps desired +// and observed resources for the same reason. +type Dependencies struct { + state protoimpl.MessageState `protogen:"open.v1"` + Items []*Dependency `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Dependencies) Reset() { + *x = Dependencies{} + mi := &file_v1_run_function_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Dependencies) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Dependencies) ProtoMessage() {} + +func (x *Dependencies) ProtoReflect() protoreflect.Message { + mi := &file_v1_run_function_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Dependencies.ProtoReflect.Descriptor instead. +func (*Dependencies) Descriptor() ([]byte, []int) { + return file_v1_run_function_proto_rawDescGZIP(), []int{4} +} + +func (x *Dependencies) GetItems() []*Dependency { + if x != nil { + return x.Items + } + return nil +} + +// A Dependency declares that one composed resource must be created after, and +// deleted before, another resource. It expresses ordering only. It doesn't move +// any data between resources. +type Dependency struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Name of the composed resource that has the dependency. A key into the + // desired or observed State.resources map. + Resource string `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` + // What the resource depends on. + // + // Types that are valid to be assigned to DependsOn: + // + // *Dependency_ComposedResource + // *Dependency_RequiredResource + DependsOn isDependency_DependsOn `protobuf_oneof:"depends_on"` + // How this dependency constrains the order its endpoints are created and + // deleted. Defaults to symmetric ordering. + Lifecycle DependencyLifecycle `protobuf:"varint,3,opt,name=lifecycle,proto3,enum=apiextensions.fn.proto.v1.DependencyLifecycle" json:"lifecycle,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Dependency) Reset() { + *x = Dependency{} + mi := &file_v1_run_function_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Dependency) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Dependency) ProtoMessage() {} + +func (x *Dependency) ProtoReflect() protoreflect.Message { + mi := &file_v1_run_function_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Dependency.ProtoReflect.Descriptor instead. +func (*Dependency) Descriptor() ([]byte, []int) { + return file_v1_run_function_proto_rawDescGZIP(), []int{5} +} + +func (x *Dependency) GetResource() string { + if x != nil { + return x.Resource + } + return "" +} + +func (x *Dependency) GetDependsOn() isDependency_DependsOn { + if x != nil { + return x.DependsOn + } + return nil +} + +func (x *Dependency) GetComposedResource() string { + if x != nil { + if x, ok := x.DependsOn.(*Dependency_ComposedResource); ok { + return x.ComposedResource + } + } + return "" +} + +func (x *Dependency) GetRequiredResource() *RequiredResourceDependency { + if x != nil { + if x, ok := x.DependsOn.(*Dependency_RequiredResource); ok { + return x.RequiredResource + } + } + return nil +} + +func (x *Dependency) GetLifecycle() DependencyLifecycle { + if x != nil { + return x.Lifecycle + } + return DependencyLifecycle_DEPENDENCY_LIFECYCLE_UNSPECIFIED +} + +type isDependency_DependsOn interface { + isDependency_DependsOn() +} + +type Dependency_ComposedResource struct { + // Name of another composed resource. A key into the desired or observed + // State.resources map. + ComposedResource string `protobuf:"bytes,2,opt,name=composed_resource,json=composedResource,proto3,oneof"` +} + +type Dependency_RequiredResource struct { + // A resource the pipeline required, rather than composed. Crossplane never + // deletes a resource it didn't compose, so these constrain only the order + // resources are created and updated. + RequiredResource *RequiredResourceDependency `protobuf:"bytes,4,opt,name=required_resource,json=requiredResource,proto3,oneof"` +} + +func (*Dependency_ComposedResource) isDependency_DependsOn() {} + +func (*Dependency_RequiredResource) isDependency_DependsOn() {} + +// A RequiredResourceDependency identifies a resource the pipeline required, +// for the purpose of ordering. +type RequiredResourceDependency struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The requirement name. A key into a RunFunctionRequest's required_resources + // map, and into a RunFunctionResponse's requirements.resources map. + RequirementName string `protobuf:"bytes,1,opt,name=requirement_name,json=requirementName,proto3" json:"requirement_name,omitempty"` + // Optional name of a single resource within the set the requirement matched. + // If unset, every resource the requirement matched must be ready. + Name *string `protobuf:"bytes,2,opt,name=name,proto3,oneof" json:"name,omitempty"` + // Namespace of name for a namespaced resource. Leave unset for a + // cluster-scoped resource. name and namespace together identify one match. + Namespace *string `protobuf:"bytes,3,opt,name=namespace,proto3,oneof" json:"namespace,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RequiredResourceDependency) Reset() { + *x = RequiredResourceDependency{} + mi := &file_v1_run_function_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RequiredResourceDependency) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RequiredResourceDependency) ProtoMessage() {} + +func (x *RequiredResourceDependency) ProtoReflect() protoreflect.Message { + mi := &file_v1_run_function_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RequiredResourceDependency.ProtoReflect.Descriptor instead. +func (*RequiredResourceDependency) Descriptor() ([]byte, []int) { + return file_v1_run_function_proto_rawDescGZIP(), []int{6} +} + +func (x *RequiredResourceDependency) GetRequirementName() string { + if x != nil { + return x.RequirementName + } + return "" +} + +func (x *RequiredResourceDependency) GetName() string { + if x != nil && x.Name != nil { + return *x.Name + } + return "" +} + +func (x *RequiredResourceDependency) GetNamespace() string { + if x != nil && x.Namespace != nil { + return *x.Namespace + } + return "" +} + // A RunFunctionResponse contains the result of a function run. type RunFunctionResponse struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -689,14 +996,21 @@ type RunFunctionResponse struct { // Optional output specific to this function invocation. // // Only Operations use function output. XRs will discard any function output. - Output *structpb.Struct `protobuf:"bytes,7,opt,name=output,proto3,oneof" json:"output,omitempty"` + Output *structpb.Struct `protobuf:"bytes,7,opt,name=output,proto3,oneof" json:"output,omitempty"` + // Optional ordering constraints over composed resources. A function that has + // an opinion about ordering must return the full set it wants going forward, + // including edges it received and still wants. Leaving this field unset means + // "no opinion" - Crossplane carries forward whatever it sent in the request. + // + // Dependencies are only used for composition. They're ignored by Operations. + Dependencies *Dependencies `protobuf:"bytes,8,opt,name=dependencies,proto3" json:"dependencies,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RunFunctionResponse) Reset() { *x = RunFunctionResponse{} - mi := &file_v1_run_function_proto_msgTypes[4] + mi := &file_v1_run_function_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -708,7 +1022,7 @@ func (x *RunFunctionResponse) String() string { func (*RunFunctionResponse) ProtoMessage() {} func (x *RunFunctionResponse) ProtoReflect() protoreflect.Message { - mi := &file_v1_run_function_proto_msgTypes[4] + mi := &file_v1_run_function_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -721,7 +1035,7 @@ func (x *RunFunctionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RunFunctionResponse.ProtoReflect.Descriptor instead. func (*RunFunctionResponse) Descriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{4} + return file_v1_run_function_proto_rawDescGZIP(), []int{7} } func (x *RunFunctionResponse) GetMeta() *ResponseMeta { @@ -773,6 +1087,13 @@ func (x *RunFunctionResponse) GetOutput() *structpb.Struct { return nil } +func (x *RunFunctionResponse) GetDependencies() *Dependencies { + if x != nil { + return x.Dependencies + } + return nil +} + // RequestMeta contains metadata pertaining to a RunFunctionRequest. type RequestMeta struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -789,7 +1110,7 @@ type RequestMeta struct { func (x *RequestMeta) Reset() { *x = RequestMeta{} - mi := &file_v1_run_function_proto_msgTypes[5] + mi := &file_v1_run_function_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -801,7 +1122,7 @@ func (x *RequestMeta) String() string { func (*RequestMeta) ProtoMessage() {} func (x *RequestMeta) ProtoReflect() protoreflect.Message { - mi := &file_v1_run_function_proto_msgTypes[5] + mi := &file_v1_run_function_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -814,7 +1135,7 @@ func (x *RequestMeta) ProtoReflect() protoreflect.Message { // Deprecated: Use RequestMeta.ProtoReflect.Descriptor instead. func (*RequestMeta) Descriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{5} + return file_v1_run_function_proto_rawDescGZIP(), []int{8} } func (x *RequestMeta) GetTag() string { @@ -853,7 +1174,7 @@ type Requirements struct { func (x *Requirements) Reset() { *x = Requirements{} - mi := &file_v1_run_function_proto_msgTypes[6] + mi := &file_v1_run_function_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -865,7 +1186,7 @@ func (x *Requirements) String() string { func (*Requirements) ProtoMessage() {} func (x *Requirements) ProtoReflect() protoreflect.Message { - mi := &file_v1_run_function_proto_msgTypes[6] + mi := &file_v1_run_function_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -878,7 +1199,7 @@ func (x *Requirements) ProtoReflect() protoreflect.Message { // Deprecated: Use Requirements.ProtoReflect.Descriptor instead. func (*Requirements) Descriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{6} + return file_v1_run_function_proto_rawDescGZIP(), []int{9} } // Deprecated: Marked as deprecated in v1/run_function.proto. @@ -916,7 +1237,7 @@ type SchemaSelector struct { func (x *SchemaSelector) Reset() { *x = SchemaSelector{} - mi := &file_v1_run_function_proto_msgTypes[7] + mi := &file_v1_run_function_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -928,7 +1249,7 @@ func (x *SchemaSelector) String() string { func (*SchemaSelector) ProtoMessage() {} func (x *SchemaSelector) ProtoReflect() protoreflect.Message { - mi := &file_v1_run_function_proto_msgTypes[7] + mi := &file_v1_run_function_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -941,7 +1262,7 @@ func (x *SchemaSelector) ProtoReflect() protoreflect.Message { // Deprecated: Use SchemaSelector.ProtoReflect.Descriptor instead. func (*SchemaSelector) Descriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{7} + return file_v1_run_function_proto_rawDescGZIP(), []int{10} } func (x *SchemaSelector) GetApiVersion() string { @@ -971,7 +1292,7 @@ type Schema struct { func (x *Schema) Reset() { *x = Schema{} - mi := &file_v1_run_function_proto_msgTypes[8] + mi := &file_v1_run_function_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -983,7 +1304,7 @@ func (x *Schema) String() string { func (*Schema) ProtoMessage() {} func (x *Schema) ProtoReflect() protoreflect.Message { - mi := &file_v1_run_function_proto_msgTypes[8] + mi := &file_v1_run_function_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -996,7 +1317,7 @@ func (x *Schema) ProtoReflect() protoreflect.Message { // Deprecated: Use Schema.ProtoReflect.Descriptor instead. func (*Schema) Descriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{8} + return file_v1_run_function_proto_rawDescGZIP(), []int{11} } func (x *Schema) GetOpenapiV3() *structpb.Struct { @@ -1030,7 +1351,7 @@ type ResourceSelector struct { func (x *ResourceSelector) Reset() { *x = ResourceSelector{} - mi := &file_v1_run_function_proto_msgTypes[9] + mi := &file_v1_run_function_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1042,7 +1363,7 @@ func (x *ResourceSelector) String() string { func (*ResourceSelector) ProtoMessage() {} func (x *ResourceSelector) ProtoReflect() protoreflect.Message { - mi := &file_v1_run_function_proto_msgTypes[9] + mi := &file_v1_run_function_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1055,7 +1376,7 @@ func (x *ResourceSelector) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceSelector.ProtoReflect.Descriptor instead. func (*ResourceSelector) Descriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{9} + return file_v1_run_function_proto_rawDescGZIP(), []int{12} } func (x *ResourceSelector) GetApiVersion() string { @@ -1132,7 +1453,7 @@ type MatchLabels struct { func (x *MatchLabels) Reset() { *x = MatchLabels{} - mi := &file_v1_run_function_proto_msgTypes[10] + mi := &file_v1_run_function_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1144,7 +1465,7 @@ func (x *MatchLabels) String() string { func (*MatchLabels) ProtoMessage() {} func (x *MatchLabels) ProtoReflect() protoreflect.Message { - mi := &file_v1_run_function_proto_msgTypes[10] + mi := &file_v1_run_function_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1157,7 +1478,7 @@ func (x *MatchLabels) ProtoReflect() protoreflect.Message { // Deprecated: Use MatchLabels.ProtoReflect.Descriptor instead. func (*MatchLabels) Descriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{10} + return file_v1_run_function_proto_rawDescGZIP(), []int{13} } func (x *MatchLabels) GetLabels() map[string]string { @@ -1183,7 +1504,7 @@ type ResponseMeta struct { func (x *ResponseMeta) Reset() { *x = ResponseMeta{} - mi := &file_v1_run_function_proto_msgTypes[11] + mi := &file_v1_run_function_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1195,7 +1516,7 @@ func (x *ResponseMeta) String() string { func (*ResponseMeta) ProtoMessage() {} func (x *ResponseMeta) ProtoReflect() protoreflect.Message { - mi := &file_v1_run_function_proto_msgTypes[11] + mi := &file_v1_run_function_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1208,7 +1529,7 @@ func (x *ResponseMeta) ProtoReflect() protoreflect.Message { // Deprecated: Use ResponseMeta.ProtoReflect.Descriptor instead. func (*ResponseMeta) Descriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{11} + return file_v1_run_function_proto_rawDescGZIP(), []int{14} } func (x *ResponseMeta) GetTag() string { @@ -1240,7 +1561,7 @@ type State struct { func (x *State) Reset() { *x = State{} - mi := &file_v1_run_function_proto_msgTypes[12] + mi := &file_v1_run_function_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1252,7 +1573,7 @@ func (x *State) String() string { func (*State) ProtoMessage() {} func (x *State) ProtoReflect() protoreflect.Message { - mi := &file_v1_run_function_proto_msgTypes[12] + mi := &file_v1_run_function_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1265,7 +1586,7 @@ func (x *State) ProtoReflect() protoreflect.Message { // Deprecated: Use State.ProtoReflect.Descriptor instead. func (*State) Descriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{12} + return file_v1_run_function_proto_rawDescGZIP(), []int{15} } func (x *State) GetComposite() *Resource { @@ -1336,7 +1657,7 @@ type Resource struct { func (x *Resource) Reset() { *x = Resource{} - mi := &file_v1_run_function_proto_msgTypes[13] + mi := &file_v1_run_function_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1348,7 +1669,7 @@ func (x *Resource) String() string { func (*Resource) ProtoMessage() {} func (x *Resource) ProtoReflect() protoreflect.Message { - mi := &file_v1_run_function_proto_msgTypes[13] + mi := &file_v1_run_function_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1361,7 +1682,7 @@ func (x *Resource) ProtoReflect() protoreflect.Message { // Deprecated: Use Resource.ProtoReflect.Descriptor instead. func (*Resource) Descriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{13} + return file_v1_run_function_proto_rawDescGZIP(), []int{16} } func (x *Resource) GetResource() *structpb.Struct { @@ -1403,7 +1724,7 @@ type Result struct { func (x *Result) Reset() { *x = Result{} - mi := &file_v1_run_function_proto_msgTypes[14] + mi := &file_v1_run_function_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1415,7 +1736,7 @@ func (x *Result) String() string { func (*Result) ProtoMessage() {} func (x *Result) ProtoReflect() protoreflect.Message { - mi := &file_v1_run_function_proto_msgTypes[14] + mi := &file_v1_run_function_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1428,7 +1749,7 @@ func (x *Result) ProtoReflect() protoreflect.Message { // Deprecated: Use Result.ProtoReflect.Descriptor instead. func (*Result) Descriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{14} + return file_v1_run_function_proto_rawDescGZIP(), []int{17} } func (x *Result) GetSeverity() Severity { @@ -1486,7 +1807,7 @@ type Condition struct { func (x *Condition) Reset() { *x = Condition{} - mi := &file_v1_run_function_proto_msgTypes[15] + mi := &file_v1_run_function_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1498,7 +1819,7 @@ func (x *Condition) String() string { func (*Condition) ProtoMessage() {} func (x *Condition) ProtoReflect() protoreflect.Message { - mi := &file_v1_run_function_proto_msgTypes[15] + mi := &file_v1_run_function_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1511,7 +1832,7 @@ func (x *Condition) ProtoReflect() protoreflect.Message { // Deprecated: Use Condition.ProtoReflect.Descriptor instead. func (*Condition) Descriptor() ([]byte, []int) { - return file_v1_run_function_proto_rawDescGZIP(), []int{15} + return file_v1_run_function_proto_rawDescGZIP(), []int{18} } func (x *Condition) GetType() string { @@ -1553,7 +1874,7 @@ var File_v1_run_function_proto protoreflect.FileDescriptor const file_v1_run_function_proto_rawDesc = "" + "\n" + - "\x15v1/run_function.proto\x12\x19apiextensions.fn.proto.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\"\xa6\t\n" + + "\x15v1/run_function.proto\x12\x19apiextensions.fn.proto.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\"\xf3\t\n" + "\x12RunFunctionRequest\x12:\n" + "\x04meta\x18\x01 \x01(\v2&.apiextensions.fn.proto.v1.RequestMetaR\x04meta\x12<\n" + "\bobserved\x18\x02 \x01(\v2 .apiextensions.fn.proto.v1.StateR\bobserved\x12:\n" + @@ -1563,7 +1884,9 @@ const file_v1_run_function_proto_rawDesc = "" + "\x0fextra_resources\x18\x06 \x03(\v2A.apiextensions.fn.proto.v1.RunFunctionRequest.ExtraResourcesEntryB\x02\x18\x01R\x0eextraResources\x12`\n" + "\vcredentials\x18\a \x03(\v2>.apiextensions.fn.proto.v1.RunFunctionRequest.CredentialsEntryR\vcredentials\x12s\n" + "\x12required_resources\x18\b \x03(\v2D.apiextensions.fn.proto.v1.RunFunctionRequest.RequiredResourcesEntryR\x11requiredResources\x12m\n" + - "\x10required_schemas\x18\t \x03(\v2B.apiextensions.fn.proto.v1.RunFunctionRequest.RequiredSchemasEntryR\x0frequiredSchemas\x1ag\n" + + "\x10required_schemas\x18\t \x03(\v2B.apiextensions.fn.proto.v1.RunFunctionRequest.RequiredSchemasEntryR\x0frequiredSchemas\x12K\n" + + "\fdependencies\x18\n" + + " \x01(\v2'.apiextensions.fn.proto.v1.DependenciesR\fdependencies\x1ag\n" + "\x13ExtraResourcesEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12:\n" + "\x05value\x18\x02 \x01(\v2$.apiextensions.fn.proto.v1.ResourcesR\x05value:\x028\x01\x1af\n" + @@ -1588,7 +1911,24 @@ const file_v1_run_function_proto_rawDesc = "" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\fR\x05value:\x028\x01\"F\n" + "\tResources\x129\n" + - "\x05items\x18\x01 \x03(\v2#.apiextensions.fn.proto.v1.ResourceR\x05items\"\xe3\x03\n" + + "\x05items\x18\x01 \x03(\v2#.apiextensions.fn.proto.v1.ResourceR\x05items\"K\n" + + "\fDependencies\x12;\n" + + "\x05items\x18\x01 \x03(\v2%.apiextensions.fn.proto.v1.DependencyR\x05items\"\x99\x02\n" + + "\n" + + "Dependency\x12\x1a\n" + + "\bresource\x18\x01 \x01(\tR\bresource\x12-\n" + + "\x11composed_resource\x18\x02 \x01(\tH\x00R\x10composedResource\x12d\n" + + "\x11required_resource\x18\x04 \x01(\v25.apiextensions.fn.proto.v1.RequiredResourceDependencyH\x00R\x10requiredResource\x12L\n" + + "\tlifecycle\x18\x03 \x01(\x0e2..apiextensions.fn.proto.v1.DependencyLifecycleR\tlifecycleB\f\n" + + "\n" + + "depends_on\"\x9a\x01\n" + + "\x1aRequiredResourceDependency\x12)\n" + + "\x10requirement_name\x18\x01 \x01(\tR\x0frequirementName\x12\x17\n" + + "\x04name\x18\x02 \x01(\tH\x00R\x04name\x88\x01\x01\x12!\n" + + "\tnamespace\x18\x03 \x01(\tH\x01R\tnamespace\x88\x01\x01B\a\n" + + "\x05_nameB\f\n" + + "\n" + + "_namespace\"\xb0\x04\n" + "\x13RunFunctionResponse\x12;\n" + "\x04meta\x18\x01 \x01(\v2'.apiextensions.fn.proto.v1.ResponseMetaR\x04meta\x12:\n" + "\adesired\x18\x02 \x01(\v2 .apiextensions.fn.proto.v1.StateR\adesired\x12;\n" + @@ -1598,7 +1938,8 @@ const file_v1_run_function_proto_rawDesc = "" + "\n" + "conditions\x18\x06 \x03(\v2$.apiextensions.fn.proto.v1.ConditionR\n" + "conditions\x124\n" + - "\x06output\x18\a \x01(\v2\x17.google.protobuf.StructH\x01R\x06output\x88\x01\x01B\n" + + "\x06output\x18\a \x01(\v2\x17.google.protobuf.StructH\x01R\x06output\x88\x01\x01\x12K\n" + + "\fdependencies\x18\b \x01(\v2'.apiextensions.fn.proto.v1.DependenciesR\fdependenciesB\n" + "\n" + "\b_contextB\t\n" + "\a_output\"j\n" + @@ -1674,7 +2015,10 @@ const file_v1_run_function_proto_rawDesc = "" + "\x06target\x18\x05 \x01(\x0e2!.apiextensions.fn.proto.v1.TargetH\x01R\x06target\x88\x01\x01B\n" + "\n" + "\b_messageB\t\n" + - "\a_target*\xc0\x01\n" + + "\a_target*k\n" + + "\x13DependencyLifecycle\x12$\n" + + " DEPENDENCY_LIFECYCLE_UNSPECIFIED\x10\x00\x12.\n" + + "*DEPENDENCY_LIFECYCLE_CREATE_BEFORE_DESTROY\x10\x01*\xdd\x01\n" + "\n" + "Capability\x12\x1a\n" + "\x16CAPABILITY_UNSPECIFIED\x10\x00\x12\x1b\n" + @@ -1682,7 +2026,8 @@ const file_v1_run_function_proto_rawDesc = "" + "\x1dCAPABILITY_REQUIRED_RESOURCES\x10\x02\x12\x1a\n" + "\x16CAPABILITY_CREDENTIALS\x10\x03\x12\x19\n" + "\x15CAPABILITY_CONDITIONS\x10\x04\x12\x1f\n" + - "\x1bCAPABILITY_REQUIRED_SCHEMAS\x10\x05*?\n" + + "\x1bCAPABILITY_REQUIRED_SCHEMAS\x10\x05\x12\x1b\n" + + "\x17CAPABILITY_DEPENDENCIES\x10\x06*?\n" + "\x05Ready\x12\x15\n" + "\x11READY_UNSPECIFIED\x10\x00\x12\x0e\n" + "\n" + @@ -1717,96 +2062,105 @@ func file_v1_run_function_proto_rawDescGZIP() []byte { return file_v1_run_function_proto_rawDescData } -var file_v1_run_function_proto_enumTypes = make([]protoimpl.EnumInfo, 5) -var file_v1_run_function_proto_msgTypes = make([]protoimpl.MessageInfo, 27) +var file_v1_run_function_proto_enumTypes = make([]protoimpl.EnumInfo, 6) +var file_v1_run_function_proto_msgTypes = make([]protoimpl.MessageInfo, 30) var file_v1_run_function_proto_goTypes = []any{ - (Capability)(0), // 0: apiextensions.fn.proto.v1.Capability - (Ready)(0), // 1: apiextensions.fn.proto.v1.Ready - (Severity)(0), // 2: apiextensions.fn.proto.v1.Severity - (Target)(0), // 3: apiextensions.fn.proto.v1.Target - (Status)(0), // 4: apiextensions.fn.proto.v1.Status - (*RunFunctionRequest)(nil), // 5: apiextensions.fn.proto.v1.RunFunctionRequest - (*Credentials)(nil), // 6: apiextensions.fn.proto.v1.Credentials - (*CredentialData)(nil), // 7: apiextensions.fn.proto.v1.CredentialData - (*Resources)(nil), // 8: apiextensions.fn.proto.v1.Resources - (*RunFunctionResponse)(nil), // 9: apiextensions.fn.proto.v1.RunFunctionResponse - (*RequestMeta)(nil), // 10: apiextensions.fn.proto.v1.RequestMeta - (*Requirements)(nil), // 11: apiextensions.fn.proto.v1.Requirements - (*SchemaSelector)(nil), // 12: apiextensions.fn.proto.v1.SchemaSelector - (*Schema)(nil), // 13: apiextensions.fn.proto.v1.Schema - (*ResourceSelector)(nil), // 14: apiextensions.fn.proto.v1.ResourceSelector - (*MatchLabels)(nil), // 15: apiextensions.fn.proto.v1.MatchLabels - (*ResponseMeta)(nil), // 16: apiextensions.fn.proto.v1.ResponseMeta - (*State)(nil), // 17: apiextensions.fn.proto.v1.State - (*Resource)(nil), // 18: apiextensions.fn.proto.v1.Resource - (*Result)(nil), // 19: apiextensions.fn.proto.v1.Result - (*Condition)(nil), // 20: apiextensions.fn.proto.v1.Condition - nil, // 21: apiextensions.fn.proto.v1.RunFunctionRequest.ExtraResourcesEntry - nil, // 22: apiextensions.fn.proto.v1.RunFunctionRequest.CredentialsEntry - nil, // 23: apiextensions.fn.proto.v1.RunFunctionRequest.RequiredResourcesEntry - nil, // 24: apiextensions.fn.proto.v1.RunFunctionRequest.RequiredSchemasEntry - nil, // 25: apiextensions.fn.proto.v1.CredentialData.DataEntry - nil, // 26: apiextensions.fn.proto.v1.Requirements.ExtraResourcesEntry - nil, // 27: apiextensions.fn.proto.v1.Requirements.ResourcesEntry - nil, // 28: apiextensions.fn.proto.v1.Requirements.SchemasEntry - nil, // 29: apiextensions.fn.proto.v1.MatchLabels.LabelsEntry - nil, // 30: apiextensions.fn.proto.v1.State.ResourcesEntry - nil, // 31: apiextensions.fn.proto.v1.Resource.ConnectionDetailsEntry - (*structpb.Struct)(nil), // 32: google.protobuf.Struct - (*durationpb.Duration)(nil), // 33: google.protobuf.Duration + (DependencyLifecycle)(0), // 0: apiextensions.fn.proto.v1.DependencyLifecycle + (Capability)(0), // 1: apiextensions.fn.proto.v1.Capability + (Ready)(0), // 2: apiextensions.fn.proto.v1.Ready + (Severity)(0), // 3: apiextensions.fn.proto.v1.Severity + (Target)(0), // 4: apiextensions.fn.proto.v1.Target + (Status)(0), // 5: apiextensions.fn.proto.v1.Status + (*RunFunctionRequest)(nil), // 6: apiextensions.fn.proto.v1.RunFunctionRequest + (*Credentials)(nil), // 7: apiextensions.fn.proto.v1.Credentials + (*CredentialData)(nil), // 8: apiextensions.fn.proto.v1.CredentialData + (*Resources)(nil), // 9: apiextensions.fn.proto.v1.Resources + (*Dependencies)(nil), // 10: apiextensions.fn.proto.v1.Dependencies + (*Dependency)(nil), // 11: apiextensions.fn.proto.v1.Dependency + (*RequiredResourceDependency)(nil), // 12: apiextensions.fn.proto.v1.RequiredResourceDependency + (*RunFunctionResponse)(nil), // 13: apiextensions.fn.proto.v1.RunFunctionResponse + (*RequestMeta)(nil), // 14: apiextensions.fn.proto.v1.RequestMeta + (*Requirements)(nil), // 15: apiextensions.fn.proto.v1.Requirements + (*SchemaSelector)(nil), // 16: apiextensions.fn.proto.v1.SchemaSelector + (*Schema)(nil), // 17: apiextensions.fn.proto.v1.Schema + (*ResourceSelector)(nil), // 18: apiextensions.fn.proto.v1.ResourceSelector + (*MatchLabels)(nil), // 19: apiextensions.fn.proto.v1.MatchLabels + (*ResponseMeta)(nil), // 20: apiextensions.fn.proto.v1.ResponseMeta + (*State)(nil), // 21: apiextensions.fn.proto.v1.State + (*Resource)(nil), // 22: apiextensions.fn.proto.v1.Resource + (*Result)(nil), // 23: apiextensions.fn.proto.v1.Result + (*Condition)(nil), // 24: apiextensions.fn.proto.v1.Condition + nil, // 25: apiextensions.fn.proto.v1.RunFunctionRequest.ExtraResourcesEntry + nil, // 26: apiextensions.fn.proto.v1.RunFunctionRequest.CredentialsEntry + nil, // 27: apiextensions.fn.proto.v1.RunFunctionRequest.RequiredResourcesEntry + nil, // 28: apiextensions.fn.proto.v1.RunFunctionRequest.RequiredSchemasEntry + nil, // 29: apiextensions.fn.proto.v1.CredentialData.DataEntry + nil, // 30: apiextensions.fn.proto.v1.Requirements.ExtraResourcesEntry + nil, // 31: apiextensions.fn.proto.v1.Requirements.ResourcesEntry + nil, // 32: apiextensions.fn.proto.v1.Requirements.SchemasEntry + nil, // 33: apiextensions.fn.proto.v1.MatchLabels.LabelsEntry + nil, // 34: apiextensions.fn.proto.v1.State.ResourcesEntry + nil, // 35: apiextensions.fn.proto.v1.Resource.ConnectionDetailsEntry + (*structpb.Struct)(nil), // 36: google.protobuf.Struct + (*durationpb.Duration)(nil), // 37: google.protobuf.Duration } var file_v1_run_function_proto_depIdxs = []int32{ - 10, // 0: apiextensions.fn.proto.v1.RunFunctionRequest.meta:type_name -> apiextensions.fn.proto.v1.RequestMeta - 17, // 1: apiextensions.fn.proto.v1.RunFunctionRequest.observed:type_name -> apiextensions.fn.proto.v1.State - 17, // 2: apiextensions.fn.proto.v1.RunFunctionRequest.desired:type_name -> apiextensions.fn.proto.v1.State - 32, // 3: apiextensions.fn.proto.v1.RunFunctionRequest.input:type_name -> google.protobuf.Struct - 32, // 4: apiextensions.fn.proto.v1.RunFunctionRequest.context:type_name -> google.protobuf.Struct - 21, // 5: apiextensions.fn.proto.v1.RunFunctionRequest.extra_resources:type_name -> apiextensions.fn.proto.v1.RunFunctionRequest.ExtraResourcesEntry - 22, // 6: apiextensions.fn.proto.v1.RunFunctionRequest.credentials:type_name -> apiextensions.fn.proto.v1.RunFunctionRequest.CredentialsEntry - 23, // 7: apiextensions.fn.proto.v1.RunFunctionRequest.required_resources:type_name -> apiextensions.fn.proto.v1.RunFunctionRequest.RequiredResourcesEntry - 24, // 8: apiextensions.fn.proto.v1.RunFunctionRequest.required_schemas:type_name -> apiextensions.fn.proto.v1.RunFunctionRequest.RequiredSchemasEntry - 7, // 9: apiextensions.fn.proto.v1.Credentials.credential_data:type_name -> apiextensions.fn.proto.v1.CredentialData - 25, // 10: apiextensions.fn.proto.v1.CredentialData.data:type_name -> apiextensions.fn.proto.v1.CredentialData.DataEntry - 18, // 11: apiextensions.fn.proto.v1.Resources.items:type_name -> apiextensions.fn.proto.v1.Resource - 16, // 12: apiextensions.fn.proto.v1.RunFunctionResponse.meta:type_name -> apiextensions.fn.proto.v1.ResponseMeta - 17, // 13: apiextensions.fn.proto.v1.RunFunctionResponse.desired:type_name -> apiextensions.fn.proto.v1.State - 19, // 14: apiextensions.fn.proto.v1.RunFunctionResponse.results:type_name -> apiextensions.fn.proto.v1.Result - 32, // 15: apiextensions.fn.proto.v1.RunFunctionResponse.context:type_name -> google.protobuf.Struct - 11, // 16: apiextensions.fn.proto.v1.RunFunctionResponse.requirements:type_name -> apiextensions.fn.proto.v1.Requirements - 20, // 17: apiextensions.fn.proto.v1.RunFunctionResponse.conditions:type_name -> apiextensions.fn.proto.v1.Condition - 32, // 18: apiextensions.fn.proto.v1.RunFunctionResponse.output:type_name -> google.protobuf.Struct - 0, // 19: apiextensions.fn.proto.v1.RequestMeta.capabilities:type_name -> apiextensions.fn.proto.v1.Capability - 26, // 20: apiextensions.fn.proto.v1.Requirements.extra_resources:type_name -> apiextensions.fn.proto.v1.Requirements.ExtraResourcesEntry - 27, // 21: apiextensions.fn.proto.v1.Requirements.resources:type_name -> apiextensions.fn.proto.v1.Requirements.ResourcesEntry - 28, // 22: apiextensions.fn.proto.v1.Requirements.schemas:type_name -> apiextensions.fn.proto.v1.Requirements.SchemasEntry - 32, // 23: apiextensions.fn.proto.v1.Schema.openapi_v3:type_name -> google.protobuf.Struct - 15, // 24: apiextensions.fn.proto.v1.ResourceSelector.match_labels:type_name -> apiextensions.fn.proto.v1.MatchLabels - 29, // 25: apiextensions.fn.proto.v1.MatchLabels.labels:type_name -> apiextensions.fn.proto.v1.MatchLabels.LabelsEntry - 33, // 26: apiextensions.fn.proto.v1.ResponseMeta.ttl:type_name -> google.protobuf.Duration - 18, // 27: apiextensions.fn.proto.v1.State.composite:type_name -> apiextensions.fn.proto.v1.Resource - 30, // 28: apiextensions.fn.proto.v1.State.resources:type_name -> apiextensions.fn.proto.v1.State.ResourcesEntry - 32, // 29: apiextensions.fn.proto.v1.Resource.resource:type_name -> google.protobuf.Struct - 31, // 30: apiextensions.fn.proto.v1.Resource.connection_details:type_name -> apiextensions.fn.proto.v1.Resource.ConnectionDetailsEntry - 1, // 31: apiextensions.fn.proto.v1.Resource.ready:type_name -> apiextensions.fn.proto.v1.Ready - 2, // 32: apiextensions.fn.proto.v1.Result.severity:type_name -> apiextensions.fn.proto.v1.Severity - 3, // 33: apiextensions.fn.proto.v1.Result.target:type_name -> apiextensions.fn.proto.v1.Target - 4, // 34: apiextensions.fn.proto.v1.Condition.status:type_name -> apiextensions.fn.proto.v1.Status - 3, // 35: apiextensions.fn.proto.v1.Condition.target:type_name -> apiextensions.fn.proto.v1.Target - 8, // 36: apiextensions.fn.proto.v1.RunFunctionRequest.ExtraResourcesEntry.value:type_name -> apiextensions.fn.proto.v1.Resources - 6, // 37: apiextensions.fn.proto.v1.RunFunctionRequest.CredentialsEntry.value:type_name -> apiextensions.fn.proto.v1.Credentials - 8, // 38: apiextensions.fn.proto.v1.RunFunctionRequest.RequiredResourcesEntry.value:type_name -> apiextensions.fn.proto.v1.Resources - 13, // 39: apiextensions.fn.proto.v1.RunFunctionRequest.RequiredSchemasEntry.value:type_name -> apiextensions.fn.proto.v1.Schema - 14, // 40: apiextensions.fn.proto.v1.Requirements.ExtraResourcesEntry.value:type_name -> apiextensions.fn.proto.v1.ResourceSelector - 14, // 41: apiextensions.fn.proto.v1.Requirements.ResourcesEntry.value:type_name -> apiextensions.fn.proto.v1.ResourceSelector - 12, // 42: apiextensions.fn.proto.v1.Requirements.SchemasEntry.value:type_name -> apiextensions.fn.proto.v1.SchemaSelector - 18, // 43: apiextensions.fn.proto.v1.State.ResourcesEntry.value:type_name -> apiextensions.fn.proto.v1.Resource - 5, // 44: apiextensions.fn.proto.v1.FunctionRunnerService.RunFunction:input_type -> apiextensions.fn.proto.v1.RunFunctionRequest - 9, // 45: apiextensions.fn.proto.v1.FunctionRunnerService.RunFunction:output_type -> apiextensions.fn.proto.v1.RunFunctionResponse - 45, // [45:46] is the sub-list for method output_type - 44, // [44:45] is the sub-list for method input_type - 44, // [44:44] is the sub-list for extension type_name - 44, // [44:44] is the sub-list for extension extendee - 0, // [0:44] is the sub-list for field type_name + 14, // 0: apiextensions.fn.proto.v1.RunFunctionRequest.meta:type_name -> apiextensions.fn.proto.v1.RequestMeta + 21, // 1: apiextensions.fn.proto.v1.RunFunctionRequest.observed:type_name -> apiextensions.fn.proto.v1.State + 21, // 2: apiextensions.fn.proto.v1.RunFunctionRequest.desired:type_name -> apiextensions.fn.proto.v1.State + 36, // 3: apiextensions.fn.proto.v1.RunFunctionRequest.input:type_name -> google.protobuf.Struct + 36, // 4: apiextensions.fn.proto.v1.RunFunctionRequest.context:type_name -> google.protobuf.Struct + 25, // 5: apiextensions.fn.proto.v1.RunFunctionRequest.extra_resources:type_name -> apiextensions.fn.proto.v1.RunFunctionRequest.ExtraResourcesEntry + 26, // 6: apiextensions.fn.proto.v1.RunFunctionRequest.credentials:type_name -> apiextensions.fn.proto.v1.RunFunctionRequest.CredentialsEntry + 27, // 7: apiextensions.fn.proto.v1.RunFunctionRequest.required_resources:type_name -> apiextensions.fn.proto.v1.RunFunctionRequest.RequiredResourcesEntry + 28, // 8: apiextensions.fn.proto.v1.RunFunctionRequest.required_schemas:type_name -> apiextensions.fn.proto.v1.RunFunctionRequest.RequiredSchemasEntry + 10, // 9: apiextensions.fn.proto.v1.RunFunctionRequest.dependencies:type_name -> apiextensions.fn.proto.v1.Dependencies + 8, // 10: apiextensions.fn.proto.v1.Credentials.credential_data:type_name -> apiextensions.fn.proto.v1.CredentialData + 29, // 11: apiextensions.fn.proto.v1.CredentialData.data:type_name -> apiextensions.fn.proto.v1.CredentialData.DataEntry + 22, // 12: apiextensions.fn.proto.v1.Resources.items:type_name -> apiextensions.fn.proto.v1.Resource + 11, // 13: apiextensions.fn.proto.v1.Dependencies.items:type_name -> apiextensions.fn.proto.v1.Dependency + 12, // 14: apiextensions.fn.proto.v1.Dependency.required_resource:type_name -> apiextensions.fn.proto.v1.RequiredResourceDependency + 0, // 15: apiextensions.fn.proto.v1.Dependency.lifecycle:type_name -> apiextensions.fn.proto.v1.DependencyLifecycle + 20, // 16: apiextensions.fn.proto.v1.RunFunctionResponse.meta:type_name -> apiextensions.fn.proto.v1.ResponseMeta + 21, // 17: apiextensions.fn.proto.v1.RunFunctionResponse.desired:type_name -> apiextensions.fn.proto.v1.State + 23, // 18: apiextensions.fn.proto.v1.RunFunctionResponse.results:type_name -> apiextensions.fn.proto.v1.Result + 36, // 19: apiextensions.fn.proto.v1.RunFunctionResponse.context:type_name -> google.protobuf.Struct + 15, // 20: apiextensions.fn.proto.v1.RunFunctionResponse.requirements:type_name -> apiextensions.fn.proto.v1.Requirements + 24, // 21: apiextensions.fn.proto.v1.RunFunctionResponse.conditions:type_name -> apiextensions.fn.proto.v1.Condition + 36, // 22: apiextensions.fn.proto.v1.RunFunctionResponse.output:type_name -> google.protobuf.Struct + 10, // 23: apiextensions.fn.proto.v1.RunFunctionResponse.dependencies:type_name -> apiextensions.fn.proto.v1.Dependencies + 1, // 24: apiextensions.fn.proto.v1.RequestMeta.capabilities:type_name -> apiextensions.fn.proto.v1.Capability + 30, // 25: apiextensions.fn.proto.v1.Requirements.extra_resources:type_name -> apiextensions.fn.proto.v1.Requirements.ExtraResourcesEntry + 31, // 26: apiextensions.fn.proto.v1.Requirements.resources:type_name -> apiextensions.fn.proto.v1.Requirements.ResourcesEntry + 32, // 27: apiextensions.fn.proto.v1.Requirements.schemas:type_name -> apiextensions.fn.proto.v1.Requirements.SchemasEntry + 36, // 28: apiextensions.fn.proto.v1.Schema.openapi_v3:type_name -> google.protobuf.Struct + 19, // 29: apiextensions.fn.proto.v1.ResourceSelector.match_labels:type_name -> apiextensions.fn.proto.v1.MatchLabels + 33, // 30: apiextensions.fn.proto.v1.MatchLabels.labels:type_name -> apiextensions.fn.proto.v1.MatchLabels.LabelsEntry + 37, // 31: apiextensions.fn.proto.v1.ResponseMeta.ttl:type_name -> google.protobuf.Duration + 22, // 32: apiextensions.fn.proto.v1.State.composite:type_name -> apiextensions.fn.proto.v1.Resource + 34, // 33: apiextensions.fn.proto.v1.State.resources:type_name -> apiextensions.fn.proto.v1.State.ResourcesEntry + 36, // 34: apiextensions.fn.proto.v1.Resource.resource:type_name -> google.protobuf.Struct + 35, // 35: apiextensions.fn.proto.v1.Resource.connection_details:type_name -> apiextensions.fn.proto.v1.Resource.ConnectionDetailsEntry + 2, // 36: apiextensions.fn.proto.v1.Resource.ready:type_name -> apiextensions.fn.proto.v1.Ready + 3, // 37: apiextensions.fn.proto.v1.Result.severity:type_name -> apiextensions.fn.proto.v1.Severity + 4, // 38: apiextensions.fn.proto.v1.Result.target:type_name -> apiextensions.fn.proto.v1.Target + 5, // 39: apiextensions.fn.proto.v1.Condition.status:type_name -> apiextensions.fn.proto.v1.Status + 4, // 40: apiextensions.fn.proto.v1.Condition.target:type_name -> apiextensions.fn.proto.v1.Target + 9, // 41: apiextensions.fn.proto.v1.RunFunctionRequest.ExtraResourcesEntry.value:type_name -> apiextensions.fn.proto.v1.Resources + 7, // 42: apiextensions.fn.proto.v1.RunFunctionRequest.CredentialsEntry.value:type_name -> apiextensions.fn.proto.v1.Credentials + 9, // 43: apiextensions.fn.proto.v1.RunFunctionRequest.RequiredResourcesEntry.value:type_name -> apiextensions.fn.proto.v1.Resources + 17, // 44: apiextensions.fn.proto.v1.RunFunctionRequest.RequiredSchemasEntry.value:type_name -> apiextensions.fn.proto.v1.Schema + 18, // 45: apiextensions.fn.proto.v1.Requirements.ExtraResourcesEntry.value:type_name -> apiextensions.fn.proto.v1.ResourceSelector + 18, // 46: apiextensions.fn.proto.v1.Requirements.ResourcesEntry.value:type_name -> apiextensions.fn.proto.v1.ResourceSelector + 16, // 47: apiextensions.fn.proto.v1.Requirements.SchemasEntry.value:type_name -> apiextensions.fn.proto.v1.SchemaSelector + 22, // 48: apiextensions.fn.proto.v1.State.ResourcesEntry.value:type_name -> apiextensions.fn.proto.v1.Resource + 6, // 49: apiextensions.fn.proto.v1.FunctionRunnerService.RunFunction:input_type -> apiextensions.fn.proto.v1.RunFunctionRequest + 13, // 50: apiextensions.fn.proto.v1.FunctionRunnerService.RunFunction:output_type -> apiextensions.fn.proto.v1.RunFunctionResponse + 50, // [50:51] is the sub-list for method output_type + 49, // [49:50] is the sub-list for method input_type + 49, // [49:49] is the sub-list for extension type_name + 49, // [49:49] is the sub-list for extension extendee + 0, // [0:49] is the sub-list for field type_name } func init() { file_v1_run_function_proto_init() } @@ -1818,22 +2172,27 @@ func file_v1_run_function_proto_init() { file_v1_run_function_proto_msgTypes[1].OneofWrappers = []any{ (*Credentials_CredentialData)(nil), } - file_v1_run_function_proto_msgTypes[4].OneofWrappers = []any{} - file_v1_run_function_proto_msgTypes[8].OneofWrappers = []any{} - file_v1_run_function_proto_msgTypes[9].OneofWrappers = []any{ + file_v1_run_function_proto_msgTypes[5].OneofWrappers = []any{ + (*Dependency_ComposedResource)(nil), + (*Dependency_RequiredResource)(nil), + } + file_v1_run_function_proto_msgTypes[6].OneofWrappers = []any{} + file_v1_run_function_proto_msgTypes[7].OneofWrappers = []any{} + file_v1_run_function_proto_msgTypes[11].OneofWrappers = []any{} + file_v1_run_function_proto_msgTypes[12].OneofWrappers = []any{ (*ResourceSelector_MatchName)(nil), (*ResourceSelector_MatchLabels)(nil), } - file_v1_run_function_proto_msgTypes[11].OneofWrappers = []any{} file_v1_run_function_proto_msgTypes[14].OneofWrappers = []any{} - file_v1_run_function_proto_msgTypes[15].OneofWrappers = []any{} + file_v1_run_function_proto_msgTypes[17].OneofWrappers = []any{} + file_v1_run_function_proto_msgTypes[18].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_v1_run_function_proto_rawDesc), len(file_v1_run_function_proto_rawDesc)), - NumEnums: 5, - NumMessages: 27, + NumEnums: 6, + NumMessages: 30, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/v1/run_function.proto b/proto/v1/run_function.proto index e58a4b5..25bf397 100644 --- a/proto/v1/run_function.proto +++ b/proto/v1/run_function.proto @@ -97,6 +97,10 @@ message RunFunctionRequest { // sets the map key to an empty Schema message to indicate that it attempted // to satisfy the request. map required_schemas = 9; + + // Optional ordering constraints over composed resources, accumulated by the + // functions that ran before this one. + Dependencies dependencies = 10; } // Credentials that a function may use to communicate with an external system. @@ -118,6 +122,78 @@ message Resources { repeated Resource items = 1; } +// Dependencies is a set of ordering constraints over composed resources. +// +// This is a message wrapping a repeated field, rather than a bare repeated +// field, so that an unset value can be told apart from an empty one. A bare +// repeated field can't express that difference: proto3 has no presence for +// repeated fields, and an empty list and an unset field are both zero bytes on +// the wire. Crossplane relies on the difference, because an unset value means +// "this function has no opinion, carry my constraints forward" while an empty +// one means "this function wants no constraints at all." State wraps desired +// and observed resources for the same reason. +message Dependencies { + repeated Dependency items = 1; +} + +// A Dependency declares that one composed resource must be created after, and +// deleted before, another resource. It expresses ordering only. It doesn't move +// any data between resources. +message Dependency { + // Name of the composed resource that has the dependency. A key into the + // desired or observed State.resources map. + string resource = 1; + + // What the resource depends on. + oneof depends_on { + // Name of another composed resource. A key into the desired or observed + // State.resources map. + string composed_resource = 2; + + // A resource the pipeline required, rather than composed. Crossplane never + // deletes a resource it didn't compose, so these constrain only the order + // resources are created and updated. + RequiredResourceDependency required_resource = 4; + } + + // How this dependency constrains the order its endpoints are created and + // deleted. Defaults to symmetric ordering. + DependencyLifecycle lifecycle = 3; +} + +// A DependencyLifecycle is how a dependency constrains the order its endpoints +// are created and deleted. +enum DependencyLifecycle { + // Symmetric ordering: the resource is created only once what it depends on + // is ready, and what it depends on is deleted only once the resource is + // gone. This is the default, and the only valid value when depends_on is a + // required resource, because Crossplane never deletes a resource it didn't + // compose. + DEPENDENCY_LIFECYCLE_UNSPECIFIED = 0; + + // The resource may be created without waiting for what it depends on to be + // deleted. It must still exist and be ready before what it depends on is + // deleted. Use for a replacement that must exist before its predecessor is + // torn down. Only valid when depends_on is a composed resource. + DEPENDENCY_LIFECYCLE_CREATE_BEFORE_DESTROY = 1; +} + +// A RequiredResourceDependency identifies a resource the pipeline required, +// for the purpose of ordering. +message RequiredResourceDependency { + // The requirement name. A key into a RunFunctionRequest's required_resources + // map, and into a RunFunctionResponse's requirements.resources map. + string requirement_name = 1; + + // Optional name of a single resource within the set the requirement matched. + // If unset, every resource the requirement matched must be ready. + optional string name = 2; + + // Namespace of name for a namespaced resource. Leave unset for a + // cluster-scoped resource. name and namespace together identify one match. + optional string namespace = 3; +} + // A RunFunctionResponse contains the result of a function run. message RunFunctionResponse { // Metadata pertaining to this response. @@ -156,6 +232,14 @@ message RunFunctionResponse { // // Only Operations use function output. XRs will discard any function output. optional google.protobuf.Struct output = 7; + + // Optional ordering constraints over composed resources. A function that has + // an opinion about ordering must return the full set it wants going forward, + // including edges it received and still wants. Leaving this field unset means + // "no opinion" - Crossplane carries forward whatever it sent in the request. + // + // Dependencies are only used for composition. They're ignored by Operations. + Dependencies dependencies = 8; } // RequestMeta contains metadata pertaining to a RunFunctionRequest. @@ -201,6 +285,11 @@ enum Capability { // OpenAPI schemas and Crossplane will return them in required_schemas. Added // in Crossplane v2.2. CAPABILITY_REQUIRED_SCHEMAS = 5; + + // Crossplane supports the dependencies field. Functions can declare ordering + // constraints over composed resources, and Crossplane will sequence the + // resources it creates, updates and deletes accordingly. + CAPABILITY_DEPENDENCIES = 6; } // Requirements that must be satisfied for a function to run successfully. diff --git a/response/response.go b/response/response.go index 170d863..d5b04cb 100644 --- a/response/response.go +++ b/response/response.go @@ -22,6 +22,7 @@ import ( "time" "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/structpb" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -35,16 +36,96 @@ import ( const DefaultTTL = 1 * time.Minute // To bootstraps a response to the supplied request. It automatically copies the -// desired state from the request. +// desired state, context and dependencies from the request. +// +// Copying dependencies means a function that adds one keeps the ones earlier +// functions declared. They're copied only when the request has them: unset +// dependencies mean "no opinion", which Crossplane reads as "carry mine +// forward", while an empty set means "drop every constraint". func To(req *v1.RunFunctionRequest, ttl time.Duration) *v1.RunFunctionResponse { return &v1.RunFunctionResponse{ Meta: &v1.ResponseMeta{ Tag: req.GetMeta().GetTag(), Ttl: durationpb.New(ttl), }, - Desired: req.GetDesired(), - Context: req.GetContext(), + Desired: req.GetDesired(), + Context: req.GetContext(), + Dependencies: dependenciesOf(req), + } +} + +// dependenciesOf copies the request's dependencies, so adding one to the +// response doesn't also add it to the request. +func dependenciesOf(req *v1.RunFunctionRequest) *v1.Dependencies { + if req.GetDependencies() == nil { + return nil + } + d, _ := proto.Clone(req.GetDependencies()).(*v1.Dependencies) + return d +} + +// A DependencyOption configures a dependency added by AddDependency. +type DependencyOption func(d *v1.Dependency) + +// WithCreateBeforeDestroy lets a resource be created without waiting for what +// it depends on to be deleted. Use it for a replacement that must exist before +// its predecessor is torn down. +func WithCreateBeforeDestroy() DependencyOption { + return func(d *v1.Dependency) { + d.Lifecycle = v1.DependencyLifecycle_DEPENDENCY_LIFECYCLE_CREATE_BEFORE_DESTROY + } +} + +// AddDependency declares that one composed resource depends on another. +// +// By default ordering is symmetric: the resource is created only once what it +// depends on is ready, and what it depends on is deleted only once the +// resource is gone. Dependencies express ordering only; they don't move any +// data between resources. +// +// A function must return the full set of dependencies it wants. To copies +// forward the ones the request carried, so add to a response it created. +// +// Only a Crossplane that advertises CAPABILITY_DEPENDENCIES honors +// dependencies. Use request.HasCapability to check before relying on them. +func AddDependency(rsp *v1.RunFunctionResponse, r, dependsOn resource.Name, o ...DependencyOption) { + d := &v1.Dependency{ + Resource: string(r), + DependsOn: &v1.Dependency_ComposedResource{ComposedResource: string(dependsOn)}, + } + for _, fn := range o { + fn(d) + } + addDependency(rsp, d) +} + +// AddRequiredResourceDependency declares that a composed resource depends on +// a resource the function requires but doesn't compose. Set the dependency's +// name, and namespace for a namespaced resource, to depend on one of the +// resources the requirement matched; leave them unset to wait for all of them. +// +// Crossplane never deletes a resource it didn't compose, so this orders only +// creation and updates. +func AddRequiredResourceDependency(rsp *v1.RunFunctionResponse, r resource.Name, dependsOn *v1.RequiredResourceDependency) { + addDependency(rsp, &v1.Dependency{ + Resource: string(r), + DependsOn: &v1.Dependency_RequiredResource{RequiredResource: dependsOn}, + }) +} + +// ClearDependencies declares that no composed resources should be ordered. It +// drops the dependencies earlier functions declared, which To copied forward. +// That's different from leaving dependencies unset, which carries them +// forward. +func ClearDependencies(rsp *v1.RunFunctionResponse) { + rsp.Dependencies = &v1.Dependencies{} +} + +func addDependency(rsp *v1.RunFunctionResponse, d *v1.Dependency) { + if rsp.GetDependencies() == nil { + rsp.Dependencies = &v1.Dependencies{} } + rsp.Dependencies.Items = append(rsp.Dependencies.Items, d) } // SetContextKey sets context to the supplied key. diff --git a/response/response_test.go b/response/response_test.go index 2187072..42a1981 100644 --- a/response/response_test.go +++ b/response/response_test.go @@ -236,3 +236,134 @@ func MustUnstructJSON(j string) *unstructured.Unstructured { } return u } + +func TestToDependencies(t *testing.T) { + edge := &v1.Dependency{Resource: "subnet", DependsOn: &v1.Dependency_ComposedResource{ComposedResource: "vpc"}} + + cases := map[string]struct { + reason string + req *v1.RunFunctionRequest + want *v1.Dependencies + }{ + "Unset": { + reason: "Unset dependencies mean no opinion, so they must stay unset rather than become an empty set.", + req: &v1.RunFunctionRequest{}, + want: nil, + }, + "Empty": { + reason: "An empty set means drop every constraint, and must be carried as such.", + req: &v1.RunFunctionRequest{Dependencies: &v1.Dependencies{}}, + want: &v1.Dependencies{}, + }, + "Carried": { + reason: "A function should keep the dependencies earlier functions declared.", + req: &v1.RunFunctionRequest{Dependencies: &v1.Dependencies{Items: []*v1.Dependency{edge}}}, + want: &v1.Dependencies{Items: []*v1.Dependency{edge}}, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + got := To(tc.req, DefaultTTL).GetDependencies() + if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" { + t.Errorf("%s\nTo(...).Dependencies: -want, +got:\n%s", tc.reason, diff) + } + }) + } +} + +func TestToDoesNotShareDependencies(t *testing.T) { + req := &v1.RunFunctionRequest{Dependencies: &v1.Dependencies{}} + rsp := To(req, DefaultTTL) + AddDependency(rsp, "subnet", "vpc") + + if n := len(req.GetDependencies().GetItems()); n != 0 { + t.Errorf("adding a dependency to the response added %d to the request", n) + } +} + +func TestAddDependency(t *testing.T) { + type args struct { + rsp *v1.RunFunctionResponse + r resource.Name + dependsOn resource.Name + o []DependencyOption + } + cases := map[string]struct { + reason string + args args + want *v1.RunFunctionResponse + }{ + "FirstDependency": { + reason: "Adding to a response with no dependencies should create the set.", + args: args{rsp: &v1.RunFunctionResponse{}, r: "subnet", dependsOn: "vpc"}, + want: &v1.RunFunctionResponse{Dependencies: &v1.Dependencies{Items: []*v1.Dependency{ + {Resource: "subnet", DependsOn: &v1.Dependency_ComposedResource{ComposedResource: "vpc"}}, + }}}, + }, + "Appends": { + reason: "Adding should keep the dependencies already there.", + args: args{ + rsp: &v1.RunFunctionResponse{Dependencies: &v1.Dependencies{Items: []*v1.Dependency{ + {Resource: "subnet", DependsOn: &v1.Dependency_ComposedResource{ComposedResource: "vpc"}}, + }}}, + r: "instance", + dependsOn: "subnet", + }, + want: &v1.RunFunctionResponse{Dependencies: &v1.Dependencies{Items: []*v1.Dependency{ + {Resource: "subnet", DependsOn: &v1.Dependency_ComposedResource{ComposedResource: "vpc"}}, + {Resource: "instance", DependsOn: &v1.Dependency_ComposedResource{ComposedResource: "subnet"}}, + }}}, + }, + "CreateBeforeDestroy": { + reason: "WithCreateBeforeDestroy should set the lifecycle.", + args: args{ + rsp: &v1.RunFunctionResponse{}, r: "new-db", dependsOn: "old-db", + o: []DependencyOption{WithCreateBeforeDestroy()}, + }, + want: &v1.RunFunctionResponse{Dependencies: &v1.Dependencies{Items: []*v1.Dependency{ + { + Resource: "new-db", + DependsOn: &v1.Dependency_ComposedResource{ComposedResource: "old-db"}, + Lifecycle: v1.DependencyLifecycle_DEPENDENCY_LIFECYCLE_CREATE_BEFORE_DESTROY, + }, + }}}, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + AddDependency(tc.args.rsp, tc.args.r, tc.args.dependsOn, tc.args.o...) + if diff := cmp.Diff(tc.want, tc.args.rsp, protocmp.Transform()); diff != "" { + t.Errorf("%s\nAddDependency(...): -want, +got:\n%s", tc.reason, diff) + } + }) + } +} + +func TestAddRequiredResourceDependency(t *testing.T) { + rsp := &v1.RunFunctionResponse{} + AddRequiredResourceDependency(rsp, "app-config", &v1.RequiredResourceDependency{RequirementName: "dbs"}) + + want := &v1.RunFunctionResponse{Dependencies: &v1.Dependencies{Items: []*v1.Dependency{ + { + Resource: "app-config", + DependsOn: &v1.Dependency_RequiredResource{RequiredResource: &v1.RequiredResourceDependency{RequirementName: "dbs"}}, + }, + }}} + if diff := cmp.Diff(want, rsp, protocmp.Transform()); diff != "" { + t.Errorf("AddRequiredResourceDependency(...): -want, +got:\n%s", diff) + } +} + +func TestClearDependencies(t *testing.T) { + rsp := &v1.RunFunctionResponse{Dependencies: &v1.Dependencies{Items: []*v1.Dependency{ + {Resource: "subnet", DependsOn: &v1.Dependency_ComposedResource{ComposedResource: "vpc"}}, + }}} + ClearDependencies(rsp) + + // Empty, not unset: unset would carry the dropped dependencies forward. + if rsp.GetDependencies() == nil || len(rsp.GetDependencies().GetItems()) != 0 { + t.Errorf("ClearDependencies(...): want an empty set, got %v", rsp.GetDependencies()) + } +}