Skip to content

Commit d8c7377

Browse files
authored
Remove GCP dependencies (#1440)
* Remove gcp dependencies * Update dockerfiles * Update dockerfiles * Update gcp error reporting * Updates to formatting * Add unit test * Update unit test * Clean up * Update unit test * Update error reporting * Update file * Update config template * Catch exception * Updates * fix lint
1 parent da36f37 commit d8c7377

27 files changed

+1021
-2186
lines changed

docker/api_server/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ WORKDIR /home/turbinia
3333

3434
# Copy requirements and install dependencies to cache them in docker layer
3535
COPY --chown=turbinia:turbinia ./pyproject.toml ./poetry.toml ./poetry.lock /home/turbinia/
36-
RUN poetry install --no-interaction --no-ansi --no-root -E gcp
36+
RUN poetry install --no-interaction --no-ansi --no-root
3737

3838
ENV PATH="/home/turbinia/.venv/bin:$PATH" \
3939
VIRTUAL_ENV=/home/turbinia/.venv
@@ -44,7 +44,7 @@ COPY --chown=turbinia:turbinia docker/api_server/start.sh /home/turbinia/start.s
4444
RUN chmod +rwx /home/turbinia/start.sh
4545

4646
# Install Turbinia package -- will skip dependencies if installed
47-
RUN poetry install --no-interaction --no-ansi -E gcp
47+
RUN poetry install --no-interaction --no-ansi
4848

4949
CMD ["/home/turbinia/start.sh"]
5050
# Expose Prometheus and API endpoints.

docker/server/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ WORKDIR /home/turbinia
3232

3333
# Copy requirements and install dependencies to cache them in docker layer
3434
COPY --chown=turbinia:turbinia ./pyproject.toml ./poetry.toml ./poetry.lock /home/turbinia/
35-
RUN poetry install --no-interaction --no-ansi --no-root -E gcp
35+
RUN poetry install --no-interaction --no-ansi --no-root
3636

3737
ENV PATH="/home/turbinia/.venv/bin:$PATH" \
3838
VIRTUAL_ENV=/home/turbinia/.venv
@@ -43,7 +43,7 @@ COPY --chown=turbinia:turbinia docker/server/start.sh /home/turbinia/start.sh
4343
RUN chmod +rwx /home/turbinia/start.sh
4444

4545
# Install Turbinia package -- will skip dependencies if installed
46-
RUN poetry install --no-interaction --no-ansi -E gcp
46+
RUN poetry install --no-interaction --no-ansi
4747

4848
CMD ["/home/turbinia/start.sh"]
4949
# Expose Prometheus endpoint.

docker/tests/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ WORKDIR /home/turbinia
9696
COPY --chown=turbinia:turbinia ./pyproject.toml ./poetry.toml ./poetry.lock /home/turbinia/
9797

9898
# Install dependencies using Poetry
99-
RUN poetry install --no-interaction --no-ansi -E worker -E gcp --with test --no-root
99+
RUN poetry install --no-interaction --no-ansi -E worker --with test --no-root
100100
RUN poetry run python3 -m pip install impacket --no-deps
101101

102102
# Activate the virtualenv
@@ -106,7 +106,7 @@ ENV PATH="/home/turbinia/.venv/bin:$PATH" \
106106
# Install Turbinia and dependencies in /home/turbinia/.venv using Poetry
107107
ADD . /home/turbinia/
108108
# RUN if $(cd /tmp/ && git rev-parse --is-shallow-repository); then cd /tmp/ && git fetch --prune --unshallow && git fetch --depth=1 origin +refs/tags/*:refs/tags/*; fi
109-
RUN poetry install --no-interaction --no-ansi -E worker -E gcp --with test
109+
RUN poetry install --no-interaction --no-ansi -E worker --with test
110110
# We need to install the current dev version of turbinia-api-lib for the cli tool test in case the API client changes
111111
RUN cd turbinia/api/client && poetry install
112112
CMD ["/bin/bash"]

docker/worker/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ WORKDIR /home/turbinia
105105

106106
# Copy requirements and install dependencies to cache them in docker layer
107107
COPY --chown=turbinia:turbinia ./pyproject.toml ./poetry.toml ./poetry.lock /home/turbinia/
108-
RUN poetry install --no-interaction --no-ansi -E worker -E gcp --no-root
108+
RUN poetry install --no-interaction --no-ansi -E worker --no-root
109109
RUN poetry run pip3 install impacket --no-deps
110110
ENV PATH="/home/turbinia/.venv/bin:$PATH" \
111111
VIRTUAL_ENV=/home/turbinia/.venv
@@ -116,7 +116,7 @@ COPY --chown=turbinia:turbinia docker/worker/start.sh /home/turbinia/start.sh
116116
RUN chmod +rwx /home/turbinia/start.sh
117117

118118
# Install Turbinia package -- will skip dependencies if installed
119-
RUN poetry install --no-interaction --no-ansi -E worker -E gcp
119+
RUN poetry install --no-interaction --no-ansi -E worker
120120

121121
CMD ["/home/turbinia/start.sh"]
122122
# Expose Prometheus endpoint.

poetry.lock

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

pyproject.toml

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "turbinia"
3-
version = "20231116.2"
3+
version = "20240219"
44
description = "Automation and Scaling of Digital Forensics Tools"
55
authors = ["Turbinia Developers <turbinia-dev@googlegroups.com>"]
66
maintainers = ["Turbinia Developers <turbinia-dev@googlegroups.com>"]
@@ -20,23 +20,11 @@ dfimagetools = { version = "^20230806", optional = true }
2020
docker = { version = "^6.1.3" }
2121
fastapi = {extras = ["all"], version = ">=0.75.0,<0.99.0"}
2222
filelock = { version = "*" }
23-
# TODO: Cleanup GCP libs and their dependencies when psq/pubsub is deprecated
24-
google-api-core = { version = "<2.0.0dev", optional = true }
25-
google-api-python-client = { version = "*", optional = true }
26-
google-auth = { version = ">=2.15.0", optional = true }
27-
google-cloud-core = { version = "<2.0dev", optional = true }
28-
google-cloud-datastore = { version = "<=2.0.0", optional = true }
29-
google-cloud-error-reporting = { version = "*", optional = true }
30-
google-cloud-pubsub = { version = "1.7.0", optional = true }
31-
google-cloud-storage = { version = "<=2.2.1", optional = true }
32-
grpcio-status = { version = "<1.49.0,>=1.33.2", optional = true }
33-
libcloudforensics = { version = "20230601" }
23+
libcloudforensics = { version = "20240214" }
3424
pandas = { version = "^2.1.0" }
3525
plaso = { version = "20231224", optional = true }
3626
prometheus_client = { version = "^0.17.1" }
3727
protobuf = { version = ">=3.19.0,<4.0.0dev", optional = true }
38-
proto-plus = { version = "<2.0.0dev,>=1.22.0", optional = true }
39-
psq = { version = "*", optional = true }
4028
pydantic = { version = "^1.10.5,<2"}
4129
pyhindsight = { version = "^20230327.0", optional = true }
4230
redis = { version = "^4.4.4" }
@@ -51,6 +39,7 @@ fakeredis = "^1.8.1"
5139
google-auth-oauthlib = "^1.1.0"
5240
mock = "*"
5341
pytest = "*"
42+
turbinia-api-lib = "^1.0.2"
5443
yapf = "*"
5544

5645
[tool.poetry.extras]
@@ -61,21 +50,6 @@ worker = [
6150
"pyhindsight",
6251
]
6352

64-
gcp = [
65-
"google-api-core",
66-
"google-api-python-client",
67-
"google-auth",
68-
"google-cloud-coverage",
69-
"google-cloud-datastore",
70-
"google-cloud-error-reporting",
71-
"google-cloud-pubsub",
72-
"google-cloud-storage",
73-
"grpcio-status",
74-
"protobuf",
75-
"proto-plus",
76-
"psq",
77-
]
78-
7953
[build-system]
8054
requires = ["poetry-core"]
8155
build-backend = "poetry.core.masonry.api"

turbinia/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ def log_and_report(message, trace):
4343
if config.CLOUD_PROVIDER.lower() == 'gcp' and config.STACKDRIVER_TRACEBACK:
4444
# Only load google_cloud if needed
4545
from turbinia.lib import google_cloud
46-
client = google_cloud.setup_stackdriver_traceback(config.TURBINIA_PROJECT)
47-
client.report_exception()
46+
error_client = google_cloud.GCPErrorReporting()
47+
message = f'{message}:{trace}'
48+
error_client.report(message)
4849

4950

5051
class TurbiniaException(Exception):

turbinia/client.py

Lines changed: 2 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,12 @@
2020
from datetime import datetime
2121
from datetime import timedelta
2222

23-
import httplib2
24-
import json
2523
import logging
2624
from operator import itemgetter
2725
from operator import attrgetter
2826
import os
2927
import time
3028

31-
from google import auth
3229
from turbinia import config
3330
from turbinia.config import logger
3431
from turbinia.config import DATETIME_FORMAT
@@ -67,9 +64,7 @@ def get_turbinia_client():
6764
"""
6865
# pylint: disable=no-else-return
6966
setup(is_client=True)
70-
if config.TASK_MANAGER.lower() == 'psq':
71-
return BaseTurbiniaClient()
72-
elif config.TASK_MANAGER.lower() == 'celery':
67+
if config.TASK_MANAGER.lower() == 'celery':
7368
return TurbiniaCeleryClient()
7469
else:
7570
msg = f'Task Manager type "{config.TASK_MANAGER:s}" not implemented'
@@ -340,123 +335,6 @@ def wait_for_request(
340335

341336
log.info(f'All {len(task_results):d} Tasks completed')
342337

343-
def get_task_data(
344-
self, instance, project, region, days=0, task_id=None, request_id=None,
345-
group_id=None, user=None, function_name='gettasks', output_json=False):
346-
"""Gets task data from Google Cloud Functions.
347-
348-
Args:
349-
instance (string): The Turbinia instance name (by default the same as the
350-
INSTANCE_ID in the config).
351-
project (string): The name of the project.
352-
region (string): The name of the region to execute in.
353-
days (int): The number of days we want history for.
354-
task_id (string): The Id of the task.
355-
group_id (string): The group Id of the requests.
356-
request_id (string): The Id of the request we want tasks for.
357-
user (string): The user of the request we want tasks for.
358-
function_name (string): The GCF function we want to call.
359-
output_json (bool): Whether to return JSON output.
360-
361-
Returns:
362-
(List|JSON string) of Task dict objects
363-
"""
364-
cloud_function = gcp_function.GoogleCloudFunction(project)
365-
func_args = {'instance': instance, 'kind': 'TurbiniaTask'}
366-
367-
if days:
368-
start_time = datetime.now() - timedelta(days=days)
369-
# Format this like '1990-01-01T00:00:00z' so we can cast it directly to a
370-
# javascript Date() object in the cloud function.
371-
start_string = start_time.strftime(DATETIME_FORMAT)
372-
func_args.update({'start_time': start_string})
373-
elif task_id:
374-
func_args.update({'task_id': task_id})
375-
elif group_id:
376-
func_args.update({'group_id': group_id})
377-
elif request_id:
378-
func_args.update({'request_id': request_id})
379-
380-
if user:
381-
func_args.update({'user': user})
382-
383-
response = {}
384-
retry_count = 0
385-
credential_error_count = 0
386-
while not response and retry_count < MAX_RETRIES:
387-
try:
388-
response = cloud_function.ExecuteFunction(
389-
function_name, region, func_args)
390-
except auth.exceptions.RefreshError as exception:
391-
if credential_error_count == 0:
392-
log.info(
393-
'GCP Credentials need to be refreshed by running gcloud auth '
394-
'application-default login, please refresh in another terminal '
395-
'and run turbiniactl -w status -r {0!s} and this process will '
396-
'resume. Error: {1!s}'.format(request_id, exception))
397-
else:
398-
log.debug(
399-
'GCP Credentials need to be refreshed by running gcloud auth '
400-
'application-default login, please refresh in another terminal '
401-
'and run turbiniactl -w status -r {0!s} and this process will '
402-
'resume. Attempt {1:d}. Error: '
403-
'{2!s}'.format(request_id, credential_error_count + 1, exception))
404-
# Note, we are intentionally not incrementing the retry_count here because
405-
# we will retry indefinitely while we wait for the user to reauth.
406-
credential_error_count += 1
407-
except httplib2.ServerNotFoundError as exception:
408-
log.info(
409-
'Error connecting to server, will retry [{0:d} of {1:d} retries]: '
410-
'{2!s}'.format(retry_count, MAX_RETRIES, exception))
411-
retry_count += 1
412-
413-
if not response:
414-
retry_count += 1
415-
time.sleep(RETRY_SLEEP)
416-
elif response.get('error', {}).get('code') == 503:
417-
log.warning(
418-
'Retriable error response from cloud functions: [{0!s}]'.format(
419-
response.get('error')))
420-
retry_count += 1
421-
response = {}
422-
time.sleep(RETRY_SLEEP)
423-
424-
if not response or 'result' not in response:
425-
log.error('No results found')
426-
if response.get('error'):
427-
msg = f"Error executing Cloud Function: [{response.get('error')!s}]."
428-
log.error(msg)
429-
log.debug(f'Invalid or empty GCF response: {response!s}')
430-
raise TurbiniaException(
431-
f'Cloud Function {function_name:s} returned no results.')
432-
433-
try:
434-
results = json.loads(response.get('result'))
435-
except (TypeError, ValueError) as exception:
436-
raise TurbiniaException(
437-
'Could not deserialize result [{0!s}] from GCF: [{1!s}]'.format(
438-
response.get('result'), exception))
439-
440-
task_data = results[0]
441-
if output_json:
442-
try:
443-
json_data = json.dumps(task_data)
444-
except (TypeError, ValueError) as exception:
445-
raise TurbiniaException(
446-
'Could not re-serialize result [{0!s}] from GCF: [{1!s}]'.format(
447-
str(task_data), exception))
448-
return json_data
449-
450-
# Convert run_time/last_update back into datetime objects
451-
for task in task_data:
452-
if task.get('run_time'):
453-
task['run_time'] = timedelta(seconds=task['run_time'])
454-
if task.get('last_update'):
455-
task['last_update'] = datetime.strptime(
456-
task['last_update'], DATETIME_FORMAT)
457-
458-
return task_data
459-
460338
def format_task_detail(self, task, show_files=False):
461339
"""Formats a single task in detail.
462340
@@ -1019,36 +897,7 @@ def send_request(self, request):
1019897
Args:
1020898
request: A TurbiniaRequest object.
1021899
"""
1022-
self.task_manager.server_pubsub.send_request(request)
1023-
1024-
def close_tasks(
1025-
self, instance, project, region, request_id=None, task_id=None, user=None,
1026-
requester=None):
1027-
"""Close Turbinia Tasks based on Request ID.
1028-
1029-
Args:
1030-
instance (string): The Turbinia instance name (by default the same as the
1031-
INSTANCE_ID in the config).
1032-
project (string): The name of the project.
1033-
region (string): The name of the zone to execute in.
1034-
request_id (string): The Id of the request we want tasks for.
1035-
task_id (string): The Id of the request we want task for.
1036-
user (string): The user of the request we want tasks for.
1037-
requester (string): The user making the request to close tasks.
1038-
1039-
Returns: String of closed Task IDs.
1040-
"""
1041-
cloud_function = gcp_function.GoogleCloudFunction(project)
1042-
func_args = {
1043-
'instance': instance,
1044-
'kind': 'TurbiniaTask',
1045-
'request_id': request_id,
1046-
'task_id': task_id,
1047-
'user': user,
1048-
'requester': requester
1049-
}
1050-
response = cloud_function.ExecuteFunction('closetasks', region, func_args)
1051-
return f"Closed Task IDs: {response.get('result')}"
900+
pass
1052901

1053902

1054903
class TurbiniaCeleryClient(BaseTurbiniaClient):

0 commit comments

Comments
 (0)