Русский
Русский
English
Статистика
Реклама

Блог компании агентство agima

Большие проблемы из-за маленьких программ. Почему вредоносные боты серьезная угроза для веб-сервиса?

10.06.2021 16:07:13 | Автор: admin

Привет! На связи снова AGIMA, и сегодня мы расскажем о ботах. А точнее о потенциально опасных ботах, способных нанести серьезный ущерб вашему веб-сервису. Эта проблема остается актуальной уже несколько лет, но, к сожалению, многие до сих пор не до конца осознают ее серьезность. Какими же бывают вредные боты, как они действуют и почему защита сервиса от них по-настоящему важная задача? Давайте разберемся.

Типы вредоносных ботов

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

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

  • Спам-боты могут автоматически оставлять в обсуждениях негативные комментарии или вредоносные ссылки на якобы полезные ресурсы. Некоторые из таких программ умеют регистрироваться на ресурсе, если это необходимо для написания комментариев. Негативные и оскорбительные комментарии повышают репутационные риски. Стоит вспомнить об опыте бьюти-бренда Z Palette, который мог стать культовым, но не успел его буквально похоронил один-единственный провокационный комментарий. Или историю одного из гигантов рынка цифровой техники Dell. В далеком 2005 году этот знаменитый бренд едва не был нокаутирован всего одним негативным отзывом.

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

  • Продвинутые боты могут имитировать действия настоящего пользователя. Их используют для взлома учетных записей, кардинга, мошенничества с покупками и других серьезных атак. Кстати, DDoS тоже реализуется с помощью ботов, которые распределены территориально и одновременно задействуются в момент атаки. К счастью, сейчас есть много сервисов для защиты от атак этого типа. Вот один из них: https://qrator.net/ru. Сервис обеспечивает эффективную защиту в автоматическом режиме, с максимальной скоростью реакции на каждую угрозу. При этом количество ложных срабатываний защиты не превышает 5%.

Выстраиваем защиту от ботов. Что сделать в первую очередь?

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

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

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

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

Как сократить активность ботов на сайте?

  • Один из самых простых и популярных инструментов Captcha. Она добавляет некую задачу, чаще всего графическую, которую пользователь должен решить при заполнении формы регистрации, входа, обратной связи и др. Это защита от брутфорса, спам-ботов и ботов авторегистрации. Но некоторые вредоносные программы умеют обходить ее. Ключевой барьер для использования Captcha ее неудобство для легитимных пользователей ресурса. Людей часто напрягает эта бессмысленная процедура, особенно если определить ответ не удается с первого раза и приходится тратить время на повторные попытки. Это может немного снизить конверсию. Решение использовать или не использовать этот инструмент зависит от оценки рисков и аналитики: если видно, что пользователи часто бросают попытки заполнить форму из-за капчи, стоит подумать над упрощением механизма в этом месте. Кстати, вот статья с интересным кейсом использования капчи (точнее, антикапчи).

  • reCAPTCHA v3 сервис, созданный для борьбы с ботами, который основывается на агрегации действий пользователей и поведенческом анализе. И в отличие от классической captcha, не видима обычному пользователю. Подгружается она как JS библиотека и отслеживает поведение пользователя, затем информация отправляется в сервисы google для вынесения вердикта.Не смотря на лояльность такой защиты к пользователям ресурса, данный способ защиты может отрицательно повлиять на скорость загрузки страницы и снизить баллы по Google PageSpeed Insight. Ну и ошибки при поведенческом анализе никто не отменял. При целевой атаке на ресурс полагаться только на данный способ защиты не стоит.

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

  • Можно настроить на сайте ловушки для ботов (honeypot). Например, добавить скрытое от пользователя поле. Обычный пользователь видит нужное ему поле для заполнения, а бот видит еще одно в коде и заполняет его. После этого бот блокируется скриптом. Против умных ботов такая защита срабатывает не всегда, но отбросить часть ненужного трафика она точно поможет. Также можно установить таймер на заполнение полей (сообщения, комментарии и т.д.). Боты обычно вносят данные в поля моментально, в отличие от живых людей это помогает идентифицировать их и блокировать их дальнейшие действия. С ловушками можно экспериментировать и адаптировать их к своему сервису. Этот способ борьбы с ботами даст возможность снизить их активность на ресурсе, но, увы, не избавит вас от них: если кто-то будет целенаправленно атаковать именно ваш сервис, он сможет просто проанализировать вашу форму и алгоритмы и внести изменения в действия своего бота.

  • Также существуют специализированные решения от таких производителей как Akamai, Imperva, Radware, PeremeterX и др., которые применяют подходы на основе анализа данных. Машинное обучение направлено на поведенческий анализ для обнаружения аномалий в трафике веб-сервиса. Анализируя трафик огромного количества интернет-ресурсов, такие инструменты оценивают каждый запрос и определяют вероятность того, что он поступает от бота. При этом учитывается структура защищаемого приложения. Со временем система адаптируется к каждому веб-сервису, и вероятность ложных срабатываний (отклонений запросов настоящих пользователей) минимизируется. Легитимный трафик отделяют от вредоносного в том числе и на основе отпечатка, состоящего из IP адреса, геолокации, браузеров и ряда других идентификаторов. Некоторые решения весьма эффективные, по мнению аналитиков Gartner умеют тестировать структуру сервиса на проникновение ботов, показывая все области, где присутствуют риски для сайта (и, соответственно, бизнеса). Такую защиту уже действительно сложно обойти, но все же возможно если долго и упорно в ней разбираться. Ключевой минус подобных решений высокая цена.

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

