-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51

Habrahabr/New








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/new/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-new-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

[Из песочницы] Как я создал систему установки принтеров на работе

Пятница, 14 Июля 2017 г. 12:55 + в цитатник
image

Здравствуй, уважаемый %user%. Сегодня я расскажу о том, как я написал пользовательскую систему для обеспечения возможности пользователям самим устанавливать требуемые для них принтеры. Немного расскажу о месте где я работаю, чтобы было понятно зачем я это сделал. Заранее скажу, что о существовании компонента роли в Windows Server 2012 R2, которая даёт возможность установить принтер с браузера я знал. Но мне хотелось изобрести свой велосипед, да и ограничения здесь будут наверное в фантазии. Кому стало интересно добро пожаловать под кат. Для не терпеливых сразу скажу, что скриншоты в конце статьи.

Немного о работе


Работаю я в хелпдеске в некой большой организации, которая имеет много филиалов. Имеется более тысячи сотрудников работающие перед компьютерами. Можно догадаться, что есть большой зоопарк сетевых принтеров, есть отдельные работники отвечающие за работоспособность принтеров. Установку принтеров компьютерам осуществляем мы – хелпдеск. Очень часто сотрудники разных отделов переходят из одного рабочего места в другое, приходят или увольняются, или вообще меняют должность и профессию и так далее. Куча всего интересного. Соответственно из-за этого очень часто поступают звонки к нам в хелпдеск о просьбе установить им принтер. Понимаете, попросить пользователя, сказать IP адрес принтера, это все равно что попросить обезьяну сварить суп. Приведу пример диалога (реальный случай):

U – user, Я - я
U- Здравствуйте, это хелпдеск туда я попал?
Я- Да. Правильно. Вы туда попали
U- Помогите мне пожалуйста установить принтер на мой компьютер
Я-Хорошо, скажите мне пожалуйста имя вашего компьютера.
U-Имя моего компьютера comp-01 (имя вымышленное)
Я-Какой принтер вам поставить? (Подключаясь к компьютеру)
U- HP LaserJet 600, он находится в %place%
Я – расположение принтера мне ничего не дает (и правда поменяют его другим принтером так как первый испортился, или поменяют его место). Хорошо, можете ли вы мне сказать ip адрес этого принтера?
U- ….
Пауза на конце трубки. Я аж услышал, как зашевелился у пользователя мозг, пошла кровь из ушей и наконец он спросил
U- Простите что? Айми? Что вам сказать? Айми адрес?
Я – Айпи, айпи адрес.
U- Айпи? А что это такое? Как можно на него посмотреть?
Я –Ладно скажите кроме вас кто-то еще этим принтером пользуется?
U-Ну им пользуется %another_user%
Я-Пожалуйста попросите у него имя компьютера и получите разрешение чтобы я на секундочку к нему подключился
U- Ок, сейчас (и слышу приглушенный голос того как он спрашивает у %another_user% имя компьютера и разрешение на подключение)
Я-…..(думая про себя: айми айми айми, елки палки какую рифму на айми можно придумать)
U-Алё
Я- Да, я вас слушаю
U- Имя компьютера comp-02, можете подключаться
Я- Попросите %another_user% показать мне принтер, которым он постоянно пользуется (подключаюсь к компьютеру)
U- Ок, сейчас (опять приглушенный голос)…… — Вот этот
Я- Ок, хорошо, я понял всё не буду вас задерживать на линии сейчас сам всё поставлю и настрою
U- Хорошо, спасибо. Однако вы мне не сказали, что такое айми.
Я- Наверное, вы имели в виду айпи. Ну сложно будет объяснить сейчас, так как звонков многовато, но в общем это адрес устройства в сети.
Вы слышали когда-нибудь как ломается мозг у гуманитария? Я услышал этот звук, знаете такой глухой звук с брызгами большого количество информации с содержанием одной лишь только «воды».
U – ....(долгая пауза)… Ясно. Я отойду по делам а вы поставьте принтер
Я- Да, хорошо, до свидания

А теперь представьте, что каждый третий звонящий в хелпдеск, с просьбой установить принтер в таком же состоянии. Как быть? Данного рода диалоги очень сильно утомляют, да и объяснить юзверю как напечатать конфигурацию устройства, чтобы посмотреть на IP принтера, это что-то сверхтяжелое. Значит нужна какая-то автоматизация. А что если собрать централизованно все принтеры на один отдельный принтер сервер, написать некий простой веб-сайт с дружелюбным интерфейсом. Пару кликов и принтер установился. Вызов принят.

Все не так и сложно как кажется


Попросил системных администраторов выделить отдельный сервер на Windows Server 2012 R2.

На принтер сервере

  • Установил роль Printer Sever
  • Создал папку scripts$, чтобы хранить скрипты установки принтеров
  • Установил XAMPP
  • Добавил apache и mysql как службу в автозагрузку, чтобы в случае перезагрузки сервера, веб-сайт не грохнулся

С принтерами:

  • Наклеил на все принтеры до которых руки доходят наклейки с их номерами и установил их на принтер сервер. Порядковый номер — это некий идентификатор для принтера. Где бы он не находился юзверь всегда сможет найти его по порядковому номеру. К примеру: «HP LaserJet 600 Printer #id %filial%», где %filial% — имя филиала, #id уникальный порядковый номер принтера. Распечатывается бумажка с порядковым номером и клеится на принтер. Знать пользователю даже имя принтера не обязательно. Совпал номер с тем что на сайте – значит он и есть.
  • Договорился с менеджерами филиалов, попросил выделить кого-то знающего в филиале чтобы собрали и отправили мне имена, ip адреса принтеров и наклеили их номера порядковые. Добавил все принтеры по одному на принтер сервер.

Начинаем программировать


Веб-сайт построен на PHP написанием самописного MVC движка, с помощью использования только редактора Notepad++, Google и браузера (да – только по хардкору). Знаниями по какому-то фреймворку не обладаю, изучать лень, использовать javascript фреймворки в 2к17?, да и не слишком ли много для принтеров?

Поставим цель, что нам нужно от этой системы:

  • Максимально простая и наглядная для пользователя
  • Возможность создания учетных записей администраторов
  • Возможность добавлять\удалять принтеры администраторам
  • Возможность добавлять\удалять филиалы администраторам
  • Наличие какой-либо справочной информации для пользователя
  • Смена языка
  • Возможность устанавливать принтеры не одним, а несколькими методами (скриптами)

Определим структуру проекта:

  • App
    1. Configs
    2. Controllers
    3. Locale
    4. Views

  • Uploads

В корневой папке создадим файл index.php добавим код:



В папке configs создадим файл database.php:



Зайдем в браузере в phpmyadmin, создадим базу данных printer с использованием кодировки utf8_general_ci. Создадим 3 таблицы с именами branches, printers, users.

Структура branches:
Имя Тип Дополнительно
Id int(6) AUTO_INCREMENT, PRIMARY, UNIQUE
branch_name varchar(255)
image varchar(255)

Структура printers:
Имя Тип Дополнительно
Id int(6) AUTO_INCREMENT, PRIMARY, UNIQUE
Name varchar(255)
branchid int(6) По умолчанию значение: 1
description text
ipaddress varchar(255)
image varchar(255)
File1 varchar(255)
File2 varchar(255)
File3 varchar(255)

Структура users:
Имя Тип Дополнительно
Id int(6) AUTO_INCREMENT, PRIMARY, UNIQUE
Login varchar(128)
Token varchar(128)
password varchar(128)
lang varchar(10)
logindate varchar(255)

Как видно из таблиц branchid определяет к какому филиалу относится принтер (получив значение его id).

Создадим запись с id равной 1, именем филиала «none» и image значением «none». Эта запись нужна для того чтобы задавать принтеру не установленный филиал. Первая запись захардкожена и не отображается в списках филиалов, как и для юзверей так и в админке. Её удалить нельзя.

Класс Database я нашел в просторах интернета. В нем имеется функция для подключения к нашей базе данных, с использования значений для подключения от файла database.php находящийся в папке configs и функция для осуществления запросов к БД.

Класс Info используется для получения, изменения, удаления данных из БД. Именно с использованием этого класса осуществляются все операции в системе.

Одним из важных классов является класс Controller.php. Как видно из названия этот класс является контроллером, который принимает GET и POST запросы в __construct($get, $post) из index.php. Получив определенный запрос он собирает куски страницы во едино соответственно требуемому запросу и выполняет требуемые функции из Info.php.

Класс Views является классом, который загружает куски страницы. Каждая страница имеет свое название соответствующее php файлу в папке Views. Так к примеру, чтобы отобразить главную страницу, следует написать:

$views = new Views;
$views->addView('header', 'header.php');
$views->addView('menu', 'menu.php');
$views->addView('dashboard', 'dashboard.php');
$views->addView('footer', 'footer.php');

В этом же классе определяется текущий язык пользователя, загружается локализация из ini файла и создается переменная $lang, которая уже используется в самом куске страницы. Пример кода главной страницы (dashboard.php):



В папке views так же есть файлы css, javascript. За основу CSS стилей я использовал Bootstrap.

Класс Lang определяет текущий язык пользователя. Сперва он пытается получить куки «cookie_lang». Если не обнаруживает его, то ставит по умолчанию русский язык. Функция getLangArray() загружает нужный ini файл и возвращает массив вида «ключ» — > «значение». Именно эта функция используется в классе views.

Что мы получили


Пользователь заходит на главную страницу. Там описание того куда он попал и кнопка, которая открывает список всех филиалов. После того как пользователь выберет филиал, откроется список принтеров. Принтеры отображаются в виде сетки, где их фотографии и их названия. Пользователь выбирает принтер, открывается страница с выбранным принтером, где есть большая кнопка «Установить» (также там информация о принтере, его IP адрес). При нажатии на эту кнопку скачивается bat файл, который в свою очередь открывает vbs скрипт, расшаренный в папке scripts$ на принтер сервере. Проблема в том что если закачивать файл vbs, то вместо того чтобы скачать его, браузер открывает этот файл у себя в новой вкладке. Поэтому пришлось так извращаться. Vbs файлы на шаре папке тоже рассортированы вместе со своим bat файлом по папкам. К примеру, ниже приведена структура папки:

Printer#id
  • Printer#id.bat
  • Printer#id.vbs

Где #id порядковый номер принтера.

При добавлении нового принтера через админку, заполняются требуемые поля и выбирается нужный bat файл из шары. Файл установки, фотография принтера загружается в папку uploads. В админке сделано так, что мы можем закачать до трех разных файлов установки. Сколько файлов закачано столько и кнопок «Установить». В данном случае обошлось без альтернативных методов установки принтеров. VBS скрипта оказалось достаточно.

VBS script


Vbs скрипт устанавливающий принтер выглядел таким образом:

printerName = "\\prnserver01\HP LaserJet 600 printer1 branch1"
Set WshNetwork = CreateObject("WScript.Network")
WshNetwork.AddWindowsPrinterConnection printerName
WSHNetwork.SetDefaultPrinter printerName

Данный скрипт выполняется в невидимом режиме, какое-то время отображается окно командной строки, затем он исчезает. Данный скрипт обладает минусом того, что не понятно, что происходит. Поэтому подумав немного поменял его в такой вид:

'Задаем путь принтера
printerName = "\\prnserver01\HP LaserJet 600 printer1"

'Создаем окно Internet Explorer
Set objExplorer = CreateObject("InternetExplorer.Application")

'Задаем настройки окна - длину, ширину, позицию на экране
objExplorer.Navigate "about:blank"  
objExplorer.ToolBar = 0
objExplorer.StatusBar = 0
objExplorer.Left = 500
objExplorer.Top = 250
objExplorer.Width = 550
objExplorer.Height = 170 
objExplorer.Visible = 1

'Задаем заголовок
objExplorer.Document.Title = "Ustanovka printera"

'Задаем html код страницы, со своим крутым дизайном
objExplorer.Document.Body.InnerHTML = "
Ustanovka printera: 0%
" & printerName & "
" 'Ждем 500 миллисекунд, находим на странице объект с id progress и меняем его значение. Психологический фактор для юзверя, толку от этого 0 Wscript.Sleep 500 objExplorer.document.getElementById("progress").innerText = " Ustanovka printera: 10%" 'Создаем объект для установки принтера, устанавливаем принтер и ставим его по умолчанию. Скрипт не продолжит выполнятся пока принтер не установится или выйдет ошибка Set WshNetwork = CreateObject("WScript.Network") WshNetwork.AddWindowsPrinterConnection printerName WSHNetwork.SetDefaultPrinter printerName 'Психологический фактор на 200 миллисекунд, хотя принтер уже установился или вышла ошибка Wscript.Sleep 200 objExplorer.document.getElementById("progress").innerText = " Ustanovka printera: 20%" 'Ещё один психологический фактор на 200 миллисекунд Wscript.Sleep 200 objExplorer.document.getElementById("progress").innerText = " Ustanovka printera: 40%" 'Ещё один, это же такой кайф когда проценты быстро идут Wscript.Sleep 200 objExplorer.document.getElementById("progress").innerText = " Ustanovka printera: 60%" 'В этом моменте можно уже откинуться Wscript.Sleep 200 objExplorer.document.getElementById("progress").innerText = " Ustanovka printera: 80%" 'О свершилось чудо, наконец! Wscript.Sleep 100 objExplorer.document.getElementById("progress").innerText = " Printer ustanovlen!" 'Получим на 3 секунды еще кайфа от того, что установили принтер и прощаемся Wscript.Sleep 3000 objExplorer.Quit

Скриншоты


Скриншоты
image

image

image

image

image

image

image

image

image

image

image

image

image

image

image

image

Исходный код всего этого выложил на github

Теперь звонков по поводу установки принтеров стало намного меньше.
Спасибо за внимание!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333056/


Метки:  

Нейротеатр: технологии Университета ИТМО помогают создавать «искусство новых медиа»

Пятница, 14 Июля 2017 г. 12:04 + в цитатник
Нейротехнологии — обычно предмет медицинских или военных разработок. В крайнем случае — основа нового высокотехнологичного стартапа. Однако в Университете ИТМО считают, что нейротехнологии вполне могут служить не только науке, но и искусству. Поэтому в прошлом месяце на фестивале Geek Picnic в Санкт-Петербурге состоялась премьера экспериментального проекта в жанре «нейротеатр». О том, что это такое и как работает NEU-theatre, расскажем ниже.

/ Фотография Университета ИТМО

Проект NEU-theatre


Нейротеатр — это, по сути, сплав искусства и технологий. На Geek Picnic этот жанр был представлен выступлением танцоров, использовавших нейроинтерфейсы. Они считывали эмоции артистов и передавали их внутреннее состояние с помощью музыки, света, цвета, проекции и ритма.

Танцоры на выступлении работали с так называемым синестетическим шаром — он позволял «отобразить» эмоции исполнителя. Кроме того, его можно было использовать, чтобы менять звуковое сопровождение перфоманса.


/ Фотография Университета ИТМО

Весь этот хайп вокруг нейроинтерфейсов хорош тем, что люди вспоминают о том, что у них есть мозг. И что его возможности стоит использовать и развивать более активно

– Юрий Дидевич, медиахудожник, один из создателей NEU-theatre

По сути, нейротеатр — это пример того, как наш мозг становится центром особого направления в искусстве. Оказывается, наши эмоции могут творить аудиовизуальную реальность, благодаря которой артист становится еще ближе к зрителю. Это уже даже не иммерсивный спектакль, когда можно оказаться участником представления — такой формат позволяет лучше понять, что творится у артиста в голове, — буквально.

Кто создавал


Создатели проекта NEU-theatre: российский медиахудожник, музыкант, лектор Института Art&Science Университета ИТМО Юрий Дидевич, Высшая школа светового дизайна Университета ИТМО и танцевальный коллектив Stage DFT.

Юрий Дидевич много лет занимается вопросами интеграции компьютерных технологий и искусства. В 2014 году его спектакль «Нейроинтегрум» был поставлен на Новой сцене Александринского театра в Санкт-Петербурге. Нейротеатр NEU-theatre — еще один проект Юрия, объединяющий искусство и ИТ.


/ Фотография Университета ИТМО

Высшая школа светового дизайна Университета ИТМО — это и образовательный, и экспериментально-практический проект, объединяющий обучение, науку и искусство. В Высшей школе светового дизайна проводятся исследования городской среды, работает проектная лаборатория, направление Light & Art занимается созданием световых инсталляций и световым сопровождением фестивалей. Кроме того, там можно получить образование по профилю «Световой дизайн» — в школе открыты магистратура и аспирантура, курсы повышения квалификации и летняя школа.

Такое сочетание науки и искусства организаторы нейротеатра считают максимально перспективным:

Было бы здорово, чтобы ученые, в том числе, российские, перестали относиться к искусству как к чему-то ущербному. Это очень хорошее сочетание — ученого и его научного подхода и художников, людей, воспринимающих мир несколько иначе. Это может привести к очень интересным результатам

– Юрий Дидевич

Как это работает


На первый взгляд может показаться, что нейротеатр — это просто мультимедийный перфоманс с музыкой, светом, цветом и танцорами. На самом деле артисты, занятые в представлении, действительно передают аудитории свое состояние — нейроинтерфейс анализирует их мозговую активность и «выдает» определенную аудиовизуальную картинку.


/ Фотография Университета ИТМО

Программа анализа мозговой активности — разработка авторов NEU-theatre (проект практически полностью основывается на собственных разработках его создателей). В основе такого анализа лежит изучение биоэлектрогенеза и работа по фиксации и расшифровке нервных импульсов, которые генерирует мозг человека.

При таком подходе в равной степени важными становятся и искусство, и технология (ПО), и конкретный инструментарий — интерфейсы, с которыми работают художники и артисты. Кстати, Юрий Дидевич отметил, что он и его коллеги ожидают появления более доступных устройств и интерфейсов для фиксации и распознавания эмоций — спроектированных специально для артистов.

Принцип работы нейротеатрального инструментария и вообще нейротеатра не нов — первые попытки зафиксировать состояние человека на сцене появились еще в начале ХХ века (тогда для этого использовались даже гальванометры). Впоследствии интерес к этому жанру то угасал, но снова возвращался — не в последнюю очередь благодаря новостям о достижениях ученых. Можно сказать, что сейчас нейротеатр переживает свое второе рождение — и дает артистам уже совершенно новые технологические возможности.

О чем еще мы рассказываем на Хабре:


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333042/


Метки:  

[Из песочницы] WPF и Box2D. Как я делал физику c WPF

Пятница, 14 Июля 2017 г. 11:33 + в цитатник
image

Доброго времени хабр. Я большой фанат физики в играх, работал с некоторыми интересными физическими движками но сегодня я расскажу о Box2D. Он максимально прост и понятен и отлично подходит для двумерной физики. Я заметил что в интернете очень мало туториалов по Box2D на C#, их почти нет. Меня неоднократно просили написать статейку по этому поводу. Чтож, время пришло. Будет много кода, букв и немного комментариев. Для вывода графики используется WPF и элемент Viewport3D. Кому интересно, добро пожаловать подкат.

Box2D — компьютерная программа, свободный открытый физический движок. Box2D является физическим движком реального времени и предназначен для работы с двумерными физическими объектами. Движок разработан Эрином Катто (англ. Erin Catto), написан на языке программирования C++ и распространяется на условиях лицензии zlib.
Движок используется в двумерных компьютерных играх, среди которых Angry Birds, Limbo, Crayon Physics Deluxe, Rolando, Fantastic Contraption, Incredibots, Transformice, Happy Wheels, Color Infection, Shovel Knight, King of Thieves.
Скачать можно по ссылке Box2D.dll

WPF и Viewport3D


Для отрисовки мира я решил по определенным причинам взять WPF, кончно отрисовывать можно на чем угодно, хоть на обычном Grphics и PictureBox, но это не желательно т.к. Graphics выводит графику через ЦП и будет отнимать много процессорного времени.
Напишем небольшое окружение для работы с графикой. В дефолтное окно проекта добавим следующий XAML:

Код

        
            
                
            
            
                
                    
						
						
						
					
				
			
		
    


ClipToBounds — говорит то что невидимые грани будут отсекаться, хотя здесь это не пригодится т.к. будет 2D проекция, я все равно это включу.

После устанавливается перспективная камера. FarPlaneDistance — максимальное расстояние которое захватывает камера, NearPlaneDistance — минимальное расстояние, и дальше позиция, куда смотрит камера и как она смотрит. Дальше мы создаем элемент Model3DGroup в который мы будем кидать геометрию через его имя «models», и добавляем в него 3 освещения.

