...

суббота, 18 апреля 2020 г.

Расширяем возможности процедурных макросов с помощью WASM

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


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

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

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

Пусть у нас имеется некоторый макрос TextMessage, который выводит для заданного типа трейты ToString и FromStr используя в качестве текстового представления некоторый кодек. У разных типов сообщений может быть различный кодек, причем их полный список со временем может расширятся, а у каждого кодека может быть свой уникальный набор атрибутов.

#[derive(Debug, Serialize, Deserialize, PartialEq, TextMessage)]
#[text_message(codec = "serde_json", params(pretty))]
struct FooMessage {
    name: String,
    description: String,
    value: u64,
}

Чтобы сделать такой макрос возможным, мы должны динамически подгружать реализации кодеков в процессе выполнения макроса. Можно вынести кодеки в подключаемую библиотеку и просто загружать их через libloading, но это очень неудобно и еще больше отдалит нас от возможности поддержки макросов в IDE. Вместо этого теоретически возможно написать такой вот кодек на динамическом языке типа Питона, но тогда нам придется писать для Питона аналоги syn и quote, что будет больше напоминать Сизифов труд, чем реальное решение проблемы.
Наиболее же простым и удобным видится вариант скомпилировать кодек в WASM модуль, объединив плюсы обоих подходов. Именно таким путем я предлагаю и пойти.


Выбираем подход к реализации

На первый взгляд кажется, что проблема уже решена в рамках watt и можно просто использовать его для загрузки и выполнения WASM модулей, но с этим подходом есть один весьма неприятный недостаток. Для своей работы watt использует модифицированный крейт proc-macro2, что частенько приводит к непонятным или трудноуловимым проблемам. Например, у меня не компилировался darling или если я забывал подменять proc-macro2, то получал в рантайме неочевидные ошибки.

В результате я решил, что лучше уж пользоваться ванильным proc-macro2, а в качестве WASM
рантайма взять какой-нибудь из самых популярных. В результате, мой субъективный выбор пал на wasmtime, этот рантайм разрабатывается сообществом bytecodealliance, в состав которого входят такие гиганты, как Mozilla, Intel и RedHat. И хотя wasmtime сейчас выглядит еще достаточно сырым, в нем не хватает документации, хороших примеров, но развивается он очень быстро и улучшается прямо на глазах


Взаимодействие хостом и таргетом

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

Погнали!

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

В самом простом виде интерфейс плагина для нашего процедурного макроса должен представлять из себя вариацию на тему:

pub fn implement_codec(input: TokenStream) -> TokenStream;

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

pub fn implement_codec(input: &str) -> String;

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

В целях обеспечения безопасности и большей стабильности, Республика будет реорганизована нами в первую Галактическую Империю, во имя сохранности и во имя блага общества! память WASM рантайма отделена от хостовой, с точки зрения хоста это просто плоский массив байт, в котором находится код программы, глобальные переменные, стек и куча.
Есть возможность сделать так, чтобы память была расширяемой, то если если при очередном выделении памяти нам не хватает места, то верхняя граница памяти автоматически увеличивается. Индекс ячейки в этом самом массиве используется в качестве указателя внутри таргета, но мы не можем просто взять и записать строчку в случайный участок памяти и отдать таргету индекс его начала, потому что снаружи мы не знаем то, как таргет в реальности использует память, где у него находится стек, а где куча. Но мы можем пойти на хитрость: с хоста обратиться к менеджеру памяти таргета и попросить у него выделить нам участок памяти.

#[no_mangle]
pub unsafe extern "C" fn toy_alloc(size: i32) -> i32 {
    let size_bytes: [u8; 4] = size.to_le_bytes();
    let mut buf: Vec<u8> = Vec::with_capacity(size as usize + size_bytes.len());
    // Первые 4 байта - это длина общая куска памяти, она нам еще понадобится в 
    // дальнейшем.
    buf.extend(size_bytes.iter());
    to_host_ptr(buf)
}

unsafe fn to_host_ptr(mut buf: Vec<u8>) -> i32 {
    let ptr = buf.as_mut_ptr();
    // Просто забываем о выделенном участке памяти, позволяя ему "утечь", таким 
    // образом мы передаем его во владение хосту.
    mem::forget(buf);
    ptr as *mut c_void as usize as i32
}

#[no_mangle]
pub unsafe extern "C" fn toy_free(ptr: i32) {
    let ptr = ptr as usize as *mut u8;
    let mut size_bytes = [0u8; 4];
    ptr.copy_to(size_bytes.as_mut_ptr(), 4);
    // Вычитываем общую длину куска памяти для того, чтобы корректно выполнить 
    // его очистку.
    let size = u32::from_le_bytes(size_bytes) as usize;
    // Собираем вектор, о котором мы ранее "забыли" в методе `to_host_ptr` и 
    // таким образом даем его деструктору вызваться нормальным образом и очистить
    // ранее выделенный участок памяти.
    Vec::from_raw_parts(ptr, size, size);
}

В принципе, ничего хитрого на самом деле в этом нет, примерно этим же занимается wasm_bindgen.

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

#[no_mangle]
pub unsafe extern "C" fn implement_codec(
    item_ptr: i32,
    item_len: i32,
) -> i32 {
    let item = str_from_raw_parts(item_ptr, item_len);
    let item = TokenStream::from_str(&item).expect("Unable to parse item");

    // Здесь уже вызывается типичная функция, реализующая процедурный макрос.
    // `fn(item: TokenStream) -> TokenStream`
    let tokens = codec::implement_codec(item);
    let out = tokens.to_string();

    to_host_buf(out)
}

pub unsafe fn str_from_raw_parts<'a>(ptr: i32, len: i32) -> &'a str {
    let slice = std::slice::from_raw_parts(ptr as *const u8, len as usize);
    std::str::from_utf8(slice).unwrap()
}

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


pub struct WasmMacro {
    module: Module,
}

impl WasmMacro {
    // Конструктор нашего макроса расширения.
    pub fn from_file(file: impl AsRef<Path>) -> anyhow::Result<Self> {
        // Загружаем и компилируем WASM модуль, находящийся по заданному пути.
        let store = Store::default();
        let module = Module::from_file(&store, file)?;
        Ok(Self { module })
    }

    // Вызываем метод с именем `fun` внутри нашего модуля, в котором содержится
    // основная логика преобразования входного TokenStream в выходной.
    pub fn proc_macro_derive(
        &self,
        fun: &str,
        item: TokenStream,
    ) -> anyhow::Result<TokenStream> {
        // Как уже описывалось ранее, чтобы передавать TokenStream между средами,
        // нам необходимо преобразовать его в строку.
        let item = item.to_string();

        // Создаем конкретный экземпляр модуля, с которым и будем работать.
        let instance = Instance::new(&self.module, &[])?;
        // Получаем указатель на нужную нам функцию, в данном случае это 
        // описанная выше `implement_codec`.
        let proc_macro_attribute_fn = instance
            .get_export(fun)
            .ok_or_else(|| anyhow!("Unable to find `{}` method in the export table", fun))?
            .func()
            .ok_or_else(|| anyhow!("export {} is not a function", fun))?
            .get2::<i32, i32, i32,>()?;

        // Для передачи данных строки внутрь WASM модуля используем специальную
        // обертку, о которой я подробнее расскажу ниже.
        let item_buf = WasmBuf::from_host_buf(&instance, item);
        // Получим из обертки указатель на начало строки и ее длину в байтах
        let (item_ptr, item_len) = item_buf.raw_parts();
        // А теперь вызываем искомый метод и в результате получаем указатель
        // на начало строки с выходным TokenStream. 
        let ptr = proc_macro_attribute_fn(item_ptr, item_len).unwrap();
        // Оборачиваем сырой указатель и читаем получившуюся строку.
        let res = WasmBuf::from_raw_ptr(&instance, ptr);
        let res_str = std::str::from_utf8(res.as_ref())?;
        // В заключительном этапе парсим строку в TokenStream и возращаем выше.
        TokenStream::from_str(&res_str)
            .map_err(|_| anyhow!("Unable to parse token stream"))
    }
}

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

struct WasmBuf<'a> {
    // Индекс начала выделенного буфера, проще говоря, указатель на его начало.
    offset: usize,
    // Длина буфера в байтах.
    len: usize,
    // Ссылка на инстанс модуля, в котором выделялась память
    instance: &'a Instance,
    // Ссылка на всю память, связанную с этим инстансом.
    memory: &'a Memory,
}

const WASM_PTR_LEN: usize = 4;

impl<'a> WasmBuf<'a> {
    // Самый простой конструктор буфера: мы просто при помощи `toy_alloc`
    // запрашиваем искомое число байт.
    pub fn new(instance: &'a Instance, len: usize) -> Self {
        let memory = Self::get_memory(instance);
        // Выделяем память и получаем на нее указатель.
        let offset = Self::toy_alloc(instance, len);

        Self {
            offset: offset as usize,
            len,
            instance,
            memory,
        }
    }

