diff --git a/simpler-sif-building.patch b/simpler-sif-building.patch new file mode 100644 index 0000000..3212910 --- /dev/null +++ b/simpler-sif-building.patch @@ -0,0 +1,336 @@ +diff --git a/CHANGELOG.md b/CHANGELOG.md +index 4a39aa99a..e76b16e65 100644 +--- a/CHANGELOG.md ++++ b/CHANGELOG.md +@@ -5,6 +5,11 @@ The Singularity Project has been + and re-branded as Apptainer. + For older changes see the [archived Singularity change log](https://github.com/apptainer/singularity/blob/release-3.8/CHANGELOG.md). + ++### Backported from main tree ++ ++- Build via zypper on SLE systems will use repositories of host via ++ suseconnect-container ++ + ## v1.1.6 - \[2023-02-14\] + + ### Security fix +diff --git a/docs/content.go b/docs/content.go +index c1ac8465a..81525d08f 100644 +--- a/docs/content.go ++++ b/docs/content.go +@@ -90,6 +90,15 @@ Enterprise Performance Computing (EPC)` + MirrorURL: http://mirror.centos.org/centos-%{OSVERSION}/%{OSVERSION}/os/x86_64/ + Include: yum + ++ SUSE: ++ Bootstrap: zypper # on SLE system registration of build host is used ++ Include: zypper ++ ++ openSUSE: ++ Bootstrap: zypper ++ MirrorURL: http://download.opensuse.org/distribution/openSUSE-stable/repo/oss ++ Include: zypper ++ + Debian/Ubuntu: + Bootstrap: debootstrap + OSVersion: trusty +diff --git a/examples/sle/Apptainer b/examples/sle/Apptainer +index 1883e9d12..9d9197436 100644 +--- a/examples/sle/Apptainer ++++ b/examples/sle/Apptainer +@@ -1,38 +1,13 @@ ++# use repos and registration from build host + BootStrap: zypper +-OSVersion: 12.4 +-Product: SLE-HPC/%{OSVERSION}/x86_64 +-User: +-Regcode: +-# MirrorURL: +-# Modules: sle-module-basesystem,sle-module-server-applications,sle-module-web-scripting,sle-module-hpc +-Include: zypper +-# Otherurl0: +-# Otherurl1: +-ProductPGP: -----BEGIN PGP PUBLIC KEY BLOCK-----\n\ +-Version: rpm-4.11.2 (NSS-3)\n\ +-\n\ +-mQENBFEKlmsBCADbpZZbbSC5Zi+HxCR/ynYsVxU5JNNiSSZabN5GMgc9Z0hxeXxp\n\ +-YWvFoE/4n0+IXIsp83iKvxf06Eu8je/DXp0lMqDZu7WiT3XXAlkOPSNV4akHTDoY\n\ +-91SJaZCpgUJ7K1QXOPABNbREsAMN1a7rxBowjNjBUyiTJ2YuvQRLtGdK1kExsVma\n\ +-hieh/QxpoDyYd5w/aky3z23erCoEd+OPfAqEHd5tQIa6LOosa63BSCEl3milJ7J9\n\ +-vDmoGPAoS6ui7S2R5X4/+PLN8Mm2kOBrFjhmL93LX0mrGCMxsNsKgP6zabYKQEb8\n\ +-L028SXvl7EGoA+Vw5Vd3wIGbM73PfbgNrXjfABEBAAG0KFN1U0UgUGFja2FnZSBT\n\ +-aWduaW5nIEtleSA8YnVpbGRAc3VzZS5kZT6JATwEEwECACYCGwMGCwkIBwMCBBUC\n\ +-CAMEFgIDAQIeAQIXgAUCWEfrHwUJDsIitAAKCRBwr56BOdt8gpqUB/wPSSS5BcDu\n\ +-Oi4n02cj4Hdt7WITKBjjo0lG1fXG1ppx1wOST+s8FertMVFY53TW6FGjcYtwVOIq\n\ +-rsMYiV6kf1NxUV/jcAy7VmC5EZnO0R/D3sT4Oh5hsLtERauZolK5BZmd0S51Qa8e\n\ +-TxZ5mX9PL2i3s/ShETc30drf83ugc7B4yZPNQWXNDPgGcC+hEeC5qw48RzHYIpUt\n\ +-RzHmefR5Z3ioTUbDlzy+SGP2uA7mhR4Lfk/df5fYxWfCoKlyGjtrvA65cB+Pksyn\n\ +-xrAeBuB+vBM+KnDrxW2Sn4AbWkzH//dfz9OJDJu4UM91hb7qxM0OkrXHQV3iNqzg\n\ +-MDEhky/9NqMy\n\ +-=GdP5\n\ +------END PGP PUBLIC KEY BLOCK----- ++ + + %runscript + echo "This is what happens when you run the container..." + + + %post ++ update-ca-certificates + echo "Hello from inside the container" + zypper lr -d + SUSEConnect -l +diff --git a/internal/pkg/build/sources/conveyorPacker_zypper.go b/internal/pkg/build/sources/conveyorPacker_zypper.go +index 43745c25e..d146f6857 100644 +--- a/internal/pkg/build/sources/conveyorPacker_zypper.go ++++ b/internal/pkg/build/sources/conveyorPacker_zypper.go +@@ -14,7 +14,6 @@ import ( + "bytes" + "context" + "fmt" +- "io/ioutil" + "os" + "os/exec" + "path/filepath" +@@ -24,13 +23,18 @@ import ( + "strings" + "syscall" + ++ "github.com/BurntSushi/toml" + "github.com/apptainer/apptainer/internal/pkg/util/bin" ++ "github.com/apptainer/apptainer/internal/pkg/util/fs" + "github.com/apptainer/apptainer/pkg/build/types" + "github.com/apptainer/apptainer/pkg/sylog" + ) + + const ( +- zypperConf = "/etc/zypp/zypp.conf" ++ zypperConf = "/etc/zypp/zypp.conf" ++ osreleaseFile = "/etc/os-release" ++ ssccredentialsFile = "/etc/zypp/credentials.d/SCCcredentials" ++ gpgKeyid = "gpg-pubkey-307e3d54-5aaa90a5 gpg-pubkey-39db7c82-5f68629b" + ) + + // ZypperConveyorPacker only needs to hold the bundle for the container +@@ -58,6 +62,8 @@ func machine() (string, error) { + func (cp *ZypperConveyorPacker) Get(ctx context.Context, b *types.Bundle) (err error) { + var suseconnectProduct, suseconnectModver string + var suseconnectPath string ++ // dependContainer is a container which shares the repos with the host through container-suseconnect ++ dependContainer := false + var pgpfile string + var iosmajor int + var otherurl [20]string +@@ -76,7 +82,6 @@ func (cp *ZypperConveyorPacker) Get(ctx context.Context, b *types.Bundle) (err e + } + + include := cp.b.Recipe.Header["include"] +- + // check for include environment variable and add it to requires string + include += ` ` + os.Getenv("INCLUDE") + +@@ -86,14 +91,22 @@ func (cp *ZypperConveyorPacker) Get(ctx context.Context, b *types.Bundle) (err e + // add aaa_base to start of include list by default + include = `aaa_base ` + include + ++ suseVars := getSusevars() + // get mirrorURL, OSVerison, and Includes components to definition + osversion, osversionOk := cp.b.Recipe.Header["osversion"] ++ if !osversionOk { ++ osversion = suseVars.Version ++ } + mirrorurl, mirrorurlOk := cp.b.Recipe.Header["mirrorurl"] + updateurl, updateurlOk := cp.b.Recipe.Header["updateurl"] + sleproduct, sleproductOk := cp.b.Recipe.Header["product"] + sleuser, sleuserOk := cp.b.Recipe.Header["user"] + sleregcode, sleregcodeOk := cp.b.Recipe.Header["regcode"] + slepgp, slepgpOk := cp.b.Recipe.Header["productpgp"] ++ if !slepgpOk && suseVars.GpgKeyOk { ++ slepgpOk = true ++ slepgp = suseVars.GpgKey ++ } + sleurl, sleurlOk := cp.b.Recipe.Header["registerurl"] + slemodules, slemodulesOk := cp.b.Recipe.Header["modules"] + cnt := -1 +@@ -116,7 +129,6 @@ func (cp *ZypperConveyorPacker) Get(ctx context.Context, b *types.Bundle) (err e + } + } + regex := regexp.MustCompile(`(?i)%{OSVERSION}`) +- + if sleproductOk || sleuserOk || sleregcodeOk { + if !sleproductOk || !sleuserOk || !sleregcodeOk { + return fmt.Errorf("for installation of SLE 'Product', 'User' and 'Regcode' need to be set") +@@ -173,23 +185,9 @@ func (cp *ZypperConveyorPacker) Get(ctx context.Context, b *types.Bundle) (err e + default: + return fmt.Errorf("malformed Product setting") + } +- if slepgpOk { +- tmpfile, err := ioutil.TempFile("/tmp", "apptainer-pgp") +- if err != nil { +- return fmt.Errorf("cannot create pgp-file: %v", err) +- } +- pgpfile = tmpfile.Name() +- +- if _, err = tmpfile.WriteString(slepgp + "\n"); err != nil { +- return fmt.Errorf("cannot write pgp-file: %v", err) +- } +- if err = tmpfile.Close(); err != nil { +- return fmt.Errorf("cannot close pgp-file %v", err) +- } +- } + + include = include + ` SUSEConnect` +- } else { ++ } else if mirrorurlOk { + if !mirrorurlOk { + return fmt.Errorf("invalid zypper header, no MirrorURL specified") + } +@@ -202,6 +200,24 @@ func (cp *ZypperConveyorPacker) Get(ctx context.Context, b *types.Bundle) (err e + updateurl = regex.ReplaceAllString(updateurl, osversion) + } + } ++ } else if suseVars.HasScc { ++ dependContainer = true ++ include += " container-suseconnect" ++ cp.b.Opts.Binds = append(cp.b.Opts.Binds, ssccredentialsFile+":"+ssccredentialsFile) ++ } ++ if slepgpOk { ++ tmpfile, err := os.CreateTemp("/tmp", "apptainer-pgp") ++ if err != nil { ++ return fmt.Errorf("cannot create pgp-file: %v", err) ++ } ++ pgpfile = tmpfile.Name() ++ ++ if _, err = tmpfile.WriteString(slepgp + "\n"); err != nil { ++ return fmt.Errorf("cannot write pgp-file: %v", err) ++ } ++ if err = tmpfile.Close(); err != nil { ++ return fmt.Errorf("cannot close pgp-file %v", err) ++ } + } + + // Create the main portion of zypper config +@@ -318,8 +334,26 @@ func (cp *ZypperConveyorPacker) Get(ctx context.Context, b *types.Bundle) (err e + return fmt.Errorf("while refreshing: %s %v", `repo-`+sID, err) + } + } ++ args := []string{`--non-interactive`, `-c`, filepath.Join(cp.b.RootfsPath, zypperConf)} ++ if dependContainer { ++ // --installroot will use containers from repo ++ args = append(args, `--installroot`, cp.b.RootfsPath) ++ include += " zypper" ++ if suseVars.HasScc { ++ if err = os.MkdirAll(filepath.Join(cp.b.RootfsPath, "/etc/zypp/credentials.d/"), 0o755); err != nil { ++ return fmt.Errorf("cannot recreate /etc/zypp/credentials.d/ directories: %v", err) ++ } ++ sccF, err := os.Create(filepath.Join(cp.b.RootfsPath, "/etc/zypp/credentials.d/SCCcredentials")) ++ if err != nil { ++ return fmt.Errorf("couldn't create SCCcredentials file: %v", err) ++ } ++ sccF.Close() ++ } ++ } else { ++ args = append(args, `--root`, cp.b.RootfsPath, `--releasever=`+osversion) ++ } ++ args = append(args, `-n`, `install`, `--auto-agree-with-licenses`, `--download-in-advance`) + +- args := []string{`--non-interactive`, `-c`, filepath.Join(cp.b.RootfsPath, zypperConf), `--root`, cp.b.RootfsPath, `--releasever=` + osversion, `-n`, `install`, `--auto-agree-with-licenses`, `--download-in-advance`} + args = append(args, strings.Fields(include)...) + + // Zypper install command +@@ -392,7 +426,7 @@ func (cp *ZypperConveyorPacker) genZypperConfig() (err error) { + return fmt.Errorf("while creating %v: %v", filepath.Join(cp.b.RootfsPath, "/etc/zypp"), err) + } + +- err = ioutil.WriteFile(filepath.Join(cp.b.RootfsPath, zypperConf), []byte("[main]\ncachedir=/val/cache/zypp-bootstrap\n\n"), 0o664) ++ err = os.WriteFile(filepath.Join(cp.b.RootfsPath, zypperConf), []byte("[main]\ncachedir=/var/cache/zypp-bootstrap\n\n"), 0o664) + if err != nil { + return + } +@@ -469,3 +503,44 @@ func rpmPathCheck() (err error) { + + return nil + } ++ ++/* ++Parse the /etc/os.release file to a a struct, so that SUSE versions ++need not to be set on a SLE syste, ++*/ ++func getSusevars() (ret struct { ++ osRelease ++ GpgKey string ++ GpgKeyOk bool ++ HasScc bool ++}, ++) { ++ // ignore errors as we check for empty fields later ++ b, _ := os.ReadFile(osreleaseFile) ++ var osrel osRelease ++ _ = toml.Unmarshal(b, &osrel) ++ ret.osRelease = osrel ++ if ret.Name != "" { ++ ret.Product = ret.Name + "/" + ret.VersionID + "/" + runtime.GOARCH ++ } ++ ret.GpgKeyOk = false ++ args := []string{"-q", "--qf", "'%{PUBKEYS:armor}'"} ++ args = append(args, strings.Split(gpgKeyid, " ")...) ++ out, err := exec.Command("rpm", args...).Output() ++ if err == nil { ++ ret.GpgKeyOk = true ++ ret.GpgKey = string(out) ++ } ++ ret.HasScc = fs.IsFile(ssccredentialsFile) ++ return ret ++} ++ ++/* ++hold the os_release vars ++*/ ++type osRelease struct { ++ Name string `toml:"NAME"` ++ Version string `toml:"VERSION"` ++ VersionID string `toml:"VERSION_ID"` ++ Product string ++} +diff --git a/internal/pkg/build/stage.go b/internal/pkg/build/stage.go +index ed041b64b..18cad349a 100644 +--- a/internal/pkg/build/stage.go ++++ b/internal/pkg/build/stage.go +@@ -109,7 +109,11 @@ func (s *stage) runPostScript(sessionResolv, sessionHosts string) error { + } + cmdArgs = append(cmdArgs, "-B", strings.Join(fakerootBinds[:], ",")) + } +- ++ if len(s.b.Opts.Binds) != 0 { ++ for _, bind := range s.b.Opts.Binds { ++ cmdArgs = append(cmdArgs, "-B", bind) ++ } ++ } + script := s.b.Recipe.BuildData.Post + scriptPath := filepath.Join(s.b.RootfsPath, ".post.script") + if err = createScript(scriptPath, []byte(script.Script)); err != nil { +@@ -153,6 +157,11 @@ func (s *stage) runTestScript(sessionResolv, sessionHosts string) error { + if sessionHosts != "" { + cmdArgs = append(cmdArgs, "-B", sessionHosts+":/etc/hosts") + } ++ if len(s.b.Opts.Binds) != 0 { ++ for _, bind := range s.b.Opts.Binds { ++ cmdArgs = append(cmdArgs, "-B", bind) ++ } ++ } + + exe := filepath.Join(buildcfg.BINDIR, "apptainer") + +diff --git a/pkg/build/types/bundle.go b/pkg/build/types/bundle.go +index 6ecee877b..322518a2c 100644 +--- a/pkg/build/types/bundle.go ++++ b/pkg/build/types/bundle.go +@@ -81,6 +81,8 @@ type Options struct { + // To warn when the above is needed, we need to know if the target of this + // bundle will be a sandbox + SandboxTarget bool ++ // Binds stores bind mounts used for the post scripts ++ Binds []string + } + + // NewEncryptedBundle creates an Encrypted Bundle environment.