Skip to content

Commit

Permalink
WIP: use network-to-resourceName map
Browse files Browse the repository at this point in the history
TODO: grant access to networks to virt-controller.
TODO: keep it backwards compatible with previous DP version.
TODO: handle errors from network client.
TODO: fix tests.

Consider:
- cache networks.

This is to align kubevirt with support for multiple allocation pools in SR-IOV
DP: k8snetworkplumbingwg/sriov-network-device-plugin#26
  • Loading branch information
booxter committed Nov 12, 2018
1 parent 8e18ed6 commit 01e0abb
Show file tree
Hide file tree
Showing 15 changed files with 201 additions and 86 deletions.
17 changes: 14 additions & 3 deletions pkg/kubecli/generated_mock_kubevirt.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
time "time"

gomock "github.com/golang/mock/gomock"
versioned "github.com/phoracek/network-attachment-definition-client/pkg/client/clientset/versioned"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
discovery "k8s.io/client-go/discovery"
Expand Down Expand Up @@ -41,7 +42,7 @@ import (
v1beta110 "k8s.io/client-go/kubernetes/typed/storage/v1beta1"
rest "k8s.io/client-go/rest"

versioned "kubevirt.io/containerized-data-importer/pkg/client/clientset/versioned"
versioned0 "kubevirt.io/containerized-data-importer/pkg/client/clientset/versioned"
v19 "kubevirt.io/kubevirt/pkg/api/v1"
)

Expand Down Expand Up @@ -126,16 +127,26 @@ func (_mr *_MockKubevirtClientRecorder) RestClient() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "RestClient")
}

func (_m *MockKubevirtClient) CdiClient() versioned.Interface {
func (_m *MockKubevirtClient) CdiClient() versioned0.Interface {
ret := _m.ctrl.Call(_m, "CdiClient")
ret0, _ := ret[0].(versioned.Interface)
ret0, _ := ret[0].(versioned0.Interface)
return ret0
}

func (_mr *_MockKubevirtClientRecorder) CdiClient() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "CdiClient")
}

func (_m *MockKubevirtClient) NetworkClient() versioned.Interface {
ret := _m.ctrl.Call(_m, "NetworkClient")
ret0, _ := ret[0].(versioned.Interface)
return ret0
}

func (_mr *_MockKubevirtClientRecorder) NetworkClient() *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "NetworkClient")
}

func (_m *MockKubevirtClient) Discovery() discovery.DiscoveryInterface {
ret := _m.ctrl.Call(_m, "Discovery")
ret0, _ := ret[0].(discovery.DiscoveryInterface)
Expand Down
14 changes: 14 additions & 0 deletions pkg/kubecli/kubecli.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import (
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"

networkclient "github.com/phoracek/network-attachment-definition-client/pkg/client/clientset/versioned"

cdiclient "kubevirt.io/containerized-data-importer/pkg/client/clientset/versioned"
"kubevirt.io/kubevirt/pkg/api/v1"
)
Expand Down Expand Up @@ -73,12 +75,18 @@ func GetKubevirtSubresourceClientFromFlags(master string, kubeconfig string) (Ku
return nil, err
}

networkClient, err := networkclient.NewForConfig(config)
if err != nil {
return nil, err
}

return &kubevirt{
master,
kubeconfig,
restClient,
config,
cdiClient,
networkClient,
coreClient,
}, nil
}
Expand Down Expand Up @@ -177,12 +185,18 @@ func GetKubevirtClientFromRESTConfig(config *rest.Config) (KubevirtClient, error
return nil, err
}

networkClient, err := networkclient.NewForConfig(config)
if err != nil {
return nil, err
}

return &kubevirt{
master,
kubeconfig,
restClient,
config,
cdiClient,
networkClient,
coreClient,
}, nil
}
Expand Down
18 changes: 13 additions & 5 deletions pkg/kubecli/kubevirt.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"

networkclient "github.com/phoracek/network-attachment-definition-client/pkg/client/clientset/versioned"

