From 6d359356c05728a9025d0a2ad86d386658dc0e3d Mon Sep 17 00:00:00 2001
From: Andre Sterba <andre.sterba@stud.h-da.de>
Date: Thu, 10 Mar 2022 12:17:42 +0000
Subject: [PATCH] Provide basic Makefile

---
 .gitignore                                 |   3 +
 .gitlab/ci/.build-container.yml            | 108 ++++++------
 .gitlab/ci/.build-release.yml              | 153 +++++++++--------
 .gitlab/ci/.code-quality-ci.yml            |  23 +--
 .gitlab/ci/.containerlab-ci.yml            | 182 +++++++++++----------
 .gitlab/ci/.deploy-k8s.yml                 | 106 ++++++------
 .gitlab/ci/.golangci-config/.golangci.yml  |  64 ++++----
 .gitlab/ci/.integration-test.yml           |  56 +++----
 .gitlab/ci/.security-and-compliance-ci.yml |  60 +++----
 .gitlab/ci/.test.yml                       |  54 +++---
 .gitlab/ci/.uml-autogen-ci.yml             |  36 ++--
 .gitlab/ci/scripts/generate_octet.sh       |   1 -
 Makefile                                   |  64 ++++++++
 13 files changed, 485 insertions(+), 425 deletions(-)
 create mode 100644 Makefile

diff --git a/.gitignore b/.gitignore
index 016de7ce1..d5a3ac156 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,9 @@ configs/gosdn.toml
 api/api_test.toml
 debug.test
 
