fix(lint): reduce standalone service lint complexity
- commit
- ed415c10221d2ef7293bafeb61c4f1935dcd86a3
- author
- brice · · 2026-06-30 15:57 UTC
- parent
- 7b54de4
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +68 -60
M internal/adapters/out/docker/runtime.go +57 -46
| @@ -141,53 +141,11 @@ func (r *Runtime) CreateContainer(ctx context.Context, config *domain.ContainerC | |||
| 141 | 141 | }) | |
| 142 | 142 | log := zerowrap.FromCtx(ctx) | |
| 143 | 143 | ||
| 144 | - | // Convert ports to Docker format | |
| 145 | - | exposedPorts := make(nat.PortSet) | |
| 146 | - | portBindings := make(nat.PortMap) | |
| 147 | - | ||
| 148 | - | for _, port := range config.Ports { | |
| 149 | - | containerPort := nat.Port(fmt.Sprintf("%d/tcp", port)) | |
| 150 | - | exposedPorts[containerPort] = struct{}{} | |
| 151 | - | ||
| 152 | - | // Bind to random available port on localhost only. | |
| 153 | - | // SECURITY: Using 127.0.0.1 prevents direct access from the network, | |
| 154 | - | // forcing all traffic through Gordon's reverse proxy where auth, | |
| 155 | - | // rate limiting, and security headers are applied. | |
| 156 | - | portBindings[containerPort] = []nat.PortBinding{ | |
| 157 | - | { | |
| 158 | - | HostIP: "127.0.0.1", | |
| 159 | - | HostPort: "0", // Docker will assign a random available port | |
| 160 | - | }, | |
| 161 | - | } | |
| 162 | - | } | |
| 163 | - | for _, publish := range config.PortPublishes { | |
| 164 | - | if publish.Protocol != domain.NetworkProtocolTCP && publish.Protocol != domain.NetworkProtocolUDP { | |
| 165 | - | return nil, fmt.Errorf("unsupported container port publish protocol %q", publish.Protocol) | |
| 166 | - | } | |
| 167 | - | containerPort := nat.Port(fmt.Sprintf("%d/%s", publish.ContainerPort, publish.Protocol)) | |
| 168 | - | exposedPorts[containerPort] = struct{}{} | |
| 169 | - | portBindings[containerPort] = append(portBindings[containerPort], nat.PortBinding{ | |
| 170 | - | HostIP: publish.HostIP, | |
| 171 | - | HostPort: strconv.Itoa(publish.HostPort), | |
| 172 | - | }) | |
| 173 | - | } | |
| 174 | - | ||
| 175 | - | // Convert volumes to Docker format | |
| 176 | - | var binds []string | |
| 177 | - | if config.Volumes != nil { | |
| 178 | - | for containerPath, volumeName := range config.Volumes { | |
| 179 | - | bind := fmt.Sprintf("%s:%s", volumeName, containerPath) | |
| 180 | - | binds = append(binds, bind) | |
| 181 | - | log.Debug().Str("volume", volumeName).Str("mount_path", containerPath).Msg("adding volume mount") | |
| 182 | - | } | |
| 183 | - | } | |
| 184 | - | if config.ReadOnlyVolumes != nil { | |
| 185 | - | for containerPath, volumeName := range config.ReadOnlyVolumes { | |
| 186 | - | bind := fmt.Sprintf("%s:%s:ro", volumeName, containerPath) | |
| 187 | - | binds = append(binds, bind) | |
| 188 | - | log.Debug().Str("volume", volumeName).Str("mount_path", containerPath).Msg("adding read-only volume mount") | |
| 189 | - | } | |
| 144 | + | exposedPorts, portBindings, err := buildPortBindings(config) | |
| 145 | + | if err != nil { | |
| 146 | + | return nil, err | |
| 190 | 147 | } | |
| 148 | + | binds := buildVolumeBinds(config, log) | |
| 191 | 149 | ||
| 192 | 150 | // Create container configuration | |
| 193 | 151 | containerConfig := &container.Config{ | |
| @@ -261,6 +219,59 @@ func (r *Runtime) CreateContainer(ctx context.Context, config *domain.ContainerC | |||
| 261 | 219 | return r.InspectContainer(ctx, resp.ID) | |
| 262 | 220 | } | |
| 263 | 221 | ||
| 222 | + | func buildPortBindings(config *domain.ContainerConfig) (nat.PortSet, nat.PortMap, error) { | |
| 223 | + | exposedPorts := make(nat.PortSet) | |
| 224 | + | portBindings := make(nat.PortMap) | |
| 225 | + | addLegacyPortBindings(config.Ports, exposedPorts, portBindings) | |
| 226 | + | if err := addExplicitPortBindings(config.PortPublishes, exposedPorts, portBindings); err != nil { | |
| 227 | + | return nil, nil, err | |
| 228 | + | } | |
| 229 | + | return exposedPorts, portBindings, nil | |
| 230 | + | } | |
| 231 | + | ||
| 232 | + | func addLegacyPortBindings(ports []int, exposedPorts nat.PortSet, portBindings nat.PortMap) { | |
| 233 | + | for _, port := range ports { | |
| 234 | + | containerPort := nat.Port(fmt.Sprintf("%d/tcp", port)) | |
| 235 | + | exposedPorts[containerPort] = struct{}{} | |
| 236 | + | ||
| 237 | + | // Bind to random available port on localhost only. | |
| 238 | + | // SECURITY: Using 127.0.0.1 prevents direct access from the network, | |
| 239 | + | // forcing all traffic through Gordon's reverse proxy where auth, | |
| 240 | + | // rate limiting, and security headers are applied. | |
| 241 | + | portBindings[containerPort] = []nat.PortBinding{{HostIP: "127.0.0.1", HostPort: "0"}} | |
| 242 | + | } | |
| 243 | + | } | |
| 244 | + | ||
| 245 | + | func addExplicitPortBindings(publishes []domain.ContainerPortPublish, exposedPorts nat.PortSet, portBindings nat.PortMap) error { | |
| 246 | + | for _, publish := range publishes { | |
| 247 | + | if publish.Protocol != domain.NetworkProtocolTCP && publish.Protocol != domain.NetworkProtocolUDP { | |
| 248 | + | return fmt.Errorf("unsupported container port publish protocol %q", publish.Protocol) | |
| 249 | + | } | |
| 250 | + | containerPort := nat.Port(fmt.Sprintf("%d/%s", publish.ContainerPort, publish.Protocol)) | |
| 251 | + | exposedPorts[containerPort] = struct{}{} | |
| 252 | + | portBindings[containerPort] = append(portBindings[containerPort], nat.PortBinding{ | |
| 253 | + | HostIP: publish.HostIP, | |
| 254 | + | HostPort: strconv.Itoa(publish.HostPort), | |
| 255 | + | }) | |
| 256 | + | } | |
| 257 | + | return nil | |
| 258 | + | } | |
| 259 | + | ||
| 260 | + | func buildVolumeBinds(config *domain.ContainerConfig, log zerowrap.Logger) []string { | |
| 261 | + | binds := make([]string, 0, len(config.Volumes)+len(config.ReadOnlyVolumes)) | |
| 262 | + | for containerPath, volumeName := range config.Volumes { | |
| 263 | + | bind := fmt.Sprintf("%s:%s", volumeName, containerPath) | |
| 264 | + | binds = append(binds, bind) | |
| 265 | + | log.Debug().Str("volume", volumeName).Str("mount_path", containerPath).Msg("adding volume mount") | |
| 266 | + | } | |
| 267 | + | for containerPath, volumeName := range config.ReadOnlyVolumes { | |
| 268 | + | bind := fmt.Sprintf("%s:%s:ro", volumeName, containerPath) | |
| 269 | + | binds = append(binds, bind) | |
| 270 | + | log.Debug().Str("volume", volumeName).Str("mount_path", containerPath).Msg("adding read-only volume mount") | |
| 271 | + | } | |
| 272 | + | return binds | |
| 273 | + | } | |
| 274 | + | ||
| 264 | 275 | // StartContainer starts a container. | |
| 265 | 276 | func (r *Runtime) StartContainer(ctx context.Context, containerID string) error { | |
| 266 | 277 | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | |
| @@ -141,53 +141,11 @@ func (r *Runtime) CreateContainer(ctx context.Context, config *domain.ContainerC | |||||
| 141 | }) | 141 | }) | ||
| 142 | log := zerowrap.FromCtx(ctx) | 142 | log := zerowrap.FromCtx(ctx) | ||
| 143 | 143 | ||||
| 144 | - | // Convert ports to Docker format | 144 | + | exposedPorts, portBindings, err := buildPortBindings(config) |
| 145 | - | exposedPorts := make(nat.PortSet) | 145 | + | if err != nil { |
| 146 | - | portBindings := make(nat.PortMap) | 146 | + | return nil, err |
| 147 | - | ||||
| 148 | - | for _, port := range config.Ports { | |||
| 149 | - | containerPort := nat.Port(fmt.Sprintf("%d/tcp", port)) | |||
| 150 | - | exposedPorts[containerPort] = struct{}{} | |||
| 151 | - | ||||
| 152 | - | // Bind to random available port on localhost only. | |||
| 153 | - | // SECURITY: Using 127.0.0.1 prevents direct access from the network, | |||
| 154 | - | // forcing all traffic through Gordon's reverse proxy where auth, | |||
| 155 | - | // rate limiting, and security headers are applied. | |||
| 156 | - | portBindings[containerPort] = []nat.PortBinding{ | |||
| 157 | - | { | |||
| 158 | - | HostIP: "127.0.0.1", | |||
| 159 | - | HostPort: "0", // Docker will assign a random available port | |||
| 160 | - | }, | |||
| 161 | - | } | |||
| 162 | - | } | |||
| 163 | - | for _, publish := range config.PortPublishes { | |||
| 164 | - | if publish.Protocol != domain.NetworkProtocolTCP && publish.Protocol != domain.NetworkProtocolUDP { | |||
| 165 | - | return nil, fmt.Errorf("unsupported container port publish protocol %q", publish.Protocol) | |||
| 166 | - | } | |||
| 167 | - | containerPort := nat.Port(fmt.Sprintf("%d/%s", publish.ContainerPort, publish.Protocol)) | |||
| 168 | - | exposedPorts[containerPort] = struct{}{} | |||
| 169 | - | portBindings[containerPort] = append(portBindings[containerPort], nat.PortBinding{ | |||
| 170 | - | HostIP: publish.HostIP, | |||
| 171 | - | HostPort: strconv.Itoa(publish.HostPort), | |||
| 172 | - | }) | |||
| 173 | - | } | |||
| 174 | - | ||||
| 175 | - | // Convert volumes to Docker format | |||
| 176 | - | var binds []string | |||
| 177 | - | if config.Volumes != nil { | |||
| 178 | - | for containerPath, volumeName := range config.Volumes { | |||
| 179 | - | bind := fmt.Sprintf("%s:%s", volumeName, containerPath) | |||
| 180 | - | binds = append(binds, bind) | |||
| 181 | - | log.Debug().Str("volume", volumeName).Str("mount_path", containerPath).Msg("adding volume mount") | |||
| 182 | - | } | |||
| 183 | - | } | |||
| 184 | - | if config.ReadOnlyVolumes != nil { | |||
| 185 | - | for containerPath, volumeName := range config.ReadOnlyVolumes { | |||
| 186 | - | bind := fmt.Sprintf("%s:%s:ro", volumeName, containerPath) | |||
| 187 | - | binds = append(binds, bind) | |||
| 188 | - | log.Debug().Str("volume", volumeName).Str("mount_path", containerPath).Msg("adding read-only volume mount") | |||
| 189 | - | } | |||
| 190 | } | 147 | } | ||
| 148 | + | binds := buildVolumeBinds(config, log) | |||
| 191 | 149 | ||||
| 192 | // Create container configuration | 150 | // Create container configuration | ||
| 193 | containerConfig := &container.Config{ | 151 | containerConfig := &container.Config{ | ||
| @@ -261,6 +219,59 @@ func (r *Runtime) CreateContainer(ctx context.Context, config *domain.ContainerC | |||||
| 261 | return r.InspectContainer(ctx, resp.ID) | 219 | return r.InspectContainer(ctx, resp.ID) | ||
| 262 | } | 220 | } | ||
| 263 | 221 | ||||
| 222 | + | func buildPortBindings(config *domain.ContainerConfig) (nat.PortSet, nat.PortMap, error) { | |||
| 223 | + | exposedPorts := make(nat.PortSet) | |||
| 224 | + | portBindings := make(nat.PortMap) | |||
| 225 | + | addLegacyPortBindings(config.Ports, exposedPorts, portBindings) | |||
| 226 | + | if err := addExplicitPortBindings(config.PortPublishes, exposedPorts, portBindings); err != nil { | |||
| 227 | + | return nil, nil, err | |||
| 228 | + | } | |||
| 229 | + | return exposedPorts, portBindings, nil | |||
| 230 | + | } | |||
| 231 | + | ||||
| 232 | + | func addLegacyPortBindings(ports []int, exposedPorts nat.PortSet, portBindings nat.PortMap) { | |||
| 233 | + | for _, port := range ports { | |||
| 234 | + | containerPort := nat.Port(fmt.Sprintf("%d/tcp", port)) | |||
| 235 | + | exposedPorts[containerPort] = struct{}{} | |||
| 236 | + | ||||
| 237 | + | // Bind to random available port on localhost only. | |||
| 238 | + | // SECURITY: Using 127.0.0.1 prevents direct access from the network, | |||
| 239 | + | // forcing all traffic through Gordon's reverse proxy where auth, | |||
| 240 | + | // rate limiting, and security headers are applied. | |||
| 241 | + | portBindings[containerPort] = []nat.PortBinding{{HostIP: "127.0.0.1", HostPort: "0"}} | |||
| 242 | + | } | |||
| 243 | + | } | |||
| 244 | + | ||||
| 245 | + | func addExplicitPortBindings(publishes []domain.ContainerPortPublish, exposedPorts nat.PortSet, portBindings nat.PortMap) error { | |||
| 246 | + | for _, publish := range publishes { | |||
| 247 | + | if publish.Protocol != domain.NetworkProtocolTCP && publish.Protocol != domain.NetworkProtocolUDP { | |||
| 248 | + | return fmt.Errorf("unsupported container port publish protocol %q", publish.Protocol) | |||
| 249 | + | } | |||
| 250 | + | containerPort := nat.Port(fmt.Sprintf("%d/%s", publish.ContainerPort, publish.Protocol)) | |||
| 251 | + | exposedPorts[containerPort] = struct{}{} | |||
| 252 | + | portBindings[containerPort] = append(portBindings[containerPort], nat.PortBinding{ | |||
| 253 | + | HostIP: publish.HostIP, | |||
| 254 | + | HostPort: strconv.Itoa(publish.HostPort), | |||
| 255 | + | }) | |||
| 256 | + | } | |||
| 257 | + | return nil | |||
| 258 | + | } | |||
| 259 | + | ||||
| 260 | + | func buildVolumeBinds(config *domain.ContainerConfig, log zerowrap.Logger) []string { | |||
| 261 | + | binds := make([]string, 0, len(config.Volumes)+len(config.ReadOnlyVolumes)) | |||
| 262 | + | for containerPath, volumeName := range config.Volumes { | |||
| 263 | + | bind := fmt.Sprintf("%s:%s", volumeName, containerPath) | |||
| 264 | + | binds = append(binds, bind) | |||
| 265 | + | log.Debug().Str("volume", volumeName).Str("mount_path", containerPath).Msg("adding volume mount") | |||
| 266 | + | } | |||
| 267 | + | for containerPath, volumeName := range config.ReadOnlyVolumes { | |||
| 268 | + | bind := fmt.Sprintf("%s:%s:ro", volumeName, containerPath) | |||
| 269 | + | binds = append(binds, bind) | |||
| 270 | + | log.Debug().Str("volume", volumeName).Str("mount_path", containerPath).Msg("adding read-only volume mount") | |||
| 271 | + | } | |||
| 272 | + | return binds | |||
| 273 | + | } | |||
| 274 | + | ||||
| 264 | // StartContainer starts a container. | 275 | // StartContainer starts a container. | ||
| 265 | func (r *Runtime) StartContainer(ctx context.Context, containerID string) error { | 276 | func (r *Runtime) StartContainer(ctx context.Context, containerID string) error { | ||
| 266 | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | 277 | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | ||
M internal/domain/traffic.go +10 -10
| @@ -196,17 +196,9 @@ func ParseTrafficServiceRef(value string) (TrafficServiceRef, error) { | |||
| 196 | 196 | case TrafficServiceRefExternalRoute: | |
| 197 | 197 | return TrafficServiceRef{Kind: TrafficServiceRefExternalRoute, Domain: rest}, nil | |
| 198 | 198 | case TrafficServiceRefNetworkService: | |
| 199 | - | name, portName, ok := strings.Cut(rest, ":") | |
| 200 | - | if !ok || name == "" || portName == "" || strings.Contains(portName, ":") { | |
| 201 | - | return TrafficServiceRef{}, fmt.Errorf("invalid network service ref %q", value) | |
| 202 | - | } | |
| 203 | - | return TrafficServiceRef{Kind: TrafficServiceRefNetworkService, Name: name, PortName: portName}, nil | |
| 199 | + | return parseNamedPortServiceRef(value, rest, TrafficServiceRefNetworkService, "network service") | |
| 204 | 200 | case TrafficServiceRefService: | |
| 205 | - | name, portName, ok := strings.Cut(rest, ":") | |
| 206 | - | if !ok || name == "" || portName == "" || strings.Contains(portName, ":") { | |
| 207 | - | return TrafficServiceRef{}, fmt.Errorf("invalid service ref %q", value) | |
| 208 | - | } | |
| 209 | - | return TrafficServiceRef{Kind: TrafficServiceRefService, Name: name, PortName: portName}, nil | |
| 201 | + | return parseNamedPortServiceRef(value, rest, TrafficServiceRefService, "service") | |
| 210 | 202 | case TrafficServiceRefStatic: | |
| 211 | 203 | return TrafficServiceRef{Kind: TrafficServiceRefStatic, Name: rest, Reserved: true}, nil | |
| 212 | 204 | default: | |
| @@ -214,6 +206,14 @@ func ParseTrafficServiceRef(value string) (TrafficServiceRef, error) { | |||
| 214 | 206 | } | |
| 215 | 207 | } | |
| 216 | 208 | ||
| 209 | + | func parseNamedPortServiceRef(value, rest string, kind TrafficServiceRefKind, label string) (TrafficServiceRef, error) { | |
| 210 | + | name, portName, ok := strings.Cut(rest, ":") | |
| 211 | + | if !ok || name == "" || portName == "" || strings.Contains(portName, ":") { | |
| 212 | + | return TrafficServiceRef{}, fmt.Errorf("invalid %s ref %q", label, value) | |
| 213 | + | } | |
| 214 | + | return TrafficServiceRef{Kind: kind, Name: name, PortName: portName}, nil | |
| 215 | + | } | |
| 216 | + | ||
| 217 | 217 | func (g TrafficGraph) Validate() error { | |
| 218 | 218 | if err := validateTrafficOptions(g.Options); err != nil { | |
| 219 | 219 | return err | |
| @@ -196,17 +196,9 @@ func ParseTrafficServiceRef(value string) (TrafficServiceRef, error) { | |||||
| 196 | case TrafficServiceRefExternalRoute: | 196 | case TrafficServiceRefExternalRoute: | ||
| 197 | return TrafficServiceRef{Kind: TrafficServiceRefExternalRoute, Domain: rest}, nil | 197 | return TrafficServiceRef{Kind: TrafficServiceRefExternalRoute, Domain: rest}, nil | ||
| 198 | case TrafficServiceRefNetworkService: | 198 | case TrafficServiceRefNetworkService: | ||
| 199 | - | name, portName, ok := strings.Cut(rest, ":") | 199 | + | return parseNamedPortServiceRef(value, rest, TrafficServiceRefNetworkService, "network service") |
| 200 | - | if !ok || name == "" || portName == "" || strings.Contains(portName, ":") { | |||
| 201 | - | return TrafficServiceRef{}, fmt.Errorf("invalid network service ref %q", value) | |||
| 202 | - | } | |||
| 203 | - | return TrafficServiceRef{Kind: TrafficServiceRefNetworkService, Name: name, PortName: portName}, nil | |||
| 204 | case TrafficServiceRefService: | 200 | case TrafficServiceRefService: | ||
| 205 | - | name, portName, ok := strings.Cut(rest, ":") | 201 | + | return parseNamedPortServiceRef(value, rest, TrafficServiceRefService, "service") |
| 206 | - | if !ok || name == "" || portName == "" || strings.Contains(portName, ":") { | |||
| 207 | - | return TrafficServiceRef{}, fmt.Errorf("invalid service ref %q", value) | |||
| 208 | - | } | |||
| 209 | - | return TrafficServiceRef{Kind: TrafficServiceRefService, Name: name, PortName: portName}, nil | |||
| 210 | case TrafficServiceRefStatic: | 202 | case TrafficServiceRefStatic: | ||
| 211 | return TrafficServiceRef{Kind: TrafficServiceRefStatic, Name: rest, Reserved: true}, nil | 203 | return TrafficServiceRef{Kind: TrafficServiceRefStatic, Name: rest, Reserved: true}, nil | ||
| 212 | default: | 204 | default: | ||
| @@ -214,6 +206,14 @@ func ParseTrafficServiceRef(value string) (TrafficServiceRef, error) { | |||||
| 214 | } | 206 | } | ||
| 215 | } | 207 | } | ||
| 216 | 208 | ||||
| 209 | + | func parseNamedPortServiceRef(value, rest string, kind TrafficServiceRefKind, label string) (TrafficServiceRef, error) { | |||
| 210 | + | name, portName, ok := strings.Cut(rest, ":") | |||
| 211 | + | if !ok || name == "" || portName == "" || strings.Contains(portName, ":") { | |||
| 212 | + | return TrafficServiceRef{}, fmt.Errorf("invalid %s ref %q", label, value) | |||
| 213 | + | } | |||
| 214 | + | return TrafficServiceRef{Kind: kind, Name: name, PortName: portName}, nil | |||
| 215 | + | } | |||
| 216 | + | ||||
| 217 | func (g TrafficGraph) Validate() error { | 217 | func (g TrafficGraph) Validate() error { | ||
| 218 | if err := validateTrafficOptions(g.Options); err != nil { | 218 | if err := validateTrafficOptions(g.Options); err != nil { | ||
| 219 | return err | 219 | return err | ||
M internal/usecase/services/service.go +1 -4
| @@ -6,7 +6,6 @@ import ( | |||
| 6 | 6 | "crypto/sha256" | |
| 7 | 7 | "encoding/hex" | |
| 8 | 8 | "encoding/json" | |
| 9 | - | "errors" | |
| 10 | 9 | "fmt" | |
| 11 | 10 | "io" | |
| 12 | 11 | "maps" | |
| @@ -506,9 +505,7 @@ func (s *Service) waitLogReadiness(ctx context.Context, containerID string, svc | |||
| 506 | 505 | if err == nil && found { | |
| 507 | 506 | return nil | |
| 508 | 507 | } | |
| 509 | - | if err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) { | |
| 510 | - | // Continue polling until timeout; many services create the log after start. | |
| 511 | - | } | |
| 508 | + | // Continue polling until timeout; many services create the log after start. | |
| 512 | 509 | select { | |
| 513 | 510 | case <-ctx.Done(): | |
| 514 | 511 | return fmt.Errorf("standalone service %q log readiness timed out: %w", svc.Name, ctx.Err()) | |
| @@ -6,7 +6,6 @@ import ( | |||||
| 6 | "crypto/sha256" | 6 | "crypto/sha256" | ||
| 7 | "encoding/hex" | 7 | "encoding/hex" | ||
| 8 | "encoding/json" | 8 | "encoding/json" | ||
| 9 | - | "errors" | |||
| 10 | "fmt" | 9 | "fmt" | ||
| 11 | "io" | 10 | "io" | ||
| 12 | "maps" | 11 | "maps" | ||
| @@ -506,9 +505,7 @@ func (s *Service) waitLogReadiness(ctx context.Context, containerID string, svc | |||||
| 506 | if err == nil && found { | 505 | if err == nil && found { | ||
| 507 | return nil | 506 | return nil | ||
| 508 | } | 507 | } | ||
| 509 | - | if err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) { | 508 | + | // Continue polling until timeout; many services create the log after start. |
| 510 | - | // Continue polling until timeout; many services create the log after start. | |||
| 511 | - | } | |||
| 512 | select { | 509 | select { | ||
| 513 | case <-ctx.Done(): | 510 | case <-ctx.Done(): | ||
| 514 | return fmt.Errorf("standalone service %q log readiness timed out: %w", svc.Name, ctx.Err()) | 511 | return fmt.Errorf("standalone service %q log readiness timed out: %w", svc.Name, ctx.Err()) | ||