Ну вот, с XAML разобрались, теперь можно начать писать класс для создания геометрии:

Код
public class MyModel3D
	{
		public Vector3D Position { get; set; } // Позиция квадрата
		public Size Size { get; set; } // Размер квадрата
		private TranslateTransform3D translateTransform; // Матрица перемещения
		private RotateTransform3D rotationTransform; // Матрица вращения
		public MyModel3D(Model3DGroup models, double x, double y, double z, string path, Size size, float axis_x = 0, double angle = 0, float axis_y = 0, float axis_z = 1)
		{
			this.Size = size;
			this.Position = new Vector3D(x, y, z);
			MeshGeometry3D mesh = new MeshGeometry3D();
			// Проставляем вершины квадрату
			mesh.Positions = new Point3DCollection(new List
			{
				new Point3D(-size.Width/2, -size.Height/2, 0),
				new Point3D(size.Width/2, -size.Height/2, 0),
				new Point3D(size.Width/2, size.Height/2, 0),
				new Point3D(-size.Width/2, size.Height/2, 0)
			});
			// Указываем индексы для квадрата
			mesh.TriangleIndices = new Int32Collection(new List { 0, 1, 2, 0, 2, 3 });
			mesh.TextureCoordinates = new PointCollection();
			// Устанавливаем текстурные координаты чтоб потом могли натянуть текстуру
			mesh.TextureCoordinates.Add(new Point(0, 1));
			mesh.TextureCoordinates.Add(new Point(1, 1));
			mesh.TextureCoordinates.Add(new Point(1, 0));
			mesh.TextureCoordinates.Add(new Point(0, 0));

			// Натягиваем текстуру
			ImageBrush brush = new ImageBrush(new BitmapImage(new Uri(path)));
			Material material = new DiffuseMaterial(brush);
			GeometryModel3D geometryModel = new GeometryModel3D(mesh, material);
			models.Children.Add(geometryModel);
			translateTransform = new TranslateTransform3D(x, y, z);
			rotationTransform = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(axis_x, axis_y, axis_z), angle), 0.5, 0.5, 0.5);

			Transform3DGroup tgroup = new Transform3DGroup();
			tgroup.Children.Add(translateTransform);
			tgroup.Children.Add(rotationTransform);
			geometryModel.Transform = tgroup;
		}
		// Утсанавливает позицию объекта
		public void SetPosition(Vector3D v3) 
		{
			translateTransform.OffsetX = v3.X;
			translateTransform.OffsetY = v3.Y;
			translateTransform.OffsetZ = v3.Z;
		}
		public Vector3D GetPosition()
		{
			return new Vector3D(translateTransform.OffsetX, translateTransform.OffsetY, translateTransform.OffsetZ);
		}
		// Поворачивает объект
		public void Rotation(Vector3D axis, double angle, double centerX = 0.5, double centerY = 0.5, double centerZ = 0.5)
		{
			rotationTransform.CenterX = translateTransform.OffsetX;
			rotationTransform.CenterY = translateTransform.OffsetY;
			rotationTransform.CenterZ = translateTransform.OffsetZ;

			rotationTransform.Rotation = new AxisAngleRotation3D(axis, angle);
		}
		public Size GetSize()
		{
			return Size;
		}
	}


Этот класс создает квадрат и отрисовывает на нем текстуру. Думаю по названиям методов понятно какой метод за что отвечает. Для рисования геометрических фигур я буду использовать текстуру и накладывать ее на квадрат с альфа каналом.

imageimage

Box2D — Hello world


Начнем с самого основного, с создания мира. Мир в Box2D имеет определенные параметры, это границы(квадрат) в которых обрабатываются физические тела.

image

В параметрах мира так же есть вектор гравитации и возможность объектов «засыпать» если их инерция равно нулю, это хорошо подходит для экономии ресурсов процессора. Пораметров конечно больше, но нам пока нужны только эти. Создадим класс Physics и добавим следующий конструктор:

Код
public class Physics
{
	private World world;
	public Physics(float x, float y, float w, float h, float g_x, float g_y, bool doSleep)
	{
		AABB aabb = new AABB();
		aabb.LowerBound.Set(x, y); // Указываем левый верхний угол начала границ
		aabb.UpperBound.Set(w, h); // Указываем нижний правый угол конца границ
		Vec2 g = new Vec2(g_x, g_y); // Устанавливаеи вектор гравитации
		world = new World(aabb, g, doSleep); // Создаем мир
	}
}


Дальше необходимо написать методы для добавления различных физических тел, я добавлю 3 метода создающих круг и квадрат и многоугольник. Сразу добавляю в класс Physics две константы:

private const string PATH_CIRCLE = @"Assets\circle.png"; // Изображение круга
private const string PATH_RECT = @"Assets\rect.png"; // Изображение квадрата

Описываю метод для создания квадратного тела:

Код
public MyModel3D AddBox(float x, float y, float w, float h, float density, float friction, float restetution)
{
	// Создается наша графическая модель
	MyModel3D model = new MyModel3D(models, x, -y, 0, PATH_RECT, new System.Windows.Size(w, h));
	// Необходим для установи позиции, поворота, различных состояний и т.д. Советую поюзать свойства этих объектов
	BodyDef bDef = new BodyDef();
	bDef.Position.Set(x, y);
	bDef.Angle = 0;
	// Наш полигон который описывает вершины			
	PolygonDef pDef = new PolygonDef();
	pDef.Restitution = restetution;
	pDef.Friction = friction;
	pDef.Density = density;
	pDef.SetAsBox(w / 2, h / 2);
	// Создание самого тела
	Body body = world.CreateBody(bDef);
	body.CreateShape(pDef);
	body.SetMassFromShapes();
	body.SetUserData(model); // Это отличная функция, она на вход принемает объекты типа object, я ее использовал для того чтобы запихнуть и хранить в ней нашу графическую модель, и в методе step ее доставать и обновлять
	return model;
}


И для создания круглого тела:

Код
public MyModel3D AddCircle(float x, float y, float radius, float angle, float density,
	float friction, float restetution)
{
	MyModel3D model = new MyModel3D(models, x, -y, 0, PATH_CIRCLE, new System.Windows.Size(radius * 2, radius * 2));

	BodyDef bDef = new BodyDef();
	bDef.Position.Set(x, y);
	bDef.Angle = angle;

	CircleDef pDef = new CircleDef();
	pDef.Restitution = restetution;
	pDef.Friction = friction;
	pDef.Density = density;
	pDef.Radius = radius;

	Body body = world.CreateBody(bDef);
	body.CreateShape(pDef);
	body.SetMassFromShapes();
	body.SetUserData(model);
	return model;
}


Я тут этого делать не буду, но можно создавать многоугольники примерно таким способом:

Код
public MyModel3D AddVert(float x, float y, Vec2[] vert, float angle, float density,
	float friction, float restetution)
{
	MyModel3D model = new MyModel3D(models, x, y, 0, Environment.CurrentDirectory + "\\" + PATH_RECT, new System.Windows.Size(w, h)); // Данный метод нужно заменить на рисование многоугольников		

	BodyDef bDef = new BodyDef();
	bDef.Position.Set(x, y);
	bDef.Angle = angle;

	PolygonDef pDef = new PolygonDef();
	pDef.Restitution = restetution;
	pDef.Friction = friction;
	pDef.Density = density;
	pDef.SetAsBox(model.Size.Width / 2, model.Size.Height / 2);
	pDef.Vertices = vert;

	Body body = world.CreateBody(bDef);
	body.CreateShape(pDef);
	body.SetMassFromShapes();
	body.SetUserData(model);
	return info;
}


Очень важно рисовать выпуклые многоугольники чтоб коллизии обрабатывались корректно.

Тут все достаточно просто если вы знаете английский. Дальше нужно создать метод для обработки логики:

Код
public void Step(float dt, int iterat)
{
	// Параметры этого метода управляют временем мира и точностью обработки коллизий тел
	world.Step(dt / 1000.0f, iterat, iterat);

	for (Body list = world.GetBodyList(); list != null; list = list.GetNext())
	{
		if (list.GetUserData() != null)
		{
			System.Windows.Media.Media3D.Vector3D position = new System.Windows.Media.Media3D.Vector3D(
			list.GetPosition().X, list.GetPosition().Y, 0);
			float angle = list.GetAngle() * 180.0f / (float)System.Math.PI; // Выполняем конвертацию из градусов в радианы
                        MyModel3D model = (MyModel3D)list.GetUserData();

		        model.SetPosition(position); // Перемещаем нашу графическую модель по x,y
			model.Rotation(new System.Windows.Media.Media3D.Vector3D(0, 0, 1), angle); // Вращаем по координате x
		}
	}
}


Помните тот model в методах AddCircle и AddBox который мы запихивали в body.SetUserDate()? Так вот, тут мы его достаем MyModel3D model = (MyModel3D)list.GetUserData(); и вертим как говорит нам Box2D.

Теперь это все можно затестить, вот мой код в дефолтном классе окна:

Код
public partial class MainWindow : Window
{
	private Game.Physics px;
	public MainWindow()
	{
		InitializeComponent();

		px = new Game.Physics(-1000, -1000, 1000, 1000, 0, -0.005f, false);
		px.SetModelsGroup(models);
		px.AddBox(0.6f, -2, 1, 1, 0, 0.3f, 0.2f);
		px.AddBox(0, 0, 1, 1, 0.5f, 0.3f, 0.2f);

		this.LayoutUpdated += MainWindow_LayoutUpdated;
	}
	private void MainWindow_LayoutUpdated(object sender, EventArgs e)
	{
		px.Step(1.0f, 20); // тут по хорошему нужно вычислять дельту времени, но лень :)
		this.InvalidateArrange();
	}
}


image

Да, я забыл упомянуть о том что я добавил в класс Physics метод px.SetModelsGroup(); для удобства передачи ссылки на объект Model3DGroup. Если вы используете какой нибудь другой графический движок, то вы можете обойтись и без этого.

Вы наверное заметили что значения координат кубиков слишком маленькие, ведь мы привыкли работать с пикселем. Это связано с тем что в Box2D все метрики расчитываются в метрах, по этому если вы хотите чтобы все расчитывалось в пикселях, вам нужно пиксели делить на 30. На пример bDef.SetPosition(x / 30.0f, y / 30.0f); и все будет гуд.

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

Код
public class Solver : ContactListener
	{
		public delegate void EventSolver(MyModel3D body1, MyModel3D body2);
		public event EventSolver OnAdd;
		public event EventSolver OnPersist;
		public event EventSolver OnResult;
		public event EventSolver OnRemove;

		public override void Add(ContactPoint point)
		{
			base.Add(point);

			OnAdd?.Invoke((MyModel3D)point.Shape1.GetBody().GetUserData(), (MyModel3D)point.Shape2.GetBody().GetUserData());
		}

		public override void Persist(ContactPoint point)
		{
			base.Persist(point);

			OnPersist?.Invoke((MyModel3D)point.Shape1.GetBody().GetUserData(), (MyModel3D)point.Shape2.GetBody().GetUserData());
		}

		public override void Result(ContactResult point)
		{
			base.Result(point);

			OnResult?.Invoke((MyModel3D)point.Shape1.GetBody().GetUserData(), (MyModel3D)point.Shape2.GetBody().GetUserData());
		}

		public override void Remove(ContactPoint point)
		{
			base.Remove(point);

			OnRemove?.Invoke((MyModel3D)point.Shape1.GetBody().GetUserData(), (MyModel3D)point.Shape2.GetBody().GetUserData());
		}
	}


Прошу заметить что мы наследуем класс от ContactListener. Используется в Box2D для отслеживания коллизий. Дальше мы просто передадим объект этого класса объекту world в классе Physics, для этого напишем функцию:

public void SetSolver(ContactListener listener)
{
	world.SetContactListener(listener);
}

Создадим объект и передадим его:

Game.Solver solver = new Game.Solver();
px.SetSolver(solver);

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

solver.OnAdd += (model1, model2) =>
{
	// Произошло столкновение тел model1 и model2
};

Так же вы можите прикрутить к классу MyModel3D свойство типа string name, задавать ему значение и уже в коллбэке OnAdd проверять конкретно какое тело с каким телом столкнулось.
Так же Box2D позволяет делать связи между телами. Они могут быть разных типов, рассмотрим пару:

Код
public Joint AddJoint(Body b1, Body b2, float x, float y)
{
	RevoluteJointDef jd = new RevoluteJointDef();
	jd.Initialize(b1, b2, new Vec2(x, y));
	Joint joint = world.CreateJoint(jd);
			
	return joint;
}


Это простое жесткое соединение тела b1 и b2 в точке x, y. Вы можете посмотреть свойства у класса RevoluteJointDef. Там можно сделать так чтобы объект вращался, подходит для создания машины, или мельницы. Идем дальше:

Код
public Joint AddDistanceJoint(Body b1, Body b2, float x1, float y1, float x2, float y2, 
	bool collideConnected = true, float hz = 1f)
{
	DistanceJointDef jd = new DistanceJointDef();
	jd.Initialize(b1, b2, new Vec2(x1, y1), new Vec2(x2, y2));
	jd.CollideConnected = collideConnected;
	jd.FrequencyHz = hz;

	Joint joint = world.CreateJoint(jd);

	return joint;
}


Это более интересная связь, она эмитирует пружину, значение hz — напряженность пружины. С таким соединением хорошо делать подвеску для машины, или катапульту.

Заключение


Это далеко не все что может Box2D. Что самое классное в этом движке, так это то что он бесплатный и на него есть порт под любую платформу, и синтаксис практически не отличается. Кстати я пробовал его использовать в Xamarin на 4.1.1 андроиде, сборщик мусора постоянно тормозил приложения из-за того что Box2D плодил много мусора. Говорят начиная с пятого андроида благодоря ART все не так плохо, хотя я не проверял.

Ссылка на проект GitHub: github.com/Winster332/Habrahabr
Порт на dotnet core: github.com/Winster332/box2d-dotnet-core-1.0
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333040/


Метки:  

Код высших достижений

Пятница, 14 Июля 2017 г. 10:55 + в цитатник
Почему мы любим спортивное программирование и почему Вам тоже стоит его полюбить.

image

Спортивное программирование объединяет турниры самого разного уровня — от школьных и студенческих олимпиад до финалов чемпионата мира. Крупнейшие международные компании организовывают свои контесты и выступают спонсорами авторитетных соревнований вроде ACM-ICPC. В нашей команде работают четыре чемпиона мира по программированию, и уже в четвёртый раз мы совместно с Codeforces проводим VK Cup — собственный турнир ВКонтакте.

image
Управляющий директор ВКонтакте Андрей Рогозов, основатель Codeforces Михаил Мирзаянов с победителями и призёрами VK Cup 2017

Откуда берутся чемпионы?


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

Если Вы уже избрали другой профессиональный путь, не стоит огорчаться. Заглянуть в мир спортивного программирования можно и более простым способом.

На платформе Codeforces любой желающий может попробовать свои силы в решении задач разного уровня сложности. За каждую решенную в рамках рейтинговых раундов задачу участник получает баллы. На основании этих баллов участники раундов ранжируются и получают рейтинг. После окончания раунда можно прочитать разбор решения, если не удалось найти его самостоятельно. Подборка задач за годы существования проекта скопилась впечатляющая, смотрите сами.

Уверен, что соревнования по программированию — увлекательный и эффективный способ научиться кодить, разобраться в структурах данных и выработать алгоритмическое мышление. Пробуйте участвовать, раунды на Codeforces открыты для всех и проходят каждую неделю. Наша цель — предложить интересные задачи для каждого участника!

Михаил Мирзаянов, основатель Codeforces


Турниры и задачи


Регламент проведения турниров может различаться в деталях, но общие принципы сохраняются. Обычно участникам отводится строго ограниченное время, сами задачи могут быть ранжированы по «стоимости» в зависимости от уровня сложности, а решения оцениваются с помощью набора готовых тестов. Самый популярный язык спортивного программирования — C++, хотя бывают и исключения. Например, на одном из раундов VK Cup 2017 нужно было решить каждую задачу на отдельном языке.

Соревнования могут быть личными и командными, с ограничениями по возрасту участников и без. У турнира может быть определённая тематика — например, AI, стратегии для биржи или классические алгоритмы. Поставленная задача может заключаться во взломе удалённого сервера или в создании нейросети для хороших результатов на синтетическом датасете.

Создание задач — отдельное искусство, которое требует от автора не меньшей подготовки, чем у самого участника. Победители прошлых лет часто принимают участие в написании задач для нового турнира. У хорошей задачи есть характерные свойства:

  1. Её можно решить.
  2. Её можно решить в отведённое время.
  3. Для проверки решения можно создать набор тестов, на которых правильное решение будет работать, а неправильное — выдавать ошибку (или не укладываться в ограничения по времени выполнения и объёму памяти).
  4. Она интересная. Олимпиадные задачи не решаются «в лоб», и даже если за условиями проглядывает какой-то фундаментальный алгоритм, придётся как следует подумать, чтобы понять, как именно его здесь применить.

Хотя опытные участники соревнований видели уже тысячи разных задач и редко запоминают их в деталях, авторы стараются добавить живое описание проблемы. В архиве Codeforces попадаются задачи про пылкий поток любви, «фибоначчиеватость» и хипстера Васю. Как такое пропустить?

После победы


Победителей престижных соревнований хотят заполучить к себе в штат самые известные IT-компании. Случается, что предложение о работе ждёт участника ещё до подведения итогов контеста.

Это неудивительно. Помимо очевидного (такие люди — умные), опыт в спортивном программировании во многих случаях даст преимущество разработчику. Он не боится сложностей, потому что привык постоянно иметь дело с нетривиальными задачами. Он умеет быстро принимать решения в непростых ситуациях и эффективно использовать все имеющиеся ресурсы.

Конечно, не во всех отраслях индустрии навыки такого разработчика будут использованы по максимуму. Отличное знание фундаментальных алгоритмов и умение выбирать эффективные структуры данных позволяют привлекать лучших из лучших к разработке высоконагруженных сложных систем, где они могут проявить себя в полной мере.

Это относится и к нам — почти все С-разработчики ВКонтакте принимали участие в соревнованиях. Мы используем самописные базы данных, созданные талантливыми программистами, которые не раз побеждали в самых престижных чемпионатах, включая ACM-ICPC.

image
Чемпионы ACM-ICPC 2014 — Егор Суворов, Дмитрий Егоров, Павел Кунявский. Тренер Андрей Лопатин. Двое участников этой команды сейчас работают ВКонтакте.

VK Cup


ВКонтакте всегда поддерживал это направление: мы спонсировали обучение олимпиадному программированию в СПбГУ, помогали организовывать Летнюю компьютерную школу, сделали свой вклад в создание Codeforces. Следующим закономерным шагом стала организация собственного турнира.

История VK Cup началась в 2012 году с первого личного состязания. Сейчас кубок проходит в формате турнира для команд из двух человек. Возраст участников ограничен — не младше 14 и не старше 23 лет.

image
Финал VK Cup 2017 в штабе ВКонтакте

С марта по июль участники решали задачи отборочных раундов. Из 2736 команд, принявших участие в квалификации, до финала дошли лишь 20 лучших. Финал чемпионата традиционно проводится в штабе ВКонтакте, Доме Зингера на Невском проспекте. Об итогах VK Cup 2017 мы рассказали в этой статье.

Хочу участвовать!


Мы будем ждать Вас на VK Cup 2018. И призываем попробовать свои силы в других открытых турнирах:
Facebook Hacker Cup
Google Code Jam
AtCoder
IPSC
Topcoder Open
deadline24
SnackDown
Russian Code Cup
Яндекс.Алгоритм
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333024/


Метки:  

Yubikey 4 — покупка, доставка и получение

Пятница, 14 Июля 2017 г. 10:37 + в цитатник

Часть 2: Покупка и доставка Yubikey 4





Итак, определившись с выбором ключа для 2FA, я начал поиски продавца для покупки Yubikey 4.

Первым делом был обследован русскоязычный сегмент интернета. Оказалось, что существуют две организации, которые вроде бы занимаются реализацией продукции Yubico.



В первую очередь — это сайт www.yubico.ru. В настоящее время он не работает. (Хинт — работает прямая ссылка на файл в формате PDF «yubikey_manual.pdf» — на русском языке, кому нужно). Но в тот момент, когда я делал анализ и выбор продавца ключа, этот сайт ещё функционировал. Тем не менее, даже тогда я обратил внимание на странную вещь — на сайте для продажи не были представлены современные устройства — например, интересный мне ключ Yubikey 4. Также в описании к существующим продуктам были указаны старые версии ПО. Более того, сайт, предлагающий продукт, обеспечивающий цифровую безопасность, был на простом http. И ещё более того. Когда я поинтересовался в почтовой переписке о возможности купить ключ, ответ мне пришёл с почтового домена mail.ru.

