1 package resource
2
3 import (
4 "context"
5 "fmt"
6 "io"
7
8 link "github.com/linkerd/linkerd2/controller/gen/apis/link/v1alpha1"
9 policy "github.com/linkerd/linkerd2/controller/gen/apis/policy/v1alpha1"
10 profile "github.com/linkerd/linkerd2/controller/gen/apis/serviceprofile/v1alpha2"
11 "github.com/linkerd/linkerd2/pkg/k8s"
12 log "github.com/sirupsen/logrus"
13 admissionRegistration "k8s.io/api/admissionregistration/v1"
14 apps "k8s.io/api/apps/v1"
15 batch "k8s.io/api/batch/v1"
16 core "k8s.io/api/core/v1"
17 k8sPolicy "k8s.io/api/policy/v1"
18 rbac "k8s.io/api/rbac/v1"
19 apiextension "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
20 kerrors "k8s.io/apimachinery/pkg/api/errors"
21 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22 "k8s.io/apimachinery/pkg/runtime"
23 "k8s.io/apimachinery/pkg/runtime/schema"
24 apiRegistration "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
25 apiregistrationv1client "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1"
26 "sigs.k8s.io/yaml"
27 )
28
29 const (
30 yamlSep = "---\n"
31 )
32
33
34 type Kubernetes struct {
35 runtime.TypeMeta
36 metav1.ObjectMeta `json:"metadata"`
37 }
38
39 var prunableNamespaceResources []schema.GroupVersionResource = []schema.GroupVersionResource{
40 core.SchemeGroupVersion.WithResource("configmaps"),
41 batch.SchemeGroupVersion.WithResource("cronjobs"),
42 apps.SchemeGroupVersion.WithResource("daemonsets"),
43 apps.SchemeGroupVersion.WithResource("deployments"),
44 batch.SchemeGroupVersion.WithResource("jobs"),
45 policy.SchemeGroupVersion.WithResource("meshtlsauthentications"),
46 policy.SchemeGroupVersion.WithResource("networkauthentications"),
47 core.SchemeGroupVersion.WithResource("replicationcontrollers"),
48 core.SchemeGroupVersion.WithResource("secrets"),
49 core.SchemeGroupVersion.WithResource("services"),
50 profile.SchemeGroupVersion.WithResource("serviceprofiles"),
51 apps.SchemeGroupVersion.WithResource("statefulsets"),
52 rbac.SchemeGroupVersion.WithResource("roles"),
53 rbac.SchemeGroupVersion.WithResource("rolebindings"),
54 core.SchemeGroupVersion.WithResource("serviceaccounts"),
55 k8sPolicy.SchemeGroupVersion.WithResource("poddisruptionbudgets"),
56 k8s.ServerGVR,
57 k8s.SazGVR,
58 k8s.AuthorizationPolicyGVR,
59 link.SchemeGroupVersion.WithResource("links"),
60 k8s.HTTPRouteGVR,
61 }
62
63 var prunableClusterResources []schema.GroupVersionResource = []schema.GroupVersionResource{
64 rbac.SchemeGroupVersion.WithResource("clusterroles"),
65 rbac.SchemeGroupVersion.WithResource("clusterrolebindings"),
66 apiRegistration.SchemeGroupVersion.WithResource("apiservices"),
67 admissionRegistration.SchemeGroupVersion.WithResource("mutatingwebhookconfigurations"),
68 admissionRegistration.SchemeGroupVersion.WithResource("validatingwebhookconfigurations"),
69 apiextension.SchemeGroupVersion.WithResource("customresourcedefinitions"),
70 }
71
72
73 func New(apiVersion, kind, name string) Kubernetes {
74 return Kubernetes{
75 runtime.TypeMeta{
76 APIVersion: apiVersion,
77 Kind: kind,
78 },
79 metav1.ObjectMeta{
80 Name: name,
81 },
82 }
83 }
84
85
86 func NewNamespaced(apiVersion, kind, name, namespace string) Kubernetes {
87 return Kubernetes{
88 runtime.TypeMeta{
89 APIVersion: apiVersion,
90 Kind: kind,
91 },
92 metav1.ObjectMeta{
93 Name: name,
94 Namespace: namespace,
95 },
96 }
97 }
98
99
100 func (r Kubernetes) RenderResource(w io.Writer) error {
101 b, err := yaml.Marshal(r)
102 if err != nil {
103 return err
104 }
105
106 _, err = w.Write(b)
107 if err != nil {
108 return err
109 }
110
111 _, err = w.Write([]byte(yamlSep))
112 return err
113 }
114
115
116
117 func FetchKubernetesResources(ctx context.Context, k *k8s.KubernetesAPI, options metav1.ListOptions) ([]Kubernetes, error) {
118
119 resources := make([]Kubernetes, 0)
120
121 clusterRoles, err := fetchClusterRoles(ctx, k, options)
122 if err != nil {
123 return nil, fmt.Errorf("could not fetch ClusterRole resources: %w", err)
124 }
125 resources = append(resources, clusterRoles...)
126
127 clusterRoleBindings, err := fetchClusterRoleBindings(ctx, k, options)
128 if err != nil {
129 return nil, fmt.Errorf("could not fetch ClusterRoleBinding resources: %w", err)
130 }
131 resources = append(resources, clusterRoleBindings...)
132
133 roles, err := fetchRoles(ctx, k, options)
134 if err != nil {
135 return nil, fmt.Errorf("could not fetch Roles: %w", err)
136 }
137 resources = append(resources, roles...)
138
139 roleBindings, err := fetchRoleBindings(ctx, k, options)
140 if err != nil {
141 return nil, fmt.Errorf("could not fetch RoleBindings: %w", err)
142 }
143 resources = append(resources, roleBindings...)
144
145 crds, err := fetchCustomResourceDefinitions(ctx, k, options)
146 if err != nil {
147 return nil, fmt.Errorf("could not fetch CustomResourceDefinition resources: %w", err)
148 }
149 resources = append(resources, crds...)
150
151 apiCRDs, err := fetchAPIRegistrationResources(ctx, k, options)
152 if err != nil {
153 return nil, fmt.Errorf("could not fetch APIService CRDs: %w", err)
154 }
155 resources = append(resources, apiCRDs...)
156
157 mutatinghooks, err := fetchMutatingWebhooksConfiguration(ctx, k, options)
158 if err != nil {
159 return nil, fmt.Errorf("could not fetch MutatingWebhookConfigurations: %w", err)
160 }
161 resources = append(resources, mutatinghooks...)
162
163 validationhooks, err := fetchValidatingWebhooksConfiguration(ctx, k, options)
164 if err != nil {
165 return nil, fmt.Errorf("could not fetch ValidatingWebhookConfiguration: %w", err)
166 }
167 resources = append(resources, validationhooks...)
168
169 namespaces, err := fetchNamespace(ctx, k, options)
170 if err != nil {
171 return nil, fmt.Errorf("could not fetch Namespace: %w", err)
172 }
173 resources = append(resources, namespaces...)
174
175 return resources, nil
176 }
177
178 func fetchClusterRoles(ctx context.Context, k *k8s.KubernetesAPI, options metav1.ListOptions) ([]Kubernetes, error) {
179 list, err := k.RbacV1().ClusterRoles().List(ctx, options)
180 if err != nil {
181 return nil, err
182 }
183
184 resources := make([]Kubernetes, len(list.Items))
185 for i, item := range list.Items {
186 resources[i] = New(rbac.SchemeGroupVersion.String(), "ClusterRole", item.Name)
187 }
188
189 return resources, nil
190 }
191
192 func fetchClusterRoleBindings(ctx context.Context, k *k8s.KubernetesAPI, options metav1.ListOptions) ([]Kubernetes, error) {
193 list, err := k.RbacV1().ClusterRoleBindings().List(ctx, options)
194 if err != nil {
195 return nil, err
196 }
197
198 resources := make([]Kubernetes, len(list.Items))
199 for i, item := range list.Items {
200 resources[i] = New(rbac.SchemeGroupVersion.String(), "ClusterRoleBinding", item.Name)
201 }
202
203 return resources, nil
204 }
205
206 func fetchRoles(ctx context.Context, k *k8s.KubernetesAPI, options metav1.ListOptions) ([]Kubernetes, error) {
207 list, err := k.RbacV1().Roles("").List(ctx, options)
208 if err != nil {
209 return nil, err
210 }
211
212 resources := make([]Kubernetes, len(list.Items))
213 for i, item := range list.Items {
214 r := New(rbac.SchemeGroupVersion.String(), "Role", item.Name)
215 r.Namespace = item.Namespace
216 resources[i] = r
217 }
218 return resources, nil
219 }
220
221 func fetchRoleBindings(ctx context.Context, k *k8s.KubernetesAPI, options metav1.ListOptions) ([]Kubernetes, error) {
222 list, err := k.RbacV1().RoleBindings("").List(ctx, options)
223 if err != nil {
224 return nil, err
225 }
226
227 resources := make([]Kubernetes, len(list.Items))
228 for i, item := range list.Items {
229 r := New(rbac.SchemeGroupVersion.String(), "RoleBinding", item.Name)
230 r.Namespace = item.Namespace
231 resources[i] = r
232 }
233 return resources, nil
234 }
235
236 func fetchCustomResourceDefinitions(ctx context.Context, k *k8s.KubernetesAPI, options metav1.ListOptions) ([]Kubernetes, error) {
237 list, err := k.Apiextensions.ApiextensionsV1().CustomResourceDefinitions().List(ctx, options)
238 if err != nil {
239 return nil, err
240 }
241
242 resources := make([]Kubernetes, len(list.Items))
243 for i, item := range list.Items {
244 resources[i] = New(apiextension.SchemeGroupVersion.String(), "CustomResourceDefinition", item.Name)
245 }
246
247 return resources, nil
248 }
249
250 func fetchNamespace(ctx context.Context, k *k8s.KubernetesAPI, options metav1.ListOptions) ([]Kubernetes, error) {
251 list, err := k.CoreV1().Namespaces().List(ctx, options)
252 if err != nil {
253 return nil, err
254 }
255
256 resources := make([]Kubernetes, len(list.Items))
257 for i, item := range list.Items {
258 r := New(core.SchemeGroupVersion.String(), "Namespace", item.Name)
259 r.Namespace = item.Namespace
260 resources[i] = r
261 }
262 return resources, nil
263 }
264
265 func fetchValidatingWebhooksConfiguration(ctx context.Context, k *k8s.KubernetesAPI, options metav1.ListOptions) ([]Kubernetes, error) {
266 list, err := k.AdmissionregistrationV1().ValidatingWebhookConfigurations().List(ctx, options)
267 if err != nil {
268 return nil, err
269 }
270
271 resources := make([]Kubernetes, len(list.Items))
272 for i, item := range list.Items {
273 resources[i] = New(admissionRegistration.SchemeGroupVersion.String(), "ValidatingWebhookConfiguration", item.Name)
274 }
275
276 return resources, nil
277 }
278
279 func fetchMutatingWebhooksConfiguration(ctx context.Context, k *k8s.KubernetesAPI, options metav1.ListOptions) ([]Kubernetes, error) {
280 list, err := k.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, options)
281 if err != nil {
282 return nil, err
283 }
284
285 resources := make([]Kubernetes, len(list.Items))
286 for i, item := range list.Items {
287 resources[i] = New(admissionRegistration.SchemeGroupVersion.String(), "MutatingWebhookConfiguration", item.Name)
288 }
289
290 return resources, nil
291 }
292 func fetchAPIRegistrationResources(ctx context.Context, k *k8s.KubernetesAPI, options metav1.ListOptions) ([]Kubernetes, error) {
293 apiClient, err := apiregistrationv1client.NewForConfig(k.Config)
294 if err != nil {
295 return nil, err
296 }
297
298 list, err := apiClient.APIServices().List(ctx, options)
299 if err != nil {
300 return nil, err
301 }
302
303 resources := make([]Kubernetes, len(list.Items))
304 for i, item := range list.Items {
305 resources[i] = New(apiRegistration.SchemeGroupVersion.String(), "APIService", item.Name)
306 }
307
308 return resources, nil
309 }
310
311 func FetchPrunableResources(ctx context.Context, k *k8s.KubernetesAPI, namespace string, options metav1.ListOptions) ([]Kubernetes, error) {
312 resources := []Kubernetes{}
313
314 for _, gvr := range prunableNamespaceResources {
315 items, err := k.DynamicClient.Resource(gvr).Namespace(namespace).List(ctx, options)
316 if err != nil {
317 if !kerrors.IsNotFound(err) {
318 log.Debugf("failed to list resources of type %s", gvr)
319 }
320 continue
321 }
322 for _, item := range items.Items {
323 resources = append(resources, NewNamespaced(item.GetAPIVersion(), item.GetKind(), item.GetName(), item.GetNamespace()))
324 }
325 }
326
327 for _, gvr := range prunableClusterResources {
328 items, err := k.DynamicClient.Resource(gvr).List(ctx, options)
329 if err != nil {
330 log.Debugf("failed to list resources of type %s", gvr)
331 continue
332 }
333 for _, item := range items.Items {
334 resources = append(resources, New(item.GetAPIVersion(), item.GetKind(), item.GetName()))
335 }
336 }
337 return resources, nil
338 }
339
View as plain text