diff --git a/.github/workflows/clean_dockerhub_images.yml b/.github/workflows/clean_dockerhub_images.yml new file mode 100644 index 000000000..510bec079 --- /dev/null +++ b/.github/workflows/clean_dockerhub_images.yml @@ -0,0 +1,30 @@ +name: Docker Cleanup + +on: + schedule: + - cron: '0 12 * * 1' # Runs every Monday at noon (UTC) + workflow_dispatch: + +jobs: + cleanup: + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.x" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install requests + + - name: Run Docker image cleanup + run: make clean-old-temporary-docker-images + env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} + DELETE_AFTER_DAYS: 30 diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..65dae373a --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +# Makefile to run the Docker cleanup script + +clean-old-temporary-docker-images: + @echo "Running Docker Hub image cleanup script..." + python ci/clean-old-temporary-docker-images.py diff --git a/ci/clean-old-temporary-docker-images.py b/ci/clean-old-temporary-docker-images.py new file mode 100755 index 000000000..a7521f37f --- /dev/null +++ b/ci/clean-old-temporary-docker-images.py @@ -0,0 +1,58 @@ +import os +import requests +from datetime import datetime, timedelta + +DOCKERHUB_USERNAME = os.environ["DOCKERHUB_USERNAME"] +DOCKERHUB_TOKEN = os.environ["DOCKERHUB_TOKEN"] +DELETE_AFTER_DAYS = os.environ["DELETE_AFTER_DAYS"] + +def get_docker_token(username, password): + url = "https://hub.docker.com/v2/users/login/" + headers = {"Content-Type": "application/json"} + data = {"username": username, "password": password} + + response = requests.post(url, json=data, headers=headers) + if response.status_code == 200: + return response.json()["token"] + else: + print(f"Failed to login to DockerHub: {response.status_code}") + return None + +def get_repo_tags(token): + url = f"https://hub.docker.com/v2/repositories/scylladb/gocql-extended-ci/tags/" + headers = {"Authorization": f"Bearer {token}"} + response = requests.get(url, headers=headers) + if response.status_code != 200: + print(f"Failed to get tags, Status Code: {response.status_code}, {response.text}") + return None + return response.json()["results"] + +def delete_tag(tag, token): + url = f"https://hub.docker.com/v2/repositories/scylladb/gocql-extended-ci/tags/{tag}/" + headers = {"Authorization": f"Bearer {token}"} + response = requests.delete(url, headers=headers) + if response.status_code > 200 and response.status_code < 300: + print(f"Deleted tag: {tag}") + return True + print(f"Failed to delete tag: {tag}, Status Code: {response.status_code}") + return False + +def clean_old_images(): + token = get_docker_token(DOCKERHUB_USERNAME, DOCKERHUB_TOKEN) + if token is None: + return False + tags = get_repo_tags(token) + if tags is None: + return False + threshold_date = datetime.now() - timedelta(days=int(DELETE_AFTER_DAYS)) + status = True + for tag in tags: + last_updated = datetime.strptime(tag["last_updated"], "%Y-%m-%dT%H:%M:%S.%fZ") + if last_updated < threshold_date: + status = status and delete_tag(tag["name"], token) + return status + +if __name__ == "__main__": + if not clean_old_images(): + exit(1) + exit(0)