@@ -1529,33 +1529,29 @@ func (p *Plugin) createPlacement(
15291529 return
15301530 }
15311531
1532+ if len (placementConfig .ClusterSelectors ) > 0 && len (placementConfig .ClusterSelector ) > 0 {
1533+ return "" , fmt .Errorf ("cannot use both clusterSelectors and clusterSelector in placement config" )
1534+ }
1535+
15321536 // Determine which selectors to use
1533- var resolvedSelectors map [string ]string
1537+ var resolvedSelectors map [string ]interface {}
15341538 if len (placementConfig .ClusterSelectors ) > 0 {
15351539 resolvedSelectors = placementConfig .ClusterSelectors
1540+ } else if len (placementConfig .ClusterSelector ) > 0 {
1541+ resolvedSelectors = placementConfig .ClusterSelector
15361542 } else if len (placementConfig .LabelSelector ) > 0 {
15371543 resolvedSelectors = placementConfig .LabelSelector
15381544 }
15391545
1540- // Sort the keys so that the match expressions can be ordered based on the label name
1546+ // build cluster selector object
15411547 keys := make ([]string , 0 , len (resolvedSelectors ))
15421548 for key := range resolvedSelectors {
15431549 keys = append (keys , key )
15441550 }
1545- sort .Strings (keys )
15461551
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 )
1552+ selectorObj , err := p .generateClusterSelector (resolvedSelectors , keys )
1553+ if err != nil {
1554+ return "" , err
15591555 }
15601556
15611557 if p .usingPlR {
@@ -1567,9 +1563,7 @@ func (p *Plugin) createPlacement(
15671563 "namespace" : p .PolicyDefaults .Namespace ,
15681564 },
15691565 "spec" : map [string ]interface {}{
1570- "clusterSelector" : map [string ]interface {}{
1571- "matchExpressions" : matchExpressions ,
1572- },
1566+ "clusterSelector" : selectorObj ,
15731567 },
15741568 }
15751569 } else {
@@ -1584,9 +1578,7 @@ func (p *Plugin) createPlacement(
15841578 "predicates" : []map [string ]interface {}{
15851579 {
15861580 "requiredClusterSelector" : map [string ]interface {}{
1587- "labelSelector" : map [string ]interface {}{
1588- "matchExpressions" : matchExpressions ,
1589- },
1581+ "labelSelector" : selectorObj ,
15901582 },
15911583 },
15921584 },
@@ -1621,6 +1613,127 @@ func (p *Plugin) createPlacement(
16211613 return
16221614}
16231615
1616+ func (p * Plugin ) generateClusterSelector (
1617+ resolvedSelectors map [string ]interface {},
1618+ keys []string ,
1619+ ) (map [string ]interface {}, error ) {
1620+ // Determine cluster selector type
1621+ var selectorType string
1622+ var usedSelectors []string
1623+ var legacy bool
1624+
1625+ for key := range resolvedSelectors {
1626+ if key == "matchExpressions" {
1627+ usedSelectors = append (usedSelectors , key )
1628+ } else if key == "matchLabels" {
1629+ usedSelectors = append (usedSelectors , key )
1630+ } else if ! legacy {
1631+ usedSelectors = append (usedSelectors , "legacy" )
1632+ legacy = true
1633+ }
1634+ }
1635+
1636+ // Determine cluster selector type
1637+ if len (usedSelectors ) > 1 {
1638+ return nil , fmt .Errorf ("too many placement selectors: %s" , strings .Join (usedSelectors , ", " ))
1639+ } else if len (usedSelectors ) == 1 {
1640+ selectorType = usedSelectors [0 ]
1641+ }
1642+
1643+ if selectorType == "matchLabels" && p .usingPlR {
1644+ return nil , fmt .Errorf ("placementRule does not support matchLabels" )
1645+ }
1646+
1647+ // build cluster selector object
1648+ var selector interface {}
1649+
1650+ if selectorType == "matchExpressions" {
1651+ matchExpressions := []map [string ]interface {}{}
1652+ // complete selector provided, so just verify it casts properly
1653+ for _ , matchExpression := range resolvedSelectors [selectorType ].([]interface {}) {
1654+ matchExpression , ok := matchExpression .(map [string ]interface {})
1655+ if ! ok {
1656+ return nil ,
1657+ fmt .Errorf (
1658+ "%s selector has non-map value in list: %s" ,
1659+ selectorType ,
1660+ fmt .Sprint (resolvedSelectors [selectorType ]),
1661+ )
1662+ }
1663+
1664+ // verify matchExpressions matches desired format
1665+ _ , keyExists := matchExpression ["key" ].(string )
1666+ _ , opExists := matchExpression ["operator" ].(string )
1667+
1668+ if ! keyExists || ! opExists {
1669+ return nil ,
1670+ fmt .Errorf (
1671+ "matchExpression formatted incorrectly: %s" ,
1672+ fmt .Sprint (matchExpression ),
1673+ )
1674+ }
1675+
1676+ _ , valExists := matchExpression ["values" ]
1677+ if valExists {
1678+ _ , valFormat := matchExpression ["values" ].([]string )
1679+ if ! valFormat {
1680+ return nil ,
1681+ fmt .Errorf (
1682+ "matchExpression formatted incorrectly: %s" ,
1683+ fmt .Sprint (matchExpression ),
1684+ )
1685+ }
1686+ }
1687+
1688+ matchExpressions = append (matchExpressions , matchExpression )
1689+ }
1690+
1691+ selector = matchExpressions
1692+ } else if selectorType == "matchLabels" {
1693+ // complete selector provided, so just verify it casts properly
1694+ matchLabels , ok := resolvedSelectors [selectorType ].(map [string ]interface {})
1695+ if ! ok {
1696+ return nil ,
1697+ fmt .Errorf (
1698+ "%s selector has non-map value: %s" ,
1699+ selectorType ,
1700+ fmt .Sprint (resolvedSelectors [selectorType ]),
1701+ )
1702+ }
1703+ selector = matchLabels
1704+ } else { // legacy cluster selection method, convert key/val to matchExpressions
1705+ // Sort the keys so that the match expressions can be ordered based on the label name
1706+ sort .Strings (keys )
1707+ matchExpressions := []map [string ]interface {}{}
1708+
1709+ for _ , label := range keys {
1710+ matchExpression := map [string ]interface {}{
1711+ "key" : label ,
1712+ }
1713+ if resolvedSelectors [label ] == "" {
1714+ matchExpression ["operator" ] = "Exists"
1715+ } else {
1716+ matchExpression ["operator" ] = "In"
1717+ value , ok := resolvedSelectors [label ].(string )
1718+ if ! ok {
1719+ return nil ,
1720+ fmt .Errorf (
1721+ "legacy placement value has non-string type as value: %s" ,
1722+ fmt .Sprint (resolvedSelectors [label ]),
1723+ )
1724+ }
1725+ matchExpression ["values" ] = []string {value }
1726+ }
1727+ matchExpressions = append (matchExpressions , matchExpression )
1728+ }
1729+ // legacy selector has been converted to matchExpressions
1730+ selectorType = "matchExpressions"
1731+ selector = matchExpressions
1732+ }
1733+
1734+ return map [string ]interface {}{selectorType : selector }, nil
1735+ }
1736+
16241737// createPlacementBinding creates a placement binding for the input placement, policies and policy sets by
16251738// writing it to the policy generator's output buffer. An error is returned if the placement binding
16261739// cannot be created.
0 commit comments