Add Waiting state on the backend and frontend (#839)

* operator: add waiting state
- add pods as related objects
- inspect pod status, set crawl status to 'waiting' if no pods are running

frontend:
- frontend support for 'waiting' state
- show waiting icon from mocks

---------
Co-authored-by: Henry Wilkinson <henry@wilkinson.graphics>
This commit is contained in:
Ilya Kreymer 2023-05-08 17:05:01 -07:00 committed by GitHub
parent 70319594c2
commit 2cae065c46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 56 additions and 9 deletions

View File

@ -32,6 +32,7 @@ from .crawls import (
STS = "StatefulSet.apps/v1" STS = "StatefulSet.apps/v1"
CMAP = "ConfigMap.v1" CMAP = "ConfigMap.v1"
PVC = "PersistentVolumeClaim.v1" PVC = "PersistentVolumeClaim.v1"
POD = "Pod.v1"
DEFAULT_TTL = 30 DEFAULT_TTL = 30
@ -186,9 +187,10 @@ class BtrixOperator(K8sAPI):
crawl_sts = f"crawl-{crawl_id}" crawl_sts = f"crawl-{crawl_id}"
redis_sts = f"redis-{crawl_id}" redis_sts = f"redis-{crawl_id}"
has_crawl_children = STS in data.children and crawl_sts in data.children[STS] has_crawl_children = crawl_sts in data.children[STS]
if has_crawl_children: if has_crawl_children:
status = await self.sync_crawl_state(redis_url, crawl, status) pods = data.related[POD]
status = await self.sync_crawl_state(redis_url, crawl, status, pods)
elif not status.finished: elif not status.finished:
status.state = "starting" status.state = "starting"
@ -220,7 +222,7 @@ class BtrixOperator(K8sAPI):
"spec" "spec"
]["volumeClaimTemplates"] ]["volumeClaimTemplates"]
has_redis_children = STS in data.children and redis_sts in data.children[STS] has_redis_children = redis_sts in data.children[STS]
if has_redis_children: if has_redis_children:
children[2]["spec"]["volumeClaimTemplates"] = data.children[STS][redis_sts][ children[2]["spec"]["volumeClaimTemplates"] = data.children[STS][redis_sts][
"spec" "spec"
@ -251,6 +253,13 @@ class BtrixOperator(K8sAPI):
"resource": "persistentvolumeclaims", "resource": "persistentvolumeclaims",
"labelSelector": {"matchLabels": {"crawl": crawl_id}}, "labelSelector": {"matchLabels": {"crawl": crawl_id}},
}, },
{
"apiVersion": "v1",
"resource": "pods",
"labelSelector": {
"matchLabels": {"crawl": crawl_id, "role": "crawler"}
},
},
] ]
} }
@ -328,7 +337,7 @@ class BtrixOperator(K8sAPI):
except: except:
return None return None
async def sync_crawl_state(self, redis_url, crawl, status): async def sync_crawl_state(self, redis_url, crawl, status, pods):
"""sync crawl state for running crawl""" """sync crawl state for running crawl"""
redis = await self._get_redis(redis_url) redis = await self._get_redis(redis_url)
if not redis: if not redis:
@ -354,7 +363,7 @@ class BtrixOperator(K8sAPI):
status.filesAdded = int(await redis.get("filesAdded") or 0) status.filesAdded = int(await redis.get("filesAdded") or 0)
# update stats and get status # update stats and get status
return await self.update_crawl_state(redis, crawl, status) return await self.update_crawl_state(redis, crawl, status, pods)
# pylint: disable=broad-except # pylint: disable=broad-except
except Exception as exc: except Exception as exc:
@ -362,6 +371,20 @@ class BtrixOperator(K8sAPI):
print(f"Crawl get failed: {exc}, will try again") print(f"Crawl get failed: {exc}, will try again")
return status return status
async def check_if_pods_running(self, pods):
"""check if at least one crawler pod has started"""
try:
for pod in pods.values():
print("Phase", pod["status"]["phase"])
if pod["status"]["phase"] == "Running":
return True
# pylint: disable=bare-except
except:
# assume no valid pod found
pass
return False
async def add_file_to_crawl(self, cc_data, crawl): async def add_file_to_crawl(self, cc_data, crawl):
"""Handle finished CrawlFile to db""" """Handle finished CrawlFile to db"""
@ -386,7 +409,7 @@ class BtrixOperator(K8sAPI):
return True return True
async def update_crawl_state(self, redis, crawl, status): async def update_crawl_state(self, redis, crawl, status, pods):
"""update crawl state and check if crawl is now done""" """update crawl state and check if crawl is now done"""
results = await redis.hvals(f"{crawl.id}:status") results = await redis.hvals(f"{crawl.id}:status")
stats = await get_redis_crawl_stats(redis, crawl.id) stats = await get_redis_crawl_stats(redis, crawl.id)
@ -405,6 +428,15 @@ class BtrixOperator(K8sAPI):
# backwards compatibility with older crawler # backwards compatibility with older crawler
await redis.set("crawl-stop", "1") await redis.set("crawl-stop", "1")
# check if at least one pod started running
# otherwise, mark as 'waiting' and return
if not await self.check_if_pods_running(pods):
if status.state not in ("waiting", "canceled"):
await update_crawl(self.crawls, crawl.id, state="waiting")
status.state = "waiting"
return status
# optimization: don't update db once crawl is already running # optimization: don't update db once crawl is already running
# will set stats at when crawl is finished, otherwise can read # will set stats at when crawl is finished, otherwise can read
# directly from redis # directly from redis

