From a64153b1c72037744d9d7f6cca128ea53bcffa57 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 3 Jun 2018 19:36:01 +0800 Subject: [PATCH] add route --- Dockerfile.dev | 9 +- Makefile | 4 +- install/ingress-controller/1-clusterrole.yaml | 51 +++ install/ingress-controller/2-role.yaml | 39 +++ .../ingress-controller/3-serviceaccount.yaml | 4 + .../4-clusterrolebinding.yaml | 11 + install/ingress-controller/5-rolebinding.yaml | 11 + .../ingress-controller/6-default-backend.yaml | 42 +++ .../7-default-backend-svc.yaml | 14 + install/ingress-controller/8-configmap.yaml | 6 + install/ingress-controller/9-with-rbac.yaml | 63 ++++ .../ingress-controller/9.1-static-ip-svc.yaml | 21 ++ pkg/apis/v1alpha/install.go | 4 + pkg/apis/v1alpha/routes/routes_handler.go | 195 +++++++++++ pkg/models/routes.go | 325 ++++++++++++++++++ vendor/k8s.io/kubernetes/pkg/util/slice/BUILD | 33 ++ .../k8s.io/kubernetes/pkg/util/slice/slice.go | 91 +++++ .../kubernetes/pkg/util/slice/slice_test.go | 172 +++++++++ 18 files changed, 1091 insertions(+), 4 deletions(-) create mode 100644 install/ingress-controller/1-clusterrole.yaml create mode 100644 install/ingress-controller/2-role.yaml create mode 100644 install/ingress-controller/3-serviceaccount.yaml create mode 100644 install/ingress-controller/4-clusterrolebinding.yaml create mode 100644 install/ingress-controller/5-rolebinding.yaml create mode 100644 install/ingress-controller/6-default-backend.yaml create mode 100644 install/ingress-controller/7-default-backend-svc.yaml create mode 100644 install/ingress-controller/8-configmap.yaml create mode 100644 install/ingress-controller/9-with-rbac.yaml create mode 100644 install/ingress-controller/9.1-static-ip-svc.yaml create mode 100644 pkg/apis/v1alpha/routes/routes_handler.go create mode 100644 pkg/models/routes.go create mode 100644 vendor/k8s.io/kubernetes/pkg/util/slice/BUILD create mode 100644 vendor/k8s.io/kubernetes/pkg/util/slice/slice.go create mode 100644 vendor/k8s.io/kubernetes/pkg/util/slice/slice_test.go diff --git a/Dockerfile.dev b/Dockerfile.dev index 07e756474..12cafc1a7 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,5 +1,10 @@ FROM alpine:3.6 -RUN apk add --update ca-certificates && update-ca-certificates -COPY ./* /usr/local/bin/ + +RUN apk add --update ca-certificates \ + && update-ca-certificates \ + && mkdir -p /etc/kubesphere/ingress-controller + +COPY ./bin/* /usr/local/bin/ +COPY ./ingress-controller /etc/kubesphere/ingress-controller CMD ["sh"] diff --git a/Makefile b/Makefile index 90518c705..158de4048 100644 --- a/Makefile +++ b/Makefile @@ -86,10 +86,10 @@ fmt-check: fmt-all .PHONY: build build: fmt - mkdir -p ./tmp/bin + mkdir -p ./tmp/bin && cp -r ./install/ ./tmp/ $(call get_build_flags) $(RUN_IN_DOCKER) time go install -ldflags '$(BUILD_FLAG)' $(TRAG.Gopkg)/cmd/... - @docker build -t $(TARG.Name) -f ./Dockerfile.dev ./tmp/bin + @docker build -t $(TARG.Name) -f ./Dockerfile.dev ./tmp @docker image prune -f 1>/dev/null 2>&1 @echo "build done" diff --git a/install/ingress-controller/1-clusterrole.yaml b/install/ingress-controller/1-clusterrole.yaml new file mode 100644 index 000000000..091b335f1 --- /dev/null +++ b/install/ingress-controller/1-clusterrole.yaml @@ -0,0 +1,51 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: kubesphere-router-clusterrole +rules: + - apiGroups: + - "" + resources: + - configmaps + - endpoints + - nodes + - pods + - secrets + verbs: + - list + - watch + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch + - apiGroups: + - "extensions" + resources: + - ingresses + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - apiGroups: + - "extensions" + resources: + - ingresses/status + verbs: + - update diff --git a/install/ingress-controller/2-role.yaml b/install/ingress-controller/2-role.yaml new file mode 100644 index 000000000..fb7d76133 --- /dev/null +++ b/install/ingress-controller/2-role.yaml @@ -0,0 +1,39 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: Role +metadata: + name: kubesphere-router-role +rules: + - apiGroups: + - "" + resources: + - configmaps + - pods + - secrets + - namespaces + verbs: + - get + - apiGroups: + - "" + resources: + - configmaps + resourceNames: + # Defaults to "-" + # Here: "-" + # This has to be adapted if you change either parameter + # when launching the nginx-ingress-controller. + - "ingress-controller-leader-nginx" + verbs: + - get + - update + - apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - apiGroups: + - "" + resources: + - endpoints + verbs: + - get diff --git a/install/ingress-controller/3-serviceaccount.yaml b/install/ingress-controller/3-serviceaccount.yaml new file mode 100644 index 000000000..849a83094 --- /dev/null +++ b/install/ingress-controller/3-serviceaccount.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kubesphere-router-serviceaccount diff --git a/install/ingress-controller/4-clusterrolebinding.yaml b/install/ingress-controller/4-clusterrolebinding.yaml new file mode 100644 index 000000000..ad17cd58e --- /dev/null +++ b/install/ingress-controller/4-clusterrolebinding.yaml @@ -0,0 +1,11 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: nginx-ingress-clusterrole-nisa-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kubesphere-roter-clusterrole +subjects: + - kind: ServiceAccount + name: kubesphere-router-serviceaccount diff --git a/install/ingress-controller/5-rolebinding.yaml b/install/ingress-controller/5-rolebinding.yaml new file mode 100644 index 000000000..b21a9f1bc --- /dev/null +++ b/install/ingress-controller/5-rolebinding.yaml @@ -0,0 +1,11 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: nginx-ingress-role-nisa-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: kubesphere-router-role +subjects: + - kind: ServiceAccount + name: kubesphere-router-serviceaccount diff --git a/install/ingress-controller/6-default-backend.yaml b/install/ingress-controller/6-default-backend.yaml new file mode 100644 index 000000000..d9b167278 --- /dev/null +++ b/install/ingress-controller/6-default-backend.yaml @@ -0,0 +1,42 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: default-http-backend + labels: + app: kubesphere + component: kubesphere-router +spec: + replicas: 1 + selector: + matchLabels: + app: kubesphere + component: kubesphere-router + template: + metadata: + labels: + app: kubesphere + component: kubesphere-router + spec: + terminationGracePeriodSeconds: 60 + containers: + - name: default-http-backend + # Any image is permissible as long as: + # 1. It serves a 404 page at / + # 2. It serves 200 on a /healthz endpoint + image: googlecontainer/defaultbackend-amd64:1.4 + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + initialDelaySeconds: 30 + timeoutSeconds: 5 + ports: + - containerPort: 8080 + resources: + limits: + cpu: 10m + memory: 20Mi + requests: + cpu: 10m + memory: 20Mi diff --git a/install/ingress-controller/7-default-backend-svc.yaml b/install/ingress-controller/7-default-backend-svc.yaml new file mode 100644 index 000000000..7990a7ff5 --- /dev/null +++ b/install/ingress-controller/7-default-backend-svc.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: default-http-backend + labels: + app: kubesphere + component: kubesphere-router +spec: + ports: + - port: 80 + targetPort: 8080 + selector: + app: kubespshere + component: kubesphere-router diff --git a/install/ingress-controller/8-configmap.yaml b/install/ingress-controller/8-configmap.yaml new file mode 100644 index 000000000..7e9a95052 --- /dev/null +++ b/install/ingress-controller/8-configmap.yaml @@ -0,0 +1,6 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: nginx-configuration + labels: + app: ingress-nginx diff --git a/install/ingress-controller/9-with-rbac.yaml b/install/ingress-controller/9-with-rbac.yaml new file mode 100644 index 000000000..4dae4c664 --- /dev/null +++ b/install/ingress-controller/9-with-rbac.yaml @@ -0,0 +1,63 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: kubesphere-router +spec: + replicas: 1 + selector: + matchLabels: + app: kubesphere-router + template: + metadata: + labels: + app: kubesphere-router + annotations: + prometheus.io/port: '10254' + prometheus.io/scrape: 'true' + spec: + serviceAccountName: nginx-ingress-serviceaccount + containers: + - name: nginx-ingress-controller + image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.14.0 + args: + - /nginx-ingress-controller + - --default-backend-service=$(POD_NAMESPACE)/default-http-backend + - --configmap=$(POD_NAMESPACE)/nginx-configuration + - --annotations-prefix=nginx.ingress.kubernetes.io + - --watch-namespace=$(POD_NAMESPACE) + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + ports: + - name: http + containerPort: 80 + - name: https + containerPort: 443 + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + securityContext: + runAsNonRoot: false + diff --git a/install/ingress-controller/9.1-static-ip-svc.yaml b/install/ingress-controller/9.1-static-ip-svc.yaml new file mode 100644 index 000000000..5d65cd4fb --- /dev/null +++ b/install/ingress-controller/9.1-static-ip-svc.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Service +metadata: + name: kubesphere-router-gateway + labels: + app: kubesphere + component: kubesphere-router +spec: + selector: + app: + type: LoadBalancer + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 80 + - name: https + protocol: TCP + port: 443 + targetPort: 443 + diff --git a/pkg/apis/v1alpha/install.go b/pkg/apis/v1alpha/install.go index 43549ab9c..0a55ffe80 100644 --- a/pkg/apis/v1alpha/install.go +++ b/pkg/apis/v1alpha/install.go @@ -28,6 +28,7 @@ import ( "kubesphere.io/kubesphere/pkg/apis/v1alpha/volumes" "kubesphere.io/kubesphere/pkg/apis/v1alpha/iam" "kubesphere.io/kubesphere/pkg/apis/v1alpha/components" + "kubesphere.io/kubesphere/pkg/apis/v1alpha/routes" ) func init() { @@ -45,6 +46,9 @@ func init() { containers.Register(ws) iam.Register(ws) components.Register(ws,"/components") + + routes.Register(ws) + // add webservice to default container restful.Add(ws) diff --git a/pkg/apis/v1alpha/routes/routes_handler.go b/pkg/apis/v1alpha/routes/routes_handler.go new file mode 100644 index 000000000..66f2ace25 --- /dev/null +++ b/pkg/apis/v1alpha/routes/routes_handler.go @@ -0,0 +1,195 @@ +/* +Copyright 2018 The KubeSphere Authors. + +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 + + http://www.apache.org/licenses/LICENSE-2.0 + +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. +*/ + +package routes + +import ( + "github.com/emicklei/go-restful" + + "errors" + "net/http" + "strings" + + "github.com/golang/glog" + "k8s.io/api/core/v1" + + "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/filter/route" + "kubesphere.io/kubesphere/pkg/models" +) + +func Register(ws *restful.WebService) { + ws.Route(ws.GET("/routers").To(GetAllRouters). + Doc("Get all routers"). + Filter(route.RouteLogging). + Produces(restful.MIME_JSON)) + + ws.Route(ws.GET("/{namespace}/router").To(GetRouter). + Doc("Get router of a specified project"). + Param(ws.PathParameter("namespace", "name of the project").DataType("string")). + Filter(route.RouteLogging). + Produces(restful.MIME_JSON)) + + ws.Route(ws.DELETE("/{namespace}/router").To(DeleteRouter). + Doc("Get router of a specified project"). + Param(ws.PathParameter("namespace", "name of the project").DataType("string")). + Filter(route.RouteLogging). + Produces(restful.MIME_JSON)) + + ws.Route(ws.POST("/{namespace}/router").To(CreateRouter). + Doc("Create a router for a specified project"). + Param(ws.PathParameter("namespace", "name of the project").DataType("string")). + Filter(route.RouteLogging). + Consumes(restful.MIME_JSON). + Produces(restful.MIME_JSON)) + + ws.Route(ws.PUT("/{namespace}/router").To(UpdateRouter). + Doc("Update a router for a specified project"). + Param(ws.PathParameter("namespace", "name of the project").DataType("string")). + Filter(route.RouteLogging). + Consumes(restful.MIME_JSON). + Produces(restful.MIME_JSON)) +} + +// Get all namespace ingress controller services +func GetAllRouters(request *restful.Request, response *restful.Response) { + + routers, err := models.GetAllRouters() + + if err != nil { + glog.Error(err) + response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) + } else { + response.WriteAsJson(routers) + } +} + +// Get ingress controller service for specified namespace +func GetRouter(request *restful.Request, response *restful.Response) { + + namespace := request.PathParameter("namespace") + router, err := models.GetRouter(namespace) + + if err != nil { + glog.Error(err) + response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) + } else if router == nil { + response.WriteHeaderAndEntity(http.StatusNotFound, constants.MessageResponse{Message: "Reseource Not Found"}) + } else { + response.WriteAsJson(router) + } + +} + +// Create ingress controller and related services +func CreateRouter(request *restful.Request, response *restful.Response) { + + namespace := request.PathParameter("namespace") + + newRouter := models.Router{} + err := request.ReadEntity(&newRouter) + + if err != nil { + response.WriteAsJson(err) + return + } + + var router *v1.Service + + serviceType, annotationMap, err := ParseParameter(newRouter) + + if err != nil { + glog.Error("Wrong annotations, missing key or value") + response.WriteHeaderAndEntity(http.StatusBadRequest, + constants.MessageResponse{Message: "Wrong annotations, missing key or value"}) + return + } + + router, err = models.CreateRouter(namespace, serviceType, annotationMap) + + if err != nil { + response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) + } else { + response.WriteAsJson(*router) + } +} + +// Delete ingress controller and services +func DeleteRouter(request *restful.Request, response *restful.Response) { + namespace := request.PathParameter("namespace") + + router, err := models.DeleteRouter(namespace) + + if err != nil { + response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) + return + } else { + response.WriteAsJson(router) + } +} + +func UpdateRouter(request *restful.Request, response *restful.Response) { + + namespace := request.PathParameter("namespace") + + newRouter := models.Router{} + err := request.ReadEntity(&newRouter) + + if err != nil { + glog.Error(err) + response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) + return + } + + serviceType, annotationMap, err := ParseParameter(newRouter) + + router, err := models.UpdateRouter(namespace, serviceType, annotationMap) + + if err != nil { + response.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) + return + } else { + response.WriteAsJson(router) + } +} + +func ParseParameter(router models.Router) (routerType v1.ServiceType, annotationMap map[string]string, err error) { + + routerType = v1.ServiceTypeNodePort + annotationMap = make(map[string]string) + + if strings.Compare(strings.ToLower(router.RouterType), "loadbalancer") == 0 { + annotations := router.Annotations + + annotation := strings.FieldsFunc(annotations, func(r rune) bool { + return r == ',' || r == '=' + }) + + if len(annotation)%2 != 0 { + glog.Error("Wrong annotations, missing key or value") + return routerType, annotationMap, errors.New("wrong annotations, missing key or value") + } + + for i := 0; i < len(annotation); i += 2 { + annotationMap[annotation[i]] = annotation[i+1] + } + + return v1.ServiceTypeLoadBalancer, annotationMap, nil + } else { + return v1.ServiceTypeNodePort, nil, nil + } + +} diff --git a/pkg/models/routes.go b/pkg/models/routes.go new file mode 100644 index 000000000..1718a3897 --- /dev/null +++ b/pkg/models/routes.go @@ -0,0 +1,325 @@ +/* +Copyright 2018 The KubeSphere Authors. + +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 + + http://www.apache.org/licenses/LICENSE-2.0 + +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. +*/ + +package models + +import ( + "strings" + "io/ioutil" + + coreV1 "k8s.io/api/core/v1" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/scheme" + extensionsV1beta1 "k8s.io/api/extensions/v1beta1" + "k8s.io/api/rbac/v1beta1" + "github.com/golang/glog" + + "kubesphere.io/kubesphere/pkg/client" +) + +const RouterYamlDirectory = "/etc/kubesphere/ingress-controller/" + +type Router struct { + RouterType string `json:"type"` + Annotations string `json:"annotations"` +} + +func GetAllRouters() ([] *coreV1.Service, error) { + + k8sClient := client.NewK8sClient() + + routers := []*coreV1.Service{} + + opts := metaV1.ListOptions{} + + namespaces, err := k8sClient.CoreV1().Namespaces().List(opts) + + if err != nil { + glog.Error(err) + return routers, err + } + + opts = metaV1.ListOptions{ + LabelSelector: "app=kubesphere,component=kubesphere-router", + FieldSelector: "metadata.name=kubesphere-router-gateway", + } + + for _, namespace := range namespaces.Items { + services, err := k8sClient.CoreV1().Services(namespace.Name).List(opts) + + if err != nil { + glog.Error(err) + return nil, err + } + + if len(services.Items) > 0 { + routers = append(routers, &services.Items[0]) + } + } + + return routers, nil +} + +// Get router from a namespace +func GetRouter(namespace string) (*coreV1.Service, error) { + k8sClient := client.NewK8sClient() + + var router *coreV1.Service + + opts := metaV1.ListOptions{ + LabelSelector: "app=kubesphere,component=kubesphere-router", + FieldSelector: "metadata.name=kubesphere-router-gateway", + } + + services, err := k8sClient.CoreV1().Services(namespace).List(opts) + + if err != nil { + glog.Error(err) + return nil, err + } + + if len(services.Items) > 0 { + router = &services.Items[0] + } + + return router, nil +} + +// Load all resource yamls +func LoadYamls() ([]string, error) { + + var yamls []string + + files, err := ioutil.ReadDir(RouterYamlDirectory) + if err != nil { + glog.Error(err) + return nil, err + } + + for _, file := range files { + content, err := ioutil.ReadFile(RouterYamlDirectory + "/" + file.Name()) + + if err != nil { + glog.Error(err) + return nil, err + } else { + yamls = append(yamls, string(content)) + } + } + + return yamls, nil +} + +func IsRouterService(serviceName string) bool { + if strings.Compare(strings.ToLower(serviceName), "default-http-backend") == 0 { + return false + } + return true +} + +// Create a ingress controller in a namespace +func CreateRouter(namespace string, routerType coreV1.ServiceType, annotations map[string]string) (*coreV1.Service, error) { + + k8sClient := client.NewK8sClient() + + var router *coreV1.Service + + yamls, err := LoadYamls() + + if err != nil { + glog.Error(err) + } + + for _, f := range yamls { + decode := scheme.Codecs.UniversalDeserializer().Decode + obj, _, err := decode([]byte(f), nil, nil) + + if err != nil { + glog.Error(err) + return router, err + } + + switch obj.(type) { + case *v1beta1.Role: + role := obj.(*v1beta1.Role) + role, err := k8sClient.RbacV1beta1().Roles(namespace).Create(role) + if err != nil { + glog.Error(err) + } + + case *v1beta1.ClusterRole: + clusterRole := obj.(*v1beta1.ClusterRole) + + clusterRole, err := k8sClient.RbacV1beta1().ClusterRoles().Create(clusterRole) + if err != nil { + glog.Error(err) + } + case *v1beta1.ClusterRoleBinding: + clusterRoleBinding := obj.(*v1beta1.ClusterRoleBinding) + clusterRoleBinding.Subjects[0].Namespace = namespace + clusterRoleBinding, err := k8sClient.RbacV1beta1().ClusterRoleBindings().Create(clusterRoleBinding) + if err != nil { + glog.Error(err) + } + case *v1beta1.RoleBinding: + roleBinding := obj.(*v1beta1.RoleBinding) + roleBinding.Subjects[0].Namespace = namespace + roleBinding, err := k8sClient.RbacV1beta1().RoleBindings(namespace).Create(roleBinding) + + if err != nil { + glog.Error(err) + } + case *coreV1.ServiceAccount: + sa := obj.(*coreV1.ServiceAccount) + sa, err := k8sClient.CoreV1().ServiceAccounts(namespace).Create(sa) + if err != nil { + glog.Error(err) + } + case *coreV1.Service: + service := obj.(*coreV1.Service) + + if IsRouterService(service.Name) { + service.SetAnnotations(annotations) + service.Spec.Type = routerType + } + + service, err := k8sClient.CoreV1().Services(namespace).Create(service) + if err != nil { + glog.Error(err) + return nil, err + } + + if IsRouterService(service.Name) { + router = service + } + + case *extensionsV1beta1.Deployment: + deployment := obj.(*extensionsV1beta1.Deployment) + deployment, err := k8sClient.ExtensionsV1beta1().Deployments(namespace).Create(deployment) + if err != nil { + glog.Error(err) + } + default: + //glog.Info("Default resource") + } + } + + return router, nil +} + +// DeleteRouter is used to delete ingress controller related resources in namespace +// It will not delete ClusterRole resource cause it maybe used other controllers +func DeleteRouter(namespace string) (*coreV1.Service, error) { + k8sClient := client.NewK8sClient() + + var router *coreV1.Service + yamls, err := LoadYamls() + + if err != nil { + glog.Error(err) + } + + for _, f := range yamls { + decode := scheme.Codecs.UniversalDeserializer().Decode + obj, _, err := decode([]byte(f), nil, nil) + + if err != nil { + glog.Error(err) + return router, err + } + + options := metaV1.DeleteOptions{} + + switch obj.(type) { + case *v1beta1.Role: + role := obj.(*v1beta1.Role) + err = k8sClient.RbacV1beta1().Roles(namespace).Delete(role.Name, &options) + if err != nil { + glog.Error(err) + } + case *v1beta1.ClusterRoleBinding: + clusterRoleBinding := obj.(*v1beta1.ClusterRoleBinding) + err = k8sClient.RbacV1beta1().ClusterRoleBindings().Delete(clusterRoleBinding.Name, &options) + if err != nil { + glog.Error(err) + } + case *v1beta1.RoleBinding: + roleBinding := obj.(*v1beta1.RoleBinding) + err = k8sClient.RbacV1beta1().RoleBindings(namespace).Delete(roleBinding.Name, &options) + if err != nil { + glog.Error(err) + } + case *coreV1.ServiceAccount: + sa := obj.(*coreV1.ServiceAccount) + err = k8sClient.CoreV1().ServiceAccounts(namespace).Delete(sa.Name, &options) + if err != nil { + glog.Error(err) + } + case *coreV1.Service: + service := obj.(*coreV1.Service) + + err = k8sClient.CoreV1().Services(namespace).Delete(service.Name, &options) + if err != nil { + glog.Error(err) + } + + if IsRouterService(service.Name) { + router = service + } + + case *extensionsV1beta1.Deployment: + deployment := obj.(*extensionsV1beta1.Deployment) + err = k8sClient.ExtensionsV1beta1().Deployments(namespace).Delete(deployment.Name, &options) + if err != nil { + glog.Error(err) + } + default: + //glog.Info("Default resource") + } + } + + return router, nil + +} + +// Update Ingress Controller Service, change type from NodePort to Loadbalancer or vice versa. +func UpdateRouter(namespace string, routerType coreV1.ServiceType, annotations map[string]string) (*coreV1.Service, error) { + k8sClient := client.NewK8sClient() + + var router *coreV1.Service + + router, err := GetRouter(namespace) + + if err != nil { + glog.Error(err) + return router, nil + } + + if router.Spec.Type != routerType { + router.Spec.Type = routerType + router.SetAnnotations(annotations) + + router, err = k8sClient.CoreV1().Services(namespace).Update(router) + + if err != nil { + glog.Error(err) + return router, err + } + } + + return router, nil + +} diff --git a/vendor/k8s.io/kubernetes/pkg/util/slice/BUILD b/vendor/k8s.io/kubernetes/pkg/util/slice/BUILD new file mode 100644 index 000000000..16325c778 --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/util/slice/BUILD @@ -0,0 +1,33 @@ +package(default_visibility = ["//visibility:public"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_library( + name = "go_default_library", + srcs = ["slice.go"], + importpath = "k8s.io/kubernetes/pkg/util/slice", + deps = ["//vendor/k8s.io/apimachinery/pkg/util/rand:go_default_library"], +) + +go_test( + name = "go_default_test", + srcs = ["slice_test.go"], + embed = [":go_default_library"], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/vendor/k8s.io/kubernetes/pkg/util/slice/slice.go b/vendor/k8s.io/kubernetes/pkg/util/slice/slice.go new file mode 100644 index 000000000..b9809cc29 --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/util/slice/slice.go @@ -0,0 +1,91 @@ +/* +Copyright 2015 The Kubernetes Authors. + +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 + + http://www.apache.org/licenses/LICENSE-2.0 + +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. +*/ + +// Package slice provides utility methods for common operations on slices. +package slice + +import ( + "sort" + + utilrand "k8s.io/apimachinery/pkg/util/rand" +) + +// CopyStrings copies the contents of the specified string slice +// into a new slice. +func CopyStrings(s []string) []string { + if s == nil { + return nil + } + c := make([]string, len(s)) + copy(c, s) + return c +} + +// SortStrings sorts the specified string slice in place. It returns the same +// slice that was provided in order to facilitate method chaining. +func SortStrings(s []string) []string { + sort.Strings(s) + return s +} + +// ShuffleStrings copies strings from the specified slice into a copy in random +// order. It returns a new slice. +func ShuffleStrings(s []string) []string { + if s == nil { + return nil + } + shuffled := make([]string, len(s)) + perm := utilrand.Perm(len(s)) + for i, j := range perm { + shuffled[j] = s[i] + } + return shuffled +} + +// ContainsString checks if a given slice of strings contains the provided string. +// If a modifier func is provided, it is called with the slice item before the comparation. +func ContainsString(slice []string, s string, modifier func(s string) string) bool { + for _, item := range slice { + if item == s { + return true + } + if modifier != nil && modifier(item) == s { + return true + } + } + return false +} + +// RemoveString returns a newly created []string that contains all items from slice that +// are not equal to s and modifier(s) in case modifier func is provided. +func RemoveString(slice []string, s string, modifier func(s string) string) []string { + newSlice := make([]string, 0) + for _, item := range slice { + if item == s { + continue + } + if modifier != nil && modifier(item) == s { + continue + } + newSlice = append(newSlice, item) + } + if len(newSlice) == 0 { + // Sanitize for unit tests so we don't need to distinguish empty array + // and nil. + newSlice = nil + } + return newSlice +} diff --git a/vendor/k8s.io/kubernetes/pkg/util/slice/slice_test.go b/vendor/k8s.io/kubernetes/pkg/util/slice/slice_test.go new file mode 100644 index 000000000..19b46c227 --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/util/slice/slice_test.go @@ -0,0 +1,172 @@ +/* +Copyright 2015 The Kubernetes Authors. + +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 + + http://www.apache.org/licenses/LICENSE-2.0 + +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. +*/ + +package slice + +import ( + "reflect" + "testing" +) + +func TestCopyStrings(t *testing.T) { + var src1 []string + dest1 := CopyStrings(src1) + + if !reflect.DeepEqual(src1, dest1) { + t.Errorf("%v and %v are not equal", src1, dest1) + } + + src2 := []string{} + dest2 := CopyStrings(src2) + + if !reflect.DeepEqual(src2, dest2) { + t.Errorf("%v and %v are not equal", src2, dest2) + } + + src3 := []string{"a", "c", "b"} + dest3 := CopyStrings(src3) + + if !reflect.DeepEqual(src3, dest3) { + t.Errorf("%v and %v are not equal", src3, dest3) + } + + src3[0] = "A" + if reflect.DeepEqual(src3, dest3) { + t.Errorf("CopyStrings didn't make a copy") + } +} + +func TestSortStrings(t *testing.T) { + src := []string{"a", "c", "b"} + dest := SortStrings(src) + expected := []string{"a", "b", "c"} + + if !reflect.DeepEqual(dest, expected) { + t.Errorf("SortString didn't sort the strings") + } + + if !reflect.DeepEqual(src, expected) { + t.Errorf("SortString didn't sort in place") + } +} + +func TestShuffleStrings(t *testing.T) { + var src []string + dest := ShuffleStrings(src) + + if dest != nil { + t.Errorf("ShuffleStrings for a nil slice got a non-nil slice") + } + + src = []string{"a", "b", "c", "d", "e", "f"} + dest = ShuffleStrings(src) + + if len(src) != len(dest) { + t.Errorf("Shuffled slice is wrong length, expected %v got %v", len(src), len(dest)) + } + + m := make(map[string]bool, len(dest)) + for _, s := range dest { + m[s] = true + } + + for _, k := range src { + if _, exists := m[k]; !exists { + t.Errorf("Element %v missing from shuffled slice", k) + } + } +} + +func TestContainsString(t *testing.T) { + src := []string{"aa", "bb", "cc"} + if !ContainsString(src, "bb", nil) { + t.Errorf("ContainsString didn't find the string as expected") + } + + modifier := func(s string) string { + if s == "cc" { + return "ee" + } + return s + } + if !ContainsString(src, "ee", modifier) { + t.Errorf("ContainsString didn't find the string by modifier") + } +} + +func TestRemoveString(t *testing.T) { + modifier := func(s string) string { + if s == "ab" { + return "ee" + } + return s + } + tests := []struct { + testName string + input []string + remove string + modifier func(s string) string + want []string + }{ + { + testName: "Nil input slice", + input: nil, + remove: "", + modifier: nil, + want: nil, + }, + { + testName: "Slice doesn't contain the string", + input: []string{"a", "ab", "cdef"}, + remove: "NotPresentInSlice", + modifier: nil, + want: []string{"a", "ab", "cdef"}, + }, + { + testName: "All strings removed, result is nil", + input: []string{"a"}, + remove: "a", + modifier: nil, + want: nil, + }, + { + testName: "No modifier func, one string removed", + input: []string{"a", "ab", "cdef"}, + remove: "ab", + modifier: nil, + want: []string{"a", "cdef"}, + }, + { + testName: "No modifier func, all(three) strings removed", + input: []string{"ab", "a", "ab", "cdef", "ab"}, + remove: "ab", + modifier: nil, + want: []string{"a", "cdef"}, + }, + { + testName: "Removed both the string and the modifier func result", + input: []string{"a", "cd", "ab", "ee"}, + remove: "ee", + modifier: modifier, + want: []string{"a", "cd"}, + }, + } + for _, tt := range tests { + if got := RemoveString(tt.input, tt.remove, tt.modifier); !reflect.DeepEqual(got, tt.want) { + t.Errorf("%v: RemoveString(%v, %q, %T) = %v WANT %v", tt.testName, tt.input, tt.remove, tt.modifier, got, tt.want) + } + } +}