diff --git a/staging/src/k8s.io/apiserver/pkg/server/config.go b/staging/src/k8s.io/apiserver/pkg/server/config.go index d9bfc8a706f..b9206370532 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/config.go +++ b/staging/src/k8s.io/apiserver/pkg/server/config.go @@ -614,11 +614,11 @@ func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*G if err != nil { return nil, err } - // TODO: Once we get rid of /healthz consider changing this to post-start-hook. - err = s.addReadyzChecks(healthz.NewInformerSyncHealthz(c.SharedInformerFactory)) - if err != nil { - return nil, err - } + } + // TODO: Once we get rid of /healthz consider changing this to post-start-hook. + err := s.addReadyzChecks(healthz.NewInformerSyncHealthz(c.SharedInformerFactory)) + if err != nil { + return nil, err } } diff --git a/test/e2e/apimachinery/BUILD b/test/e2e/apimachinery/BUILD index f57debecae8..31d7e02a218 100644 --- a/test/e2e/apimachinery/BUILD +++ b/test/e2e/apimachinery/BUILD @@ -21,6 +21,7 @@ go_library( "framework.go", "garbage_collector.go", "generated_clientset.go", + "health_handlers.go", "namespace.go", "protocol.go", "resource_quota.go", @@ -62,6 +63,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", diff --git a/test/e2e/apimachinery/health_handlers.go b/test/e2e/apimachinery/health_handlers.go new file mode 100644 index 00000000000..b67aa01945c --- /dev/null +++ b/test/e2e/apimachinery/health_handlers.go @@ -0,0 +1,133 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package apimachinery + +import ( + "context" + "fmt" + "strings" + "time" + + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/wait" + clientset "k8s.io/client-go/kubernetes" + restclient "k8s.io/client-go/rest" + "k8s.io/kubernetes/test/e2e/framework" + + "github.com/onsi/ginkgo" +) + +var ( + requiredHealthzChecks = sets.NewString( + "[+]ping ok", + "[+]log ok", + "[+]etcd ok", + "[+]poststarthook/start-kube-apiserver-admission-initializer ok", + "[+]poststarthook/generic-apiserver-start-informers ok", + "[+]poststarthook/start-apiextensions-informers ok", + "[+]poststarthook/start-apiextensions-controllers ok", + "[+]poststarthook/crd-informer-synced ok", + "[+]poststarthook/bootstrap-controller ok", + "[+]poststarthook/scheduling/bootstrap-system-priority-classes ok", + "[+]poststarthook/start-cluster-authentication-info-controller ok", + "[+]poststarthook/start-kube-aggregator-informers ok", + "[+]poststarthook/apiservice-registration-controller ok", + "[+]poststarthook/apiservice-status-available-controller ok", + "[+]poststarthook/kube-apiserver-autoregistration ok", + "[+]autoregister-completion ok", + "[+]poststarthook/apiservice-openapi-controller ok", + ) + requiredLivezChecks = sets.NewString( + "[+]ping ok", + "[+]log ok", + "[+]etcd ok", + "[+]poststarthook/start-kube-apiserver-admission-initializer ok", + "[+]poststarthook/generic-apiserver-start-informers ok", + "[+]poststarthook/start-apiextensions-informers ok", + "[+]poststarthook/start-apiextensions-controllers ok", + "[+]poststarthook/crd-informer-synced ok", + "[+]poststarthook/bootstrap-controller ok", + "[+]poststarthook/scheduling/bootstrap-system-priority-classes ok", + "[+]poststarthook/start-cluster-authentication-info-controller ok", + "[+]poststarthook/start-kube-aggregator-informers ok", + "[+]poststarthook/apiservice-registration-controller ok", + "[+]poststarthook/apiservice-status-available-controller ok", + "[+]poststarthook/kube-apiserver-autoregistration ok", + "[+]autoregister-completion ok", + "[+]poststarthook/apiservice-openapi-controller ok", + ) + requiredReadyzChecks = sets.NewString( + "[+]ping ok", + "[+]log ok", + "[+]etcd ok", + "[+]informer-sync ok", + "[+]poststarthook/start-kube-apiserver-admission-initializer ok", + "[+]poststarthook/generic-apiserver-start-informers ok", + "[+]poststarthook/start-apiextensions-informers ok", + "[+]poststarthook/start-apiextensions-controllers ok", + "[+]poststarthook/crd-informer-synced ok", + "[+]poststarthook/bootstrap-controller ok", + "[+]poststarthook/scheduling/bootstrap-system-priority-classes ok", + "[+]poststarthook/start-cluster-authentication-info-controller ok", + "[+]poststarthook/start-kube-aggregator-informers ok", + "[+]poststarthook/apiservice-registration-controller ok", + "[+]poststarthook/apiservice-status-available-controller ok", + "[+]poststarthook/kube-apiserver-autoregistration ok", + "[+]autoregister-completion ok", + "[+]poststarthook/apiservice-openapi-controller ok", + ) +) + +func testPath(client clientset.Interface, path string, requiredChecks sets.String) error { + var result restclient.Result + err := wait.Poll(100*time.Millisecond, 30*time.Second, func() (bool, error) { + result = client.CoreV1().RESTClient().Get().RequestURI(path).Do(context.TODO()) + status := 0 + result.StatusCode(&status) + return status == 200, nil + }) + if err != nil { + return err + } + body, err := result.Raw() + if err != nil { + return err + } + checks := sets.NewString(strings.Split(string(body), "\n")...) + if missing := requiredChecks.Difference(checks); missing.Len() > 0 { + return fmt.Errorf("missing required %s checks: %v in: %s", path, missing, string(body)) + } + return nil +} + +var _ = SIGDescribe("health handlers", func() { + f := framework.NewDefaultFramework("health") + + ginkgo.It("should contain necessary checks", func() { + ginkgo.By("/health") + err := testPath(f.ClientSet, "/healthz?verbose=1", requiredHealthzChecks) + framework.ExpectNoError(err) + + ginkgo.By("/livez") + err = testPath(f.ClientSet, "/livez?verbose=1", requiredLivezChecks) + framework.ExpectNoError(err) + + ginkgo.By("/readyz") + err = testPath(f.ClientSet, "/readyz?verbose=1", requiredReadyzChecks) + framework.ExpectNoError(err) + }) +})