1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
|
package gitaly
import (
"embed"
"fmt"
"io"
"os"
"path/filepath"
"gitlab.com/gitlab-org/gitaly/v16/internal/helper/perm"
)
// buildDir is the directory path where our build target places the built binaries.
const buildDir = "_build/bin"
// packedBinariesFS contains embedded binaries. If you modify the above embeddings, you must also update
// GITALY_PACKED_EXECUTABLES in Makefile and packedBinaries in internal/gitaly/config/config.go.
//
//go:embed _build/bin/gitaly-hooks _build/bin/gitaly-ssh _build/bin/gitaly-git2go _build/bin/gitaly-lfs-smudge
var packedBinariesFS embed.FS
// UnpackAuxiliaryBinaries unpacks the packed auxiliary binaries of Gitaly into destination directory.
//
// Gitaly invoking auxiliary binaries across different releases is a source of backwards compatibility issues.
// The calling protocol may change and cause issues if we don't carefully maintain the compatibility. Major version
// changing the module path also causes problems for gob encoding as it effectively changes the name of every type.
// To avoid having to maintain backwards compatibility between the different Gitaly binaries, we want to pin a given
// gitaly binary to only ever call the auxiliary binaries of the same build. We achieve this by packing the auxiliary
// binaries in the main gitaly binary and unpacking them on start to a temporary directory we can call them from. This
// way updating the gitaly binaries on the disk is atomic and a running gitaly can't call auxiliary binaries from a
// different version.
func UnpackAuxiliaryBinaries(destinationDir string) error {
entries, err := packedBinariesFS.ReadDir(buildDir)
if err != nil {
return fmt.Errorf("list packed binaries: %w", err)
}
for _, entry := range entries {
if err := func() error {
packedPath := filepath.Join(buildDir, entry.Name())
packedFile, err := packedBinariesFS.Open(packedPath)
if err != nil {
return fmt.Errorf("open packed binary %q: %w", packedPath, err)
}
defer func() {
// We already check the error below.
_ = packedFile.Close()
}()
unpackedPath := filepath.Join(destinationDir, entry.Name())
unpackedFile, err := os.OpenFile(unpackedPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, perm.PrivateExecutable)
if err != nil {
return err
}
defer func() {
// We already check the error below.
_ = unpackedFile.Close()
}()
if _, err := io.Copy(unpackedFile, packedFile); err != nil {
return fmt.Errorf("unpack %q: %w", unpackedPath, err)
}
if err := unpackedFile.Close(); err != nil {
return fmt.Errorf("close %q: %w", unpackedPath, err)
}
if err := packedFile.Close(); err != nil {
return fmt.Errorf("close packed file %q: %w", packedPath, err)
}
return nil
}(); err != nil {
return err
}
}
return nil
}
|