Skip to content
This repository was archived by the owner on Jun 13, 2023. It is now read-only.

Commit 3cfa8d2

Browse files
authored
support dockerconfigjson secret for oci registry authentication (#202)
* add AuthSecretSelector refactor descriptor parse to support different auth keychain * add test for private registry * make lint happy * change CredSecretSelector to imagespec * add cred secret support to config image * remove AuthSecretSelector * remove OCIRegistryCred as a fixed secret label selector * upgrade k8s.io/* package to the same version * downgrade k8s.io/* package due to controller-runtime breaking changes * upgrade controller-runtime to latest v0.14.1 * resolve lint * add todo * refactor how to apply options
1 parent 0d1070d commit 3cfa8d2

File tree

15 files changed

+653
-789
lines changed

15 files changed

+653
-789
lines changed

api/v1alpha1/zz_generated.deepcopy.go

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/operator.kyma-project.io_manifests.yaml

+96
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,54 @@ spec:
4545
config:
4646
description: Config specifies OCI image configuration for Manifest
4747
properties:
48+
credSecretSelector:
49+
description: CredSecretSelector is on optional field, for OCI
50+
image saved in private registry, use it to indicate the secret
51+
which contains registry credentials, must exist in the namespace
52+
same as manifest
53+
properties:
54+
matchExpressions:
55+
description: matchExpressions is a list of label selector
56+
requirements. The requirements are ANDed.
57+
items:
58+
description: A label selector requirement is a selector
59+
that contains values, a key, and an operator that relates
60+
the key and values.
61+
properties:
62+
key:
63+
description: key is the label key that the selector
64+
applies to.
65+
type: string
66+
operator:
67+
description: operator represents a key's relationship
68+
to a set of values. Valid operators are In, NotIn,
69+
Exists and DoesNotExist.
70+
type: string
71+
values:
72+
description: values is an array of string values. If
73+
the operator is In or NotIn, the values array must
74+
be non-empty. If the operator is Exists or DoesNotExist,
75+
the values array must be empty. This array is replaced
76+
during a strategic merge patch.
77+
items:
78+
type: string
79+
type: array
80+
required:
81+
- key
82+
- operator
83+
type: object
84+
type: array
85+
matchLabels:
86+
additionalProperties:
87+
type: string
88+
description: matchLabels is a map of {key,value} pairs. A
89+
single {key,value} in the matchLabels map is equivalent
90+
to an element of matchExpressions, whose key field is "key",
91+
the operator is "In", and the values array contains only
92+
"value". The requirements are ANDed.
93+
type: object
94+
type: object
95+
x-kubernetes-map-type: atomic
4896
name:
4997
description: Name defines the Image name
5098
type: string
@@ -66,6 +114,54 @@ spec:
66114
crds:
67115
description: CRDs specifies the custom resource definitions' ImageSpec
68116
properties:
117+
credSecretSelector:
118+
description: CredSecretSelector is on optional field, for OCI
119+
image saved in private registry, use it to indicate the secret
120+
which contains registry credentials, must exist in the namespace
121+
same as manifest
122+
properties:
123+
matchExpressions:
124+
description: matchExpressions is a list of label selector
125+
requirements. The requirements are ANDed.
126+
items:
127+
description: A label selector requirement is a selector
128+
that contains values, a key, and an operator that relates
129+
the key and values.
130+
properties:
131+
key:
132+
description: key is the label key that the selector
133+
applies to.
134+
type: string
135+
operator:
136+
description: operator represents a key's relationship
137+
to a set of values. Valid operators are In, NotIn,
138+
Exists and DoesNotExist.
139+
type: string
140+
values:
141+
description: values is an array of string values. If
142+
the operator is In or NotIn, the values array must
143+
be non-empty. If the operator is Exists or DoesNotExist,
144+
the values array must be empty. This array is replaced
145+
during a strategic merge patch.
146+
items:
147+
type: string
148+
type: array
149+
required:
150+
- key
151+
- operator
152+
type: object
153+
type: array
154+
matchLabels:
155+
additionalProperties:
156+
type: string
157+
description: matchLabels is a map of {key,value} pairs. A
158+
single {key,value} in the matchLabels map is equivalent
159+
to an element of matchExpressions, whose key field is "key",
160+
the operator is "In", and the values array contains only
161+
"value". The requirements are ANDed.
162+
type: object
163+
type: object
164+
x-kubernetes-map-type: atomic
69165
name:
70166
description: Name defines the Image name
71167
type: string

controllers/manifest_controller_helper_test.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,20 @@ func PushToRemoteOCIRegistry(layerName string, ociLayerType OCILayerType) {
106106
Expect(gotHash).To(Equal(digest))
107107
}
108108

109-
func createImageSpec(name, repo string, ociLayerType OCILayerType) manifestTypes.ImageSpec {
109+
func createOCIImageSpecWithCredSelect(name, repo, digest string,
110+
credSecretSelector metav1.LabelSelector,
111+
) manifestTypes.ImageSpec {
112+
imageSpec := manifestTypes.ImageSpec{
113+
Name: name,
114+
Repo: repo,
115+
Type: "oci-ref",
116+
Ref: digest,
117+
CredSecretSelector: &credSecretSelector,
118+
}
119+
return imageSpec
120+
}
121+
122+
func createOCIImageSpec(name, repo string, ociLayerType OCILayerType) manifestTypes.ImageSpec {
110123
imageSpec := manifestTypes.ImageSpec{
111124
Name: name,
112125
Repo: repo,

controllers/manifest_controller_test.go

+6-7
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,7 @@ var _ = Describe("Given manifest with OCI specs", Ordered, func() {
145145
Entry("When Manifest CR contains an invalid install OCI image specification, "+
146146
"expect state in error and no helmClient cache exit",
147147
withInvalidInstallImageSpec(false),
148-
expectManifestStateIn(v1alpha1.ManifestStateError), expectHelmClientCacheExist(false),
149-
),
148+
expectManifestStateIn(v1alpha1.ManifestStateError), expectHelmClientCacheExist(false)),
150149
)
151150
})
152151

@@ -185,7 +184,7 @@ var _ = Describe("Test multiple Manifest CRs with same parent and OCI spec", Ord
185184
manifestWithInstall := NewTestManifest("multi-oci1")
186185
Eventually(withValidInstallImageSpec(installName, false), standardTimeout, standardInterval).
187186
WithArguments(manifestWithInstall).Should(Succeed())
188-
validImageSpec := createImageSpec(installName, server.Listener.Addr().String(), layerInstalls)
187+
validImageSpec := createOCIImageSpec(installName, server.Listener.Addr().String(), layerInstalls)
189188
Eventually(expectHelmClientCacheExist(true), standardTimeout, standardInterval).
190189
WithArguments(manifestWithInstall.GetLabels()[labels.ComponentOwner]).Should(BeTrue())
191190
// this will ensure only manifest.yaml remains
@@ -218,7 +217,7 @@ func skipExpect() func() bool {
218217

219218
func withInvalidInstallImageSpec(remote bool) func(manifest *v1alpha1.Manifest) error {
220219
return func(manifest *v1alpha1.Manifest) error {
221-
invalidImageSpec := createImageSpec("invalid-image-spec", "domain.invalid", layerInstalls)
220+
invalidImageSpec := createOCIImageSpec("invalid-image-spec", "domain.invalid", layerInstalls)
222221
imageSpecByte, err := json.Marshal(invalidImageSpec)
223222
Expect(err).ToNot(HaveOccurred())
224223
return installManifest(manifest, imageSpecByte, types.ImageSpec{}, remote)
@@ -227,7 +226,7 @@ func withInvalidInstallImageSpec(remote bool) func(manifest *v1alpha1.Manifest)
227226

228227
func withValidInstallImageSpec(name string, remote bool) func(manifest *v1alpha1.Manifest) error {
229228
return func(manifest *v1alpha1.Manifest) error {
230-
validImageSpec := createImageSpec(name, server.Listener.Addr().String(), layerInstalls)
229+
validImageSpec := createOCIImageSpec(name, server.Listener.Addr().String(), layerInstalls)
231230
imageSpecByte, err := json.Marshal(validImageSpec)
232231
Expect(err).ToNot(HaveOccurred())
233232
return installManifest(manifest, imageSpecByte, types.ImageSpec{}, remote)
@@ -244,11 +243,11 @@ func withValidInstallAndCRDsImageSpec(installName,
244243
crdName string, remote bool,
245244
) func(manifest *v1alpha1.Manifest) error {
246245
return func(manifest *v1alpha1.Manifest) error {
247-
validInstallImageSpec := createImageSpec(installName, server.Listener.Addr().String(), layerInstalls)
246+
validInstallImageSpec := createOCIImageSpec(installName, server.Listener.Addr().String(), layerInstalls)
248247
installSpecByte, err := json.Marshal(validInstallImageSpec)
249248
Expect(err).ToNot(HaveOccurred())
250249

251-
validCRDsImageSpec := createImageSpec(crdName, server.Listener.Addr().String(), layerCRDs)
250+
validCRDsImageSpec := createOCIImageSpec(crdName, server.Listener.Addr().String(), layerCRDs)
252251
return installManifest(manifest, installSpecByte, validCRDsImageSpec, remote)
253252
}
254253
}

controllers/oci_registry_test.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package controllers_test
2+
3+
import (
4+
"os"
5+
6+
"github.com/kyma-project/module-manager/internal/pkg/prepare"
7+
. "github.com/onsi/ginkgo/v2"
8+
. "github.com/onsi/gomega"
9+
corev1 "k8s.io/api/core/v1"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"sigs.k8s.io/controller-runtime/pkg/client"
12+
"sigs.k8s.io/yaml"
13+
)
14+
15+
var _ = Describe("test authnKeyChain", func() {
16+
It("should fetch authnKeyChain from secret correctly", func() {
17+
By("install secret")
18+
installCredSecret()
19+
const repo = "test.registry.io"
20+
imageSpecWithCredSelect := createOCIImageSpecWithCredSelect("imageName",
21+
repo, "digest",
22+
credSecretLabel())
23+
keychain, err := prepare.GetAuthnKeychain(ctx, imageSpecWithCredSelect, k8sClient, metav1.NamespaceDefault)
24+
Expect(err).ToNot(HaveOccurred())
25+
dig := &TestRegistry{target: repo, registry: repo}
26+
authenticator, err := keychain.Resolve(dig)
27+
Expect(err).ToNot(HaveOccurred())
28+
authConfig, err := authenticator.Authorization()
29+
Expect(err).ToNot(HaveOccurred())
30+
Expect(authConfig.Username).To(Equal("test_user"))
31+
Expect(authConfig.Password).To(Equal("test_pass"))
32+
})
33+
})
34+
35+
type TestRegistry struct {
36+
target string
37+
registry string
38+
}
39+
40+
func (d TestRegistry) String() string {
41+
return d.target
42+
}
43+
44+
func (d TestRegistry) RegistryStr() string {
45+
return d.registry
46+
}
47+
48+
func installCredSecret() {
49+
secret := &corev1.Secret{}
50+
secretFile, err := os.ReadFile("../pkg/test_samples/auth_secret.yaml")
51+
Expect(err).ToNot(HaveOccurred())
52+
err = yaml.Unmarshal(secretFile, secret)
53+
Expect(err).ToNot(HaveOccurred())
54+
err = k8sClient.Create(ctx, secret)
55+
Expect(err).ToNot(HaveOccurred())
56+
err = k8sClient.Get(ctx, client.ObjectKeyFromObject(secret), &corev1.Secret{})
57+
Eventually(err, standardTimeout, standardInterval).Should(Succeed())
58+
}
59+
60+
func credSecretLabel() metav1.LabelSelector {
61+
return metav1.LabelSelector{
62+
MatchLabels: map[string]string{"operator.kyma-project.io/oci-registry-cred": "test-operator"},
63+
}
64+
}

0 commit comments

Comments
 (0)