Интеграция Terraform с Backstage для реализации самообслуживания

Источник: Spacelift•

Интеграция Terraform с Backstage для реализации самообслуживания

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

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

Backstage стал одним из самых популярных инструментов с открытым исходным кодом для создания IDP. Распространенным сценарием использования является интеграция Terraform с Backstage для поддержки рабочих процессов самообслуживания инфраструктуры. Пользователи получают интерфейс без написания кода для создания облачной инфраструктуры, в то время как Terraform продолжает управлять ею «под капотом».

В этой статье рассматривается Backstage и способы интеграции с ним Terraform. Обратите внимание, что большая часть материала применима и к OpenTofu, если вы предпочитаете открытую альтернативу Terraform.

Backstage не выполняет код Terraform. Он собирает запрос и передает его выполнение системе, которая умеет это делать. Существует три способа их соединения:

  • Каталог программного обеспечения моделирует стеки, пространства и модули как сущности, чтобы разработчики могли видеть, что существует и кому это принадлежит.
  • Шаблоны программного обеспечения преобразуют форму в сгенерированный код Terraform, пулл-реквест и новую запись в каталоге.
  • Плагины, такие как плагин Spacelift, выводят список стеков и запускают выполнения прямо из портала.

Сохраняйте систему контроля версий в качестве единственного источника достоверных данных. Все вышесказанное относится и к OpenTofu.

Backstage — это фреймворк с открытым исходным кодом для создания IDP, изначально разработанный Spotify и переданный в открытый доступ в 2020 году. Он является инкубационным проектом Cloud Native Computing Foundation (CNCF).

Поскольку Backstage является фреймворком для построения IDP, он не является готовым IDP «из коробки». Он поставляется с инструментами, которые помогают вам построить собственную IDP:

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

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

Предложение Backstage SaaS под названием Spotify Portal for Backstage является отличной альтернативой, если вы хотите сосредоточиться на использовании Backstage для создания IDP без необходимости администрировать само ПО и базовую инфраструктуру.

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

Terraform — подходящий инструмент для подготовки инфраструктуры, но неподходящий интерфейс для пользователя, который ее запрашивает. Backstage ликвидирует этот разрыв. Шаблон программного обеспечения превращает запрос в форму, генерирует код Terraform на основе проверенного шаблона и открывает пулл-реквест. Разработчик получает базу данных, а команда платформы сохраняет контроль над модулем, настройками по умолчанию и ревью.

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

Что вы получаете:

  • Самообслуживание для разработчиков, которые никогда не касаются Terraform
  • Стандартизированные модули вместо дублированного кода
  • Информация о владении и зависимостях в едином каталоге
  • Каждое изменение по-прежнему поступает в виде пулл-реквеста

Одно важное условие определяет все описанное ниже: Backstage не запускает Terraform. Он собирает запрос, рендерит код и передает выполнение в конвейер CI/CD или платформу оркестрации.

Вы можете интегрировать Terraform с Backstage различными способами в зависимости от вашей цели.

В следующих разделах мы объясним, как установить Backstage, настроить интеграцию между Backstage и другими системами (например, GitHub и Spacelift Deploy), визуализировать инфраструктуру Terraform в каталоге программного обеспечения, создавать шаблоны программного обеспечения, использовать плагины Backstage и управлять Backstage с помощью Terraform.

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

Установка и запуск Backstage

Самый простой способ начать работу с Backstage — запустить экземпляр Backstage локально. Это позволит протестировать интеграции перед развертыванием Backstage в продакшене.

В documentation приведены подробные предварительные требования и шаги, необходимые для запуска Backstage в качестве автономной установки на вашей локальной системе. Если кратко, на вашей машине должны быть установлены Docker, Node.js и yarn. Инициализируйте новый проект Backstage с помощью следующей команды:

Ответьте на вопросы для настройки вашего проекта. Затем перейдите в каталог нового проекта и запустите Backstage с помощью следующей команды:

Backstage запускается по адресу http://localhost:3000.

Для продакшн-инсталляций распространенным подходом является запуск Backstage в качестве контейнера в кластере Kubernetes. Подробности см. в documentation.

Как настроить Backstage

Вы настраиваете Backstage с помощью файлов YAML. Точкой входа является файл с именем app-config.yaml в корне проекта. Вы можете настроить локальные переопределения в файле app-config.local.yaml, а также специфичные для окружения переопределения конфигурации в файлах вроде app-config.development.yaml и app-config.production.yaml (их необходимо передавать с помощью флага --config при запуске).

