Repository: traefik/traefik
File: pkg/provider/configuration.go (Go)

Code before:
func MakeDefaultRuleTemplate(defaultRule string, funcMap template.FuncMap) (*template.Template, error) {
	defaultFuncMap := sprig.TxtFuncMap()
	defaultFuncMap["normalize"] = Normalize

	for k, fn := range funcMap {
		defaultFuncMap[k] = fn
	}

	return template.New("defaultRule").Funcs(defaultFuncMap).Parse(defaultRule)
}

// BuildTCPRouterConfiguration builds a router configuration.
func BuildTCPRouterConfiguration(ctx context.Context, configuration *dynamic.TCPConfiguration) {
	for routerName, router := range configuration.Routers {
		loggerRouter := log.FromContext(ctx).WithField(log.RouterName, routerName)
		if len(router.Rule) == 0 {
			delete(configuration.Routers, routerName)
			loggerRouter.Errorf("Empty rule")
			continue
		}

		if router.Service == "" {
			if len(configuration.Services) > 1 {
				delete(configuration.Routers, routerName)
				loggerRouter.
					Errorf("No service name for the router: %s, see https://doc.traefik.io/traefik/routing/providers/docker/#service-definition", routerName)
				continue
			}

			for serviceName := range configuration.Services {
				router.Service = serviceName
			}
		}
	}
}

// BuildUDPRouterConfiguration builds a router configuration.
func BuildUDPRouterConfiguration(ctx context.Context, configuration *dynamic.UDPConfiguration) {
	for routerName, router := range configuration.Routers {
		loggerRouter := log.FromContext(ctx).WithField(log.RouterName, routerName)
		if router.Service != "" {
			continue
		}

		if len(configuration.Services) > 1 {
			delete(configuration.Routers, routerName)
			loggerRouter.
				Errorf("No service name for the router: %s, see https://doc.traefik.io/traefik/routing/providers/docker/#service-definition", routerName)
			continue
		}


Review comment (suggestion):
I suggest to collect service names and print something like:
```suggestion
					Errorf("Service cannot be attached automatically  for router %q, router configuration should specify which of the many possible services should be attached: ", routerName, serviceNames])
```

Code after:
// MakeDefaultRuleTemplate creates the default rule template.
func MakeDefaultRuleTemplate(defaultRule string, funcMap template.FuncMap) (*template.Template, error) {
	defaultFuncMap := sprig.TxtFuncMap()
	defaultFuncMap["normalize"] = Normalize

	for k, fn := range funcMap {
		defaultFuncMap[k] = fn
	}

	return template.New("defaultRule").Funcs(defaultFuncMap).Parse(defaultRule)
}

// BuildTCPRouterConfiguration builds a router configuration.
func BuildTCPRouterConfiguration(ctx context.Context, configuration *dynamic.TCPConfiguration) {
	for routerName, router := range configuration.Routers {
		loggerRouter := log.FromContext(ctx).WithField(log.RouterName, routerName)
		if len(router.Rule) == 0 {
			delete(configuration.Routers, routerName)
			loggerRouter.Errorf("Empty rule")
			continue
		}

		if router.Service == "" {
			if len(configuration.Services) > 1 {
				delete(configuration.Routers, routerName)
				loggerRouter.
					Errorf("Router %s cannot be linked automatically with multiple Services: %q", routerName, maps.Keys(configuration.Services))
				continue
			}

			for serviceName := range configuration.Services {
				router.Service = serviceName
			}
		}
	}
}

// BuildUDPRouterConfiguration builds a router configuration.
func BuildUDPRouterConfiguration(ctx context.Context, configuration *dynamic.UDPConfiguration) {
	for routerName, router := range configuration.Routers {
		loggerRouter := log.FromContext(ctx).WithField(log.RouterName, routerName)
		if router.Service != "" {
			continue
		}

		if len(configuration.Services) > 1 {
			delete(configuration.Routers, routerName)
			loggerRouter.
				Errorf("Router %s cannot be linked automatically with multiple Services: %q", routerName, maps.Keys(configuration.Services))
			continue
		}