Move runtime to core/runtime
Signed-off-by: Derek McGowan <derek@mcg.dev>
This commit is contained in:
169
core/runtime/v2/bundle.go
Normal file
169
core/runtime/v2/bundle.go
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/mount"
|
||||
"github.com/containerd/containerd/v2/pkg/identifiers"
|
||||
"github.com/containerd/containerd/v2/pkg/namespaces"
|
||||
"github.com/containerd/containerd/v2/pkg/oci"
|
||||
"github.com/containerd/typeurl/v2"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// LoadBundle loads an existing bundle from disk
|
||||
func LoadBundle(ctx context.Context, root, id string) (*Bundle, error) {
|
||||
ns, err := namespaces.NamespaceRequired(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Bundle{
|
||||
ID: id,
|
||||
Path: filepath.Join(root, ns, id),
|
||||
Namespace: ns,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewBundle returns a new bundle on disk
|
||||
func NewBundle(ctx context.Context, root, state, id string, spec typeurl.Any) (b *Bundle, err error) {
|
||||
if err := identifiers.Validate(id); err != nil {
|
||||
return nil, fmt.Errorf("invalid task id %s: %w", id, err)
|
||||
}
|
||||
|
||||
ns, err := namespaces.NamespaceRequired(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
work := filepath.Join(root, ns, id)
|
||||
b = &Bundle{
|
||||
ID: id,
|
||||
Path: filepath.Join(state, ns, id),
|
||||
Namespace: ns,
|
||||
}
|
||||
var paths []string
|
||||
defer func() {
|
||||
if err != nil {
|
||||
for _, d := range paths {
|
||||
os.RemoveAll(d)
|
||||
}
|
||||
}
|
||||
}()
|
||||
// create state directory for the bundle
|
||||
if err := os.MkdirAll(filepath.Dir(b.Path), 0711); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.Mkdir(b.Path, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if typeurl.Is(spec, &specs.Spec{}) {
|
||||
if err := prepareBundleDirectoryPermissions(b.Path, spec.GetValue()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
paths = append(paths, b.Path)
|
||||
// create working directory for the bundle
|
||||
if err := os.MkdirAll(filepath.Dir(work), 0711); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rootfs := filepath.Join(b.Path, "rootfs")
|
||||
if err := os.MkdirAll(rootfs, 0711); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths = append(paths, rootfs)
|
||||
if err := os.Mkdir(work, 0711); err != nil {
|
||||
if !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
os.RemoveAll(work)
|
||||
if err := os.Mkdir(work, 0711); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
paths = append(paths, work)
|
||||
// symlink workdir
|
||||
if err := os.Symlink(work, filepath.Join(b.Path, "work")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if spec := spec.GetValue(); spec != nil {
|
||||
// write the spec to the bundle
|
||||
specPath := filepath.Join(b.Path, oci.ConfigFilename)
|
||||
err = os.WriteFile(specPath, spec, 0666)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to write bundle spec: %w", err)
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Bundle represents an OCI bundle
|
||||
type Bundle struct {
|
||||
// ID of the bundle
|
||||
ID string
|
||||
// Path to the bundle
|
||||
Path string
|
||||
// Namespace of the bundle
|
||||
Namespace string
|
||||
}
|
||||
|
||||
// Delete a bundle atomically
|
||||
func (b *Bundle) Delete() error {
|
||||
work, werr := os.Readlink(filepath.Join(b.Path, "work"))
|
||||
rootfs := filepath.Join(b.Path, "rootfs")
|
||||
if runtime.GOOS != "darwin" {
|
||||
if err := mount.UnmountRecursive(rootfs, 0); err != nil {
|
||||
return fmt.Errorf("unmount rootfs %s: %w", rootfs, err)
|
||||
}
|
||||
}
|
||||
if err := os.Remove(rootfs); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to remove bundle rootfs: %w", err)
|
||||
}
|
||||
err := atomicDelete(b.Path)
|
||||
if err == nil {
|
||||
if werr == nil {
|
||||
return atomicDelete(work)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// error removing the bundle path; still attempt removing work dir
|
||||
var err2 error
|
||||
if werr == nil {
|
||||
err2 = atomicDelete(work)
|
||||
if err2 == nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("failed to remove both bundle and workdir locations: %v: %w", err2, err)
|
||||
}
|
||||
|
||||
// atomicDelete renames the path to a hidden file before removal
|
||||
func atomicDelete(path string) error {
|
||||
// create a hidden dir for an atomic removal
|
||||
atomicPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path)))
|
||||
if err := os.Rename(path, atomicPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return os.RemoveAll(atomicPath)
|
||||
}
|
||||
Reference in New Issue
Block a user