-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapply.go
111 lines (94 loc) · 2.77 KB
/
apply.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
110
111
package k8t
import (
"context"
"encoding/json"
"io/ioutil"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/discovery"
memory "k8s.io/client-go/discovery/cached"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/restmapper"
)
// options for GetWithOpts function
type ApplyOpts struct {
// here you can pass your context. It it's not set, the default
// context.Background() will be used
Context context.Context
// namespace where to apply resource. If it's not set, the cluster's
// test namespace will be used. This is ignored for cluster-wide resources
Namespace string
}
// Apply manifest on given path
func (c *Cluster) ApplyFile(path string) error {
data, err := ioutil.ReadFile(path)
if err != nil {
return err
}
return c.Apply(string(data))
}
// Same as kubectl apply. Function apply any YAML into given cluster,
// into default test namespace.
func (c *Cluster) Apply(yml string) error {
return c.ApplyWithOpts(yml, ApplyOpts{})
}
// More verbose version of Apply() function where you can pass your own
// context or set namespace
func (c *Cluster) ApplyWithOpts(yml string, opts ApplyOpts) error {
ctx := opts.Context
if ctx == nil {
ctx = context.Background()
}
namespace := opts.Namespace
if namespace == "" {
namespace = c.testNamespace
}
// Decode YAML manifest into unstructured.Unstructured
decUnstructured := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
obj := &unstructured.Unstructured{}
_, gvk, err := decUnstructured.Decode([]byte(yml), nil, obj)
if err != nil {
return err
}
// Prepare a RESTMapper and find GVR
dc, err := discovery.NewDiscoveryClientForConfig(c.restConfig)
if err != nil {
return err
}
mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(dc))
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
return err
}
// Prepare the dynamic client
dyn, err := dynamic.NewForConfig(c.restConfig)
if err != nil {
return err
}
// Obtain REST interface for the GVR
var dr dynamic.ResourceInterface
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
// namespaced resources are placed into test Namespace
dr = dyn.Resource(mapping.Resource).Namespace(namespace)
} else {
// for cluster-wide resources
dr = dyn.Resource(mapping.Resource)
}
// Marshal object into JSON
data, err := json.Marshal(obj)
if err != nil {
return err
}
// Create or Update the object with server-side-apply
_, err = dr.Patch(ctx, obj.GetName(), types.ApplyPatchType, data, metav1.PatchOptions{
FieldManager: "k8t",
})
if err != nil {
return err
} else {
return nil
}
}