Skip to content

Commit a723b9e

Browse files
author
Erik Hollensbe
committed
Archive and storage tests
Signed-off-by: Erik Hollensbe <[email protected]>
1 parent b1b1dbc commit a723b9e

File tree

2 files changed

+253
-1
lines changed

2 files changed

+253
-1
lines changed

controllers/storage.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import (
4040
const (
4141
excludeFile = ".sourceignore"
4242
excludeVCS = ".git/,.gitignore,.gitmodules,.gitattributes"
43-
excludeExt = "*.jpg,*.jpeg,*.gif,*.png,*.wmv,.*flv,.*tar.gz,*.zip"
43+
excludeExt = "*.jpg,*.jpeg,*.gif,*.png,*.wmv,*.flv,*.tar.gz,*.zip"
4444
)
4545

4646
// Storage manages artifacts

controllers/storage_test.go

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
package controllers
2+
3+
import (
4+
"archive/tar"
5+
"compress/gzip"
6+
"fmt"
7+
"io"
8+
"io/ioutil"
9+
"os"
10+
"os/exec"
11+
"path/filepath"
12+
"testing"
13+
"time"
14+
15+
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
16+
)
17+
18+
type ignoreMap map[string]bool
19+
20+
var remoteRepository = "https://github.com/fluxcd/source-controller"
21+
22+
func init() {
23+
// if this remote repo ever gets in your way, this is an escape; just set
24+
// this to the url you want to clone. Be the source you want to be.
25+
s := os.Getenv("REMOTE_REPOSITORY")
26+
if s != "" {
27+
remoteRepository = s
28+
}
29+
}
30+
31+
func createStoragePath() (string, error) {
32+
return ioutil.TempDir("", "")
33+
}
34+
35+
func cleanupStoragePath(dir string) func() {
36+
return func() { os.RemoveAll(dir) }
37+
}
38+
39+
func TestStorageConstructor(t *testing.T) {
40+
dir, err := createStoragePath()
41+
if err != nil {
42+
t.Fatal(err)
43+
}
44+
t.Cleanup(cleanupStoragePath(dir))
45+
46+
if _, err := NewStorage("/nonexistent", "hostname", time.Minute); err == nil {
47+
t.Fatal("nonexistent path was allowable in storage constructor")
48+
}
49+
50+
f, err := ioutil.TempFile(dir, "")
51+
if err != nil {
52+
t.Fatalf("while creating temporary file: %v", err)
53+
}
54+
f.Close()
55+
56+
if _, err := NewStorage(f.Name(), "hostname", time.Minute); err == nil {
57+
t.Fatal("file path was accepted as basedir")
58+
}
59+
60+
os.Remove(f.Name())
61+
62+
if _, err := NewStorage(dir, "hostname", time.Minute); err != nil {
63+
t.Fatalf("Valid path did not successfully return: %v", err)
64+
}
65+
}
66+
67+
func artifactFromURLRepository(repo string) sourcev1.Artifact {
68+
f, err := ioutil.TempFile("", "")
69+
if err != nil {
70+
panic(fmt.Errorf("could not create temporary file: %w", err))
71+
}
72+
f.Close()
73+
os.Remove(f.Name())
74+
75+
return sourcev1.Artifact{Path: f.Name(), URL: repo}
76+
}
77+
78+
// walks a tar.gz and looks for paths with the basename. It does not match
79+
// symlinks properly at this time because that's painful.
80+
func walkTar(tarFile string, match string) (bool, error) {
81+
f, err := os.Open(tarFile)
82+
if err != nil {
83+
return false, fmt.Errorf("could not open file: %w", err)
84+
}
85+
defer f.Close()
86+
87+
gzr, err := gzip.NewReader(f)
88+
if err != nil {
89+
return false, fmt.Errorf("could not unzip file: %w", err)
90+
}
91+
defer gzr.Close()
92+
93+
tr := tar.NewReader(gzr)
94+
for {
95+
header, err := tr.Next()
96+
if err == io.EOF {
97+
break
98+
} else if err != nil {
99+
return false, fmt.Errorf("Corrupt tarball reading header: %w", err)
100+
}
101+
102+
switch header.Typeflag {
103+
case tar.TypeDir, tar.TypeReg:
104+
if filepath.Base(header.Name) == match {
105+
return true, nil
106+
}
107+
default:
108+
// skip
109+
}
110+
}
111+
112+
return false, nil
113+
}
114+
115+
func testPatterns(t *testing.T, artifact sourcev1.Artifact, table ignoreMap) {
116+
for name, expected := range table {
117+
res, err := walkTar(artifact.Path, name)
118+
if err != nil {
119+
t.Fatalf("while reading tarball: %v", err)
120+
}
121+
122+
if res != expected {
123+
if expected {
124+
t.Fatalf("Could not find repository file matching %q in tarball for repo %q", name, remoteRepository)
125+
} else {
126+
t.Fatalf("Repository contained ignored file %q in tarball for repo %q", name, remoteRepository)
127+
}
128+
}
129+
}
130+
}
131+
132+
func createArchive(t *testing.T, filenames []string, sourceIgnore string, spec sourcev1.GitRepositorySpec) sourcev1.Artifact {
133+
dir, err := createStoragePath()
134+
if err != nil {
135+
t.Fatal(err)
136+
}
137+
t.Cleanup(cleanupStoragePath(dir))
138+
139+
storage, err := NewStorage(dir, "hostname", time.Minute)
140+
if err != nil {
141+
t.Fatalf("Error while bootstrapping storage: %v", err)
142+
}
143+
144+
gitDir, err := ioutil.TempDir("", "")
145+
if err != nil {
146+
t.Fatalf("could not create temporary directory: %v", err)
147+
}
148+
t.Cleanup(func() { os.RemoveAll(gitDir) })
149+
150+
if err := exec.Command("git", "clone", remoteRepository, gitDir).Run(); err != nil {
151+
t.Fatalf("Could not clone remote repository: %v", err)
152+
}
153+
154+
// inject files.. just empty files
155+
for _, name := range filenames {
156+
f, err := os.Create(filepath.Join(gitDir, name))
157+
if err != nil {
158+
t.Fatalf("Could not inject filename %q: %v", name, err)
159+
}
160+
f.Close()
161+
}
162+
163+
// inject sourceignore if not empty
164+
if sourceIgnore != "" {
165+
si, err := os.Create(filepath.Join(gitDir, ".sourceignore"))
166+
if err != nil {
167+
t.Fatalf("Could not create .sourceignore: %v", err)
168+
}
169+
170+
if _, err := io.WriteString(si, sourceIgnore); err != nil {
171+
t.Fatalf("Could not write to .sourceignore: %v", err)
172+
}
173+
174+
si.Close()
175+
}
176+
artifact := artifactFromURLRepository(remoteRepository)
177+
178+
if err := storage.Archive(artifact, gitDir, spec); err != nil {
179+
t.Fatalf("basic archive case failed: %v", err)
180+
}
181+
182+
if !storage.ArtifactExist(artifact) {
183+
t.Fatalf("artifact was created but does not exist: %+v", artifact)
184+
}
185+
186+
return artifact
187+
}
188+
189+
func stringPtr(s string) *string {
190+
return &s
191+
}
192+
193+
func TestArchiveBasic(t *testing.T) {
194+
table := ignoreMap{
195+
"README.md": true,
196+
".gitignore": false,
197+
}
198+
199+
testPatterns(t, createArchive(t, []string{"README.md", ".gitignore"}, "", sourcev1.GitRepositorySpec{}), table)
200+
}
201+
202+
func TestArchiveIgnore(t *testing.T) {
203+
// this is a list of files that will be created in the repository for each
204+
// subtest. it is manipulated later on.
205+
filenames := []string{
206+
"foo.tar.gz",
207+
"bar.jpg",
208+
"bar.gif",
209+
"foo.jpeg",
210+
"video.flv",
211+
"video.wmv",
212+
"bar.png",
213+
"foo.zip",
214+
}
215+
216+
// this is the table of ignored files and their values. true means that it's
217+
// present in the resulting tarball.
218+
table := ignoreMap{}
219+
for _, item := range filenames {
220+
table[item] = false
221+
}
222+
223+
t.Run("automatically ignored files", func(t *testing.T) {
224+
testPatterns(t, createArchive(t, filenames, "", sourcev1.GitRepositorySpec{}), table)
225+
})
226+
227+
table = ignoreMap{}
228+
for _, item := range filenames {
229+
table[item] = true
230+
}
231+
232+
t.Run("only vcs ignored files", func(t *testing.T) {
233+
testPatterns(t, createArchive(t, filenames, "", sourcev1.GitRepositorySpec{SourceIgnore: stringPtr("")}), table)
234+
})
235+
236+
filenames = append(filenames, "test.txt")
237+
table["test.txt"] = false
238+
sourceIgnoreFile := "*.txt"
239+
240+
t.Run("sourceignore injected via CRD", func(t *testing.T) {
241+
testPatterns(t, createArchive(t, filenames, "", sourcev1.GitRepositorySpec{SourceIgnore: stringPtr(sourceIgnoreFile)}), table)
242+
})
243+
244+
table = ignoreMap{}
245+
for _, item := range filenames {
246+
table[item] = false
247+
}
248+
249+
t.Run("sourceignore injected via filename", func(t *testing.T) {
250+
testPatterns(t, createArchive(t, filenames, sourceIgnoreFile, sourcev1.GitRepositorySpec{}), table)
251+
})
252+
}

0 commit comments

Comments
 (0)