// desktop only

Презентации лучше смотреть с десктопа

Слайды рассчитаны на широкий экран, клавиатуру и формат 16:9. Откройте эту страницу на ноутбуке или компьютере.

Вернуться на сайт
// PHD2 · Positive Hack Days Fest · proto · buf

Как и зачем
мы делаем свой buf

Эдгар Сипки//OZON FinTech

Василий Близнецов//Positive Technologies AppSec

// speakers.bio

Кто мы

01

Эдгар Сипки

Senior golang developer, евангелист, проповедник, проктологист.

Тимлид OZON FinTech

02

Василий Близнецов

Senior golang developer.

Positive Technologies AppSec

// agenda

О чём поговорим?

  1. 01Традиционный toolset для proto, и его проблемы
  2. 02buf, что это и зачем оно
  3. 03Проблемы buf, настоящие и придуманные
  4. 04Как нам справиться с бедой
// chapter 01

ТРАДИЦИОННЫЙ
TOOLSET

// grpc.recap

gRPC за 30 секунд

Высокоскоростной RPC

HTTP/2 и асинхронность

Строгая типизация

.proto — общий контракт клиента и сервера

Кодогенерация

Клиенты и сервера для любого языка

Бинарная сериализация

Меньше байт, больше скорость

// proto.toolset

Традиционный toolset

01

Golang

Целевой язык наших сервисов

02

protoc

Канонический компилятор от Google

03

Plugins

protoc-gen-go
protoc-gen-go-grpc

// proto.problems

Проблемы proto tools

  • Технологически не обвязаны практически с моментом выхода
  • Конфигурация в стиле «старой школы» — портянки shell-команд
  • Победа старой школы: конфигурация protoc-плагинов
// problem.01

Неконсистентное описание API

  • Отсутствие жёсткой структуры описания
  • Вариации именования (snake, camel)
  • Легко сломать обратную совместимость
  • → увеличивает когнитивную нагрузку
// problem.01 / service

Зоопарк rpc-методов

service ServiceAPI {
  rpc example_one(google.protobuf.Empty)
    returns (google.protobuf.Empty);

  rpc ExampleSecond(google.protobuf.Empty)
    returns (google.protobuf.Empty);
}
// problem.01 / message

Зоопарк message-ей

message RequestOne {
  string example_one = 1;
}

message requestSecond {
  string ExampleSecond = 1;
}
// problem.01 / enum

Зоопарк enum-ов

enum Example {
  example_one = 0;
  ExampleOne = 1;
}
// problem.02

Работа с зависимостями

  • Отсутствие пакетного менеджера
  • Ручной перенос пакетов в проект
  • Использование git submodules для управления зависимостями
// third_party / README

Реальный пример

## third_party

This directory contains ("vendors") abbreviated copies
of the following repositories:

- google api types:
  https://github.com/googleapis/googleapis
- validate:
  https://github.com/bufbuild/protoc-gen-validate
// problem.03

Breaking change

Нарушение обратной совместимости

  1. Синтаксические изменения в коде
    • Синтаксический вывод
    • А в чём случае невозможен!
// breaking.before

Было

syntax = "proto3";
...
service ServiceAPI {
  rpc Auth(AuthInfo) returns (google.protobuf.Empty);
}

message AuthInfo {
  string username = 1;
  string password = 2;
}
// breaking.after

Стало

message AuthInfo {
  oneof login {
    string username = 1;
    string email    = 2;
  }
  string password = 3;
}

→ клиенты сломаются на следующем релизе

// problem.04

Версионирование плагинов

// pull request
✓ Checks 1·± Files changed 359

обновили плагин на минорную версию — переписался весь сгенерированный код

// chapter 02 / starting

О чём поговорим?

  • 01Традиционный toolset для proto, и его проблемы
  • 02buf, что это и зачем оно
  • 03Проблемы buf, настоящие и придуманные
  • 04Как нам справиться с бедой
// chapter 02

BUF —
ЧТО И ЗАЧЕМ

// buf.features

Buf

lint

находит и помогает исправлять ошибки

breaking checker

проверяет обратную совместимость

generate

генерирует код на основе описания протокола

pkg manager

управляет зависимостями

formatter

форматирует код в соответствии с заданными правилами

// buf.lint

Линтер

  • Инструмент анализа кода
  • Помощь в нахождении и исправлении ошибок
  • Выявление несоответствий стилей кодирования
  • Обнаружение подозрительных конструкций в коде
  • Единообразие!
// lint / input

Линтер: что подаём

syntax = "proto3";
package api;
import 'google/protobuf/empty.proto';

service ServiceAPI {
  rpc Auth(AuthInfo) returns (google.protobuf.Empty);
}

message AuthInfo {
  string username = 1;
  string password = 2;
}
// lint / output

Линтер: что говорит

auth.proto:3:1: Package name "api" should be suffixed
  with a correctly formed version, such as "api.v1".

auth.proto:7:1: Service "ServiceAPI" should have a
  non-empty comment for documentation.

auth.proto:8:3: RPC "Auth" should have a non-empty
  comment for documentation.

