...

Source file src/github.com/chaos-mesh/chaos-mesh/pkg/selector/physicalmachine/selector.go

Documentation: github.com/chaos-mesh/chaos-mesh/pkg/selector/physicalmachine

     1  // Copyright 2021 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    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  // SelectAndFilterPhysicalMachines returns the list of physical machines that filtered by selector and SelectorMode
    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  				// in fact, this will never happen
   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  // filterPhysicalMachinesByMode filters physical machines by mode from physical machine list
   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