-
Notifications
You must be signed in to change notification settings - Fork 137
/
sources.go
144 lines (109 loc) · 3.42 KB
/
sources.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Copyright 2024 The Carvel Authors.
// SPDX-License-Identifier: Apache-2.0
package files
import (
"fmt"
"io"
"net/http"
"os"
"path"
"path/filepath"
"strings"
)
type Source interface {
Description() string
RelativePath() (string, error)
Bytes() ([]byte, error)
}
var _ []Source = []Source{BytesSource{}, StdinSource{},
LocalSource{}, HTTPSource{}, &CachedSource{}}
type BytesSource struct {
path string
data []byte
}
func NewBytesSource(path string, data []byte) BytesSource { return BytesSource{path, data} }
func (s BytesSource) Description() string { return s.path }
func (s BytesSource) RelativePath() (string, error) { return s.path, nil }
func (s BytesSource) Bytes() ([]byte, error) { return s.data, nil }
type StdinSource struct {
bytes []byte
err error
}
func NewStdinSource() StdinSource {
// only read stdin once
bs, err := ReadStdin()
return StdinSource{bs, err}
}
func (s StdinSource) Description() string { return "stdin.yml" }
func (s StdinSource) RelativePath() (string, error) { return "stdin.yml", nil }
func (s StdinSource) Bytes() ([]byte, error) { return s.bytes, s.err }
type LocalSource struct {
path string
dir string
}
func NewLocalSource(path, dir string) LocalSource { return LocalSource{path, dir} }
// Description return LocalSource's (i.e. file's) path
func (s LocalSource) Description() string { return fmt.Sprintf("file %s", s.path) }
func (s LocalSource) RelativePath() (string, error) {
if s.dir == "" {
return filepath.Base(s.path), nil
}
cleanPath, err := filepath.Abs(filepath.Clean(s.path))
if err != nil {
return "", err
}
cleanDir, err := filepath.Abs(filepath.Clean(s.dir))
if err != nil {
return "", err
}
if strings.HasPrefix(cleanPath, cleanDir) {
result := strings.TrimPrefix(cleanPath, cleanDir)
result = strings.TrimPrefix(result, string(os.PathSeparator))
return result, nil
}
return "", fmt.Errorf("unknown relative path for %s", s.path)
}
// Bytes returns bytes of the read file
func (s LocalSource) Bytes() ([]byte, error) { return os.ReadFile(s.path) }
type HTTPSource struct {
url string
Client *http.Client
}
// NewHTTPSource returns a new source of type HTTP
func NewHTTPSource(path string) HTTPSource { return HTTPSource{path, &http.Client{}} }
func (s HTTPSource) Description() string {
return fmt.Sprintf("HTTP URL '%s'", s.url)
}
func (s HTTPSource) RelativePath() (string, error) { return path.Base(s.url), nil }
func (s HTTPSource) Bytes() ([]byte, error) {
resp, err := s.Client.Get(s.url)
if err != nil {
return nil, fmt.Errorf("Requesting URL '%s': %s", s.url, err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return nil, fmt.Errorf("Requesting URL '%s': %s", s.url, resp.Status)
}
result, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("Reading URL '%s': %s", s.url, err)
}
return result, nil
}
type CachedSource struct {
src Source
bytesFetched bool
bytes []byte
bytesErr error
}
func NewCachedSource(src Source) *CachedSource { return &CachedSource{src: src} }
func (s *CachedSource) Description() string { return s.src.Description() }
func (s *CachedSource) RelativePath() (string, error) { return s.src.RelativePath() }
func (s *CachedSource) Bytes() ([]byte, error) {
if s.bytesFetched {
return s.bytes, s.bytesErr
}
s.bytesFetched = true
s.bytes, s.bytesErr = s.src.Bytes()
return s.bytes, s.bytesErr
}