github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/pkg/packager/sources/utils.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // SPDX-FileCopyrightText: 2021-Present The Jackal Authors
     3  
     4  // Package sources contains core implementations of the PackageSource interface.
     5  package sources
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  
    14  	"github.com/Racer159/jackal/src/config"
    15  	"github.com/Racer159/jackal/src/pkg/layout"
    16  	"github.com/Racer159/jackal/src/pkg/zoci"
    17  	"github.com/Racer159/jackal/src/types"
    18  	"github.com/defenseunicorns/pkg/helpers"
    19  	goyaml "github.com/goccy/go-yaml"
    20  	"github.com/mholt/archiver/v3"
    21  )
    22  
    23  // GetValidPackageExtensions returns the valid package extensions.
    24  func GetValidPackageExtensions() [2]string {
    25  	return [...]string{".tar.zst", ".tar"}
    26  }
    27  
    28  // IsValidFileExtension returns true if the filename has a valid package extension.
    29  func IsValidFileExtension(filename string) bool {
    30  	for _, extension := range GetValidPackageExtensions() {
    31  		if strings.HasSuffix(filename, extension) {
    32  			return true
    33  		}
    34  	}
    35  
    36  	return false
    37  }
    38  
    39  func identifyUnknownTarball(path string) (string, error) {
    40  	if helpers.InvalidPath(path) {
    41  		return "", &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist}
    42  	}
    43  	if filepath.Ext(path) != "" && IsValidFileExtension(path) {
    44  		return path, nil
    45  	} else if filepath.Ext(path) != "" && !IsValidFileExtension(path) {
    46  		return "", fmt.Errorf("%s is not a supported tarball format (%+v)", path, GetValidPackageExtensions())
    47  	}
    48  
    49  	// rename to .tar.zst and check if it's a valid tar.zst
    50  	tzst := fmt.Sprintf("%s.tar.zst", path)
    51  	if err := os.Rename(path, tzst); err != nil {
    52  		return "", err
    53  	}
    54  	format, err := archiver.ByExtension(tzst)
    55  	if err != nil {
    56  		return "", err
    57  	}
    58  	_, ok := format.(*archiver.TarZstd)
    59  	if ok {
    60  		return tzst, nil
    61  	}
    62  
    63  	// rename to .tar and check if it's a valid tar
    64  	tb := fmt.Sprintf("%s.tar", path)
    65  	if err := os.Rename(tzst, tb); err != nil {
    66  		return "", err
    67  	}
    68  	format, err = archiver.ByExtension(tb)
    69  	if err != nil {
    70  		return "", err
    71  	}
    72  	_, ok = format.(*archiver.Tar)
    73  	if ok {
    74  		return tb, nil
    75  	}
    76  
    77  	return "", fmt.Errorf("%s is not a supported tarball format (%+v)", path, GetValidPackageExtensions())
    78  }
    79  
    80  // RenameFromMetadata renames a tarball based on its metadata.
    81  func RenameFromMetadata(path string) (string, error) {
    82  	var pkg types.JackalPackage
    83  
    84  	ext := filepath.Ext(path)
    85  	if ext == "" {
    86  		pathWithExt, err := identifyUnknownTarball(path)
    87  		if err != nil {
    88  			return "", err
    89  		}
    90  		path = pathWithExt
    91  		ext = filepath.Ext(path)
    92  	}
    93  	if ext == ".zst" {
    94  		ext = ".tar.zst"
    95  	}
    96  
    97  	if err := archiver.Walk(path, func(f archiver.File) error {
    98  		if f.Name() == layout.JackalYAML {
    99  			b, err := io.ReadAll(f)
   100  			if err != nil {
   101  				return err
   102  			}
   103  			if err := goyaml.Unmarshal(b, &pkg); err != nil {
   104  				return err
   105  			}
   106  		}
   107  		return nil
   108  	}); err != nil {
   109  		return "", err
   110  	}
   111  
   112  	if pkg.Metadata.Name == "" {
   113  		return "", fmt.Errorf("%q does not contain a jackal.yaml", path)
   114  	}
   115  
   116  	name := NameFromMetadata(&pkg, false)
   117  
   118  	name = fmt.Sprintf("%s%s", name, ext)
   119  
   120  	tb := filepath.Join(filepath.Dir(path), name)
   121  
   122  	return tb, os.Rename(path, tb)
   123  }
   124  
   125  // NameFromMetadata generates a name from a package's metadata.
   126  func NameFromMetadata(pkg *types.JackalPackage, isSkeleton bool) string {
   127  	var name string
   128  
   129  	arch := config.GetArch(pkg.Metadata.Architecture, pkg.Build.Architecture)
   130  
   131  	if isSkeleton {
   132  		arch = zoci.SkeletonArch
   133  	}
   134  
   135  	switch pkg.Kind {
   136  	case types.JackalInitConfig:
   137  		name = fmt.Sprintf("jackal-init-%s", arch)
   138  	case types.JackalPackageConfig:
   139  		name = fmt.Sprintf("jackal-package-%s-%s", pkg.Metadata.Name, arch)
   140  	default:
   141  		name = fmt.Sprintf("jackal-%s-%s", strings.ToLower(string(pkg.Kind)), arch)
   142  	}
   143  
   144  	if pkg.Build.Differential {
   145  		name = fmt.Sprintf("%s-%s-differential-%s", name, pkg.Build.DifferentialPackageVersion, pkg.Metadata.Version)
   146  	} else if pkg.Metadata.Version != "" {
   147  		name = fmt.Sprintf("%s-%s", name, pkg.Metadata.Version)
   148  	}
   149  
   150  	return name
   151  }
   152  
   153  // GetInitPackageName returns the formatted name of the init package.
   154  func GetInitPackageName() string {
   155  	// No package has been loaded yet so lookup GetArch() with no package info
   156  	arch := config.GetArch()
   157  	return fmt.Sprintf("jackal-init-%s-%s.tar.zst", arch, config.CLIVersion)
   158  }
   159  
   160  // PkgSuffix returns a package suffix based on whether it is uncompressed or not.
   161  func PkgSuffix(uncompressed bool) (suffix string) {
   162  	suffix = ".tar.zst"
   163  	if uncompressed {
   164  		suffix = ".tar"
   165  	}
   166  	return suffix
   167  }