А теперь небольшой инсайт. Мы в AGIMA создаем десятки веб-сервисов в год. И каждому второму проекту, который мы разрабатываем, нужна настройка обороны от вредоносных программ. В нашей практике встречались случаи, когда после внедрения определенных мер количество нежелательного трафика на сервис сокращалась на 20-40%, а нагрузка на серверные платформы и бэкенд на 15-30%. Это достаточно ощутимый результат для высоконагруженных веб-сервисов.

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

Затягивать с поиском решения не стоит, промедления могут обернуться существенными потерями. Вот пример: по данным Financial Times, на сегодняшний день из-за активности ботов ведущие стриминговые сервисы (Spotify, Amazon, Universal Music Group, Warner Music и др.) теряют в общей сложности около 300 миллионов долларов в год! Кто знает, возможно, своевременный анализ рисков помог бы этого избежать. Так что угрозу вредоносных ботов никак нельзя игнорировать. Но не стоит и паниковать варианты защиты найдутся в любом случае!

Статья написана совместно с Алексеем Клиновым, Ex-Head of Information Security, AGIMA.

Подробнее..

Kubernetes мониторинг c помощью Prometheus

29.10.2020 14:07:14 | Автор: admin


В этои статье я постарался показать, как можно использовать Prometheus в качестве системы мониторинга для микросервиснои архитектуры. Подробно рассмотрел архитектуру Prometheus и взаимодеиствие его компонентов. Обозначил ключевые характеристики благодаря чему эта система получила такое широкое распространение в средах использующих контеинеризацию. Предупреждаю сразу: статья получилась довольно объемной. Эта статься будет полезна для начинающих DevOps специалистов, которые планируют или уже используют в своеи работе Docker, Kubernetes. Итак, начнем!



Что такое Prometheus?


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


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


Цель настроить в Prometheus мониторинг для Redis-кластера в Kubernetes. Графический интерфейс Grafana. Для оповещения задействуем email и Slack.


Основные компоненты Prometheus


"

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


  • Time Series Data Base (TSDB) база данных, в которой хранятся метрики, полученные от целевых объектов. Например, CPU usage, Memory utilization или количество запросов к сервису

  • Retrieval worker отвечает за получение этих метрик с целевых ресурсов и размещение данных в TSDB

  • HTTP server API для выполнения запросов к сохраненным в TSDB данным. Используется для отображения данных на дашборде в Prometheus или сторонних системах визуализации таких, как Grafana.

От теории к практике


Давайте развернем наш сервер Prometheus с помощью пакетного менеджера Helm.



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

Для этого потребуются рабочий кластер Kubernetes и настроенный kubectl. Можно попробовать использовать кластер Kubernetes в MCS. Сервер Prometheus устанавливается достаточно просто:


#Добавляем репозиторийhelm repo add stable https://kubernetes-charts.storage.googleapis.com#Обновляемhelm repo update#Создаем namespacekubectl create namespace monitoring#Устанавливаем helm c именем my-prometheushelm install my-prometheus stable/prometheus -n monitoring

В результате выполнения мы получаем:


The Prometheus PushGateway can be accessed via port 9091 on the following DNS name from within your cluster:my-prometheus-pushgateway.monitoring.svc.cluster.localGet the PushGateway URL by running these commands in the same shell:  export POD_NAME=$(kubectl get pods --namespace monitoring -l "app=prometheus,component=pushgateway" -o jsonpath="{.items[0].metadata.name}")  kubectl --namespace monitoring port-forward $POD_NAME 9091For more information on running Prometheus, visit:https://prometheus.io/

Проверяем, все ли поды запущены:


kubectl get pods -n monitoringNAME                                               READY   STATUS    RESTARTS   AGEmy-prometheus-alertmanager-86986878b5-5w2vw        2/2     Running   0          76smy-prometheus-kube-state-metrics-dc694d6d7-xk85n   1/1     Running   0          76smy-prometheus-node-exporter-grgqw                  1/1     Running   0          76smy-prometheus-node-exporter-njksq                  1/1     Running   0          76smy-prometheus-node-exporter-pcmgv                  1/1     Running   0          76smy-prometheus-pushgateway-6694855f-n6xwt           1/1     Running   0          76smy-prometheus-server-77ff45bc6-shrmd               2/2     Running   0          76s

При выводе Helm chart предлагает выполнить port-forward для pushgateway, чтобы получить доступ к интерфейсу Prometheus. Меняем component на server и выполняем:


#получаем название контейнера и сохраняем его в переменную POD_NAMEexport POD_NAME=$(kubectl get pods --namespace monitoring -l "app=prometheus,component=server" -o jsonpath="{.items[0].metadata.name}")#пробрасываем портkubectl --namespace monitoring port-forward $POD_NAME 9090

Теперь открываем http://127.0.0.1:9090/ в браузере и видим интерфейс Prometheus:



Итак, у нас есть сервер Prometheus, развернутый внутри кластера Kubernetes. Теперь давайте развернем Redis, который впоследствии и поставим на мониторинг в Prometheus. Для этого также используем Helm:


#добавляем репозиторийhelm repo add bitnami https://charts.bitnami.com/bitnami#Обновляемhelm repo update#Создаем namespacekubectl create namespace redis#Устанавливаем helm c именем redishelm install redis bitnami/redis -n redis --set cluster.enabled=true --set cluster.slaveCount=2 --set master.persistence.enabled=false --set slave.persistence.enabled=false

Параметры cluster.enabled и cluster.slaveCount определяют, что Redis будет развернут в режиме кластер, и в этом кластере два пода будут работать как slave. В параметрах указываем: не использовать persistent volume для master и slave (persistence.enabled=false). Сейчас это нужно, чтобы продемонстрировать работу Redis. В продакшене будет необходимо сделать настройку persistent volume.

Проверяем, что все поды redis запущены:


kubectl get pod -n redis NAME             READY   STATUS    RESTARTS   AGEredis-master-0   1/1     Running   0          65sredis-slave-0    1/1     Running   0          65sredis-slave-1    1/1     Running   0          28s

Теперь, когда у нас развернуты Prometheus и Redis, нужно настроить их взаимодействие.



Targets и metrics


Prometheus-сервер может мониторить самые разные объекты к примеру, Linux- и Windows-серверы. Это может быть база данных или приложение, которое предоставляет информацию о своем состоянии. Такие объекты в Prometheus называются targets. Каждый объект имеет так называемые единицы мониторинга. Для Linux-сервера это может быть текущая утилизация CPU, использование memory и диска. Для приложения количество ошибок, количество запросов и время их выполнения. Эти единицы называются metrics и хранятся в TSDB.



Exporters


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


 scrape_configs:      job_name: prometheus       static_configs:          targets:            localhost:9090

Указываем серверу Prometheus забирать метрики из конечной точки: localhost:9090/metrics.


Для сервисов, которые не могут самостоятельно предоставлять метрики в формате Prometheus, нужно установить дополнительный компонент exporters. Обычно exporters скрипт или сервис, который получает метрики от цели, конвертирует их формат, который понимает Prometheus, и предоставляет эти данные серверу по пути /metrics. Prometheus имеет большой набор готовых exporters для разных сервисов эти компоненты можно использовать для HAProxy, Linux system, облачных платформ и др.


Redis и exporter



Давайте подключим exporter для нашего Redis-кластера. Сначала потребуется создать файл values.yaml со следующим содержанием:


cluster: enabled: true slaveCount: 2 #### Redis Master parameters##master: persistence:   enabled: false extraFlags:    "--maxmemory 256mb" slave: persistence:   enabled: false extraFlags:    "--maxmemory 256mb"  ## Prometheus Exporter / Metrics##metrics: enabled: true  image:   registry: docker.io   repository: bitnami/redis-exporter   tag: 1.4.0-debian-10-r3   pullPolicy: IfNotPresent  ## Metrics exporter pod Annotation and Labels podAnnotations:   prometheus.io/scrape: "true"   prometheus.io/port: "9121"

Здесь параметры, которые использовались в командной строке для настройки Redis в режиме cluster, перенесены в yaml-файл. Для сбора метрик добавлено подключение redis-exporter.


Обновляем Redis с новыми параметрами:


helm upgrade redis -f redis/values.yaml bitnami/redis -n redis

Проверяем, что поды Redis запущены:


kubectl get pods -n redisredis-master-0   2/2     Running   0          3m40sredis-slave-0    2/2     Running   0          2m4sredis-slave-1    2/2     Running   0          2m16s

Теперь к каждому pod привязан дополнительный контейнер redis-exporter, который предоставляет доступ к метрикам Redis.


Добавлять снятие метрик для развернутых контейнеров redis-exporter в конфигурацию Prometheus не нужно, так как он по умолчанию содержит kubernetes_sd_configs (листинг представлен ниже). За счёт этого динамически подключается сбор метрик для вновь появившихся подов. Подробнее об этом можно прочитать в документации.

   job_name: 'kubernetes-pods'       kubernetes_sd_configs:          role: pod       relabel_configs:          source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]           action: keep           regex: true          source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]           action: replace           target_label: __metrics_path__           regex: (.+)          source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]           action: replace           regex: ([^:]+)(?::\d+)?;(\d+)           replacement: $1:$2           target_label: __address__          action: labelmap           regex: __meta_kubernetes_pod_label_(.+)          source_labels: [__meta_kubernetes_namespace]           action: replace           target_label: kubernetes_namespace          source_labels: [__meta_kubernetes_pod_name]           action: replace           target_label: kubernetes_pod_name


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


"

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


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


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


Alertmanager и Alerting rules


За отправку предупреждений в Prometheus отвечает компонент AlertManager. В качестве каналов оповещения могут выступать: email, slack и другие клиенты. Для настройки оповещения необходимо обновить конфигурацию файла alertmanager.yml.


Создадим файл prometheus/values.yaml со следующим содержимым:


## alertmanager ConfigMap entries##alertmanagerFiles: alertmanager.yml:   global:     slack_api_url: <secret>    route:     receiver: slack-alert     group_by:        redis_group     repeat_interval: 30m     routes:        match:           severity: critical         receiver: slack-alert    receivers:      name: slack-alert       slack_configs:          channel: 'general'           send_resolved: true           color: '{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}'           title: '[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing             | len }}{{ end }}] {{ .CommonAnnotations.summary }}'           text: |-             {{ range .Alerts }}               *Alert:* {{ .Annotations.summary }}  *{{ .Labels.severity | toUpper }}* on {{ .Labels.instance }}               *Description:* {{ .Annotations.description }}               *Details:*               {{ range .Labels.SortedPairs }}  *{{ .Name }}:* `{{ .Value }}`               {{ end }}             {{ end }}

Slack_api_url должен содержать ключ, который можно получить на сайте Slack.


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


Конфигурацию, описанную далее, можно найти в репозитории.


Обновляем my-prometheus:


helm upgrade my-prometheus -f prometheus/values.yaml stable/prometheus -n monitoring

Для того, чтобы получить доступ к интерфейсу Alertmanager, выполним следующее:


export POD_NAME=$(kubectl get pods --namespace monitoring -l "app=prometheus,component=alertmanager" -o jsonpath="{.items[0].metadata.name}")kubectl --namespace monitoring port-forward $POD_NAME 9093

Теперь в интерфейсе Alertmanager можно убедиться, что появилась конфигурация для отправки оповещения в канал Slack http://127.0.0.1:9093/#/status:



Далее нужно создать правила, по которым нотификация будет отправляться в Slack-канал.


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


Добавляем в файл prometheus/values.yaml стандартные правила для сигнализации о проблемах в Redis:


serverFiles:  ## Alerts configuration ## Ref: https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/ alerting_rules.yml:   groups:      name: redis_group       rules:          alert: redis_is_running           expr: redis_up == 0           for: 30s           labels:             severity: critical           annotations:             summary: "Critical: Redis is down on the host {{ $labels.instance }}."             description: "Redis has been down for more than 30 seconds"          alert: redis_memory_usage           expr:  redis_memory_used_bytes / redis_memory_max_bytes * 100 > 40           for: 5m           labels:             severity: warning           annotations:             description: "Warning: Redis high memory(>40%) usage on the host {{ $labels.instance }} for more than 5 minutes"             summary: "Redis memory usage {{ humanize $value}}% of the host memory"          alert: redis_master           expr: redis_connected_clients{instance!~"server1.mydomain.com.+"} > 50           for: 5m           labels:             severity: warning           annotations:             description: "Warning: Redis has many connections on the host {{ $labels.instance }} for more than 5 minutes"             summary: "Redis number of connections {{ $value }}"          alert: redis_rejected_connections           expr: increase(redis_rejected_connections_total[1m]) > 0           for: 30s           labels:             severity: critical           annotations:             description: "Critical: Redis rejected connections on the host {{ $labels.instance }}"             summary: "Redis rejected connections are {{ $value }}"          alert: redis_evicted_keys           expr: increase(redis_evicted_keys_total[1m]) > 0           for: 30s           labels:             severity: critical           annotations:             description: "Critical: Redis evicted keys on the host {{ $labels.instance }}"             summary: "Redis evicted keys are {{ $value }}"

Обновляем my-prometheus:


helm upgrade my-prometheus -f prometheus/values.yaml stable/prometheus -n monitoring

Для проверки работы нотификации в Slack, изменим правило алерта redis_memory_usage:


 expr:  redis_memory_used_bytes / redis_memory_max_bytes * 100 < 40

Снова обновляем my-prometheus:


helm upgrade my-prometheus -f prometheus/values.yaml stable/prometheus -n monitoring

Переходим на страницу http://127.0.0.1:9090/alerts:



