|
|
|
|
@ -321,20 +321,24 @@ func (c *Client) makeCreateApplyFunc(serverSideApply, forceConflicts, dryRun boo
|
|
|
|
|
slog.String("fieldValidationDirective", string(fieldValidationDirective)))
|
|
|
|
|
|
|
|
|
|
return func(target *resource.Info) error {
|
|
|
|
|
err := patchResourceServerSide(target, dryRun, forceConflicts, fieldValidationDirective)
|
|
|
|
|
|
|
|
|
|
logger := c.Logger().With(
|
|
|
|
|
slog.String("namespace", target.Namespace),
|
|
|
|
|
slog.String("name", target.Name),
|
|
|
|
|
slog.String("gvk", target.Mapping.GroupVersionKind.String()))
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Debug("Error creating resource via patch", slog.Any("error", err))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.Debug("Created resource via patch")
|
|
|
|
|
return retry.OnError(
|
|
|
|
|
retry.DefaultRetry,
|
|
|
|
|
isServerSideRetryable,
|
|
|
|
|
func() error {
|
|
|
|
|
err := patchResourceServerSide(target, dryRun, forceConflicts, fieldValidationDirective)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Debug("Error creating resource via patch", slog.Any("error", err))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
logger.Debug("Created resource via patch")
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -952,6 +956,32 @@ func isIncompatibleServerError(err error) bool {
|
|
|
|
|
return err.(*apierrors.StatusError).Status().Code == http.StatusUnsupportedMediaType
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// isServerSideRetryable checks if an error encountered during server-side apply
|
|
|
|
|
// should be retried. Currently, only ResourceQuota conflicts are considered retryable.
|
|
|
|
|
func isServerSideRetryable(err error) bool {
|
|
|
|
|
return isResourceQuotaConflict(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// isResourceQuotaConflict checks if the error is a conflict error specifically caused by
|
|
|
|
|
// a ResourceQuota. This is used to determine if a retry should be attempted,
|
|
|
|
|
// since quota conflicts are typically transient and can be resolved by retrying.
|
|
|
|
|
func isResourceQuotaConflict(err error) bool {
|
|
|
|
|
if !apierrors.IsConflict(err) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check the error message for the specific ResourceQuota conflict pattern.
|
|
|
|
|
// The error message from the ResourceQuota admission controller contains:
|
|
|
|
|
// "Operation cannot be fulfilled on resourcequotas" and "the object has been modified"
|
|
|
|
|
errMsg := err.Error()
|
|
|
|
|
if strings.Contains(errMsg, "Operation cannot be fulfilled on resourcequotas") &&
|
|
|
|
|
strings.Contains(errMsg, "the object has been modified") {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getManagedFieldsManager returns the manager string. If one was set it will be returned.
|
|
|
|
|
// Otherwise, one is calculated based on the name of the binary.
|
|
|
|
|
func getManagedFieldsManager() string {
|
|
|
|
|
|