1
2
3
4 package localizer
5
6 import (
7 "log"
8 "net/url"
9 "path/filepath"
10 "strings"
11
12 "sigs.k8s.io/kustomize/api/ifc"
13 "sigs.k8s.io/kustomize/api/internal/git"
14 "sigs.k8s.io/kustomize/kyaml/errors"
15 "sigs.k8s.io/kustomize/kyaml/filesys"
16 )
17
18 const (
19
20 DstPrefix = "localized"
21
22
23 LocalizeDir = "localized-files"
24
25
26 FileSchemeDir = "file-schemed"
27 )
28
29
30
31 func establishScope(rawScope string, rawTarget string, targetLdr ifc.Loader, fSys filesys.FileSystem) (filesys.ConfirmedDir, error) {
32 if repo := targetLdr.Repo(); repo != "" {
33 if rawScope != "" {
34 return "", errors.Errorf("scope %q specified for remote localize target %q", rawScope, rawTarget)
35 }
36 return filesys.ConfirmedDir(repo), nil
37 }
38
39 if rawScope == "" {
40 return filesys.ConfirmedDir(targetLdr.Root()), nil
41 }
42 scope, err := filesys.ConfirmDir(fSys, rawScope)
43 if err != nil {
44 return "", errors.WrapPrefixf(err, "unable to establish localize scope")
45 }
46 if !filesys.ConfirmedDir(targetLdr.Root()).HasPrefix(scope) {
47 return scope, errors.Errorf("localize scope %q does not contain target %q at %q", rawScope, rawTarget,
48 targetLdr.Root())
49 }
50 return scope, nil
51 }
52
53
54
55 func createNewDir(rawNewDir string, targetLdr ifc.Loader, spec *git.RepoSpec, fSys filesys.FileSystem) (filesys.ConfirmedDir, error) {
56 if rawNewDir == "" {
57 rawNewDir = defaultNewDir(targetLdr, spec)
58 }
59 if fSys.Exists(rawNewDir) {
60 return "", errors.Errorf("localize destination %q already exists", rawNewDir)
61 }
62
63 if err := fSys.Mkdir(rawNewDir); err != nil {
64 return "", errors.WrapPrefixf(err, "unable to create localize destination directory")
65 }
66 newDir, err := filesys.ConfirmDir(fSys, rawNewDir)
67 if err != nil {
68 if errCleanup := fSys.RemoveAll(newDir.String()); errCleanup != nil {
69 log.Printf("%s", errors.WrapPrefixf(errCleanup, "unable to clean localize destination"))
70 }
71 return "", errors.WrapPrefixf(err, "unable to establish localize destination")
72 }
73
74 return newDir, nil
75 }
76
77
78
79 func defaultNewDir(targetLdr ifc.Loader, spec *git.RepoSpec) string {
80 targetDir := filepath.Base(targetLdr.Root())
81 if repo := targetLdr.Repo(); repo != "" {
82
83
84 if repo == targetLdr.Root() {
85 targetDir = urlBase(spec.RepoPath)
86 }
87 return strings.Join([]string{DstPrefix, targetDir, strings.ReplaceAll(spec.Ref, "/", "-")}, "-")
88 }
89
90 if targetDir == string(filepath.Separator) {
91 return DstPrefix
92 }
93 return strings.Join([]string{DstPrefix, targetDir}, "-")
94 }
95
96
97 func urlBase(url string) string {
98 cleaned := strings.TrimRight(url, "/")
99 i := strings.LastIndex(cleaned, "/")
100 if i < 0 {
101 return cleaned
102 }
103 return cleaned[i+1:]
104 }
105
106
107 func hasRef(repoURL string) bool {
108 repoSpec, err := git.NewRepoSpecFromURL(repoURL)
109 if err != nil {
110 log.Fatalf("unable to parse validated root url: %s", err)
111 }
112 return repoSpec.Ref != ""
113 }
114
115
116 func cleanFilePath(fSys filesys.FileSystem, root filesys.ConfirmedDir, file string) string {
117 abs := root.Join(file)
118 dir, f, err := fSys.CleanedAbs(abs)
119 if err != nil {
120 log.Fatalf("cannot clean validated file path %q: %s", abs, err)
121 }
122 locPath, err := filepath.Rel(root.String(), dir.Join(f))
123 if err != nil {
124 log.Fatalf("cannot find path from parent %q to file %q: %s", root, dir.Join(f), err)
125 }
126 return locPath
127 }
128
129
130
131
132
133
134 func locFilePath(fileURL string) string {
135
136 u, err := url.Parse(fileURL)
137 if err != nil {
138 log.Panicf("cannot parse validated file url %q: %s", fileURL, err)
139 }
140
141
142
143
144
145 path := filepath.Join(string(filepath.Separator), filepath.FromSlash(u.EscapedPath()))
146
147
148
149
150
151 return filepath.Join(LocalizeDir, u.Hostname(), path)
152 }
153
154
155
156 func locRootPath(rootURL, repoDir string, root filesys.ConfirmedDir, fSys filesys.FileSystem) (string, error) {
157 repoSpec, err := git.NewRepoSpecFromURL(rootURL)
158 if err != nil {
159 log.Panicf("cannot parse validated repo url %q: %s", rootURL, err)
160 }
161 host, err := parseHost(repoSpec)
162 if err != nil {
163 return "", errors.WrapPrefixf(err, "unable to parse host of remote root %q", rootURL)
164 }
165 repo, err := filesys.ConfirmDir(fSys, repoDir)
166 if err != nil {
167 log.Panicf("unable to establish validated repo download location %q: %s", repoDir, err)
168 }
169
170 inRepo, err := filepath.Rel(repo.String(), root.String())
171 if err != nil {
172 log.Panicf("cannot find path from %q to child directory %q: %s", repo, root, err)
173 }
174
175
176 localRepoPath := strings.TrimSuffix(repoSpec.RepoPath, ".git")
177
178
179
180
181 return filepath.Join(LocalizeDir,
182 host,
183 filepath.Join(string(filepath.Separator), filepath.FromSlash(localRepoPath)),
184 filepath.FromSlash(repoSpec.Ref),
185 inRepo), nil
186 }
187
188
189 func parseHost(repoSpec *git.RepoSpec) (string, error) {
190 var target string
191 switch scheme, _, _ := strings.Cut(repoSpec.Host, "://"); scheme {
192 case "gh:":
193
194
195
196
197
198 return "gh", nil
199 case "file":
200
201
202 return FileSchemeDir, nil
203 case "https", "http", "ssh":
204 target = repoSpec.Host
205 default:
206
207
208 target = "ssh://" + repoSpec.Host
209 }
210
211 target = strings.TrimSuffix(target, ":")
212 u, err := url.Parse(target)
213 if err != nil {
214 return "", errors.Wrap(err)
215 }
216
217 return u.Hostname(), nil
218 }
219
View as plain text