    // Намного удобнее не просто запрашивать буфер, а потом руками заполнять его,
    // а сразу передать ссылку на байты, которые мы хотим в него записать.
    pub fn from_host_buf(instance: &'a Instance, bytes: impl AsRef<[u8]>) -> Self {
        let bytes = bytes.as_ref();
        let len = bytes.len();

        let mut wasm_buf = Self::new(instance, len);
        // Копируем байты с хостового буфера в буфер таргета.
        wasm_buf.as_mut().copy_from_slice(bytes);
        wasm_buf
    }

    // Если же буфер был выделен внутри таргета, то все становится несколько
    // сложнее. Так как получить мы можем лишь указатель на начало буфера и 
    // непонятно каким же образом мы получим размер выделенной памяти.
    // Но мы не зря написали `toy_alloc` таким образом, чтобы первые его 4
    // байта содержали размер выделенного буфера.
    pub fn from_raw_ptr(instance: &'a Instance, offset: i32) -> Self {
        let offset = offset as usize;
        let memory = Self::get_memory(instance);

        let len = unsafe {
            // Получаем сырой указатель на память инстанса.
            let buf = memory.data_unchecked();

            let mut len_bytes = [0; WASM_PTR_LEN];
            // Читаем байты с размером выделенного буфера.
            len_bytes.copy_from_slice(&buf[offset..offset + WASM_PTR_LEN]);
            u32::from_le_bytes(len_bytes)
        };

        Self {
            offset,
            len: len as usize,
            memory,
            instance,
        }
    }

    // Методы для чтения и записи данных являются весьма тривиальными.
    // Важно лишь помнить про то, что нужно читать со смещением в 4 байта.

    pub fn as_ref(&self) -> &[u8] {
        unsafe {
            let begin = self.offset + WASM_PTR_LEN;
            let end = begin + self.len;

            &self.memory.data_unchecked()[begin..end]
        }
    }

    pub fn as_mut(&mut self) -> &mut [u8] {
        unsafe {
            let begin = self.offset + WASM_PTR_LEN;
            let end = begin + self.len;

            &mut self.memory.data_unchecked_mut()[begin..end]
        }
    }    
}

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

impl Drop for WasmBuf<'_> {
    fn drop(&mut self) {
        Self::toy_free(self.instance, self.len);
    }
}

Собираем все вместе

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

#[proc_macro_derive(TextMessage, attributes(text_message))]
pub fn text_message(input: TokenStream) -> TokenStream {
    let input: DeriveInput = parse_macro_input!(input);

    let attrs = TextMessageAttrs::from_raw(&input.attrs)
        .expect("Unable to parse text message attributes.");

    // Для простоты будем грузить модули из директории codecs, которые имеют 
    // особым образом сформированное имя. 
    let codec_dir = Path::new(&std::env::var("CARGO_MANIFEST_DIR")
        .unwrap())
        .join("codecs");
    let plugin_name = format!("{}_text_codec.wasm", attrs.codec);
    let codec_path = codec_dir.join(plugin_name);

    let wasm_macro = WasmMacro::from_file(codec_path)
        .expect("Unable to load wasm module");

    wasm_macro
        .proc_macro_derive(
            "implement_codec",
            input.into_token_stream().into(),
        )
        .expect("Unable to apply proc_macro_attribute")
}

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

#[derive(Debug, Serialize, Deserialize, PartialEq, TextMessage)]
// Что особенно хорошо, каждый WASM плагин может иметь свои произвольные атрибуты.
#[text_message(codec = "serde_json", params(pretty))]
struct FooMessage {
    name: String,
    description: String,
    value: u64,
}

fn main() {
    let msg = FooMessage {
        name: "Linus Torvalds".to_owned(),
        description: "The Linux founder.".to_owned(),
        value: 1,
    };

    let text = msg.to_string();
    println!("{}", text);
    let msg2 = text.parse().unwrap();

    assert_eq!(msg, msg2);
}

Выводы

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

Let's block ads! (Why?)

2 в 1: шифрование с имитозащитой

Классическими задачами, которые решаются криптографическими методами, являются обеспечение конфиденциальности и обеспечение аутентичности/имитостойкости хранимых и передаваемых данных. Ранее (примерно до середины 2000-х годов) для решения подобных задач использовались шифрование (конфиденциальность) и функции выработки имитовставки/кодов аутентификации (имитостойоксть). При этом шифрование и функция выработки имитовставки реализовывались отдельными криптографическими механизмами, что вызывало много проблем. В первую очередь, это связано с управлением ключевой информацией: при использовании одного ключа для шифрования и имитозащиты ряд схем, например, AES-CBC + AES-CBC-MAC являются полностью нестойкими. Для того, чтобы сделать такие конструкции безопасности приходится вырабатывать дополнительные ключи используя, например, функции выработки производных ключей (KDF). Это, в свою очередь, зачастую приводит к сильному усложнению криптографических протоколов, использующих подобные схемы. Кроме этого, последовательное использование двух механизмов не всегда является самым быстрым решением с точки зрения производительности.

С начала XXI века начались попытки создать механизмы шифрования с имитозащитой (иногда можно встретить термин «аутентифицированное шифрование» — от английского Authenticated Encryption), которые бы решали сразу обе указанные задачи.
Следующей ступенью развития таких механизмов можно считать механизмы шифрования с имитозащитой и ассоциированными данными (от англ. AEAD — Authenticated Encryption with Associated Data). Особенностью AEAD-механизмов является то, что они одновременно могут обрабатывать данные двух типов: данные, для которых необходимо обеспечить конфиденциальность и имитозащиту (например, данные IP-пакета), и данные, для которых необходимо обеспечить только имитозащиту без конфиденциальности – их еще называют «дополнительно имитозащищаемые данные» («ассоциированные данные», «дополнительно аутентифицируемые данные» — это может быть заголовок IP-пакета). Одной из наиболее востребованных областей применения AEAD-механизмов являются различные криптографические протоколы защиты данных, например, недавно принятый IETF TLS 1.3 RFC 8446, о чем уже писали на Хабре. Так вот, этот RFC 8446 рассматривает в качестве используемых в протоколе алгоритмы аутентифицированного шифрования (о принципах, лежащих в основе протокола TLS 1.3, можно почитать тут).

Строиться AEAD-механизмы могут на основе различных конструкций: поточные и блочные шифры, сжимающие отображения (хэш-функции), популярные сейчас конструкции типа «губка» (от англ. «sponge»). Разнообразие вариантов можно посмотреть, в частности, на сайте конкурса CAESAR и в различных обзорах про этот конкурс, см. например здесь и здесь. Кстати, сам конкурс был организован в 2013 году как раз с целью определения лучшего AEAD-механизма взамен широко используемого AES-GCM (режим GCM стандартизован NIST в 2007 году), для которого на тот момент был предложен ряд атак (вот и вот). При этом, к участникам конкурса CAESAR были предъявлены дополнительные функциональные требования, такие как возможность работы «онлайн», возможность распараллеливания, свобода от инверсий, защита от некорректного использования инициализационных и одноразовых векторов, наличие предвычислений, инкрементация данных, промежуточные имитовставки, повторное использование фиксированных ассоциированных данных. Поясним подробнее.

Работа «онлайн»: зачастую для обеспечения конфиденциальности/имитостойкости требуется сначала сформировать полностью весь пакет данных, который будет обрабатываться, а только потом начать процедуру обработки. Механизмы, допускающие работу «онлайн», этого не требуют, они могут работать с поступающим в режиме реального времени потоком данных, обрабатывая его «на лету». Под распараллеливанием AEAD-механизмов понимается возможность распределения вычислений между несколькими процессорами. Под свободой от инверсий понимается использование в AEAD-механизме только функции зашифрования или только функции расшифрования. Эта характеристика важна с точки зрения реализации: для некоторых шифров (как, например, Кузнечик, AES) – шифрование и расшифрование реализуются с помощью разных преобразований, соответственно, свобода от инверсий означает меньшую площадь кристалла при аппаратных реализациях или меньший объем программного кода при программной. С предвычислениями все просто – это возможность после выбора ключа провести ряд предварительных вычислений, которые в дальнейшем ускорят обработку поступающих данных.

Инкрементацию данных и повторное использование фиксированных ассоциированных данных в некотором смысле тоже можно отнести к предвычислениям. Под инкрементацией понимается возможность быстро пересчитать имитовставку, в случае если к уже обработанным данным мы добавили некоторые дополнительные данные, без повторной обработки всех данных. Использование фиксированных ассоциированных данных – это возможность выполнить предвычисления для часто встречаемых данных, чтобы каждый раз, когда они появляются, заново их не обрабатывать. Последнее свойство (промежуточные имитовставки) – это, в некотором смысле, тоже работа «на лету», т.е. возможность проверки на принимающей стороне корректности данных в процессе их обработки, не дожидаясь окончания передачи. Таким образом, если проверка промежуточной имитовставки не пройдет, весь последующий поток данных не требуется обрабатывать, и это экономит время и ресурсы.

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

