ContainerImage.Pinniped/internal/controller/issuerconfig/issuerconfig.go
Matt Moyer ab750f48aa
When merging CredentialIssuer updates, don't overwrite LastUpdated.
If the only thing that has changed about a strategy is the LastUpdated timestamp, then we should not update the object.

Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-05-27 17:09:12 -05:00

88 lines
3.2 KiB
Go

// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package issuerconfig contains helpers for updating CredentialIssuer status entries.
package issuerconfig
import (
"context"
"fmt"
"sort"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
"go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
)
// Update a strategy on an existing CredentialIssuer, merging into any existing strategy entries.
func Update(ctx context.Context, client versioned.Interface, issuer *v1alpha1.CredentialIssuer, strategy v1alpha1.CredentialIssuerStrategy) error {
// Update the existing object to merge in the new strategy.
updated := issuer.DeepCopy()
mergeStrategy(&updated.Status, strategy)
// If the status has not changed, we're done.
if apiequality.Semantic.DeepEqual(issuer.Status, updated.Status) {
return nil
}
if _, err := client.ConfigV1alpha1().CredentialIssuers().UpdateStatus(ctx, updated, metav1.UpdateOptions{}); err != nil {
return fmt.Errorf("failed to update CredentialIssuer status: %w", err)
}
return nil
}
func mergeStrategy(configToUpdate *v1alpha1.CredentialIssuerStatus, strategy v1alpha1.CredentialIssuerStrategy) {
var existing *v1alpha1.CredentialIssuerStrategy
for i := range configToUpdate.Strategies {
if configToUpdate.Strategies[i].Type == strategy.Type {
existing = &configToUpdate.Strategies[i]
break
}
}
if existing != nil {
if !equalExceptLastUpdated(existing, &strategy) {
strategy.DeepCopyInto(existing)
}
} else {
configToUpdate.Strategies = append(configToUpdate.Strategies, strategy)
}
sort.Stable(sortableStrategies(configToUpdate.Strategies))
// Special case: the "TokenCredentialRequestAPI" data is mirrored into the deprecated status.kubeConfigInfo field.
if strategy.Frontend != nil && strategy.Frontend.Type == v1alpha1.TokenCredentialRequestAPIFrontendType {
configToUpdate.KubeConfigInfo = &v1alpha1.CredentialIssuerKubeConfigInfo{
Server: strategy.Frontend.TokenCredentialRequestAPIInfo.Server,
CertificateAuthorityData: strategy.Frontend.TokenCredentialRequestAPIInfo.CertificateAuthorityData,
}
}
}
// weights are a set of priorities for each strategy type.
//nolint: gochecknoglobals
var weights = map[v1alpha1.StrategyType]int{
v1alpha1.KubeClusterSigningCertificateStrategyType: 2, // most preferred strategy
v1alpha1.ImpersonationProxyStrategyType: 1,
// unknown strategy types will have weight 0 by default
}
type sortableStrategies []v1alpha1.CredentialIssuerStrategy
func (s sortableStrategies) Len() int { return len(s) }
func (s sortableStrategies) Less(i, j int) bool {
if wi, wj := weights[s[i].Type], weights[s[j].Type]; wi != wj {
return wi > wj
}
return s[i].Type < s[j].Type
}
func (s sortableStrategies) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func equalExceptLastUpdated(s1, s2 *v1alpha1.CredentialIssuerStrategy) bool {
s1 = s1.DeepCopy()
s2 = s2.DeepCopy()
s1.LastUpdateTime = metav1.Time{}
s2.LastUpdateTime = metav1.Time{}
return apiequality.Semantic.DeepEqual(s1, s2)
}