Можно было сделать предположение, что новых устройств продавец не предлагает. Предположение оказалось верным, сайт работу по факту прекратил.

Вторая организация, которая предлагала продажу Yubikey была ещё более странной. Она называется >the_kernel. Интернет магазина я не нашёл, предлагалось отправить заявку через форму на сайте. С одной стороны, цены указаны такие же как и у производителя, но отсутствие возможности сделать заказ, оплатить и ждать доставку меня немного удивило. Для XXI века это — мягко говоря — странность. Специально для тех, кто не любит ходить по ссылкам, живёт в первопрестольной и хочет иметь Yubikey, даю контактную информацию с сайта. Возможно, данные ещё актуальны. Вот они:

121357, г. Москва, ул. Верейская, д. 29, стр. 33, офис D216.2 (БЦ Верейская Плаза 3) Тел. +7 499 648-8848 Email info [dot] ru [at] thekernel [dot] com Web www.thekernel.ru

Вот здесь, на Тостере также есть небольшая ветка по вопросам приобретения устройств Yubico.

Один из участников обсуждения (@Driwars) упомянул о том, что таможня не пропустила до адресата купленный на Амазоне ключ. Имейте это ввиду.

Так, в РФ ситуация с продажей в розницу ключей Yubikey более менее ясна. Что же может предложить нам западный (для кого-то — восточный) рынок?

Как правило, наиболее низкую цену может предложить сам производитель, поэтому будет разумно поискать возможность покупки товара напрямую у Yubico. Однако, учитывая стоимость доставки, которая может составлять существенную величину в общей стоимости товара, имеет смысл посмотреть и у других игроков рынка, например — Амазон.

Приступим.

Вопрос — занимается ли компания Yubico непосредственно продажами устройств? Ответ — да, занимается. Открывая заглавную страницу сайта www.yubico.com мы видим вверху кнопку «BUY NOW», и при нажатии на неё переходим сразу к возможности купить товар. Это уже хорошо! Кроме того, обращаем внимание на то, что при покупке есть возможность оформить дисконт — для пользователей GitHub и для учащихся. Насколько можно видеть, скидка одинаковая и составляет 20%, а это уже весьма и весьма неплохо!

Переходим к процедуре оплаты, но перед этим смотрим на доставку (Shipping). И тут — раз — и видим такую фразу:

*no shipping to: Afghanistan, North Korea, Iran, Russia, Sudan, Syria


Вот это да! Как неожиданно! Россия между Ираном и Суданом, в приятном соседстве с Северной Кореей и Сирией… Для контраста можете полистать список стран, где можно приобрести и получить устройство с доставкой от Yubico.

Так, какие же могут быть варианты? Для России — на оффсайте указан >the_kernel. Можете попробовать связаться с представителями этой ТНК.

А что с Амазоном? Давайте посмотрим поближе.
Находим в поиске Yubikey, меняем справа в поле «Ship to» страну на Russian Federation и видим в поле описания товара и доставки следующее:

This item does not ship to Russian Federation.


Вот так.

Что же делать, если вам всё же хотелось иметь для тестирования либо функционального применения устройство Yubikey?

Вариантов может быть несколько:
1. Если вы имеете возможность по работе или по личной инициативе бывать за границей, едете туда и покупаете там. Либо предварительно оплатив товар и доставку, либо в отпуске, либо через друзей и знакомых.
2. Попросите ваших живущих за границей друзей/родных/коллег/знакомых купить устройство и выслать вам. Условия согласуйте сами.
3. Воспользоваться услугами пересыльщиков. Типа Шопотама и Бандерольки. Полагаю, противозаконного в этом ничего нет.
4. Бартер. Скажем, с индусами. Вы им — код, они вам — ключ.
5. Something else. Insert your own point.

Что до меня? Как я покупал ключ? Я воспользовался пунктом 3, посчитав предварительно все расходы. Итого, в реализации получилось вот так:

стоимость самого ключа Yubikey4: $40
скидка: -$8
доставка на адрес в USA: $5
налог: $2.88
итого — на выходе с Yubico: $39.88

Услуги пересылки: $5.78 (и я считаю это весьма гуманной стоимостью)

Итого, в сумме получается $45.66, что по текущему курсу составляет 2 735,52 рубля.

Успехов!

P.S. Есть новое в линейке продуктов. Появился ключ с интерфейсом USB-C.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333036/


Метки:  

Вышел Upsource 2017.2 с поддержкой внешних инспекций кода, Python, NPM и многим другим

Пятница, 14 Июля 2017 г. 10:23 + в цитатник
Всем привет!
У нас отличные новости — вышел Upsource 2017.2! В этом релизе мы добавили ряд наиболее часто запрашиваемых возможностей, и, как обычно, улучшили уже имеющуюся функциональность.
Давайте посмотрим, что попало в этот релиз.

Поддержка внешних инспекций кода
Если Вы используете встроенные в TeamCity инспекции кода на базе ReSharper или IntelliJ IDEA, или пользуетесь инспекциями SonarQube, теперь Вы сможете извлечь из них дополнительную пользу. Upsource 2017.2 умеет показывать результаты такого анализа в своем UI, наряду с результатами встроенных инспекций. Рецензирование кода становится немного проще, если сразу видно, какие новые потенциальные проблемы привнесло именно это изменение.
Инспекции ReSharper в Upsource


Поддержка Python
Навигация по коду и статический анализ, аналогичные тем, что уже имеются для Java, JavaScript, PHP и Kotlin, стали доступны и для Python проектов. Эта функциональность помогает лучше понять изменения в проекте и разобраться в том, какие потенциальные проблемы могли прокрасться в эти изменения, или как можно еще улучшить качество кода.
Поддержка Python

Реакции
Довольно часто дискуссии приходят к тому, что в развернутом ответе необходимости нет, а отреагировать на комментарий все-таки как-то нужно. Для того, чтобы Вы могли сэкономить время себе и другим в таких ситуациях, мы добавили небольшой набор реакций.
Реакции

Поддержка GitLab
Мы рады сообщить, что теперь Вы можете создавать code review для GitLab merge requests в Upsource.

Поддержка NPM
Мы улучшили «Go to declaration» и «Find usages» для JavaScript кода. Теперь Upsource устанавливает зависимости, перечисленные в package.json.
Поддержка NPM

Подсказка при создании Code Review
Используя статистический анализ, Upsource теперь способен подсказать, к какому code review относится новое изменение.
Подсказка при создании Code Review

Немного игровых элементов
Чтобы разнообразить рутину и сделать ознакомление с продуктом чуть более занимательным, мы добавили систему ачивок. На данном этапе, их набор ограничен, но это только начало!
Ачивки

И многое другое

Про остальные новые возможности Upsource Вы можете узнать на странице продукта.

Скачать продукт можно отсюда; ознакомиться с ценами — здесь, а следить за самыми последними новостями можно, подписавшись на Twitter-аккаунт Upsource.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333034/


Метки:  

Почему руководство не принимает agile и что вы можете с этим сделать

Пятница, 14 Июля 2017 г. 09:55 + в цитатник
Данный материал — перевод статьи за авторством Matthew Heusser, Managing Consultant в Excelon Developmen. Редактура — Ирина Махмутова.

image

Почему руководство не принимает agile и что вы можете с этим сделать


Первый в моей практике переход в agile имел полную поддержку высшего руководства компании. Топ менеджмент обеспечил финансирование всех необходимых тренингов, инструментов и консалтинга.
Руководители провели встречи со всеми сотрудниками компании, на которых разъяснили новый подход к работе и то, как изменятся документирование, разработка и структура команд. Правда была одна странная штука: проектные планы, управление портфелем и бюджетирование должны были остаться прежними, не говоря уже про HR и финансы. С тех пор я слышал эту историю много раз, разница была только в деталях — что именно должно было остаться неизменным. При этом все (за исключением может быть пары executives)) согласно кивали.
Наши топы часто тратят уйму времени и денег на agile, но при этом дискредитируют инициативы, которые продвигают. Это не просто частая проблема; это практически всеобщая проблема, а всеобщие проблемы склонны иметь системные причины.
Я приведу пять самых распространенных причин, почему топы не получают agile — и что с этим делать.

Enterprise Agile: Поторопитесь ускориться быстрее

1. Руководство рассматривает agile как активности исключительно командного уровня


Рассмотрим ситуацию: Обычная agile «трансформация» начинается с организации персонала в Scrum команды. Это сокращает время передачи требований в разработку, кода из разработки в тестирование, но никак не решает вопросов внешних зависимостей команды от ИТ, эксплуатации или HR.
Как недавно отметил Mike Cottmeyer pointed out на конференции Agile & Beyond — внешние зависимости разрушают устойчивую производительность команды (velocity).
А когда производительность команды неустойчива, невозможно сказать, когда проект будет завершен.
Cottmeyer отмечал, что именно в этот момент у agile-коучей начинаются проблемы при попытке уговорить руководителей «начать доверять командам». Доверие к командам это прекрасно, но у руководства возникают вопросы, на которые нет ответов. Например: «Когда мы можем открыть новый проект? На что направить маркетинговый бюджет и когда? Сколько заложить в бюджет, чтобы сделать следующую штуку?»
Cottmeyer говорит о том, что внешние зависимости команды это ключевое препятствие. Он рекомендует компаниям, которые стоят на пути адаптации или «внедрения Agile» убирать зависимости, искать обходные пути, пересматривать любые соглашения об уровне сервиса (SLA), чтобы сделать функциональные команды более предсказуемыми. Scrum и XP не предназначены для решения проблем взаимозависимости команд, а я вижу эти проблемы в любой организации с тремя и более командами.
Почти всегда настоящее узкое горло — это взаимозависимости команд. И именно с ним agile методики командного уровня не помогут, а руководители окажутся без желанных результатов и продолжат задавать неудобные вопросы.

2. Руководство не получает обучения необходимого уровня


Недавно мои коллеги проводили двухдневный тренинг для высшего руководства очень прибыльной компании. Тренинг назывался «Agile for Executives», но выяснилось, что это скорее тренинг по Scrum — речь шла о спринтах, историях и самоорганизованных командах. После тренинга у руководства появилось много идей о том, как «те ребята» должны «делать» программы, но оно не получило никаких реальных инструментов управления зависимостями или понимания, что делать с портфелем и бюджетами.
Большая часть руководства слишком занята, чтобы пойти на двухдневный тренинг. Обучение Scrum им в любом случае бесполезно. Как впрочем не сильно помогут и все книги про разработку программного обеспечения, которые обычно читают команды. Сайты о том, как требуется меняться руководству, полны многозначительных советов типа «исследуйте и адаптируйте» или «учитесь быстро», но эти советы никак не говорят руководству о том, как им в реальности управлять проектами в больших организациях.
Eric Willeke, советник по корпоративной гибкости в CA Technologies утверждает, что большинство agile-коучей уровня команды и программы никогда не принимали решения на том уровне, который является повседневным для руководства. И тот факт, что у них нет этого совершенно необходимого опыта означает, что они не готовы обеспечить обучение, которое требуется руководству.

3. Водопадная модель скрывает неэффективность от руководства


Когда водопад был обычной практикой, организации пытались вести две дюжины инициатив, когда-то меньше, когда-то больше. Примерно первые восемь были высокоприритетными, следующе восемь среднеприоритетными и оставшиеся восемь низкоприоритетными. Даты в плане появлялись на основании прошлого опыта и принятых обязательствах, а PMO создавал виртуальные команды, чтобы сделать обещанное.
Хорошая штука в таком подходе в том, что если что-то затягивается, то команда может легко переключиться и сфокусироваться на чем-то, что помечено как высокоприоритетное. Это позволяет избежать печальных последствий закона Брукса (добавление людей на последних стадиях проекта удлиняет его), потому что просто увеличивает время работы или процент участия уже задействованных участников проекта — да-да, вы не добавляете «больше» людей.
В результате некоторые компании, использующие водопад, сумели избежать части проблем от задержанных проектов.
Конечно то, что было помечено как низкоприоритетное, делается крайне долго, если вообще делается, но мы же знаем, что они и были низкоприоритетными. Организация получает то. что было заявлено как необходимое, примерно в запланированное время. И конечно, все известные типы скрытой неэффективности водопада здесь присутствуют: все происходит долго, и полно необдуманных решений, взаимозависимостей и переделок. Но для управления это не так значимо, потому что проекты завершаются.
Завершенный проект — это довольно приличный клапан сброса давления, который организация теряет, когда переходит в agile разработку и ограничивает количество задач в работе. Почему? Потому что, когда команды выделены на один проект, не остается того процента времени, который можно отжать у них, нет способа подшаманить, чтобы компенсировать потери. Таким образом у менеджмента становится на один рычаг меньше для выруливания в сторону дедлайна.
Конечно, всегда сложно держаться курса, но некоторые организации научились скрывать это с помощью водопада. Менеджеры, которые нуждаются в этом, обнаружат, что инструмент, доступный в водопаде, исчезает в agile разработке. Если организация заблокирована многочисленными взаимозависимостями команд и производительность команд нестабильна, результат будет по-прежнему запаздывать.
Для руководства в таких компаниях все выглядит так, как будто все стало только хуже, когда они перешли на agile.

4. Ведущие руководители слишком сосредоточены на каком-то одном аспекте


DevOps невероятно привлекательная и популярная по многим причинам штука. Высшее руководство реально хочет DevOps, но некоторые верят, что DevOps можно купить и «установить». Если вы обзавелись современным средством контроля версий и добавили немного непрерывной интеграции, автоматизированного тестирования и выкладку одной кнопкой, вы волшебным образом сократили путь от запроса до реализации с месяцев до минут.
Все это может быть правдой, есть реально мощные инструменты, но организация получает значительно больше, воспитывая в себе эффективность, а не приобретая инструменты. Когда ScotiaBank проводил agile эксперимент, они обнаружили семь уровней согласования для внесения изменения в продуктивную среду. Консалтинговая компания, которая запускала процесс, просто наняла больше людей, которые занимались согласованиями параллельно разработке. Это давало свои плоды, а еще пополняло счета консалтинговой компании. Dave Dame и Aaron Sampson, которые работали на этом проекте, придерживались другой тактики: изменить процесс так, чтобы контроль соответствовал рискам (теперь уже сниженным).
В то же время вы не сможете снижать риски, если единственная вещь, которая интересует руководство, — это статус запуска непрерывной поставки, и единственное препятствие, которое они видят, это «Doker-изация серверной фермы».
Полная agile-трансформация будет включать изменения организационной структуры, процессов развития и построения технических компетенций, культуры, управления продуктами, закупок, практик контрактованная и, да, даже HR. Фокусировка на одной из этих областей или даже на нескольких из них может привести к созданию чего-то типа трубопровода с кучей засоров, колен и букетом других проблем.

5. Множество конфликтующих целей на уровне руководителей


Agile подход применяется не ради самого себя, а для получения желаемого результата. CA Technologies’ Willeke спешит отметить, что «гибкость без цели лишена смысла». Внедрение agile, по его мнению, должно быть неразрывно связано с бизнес результатом — сокращением time to market, снижением издержек, лучшим клиентским сервисом, лучшим соответствием продукта потребностям заказчика и более высоким качеством. Но если цели или «зачем» недостаточно ясны, то «что» и «как» будут перепутаны. Это самое «что» может в конце концов оказаться совсем не тем, что руководство думало себе получить.
Выделить единственную цель для движения в agile может быть крайне сложно и, на самом деле, не нужно: в конце концов, руководству были обещаны все указанные выше преимущества (смотри пункт 1). Но всякое может быть.

Как это починить


Если что-то сломано, возможно, еще не поздно это исправить. Помните только, что исправить сломанное намного сложнее, чем предотвратить поломку. В крупных организациях все обсуждения будут сводиться к описанным выше ошибкам. И если вы нашли у себя эти корневые причины (нестабильная производительность, отсутствие времени на тренинги для руководства, утверждения типа «раньше такого не было», чрезмерное внимание только к одному аспекту agile разработки и т.д.) — это явный показатель того, что на уровне руководства есть явное непонимание того, что же такое agile и как туда попасть.
И вы не сможете исправить это, болтая у кулера про то, как руководство «не покупает». И посмотрите правде в глаза: просто поправлять людей на встречах — это прекрасный путь в никуда.
Напротив, найдите самую неприятную корневую проблему и займитесь ей. Если это тренинги, сделайте их информативными. Если проблема в выравнивании понимания, задавайте уточняющие вопросы для того, чтобы выявить противоречия. Если действующий водопад прячет проблемы и вызывает вопросы к agile, поднимите исторические данные и решите проблему предсказуемости.
Подумайте о проблемах руководства, не воспринимая «они не покупают» как ограничение. Не пытайтесь их переспорить. Найдите пути расширения собственных возможностей.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333032/


Метки:  

Как мы упоролись и откалибровали кофе-машину на спектрофотометре

Пятница, 14 Июля 2017 г. 08:38 + в цитатник

Однажды, посреди рабочего дня мы внезапно осознали, что мы больше не можем так жить. Душа требовала совершить что-то бессмысленное и беспощадное во имя науки. И мы решили откалибровать кофе-машину. Нормальные люди тыкают в дефолтную кнопку и пьют все, что вытечет из кофеварки. Чуть более продвинутые для этого открывают инструкцию и тщательно ей следуют. Может быть еще читают рекомендации обжарщика, если конечно это не прогорклые noname зерна, которые пару лет лежали на безымянном складе. Нас к нормальным можно отнести с большой натяжкой, поэтому мы решили идти своим путем. Короче говоря, под легкой кофеиновой интоксикацией от седьмой чашки эспрессо мы решили задействовать весь возможный арсенал лаборатории, чтобы получить эталонный напиток.
Добро пожаловать в мир безумия, ультрацентрифуг, спектрофотометрии кофе в специальных планшетах и небольшого количества python, pandas и seaborn, чтобы визуализировать все это безобразие.

Правильная экстракция


Для начала, надо понять, что мы хотим получить. Основной смысл правильной настройки кофеварки в том, чтобы получить сбалансированный профиль экстракции. При этом не меньше половины успеха зависит от правильного зерна, которое было обжарено с соблюдением всех деталей термопрофиля. У хреновых обжарщиков зерна могут быть неравномерной обжарки или с другими дефектами. Но даже идеальный кофе можно превратить в жуткое пойло, если неправильно его приготовить.
Оптимальная температура для классического способа приготовления кофе — 90-95°С. При этом количество зерен должно быть примерно 10-20 г/ 100 мл воды. Также надо учитывать, что процесс экстракции идет неравномерно, от более легких и летучих к менее растворимым компонентам. Вся беда заключается в том, что при выходе за оптимальные значения температуры воды, степени компрессии кофейной таблетки (для эспрессо), степени помола, соотношения кофе к воде или времени мы можем не успеть «вытащить» из зерна все, что нам нужно. Или наоборот захватить чрезмерное количество тяжелых фракций, испортив вкус и баланс напитка. В частности, при гипер-экстракции в чашку попадает избыточное количество хлорогеновых кислот, которые заставляют кофе чрезмерно горчить и сдвигают баланс вкуса в кислую сторону. При недостаточной экстракции будет что-то водянистое и печальное на вкус.


Хининовая и гидроксикоричные кислоты, структурная основа хлорогеновых кислот

Подготовка эксперимента


В автоматической кофе-машине нам доступны для регулировки только два параметра: помол и компрессия. Степень помола определяется механическим вращением регулятора, который устанавливает зазор между жерновами. Компрессия свежесмолотой кофейной таблетки предустановлена и имеет 5 условных уровней сжатия. Наша задача состоит в подборе оптимальных параметров, при которых концентрация и баланс растворенных веществ будут давать идеальный вкус.
Сам кофе для тестирования нам прислали для бесчеловечных экспериментов из Торрефакто, за что им огромное спасибо. Две основные категории: B и C (темная и светлая обжарка в их классификации).
image
Гондурас Сан-Маркос (источник)
Темная обжарка. Мы как-то уже перешли на среднюю, но для сравнения этот сорт очень достойный вариант. Особенно хорош с молоком, но для наших задач мы будем готовить из него эспрессо. Вкус довольно простой, без тонких нюансов, но очень насыщенный.

image
Бразилия Ипанема Дульче (источник)
Просто офигительный ароматный сорт, с очень сбалансированным вкусом и сладкой фруктовой кислинкой. Само зерно содержит большое количество углеводов, что и придает легкую сладость.

