Merge pull request #67747 from smarterclayton/fix_apiservice

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions here: https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md.

Give APIServer pretty column output

Simple server side render that prints the implementing service (if any)
and the available condition.

```
$ kubectl get apiservice
NAME                               SERVICE                      AVAILABLE                 AGE
v1.                                Local                        True                      10m
v1.apps                            Local                        True                      10m
v1.authentication.k8s.io           Local                        True                      10m
v2beta1.autoscaling                Local                        True                      10m
v1beta1.metrics                    kube-system/metrics-server   False (DiscoveryFailed)   10m
```

@liggitt @deads2k helps to debug why controllers block (aggregate api is down)

```release-note
`kubectl get apiservice` now shows the target service and whether the service is available
```
This commit is contained in:
Kubernetes Submit Queue 2018-08-29 18:53:19 -07:00 committed by GitHub
commit 72ef97a141
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 69 additions and 0 deletions

View File

@ -658,6 +658,10 @@
"ImportPath": "k8s.io/apimachinery/pkg/api/meta",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/meta/table",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/resource",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
@ -758,6 +762,10 @@
"ImportPath": "k8s.io/apimachinery/pkg/util/diff",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/duration",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/errors",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

View File

@ -11,7 +11,10 @@ go_library(
importmap = "k8s.io/kubernetes/vendor/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd",
importpath = "k8s.io/kube-aggregator/pkg/registry/apiservice/etcd",
deps = [
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/meta/table:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",

View File

@ -18,8 +18,12 @@ package etcd
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/api/meta"
metatable "k8s.io/apimachinery/pkg/api/meta/table"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
@ -53,6 +57,60 @@ func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) *REST
return &REST{store}
}
var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
func (c *REST) ConvertToTable(ctx context.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1beta1.Table, error) {
table := &metav1beta1.Table{
ColumnDefinitions: []metav1beta1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name", Description: swaggerMetadataDescriptions["name"]},
{Name: "Service", Type: "string", Description: "The reference to the service that hosts this API endpoint."},
{Name: "Available", Type: "string", Description: "Whether this service is available."},
{Name: "Age", Type: "string", Description: swaggerMetadataDescriptions["creationTimestamp"]},
},
}
if m, err := meta.ListAccessor(obj); err == nil {
table.ResourceVersion = m.GetResourceVersion()
table.SelfLink = m.GetSelfLink()
table.Continue = m.GetContinue()
} else {
if m, err := meta.CommonAccessor(obj); err == nil {
table.ResourceVersion = m.GetResourceVersion()
table.SelfLink = m.GetSelfLink()
}
}
var err error
table.Rows, err = metatable.MetaToTableRow(obj, func(obj runtime.Object, m metav1.Object, name, age string) ([]interface{}, error) {
svc := obj.(*apiregistration.APIService)
service := "Local"
if svc.Spec.Service != nil {
service = fmt.Sprintf("%s/%s", svc.Spec.Service.Namespace, svc.Spec.Service.Name)
}
status := string(apiregistration.ConditionUnknown)
if condition := getCondition(svc.Status.Conditions, "Available"); condition != nil {
switch {
case condition.Status == apiregistration.ConditionTrue:
status = string(condition.Status)
case len(condition.Reason) > 0:
status = fmt.Sprintf("%s (%s)", condition.Status, condition.Reason)
default:
status = string(condition.Status)
}
}
return []interface{}{name, service, status, age}, nil
})
return table, err
}
func getCondition(conditions []apiregistration.APIServiceCondition, conditionType apiregistration.APIServiceConditionType) *apiregistration.APIServiceCondition {
for i, condition := range conditions {
if condition.Type == conditionType {
return &conditions[i]
}
}
return nil
}
// NewStatusREST makes a RESTStorage for status that has more limited options.
// It is based on the original REST so that we can share the same underlying store
func NewStatusREST(scheme *runtime.Scheme, rest *REST) *StatusREST {