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 }