github.com/kubri/kubri@v0.5.1-0.20240317001612-bda2aaef967e/integrations/apk/repo.go (about)

     1  package apk
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"io/fs"
    10  	"os"
    11  	"path/filepath"
    12  
    13  	"gitlab.alpinelinux.org/alpine/go/repository"
    14  
    15  	"github.com/kubri/kubri/pkg/crypto/rsa"
    16  	"github.com/kubri/kubri/target"
    17  )
    18  
    19  type repo struct {
    20  	repos map[string]*repository.ApkIndex
    21  	dir   string
    22  }
    23  
    24  func openRepo(ctx context.Context, t target.Target) (*repo, error) {
    25  	dir, err := os.MkdirTemp("", "")
    26  	if err != nil {
    27  		return nil, err
    28  	}
    29  
    30  	res := &repo{
    31  		repos: make(map[string]*repository.ApkIndex),
    32  		dir:   dir,
    33  	}
    34  
    35  	// See https://wiki.alpinelinux.org/wiki/Architecture
    36  	archs := []string{"x86", "x86_64", "armhf", "armv7", "aarch64", "ppc64le", "s390x"}
    37  
    38  	for _, arch := range archs {
    39  		r, err := t.NewReader(ctx, arch+"/APKINDEX.tar.gz")
    40  		if err != nil {
    41  			if errors.Is(err, fs.ErrNotExist) {
    42  				continue
    43  			}
    44  			return nil, err
    45  		}
    46  		index, err := repository.IndexFromArchive(r)
    47  		if err != nil {
    48  			return nil, err
    49  		}
    50  		res.repos[arch] = index
    51  	}
    52  
    53  	return res, nil
    54  }
    55  
    56  func (r *repo) Add(b []byte) error {
    57  	p, err := repository.ParsePackage(bytes.NewReader(b))
    58  	if err != nil {
    59  		return err
    60  	}
    61  
    62  	index, ok := r.repos[p.Arch]
    63  	if !ok {
    64  		index = &repository.ApkIndex{}
    65  		r.repos[p.Arch] = index
    66  	}
    67  	index.Packages = append(index.Packages, p)
    68  
    69  	dirname := filepath.Join(r.dir, p.Arch)
    70  	if err = os.MkdirAll(dirname, fs.ModePerm); err != nil {
    71  		return err
    72  	}
    73  
    74  	filename := fmt.Sprintf("%s-%s.apk", p.Name, p.Version)
    75  	return os.WriteFile(filepath.Join(dirname, filename), b, 0o600)
    76  }
    77  
    78  func (r *repo) Write(rsaKey *rsa.PrivateKey, publicKeyName string) error {
    79  	for arch, index := range r.repos {
    80  		rd, err := repository.ArchiveFromIndex(index)
    81  		if err != nil {
    82  			return err
    83  		}
    84  
    85  		if rsaKey != nil {
    86  			rd, err = repository.SignArchive(rd, rsaKey, publicKeyName)
    87  			if err != nil {
    88  				return err
    89  			}
    90  		}
    91  
    92  		path := filepath.Join(r.dir, arch, "APKINDEX.tar.gz")
    93  		f, err := os.Create(path)
    94  		if err != nil {
    95  			return err
    96  		}
    97  		if _, err = io.Copy(f, rd); err != nil {
    98  			return err
    99  		}
   100  		if err = f.Close(); err != nil {
   101  			return err
   102  		}
   103  	}
   104  
   105  	if rsaKey != nil {
   106  		pub, err := rsa.MarshalPublicKey(rsa.Public(rsaKey))
   107  		if err != nil {
   108  			return err
   109  		}
   110  		return os.WriteFile(filepath.Join(r.dir, publicKeyName), pub, 0o600)
   111  	}
   112  
   113  	return nil
   114  }