fix: enforce Swarm service name length limits#364
Conversation
Docker Swarm rejects service names longer than 63 characters.
`ServiceInstanceName` produces `{databaseID}-{serviceID}-{8charHash}`,
consuming 10 characters of that budget, leaving 53 for the two IDs
combined. Previously there was no guard — oversized IDs passed API
validation, entered the workflow, and failed deep in the orchestrator
with an opaque Docker error.
Two constraints are now enforced at the API validation layer:
- `Identifier` max length tightened from 63 → 36 characters (regex,
Goa design, and error message all updated).
- Cross-field budget check added to `validateServiceSpec`:
`len(databaseID) + len(serviceID)` must be ≤ 53. Applies to both
`create-database` and `update-database`.
New tests: `TestValidateID` covers the 36-char boundary and all
invalid formats; `TestValidateServiceSpec_NameBudget` covers the
combined budget at, below, and above the limit.
PLAT-562
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 50 minutes and 48 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis change tightens identifier length limits from 1–63 to 1–36 characters and threads a Changes
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 0 |
| Duplication | 0 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes. Give us feedback
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
server/internal/api/apiv1/validate_test.go (1)
1882-1911: Nice boundary coverage; consider also asserting the error path is tied toservice_id.The 53/54 boundary assertions are good. Optional: assert the error message is attached to the
service_idfield path (e.g.,assert.ErrorContains(t, err, "service_id:")) so a future refactor that moves the check or changes its path is caught.Proposed tweak
- err = errors.Join(validateServiceSpec(baseSvc(svcID27), nil, false, dbID27, testDBUsers)...) - assert.ErrorContains(t, err, "database ID and service ID combined must not exceed 53 characters (got 54)") + err = errors.Join(validateServiceSpec(baseSvc(svcID27), nil, false, dbID27, testDBUsers)...) + assert.ErrorContains(t, err, "service_id:") + assert.ErrorContains(t, err, "must not exceed 53 characters")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/internal/api/apiv1/validate_test.go` around lines 1882 - 1911, Update TestValidateServiceSpec_NameBudget to assert that the failure for the 54-char combined ID is tied to the service_id path: after calling validateServiceSpec for the dbID27 case (using baseSvc(svcID27) and testDBUsers) capture the joined error (err) and add an assertion like assert.ErrorContains(t, err, "service_id:") so the test ensures the validation error is reported on the service_id field; reference validateServiceSpec and TestValidateServiceSpec_NameBudget when locating where to add this extra assertion.server/internal/api/apiv1/validate.go (1)
313-317: Budget check looks correct; small UX tweak on the error message.The arithmetic is right:
{databaseID}-{serviceID}-{8hash}⇒len(db)+len(svc)+10 ≤ 63⇒len(db)+len(svc) ≤ 53. Two minor suggestions:
- Include the current database ID length in the error so users understand why a short service_id still fails: e.g.
"database ID (%d) + service ID (%d) must be ≤ 53 characters (Docker Swarm service name limit)".- Since
databaseIDis a server-owned value oncreate-database(UUID = 36 chars), users writing a 20-charservice_idwill be rejected with a message that blames only the combined length. Surfacing the db-id length makes it clearer that they have only53 - len(databaseID)characters available.Proposed message tweak
- err := fmt.Errorf("database ID and service ID combined must not exceed 53 characters (got %d)", len(databaseID)+len(string(svc.ServiceID))) + err := fmt.Errorf("database ID (%d chars) and service ID (%d chars) combined must not exceed 53 characters — required by Docker Swarm service name limit", + len(databaseID), len(string(svc.ServiceID)))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/internal/api/apiv1/validate.go` around lines 313 - 317, Update the validation error message emitted in the check that enforces "{databaseID}-{serviceID}-{8charHash}" ≤ 63 (the block using databaseID, svc.ServiceID, serviceIDPath and newValidationError): include the current lengths of databaseID and serviceID and the remaining allowed characters so users understand the constraint (e.g. "database ID (%d) + service ID (%d) must be ≤ 53 characters (Docker Swarm service name limit); you have %d characters left for service_id"), and keep the error constructed via newValidationError(serviceIDPath) as before.server/internal/utils/utils_test.go (1)
73-110: Good boundary coverage; consider adding an explicit 36-char UUID-lookalike mix and lower-bound length-1 is already covered.Nit:
"abcdefghijklmnopqrstuvwxyz1234567890"(36) and the UUID (36) both exercise the max — clear. You may also want one 35-char case and/or a hyphen-ending/starting test that's currently covered by leading/trailing-hyphen invalids.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/internal/utils/utils_test.go` around lines 73 - 110, Add an explicit 35-character positive test case to TestValidateID so the off-by-one lower/upper boundary is exercised separately from the existing 36-char cases: update the valid slice inside TestValidateID to include a 35-char string (e.g., a mix of letters/digits, distinct from the 36-char UUID and the other 36-char sample) and keep the existing invalid leading/trailing-hyphen and 37-char negative cases unchanged; this ensures ValidateID is tested for length==35 as well as the 36-char max.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@api/apiv1/design/common.go`:
- Around line 8-15: The change tightens the Identifier max length to 36 which is
a breaking change for existing databases; revert or avoid enforcing 36 during
updates by restoring g.MaxLength(63) for the shared Identifier definition and
instead enforce the 36-char limit only for new creations (e.g., add a separate
validation path or check in the create handler that validates Identifier ≤ 36),
or modify validateDatabaseUpdate to allow existing database_id values >36 while
still rejecting new/changed IDs >36; update references to Identifier,
validateDatabaseUpdate, and any create/update handlers so create operations
enforce 36 chars but existing database_id values are accepted during
update-database validation.
---
Nitpick comments:
In `@server/internal/api/apiv1/validate_test.go`:
- Around line 1882-1911: Update TestValidateServiceSpec_NameBudget to assert
that the failure for the 54-char combined ID is tied to the service_id path:
after calling validateServiceSpec for the dbID27 case (using baseSvc(svcID27)
and testDBUsers) capture the joined error (err) and add an assertion like
assert.ErrorContains(t, err, "service_id:") so the test ensures the validation
error is reported on the service_id field; reference validateServiceSpec and
TestValidateServiceSpec_NameBudget when locating where to add this extra
assertion.
In `@server/internal/api/apiv1/validate.go`:
- Around line 313-317: Update the validation error message emitted in the check
that enforces "{databaseID}-{serviceID}-{8charHash}" ≤ 63 (the block using
databaseID, svc.ServiceID, serviceIDPath and newValidationError): include the
current lengths of databaseID and serviceID and the remaining allowed characters
so users understand the constraint (e.g. "database ID (%d) + service ID (%d)
must be ≤ 53 characters (Docker Swarm service name limit); you have %d
characters left for service_id"), and keep the error constructed via
newValidationError(serviceIDPath) as before.
In `@server/internal/utils/utils_test.go`:
- Around line 73-110: Add an explicit 35-character positive test case to
TestValidateID so the off-by-one lower/upper boundary is exercised separately
from the existing 36-char cases: update the valid slice inside TestValidateID to
include a 35-char string (e.g., a mix of letters/digits, distinct from the
36-char UUID and the other 36-char sample) and keep the existing invalid
leading/trailing-hyphen and 37-char negative cases unchanged; this ensures
ValidateID is tested for length==35 as well as the 36-char max.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 81cd2824-2822-4b21-bdbc-4dbd766edd42
⛔ Files ignored due to path filters (9)
api/apiv1/gen/control_plane/service.gois excluded by!**/gen/**api/apiv1/gen/http/control_plane/client/cli.gois excluded by!**/gen/**api/apiv1/gen/http/control_plane/client/types.gois excluded by!**/gen/**api/apiv1/gen/http/control_plane/server/encode_decode.gois excluded by!**/gen/**api/apiv1/gen/http/control_plane/server/types.gois excluded by!**/gen/**api/apiv1/gen/http/openapi.jsonis excluded by!**/gen/**api/apiv1/gen/http/openapi.yamlis excluded by!**/gen/**api/apiv1/gen/http/openapi3.jsonis excluded by!**/gen/**api/apiv1/gen/http/openapi3.yamlis excluded by!**/gen/**
📒 Files selected for processing (6)
api/apiv1/design/common.goserver/internal/api/apiv1/convert.goserver/internal/api/apiv1/validate.goserver/internal/api/apiv1/validate_test.goserver/internal/utils/utils.goserver/internal/utils/utils_test.go
3b98b40 to
c410a98
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@server/internal/utils/utils.go`:
- Around line 129-130: Update the validation error text in ErrInvalidIdentifier
to mention digits/numbers as allowed characters to match the regex idPattern;
locate the ErrInvalidIdentifier declaration and change its message to include
"digits" or "numbers" (e.g., "contain only lowercase letters, digits and
hyphens") while keeping the rest of the constraints (1-36 chars, start/end with
letter or number, no consecutive hyphens) unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f19f7d69-caec-4b56-b5d3-6af91c3d991f
⛔ Files ignored due to path filters (9)
api/apiv1/gen/control_plane/service.gois excluded by!**/gen/**api/apiv1/gen/http/control_plane/client/cli.gois excluded by!**/gen/**api/apiv1/gen/http/control_plane/client/types.gois excluded by!**/gen/**api/apiv1/gen/http/control_plane/server/encode_decode.gois excluded by!**/gen/**api/apiv1/gen/http/control_plane/server/types.gois excluded by!**/gen/**api/apiv1/gen/http/openapi.jsonis excluded by!**/gen/**api/apiv1/gen/http/openapi.yamlis excluded by!**/gen/**api/apiv1/gen/http/openapi3.jsonis excluded by!**/gen/**api/apiv1/gen/http/openapi3.yamlis excluded by!**/gen/**
📒 Files selected for processing (6)
api/apiv1/design/common.goserver/internal/api/apiv1/convert.goserver/internal/api/apiv1/validate.goserver/internal/api/apiv1/validate_test.goserver/internal/utils/utils.goserver/internal/utils/utils_test.go
✅ Files skipped from review due to trivial changes (2)
- api/apiv1/design/common.go
- server/internal/utils/utils_test.go
Docker Swarm rejects service names longer than 63 characters.
ServiceInstanceNameproduces{databaseID}-{serviceID}-{8charHash},consuming 10 characters of that budget, leaving 53 for the two IDs
combined. Previously there was no guard — oversized IDs passed API
validation, entered the workflow, and failed deep in the orchestrator
with an opaque Docker error.
Two constraints are now enforced at the API validation layer:
Identifiermax length tightened from 63 → 36 characters (regex,Goa design, and error message all updated).
validateServiceSpec:len(databaseID) + len(serviceID)must be ≤ 53. Applies to bothcreate-databaseandupdate-database.New tests:
TestValidateIDcovers the 36-char boundary and allinvalid formats;
TestValidateServiceSpec_NameBudgetcovers thecombined budget at, below, and above the limit.
PLAT-562
Summary
Docker Swarm enforces a 63-character limit on service names. Because
service names are generated as
{databaseID}-{serviceID}-{8charHash},the combined budget for the two IDs is 53 characters. This PR adds
validation that catches oversized IDs at the API layer before any
workflow starts, replacing a silent orchestrator failure with a clear
400 error.
Changes
Identifiermax length tightened from 63 → 36 characters — regexpattern, Goa design type, and error message all updated; Goa code
regenerated
validateServiceSpecnow receivesdatabaseIDand rejects requestswhere
len(databaseID) + len(serviceID) > 53validateDatabaseSpecandvalidateDatabaseUpdateupdated to passthe database ID through to
validateServiceSpecTesting