Redis_memory_usage перешел в статус pending. Значение выражения для всех трех подов чуть больше 0.72. Далее уведомление проходит в Slack:



Теперь добавляем нотификацию по email. При этом конфигурация alermanager.yml изменится так:


alertmanagerFiles: alertmanager.yml:   global:     slack_api_url: <secret>    route:     receiver: slack-alert     group_by:        redis_group     repeat_interval: 30m     routes:        match:           severity: critical         receiver: slack-alert         continue: true        match:           severity: critical         receiver: email-alert    receivers:      name: slack-alert       slack_configs:          channel: 'general'           send_resolved: true           color: '{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}'           title: '[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing             | len }}{{ end }}] {{ .CommonAnnotations.summary }}'           text: |-             {{ range .Alerts }}               *Alert:* {{ .Annotations.summary }}  *{{ .Labels.severity | toUpper }}* on {{ .Labels.instance }}               *Description:* {{ .Annotations.description }}               *Details:*               {{ range .Labels.SortedPairs }}  *{{ .Name }}:* `{{ .Value }}`               {{ end }}             {{ end }}      name: email-alert       email_configs:        to: alert@agima.ru         send_resolved: true         require_tls: false         from: alert@agima.ru         smarthost: smtp.agima.ru:465         auth_username: "alert@agima.ru"         auth_identity: "alert@agima.ru"         auth_password: <secret>

В блок routes добавляем еще один путь для нотификации: receiver: email-alert. Ниже описываем параметры для email. В этом случае при том или ином событии мы получим уведомления одновременно в Slack и на email:




Blackbox exporter


Теперь рассмотрим, как в Prometheus добавляются targets на примере контейнера blackbox exporter, который позволяет организовать мониторинг внешних сервисов по протоколам HTTP(s), DNS, TCP, ICMP.


Для установки blackbox exporter используем Helm:


helm install blackbox-exporter stable/prometheus-blackbox-exporter --namespace monitoring 

Проверяем, что поды blackbox exporter запущены:


kubectl get pods -n monitoring | grep blackboxblackbox-exporter-prometheus-blackbox-exporter-df9f6d679-tvhrp   1/1     Running   0          20s

Добавляем в файл prometheus/values.yaml следующую конфигурацию:


extraScrapeConfigs: |  job_name: 'prometheus-blackbox-exporter'   metrics_path: /probe   params:     module: [http_2xx]   static_configs:      targets:        https://example.org    relabel_configs:      source_labels: [__address__]       target_label: __param_target      source_labels: [__param_target]       target_label: instance      target_label: __address__       replacement: blackbox-exporter-prometheus-blackbox-exporter.monitoring.svc.cluster.local:9115

Указываем, откуда собирать метрики:


blackbox-exporter-prometheus-blackbox-exporter.monitoring.svc.cluster.local:9115/probe. В качестве targets указываем URL сервиса для мониторинга: https://example.org. Для проверки используется модуль http_2xx, который по умолчанию устанавливается в blackbox exporter. Конфигурация проверки:


secretConfig: falseconfig: modules:   http_2xx:     prober: http     timeout: 5s     http:       valid_http_versions: ["HTTP/1.1", "HTTP/2"]       no_follow_redirects: false       preferred_ip_protocol: "ip4"

Обновляем конфигурацию my-prometheus:


helm upgrade my-prometheus -f prometheus/values.yaml stable/prometheus -n monitoring

В интерфейсе Prometheus http://127.0.0.1:9090/targets проверяем, что у нас появилась конечная точка для сбора метрик:



Чтобы расширить область проверки, добавляем http_2xx_check-модуль, который, помимо валидации версии и статуса 200 http, будет проверять наличие заданного текста в теле ответа:


secretConfig: falseconfig: modules:   http_2xx:     prober: http     timeout: 5s     http:       valid_http_versions: ["HTTP/1.1", "HTTP/2"]       no_follow_redirects: false       preferred_ip_protocol: "ip4"       valid_status_codes: [200]   http_2xx_check:     prober: http     timeout: 5s     http:       method: GET       fail_if_body_not_matches_regexp:        "Example Domain"       fail_if_not_ssl: true       preferred_ip_protocol: ip4       valid_http_versions: ["HTTP/1.1", "HTTP/2"]       valid_status_codes: [200]

Обновляем конфигурацию blackbox-exporter/values.yaml:


helm upgrade blackbox-exporter -f blackbox-exporter/values.yaml stable/prometheus-blackbox-exporter --namespace monitoring

Изменяем в файле prometheus/values.yaml модуль http_2xx на http_2xx_check:


extraScrapeConfigs: |  job_name: 'prometheus-blackbox-exporter'   metrics_path: /probe   params:     module: [http_2xx]

Описания проверок, которые можно делать с blackbox exporter, приведены в документации.


