Skip to content

Commit 4bcecaa

Browse files
committed
fix: preserve user-specified registry on OpenShift redeploy
1 parent 3919780 commit 4bcecaa

File tree

5 files changed

+117
-6
lines changed

5 files changed

+117
-6
lines changed

cmd/deploy.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,7 @@ func runDeploy(cmd *cobra.Command, newClient ClientFactory) (err error) {
291291
// also update the registry because there is a registry per namespace,
292292
// and their name includes the namespace.
293293
// This saves needing a manual flag ``--registry={destination namespace registry}``
294-
if changingNamespace(f) && k8s.IsOpenShift() {
295-
// TODO(lkingland): this appears to force use of the openshift
296-
// internal registry.
294+
if changingNamespace(f) && k8s.IsOpenShift() && k8s.IsOpenShiftInternalRegistry(f.Registry) {
297295
f.Registry = "image-registry.openshift-image-registry.svc:5000/" + f.Namespace
298296
if cfg.Verbose {
299297
fmt.Fprintf(cmd.OutOrStdout(), "Info: Overriding openshift registry to %s\n", f.Registry)

cmd/deploy_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,78 @@ func TestDeploy_BasicRedeployPipelinesCorrectNamespace(t *testing.T) {
13371337
}
13381338
}
13391339

1340+
// TestDeploy_NamespaceChangePreservesExternalRegistry ensures that changing
1341+
// namespace on OpenShift does not overwrite an external registry (e.g.
1342+
// docker.io/user) with the internal OpenShift registry.
1343+
// Regression test for https://github.com/knative/func/issues/2172
1344+
func TestDeploy_NamespaceChangePreservesExternalRegistry(t *testing.T) {
1345+
root := FromTempDirectory(t)
1346+
1347+
cleanup := k8s.SetOpenShiftForTest(true)
1348+
defer cleanup()
1349+
1350+
// Create a function deployed to "ns1" with an external registry
1351+
f := fn.Function{
1352+
Runtime: "go",
1353+
Root: root,
1354+
Registry: "docker.io/user",
1355+
Deploy: fn.DeploySpec{Namespace: "ns1"},
1356+
}
1357+
f, err := fn.New().Init(f)
1358+
if err != nil {
1359+
t.Fatal(err)
1360+
}
1361+
1362+
// Deploy to a different namespace
1363+
cmd := NewDeployCmd(NewTestClient(fn.WithDeployer(mock.NewDeployer())))
1364+
cmd.SetArgs([]string{"--namespace=ns2"})
1365+
if err := cmd.Execute(); err != nil {
1366+
t.Fatal(err)
1367+
}
1368+
1369+
// Reload and verify the external registry was preserved
1370+
f, _ = fn.NewFunction(root)
1371+
if f.Registry != "docker.io/user" {
1372+
t.Errorf("expected registry 'docker.io/user' to be preserved, got %q", f.Registry)
1373+
}
1374+
}
1375+
1376+
// TestDeploy_NamespaceChangeUpdatesInternalRegistry ensures that changing
1377+
// namespace on OpenShift DOES update the registry when the function uses
1378+
// the internal OpenShift registry (the namespace is part of the registry path).
1379+
func TestDeploy_NamespaceChangeUpdatesInternalRegistry(t *testing.T) {
1380+
root := FromTempDirectory(t)
1381+
1382+
cleanup := k8s.SetOpenShiftForTest(true)
1383+
defer cleanup()
1384+
1385+
// Create a function deployed to "ns1" using the internal registry
1386+
f := fn.Function{
1387+
Runtime: "go",
1388+
Root: root,
1389+
Registry: "image-registry.openshift-image-registry.svc:5000/ns1",
1390+
Deploy: fn.DeploySpec{Namespace: "ns1"},
1391+
}
1392+
f, err := fn.New().Init(f)
1393+
if err != nil {
1394+
t.Fatal(err)
1395+
}
1396+
1397+
// Deploy to a different namespace
1398+
cmd := NewDeployCmd(NewTestClient(fn.WithDeployer(mock.NewDeployer())))
1399+
cmd.SetArgs([]string{"--namespace=ns2"})
1400+
if err := cmd.Execute(); err != nil {
1401+
t.Fatal(err)
1402+
}
1403+
1404+
// Reload and verify the internal registry was updated to the new namespace
1405+
f, _ = fn.NewFunction(root)
1406+
expected := "image-registry.openshift-image-registry.svc:5000/ns2"
1407+
if f.Registry != expected {
1408+
t.Errorf("expected registry to update to %q, got %q", expected, f.Registry)
1409+
}
1410+
}
1411+
13401412
// TestDeploy_Registry ensures that a function's registry member is kept in
13411413
// sync with the image tag.
13421414
// During normal operation (using the client API) a function's state on disk

pkg/config/config_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ func TestApply(t *testing.T) {
226226
}
227227
}
228228

229-
// TestConfigyre ensures that configuring a function results in every member
229+
// TestConfigure ensures that configuring a function results in every member
230230
// of the function in the intersection of the two sets, global config and function
231231
// members, to be set to the values of the config.
232232
// (See the associated cfg.Apply)
@@ -253,7 +253,7 @@ func TestConfigure(t *testing.T) {
253253
t.Error("configure missing map for f.Registry")
254254
}
255255

256-
// empty values in the global config shoul not zero out function values
256+
// empty values in the global config should not zero out function values
257257
// when configuring.
258258
f = config.Global{}.Configure(f)
259259
if f.Build.Builder == "" {
@@ -268,7 +268,6 @@ func TestConfigure(t *testing.T) {
268268
if f.Registry == "" {
269269
t.Error("empty cfg.Registry should not mutate f")
270270
}
271-
272271
}
273272

274273
// TestGet_Invalid ensures that attempting to get the value of a nonexistent

pkg/k8s/openshift.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"crypto/x509"
66
"encoding/pem"
77
"errors"
8+
"strings"
89
"sync"
910
"time"
1011

@@ -93,6 +94,12 @@ func GetDefaultOpenShiftRegistry() string {
9394
return openShiftRegistryHostPort + "/" + ns
9495
}
9596

97+
// IsOpenShiftInternalRegistry returns true if the given registry string
98+
// refers to the OpenShift internal image registry.
99+
func IsOpenShiftInternalRegistry(registry string) bool {
100+
return strings.HasPrefix(registry, openShiftRegistryHost)
101+
}
102+
96103
func GetOpenShiftDockerCredentialLoaders() []creds.CredentialsCallback {
97104
conf := GetClientConfig()
98105

@@ -126,6 +133,15 @@ func GetOpenShiftDockerCredentialLoaders() []creds.CredentialsCallback {
126133
var isOpenShift bool
127134
var checkOpenShiftOnce sync.Once
128135

136+
// SetOpenShiftForTest overrides OpenShift detection for testing.
137+
// Returns a cleanup function that restores the previous state.
138+
func SetOpenShiftForTest(val bool) func() {
139+
checkOpenShiftOnce.Do(func() {}) // ensure real detection won't run
140+
prev := isOpenShift
141+
isOpenShift = val
142+
return func() { isOpenShift = prev }
143+
}
144+
129145
func IsOpenShift() bool {
130146
checkOpenShiftOnce.Do(func() {
131147
isOpenShift = false

pkg/k8s/openshift_unit_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package k8s
2+
3+
import "testing"
4+
5+
func TestIsOpenShiftInternalRegistry(t *testing.T) {
6+
tests := []struct {
7+
name string
8+
registry string
9+
want bool
10+
}{
11+
{"internal with port and namespace", "image-registry.openshift-image-registry.svc:5000/mynamespace", true},
12+
{"internal without port", "image-registry.openshift-image-registry.svc/mynamespace", true},
13+
{"internal host only", "image-registry.openshift-image-registry.svc", true},
14+
{"docker.io", "docker.io/user", false},
15+
{"ghcr.io", "ghcr.io/user", false},
16+
{"quay.io", "quay.io/user", false},
17+
{"empty string", "", false},
18+
}
19+
for _, tt := range tests {
20+
t.Run(tt.name, func(t *testing.T) {
21+
if got := IsOpenShiftInternalRegistry(tt.registry); got != tt.want {
22+
t.Errorf("expected IsOpenShiftInternalRegistry(%q) = %v, got %v", tt.registry, got, tt.want)
23+
}
24+
})
25+
}
26+
}

0 commit comments

Comments
 (0)