From a8b4c0f91c54ccf72554316a8d0802adcca75e1b Mon Sep 17 00:00:00 2001
From: Benjamin Bertrand <benjamin.bertrand@esss.se>
Date: Mon, 1 Apr 2019 18:41:32 +0200
Subject: [PATCH] Initialize IOC repository on GitLab

This is temporary. The repository should be created by IOCFactory.

JIRA INFRA-932 #action In Progress
---
 .env                        |  1 +
 app/models.py               | 10 ++++++++
 app/settings.py             |  9 +++++++
 app/tasks.py                | 49 +++++++++++++++++++++++++++++++++++++
 app/utils.py                | 17 +++++++++++++
 docker-compose.override.yml |  1 +
 6 files changed, 87 insertions(+)

diff --git a/.env b/.env
index 29cca1b..cce874f 100644
--- a/.env
+++ b/.env
@@ -3,3 +3,4 @@ POSTGRES_PASSWORD=icspwd
 POSTGRES_DB=csentry_db
 PGDATA_VOLUME=./data/postgres
 ELASTIC_DATA_VOLUME=./data/elastic
+GL_ACCESS_TOKEN=xxxx
diff --git a/app/models.py b/app/models.py
index 4fd0936..df0edd3 100644
--- a/app/models.py
+++ b/app/models.py
@@ -1745,6 +1745,16 @@ def before_flush(session, flush_context, instances):
     trigger_inventory_update(session)
 
 
+@sa.event.listens_for(Host.is_ioc, "set")
+def receive_host_is_ioc_set(target, value, oldvalue, initiator):
+    """Listen for the 'set' event on Host.is_ioc
+
+    Trigger repository creation in GitLab
+    """
+    if value is True and oldvalue is not True:
+        utils.trigger_ioc_repository_creation(target)
+
+
 # call configure_mappers after defining all the models
 # required by sqlalchemy_continuum
 sa.orm.configure_mappers()
diff --git a/app/settings.py b/app/settings.py
index b0f759b..37e804c 100644
--- a/app/settings.py
+++ b/app/settings.py
@@ -122,6 +122,15 @@ VIOC_MEMORY_CHOICES = [2, 4, 8]
 VIOC_DISK_CHOICES = [15, 50, 100, 250]
 VIOC_OSVERSION_CHOICES = ["centos7"]
 
+# IOC repository creation
+IOC_REPOSITORY_CREATION_ENABLED = False
+IOC_REPOSITORY_GROUP_ID = 208
+IOC_REPOSITORY_VISIBILITY = "internal"
+IOC_REPOSITORY_GITLAB_URL = "https://gitlab.esss.lu.se"
+IOC_REPOSITORY_GITLAB_CI_YML = """---
+include: 'https://gitlab.esss.lu.se/ics-infrastructure/gitlab-ci-yml/raw/master/Ioc.gitlab-ci.yml'
+"""
+
 # Sentry integration
 CSENTRY_RELEASE = raven.fetch_git_sha(Path(__file__).parents[1])
 # Leave to empty string to disable sentry integration
diff --git a/app/tasks.py b/app/tasks.py
index 6bef637..e0ad7c3 100644
--- a/app/tasks.py
+++ b/app/tasks.py
@@ -9,8 +9,10 @@ This module implements tasks to run.
 :license: BSD 2-Clause, see LICENSE for more details.
 
 """
+import os
 import time
 import traceback
+import requests
 import tower_cli
 from datetime import datetime
 from flask import current_app
@@ -226,3 +228,50 @@ def generate_items_excel_file():
         pagination = pagination.next()
     wb.save(full_path)
     return full_path.name
+
+
+def create_ioc_repository(project_name):
+    """Create the IOC repository on GitLab
+
+    Return the response of the POST
+    """
+    api_url = current_app.config["IOC_REPOSITORY_GITLAB_URL"] + "/api/v4"
+    headers = {
+        "user-agent": "csentry",
+        "accept": "application/json",
+        "private-token": os.environ.get("GL_ACCESS_TOKEN"),
+    }
+    # The repository path can't contain "."
+    data = {
+        "namespace_id": current_app.config["IOC_REPOSITORY_GROUP_ID"],
+        "visibility": current_app.config["IOC_REPOSITORY_VISIBILITY"],
+        "name": project_name,
+        "path": project_name.lower().replace(".", "_"),
+    }
+    response = requests.post(api_url + "/projects", headers=headers, json=data)
+    try:
+        payload = response.json()
+    except ValueError:
+        payload = None
+    if response.status_code != 201:
+        current_app.logger.warning(
+            f"{project_name} repository couldn't be created: {payload}"
+        )
+    else:
+        commit_data = {
+            "branch": "master",
+            "commit_message": "Add .gitlab-ci.yml file",
+            "actions": [
+                {
+                    "action": "create",
+                    "file_path": ".gitlab-ci.yml",
+                    "content": current_app.config["IOC_REPOSITORY_GITLAB_CI_YML"],
+                }
+            ],
+        }
+        response = requests.post(
+            api_url + f"/projects/{payload['id']}/repository/commits",
+            headers=headers,
+            json=commit_data,
+        )
+    return response
diff --git a/app/utils.py b/app/utils.py
index 6a20b77..d9005a8 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -301,6 +301,23 @@ def trigger_inventory_update():
     return task
 
 
+def trigger_ioc_repository_creation(host):
+    """Trigger a job to create an IOC repository on GitLab"""
+    if not current_app.config["IOC_REPOSITORY_CREATION_ENABLED"]:
+        current_app.logger.info(
+            f"IOC repository creation is disabled. No job triggered for {host.fqdn}."
+        )
+    else:
+        current_app.logger.info(
+            f"Launch new job to create the IOC repository: {host.fqdn}"
+        )
+        kwargs = {"func": "create_ioc_repository", "project_name": host.fqdn}
+        task = current_user.launch_task(
+            "create_ioc_repository", queue_name="low", **kwargs
+        )
+        return task
+
+
 def redirect_to_job_status(job_id):
     """
     The answer to a client request, leading it to regularly poll a job status.
diff --git a/docker-compose.override.yml b/docker-compose.override.yml
index 0fe282a..79d4364 100644
--- a/docker-compose.override.yml
+++ b/docker-compose.override.yml
@@ -18,6 +18,7 @@ services:
     environment:
       LOCAL_SETTINGS: /app/settings.cfg
       FLASK_APP: /app/wsgi.py
+      GL_ACCESS_TOKEN: ${GL_ACCESS_TOKEN}
     volumes:
       - .:/app
   postgres:
-- 
GitLab