Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import axios from 'axios';
import { getAkashTypeRegistry } from "@akashnetwork/akashjs/build/stargate";

const RPC_ENDPOINT = process.env.AKASH_NODE || "https://rpc.akash.forbole.com:443";
const REST_ENDPOINT = process.env.AKASH_API || "https://api.akashnet.net";

export class AkashService {
private wallet: DirectSecp256k1HdWallet | null = null;
Expand Down Expand Up @@ -126,7 +127,7 @@ export class AkashService {
}

async fetchBids(dseq: string): Promise<any[]> {
const url = `https://api.akashnet.net/akash/market/v1beta4/bids/list?filters.owner=${this.address}&filters.dseq=${dseq}&filters.state=open`;
const url = `${REST_ENDPOINT}/akash/market/v1beta4/bids/list?filters.owner=${this.address}&filters.dseq=${dseq}&filters.state=open`;
try {
const res = await axios.get(url);
return res.data.bids || [];
Expand All @@ -136,6 +137,14 @@ export class AkashService {
}
}

// "active" or "closed", as the chain reports it.
async getDeploymentStatus(dseq: string): Promise<{ state: string }> {
if (!this.address) throw new Error("Akash SDK not initialized (check mnemonic)");
const url = `${REST_ENDPOINT}/akash/deployment/v1beta3/deployments/info?id.owner=${this.address}&id.dseq=${encodeURIComponent(dseq)}`;
const res = await axios.get(url);
return { state: res.data?.deployment?.state || "unknown" };
}

async sendManifest(sdl: SDL, dseq: string, provider: string) {
console.log(`Sending manifest to ${provider}... (Mocked for SDK)`);
// Actual mTLS logic would go here
Expand Down
9 changes: 9 additions & 0 deletions src/orchestration/services/depin-sidecar/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,15 @@ akashRouter.post('/deployments/close', async (req, res) => {
}
});

akashRouter.get('/deployments/status/:id', async (req, res) => {
try {
res.json(await akashService.getDeploymentStatus(req.params.id));
} catch (error: any) {
const status = error.response?.status === 404 ? 404 : 502;
res.status(status).json({ error: error.message });
}
});

akashRouter.get('/deployments/:id/logs', async (req, res) => {
try {
const logs = await akashService.getLogs(req.params.id);
Expand Down
26 changes: 26 additions & 0 deletions src/providers/akash/akash_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,32 @@ async def wait_for_ready(

await asyncio.sleep(poll_interval)

async def get_node_status(
self,
*,
provider_instance_id: str,
provider_credential_name: Optional[str] = None,
) -> str:
"""Chain state of the deployment: RUNNING while active, STOPPED once
closed, ``"unknown"`` on any error so liveness never acts on a guess."""
try:
async with aiohttp.ClientSession() as session:
async with session.get(
f"{AKASH_SIDECAR_URL}/deployments/status/{provider_instance_id}",
headers=internal_headers,
timeout=aiohttp.ClientTimeout(total=30),
) as resp:
if resp.status != 200:
return "unknown"
state = (await resp.json()).get("state")
except Exception:
logger.warning(
"Akash get_node_status failed for %s", provider_instance_id,
exc_info=True,
)
return "unknown"
return {"active": "RUNNING", "closed": "STOPPED"}.get(state, "unknown")

async def deprovision_node(
self,
*,
Expand Down
38 changes: 38 additions & 0 deletions src/tests/providers/test_akash_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import pytest
import pytest_asyncio
from aiohttp import web
from aiohttp.test_utils import TestServer

from providers.akash import akash_adapter
from providers.akash.akash_adapter import AkashAdapter

STATES = {"live": "active", "gone": "closed"}


pytestmark = pytest.mark.asyncio


@pytest_asyncio.fixture
async def sidecar(monkeypatch):
async def status(request):
dseq = request.match_info["id"]
if dseq not in STATES:
return web.json_response({"error": "not found"}, status=404)
return web.json_response({"state": STATES[dseq]})

app = web.Application()
app.router.add_get("/akash/deployments/status/{id}", status)
server = TestServer(app)
await server.start_server()
monkeypatch.setattr(akash_adapter, "AKASH_SIDECAR_URL", str(server.make_url("/akash")))
yield server
await server.close()


@pytest.mark.parametrize("dseq,expected", [("live", "RUNNING"), ("gone", "STOPPED"), ("missing", "unknown")])
async def test_get_node_status(sidecar, dseq, expected):
assert await AkashAdapter().get_node_status(provider_instance_id=dseq) == expected


async def test_wait_for_ready_returns_on_active(sidecar):
assert await AkashAdapter().wait_for_ready(provider_instance_id="live", timeout=5) == "akash-ready"
Loading