Skip to content

Commit 43dc569

Browse files
committed
Ingress controller implemented
1 parent 0f65bfa commit 43dc569

File tree

6 files changed

+208
-0
lines changed

6 files changed

+208
-0
lines changed

PROJECT

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,10 @@ resources:
1717
kind: Dashboard
1818
path: github.com/rajsinghtech/homer-operator.git/api/v1alpha1
1919
version: v1alpha1
20+
- controller: true
21+
domain: k8s.io
22+
group: networking
23+
kind: Ingress
24+
path: k8s.io/api/networking/v1
25+
version: v1
2026
version: "3"

cmd/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ func main() {
129129
setupLog.Error(err, "unable to create controller", "controller", "Dashboard")
130130
os.Exit(1)
131131
}
132+
if err = (&controller.IngressReconciler{
133+
Client: mgr.GetClient(),
134+
Scheme: mgr.GetScheme(),
135+
}).SetupWithManager(mgr); err != nil {
136+
setupLog.Error(err, "unable to create controller", "controller", "Ingress")
137+
os.Exit(1)
138+
}
132139
//+kubebuilder:scaffold:builder
133140

134141
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
Copyright 2024 RajSingh.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package controller
18+
19+
import (
20+
"context"
21+
homerv1alpha1 "github.com/rajsinghtech/homer-operator.git/api/v1alpha1"
22+
networkingv1 "k8s.io/api/networking/v1"
23+
"k8s.io/apimachinery/pkg/runtime"
24+
ctrl "sigs.k8s.io/controller-runtime"
25+
"sigs.k8s.io/controller-runtime/pkg/client"
26+
homer "github.com/rajsinghtech/homer-operator.git/pkg/homer"
27+
corev1 "k8s.io/api/core/v1"
28+
"sigs.k8s.io/controller-runtime/pkg/log"
29+
)
30+
31+
// IngressReconciler reconciles a Ingress object
32+
type IngressReconciler struct {
33+
client.Client
34+
Scheme *runtime.Scheme
35+
}
36+
37+
//+kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;create;update;patch;delete
38+
//+kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses/status,verbs=get;update;patch
39+
//+kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses/finalizers,verbs=update
40+
41+
// Reconcile is part of the main kubernetes reconciliation loop which aims to
42+
// move the current state of the cluster closer to the desired state.
43+
// TODO(user): Modify the Reconcile function to compare the state specified by
44+
// the Ingress object against the actual cluster state, and then
45+
// perform operations to make the cluster state reflect the state specified by
46+
// the user.
47+
//
48+
// For more details, check Reconcile and its Result here:
49+
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
50+
func (r *IngressReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
51+
log := log.FromContext(ctx)
52+
var ingress networkingv1.Ingress
53+
if err := r.Get(ctx, req.NamespacedName, &ingress); err != nil {
54+
if client.IgnoreNotFound(err) != nil {
55+
log.Error(err, "unable to fetch Ingress", "ingress", req.NamespacedName)
56+
return ctrl.Result{}, client.IgnoreNotFound(err)
57+
}
58+
}
59+
dashboardList, error := getAllDashboard(ctx, r)
60+
if error != nil {
61+
log.Error(error, "unable to fetch DashboardList")
62+
return ctrl.Result{}, error
63+
}
64+
for _, dashboard := range dashboardList.Items {
65+
// Check if dashboard annotations are a subset of the ingress annotations
66+
delete(dashboard.Annotations, "kubectl.kubernetes.io/last-applied-configuration")
67+
if isSubset(ingress.Annotations, dashboard.Annotations) {
68+
configMap := corev1.ConfigMap{}
69+
log.Info("Dashboard annotations are a subset of the ingress annotations", "dashboard", dashboard.Name)
70+
if error := r.Get(ctx, client.ObjectKey{Namespace: dashboard.Namespace, Name: dashboard.Name}, &configMap); error != nil {
71+
log.Error(error, "unable to fetch ConfigMap", "configmap", dashboard.Name)
72+
return ctrl.Result{}, error
73+
}
74+
homer.UpdateConfigMapIngress(&configMap, ingress)
75+
if error := r.Update(ctx, &configMap); error != nil {
76+
log.Error(error, "unable to update ConfigMap", "configmap", dashboard.Name)
77+
return ctrl.Result{}, error
78+
}
79+
log.Info("Updated ConfigMap", "configmap", dashboard.Name)
80+
}
81+
}
82+
83+
return ctrl.Result{}, nil
84+
}
85+
86+
// isSubset checks if the first map is a subset of the second map
87+
func isSubset(map1, map2 map[string]string) bool {
88+
for key, value := range map2 {
89+
if map1[key] != value {
90+
return false
91+
}
92+
}
93+
return true
94+
}
95+
96+
// SetupWithManager sets up the controller with the Manager.
97+
func (r *IngressReconciler) SetupWithManager(mgr ctrl.Manager) error {
98+
return ctrl.NewControllerManagedBy(mgr).
99+
For(&networkingv1.Ingress{}).
100+
Complete(r)
101+
}
102+
103+
func getAllDashboard(ctx context.Context, r *IngressReconciler) (*homerv1alpha1.DashboardList, error) {
104+
var dashboardList homerv1alpha1.DashboardList
105+
if err := r.List(ctx, &dashboardList); err != nil {
106+
return nil, err
107+
}
108+
return &dashboardList, nil
109+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
Copyright 2024 RajSingh.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package controller
18+
19+
import (
20+
. "github.com/onsi/ginkgo/v2"
21+
)
22+
23+
var _ = Describe("Ingress Controller", func() {
24+
Context("When reconciling a resource", func() {
25+
26+
It("should successfully reconcile the resource", func() {
27+
28+
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
29+
// Example: If you expect a certain status condition after reconciliation, verify it here.
30+
})
31+
})
32+
})

internal/controller/suite_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import (
3232
logf "sigs.k8s.io/controller-runtime/pkg/log"
3333
"sigs.k8s.io/controller-runtime/pkg/log/zap"
3434

35+
networkingv1 "k8s.io/api/networking/v1"
36+
3537
homerv1alpha1 "github.com/rajsinghtech/homer-operator.git/api/v1alpha1"
3638
//+kubebuilder:scaffold:imports
3739
)
@@ -75,6 +77,9 @@ var _ = BeforeSuite(func() {
7577
err = homerv1alpha1.AddToScheme(scheme.Scheme)
7678
Expect(err).NotTo(HaveOccurred())
7779

80+
err = networkingv1.AddToScheme(scheme.Scheme)
81+
Expect(err).NotTo(HaveOccurred())
82+
7883
//+kubebuilder:scaffold:scheme
7984

8085
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})

pkg/homer/config.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,52 @@ func UpdateHomerConfig(config *HomerConfig, ingresses networkingv1.IngressList)
236236
}
237237
return nil
238238
}
239+
func UpdateHomerConfigIngress(homerConfig *HomerConfig, ingress networkingv1.Ingress) {
240+
service := Service{}
241+
item := Item{}
242+
service.Name = ingress.ObjectMeta.Namespace
243+
item.Name = ingress.ObjectMeta.Name
244+
service.Logo = "https://raw.githubusercontent.com/kubernetes/community/master/icons/png/resources/labeled/ns-128.png"
245+
if len(ingress.Spec.TLS) > 0 {
246+
item.Url = "https://" + ingress.Spec.Rules[0].Host
247+
} else {
248+
item.Url = "http://" + ingress.Spec.Rules[0].Host
249+
}
250+
item.Logo = "https://raw.githubusercontent.com/kubernetes/community/master/icons/png/resources/labeled/ing-128.png"
251+
item.Subtitle = ingress.Spec.Rules[0].Host
252+
for key, value := range ingress.ObjectMeta.Annotations {
253+
if strings.HasPrefix(key, "item.homer.rajsingh.info/") {
254+
fieldName := strings.TrimPrefix(key, "item.homer.rajsingh.info/")
255+
reflect.ValueOf(&item).Elem().FieldByName(fieldName).SetString(value)
256+
}
257+
if strings.HasPrefix(key, "service.homer.rajsingh.info/") {
258+
fieldName := strings.TrimPrefix(key, "service.homer.rajsingh.info/")
259+
reflect.ValueOf(&service).Elem().FieldByName(fieldName).SetString(value)
260+
}
261+
}
262+
for sx, s := range homerConfig.Services {
263+
if s.Name == service.Name {
264+
for ix, i := range s.Items {
265+
if i.Name == item.Name {
266+
homerConfig.Services[sx].Items[ix] = item
267+
return
268+
}
269+
}
270+
homerConfig.Services[sx].Items = append(homerConfig.Services[sx].Items, item)
271+
}
272+
}
273+
}
274+
275+
func UpdateConfigMapIngress(cm *corev1.ConfigMap, ingress networkingv1.Ingress) {
276+
homerConfig := HomerConfig{}
277+
err := yaml.Unmarshal([]byte(cm.Data["config.yml"]), &homerConfig)
278+
if err != nil {
279+
return
280+
}
281+
UpdateHomerConfigIngress(&homerConfig, ingress)
282+
objYAML, err := yaml.Marshal(homerConfig)
283+
if err != nil {
284+
return
285+
}
286+
cm.Data["config.yml"] = string(objYAML)
287+
}

0 commit comments

Comments
 (0)