Skip to content

Commit 0a9261b

Browse files
committed
New action: Scan single image
1 parent acb817b commit 0a9261b

File tree

3 files changed

+125
-62
lines changed

3 files changed

+125
-62
lines changed

.github/workflows/scan.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ jobs:
2626
run: poetry install
2727
- name: Scan all images of the release
2828
id: scan
29-
run: poetry run python stack_scanner/main.py ${{ matrix.release }} ${{ secrets.SECOBSERVE_API_TOKEN }}
29+
run: poetry run python stack_scanner/main.py scan-release ${{ secrets.SECOBSERVE_API_TOKEN }} ${{ matrix.release }}
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Scan single image
2+
on:
3+
workflow_dispatch:
4+
inputs:
5+
product_name:
6+
description: 'Product name in SecObserve (example: hbase)'
7+
required: true
8+
product_version:
9+
description: 'Product version in SecObserve (example: 2.4.17-stackable24.3.0)'
10+
required: true
11+
image:
12+
description: 'Location of the image (example: oci.stackable.tech/sdp/hbase:2.4.17-stackable24.3.0)'
13+
required: true
14+
15+
jobs:
16+
scan_image:
17+
runs-on: ubuntu-latest
18+
steps:
19+
- uses: actions/checkout@v4
20+
- uses: actions/setup-python@v5
21+
with:
22+
python-version: 3.11
23+
- name: Run image
24+
uses: abatilo/actions-poetry@v2
25+
with:
26+
poetry-version: 1.7.1
27+
- name: Install deps
28+
run: poetry install
29+
- name: Scan image
30+
id: scan
31+
run: poetry run python stack_scanner/main.py scan-image ${{ secrets.SECOBSERVE_API_TOKEN }} ${{ github.event.inputs.image }} ${{ github.event.inputs.product_name }} ${{ github.event.inputs.product_version }}

stack_scanner/main.py

+93-61
Original file line numberDiff line numberDiff line change
@@ -19,83 +19,114 @@
1919

2020

2121
def main():
22-
if len(sys.argv) < 3:
23-
print("Usage: python main.py <release> <secobserve_api_token>")
22+
if len(sys.argv) < 4:
23+
print(
24+
"Usage:\n"
25+
"python main.py scan-release <secobserve_api_token> <release>\n"
26+
"or\n"
27+
"python main.py scan-image <secobserve_api_token> <image> <product_name> <product_version>"
28+
)
2429
sys.exit(1)
2530

2631
os.makedirs("/tmp/stackable", exist_ok=True)
2732
os.system("rm -rf /tmp/stackable/*")
2833

