-
Notifications
You must be signed in to change notification settings - Fork 0
/
update.go
109 lines (93 loc) · 3.51 KB
/
update.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package registry
import (
"context"
"fmt"
reststore "github.com/henderiw/apiserver-store/pkg/rest"
"github.com/henderiw/logger/log"
"go.opentelemetry.io/otel/trace"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/rest"
)
const (
OptimisticLockErrorMsg = "the object has been modified; please apply your changes to the latest version and try again"
)
// Update performs an atomic update and set of the object. Returns the result of the update
// or an error. If the registry allows create-on-update, the create flow will be executed.
// A bool is returned along with the object and any errors, to indicate object creation.
func (r *Store) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
ctx, span := r.Tracer.Start(ctx, fmt.Sprintf("%s:update", r.DefaultQualifiedResource.Resource), trace.WithAttributes())
defer span.End()
log := log.FromContext(ctx)
log.Info("update")
if err := r.UpdateStrategy.BeginUpdate(ctx); err != nil {
return nil, false, err
}
key, err := r.KeyFunc(ctx, name)
if err != nil {
return nil, false, err
}
qualifiedResource := r.qualifiedResourceFromContext(ctx)
creating := false
existing, err := r.GetStrategy.Get(ctx, key)
if err != nil {
if !r.UpdateStrategy.AllowCreateOnUpdate() && !forceAllowCreate {
return nil, creating, apierrors.NewNotFound(qualifiedResource, name)
}
creating = true
}
obj, err := objInfo.UpdatedObject(ctx, existing)
if err != nil {
log.Error("update failed to construct UpdatedObject", "error", err.Error())
return nil, creating, err
}
if creating {
obj, err := r.Create(ctx, obj, createValidation, &metav1.CreateOptions{
TypeMeta: options.TypeMeta,
DryRun: options.DryRun,
FieldManager: options.FieldManager,
FieldValidation: options.FieldValidation,
})
if err != nil {
return nil, creating, err
}
return obj, creating, nil
}
if err := reststore.BeforeUpdate(r.UpdateStrategy, ctx, obj, existing); err != nil {
return nil, creating, err
}
// at this point we have a fully formed object. It is time to call the validators that the apiserver
// handling chain wants to enforce.
if updateValidation != nil {
if err := updateValidation(ctx, obj.DeepCopyObject(), existing.DeepCopyObject()); err != nil {
return nil, creating, err
}
}
newaccessor, err := meta.Accessor(obj)
if err != nil {
return nil, creating, err
}
oldaccessor, err := meta.Accessor(existing)
if err != nil {
return nil, creating, err
}
if newaccessor.GetResourceVersion() != oldaccessor.GetResourceVersion() {
return nil, false, apierrors.NewConflict(r.DefaultQualifiedResource, oldaccessor.GetName(), fmt.Errorf(OptimisticLockErrorMsg))
}
if oldaccessor.GetDeletionTimestamp() != nil && len(newaccessor.GetFinalizers()) == 0 {
if err := r.DeleteStrategy.Delete(ctx, key, obj, isDryRun(options.DryRun)); err != nil {
return nil, false, apierrors.NewInternalError(err)
}
// deleted
return obj, false, nil
}
obj, err = r.UpdateStrategy.Update(ctx, key, obj, existing, isDryRun(options.DryRun))
if err != nil {
// TODO see if we need to return more errors
return obj, creating, apierrors.NewInternalError(err)
}
// The operation has succeeded.
return obj, creating, nil
}