Теперь добавим правила для сигнализации в Prometheus в файл prometheus/values.yaml:


   name: http_probe       rules:          alert: example.org_down           expr: probe_success{instance="http://personeltest.ru/aways/example.org",job="prometheus-blackbox-exporter"} == 0           for: 5s           labels:             severity: critical           annotations:             description: '{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 1 minutes.'             summary: 'Instance {{ $labels.instance }} down'

И обновляем конфигурацию my-prometheus:


helm upgrade my-prometheus -f prometheus/values.yaml stable/prometheus -n monitoring

Для проверки можно изменить в blackbox-exporter/values.yaml значение текста для модуля http_2xx_check, который ищется в теле ответа, и обновить blackbox exporter. Должна сработать нотификация в Slack и Email.



Grafana


Настала очередь визуализации. Для отображения графиков будем использовать
Grafana.


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


helm install my-grafana bitnami/grafana -n monitoring --set=persistence.enabled=false

В параметрах указываем не использовать persistent volume (--set=persistence.enabled=false), чтобы только продемонстрировать работу grafana. В продакшн- среде нужно настроить хранилище, так как поды по своей природе эфемерны, и есть риск потерять настройки Grafana.


Должен получиться вот такой вывод:


2. Get the admin credentials:    echo "User: admin"    echo "Password: $(kubectl get secret my-grafana-admin --namespace monitoring -o jsonpath="{.data.GF_SECURITY_ADMIN_PASSWORD}" | base64 --decode)"

Проверяем, что под Grafana запущен:


$kubectl get pods -n monitoring | grep grafanaNAME                                               READY   STATUS    RESTARTS   AGEmy-grafana-67c9776d7-nwbqj                         1/1     Running   0          55s

Перед тем как открыть интерфейс Grafana, нужно получить пароль от пользователя admin, сделать это можно так:


$echo "Password: $(kubectl get secret my-grafana-admin --namespace monitoring -o jsonpath="{.data.GF_SECURITY_ADMIN_PASSWORD}" | base64 --decode)"

Затем пробрасываем порт Grafana:


kubectl port-forward -n monitoring svc/my-grafana 8080:3000

Открываем http://127.0.0.1:9090/ в браузере и авторизуемся:



Для того чтобы Grafana могла получать значения метрик, хранящихся в базе данных Prometheus, необходимо подключить его. Переходим на http://127.0.0.1:8080/datasources и добавляем data source. В качестве TSDB выбираем Prometheus, который доступен в нашем кластере по адресу my-prometheus-server.monitoring.svc.cluster.local.


Должно получиться примерно так:



После добавления data source нужно добавить dashboard в Grafana чтобы состояния показателей Redis-кластера отображались на графиках. Переходим на http://127.0.0.1:8080/dashboard/import и добавляем id = 763. Итог:



После импорта получаем следующий dashboard с виджетами, которые отображают собранные метрики c кластера Redis:



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


Вот, в принципе, и всё. Надеюсь, что сумел рассказать вам что-то новое. А главное убедил, что пользоваться Prometheus просто и удобно!

Подробнее..

Без тимлида не обойтись, а что насчет техлида?

24.05.2021 16:18:49 | Автор: admin

Привет, Хабр!

Меня зовут Ваня Антипин, я Deputy CTO в компании AGIMA. Сегодня я постараюсь вам рассказать про роль техлида в компании. Напомню, что в октябре 2020 года мы говорили о роли тимлида в компании и команде. Если кратко от тимлида зависит многое, включая эффективность команды, достижение поставленных целей, оперативное решение рабочих тасков.

А что насчет техлида? Об этой специальности говорят не так часто, но техлиды важны и нужны не менее тимлидов. Более подробно я рассказываю об этом на курсе Руководитель команды разработки и делюсь реальными кейсами. Но не будем отвлекаться, приступим!

Техлид не специальность, а роль

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

Что касается формальной или номинальной роли, то в классическом Scrum, например, нет роли техлида, а вот в проектах и командах, которые живут по Scrum, техлид есть.

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

А что насчет обязанностей?

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

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

Если проект не очень большой, то случается, что тимлид забирает на себя задачи техлида это те самые hard-скиллс, которые нужны тимлиду наравне с soft-скиллс.

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

Техлиды и тимлиды зоны ответственности

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

Мы сразу провели границу между тимлидами и техлидами. Техлид это упор на Hard-скиллс, а тимлид на Soft-скиллс. Граница в соотношении этих навыков, причем в зависимости от контекста, заданного проектом и командой.

