Skip to content

Commit 8acb2a0

Browse files
theSuesslexfreihkmdxlftjf
committed
add reconciliation logic for HTTPRoutes
Co-Authored-By: Aleksei Sviridkin <[email protected]> Co-Authored-By: sure <[email protected]>
1 parent b99b71a commit 8acb2a0

File tree

8 files changed

+5831
-6
lines changed

8 files changed

+5831
-6
lines changed

api/v1beta1/zz_generated.deepcopy.go

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

controllers/autodetect/main.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ var _ AutoDetect = (*autoDetect)(nil)
1111
// AutoDetect provides an assortment of routines that auto-detect traits based on the runtime.
1212
type AutoDetect interface {
1313
IsOpenshift() (bool, error)
14+
HasGatewayAPI() (bool, error)
1415
}
1516

1617
type autoDetect struct {
@@ -32,19 +33,28 @@ func New(restConfig *rest.Config) (AutoDetect, error) {
3233
}, nil
3334
}
3435

35-
// Platform returns the detected platform this operator is running on. Possible values: Kubernetes, OpenShift.
36-
func (a *autoDetect) IsOpenshift() (bool, error) {
36+
func (a *autoDetect) hasAPIGroup(target string) (bool, error) {
3737
apiList, err := a.dcl.ServerGroups()
3838
if err != nil {
3939
return false, err
4040
}
4141

4242
apiGroups := apiList.Groups
43-
for i := range apiGroups {
44-
if apiGroups[i].Name == "route.openshift.io" {
43+
for _, group := range apiGroups {
44+
if group.Name == target {
4545
return true, nil
4646
}
4747
}
4848

4949
return false, nil
5050
}
51+
52+
// Tests for the presence of the `route.openshift.io` api group as an indicator of if we're running in OpenShift
53+
func (a *autoDetect) IsOpenshift() (bool, error) {
54+
return a.hasAPIGroup("route.openshift.io")
55+
}
56+
57+
// Tests if the GatewayAPI CRDs are present
58+
func (a *autoDetect) HasGatewayAPI() (bool, error) {
59+
return a.hasAPIGroup("gateway.networking.k8s.io")
60+
}

controllers/model/grafana_resources.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1212
"k8s.io/apimachinery/pkg/runtime"
1313
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
14+
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
1415
)
1516

1617
func GetCommonLabels() map[string]string {
@@ -127,6 +128,19 @@ func GetGrafanaRoute(cr *grafanav1beta1.Grafana, scheme *runtime.Scheme) *routev
127128
return route
128129
}
129130

131+
func GetGrafanaHTTPRoute(cr *grafanav1beta1.Grafana, scheme *runtime.Scheme) *gwapiv1.HTTPRoute {
132+
httpRoute := &gwapiv1.HTTPRoute{
133+
ObjectMeta: metav1.ObjectMeta{
134+
Name: fmt.Sprintf("%s-httproute", cr.Name),
135+
Namespace: cr.Namespace,
136+
Labels: GetCommonLabels(),
137+
},
138+
}
139+
controllerutil.SetControllerReference(cr, httpRoute, scheme) //nolint:errcheck
140+
141+
return httpRoute
142+
}
143+
130144
func GetGrafanaDeployment(cr *grafanav1beta1.Grafana, scheme *runtime.Scheme) *v13.Deployment {
131145
deployment := &v13.Deployment{
132146
ObjectMeta: metav1.ObjectMeta{

controllers/reconcilers/grafana/ingress_reconciler.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"sigs.k8s.io/controller-runtime/pkg/client"
1616
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1717
logf "sigs.k8s.io/controller-runtime/pkg/log"
18+
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
1819
)
1920

2021
const (
@@ -42,6 +43,11 @@ func (r *IngressReconciler) Reconcile(ctx context.Context, cr *v1beta1.Grafana,
4243
return r.reconcileRoute(ctx, cr, vars, scheme)
4344
}
4445

46+
if cr.Spec.HTTPRoute != nil {
47+
log.Info("reconciling HTTPRoute")
48+
return r.reconcileHTTPRoute(ctx, cr, vars, scheme)
49+
}
50+
4551
log.Info("reconciling ingress")
4652

4753
return r.reconcileIngress(ctx, cr, vars, scheme)
@@ -139,6 +145,42 @@ func (r *IngressReconciler) reconcileRoute(ctx context.Context, cr *v1beta1.Graf
139145
return v1beta1.OperatorStageResultSuccess, nil
140146
}
141147

148+
func (r *IngressReconciler) reconcileHTTPRoute(ctx context.Context, cr *v1beta1.Grafana, _ *v1beta1.OperatorReconcileVars, scheme *runtime.Scheme) (v1beta1.OperatorStageStatus, error) {
149+
if cr.Spec.HTTPRoute == nil {
150+
return v1beta1.OperatorStageResultSuccess, nil
151+
}
152+
153+
route := model.GetGrafanaHTTPRoute(cr, scheme)
154+
155+
_, err := controllerutil.CreateOrUpdate(ctx, r.client, route, func() error {
156+
route.Spec = getHTTPRouteSpec(cr, scheme)
157+
158+
err := v1beta1.Merge(route, cr.Spec.HTTPRoute)
159+
if err != nil {
160+
setInvalidMergeCondition(cr, "HTTPRoute", err)
161+
return err
162+
}
163+
164+
removeInvalidMergeCondition(cr, "HTTPRoute")
165+
166+
if scheme != nil {
167+
err = controllerutil.SetControllerReference(cr, route, scheme)
168+
if err != nil {
169+
return err
170+
}
171+
}
172+
173+
model.SetInheritedLabels(route, cr.Labels)
174+
175+
return nil
176+
})
177+
if err != nil {
178+
return v1beta1.OperatorStageResultFailed, err
179+
}
180+
181+
return v1beta1.OperatorStageResultSuccess, nil
182+
}
183+
142184
// getIngressAdminURL returns the first valid URL (Host field is set) from the ingress spec
143185
func (r *IngressReconciler) getIngressAdminURL(ingress *v1.Ingress) string {
144186
if ingress == nil {
@@ -229,6 +271,29 @@ func getRouteSpec(cr *v1beta1.Grafana, scheme *runtime.Scheme) routev1.RouteSpec
229271
}
230272
}
231273

274+
func getHTTPRouteSpec(cr *v1beta1.Grafana, scheme *runtime.Scheme) gwapiv1.HTTPRouteSpec {
275+
service := model.GetGrafanaService(cr, scheme)
276+
port := gwapiv1.PortNumber(GetGrafanaPort(cr)) //nolint:gosec
277+
backendRefs := []gwapiv1.HTTPBackendRef{
278+
{
279+
BackendRef: gwapiv1.BackendRef{
280+
BackendObjectReference: gwapiv1.BackendObjectReference{
281+
Name: gwapiv1.ObjectName(service.Name),
282+
Port: &port,
283+
},
284+
},
285+
},
286+
}
287+
288+
return gwapiv1.HTTPRouteSpec{
289+
Rules: []gwapiv1.HTTPRouteRule{
290+
{
291+
BackendRefs: backendRefs,
292+
},
293+
},
294+
}
295+
}
296+
232297
func getIngressSpec(cr *v1beta1.Grafana, scheme *runtime.Scheme) v1.IngressSpec {
233298
service := model.GetGrafanaService(cr, scheme)
234299

controllers/reconcilers/grafana/ingress_reconciler_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
networkingv1 "k8s.io/api/networking/v1"
1313
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1414
"k8s.io/apimachinery/pkg/types"
15+
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
1516
)
1617

1718
var _ = Describe("Allow use of Ingress on OpenShift", func() {
@@ -72,3 +73,35 @@ var _ = Describe("Allow use of Ingress on OpenShift", func() {
7273
Expect(status).To(Equal(v1beta1.OperatorStageResultFailed), "Route does not exist in Scheme outside of OpenShift")
7374
})
7475
})
76+
77+
var _ = Describe("GatewayAPI support", func() {
78+
It("Creates HTTPRoute when .spec.httpRoute is defined", func() {
79+
r := NewIngressReconciler(k8sClient, false)
80+
cr := &v1beta1.Grafana{
81+
ObjectMeta: metav1.ObjectMeta{
82+
Name: "httproute-test",
83+
Namespace: "default",
84+
Labels: map[string]string{"test": "httproute"},
85+
},
86+
Spec: v1beta1.GrafanaSpec{
87+
HTTPRoute: &v1beta1.HTTPRouteV1{},
88+
},
89+
}
90+
91+
ctx := context.Background()
92+
Expect(k8sClient.Create(ctx, cr)).To(Succeed())
93+
94+
vars := &v1beta1.OperatorReconcileVars{}
95+
status, err := r.Reconcile(ctx, cr, vars, scheme.Scheme)
96+
97+
Expect(err).ToNot(HaveOccurred())
98+
Expect(status).To(Equal(v1beta1.OperatorStageResultSuccess))
99+
100+
httpRoute := &gwapiv1.HTTPRoute{}
101+
err = k8sClient.Get(ctx, types.NamespacedName{
102+
Name: fmt.Sprintf("%s-httproute", cr.Name),
103+
Namespace: "default",
104+
}, httpRoute)
105+
Expect(err).ToNot(HaveOccurred())
106+
})
107+
})

controllers/reconcilers/grafana/suite_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"sigs.k8s.io/controller-runtime/pkg/envtest"
2828
logf "sigs.k8s.io/controller-runtime/pkg/log"
2929
"sigs.k8s.io/controller-runtime/pkg/log/zap"
30+
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
3031

3132
grafanav1beta1 "github.com/grafana/grafana-operator/v5/api/v1beta1"
3233
//+kubebuilder:scaffold:imports
@@ -55,7 +56,10 @@ var _ = BeforeSuite(func() {
5556

5657
By("bootstrapping test environment")
5758
testEnv = &envtest.Environment{
58-
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")},
59+
CRDDirectoryPaths: []string{
60+
filepath.Join("..", "..", "..", "config", "crd", "bases"),
61+
filepath.Join("..", "..", "..", "tests", "fixtures"),
62+
},
5963
ErrorIfCRDPathMissing: true,
6064
}
6165

@@ -65,7 +69,8 @@ var _ = BeforeSuite(func() {
6569

6670
err = grafanav1beta1.AddToScheme(scheme.Scheme)
6771
Expect(err).NotTo(HaveOccurred())
68-
72+
err = gwapiv1.Install(scheme.Scheme)
73+
Expect(err).NotTo(HaveOccurred())
6974
//+kubebuilder:scaffold:scheme
7075

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

main.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import (
5656
"sigs.k8s.io/controller-runtime/pkg/healthz"
5757
"sigs.k8s.io/controller-runtime/pkg/log/zap"
5858
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
59+
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
5960

6061
grafanav1beta1 "github.com/grafana/grafana-operator/v5/api/v1beta1"
6162
"github.com/grafana/grafana-operator/v5/controllers"
@@ -101,6 +102,8 @@ func init() {
101102
utilruntime.Must(grafanav1beta1.AddToScheme(scheme))
102103

103104
utilruntime.Must(routev1.AddToScheme(scheme))
105+
106+
utilruntime.Must(gwapiv1.Install(scheme))
104107
//+kubebuilder:scaffold:scheme
105108
}
106109

@@ -191,6 +194,12 @@ func main() { // nolint:gocyclo
191194
os.Exit(1)
192195
}
193196

197+
hasGatewayAPI, err := autodetect.HasGatewayAPI()
198+
if err != nil {
199+
setupLog.Error(err, "unable to detect the GatewayAPI CRDs")
200+
os.Exit(1)
201+
}
202+
194203
mgrOptions := ctrl.Options{
195204
Scheme: scheme,
196205
Metrics: metricsserver.Options{BindAddress: metricsAddr},
@@ -235,6 +244,9 @@ func main() { // nolint:gocyclo
235244
if isOpenShift {
236245
mgrOptions.Cache.ByObject[&routev1.Route{}] = cacheLabelConfig
237246
}
247+
if hasGatewayAPI {
248+
mgrOptions.Cache.ByObject[&gwapiv1.HTTPRoute{}] = cacheLabelConfig
249+
}
238250

239251
if enforceCacheLabelsLevel == cachingLevelSafe {
240252
mgrOptions.Client.Cache = &client.CacheOptions{

0 commit comments

Comments
 (0)