Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automated cherry pick of #32194 #32182 #32196 #32464

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions cluster/saltbase/salt/rescheduler/rescheduler.manifest
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
apiVersion: v1
kind: Pod
metadata:
name: rescheduler-v0.2.0
name: rescheduler-v0.2.1
namespace: kube-system
labels:
k8s-app: rescheduler
version: v0.2.0
version: v0.2.1
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "Rescheduler"
spec:
hostNetwork: true
containers:
- image: gcr.io/google_containers/rescheduler:v0.2.0
- image: gcr.io/google_containers/rescheduler:v0.2.1
name: rescheduler
volumeMounts:
- mountPath: /var/log/rescheduler.log
Expand Down
42 changes: 35 additions & 7 deletions pkg/client/typed/discovery/discovery_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"encoding/json"
"fmt"
"net/url"
"sort"
"strings"

"github.com/emicklei/go-restful/swagger"
Expand All @@ -31,7 +32,6 @@ import (
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/serializer"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/version"
)

Expand Down Expand Up @@ -149,9 +149,8 @@ func (d *DiscoveryClient) ServerResourcesForGroupVersion(groupVersion string) (r
// ignore 403 or 404 error to be compatible with an v1.0 server.
if groupVersion == "v1" && (errors.IsNotFound(err) || errors.IsForbidden(err)) {
return resources, nil
} else {
return nil, err
}
return nil, err
}
return resources, nil
}
Expand All @@ -174,6 +173,29 @@ func (d *DiscoveryClient) ServerResources() (map[string]*unversioned.APIResource
return result, nil
}

// ErrGroupDiscoveryFailed is returned if one or more API groups fail to load.
type ErrGroupDiscoveryFailed struct {
// Groups is a list of the groups that failed to load and the error cause
Groups map[unversioned.GroupVersion]error
}

// Error implements the error interface
func (e *ErrGroupDiscoveryFailed) Error() string {
var groups []string
for k, v := range e.Groups {
groups = append(groups, fmt.Sprintf("%s: %v", k, v))
}
sort.Strings(groups)
return fmt.Sprintf("unable to retrieve the complete list of server APIs: %s", strings.Join(groups, ", "))
}

// IsGroupDiscoveryFailedError returns true if the provided error indicates the server was unable to discover
// a complete list of APIs for the client to use.
func IsGroupDiscoveryFailedError(err error) bool {
_, ok := err.(*ErrGroupDiscoveryFailed)
return err != nil && ok
}

// serverPreferredResources returns the supported resources with the version preferred by the
// server. If namespaced is true, only namespaced resources will be returned.
func (d *DiscoveryClient) serverPreferredResources(namespaced bool) ([]unversioned.GroupVersionResource, error) {
Expand All @@ -183,15 +205,18 @@ func (d *DiscoveryClient) serverPreferredResources(namespaced bool) ([]unversion
return results, err
}

allErrs := []error{}
var failedGroups map[unversioned.GroupVersion]error
for _, apiGroup := range serverGroupList.Groups {
preferredVersion := apiGroup.PreferredVersion
groupVersion := unversioned.GroupVersion{Group: apiGroup.Name, Version: preferredVersion.Version}
apiResourceList, err := d.ServerResourcesForGroupVersion(preferredVersion.GroupVersion)
if err != nil {
allErrs = append(allErrs, err)
if failedGroups == nil {
failedGroups = make(map[unversioned.GroupVersion]error)
}
failedGroups[groupVersion] = err
continue
}
groupVersion := unversioned.GroupVersion{Group: apiGroup.Name, Version: preferredVersion.Version}
for _, apiResource := range apiResourceList.APIResources {
// ignore the root scoped resources if "namespaced" is true.
if namespaced && !apiResource.Namespaced {
Expand All @@ -203,7 +228,10 @@ func (d *DiscoveryClient) serverPreferredResources(namespaced bool) ([]unversion
results = append(results, groupVersion.WithResource(apiResource.Name))
}
}
return results, utilerrors.NewAggregate(allErrs)
if len(failedGroups) > 0 {
return results, &ErrGroupDiscoveryFailed{Groups: failedGroups}
}
return results, nil
}

// ServerPreferredResources returns the supported resources with the version preferred by the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,3 +320,137 @@ func TestGetSwaggerSchemaFail(t *testing.T) {
t.Errorf("expected an error, got %v", err)
}
}

