improve apisix router and metric observer

Signed-off-by: Gallardot <tttick@163.com>
This commit is contained in:
Gallardot
2022-12-06 17:52:38 +05:30
committed by Sanskar Jaiswal
parent badf7b9a4f
commit ce52408bbc
6 changed files with 61 additions and 45 deletions
+2
View File
@@ -87,6 +87,7 @@ type MetricTemplateModel struct {
Target string `json:"target"`
Service string `json:"service"`
Ingress string `json:"ingress"`
Route string `json:"route"`
Interval string `json:"interval"`
}
@@ -98,6 +99,7 @@ func (mtm *MetricTemplateModel) TemplateFunctions() template.FuncMap {
"target": func() string { return mtm.Target },
"service": func() string { return mtm.Service },
"ingress": func() string { return mtm.Ingress },
"route": func() string { return mtm.Route },
"interval": func() string { return mtm.Interval },
}
}
+5
View File
@@ -319,12 +319,17 @@ func toMetricModel(r *flaggerv1.Canary, interval string) flaggerv1.MetricTemplat
if r.Spec.IngressRef != nil {
ingress = r.Spec.IngressRef.Name
}
route := r.Spec.TargetRef.Name
if r.Spec.RouteRef != nil {
route = r.Spec.RouteRef.Name
}
return flaggerv1.MetricTemplateModel{
Name: r.Name,
Namespace: r.Namespace,
Target: r.Spec.TargetRef.Name,
Service: service,
Ingress: ingress,
Route: route,
Interval: interval,
}
}
+3 -4
View File
@@ -29,7 +29,7 @@ var apisixQueries = map[string]string{
sum(
rate(
apisix_http_status{
route=~"{{ namespace }}_{{ target }}-canary_.+",
route=~"{{ namespace }}_{{ route }}-{{ target }}-canary_.+",
code!~"5.."
}[{{ interval }}]
)
@@ -38,7 +38,7 @@ var apisixQueries = map[string]string{
sum(
rate(
apisix_http_status{
route=~"{{ namespace }}_{{ target }}-canary_.+"
route=~"{{ namespace }}_{{ route }}-{{ target }}-canary_.+"
}[{{ interval }}]
)
) * 100`,
@@ -49,7 +49,7 @@ var apisixQueries = map[string]string{
rate(
apisix_http_latency_bucket{
type=~"request",
route=~"{{ namespace }}_{{ target }}-canary_.+"
route=~"{{ namespace }}_{{ route }}-{{ target }}-canary_.+"
}[{{ interval }}]
)
) by (le)
@@ -61,7 +61,6 @@ type ApisixObserver struct {
}
func (ob *ApisixObserver) GetRequestSuccessRate(model flaggerv1.MetricTemplateModel) (float64, error) {
query, err := RenderQuery(apisixQueries["request-success-rate"], model)
if err != nil {
return 0, fmt.Errorf("rendering query failed: %w", err)
+4 -2
View File
@@ -31,7 +31,7 @@ import (
func TestApisixObserver_GetRequestSuccessRate(t *testing.T) {
t.Run("ok", func(t *testing.T) {
expected := ` sum( rate( apisix_http_status{ route=~"default_podinfo-canary_.+", code!~"5.." }[1m] ) ) / sum( rate( apisix_http_status{ route=~"default_podinfo-canary_.+" }[1m] ) ) * 100`
expected := ` sum( rate( apisix_http_status{ route=~"default_podinfo-podinfo-canary_.+", code!~"5.." }[1m] ) ) / sum( rate( apisix_http_status{ route=~"default_podinfo-podinfo-canary_.+" }[1m] ) ) * 100`
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
promql := r.URL.Query()["query"][0]
@@ -56,6 +56,7 @@ func TestApisixObserver_GetRequestSuccessRate(t *testing.T) {
Namespace: "default",
Target: "podinfo",
Service: "podinfo",
Route: "podinfo",
Interval: "1m",
})
require.NoError(t, err)
@@ -84,7 +85,7 @@ func TestApisixObserver_GetRequestSuccessRate(t *testing.T) {
}
func TestApisixObserver_GetRequestDuration(t *testing.T) {
expected := ` histogram_quantile( 0.99, sum( rate( apisix_http_latency_bucket{ type=~"request", route=~"default_podinfo-canary_.+" }[1m] ) ) by (le) )`
expected := ` histogram_quantile( 0.99, sum( rate( apisix_http_latency_bucket{ type=~"request", route=~"default_podinfo-podinfo-canary_.+" }[1m] ) ) by (le) )`
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
promql := r.URL.Query()["query"][0]
@@ -109,6 +110,7 @@ func TestApisixObserver_GetRequestDuration(t *testing.T) {
Namespace: "default",
Target: "podinfo",
Service: "podinfo",
Route: "podinfo",
Interval: "1m",
})
require.NoError(t, err)
+43 -37
View File
@@ -49,24 +49,46 @@ func (ar *ApisixRouter) Reconcile(canary *flaggerv1.Canary) error {
apisixRoute, err := ar.apisixClient.ApisixV2().ApisixRoutes(canary.Namespace).Get(context.TODO(), canary.Spec.RouteRef.Name, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("apisix route %s.%s get query error: %w", canary.Spec.RouteRef.Name, canary.Namespace, err)
return fmt.Errorf("APISIX route %s.%s get query error: %w",
canary.Spec.RouteRef.Name, canary.Namespace, err)
}
apisixRouteClone := apisixRoute.DeepCopy()
if len(apisixRouteClone.Spec.HTTP) != 1 && len(apisixRouteClone.Spec.HTTP[0].Backends) != 1 {
return fmt.Errorf("apisix route %s.%s only one http backend is supported", canary.Spec.RouteRef.Name, canary.Namespace)
if len(apisixRouteClone.Spec.HTTP) == 0 {
return fmt.Errorf("APISIX route %s.%s's spec.http is empty",
canary.Spec.RouteRef.Name, canary.Namespace)
}
httpBackend := apisixRouteClone.Spec.HTTP[0]
httpBackend.Priority = maxPriority
apexName, primaryName, canaryName := canary.GetServiceNames()
var targetHttpRoute *a6v2.ApisixRouteHTTP
var targetIndex int
for index, item := range apisixRouteClone.Spec.HTTP {
for _, backend := range item.Backends {
if backend.ServiceName == apexName {
targetHttpRoute = &item
targetIndex = index
goto found
}
}
}
_, primaryName, canaryName := canary.GetServiceNames()
found:
if targetHttpRoute == nil {
return fmt.Errorf("Can not find %s backend on apisix route %s.%s ",
primaryName, canary.Spec.RouteRef.Name, canary.Namespace)
}
if len(targetHttpRoute.Backends) != 1 {
return fmt.Errorf("APISIX route %s.%s's http route %s only one http backend is supported",
canary.Spec.RouteRef.Name, canary.Namespace, targetHttpRoute.Name)
}
primaryBackend := httpBackend.Backends[0]
targetHttpRoute.Priority = maxPriority
primaryBackend := targetHttpRoute.Backends[0]
primaryBackend.ServiceName = primaryName
primaryWeight := 100
primaryBackend.Weight = &primaryWeight
httpBackend.Backends[0] = primaryBackend
targetHttpRoute.Backends[0] = primaryBackend
canaryWeight := 0
canaryBackend := a6v2.ApisixRouteHTTPBackend{
@@ -76,10 +98,11 @@ func (ar *ApisixRouter) Reconcile(canary *flaggerv1.Canary) error {
Weight: &canaryWeight,
Subset: primaryBackend.Subset,
}
httpBackend.Backends = append(httpBackend.Backends, canaryBackend)
apisixRouteClone.Spec.HTTP[0] = httpBackend
canaryApisixRouteName := fmt.Sprintf("%s-canary", canary.Spec.RouteRef.Name)
targetHttpRoute.Backends = append(targetHttpRoute.Backends, canaryBackend)
apisixRouteClone.Spec.HTTP[targetIndex] = *targetHttpRoute
canaryApisixRouteName := fmt.Sprintf("%s-%s-canary", canary.Spec.RouteRef.Name, apexName)
canaryApisixRoute, err := ar.apisixClient.ApisixV2().ApisixRoutes(canary.Namespace).Get(context.TODO(), canaryApisixRouteName, metav1.GetOptions{})
if errors.IsNotFound(err) {
@@ -105,41 +128,24 @@ func (ar *ApisixRouter) Reconcile(canary *flaggerv1.Canary) error {
_, err := ar.apisixClient.ApisixV2().ApisixRoutes(canary.Namespace).Create(context.TODO(), route, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("apisix route %s.%s create error: %w", route.Name, route.Namespace, err)
return fmt.Errorf("APISIX route %s.%s create error: %w", route.Name, route.Namespace, err)
}
ar.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
Infof("apisix route %s.%s created", route.GetName(), canary.Namespace)
Infof("APISIX route %s.%s created", route.GetName(), canary.Namespace)
return nil
} else if err != nil {
return fmt.Errorf("apisix route %s.%s query error: %w", canaryApisixRouteName, canary.Namespace, err)
return fmt.Errorf("APISIX route %s.%s query error: %w", canaryApisixRouteName, canary.Namespace, err)
}
diffHttpSpec := "placeholder"
diffBackend := "placeholder"
diffCanaryBackend := "placeholder"
if len(canaryApisixRoute.Spec.HTTP) == 1 &&
len(canaryApisixRoute.Spec.HTTP[0].Backends) == 2 {
diffHttpSpec = cmp.Diff(apisixRouteClone.Spec.HTTP[0],
canaryApisixRoute.Spec.HTTP[0],
cmpopts.IgnoreFields(a6v2.ApisixRouteHTTP{}, "Backends"))
diffBackend = cmp.Diff(apisixRouteClone.Spec.HTTP[0].Backends[0],
canaryApisixRoute.Spec.HTTP[0].Backends[0],
cmpopts.IgnoreFields(a6v2.ApisixRouteHTTPBackend{}, "Weight"))
diffCanaryBackend = cmp.Diff(apisixRouteClone.Spec.HTTP[0].Backends[1],
canaryApisixRoute.Spec.HTTP[0].Backends[1],
cmpopts.IgnoreFields(a6v2.ApisixRouteHTTPBackend{}, "Weight"))
}
if diffHttpSpec != "" || diffBackend != "" || diffCanaryBackend != "" {
if diff := cmp.Diff(canaryApisixRoute.Spec, apisixRouteClone.Spec,
cmpopts.IgnoreFields(a6v2.ApisixRouteHTTPBackend{}, "Weight")); diff != "" {
iClone := canaryApisixRoute.DeepCopy()
iClone.Spec = apisixRouteClone.Spec
_, err := ar.apisixClient.ApisixV2().ApisixRoutes(canary.Namespace).Update(context.TODO(), iClone, metav1.UpdateOptions{})
if err != nil {
return fmt.Errorf("apisix route %s.%s update error: %w", canaryApisixRouteName, iClone.Namespace, err)
return fmt.Errorf("APISIX route %s.%s update error: %w", canaryApisixRouteName, iClone.Namespace, err)
}
ar.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
Infof("Apisix route %s updated", canaryApisixRouteName)
@@ -155,13 +161,13 @@ func (ar *ApisixRouter) GetRoutes(canary *flaggerv1.Canary) (
mirrored bool,
err error,
) {
canaryApisixRouteName := fmt.Sprintf("%s-canary", canary.Spec.RouteRef.Name)
apexName, primaryName, _ := canary.GetServiceNames()
canaryApisixRouteName := fmt.Sprintf("%s-%s-canary", canary.Spec.RouteRef.Name, apexName)
apisixRoute, err := ar.apisixClient.ApisixV2().ApisixRoutes(canary.Namespace).Get(context.TODO(), canaryApisixRouteName, metav1.GetOptions{})
if err != nil {
err = fmt.Errorf("apisix route %s.%s query error: %w", canaryApisixRouteName, canary.Namespace, err)
return
}
_, primaryName, _ := canary.GetServiceNames()
for _, backend := range apisixRoute.Spec.HTTP[0].Backends {
if backend.ServiceName == primaryName {
@@ -187,7 +193,7 @@ func (ar *ApisixRouter) SetRoutes(
return fmt.Errorf("RoutingRule %s.%s update failed: no valid weights", apexName, canary.Namespace)
}
canaryApisixRouteName := fmt.Sprintf("%s-canary", canary.Spec.RouteRef.Name)
canaryApisixRouteName := fmt.Sprintf("%s-%s-canary", canary.Spec.RouteRef.Name, apexName)
apisixRoute, err := ar.apisixClient.ApisixV2().ApisixRoutes(canary.Namespace).Get(context.TODO(), canaryApisixRouteName, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("apisix route %s.%s query error: %w", canaryApisixRouteName, canary.Namespace, err)
+4 -2
View File
@@ -38,9 +38,10 @@ func TestApisixRouter_Reconcile(t *testing.T) {
apisixClient: mocks.flaggerClient,
logger: mocks.logger,
}
apexName, _, _ := mocks.canary.GetServiceNames()
err := router.Reconcile(mocks.canary)
require.NoError(t, err)
canaryName := fmt.Sprintf("%s-canary", mocks.canary.Spec.RouteRef.Name)
canaryName := fmt.Sprintf("%s-%s-canary", mocks.canary.Spec.RouteRef.Name, apexName)
arCanary, err := router.apisixClient.ApisixV2().ApisixRoutes("default").Get(context.TODO(), canaryName, metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, 2, len(arCanary.Spec.HTTP[0].Backends))
@@ -77,7 +78,8 @@ func TestApisixRouter_GetSetRoutes(t *testing.T) {
assert.Equal(t, 50, c)
assert.False(t, m)
canaryName := fmt.Sprintf("%s-canary", mocks.canary.Spec.RouteRef.Name)
apexName, _, _ := mocks.canary.GetServiceNames()
canaryName := fmt.Sprintf("%s-%s-canary", mocks.canary.Spec.RouteRef.Name, apexName)
arRouter, err := router.apisixClient.ApisixV2().ApisixRoutes("default").Get(context.TODO(), canaryName, metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, 2, len(arRouter.Spec.HTTP[0].Backends))