Skip to content

Commit 7ccd574

Browse files
committed
ci: run CI and e2e on ubuntu-26.04, with infra-e2e importing kind images per platform
Docker 29 (ubuntu-26.04 runners) uses the containerd image store, where a pulled multi-platform image holds only the host platform; importing every platform into kind failed for the OAP image ("content digest ... not found"). Saved for one platform, the archive also lists the image's referrers, such as attestations, at its top level, and the containerd 1.7 kindest/node:v1.28.15 these cases pin imports each one as an image, which failed for the Rover image ("mismatched image rootfs and manifest layers"). infra-e2e now saves only the node platform and leaves the referrers out of the archive (apache/skywalking-infra-e2e#150, #151), so the e2e cases leave the ubuntu-24.04 pin #205 added. The CI and e2e jobs name ubuntu-26.04 rather than ubuntu-latest: ubuntu-latest jobs sat queued with no runner while jobs naming ubuntu-24.04 or ubuntu-26.04 started within seconds. The istio mesh fixture is fully known, so its specs now require all of it and accept only `UNKNOWN` beyond it. The mesh spec asserts the service picker lists exactly the five workloads (bookinfo's four and the ingress gateway) plus, optionally, `UNKNOWN`; the topology spec requires a node for each workload by name; and the roster readiness check waits until Horizon lists all five. MESH_PEERS, which left `ratings` out, is gone. `UNKNOWN` is accepted as the service the MESH header opens on (MESH_HEADER_SERVICES, in the mesh and eBPF profiling specs). Since the e2e OAP pin moved past apache/skywalking#14135 (#204), OAP reads the Kubernetes metadata of a new pod address in the background, so bookinfo's first calls count under `UNKNOWN`; in the first minute of traffic that burst can rank first by calls per minute, and the header then picks it. It failed so on main at ed6171d (ubuntu-24.04) as well as here.
1 parent ed6171d commit 7ccd574

7 files changed

Lines changed: 45 additions & 46 deletions

File tree

‎.github/workflows/ci.yaml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
# container action that would otherwise sit behind the toolchain setup the
3333
# build job spends its first half-minute on.
3434
name: License header
35-
runs-on: ubuntu-latest
35+
runs-on: ubuntu-26.04
3636
timeout-minutes: 10
3737
steps:
3838
- uses: actions/checkout@v6
@@ -48,7 +48,7 @@ jobs:
4848
# the setup cost eight times over. Steps run cheapest-first so a lint or
4949
# type error reports before the package build starts.
5050
name: Build and check
51-
runs-on: ubuntu-latest
51+
runs-on: ubuntu-26.04
5252
timeout-minutes: 30
5353
steps:
5454
- uses: actions/checkout@v6
@@ -252,7 +252,7 @@ jobs:
252252
# `test/e2e/script/env` pins, on an ephemeral port, and stops it after. No
253253
# service container to declare and no port to collide with.
254254
name: BanyanDB client IT
255-
runs-on: ubuntu-latest
255+
runs-on: ubuntu-26.04
256256
timeout-minutes: 20
257257
steps:
258258
- uses: actions/checkout@v6
@@ -278,7 +278,7 @@ jobs:
278278
if: always() && !cancelled()
279279
name: Required
280280
needs: [license-header, build-and-check, banyandb-client-it]
281-
runs-on: ubuntu-latest
281+
runs-on: ubuntu-26.04
282282
timeout-minutes: 5
283283
steps:
284284
- name: Check upstream jobs

‎.github/workflows/e2e.yaml‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,7 @@ concurrency:
225225
jobs:
226226
e2e:
227227
name: ${{ matrix.case.name }}
228-
# Not ubuntu-latest: on ubuntu-26.04, kind cannot load the multi-platform
229-
# OAP image into its node (`ctr images import --all-platforms`: content
230-
# digest not found), so the kind cases fail before any test runs.
231-
runs-on: ubuntu-24.04
228+
runs-on: ubuntu-26.04
232229
timeout-minutes: 45
233230
strategy:
234231
# One case failing must not cancel the others: when several go red at
@@ -292,7 +289,7 @@ jobs:
292289
run: make -C test/e2e images
293290

294291
- name: ${{ matrix.case.name }}
295-
uses: apache/skywalking-infra-e2e@68bbdec303cf2d3df9f2353f970f91938b52f674
292+
uses: apache/skywalking-infra-e2e@56ac8befeb5f910f2dc9339e65d07cd18953f90e
296293
with:
297294
e2e-file: $GITHUB_WORKSPACE/${{ matrix.case.config }}
298295
# Container logs land here. On a red run they answer the first
@@ -330,7 +327,7 @@ jobs:
330327
name: E2E Required
331328
if: always() && !cancelled()
332329
needs: [e2e]
333-
runs-on: ubuntu-latest
330+
runs-on: ubuntu-26.04
334331
steps:
335332
- name: Assert every case passed
336333
run: |

‎test/e2e/cases/istio/expected/horizon-mesh-roster.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616
# under the License.
1717

1818
reachable: true
19-
meshServices: 1
19+
meshServices: 5

‎test/e2e/cases/istio/verify-mesh.yaml‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,13 @@ cases:
4343
)
4444
expected: expected/metrics-has-value.yml
4545
46-
# 3. READINESS: Horizon can see the mesh roster too, absorbing the catalog TTL
47-
# before the browser project pays for it.
46+
# 3. READINESS: Horizon's mesh roster holds every workload the fixture
47+
# produces (MESH_SERVICES in the specs), which the mesh spec asserts
48+
# whole, absorbing the catalog TTL before the browser project pays for it.
4849
- query: |
4950
bash test/e2e/script/prepare/horizon-get.sh \
5051
"http://${service_horizon_host}:${service_horizon_8081}" /api/layer/mesh/services \
51-
| yq -P '{"reachable": .reachable, "meshServices": [.services[] | select(.name == "productpage")] | length}'
52+
| yq -P '{"reachable": .reachable, "meshServices": [.services[] | select(.name == "productpage" or .name == "reviews" or .name == "details" or .name == "ratings" or .name == "istio-ingressgateway")] | length}'
5253
expected: expected/horizon-mesh-roster.yml
5354
5455
# 3b. READINESS: the mesh topology has EDGES. The service roster above proves

‎test/e2e/playwright/specs/fixture.ts‎

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -124,29 +124,27 @@ export const WINDOW_MINUTES = 30;
124124
export const MESH_LAYER = 'mesh';
125125

126126
/**
127-
* bookinfo's workloads as MESH services. Matched as a SET, never pinned to
128-
* one: which service the layer header auto-resolves follows OAP's ordering,
129-
* so naming a single one fails on a healthy mesh whenever traffic shifts.
130-
*
131-
* `ratings` is deliberately absent. Only reviews-v2 and v3 call it, so
132-
* whether it has appeared yet depends on which reviews version the traffic
133-
* happened to reach — true of a healthy mesh, and not something to assert.
127+
* EVERY service this fixture's mesh produces: all four bookinfo workloads
128+
* plus the ingress gateway bookinfo is reached through, which ALS reports
129+
* like any other mesh workload. The mesh is fully known, so the mesh spec
130+
* asserts the roster against this whole and the topology must draw every one;
131+
* the istio readiness check waits until Horizon lists all of them.
134132
*/
135-
export const MESH_PEERS = ['productpage', 'reviews', 'details'];
133+
export const MESH_SERVICES = ['productpage', 'reviews', 'details', 'ratings', 'istio-ingressgateway'];
136134

137135
/**
138-
* EVERY service this fixture's mesh can produce: all four bookinfo workloads
139-
* plus the ingress gateway bookinfo is reached through, which ALS reports
140-
* like any other mesh workload.
136+
* The services the MESH layer header may open on: MESH_SERVICES plus OAP's
137+
* `UNKNOWN`. Which one it picks follows OAP's ordering, so it is matched as a
138+
* set. OAP reads the Kubernetes metadata of a new pod address in the
139+
* background rather than waiting for it (apache/skywalking#14135), so
140+
* bookinfo's first calls count under `UNKNOWN`, and in the first minute of
141+
* traffic that burst can rank above every workload.
141142
*
142-
* Distinct from MESH_PEERS on purpose. `ratings` is reached only via
143-
* reviews-v2/v3, so whether it exists in a given window depends on which
144-
* review version the traffic hit — it cannot be REQUIRED to be listed, but it
145-
* can certainly be the one the header resolves to. Which service that is
146-
* follows OAP's ordering and changes between runs, so anything asserting on
147-
* the SELECTED service must accept the full set.
143+
* Only for the header's pick. `UNKNOWN` is the one service outside
144+
* MESH_SERVICES the roster may hold, and a roster missing any workload still
145+
* fails the mesh spec.
148146
*/
149-
export const MESH_SERVICES = [...MESH_PEERS, 'ratings', 'istio-ingressgateway'];
147+
export const MESH_HEADER_SERVICES = [...MESH_SERVICES, 'UNKNOWN'];
150148

151149
/**
152150
* The SAME workload in the MESH_DP layer, which names services

‎test/e2e/playwright/specs/istio-profiling/ebpf.spec.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717

1818
import { test, expect } from '../support/diagnostics.js';
19-
import { MESH_LAYER, MESH_SERVICES } from '../fixture.js';
19+
import { MESH_LAYER, MESH_HEADER_SERVICES } from '../fixture.js';
2020

2121
// Profiling pages, which exist only where rover does.
2222
//
@@ -43,7 +43,7 @@ test('the eBPF profiling page mounts and resolves a service', async ({ page, pag
4343
// ours.
4444
const selected = page.locator('.service-row .svc-name');
4545
await expect(selected).toBeVisible({ timeout: 60_000 });
46-
await expect(selected).toHaveText(new RegExp(`^(${MESH_SERVICES.join('|')})$`));
46+
await expect(selected).toHaveText(new RegExp(`^(${MESH_HEADER_SERVICES.join('|')})$`));
4747

4848
expect(pageErrors, 'an uncaught error during mount blanks the page').toEqual([]);
4949
});

‎test/e2e/playwright/specs/istio/mesh.spec.ts‎

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
import { test, expect } from '../support/diagnostics.js';
1919
import {
2020
MESH_LAYER,
21-
MESH_PEERS,
2221
MESH_SERVICES,
22+
MESH_HEADER_SERVICES,
2323
MESH_DP_LAYER,
2424
MESH_DP_SUFFIX,
2525
} from '../fixture.js';
@@ -41,17 +41,19 @@ test('the mesh layer lists services built from Envoy access logs', async ({ page
4141
// member of the known set instead.
4242
const selected = page.locator('.service-row .svc-name');
4343
await expect(selected).toBeVisible({ timeout: 45_000 });
44-
await expect(selected).toHaveText(new RegExp(`^(${MESH_SERVICES.join('|')})$`));
44+
await expect(selected).toHaveText(new RegExp(`^(${MESH_HEADER_SERVICES.join('|')})$`));
4545

4646
// The roster itself is the real assertion: every one of these exists only
4747
// because OAP analysed Envoy access logs into mesh entities. No agent
48-
// reported any of them.
48+
// reported any of them. The fixture's mesh is fully known, so the roster is
49+
// asserted whole: every workload, and nothing else but `UNKNOWN`.
4950
await page.locator('.service-row button.switch').click();
5051
const picker = page.locator('.picker-table');
5152
await expect(picker).toBeVisible();
52-
for (const peer of MESH_PEERS) {
53-
await expect(picker.locator('tr.row', { hasText: peer }).first()).toBeVisible();
54-
}
53+
const names = picker.locator('tr.row .name-text');
54+
await expect
55+
.poll(async () => (await names.allTextContents()).map((n) => n.trim()).filter((n) => n !== 'UNKNOWN').sort())
56+
.toEqual([...MESH_SERVICES].sort());
5557

5658
expect(pageErrors, 'an uncaught error during mount blanks the page').toEqual([]);
5759
});
@@ -62,12 +64,13 @@ test('the mesh topology draws the bookinfo call graph', async ({ page, pageError
6264
const nodes = page.locator('.sm-node');
6365
await expect(nodes.first()).toBeVisible({ timeout: 60_000 });
6466

65-
// bookinfo is a four-service graph on purpose: with two services a single
66-
// edge drawn twice is indistinguishable from a real topology. Requiring
67-
// more than one peer is what makes this assert a GRAPH.
68-
await expect
69-
.poll(async () => nodes.count(), { timeout: 60_000 })
70-
.toBeGreaterThanOrEqual(MESH_PEERS.length);
67+
// bookinfo is a multi-service graph on purpose: with two services a single
68+
// edge drawn twice is indistinguishable from a real topology. The mesh is
69+
// fully known, so every workload must be drawn, by name — a count would let
70+
// `UNKNOWN` stand in for a missing one.
71+
for (const name of MESH_SERVICES) {
72+
await expect(nodes.filter({ hasText: name }).first()).toBeVisible({ timeout: 60_000 });
73+
}
7174
await expect(page.locator('.sm-edge').first()).toBeVisible({ timeout: 60_000 });
7275

7376
expect(pageErrors).toEqual([]);

0 commit comments

Comments
 (0)