Блог Загирова Рустама

Около-интернетные заметки

Проект в Utf8, Mysql-база в Cp1251

| Комментарии

Бывает так, что исходники проекта в utf8, а данные в базе хранятся в cp1251. Чтобы MySQL сам занимался переводом в другую кодировку, но после коннекта выполнить команды:

1
2
$db->query('SET NAMES cp1251');
$db->query('SET CHARACTER SET utf8');

Голосовое напоминание в Ubuntu Gnu/Linux

| Комментарии

Сидя за компом забываешь кое-что сделать, например, выключить плиту. В линуксе это можно реализовать так:

1
echo "нужная комманда" | at 07:00

Примеры: Сказать голосом через 10 минут, что нужно выключить плиту:

1
$ echo "espeak -v ru 'Выключи плиту'" | at `date -d '+10 minute' +%H:%M`

Пора выходить:

1
$ echo "espeak -v ru 'Пока выходить'" | at 20:00

Весело получается ;)

3G интернет в Ubuntu

| Комментарии

Хочу показать насколько просто настраивать 3G интернет в Ubuntu. Имеется huawei E1550 от МТС и Ubuntu 10.10. Хочу обратить внимание, что это всё работает “из коробки”.

3g-internet-in-ubuntu 3g-internet-in-ubuntu 3g-internet-in-ubuntu 3g-internet-in-ubuntu 3g-internet-in-ubuntu 3g-internet-in-ubuntu 3g-internet-in-ubuntu

Всё, интернет подключен ;)

Расширение для Google Chrome: Local Database Storage

| Комментарии

Продолжаем осваивать плагинописание для браузеров на основе chromium и ,соответственно, для google chrome. Текущая тема: научиться использовать local database storage. Local database storage – это база данных, использумая из javascript. Представляет она собой SQLite внутри. Синтаксис привычный для SQL баз. Я буду использовать базу для запоминания всех полученных цитат. Логически все действия можно разделить на 4 операции:

  1. Открытие базы и получение коннекта к ней
  2. Создание таблицы
  3. Запись в таблицу
  4. Чтение из таблицы

Открытие базы и получение коннекта

1
2
3
4
function get_db()
{
  return openDatabase("quotes", "1.0", "A list of quotes.", 2*1024*1024);
}

где quotes – название базы данных 1.0 – версия БД (не изменять) A list of quotes – комментарий 210241024 – максимальный размер базы данных в байтах

Создание таблицы

1
2
3
get_db().transaction(function(tx) {
  tx.executeSql("CREATE TABLE quotes (text TEXT, author TEXT, timestamp REAL)");
});

Запись в таблицу

1
2
3
get_db().transaction(function(tx) {
  tx.executeSql("INSERT INTO quotes (text, author, timestamp) VALUES (?, ?, ?)", [text, author, new Date().getTime()]);
});

Чтение из таблицы

1
2
3
4
5
6
7
get_db().transaction(function(tx) {
  tx.executeSql("SELECT * FROM quotes ORDER BY rowid DESC LIMIT 3", [], function(tx, result) {
    for(var i = 0; i < result.rows.length; i++) {
      alert(result.rows.item(i)['text']);
    }
  }, null)
});

rowid – “скрытое” поле, с типом autoinc

Исходный код на github

Ссылки по теме:

Пишем расширение под Google Chrome

| Комментарии

В связи с последними тенденциями в вебе (в том числе и недавний запуск webstore), возникла идея попрактиковаться в написании плагина под Google Chrome. Решил написать плагин, который бы показывал цитаты с сайта Forismatic.com, после прочтения статьи о написании аналогичного апплета для gnome.

Страница установки плагина.

Требования:

  • показ уведомлений через всплывающее окошко.
  • двуязычность: русский и английский, причём язык должен подцепляться автоматически в зависимости от выставленного в браузере. Но при этом оставлять выбор на каком языке получать цитаты.
  • настройка интервала обновления

Всем начинающим рекомендую сначала прочитать отличный мануал от самого google. В нём всё подробно описано, также есть примеры. Вообще можно увидеть исходники любого приложения: достаточно зайти в папку ~/.config/chromium/Default/Extensions (используется Chromium на Ubuntu).

