kruise/pkg/controller/imagepulljob/imagepulljob_utils.go

248 lines
7.2 KiB
Go

/*
Copyright 2021 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package imagepulljob
import (
"fmt"
"math/rand"
"reflect"
"strings"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"
utilpointer "k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
"github.com/openkruise/kruise/pkg/util"
)
type syncAction string
const (
defaultTTLSecondsForNever = int32(24 * 3600)
defaultActiveDeadlineSecondsForNever = int64(1800)
create syncAction = "create"
update syncAction = "update"
noAction syncAction = "noAction"
)
func getTTLSecondsForAlways(job *appsv1alpha1.ImagePullJob) *int32 {
var ret int32
if job.Spec.CompletionPolicy.TTLSecondsAfterFinished != nil {
ret = *job.Spec.CompletionPolicy.TTLSecondsAfterFinished
} else if job.Spec.CompletionPolicy.ActiveDeadlineSeconds != nil {
ret = int32(*job.Spec.CompletionPolicy.ActiveDeadlineSeconds)
} else {
timeoutSeconds := int32(600)
backoffLimit := int32(3)
if job.Spec.PullPolicy != nil && job.Spec.PullPolicy.TimeoutSeconds != nil {
timeoutSeconds = *job.Spec.PullPolicy.TimeoutSeconds
}
if job.Spec.PullPolicy != nil && job.Spec.PullPolicy.BackoffLimit != nil {
backoffLimit = *job.Spec.PullPolicy.BackoffLimit
}
ret = timeoutSeconds * backoffLimit
}
ret += 300 + rand.Int31n(300)
return &ret
}
func getOwnerRef(job *appsv1alpha1.ImagePullJob) *v1.ObjectReference {
return &v1.ObjectReference{
APIVersion: controllerKind.GroupVersion().String(),
Kind: controllerKind.Kind,
Name: job.Name,
Namespace: job.Namespace,
UID: job.UID,
}
}
func getSecrets(job *appsv1alpha1.ImagePullJob) []appsv1alpha1.ReferenceObject {
var secrets []appsv1alpha1.ReferenceObject
for _, secret := range job.Spec.PullSecrets {
secrets = append(secrets,
appsv1alpha1.ReferenceObject{
Namespace: job.Namespace,
Name: secret,
})
}
return secrets
}
func getImagePullPolicy(job *appsv1alpha1.ImagePullJob) *appsv1alpha1.ImageTagPullPolicy {
pullPolicy := &appsv1alpha1.ImageTagPullPolicy{}
if job.Spec.PullPolicy != nil {
pullPolicy.BackoffLimit = job.Spec.PullPolicy.BackoffLimit
pullPolicy.TimeoutSeconds = job.Spec.PullPolicy.TimeoutSeconds
}
if job.Spec.CompletionPolicy.Type == appsv1alpha1.Never {
pullPolicy.TTLSecondsAfterFinished = getTTLSecondsForNever()
pullPolicy.ActiveDeadlineSeconds = getActiveDeadlineSecondsForNever(job)
} else {
pullPolicy.TTLSecondsAfterFinished = getTTLSecondsForAlways(job)
pullPolicy.ActiveDeadlineSeconds = job.Spec.CompletionPolicy.ActiveDeadlineSeconds
}
return pullPolicy
}
func getTTLSecondsForNever() *int32 {
// 24h +- 10min
var ret = defaultTTLSecondsForNever + rand.Int31n(1200) - 600
return &ret
}
func getActiveDeadlineSecondsForNever(job *appsv1alpha1.ImagePullJob) *int64 {
if job.Spec.PullPolicy != nil && job.Spec.PullPolicy.TimeoutSeconds != nil &&
int64(*job.Spec.PullPolicy.TimeoutSeconds) > defaultActiveDeadlineSecondsForNever {
ret := int64(*job.Spec.PullPolicy.TimeoutSeconds)
return utilpointer.Int64(ret)
}
var ret = defaultActiveDeadlineSecondsForNever
return utilpointer.Int64(ret)
}
func containsObject(slice []appsv1alpha1.ReferenceObject, obj appsv1alpha1.ReferenceObject) bool {
for _, o := range slice {
if o.Namespace == obj.Namespace && o.Name == obj.Name {
return true
}
}
return false
}
func formatStatusMessage(status *appsv1alpha1.ImagePullJobStatus) (ret string) {
if status.CompletionTime != nil {
return "job has completed"
}
if status.Desired == 0 {
return "job is running, no progress"
}
return fmt.Sprintf("job is running, progress %.1f%%", 100.0*float64(status.Succeeded+status.Failed)/float64(status.Desired))
}
func keyFromRef(ref appsv1alpha1.ReferenceObject) types.NamespacedName {
return types.NamespacedName{
Name: ref.Name,
Namespace: ref.Namespace,
}
}
func keyFromObject(object client.Object) types.NamespacedName {
return types.NamespacedName{
Name: object.GetName(),
Namespace: object.GetNamespace(),
}
}
func targetFromSource(source *v1.Secret, keySet referenceSet) *v1.Secret {
target := source.DeepCopy()
target.ObjectMeta = metav1.ObjectMeta{
Namespace: util.GetKruiseDaemonConfigNamespace(),
GenerateName: fmt.Sprintf("%s-", source.Name),
Labels: source.Labels,
Annotations: source.Annotations,
}
if target.Labels == nil {
target.Labels = map[string]string{}
}
target.Labels[SourceSecretUIDLabelKey] = string(source.UID)
if target.Annotations == nil {
target.Annotations = map[string]string{}
}
target.Annotations[SourceSecretKeyAnno] = keyFromObject(source).String()
target.Annotations[TargetOwnerReferencesAnno] = keySet.String()
return target
}
func updateTarget(target, source *v1.Secret, keySet referenceSet) *v1.Secret {
target = target.DeepCopy()
target.Data = source.Data
target.StringData = source.StringData
target.Annotations[TargetOwnerReferencesAnno] = keySet.String()
return target
}
func referenceSetFromTarget(target *v1.Secret) referenceSet {
refs := strings.Split(target.Annotations[TargetOwnerReferencesAnno], ",")
keys := makeReferenceSet()
for _, ref := range refs {
namespace, name, err := cache.SplitMetaNamespaceKey(ref)
if err != nil {
klog.ErrorS(err, "Failed to parse job key from annotations in target Secret", "secret", klog.KObj(target))
continue
}
keys.Insert(types.NamespacedName{Namespace: namespace, Name: name})
}
return keys
}
func computeTargetSyncAction(source, target *v1.Secret, job *appsv1alpha1.ImagePullJob) syncAction {
if target == nil || len(target.UID) == 0 {
return create
}
keySet := referenceSetFromTarget(target)
if !keySet.Contains(keyFromObject(job)) ||
!reflect.DeepEqual(source.Data, target.Data) ||
!reflect.DeepEqual(source.StringData, target.StringData) {
return update
}
return noAction
}
func makeReferenceSet(items ...types.NamespacedName) referenceSet {
refSet := map[types.NamespacedName]struct{}{}
for _, item := range items {
refSet[item] = struct{}{}
}
return refSet
}
type referenceSet map[types.NamespacedName]struct{}
func (set referenceSet) String() string {
keyList := make([]string, 0, len(set))
for ref := range set {
keyList = append(keyList, ref.String())
}
return strings.Join(keyList, ",")
}
func (set referenceSet) Contains(key types.NamespacedName) bool {
_, exists := set[key]
return exists
}
func (set referenceSet) Insert(key types.NamespacedName) referenceSet {
set[key] = struct{}{}
return set
}
func (set referenceSet) Delete(key types.NamespacedName) referenceSet {
delete(set, key)
return set
}
func (set referenceSet) IsEmpty() bool {
return len(set) == 0
}