Skip to content

fix(lb): reject a balancer name Hetzner does not accept - #67

Closed
lexfrei wants to merge 2 commits into
fix/release-without-annotationsfrom
fix/validate-balancer-name
Closed

lexfrei wants to merge 2 commits into
fix/release-without-annotationsfrom
fix/validate-balancer-name

Conversation

@lexfrei

@lexfrei lexfrei commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

A balancer name from the robotlb/balancer annotation now gets checked before robotlb looks a balancer up by that name or creates one. Before this, any value went to the API as is. Hetzner takes 1 to 128 characters matching ^\S(.*\S)?$, per the name schema of POST /load_balancers in its OpenAPI spec. So a name with whitespace at the start or end, a line break, or more than 128 characters failed with 422 on every retry. An empty value was used as the name too, first in the lookup and then in the create.

A bad name fails the reconcile with a new InvalidBalancerName error, and the usual failure path shows it as a warning event on the Service. The message states the rule before the name, since event notes get cut at 1024 bytes. The check sits right after the lookup by the service UID label, not in LoadBalancer::try_from_svc. A service whose balancer already carries that label never uses the name, so it keeps being managed whatever the annotation says, same as before. A release still reads no annotations.

The check follows the regex dialect of the schema, ECMA-262. That decides the rare cases: . stops at U+2028, and U+FEFF counts as whitespace. I can't tell which regex engine Hetzner runs on the server, so an odd character may still be judged differently there.

The second commit drops the unused LB_NODE_IP_LABEL_NAME constant. Nothing in the code, README or Helm chart reads robotlb/node-ip.

Uniqueness is not part of the check. A name already taken in the project is found by the lookup by name, and the service either uses that balancer or gets a warning event.

The README doesn't describe the name rule. It didn't before either, so I left it as is.

Stacked on #64.

Closes #61

@s3rius

s3rius commented Oct 6, 2026

Copy link
Copy Markdown
Member

I'm not sure if we want this change. Since hetzner will reject the request anyway. Also, as part of reconcilation the request will be retried.

And aside from that they can update their name policies, which we will need to support. Generally I don't think it's necessary.

Comment thread src/lb.rs
// ECMA `\s` differs from Rust whitespace in U+FEFF (in) and U+0085 (out).
let not_space = |c: char| c != '\u{feff}' && (!c.is_whitespace() || c == '\u{85}');
let edges_ok = name.starts_with(not_space) && name.ends_with(not_space);
let one_line = !name.contains(['\n', '\r', '\u{2028}', '\u{2029}']);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a convenient method for this:

Suggested change
let one_line = !name.contains(['\n', '\r', '\u{2028}', '\u{2029}']);
let one_line = name.lines().count() == 1;

Also, few side notes:

  • \r can appear without \n, which is not a new line.
  • \u{2028} and \u{2029} should only be interpreted by text editors, most probably it's going to be ignored. But it really depends on the implementation. https://unicode.org/versions/Unicode5.2.0/ch05.pdf
  • We can simplify checks by only allowing ascii encodable strings.

The name from the robotlb/balancer annotation went to Hetzner as is.
Hetzner takes 1 to 128 characters matching ^\S(.*\S)?$, so a name
with whitespace at an edge, a line break or more than 128 characters
failed with 422 on every retry. An empty annotation was taken as the
name too and went into the name lookup before the create.

Check the name before the balancer is looked up by name or created,
and report the rule in a warning event on the service. A service
whose balancer already carries its UID label does not use the name,
so it keeps being managed whatever the annotation says, as before.
A release still reads no annotations.

Signed-off-by: Aleksei Sviridkin <f@lex.la>
Assisted-by: LLM
Nothing reads robotlb/node-ip: neither the code nor the README or the
Helm chart mention it, so the constant only suggests an annotation
that does nothing.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
@lexfrei
lexfrei force-pushed the fix/validate-balancer-name branch from cfbcc9a to 24a9f51 Compare October 6, 2026 09:34
@lexfrei
lexfrei force-pushed the fix/release-without-annotations branch from f9788d9 to 01819e7 Compare October 6, 2026 09:34
@lexfrei

lexfrei commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

@s3rius Closing this. Hetzner already rejects a bad name with an error that says what is wrong, and since #55 the retries back off. A check on our side would mostly be a copy of their rule that can go stale. Same story as the annotation in #47: while building the stack I didn't notice this part could go.

I'll send the unused constant cleanup from this branch as a separate small PR and move #68 off this branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants