From 488d04184da878384619a4e95f62e8c8098e96a4 Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Thu, 6 Nov 2025 12:32:04 +0000 Subject: [PATCH 1/4] Route backend jobs to use correct image This selects a pod that matches the selector[1] for the appropriate backend service rather than any random backend MediaWiki pod [1] https://kubernetes.io/docs/concepts/services-networking/service/#services-in-kubernetes Bug: T408624 --- app/Jobs/ProcessMediaWikiJobsJob.php | 25 +++++++++++++---- tests/Jobs/ProcessMediaWikiJobsJobTest.php | 31 ++++++++++++++++++---- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/app/Jobs/ProcessMediaWikiJobsJob.php b/app/Jobs/ProcessMediaWikiJobsJob.php index 298c3ee79..37b608d2d 100644 --- a/app/Jobs/ProcessMediaWikiJobsJob.php +++ b/app/Jobs/ProcessMediaWikiJobsJob.php @@ -2,6 +2,7 @@ namespace App\Jobs; +use App\Services\MediaWikiHostResolver; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; @@ -27,14 +28,28 @@ public function uniqueId(): string { return $this->wikiDomain; } - public function handle(Client $kubernetesClient): void { + public function handle(Client $kubernetesClient, MediaWikiHostResolver $resolver): void { + $domain = $resolver->getBackendHostForDomain($this->wikiDomain); + $serviceName = $domain; $kubernetesClient->setNamespace('default'); + $backendService = $kubernetesClient->services()->setLabelSelector([ + 'name' => $serviceName, + ])->first(); + + if ($backendService === null) { + $this->fail( + new \RuntimeException( + 'Unable to find a matching MediaWiki backend service in the cluster' + ) + ); + + return; + } + + $MWPodSelector = $backendService->toArray()['spec']['selector']; $mediawikiPod = $kubernetesClient->pods()->setFieldSelector([ 'status.phase' => 'Running', - ])->setLabelSelector([ - 'app.kubernetes.io/name' => 'mediawiki', - 'app.kubernetes.io/component' => 'app-backend', - ])->first(); + ])->setLabelSelector($MWPodSelector)->first(); if ($mediawikiPod === null) { $this->fail( diff --git a/tests/Jobs/ProcessMediaWikiJobsJobTest.php b/tests/Jobs/ProcessMediaWikiJobsJobTest.php index 967efabe0..c46e87a84 100644 --- a/tests/Jobs/ProcessMediaWikiJobsJobTest.php +++ b/tests/Jobs/ProcessMediaWikiJobsJobTest.php @@ -3,6 +3,7 @@ namespace Tests\Jobs; use App\Jobs\ProcessMediaWikiJobsJob; +use App\Services\MediaWikiHostResolver; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; use GuzzleHttp\Psr7\Response; @@ -16,6 +17,9 @@ class ProcessMediaWikiJobsJobTest extends TestCase { use RefreshDatabase; public function testJobFailOnNoMediaWikiPod() { + + $mockResolver = $this->createMock(MediaWikiHostResolver::class); + $mockJob = $this->createMock(Job::class); $mockJob->expects($this->once())->method('fail'); @@ -24,6 +28,7 @@ public function testJobFailOnNoMediaWikiPod() { $mock = new MockHandler([ new Response(200, [], json_encode(['items' => []])), + new Response(200, [], json_encode(['items' => []])), ]); $handlerStack = HandlerStack::create($mock); @@ -35,10 +40,11 @@ public function testJobFailOnNoMediaWikiPod() { $job->handle(new Client([ 'master' => 'https://kubernetes.default.svc', 'token' => '/var/run/secrets/kubernetes.io/serviceaccount/token', - ], null, $mockGuzzle)); + ], null, $mockGuzzle), $mockResolver); } public function testJobDoesNotFail() { + $mockResolver = $this->createMock(MediaWikiHostResolver::class); $mockJob = $this->createMock(Job::class); $mockJob->expects($this->never())->method('fail'); @@ -46,6 +52,18 @@ public function testJobDoesNotFail() { $job->setJob($mockJob); $mock = new MockHandler([ + new Response(200, [], json_encode(['items' => [ + [ + 'kind' => 'Service', + 'spec' => [ + 'selector' => [ + 'app.kubernetes.io/component' => 'app-backend', + 'app.kubernetes.io/instance' => 'mediawiki-143', + 'app.kubernetes.io/name' => 'mediawiki', + ], + ], + ], + ]])), new Response(200, [], json_encode(['items' => [ [ 'kind' => 'Pod', @@ -75,9 +93,12 @@ public function testJobDoesNotFail() { 'verify' => '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt', ]); - $job->handle(new Client([ - 'master' => 'https://kubernetes.default.svc', - 'token' => '/var/run/secrets/kubernetes.io/serviceaccount/token', - ], null, $mockGuzzle)); + $job->handle( + new Client([ + 'master' => 'https://kubernetes.default.svc', + 'token' => '/var/run/secrets/kubernetes.io/serviceaccount/token', + ], null, $mockGuzzle), + $mockResolver + ); } } From 87937d48b59d4df40b539da15ef12e3685cc0636 Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Wed, 12 Nov 2025 20:23:23 +0000 Subject: [PATCH 2/4] parse out service name from backend host --- app/Jobs/ProcessMediaWikiJobsJob.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Jobs/ProcessMediaWikiJobsJob.php b/app/Jobs/ProcessMediaWikiJobsJob.php index 37b608d2d..b9e8f4403 100644 --- a/app/Jobs/ProcessMediaWikiJobsJob.php +++ b/app/Jobs/ProcessMediaWikiJobsJob.php @@ -29,8 +29,8 @@ public function uniqueId(): string { } public function handle(Client $kubernetesClient, MediaWikiHostResolver $resolver): void { - $domain = $resolver->getBackendHostForDomain($this->wikiDomain); - $serviceName = $domain; + $FQDNOfService = $resolver->getBackendHostForDomain($this->wikiDomain); + $serviceName = explode(".", $FQDNOfService)[0]; $kubernetesClient->setNamespace('default'); $backendService = $kubernetesClient->services()->setLabelSelector([ 'name' => $serviceName, From 35a638ad607f53d010d92a26b34321c44dfd1682 Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Wed, 12 Nov 2025 20:35:07 +0000 Subject: [PATCH 3/4] try getting service using field not label selector --- app/Jobs/ProcessMediaWikiJobsJob.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Jobs/ProcessMediaWikiJobsJob.php b/app/Jobs/ProcessMediaWikiJobsJob.php index b9e8f4403..547a3903b 100644 --- a/app/Jobs/ProcessMediaWikiJobsJob.php +++ b/app/Jobs/ProcessMediaWikiJobsJob.php @@ -32,8 +32,8 @@ public function handle(Client $kubernetesClient, MediaWikiHostResolver $resolver $FQDNOfService = $resolver->getBackendHostForDomain($this->wikiDomain); $serviceName = explode(".", $FQDNOfService)[0]; $kubernetesClient->setNamespace('default'); - $backendService = $kubernetesClient->services()->setLabelSelector([ - 'name' => $serviceName, + $backendService = $kubernetesClient->services()->setFieldSelector([ + 'metadata.name' => $serviceName, ])->first(); if ($backendService === null) { From 2c197da9e574291a909628e20012a05ad4e89751 Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Wed, 12 Nov 2025 20:39:28 +0000 Subject: [PATCH 4/4] fix pint --- app/Jobs/ProcessMediaWikiJobsJob.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Jobs/ProcessMediaWikiJobsJob.php b/app/Jobs/ProcessMediaWikiJobsJob.php index 547a3903b..56e36dd7f 100644 --- a/app/Jobs/ProcessMediaWikiJobsJob.php +++ b/app/Jobs/ProcessMediaWikiJobsJob.php @@ -30,7 +30,7 @@ public function uniqueId(): string { public function handle(Client $kubernetesClient, MediaWikiHostResolver $resolver): void { $FQDNOfService = $resolver->getBackendHostForDomain($this->wikiDomain); - $serviceName = explode(".", $FQDNOfService)[0]; + $serviceName = explode('.', $FQDNOfService)[0]; $kubernetesClient->setNamespace('default'); $backendService = $kubernetesClient->services()->setFieldSelector([ 'metadata.name' => $serviceName,