Adding ability to override VM interface

Change-Id: I35cc4368c4f7a0e0642c16d089bda910412a832c
This commit is contained in:
Kostiantyn Kalynovskyi 2021-01-29 21:23:29 +00:00
parent 0e2baf38e4
commit bccf6d68c5
7 changed files with 234 additions and 6 deletions

View File

@ -88,6 +88,10 @@ spec:
type: object
subnet:
type: string
vmInterfaceName:
description: VMinterfaceName defines the interface name to be used
as bridge for virtual machines
type: string
type: object
nodeSelector:
description: Define nodelabel parameters

View File

@ -340,6 +340,17 @@ VMRoutes
<td>
</td>
</tr>
<tr>
<td>
<code>vmInterfaceName</code><br>
<em>
string
</em>
</td>
<td>
<p>VMinterfaceName defines the interface name to be used as bridge for virtual machines</p>
</td>
</tr>
</tbody>
</table>
</div>

1
go.mod
View File

@ -5,6 +5,7 @@ go 1.13
require (
github.com/evanphx/json-patch v4.9.0+incompatible
github.com/go-logr/logr v0.3.0
github.com/go-logr/zapr v0.2.0
github.com/metal3-io/baremetal-operator v0.0.0-20210111093319-93a6fd209b9a
github.com/onsi/ginkgo v1.14.2
github.com/onsi/gomega v1.10.2

View File

@ -31,6 +31,8 @@ const (
VinoLabelDSNamespaceSelector = VinoLabel + "/" + "cr-namespace"
// VinoFinalizer constant
VinoFinalizer = "vino.airshipit.org"
// EnvVarVMInterfaceName environment variable that is used to find VM interface to use for vms
EnvVarVMInterfaceName = "VM_BRIDGE_INTERFACE"
)
// VinoSpec defines the desired state of Vino
@ -40,7 +42,7 @@ type VinoSpec struct {
// Define CPU configuration
CPUConfiguration *CPUConfiguration `json:"configuration,omitempty"`
// Define network parameters
Network *Network `json:"networks,omitempty"`
Network Network `json:"networks,omitempty"`
// Define node details
Node []NodeSet `json:"nodes,omitempty"`
// DaemonSetOptions defines how vino will spawn daemonset on nodes
@ -68,6 +70,8 @@ type Network struct {
AllocationStop string `json:"allocationStop,omitempty"`
DNSServers []string `json:"dns_servers,omitempty"`
Routes *VMRoutes `json:"routes,omitempty"`
// VMinterfaceName defines the interface name to be used as bridge for virtual machines
VMInterfaceName string `json:"vmInterfaceName,omitempty"`
}
// VMRoutes defined

View File

@ -315,11 +315,7 @@ func (in *VinoSpec) DeepCopyInto(out *VinoSpec) {
*out = new(CPUConfiguration)
**out = **in
}
if in.Network != nil {
in, out := &in.Network, &out.Network
*out = new(Network)
(*in).DeepCopyInto(*out)
}
in.Network.DeepCopyInto(&out.Network)
if in.Node != nil {
in, out := &in.Node, &out.Node
*out = make([]NodeSet, len(*in))

View File

@ -514,6 +514,12 @@ func (r *VinoReconciler) decorateDaemonSet(ctx context.Context, ds *appsv1.Daemo
}
}
// TODO develop logic to derive all required ENV variables from VINO CR, and pass them
// to setENV function instead
if vino.Spec.Network.VMInterfaceName != "" {
setEnv(ctx, ds, vino)
}
// 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
@ -522,6 +528,32 @@ func (r *VinoReconciler) decorateDaemonSet(ctx context.Context, ds *appsv1.Daemo
ds.Spec.Template.ObjectMeta.Labels[vinov1.VinoLabelDSNamespaceSelector] = vino.Namespace
}
func setEnv(ctx context.Context, ds *appsv1.DaemonSet, vino *vinov1.Vino) {
for i, c := range ds.Spec.Template.Spec.Containers {
var set bool
for j, envVar := range c.Env {
if envVar.Name == vinov1.EnvVarVMInterfaceName {
logr.FromContext(ctx).Info("found env variable with vm interface name on daemonset template, overriding it",
"vino instance", vino.Namespace+"/"+vino.Name,
"container name", c.Name,
"value", envVar.Value,
)
ds.Spec.Template.Spec.Containers[i].Env[j].Value = vino.Spec.Network.VMInterfaceName
set = true
break
}
}
if !set {
ds.Spec.Template.Spec.Containers[i].Env = append(
ds.Spec.Template.Spec.Containers[i].Env, corev1.EnvVar{
Name: vinov1.EnvVarVMInterfaceName,
Value: vino.Spec.Network.VMInterfaceName,
},
)
}
}
}
func (r *VinoReconciler) waitDaemonSet(ctx context.Context, ds *appsv1.DaemonSet) error {
logger := logr.FromContext(ctx).WithValues(
"daemonset", ds.Namespace+"/"+ds.Name)

View File

@ -0,0 +1,180 @@
package controllers
import (
"context"
"github.com/go-logr/logr"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
vinov1 "vino/pkg/api/v1"
)
func testDS() *appsv1.DaemonSet {
return &appsv1.DaemonSet{Spec: appsv1.DaemonSetSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{}}}}}
}
func testVINO() *vinov1.Vino {
return &vinov1.Vino{
ObjectMeta: v1.ObjectMeta{},
Spec: vinov1.VinoSpec{
Network: vinov1.Network{}}}
}
var _ = Describe("Test Setting Env variables", func() {
Context("when daemonset is created", func() {
l := logr.Discard()
ctx := logr.NewContext(context.Background(), l)
Context("no containers defined in damonset", func() {
It("does nothing", func() {
ds := testDS()
setEnv(ctx, ds, testVINO())
Expect(ds.Spec.Template.Spec.Containers).To(HaveLen(0))
})
})
Context("when daemonset has containers", func() {
It("sets env interface variable to every container", func() {
vino := testVINO()
ifName := "eth0"
vino.Spec.Network.VMInterfaceName = ifName
ds := testDS()
ds.Spec.Template.Spec.Containers = make([]corev1.Container, 3)
setEnv(ctx, ds, vino)
for _, container := range ds.Spec.Template.Spec.Containers {
Expect(container.Env).To(HaveLen(1))
Expect(container.Env[0].Name).To(Equal(vinov1.EnvVarVMInterfaceName))
Expect(container.Env[0].Value).To(Equal(ifName))
}
})
})
Context("when daemonset has container with wrong env var values", func() {
It("overrides that variable in the container", func() {
vino := testVINO()
ifName := "eth0"
vino.Spec.Network.VMInterfaceName = ifName
ds := testDS()
ds.Spec.Template.Spec.Containers = []corev1.Container{
{
Env: []corev1.EnvVar{
{
Name: vinov1.EnvVarVMInterfaceName,
Value: "wrong-value",
},
},
},
}
setEnv(ctx, ds, vino)
Expect(ds.Spec.Template.Spec.Containers).To(HaveLen(1))
container := ds.Spec.Template.Spec.Containers[0]
Expect(container.Env).To(HaveLen(1))
Expect(container.Env[0].Name).To(Equal(vinov1.EnvVarVMInterfaceName))
Expect(container.Env[0].Value).To(Equal(ifName))
})
})
Context("when daemonset containers don't have required variable", func() {
It("overrides that variable in the container", func() {
vino := testVINO()
ifName := "eth0"
vino.Spec.Network.VMInterfaceName = ifName
ds := testDS()
ds.Spec.Template.Spec.Containers = []corev1.Container{
{
Env: []corev1.EnvVar{
{
Name: "bar",
Value: "wrong-value",
},
{
Name: "foo",
Value: "wrong-value",
},
},
},
{
Env: []corev1.EnvVar{
{
Name: "foo",
Value: "wrong-value",
},
{
Name: "bar",
Value: "wrong-value",
},
},
},
}
setEnv(ctx, ds, vino)
Expect(ds.Spec.Template.Spec.Containers).To(HaveLen(2))
for _, container := range ds.Spec.Template.Spec.Containers {
Expect(container.Env).To(HaveLen(3))
for i, env := range container.Env {
if i == len(container.Env)-1 {
// only last env holds the correct values
Expect(env.Name).To(Equal(vinov1.EnvVarVMInterfaceName))
Expect(env.Value).To(Equal(ifName))
} else {
Expect(env.Name).NotTo(Equal(vinov1.EnvVarVMInterfaceName))
Expect(env.Value).NotTo(Equal(ifName))
}
}
}
})
})
Context("when daemonset container has many variables", func() {
It("it sets required variable only single time", func() {
vino := testVINO()
ifName := "eth0"
vino.Spec.Network.VMInterfaceName = ifName
ds := testDS()
ds.Spec.Template.Spec.Containers = []corev1.Container{
{
Env: []corev1.EnvVar{
{
Name: "foo",
Value: "wrong-value",
},
{
Name: vinov1.EnvVarVMInterfaceName,
Value: "wrong-value",
},
{
Name: "bar",
Value: "wrong-value",
},
},
},
}
setEnv(ctx, ds, vino)
Expect(ds.Spec.Template.Spec.Containers).To(HaveLen(1))
container := ds.Spec.Template.Spec.Containers[0]
Expect(container.Env).To(HaveLen(3))
for i, env := range container.Env {
if i == 1 {
// only env var with index 1 holds the correct values
Expect(env.Name).To(Equal(vinov1.EnvVarVMInterfaceName))
Expect(env.Value).To(Equal(ifName))
} else {
Expect(env.Name).NotTo(Equal(vinov1.EnvVarVMInterfaceName))
Expect(env.Value).NotTo(Equal(ifName))
}
}
})
})
})
})