-
Notifications
You must be signed in to change notification settings - Fork 27
/
replace.go
108 lines (96 loc) · 3.17 KB
/
replace.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
/*
* Copyright the original author or 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 kubernetes
import (
"context"
"fmt"
batchv1 "k8s.io/api/batch/v1"
"github.com/citrusframework/yaks/pkg/client"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime/pkg/client"
)
// ReplaceResources allows to completely replace a list of resources on Kubernetes, taking care of immutable fields and resource versions.
func ReplaceResources(ctx context.Context, c client.Client, objects []ctrl.Object) error {
for _, object := range objects {
err := ReplaceResource(ctx, c, object)
if err != nil {
return err
}
}
return nil
}
// ReplaceResource allows to completely replace a resource on Kubernetes, taking care of immutable fields and resource versions.
func ReplaceResource(ctx context.Context, c client.Client, res ctrl.Object) error {
err := c.Create(ctx, res)
if err != nil && k8serrors.IsAlreadyExists(err) {
if existing, ok := res.DeepCopyObject().(ctrl.Object); ok {
err = c.Get(ctx, ctrl.ObjectKeyFromObject(existing), existing)
if err != nil {
return err
}
mapRequiredMeta(existing, res)
mapRequiredServiceData(existing, res)
mapRequiredJobData(existing, res)
} else {
return fmt.Errorf("type assertion failed: %v", res.DeepCopyObject())
}
err = c.Update(ctx, res)
}
if err != nil {
return errors.Wrap(err, "could not create or replace "+findResourceDetails(res))
}
return nil
}
func mapRequiredMeta(from runtime.Object, to runtime.Object) {
if fromC, ok := from.(metav1.Object); ok {
if toC, ok := to.(metav1.Object); ok {
toC.SetResourceVersion(fromC.GetResourceVersion())
}
}
}
func mapRequiredServiceData(from runtime.Object, to runtime.Object) {
if fromC, ok := from.(*corev1.Service); ok {
if toC, ok := to.(*corev1.Service); ok {
toC.Spec.ClusterIP = fromC.Spec.ClusterIP
}
}
}
func mapRequiredJobData(from runtime.Object, to runtime.Object) {
if fromC, ok := from.(*batchv1.Job); ok {
if toC, ok := to.(*batchv1.Job); ok {
toC.Spec.Selector = fromC.Spec.Selector
toC.Spec.Template.Labels["job-name"] = fromC.Spec.Template.Labels["job-name"]
toC.Spec.Template.Labels["controller-uid"] = fromC.Spec.Template.Labels["controller-uid"]
}
}
}
func findResourceDetails(res runtime.Object) string {
if res == nil {
return "nil resource"
}
if meta, ok := res.(metav1.Object); ok {
name := meta.GetName()
if ty, ok := res.(metav1.Type); ok {
return ty.GetKind() + " " + name
}
return "resource " + name
}
return "unnamed resource"
}