+# developer tools
+build-tools/
+
 # test files
 report.xml
 nucleus/util/proto/*_test
diff --git a/.gitlab/ci/.build-container.yml b/.gitlab/ci/.build-container.yml
index d948433ab..54b27e2df 100644
--- a/.gitlab/ci/.build-container.yml
+++ b/.gitlab/ci/.build-container.yml
@@ -1,59 +1,59 @@
 .build: &build
-  stage: build
-  image:
-    name: gcr.io/kaniko-project/executor:debug
-    entrypoint: [ "" ]
-  variables:
-    TAG: $CI_COMMIT_BRANCH
-  before_script:
-      # replace all slashes in the tag with hyphen, because slashes are not allowed in tags
-    - TAG=${TAG//\//-}
-    - mkdir -p /kaniko/.docker
-    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"},\"$CI_DEPENDENCY_PROXY_SERVER\":{\"username\":\"$CI_DEPENDENCY_PROXY_USER\",\"password\":\"$CI_DEPENDENCY_PROXY_TOKEN\"}}}" > /kaniko/.docker/config.json
-  needs: []
+    stage: build
+    image:
+        name: gcr.io/kaniko-project/executor:debug
+        entrypoint: [""]
+    variables:
+        TAG: $CI_COMMIT_BRANCH
+    before_script:
+        # replace all slashes in the tag with hyphen, because slashes are not allowed in tags
+        - TAG=${TAG//\//-}
+        - mkdir -p /kaniko/.docker
+        - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"},\"$CI_DEPENDENCY_PROXY_SERVER\":{\"username\":\"$CI_DEPENDENCY_PROXY_USER\",\"password\":\"$CI_DEPENDENCY_PROXY_TOKEN\"}}}" > /kaniko/.docker/config.json
+    needs: []
 
 build-testing-image:
-  rules:
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-      variables:
-        TAG: latest
-    - if: $CI_COMMIT_BRANCH == "develop"
-      variables:
-        TAG: $CI_COMMIT_BRANCH
-        BUILDARGS: -race
-    - when: always
-  script:
-    - /kaniko/executor
-      --cache=true
-      --context "$CI_PROJECT_DIR"
-      --dockerfile "Dockerfile"
-      --build-arg "GOLANG_VERSION=$GOLANG_VERSION"
-      --build-arg "BUILDARGS=$BUILDARGS"
-      --build-arg "GITLAB_PROXY=${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/"
-      --destination "$GOSDN_TESTING_IMAGE"
-      --target "installer"
-  <<: *build
+    rules:
+        - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+          variables:
+              TAG: latest
+        - if: $CI_COMMIT_BRANCH == "develop"
+          variables:
+              TAG: $CI_COMMIT_BRANCH
+              BUILDARGS: -race
+        - when: always
+    script:
+        - /kaniko/executor
+          --cache=true
+          --context "$CI_PROJECT_DIR"
+          --dockerfile "Dockerfile"
+          --build-arg "GOLANG_VERSION=$GOLANG_VERSION"
+          --build-arg "BUILDARGS=$BUILDARGS"
+          --build-arg "GITLAB_PROXY=${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/"
+          --destination "$GOSDN_TESTING_IMAGE"
+          --target "installer"
+    <<: *build
 
 build-image:
-  rules:
-    - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
-      when: never
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-      variables:
-        TAG: $CI_DEFAULT_BRANCH
-    - if: $CI_COMMIT_BRANCH == "develop"
-      variables:
-        TAG: develop
-        BUILDARGS: -race
-    - when: always
-  script:
-    - /kaniko/executor
-      --cache=true
-      --context "$CI_PROJECT_DIR"
-      --dockerfile "Dockerfile"
-      --build-arg "GOLANG_VERSION=$GOLANG_VERSION"
-      --build-arg "BUILDARGS=$BUILDARGS"
-      --build-arg "GITLAB_PROXY=${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/"
-      --destination "$GOSDN_IMAGE"
-      --destination "$CI_REGISTRY_IMAGE:$TAG"
-  <<: *build
+    rules:
+        - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
+          when: never
+        - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+          variables:
+              TAG: $CI_DEFAULT_BRANCH
+        - if: $CI_COMMIT_BRANCH == "develop"
+          variables:
+              TAG: develop
+              BUILDARGS: -race
+        - when: always
+    script:
+        - /kaniko/executor
+          --cache=true
+          --context "$CI_PROJECT_DIR"
+          --dockerfile "Dockerfile"
+          --build-arg "GOLANG_VERSION=$GOLANG_VERSION"
+          --build-arg "BUILDARGS=$BUILDARGS"
+          --build-arg "GITLAB_PROXY=${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/"
+          --destination "$GOSDN_IMAGE"
+          --destination "$CI_REGISTRY_IMAGE:$TAG"
+    <<: *build
diff --git a/.gitlab/ci/.build-release.yml b/.gitlab/ci/.build-release.yml
index e671eb527..a741ead91 100644
--- a/.gitlab/ci/.build-release.yml
+++ b/.gitlab/ci/.build-release.yml
@@ -1,86 +1,85 @@
 .build-release-docker: &build-release-docker
-  stage: build-release
-  image:
-    name: gcr.io/kaniko-project/executor:debug
-    entrypoint: [ "" ]
-  rules:
-    # TODO: Implement later when we need it
-    #- if: $CI_COMMIT_TAG =~ .+ && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-    #  variables:
-    #    TAG: $CI_COMMIT_TAG
-    #  when: on_success
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-      variables:
-        TAG: $CI_DEFAULT_BRANCH
-      when: on_success
-    - when: never
-  before_script:
-      # replace all slashes in the tag with hyphen, because slashes are not allowed in tags
-    - TAG=${TAG//\//-}
-    - mkdir -p /kaniko/.docker
-    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"},\"$CI_DEPENDENCY_PROXY_SERVER\":{\"username\":\"$CI_DEPENDENCY_PROXY_USER\",\"password\":\"$CI_DEPENDENCY_PROXY_TOKEN\"}}}" > /kaniko/.docker/config.json
+    stage: build-release
+    image:
+        name: gcr.io/kaniko-project/executor:debug
+        entrypoint: [""]
+    rules:
+        # TODO: Implement later when we need it
+        #- if: $CI_COMMIT_TAG =~ .+ && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+        #  variables:
+        #    TAG: $CI_COMMIT_TAG
+        #  when: on_success
+        - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+          variables:
+              TAG: $CI_DEFAULT_BRANCH
+          when: on_success
+        - when: never
+    before_script:
+        # replace all slashes in the tag with hyphen, because slashes are not allowed in tags
+        - TAG=${TAG//\//-}
+        - mkdir -p /kaniko/.docker
+        - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"},\"$CI_DEPENDENCY_PROXY_SERVER\":{\"username\":\"$CI_DEPENDENCY_PROXY_USER\",\"password\":\"$CI_DEPENDENCY_PROXY_TOKEN\"}}}" > /kaniko/.docker/config.json
 
 build-release-image:
-  script:
-    - /kaniko/executor
-      --cache=true
-      --context "$CI_PROJECT_DIR"
-      --dockerfile "Dockerfile"
-      --build-arg "GOLANG_VERSION=$GOLANG_VERSION"
-      --build-arg "BUILDARGS=$BUILDARGS"
-      --build-arg "GITLAB_PROXY=${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/"
-      --destination "$GOSDN_IMAGE"
-      --destination "$CI_REGISTRY_IMAGE:$TAG"
-    - /kaniko/executor
-      --cache=true
-      --context "$CI_PROJECT_DIR"
-      --dockerfile "Dockerfile"
-      --build-arg "GOLANG_VERSION=$GOLANG_VERSION"
-      --build-arg "BUILDARGS=$BUILDARGS"
-      --build-arg "GITLAB_PROXY=${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/"
-      --destination "$GOSDN_IMAGE"
-      --destination "$CI_REGISTRY_IMAGE:latest"
-  <<: *build-release-docker
-
+    script:
+        - /kaniko/executor
+          --cache=true
+          --context "$CI_PROJECT_DIR"
+          --dockerfile "Dockerfile"
+          --build-arg "GOLANG_VERSION=$GOLANG_VERSION"
+          --build-arg "BUILDARGS=$BUILDARGS"
+          --build-arg "GITLAB_PROXY=${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/"
+          --destination "$GOSDN_IMAGE"
+          --destination "$CI_REGISTRY_IMAGE:$TAG"
+        - /kaniko/executor
+          --cache=true
+          --context "$CI_PROJECT_DIR"
+          --dockerfile "Dockerfile"
+          --build-arg "GOLANG_VERSION=$GOLANG_VERSION"
+          --build-arg "BUILDARGS=$BUILDARGS"
+          --build-arg "GITLAB_PROXY=${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/"
+          --destination "$GOSDN_IMAGE"
+          --destination "$CI_REGISTRY_IMAGE:latest"
+    <<: *build-release-docker
 
 .build-release-binary: &build-release-binary
-  stage: build-release
-  image:
-    name: ${GITLAB_PROXY}golang:$GOLANG_VERSION
-  rules:
-    # TODO: Implement later when we need it
-    #- if: $CI_COMMIT_TAG =~ .+ && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-    #  variables:
-    #    TAG: $CI_COMMIT_TAG
-    #  when: on_success
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-      variables:
-        TAG: $CI_DEFAULT_BRANCH
-      when: on_success
-    - if: $CI_COMMIT_BRANCH == "develop"
-      variables:
-        TAG: $CI_COMMIT_BRANCH
-      when: on_success
-    - when: never
+    stage: build-release
+    image:
+        name: ${GITLAB_PROXY}golang:$GOLANG_VERSION
+    rules:
+        # TODO: Implement later when we need it
+        #- if: $CI_COMMIT_TAG =~ .+ && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+        #  variables:
+        #    TAG: $CI_COMMIT_TAG
+        #  when: on_success
+        - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+          variables:
+              TAG: $CI_DEFAULT_BRANCH
+          when: on_success
+        - if: $CI_COMMIT_BRANCH == "develop"
+          variables:
+              TAG: $CI_COMMIT_BRANCH
+          when: on_success
+        - when: never
 
 build-release-linux:
-  script:
-    - GOOS=linux go build ./cmd/gosdn
-    - mv gosdn gosdn-$TAG
-    - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file gosdn-$TAG "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/gosdn/$TAG/gosdn-$TAG-linux"'
-  artifacts:
-    paths:
-      - gosdn-$TAG
-    expire_in: 4 weeks
-  <<: *build-release-binary
+    script:
+        - GOOS=linux go build ./cmd/gosdn
+        - mv gosdn gosdn-$TAG
+        - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file gosdn-$TAG "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/gosdn/$TAG/gosdn-$TAG-linux"'
+    artifacts:
+        paths:
+            - gosdn-$TAG
+        expire_in: 4 weeks
+    <<: *build-release-binary
 
 build-release-freebsd:
-  script:
-    - GOOS=freebsd go build ./cmd/gosdn
-    - mv gosdn gosdn-$TAG
-    - - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file gosdn-$TAG "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/gosdn/$TAG/gosdn-$TAG-freebsd"'
-  artifacts:
-    paths:
-      - gosdn-$TAG
-    expire_in: 4 weeks
-  <<: *build-release-binary
\ No newline at end of file
+    script:
+        - GOOS=freebsd go build ./cmd/gosdn
+        - mv gosdn gosdn-$TAG
+        - - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file gosdn-$TAG "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/gosdn/$TAG/gosdn-$TAG-freebsd"'
+    artifacts:
+        paths:
+            - gosdn-$TAG
+        expire_in: 4 weeks
+    <<: *build-release-binary
diff --git a/.gitlab/ci/.code-quality-ci.yml b/.gitlab/ci/.code-quality-ci.yml
index 9a66d4a43..e70efe248 100644
--- a/.gitlab/ci/.code-quality-ci.yml
+++ b/.gitlab/ci/.code-quality-ci.yml
@@ -1,12 +1,13 @@
 code-quality:
-  image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/golangci/golangci-lint:v1.42-alpine
-  stage: analyze
-  script:
-    # writes golangci-lint output to gl-code-quality-report.json
-    - golangci-lint run --config .gitlab/ci/.golangci-config/.golangci.yml --out-format code-climate | tee gl-code-quality-report.json
-  artifacts:
-    reports:
-      codequality: gl-code-quality-report.json
-    paths:
-      - gl-code-quality-report.json
-  needs: []
+    image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/golangci/golangci-lint:v1.42-alpine
+    stage: analyze
+    script:
+        # writes golangci-lint output to gl-code-quality-report.json
+        - apk add --update make jq
+        - make ci-lint
+    artifacts:
+        reports:
+            codequality: gl-code-quality-report.json
+        paths:
+            - gl-code-quality-report.json
+    needs: []
diff --git a/.gitlab/ci/.containerlab-ci.yml b/.gitlab/ci/.containerlab-ci.yml
index f3a06f8ad..a37e7243e 100644
--- a/.gitlab/ci/.containerlab-ci.yml
+++ b/.gitlab/ci/.containerlab-ci.yml
@@ -1,90 +1,93 @@
 variables:
-  CLAB_DIR: "/mnt"
-  CLAB_NAME: "clab${CI_PIPELINE_IID}"
+    CLAB_DIR: "/mnt"
+    CLAB_NAME: "clab${CI_PIPELINE_IID}"
 
 # Templates for Job Types
 .containerlab_deploy: &containerlab_deploy
-  stage: apply
-  rules:
-    - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
-      when: never
-    - when: on_success
-  tags:
-    - shell
-  before_script:
-    - cd ${CLAB_DIR}
-    - export PATH="${PATH}:${CI_PROJECT_DIR}/.gitlab/ci/scripts"
-    - echo "$CI_REGISTRY_PASSWORD" | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
-    - echo ${GOSDN_IMAGE}
-    - docker pull ${GOSDN_IMAGE}
-    - docker pull ${CEOS_IMAGE}
+    stage: apply
+    rules:
+        - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
+          when: never
+        - when: on_success
+    tags:
+        - shell
+    before_script:
+        - cd ${CLAB_DIR}
+        - export PATH="${PATH}:${CI_PROJECT_DIR}/.gitlab/ci/scripts"
+        - echo "$CI_REGISTRY_PASSWORD" | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
+        - echo ${GOSDN_IMAGE}
+        - docker pull ${GOSDN_IMAGE}
+        - docker pull ${CEOS_IMAGE}
 
 .containerlab_template: &containerlab_template
-  image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/alpine:latest
-  stage: build
-  rules:
-    - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
-      when: never
-    - when: on_success
-  before_script:
-    - export PATH="${PATH}:${CI_PROJECT_DIR}/.gitlab/ci/scripts"
-    - firstOctet=$(generate_octet.sh $CI_COMMIT_SHA)
-    - secondOctet=$(generate_octet.sh $CI_PIPELINE_ID)
-    - export CLAB_MGMT_SUBNET="172.$firstOctet.$secondOctet.0/24"
-  script:
-    - |
-      sed -e "s|@@CEOS_CONTAINER_IMAGE@@|${CEOS_IMAGE}|g" \
-          -e "s|@@GOSDN_CONTAINER_IMAGE@@|${GOSDN_IMAGE}|g" \
-          -e "s|@@CLAB_NAME@@|${CLAB_NAME}|g" \
-          -e "s|@@CLAB_MGMT_SUBNET@@|${CLAB_MGMT_SUBNET}|g" \
-          ${CLAB_TEMPLATE} > ${CLAB_NAME}.clab.yml
-    - cat ${CLAB_NAME}.clab.yml
-  artifacts:
-    name: ${CLAB_NAME}
-    paths:
-      - ${CLAB_NAME}.clab.yml
+    image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/alpine:latest
+    stage: build
+    rules:
+        - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
+          when: never
+        - when: on_success
+    before_script:
+        - export PATH="${PATH}:${CI_PROJECT_DIR}/.gitlab/ci/scripts"
+        - firstOctet=$(generate_octet.sh $CI_COMMIT_SHA)
+        - secondOctet=$(generate_octet.sh $CI_PIPELINE_ID)
+        - export CLAB_MGMT_SUBNET="172.$firstOctet.$secondOctet.0/24"
+    script:
+        - |
+            sed -e "s|@@CEOS_CONTAINER_IMAGE@@|${CEOS_IMAGE}|g" \
+                -e "s|@@GOSDN_CONTAINER_IMAGE@@|${GOSDN_IMAGE}|g" \
+                -e "s|@@CLAB_NAME@@|${CLAB_NAME}|g" \
+                -e "s|@@CLAB_MGMT_SUBNET@@|${CLAB_MGMT_SUBNET}|g" \
+                ${CLAB_TEMPLATE} > ${CLAB_NAME}.clab.yml
+        - cat ${CLAB_NAME}.clab.yml
+    artifacts:
+        name: ${CLAB_NAME}
+        paths:
+            - ${CLAB_NAME}.clab.yml
 
 # JOBS
 containerlab:template:integration:
-  extends: .containerlab_template
-  variables:
-    CLAB_TEMPLATE: "${CI_PROJECT_DIR}/test/containerlab/int01.clab.tmpl.yml"
-
+    extends: .containerlab_template
+    variables:
+        CLAB_TEMPLATE: "${CI_PROJECT_DIR}/test/containerlab/int01.clab.tmpl.yml"
 
 containerlab:deploy:integration:
-  extends: .containerlab_deploy
-  needs: ["containerlab:template:integration", "build-image"]
-  script:
-    - sudo containerlab deploy --topo ${CI_PROJECT_DIR}/${CLAB_NAME}.clab.yml --reconfigure
-    - |
-      echo -e "\
-      GOSDN_HTTP_PORT=$(docker_host_port 8080 clab-${CLAB_NAME}-gosdn)\n\
-      GOSDN_GRPC_PORT=$(docker_host_port 55055 clab-${CLAB_NAME}-gosdn)\n\
-      CEOS1_PORT=$(docker_host_port 6030 clab-${CLAB_NAME}-ceos1)" \
-      > ${CI_PROJECT_DIR}/build.env
-    - cat ${CI_PROJECT_DIR}/build.env
-  artifacts:
-    reports:
-      dotenv: ${CI_PROJECT_DIR}/build.env
-
+    extends: .containerlab_deploy
+    needs: ["containerlab:template:integration", "build-image"]
+    script:
+        - sudo containerlab deploy --topo ${CI_PROJECT_DIR}/${CLAB_NAME}.clab.yml --reconfigure
+        - |
+            echo -e "\
+            GOSDN_HTTP_PORT=$(docker_host_port 8080 clab-${CLAB_NAME}-gosdn)\n\
+            GOSDN_GRPC_PORT=$(docker_host_port 55055 clab-${CLAB_NAME}-gosdn)\n\
+            CEOS1_PORT=$(docker_host_port 6030 clab-${CLAB_NAME}-ceos1)" \
+            > ${CI_PROJECT_DIR}/build.env
+        - cat ${CI_PROJECT_DIR}/build.env
+    artifacts:
+        reports:
+            dotenv: ${CI_PROJECT_DIR}/build.env
 
 containerlab:destroy:
-  rules:
-    - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
-      when: never
-    - when: always
-  stage: .post
-  tags:
-    - shell
-  needs: ["containerlab:deploy:integration", "integration-test:nucleus", "integration-test:api", "containerlab:template:integration"]
-  before_script:
-    - cd ${CLAB_DIR}
-  script:
-    - sudo containerlab destroy --topo ${CI_PROJECT_DIR}/${CLAB_NAME}.clab.yml
-    - docker volume rm -f ${CLAB_NAME}-volume
-    - docker image rm -f ${GOSDN_IMAGE}
-  allow_failure: true
-
+    rules:
+        - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
+          when: never
+        - when: always
+    stage: .post
+    tags:
+        - shell
+    needs:
+        [
+            "containerlab:deploy:integration",
+            "integration-test:nucleus",
+            "integration-test:api",
+            "containerlab:template:integration",
+        ]
+    before_script:
+        - cd ${CLAB_DIR}
+    script:
+        - sudo containerlab destroy --topo ${CI_PROJECT_DIR}/${CLAB_NAME}.clab.yml
+        - docker volume rm -f ${CLAB_NAME}-volume
+        - docker image rm -f ${GOSDN_IMAGE}
+    allow_failure: true
 
 #containerlab:template:develop:
 #  extends: .containerlab_template
@@ -108,20 +111,19 @@ containerlab:destroy:
 #  rules:
 #    - if: $CI_COMMIT_BRANCH == 'develop' && $CI_NIGHTLY == null
 
-
 containerlab:clean:
-  stage: .post
-  tags:
-    - shell
-  before_script:
-    - cd ${CLAB_DIR}
-  script:
-    - docker kill $(docker ps -q) || true
-    - docker rm $(docker ps -a -q) || true
-    - docker rmi $(docker images | grep 'registry.code.fbi.h-da.de/danet/gosdn ' | awk '{print $3}') || true
-    - sudo rm -rf ${CLAB_DIR}/clab-* || true
-    - sudo sed -i 's|.*clab.*||g' /etc/hosts
-  allow_failure: true
-  rules:
-    - if: $CI_SCHEDULE_CLEAN
-      when: always
+    stage: .post
+    tags:
+        - shell
+    before_script:
+        - cd ${CLAB_DIR}
+    script:
+        - docker kill $(docker ps -q) || true
+        - docker rm $(docker ps -a -q) || true
+        - docker rmi $(docker images | grep 'registry.code.fbi.h-da.de/danet/gosdn ' | awk '{print $3}') || true
+        - sudo rm -rf ${CLAB_DIR}/clab-* || true
+        - sudo sed -i 's|.*clab.*||g' /etc/hosts
+    allow_failure: true
+    rules:
+        - if: $CI_SCHEDULE_CLEAN
+          when: always
diff --git a/.gitlab/ci/.deploy-k8s.yml b/.gitlab/ci/.deploy-k8s.yml
index e2d8e52ba..dc48c9067 100644
--- a/.gitlab/ci/.deploy-k8s.yml
+++ b/.gitlab/ci/.deploy-k8s.yml
@@ -1,64 +1,64 @@
 build:k8s-bot:
-  stage: build
-  image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/golang:$GOLANG_VERSION
-  rules:
-    - if: $CI_COMMIT_BRANCH == "develop"
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-  script:
-    - cd build/cd
-    - go build -o k8s-bot
-  artifacts:
-    name: binary
-    paths:
-      - build/cd/k8s-bot
+    stage: build
+    image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/golang:$GOLANG_VERSION
+    rules:
+        - if: $CI_COMMIT_BRANCH == "develop"
+        - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+    script:
+        - cd build/cd
+        - go build -o k8s-bot
+    artifacts:
+        name: binary
+        paths:
+            - build/cd/k8s-bot
 
 .deploy: &deploy
-  image: 
-    name: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/bitnami/kubectl:latest
-    entrypoint: [""]
-  before_script:
-    - echo "override global before script"
-  variables:
-    K8S_OP: "create"
-  script:
-    - ./build/cd/k8s-bot
+    image:
+        name: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/bitnami/kubectl:latest
+        entrypoint: [""]
+    before_script:
+        - echo "override global before script"
+    variables:
+        K8S_OP: "create"
+    script:
+        - ./build/cd/k8s-bot
 
 deploy:develop:
-  <<: *deploy
-  stage: deploy
-  needs:
-    - job: "build-docker"
-    - job: "build:k8s-bot"
-      artifacts: true
-  rules:
-    - if: $CI_COMMIT_BRANCH == "develop" && $CI_NIGHTLY == null
+    <<: *deploy
+    stage: deploy
+    needs:
+        - job: "build-docker"
+        - job: "build:k8s-bot"
+          artifacts: true
+    rules:
+        - if: $CI_COMMIT_BRANCH == "develop" && $CI_NIGHTLY == null
 
 deploy:latest:
-  <<: *deploy
-  stage: deploy
-  needs:
-    - job: "build-docker"
-    - job: "build:k8s-bot"
-      artifacts: true
-  rules:
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_NIGHTLY == null
+    <<: *deploy
+    stage: deploy
+    needs:
+        - job: "build-docker"
+        - job: "build:k8s-bot"
+          artifacts: true
+    rules:
+        - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_NIGHTLY == null
 
 deploy:nightly:mainline:
-  <<: *deploy
-  stage: deploy
-  needs:
-    - job: "build-docker"
-    - job: "build:k8s-bot"
-      artifacts: true
-  rules:
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_NIGHTLY == "mainline"
+    <<: *deploy
+    stage: deploy
+    needs:
+        - job: "build-docker"
+        - job: "build:k8s-bot"
+          artifacts: true
+    rules:
+        - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_NIGHTLY == "mainline"
 
 deploy:nightly:develop:
-  <<: *deploy
-  stage: deploy
-  needs:
-    - job: "build-docker"
-    - job: "build:k8s-bot"
-      artifacts: true
-  rules:
-    - if: $CI_COMMIT_BRANCH == "develop" && $CI_NIGHTLY == "mainline"
+    <<: *deploy
+    stage: deploy
+    needs:
+        - job: "build-docker"
+        - job: "build:k8s-bot"
+          artifacts: true
+    rules:
+        - if: $CI_COMMIT_BRANCH == "develop" && $CI_NIGHTLY == "mainline"
diff --git a/.gitlab/ci/.golangci-config/.golangci.yml b/.gitlab/ci/.golangci-config/.golangci.yml
index e9ea64def..53a29c482 100644
--- a/.gitlab/ci/.golangci-config/.golangci.yml
+++ b/.gitlab/ci/.golangci-config/.golangci.yml
@@ -1,40 +1,40 @@
 run:
-  timeout: 10m
-  issues-exit-code: 1
-  # directories to be ignored by linters
-  skip-dirs:
-    - forks
-    - test
-  skip-dirs-default: true
-  skip-files:
-    - http.go
+    timeout: 10m
+    issues-exit-code: 1
+    # directories to be ignored by linters
+    skip-dirs:
+        - forks
+        - test
+    skip-dirs-default: true
+    skip-files:
+        - http.go
 # output settings -> code-climate for GitLab
 output:
-  format: code-climate
-  print-issued-lines: true
-  print-linter-name: true
-  uniq-by-line: true
-  path-prefix: ""
+    format: code-climate
+    print-issued-lines: true
+    print-linter-name: true
+    uniq-by-line: true
+    path-prefix: ""
 # custom settings for linters
 linters-settings:
-  gocyclo:
-    min-complexity: 15
-  golint:
-    min-confidence: 0.8
+    gocyclo:
+        min-complexity: 15
+    golint:
+        min-confidence: 0.8
 # enable the specific needed linters
 linters:
-  disable-all: true
-  enable:
-    - gofmt
-    - goimports
-    - gocyclo
-    - govet
-    - unused
-    - staticcheck
-    - typecheck
-    - revive
-    - whitespace
+    disable-all: true
+    enable:
+        - gofmt
+        - goimports
+        - gocyclo
+        - govet
+        - unused
+        - staticcheck
+        - typecheck
+        - revive
+        - whitespace
 issues:
-  exclude-use-default: false
-  max-issues-per-linter: 0
-  max-same-issues: 0
+    exclude-use-default: false
+    max-issues-per-linter: 0
+    max-same-issues: 0
diff --git a/.gitlab/ci/.integration-test.yml b/.gitlab/ci/.integration-test.yml
index dc80d9815..b6577b495 100644
--- a/.gitlab/ci/.integration-test.yml
+++ b/.gitlab/ci/.integration-test.yml
@@ -1,36 +1,30 @@
 .integration-test: &integration-test
-  image: $GOSDN_TESTING_IMAGE
-  stage: integration-test
-  rules:
-    - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
-      when: never
-    - when: on_success
-  needs:
-    - job: "containerlab:deploy:integration"
-  tags:
-    - integration-test-docker
-  variables:
-    GOSDN_LOG: "nolog"
-    GOSDN_TEST_API_ENDPOINT: "141.100.70.178:${GOSDN_GRPC_PORT}"
-    CEOS_TEST_ENDPOINT: "141.100.70.178:${CEOS1_PORT}"
-    GOSDN_TEST_USER: "admin"
-    GOSDN_TEST_PASSWORD: "admin"
-
+    image: $GOSDN_TESTING_IMAGE
+    stage: integration-test
+    rules:
+        - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
+          when: never
+        - when: on_success
+    needs:
+        - job: "containerlab:deploy:integration"
+    tags:
+        - integration-test-docker
+    variables:
+        GOSDN_LOG: "nolog"
+        GOSDN_TEST_API_ENDPOINT: "${CI_SERVER_IP}:${GOSDN_GRPC_PORT}"
+        CEOS_TEST_ENDPOINT: "${CI_SERVER_IP}:${CEOS1_PORT}"
+        GOSDN_TEST_USER: "admin"
+        GOSDN_TEST_PASSWORD: "admin"
 
 integration-test:nucleus:
-  <<: *integration-test
-  script:
-    - ${CI_PROJECT_DIR}/.gitlab/ci/scripts/wait-for-it.sh ${CEOS_TEST_ENDPOINT} -s -t 180 -- echo "CEOS is up"
-    - cd ./test/integration
-    - go test -race -v -run TestGnmi_SetIntegration
-    - go test -race -v -run TestGnmi_GetIntegration
-    - go test -race -v -run TestGnmi_SubscribeIntegration
-    - go test -race -v -run TestGnmi_CapabilitiesIntegration
+    <<: *integration-test
+    script:
+        - ${CI_PROJECT_DIR}/.gitlab/ci/scripts/wait-for-it.sh ${CEOS_TEST_ENDPOINT} -s -t 180 -- echo "CEOS is up"
+        - make integration-test-nucleus
 
 integration-test:api:
-  <<: *integration-test
-  variables:
-    K8S_OP: "getenv"
-  script:
-    - cd ./api
-    - go test -race -v -run TestApiIntegration
+    <<: *integration-test
+    variables:
+        K8S_OP: "getenv"
+    script:
+        - make integration-test-api
diff --git a/.gitlab/ci/.security-and-compliance-ci.yml b/.gitlab/ci/.security-and-compliance-ci.yml
index a42c6695e..97b3cf69a 100644
--- a/.gitlab/ci/.security-and-compliance-ci.yml
+++ b/.gitlab/ci/.security-and-compliance-ci.yml
@@ -1,48 +1,48 @@
 .rules: &rules
-  stage: analyze
-  rules:
-    - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
-      when: never
-    - when: always
-  needs: []
+    stage: analyze
+    rules:
+        - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
+          when: never
+        - when: always
+    needs: []
 
 sast:
-  variables:
-    SAST_ANALYZER_IMAGE_TAG: '2'
-    SAST_EXCLUDED_PATHS: spec, test, tests, tmp
-    SEARCH_MAX_DEPTH: '4'
+    variables:
+        SAST_ANALYZER_IMAGE_TAG: "2"
+        SAST_EXCLUDED_PATHS: spec, test, tests, tmp
+        SEARCH_MAX_DEPTH: "4"
 
 include:
-  - template: Security/SAST.gitlab-ci.yml
-  - template: Dependency-Scanning.gitlab-ci.yml
-  - template: Security/License-Scanning.gitlab-ci.yml
-#  - template: Security/Secret-Detection.gitlab-ci.yml
-  - template: Security/Container-Scanning.gitlab-ci.yml
+    - template: Security/SAST.gitlab-ci.yml
+    - template: Dependency-Scanning.gitlab-ci.yml
+    - template: Security/License-Scanning.gitlab-ci.yml
+    #  - template: Security/Secret-Detection.gitlab-ci.yml
+    - template: Security/Container-Scanning.gitlab-ci.yml
 
 gemnasium-dependency_scanning:
-  <<: *rules
+    <<: *rules
 
 gosec-sast:
-  <<: *rules
+    <<: *rules
 
 license_scanning:
-  <<: *rules
+    <<: *rules
 
 semgrep-sast:
-  <<: *rules
+    <<: *rules
 
 #secret_detection:
 #  <<: *rules
 
 container_scanning:
-  stage: analyze
-  rules:
-    - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
-      when: never
-    - when: always
-  variables:
-    DOCKER_IMAGE: "${GOSDN_IMAGE}"
-    DOCKER_USER: "${CI_REGISTRY_USER}"
-    DOCKER_PASSWORD: "${CI_REGISTRY_PASSWORD}"
-  needs: 
-    - build-image
+    stage: analyze
+    rules:
+        - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
+          when: never
+        - when: always
+    variables:
+        DOCKER_IMAGE: "${GOSDN_IMAGE}"
+        DOCKER_USER: "${CI_REGISTRY_USER}"
+        DOCKER_PASSWORD: "${CI_REGISTRY_PASSWORD}"
+    needs:
+        - build-image
diff --git a/.gitlab/ci/.test.yml b/.gitlab/ci/.test.yml
index 762da8357..226d5ce0b 100644
--- a/.gitlab/ci/.test.yml
+++ b/.gitlab/ci/.test.yml
@@ -1,35 +1,33 @@
 .test: &test
-  image: $GOSDN_TESTING_IMAGE
-  stage: test
-  rules:
-    - when: on_success
-  variables:
-    GOSDN_LOG: "nolog"
-    GOSDN_CHANGE_TIMEOUT: "100ms"
-  before_script:
-    - go get gotest.tools/gotestsum
-  artifacts:
-    when: always
-    reports:
-      junit: report.xml
-  needs:
-    - build-testing-image
+    image: $GOSDN_TESTING_IMAGE
+    stage: test
+    rules:
+        - when: on_success
+    variables:
+        GOSDN_LOG: "nolog"
+        GOSDN_CHANGE_TIMEOUT: "100ms"
+    artifacts:
+        when: always
+        reports:
+            junit: report.xml
+    needs:
+        - build-testing-image
 
 unit-test:
-  script:
-    - gotestsum --junitfile report.xml --format testname -- -short -race $(go list ./... | grep -v /forks/ | grep -v /mocks ) -v -coverprofile=coverage.out 
-  after_script:
-    - go tool cover -func=coverage.out
-  <<: *test
+    script:
+        - make ci-unit-test
+    after_script:
+        - go tool cover -func=coverage.out
+    <<: *test
 
 controller-test:
-  script:
-    - gotestsum --junitfile report.xml --format testname -- -race -v -run TestRun
-  <<: *test
+    script:
+        - make ci-controller-test
+    <<: *test
 
 test-build:
-  artifacts:
-    when: never
-  script:
-    - GOOS=linux go build $BUILDARGS ./cmd/gosdn
-  <<: *test
+    artifacts:
+        when: never
+    script:
+        - GOOS=linux go build $BUILDARGS ./cmd/gosdn
+    <<: *test
diff --git a/.gitlab/ci/.uml-autogen-ci.yml b/.gitlab/ci/.uml-autogen-ci.yml
index adaf8c99d..1a3458969 100644
--- a/.gitlab/ci/.uml-autogen-ci.yml
+++ b/.gitlab/ci/.uml-autogen-ci.yml
@@ -9,23 +9,23 @@ goplantuml:
         WIKI_URL: "${CI_SERVER_PROTOCOL}://project_${CI_PROJECT_ID}_bot3:${GOPLANTUML_TOKEN}@${CI_SERVER_HOST}:${CI_SERVER_PORT}/${CI_PROJECT_PATH}.wiki.git"
 
     script:
-    # install goplantuml
-    - go get github.com/jfeliu007/goplantuml/cmd/goplantuml
-    # remove old wiki folder
-    - rm -rf "/tmp/${CI_PROJECT_NAME}.wiki"
-    # clone and move to wiki folder
-    - cd /tmp
-    - git clone "${WIKI_URL}"
-    - cd "${CI_PROJECT_NAME}.wiki"
+        # install goplantuml
+        - go get github.com/jfeliu007/goplantuml/cmd/goplantuml
+        # remove old wiki folder
+        - rm -rf "/tmp/${CI_PROJECT_NAME}.wiki"
+        # clone and move to wiki folder
+        - cd /tmp
+        - git clone "${WIKI_URL}"
+        - cd "${CI_PROJECT_NAME}.wiki"
 
-    - echo '```plantuml' > "$FILENAME"
-    # run goplantuml
-    - goplantuml "${CI_PROJECT_DIR}/nucleus/" >> "$FILENAME"
-    - echo '```' >> "$FILENAME"
+        - echo '```plantuml' > "$FILENAME"
+        # run goplantuml
+        - goplantuml "${CI_PROJECT_DIR}/nucleus/" >> "$FILENAME"
+        - echo '```' >> "$FILENAME"
 
-    # git commit
-    - git config user.name "$GITLAB_USER_NAME"
-    - git config user.email "$GITLAB_USER_EMAIL"
-    - git add "$FILENAME"
-    - git commit -m "Auto-updated UML diagram via goplantuml CI job"
-    - git push origin "HEAD:master"
+        # git commit
+        - git config user.name "$GITLAB_USER_NAME"
+        - git config user.email "$GITLAB_USER_EMAIL"
+        - git add "$FILENAME"
+        - git commit -m "Auto-updated UML diagram via goplantuml CI job"
+        - git push origin "HEAD:master"
diff --git a/.gitlab/ci/scripts/generate_octet.sh b/.gitlab/ci/scripts/generate_octet.sh
index 9d5dce731..c45a36839 100755
--- a/.gitlab/ci/scripts/generate_octet.sh
+++ b/.gitlab/ci/scripts/generate_octet.sh
@@ -1,7 +1,6 @@
 #!/bin/sh
 
 input=$1
-#trailing_backspace=$2
 
 hashed_value=$(echo $input | sha256sum | awk '{print $1}' )
 
diff --git a/Makefile b/Makefile
new file mode 100644
index 000000000..9d75c33a4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,64 @@
+MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
+MAKEFILE_DIR  := $(dir $(MAKEFILE_PATH))
+TOOLS_DIR:= build-tools
+GOSDN_PRG := $(MAKEFILE_DIR)$(TOOLS_DIR)
+GOBIN := $(GOSDN_PRG)
+
+GOCMD=go
+GOBUILD=$(GOCMD) build
+GOCLEAN=$(GOCMD) clean
+BINARY_NAME=gosdn
+
+all: build
+
+install-tools:
+	@echo Install development tooling
+	mkdir -p $(GOSDN_PRG)
+	go install gotest.tools/gotestsum@v1.7.0
+	go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.42
+	@echo Finished installing development tooling
+
+ci-install-tools:
+	go install gotest.tools/gotestsum@v1.7.0
+
+build:
+	$(GOBUILD) -o $(BINARY_NAME) ./cmd/gosdn
+
+clean:
+	$(GOCLEAN)
+	rm -f $(BINARY_NAME)
+
+start: clean build
+	./$(BINARY_NAME) -l debug
+
+unit-test: install-tools
+	./$(TOOLS_DIR)/gotestsum --junitfile report.xml --format testname -- -short -race $$( go list ./... | grep -v /forks/ | grep -v /mocks ) -v -coverprofile=coverage.out
+
+controller-test: install-tools
+	./$(TOOLS_DIR)/gotestsum --junitfile report.xml --format testname -- -race -v -run TestRun
+
+lint: install-tools
+	./$(TOOLS_DIR)/golangci-lint run --config .gitlab/ci/.golangci-config/.golangci.yml
+
+lint-fix: install-tools
+	./$(TOOLS_DIR)/golangci-lint run --config .gitlab/ci/.golangci-config/.golangci.yml --fix
+
+ci-lint:
+	golangci-lint run --config .gitlab/ci/.golangci-config/.golangci.yml --out-format code-climate | jq -r '.[] | "\(.location.path):\(.location.lines.begin) \(.description)"'
+
+ci-unit-test: ci-install-tools
+	gotestsum --junitfile report.xml --format testname -- -short -race $$( go list ./... | grep -v /forks/ | grep -v /mocks ) -v -coverprofile=coverage.out
+
+ci-controller-test: ci-install-tools
+	gotestsum --junitfile report.xml --format testname -- -race -v -run TestRun
+
+integration-test-nucleus:
+	cd ./test/integration &&\
+	go test -race -v -run TestGnmi_SetIntegration &&\
+	go test -race -v -run TestGnmi_GetIntegration &&\
+	go test -race -v -run TestGnmi_SubscribeIntegration &&\
+	go test -race -v -run TestGnmi_CapabilitiesIntegration
+
+integration-test-api:
+	cd ./api &&\
+	go test -race -v -run TestApiIntegration
-- 
GitLab