Skip to content

Commit fd8b731

Browse files
Merge pull request #16219 from mrogers950/discovery_gating
Automatic merge from submit-queue (batch tested with PRs 16089, 16305, 16219, 15934, 16366) Use discovery based version gating for policy commands This PR switches from the policy command version gating with hard-coded 3.7 versions to a solution based on the discovery of supported server resources. Fixes #15831
2 parents ca68fa4 + 546fe74 commit fd8b731

File tree

8 files changed

+478
-48
lines changed

8 files changed

+478
-48
lines changed

pkg/cmd/server/admin/overwrite_bootstrappolicy.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,12 @@ func (o OverwriteBootstrapPolicyOptions) Validate(args []string) error {
100100
}
101101

102102
func (o OverwriteBootstrapPolicyOptions) Complete(f *clientcmd.Factory) error {
103-
oClient, _, err := f.Clients()
103+
_, kclient, err := f.Clients()
104104
if err != nil {
105105
return err
106106
}
107107

108-
return clientcmd.Gate(oClient, "", "3.7.0")
108+
return clientcmd.LegacyPolicyResourceGate(kclient.Discovery())
109109
}
110110

111111
func (o OverwriteBootstrapPolicyOptions) OverwriteBootstrapPolicy() error {

pkg/cmd/util/clientcmd/gating.go

Lines changed: 103 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,126 @@
11
package clientcmd
22

33
import (
4-
"encoding/json"
54
"fmt"
65

7-
"github.com/blang/semver"
6+
"k8s.io/apimachinery/pkg/api/errors"
7+
"k8s.io/apimachinery/pkg/apis/meta/v1"
8+
"k8s.io/apimachinery/pkg/runtime/schema"
9+
"k8s.io/client-go/discovery"
810

9-
"github.com/openshift/origin/pkg/client"
10-
"github.com/openshift/origin/pkg/version"
11+
"github.com/openshift/origin/pkg/authorization/apis/authorization"
1112
)
1213

13-
// Gate returns an error if the server is below minServerVersion or above/equal maxServerVersion.
14-
// To test only for min or only max version, set the other string to the empty value.
15-
func Gate(ocClient *client.Client, minServerVersion, maxServerVersion string) error {
16-
if len(minServerVersion) == 0 && len(maxServerVersion) == 0 {
17-
return fmt.Errorf("No version info passed to gate command")
18-
}
14+
// LegacyPolicyResourceGate returns err if the server does not support the set of legacy policy objects (< 3.7)
15+
func LegacyPolicyResourceGate(client discovery.ServerResourcesInterface) error {
16+
// The server must support the 4 legacy policy objects in either of the GV schemes.
17+
_, all, err := DiscoverGroupVersionResources(client,
18+
schema.GroupVersionResource{
19+
Group: authorization.LegacySchemeGroupVersion.Group,
20+
Version: authorization.LegacySchemeGroupVersion.Version,
21+
Resource: "clusterpolicies",
22+
},
23+
schema.GroupVersionResource{
24+
Group: authorization.LegacySchemeGroupVersion.Group,
25+
Version: authorization.LegacySchemeGroupVersion.Version,
26+
Resource: "clusterpolicybindings",
27+
},
28+
schema.GroupVersionResource{
29+
Group: authorization.LegacySchemeGroupVersion.Group,
30+
Version: authorization.LegacySchemeGroupVersion.Version,
31+
Resource: "policies",
32+
},
33+
schema.GroupVersionResource{
34+
Group: authorization.LegacySchemeGroupVersion.Group,
35+
Version: authorization.LegacySchemeGroupVersion.Version,
36+
Resource: "policybindings",
37+
},
38+
)
1939

20-
ocVersionBody, err := ocClient.Get().AbsPath("/version/openshift").Do().Raw()
2140
if err != nil {
2241
return err
2342
}
24-
ocServerInfo := &version.Info{}
25-
if err := json.Unmarshal(ocVersionBody, ocServerInfo); err != nil {
26-
return err
43+
if all {
44+
return nil
2745
}
28-
ocVersion := ocServerInfo.String()
29-
// skip first chracter as Openshift returns a 'v' preceding the actual
30-
// version string which semver does not grok
31-
semVersion, err := semver.Parse(ocVersion[1:])
46+
_, all, err = DiscoverGroupVersionResources(client,
47+
schema.GroupVersionResource{
48+
Group: authorization.SchemeGroupVersion.Group,
49+
Version: authorization.SchemeGroupVersion.Version,
50+
Resource: "clusterpolicies",
51+
},
52+
schema.GroupVersionResource{
53+
Group: authorization.SchemeGroupVersion.Group,
54+
Version: authorization.SchemeGroupVersion.Version,
55+
Resource: "clusterpolicybindings",
56+
},
57+
schema.GroupVersionResource{
58+
Group: authorization.SchemeGroupVersion.Group,
59+
Version: authorization.SchemeGroupVersion.Version,
60+
Resource: "policies",
61+
},
62+
schema.GroupVersionResource{
63+
Group: authorization.SchemeGroupVersion.Group,
64+
Version: authorization.SchemeGroupVersion.Version,
65+
Resource: "policybindings",
66+
},
67+
)
68+
3269
if err != nil {
33-
return fmt.Errorf("Failed to parse server version %s: %v", ocVersion, err)
70+
return err
71+
}
72+
if all {
73+
return nil
3474
}
35-
// ignore pre-release version info
36-
semVersion.Pre = nil
3775

38-
if len(minServerVersion) > 0 {
39-
min, err := semver.Parse(minServerVersion)
40-
if err != nil {
41-
return fmt.Errorf("Failed to parse min gate version %s: %v", minServerVersion, err)
42-
}
43-
// ignore pre-release version info
44-
min.Pre = nil
45-
if semVersion.LT(min) {
46-
return fmt.Errorf("This command works only with server versions > %s, found %s", minServerVersion, ocVersion)
47-
}
76+
return fmt.Errorf("the server does not support legacy policy resources")
77+
}
78+
79+
// DiscoverGroupVersionResources performs a server resource discovery for each filterGVR, returning a slice of
80+
// GVRs for the matching Resources, and a bool for "all" indicating that each item in filterGVR was found.
81+
func DiscoverGroupVersionResources(client discovery.ServerResourcesInterface, filterGVR ...schema.GroupVersionResource) ([]schema.GroupVersionResource, bool, error) {
82+
if len(filterGVR) == 0 {
83+
return nil, false, fmt.Errorf("at least one GroupVersionResource must be provided")
4884
}
4985

50-
if len(maxServerVersion) > 0 {
51-
max, err := semver.Parse(maxServerVersion)
52-
if err != nil {
53-
return fmt.Errorf("Failed to parse max gate version %s: %v", maxServerVersion, err)
86+
var groupVersionCache = make(map[string]*v1.APIResourceList)
87+
all := true
88+
ret := []schema.GroupVersionResource{}
89+
for i := range filterGVR {
90+
var serverResources *v1.APIResourceList
91+
var err error
92+
93+
// Discover the list of resources for this GVR, with a cache of resources per GV to avoid extra round trips
94+
gv := filterGVR[i].GroupVersion()
95+
if cachedList, ok := groupVersionCache[gv.String()]; ok {
96+
serverResources = cachedList
97+
} else {
98+
serverResources, err = client.ServerResourcesForGroupVersion(gv.String())
99+
if err != nil && errors.IsNotFound(err) {
100+
// Cache an empty resource list when not found, we don't want another discovery
101+
groupVersionCache[gv.String()] = &v1.APIResourceList{}
102+
all = false
103+
continue
104+
}
105+
106+
if err != nil {
107+
return nil, false, err
108+
}
109+
110+
groupVersionCache[gv.String()] = serverResources
54111
}
55-
// ignore pre-release version info
56-
max.Pre = nil
57-
if semVersion.GTE(max) {
58-
return fmt.Errorf("This command works only with server versions < %s, found %s", maxServerVersion, ocVersion)
112+
113+
seen := false
114+
// Add matching resources to the return slice
115+
for _, resource := range serverResources.APIResources {
116+
if filterGVR[i].Resource == resource.Name {
117+
seen = true
118+
ret = append(ret, filterGVR[i])
119+
break
120+
}
59121
}
122+
all = all && seen
60123
}
61124

62-
// OK this is within min/max all good!
63-
return nil
125+
return ret, all, nil
64126
}

0 commit comments

Comments
 (0)