Better convey duplicate name errors to client (#116)
Motivation: Creating duplicate resources currently results in a 500 error being displayed by the client, with no information about the true nature of the failure. Modifications: * ResourceStore now converts any errors from the etcd client into fission errors, capturing the reason for the error in the case of a duplicate key (as ErrorNameExists) * ErrorNameExists errors are signaled to the client with a 409 (Conflict) HTTP status * MakeErrorFromHTTP() now reads the body of the error response to retrieve the actual error message instead of using the HTTP status message * the controller client now uses MakeErrorFromHTTP() to centralize status code -> error code mapping * tests for all of the resources now check that duplicate resources are reported properly Result: The client can now provide more context when a duplicate name is given related to #112
This commit is contained in:
committed by
Soam Vasani
parent
99d6a95bc1
commit
c8b5bb2972
+34
-14
@@ -25,6 +25,9 @@ import (
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/satori/go.uuid"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"fmt"
|
||||
"github.com/fission/fission"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -93,7 +96,7 @@ func (rs *ResourceStore) create(r resource) error {
|
||||
|
||||
_, err = rs.KeysAPI.Set(context.Background(), key, string(serialized),
|
||||
&client.SetOptions{PrevExist: client.PrevNoExist})
|
||||
return err
|
||||
return handleEtcdError(err)
|
||||
}
|
||||
|
||||
func (rs *ResourceStore) read(rkey string, res resource) error {
|
||||
@@ -105,7 +108,7 @@ func (rs *ResourceStore) read(rkey string, res resource) error {
|
||||
|
||||
resp, err := rs.KeysAPI.Get(context.Background(), key, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return handleEtcdError(err)
|
||||
}
|
||||
return rs.serializer.deserialize([]byte(resp.Node.Value), res)
|
||||
}
|
||||
@@ -123,13 +126,13 @@ func (rs *ResourceStore) update(r resource) error {
|
||||
|
||||
_, err = rs.KeysAPI.Set(context.Background(), key, string(serialized),
|
||||
&client.SetOptions{PrevExist: client.PrevExist})
|
||||
return err
|
||||
return handleEtcdError(err)
|
||||
}
|
||||
|
||||
func (rs *ResourceStore) delete(typename, rkey string) error {
|
||||
key := typename + "/" + rkey
|
||||
_, err := rs.KeysAPI.Delete(context.Background(), key, nil) // ignore response
|
||||
return err
|
||||
return handleEtcdError(err)
|
||||
}
|
||||
|
||||
// getAll finds all entries under key. If none or found or key
|
||||
@@ -140,7 +143,7 @@ func (rs *ResourceStore) getAll(key string) ([]string, error) {
|
||||
if client.IsKeyNotFound(err) {
|
||||
return []string{}, nil
|
||||
}
|
||||
return nil, err
|
||||
return nil, handleEtcdError(err)
|
||||
}
|
||||
|
||||
res := make([]string, 0, len(resp.Node.Nodes))
|
||||
@@ -162,7 +165,7 @@ func (rs *ResourceStore) writeFile(parentKey string, contents []byte) (string, s
|
||||
resp, err := rs.KeysAPI.CreateInOrder(context.Background(), parentKey, uid, nil)
|
||||
if err != nil {
|
||||
_ = rs.FileStore.delete(uid)
|
||||
return "", "", err
|
||||
return "", "", handleEtcdError(err)
|
||||
}
|
||||
|
||||
return resp.Node.Key, uid, nil
|
||||
@@ -172,7 +175,7 @@ func (rs *ResourceStore) readFile(key string, uid *string) ([]byte, error) {
|
||||
key = "file/" + key
|
||||
resp, err := rs.KeysAPI.Get(context.Background(), key, &client.GetOptions{Sort: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, handleEtcdError(err)
|
||||
}
|
||||
|
||||
if uid == nil {
|
||||
@@ -194,14 +197,14 @@ func (rs *ResourceStore) readFile(key string, uid *string) ([]byte, error) {
|
||||
}
|
||||
|
||||
contents, err := rs.FileStore.read(*uid)
|
||||
return contents, err
|
||||
return contents, handleEtcdError(err)
|
||||
}
|
||||
|
||||
func (rs *ResourceStore) deleteFile(key string, uid string) error {
|
||||
key = "file/" + key
|
||||
resp, err := rs.KeysAPI.Get(context.Background(), key, &client.GetOptions{Sort: true})
|
||||
if err != nil {
|
||||
return err
|
||||
return handleEtcdError(err)
|
||||
}
|
||||
|
||||
var node *client.Node
|
||||
@@ -222,12 +225,12 @@ func (rs *ResourceStore) deleteFile(key string, uid string) error {
|
||||
|
||||
_, err = rs.KeysAPI.Delete(context.Background(), node.Key, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return handleEtcdError(err)
|
||||
}
|
||||
|
||||
if len(resp.Node.Nodes) == 1 {
|
||||
_, err = rs.KeysAPI.Delete(context.Background(), key, &client.DeleteOptions{Dir: true})
|
||||
return err
|
||||
return handleEtcdError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -236,7 +239,7 @@ func (rs *ResourceStore) deleteAllFiles(key string) error {
|
||||
key = "file/" + key
|
||||
resp, err := rs.KeysAPI.Get(context.Background(), key, &client.GetOptions{Sort: true})
|
||||
if err != nil {
|
||||
return err
|
||||
return handleEtcdError(err)
|
||||
}
|
||||
for _, u := range resp.Node.Nodes {
|
||||
err = rs.FileStore.delete(u.Value)
|
||||
@@ -246,9 +249,26 @@ func (rs *ResourceStore) deleteAllFiles(key string) error {
|
||||
|
||||
_, err = rs.KeysAPI.Delete(context.Background(), u.Key, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return handleEtcdError(err)
|
||||
}
|
||||
}
|
||||
_, err = rs.KeysAPI.Delete(context.Background(), key, &client.DeleteOptions{Dir: true})
|
||||
return err
|
||||
return handleEtcdError(err)
|
||||
}
|
||||
|
||||
func handleEtcdError(e error) error {
|
||||
ee, ok := e.(client.Error)
|
||||
if !ok {
|
||||
return e
|
||||
}
|
||||
code := fission.ErrorInternal
|
||||
msg := ee.Error()
|
||||
|
||||
//TODO: handle any other etcd error codes we care about
|
||||
switch ee.Code {
|
||||
case client.ErrorCodeNodeExist:
|
||||
code = fission.ErrorNameExists
|
||||
msg = fmt.Sprintf("%v (%v)", ee.Message, ee.Cause)
|
||||
}
|
||||
return fission.MakeError(code, msg)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user