1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package physicalmachine
17
18 import (
19 "context"
20
21 "github.com/go-logr/logr"
22 "github.com/pkg/errors"
23 "go.uber.org/fx"
24 apierrors "k8s.io/apimachinery/pkg/api/errors"
25 "k8s.io/apimachinery/pkg/types"
26 "sigs.k8s.io/controller-runtime/pkg/client"
27
28 "github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
29 "github.com/chaos-mesh/chaos-mesh/controllers/config"
30 "github.com/chaos-mesh/chaos-mesh/pkg/selector/generic"
31 genericannotation "github.com/chaos-mesh/chaos-mesh/pkg/selector/generic/annotation"
32 genericfield "github.com/chaos-mesh/chaos-mesh/pkg/selector/generic/field"
33 genericlabel "github.com/chaos-mesh/chaos-mesh/pkg/selector/generic/label"
34 genericnamespace "github.com/chaos-mesh/chaos-mesh/pkg/selector/generic/namespace"
35 "github.com/chaos-mesh/chaos-mesh/pkg/selector/generic/registry"
36 )
37
38 type SelectImpl struct {
39 c client.Client
40 r client.Reader
41
42 generic.Option
43 logger logr.Logger
44 }
45
46 type Params struct {
47 fx.In
48
49 Client client.Client
50 Reader client.Reader `name:"no-cache"`
51 }
52
53 type PhysicalMachine struct {
54 v1alpha1.PhysicalMachine
55 Address string
56 }
57
58 func (pm *PhysicalMachine) Id() string {
59 if len(pm.Address) > 0 {
60 return pm.Address
61 }
62 return (types.NamespacedName{
63 Name: pm.Name,
64 Namespace: pm.Namespace,
65 }).String()
66 }
67
68 func (impl *SelectImpl) Select(ctx context.Context, physicalMachineSelector *v1alpha1.PhysicalMachineSelector) ([]*PhysicalMachine, error) {
69 if physicalMachineSelector == nil {
70 return []*PhysicalMachine{}, nil
71 }
72
73 physicalMachines, err := SelectAndFilterPhysicalMachines(ctx, impl.c, impl.r, physicalMachineSelector, impl.ClusterScoped, impl.TargetNamespace, impl.EnableFilterNamespace, impl.logger)
74 if err != nil {
75 return nil, err
76 }
77
78 filtered, err := filterPhysicalMachinesByMode(physicalMachines, physicalMachineSelector.Mode, physicalMachineSelector.Value)
79 if err != nil {
80 return nil, err
81 }
82 return filtered, nil
83 }
84
85 func New(params Params, logger logr.Logger) *SelectImpl {
86 return &SelectImpl{
87 params.Client,
88 params.Reader,
89 generic.Option{
90 ClusterScoped: config.ControllerCfg.ClusterScoped,
91 TargetNamespace: config.ControllerCfg.TargetNamespace,
92 EnableFilterNamespace: config.ControllerCfg.EnableFilterNamespace,
93 },
94 logger.WithName("physical-machine-selector"),
95 }
96 }
97
98
99 func SelectAndFilterPhysicalMachines(ctx context.Context, c client.Client, r client.Reader, spec *v1alpha1.PhysicalMachineSelector, clusterScoped bool, targetNamespace string, enableFilterNamespace bool, logger logr.Logger) ([]*PhysicalMachine, error) {
100 if len(spec.Address) > 0 {
101 var result []*PhysicalMachine
102 for _, address := range spec.Address {
103 result = append(result, &PhysicalMachine{
104 Address: address,
105 })
106 }
107 return result, nil
108 }
109
110 physicalMachines, err := SelectPhysicalMachines(ctx, c, r, spec.Selector, clusterScoped, targetNamespace, enableFilterNamespace, logger)
111 if err != nil {
112 return nil, err
113 }
114
115 if len(physicalMachines) == 0 {
116 err = errors.New("no physical machine is selected")
117 return nil, err
118 }
119
120 var result []*PhysicalMachine
121 for _, physicalMachine := range physicalMachines {
122 result = append(result, &PhysicalMachine{
123 PhysicalMachine: physicalMachine,
124 })
125 }
126 return result, nil
127 }
128
129 func SelectPhysicalMachines(ctx context.Context, c client.Client, r client.Reader,
130 selector v1alpha1.PhysicalMachineSelectorSpec,
131 clusterScoped bool, targetNamespace string, enableFilterNamespace bool, logger logr.Logger) ([]v1alpha1.PhysicalMachine, error) {
132 if len(selector.PhysicalMachines) > 0 {
133 return selectSpecifiedPhysicalMachines(ctx, c, selector, clusterScoped, targetNamespace, enableFilterNamespace, logger)
134 }
135
136 selectorRegistry := newSelectorRegistry()
137 selectorChain, err := registry.Parse(selectorRegistry, selector.GenericSelectorSpec, generic.Option{
138 ClusterScoped: clusterScoped,
139 TargetNamespace: targetNamespace,
140 EnableFilterNamespace: enableFilterNamespace,
141 })
142 if err != nil {
143 return nil, err
144 }
145
146 return listPhysicalMachines(ctx, c, r, selector, selectorChain, enableFilterNamespace, logger)
147 }
148
149 func listPhysicalMachines(ctx context.Context, c client.Client, r client.Reader, spec v1alpha1.PhysicalMachineSelectorSpec,
150 selectorChain generic.SelectorChain, enableFilterNamespace bool, logger logr.Logger) ([]v1alpha1.PhysicalMachine, error) {
151 var physicalMachines []v1alpha1.PhysicalMachine
152 namespaceCheck := make(map[string]bool)
153
154 if err := selectorChain.ListObjects(c, r,
155 func(listFunc generic.ListFunc, opts client.ListOptions) error {
156 var pmList v1alpha1.PhysicalMachineList
157
158 if len(spec.Namespaces) > 0 {
159 for _, namespace := range spec.Namespaces {
160 if enableFilterNamespace {
161 allow, ok := namespaceCheck[namespace]
162 if !ok {
163 allow = genericnamespace.CheckNamespace(ctx, c, namespace, logger)
164 namespaceCheck[namespace] = allow
165 }
166 if !allow {
167 continue
168 }
169 }
170
171 opts.Namespace = namespace
172 if err := listFunc(ctx, &pmList, &opts); err != nil {
173 return err
174 }
175 physicalMachines = append(physicalMachines, pmList.Items...)
176 }
177 } else {
178
179 if err := listFunc(ctx, &pmList, &opts); err != nil {
180 return err
181 }
182 physicalMachines = append(physicalMachines, pmList.Items...)
183 }
184 return nil
185 }); err != nil {
186 return nil, err
187 }
188
189 filterList := make([]v1alpha1.PhysicalMachine, 0, len(physicalMachines))
190 for _, physicalMachine := range physicalMachines {
191 physicalMachine := physicalMachine
192 if selectorChain.Match(&physicalMachine) {
193 filterList = append(filterList, physicalMachine)
194 }
195 }
196 return filterList, nil
197 }
198
199 func newSelectorRegistry() registry.Registry {
200 return map[string]registry.SelectorFactory{
201 genericlabel.Name: genericlabel.New,
202 genericnamespace.Name: genericnamespace.New,
203 genericfield.Name: genericfield.New,
204 genericannotation.Name: genericannotation.New,
205 }
206 }
207
208 func selectSpecifiedPhysicalMachines(ctx context.Context, c client.Client, spec v1alpha1.PhysicalMachineSelectorSpec,
209 clusterScoped bool, targetNamespace string, enableFilterNamespace bool, logger logr.Logger) ([]v1alpha1.PhysicalMachine, error) {
210 var physicalMachines []v1alpha1.PhysicalMachine
211 namespaceCheck := make(map[string]bool)
212
213 for ns, names := range spec.PhysicalMachines {
214 if !clusterScoped {
215 if targetNamespace != ns {
216 logger.Info("skip namespace because ns is out of scope within namespace scoped mode", "namespace", ns)
217 continue
218 }
219 }
220 if enableFilterNamespace {
221 allow, ok := namespaceCheck[ns]
222 if !ok {
223 allow = genericnamespace.CheckNamespace(ctx, c, ns, logger)
224 namespaceCheck[ns] = allow
225 }
226 if !allow {
227 continue
228 }
229 }
230 for _, name := range names {
231 var physicalMachine v1alpha1.PhysicalMachine
232 err := c.Get(ctx, types.NamespacedName{
233 Namespace: ns,
234 Name: name,
235 }, &physicalMachine)
236 if err == nil {
237 physicalMachines = append(physicalMachines, physicalMachine)
238 continue
239 }
240
241 if apierrors.IsNotFound(err) {
242 logger.Error(err, "PhysicalMachine is not found", "namespace", ns, "physical machine name", name)
243 continue
244 }
245
246 return nil, err
247 }
248 }
249 return physicalMachines, nil
250 }
251
252
253 func filterPhysicalMachinesByMode(physicalMachines []*PhysicalMachine, mode v1alpha1.SelectorMode, value string) ([]*PhysicalMachine, error) {
254 indexes, err := generic.FilterObjectsByMode(mode, value, len(physicalMachines))
255 if err != nil {
256 return nil, err
257 }
258
259 var filtered []*PhysicalMachine
260 for _, index := range indexes {
261 index := index
262 filtered = append(filtered, physicalMachines[index])
263 }
264 return filtered, nil
265 }
266