diff --git a/provider/kubernetes/client.go b/provider/kubernetes/client.go index 15e22b21a..ca2c8eefd 100644 --- a/provider/kubernetes/client.go +++ b/provider/kubernetes/client.go @@ -9,6 +9,7 @@ import ( "github.com/containous/traefik/log" corev1 "k8s.io/api/core/v1" extensionsv1beta1 "k8s.io/api/extensions/v1beta1" + kubeerror "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/informers" @@ -169,17 +170,11 @@ func (c *clientImpl) GetIngresses() []*extensionsv1beta1.Ingress { // UpdateIngressStatus updates an Ingress with a provided status. func (c *clientImpl) UpdateIngressStatus(namespace, name, ip, hostname string) error { - keyName := namespace + "/" + name - - item, exists, err := c.factories[c.lookupNamespace(namespace)].Extensions().V1beta1().Ingresses().Informer().GetStore().GetByKey(keyName) + ing, err := c.factories[c.lookupNamespace(namespace)].Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).Get(name) if err != nil { - return fmt.Errorf("failed to get ingress %s with error: %v", keyName, err) - } - if !exists { - return fmt.Errorf("failed to update ingress %s because it does not exist", keyName) + return fmt.Errorf("failed to get ingress %s/%s: %v", namespace, name, err) } - ing := item.(*extensionsv1beta1.Ingress) if len(ing.Status.LoadBalancer.Ingress) > 0 { if ing.Status.LoadBalancer.Ingress[0].Hostname == hostname && ing.Status.LoadBalancer.Ingress[0].IP == ip { // If status is already set, skip update @@ -192,40 +187,31 @@ func (c *clientImpl) UpdateIngressStatus(namespace, name, ip, hostname string) e _, err = c.clientset.ExtensionsV1beta1().Ingresses(ingCopy.Namespace).UpdateStatus(ingCopy) if err != nil { - return fmt.Errorf("failed to update ingress status %s with error: %v", keyName, err) + return fmt.Errorf("failed to update ingress status %s/%s: %v", namespace, name, err) } - log.Infof("Updated status on ingress %s", keyName) + log.Infof("Updated status on ingress %s/%s", namespace, name) return nil } // GetService returns the named service from the given namespace. func (c *clientImpl) GetService(namespace, name string) (*corev1.Service, bool, error) { - var service *corev1.Service - item, exists, err := c.factories[c.lookupNamespace(namespace)].Core().V1().Services().Informer().GetStore().GetByKey(namespace + "/" + name) - if item != nil { - service = item.(*corev1.Service) - } - return service, exists, err + service, err := c.factories[c.lookupNamespace(namespace)].Core().V1().Services().Lister().Services(namespace).Get(name) + exist, err := translateNotFoundError(err) + return service, exist, err } // GetEndpoints returns the named endpoints from the given namespace. func (c *clientImpl) GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error) { - var endpoint *corev1.Endpoints - item, exists, err := c.factories[c.lookupNamespace(namespace)].Core().V1().Endpoints().Informer().GetStore().GetByKey(namespace + "/" + name) - if item != nil { - endpoint = item.(*corev1.Endpoints) - } - return endpoint, exists, err + endpoint, err := c.factories[c.lookupNamespace(namespace)].Core().V1().Endpoints().Lister().Endpoints(namespace).Get(name) + exist, err := translateNotFoundError(err) + return endpoint, exist, err } // GetSecret returns the named secret from the given namespace. func (c *clientImpl) GetSecret(namespace, name string) (*corev1.Secret, bool, error) { - var secret *corev1.Secret - item, exists, err := c.factories[c.lookupNamespace(namespace)].Core().V1().Secrets().Informer().GetStore().GetByKey(namespace + "/" + name) - if err == nil && item != nil { - secret = item.(*corev1.Secret) - } - return secret, exists, err + secret, err := c.factories[c.lookupNamespace(namespace)].Core().V1().Secrets().Lister().Secrets(namespace).Get(name) + exist, err := translateNotFoundError(err) + return secret, exist, err } // lookupNamespace returns the lookup namespace key for the given namespace. @@ -264,3 +250,12 @@ func eventHandlerFunc(events chan<- interface{}, obj interface{}) { default: } } + +// translateNotFoundError will translate a "not found" error to a boolean return +// value which indicates if the resource exists and a nil error. +func translateNotFoundError(err error) (bool, error) { + if kubeerror.IsNotFound(err) { + return false, nil + } + return err == nil, err +} diff --git a/provider/kubernetes/client_test.go b/provider/kubernetes/client_test.go new file mode 100644 index 000000000..250e51ac3 --- /dev/null +++ b/provider/kubernetes/client_test.go @@ -0,0 +1,49 @@ +package kubernetes + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + kubeerror "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func TestTranslateNotFoundError(t *testing.T) { + testCases := []struct { + desc string + err error + expectedExists bool + expectedError error + }{ + { + desc: "kubernetes not found error", + err: kubeerror.NewNotFound(schema.GroupResource{}, "foo"), + expectedExists: false, + expectedError: nil, + }, + { + desc: "nil error", + err: nil, + expectedExists: true, + expectedError: nil, + }, + { + desc: "not a kubernetes not found error", + err: fmt.Errorf("bar error"), + expectedExists: false, + expectedError: fmt.Errorf("bar error"), + }, + } + + for _, testCase := range testCases { + test := testCase + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + exists, err := translateNotFoundError(test.err) + assert.Equal(t, test.expectedExists, exists) + assert.Equal(t, test.expectedError, err) + }) + } +}