Completed
Push — master ( 4f1d49...8186c0 )
by Sebastian
07:15
created

Redis::createStatus()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
ccs 0
cts 0
cp 0
cc 1
eloc 3
nc 1
nop 1
crap 2
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');
110
        }
111
        $this->isDumpCreatedYet($lastBackupTimestamp, $redisLast);
112 3
113
        return $this->createStatus($target);
114
    }
115 3
116
    /**
117 3
     * Setup the Executable to run the 'tar' command.
118 3
     *
119 3
     * @param  \phpbu\App\Backup\Target
120
     * @return \phpbu\App\Cli\Executable\RedisCli
121 3
     */
122
    public function getExecutable(Target $target)
123 3
    {
124 3
        if (null == $this->executable) {
125 3
            $this->executable = new Executable\RedisCli($this->pathToRedisCli);
126 1
            $this->executable->backup()
127
                             ->useHost($this->host)
128 2
                             ->usePort($this->port)
129
                             ->usePassword($this->password);
130 1
        }
131
        return $this->executable;
132 1
    }
133
134
    /**
135
     * Creates a RedisLastSave command from a RedisSave command.
136
     *
137
     * @param  \phpbu\App\Cli\Executable\RedisCli $redis
138
     * @return \phpbu\App\Cli\Executable\RedisCli
139
     */
140
    public function getRedisLastSave(Executable\RedisCli $redis)
141 4
    {
142
        $redisLast = clone($redis);
143 4
        $redisLast->lastBackupTime();
144 1
        return $redisLast;
145 1
    }
146 1
147 1
    /**
148 1
     * Return last successful save timestamp.
149 1
     *
150 1
     * @param  \phpbu\App\Cli\Executable\RedisCli $redis
151 4
     * @return int
152
     * @throws \phpbu\App\Exception
153
     */
154
    private function getLastBackupTime(Executable\RedisCli $redis)
155
    {
156
        $result  = $redis->run();
157
        $output  = $result->getStdOut();
158
        $matches = [];
159
        if (!preg_match('#(\(integer\) )?([0-9]+)#i', $output, $matches)) {
160
            throw new Exception('invalid redis-cli LASTSAVE output');
161 3
        }
162
        return (int) $matches[2];
163 3
    }
164 3
165 3
    /**
166 3
     * Check the dump date and return true if BGSAVE is finished.
167
     *
168
     * @param  int $lastTimestamp
169 3
     * @param  \phpbu\App\Cli\Executable\RedisCli $redis
170
     * @return bool
171
     * @throws \phpbu\App\Exception
172
     */
173
    private function isDumpCreatedYet($lastTimestamp, $redis)
174
    {
175
        $i = 0;
176
        while ($this->getLastBackupTime($redis) <= $lastTimestamp) {
177
            if ($i > $this->timeout) {
178
                throw new Exception('redis-cli BGSAVE is taking to long, increase timeout');
179
            }
180 2
            $i++;
181
            sleep(1);
182 2
        }
183 2
        return true;
184 1
    }
185 1
186
    /**
187 1
     * Copy the redis RDB file to its backup location.
188 1
     *
189 1
     * @param  \phpbu\App\Backup\Target $target
190 1
     * @return string
191
     * @throws \phpbu\App\Exception
192
     */
193
    private function copyDumpToTargetDir(Target $target)
194
    {
195
        if (!file_exists($this->pathToRedisData)) {
196
            throw new Exception('Redis data not found at: \'' . $this->pathToRedisCli . '\'');
197
        }
198
        $targetFile = $target->getPathnamePlain();
199
        copy($this->pathToRedisData, $targetFile);
200 1
        return $targetFile;
201
    }
202 1
203
    /**
204
     * Create backup status.
205 1
     *
206 1
     * @param  \phpbu\App\Backup\Target
207 1
     * @return \phpbu\App\Backup\Source\Status
208
     */
209
    protected function createStatus(Target $target)
210
    {
211
        $pathToDump = $this->copyDumpToTargetDir($target);
212
213
        return Status::create()->uncompressed($pathToDump);
214
    }
215
}
216