-
Notifications
You must be signed in to change notification settings - Fork 69
/
machinehandler.go
180 lines (156 loc) · 5.04 KB
/
machinehandler.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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
package machinehandler
import (
"context"
"errors"
"fmt"
"reflect"
"time"
"github.com/mitchellh/mapstructure"
corev1 "k8s.io/api/core/v1"
k8serror "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
)
var (
ErrApiGroupNotFound = errors.New("failed to find API group")
)
type MachineHandler struct {
Client client.Client
Config *rest.Config
Ctx context.Context
Namespace string
}
type Machine struct {
metav1.ObjectMeta `json:"metadata,omitempty"`
Status MachineStatus `json:"status,omitempty"`
}
type MachineStatus struct {
NodeRef *corev1.ObjectReference `json:"nodeRef,omitempty"`
Addresses []corev1.NodeAddress `json:"addresses,omitempty"`
}
// ListMachines list all machines using given client
func (m *MachineHandler) ListMachines(apiGroupVersion schema.GroupVersion) ([]Machine, error) {
apiVersion, err := m.getAPIGroupPreferredVersion(apiGroupVersion.Group)
if err != nil {
// when MachineAPI capability is disabled we ignore error
// that we can't find api version/group for given group
// and return nil, because there are no machines,
// and it makes no sense to continue function
if err == ErrApiGroupNotFound {
return nil, nil
}
return nil, err
}
// we set group version to user provided one
// if not, set preffered version from discovery above
if apiGroupVersion.Version == "" {
apiGroupVersion.Version = apiVersion
}
// Detect if machine api present in the cluster
// If it's not then return empty array because
// there are no machines present
present, err := isMachineCRDPresent(m.Config, apiGroupVersion)
if err != nil {
return nil, err
}
if !present {
return nil, nil
}
unstructuredMachineList := &unstructured.UnstructuredList{}
unstructuredMachineList.SetGroupVersionKind(apiGroupVersion.WithKind("MachineList"))
listOpts := make([]client.ListOption, 0)
if m.Namespace != "" {
listOpts = append(listOpts, client.InNamespace(m.Namespace))
}
if err := m.Client.List(m.Ctx, unstructuredMachineList, listOpts...); err != nil {
return nil, err
}
machines := []Machine{}
stringToTimeHook := func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
if f.Kind() == reflect.String && t == reflect.TypeOf(metav1.Time{}) {
t, err := time.Parse(time.RFC3339, data.(string))
return metav1.Time{Time: t}, err
}
return data, nil
}
for _, obj := range unstructuredMachineList.Items {
machine := Machine{}
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
TagName: "json",
Result: &machine,
DecodeHook: stringToTimeHook,
})
if err != nil {
return nil, err
}
err = decoder.Decode(obj.Object)
if err != nil {
return nil, err
}
machines = append(machines, machine)
}
return machines, nil
}
// getAPIGroupPreferredVersion get preferred API version using API group
func (m *MachineHandler) getAPIGroupPreferredVersion(apiGroup string) (string, error) {
if m.Config == nil {
return "", fmt.Errorf("machine handler config can't be nil")
}
managementDiscoveryClient, err := discovery.NewDiscoveryClientForConfig(m.Config)
if err != nil {
return "", fmt.Errorf("create discovery client failed: %v", err)
}
groupList, err := managementDiscoveryClient.ServerGroups()
if err != nil {
return "", fmt.Errorf("failed to get ServerGroups: %v", err)
}
for _, group := range groupList.Groups {
if group.Name == apiGroup {
return group.PreferredVersion.Version, nil
}
}
return "", ErrApiGroupNotFound
}
func isMachineCRDPresent(cfg *rest.Config, groupVersion schema.GroupVersion) (bool, error) {
discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg)
if err != nil {
return false, fmt.Errorf("create discovery client failed: %v", err)
}
res, err := discoveryClient.ServerResourcesForGroupVersion(groupVersion.String())
if err != nil {
if k8serror.IsNotFound(err) {
return false, nil
}
return false, err
}
for _, resource := range res.APIResources {
if resource.Kind == "Machine" {
return true, nil
}
}
return false, nil
}
// FindMatchingMachineFromInternalDNS find matching machine for node using internal DNS
func FindMatchingMachineFromInternalDNS(machines []Machine, nodeName string) (*Machine, error) {
for _, machine := range machines {
for _, address := range machine.Status.Addresses {
if corev1.NodeAddressType(address.Type) == corev1.NodeInternalDNS && address.Address == nodeName {
return &machine, nil
}
}
}
return nil, fmt.Errorf("matching machine not found")
}
// FindMatchingMachineFromNodeRef find matching machine for node using node ref
func FindMatchingMachineFromNodeRef(machines []Machine, nodeName string) (*Machine, error) {
for _, machine := range machines {
if machine.Status.NodeRef != nil && machine.Status.NodeRef.Name == nodeName {
return &machine, nil
}
}
return nil, fmt.Errorf("matching machine not found")
}