fix: update CR's status with retry.RetryOnConflict (#1015)

This commit is contained in:
Dylan
2021-02-06 23:08:22 -08:00
committed by GitHub
parent aabd1e399a
commit abe01d2add
9 changed files with 138 additions and 28 deletions
@@ -27,6 +27,8 @@ import (
"github.com/pkg/errors"
kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -77,13 +79,13 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
app.Status.Phase = v1alpha2.ApplicationRollingOut
app.Status.SetConditions(readyCondition("Rolling"))
// do not process apps still in rolling out
return ctrl.Result{RequeueAfter: RolloutReconcileWaitTime}, r.Status().Update(ctx, app)
return ctrl.Result{RequeueAfter: RolloutReconcileWaitTime}, r.UpdateStatus(ctx, app)
}
applog.Info("Start Rendering")
app.Status.Phase = v1alpha2.ApplicationRendering
handler := &appHandler{r.Client, app, applog}
handler := &appHandler{r, app, applog}
app.Status.Conditions = []v1alpha1.Condition{}
@@ -149,7 +151,7 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
})
}
app.Status.Components = refComps
return ctrl.Result{}, r.Status().Update(ctx, app)
return ctrl.Result{}, r.UpdateStatus(ctx, app)
}
// SetupWithManager install to manager
@@ -160,6 +162,18 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
Complete(r)
}
// UpdateStatus updates v1alpha2.Application's Status with retry.RetryOnConflict
func (r *Reconciler) UpdateStatus(ctx context.Context, app *v1alpha2.Application, opts ...client.UpdateOption) error {
status := app.DeepCopy().Status
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err = r.Get(ctx, types.NamespacedName{Namespace: app.Namespace, Name: app.Name}, app); err != nil {
return
}
app.Status = status
return r.Status().Update(ctx, app, opts...)
})
}
// Setup adds a controller that reconciles ApplicationDeployment.
func Setup(mgr ctrl.Manager, _ core.Args, _ logging.Logger) error {
dm, err := discoverymapper.New(mgr.GetConfig())
@@ -40,13 +40,13 @@ func readyCondition(tpy string) runtimev1alpha1.Condition {
}
type appHandler struct {
c client.Client
r *Reconciler
app *v1alpha2.Application
l logr.Logger
}
func (ret *appHandler) Err(err error) (ctrl.Result, error) {
nerr := ret.c.Status().Update(context.Background(), ret.app)
nerr := ret.r.UpdateStatus(context.Background(), ret.app)
if err == nil && nerr == nil {
return ctrl.Result{}, nil
}
@@ -92,7 +92,7 @@ func (ret *appHandler) statusAggregate(appfile *appfile.Appfile) ([]v1alpha2.App
}
}
workloadHealth, err := wl.EvalHealth(pCtx, ret.c, ret.app.Namespace)
workloadHealth, err := wl.EvalHealth(pCtx, ret.r, ret.app.Namespace)
if err != nil {
return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, check health error", appfile.Name, wl.Name)
}
@@ -101,7 +101,7 @@ func (ret *appHandler) statusAggregate(appfile *appfile.Appfile) ([]v1alpha2.App
status.Healthy = false
healthy = false
}
status.Message, err = wl.EvalStatus(pCtx, ret.c, ret.app.Namespace)
status.Message, err = wl.EvalStatus(pCtx, ret.r, ret.app.Namespace)
if err != nil {
return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, evaluate workload status message error", appfile.Name, wl.Name)
}
@@ -111,7 +111,7 @@ func (ret *appHandler) statusAggregate(appfile *appfile.Appfile) ([]v1alpha2.App
Type: trait.Name,
Healthy: true,
}
traitHealth, err := trait.EvalHealth(pCtx, ret.c, ret.app.Namespace)
traitHealth, err := trait.EvalHealth(pCtx, ret.r, ret.app.Namespace)
if err != nil {
return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, trait=%s, check health error", appfile.Name, wl.Name, trait.Name)
}
@@ -120,7 +120,7 @@ func (ret *appHandler) statusAggregate(appfile *appfile.Appfile) ([]v1alpha2.App
traitStatus.Healthy = false
healthy = false
}
traitStatus.Message, err = trait.EvalStatus(pCtx, ret.c, ret.app.Namespace)
traitStatus.Message, err = trait.EvalStatus(pCtx, ret.r, ret.app.Namespace)
if err != nil {
return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, trait=%s, evaluate status message error", appfile.Name, wl.Name, trait.Name)
}
@@ -167,12 +167,12 @@ func CreateOrUpdateAppConfig(ctx context.Context, client client.Client, appConfi
// Sync perform synchronization operations
func (ret *appHandler) Sync(ctx context.Context, ac *v1alpha2.ApplicationConfiguration, comps []*v1alpha2.Component) error {
for _, comp := range comps {
if err := CreateOrUpdateComponent(ctx, ret.c, comp.DeepCopy()); err != nil {
if err := CreateOrUpdateComponent(ctx, ret.r, comp.DeepCopy()); err != nil {
return err
}
}
if err := CreateOrUpdateAppConfig(ctx, ret.c, ac); err != nil {
if err := CreateOrUpdateAppConfig(ctx, ret.r, ac); err != nil {
return err
}
@@ -191,7 +191,7 @@ func (ret *appHandler) Sync(ctx context.Context, ac *v1alpha2.ApplicationConfigu
}
// Component not exits in current Application, should be deleted
var oldC = &v1alpha2.Component{ObjectMeta: metav1.ObjectMeta{Name: comp.Name, Namespace: ac.Namespace}}
if err := ret.c.Delete(ctx, oldC); err != nil {
if err := ret.r.Delete(ctx, oldC); err != nil {
return err
}
}
@@ -27,6 +27,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -245,7 +247,7 @@ func (r *OAMApplicationReconciler) Reconcile(req reconcile.Request) (result reco
"error", err, "requeue-after", result.RequeueAfter)
r.record.Event(ac, event.Warning(reasonCannotFinalizeWorkloads, err))
ac.SetConditions(v1alpha1.ReconcileError(errors.Wrap(err, errFinalizeWorkloads)))
return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, ac), errUpdateAppConfigStatus)
return reconcile.Result{}, errors.Wrap(r.UpdateStatus(ctx, ac), errUpdateAppConfigStatus)
}
return reconcile.Result{}, errors.Wrap(r.client.Update(ctx, ac), errUpdateAppConfigStatus)
}
@@ -260,12 +262,12 @@ func (r *OAMApplicationReconciler) Reconcile(req reconcile.Request) (result reco
r.record.Event(ac, event.Warning(reasonCannotExecutePosthooks, err))
ac.SetConditions(v1alpha1.ReconcileError(errors.Wrap(err, errExecutePosthooks)))
result = exeResult
returnErr = errors.Wrap(r.client.Status().Update(ctx, ac), errUpdateAppConfigStatus)
returnErr = errors.Wrap(r.UpdateStatus(ctx, ac), errUpdateAppConfigStatus)
return
}
r.record.Event(ac, event.Normal(reasonExecutePosthook, "Successfully executed a posthook", "posthook name", name))
}
returnErr = errors.Wrap(r.client.Status().Update(ctx, ac), errUpdateAppConfigStatus)
returnErr = errors.Wrap(r.UpdateStatus(ctx, ac), errUpdateAppConfigStatus)
// Make sure if error occurs, reconcile will not happen too frequency
if returnErr != nil && result.RequeueAfter < shortWait {
@@ -280,7 +282,7 @@ func (r *OAMApplicationReconciler) Reconcile(req reconcile.Request) (result reco
log.Debug("Failed to execute pre-hooks", "hook name", name, "error", err, "requeue-after", result.RequeueAfter)
r.record.Event(ac, event.Warning(reasonCannotExecutePrehooks, err))
ac.SetConditions(v1alpha1.ReconcileError(errors.Wrap(err, errExecutePrehooks)))
return result, errors.Wrap(r.client.Status().Update(ctx, ac), errUpdateAppConfigStatus)
return result, errors.Wrap(r.UpdateStatus(ctx, ac), errUpdateAppConfigStatus)
}
r.record.Event(ac, event.Normal(reasonExecutePrehook, "Successfully executed a prehook", "prehook name ", name))
}
@@ -292,7 +294,7 @@ func (r *OAMApplicationReconciler) Reconcile(req reconcile.Request) (result reco
log.Info("Cannot render components", "error", err, "requeue-after", time.Now().Add(shortWait))
r.record.Event(ac, event.Warning(reasonCannotRenderComponents, err))
ac.SetConditions(v1alpha1.ReconcileError(errors.Wrap(err, errRenderComponents)))
return errResult, errors.Wrap(r.client.Status().Update(ctx, ac), errUpdateAppConfigStatus)
return errResult, errors.Wrap(r.UpdateStatus(ctx, ac), errUpdateAppConfigStatus)
}
log.Debug("Successfully rendered components", "workloads", len(workloads))
r.record.Event(ac, event.Normal(reasonRenderComponents, "Successfully rendered components", "workloads", strconv.Itoa(len(workloads))))
@@ -302,7 +304,7 @@ func (r *OAMApplicationReconciler) Reconcile(req reconcile.Request) (result reco
log.Debug("Cannot apply components", "error", err, "requeue-after", time.Now().Add(shortWait))
r.record.Event(ac, event.Warning(reasonCannotApplyComponents, err))
ac.SetConditions(v1alpha1.ReconcileError(errors.Wrap(err, errApplyComponents)))
return errResult, errors.Wrap(r.client.Status().Update(ctx, ac), errUpdateAppConfigStatus)
return errResult, errors.Wrap(r.UpdateStatus(ctx, ac), errUpdateAppConfigStatus)
}
log.Debug("Successfully applied components", "workloads", len(workloads))
r.record.Event(ac, event.Normal(reasonApplyComponents, "Successfully applied components", "workloads", strconv.Itoa(len(workloads))))
@@ -322,7 +324,7 @@ func (r *OAMApplicationReconciler) Reconcile(req reconcile.Request) (result reco
log.Debug("Cannot garbage collect component", "error", err, "requeue-after", time.Now().Add(shortWait))
record.Event(ac, event.Warning(reasonCannotGGComponents, err))
ac.SetConditions(v1alpha1.ReconcileError(errors.Wrap(err, errGCComponent)))
return errResult, errors.Wrap(r.client.Status().Update(ctx, ac), errUpdateAppConfigStatus)
return errResult, errors.Wrap(r.UpdateStatus(ctx, ac), errUpdateAppConfigStatus)
}
log.Debug("Garbage collected resource")
record.Event(ac, event.Normal(reasonGGComponent, "Successfully garbage collected component"))
@@ -342,6 +344,18 @@ func (r *OAMApplicationReconciler) Reconcile(req reconcile.Request) (result reco
return reconcile.Result{RequeueAfter: waitTime}, nil
}
// UpdateStatus updates v1alpha2.ApplicationConfiguration's Status with retry.RetryOnConflict
func (r *OAMApplicationReconciler) UpdateStatus(ctx context.Context, ac *v1alpha2.ApplicationConfiguration, opts ...client.UpdateOption) error {
status := ac.DeepCopy().Status
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err = r.client.Get(ctx, types.NamespacedName{Namespace: ac.Namespace, Name: ac.Name}, ac); err != nil {
return
}
ac.Status = status
return r.client.Status().Update(ctx, ac, opts...)
})
}
func (r *OAMApplicationReconciler) updateStatus(ctx context.Context, ac, acPatch *v1alpha2.ApplicationConfiguration, workloads []Workload) {
ac.Status.Workloads = make([]v1alpha2.WorkloadStatus, len(workloads))
historyWorkloads := make([]v1alpha2.HistoryWorkload, 0)
@@ -11,6 +11,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
@@ -191,7 +192,7 @@ func (c *ComponentHandler) createControllerRevision(mt metav1.Object, obj runtim
return nil, false
}
err = c.Client.Status().Update(context.Background(), comp)
err = c.UpdateStatus(context.Background(), comp)
if err != nil {
c.Logger.Info(fmt.Sprintf("update component status latestRevision %s err %v", revisionName, err), "componentName", mt.GetName())
return nil, false
@@ -277,6 +278,18 @@ func (c *ComponentHandler) cleanupControllerRevision(curComp *v1alpha2.Component
return nil
}
// UpdateStatus updates v1alpha2.Component's Status with retry.RetryOnConflict
func (c *ComponentHandler) UpdateStatus(ctx context.Context, comp *v1alpha2.Component, opts ...client.UpdateOption) error {
status := comp.DeepCopy().Status
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err = c.Client.Get(ctx, types.NamespacedName{Namespace: comp.Namespace, Name: comp.Name}, comp); err != nil {
return
}
comp.Status = status
return c.Client.Status().Update(ctx, comp, opts...)
})
}
// ConstructRevisionName will generate revisionName from componentName
// will be <componentName>-v<RevisionNumber>, for example: comp-v1
func ConstructRevisionName(componentName string, revision int64) string {
@@ -23,6 +23,8 @@ import (
"time"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -172,7 +174,7 @@ func (r *Reconciler) Reconcile(req reconcile.Request) (reconcile.Result, error)
hs.Status.ScopeHealthCondition = scopeCondition
hs.Status.WorkloadHealthConditions = wlConditions
return reconcile.Result{RequeueAfter: interval - elapsed}, errors.Wrap(r.client.Status().Update(ctx, hs), errUpdateHealthScopeStatus)
return reconcile.Result{RequeueAfter: interval - elapsed}, errors.Wrap(r.UpdateStatus(ctx, hs), errUpdateHealthScopeStatus)
}
// GetScopeHealthStatus get the status of the healthscope based on workload resources.
@@ -257,3 +259,15 @@ func (r *Reconciler) GetScopeHealthStatus(ctx context.Context, healthScope *v1al
return scopeCondition, workloadHealthConditions
}
// UpdateStatus updates v1alpha2.HealthScope's Status with retry.RetryOnConflict
func (r *Reconciler) UpdateStatus(ctx context.Context, hs *v1alpha2.HealthScope, opts ...client.UpdateOption) error {
status := hs.DeepCopy().Status
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err = r.client.Get(ctx, types.NamespacedName{Namespace: hs.Namespace, Name: hs.Name}, hs); err != nil {
return
}
hs.Status = status
return r.client.Status().Update(ctx, hs, opts...)
})
}
@@ -29,6 +29,8 @@ import (
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -172,12 +174,24 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
},
)
if err := r.Status().Update(ctx, &workload); err != nil {
if err := r.UpdateStatus(ctx, &workload); err != nil {
return util.ReconcileWaitResult, err
}
return ctrl.Result{}, util.PatchCondition(ctx, r, &workload, cpv1alpha1.ReconcileSuccess())
}
// UpdateStatus updates v1alpha2.ContainerizedWorkload's Status with retry.RetryOnConflict
func (r *Reconciler) UpdateStatus(ctx context.Context, workload *v1alpha2.ContainerizedWorkload, opts ...client.UpdateOption) error {
status := workload.DeepCopy().Status
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err = r.Get(ctx, types.NamespacedName{Namespace: workload.Namespace, Name: workload.Name}, workload); err != nil {
return
}
workload.Status = status
return r.Status().Update(ctx, workload, opts...)
})
}
// SetupWithManager setups up k8s controller.
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
src := &v1alpha2.ContainerizedWorkload{}
@@ -31,7 +31,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/util/retry"
"k8s.io/utils/pointer"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -163,7 +165,7 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
r.gcOrphanServiceMonitor(ctx, mLog, &metricsTrait)
(&metricsTrait).SetConditions(cpv1alpha1.ReconcileSuccess())
return ctrl.Result{}, errors.Wrap(r.Status().Update(ctx, &metricsTrait), common.ErrUpdateStatus)
return ctrl.Result{}, errors.Wrap(r.UpdateStatus(ctx, &metricsTrait), common.ErrUpdateStatus)
}
// fetch the label of the service that is associated with the workload
@@ -336,6 +338,18 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
Complete(r)
}
// UpdateStatus updates v1alpha1.MetricsTrait's Status with retry.RetryOnConflict
func (r *Reconciler) UpdateStatus(ctx context.Context, mt *v1alpha1.MetricsTrait, opts ...client.UpdateOption) error {
status := mt.DeepCopy().Status
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err = r.Get(ctx, types.NamespacedName{Namespace: mt.Namespace, Name: mt.Name}, mt); err != nil {
return
}
mt.Status = status
return r.Status().Update(ctx, mt, opts...)
})
}
// Setup adds a controller that reconciles MetricsTrait.
func Setup(mgr ctrl.Manager) error {
dm, err := discoverymapper.New(mgr.GetConfig())
@@ -30,7 +30,9 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/util/retry"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -151,7 +153,7 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
})
}
if err := r.Status().Update(ctx, &workload); err != nil {
if err := r.UpdateStatus(ctx, &workload); err != nil {
return util.ReconcileWaitResult, err
}
return ctrl.Result{}, util.PatchCondition(ctx, r, &workload, cpv1alpha1.ReconcileSuccess())
@@ -277,6 +279,18 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
Complete(r)
}
// UpdateStatus updates *v1alpha1.PodSpecWorkload's Status with retry.RetryOnConflict
func (r *Reconciler) UpdateStatus(ctx context.Context, workload *v1alpha1.PodSpecWorkload, opts ...client.UpdateOption) error {
status := workload.DeepCopy().Status
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err = r.Get(ctx, types.NamespacedName{Namespace: workload.Namespace, Name: workload.Name}, workload); err != nil {
return
}
workload.Status = status
return r.Status().Update(ctx, workload, opts...)
})
}
// Setup adds a controller that reconciles PodSpecWorkload.
func Setup(mgr ctrl.Manager) error {
reconciler := Reconciler{
@@ -23,11 +23,10 @@ import (
"reflect"
"time"
"github.com/oam-dev/kubevela/pkg/controller/utils"
standardv1alpha1 "github.com/oam-dev/kubevela/apis/standard.oam.dev/v1alpha1"
"github.com/oam-dev/kubevela/pkg/controller/common"
"github.com/oam-dev/kubevela/pkg/controller/standard.oam.dev/v1alpha1/routes/ingress"
"github.com/oam-dev/kubevela/pkg/controller/utils"
runtimev1alpha1 "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
"github.com/crossplane/crossplane-runtime/pkg/event"
@@ -39,7 +38,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/util/retry"
"k8s.io/utils/pointer"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -146,9 +147,9 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
routeTrait.Status.Status, conditions = routeIngress.CheckStatus(&routeTrait)
routeTrait.Status.Conditions = conditions
if routeTrait.Status.Status != ingress.StatusReady {
return ctrl.Result{RequeueAfter: requeueNotReady}, r.Status().Update(ctx, &routeTrait)
return ctrl.Result{RequeueAfter: requeueNotReady}, r.UpdateStatus(ctx, &routeTrait)
}
err = r.Status().Update(ctx, &routeTrait)
err = r.UpdateStatus(ctx, &routeTrait)
if err != nil {
return oamutil.ReconcileWaitResult, err
}
@@ -247,6 +248,18 @@ func (r *Reconciler) fillBackendByCreatedService(ctx context.Context, mLog logr.
}, nil
}
// UpdateStatus updates standardv1alpha1.Route's Status with retry.RetryOnConflict
func (r *Reconciler) UpdateStatus(ctx context.Context, route *standardv1alpha1.Route, opts ...client.UpdateOption) error {
status := route.DeepCopy().Status
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err = r.Get(ctx, types.NamespacedName{Namespace: route.Namespace, Name: route.Name}, route); err != nil {
return
}
route.Status = status
return r.Status().Update(ctx, route, opts...)
})
}
// DiscoverPortsLabel assume the workload or it's childResource will always having spec.template as PodTemplate if discoverable
func DiscoverPortsLabel(ctx context.Context, workload *unstructured.Unstructured, r client.Reader, dm discoverymapper.DiscoveryMapper, childResources []*unstructured.Unstructured) ([]intstr.IntOrString, map[string]string, error) {