Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 46
DockerScaler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 7
506
0.00% covered (danger)
0.00%
0 / 46
 __construct
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 manualScale
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 10
 run
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 10
 mustScaleUp
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 3
 mustScaleDown
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 3
 scaleServices
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 14
 isProviderClass
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 4
1<?php
2
3namespace Qmp\Laravel\DockerScale\Scaler;
4
5use Qmp\Laravel\DockerScale\Models\DockerScalerConfig;
6use Symfony\Component\Process\Exception\ProcessFailedException;
7use Symfony\Component\Process\Process;
8use Illuminate\Support\Str;
9use Qmp\Laravel\DockerScale\Scaler\Providers\ProviderAbstract;
10
11use Illuminate\Support\Facades\Log;
12
13class DockerScaler
14{
15    protected $cmdScale = 'docker service scale -d ';
16
17    protected $providersNamespace = '';
18
19    protected $servicesScaledUp = [];
20
21    protected $servicesScaledDown = [];
22
23    public function __construct()
24    {
25        $this->providersNamespace = __NAMESPACE__ . '\\Providers\\';
26    }
27
28    /**
29     * @param array $services
30     * @throws \Exception
31     */
32    public function manualScale(array $services): void
33    {
34        foreach ($services as $service => $replica) {
35
36            $serviceFullName = exec('docker service ls -- | grep "' . $service . '" | awk \'{print $2 }\'');
37
38            if (strlen($serviceFullName) == 0) {
39                throw new \Exception($service . ' not found in docker service ls');
40            }
41
42            if ($replica == 1) {
43                $this->servicesScaledUp[$serviceFullName] = 0;
44            }
45
46            if ($replica == 0) {
47                $this->servicesScaledDown[$serviceFullName] = 1;
48            }
49        }
50
51        $this->scaleServices();
52    }
53
54    public function run()
55    {
56        $configs = DockerScalerConfig::where('active', 1)->get();
57        foreach ($configs as $config) {
58            $serviceName = $config->service->name;
59            $totalReplicas = $config->service->total_replicas;
60            if ($this->mustScaleUp($config)) {
61                $this->servicesScaledUp[$serviceName] = $totalReplicas;
62            } else if ($this->mustScaleDown($config)) {
63                $this->servicesScaledDown[$serviceName] = $totalReplicas;
64            }
65        }
66
67        $this->scaleServices();
68    }
69
70    protected function mustScaleUp(DockerScalerConfig $config): bool
71    {
72        if ($className = $this->isProviderClass($config)) {
73            return $className::mustScaleUp($config);
74        }
75
76        return false;
77    }
78
79    protected function mustScaleDown(DockerScalerConfig $config): bool
80    {
81        if ($className = $this->isProviderClass($config)) {
82            return $className::mustScaleDown($config);
83        }
84
85        return false;
86    }
87
88    protected function scaleServices(): void
89    {
90        $cmdServices = [];
91        foreach ($this->servicesScaledUp as $service => $replicas) {
92            $newTotalReplicas = $replicas + 1;
93            Log::debug('scale up : ' . $service . '->' . $newTotalReplicas);
94            $cmdServices[] = $service . '=' . $newTotalReplicas;
95        }
96
97        foreach ($this->servicesScaledDown as $service => $replicas) {
98            if (!isset($this->servicesScaledUp[$service])) {
99                $newTotalReplicas = $replicas - 1;
100                Log::debug('scale down : ' . $service . '->' . $newTotalReplicas);
101                $cmdServices[] = $service . '=' . $newTotalReplicas;
102            }
103        }
104
105        if (!empty($cmdServices)) {
106            $cmd = $this->cmdScale . implode(' ', $cmdServices);
107            processExec($cmd);
108        }
109    }
110
111    /*    protected function exec(string $cmd)
112        {
113            $process = new Process(explode(' ', $cmd));
114            $process->run();
115
116            // executes after the command finishes
117            if (!$process->isSuccessful()) {
118                throw new ProcessFailedException($process);
119            }
120
121            return $process->getOutput();
122        }*/
123
124    protected function isProviderClass(DockerScalerConfig $config)
125    {
126        $className = $this->providersNamespace . Str::studly($config->type) . 'Provider';
127        if (class_exists($className) && is_subclass_of($className, ProviderAbstract::class)) {
128            return $className;
129        }
130
131        return false;
132    }
133}