В следующих разделах рассматриваются части конфигурации, которые вносятся в app-config.yaml и сопутствующие конфигурационные файлы. Однако все подробности настройки Backstage выходят за рамки данной статьи.

Как настроить аутентификацию, идентификаторы, команды и права владения

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

При экспериментировании с локальным экземпляром Backstage вы можете использовать встроенный гостевой провайдер аутентификации, который предварительно настроен в файле app-config.yaml:

Компоненты в Backstage принадлежат группам пользователей. Вы можете использовать своего провайдера идентификационных данных для синхронизации пользователей и групп с Backstage и использовать их при настройке прав владения.

Для локального экземпляра Backstage, используемого в этой статье, мы будем применять стафическую конфигурацию групп и пользователей. Группы и пользователи являются примерами сущностей каталога Backstage. Сущности каталога определяются в формате YAML, аналогично манифестам объектов Kubernetes. Мы можем загружать сущности каталога из отдельных файлов, добавляя их в catalog.locations в app-config.yaml:

Группы могут содержать другие группы, и каждая группа имеет атрибут type, который можно использовать для моделирования вашей организации. Атрибут type не имеет определенной схемы; он принимает любое строковое значение. Мы начнем с создания группы для бизнес-подразделения (type: business-unit) с именем engineering в нашем файле organization.yaml:

Эта группа содержит ряд дочерних групп, как видно в свойстве spec.children. Каждая дочерняя группа имеет конфигурацию, аналогичную бизнес-подразделению engineering, но использует type: team, чтобы указать, что это команды пользователей. Мы можем добавлять пользователей в команду с помощью сущности каталога User:

Пользователь по имени Alice является членом team-a, как настроено в свойстве spec.memberOf.

Имея настроенные пользователи и группы, мы можем настроить владение для других типов сущностей каталога, которые мы добавляем. Сокращенный пример того, как назначить владение для platform-team, показан в следующем примере для сущности каталога Component:

На портале Backstage перейдите в каталог и отфильтруйте поле Kind по значению Group, чтобы увидеть ваши различные группы. В этом примере у нас есть следующие группы:

Настройка системы

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

В этом примере мы добавим систему для Spacelift Deploy:

Сущность каталога системы и все последующие сущности, описанные в этой статье, добавляются в один или несколько файлов YAML, которые загружаются в основной файл app-config.yaml, аналогично тому, как мы сделали для файла organization.yaml в предыдущем разделе. Эта конфигурация опускается здесь для краткости.

Визуализация инфраструктуры Terraform с помощью сущностей ресурсов

Мы можем визуализировать инфраструктуру, которой управляем с помощью Terraform, используя сущности ресурсов в нашем каталоге Backstage.

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

Затем мы определяем стек:

После регистрации этих двух сущностей ресурсов программный каталог отображает их:

Когда у вас много ресурсов, вы можете начать фильтрацию по типу, команде, тегам и т. д.

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

Настройка шаблонов программного обеспечения для самообслуживания инфраструктуры

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

В качестве примера мы добавим шаблон программного обеспечения для создания нового модуля Terraform с выделенным репозиторием GitHub и набором стандартизированных файлов модуля Terraform.

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

Аналогично другим сущностям каталога, мы определяем ее метаданные и принадлежность:

Затем мы добавляем свойство параметров, вложенное под свойство spec. Каждый элемент в parameters отображается как страница формы в пользовательском интерфейсе, причем каждый запрашивает ряд свойств, которые пользователь должен указать (обратите внимание, что большая часть кода опущена для краткости; см. документацию о том, как настраивать свойства):

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

Третья страница «Review» добавляется автоматически для всех шаблонов программного обеспечения.

Наконец, добавьте свойство steps в качестве вложенного объекта свойства spec. Свойство steps определяет шаги для обработки пользовательского ввода и получения желаемого результата (в данном случае репозитория GitHub):

Этот рабочий процесс состоит из трех шагов:

  • Рендеринг исходного кода Terraform для модуля. Это делается с помощью каталога skeleton, в котором у нас есть необходимые файлы, написанные на комбинации кода Terraform HCL и языка шаблонов (например, с использованием синтаксиса ${{ values.x }} для подстановки значений из формы).
  • Публикация сгенерированного кода в репозитории GitHub.
  • Регистрация нового модуля Terraform в качестве сущности каталога в Backstage.

Интеграция с GitHub настраивается в app-config.yaml следующим образом:

Эта конфигурация предназначена для github.com с использованием персонального токена доступа. Вы также можете использовать приложение GitHub для аутентификации, а также использовать окружение GitHub Enterprise Cloud (ghe.com), но это требует дополнительной настройки.

