Skip to content

Commit 8ebfd8c

Browse files
workflows and things
1 parent 3f70a56 commit 8ebfd8c

10 files changed

Lines changed: 293 additions & 158 deletions

File tree

.github/dependabot.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# To get started with Dependabot version updates, you'll need to specify which
2+
# package ecosystems to update and where the package manifests are located.
3+
# Please see the documentation for all configuration options:
4+
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5+
6+
version: 2
7+
updates:
8+
- package-ecosystem: "uv"
9+
directory: "/"
10+
schedule:
11+
interval: "weekly"
12+
target-branch: "master"
13+
open-pull-requests-limit: 10
14+
- package-ecosystem: github-actions
15+
directory: /
16+
groups:
17+
github-actions:
18+
patterns:
19+
- "*" # Group all Actions updates into a single larger pull request
20+
schedule:
21+
interval: weekly

.github/workflows/python-tests.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Python Tests
2+
3+
on:
4+
push:
5+
branches: [ master ]
6+
pull_request:
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v6
14+
15+
- name: Set up Python
16+
uses: actions/setup-python@v6
17+
with:
18+
python-version: 3.x
19+
20+
- name: Set up uv
21+
uses: astral-sh/setup-uv@v7
22+
23+
- name: Install Rust toolchain
24+
uses: actions-rust-lang/setup-rust-toolchain@v1
25+
26+
- name: Install dependencies
27+
run: |
28+
uv sync
29+
30+
- name: Build Python bindings
31+
run: |
32+
maturin develop --release --features pyo3
33+
34+
- name: Run Python tests
35+
run: |
36+
uv run pytest test_cloudcheck.py -v

Cargo.lock

Lines changed: 77 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ serde = { version = "1.0", features = ["derive"] }
99
serde_json = "1.0"
1010
tokio = { version = "1", features = ["full"] }
1111
reqwest = { version = "0.12", features = ["json"] }
12+
pyo3 = { version = "0.27", features = ["auto-initialize"], optional = true }
13+
pyo3-async-runtimes = { version = "0.27", features = ["tokio-runtime"], optional = true }
14+
15+
[features]
16+
default = ["pyo3"]
17+
pyo3 = ["dep:pyo3", "dep:pyo3-async-runtimes"]
1218

1319
[[bin]]
1420
name = "cloudcheck"
@@ -17,4 +23,4 @@ path = "src/main.rs"
1723
[lib]
1824
name = "cloudcheck"
1925
path = "src/lib.rs"
20-
26+
crate-type = ["cdylib", "rlib"]

cloudcheck/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .cloudcheck import CloudCheck
2+
3+
__all__ = ["CloudCheck"]
4+

pyproject.toml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
1+
[build-system]
2+
requires = ["maturin>=1.0,<2.0"]
3+
build-backend = "maturin"
4+
15
[project]
26
name = "cloudcheck"
37
version = "8.1.0rc1"
48
description = "Detailed database of cloud providers. Instantly look up a domain or IP address"
59
readme = "README.md"
610
requires-python = ">=3.9"
7-
dependencies = [
8-
"requests (>=2.32.5,<3.0.0)",
9-
]
10-
11-
[tool.poetry.scripts]
12-
cloudcheck-update = 'cloudcheck_update.cli:main'
11+
dependencies = []
1312

1413
[dependency-groups]
1514
dev = [
15+
"maturin>=1.10.2",
1616
"pydantic>=2.12.5",
1717
"pytest>=8.4.2",
18+
"pytest-asyncio>=0.24.0",
1819
"ruff>=0.14.8",
1920
]
21+
22+
[tool.maturin]
23+
features = ["pyo3"]
24+
module-name = "cloudcheck"

src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ use std::sync::Arc;
66
use std::time::{Duration, SystemTime};
77
use tokio::sync::OnceCell;
88

9+
#[cfg(feature = "pyo3")]
10+
mod python;
11+
912
const CLOUDCHECK_SIGNATURE_URL: &str = "https://raw.githubusercontent.com/blacklanternsecurity/cloudcheck/refs/heads/cloudcheck-v8/cloud_providers_v2.json";
1013

1114
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -22,6 +25,7 @@ struct ProviderData {
2225
domains: Vec<String>,
2326
}
2427

28+
#[derive(Clone)]
2529
pub struct CloudCheck {
2630
radix: Arc<OnceCell<RadixTarget>>,
2731
providers: Arc<OnceCell<HashMap<String, Vec<CloudProvider>>>>,
@@ -188,3 +192,4 @@ mod tests {
188192
);
189193
}
190194
}
195+

src/python.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use pyo3::prelude::*;
2+
use pyo3::types::PyDict;
3+
use crate::CloudCheck as RustCloudCheck;
4+
5+
#[pymodule]
6+
fn cloudcheck(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
7+
m.add_class::<CloudCheck>()?;
8+
Ok(())
9+
}
10+
11+
#[pyclass(name = "CloudCheck")]
12+
pub struct CloudCheck {
13+
inner: RustCloudCheck,
14+
}
15+
16+
#[pymethods]
17+
impl CloudCheck {
18+
#[new]
19+
fn new() -> Self {
20+
CloudCheck {
21+
inner: RustCloudCheck::new(),
22+
}
23+
}
24+
25+
fn lookup<'py>(&self, py: Python<'py>, target: &str) -> PyResult<Bound<'py, PyAny>> {
26+
let inner = self.inner.clone();
27+
let target = target.to_string();
28+
pyo3_async_runtimes::tokio::future_into_py(py, async move {
29+
match inner.lookup(&target).await {
30+
Ok(providers) => {
31+
Python::attach(|py| -> PyResult<Vec<Py<PyAny>>> {
32+
let mut result = Vec::new();
33+
for provider in providers {
34+
let dict = PyDict::new(py);
35+
dict.set_item("name", provider.name)?;
36+
dict.set_item("tags", provider.tags)?;
37+
result.push(dict.unbind().into());
38+
}
39+
Ok(result)
40+
})
41+
}
42+
Err(e) => Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
43+
format!("CloudCheck error: {}", e),
44+
)),
45+
}
46+
})
47+
}
48+
}
49+

test_cloudcheck.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import pytest
2+
from cloudcheck import CloudCheck
3+
4+
5+
@pytest.mark.asyncio
6+
async def test_lookup_google_dns():
7+
cloudcheck = CloudCheck()
8+
results = await cloudcheck.lookup("8.8.8.8")
9+
names = [provider["name"] for provider in results]
10+
assert "Google" in names, f"Expected Google in results: {names}"
11+
12+
13+
@pytest.mark.asyncio
14+
async def test_lookup_amazon_domain():
15+
cloudcheck = CloudCheck()
16+
results = await cloudcheck.lookup("asdf.amazon.com")
17+
names = [provider["name"] for provider in results]
18+
assert "Amazon" in names, f"Expected Amazon in results: {names}"
19+

0 commit comments

Comments
 (0)