routers.go 12.5 KB
Newer Older
J
jeff 已提交
1
/*
H
hongming 已提交
2
Copyright 2019 The KubeSphere Authors.
J
jeff 已提交
3

H
hongming 已提交
4 5 6
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
J
jeff 已提交
7

H
hongming 已提交
8
    http://www.apache.org/licenses/LICENSE-2.0
J
jeff 已提交
9

H
hongming 已提交
10 11 12 13 14
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
J
jeff 已提交
15 16 17 18 19
*/

package routers

import (
J
Jeff 已提交
20
	"fmt"
J
jeff 已提交
21
	"io/ioutil"
22
	v1 "k8s.io/api/apps/v1"
Z
zryfish 已提交
23
	corev1 "k8s.io/api/core/v1"
H
hongming 已提交
24
	"k8s.io/apimachinery/pkg/api/errors"
Z
zryfish 已提交
25 26
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
J
Jeff 已提交
27
	"k8s.io/apimachinery/pkg/runtime"
Z
zryfish 已提交
28 29 30
	"k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
J
Jeff 已提交
31
	"k8s.io/klog"
J
Jeff 已提交
32
	"sort"
J
jeff 已提交
33 34 35
	"strings"
)

J
Jeff 已提交
36
// choose router node ip by labels, currently select master node
37
var routerNodeIPLabelSelector = map[string]string{
J
Jeff 已提交
38 39 40
	"node-role.kubernetes.io/master": "",
}

J
Jeff 已提交
41
const (
Z
zryfish 已提交
42 43 44 45 46
	servicemeshEnabled         = "servicemesh.kubesphere.io/enabled"
	sidecarInject              = "sidecar.istio.io/inject"
	ingressControllerFolder    = "/etc/kubesphere/ingress-controller"
	ingressControllerPrefix    = "kubesphere-router-"
	ingressControllerNamespace = "kubesphere-controls-system"
J
Jeff 已提交
47 48
)

Z
zryfish 已提交
49 50 51 52 53 54 55 56 57 58 59 60
type RouterOperator interface {
	GetRouter(namespace string) (*corev1.Service, error)
	CreateRouter(namespace string, serviceType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error)
	DeleteRouter(namespace string) (*corev1.Service, error)
	UpdateRouter(namespace string, serviceType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error)
}

type routerOperator struct {
	routerTemplates map[string]runtime.Object
	client          kubernetes.Interface
	informers       informers.SharedInformerFactory
}
J
Jeff 已提交
61

Z
zryfish 已提交
62
func NewRouterOperator(client kubernetes.Interface, informers informers.SharedInformerFactory) RouterOperator {
63
	yamls, err := loadYamls()
Z
zryfish 已提交
64
	routerTemplates := make(map[string]runtime.Object, 2)
J
Jeff 已提交
65 66

	if err != nil {
Z
zryfish 已提交
67
		klog.Errorf("error happened during loading external yamls, %v", err)
J
Jeff 已提交
68 69 70 71 72 73 74
	}

	for _, f := range yamls {
		decode := scheme.Codecs.UniversalDeserializer().Decode
		obj, _, err := decode([]byte(f), nil, nil)

		if err != nil {
J
Jeff 已提交
75
			klog.Error(err)
J
Jeff 已提交
76 77 78 79 80 81
			continue
		}

		switch obj.(type) {
		case *corev1.Service:
			routerTemplates["SERVICE"] = obj
82
		case *v1.Deployment:
J
Jeff 已提交
83 84 85 86
			routerTemplates["DEPLOYMENT"] = obj
		}
	}

Z
zryfish 已提交
87 88 89 90 91
	return &routerOperator{
		client:          client,
		informers:       informers,
		routerTemplates: routerTemplates,
	}
J
Jeff 已提交
92 93
}