func TestGetServerPreferredResources(t *testing.T) {
stable := unversioned.APIResourceList{
GroupVersion: "v1",
APIResources: []unversioned.APIResource{
{Name: "pods", Namespaced: true, Kind: "Pod"},
{Name: "services", Namespaced: true, Kind: "Service"},
{Name: "namespaces", Namespaced: false, Kind: "Namespace"},
},
}
/*beta := unversioned.APIResourceList{
GroupVersion: "extensions/v1",
APIResources: []unversioned.APIResource{
{Name: "deployments", Namespaced: true, Kind: "Deployment"},
{Name: "ingresses", Namespaced: true, Kind: "Ingress"},
{Name: "jobs", Namespaced: true, Kind: "Job"},
},
}*/
tests := []struct {
resourcesList *unversioned.APIResourceList
response func(w http.ResponseWriter, req *http.Request)
expectErr func(err error) bool
}{
{
resourcesList: &stable,
expectErr: IsGroupDiscoveryFailedError,
response: func(w http.ResponseWriter, req *http.Request) {
var list interface{}
switch req.URL.Path {
case "/apis/extensions/v1beta1":
w.WriteHeader(http.StatusInternalServerError)
return
case "/api/v1":
list = &stable
case "/api":
list = &unversioned.APIVersions{
Versions: []string{
"v1",
},
}
case "/apis":
list = &unversioned.APIGroupList{
Groups: []unversioned.APIGroup{
{
Versions: []unversioned.GroupVersionForDiscovery{
{GroupVersion: "extensions/v1beta1"},
},
},
},
}
default:
t.Logf("unexpected request: %s", req.URL.Path)
w.WriteHeader(http.StatusNotFound)
return
}
output, err := json.Marshal(list)
if err != nil {
t.Errorf("unexpected encoding error: %v", err)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(output)
},
},
{
resourcesList: nil,
expectErr: IsGroupDiscoveryFailedError,
response: func(w http.ResponseWriter, req *http.Request) {
var list interface{}
switch req.URL.Path {
case "/apis/extensions/v1beta1":
w.WriteHeader(http.StatusInternalServerError)
return
case "/api/v1":
w.WriteHeader(http.StatusInternalServerError)
case "/api":
list = &unversioned.APIVersions{
Versions: []string{
"v1",
},
}
case "/apis":
list = &unversioned.APIGroupList{
Groups: []unversioned.APIGroup{
{
Versions: []unversioned.GroupVersionForDiscovery{
{GroupVersion: "extensions/v1beta1"},
},
},
},
}
default:
t.Logf("unexpected request: %s", req.URL.Path)
w.WriteHeader(http.StatusNotFound)
return
}
output, err := json.Marshal(list)
if err != nil {
t.Errorf("unexpected encoding error: %v", err)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(output)
},
},
/*{
resourcesList: &stable,
},*/
}
for _, test := range tests {
server := httptest.NewServer(http.HandlerFunc(test.response))
defer server.Close()

client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
got, err := client.ServerPreferredResources()
if test.expectErr != nil {
if err == nil {
t.Error("unexpected non-error")
}

continue
}
if err != nil {
t.Errorf("unexpected error: %v", err)
continue
}
if !reflect.DeepEqual(got, test.resourcesList) {
t.Errorf("expected:\n%v\ngot:\n%v\n", test.resourcesList, got)
}
server.Close()
}
}
2 changes: 1 addition & 1 deletion test/e2e/rescheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var _ = framework.KubeDescribe("Rescheduler [Serial]", func() {
var totalMillicores int

BeforeEach(func() {
framework.SkipUnlessProviderIs("gce")
framework.SkipUnlessProviderIs("gce", "gke")
ns = f.Namespace.Name
nodes := framework.GetReadySchedulableNodesOrDie(f.Client)
nodeCount := len(nodes.Items)
Expand Down