Completed
Push — master ( 9d0750...b73b31 )
by Sebastian
10:06
created

Redis::setup()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 2
Bugs 0 Features 1
Metric Value
dl 0
loc 13
ccs 0
cts 0
cp 0
rs 9.4285
c 2
b 0
f 1
cc 2
eloc 9
nc 2
nop 1
crap 6
1
<?php
2
namespace phpbu\App\Backup\Source;
3
4
use phpbu\App\Backup\Source;
5
use phpbu\App\Backup\Target;
6
use phpbu\App\Cli\Executable;
7
use phpbu\App\Exception;
8
use phpbu\App\Result;
9
use phpbu\App\Util;
10
11
/**
12
 * Tar source class.
13
 *
14
 * @package    phpbu
15
 * @subpackage Backup
16
 * @author     Sebastian Feldmann <[email protected]>
17
 * @copyright  Sebastian Feldmann <[email protected]>
18
 * @license    https://opensource.org/licenses/MIT The MIT License (MIT)
19
 * @link       http://phpbu.de/
20
 * @since      Class available since Release 2.1.12
21
 */
22
class Redis extends SimulatorExecutable implements Simulator
23
{
24
    /**
25
     * Path to executable.
26
     *
27
     * @var string
28
     */
29
    private $pathToRedisCli;
30
31
    /**
32
     * Time to wait for the dump to finish
33
     *
34
     * @var integer
35
     */
36
    private $timeout;
37
38
    /**
39
     * Host to backup
40
     *
41
     * @var string
42
     */
43
    private $host;
44
45
    /**
46
     * Port to connect to
47
     *
48
     * @var int
49
     */
50
    private $port;
51
52
    /**
53
     * Password for authentication
54
     *
55
     * @var string
56
     */
57
    private $password;
58
59
    /**
60
     * Path to the redis rdb directory, for Debian it's /var/lib/redis/{PORT}/dump.rdb
61
     *
62
     * @var string
63
     */
64
    private $pathToRedisData;
65
66
    /**
67
     * Setup.
68
     *
69
     * @see    \phpbu\App\Backup\Source
70
     * @param  array $conf
71
     * @throws \phpbu\App\Exception
72
     */
73
    public function setup(array $conf = [])
74
    {
75
        $this->pathToRedisCli  = Util\Arr::getValue($conf, 'pathToRedisCli');
76
        $this->pathToRedisData = Util\Arr::getValue($conf, 'pathToRedisData');
77
        $this->timeout         = Util\Arr::getValue($conf, 'timeout', 45);
78
        $this->host            = Util\Arr::getValue($conf, 'host');
79
        $this->port            = Util\Arr::getValue($conf, 'port');
80
        $this->password        = Util\Arr::getValue($conf, 'password');
81
82
        if (empty($this->pathToRedisData)) {
83
            throw new Exception('pathToRedisData option is mandatory');
84
        }
85
    }
86
87
    /**
88 5
     * Execute the backup.
89
     *
90 5
     * @see    \phpbu\App\Backup\Source
91 5
     * @param  \phpbu\App\Backup\Target $target
92 5
     * @param  \phpbu\App\Result        $result
93 5
     * @return \phpbu\App\Backup\Source\Status
94 5
     * @throws \phpbu\App\Exception
95 5
     */
96 5
    public function backup(Target $target, Result $result)
97
    {
98 5
        // set uncompressed default MIME type
99 1
        $target->setMimeType('application/octet-stream');
100
101 4
        $redisSave = $this->getExecutable($target);
102
        $redisLast = $this->getRedisLastSave($redisSave);
103
104
        $lastBackupTimestamp = $this->getLastBackupTime($redisLast);
105
106
        $saveResult = $redisSave->run();
107
        $result->debug($saveResult->getCmd());
108
        if (!$saveResult->wasSuccessful()) {
109
            throw new Exception('redis-cli BGSAVE failed:' . $saveResult->getStdErr());
110
        }
111
        // check if the save process is finished
112 3
        $this->isDumpCreatedYet($lastBackupTimestamp, $redisLast);
113
        $this->copyDumpToTarget($target);
114
115 3
        return $this->createStatus($target);
116
    }
117 3
118 3
    /**
119 3
     * Setup the Executable to run the 'tar' command.
120
     *
121 3
     * @param  \phpbu\App\Backup\Target
122
     * @return \phpbu\App\Cli\Executable\RedisCli
123 3
     */
124 3
    public function getExecutable(Target $target)
125 3
    {
126 1
        if (null == $this->executable) {
127
            $this->executable = new Executable\RedisCli($this->pathToRedisCli);
128 2
            $this->executable->backup()
129
                             ->useHost($this->host)
130 1
                             ->usePort($this->port)
131
                             ->usePassword($this->password);
132 1
        }
133
        return $this->executable;
134
    }
135
136
    /**
137
     * Creates a RedisLastSave command from a RedisSave command.
138
     *
139
     * @param  \phpbu\App\Cli\Executable\RedisCli $redis
140
     * @return \phpbu\App\Cli\Executable\RedisCli
141 4
     */
142
    public function getRedisLastSave(Executable\RedisCli $redis)
143 4
    {
144 1
        $redisLast = clone($redis);
145 1
        $redisLast->lastBackupTime();
146 1
        return $redisLast;
147 1
    }
148 1
149 1
    /**
150 1
     * Return last successful save timestamp.
151 4
     *
152
     * @param  \phpbu\App\Cli\Executable\RedisCli $redis
153
     * @return int
154
     * @throws \phpbu\App\Exception
155
     */
156
    private function getLastBackupTime(Executable\RedisCli $redis)
157
    {
158
        $result  = $redis->run();
159
        $output  = $result->getStdOut();
160
        $matches = [];
161 3
        if (!preg_match('#(\(integer\) )?([0-9]+)#i', $output, $matches)) {
162
            throw new Exception('invalid redis-cli LASTSAVE output');
163 3
        }
164 3
        return (int) $matches[2];
165 3
    }
166 3
167
    /**
168
     * Check the dump date and return true if BGSAVE is finished.
169 3
     *
170
     * @param  int                                $lastTimestamp
171
     * @param  \phpbu\App\Cli\Executable\RedisCli $redis
172
     * @return bool
173
     * @throws \phpbu\App\Exception
174
     */
175
    private function isDumpCreatedYet($lastTimestamp, $redis)
176
    {
177
        $i = 0;
178
        while ($this->getLastBackupTime($redis) <= $lastTimestamp) {
179
            if ($i > $this->timeout) {
180 2
                throw new Exception('redis-cli BGSAVE is taking to long, increase timeout');
181
            }
182 2
            $i++;
183 2
            sleep(1);
184 1
        }
185 1
        return true;
186
    }
187 1
188 1
    /**
189 1
     * Copy the redis RDB file to its backup location.
190 1
     *
191
     * @param  \phpbu\App\Backup\Target $target
192
     * @return string
193
     * @throws \phpbu\App\Exception
194
     */
195
    private function copyDumpToTarget(Target $target)
196
    {
197
        if (!file_exists($this->pathToRedisData)) {
198
            throw new Exception('Redis data not found at: \'' . $this->pathToRedisCli . '\'');
199
        }
200 1
        $targetFile = $target->getPathnamePlain();
201
        copy($this->pathToRedisData, $targetFile);
202 1
        return $targetFile;
203
    }
204
205 1
    /**
206 1
     * Create backup status.
207 1
     *
208
     * @param  \phpbu\App\Backup\Target
209
     * @return \phpbu\App\Backup\Source\Status
210
     */
211
    protected function createStatus(Target $target)
212
    {
213
        return Status::create()->uncompressedFile($target->getPathnamePlain());
214
    }
215
}
216