Shaman: handle duplicate symlinks gracefully
Instead of erroring out when a symlink already exists, investigate it. If the linked file is the one that's intended, just use it. For some reason, BAT and/or the Flamenco add-on include some files twice in the checkout request to Shaman. This is now handled gracefully.
This commit is contained in:
parent
7102a3def8
commit
44ccc6c3ca
@ -200,26 +200,47 @@ func (m *Manager) SymlinkToCheckout(blobPath, checkoutPath, symlinkRelativePath
|
|||||||
// This is expected to fail sometimes, because we don't create parent directories yet.
|
// This is expected to fail sometimes, because we don't create parent directories yet.
|
||||||
// We only create those when we get a failure from symlinking.
|
// We only create those when we get a failure from symlinking.
|
||||||
err = os.Symlink(blobPath, symlinkPath)
|
err = os.Symlink(blobPath, symlinkPath)
|
||||||
if err == nil {
|
switch {
|
||||||
|
case err == nil:
|
||||||
|
return nil
|
||||||
|
case errors.Is(err, fs.ErrExist):
|
||||||
|
// The symlink already exists, which is weird. Investigate so we can log a
|
||||||
|
// more detailed warning.
|
||||||
|
linkTarget, readErr := os.Readlink(symlinkPath)
|
||||||
|
if readErr != nil {
|
||||||
|
logger.Error().
|
||||||
|
AnErr("symlinkError", err).
|
||||||
|
AnErr("symlinkReadError", readErr).
|
||||||
|
Msg("shaman: unable to create symlink as it already exists, but also it cannot be read")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
if linkTarget != blobPath {
|
||||||
logger.Error().Err(err).Msg("shaman: unable to create symlink")
|
logger.Error().
|
||||||
|
AnErr("symlinkError", err).
|
||||||
|
Str("alreadyLinkedFrom", linkTarget).
|
||||||
|
Msg("shaman: unable to create simlink, as it already exists and links a different blob")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// The right file is linked, so let's warn about the situation and otherwise ignore it.
|
||||||
|
logger.Warn().
|
||||||
|
AnErr("symlinkError", err).
|
||||||
|
Msg("shaman: symlink unexpectedly already exists, but it is linking the right path so let's just use it")
|
||||||
|
case errors.Is(err, fs.ErrNotExist):
|
||||||
|
// The directory doesn't exist yet.
|
||||||
logger.Debug().Msg("shaman: creating parent directory")
|
logger.Debug().Msg("shaman: creating parent directory")
|
||||||
|
|
||||||
dir := filepath.Dir(symlinkPath)
|
dir := filepath.Dir(symlinkPath)
|
||||||
if err := os.MkdirAll(dir, 0777); err != nil {
|
if err := os.MkdirAll(dir, 0777); err != nil {
|
||||||
logger.Error().Err(err).Msg("shaman: unable to create parent directory")
|
logger.Error().Err(err).Msg("shaman: unable to create parent directory")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Symlink(blobPath, symlinkPath); err != nil {
|
if err := os.Symlink(blobPath, symlinkPath); err != nil {
|
||||||
logger.Error().Err(err).Msg("shaman: unable to create symlink, after creating parent directory")
|
logger.Error().Err(err).Msg("shaman: unable to create symlink, after creating parent directory")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
logger.Error().Err(err).Msg("shaman: unable to create symlink")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Change the modification time of the blob to mark it as 'referenced' just now.
|
// Change the modification time of the blob to mark it as 'referenced' just now.
|
||||||
m.wg.Add(1)
|
m.wg.Add(1)
|
||||||
|
@ -58,6 +58,9 @@ func TestSymlinkToCheckout(t *testing.T) {
|
|||||||
err = manager.SymlinkToCheckout(blobPath, manager.checkoutBasePath, symlinkRelativePath)
|
err = manager.SymlinkToCheckout(blobPath, manager.checkoutBasePath, symlinkRelativePath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = manager.SymlinkToCheckout(blobPath, manager.checkoutBasePath, symlinkRelativePath)
|
||||||
|
assert.NoError(t, err, "symlinking a file twice should not be an issue")
|
||||||
|
|
||||||
// Wait for touch() calls to be done.
|
// Wait for touch() calls to be done.
|
||||||
manager.wg.Wait()
|
manager.wg.Wait()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user