Теперь пользователь может зайти в Backstage, нажать Create в меню и выбрать один из доступных шаблонов для самообслуживания:

Нажмите на Choose и заполните запрашиваемую информацию:

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

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

Расширение Backstage с помощью плагинов Terraform и платформ IaC

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

Вот некоторые плагины, связанные с Terraform:

  • Платформа Facets.cloud: визуализация вашего окружения Terraform с платформы facets.cloud в вашем окружении Backstage.
  • HashiCorp Terraform: визуализация статуса воркспейсов HCP Terraform или Terraform Enterprise прямо в Backstage. Обратите внимание, что это не официальный плагин, разработанный IBM/HashiCorp.
  • Spacelift.io: визуализация ваших стеков Spacelift Deploy и запуск запусков прямо из Backstage.

В этом разделе мы сосредоточимся на плагине для Spacelift Deploy.

Установите плагины интерфейса и бэкенда для Spacelift в ваш экземпляр Backstage, выполнив следующие команды из корня проекта:

Добавьте плагин в бэкенд в packages/backend/src/index.ts:

Плагин интерфейса будет зарегистрирован автоматически.

В ваш файл app-config.yaml (или в соответствующий файл app-config.<environment>.yaml) добавьте следующую конфигурацию для плагина Spacelift:

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

  • В своей учетной записи Spacelift Deploy перейдите в Integrations и найдите Backstage.
  • Нажмите Create integration.
  • Присвойте интеграции имя (например, «local-backstage»), выберите пространство, в котором должна быть создана интеграция, и укажите необязательное описание.
  • Нажмите Create.

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

Установите эти значения в качестве локальных переменных среды и запустите Backstage:

В меню слева появился новый пункт «Spacelift». Выбор этого пункта открывает обзорную страницу стеков, доступных на уровне (и ниже уровня), где вы установили интеграцию с Backstage:

Клик по названию стека переводит на его целевую страницу в Spacelift Deploy. Клик по значку справа от стека инициирует новый запуск.

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

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

Как управлять Backstage с помощью Terraform

Официального провайдера Backstage для Terraform от Spotify не существует. Тем не менее, в реестре Terraform доступен сторонний провайдер под названием datolabs-io/backstage.

Чтобы использовать этого провайдера, добавьте его в блок required_providers в вашей конфигурации Terraform и настройте его, указав базовый URL для бэкенда Backstage (не тот же порт, что у фронтенда; по умолчанию используется порт 7007) и заголовок аутентификации:

При локальной работе вы можете настроить статический токен в файле app-config.local.yaml, который будет использоваться для аутентификации провайдера:

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

При интеграции Terraform с Backstage руководствуйтесь следующими рекомендациями:

1. Сделайте систему контроля версий источником истины

Рекомендуется использовать программные шаблоны для генерации кода Terraform и создания пулл-реквестов с изменениями, вместо того чтобы напрямую вызывать лежащие в основе API (например, напрямую вызывать Azure API для создания группы ресурсов).

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

2. Автоматизируйте обновления каталога ПО

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

3. Обеспечьте правильный уровень абстракции в программных шаблонах

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

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

4. Относитесь к Backstage как к продукту

Важная рекомендация — относиться к своей IDP как к продукту. Начните с создания хотя бы одного программного шаблона для поддержки наиболее ценных рабочих процессов. Измеряйте его популярность и постоянно совершенствуйтесь. Со временем увеличивайте количество рабочих процессов.

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

5. Используйте принцип наименьших привилегий для сторонних интеграций

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

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

Backstage собирает запрос. Что-то другое выполняет план, хранит состояние, обеспечивает соблюдение политик и сообщает об отклонениях. В этой статье этим «чем-то» является Spacelift Deploy, поэтому стоит разделять обязанности каждой стороны.

Рассмотренный ранее плагин отвечает за видимость: стеки и их статус внутри портала, с кнопкой запуска (если не задано readOnly: true). Он не проверяет собственные разрешения пользователей. Каждый, кто может открыть страницу, действует как ключ интеграции, который имеет права администратора в выбранном при настройке Пространстве. Пространство задает верхнюю границу, а разрешения в Backstage определяют, кто попадает под нее.

Контроль доступа и политики остаются в Spacelift. Пространства определяют, кто видит те или иные стеки, политики OPA регулируют допустимые действия для запуска, а обнаружение отклонений выявляет ручные изменения. Шаблон Backstage, создающий пулл-реквест для репозитория, подключенного к стеку Spacelift, наследует всё это, и порталу не нужно знать правила.