Прообраз отечественного AEAD-режима, впоследствии названого MGM (Multi Linear Galois Mode), впервые был представлен в 2017 г. MGM является режимом работы блочного шифра. Режим состоит из двух частей, каждая из которых основывается на своем счетчике. Первый счетчик используется для выработки последовательности, которая далее используется для шифрования. Принцип работы схож со счетчиком режима CTR (см. ГОСТ Р 34.13-2015 или ISO/IEC 10116), однако имеет существенное отличие: первоначальное значение счетчика получается с помощью шифрования из уникального вектора инициализации (nonce). Второй счетчик используется для построения мультилинейной функции, на основе которой вырабатывается имитовставка. Первый и второй счетчики работают по разному, первый инкрементирует правую половину блока, а второй — левую. Схема работы режима представлена на рисунке.

Здесь $E_k$ — произвольный блочный шифр с длиной блока $n$ бит, $A_1,...,A_h^*$ — блоки ассоциированных данных, $P_1,...,P_q^*$ — блоки открытого текста, $nonce$ — уникальный вектор инициализации длины $n-1$ бит, $\oplus$ — операция побитового сложения, $\otimes$ — операция умножения в поле $GF(2^{128})$, $MSB_S$ — усечение блока длины до длины $S$, $len(A)||len(C)$ — длина в битах ассоциированных данных и шифртекста соответственно, $inc_r,inc_l$ — функции инкрементирования.

В докладе и в статье можно найти подробное описание принципов построения режима MGM. Если коротко, то при разработке решалась следующая задача: создать функциональный и хорошо распараллеливаемый режим работы блочных шифров, который был бы не подвержен известным атакам, в частности, атакам, успешно применимым к режиму GCM. В упомянуты докладе показаны следующие функциональные возможности режима:

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

В отличие от многих используемых в настоящее время режимов, для MGM удалось получить формальное обоснование стойкости в модели так называемой доказуемой стойкости (provable security), которое может быть резюмировано в виде двух теорем, оценивающих безопасное число блоков открытого текста, допустимых для обработки с помощью режима MGM без смены ключа. Пожалеем читателей и приведем здесь только их формулировки, желающие посмотреть полное доказательство и хорошенько проплавить мозги могут обратиться к исходной публикации. Первая теорема говорит об обеспечении конфиденциальности информации.
Теорема
Преимущество противника в задаче нарушения конфиденциальности, делающего не более $q$ запросов к оракулу, общей длины открытого текста и ассоциированных данных не более $\sigma$ блоков, оценивается следующим неравенством:

$Adv_{MGM_{Perm(\{0,1\}^n)}^{Priv}} \leq \frac{3(\sigma+4q)^2}{2^n}$



Вторая — о безопасности аутентификации данных (их имитостойкости).
Теорема
Преимущество противника в задаче нарушения имитостойкости, делающего не более $q$ запросов к оракулу, общей длины открытого текста и ассоциированных данных не более $\sigma$ блоков, и выдающего подделку общей длины шифртекста и ассоциированных данных не более $l$ блоков, оценивается следующим неравенством:

$Adv_{MGM_{Perm(\{0,1\}^n)}^{Auth}} \leq \frac{3(\sigma+3q+l+2)^2}{2^n}+\frac{1}{2^{s-1}}$



Заметим, что при использовании подхода «доказуемой стойкости» всегда возникает вопрос о точности полученных оценок (т.е. насколько они на самом деле соответствуют действительности и адекватны практике). Так вот в данном случае они оказались точными, что подтвердили результаты работы, в которой указаны теоретические атаки для режима MGM в случае, если объем материала не удовлетворяет приведенным теоремам.

В таблице далее представлено сравнение разработанного режима с финалистами конкурса CAESAR по указанным выше характеристикам.

В таблице в качестве используемого примитива: обозначены BC — блочный шифр, SC — поточный шифр, Dedic — оригинальная конструкция (не использует шифр), Sponge — использует губку.

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

В сентябре прошлого года Росстандарт утвердил режим MGM в рекомендациях по стандартизации Р 1323565.1.026–2019 «Информационная технология. Криптографическая защита информации. Режимы работы блочных шифров, реализующие аутентифицированное шифрование». Кроме того, в начале этого года организацией IANA были выделены идентификаторы, а Росстандартом были приняты рекомендации по стандартизации Р 1323565.1.030-2020 «Информационная технология. Криптографическая защита информации. Использование криптографических алгоритмов в протоколе безопасности транспортного уровня (TLS 1.3)» для использования российских криптоалгоритмов в протоколе TLS 1.3, — и как раз с применением режима MGM.

Let's block ads! (Why?)

Голосовые помощники помогут водителям не отвлекаться за рулем: на самом деле — не всегда и не всем

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


Фото Samuele Errico Piccarini / Unsplash

Еще удобнее


Мультимедийные системы — один из центральных элементов автомобиля. Они отвечают за управление множеством функций, что с одной стороны, достаточно удобно, а с другой — создает проблемы с безопасностью на дороге. Согласно исследованию, проведенному некоммерческой организацией IAM Roadsmart, работая с тачскрином, водитель теряет концентрацию в среднем на 16 секунд, хуже держит дистанцию и виляет в полосе.

Автопроизводители признают проблему и предлагают варианты её устранения — внедряют альтернативные способны управления инфотейнмент-системами и возвращают кнопки для часто используемых функций на приборную панель и рулевое колесо. Также одним из перспективных направлений считают голосовых ассистентов. Многие концерны разрабатывают собственных помощников. Ford предлагает Sync, General Motors — OnStar, проприетарная технология есть даже у Mini Cooper. Большая часть производителей добавляет интеграции со сторонними решениями вроде Alexa и Google Assistant. Например, помощник от Amazon появится в новом Volkswagen Golf, а Volvo запустит линейку электрокаров с мультимедийной системой на Android.

Эксперты говорят, что к 2022 году автомобильными голосовыми ассистентами будут пользоваться 73% водителей. Для сравнения, на конец 2019 года эта цифра не превышала 50%.

Или нет


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

Еще в 2014 году некоммерческая организация AAA Foundation for Traffic Safety, занимающаяся вопросами безопасности дорожного движения, и Университет штата Юта определили, что голосовое управление отвлекает водителей сильнее, чем простой разговор по телефону. Эти данные подтверждает свежее исследование британской корпорации Transport Research Laboratory. При этом в компании утверждают, что общение с голосовым ассистентом ведет к рискам, сравнимым с вождением в пьяном виде.


Фото Damir Kopezhanov / Unsplash

Кроме того, как пишет The New York Times, голосовые помощники в автомобилях создают проблему с информационной безопасностью и открывают новые векторы атак для злоумышленников. Устройства вроде Alexa умеют удаленно заводить автомобиль, при этом ассистент может иметь доступ к подключенным устройствам в доме — даже электронным замкам. Хакеры могут использовать эту особенность, чтобы проникнуть в жилище или украсть персональные данные — например, о местоположении. Инженерам из ИБ-компании Armis уже удалось обнаружить серию уязвимостей, позволяющую реализовать подобные атаки, — её назвали BlueBorn.

Некоторые компании делают ставку на особый уровень защищенности голосовых помощников для авто. Французский стартап Snips проектирует SDK для локальной системы, которая не отправляет данные в облако и может работать при полном отсутствии интернета. В прошлом году стартап купили Sonos. Сейчас аудиокомпания готовится к выходу на автомобильный рынок. Можно предположить, что она будет продвигать и технологию Snips, если ей удастся заключить сделку с каким-либо концерном, а подобные решения начнут выпускать и другие ИТ-фирмы.



Материалы по теме из нашего «Мира Hi-Fi»:

«Стервозная Бетти» и аудиоинтерфейсы: почему они говорят женским голосом?
Первый в мире «гендерно-нейтральный» голосовой помощник
Аудиоинтерфейсы: звук как источник информации на дороге, в офисе и в небе


Let's block ads! (Why?)

ДИТ Москвы: сервис по проверке и редактированию данных для цифровых пропусков по Москве был атакован хакерами

Согласно информации агентства «Интерфакс», представитель столичного департамента информационных технологий рассказал, что новая форма веб-приложения по оформлению цифровых пропусков за несколько часов своей работы подверглась хакерским атакам. В этой форме можно проверить актуальность пропуска и скорректировать данные, например, поменять или добавить номер автомобиля, а также внести информацию о картах «Тройка», «Стрелка», данные по проездному билету или социальной карте.
«Пока еще не очень много хакерских атак, хотя уже попытки были и были пресечены сразу же. Но, я думаю, что постепенно будут как-то повышать нагрузку на нас, но мы к ней готовы», — заявил в эфире телеканала «Россия 24» (ВГТРК) Эдуард Лысенко, глава ДИТ Москвы.

Специальная веб-форма, в которой можно внести изменения в данные цифрового пропуска или исправить ошибки, если они были допущены при оформлении, появилась на портале nedoma.mos.ru утром в субботу 18 апреля 2020 года.

У этой формы очень полезный функционал — узнать актуальность полученного ранее электронного пропуска. Так как очень большая часть пропусков ранее была аннулирована из-за неправильно введенных данных. Ранее 14 апреля 2020 года cервисом по выдаче электронных пропусков на портале nedoma.mos.ru воспользовались более 3,2 млн пользователей, однако 900 тысяч пропусков позже были аннулированы из-за недостоверных сведений. В том числе часть этих аннулированных пропусков были отправлены для регистрации пользователями по SMS. Но тогда пользователи не могли своевременно проверить, что их пропуска в порядке или с ними проблемы. Теперь же все стало проще с получением этих данных.

Вдобавок стало известно, что, например, на 18 апреля 2020 года в Москве аннулировали 554 пропуска больных COVID-19 и проживающих с ними. Таким образом, эти пользователи сервиса nedoma.mos.ru также могут проверить свои пропуска в новой форме и удостоверится, что они теперь не работают.

Как защищен новый веб-сервис по проверке и редактированию данных для цифровых пропусков по Москве от злоумышленников? В сервисе используется reCAPTCHA и подтверждение изменений в пропуске по SMS.

При входа в сервис необходимо ввести 16-значный код электронного пропуска и пройти reCAPTCHA. Эти коды можно найти в соцсетях и в сети интернет на фотографиях пользователей, которые их выкладывали на общее обозрение, не задумываясь о последствиях. Можно попробовать сгенерировать часть 16-значных кодов, но это будет долгий процесс, хотя первая часть этих кодов в некоторых случаях стандартная — например, 3004 или 3KEP.

Далее появляется информацию о данных по электронному пропуску. Причем ее видят все пользователи, которые даже не зарегистрированы на портале mos.ru. Так как часть данных по пропуску все равно в этой форме скрыто или заменено на символ «X», то проверить или узнать их полный объем для злоумышленника будет проблематично.

Если нажать кнопку «Изменить данные пропуска», то появится страничка, где можно выбрать категорию изменения данных — поменять номер автомобиля, поменять или внести данные по картам пользователя. Можно сразу все данные скорректировать.

Однако, если внести в этот пропуск новые данные, то просто так их сохранить не удасться. Так на на мобильный телефон пользователя будет отправлено SMS-сообщение с пятизначным кодом, который нужно ввести, чтобы подтвердить новые данные. Технически, это единственное место в этой веб-форме, где можно путем подбора кода из сообщения скомпрометировать и испортить электронный пропуск, так как пока что можно вводить несколько кодов подряд без блокировки. Но для этого нужно перебрать 99999 комбинаций, пока пользователь не обратил внимание, что ему пришло SMS с портала mos.ru и не начал разбираться с этим непонятным событием.

После изменения этих данных в пропуске новая сохраненная злоумышленником информация в течение 5 часов должна поступить в систему контроля от ДИТ (требуется не менее чем за 5 часов до начала первой поездки по этому пропуску) и только тогда именно она будет использоваться для пропуска или блокировки пользователя или даже вынесения штрафа в его сторону, так как с 22 апреля 2020 года в Москве все машины, не включенные в цифровые пропуска, будут автоматически считаться нарушителями, а их владельцы будут штрафоваться.

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

Let's block ads! (Why?)

[recovery mode] Reuters: 5 возможных сценариев восстановления экономики после пандемии коронавируса

Агентство Reuters опросило более 50 экономистов, задав им вопрос о том, как может выглядеть восстановление экономики после завершения основной волны пандемии COVID-19. Будет график восстановления напоминать английскую букву U, V или, может быть, W?

В нашем новом материале – основные тезисы всех представленных гипотез.

Первый вопрос: насколько сильным будет падение?


Существуют разные прогнозы относительно падения объемов мировой экономики: от падения на 6% в 2020 году до роста в 0,7%. В среднем, эксперты предсказывают падение на 1,2%.

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

1. Латинская V: быстрое падение и рост


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

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

Открытие бизнесов и восстановление деловой активности могут привести к ощутимым результатам уже в 3-4 квартале 2020 года.

2. U-график: падение, стагнация и активный рост


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

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

3. W: восстановление и вторая волна кризиса


Это так называемый «сценарий двойного падения»: облегчение мер локдауна принесет оживление в экономику, однако кризис никуда не денется. За время карантина многие предприятия и бизнесы обанкротятся, вырастет безработица. Также сохраняется вероятность второй волны эпидемии ближе к осени – она может быть спровоцирована отменой карантинных мер.

4. L: затяжной кризис


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

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

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

5. Альтернативный вариант


Как пишет Bloomberg, многие экономисты постепенно теряют веру в реализацию сценария с V-образным графиком восстановления мировой экономики. Вместо этого они предполагают восстановление в рваном ритме. В итоге это будет график не в виде буквы V, U или L. Вот, как видят этот график эксперты Deutsche Bank:

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

Читайте обзоры, аналитику рынков и инвестидеи в Telegram-канале ITI Capital

Полезные ссылки по теме инвестиций и биржевой торговли:


Let's block ads! (Why?)

[Из песочницы] DevOps инструменты не только для DevOps. Процесс построения инфраструктуры автоматизации тестирования с нуля


Примечание: данная статья является переводом на русский язык оригинальной статьи «DevOps tools are not only for DevOps. Building test automation infrastructure from scratch». Однако все иллюстрации, ссылки, цитаты и термины сохранены на языке оригинала, чтобы избежать искажения смысла при переводе на русский язык. Желаю вам приятного изучения!

В настоящее время специальность DevOps является одной из наиболее востребованных в IT-индустрии. Если вы откроете популярные сайты по поиску работы и зададите фильтр по зарплатам, то увидите, что вакансии, связанные с DevOps, находятся в начале списка. Однако важно понимать, что это в основном относится к позиции ‘Senior’, что подразумевает, что кандидат обладает высоким уровнем навыков, знанием технологий и инструментов. К этому также прилагается высокая степень ответственности, связанная с бесперебойной работой production. Однако мы стали забывать, что такое DevOps. Изначально это не был какой-то конкретный человек или департамент. Если поискать определения этого термина, то мы найдем много красивых и правильных существительных, таких как методология, практики, культурная философия, группа концептов и так далее.

Моя специализация – инженер по автоматизации тестирования (QA automation engineer), но я считаю, что она не должна быть связана только с написанием авто-тестов или разработкой архитектуры тестового framework. В 2020 году знания инфраструктуры автоматизации также необходимы. Это позволяет организовать процесс автоматизации самостоятельно, начиная от запуска тестов и заканчивая предоставлением результатов всем заинтересованным лицам в соответствии с поставленными целями. В результате чего, навыки DevOps являются обязательным фактором для выполнения данной работы. И все это хорошо, но, к сожалению, есть проблема (spoiler: данная статья делает попытки упростить это проблему). Она заключается в том, что DevOps – это сложно. И это очевидно, ведь компании не станут платить много за то, что легко сделать… В мире DevOps большое количество инструментов, терминов, практик, которыми нужно овладеть. Особенно это тяжело дается в начале карьеры и зависит от накопленного технического опыта.


Источник: http://maximelanciauxbi.blogspot.com/2017/04/devops-tools.html

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

О чем эта статья


В этой статье я собираюсь поделиться моим опытом построения инфраструктуры автоматизации тестирования. В интернете можно найти много источников информации о различных инструментах и как их использовать, но я бы хотел рассмотреть их исключительно в контексте автоматизации. Я полагаю, что многим инженерам по автоматизации знакома ситуация, когда разработанные тесты, кроме вас самих, никто не запускает и не заботится об их поддержке. В результате чего тесты становятся устаревшими и приходится тратить время на их актуализацию. Опять же в начале карьеры это может быть довольно сложной задачей: грамотно решить, какие инструменты должны помочь устранить данную проблему, как их выбрать, настроить и поддерживать. Некоторые тестировщики обращаются за помощью к DevOps (людям) и, будем честными, такой подход работает .  Во многих случаях это может быть единственным вариантом, так как у нас отсутствует видимость всех зависимостей. Но, как мы знаем, DevOps – очень занятые ребята, ведь они должны думать об инфраструктуре всей компании, deployment, мониторинге, микросервисах и о других подобных задачах в зависимости от организации/команды. Как это обычно бывает, автоматизация не является приоритетом. В таком случае мы должны попытаться сделать все возможное с нашей стороны от начала и до конца. Это уменьшит зависимости, ускорит рабочий процесс, усовершенствует наши навыки и позволит увидеть более широкую картину происходящего.

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

Чего в этой статье нет


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

Это сделано по причине того, что: 

  • этот материал очень легко найти в различных источниках (документация, книги, видео курсы);
  • если мы начнем углубляться, то придется написать 10, 20, 30 частей данной статьи (тогда как в планах 2-3);
  • я просто не хочу тратить ваше время, так как, возможно, вы хотите использовать другие инструменты для достижения тех же целей.

Практика


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

План


Структура каждой секции


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

1. Локальный запуск тестов


Краткое описание технологии


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

Однако в качестве инструментов автоматизации я рекомендую использовать Selenium WebDriver для web-платформ и Appium для Android-платформы соответственно, так как на следующих шагах мы будем использовать Docker-образы, которые заточены на работу конкретно с этими инструментами. Более того, ссылаясь на требования в вакансиях, эти инструменты наиболее востребованы на рынке.

Как вы могли заметить, мы рассматриваем только web и Android-тесты. К сожалению, IOS – совершенно другая история (спасибо Apple). Я планирую продемонстрировать решения и практики, связанные с IOS, в следующих частях.

Ценность для инфраструктуры автоматизации


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

Иллюстрация текущего состояния инфраструктуры


Ссылки для изучения


Аналогичные инструменты


  • любой язык программирования,  который вам нравится, в связке с Selenium/Appium — тестами;
  • любые тесты;
  • любой тест-раннер.

2. Системы контроля версий (Git)


Краткое описание технологии


Ни для кого не будет большим открытием, если я скажу, что система контроля версий – чрезвычайно важная часть разработки как в команде, так и индивидуально. Опираясь на различные источники, можно с уверенностью сказать, что Git является наиболее популярным представителем. Система контроля версий дает множество преимуществ, таких как обмен кодом, хранение версий, восстановление на предыдущие ветки, мониторинг проектной истории, бэкапы. Мы не будем обсуждать каждый пункт в деталях, так как я уверен, что вы с этим хорошо знакомы и используете в повседневной работе. Но если вдруг нет, то я рекомендую приостановить чтение данной статьи и как можно скорее восполнить данный пробел.

Ценность для инфраструктуры автоматизации


И тут вы можете задать резонный вопрос: «Зачем он нам рассказывает о Git? Все это знают и используют как для кода разработки, так и для кода авто-тестов». Вы будете абсолютно правы, но в этой статье мы говорим об инфраструктуре и данная секция играет роль превью для секции 7: «Инфраструктура как код (IaC)». Для нас это означает, что вся инфраструктура, включая тестовую, описывается в виде кода, соответственно мы к ней тоже можем применить системы версионирования и получить аналогичные преимущества как для кода разработки и автоматизации.

Мы рассмотрим IaC более детально на шаге 7, но даже сейчас можно начать использовать Git локально, создав локальный репозиторий. Общая картина будет расширена, когда мы добавим к инфраструктуре удаленный репозиторий.

Иллюстрация текущего состояния инфраструктуры


Ссылки для изучения


Аналогичные инструменты


3. Контейнеризация (Docker)


Краткое описание технологии


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

Следующим этапом эволюции стали виртуальные машины (VM), которые решили проблему траты средств на неиспользуемые ресурсы. Эта технология позволила запускать приложения независимо друг от друга внутри одного сервера, выделяя полностью изолированное пространство. Но, к сожалению, любая технология имеет свои недостатки. Запуск VM требует полноценной операционной системы, которая потребляет CPU, RAM, хранилище и, в зависимости от OS, нужно учитывать расходы на лицензию. Эти факторы влияют на скорость загрузки и усложняют переносимость.

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

Конечно, технология контейнеризации не является чем-то новым и впервые была представлена в конце 70-х. В те времена проводилось много исследований, наработок, и попыток. Но именно Docker адаптировал эту технологию и сделал ее легкодоступной для масс. В наше время, когда мы говорим о контейнерах, в большинстве случаев мы имеем ввиду Docker. Когда мы говорим о Docker-контейнерах, мы подразумеваем Linux-контейнеры. Мы можем использовать Windows и macOS-системы для запуска контейнеров, но важно понимать, что в таком случае появляется дополнительная прослойка. Например, Docker на Mac незаметно запускает контейнеры внутри легковесной Linux VM. Мы еще вернемся к этой теме, когда будем обсуждать запуск Android-эмуляторов внутри контейнеров, так так здесь появляется очень важной нюанс, который необходимо разобрать более подробно.

Ценность для инфраструктуры автоматизации


Мы выяснили, что контейнеризация и Docker – это круто. Давайте посмотрим на это в контексте автоматизации, ведь каждый инструмент или технология должны решать какую-либо проблему. Обозначим очевидные проблемы автоматизации тестирования в контексте UI-тестов:
  • огромное количество зависимостей при установке Selenium и в особенности Appium;
  • проблемы совместимости между версиями браузеров, симуляторов и драйверов;
  • отсутствие изолированного пространства для браузеров/симуляторов, что особенно критично для параллельного запуска;
  • тяжело управлять и поддерживать, если необходимо запустить 10, 50, 100 или даже 1000 браузеров одновременно.

Но поскольку Selenium является самым популярным инструментом автоматизации, а Docker – самым популярным инструментом контейнеризации, то ни для кого не должно быть сюрпризом, что кто-то попытался их объединить, чтобы получить мощный инструмент для решения вышеупомянутых проблем. Рассмотрим такие решения более подробно. 

Selenium grid in docker

Этот инструмент является самым популярным в мире Selenium для запуска нескольких браузеров на нескольких машинах и управления ими из центрального узла. Для запуска необходимо зарегистрировать как минимум 2 части: Hub и Node(s). Hub – это центральный узел, который получает все запросы от тестов и распределяет их по соответствующим Nodes. Для каждого Node мы можем настроить конкретную конфигурацию, например, указав нужный браузер и его версию. Однако нам все еще необходимо самим позаботиться о совместимых драйверах для браузеров и установить их на нужные Nodes. По этой причине Selenium grid не используется в чистом виде, за исключением тех случаев, когда нам нужно работать с браузерами, которые нельзя установить на Linux OS. Для всех остальных случаев значительно гибким и правильным решением будет использование Docker-образов для запуска Selenium grid Hub и Nodes. Такой подход сильно упрощает управление узлами, так как мы можем выбрать нужный нам образ с уже установленными совместимыми версиями браузеров и драйверов.

Несмотря на негативные отзывы о стабильности работы, особенно при запуске большого числа Nodes параллельно, Selenium grid все еще остается самым популярным инструментом для параллельного запуска Selenium-тестов. Важно отметить, что в open-source постоянно появляются различные доработки и модификации данного инструмента, которые борются с различными узкими местами.

Selenoid for Web

Этот инструмент является прорывом в мире Selenium, так как он работает сразу из коробки и сделал жизнь многих инженеров по автоматизации значительно проще. Прежде всего, это не очередная модификация Selenium grid. Вместо этого разработчики создали абсолютно новую версию Selenium Hub на языке Golang, что в связке с легковесными Docker-образами для различных браузеров дало толчок в развитии автоматизации тестирования. Более того, в случае Selenium Grid мы должны определить все требуемые браузеры и их версии заранее, что не является проблемой, когда работа идет только с каким-то одним браузером. Но когда речь идет о нескольких поддерживаемых браузерах, то Selenoid – это решение номер один, благодаря функции ‘браузер по требованию’. Все, что от нас требуется, это заранее выгрузить нужные образы с браузерами и обновить файл конфигурации, с которым взаимодействует Selenoid. После того как Selenoid получит запрос от тестов, он автоматически запустит нужный контейнер с нужным браузером. Когда тест завершится, Selenoid отставит контейнер, тем самым освободив ресурсы для следующих запросов. Такой подход полностью устраняет известную проблему ‘деградации узлов’, что мы часто встречаем в Selenium grid.

Но, увы, Selenoid все еще не серебряная пуля. Мы получили функцию ‘браузер по требованию’, но функция ‘ресурсы по требованию’ все еще не доступна. Для использования Selenoid мы должны развернуть его на физическом железе или на VM, что означает, что мы должны заранее знать, сколько ресурсов необходимо выделить. Я полагаю, это не проблема для маленьких проектов, которые запускают 10, 20 или даже 30 браузеров параллельно. Но что если нам нужно 100, 500, 1000 и больше? Не имеет никакого смысла поддерживать и платить за такое количество ресурсов постоянно. В секции 5 и 6 данной статьи мы обсудим решения, которые позволяют масштабироваться, тем самым значительно снижая расходы компании.

Selenoid for Android

После успеха Selenoid в качестве инструмента для web-автоматизации, люди хотели получить что-то подобное для Android. И это свершилось – Selenoid был выпущен с поддержкой Android. С высокоуровневой пользовательской точки зрения принцип работы аналогичен web-автоматизации. Единственное отличие заключается в том, что вместо контейнеров с браузерами Selenoid запускает контейнеры с Android-эмуляторами. На мой взгляд, на сегодняшний момент это самый мощный бесплатный инструмент для запуска Android-тестов параллельно.

Мне бы очень не хотелось говорить о негативных сторонах данного инструмента, так как он действительно мне очень нравится. Но все же тут присутствуют те же недостатки, относящиеся и к web-автоматизации, связанные с масштабированием. В дополнение к этому нужно рассказать о еще одном ограничении, которое может стать неожиданностью, если мы настраиваем инструмент впервые. Для запуска Android-образов нам необходима физическая машина или VM с nested virtualisation — поддержкой. В практическом руководстве я демонстрирую, как это активировать на Linux VM. Однако если вы являетесь macOS пользователем и хотите развернуть Selenoid локально, то для запуска Android-тестов это будет невозможно. Но вы всегда можете запустить Linux VM локально с настроенной ‘nested virtualisation’ и развернуть Selenoid внутри.

Иллюстрация текущего состояния инфраструктуры


В контексте этой статьи мы добавим 2 инструмента для иллюстрации инфраструктуры. Это Selenium grid для web-тестов и Selenoid для Android-тестов. В руководстве на GitHub я также покажу, как использовать Selenoid для запуска web-тестов. 

Ссылки для изучения


Аналогичные инструменты


  • Существуют другие инструменты контейнеризации, но Docker – самый популярный. Если вы хотите попробовать что-то еще, то учтите, что те инструменты, которые мы рассмотрели для параллельного запуска Selenium-тестов, не будут работать из коробки.  
  • Как уже было сказано, существует много модификаций Selenium grid, например, Zalenium.

4. CI / CD


Краткое описание технологии


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

Итак, существуют 3 термина: CI — Continuous Integration (непрерывная интеграция), CD — Continuous Delivery (непрерывная поставка) и снова CD — Continuous Deployment (непрерывное развертывание).  (Далее я буду использовать эти термины на английском языке). Каждая модификация добавляет несколько дополнительных этапов к вашему конвейеру разработки. Но слово continuous (непрерывная) является самым главным. В этом контексте мы подразумеваем что-то, что происходит от начала и до конца, без прерываний или ручного воздействия. Давайте посмотрим на CI & CD и CD в данном контексте.

  • Continuous Integration – это начальный шаг эволюции. После отправки нового кода на сервер, мы ожидаем получить быструю обратную связь, что с нашими изменениями все в порядке. Обычно CI включает запуск инструментов статического анализа кода и модульные/внутренние API тесты Это позволяет получать информацию о нашем коде уже несколько секунд/минут спустя.
  • Continuous Delivery является более продвинутым шагом, на котором мы запускаем интеграционные/UI-тесты. Однако на данном этапе мы не получаем результаты так же быстро, как в случае с CI. Во-первых, эти типы тестов требует больше времени для прохождения. Во-вторых, перед запуском мы должны развернуть наши изменения на test/staging — среде. Более того, если мы говорим о мобильной разработке, то появляется дополнительный этап для создания сборки нашего приложения.
  • Continuous Deployment предполагает, что мы автоматически выпускаем (release) наши изменения на production, если все приемочные тесты были пройдены на предыдущих этапах. В дополнение к этому после этапа release можно настроить различные этапы, такие как запуск smoke — тестов на production и сбор интересующих метрик. Continuous Deployment возможно только при хорошем покрытии автоматизированными тестами. Если требуются какие-то ручные вмешательства, в том числе и тестирование, то это больше не Continuous (непрерывное). Тогда мы можем говорить, что наш конвейер соответствует только практике Continuous Delivery.

Ценность для инфраструктуры автоматизации


В этом разделе я должен уточнить, что, когда мы говорим об end-to-end UI-тестах, это подразумевает, что мы должны разворачивать наши изменения и связанные сервисы на тестовых средах. Continuous Integration — процесс не применим для данной задачи и мы должны позаботиться о внедрении как минимум Continuous Deliver практик. Continuous Deployment также имеет смысл в контексте UI-тестов, если мы собираемся запускать их на production.

И перед тем как мы посмотрим на иллюстрацию изменения архитектуры, я хочу сказать несколько слов о GitLab CI. В отличие от других CI/CD-инструментов, GitLab предоставляет удаленный репозиторий и много других дополнительных функций. Таким образом, GitLab – это больше чем CI. Он включает из коробки управление исходным кодом, Agile управление, CI/CD pipelines, инструменты логирования и сборы метрик. Архитектура GitLab состоит из Gitlab CI/CD и GitLab Runner. Привожу краткое описание с официально сайта:

Gitlab CI/CD is a web application with an API that stores its state in a database, manages projects/builds and provides a user interface. GitLab Runner is an application which processes builds. It can be deployed separately and works with GitLab CI/CD through an API. For tests running you need both Gitlab instance and Runner.

Иллюстрация текущего состояния инфраструктуры


Ссылки для изучения


Аналогичные инструменты


5. Облачные платформы


Краткое описание технологии


В этом разделе мы поговорим о популярном тренде, который называется ‘публичные облака’. Несмотря на огромную пользу, которую дают описанные выше технологии виртуализации и контейнеризации, нам все еще необходимы вычислительные ресурсы. Компании приобретают дорогие сервера или арендуют дата-центры, но в таком случае необходимо сделать расчеты (иногда нереалистичные) того, как много ресурсов нам понадобится, будем ли мы их использовать 24/7 и для каких целей. Например, для production требуется работающий круглосуточно сервер, но нужны ли нам аналогичные ресурсы для тестирования в нерабочее время? Это также зависит от типа выполняемого тестирования. Примером могут быть нагрузочные/стрессовые тесты, которые мы планируем прогонять в нерабочие часы, чтобы получить результаты на следующий день. Но, определенно, круглосуточная доступность серверов не требуется для end-to-end авто-тестов и в особенности для сред ручного тестирования. Для таких ситуаций было бы хорошо получать столько ресурсов, сколько необходимо по требованию, использовать их и прекращать платить, когда они больше не нужны. Более того, было бы прекрасно получать их моментально, сделав несколько кликов мышкой или запустив пару скриптов. Для этого и используются публичные облака. Давайте посмотрим на определение:
«The public cloud is defined as computing services offered by third-party providers over the public Internet, making them available to anyone who wants to use or purchase them. They may be free or sold on-demand, allowing customers to pay only per usage for the CPU cycles, storage, or bandwidth they consume».

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

Ценность для инфраструктуры автоматизации


Какие конкретно ресурсы нам необходимы для end-to-end UI-тестов? В основном это виртуальные машины или кластеры (мы поговорим о Kubernetes в следующей секции) для запуска браузеров и эмуляторов. Чем больше браузеров и эмуляторов мы хотим запустить одновременно, тем больше CPU и памяти требуется и тем больше денег нам придется за это заплатить. Таким образом, публичные облака в контексте автоматизации тестирования позволяют нам запускать большое число (100, 200, 1000 …) браузеров/эмуляторов по требованию, получать результаты тестирования как можно быстрее и переставать платить за такие безумно ресурсозатратные мощности. 

Самыми популярными облачными провайдерами являются Amazon Web Services (AWS), Microsoft Azure, Google Cloud Platform (GCP). В практическом руководстве представлены примеры использования GCP, но в целом неважно, что именно вы будете использовать для задач автоматизации. Все они предоставляют примерно одинаковый функционал. Обычно для выбора провайдера руководство фокусируется на всей инфраструктуре компании и бизнес-требованиях, что за рамками данной статьи. Для инженеров по автоматизации будет интереснее сравнить использование облачных провайдеров с использованием облачных платформ конкретно для целей тестирования, таких как Sauce Labs, BrowserStack, BitBar и так далее. Так давай те же сделаем это! На мой взгляд, Sauce Labs является самой известной фермой облачного тестирования, поэтому я и взял ее для сравнения. 

GCP против Sauce Labs для целей автоматизации:

Представим, что нам нужно прогнать одновременно 8 web-тестов и 8 Android-тестов. Для этого мы будем использовать GCP и запустим 2 виртуальные машины с Selenoid. На первой мы поднимем 8 контейнеров с браузерами. На второй – 8 контейнеров с эмуляторами. Давайте взглянем на цены:  


Для запуска одного контейнера с Chrome, нам понадобится n1-standard-1 машина. В случае с Android это будет n1-standard-4 для одного эмулятора. На самом деле более гибкий и дешевый способ – это задание конкретных пользовательских значений для CPU/Memory, но в данный момент для сравнения с Sauce Labs это не принципиально.

А вот тарифы на использование Sauce Labs:


Я полагаю, вы уже заметили разницу, но все же приведу таблицу с расчетами для нашей задачи:
Как видно, разница в стоимости огромна, особенно если запускать тесты только в рабочий двенадцатичасовой промежуток. Но можно еще сильнее сократить расходы, если использовать preemptible машины. Что же это такое?
A preemptible VM is an instance that you can create and run at a muchower price than normal instances. However, Compute Engine might terminate (preempt) these instances if it requires access to those resources for other tasks. Preemptible instances are excess Compute Engine capacity, so their availability varies with usage.

If your apps are fault-tolerant and can withstand possible instance preemptions, then preemptible instances can reduce your Compute Engine costs significantly. For example, batch processing jobs can run on preemptible instances. If some of those instances terminate during processing, the job slows but does not completely stop. Preemptible instances complete your batch processing tasks without placing additional workload on your existing instances and without requiring you to pay full price for additional normal instances.


И это все еще не конец! В действительности я уверен, что никто не запускает тесты по 12 часов без перерыва. И если это так, то вы можете автоматически запускать и останавливать виртуальные машины, когда они не нужны. Реальное время использования может снизиться до 6 часов в сутки. Тогда оплата в контексте нашей задачи уменьшится аж до 11$ в месяц за 8 браузеров. Разве это не прекрасно? Но с preemptible машинами мы должны быть осторожны и готовы к прерываниям и нестабильной работе, хотя эти ситуации могут быть предусмотрены и обработаны программно. Оно того стоит!

Но ни в коем случае я не говорю ‘никогда не используйте облачные тестовые фермы’. Они имеют ряд преимуществ. Прежде всего это не просто виртуальная машина, а полноценное решение для автоматизации тестирования с набором функционала из коробки: удаленный доступ, логи, скриншоты, видеозапись, различные браузеры и физические мобильные устройства. Во многих ситуациях это может быть незаменимой шикарной альтернативой. Особенно тестовые платформы полезны для IOS-автоматизации, когда публичные облака могут предложить только Linux/Windows-системы. Но разговор про IOS будет в следующих статьях. Я рекомендую всегда смотреть по ситуации и отталкиваться от задач: в каких-то дешевле и эффективнее использовать публичные облака, а в каких то тестовые платформы определено стоят потраченных денег.

Иллюстрация текущего состояния инфраструктуры


Ссылки для изучения


Аналогичные инструменты:


6. Оркестрация


Краткое описание технологии


У меня хорошие новости – мы почти достигли конца статьи! На данный момент наша инфраструктура автоматизации состоит из web и Android-тестов, которые мы запускаем через GitLab CI параллельно, используя инструменты с поддержкой Docker: Selenium grid и Selenoid. Более того, мы используем созданные через GCP виртуальные машины для поднятия в них контейнеров с браузерами и эмуляторами. Для уменьшения расходов мы запускаем этим виртуальные машины только по требованию и останавливаем, когда тестирование не проводится. Существует ли что-то еще, что может улучшить нашу инфраструктуру? Ответ – да! Встречаем Kubernetes (K8s)!

Для начала рассмотрим, как слова оркестрация, кластер и Kubernetes связаны между собой. На высоком уровне оркестрация – это система, которая разворачивает и управляет приложениями. Для автоматизации тестирования такими контейнеризируемыми (containerised) приложениями являются Selenium grid и Selenoid. Docker и K8s дополняют друг друга. Первый используется для развертывания приложений, второй – для оркестрации. В свою очередь K8s является кластером. Задача кластера использовать VMs в качестве Nodes, что позволяет устанавливать различный функционал, программы и сервисы в рамках одного сервера (кластера). Если какой либо из Node упадет, то подхватятся другие Nodes, что обеспечивает нашему приложению бесперебойную работу. В дополнение к этому K8s имеет важную функциональность, связанную с масштабированием (scaling), благодаря чему мы автоматически получаем оптимальное количество ресурсов, основываясь на нагрузке и установленных ограничениях.

По правде говоря, ручное разворачивание Kubernetes с нуля является совсем нетривиальной задачей. Я оставлю ссылку на известное практическое руководство «Kubernetes The Hard Way», и, если вам интересно, вы можете попрактиковаться. Но, к счастью, существуют альтернативные способы и инструменты. Самым легкий из них – использовать Google Kubernetes Engine (GKE) в GCP, что позволит получить готовый кластер после нескольких кликов. Для начала изучения я рекомендую использовать именно этот подход, так как он позволит вам сфокусироваться на изучении того, как использовать K8s для своих задач вместо исследования того, как внутренние компоненты должны быть между собой интегрированы. 

Ценность для инфраструктуры автоматизации


Рассмотрим несколько значимых функций, которые предоставляет K8s:
  • развертывании приложения: использование multi-nodes кластера, вместо VMs;
  • динамическое масштабирование: уменьшает расходы на ресурсы, которые используются только по требованию;
  • самовосстановление (Self-healing): автоматическое восстановление pods (в результате чего восстанавливаются и контейнеры);
  • выкатка обновления и откатов изменений без простоя: обновление инструментов, браузеров и эмуляторов не прерывает работу текущих пользователей

Но K8s – все еще не серебряная пуля. Для понимания всех преимуществ и ограничений в контексте рассматриваемых нами инструментов (Selenium grid, Selenoid) кратко обсудим строение K8s. Cluster содержит два типа Nodes: Master Nodes и Workers Nodes. Master Nodes отвечают за управление, развертывание и scheduling decisions. Workers nodes – это то, где приложения запущены. Nodes также содержат среду запуска контейнеров. В нашем случае это Docker, который отвечает за операции, связанные с контейнерами. Но есть и альтернативные решения, например containerd. Важно понимать, что масштабирование или самовосстановление не относится к контейнерам напрямую. Это реализуется через добавление/уменьшение числа pods, которые в свою очередь содержат контейнеры (обычно один container на pod, но в зависимости от задачи может быть и больше). Высокоуровневая иерархия представляет собой worker nodes, внутри которых находятся pods, внутри которых подняты контейнеры.

Функция масштабирования является ключевой и может быть применена как к nodes внутри cluster node-pool, так и к pods внутри node. Существует 2 типа масштабирования, которые относятся как к nodes, так и pods. Первый тип – горизонтальный –  масштабирование происходит за счет увеличения числа nodes/pods. Такой тип является более предпочтительным. Второй тип, соответственно, вертикальный. Масштабирование осуществляется за счет увеличения размеров nodes/pods, а не их количества.

Теперь рассмотрим наши инструменты в контексте вышеупомянутых терминов.

Selenium grid

Как было упомянуто ранее, Selenium grid – очень популярный инструмент, и не сюрприз, что он был контейнеризирован (containerised). Следовательно, не вызывает удивление, что Selenium grid можно развернуть в K8s. Пример того, как это сделать, можно найти в официальном K8s-репозитории. Как обычно, прикладываю ссылки в конце секции. В дополнение к этому в практическом руководстве показано, как это сделать черед Terraform. Также есть инструкция, как масштабировать число pods, которые содержат контейнеры с браузерами. Но функция автоматического масштабирования в контексте K8s – все еще не до конца очевидная задача. Когда я начинал изучение, я не нашел никакого практического руководства или рекомендаций. После нескольких исследований и экспериментов при поддержке DevOps-команды мы выбрали подход поднятия контейнеров с нужными браузерами внутри одного pod, который находится внутри одного worker node. Такой способ позволяет нам применить стратегию горизонтального масштабирования nodes за счет увеличения их числа. Я надеюсь, что в будущем ситуация изменится, и мы увидим все больше и больше описаний лучших подходов и готовых решений, особенно после выпуска Selenium grid 4 с измененной внутренней архитектурой.

Selenoid:

В настоящее время развертывание Selenoid в K8s является самым большим разочарованием. Они не совместимы. Теоретически мы можем поднять Selenoid-контейнер внутри pod, но когда Selenoid начнет запускать контейнеры с браузерами, они все еще будут находиться внутри этого же pod. Это делает scaling невозможным и, как результат, работа Selenoid внутри кластера не будет отличаться от работы внутри виртуальной машины. Конец истории.

Moon:

Зная это узкое место при работе с Selenoid, разработчики выпустили более мощный инструмент, который назвали Moon. Этот инструмент был изначально задуман для работы с Kubernetes и, как результат, можно и нужно использовать функцию автомасштабирования. Более того, я бы сказал, что в настоящий момент это единственный инструмент в мире Selenium, который из коробки имеет native K8s cluster-поддержку (уже нет, см. следующий инструмент ). Ключевой особенностью Moon, которая обеспечивает данную поддержку, является: 

