I am working on an API application that automates the deployments to a Docker Swarm cluster which inspired the this article. The application is using a combination of the API Platform and the EasyAdminBundle, which are the opinionated choices of the Symfony community for the Symfony Flex recipes of “api” and “admin”. One trick that is helpful to know is how to create custom actions in the admin overviews. The final system should automate the deployments as much as possible with the following features:

  • Register and set up domains (using the TransIP API)
  • Deployments on the Docker Swarm cluster
  • Configuring an NGINX proxy to reach the stack

Even though automation is the goal, it is still valuable to be able to use the system manually. One option is to create a front-end application that consumes the API, but that adds complexity and would cost time to create. Therefore, we utilize the EasyAdminBundle which allows us to use CRUD operations on the entities in a responsive interface and the bundle makes it easy to define custom actions on the entities.

Let’s see some code

In this case, we have three actions/operations which we want to execute on the Environment entity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# config/packages/easy_admin.yaml
easy_admin
:
    entities
:
       # List the entity class name you want to manage
        Stack
:
            class
: App\Entity\Stack
        Environment
:
            class
: App\Entity\Environment
            list
:
                actions
:
                    - { name
: 'store_file', type: 'route' }
                    - { name
: 'deploy', type: 'route' }
                    - { name
: 'remove', type: 'route' }
        EnvironmentVariable
:
            class
: App\Entity\EnvironmentVariable
        Instance
:
            class
: App\Entity\Instance
        User
:
            class
: App\Entity\User

Now, we can create a new Admin controller for the action routes. The Environment entity and the EnvironmentService are loaded in automatically when you include the {id} in the path thanks to the autowiring magic of Symfony.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# src/Controller/EnvironmentAdminController.php
namespace App\Controller;

use App\Entity\Environment;
use App\Service\EnvironmentService;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AdminController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;

class EnvironmentAdminController extends Controller
{
    /**
     * @Route(path="admin/environment/{id}/store_file", name = "store_file")
     * @Security("has_role('ROLE_ADMIN')")
     */

    public function storeFile(Environment $environment, EnvironmentService $environmentService)
    {
        $environmentService->saveDockerStackFile($environment);
        return new Response('<html><body>Docker Stack file saved on the filesystem</body></html>');
    }

    /**
     * @Route(path="admin/environment/{id}/deploy", name = "deploy")
     * @Security("has_role('ROLE_ADMIN')")
     */

    public function deploy(Environment $environment, EnvironmentService $environmentService)
    {
        $environmentService->dockerDeployStack($environment);
        return new Response('<html><body>Docker Stack deployed</body></html>');
    }

    /**
     * @Route(path="admin/environment/{id}/remove", name = "remove")
     * @Security("has_role('ROLE_ADMIN')")
     */

    public function remove(Environment $environment, EnvironmentService $environmentService)
    {
        $environmentService->removeDockerStack($environment);
        return new Response('<html><body>Docker Stack removed</body></html>');
    }
}

The result is that these actions appear on the Admin overview for the Environment Entity:

Discussion

The EasyAdminBundle gives a great out of the box experience and gives enough flexibility for customisations. There is however, a potential alternative in the form of The Platform Admin admin component. I did not test the API Platform admin alternative but the main benefit compared to the EasyAdminBundle seems to be that it consumes the API instead of communicating with the application directly.

Consuming the API would avoid potential accidents where the logic in the admin component is not updated correctly with code from the API, however, using the same services mitigates this risk as well. The main issue with the API Platform Admin component seems to be that there is no recipe available for Symfony so it would require a manual installation (in a Dockerfile). Furthermore, it is built with React, making it more difficult to extend than the EasyAdminBundle.


Leave a Reply

Your email address will not be published.