cdiclient "kubevirt.io/containerized-data-importer/pkg/client/clientset/versioned"
"kubevirt.io/kubevirt/pkg/api/v1"
)
Expand All @@ -46,22 +48,28 @@ type KubevirtClient interface {
ServerVersion() *ServerVersion
RestClient() *rest.RESTClient
CdiClient() cdiclient.Interface
NetworkClient() networkclient.Interface
kubernetes.Interface
}

type kubevirt struct {
master string
kubeconfig string
restClient *rest.RESTClient
config *rest.Config
cdiClient *cdiclient.Clientset
master string
kubeconfig string
restClient *rest.RESTClient
config *rest.Config
cdiClient *cdiclient.Clientset
networkClient *networkclient.Clientset
*kubernetes.Clientset
}

func (k kubevirt) CdiClient() cdiclient.Interface {
return k.cdiClient
}

func (k kubevirt) NetworkClient() networkclient.Interface {
return k.networkClient
}

func (k kubevirt) RestClient() *rest.RESTClient {
return k.restClient
}
Expand Down
9 changes: 8 additions & 1 deletion pkg/virt-api/rest/subresource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/ghttp"
networkv1 "github.com/phoracek/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
k8sv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/client-go/tools/cache"
Expand Down Expand Up @@ -53,7 +54,7 @@ var _ = Describe("VirtualMachineInstance Subresources", func() {
vmi := v1.NewMinimalVMI("testvmi")
vmi.Status.Phase = v1.Running
vmi.ObjectMeta.SetUID(uuid.NewUUID())
templateService := services.NewTemplateService("whatever", "whatever", "whatever", "whatever", configCache, pvcCache)
templateService := services.NewTemplateService("whatever", "whatever", "whatever", "whatever", configCache, pvcCache, app.VirtCli)

pod, err := templateService.RenderLaunchManifest(vmi)
Expect(err).ToNot(HaveOccurred())
Expand All @@ -66,11 +67,17 @@ var _ = Describe("VirtualMachineInstance Subresources", func() {
podList.Items = []k8sv1.Pod{}
podList.Items = append(podList.Items, *pod)

networkList := networkv1.NetworkAttachmentDefinitionList{}

server.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/api/v1/namespaces/default/pods"),
ghttp.RespondWithJSONEncoded(http.StatusOK, podList),
),
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/apis/k8s.cni.cncf.io/v1/namespaces/default/network-attachment-definitions"),
ghttp.RespondWithJSONEncoded(http.StatusOK, networkList),
),
)

podName, httpStatusCode, err := app.remoteExecInfo(vmi)
Expand Down
40 changes: 36 additions & 4 deletions pkg/virt-controller/services/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"

networkv1 "github.com/phoracek/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"

"kubevirt.io/kubevirt/pkg/api/v1"
"kubevirt.io/kubevirt/pkg/config"
"kubevirt.io/kubevirt/pkg/hooks"
"kubevirt.io/kubevirt/pkg/kubecli"
"kubevirt.io/kubevirt/pkg/log"
"kubevirt.io/kubevirt/pkg/precond"
"kubevirt.io/kubevirt/pkg/registry-disk"
Expand Down Expand Up @@ -62,6 +65,7 @@ type templateService struct {
imagePullSecret string
configMapStore cache.Store
persistentVolumeClaimStore cache.Store
virtClient kubecli.KubevirtClient
}

type PvcNotFoundError error
Expand Down Expand Up @@ -571,6 +575,14 @@ func (t *templateService) RenderLaunchManifest(vmi *v1.VirtualMachineInstance) (
podLabels[v1.AppLabel] = "virt-launcher"
podLabels[v1.CreatedByLabel] = string(vmi.UID)

cniNetworks, cniAnnotation, networkToResourceMap := getCniInterfaceList(t.virtClient, vmi)
for networkName, resourceName := range networkToResourceMap {
// TODO: move it to a separate helper
// TODO: ideally, this mapping would be provided to us by other components (Multus?)
varName := fmt.Sprintf("KUBEVIRT_RESOURCE_NAME_%s", networkName)
container.Env = append(container.Env, k8sv1.EnvVar{Name: varName, Value: resourceName})
}

containers = append(containers, container)

for i, requestedHookSidecar := range requestedHookSidecarList {
Expand Down Expand Up @@ -603,7 +615,6 @@ func (t *templateService) RenderLaunchManifest(vmi *v1.VirtualMachineInstance) (
v1.OwnedByAnnotation: "virt-controller",
}

cniNetworks, cniAnnotation := getCniInterfaceList(vmi)
if len(cniNetworks) > 0 {
annotationsList[cniAnnotation] = cniNetworks
}
Expand Down Expand Up @@ -781,17 +792,36 @@ func getPortsFromVMI(vmi *v1.VirtualMachineInstance) []k8sv1.ContainerPort {
return ports
}

func getCniInterfaceList(vmi *v1.VirtualMachineInstance) (ifaceListString string, cniAnnotation string) {
func getResourceNameForNetwork(networks *networkv1.NetworkAttachmentDefinitionList, networkName string) string {
logger := log.DefaultLogger()
logger.Errorf("number of networks: %d", len(networks.Items))
for _, network := range networks.Items {
if network.Name == networkName {
return network.Annotations["k8s.v1.cni.cncf.io/resourceName"]
}
}
return ""
}

func getCniInterfaceList(virtClient kubecli.KubevirtClient, vmi *v1.VirtualMachineInstance) (ifaceListString string, cniAnnotation string, networkToResourceMap map[string]string) {
ifaceList := make([]string, 0)
networkToResourceMap = make(map[string]string)
networks, err := virtClient.NetworkClient().K8sCniCncfIo().NetworkAttachmentDefinitions("default").List(metav1.ListOptions{})
if err != nil {
logger := log.DefaultLogger()
logger.Errorf("error fetching networks: %e", err)
}

for _, network := range vmi.Spec.Networks {
// set the type for the first network
// all other networks must have same type
if network.Multus != nil {
ifaceList = append(ifaceList, network.Multus.NetworkName)
networkName := network.Multus.NetworkName
ifaceList = append(ifaceList, networkName)
if cniAnnotation == "" {
cniAnnotation = "k8s.v1.cni.cncf.io/networks"
}
networkToResourceMap[networkName] = getResourceNameForNetwork(networks, networkName)
} else if network.Genie != nil {
ifaceList = append(ifaceList, network.Genie.NetworkName)
if cniAnnotation == "" {
Expand All @@ -809,7 +839,8 @@ func NewTemplateService(launcherImage string,
ephemeralDiskDir string,
imagePullSecret string,
configMapCache cache.Store,
persistentVolumeClaimCache cache.Store) TemplateService {
persistentVolumeClaimCache cache.Store,
virtClient kubecli.KubevirtClient) TemplateService {

precond.MustNotBeEmpty(launcherImage)
svc := templateService{
Expand All @@ -819,6 +850,7 @@ func NewTemplateService(launcherImage string,
imagePullSecret: imagePullSecret,
configMapStore: configMapCache,
persistentVolumeClaimStore: persistentVolumeClaimCache,
virtClient: virtClient,
}
return &svc
}
17 changes: 16 additions & 1 deletion pkg/virt-controller/services/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ import (
"testing"
"time"

"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
fakenetworkclient "github.com/phoracek/network-attachment-definition-client/pkg/client/clientset/versioned/fake"
kubev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -36,6 +38,7 @@ import (

"kubevirt.io/kubevirt/pkg/api/v1"
"kubevirt.io/kubevirt/pkg/hooks"
"kubevirt.io/kubevirt/pkg/kubecli"
"kubevirt.io/kubevirt/pkg/log"
. "kubevirt.io/kubevirt/pkg/virt-controller/services"
)
Expand All @@ -47,12 +50,24 @@ var _ = Describe("Template", func() {
log.Log.SetIOWriter(GinkgoWriter)
configCache := cache.NewIndexer(cache.DeletionHandlingMetaNamespaceKeyFunc, nil)
pvcCache := cache.NewIndexer(cache.DeletionHandlingMetaNamespaceKeyFunc, nil)

ctrl := gomock.NewController(GinkgoT())
virtClient := kubecli.NewMockKubevirtClient(ctrl)
var networkClient *fakenetworkclient.Clientset

svc := NewTemplateService("kubevirt/virt-launcher",
"/var/run/kubevirt",
"/var/run/kubevirt-ephemeral-disks",
"pull-secret-1",
configCache,
pvcCache)
pvcCache,
virtClient)

BeforeEach(func() {
// Set up mock client
networkClient = fakenetworkclient.NewSimpleClientset()
virtClient.EXPECT().NetworkClient().Return(networkClient).AnyTimes()
})

Describe("Rendering", func() {
Context("launch template with correct parameters", func() {
Expand Down
8 changes: 7 additions & 1 deletion pkg/virt-controller/watch/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,11 @@ func (vca *VirtControllerApp) getNewRecorder(namespace string, componentName str
func (vca *VirtControllerApp) initCommon() {
var err error

virtClient, err := kubecli.GetKubevirtClient()
if err != nil {
golog.Fatal(err)
}

registrydisk.SetLocalDirectory(vca.ephemeralDiskDir + "/registry-disk-data")
if err != nil {
golog.Fatal(err)
Expand All @@ -273,7 +278,8 @@ func (vca *VirtControllerApp) initCommon() {
vca.ephemeralDiskDir,
vca.imagePullSecret,
vca.configMapCache,
vca.persistentVolumeClaimCache)
vca.persistentVolumeClaimCache,
virtClient)

vca.vmiController = NewVMIController(vca.templateService, vca.vmiInformer, vca.podInformer, vca.vmiRecorder, vca.clientSet, vca.configMapInformer, vca.dataVolumeInformer)
recorder := vca.getNewRecorder(k8sv1.NamespaceAll, "node-controller")
Expand Down
6 changes: 5 additions & 1 deletion pkg/virt-controller/watch/migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
. "github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
fakenetworkclient "github.com/phoracek/network-attachment-definition-client/pkg/client/clientset/versioned/fake"
k8sv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand Down Expand Up @@ -60,6 +61,7 @@ var _ = Describe("Migration watcher", func() {
var podFeeder *testutils.PodFeeder
var virtClient *kubecli.MockKubevirtClient
var kubeClient *fake.Clientset
var networkClient *fakenetworkclient.Clientset
var configMapInformer cache.SharedIndexInformer
var pvcInformer cache.SharedIndexInformer

Expand Down Expand Up @@ -163,7 +165,7 @@ var _ = Describe("Migration watcher", func() {
pvcInformer, _ = testutils.NewFakeInformerFor(&k8sv1.PersistentVolumeClaim{})

controller = NewMigrationController(
services.NewTemplateService("a", "b", "c", "d", configMapInformer.GetStore(), pvcInformer.GetStore()),
services.NewTemplateService("a", "b", "c", "d", configMapInformer.GetStore(), pvcInformer.GetStore(), virtClient),
vmiInformer,
podInformer,
migrationInformer,
Expand All @@ -179,6 +181,8 @@ var _ = Describe("Migration watcher", func() {
virtClient.EXPECT().VirtualMachineInstanceMigration(k8sv1.NamespaceDefault).Return(migrationInterface).AnyTimes()
virtClient.EXPECT().VirtualMachineInstance(k8sv1.NamespaceDefault).Return(vmiInterface).AnyTimes()
virtClient.EXPECT().CoreV1().Return(kubeClient.CoreV1()).AnyTimes()
networkClient = fakenetworkclient.NewSimpleClientset()
virtClient.EXPECT().NetworkClient().Return(networkClient).AnyTimes()

// Make sure that all unexpected calls to kubeClient will fail
kubeClient.Fake.PrependReactor("*", "*", func(action testing.Action) (handled bool, obj runtime.Object, err error) {
Expand Down
Loading

0 comments on commit 01e0abb

Please sign in to comment.