forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnegotiate.go
117 lines (102 loc) · 3.8 KB
/
negotiate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package clientcmd
import (
"encoding/json"
"fmt"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/client/restclient"
kclient "k8s.io/kubernetes/pkg/client/unversioned"
)
// negotiateVersion queries the server's supported api versions to find a version that both client and server support.
// - If no version is provided, try registered client versions in order of preference.
// - If version is provided, but not default config (explicitly requested via
// commandline flag), and is unsupported by the server, print a warning to
// stderr and try client's registered versions in order of preference.
// - If version is config default, and the server does not support it, return an error.
func negotiateVersion(client *kclient.Client, config *restclient.Config, requestedGV *unversioned.GroupVersion, clientGVs []unversioned.GroupVersion) (*unversioned.GroupVersion, error) {
// Ensure we have a client
var err error
if client == nil {
client, err = kclient.New(config)
if err != nil {
return nil, err
}
}
// Get server versions
serverGVs, err := serverAPIVersions(client, "/oapi")
if err != nil {
return nil, err
}
// Determine our preferred version
preferredGV := copyGroupVersion(requestedGV)
if preferredGV == nil {
preferredGV = copyGroupVersion(config.GroupVersion)
}
// Find a version we can all agree on
matchedGV, err := matchAPIVersion(preferredGV, clientGVs, serverGVs)
if err != nil {
return nil, err
}
// Enforce a match if the preferredGV is the config default
if config.GroupVersion != nil && (*preferredGV == *config.GroupVersion) && (*matchedGV != *config.GroupVersion) {
return nil, fmt.Errorf("server does not support API version %q", config.GroupVersion.String())
}
return matchedGV, err
}
// serverAPIVersions fetches the server versions available from the groupless API at the given prefix
func serverAPIVersions(c *kclient.Client, grouplessPrefix string) ([]unversioned.GroupVersion, error) {
// Get versions doc
body, err := c.Get().AbsPath(grouplessPrefix).Do().Raw()
if err != nil {
return []unversioned.GroupVersion{}, err
}
// Unmarshal
var v unversioned.APIVersions
err = json.Unmarshal(body, &v)
if err != nil {
return []unversioned.GroupVersion{}, fmt.Errorf("got '%s': %v", string(body), err)
}
// Convert to GroupVersion structs
serverAPIVersions := []unversioned.GroupVersion{}
for _, version := range v.Versions {
gv, err := unversioned.ParseGroupVersion(version)
if err != nil {
return []unversioned.GroupVersion{}, err
}
serverAPIVersions = append(serverAPIVersions, gv)
}
return serverAPIVersions, nil
}
func matchAPIVersion(preferredGV *unversioned.GroupVersion, clientGVs []unversioned.GroupVersion, serverGVs []unversioned.GroupVersion) (*unversioned.GroupVersion, error) {
// If version explicitly requested verify that both client and server support it.
// If server does not support warn, but try to negotiate a lower version.
if preferredGV != nil {
if !containsGroupVersion(clientGVs, *preferredGV) {
return nil, fmt.Errorf("client does not support API version %q; client supported API versions: %v", preferredGV, clientGVs)
}
if containsGroupVersion(serverGVs, *preferredGV) {
return preferredGV, nil
}
}
for _, clientGV := range clientGVs {
if containsGroupVersion(serverGVs, clientGV) {
t := clientGV
return &t, nil
}
}
return nil, fmt.Errorf("failed to negotiate an api version; server supports: %v, client supports: %v", serverGVs, clientGVs)
}
func copyGroupVersion(version *unversioned.GroupVersion) *unversioned.GroupVersion {
if version == nil {
return nil
}
versionCopy := *version
return &versionCopy
}
func containsGroupVersion(versions []unversioned.GroupVersion, version unversioned.GroupVersion) bool {
for _, v := range versions {
if v == version {
return true
}
}
return false
}