Make runnable binary
This commit is contained in:
parent
7eedd41bb9
commit
b3c1d98534
11 changed files with 368 additions and 23 deletions
controllers
22
controllers/helpers.go
Normal file
22
controllers/helpers.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package controllers
|
||||
|
||||
// Helper functions to check and remove string from a slice of strings.
|
||||
func containsString(slice []string, s string) bool {
|
||||
for _, item := range slice {
|
||||
if item == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Helper functions to check and remove string from a slice of strings.
|
||||
func removeString(slice []string, s string) (result []string) {
|
||||
for _, item := range slice {
|
||||
if item == s {
|
||||
continue
|
||||
}
|
||||
result = append(result, item)
|
||||
}
|
||||
return
|
||||
}
|
98
controllers/keycloak/keycloak.go
Normal file
98
controllers/keycloak/keycloak.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package keycloak
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Nerzal/gocloak/v7"
|
||||
)
|
||||
|
||||
// Keycloak contains all information to administrate keycloak
|
||||
// and stay logged in
|
||||
type Keycloak struct {
|
||||
mutex sync.Mutex
|
||||
|
||||
client gocloak.GoCloak
|
||||
accessToken string
|
||||
validUntil time.Time
|
||||
user, pass, realm string
|
||||
}
|
||||
|
||||
func (kc *Keycloak) CreateRealmIfNotExists(ctx context.Context, realm gocloak.RealmRepresentation) error {
|
||||
_, err := kc.client.CreateRealm(ctx, kc.getToken(), realm)
|
||||
if isConflict(err) {
|
||||
log.Printf("Realm '%s' already exists, not updated", *realm.Realm)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (kc *Keycloak) CreateProviderIfNotExists(ctx context.Context, realm string, provider gocloak.IdentityProviderRepresentation) error {
|
||||
_, err := kc.client.CreateIdentityProvider(ctx, kc.getToken(), realm, provider)
|
||||
if isConflict(err) {
|
||||
log.Printf("Provider '%s/%s' already exists, not updated", realm, *provider.Alias)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (kc *Keycloak) CreateClientIfNotExists(ctx context.Context, realm string, c gocloak.Client) error {
|
||||
_, err := kc.client.CreateClient(ctx, kc.getToken(), realm, c)
|
||||
if isConflict(err) {
|
||||
log.Printf("Client '%s/%s' already exists, not updated", realm, *c.ClientID)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func New(url, user, pass, realm string) (*Keycloak, error) {
|
||||
kc := &Keycloak{}
|
||||
kc.client = gocloak.NewClient(url)
|
||||
kc.user = user
|
||||
kc.pass = pass
|
||||
kc.realm = realm
|
||||
|
||||
jwt, err := kc.createToken()
|
||||
kc.accessToken = jwt.AccessToken
|
||||
kc.validUntil = time.Now().Add(time.Duration(jwt.ExpiresIn) * time.Second)
|
||||
|
||||
return kc, err
|
||||
}
|
||||
|
||||
func (kc *Keycloak) getToken() string {
|
||||
kc.mutex.Lock()
|
||||
defer kc.mutex.Unlock()
|
||||
|
||||
// If token is not valid 30 seconds in the future,
|
||||
// we need to create a new one
|
||||
if time.Now().Add(30 * time.Second).After(kc.validUntil) {
|
||||
jwt, err := kc.createToken()
|
||||
if err != nil {
|
||||
log.Fatalf("Valid credentials became invalid: %s", err)
|
||||
}
|
||||
|
||||
kc.accessToken = jwt.AccessToken
|
||||
kc.validUntil = time.Now().Add(time.Duration(jwt.ExpiresIn) * time.Second)
|
||||
}
|
||||
|
||||
return kc.accessToken
|
||||
}
|
||||
|
||||
func (kc *Keycloak) createToken() (*gocloak.JWT, error) {
|
||||
token, err := kc.client.LoginAdmin(context.Background(),
|
||||
kc.user, kc.pass, kc.realm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func isConflict(err error) bool {
|
||||
if e, ok := err.(*gocloak.APIError); ok {
|
||||
return (e.Code == 409)
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -20,18 +20,26 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
keycloakv1alpha1 "git.zom.bi/images/keycloak-operator/api/v1alpha1"
|
||||
"git.zom.bi/images/keycloak-operator/controllers/keycloak"
|
||||
)
|
||||
|
||||
const (
|
||||
// FinalizerName is the Name of our finalizer used by this package
|
||||
FinalizerName = "finalizer.keycloak.bitmask.me"
|
||||
)
|
||||
|
||||
// KeycloakRealmReconciler reconciles a KeycloakRealm object
|
||||
type KeycloakRealmReconciler struct {
|
||||
client.Client
|
||||
Log logr.Logger
|
||||
Scheme *runtime.Scheme
|
||||
Keycloak *keycloak.Keycloak
|
||||
Log logr.Logger
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=keycloak.bitmask.me,resources=keycloakrealms,verbs=get;list;watch;create;update;patch;delete
|
||||
|
@ -40,17 +48,37 @@ type KeycloakRealmReconciler struct {
|
|||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
// TODO(user): Modify the Reconcile function to compare the state specified by
|
||||
// the KeycloakRealm object against the actual cluster state, and then
|
||||
// perform operations to make the cluster state reflect the state specified by
|
||||
// the user.
|
||||
//
|
||||
// For more details, check Reconcile and its Result here:
|
||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.7.0/pkg/reconcile
|
||||
func (r *KeycloakRealmReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
_ = r.Log.WithValues("keycloakrealm", req.NamespacedName)
|
||||
log := r.Log.WithValues("keycloakrealm", req.NamespacedName)
|
||||
|
||||
// your logic here
|
||||
log.Info("reconciling")
|
||||
|
||||
var realm keycloakv1alpha1.KeycloakRealm
|
||||
if err := r.Get(ctx, req.NamespacedName, &realm); err != nil {
|
||||
if apierrs.IsNotFound(err) {
|
||||
log.Info("I would now unregister the realm")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
if realm.Status.ID != "" {
|
||||
// try to get existing realm
|
||||
log.Info("Would try to fetch the realm by its id.",
|
||||
"id", realm.Status.ID)
|
||||
// if found {
|
||||
log.Info("will act like i found it, updating.")
|
||||
// update()
|
||||
return ctrl.Result{}, nil
|
||||
// }
|
||||
}
|
||||
|
||||
log.Info("Would now create the realm.")
|
||||
|
||||
realm.Status.ID = "dummy"
|
||||
realm.Status.Available = true
|
||||
r.Status().Update(ctx, &realm)
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue