Skip to content

Commit 857b3b2

Browse files
committed
Implement integration tests. Minor tidy up too.
1 parent 6501a98 commit 857b3b2

File tree

7 files changed

+519
-20
lines changed

7 files changed

+519
-20
lines changed

domain-proxy/go.mod

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,60 @@
11
module github.com/redhat-appstudio/jvm-build-service/domain-proxy
22

33
go 1.23
4+
5+
require (
6+
github.com/testcontainers/testcontainers-go v0.31.0
7+
github.com/wiremock/go-wiremock v1.8.0
8+
github.com/wiremock/wiremock-testcontainers-go v1.0.0-alpha-9
9+
)
10+
11+
require (
12+
dario.cat/mergo v1.0.0 // indirect
13+
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
14+
github.com/Microsoft/go-winio v0.6.1 // indirect
15+
github.com/Microsoft/hcsshim v0.11.4 // indirect
16+
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
17+
github.com/containerd/containerd v1.7.15 // indirect
18+
github.com/containerd/log v0.1.0 // indirect
19+
github.com/cpuguy83/dockercfg v0.3.1 // indirect
20+
github.com/distribution/reference v0.5.0 // indirect
21+
github.com/docker/docker v25.0.5+incompatible // indirect
22+
github.com/docker/go-connections v0.5.0 // indirect
23+
github.com/docker/go-units v0.5.0 // indirect
24+
github.com/felixge/httpsnoop v1.0.4 // indirect
25+
github.com/go-logr/logr v1.4.1 // indirect
26+
github.com/go-logr/stdr v1.2.2 // indirect
27+
github.com/go-ole/go-ole v1.2.6 // indirect
28+
github.com/gogo/protobuf v1.3.2 // indirect
29+
github.com/golang/protobuf v1.5.4 // indirect
30+
github.com/google/uuid v1.6.0 // indirect
31+
github.com/klauspost/compress v1.16.0 // indirect
32+
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
33+
github.com/magiconair/properties v1.8.7 // indirect
34+
github.com/moby/patternmatcher v0.6.0 // indirect
35+
github.com/moby/sys/sequential v0.5.0 // indirect
36+
github.com/moby/sys/user v0.1.0 // indirect
37+
github.com/moby/term v0.5.0 // indirect
38+
github.com/morikuni/aec v1.0.0 // indirect
39+
github.com/opencontainers/go-digest v1.0.0 // indirect
40+
github.com/opencontainers/image-spec v1.1.0 // indirect
41+
github.com/pkg/errors v0.9.1 // indirect
42+
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
43+
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
44+
github.com/shoenig/go-m1cpu v0.1.6 // indirect
45+
github.com/sirupsen/logrus v1.9.3 // indirect
46+
github.com/tklauser/go-sysconf v0.3.12 // indirect
47+
github.com/tklauser/numcpus v0.6.1 // indirect
48+
github.com/yusufpapurcu/wmi v1.2.3 // indirect
49+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
50+
go.opentelemetry.io/otel v1.24.0 // indirect
51+
go.opentelemetry.io/otel/metric v1.24.0 // indirect
52+
go.opentelemetry.io/otel/trace v1.24.0 // indirect
53+
golang.org/x/crypto v0.22.0 // indirect
54+
golang.org/x/mod v0.16.0 // indirect
55+
golang.org/x/sys v0.19.0 // indirect
56+
golang.org/x/tools v0.13.0 // indirect
57+
google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d // indirect
58+
google.golang.org/grpc v1.58.3 // indirect
59+
google.golang.org/protobuf v1.33.0 // indirect
60+
)

domain-proxy/go.sum

Lines changed: 192 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
package integration
2+
3+
import (
4+
"context"
5+
"crypto/md5"
6+
"crypto/tls"
7+
"encoding/hex"
8+
. "github.com/redhat-appstudio/jvm-build-service/domain-proxy/pkg/client"
9+
. "github.com/redhat-appstudio/jvm-build-service/domain-proxy/pkg/common"
10+
. "github.com/redhat-appstudio/jvm-build-service/domain-proxy/pkg/server"
11+
"github.com/testcontainers/testcontainers-go"
12+
"github.com/testcontainers/testcontainers-go/wait"
13+
"github.com/wiremock/go-wiremock"
14+
. "github.com/wiremock/wiremock-testcontainers-go"
15+
"io"
16+
"math/rand"
17+
"net/http"
18+
"net/url"
19+
"os"
20+
"strconv"
21+
"strings"
22+
"testing"
23+
"time"
24+
)
25+
26+
const (
27+
WireMockHttpPort = "8080"
28+
WireMockHttpsPort = "8443"
29+
WireMockProtocol = "/tcp"
30+
ProxyUrl = "http://" + Localhost + ":8081"
31+
ContentType = "text/xml"
32+
Md5Hash = "ea3ca57f8f99d1d210d1b438c9841440"
33+
ContentLength = "403"
34+
)
35+
36+
type containerCustomizer struct{}
37+
38+
func (c containerCustomizer) Customize(req *testcontainers.GenericContainerRequest) error {
39+
req.ExposedPorts = []string{WireMockHttpPort + WireMockProtocol, WireMockHttpsPort + WireMockProtocol}
40+
req.WaitingFor = wait.ForListeningPort(WireMockHttpPort)
41+
req.Cmd = []string{"--port", WireMockHttpPort, "--https-port", WireMockHttpsPort}
42+
return nil
43+
}
44+
45+
func createClient(t *testing.T) *http.Client {
46+
proxy, err := url.Parse(ProxyUrl)
47+
if err != nil {
48+
t.Fatal(err)
49+
}
50+
transport := &http.Transport{
51+
Proxy: http.ProxyURL(proxy),
52+
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
53+
}
54+
return &http.Client{
55+
Transport: transport,
56+
}
57+
}
58+
59+
func getMd5Hash(bytes []byte) string {
60+
hash := md5.Sum(bytes)
61+
return hex.EncodeToString(hash[:])
62+
}
63+
64+
func head(urlMatchingPair wiremock.URLMatcher) *wiremock.StubRule {
65+
return wiremock.NewStubRule(http.MethodHead, urlMatchingPair)
66+
}
67+
68+
func TestDomainProxy(t *testing.T) {
69+
// Start Wiremock container
70+
ctx := context.Background()
71+
container, err := RunContainerAndStopOnCleanup(ctx, t, containerCustomizer{})
72+
if err != nil {
73+
t.Fatal(err)
74+
}
75+
// HTTP Get stub
76+
pom, err := os.ReadFile("testdata/bar-1.0.pom")
77+
err = container.Client.StubFor(
78+
wiremock.Get(wiremock.URLEqualTo("/com/foo/bar/1.0/bar-1.0.pom")).
79+
WillReturnResponse(
80+
wiremock.NewResponse().
81+
WithHeader("Content-Type", ContentType).
82+
WithBody(string(pom)).
83+
WithStatus(http.StatusOK),
84+
),
85+
)
86+
if err != nil {
87+
t.Fatal(err)
88+
}
89+
// HTTP Head stub
90+
err = container.Client.StubFor(
91+
head(wiremock.URLEqualTo("/com/foo/bar/1.0/bar-1.0.pom")).
92+
WillReturnResponse(
93+
wiremock.NewResponse().
94+
WithHeader("Content-Type", ContentType).
95+
WithHeader("Content-Length", ContentLength).
96+
WithStatus(http.StatusOK),
97+
),
98+
)
99+
if err != nil {
100+
t.Fatal(err)
101+
}
102+
// Set env variables
103+
os.Setenv(DomainSocketKey, "/tmp/domain-socket-"+strconv.Itoa(rand.Int())+".sock")
104+
os.Setenv(ServerHttpPortKey, "8081")
105+
os.Setenv(ProxyTargetWhitelistKey, "localhost,foo.bar")
106+
// Start services
107+
domainProxyServer := NewDomainProxyServer()
108+
go domainProxyServer.Start()
109+
domainProxyClient := NewDomainProxyClient()
110+
go domainProxyClient.Start()
111+
time.Sleep(1 * time.Second)
112+
defer domainProxyServer.Stop()
113+
defer domainProxyClient.Stop()
114+
// Get Wiremock container details
115+
mappedHttpPort, err := container.MappedPort(ctx, WireMockHttpPort)
116+
mappedHttpsPort, err := container.MappedPort(ctx, WireMockHttpsPort)
117+
wireMockHttpUrl := "http://" + Localhost + ":" + mappedHttpPort.Port()
118+
wireMockHttpsUrl := "https://" + Localhost + ":" + mappedHttpsPort.Port()
119+
// Create HTTP client
120+
httpClient := createClient(t)
121+
122+
t.Run("Test HTTP GET dependency", func(t *testing.T) {
123+
response, err := httpClient.Get(wireMockHttpUrl + "/com/foo/bar/1.0/bar-1.0.pom")
124+
if err != nil {
125+
t.Fatal(err)
126+
}
127+
if response.StatusCode != http.StatusOK {
128+
t.Fatalf("Actual HTTP status %d did not match expected HTTP status %d", response.StatusCode, http.StatusOK)
129+
}
130+
defer response.Body.Close()
131+
pom, err := io.ReadAll(response.Body)
132+
if err != nil {
133+
t.Fatal(err)
134+
}
135+
hash := getMd5Hash(pom)
136+
if hash != Md5Hash {
137+
t.Fatalf("Actual MD5 hash %s did not match expected MD5 hash %s", hash, Md5Hash)
138+
}
139+
})
140+
141+
t.Run("Test HTTPS GET dependency", func(t *testing.T) {
142+
response, err := httpClient.Get(wireMockHttpsUrl + "/com/foo/bar/1.0/bar-1.0.pom")
143+
if err != nil {
144+
t.Fatal(err)
145+
}
146+
if response.StatusCode != http.StatusOK {
147+
t.Fatalf("Actual HTTP status %d did not match expected HTTP status %d", response.StatusCode, http.StatusOK)
148+
}
149+
defer response.Body.Close()
150+
pom, err := io.ReadAll(response.Body)
151+
if err != nil {
152+
t.Fatal(err)
153+
}
154+
hash := getMd5Hash(pom)
155+
if hash != Md5Hash {
156+
t.Fatalf("Actual MD5 hash %s did not match expected MD5 hash %s", hash, Md5Hash)
157+
}
158+
})
159+
160+
t.Run("Test HTTP GET non-existent dependency", func(t *testing.T) {
161+
response, err := httpClient.Get(wireMockHttpUrl + "/com/foo/bar/1.0/bar-2.0.pom")
162+
if err != nil {
163+
t.Fatal(err)
164+
}
165+
if response.StatusCode != http.StatusNotFound {
166+
t.Fatalf("Actual HTTP status %d did not match expected HTTP status %d", response.StatusCode, http.StatusNotFound)
167+
}
168+
})
169+
170+
t.Run("Test HTTPS GET non-existent dependency", func(t *testing.T) {
171+
response, err := httpClient.Get(wireMockHttpsUrl + "/com/foo/bar/1.0/bar-2.0.pom")
172+
if err != nil {
173+
t.Fatal(err)
174+
}
175+
if response.StatusCode != http.StatusNotFound {
176+
t.Fatalf("Actual HTTP status %d did not match expected HTTP status %d", response.StatusCode, http.StatusNotFound)
177+
}
178+
})
179+
180+
t.Run("Test HTTP non-whitelisted host", func(t *testing.T) {
181+
response, err := httpClient.Get("http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-jar-plugin/3.4.1/maven-jar-plugin-3.4.1.jar")
182+
if err != nil {
183+
t.Fatal(err)
184+
}
185+
if response.StatusCode != http.StatusForbidden {
186+
t.Fatalf("Actual HTTP status %d did not match expected HTTP status %d", response.StatusCode, http.StatusForbidden)
187+
}
188+
})
189+
190+
t.Run("Test HTTPS non-whitelisted host", func(t *testing.T) {
191+
_, err := httpClient.Get("https://repo1.maven.org/maven2/org/apache/maven/plugins/maven-jar-plugin/3.4.1/maven-jar-plugin-3.4.1.jar")
192+
statusText := http.StatusText(http.StatusForbidden)
193+
if !strings.Contains(err.Error(), statusText) {
194+
t.Fatalf("Actual error %s did not contain expected HTTP status text %s", err.Error(), statusText)
195+
}
196+
})
197+
198+
t.Run("Test HTTP non-existent host", func(t *testing.T) {
199+
response, err := httpClient.Get("http://foo.bar")
200+
if err != nil {
201+
t.Fatal(err)
202+
}
203+
if response.StatusCode != http.StatusBadGateway {
204+
t.Fatalf("Actual HTTP status %d did not match expected HTTP status %d", response.StatusCode, http.StatusBadGateway)
205+
}
206+
})
207+
208+
t.Run("Test HTTPS non-existent host", func(t *testing.T) {
209+
_, err := httpClient.Get("https://foo.bar")
210+
statusText := http.StatusText(http.StatusBadGateway)
211+
if !strings.Contains(err.Error(), statusText) {
212+
t.Fatalf("Actual error %s did not contain expected HTTP status text %s", err.Error(), statusText)
213+
}
214+
})
215+
216+
t.Run("Test HTTP HEAD dependency", func(t *testing.T) {
217+
response, err := httpClient.Head(wireMockHttpUrl + "/com/foo/bar/1.0/bar-1.0.pom")
218+
if err != nil {
219+
t.Fatal(err)
220+
}
221+
actualContentLength := response.Header.Get("Content-Length")
222+
if actualContentLength != ContentLength {
223+
t.Fatalf("Actual content length %s did not match expected content length %s", actualContentLength, ContentLength)
224+
}
225+
})
226+
227+
t.Run("Test HTTPS HEAD dependency", func(t *testing.T) {
228+
response, err := httpClient.Head(wireMockHttpsUrl + "/com/foo/bar/1.0/bar-1.0.pom")
229+
if err != nil {
230+
t.Fatal(err)
231+
}
232+
actualContentLength := response.Header.Get("Content-Length")
233+
if actualContentLength != ContentLength {
234+
t.Fatalf("Actual content length %s did not match expected content length %s", actualContentLength, ContentLength)
235+
}
236+
})
237+
238+
t.Run("Test HTTP HEAD non-existent dependency", func(t *testing.T) {
239+
response, err := httpClient.Head(wireMockHttpUrl + "/com/foo/bar/1.0/bar-2.0.pom")
240+
if err != nil {
241+
t.Fatal(err)
242+
}
243+
if response.StatusCode != http.StatusNotFound {
244+
t.Fatalf("Actual HTTP status %d did not match expected HTTP status %d", response.StatusCode, http.StatusNotFound)
245+
}
246+
})
247+
248+
t.Run("Test HTTPS HEAD non-existent dependency", func(t *testing.T) {
249+
response, err := httpClient.Head(wireMockHttpsUrl + "/com/foo/bar/1.0/bar-2.0.pom")
250+
if err != nil {
251+
t.Fatal(err)
252+
}
253+
if response.StatusCode != http.StatusNotFound {
254+
t.Fatalf("Actual HTTP status %d did not match expected HTTP status %d", response.StatusCode, http.StatusNotFound)
255+
}
256+
})
257+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<groupId>com.foo</groupId>
7+
<artifactId>bar</artifactId>
8+
<version>1.0</version>
9+
</project>

domain-proxy/integration_tests/integration_test.go

Lines changed: 0 additions & 16 deletions
This file was deleted.

domain-proxy/pkg/server/server.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const (
1717
HttpPort = 80
1818
HttpsPort = 443
1919
ProxyTargetWhitelistKey = "PROXY_TARGET_WHITELIST"
20-
DefaultProxyTargetWhitelist = "repo1.maven.org,repo.maven.apache.org,repository.jboss.org,packages.confluent.io,jitpack.io,repo.gradle.org,plugins.gradle.org"
20+
DefaultProxyTargetWhitelist = "localhost,repo1.maven.org,repo.maven.apache.org,repository.jboss.org,packages.confluent.io,jitpack.io,repo.gradle.org,plugins.gradle.org"
2121
InternalNonProxyHostsKey = "INTERNAL_NON_PROXY_HOSTS"
2222
DefaultInternalNonProxyHosts = "localhost"
2323
DomainSocketToHttp = "Domain Socket <-> HTTP"
@@ -200,8 +200,8 @@ func getTargetHostAndPort(host string, defaultPort int) (string, int) {
200200
}
201201

202202
func (dps *DomainProxyServer) isTargetWhitelisted(targetHost string, writer http.ResponseWriter) bool {
203-
if !dps.proxyTargetWhitelist[targetHost] && !dps.nonProxyHosts[targetHost] {
204-
message := fmt.Sprintf("Target host %s is not whitelisted nor a non-proxy host", targetHost)
203+
if !dps.proxyTargetWhitelist[targetHost] {
204+
message := fmt.Sprintf("Target host %s is not whitelisted", targetHost)
205205
logger.Println(message)
206206
http.Error(writer, message, http.StatusForbidden)
207207
return false

pkg/reconciler/dependencybuild/buildrecipeyaml.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ func createPipelineSpec(log logr.Logger, tool string, commitTime int64, jbsConfi
556556
Name: "PROXY_TARGET_WHITELIST",
557557
Value: tektonpipeline.ParamValue{
558558
Type: tektonpipeline.ParamTypeString,
559-
StringVal: "cdn-ubi.redhat.com,repo1.maven.org,repo.scala-sbt.org,scala.jfrog.io,repo.typesafe.com,jfrog-prod-usw2-shared-oregon-main.s3.amazonaws.com",
559+
StringVal: whitelistUrl.Host + ",localhost,cdn-ubi.redhat.com,repo1.maven.org,repo.scala-sbt.org,scala.jfrog.io,repo.typesafe.com,jfrog-prod-usw2-shared-oregon-main.s3.amazonaws.com",
560560
},
561561
},
562562
{

0 commit comments

Comments
 (0)