Continuous Integration and Continuous Delivery (CI/CD)

The provided codebase for the Balena Prometheus Exporter, while not explicitly incorporating CI/CD, has a clear foundation for automation.

Workflow:

  1. Build: The Dockerfile defines the process for building the Docker image.

    FROM python:3.10-alpine
              
              WORKDIR /usr/src/app
              
              COPY requirements.txt ./
              RUN pip install --no-cache-dir -r requirements.txt
              
              COPY main.py .
              
              USER nobody
              
              CMD [ "python", "./main.py" ]
              
  2. Test: Tests are provided in tests/test_exporter.py.

    import unittest
              from unittest import mock
              
              from main import BalenaCollector
              
              class TestBalenaCollector(unittest.TestCase):
                  def setUp(self):
                      self.collector = BalenaCollector()
              
                  @mock.patch("main.BalenaCollector.get_fleet_metrics")
                  def test_collect(self, mock_metrics):
                      with mock.patch.object(self.collector, "get_balena_fleets") as mock_fleets:
                          mock_fleets.return_value = ["fleet1", "fleet2"]
                          mock_metrics.side_effect = [("test_fleet", 3), ("test_fleet", 3)]
              
                          result = list(self.collector.collect()[0].samples)
                          expected = [('balena_devices_online', {'fleet_name': 'test_fleet'}, 3),
                                      ('balena_devices_online', {'fleet_name': 'test_fleet'}, 3)]
              
                          result = [(s.name, s.labels, s.value) for s in result]
              
                          self.assertEqual(result, expected)
              
                  def test_get_balena_fleets(self):
                      with mock.patch("main.requests.get") as mock_get:
                          mock_get.return_value.ok = True
                          mock_get.return_value.json.return_value = {"d": [{"id": "fleet1"}, {"id": "fleet2"}]}
                          result = list(self.collector.get_balena_fleets())
                          expected = ["fleet1", "fleet2"]
                          self.assertEqual(result, expected)
              
              
                  def test_get_fleet_metrics(self):
                      with mock.patch("main.requests.get") as mock_get:
                          mock_get.return_value.ok = True
                          mock_get.return_value.json.return_value = {"d": [{"owns__device": 3, "app_name": "test_fleet"}]}
                          result = self.collector.get_fleet_metrics("fleet1")
                          expected = ("test_fleet", 3)
                          self.assertEqual(result, expected)
              
              unittest.main()
              
  3. Deploy: The README.md demonstrates a basic Docker run command for local deployment.

    $ docker build -t balena-exporter .
              $ docker run -d \
              --name balena-exporter \
              -p 8000:8000 \
              -e BALENA_TOKEN= \
              balena-exporter
              

CI/CD Implementation:

GitHub Actions:

Jenkins:

  • Jenkins is a widely used CI/CD server that can be integrated with the project.
  • Jenkins pipelines can be created to automate the build, test, and deployment processes.
  • https://www.jenkins.io/

Balena Cloud:

  • Balena Cloud offers a built-in CI/CD system.
  • It provides a workflow for building Docker images, running tests, and deploying them to devices.
  • https://www.balena.io/docs/

Considerations:

  • Security: Securely store the BALENA_TOKEN and other sensitive credentials.
  • Testing: Expand the test suite to cover more code paths and edge cases.
  • Monitoring: Integrate with monitoring tools to track the performance and health of the exporter.
  • Deployment Strategy: Choose the appropriate deployment strategy, considering factors like blue-green deployments or canary releases.

Example CI/CD Pipeline (GitHub Actions):

name: CI/CD
          on:
            push:
              branches:
                - main
          
          jobs:
            build:
              runs-on: ubuntu-latest
              steps:
                - uses: actions/checkout@v3
                - name: Build Docker Image
                  run: docker build -t balena-exporter .
                - name: Run Tests
                  run: pytest tests/test_exporter.py
                - name: Push Image to Registry
                  run: docker push balena-exporter
            deploy:
              runs-on: ubuntu-latest
              needs: build
              steps:
                - uses: actions/checkout@v3
                - name: Deploy to Balena Cloud
                  uses: balena-io/[email protected]
                  with:
                    balenaToken: ${{ secrets.BALENA_TOKEN }}
                    applicationId: ${{ secrets.APPLICATION_ID }}
                    release: balena-exporter
          

Top-Level Directory Explanations

tests/ - This directory contains all the unit and integration tests for the project. It includes the __init__.py file which makes it a package, and specific test files like test_exporter.py.