J
Jeff 已提交
94 95
// get master node ip, if there are multiple master nodes,
// choose first one according by their names alphabetically
Z
zryfish 已提交
96
func (c *routerOperator) getMasterNodeIp() string {
J
Jeff 已提交
97

Z
zryfish 已提交
98
	nodeLister := c.informers.Core().V1().Nodes().Lister()
99
	selector := labels.SelectorFromSet(routerNodeIPLabelSelector)
J
Jeff 已提交
100 101 102 103 104 105

	masters, err := nodeLister.List(selector)
	sort.Slice(masters, func(i, j int) bool {
		return strings.Compare(masters[i].Name, masters[j].Name) > 0
	})
	if err != nil {
J
Jeff 已提交
106
		klog.Error(err)
J
Jeff 已提交
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
		return ""
	}

	if len(masters) == 0 {
		return ""
	} else {
		for _, address := range masters[0].Status.Addresses {
			if address.Type == corev1.NodeInternalIP {
				return address.Address
			}
		}
	}

	return ""
}

Z
zryfish 已提交
123
func (c *routerOperator) addLoadBalancerIp(service *corev1.Service) {
J
Jeff 已提交
124

J
Jeff 已提交
125 126 127 128
	if service == nil {
		return
	}

J
Jeff 已提交
129
	// append selected node ip as loadbalancer ingress ip
J
Jeff 已提交
130
	if service.Spec.Type != corev1.ServiceTypeLoadBalancer && len(service.Status.LoadBalancer.Ingress) == 0 {
Z
zryfish 已提交
131
		rip := c.getMasterNodeIp()
J
Jeff 已提交
132
		if len(rip) == 0 {
J
Jeff 已提交
133
			klog.Info("can not get node ip")
J
Jeff 已提交
134 135 136 137 138 139 140 141 142 143 144
			return
		}

		gIngress := corev1.LoadBalancerIngress{
			IP: rip,
		}

		service.Status.LoadBalancer.Ingress = append(service.Status.LoadBalancer.Ingress, gIngress)
	}
}

J
jeff 已提交
145
// Get router from a namespace
Z
zryfish 已提交
146 147 148
func (c *routerOperator) GetRouter(namespace string) (*corev1.Service, error) {
	service, err := c.getRouterService(namespace)
	c.addLoadBalancerIp(service)
J
Jeff 已提交
149 150 151
	return service, err
}

Z
zryfish 已提交
152 153 154 155
func (c *routerOperator) getRouterService(namespace string) (*corev1.Service, error) {
	serviceName := ingressControllerPrefix + namespace
	serviceLister := c.informers.Core().V1().Services().Lister()
	service, err := serviceLister.Services(ingressControllerNamespace).Get(serviceName)
J
jeff 已提交
156 157

	if err != nil {
J
Jeff 已提交
158 159 160
		if errors.IsNotFound(err) {
			return nil, errors.NewNotFound(corev1.Resource("service"), serviceName)
		}
J
Jeff 已提交
161
		klog.Error(err)
J
jeff 已提交
162 163
		return nil, err
	}
J
Jeff 已提交
164
	return service, nil
J
jeff 已提交
165 166 167
}

// Load all resource yamls
168
func loadYamls() ([]string, error) {
J
jeff 已提交
169
	var yamls []string
Z
zryfish 已提交
170
	files, err := ioutil.ReadDir(ingressControllerFolder)
J
jeff 已提交
171
	if err != nil {
172
		klog.Warning(err)
J
jeff 已提交
173 174 175 176 177 178 179
		return nil, err
	}

	for _, file := range files {
		if file.IsDir() || !strings.HasSuffix(file.Name(), ".yaml") {
			continue
		}
Z
zryfish 已提交
180
		content, err := ioutil.ReadFile(ingressControllerFolder + "/" + file.Name())
J
jeff 已提交
181 182

		if err != nil {
J
Jeff 已提交
183
			klog.Error(err)
J
jeff 已提交
184 185 186 187 188 189 190 191 192 193
			return nil, err
		} else {
			yamls = append(yamls, string(content))
		}
	}

	return yamls, nil
}

// Create a ingress controller in a namespace
Z
zryfish 已提交
194
func (c *routerOperator) CreateRouter(namespace string, routerType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) {
J
jeff 已提交
195

J
Jeff 已提交
196
	injectSidecar := false
197
	if enabled, ok := annotations[servicemeshEnabled]; ok {
J
Jeff 已提交
198 199 200 201 202
		if enabled == "true" {
			injectSidecar = true
		}
	}

Z
zryfish 已提交
203
	err := c.createOrUpdateRouterWorkload(namespace, routerType == corev1.ServiceTypeLoadBalancer, injectSidecar)
J
jeff 已提交
204
	if err != nil {
J
Jeff 已提交
205
		klog.Error(err)
J
Jeff 已提交
206
		return nil, err
J
jeff 已提交
207 208
	}

Z
zryfish 已提交
209
	router, err := c.createRouterService(namespace, routerType, annotations)
J
Jeff 已提交
210
	if err != nil {
J
Jeff 已提交
211
		klog.Error(err)
Z
zryfish 已提交
212
		_ = c.deleteRouterWorkload(namespace)
J
Jeff 已提交
213 214
		return nil, err
	}
J
jeff 已提交
215

Z
zryfish 已提交
216
	c.addLoadBalancerIp(router)
J
Jeff 已提交
217 218
	return router, nil
}
J
jeff 已提交
219

J
Jeff 已提交
220 221
// DeleteRouter is used to delete ingress controller related resources in namespace
// It will not delete ClusterRole resource cause it maybe used by other controllers
Z
zryfish 已提交
222 223
func (c *routerOperator) DeleteRouter(namespace string) (*corev1.Service, error) {
	err := c.deleteRouterWorkload(namespace)
J
Jeff 已提交
224
	if err != nil {
J
Jeff 已提交
225
		klog.Error(err)
J
Jeff 已提交
226
	}
J
jeff 已提交
227

Z
zryfish 已提交
228
	router, err := c.deleteRouterService(namespace)
J
jeff 已提交
229

J
Jeff 已提交
230
	if err != nil {
J
Jeff 已提交
231
		klog.Error(err)
J
Jeff 已提交
232 233 234 235
		return router, err
	}
	return router, nil
}
J
jeff 已提交
236

Z
zryfish 已提交
237
func (c *routerOperator) createRouterService(namespace string, routerType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) {
J
jeff 已提交
238

Z
zryfish 已提交
239
	obj, ok := c.routerTemplates["SERVICE"]
J
Jeff 已提交
240
	if !ok {
J
Jeff 已提交
241
		klog.Error("service template not loaded")
J
Jeff 已提交
242 243
		return nil, fmt.Errorf("service template not loaded")
	}
J
jeff 已提交
244

J
Jeff 已提交
245 246 247
	service := obj.(*corev1.Service)
	service.SetAnnotations(annotations)
	service.Spec.Type = routerType
Z
zryfish 已提交
248
	service.Name = ingressControllerPrefix + namespace
J
jeff 已提交
249

J
Jeff 已提交
250 251 252
	// Add project selector
	service.Labels["project"] = namespace
	service.Spec.Selector["project"] = namespace
Z
zryfish 已提交
253
	service, err := c.client.CoreV1().Services(ingressControllerNamespace).Create(service)
J
Jeff 已提交
254
	if err != nil {
J
Jeff 已提交
255
		klog.Error(err)
J
Jeff 已提交
256
		return nil, err
J
jeff 已提交
257 258
	}

J
Jeff 已提交
259
	return service, nil
J
jeff 已提交
260 261
}

Z
zryfish 已提交
262 263
func (c *routerOperator) updateRouterService(namespace string, routerType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) {
	service, err := c.getRouterService(namespace)
J
Jeff 已提交
264
	if err != nil {
J
Jeff 已提交
265
		klog.Error(err, "get router failed")
J
Jeff 已提交
266 267 268 269
		return service, err
	}

	service.Spec.Type = routerType
J
Jeff 已提交
270
	service.SetAnnotations(annotations)
Z
zryfish 已提交
271
	service, err = c.client.CoreV1().Services(ingressControllerNamespace).Update(service)
J
Jeff 已提交
272 273 274
	return service, err
}

Z
zryfish 已提交
275
func (c *routerOperator) deleteRouterService(namespace string) (*corev1.Service, error) {
J
Jeff 已提交
276

Z
zryfish 已提交
277
	service, err := c.getRouterService(namespace)
J
Jeff 已提交
278
	if err != nil {
J
Jeff 已提交
279
		klog.Error(err)
J
Jeff 已提交
280 281 282
		return service, err
	}

J
jeff 已提交
283
	// delete controller service
Z
zryfish 已提交
284
	serviceName := ingressControllerPrefix + namespace
285
	deleteOptions := metav1.DeleteOptions{}
J
jeff 已提交
286

Z
zryfish 已提交
287
	err = c.client.CoreV1().Services(ingressControllerNamespace).Delete(serviceName, &deleteOptions)
J
jeff 已提交
288
	if err != nil {
J
Jeff 已提交
289
		klog.Error(err)
J
Jeff 已提交
290 291 292 293 294 295
		return service, err
	}

	return service, nil
}

Z
zryfish 已提交
296 297
func (c *routerOperator) createOrUpdateRouterWorkload(namespace string, publishService bool, servicemeshEnabled bool) error {
	obj, ok := c.routerTemplates["DEPLOYMENT"]
J
Jeff 已提交
298
	if !ok {
J
Jeff 已提交
299
		klog.Error("Deployment template file not loaded")
J
Jeff 已提交
300 301 302
		return fmt.Errorf("deployment template file not loaded")
	}

Z
zryfish 已提交
303
	deployName := ingressControllerPrefix + namespace
J
Jeff 已提交
304

Z
zryfish 已提交
305
	deployment, err := c.client.AppsV1().Deployments(ingressControllerNamespace).Get(deployName, metav1.GetOptions{})
J
Jeff 已提交
306

J
Jeff 已提交
307
	createDeployment := true
J
Jeff 已提交
308

J
Jeff 已提交
309 310
	if err != nil {
		if errors.IsNotFound(err) {
311
			deployment = obj.(*v1.Deployment)
J
Jeff 已提交
312

Z
zryfish 已提交
313
			deployment.Name = ingressControllerPrefix + namespace
J
Jeff 已提交
314 315 316 317 318

			// Add project label
			deployment.Spec.Selector.MatchLabels["project"] = namespace
			deployment.Spec.Template.Labels["project"] = namespace

J
Jeff 已提交
319 320 321
			// Add configmap
			deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--configmap=$(POD_NAMESPACE)/"+deployment.Name)

J
Jeff 已提交
322 323 324 325 326
			// Isolate namespace
			deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--watch-namespace="+namespace)

			// Choose self as master
			deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--election-id="+deployment.Name)
J
Jeff 已提交
327 328

		}
J
Jeff 已提交
329 330 331 332 333 334 335
	} else {
		createDeployment = false

		for i := range deployment.Spec.Template.Spec.Containers {
			if deployment.Spec.Template.Spec.Containers[i].Name == "nginx-ingress-controller" {
				var args []string
				for j := range deployment.Spec.Template.Spec.Containers[i].Args {
J
Jeff 已提交
336 337 338 339
					argument := deployment.Spec.Template.Spec.Containers[i].Args[j]
					if strings.HasPrefix("--publish-service", argument) ||
						strings.HasPrefix("--configmap", argument) ||
						strings.HasPrefix("--report-node-internal-ip-address", argument) {
J
Jeff 已提交
340 341 342 343 344 345 346
						continue
					}
					args = append(args, deployment.Spec.Template.Spec.Containers[i].Args[j])
				}
				deployment.Spec.Template.Spec.Containers[i].Args = args
			}
		}
J
Jeff 已提交
347 348
	}

J
Jeff 已提交
349 350 351 352
	if deployment.Spec.Template.Annotations == nil {
		deployment.Spec.Template.Annotations = make(map[string]string, 0)
	}
	if servicemeshEnabled {
353
		deployment.Spec.Template.Annotations[sidecarInject] = "true"
J
Jeff 已提交
354
	} else {
355
		deployment.Spec.Template.Annotations[sidecarInject] = "false"
J
Jeff 已提交
356
	}
J
Jeff 已提交
357 358

	if publishService {
Z
zryfish 已提交
359
		deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--publish-service="+ingressControllerNamespace+"/"+ingressControllerPrefix+namespace)
J
Jeff 已提交
360 361 362 363
	} else {
		deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--report-node-internal-ip-address")
	}

J
Jeff 已提交
364
	if createDeployment {
Z
zryfish 已提交
365
		deployment, err = c.client.AppsV1().Deployments(ingressControllerNamespace).Create(deployment)
J
Jeff 已提交
366
	} else {
Z
zryfish 已提交
367
		deployment, err = c.client.AppsV1().Deployments(ingressControllerNamespace).Update(deployment)
J
Jeff 已提交
368 369
	}

J
Jeff 已提交
370
	if err != nil {
J
Jeff 已提交
371
		klog.Error(err)
J
Jeff 已提交
372
		return err
J
jeff 已提交
373 374
	}

J
Jeff 已提交
375 376 377
	return nil
}

Z
zryfish 已提交
378
func (c *routerOperator) deleteRouterWorkload(namespace string) error {
379
	deleteOptions := metav1.DeleteOptions{}
J
jeff 已提交
380
	// delete controller deployment
Z
zryfish 已提交
381 382
	deploymentName := ingressControllerPrefix + namespace
	err := c.client.AppsV1().Deployments(ingressControllerNamespace).Delete(deploymentName, &deleteOptions)
J
jeff 已提交
383
	if err != nil {
J
Jeff 已提交
384
		klog.Error(err)
J
jeff 已提交
385 386
	}

J
Jeff 已提交
387 388 389 390 391 392
	// delete replicaset if there are any
	selector := labels.SelectorFromSet(
		labels.Set{
			"app":       "kubesphere",
			"component": "ks-router",
			"tier":      "backend",
J
Jeff 已提交
393
			"project":   namespace,
J
Jeff 已提交
394
		})
Z
zryfish 已提交
395 396
	replicaSetLister := c.informers.Apps().V1().ReplicaSets().Lister()
	replicaSets, err := replicaSetLister.ReplicaSets(ingressControllerNamespace).List(selector)
J
Jeff 已提交
397
	if err != nil {
J
Jeff 已提交
398
		klog.Error(err)
J
Jeff 已提交
399
	}
J
jeff 已提交
400

J
Jeff 已提交
401
	for i := range replicaSets {
Z
zryfish 已提交
402
		err = c.client.AppsV1().ReplicaSets(ingressControllerNamespace).Delete(replicaSets[i].Name, &deleteOptions)
J
Jeff 已提交
403
		if err != nil {
J
Jeff 已提交
404
			klog.Error(err)
J
Jeff 已提交
405
		}
J
jeff 已提交
406 407
	}

J
Jeff 已提交
408
	return nil
J
jeff 已提交
409 410
}

J
Jeff 已提交
411
// Update Ingress Controller Service, change type from NodePort to loadbalancer or vice versa.
Z
zryfish 已提交
412
func (c *routerOperator) UpdateRouter(namespace string, routerType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) {
H
hongming 已提交
413
	var router *corev1.Service
J
jeff 已提交
414

Z
zryfish 已提交
415
	router, err := c.getRouterService(namespace)
J
jeff 已提交
416 417

	if err != nil {
J
Jeff 已提交
418
		klog.Error(err)
J
Jeff 已提交
419
		return router, err
J
jeff 已提交
420 421
	}

422
	enableServicemesh := annotations[servicemeshEnabled] == "true"
J
jeff 已提交
423

Z
zryfish 已提交
424
	err = c.createOrUpdateRouterWorkload(namespace, routerType == corev1.ServiceTypeLoadBalancer, enableServicemesh)
J
Jeff 已提交
425
	if err != nil {
J
Jeff 已提交
426
		klog.Error(err)
J
Jeff 已提交
427
		return router, err
J
Jeff 已提交
428
	}
J
jeff 已提交
429

Z
zryfish 已提交
430
	newRouter, err := c.updateRouterService(namespace, routerType, annotations)
J
Jeff 已提交
431
	if err != nil {
J
Jeff 已提交
432
		klog.Error(err)
J
Jeff 已提交
433
		return newRouter, err
J
jeff 已提交
434 435
	}

J
Jeff 已提交
436
	return newRouter, nil
J
jeff 已提交
437
}