github.com/apptainer/singularity@v3.1.1+incompatible/internal/pkg/build/sources/conveyorPacker_arch.go (about) 1 // Copyright (c) 2018, Sylabs Inc. All rights reserved. 2 // This software is licensed under a 3-clause BSD license. Please consult the 3 // LICENSE.md file distributed with the sources of this project regarding your 4 // rights to use or distribute this software. 5 6 package sources 7 8 import ( 9 "bufio" 10 "bytes" 11 "fmt" 12 "io" 13 "io/ioutil" 14 "net/http" 15 "os" 16 "os/exec" 17 "path/filepath" 18 "runtime" 19 20 "github.com/sylabs/singularity/internal/pkg/sylog" 21 "github.com/sylabs/singularity/pkg/build/types" 22 ) 23 24 const ( 25 pacmanConfURL = "https://git.archlinux.org/svntogit/packages.git/plain/trunk/pacman.conf?h=packages/pacman" 26 ) 27 28 // `pacstrap' installs the whole "base" package group, unless told otherwise. 29 // baseToSkip are "base" packages that won't be normally needed on a 30 // container system. $BASE_TO_INST are "base" packages not present in 31 // baseToSkip. The list of packages included in "base" group may (it surely 32 // will, one day) change in future, so baseToSkip will need an update from 33 // time to time. Here I'm referring to `base' group contents as of 30.08.2016. 34 var baseToSkip = map[string]bool{ 35 "cryptsetup": true, 36 "device-mapper": true, 37 "dhcpcd": true, 38 "iproute2": true, 39 "jfsutils": true, 40 "linux": true, 41 "lvm2": true, 42 "man-db": true, 43 "man-pages": true, 44 "mdadm": true, 45 "nano": true, 46 "netctl": true, 47 "openresolv": true, 48 "pciutils": true, 49 "pcmciautils": true, 50 "reiserfsprogs": true, 51 "s-nail": true, 52 "systemd-sysvcompat": true, 53 "usbutils": true, 54 "vi": true, 55 "xfsprogs": true, 56 } 57 58 // ArchConveyorPacker only needs to hold the conveyor to have the needed data to pack 59 type ArchConveyorPacker struct { 60 b *types.Bundle 61 } 62 63 // Get just stores the source 64 func (cp *ArchConveyorPacker) Get(b *types.Bundle) (err error) { 65 cp.b = b 66 67 //check for pacstrap on system 68 pacstrapPath, err := exec.LookPath("pacstrap") 69 if err != nil { 70 return fmt.Errorf("pacstrap is not in PATH: %v", err) 71 } 72 73 //make sure architecture is supported 74 if arch := runtime.GOARCH; arch != `amd64` { 75 return fmt.Errorf("%v architecture is not supported", arch) 76 } 77 78 instList, err := getPacmanBaseList() 79 if err != nil { 80 return fmt.Errorf("While generating the installation list: %v", err) 81 } 82 83 pacConf, err := cp.getPacConf(pacmanConfURL) 84 if err != nil { 85 return fmt.Errorf("While getting pacman config: %v", err) 86 } 87 88 args := []string{"-C", pacConf, "-c", "-d", "-G", "-M", cp.b.Rootfs(), "haveged"} 89 args = append(args, instList...) 90 91 pacCmd := exec.Command(pacstrapPath, args...) 92 pacCmd.Stdout = os.Stdout 93 pacCmd.Stderr = os.Stderr 94 sylog.Debugf("\n\tPacstrap Path: %s\n\tPac Conf: %s\n\tRootfs: %s\n\tInstall List: %s\n", pacstrapPath, pacConf, cp.b.Rootfs(), instList) 95 96 if err = pacCmd.Run(); err != nil { 97 return fmt.Errorf("While pacstrapping: %v", err) 98 } 99 100 //Pacman package signing setup 101 cmd := exec.Command("arch-chroot", cp.b.Rootfs(), "/bin/sh", "-c", "haveged -w 1024; pacman-key --init; pacman-key --populate archlinux") 102 cmd.Stdout = os.Stdout 103 cmd.Stderr = os.Stderr 104 if err = cmd.Run(); err != nil { 105 return fmt.Errorf("While setting up package signing: %v", err) 106 } 107 108 //Clean up haveged 109 cmd = exec.Command("arch-chroot", cp.b.Rootfs(), "pacman", "-Rs", "--noconfirm", "haveged") 110 cmd.Stdout = os.Stdout 111 cmd.Stderr = os.Stderr 112 if err = cmd.Run(); err != nil { 113 return fmt.Errorf("While cleaning up packages: %v", err) 114 } 115 116 return nil 117 } 118 119 // Pack puts relevant objects in a Bundle! 120 func (cp *ArchConveyorPacker) Pack() (b *types.Bundle, err error) { 121 err = cp.insertBaseEnv() 122 if err != nil { 123 return nil, fmt.Errorf("While inserting base environment: %v", err) 124 } 125 126 err = cp.insertRunScript() 127 if err != nil { 128 return nil, fmt.Errorf("While inserting runscript: %v", err) 129 } 130 131 return cp.b, nil 132 } 133 134 func getPacmanBaseList() (instList []string, err error) { 135 var output, stderr bytes.Buffer 136 cmd := exec.Command("pacman", "-Sgq", "base") 137 cmd.Stdout = &output 138 cmd.Stderr = &stderr 139 if err = cmd.Run(); err != nil { 140 return nil, fmt.Errorf("%v: %v", err, stderr.String()) 141 } 142 143 var toInstall []string 144 scanner := bufio.NewScanner(&output) 145 scanner.Split(bufio.ScanWords) 146 147 for scanner.Scan() { 148 if !baseToSkip[scanner.Text()] { 149 toInstall = append(toInstall, scanner.Text()) 150 } 151 } 152 153 return toInstall, nil 154 } 155 156 func (cp *ArchConveyorPacker) getPacConf(pacmanConfURL string) (pacConf string, err error) { 157 pacConfFile, err := ioutil.TempFile(cp.b.Rootfs(), "pac-conf-") 158 if err != nil { 159 return 160 } 161 162 resp, err := http.Get(pacmanConfURL) 163 if err != nil { 164 return "", fmt.Errorf("While performing http request: %v", err) 165 } 166 defer resp.Body.Close() 167 168 bytesWritten, err := io.Copy(pacConfFile, resp.Body) 169 if err != nil { 170 return 171 } 172 173 //Simple check to make sure file received is the correct size 174 if bytesWritten != resp.ContentLength { 175 return "", fmt.Errorf("File received is not the right size. Supposed to be: %v Actually: %v", resp.ContentLength, bytesWritten) 176 } 177 178 return pacConfFile.Name(), nil 179 } 180 181 func (cp *ArchConveyorPacker) insertBaseEnv() (err error) { 182 if err = makeBaseEnv(cp.b.Rootfs()); err != nil { 183 return 184 } 185 return nil 186 } 187 188 func (cp *ArchConveyorPacker) insertRunScript() (err error) { 189 err = ioutil.WriteFile(filepath.Join(cp.b.Rootfs(), "/.singularity.d/runscript"), []byte("#!/bin/sh\n"), 0755) 190 if err != nil { 191 return 192 } 193 194 return nil 195 } 196 197 // CleanUp removes any tmpfs owned by the conveyorPacker on the filesystem 198 func (cp *ArchConveyorPacker) CleanUp() { 199 os.RemoveAll(cp.b.Path) 200 }