Completely stateless. Selenoid stores in memory information about currently running browser sessions. If for some reason its process crashes — then all running sessions are lost. Moon contrarily has no internal state and can be replicated across data centers. Browser sessions remain alive even if one or more replicas go down.
Итак, Moon шикарное решение, но с одной проблемой, он не бесплатный. Цена зависит от числа сессий. Бесплатно можно запустить только 0-4 сессии, что не особо полезно. Но, начиная уже с пятой сессии, придется заплатить по 5$ за каждую. Ситуация может отличаться от компании к компании, но в нашем случае использование Moon бессмысленно. Как я описывал выше, мы можем запустить VMs с Selenium Grid по требованию или увеличить число Nodes в кластере. Приблизительно на один pipeline мы запускаем 500 браузеров и останавливаем все ресурсы после завершения тестов. Если бы мы использовали Moon, нам бы пришлось заплатить дополнительных 500 x 5 = 2500 $ в месяц и не важно, как часто мы запускаем тесты. И опять же, я не говорю «не используйте Moon». Для ваших задач это может быть незаменимым решением, например, если у вас в  организации много проектов/команд и вам нужен огромный общий кластер для всех. Как всегда, я оставляю ссылку в конце и рекомендую сделать все необходимые расчеты в контексте вашей задачи.

Callisto: (Внимание! Этого нет в оригинальной статье и содержится только в русском переводе)

Как я и сказал, Selenium – очень популярный инструмент, а сфера IT развивается очень быстро. Пока я работал над переводом, в сети появился новый многообещающий инструмент Callisto (привет Cypress и другим убийцам Selenium). Он работает нативно с K8s и позволяет запускать Selenoid-контейнеры в pods, распределено по Nodes. Все работает сразу из коробки, включая автомасштабирование. Фантастика, но надо тестировать. Мне уже удалось развернуть данный инструмент и поставить несколько экспериментов. Но выводы делать рано, после получения результатов на длинной дистанции, возможно, я сделаю обзор в следующих статьях. Пока оставляю только ссылки для самостоятельных исследований.  

Иллюстрация текущего состояния инфраструктуры

Ссылки для изучения


Аналогичные инструменты


7. Инфраструктура как код (IaC)


Краткое описание технологии


И вот мы подобрались к последнему разделу. Обычно, данная технология и связанные с ней задачи не входят в зону ответсвенности инженеров по автоматизации. И на это есть свои причины. Во-первых, во многих организациях инфраструктурные вопросы находятся под контролем DevOps отдела и команды разработки не особо заботятся о том, благодаря чему работает pipeline и каким образом нужно поддерживать все, что с ним связано. Во-вторых, будем честными, практика «Инфраструктура как код (IaC)» все еще не применяется во многих компаниях. Но определенно это стало популярным трендом и важно стараться быть вовлеченным в связанные с этим процессы, подходы и инструменты. Или, по крайней мере, быть в курсе событий.

Начнем с мотивации использования данного подхода. Мы уже обсудили, что для запуска тестов в GitlabCI нам понадобятся как минимум ресурсы для запуска Gitlab Runner. А для запуска контейнеров с браузерами/эмуляторами нам нужно зарезервировать VM или кластер. Помимо ресурсов для тестирования, нам необходимо значительное число мощностей для поддержания сред разработки, staging, production, что также включает базы данных, автоматические расписания, конфигурации сети, балансировщик нагрузки, права пользователей и так далее. Ключевая проблема заключается в требуемых усилиях для поддержки этого всего. Есть несколько способов того, как мы можем вносить изменения и выкатывать обновления. Например, в контексте GCP мы можем использовать UI-консоль в браузере и выполнять все действия, кликая кнопки. Альтернативным способом может быть использование API-вызовов для взаимодействия с облачными сущностями или применение  утилиты командной сроки gcloud для выполнения нужных манипуляций. Но при действительно большом количестве различных сущностей и инфраструктурных элементов становится тяжело или даже невозможно выполнять все операции вручную. Более того, все эти ручные действия неконтролируемые. Мы не можем отправить их на review перед выполнением, использовать систему контроля версий и быстро откатить правки, которые привели к инциденту. Для решения таких проблем инженеры создавали и создают автоматические bash/shell-скрипты, что ненамного лучше предыдущих способов, так как их не так уж и легко быстро прочесть, понять, поддерживать и модифицировать в процедурном стиле.

В этой статье и практическом руководстве я использую 2 инструмента, относящихся к практике IaC. Это Terraform и Ansible. Некоторые полагают, что не имеет смысла использовать их одновременно, так как их функционал схож и они взаимозаменяемые. Но дело в том, что изначально перед ними ставятся совершенно разные задачи. И факт того, что эти инструменты должны дополнять друг друга, был подтвержден на совместной презентации разработчиками, представляющими компании HashiCorp и RedHat. Концептуальная разница заключается в том, что Terraform – это provisioning-инструмент для управления самими серверами. В то время как Ansible является инструментом управления конфигурациями, задачей которого является установка, настройка и управление софтом на этих серверах.

Еще одной ключевой отличительной особенностью данных инструментов является стиль написания кода. В отличие от bash и Ansible, Terraform используют декларативный стиль, основанный на описании желаемого конечного состояния, которого необходимо достичь в результате выполнения. Например, если мы собираемся создать 10 VMs и применить изменения через Terraform, то мы получим 10 VMs. Если применить скрипт еще раз, ничего не произойдет, так как у нас уже есть 10 VMs, и Terraform знает об этом, поскольку он хранит текущее состояние инфраструктуры в state-файле. А вот Ansible использует процедурный подход и, если попросить его создать 10 VMs, то на первом запуске мы получим 10 VMs, аналогично с Terraform. Но после повторного запуска у нас уже будет 20 VMs. В этом и заключается важное отличие. В процедурном стиле мы не храним текущее состояние и просто описываем последовательность шагов, которые должны быть выполнены. Разумеется, мы можем обработать различные ситуации, добавить несколько проверок на существование ресурсов и текущее состояние, но нет смысла тратить наше время и прикладывать усилия на контролирование данной логики. К тому же это увеличивает риск совершить ошибки. 

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

Ценность для инфраструктуры автоматизации


Здесь важно понимать лишь то, что инфраструктура автоматизации тестирования должна рассматриваться как часть всей инфраструктуры компании. А это значит, что все IaC-практики должны  быть применены глобально к ресурсам всей организации. Кто за это ответственен, зависит от ваших процессов. DevOps-команда более опытна в данных вопросах, они видят всю картину происходящего. Однако QA-инженеры сильнее вовлечены в процесс построения автоматизации и структуру pipeline, что позволяет им лучше видеть все требуемые изменения и возможности для улучшения. Самый лучший вариант – это работать сообща, обмениваться знаниями и идеями для достижения ожидаемого результата. 

Приведу несколько примеров использования Terraform и Ansible в контексте автоматизации тестирования и инструментов, которые мы обсуждали до этого:

1. Описать через Terraform необходимые характеристики и параметры VMs и кластеров.

2. Установить с помощью Ansible необходимые для тестирования инструменты: docker, Selenoid, Selenium Grid и загрузить нужные версии браузеров/эмуляторов.

3. Описать через Terraform характеристики VM, в которой будет запущен GitLab Runner.

4. Установить с помощью Ansible GitLab Runner и необходимые сопутствующие инструменты, задать настройки и конфигурации.

Иллюстрация текущего состояния инфраструктуры



Ссылки для изучения:


Аналогичные инструменты


Подведем итоги!


Mind map диаграммы: эволюция инфраструктуры


step1: Local

step2: VCS

step3: Containerisation 

step4: CI/CD 

step5: Cloud Platforms

step6: Orchestration

step7: IaC

Что дальше?


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

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

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

С моей стороны

Из заголовка видно, что это была только первая часть. Несмотря на то, что она получилось довольно большой, здесь все еще не раскрыты важные темы. Во второй части я планирую рассмотреть инфраструктуру автоматизации в контексте IOS. Из-за ограничений Apple, связанных с запусками IOS симуляторов только на macOS системах, наш набор решений сужен. Например, мы лишены возможности использовать Docker для запуска симулятора или публичных облаков для запуска виртуальных машин. Но это не означает, что нет других альтернатив. Я постараюсь держать вас в курсе передовых решений и современных инструментов!

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

И напоследок. В будущем я планирую выпустить видео курс по построению тестовой инфраструктуры и популярным инструментам. В настоящее время в интернете довольно много курсов и лекций по DevOps, но все материалы представлены в контексте разработки, но не автоматизации тестирования. В этом вопросе мне очень нужна обратная связь, будет ли такой курс интересен и ценен для сообщества тестировщиков и автоматизаторов. Заранее спасибо!

Let's block ads! (Why?)