Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add skip-path Feature for Dependency Mirror #606

Open
wants to merge 2 commits into
base: v2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions postal/internal/dependency_mirror.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,44 @@ func NewDependencyMirrorResolver(bindingResolver BindingResolver) DependencyMirr
}
}

// Parses a raw mirror string into a map of arguments.
func getMirrorArgs(mirror string) map[string]string {

mirrorArgs := map[string]string{
"mirror": mirror,
"skip-path": "",
}

// Split mirror string at commas and extract specified arguments.
for _, arg := range strings.Split(mirror, ",") {
argPair := strings.SplitN(arg, "=", 2)
// If a URI is provided without the key 'mirror=', still treat it as the 'mirror' argument.
// This addresses backwards compatibility and user experience as most mirrors won't need any additional arguments.
if len(argPair) == 1 && (strings.HasPrefix(argPair[0], "https") || strings.HasPrefix(argPair[0], "file")) {
mirrorArgs["mirror"] = argPair[0]
}
// Add all provided arguments to key/value map.
if len(argPair) == 2 {
mirrorArgs[argPair[0]] = argPair[1]
}
}

// Unescape mirror arguments to support URL-encoded strings.
tmp, err := url.PathUnescape(mirrorArgs["mirror"])
if err == nil {
mirrorArgs["mirror"] = tmp
}
tmp, err = url.PathUnescape(mirrorArgs["skip-path"])
if err == nil {
mirrorArgs["skip-path"] = tmp
}

return mirrorArgs
}

func formatAndVerifyMirror(mirror, uri string) (string, error) {
mirrorURL, err := url.Parse(mirror)
mirrorArgs := getMirrorArgs(mirror)
mirrorURL, err := url.Parse(mirrorArgs["mirror"])
if err != nil {
return "", err
}
Expand All @@ -32,7 +68,7 @@ func formatAndVerifyMirror(mirror, uri string) (string, error) {
return "", fmt.Errorf("invalid mirror scheme")
}

mirrorURL.Path = strings.Replace(mirrorURL.Path, "{originalHost}", uriURL.Hostname(), 1) + uriURL.Path
mirrorURL.Path = strings.Replace(mirrorURL.Path, "{originalHost}", uriURL.Hostname(), 1) + strings.Replace(uriURL.Path, mirrorArgs["skip-path"], "", 1)
return mirrorURL.String(), nil
}

Expand Down
104 changes: 104 additions & 0 deletions postal/internal/dependency_mirror_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,73 @@ func testDependencyMirror(t *testing.T, context spec.G, it spec.S) {
})
})

context("via binding with additional arguments", func() {
it.Before(func() {
tmpDir, err = os.MkdirTemp("", "dependency-mirror")
Expect(err).NotTo(HaveOccurred())
Expect(os.WriteFile(filepath.Join(tmpDir, "type"), []byte("dependency-mirror"), os.ModePerm))

bindingResolver = &fakes.BindingResolver{}
resolver = internal.NewDependencyMirrorResolver(bindingResolver)

bindingResolver.ResolveCall.Returns.BindingSlice = []servicebindings.Binding{
{
Name: "some-binding",
Path: "some-path",
Type: "dependency-mirror",
Entries: map[string]*servicebindings.Entry{
"default": servicebindings.NewEntry(filepath.Join(tmpDir, "default")),
},
},
}
})

it.After(func() {
Expect(os.RemoveAll(tmpDir)).To(Succeed())
})

context("respects skip-path argument", func() {
it.Before(func() {
Expect(os.WriteFile(filepath.Join(tmpDir, "github.com"), []byte("mirror=https://mirror.example.org/public-github,skip-path=/path-to-skip"), os.ModePerm))
Expect(os.WriteFile(filepath.Join(tmpDir, "nodejs.org"), []byte("https://mirror.example.org/node-dist,skip-path=/path-to-skip"), os.ModePerm))
Expect(os.WriteFile(filepath.Join(tmpDir, "maven.org"), []byte("https://user%3Apa%24%24word%2C%40mirror.example.org%2Fmaven,skip-path=%2Fpath%20to%20skip"), os.ModePerm))

bindingResolver.ResolveCall.Returns.BindingSlice[0].Entries = map[string]*servicebindings.Entry{
"github.com": servicebindings.NewEntry(filepath.Join(tmpDir, "github.com")),
"nodejs.org": servicebindings.NewEntry(filepath.Join(tmpDir, "nodejs.org")),
"maven.org": servicebindings.NewEntry(filepath.Join(tmpDir, "maven.org")),
}
})

it("sets mirror excluding a path segment with 'mirror' argument", func() {
boundDependency, err := resolver.FindDependencyMirror("https://github.com/path-to-skip/dep.tgz", "some-platform-dir")
Expect(err).ToNot(HaveOccurred())
Expect(bindingResolver.ResolveCall.Receives.Typ).To(Equal("dependency-mirror"))
Expect(bindingResolver.ResolveCall.Receives.Provider).To(BeEmpty())
Expect(bindingResolver.ResolveCall.Receives.PlatformDir).To(Equal("some-platform-dir"))
Expect(boundDependency).To(Equal("https://mirror.example.org/public-github/dep.tgz"))
})

it("sets mirror excluding a path segment without 'mirror' argument", func() {
boundDependency, err := resolver.FindDependencyMirror("https://nodejs.org/path-to-skip/dep.tgz", "some-platform-dir")
Expect(err).ToNot(HaveOccurred())
Expect(bindingResolver.ResolveCall.Receives.Typ).To(Equal("dependency-mirror"))
Expect(bindingResolver.ResolveCall.Receives.Provider).To(BeEmpty())
Expect(bindingResolver.ResolveCall.Receives.PlatformDir).To(Equal("some-platform-dir"))
Expect(boundDependency).To(Equal("https://mirror.example.org/node-dist/dep.tgz"))
})

it("sets mirror excluding a path segment using URL encoding", func() {
boundDependency, err := resolver.FindDependencyMirror("https://maven.org/path to skip/dep.tgz", "some-platform-dir")
Expect(err).ToNot(HaveOccurred())
Expect(bindingResolver.ResolveCall.Receives.Typ).To(Equal("dependency-mirror"))
Expect(bindingResolver.ResolveCall.Receives.Provider).To(BeEmpty())
Expect(bindingResolver.ResolveCall.Receives.PlatformDir).To(Equal("some-platform-dir"))
Expect(boundDependency).To(Equal("https://user:pa$$word,@mirror.example.org/maven/dep.tgz"))
})
})
})

context("via environment variables", func() {
it.Before(func() {
Expect(os.Setenv("BP_DEPENDENCY_MIRROR", "https://mirror.example.org/{originalHost}"))
Expand Down Expand Up @@ -197,6 +264,43 @@ func testDependencyMirror(t *testing.T, context spec.G, it spec.S) {
})
})

context("via environment variables with additional arguments", func() {
context("respects skip-path argument", func() {
it.Before(func() {
Expect(os.Setenv("BP_DEPENDENCY_MIRROR_GITHUB_COM", "mirror=https://mirror.example.org/public-github,skip-path=/path-to-skip"))
Expect(os.Setenv("BP_DEPENDENCY_MIRROR_NODEJS_ORG", "https://mirror.example.org/node-dist,skip-path=/path-to-skip"))
Expect(os.Setenv("BP_DEPENDENCY_MIRROR_MAVEN_ORG", "https://user%3Apa%24%24word%2C%40mirror.example.org%2Fmaven,skip-path=%2Fpath%20to%20skip"))

bindingResolver = &fakes.BindingResolver{}
resolver = internal.NewDependencyMirrorResolver(bindingResolver)
})

it.After(func() {
Expect(os.Unsetenv("BP_DEPENDENCY_MIRROR_GITHUB_COM"))
Expect(os.Unsetenv("BP_DEPENDENCY_MIRROR_NODEJS_ORG"))
Expect(os.Unsetenv("BP_DEPENDENCY_MIRROR_MAVEN_ORG"))
})

it("sets mirror excluding a path segment with 'mirror' argument", func() {
boundDependency, err := resolver.FindDependencyMirror("https://github.com/path-to-skip/dep.tgz", "some-platform-dir")
Expect(err).ToNot(HaveOccurred())
Expect(boundDependency).To(Equal("https://mirror.example.org/public-github/dep.tgz"))
})

it("sets mirror excluding a path segment without 'mirror' argument", func() {
boundDependency, err := resolver.FindDependencyMirror("https://nodejs.org/path-to-skip/dep.tgz", "some-platform-dir")
Expect(err).ToNot(HaveOccurred())
Expect(boundDependency).To(Equal("https://mirror.example.org/node-dist/dep.tgz"))
})

it("sets mirror excluding a path segment using URL encoding", func() {
boundDependency, err := resolver.FindDependencyMirror("https://maven.org/path to skip/dep.tgz", "some-platform-dir")
Expect(err).ToNot(HaveOccurred())
Expect(boundDependency).To(Equal("https://user:pa$$word,@mirror.example.org/maven/dep.tgz"))
})
})
})

context("when mirror is provided by both bindings and environment variables", func() {
it.Before(func() {
tmpDir, err = os.MkdirTemp("", "dependency-mirror")
Expand Down
Loading