Skip to content

Commit

Permalink
Updates the response API to allow targeting and conditions.
Browse files Browse the repository at this point in the history
Signed-off-by: dalton hill <[email protected]>
  • Loading branch information
dalton-hill-0 committed Jun 12, 2024
1 parent 95d3207 commit d685d66
Show file tree
Hide file tree
Showing 7 changed files with 614 additions and 381 deletions.
429 changes: 239 additions & 190 deletions proto/v1beta1/run_function.pb.go

Large diffs are not rendered by default.

57 changes: 39 additions & 18 deletions proto/v1beta1/run_function.proto
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ message RunFunctionResponse {

// Requirements that must be satisfied for this Function to run successfully.
Requirements requirements = 5;

// Status Conditions to be applied to the Composite Resource and sometimes the
// Claim.
repeated Condition conditions = 6;
}

// RequestMeta contains metadata pertaining to a RunFunctionRequest.
Expand All @@ -142,11 +146,18 @@ message Requirements {

// ResourceSelector selects a group of resources, either by name or by label.
message ResourceSelector {
// API version of resources to select.
string api_version = 1;

// Kind of resources to select.
string kind = 2;

// Resources to match.
oneof match {
// Match the resource with this name.
string match_name = 3;

// Match all resources with these labels.
MatchLabels match_labels = 4;
}
}
Expand Down Expand Up @@ -240,16 +251,11 @@ message Result {
// Human-readable details about the result.
string message = 2;

// The resources this result targets.
Target target = 3;

// Optional PascalCase, machine-readable reason for this result.
optional string reason = 4;
optional string reason = 3;

// A result can optionally indicate the condition of the target resources.
// Crossplane will consider the resources to be in the supplied condition
// until another result changes it.
optional Condition condition = 5;
// The resources this result targets.
optional Target target = 4;
}

// Severity of Function results.
Expand Down Expand Up @@ -285,20 +291,35 @@ enum Target {
TARGET_COMPOSITE_AND_CLAIM = 2;
}

// Status of Function result condition.
enum Status {
STATUS_UNSPECIFIED = 0;
STATUS_FALSE = 1;
STATUS_TRUE = 2;
}

// A Condition
// A Status Condition to be applied to the Composite Resource and sometimes the
// Claim. For detailed information on proper usage of Conditions, please see
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties.
message Condition {
// Type of the condition, e.g. DatabaseReady.
// Ready and Synced are reserved for use by Crossplane.
// Type of condition in CamelCase or in foo.example.com/CamelCase.
string type = 1;

// Status of the condition.
Status status = 2;

// Reason contains a programmatic identifier indicating the reason for the
// condition's last transition. Producers of specific condition types may
// define expected values and meanings for this field, and whether the values
// are considered a guaranteed API. The value should be a CamelCase string.
// This field may not be empty.
string reason = 3;

// Message is a human readable message indicating details about the
// transition. This may be an empty string.
optional string message = 4;

// The resources this condition targets.
optional Target target = 5;
}

enum Status {
STATUS_CONDITION_UNKNOWN = 0;

STATUS_CONDITION_TRUE = 1;

STATUS_CONDITION_FALSE = 2;
}
78 changes: 78 additions & 0 deletions response/condition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
Copyright 2024 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.
*/

package response

import (
"github.com/crossplane/function-sdk-go/proto/v1beta1"
)

// Condition to be applied to the Composite Resource and sometimes the
// Claim. For detailed information on proper usage of Conditions, please see
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties.
type Condition struct {
*v1beta1.Condition
}

// ConditionTrue will create a condition with the status of true and add the
// condition to the supplied RunFunctionResponse.
func ConditionTrue(rsp *v1beta1.RunFunctionResponse, typ, reason string) *Condition {
return newCondition(rsp, typ, reason, v1beta1.Status_STATUS_CONDITION_TRUE)
}

// ConditionFalse will create a condition with the status of false and add the
// condition to the supplied RunFunctionResponse.
func ConditionFalse(rsp *v1beta1.RunFunctionResponse, typ, reason string) *Condition {
return newCondition(rsp, typ, reason, v1beta1.Status_STATUS_CONDITION_FALSE)
}

// ConditionUnknown will create a condition with the status of unknown and add
// the condition to the supplied RunFunctionResponse.
func ConditionUnknown(rsp *v1beta1.RunFunctionResponse, typ, reason string) *Condition {
return newCondition(rsp, typ, reason, v1beta1.Status_STATUS_CONDITION_UNKNOWN)
}

func newCondition(rsp *v1beta1.RunFunctionResponse, typ, reason string, s v1beta1.Status) *Condition {
if rsp.GetConditions() == nil {
rsp.Conditions = make([]*v1beta1.Condition, 0, 1)
}
c := &Condition{
Condition: &v1beta1.Condition{
Type: typ,
Status: s,
Reason: reason,
Target: v1beta1.Target_TARGET_COMPOSITE.Enum(),
},
}
rsp.Conditions = append(rsp.GetConditions(), c.Condition)
return c
}

// TargetComposite updates the condition to target the composite resource.
func (c *Condition) TargetComposite() {
c.Target = v1beta1.Target_TARGET_COMPOSITE.Enum()
}

// TargetCompositeAndClaim updates the condition to target both the composite
// resource and claim.
func (c *Condition) TargetCompositeAndClaim() {
c.Target = v1beta1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum()
}

// WithMessage adds the message to the condition.
func (c *Condition) WithMessage(message string) {
c.Message = &message
}
158 changes: 158 additions & 0 deletions response/condition_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
Copyright 2024 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.
*/

package response_test

import (
"testing"

"github.com/google/go-cmp/cmp"
"google.golang.org/protobuf/testing/protocmp"
"k8s.io/utils/ptr"

"github.com/crossplane/function-sdk-go/proto/v1beta1"
"github.com/crossplane/function-sdk-go/response"
)

// Condition types.
const (
typeDatabaseReady = "DatabaseReady"
)

// Condition reasons.
const (
reasonAvailable = "ReasonAvailable"
reasonCreating = "ReasonCreating"
reasonPriorFailure = "ReasonPriorFailure"
reasonUnauthorized = "ReasonUnauthorized"
)

func TestCondition(t *testing.T) {
type testFn func(*v1beta1.RunFunctionResponse)
type args struct {
fns []testFn
}
type want struct {
conditions []*v1beta1.Condition
}
cases := map[string]struct {
reason string
args args
want want
}{
"CreateBasicRecords": {
reason: "Correctly adds conditions to the response.",
args: args{
fns: []testFn{
func(rsp *v1beta1.RunFunctionResponse) {
response.ConditionTrue(rsp, typeDatabaseReady, reasonAvailable)
},
func(rsp *v1beta1.RunFunctionResponse) {
response.ConditionFalse(rsp, typeDatabaseReady, reasonCreating)
},
func(rsp *v1beta1.RunFunctionResponse) {
response.ConditionUnknown(rsp, typeDatabaseReady, reasonPriorFailure)
},
},
},
want: want{
conditions: []*v1beta1.Condition{
{
Type: typeDatabaseReady,
Status: v1beta1.Status_STATUS_CONDITION_TRUE,
Reason: reasonAvailable,
Target: v1beta1.Target_TARGET_COMPOSITE.Enum(),
},
{
Type: typeDatabaseReady,
Status: v1beta1.Status_STATUS_CONDITION_FALSE,
Reason: reasonCreating,
Target: v1beta1.Target_TARGET_COMPOSITE.Enum(),
},
{
Type: typeDatabaseReady,
Status: v1beta1.Status_STATUS_CONDITION_UNKNOWN,
Reason: reasonPriorFailure,
Target: v1beta1.Target_TARGET_COMPOSITE.Enum(),
},
},
},
},
"SetTargets": {
reason: "Correctly sets targets on condition and adds it to the response.",
args: args{
fns: []testFn{
func(rsp *v1beta1.RunFunctionResponse) {
response.ConditionTrue(rsp, typeDatabaseReady, reasonAvailable).TargetComposite()
},
func(rsp *v1beta1.RunFunctionResponse) {
response.ConditionTrue(rsp, typeDatabaseReady, reasonAvailable).TargetCompositeAndClaim()
},
},
},
want: want{
conditions: []*v1beta1.Condition{
{
Type: typeDatabaseReady,
Status: v1beta1.Status_STATUS_CONDITION_TRUE,
Reason: reasonAvailable,
Target: v1beta1.Target_TARGET_COMPOSITE.Enum(),
},
{
Type: typeDatabaseReady,
Status: v1beta1.Status_STATUS_CONDITION_TRUE,
Reason: reasonAvailable,
Target: v1beta1.Target_TARGET_COMPOSITE_AND_CLAIM.Enum(),
},
},
},
},
"SetMessage": {
reason: "Correctly sets message on condition and adds it to the response.",
args: args{
fns: []testFn{
func(rsp *v1beta1.RunFunctionResponse) {
response.ConditionTrue(rsp, typeDatabaseReady, reasonAvailable).WithMessage("a test message")
},
},
},
want: want{
conditions: []*v1beta1.Condition{
{
Type: typeDatabaseReady,
Status: v1beta1.Status_STATUS_CONDITION_TRUE,
Reason: reasonAvailable,
Target: v1beta1.Target_TARGET_COMPOSITE.Enum(),
Message: ptr.To("a test message"),
},
},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
rsp := &v1beta1.RunFunctionResponse{}
for _, f := range tc.args.fns {
f(rsp)
}

if diff := cmp.Diff(tc.want.conditions, rsp.GetConditions(), protocmp.Transform()); diff != "" {
t.Errorf("\n%s\nFrom(...): -want, +got:\n%s", tc.reason, diff)
}

})
}
}
Loading

0 comments on commit d685d66

Please sign in to comment.