
If Shaman is used to submit the job files, store the job's checkout ID (i.e. the path relative to the checkout root) in the database. This will make it possible in the future to remove the Shaman checkout along with the job itself.
75 lines
2.2 KiB
Go
75 lines
2.2 KiB
Go
package checkout
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"git.blender.org/flamenco/pkg/api"
|
|
"git.blender.org/flamenco/pkg/shaman/filestore"
|
|
"github.com/rs/zerolog"
|
|
)
|
|
|
|
var (
|
|
ErrMissingFiles = errors.New("unknown files requested in checkout")
|
|
|
|
validCheckoutRegexp = regexp.MustCompile(`^[^/?*:;{}\\][^?*:;{}\\]*$`)
|
|
)
|
|
|
|
// Checkout symlinks the requested files into the checkout directory.
|
|
// Returns the actually-used checkout directory, relative to the configured checkout root.
|
|
func (m *Manager) Checkout(ctx context.Context, checkout api.ShamanCheckout) (string, error) {
|
|
logger := (*zerolog.Ctx(ctx))
|
|
logger.Debug().
|
|
Str("checkoutPath", checkout.CheckoutPath).
|
|
Msg("shaman: user requested checkout creation")
|
|
|
|
// Actually create the checkout.
|
|
resolvedCheckoutInfo, err := m.PrepareCheckout(checkout.CheckoutPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
logger = logger.With().Str("checkoutPath", resolvedCheckoutInfo.RelativePath).Logger()
|
|
|
|
// The checkout directory was created, so if anything fails now, it should be erased.
|
|
var checkoutOK bool
|
|
defer func() {
|
|
if !checkoutOK {
|
|
err := m.EraseCheckout(checkout.CheckoutPath)
|
|
if err != nil {
|
|
logger.Error().Err(err).Msg("shaman: error erasing checkout directory")
|
|
}
|
|
}
|
|
}()
|
|
|
|
for _, fileSpec := range checkout.Files {
|
|
blobPath, status := m.fileStore.ResolveFile(fileSpec.Sha, int64(fileSpec.Size), filestore.ResolveStoredOnly)
|
|
if status != filestore.StatusStored {
|
|
// Caller should upload this file before we can create the checkout.
|
|
return "", ErrMissingFiles
|
|
}
|
|
|
|
if err := m.SymlinkToCheckout(blobPath, resolvedCheckoutInfo.absolutePath, fileSpec.Path); err != nil {
|
|
return "", fmt.Errorf("symlinking %q to checkout: %w", fileSpec.Path, err)
|
|
}
|
|
}
|
|
|
|
checkoutOK = true // Prevent the checkout directory from being erased again.
|
|
logger.Info().Msg("shaman: checkout created")
|
|
return resolvedCheckoutInfo.RelativePath, nil
|
|
}
|
|
|
|
func isValidCheckoutPath(checkoutPath string) bool {
|
|
if !validCheckoutRegexp.MatchString(checkoutPath) {
|
|
return false
|
|
}
|
|
if strings.Contains(checkoutPath, "../") || strings.Contains(checkoutPath, "/..") {
|
|
return false
|
|
}
|
|
return true
|
|
}
|