Статьи

Встраивание активов в Go

В этой статье я собираюсь показать вам, как встраивать активы в Go. В частности, я проведу вас через процесс, который я использовал для встраивания сценария оболочки в chef-runner, один из моих проектов Go с открытым исходным кодом. Моя цель — дать вам возможность применить эту полезную технику к вашим собственным проектам — используя правильные инструменты для работы и комбинируя их наилучшим образом. Давайте начнем!

Встречайте шеф-повара: самый быстрый способ запустить поваренные книги шеф-повара

Цель Chef -Runner — ускорить разработку и тестирование поваренных книг Chef . Первоначально я разработал инструмент как быструю альтернативу мучительно медленной vagrant provisionкоманде. С тех пор chef-runner развился и теперь может использоваться для быстрого предоставления не только локальных машин Vagrant, но и удаленных хостов, таких как экземпляры EC2.

Одна из вещей, которую может сделать chef-runner, — это установить Chef на компьютер перед его подготовкой. Это позволяет настроить серверы без поддержки, на которых ничего не установлено, кроме базовой операционной системы. Для этой функции chef-runner обычно загружал установщик Omnibus, более известный как install.shскрипт, в локальную папку, прежде чем копировать его на целевой компьютер, где он будет запущен для установки Chef.

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

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

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

шеф-бегун написан на го. Я знал, что в Go можно встроить активы, и это казалось идеальной возможностью познакомиться с инструментарием. Вот как я это сделал, используя комбинацию go-bindataи go generate.

Внедрение Активов, Сделанных Простым с go-bindata

go-bindata преобразует любой текстовый или двоичный файл в исходный код Go, что делает его идеальным инструментом для встраивания данных в программы Go. Вы можете использовать его, например, для встраивания ресурсов, таких как CSS, JavaScript и файлы изображений, в ваше веб-приложение. Результатом будет полностью автономный двоичный файл, который можно развернуть так же легко, как и любую другую программу Go.

Раньше я go-bindataдобавлял установщик Omnibus в качестве актива в пакет omnibus от chef-runner . Для этого все, что мне нужно было сделать, это загрузить скрипт установщика один раз, а затем вызвать go-bindataинструмент командной строки для генерации кода Go из него. Точные шаги заключаются в следующем:

# inside $GOPATH/src/github.com/mlafeldt/chef-runner
$ cd chef/omnibus/ 
$ mkdir assets 
$ curl https://www.chef.io/chef/install.sh > assets/install.sh 
$ go-bindata -pkg omnibus -o assets.go assets/

После этого мне пришлось адаптироваться, chef/omnibus/omnibus.goчтобы использовать ресурс напрямую, а не загружать его. Доступ к данным активов можно получить через Assetфункцию, которая включена в созданный исходный файл assets.go . Получившийся код в итоге выглядел так:

// chef/omnibus/omnibus.go

package omnibus

type Installer struct { 
    ChefVersion string 
    SandboxPath string 
    RootPath string 
    Sudo bool 
}

func (i Installer) writeOmnibusScript() error { 
    script := path.Join(i.SandboxPath, "install.sh") 
    log.Debugf("Writing Omnibus script to %s\n", script) 
    data, err := Asset("assets/install.sh") 
    if err != nil { 
        return err 
    } 
    return ioutil.WriteFile(script, []byte(data), 0644) 
}

Это действительно все, что я должен был сделать в отношении встраивания; Я был удивлен, как мало усилий потребовалось.

Однако есть одна вещь, которая мне не нравится в Assetсгенерированной функции go-bindata: она будет отображаться в Godoc вашего пакета, что может быть немного запутанным, если не сказать больше. Одним из возможных решений является хранение активов в отдельном пакете.

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

Другой связанный проект, go-bindata-assetfs , может использоваться для обслуживания файлов, которые были встроены go-bindataчерез net/httpпакет (путем реализации http.FileSystemинтерфейса). Еще раз, фокус Go на компоновке делает для хороших дополнительных решений.

Go Generate: интегрированная генерация кода

Хотя подобные инструменты go-bindataценны сами по себе, вам все равно необходимо интегрировать их в процесс сборки. Для этого вы можете рассмотреть возможность использования инструмента сборки, такого как Make, чтобы склеить все части вместе. Но разве не было бы замечательно, если бы сам Go предоставил возможность автоматически генерировать исходный код? Ну, это так!

Go 1.4 ввел новую команду, go generateчтобы автоматизировать генерацию исходного кода перед компиляцией. Инструмент работает путем сканирования исходного кода Go на наличие специальных комментариев, которые определяют команды для запуска. Таким образом, вы можете объявить инструкции по сборке в своем коде, сохраняя все вместе в хорошем виде.

Это комментарий, который я добавил к omnibusпакету chef-runner :

// chef/omnibus/omnibus.go

package omnibus

//go:generate go-bindata -pkg $GOPACKAGE -o assets.go assets/

Теперь при запуске go generateон подберет команду и выполнит go-bindataс указанными параметрами ( $GOPACKAGEбудет заменен фактическим именем пакета, то есть «omnibus»). Давайте сгенерируем некоторый исходный код:

$ go generate -x ./chef/omnibus
go-bindata -pkg omnibus -o assets.go assets/

-xФлаг заставляет go generateпечатать команды , как они выполняются. Одна команда, показанная выше, должна выглядеть знакомо.

Как и большинство инструментов Go, вы можете запускать go generate ./...для обработки всех пакетов вашего проекта одновременно. Если ваш проект Go содержит Makefile— как это делают большинство в наши дни — это хорошая идея make generate, как для краткости, так и для использования другими целями Make.

Однако есть одна вещь, о которой вы должны знать при использовании go generate. Инструмент не интегрирован с go get, как можно было бы ожидать. Из-за этого ваш проект будет «становиться доступным», только если вы отметите все источники, созданные go generate. Поэтому я поместил упомянутый файл assets.go под контроль версий.

Для получения более подробной информации go generate, проконсультируйтесь go help generateили, что еще лучше, прочитайте эту статью в блоге Go.

В заключении

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

И последнее, но не менее важное: в одном из моих других проектов с открытым исходным кодом я использовал скрипт Python для генерации карты распределений Go, поддерживаемой Packagecloud . Сделав это перед компиляцией, мне удалось сохранить довольно дорогой вызов API, который в противном случае был бы выполнен во время выполнения. Я рекомендую проверить полный исходный код, если вам интересно увидеть еще один практический пример встраивания ресурсов в Go.

Эта статья первоначально появилась в блоге Codeship Матиасом Лафельдом.