Для каждого сорта и каждого из пяти уровней сжатия кофейной таблетки выбирается по 8 чашек образцов. Заодно сотрудники лаборатории плюются или радуются полученному результату. Вслепую, естественно. Выбирают оптимальные по вкусу образцы, чтобы потом сравнить с объективными показателями аппаратного исследования.

Глубокая заморозка


Нам себя стало жалко и мы не стали пить 80 чашек кофе за один день. Поэтому образцы маркировались и забрасывались в морозилку. Милая такая морозильная камера, с температурой около -90 градусов. Жрет 4 киловатта, но в итоге внутри даже углекислый газ выпадает в виде снежка на стенках. Идеальный вариант.





Когда все образцы готовы, достаем их из морозильной камеры и кладем в орбитальный шейкер. Да мне тоже нравится, как это звучит. Почти как орбитальный планетарный лазер, но это просто шейкер. Ждем полной разморозки образцов и заодно все хорошо все перемешиваем.

Разливаем по пробиркам и центрифугируем



Для начала надо очень глубокомысленно осмотреть образцы. Без этого чуда не произойдет)



Из больших пробирок забираем микропипеткой кофе и разливаем по маленьким пробиркам для центрифугирования. Каждую пробирку маркируем номером и вносим в отдельный журнал, чтобы потом не перепутать образцы.



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

Спектрофотометрия



Снова берем микропипетку и разливаем точные дозы образцов по отдельным лункам специального 48-луночного планшета.


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


Я у мамы оператор




Планшеты загружаются в спектрофотометр FilterMax от Molecular Devices. Там целая куча режимов исследования образца, различные варианты фильтров, лазерных источников излучения и тому подобного. Мы долго думали, что бы такого странного имело смысл измерить на кофе и решили, что измерять ту же флюоресценцию в ультрафиолете довольно бессмысленно. Решили оценить степень поглощения лазерного излучения на длине волны 450 нм. Эта длина волны синего лазера. В принципе, вполне логично. Насыщенный раствор кофе имеет красноватый оттенок и должен хорошо поглощать синюю часть спектра.
В результате мы получили таблицы поглощения для всех наших образцов. Однако было бы неплохо все это наглядно визуализировать. Так как я чаще всего в работе использую python и pandas с seaborn, то и данные мы сохраним в наиболее удобном для загрузки в pandas виде.
Степень поглощения в процентах в csv формате.
Roast,Compression,Absorbance
medium,level 1,21.31
medium,level 1,20.57
medium,level 1,24.49
medium,level 1,26.95
medium,level 1,20.49
medium,level 1,20.06
medium,level 1,21.22
medium,level 1,23.32
medium,level 2,28.09
medium,level 2,28.27
medium,level 2,23.13
medium,level 2,25.72
medium,level 2,26.75
medium,level 2,26.05
medium,level 2,26.92
medium,level 2,25.92
medium,level 3,32.88
medium,level 3,32.23
medium,level 3,33.13
medium,level 3,28.72
medium,level 3,28.82
medium,level 3,31.49
medium,level 3,32.31
medium,level 3,33.81
medium,level 4,38.68
medium,level 4,40.54
medium,level 4,39.34
medium,level 4,43.3
medium,level 4,41.48
medium,level 4,42.26
medium,level 4,42.73
medium,level 4,42.35
medium,level 5,57.62
medium,level 5,70.62
medium,level 5,70.74
medium,level 5,57.94
medium,level 5,77.62
medium,level 5,76.64
medium,level 5,69.12
medium,level 5,66.39
dark,level 1,27.54
dark,level 1,26.8
dark,level 1,30.72
dark,level 1,33.18
dark,level 1,26.72
dark,level 1,26.29
dark,level 1,27.45
dark,level 1,29.55
dark,level 2,34.32
dark,level 2,34.5
dark,level 2,29.36
dark,level 2,31.95
dark,level 2,32.98
dark,level 2,32.28
dark,level 2,33.15
dark,level 2,32.15
dark,level 3,39.11
dark,level 3,38.46
dark,level 3,39.36
dark,level 3,34.95
dark,level 3,35.05
dark,level 3,37.72
dark,level 3,38.54
dark,level 3,40.04
dark,level 4,44.91
dark,level 4,46.77
dark,level 4,45.57
dark,level 4,49.53
dark,level 4,47.71
dark,level 4,48.49
dark,level 4,48.96
dark,level 4,48.58
dark,level 5,63.85
dark,level 5,76.85
dark,level 5,76.97
dark,level 5,64.17
dark,level 5,83.85
dark,level 5,82.87
dark,level 5,75.35
dark,level 5,72.62


Рисуем графики с python, pandas и seaborn


Для построения графика вначале импортируем наш csv в виде pandas dataframe. После этого с помощью замечательной библиотеки seaborn и функции barplot построим график, сгруппированный по степени обжарки. Для большей контрастности используем палитру «Paired», она очень хороша при сравнении разных групп. Кстати, как вы помните, мы ранжировали образцы по вкусовым качествам. Дегустаторы кофе из нас так себе, но на удивление конценсус был достигнут. Оптимальный вкус у напитка был в том случае, когда степень поглощения на длине 450 нм была в районе 32%. Отрисуем соответствующую линию на графике с помощью plt.axhline.


Код на python
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns


df = pd.read_csv("data.csv")

sns.set()
sns.set_style("whitegrid")
sns.set_context("talk")

ax = sns.barplot(x="Compression", y="Absorbance", hue="Roast", data=df, palette="Paired")
ax.set(ylim=(0, 100), xlabel='Compression level', ylabel='Absorbance at 450 nm, %')
plt.axhline(32, alpha=0.4, color='black', linestyle='dashed', label='Optimal concentration')
plt.legend(loc='upper left')
plt.savefig("plot.png", dpi=300)
plt.show()



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

Чтобы сделать окончательно красиво, построим график отклонения нашего параметра от оптимума. Для этого из колонки Absorbance в нашем dataframe вычтем 32 (наш оптимум).
# Subtract optimal value
df['Absorbance'] = df['Absorbance'] - 32


Код на python
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

df = pd.read_csv("data.csv")

# Subtract optimal value
df['Absorbance'] = df['Absorbance'] - 32

sns.set()
sns.set_style("whitegrid")
sns.set_context("talk")

ax = sns.barplot(x="Compression", y="Absorbance", hue="Roast", data=df, palette="Paired")
ax.set(xlabel='Compression level', ylabel='Deviation from optimal concentration')
plt.legend(loc='upper left')
plt.savefig("plot_diverging.png", dpi=300)
plt.show()



Как-то так, бессмысленно и беспощадно, можно получить чашку идеального эспрессо)
P.S. Если смогу найти время, то будет еще пост про скоростную ультразвуковую экстракцию ледяного кофе. Образцы есть, будем творить странное дальше.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331754/


Метки:  

Рано закапывать Java

Пятница, 14 Июля 2017 г. 05:23 + в цитатник
image

Много было сказано про «красоту» кода на Java, но на мой взгляд, главное — не инструмент, а умение им пользоваться. Под катом попытка написать декларативный DSL для вёрстки под Android даже не изобретая новый язык программирования!



Вёрстка на Java всегда ассоциировалась у меня с болью.

float dp = getResources().getDisplayMetrics().density;

FrameLayout root = new FrameLayout(this);
root.setBackgroundColor(RED);

root.setLayoutParams(
        new ViewGroup.LayoutParams(
                MATCH_PARENT,
                (int)(100f*dp)));

FrameLayout child = new FrameLayout(this);
child.setBackgroundColor(GREEN);

FrameLayout.LayoutParams childParams =
        new FrameLayout.LayoutParams(
                (int)(50f*dp),
                (int)(50f*dp));

childParams.gravity = CENTER;

child.setLayoutParams(childParams);

root.addView(child);


Результат:



И дело даже не в том, что код выглядит страшно (а он страшный как чёрт). Основная проблема в том, что в нём невозможно не ошибиться. Я 3 раза перезаливал сюда этот код, в первый и второй разы наивно полагая, что смогу всё правильно написать сразу, и тщательно перепроверив всё лишь в третий. Скажете, что дело в моей невнимательности и будете правы, но если даже в такой простой вёрстке можно накосячить, то что уж говорить про что-то более сложное?

Но почему с вёрсткой на Java всё так грустно? На мой взгляд основная причина — возможность верстать в xml и отсутствие инструмента для вёрстки на Java.

Минусы xml



Для меня их 3.

Первый — оверхед.
Зачем тратить ресурсы и без того не очень мощных устройств на Android на такие операции, как inflate и findViewById? На оптимизацию этих операций было потрачено много времени и сил, но они от этого не стали бесплатными.

Второй — громоздкость.



    



Удручает необходимость дублировать тэги, писать перед каждым атрибутом «android:», а после вырабатывать частиную слепоту чтобы читать этот код.

Третий — ограниченность языка.
Допустим, я хочу сделать подпись к автару больше самого аватара на 10dp.





Но этого сделать нельзя потому что xml не поддерживает выражения.

Почему не Anko?



Anko — это DSL, с помощью которого можно декларативно описывать разметку на Kotlin.

frameLayout {

    backgroundColor = RED

    frameLayout {
        backgroundColor = GREEN
    }.lparams(dip(50), dip(50)) {
        gravity = CENTER
    }

}.lparams(matchParent, dip(100))


Получаем все возможности полноценного языка программирования, лучшую производительность и даже не мучаемся с вёрсткой интерфейса на Java!

Всё прекрасно, но, на мой взгляд, неприлично тянуть за собой целый рантайм языка при разработке библиотек. 500 кб — не так много для конечного приложения, но для библиотеки — явно перебор.

JAnko



Как оказалось, возможностей Java хватает чтобы верстать декларативно.

new frameLayout(this) {{

    new lparams(this) {{
        width  = MATCH_PARENT;
        height = dip(100);
    }}._();

    backgroundColor = RED;

    new frameLayout(this) {{

        new lparams(this) {{
            width  = dip(50);
            height = dip(50);
            gravity = CENTER;
        }}._();

        backgroundColor = GREEN;

    }}._();

}}._();


Язык поддерживает блоки кода без названия. Они выполняются перед конструктором класса сразу после конструктора класса-родителя.

class A {
    // block
    {
        // some code
    }
}


Именно эту возможность Java я использовал чтобы не писать название переменной, в которой лежит виджет для изменения каждого его свойства.

Пример с аватаркой и подписью.

new imageView(this) {{

    new lparams(this) {{
        width = dimen(R.dimen.avatarSide);
        height = dimen(R.dimen.avatarSide);
    }}._();

}}._();

new textView(this) {{

    new lparams(this) {{
        width = dimen(R.dimen.avatarSide) + dip(10);
        height = WRAP_CONTENT;
    }}._();

}}._();


Выглядит немного странно.



Похоже на человека в монокле и оператор на scala. Но для proof of concept — вполне достаточно.

Итоги



0). На Kotlin код выглядит вот так:

object : frameLayout(this) {
    init {

        object : lparams(this) {
            init {
                width = MATCH_PARENT
                height = dip(100f)
            }
        }.`_`()

        backgroundColor = RED

        object : frameLayout(this) {
            init {

                object : lparams(this) {
                    init {
                        width = dip(50f)
                        height = dip(50f)
                        gravity = CENTER
                    }
                }.`_`()

                backgroundColor = GREEN

            }
        }.`_`()

    }

}.`_`()


1). Вес aar составляет 12кб
2). Idea не сбивает форматирование
3). Коду на Java иногда можно придать неожиданный для Java вид

Репозиторий с библиотекой и примерами: github.com/a-dminator/janko
Закопать обратно?

Проголосовало 87 человек. Воздержалось 45 человек.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331790/


Метки:  

Bare words в JavaScript

Пятница, 14 Июля 2017 г. 02:12 + в цитатник
Однажды отдыхая в диване, я вспомнил мимолетный замечательный ролик из своего детства, который назывался WAT.
И там была такая штука как bare words, которая показывалась на примере Ruby.
image
«Голые слова» (barewords) — это слова без кавычек, которые вы можете использовать в качестве строк.

В JavaScript их, вроде как и нет, поэтому я попробовал их в него пропихнуть, что в целом получилось.

