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
package clusterkcp
import (
"context"
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"github.com/johnandersen777/socialweb-computer-kcp/internal/scale"
)
type NodeObserver struct {
Client func(ctx context.Context, cluster string) (kubernetes.Interface, error)
Baseline func(cluster string) (scale.Baseline, error)
Operators scale.Operators
CustomResources func(ctx context.Context, cluster string, kind scale.OperatorKind) ([]scale.CustomResource, error)
}
func (o NodeObserver) Observe(ctx context.Context, cluster string) (scale.Observation, int, error) {
if o.Client == nil {
return scale.Observation{}, 0, fmt.Errorf(
"clusterprovider: NodeObserver has no client, so %s cannot be read", cluster)
}
cs, err := o.Client(ctx, cluster)
if err != nil {
return scale.Observation{}, 0, fmt.Errorf("clusterprovider: the client for %s: %w", cluster, err)
}
pods, err := cs.CoreV1().Pods(metav1.NamespaceAll).List(ctx, metav1.ListOptions{})
if err != nil {
return scale.Observation{}, 0, fmt.Errorf("clusterprovider: listing pods in %s: %w", cluster, err)
}
jobs, err := cs.BatchV1().Jobs(metav1.NamespaceAll).List(ctx, metav1.ListOptions{})
if err != nil {
return scale.Observation{}, 0, fmt.Errorf("clusterprovider: listing jobs in %s: %w", cluster, err)
}
deployments, err := cs.AppsV1().Deployments(metav1.NamespaceAll).List(ctx, metav1.ListOptions{})
if err != nil {
return scale.Observation{}, 0, fmt.Errorf("clusterprovider: listing deployments in %s: %w", cluster, err)
}
statefulsets, err := cs.AppsV1().StatefulSets(metav1.NamespaceAll).List(ctx, metav1.ListOptions{})
if err != nil {
return scale.Observation{}, 0, fmt.Errorf("clusterprovider: listing statefulsets in %s: %w", cluster, err)
}
daemonsets, err := cs.AppsV1().DaemonSets(metav1.NamespaceAll).List(ctx, metav1.ListOptions{})
if err != nil {
return scale.Observation{}, 0, fmt.Errorf("clusterprovider: listing daemonsets in %s: %w", cluster, err)
}
nodes, err := cs.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
if err != nil {
return scale.Observation{}, 0, fmt.Errorf("clusterprovider: listing nodes in %s: %w", cluster, err)
}
replicasets, err := cs.AppsV1().ReplicaSets(metav1.NamespaceAll).List(ctx, metav1.ListOptions{})
if err != nil {
return scale.Observation{}, 0, fmt.Errorf("clusterprovider: listing replicasets in %s: %w", cluster, err)
}
appliedByTheControlPlane := map[string]bool{}
for i := range replicasets.Items {
rs := &replicasets.Items[i]
if scale.AppliedByTheControlPlane(rs.ObjectMeta) {
appliedByTheControlPlane[rs.Namespace+"/"+rs.Name] = true
}
}
for i := range pods.Items {
p := &pods.Items[i]
for _, ref := range p.OwnerReferences {
if ref.Kind != "ReplicaSet" {
continue
}
if !appliedByTheControlPlane[p.Namespace+"/"+ref.Name] {
continue
}
scale.StampAppliedByTheControlPlane(&p.ObjectMeta)
break
}
}
var baseline scale.Baseline
if o.Baseline != nil {
baseline, err = o.Baseline(cluster)
if err != nil {
return scale.Observation{}, 0, fmt.Errorf("clusterprovider: the add-on baseline of %s: %w", cluster, err)
}
}
unreconciled, err := o.unreconciled(ctx, cluster)
if err != nil {
return scale.Observation{}, 0, err
}
raw := scale.Observation{
Pods: pods.Items,
Jobs: jobs.Items,
Deployments: deployments.Items,
StatefulSets: statefulsets.Items,
DaemonSets: daemonsets.Items,
UnreconciledCustomResources: unreconciled,
}
raw.Placement = scale.Loads(nodes.Items, raw, replicasets.Items, baseline)
return scale.Narrow(raw, replicasets.Items, baseline), len(nodes.Items), nil
}
func (o NodeObserver) unreconciled(ctx context.Context, cluster string) ([]scale.CustomResource, error) {
inCluster := o.Operators.InCluster()
if len(inCluster) == 0 {
return nil, nil
}
if o.CustomResources == nil {
return nil, fmt.Errorf("clusterprovider: %d kind(s) in the operator registry run in the tenant "+
"cluster and no lister for custom resources was supplied, so their unreconciled CRs could "+
"never ask for a node: a CR its operator cannot reach at zero nodes would wait for ever", len(inCluster))
}
var all []scale.CustomResource
for _, kind := range inCluster {
crs, err := o.CustomResources(ctx, cluster, kind)
if err != nil {
return nil, fmt.Errorf("clusterprovider: listing %s.%s in %s: %w -- a read that failed is "+
"not a cluster whose CRs are all reconciled", kind.Kind, kind.Group, cluster, err)
}
all = append(all, crs...)
}
return o.Operators.Unreconciled(all), nil
}