@@ -3,6 +3,7 @@ package internal
33
44import (
55 "bytes"
6+ "encoding/json"
67 "errors"
78 "fmt"
89 "os"
@@ -12,7 +13,9 @@ import (
1213 "time"
1314
1415 yaml "gopkg.in/yaml.v3"
16+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1517 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
18+ "k8s.io/apimachinery/pkg/runtime"
1619 "k8s.io/apimachinery/pkg/util/validation"
1720 "open-cluster-management.io/ocm-kustomize-generator-plugins/internal/types"
1821)
@@ -831,7 +834,7 @@ func (p *Plugin) assertValidConfig() error {
831834 }
832835
833836 // Validate default policy placement settings
834- err := assertValidPlacement (p .PolicyDefaults .Placement , "policyDefaults" , nil )
837+ err := p . assertValidPlacement (p .PolicyDefaults .Placement , "policyDefaults" , nil )
835838 if err != nil {
836839 return err
837840 }
@@ -1061,14 +1064,14 @@ func (p *Plugin) assertValidConfig() error {
10611064 }
10621065 }
10631066
1064- err := assertValidPlacement (policy .Placement , fmt .Sprintf ("policy %s" , policy .Name ), & plCount )
1067+ err := p . assertValidPlacement (policy .Placement , fmt .Sprintf ("policy %s" , policy .Name ), & plCount )
10651068 if err != nil {
10661069 return err
10671070 }
10681071 }
10691072
10701073 // Validate default policy set placement settings
1071- err = assertValidPlacement (p .PolicySetDefaults .Placement , "policySetDefaults" , nil )
1074+ err = p . assertValidPlacement (p .PolicySetDefaults .Placement , "policySetDefaults" , nil )
10721075 if err != nil {
10731076 return err
10741077 }
@@ -1099,7 +1102,7 @@ func (p *Plugin) assertValidConfig() error {
10991102 seenPlcset [plcset .Name ] = true
11001103
11011104 // Validate policy set Placement settings
1102- err := assertValidPlacement (plcset .Placement , fmt .Sprintf ("policySet %s" , plcset .Name ), & plCount )
1105+ err := p . assertValidPlacement (plcset .Placement , fmt .Sprintf ("policySet %s" , plcset .Name ), & plCount )
11031106 if err != nil {
11041107 return err
11051108 }
@@ -1120,7 +1123,7 @@ func (p *Plugin) assertValidConfig() error {
11201123}
11211124
11221125// assertValidPlacement is a helper for assertValidConfig to verify placement configurations
1123- func assertValidPlacement (
1126+ func ( p * Plugin ) assertValidPlacement (
11241127 placement types.PlacementConfig ,
11251128 path string ,
11261129 plCount * struct {
@@ -1237,6 +1240,26 @@ func assertValidPlacement(
12371240 }
12381241 }
12391242
1243+ if len (placement .ClusterSelectors ) > 0 && len (placement .ClusterSelector ) > 0 {
1244+ return fmt .Errorf ("cannot use both clusterSelector and clusterSelectors in %s placement config " +
1245+ "(clusterSelector is recommended since it matches the actual placement field)" , path )
1246+ }
1247+
1248+ // Determine which selectors to use
1249+ var resolvedSelectors map [string ]interface {}
1250+ if len (placement .ClusterSelectors ) > 0 {
1251+ resolvedSelectors = placement .ClusterSelectors
1252+ } else if len (placement .ClusterSelector ) > 0 {
1253+ resolvedSelectors = placement .ClusterSelector
1254+ } else if len (placement .LabelSelector ) > 0 {
1255+ resolvedSelectors = placement .LabelSelector
1256+ }
1257+
1258+ _ , err := p .generateSelector (resolvedSelectors )
1259+ if err != nil {
1260+ return fmt .Errorf ("%s placement has invalid selectors: %w" , path , err )
1261+ }
1262+
12401263 return nil
12411264}
12421265
@@ -1530,32 +1553,19 @@ func (p *Plugin) createPlacement(
15301553 }
15311554
15321555 // Determine which selectors to use
1533- var resolvedSelectors map [string ]string
1556+ var resolvedSelectors map [string ]interface {}
15341557 if len (placementConfig .ClusterSelectors ) > 0 {
15351558 resolvedSelectors = placementConfig .ClusterSelectors
1559+ } else if len (placementConfig .ClusterSelector ) > 0 {
1560+ resolvedSelectors = placementConfig .ClusterSelector
15361561 } else if len (placementConfig .LabelSelector ) > 0 {
15371562 resolvedSelectors = placementConfig .LabelSelector
15381563 }
15391564
1540- // Sort the keys so that the match expressions can be ordered based on the label name
1541- keys := make ([]string , 0 , len (resolvedSelectors ))
1542- for key := range resolvedSelectors {
1543- keys = append (keys , key )
1544- }
1545- sort .Strings (keys )
1546-
1547- matchExpressions := []map [string ]interface {}{}
1548- for _ , label := range keys {
1549- matchExpression := map [string ]interface {}{
1550- "key" : label ,
1551- }
1552- if resolvedSelectors [label ] == "" {
1553- matchExpression ["operator" ] = "Exists"
1554- } else {
1555- matchExpression ["operator" ] = "In"
1556- matchExpression ["values" ] = []string {resolvedSelectors [label ]}
1557- }
1558- matchExpressions = append (matchExpressions , matchExpression )
1565+ // Build cluster selector object
1566+ selectorObj , err := p .generateSelector (resolvedSelectors )
1567+ if err != nil {
1568+ return "" , err
15591569 }
15601570
15611571 if p .usingPlR {
@@ -1567,9 +1577,7 @@ func (p *Plugin) createPlacement(
15671577 "namespace" : p .PolicyDefaults .Namespace ,
15681578 },
15691579 "spec" : map [string ]interface {}{
1570- "clusterSelector" : map [string ]interface {}{
1571- "matchExpressions" : matchExpressions ,
1572- },
1580+ "clusterSelector" : selectorObj ,
15731581 },
15741582 }
15751583 } else {
@@ -1584,9 +1592,7 @@ func (p *Plugin) createPlacement(
15841592 "predicates" : []map [string ]interface {}{
15851593 {
15861594 "requiredClusterSelector" : map [string ]interface {}{
1587- "labelSelector" : map [string ]interface {}{
1588- "matchExpressions" : matchExpressions ,
1589- },
1595+ "labelSelector" : selectorObj ,
15901596 },
15911597 },
15921598 },
@@ -1621,6 +1627,60 @@ func (p *Plugin) createPlacement(
16211627 return
16221628}
16231629
1630+ // generateSelector determines the type of input and creates a map of selectors to be used in either the
1631+ // clusterSelector or labelSelector field
1632+ func (p * Plugin ) generateSelector (
1633+ resolvedSelectors map [string ]interface {},
1634+ ) (map [string ]interface {}, error ) {
1635+ if resolvedSelectors == nil {
1636+ return map [string ]interface {}{"matchExpressions" : []interface {}{}}, nil
1637+ }
1638+
1639+ resolvedSelectorsJSON , err := json .Marshal (resolvedSelectors )
1640+ if err != nil {
1641+ return nil , err
1642+ }
1643+
1644+ resolvedSelectorsLS := metav1.LabelSelector {}
1645+ decoder := json .NewDecoder (bytes .NewReader (resolvedSelectorsJSON ))
1646+ decoder .DisallowUnknownFields ()
1647+
1648+ err = decoder .Decode (& resolvedSelectorsLS )
1649+ if err != nil {
1650+ resolvedSelectorsLS = metav1.LabelSelector {}
1651+
1652+ // Check if it's a legacy selector
1653+ for label , value := range resolvedSelectors {
1654+ valueStr , ok := value .(string )
1655+ if ! ok {
1656+ return nil , fmt .Errorf (
1657+ "the input is not a valid label selector or key-value label matching map" ,
1658+ )
1659+ }
1660+
1661+ lsReq := metav1.LabelSelectorRequirement {Key : label }
1662+
1663+ if valueStr == "" {
1664+ lsReq .Operator = metav1 .LabelSelectorOpExists
1665+ } else {
1666+ lsReq .Operator = metav1 .LabelSelectorOpIn
1667+ lsReq .Values = []string {valueStr }
1668+ }
1669+
1670+ resolvedSelectorsLS .MatchExpressions = append (resolvedSelectorsLS .MatchExpressions , lsReq )
1671+ }
1672+
1673+ resolved , err := runtime .DefaultUnstructuredConverter .ToUnstructured (& resolvedSelectorsLS )
1674+ if err != nil {
1675+ panic (err )
1676+ }
1677+
1678+ return resolved , nil
1679+ }
1680+
1681+ return resolvedSelectors , nil
1682+ }
1683+
16241684// createPlacementBinding creates a placement binding for the input placement, policies and policy sets by
16251685// writing it to the policy generator's output buffer. An error is returned if the placement binding
16261686// cannot be created.
0 commit comments