github.com/argoproj/argo-cd/v3@v3.2.1/applicationset/generators/matrix.go (about)

     1  package generators
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"time"
     7  
     8  	"dario.cat/mergo"
     9  	"sigs.k8s.io/controller-runtime/pkg/client"
    10  
    11  	"github.com/argoproj/argo-cd/v3/applicationset/utils"
    12  	argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    13  )
    14  
    15  var _ Generator = (*MatrixGenerator)(nil)
    16  
    17  var (
    18  	ErrMoreThanTwoGenerators      = errors.New("found more than two generators, Matrix support only two")
    19  	ErrLessThanTwoGenerators      = errors.New("found less than two generators, Matrix support only two")
    20  	ErrMoreThenOneInnerGenerators = errors.New("found more than one generator in matrix.Generators")
    21  )
    22  
    23  type MatrixGenerator struct {
    24  	// The inner generators supported by the matrix generator (cluster, git, list...)
    25  	supportedGenerators map[string]Generator
    26  }
    27  
    28  func NewMatrixGenerator(supportedGenerators map[string]Generator) Generator {
    29  	m := &MatrixGenerator{
    30  		supportedGenerators: supportedGenerators,
    31  	}
    32  	return m
    33  }
    34  
    35  func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]any, error) {
    36  	if appSetGenerator.Matrix == nil {
    37  		return nil, ErrEmptyAppSetGenerator
    38  	}
    39  
    40  	if len(appSetGenerator.Matrix.Generators) < 2 {
    41  		return nil, ErrLessThanTwoGenerators
    42  	}
    43  
    44  	if len(appSetGenerator.Matrix.Generators) > 2 {
    45  		return nil, ErrMoreThanTwoGenerators
    46  	}
    47  
    48  	res := []map[string]any{}
    49  
    50  	g0, err := m.getParams(appSetGenerator.Matrix.Generators[0], appSet, nil, client)
    51  	if err != nil {
    52  		return nil, fmt.Errorf("error failed to get params for first generator in matrix generator: %w", err)
    53  	}
    54  	for _, a := range g0 {
    55  		g1, err := m.getParams(appSetGenerator.Matrix.Generators[1], appSet, a, client)
    56  		if err != nil {
    57  			return nil, fmt.Errorf("failed to get params for second generator in the matrix generator: %w", err)
    58  		}
    59  		for _, b := range g1 {
    60  			if appSet.Spec.GoTemplate {
    61  				tmp := map[string]any{}
    62  				if err := mergo.Merge(&tmp, b, mergo.WithOverride); err != nil {
    63  					return nil, fmt.Errorf("failed to merge params from the second generator in the matrix generator with temp map: %w", err)
    64  				}
    65  				if err := mergo.Merge(&tmp, a, mergo.WithOverride); err != nil {
    66  					return nil, fmt.Errorf("failed to merge params from the second generator in the matrix generator with the first: %w", err)
    67  				}
    68  				res = append(res, tmp)
    69  			} else {
    70  				val, err := utils.CombineStringMaps(a, b)
    71  				if err != nil {
    72  					return nil, fmt.Errorf("failed to combine string maps with merging params for the matrix generator: %w", err)
    73  				}
    74  				res = append(res, val)
    75  			}
    76  		}
    77  	}
    78  
    79  	return res, nil
    80  }
    81  
    82  func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, params map[string]any, client client.Client) ([]map[string]any, error) {
    83  	matrixGen, err := getMatrixGenerator(appSetBaseGenerator)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	mergeGen, err := getMergeGenerator(appSetBaseGenerator)
    88  	if err != nil {
    89  		return nil, fmt.Errorf("error retrieving merge generator: %w", err)
    90  	}
    91  
    92  	t, err := Transform(
    93  		argoprojiov1alpha1.ApplicationSetGenerator{
    94  			List:                    appSetBaseGenerator.List,
    95  			Clusters:                appSetBaseGenerator.Clusters,
    96  			Git:                     appSetBaseGenerator.Git,
    97  			SCMProvider:             appSetBaseGenerator.SCMProvider,
    98  			ClusterDecisionResource: appSetBaseGenerator.ClusterDecisionResource,
    99  			PullRequest:             appSetBaseGenerator.PullRequest,
   100  			Plugin:                  appSetBaseGenerator.Plugin,
   101  			Matrix:                  matrixGen,
   102  			Merge:                   mergeGen,
   103  			Selector:                appSetBaseGenerator.Selector,
   104  		},
   105  		m.supportedGenerators,
   106  		argoprojiov1alpha1.ApplicationSetTemplate{},
   107  		appSet,
   108  		params,
   109  		client)
   110  	if err != nil {
   111  		return nil, fmt.Errorf("child generator returned an error on parameter generation: %w", err)
   112  	}
   113  
   114  	if len(t) == 0 {
   115  		return nil, errors.New("child generator generated no parameters")
   116  	}
   117  
   118  	if len(t) > 1 {
   119  		return nil, ErrMoreThenOneInnerGenerators
   120  	}
   121  
   122  	return t[0].Params, nil
   123  }
   124  
   125  const maxDuration time.Duration = 1<<63 - 1
   126  
   127  func (m *MatrixGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
   128  	res := maxDuration
   129  	var found bool
   130  
   131  	for _, r := range appSetGenerator.Matrix.Generators {
   132  		matrixGen, _ := getMatrixGenerator(r)
   133  		mergeGen, _ := getMergeGenerator(r)
   134  		base := &argoprojiov1alpha1.ApplicationSetGenerator{
   135  			List:                    r.List,
   136  			Clusters:                r.Clusters,
   137  			Git:                     r.Git,
   138  			PullRequest:             r.PullRequest,
   139  			Plugin:                  r.Plugin,
   140  			SCMProvider:             r.SCMProvider,
   141  			ClusterDecisionResource: r.ClusterDecisionResource,
   142  			Matrix:                  matrixGen,
   143  			Merge:                   mergeGen,
   144  		}
   145  		generators := GetRelevantGenerators(base, m.supportedGenerators)
   146  
   147  		for _, g := range generators {
   148  			temp := g.GetRequeueAfter(base)
   149  			if temp < res && temp != NoRequeueAfter {
   150  				found = true
   151  				res = temp
   152  			}
   153  		}
   154  	}
   155  
   156  	if found {
   157  		return res
   158  	}
   159  	return NoRequeueAfter
   160  }
   161  
   162  func getMatrixGenerator(r argoprojiov1alpha1.ApplicationSetNestedGenerator) (*argoprojiov1alpha1.MatrixGenerator, error) {
   163  	if r.Matrix == nil {
   164  		return nil, nil
   165  	}
   166  	matrix, err := argoprojiov1alpha1.ToNestedMatrixGenerator(r.Matrix)
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  	return matrix.ToMatrixGenerator(), nil
   171  }
   172  
   173  func (m *MatrixGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
   174  	return &appSetGenerator.Matrix.Template
   175  }