Получился вот такой код:
with(bareWords) {
    alert(Иван + Ургант)
    console.log(We, can, use, bare, words)
}
И он успешно выводил нужные фразы.
А реализовано это было вот так:
try {
    let self = this
    window.bareWords = new Proxy({}, {
        has: function(target, name) {
            return !(name in self)
        },
        get: function(target, name) {
            return name
        },
    })
} catch(e) {
    console.error('Your browser doesn't support bare words.')
}
Как видите, чтобы мы смогли использовать голые слова, мы использовали такие фичи джаваскрипта, как Proxy и with.

При попытке получить свойство из this, из-за происков with, запрос идет в прокси, где мы и возвращаем строковое значение вместо злобной ошибки о отсутствии переменной.

Спасибо за внимание.
image
Если что, вот ссылочка на Github
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333030/


Метки:  

[Перевод] Создание движка для блога с помощью Phoenix и Elixir / Часть 10. Тестирование каналов

Пятница, 14 Июля 2017 г. 00:41 + в цитатник


От переводчика: «Elixir и Phoenix — прекрасный пример того, куда движется современная веб-разработка. Уже сейчас эти инструменты предоставляют качественный доступ к технологиям реального времени для веб-приложений. Сайты с повышенной интерактивностью, многопользовательские браузерные игры, микросервисы — те направления, в которых данные технологии сослужат хорошую службу. Далее представлен перевод серии из 11 статей, подробно описывающих аспекты разработки на фреймворке Феникс казалось бы такой тривиальной вещи, как блоговый движок. Но не спешите кукситься, будет действительно интересно, особенно если статьи побудят вас обратить внимание на Эликсир либо стать его последователями.»


В этой части мы научимся тестировать каналы.


На чём мы остановились


В конце прошлой части мы доделали классную систему «живых» комментариев для блога. Но к ужасу, на тесты не хватило времени! Займёмся ими сегодня. Этот пост будет понятным и коротким, в отличие от чересчур длинного предыдущего.


Прибираем хлам


Прежде, чем перейти к тестам, нам нужно подтянуть несколько мест. Во-первых, давайте включим
флаг approved в вызов broadcast. Таким образом мы сможем проверять в тестах изменение состояния подтверждения комментариев.


new_payload = payload
  |> Map.merge(%{
    insertedAt: comment.inserted_at,
    commentId: comment.id,
    approved: comment.approved
  })

broadcast socket, "APPROVED_COMMENT", new_payload

Также нужно изменить файл web/channels/comment_helper.ex, чтобы он реагировал на пустые данные, отправляемые в сокет запросами на одобрение/удаление комментариев. После функции approve добавьте:


def approve(_params, %{}), do: {:error, "User is not authorized"}
def approve(_params, nil), do: {:error, "User is not authorized"}

А после функции delete:


def delete(_params, %{}), do: {:error, "User is not authorized"}
def delete(_params, nil), do: {:error, "User is not authorized"}

Это позволит сделать код проще, обработку ошибок – лучше, а тестирование – легче.


Тестируем хелпер комментариев


Будем использовать фабрики, которые написали с помощью ExMachina ранее. Нам нужно протестировать создание комментария, а также одобрение/отклонение/удаление комментария на основе авторизации пользователя. Создадим файл test/channels/comment_helper_test.exs, а затем добавим подготовительный код в начало:


defmodule Pxblog.CommentHelperTest do
  use Pxblog.ModelCase

  alias Pxblog.Comment
  alias Pxblog.CommentHelper

  import Pxblog.Factory

  setup do
    user        = insert(:user)
    post        = insert(:post, user: user)
    comment     = insert(:comment, post: post, approved: false)
    fake_socket = %{assigns: %{user: user.id}}

    {:ok, user: user, post: post, comment: comment, socket: fake_socket}
  end

  # Insert our tests after this line

end

Здесь используется модуль ModelCase для добавления возможности использования блока setup. Ниже добавляются алиасы для модулей Comment, Factory и CommentHelper, чтобы можно было проще вызывать их функции.


Затем идёт настройка некоторых основных данных, которые можно будет использовать в каждом тесте. Также как и раньше, здесь создаются пользователь, пост и комментарий. Но обратите внимание на создание "фальшивого сокета", который включает в себя лишь ключ assigns. Мы можем передать его в CommentHelper, чтобы тот думал о нём как о настоящем сокете.


Затем возвращается кортеж, состоящий из атома :ok и словарь-список (также как и в других тестах). Давайте уже напишем сами тесты!


Начнём с простейшего теста на создание комментария. Так как комментарий может написать любой пользователь, здесь не требуется никакой специальной логики. Мы проверяем, что комментарий действительно был создан и… всё!


test "creates a comment for a post", %{post: post} do
  {:ok, comment} = CommentHelper.create(%{
    "postId" => post.id,
    "author" => "Some Person",
    "body" => "Some Post"
  }, %{})
  assert comment
  assert Repo.get(Comment, comment.id)
end

Для этого вызываем функцию create из модуля CommentHelper и передаём в неё информацию, как будто это информация была получена из канала.


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


test "approves a comment when an authorized user", %{post: post, comment: comment, socket: socket} do
  {:ok, comment} = CommentHelper.approve(%{"postId" => post.id, "commentId" => comment.id}, socket)
  assert comment.approved
end

test "does not approve a comment when not an authorized user", %{post: post, comment: comment} do
  {:error, message} = CommentHelper.approve(%{"postId" => post.id, "commentId" => comment.id}, %{})
  assert message == "User is not authorized"
end

Схожим с созданием комментария образом, мы вызываем функцию CommentHelper.approve и передаём в неё информацию "из канала". Мы передаём "фальшивый сокет" в функцию и она получает доступ к значению assign. Мы тестируем их оба с помощью валидного сокета (с вошедшим в систему пользователем) и невалидного сокета (с пустым assign). Затем просто проверяем, что получаем комментарий в положительном исходе и сообщение об ошибке в отрицательном.


Теперь о тестах на удаление (которые по сути идентичны):


test "deletes a comment when an authorized user", %{post: post, comment: comment, socket: socket} do
  {:ok, comment} = CommentHelper.delete(%{"postId" => post.id, "commentId" => comment.id}, socket)
  refute Repo.get(Comment, comment.id)
end

test "does not delete a comment when not an authorized user", %{post: post, comment: comment} do
  {:error, message} = CommentHelper.delete(%{"postId" => post.id, "commentId" => comment.id}, %{})
  assert message == "User is not authorized"
end

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


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


$ mix test test/channels/comment_helper_test.exs --cover

Она создаст в директории [project root]/cover отчёт, который скажет нам какой код не покрыт тестами. Если все тесты зелёные, откройте файл в браузере ./cover/Elixir.Pxblog.CommentHelper.html. Если вы видите красный цвет, значит этот код не покрыт тестами. Отсутствие красного цвета означает 100% покрытие.


Полностью файл с тестами хелпера комментариев выглядит следующим образом:


defmodule Pxblog.CommentHelperTest do
  use Pxblog.ModelCase

  alias Pxblog.Comment
  alias Pxblog.CommentHelper

  import Pxblog.Factory

  setup do
    user        = insert(:user)
    post        = insert(:post, user: user)
    comment     = insert(:comment, post: post, approved: false)
    fake_socket = %{assigns: %{user: user.id}}

    {:ok, user: user, post: post, comment: comment, socket: fake_socket}
  end

  # Insert our tests after this line
  test "creates a comment for a post", %{post: post} do
    {:ok, comment} = CommentHelper.create(%{
      "postId" => post.id,
      "author" => "Some Person",
      "body" => "Some Post"
    }, %{})
    assert comment
    assert Repo.get(Comment, comment.id)
  end

  test "approves a comment when an authorized user", %{post: post, comment: comment, socket: socket} do
    {:ok, comment} = CommentHelper.approve(%{"postId" => post.id, "commentId" => comment.id}, socket)
    assert comment.approved
  end

  test "does not approve a comment when not an authorized user", %{post: post, comment: comment} do
    {:error, message} = CommentHelper.approve(%{"postId" => post.id, "commentId" => comment.id}, %{})
    assert message == "User is not authorized"
  end

  test "deletes a comment when an authorized user", %{post: post, comment: comment, socket: socket} do
    {:ok, comment} = CommentHelper.delete(%{"postId" => post.id, "commentId" => comment.id}, socket)
    refute Repo.get(Comment, comment.id)
  end

  test "does not delete a comment when not an authorized user", %{post: post, comment: comment} do
    {:error, message} = CommentHelper.delete(%{"postId" => post.id, "commentId" => comment.id}, %{})
    assert message == "User is not authorized"
  end
end

Тестируем канал комментариев


Генератор уже создал для нас основу тестов каналов, осталось наполнить их мясом. Начнём с добавления алиаса Pxblog.Factory для использования фабрик в блоке setup. Собственно, всё как и раньше. Затем необходимо настроить сокет, а именно, представиться созданным пользователем и подключиться к каналу комментариев созданного поста. Оставим тесты ping и broadcast на месте, но удалим тесты, связанные с shout, поскольку у нас больше нет этого обработчика. В файле test/channels/comment_channel_test.exs:


defmodule Pxblog.CommentChannelTest do
  use Pxblog.ChannelCase

  alias Pxblog.CommentChannel
  alias Pxblog.Factory

  setup do
    user    = Factory.create(:user)
    post    = Factory.create(:post, user: user)
    comment = Factory.create(:comment, post: post, approved: false)

    {:ok, _, socket} =
      socket("user_id", %{user: user.id})
      |> subscribe_and_join(CommentChannel, "comments:#{post.id}")

    {:ok, socket: socket, post: post, comment: comment}
  end

  test "ping replies with status ok", %{socket: socket} do
    ref = push socket, "ping", %{"hello" => "there"}
    assert_reply ref, :ok, %{"hello" => "there"}
  end

  test "broadcasts are pushed to the client", %{socket: socket} do
    broadcast_from! socket, "broadcast", %{"some" => "data"}
    assert_push "broadcast", %{"some" => "data"}
  end
end

У нас уже написаны довольно полноценные тесты для модуля CommentHelper, так что здесь оставим тесты, непосредственно связанные с функциональностью каналов. Создадим тест для трёх сообщений: CREATED_COMMENT, APPROVED_COMMENT и DELETED_COMMENT.


test "CREATED_COMMENT broadcasts to comments:*", %{socket: socket, post: post} do
  push socket, "CREATED_COMMENT", %{"body" => "Test Post", "author" => "Test Author", "postId" => post.id}
  expected = %{"body" => "Test Post", "author" => "Test Author"}
  assert_broadcast "CREATED_COMMENT", expected
end

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


Начинаем с передачи в тест сокета и поста, созданных в блоке setup. Следующей строкой мы отправляем в сокет событие CREATED_COMMENT вместе с ассоциативным массивом, схожим с тем, что клиент на самом деле отправляет в сокет.


Далее описываем наши "ожидания". Пока что вы не можете определить список, ссылающийся на любые другие переменные внутри функции assert_broadcast, так что следует выработать привычку по определению ожидаемых значений отдельно и передачу переменной expected в вызов assert_broadcast. Здесь мы ожидаем, что значения body и author совпадут с тем, что мы передали внутрь.


Наконец, проверяем, что сообщение CREATED_COMMENT было транслировано вместе с ожидаемым ассоциативным массивом.


Теперь переходим к событию APPROVED_COMMENT:


test "APPROVED_COMMENT broadcasts to comments:*", %{socket: socket, post: post, comment: comment} do
  push socket, "APPROVED_COMMENT", %{"commentId" => comment.id, "postId" => post.id, approved: false}
  expected = %{"commentId" => comment.id, "postId" => post.id, approved: true}
  assert_broadcast "APPROVED_COMMENT", expected
end

Этот тест в значительной степени похож на предыдущий, за исключением того, что мы передаём в сокет значение approved равное false и ожидаем увидеть после выполнения значение approved равное true. Обратите внимание, что в переменной expected мы используем commentId и postId как указатели на comment.id и post.id. Это выражения вызовут ошибку, поэтому нужно использовать разделение ожидаемой переменной в функции assert_broadcast.


Наконец, взглянем на тест для сообщения DELETED_COMMENT:


test "DELETED_COMMENT broadcasts to comments:*", %{socket: socket, post: post, comment: comment} do
  payload = %{"commentId" => comment.id, "postId" => post.id}
  push socket, "DELETED_COMMENT", payload
  assert_broadcast "DELETED_COMMENT", payload
end

Ничего особо интересного. Передаём стандартные данные в сокет и проверяем, что транслируем событие об удалении комментария.


Подобно тому, как мы поступали с CommentHelper, запустим тесты конкретно для этого файла с опцией --cover:


$ mix test test/channels/comment_channel_test.exs --cover

Вы получите предупреждения, что переменная expected не используется, которые можно благополучно проигнорировать.


test/channels/comment_channel_test.exs:31: warning: variable expected is unused
test/channels/comment_channel_test.exs:37: warning: variable expected is unused

Если вы открыли файл ./cover/Elixir.Pxblog.CommentChannel.html и не видите ничего красного, то можете кричать "Ура!". Полное покрытие!


Финальная версия теста CommentChannel полностью должна выглядеть так:


defmodule Pxblog.CommentChannelTest do
  use Pxblog.ChannelCase

  alias Pxblog.CommentChannel
  import Pxblog.Factory

  setup do
    user    = insert(:user)
    post    = insert(:post, user: user)
    comment = insert(:comment, post: post, approved: false)

    {:ok, _, socket} =
      socket("user_id", %{user: user.id})
      |> subscribe_and_join(CommentChannel, "comments:#{post.id}")

    {:ok, socket: socket, post: post, comment: comment}
  end

  test "ping replies with status ok", %{socket: socket} do
    ref = push socket, "ping", %{"hello" => "there"}
    assert_reply ref, :ok, %{"hello" => "there"}
  end

  test "broadcasts are pushed to the client", %{socket: socket} do
    broadcast_from! socket, "broadcast", %{"some" => "data"}
    assert_push "broadcast", %{"some" => "data"}
  end

  test "CREATED_COMMENT broadcasts to comments:*", %{socket: socket, post: post} do
    push socket, "CREATED_COMMENT", %{"body" => "Test Post", "author" => "Test Author", "postId" => post.id}
    expected = %{"body" => "Test Post", "author" => "Test Author"}
    assert_broadcast "CREATED_COMMENT", expected
  end

  test "APPROVED_COMMENT broadcasts to comments:*", %{socket: socket, post: post, comment: comment} do
    push socket, "APPROVED_COMMENT", %{"commentId" => comment.id, "postId" => post.id, approved: false}
    expected = %{"commentId" => comment.id, "postId" => post.id, approved: true}
    assert_broadcast "APPROVED_COMMENT", expected
  end

  test "DELETED_COMMENT broadcasts to comments:*", %{socket: socket, post: post, comment: comment} do
    payload = %{"commentId" => comment.id, "postId" => post.id}
    push socket, "DELETED_COMMENT", payload
    assert_broadcast "DELETED_COMMENT", payload
  end
end

Финальные штрихи


Так как отчёт о покрытии тестами можно легко создать с помощью Mix, то не имеет смысла включать его в историю Git, так что откройте файл .gitignore и добавьте в него следующую строчку:


/cover

Вот и всё! Теперь у нас есть полностью покрытый тестами код каналов (за исключением Javascript-тестов, которые представляют собой отдельный мир, не вписывающийся в эту серию уроков). В следующей части мы перейдём к работе над UI, сделаем его немного симпатичнее и более функциональнее, а также заменим стандартные стили, логотипы и т.п., чтобы проект выглядел более профессионально. В дополнение, удобство использования нашего сайта сейчас абсолютно никакое. Мы поправим и это, чтобы людям хотелось использовать нашу блоговую платформу!


А вы, уважаемые читатели, подписывайтесь на нашу рассылку, читайте другие интересные статьи, задавайте вопросы в чате, присоединяйтесь к каналу, ходите на митапы и пишите классный код на Эликсире! Ваш Вунш.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333020/


Метки:  

[Перевод] Захват всех доменов .io с помощью таргетированной регистрации

Пятница, 14 Июля 2017 г. 00:13 + в цитатник
В предыдущей статье мы обсуждали захват доменных расширений .na, .co.ao и .it.ao разными хитростями с DNS. Сейчас рассмотрим угрозу компрометации домена верхнего уровня (TLD) и как нужно действовать злоумышленнику, чтобы достичь поставленной цели. Одним из довольно простых методов видится регистрация доменного имени одного из авторитативных серверов имён этой TLD. Поскольку в TLD авторитативные серверы могут размещаться на произвольных доменах, то есть вероятность зарегистрировать такой домен, воспользовавшись ошибкой из-за неправильной конфигурации, истечения срока действия или других ошибок. Затем этот сервер можно использовать для выдачи новых DNS-записей в целой доменной зоне.

Приведу здесь соответствующую цитату из предыдущей статьи:

Такой вариант казался верной дорогой к победе, так что я потратил много времени на разработку инструментария для проверки ошибок этого типа. По сути, этот процесс состоит в записи всех хостов серверов имён для данного домена — и проверке, когда истечёт срок регистрации какого-нибудь из корневых доменов и он станет доступен для регистрации. Основная проблема в том, что многие регистраторы не говорят, что домен полностью свободен, пока вы реально не попробуете его купить. Кроме того, было несколько случаев, когда у сервера заканчивался срок регистрации, но по какой-то причине домен был недоступен для регистрации, хотя не был помечен как зарезервированный. В результате такого сканирования удалось зафиксировать много перехватов доменов в закрытых зонах (.gov, .edu, .int и др.), но не самих TLD.

Как выяснилось, такой способ не только подходит для атаки TLD, но в реальности привёл к крупнейшему захвату TLD на сегодняшний день.

Аномалия .io


В пятницу вечером, составляя график путей делегирования DNS для различных TLD, мой скрипт выдал неожиданный результат для зоны .io:



Одна из функций этого скрипта (который называется TrustTrees) заключается в том, что ему можно передать ключ Gandi API — а он проверит, есть ли в цепочке делегирования серверы имён со свободными для регистрации доменными именами. Скрипт показал, что в зоне .io многие домены серверов имён доступны для покупки! Впрочем, это не означает, что их в самом деле можно купить. Раньше я неоднократно сталкивался с тем, что свободное доменное имя нельзя зарегистрировать, потому что оно «зарезервировано». Я решил пойти на сайт регистратора NIC.IO и проверить. Быстренько поискав доменное имя ns-a1.io, с удивлением обнаружил, что оно выставлено на продажу по цене $90,00. Поскольку было около полуночи (наверное, автор считает это «детским» временем — прим.пер.), я решил продолжить и реально попытаться купить этот домен. Вскоре пришло письмо с подтверждением, что регистрация доменного имени «обрабатывается»:



Не совсем понятно, почему письмо пришло от компании 101Domain. Вероятно, она занимается регистрацией доменов в зоне .io (возможно, и управляет всем реестром?). В этот время уже далеко за полночь я пожал плечами и пошёл спать, и забыл об этом случае, пока в среду утром не пришло такое письмо, когда я уже собирался уходить на работу:



Тут я вспомнил о своём пятничном расследовании и всех подробностях регистрации. Пришлось вернуться за компьютер: интересно, неужели я действительно получил контроль над серверами имён доменной зоны .io? Я быстренько запустил команду dig для домена — и убедился, что мои тестовые серверы имён DNS (ns1/ns2.networkobservatory.com ) действительно записаны на ns-a1.io:

bash-3.2$ dig NS ns-a1.io

; <<>> DiG 9.8.3-P1 <<>> NS ns-a1.io
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8052
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;ns-a1.io.          IN  NS

;; ANSWER SECTION:
ns-a1.io.       86399   IN  NS  ns2.networkobservatory.com.
ns-a1.io.       86399   IN  NS  ns1.networkobservatory.com.

;; Query time: 4 msec
;; SERVER: 2604:5500:16:32f9:6238:e0ff:feb2:e7f8#53(2604:5500:16:32f9:6238:e0ff:feb2:e7f8)
;; WHEN: Wed Jul  5 08:46:44 2017
;; MSG SIZE  rcvd: 84

bash-3.2$

Запросил один из корневых серверов DNS и снова убедился, что этот домен указан в качестве авторитативного сервера имён для доменной зоны первого уровня .io:

bash-3.2$ dig NS io. @k.root-servers.net.

; <<>> DiG 9.8.3-P1 <<>> NS io. @k.root-servers.net.
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19611
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 7, ADDITIONAL: 12
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;io.                IN  NS

;; AUTHORITY SECTION:
io.         172800  IN  NS  ns-a1.io.
io.         172800  IN  NS  ns-a2.io.
io.         172800  IN  NS  ns-a3.io.
io.         172800  IN  NS  ns-a4.io.
io.         172800  IN  NS  a0.nic.io.
io.         172800  IN  NS  b0.nic.io.
io.         172800  IN  NS  c0.nic.io.

;; ADDITIONAL SECTION:
ns-a1.io.       172800  IN  AAAA    2001:678:4::1
ns-a2.io.       172800  IN  AAAA    2001:678:5::1
a0.nic.io.      172800  IN  AAAA    2a01:8840:9e::17
b0.nic.io.      172800  IN  AAAA    2a01:8840:9f::17
c0.nic.io.      172800  IN  AAAA    2a01:8840:a0::17
ns-a1.io.       172800  IN  A   194.0.1.1
ns-a2.io.       172800  IN  A   194.0.2.1
ns-a3.io.       172800  IN  A   74.116.178.1
ns-a4.io.       172800  IN  A   74.116.179.1
a0.nic.io.      172800  IN  A   65.22.160.17
b0.nic.io.      172800  IN  A   65.22.161.17
c0.nic.io.      172800  IN  A   65.22.162.17

;; Query time: 70 msec
;; SERVER: 2001:7fd::1#53(2001:7fd::1)
;; WHEN: Wed Jul  5 08:46:14 2017
;; MSG SIZE  rcvd: 407

Ничего себе! Я сразу же соединился по SSH со своим тестовым сервером DNS, к которому теперь был привязан этот домен, и быстренько убил работающий BIND-сервер. Если ко мне повалит DNS-трафик, то я определённо не хочу возвращать плохие ответы людям, которые имеют законный доступ ко своим доменам .io. Поскольку BIND-сервер больше не обрабатывает запросы на порт 53, то все DNS-запросы автоматически перенаправятся на другие сервера имён этой TLD и не слишком повлияют на трафик (кроме небольшой задержки при резолвинге, пока DNS-клиенты достучаться до работающего сервера имён). Чтобы посмотреть, действительно ли ко мне пошли DNS-запросы, я быстренько запустил запись дампа tcpdump всего DNS-трафика в файл — и тут же на экран хлынули сотни запросов со случайных IP со всех уголков интернета. Похоже, я действительно обрабатывал трафике для всей доменной зоны. А ещё хуже: вероятно, это было только начало, потому что многие DNS-клиенты ещё руководствовались кешированными старыми DNS-записями, которые скоро обновятся.

Сообщение об проблеме безопасности в TLD


Поскольку мой сервер больше не отвечал на DNS-запросы, я думал только о том, как побыстрее исправить ситуацию. Больше всего меня беспокоило, что в продаже осталось ещё много доменов для серверов имён, которые может зарегистрировать кто угодно, у кого есть деньги и знания, что нужно делать. Я поискал контакты администраторов .io TLD в корневой базе данных IANA:



Затем составил описание проблемы и отправил письма на оба адреса, отметив необходимость срочного решения. Указал, что домены для других серверов имён TLD по-прежнему доступны для регистрации и что если проблему не решат в течение нескольких часов, я зарегистрирую их, чтобы защитить зону. Немедленно после отправки письма я получил отлуп от почтового сервера с указанием, что адрес adminstrator@nic.io не существует:



Это явно не прибавляло уверенности, что кто-то прочитает моё письмо. Так что я решил потратить немного денег и купить остальные домены серверов имён, чтобы никто не хакнул TLD. Как и в первом случае, я явно настроил DNS-сервер не принимать входящие запросы, так что не вмешивался в обычный трафик .io. Эти заказы на покупку оформили достаточно быстро:



Фух! По крайней мере, их не взломает какой-нибудь случайный хакер, а я могу с чистым сердцем идти на работу.

Позже в этот день я позвонил по телефону в поддержку NIC.IO и спросил действующий адрес электронной почты кого-нибудь из сотрудников отдела безопасности для TLD. Агент заверил меня, что подходящим адресом для такого запроса будет abuse@101domain.com (я дважды переспросил его на этот счёт для уверенности). Хотя такой адрес не выглядит подходящим, но я переправил туда отчёт с описанием проблемы и надеялся, что его хотя бы перешлют компетентному специалисту. Поскольку не было возможности узнать другие адреса, я ждал ответа.

«Упс» — исправление путём аннулирования регистрации доменов


Около полудня следующего дня пришла серия уведомлений от 101Domains о том, что регистрации доменов отменяются, деньги возвращаются на карточку, а мой «тикет в суппорте» отвечен и закрыт:



Удивительно, но похоже на то, что адрес abuse@ действительно оказался правильным адресом для решения проблемы. Зайдя в свой аккаунт 101Domain, я обнаружил такое письмо из юридического отдела 101Domain:



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

Возможные последствия


Учитывая, что мы зарегистрировали четыре из семи авторитативных серверов имён для доменной зоны .io, мы могли бы изменить/перенаправить записи DNS для всех зарегистрированных доменов .io. Более того, поскольку мы контролировали более половины серверов имён, то DNS-запросы с большей вероятностью шли бы к нам без всяких дополнительных трюков вроде долгих TTL ответов и других, которые ещё больше увеличивают наши шансы. Даже при условии немедленной реакции на такого рода атаку потребуется некоторое время, пока кешированные записи полностью сотрутся из всех мировых DNS-резолверов.

Для исправления ситуации помогло бы то, что в доменной зоне .io поддерживается технология DNSSEC. Это значит, что если ваш резолвер тоже поддерживает эту технологию, то можно защититься от подобных атак с подделкой записей DNS. Но как я говорил в прошлой статье, почти никто не использует DNSSEC. Я редко вижу хоть какую-либо поддержку этой технологии, если только сам специально не настраиваю резолвер.

*Одно замечание, не имеющее отношения к теме: важно не забыть остановить tcpdump после запуска. Если этого не сделать, то у вас хорошие шансы забить всё место на диске VPS данными DNS. Обратите внимание на то, что все записанные для отладочных целей данные DNS очищены от конфиденциальной информации о пользователях.

Защита и безопасность TLD


Я довольно подробно писал, как TLD могут избежать некоторых из этих проблем (для дополнительной информации см. «Выводы и последние мысли» в статье «Маршрут взлома национальной TLD — скрытые риски расширений доменных имён». В будущем я собираюсь составить более обширное руководство, как TLD и операторам расширений доменных имён отслеживать и предотвращать такие ситуации.

Приветы


Я обещал своему другу, что передам ему хакерский приветик в следующем посте в блоге, так что должен выполнить обещание :). Особая благодарность этим ребятам за моральную и «аморальную» поддержку (как говорил единственный и неповторимый HackerBadger).

Привет, HackerBadger, EngSec (вы знаете, кто вы), Hacke the planet, the gibson и всё такое.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333026/


Метки:  

Yet another tutorial: запускаем dotnet core приложение в docker на Linux

Четверг, 13 Июля 2017 г. 23:50 + в цитатник


В один пасмурный летний день, после посещения секции от авито на РИТ2017, до меня вдруг дошло, что хайп по поводу докера не смолкает уже пару лет и пора, наконец, уже его освоить. В качестве подопытного для упаковки был выбран dotnet core+C#, т. к. давно интересно было посмотреть, каково это — разрабатывать на C# под Linux.
Предупреждение читателю: статья ориентирована на совсем новичков в docker/dotnet core и писалась большей частью, как напоминалка для себя. Вдохновлялся я первыми 3 частями Docker Get Started Guide и неким блог-постом на english. У кого хорошо с английским, можно читать сразу их и в общем-то будет сильно похоже. Если же после всего вышенаписанного вы еще не передумали продолжить чтение, то добро пожаловать под кат.

Пререквизиты
Итак нам понадобится собственно Linux (в моем случае это была Ubuntu 16.04 под VirtualBox на Windows 10), dotnet core, docker, а также docker compose, чтобы было удобнее поднимать сразу несколько контейнеров за раз.
Никаких особых проблем с установкой возникнуть не должно. По крайней мере, у меня таковых не возникло.
Выбор среды разработки
Формально можно писать хоть в блокноте и отлаживаться через логи или консольные сообщения, но т.к. я несколько избалован, то все-таки хотелось получить нормальную отладку и, желательно, еще и нормальный рефакторинг.
Из того, что может под Linux, для себя я попробовал Visual Studio Code и JetBrains Rider.

Visual Studio Code
Что могу сказать — оно работает. Отлаживаться можно, синтаксис подсвечивается, но уж очень все незатейлево — осталось впечатление, что это блокнот с возможностью отладки.

Rider
По сути Idea, скрещенная с решарпером — все просто и понятно, если работал до этого с любой IDE от JetBrains. До недавнего времени не работал debug под linux, но в последней EAP сборке его вернули. Вообщем для меня выбор в пользу Rider был однозначен. Спасибо JetBrains за их классные продукты.


Создаем проект
Презреем в учебных целях кнопки Create Project различных IDE и cделаем все ручками через консоль.
1. Переходим в директорию нашего будущего проекта
2. посмотрим ради интереса, какие шаблоны мы можем использовать
dotnet new -all

3. создадим WebApi проект
dotnet new webapi

4. подтянем зависимости
dotnet restore

5. запустим наше приложение
dotnet run

6. Открываем http://localhost:5000/api/values и наслаждаемся работой C# кода на Linux

Готовим приложение к докеризации
Заходим в Program.cs и в настройке хоста добавляем
.UseUrls("http://*:5000") // listen on port 5000 on all network interfaces

В итоге должно получится что-то типа
public static void Main(string[] args)
{
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseUrls("http://*:5000") // listen on port 5000 on all network interfaces
        .UseStartup()
        .Build();

    host.Run();
}

Это нужно для того, чтобы мы смогли обратится к приложению, находящемуся внутри контейнера.
По дефолту Kestrel, на котором работает наше приложение, слушает http://localhost:5000. Проблема в том, что localhost является loopback-интерфейсом и при запуске приложения в контейнере доступен только внутри контейнера. Соответственно, докеризовав dotnet core приложение с дефолтной настройкой прослушиваемых url можно потом довольно долго удивляться, почему проброс портов не работает, и перечитывать свой docker-файл в поисках ошибок.
А еще можно добавить приложению немного функциональности
Передача параметров в приложение
При запуске контейнера хотелось бы иметь возможность передавать приложению параметры.
Беглое гугление показало, что если обойтись без экзотики типа обращения изнутри контейнера к сервису конфигурации, то можно использовать передачу параметров через переменные окружения или подмену файла конфига.
Отлично, будем передавать через переменные окружения.
Идем в Startup.cs publicpublic Startup(IHostingEnvironment env) и смотрим, что у нашего ConfigurationBuilder вызван метод AddEnvironmentVariables().
Собственно все — теперь можно инжектить параметры из переменных окружения куда угодно через DI.

Идентификатор инстанса
При старте инстанса приложения будем генерировать новый Guid и засунем его в IoC-контейнер, чтобы раздавать страждущим. Нужно это, например, для анализа логов от нескольких параллельно работающих инстансов сервиса.
Все тоже достаточно тривильно — у ConfigurationBuilder вызываем
 .AddInMemoryCollection(new Dictionary
                {
                    {"InstanseId", Guid.NewGuid().ToString()}
                })


После этих двух шагов public Startup(IHostingEnvironment env) будет выглядеть примерно так:
public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddInMemoryCollection(new Dictionary
        {
            {"InstanseId", Guid.NewGuid().ToString()}
        })
        .AddEnvironmentVariables();
    Configuration = builder.Build();
}

