in internal/controller/acrpullbinding_v1beta2_controller.go [51:177]
func NewV1beta2Reconciler(opts *V1beta2ReconcilerOpts) *PullBindingReconciler {
if opts.now == nil {
opts.now = time.Now
}
if opts.fetchArmToken == nil {
opts.fetchArmToken = authorizer.ARMTokenForBinding
}
if opts.exchangeArmTokenForAcrToken == nil {
opts.exchangeArmTokenForAcrToken = authorizer.ExchangeACRAccessTokenForSpec
}
if opts.mintToken == nil {
opts.mintToken = func(ctx context.Context, serviceAccountNamespace, serviceAccountName string) (*authenticationv1.TokenRequest, error) {
return opts.ServiceAccountClient.ServiceAccounts(serviceAccountNamespace).CreateToken(ctx, serviceAccountName, &authenticationv1.TokenRequest{
Spec: authenticationv1.TokenRequestSpec{
Audiences: []string{opts.ServiceAccountTokenAudience},
},
}, metav1.CreateOptions{})
}
}
return &PullBindingReconciler{
genericReconciler: &genericReconciler[*msiacrpullv1beta2.AcrPullBinding]{
Client: opts.Client,
Logger: opts.Logger,
Scheme: opts.Scheme,
NewBinding: func() *msiacrpullv1beta2.AcrPullBinding {
return &msiacrpullv1beta2.AcrPullBinding{}
},
AddFinalizer: func(binding *msiacrpullv1beta2.AcrPullBinding, finalizer string) *msiacrpullv1beta2.AcrPullBinding {
updated := binding.DeepCopy()
updated.ObjectMeta.Finalizers = append(updated.ObjectMeta.Finalizers, finalizer)
return updated
},
RemoveFinalizer: func(binding *msiacrpullv1beta2.AcrPullBinding, finalizer string) *msiacrpullv1beta2.AcrPullBinding {
updated := binding.DeepCopy()
updated.ObjectMeta.Finalizers = slices.DeleteFunc(updated.ObjectMeta.Finalizers, func(s string) bool {
return s == finalizer
})
return updated
},
GetServiceAccountName: func(binding *msiacrpullv1beta2.AcrPullBinding) string {
return binding.Spec.ServiceAccountName
},
GetPullSecretName: func(binding *msiacrpullv1beta2.AcrPullBinding) string {
return pullSecretName(binding.ObjectMeta.Name)
},
GetInputsHash: func(binding *msiacrpullv1beta2.AcrPullBinding) string {
return inputsHash(binding.Spec)
},
CreatePullCredential: func(ctx context.Context, binding *msiacrpullv1beta2.AcrPullBinding, serviceAccount *corev1.ServiceAccount) (string, time.Time, error) {
var tenantId, clientId, token string
if binding.Spec.Auth.WorkloadIdentity != nil {
if binding.Spec.Auth.WorkloadIdentity.TenantID != "" {
tenantId = binding.Spec.Auth.WorkloadIdentity.TenantID
clientId = binding.Spec.Auth.WorkloadIdentity.ClientID
} else {
for _, annotation := range []struct { // n.b. we need an array here to be able to test for the error output
value string
into *string
}{
{value: azworkloadidentity.ClientIDAnnotation, into: &clientId},
{value: azworkloadidentity.TenantIDAnnotation, into: &tenantId},
} {
value, set := serviceAccount.Annotations[annotation.value]
if !set {
return "", time.Time{}, fmt.Errorf("service account %s missing %s annotation", serviceAccount.Name, annotation.value)
}
*annotation.into = value
}
}
response, err := opts.mintToken(ctx, serviceAccount.Namespace, serviceAccount.Name)
if err != nil {
return "", time.Time{}, fmt.Errorf("failed to mint service account token: %w", err)
}
token = response.Status.Token
}
armToken, err := opts.fetchArmToken(ctx, binding.Spec, tenantId, clientId, token)
if err != nil {
return "", time.Time{}, fmt.Errorf("failed to retrieve ARM token: %v", err)
}
acrToken, err := opts.exchangeArmTokenForAcrToken(ctx, armToken, binding.Spec.ACR)
if err != nil {
return "", time.Time{}, fmt.Errorf("failed to retrieve ACR token: %v", err)
}
dockerConfig, err := authorizer.CreateACRDockerCfg(binding.Spec.ACR.Server, acrToken)
if err != nil {
return "", time.Time{}, fmt.Errorf("failed to write ACR dockercfg: %v", err)
}
return dockerConfig, acrToken.ExpiresOn, nil
},
UpdateStatusError: func(binding *msiacrpullv1beta2.AcrPullBinding, s string) *msiacrpullv1beta2.AcrPullBinding {
updated := binding.DeepCopy()
updated.Status.Error = s
return updated
},
NeedsRefresh: func(logger logr.Logger, pullSecret *corev1.Secret, now func() time.Time) bool {
return needsRefresh(now, pullSecretRefresh(logger, pullSecret), pullSecretExpiry(logger, pullSecret), opts.TTLRotationFraction)
},
RequeueAfter: func(now func() time.Time) func(binding *msiacrpullv1beta2.AcrPullBinding) time.Duration {
return func(binding *msiacrpullv1beta2.AcrPullBinding) time.Duration {
var requeueAfter time.Duration
if binding.Status.TokenExpirationTime != nil && binding.Status.LastTokenRefreshTime != nil {
refresh, expiry := binding.Status.LastTokenRefreshTime.Time, binding.Status.TokenExpirationTime.Time
requeueAfter = refreshBoundary(refresh, expiry, opts.TTLRotationFraction).Sub(now())
}
return requeueAfter
}
},
NeedsStatusUpdate: func(refresh time.Time, expiry time.Time, binding *msiacrpullv1beta2.AcrPullBinding) bool {
return binding.Status.Error != "" || binding.Status.TokenExpirationTime == nil || !binding.Status.TokenExpirationTime.Equal(&metav1.Time{Time: expiry}) ||
binding.Status.LastTokenRefreshTime == nil || !binding.Status.LastTokenRefreshTime.Equal(&metav1.Time{Time: refresh})
},
UpdateStatus: func(refresh time.Time, expiry time.Time, binding *msiacrpullv1beta2.AcrPullBinding) *msiacrpullv1beta2.AcrPullBinding {
updated := binding.DeepCopy()
updated.Status.TokenExpirationTime = &metav1.Time{Time: expiry}
updated.Status.LastTokenRefreshTime = &metav1.Time{Time: refresh}
updated.Status.Error = ""
return updated
},
now: opts.now,
},
}
}