Skip to content

Commit 3cb4ac1

Browse files
committed
fixup! revert: Keep old image credentials handler
Use for existing CCs.
1 parent ab9a186 commit 3cb4ac1

15 files changed

+2388
-2
lines changed

pkg/handlers/deleteinv0280/generic/mutation/handlers.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation"
1010
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/aws/mutation/cni/calico"
11+
deleteinv0280credentials "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/deleteinv0280/generic/mutation/imageregistries/credentials"
1112
deleteinv0280mirrors "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/deleteinv0280/generic/mutation/mirrors"
1213
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/auditpolicy"
1314
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/autorenewcerts"
@@ -19,7 +20,6 @@ import (
1920
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/etcd"
2021
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/extraapiservercertsans"
2122
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/httpproxy"
22-
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/imageregistries/credentials"
2323
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/kubernetesimagerepository"
2424
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/users"
2525
)
@@ -33,7 +33,7 @@ func MetaMutators(mgr manager.Manager) []mutation.MetaMutator {
3333
extraapiservercertsans.NewPatch(),
3434
httpproxy.NewPatch(mgr.GetClient()),
3535
kubernetesimagerepository.NewPatch(),
36-
credentials.NewPatch(mgr.GetClient()),
36+
deleteinv0280credentials.NewPatch(mgr.GetClient()),
3737
deleteinv0280mirrors.NewPatch(mgr.GetClient()),
3838
calico.NewPatch(),
3939
users.NewPatch(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
// Copyright 2023 Nutanix. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package credentials
5+
6+
import (
7+
"bytes"
8+
_ "embed"
9+
"fmt"
10+
"net/url"
11+
"path"
12+
"text/template"
13+
14+
corev1 "k8s.io/api/core/v1"
15+
credentialproviderv1 "k8s.io/kubelet/pkg/apis/credentialprovider/v1"
16+
cabpkv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
17+
18+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/imageregistries/credentials/credentialprovider"
19+
)
20+
21+
const (
22+
//nolint:gosec // Does not contain hard coded credentials.
23+
kubeletStaticCredentialProviderCredentialsOnRemote = "/etc/kubernetes/static-image-credentials.json"
24+
25+
//nolint:gosec // Does not contain hard coded credentials.
26+
kubeletImageCredentialProviderConfigOnRemote = "/etc/kubernetes/image-credential-provider-config.yaml"
27+
28+
//nolint:gosec // Does not contain hard coded credentials.
29+
kubeletDynamicCredentialProviderConfigOnRemote = "/etc/kubernetes/dynamic-credential-provider-config.yaml"
30+
31+
azureCloudConfigFilePath = "/etc/kubernetes/azure.json"
32+
33+
secretKeyForCACert = "ca.crt"
34+
)
35+
36+
var (
37+
//go:embed templates/dynamic-credential-provider-config.yaml.gotmpl
38+
dynamicCredentialProviderConfigPatch []byte
39+
40+
dynamicCredentialProviderConfigPatchTemplate = template.Must(
41+
template.New("").Parse(string(dynamicCredentialProviderConfigPatch)),
42+
)
43+
44+
//go:embed templates/kubelet-image-credential-provider-config.yaml.gotmpl
45+
kubeletImageCredentialProviderConfigPatch []byte
46+
47+
kubeletImageCredentialProviderConfigPatchTemplate = template.Must(
48+
template.New("").Parse(string(kubeletImageCredentialProviderConfigPatch)),
49+
)
50+
)
51+
52+
type providerConfig struct {
53+
URL string
54+
Username string
55+
Password string
56+
HasCACert bool
57+
Mirror bool
58+
}
59+
60+
func (c providerConfig) isCredentialsEmpty() bool {
61+
return c.Username == "" &&
62+
c.Password == ""
63+
}
64+
65+
func (c providerConfig) requiresStaticCredentials() (bool, error) {
66+
registryHostWithPath, err := c.registryHostWithPath()
67+
if err != nil {
68+
return false, fmt.Errorf(
69+
"failed to get registry host with path: %w",
70+
err,
71+
)
72+
}
73+
74+
knownRegistryProvider, err := credentialprovider.URLMatchesKnownRegistryProvider(
75+
registryHostWithPath,
76+
)
77+
if err != nil {
78+
return false, fmt.Errorf(
79+
"failed to check if registry matches a known registry provider: %w",
80+
err,
81+
)
82+
}
83+
84+
// require static credentials if the registry provider is not known
85+
return !knownRegistryProvider, nil
86+
}
87+
88+
func (c providerConfig) registryHostWithPath() (string, error) {
89+
registryURL, err := url.ParseRequestURI(c.URL)
90+
if err != nil {
91+
return "", fmt.Errorf("failed parsing registry URL: %w", err)
92+
}
93+
94+
registryHostWithPath := registryURL.Host
95+
if registryURL.Path != "" {
96+
registryHostWithPath = path.Join(registryURL.Host, registryURL.Path)
97+
}
98+
99+
return registryHostWithPath, nil
100+
}
101+
102+
func templateFilesForImageCredentialProviderConfigs(
103+
configs []providerConfig,
104+
) ([]cabpkv1.File, error) {
105+
var files []cabpkv1.File
106+
107+
kubeletCredentialProviderConfigFile, err := templateKubeletCredentialProviderConfig(configs)
108+
if err != nil {
109+
return nil, err
110+
}
111+
if kubeletCredentialProviderConfigFile != nil {
112+
files = append(files, *kubeletCredentialProviderConfigFile)
113+
}
114+
115+
kubeletDynamicCredentialProviderConfigFile, err := templateDynamicCredentialProviderConfig(
116+
configs,
117+
)
118+
if err != nil {
119+
return nil, err
120+
}
121+
if kubeletDynamicCredentialProviderConfigFile != nil {
122+
files = append(files, *kubeletDynamicCredentialProviderConfigFile)
123+
}
124+
125+
return files, nil
126+
}
127+
128+
func templateKubeletCredentialProviderConfig(
129+
configs []providerConfig,
130+
) (*cabpkv1.File, error) {
131+
providerBinary, providerArgs, providerAPIVersion := kubeletCredentialProvider()
132+
133+
// In addition to the globs already defined in the template, also include the user provided registries.
134+
//
135+
// This is needed to match registries with a port and/or a URL path.
136+
// From https://kubernetes.io/docs/tasks/administer-cluster/kubelet-credential-provider/#configure-image-matching
137+
registryHosts := make([]string, 0, len(configs))
138+
for _, config := range configs {
139+
registryHostWithPath, err := config.registryHostWithPath()
140+
if err != nil {
141+
return nil, err
142+
}
143+
registryHosts = append(registryHosts, registryHostWithPath)
144+
}
145+
146+
templateInput := struct {
147+
RegistryHosts []string
148+
ProviderBinary string
149+
ProviderArgs []string
150+
ProviderAPIVersion string
151+
}{
152+
RegistryHosts: registryHosts,
153+
ProviderBinary: providerBinary,
154+
ProviderArgs: providerArgs,
155+
ProviderAPIVersion: providerAPIVersion,
156+
}
157+
158+
return fileFromTemplate(
159+
kubeletImageCredentialProviderConfigPatchTemplate,
160+
templateInput,
161+
kubeletImageCredentialProviderConfigOnRemote,
162+
)
163+
}
164+
165+
func templateDynamicCredentialProviderConfig(
166+
configs []providerConfig,
167+
) (*cabpkv1.File, error) {
168+
type templateInput struct {
169+
RegistryHost string
170+
ProviderBinary string
171+
ProviderArgs []string
172+
ProviderAPIVersion string
173+
Mirror bool
174+
}
175+
176+
inputs := make([]templateInput, 0, len(configs))
177+
178+
for _, config := range configs {
179+
registryHostWithPath, err := config.registryHostWithPath()
180+
if err != nil {
181+
return nil, err
182+
}
183+
184+
providerBinary, providerArgs, providerAPIVersion, err := dynamicCredentialProvider(
185+
registryHostWithPath,
186+
)
187+
if err != nil {
188+
return nil, err
189+
}
190+
191+
inputs = append(inputs, templateInput{
192+
RegistryHost: registryHostWithPath,
193+
ProviderBinary: providerBinary,
194+
ProviderArgs: providerArgs,
195+
ProviderAPIVersion: providerAPIVersion,
196+
Mirror: config.Mirror,
197+
})
198+
}
199+
200+
return fileFromTemplate(
201+
dynamicCredentialProviderConfigPatchTemplate,
202+
inputs,
203+
kubeletDynamicCredentialProviderConfigOnRemote,
204+
)
205+
}
206+
207+
func kubeletCredentialProvider() (providerBinary string, providerArgs []string, providerAPIVersion string) {
208+
return "dynamic-credential-provider",
209+
[]string{"get-credentials", "-c", kubeletDynamicCredentialProviderConfigOnRemote},
210+
credentialproviderv1.SchemeGroupVersion.String()
211+
}
212+
213+
func dynamicCredentialProvider(host string) (
214+
providerBinary string, providerArgs []string, providerAPIVersion string, err error,
215+
) {
216+
if matches, err := credentialprovider.URLMatchesECR(host); matches || err != nil {
217+
return "ecr-credential-provider", []string{"get-credentials"},
218+
credentialproviderv1.SchemeGroupVersion.String(), err
219+
}
220+
221+
if matches, err := credentialprovider.URLMatchesGCR(host); matches || err != nil {
222+
return "gcr-credential-provider", []string{"get-credentials"},
223+
credentialproviderv1.SchemeGroupVersion.String(), err
224+
}
225+
226+
if matches, err := credentialprovider.URLMatchesACR(host); matches || err != nil {
227+
return "acr-credential-provider", []string{
228+
azureCloudConfigFilePath,
229+
}, credentialproviderv1.SchemeGroupVersion.String(), err
230+
}
231+
232+
// if no supported provider was found, assume we are using the static credential provider
233+
return "static-credential-provider",
234+
[]string{kubeletStaticCredentialProviderCredentialsOnRemote},
235+
credentialproviderv1.SchemeGroupVersion.String(),
236+
nil
237+
}
238+
239+
func fileFromTemplate(
240+
t *template.Template,
241+
templateInput any,
242+
fPath string,
243+
) (*cabpkv1.File, error) {
244+
var b bytes.Buffer
245+
err := t.Execute(&b, templateInput)
246+
if err != nil {
247+
return nil, fmt.Errorf("failed executing template: %w", err)
248+
}
249+
250+
return &cabpkv1.File{
251+
Path: fPath,
252+
Content: b.String(),
253+
Permissions: "0600",
254+
}, nil
255+
}
256+
257+
func secretHasCACert(secret *corev1.Secret) bool {
258+
if secret == nil {
259+
return false
260+
}
261+
262+
_, ok := secret.Data[secretKeyForCACert]
263+
return ok
264+
}

0 commit comments

Comments
 (0)