generated from giantswarm/template-app
/
podfinder.go
119 lines (97 loc) · 3.24 KB
/
podfinder.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
package podfinder
import (
"context"
"github.com/giantswarm/microerror"
"github.com/giantswarm/micrologger"
coreV1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
const (
irsaVolumeName = "aws-iam-token"
irsaServiceAccountAnnotation = "eks.amazonaws.com/role-arn"
)
type Config struct {
CtrlClient client.Client
Logger micrologger.Logger
}
type PodFinder struct {
ctrlClient client.Client
logger micrologger.Logger
}
func New(config Config) (*PodFinder, error) {
if config.CtrlClient == nil {
return nil, microerror.Maskf(invalidConfigError, "%T.CtrlClient must not be empty", config)
}
if config.Logger == nil {
return nil, microerror.Maskf(invalidConfigError, "%T.Logger must not be empty", config)
}
return &PodFinder{
ctrlClient: config.CtrlClient,
logger: config.Logger,
}, nil
}
func (p *PodFinder) FindPodsToBeTerminated(ctx context.Context) ([]coreV1.Pod, error) {
ret := make([]coreV1.Pod, 0)
sas, err := p.getServiceAccountsWithIRSAEnabled(ctx)
if err != nil {
return nil, microerror.Mask(err)
}
// Get pods using this service account.
for _, sa := range sas {
pods, err := p.getPodsUsingServiceAccount(ctx, sa)
if err != nil {
return nil, microerror.Mask(err)
}
for _, pod := range pods {
needs, err := p.needsToBeRecreated(ctx, pod)
if err != nil {
return nil, microerror.Mask(err)
}
if needs {
ret = append(ret, pod)
}
}
}
return ret, nil
}
func (p *PodFinder) getServiceAccountsWithIRSAEnabled(ctx context.Context) ([]coreV1.ServiceAccount, error) {
p.logger.Debugf(ctx, "Finding all ServiceAccounts with IRSA annotation %q", irsaServiceAccountAnnotation)
serviceaccounts := coreV1.ServiceAccountList{}
err := p.ctrlClient.List(ctx, &serviceaccounts)
if err != nil {
return nil, microerror.Mask(err)
}
ret := make([]coreV1.ServiceAccount, 0)
for _, sa := range serviceaccounts.Items {
if _, ok := sa.Annotations[irsaServiceAccountAnnotation]; ok {
ret = append(ret, sa)
}
}
p.logger.Debugf(ctx, "Found %d ServiceAccounts", len(ret))
return ret, nil
}
func (p *PodFinder) getPodsUsingServiceAccount(ctx context.Context, sa coreV1.ServiceAccount) ([]coreV1.Pod, error) {
p.logger.Debugf(ctx, "Looking up pods using service account %s in namespace %s", sa.Name, sa.Namespace)
podList := coreV1.PodList{}
err := p.ctrlClient.List(ctx, &podList, client.InNamespace(sa.Namespace), client.MatchingFields{
"spec.serviceAccountName": sa.Name,
})
if err != nil {
return nil, microerror.Mask(err)
}
p.logger.Debugf(ctx, "Found %d pods using service account %s in namespace %s", len(podList.Items), sa.Name, sa.Namespace)
return podList.Items, nil
}
func (p *PodFinder) needsToBeRecreated(ctx context.Context, pod coreV1.Pod) (bool, error) {
p.logger.Debugf(ctx, "Checking if pod %s/%s needs to be recreated", pod.Namespace, pod.Name)
// Check if pod has needed volume.
for _, v := range pod.Spec.Volumes {
if v.Name == irsaVolumeName {
p.logger.Debugf(ctx, "Checking if pod %s/%s does NOT need to be recreated", pod.Namespace, pod.Name)
// volume present, pod does not need to be recreated.
return false, nil
}
}
p.logger.Debugf(ctx, "Checking if pod %s/%s needs to be recreated", pod.Namespace, pod.Name)
return true, nil
}