github.com/mgoltzsche/ctnr@v0.7.1-alpha/bundle/builder/bundlebuilder.go (about)

     1  package builder
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  
     7  	"github.com/cyphar/filepath-securejoin"
     8  	"github.com/mgoltzsche/ctnr/bundle"
     9  	"github.com/openSUSE/umoci/pkg/fseval"
    10  	"github.com/pkg/errors"
    11  )
    12  
    13  type BundleBuilder struct {
    14  	id string
    15  	*SpecBuilder
    16  	image        bundle.BundleImage
    17  	managedFiles map[string]bool
    18  }
    19  
    20  func Builder(id string) *BundleBuilder {
    21  	specgen := NewSpecBuilder()
    22  	specgen.SetRootPath("rootfs")
    23  	b := &BundleBuilder{"", &specgen, nil, map[string]bool{}}
    24  	b.SetID(id)
    25  	return b
    26  }
    27  
    28  func (b *BundleBuilder) SetID(id string) {
    29  	if id == "" {
    30  		panic("no bundle id provided")
    31  	}
    32  	b.id = id
    33  	b.SetHostname(id)
    34  	b.AddAnnotation(bundle.ANNOTATION_BUNDLE_ID, id)
    35  }
    36  
    37  func (b *BundleBuilder) SetImage(image bundle.BundleImage) {
    38  	b.ApplyImage(image.Config())
    39  	b.image = image
    40  }
    41  
    42  // Overlays the provided file path with a bind mounted read-only copy.
    43  // The file's content is supposed to be managed by an OCI hook.
    44  func (b *BundleBuilder) AddBindMountConfig(path string) {
    45  	path = filepath.Clean(path)
    46  	opts := []string{"bind", "mode=0444", "nosuid", "noexec", "nodev", "ro"}
    47  	b.managedFiles[path] = true
    48  	b.AddBindMount(filepath.Join("mount", path), path, opts)
    49  }
    50  
    51  func (b *BundleBuilder) Build(bundle *bundle.LockedBundle) (err error) {
    52  	// Prepare rootfs
    53  	if err = bundle.UpdateRootfs(b.image); err != nil {
    54  		return errors.Wrap(err, "build bundle")
    55  	}
    56  
    57  	// Generate managed config files
    58  	for path := range b.managedFiles {
    59  		if err = b.touchManagedFile(bundle.Dir(), path); err != nil {
    60  			return errors.Wrap(err, "build bundle")
    61  		}
    62  	}
    63  
    64  	// Resolve user/group names
    65  	rootfs := filepath.Join(bundle.Dir(), b.Generator.Spec().Root.Path)
    66  	spec, err := b.Spec(rootfs)
    67  	if err != nil {
    68  		return errors.Wrap(err, "build bundle")
    69  	}
    70  
    71  	// Apply spec
    72  	return errors.Wrap(bundle.SetSpec(spec), "build bundle")
    73  }
    74  
    75  func (b *BundleBuilder) touchManagedFile(bundleDir, path string) (err error) {
    76  	file, err := securejoin.SecureJoinVFS(filepath.Join(bundleDir, "mount"), path, fseval.RootlessFsEval)
    77  	if err != nil {
    78  		return
    79  	}
    80  	if err = os.MkdirAll(filepath.Dir(file), 0755); err != nil {
    81  		return
    82  	}
    83  	f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
    84  	if err != nil {
    85  		return
    86  	}
    87  	return f.Close()
    88  }