generated from crossplane/function-template-go
-
Notifications
You must be signed in to change notification settings - Fork 62
Expand file tree
/
Copy pathextraresources.go
More file actions
118 lines (100 loc) · 3.79 KB
/
extraresources.go
File metadata and controls
118 lines (100 loc) · 3.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package main
import (
"encoding/json"
"maps"
"github.com/crossplane/crossplane-runtime/v2/pkg/errors"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/structpb"
fnv1 "github.com/crossplane/function-sdk-go/proto/v1"
"github.com/crossplane/function-sdk-go/request"
"github.com/crossplane/function-sdk-go/response"
)
// ExtraResourcesRequirements defines the requirements for extra resources.
type ExtraResourcesRequirements map[string]ExtraResourcesRequirement
// ExtraResourcesRequirement defines a single requirement for extra resources.
// Needed to have camelCase keys instead of the snake_case keys as defined
// through json tags by fnv1.ResourceSelector.
type ExtraResourcesRequirement struct {
// APIVersion of the resource.
APIVersion string `json:"apiVersion"`
// Kind of the resource.
Kind string `json:"kind"`
// MatchLabels defines the labels to match the resource, if defined,
// matchName is ignored.
MatchLabels map[string]string `json:"matchLabels,omitempty"`
// MatchName defines the name to match the resource, if MatchLabels is
// empty.
MatchName string `json:"matchName,omitempty"`
// Namespace defines the namespace of the resource to match, leave empty for cluster-scoped.
Namespace string `json:"namespace,omitempty"`
}
const (
extraResourcesContextKey = "apiextensions.crossplane.io/extra-resources"
)
// ToResourceSelector converts the ExtraResourcesRequirement to a fnv1.ResourceSelector.
func (e *ExtraResourcesRequirement) ToResourceSelector() *fnv1.ResourceSelector {
out := &fnv1.ResourceSelector{
ApiVersion: e.APIVersion,
Kind: e.Kind,
}
if e.MatchName == "" {
out.Match = &fnv1.ResourceSelector_MatchLabels{
MatchLabels: &fnv1.MatchLabels{Labels: e.MatchLabels},
}
return out
}
out.Match = &fnv1.ResourceSelector_MatchName{
MatchName: e.MatchName,
}
if e.Namespace != "" {
out.Namespace = &e.Namespace
}
return out
}
func mergeExtraResourcesToContext(req *fnv1.RunFunctionRequest, rsp *fnv1.RunFunctionResponse) error {
b, err := json.Marshal(req.GetExtraResources()) //nolint:staticcheck // retain support for v1 interface
if err != nil {
return errors.Errorf("cannot marshal %T: %w", req.GetExtraResources(), err) //nolint:staticcheck // retain support for v1 interface
}
s := &structpb.Struct{}
if err := protojson.Unmarshal(b, s); err != nil {
return errors.Errorf("cannot unmarshal %T into %T: %w", req.GetExtraResources(), s, err) //nolint:staticcheck // retain support for v1 interface
}
extraResourcesFromContext, exists := request.GetContextKey(req, extraResourcesContextKey)
if exists {
merged := mergeStructs(extraResourcesFromContext.GetStructValue(), s)
s = merged
}
response.SetContextKey(rsp, extraResourcesContextKey, structpb.NewStructValue(s))
return nil
}
func mergeRequiredResourcesToContext(req *fnv1.RunFunctionRequest, rsp *fnv1.RunFunctionResponse) error {
b, err := json.Marshal(req.GetRequiredResources())
if err != nil {
return errors.Errorf("cannot marshal %T: %w", req.GetRequiredResources(), err)
}
s := &structpb.Struct{}
if err := protojson.Unmarshal(b, s); err != nil {
return errors.Errorf("cannot unmarshal %T into %T: %w", req.GetRequiredResources(), s, err)
}
extraResourcesFromContext, exists := request.GetContextKey(req, extraResourcesContextKey)
if exists {
merged := mergeStructs(extraResourcesFromContext.GetStructValue(), s)
s = merged
}
response.SetContextKey(rsp, extraResourcesContextKey, structpb.NewStructValue(s))
return nil
}
// MergeStructs merges fields from s2 into s1, overwriting s1's fields if keys overlap.
func mergeStructs(s1, s2 *structpb.Struct) *structpb.Struct {
if s1 == nil {
return s2
}
if s2 == nil {
return s1
}
merged := s1.AsMap()
maps.Copy(merged, s2.AsMap())
result, _ := structpb.NewStruct(merged)
return result
}