@@ -25,26 +25,31 @@ import (
25
25
"time"
26
26
27
27
"github.com/emicklei/go-restful"
28
+ "k8s.io/apimachinery/pkg/api/errors"
28
29
"k8s.io/client-go/listers/core/v1"
29
30
glog "k8s.io/klog"
30
31
"tkestack.io/galaxy/pkg/api/galaxy/constant"
31
32
"tkestack.io/galaxy/pkg/ipam/floatingip"
33
+ "tkestack.io/galaxy/pkg/ipam/schedulerplugin"
32
34
"tkestack.io/galaxy/pkg/ipam/schedulerplugin/util"
33
35
"tkestack.io/galaxy/pkg/utils/httputil"
34
36
pageutil "tkestack.io/galaxy/pkg/utils/page"
35
37
)
36
38
37
39
// Controller is the API controller
38
40
type Controller struct {
39
- ipam floatingip.IPAM
40
- podLister v1.PodLister
41
+ ipam floatingip.IPAM
42
+ releaseFunc func (r * schedulerplugin.ReleaseRequest ) error
43
+ podLister v1.PodLister
41
44
}
42
45
43
46
// NewController construct a controller object
44
- func NewController (ipam floatingip.IPAM , lister v1.PodLister ) * Controller {
47
+ func NewController (
48
+ ipam floatingip.IPAM , lister v1.PodLister , releaseFunc func (r * schedulerplugin.ReleaseRequest ) error ) * Controller {
45
49
return & Controller {
46
- ipam : ipam ,
47
- podLister : lister ,
50
+ ipam : ipam ,
51
+ podLister : lister ,
52
+ releaseFunc : releaseFunc ,
48
53
}
49
54
}
50
55
@@ -118,37 +123,35 @@ func (c *Controller) ListIPs(req *restful.Request, resp *restful.Response) {
118
123
sort .Sort (bySortParam {array : fips , lessFunc : sortFunc (sortParam )})
119
124
start , end , pagin := pageutil .Pagination (page , size , len (fips ))
120
125
pagedFips := fips [start :end ]
121
- if err := fillReleasableAndStatus (c .podLister , pagedFips ); err != nil {
122
- httputil .InternalError (resp , err )
123
- return
126
+ for i := range pagedFips {
127
+ releasable , status := c .checkReleasableAndStatus (& pagedFips [i ])
128
+ pagedFips [i ].Status = status
129
+ pagedFips [i ].Releasable = releasable
124
130
}
125
131
resp .WriteEntity (ListIPResp {Page : * pagin , Content : pagedFips }) // nolint: errcheck
126
132
}
127
133
128
- // fillReleasableAndStatus fills status and releasable field
129
- func fillReleasableAndStatus (lister v1.PodLister , ips []FloatingIP ) error {
130
- for i := range ips {
131
- if ips [i ].labels != nil {
132
- if _ , ok := ips [i ].labels [constant .ReserveFIPLabel ]; ok {
133
- ips [i ].Releasable = false
134
- continue
135
- }
136
- }
137
- ips [i ].Releasable = true
138
- if ips [i ].PodName == "" {
139
- continue
140
- }
141
- pod , err := lister .Pods (ips [i ].Namespace ).Get (ips [i ].PodName )
142
- if err != nil || pod == nil {
143
- ips [i ].Status = "Deleted"
144
- continue
134
+ func (c * Controller ) checkReleasableAndStatus (fip * FloatingIP ) (releasable bool , status string ) {
135
+ if fip .labels != nil {
136
+ if _ , ok := fip .labels [constant .ReserveFIPLabel ]; ok {
137
+ return
145
138
}
146
- ips [i ].Status = string (pod .Status .Phase )
147
- // On public cloud, we can't release exist pod's ip, because we need to call unassign ip first
148
- // TODO while on private environment, we can
149
- ips [i ].Releasable = false
150
139
}
151
- return nil
140
+ if fip .PodName == "" {
141
+ return
142
+ }
143
+ pod , err := c .podLister .Pods (fip .Namespace ).Get (fip .PodName )
144
+ if err == nil {
145
+ status = string (pod .Status .Phase )
146
+ return
147
+ }
148
+ if errors .IsNotFound (err ) {
149
+ releasable = true
150
+ status = "Deleted"
151
+ } else {
152
+ status = "Unknown"
153
+ }
154
+ return
152
155
}
153
156
154
157
// bySortParam defines sort funcs for FloatingIP array
@@ -225,6 +228,8 @@ type ReleaseIPReq struct {
225
228
type ReleaseIPResp struct {
226
229
httputil.Resp
227
230
Unreleased []string `json:"unreleased,omitempty"`
231
+ // Reason is the reason why this ip is not released
232
+ Reason []string `json:"reasons,omitempty"`
228
233
}
229
234
230
235
// SwaggerDoc generates swagger doc for release ip response
@@ -242,7 +247,10 @@ func (c *Controller) ReleaseIPs(req *restful.Request, resp *restful.Response) {
242
247
httputil .BadRequest (resp , err )
243
248
return
244
249
}
245
- expectIPtoKey := make (map [string ]string )
250
+ var (
251
+ released , unreleasedIP , reasons []string
252
+ unbindRequests []* schedulerplugin.ReleaseRequest
253
+ )
246
254
for i := range releaseIPReq .IPs {
247
255
temp := releaseIPReq .IPs [i ]
248
256
ip := net .ParseIP (temp .IP )
@@ -259,36 +267,34 @@ func (c *Controller) ReleaseIPs(req *restful.Request, resp *restful.Response) {
259
267
httputil .BadRequest (resp , fmt .Errorf ("unknown app type %q" , temp .AppType ))
260
268
return
261
269
}
270
+ releasable , status := c .checkReleasableAndStatus (& temp )
271
+ if ! releasable {
272
+ unreleasedIP = append (unreleasedIP , temp .IP )
273
+ reasons = append (reasons , "releasable is false, pod status " + status )
274
+ continue
275
+ }
262
276
keyObj := util .NewKeyObj (appTypePrefix , temp .Namespace , temp .AppName , temp .PodName , temp .PoolName )
263
- expectIPtoKey [ temp . IP ] = keyObj . KeyInDB
277
+ unbindRequests = append ( unbindRequests , & schedulerplugin. ReleaseRequest { IP : ip , KeyObj : keyObj })
264
278
}
265
- if err := fillReleasableAndStatus (c .podLister , releaseIPReq .IPs ); err != nil {
266
- httputil .BadRequest (resp , err )
267
- return
268
- }
269
- for _ , ip := range releaseIPReq .IPs {
270
- if ! ip .Releasable {
271
- httputil .BadRequest (resp , fmt .Errorf ("%s is not releasable" , ip .IP ))
272
- return
279
+ for _ , req := range unbindRequests {
280
+ if err := c .releaseFunc (req ); err != nil {
281
+ unreleasedIP = append (unreleasedIP , req .IP .String ())
282
+ reasons = append (reasons , err .Error ())
283
+ } else {
284
+ released = append (released , req .IP .String ())
273
285
}
274
286
}
275
- _ , unreleased , err := batchReleaseIPs (expectIPtoKey , c .ipam )
276
- var unreleasedIP []string
277
- for ip := range unreleased {
278
- unreleasedIP = append (unreleasedIP , ip )
279
- }
287
+ glog .Infof ("releaseIPs %v" , released )
280
288
var res * ReleaseIPResp
281
- if err != nil {
282
- res = & ReleaseIPResp {Resp : httputil .NewResp (
283
- http .StatusInternalServerError , fmt .Sprintf ("server error: %v" , err ))}
284
- } else if len (unreleasedIP ) > 0 {
289
+ if len (unreleasedIP ) > 0 {
285
290
res = & ReleaseIPResp {Resp : httputil .NewResp (
286
- http .StatusAccepted , fmt .Sprintf ("Unreleased ips have been released or allocated to other pods, " +
287
- "or are not within valid range" ))}
291
+ http .StatusAccepted , fmt .Sprintf ("Released %d ips, %d ips failed, please check the reasons " +
292
+ "why they failed" , len ( released ), len ( unreleasedIP ) ))}
288
293
} else {
289
294
res = & ReleaseIPResp {Resp : httputil .NewResp (http .StatusOK , "" )}
290
295
}
291
296
res .Unreleased = unreleasedIP
297
+ res .Reason = reasons
292
298
resp .WriteHeaderAndEntity (res .Code , res )
293
299
}
294
300
@@ -328,12 +334,3 @@ func convert(fip *floatingip.FloatingIP) FloatingIP {
328
334
UpdateTime : fip .UpdatedAt ,
329
335
labels : fip .Labels }
330
336
}
331
-
332
- // batchReleaseIPs release ips from ipams
333
- func batchReleaseIPs (ipToKey map [string ]string , ipam floatingip.IPAM ) (map [string ]string , map [string ]string , error ) {
334
- released , unreleased , err := ipam .ReleaseIPs (ipToKey )
335
- if len (released ) > 0 {
336
- glog .Infof ("releaseIPs %v" , released )
337
- }
338
- return released , unreleased , err
339
- }
0 commit comments