From 79ac53278acf1e2e82121940b474c694f5e3f0ad Mon Sep 17 00:00:00 2001 From: Prasanth B <89722848+bupd@users.noreply.github.com> Date: Thu, 20 Jun 2024 03:14:37 +0530 Subject: [PATCH 1/4] update satellite to push to insecure registry Signed-off-by: bupd Add: Dagger integration for building and running Harbor Satellite Signed-off-by: bupd create test to test the registry Signed-off-by: bupd update registry test with e2e Signed-off-by: bupd add testing for containers e2e Signed-off-by: bupd update satellite to push to insecure registry Signed-off-by: bupd --- ci/main.go | 9 + internal/replicate/replicate.go | 5 +- main.go | 16 +- test/e2e/registry_test.go | 282 ++++++++++++++++++++++++++++ test/e2e/testdata/config.toml | 5 + test/e2e/testdata/harbor.yml | 313 ++++++++++++++++++++++++++++++++ 6 files changed, 618 insertions(+), 12 deletions(-) create mode 100644 test/e2e/registry_test.go create mode 100644 test/e2e/testdata/config.toml create mode 100644 test/e2e/testdata/harbor.yml diff --git a/ci/main.go b/ci/main.go index aba0741..2b71c0c 100644 --- a/ci/main.go +++ b/ci/main.go @@ -8,6 +8,15 @@ import ( "dagger.io/dagger" ) +const ( + imageVersion = "golang:1.22" // Use a constant for the Go image version + exposePort = 9090 // Client port to expose + containerPort = 9090 // Container port to expose + appDir = "/app" // Directory inside the container + appBinary = "app" // Name of the built application + sourceFile = "main.go" // Source file to build +) + func main() { ctx := context.Background() diff --git a/internal/replicate/replicate.go b/internal/replicate/replicate.go index 7b0b3e3..3bcda85 100644 --- a/internal/replicate/replicate.go +++ b/internal/replicate/replicate.go @@ -40,7 +40,6 @@ func NewReplicator() Replicator { } func (r *BasicReplicator) Replicate(ctx context.Context, image string) error { - source := getPullSource(image) if source != "" { @@ -115,7 +114,6 @@ func getPullSource(image string) string { return registryURL + repositoryName + "/" + image } - } func getFileInfo(input string) (*RegistryInfo, error) { @@ -152,6 +150,7 @@ func CopyImage(imageName string) error { srcRef := imageName destRef := zotUrl + "/" + imageName + fmt.Println("this is destRef: ", destRef) // Get credentials from environment variables username := os.Getenv("HARBOR_USERNAME") @@ -176,7 +175,7 @@ func CopyImage(imageName string) error { } // Push the image to the destination registry - err = crane.Push(srcImage, destRef) + err = crane.Push(srcImage, destRef, crane.Insecure) if err != nil { fmt.Printf("Failed to push image: %v\n", err) return fmt.Errorf("failed to push image: %w", err) diff --git a/main.go b/main.go index df3c04b..0201807 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,6 @@ import ( "os" "os/signal" "path/filepath" - "regexp" "strings" "syscall" "time" @@ -85,13 +84,13 @@ func run() error { registryAdr := viper.GetString("own_registry_adr") // Validate registryAdr format - matched, err := regexp.MatchString(`^127\.0\.0\.1:\d{1,5}$`, registryAdr) - if err != nil { - return fmt.Errorf("error validating registry address: %w", err) - } - if !matched { - return fmt.Errorf("invalid registry address format: %s", registryAdr) - } + // matched, err := regexp.MatchString(`^127\.0\.0\.1:\d{1,5}$`, registryAdr) + // if err != nil { + // return fmt.Errorf("error validating registry address: %w", err) + // } + // if matched { + // return fmt.Errorf("invalid registry address format: %s", registryAdr) + // } os.Setenv("ZOT_URL", registryAdr) fmt.Println("Registry URL set to:", registryAdr) } else { @@ -105,7 +104,6 @@ func run() error { cancel() return err } - }) } diff --git a/test/e2e/registry_test.go b/test/e2e/registry_test.go new file mode 100644 index 0000000..dbf0514 --- /dev/null +++ b/test/e2e/registry_test.go @@ -0,0 +1,282 @@ +package main + +import ( + "context" + "fmt" + "log" + "log/slog" + "os" + "path/filepath" + "testing" + + "dagger.io/dagger" + "github.com/stretchr/testify/assert" +) + +const ( + regUrl = "localhost:5000" + imageToPush = "ubuntu" // Image to push to the registry + imageVersion = "golang:1.22" + exposePort = 9090 + containerPort = 9090 + appDir = "/app" + appBinary = "app" + sourceFile = "main.go" +) + +func TestSetupContainerRegistry(t *testing.T) { + ctx := context.Background() + + // Initialize Dagger client + client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr)) + if err != nil { + log.Fatalf("Failed to connect to Dagger: %v", err) + } + defer client.Close() + + // Set up remote Registry + remote, err := setupRemoteRegistry(client, ctx) + assert.NoError(t, err, "Failed to set up remote registry") + // Set up the container registry + registry, err := setupContainerRegistry(client, ctx) + assert.NoError(t, err, "Failed to set up container registry") + // reg, _ := registry.Hostname(ctx) + // fmt.Println(reg) + // + // // expose HTTP service to host + // tunnel, err := client.Host().Tunnel(registry).Start(ctx) + // assert.NoError(t, err, "Failed to serve tunnel to host") + // + // // get HTTP service address + // endpoint, err := tunnel.Endpoint(ctx, dagger.ServiceEndpointOpts{ + // Scheme: "tcp", + // }) + // assert.NoError(t, err, "Failed to get registry endpoint") + // + // log.Println(endpoint, "\n\n\n\n\nthe tunnel endpoint", endpoint) + + // Push the image to the registry + // pushImageToRegistry(ctx, client, registry, endpoint) + // assert.NoError(t, err, "Failed to upload image to registry") + + // Implement the Satellite Testing + buildSatellite(client, ctx, registry, remote) + assert.NoError(t, err, "Failed to build Satellite") +} + +// Setup Container Registry as a Dagger Service +func setupRemoteRegistry( + client *dagger.Client, + ctx context.Context, +) (*dagger.Service, error) { + // socket to connect to host Docker + socket := client.Host().UnixSocket("/var/run/docker.sock") + configFile := client.Host().File("./testdata/harbor.yml") + dir := client.Host().Directory("./testdata/") + + // Pull the Harbor registry image + container, err := client.Container(). + From("docker:dind"). + WithExposedPort(80).WithUnixSocket("/var/run/docker.sock", socket). + WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock"). + WithMountedDirectory("/data", dir). + WithWorkdir("/data"). + WithExec([]string{"ls"}). + WithExec([]string{"pwd"}). + WithExec([]string{"apk", "update"}). + WithExec([]string{"apk", "add", "wget", "ca-certificates"}). + WithExec([]string{"update-ca-certificates"}). + WithExec([]string{"wget", "https://github.com/goharbor/harbor/releases/download/v2.11.0/harbor-offline-installer-v2.11.0.tgz"}). + WithExec([]string{"pwd"}). + WithExec([]string{"tar", "xzvf", "harbor-offline-installer-v2.11.0.tgz"}). + WithExec([]string{"pwd"}). + WithExec([]string{"sh", "-c", "cd harbor/"}). + WithExec([]string{"pwd"}). + WithExec([]string{"ls", "-al"}). + WithFile("./harbor.yml", configFile). + WithExec([]string{"pwd"}). + WithExec([]string{"pwd || ./install.sh"}). + Sync(ctx) + + service, err := container.AsService().Start(ctx) + if err != nil { + log.Fatal("Error while creating registry: ", err) + } + + // Return the registry URL + return service, nil +} + +// Setup Container Registry as a Dagger Service +func setupContainerRegistry( + client *dagger.Client, + ctx context.Context, +) (*dagger.Service, error) { + // socket to connect to host Docker + // socket := client.Host().UnixSocket("/var/run/docker.sock") + + // Pull the registry image + container, err := client.Container(). + From("registry:2"). + WithExposedPort(5000, dagger.ContainerWithExposedPortOpts{Protocol: "TCP"}). + AsService().Start(ctx) + if err != nil { + log.Fatal("Error while creating registry: ", err) + } + + // Return the registry URL + return container, nil +} + +// Upload image to the registry +func pushImageToRegistry( + ctx context.Context, + client *dagger.Client, + registry *dagger.Service, + srvAddr string, +) { + // socket to connect to host Docker + socket := client.Host().UnixSocket("/var/run/docker.sock") + + container := client.Container(). + From("docker:dind"). + WithServiceBinding("reg", registry).WithUnixSocket("/var/run/docker.sock", socket). + WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock") + + log.Println("completed setting up the docker container") + + log.Println("\n\n\ncompleted pulling docker image") + container = container.WithExec([]string{"docker", "--version"}) + container = container.WithExec([]string{"docker", "info"}) + + log.Println("going to pull the image") + container = container.WithExec([]string{"docker", "pull", imageToPush}) + + // tag the image + imageTag := fmt.Sprintf("%s/%s", srvAddr, imageToPush) + container = container.WithExec([]string{"docker", "tag", imageToPush, imageTag}) + + // list alll the images present + container = container.WithExec([]string{"docker", "images"}) + + // push and then delete the images + container = container.WithExec([]string{"docker", "push", imageTag}) + + container = container.WithExec([]string{"docker", "pull", "golang:1.22"}) + imageTag = fmt.Sprintf("%s/%s", srvAddr, "golang:1.22") + container = container.WithExec([]string{"docker", "tag", "golang:1.22", imageTag}) + + container = container.WithExec([]string{"apk", "add", "go"}). + WithExec([]string{"apk", "add", "curl"}). + WithExec([]string{"apk", "add", "crane"}). + WithExec([]string{"docker", "run", "-d", "-p", "5000:5000", "--name", "registry", "registry:2"}). + WithExec([]string{"docker", "container", "ls"}). + WithExec([]string{"crane", "catalog", "localhost:5000"}) + + container = container.WithExec([]string{"go", "version"}) + + // building and testing the satellite + // buildSatellite(client, ctx, registry, container, srvAddr) + + // prints, _ := container.Stdout(ctx) + // fmt.Println(prints) +} + +// buildSatellite and test test the connection +func buildSatellite( + client *dagger.Client, + ctx context.Context, + registry *dagger.Service, + remote *dagger.Service, +) { + // slog.Info("Starting the Satellite build process...") + // endp, err := registry.Endpoint(ctx) + // hostname, err := registry.Hostname(ctx) + // ports, err := registry.Ports(ctx) + // fmt.Println( + // "\n\n\n \n \n ------------------------------------------------------------------------------", + // ) + // fmt.Printf("endpoint: %s", endp) + // fmt.Printf("hostname: %s", hostname) + // fmt.Printf("ports: %v %v", ports, srvAddr) + + // Get the directory of project located one level up from the current working directory + parentDir, err := getProjectDir() + if err != nil { + log.Fatalf("Error getting parentDirectory: %v", err) + } + + // Use the parent directory path in Dagger + dir := client.Host().Directory(parentDir) + + // Create the configuration file on the host + configFile := client.Host().File("./testdata/config.toml") + + // File path to write the config.toml + // filePath := "./testdata/config.toml" + + // Generate the config file + // err = generateConfigFile(filePath, srvAddr) + // if err != nil { + // log.Fatalf("Failed to generate config file: %v", err) + // } + + // Pull the image from Docker Hub + // socket := client.Host().UnixSocket("/var/run/docker.sock") + + // Configure and build the container + + container := client.Container().From("golang:alpine").WithDirectory(appDir, dir). + WithWorkdir(appDir). + WithServiceBinding("reg", registry). + WithExec([]string{"cat", "config.toml"}). + WithFile("./config.toml", configFile). + WithExec([]string{"apk", "add", "crane"}). + WithExec([]string{"crane", "-v", "catalog", "reg:5000", "--insecure"}). + // WithExec([]string{"curl", "-v", "https://reg:5000"}). + WithExec([]string{"cat", "config.toml"}). + WithExec([]string{"go", "build", "-o", appBinary, sourceFile}). + WithExposedPort(containerPort). + WithExec([]string{"./" + appBinary}) + + slog.Info("Satellite service is running and accessible") + + prints, _ := container.Stdout(ctx) + fmt.Println(prints) +} + +// getProjectDir gets the directory of the project +func getProjectDir() (string, error) { + currentDir, err := os.Getwd() + if err != nil { + return "", err + } + return filepath.Abs(filepath.Join(currentDir, "../..")) +} + +func generateConfigFile(filePath string, srvAddr string) error { + // Define the TOML content + configContent := ` +# Auto-generated +bring_own_registry = true +url_or_file = "https://demo.goharbor.io/v2/library/registry" +` + // addr := strings.TrimPrefix(srvAddr, "http://") + + configContent = configContent + fmt.Sprintf("own_registry_adr = \"%s\"", "reg:5000") + + // Create or open the file + file, err := os.Create(filePath) + if err != nil { + return err + } + defer file.Close() + + // Write the TOML content to the file + _, err = file.WriteString(configContent) + if err != nil { + return err + } + + return nil +} diff --git a/test/e2e/testdata/config.toml b/test/e2e/testdata/config.toml new file mode 100644 index 0000000..1c9de64 --- /dev/null +++ b/test/e2e/testdata/config.toml @@ -0,0 +1,5 @@ + +# Auto-generated +bring_own_registry = true +url_or_file = "https://demo.goharbor.io/v2/library/registry" +own_registry_adr = "reg:5000" \ No newline at end of file diff --git a/test/e2e/testdata/harbor.yml b/test/e2e/testdata/harbor.yml new file mode 100644 index 0000000..6dc9bec --- /dev/null +++ b/test/e2e/testdata/harbor.yml @@ -0,0 +1,313 @@ +# Configuration file of Harbor + +# The IP address or hostname to access admin UI and registry service. +# DO NOT use localhost or 127.0.0.1, because Harbor needs to be accessed by external clients. +hostname: 0.0.0.0 + +# http related config +http: + # port for http, default is 80. If https enabled, this port will redirect to https port + port: 80 + +# # https related config +# https: +# # https port for harbor, default is 443 +# port: 443 +# # The path of cert and key files for nginx +# certificate: /etc/ssl/mycerts/mycert.crt +# private_key: /etc/ssl/mycerts/mykey.key +# # enable strong ssl ciphers (default: false) +# # strong_ssl_ciphers: false + +# # Uncomment following will enable tls communication between all harbor components +# internal_tls: +# # set enabled to true means internal tls is enabled +# enabled: true +# # put your cert and key files on dir +# dir: /etc/harbor/tls/internal + + +# Uncomment external_url if you want to enable external proxy +# And when it enabled the hostname will no longer used +# external_url: https://reg.mydomain.com:8433 + +# The initial password of Harbor admin +# It only works in first time to install harbor +# Remember Change the admin password from UI after launching Harbor. +harbor_admin_password: Harbor12345 + +# Harbor DB configuration +database: + # The password for the root user of Harbor DB. Change this before any production use. + password: root123 + # The maximum number of connections in the idle connection pool. If it <=0, no idle connections are retained. + max_idle_conns: 100 + # The maximum number of open connections to the database. If it <= 0, then there is no limit on the number of open connections. + # Note: the default number of connections is 1024 for postgres of harbor. + max_open_conns: 900 + # The maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If it <= 0, connections are not closed due to a connection's age. + # The value is a duration string. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + conn_max_lifetime: 5m + # The maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If it <= 0, connections are not closed due to a connection's idle time. + # The value is a duration string. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + conn_max_idle_time: 0 + +# The default data volume +# data_volume: /home/bupd/harbor/data + data_volume: /data + +# Harbor Storage settings by default is using /data dir on local filesystem +# Uncomment storage_service setting If you want to using external storage +# storage_service: +# # ca_bundle is the path to the custom root ca certificate, which will be injected into the truststore +# # of registry's containers. This is usually needed when the user hosts a internal storage with self signed certificate. +# ca_bundle: + +# # storage backend, default is filesystem, options include filesystem, azure, gcs, s3, swift and oss +# # for more info about this configuration please refer https://docs.docker.com/registry/configuration/ +# filesystem: +# maxthreads: 100 +# # set disable to true when you want to disable registry redirect +# redirect: +# disable: false + +# Trivy configuration +# +# Trivy DB contains vulnerability information from NVD, Red Hat, and many other upstream vulnerability databases. +# It is downloaded by Trivy from the GitHub release page https://github.com/aquasecurity/trivy-db/releases and cached +# in the local file system. In addition, the database contains the update timestamp so Trivy can detect whether it +# should download a newer version from the Internet or use the cached one. Currently, the database is updated every +# 12 hours and published as a new release to GitHub. +trivy: + # ignoreUnfixed The flag to display only fixed vulnerabilities + ignore_unfixed: false + # skipUpdate The flag to enable or disable Trivy DB downloads from GitHub + # + # You might want to enable this flag in test or CI/CD environments to avoid GitHub rate limiting issues. + # If the flag is enabled you have to download the `trivy-offline.tar.gz` archive manually, extract `trivy.db` and + # `metadata.json` files and mount them in the `/home/scanner/.cache/trivy/db` path. + skip_update: false + # + # skipJavaDBUpdate If the flag is enabled you have to manually download the `trivy-java.db` file and mount it in the + # `/home/scanner/.cache/trivy/java-db/trivy-java.db` path + skip_java_db_update: false + # + # The offline_scan option prevents Trivy from sending API requests to identify dependencies. + # Scanning JAR files and pom.xml may require Internet access for better detection, but this option tries to avoid it. + # For example, the offline mode will not try to resolve transitive dependencies in pom.xml when the dependency doesn't + # exist in the local repositories. It means a number of detected vulnerabilities might be fewer in offline mode. + # It would work if all the dependencies are in local. + # This option doesn't affect DB download. You need to specify "skip-update" as well as "offline-scan" in an air-gapped environment. + offline_scan: false + # + # Comma-separated list of what security issues to detect. Possible values are `vuln`, `config` and `secret`. Defaults to `vuln`. + security_check: vuln + # + # insecure The flag to skip verifying registry certificate + insecure: false + # github_token The GitHub access token to download Trivy DB + # + # Anonymous downloads from GitHub are subject to the limit of 60 requests per hour. Normally such rate limit is enough + # for production operations. If, for any reason, it's not enough, you could increase the rate limit to 5000 + # requests per hour by specifying the GitHub access token. For more details on GitHub rate limiting please consult + # https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting + # + # You can create a GitHub token by following the instructions in + # https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line + # + # github_token: xxx + +jobservice: + # Maximum number of job workers in job service + max_job_workers: 10 + # The jobLoggers backend name, only support "STD_OUTPUT", "FILE" and/or "DB" + job_loggers: + - STD_OUTPUT + - FILE + # - DB + # The jobLogger sweeper duration (ignored if `jobLogger` is `stdout`) + logger_sweeper_duration: 1 #days + +notification: + # Maximum retry count for webhook job + webhook_job_max_retry: 3 + # HTTP client timeout for webhook job + webhook_job_http_client_timeout: 3 #seconds + +# Log configurations +log: + # options are debug, info, warning, error, fatal + level: info + # configs for logs in local storage + local: + # Log files are rotated log_rotate_count times before being removed. If count is 0, old versions are removed rather than rotated. + rotate_count: 50 + # Log files are rotated only if they grow bigger than log_rotate_size bytes. If size is followed by k, the size is assumed to be in kilobytes. + # If the M is used, the size is in megabytes, and if G is used, the size is in gigabytes. So size 100, size 100k, size 100M and size 100G + # are all valid. + rotate_size: 200M + # The directory on your host that store log + location: /data/hlogs + + # Uncomment following lines to enable external syslog endpoint. + # external_endpoint: + # # protocol used to transmit log to external endpoint, options is tcp or udp + # protocol: tcp + # # The host of external endpoint + # host: localhost + # # Port of external endpoint + # port: 5140 + +#This attribute is for migrator to detect the version of the .cfg file, DO NOT MODIFY! +_version: 2.10.0 + +# Uncomment external_database if using external database. +# external_database: +# harbor: +# host: harbor_db_host +# port: harbor_db_port +# db_name: harbor_db_name +# username: harbor_db_username +# password: harbor_db_password +# ssl_mode: disable +# max_idle_conns: 2 +# max_open_conns: 0 + +# Uncomment redis if need to customize redis db +# redis: +# # db_index 0 is for core, it's unchangeable +# # registry_db_index: 1 +# # jobservice_db_index: 2 +# # trivy_db_index: 5 +# # it's optional, the db for harbor business misc, by default is 0, uncomment it if you want to change it. +# # harbor_db_index: 6 +# # it's optional, the db for harbor cache layer, by default is 0, uncomment it if you want to change it. +# # cache_db_index: 7 + +# Uncomment redis if need to customize redis db +# redis: +# # db_index 0 is for core, it's unchangeable +# # registry_db_index: 1 +# # jobservice_db_index: 2 +# # trivy_db_index: 5 +# # it's optional, the db for harbor business misc, by default is 0, uncomment it if you want to change it. +# # harbor_db_index: 6 +# # it's optional, the db for harbor cache layer, by default is 0, uncomment it if you want to change it. +# # cache_layer_db_index: 7 + +# Uncomment external_redis if using external Redis server +# external_redis: +# # support redis, redis+sentinel +# # host for redis: : +# # host for redis+sentinel: +# # :,:,: +# host: redis:6379 +# password: +# # Redis AUTH command was extended in Redis 6, it is possible to use it in the two-arguments AUTH form. +# # there's a known issue when using external redis username ref:https://github.com/goharbor/harbor/issues/18892 +# # if you care about the image pull/push performance, please refer to this https://github.com/goharbor/harbor/wiki/Harbor-FAQs#external-redis-username-password-usage +# # username: +# # sentinel_master_set must be set to support redis+sentinel +# #sentinel_master_set: +# # db_index 0 is for core, it's unchangeable +# registry_db_index: 1 +# jobservice_db_index: 2 +# trivy_db_index: 5 +# idle_timeout_seconds: 30 +# # it's optional, the db for harbor business misc, by default is 0, uncomment it if you want to change it. +# # harbor_db_index: 6 +# # it's optional, the db for harbor cache layer, by default is 0, uncomment it if you want to change it. +# # cache_layer_db_index: 7 + +# Uncomment uaa for trusting the certificate of uaa instance that is hosted via self-signed cert. +# uaa: +# ca_file: /path/to/ca + +# Global proxy +# Config http proxy for components, e.g. http://my.proxy.com:3128 +# Components doesn't need to connect to each others via http proxy. +# Remove component from `components` array if want disable proxy +# for it. If you want use proxy for replication, MUST enable proxy +# for core and jobservice, and set `http_proxy` and `https_proxy`. +# Add domain to the `no_proxy` field, when you want disable proxy +# for some special registry. +proxy: + http_proxy: + https_proxy: + no_proxy: + components: + - core + - jobservice + - trivy + +# metric: +# enabled: false +# port: 9090 +# path: /metrics + +# Trace related config +# only can enable one trace provider(jaeger or otel) at the same time, +# and when using jaeger as provider, can only enable it with agent mode or collector mode. +# if using jaeger collector mode, uncomment endpoint and uncomment username, password if needed +# if using jaeger agetn mode uncomment agent_host and agent_port +# trace: +# enabled: true +# # set sample_rate to 1 if you wanna sampling 100% of trace data; set 0.5 if you wanna sampling 50% of trace data, and so forth +# sample_rate: 1 +# # # namespace used to differenciate different harbor services +# # namespace: +# # # attributes is a key value dict contains user defined attributes used to initialize trace provider +# # attributes: +# # application: harbor +# # # jaeger should be 1.26 or newer. +# # jaeger: +# # endpoint: http://hostname:14268/api/traces +# # username: +# # password: +# # agent_host: hostname +# # # export trace data by jaeger.thrift in compact mode +# # agent_port: 6831 +# # otel: +# # endpoint: hostname:4318 +# # url_path: /v1/traces +# # compression: false +# # insecure: true +# # # timeout is in seconds +# # timeout: 10 + +# Enable purge _upload directories +upload_purging: + enabled: true + # remove files in _upload directories which exist for a period of time, default is one week. + age: 168h + # the interval of the purge operations + interval: 24h + dryrun: false + +# Cache layer configurations +# If this feature enabled, harbor will cache the resource +# `project/project_metadata/repository/artifact/manifest` in the redis +# which can especially help to improve the performance of high concurrent +# manifest pulling. +# NOTICE +# If you are deploying Harbor in HA mode, make sure that all the harbor +# instances have the same behaviour, all with caching enabled or disabled, +# otherwise it can lead to potential data inconsistency. +cache: + # not enabled by default + enabled: false + # keep cache for one day by default + expire_hours: 24 + +# Harbor core configurations +# Uncomment to enable the following harbor core related configuration items. +# core: +# # The provider for updating project quota(usage), there are 2 options, redis or db, +# # by default is implemented by db but you can switch the updation via redis which +# # can improve the performance of high concurrent pushing to the same project, +# # and reduce the database connections spike and occupies. +# # By redis will bring up some delay for quota usage updation for display, so only +# # suggest switch provider to redis if you were ran into the db connections spike aroud +# # the scenario of high concurrent pushing to same project, no improvment for other scenes. +# quota_update_provider: redis # Or db + From faae41883fe5ce5bda951c6ba859e0cefe0374e5 Mon Sep 17 00:00:00 2001 From: bupd Date: Mon, 24 Jun 2024 21:00:21 +0530 Subject: [PATCH 2/4] handle replication methods Signed-off-by: bupd Signed-off-by: bupd --- internal/replicate/replicate.go | 33 +++- internal/store/http-fetch.go | 2 +- internal/store/in-memory-store.go | 17 +- test/e2e/registry_test.go | 124 ++++-------- test/e2e/testdata/config.toml | 5 +- test/e2e/testdata/harbor.yml | 313 ------------------------------ 6 files changed, 84 insertions(+), 410 deletions(-) delete mode 100644 test/e2e/testdata/harbor.yml diff --git a/internal/replicate/replicate.go b/internal/replicate/replicate.go index 3bcda85..84812e1 100644 --- a/internal/replicate/replicate.go +++ b/internal/replicate/replicate.go @@ -97,7 +97,8 @@ func (r *BasicReplicator) DeleteExtraImages(ctx context.Context, imgs []store.Im func getPullSource(image string) string { input := os.Getenv("USER_INPUT") - if os.Getenv("SCHEME") == "https://" { + scheme := os.Getenv("SCHEME") + if strings.HasPrefix(scheme, "http://") || strings.HasPrefix(scheme, "https://") { url := os.Getenv("HOST") + "/" + os.Getenv("REGISTRY") + "/" + image return url } else { @@ -148,9 +149,10 @@ func CopyImage(imageName string) error { return fmt.Errorf("ZOT_URL environment variable is not set") } - srcRef := imageName - destRef := zotUrl + "/" + imageName - fmt.Println("this is destRef: ", destRef) + // Clean up the image name by removing any host part + cleanedImageName := removeHostName(imageName) + destRef := fmt.Sprintf("%s/%s", zotUrl, cleanedImageName) + fmt.Println("Destination reference:", destRef) // Get credentials from environment variables username := os.Getenv("HARBOR_USERNAME") @@ -165,7 +167,7 @@ func CopyImage(imageName string) error { }) // Pull the image with authentication - srcImage, err := crane.Pull(srcRef, crane.WithAuth(auth)) + srcImage, err := crane.Pull(imageName, crane.WithAuth(auth), crane.Insecure) if err != nil { fmt.Printf("Failed to pull image: %v\n", err) return fmt.Errorf("failed to pull image: %w", err) @@ -192,5 +194,26 @@ func CopyImage(imageName string) error { return fmt.Errorf("failed to remove directory: %w", err) } + // // Use crane.Copy to copy the image directly without pulling & storing + // // this only works when remote & local registries are same. + // err := crane.Copy(imageName, destRef, crane.WithAuth(auth), crane.Insecure) + // if err != nil { + // fmt.Printf("Failed to copy image: %v\n", err) + // return fmt.Errorf("failed to copy image: %w", err) + // } else { + // fmt.Println("Image copied successfully") + // fmt.Printf("Copied image from %s to: %s\n", imageName, destRef) + // } + return nil } + +// Split the imageName by "/" and take only the parts after the hostname +func removeHostName(imageName string) string { + parts := strings.Split(imageName, "/") + if len(parts) > 1 { + return strings.Join(parts[1:], "/") + } + + return imageName +} diff --git a/internal/store/http-fetch.go b/internal/store/http-fetch.go index f878710..309287a 100644 --- a/internal/store/http-fetch.go +++ b/internal/store/http-fetch.go @@ -99,7 +99,7 @@ func (client *RemoteImageList) GetDigest(ctx context.Context, tag string) (strin digest, err := crane.Digest(imageRef, crane.WithAuth(&authn.Basic{ Username: username, Password: password, - })) + }), crane.Insecure) if err != nil { fmt.Printf("failed to fetch digest for %s: %v\n", imageRef, err) return "", nil diff --git a/internal/store/in-memory-store.go b/internal/store/in-memory-store.go index 6652e5e..de3a702 100644 --- a/internal/store/in-memory-store.go +++ b/internal/store/in-memory-store.go @@ -155,7 +155,6 @@ func (s *inMemoryStore) List(ctx context.Context) ([]Image, error) { fmt.Println("No changes detected in the store") return nil, nil } - } 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 { // TODO: Rework complicated logic and add support for multiple repositories // checkImageAndDigest checks if the image exists in the store and if the digest matches the image reference func (s *inMemoryStore) checkImageAndDigest(digest string, image string) bool { - // Check if the received image exists in the store for storeDigest, storeImage := range s.images { if storeImage == image { @@ -236,19 +234,18 @@ func (s *inMemoryStore) checkImageAndDigest(digest string, image string) bool { // If adding was successful, return true, else return false err := s.Add(context.Background(), digest, image) return err != nil - } func GetLocalDigest(ctx context.Context, tag string) (string, error) { - zotUrl := os.Getenv("ZOT_URL") userURL := os.Getenv("USER_INPUT") // Remove extra characters from the URLs userURL = userURL[strings.Index(userURL, "//")+2:] userURL = strings.ReplaceAll(userURL, "/v2", "") + regUrl := removeHostName(userURL) // Construct the URL for fetching the digest - url := zotUrl + "/" + userURL + ":" + tag + url := zotUrl + "/" + regUrl + ":" + tag // Use crane.Digest to get the digest of the image digest, err := crane.Digest(url) @@ -258,3 +255,13 @@ func GetLocalDigest(ctx context.Context, tag string) (string, error) { return digest, nil } + +// Split the imageName by "/" and take only the parts after the hostname +func removeHostName(imageName string) string { + parts := strings.Split(imageName, "/") + if len(parts) > 1 { + return strings.Join(parts[1:], "/") + } + + return imageName +} diff --git a/test/e2e/registry_test.go b/test/e2e/registry_test.go index dbf0514..a13eda9 100644 --- a/test/e2e/registry_test.go +++ b/test/e2e/registry_test.go @@ -42,22 +42,10 @@ func TestSetupContainerRegistry(t *testing.T) { assert.NoError(t, err, "Failed to set up container registry") // reg, _ := registry.Hostname(ctx) // fmt.Println(reg) - // - // // expose HTTP service to host - // tunnel, err := client.Host().Tunnel(registry).Start(ctx) - // assert.NoError(t, err, "Failed to serve tunnel to host") - // - // // get HTTP service address - // endpoint, err := tunnel.Endpoint(ctx, dagger.ServiceEndpointOpts{ - // Scheme: "tcp", - // }) - // assert.NoError(t, err, "Failed to get registry endpoint") - // - // log.Println(endpoint, "\n\n\n\n\nthe tunnel endpoint", endpoint) // Push the image to the registry - // pushImageToRegistry(ctx, client, registry, endpoint) - // assert.NoError(t, err, "Failed to upload image to registry") + pushImageToRegistry(ctx, client, remote) + assert.NoError(t, err, "Failed to upload image to registry") // Implement the Satellite Testing buildSatellite(client, ctx, registry, remote) @@ -71,40 +59,20 @@ func setupRemoteRegistry( ) (*dagger.Service, error) { // socket to connect to host Docker socket := client.Host().UnixSocket("/var/run/docker.sock") - configFile := client.Host().File("./testdata/harbor.yml") - dir := client.Host().Directory("./testdata/") // Pull the Harbor registry image container, err := client.Container(). - From("docker:dind"). - WithExposedPort(80).WithUnixSocket("/var/run/docker.sock", socket). + From("registry:2"). + WithExposedPort(5000). + WithUnixSocket("/var/run/docker.sock", socket). WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock"). - WithMountedDirectory("/data", dir). - WithWorkdir("/data"). - WithExec([]string{"ls"}). - WithExec([]string{"pwd"}). - WithExec([]string{"apk", "update"}). - WithExec([]string{"apk", "add", "wget", "ca-certificates"}). - WithExec([]string{"update-ca-certificates"}). - WithExec([]string{"wget", "https://github.com/goharbor/harbor/releases/download/v2.11.0/harbor-offline-installer-v2.11.0.tgz"}). - WithExec([]string{"pwd"}). - WithExec([]string{"tar", "xzvf", "harbor-offline-installer-v2.11.0.tgz"}). - WithExec([]string{"pwd"}). - WithExec([]string{"sh", "-c", "cd harbor/"}). - WithExec([]string{"pwd"}). - WithExec([]string{"ls", "-al"}). - WithFile("./harbor.yml", configFile). - WithExec([]string{"pwd"}). - WithExec([]string{"pwd || ./install.sh"}). - Sync(ctx) - - service, err := container.AsService().Start(ctx) + AsService().Start(ctx) if err != nil { - log.Fatal("Error while creating registry: ", err) + return nil, err } // Return the registry URL - return service, nil + return container, nil } // Setup Container Registry as a Dagger Service @@ -113,12 +81,14 @@ func setupContainerRegistry( ctx context.Context, ) (*dagger.Service, error) { // socket to connect to host Docker - // socket := client.Host().UnixSocket("/var/run/docker.sock") + socket := client.Host().UnixSocket("/var/run/docker.sock") // Pull the registry image container, err := client.Container(). From("registry:2"). WithExposedPort(5000, dagger.ContainerWithExposedPortOpts{Protocol: "TCP"}). + WithUnixSocket("/var/run/docker.sock", socket). + WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock"). AsService().Start(ctx) if err != nil { log.Fatal("Error while creating registry: ", err) @@ -133,53 +103,31 @@ func pushImageToRegistry( ctx context.Context, client *dagger.Client, registry *dagger.Service, - srvAddr string, ) { - // socket to connect to host Docker + // // socket to connect to host Docker socket := client.Host().UnixSocket("/var/run/docker.sock") + // newUrl := strings.Replace(srvAddr, "tcp://", "", 1) + // fmt.Println(newUrl) container := client.Container(). - From("docker:dind"). - WithServiceBinding("reg", registry).WithUnixSocket("/var/run/docker.sock", socket). - WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock") - - log.Println("completed setting up the docker container") - - log.Println("\n\n\ncompleted pulling docker image") - container = container.WithExec([]string{"docker", "--version"}) - container = container.WithExec([]string{"docker", "info"}) - - log.Println("going to pull the image") - container = container.WithExec([]string{"docker", "pull", imageToPush}) - - // tag the image - imageTag := fmt.Sprintf("%s/%s", srvAddr, imageToPush) - container = container.WithExec([]string{"docker", "tag", imageToPush, imageTag}) - - // list alll the images present - container = container.WithExec([]string{"docker", "images"}) - - // push and then delete the images - container = container.WithExec([]string{"docker", "push", imageTag}) + From("alpine"). + WithUnixSocket("/var/run/docker.sock", socket). + WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock"). + WithServiceBinding("remote", registry) - container = container.WithExec([]string{"docker", "pull", "golang:1.22"}) - imageTag = fmt.Sprintf("%s/%s", srvAddr, "golang:1.22") - container = container.WithExec([]string{"docker", "tag", "golang:1.22", imageTag}) + log.Println("completed setting up the pushing container") - container = container.WithExec([]string{"apk", "add", "go"}). - WithExec([]string{"apk", "add", "curl"}). - WithExec([]string{"apk", "add", "crane"}). - WithExec([]string{"docker", "run", "-d", "-p", "5000:5000", "--name", "registry", "registry:2"}). - WithExec([]string{"docker", "container", "ls"}). - WithExec([]string{"crane", "catalog", "localhost:5000"}) + log.Println("going to pull and push the image") - container = container.WithExec([]string{"go", "version"}) + // add crane push images + container = container.WithExec([]string{"apk", "add", "crane"}). + WithExec([]string{"crane", "copy", "busybox:stable", "remote:5000/library/busybox:stable", "--insecure"}). + WithExec([]string{"crane", "copy", "busybox:latest", "remote:5000/library/busybox:latest", "--insecure"}) - // building and testing the satellite - // buildSatellite(client, ctx, registry, container, srvAddr) + container = container.WithExec([]string{"crane", "catalog", "remote:5000", "--insecure"}) - // prints, _ := container.Stdout(ctx) - // fmt.Println(prints) + prints, _ := container.Stdout(ctx) + fmt.Println(prints) } // buildSatellite and test test the connection @@ -188,7 +136,7 @@ func buildSatellite( ctx context.Context, registry *dagger.Service, remote *dagger.Service, -) { +) *dagger.Service { // slog.Info("Starting the Satellite build process...") // endp, err := registry.Endpoint(ctx) // hostname, err := registry.Hostname(ctx) @@ -222,27 +170,37 @@ func buildSatellite( // } // Pull the image from Docker Hub - // socket := client.Host().UnixSocket("/var/run/docker.sock") + socket := client.Host().UnixSocket("/var/run/docker.sock") // Configure and build the container container := client.Container().From("golang:alpine").WithDirectory(appDir, dir). WithWorkdir(appDir). WithServiceBinding("reg", registry). + WithServiceBinding("remote", remote). + WithUnixSocket("/var/run/docker.sock", socket). + WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock"). WithExec([]string{"cat", "config.toml"}). WithFile("./config.toml", configFile). + WithExec([]string{"cat", "config.toml"}). WithExec([]string{"apk", "add", "crane"}). WithExec([]string{"crane", "-v", "catalog", "reg:5000", "--insecure"}). - // WithExec([]string{"curl", "-v", "https://reg:5000"}). - WithExec([]string{"cat", "config.toml"}). + WithExec([]string{"crane", "-v", "catalog", "remote:5000", "--insecure"}). WithExec([]string{"go", "build", "-o", appBinary, sourceFile}). WithExposedPort(containerPort). WithExec([]string{"./" + appBinary}) + service, err := container.AsService().Start(ctx) + if err != nil { + log.Fatalf("Error in running Satellite: %v", err) + } + slog.Info("Satellite service is running and accessible") prints, _ := container.Stdout(ctx) fmt.Println(prints) + + return service } // getProjectDir gets the directory of the project diff --git a/test/e2e/testdata/config.toml b/test/e2e/testdata/config.toml index 1c9de64..7b03204 100644 --- a/test/e2e/testdata/config.toml +++ b/test/e2e/testdata/config.toml @@ -1,5 +1,4 @@ - # Auto-generated bring_own_registry = true -url_or_file = "https://demo.goharbor.io/v2/library/registry" -own_registry_adr = "reg:5000" \ No newline at end of file +own_registry_adr = "reg:5000" +url_or_file = "http://remote:5000/v2/library/busybox" diff --git a/test/e2e/testdata/harbor.yml b/test/e2e/testdata/harbor.yml deleted file mode 100644 index 6dc9bec..0000000 --- a/test/e2e/testdata/harbor.yml +++ /dev/null @@ -1,313 +0,0 @@ -# Configuration file of Harbor - -# The IP address or hostname to access admin UI and registry service. -# DO NOT use localhost or 127.0.0.1, because Harbor needs to be accessed by external clients. -hostname: 0.0.0.0 - -# http related config -http: - # port for http, default is 80. If https enabled, this port will redirect to https port - port: 80 - -# # https related config -# https: -# # https port for harbor, default is 443 -# port: 443 -# # The path of cert and key files for nginx -# certificate: /etc/ssl/mycerts/mycert.crt -# private_key: /etc/ssl/mycerts/mykey.key -# # enable strong ssl ciphers (default: false) -# # strong_ssl_ciphers: false - -# # Uncomment following will enable tls communication between all harbor components -# internal_tls: -# # set enabled to true means internal tls is enabled -# enabled: true -# # put your cert and key files on dir -# dir: /etc/harbor/tls/internal - - -# Uncomment external_url if you want to enable external proxy -# And when it enabled the hostname will no longer used -# external_url: https://reg.mydomain.com:8433 - -# The initial password of Harbor admin -# It only works in first time to install harbor -# Remember Change the admin password from UI after launching Harbor. -harbor_admin_password: Harbor12345 - -# Harbor DB configuration -database: - # The password for the root user of Harbor DB. Change this before any production use. - password: root123 - # The maximum number of connections in the idle connection pool. If it <=0, no idle connections are retained. - max_idle_conns: 100 - # The maximum number of open connections to the database. If it <= 0, then there is no limit on the number of open connections. - # Note: the default number of connections is 1024 for postgres of harbor. - max_open_conns: 900 - # The maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If it <= 0, connections are not closed due to a connection's age. - # The value is a duration string. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". - conn_max_lifetime: 5m - # The maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If it <= 0, connections are not closed due to a connection's idle time. - # The value is a duration string. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". - conn_max_idle_time: 0 - -# The default data volume -# data_volume: /home/bupd/harbor/data - data_volume: /data - -# Harbor Storage settings by default is using /data dir on local filesystem -# Uncomment storage_service setting If you want to using external storage -# storage_service: -# # ca_bundle is the path to the custom root ca certificate, which will be injected into the truststore -# # of registry's containers. This is usually needed when the user hosts a internal storage with self signed certificate. -# ca_bundle: - -# # storage backend, default is filesystem, options include filesystem, azure, gcs, s3, swift and oss -# # for more info about this configuration please refer https://docs.docker.com/registry/configuration/ -# filesystem: -# maxthreads: 100 -# # set disable to true when you want to disable registry redirect -# redirect: -# disable: false - -# Trivy configuration -# -# Trivy DB contains vulnerability information from NVD, Red Hat, and many other upstream vulnerability databases. -# It is downloaded by Trivy from the GitHub release page https://github.com/aquasecurity/trivy-db/releases and cached -# in the local file system. In addition, the database contains the update timestamp so Trivy can detect whether it -# should download a newer version from the Internet or use the cached one. Currently, the database is updated every -# 12 hours and published as a new release to GitHub. -trivy: - # ignoreUnfixed The flag to display only fixed vulnerabilities - ignore_unfixed: false - # skipUpdate The flag to enable or disable Trivy DB downloads from GitHub - # - # You might want to enable this flag in test or CI/CD environments to avoid GitHub rate limiting issues. - # If the flag is enabled you have to download the `trivy-offline.tar.gz` archive manually, extract `trivy.db` and - # `metadata.json` files and mount them in the `/home/scanner/.cache/trivy/db` path. - skip_update: false - # - # skipJavaDBUpdate If the flag is enabled you have to manually download the `trivy-java.db` file and mount it in the - # `/home/scanner/.cache/trivy/java-db/trivy-java.db` path - skip_java_db_update: false - # - # The offline_scan option prevents Trivy from sending API requests to identify dependencies. - # Scanning JAR files and pom.xml may require Internet access for better detection, but this option tries to avoid it. - # For example, the offline mode will not try to resolve transitive dependencies in pom.xml when the dependency doesn't - # exist in the local repositories. It means a number of detected vulnerabilities might be fewer in offline mode. - # It would work if all the dependencies are in local. - # This option doesn't affect DB download. You need to specify "skip-update" as well as "offline-scan" in an air-gapped environment. - offline_scan: false - # - # Comma-separated list of what security issues to detect. Possible values are `vuln`, `config` and `secret`. Defaults to `vuln`. - security_check: vuln - # - # insecure The flag to skip verifying registry certificate - insecure: false - # github_token The GitHub access token to download Trivy DB - # - # Anonymous downloads from GitHub are subject to the limit of 60 requests per hour. Normally such rate limit is enough - # for production operations. If, for any reason, it's not enough, you could increase the rate limit to 5000 - # requests per hour by specifying the GitHub access token. For more details on GitHub rate limiting please consult - # https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting - # - # You can create a GitHub token by following the instructions in - # https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line - # - # github_token: xxx - -jobservice: - # Maximum number of job workers in job service - max_job_workers: 10 - # The jobLoggers backend name, only support "STD_OUTPUT", "FILE" and/or "DB" - job_loggers: - - STD_OUTPUT - - FILE - # - DB - # The jobLogger sweeper duration (ignored if `jobLogger` is `stdout`) - logger_sweeper_duration: 1 #days - -notification: - # Maximum retry count for webhook job - webhook_job_max_retry: 3 - # HTTP client timeout for webhook job - webhook_job_http_client_timeout: 3 #seconds - -# Log configurations -log: - # options are debug, info, warning, error, fatal - level: info - # configs for logs in local storage - local: - # Log files are rotated log_rotate_count times before being removed. If count is 0, old versions are removed rather than rotated. - rotate_count: 50 - # Log files are rotated only if they grow bigger than log_rotate_size bytes. If size is followed by k, the size is assumed to be in kilobytes. - # If the M is used, the size is in megabytes, and if G is used, the size is in gigabytes. So size 100, size 100k, size 100M and size 100G - # are all valid. - rotate_size: 200M - # The directory on your host that store log - location: /data/hlogs - - # Uncomment following lines to enable external syslog endpoint. - # external_endpoint: - # # protocol used to transmit log to external endpoint, options is tcp or udp - # protocol: tcp - # # The host of external endpoint - # host: localhost - # # Port of external endpoint - # port: 5140 - -#This attribute is for migrator to detect the version of the .cfg file, DO NOT MODIFY! -_version: 2.10.0 - -# Uncomment external_database if using external database. -# external_database: -# harbor: -# host: harbor_db_host -# port: harbor_db_port -# db_name: harbor_db_name -# username: harbor_db_username -# password: harbor_db_password -# ssl_mode: disable -# max_idle_conns: 2 -# max_open_conns: 0 - -# Uncomment redis if need to customize redis db -# redis: -# # db_index 0 is for core, it's unchangeable -# # registry_db_index: 1 -# # jobservice_db_index: 2 -# # trivy_db_index: 5 -# # it's optional, the db for harbor business misc, by default is 0, uncomment it if you want to change it. -# # harbor_db_index: 6 -# # it's optional, the db for harbor cache layer, by default is 0, uncomment it if you want to change it. -# # cache_db_index: 7 - -# Uncomment redis if need to customize redis db -# redis: -# # db_index 0 is for core, it's unchangeable -# # registry_db_index: 1 -# # jobservice_db_index: 2 -# # trivy_db_index: 5 -# # it's optional, the db for harbor business misc, by default is 0, uncomment it if you want to change it. -# # harbor_db_index: 6 -# # it's optional, the db for harbor cache layer, by default is 0, uncomment it if you want to change it. -# # cache_layer_db_index: 7 - -# Uncomment external_redis if using external Redis server -# external_redis: -# # support redis, redis+sentinel -# # host for redis: : -# # host for redis+sentinel: -# # :,:,: -# host: redis:6379 -# password: -# # Redis AUTH command was extended in Redis 6, it is possible to use it in the two-arguments AUTH form. -# # there's a known issue when using external redis username ref:https://github.com/goharbor/harbor/issues/18892 -# # if you care about the image pull/push performance, please refer to this https://github.com/goharbor/harbor/wiki/Harbor-FAQs#external-redis-username-password-usage -# # username: -# # sentinel_master_set must be set to support redis+sentinel -# #sentinel_master_set: -# # db_index 0 is for core, it's unchangeable -# registry_db_index: 1 -# jobservice_db_index: 2 -# trivy_db_index: 5 -# idle_timeout_seconds: 30 -# # it's optional, the db for harbor business misc, by default is 0, uncomment it if you want to change it. -# # harbor_db_index: 6 -# # it's optional, the db for harbor cache layer, by default is 0, uncomment it if you want to change it. -# # cache_layer_db_index: 7 - -# Uncomment uaa for trusting the certificate of uaa instance that is hosted via self-signed cert. -# uaa: -# ca_file: /path/to/ca - -# Global proxy -# Config http proxy for components, e.g. http://my.proxy.com:3128 -# Components doesn't need to connect to each others via http proxy. -# Remove component from `components` array if want disable proxy -# for it. If you want use proxy for replication, MUST enable proxy -# for core and jobservice, and set `http_proxy` and `https_proxy`. -# Add domain to the `no_proxy` field, when you want disable proxy -# for some special registry. -proxy: - http_proxy: - https_proxy: - no_proxy: - components: - - core - - jobservice - - trivy - -# metric: -# enabled: false -# port: 9090 -# path: /metrics - -# Trace related config -# only can enable one trace provider(jaeger or otel) at the same time, -# and when using jaeger as provider, can only enable it with agent mode or collector mode. -# if using jaeger collector mode, uncomment endpoint and uncomment username, password if needed -# if using jaeger agetn mode uncomment agent_host and agent_port -# trace: -# enabled: true -# # set sample_rate to 1 if you wanna sampling 100% of trace data; set 0.5 if you wanna sampling 50% of trace data, and so forth -# sample_rate: 1 -# # # namespace used to differenciate different harbor services -# # namespace: -# # # attributes is a key value dict contains user defined attributes used to initialize trace provider -# # attributes: -# # application: harbor -# # # jaeger should be 1.26 or newer. -# # jaeger: -# # endpoint: http://hostname:14268/api/traces -# # username: -# # password: -# # agent_host: hostname -# # # export trace data by jaeger.thrift in compact mode -# # agent_port: 6831 -# # otel: -# # endpoint: hostname:4318 -# # url_path: /v1/traces -# # compression: false -# # insecure: true -# # # timeout is in seconds -# # timeout: 10 - -# Enable purge _upload directories -upload_purging: - enabled: true - # remove files in _upload directories which exist for a period of time, default is one week. - age: 168h - # the interval of the purge operations - interval: 24h - dryrun: false - -# Cache layer configurations -# If this feature enabled, harbor will cache the resource -# `project/project_metadata/repository/artifact/manifest` in the redis -# which can especially help to improve the performance of high concurrent -# manifest pulling. -# NOTICE -# If you are deploying Harbor in HA mode, make sure that all the harbor -# instances have the same behaviour, all with caching enabled or disabled, -# otherwise it can lead to potential data inconsistency. -cache: - # not enabled by default - enabled: false - # keep cache for one day by default - expire_hours: 24 - -# Harbor core configurations -# Uncomment to enable the following harbor core related configuration items. -# core: -# # The provider for updating project quota(usage), there are 2 options, redis or db, -# # by default is implemented by db but you can switch the updation via redis which -# # can improve the performance of high concurrent pushing to the same project, -# # and reduce the database connections spike and occupies. -# # By redis will bring up some delay for quota usage updation for display, so only -# # suggest switch provider to redis if you were ran into the db connections spike aroud -# # the scenario of high concurrent pushing to same project, no improvment for other scenes. -# quota_update_provider: redis # Or db - From 62cfeb9c71f38c4d214e44ab567b9b1476a0860f Mon Sep 17 00:00:00 2001 From: bupd Date: Tue, 25 Jun 2024 02:36:52 +0530 Subject: [PATCH 3/4] add complete e2e test of the satellite Signed-off-by: bupd update dagger test Signed-off-by: bupd add complete e2e test of the satellite Signed-off-by: bupd --- .DS_Store | Bin 10244 -> 0 bytes ci/main.go | 9 -- config.toml | 9 +- registry/.DS_Store | Bin 6148 -> 0 bytes registry/config.json | 2 +- .../{registry_test.go => satellite_test.go} | 99 +++++++++--------- test/e2e/test.go | 54 ++++++++++ test/e2e/testdata/config.toml | 3 + 8 files changed, 110 insertions(+), 66 deletions(-) delete mode 100644 .DS_Store delete mode 100644 registry/.DS_Store rename test/e2e/{registry_test.go => satellite_test.go} (72%) create mode 100644 test/e2e/test.go diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 4e52721db1571b79689b17b0ed86f939f1d2407b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10244 zcmeHMTWl0n82t>p!)<9R3#&ygwqQ%41!`d1ZVTmBV0)t=b$4fIC(O>YJF_iF zicO4{pb`!G;Df|lcrZpKhJ=Vdkf_lYOt3+S*XWapiHREHgAxC8X13Yg7M?^*oJnT> zIsf_obN=(qcXrO11psVLtF-_j0FdeARw<#&GzYKe=afV8JVCNZ@z8EqiMV0KsjN&v z`wpK0p8=l%p8=l%pMn1Z1N6@3hg|Gh|LQZ~GvG5Y$pHU-2+_%H*t6rV6+azx5nBMV zh2$0o-HW9^*${V-4SROnwdA1#-B7Y_D0*8A^mY#DhEsjm!;ibx4F?oGQ#)SI=j>r$FrRlr`ic4ZdnG^ z6D3}-F;E*#Sji#o1ZnHh?7E~CiFltowqK@Dm`M~^ zL`agZYHs3!xa`C;Ser;r=3zNu+1OIit^qM3cz}~feH^1w5{%qPP9x6FqHz{slY|@~ ziL3^LO7?UD$cd2&Wr9NyDYCKrKKMv-@I|l_+MxwnVK+pfH8&%qvE-q7KD-AmM=KXC z@;{N4KZAYLZWBac7g^419>IAdm!TqGCE4@L*A(Wn!1(spw!b%2*TNqF(heJtLsYoqkkV!ElNM1Zbp z1UcBFo2oV1Y^6=r7O_#cLS{1SSIUkbUst!jI#k!NVXQiIeB;`h>d?Bn`mr%4FRZ-3 zabN!tBWYSEx!7pj=vpZCY{qc?OyR+>M_Sx*Nq=U>buBY4#)?^IS9edpqNHtYpJv1y zeMF1I9L?@m=GZjE_Jn2bNogZAw4Q`zq?0Dq%uU3MM4G@_lVL>lhc&y`(T+O&uFtko zPKyBc>J-a;{mMKZ-!bIqmT7luDVr=~ZpSX#Mq_1cC9n%cJSo+zC?N14m!vp!N@Z_4t`Dbm z=a}%aKyV3@mGcKxN_3Z{@)lJOAG}AF4{(1=N~5dmW`X4^C?M8pQ_^k|tg4zZ#qK;p zkIxNdHMKHR)Gi|y*9_0KzMfn=dPdxldCpCZGG$oZOFu+O8jtO|16#tB>7_%w@0~H* z=@`Z&s$nyec36lY@vR^l?e7gu04uE%<8 zz>T;Wx8gQz#~rvA`|toB#6x%(EgZsObZ`tG$0zVfJc%#jtN0qej%V>4zK0j_eY}ic z;5EFCKj2SzLjp;X3Zz1*RGKH<<>oDA=01EWGtzseE9JveOiYJ|~xWJ;a@(Q*%*hR|Lj^1pdDMehv z@&rdOWp@Xa8Kix;xQs0iDl_-Q41uU=33WXfiPoTNkWi3d(Mwfo)_; zAx$ebQNE}wz}V(3j8A3-1hy&I5>)sk`3J=M3cLaDzyz-Pc`z-QogGf?1m5Gp3y zdh`GPZ+9Ag`#u9c19y-CEQ~}W&6Gf9$CIB6|JEL&^C3Fj@*pMP6M2F@LZW|3VP1IEB!G2jN}teoJH yw6|_O9QRrey@s-IUMsjN!6LR|%Mf<}Sg^8y$YTS0gr_9GB!Fk=k-DFff&q(;R6 diff --git a/registry/config.json b/registry/config.json index 01970c0..34e2b82 100644 --- a/registry/config.json +++ b/registry/config.json @@ -10,4 +10,4 @@ "log": { "level": "" } -} \ No newline at end of file +} diff --git a/test/e2e/registry_test.go b/test/e2e/satellite_test.go similarity index 72% rename from test/e2e/registry_test.go rename to test/e2e/satellite_test.go index a13eda9..b9cd438 100644 --- a/test/e2e/registry_test.go +++ b/test/e2e/satellite_test.go @@ -8,20 +8,16 @@ import ( "os" "path/filepath" "testing" + "time" "dagger.io/dagger" "github.com/stretchr/testify/assert" ) const ( - regUrl = "localhost:5000" - imageToPush = "ubuntu" // Image to push to the registry - imageVersion = "golang:1.22" - exposePort = 9090 - containerPort = 9090 - appDir = "/app" - appBinary = "app" - sourceFile = "main.go" + appDir = "/app" + appBinary = "app" + sourceFile = "main.go" ) func TestSetupContainerRegistry(t *testing.T) { @@ -35,25 +31,27 @@ func TestSetupContainerRegistry(t *testing.T) { defer client.Close() // Set up remote Registry - remote, err := setupRemoteRegistry(client, ctx) + remote, err := setupRemoteRegistry(t, client, ctx) assert.NoError(t, err, "Failed to set up remote registry") // Set up the container registry - registry, err := setupContainerRegistry(client, ctx) + registry, err := setupContainerRegistry(t, client, ctx) assert.NoError(t, err, "Failed to set up container registry") // reg, _ := registry.Hostname(ctx) // fmt.Println(reg) // Push the image to the registry - pushImageToRegistry(ctx, client, remote) + pushImageToRegistry(t, ctx, client, remote) assert.NoError(t, err, "Failed to upload image to registry") // Implement the Satellite Testing - buildSatellite(client, ctx, registry, remote) + stdOut := buildSatellite(t, client, ctx, remote, registry) assert.NoError(t, err, "Failed to build Satellite") + fmt.Println(stdOut) } // Setup Container Registry as a Dagger Service func setupRemoteRegistry( + t *testing.T, client *dagger.Client, ctx context.Context, ) (*dagger.Service, error) { @@ -64,12 +62,12 @@ func setupRemoteRegistry( container, err := client.Container(). From("registry:2"). WithExposedPort(5000). - WithUnixSocket("/var/run/docker.sock", socket). + WithUnixSocket("/var/run/docker.sock", socket). WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock"). + WithEnvVariable("CACHEBUSTER", time.Now().String()). AsService().Start(ctx) - if err != nil { - return nil, err - } + + assert.NoError(t, err, "Failed in setting up remote registry.") // Return the registry URL return container, nil @@ -77,6 +75,7 @@ func setupRemoteRegistry( // Setup Container Registry as a Dagger Service func setupContainerRegistry( + t *testing.T, client *dagger.Client, ctx context.Context, ) (*dagger.Service, error) { @@ -87,12 +86,12 @@ func setupContainerRegistry( container, err := client.Container(). From("registry:2"). WithExposedPort(5000, dagger.ContainerWithExposedPortOpts{Protocol: "TCP"}). - WithUnixSocket("/var/run/docker.sock", socket). + WithUnixSocket("/var/run/docker.sock", socket). WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock"). + WithEnvVariable("CACHEBUSTER", time.Now().String()). AsService().Start(ctx) - if err != nil { - log.Fatal("Error while creating registry: ", err) - } + + assert.NoError(t, err, "Failed in setting up registry") // Return the registry URL return container, nil @@ -100,6 +99,7 @@ func setupContainerRegistry( // Upload image to the registry func pushImageToRegistry( + t *testing.T, ctx context.Context, client *dagger.Client, registry *dagger.Service, @@ -110,44 +110,36 @@ func pushImageToRegistry( // fmt.Println(newUrl) container := client.Container(). - From("alpine"). - WithUnixSocket("/var/run/docker.sock", socket). + From("docker:dind"). + WithUnixSocket("/var/run/docker.sock", socket). WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock"). + WithEnvVariable("CACHEBUSTER", time.Now().String()). WithServiceBinding("remote", registry) - log.Println("completed setting up the pushing container") - - log.Println("going to pull and push the image") - // add crane push images container = container.WithExec([]string{"apk", "add", "crane"}). + WithExec([]string{"docker", "pull", "busybox:1.36"}). + WithExec([]string{"docker", "pull", "busybox:stable"}). + WithExec([]string{"crane", "copy", "busybox:1.36", "remote:5000/library/busybox:1.36", "--insecure"}). WithExec([]string{"crane", "copy", "busybox:stable", "remote:5000/library/busybox:stable", "--insecure"}). - WithExec([]string{"crane", "copy", "busybox:latest", "remote:5000/library/busybox:latest", "--insecure"}) + WithExec([]string{"crane", "digest", "remote:5000/library/busybox:1.36", "--insecure"}). + WithExec([]string{"crane", "digest", "remote:5000/library/busybox:stable", "--insecure"}) container = container.WithExec([]string{"crane", "catalog", "remote:5000", "--insecure"}) - prints, _ := container.Stdout(ctx) + prints, err := container.Stdout(ctx) + assert.NoError(t, err, "Failed to push image to remote registry") fmt.Println(prints) } // buildSatellite and test test the connection func buildSatellite( + t *testing.T, client *dagger.Client, ctx context.Context, - registry *dagger.Service, remote *dagger.Service, -) *dagger.Service { - // slog.Info("Starting the Satellite build process...") - // endp, err := registry.Endpoint(ctx) - // hostname, err := registry.Hostname(ctx) - // ports, err := registry.Ports(ctx) - // fmt.Println( - // "\n\n\n \n \n ------------------------------------------------------------------------------", - // ) - // fmt.Printf("endpoint: %s", endp) - // fmt.Printf("hostname: %s", hostname) - // fmt.Printf("ports: %v %v", ports, srvAddr) - + registry *dagger.Service, +) *dagger.Container { // Get the directory of project located one level up from the current working directory parentDir, err := getProjectDir() if err != nil { @@ -176,31 +168,34 @@ func buildSatellite( container := client.Container().From("golang:alpine").WithDirectory(appDir, dir). WithWorkdir(appDir). - WithServiceBinding("reg", registry). WithServiceBinding("remote", remote). - WithUnixSocket("/var/run/docker.sock", socket). + WithServiceBinding("reg", registry). + WithUnixSocket("/var/run/docker.sock", socket). WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock"). + WithEnvVariable("CACHEBUSTER", time.Now().String()). WithExec([]string{"cat", "config.toml"}). WithFile("./config.toml", configFile). WithExec([]string{"cat", "config.toml"}). WithExec([]string{"apk", "add", "crane"}). WithExec([]string{"crane", "-v", "catalog", "reg:5000", "--insecure"}). WithExec([]string{"crane", "-v", "catalog", "remote:5000", "--insecure"}). + WithExec([]string{"crane", "digest", "remote:5000/library/busybox:stable", "--insecure"}). WithExec([]string{"go", "build", "-o", appBinary, sourceFile}). - WithExposedPort(containerPort). - WithExec([]string{"./" + appBinary}) + WithExposedPort(9090). + WithExec([]string{"go", "run", "./test/e2e/test.go"}) - service, err := container.AsService().Start(ctx) - if err != nil { - log.Fatalf("Error in running Satellite: %v", err) - } + assert.NoError(t, err, "Test failed in buildSatellite") + // service, err := container.AsService().Start(ctx) + // if err != nil { + // log.Fatalf("Error in running Satellite: %v", err) + // } - slog.Info("Satellite service is running and accessible") + slog.Info("Satellite is running and accessible") - prints, _ := container.Stdout(ctx) + prints, err := container.Stdout(ctx) fmt.Println(prints) - return service + return container } // getProjectDir gets the directory of the project diff --git a/test/e2e/test.go b/test/e2e/test.go new file mode 100644 index 0000000..2cd30ec --- /dev/null +++ b/test/e2e/test.go @@ -0,0 +1,54 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "os/exec" + "strings" +) + +func main() { + // Command to execute + cmd := exec.Command("go", "run", "./main.go") + + // Get stdout pipe + stdout, err := cmd.StdoutPipe() + if err != nil { + fmt.Println("Error creating stdout pipe:", err) + os.Exit(1) + } + + // Start the command + if err := cmd.Start(); err != nil { + fmt.Println("Error starting command:", err) + os.Exit(1) + } + + // Create a scanner to read the command output line by line + scanner := bufio.NewScanner(stdout) + for scanner.Scan() { + line := scanner.Text() + fmt.Println(line) // Print each line of output + + lineParts := strings.Split(line, "--") + // Check if the line contains "----" + if len(lineParts) > 2 { + fmt.Println("Satellite is Working...\nExiting...") + cmd.Process.Kill() // Kill the process + os.Exit(0) // Exit the program + } + } + + // Handle any scanner error + if err := scanner.Err(); err != nil { + fmt.Println("Error reading stdout:", err) + os.Exit(1) + } + + // Wait for the command to finish + if err := cmd.Wait(); err != nil { + fmt.Println("Command execution failed:", err) + os.Exit(1) + } +} diff --git a/test/e2e/testdata/config.toml b/test/e2e/testdata/config.toml index 7b03204..1343914 100644 --- a/test/e2e/testdata/config.toml +++ b/test/e2e/testdata/config.toml @@ -2,3 +2,6 @@ bring_own_registry = true own_registry_adr = "reg:5000" url_or_file = "http://remote:5000/v2/library/busybox" + +# url_or_file = "https://demo.goharbor.io/v2/myproject/album-server" +# url_or_file = "http://localhost:5001/v2/library/busybox" From 2c5bb85b74138cac3adc10ecb6006cf95ca24cec Mon Sep 17 00:00:00 2001 From: bupd Date: Tue, 25 Jun 2024 20:47:11 +0530 Subject: [PATCH 4/4] improve error handling Signed-off-by: bupd add: .gitignore This commit adds standard go gitignore file to the repo. Signed-off-by: bupd --- .gitignore | 29 +++++- internal/replicate/replicate.go | 13 +-- test/e2e/satellite_test.go | 173 +++++++++++--------------------- test/e2e/test.go | 19 ++-- test/e2e/testdata/config.toml | 6 +- 5 files changed, 99 insertions(+), 141 deletions(-) diff --git a/.gitignore b/.gitignore index 2eea525..86a460d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,28 @@ -.env \ No newline at end of file +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work +/bin + +/harbor +dist/ + +# Remove DS_Store +.DS_Store diff --git a/internal/replicate/replicate.go b/internal/replicate/replicate.go index 84812e1..aa73454 100644 --- a/internal/replicate/replicate.go +++ b/internal/replicate/replicate.go @@ -194,21 +194,10 @@ func CopyImage(imageName string) error { return fmt.Errorf("failed to remove directory: %w", err) } - // // Use crane.Copy to copy the image directly without pulling & storing - // // this only works when remote & local registries are same. - // err := crane.Copy(imageName, destRef, crane.WithAuth(auth), crane.Insecure) - // if err != nil { - // fmt.Printf("Failed to copy image: %v\n", err) - // return fmt.Errorf("failed to copy image: %w", err) - // } else { - // fmt.Println("Image copied successfully") - // fmt.Printf("Copied image from %s to: %s\n", imageName, destRef) - // } - return nil } -// Split the imageName by "/" and take only the parts after the hostname +// take only the parts after the hostname func removeHostName(imageName string) string { parts := strings.Split(imageName, "/") if len(parts) > 1 { diff --git a/test/e2e/satellite_test.go b/test/e2e/satellite_test.go index b9cd438..3175d31 100644 --- a/test/e2e/satellite_test.go +++ b/test/e2e/satellite_test.go @@ -3,8 +3,6 @@ package main import ( "context" "fmt" - "log" - "log/slog" "os" "path/filepath" "testing" @@ -20,37 +18,33 @@ const ( sourceFile = "main.go" ) -func TestSetupContainerRegistry(t *testing.T) { +func TestSatellite(t *testing.T) { ctx := context.Background() // Initialize Dagger client client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr)) - if err != nil { - log.Fatalf("Failed to connect to Dagger: %v", err) - } + assert.NoError(t, err, "Failed to connect to Dagger") defer client.Close() - // Set up remote Registry - remote, err := setupRemoteRegistry(t, client, ctx) - assert.NoError(t, err, "Failed to set up remote registry") - // Set up the container registry - registry, err := setupContainerRegistry(t, client, ctx) - assert.NoError(t, err, "Failed to set up container registry") - // reg, _ := registry.Hostname(ctx) - // fmt.Println(reg) - - // Push the image to the registry - pushImageToRegistry(t, ctx, client, remote) - assert.NoError(t, err, "Failed to upload image to registry") - - // Implement the Satellite Testing - stdOut := buildSatellite(t, client, ctx, remote, registry) - assert.NoError(t, err, "Failed to build Satellite") - fmt.Println(stdOut) + // Set up Source Registry + source, err := setupSourceRegistry(t, client, ctx) + assert.NoError(t, err, "Failed to set up source registry") + + // Set up Destination registry + dest, err := setupDestinationRegistry(t, client, ctx) + assert.NoError(t, err, "Failed to set up destination registry") + + // Push images to Source registry + pushImageToSourceRegistry(t, ctx, client, source) + assert.NoError(t, err, "Failed to upload image to source registry") + + // Build & Run Satellite + buildSatellite(t, client, ctx, source, dest) + assert.NoError(t, err, "Failed to build and run Satellite") } -// Setup Container Registry as a Dagger Service -func setupRemoteRegistry( +// Setup Source Registry as a Dagger Service +func setupSourceRegistry( t *testing.T, client *dagger.Client, ctx context.Context, @@ -58,7 +52,6 @@ func setupRemoteRegistry( // socket to connect to host Docker socket := client.Host().UnixSocket("/var/run/docker.sock") - // Pull the Harbor registry image container, err := client.Container(). From("registry:2"). WithExposedPort(5000). @@ -67,14 +60,13 @@ func setupRemoteRegistry( WithEnvVariable("CACHEBUSTER", time.Now().String()). AsService().Start(ctx) - assert.NoError(t, err, "Failed in setting up remote registry.") + assert.NoError(t, err, "Failed setting up source registry.") - // Return the registry URL return container, nil } -// Setup Container Registry as a Dagger Service -func setupContainerRegistry( +// Setup Destination Registry as a Dagger Service +func setupDestinationRegistry( t *testing.T, client *dagger.Client, ctx context.Context, @@ -82,54 +74,52 @@ func setupContainerRegistry( // socket to connect to host Docker socket := client.Host().UnixSocket("/var/run/docker.sock") - // Pull the registry image container, err := client.Container(). From("registry:2"). - WithExposedPort(5000, dagger.ContainerWithExposedPortOpts{Protocol: "TCP"}). + WithExposedPort(5000). WithUnixSocket("/var/run/docker.sock", socket). WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock"). WithEnvVariable("CACHEBUSTER", time.Now().String()). AsService().Start(ctx) - assert.NoError(t, err, "Failed in setting up registry") + assert.NoError(t, err, "Failed setting up destination registry") - // Return the registry URL return container, nil } -// Upload image to the registry -func pushImageToRegistry( +// Push image to the Source registry +func pushImageToSourceRegistry( t *testing.T, ctx context.Context, client *dagger.Client, - registry *dagger.Service, + source *dagger.Service, ) { - // // socket to connect to host Docker + // socket to connect to host Docker socket := client.Host().UnixSocket("/var/run/docker.sock") - // newUrl := strings.Replace(srvAddr, "tcp://", "", 1) - // fmt.Println(newUrl) container := client.Container(). From("docker:dind"). WithUnixSocket("/var/run/docker.sock", socket). WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock"). WithEnvVariable("CACHEBUSTER", time.Now().String()). - WithServiceBinding("remote", registry) + WithServiceBinding("source", source) - // add crane push images + // add crane & push images container = container.WithExec([]string{"apk", "add", "crane"}). WithExec([]string{"docker", "pull", "busybox:1.36"}). WithExec([]string{"docker", "pull", "busybox:stable"}). - WithExec([]string{"crane", "copy", "busybox:1.36", "remote:5000/library/busybox:1.36", "--insecure"}). - WithExec([]string{"crane", "copy", "busybox:stable", "remote:5000/library/busybox:stable", "--insecure"}). - WithExec([]string{"crane", "digest", "remote:5000/library/busybox:1.36", "--insecure"}). - WithExec([]string{"crane", "digest", "remote:5000/library/busybox:stable", "--insecure"}) + WithExec([]string{"crane", "copy", "busybox:1.36", "source:5000/library/busybox:1.36", "--insecure"}). + WithExec([]string{"crane", "copy", "busybox:stable", "source:5000/library/busybox:stable", "--insecure"}). + WithExec([]string{"crane", "digest", "source:5000/library/busybox:1.36", "--insecure"}). + WithExec([]string{"crane", "digest", "source:5000/library/busybox:stable", "--insecure"}) - container = container.WithExec([]string{"crane", "catalog", "remote:5000", "--insecure"}) + // check pushed images exist + container = container.WithExec([]string{"crane", "catalog", "source:5000", "--insecure"}) - prints, err := container.Stdout(ctx) - assert.NoError(t, err, "Failed to push image to remote registry") - fmt.Println(prints) + stdOut, err := container.Stdout(ctx) + assert.NoError(t, err, "Failed to print stdOut in pushing Image to Source") + + fmt.Println(stdOut) } // buildSatellite and test test the connection @@ -137,39 +127,26 @@ func buildSatellite( t *testing.T, client *dagger.Client, ctx context.Context, - remote *dagger.Service, - registry *dagger.Service, -) *dagger.Container { - // Get the directory of project located one level up from the current working directory + source *dagger.Service, + dest *dagger.Service, +) { + socket := client.Host().UnixSocket("/var/run/docker.sock") + + // Get the directory parentDir, err := getProjectDir() - if err != nil { - log.Fatalf("Error getting parentDirectory: %v", err) - } + assert.NoError(t, err, "Failed to get Project Directory") - // Use the parent directory path in Dagger + // Use the directory path in Dagger dir := client.Host().Directory(parentDir) - // Create the configuration file on the host + // Get configuration file on the host configFile := client.Host().File("./testdata/config.toml") - // File path to write the config.toml - // filePath := "./testdata/config.toml" - - // Generate the config file - // err = generateConfigFile(filePath, srvAddr) - // if err != nil { - // log.Fatalf("Failed to generate config file: %v", err) - // } - - // Pull the image from Docker Hub - socket := client.Host().UnixSocket("/var/run/docker.sock") - - // Configure and build the container - + // Configure and build the Satellite container := client.Container().From("golang:alpine").WithDirectory(appDir, dir). WithWorkdir(appDir). - WithServiceBinding("remote", remote). - WithServiceBinding("reg", registry). + WithServiceBinding("source", source). + WithServiceBinding("dest", dest). WithUnixSocket("/var/run/docker.sock", socket). WithEnvVariable("DOCKER_HOST", "unix:///var/run/docker.sock"). WithEnvVariable("CACHEBUSTER", time.Now().String()). @@ -177,28 +154,21 @@ func buildSatellite( WithFile("./config.toml", configFile). WithExec([]string{"cat", "config.toml"}). WithExec([]string{"apk", "add", "crane"}). - WithExec([]string{"crane", "-v", "catalog", "reg:5000", "--insecure"}). - WithExec([]string{"crane", "-v", "catalog", "remote:5000", "--insecure"}). - WithExec([]string{"crane", "digest", "remote:5000/library/busybox:stable", "--insecure"}). + WithExec([]string{"crane", "-v", "catalog", "source:5000", "--insecure"}). + WithExec([]string{"crane", "digest", "source:5000/library/busybox:stable", "--insecure"}). WithExec([]string{"go", "build", "-o", appBinary, sourceFile}). WithExposedPort(9090). WithExec([]string{"go", "run", "./test/e2e/test.go"}) assert.NoError(t, err, "Test failed in buildSatellite") - // service, err := container.AsService().Start(ctx) - // if err != nil { - // log.Fatalf("Error in running Satellite: %v", err) - // } - slog.Info("Satellite is running and accessible") + stdOut, err := container.Stdout(ctx) + assert.NoError(t, err, "Failed to get stdOut in Satellite") - prints, err := container.Stdout(ctx) - fmt.Println(prints) - - return container + fmt.Println(stdOut) } -// getProjectDir gets the directory of the project +// Gets the directory of the project func getProjectDir() (string, error) { currentDir, err := os.Getwd() if err != nil { @@ -206,30 +176,3 @@ func getProjectDir() (string, error) { } return filepath.Abs(filepath.Join(currentDir, "../..")) } - -func generateConfigFile(filePath string, srvAddr string) error { - // Define the TOML content - configContent := ` -# Auto-generated -bring_own_registry = true -url_or_file = "https://demo.goharbor.io/v2/library/registry" -` - // addr := strings.TrimPrefix(srvAddr, "http://") - - configContent = configContent + fmt.Sprintf("own_registry_adr = \"%s\"", "reg:5000") - - // Create or open the file - file, err := os.Create(filePath) - if err != nil { - return err - } - defer file.Close() - - // Write the TOML content to the file - _, err = file.WriteString(configContent) - if err != nil { - return err - } - - return nil -} diff --git a/test/e2e/test.go b/test/e2e/test.go index 2cd30ec..53ad8f7 100644 --- a/test/e2e/test.go +++ b/test/e2e/test.go @@ -3,6 +3,7 @@ package main import ( "bufio" "fmt" + "log" "os" "os/exec" "strings" @@ -15,14 +16,12 @@ func main() { // Get stdout pipe stdout, err := cmd.StdoutPipe() if err != nil { - fmt.Println("Error creating stdout pipe:", err) - os.Exit(1) + log.Fatalf("Error creating stdout pipe: %v", err) } // Start the command if err := cmd.Start(); err != nil { - fmt.Println("Error starting command:", err) - os.Exit(1) + log.Fatalf("Error starting command: %v", err) } // Create a scanner to read the command output line by line @@ -35,20 +34,20 @@ func main() { // Check if the line contains "----" if len(lineParts) > 2 { fmt.Println("Satellite is Working...\nExiting...") - cmd.Process.Kill() // Kill the process - os.Exit(0) // Exit the program + if err := cmd.Process.Kill(); err != nil { + fmt.Println("Error killing process:", err) + } + os.Exit(0) // Exit the program } } // Handle any scanner error if err := scanner.Err(); err != nil { - fmt.Println("Error reading stdout:", err) - os.Exit(1) + log.Fatalf("Error reading stdout: %v", err) } // Wait for the command to finish if err := cmd.Wait(); err != nil { - fmt.Println("Command execution failed:", err) - os.Exit(1) + log.Fatalf("Command execution failed: %v", err) } } diff --git a/test/e2e/testdata/config.toml b/test/e2e/testdata/config.toml index 1343914..06d9736 100644 --- a/test/e2e/testdata/config.toml +++ b/test/e2e/testdata/config.toml @@ -1,7 +1,7 @@ -# Auto-generated bring_own_registry = true -own_registry_adr = "reg:5000" -url_or_file = "http://remote:5000/v2/library/busybox" +own_registry_adr = "dest:5000" +url_or_file = "http://source:5000/v2/library/busybox" +# Additional test cases need to be handled. # url_or_file = "https://demo.goharbor.io/v2/myproject/album-server" # url_or_file = "http://localhost:5001/v2/library/busybox"