Skip to content

Commit 657cedc

Browse files
committed
Update EgressGateway validation
Signed-off-by: lou-lan <[email protected]>
1 parent 1e4de21 commit 657cedc

File tree

1 file changed

+117
-42
lines changed

1 file changed

+117
-42
lines changed

pkg/egressgateway/egress_gateway_webhook.go

+117-42
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import (
1111
"net"
1212
"time"
1313

14-
"github.com/spidernet-io/egressgateway/pkg/utils/ip"
15-
1614
admissionv1 "k8s.io/api/admission/v1"
1715
v1 "k8s.io/api/admission/v1"
1816
"k8s.io/apimachinery/pkg/api/errors"
@@ -23,6 +21,7 @@ import (
2321
"github.com/spidernet-io/egressgateway/pkg/config"
2422
"github.com/spidernet-io/egressgateway/pkg/constant"
2523
egress "github.com/spidernet-io/egressgateway/pkg/k8s/apis/v1beta1"
24+
"github.com/spidernet-io/egressgateway/pkg/utils/ip"
2625
)
2726

2827
type EgressGatewayWebhook struct {
@@ -85,7 +84,7 @@ func (egw *EgressGatewayWebhook) EgressGatewayValidate(ctx context.Context, req
8584
return webhook.Denied(fmt.Sprintf("Failed to check IP: %v", err))
8685
}
8786

88-
ipv6Ranges, _ := ip.MergeIPRanges(constant.IPv6, newEg.Spec.Ippools.IPv6)
87+
ipv6Ranges, err := ip.MergeIPRanges(constant.IPv6, newEg.Spec.Ippools.IPv6)
8988
if err != nil {
9089
return webhook.Denied(fmt.Sprintf("Failed to check IP: %v", err))
9190
}
@@ -95,15 +94,13 @@ func (egw *EgressGatewayWebhook) EgressGatewayValidate(ctx context.Context, req
9594
if err != nil {
9695
return webhook.Denied(fmt.Sprintf("Failed to check IP: %v", err))
9796
}
98-
9997
}
10098

10199
if egw.Config.FileConfig.EnableIPv6 {
102100
ipv6s, err = ip.ParseIPRanges(constant.IPv6, ipv6Ranges)
103101
if err != nil {
104102
return webhook.Denied(fmt.Sprintf("Failed to check IP: %v", err))
105103
}
106-
107104
}
108105

109106
if egw.Config.FileConfig.EnableIPv4 && egw.Config.FileConfig.EnableIPv6 {
@@ -113,65 +110,143 @@ func (egw *EgressGatewayWebhook) EgressGatewayValidate(ctx context.Context, req
113110
}
114111
}
115112

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))
121121
}
122122
}
123123

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))
128128
}
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))
131131
}
132132
}
133133

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+
// check if the current egw ip pool is duplicated by other egw ip pools
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+
}
141148

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))
145156
}
157+
}
146158

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+
}
149193
}
150194
}
151195
}
152196

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)
156205
if err != nil {
157-
return webhook.Denied(fmt.Sprintf("Failed to check Ipv4DefaultEIP: %v", err))
206+
return nil, err
158207
}
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
161211
}
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)
166213
if err != nil {
167-
return webhook.Denied(fmt.Sprintf("Failed to check Ipv6DefaultEIP: %v", err))
214+
return nil, err
168215
}
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{}{}
171223
}
224+
for _, v := range ipv6s {
225+
m[v.String()] = struct{}{}
226+
}
227+
res[item.Name] = m
172228
}
229+
return res, nil
230+
}
173231

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
175250
}
176251

177252
func (egw *EgressGatewayWebhook) EgressGatewayMutate(ctx context.Context, req webhook.AdmissionRequest) webhook.AdmissionResponse {

0 commit comments

Comments
 (0)