@@ -11,8 +11,6 @@ import (
11
11
"net"
12
12
"time"
13
13
14
- "github.com/spidernet-io/egressgateway/pkg/utils/ip"
15
-
16
14
admissionv1 "k8s.io/api/admission/v1"
17
15
v1 "k8s.io/api/admission/v1"
18
16
"k8s.io/apimachinery/pkg/api/errors"
@@ -23,6 +21,7 @@ import (
23
21
"github.com/spidernet-io/egressgateway/pkg/config"
24
22
"github.com/spidernet-io/egressgateway/pkg/constant"
25
23
egress "github.com/spidernet-io/egressgateway/pkg/k8s/apis/v1beta1"
24
+ "github.com/spidernet-io/egressgateway/pkg/utils/ip"
26
25
)
27
26
28
27
type EgressGatewayWebhook struct {
@@ -85,7 +84,7 @@ func (egw *EgressGatewayWebhook) EgressGatewayValidate(ctx context.Context, req
85
84
return webhook .Denied (fmt .Sprintf ("Failed to check IP: %v" , err ))
86
85
}
87
86
88
- ipv6Ranges , _ := ip .MergeIPRanges (constant .IPv6 , newEg .Spec .Ippools .IPv6 )
87
+ ipv6Ranges , err := ip .MergeIPRanges (constant .IPv6 , newEg .Spec .Ippools .IPv6 )
89
88
if err != nil {
90
89
return webhook .Denied (fmt .Sprintf ("Failed to check IP: %v" , err ))
91
90
}
@@ -95,15 +94,13 @@ func (egw *EgressGatewayWebhook) EgressGatewayValidate(ctx context.Context, req
95
94
if err != nil {
96
95
return webhook .Denied (fmt .Sprintf ("Failed to check IP: %v" , err ))
97
96
}
98
-
99
97
}
100
98
101
99
if egw .Config .FileConfig .EnableIPv6 {
102
100
ipv6s , err = ip .ParseIPRanges (constant .IPv6 , ipv6Ranges )
103
101
if err != nil {
104
102
return webhook .Denied (fmt .Sprintf ("Failed to check IP: %v" , err ))
105
103
}
106
-
107
104
}
108
105
109
106
if egw .Config .FileConfig .EnableIPv4 && egw .Config .FileConfig .EnableIPv6 {
@@ -113,65 +110,143 @@ func (egw *EgressGatewayWebhook) EgressGatewayValidate(ctx context.Context, req
113
110
}
114
111
}
115
112
116
- eg := new (egress.EgressGateway )
117
- err = egw .Client .Get (ctx , types.NamespacedName {Name : req .Name , Namespace : req .Namespace }, eg )
118
- if err != nil {
119
- if ! errors .IsNotFound (err ) {
120
- return webhook .Denied (fmt .Sprintf ("failed to obtain the EgressGateway: %v" , err ))
113
+ // Check the defaultEIP
114
+ if len (newEg .Spec .Ippools .Ipv4DefaultEIP ) != 0 {
115
+ result , err := ip .IsIPIncludedRange (constant .IPv4 , newEg .Spec .Ippools .Ipv4DefaultEIP , ipv4Ranges )
116
+ if err != nil {
117
+ return webhook .Denied (fmt .Sprintf ("Failed to check ipv4DefaultEIP: %v" , err ))
118
+ }
119
+ if ! result {
120
+ return webhook .Denied (fmt .Sprintf ("%v is not covered by IPPools" , newEg .Spec .Ippools .Ipv4DefaultEIP ))
121
121
}
122
122
}
123
123
124
- // it should be denied when the single IPv4 or IPv6 is updated to the other type
125
- if req . Operation == v1 . Update {
126
- if len ( eg . Spec . Ippools . IPv4 ) == 0 && len ( newEg . Spec . Ippools . IPv4 ) > 0 {
127
- return webhook .Denied ("the 'spec.Ippools.IPv4' field cannot to be modified when it is empty" )
124
+ if len ( newEg . Spec . Ippools . Ipv6DefaultEIP ) != 0 {
125
+ result , err := ip . IsIPIncludedRange ( constant . IPv6 , newEg . Spec . Ippools . Ipv6DefaultEIP , ipv6Ranges )
126
+ if err != nil {
127
+ return webhook .Denied (fmt . Sprintf ( "Failed to check ipv6DefaultEIP: %v" , err ) )
128
128
}
129
- if len ( eg . Spec . Ippools . IPv6 ) == 0 && len ( newEg . Spec . Ippools . IPv6 ) > 0 {
130
- return webhook .Denied ("the 'spec.Ippools.IPv6' field cannot to be modified when it is empty" )
129
+ if ! result {
130
+ return webhook .Denied (fmt . Sprintf ( "%v is not covered by Ippools" , newEg . Spec . Ippools . Ipv6DefaultEIP ) )
131
131
}
132
132
}
133
133
134
- // Check whether the IP address to be deleted has been allocated
135
- for _ , item := range eg .Status .NodeList {
136
- for _ , eip := range item .Eips {
137
- // skip the cases of using useNodeIP
138
- if eip .IPv4 == "" && eip .IPv6 == "" {
139
- continue
140
- }
134
+ // 检查 ip pool 是否和其他 egw 的 ip pool 重复
135
+ egwList := & egress.EgressGatewayList {}
136
+ err = egw .Client .List (ctx , egwList )
137
+ if err != nil {
138
+ return webhook .Denied (fmt .Sprintf ("Failed to get EgressGatewayList: %v" , err ))
139
+ }
140
+ clusterMap , err := buildClusterIPMap (egwList )
141
+ if err != nil {
142
+ return webhook .Denied (fmt .Sprintf ("Failed to build cluster EgressGateway IP map: %v" , err ))
143
+ }
144
+ err = checkDupIP (ipv4s , ipv6s , clusterMap )
145
+ if err != nil {
146
+ return webhook .Denied (err .Error ())
147
+ }
141
148
142
- result , err := ip .IsIPIncludedRange (constant .IPv4 , eip .IPv4 , ipv4Ranges )
143
- if err != nil {
144
- return webhook .Denied (fmt .Sprintf ("Failed to check IP: %v" , err ))
149
+ // only for update
150
+ if req .Operation == v1 .Update {
151
+ oldEgressGateway := new (egress.EgressGateway )
152
+ err = egw .Client .Get (ctx , types.NamespacedName {Name : req .Name , Namespace : req .Namespace }, oldEgressGateway )
153
+ if err != nil {
154
+ if ! errors .IsNotFound (err ) {
155
+ return webhook .Denied (fmt .Sprintf ("failed to obtain the EgressGateway: %v" , err ))
145
156
}
157
+ }
146
158
147
- if ! result {
148
- return webhook .Denied (fmt .Sprintf ("%v has been allocated and cannot be deleted" , eip .IPv4 ))
159
+ // it should be denied when the single IPv4 or IPv6 is updated to the other type
160
+ if len (oldEgressGateway .Spec .Ippools .IPv4 ) == 0 && len (newEg .Spec .Ippools .IPv4 ) > 0 {
161
+ return webhook .Denied ("the 'spec.Ippools.IPv4' field cannot to be modified when it is empty" )
162
+ }
163
+ if len (oldEgressGateway .Spec .Ippools .IPv6 ) == 0 && len (newEg .Spec .Ippools .IPv6 ) > 0 {
164
+ return webhook .Denied ("the 'spec.Ippools.IPv6' field cannot to be modified when it is empty" )
165
+ }
166
+
167
+ // check if the IP to be deleted is already assigned
168
+ for _ , item := range oldEgressGateway .Status .NodeList {
169
+ for _ , eip := range item .Eips {
170
+ // skip the cases of using useNodeIP
171
+ if eip .IPv4 == "" && eip .IPv6 == "" {
172
+ continue
173
+ }
174
+
175
+ if eip .IPv4 != "" {
176
+ result , err := ip .IsIPIncludedRange (constant .IPv4 , eip .IPv4 , ipv4Ranges )
177
+ if err != nil {
178
+ return webhook .Denied (fmt .Sprintf ("Failed to check IPv4: %v" , err ))
179
+ }
180
+ if ! result {
181
+ return webhook .Denied (fmt .Sprintf ("%v has been allocated and cannot be deleted" , eip .IPv4 ))
182
+ }
183
+ }
184
+ if eip .IPv6 != "" {
185
+ result , err := ip .IsIPIncludedRange (constant .IPv6 , eip .IPv6 , ipv6Ranges )
186
+ if err != nil {
187
+ return webhook .Denied (fmt .Sprintf ("Failed to check IPv6: %v" , err ))
188
+ }
189
+ if ! result {
190
+ return webhook .Denied (fmt .Sprintf ("%v has been allocated and cannot be deleted" , eip .IPv6 ))
191
+ }
192
+ }
149
193
}
150
194
}
151
195
}
152
196
153
- // Check the defaultEIP
154
- if len (newEg .Spec .Ippools .Ipv4DefaultEIP ) != 0 {
155
- result , err := ip .IsIPIncludedRange (constant .IPv4 , newEg .Spec .Ippools .Ipv4DefaultEIP , ipv4Ranges )
197
+ return webhook .Allowed ("checked" )
198
+ }
199
+
200
+ func buildClusterIPMap (egwList * egress.EgressGatewayList ) (map [string ]map [string ]struct {}, error ) {
201
+ res := make (map [string ]map [string ]struct {})
202
+ for _ , item := range egwList .Items {
203
+ var ipv4s , ipv6s []net.IP
204
+ ipv4Ranges , err := ip .MergeIPRanges (constant .IPv4 , item .Spec .Ippools .IPv4 )
156
205
if err != nil {
157
- return webhook . Denied ( fmt . Sprintf ( "Failed to check Ipv4DefaultEIP: %v" , err ))
206
+ return nil , err
158
207
}
159
- if ! result {
160
- return webhook .Denied (fmt .Sprintf ("%v is not covered by Ippools" , newEg .Spec .Ippools .Ipv4DefaultEIP ))
208
+ ipv6Ranges , err := ip .MergeIPRanges (constant .IPv6 , item .Spec .Ippools .IPv6 )
209
+ if err != nil {
210
+ return nil , err
161
211
}
162
- }
163
-
164
- if len (newEg .Spec .Ippools .Ipv6DefaultEIP ) != 0 {
165
- result , err := ip .IsIPIncludedRange (constant .IPv6 , newEg .Spec .Ippools .Ipv6DefaultEIP , ipv6Ranges )
212
+ ipv4s , err = ip .ParseIPRanges (constant .IPv4 , ipv4Ranges )
166
213
if err != nil {
167
- return webhook . Denied ( fmt . Sprintf ( "Failed to check Ipv6DefaultEIP: %v" , err ))
214
+ return nil , err
168
215
}
169
- if ! result {
170
- return webhook .Denied (fmt .Sprintf ("%v is not covered by Ippools" , newEg .Spec .Ippools .Ipv6DefaultEIP ))
216
+ ipv6s , err = ip .ParseIPRanges (constant .IPv6 , ipv6Ranges )
217
+ if err != nil {
218
+ return nil , err
219
+ }
220
+ m := make (map [string ]struct {})
221
+ for _ , v := range ipv4s {
222
+ m [v .String ()] = struct {}{}
171
223
}
224
+ for _ , v := range ipv6s {
225
+ m [v .String ()] = struct {}{}
226
+ }
227
+ res [item .Name ] = m
172
228
}
229
+ return res , nil
230
+ }
173
231
174
- return webhook .Allowed ("checked" )
232
+ func checkDupIP (currentIPv4List , currentIPv6List []net.IP , clusterIPMap map [string ]map [string ]struct {}) error {
233
+ for _ , v := range currentIPv4List {
234
+ addr := v .String ()
235
+ for gwName , gw := range clusterIPMap {
236
+ if _ , ok := gw [addr ]; ok {
237
+ return fmt .Errorf ("find duplicate IPv4 %s in EgressGateway %s" , v .String (), gwName )
238
+ }
239
+ }
240
+ }
241
+ for _ , v := range currentIPv6List {
242
+ addr := v .String ()
243
+ for gwName , gw := range clusterIPMap {
244
+ if _ , ok := gw [addr ]; ok {
245
+ return fmt .Errorf ("find duplicate IPv6 %s in EgressGateway %s" , v .String (), gwName )
246
+ }
247
+ }
248
+ }
249
+ return nil
175
250
}
176
251
177
252
func (egw * EgressGatewayWebhook ) EgressGatewayMutate (ctx context.Context , req webhook.AdmissionRequest ) webhook.AdmissionResponse {
0 commit comments