Files
DoGaMa-serv/internal/agent/agent_assets.go
T
codex b575b5260d
CI / validate (pull_request) Failing after 8m3s
fix(deploy): start current Palworld template
2026-08-15 21:43:48 +02:00

83 lines
2.5 KiB
Go

package agent
import (
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"os"
"path/filepath"
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/agentwire"
)
func (s *service) prepareAssets(plan agentwire.DeploymentPlan) ([]AssetMount, error) {
approved, err := s.plans.Assets(plan)
if err != nil || len(approved) == 0 {
return nil, err
}
assetRoot := filepath.Join(filepath.Dir(plan.Mounts[0].HostPath), ".dogama", "assets")
assetRoot, err = s.paths.Prepare(assetRoot)
if err != nil {
return nil, errors.New("asset root is not allowed")
}
result := make([]AssetMount, 0, len(approved))
for _, asset := range approved {
digest := sha256.Sum256(asset.Content)
if hex.EncodeToString(digest[:]) != asset.SHA256 {
return nil, errors.New("approved asset integrity check failed")
}
target := filepath.Join(assetRoot, asset.SHA256)
if err := writeImmutableAsset(target, asset.Content); err != nil {
return nil, err
}
result = append(result, AssetMount{HostPath: target, ContainerPath: asset.Destination})
}
return result, nil
}
func writeImmutableAsset(path string, content []byte) error {
if info, err := os.Lstat(path); err == nil {
if !info.Mode().IsRegular() || info.Mode()&os.ModeSymlink != 0 {
return errors.New("approved asset path is not a regular file")
}
existing, err := os.ReadFile(path)
if err != nil {
return errors.New("read approved asset")
}
existingDigest, wantedDigest := sha256.Sum256(existing), sha256.Sum256(content)
if existingDigest != wantedDigest {
return errors.New("approved asset content conflict")
}
return os.Chmod(path, 0o555)
} else if !errors.Is(err, os.ErrNotExist) {
return errors.New("inspect approved asset path")
}
temporary, err := os.CreateTemp(filepath.Dir(path), ".asset-*")
if err != nil {
return fmt.Errorf("create approved asset: %w", err)
}
temporaryPath := temporary.Name()
defer func() { _ = os.Remove(temporaryPath) }()
// Template entrypoint assets are bind-mounted into images that can run as a
// non-root image user. The host-side agent owns these files, so the image
// user must be able to read and execute an approved helper without gaining
// write access.
if err = temporary.Chmod(0o555); err == nil {
_, err = temporary.Write(content)
}
if err == nil {
err = temporary.Sync()
}
if closeErr := temporary.Close(); err == nil {
err = closeErr
}
if err != nil {
return errors.New("write approved asset")
}
if err := os.Rename(temporaryPath, path); err != nil {
return errors.New("publish approved asset")
}
return nil
}