Skip to content

Commit e574d53

Browse files
committed
feat: create images from nodes instead of pods and update their status
1 parent 0178062 commit e574d53

File tree

6 files changed

+269
-213
lines changed

6 files changed

+269
-213
lines changed

api/kuik/v1alpha1/image_types.go

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,29 @@ type ImageSpec struct {
1414
Name string `json:"name"`
1515
}
1616

17-
type PodReference struct {
18-
// NamespacedName is the namespaced name of a pod (namespace/name)
19-
NamespacedName string `json:"namespacedName,omitempty"`
20-
}
21-
22-
type UsedBy struct {
23-
// Pods is a list of reference to pods using this Image
24-
Pods []PodReference `json:"pods,omitempty" patchStrategy:"merge" patchMergeKey:"namespacedName"`
25-
// Count is the number of pods using this image
17+
type ReferencesWithCount struct {
18+
// Items is a list of reference to objects using this Image
19+
Items []string `json:"items,omitempty"`
20+
// Count is the number of objects using this image
2621
//
27-
// jsonpath function .length() is not implemented, so the count field is required to display pods count in additionalPrinterColumns
22+
// jsonpath function .length() is not implemented, so the count field is required to display objects count in additionalPrinterColumns
2823
// see https://github.com/kubernetes-sigs/controller-tools/issues/447
2924
Count int `json:"count,omitempty"`
3025
}
3126

3227
// ImageStatus defines the observed state of Image.
3328
type ImageStatus struct {
34-
// UsedBy is the list of pods using this image
35-
UsedBy UsedBy `json:"usedBy,omitempty"`
29+
// UsedByPods is the list of pods using this image
30+
UsedByPods ReferencesWithCount `json:"usedByPods,omitempty"`
31+
// AvailableOnNodes is the list of nodes that have this image available locally
32+
AvailableOnNodes ReferencesWithCount `json:"availableOnNodes,omitempty"`
3633
}
3734

3835
// +kubebuilder:object:root=true
3936
// +kubebuilder:subresource:status
4037
// +kubebuilder:resource:scope=Cluster,shortName=img
41-
// +kubebuilder:printcolumn:name="Pods count",type="integer",JSONPath=".status.usedBy.count"
38+
// +kubebuilder:printcolumn:name="Pods count",type="integer",JSONPath=".status.usedByPods.count"
39+
// +kubebuilder:printcolumn:name="Nodes count",type="integer",JSONPath=".status.availableOnNodes.count"
4240
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
4341

4442
// Image is the Schema for the images API.
@@ -63,7 +61,24 @@ func init() {
6361
SchemeBuilder.Register(&Image{}, &ImageList{})
6462
}
6563

66-
func ImageNameFromSourceImage(sourceImage string) (string, error) {
64+
func ImageFromSourceImage(sourceImage string) (*Image, error) {
65+
sanitizedName, err := imageNameFromSourceImage(sourceImage)
66+
if err != nil {
67+
return nil, err
68+
}
69+
70+
return &Image{
71+
TypeMeta: metav1.TypeMeta{APIVersion: GroupVersion.String(), Kind: "Image"},
72+
ObjectMeta: metav1.ObjectMeta{
73+
Name: sanitizedName,
74+
},
75+
Spec: ImageSpec{
76+
Name: sourceImage,
77+
},
78+
}, nil
79+
}
80+
81+
func imageNameFromSourceImage(sourceImage string) (string, error) {
6782
ref, err := reference.ParseAnyReference(sourceImage)
6883
if err != nil {
6984
return "", err

api/kuik/v1alpha1/zz_generated.deepcopy.go

Lines changed: 9 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/kuik.enix.io_images.yaml

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ spec:
1717
scope: Cluster
1818
versions:
1919
- additionalPrinterColumns:
20-
- jsonPath: .status.usedBy.count
20+
- jsonPath: .status.usedByPods.count
2121
name: Pods count
2222
type: integer
23+
- jsonPath: .status.availableOnNodes.count
24+
name: Nodes count
25+
type: integer
2326
- jsonPath: .metadata.creationTimestamp
2427
name: Age
2528
type: date
@@ -57,25 +60,39 @@ spec:
5760
status:
5861
description: ImageStatus defines the observed state of Image.
5962
properties:
60-
usedBy:
61-
description: UsedBy is the list of pods using this image
63+
availableOnNodes:
64+
description: AvailableOnNodes is the list of nodes that have this
65+
image available locally
66+
properties:
67+
count:
68+
description: |-
69+
Count is the number of objects using this image
70+
71+
jsonpath function .length() is not implemented, so the count field is required to display objects count in additionalPrinterColumns
72+
see https://github.com/kubernetes-sigs/controller-tools/issues/447
73+
type: integer
74+
items:
75+
description: Items is a list of reference to objects using this
76+
Image
77+
items:
78+
type: string
79+
type: array
80+
type: object
81+
usedByPods:
82+
description: UsedByPods is the list of pods using this image
6283
properties:
6384
count:
6485
description: |-
65-
Count is the number of pods using this image
86+
Count is the number of objects using this image
6687
67-
jsonpath function .length() is not implemented, so the count field is required to display pods count in additionalPrinterColumns
88+
jsonpath function .length() is not implemented, so the count field is required to display objects count in additionalPrinterColumns
6889
see https://github.com/kubernetes-sigs/controller-tools/issues/447
6990
type: integer
70-
pods:
71-
description: Pods is a list of reference to pods using this Image
91+
items:
92+
description: Items is a list of reference to objects using this
93+
Image
7294
items:
73-
properties:
74-
namespacedName:
75-
description: NamespacedName is the namespaced name of a
76-
pod (namespace/name)
77-
type: string
78-
type: object
95+
type: string
7996
type: array
8097
type: object
8198
type: object

internal/controller/core/node_controller.go

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@ package core
33
import (
44
"context"
55

6+
kuikv1alpha1 "github.com/enix/kube-image-keeper/api/kuik/v1alpha1"
67
corev1 "k8s.io/api/core/v1"
8+
apierrors "k8s.io/apimachinery/pkg/api/errors"
79
"k8s.io/apimachinery/pkg/runtime"
10+
"k8s.io/klog/v2"
811
ctrl "sigs.k8s.io/controller-runtime"
12+
"sigs.k8s.io/controller-runtime/pkg/builder"
913
"sigs.k8s.io/controller-runtime/pkg/client"
14+
"sigs.k8s.io/controller-runtime/pkg/event"
15+
"sigs.k8s.io/controller-runtime/pkg/handler"
1016
logf "sigs.k8s.io/controller-runtime/pkg/log"
17+
"sigs.k8s.io/controller-runtime/pkg/predicate"
1118
)
1219

1320
// NodeReconciler reconciles a Node object
@@ -30,17 +37,105 @@ type NodeReconciler struct {
3037
// For more details, check Reconcile and its Result here:
3138
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
3239
func (r *NodeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
33-
_ = logf.FromContext(ctx)
40+
log := logf.FromContext(ctx)
3441

35-
// TODO(user): your logic here
42+
var node corev1.Node
43+
if err := r.Get(ctx, req.NamespacedName, &node); err != nil {
44+
return ctrl.Result{}, client.IgnoreNotFound(err)
45+
}
46+
47+
images := ImagesFromNode(ctx, &node)
48+
49+
hasDeletingImages := false
50+
for _, image := range images {
51+
var img kuikv1alpha1.Image
52+
err := r.Get(ctx, client.ObjectKeyFromObject(&image), &img)
53+
if err != nil && !apierrors.IsNotFound(err) {
54+
return ctrl.Result{}, err
55+
}
56+
57+
if !img.DeletionTimestamp.IsZero() {
58+
hasDeletingImages = true
59+
// image is already scheduled for deletion, thus we don't have to handle it here and will enqueue it back later
60+
log.Info("image is already being deleted, skipping", "image", klog.KObj(&image))
61+
continue
62+
}
63+
64+
// create or update Image depending on weather it already exists or not
65+
if apierrors.IsNotFound(err) {
66+
err = r.Create(ctx, &image)
67+
if err != nil {
68+
return ctrl.Result{}, err
69+
}
70+
} else {
71+
patch := client.MergeFrom(img.DeepCopy())
72+
73+
img.Spec.Name = image.Spec.Name
74+
75+
if err = r.Patch(ctx, &img, patch); err != nil {
76+
return ctrl.Result{}, err
77+
}
78+
}
79+
}
80+
81+
if hasDeletingImages {
82+
log.Info("some images are being deleted, requeuing later")
83+
return ctrl.Result{Requeue: true}, nil
84+
}
3685

3786
return ctrl.Result{}, nil
3887
}
3988

4089
// SetupWithManager sets up the controller with the Manager.
4190
func (r *NodeReconciler) SetupWithManager(mgr ctrl.Manager) error {
91+
p := predicate.Not(predicate.Funcs{
92+
DeleteFunc: func(e event.DeleteEvent) bool {
93+
return false
94+
},
95+
})
96+
4297
return ctrl.NewControllerManagedBy(mgr).
4398
For(&corev1.Node{}).
4499
Named("core-node").
100+
Watches(
101+
&kuikv1alpha1.Image{},
102+
handler.EnqueueRequestsFromMapFunc(r.nodesWithDeletingImages),
103+
builder.WithPredicates(p),
104+
).
45105
Complete(r)
46106
}
107+
108+
func (r *NodeReconciler) nodesWithDeletingImages(ctx context.Context, obj client.Object) []ctrl.Request {
109+
log := logf.
110+
FromContext(ctx).
111+
WithName("controller-runtime.manager.controller.node.deletingImages").
112+
WithValues("image", klog.KObj(obj))
113+
114+
image := obj.(*kuikv1alpha1.Image)
115+
res := make([]ctrl.Request, len(image.Status.AvailableOnNodes.Items))
116+
117+
for i, item := range image.Status.AvailableOnNodes.Items {
118+
log.Info("image in use, reconciling related node", "node", item)
119+
res[i].NamespacedName = client.ObjectKey{Namespace: "", Name: item}
120+
}
121+
122+
return res
123+
}
124+
125+
func ImagesFromNode(ctx context.Context, node *corev1.Node) []kuikv1alpha1.Image {
126+
log := logf.FromContext(ctx)
127+
images := []kuikv1alpha1.Image{}
128+
129+
for _, localImage := range node.Status.Images {
130+
for _, name := range localImage.Names {
131+
image, err := kuikv1alpha1.ImageFromSourceImage(name)
132+
if err != nil {
133+
log.Error(err, "could not parse image, ignoring")
134+
continue
135+
}
136+
images = append(images, *image)
137+
}
138+
}
139+
140+
return images
141+
}

0 commit comments

Comments
 (0)