mirror of https://github.com/k3s-io/k3s
Merge pull request #1192 from smarterclayton/standardize_etcd_errors
Return standard API errors from etcd registry by operationpull/6/head
commit
41754f5bd4
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 etcd provides conversion of etcd errors to API errors.
|
||||||
|
package etcd
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
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 etcd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InterpretGetError converts a generic etcd error on a retrieval
|
||||||
|
// operation into the appropriate API error.
|
||||||
|
func InterpretGetError(err error, kind, name string) error {
|
||||||
|
switch {
|
||||||
|
case tools.IsEtcdNotFound(err):
|
||||||
|
return errors.NewNotFound(kind, name)
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterpretCreateError converts a generic etcd error on a create
|
||||||
|
// operation into the appropriate API error.
|
||||||
|
func InterpretCreateError(err error, kind, name string) error {
|
||||||
|
switch {
|
||||||
|
case tools.IsEtcdNodeExist(err):
|
||||||
|
return errors.NewAlreadyExists(kind, name)
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterpretUpdateError converts a generic etcd error on a create
|
||||||
|
// operation into the appropriate API error.
|
||||||
|
func InterpretUpdateError(err error, kind, name string) error {
|
||||||
|
switch {
|
||||||
|
case tools.IsEtcdTestFailed(err), tools.IsEtcdNodeExist(err):
|
||||||
|
return errors.NewConflict(kind, name, err)
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterpretDeleteError converts a generic etcd error on a create
|
||||||
|
// operation into the appropriate API error.
|
||||||
|
func InterpretDeleteError(err error, kind, name string) error {
|
||||||
|
switch {
|
||||||
|
case tools.IsEtcdNotFound(err):
|
||||||
|
return errors.NewNotFound(kind, name)
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
etcderr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors/etcd"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/constraint"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/constraint"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
@ -96,7 +96,7 @@ func (r *Registry) WatchPods(resourceVersion uint64, filter func(*api.Pod) bool)
|
||||||
func (r *Registry) GetPod(podID string) (*api.Pod, error) {
|
func (r *Registry) GetPod(podID string) (*api.Pod, error) {
|
||||||
var pod api.Pod
|
var pod api.Pod
|
||||||
if err := r.ExtractObj(makePodKey(podID), &pod, false); err != nil {
|
if err := r.ExtractObj(makePodKey(podID), &pod, false); err != nil {
|
||||||
return nil, err
|
return nil, etcderr.InterpretGetError(err, "pod", podID)
|
||||||
}
|
}
|
||||||
// TODO: Currently nothing sets CurrentState.Host. We need a feedback loop that sets
|
// TODO: Currently nothing sets CurrentState.Host. We need a feedback loop that sets
|
||||||
// the CurrentState.Host and Status fields. Here we pretend that reality perfectly
|
// the CurrentState.Host and Status fields. Here we pretend that reality perfectly
|
||||||
|
@ -117,12 +117,13 @@ func (r *Registry) CreatePod(pod *api.Pod) error {
|
||||||
// DesiredState.Host == "" is a signal to the scheduler that this pod needs scheduling.
|
// DesiredState.Host == "" is a signal to the scheduler that this pod needs scheduling.
|
||||||
pod.DesiredState.Status = api.PodRunning
|
pod.DesiredState.Status = api.PodRunning
|
||||||
pod.DesiredState.Host = ""
|
pod.DesiredState.Host = ""
|
||||||
return r.CreateObj(makePodKey(pod.ID), pod)
|
err := r.CreateObj(makePodKey(pod.ID), pod)
|
||||||
|
return etcderr.InterpretCreateError(err, "pod", pod.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyBinding implements binding's registry
|
// ApplyBinding implements binding's registry
|
||||||
func (r *Registry) ApplyBinding(binding *api.Binding) error {
|
func (r *Registry) ApplyBinding(binding *api.Binding) error {
|
||||||
return r.assignPod(binding.PodID, binding.Host)
|
return etcderr.InterpretCreateError(r.assignPod(binding.PodID, binding.Host), "binding", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// setPodHostTo sets the given pod's host to 'machine' iff it was previously 'oldMachine'.
|
// setPodHostTo sets the given pod's host to 'machine' iff it was previously 'oldMachine'.
|
||||||
|
@ -183,20 +184,14 @@ func (r *Registry) DeletePod(podID string) error {
|
||||||
var pod api.Pod
|
var pod api.Pod
|
||||||
podKey := makePodKey(podID)
|
podKey := makePodKey(podID)
|
||||||
err := r.ExtractObj(podKey, &pod, false)
|
err := r.ExtractObj(podKey, &pod, false)
|
||||||
if tools.IsEtcdNotFound(err) {
|
|
||||||
return errors.NewNotFound("pod", podID)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return etcderr.InterpretDeleteError(err, "pod", podID)
|
||||||
}
|
}
|
||||||
// First delete the pod, so a scheduler doesn't notice it getting removed from the
|
// First delete the pod, so a scheduler doesn't notice it getting removed from the
|
||||||
// machine and attempt to put it somewhere.
|
// machine and attempt to put it somewhere.
|
||||||
err = r.Delete(podKey, true)
|
err = r.Delete(podKey, true)
|
||||||
if tools.IsEtcdNotFound(err) {
|
|
||||||
return errors.NewNotFound("pod", podID)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return etcderr.InterpretDeleteError(err, "pod", podID)
|
||||||
}
|
}
|
||||||
machine := pod.DesiredState.Host
|
machine := pod.DesiredState.Host
|
||||||
if machine == "" {
|
if machine == "" {
|
||||||
|
@ -248,11 +243,8 @@ func (r *Registry) GetController(controllerID string) (*api.ReplicationControlle
|
||||||
var controller api.ReplicationController
|
var controller api.ReplicationController
|
||||||
key := makeControllerKey(controllerID)
|
key := makeControllerKey(controllerID)
|
||||||
err := r.ExtractObj(key, &controller, false)
|
err := r.ExtractObj(key, &controller, false)
|
||||||
if tools.IsEtcdNotFound(err) {
|
|
||||||
return nil, errors.NewNotFound("replicationController", controllerID)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, etcderr.InterpretGetError(err, "replicationController", controllerID)
|
||||||
}
|
}
|
||||||
return &controller, nil
|
return &controller, nil
|
||||||
}
|
}
|
||||||
|
@ -260,25 +252,20 @@ func (r *Registry) GetController(controllerID string) (*api.ReplicationControlle
|
||||||
// CreateController creates a new ReplicationController.
|
// CreateController creates a new ReplicationController.
|
||||||
func (r *Registry) CreateController(controller *api.ReplicationController) error {
|
func (r *Registry) CreateController(controller *api.ReplicationController) error {
|
||||||
err := r.CreateObj(makeControllerKey(controller.ID), controller)
|
err := r.CreateObj(makeControllerKey(controller.ID), controller)
|
||||||
if tools.IsEtcdNodeExist(err) {
|
return etcderr.InterpretCreateError(err, "replicationController", controller.ID)
|
||||||
return errors.NewAlreadyExists("replicationController", controller.ID)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateController replaces an existing ReplicationController.
|
// UpdateController replaces an existing ReplicationController.
|
||||||
func (r *Registry) UpdateController(controller *api.ReplicationController) error {
|
func (r *Registry) UpdateController(controller *api.ReplicationController) error {
|
||||||
return r.SetObj(makeControllerKey(controller.ID), controller)
|
err := r.SetObj(makeControllerKey(controller.ID), controller)
|
||||||
|
return etcderr.InterpretUpdateError(err, "replicationController", controller.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteController deletes a ReplicationController specified by its ID.
|
// DeleteController deletes a ReplicationController specified by its ID.
|
||||||
func (r *Registry) DeleteController(controllerID string) error {
|
func (r *Registry) DeleteController(controllerID string) error {
|
||||||
key := makeControllerKey(controllerID)
|
key := makeControllerKey(controllerID)
|
||||||
err := r.Delete(key, false)
|
err := r.Delete(key, false)
|
||||||
if tools.IsEtcdNotFound(err) {
|
return etcderr.InterpretDeleteError(err, "replicationController", controllerID)
|
||||||
return errors.NewNotFound("replicationController", controllerID)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeServiceKey(name string) string {
|
func makeServiceKey(name string) string {
|
||||||
|
@ -295,10 +282,7 @@ func (r *Registry) ListServices() (*api.ServiceList, error) {
|
||||||
// CreateService creates a new Service.
|
// CreateService creates a new Service.
|
||||||
func (r *Registry) CreateService(svc *api.Service) error {
|
func (r *Registry) CreateService(svc *api.Service) error {
|
||||||
err := r.CreateObj(makeServiceKey(svc.ID), svc)
|
err := r.CreateObj(makeServiceKey(svc.ID), svc)
|
||||||
if tools.IsEtcdNodeExist(err) {
|
return etcderr.InterpretCreateError(err, "service", svc.ID)
|
||||||
return errors.NewAlreadyExists("service", svc.ID)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetService obtains a Service specified by its name.
|
// GetService obtains a Service specified by its name.
|
||||||
|
@ -306,11 +290,8 @@ func (r *Registry) GetService(name string) (*api.Service, error) {
|
||||||
key := makeServiceKey(name)
|
key := makeServiceKey(name)
|
||||||
var svc api.Service
|
var svc api.Service
|
||||||
err := r.ExtractObj(key, &svc, false)
|
err := r.ExtractObj(key, &svc, false)
|
||||||
if tools.IsEtcdNotFound(err) {
|
|
||||||
return nil, errors.NewNotFound("service", name)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, etcderr.InterpretGetError(err, "service", name)
|
||||||
}
|
}
|
||||||
return &svc, nil
|
return &svc, nil
|
||||||
}
|
}
|
||||||
|
@ -320,11 +301,8 @@ func (r *Registry) GetEndpoints(name string) (*api.Endpoints, error) {
|
||||||
key := makeServiceEndpointsKey(name)
|
key := makeServiceEndpointsKey(name)
|
||||||
var endpoints api.Endpoints
|
var endpoints api.Endpoints
|
||||||
err := r.ExtractObj(key, &endpoints, false)
|
err := r.ExtractObj(key, &endpoints, false)
|
||||||
if tools.IsEtcdNotFound(err) {
|
|
||||||
return nil, errors.NewNotFound("endpoints", name)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, etcderr.InterpretGetError(err, "endpoints", name)
|
||||||
}
|
}
|
||||||
return &endpoints, nil
|
return &endpoints, nil
|
||||||
}
|
}
|
||||||
|
@ -337,23 +315,23 @@ func makeServiceEndpointsKey(name string) string {
|
||||||
func (r *Registry) DeleteService(name string) error {
|
func (r *Registry) DeleteService(name string) error {
|
||||||
key := makeServiceKey(name)
|
key := makeServiceKey(name)
|
||||||
err := r.Delete(key, true)
|
err := r.Delete(key, true)
|
||||||
if tools.IsEtcdNotFound(err) {
|
|
||||||
return errors.NewNotFound("service", name)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return etcderr.InterpretDeleteError(err, "service", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: can leave dangling endpoints, and potentially return incorrect
|
||||||
|
// endpoints if a new service is created with the same name
|
||||||
key = makeServiceEndpointsKey(name)
|
key = makeServiceEndpointsKey(name)
|
||||||
err = r.Delete(key, true)
|
if err := r.Delete(key, true); err != nil && !tools.IsEtcdNotFound(err) {
|
||||||
if !tools.IsEtcdNotFound(err) {
|
return etcderr.InterpretDeleteError(err, "endpoints", name)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateService replaces an existing Service.
|
// UpdateService replaces an existing Service.
|
||||||
func (r *Registry) UpdateService(svc *api.Service) error {
|
func (r *Registry) UpdateService(svc *api.Service) error {
|
||||||
return r.SetObj(makeServiceKey(svc.ID), svc)
|
err := r.SetObj(makeServiceKey(svc.ID), svc)
|
||||||
|
return etcderr.InterpretUpdateError(err, "service", svc.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WatchServices begins watching for new, changed, or deleted service configurations.
|
// WatchServices begins watching for new, changed, or deleted service configurations.
|
||||||
|
@ -380,11 +358,12 @@ func (r *Registry) ListEndpoints() (*api.EndpointsList, error) {
|
||||||
// UpdateEndpoints update Endpoints of a Service.
|
// UpdateEndpoints update Endpoints of a Service.
|
||||||
func (r *Registry) UpdateEndpoints(e *api.Endpoints) error {
|
func (r *Registry) UpdateEndpoints(e *api.Endpoints) error {
|
||||||
// TODO: this is a really bad misuse of AtomicUpdate, need to compute a diff inside the loop.
|
// TODO: this is a really bad misuse of AtomicUpdate, need to compute a diff inside the loop.
|
||||||
return r.AtomicUpdate(makeServiceEndpointsKey(e.ID), &api.Endpoints{},
|
err := r.AtomicUpdate(makeServiceEndpointsKey(e.ID), &api.Endpoints{},
|
||||||
func(input runtime.Object) (runtime.Object, error) {
|
func(input runtime.Object) (runtime.Object, error) {
|
||||||
// TODO: racy - label query is returning different results for two simultaneous updaters
|
// TODO: racy - label query is returning different results for two simultaneous updaters
|
||||||
return e, nil
|
return e, nil
|
||||||
})
|
})
|
||||||
|
return etcderr.InterpretUpdateError(err, "endpoints", e.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WatchEndpoints begins watching for new, changed, or deleted endpoint configurations.
|
// WatchEndpoints begins watching for new, changed, or deleted endpoint configurations.
|
||||||
|
|
|
@ -63,8 +63,8 @@ func TestEtcdGetPodNotFound(t *testing.T) {
|
||||||
}
|
}
|
||||||
registry := NewTestEtcdRegistry(fakeClient)
|
registry := NewTestEtcdRegistry(fakeClient)
|
||||||
_, err := registry.GetPod("foo")
|
_, err := registry.GetPod("foo")
|
||||||
if err == nil {
|
if !errors.IsNotFound(err) {
|
||||||
t.Errorf("Unexpected non-error.")
|
t.Errorf("Unexpected error returned: %#v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,8 +144,8 @@ func TestEtcdCreatePodAlreadyExisting(t *testing.T) {
|
||||||
ID: "foo",
|
ID: "foo",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err == nil {
|
if !errors.IsAlreadyExists(err) {
|
||||||
t.Error("Unexpected non-error")
|
t.Errorf("Unexpected error returned: %#v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ func TestEtcdCreatePodWithContainersError(t *testing.T) {
|
||||||
R: &etcd.Response{
|
R: &etcd.Response{
|
||||||
Node: nil,
|
Node: nil,
|
||||||
},
|
},
|
||||||
E: tools.EtcdErrorValueRequired,
|
E: tools.EtcdErrorNodeExist, // validate that ApplyBinding is translating Create errors
|
||||||
}
|
}
|
||||||
registry := NewTestEtcdRegistry(fakeClient)
|
registry := NewTestEtcdRegistry(fakeClient)
|
||||||
err := registry.CreatePod(&api.Pod{
|
err := registry.CreatePod(&api.Pod{
|
||||||
|
@ -176,8 +176,8 @@ func TestEtcdCreatePodWithContainersError(t *testing.T) {
|
||||||
|
|
||||||
// Suddenly, a wild scheduler appears:
|
// Suddenly, a wild scheduler appears:
|
||||||
err = registry.ApplyBinding(&api.Binding{PodID: "foo", Host: "machine"})
|
err = registry.ApplyBinding(&api.Binding{PodID: "foo", Host: "machine"})
|
||||||
if err == nil {
|
if !errors.IsAlreadyExists(err) {
|
||||||
t.Fatalf("Unexpected non error.")
|
t.Fatalf("Unexpected error returned: %#v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
existingPod, err := registry.GetPod("foo")
|
existingPod, err := registry.GetPod("foo")
|
||||||
|
@ -185,7 +185,7 @@ func TestEtcdCreatePodWithContainersError(t *testing.T) {
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if existingPod.DesiredState.Host == "machine" {
|
if existingPod.DesiredState.Host == "machine" {
|
||||||
t.Fatal("Pod's host changed in response to an unappliable binding.")
|
t.Fatal("Pod's host changed in response to an non-apply-able binding.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,8 +568,8 @@ func TestEtcdGetControllerNotFound(t *testing.T) {
|
||||||
if ctrl != nil {
|
if ctrl != nil {
|
||||||
t.Errorf("Unexpected non-nil controller: %#v", ctrl)
|
t.Errorf("Unexpected non-nil controller: %#v", ctrl)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if !errors.IsNotFound(err) {
|
||||||
t.Error("Unexpected non-error.")
|
t.Errorf("Unexpected error returned: %#v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -745,8 +745,8 @@ func TestEtcdGetServiceNotFound(t *testing.T) {
|
||||||
}
|
}
|
||||||
registry := NewTestEtcdRegistry(fakeClient)
|
registry := NewTestEtcdRegistry(fakeClient)
|
||||||
_, err := registry.GetService("foo")
|
_, err := registry.GetService("foo")
|
||||||
if err == nil {
|
if !errors.IsNotFound(err) {
|
||||||
t.Errorf("Unexpected non-error.")
|
t.Errorf("Unexpected error returned: %#v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue