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  }