convertSIFToElements processes sifImage and creates/returns the relevant elements for constructing an OCI-like image: - A path to a tar file containing a root filesystem, - A command to run. The returned tar file path is inside tempDir, which can be assumed to be empty at start, and is exclusively u
(ctx context.Context, sifImage *sif.FileImage, tempDir string)
| 148 | // at start, and is exclusively used by the current process (i.e. it is safe |
| 149 | // to use hard-coded relative paths within it). |
| 150 | func convertSIFToElements(ctx context.Context, sifImage *sif.FileImage, tempDir string) (string, []string, error) { |
| 151 | // We could allocate unique names for all of these using os.{CreateTemp,MkdirTemp}, but tempDir is exclusive, |
| 152 | // so we can just hard-code a set of unique values here. |
| 153 | // We create and/or manage cleanup of these two paths. |
| 154 | squashFSPath := filepath.Join(tempDir, "rootfs.squashfs") |
| 155 | tarPath := filepath.Join(tempDir, "rootfs.tar") |
| 156 | // We only allocate these paths, the user is responsible for cleaning them up. |
| 157 | extractedRootPath := filepath.Join(tempDir, "rootfs") |
| 158 | scriptPath := filepath.Join(tempDir, "script") |
| 159 | |
| 160 | succeeded := false |
| 161 | // It's safe for the Remove calls to happen even before we create the files, because tempDir is exclusive |
| 162 | // for our use. |
| 163 | // Ideally we would remove squashFSPath immediately after creating extractedRootPath, but we need |
| 164 | // to run both creation and consumption of extractedRootPath in the same fakeroot context. |
| 165 | // So, overall, this process requires at least 2 compressed copies (SIF and squashFSPath) and 2 |
| 166 | // uncompressed copies (extractedRootPath and tarPath) of the data, all using up space at the same time. |
| 167 | // That's rather unsatisfactory, ideally we would be streaming the data directly from a squashfs parser |
| 168 | // reading from the SIF file to a tarball, for 1 compressed and 1 uncompressed copy. |
| 169 | defer os.Remove(squashFSPath) |
| 170 | defer func() { |
| 171 | if !succeeded { |
| 172 | os.Remove(tarPath) |
| 173 | } |
| 174 | }() |
| 175 | |
| 176 | command, injectedScript, err := processDefFile(sifImage) |
| 177 | if err != nil { |
| 178 | return "", nil, err |
| 179 | } |
| 180 | |
| 181 | rootFS, err := sifImage.GetDescriptor(sif.WithPartitionType(sif.PartPrimSys)) |
| 182 | if err != nil { |
| 183 | return "", nil, fmt.Errorf("looking up rootfs from SIF file: %w", err) |
| 184 | } |
| 185 | // TODO: We'd prefer not to make a full copy of the file here; unsquashfs ≥ 4.4 |
| 186 | // has an -o option that allows extracting a squashfs from the SIF file directly, |
| 187 | // but that version is not currently available in RHEL 8. |
| 188 | logrus.Debugf("Creating a temporary squashfs image %s ...", squashFSPath) |
| 189 | if err := func() (retErr error) { // A scope for defer |
| 190 | f, err := os.Create(squashFSPath) |
| 191 | if err != nil { |
| 192 | return err |
| 193 | } |
| 194 | // since we are writing to this file, make sure we handle err on Close() |
| 195 | defer func() { |
| 196 | closeErr := f.Close() |
| 197 | if retErr == nil { |
| 198 | retErr = closeErr |
| 199 | } |
| 200 | }() |
| 201 | // TODO: This can take quite some time, and should ideally be cancellable using ctx.Done(). |
| 202 | if _, err := io.CopyN(f, rootFS.GetReader(), rootFS.Size()); err != nil { |
| 203 | return err |
| 204 | } |
| 205 | return nil |
| 206 | }(); err != nil { |
| 207 | return "", nil, err |
no test coverage detected
searching dependent graphs…