Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions cmd/atelet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,13 +249,13 @@ func (s *AteomHerder) Run(ctx context.Context, req *ateletpb.RunRequest) (*atele
return nil, err
}

// Tell ateom to start the workload. gVisor uses RunscPath; the micro-VM
// runtime uses the full RuntimeAssetPaths set.
// Tell ateom to start the workload. Every backend reads the assets it needs
// from RuntimeAssetPaths (gVisor reads "runsc"; the micro-VM runtime reads its
// cloud-hypervisor/kata-* set).
if _, err := client.RunWorkload(ctx, &ateompb.RunWorkloadRequest{
ActorTemplateNamespace: ns,
ActorTemplateName: tmpl,
ActorId: actorID,
RunscPath: runscPathFor(assetPaths),
RuntimeAssetPaths: assetPaths,
Spec: buildAteomWorkloadSpec(req.GetSpec()),
}); err != nil {
Expand Down Expand Up @@ -335,7 +335,6 @@ func (s *AteomHerder) Checkpoint(ctx context.Context, req *ateletpb.CheckpointRe
ActorTemplateNamespace: ns,
ActorTemplateName: tmpl,
ActorId: actorID,
RunscPath: runscPathFor(assetPaths),
RuntimeAssetPaths: assetPaths,
Spec: buildAteomWorkloadSpec(req.GetSpec()),
})
Expand Down Expand Up @@ -534,7 +533,6 @@ func (s *AteomHerder) Restore(ctx context.Context, req *ateletpb.RestoreRequest)
ActorTemplateNamespace: ns,
ActorTemplateName: tmpl,
ActorId: actorID,
RunscPath: runscPathFor(assetPaths),
RuntimeAssetPaths: assetPaths,
Spec: buildAteomWorkloadSpec(req.GetSpec()),
}); err != nil {
Expand Down
6 changes: 3 additions & 3 deletions cmd/atelet/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ func TestFetchAssetRejectsBadHash(t *testing.T) {
// Invalid (8 chars, not 64) but separator-free, so it resolves to a normal
// filename inside the temp StaticFilesDir.
const badHash = "deadbeef"
if err := os.WriteFile(ateompath.RunSCBinaryPath(badHash), []byte("planted"), 0o755); err != nil {
if err := os.WriteFile(ateompath.CachedAssetPath(badHash), []byte("planted"), 0o755); err != nil {
t.Fatalf("planting cache file: %v", err)
}

Expand Down Expand Up @@ -313,7 +313,7 @@ func TestFetchAssetStreaming(t *testing.T) {
if _, err := s.fetchAsset(context.Background(), assetEntry{URL: url, SHA256: goodHash}); err == nil {
t.Fatal("fetchAsset accepted an over-cap asset")
}
if _, err := os.Stat(ateompath.RunSCBinaryPath(goodHash)); !errors.Is(err, os.ErrNotExist) {
if _, err := os.Stat(ateompath.CachedAssetPath(goodHash)); !errors.Is(err, os.ErrNotExist) {
t.Errorf("over-cap download left a file at the cache path (stat err = %v)", err)
}
})
Expand All @@ -326,7 +326,7 @@ func TestFetchAssetStreaming(t *testing.T) {
if _, err := s.fetchAsset(context.Background(), assetEntry{URL: url, SHA256: wrongHash}); err == nil {
t.Fatal("fetchAsset accepted a hash mismatch")
}
if _, err := os.Stat(ateompath.RunSCBinaryPath(wrongHash)); !errors.Is(err, os.ErrNotExist) {
if _, err := os.Stat(ateompath.CachedAssetPath(wrongHash)); !errors.Is(err, os.ErrNotExist) {
t.Errorf("mismatched download left a file at the cache path (stat err = %v)", err)
}
})
Expand Down
8 changes: 2 additions & 6 deletions cmd/atelet/sandbox_assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,19 +107,15 @@ func (s *AteomHerder) ensureSandboxAssets(ctx context.Context, rec *sandboxAsset
return paths, nil
}

// runscPathFor returns the local path of the gVisor "runsc" asset from a fetched
// asset-path map, or "" if the runtime has none (e.g. micro-VM).
func runscPathFor(paths map[string]string) string { return paths["runsc"] }

// fetchAsset downloads one content-addressed asset (verifying its sha256) into
// the shared static-files cache and returns its local path. On a cache hit it
// returns immediately.
func (s *AteomHerder) fetchAsset(ctx context.Context, entry assetEntry) (string, error) {
if err := resources.ValidateRunscHash(entry.SHA256); err != nil {
if err := resources.ValidateAssetHash(entry.SHA256); err != nil {
return "", status.Error(codes.InvalidArgument, err.Error())
}

localPath := ateompath.RunSCBinaryPath(entry.SHA256)
localPath := ateompath.CachedAssetPath(entry.SHA256)
_, err := os.Stat(localPath)
if err == nil { // EQUALS nil
return localPath, nil
Expand Down
6 changes: 3 additions & 3 deletions cmd/ateom-gvisor/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ func (s *AteomService) RunWorkload(ctx context.Context, req *ateompb.RunWorkload
}()

rcmd := &runsc{
path: req.GetRunscPath(),
path: req.GetRuntimeAssetPaths()["runsc"],
actorTemplateNamespace: req.GetActorTemplateNamespace(),
actorTemplateName: req.GetActorTemplateName(),
actorID: req.GetActorId(),
Expand Down Expand Up @@ -244,7 +244,7 @@ func (s *AteomService) CheckpointWorkload(ctx context.Context, req *ateompb.Chec
// * After we exit, atelet will tear down OCI bundles and reset the actor directory.

rcmd := &runsc{
path: req.GetRunscPath(),
path: req.GetRuntimeAssetPaths()["runsc"],
actorTemplateNamespace: req.GetActorTemplateNamespace(),
actorTemplateName: req.GetActorTemplateName(),
actorID: req.GetActorId(),
Expand Down Expand Up @@ -351,7 +351,7 @@ func (s *AteomService) RestoreWorkload(ctx context.Context, req *ateompb.Restore
}()

rcmd := &runsc{
path: req.GetRunscPath(),
path: req.GetRuntimeAssetPaths()["runsc"],
actorTemplateNamespace: req.GetActorTemplateNamespace(),
actorTemplateName: req.GetActorTemplateName(),
actorID: req.GetActorId(),
Expand Down
7 changes: 5 additions & 2 deletions internal/ateompath/ateompath.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ const (
)

var (
// StaticFilesDir holds things like downloaded runsc binaries.
// StaticFilesDir holds downloaded, content-addressed sandbox assets.
StaticFilesDir = filepath.Join(BasePath, "static-files")
)

func RunSCBinaryPath(sha256 string) string {
// CachedAssetPath returns the on-disk cache path for a content-addressed
// sandbox asset, keyed by its SHA-256 hash. The "runsc-" filename prefix is
// kept for backward compatibility — existing snapshots embed this absolute path.
func CachedAssetPath(sha256 string) string {
return filepath.Join(StaticFilesDir, "runsc-"+sha256)
}

Expand Down
64 changes: 17 additions & 47 deletions internal/proto/ateompb/ateom.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 11 additions & 11 deletions internal/proto/ateompb/ateom.proto
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ message RunWorkloadRequest {
string actor_template_name = 2;
string actor_id = 3;

string runsc_path = 4;
reserved 4; // was runsc_path; backends now read runtime_asset_paths instead.

WorkloadSpec spec = 5;

// runtime_asset_paths maps a runtime asset name (e.g. "cloud-hypervisor",
// "virtiofsd", "kata-kernel", "kata-image", "kata-config")
// to the local on-disk path atelet fetched it to (content-addressed, like
// runsc_path). Empty for the gVisor runtime, which uses runsc_path.
// runtime_asset_paths maps a runtime asset name to the local on-disk path
// atelet fetched it to (content-addressed). Each backend reads the names it
// needs: gVisor reads "runsc"; a micro-VM backend reads "cloud-hypervisor",
// "kata-kernel", "kata-image", "kata-config"; a custom ateom-* reads its own.
map<string, string> runtime_asset_paths = 7;
}

Expand All @@ -80,7 +80,7 @@ message CheckpointWorkloadRequest {
string actor_template_name = 2;
string actor_id = 3;

string runsc_path = 4;
reserved 4; // was runsc_path; backends now read runtime_asset_paths instead.

WorkloadSpec spec = 5;

Expand All @@ -94,8 +94,8 @@ message CheckpointWorkloadRequest {
// For example: "gs://bucket/actors/1234/snapshots/5678/"
string snapshot_uri_prefix = 6;

// runtime_asset_paths maps a runtime asset name to the local on-disk path
// atelet fetched it to (see RunWorkloadRequest). Empty for gVisor.
// runtime_asset_paths maps a runtime asset name to its local on-disk path
// (see RunWorkloadRequest). gVisor reads "runsc".
map<string, string> runtime_asset_paths = 7;
}

Expand All @@ -111,15 +111,15 @@ message RestoreWorkloadRequest {
string actor_template_name = 2;
string actor_id = 3;

string runsc_path = 4;
reserved 4; // was runsc_path; backends now read runtime_asset_paths instead.

WorkloadSpec spec = 5;

// The object storage URI prefix of the snapshot to restore.
string snapshot_uri_prefix = 6;

// runtime_asset_paths maps a runtime asset name to the local on-disk path
// atelet fetched it to (see RunWorkloadRequest). Empty for gVisor.
// runtime_asset_paths maps a runtime asset name to its local on-disk path
// (see RunWorkloadRequest). gVisor reads "runsc".
map<string, string> runtime_asset_paths = 7;
}

Expand Down
18 changes: 9 additions & 9 deletions internal/resources/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,19 +94,19 @@ func ValidateContainerNames(names []string) error {
return nil
}

// ValidateRunscHash ensures the runsc SHA-256 hash is exactly 64 hex
// characters before it is used to build the on-disk binary path
// (static-files/runsc-<hash>) and, on a cache hit, returned for ateom to
// execute. Without this, a hash containing path separators or ".." could
// point the cache-hit early return (and the download target) at an arbitrary
// binary outside the static-files dir.
func ValidateRunscHash(sha256Hash string) error {
// ValidateAssetHash ensures a content-addressed asset's SHA-256 hash is exactly
// 64 hex characters before it is used to build the on-disk cache path
// (static-files/runsc-<hash>) and, on a cache hit, returned for ateom to use.
// Without this, a hash containing path separators or ".." could point the
// cache-hit early return (and the download target) at an arbitrary file outside
// the static-files dir.
func ValidateAssetHash(sha256Hash string) error {
if len(sha256Hash) != 64 {
return fmt.Errorf("invalid runsc sha256 hash: want 64 hex chars, got %d", len(sha256Hash))
return fmt.Errorf("invalid asset sha256 hash: want 64 hex chars, got %d", len(sha256Hash))
}
// Same decoder atelet's digest comparison uses.
if _, err := hex.DecodeString(sha256Hash); err != nil {
return fmt.Errorf("invalid runsc sha256 hash %q: must be hex", sha256Hash)
return fmt.Errorf("invalid asset sha256 hash %q: must be hex", sha256Hash)
}
return nil
}
Expand Down
6 changes: 3 additions & 3 deletions internal/resources/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func TestValidateContainerNames(t *testing.T) {
}
}

func TestValidateRunscHash(t *testing.T) {
func TestValidateAssetHash(t *testing.T) {
const valid = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"

tests := []struct {
Expand All @@ -129,8 +129,8 @@ func TestValidateRunscHash(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := ValidateRunscHash(tt.hash); (err != nil) != tt.wantErr {
t.Errorf("ValidateRunscHash(%q) err = %v, wantErr %v", tt.hash, err, tt.wantErr)
if err := ValidateAssetHash(tt.hash); (err != nil) != tt.wantErr {
t.Errorf("ValidateAssetHash(%q) err = %v, wantErr %v", tt.hash, err, tt.wantErr)
}
})
}
Expand Down