auth.proto:8:12: RPC request type "AuthInfo" should
  be named "AuthRequest" or "ServiceAPIAuthRequest".

auth.proto:8:31: RPC response type "Empty" should
  be named "AuthResponse" or "ServiceAPIAuthResponse".
// lint / fix package

Чиним именование

syntax = "proto3";

package api.auth.v1;
// lint / fix docs

Чиним документацию

// AuthAPI — отвечает за работу с сессиями пользователей.
// Все запросы автоматически фиксируют IP и UserAgent
// для отправки их в топик ......
service AuthAPI {
  // Авторизовывает пользователя и возвращает token сессии в metadata.
  // При нескольких неуспешных кейсах с одного IP
  // временно блокирует возможность авторизоваться.
  // В случае отсутствия такого пользователя
  // будет код ошибки bad request, а не not found.
  rpc Auth(AuthRequest) returns (AuthResponse);
}
// lint / fix oneof

Чиним AuthRequest

message AuthRequest {
  // Для авторизации может быть использован как username,
  // так и email. email регистронезависимый,
  // а username — зависимый.
  oneof login {
    string username = 1;
    string email    = 2;
  }
  string password   = 3;
}

message AuthResponse {}
// buf.yaml / lint

Конфигурация линтера

lint:
  use: [
    DIRECTORY_SAME_PACKAGE,
    PACKAGE_DEFINED,
    PACKAGE_DIRECTORY_MATCH,
    PACKAGE_SAME_DIRECTORY,
    ENUM_PASCAL_CASE,
    ENUM_VALUE_UPPER_SNAKE_CASE,
    FIELD_LOWER_SNAKE_CASE,
    MESSAGE_PASCAL_CASE,
    ......
  ]
// buf.breaking

Breaking Change

buf breaking --against '.git#branch=main'

→ или поднимаем версию пакета

syntax = "proto3";

package api.auth.v2;
// buf.yml / deps

Пакетный менеджер

# buf.yml

version: v1
deps:
  - buf.build/googleapis/googleapis
  - buf.build/bufbuild/protovalidate

buf mod update

// pkg / usage

И в коде ничего лишнего

syntax = "proto3";
package api;
import 'google/type/money.proto';
import "validate/validate.proto";

message MyMessage {
  google.type.Money money = 1;
  string token = 2 [(validate.rules).string = {
    min_len: 1,
    max_len: 999
  }];
}
// buf.configs

Конфигурации

buf.work.yml

workspace

buf.yml

правила и зависимости

buf.gen.yml

генерация

// buf.holy-grail

Buf — наш грааль?

  • linter
  • проверки breaking change
  • registry для пакетов
  • generate
// buf.lock

А где коммит?

# Generated by buf. DO NOT EDIT.
version: v1
deps:
  - remote: buf.build
    owner: bufbuild
    repository: protoc-gen-validate
    commit: 53d835564305d73b17e9b122d58e5001b2e93fc6
  - remote: buf.build
    owner: googleapis
    repository: googleapis
    commit: 3dd3023f54b3432dac2ef4f80c8a327d639dcc71
// buf.gen.yaml

Удалённая генерация

version: v1
plugins:
  - plugin: buf.build/protocolbuffers/go:v1.31.0
    out: .
    opt:
      - "paths=source_relative"
  - plugin: buf.build/grpc/go:v1.3.0
    out: .
    opt:
      - "paths=source_relative"
// buf.alpha.image.v1

Под капотом — proto до конца

message GenerateCodeRequest {
  ...
  buf.alpha.image.v1.Image image = 1;
  ...
}
// chapter 03 / starting

О чём поговорим?

  • 01Традиционный toolset для proto, и его проблемы
  • 02buf, что это и зачем оно
  • 03Проблемы buf, настоящие и придуманные
  • 04Как нам справиться с бедой
// chapter 03

ПРОБЛЕМЫ BUF
НАСТОЯЩИЕ И
ПРИДУМАННЫЕ

// blocked

Блокировка в РФ и РБ

  • Нельзя использовать пакетный менеджер
  • Нельзя использовать удалённую генерацию
  • Нельзя использовать registry

→ buf.build не отвечает на наши запросы

// option.01

VPN / Proxy

// плюсы
  • Легко
  • Быстро
  • Заработало сразу
// минусы
  • Нельзя запустить в CI без открытия контура
// option.02

Альтернатива — Prototool

// плюсы
  • Имеет такой же функционал:
    • Линтер
    • Проверка обратной совместимости
    • Компиляция
    • ...
