diff --git a/README.md b/README.md index 69ed6e0..faf5d14 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ - [Setting up Cloud Spanner](#setting-up-cloud-spanner) - [Setting up importers](#setting-up-importers) - [TarGz](#targz) + - [Deb](#deb) - [GCP](#gcp) - [Windows](#windows) - [WSUS](#wsus) @@ -205,6 +206,13 @@ This is a simple importer that traverses repositories and looks for `.tar.gz` fi 1. `-targz_repo_path` which should point to the path on the local file system that contains `.tar.gz` files +#### Deb + +This is very similar to the TarGz importer except that it looks for `.deb` packages. Once found it will hash the first and the last 10MB of the file to check if it was already processed. This is done to prevent hashing the whole file every time the repository is scanned for new sources. To use this importer you need to specify the following flag(s): + +1. `-deb_repo_path` which should point to the path on the local file system that contains `.deb` files + + #### GCP This importer can extract files from GCP disk [images](https://cloud.google.com/compute/docs/images). This is done in few steps: diff --git a/go.mod b/go.mod index 5cd94e5..a1e1c3c 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( require ( cloud.google.com/go v0.100.2 // indirect cloud.google.com/go/compute v1.6.1 // indirect + github.com/DataDog/zstd v1.4.8 // indirect github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect @@ -27,11 +28,16 @@ require ( github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/googleapis/gax-go/v2 v2.3.0 // indirect + github.com/kjk/lzma v0.0.0-20161016003348-3fd93898850d // indirect + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect go.opencensus.io v0.23.0 // indirect + golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect google.golang.org/appengine v1.6.7 // indirect + pault.ag/go/debian v0.12.0 // indirect + pault.ag/go/topsort v0.0.0-20160530003732-f98d2ad46e1a // indirect ) diff --git a/go.sum b/go.sum index 9d55ae4..d8bce46 100644 --- a/go.sum +++ b/go.sum @@ -58,6 +58,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/DataDog/zstd v1.4.8 h1:Rpmta4xZ/MgZnriKNd24iZMhGpP5dvUcs/uqfBapKZY= +github.com/DataDog/zstd v1.4.8/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Microsoft/go-winio v0.5.3-0.20220712145307-8fca75951feb h1:mYouFl1H94ZXkNVZ4/b4gQgaig4ch4G1f/oTWdTPhN8= github.com/Microsoft/go-winio v0.5.3-0.20220712145307-8fca75951feb/go.mod h1:9ZRWkpdsaDaHBql4MK5YereVcy6vkcO0xVhq5B1THlk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -188,6 +190,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1: github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kjk/lzma v0.0.0-20161016003348-3fd93898850d h1:RnWZeH8N8KXfbwMTex/KKMYMj0FJRCF6tQubUuQ02GM= +github.com/kjk/lzma v0.0.0-20161016003348-3fd93898850d/go.mod h1:phT/jsRPBAEqjAibu1BurrabCBNTYiVI+zbmyCZJY6Q= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -203,6 +207,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -222,6 +228,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -637,6 +645,10 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +pault.ag/go/debian v0.12.0 h1:b8ctSdBSGJ98NE1VLn06aSx70EUpczlP2qqSHEiYYJA= +pault.ag/go/debian v0.12.0/go.mod h1:UbnMr3z/KZepjq7VzbYgBEfz8j4+Pyrm2L5X1fzhy/k= +pault.ag/go/topsort v0.0.0-20160530003732-f98d2ad46e1a h1:WwS7vlB5H2AtwKj1jsGwp2ZLud1x6WXRXh2fXsRqrcA= +pault.ag/go/topsort v0.0.0-20160530003732-f98d2ad46e1a/go.mod h1:INqx0ClF7kmPAMk2zVTX8DRnhZ/yaA/Mg52g8KFKE7k= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/hashr.go b/hashr.go index c5fe9d4..3a6fd8a 100644 --- a/hashr.go +++ b/hashr.go @@ -26,6 +26,7 @@ import ( "github.com/google/hashr/core/hashr" gcpExporter "github.com/google/hashr/exporters/gcp" postgresExporter "github.com/google/hashr/exporters/postgres" + "github.com/google/hashr/importers/deb" "github.com/google/hashr/importers/gcp" "github.com/google/hashr/importers/targz" "github.com/google/hashr/importers/windows" @@ -41,7 +42,7 @@ import ( var ( processingWorkerCount = flag.Int("processing_worker_count", 2, "Number of processing workers.") - importersToRun = flag.String("importers", strings.Join([]string{}, ","), fmt.Sprintf("Importers to be run: %s,%s,%s,%s", gcp.RepoName, targz.RepoName, windows.RepoName, wsus.RepoName)) + importersToRun = flag.String("importers", strings.Join([]string{}, ","), fmt.Sprintf("Importers to be run: %s,%s,%s,%s,%s", gcp.RepoName, targz.RepoName, windows.RepoName, wsus.RepoName, deb.RepoName)) exportersToRun = flag.String("exporters", strings.Join([]string{}, ","), fmt.Sprintf("Exporters to be run: %s,%s", gcpExporter.Name, postgresExporter.Name)) jobStorage = flag.String("storage", "", "Storage that should be used for storing data about processing jobs, can have one of the two values: postgres, cloudspanner") cacheDir = flag.String("cache_dir", "/tmp/", "Path to cache dir used to store local cache.") @@ -69,6 +70,8 @@ var ( windowsRepoPath = flag.String("windows_iso_repo_path", "", "Path to Windows ISO repository.") // tarGz importer flags tarGzRepoPath = flag.String("targz_repo_path", "", "Path to TarGz repository.") + // deb importer flags + debRepoPath = flag.String("deb_repo_path", "", "Path to Deb repository.") ) func main() { @@ -123,6 +126,8 @@ func main() { } case targz.RepoName: importers = append(importers, targz.NewRepo(*tarGzRepoPath)) + case deb.RepoName: + importers = append(importers, deb.NewRepo(*debRepoPath)) } } diff --git a/importers/deb/deb.go b/importers/deb/deb.go new file mode 100644 index 0000000..37e955c --- /dev/null +++ b/importers/deb/deb.go @@ -0,0 +1,301 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package deb implements deb package importer. +package deb + +import ( + "archive/tar" + "crypto/sha256" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/golang/glog" + + "github.com/google/hashr/core/hashr" + "github.com/google/hashr/importers/common" + + "pault.ag/go/debian/deb" +) + +const ( + // RepoName contains the repository name. + RepoName = "deb" + chunkSize = 1024 * 1024 * 10 // 10MB +) + +// Archive holds data related to deb archive. +type Archive struct { + filename string + remotePath string + localPath string + quickSha256hash string + repoPath string +} + +func isSubElem(parent, sub string) (bool, error) { + up := ".." + string(os.PathSeparator) + + // path-comparisons using filepath.Abs don't work reliably according to docs (no unique representation). + rel, err := filepath.Rel(parent, sub) + if err != nil { + return false, err + } + if !strings.HasPrefix(rel, up) && rel != ".." { + return true, nil + } + return false, nil +} + +func extractTar(tarfile *tar.Reader, outputFolder string) error { + for { + header, err := tarfile.Next() + + if err == io.EOF { + break + } + + if err != nil { + return fmt.Errorf("error while unpacking deb package: %v", err) + } + + name := header.Name + + switch header.Typeflag { + case tar.TypeDir: + continue + case tar.TypeReg: + unpackPath := filepath.Join(outputFolder, name) + unpackFolder := filepath.Dir(unpackPath) + fmt.Printf("a: %s, b: %s", unpackFolder, unpackPath) + if _, err := os.Stat(unpackFolder); os.IsNotExist(err) { + if err2 := os.MkdirAll(unpackFolder, 0755); err2 != nil { + return fmt.Errorf("error while creating target directory: %v", err2) + } + } + + is_subelem, err := isSubElem(outputFolder, unpackPath) + if err != nil || !is_subelem { + return fmt.Errorf("error, deb package tried to unpack file above parent") + } + + unpackFileHandle, err := os.Create(unpackPath) + if err != nil { + return fmt.Errorf("error while creating destination file: %v", err) + } + defer unpackFileHandle.Close() + io.Copy(unpackFileHandle, tarfile) + + default: + fmt.Printf("Unknown tar entry type: %c in file %s\n", header.Typeflag, name) + } + } + + return nil +} + +func extractDeb(debPath, outputFolder string) error { + if _, err := os.Stat(outputFolder); os.IsNotExist(err) { + if err2 := os.MkdirAll(outputFolder, 0755); err2 != nil { + return fmt.Errorf("error while creating target directory: %v", err2) + } + } + + fd, err := os.Open(debPath) + if err != nil { + return fmt.Errorf("failed to open deb file: %v", err) + } + defer fd.Close() + + debFile, err := deb.Load(fd, debPath) + if err != nil { + return fmt.Errorf("failed to parse deb file: %v", err) + } + + for _, arEntry := range debFile.ArContent { + if !arEntry.IsTarfile() { + continue + } + + tarfile, closer, err := arEntry.Tarfile() + if err != nil { + return fmt.Errorf("error while opening tar archive in deb package: %v", err) + } + defer closer.Close() + + err = extractTar(tarfile, outputFolder) + if err != nil { + return err + } + } + + return nil +} + +// Preprocess extracts the contents of a .deb file. +func (a *Archive) Preprocess() (string, error) { + var err error + a.localPath, err = common.CopyToLocal(a.remotePath, a.ID()) + if err != nil { + return "", fmt.Errorf("error while copying %s to local file system: %v", a.remotePath, err) + } + + baseDir, _ := filepath.Split(a.localPath) + extractionDir := filepath.Join(baseDir, "extracted") + + if err := extractDeb(a.localPath, extractionDir); err != nil { + return "", err + } + + return extractionDir, nil +} + +// ID returns non-unique deb Archive ID. +func (a *Archive) ID() string { + return a.filename +} + +// RepoName returns repository name. +func (a *Archive) RepoName() string { + return RepoName +} + +// RepoPath returns repository path. +func (a *Archive) RepoPath() string { + return a.repoPath +} + +// LocalPath returns local path to a deb Archive .deb file. +func (a *Archive) LocalPath() string { + return a.localPath +} + +// RemotePath returns non-local path to a deb Archive .deb file. +func (a *Archive) RemotePath() string { + return a.remotePath +} + +// Description provides additional description for a .deb file. +func (a *Archive) Description() string { + return "" +} + +// QuickSHA256Hash calculates sha256 hash of .deb file. +func (a *Archive) QuickSHA256Hash() (string, error) { + // Check if the quick hash was already calculated. + if a.quickSha256hash != "" { + return a.quickSha256hash, nil + } + + f, err := os.Open(a.remotePath) + if err != nil { + return "", err + } + defer f.Close() + + fileInfo, err := f.Stat() + if err != nil { + return "", err + } + + // Check if the file is smaller than 20MB, if so hash the whole file. + if fileInfo.Size() < int64(chunkSize*2) { + h := sha256.New() + if _, err := io.Copy(h, f); err != nil { + return "", err + } + a.quickSha256hash = fmt.Sprintf("%x", h.Sum(nil)) + return a.quickSha256hash, nil + } + + header := make([]byte, chunkSize) + _, err = f.Read(header) + if err != nil { + return "", err + } + + footer := make([]byte, chunkSize) + _, err = f.ReadAt(footer, fileInfo.Size()-int64(chunkSize)) + if err != nil { + return "", err + } + + a.quickSha256hash = fmt.Sprintf("%x", sha256.Sum256(append(header, footer...))) + return a.quickSha256hash, nil +} + +// NewRepo returns new instance of deb repository. +func NewRepo(path string) *Repo { + return &Repo{location: path} +} + +// Repo holds data related to a deb repository. +type Repo struct { + location string + files []string + Archives []*Archive +} + +// RepoName returns repository name. +func (r *Repo) RepoName() string { + return RepoName +} + +// RepoPath returns repository path. +func (r *Repo) RepoPath() string { + return r.location +} + +// DiscoverRepo traverses the repository and looks for files that are related to deb archives. +func (r *Repo) DiscoverRepo() ([]hashr.Source, error) { + + if err := filepath.Walk(r.location, walk(&r.files)); err != nil { + return nil, err + } + + for _, file := range r.files { + _, filename := filepath.Split(file) + + if strings.HasSuffix(filename, ".deb") { + r.Archives = append(r.Archives, &Archive{filename: filename, remotePath: file, repoPath: r.location}) + } + } + + var sources []hashr.Source + for _, Archive := range r.Archives { + sources = append(sources, Archive) + } + + return sources, nil +} + +func walk(files *[]string) filepath.WalkFunc { + return func(path string, info os.FileInfo, err error) error { + if err != nil { + glog.Errorf("Could not open %s: %v", path, err) + return nil + } + if info.IsDir() { + return nil + } + if strings.HasSuffix(info.Name(), ".deb") { + *files = append(*files, path) + } + + return nil + } +} diff --git a/importers/deb/deb_test.go b/importers/deb/deb_test.go new file mode 100644 index 0000000..eda4a59 --- /dev/null +++ b/importers/deb/deb_test.go @@ -0,0 +1,434 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package deb + +import ( + "crypto/sha256" + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func sha256sum(path string) ([32]byte, error) { + data, err := ioutil.ReadFile(path) + if err != nil { + return [32]byte{}, err + } + + return sha256.Sum256(data), nil +} + +func testImages() ([]*Archive, error) { + debRepo := NewRepo("testdata") + gotSources, err := debRepo.DiscoverRepo() + if err != nil { + return nil, fmt.Errorf("unexpected error while discovering repo: %v", err) + } + + images := []*Archive{} + for _, source := range gotSources { + if image, ok := source.(*Archive); ok { + images = append(images, image) + } else { + return nil, errors.New("error while casting Source interface to Image struct") + } + } + + return images, nil +} + +func TestDiscover(t *testing.T) { + gotImages, err := testImages() + if err != nil { + t.Fatal(err) + } + + wantImages := []*Archive{ + { + filename: "ubuntu-desktop.deb", + remotePath: "testdata/20200106.00.00/ubuntu-desktop.deb", + repoPath: "testdata", + }, + { + filename: "ubuntu-laptop.deb", + remotePath: "testdata/20200106.00.00/ubuntu-laptop.deb", + repoPath: "testdata", + }, + { + filename: "ubuntu-server.deb", + remotePath: "testdata/20200106.00.00/ubuntu-server.deb", + repoPath: "testdata", + }, + { + filename: "ubuntu-desktop.deb", + remotePath: "testdata/20200107.00.00/ubuntu-desktop.deb", + repoPath: "testdata", + }, + { + filename: "ubuntu-laptop.deb", + remotePath: "testdata/20200107.00.00/ubuntu-laptop.deb", + repoPath: "testdata", + }, + { + filename: "ubuntu-server.deb", + remotePath: "testdata/20200107.00.00/ubuntu-server.deb", + repoPath: "testdata", + }, + { + filename: "ubuntu-desktop.deb", + remotePath: "testdata/20200107.01.00/ubuntu-desktop.deb", + repoPath: "testdata", + }, + { + filename: "ubuntu-laptop.deb", + remotePath: "testdata/20200107.01.00/ubuntu-laptop.deb", + repoPath: "testdata", + }, + { + filename: "ubuntu-server.deb", + remotePath: "testdata/20200107.01.00/ubuntu-server.deb", + repoPath: "testdata", + }, + { + filename: "ubuntu-desktop.deb", + remotePath: "testdata/20200108.00.00/ubuntu-desktop.deb", + repoPath: "testdata", + }, + { + filename: "ubuntu-laptop.deb", + remotePath: "testdata/20200108.00.00/ubuntu-laptop.deb", + repoPath: "testdata", + }, + { + filename: "ubuntu-server.deb", + remotePath: "testdata/20200108.00.00/ubuntu-server.deb", + repoPath: "testdata", + }, + } + + if !cmp.Equal(wantImages, gotImages, cmp.AllowUnexported(Archive{})) { + t.Errorf("Discovery() unexpected diff (-want/+got):\n%s", cmp.Diff(wantImages, gotImages, cmp.AllowUnexported(Archive{}))) + } +} + +func TestQuickHash(t *testing.T) { + gotImages, err := testImages() + if err != nil { + t.Fatal(err) + } + + gotHashes := make(map[string]string) + for _, image := range gotImages { + hash, _ := image.QuickSHA256Hash() + gotHashes[image.remotePath] = hash + } + + wantHashes := map[string]string{ + "testdata/20200106.00.00/ubuntu-desktop.deb": "46f4195eeee153f88d0661b8c4f2ae3b943548f1614ee608235cfdf734f16cc9", + "testdata/20200106.00.00/ubuntu-laptop.deb": "6671cc69fe1ab79b8522eb099b101adaf508395fab92d1c214044439e5d80596", + "testdata/20200106.00.00/ubuntu-server.deb": "1c210dd489e1246bc49d7a07a8d018d17846db22df721b7513c2928197649508", + "testdata/20200107.00.00/ubuntu-desktop.deb": "89e8e73527902f4fc727f543ba84dfaf174b27a1c2e691ed0bafc8d82a98874f", + "testdata/20200107.00.00/ubuntu-laptop.deb": "103cf8ee8b8b69b853cbe5d56eeb302ee555f8f837961fbb1935f513bd0374f0", + "testdata/20200107.00.00/ubuntu-server.deb": "25944c55094296c94ce3bd41d49b3db50737ecba3f6ab7d66081eb1ed492fa60", + "testdata/20200107.01.00/ubuntu-desktop.deb": "e5dfde39622e00d96c420f15625ebbb1263d3a6b7b2945675a1e1d86ebb93ad0", + "testdata/20200107.01.00/ubuntu-laptop.deb": "6a37c9a763dcc3952d4040cde74dea9dd98cc13290b4a22a9f35504be440de19", + "testdata/20200107.01.00/ubuntu-server.deb": "1b470be71f59a8f384dcb5c35232d20e41747af8c378c9f34d792c8741c8c71f", + "testdata/20200108.00.00/ubuntu-desktop.deb": "3371e25c4100130aade6d588f2b6af05f4b23f846d4977c3ea6f18f0d84e266f", + "testdata/20200108.00.00/ubuntu-laptop.deb": "c4ecda41be40868c172272fde62ab7c8408e401bcaa38aafe6a88340478bf052", + "testdata/20200108.00.00/ubuntu-server.deb": "94172f23e70c763949202b9df36d22c503319f70719f787460f70e4b5d6ad355", + } + + if !cmp.Equal(wantHashes, gotHashes) { + t.Errorf("QuickHash() unexpected diff (-want/+got):\n%s", cmp.Diff(wantHashes, gotHashes)) + } +} + +func TestPreprocess(t *testing.T) { + type extraction struct { + image string + files map[string]string + } + + var gotExtractions []extraction + + images, err := testImages() + if err != nil { + t.Fatal(err) + } + + for _, image := range images { + extractionDir, err := image.Preprocess() + if err != nil { + t.Fatalf("unexpected Preprocess() error: %v", err) + } + + var extractedFiles []string + err = filepath.Walk(extractionDir, func(path string, info os.FileInfo, err error) error { + if info.IsDir() { + return nil + } + extractedFiles = append(extractedFiles, path) + return nil + }) + if err != nil { + t.Fatalf("unexpected error while traversing extraction directory: %v", err) + } + + files := make(map[string]string) + for _, file := range extractedFiles { + hash, err := sha256sum(file) + if err != nil { + t.Fatalf("unexpected error while hashing extracted files: %v", err) + } + _, filename := filepath.Split(file) + files[filename] = fmt.Sprintf("%x", hash) + } + gotExtractions = append(gotExtractions, extraction{image: image.ID(), files: files}) + } + + wantExtractions := []extraction{ + { + image: "ubuntu-desktop.deb", + files: map[string]string{ + "file.01": "c2e7f7d23b30766c2d55e847b349d0540f4847b263ee15521dc72023846884ea", + "file.02": "a74bb803c7ff5bd875867fc3f4ceabb6fbe888eea6361b876111cb8060fe7e8c", + "file.03": "2789f4b90b038d57e592d01e0cd13a98b398cc7a524c3e8a7faaaaaf59893e7d", + "file.04": "efa02f852f81f973f2c10784bc5194de1d09f3e905ea296b22090ff3379ed6c1", + "file.05": "ddf7c381937d07c67e509f18feec42a939bddf2ea7db985a4b045d583c95ec04", + "file.06": "2fd1880876ca7640d04fae51fa988fe40505ab15f0f1a05ca6b0b5f09985c82a", + "file.07": "2cbbbd2fa4045f092ed359cd6632e01e1e45006681949aa98cee7aa0edc6f771", + "file.08": "8ab37107e0ed8d084afaf252fcdb2e66b99812ab12864b1cd12dfd5a44b25e5e", + "file.09": "952b39dff291f84b330206a1131c06592c8055800071fc84888c9a3052e51543", + "file.10": "79f5431b2eecae25c0b29ad8e5d8642d0575b015ed8008ff277dd2308cbdd173", + }, + }, + { + image: "ubuntu-laptop.deb", + files: map[string]string{ + "file.01": "8bc259fd7d49e3a94a2001e7ec276c51736a66167fc90e3453771b0e8e9fc17c", + "file.02": "3f1ee77b201b6c4f1c37872e363387b247415293853a3f7eed25effee396b68f", + "file.03": "b9b1fcb88ca7c884c4105c3f9e6f5c782521533ab529b84db41d82241a1b148e", + "file.04": "b6f453c6cb97193dbf52bdd8423d3c5f6308521af9254cd65f5cb9a777c6b203", + "file.05": "e6af44bf176b209b8ca050e7834aef2b1b6bcc292acde3c456a8a81d2d47c37c", + "file.06": "a649460a16c3a2d9a097f93e6e2d0c89c5a52ca5e1cc6d6ca03c64417905753d", + "file.07": "fa6d182f5bd8613830c118e0d7296baa59ae33b0d32a4557106cd15098b8bcf9", + "file.08": "f9788be264fc476a842f3e23950a1c0070b47948f95eeccc8c243c45afd62524", + "file.09": "d370aa6801e91d7fa48db7f4388c4e3858f58de124d9c4fd53cb9a25bbc2fa34", + "file.10": "b665f633c8f1e184972cb9ebc99490cf999fcae5d520fc8e144ee1124d05628b", + }, + }, + { + image: "ubuntu-server.deb", + files: map[string]string{ + "file.01": "c4013088429ec4cbb0b8c6e62c8260081200f14f427c3f6def1892ea17a1e1d2", + "file.02": "db378bba4f2aeb9dd8bf155053d188d471dea2aa1eeee867eba35859b3231248", + "file.03": "aa7178c790d844d39d5950ffc8e4b90e6b6402fd8ab61d3ec2140b9c019f71f3", + "file.04": "bf28724a6549faeeaf7713cf84028c4a67710a083d38e9085fbb79ac99c0665d", + "file.05": "8ce9373260d0332f867986ce93ba7ee27ca1177f40262da2532c27be2e2c2e3b", + "file.06": "0673ad1ccd50967bd6832050a08a0df5d3a4c0dba71faaa3f887c2fa69ce8371", + "file.07": "f354a000db035fbd924d6be528ce4863fa9596c012f18545fe08f64aa00e2791", + "file.08": "129c8674ec47634e806e7ae3fda02f32ab227dabc56bcb73e7a8441dddb1077c", + "file.09": "67eae811b446beb5ed2d16277731203f472de1a957d1580f6a4275d4475050b1", + "file.10": "2d6538330f5849f3a614c1db845c72ca8312ea20122f94ea7bd2390bbebdd600", + }, + }, + { + image: "ubuntu-desktop.deb", + files: map[string]string{ + "file.01": "13911129c994f843fed92c6deab2d3ae6644d6bee801726b316555d1810c70d5", + "file.02": "516532ef7e7dbe21befe447e77a0be12debfe8d582990c75fd7d38757b6f891c", + "file.03": "faba24a2bf832b241b3278e49619dba4c3e5c67c12caf2d6df24f0d8a734b150", + "file.04": "4aee66f73d018cd369ef77a8db2e77250587613259e959a2d595a59a04dd8a30", + "file.05": "9ba07fbc067bb8b9d2bed6f808a45e9e3dd321e3dfd1e4d9dcce10753f5b9b51", + "file.06": "f8035b6497eb22ecabd90f88aed82328cdd7f2daab1b53959f665cdd74a0431e", + "file.07": "beff39f558826a8349fe6243785669ebfa5f57d47261f3d683ee17fb9be6566a", + "file.08": "4570a774b4b285c514348ead11078325ff6773c9e5cb8207642404c99df98740", + "file.09": "d5ae5038f2909618d2bd64c409d3b0ac8f0de3ab8a2d45b974da02633b9f0e40", + "file.10": "89811409c6ad0b14a8a773c92a5a28971a70a6685031a57c8bd6cbf6a80f7be5", + }, + }, + { + image: "ubuntu-laptop.deb", + files: map[string]string{ + "file.01": "280fc584e14a2b8c74aa2c21d33300d4ae97703087a12cea845229aaf7b9abc1", + "file.02": "f3afff3f52163c66e72cfade56b210350bf4095bb377d54cb6d09fbaa8f48128", + "file.03": "6e2c2e9df023d5ea5919e630bfbf070a07226d1b8cd8495f312e3716050c3860", + "file.04": "9cea0c8afccf5703c79551bd3fa03eba3f2d86fbd26b07f8441bcb096cbbb6f4", + "file.05": "592c25efd491ae7d78d7146f4b8043d930feb2d8f80e424b701b2cb31aaeffc7", + "file.06": "d3df0f5cb1cb8889ff9073552980d883e40cbffed16ec0a3abff9449b2138030", + "file.07": "ff4ae7dc0c5b1461560f35b9ce4714a8f43e3d711da5d7d10a795fac8265b244", + "file.08": "c8617057c36c6d796684b8b371d404392b4acbbe27504292d1572cd316b426ac", + "file.09": "e3528da985f1233116d0140c93f110e7a1416e6924716b38a1b02df96f39ab20", + "file.10": "ab5da92f8e8a364dd43c7110438d65831a5a4efb8be1c86a1ce2e15844cf95ee", + }, + }, + { + image: "ubuntu-server.deb", + files: map[string]string{ + "file.01": "09b1fec24c1e8c22a186b639383a86d577da8007642af3db509becac512eeb4c", + "file.02": "f61a659e7463abb181db43b7185caf1c44c4229cf4c970b2ef843500e2924592", + "file.03": "8fce39696330405e3f5befdb18a92ba8aacc12556f7ddf4053367ba617679dd0", + "file.04": "78747c0da578c721411d4e725d0b906ac57b27c473657f5aea64a9498f438381", + "file.05": "80cc1c37e306d63567bc92c4b026b858744f5baa90806d6ef39215231a900f0b", + "file.06": "5749e9588919dfe8912694c7e67ead1ace8edbc854a8d4257a882ffba2efd904", + "file.07": "4eb1ec9cc85ff46cc6390943882d6794828431f6ddf0b28555b456cb83328919", + "file.08": "ad7a7ca2679ff5257e834ae4acda9a1a1d6b048c441c088137a5059352fce3f5", + "file.09": "610a552ab8b44dbc6aebfc548ba289a4b5b6c1e9e9d530aae728c9a3867db469", + "file.10": "c1650357d2a44187e9cd489dccf902a53fdded14034ff7af788587a43336e2c1", + }, + }, + { + image: "ubuntu-desktop.deb", + files: map[string]string{ + "file.01": "3e720d724145502b0761501f0d918eb45a15434774eee02f8f6a6ddc87eaa2cf", + "file.02": "2cdea17b0023f6f6292aa1c5141b6accd547eb32a4ea073e2517805ca0900747", + "file.03": "7fa2c5c87edecfc5f07739ca0dad5d140f922161fb2e5a8caf136e7db8bce91c", + "file.04": "8bdb6d28e04d7711a6c6d7693ddb2376a112dac4846a8367debb2848847e03c6", + "file.05": "1bd5c4eaf72be2dc3309469b55409c76aa092ee4e683a8c9eda06a04e15894d4", + "file.06": "79f7a9846974597b598d864bea012c7832e118912693d59523363df3ee52d5e1", + "file.07": "972d269f3f9a26581afa646de77f7698da3e990d30e7b5b1fbad66eecf07b803", + "file.08": "ec8284d58abefc0a82e490641031d4a88cee7d8bdc09cef7b5fd8f18dede6e8a", + "file.09": "8a95a96a002834ceedb1dcf7c10c8b2010f27a946ebb14f75b63f24eee8789e2", + "file.10": "26a691931baa193647481207686b92b58e7064520d604014ba72afdc12f218cd", + }, + }, + { + image: "ubuntu-laptop.deb", + files: map[string]string{ + "file.01": "ff9a95c3c460d65453a3abc30126758c07b4d35a960c11e38fb801c9fb76b383", + "file.02": "753253fe1cf63099e5c05f32b9ff2d7def422814e12298eac4beec6d85dccb60", + "file.03": "3dcb6600b7a12863a2c5bd183731724819a70cc7513e5a78db4278841a95b2eb", + "file.04": "4f79ac7abf5cca0b842f73c96a60e129613f6815a3ffa3b1af4cfa5db8c7ab80", + "file.05": "ac809183432d85d686dbb1ffa4d6261641a31b2a77b494941f14fa2e4e0b9315", + "file.06": "e92f7e78485166d9882980fce6a78ef73bd23f30902526dedb30ad08f4164809", + "file.07": "f0169ce59a87329aaec3d7dda41cb3214091086cf2c5d28a7d5deab44e0db8ef", + "file.08": "da3a5df23487dcef33b16f7f01e20325b646819489fae3cdc2e8f34da4d3f2cd", + "file.09": "dcc1e672611fc4d5ddbe5f9df041cefc90dc5706535ebf39c5a0a146622db33e", + "file.10": "704db5c084a9471627ae9a02cf430aad0b05c1cef8c28758536ff4469d8bb009", + }, + }, + { + image: "ubuntu-server.deb", + files: map[string]string{ + "file.01": "df5d51ea487e697da38a1ae8371507798459df3452d34b2b5b96a70ca22d8d05", + "file.02": "a868d4cc1a81176dfead1e3fb778c16be541c32760dd8bc41e0ede6d0b0137af", + "file.03": "e33b1aec41f596bd0d02e5ef60350166fef83c3e65ea968286e4bd66e64ad03c", + "file.04": "14f0c6763b8c50703547bf1ddd6dc367238e0592c4142115378ea72daa86655c", + "file.05": "323259850a60979ae147edaf877447c456d6f0cfa96e91e919d175066340cc99", + "file.06": "24ac3b8a9c1b9e3e48862f5253aa895ae1a2c70b24108c6e3f280f92e8f28b85", + "file.07": "fc08b4aeea6748f0c5d1a2bdc751bd93cc3d72a59214c91a201edcaf6c5deea4", + "file.08": "5de2e0bbfbb17152a59c480974593d306947146df873be5a52162db9261496bb", + "file.09": "4047ba956f12271ab680c7039ec6271acc168dfe88cde1938ee7066c61de62db", + "file.10": "dbf21781a7f20f1651cb7af67aeb82d2382e713c2a6bc41e9906679ef2463ba4", + }, + }, + { + image: "ubuntu-desktop.deb", + files: map[string]string{ + "file.01": "9f114adf6f76f6c2f403699c31b8eadb168c5fd0ee71e423ae29b99251c2595e", + "file.02": "4878dd6c7af7fecdf89832384d84ed93b781d3e69e6a0097efac5320da2ac637", + "file.03": "9862cfa4e6b71b6d11e13c5392cd3607c96a279e1e22a4535cf4d019055494d1", + "file.04": "0a632850049f80763ada81ec0cacf015dbd67fb1b956ec2acb8aa862e511b3bc", + "file.05": "471ed5b2f059540ae4d8586a47329272c774561fc1a9c15fb22f2d7939b6820b", + "file.06": "b8862d9e62c15c73527ca72b4e5e85809d4254326800eb2c65b35339029e02d1", + "file.07": "733dac1b5272600122760db1f917bb4729d69e5a69f039797f75e685a1152bf0", + "file.08": "c8c6fb7771c086f786ea8d60bf703bbeea3ed7984ce962493994493839f70713", + "file.09": "d5de3fe6a4559c59ad103ab40e01c4fc0df7eb8ba901d50e5ceae3909b2e0d61", + "file.10": "05d1223b8b287684ce015d918b90555f9cb4c753558d511d179ada227820f6ba", + }, + }, + { + image: "ubuntu-laptop.deb", + files: map[string]string{ + "file.01": "db391f171b17f750e440c6311b8ef618705d14f0de99eedff954b318967d9e1a", + "file.02": "f99254f3f48cf49195f0de6ce59257634e2c9a1c251c7013d7ba246ed6ec841d", + "file.03": "d15a7d5f9731738452cd67b6bfa035e4b12b3d934d3b47d37c1c8273eeaa23fd", + "file.04": "15a11e8e69ba8203df18c1f2baaee4ffb7de4be7d4abb4bd86d7e232231aeaae", + "file.05": "c84f7f5db9e404bafe87bc4123474801486ad8f65ca63de4f790bb885c99e25d", + "file.06": "ff0ca17a9756bb3d91b748887ef2d5fe7e97137e3f5b1f6b8ec6cc7fe369f8fd", + "file.07": "229faa1edf71fa3b221d17f5d90d852029c2559f91f53eb464036d9b0a197eee", + "file.08": "c8fc201d41aeb679d8da4708491e7ed4ba3a4e3be85c50210b08a2372284624a", + "file.09": "ba1b175b82ea3493bb2c0c0bca529aaeb42ce5cf6888cb09f7df8e87a639beae", + "file.10": "7ca80a863d5c935068511d939ad8857f6c0e1fe1bf62ff4160b1a78bba84e3ba", + }, + }, + { + image: "ubuntu-server.deb", + files: map[string]string{ + "file.01": "9076527a1200b53570b8719f0049b8544c7366003c355cd740858fd3569729f6", + "file.02": "78d78a3d686c9580a0fb123894f16156a822261a30499f3e8d34cad77496e0fc", + "file.03": "ebb15f223ca81484e714a08ff93689189214fbb1003f12e007fd8deb98fd6b35", + "file.04": "cd1be623861c325203d1582826a5bffdbc732fa5f86984d8422c2623d45c3164", + "file.05": "207b2c6bc867f366e2f7d7607662bd9329e84c68a97affba23963e9a4245ef01", + "file.06": "f481bbd1719e1adb5446b1cb0df10b7564edfdc170cc5e2afa4b72b74a56788c", + "file.07": "807034689f2c2b179b60692162bf99eb390d39a2b39e6897949b030805e953d2", + "file.08": "3665d702279c54659944ba9def060306f261d2706793ddbc9f6214a377c36a4c", + "file.09": "ebd093091d65f012ebb1c0d6128334984924132e865a98b6fa6349f143102811", + "file.10": "40998bbd7e456cb57beacc7006eb5c949160527fb6589c8fce3b1f49a1a9f3bd", + }, + }, + } + + if !cmp.Equal(wantExtractions, gotExtractions, cmp.AllowUnexported(extraction{})) { + t.Errorf("Preprocess() unexpected diff (-want/+got):\n%s", cmp.Diff(wantExtractions, gotExtractions, cmp.AllowUnexported(extraction{}))) + } +} + +func TestImageFunctions(t *testing.T) { + id := "ubuntu-desktop.deb" + repoPath := "/tmp/deb-repo" + localTarGzPath := "/tmp/deb-repo/20200108.00.00-ubuntu-desktop.deb" + remotePath := "/share/deb-repo/20200108.00.00-ubuntu-desktop.deb" + + img := Archive{filename: id, localPath: localTarGzPath, remotePath: remotePath, repoPath: repoPath} + + if img.ID() != id { + t.Errorf("ID() = %s; want = %s", img.ID(), id) + } + + if img.RepoName() != RepoName { + t.Errorf("RepoName() = %s; want = %s", img.RepoName(), RepoName) + } + + if img.RepoPath() != repoPath { + t.Errorf("RepoPath() = %s; want = %s", img.RepoPath(), repoPath) + } + + if img.LocalPath() != localTarGzPath { + t.Errorf("LocalPath() = %s; want = %s", img.LocalPath(), localTarGzPath) + } + + if img.RemotePath() != remotePath { + t.Errorf("RemotePath() = %s; want = %s", img.RemotePath(), remotePath) + } +} + +func TestRepoFunctions(t *testing.T) { + repoPath := "/tmp/deb-repo" + repo := NewRepo(repoPath) + + if repo.RepoName() != RepoName { + t.Errorf("RepoName() = %s; want = %s", repo.RepoName(), RepoName) + } + + if repo.RepoPath() != repoPath { + t.Errorf("RepoPath() = %s; want = %s", repo.RepoPath(), repoPath) + } +} diff --git a/importers/deb/generate_tests.sh b/importers/deb/generate_tests.sh new file mode 100755 index 0000000..7c9d4bf --- /dev/null +++ b/importers/deb/generate_tests.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +for tar in $(find -name '*.tar.gz'); do + echo "$tar" + filename=$(basename "$tar") + tardir=$(dirname "$tar") + tempdir=$(mktemp -d) + tar -C "$tempdir" -xf "$tar" + mkdir -p "$tempdir/DEBIAN" + + cat < "$tempdir/DEBIAN/control" +Package: hashr-testdata +Version: 1.0 +Architecture: arm64 +Maintainer: Example +Description: This text does not matter. +EOF + dpkg-deb --build --root-owner-group "$tempdir" + rm -r "$tempdir" + cp "$tempdir.deb" "$tardir/$(echo "$filename" | sed 's/.tar.gz/.deb/g')" + rm "$tar" +done diff --git a/importers/deb/testdata/20200106.00.00/ubuntu-desktop.deb b/importers/deb/testdata/20200106.00.00/ubuntu-desktop.deb new file mode 100644 index 0000000..e40bd2c Binary files /dev/null and b/importers/deb/testdata/20200106.00.00/ubuntu-desktop.deb differ diff --git a/importers/deb/testdata/20200106.00.00/ubuntu-laptop.deb b/importers/deb/testdata/20200106.00.00/ubuntu-laptop.deb new file mode 100644 index 0000000..ece5d19 Binary files /dev/null and b/importers/deb/testdata/20200106.00.00/ubuntu-laptop.deb differ diff --git a/importers/deb/testdata/20200106.00.00/ubuntu-server.deb b/importers/deb/testdata/20200106.00.00/ubuntu-server.deb new file mode 100644 index 0000000..fb7bd36 Binary files /dev/null and b/importers/deb/testdata/20200106.00.00/ubuntu-server.deb differ diff --git a/importers/deb/testdata/20200107.00.00/ubuntu-desktop.deb b/importers/deb/testdata/20200107.00.00/ubuntu-desktop.deb new file mode 100644 index 0000000..eb14750 Binary files /dev/null and b/importers/deb/testdata/20200107.00.00/ubuntu-desktop.deb differ diff --git a/importers/deb/testdata/20200107.00.00/ubuntu-laptop.deb b/importers/deb/testdata/20200107.00.00/ubuntu-laptop.deb new file mode 100644 index 0000000..302d610 Binary files /dev/null and b/importers/deb/testdata/20200107.00.00/ubuntu-laptop.deb differ diff --git a/importers/deb/testdata/20200107.00.00/ubuntu-server.deb b/importers/deb/testdata/20200107.00.00/ubuntu-server.deb new file mode 100644 index 0000000..cfd075b Binary files /dev/null and b/importers/deb/testdata/20200107.00.00/ubuntu-server.deb differ diff --git a/importers/deb/testdata/20200107.01.00/ubuntu-desktop.deb b/importers/deb/testdata/20200107.01.00/ubuntu-desktop.deb new file mode 100644 index 0000000..d122bea Binary files /dev/null and b/importers/deb/testdata/20200107.01.00/ubuntu-desktop.deb differ diff --git a/importers/deb/testdata/20200107.01.00/ubuntu-laptop.deb b/importers/deb/testdata/20200107.01.00/ubuntu-laptop.deb new file mode 100644 index 0000000..6ab0ac3 Binary files /dev/null and b/importers/deb/testdata/20200107.01.00/ubuntu-laptop.deb differ diff --git a/importers/deb/testdata/20200107.01.00/ubuntu-server.deb b/importers/deb/testdata/20200107.01.00/ubuntu-server.deb new file mode 100644 index 0000000..23bbf23 Binary files /dev/null and b/importers/deb/testdata/20200107.01.00/ubuntu-server.deb differ diff --git a/importers/deb/testdata/20200108.00.00/ubuntu-desktop.deb b/importers/deb/testdata/20200108.00.00/ubuntu-desktop.deb new file mode 100644 index 0000000..1bf459d Binary files /dev/null and b/importers/deb/testdata/20200108.00.00/ubuntu-desktop.deb differ diff --git a/importers/deb/testdata/20200108.00.00/ubuntu-laptop.deb b/importers/deb/testdata/20200108.00.00/ubuntu-laptop.deb new file mode 100644 index 0000000..aa096e3 Binary files /dev/null and b/importers/deb/testdata/20200108.00.00/ubuntu-laptop.deb differ diff --git a/importers/deb/testdata/20200108.00.00/ubuntu-server.deb b/importers/deb/testdata/20200108.00.00/ubuntu-server.deb new file mode 100644 index 0000000..e5a31b2 Binary files /dev/null and b/importers/deb/testdata/20200108.00.00/ubuntu-server.deb differ