Немного про DI
Он не показался мне хоть сколько-нибудь интуитивным. Глубоко я не копал, но тем не менее приведу ниже небольшой пример того, как прокинуть Id инстанса, который мы задаем при старте, и что-нибудь из переменных окружения(например переменную MyTestParam) в контроллер.
Первым делом надо создать класс настроек — имена полей должны совпадать с именами параметров конфигурации, которые мы хотим инжектить
  public class ValuesControllerSettings
  {
        public string MyTestParam { get; set; }
        public string InstanseId { get; set; }
  }

Далее идем в Startup.cs и вносим изменения в ConfigureServices(IServiceCollection services)
  // This method gets called by the runtime. Use this method to add services to the container.
  public void ConfigureServices(IServiceCollection services)
  {
        //регистрируем экземпляр IСonfiguration
        //откуда будет заполняться наш ValuesControllerSettings
        services.Configure(Configuration); 
             
        // Add framework services.
        services.AddMvc();
  }

И последним шагом идем в наш подопытный и единственный созданный автоматом ValuesController и пишем инжекцию через конструктор
  private readonly ValuesControllerSettings _settings;

  public ValuesController(IOptions settings)
  {
        _settings = settings.Value;
  }

не забывая добавить using Microsoft.Extensions.Options;.
Для теста переопределяем ответ любого понравившегося Get метода, чтобы он возвращал обретенные контроллером параметры, запускаем, проверяем -> профит.


Собираем и запускаем docker-образ
1. Первым делом получим бинарники нашего приложения для публикации. Для этого открываем терминал, переходим в директорию проекта и вызываем
dotnet publish

Более подробно про команду можно почитать тут.
Запуск этой команды без доп. аргументов из директории проекта сложит dll-ки для публикации в ./bin/Debug/[framework]/publish

2. Собственно эту папочку мы и положим в наш docker-образ.
Для этого создадим в директории проекта Dockerfile и напишем туда примерно следующее

# базовый образ для нашего приложения
FROM microsoft/dotnet:runtime

# рабочая директория внутри контейнера для запуска команды CMD
WORKDIR /testapp

# копируем бинарники для публикации нашего приложения(напомню,что dockerfile лежит в корневой папке проекта) в рабочую директорию
COPY /bin/Debug/netcoreapp1.1/publish /testapp

# пробрасываем из контейнера порт 5000, который слушает Kestrel
EXPOSE 5000

# при старте контейнера поднимаем наше приложение
CMD ["dotnet","ИмяВашегоСервиса.dll"]

3. После того, как Dockerfile написан, запускаем
docker build -t my-cool-service:1.0 . 

Где my-cool-service — имя образа, а 1.0 — тэг с указанием версии нашего приложения

4. Теперь проверим, что образ нашего сервиса попал в репозиторий
docker images

5. И, наконец, запустим наш образ
docker run -p 5000:5000 my-cool-service:1.0

6. Открываем http://localhost:5000/api/values и наслаждаемся работой C# кода на Linux в docker

Полезные команды для работы с docker
Посмотреть образы в локальном репозитории
docker images

Посмотреть запущенные контейнеры
docker ps

Запустить контейнер в detached mode
docker run с флагом -d

Получить информацию о контейнере
docker inspect имя_или_ид_контейнера

Остановить контейнер
docker stop имя_или_ид_контейнера

Удалить все контейнеры и все образы
# Delete all containers
docker rm $(docker ps -a -q)
# Delete all images
docker rmi $(docker images -q)



Немного docker-compose напоследок
docker-compose удобен для запуска групп связанных контейнеров. Как пример, приведу разработку нового микросервиса: пусть мы пишем сервис3, который хочет общаться с уже написанными и докеризованными сервисом1 и сервисом2. Сервис1 и сервис2 для целей разработки удобно и быстро поднять из репозитория через docker-compose.
Напишем простенький docker-compose.yml, который будет поднимать контейнер нашего приложения и контейнер с nginx (не знаю, зачем он мог понадобиться нам локально при разработке, но для примера сойдет) и настраивать последний, как reverse proxy для нашего приложения.

# версия синтаксиса docker-compose файла
version: '3.3'
services:
    # сервис нашего приложения
    service1:
        container_name: service1_container
        # имя образа приложения
        image: my-cool-service:1.0
        # переменные окружения, которые хотим передать внутрь контейнера
        environment:
         - MyTestParam=DBForService1

    # nginx
    reverse-proxy:
        container_name: reverse-proxy
        image: nginx
        # маппинг портов для контейнера с nginx 
        ports:
         - "777:80"
        # подкладываем nginx файл конфига 
        volumes:
         - ./test_nginx.conf:/etc/nginx/conf.d/default.conf

docker-compose поднимает при старте между сервисами, описанными в docker-compose файле, локальную сеть и раздает hostname в соотвествии с названиями сервисов. Это позволяет таким сервисам удобно между собой общаться. Воспользуемся этим свойством и напишем простенький файл конфигурации для nginx
upstream myapp1 {
     server service1:5000; /* мы можем обратится к нашему приложению по имени сервиса из docker-compose файла*/
}
server {
    listen 80;
    location / {
        proxy_pass http://myapp1;
    }
}

Вызываем
docker-compose up 

из директории с docker-compose.yml и получаем nginx, как reverse proxy для нашего приложения. При этом рекомендуется представить, что тут вместо nginx что-то действительно вам полезное и нужное. Например база данных при запуске тестов.

Заключение
Мы создали dotnet core приложение на Linux, научились собирать и запускать для него docker-образ, а также немного познакомились с docker-compose.
Надеюсь, что кому-нибудь эта статья поможет сэкономить немного времени на пути освоения docker и/или dotnet core.

Просьба к читателям: если вдруг среди вас у кого-нибудь есть опыт с dotnet core в продакшене на Linux (не обязательно в docker, хотя в docker особенно интересно) — поделитесь, пожалуйста, впечатлениями от использования в комментариях. Особенно будет интересно услышать про реальные проблемы и то, как их решали.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332582/


Метки:  

Тестирование в Openshift: Автоматизированное тестирование

Четверг, 13 Июля 2017 г. 19:48 + в цитатник

Это заключительная часть серии из трех статей, которые посвящены автоматизированному тестированию программных продуктов в Openshift Origin. В данной статье будут рассмотрены аспекты тестирования в контейнерах и особенности выстраивания CI/CD при участии таких продуктов как:


  1. Openshit Origin — как система развертывании тестовых окружений.
  2. Jenkins — как инструмент непрерывной интеграции.
  3. Testlink — как система управления тестами.
  4. Robot Framework — как framework для написания тестов.


    Для лучшей репрезентативности я подготовил образ Vagrant, который содержит содержит преднастроенную среду из вышеперечисленных продуктов (все перечисленные в данной статье объекты и механизмы могут быть легко проинспектированы). Чтобы повысить градус понимания материала я создал две задачи: задачу сборки, задачу тестирования. Обе задачи разбиты на этапы и детально описаны.



Быстрый старт:


  1. Загрузить Vagrant образ и Vagrantfile
  2. vagrant box add --name viewshift viewshift-1.0.box && vagrant up

Описание окружения

Создание полноценного окружения не входило в мои планы, но проиграв несколько сценариев со связыванием minishift c docker контейнерами пришло понимание, что это категорически неудобно и чревато ошибками. Тренировать воображение читателей с помощью одного текста считаю бесполезным занятием.


По умолчанию окружение стартует в графическом режиме. Сделано это для того, чтобы обойти проблему с доступом к продуктам извне. Настроен автоматический вход пользователя. Пользовательский Firefox содержит сохраненные закладки и учетные данные для доступа к продуктам.


Системные пользователи user и vagrant имеют неограниченный sudo доступ.


Задействованное ПО:


Название Версия Учетные данные
Openshift 1.5.1 admin:admin
Jenkins 2.60.1 admin:admin
Testlink 1.9.16 admin:admin
Gogs 0.11.19.0609 git:git
Mariadb 5.5.52 root:root
OpenShift Pipeline Jenkins Plugin 1.0.47 -
TestLink Plugin 3.12 -
Robot Framework plugin 1.6.4 -
Post-Build Script Plug-in 0.17 -
system - root:root
system - user:user
system - vagrant:vagrant

SHA1:


0992d621809446e570be318067b70fe2b8e786b2 viewshift-1.0.box


Задача сборки:


Задача сборки подразумевает под собой сборку образа Docker с приложением "curl", которое в последующем будет участвовать в задаче тестирования.


Примечание: в качестве корневого процесса (PID 1) в контейнере используется supervisord. supervisord и другие похожие инструменты очень полезны в тех случаях, когда нужно завершить работу приложения полностью или управлять процессами удаленно.


Принципиальная схема:



Этапы:


  1. Определяем переменные, которые будут задействованы в задаче:

    PROJECT — название проекта Openshift. Для данного проекта был создан ServiceAccount "jenkins", который обладает правами администратора в проекте. Данный ServiceAccount используется для доступа к проекту из Jenkins (данный аккаунт также используется в задаче тестирования).
    APP_NAME и APP_VERSION — условное название и версия приложения, которые, тем не менее, фигурируют в нескольких местах: название и таг результирующего образа Docker, название запускаемого Build и т.д.


  2. После того, как требуемые переменные были определены (продумана гранулярность/отличимость задач в проекте), требуется разнести их по всем YAML конфигурациям Openshift и другим шагам Jenkins.


  3. На данном этапе создается объект BuildConfig, на базе которого будет создан и выполнен объект Build.


  4. Происходит запуск процесс сборки на основе созданного BuildConfig. В случае успеха результирующий образ будет помещен во внутренний Docker регистр.


  5. Все созданные объекты удаляются вне зависимости от успешности сборки.

Задача тестирования:


Под задачей тестирования подразумевается процесс тестирования приложения "curl", которое взаимодействует с сервисом "nginx" по протоколу HTTP. Мы хотим удостовериться, что приложение работает корректно и проходит заданные тесты.


Принципиальная схема:



Этапы:


  1. Определяем параметры, которые будут задействованы в задаче:

    PROJECT — название проекта Openshift.
    TESTPLAN — название тест-плана в Testlink. Задача завершится ошибкой, если указанный тест-план отсутствует в Testlink.
    APP_NAME и APP_VERSION — условное название и версия приложения, которые аналогичным образом как и в задаче сборки.
    TEST_CMD — переменная, которая содержит название исполняемого файла, который будет запущен внутри контейнера. Аргументы командной строки указаываются в соотвествующем шаге Jenkins.
    TEST_TIMEOUT — численное выражение, которое задает время ожидания выполнения команды внутри контейнера. По истечении данного времени Jenkins задача завершает своё выполнение с ошибкой.


  2. см. задачу сборки.


  3. На данном этапе задается конфигурация Testlink, в которой указывается: с каким сервером будет установлена связь, какой тест-план будет использоваться (из тест-плана загружаются все назначенные данном тест-плану тесты для последующего сравнения), под какой платформой проводилось тестирование и т.д. Всё это требуется для последующей публикации пройденных тестов обратно в Testlink и отображения отчета тестирования непосредственно в Jenkins.


  4. Данный этап предназначен для создания Service. Создаваемые сервисы будут указывать на приложения, которые будут запущены позднее. Через данные сервисы осуществляется проверка доступности приложений.


  5. На данном этапе создается Pod для приложения "nginx".


  6. На данном этапе создается Pod для приложения "curl". Образом для данного контейнера является образ, который создается в процессе задачи сборки. В отличии от "nginx", в данный образ добавлен том данных "share", который позволит контейнеру коммуницировать с файловой системой рабочего узла.


  7. После того, как все Pod созданы, требуется проверка доступности приложений через опубликованные раннее сервисы.


  8. На данном этапе происходит запуск команды тестирования в Pod с последующим ожиданием завершения выполнения данной команды.


  9. После прохождения всех тестов происходит копирование отчета о тестировании в workspace задачи для последующего иморта в Testlink.


  10. На данном этапе указывается стратегия (может быть не одна) сопоставления пройденных тестов с тем, что было получено из указанного раннее тест-плана. В данном случае идет простое сравнение названий тест-кейсов. После всех операции происходит публикация отчета о тестировании в Testlink.


  11. Помимо отчета Teslink в формате Junit присутствует отчет о тестировании в формате Robot Framework, который установит статус выполненной задачи исходя из пороговых значений пройденных тестов.


  12. На данном этапе происходит удаление всех созданных в процессе выполнения задачи объектов Openshift.

Всё вместе:





Преимущества и недостатки тестирования в контейнерах:


Недостатки:


  1. Только Linux. Развивается так называемая "легкая" виртуализация и стоит, наверное, ожидать изменений в ситуациии, но пока только Linux.
  2. Единая версия ядра для всех контейнеров. Возможно в п.1
  3. Только x86_64. Нет, безусловно, ваш образ может быть x86, но ядро будет x86_64. Для кого-то это может стать препятствием.
  4. Отсутствие вложенного SELinux (вложенные CGroups существуют).
  5. Отсутствие полноценного видеоустройства и доступа к экрану. Возможно в п.1

Плюсы:


  1. Скорость работы и гибкость запускаемых окружений, высокая плотность.
  2. Уифицированный способ доставки, переносимости и повторяемости приложений.

Заключение:


Openshift Origin в связке с другими инструментами позволяет добиться впечатляющей гибкости и эффективности. Продуманная схема именования проектов/объектов позволяет избежать возникновения ошибок при массовых запусках задач тестирования.


Благодарность:


Хочу выразить благодарность сотрудникам компании Google за то, что сделали такую замечательную платформу.


Хочу выразить благодарность сотрудникам компании Red Hat, которые сделали из замечательной платформы законченный продукт.


Полезные ссылки:


  1. Minikube — быстрый способ познакомиться с Kubernetes
  2. Minishift — быстрый способ познакомиться с Openshift
  3. v2c — утилита для конвертации виртуальных машин в контейнеры
  4. Почему для встроенного регистра используется IP-адрес вместо FQDN
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333014/


Метки:  

Тестирование в Openshift: Внутреннее устройство кластера

Четверг, 13 Июля 2017 г. 19:36 + в цитатник

Это продолжение серии из трех статей об автоматизированном тестировании программных продуктов в Openshift Origin. В данной статье будут описаны основные объекты Openshift, а также описаны принципы работы кластера. Я осознано не делаю попытку описать все возможные объекты и их особенности, так как это очень трудоемкая задача, которая выходит за рамки данной статьи.


Кластер:



В целом работа кластера Openshift Origin не сильно отличается от других решений. Поступающие задачи распределяются по рабочим узлам на основе их загруженности, данное распределение берет на себя планировщик.


Для запуска контейнеров требуется Docker образа, которые могут быть загружены из внутреннего или внешнего регистра. Непосредственно запуск контейнеров происходит в различных контекстах безопасности (политики безопасности, которые ограничивают доступ контейнера к различным ресурсам).


По умолчанию контейнеры из разных проектов могут коммуницировать друг с другом с помощью overlay сети (выделяется одна большая подсеть, которая разбивается на более мелкие подсети для всех рабочих узлов кластера). Запущенному на рабочем узле контейнеру выделяется IP-адрес из той подсети, которая была назначена данному узлу. Сама overlay сеть построена на базе Open vSwitch, который использует VXLAN для связи между рабочими узлами.


На каждом рабочем узле запускается выделенныё экземпляр Dnsmasq, который перенаправляет все DNS запросы контейнеров на SkyDNS во внутреннюю сервисную подсеть.


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


Стоит отметить что:


  1. SELinux не является строгим условием работы кластера. Отключение оного (не рекомендуется по соображениям безопасности) привнесет некое увелечение скорости (равно как и отключение мониторинга, кстати) при работе с контейнерами. Если SELinux мешает работе приложения в контейнере, присутствует возможность добавления исключения SELinux непосредственно на рабочем узле кластера.


  2. По умолчанию используется LVM в качестве хранилища Docker Engine. Это далеко не самое быстрое решение, но можно использовать любой другой тип хранилища (BTRFS, например).


  3. Стоит иметь ввиду, что название сервиса (см. Service) — это DNS имя, которое влечет за собой ограничения на длину и допустимые символы.


  4. Чтобы сократить временные и аппаратные издержки при сборке Docker образов можно использовать так называемый "слоистый" подход (multi-stage в Docker). В данном подходе используются базовые и промежуточные образа, которые дополняют друг друга. Имеем базовый образ "centos:7" (полностью обновлен), имеем промежуточный образ "centos:7-tools" (установлены иструменты), имеем финальный образ "centos:7-app" (содержит "centos:7" и "centos:7-tools"). То есть вы можете создавать задачи сборки, которые основываются на других образах (см. BuildConfig).


  5. Достаточно гибким решением является подход, когда существует один проект, который занимается только сборкой Docker образов с последующей "линковкой" данных образов в другие проекты (см. ImageStream). Это позволяет не плодить лишних сущностей в каждом проекте и приведет к некой унификации.


  6. Большинству объектов в кластере можно присвоить произвольные метки, с помощью которых можно совершать массовые операции над данными объектами (удаление определенных контейнеров в проекте, например).


  7. Если приложению требуется некий ядерный функционал ядра Linux, то тогда требуется загрузить данный модуль на всех рабочих узлах, где требуется запуск данного приложения.


  8. Стоит сразу побеспокоиться об удалении старых образов и забытых окружений. Если первое решается с помощью сборщика мусора/oadm prune, то второе требует проработки и ознакомлении всех участников с правилами совместной работы в Openshift Origin.


  9. Любой кластер ограничен ресурсами, поэтому очень желательно организовать мониторинг хотя бы на уровне рабочих узлов (возможен мониторинг на уровне приложения в контейнере). Сделать это можно как с помощью готового решения Openshift Metrics, так и с помощью сторонних решений (Sysdig, например). При наличии метрик загруженности кластера (в целом или по проектно) можно организовать гибкую диспетчерезацию поступающих задач.


  10. Особенно хочется отметить тот факт, что рабочие узлы могут быть динамически проинициализированы, а это значит, что вы можете расширить свой кластер Openshift Origin на существующих мощностях IaaS. То есть, во время предрелизного тестирования вы можете существенно расширить свои мощности и сократить время тестирования.


Объекты:


Project — объект является Kubernetes namespace. Верхний уровень абстракции, который содержит другие объекты. Созданные в проекте объекты не пересекаются с объектами в других проектах. На проект могут быть выставлены квоты, привилегии, метки узлов кластера и т.д. Вложенная иерархия и наследование между проектами отсутствуют, доступна "плоская" структура проектов. Существуюет несколько системных проектов (kube-system, openshift, openshift-infra), которые предназначены для нормального функционирования кластера.


Создание нового проекта:


oc adm new-project project1 --node-selector='node_type=minion'

Редактирование настроек проекта:


oc edit namespace project1

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
kind: Namespace
metadata:
  annotations:
    openshift.io/description: ""
    openshift.io/display-name: ""
    openshift.io/node-selector: node_type=minion
...


Pod — объект, который стал одним из решающих факторов, так как позволяет запускать произвольные команды внутри контейнера с помощью специальных хуков (и не только). Pod является основной рабочей единицей в кластере. Любой запущенный в кластере контйенер — Pod. По своей сути — группа из одного и более контейнеров, которые работают в единых для этих контейнеров namespaces (network, ipc, uts, cgroup), используют общее хранилище данных, секреты. Контейнеры, из которых состоит Pod, всегда запущены на одном узле кластера, а не распределены в одинаковых пропорциях по всем узлам (если Pod будет состоять из 10 контейнеров, все 10 будут работать на одном узле).


Pod:


apiVersion: "v1"
kind: "Pod"
metadata:
  name: "nginx-redis"
spec:
  containers:
    -
      name: "nginx"
      image: "nginx:latest"

    -
      name: "redis"
      image: "redis:latest"

Статус Pod:


NAME          READY     STATUS    RESTARTS   AGE
nginx-redis   2/2       Running   0          7s


Secret — может являться строкой или файлом, предназначен для проброса чувствительной (хранится в открытом виде в etcd (поддержка шифрования в Kubernetes 1.7)) информации в Pod. Один Secret может содержать множество значений.


Создание Secret:


oc secrets new gitconfig .gitconfig=/home/user/.gitconfig

Использование Secret в BuildConfig:


apiVersion: "v1"
kind: "BuildConfig"
metadata:
  name: "nginx-bc"
spec:
  source:
    type: "Git"
    git:
      uri: "https://github.com/username/nginx.git"
    sourceSecret:
      name: "gitconfig"

  strategy:
    type: "Docker"
    dockerStrategy:
      dockerfilePath: docker/nginx-custom
      noCache: true

  output:
    to:
      kind: "ImageStreamTag"
      name: "nginx-custom:latest"


ServiceAccount — специальный тип объекта, который предназначен для взаимодействия с ресурсам кластера. По своей сути является системным пользователем.


По умолчанию новый проект содержит три ServiceAccount:


  • builder — отвечает за сборку Docker образов и их выгрузку в регистр (см. BuildConfig).
  • deployer — от этого аккаунта запускаются задачи развертывания (см. DeploymentConfig).
  • default — все остальные Pod (которые не относятся к задачам развертывания) запускаются именно от этого аккаунта.

Перечисленные служебные аккаунты:


  1. Cодержат автоматически созданные секреты, которые используются для доступа к ресурсам кластера.
  2. Обладают ролями, которые позволяют им осуществлять те или иные действия в кластере.

ServiceAccount:


apiVersion: "v1"
kind: "ServiceAccount"
metadata:
  name: "jenkins"

Свойства ServiceAccount:


Name:           jenkins
Namespace:      project1
Labels:         

Image pull secrets:     jenkins-dockercfg-pvgsr

Mountable secrets:      jenkins-dockercfg-pvgsr
                        jenkins-token-p8bwz

Tokens:                 jenkins-token-p8bwz
                        jenkins-token-zsn9p

Добавление прав администратора проекта ServiceAccount:


oc policy add-role-to-user admin system:serviceaccount:project1:jenkins


DeploymentConfig — это объект, который оперирует всё теми же Pod, но при этом привносит ряд дополнительных механизмов для управления жизненным циклом запущенных приложений, а именно:


  1. Добавляет стратегию развертывания, т.е. позволяет определить каким образом будет: обновлено приложение при выходе новой версии, произведен откат к работающей версии в случае неудачи.
  2. Позволяет установить триггеры, которые вызовут повторное развертывание конфигурации.
  3. Позволяет указать количество экземпляров/реплик приложения.

DeploymentConfig:


apiVersion: "v1"
kind: "DeploymentConfig"
metadata:
  name: "nginx-dc"
spec:
  template:
    metadata:
      labels:
        name: "nginx-dc"
    spec:
      containers:
        -
          name: "nginx"
          image: "nginx:latest"

  replicas: 3

  selector:
    name: "nginx-dc"

Статус DeploymentConfig:


NAME               READY     STATUS    RESTARTS   AGE
nginx-dc-1-1wl8m   1/1       Running   0          7s
nginx-dc-1-k3mss   1/1       Running   0          7s
nginx-dc-1-t8qf3   1/1       Running   0          7s


ImageStream — по своей сути является "контейнером" для "ссылок" (ImageStreamTag), которые указывают на Docker образа или другие ImageStream.


ImageStream:


apiVersion: "v1"
kind: "ImageStream"
metadata:
  name: "third-party"

Создание тага/ссылки на Docker образ между проектами:


oc tag project2/app:v1 project1/third-party:app

Создание тага/ссылки на Docker образ, который расположен на Docker Hub:


oc tag --source=docker nginx:latest project1/third-party:nginx


BuildConfig — объект является сценарием того, как будет собран Docker образ и куда он будет помещен. Сборка нового образа может базироваться на других образах, за это отвечает секция "from:"


Источники сборки (то место, где размещены исходные данные для сборки):


  • Binary
  • Dockerfile
  • Git
  • Image
  • Input Secrets
  • External artifcats

Стратегии сборки (каким образом следует интерпретировать источник данных):


  • Custom
  • Docker
  • Pipeline
  • S2I

Назначение сборки (куда будет выгружен собранный образ):


  • DockerImage
  • ImageStreamTag

BuildConfig:


apiVersion: "v1"
kind: "BuildConfig"
metadata:
  name: "nginx-bc"
spec:
  source:
    type: "Git"
    git:
      uri: "https://github.com/username/nginx.git"

  strategy:
    type: "Docker"
    dockerStrategy:
      from:
        kind: "ImageStreamTag"
        name: "nginx:latest"

      dockerfilePath: docker/nginx-custom
      noCache: true

  output:
    to:
      kind: "ImageStreamTag"
      name: "nginx-custom:latest"

Какие операции выполнит данный BuildConfig:


  1. Возьмет за основу ImageStream "nginx:latest"
  2. Склонирует Git репозиторий, найдет в данном репозитории файл docker/nginx-custom, загрузит из данного файла Dockerfile инструкции, выполнит данные инструкции над базовым образом.
  3. Результирующий образ поместит в ImageStream "nginx-custom:latest"


Service — объект, который стал одним из решающих факторов при выборе системы запуска сред, так как он позволяет гибко настраивать коммуникации между средами (что очень важно в тестировании). В случаях с использованием других систем требовались подготовительные манипуляции: выделить диапазоны IP-адресов, зарегистрировать DNS имена, осуществить проброс портов и т.д. и т.п. Service может быть объявлен до фактического развертывания приложения.


Что происходит во время публикации сервиса в проекте:


  1. Для сервиса выделяется IP-адрес из специальной сервисной подсети.
  2. Регистрируется DNS имя данного сервиса. Все Pod в проекте, которые были запущены до/после публикации сервиса, смогут разрешать данное DNS имя.
  3. Все Pod в проекте, которые будут запущены после публикации сервиса, получат список переменных окружения, которые содержат IP-адрес и порты опубликованного сервиса.

Service:


apiVersion: v1
kind: Service
metadata:
  name: "nginx-svc"
spec:
  selector:
    name: "nginx-pod"
  ports:
    - port: 80
      targetPort: 80
      name: "http"

    - port: 443
      targetPort: 443
      name: "https"

Разрешение DNS имени:


root@nginx-pod:/# ping nginx-svc              
PING nginx-svc.myproject.svc.cluster.local (172.30.217.250) 56(84) bytes of data.

Переменные окружения:


root@nginx-pod:/# env | grep -i nginx
NGINX_SVC_PORT_443_TCP_ADDR=172.30.217.250
HOSTNAME=nginx-pod
NGINX_VERSION=1.13.1-1~stretch
NGINX_SVC_PORT_80_TCP_PORT=80
NGINX_SVC_PORT_80_TCP_ADDR=172.30.217.250
NGINX_SVC_SERVICE_PORT=80
NGINX_SVC_PORT_80_TCP_PROTO=tcp
NGINX_SVC_PORT_443_TCP=tcp://172.30.217.250:443
NGINX_SVC_SERVICE_HOST=172.30.217.250
NGINX_SVC_PORT_443_TCP_PROTO=tcp
NGINX_SVC_SERVICE_PORT_HTTPS=443
NGINX_SVC_PORT_443_TCP_PORT=443
NGINX_SVC_PORT=tcp://172.30.217.250:80
NGINX_SVC_SERVICE_PORT_HTTP=80
NGINX_SVC_PORT_80_TCP=tcp://172.30.217.250:80

Заключение:


Все объекты кластера можно описать с помощью YAML, это, в свою очередь, дает возможность полностью автоматизировать любые процессы, которые протекают в Openshift Origin. Вся сложность в работе с кластером заключается в знании приципов работы и механизмов взаимодействия объектов. Такие рутинные операции как инициализация новых рабочих узлов берут на себя сценарии Ansible. Доступность API открывает возможность работать с кластером напрямую минуя посредников.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333012/


Метки:  

Интеграция 3D-мыши в Renga

Четверг, 13 Июля 2017 г. 18:17 + в цитатник
Всем привет! Я работаю в компании Renga Software (совместное предприятие АСКОН и фирмы «1С»), которая занимается разработкой BIM-систем: Renga Architecture — для архитектурно-строительного проектирования и Renga Structure — для проектирования конструктивной части зданий и сооружений.

В этой статье хочу поделиться опытом интеграции 3D-мыши в систему Renga.
image

Подробнее о семействе продуктов Renga (Осторожно маркетинг!)
Renga Architecture – система для архитектурно-строительного проектирования. Программа создана для максимальной помощи проектировщику в решении его задач: создание архитектурного облика здания, информационной модели и быстрая компоновка чертежей согласно стандартам СПДС и многое другое.

Renga Structure — cистема для проектирования конструктивной части зданий/сооружений. Программа для инженеров-конструкторов и проектировщиков по созданию информационной модели здания или сооружения и получению чертежей марок КР/КЖ/КЖИ/КМ/АС.

Семейство продуктов Renga предназначено для проектирования по технологии BIM. Высокая производительность систем позволяет работать с большими проектами без видимого снижения качества работы с 3D-моделью:

Объектное проектирование
Создание в Renga 3D-модели здания/сооружения инструментами объектного проектирования (стена, колонна, окно и т.д.)

Коллективная работа
Обмен хранение и управление данными осуществляется с помощью BIM-Server Pilot
Взаимодействие со сметными системами
Интеграция Renga по средством API со сметными системами 1С-смета и ABC-смета для взаимодействия проектного и сметного отделов.

Обмен данными
Renga позволяет обмениваться данными с другими системами через различные форматы (.ifc, .dwg, .dxf, .obj, .dae, .stl, .3ds, .lwo и .csv)

Автоматизация получения спецификаций и ведомостей
В Renga реализована функция получения отчетов для формирования спецификаций, ведомостей и экспликаций.

Автоматизация получения чертежей
По данным 3D-модели автоматически получаются виды (фасады, разрезы и планы) и размещаются на чертежах в заданных масштабах.


3D-манипуляторы — это средства взаимодействия пользователя с программным обеспечением, которые обеспечивают интуитивную навигацию в трехмерном пространстве и возможность работать двумя руками одновременно. Инженеры-проектировщики и компании, которые внедрили 3D-манипуляторы, сообщают о внушительном приросте производительности.

Речь в статье пойдет о 3D-мыши от компании 3DСonnexion. Вот так она выглядит (картинка из интернетов):

image

У 3D-мышки 6 степеней свободы: смещение по осям X, Y, Z, а также поворот вокруг осей, соответственно: Roll, Pitch, Yaw.

image

Степени свободы мышки:

image

Для интеграции 3D-мыши в ваше приложение компания 3DСonnexion предоставляет SDK. Его можно скачать с сайта производителя после регистрации.

Продемонстрирую способ интеграции 3D-мыши в приложение на основе Qt5.
Создадим простое Qt приложение с помощью мастера новых проектов в Visual Studio.

Для работы с 3D-мышью нужно включить несколько заголовочных файлов из SDK:

// Mouse 3D stuff
#include   /* Common macros used by SpaceWare functions. */
#include              /* Required for any SpaceWare support within an app.*/
#include        /* Required for siapp.lib symbols */
#include "virtualkeys.hpp"

Для того чтобы мышка заработала, ей нужно передать handle окна, куда мышка будет присылать сообщения. Напишем код инициализации мышки. Он будет выглядеть как-то так:

bool init3DMouse()
{
  SiOpenData oData; 

  /*init the SpaceWare input library */
  if (SiInitialize() == SPW_DLL_LOAD_ERROR)  
      return false;
  
  SiOpenWinInit(&oData, (HWND)winId());    /* init Win. platform specific data  */
  SiSetUiMode(mouse3DHandle, SI_UI_ALL_CONTROLS); /* Config SoftButton Win Display */

  /* open data, which will check for device type and return the device handle
  to be used by this function */ 
  if ( (mouse3DHandle = SiOpen ("HabrahabrAnd3DMouse", SI_ANY_DEVICE, SI_NO_MASK, SI_EVENT, &oData)) == NULL) 
  {
    SiTerminate();  /* called to shut down the SpaceWare input library */
    return false;       /* could not open device */
  }
  else
  {
    return true; /* opened device succesfully */ 
  }  
}

Теперь мышка подключена к нашему приложению и будет присылать в message loop нашего окна сообщения. Сообщение от мышки имеет следующую структуру:

typedef struct          /* 3DxWare event */
{
  int type;                 /* Event type */
  union
  {
    SiSpwData spwData;            /* Button, motion, or combo data        */
    SiSpwOOB spwOOB;              /* Out of band message                  */
    SiOrientation spwOrientation; /* Which hand orientation is the device */
    char exData[SI_MAXBUF];       /* Exception data. Driver use only      */
    SiKeyboardData spwKeyData;    /* String for keyboard data             */
    SiSyncPacket siSyncPacket;    /* GUI SyncPacket sent to applications  */
    SiHWButtonData hwButtonEvent; /* V3DKey that goes with          *
                                   * SI_BUTTON_PRESS/RELEASE_EVENT        */
    SiAppCommandData appCommandData;  /* Application command event function data that *
                                   * goes with an SI_APP_EVENT event      */
    SiDeviceChangeEventData deviceChangeEventData;    /* Data for connecting/disconnecting devices */
    SiCmdEventData cmdEventData;  /* V3DCMD_* function data that *
                                   * goes with an SI_CMD_EVENT event      */
  } u;
} SiSpwEvent;

Нас интересует тип события — SiSpwEvent::type. И SiSpwData::spwData — там находится информация о нажатых кнопках, перемещении и вращении по осям.

Наша задача отфильтровать сообщения от мышки. Для этого установим наш класс в качестве фильтра событий. Отнаследуемся от QAbstractNativeEventFilter и переопределим функцию nativeEventFilter:

bool HabrahabrAnd3DMouse::nativeEventFilter(const QByteArray &eventType, void *msg, long *)
{
  if(!mouse3DHandle)
    return false;

  MSG* winMSG = (MSG*)msg;

  bool handled = SPW_FALSE;
  SiSpwEvent     Event;    /* SpaceWare Event */ 
  SiGetEventData EData;    /* SpaceWare Event Data */

  /* init Window platform specific data for a call to SiGetEvent */
  SiGetEventWinInit(&EData, winMSG->message, winMSG->wParam, winMSG->lParam);

  /* check whether msg was a 3D mouse event and process it */
  if (SiGetEvent (mouse3DHandle, SI_AVERAGE_EVENTS, &EData, &Event) == SI_IS_EVENT)
  {
    if (Event.type == SI_MOTION_EVENT)
    {
      qDebug() << "delta by X coordinate = " << Event.u.spwData.mData[SI_TX] << "\n";
      qDebug() << "delta by Y coordinate = " << Event.u.spwData.mData[SI_TY] << "\n";
      qDebug() << "delta by Z coordinate = " << Event.u.spwData.mData[SI_TZ] << "\n";

      qDebug() << "delta by Yaw = " << Event.u.spwData.mData[SI_RX] << "\n";
      qDebug() << "delta by Pitch = " << Event.u.spwData.mData[SI_RY] << "\n";
      qDebug() << "delta by Roll = " << Event.u.spwData.mData[SI_RZ] << "\n";
    }
    else if (Event.type == SI_ZERO_EVENT)
    {
      // ZERO event
    }
    else if (Event.type == SI_BUTTON_EVENT)
    {
      // misc button events
    }

    handled = SPW_TRUE;  /* 3D mouse event handled */ 
  }

  return handled;
}

На этом подключение мышки к нашему приложению завершено. Ссылка на полный код примера.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332676/


«Доктор Веб»: портал gosuslugi.ru скомпрометирован и может начать заражать посетителей или красть информацию

Четверг, 13 Июля 2017 г. 18:12 + в цитатник

На портале государственных услуг Российской Федерации (gosuslugi.ru) специалисты компании «Доктор Веб» обнаружили внедрённый неизвестными потенциально вредоносный код. В связи с отсутствием реакции со стороны администрации сайта gosuslugi.ru мы вынуждены прибегнуть к публичному информированию об угрозе.


Дату начала компрометации, а также прошлую активность по этому вектору атаки, установить на данный момент не представляется возможным. Вредоносный код заставляет браузер любого посетителя сайта незаметно связываться с одним из не менее 15 доменных адресов, зарегистрированных на неизвестное частное лицо. В ответ с этих доменов может поступить любой независимый документ, начиная от фальшивой формы ввода данных кредитной карточки и заканчивая перебором набора уязвимостей с целью получить доступ к компьютеру посетителя сайта.



В процессе динамического генерирования страницы сайта, к которой обращается пользователь, в код сайта добавляется контейнер , позволяющий загрузить или запросить любые сторонние данные у браузера пользователя. На текущий момент специалистами обнаружено не менее 15 доменов, среди которых: m3oxem1nip48.ru, m81jmqmn.ru и другие адреса намеренно неинформативных наименований. Как минимум для 5 из них диапазон адресов принадлежит компаниям, зарегистрированным в Нидерландах. За последние сутки запросы к этим доменам либо не завершаются успехом, так как сертификат безопасности большинства этих сайтов просрочен, либо не содержит вредоносного кода, однако ничего не мешает владельцам доменов в любой момент обновить сертификаты и разместить на этих доменах вредоносный программный код.


На данный момент сайт gosuslugi.ru по-прежнему скомпрометирован, информация передана в техническую поддержку сайта, но подтверждения принятия необходимых мер по предотвращению инцидентов в будущем и расследования в прошлом не получено. «Доктор Веб» рекомендует проявлять осторожность при использовании портала государственных услуг Российской Федерации до разрешения ситуации. ООО «Доктор Веб» рекомендует администрации сайта gosuslugi.ru и компетентным органам осуществить проверку безопасности сайта.


Любой пользователь может проверить наличие кода самостоятельно, использовав поисковый сервис и задав запрос о поиске следующей формулировки:


site:gosuslugi.ru "A1996667054"

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333008/


Метки:  

Авторизация OAuth для Xamarin-приложений

