Skip to content

Commit

Permalink
internal/k8s: add check to verify resources exist in cluster
Browse files Browse the repository at this point in the history
Add a discovery client to look up server groups and resources which
allows for a check to be made that a resource type exists before
starting any informers against.

Fixes projectcontour#2219

Signed-off-by: Steve Sloka <[email protected]>
  • Loading branch information
stevesloka committed Jul 10, 2020
1 parent d835474 commit d09e3ed
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 4 deletions.
11 changes: 9 additions & 2 deletions cmd/contour/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,14 @@ func doServe(log logrus.FieldLogger, ctx *serveContext) error {
informerSyncList.InformOnResources(clusterInformerFactory, dynamicHandler, k8s.DefaultResources()...)

if ctx.UseExperimentalServiceAPITypes {
informerSyncList.InformOnResources(clusterInformerFactory,
dynamicHandler, k8s.ServiceAPIResources()...)
for _, s := range k8s.ServiceAPIResources() {
// Check if the resource exists in the API server before setting up the informer.
if !clients.ResourceExists(s) {
log.WithField("InformOnResources", "ExperimentalServiceAPITypes").Warnf("resource %v not found in api server", s.String())
continue
}
informerSyncList.InformOnResources(clusterInformerFactory, dynamicHandler, s)
}
}

// TODO(youngnick): Move this logic out to internal/k8s/informers.go somehow.
Expand Down Expand Up @@ -369,6 +375,7 @@ func doServe(log logrus.FieldLogger, ctx *serveContext) error {
}
factory := clients.NewInformerFactoryForNamespace(ctx.EnvoyServiceNamespace)
informerSyncList.InformOnResources(factory, dynamicServiceHandler, k8s.ServicesResources()...)

g.Add(startInformer(factory, log.WithField("context", "serviceStatusLoadBalancerWatcher")))
log.WithField("envoy-service-name", ctx.EnvoyServiceName).
WithField("envoy-service-namespace", ctx.EnvoyServiceNamespace).
Expand Down
51 changes: 49 additions & 2 deletions internal/k8s/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ package k8s
import (
"time"

"k8s.io/apimachinery/pkg/runtime/schema"

"k8s.io/client-go/discovery"

"k8s.io/client-go/dynamic"
"k8s.io/client-go/dynamic/dynamicinformer"
"k8s.io/client-go/kubernetes"
Expand All @@ -25,8 +29,11 @@ import (

// Clients holds the various API clients required by Contour.
type Clients struct {
core *kubernetes.Clientset
dynamic dynamic.Interface
core *kubernetes.Clientset
dynamic dynamic.Interface
discovery *discovery.DiscoveryClient

apiResources *APIResources
}

// NewClients returns a new set of the various API clients required
Expand All @@ -35,6 +42,7 @@ type Clients struct {
func NewClients(kubeconfig string, inCluster bool) (*Clients, error) {
config, err := newRestConfig(kubeconfig, inCluster)
if err != nil {

return nil, err
}

Expand All @@ -49,6 +57,17 @@ func NewClients(kubeconfig string, inCluster bool) (*Clients, error) {
return nil, err
}

clients.discovery, err = discovery.NewDiscoveryClientForConfig(config)
if err != nil {
return nil, err
}

// populate the apiResources cache
clients.apiResources, err = clients.serverResources()
if err != nil {
return nil, err
}

return &clients, nil
}

Expand Down Expand Up @@ -84,3 +103,31 @@ func (c *Clients) ClientSet() *kubernetes.Clientset {
func (c *Clients) DynamicClient() dynamic.Interface {
return c.dynamic
}

// APIResources holds a map of API, GroupVersionResources, that exist in the Kubernetes cluster
type APIResources struct {
serverResources map[schema.GroupVersionResource]struct{}
}

// serverResources returns the list of all the resources supported
// by the API server. Note that this method guarantees to populate the
// Group and Version fields in the result.
func (c *Clients) serverResources() (*APIResources, error) {
_, apiList, err := c.discovery.ServerGroupsAndResources()
if err != nil {
return nil, err
}

gvrs, err := discovery.GroupVersionResources(apiList)
return &APIResources{
serverResources: gvrs,
}, err
}

// ResourceExists returns true if an GroupVersionResource exists in the cluster.
func (c *Clients) ResourceExists(gvr schema.GroupVersionResource) bool {
if _, ok := c.apiResources.serverResources[gvr]; ok {
return true
}
return false
}
75 changes: 75 additions & 0 deletions internal/k8s/clients_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright Project Contour 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 k8s

import (
"testing"

"k8s.io/apimachinery/pkg/runtime/schema"

"github.com/projectcontour/contour/internal/assert"
)

func TestResourceKindExists(t *testing.T) {
type testcase struct {
gvr schema.GroupVersionResource
apiResourceList map[schema.GroupVersionResource]struct{}
want bool
}

run := func(t *testing.T, name string, tc testcase) {
t.Helper()

t.Run(name, func(t *testing.T) {
t.Helper()

clients := &Clients{
apiResources: &APIResources{
serverResources: tc.apiResourceList,
},
}
got := clients.ResourceExists(tc.gvr)
assert.Equal(t, tc.want, got)
})
}

valid := schema.GroupVersionResource{
Group: "networking.k8s.io",
Version: "v1beta1",
Resource: "ingress",
}

invalid := schema.GroupVersionResource{
Group: "networking.k8s.io",
Version: "v1beta1",
Resource: "ingressclass",
}

run(t, "ingress exist", testcase{
gvr: valid,
want: true,
apiResourceList: map[schema.GroupVersionResource]struct{}{
valid: struct{}{},
},
})

run(t, "ingressclass does not exist", testcase{
gvr: invalid,
want: false,
apiResourceList: map[schema.GroupVersionResource]struct{}{
valid: struct{}{},
},
})

}

0 comments on commit d09e3ed

Please sign in to comment.