Весь исходный код моего расширения доступен на гитхабе.

Получать цитаты будем с помощью api forismatic.com в формате json.

Вот такой GET-запрос должен получится: http://api.forismatic.com/api/1.0/?method=getQuote&format=json&key=123456lang=ru

Меняем lang на выбранный язык, а параметр key произвольно.

Теперь перейдём непосредственно к созданию приложения Необходимо создать такую структуру:

  • _locales/en/messages.json #для перевода на английский язык
  • _locales/ru/messages.json #для перевода на русский язык
  • background.html #файл, который выполняется при запуске плагина
  • functions.js #небольшая библиотечка написанных функций
  • icon.png #В связи с запуском webstore рекомендую использовать иконки по-качественней
  • manifest.json #файл описания плагина
  • options.html #html-файл настроек

Сначала создаём файлы переводы.

_locales/en/messages.json

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
{
  "extName": {
    "message": "Forismatic quotes"
  },
  "extDescr": {
    "message": "Show quote from Forismatic.com"
  },
  "language": {
    "message": "Language"
  },
  "refresh": {
    "message": "Time refresh"
  },
  "save": {
    "message": "Save"
  },
  "options": {
    "message": "Options"
  },
  "options_saved": {
    "message": "Options saved"
  },
  "minutes": {
    "message": "in minutes"
  }
}

_locales/ru/messages.json

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
{
  "extName": {
    "message": "Forismatic цицаты"
  },
  "extDescr": {
    "message": "Показывает цитаты с сайта Forismatic.com"
  },
  "language": {
    "message": "Язык"
  },
  "refresh": {
    "message": "Время обновления"
  },
  "save": {
    "message": "Сохранить"
  },
  "options": {
    "message": "Опции"
  },
  "options_saved": {
    "message": "Настройки сохранены"
  },
  "minutes": {
    "message": "в минутах"
  }
}

Теперь можно описать расширение в манифесте

manifest.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "default_locale":"en",
  "name": "__MSG_extName__",
  "description": "__MSG_extDescr__",
  "version": "0.1.1",
  "background_page": "background.html",
  "options_page": "options.html",
  "permissions": [
    "notifications",
    "http://api.forismatic.com/*"
  ],
  "icons": {
    "128": "icon.png"
  }
}

Параметр default_locale определяет стандартную локаль.

Параметр name описывает имя расширения. Мы его задаём в зависимости от языка, используя ранее описанную переменную в файлах локализации extName, предварительно обернув её: MSG_extName Также поступим и с параметром description. Кстати, при отображении расширения на https://chrome.google.com/extensions/ или на https://chrome.google.com/webstore будет тоже подставляться значение в зависимости от выбранного языка.

В значении version указываем версию плагина (причём при заливке новых версий, она должна быть больше предыдущей, что логично). Chrome будет раз в несколько часов и при загрузке проверять обновление плагина и автоматически обновлять его.

