bhouse-nexthop commented on PR #14131:
URL: https://github.com/apache/cloudstack/pull/14131#issuecomment-6733519502

   ### Test report: per-rule keepalive and timeouts, and the HAProxy fixes, on 
running virtual routers
   
   **Environment**
   - A CloudStack 24.0 (main) build with this PR applied, alongside some of my 
other open PRs. This PR targets main, so no backport caveat applies.
   - Three KVM hosts on Ubuntu 24.04, nested virtualisation; Ceph RBD primary 
storage.
   - System VM template 4.22.0 (Debian 12, HAProxy 2.6.12). The routers were 
created on this build. Their `configure.py` and 
`/root/health_checks/haproxy_check.py` match the build's.
   - Two isolated networks:
     - N1 on `DefaultIsolatedNetworkOfferingWithSourceNatService`, with 
backends B1 and B2 (Ubuntu 24.04, nginx). nginx logs `X-Forwarded-For`, 
`$connection` and `$connection_requests`, and sets `JSESSIONID=sess-<name>`.
     - N2 on a copy of that offering with `keepaliveenabled=true`, with backend 
B3.
   - I moved N2's router onto N1's KVM host, so that one agent JVM generated 
both routers' configs.
   - The client was a host on the public network.
   
   **1. An http rule with all three settings**
   
   `createLoadBalancerRule ... publicport=80 keepalive=true idletimeout=600000 
keepalivetimeout=15000`, with B1 and B2:
   ```
   listen <P1>-80
        bind <P1>:80
        mode http
        option http-keep-alive
        timeout http-keep-alive 15000
        timeout client     600000
        timeout server     600000
        balance roundrobin
        server <P1>-80_0 <B2>:80 check
        server <P1>-80_1 <B1>:80 check
   ```
   - `haproxy -c` reports the file valid.
   - `listLoadBalancerRules` returns `keepalive=true, idletimeout=600000, 
keepalivetimeout=15000`.
   - `curl -v` with four URLs on one command line: `Connected`, then `Re-using 
existing connection` three times. The answers alternate B2, B1, B2, B1.
   - nginx on each backend shows the client address in XFF, and two requests on 
one backend connection: `xff="<client>" conn=2 reqs=1` and then `conn=2 reqs=2`.
   
   So keep-alive and `X-Forwarded-For` work together on one rule. They could 
not before.
   
   **2. A tcp rule with only an idle timeout** (`publicport=3306 
idletimeout=120000`)
   ```
   listen <P1>-3306
        bind <P1>:3306
        timeout client     120000
        timeout server     120000
        balance roundrobin
        server <P1>-3306_0 <B1>:3306 check
   ```
   
   **3. Rules that set nothing** render as before:
   - Port 80: `mode http`, `option httpclose`, `balance`, `server` lines. The 
client gets `connection: close` on every response.
   - Port 8080: `bind`, `balance`, `server` only.
   - The `defaults` section keeps `option httpclose` and `timeout client/server 
50000`.
   
   **4. `no option forceclose` is gone (keepalive with LbCookie stickiness)**
   - On rule 1 (per-rule keepalive), and on an N2 rule (keepalive from the 
offering, which is the old trigger), I added an `LbCookie` policy.
   - Both render `mode http`, `option http-keep-alive`, the `cookie ... insert` 
line and the per-server cookies.
   - `grep -c forceclose` is 0, and `haproxy -c` passes.
   - haproxy is listening, and the haproxy cookie pins the client: four 
requests with it all go to the same backend.
   - For comparison, the same file with `no option forceclose` in place of 
`option http-keep-alive` fails `haproxy -c`:
     ```
     [ALERT] config : parsing [/tmp/fc.cfg:36]: option 'forceclose' is not 
supported any more since HAProxy 2.0 ...
     [ALERT] config : Fatal errors found in configuration.
     ```
   
   **5. AppCookie renders as a stick table.**
   
   Since #13315 (merged 10-21; this PR's branch does not include it yet), 
`createLBStickinessPolicy methodname=AppCookie` is refused for Virtual Router 
networks:
   ```
   AppCookie based stickiness (HAProxy 'appsession') is not supported on a 
Virtual Router, as this directive was removed in HAProxy 1.6
   ```
   So the generator path is reached only by an AppCookie policy stored before 
an upgrade. To test that case, I inserted one 
(`cookie-name=JSESSIONID&length=52&holdtime=3h`) into 
`load_balancer_stickiness_policies` for a port 80 rule. Then I regenerated the 
config by editing another rule on the network.
   ```
        stick-table type string len 52 size 10k expire 3h
        stick store-response res.cook(JSESSIONID)
        stick match req.cook(JSESSIONID)
   ```
   - There is no `appsession`, and `haproxy -c` passes.
   - Six requests with `Cookie: JSESSIONID=sess-B1` all go to B1, and six with 
`sess-B2` all go to B2. Without a cookie they alternate.
   - `show table` holds both keys.
   - The same file with `appsession JSESSIONID len 52 timeout 3h` fails 
`haproxy -c` with `'appsession' is not supported anymore since HAProxy 1.6`.
   
   The PR description says AppCookie "is reachable from the API". That is no 
longer true on main because of #13315. The fix still matters for policies 
stored before that change.
   
   **6. No leak between configs from one agent**
   - I regenerated N2's config (keepalive offering: its `defaults` has `no 
option httpclose`), and then N1's config, on the same agent.
   - N1's `defaults` still has `option httpclose`, and its port 80 rule without 
settings still has `option httpclose`.
   - The 600000 and 120000 rule timeouts never reach either `defaults`, which 
stays at 50000.
   
   **7. Health check**
   - `getRouterHealthCheckResults performfreshchecks=true`: `haproxy_check.py` 
reports `All checks pass`.
   - I changed `timeout client 45000` to `45001` by hand in the rule's listen 
section. The next fresh check failed with:
     ```
     Expected 'timeout client 45000' in listen <P1>-80 but found 45001
     ```
   - A real re-apply (an algorithm change on another rule) rewrote the file, 
and the check passed again.
   
   **8. Back to defaults**
   - `updateLoadBalancerRule cleanupconnectionsettings=true` returns success, 
and the three fields come back empty (retested on the fixed build, see below). 
The rule renders `mode http` / `option httpclose`, with the LbCookie lines kept.
   - With `cleanupconnectionsettings=true keepalive=true`, the update returns 
success and only keepalive remains: `option http-keep-alive`, no timeouts. 
Connection reuse works again.
   
   **9. Validation**
   `idletimeout=-1` is refused on create (`idletimeout must be 0 or greater, 
got [-1]. 0 means no timeout.`) and on update. `keepalivetimeout=-1` is refused 
on update with the same wording.
   
   **10. Rollback when the router update fails**
   I reran this step on the build with both update fixes, so that only an 
unreachable router can make the update fail.
   - `updateLoadBalancerRule idletimeout=30000 keepalivetimeout=20000` returned 
success.
   - I stopped the agent of the router's host, so the update could not reach 
the router.
   - `updateLoadBalancerRule idletimeout=45000 keepalivetimeout=25000` then 
failed with `Failed to update load balancer rule`.
   - `listLoadBalancerRules` and `firewall_rule_details` still held the old 
30000 / 20000, and the management server logged `LB Rollback rule`.
   - With the agent back, the same update returned success, and the router 
shows `timeout client/server 45000` and `timeout http-keep-alive 25000`.
   
   The test plan's first idea for this step was `chattr +i 
/etc/haproxy/haproxy.cfg` on the router. That does not fail the update: the 
router logs `Could not copy /etc/haproxy/haproxy.cfg.new to 
/etc/haproxy/haproxy.cfg`, reloads the old file and reports success. So the 
management server has nothing to roll back. That is router-side behaviour, 
unchanged by this PR.
   
   **Result:** pass.
   - Every generated config passes `haproxy -c` and is live.
   - Keep-alive and XFF work together.
   - Rules without settings render as before.
   - There is no leak between configs.
   - The health check notices drift.
   - Reset to defaults and rollback behave as described.
   
   **Two update bugs the lab found, both fixed in the PR**
   
   The first run found two ways `updateLoadBalancerRule` returned job result 
530 although the change had been applied:
   - A rule created through the API without `protocol` has a NULL 
`lb_protocol`, and the SSL check after the apply threw an NPE on it. The PR now 
compares null-safely.
   - An update that changes only connection settings failed with `Failed to 
update load balancer rule`. These settings live in `firewall_rule_details`, so 
no column of the rule changed and the DAO update reported no row updated. The 
PR now treats a settings change as a successful update.
   
   I retested on a build with both fixes, on two rules: one created without 
`protocol`, and one with `protocol=tcp` as the UI sends it. Every update below 
returned success. `listLoadBalancerRules` showed the new values, the router's 
listen section had them, and `haproxy -c` passed after each.
   
   | Rule | Update | Router listen section after it |
   |---|---|---|
   | no protocol | `keepalive=true idletimeout=600000 keepalivetimeout=15000` | 
`option http-keep-alive`, `timeout http-keep-alive 15000`, `timeout 
client/server 600000` |
   | no protocol | `idletimeout=120000` | `timeout client 120000`, keep-alive 
timeout kept at 15000 |
   | no protocol | `keepalivetimeout=20000` | `timeout http-keep-alive 20000` |
   | no protocol | `algorithm=leastconn` | `balance leastconn`, timeouts kept |
   | no protocol | `cleanupconnectionsettings=true` | no timeouts, no 
`http-keep-alive` |
   | no protocol | `cleanupconnectionsettings=true keepalive=true` | `option 
http-keep-alive` only |
   | no protocol | `idletimeout=45000` | `timeout client 45000`, keep-alive 
kept |
   | `protocol=tcp` | `idletimeout=90000` | `timeout client/server 90000` |
   | `protocol=tcp` | `keepalive=true` | timeouts kept |
   | `protocol=tcp` | `keepalivetimeout=12000` | timeouts kept |
   | `protocol=tcp` | `cleanupconnectionsettings=true` | no timeouts |
   | `protocol=tcp` | `algorithm=source idletimeout=70000` | `balance source`, 
`timeout client/server 70000` |
   
   - The management server log has no `getLbProtocol` / `NullPointerException` 
line over the retest.
   - Requests through the rule still alternate between both backends.
   - An update that names only the rule id and changes nothing still fails with 
`Failed to update load balancer rule`, as on stock. That is the only `Failed to 
update` line in the log.
   
   **Not covered**
   - The UI edit form (blank = "Use default", clearing a field sends 
`cleanupconnectionsettings`). I had no browser on the lab network.
   - SSL offload rules, and the `tcp-proxy` protocol.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to