in pkg/controller/tf/controller.go [259:412]
func (r *Reconciler) sync(ctx context.Context, krmResource *krmtotf.Resource) (requeue bool, err error) {
// isolate any panics to only this function
defer execution.RecoverWithInternalError(&err)
if !krmResource.GetDeletionTimestamp().IsZero() {
// Deleting
r.logger.Info("finalizing resource deletion", "resource", k8s.GetNamespacedName(krmResource))
if !k8s.HasFinalizer(krmResource, k8s.ControllerFinalizerName) {
r.logger.Info("no controller finalizer is present; no finalization necessary",
"resource", k8s.GetNamespacedName(krmResource))
return false, nil
}
if k8s.HasFinalizer(krmResource, k8s.DeletionDefenderFinalizerName) {
r.logger.Info("deletion defender has not yet been finalized; requeuing", "resource", k8s.GetNamespacedName(krmResource))
return true, nil
}
if err := r.HandleDeleting(ctx, &krmResource.Resource); err != nil {
return false, err
}
if k8s.HasAbandonAnnotation(krmResource) {
r.logger.Info("deletion policy set to abandon; abandoning underlying resource", "resource", k8s.GetNamespacedName(krmResource))
return false, r.handleDeleted(ctx, krmResource)
}
if krmtotf.ShouldResolveParentForDelete(krmResource) {
orphaned, parent, err := r.isOrphaned(krmResource)
// Handle orphaned resources
if err != nil {
return false, err
}
if orphaned {
r.logger.Info("resource has been orphaned; no API call necessary", "resource", k8s.GetNamespacedName(krmResource))
return false, r.handleDeleted(ctx, krmResource)
}
if parent != nil && !k8s.IsResourceReady(parent) {
if krmtotf.ShouldCheckParentReadyForDelete(krmResource, parent) {
// If this resource has a parent and is not orphaned, ensure its parent
// is ready before attempting deletion.
// Requeue resource for reconciliation with exponential backoff applied
return true, r.HandleUnresolvableDeps(ctx, &krmResource.Resource, k8s.NewReferenceNotReadyErrorForResource(parent))
}
}
}
liveState, err := krmtotf.FetchLiveStateForDelete(ctx, krmResource, r.provider, r, r.smLoader)
if err != nil {
return false, r.HandleDeleteFailed(ctx, &krmResource.Resource, fmt.Errorf("error fetching live state: %w", err))
}
if liveState.Empty() {
r.logger.Info("underlying resource does not exist; no API call necessary", "resource", k8s.GetNamespacedName(krmResource))
return false, r.handleDeleted(ctx, krmResource)
}
if err := r.obtainResourceLeaseIfNecessary(ctx, krmResource, liveState); err != nil {
return false, err
}
r.logger.Info("deleting underlying resource", "resource", k8s.GetNamespacedName(krmResource))
if _, err := krmResource.TFResource.Apply(ctx, liveState, &terraform.InstanceDiff{Destroy: true}, r.provider.Meta()); err != nil {
return false, r.HandleDeleteFailed(ctx, &krmResource.Resource, fmt.Errorf("error deleting resource: %v", err))
}
return false, r.handleDeleted(ctx, krmResource)
}
liveState, err := krmtotf.FetchLiveStateForCreateAndUpdate(ctx, krmResource, r.provider, r, r.smLoader)
if err != nil {
if unwrappedErr, ok := lifecyclehandler.CausedByUnresolvableDeps(err); ok {
r.logger.Info(unwrappedErr.Error(), "resource", k8s.GetNamespacedName(krmResource))
return r.handleUnresolvableDeps(ctx, &krmResource.Resource, unwrappedErr)
}
return false, r.HandleUpdateFailed(ctx, &krmResource.Resource, fmt.Errorf("error fetching live state: %w", err))
}
if err := r.obtainResourceLeaseIfNecessary(ctx, krmResource, liveState); err != nil {
return false, err
}
ok, err := r.hasServerGeneratedIDAndHadBeenCreatedOnceAlready(krmResource)
if err != nil {
return false, r.HandleUpdateFailed(ctx, &krmResource.Resource, err)
}
if ok && liveState.Empty() {
// GCP resource with server-generated ID had been created once already,
// but no longer exists. Don't "recreate" the resource, since
// "recreating" resources with server-generated IDs technically creates
// a brand new resource instead.
return false, r.HandleUpdateFailed(ctx, &krmResource.Resource,
fmt.Errorf("underlying resource no longer exists and can't be recreated without creating a brand new resource"))
}
config, secretVersions, err := krmtotf.KRMResourceToTFResourceConfigFull(
krmResource, r, r.smLoader, liveState, r.schemaRef.JSONSchema, true,
)
if err != nil {
if unwrappedErr, ok := lifecyclehandler.CausedByUnresolvableDeps(err); ok {
r.logger.Info(unwrappedErr.Error(), "resource", k8s.GetNamespacedName(krmResource))
return r.handleUnresolvableDeps(ctx, &krmResource.Resource, unwrappedErr)
}
return false, r.HandleUpdateFailed(ctx, &krmResource.Resource, fmt.Errorf("error expanding resource configuration for kind %s: %w", krmResource.Kind, err))
}
// Apply last-minute apply overrides
if err := resourceoverrides.Handler.PreTerraformApply(ctx, krmResource.GroupVersionKind(), &operations.PreTerraformApply{KRMResource: krmResource, TerraformConfig: config, LiveState: liveState}); err != nil {
return false, r.HandleUpdateFailed(ctx, &krmResource.Resource, fmt.Errorf("error applying pre-apply transformation to resource: %w", err))
}
diff, err := krmResource.TFResource.Diff(ctx, liveState, config, r.provider.Meta())
if err != nil {
return false, r.HandleUpdateFailed(ctx, &krmResource.Resource, fmt.Errorf("error calculating diff: %w", err))
}
if !liveState.Empty() && diff.RequiresNew() {
return false, r.HandleUpdateFailed(ctx, &krmResource.Resource,
k8s.NewImmutableFieldsMutationError(tfresource.ImmutableFieldsFromDiff(diff)))
}
if err := r.EnsureFinalizers(ctx, krmResource.Original, &krmResource.Resource, k8s.ControllerFinalizerName, k8s.DeletionDefenderFinalizerName); err != nil {
return false, err
}
if diff.Empty() {
r.logger.Info("underlying resource already up to date", "resource", k8s.GetNamespacedName(krmResource))
return false, r.handleUpToDate(ctx, krmResource, liveState, secretVersions)
}
// Report diff to structured-reporting subsystem
{
report := &structuredreporting.Diff{}
u, err := krmResource.MarshalAsUnstructured()
if err != nil {
log := log.FromContext(ctx)
log.Error(err, "error reporting diff")
}
report.Object = u
if diff != nil {
for k, attr := range diff.Attributes {
report.Fields = append(report.Fields, structuredreporting.DiffField{
ID: k,
Old: attr.Old,
New: attr.New,
})
}
}
report.IsNewObject = liveState.Empty()
structuredreporting.ReportDiff(ctx, report)
}
r.logger.Info("creating/updating underlying resource", "resource", k8s.GetNamespacedName(krmResource))
if err := r.HandleUpdating(ctx, &krmResource.Resource); err != nil {
return false, err
}
// If creating a new resource, turn off RequiresNew in the diff. This is
// done to prevent TF from clearing providerMeta (which contains blueprint
// attribution information) during Apply() (b/193567082#comment15).
if liveState.Empty() {
for _, d := range diff.Attributes {
d.RequiresNew = false
}
}
newState, diagnostics := krmResource.TFResource.Apply(ctx, liveState, diff, r.provider.Meta())
if err := krmtotf.NewErrorFromDiagnostics(diagnostics); err != nil {
r.logger.Error(err, "error applying desired state", "resource", krmResource.GetNamespacedName())
return false, r.HandleUpdateFailed(ctx, &krmResource.Resource, fmt.Errorf("error applying desired state: %w", err))
}
return false, r.handleUpToDate(ctx, krmResource, newState, secretVersions)
}