Skip to content
This repository was archived by the owner on Dec 5, 2025. It is now read-only.

Commit c4c52fc

Browse files
committed
Initial release
Signed-off-by: Tom Duffield <tom@chef.io>
1 parent 212fe1a commit c4c52fc

File tree

7 files changed

+629
-0
lines changed

7 files changed

+629
-0
lines changed

archive.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package filesystem
2+
3+
import (
4+
"github.com/mholt/archiver"
5+
)
6+
7+
type Archiver interface {
8+
Unarchive(source string, destination string) error
9+
}
10+
11+
type Unarchiver struct{}
12+
13+
func (a *Unarchiver) Unarchive(source string, destination string) error {
14+
return archiver.Unarchive(source, destination)
15+
}

filesystem.go

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
package filesystem
2+
3+
import (
4+
"io"
5+
"net/http"
6+
"os"
7+
"path/filepath"
8+
"time"
9+
10+
"github.com/pkg/errors"
11+
"github.com/spf13/afero"
12+
)
13+
14+
// FileSystem is an interface that wraps a subset of afero calls but also adds a few of our own
15+
type FileSystem interface {
16+
AppendIfMissing(name string, content []byte, mode os.FileMode) error
17+
Chmod(name string, mode os.FileMode) error
18+
CopyDir(src string, dst string) error
19+
CopyFile(src string, dst string) error
20+
Chtimes(name string, atime time.Time, mtime time.Time) error
21+
Create(name string) (afero.File, error)
22+
DownloadRemoteFile(url string, name string) error
23+
Exists(path string) (bool, error)
24+
FileContainsBytes(filename string, subslice []byte) (bool, error)
25+
MkdirAll(path string, perm os.FileMode) error
26+
ReadFile(filename string) ([]byte, error)
27+
Rename(oldname string, newname string) error
28+
Remove(name string) error
29+
RemoveAll(path string) error
30+
Stat(path string) (os.FileInfo, error)
31+
WriteFile(filename string, data []byte, perm os.FileMode) error
32+
}
33+
34+
// A wrapper around the real afero.Fs we want
35+
type Fs struct {
36+
AferoFs afero.Fs
37+
}
38+
39+
func NewOsFs() *Fs {
40+
return &Fs{
41+
AferoFs: afero.NewOsFs(),
42+
}
43+
}
44+
45+
func NewMemFs() *Fs {
46+
return &Fs{
47+
AferoFs: afero.NewMemMapFs(),
48+
}
49+
}
50+
51+
// AppendIfMissing appends the content string to the given file
52+
func (f *Fs) AppendIfMissing(filePath string, content []byte, mode os.FileMode) error {
53+
exists, err := f.Exists(filePath)
54+
if err != nil {
55+
return errors.Wrapf(err, "coult not confirm that file %s exists", filePath)
56+
}
57+
58+
contentToWrite := append(content, []byte("\n")...)
59+
60+
if !exists {
61+
err := f.WriteFile(filePath, contentToWrite, mode)
62+
if err != nil {
63+
return errors.Wrapf(err, "could not create file %s", filePath)
64+
}
65+
66+
return nil
67+
}
68+
69+
contains, err := f.FileContainsBytes(filePath, content)
70+
if err != nil {
71+
return errors.Wrapf(err, "could not check file %s for content", filePath)
72+
}
73+
74+
if !contains {
75+
fileContents, err := f.ReadFile(filePath)
76+
if err != nil {
77+
return errors.Wrapf(err, "failed to read file %s", filePath)
78+
}
79+
80+
newContents := append(fileContents, contentToWrite...)
81+
err = f.WriteFile(filePath, newContents, mode)
82+
if err != nil {
83+
return errors.Wrapf(err, "failed to append contents to file %s", filePath)
84+
}
85+
}
86+
87+
return nil
88+
}
89+
90+
// Chmod modified the file permissions
91+
func (f *Fs) Chmod(name string, mode os.FileMode) error {
92+
return f.AferoFs.Chmod(name, mode)
93+
}
94+
95+
// Chtimes changes the access and modification times of the named file
96+
func (f *Fs) Chtimes(name string, atime time.Time, mtime time.Time) error {
97+
return f.AferoFs.Chtimes(name, atime, mtime)
98+
}
99+
100+
// CopyDir copies the contents of the given directory to another directory on the same filesystem
101+
func (f *Fs) CopyDir(src string, dst string) error {
102+
srcInfo, err := f.AferoFs.Stat(src)
103+
if err != nil {
104+
return err
105+
}
106+
107+
dir, err := f.AferoFs.Open(src)
108+
if err != nil {
109+
return err
110+
}
111+
defer dir.Close()
112+
113+
entries, err := dir.Readdir(-1)
114+
if err != nil {
115+
return err
116+
}
117+
118+
dstExists, err := f.Exists(dst)
119+
if err != nil {
120+
return err
121+
}
122+
123+
if !dstExists {
124+
if err = f.MkdirAll(dst, srcInfo.Mode()); err != nil {
125+
return err
126+
}
127+
}
128+
129+
for _, e := range entries {
130+
srcFullPath := filepath.Join(src, e.Name())
131+
dstFullPath := filepath.Join(dst, e.Name())
132+
133+
if e.IsDir() {
134+
if err = f.CopyDir(srcFullPath, dstFullPath); err != nil {
135+
return err
136+
}
137+
} else {
138+
if err = f.CopyFile(srcFullPath, dstFullPath); err != nil {
139+
return err
140+
}
141+
}
142+
}
143+
144+
return nil
145+
}
146+
147+
// CopyFile copies the specified file to the the given destination
148+
func (f *Fs) CopyFile(src string, dst string) error {
149+
srcFile, err := f.AferoFs.Open(src)
150+
if err != nil {
151+
return err
152+
}
153+
defer srcFile.Close()
154+
155+
srcInfo, err := srcFile.Stat()
156+
if err != nil {
157+
return err
158+
}
159+
160+
dstFile, err := f.AferoFs.OpenFile(dst, os.O_RDWR|os.O_CREATE, srcInfo.Mode())
161+
if err != nil {
162+
return err
163+
}
164+
defer dstFile.Close()
165+
166+
if _, err = io.Copy(dstFile, srcFile); err != nil {
167+
return err
168+
}
169+
170+
return nil
171+
}
172+
173+
// Create creates a file
174+
func (f *Fs) Create(name string) (afero.File, error) {
175+
return f.AferoFs.Create(name)
176+
}
177+
178+
// DownloadRemoteFile downloads file from the internet onto disk
179+
func (f *Fs) DownloadRemoteFile(url string, name string) error {
180+
// Get the data
181+
resp, err := http.Get(url)
182+
if err != nil {
183+
return err
184+
}
185+
defer resp.Body.Close()
186+
187+
// Create the file
188+
out, err := f.AferoFs.Create(name)
189+
if err != nil {
190+
return err
191+
}
192+
defer out.Close()
193+
194+
// Write the body to file
195+
_, err = io.Copy(out, resp.Body)
196+
197+
return err
198+
}
199+
200+
// Exists returns whether or not the file exists
201+
func (f *Fs) Exists(path string) (bool, error) {
202+
return afero.Exists(f.AferoFs, path)
203+
}
204+
205+
// FileContainsBytes returns whether or not the given file contains the subslice, otherwise an error
206+
func (f *Fs) FileContainsBytes(filename string, subslice []byte) (bool, error) {
207+
return afero.FileContainsBytes(f.AferoFs, filename, subslice)
208+
}
209+
210+
// MkdirAll creates a directory path and all parents that does not exist yet.
211+
func (f *Fs) MkdirAll(path string, perm os.FileMode) error {
212+
return f.AferoFs.MkdirAll(path, perm)
213+
}
214+
215+
// ReadFile returns the contents of the file as a slice, otherwise error
216+
func (f *Fs) ReadFile(filename string) ([]byte, error) {
217+
return afero.ReadFile(f.AferoFs, filename)
218+
}
219+
220+
// Rename returns an error if there was an issue renaming the given path
221+
func (f *Fs) Rename(oldname string, newname string) error {
222+
return f.AferoFs.Rename(oldname, newname)
223+
}
224+
225+
// Remove removes a file identified by name, returning an error, if any happens.
226+
func (f *Fs) Remove(name string) error {
227+
return f.AferoFs.Remove(name)
228+
}
229+
230+
// RemoveAll removes a directory path and any children it contains. It
231+
// does not fail if the path does not exist (return nil).
232+
func (f *Fs) RemoveAll(path string) error {
233+
return f.AferoFs.RemoveAll(path)
234+
}
235+
236+
// Stat returns a FileInfo describing the named file, or an error, if any
237+
// happens.
238+
func (f *Fs) Stat(path string) (os.FileInfo, error) {
239+
return f.AferoFs.Stat(path)
240+
}
241+
242+
// WriteFile writes the byte slice to the givn file
243+
func (f *Fs) WriteFile(filename string, data []byte, perm os.FileMode) error {
244+
return afero.WriteFile(f.AferoFs, filename, data, perm)
245+
}

0 commit comments

Comments
 (0)