Skip to content

Commit ac5e56d

Browse files
authored
chore: add test cases for external service (#2500)
1 parent a2bea45 commit ac5e56d

File tree

1 file changed

+261
-0
lines changed

1 file changed

+261
-0
lines changed

test/e2e/crds/v2/route.go

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/gorilla/websocket"
3131
. "github.com/onsi/ginkgo/v2"
3232
. "github.com/onsi/gomega"
33+
"github.com/stretchr/testify/assert"
3334
"k8s.io/apimachinery/pkg/types"
3435
"k8s.io/apimachinery/pkg/util/wait"
3536

@@ -802,6 +803,7 @@ spec:
802803
}
803804
})
804805
})
806+
805807
Context("Test ApisixRoute sync during startup", func() {
806808
const route = `
807809
apiVersion: apisix.apache.org/v2
@@ -1041,4 +1043,263 @@ spec:
10411043
Expect(string(msg)).To(Equal(testMessage), "message content verification")
10421044
})
10431045
})
1046+
1047+
Context("Test ApisixRoute with External Services", func() {
1048+
const (
1049+
externalServiceName = "ext-httpbin"
1050+
upstreamName = "httpbin-upstream"
1051+
routeName = "httpbin-route"
1052+
)
1053+
1054+
createExternalService := func(externalName string) {
1055+
By(fmt.Sprintf("create ExternalName service: %s -> %s", externalServiceName, externalName))
1056+
svcSpec := fmt.Sprintf(`
1057+
apiVersion: v1
1058+
kind: Service
1059+
metadata:
1060+
name: %s
1061+
spec:
1062+
type: ExternalName
1063+
externalName: %s
1064+
`, externalServiceName, externalName)
1065+
err := s.CreateResourceFromString(svcSpec)
1066+
Expect(err).ShouldNot(HaveOccurred(), "creating ExternalName service")
1067+
}
1068+
1069+
createApisixUpstream := func(externalType apiv2.ApisixUpstreamExternalType, name string) {
1070+
By(fmt.Sprintf("create ApisixUpstream: type=%s, name=%s", externalType, name))
1071+
upstreamSpec := fmt.Sprintf(`
1072+
apiVersion: apisix.apache.org/v2
1073+
kind: ApisixUpstream
1074+
metadata:
1075+
name: %s
1076+
spec:
1077+
externalNodes:
1078+
- type: %s
1079+
name: %s
1080+
`, upstreamName, externalType, name)
1081+
var upstream apiv2.ApisixUpstream
1082+
applier.MustApplyAPIv2(
1083+
types.NamespacedName{Namespace: s.Namespace(), Name: upstreamName},
1084+
&upstream,
1085+
upstreamSpec,
1086+
)
1087+
}
1088+
1089+
createApisixRoute := func() {
1090+
By("create ApisixRoute referencing ApisixUpstream")
1091+
routeSpec := fmt.Sprintf(`
1092+
apiVersion: apisix.apache.org/v2
1093+
kind: ApisixRoute
1094+
metadata:
1095+
name: %s
1096+
spec:
1097+
ingressClassName: apisix
1098+
http:
1099+
- name: rule1
1100+
match:
1101+
hosts:
1102+
- httpbin.org
1103+
paths:
1104+
- /ip
1105+
upstreams:
1106+
- name: %s
1107+
`, routeName, upstreamName)
1108+
var route apiv2.ApisixRoute
1109+
applier.MustApplyAPIv2(
1110+
types.NamespacedName{Namespace: s.Namespace(), Name: routeName},
1111+
&route,
1112+
routeSpec,
1113+
)
1114+
}
1115+
1116+
createApisixRouteWithHostRewrite := func(host string) {
1117+
By("create ApisixRoute with host rewrite")
1118+
routeSpec := fmt.Sprintf(`
1119+
apiVersion: apisix.apache.org/v2
1120+
kind: ApisixRoute
1121+
metadata:
1122+
name: %s
1123+
spec:
1124+
ingressClassName: apisix
1125+
http:
1126+
- name: rule1
1127+
match:
1128+
hosts:
1129+
- httpbin.org
1130+
paths:
1131+
- /ip
1132+
upstreams:
1133+
- name: %s
1134+
plugins:
1135+
- name: proxy-rewrite
1136+
enable: true
1137+
config:
1138+
host: %s
1139+
`, routeName, upstreamName, host)
1140+
var route apiv2.ApisixRoute
1141+
applier.MustApplyAPIv2(
1142+
types.NamespacedName{Namespace: s.Namespace(), Name: routeName},
1143+
&route,
1144+
routeSpec,
1145+
)
1146+
}
1147+
1148+
verifyAccess := func() {
1149+
By("verify access to external service")
1150+
request := func() int {
1151+
return s.NewAPISIXClient().GET("/ip").
1152+
WithHost("httpbin.org").
1153+
Expect().Raw().StatusCode
1154+
}
1155+
Eventually(request).WithTimeout(30 * time.Second).ProbeEvery(2 * time.Second).
1156+
Should(Equal(http.StatusOK))
1157+
}
1158+
1159+
It("access third-party service directly", func() {
1160+
createApisixUpstream(apiv2.ExternalTypeDomain, "httpbin.org")
1161+
createApisixRoute()
1162+
verifyAccess()
1163+
})
1164+
1165+
It("access third-party service with host rewrite", func() {
1166+
createApisixUpstream(apiv2.ExternalTypeDomain, "httpbin.org")
1167+
createApisixRouteWithHostRewrite("httpbin.org")
1168+
verifyAccess()
1169+
})
1170+
1171+
It("access external domain via ExternalName service", func() {
1172+
createExternalService("httpbin.org")
1173+
createApisixUpstream(apiv2.ExternalTypeService, externalServiceName)
1174+
createApisixRoute()
1175+
verifyAccess()
1176+
})
1177+
1178+
It("access in-cluster service via ExternalName", func() {
1179+
By("create temporary httpbin service")
1180+
1181+
By("get FQDN of temporary service")
1182+
fqdn := fmt.Sprintf("%s.%s.svc.cluster.local", "httpbin-service-e2e-test", s.Namespace())
1183+
1184+
By("setup external service and route")
1185+
createExternalService(fqdn)
1186+
createApisixUpstream(apiv2.ExternalTypeService, externalServiceName)
1187+
createApisixRoute()
1188+
verifyAccess()
1189+
})
1190+
1191+
Context("complex scenarios", func() {
1192+
It("multiple external services in one upstream", func() {
1193+
By("create ApisixUpstream with multiple external nodes")
1194+
upstreamSpec := `
1195+
apiVersion: apisix.apache.org/v2
1196+
kind: ApisixUpstream
1197+
metadata:
1198+
name: httpbin-upstream
1199+
spec:
1200+
externalNodes:
1201+
- type: Domain
1202+
name: httpbin.org
1203+
- type: Domain
1204+
name: postman-echo.com
1205+
`
1206+
var upstream apiv2.ApisixUpstream
1207+
applier.MustApplyAPIv2(
1208+
types.NamespacedName{Namespace: s.Namespace(), Name: upstreamName},
1209+
&upstream,
1210+
upstreamSpec,
1211+
)
1212+
1213+
createApisixRoute()
1214+
1215+
By("verify access to multiple services")
1216+
time.Sleep(7 * time.Second)
1217+
hasEtag := false // postman-echo.com
1218+
hasNoEtag := false // httpbin.org
1219+
for range 20 {
1220+
headers := s.NewAPISIXClient().GET("/ip").
1221+
WithHeader("Host", "httpbin.org").
1222+
WithHeader("X-Foo", "bar").
1223+
Expect().
1224+
Headers().Raw()
1225+
if _, ok := headers["Etag"]; ok {
1226+
hasEtag = true
1227+
} else {
1228+
hasNoEtag = true
1229+
}
1230+
if hasEtag && hasNoEtag {
1231+
break
1232+
}
1233+
}
1234+
assert.True(GinkgoT(), hasEtag && hasNoEtag, "both httpbin and postman should be accessed at least once")
1235+
})
1236+
1237+
It("should be able to use backends and upstreams together", func() {
1238+
upstreamSpec := `
1239+
apiVersion: apisix.apache.org/v2
1240+
kind: ApisixUpstream
1241+
metadata:
1242+
name: httpbin-upstream
1243+
spec:
1244+
externalNodes:
1245+
- type: Domain
1246+
name: postman-echo.com
1247+
`
1248+
var upstream apiv2.ApisixUpstream
1249+
applier.MustApplyAPIv2(
1250+
types.NamespacedName{Namespace: s.Namespace(), Name: upstreamName},
1251+
&upstream,
1252+
upstreamSpec,
1253+
)
1254+
By("create ApisixRoute with both backends and upstreams")
1255+
routeSpec := fmt.Sprintf(`
1256+
apiVersion: apisix.apache.org/v2
1257+
kind: ApisixRoute
1258+
metadata:
1259+
name: %s
1260+
spec:
1261+
ingressClassName: apisix
1262+
http:
1263+
- name: rule1
1264+
match:
1265+
hosts:
1266+
- httpbin.org
1267+
paths:
1268+
- /ip
1269+
backends:
1270+
- serviceName: httpbin-service-e2e-test
1271+
servicePort: 80
1272+
resolveGranularity: service
1273+
upstreams:
1274+
- name: %s
1275+
`, routeName, upstreamName)
1276+
var route apiv2.ApisixRoute
1277+
applier.MustApplyAPIv2(
1278+
types.NamespacedName{Namespace: s.Namespace(), Name: routeName},
1279+
&route,
1280+
routeSpec,
1281+
)
1282+
By("verify access to multiple services")
1283+
time.Sleep(7 * time.Second)
1284+
hasEtag := false // postman-echo.com
1285+
hasNoEtag := false // httpbin.org
1286+
for range 20 {
1287+
headers := s.NewAPISIXClient().GET("/ip").
1288+
WithHeader("Host", "httpbin.org").
1289+
WithHeader("X-Foo", "bar").
1290+
Expect().
1291+
Headers().Raw()
1292+
if _, ok := headers["Etag"]; ok {
1293+
hasEtag = true
1294+
} else {
1295+
hasNoEtag = true
1296+
}
1297+
if hasEtag && hasNoEtag {
1298+
break
1299+
}
1300+
}
1301+
assert.True(GinkgoT(), hasEtag && hasNoEtag, "both httpbin and postman should be accessed at least once")
1302+
})
1303+
})
1304+
})
10441305
})

0 commit comments

Comments
 (0)