View File

@ -61,7 +61,7 @@ def test_wait_for_complete(admin_auth_headers, default_org_id, admin_crawl_id):
# ensure filename matches specified pattern # ensure filename matches specified pattern
# set in default_crawl_filename_template # set in default_crawl_filename_template
assert re.search('/[\\d]+-testing-[\\w-]+\.wacz', data["resources"][0]["path"]) assert re.search("/[\\d]+-testing-[\\w-]+\\.wacz", data["resources"][0]["path"])
assert data["tags"] == ["wr-test-1", "wr-test-2"] assert data["tags"] == ["wr-test-1", "wr-test-2"]

View File

@ -76,6 +76,17 @@ export class CrawlStatus extends LitElement {
break; break;
} }
case "waiting": {
icon = html`<sl-icon
name="hourglass-split"
class="animatePulse"
slot="prefix"
style="color: var(--sl-color-purple-600)"
></sl-icon>`;
label = msg("Waiting");
break;
}
case "running": { case "running": {
icon = html`<sl-icon icon = html`<sl-icon
name="dot" name="dot"

View File

@ -90,6 +90,7 @@ export class CrawlDetail extends LiteElement {
return ( return (
this.crawl.state === "running" || this.crawl.state === "running" ||
this.crawl.state === "starting" || this.crawl.state === "starting" ||
this.crawl.state === "waiting" ||
this.crawl.state === "stopping" this.crawl.state === "stopping"
); );
} }

View File

@ -794,15 +794,16 @@ export class WorkflowDetail extends LiteElement {
if (!this.authState || !this.workflow?.currCrawlState) return ""; if (!this.authState || !this.workflow?.currCrawlState) return "";
const isStarting = this.workflow.currCrawlState === "starting"; const isStarting = this.workflow.currCrawlState === "starting";
const isWaiting = this.workflow.currCrawlState === "waiting";
const isRunning = this.workflow.currCrawlState === "running"; const isRunning = this.workflow.currCrawlState === "running";
const isStopping = this.workflow.currCrawlState === "stopping"; const isStopping = this.workflow.currCrawlState === "stopping";
const authToken = this.authState.headers.Authorization.split(" ")[1]; const authToken = this.authState.headers.Authorization.split(" ")[1];
return html` return html`
${isStarting ${isStarting || isWaiting
? html`<div class="rounded border p-3"> ? html`<div class="rounded border p-3">
<p class="text-sm text-neutral-600 motion-safe:animate-pulse"> <p class="text-sm text-neutral-600 motion-safe:animate-pulse">
${msg("Crawl starting...")} ${isStarting ? msg("Crawl starting...") : msg("Crawl waiting for available resources before it can start...")}
</p> </p>
</div>` </div>`
: isActive(this.workflow.currCrawlState) : isActive(this.workflow.currCrawlState)

View File

@ -90,6 +90,7 @@ export type Profile = {
export type CrawlState = export type CrawlState =
| "starting" | "starting"
| "waiting"
| "running" | "running"
| "complete" | "complete"
| "failed" | "failed"

View File

@ -1,6 +1,7 @@
import type { CrawlState } from "../types/crawler"; import type { CrawlState } from "../types/crawler";
export const activeCrawlStates: CrawlState[] = [ export const activeCrawlStates: CrawlState[] = [
"starting", "starting",
"waiting",
"running", "running",
"stopping", "stopping",
]; ];