-
Notifications
You must be signed in to change notification settings - Fork 0
test api review #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test api review #41
Conversation
@@ -70,7 +72,7 @@ func decode(data []byte) (*kubeletconfig.CredentialProviderConfig, error) { | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: It would be helpful to add a comment explaining what saTokenForCredentialProviders
represents.
|
||
if provider.TokenAttributes != nil { | ||
fldPath := fieldPath.Child("tokenAttributes") | ||
if !saTokenForCredentialProviders { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Consider validating the length/format of ServiceAccountTokenAudience
to prevent excessively long or invalid values. Use validation.IsDNS1123Subdomain
or similar validation functions from k8s.io/apimachinery/pkg/util/validation
if appropriate.
@@ -125,6 +127,35 @@ func validateCredentialProviderConfig(config *kubeletconfig.CredentialProviderCo | |||
if provider.DefaultCacheDuration != nil && provider.DefaultCacheDuration.Duration < 0 { | |||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("defaultCacheDuration"), provider.DefaultCacheDuration.Duration, "defaultCacheDuration must be greater than or equal to 0")) | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider changing len(provider.TokenAttributes.ServiceAccountTokenAudience) == 0
to len(strings.TrimSpace(provider.TokenAttributes.ServiceAccountTokenAudience)) == 0
to prevent the usage of empty strings consisting of spaces.
if !saTokenForCredentialProviders { | ||
allErrs = append(allErrs, field.Forbidden(fldPath, "tokenAttributes is not supported when KubeletServiceAccountTokenForCredentialProviders feature gate is disabled")) | ||
} | ||
if len(provider.TokenAttributes.ServiceAccountTokenAudience) == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: RequireServiceAccount
is a pointer, is it intentional to mark this required? Should the intention actually be distinguishing unset from false
? If so, it shouldn't be required. Consider adding a comment clarifying this.
if !saTokenForCredentialProviders { | ||
allErrs = append(allErrs, field.Forbidden(fldPath, "tokenAttributes is not supported when KubeletServiceAccountTokenForCredentialProviders feature gate is disabled")) | ||
} | ||
if len(provider.TokenAttributes.ServiceAccountTokenAudience) == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message tokenAttributes is only supported for %s API version
would be better worded to indicate what versions are not supported.
} | ||
if provider.TokenAttributes.RequireServiceAccount == nil { | ||
allErrs = append(allErrs, field.Required(fldPath.Child("requireServiceAccount"), "requireServiceAccount is required")) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: It may be worthwhile to extract the string representation of credentialproviderv1.SchemeGroupVersion
into a constant for reuse and clarity. This would also make it easier to update if the API version changes.
allErrs = append(allErrs, field.Required(fldPath.Child("requireServiceAccount"), "requireServiceAccount is required")) | ||
} | ||
if provider.APIVersion != credentialproviderv1.SchemeGroupVersion.String() { | ||
allErrs = append(allErrs, field.Forbidden(fldPath, fmt.Sprintf("tokenAttributes is only supported for %s API version", credentialproviderv1.SchemeGroupVersion.String()))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to use apimachinery/pkg/util/validation/validation.IsQualifiedName
directly to validate the annotation key rather than converting it to lowercase first and then calling IsQualifiedName
. The function is case-insensitive, so explicitly converting to lowercase is unnecessary and reduces readability.
} | ||
|
||
seenAnnotationKeys := sets.NewString() | ||
// Using the validation logic for keys from https://github.com/kubernetes/kubernetes/blob/69dbc74417304328a9fd3c161643dc4f0a057f41/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go#L46-L51 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Consider moving the key validation logic to a separate function for better readability and testability.
seenAnnotationKeys := sets.NewString() | ||
// Using the validation logic for keys from https://github.com/kubernetes/kubernetes/blob/69dbc74417304328a9fd3c161643dc4f0a057f41/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go#L46-L51 | ||
for _, k := range provider.TokenAttributes.ServiceAccountAnnotationKeys { | ||
// The rule is QualifiedName except that case doesn't matter, so convert to lowercase before checking. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: The comment refers to a specific commit hash in objectmeta.go
. Consider updating this comment to refer to a stable location, such as a specific function or a more general description of the validation logic. Also, verify that the linked validation is still functionally the same, and update it if it is not.
for _, k := range provider.TokenAttributes.ServiceAccountAnnotationKeys { | ||
// The rule is QualifiedName except that case doesn't matter, so convert to lowercase before checking. | ||
for _, msg := range validation.IsQualifiedName(strings.ToLower(k)) { | ||
allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceAccountAnnotationKeys"), k, msg)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Rather than converting the annotation key to lowercase, consider using validation.IsQualifiedName
directly, which is case-insensitive. This removes the potential for unintended behavior with mixed-case keys.
allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceAccountAnnotationKeys"), k, msg)) | ||
} | ||
if seenAnnotationKeys.Has(k) { | ||
allErrs = append(allErrs, field.Duplicate(fldPath.Child("serviceAccountAnnotationKeys"), k)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Add a unit test specifically for the annotation key validation to ensure it correctly handles valid and invalid keys.
func RegisterCredentialProviderPlugins(pluginConfigFile, pluginBinDir string, | ||
getServiceAccountToken func(namespace, name string, tr *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error), | ||
getServiceAccount func(namespace, name string) (*v1.ServiceAccount, error), | ||
) error { | ||
if _, err := os.Stat(pluginBinDir); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: It may be useful to emit an event, log a warning, or return a non-nil error from the RegisterCredentialProviderPlugins
function when the feature gate is disabled. This would provide better visibility into the configuration state and help users understand why service account tokens are not being used.
if !featureGateEnabled || !serviceAccountTokenAudienceSet { | ||
return nil | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Add a comment explaining why the default value of expirationSeconds
in the TokenRequestSpec
is acceptable.
@@ -28,12 +28,18 @@ import ( | |||
"sync" | |||
"time" | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Add a comment describing the purpose of importing golang.org/x/crypto/cryptobyte
.
audience: provider.TokenAttributes.ServiceAccountTokenAudience, | ||
requireServiceAccount: *provider.TokenAttributes.RequireServiceAccount, | ||
getServiceAccountFunc: getServiceAccount, | ||
getServiceAccountTokenFunc: getServiceAccountToken, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: APIVersion
and Kind
should be defined as constants to avoid magic strings.
@@ -75,7 +82,10 @@ func init() { | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Instead of passing saTokenForCredentialProvidersFeatureEnabled
as an argument, directly invoke utilfeature.DefaultFeatureGate.Enabled
within the validateCredentialProviderConfig
function. This simplifies the function signature and reduces the chance of passing an incorrect value.
|
||
if p.serviceAccountProvider != nil { | ||
if len(serviceAccountName) == 0 && p.serviceAccountProvider.requireServiceAccount { | ||
klog.Errorf("Service account name is empty for pod %s/%s", podNamespace, podName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: It might be beneficial to add a comment explaining the purpose of using both image
and registry
as cache keys. Providing context on why two keys are utilized for caching would enhance comprehension.
@@ -89,8 +99,8 @@ func RegisterCredentialProviderPlugins(pluginConfigFile, pluginBinDir string) er | |||
return err | |||
} | |||
|
|||
errs := validateCredentialProviderConfig(credentialProviderConfig) | |||
if len(errs) > 0 { | |||
saTokenForCredentialProvidersFeatureEnabled := utilfeature.DefaultFeatureGate.Enabled(features.KubeletServiceAccountTokenForCredentialProviders) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: The getServiceAccountToken and getServiceAccount functions should not be passed as parameters to newPluginProvider.
return credentialprovider.DockerConfig{} | ||
} | ||
} | ||
res, err, _ := p.group.Do(singleFlightKey, func() (interface{}, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Consider changing the input parameter to the ExecPlugin interface to a struct type instead of having multiple separate parameters.
@@ -89,8 +99,8 @@ func RegisterCredentialProviderPlugins(pluginConfigFile, pluginBinDir string) er | |||
return err | |||
} | |||
|
|||
errs := validateCredentialProviderConfig(credentialProviderConfig) | |||
if len(errs) > 0 { | |||
saTokenForCredentialProvidersFeatureEnabled := utilfeature.DefaultFeatureGate.Enabled(features.KubeletServiceAccountTokenForCredentialProviders) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Consider using dependency injection for getServiceAccountToken and getServiceAccount to improve testability and reduce dependencies. This could involve creating an interface for fetching service account tokens and having different implementations for testing and production.
} | ||
} | ||
res, err, _ := p.group.Do(singleFlightKey, func() (interface{}, error) { | ||
return p.plugin.ExecPlugin(context.Background(), image, serviceAccountToken, requiredAnnotations) | ||
}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: It would be beneficial to add more details in the comment regarding the purpose and usage of serviceAccountAnnotations in the ExecPlugin
function.
@@ -150,6 +162,7 @@ func newPluginProvider(pluginBinDir string, provider kubeletconfig.CredentialPro | |||
envVars: provider.Env, | |||
environ: os.Environ, | |||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: The type for serviceAccountAnnotationKeys
should be []string
type instead of the string
type.
return nil, false, fmt.Errorf("error generating cache key: %w", err) | ||
} | ||
|
||
obj, found, err = p.cache.GetByKey(cacheKey) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: It might be worthwhile to add a comment explaining the rationale behind using cryptobyte
for generating the cache key. Consider using a standard hashing library.
// getServiceAccountData returns the service account UID and required annotations for the service account. | ||
// If the service account does not exist, an error is returned. | ||
// requiredAnnotations is a map of annotation keys and values that the plugin requires to generate credentials | ||
// that's defined in the tokenAttributes.serviceAccountAnnotationKeys field in the credential provider config. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Consider passing the pod information perPodPluginProvider
during plugin initialization, rather than on every Provide
call. This could reduce overhead if the pod information doesn't change frequently.
@@ -340,7 +514,12 @@ func (p *pluginProvider) getCachedCredentials(image string) (credentialprovider. | |||
return obj.(*cacheEntry).credentials, true, nil | |||
} | |||
|
|||
obj, found, err = p.cache.GetByKey(globalCacheKey) | |||
cacheKey, err = p.generateCacheKey(globalCacheKey, baseCacheKey) | |||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: The magic number 16
should be extracted to a named constant.
// that's defined in the tokenAttributes.serviceAccountAnnotationKeys field in the credential provider config. | ||
func (s *serviceAccountProvider) getServiceAccountData(namespace, name string) (types.UID, map[string]string, error) { | ||
sa, err := s.getServiceAccountFunc(namespace, name) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: It is required to check feature-gate before calling function p.provider.provide
to avoid the case that new introduced functions get invoked by the old version api-server without enabling the feature-gate.
if err != nil { | ||
return nil, false, fmt.Errorf("error generating cache key: %w", err) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: The magic number 32
should be extracted to a named constant.
@@ -355,7 +534,7 @@ func (p *pluginProvider) getCachedCredentials(image string) (credentialprovider. | |||
// Plugin is the interface calling ExecPlugin. This is mainly for testability | |||
// so tests don't have to actually exec any processes. | |||
type Plugin interface { | |||
ExecPlugin(ctx context.Context, image string) (*credentialproviderapi.CredentialProviderResponse, error) | |||
ExecPlugin(ctx context.Context, image, serviceAccountToken string, serviceAccountAnnotations map[string]string) (*credentialproviderapi.CredentialProviderResponse, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Add a comment explaining why the StringKeySet
is sorted.
return credentialprovider.DockerConfig{} | ||
} | ||
} | ||
res, err, _ := p.group.Do(singleFlightKey, func() (interface{}, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Suggest adding a comment explaining that ServiceAccountAnnotations
are passed to the plugin via stdin.
@@ -370,7 +368,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate | |||
|
|||
genericfeatures.WatchFromStorageWithoutResourceVersion: { | |||
{Version: version.MustParse("1.27"), Default: false, PreRelease: featuregate.Beta}, | |||
{Version: version.MustParse("1.33"), Default: false, PreRelease: featuregate.Deprecated, LockToDefault: true}, | |||
}, | |||
|
|||
genericfeatures.WatchList: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to line 272, consider adding a comment explaining the deprecation of WatchFromStorageWithoutResourceVersion
and why it is deprecated in 1.33.
KubeletServiceAccountTokenForCredentialProviders: { | ||
{Version: version.MustParse("1.33"), Default: false, PreRelease: featuregate.Alpha}, | ||
}, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is good that KubeletServiceAccountTokenForCredentialProviders
is introduced as an alpha feature gated on 1.33. Please also ensure you've added a // +featureGate=KubeletServiceAccountTokenForCredentialProviders
to the corresponding field in the API definition. Also add the KEP link here for reviewers.
@@ -308,7 +308,7 @@ func (f *FakeRuntime) GetContainerLogs(_ context.Context, pod *v1.Pod, container | |||
return f.Err | |||
} | |||
|
|||
func (f *FakeRuntime) PullImage(ctx context.Context, image kubecontainer.ImageSpec, pullSecrets []v1.Secret, podSandboxConfig *runtimeapi.PodSandboxConfig) (string, error) { | |||
func (f *FakeRuntime) PullImage(ctx context.Context, image kubecontainer.ImageSpec, pullSecrets []v1.Secret, podSandboxConfig *runtimeapi.PodSandboxConfig, serviceAccountName string) (string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: The serviceAccountName
parameter added to the PullImage
function should be documented with a comment explaining its purpose and how it's used (or not used) within the FakeRuntime. This will improve readability and maintainability.
@@ -678,12 +679,10 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate | |||
|
|||
StorageNamespaceIndex: { | |||
{Version: version.MustParse("1.30"), Default: true, PreRelease: featuregate.Beta}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to line 272, consider adding a comment explaining the deprecation of StorageNamespaceIndex
and why it is deprecated in 1.33.
}, | ||
|
||
ServiceAccountNodeAudienceRestriction: { | ||
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Beta}, | ||
{Version: version.MustParse("1.33"), Default: true, PreRelease: featuregate.Beta}, | ||
{Version: version.MustParse("1.32"), Default: true, PreRelease: featuregate.Beta}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like the feature ServiceAccountNodeAudienceRestriction
is already in Beta in 1.32 and enabled by default, so the entry for 1.33 seems redundant. Consider removing it or adding a comment explaining why it's there.
// PullImage provides a mock function with given fields: ctx, image, pullSecrets, podSandboxConfig | ||
func (_m *MockRuntime) PullImage(ctx context.Context, image container.ImageSpec, pullSecrets []corev1.Secret, podSandboxConfig *v1.PodSandboxConfig) (string, error) { | ||
ret := _m.Called(ctx, image, pullSecrets, podSandboxConfig) | ||
// PullImage provides a mock function with given fields: ctx, image, pullSecrets, podSandboxConfig, serviceAccountName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: The comment for the PullImage
method should be updated to reflect the purpose of the new serviceAccountName
parameter. Also, consider including the valid format for the parameter.
@@ -718,7 +717,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate | |||
SidecarContainers: { | |||
{Version: version.MustParse("1.28"), Default: false, PreRelease: featuregate.Alpha}, | |||
{Version: version.MustParse("1.29"), Default: true, PreRelease: featuregate.Beta}, | |||
{Version: version.MustParse("1.33"), Default: true, LockToDefault: true, PreRelease: featuregate.GA}, // GA in 1.33 remove in 1.36 | |||
}, | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is good that you're marking SidecarContainers
as GA in 1.33, but please ensure you remove the feature gate altogether in 1.36. Also, consider providing a KEP link.
6683b3e
to
0dcfd19
Compare
@@ -70,7 +72,7 @@ func decode(data []byte) (*kubeletconfig.CredentialProviderConfig, error) { | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to pass *field.ErrorList
instead of field.ErrorList
to avoid unnecessary copying of the list.
@@ -125,6 +127,35 @@ func validateCredentialProviderConfig(config *kubeletconfig.CredentialProviderCo | |||
if provider.DefaultCacheDuration != nil && provider.DefaultCacheDuration.Duration < 0 { | |||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("defaultCacheDuration"), provider.DefaultCacheDuration.Duration, "defaultCacheDuration must be greater than or equal to 0")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could include the error message: "The TokenAttributes field is only supported when the KubeletServiceAccountToken feature gate is enabled."
@@ -125,6 +127,35 @@ func validateCredentialProviderConfig(config *kubeletconfig.CredentialProviderCo | |||
if provider.DefaultCacheDuration != nil && provider.DefaultCacheDuration.Duration < 0 { | |||
allErrs = append(allErrs, field.Invalid(fieldPath.Child("defaultCacheDuration"), provider.DefaultCacheDuration.Duration, "defaultCacheDuration must be greater than or equal to 0")) | |||
} | |||
|
|||
if provider.TokenAttributes != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ServiceAccountTokenAudience
is Required
, suggest to include that this ServiceAccountTokenAudience
is a list and add length validation for that list. For example:
if provider.TokenAttributes != nil { | ||
fldPath := fieldPath.Child("tokenAttributes") | ||
if !saTokenForCredentialProviders { | ||
allErrs = append(allErrs, field.Forbidden(fldPath, "tokenAttributes is not supported when KubeletServiceAccountTokenForCredentialProviders feature gate is disabled")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RequireServiceAccount
is Required
, suggest to include that this RequireServiceAccount
is a bool value, and must not be nil.
allErrs = append(allErrs, field.Forbidden(fldPath, "tokenAttributes is not supported when KubeletServiceAccountTokenForCredentialProviders feature gate is disabled")) | ||
} | ||
if len(provider.TokenAttributes.ServiceAccountTokenAudience) == 0 { | ||
allErrs = append(allErrs, field.Required(fldPath.Child("serviceAccountTokenAudience"), "serviceAccountTokenAudience is required")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check should compare the API group name and API version, using constants. This makes the code more maintainable.
allErrs = append(allErrs, field.Required(fldPath.Child("serviceAccountTokenAudience"), "serviceAccountTokenAudience is required")) | ||
} | ||
if provider.TokenAttributes.RequireServiceAccount == nil { | ||
allErrs = append(allErrs, field.Required(fldPath.Child("requireServiceAccount"), "requireServiceAccount is required")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is good practice to include a comment that refers to the Kubernetes validation logic, to provide context as to why it was included in the code, as well as a way to more easily locate the logic being referred to.
if provider.TokenAttributes.RequireServiceAccount == nil { | ||
allErrs = append(allErrs, field.Required(fldPath.Child("requireServiceAccount"), "requireServiceAccount is required")) | ||
} | ||
if provider.APIVersion != credentialproviderv1.SchemeGroupVersion.String() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The strings.ToLower(k)
conversion could be done once and reused to avoid redundant work.
allErrs = append(allErrs, field.Required(fldPath.Child("requireServiceAccount"), "requireServiceAccount is required")) | ||
} | ||
if provider.APIVersion != credentialproviderv1.SchemeGroupVersion.String() { | ||
allErrs = append(allErrs, field.Forbidden(fldPath, fmt.Sprintf("tokenAttributes is only supported for %s API version", credentialproviderv1.SchemeGroupVersion.String()))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of inlining the loop, you could move it into a separate function, with an expressive function name.
if provider.APIVersion != credentialproviderv1.SchemeGroupVersion.String() { | ||
allErrs = append(allErrs, field.Forbidden(fldPath, fmt.Sprintf("tokenAttributes is only supported for %s API version", credentialproviderv1.SchemeGroupVersion.String()))) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider to add max length validation for serviceAccountAnnotationKeys
.
@@ -28,12 +28,18 @@ import ( | |||
"sync" | |||
"time" | |||
|
|||
"golang.org/x/crypto/cryptobyte" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding an explicit import alias for clock
to avoid potential naming conflicts. For example, kclock "k8s.io/utils/clock"
.
@@ -75,7 +82,10 @@ func init() { | |||
|
|||
// RegisterCredentialProviderPlugins is called from kubelet to register external credential provider | |||
// plugins according to the CredentialProviderConfig config file. | |||
func RegisterCredentialProviderPlugins(pluginConfigFile, pluginBinDir string) error { | |||
func RegisterCredentialProviderPlugins(pluginConfigFile, pluginBinDir string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to pass the saTokenForCredentialProvidersFeatureEnabled flag to newServiceAccountProvider rather than passing it to validateCredentialProviderConfig function.
func newPluginProvider(pluginBinDir string, provider kubeletconfig.CredentialProvider, | ||
getServiceAccountToken func(namespace, name string, tr *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error), | ||
getServiceAccount func(namespace, name string) (*v1.ServiceAccount, error), | ||
) (*pluginProvider, error) { | ||
mediaType := "application/json" | ||
info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType) | ||
if !ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to construct the serviceAccountProvider here and let the newPluginProvider function return error if needed.
@@ -150,6 +162,7 @@ func newPluginProvider(pluginBinDir string, provider kubeletconfig.CredentialPro | |||
envVars: provider.Env, | |||
environ: os.Environ, | |||
}, | |||
serviceAccountProvider: newServiceAccountProvider(provider, getServiceAccount, getServiceAccountToken), | |||
}, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add comments to describe the purpose and usage of each field in the serviceAccountProvider
struct.
} | ||
|
||
type serviceAccountProvider struct { | ||
audience string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add comments to describe the purpose and usage of each field in the perPodPluginProvider
struct.
|
||
// getServiceAccountToken returns a service account token for the service account. | ||
func (s *serviceAccountProvider) getServiceAccountToken(podName, podNamespace, serviceAccountName string, podUID types.UID) (string, error) { | ||
tr, err := s.getServiceAccountTokenFunc(podNamespace, serviceAccountName, &authenticationv1.TokenRequest{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The err variable is shadowed here. Use a different variable name for the error returned by p.generateCacheKey
.
// from cache or the exec plugin. | ||
func (p *pluginProvider) Provide(image string) credentialprovider.DockerConfig { | ||
func (p *pluginProvider) provide(image, podName, podNamespace string, podUID types.UID, serviceAccountName string) credentialprovider.DockerConfig { | ||
if !p.isImageAllowed(image) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment does not accurately describe the behavior of NewExpirationCache.
if err != nil { | ||
klog.Errorf("Error generating cache key: %v", err) | ||
return credentialprovider.DockerConfig{} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems odd to use uint32
for the length of annotations. Are you expecting more than 65535 annotations in total? Also, limiting the length of the entire cache key might be a good idea.
cacheKey, err = p.generateCacheKey(registry, baseCacheKey) | ||
if err != nil { | ||
return nil, false, fmt.Errorf("error generating cache key: %w", err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consider adding a unit test.
|
||
func registerCredentialProviderPlugin(name string, p *pluginProvider) { | ||
providersMutex.Lock() | ||
defer providersMutex.Unlock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using a more descriptive name than p
for the loop variable in for _, p := range providers
. A name like providerInfo
might improve readability.
providersMutex.RLock() | ||
defer providersMutex.RUnlock() | ||
|
||
keyring := &externalCredentialProviderKeyring{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be beneficial to include a comment explaining why a zero-length slice is being pre-allocated with make([]credentialprovider.DockerConfigProvider, 0, len(providers))
. Is it for performance reasons, or to avoid nil slices?
What type of PR is this?
What this PR does / why we need it:
Which issue(s) this PR fixes:
Fixes #
Special notes for your reviewer:
Does this PR introduce a user-facing change?
Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.: