diff --git a/cmd/argo/commands/executorplugin/io.go b/cmd/argo/commands/executorplugin/io.go index 70a90195b711..41b768b851f6 100644 --- a/cmd/argo/commands/executorplugin/io.go +++ b/cmd/argo/commands/executorplugin/io.go @@ -41,7 +41,7 @@ func loadPluginManifest(pluginDir string) (*spec.Plugin, error) { } func addHeader(x []byte, h string) []byte { - return []byte(fmt.Sprintf("%s\n%s", h, string(x))) + return fmt.Appendf(nil, "%s\n%s", h, string(x)) } func addCodegenHeader(x []byte) []byte { diff --git a/cmd/argoexec/commands/emissary_windows_test.go b/cmd/argoexec/commands/emissary_windows_test.go index 9df0b4457fe8..7da083e0f7d7 100644 --- a/cmd/argoexec/commands/emissary_windows_test.go +++ b/cmd/argoexec/commands/emissary_windows_test.go @@ -6,10 +6,11 @@ import ( "os" "testing" - cmdutil "github.com/argoproj/argo-workflows/v3/util/cmd" - "github.com/argoproj/argo-workflows/v3/util/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + cmdutil "github.com/argoproj/argo-workflows/v3/util/cmd" + "github.com/argoproj/argo-workflows/v3/util/errors" ) func TestEmissary(t *testing.T) { diff --git a/hack/docs/configdoc.go b/hack/docs/configdoc.go index 4e8b67b85a47..3c76d30810a8 100644 --- a/hack/docs/configdoc.go +++ b/hack/docs/configdoc.go @@ -274,19 +274,19 @@ func createTypeLinkWithSpacing(baseType string) (string, bool) { return fmt.Sprintf("[`%s`](#%s)", cleanBaseType, strings.ToLower(baseType)), true } - if strings.HasPrefix(baseType, "wfv1.") { - wfType := strings.TrimPrefix(baseType, "wfv1.") + if after, ok := strings.CutPrefix(baseType, "wfv1."); ok { + wfType := after return fmt.Sprintf("[`%s`](fields.md#%s)", wfType, strings.ToLower(wfType)), true } - if strings.HasPrefix(baseType, "apiv1.") { - typeName := strings.TrimPrefix(baseType, "apiv1.") + if after, ok := strings.CutPrefix(baseType, "apiv1."); ok { + typeName := after anchor := strings.ToLower(typeName) return fmt.Sprintf("[`%s`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#%s-v1-core)", typeName, anchor), true } - if strings.HasPrefix(baseType, "metav1.") { - typeName := strings.TrimPrefix(baseType, "metav1.") + if after, ok := strings.CutPrefix(baseType, "metav1."); ok { + typeName := after anchor := strings.ToLower(typeName) return fmt.Sprintf("[`%s`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#%s-v1-meta)", typeName, anchor), true } diff --git a/hack/featuregen/contents.go b/hack/featuregen/contents.go index 2b64d7cd30de..77c1c83033a1 100644 --- a/hack/featuregen/contents.go +++ b/hack/featuregen/contents.go @@ -186,7 +186,7 @@ func format(version string, features []feature) string { output.WriteString(fmt.Sprintf("- %s by %s %s\n", feature.Description, feature.Author, issuesStr)) if feature.Details != "" { - for _, line := range strings.Split(feature.Details, "\n") { + for line := range strings.SplitSeq(feature.Details, "\n") { if line != "" { output.WriteString(fmt.Sprintf(" %s\n", line)) } diff --git a/server/utils/list_options.go b/server/utils/list_options.go index 794e9ddcfa91..22674b21cb9f 100644 --- a/server/utils/list_options.go +++ b/server/utils/list_options.go @@ -92,14 +92,14 @@ func BuildListOptions(options metav1.ListOptions, ns, namePrefix, nameFilter, cr } } showRemainingItemCount := false - for _, selector := range strings.Split(options.FieldSelector, ",") { + for selector := range strings.SplitSeq(options.FieldSelector, ",") { if len(selector) == 0 { continue } - if strings.HasPrefix(selector, "metadata.namespace=") { + if after, ok := strings.CutPrefix(selector, "metadata.namespace="); ok { // for backward compatibility, the field selector 'metadata.namespace' is supported for now despite the addition // of the new 'namespace' query parameter, which is what the UI uses - fieldSelectedNamespace := strings.TrimPrefix(selector, "metadata.namespace=") + fieldSelectedNamespace := after switch namespace { case "": namespace = fieldSelectedNamespace @@ -109,16 +109,16 @@ func BuildListOptions(options metav1.ListOptions, ns, namePrefix, nameFilter, cr return ListOptions{}, status.Errorf(codes.InvalidArgument, "'namespace' query param (%q) and fieldselector 'metadata.namespace' (%q) are both specified and contradict each other", namespace, fieldSelectedNamespace) } - } else if strings.HasPrefix(selector, "metadata.name=") { - name = strings.TrimPrefix(selector, "metadata.name=") - } else if strings.HasPrefix(selector, "spec.startedAt>") { - minStartedAt, err = time.Parse(time.RFC3339, strings.TrimPrefix(selector, "spec.startedAt>")) + } else if after, ok := strings.CutPrefix(selector, "metadata.name="); ok { + name = after + } else if after, ok := strings.CutPrefix(selector, "spec.startedAt>"); ok { + minStartedAt, err = time.Parse(time.RFC3339, after) if err != nil { // startedAt is populated by us, it should therefore be valid. return ListOptions{}, ToStatusError(err, codes.Internal) } - } else if strings.HasPrefix(selector, "spec.startedAt<") { - maxStartedAt, err = time.Parse(time.RFC3339, strings.TrimPrefix(selector, "spec.startedAt<")) + } else if after, ok := strings.CutPrefix(selector, "spec.startedAt<"); ok { + maxStartedAt, err = time.Parse(time.RFC3339, after) if err != nil { // no need to use sutils here return ListOptions{}, ToStatusError(err, codes.Internal) diff --git a/server/workflow/workflow_server.go b/server/workflow/workflow_server.go index 1c2dd7e220c2..688c170572db 100644 --- a/server/workflow/workflow_server.go +++ b/server/workflow/workflow_server.go @@ -250,10 +250,7 @@ func (s *workflowServer) ListWorkflows(ctx context.Context, req *workflowpkg.Wor var remainCount int64 if options.ShowRemainingItemCount { // Calculate exact remaining count when requested - remainCount = totalCount - int64(options.Offset) - int64(len(wfs)) - if remainCount < 0 { - remainCount = 0 - } + remainCount = max(totalCount-int64(options.Offset)-int64(len(wfs)), 0) meta.RemainingItemCount = &remainCount } else { // For pagination without remaining count, use the efficient HasMoreWorkflows method diff --git a/test/e2e/fixtures/given.go b/test/e2e/fixtures/given.go index 3defa9804ae4..715ef98ea5e3 100644 --- a/test/e2e/fixtures/given.go +++ b/test/e2e/fixtures/given.go @@ -71,8 +71,8 @@ func (g *Given) WorkflowWorkflow(wf *wfv1.Workflow) *Given { func (g *Given) readResource(text string, v metav1.Object) { g.t.Helper() var file string - if strings.HasPrefix(text, "@") { - file = strings.TrimPrefix(text, "@") + if after, ok := strings.CutPrefix(text, "@"); ok { + file = after } else { f, err := os.CreateTemp("", "argo_e2e") if err != nil { diff --git a/test/e2e/fixtures/metrics.go b/test/e2e/fixtures/metrics.go index c35570640f12..e25d08450786 100644 --- a/test/e2e/fixtures/metrics.go +++ b/test/e2e/fixtures/metrics.go @@ -95,9 +95,9 @@ func (mb *MetricBaseline) ExpectIncrease() { func parseMetricValue(body, metricPattern string) float64 { // Escape special regex characters in the metric pattern, but keep the spaces // We'll look for lines that match the pattern and extract the value - lines := strings.Split(body, "\n") + lines := strings.SplitSeq(body, "\n") - for _, line := range lines { + for line := range lines { line = strings.TrimSpace(line) if line == "" || strings.HasPrefix(line, "#") { continue diff --git a/test/e2e/fixtures/util.go b/test/e2e/fixtures/util.go index d21c20abe7d7..8994ac9c89e4 100644 --- a/test/e2e/fixtures/util.go +++ b/test/e2e/fixtures/util.go @@ -26,7 +26,7 @@ func Exec(name string, args ...string) (string, error) { if err != nil { errorln(err) } - for _, s := range strings.Split(output, "\n") { + for s := range strings.SplitSeq(output, "\n") { _, _ = fmt.Println(s) } return output, err @@ -56,8 +56,8 @@ func runWithTimeout(cmd *exec.Cmd) (string, error) { // LoadObject is used to load yaml to runtime.Object func LoadObject(text string) (runtime.Object, error) { var yaml string - if strings.HasPrefix(text, "@") { - file := strings.TrimPrefix(text, "@") + if after, ok := strings.CutPrefix(text, "@"); ok { + file := after f, err := os.ReadFile(filepath.Clean(file)) if err != nil { return nil, err diff --git a/test/e2e/fixtures/when.go b/test/e2e/fixtures/when.go index 0a9291b42a73..048d558e8b2a 100644 --- a/test/e2e/fixtures/when.go +++ b/test/e2e/fixtures/when.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "maps" "reflect" "strconv" "strings" @@ -601,9 +602,7 @@ func (w *When) CreateConfigMap(name string, data map[string]string, customLabels labels := map[string]string{Label: "true"} - for k, v := range customLabels { - labels[k] = v - } + maps.Copy(labels, customLabels) ctx := logging.TestContext(w.t.Context()) _, err := w.kubeClient.CoreV1().ConfigMaps(Namespace).Create(ctx, &corev1.ConfigMap{ @@ -621,9 +620,7 @@ func (w *When) UpdateConfigMap(name string, data map[string]string, customLabels labels := map[string]string{Label: "true"} - for k, v := range customLabels { - labels[k] = v - } + maps.Copy(labels, customLabels) ctx := logging.TestContext(w.t.Context()) _, err := w.kubeClient.CoreV1().ConfigMaps(Namespace).Update(ctx, &corev1.ConfigMap{ diff --git a/util/fields/fields.go b/util/fields/fields.go index 0b5849c674bd..b2f77764ea76 100644 --- a/util/fields/fields.go +++ b/util/fields/fields.go @@ -12,7 +12,7 @@ func NewCleaner(x string) Cleaner { x = x[1:] y.exclude = true } - for _, field := range strings.Split(x, ",") { + for field := range strings.SplitSeq(x, ",") { y.fields[field] = true } } diff --git a/util/logging/slog.go b/util/logging/slog.go index 71b4399b8c1d..bb56774b88aa 100644 --- a/util/logging/slog.go +++ b/util/logging/slog.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "log/slog" + "maps" "os" ) @@ -80,13 +81,9 @@ func (s *slogLogger) Level() Level { func (s *slogLogger) WithFields(fields Fields) Logger { newFields := make(Fields) - for k, v := range s.fields { - newFields[k] = v - } + maps.Copy(newFields, s.fields) - for k, v := range fields { - newFields[k] = v - } + maps.Copy(newFields, fields) return &slogLogger{ fields: newFields, @@ -101,9 +98,7 @@ func (s *slogLogger) WithFields(fields Fields) Logger { func (s *slogLogger) WithField(name string, value any) Logger { newFields := make(Fields) - for k, v := range s.fields { - newFields[k] = v - } + maps.Copy(newFields, s.fields) newFields[name] = value diff --git a/util/resource/duration.go b/util/resource/duration.go index ba7bc4374a82..de68fbd3dedc 100644 --- a/util/resource/duration.go +++ b/util/resource/duration.go @@ -1,6 +1,8 @@ package resource import ( + "maps" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" @@ -18,12 +20,8 @@ func DurationForPod(pod *corev1.Pod) wfv1.ResourcesDuration { corev1.ResourceMemory: resource.MustParse("100Mi"), }} // Update with user-configured resources (falls back to limits as == requests, same as Kubernetes). - for name, quantity := range c.Resources.Limits { - summaries[c.Name].ResourceList[name] = quantity - } - for name, quantity := range c.Resources.Requests { - summaries[c.Name].ResourceList[name] = quantity - } + maps.Copy(summaries[c.Name].ResourceList, c.Resources.Limits) + maps.Copy(summaries[c.Name].ResourceList, c.Resources.Requests) } for _, c := range append(pod.Status.InitContainerStatuses, pod.Status.ContainerStatuses...) { summaries[c.Name] = Summary{ResourceList: summaries[c.Name].ResourceList, ContainerState: c.State} diff --git a/util/telemetry/builder/builder.go b/util/telemetry/builder/builder.go index 936538057448..380c03340d4c 100644 --- a/util/telemetry/builder/builder.go +++ b/util/telemetry/builder/builder.go @@ -105,7 +105,7 @@ func upperToSnake(in string) string { in = string(append([]rune{unicode.ToLower(runes[0])}, runes[1:]...)) re := regexp.MustCompile(`[A-Z]`) return string(re.ReplaceAllFunc([]byte(in), func(in []byte) []byte { - return []byte(fmt.Sprintf("_%s", strings.ToLower(string(in[0])))) + return fmt.Appendf(nil, "_%s", strings.ToLower(string(in[0]))) })) } diff --git a/util/template/expression_template.go b/util/template/expression_template.go index 1c4c84753a4c..18d6a6da68a8 100644 --- a/util/template/expression_template.go +++ b/util/template/expression_template.go @@ -36,7 +36,7 @@ func expressionReplace(ctx context.Context, w io.Writer, expression string, env log := logging.RequireLoggerFromContext(ctx) // The template is JSON-marshaled. This JSON-unmarshals the expression to undo any character escapes. var unmarshalledExpression string - err := json.Unmarshal([]byte(fmt.Sprintf(`"%s"`, expression)), &unmarshalledExpression) + err := json.Unmarshal(fmt.Appendf(nil, `"%s"`, expression), &unmarshalledExpression) if err != nil && allowUnresolved { log.WithError(err).Debug(ctx, "unresolved is allowed ") return fmt.Fprintf(w, "{{%s%s}}", kindExpression, expression) diff --git a/util/template/kind.go b/util/template/kind.go index 989464f41300..cdbf20a7e913 100644 --- a/util/template/kind.go +++ b/util/template/kind.go @@ -21,8 +21,8 @@ func registerKind(k kind) { func parseTag(tag string) (kind, string) { for _, k := range kinds { - if strings.HasPrefix(tag, k) { - return k, jsonutil.Fix(strings.TrimPrefix(tag, k)) + if after, ok := strings.CutPrefix(tag, k); ok { + return k, jsonutil.Fix(after) } } return kindSimple, tag diff --git a/workflow/common/convert.go b/workflow/common/convert.go index 6ab45463d014..f2177fd3f4ff 100644 --- a/workflow/common/convert.go +++ b/workflow/common/convert.go @@ -2,6 +2,7 @@ package common import ( "context" + "maps" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -94,14 +95,10 @@ func toWorkflow(cronWf wfv1.CronWorkflow, objectMeta metav1.ObjectMeta) *wfv1.Wo wf.Labels[LabelKeyCronWorkflow] = cronWf.Name if cronWf.Spec.WorkflowMetadata != nil { - for key, label := range cronWf.Spec.WorkflowMetadata.Labels { - wf.Labels[key] = label - } + maps.Copy(wf.Labels, cronWf.Spec.WorkflowMetadata.Labels) if len(cronWf.Spec.WorkflowMetadata.Annotations) > 0 { - for key, annotation := range cronWf.Spec.WorkflowMetadata.Annotations { - wf.Annotations[key] = annotation - } + maps.Copy(wf.Annotations, cronWf.Spec.WorkflowMetadata.Annotations) } wf.Finalizers = append(wf.Finalizers, cronWf.Spec.WorkflowMetadata.Finalizers...) diff --git a/workflow/common/params.go b/workflow/common/params.go index fa7314e8ebf1..dcb99b072422 100644 --- a/workflow/common/params.go +++ b/workflow/common/params.go @@ -1,5 +1,7 @@ package common +import "maps" + // Parameters extends string map with useful methods. type Parameters map[string]string @@ -7,9 +9,7 @@ type Parameters map[string]string func (ps Parameters) Merge(args ...Parameters) Parameters { newParams := ps.DeepCopy() for _, params := range args { - for k, v := range params { - newParams[k] = v - } + maps.Copy(newParams, params) } return newParams } @@ -17,8 +17,6 @@ func (ps Parameters) Merge(args ...Parameters) Parameters { // DeepCopy returns a new instance which has the same parameters as the receiver. func (ps Parameters) DeepCopy() Parameters { newParams := make(Parameters) - for k, v := range ps { - newParams[k] = v - } + maps.Copy(newParams, ps) return newParams } diff --git a/workflow/controller/artifact_gc.go b/workflow/controller/artifact_gc.go index a04b1785a02a..144fd8121bb8 100644 --- a/workflow/controller/artifact_gc.go +++ b/workflow/controller/artifact_gc.go @@ -538,12 +538,8 @@ func (woc *wfOperationCtx) createArtifactGCPod(ctx context.Context, strategy wfv if podInfo.serviceAccount != "" { pod.Spec.ServiceAccountName = podInfo.serviceAccount } - for label, labelVal := range podInfo.podMetadata.Labels { - pod.Labels[label] = labelVal - } - for annotation, annotationVal := range podInfo.podMetadata.Annotations { - pod.Annotations[annotation] = annotationVal - } + maps.Copy(pod.Labels, podInfo.podMetadata.Labels) + maps.Copy(pod.Annotations, podInfo.podMetadata.Annotations) if v := woc.controller.Config.InstanceID; v != "" { pod.Labels[common.EnvVarInstanceID] = v @@ -771,15 +767,11 @@ func (woc *wfOperationCtx) updateArtifactGCPodInfo(artifactGC *wfv1.ArtifactGC, if len(artifactGC.PodMetadata.Labels) > 0 && podInfo.podMetadata.Labels == nil { podInfo.podMetadata.Labels = make(map[string]string) } - for labelKey, labelValue := range artifactGC.PodMetadata.Labels { - podInfo.podMetadata.Labels[labelKey] = labelValue - } + maps.Copy(podInfo.podMetadata.Labels, artifactGC.PodMetadata.Labels) if len(artifactGC.PodMetadata.Annotations) > 0 && podInfo.podMetadata.Annotations == nil { podInfo.podMetadata.Annotations = make(map[string]string) } - for annotationKey, annotationValue := range artifactGC.PodMetadata.Annotations { - podInfo.podMetadata.Annotations[annotationKey] = annotationValue - } + maps.Copy(podInfo.podMetadata.Annotations, artifactGC.PodMetadata.Annotations) } } diff --git a/workflow/controller/scope.go b/workflow/controller/scope.go index 0967d17fc7b7..ee8c6f02b3f1 100644 --- a/workflow/controller/scope.go +++ b/workflow/controller/scope.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "maps" "github.com/expr-lang/expr" @@ -61,9 +62,7 @@ func (s *wfScope) addArtifactToScope(key string, artifact wfv1.Artifact) { // resolveVar resolves a parameter or artifact func (s *wfScope) resolveVar(v string) (interface{}, error) { m := make(map[string]interface{}) - for k, v := range s.scope { - m[k] = v - } + maps.Copy(m, s.scope) if s.tmpl != nil { for _, a := range s.tmpl.Inputs.Artifacts { m["inputs.artifacts."+a.Name] = a // special case for artifacts diff --git a/workflow/controller/workflowpod.go b/workflow/controller/workflowpod.go index 8d4c6ede9c36..6382da137a12 100644 --- a/workflow/controller/workflowpod.go +++ b/workflow/controller/workflowpod.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "maps" "os" "path/filepath" "strconv" @@ -847,20 +848,12 @@ func (woc *wfOperationCtx) newExecContainer(name string, tmpl *wfv1.Template) *a func (woc *wfOperationCtx) addMetadata(pod *apiv1.Pod, tmpl *wfv1.Template) { if woc.execWf.Spec.PodMetadata != nil { // add workflow-level pod annotations and labels - for k, v := range woc.execWf.Spec.PodMetadata.Annotations { - pod.Annotations[k] = v - } - for k, v := range woc.execWf.Spec.PodMetadata.Labels { - pod.Labels[k] = v - } + maps.Copy(pod.Annotations, woc.execWf.Spec.PodMetadata.Annotations) + maps.Copy(pod.Labels, woc.execWf.Spec.PodMetadata.Labels) } - for k, v := range tmpl.Metadata.Annotations { - pod.Annotations[k] = v - } - for k, v := range tmpl.Metadata.Labels { - pod.Labels[k] = v - } + maps.Copy(pod.Annotations, tmpl.Metadata.Annotations) + maps.Copy(pod.Labels, tmpl.Metadata.Labels) } // addDNSConfig applies DNSConfig to the pod diff --git a/workflow/executor/osspecific/signal_windows.go b/workflow/executor/osspecific/signal_windows.go index bfb2c05f2230..04164ef3f176 100644 --- a/workflow/executor/osspecific/signal_windows.go +++ b/workflow/executor/osspecific/signal_windows.go @@ -6,8 +6,9 @@ import ( "syscall" "unsafe" - "github.com/argoproj/argo-workflows/v3/util/errors" "golang.org/x/sys/windows" + + "github.com/argoproj/argo-workflows/v3/util/errors" ) var ( diff --git a/workflow/executor/osspecific/signal_windows_test.go b/workflow/executor/osspecific/signal_windows_test.go index c8427f8402b1..bf42ee61dbda 100644 --- a/workflow/executor/osspecific/signal_windows_test.go +++ b/workflow/executor/osspecific/signal_windows_test.go @@ -8,9 +8,10 @@ import ( "syscall" "testing" - "github.com/argoproj/argo-workflows/v3/util/logging" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/argoproj/argo-workflows/v3/util/logging" ) func TestKill(t *testing.T) { diff --git a/workflow/sync/semaphore.go b/workflow/sync/semaphore.go index 1d99324708e5..a61bc4682b4a 100644 --- a/workflow/sync/semaphore.go +++ b/workflow/sync/semaphore.go @@ -84,11 +84,8 @@ func (s *prioritySemaphore) getCurrentHolders(_ context.Context) ([]string, erro } func (s *prioritySemaphore) resize(ctx context.Context, n int) bool { - cur := len(s.lockHolder) // downward case, acquired n locks - if cur > n { - cur = n - } + cur := min(len(s.lockHolder), n) semaphore := sema.NewWeighted(int64(n)) status := semaphore.TryAcquire(int64(cur)) @@ -129,10 +126,7 @@ func (s *prioritySemaphore) release(ctx context.Context, key string) bool { // notifyWaiters enqueues the next N workflows who are waiting for the semaphore to the workqueue, // where N is the availability of the semaphore. If semaphore is out of capacity, this does nothing. func (s *prioritySemaphore) notifyWaiters(ctx context.Context) { - triggerCount := s.getLimit(ctx) - len(s.lockHolder) - if s.pending.Len() < triggerCount { - triggerCount = s.pending.Len() - } + triggerCount := min(s.pending.Len(), s.getLimit(ctx)-len(s.lockHolder)) for idx := 0; idx < triggerCount; idx++ { item := s.pending.items[idx] wfKey := workflowKey(item.key) diff --git a/workflow/templateresolution/context.go b/workflow/templateresolution/context.go index bca6386627a8..1185e34ec739 100644 --- a/workflow/templateresolution/context.go +++ b/workflow/templateresolution/context.go @@ -3,6 +3,7 @@ package templateresolution import ( "context" "fmt" + "maps" apierr "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -288,15 +289,11 @@ func (tplCtx *TemplateContext) addPodMetadata(podMetadata *wfv1.Metadata, tmpl * if tmpl.Metadata.Annotations == nil { tmpl.Metadata.Annotations = make(map[string]string) } - for k, v := range podMetadata.Annotations { - tmpl.Metadata.Annotations[k] = v - } + maps.Copy(tmpl.Metadata.Annotations, podMetadata.Annotations) if tmpl.Metadata.Labels == nil { tmpl.Metadata.Labels = make(map[string]string) } - for k, v := range podMetadata.Labels { - tmpl.Metadata.Labels[k] = v - } + maps.Copy(tmpl.Metadata.Labels, podMetadata.Labels) } } diff --git a/workflow/util/plugins/configmap.go b/workflow/util/plugins/configmap.go index a97df800a66b..72055a06dd54 100644 --- a/workflow/util/plugins/configmap.go +++ b/workflow/util/plugins/configmap.go @@ -2,6 +2,7 @@ package plugin import ( "fmt" + "maps" "strings" apiv1 "k8s.io/api/core/v1" @@ -38,12 +39,8 @@ func ToConfigMap(p *spec.Plugin) (*apiv1.ConfigMap, error) { "sidecar.container": string(data), }, } - for k, v := range p.Annotations { - cm.Annotations[k] = v - } - for k, v := range p.Labels { - cm.Labels[k] = v - } + maps.Copy(cm.Annotations, p.Annotations) + maps.Copy(cm.Labels, p.Labels) return cm, nil } @@ -58,12 +55,8 @@ func FromConfigMap(cm *apiv1.ConfigMap) (*spec.Plugin, error) { Labels: map[string]string{}, }, } - for k, v := range cm.Annotations { - p.Annotations[k] = v - } - for k, v := range cm.Labels { - p.Labels[k] = v - } + maps.Copy(p.Annotations, cm.Annotations) + maps.Copy(p.Labels, cm.Labels) delete(p.Labels, common.LabelKeyConfigMapType) p.Spec.Sidecar.AutomountServiceAccountToken = cm.Data["sidecar.automountServiceAccountToken"] == "true" if err := yaml.UnmarshalStrict([]byte(cm.Data["sidecar.container"]), &p.Spec.Sidecar.Container); err != nil { diff --git a/workflow/util/util.go b/workflow/util/util.go index 3a5f704536e7..ae9af3480330 100644 --- a/workflow/util/util.go +++ b/workflow/util/util.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io" + "maps" "math/rand" "net/http" "os" @@ -269,9 +270,7 @@ func ApplySubmitOpts(wf *wfv1.Workflow, opts *wfv1.SubmitOpts) error { if err != nil { return fmt.Errorf("expected labels of the form: NAME1=VALUE2,NAME2=VALUE2. Received: %s: %w", opts.Labels, err) } - for k, v := range passedLabels { - wfLabels[k] = v - } + maps.Copy(wfLabels, passedLabels) } wf.SetLabels(wfLabels) wfAnnotations := wf.GetAnnotations() @@ -283,9 +282,7 @@ func ApplySubmitOpts(wf *wfv1.Workflow, opts *wfv1.SubmitOpts) error { if err != nil { return fmt.Errorf("expected Annotations of the form: NAME1=VALUE2,NAME2=VALUE2. Received: %s: %w", opts.Labels, err) } - for k, v := range passedAnnotations { - wfAnnotations[k] = v - } + maps.Copy(wfAnnotations, passedAnnotations) } wf.SetAnnotations(wfAnnotations) err := overrideParameters(wf, opts.Parameters) @@ -693,9 +690,7 @@ func FormulateResubmitWorkflow(ctx context.Context, wf *wfv1.Workflow, memoized if newWF.Annotations == nil { newWF.Annotations = make(map[string]string) } - for key, val := range wf.Annotations { - newWF.Annotations[key] = val - } + maps.Copy(newWF.Annotations, wf.Annotations) // Setting OwnerReference from original Workflow newWF.OwnerReferences = append(newWF.OwnerReferences, wf.OwnerReferences...) @@ -766,9 +761,7 @@ func FormulateResubmitWorkflow(ctx context.Context, wf *wfv1.Workflow, memoized } newWF.Status.StoredTemplates = make(map[string]wfv1.Template) - for id, tmpl := range wf.Status.StoredTemplates { - newWF.Status.StoredTemplates[id] = tmpl - } + maps.Copy(newWF.Status.StoredTemplates, wf.Status.StoredTemplates) newWF.Status.Conditions = wfv1.Conditions{{Status: metav1.ConditionFalse, Type: wfv1.ConditionTypeCompleted}} newWF.Status.Phase = wfv1.WorkflowUnknown @@ -1090,9 +1083,7 @@ func resetPath(allNodes []*dagNode, startNode string) (map[string]bool, map[stri func setUnion[T comparable](m1 map[T]bool, m2 map[T]bool) map[T]bool { res := make(map[T]bool) - for k, v := range m1 { - res[k] = v - } + maps.Copy(res, m1) for k, v := range m2 { if _, ok := m1[k]; !ok { diff --git a/workflow/validate/validate.go b/workflow/validate/validate.go index 20cc6dcb5944..7082acbc55f1 100644 --- a/workflow/validate/validate.go +++ b/workflow/validate/validate.go @@ -982,9 +982,7 @@ func (tctx *templateValidationCtx) validateSteps(ctx context.Context, scope map[ } stepScope := make(map[string]interface{}) - for k, v := range scope { - stepScope[k] = v - } + maps.Copy(stepScope, scope) if i := step.Inline; i != nil { for _, p := range i.Inputs.Parameters { @@ -1413,9 +1411,7 @@ func (tctx *templateValidationCtx) validateDAG(ctx context.Context, scope map[st return errors.InternalWrapError(err) } taskScope := make(map[string]interface{}) - for k, v := range scope { - taskScope[k] = v - } + maps.Copy(taskScope, scope) ancestry := common.GetTaskAncestry(ctx, dagValidationCtx, task.Name) for _, ancestor := range ancestry { ancestorTask := dagValidationCtx.GetTask(ctx, ancestor) @@ -1492,7 +1488,7 @@ func validateDAGTargets(tmpl *wfv1.Template, nameToTask map[string]wfv1.DAGTask) if tmpl.DAG.Target == "" { return nil } - for _, targetName := range strings.Split(tmpl.DAG.Target, " ") { + for targetName := range strings.SplitSeq(tmpl.DAG.Target, " ") { if isParameter(targetName) { continue }