Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions pkg/apis/scheduling/v2alpha2/podgroup_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
"strings"

"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -67,6 +68,9 @@ func (_ *PodGroup) ValidateDelete(ctx context.Context, obj runtime.Object) (admi
func validateSubGroups(subGroups []SubGroup) error {
subGroupMap := map[string]*SubGroup{}
for _, subGroup := range subGroups {
if err := validateSubGroupName(subGroup.Name); err != nil {
return err
}
if subGroupMap[subGroup.Name] != nil {
return fmt.Errorf("duplicate subgroup name %s", subGroup.Name)
}
Expand All @@ -83,6 +87,16 @@ func validateSubGroups(subGroups []SubGroup) error {
return nil
}

func validateSubGroupName(name string) error {
if name == "" {
return nil
}
if strings.ToLower(name) != name {
return fmt.Errorf("subgroup name %q must be lowercase", name)
}
return nil
}

func validateParent(subGroupMap map[string]*SubGroup) error {
for _, subGroup := range subGroupMap {
if subGroup.Parent == nil {
Expand Down
77 changes: 49 additions & 28 deletions pkg/apis/scheduling/v2alpha2/podgroup_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,29 @@ func TestValidateSubGroups(t *testing.T) {
{
name: "Valid DAG single root",
subGroups: []SubGroup{
{Name: "A", MinMember: 1},
{Name: "B", Parent: ptr.To("A"), MinMember: 1},
{Name: "C", Parent: ptr.To("B"), MinMember: 1},
{Name: "a", MinMember: 1},
{Name: "b", Parent: ptr.To("a"), MinMember: 1},
{Name: "c", Parent: ptr.To("b"), MinMember: 1},
},
wantErr: nil,
},
{
name: "Valid DAG multiple roots",
subGroups: []SubGroup{
{Name: "A", MinMember: 1},
{Name: "B", MinMember: 1},
{Name: "C", Parent: ptr.To("A"), MinMember: 1},
{Name: "D", Parent: ptr.To("B"), MinMember: 1},
{Name: "a", MinMember: 1},
{Name: "b", MinMember: 1},
{Name: "c", Parent: ptr.To("a"), MinMember: 1},
{Name: "d", Parent: ptr.To("b"), MinMember: 1},
},
wantErr: nil,
},
{
name: "Missing parent",
subGroups: []SubGroup{
{Name: "A", MinMember: 1},
{Name: "B", Parent: ptr.To("X"), MinMember: 1}, // parent X does not exist
{Name: "a", MinMember: 1},
{Name: "b", Parent: ptr.To("x"), MinMember: 1}, // parent x does not exist
},
wantErr: errors.New("parent X of B was not found"),
wantErr: errors.New("parent x of b was not found"),
},
{
name: "Empty list",
Expand All @@ -51,47 +51,68 @@ func TestValidateSubGroups(t *testing.T) {
{
name: "Duplicate subgroup names",
subGroups: []SubGroup{
{Name: "A", MinMember: 1},
{Name: "A", MinMember: 1}, // duplicate
{Name: "a", MinMember: 1},
{Name: "a", MinMember: 1}, // duplicate
},
wantErr: errors.New("duplicate subgroup name A"),
wantErr: errors.New("duplicate subgroup name a"),
},
{
name: "Cycle in graph (A -> B -> C -> A) - duplicate subgroup name",
name: "Cycle in graph (a -> b -> c -> a) - duplicate subgroup name",
subGroups: []SubGroup{
{Name: "A", MinMember: 1},
{Name: "B", Parent: ptr.To("A"), MinMember: 1},
{Name: "C", Parent: ptr.To("B"), MinMember: 1},
{Name: "A", Parent: ptr.To("C"), MinMember: 1}, // creates a cycle
{Name: "a", MinMember: 1},
{Name: "b", Parent: ptr.To("a"), MinMember: 1},
{Name: "c", Parent: ptr.To("b"), MinMember: 1},
{Name: "a", Parent: ptr.To("c"), MinMember: 1}, // creates a cycle
},
wantErr: errors.New("duplicate subgroup name A"), // duplicate is caught before cycle
wantErr: errors.New("duplicate subgroup name a"), // duplicate is caught before cycle
},
{
name: "Self-parent subgroup (cycle of length 1)",
subGroups: []SubGroup{
{Name: "A", Parent: ptr.To("A"), MinMember: 1},
{Name: "a", Parent: ptr.To("a"), MinMember: 1},
},
wantErr: errors.New("cycle detected in subgroups"),
},
{
name: "Cycle in graph (A -> B -> C -> A)",
name: "Cycle in graph (a -> b -> c -> a)",
subGroups: []SubGroup{
{Name: "A", Parent: ptr.To("C"), MinMember: 1},
{Name: "B", Parent: ptr.To("A"), MinMember: 1},
{Name: "C", Parent: ptr.To("B"), MinMember: 1}, // creates a cycle
{Name: "a", Parent: ptr.To("c"), MinMember: 1},
{Name: "b", Parent: ptr.To("a"), MinMember: 1},
{Name: "c", Parent: ptr.To("b"), MinMember: 1}, // creates a cycle
},
wantErr: errors.New("cycle detected in subgroups"),
},
{
name: "Multiple disjoint cycles",
subGroups: []SubGroup{
{Name: "A", Parent: ptr.To("B"), MinMember: 1},
{Name: "B", Parent: ptr.To("A"), MinMember: 1}, // cycle A <-> B
{Name: "C", Parent: ptr.To("D"), MinMember: 1},
{Name: "D", Parent: ptr.To("C"), MinMember: 1}, // cycle C <-> D
{Name: "a", Parent: ptr.To("b"), MinMember: 1},
{Name: "b", Parent: ptr.To("a"), MinMember: 1}, // cycle a <-> b
{Name: "c", Parent: ptr.To("d"), MinMember: 1},
{Name: "d", Parent: ptr.To("c"), MinMember: 1}, // cycle c <-> d
},
wantErr: errors.New("cycle detected in subgroups"),
},
{
name: "Mixed case subgroup name",
subGroups: []SubGroup{
{Name: "Subgroup", MinMember: 1},
},
wantErr: errors.New(`subgroup name "Subgroup" must be lowercase`),
},
{
name: "All uppercase subgroup name",
subGroups: []SubGroup{
{Name: "SUBGROUP", MinMember: 1},
},
wantErr: errors.New(`subgroup name "SUBGROUP" must be lowercase`),
},
{
name: "Valid lowercase subgroup name",
subGroups: []SubGroup{
{Name: "subgroup", MinMember: 1},
},
wantErr: nil,
},
}

for _, tt := range tests {
Expand Down
Loading