Skip to content

Commit bd195d1

Browse files
committed
Invalidate registries cache upon configuration change to allow dynamic config updates without restart
Signed-off-by: dtfranz <[email protected]>
1 parent a495be1 commit bd195d1

File tree

4 files changed

+127
-4
lines changed

4 files changed

+127
-4
lines changed

cmd/manager/main.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ func main() {
102102
systemNamespace string
103103
caCertDir string
104104
globalPullSecret string
105+
registriesConfig string
105106
)
106107
flag.StringVar(&metricsAddr, "metrics-bind-address", "", "The address for the metrics endpoint. Requires tls-cert and tls-key. (Default: ':8443')")
107108
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
@@ -115,6 +116,7 @@ func main() {
115116
flag.BoolVar(&operatorControllerVersion, "version", false, "Prints operator-controller version information")
116117
flag.StringVar(&systemNamespace, "system-namespace", "", "Configures the namespace that gets used to deploy system resources.")
117118
flag.StringVar(&globalPullSecret, "global-pull-secret", "", "The <namespace>/<name> of the global pull secret that is going to be used to pull bundle images.")
119+
flag.StringVar(&registriesConfig, "registries-config", "/etc/containers/registries.conf", "The local file path of the registries configuration file")
118120

119121
klog.InitFlags(flag.CommandLine)
120122

@@ -294,8 +296,9 @@ func main() {
294296
BaseCachePath: filepath.Join(cachePath, "unpack"),
295297
SourceContextFunc: func(logger logr.Logger) (*types.SystemContext, error) {
296298
srcContext := &types.SystemContext{
297-
DockerCertPath: caCertDir,
298-
OCICertPath: caCertDir,
299+
DockerCertPath: caCertDir,
300+
OCICertPath: caCertDir,
301+
SystemRegistriesConfPath: registriesConfig,
299302
}
300303
if _, err := os.Stat(authFilePath); err == nil && globalPullSecretKey != nil {
301304
logger.Info("using available authentication information for pulling image")

internal/rukpak/source/containers_image.go

+26-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package source
33
import (
44
"archive/tar"
55
"context"
6+
"crypto/sha256"
67
"errors"
78
"fmt"
89
"io"
@@ -17,6 +18,7 @@ import (
1718
"github.com/containers/image/v5/oci/layout"
1819
"github.com/containers/image/v5/pkg/blobinfocache/none"
1920
"github.com/containers/image/v5/pkg/compression"
21+
"github.com/containers/image/v5/pkg/sysregistriesv2"
2022
"github.com/containers/image/v5/signature"
2123
"github.com/containers/image/v5/types"
2224
"github.com/go-logr/logr"
@@ -26,8 +28,9 @@ import (
2628
)
2729

2830
type ContainersImageRegistry struct {
29-
BaseCachePath string
30-
SourceContextFunc func(logger logr.Logger) (*types.SystemContext, error)
31+
BaseCachePath string
32+
SourceContextFunc func(logger logr.Logger) (*types.SystemContext, error)
33+
registriesConfHash [32]byte
3134
}
3235

3336
func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSource) (*Result, error) {
@@ -45,6 +48,22 @@ func (i *ContainersImageRegistry) Unpack(ctx context.Context, bundle *BundleSour
4548
if err != nil {
4649
return nil, err
4750
}
51+
52+
//////////////////////////////////////////////////////
53+
//
54+
// Verify that registries configuration is up-to-date
55+
//
56+
//////////////////////////////////////////////////////
57+
data, err := os.ReadFile(srcCtx.SystemRegistriesConfPath)
58+
if err != nil {
59+
return nil, fmt.Errorf("could not read registries configuration file, error: %w", err)
60+
}
61+
registriesConfHash := sha256.Sum256(data)
62+
if registriesConfHash != i.registriesConfHash {
63+
i.registriesConfHash = registriesConfHash
64+
sysregistriesv2.InvalidateCache()
65+
}
66+
4867
//////////////////////////////////////////////////////
4968
//
5069
// Resolve a canonical reference for the image.
@@ -250,6 +269,11 @@ func (i *ContainersImageRegistry) unpackImage(ctx context.Context, unpackPath st
250269
if err != nil {
251270
return fmt.Errorf("error creating image source: %w", err)
252271
}
272+
defer func() {
273+
if err := layoutSrc.Close(); err != nil {
274+
panic(err)
275+
}
276+
}()
253277

254278
if err := os.MkdirAll(unpackPath, 0700); err != nil {
255279
return fmt.Errorf("error creating unpack directory: %w", err)

test/e2e/cluster_extension_install_test.go

+76
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,82 @@ func TestClusterExtensionInstallRegistry(t *testing.T) {
358358
}
359359
}
360360

361+
func TestClusterExtensionInstallRegistryDynamic(t *testing.T) {
362+
// NOTE: Like 'TestClusterExtensionInstallRegistry', this test also requires extra configuration in /etc/containers/registries.conf
363+
packageName := "dynamic"
364+
365+
t.Log("When a cluster extension is installed from a catalog")
366+
t.Log("When the extension bundle format is registry+v1")
367+
368+
clusterExtension, extensionCatalog, sa, ns := testInit(t)
369+
defer testCleanup(t, extensionCatalog, clusterExtension, sa, ns)
370+
defer getArtifactsOutput(t)
371+
372+
clusterExtension.Spec = ocv1.ClusterExtensionSpec{
373+
Source: ocv1.SourceConfig{
374+
SourceType: "Catalog",
375+
Catalog: &ocv1.CatalogSource{
376+
PackageName: packageName,
377+
Selector: &metav1.LabelSelector{
378+
MatchLabels: map[string]string{"olm.operatorframework.io/metadata.name": extensionCatalog.Name},
379+
},
380+
},
381+
},
382+
Namespace: ns.Name,
383+
ServiceAccount: ocv1.ServiceAccountReference{
384+
Name: sa.Name,
385+
},
386+
}
387+
t.Log("It updates the registries.conf file contents")
388+
cm := corev1.ConfigMap{
389+
ObjectMeta: metav1.ObjectMeta{
390+
Name: "e2e-registries-conf",
391+
Namespace: "olmv1-system",
392+
},
393+
Data: map[string]string{
394+
"registries.conf": `[[registry]]
395+
prefix = "dynamic-registry.operator-controller-e2e.svc.cluster.local:5000"
396+
location = "docker-registry.operator-controller-e2e.svc.cluster.local:5000"`,
397+
},
398+
}
399+
require.NoError(t, c.Update(context.Background(), &cm))
400+
401+
t.Log("It resolves the specified package with correct bundle path")
402+
t.Log("By creating the ClusterExtension resource")
403+
require.NoError(t, c.Create(context.Background(), clusterExtension))
404+
405+
t.Log("By eventually reporting a successful resolution and bundle path")
406+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
407+
assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
408+
}, 2*time.Minute, pollInterval)
409+
410+
// Give the check 2 minutes instead of the typical 1 for the pod's
411+
// files to update from the configmap change.
412+
// The theoretical max time is the kubelet sync period of 1 minute +
413+
// ConfigMap cache TTL of 1 minute = 2 minutes
414+
t.Log("By eventually reporting progressing as True")
415+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
416+
assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
417+
cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeProgressing)
418+
if assert.NotNil(ct, cond) {
419+
assert.Equal(ct, metav1.ConditionTrue, cond.Status)
420+
assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
421+
}
422+
}, 2*time.Minute, pollInterval)
423+
424+
t.Log("By eventually installing the package successfully")
425+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
426+
assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: clusterExtension.Name}, clusterExtension))
427+
cond := apimeta.FindStatusCondition(clusterExtension.Status.Conditions, ocv1.TypeInstalled)
428+
if assert.NotNil(ct, cond) {
429+
assert.Equal(ct, metav1.ConditionTrue, cond.Status)
430+
assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
431+
assert.Contains(ct, cond.Message, "Installed bundle")
432+
assert.NotEmpty(ct, clusterExtension.Status.Install.Bundle)
433+
}
434+
}, pollDuration, pollInterval)
435+
}
436+
361437
func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) {
362438
t.Log("When a cluster extension is installed from a catalog")
363439

testdata/images/catalogs/test-catalog/v1/configs/catalog.yaml

+20
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,23 @@ properties:
6969
value:
7070
packageName: test-mirrored
7171
version: 1.2.0
72+
---
73+
schema: olm.package
74+
name: dynamic
75+
defaultChannel: beta
76+
---
77+
schema: olm.channel
78+
name: beta
79+
package: dynamic
80+
entries:
81+
- name: dynamic-operator.1.2.0
82+
---
83+
schema: olm.bundle
84+
name: dynamic-operator.1.2.0
85+
package: dynamic
86+
image: dynamic-registry.operator-controller-e2e.svc.cluster.local:5000/bundles/registry-v1/test-operator:v1.0.0
87+
properties:
88+
- type: olm.package
89+
value:
90+
packageName: dynamic
91+
version: 1.2.0

0 commit comments

Comments
 (0)