Четверг, 13 Июля 2017 г. 17:57 + в цитатник
Итак, сегодня мы продолжаем разбираться с различными механизмами авторизации пользователей в приложениях на Xamarin. После знакомства с SDK от Facebook и ВКонтакте (здесь и здесь), можем перейти к одному из самых популярных (на текущий момент) механизмов внешней авторизации пользователей — OAuth. Большинство популярных сервисов вроде Twitter, Microsoft Live, Github и так далее, предоставляют своим пользователям возможность входа в сторонние приложения с помощью одного привычного аккаунта. Научившись работать с OAuth вы легко сможете подключать все эти сервисы и забирать из них информацию о пользователе.




Предполагается, что вы уже знакомы с тем, как работает OAuth, а если нет — рекомендуем вот эту хорошую статью на Хабре. Если коротко, то при авторизации OAuth пользователь перенаправляется с одной веб-страницы на другую (обычно 2-3 шага) до тех пор, пока не перейдет на конечный URL. Этот финальный переход и будет отловлен в приложении (если писать логику самому) на уровне WebView, а нужные данные (token и срок его валидности) будут указаны прямо в URL.


Небольшой список популярных сервисов, которые предоставляют возможность авторизации пользователей по OAuth: Одноклассники, Mail.ru, Dropbox, Foursquare, GitHub, Instagram, LinkedIn, Microsoft, Slack, SoundCloud, Visual Studio Online, Trello.


Xamarin.Auth


Для того, чтобы работать с OAuth в Xamarin мы остановимся на простой и удобной библиотеке Xamarin.Auth, которая развивается уже не первый год и имеет все необходимые для нас механизмы:


  1. Отображение браузера со страницами авторизации
  2. Управление потоком редиректов и процессом авторизации
  3. Получение нужных данных
  4. Предоставление механизмов для дополнительных запросов к сервису, например для получения информации о пользователе

Также Xamarin.Auth поддерживает возможность хранения учетных данных пользователя в защищенном хранилище. В общем, зрелый и качественный компонент с необходимой функциональностью.


Рекомендуем устанавливать Xamarin.Auth из Nuget, так как версия в Xamarin Components уже давно устарела и не обновляется.




Напомню, что мы уже ранее рассказывали про авторизацию с помощью SDK от Facebook и ВКонтакте. В нашем примере мы вынесли всю логику авторизации в платформенные проекты, оставив в PCL только интерфейсы. Для OAuth мы пойдем тем же путем, несмотря на поддержку PCL в самом Xamarin.Auth.


Помимо Xamarin.Auth можем также порекомендовать библиотеку Xamarin.Forms.OAuth от Bruno Bernardo. Даже если вы используете классический Xamarin, в исходных кодах этого проекта можно найти множество готовых конфигураций для различных сервисов.


Мы же в качестве примера работы OAuth подключим авторизацию с помощью Microsoft. Первым делом создадим приложение на сайте https://apps.dev.microsoft.com и получим там Client ID (ИД клиента или приложения).



Подключаем авторизацию в PCL


На уровне PCL все как обычно — делаем простой интерфейс IOAuthService для платформенного сервиса, никаких новых зависимостей в проект не добавляем.


public interface IOAuthService
{
   Task Login();
   void Logout();
}

Ну и, конечно же, будет необходимо добавить обращение к методам DependencyService.Get().Login() и DependencyService.Get().Logout() внутри нашей страницы авторизации.


Также нет проблем добавить поддержку нескольких OAuth-сервисов. Для этого можно добавить в методы Login() и Logout() аргумент providerName (тип string, int или enum) и в зависимости от его значения выбирать поставщика услуг.


Реализация платформенной части


Как уже отмечалось ранее, необходимо добавить библиотеки Xamarin.Auth из Nuget в каждый платформенный проект, в нашем случае — iOS и Android. Дальше пишем нашу реализацию IOAuthService для каждой платформы и регистрируем ее в качестве Dependency.


Теперь нам достаточно создать экземпляр класса OAuth2Authenticator с нужными параметрами:


         var auth = new OAuth2Authenticator
           (
               clientId: "ВАШ_CLIENT_ID",
               scope: "wl.basic, wl.emails, wl.photos",
               authorizeUrl: new Uri("https://login.live.com/oauth20_authorize.srf"),
               redirectUrl: new Uri("https://login.live.com/oauth20_desktop.srf"),
               clientSecret: null,
               accessTokenUrl: new Uri("https://login.live.com/oauth20_token.srf")
           )
           {
               AllowCancel = true
           };

Теперь повесим обработчик завершения авторизации:


auth.Completed += AuthOnCompleted;

Всё, можно показать модальное окно со встроенным веб-браузером для авторизации, получаемое через метод auth.GetUI(). На iOS это можно сделать примерно так:


UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(auth.GetUI(), true, null);

На Android при использовании Xamarin.Forms код может получится следующим:


Forms.Context.StartActivity(auth.GetUI(Forms.Context));

После успешной авторизации вызовется наш метод AuthOnCompleted(), и для iOS будет необходимо скрыть модальное окно с браузером (на Android само скроется):


UIApplication.SharedApplication.KeyWindow.RootViewController.DismissViewController(true, null);

Теперь можно получать нужные данные (access_token и время его жизни в секундах — expires_in)


var token = authCompletedArgs.Account.Properties["access_token"];                
var expireIn = Convert.ToInt32(authCompletedArgs.Account.Properties["expires_in"]);
var expireAt = DateTimeOffset.Now.AddSeconds(expireIn);

И нам остался последний шаг — получить расширенную информацию из профиля пользователя, включая email и ссылку на аватарку. Для этого в Xamarin.Auth есть специальный класс OAuth2Request с помощью которого удобно делать подобные запросы.


             var request = new OAuth2Request("GET", new Uri("https://apis.live.net/v5.0/me"), null, account);
var response = await request.GetResponseAsync();

Теперь нам приходит JSON с данными пользователя, и мы можем их сохранить и отобразить в приложении.


             if (response.StatusCode == HttpStatusCode.OK)
{
    var userJson = response.GetResponseText();
    var jobject = JObject.Parse(userJson);
    result.LoginState = LoginState.Success;
    result.Email = jobject["emails"]?["preferred"].ToString();
    result.FirstName = jobject["first_name"]?.ToString();
    result.LastName = jobject["last_name"]?.ToString();
    result.ImageUrl = jobject["picture"]?["data"]?["url"]?.ToString();
    var userId = jobject["id"]?.ToString();
    result.UserId = userId;
    result.ImageUrl = $"https://apis.live.net/v5.0/{userId}/picture";
}

Как видим, ничего сложного нет. Вопрос в том, чтобы правильно прописать URL для процесса авторизации. Ну и помнить, что поле expires_in содержит время в секундах (это вызывает частые вопросы).




В реальных проектах также рекомендуем назначить обработчик ошибок на событие auth.Error, чтобы ни одна проблема не осталась без решения.


Заключение


Сегодня мы завершили рассмотрение всех популярных способов авторизации пользователей и получения базовой информации о них через внешние сервисы. Описанные механизмы подходят как для Xamarin.Forms, так и для классического Xamarin iOS/Android. Полные исходные коды проекта со всеми примерами можно найти в нашем репозитории:


https://bitbucket.org/binwell/login


Задавайте ваши вопросы в комментариях к статье и оставайтесь на связи!



Об авторе


Вячеслав Черников — руководитель отдела разработки компании Binwell, Microsoft MVP и Xamarin Certified Developer. В прошлом — один из Nokia Champion и Qt Certified Specialist, в настоящее время — специалист по платформам Xamarin и Azure. В сферу mobile пришел в 2005 году, с 2008 года занимается разработкой мобильных приложений: начинал с Symbian, Maemo, Meego, Windows Mobile, потом перешел на iOS, Android и Windows Phone. Статьи Вячеслава вы также можете прочитать в блоге на Medium.

Другие статьи автора:

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332970/


[Из песочницы] Noty.js V3 — шикарная javascript библиотека для создания уведомлений. А также готовый плагин для vuejs

Четверг, 13 Июля 2017 г. 17:24 + в цитатник


Простая, быстрая и производительная библиотека для создания none-block уведомлений для вашего сайта или приложения.

Пару слов о авторе


Nedim ARABACI Co-founder, CEO WHODIDTHIS.IO
Github: @needim

О Noty.js V3


Привет Мир! Прошло много времени с момента написания первой и единственной статьи на Хабре о notyjs. С 2012 года библиотека расширила свои возможности.

Сегодня она не зависит от jQuery, переписана на нативном JS.

import Noty from 'noty';
new Noty({
    text: 'Some notification text',
}).show();

Поддержка из коробки css библиотек animate.css, mojs, bounce.js, velocity и других.

new Noty({
    text: 'Some notification text',
    animation: {
        open : 'animated fadeInRight',
        close: 'animated fadeOutRight'
    }
}).show();

Добавлен дизайн уведомлений.

В версии 3.1 BETA появилась возможность создания Web Push уведомлений и многое другое.

По прежнему имеет 11 мест размещения уведомлений и 5 типов самих уведомлений.

Собственный опыт


Для своего проекта я искал наиболее простую и интересную реализацию из «коробки». Первое на что наткнулся, это модуль vue-notifications + mini-toastr. Сколько я его не крутил, не пришелся он мне по душе. Уже после встретил noty.js. Прикрутил к проекту и надо отдать должное, это была любовь с первого взгляда.

На проекте был готов дизайн, приятная анимация, но чего-то не хватало. Лишь когда уведомления начали работать на noty, я понял, какую изюминку искал. Не мог я себе представить, что «какие то там» уведомления создадут теплое и отзывчивое восприятие дизайна.



p.s. Это первая библиотека, дизайн которой я не перестраивал под себя.

Vue.js плагин для noty.js


Кастомизация уведомлений очень простая. А для пользователей vuejs, предлагаю готовую обертку этой библиотеки vue-notice с глобальным доступом из компонентов:

// минимальным опцией для запуска уведомления является текст
this.$notice.info("New version of the app is available!")

// также, легко переопределить стандартные настройки
// они основаны на опциях плагина
this.$notice.info("Hey! Something very important here...", {
  timeout: 6000,
  layout: 'topLeft'
})

Развернутая документация к плагину, в описании репозитория.

Уверен, что для тех, кто еще с ней не знаком она во многом придется по вкусу.

Спасибо за внимание.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333006/


Метки:  

[recovery mode] 7 способов улучшить дизайн сайта

Четверг, 13 Июля 2017 г. 16:58 + в цитатник
Алёна Лазарева, редактор-фрилансер, специально для блога Нетологии рассказала о семи возможных способах улучшить веб-дизайн.

Ученые Стэнфордского университета провели опрос, который показал, что 46% респондентов составляют мнение о сайте на основании его внешнего вида и интерфейса. Если дизайн не нравится пользователю, то и весь контент кажется ненадежным и не заслуживающим доверия. В этой статье хочу поговорить о том, какие есть способы для того, чтобы улучшить дизайн сайта.

Иллюстрации


Правильно подобранные иллюстрации делают сайт живым. Их главная задача — не просто заполнить пустое место, а вызвать эмоции и связать их с брендом. Авторские иллюстрации помогут сделать сайт узнаваемым. Их можно использовать в заголовках, иконках и при создании анимации.

Иллюстрации помогут сделать дружелюбным даже самый минималистичный дизайн.

При наполнении сайта иллюстрациями важно помнить о единообразии и не переусердствовать. Самые красивые иллюстрации вызовут отторжение, если их будет слишком много.



Dropbox использует простые в исполнении иллюстрации, которые усиливают впечатление от текста.

Типографика


Типографика играет большую роль в формировании идентичности бренда. От нее зависит, как пользователи будут воспринимать текст.

У каждого шрифта свой характер. Постарайтесь выбрать тот шрифт, который передаст настроение текста за счет графического образа.

Условно шрифты можно разделить на четыре категории:

Антиквенные шрифты (Serif) — это шрифты с засечками. Раньше они использовались для набора книг и периодики, но с появлением экранов с высоким разрешением, антиквенные шрифты пришли в веб-дизайн. Они легко читаются, поэтому подходит для набора большого объема текста.



Гротескные шрифты (Sans Serif) — шрифты без засечек. Хорошо подходят для текстов небольшого объёма, заголовков и узких колонок.



Декоративные шрифты — шрифты, которые имитируют исторический стиль или декоративную обработку. Подходят для набора небольшого объёма текста, который требует сложного художественного оформления.



Рукописные шрифты — шрифты, имитирующие почерк или каллиграфический стиль. Используются так же, как и декоративные.



Google Fonts — известная библиотека бесплатных шрифтов, где можно легко подобрать подходящий шрифт и импортировать его на сайт. Главное не забыть настроить кегль, начертание и междустрочный интервал.



Для оформления своего сайта студия дизайна Perky Bros использовала два шрифта: Brown и Minion. Гротескный шрифт Brown использован в оформлении панели навигации, а антиквенный Minion для набора текста о компании.



Другое дизайнерское бюро Your Majesty тоже использует два шрифта: гротескный Plain для оформления меню и декоративный Canela для выделения информации о главных партнерах компании.

Не бойтесь комбинировать шрифты с засечками и без. Главное, чтобы они легко считывались и сочетались между собой.

Фотографии


Большинству людей легче запомнить картинку, чем прочитанный или услышанный текст. Поэтому к выбору фотографий для сайта стоит подходить особенно тщательно.

Согласно исследованиям, одно изображение может заменить 84 слова.

Правильная фотография для сайта должна быть:
  • информативной;
  • качественной;
  • оригинальной;
  • эмоциональной.

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

Идеальный вариант — отказаться от стоковых фотографий, особенно с бесплатных фотостоков. Если пользователь увидит фотографию с сайта на другом ресурсе, это может подорвать доверие к вашему контенту и бренду в целом.

У Почты России есть два официальных представителя в социальных сетях — Дмитрий Маркин и Татьяна Кузнецова. Пользователи выяснили, что фотографии обоих были приобретены на фотостоках и усомнились в реальности сотрудников. История быстро разошлась по сети, ударив по репутации Почты и её «официальных лиц».





Анимация


Анимация на сайте — эффектный, но далеко не новый прием. Сначала анимацию создавали при помощи GIF, затем — Flash. Оба решения сильно замедляли работу сайта. Сейчас для создания анимации используют CSS-кодирование и JavaScript, которые не так заметно влияют на производительность.

Анимация может быть полноэкранной или интегрированной в навигацию. Её главная задача — помочь пользователю разобраться со сложными моментами и привлечь его внимание к важным деталям.



Студия веб-разработки HTMLBURGER использует «бургерное» меню в прямом смысле этого слова. Элементы навигации оживают при наведении курсора. Это не только привлекает внимание, но и дает понять, какие элементы кликабельны.

Видео


Как и в случае с анимацией, движущееся изображение на странице мгновенно захватывает внимание пользователей. Главные преимущества видео — динамика, развитие сюжета и больший эмоциональный отклик.

Видео на сайте подходит для следующих целей:
  • показать то, о чем сложно рассказать (обучающие ролики);
  • рассказать историю;
  • продемонстрировать продукт.

Фоновых видео только для красоты лучше не использовать: они уменьшают производительность сайта и при этом не доносят никакой информации до пользователя. Если же удивить пользователей все равно хочется, советую присмотреться к альтернативам видео: анимации, параллаксу и синемаграфии.

Синемаграф — это фотография, один элемент которой совершает незначительное и бесконечно повторяющееся движение, а остальное изображение остается неподвижным.


Источник — cinemagraphs.com

При размещении на сайте видео, которое воспроизводится автоматически, хорошим тоном будет отключить автовоспроизведение звука. Пользователям нравиться самим решать, нужен ли звук — и лучше предоставить возможность сделать это легко. Внезапный громкий звук пугает не только пользователя, но и всех окружающих.



Вместо тысячи слов — яркое, динамичное видео, которое с первых секунд погружает в мир игры Monument Valley.

Цвета


С каждым новым поколением мониторы и дисплеи устройств становятся лучше. Поэтому концепция «безопасной веб-палитры» устарела. Выбирая цветовое оформление сайта, стоит помнить о:
  • целевой аудитории;
  • корпоративном стиле;
  • контрастной типографике;
  • цветовой гармонии.
Чтобы выбрать цветовое решение, определите целевую аудиторию и сообщение, которое хотите до нее донести.

Яркие цвета


Использование яркой цветовой палитры при оформлении веб-сайта — популярный тренд последних лет. Это универсальное решение для сайтов, рассчитанных на широкую аудиторию.

Одни дизайнеры подбирают цветовую схему на глаз, другие используют генераторы цветовой палитры. Одним из лучших на данный момент считается Adobe Color CC. С его помощью можно создавать свои цветовые схемы, а также вдохновляться работами других дизайнеров.

Любителям материального дизайна нравятся таже инструменты вроде MaterialPallete или Material Design Palette Generator.

Правда, важно помнить, что генераторы помогают дизайнеру, но не заменяют чувство вкуса. Эффектная палитра, которую выдал генератор, может оказаться неудачными решением на этапе реализации.



Сервис, который подпишет и отправит за вас открытку вашим друзьям. Основная целевая аудитория сервиса — женщины, но яркую цветовую схему и милые иллюстрации оценят не только они.

Монохром


Монохромная палитра — сочетание одного основного цвета и теней, тонов и полутонов этого оттенка — позволяет сайту выглядеть стильно и лаконично. Такое решение отлично подходит для создания плоского или минималистичного дизайна.

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



Сдержанный монохромный сайт дизайн-студии из Канады.

Ретро


Как только все забывают про стиль ретро в веб-дизайне, он снова возвращается. Дизайнеры любят его за сдержанную цветовую палитру и возможность вызвать ностальгию у пользователей.



Для оформления своего сайта диджитал-агентство использует сепию и знак, которым раньше обозначали придорожные мотели и рестораны на трассах Америки.

VR-эффекты


Развитие технологий виртуальной реальности — одна из самых обсуждаемых тем этого года. Пока технологии еще не позволяют полноценно использовать VR в веб-дизайне, все существующие попытки находятся на стадии прототипов. Не до конца понятно, сможет ли VR сделать сайты удобнее или просто останется способом привлечь внимание.

Но существуют приемы, которые можно назвать имитацией виртуальной реальности. Их главная цель — позволить пользователю почувствовать себя участником событий.

Параллакс-эффект


Параллакс-эффект создается при скроллинге, когда фон и объект на нем прокручиваются с разной скоростью. Он позволяет создать у пользователя иллюзию трехмерности. Прием можно применять ко всей странице или отдельным блокам, чтобы привлечь внимание к определенной части контента.



Герои проекта о помощи бездомным оживают при прокручивании страницы и рассказывают историю своей жизни.

360°-обзор


360°-обзор создают из множества фотографий одного предмета под разными углами. Они склеены таким образом, что пользователь может повернуть его на 360° и рассмотреть со всех сторон.

Этот прием выполняет ту же функцию, что и видео: он демонстрирует товар. Однако главное отличие от видео — возможность контроля.



При просмотре товара покупатель сам контролирует время и скорость обзора. Это делает его участником процесса, а не пассивным наблюдателем.

Итак, советы, которые помогут улучшить дизайн сайта


  1. Использование авторских иллюстраций.
  2. Внимание на типографику. У каждого шрифта есть характер — подбирайте тот, что подойдет вашему тексту.
  3. Отказ от стоковых фотографий: их использование подрывает доверие к бренду.
  4. Использование анимации для выделения важных блоков.
  5. Определение цели видео на сайте: рассказать историю, объяснить сложное, продемонстрировать продукт.
  6. Выбор цветовой схемы, которая поддержит идеологию бренда.
  7. Превращение пользователя в участника событий.

Какой бы прием вы ни выбрали, помните о здравом смысле и доверяйте собственному опыту. Каждый прием должен быть оправдан определенной целью. Идеальных решений для всех не бывает, но те, что подойдут именно вашему проекту.

От редакции



Записывайтесь на курсы Нетологии:

«Веб-дизайнер: эффективный сайт от идеи до реализации» ->

«Моушн-дизайн: графика в движении» ->

«Проектирование интерфейсов: UX-дизайн от стратегии до тестирования» ->

«UX-аналитика: исследования пользователей и здравый смысл» ->

… и на наши бесплатные программы:

«Основы графического дизайна: композиция, цвет, типографика» ->

«Adobe Photoshop: основы для веб-дизайнера» ->

«Adobe XD: основы для дизайнера интерфейсов» ->
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332992/


Метки:  

Поиск сообщений в rss_rss_hh_new
Страницы: 1437 ... 1050 1049 [1048] 1047 1046 ..
.. 1 Календарь