Skip to content

Commit 2bd62e4

Browse files
committed
feat: initial traefik support
1 parent 3c4e9a7 commit 2bd62e4

File tree

4 files changed

+113
-35
lines changed

4 files changed

+113
-35
lines changed

internal/handlers/idler/helpers.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package idler
22

33
import (
4+
"strings"
5+
46
"k8s.io/apimachinery/pkg/labels"
57
)
68

@@ -17,3 +19,17 @@ func generateLabelRequirements(selectors []idlerSelector) []labels.Requirement {
1719
}
1820
return labelRequirements
1921
}
22+
23+
func addStatusCode(codes string, code string) *string {
24+
if codes == "" {
25+
return &code
26+
}
27+
parts := strings.Split(codes, ",")
28+
for _, c := range parts {
29+
if c == code {
30+
return &codes
31+
}
32+
}
33+
newCodes := codes + "," + code
34+
return &newCodes
35+
}

internal/handlers/idler/service-kubernetes.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,14 +241,24 @@ func (h *Idler) patchIngress(ctx context.Context, opLog logr.Logger, namespace c
241241
for _, ingress := range ingressList.Items {
242242
if !h.DryRun {
243243
ingressCopy := ingress.DeepCopy()
244+
var ingressCodes, traefikMiddlewares *string
245+
ingressValue, ok := ingress.Annotations["nginx.ingress.kubernetes.io/custom-http-errors"]
246+
if ok {
247+
ingressCodes = addStatusCode(ingressValue, "503")
248+
}
249+
traefikValue, ok := ingress.Annotations["traefik.ingress.kubernetes.io/router.middlewares"]
250+
if ok {
251+
traefikMiddlewares = addStatusCode(traefikValue, fmt.Sprintf("%s-aergia@kubernetescrd", ingress.Namespace))
252+
}
244253
mergePatch, _ := json.Marshal(map[string]interface{}{
245254
"metadata": map[string]interface{}{
246255
"labels": map[string]string{
247256
"idling.amazee.io/idled": "true",
248257
},
249-
"annotations": map[string]string{
258+
"annotations": map[string]interface{}{
250259
// add the custom-http-errors annotation so that the unidler knows to handle this ingress
251-
"nginx.ingress.kubernetes.io/custom-http-errors": "503",
260+
"nginx.ingress.kubernetes.io/custom-http-errors": ingressCodes,
261+
"traefik.ingress.kubernetes.io/router.middlewares": traefikMiddlewares,
252262
},
253263
},
254264
})

internal/handlers/unidler/checks.go

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -65,39 +65,54 @@ func (h *Unidler) removeCodeFromIngress(ctx context.Context, ns string, opLog lo
6565
})
6666
ingresses := &networkv1.IngressList{}
6767
if err := h.Client.List(ctx, ingresses, listOption); err != nil {
68-
opLog.Info(fmt.Sprintf("Unable to get any deployments - %s", ns))
68+
opLog.Info(fmt.Sprintf("Unable to get any ingress - %s", ns))
6969
return
7070
}
7171
for _, ingress := range ingresses.Items {
7272
// if the nginx.ingress.kubernetes.io/custom-http-errors annotation is set
7373
// then strip out the 503 error code that is there so that
7474
// users will see their application errors rather than the loading page
75-
if value, ok := ingress.Annotations["nginx.ingress.kubernetes.io/custom-http-errors"]; ok {
76-
newVals := removeStatusCode(value, "503")
75+
var ingressCodes, traefikMiddlewares *string
76+
patch := false
77+
ingressValue, ok := ingress.Annotations["nginx.ingress.kubernetes.io/custom-http-errors"]
78+
if ok {
79+
ingressCodes = removeStatusCode(ingressValue, "503")
80+
patch = true
81+
}
82+
traefikValue, ok := ingress.Annotations["traefik.ingress.kubernetes.io/router.middlewares"]
83+
if ok {
84+
traefikMiddlewares = removeStatusCode(traefikValue, fmt.Sprintf("%s-aergia@kubernetescrd", ingress.Namespace))
85+
patch = true
86+
}
87+
if patch {
7788
// if the 503 code was removed from the annotation
7889
// then patch it
79-
if newVals == nil || *newVals != value {
80-
mergePatch, _ := json.Marshal(map[string]interface{}{
81-
"metadata": map[string]interface{}{
82-
"labels": map[string]interface{}{
83-
"idling.amazee.io/idled": "false",
84-
},
85-
"annotations": map[string]interface{}{
86-
"nginx.ingress.kubernetes.io/custom-http-errors": newVals,
87-
"idling.amazee.io/idled-at": nil,
88-
},
90+
annotations := map[string]interface{}{
91+
"idling.amazee.io/idled-at": nil,
92+
}
93+
if ingressCodes == nil || *ingressCodes != ingressValue {
94+
annotations["nginx.ingress.kubernetes.io/custom-http-errors"] = ingressCodes
95+
}
96+
if traefikMiddlewares == nil || *traefikMiddlewares != traefikValue {
97+
annotations["traefik.ingress.kubernetes.io/router.middlewares"] = traefikMiddlewares
98+
}
99+
mergePatch, _ := json.Marshal(map[string]interface{}{
100+
"metadata": map[string]interface{}{
101+
"labels": map[string]interface{}{
102+
"idling.amazee.io/idled": "false",
89103
},
90-
})
91-
patchIngress := ingress.DeepCopy()
92-
if err := h.Client.Patch(ctx, patchIngress, ctrlClient.RawPatch(types.MergePatchType, mergePatch)); err != nil {
93-
// log it but try and patch the rest of the ingressses anyway (some is better than none?)
94-
opLog.Info(fmt.Sprintf("Error patching custom-http-errors on ingress %s - %s", ingress.Name, ns))
104+
"annotations": annotations,
105+
},
106+
})
107+
patchIngress := ingress.DeepCopy()
108+
if err := h.Client.Patch(ctx, patchIngress, ctrlClient.RawPatch(types.MergePatchType, mergePatch)); err != nil {
109+
// log it but try and patch the rest of the ingressses anyway (some is better than none?)
110+
opLog.Info(fmt.Sprintf("Error patching custom-http-errors on ingress %s - %s", ingress.Name, ns))
111+
} else {
112+
if ingressCodes == nil {
113+
opLog.Info(fmt.Sprintf("Ingress %s custom-http-errors annotation removed - %s", ingress.Name, ns))
95114
} else {
96-
if newVals == nil {
97-
opLog.Info(fmt.Sprintf("Ingress %s custom-http-errors annotation removed - %s", ingress.Name, ns))
98-
} else {
99-
opLog.Info(fmt.Sprintf("Ingress %s custom-http-errors annotation patched with %s - %s", ingress.Name, *newVals, ns))
100-
}
115+
opLog.Info(fmt.Sprintf("Ingress %s custom-http-errors annotation patched with %s - %s", ingress.Name, *ingressCodes, ns))
101116
}
102117
}
103118
}

internal/handlers/unidler/handler.go

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"net/http"
7+
"net/url"
78
"os"
89
"strconv"
910
"strings"
@@ -15,6 +16,7 @@ import (
1516
corev1 "k8s.io/api/core/v1"
1617
networkv1 "k8s.io/api/networking/v1"
1718
"k8s.io/apimachinery/pkg/types"
19+
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
1820
)
1921

2022
func (h *Unidler) ingressHandler(path string) func(http.ResponseWriter, *http.Request) {
@@ -56,6 +58,21 @@ func (h *Unidler) ingressHandler(path string) func(http.ResponseWriter, *http.Re
5658
w.WriteHeader(code)
5759
ns := r.Header.Get(Namespace)
5860
ingressName := r.Header.Get(IngressName)
61+
hostname := ""
62+
nsParam := r.URL.Query().Get("namespace")
63+
if nsParam != "" {
64+
opLog.Info(fmt.Sprintf("Namespace Param: %s", nsParam))
65+
ns = nsParam
66+
}
67+
urlParam := r.URL.Query().Get("url")
68+
if urlParam != "" {
69+
url, err := url.Parse(urlParam)
70+
if err != nil {
71+
opLog.Info(fmt.Sprintf("URL Param err: %v", err))
72+
}
73+
opLog.Info(fmt.Sprintf("URL Param: %s", urlParam))
74+
hostname = url.Hostname()
75+
}
5976
// check if the namespace exists so we know this is somewhat legitimate request
6077
if ns != "" {
6178
namespace := &corev1.Namespace{}
@@ -66,14 +83,34 @@ func (h *Unidler) ingressHandler(path string) func(http.ResponseWriter, *http.Re
6683
return
6784
}
6885
ingress := &networkv1.Ingress{}
69-
if err := h.Client.Get(ctx, types.NamespacedName{
70-
Namespace: ns,
71-
Name: ingressName,
72-
}, ingress); err != nil {
73-
opLog.Info(fmt.Sprintf("Unable to get the ingress %s in %s", ingressName, ns))
74-
h.genericError(w, r, opLog, format, path, 400)
75-
h.setMetrics(r, start)
76-
return
86+
if ingressName != "" {
87+
if err := h.Client.Get(ctx, types.NamespacedName{
88+
Namespace: ns,
89+
Name: ingressName,
90+
}, ingress); err != nil {
91+
opLog.Info(fmt.Sprintf("Unable to get the ingress %s in %s", ingressName, ns))
92+
h.genericError(w, r, opLog, format, path, 400)
93+
h.setMetrics(r, start)
94+
return
95+
}
96+
} else {
97+
listOption := (&ctrlClient.ListOptions{}).ApplyOptions([]ctrlClient.ListOption{
98+
ctrlClient.InNamespace(ns),
99+
})
100+
ingresses := &networkv1.IngressList{}
101+
if err := h.Client.List(ctx, ingresses, listOption); err != nil {
102+
opLog.Info(fmt.Sprintf("Unable to get any ingress - %s", ns))
103+
return
104+
}
105+
for _, ingressss := range ingresses.Items {
106+
for _, rule := range ingressss.Spec.Rules {
107+
for _, host := range rule.Host {
108+
if string(host) == hostname {
109+
ingress = ingressss.DeepCopy()
110+
}
111+
}
112+
}
113+
}
77114
}
78115
// if hmac verification is enabled, perform the verification of the request
79116
signedNamespace, verfied := h.verifyRequest(r, namespace, ingress)
@@ -129,8 +166,8 @@ func (h *Unidler) ingressHandler(path string) func(http.ResponseWriter, *http.Re
129166
CodeHeader: r.Header.Get(CodeHeader),
130167
ContentType: r.Header.Get(ContentType),
131168
OriginalURI: r.Header.Get(OriginalURI),
132-
Namespace: r.Header.Get(Namespace),
133-
IngressName: r.Header.Get(IngressName),
169+
Namespace: ns,
170+
IngressName: ingress.Name,
134171
ServiceName: r.Header.Get(ServiceName),
135172
ServicePort: r.Header.Get(ServicePort),
136173
RequestID: r.Header.Get(RequestID),

0 commit comments

Comments
 (0)