// минусы
  • Он заморожен и его развитие остановлено :(
// uber/prototool / README

Сами авторы

Update: We recommend checking out Buf, which is under active development. There are a ton of docs for getting started, including for migration from Prototool.
// option.03

Велосипед?

// плюсы
  • Можно написать свою тулзу с 0!
  • Возможность адаптировать под требования компании
// минусы
  • Нужно написать свою тулзу с 0!
  • Ресурсы на разработку
  • Ресурсы на тестирование
  • Ещё ресурсы — на внедрение
// option.04 / fork

Исходники buf

buf — open source, поэтому можно посмотреть, как он устроен.

buf/
├── .github/
├── cmd/
├── data/
├── make/
├── private/
├── proto/    # api описан proto-файлами
└── windows/
// option.05

easyp

// плюсы
  • Не придётся переезжать на другое cli
  • Обратно-совместимый сервис
// минусы
  • Конечно же нет
// architecture

Как работает обмен

Client → CLI:                Request
CLI → ResolveService:        Resolve request
ResolveService → CLI:        repository address
CLI → RepositoryService:     Get repository information
RepositoryService → CLI:     repository information
CLI → DownloadService:       Download
DownloadService → CLI:       files
CLI → Client:                Packages
// approach.01

Git Hosting

// плюсы
  • Полный и удобный контроль proto-файлов
// минусы
  • Сложно
  • Долго
  • Нужно завозить к gitlab ещё один git host
// approach.02

Git Proxy

// плюсы
  • Легко
  • Мало кода
  • Не храним данные
// минусы
  • Скорость работы низкая
// approach.03

Git proxy to disk

// плюсы
  • Легко
  • Мало кода
  • Работает быстро
// минусы
  • Сложности с версионированием
  • Неудобства с добавлением новых пакетов
// buf + easyp

buf + easyp?

deps:
  - <your_host>/googleapis/googleapis
  - <your_host>/bufbuild/protoc-gen-validate

→ подменяем remote на свой и работаем как обычно

// pull deps

Как подтянем зависимости?

buf mod update
// chapter 04 / starting

О чём поговорим?

  • 01Традиционный toolset для proto, и его проблемы
  • 02buf, что это и зачем оно
  • 03Проблемы buf, настоящие и придуманные
  • 04Как нам справиться с бедой
// chapter 04

КАК НАМ
СПРАВИТЬСЯ
С БЕДОЙ

Пришло время переезжать без buf?

// pain

Боль с зависимостями

  • блокировка buf
  • buf не умеет работать с корпоративными git хостингами
  • нет пакетных менеджеров вообще?
// google.search

Гуглим...

  • protop! · github.com/protop-io/protop
  • Есть демо!!
  • Умеет в приватные репозитории!!!
  • profit?! profit?!

→ но... последний коммит — 4 года назад, 12 issues, заброшен

// build.our.own

Делаем своё

  • git sdk для Golang (например, gogit)
  • Пилим PoC
  • Иии?
// problems

Проблемы

  • private ключи
  • google apis
  • На этом всё? Но есть же... go mod!
// go.mod

go mod

// плюсы
  • Работает с системными конфигами git'a
  • Есть доступ к репе == может поставить зависимость
  • Переваривает «тяжёлые репы»
// минусы
  • Конечно же нет
// go.mod / under the hood

Смотрим исходники

  • os.Exec("git", ...)
  • git init --bare
  • git fetch --depth=1
  • git ls-tree
  • git archive --format=zip
  • и всё! (ну почти)
// easyp.pkg

easyp package manager

версионирование

buf.work.yaml

вендоринг

// easyp.yaml

Пакетный менеджер

version: v1
deps:
  - github.com/googleapis/googleapis
  - github.com/bufbuild/protovalidate@v0.3.1
  - github.com/grpc-ecosystem/grpc-gateway@v2.19.1

→ любой git host, любая версия, любой тэг

// Makefile / vendor

Подтягиваем зависимости

.PHONY: vendor_proto
vendor_proto:
  EASYPPATH=$(VENDOR_PROTO) \
    $(LOCAL_BIN)/easyp mod -c easyp.yaml
// Makefile / gen

Генерируем код

.PHONY: gen-proto
gen-proto:
  cd ./pkg/api && \
  protoc -I ./../../vendor_proto/mod \
    --plugin=protoc-gen-go=$(LOCAL_BIN)/protoc-gen-go \
    --go_out=. --go_opt=paths=source_relative \
    --plugin=protoc-gen-go-grpc=$(LOCAL_BIN)/protoc-gen-go-grpc \
    --go-grpc_out=. --go-grpc_opt=paths=source_relative \
    --plugin=protoc-gen-goclay=$(LOCAL_BIN)/protoc-gen-goclay \
    own_service/service.proto
// summary

Заключение

  • Сохранили cli функционал от buf
  • Решили проблему пакетного менеджера
  • Создали аналог buf cli для работы с proto
// roadmap

ЧТО
ДАЛЬШЕ?

// roadmap / v0.1 — v0.3

Стартовый забег

v0.1
Package manager and basic lint rules compatible with buf
v0.2
Version rules for caching packages · lock file support
v0.3
Supporting all lint rules from buf · shell auto complete
// roadmap / v0.4 — v0.6

Догоняем buf

v0.4
Generating code from proto files · JetBrains IDE Plugin
v0.5
Add breaking change checker
v0.6
CI/CD support (github actions, gitlab ci) · Docs site
// roadmap / v0.7 — v1.0

Финальный рывок

v0.7
Integration tests
v0.8
Format · Dependency graph
v0.9
Version rules for caching packages · lock file support
v1.0
Stabilization API
// спасибо · вопросы · github

easyp

easyp.tech//github.com/easyp-tech/easyp

Эдгар Сипки//Василий Близнецов