-
Notifications
You must be signed in to change notification settings - Fork 11
/
options.go
150 lines (121 loc) · 5.24 KB
/
options.go
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package qtransform
import (
"context"
"fmt"
"github.com/siderolabs/gen/optional"
"go.uber.org/zap"
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/controller/generic"
"github.com/cosi-project/runtime/pkg/resource"
)
type namespaceType struct {
Namespace resource.Namespace
Type resource.Type
}
type mapperFunc func(context.Context, *zap.Logger, controller.QRuntime, resource.Resource) ([]resource.Pointer, error)
// MapperFuncGeneric is a generic version of mapperFunc.
type MapperFuncGeneric[I generic.ResourceWithRD] func(context.Context, *zap.Logger, controller.QRuntime, I) ([]resource.Pointer, error)
// MapperSameID is a mapper that returns the same namespace ID as the input resource, but uses output resource type.
func MapperSameID[I generic.ResourceWithRD, O generic.ResourceWithRD]() MapperFuncGeneric[I] {
var zeroOutput O
outputType := zeroOutput.ResourceDefinition().Type
return func(_ context.Context, _ *zap.Logger, _ controller.QRuntime, v I) ([]resource.Pointer, error) {
return []resource.Pointer{resource.NewMetadata(v.Metadata().Namespace(), outputType, v.Metadata().ID(), resource.VersionUndefined)}, nil
}
}
// MapperNone is a mapper that returns no pointers.
func MapperNone[I generic.ResourceWithRD]() MapperFuncGeneric[I] {
return func(context.Context, *zap.Logger, controller.QRuntime, I) ([]resource.Pointer, error) {
return nil, nil
}
}
func mapperFuncFromGeneric[I generic.ResourceWithRD](generic MapperFuncGeneric[I]) mapperFunc {
return func(ctx context.Context, logger *zap.Logger, r controller.QRuntime, res resource.Resource) ([]resource.Pointer, error) {
v, ok := res.(I)
if !ok {
return nil, fmt.Errorf("unexpected resource type in mapFunc %T", res)
}
return generic(ctx, logger, r, v)
}
}
// ControllerOptions configures QTransformController.
type ControllerOptions struct {
mappers map[namespaceType]mapperFunc
leftoverFinalizers map[resource.Finalizer]struct{}
extraInputs []controller.Input
extraOutputs []controller.Output
primaryOutputKind controller.OutputKind
concurrency optional.Optional[uint]
}
// ControllerOption is an option for QTransformController.
type ControllerOption func(*ControllerOptions)
// WithExtraMappedInputKind adds an extra input with the given kind to the controller.
func WithExtraMappedInputKind[I generic.ResourceWithRD](mapFunc MapperFuncGeneric[I], inputKind controller.InputKind) ControllerOption {
return func(o *ControllerOptions) {
var zeroInput I
if inputKind != controller.InputQMapped && inputKind != controller.InputQMappedDestroyReady {
panic(fmt.Errorf("unexpected input kind for QController %q", inputKind))
}
if o.mappers == nil {
o.mappers = map[namespaceType]mapperFunc{}
}
nsType := namespaceType{
Namespace: zeroInput.ResourceDefinition().DefaultNamespace,
Type: zeroInput.ResourceDefinition().Type,
}
if _, present := o.mappers[nsType]; present {
panic(fmt.Errorf("duplicate mapper for %q", nsType))
}
o.mappers[nsType] = mapperFuncFromGeneric(mapFunc)
o.extraInputs = append(o.extraInputs, controller.Input{
Namespace: zeroInput.ResourceDefinition().DefaultNamespace,
Type: zeroInput.ResourceDefinition().Type,
Kind: inputKind,
})
}
}
// WithExtraMappedInput adds an extra mapped input to the controller.
func WithExtraMappedInput[I generic.ResourceWithRD](mapFunc MapperFuncGeneric[I]) ControllerOption {
return WithExtraMappedInputKind(mapFunc, controller.InputQMapped)
}
// WithExtraMappedDestroyReadyInput adds an extra mapped destroy-ready input to the controller.
func WithExtraMappedDestroyReadyInput[I generic.ResourceWithRD](mapFunc MapperFuncGeneric[I]) ControllerOption {
return WithExtraMappedInputKind(mapFunc, controller.InputQMappedDestroyReady)
}
// WithExtraOutputs adds extra outputs to the controller.
func WithExtraOutputs(outputs ...controller.Output) ControllerOption {
return func(o *ControllerOptions) {
o.extraOutputs = append(o.extraOutputs, outputs...)
}
}
// WithOutputKind sets main output resource kind.
func WithOutputKind(kind controller.OutputKind) ControllerOption {
return func(o *ControllerOptions) {
o.primaryOutputKind = kind
}
}
// WithConcurrency sets the maximum number of concurrent reconciles.
func WithConcurrency(n uint) ControllerOption {
return func(o *ControllerOptions) {
o.concurrency = optional.Some(n)
}
}
// WithIgnoreTeardownUntil ignores input resource teardown until the input resource has only mentioned finalizers left.
//
// This allows to keep output resources not destroyed until other controllers remove their finalizers.
//
// Implicitly the controller will also ignore its own finalizer, so if the list is empty, the controller will wait
// to be the last one not done with the resource.
func WithIgnoreTeardownUntil(finalizers ...resource.Finalizer) ControllerOption {
return func(o *ControllerOptions) {
if o.leftoverFinalizers == nil {
o.leftoverFinalizers = map[resource.Finalizer]struct{}{}
}
for _, fin := range finalizers {
o.leftoverFinalizers[fin] = struct{}{}
}
}
}