Skip to content

Commit cdd4d36

Browse files
authored
Merge pull request #108 from tri-adam/build-context
Build Context Upload/Delete
2 parents 32f3063 + c18651e commit cdd4d36

File tree

14 files changed

+962
-4
lines changed

14 files changed

+962
-4
lines changed

client/archive.go

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// Copyright (c) 2022, Sylabs Inc. All rights reserved.
2+
// This software is licensed under a 3-clause BSD license. Please consult the
3+
// LICENSE.md file distributed with the sources of this project regarding your
4+
// rights to use or distribute this software.
5+
6+
package client
7+
8+
import (
9+
"archive/tar"
10+
"errors"
11+
"fmt"
12+
"io"
13+
"io/fs"
14+
"path"
15+
"path/filepath"
16+
"strings"
17+
)
18+
19+
type archiver struct {
20+
fs fs.FS
21+
w *tar.Writer
22+
archived map[string]struct{}
23+
}
24+
25+
// newArchiver returns an archiver that will write an archive to w.
26+
func newArchiver(fsys fs.FS, w io.Writer) *archiver {
27+
return &archiver{
28+
fs: fsys,
29+
w: tar.NewWriter(w),
30+
archived: make(map[string]struct{}),
31+
}
32+
}
33+
34+
var errUnsupportedType = errors.New("unsupported file type")
35+
36+
// writeEntry writes the named path from the file system to the archive.
37+
func (ar *archiver) writeEntry(name string) (err error) {
38+
// If entry already exists, skip it.
39+
if _, ok := ar.archived[name]; ok {
40+
return nil
41+
}
42+
defer func() {
43+
if err == nil {
44+
ar.archived[name] = struct{}{}
45+
}
46+
}()
47+
48+
// Get file info.
49+
fi, err := fs.Stat(ar.fs, name)
50+
if err != nil {
51+
return err
52+
}
53+
54+
// Populate TAR header based on file info, and normalize name.
55+
h, err := tar.FileInfoHeader(fi, "")
56+
if err != nil {
57+
return err
58+
}
59+
h.Name = filepath.ToSlash(name)
60+
61+
// Check that we're writing a supported type, and make any necessary adjustments.
62+
switch h.Typeflag {
63+
case tar.TypeReg:
64+
// Nothing to do.
65+
66+
case tar.TypeSymlink:
67+
// Always follow symbolic links.
68+
h.Typeflag = tar.TypeReg
69+
h.Linkname = ""
70+
h.Size = fi.Size()
71+
72+
case tar.TypeDir:
73+
// Normalize name.
74+
if !strings.HasSuffix(h.Name, "/") {
75+
h.Name += "/"
76+
}
77+
78+
default:
79+
return fmt.Errorf("%v: %w (%v)", name, errUnsupportedType, h.Typeflag)
80+
}
81+
82+
// Write TAR header.
83+
if err := ar.w.WriteHeader(h); err != nil {
84+
return err
85+
}
86+
87+
// Write file contents, if applicable.
88+
if h.Typeflag == tar.TypeReg && h.Size > 0 {
89+
f, err := ar.fs.Open(name)
90+
if err != nil {
91+
return err
92+
}
93+
defer f.Close()
94+
95+
if _, err := io.Copy(ar.w, f); err != nil {
96+
return err
97+
}
98+
}
99+
100+
return nil
101+
}
102+
103+
// walkDirFunc returns a WalkDirFunc that writes each path to ar.
104+
func (ar *archiver) walkDirFunc() fs.WalkDirFunc {
105+
return func(path string, d fs.DirEntry, err error) error {
106+
if err != nil {
107+
return err
108+
}
109+
110+
if path == "." {
111+
return nil
112+
}
113+
114+
return ar.writeEntry(path)
115+
}
116+
}
117+
118+
// writeDirAll writes an entry for directory name to the archive, along with any necessary parents.
119+
func (ar *archiver) writeDirAll(name string) error {
120+
if name == "." {
121+
return nil
122+
}
123+
124+
if _, ok := ar.archived[name]; ok {
125+
return nil // Path already archived, no need to recurse.
126+
}
127+
128+
// Recursively create parent(s).
129+
if err := ar.writeDirAll(path.Dir(name)); err != nil {
130+
return err
131+
}
132+
133+
return ar.writeEntry(name)
134+
}
135+
136+
// WriteFiles writes all files matching pattern from the file system to the archive. If the named
137+
// path is a directory, its contents are recursively added using fs.WalkDir.
138+
func (ar *archiver) WriteFiles(pattern string) error {
139+
names, err := fs.Glob(ar.fs, pattern)
140+
if err != nil {
141+
return err
142+
}
143+
144+
if len(names) == 0 {
145+
return fmt.Errorf("%v: %w", pattern, fs.ErrNotExist)
146+
}
147+
148+
for _, name := range names {
149+
// Ensure parent directory exists in archive.
150+
if err := ar.writeDirAll(path.Dir(name)); err != nil {
151+
return err
152+
}
153+
154+
fi, err := fs.Stat(ar.fs, name)
155+
if err != nil {
156+
return err
157+
}
158+
159+
// If name refers to a directory, walk it, adding entries. Otherwise, add a single entry.
160+
if fi.IsDir() {
161+
if err := fs.WalkDir(ar.fs, name, ar.walkDirFunc()); err != nil {
162+
return err
163+
}
164+
} else if err := ar.writeEntry(name); err != nil {
165+
return err
166+
}
167+
}
168+
169+
return nil
170+
}
171+
172+
// Close closes the archive.
173+
func (ar *archiver) Close() error {
174+
return ar.w.Close()
175+
}

0 commit comments

Comments
 (0)