istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/framework/components/cluster/clusterboot/factory.go (about)

     1  //  Copyright Istio 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  package clusterboot
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"github.com/hashicorp/go-multierror"
    21  
    22  	"istio.io/istio/pkg/test/framework/components/cluster"
    23  	// imported to trigger registration
    24  	_ "istio.io/istio/pkg/test/framework/components/cluster/kube"
    25  	// imported to trigger registration
    26  	_ "istio.io/istio/pkg/test/framework/components/cluster/staticvm"
    27  	"istio.io/istio/pkg/test/framework/config"
    28  	"istio.io/istio/pkg/test/scopes"
    29  )
    30  
    31  var _ cluster.Factory = factory{}
    32  
    33  func NewFactory() cluster.Factory {
    34  	return factory{}
    35  }
    36  
    37  type factory struct {
    38  	configs []cluster.Config
    39  }
    40  
    41  func (a factory) Kind() cluster.Kind {
    42  	return cluster.Aggregate
    43  }
    44  
    45  func (a factory) With(configs ...cluster.Config) cluster.Factory {
    46  	return factory{configs: append(a.configs, configs...)}
    47  }
    48  
    49  func (a factory) Build() (cluster.Clusters, error) {
    50  	scopes.Framework.Infof("=== BEGIN: Building clusters ===")
    51  
    52  	// use multierror to give as much detail as possible if the config is bad
    53  	var errs error
    54  	defer func() {
    55  		if errs != nil {
    56  			scopes.Framework.Infof("=== FAILED: Building clusters ===")
    57  		}
    58  	}()
    59  
    60  	allClusters := make(cluster.Map)
    61  	var clusters cluster.Clusters
    62  	for i, cfg := range a.configs {
    63  		c, err := buildCluster(cfg, allClusters)
    64  		if err != nil {
    65  			errs = multierror.Append(errs, fmt.Errorf("failed building cluster from config %d: %v", i, err))
    66  			continue
    67  		}
    68  		if _, ok := allClusters[c.Name()]; ok {
    69  			errs = multierror.Append(errs, fmt.Errorf("more than one cluster named %s", c.Name()))
    70  			continue
    71  		}
    72  		allClusters[c.Name()] = c
    73  		clusters = append(clusters, c)
    74  	}
    75  	if errs != nil {
    76  		return nil, errs
    77  	}
    78  
    79  	// validate the topology has no open edges
    80  	for _, c := range allClusters {
    81  		if primary, ok := allClusters[c.PrimaryName()]; !ok {
    82  			errs = multierror.Append(errs, fmt.Errorf("primary %s for %s is not in the topology", c.PrimaryName(), c.Name()))
    83  			continue
    84  		} else if !validPrimaryOrConfig(primary) {
    85  			errs = multierror.Append(errs, fmt.Errorf("primary %s for %s is of kind %s, primaries must be Kubernetes", primary.Name(), c.Name(), primary.Kind()))
    86  			continue
    87  		}
    88  		if cfg, ok := allClusters[c.ConfigName()]; !ok {
    89  			errs = multierror.Append(errs, fmt.Errorf("config %s for %s is not in the topology", c.ConfigName(), c.Name()))
    90  			continue
    91  		} else if !validPrimaryOrConfig(cfg) {
    92  			errs = multierror.Append(errs, fmt.Errorf("config %s for %s is of kind %s, primaries must be Kubernetes", cfg.Name(), c.Name(), cfg.Kind()))
    93  			continue
    94  		}
    95  	}
    96  	if errs != nil {
    97  		return nil, errs
    98  	}
    99  
   100  	for _, c := range clusters {
   101  		scopes.Framework.Infof("Built Cluster:\n%s", c.String())
   102  	}
   103  
   104  	scopes.Framework.Infof("=== DONE: Building clusters ===")
   105  	return clusters, errs
   106  }
   107  
   108  func validPrimaryOrConfig(c cluster.Cluster) bool {
   109  	return c.Kind() == cluster.Kubernetes || c.Kind() == cluster.Fake
   110  }
   111  
   112  func buildCluster(cfg cluster.Config, allClusters cluster.Map) (cluster.Cluster, error) {
   113  	cfg, err := validConfig(cfg)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	f, err := cluster.GetFactory(cfg)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	return f(cfg, cluster.NewTopology(cfg, allClusters))
   122  }
   123  
   124  func validConfig(cfg cluster.Config) (cluster.Config, error) {
   125  	if cfg.Name == "" {
   126  		return cfg, fmt.Errorf("empty cluster name")
   127  	}
   128  	if cfg.Kind == "" {
   129  		return cfg, fmt.Errorf("unspecified Kind for %s", cfg.Name)
   130  	}
   131  	if cfg.PrimaryClusterName == "" {
   132  		cfg.PrimaryClusterName = cfg.Name
   133  	}
   134  	if cfg.ConfigClusterName == "" {
   135  		cfg.ConfigClusterName = cfg.PrimaryClusterName
   136  	}
   137  	if cfg.Meta == nil {
   138  		cfg.Meta = config.Map{}
   139  	}
   140  	return cfg, nil
   141  }