Harden SDK docker runtime cache
This commit is contained in:
parent
cbe1c419e1
commit
820323abd5
2 changed files with 56 additions and 7 deletions
|
|
@ -2,6 +2,10 @@ package generators
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
stdio "io"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -20,6 +24,8 @@ var (
|
|||
|
||||
var availabilityProbeTimeout = 2 * time.Second
|
||||
|
||||
const dockerRuntimeFingerprintBytes = 4 * 1024
|
||||
|
||||
func dockerRuntimeAvailable() bool {
|
||||
ctx, cancel := availabilityProbeContext()
|
||||
defer cancel()
|
||||
|
|
@ -50,6 +56,9 @@ func dockerRuntimeAvailableWithContext(ctx context.Context) bool {
|
|||
if err != nil && ctx.Err() != nil {
|
||||
return false
|
||||
}
|
||||
if ctx.Err() != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
available := err == nil
|
||||
storeDockerRuntimeAvailability(dockerCommand, commandState, available)
|
||||
|
|
@ -84,9 +93,22 @@ func dockerRuntimeCommandState(command string) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
|
||||
file, err := ax.Open(command)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
hasher := sha256.New()
|
||||
if _, err := stdio.CopyN(hasher, file, dockerRuntimeFingerprintBytes); err != nil && !errors.Is(err, stdio.EOF) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return command + "|" +
|
||||
strconv.FormatInt(info.Size(), 10) + "|" +
|
||||
strconv.FormatInt(info.ModTime().UnixNano(), 10), nil
|
||||
strconv.FormatInt(info.ModTime().UnixNano(), 10) + "|" +
|
||||
info.Mode().String() + "|" +
|
||||
hex.EncodeToString(hasher.Sum(nil)), nil
|
||||
}
|
||||
|
||||
func cachedDockerRuntimeAvailability(command, state string) (bool, bool) {
|
||||
|
|
@ -111,3 +133,13 @@ func storeDockerRuntimeAvailability(command, state string, available bool) {
|
|||
dockerRuntimeOK = available
|
||||
dockerRuntimeChecked = true
|
||||
}
|
||||
|
||||
func resetDockerRuntimeAvailabilityCache() {
|
||||
dockerRuntimeMu.Lock()
|
||||
defer dockerRuntimeMu.Unlock()
|
||||
|
||||
dockerRuntimeChecked = false
|
||||
dockerRuntimeOK = false
|
||||
dockerRuntimeCommand = ""
|
||||
dockerRuntimeState = ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ package generators
|
|||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -13,11 +13,7 @@ import (
|
|||
)
|
||||
|
||||
func resetDockerRuntimeState() {
|
||||
dockerRuntimeMu = sync.Mutex{}
|
||||
dockerRuntimeChecked = false
|
||||
dockerRuntimeOK = false
|
||||
dockerRuntimeCommand = ""
|
||||
dockerRuntimeState = ""
|
||||
resetDockerRuntimeAvailabilityCache()
|
||||
}
|
||||
|
||||
func setAvailabilityProbeTimeout(t *testing.T, timeout time.Duration) {
|
||||
|
|
@ -197,3 +193,24 @@ func TestSDK_DockerRuntimeAvailabilityInvalidatesCachedSuccessWhenCommandMutates
|
|||
|
||||
assert.False(t, dockerRuntimeAvailable())
|
||||
}
|
||||
|
||||
func TestSDK_DockerRuntimeAvailabilityInvalidatesCachedSuccessWhenCommandKeepsSizeAndMTime_Good(t *testing.T) {
|
||||
resetDockerRuntimeState()
|
||||
t.Cleanup(resetDockerRuntimeState)
|
||||
|
||||
dockerDir := t.TempDir()
|
||||
successScript := "#!/bin/sh\nif [ \"$1\" = \"--help\" ]; then\n exit 0\nfi\nexit 0\n"
|
||||
failureScript := "#!/bin/sh\nif [ \"$1\" = \"--help\" ]; then\n exit 1\nfi\nexit 0\n"
|
||||
dockerPath := writeFakeDockerRuntime(t, dockerDir, successScript)
|
||||
t.Setenv("PATH", dockerDir)
|
||||
|
||||
assert.True(t, dockerRuntimeAvailable())
|
||||
|
||||
info, err := os.Stat(dockerPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, ax.WriteFile(dockerPath, []byte(failureScript), 0o755))
|
||||
require.NoError(t, os.Chtimes(dockerPath, info.ModTime(), info.ModTime()))
|
||||
|
||||
assert.False(t, dockerRuntimeAvailable())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue