Unverified Commit 875ba289 authored by Darren Shepherd's avatar Darren Shepherd Committed by GitHub

Merge pull request #375 from galal-hussein/schedule_svclb

Add node selector to service loadbalancer controller
parents 50f405dd f293e146
...@@ -21,13 +21,16 @@ import ( ...@@ -21,13 +21,16 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
v1getter "k8s.io/client-go/kubernetes/typed/apps/v1"
coregetter "k8s.io/client-go/kubernetes/typed/core/v1" coregetter "k8s.io/client-go/kubernetes/typed/core/v1"
) )
const ( const (
image = "rancher/klipper-lb:v0.1.1" image = "rancher/klipper-lb:v0.1.1"
svcNameLabel = "svccontroller.k3s.cattle.io/svcname" svcNameLabel = "svccontroller.k3s.cattle.io/svcname"
Ready = condition.Cond("Ready") daemonsetNodeLabel = "svccontroller.k3s.cattle.io/enablelb"
nodeSelectorLabel = "svccontroller.k3s.cattle.io/nodeselector"
Ready = condition.Cond("Ready")
) )
var ( var (
...@@ -44,12 +47,14 @@ func Register(ctx context.Context, kubernetes kubernetes.Interface, enabled, roo ...@@ -44,12 +47,14 @@ func Register(ctx context.Context, kubernetes kubernetes.Interface, enabled, roo
nodeCache: clients.Node.Cache(), nodeCache: clients.Node.Cache(),
podCache: clients.Pod.Cache(), podCache: clients.Pod.Cache(),
processor: objectset.NewProcessor("svccontroller"). processor: objectset.NewProcessor("svccontroller").
Client(appClients.Deployment), Client(appClients.DaemonSet),
serviceCache: clients.Service.Cache(), serviceCache: clients.Service.Cache(),
services: kubernetes.CoreV1(), services: kubernetes.CoreV1(),
daemonsets: kubernetes.AppsV1(),
} }
clients.Service.OnChange(ctx, "svccontroller", h.onChange) clients.Service.OnChange(ctx, "svccontroller", h.onChangeService)
clients.Node.OnChange(ctx, "svccontroller", h.onChangeNode)
changeset.Watch(ctx, "svccontroller-watcher", changeset.Watch(ctx, "svccontroller-watcher",
h.onResourceChange, h.onResourceChange,
clients.Service, clients.Service,
...@@ -67,6 +72,7 @@ type handler struct { ...@@ -67,6 +72,7 @@ type handler struct {
processor *objectset.Processor processor *objectset.Processor
serviceCache coreclient.ServiceClientCache serviceCache coreclient.ServiceClientCache
services coregetter.ServicesGetter services coregetter.ServicesGetter
daemonsets v1getter.DaemonSetsGetter
} }
func (h *handler) onResourceChange(name, namespace string, obj runtime.Object) ([]changeset.Key, error) { func (h *handler) onResourceChange(name, namespace string, obj runtime.Object) ([]changeset.Key, error) {
...@@ -101,7 +107,7 @@ func (h *handler) onResourceChange(name, namespace string, obj runtime.Object) ( ...@@ -101,7 +107,7 @@ func (h *handler) onResourceChange(name, namespace string, obj runtime.Object) (
}, nil }, nil
} }
func (h *handler) onChange(svc *core.Service) (runtime.Object, error) { func (h *handler) onChangeService(svc *core.Service) (runtime.Object, error) {
if svc.Spec.Type != core.ServiceTypeLoadBalancer || svc.Spec.ClusterIP == "" || if svc.Spec.Type != core.ServiceTypeLoadBalancer || svc.Spec.ClusterIP == "" ||
svc.Spec.ClusterIP == "None" { svc.Spec.ClusterIP == "None" {
return svc, nil return svc, nil
...@@ -116,6 +122,18 @@ func (h *handler) onChange(svc *core.Service) (runtime.Object, error) { ...@@ -116,6 +122,18 @@ func (h *handler) onChange(svc *core.Service) (runtime.Object, error) {
return nil, err return nil, err
} }
func (h *handler) onChangeNode(node *core.Node) (runtime.Object, error) {
if _, ok := node.Labels[daemonsetNodeLabel]; !ok {
return node, nil
}
if err := h.updateDaemonSets(); err != nil {
return node, err
}
return node, nil
}
func (h *handler) updateService(svc *core.Service) (runtime.Object, error) { func (h *handler) updateService(svc *core.Service) (runtime.Object, error) {
pods, err := h.podCache.List(svc.Namespace, labels.SelectorFromSet(map[string]string{ pods, err := h.podCache.List(svc.Namespace, labels.SelectorFromSet(map[string]string{
svcNameLabel: svc.Name, svcNameLabel: svc.Name,
...@@ -200,43 +218,27 @@ func (h *handler) podIPs(pods []*core.Pod) ([]string, error) { ...@@ -200,43 +218,27 @@ func (h *handler) podIPs(pods []*core.Pod) ([]string, error) {
} }
func (h *handler) deployPod(svc *core.Service) error { func (h *handler) deployPod(svc *core.Service) error {
objs := objectset.NewObjectSet() objs := objectset.NewObjectSet()
if !h.enabled { if !h.enabled {
return h.processor.NewDesiredSet(svc, objs).Apply() return h.processor.NewDesiredSet(svc, objs).Apply()
} }
dep, err := h.newDeployment(svc) ds, err := h.newDaemonSet(svc)
if err != nil { if err != nil {
return err return err
} }
if dep != nil { if ds != nil {
objs.Add(dep) objs.Add(ds)
} }
return h.processor.NewDesiredSet(svc, objs).Apply() return h.processor.NewDesiredSet(svc, objs).Apply()
} }
func (h *handler) newDeployment(svc *core.Service) (*apps.Deployment, error) { func (h *handler) newDaemonSet(svc *core.Service) (*apps.DaemonSet, error) {
name := fmt.Sprintf("svclb-%s", svc.Name) name := fmt.Sprintf("svclb-%s", svc.Name)
zeroInt := intstr.FromInt(0)
oneInt := intstr.FromInt(1) oneInt := intstr.FromInt(1)
replicas := int32(0)
nodes, err := h.nodeCache.List("", labels.Everything())
if err != nil {
return nil, err
}
for _, node := range nodes { ds := &apps.DaemonSet{
if Ready.IsTrue(node) {
replicas++
}
if replicas >= 2 {
break
}
}
dep := &apps.Deployment{
ObjectMeta: meta.ObjectMeta{ ObjectMeta: meta.ObjectMeta{
Name: name, Name: name,
Namespace: svc.Namespace, Namespace: svc.Namespace,
...@@ -249,13 +251,15 @@ func (h *handler) newDeployment(svc *core.Service) (*apps.Deployment, error) { ...@@ -249,13 +251,15 @@ func (h *handler) newDeployment(svc *core.Service) (*apps.Deployment, error) {
Controller: &trueVal, Controller: &trueVal,
}, },
}, },
Labels: map[string]string{
nodeSelectorLabel: "false",
},
}, },
TypeMeta: meta.TypeMeta{ TypeMeta: meta.TypeMeta{
Kind: "Deployment", Kind: "DaemonSet",
APIVersion: "apps/v1", APIVersion: "apps/v1",
}, },
Spec: apps.DeploymentSpec{ Spec: apps.DaemonSetSpec{
Replicas: &replicas,
Selector: &meta.LabelSelector{ Selector: &meta.LabelSelector{
MatchLabels: map[string]string{ MatchLabels: map[string]string{
"app": name, "app": name,
...@@ -269,10 +273,9 @@ func (h *handler) newDeployment(svc *core.Service) (*apps.Deployment, error) { ...@@ -269,10 +273,9 @@ func (h *handler) newDeployment(svc *core.Service) (*apps.Deployment, error) {
}, },
}, },
}, },
Strategy: apps.DeploymentStrategy{ UpdateStrategy: apps.DaemonSetUpdateStrategy{
Type: apps.RollingUpdateDeploymentStrategyType, Type: apps.RollingUpdateDaemonSetStrategyType,
RollingUpdate: &apps.RollingUpdateDeployment{ RollingUpdate: &apps.RollingUpdateDaemonSet{
MaxSurge: &zeroInt,
MaxUnavailable: &oneInt, MaxUnavailable: &oneInt,
}, },
}, },
...@@ -319,8 +322,44 @@ func (h *handler) newDeployment(svc *core.Service) (*apps.Deployment, error) { ...@@ -319,8 +322,44 @@ func (h *handler) newDeployment(svc *core.Service) (*apps.Deployment, error) {
}, },
} }
dep.Spec.Template.Spec.Containers = append(dep.Spec.Template.Spec.Containers, container) ds.Spec.Template.Spec.Containers = append(ds.Spec.Template.Spec.Containers, container)
}
// Add node selector only if label "svccontroller.k3s.cattle.io/enablelb" exists on the nodes
selector, err := labels.Parse(daemonsetNodeLabel)
if err != nil {
return nil, err
}
nodesWithLabel, err := h.nodeCache.List("", selector)
if err != nil {
return nil, err
}
if len(nodesWithLabel) > 0 {
ds.Spec.Template.Spec.NodeSelector = map[string]string{
daemonsetNodeLabel: "true",
}
ds.Labels[nodeSelectorLabel] = "true"
}
return ds, nil
}
func (h *handler) updateDaemonSets() error {
daemonsets, err := h.daemonsets.DaemonSets("").List(meta.ListOptions{
LabelSelector: nodeSelectorLabel + "=false",
})
if err != nil {
return err
}
for _, ds := range daemonsets.Items {
ds.Spec.Template.Spec.NodeSelector = map[string]string{
daemonsetNodeLabel: "true",
}
ds.Labels[nodeSelectorLabel] = "true"
if _, err := h.daemonsets.DaemonSets(ds.Namespace).Update(&ds); err != nil {
return err
}
} }
return dep, nil return nil
} }
...@@ -6,54 +6,54 @@ import ( ...@@ -6,54 +6,54 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
) )
type DeploymentLifecycle interface { type DaemonSetLifecycle interface {
Create(obj *v1.Deployment) (runtime.Object, error) Create(obj *v1.DaemonSet) (runtime.Object, error)
Remove(obj *v1.Deployment) (runtime.Object, error) Remove(obj *v1.DaemonSet) (runtime.Object, error)
Updated(obj *v1.Deployment) (runtime.Object, error) Updated(obj *v1.DaemonSet) (runtime.Object, error)
} }
type deploymentLifecycleAdapter struct { type daemonSetLifecycleAdapter struct {
lifecycle DeploymentLifecycle lifecycle DaemonSetLifecycle
} }
func (w *deploymentLifecycleAdapter) HasCreate() bool { func (w *daemonSetLifecycleAdapter) HasCreate() bool {
o, ok := w.lifecycle.(lifecycle.ObjectLifecycleCondition) o, ok := w.lifecycle.(lifecycle.ObjectLifecycleCondition)
return !ok || o.HasCreate() return !ok || o.HasCreate()
} }
func (w *deploymentLifecycleAdapter) HasFinalize() bool { func (w *daemonSetLifecycleAdapter) HasFinalize() bool {
o, ok := w.lifecycle.(lifecycle.ObjectLifecycleCondition) o, ok := w.lifecycle.(lifecycle.ObjectLifecycleCondition)
return !ok || o.HasFinalize() return !ok || o.HasFinalize()
} }
func (w *deploymentLifecycleAdapter) Create(obj runtime.Object) (runtime.Object, error) { func (w *daemonSetLifecycleAdapter) Create(obj runtime.Object) (runtime.Object, error) {
o, err := w.lifecycle.Create(obj.(*v1.Deployment)) o, err := w.lifecycle.Create(obj.(*v1.DaemonSet))
if o == nil { if o == nil {
return nil, err return nil, err
} }
return o, err return o, err
} }
func (w *deploymentLifecycleAdapter) Finalize(obj runtime.Object) (runtime.Object, error) { func (w *daemonSetLifecycleAdapter) Finalize(obj runtime.Object) (runtime.Object, error) {
o, err := w.lifecycle.Remove(obj.(*v1.Deployment)) o, err := w.lifecycle.Remove(obj.(*v1.DaemonSet))
if o == nil { if o == nil {
return nil, err return nil, err
} }
return o, err return o, err
} }
func (w *deploymentLifecycleAdapter) Updated(obj runtime.Object) (runtime.Object, error) { func (w *daemonSetLifecycleAdapter) Updated(obj runtime.Object) (runtime.Object, error) {
o, err := w.lifecycle.Updated(obj.(*v1.Deployment)) o, err := w.lifecycle.Updated(obj.(*v1.DaemonSet))
if o == nil { if o == nil {
return nil, err return nil, err
} }
return o, err return o, err
} }
func NewDeploymentLifecycleAdapter(name string, clusterScoped bool, client DeploymentInterface, l DeploymentLifecycle) DeploymentHandlerFunc { func NewDaemonSetLifecycleAdapter(name string, clusterScoped bool, client DaemonSetInterface, l DaemonSetLifecycle) DaemonSetHandlerFunc {
adapter := &deploymentLifecycleAdapter{lifecycle: l} adapter := &daemonSetLifecycleAdapter{lifecycle: l}
syncFn := lifecycle.NewObjectLifecycleAdapter(name, clusterScoped, adapter, client.ObjectClient()) syncFn := lifecycle.NewObjectLifecycleAdapter(name, clusterScoped, adapter, client.ObjectClient())
return func(key string, obj *v1.Deployment) (runtime.Object, error) { return func(key string, obj *v1.DaemonSet) (runtime.Object, error) {
newObj, err := syncFn(key, obj) newObj, err := syncFn(key, obj)
if o, ok := newObj.(runtime.Object); ok { if o, ok := newObj.(runtime.Object); ok {
return o, err return o, err
......
...@@ -6,13 +6,13 @@ import ( ...@@ -6,13 +6,13 @@ import (
) )
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeploymentList) DeepCopyInto(out *DeploymentList) { func (in *DaemonSetList) DeepCopyInto(out *DaemonSetList) {
*out = *in *out = *in
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta out.ListMeta = in.ListMeta
if in.Items != nil { if in.Items != nil {
in, out := &in.Items, &out.Items in, out := &in.Items, &out.Items
*out = make([]appsv1.Deployment, len(*in)) *out = make([]appsv1.DaemonSet, len(*in))
for i := range *in { for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
...@@ -20,18 +20,18 @@ func (in *DeploymentList) DeepCopyInto(out *DeploymentList) { ...@@ -20,18 +20,18 @@ func (in *DeploymentList) DeepCopyInto(out *DeploymentList) {
return return
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentList. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DaemonSetList.
func (in *DeploymentList) DeepCopy() *DeploymentList { func (in *DaemonSetList) DeepCopy() *DaemonSetList {
if in == nil { if in == nil {
return nil return nil
} }
out := new(DeploymentList) out := new(DaemonSetList)
in.DeepCopyInto(out) in.DeepCopyInto(out)
return out return out
} }
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *DeploymentList) DeepCopyObject() runtime.Object { func (in *DaemonSetList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil { if c := in.DeepCopy(); c != nil {
return c return c
} }
......
...@@ -20,13 +20,13 @@ type Interface interface { ...@@ -20,13 +20,13 @@ type Interface interface {
RESTClient() rest.Interface RESTClient() rest.Interface
controller.Starter controller.Starter
DeploymentsGetter DaemonSetsGetter
} }
type Clients struct { type Clients struct {
Interface Interface Interface Interface
Deployment DeploymentClient DaemonSet DaemonSetClient
} }
type Client struct { type Client struct {
...@@ -34,7 +34,7 @@ type Client struct { ...@@ -34,7 +34,7 @@ type Client struct {
restClient rest.Interface restClient rest.Interface
starters []controller.Starter starters []controller.Starter
deploymentControllers map[string]DeploymentController daemonSetControllers map[string]DaemonSetController
} }
func Factory(ctx context.Context, config rest.Config) (context.Context, controller.Starter, error) { func Factory(ctx context.Context, config rest.Config) (context.Context, controller.Starter, error) {
...@@ -70,8 +70,8 @@ func NewClientsFromInterface(iface Interface) *Clients { ...@@ -70,8 +70,8 @@ func NewClientsFromInterface(iface Interface) *Clients {
return &Clients{ return &Clients{
Interface: iface, Interface: iface,
Deployment: &deploymentClient2{ DaemonSet: &daemonSetClient2{
iface: iface.Deployments(""), iface: iface.DaemonSets(""),
}, },
} }
} }
...@@ -89,7 +89,7 @@ func NewForConfig(config rest.Config) (Interface, error) { ...@@ -89,7 +89,7 @@ func NewForConfig(config rest.Config) (Interface, error) {
return &Client{ return &Client{
restClient: restClient, restClient: restClient,
deploymentControllers: map[string]DeploymentController{}, daemonSetControllers: map[string]DaemonSetController{},
}, nil }, nil
} }
...@@ -105,13 +105,13 @@ func (c *Client) Start(ctx context.Context, threadiness int) error { ...@@ -105,13 +105,13 @@ func (c *Client) Start(ctx context.Context, threadiness int) error {
return controller.Start(ctx, threadiness, c.starters...) return controller.Start(ctx, threadiness, c.starters...)
} }
type DeploymentsGetter interface { type DaemonSetsGetter interface {
Deployments(namespace string) DeploymentInterface DaemonSets(namespace string) DaemonSetInterface
} }
func (c *Client) Deployments(namespace string) DeploymentInterface { func (c *Client) DaemonSets(namespace string) DaemonSetInterface {
objectClient := objectclient.NewObjectClient(namespace, c.restClient, &DeploymentResource, DeploymentGroupVersionKind, deploymentFactory{}) objectClient := objectclient.NewObjectClient(namespace, c.restClient, &DaemonSetResource, DaemonSetGroupVersionKind, daemonSetFactory{})
return &deploymentClient{ return &daemonSetClient{
ns: namespace, ns: namespace,
client: c, client: c,
objectClient: objectClient, objectClient: objectClient,
......
...@@ -33,7 +33,7 @@ func addKnownTypes(scheme *runtime.Scheme) error { ...@@ -33,7 +33,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
// TODO this gets cleaned up when the types are fixed // TODO this gets cleaned up when the types are fixed
scheme.AddKnownTypes(SchemeGroupVersion, scheme.AddKnownTypes(SchemeGroupVersion,
&DeploymentList{}, &DaemonSetList{},
) )
return nil return nil
} }
...@@ -99,7 +99,7 @@ func main() { ...@@ -99,7 +99,7 @@ func main() {
} }
if err := generator.ControllersForForeignTypes(basePackage, appsv1.SchemeGroupVersion, []interface{}{ if err := generator.ControllersForForeignTypes(basePackage, appsv1.SchemeGroupVersion, []interface{}{
appsv1.Deployment{}, appsv1.DaemonSet{},
}, nil); err != nil { }, nil); err != nil {
logrus.Fatal(err) logrus.Fatal(err)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment