func()

in internal/controller/appconfigurationprovider_controller.go [86:296]


func (reconciler *AzureAppConfigurationProviderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	provider := &acpv1.AzureAppConfigurationProvider{}
	err := reconciler.Get(ctx, req.NamespacedName, provider)
	//Get object, if not exists, exit reconcile
	if err != nil && apierrors.IsNotFound(err) {
		delete(reconciler.ProvidersReconcileState, req.NamespacedName)
		return reconcile.Result{}, nil
	} else if err != nil {
		klog.ErrorS(err, "Fail to get AzureAppConfigurationProvider object.")
		return reconcile.Result{}, err
	}

	/* Patch the object status when finish processing. */
	var patch client.Patch = client.MergeFrom(provider.DeepCopy())
	defer func() {
		retry := RetryAttempt
		patchSuccess := false
		for retry > 0 {
			err = reconciler.Status().Patch(ctx, provider, patch)
			if err != nil {
				retry--
			} else {
				patchSuccess = true
				break
			}
		}
		if !patchSuccess {
			klog.ErrorS(err, "Fail to patch the status of AzureAppConfigurationProvider.")
		}
	}()

	/* Status initialization and resource object verification. */
	if provider.Status.Phase == "" {
		provider.Status.Phase = acpv1.PhasePending
	}

	if provider.Status.Phase == acpv1.PhaseRunning {
		klog.V(3).Infof("The reconcile for AzureAppConfigurationProvider '%s' is running, just exit.", provider.Name)
		return reconcile.Result{}, nil
	}

	provider.Status = newProviderStatus(acpv1.PhaseRunning, acpv1.SyncRunningMessage, provider.Status.LastSyncTime, provider.Status.RefreshStatus)
	klog.V(3).Infof("Start reconcile AzureAppConfigurationProvider %q in %q namespace ", provider.Name, provider.Namespace)

	err = verifyObject(provider.Spec)
	if err != nil {
		reconciler.logAndSetFailStatus(provider, err)
		return reconcile.Result{Requeue: false}, nil
	}

	existingConfigMap := corev1.ConfigMap{}
	isExisting := false
	_, err = reconciler.verifyTargetObjectExistence(ctx, provider, &existingConfigMap)
	if err != nil {
		reconciler.logAndSetFailStatus(provider, err)
		return reconcile.Result{Requeue: true, RequeueAfter: RequeueReconcileAfter}, nil
	}

	existingSecrets := make(map[string]corev1.Secret)
	var existingSecret corev1.Secret
	if provider.Spec.Secret != nil {
		existingSecret = corev1.Secret{
			ObjectMeta: metav1.ObjectMeta{
				Name: provider.Spec.Secret.Target.SecretName,
			},
		}
		isExisting, err = reconciler.verifyTargetObjectExistence(ctx, provider, &existingSecret)
		if err != nil {
			reconciler.logAndSetFailStatus(provider, err)
			return reconcile.Result{Requeue: true, RequeueAfter: RequeueReconcileAfter}, nil
		}
		if isExisting {
			existingSecrets[provider.Spec.Secret.Target.SecretName] = existingSecret
		}
	}

	if reconciler.ProvidersReconcileState[req.NamespacedName] != nil {
		for name := range reconciler.ProvidersReconcileState[req.NamespacedName].ExistingK8sSecrets {
			if _, ok := existingSecrets[name]; !ok {
				existingSecret = corev1.Secret{
					ObjectMeta: metav1.ObjectMeta{
						Name: name,
					},
				}
				isExisting, err = reconciler.verifyTargetObjectExistence(ctx, provider, &existingSecret)
				if err != nil {
					reconciler.logAndSetFailStatus(provider, err)
					return reconcile.Result{Requeue: true, RequeueAfter: RequeueReconcileAfter}, nil
				}
				if isExisting {
					existingSecrets[name] = existingSecret
				}
			}
		}
	} else {
		// Initialize the ReconcileState for the provider
		reconciler.ProvidersReconcileState[req.NamespacedName] = &ReconciliationState{
			Generation:               -1,
			ConfigMapResourceVersion: nil,
			Annotations:              make(map[string]string),
			SentinelETags:            make(map[acpv1.Sentinel]*azcore.ETag),
			KeyValueETags:            make(map[acpv1.Selector][]*azcore.ETag),
			FeatureFlagETags:         make(map[acpv1.Selector][]*azcore.ETag),
			ExistingK8sSecrets:       make(map[string]*loader.TargetK8sSecretMetadata),
			ClientManager:            nil,
		}
	}

	// Reset the resource version if the configmap or secret was unexpected deleted
	if existingConfigMap.Name == "" {
		reconciler.ProvidersReconcileState[req.NamespacedName].ConfigMapResourceVersion = nil
	}

	if provider.Spec.Secret == nil {
		reconciler.ProvidersReconcileState[req.NamespacedName].ExistingK8sSecrets = make(map[string]*loader.TargetK8sSecretMetadata)
	} else {
		for name := range reconciler.ProvidersReconcileState[req.NamespacedName].ExistingK8sSecrets {
			if _, ok := existingSecrets[name]; !ok {
				reconciler.ProvidersReconcileState[req.NamespacedName].ExistingK8sSecrets[name].SecretResourceVersion = ""
			}
		}
	}

	if reconciler.ProvidersReconcileState[req.NamespacedName].ClientManager == nil ||
		reconciler.ProvidersReconcileState[req.NamespacedName].Generation != provider.Generation {
		clientManager, err := loader.NewConfigurationClientManager(ctx, *provider)
		if err != nil {
			reconciler.logAndSetFailStatus(provider, err)
			return reconcile.Result{Requeue: true, RequeueAfter: RequeueReconcileAfter}, nil
		}
		reconciler.ProvidersReconcileState[req.NamespacedName].ClientManager = clientManager
	}

	/* Create ConfigurationSettingLoader to get the key-value settings from Azure AppConfiguration. */
	clientManager := reconciler.ProvidersReconcileState[req.NamespacedName].ClientManager
	configLoader, err := loader.NewConfigurationSettingLoader(*provider, clientManager, nil)
	if err != nil {
		reconciler.logAndSetFailStatus(provider, err)
		return reconcile.Result{Requeue: true, RequeueAfter: RequeueReconcileAfter}, nil
	}
	var retriever loader.ConfigurationSettingsRetriever
	if reconciler.Retriever == nil {
		retriever = configLoader
	} else {
		retriever = reconciler.Retriever
	}

	ctx = context.WithValue(ctx, loader.RequestTracingKey, loader.RequestTracing{
		IsStartUp: reconciler.ProvidersReconcileState[req.NamespacedName].ConfigMapResourceVersion == nil,
	})

	// Initialize the processor setting in this reconcile
	processor := &AppConfigurationProviderProcessor{
		Context:                 ctx,
		Provider:                provider,
		Retriever:               retriever,
		CurrentTime:             metav1.Now(),
		ReconciliationState:     reconciler.ProvidersReconcileState[req.NamespacedName],
		Settings:                &loader.TargetKeyValueSettings{},
		RefreshOptions:          NewRefreshOptions(),
		SecretReferenceResolver: nil,
	}

	if err := processor.PopulateSettings(&existingConfigMap, existingSecrets); err != nil {
		return reconciler.requeueWhenGetSettingsFailed(provider, err)
	}

	/* Create ConfigMap from key-value settings */
	if processor.RefreshOptions.ConfigMapSettingPopulated {
		result, err := reconciler.createOrUpdateConfigMap(ctx, &existingConfigMap, provider, processor.Settings)
		if err != nil {
			return result, nil
		}
	}

	/* Create secret when there are secret settings */
	if processor.RefreshOptions.SecretSettingPopulated {
		// Verify the existence of the secret which is not owned by the current provider
		for name := range processor.Settings.SecretSettings {
			if _, ok := existingSecrets[name]; !ok {
				_, err := reconciler.verifyTargetObjectExistence(ctx, provider, &corev1.Secret{
					ObjectMeta: metav1.ObjectMeta{
						Name: name,
					},
				})

				if err != nil {
					reconciler.logAndSetFailStatus(provider, err)
					return reconcile.Result{Requeue: true, RequeueAfter: RequeueReconcileAfter}, nil
				}
			}
		}

		result, err := reconciler.createOrUpdateSecrets(ctx, provider, processor, existingSecrets)
		if err != nil {
			return result, nil
		}
	}

	// Expel the secrets which are no longer selected by the provider.
	if provider.Spec.Secret == nil || processor.RefreshOptions.SecretSettingPopulated {
		result, err := reconciler.expelRemovedSecrets(ctx, provider, existingSecrets, processor.Settings.K8sSecrets)
		if err != nil {
			return result, nil
		}
	}

	/* Finish the reconcile */
	provider.Status = newProviderStatus(acpv1.PhaseComplete, acpv1.SyncCompleteMessage, metav1.Now(), provider.Status.RefreshStatus)
	return processor.Finish()
}