什么是resthandler 之前已经分析到,Kubernetes是如何注册API的。Kubernetes会把对应路径上的请求交给对应的resthandler处理。所以,resthandler是对请求进行处理并响应的函数。在Kubernetes中,给每一种动作设置了对应的resthandler,如下表格所示(只列出了主要部分):
动作
函数
GET
GetResource
LIST
ListResource
PUT
UpdateResource
PATCH
PatchResource
POST
CreateResource
DELETE
DeleteResource
WATCH
ListResource
GetResource() GetResource()定义在/pkg/apiserver/resthandler.go中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func GetResource (r rest.Getter, e rest.Exporter, scope RequestScope) restful .RouteFunction {
return getResourceHandler(scope,
func (ctx api.Context, name string , req *restful.Request) (runtime.Object, error) {
trace := util.NewTrace("Get " + req.Request.URL.Path)
defer trace.LogIfLong(500 * time.Millisecond)
if values := req.Request.URL.Query(); len (values) > 0 {
exports := unversioned.ExportOptions{}
if err := scope.ParameterCodec.DecodeParameters(values, unversioned.GroupVersion{Version: "v1" }, &exports); err != nil {
return nil , err
}
if exports.Export {
if e == nil {
return nil , errors.NewBadRequest(fmt.Sprintf("export of %q is not supported" , scope.Resource.Resource))
}
return e.Export(ctx, name, exports)
}
}
return r.Get(ctx, name)
})
}
GetResource()中定义了获取资源的函数,然后通过getResourceHandler()进行封装。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func getResourceHandler (scope RequestScope, getter getterFunc) restful .RouteFunction {
return func (req *restful.Request, res *restful.Response) {
w := res.ResponseWriter
namespace, name, err := scope.Namer.Name(req)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
ctx := scope.ContextFunc(req)
ctx = api.WithNamespace(ctx, namespace)
result, err := getter(ctx, name, req)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
if err := setSelfLink(result, req, scope.Namer); err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
write(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request)
}
}
getResourceHandler()先从Request中获取namespace和name,然后namespace封装到ctx中,最后调用GetResource()中定义的获取资源的函数,获取到资源后,把结果写入请求的应答体。
ListResource() ListResource()用来获取资源列表。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
func ListResource (r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch bool , minRequestTimeout time.Duration) restful .RouteFunction {
return func (req *restful.Request, res *restful.Response) {
trace := util.NewTrace("List " + req.Request.URL.Path)
w := res.ResponseWriter
namespace, err := scope.Namer.Namespace(req)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
hasName := true
_, name, err := scope.Namer.Name(req)
if err != nil {
hasName = false
}
ctx := scope.ContextFunc(req)
ctx = api.WithNamespace(ctx, namespace)
opts := api.ListOptions{}
if err := scope.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.Kind.GroupVersion(), &opts); err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
if opts.FieldSelector != nil {
fn := func (label, value string ) (newLabel, newValue string , err error) {
return scope.Convertor.ConvertFieldLabel(scope.Kind.GroupVersion().String(), scope.Kind.Kind, label, value)
}
if opts.FieldSelector, err = opts.FieldSelector.Transform(fn); err != nil {
err = errors.NewBadRequest(err.Error())
scope.err(err, res.ResponseWriter, req.Request)
return
}
}
if hasName {
nameSelector := fields.OneTermEqualSelector("metadata.name" , name)
if opts.FieldSelector != nil && !opts.FieldSelector.Empty() {
scope.err(errors.NewBadRequest("both a name and a field selector provided; please provide one or the other." ), res.ResponseWriter, req.Request)
return
}
opts.FieldSelector = nameSelector
}
if (opts.Watch || forceWatch) && rw != nil {
watcher, err := rw.Watch(ctx, &opts)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
timeout := time.Duration(0 )
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
if timeout == 0 && minRequestTimeout > 0 {
timeout = time.Duration(float64 (minRequestTimeout) * (rand.Float64() + 1.0 ))
}
serveWatch(watcher, scope, req, res, timeout)
return
}
defer trace.LogIfLong(500 * time.Millisecond)
trace.Step("About to List from storage" )
result, err := r.List(ctx, &opts)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
trace.Step("Listing from storage done" )
numberOfItems, err := setListSelfLink(result, req, scope.Namer)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
trace.Step("Self-linking done" )
write(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request)
trace.Step(fmt.Sprintf("Writing http response done (%d items)" , numberOfItems))
}
}
如果执行的是WATCH动作,则会调用serveWatch(),定义在/pkg/apiserver/watch.go中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
func serveWatch (watcher watch.Interface, scope RequestScope, req *restful.Request, res *restful.Response, timeout time.Duration) {
serializer, err := negotiateOutputStreamSerializer(req.Request, scope.Serializer)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
framer := serializer.StreamSerializer.Framer
streamSerializer := serializer.StreamSerializer.Serializer
embedded := serializer.Serializer
if framer == nil {
scope.err(fmt.Errorf("no framer defined for %q available for embedded encoding" , serializer.MediaType), res.ResponseWriter, req.Request)
return
}
encoder := scope.Serializer.EncoderForVersion(streamSerializer, scope.Kind.GroupVersion())
useTextFraming := serializer.EncodesAsText
embeddedEncoder := scope.Serializer.EncoderForVersion(embedded, scope.Kind.GroupVersion())
mediaType := serializer.MediaType
if mediaType != runtime.ContentTypeJSON {
mediaType += ";stream=watch"
}
server := &WatchServer{
watching: watcher,
scope: scope,
useTextFraming: useTextFraming,
mediaType: mediaType,
framer: framer,
encoder: encoder,
embeddedEncoder: embeddedEncoder,
fixup: func (obj runtime.Object) {
if err := setSelfLink(obj, req, scope.Namer); err != nil {
utilruntime.HandleError(fmt.Errorf("failed to set link for object %v: %v" , reflect.TypeOf(obj), err))
}
},
t: &realTimeoutFactory{timeout},
}
server.ServeHTTP(res.ResponseWriter, req.Request)
}
关于List和Watch的操作,以后会在专题进行分析。
UpdateResource() UpdateResource()用于处理更新资源人请求。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
func UpdateResource (r rest.Updater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface) restful .RouteFunction {
return func (req *restful.Request, res *restful.Response) {
trace := util.NewTrace("Update " + req.Request.URL.Path)
defer trace.LogIfLong(500 * time.Millisecond)
w := res.ResponseWriter
timeout := parseTimeout(req.Request.URL.Query().Get("timeout" ))
namespace, name, err := scope.Namer.Name(req)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
ctx := scope.ContextFunc(req)
ctx = api.WithNamespace(ctx, namespace)
body, err := readBody(req.Request)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
s, err := negotiateInputSerializer(req.Request, scope.Serializer)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
defaultGVK := scope.Kind
original := r.New()
trace.Step("About to convert to expected version" )
obj, gvk, err := scope.Serializer.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, original)
if err != nil {
err = transformDecodeError(typer, err, original, gvk, body)
scope.err(err, res.ResponseWriter, req.Request)
return
}
if gvk.GroupVersion() != defaultGVK.GroupVersion() {
err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%s)" , gvk.GroupVersion(), defaultGVK.GroupVersion()))
scope.err(err, res.ResponseWriter, req.Request)
return
}
trace.Step("Conversion done" )
if err := checkName(obj, name, namespace, scope.Namer); err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
var transformers []rest.TransformFunc
if admit != nil && admit.Handles(admission.Update) {
transformers = append (transformers, func (ctx api.Context, newObj, oldObj runtime.Object) (runtime.Object, error) {
userInfo, _ := api.UserFrom(ctx)
return newObj, admit.Admit(admission.NewAttributesRecord(newObj, oldObj, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo))
})
}
trace.Step("About to store object in database" )
wasCreated := false
result, err := finishRequest(timeout, func () (runtime.Object, error) {
obj, created, err := r.Update(ctx, name, rest.DefaultUpdatedObjectInfo(obj, scope.Copier, transformers...))
wasCreated = created
return obj, err
})
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
trace.Step("Object stored in database" )
if err := setSelfLink(result, req, scope.Namer); err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
trace.Step("Self-link added" )
status := http.StatusOK
if wasCreated {
status = http.StatusCreated
}
write(status, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request)
}
}
PatchResource() 用来处理局部更新的请求。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
func PatchResource (r rest.Patcher, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface, converter runtime.ObjectConvertor) restful .RouteFunction {
return func (req *restful.Request, res *restful.Response) {
w := res.ResponseWriter
timeout := parseTimeout(req.Request.URL.Query().Get("timeout" ))
namespace, name, err := scope.Namer.Name(req)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
ctx := scope.ContextFunc(req)
ctx = api.WithNamespace(ctx, namespace)
versionedObj, err := converter.ConvertToVersion(r.New(), scope.Kind.GroupVersion())
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
contentType := req.HeaderParameter("Content-Type" )
if idx := strings.Index(contentType, ";" ); idx > 0 {
contentType = contentType[:idx]
}
patchType := api.PatchType(contentType)
patchJS, err := readBody(req.Request)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
s, ok := runtime.SerializerInfoForMediaType(scope.Serializer.SupportedMediaTypes(), runtime.ContentTypeJSON)
if !ok {
scope.err(fmt.Errorf("no serializer defined for JSON" ), res.ResponseWriter, req.Request)
return
}
gv := scope.Kind.GroupVersion()
codec := runtime.NewCodec(
scope.Serializer.EncoderForVersion(s.Serializer, gv),
scope.Serializer.DecoderToVersion(s.Serializer, unversioned.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal}),
)
updateAdmit := func (updatedObject runtime.Object, currentObject runtime.Object) error {
if admit != nil && admit.Handles(admission.Update) {
userInfo, _ := api.UserFrom(ctx)
return admit.Admit(admission.NewAttributesRecord(updatedObject, currentObject, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Update, userInfo))
}
return nil
}
result, err := patchResource(ctx, updateAdmit, timeout, versionedObj, r, name, patchType, patchJS, scope.Namer, scope.Copier, scope.Resource, codec)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
if err := setSelfLink(result, req, scope.Namer); err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
write(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
func patchResource (
ctx api.Context,
admit updateAdmissionFunc,
timeout time.Duration,
versionedObj runtime.Object,
patcher rest.Patcher,
name string ,
patchType api.PatchType,
patchJS []byte ,
namer ScopeNamer,
copier runtime.ObjectCopier,
resource unversioned.GroupVersionResource,
codec runtime.Codec,
) (runtime.Object, error) {
namespace := api.NamespaceValue(ctx)
var (
originalObjJS []byte
originalPatchedObjJS []byte
lastConflictErr error
)
applyPatch := func (_ api.Context, _, currentObject runtime.Object) (runtime.Object, error) {
if hasUID, err := hasUID(currentObject); err != nil {
return nil , err
} else if !hasUID {
return nil , errors.NewNotFound(resource.GroupResource(), name)
}
switch {
case len (originalObjJS) == 0 || len (originalPatchedObjJS) == 0 :
if js, err := runtime.Encode(codec, currentObject); err != nil {
return nil , err
} else {
originalObjJS = js
}
if js, err := getPatchedJS(patchType, originalObjJS, patchJS, versionedObj); err != nil {
return nil , err
} else {
originalPatchedObjJS = js
}
objToUpdate := patcher.New()
if err := runtime.DecodeInto(codec, originalPatchedObjJS, objToUpdate); err != nil {
return nil , err
}
if err := checkName(objToUpdate, name, namespace, namer); err != nil {
return nil , err
}
return objToUpdate, nil
default :
currentObjectJS, err := runtime.Encode(codec, currentObject)
if err != nil {
return nil , err
}
currentPatch, err := strategicpatch.CreateStrategicMergePatch(originalObjJS, currentObjectJS, versionedObj)
if err != nil {
return nil , err
}
originalPatch, err := strategicpatch.CreateStrategicMergePatch(originalObjJS, originalPatchedObjJS, versionedObj)
if err != nil {
return nil , err
}
diff1 := make (map [string ]interface {})
if err := json.Unmarshal(originalPatch, &diff1); err != nil {
return nil , err
}
diff2 := make (map [string ]interface {})
if err := json.Unmarshal(currentPatch, &diff2); err != nil {
return nil , err
}
hasConflicts, err := strategicpatch.HasConflicts(diff1, diff2)
if err != nil {
return nil , err
}
if hasConflicts {
glog.V(4 ).Infof("patchResource failed for resource %s, because there is a meaningful conflict.\n diff1=%v\n, diff2=%v\n" , name, diff1, diff2)
if lastConflictErr != nil {
return nil , lastConflictErr
}
return nil , errors.NewConflict(resource.GroupResource(), name, nil )
}
newlyPatchedObjJS, err := getPatchedJS(api.StrategicMergePatchType, currentObjectJS, originalPatch, versionedObj)
if err != nil {
return nil , err
}
objToUpdate := patcher.New()
if err := runtime.DecodeInto(codec, newlyPatchedObjJS, objToUpdate); err != nil {
return nil , err
}
return objToUpdate, nil
}
}
applyAdmission := func (ctx api.Context, patchedObject runtime.Object, currentObject runtime.Object) (runtime.Object, error) {
return patchedObject, admit(patchedObject, currentObject)
}
updatedObjectInfo := rest.DefaultUpdatedObjectInfo(nil , copier, applyPatch, applyAdmission)
return finishRequest(timeout, func () (runtime.Object, error) {
updateObject, _, updateErr := patcher.Update(ctx, name, updatedObjectInfo)
for i := 0 ; i < MaxPatchConflicts && (errors.IsConflict(updateErr)); i++ {
lastConflictErr = updateErr
updateObject, _, updateErr = patcher.Update(ctx, name, updatedObjectInfo)
}
return updateObject, updateErr
})
}
CreateResource() CreateResource()用来处理创建的请求。1
2
3
4
func CreateResource (r rest.Creater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface) restful .RouteFunction {
return createHandler(&namedCreaterAdapter{r}, scope, typer, admit, false )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
func createHandler (r rest.NamedCreater, scope RequestScope, typer runtime.ObjectTyper, admit admission.Interface, includeName bool ) restful .RouteFunction {
return func (req *restful.Request, res *restful.Response) {
trace := util.NewTrace("Create " + req.Request.URL.Path)
defer trace.LogIfLong(500 * time.Millisecond)
w := res.ResponseWriter
timeout := parseTimeout(req.Request.URL.Query().Get("timeout" ))
var (
namespace, name string
err error
)
if includeName {
namespace, name, err = scope.Namer.Name(req)
} else {
namespace, err = scope.Namer.Namespace(req)
}
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
ctx := scope.ContextFunc(req)
ctx = api.WithNamespace(ctx, namespace)
gv := scope.Kind.GroupVersion()
s, err := negotiateInputSerializer(req.Request, scope.Serializer)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
decoder := scope.Serializer.DecoderToVersion(s.Serializer, unversioned.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal})
body, err := readBody(req.Request)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
defaultGVK := scope.Kind
original := r.New()
trace.Step("About to convert to expected version" )
obj, gvk, err := decoder.Decode(body, &defaultGVK, original)
if err != nil {
err = transformDecodeError(typer, err, original, gvk, body)
scope.err(err, res.ResponseWriter, req.Request)
return
}
if gvk.GroupVersion() != gv {
err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%v)" , gvk.GroupVersion().String(), gv.String()))
scope.err(err, res.ResponseWriter, req.Request)
return
}
trace.Step("Conversion done" )
if admit != nil && admit.Handles(admission.Create) {
userInfo, _ := api.UserFrom(ctx)
err = admit.Admit(admission.NewAttributesRecord(obj, nil , scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, userInfo))
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
}
trace.Step("About to store object in database" )
result, err := finishRequest(timeout, func () (runtime.Object, error) {
out, err := r.Create(ctx, name, obj)
if status, ok := out.(*unversioned.Status); ok && err == nil && status.Code == 0 {
status.Code = http.StatusCreated
}
return out, err
})
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
trace.Step("Object stored in database" )
if err := setSelfLink(result, req, scope.Namer); err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
trace.Step("Self-link added" )
write(http.StatusCreated, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request)
}
}
DeleteResource() DeleteResource()用来处理删除的请求。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
func DeleteResource (r rest.GracefulDeleter, allowsOptions bool , scope RequestScope, admit admission.Interface) restful .RouteFunction {
return func (req *restful.Request, res *restful.Response) {
trace := util.NewTrace("Delete " + req.Request.URL.Path)
defer trace.LogIfLong(500 * time.Millisecond)
w := res.ResponseWriter
timeout := parseTimeout(req.Request.URL.Query().Get("timeout" ))
namespace, name, err := scope.Namer.Name(req)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
ctx := scope.ContextFunc(req)
ctx = api.WithNamespace(ctx, namespace)
options := &api.DeleteOptions{}
if allowsOptions {
body, err := readBody(req.Request)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
if len (body) > 0 {
s, err := negotiateInputSerializer(req.Request, scope.Serializer)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
defaultGVK := scope.Kind.GroupVersion().WithKind("DeleteOptions" )
obj, _, err := scope.Serializer.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
if obj != options {
scope.err(fmt.Errorf("decoded object cannot be converted to DeleteOptions" ), res.ResponseWriter, req.Request)
return
}
} else {
if values := req.Request.URL.Query(); len (values) > 0 {
if err := scope.ParameterCodec.DecodeParameters(values, scope.Kind.GroupVersion(), options); err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
}
}
}
if admit != nil && admit.Handles(admission.Delete) {
userInfo, _ := api.UserFrom(ctx)
err = admit.Admit(admission.NewAttributesRecord(nil , nil , scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Delete, userInfo))
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
}
trace.Step("About do delete object from database" )
result, err := finishRequest(timeout, func () (runtime.Object, error) {
return r.Delete(ctx, name, options)
})
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
trace.Step("Object deleted from database" )
if result == nil {
result = &unversioned.Status{
Status: unversioned.StatusSuccess,
Code: http.StatusOK,
Details: &unversioned.StatusDetails{
Name: name,
Kind: scope.Kind.Kind,
},
}
} else {
if _, ok := result.(*unversioned.Status); !ok {
if err := setSelfLink(result, req, scope.Namer); err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
}
}
}
write(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request)
}
}