Files
zitadel-k8s-operator/src/internal/controller/zitadelcluster_controller.go
Haim Kortovich 430d4a7383 Add organizationAdmin
[ZITADOPER-1]
2024-05-20 19:45:14 -05:00

593 lines
22 KiB
Go

/*
Copyright 2024.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"context"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"strings"
"time"
zitadelv1alpha1 "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/api/v1alpha1"
builder "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/builder"
condition "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/condition"
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/configuration"
configmap "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/controller/configmap"
secret "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/controller/secret"
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/controller/service"
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/deployment"
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/machinekey"
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/masterkey"
systemapiaccount "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/systemapi"
zitadelClient "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/zitadel"
"github.com/hashicorp/go-multierror"
"github.com/zitadel/zitadel-go/v2/pkg/client/system"
adm "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin"
authn "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/authn"
pb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/system"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/workqueue"
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/log"
)
type reconcilePhase struct {
Name string
Reconcile func(context.Context, *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error)
}
type patcher func(*zitadelv1alpha1.ZitadelClusterStatus) error
// ZitadelClusterReconciler reconciles a ZitadelCluster object
type ZitadelClusterReconciler struct {
client.Client
Scheme *runtime.Scheme
ConditionReady *condition.Ready
Builder *builder.Builder
SecretReconciler *secret.SecretReconciler
ConfigMapReconciler *configmap.ConfigMapReconciler
ServiceReconciler *service.ServiceReconciler
RefResolver *zitadelv1alpha1.RefResolver
}
// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;patch
// +kubebuilder:rbac:groups="",resources=services,verbs=list;watch;create;patch
// +kubebuilder:rbac:groups="",resources=secrets,verbs=list;watch;create;patch
// +kubebuilder:rbac:groups="",resources=endpoints,verbs=create;patch;get;list;watch
// +kubebuilder:rbac:groups="",resources=endpoints/restricted,verbs=create;patch;get;list;watch
// +kubebuilder:rbac:groups="",resources=pods,verbs=get;delete
// +kubebuilder:rbac:groups="",resources=events,verbs=list;watch;create;patch
// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=list;watch;create;patch
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=list;watch;create;patch
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=list;watch;create;patch
// +kubebuilder:rbac:groups=policy,resources=poddisruptionbudgets,verbs=list;watch;create;patch
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles;rolebindings;clusterrolebindings,verbs=list;watch;create;patch
// +kubebuilder:rbac:groups=zitadel.topmanage.com,resources=zitadelclusters,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=zitadel.topmanage.com,resources=zitadelclusters/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=zitadel.topmanage.com,resources=zitadelclusters/finalizers,verbs=update
// +kubebuilder:rbac:groups=crdb.cockroachlabs.com,resources=crdbclusters,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=crdb.cockroachlabs.com,resources=crdbclusters/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=crdb.cockroachlabs.com,resources=crdbclusters/finalizers,verbs=update
// +kubebuilder:rbac:groups=certificates.k8s.io,resources=certificatesigningrequests,verbs=get;list;watch;create;patch;delete
// +kubebuilder:rbac:groups=certificates.k8s.io,resources=certificatesigningrequests/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=certificates.k8s.io,resources=certificatesigningrequests/approval,verbs=update
// +kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;update;patch;delete
func (r *ZitadelClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := log.FromContext(ctx)
logger.Info("Starting Reconcile")
var zitadel zitadelv1alpha1.ZitadelCluster
if err := r.Get(ctx, req.NamespacedName, &zitadel); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
phases := []reconcilePhase{
{
Name: "Spec",
Reconcile: r.setSpecDefaults,
},
{
Name: "Status",
Reconcile: r.setStatusDefaults,
},
{
Name: "MasterkeySecret",
Reconcile: r.reconcileMasterKeySecret,
},
{
Name: "ServiceAccount",
Reconcile: r.reconcileSystemAPIUser,
},
{
Name: "Configuration",
Reconcile: r.reconcileConfig,
},
{
Name: "InitJob",
Reconcile: r.reconcileInitJob,
},
{
Name: "SetupJob",
Reconcile: r.reconcileSetupJob,
},
{
Name: "Deployment",
Reconcile: r.reconcileDeployment,
},
{
Name: "Service",
Reconcile: r.reconcileService,
},
{
Name: "DefaultInstance",
Reconcile: r.reconcileDefaultInstance,
},
{
Name: "SMTPConfig",
Reconcile: r.reconcileSMTPConfig,
},
{
Name: "DomainPolicyConfig",
Reconcile: r.reconcileDomainPolicy,
},
}
for _, p := range phases {
result, err := p.Reconcile(ctx, &zitadel)
if err != nil {
if errors.IsNotFound(err) {
continue
}
var errBundle *multierror.Error
errBundle = multierror.Append(errBundle, err)
msg := fmt.Sprintf("Error reconciling %s: %v", p.Name, err)
patchErr := r.patchStatus(ctx, &zitadel, func(s *zitadelv1alpha1.ZitadelClusterStatus) error {
patcher := r.ConditionReady.PatcherFailed(msg)
patcher(s)
return nil
})
if errors.IsNotFound(patchErr) {
errBundle = multierror.Append(errBundle, patchErr)
}
if err := errBundle.ErrorOrNil(); err != nil {
return ctrl.Result{}, fmt.Errorf("error reconciling %s: %v", p.Name, err)
}
}
if !result.IsZero() {
return result, err
}
}
if err := r.patchStatus(ctx, &zitadel, r.patcher(ctx, &zitadel)); err != nil && !errors.IsNotFound(err) {
return ctrl.Result{}, err
}
return ctrl.Result{RequeueAfter: 2 * time.Minute}, nil
}
func (r *ZitadelClusterReconciler) setSpecDefaults(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
return ctrl.Result{}, r.patch(ctx, zitadel, func(zit *zitadelv1alpha1.ZitadelCluster) {
zit.SetDefaults()
})
}
func (r *ZitadelClusterReconciler) setStatusDefaults(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
return ctrl.Result{}, r.patchStatus(ctx, zitadel, func(status *zitadelv1alpha1.ZitadelClusterStatus) error {
status.FillWithDefaults(zitadel)
return nil
})
}
func (r *ZitadelClusterReconciler) reconcileMasterKeySecret(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
secretName := masterkey.MasterKeyName(zitadel)
key := types.NamespacedName{
Name: secretName,
Namespace: zitadel.Namespace,
}
_, err := r.SecretReconciler.ReconcileRandomPassword(ctx, key, masterkey.Key, zitadel)
if err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
func (r *ZitadelClusterReconciler) reconcileSystemAPIUser(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
secretName := systemapiaccount.SystemAPIAccountName(zitadel)
key := types.NamespacedName{
Name: secretName,
Namespace: zitadel.Namespace,
}
_, err := r.SecretReconciler.ReconcileRandomPrivateRSA(ctx, key, systemapiaccount.Key, zitadel)
if err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
func (r *ZitadelClusterReconciler) reconcileConfig(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
crdb, err := r.RefResolver.CrdbClusterRef(ctx, &zitadel.Spec.CrdbClusterRef, zitadel.Namespace)
if err != nil {
return ctrl.Result{}, err
}
configName := configuration.ConfigurationName(zitadel)
key := types.NamespacedName{
Name: configName,
Namespace: zitadel.Namespace,
}
privateKeyData, err := r.RefResolver.SecretKeyRef(ctx, corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: systemapiaccount.SystemAPIAccountName(zitadel)}, Key: systemapiaccount.Key}, zitadel.Namespace)
if err != nil {
return ctrl.Result{}, err
}
pemBlock, _ := pem.Decode([]byte(privateKeyData))
if pemBlock == nil {
return ctrl.Result{}, fmt.Errorf("failed to decode PEM block")
}
privateKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
publicKeyBytes, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
if err != nil {
return ctrl.Result{}, err
}
publicKeyPem := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: publicKeyBytes,
},
)
base64key := base64.StdEncoding.EncodeToString(publicKeyPem)
err = r.ConfigMapReconciler.ReconcileZitadelConfiguration(ctx, key, zitadel, crdb, base64key)
if err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
func (r *ZitadelClusterReconciler) reconcileInitJob(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
key := client.ObjectKeyFromObject(zitadel)
key.Name = "init-job-" + key.Name
desiredInitJob, err := r.Builder.BuildInitJob(zitadel, key)
if err != nil {
return ctrl.Result{}, fmt.Errorf("error building InitJob: %v", err)
}
var existingJob batchv1.Job
if err := r.Get(ctx, key, &existingJob); err != nil {
if !errors.IsNotFound(err) {
return ctrl.Result{}, fmt.Errorf("error getting InitJob: %v", err)
}
if err := r.Create(ctx, desiredInitJob); err != nil {
return ctrl.Result{}, fmt.Errorf("error creating InitJob: %v", err)
}
return ctrl.Result{}, nil
}
patch := client.MergeFrom(existingJob.DeepCopy())
existingJob.Spec.Template.Spec = desiredInitJob.Spec.Template.Spec
return ctrl.Result{}, r.Patch(ctx, &existingJob, patch)
}
func (r *ZitadelClusterReconciler) reconcileSetupJob(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
key := client.ObjectKeyFromObject(zitadel)
key.Name = "setup-job-" + key.Name
desiredSetupjob, err := r.Builder.BuildSetupJob(zitadel, key)
if err != nil {
return ctrl.Result{}, fmt.Errorf("error building Setupjob: %v", err)
}
var existingJob batchv1.Job
if err := r.Get(ctx, key, &existingJob); err != nil {
if !errors.IsNotFound(err) {
return ctrl.Result{}, fmt.Errorf("error getting Setupjob: %v", err)
}
if err := r.Create(ctx, desiredSetupjob); err != nil {
return ctrl.Result{}, fmt.Errorf("error creating Setupjob: %v", err)
}
return ctrl.Result{}, nil
}
patch := client.MergeFrom(existingJob.DeepCopy())
existingJob.Spec.Template.Spec = desiredSetupjob.Spec.Template.Spec
return ctrl.Result{}, r.Patch(ctx, &existingJob, patch)
}
func (r *ZitadelClusterReconciler) reconcileDeployment(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
// TODO: Reload on config changed
key := client.ObjectKeyFromObject(zitadel)
desiredSts, err := r.Builder.BuildDeployment(zitadel, key)
if err != nil {
return ctrl.Result{}, fmt.Errorf("error building Deployment: %v", err)
}
var existingDep appsv1.Deployment
if err := r.Get(ctx, key, &existingDep); err != nil {
if !errors.IsNotFound(err) {
return ctrl.Result{}, fmt.Errorf("error getting Deployment: %v", err)
}
if err := r.Create(ctx, desiredSts); err != nil {
return ctrl.Result{}, fmt.Errorf("error creating Deployment: %v", err)
}
return ctrl.Result{}, nil
}
patch := client.MergeFrom(existingDep.DeepCopy())
existingDep.Spec.Template = desiredSts.Spec.Template
existingDep.Spec.Replicas = desiredSts.Spec.Replicas
return ctrl.Result{}, r.Patch(ctx, &existingDep, patch)
}
func (r *ZitadelClusterReconciler) reconcileService(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
return ctrl.Result{}, r.reconcileDefaultService(ctx, zitadel)
}
func (r *ZitadelClusterReconciler) reconcileDefaultService(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) error {
key := client.ObjectKeyFromObject(zitadel)
opts := builder.ServiceOpts{
Ports: []corev1.ServicePort{
{
Name: deployment.ZitadelName,
Port: deployment.ZitadelPort,
},
},
}
desiredSvc, err := r.Builder.BuildService(zitadel, key, opts)
if err != nil {
return fmt.Errorf("error building Service: %v", err)
}
return r.ServiceReconciler.Reconcile(ctx, desiredSvc)
}
func (r *ZitadelClusterReconciler) reconcileDefaultInstance(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
// First create systemapi to get, delete and create instances
privateKeyData, err := r.RefResolver.SecretKeyRef(ctx, corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: systemapiaccount.SystemAPIAccountName(zitadel)}, Key: systemapiaccount.Key}, zitadel.Namespace)
if err != nil {
return ctrl.Result{}, err
}
ztdClient, err := system.NewClient(GetIssuer(zitadel), GetAPI(zitadel), system.JWTProfileFromKey([]byte(privateKeyData), masterkey.OwnerName), system.WithInsecure())
if err != nil {
return ctrl.Result{}, fmt.Errorf("Error creating sytem client: %v", err)
}
defer ztdClient.Connection.Close()
// Delete all Instances that isn't the default
{
resp, err := ztdClient.ListInstances(ctx, &pb.ListInstancesRequest{})
if err != nil {
return ctrl.Result{}, fmt.Errorf("Error listing instances: %v", err)
}
for _, instance := range resp.Result {
if instance.Id != zitadel.Status.DefaultInstanceId || instance.Id == "" {
fmt.Println("DELETING INSTANCE")
_, err := ztdClient.RemoveInstance(ctx, &pb.RemoveInstanceRequest{InstanceId: instance.Id})
if err != nil {
return ctrl.Result{}, err
}
}
}
}
// Check if instance already exists
_, err = ztdClient.GetInstance(ctx, &pb.GetInstanceRequest{InstanceId: zitadel.Status.DefaultInstanceId})
if err != nil {
if strings.Contains(err.Error(), "Instance not found") {
// if Instance doesn't exist, then create and assign secrets
resp, err := ztdClient.CreateInstance(ctx, &pb.CreateInstanceRequest{
InstanceName: zitadel.Spec.FirstOrgName,
FirstOrgName: zitadel.Spec.FirstOrgName,
CustomDomain: zitadel.Spec.Host,
Owner: &pb.CreateInstanceRequest_Machine_{Machine: &pb.CreateInstanceRequest_Machine{
Name: "k8s-operator",
UserName: "k8s-operator",
MachineKey: &pb.CreateInstanceRequest_MachineKey{
Type: authn.KeyType_KEY_TYPE_JSON},
PersonalAccessToken: &pb.CreateInstanceRequest_PersonalAccessToken{}},
}})
if err != nil {
return ctrl.Result{}, fmt.Errorf("Error creating default instance: %v", err)
}
var machineKeyData zitadelClient.MachineKey
if err := json.Unmarshal(resp.MachineKey, &machineKeyData); err != nil {
return ctrl.Result{}, err
}
secretName := machinekey.MachineKeySecretName(zitadel)
key := types.NamespacedName{
Name: secretName,
Namespace: zitadel.Namespace,
}
secretData := make(map[string][]byte)
jsonData, err := json.Marshal(machineKeyData)
if err != nil {
return ctrl.Result{}, err
}
secretData[machinekey.Key] = jsonData
secret, err := r.Builder.BuildSecret(builder.SecretOpts{Zitadel: zitadel, Key: key, Data: secretData}, zitadel)
if err != nil {
return ctrl.Result{}, fmt.Errorf("error building machinekey Secret: %v", err)
}
if err := r.Create(ctx, secret); err != nil {
return ctrl.Result{}, fmt.Errorf("error creating machinekey Secret: %v", err)
}
patch := client.MergeFrom(zitadel.DeepCopy())
zitadel.Status.DefaultInstanceId = resp.InstanceId
return ctrl.Result{}, r.Status().Patch(ctx, zitadel, patch)
} else {
return ctrl.Result{}, fmt.Errorf("Error getting instance with id: %s: %v", zitadel.Status.DefaultInstanceId, err)
}
}
return ctrl.Result{}, nil
}
func (r *ZitadelClusterReconciler) reconcileSMTPConfig(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
adminClient, err := zitadelClient.NewAdminClient(ctx, zitadel, *r.RefResolver)
if err != nil {
return ctrl.Result{}, err
}
resp, err := adminClient.GetSMTPConfig(ctx, &adm.GetSMTPConfigRequest{})
if err != nil {
if !strings.Contains(err.Error(), "SMTP configuration not found") {
return ctrl.Result{}, fmt.Errorf("Error getting SMTP config: %v", err)
}
}
if resp != nil {
adminRequest := &adm.UpdateSMTPConfigRequest{
SenderAddress: zitadel.Spec.SMTPConfig.SenderAddress,
SenderName: zitadel.Spec.SMTPConfig.SenderName,
Tls: zitadel.Spec.SMTPConfig.TLS,
Host: zitadel.Spec.SMTPConfig.Host,
}
if zitadel.Spec.SMTPConfig.User != nil && zitadel.Spec.SMTPConfig.Password != nil {
adminRequest.User = *zitadel.Spec.SMTPConfig.User
}
if zitadel.Spec.SMTPConfig.ReplyToAddress != nil {
adminRequest.ReplyToAddress = *zitadel.Spec.SMTPConfig.ReplyToAddress
}
passwordSecret, err := r.RefResolver.SecretKeyRef(ctx, zitadel.Spec.SMTPConfig.Password.SecretKeyRef, zitadel.Namespace)
if err != nil {
return ctrl.Result{}, err
}
if _, err = adminClient.UpdateSMTPConfig(ctx, adminRequest); err != nil {
if !strings.Contains(err.Error(), "No changes") {
return ctrl.Result{}, fmt.Errorf("Could not update SMTP config: %v", err)
}
}
if zitadel.Spec.SMTPConfig.Password != nil {
if _, err = adminClient.UpdateSMTPConfigPassword(ctx, &adm.UpdateSMTPConfigPasswordRequest{
Password: passwordSecret,
}); err != nil {
if !strings.Contains(err.Error(), "No changes") {
return ctrl.Result{}, fmt.Errorf("Could not update SMTP config: %v", err)
}
}
}
} else {
adminRequest := &adm.AddSMTPConfigRequest{
SenderAddress: zitadel.Spec.SMTPConfig.SenderAddress,
SenderName: zitadel.Spec.SMTPConfig.SenderName,
Tls: zitadel.Spec.SMTPConfig.TLS,
Host: zitadel.Spec.SMTPConfig.Host,
}
if zitadel.Spec.SMTPConfig.User != nil && zitadel.Spec.SMTPConfig.Password != nil {
passwordSecret, err := r.RefResolver.SecretKeyRef(ctx, zitadel.Spec.SMTPConfig.Password.SecretKeyRef, zitadel.Namespace)
if err != nil {
return ctrl.Result{}, err
}
adminRequest.Password = passwordSecret
adminRequest.User = *zitadel.Spec.SMTPConfig.User
}
if zitadel.Spec.SMTPConfig.ReplyToAddress != nil {
adminRequest.ReplyToAddress = *zitadel.Spec.SMTPConfig.ReplyToAddress
}
if _, err = adminClient.AddSMTPConfig(ctx, adminRequest); err != nil {
return ctrl.Result{}, fmt.Errorf("Could not add SMTP config: %v", err)
}
}
return ctrl.Result{}, nil
}
func (r *ZitadelClusterReconciler) reconcileDomainPolicy(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
adminClient, err := zitadelClient.NewAdminClient(ctx, zitadel, *r.RefResolver)
if err != nil {
return ctrl.Result{}, err
}
if _, err = adminClient.UpdateDomainPolicy(ctx, &adm.UpdateDomainPolicyRequest{
UserLoginMustBeDomain: zitadel.Spec.DomainSettings.UserLoginMustBeDomain,
ValidateOrgDomains: zitadel.Spec.DomainSettings.ValidateOrgDomains,
SmtpSenderAddressMatchesInstanceDomain: zitadel.Spec.DomainSettings.SMTPSenderAddressMatchesInstanceDomain,
}); err != nil {
if !strings.Contains(err.Error(), "not been changed") {
return ctrl.Result{}, fmt.Errorf("Could not update domain policy config: %v", err)
}
}
return ctrl.Result{}, nil
}
func GetIssuer(zitadel *zitadelv1alpha1.ZitadelCluster) string {
scheme := "http"
if zitadel.Spec.ExternalSecure {
scheme = "https"
}
if zitadel.Spec.ExternalPort == 443 {
return fmt.Sprintf("%s://%s", scheme, zitadel.Spec.Host)
}
return fmt.Sprintf("%s://%s:%d", scheme, zitadel.Spec.Host, zitadel.Spec.ExternalPort)
}
func GetAPI(zitadel *zitadelv1alpha1.ZitadelCluster) string {
return fmt.Sprintf("%s:%d", deployment.ServiceFQDN(zitadel.ObjectMeta), deployment.ZitadelPort)
}
func (r *ZitadelClusterReconciler) patchStatus(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster,
patcher patcher) error {
patch := client.MergeFrom(zitadel.DeepCopy())
if err := patcher(&zitadel.Status); err != nil {
return err
}
return r.Status().Patch(ctx, zitadel, patch)
}
func (r *ZitadelClusterReconciler) patcher(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) patcher {
return func(s *zitadelv1alpha1.ZitadelClusterStatus) error {
var sts appsv1.Deployment
if err := r.Get(ctx, client.ObjectKeyFromObject(zitadel), &sts); err != nil {
return err
}
zitadel.Status.Replicas = sts.Status.ReadyReplicas
condition.SetReadyWithDeployment(&zitadel.Status, &sts, zitadel.Status.DefaultInstanceId)
return nil
}
}
func (r *ZitadelClusterReconciler) patch(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster,
patcher func(*zitadelv1alpha1.ZitadelCluster)) error {
patch := client.MergeFrom(zitadel.DeepCopy())
patcher(zitadel)
return r.Patch(ctx, zitadel, patch)
}
// SetupWithManager sets up the controller with the Manager.
func (r *ZitadelClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&zitadelv1alpha1.ZitadelCluster{}).
Owns(&appsv1.Deployment{}).
Owns(&corev1.Service{}).
Owns(&corev1.ConfigMap{}).
Owns(&corev1.Secret{}).
Owns(&zitadelv1alpha1.Organization{}).
WithOptions(controller.Options{RateLimiter: workqueue.NewItemExponentialFailureRateLimiter(time.Millisecond*500, time.Minute*3)}).
Complete(r)
}