Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test-proxy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ jobs:
run: |
chmod +x build/zarf

- name: Run tests
- name: Run proxy tests
run: |
build/zarf init --features=registry-proxy=true --registry-mode=proxy --confirm
make test-proxy

- name: get cluster info
uses: ./.github/actions/debug-cluster
Expand Down
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@ test-external: ## Run the Zarf CLI E2E tests for an external registry and cluste
@test -s ./build/zarf-init-$(ARCH)-$(CLI_VERSION).tar.zst || $(MAKE) init-package
cd src/test/external && go test -failfast -v -timeout 30m

.PHONY: test-proxy
test-proxy:
@test -s $(ZARF_BIN) || $(MAKE)
@test -s ./build/zarf-init-$(ARCH)-$(CLI_VERSION).tar.zst || $(MAKE) init-package
cd src/test/proxy && go test -failfast -v -timeout 30m

## NOTE: Requires an existing cluster and
.PHONY: test-upgrade
test-upgrade: ## Run the Zarf CLI E2E tests for an external registry and cluster
Expand Down
2 changes: 1 addition & 1 deletion src/internal/agent/hooks/flux-helmrepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func mutateHelmRepo(ctx context.Context, r *v1.AdmissionRequest, cluster *cluste
}

// Get the registry service info if this is a NodePort service to use the internal kube-dns
registryAddress, err := cluster.GetServiceInfoFromRegistryAddress(ctx, zarfState.RegistryInfo.Address)
registryAddress, err := cluster.GetServiceInfoFromRegistryAddress(ctx, zarfState.RegistryInfo)
if err != nil {
return nil, err
}
Expand Down
8 changes: 3 additions & 5 deletions src/internal/agent/hooks/flux-helmrepo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func TestFluxHelmMutationWebhook(t *testing.T) {
patch: []operations.PatchOperation{
operations.ReplacePatchOperation(
"/spec/url",
"oci://10.11.12.13:5000/stefanprodan/charts",
"oci://zarf-docker-registry.zarf.svc.cluster.local:5000/stefanprodan/charts",
),
operations.AddPatchOperation(
"/spec/secretRef",
Expand Down Expand Up @@ -140,7 +140,6 @@ func TestFluxHelmMutationWebhook(t *testing.T) {
Port: 5000,
},
},
ClusterIP: "10.11.12.13",
},
},
code: http.StatusOK,
Expand Down Expand Up @@ -181,14 +180,14 @@ func TestFluxHelmMutationWebhook(t *testing.T) {
Name: "no-mutate-this",
},
Spec: flux.HelmRepositorySpec{
URL: "oci://10.11.12.13:5000/stefanprodan/charts",
URL: "oci://zarf-docker-registry.zarf.svc.cluster.local:5000/stefanprodan/charts",
Type: "oci",
},
}),
patch: []operations.PatchOperation{
operations.ReplacePatchOperation(
"/spec/url",
"oci://10.11.12.13:5000/stefanprodan/charts",
"oci://zarf-docker-registry.zarf.svc.cluster.local:5000/stefanprodan/charts",
),
operations.AddPatchOperation(
"/spec/secretRef",
Expand Down Expand Up @@ -218,7 +217,6 @@ func TestFluxHelmMutationWebhook(t *testing.T) {
Port: 5000,
},
},
ClusterIP: "10.11.12.13",
},
},
code: http.StatusOK,
Expand Down
2 changes: 1 addition & 1 deletion src/internal/agent/hooks/flux-ocirepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func mutateOCIRepo(ctx context.Context, r *v1.AdmissionRequest, cluster *cluster
}

// Get the registry service info if this is a NodePort service to use the internal kube-dns
registryAddress, err := cluster.GetServiceInfoFromRegistryAddress(ctx, zarfState.RegistryInfo.Address)
registryAddress, err := cluster.GetServiceInfoFromRegistryAddress(ctx, zarfState.RegistryInfo)
if err != nil {
return nil, err
}
Expand Down
8 changes: 3 additions & 5 deletions src/internal/agent/hooks/flux-ocirepo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ func TestFluxOCIMutationWebhook(t *testing.T) {
patch: []operations.PatchOperation{
operations.ReplacePatchOperation(
"/spec/url",
"oci://10.11.12.13:5000/stefanprodan/charts",
"oci://zarf-docker-registry.zarf.svc.cluster.local:5000/stefanprodan/charts",
),
operations.AddPatchOperation(
"/spec/secretRef",
Expand Down Expand Up @@ -239,7 +239,6 @@ func TestFluxOCIMutationWebhook(t *testing.T) {
Port: 5000,
},
},
ClusterIP: "10.11.12.13",
},
},
code: http.StatusOK,
Expand Down Expand Up @@ -286,7 +285,7 @@ func TestFluxOCIMutationWebhook(t *testing.T) {
Name: "mutate-this",
},
Spec: flux.OCIRepositorySpec{
URL: "oci://10.11.12.13:5000/stefanprodan/charts",
URL: "oci://zarf-docker-registry.zarf.svc.cluster.local:5000/stefanprodan/charts",
Reference: &flux.OCIRepositoryRef{
Digest: "sha256:6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b",
},
Expand All @@ -295,7 +294,7 @@ func TestFluxOCIMutationWebhook(t *testing.T) {
patch: []operations.PatchOperation{
operations.ReplacePatchOperation(
"/spec/url",
"oci://10.11.12.13:5000/stefanprodan/charts",
"oci://zarf-docker-registry.zarf.svc.cluster.local:5000/stefanprodan/charts",
),
operations.AddPatchOperation(
"/spec/secretRef",
Expand Down Expand Up @@ -325,7 +324,6 @@ func TestFluxOCIMutationWebhook(t *testing.T) {
Port: 5000,
},
},
ClusterIP: "10.11.12.13",
},
},
code: http.StatusOK,
Expand Down
68 changes: 49 additions & 19 deletions src/pkg/cluster/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ type DockerConfigEntryWithAuth struct {
Auth string `json:"auth"`
}

// addRegistryAuthEntries adds registry authentication entries for a service's ClusterIP and DNS hostname.
func addRegistryAuthEntries(auths DockerConfigEntry, svc *corev1.Service, port int32, authValue string) {
kubeDNSRegistryURL := fmt.Sprintf("%s:%d", svc.Spec.ClusterIP, port)
auths[kubeDNSRegistryURL] = DockerConfigEntryWithAuth{
Auth: authValue,
}

kubeDNSRegistryHostname := fmt.Sprintf("%s.%s.svc.cluster.local:%d", svc.Name, svc.Namespace, port)
auths[kubeDNSRegistryHostname] = DockerConfigEntryWithAuth{
Auth: authValue,
}
}

// GenerateRegistryPullCreds generates a secret containing the registry credentials.
func (c *Cluster) GenerateRegistryPullCreds(ctx context.Context, namespace, name string, registryInfo state.RegistryInfo) (*v1ac.SecretApplyConfiguration, error) {
// Auth field must be username:password and base64 encoded
Expand All @@ -48,21 +61,27 @@ func (c *Cluster) GenerateRegistryPullCreds(ctx context.Context, namespace, name
},
}

serviceList, err := c.Clientset.CoreV1().Services("").List(ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}
// Build zarf-docker-registry service address and internal dns string
svc, port, err := serviceInfoFromNodePortURL(serviceList.Items, registryInfo.Address)
if err == nil {
kubeDNSRegistryURL := fmt.Sprintf("%s:%d", svc.Spec.ClusterIP, port)
dockerConfigJSON.Auths[kubeDNSRegistryURL] = DockerConfigEntryWithAuth{
Auth: authEncodedValue,
if registryInfo.RegistryMode == state.RegistryModeProxy {
svc, err := c.Clientset.CoreV1().Services("zarf").Get(ctx, "zarf-docker-registry", metav1.GetOptions{})
if err != nil && !kerrors.IsNotFound(err) {
return nil, err
}

kubeDNSRegistryHostname := fmt.Sprintf("%s.%s.svc.cluster.local:%d", svc.Name, svc.Namespace, port)
dockerConfigJSON.Auths[kubeDNSRegistryHostname] = DockerConfigEntryWithAuth{
Auth: authEncodedValue,
if !kerrors.IsNotFound(err) {
if len(svc.Spec.Ports) == 0 {
return nil, fmt.Errorf("registry service has no ports")
}
port := svc.Spec.Ports[0].Port
addRegistryAuthEntries(dockerConfigJSON.Auths, svc, port, authEncodedValue)
}
} else {
serviceList, err := c.Clientset.CoreV1().Services("").List(ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}
// Build zarf-docker-registry service address and internal dns string
svc, port, err := serviceInfoFromNodePortURL(serviceList.Items, registryInfo.Address)
if err == nil {
addRegistryAuthEntries(dockerConfigJSON.Auths, &svc, int32(port), authEncodedValue)
}
}

Expand Down Expand Up @@ -162,18 +181,29 @@ func (c *Cluster) UpdateZarfManagedGitSecrets(ctx context.Context, s *state.Stat
}

// GetServiceInfoFromRegistryAddress gets the service info for a registry address if it is a NodePort
func (c *Cluster) GetServiceInfoFromRegistryAddress(ctx context.Context, stateRegistryAddress string) (string, error) {
func (c *Cluster) GetServiceInfoFromRegistryAddress(ctx context.Context, registryInfo state.RegistryInfo) (string, error) {
if registryInfo.RegistryMode == state.RegistryModeProxy {
svc, err := c.Clientset.CoreV1().Services("zarf").Get(ctx, "zarf-docker-registry", metav1.GetOptions{})
if err != nil {
return "", err
}
if len(svc.Spec.Ports) == 0 {
return "", fmt.Errorf("registry service has no ports")
}
return fmt.Sprintf("%s.%s.svc.cluster.local:%d", svc.Name, svc.Namespace, svc.Spec.Ports[0].Port), nil
}

serviceList, err := c.Clientset.CoreV1().Services("").List(ctx, metav1.ListOptions{})
if err != nil {
return "", err
}

// If this is an internal service then we need to look it up and
svc, port, err := serviceInfoFromNodePortURL(serviceList.Items, stateRegistryAddress)
svc, port, err := serviceInfoFromNodePortURL(serviceList.Items, registryInfo.Address)
if err != nil {
logger.From(ctx).Debug("registry appears to not be a nodeport service, using original address", "address", stateRegistryAddress)
return stateRegistryAddress, nil
logger.From(ctx).Debug("registry appears to not be a nodeport service, using original address", "address", registryInfo.Address)
return registryInfo.Address, nil
}

return fmt.Sprintf("%s:%d", svc.Spec.ClusterIP, port), nil
return fmt.Sprintf("%s.%s.svc.cluster.local:%d", svc.Name, svc.Namespace, port), nil
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing behavior of the nodeport service as well. I cannot think of any reason we initially used clusterip. Seems like hostname is a more consistent alternative.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also a little un-educated guess, but I wonder if it is because not every cluster uses cluster.local as its FQDN?

}
1 change: 1 addition & 0 deletions src/pkg/cluster/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ func (c *Cluster) findPodContainerPort(ctx context.Context, svc corev1.Service)
}

// TODO: Refactor to use netip.AddrPort instead of a string for nodePortURL.
// This functions assumes that the nodePortURL is in the form 127.0.0.1:<port>
func serviceInfoFromNodePortURL(services []corev1.Service, nodePortURL string) (corev1.Service, int, error) {
// Attempt to parse as normal, if this fails add a scheme to the URL (docker registries don't use schemes)
parsedURL, err := url.Parse(nodePortURL)
Expand Down
2 changes: 1 addition & 1 deletion src/test/e2e/22_git_and_gitops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func waitFluxPodInfoDeployment(t *testing.T) {
require.NoError(t, err)
s, err := c.LoadState(ctx)
require.NoError(t, err, "Failed to load Zarf state")
registryAddress, err := c.GetServiceInfoFromRegistryAddress(ctx, s.RegistryInfo.Address)
registryAddress, err := c.GetServiceInfoFromRegistryAddress(ctx, s.RegistryInfo)
require.NoError(t, err)
// Deploy the flux example and verify that it works
stdOut, stdErr, err := e2e.Zarf(t, "package", "create", "examples/podinfo-flux", "-o", tmpdir, "--skip-sbom")
Expand Down
38 changes: 38 additions & 0 deletions src/test/proxy/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

// Package proxy provides tests for Zarf registry proxy mode.
package proxy

import (
"log"
"os"
"path/filepath"
"testing"

"github.com/zarf-dev/zarf/src/test"
)

var (
e2e test.ZarfE2ETest
)

func TestMain(m *testing.M) {
rootDir, err := filepath.Abs("../../../")
if err != nil {
log.Fatal(err)
}
if err := os.Chdir(rootDir); err != nil {
log.Fatal(err)
}

e2e.ZarfBinPath, err = filepath.Abs(filepath.Join("build", test.GetCLIName()))
if err != nil {
log.Fatal(err)
}

if _, err := os.Stat(e2e.ZarfBinPath); err != nil {
log.Fatalf("zarf binary %s not found: %v", e2e.ZarfBinPath, err)
}
os.Exit(m.Run())
}
43 changes: 43 additions & 0 deletions src/test/proxy/proxy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

// Package proxy provides tests for Zarf registry proxy mode.
package proxy

import (
"fmt"
"path/filepath"
"runtime"
"testing"

"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)

type RegistryProxyTestSuite struct {
suite.Suite
*require.Assertions
}

func (suite *RegistryProxyTestSuite) SetupSuite() {
suite.Assertions = require.New(suite.T())
}

func (suite *RegistryProxyTestSuite) Test_0_RegistryProxyInit() {
stdOut, stdErr, err := e2e.Zarf(suite.T(), "init", "--features=registry-proxy=true", "--registry-mode=proxy", "--components=git-server", "--confirm")
suite.NoError(err, stdOut, stdErr)
}

func (suite *RegistryProxyTestSuite) Test_1_Flux() {
tmpdir := suite.T().TempDir()
stdOut, stdErr, err := e2e.Zarf(suite.T(), "package", "create", "examples/podinfo-flux", "-o", tmpdir)
suite.NoError(err, stdOut, stdErr)

deployPath := filepath.Join(tmpdir, fmt.Sprintf("zarf-package-podinfo-flux-%s.tar.zst", runtime.GOARCH))
stdOut, stdErr, err = e2e.Zarf(suite.T(), "package", "deploy", deployPath, "--confirm")
suite.NoError(err, stdOut, stdErr)
}

func TestRegistryProxy(t *testing.T) {
suite.Run(t, new(RegistryProxyTestSuite))
}
Loading