Для самообслуживания шаблоны Spacelift Templates являются альтернативой написанию действия scaffolder. Шаблон представляет утвержденную инфраструктуру в виде параметризованной формы с привязанными проверками политик, поэтому «золотой путь» определяется один раз в Spacelift, а не дублируется в программных шаблонах.

Spacelift Flows обрабатывает шаги, которые не может выразить программный шаблон. Приложение Backstage в Flows регистрирует любой поток, содержащий блок Backstage Entrypoint, в качестве программного шаблона. Блок entrypoint определяет форму, а следующие за ним блоки выполняют рабочий процесс: ветвление по введенным данным, ожидание подтверждения в Slack, развертывание шаблона Spacelift и отправка сообщения запрашивающему с данными для подключения. Установка приложения требует однократного изменения конфигурации Backstage.

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

Обе интеграции имеют документацию: интеграция Backstage и расширение Backstage с помощью Flows.

В этой статье блога было рассказано о том, как интегрировать Terraform с Backstage.

Backstage — это фреймворк для создания внутренних платформ разработчика (IDP) в вашей организации. Он поставляется с каталогом программного обеспечения, программными шаблонами и TechDocs для документирования.

Вы настраиваете Backstage и сущности в вашей среде Backstage с помощью файлов YAML. В этой статье мы увидели несколько разных сущностей: User, Group, Template, Resource и System. Для других сценариев использования их гораздо больше.

Мы рассмотрели, как создать рабочий процесс самообслуживания с помощью шаблона программного обеспечения. Шаблон состоит из формы и рабочего процесса, обрабатывающего данные, введенные в форме. Рассмотренный нами пример представлял собой шаблон для создания нового модуля Terraform в виде отдельного репозитория GitHub с типичными файлами модуля Terraform.

Мы также описали, как установить плагин Backstage для Spacelift Deploy для визуализации стеков и предоставления пользователям возможности запускать выполнения прямо из Backstage. Наконец, мы вкратце рассмотрели, как управлять Backstage с помощью стороннего провайдера Terraform.

Часто задаваемые вопросы

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

Для чего используется Backstage?

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

  • Может ли Backstage запускать Terraform напрямую? Нет, Backstage сама по себе не выполняет Terraform. Она запускает выполнения через внешние системы, такие как конвейеры CI/CD, Terraform Cloud или такие платформы, как Spacelift, с помощью плагинов или действий scaffolder.

Может ли Backstage запускать Terraform напрямую?

Нет, Backstage сама по себе не выполняет Terraform. Она запускает выполнения через внешние системы, такие как конвейеры CI/CD, Terraform Cloud или такие платформы, как Spacelift, с помощью плагинов или действий scaffolder.

  • Нужен ли мне Terraform Cloud для интеграции Terraform с Backstage? Нет, Terraform Cloud не требуется. Интеграции могут выполняться через GitHub Actions, GitLab CI, хостируемые самостоятельно обработчики или управляемые платформы, такие как Spacelift, каждая из которых имеет свои собственные плагины для Backstage.

Нужен ли мне Terraform Cloud для интеграции Terraform с Backstage?

Нет, Terraform Cloud не требуется. Интеграции могут выполняться через GitHub Actions, GitLab CI, хостируемые самостоятельно обработчики или управляемые платформы, такие как Spacelift, каждая из которых имеет свои собственные плагины для Backstage.

  • Как Backstage обрабатывает состояние Terraform? Backstage не управляет состоянием Terraform. Состояние остается в том бэкенде, который выполняет запуск (S3, Terraform Cloud, Spacelift или другой удаленный бэкенд), в то время как Backstage отображает только результаты выполнения и статус рабочей области.

Как Backstage обрабатывает состояние Terraform?

Backstage не управляет состоянием Terraform. Состояние остается в том бэкенде, который выполняет запуск (S3, Terraform Cloud, Spacelift или другой удаленный бэкенд), в то время как Backstage отображает только результаты выполнения и статус рабочей области.

  • Может ли Backstage работать с OpenTofu? Да. Поскольку Backstage делегирует выполнение внешним обработчикам, любой бэкенд, совместимый с OpenTofu (например, Spacelift), может быть подключен к Backstage с помощью шаблонов scaffolder или плагинов сообщества.

Может ли Backstage работать с OpenTofu?

Да. Поскольку Backstage делегирует выполнение внешним обработчикам, любой бэкенд, совместимый с OpenTofu (например, Spacelift), может быть подключен к Backstage с помощью шаблонов scaffolder или плагинов сообщества.

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

Ещё в разделе «Облака и инфраструктура»

Все →

Ещё от Spacelift