Для того чтобы прояснить ситуацию, приведу пример. Это кросс-платформенные проекты с сервис-ориентированной архитектурой по разработке омниканальных цифровых витрин. В рамках такого проекта разрабатываются web&mobile-приложения, сервисы управления контентом, сервисы интеграции и реализации бизнес-логики (API). В таком проекте может быть целая команда лидов:

  • Тимлид, управляющий процессами, коммуникациями и бюджетом.

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

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

  1. Списком задач в бэклоге управляет руководитель проекта. Задачи по разработке уходят в работу на тимлида.

  2. Тимлид получает задачу, проверяет точность и полноту требований в задаче, при необходимости уточняет детали или дополняет описание задачи описанием уточнением требований или описанием возможной реализации. Задача уходит в работу на техлида. При этом на проекте работает три техлида: фронт, бэк, мобилка; и тимлид понимаем из описания, на кого делегировать задачу.

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

  4. Техлид определял исполнителя и отдаёт задачу в работу.

  5. Задача от разработчика возвращается на код-ревью к техлиду и техлид принимает задачу или отправляет на доработку с комментарием содержащим уточнения и рекомендации.

В таком процессе за техническое качество реализации отвечает техлид, а тимлид за сроки и бюджет.

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

Знания, умения и скиллы поговорим конкретнее

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

Базовый набор Soft-скиллс:

  • Поиск и подбор кандидата, собеседование.

  • Постановка личных целей.

  • Стратегическое видение развития.

  • Отношения с людьми: эмпатия и эмоциональный интеллект.

Базовый набор Hard-скиллс:

  • Знание языков разработки и опыт программирования. Знание сопутствующих и окружающих этот стек технологий.

  • Понимание архитектуры проекта: принципы проектирования архитектуры, паттерны и инструменты.

  • Процессы и инструменты тестирования. Оптимизация тестирования, метрики и мониторинг.

  • Управление инцидентами.

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

  • Текстовые редакторы и интегрированные среды разработки.

  • Инструменты для создания схем в разных графических нотациях и офисные программы.

  • Системы управления задачами и проектом.

  • Системы управления знаниями и документаций.

  • Системы управления версиями кода и инструменты CI/CD.

  • Системы контейнеризации и инструменты DevOps.

  • Системы мониторинга и управления инцидентами.

  • Серверные операционные системы и их сервисы.

  • Скрипты и собственные наработки кода.

Кто может стать техлидом?

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

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

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

Подробнее..

Как сделать экран подтверждения СМС-кода на iOS

03.06.2021 16:16:28 | Автор: admin

Привет, Хабр!

Меня зовут Игорь, я Head of Mobile в компании AGIMA.

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

Важно: в примере кода на github будет полноценный пример с вводом номера телефона и кодом, но экран ввода номера телефона совсем скучный, поэтому сегодня мы вводим код :)

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

  • отправить код на сервер;

  • включить таймер повторной отправки + отобразить визуально;

  • после завершения таймера показать кнопку отправить еще раз;

  • отправить повторный запрос на получение кода;

  • отобразить все ошибки;

  • обработать успешное подтверждение кода.

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

Можно, конечно, отправить всю логику про таймеры и isLoading на View слой, но мне больше нравится относить это к логике. Особенно учитывая то, что я большой поклонник MVVM+Rx (и буду это использовать в статье), это более чем уместно смотрится. Ну да ладно.

ViewModel в этом случае играет роль некоего преобразователя пользовательских действий: у нее есть input и output (видно на картинке выше). За навигацию будет отвечать кто-то еще, например, координатор.

Со стороны UI нам будут интересны следующие компоненты:

final class ConfirmCodeViewController: BaseViewController {  /// поле ввода кода  private lazy var codeTextField = CodeTextField()  /// лейбл для отображения ошибок   private lazy var errorLabel = UILabel()  /// один лоадер для запросов на отправку кода и на повторный запрос кода  private lazy var loader = UIActivityIndicatorView()  /// лейбл с обратным отсчетом для повторной отправки кода  private lazy var timerLabel = UILabel()  /// кнопка повторной отправки кода  private lazy var retryButton = UIButton(type: .system)  /// это все будет в стеквью  private lazy var stackView = UIStackView()}

ViewModel будет выглядеть так:

/// Например, после успешного подтверждения кода нам могут предложить ввести перс. данныеenum AuthResult {case successcase needPersonalData}protocol ConfirmCodeViewModelProtocol {    /// Введенный пользователем код для подтверждения    var code: AnyObserver<String> { get }        /// Пользователь нажал на отправить повторно    var getNewCode: AnyObserver<Void> { get }        /// Результат подтверждения кода    var didAuthorize: Driver<AuthResult> { get }        /// Один индикатор на все запросы на этом экране    var isLoading: Driver<Bool> { get }        /// Ошибки из всех запросов на этом экране    var errors: Driver<String> { get }        /// Таймер отправки нового кода    var newCodeTimer: Driver<Int> { get }        /// Запросили новый код при нажатии на отправить заново    var didRequestNewCode: Driver<Void> { get }      /// Таймер отправки нового кода запущен    var codeTimerIsActive: Driver<Bool> { get }}

Обратите внимание, что при таком подходе мы стараемся не использовать PublishSubject, BehaviourRelay итп, чтобы четко разделить input и output у ViewModel. Теперь давайте это все свяжем.

View отдает следующие потоки данных:

let codeText = codeTextField.rx.text.share()codeText    .bind(to: viewModel.code)    .disposed(by: disposeBag)retryButton.rx.tap    .bind(to: viewModel.getNewCode)    .disposed(by: disposeBag)

ViewModel будет как-то (покажу ниже) обрабатывать ввод кода пользователя, а также делать запрос на повторную отправку кода, если мы нажмем на кнопку.

Сначала давайте посмотрим ViewModel целиком, далее разберем ее более подробно.

ViewModel рассмотрим по кусочкам:

let _codeSubject = PublishSubject<String>()self.code = _codeSubject.asObserver()let codeObservable = _codeSubject.asObservable()let validCodeObservable = codeObservable.filter { $0.count == codeLength }

_codeSubject это поток данных из textfield ввода кода.

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

Выше мы договорились, что PublishSubject не используем, но внутри нам от того же кода нужен не только AnyObserver, но и Observable , чтобы использовать его, например, для отправки кода на сервер. В дальнейшем я планирую использовать такую технику: AnyObserver или Observable в публичном интерфейсе и PublishSubject внутри.

let codeEvents: Observable<Result<Void, Error>> = validCodeObservable    .flatMap { (code) in        authService.confirmCode(code: code, token: token).materialize()    }.share()

Собственно, отправка кода на сервер :) Обращаем внимание на .materialize(). Поскольку мы планируем использовать этот Observable в реактивных цепочках, мы не хотим получить ошибку и прерывать их. materialize позволяет завернуть все значения и ошибки в Result<Value, Error> и тем самым мы никогда не прервем реактивную цепочку из-за ошибки.

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

