hydra-maester/controllers/oauth2client_controller_integration_test.go

198 lines
6.1 KiB
Go

package controllers_test
import (
"context"
"fmt"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
hydrav1alpha1 "github.com/ory/hydra-maester/api/v1alpha1"
"github.com/ory/hydra-maester/controllers"
"github.com/ory/hydra-maester/hydra"
apiv1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
)
const timeout = time.Second * 5
var _ = Describe("OAuth2Client Controller", func() {
Context("in a happy-path scenario", func() {
var tstName = "test"
var tstNamespace = "default"
var tstScopes = "a b c"
var tstClientID = "testClientID"
var tstSecret = "testSecret"
var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: tstName, Namespace: tstNamespace}}
It("should call create OAuth2 client in Hydra and a Secret", func() {
s := scheme.Scheme
err := hydrav1alpha1.AddToScheme(s)
Expect(err).NotTo(HaveOccurred())
// Setup the Manager and Controller. Wrap the Controller Reconcile function so it writes each request to a
// channel when it is finished.
mgr, err := manager.New(cfg, manager.Options{Scheme: s})
Expect(err).NotTo(HaveOccurred())
c := mgr.GetClient()
mch := (&mockHydraClient{}).
withSecret(tstSecret).
withClientID(tstClientID)
recFn, requests := SetupTestReconcile(getAPIReconciler(mgr, mch))
//_, requests := SetupTestReconcile(getApiReconciler(mgr))
Expect(add(mgr, recFn)).To(Succeed())
//Start the manager and the controller
stopMgr, mgrStopped := StartTestManager(mgr)
//Ensure manager is stopped properly
defer func() {
close(stopMgr)
mgrStopped.Wait()
}()
instance := testInstance(tstName, tstNamespace, tstScopes)
err = c.Create(context.TODO(), instance)
// The instance object may not be a valid object because it might be missing some required fields.
// Please modify the instance object by adding required fields and then remove the following if statement.
if apierrors.IsInvalid(err) {
Fail(fmt.Sprintf("failed to create object, got an invalid object error: %v", err))
return
}
Expect(err).NotTo(HaveOccurred())
defer c.Delete(context.TODO(), instance)
Eventually(requests, timeout).Should(Receive(Equal(expectedRequest)))
//Verify the created CR instance status
var retrieved hydrav1alpha1.OAuth2Client
ok := client.ObjectKey{Name: tstName, Namespace: tstNamespace}
err = c.Get(context.TODO(), ok, &retrieved)
Expect(err).NotTo(HaveOccurred())
Expect(*retrieved.Status.ClientID).To(Equal(tstClientID))
Expect(*retrieved.Status.Secret).To(Equal(tstName)) //Secret contents is not visible in the CR instance!
//Verify the created Secret
var createdSecret = apiv1.Secret{}
k8sClient.Get(context.TODO(), ok, &createdSecret)
Expect(err).NotTo(HaveOccurred())
Expect(createdSecret.Data["client_secret"]).To(Equal([]byte(tstSecret)))
})
})
})
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("api-gateway-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}
// Watch for changes to Api
err = c.Watch(&source.Kind{Type: &hydrav1alpha1.OAuth2Client{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
}
// TODO(user): Modify this to be the types you create
// Uncomment watch a Deployment created by Guestbook - change this for objects you create
//err = c.Watch(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForOwner{
// IsController: true,
// OwnerType: &webappv1.Guestbook{},
//})
//if err != nil {
// return err
//}
return nil
}
func getAPIReconciler(mgr ctrl.Manager, mock *mockHydraClient) reconcile.Reconciler {
return &controllers.OAuth2ClientReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("OAuth2Client"),
HydraClient: mock,
}
}
func testInstance(name, namespace, scopes string) *hydrav1alpha1.OAuth2Client {
return &hydrav1alpha1.OAuth2Client{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: hydrav1alpha1.OAuth2ClientSpec{
GrantTypes: []hydrav1alpha1.GrantType{"client_credentials"},
ResponseTypes: []hydrav1alpha1.ResponseType{"token"},
Scope: scopes,
}}
}
//TODO: Replace with full-fledged mocking framework (mockery/go-mock)
type mockHydraClient struct {
resSecret string
resClientID string
postedData *hydra.OAuth2ClientJSON
}
func (m *mockHydraClient) withSecret(secret string) *mockHydraClient {
m.resSecret = secret
return m
}
func (m *mockHydraClient) withClientID(clientID string) *mockHydraClient {
m.resClientID = clientID
return m
}
//Returns the data previously "stored" by PostOAuth2Client
func (m *mockHydraClient) GetOAuth2Client(id string) (*hydra.OAuth2ClientJSON, bool, error) {
res := &hydra.OAuth2ClientJSON{
ClientID: &m.resClientID,
Secret: &m.resSecret,
Name: m.postedData.Name,
GrantTypes: m.postedData.GrantTypes,
ResponseTypes: m.postedData.ResponseTypes,
Scope: m.postedData.Scope,
}
return res, true, nil
}
func (m *mockHydraClient) PostOAuth2Client(o *hydra.OAuth2ClientJSON) (*hydra.OAuth2ClientJSON, error) {
m.postedData = o
res := &hydra.OAuth2ClientJSON{
ClientID: &m.resClientID,
Secret: &m.resSecret,
Name: o.Name,
GrantTypes: o.GrantTypes,
ResponseTypes: o.ResponseTypes,
Scope: o.Scope,
}
return res, nil
}
func (m *mockHydraClient) DeleteOAuth2Client(id string) error {
panic("Should not be invoked!")
}
func (m *mockHydraClient) PutOAuth2Client(o *hydra.OAuth2ClientJSON) (*hydra.OAuth2ClientJSON, error) {
panic("Should not be invoked!")
}