198 lines
6.1 KiB
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!")
|
||
|
}
|