Add default daemonset template, remove hardcoded DS definition

Change-Id: I4602978a23da3fa446e50fd1ecfd3cc78aa27999
This commit is contained in:
Kostiantyn Kalynovskyi 2021-01-14 12:13:19 -06:00
parent 36b14784e1
commit bd8fb9205d
7 changed files with 45 additions and 189 deletions

View File

@ -1,22 +1,19 @@
apiVersion: v1 apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:
name: libvirt-template name: daemonset-template
data: data:
template: | template: |
apiVersion: apps/v1 apiVersion: apps/v1
kind: DaemonSet kind: DaemonSet
metadata:
name: vino
namespace: default
spec: spec:
selector: selector:
matchLabels: matchLabels:
vino-test: cr-with-ds-template vino-role: vino-builder
template: template:
metadata: metadata:
labels: labels:
vino-test: cr-with-ds-template vino-role: vino-builder
spec: spec:
tolerations: tolerations:
- key: node-role.kubernetes.io/master - key: node-role.kubernetes.io/master

View File

@ -1,2 +1,3 @@
resources: resources:
- manager.yaml - manager.yaml
- daemonset-template.yaml

View File

@ -1,12 +1,12 @@
apiVersion: airship.airshipit.org/v1 apiVersion: airship.airshipit.org/v1
kind: Vino kind: Vino
metadata: metadata:
name: vino name: vino-test-cr
# labels: ... # labels: ...
spec: spec:
nodeSelector: nodeSelector:
matchLabels: matchLabels:
node-type: worker beta.kubernetes.io/os: linux
configuration: configuration:
cpuExclude: 0-4,54-60 cpuExclude: 0-4,54-60
redfishCredentialSecret: redfishCredentialSecret:

View File

@ -1,16 +0,0 @@
apiVersion: airship.airshipit.org/v1
kind: Vino
metadata:
name: vino-with-template
spec:
daemonSetOptions:
namespacedName:
name: libvirt-template
namespace: default
nodeSelector:
matchLabels:
beta.kubernetes.io/os: linux
configuration:
cpuExclude: 0-4,54-60
redfishCredentialSecret:
name: redfishSecret

View File

@ -22,6 +22,12 @@ import (
) )
const ( const (
// VinoLabel is a vino base label
VinoLabel = "vino.airshipit.org"
// VinoLabelDSNameSelector used to label pods in daemon set to avoid collisions
VinoLabelDSNameSelector = VinoLabel + "/" + "cr-name"
// VinoLabelDSNamespaceSelector used to label pods in daemon set to avoid collisions
VinoLabelDSNamespaceSelector = VinoLabel + "/" + "cr-namespace"
// VinoFinalizer constant // VinoFinalizer constant
VinoFinalizer = "vino.airshipit.org" VinoFinalizer = "vino.airshipit.org"
) )

View File

@ -39,13 +39,12 @@ import (
) )
const ( const (
DaemonSetTemplateDefaultDataKey = "template" DaemonSetTemplateDefaultDataKey = "template"
DaemonSetTemplateDefaultName = "vino-daemonset-template"
DaemonSetTemplateDefaultNamespace = "vino-system"
ContainerNameLibvirt = "libvirt" ContainerNameLibvirt = "libvirt"
ConfigMapKeyVinoSpec = "vino-spec" ConfigMapKeyVinoSpec = "vino-spec"
// TODO (kkalynovskyi) remove this, when moving to default libvirt template.
DefaultImageLibvirt = "quay.io/teoyaomiqui/libvirt"
) )
// VinoReconciler reconciles a Vino object // VinoReconciler reconciles a Vino object
@ -225,22 +224,22 @@ func needsUpdate(generated, current *corev1.ConfigMap) bool {
} }
func (r *VinoReconciler) ensureDaemonSet(ctx context.Context, vino *vinov1.Vino) error { func (r *VinoReconciler) ensureDaemonSet(ctx context.Context, vino *vinov1.Vino) error {
ds, err := r.overrideDaemonSet(ctx, vino) ds, err := r.daemonSet(ctx, vino)
if err != nil { if err != nil {
return err return err
} }
if ds == nil {
ds = defaultDaemonSet(vino)
}
r.decorateDaemonSet(ds, vino) r.decorateDaemonSet(ds, vino)
if err := applyRuntimeObject( existDS := &appsv1.DaemonSet{}
ctx, err = r.Get(ctx, types.NamespacedName{Name: ds.Name, Namespace: ds.Namespace}, existDS)
types.NamespacedName{Name: ds.Name, Namespace: ds.Namespace}, switch {
ds, case apierror.IsNotFound(err):
r.Client); err != nil { err = r.Create(ctx, ds)
case err == nil:
err = r.Patch(ctx, ds, client.MergeFrom(existDS))
}
if err != nil {
return err return err
} }
@ -248,8 +247,10 @@ func (r *VinoReconciler) ensureDaemonSet(ctx context.Context, vino *vinov1.Vino)
// controller should watch for changes in daemonset to reconcile if it breaks, and change status // controller should watch for changes in daemonset to reconcile if it breaks, and change status
// of the vino object // of the vino object
// controlleruti.SetControllerReference(vino, ds, r.scheme) // controlleruti.SetControllerReference(vino, ds, r.scheme)
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
defer cancel()
return r.waitDaemonSet(30, ds) return r.waitDaemonSet(ctx, ds)
} }
func (r *VinoReconciler) decorateDaemonSet(ds *appsv1.DaemonSet, vino *vinov1.Vino) { func (r *VinoReconciler) decorateDaemonSet(ds *appsv1.DaemonSet, vino *vinov1.Vino) {
@ -299,14 +300,17 @@ func (r *VinoReconciler) decorateDaemonSet(ds *appsv1.DaemonSet, vino *vinov1.Vi
}) })
} }
} }
// this will help avoid colisions if we have two vino CRs in the same namespace
ds.Spec.Selector.MatchLabels[vinov1.VinoLabelDSNameSelector] = vino.Name
ds.Spec.Template.ObjectMeta.Labels[vinov1.VinoLabelDSNameSelector] = vino.Name
ds.Spec.Selector.MatchLabels[vinov1.VinoLabelDSNamespaceSelector] = vino.Namespace
ds.Spec.Template.ObjectMeta.Labels[vinov1.VinoLabelDSNamespaceSelector] = vino.Namespace
} }
func (r *VinoReconciler) waitDaemonSet(timeout int, ds *appsv1.DaemonSet) error { func (r *VinoReconciler) waitDaemonSet(ctx context.Context, ds *appsv1.DaemonSet) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
defer cancel()
logger := r.Log.WithValues( logger := r.Log.WithValues(
"timeout in seconds", timeout,
"daemonset", ds.Namespace+"/"+ds.Name) "daemonset", ds.Namespace+"/"+ds.Name)
for { for {
select { select {
@ -336,14 +340,15 @@ func (r *VinoReconciler) waitDaemonSet(timeout int, ds *appsv1.DaemonSet) error
} }
} }
func (r *VinoReconciler) overrideDaemonSet(ctx context.Context, vino *vinov1.Vino) (*appsv1.DaemonSet, error) { func (r *VinoReconciler) daemonSet(ctx context.Context, vino *vinov1.Vino) (*appsv1.DaemonSet, error) {
dsTemplate := vino.Spec.DaemonSetOptions.Template dsTemplate := vino.Spec.DaemonSetOptions.Template
logger := r.Log.WithValues("DaemonSetTemplate", dsTemplate) logger := r.Log.WithValues("DaemonSetTemplate", &dsTemplate)
cm := &corev1.ConfigMap{} cm := &corev1.ConfigMap{}
if dsTemplate.Name == "" || dsTemplate.Namespace == "" { if dsTemplate == (vinov1.NamespacedName{}) {
logger.Info("user provided vino DaemonSet template is empty or missing name or namespace") logger.Info("using default configmap for daemonset template")
return nil, nil dsTemplate.Name = DaemonSetTemplateDefaultName
dsTemplate.Namespace = DaemonSetTemplateDefaultNamespace
} }
err := r.Get(ctx, types.NamespacedName{ err := r.Get(ctx, types.NamespacedName{
@ -400,143 +405,6 @@ func (r *VinoReconciler) finalize(ctx context.Context, vino *vinov1.Vino) error
return r.Update(ctx, vino) return r.Update(ctx, vino)
} }
func defaultDaemonSet(vino *vinov1.Vino) (ds *appsv1.DaemonSet) {
libvirtImage := DefaultImageLibvirt
if vino.Spec.DaemonSetOptions.LibvirtImage != "" {
libvirtImage = vino.Spec.DaemonSetOptions.LibvirtImage
}
biDirectional := corev1.MountPropagationBidirectional
return &appsv1.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
Name: vino.Name,
Namespace: vino.Namespace,
},
Spec: appsv1.DaemonSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"vino-id": vino.Name,
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"vino-id": vino.Name,
},
},
Spec: corev1.PodSpec{
HostPID: true,
HostNetwork: true,
HostIPC: true,
Volumes: []corev1.Volume{
{
Name: "cgroup",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{
Path: "/sys/fs/cgroup",
},
},
},
{
Name: "dev",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{
Path: "/dev",
},
},
},
{
Name: "run",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{
Path: "/run",
},
},
},
{
Name: "var-lib-libvirt",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{
Path: "/var/lib/libvirt",
},
},
},
{
Name: "var-lib-libvirt-images",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{
Path: "var-lib-libvirt-images",
},
},
},
{
Name: "logs",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{
Path: "/var/log/libvirt",
},
},
},
{
Name: "libmodules",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{
Path: "/lib/modules",
},
},
},
},
Containers: []corev1.Container{
{
Name: ContainerNameLibvirt,
SecurityContext: &corev1.SecurityContext{
Privileged: &[]bool{true}[0],
RunAsUser: &[]int64{0}[0],
},
Image: libvirtImage,
Command: []string{"/tmp/libvirt.sh"},
VolumeMounts: []corev1.VolumeMount{
{
MountPath: "/lib/modules",
Name: "libmodules",
ReadOnly: true,
},
{
MountPath: "/var/lib/libvirt",
Name: "var-lib-libvirt",
MountPropagation: &biDirectional,
},
{
MountPath: "/var/lib/libvirt/images",
Name: "var-lib-libvirt-images",
},
{
MountPath: "/run",
Name: "run",
},
{
MountPath: "/dev",
Name: "dev",
},
{
MountPath: "/sys/fs/cgroup",
Name: "cgroup",
},
{
MountPath: "/var/log/libvirt",
Name: "logs",
},
},
},
},
},
},
},
}
}
func applyRuntimeObject(ctx context.Context, key client.ObjectKey, obj client.Object, c client.Client) error { func applyRuntimeObject(ctx context.Context, key client.ObjectKey, obj client.Object, c client.Client) error {
getObj := obj getObj := obj
switch err := c.Get(ctx, key, getObj); { switch err := c.Get(ctx, key, getObj); {

View File

@ -12,14 +12,14 @@ function vinoDebugInfo () {
exit 1 exit 1
} }
kubectl apply -f config/samples/daemonset_template.yaml -f config/samples/vino_cr_daemonset_template.yaml kubectl apply -f config/samples/vino_cr.yaml
# Remove logs collection from here, when we will have zuul collect logs job # Remove logs collection from here, when we will have zuul collect logs job
if ! kubectl wait --for=condition=Ready vino vino-with-template --timeout=180s; then if ! kubectl wait --for=condition=Ready vino vino-test-cr --timeout=180s; then
vinoDebugInfo vinoDebugInfo
fi fi
# no need to collect logs on fail, since they are already collected before # no need to collect logs on fail, since they are already collected before
if ! kubectl wait --for=condition=Ready pods -l 'vino-test=cr-with-ds-template' --timeout=5s; then if ! kubectl wait --for=condition=Ready pods -l 'vino-role=vino-builder' --timeout=5s; then
vinoDebugInfo vinoDebugInfo
fi fi