in pkg/controller/keyvault/gateway_secret_provider_class.go [53:196]
func (g *GatewaySecretProviderClassReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl.Result, retErr error) {
// set up metrics given result/error
defer func() {
metrics.HandleControllerReconcileMetrics(gatewaySecretProviderControllerName, res, retErr)
}()
// set up logger
logger, err := logr.FromContext(ctx)
if err != nil {
return ctrl.Result{}, fmt.Errorf("creating logger: %w", err)
}
logger = gatewaySecretProviderControllerName.AddToLogger(logger).WithValues("name", req.Name, "namespace", req.Namespace)
// retrieve gateway resource from request + log the get attempt, but ignore not found
gwObj := &gatewayv1.Gateway{}
err = g.client.Get(ctx, req.NamespacedName, gwObj)
if err != nil {
if client.IgnoreNotFound(err) != nil {
logger.Error(err, "failed to fetch Gateway")
return ctrl.Result{}, fmt.Errorf("fetching gateway: %w", err)
}
return ctrl.Result{}, nil
}
if !shouldReconcileGateway(gwObj) {
return ctrl.Result{}, nil
}
// check its TLS options - needs to have both cert uri and either serviceaccount name or clientid
for index, listener := range gwObj.Spec.Listeners {
spc := &secv1.SecretProviderClass{
TypeMeta: metav1.TypeMeta{
APIVersion: "secrets-store.csi.x-k8s.io/v1",
Kind: "SecretProviderClass",
},
ObjectMeta: metav1.ObjectMeta{
Name: generateGwListenerCertName(gwObj.Name, listener.Name),
Namespace: req.Namespace,
Labels: manifests.GetTopLevelLabels(),
OwnerReferences: []metav1.OwnerReference{{
APIVersion: gwObj.APIVersion,
Controller: util.ToPtr(true),
Kind: gwObj.Kind,
Name: gwObj.Name,
UID: gwObj.UID,
}},
},
}
logger = logger.WithValues("spc", spc.Name)
if listenerIsKvEnabled(listener) {
var clientId string
clientId, err = retrieveClientIdForListener(ctx, g.client, req.Namespace, listener.TLS.Options)
if err != nil {
var userErr util.UserError
if errors.As(err, &userErr) {
logger.Info(fmt.Sprintf("failed to fetch clientId for SPC for listener %s due to user error: %s, sending warning event", listener.Name, userErr.UserMessage))
g.events.Eventf(gwObj, corev1.EventTypeWarning, "InvalidInput", "invalid TLS configuration: %s", userErr.UserMessage)
return ctrl.Result{}, nil
}
logger.Error(err, fmt.Sprintf("failed to fetch clientId for listener %s: %s", listener.Name, err.Error()))
return ctrl.Result{}, fmt.Errorf("fetching clientId for listener: %w", err)
}
// otherwise it's active + valid - build SPC
certUri := string(listener.TLS.Options[certUriTLSOption])
logger.Info("building spc for listener and upserting")
spcConf := spcConfig{
ClientId: clientId,
TenantId: g.config.TenantID,
KeyvaultCertUri: certUri,
Name: generateGwListenerCertName(gwObj.Name, listener.Name),
}
err = buildSPC(spc, spcConf)
if err != nil {
var userErr util.UserError
if errors.As(err, &userErr) {
logger.Info("failed to build SecretProviderClass from user error: %s, sending warning event", userErr.UserMessage)
g.events.Eventf(gwObj, corev1.EventTypeWarning, "InvalidInput", "invalid TLS configuration: %s", userErr.UserMessage)
return ctrl.Result{}, nil
}
logger.Error(err, fmt.Sprintf("building SPC for listener %s: %s", listener.Name, err.Error()))
return ctrl.Result{}, fmt.Errorf("building spc: %w", err)
}
logger.Info(fmt.Sprintf("reconciling SecretProviderClass %s for listener %s", spc.Name, listener.Name))
if err = util.Upsert(ctx, g.client, spc); err != nil {
fullErr := fmt.Errorf("failed to reconcile SecretProviderClass %s: %w", req.Name, err)
logger.Error(err, fullErr.Error())
g.events.Event(gwObj, corev1.EventTypeWarning, "FailedUpdateOrCreateSPC", fullErr.Error())
return ctrl.Result{}, fullErr
}
logger.Info(fmt.Sprintf("preemptively attaching secret reference for listener %s", listener.Name))
newCertRef := gatewayv1.SecretObjectReference{
Namespace: to.Ptr(gatewayv1.Namespace(req.Namespace)),
Group: to.Ptr(gatewayv1.Group(corev1.GroupName)),
Kind: to.Ptr(gatewayv1.Kind("Secret")),
Name: gatewayv1.ObjectName(generateGwListenerCertName(gwObj.Name, listener.Name)),
}
gwObj.Spec.Listeners[index].TLS.CertificateRefs = []gatewayv1.SecretObjectReference{newCertRef}
continue
}
// we should delete the SPC if it exists
logger.Info(fmt.Sprintf("attempting to remove unused SPC %s", spc.Name))
deletionSpc := &secv1.SecretProviderClass{}
if err = g.client.Get(ctx, client.ObjectKeyFromObject(spc), deletionSpc); err != nil {
if client.IgnoreNotFound(err) != nil {
logger.Error(err, fmt.Sprintf("failed to fetch SPC for deletion %s", spc.Name))
return ctrl.Result{}, fmt.Errorf("fetching SPC for deletion: %w", err)
}
continue
}
if manifests.HasTopLevelLabels(deletionSpc.Labels) {
// return if we fail to delete, but otherwise, keep going
if err = g.client.Delete(ctx, deletionSpc); err != nil {
if client.IgnoreNotFound(err) != nil {
logger.Error(err, fmt.Sprintf("failed to delete SPC %s", spc.Name))
return ctrl.Result{}, fmt.Errorf("deleting SPC: %w", err)
}
continue
}
}
}
logger.Info("reconciling Gateway resource with new secret refs for each TLS-enabled listener")
if err = g.client.Update(ctx, gwObj); client.IgnoreNotFound(err) != nil {
if apierrors.IsConflict(err) {
logger.Info("Gateway resource was updated by another process, retrying")
return ctrl.Result{Requeue: true}, nil
}
fullErr := fmt.Errorf("failed to reconcile Gateway resource %s: %w", req.Name, err)
logger.Error(err, fullErr.Error())
g.events.Event(gwObj, corev1.EventTypeWarning, "FailedUpdateOrCreateGateway", fullErr.Error())
return ctrl.Result{}, fullErr
}
return ctrl.Result{}, nil
}