Validate clustermap parent-child dependancy

Add ValidateClusterMap() method to confirm there are no parent-child
dependancy issues for any clustermaps.

Closes: #481
Change-Id: Ifb57b026933909a4350d71353adca23a42440a48
This commit is contained in:
Graham Steffaniak 2021-06-08 08:55:04 -05:00
parent e2e8732fed
commit edb2aae971
3 changed files with 59 additions and 0 deletions

View File

@ -39,3 +39,13 @@ type ErrClusterNotInMap struct {
func (e ErrClusterNotInMap) Error() string {
return fmt.Sprintf("cluster '%s' is not defined in cluster map %v", e.Child, e.Map)
}
// ErrClusterCircularDependency returned for circular dependencies
type ErrClusterCircularDependency struct {
Parent string
Map *v1alpha1.ClusterMap
}
func (e ErrClusterCircularDependency) Error() string {
return fmt.Sprintf("%v contains cluster referenced as both parent and child: %s", e.Map, e.Parent)
}

View File

@ -36,6 +36,7 @@ type WriteOptions struct {
// TODO use typed cluster names
type ClusterMap interface {
ParentCluster(string) (string, error)
ValidateClusterMap() error
AllClusters() []string
ClusterKubeconfigContext(string) (string, error)
Sources(string) ([]v1alpha1.KubeconfigSource, error)
@ -66,6 +67,33 @@ func (cm clusterMap) ParentCluster(child string) (string, error) {
return currentCluster.Parent, nil
}
// Validates a clustermap has valid parent-child map structure
func (cm clusterMap) ValidateClusterMap() error {
clusterMap := cm.AllClusters()
for _, childCluster := range clusterMap {
var parentClusters []string
var currentChild string = childCluster
for {
currentCluster, _ := cm.apiMap.Map[currentChild]
for _, c := range parentClusters {
if c == currentCluster.Parent {
// Quit on parent whos also child
return ErrClusterCircularDependency{Parent: childCluster, Map: cm.apiMap}
}
}
// Quit loop once top level of current cluster is reached
if currentCluster.Parent == "" {
break
}
parentClusters = append(parentClusters, currentCluster.Parent)
currentChild = currentCluster.Parent
}
}
// Return success if there are no conflicts
return nil
}
// AllClusters returns all clusters in a map
func (cm clusterMap) AllClusters() []string {
clusters := []string{}

View File

@ -97,6 +97,27 @@ func TestClusterMap(t *testing.T) {
assert.Equal(t, "", parent)
})
t.Run("Validate Circular Clustermap", func(t *testing.T) {
// Create new map with circular dependency
circularAPIMap := &v1alpha1.ClusterMap{
Map: map[string]*v1alpha1.Cluster{},
}
for key, value := range apiMap.Map {
newValue := *value
circularAPIMap.Map[key] = &newValue
}
circularAPIMap.Map["ephemeral"].Parent = "workload"
cMapCircular := clustermap.NewClusterMap(circularAPIMap)
err := cMapCircular.ValidateClusterMap()
assert.Error(t, err)
})
t.Run("Validate all Clustermaps", func(t *testing.T) {
// Check child clusterID against map of parent clusterID map
err := cMap.ValidateClusterMap()
assert.NoError(t, err)
})
t.Run("all clusters", func(t *testing.T) {
clusters := cMap.AllClusters()
assert.Len(t, clusters, 4)