2934
with tempfile.TemporaryDirectory() as tempdir:
30-
release = sys.argv[1]
31-
secobserve_api_token = sys.argv[2]
32-
# Create a file in the temp dir and download the conf.py from the git tag referring to that version
33-
filename = os.path.join(tempdir, f"products-{release}.py")
34-
branch = release
35-
if release == "0.0.0-dev":
36-
branch = "main"
37-
url = f"https://raw.githubusercontent.com/stackabletech/docker-images/{branch}/conf.py"
38-
oldurl = f"https://raw.githubusercontent.com/stackabletech/docker-images/{branch}/image_tools/conf.py"
39-
print(
40-
f"Loading product config for version [{release}] from [{url}] (via file [{filename}]"
41-
)
42-
try:
43-
urlretrieve(url, filename)
44-
except:
35+
# dump argv to console
36+
print(sys.argv)
37+
if sys.argv[1] == "scan-image":
38+
secobserve_api_token = sys.argv[2]
39+
image = sys.argv[3]
40+
product_name = sys.argv[4]
41+
product_version = sys.argv[5]
42+
scan_image(secobserve_api_token, image, product_name, product_version)
43+
sys.exit(0)
44+
else:
45+
secobserve_api_token = sys.argv[2]
46+
release = sys.argv[3]
47+
# Create a file in the temp dir and download the conf.py from the git tag referring to that version
48+
filename = os.path.join(tempdir, f"products-{release}.py")
49+
branch = release
50+
if release == "0.0.0-dev":
51+
branch = "main"
52+
url = f"https://raw.githubusercontent.com/stackabletech/docker-images/{branch}/conf.py"
53+
oldurl = f"https://raw.githubusercontent.com/stackabletech/docker-images/{branch}/image_tools/conf.py"
4554
print(
46-
f"Got 404 for release file, falling back to old file location [{oldurl}]"
55+
f"Loading product config for version [{release}] from [{url}] (via file [{filename}]"
4756
)
4857
try:
49-
urlretrieve(oldurl, filename)
58+
urlretrieve(url, filename)
5059
except:
51-
print(f"Unable to retrieve config file for release [{release}]")
52-
sys.exit(1)
53-
54-
operators = ["airflow", "commons", "druid", "hbase", "hdfs", "hello-world", "hive", "kafka", "listener", "nifi", "opa", "secret", "spark-k8s", "superset", "trino", "zookeeper"]
55-
56-
for operator_name in operators:
57-
scan_image(secobserve_api_token, f"{operator_name}-operator", release)
58-
59-
# Free up space after scanning operators
60-
os.system(
61-
'docker system prune -f'
62-
)
63-
os.system(
64-
'docker system prune -f -a --filter="label=vendor=Stackable GmbH"'
65-
)
66-
67-
# Load product versions from that file using the image-tools functionality
68-
product_versions = load_configuration(filename)
69-
70-
for product in product_versions.products:
71-
product_name: str = product["name"]
72-
73-
if product_name in excluded_products:
74-
continue
75-
for version_dict in product.get("versions", []):
76-
product_version: str = version_dict["product"]
77-
78-
scan_image(secobserve_api_token, product_name, f"{product_version}-stackable{release}")
79-
80-
# Free up space after each product scan
81-
os.system(
82-
'docker system prune -f'
83-
)
84-
os.system(
85-
'docker system prune -f -a --filter="label=vendor=Stackable GmbH"'
86-
)
87-
88-
def scan_image(secobserve_api_token: str, product_name: str, image_tag: str) -> None:
60+
print(
61+
f"Got 404 for release file, falling back to old file location [{oldurl}]"
62+
)
63+
try:
64+
urlretrieve(oldurl, filename)
65+
except:
66+
print(f"Unable to retrieve config file for release [{release}]")
67+
sys.exit(1)
68+
69+
operators = [
70+
"airflow",
71+
"commons",
72+
"druid",
73+
"hbase",
74+
"hdfs",
75+
"hello-world",
76+
"hive",
77+
"kafka",
78+
"listener",
79+
"nifi",
80+
"opa",
81+
"secret",
82+
"spark-k8s",
83+
"superset",
84+
"trino",
85+
"zookeeper",
86+
]
87+
88+
for operator_name in operators:
89+
product_name = f"{operator_name}-operator"
90+
scan_image(secobserve_api_token, f"{REGISTRY_URL}/stackable/{product_name}:{release}", product_name, release)
91+
92+
# Free up space after scanning operators
93+
os.system("docker system prune -f")
94+
os.system('docker system prune -f -a --filter="label=vendor=Stackable GmbH"')
95+
96+
# Load product versions from that file using the image-tools functionality
97+
product_versions = load_configuration(filename)
98+
99+
for product in product_versions.products:
100+
product_name: str = product["name"]
101+
102+
if product_name in excluded_products:
103+
continue
104+
for version_dict in product.get("versions", []):
105+
version: str = version_dict["product"]
106+
product_version = f"{version}-stackable{release}"
107+
scan_image(
108+
secobserve_api_token,
109+
f"{REGISTRY_URL}/stackable/{product_name}:{product_version}",
110+
product_name,
111+
product_version,
112+
)
113+
114+
# Free up space after each product scan
115+
os.system("docker system prune -f")
116+
os.system(
117+
'docker system prune -f -a --filter="label=vendor=Stackable GmbH"'
118+
)
119+
120+
121+
def scan_image(secobserve_api_token: str, image: str, product_name: str, product_version: str) -> None:
89122
# Run Trivy
90123
env = {}
91-
env["TARGET"] = (
92-
f"{REGISTRY_URL}/stackable/{product_name}:{image_tag}"
93-
)
124+
env["TARGET"] = image
94125
env["SO_UPLOAD"] = "true"
95126
env["SO_PRODUCT_NAME"] = product_name
96127
env["SO_API_BASE_URL"] = "https://secobserve-backend.stackable.tech"
97128
env["SO_API_TOKEN"] = secobserve_api_token
98-
env["SO_BRANCH_NAME"] = image_tag
129+
env["SO_BRANCH_NAME"] = product_version
99130
env["TMPDIR"] = "/tmp"
100131
env["REPORT_NAME"] = "trivy.json"
101132

@@ -146,5 +177,6 @@ def scan_image(secobserve_api_token: str, product_name: str, image_tag: str) ->
146177

147178
subprocess.run(cmd)
148179

180+
149181
if __name__ == "__main__":
150182
main()

0 commit comments

Comments
 (0)