Skip to content

Commit 2978bc7

Browse files
authored
Merge pull request #27 from bupd/testing
feat: Add E2E Test for Harbor Satellite
2 parents 8439290 + 2cf1671 commit 2978bc7

File tree

12 files changed

+282
-28
lines changed

12 files changed

+282
-28
lines changed

.DS_Store

-10 KB
Binary file not shown.

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
.env
21
# If you prefer the allow list template instead of the deny list, see community template:
32
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
43
#

config.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
# Wether to us the built-in Zot registry or not
2-
bring_own_registry = false
2+
bring_own_registry = true
33

44
# URL of own registry
5-
own_registry_adr = "127.0.0.1:8585"
5+
own_registry_adr = "127.0.0.1:5000"
66

77
# URL of remote registry OR local file path
8-
url_or_file = "https://demo.goharbor.io/v2/myproject/album-server"
8+
# url_or_file = "https://demo.goharbor.io/v2/myproject/album-server"
9+
url_or_file = "http://localhost:5001/v2/library/busybox"
910

1011
# For testing purposes :
1112
# https://demo.goharbor.io/v2/myproject/album-server
12-
# /image-list/images.json
13+
# /image-list/images.json

internal/replicate/replicate.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ func NewReplicator() Replicator {
4040
}
4141

4242
func (r *BasicReplicator) Replicate(ctx context.Context, image string) error {
43-
4443
source := getPullSource(image)
4544

4645
if source != "" {
@@ -98,7 +97,8 @@ func (r *BasicReplicator) DeleteExtraImages(ctx context.Context, imgs []store.Im
9897

9998
func getPullSource(image string) string {
10099
input := os.Getenv("USER_INPUT")
101-
if os.Getenv("SCHEME") == "https://" {
100+
scheme := os.Getenv("SCHEME")
101+
if strings.HasPrefix(scheme, "http://") || strings.HasPrefix(scheme, "https://") {
102102
url := os.Getenv("HOST") + "/" + os.Getenv("REGISTRY") + "/" + image
103103
return url
104104
} else {
@@ -115,7 +115,6 @@ func getPullSource(image string) string {
115115

116116
return registryURL + repositoryName + "/" + image
117117
}
118-
119118
}
120119

121120
func getFileInfo(input string) (*RegistryInfo, error) {
@@ -150,8 +149,10 @@ func CopyImage(imageName string) error {
150149
return fmt.Errorf("ZOT_URL environment variable is not set")
151150
}
152151

153-
srcRef := imageName
154-
destRef := zotUrl + "/" + imageName
152+
// Clean up the image name by removing any host part
153+
cleanedImageName := removeHostName(imageName)
154+
destRef := fmt.Sprintf("%s/%s", zotUrl, cleanedImageName)
155+
fmt.Println("Destination reference:", destRef)
155156

156157
// Get credentials from environment variables
157158
username := os.Getenv("HARBOR_USERNAME")
@@ -166,7 +167,7 @@ func CopyImage(imageName string) error {
166167
})
167168

168169
// Pull the image with authentication
169-
srcImage, err := crane.Pull(srcRef, crane.WithAuth(auth))
170+
srcImage, err := crane.Pull(imageName, crane.WithAuth(auth), crane.Insecure)
170171
if err != nil {
171172
fmt.Printf("Failed to pull image: %v\n", err)
172173
return fmt.Errorf("failed to pull image: %w", err)
@@ -176,7 +177,7 @@ func CopyImage(imageName string) error {
176177
}
177178

178179
// Push the image to the destination registry
179-
err = crane.Push(srcImage, destRef)
180+
err = crane.Push(srcImage, destRef, crane.Insecure)
180181
if err != nil {
181182
fmt.Printf("Failed to push image: %v\n", err)
182183
return fmt.Errorf("failed to push image: %w", err)
@@ -195,3 +196,13 @@ func CopyImage(imageName string) error {
195196

196197
return nil
197198
}
199+
200+
// take only the parts after the hostname
201+
func removeHostName(imageName string) string {
202+
parts := strings.Split(imageName, "/")
203+
if len(parts) > 1 {
204+
return strings.Join(parts[1:], "/")
205+
}
206+
207+
return imageName
208+
}

internal/store/http-fetch.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func (client *RemoteImageList) GetDigest(ctx context.Context, tag string) (strin
9999
digest, err := crane.Digest(imageRef, crane.WithAuth(&authn.Basic{
100100
Username: username,
101101
Password: password,
102-
}))
102+
}), crane.Insecure)
103103
if err != nil {
104104
fmt.Printf("failed to fetch digest for %s: %v\n", imageRef, err)
105105
return "", nil

internal/store/in-memory-store.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ func (s *inMemoryStore) List(ctx context.Context) ([]Image, error) {
155155
fmt.Println("No changes detected in the store")
156156
return nil, nil
157157
}
158-
159158
}
160159

161160
func (s *inMemoryStore) Add(ctx context.Context, digest string, image string) error {
@@ -201,7 +200,6 @@ func (s *inMemoryStore) RemoveImage(ctx context.Context, image string) error {
201200
// TODO: Rework complicated logic and add support for multiple repositories
202201
// checkImageAndDigest checks if the image exists in the store and if the digest matches the image reference
203202
func (s *inMemoryStore) checkImageAndDigest(digest string, image string) bool {
204-
205203
// Check if the received image exists in the store
206204
for storeDigest, storeImage := range s.images {
207205
if storeImage == image {
@@ -236,19 +234,18 @@ func (s *inMemoryStore) checkImageAndDigest(digest string, image string) bool {
236234
// If adding was successful, return true, else return false
237235
err := s.Add(context.Background(), digest, image)
238236
return err != nil
239-
240237
}
241238

242239
func GetLocalDigest(ctx context.Context, tag string) (string, error) {
243-
244240
zotUrl := os.Getenv("ZOT_URL")
245241
userURL := os.Getenv("USER_INPUT")
246242
// Remove extra characters from the URLs
247243
userURL = userURL[strings.Index(userURL, "//")+2:]
248244
userURL = strings.ReplaceAll(userURL, "/v2", "")
249245

246+
regUrl := removeHostName(userURL)
250247
// Construct the URL for fetching the digest
251-
url := zotUrl + "/" + userURL + ":" + tag
248+
url := zotUrl + "/" + regUrl + ":" + tag
252249

253250
// Use crane.Digest to get the digest of the image
254251
digest, err := crane.Digest(url)
@@ -258,3 +255,13 @@ func GetLocalDigest(ctx context.Context, tag string) (string, error) {
258255

259256
return digest, nil
260257
}
258+
259+
// Split the imageName by "/" and take only the parts after the hostname
260+
func removeHostName(imageName string) string {
261+
parts := strings.Split(imageName, "/")
262+
if len(parts) > 1 {
263+
return strings.Join(parts[1:], "/")
264+
}
265+
266+
return imageName
267+
}

main.go

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"os"
1212
"os/signal"
1313
"path/filepath"
14-
"regexp"
1514
"strings"
1615
"syscall"
1716
"time"
@@ -85,13 +84,13 @@ func run() error {
8584
registryAdr := viper.GetString("own_registry_adr")
8685

8786
// Validate registryAdr format
88-
matched, err := regexp.MatchString(`^127\.0\.0\.1:\d{1,5}$`, registryAdr)
89-
if err != nil {
90-
return fmt.Errorf("error validating registry address: %w", err)
91-
}
92-
if !matched {
93-
return fmt.Errorf("invalid registry address format: %s", registryAdr)
94-
}
87+
// matched, err := regexp.MatchString(`^127\.0\.0\.1:\d{1,5}$`, registryAdr)
88+
// if err != nil {
89+
// return fmt.Errorf("error validating registry address: %w", err)
90+
// }
91+
// if matched {
92+
// return fmt.Errorf("invalid registry address format: %s", registryAdr)
93+
// }
9594
os.Setenv("ZOT_URL", registryAdr)
9695
fmt.Println("Registry URL set to:", registryAdr)
9796
} else {
@@ -105,7 +104,6 @@ func run() error {
105104
cancel()
106105
return err
107106
}
108-
109107
})
110108
}
111109

registry/.DS_Store

-6 KB
Binary file not shown.

registry/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010
"log": {
1111
"level": ""
1212
}
13-
}
13+
}

test/e2e/satellite_test.go

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"testing"
9+
"time"
10+
11+
"dagger.io/dagger"
12+
"github.com/stretchr/testify/assert"
13+
)
14+
15+
const (
16+
appDir = "/app"
17+
appBinary = "app"
18+
sourceFile = "main.go"
19+
)
20+
21+
func TestSatellite(t *testing.T) {
22+
ctx := context.Background()
23+
24+
// Initialize Dagger client
25+
client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
26+
assert.NoError(t, err, "Failed to connect to Dagger")
27+
defer client.Close()
28+
29+
// Set up Source Registry
30+
source, err := setupSourceRegistry(t, client, ctx)
31+
assert.NoError(t, err, "Failed to set up source registry")
32+
33+
// Set up Destination registry
34+
dest, err := setupDestinationRegistry(t, client, ctx)
35+
assert.NoError(t, err, "Failed to set up destination registry")
36+
37+
// Push images to Source registry
38+
pushImageToSourceRegistry(t, ctx, client, source)
39+
assert.NoError(t, err, "Failed to upload image to source registry")
40+
41+
// Build & Run Satellite
42+
buildSatellite(t, client, ctx, source, dest)
43+
assert.NoError(t, err, "Failed to build and run Satellite")
44+
}
45+
46+
// Setup Source Registry as a Dagger Service
47+
func setupSourceRegistry(
48+
t *testing.T,
49+
client *dagger.Client,
50+
ctx context.Context,
51+
) (*dagger.Service, error) {
52+
// socket to connect to host Docker
53+
socket := client.Host().UnixSocket("/var/run/docker.sock")
54+
55+
container, err := client.Container().
56+
From("registry:2").
57+
WithExposedPort(5000).
58+
WithUnixSocket("/var/run/docker.sock", socket).
59+
WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock").
60+
WithEnvVariable("CACHEBUSTER", time.Now().String()).
61+
AsService().Start(ctx)
62+
63+
assert.NoError(t, err, "Failed setting up source registry.")
64+
65+
return container, nil
66+
}
67+
68+
// Setup Destination Registry as a Dagger Service
69+
func setupDestinationRegistry(
70+
t *testing.T,
71+
client *dagger.Client,
72+
ctx context.Context,
73+
) (*dagger.Service, error) {
74+
// socket to connect to host Docker
75+
socket := client.Host().UnixSocket("/var/run/docker.sock")
76+
77+
container, err := client.Container().
78+
From("registry:2").
79+
WithExposedPort(5000).
80+
WithUnixSocket("/var/run/docker.sock", socket).
81+
WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock").
82+
WithEnvVariable("CACHEBUSTER", time.Now().String()).
83+
AsService().Start(ctx)
84+
85+
assert.NoError(t, err, "Failed setting up destination registry")
86+
87+
return container, nil
88+
}
89+
90+
// Push image to the Source registry
91+
func pushImageToSourceRegistry(
92+
t *testing.T,
93+
ctx context.Context,
94+
client *dagger.Client,
95+
source *dagger.Service,
96+
) {
97+
// socket to connect to host Docker
98+
socket := client.Host().UnixSocket("/var/run/docker.sock")
99+
100+
container := client.Container().
101+
From("docker:dind").
102+
WithUnixSocket("/var/run/docker.sock", socket).
103+
WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock").
104+
WithEnvVariable("CACHEBUSTER", time.Now().String()).
105+
WithServiceBinding("source", source)
106+
107+
// add crane & push images
108+
container = container.WithExec([]string{"apk", "add", "crane"}).
109+
WithExec([]string{"docker", "pull", "busybox:1.36"}).
110+
WithExec([]string{"docker", "pull", "busybox:stable"}).
111+
WithExec([]string{"crane", "copy", "busybox:1.36", "source:5000/library/busybox:1.36", "--insecure"}).
112+
WithExec([]string{"crane", "copy", "busybox:stable", "source:5000/library/busybox:stable", "--insecure"}).
113+
WithExec([]string{"crane", "digest", "source:5000/library/busybox:1.36", "--insecure"}).
114+
WithExec([]string{"crane", "digest", "source:5000/library/busybox:stable", "--insecure"})
115+
116+
// check pushed images exist
117+
container = container.WithExec([]string{"crane", "catalog", "source:5000", "--insecure"})
118+
119+
stdOut, err := container.Stdout(ctx)
120+
assert.NoError(t, err, "Failed to print stdOut in pushing Image to Source")
121+
122+
fmt.Println(stdOut)
123+
}
124+
125+
// buildSatellite and test test the connection
126+
func buildSatellite(
127+
t *testing.T,
128+
client *dagger.Client,
129+
ctx context.Context,
130+
source *dagger.Service,
131+
dest *dagger.Service,
132+
) {
133+
socket := client.Host().UnixSocket("/var/run/docker.sock")
134+
135+
// Get the directory
136+
parentDir, err := getProjectDir()
137+
assert.NoError(t, err, "Failed to get Project Directory")
138+
139+
// Use the directory path in Dagger
140+
dir := client.Host().Directory(parentDir)
141+
142+
// Get configuration file on the host
143+
configFile := client.Host().File("./testdata/config.toml")
144+
145+
// Configure and build the Satellite
146+
container := client.Container().From("golang:alpine").WithDirectory(appDir, dir).
147+
WithWorkdir(appDir).
148+
WithServiceBinding("source", source).
149+
WithServiceBinding("dest", dest).
150+
WithUnixSocket("/var/run/docker.sock", socket).
151+
WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock").
152+
WithEnvVariable("CACHEBUSTER", time.Now().String()).
153+
WithExec([]string{"cat", "config.toml"}).
154+
WithFile("./config.toml", configFile).
155+
WithExec([]string{"cat", "config.toml"}).
156+
WithExec([]string{"apk", "add", "crane"}).
157+
WithExec([]string{"crane", "-v", "catalog", "source:5000", "--insecure"}).
158+
WithExec([]string{"crane", "digest", "source:5000/library/busybox:stable", "--insecure"}).
159+
WithExec([]string{"go", "build", "-o", appBinary, sourceFile}).
160+
WithExposedPort(9090).
161+
WithExec([]string{"go", "run", "./test/e2e/test.go"})
162+
163+
assert.NoError(t, err, "Test failed in buildSatellite")
164+
165+
stdOut, err := container.Stdout(ctx)
166+
assert.NoError(t, err, "Failed to get stdOut in Satellite")
167+
168+
fmt.Println(stdOut)
169+
}
170+
171+
// Gets the directory of the project
172+
func getProjectDir() (string, error) {
173+
currentDir, err := os.Getwd()
174+
if err != nil {
175+
return "", err
176+
}
177+
return filepath.Abs(filepath.Join(currentDir, "../.."))
178+
}

0 commit comments

Comments
 (0)