Параметры background_page и options_page указывают на страницу, выполняющуюся в фоне, и страницу настроек. В параметре permissions передаем массив, в котором описываем, что может делать расширение. Мы включили доступ к уведомлениям (notifications) и доступ к сайту api (http://api.forismatic.com/*).

С параметром icons всё понятно.

Библиотека функций для javascript

functions.js

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
44
45
46
47
//"Главная" функция, которая будет показывать всплывающее окно с цитатой
function main()
{
  req = new XMLHttpRequest();
  req.onload = function () {
    var doc = req.responseText;
    if (doc) {
      //Хак, позволяющий получить json как объект
      var json = eval('(' + doc + ')');
      author = json.quoteAuthor;
      text = json.quoteText;
      link = json.quoteLink;
      //Можно создать запись в логе
      console.log(author + ' ' + text);
      //Показываем цитату
      showNotification(author, text);
    }
  };
  req.open("GET", "http://api.forismatic.com/api/1.0/?method=getQuote&format=json&key=" + Math.random()*1000000 + "&lang=" + localStorage['lang'], true);
  req.send(null);
}
//Хелпер для показа всплывающего окна
function showNotification(title, text)
{
  var notification = webkitNotifications.createNotification(
    '',
    title,
    text
  );
  notification.show();
  //Убираем окно через 15 секунд
  window.setInterval(function() {
    notification.cancel();
  }, 15000);
}
//Глобальная переменная для сохранения setInterval
var interval;
//Функция для запуска, которая через выставленное время в настройках будет запускать функцию main. Также запускается при измении времени обновления
function init() {
  if ( ! localStorage['refresh'])
    return;
  window.clearInterval(interval)
  interval = window.setInterval(function() {
    main();
  }, localStorage['refresh'] * 60000);
  console.log(localStorage['refresh'] * 60000);
}

Файл, который выполняется при запуске плагина.

background.html

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
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<script src="functions.js"></script>
<script>

//Поставить дефолтные значения при первом запуске
if( ! localStorage['refresh'])
{
  localStorage['refresh'] = 30;
}
if( ! localStorage['lang'])
{
  //Получаем язык, используемый в браузере
  var lang = chrome.i18n.getMessage("@@ui_locale");
  if (lang == 'ru')
    localStorage['lang'] = "ru";
  else
    localStorage['lang'] = "en";
}

//Добавляем listener, для приёма вызова со страницы опций, чтобы обновить время обновления цитат
chrome.extension.onRequest.addListener(
  function(request, sender, sendResponse) {
    //Проверка, что данные пришли со страницы опций этого же плагина и параметр do равен update
    if (sender.tab.url == chrome.extension.getURL(«options.html») && request.do == "update")
   {
     //Показываем цитату
     main();
      init();
      console.log('Update refresh time from options !');
    }
  }
);
//Показываем цитату при запуске
main();

init();
</script>
</head>
</html>

Настройки плагина

options.html

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
44
45
46
47
48
49
50
51
52
53
54
55
56
<!DOCTYPE html>
<html>
<meta charset=utf-8 />
<head><title>Options</title></head>
<script src="functions.js"></script>
<script type="text/javascript">

//Сохраняем опции
function save_options()
{
  localStorage['lang'] = document.getElementById("lang").value;
  localStorage['refresh'] = parseFloat(document.getElementById("refresh").value);

  //Показываем пользователю, что настройки сохранены
  var status = document.getElementById("status");
  status.innerHTML = chrome.i18n.getMessage("options_saved");
  setTimeout(function() {
    status.innerHTML = "";
  }, 1750);

  //Посылаем запрос на background.html
  chrome.extension.sendRequest({do: "update"});
}

//Восстанавливаем значения из localStorage
function restore_options()
{
  //Выставляем язык
  document.getElementById("locale_refresh").innerHTML = chrome.i18n.getMessage("refresh");
  document.getElementById("locale_minutes").innerHTML = chrome.i18n.getMessage("minutes");
  document.getElementById("locale_language").innerHTML = chrome.i18n.getMessage("language");
  document.getElementById("locale_save").value = chrome.i18n.getMessage("save");
  document.title = chrome.i18n.getMessage("options");

  //Выставляем значения
  //Можно поставить:
  //document.getElementById("lang").value = localStorage["lang"] == undefined ? 'en' : localStorage["lang"];
  //Но в background.html уже выставлены значения по-умолчанию
  document.getElementById("lang").value = localStorage["lang"];
  document.getElementById("refresh").value = localStorage["refresh"];
}
</script>
<body onload="restore_options()">
<form action="" onsubmit="save_options();return false;">
<span id="locale_refresh">Time refresh</span>: <input name="refresh" id="refresh" size="1" /> <span id="locale_minutes">in minutes</span> <br />
<span id="locale_language">Language</span>:
  <select name="lang" id="lang">
    <option value="ru">Русский</option>
    <option value="en">English</option>
  </select>
<br />
<input type="submit" value="Save" id="locale_save"/>
</form>
<div id="status"></div>
</body>
</html>

Cкрипт, который помогает упаковывать расширение:

zip.sh

1
2
#!/bin/sh
zip -r chrome-ext-forismatic.zip ./* -x ".git" -x "zip.sh"

Теперь можно запустить плагин в браузере: Tools » Расширения » Загрузить распакованное расширение Должно появиться всплывающее окно с цитатой. Можно зайти в настройки, сменить язык. При сохранении, должная появиться цитата на другом языке.

После создания и тестирования необходимо выложить плагин. Заходим на страницу: https://chrome.google.com/extensions/developer/dashboard?hl=ru, загружаем архив с плагином, указываем нужные параметры (скриншоты, разделы) и нажимаем Publish changes и наше приложение уже на странице плагинов и на webstore.

Настройка Netbeans для Yii с поддержкой Xdebug, тестов Phpunit и Selenium

| Комментарии

После прочтения книги Agile Web Application Development with Yii 1.1 and PHP5 захотелось рассказать о настройке NetBeans для работы с yii, включая поддержку unit-тестов + тесты через selenium.

Selenium позволяет проводить тесты, почти полностью эмулируя действия через браузер: кликать по ссылкам, вводить текст.

Это очень мощно! Имеются: – Сервер (ip: 192.168.0.3) Debian или другой linux-сервер с настроенным nginx, php5-fpm, xdebug – Компьютер разработчика (ip: 192.168.0.2) Ubuntu 10.10 с установленным NetBeans 7.0m2

Сайт будет располагаться в /var/www/yii/www, а yii в /var/www/yii-lib/yii

Действия на сервере Создаём папку, где будет располагаться сайт.

1
2
3
mkdir -p /var/www/yii/{logs,tmp,www}
chown www-data.www-data /var/www/yii/{tmp,www}
chmod 0700 /var/www/yii/{tmp,www}

Создаём конфиг для php5-fpm. Файл: /etc/php5/fpm/pool.d/yii.conf

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
[yii]
listen = /var/run/php5-fpm/yii.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

user = www-data
group = www-data

pm = dynamic
pm.max_children = 50
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 5
pm.max_requests = 500

pm.status_path = /status
ping.path = /ping
ping.response = pong
; Это значение стоит увеличить при активном использовании xdebug, иначе скрипт отвалится. Также нужно соответственно изменить в nginx параметр fastcgi_read_timeout.
request_terminate_timeout = 5m
request_slowlog_timeout = 2s
slowlog = /var/www/yii/logs/php-fpm.slow.log
chdir = /var/www/yii/www
catch_workers_output = yes

env[TMP] = /var/www/yii/tmp
env[TMPDIR] = /var/www/yii/tmp
env[TEMP] = /var/www/yii/tmp

php_flag[display_errors] = on
php_admin_value[error_log] = /var/www/yii/logs/fpm-php.log
php_admin_flag[log_errors] = on

php_admin_value[open_basedir] = /var/www/yii/www:/var/www/yii/tmp:/var/www/yii/yii
php_admin_value[upload_tmp_dir] = /var/www/yii/tmp
php_admin_value[session.save_path] = /var/www/yii/tmp

Конфигурация сайта для nginx. Файл: /etc/nginx/sites-available/yii

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
upstream yii {
  server unix:/var/run/php5-fpm/yii.sock;
}
server {
  listen   80;
  server_name yii.local;
  server_tokens off;
  server_name_in_redirect  off;

  access_log /var/www/yii/logs/nginx.access.log gzip;
  error_log /var/www/yii/logs/nginx.error.log warn;

  # ОБЯЗАТЕЛЬНО нужно отключить gzip, потому что тесты selenium не будет работать
  gzip off;
  charset utf-8;
  client_max_body_size 1m;
  fastcgi_intercept_errors on;

  # Это значение стоит увеличить при активном использовании xdebug, иначе скрипт отвалится. Также нужно соответственно изменить в php5-fpm параметр request_terminate_timeout.
  fastcgi_read_timeout 600;
  root /var/www/yii/www;
  index index.php index.html index.htm;

  location ~ /.ht
  {
    deny all;
  }

  location ~ /(.git|protected)/ {
    deny all;
  }

  location ~ /(assets|css)/ {
    expires 7d;
  }

  location / {
    try_files $uri $uri/ @php;
  }

  location ~ \.php$ {
    try_files $uri @php;
    fastcgi_pass yii;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME /var/www/yii/www$fastcgi_script_name;
    include fastcgi_params;
  }

  location @php {
    fastcgi_pass yii;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME /var/www/yii/www/index.php;
    fastcgi_param SCRIPT_NAME /index.php;
    fastcgi_param QUERY_STRING q=$uri&$args;
    include fastcgi_params;
  }
}

Включаем сайт, путём линкования в папку sites-enabled.

Скачивание yii

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Скачиваем исходники yii
cd /usr/local/src
wget http://yii.googlecode.com/files/yii-1.1.4.r2429.tar.gz
mkdir -p /var/www/yii-lib/
tar xvfz yii-1.1.4.r2429.tar.gz -C /var/www/yii-lib/
cd /var/www/yii-lib/
# Делаем линк на текущую версию
ln -s yii-1.1.4.r2429 yii
# Меняем пользователя и группу
chown -R www-data.www-data /var/www/yii-lib/
# Создаём веб-приложение
/var/www/yii-lib/yii/framework/yiic webapp /var/www/yii/www/
Create a Web application under '/var/www/yii/www'? [Yes|No] Y
# Меняем пользователя и группу
chown -R www-data.www-data /var/www/yii/www

Устанавливаем php5-xdebug

1
aptitude install php5-xdebug

Настраиваем xdebug для работы отладки /etc/php5/fpm/conf.d/xdebug.ini

1
2
3
4
5
6
7
8
9
10
11
zend_extension=/usr/lib/php5/20090626+lfs/xdebug.so

xdebug.remote_enable=on
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
# Ip-адрес компьютера разработчика
xdebug.remote_host=192.168.0.2
xdebug.remote_log="/var/log/xdebug.log"
# По-умолчанию, используется 9000 порт, но он у меня уже занят.
xdebug.remote_port=9009
xdebug.idekey=netbeans-xdebug

Рестартим fpm и apache:

1
2
invoke-rc.d php5-fpm restart
invoke-rc.d apache2 restart

Настройка компьютера разработчика Я монтирую всю папку /var/www к себе. Это очень удобно, т.к. не нужно скачивать все файлы с сайта. Нужен пакет sshfs.

1
2
3
sudo mkdir -p /mnt/www
sudo chown ВЫ.ВЫ /mnt/www
sshfs www-data@192.168.0.3:/var/www /mnt/www

Прописываем в /etc/hosts

1
192.168.0.3 yii.local

Phpunit

Устанавливаем phpunit

Selenium

Скачиваем Selenium RC. Распаковываем и запускаем:

1
java -jar selenium/selenium-server-1.0.3/selenium-server.jar

Netbeans

Устанавливаем NetBeans (в моём случае это NetBeans 7.0m2). Ставим плагин Selenium Module for PHP (Tools → Plugins → Available Plugins).

Немного настраиваем (Tools → Options):

Php → General Ставим порт 9009 для xdebug и снимаем галку с опции Stop at First Line.

phpunit

Php → Unit Testing Указываем путь до phpunit: /usr/bin/phpunit

phpunit

Miscellaneous → Files Исключаем файл yiilite.php, чтобы при автокомплите подсказки не дублировались ^(yiilite.php|CVS|

phpunit

Создаём новый проект:

phpunit

Указываем пути, название проект. Meta-файлы сохраняем в другой директории.

phpunit

Указываем url проекта: http://yii.local/

phpunit

Теперь вызываем настройки проекта.

Указываем директорию тестов (File → Project properties → Sources → Test Folder)

phpunit

Задаём маппинг пути (File → Project properties → Run configuration → Advanced). Тут не видно, но указано, что /var/www доступно в /mnt/www.

phpunit

Указываем директорию с yii: /mnt/www/yii-lib/yii (File → Project properties → PHP Include Path)

phpunit

Папки, которые будут игнорироваться: /mnt/www/yii/www/protected/runtime (File → Project properties → Ignored Folders → Add Folder).

phpunit

Настройка phpunit (File → Project properties → PhpUnit).

phpunit

Открываем файлы index.php, index-test.php, protected/tests/bootstrap.php и заменяем /yii-1.1.4.r2429/ на /yii/

Удалить из protected/tests/phpunit.xml тест под IE

1
<browser name="Internet Explorer" browser="*iexplore" />

Меняем константу TEST_BASE_URL в файле protected/tests/WebTestCase.php:

1
define('TEST_BASE_URL','http://yii.local/index-test.php');

Правим тест protected/tests/functional/SiteTest.php (баг):

Заменяем $this->clickAndWait(‘link=Logout’); на $this->clickAndWait(‘link=Logout (demo)’);

Теперь можно запустить тест Selenium

phpunit

Появиться окошко выбора папки с этими тестами, указываем: /mnt/www/yii/www/protected/tests/functional

phpunit

Будут всплывать окошки с firefox’ом и в конце концов появиться результат:

phpunit

Phpunit

Можно запускать phpunit тесты прямо с сервера или с рабочего компьютера, но придётся установить php и все используемые библиотеки (php5-pgsql, php5-mysql, etc). Рассмотрим 2-ой вариант. На компьютере разработчика установить phpunit и php5: Для phpunit теста можно установить свои параметры для yii в protected/config/test.php поверх стандартных (например, коннект к базе). Напишем простейший тест для проверки авторизации. Файл protected/tests/unit/AuthTest.php:

1
2
3
4
5
6
7
8
9
10
11
< ?php
class AuthTest extends CTestCase
{
  public function testAuth()
  {
    $login = new LoginForm;
    $login->username = 'demo';
    $login->password = 'demo';
    $this->assertTrue($login->login());
  }
}

Для выполнения теста в Netbeans нажимаем Alt+F6. При этом выполняться все тесты: и phpunit и selenium.

phpunit

Можно выбрать AuthTest.php и нажать Shift+F6, тогда тестирование выполниться только из этого файла. Также можно выполнять phpunit тесты прямо с сервера (aptitude install phpunit):

1
2
3
4
5
6
7
8
9
cd /var/www/yii/www/protected/tests/
phpunit unit/AuthTest.php
PHPUnit 3.4.14 by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 7.25Mb

OK (1 test, 1 assertion)

Также можно написать тесты, не использую базу данных, подменив некоторые таблицы fixtures – ассоциативным массивом, имитирующим записи в таблице.

Дебагинг кода Тесты написаны, теперь можно дебажить код. Открываем index.php, на любой строке добавляем breakpoint (Ctrl+F8). Запускаем дебагинг (Ctrl+F5). Теперь можно “пройтись” по коду клавишами F7 (Step Into) и F8 (Step Over). Это очень помогает понять как же работает сам yii, а так же “качественно” дебажить код, видя текущие переменные, watches, Call stack.

Советую всем прочитать книгу Agile Web Application Development with Yii1.1 and PHP5 всем, кто работает с yii. Книга поднимет уровень и в правильном написании кода для yii, и английского языка.

MySQL запросы через консоль

| Комментарии

Обнаружил очень простой способ выполнения MySQL запросов в Debian. Причём без указания логина и пароля. В Debian’е создаётся системный пользователь debian-sys-maint, от которого и будут идти запросы. Например выборка:

1
echo "SELECT * FROM database.table WHERE id > 10" | mysql --defaults-file=/etc/mysql/debian.cnf -Bs

Можно мониторить нагрузку:

1
watch 'echo "show full processlist" | mysql --defaults-file=/etc/mysql/debian.cnf -Bs'

Apt: управление приоритетами пакетов из Stable, Testing, Unstable, Experimental

| Комментарии

Есть несколько “виртуальных” релизов у debian: stable – на текущий момент это 5 версия (lenny), testing – squeeze (когда он будет выпущен, то перейдёт в релиз stable). Unstable и experimental – экспериментальные релизы, не для продакшена!. Пакет проходит путь из experimental => unstable => testing => stable. Иногда бывают ситуации, когда нужно установить пакеты поновее. Можно, конечно, скачать отдельно deb-пакет и установить его, но в этом случае одни минусы: возможно требуются удовлетворения зависимостей и лишаемся обновлений. А можно рулить приоритетами пакетов в зависимости от релиза (stable, testing, unstable, experimental).

Создаём файлы в /etc/apt/sources.list.d

testing.list

1
2
3
4
deb http://ftp.debian.org/debian/ testing main non-free contrib
deb-src http://ftp.debian.org/debian/ testing main non-free contrib
deb http://security.debian.org/ testing/updates main contrib non-free
deb-src http://security.debian.org/ testing/updates main contrib non-free

unstable.list

1
2
deb http://ftp.debian.org/debian/ sid main non-free contrib
deb-src http://ftp.debian.org/debian/ sid main non-free contrib

experimental.list

1
2
deb http://ftp.debian.org/debian/ experimental main non-free contrib
deb-src http://ftp.debian.org/debian/ experimental main non-free contrib

Теперь настраиваем приоритеты в файле /etc/apt/preferences:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Package: *
Pin: release a=stable
Pin-Priority: 200

Package: *
Pin: release a=testing
Pin-Priority: 201

Package: *
Pin: release a=unstable
Pin-Priority: 199

Package: *
Pin: release a=experimental
Pin-Priority: 198

Этот конфиг означает, что пакеты будут искаться в такой последовательности: testing => stable => unstable => experimental. Вместо Package: * можно указать конкретные пакеты, но, к сожалению, нельзя указать маску пакетов.

Установка пакета и попытка решить зависимости из unstable:

1
aptitude -t unstable install <package>

Установка пакета и попытка решить зависимости из релиза с наивысшим приоритетом:

1
aptitude install <package>/unstable

Ссылки по теме: http://www.debian.org/doc/manuals/apt-howto/ch-apt-get.ru.html

Bash Completion - расширенная автоподстановка

| Комментарии

Очень нужная тулза для ленивых админов. Может работать автодополнением для разных консольных программ: aptitude, git, invoke-rc.d, ssh и других.

Список поддерживающих программ находиться в директории /etc/bash_completion.d

По желанию можно самому расширить этот список. Не забудьте прислать ваши труды мейнтейнеру пакета.

1
aptitude install bash-completion

Добавляем в ~/.bashrc

1
2
3
4
# Use bash-completion, if available
if [ -f /etc/bash_completion ]; then
  . /etc/bash_completion
fi

Debian. Часть 4. Настройка веб-сервера: Nginx, Apache, Mysql, Postgresql

| Комментарии

У меня работает связка nginx –> apache2 + mysql + postgresql. Поставим memcached, APC (кэшер для php), и несколько модулей для php5.

Для того, чтобы nginx проксировал через локальный адрес (192.168.1.254, например), необходимо добавить в bind наш домен. Это ещё пригодиться для доступа из локальной сети, чтобы запросы не шли через «внешку», а также для однозначной идентификации того, что заходят из «доверенной» сети. Добавляем в файл /etc/bind/named.conf.local наш домен:

1
2
3
4
zone "zagirov.name" {
    type master;
    file "/etc/bind/db.zagirov.name";
};

Создаём файл /etc/bind/db.zagirov.name:

1
2
3
4
5
6
7
8
9
10
11
$TTL    604800
@   IN  SOA zagirov.name. root.zagirov.name. (
                  2     ; Serial
             604800     ; Refresh
              86400     ; Retry
            2419200     ; Expire
             604800 )   ; Negative Cache TTL
;
@   IN  NS  localhost.
@   IN  A   192.168.1.254
*   IN  A   192.168.1.254

Я использую Debian testing (squeeze). Устанавливаем пакеты:

1
aptitude install apache2 nginx libapache2-mod-php5 php5-cli php5-mysql php5-pgsql php5-xmlrpc php5-gd php5-curl php5-xsl php-apc memcached php5-memcache mysql-server postgresql

Структура папок сайта: logs – логи tmp – папка для временных файлов и файлов сессии www – содержимое сайта

Создадим «эталонную» папку для сайта, которая будет структурой для будущих сайтов

1
2
3
mkdir - p /var/www/etalon/{logs,tmp,www}
chown www-data.www-data /var/www/etalon/{tmp,www}
chmod 0700 /var/www/etalon/{tmp,www}

Открываем в mc папку /var/www/, выбираем папку etalon, нажимает shift+F5, вводим название сайта(zagirov.name). Появилась папка для будущего сайта: /var/www/zagirov.name/ Чтобы логи архивировались, создаём правило для logrotate в файле /etc/logrotate.d/sites:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/var/www/*/logs/*.log {
    weekly
    missingok
    rotate 52
    compress
    delaycompress
    notifempty
    create 644 root root
    sharedscripts
    postrotate
        if [ -f "`. /etc/apache2/envvars ; echo ${APACHE_PID_FILE:-/var/run/apache2.pid}`" ]; then
            /etc/init.d/apache2 reload > /dev/null
        fi
        [ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid`
    endscript
}

Теперь настраиваем nginx. Удаляем все символические ссылки из папки /etc/nginx/sites-enabled Создаём конфигурацию для неописанных доменов: /etc/nginx/sites-available/default_http В случае, когда сайт не описан nginx просто разорвёт соединение, ничего не выдав.

1
2
3
4
5
6
7
server {
    listen          80 default;
    server_name     _;
    access_log      off;
    server_name_in_redirect  off;
    return 444;
}

Открываем mc. В левой панели /etc/nginx/sites-enabled, в правой – /etc/nginx/sites-available Выбираем файл default_http и нажимаем комбинацию Ctrl+X, а затем S – это создаст символическую ссылку выбранного файла в папке другой панели. Теперь создадим файл для сайта /etc/nginx/sites-available/zagirov.name:

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
server {
    listen   80;
    server_name zagirov.name www.zagirov.name rustam.zagirov.name;

    server_tokens off;

    access_log /var/www/zagirov.name/logs/nginx.access.log;
    error_log /var/www/zagirov.name/logs/nginx.error.log warn;

    charset utf-8;
    client_max_body_size 1m;

    root /var/www/zagirov.name/www;
    index index.php index.html index.htm;

    location / {
        #Реврайт
        #try_files $uri $uri /index.php?q=$uri&$args;

        proxy_pass http://zagirov.name:81;
        include proxy.conf;
    }

    #Тут перечисляем все пути и файлы статики (картинки, стили)
    location ~ /(favicon.ico|wp-content/uploads/|wp-content/themes/openark-blog/(images/|style.css)) {
        expires 7d;
    }
}

Опять включаем сайт созданием символической ссылки.

Теперь настраиваем apache. Удаляем включённые по дефолту сайты из папки /etc/apache2/sites-enabled Меняем порт с 80 на 81 в файле /etc/apache2/ports.conf:

1
2
NameVirtualHost *:81
Listen 81

Создаём конфигурацию для неописанных доменов: /etc/apache2/sites-available/default_http

1
2
3
<virtualhost *:81>
    ServerName default
</virtualhost>

Создадим настройки для сайта в файле /etc/apache2/sites-available/zagirov.name:

1
2
3
4
5
6
7
8
9
10
11
12
<virtualhost *:81>
    ServerName zagirov.name
    ServerAlias www.zagirov.name rustam.zagirov.name
    DocumentRoot /var/www/zagirov.name/www
    ErrorLog /var/www/zagirov.name/logs/apache2.error.log
    CustomLog /var/www/zagirov.name/logs/apache2.access.log combined
    AddDefaultCharset UTF8
    php_flag magic_quotes_gpc off
    php_admin_value open_basedir "/var/www/zagirov.name/www"
    php_admin_value upload_tmp_dir "/var/www/zagirov.name/tmp"
    php_value session.save_path "/var/www/zagirov.name/tmp"
</virtualhost>

Включаем сайт:

1
a2ensite zagirov.name

Перезапускаем apache и nginx.

1
2
invoke-rc.d apache2 restart
invoke-rc.d nginx restart

Установка рутового пароля для пользователя postgres для PostgreSQL:

1
2
3
su postgres
psql -d template1
ALTER USER postgres WITH PASSWORD 'Str0ng passw0rd';

← Часть 3