Skip to content

Commit

Permalink
kuadrant status reports on lack of at least one of the supported Gate…
Browse files Browse the repository at this point in the history
…wayAPI providers
  • Loading branch information
eguzki committed May 27, 2024
1 parent 5f16900 commit d9a3850
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 27 deletions.
2 changes: 2 additions & 0 deletions controllers/kuadrant_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
limitadorv1alpha1 "github.com/kuadrant/limitador-operator/api/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -46,6 +47,7 @@ const (
// KuadrantReconciler reconciles a Kuadrant object
type KuadrantReconciler struct {
*reconcilers.BaseReconciler
RestMapper meta.RESTMapper
}

//+kubebuilder:rbac:groups=kuadrant.io,resources=kuadrants,verbs=get;list;watch;create;update;patch;delete
Expand Down
44 changes: 43 additions & 1 deletion controllers/kuadrant_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
Expand All @@ -19,6 +20,7 @@ import (

kuadrantv1beta1 "github.com/kuadrant/kuadrant-operator/api/v1beta1"
"github.com/kuadrant/kuadrant-operator/pkg/common"
kuadrantistioutils "github.com/kuadrant/kuadrant-operator/pkg/istio"
)

const (
Expand Down Expand Up @@ -97,7 +99,18 @@ func (r *KuadrantReconciler) readyCondition(ctx context.Context, kObj *kuadrantv
return cond, nil
}

reason, err := r.checkLimitadorReady(ctx, kObj)
reason, err := r.checkGatewayProviders()
if err != nil {
return nil, err
}
if reason != nil {
cond.Status = metav1.ConditionFalse
cond.Reason = "GatewayAPIPRoviderNotFound"
cond.Message = *reason
return cond, nil
}

reason, err = r.checkLimitadorReady(ctx, kObj)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -173,3 +186,32 @@ func (r *KuadrantReconciler) checkAuthorinoAvailable(ctx context.Context, kObj *

return nil, nil
}

func (r *KuadrantReconciler) checkGatewayProviders() (*string, error) {
anyProviderFunc := func(checks []func(restMapper meta.RESTMapper) (bool, error)) (bool, error) {
for _, check := range checks {
ok, err := check(r.RestMapper)
if err != nil {
return false, err
}
if ok {
return true, nil
}
}
return false, nil
}

anyProvider, err := anyProviderFunc([]func(restMapper meta.RESTMapper) (bool, error){
kuadrantistioutils.IsIstioInstalled,
})

if err != nil {
return nil, err
}

if anyProvider {
return nil, nil
}

return ptr.To("GatewayAPI provider not found"), nil
}
2 changes: 1 addition & 1 deletion controllers/limitador_cluster_envoyfilter_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func (r *LimitadorClusterEnvoyFilterReconciler) desiredRateLimitingClusterEnvoyF

// SetupWithManager sets up the controller with the Manager.
func (r *LimitadorClusterEnvoyFilterReconciler) SetupWithManager(mgr ctrl.Manager) error {
ok, err := kuadrantistioutils.IsIstioEnvoyFilterInstalled(mgr.GetRESTMapper())
ok, err := kuadrantistioutils.IsEnvoyFilterInstalled(mgr.GetRESTMapper())
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion controllers/rate_limiting_wasmplugin_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ func addHTTPRouteByGatewayIndexer(mgr ctrl.Manager, baseLogger logr.Logger) erro

// SetupWithManager sets up the controller with the Manager.
func (r *RateLimitingWASMPluginReconciler) SetupWithManager(mgr ctrl.Manager) error {
ok, err := kuadrantistioutils.IsIstioWASMPluginInstalled(mgr.GetRESTMapper())
ok, err := kuadrantistioutils.IsWASMPluginInstalled(mgr.GetRESTMapper())
if err != nil {
return err
}
Expand Down
2 changes: 2 additions & 0 deletions controllers/ratelimitpolicy_status_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build unit

package controllers

import (
Expand Down
1 change: 1 addition & 0 deletions controllers/test_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ func SetupKuadrantOperatorForTest(s *runtime.Scheme, cfg *rest.Config) {

err = (&KuadrantReconciler{
BaseReconciler: kuadrantBaseReconciler,
RestMapper: mgr.GetRESTMapper(),
}).SetupWithManager(mgr)

Expect(err).NotTo(HaveOccurred())
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ func main() {

if err = (&controllers.KuadrantReconciler{
BaseReconciler: kuadrantBaseReconciler,
RestMapper: mgr.GetRESTMapper(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Kuadrant")
os.Exit(1)
Expand Down
2 changes: 1 addition & 1 deletion pkg/istio/external_authorizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func getIstioConfigObjects(ctx context.Context, cl client.Client) ([]configWrapp
iop := &iopv1alpha1.IstioOperator{}
istKey := client.ObjectKey{Name: controlPlaneProviderName(), Namespace: controlPlaneProviderNamespace()}
err := cl.Get(ctx, istKey, iop)
// TODO(eguzki): burn this spaghetti code
// TODO(eguzki): 🔥 this spaghetti code 🔥
if err == nil {
configsToUpdate = append(configsToUpdate, NewOperatorWrapper(iop))
} else if meta.IsNoMatchError(err) || apierrors.IsNotFound(err) {
Expand Down
51 changes: 49 additions & 2 deletions pkg/istio/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
istiocommon "istio.io/api/type/v1beta1"
istioclientgoextensionv1alpha1 "istio.io/client-go/pkg/apis/extensions/v1alpha1"
istioclientnetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
istioclientgosecurityv1beta1 "istio.io/client-go/pkg/apis/security/v1beta1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -35,7 +36,7 @@ func PolicyTargetRefFromGateway(gateway *gatewayapiv1.Gateway) *istiocommon.Poli
}
}

func IsIstioEnvoyFilterInstalled(restMapper meta.RESTMapper) (bool, error) {
func IsEnvoyFilterInstalled(restMapper meta.RESTMapper) (bool, error) {
_, err := restMapper.RESTMapping(
schema.GroupKind{Group: istioclientnetworkingv1alpha3.GroupName, Kind: "EnvoyFilter"},
istioclientnetworkingv1alpha3.SchemeGroupVersion.Version,
Expand All @@ -52,7 +53,7 @@ func IsIstioEnvoyFilterInstalled(restMapper meta.RESTMapper) (bool, error) {
return false, err
}

func IsIstioWASMPluginInstalled(restMapper meta.RESTMapper) (bool, error) {
func IsWASMPluginInstalled(restMapper meta.RESTMapper) (bool, error) {
_, err := restMapper.RESTMapping(
schema.GroupKind{Group: istioclientgoextensionv1alpha1.GroupName, Kind: "WasmPlugin"},
istioclientgoextensionv1alpha1.SchemeGroupVersion.Version,
Expand All @@ -68,3 +69,49 @@ func IsIstioWASMPluginInstalled(restMapper meta.RESTMapper) (bool, error) {

return false, err
}

func IsAuthorizationPolicyInstalled(restMapper meta.RESTMapper) (bool, error) {
_, err := restMapper.RESTMapping(
schema.GroupKind{Group: istioclientgosecurityv1beta1.GroupName, Kind: "AuthorizationPolicy"},
istioclientgosecurityv1beta1.SchemeGroupVersion.Version,
)

if err == nil {
return true, nil
}

if meta.IsNoMatchError(err) {
return false, nil
}

return false, err
}

func IsIstioInstalled(restMapper meta.RESTMapper) (bool, error) {
ok, err := IsWASMPluginInstalled(restMapper)
if err != nil {
return false, err
}
if !ok {
return false, nil
}

ok, err = IsAuthorizationPolicyInstalled(restMapper)
if err != nil {
return false, err
}
if !ok {
return false, nil
}

ok, err = IsEnvoyFilterInstalled(restMapper)
if err != nil {
return false, err
}
if !ok {
return false, nil
}

// Istio found
return true, nil
}
6 changes: 3 additions & 3 deletions tests/bare_k8s/kuadrant_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"

kuadrantv1beta1 "github.com/kuadrant/kuadrant-operator/api/v1beta1"
"github.com/kuadrant/kuadrant-operator/controllers"
"github.com/kuadrant/kuadrant-operator/tests"
)

var _ = Describe("Kuadrant controller is disabled", func() {
Expand All @@ -22,11 +22,11 @@ var _ = Describe("Kuadrant controller is disabled", func() {
)

BeforeEach(func(ctx SpecContext) {
testNamespace = controllers.CreateNamespace(ctx, testClient())
testNamespace = tests.CreateNamespace(ctx, testClient())
})

AfterEach(func(ctx SpecContext) {
controllers.DeleteNamespace(ctx, testClient(), testNamespace)
tests.DeleteNamespace(ctx, testClient(), testNamespace)
}, afterEachTimeOut)

Context("when default kuadrant CR is created", func() {
Expand Down
24 changes: 14 additions & 10 deletions tests/gatewayapi/kuadrant_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import (

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

kuadrantv1beta1 "github.com/kuadrant/kuadrant-operator/api/v1beta1"
"github.com/kuadrant/kuadrant-operator/controllers"
"github.com/kuadrant/kuadrant-operator/tests"
)

var _ = Describe("Kuadrant controller when gateway provider is missing", func() {
Expand All @@ -22,11 +24,11 @@ var _ = Describe("Kuadrant controller when gateway provider is missing", func()
)

BeforeEach(func(ctx SpecContext) {
testNamespace = controllers.CreateNamespace(ctx, testClient())
testNamespace = tests.CreateNamespace(ctx, testClient())
})

AfterEach(func(ctx SpecContext) {
controllers.DeleteNamespace(ctx, testClient(), testNamespace)
tests.DeleteNamespace(ctx, testClient(), testNamespace)
}, afterEachTimeOut)

Context("when default kuadrant CR is created", func() {
Expand All @@ -43,14 +45,16 @@ var _ = Describe("Kuadrant controller when gateway provider is missing", func()
}
Expect(testClient().Create(ctx, kuadrantCR)).ToNot(HaveOccurred())

kObj := &kuadrantv1beta1.Kuadrant{}
err := testClient().Get(ctx, client.ObjectKeyFromObject(kuadrantCR), kObj)
Expect(err).ToNot(HaveOccurred())
// expected empty. The controller should not have updated it

// TODO

//Expect(kObj.Status).To(Equal(kuadrantv1beta1.KuadrantStatus{}))
Eventually(func(g Gomega) {
kObj := &kuadrantv1beta1.Kuadrant{}
err := testClient().Get(ctx, client.ObjectKeyFromObject(kuadrantCR), kObj)
g.Expect(err).ToNot(HaveOccurred())
cond := meta.FindStatusCondition(kObj.Status.Conditions, string(controllers.ReadyConditionType))
g.Expect(cond).ToNot(BeNil())
g.Expect(cond.Status).To(Equal(metav1.ConditionFalse))
g.Expect(cond.Reason).To(Equal("GatewayAPIPRoviderNotFound"))
g.Expect(cond.Message).To(Equal("GatewayAPI provider not found"))
}, time.Minute, 15*time.Second).WithContext(ctx).Should(Succeed())
})
})
})
22 changes: 14 additions & 8 deletions tests/istio/kuadrant_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ import (

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

kuadrantv1beta1 "github.com/kuadrant/kuadrant-operator/api/v1beta1"
"github.com/kuadrant/kuadrant-operator/controllers"
"github.com/kuadrant/kuadrant-operator/tests"
)

var _ = Describe("Kuadrant controller is disabled", func() {
var _ = Describe("Kuadrant controller on istio", func() {
var (
testNamespace string
kuadrantName string = "local"
Expand All @@ -30,7 +32,7 @@ var _ = Describe("Kuadrant controller is disabled", func() {
}, afterEachTimeOut)

Context("when default kuadrant CR is created", func() {
It("Status is not populated", func(ctx SpecContext) {
It("Status is ready", func(ctx SpecContext) {
kuadrantCR := &kuadrantv1beta1.Kuadrant{
TypeMeta: metav1.TypeMeta{
Kind: "Kuadrant",
Expand All @@ -43,12 +45,16 @@ var _ = Describe("Kuadrant controller is disabled", func() {
}
Expect(testClient().Create(ctx, kuadrantCR)).ToNot(HaveOccurred())

kObj := &kuadrantv1beta1.Kuadrant{}
err := testClient().Get(ctx, client.ObjectKeyFromObject(kuadrantCR), kObj)
Expect(err).ToNot(HaveOccurred())
// expected empty. The controller should not have updated it
// TODO: status should not be empty
Expect(kObj.Status).To(Equal(kuadrantv1beta1.KuadrantStatus{}))
Eventually(func(g Gomega) {
kObj := &kuadrantv1beta1.Kuadrant{}
err := testClient().Get(ctx, client.ObjectKeyFromObject(kuadrantCR), kObj)
g.Expect(err).ToNot(HaveOccurred())
cond := meta.FindStatusCondition(kObj.Status.Conditions, string(controllers.ReadyConditionType))
g.Expect(cond).ToNot(BeNil())
g.Expect(cond.Status).To(Equal(metav1.ConditionTrue))
g.Expect(cond.Reason).To(Equal("Ready"))
g.Expect(cond.Message).To(Equal("Kuadrant is ready"))
}, time.Minute, 15*time.Second).WithContext(ctx).Should(Succeed())
})
})
})

0 comments on commit d9a3850

Please sign in to comment.