Состояние загрузки

Здесь довольно интересный момент. Если мы получили валидный код, готовый к отправке, то мы отображаем интерфейс загрузки. Если мы получили ответ от сервера, это означает, что нам надо скрыть состояние загрузки. Таким образом, мы можем взять эти потоки данных (на примерах выше), смаппить их в true или false и забиндить в isLoading.

didAuthorize = codeEvents.elements()...

.elements() работает как фильтр и пропускает только значения из codeEvents и игнорирует ошибки. Напомню, что тип значений у codeEvents это Result<Void, Error> , что является частью RxSwiftExt.

Таймер повторной отправки кода

Таймер включается при следующих событиях:

  • мы отправили код на подтверждение (validCodeObservable.mapTo(Void()));

  • мы перезапросили код (didRequestNewCode);

  • сразу же при заходе на экран (.startWith(Void())).

Именно это описано в строчке Observable.merge... Сам таймер делается стандартными средствами RxSwift. Останавливаем таймер с помощью оператора take(while:), пока значение таймера не станет равно 0.

Лейбл с таймером и кнопка переотправить должны скрываться/показываться в зависимости от того, активен ли таймер:

viewModel.codeTimerIsActive    .drive(retryButton.rx.isHidden)    .disposed(by: disposeBag)        viewModel.codeTimerIsActive    .not()    .drive(timerLabel.rx.isHidden)    .disposed(by: disposeBag)

За ошибки отправки и запроса нового кода у нас будет отвечать один поток данных errors.

errors = codeEvents.errors().merge(with: fetchNewCode.errors())            .compactMap { ($0 as? ErrorType)?.localizedDescription }            .asDriver(onErrorJustReturn: "")

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

viewModel.isLoading    .not()    .drive(codeTextField.rx.isEnabled)    .disposed(by: disposeBag)

ViewModel получилась довольно-таки тестируемая, поэтому давайте напишем тесты! Я приведу примеры тестов, которые будут показывать, как ViewModel реагирует на пользовательский ввод. Создадим вспомогательный метод, который будет создавать поток событий ввода кода. Внимание, используется RxTest!

class ConfirmCodeViewModelTests: XCTestCase {    // properties// methods     //MARK:- Helpers    private func bindCodeInputEvents(        _ events: [Recorded<Event<String>>] = [.next(100, "1"), .next(200, "11"), .next(300, "111"), .next(400, "1111")])    {        codeInputEvents = scheduler.createHotObservable(events)        codeInputEvents.bind(to: viewModel.code).disposed(by: disposeBag)    }}

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

   func test_timerInvokedAutomatically() {        let sut = scheduler.start(created: 0, subscribed: 0, disposed: 1000) { self.viewModel.newCodeTimer }        XCTAssertEqual(sut.events, [.next(1, 2), .next(2, 1), .next(3, 0)])    }

Или вот такой: проверим, что у нас передается на UI событие об ошибках

 func test_errorEmmitedValueAtFailure() throws {        bindCodeInputEvents()        setConfirmCodeResult(.error(0, MockError.confirmFailure))         let sut = scheduler.start { self.viewModel.errors }        XCTAssertEqual(sut.events, [.next(400, "confirmFailure")])    }

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

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

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru