add kubectl/kubeconfig/quota/terminal api, but quota api is tempoary, will be changed as soon

This commit is contained in:
jenkins
2018-06-08 00:50:21 -04:00
committed by jeff
parent e9957a1aa7
commit 7140181e94
55 changed files with 8295 additions and 139 deletions

View File

@@ -0,0 +1,231 @@
/*
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 cronjobs
import (
"encoding/json"
"time"
"github.com/golang/glog"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/jobs/resources"
)
const (
pods = "count/pods"
daemonsets = "count/daemonsets.apps"
deployments = "count/deployments.apps"
ingress = "count/ingresses.extensions"
roles = "count/roles.rbac.authorization.k8s.io"
services = "count/services"
statefulsets = "count/statefulsets.apps"
persistentvolumeclaims = "persistentvolumeclaims"
)
type resourceUsage struct {
NameSpace string
Data v1.ResourceQuotaStatus
UpdateTimeStamp int64
}
type resourceQuotaWorker struct {
k8sClient *kubernetes.Clientset
resChan chan dataType
stopChan chan struct{}
}
func (ru resourceUsage) namespace() string {
return ru.NameSpace
}
type workloadList map[string][]resources.WorkLoadObject
type otherResourceList map[string][]resources.OtherResourceObject
type workload struct {
ResourceType string `json:"type"`
ResourceList workloadList `json:"lists"`
UpdateTimeStamp int64 `json:"updateTimestamp"`
}
type otherResource struct {
ResourceType string `json:"type"`
ResourceList otherResourceList `json:"lists"`
UpdateTimeStamp int64 `json:"updateTimestamp"`
}
var workLoads = []string{"deployments", "daemonsets", "statefulsets"}
var resourceMap = map[string]string{daemonsets: "daemonsets", deployments: "deployments", ingress: "ingresses",
roles: "roles", services: "services", statefulsets: "statefulsets", persistentvolumeclaims: "persistent-volume-claim", pods: "pods"}
func contain(items []string, item string) bool {
for _, v := range items {
if v == item {
return false
}
}
return true
}
func (rw *resourceQuotaWorker) getResourceusage(namespace, resourceName string) (int, error) {
etcdcli, err := client.NewEtcdClient()
if err != nil {
glog.Error(err)
return 0, err
}
defer etcdcli.Close()
key := constants.Root + "/" + resourceName
value, err := etcdcli.Get(key)
if err != nil {
glog.Error(err)
}
if contain(workLoads, resourceName) {
resourceStatus := workload{ResourceList: make(workloadList)}
err := json.Unmarshal(value, &resourceStatus)
if err != nil {
glog.Error(err)
return 0, nil
}
return len(resourceStatus.ResourceList[namespace]), nil
} else {
resourceStatus := otherResource{ResourceList: make(otherResourceList)}
err := json.Unmarshal(value, &resourceStatus)
if err != nil {
glog.Error(err)
return 0, err
}
return len(resourceStatus.ResourceList[namespace]), nil
}
return 0, nil
}
func (rw *resourceQuotaWorker) updateNamespaceQuota(tmpResourceList, resourceList v1.ResourceList) {
if tmpResourceList == nil {
tmpResourceList = resourceList
}
for resource, usage := range resourceList {
tmpUsage, exist := tmpResourceList[resource]
if !exist {
tmpResourceList[resource] = usage
}
if tmpUsage.Cmp(usage) == 1 {
tmpResourceList[resource] = usage
}
}
}
func (rw *resourceQuotaWorker) getNamespaceResourceUsageByQuota(namespace string) (*v1.ResourceQuotaStatus, error) {
quotaList, err := rw.k8sClient.CoreV1().ResourceQuotas(namespace).List(meta_v1.ListOptions{})
if err != nil || len(quotaList.Items) == 0 {
return nil, err
}
quotaStatus := v1.ResourceQuotaStatus{Hard: make(v1.ResourceList), Used: make(v1.ResourceList)}
for _, quota := range quotaList.Items {
rw.updateNamespaceQuota(quotaStatus.Hard, quota.Status.Hard)
rw.updateNamespaceQuota(quotaStatus.Used, quota.Status.Used)
}
return &quotaStatus, nil
}
func (rw *resourceQuotaWorker) getNamespaceQuota(namespace string) (v1.ResourceQuotaStatus, error) {
quota, err := rw.getNamespaceResourceUsageByQuota(namespace)
if err != nil {
return v1.ResourceQuotaStatus{}, err
}
if quota == nil {
quota = new(v1.ResourceQuotaStatus)
quota.Used = make(v1.ResourceList)
}
for k, v := range resourceMap {
if _, exist := quota.Used[v1.ResourceName(k)]; !exist {
used, err := rw.getResourceusage(namespace, v)
if err != nil {
continue
}
var quantity resource.Quantity
quantity.Set(int64(used))
quota.Used[v1.ResourceName(k)] = quantity
}
}
return *quota, nil
}
func (rw *resourceQuotaWorker) workOnce() {
clusterQuota := new(v1.ResourceQuotaStatus)
clusterQuota.Used = make(v1.ResourceList)
namespaces, err := rw.k8sClient.CoreV1().Namespaces().List(meta_v1.ListOptions{})
if err != nil {
glog.Error(err)
return
}
for _, ns := range namespaces.Items {
namespace := ns.Name
nsquota, err := rw.getNamespaceQuota(namespace)
if err != nil {
glog.Error(err)
return
}
res := resourceUsage{NameSpace: namespace, Data: nsquota, UpdateTimeStamp: time.Now().Unix()}
rw.resChan <- res
for k, v := range nsquota.Used {
tmp := clusterQuota.Used[k]
tmp.Add(v)
clusterQuota.Used[k] = tmp
}
}
var quantity resource.Quantity
quantity.Set(int64(len(namespaces.Items)))
clusterQuota.Used["count/namespaces"] = quantity
res := resourceUsage{NameSpace: "\"\"", Data: *clusterQuota, UpdateTimeStamp: time.Now().Unix()}
rw.resChan <- res
}
func (rw *resourceQuotaWorker) chanStop() chan struct{} {
return rw.stopChan
}
func (rw *resourceQuotaWorker) chanRes() chan dataType {
return rw.resChan
}

View File

@@ -0,0 +1,136 @@
/*
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 cronjobs
import (
"encoding/json"
"time"
"github.com/golang/glog"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/constants"
)
var etcdClient *client.EtcdClient
var stopChan = make(chan struct{})
type dataType interface {
namespace() string
}
type Worker interface {
workOnce()
chanRes() chan dataType
chanStop() chan struct{}
}
func registerWorker(workers map[string]Worker, name string) {
glog.Infof("Register cronjob: %s", name)
k8sClient := client.NewK8sClient()
switch name {
case constants.WorkloadStatusKey:
worker := workloadWorker{k8sClient: k8sClient, stopChan: stopChan, resChan: make(chan dataType, 10)}
workers[constants.WorkloadStatusKey] = &worker
case constants.QuotaKey:
worker := resourceQuotaWorker{k8sClient: k8sClient, stopChan: stopChan, resChan: make(chan dataType, 10)}
workers[constants.QuotaKey] = &worker
}
}
func run(worker Worker) {
defer func() {
if err := recover(); err != nil {
glog.Error(err)
close(worker.chanRes())
}
}()
for {
select {
case <-worker.chanStop():
return
default:
break
}
worker.workOnce()
time.Sleep(time.Duration(constants.UpdateCircle) * time.Second)
}
}
func startWorks(workers map[string]Worker) {
for wokername, woker := range workers {
glog.Infof("cronjob %s start to work", wokername)
go run(woker)
}
}
func receiveResourceStatus(workers map[string]Worker) {
defer func() {
close(stopChan)
}()
for {
for name, worker := range workers {
select {
case res, ok := <-worker.chanRes():
if !ok {
glog.Errorf("cronjob:%s have stopped", name)
registerWorker(workers, name)
run(workers[name])
} else {
value, err := json.Marshal(res)
if err != nil {
glog.Error(err)
continue
}
key := constants.Root + "/" + name + "/" + res.namespace()
err = etcdClient.Put(key, string(value))
if err != nil {
glog.Error(err)
}
}
default:
continue
}
}
}
}
func Run() {
glog.Info("Begin to run cronjob")
var err error
etcdClient, err = client.NewEtcdClient()
if err != nil {
glog.Error(err)
}
defer etcdClient.Close()
workers := make(map[string]Worker)
workerList := []string{constants.QuotaKey, constants.WorkloadStatusKey}
for _, name := range workerList {
registerWorker(workers, name)
}
startWorks(workers)
receiveResourceStatus(workers)
}

View File

@@ -0,0 +1,104 @@
package cronjobs
import (
"encoding/json"
"time"
"github.com/golang/glog"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/constants"
)
var workLoadList = []string{"deployments", "daemonsets", "statefulsets"}
type workLoadStatus struct {
NameSpace string
Data map[string]int
UpdateTimeStamp int64
}
func (ws workLoadStatus) namespace() string {
return ws.NameSpace
}
type workloadWorker struct {
k8sClient *kubernetes.Clientset
resChan chan dataType
stopChan chan struct{}
}
func (ww *workloadWorker) GetNamespacesResourceStatus(namespace string) (map[string]int, error) {
cli, err := client.NewEtcdClient()
if err != nil {
glog.Error(err)
return nil, err
}
defer cli.Close()
res := make(map[string]int)
for _, resourceName := range workLoadList {
key := constants.Root + "/" + resourceName
value, err := cli.Get(key)
if err != nil {
continue
}
resourceStatus := workload{ResourceList: make(workloadList)}
err = json.Unmarshal(value, &resourceStatus)
if err != nil {
glog.Error(err)
return nil, err
}
notReady := 0
for _, v := range resourceStatus.ResourceList[namespace] {
if !v.Ready {
notReady++
}
}
res[resourceName] = notReady
}
return res, nil
}
func (ww workloadWorker) workOnce() {
namespaces, err := ww.k8sClient.CoreV1().Namespaces().List(meta_v1.ListOptions{})
if err != nil {
glog.Error(err)
}
resourceStatus := make(map[string]int)
for _, item := range namespaces.Items {
namespace := item.Name
namespacesResourceStatus, err := ww.GetNamespacesResourceStatus(namespace)
if err != nil {
glog.Error(err)
}
var ws = workLoadStatus{UpdateTimeStamp: time.Now().Unix(), Data: namespacesResourceStatus, NameSpace: namespace}
ww.resChan <- ws
for k, v := range namespacesResourceStatus {
resourceStatus[k] = v + resourceStatus[k]
}
}
var ws = workLoadStatus{UpdateTimeStamp: time.Now().Unix(), Data: resourceStatus, NameSpace: "\"\""}
ww.resChan <- ws
}
func (ww workloadWorker) chanRes() chan dataType {
return ww.resChan
}
func (ww workloadWorker) chanStop() chan struct{} {
return ww.stopChan
}

View File

@@ -0,0 +1,108 @@
/*
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 resources
import (
"k8s.io/api/apps/v1beta2"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
)
type daemonset struct {
k8sClient *kubernetes.Clientset
}
func (ds *daemonset) list() (interface{}, error) {
daemonsetList, err := ds.k8sClient.AppsV1beta2().DaemonSets("").List(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return daemonsetList.Items, nil
}
func (ds *daemonset) getWatcher() (watch.Interface, error) {
watcher, err := ds.k8sClient.AppsV1beta2().DaemonSets("").Watch(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return watcher, nil
}
func (ds *daemonset) generateObject(item v1beta2.DaemonSet) WorkLoadObject {
var app string
var ready bool
name := item.Name
namespace := item.Namespace
availablePodNum := item.Status.CurrentNumberScheduled
desirePodNum := item.Status.DesiredNumberScheduled
createTime := item.CreationTimestamp
release := item.ObjectMeta.Labels["release"]
nodeSelector := item.Spec.Template.Spec.NodeSelector
chart := item.ObjectMeta.Labels["chart"]
if len(release) > 0 && len(chart) > 0 {
app = release + "/" + chart
} else {
app = "-"
}
if availablePodNum >= desirePodNum {
ready = true
} else {
ready = false
}
workloadObject := WorkLoadObject{Namespace: namespace, Name: name, Available: availablePodNum, Desire: desirePodNum,
App: app, CreateTime: createTime, Ready: ready, NodeSelector: nodeSelector}
return workloadObject
}
func (ds *daemonset) updateWithObject(status *ResourceStatus, item v1beta2.DaemonSet) {
namespace := item.Namespace
dsObject := ds.generateObject(item)
status.ResourceList.update(namespace, dsObject)
}
func (ds *daemonset) updateWithObjects(status *ResourceStatus, objects interface{}) {
if status.ResourceList == nil {
status.ResourceList = make(Resources)
}
items := objects.([]v1beta2.DaemonSet)
for _, item := range items {
ds.updateWithObject(status, item)
}
}
func (ds *daemonset) updateWithEvent(status *ResourceStatus, event watch.Event) {
object := event.Object.(*v1beta2.DaemonSet)
namespace := object.Namespace
daemonsetObject := ds.generateObject(*object)
if event.Type == watch.Deleted {
status.ResourceList.del(namespace, daemonsetObject)
return
}
ds.updateWithObject(status, *object)
}

View File

@@ -0,0 +1,113 @@
/*
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 resources
import (
"k8s.io/api/apps/v1beta2"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
)
type deployment struct {
k8sClient *kubernetes.Clientset
}
func (deploy *deployment) list() (interface{}, error) {
deoloyList, err := deploy.k8sClient.AppsV1beta2().Deployments("").List(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return deoloyList.Items, nil
}
func (deploy *deployment) getWatcher() (watch.Interface, error) {
watcher, err := deploy.k8sClient.AppsV1beta2().Deployments("").Watch(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return watcher, nil
}
func (deploy *deployment) generateObject(item v1beta2.Deployment) WorkLoadObject {
var app string
var ready bool
var updateTime meta_v1.Time
name := item.Name
namespace := item.Namespace
availablePodNum := item.Status.AvailableReplicas
desirePodNum := *item.Spec.Replicas
release := item.ObjectMeta.Labels["release"]
chart := item.ObjectMeta.Labels["chart"]
if len(release) > 0 && len(chart) > 0 {
app = release + "/" + chart
} else {
app = "-"
}
for _, conditon := range item.Status.Conditions {
if conditon.Type == "Progressing" {
updateTime = conditon.LastUpdateTime
}
}
if availablePodNum >= desirePodNum {
ready = true
} else {
ready = false
}
deployObject := WorkLoadObject{Namespace: namespace, Name: name, Available: availablePodNum, Desire: desirePodNum,
App: app, UpdateTime: updateTime, Ready: ready}
return deployObject
}
func (deploy *deployment) updateWithObject(status *ResourceStatus, item v1beta2.Deployment) {
namespace := item.Namespace
deployObject := deploy.generateObject(item)
status.ResourceList.update(namespace, deployObject)
}
func (deploy *deployment) updateWithObjects(status *ResourceStatus, objects interface{}) {
if status.ResourceList == nil {
status.ResourceList = make(Resources)
}
items := objects.([]v1beta2.Deployment)
for _, item := range items {
deploy.updateWithObject(status, item)
}
}
func (deploy *deployment) updateWithEvent(status *ResourceStatus, event watch.Event) {
object := event.Object.(*v1beta2.Deployment)
namespace := object.Namespace
deployObject := deploy.generateObject(*object)
if event.Type == watch.Deleted {
status.ResourceList.del(namespace, deployObject)
return
}
deploy.updateWithObject(status, *object)
}

View File

@@ -0,0 +1,87 @@
/*
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 resources
import (
"k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
)
type ingress struct {
k8sClient *kubernetes.Clientset
}
func (ing *ingress) list() (interface{}, error) {
list, err := ing.k8sClient.ExtensionsV1beta1().Ingresses("").List(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return list.Items, nil
}
func (ing *ingress) getWatcher() (watch.Interface, error) {
watcher, err := ing.k8sClient.ExtensionsV1beta1().Ingresses("").Watch(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return watcher, nil
}
func (ing *ingress) generateObject(item v1beta1.Ingress) OtherResourceObject {
name := item.Name
ns := item.Namespace
object := OtherResourceObject{Namespace: ns, Name: name}
return object
}
func (ing *ingress) updateWithObject(status *ResourceStatus, item v1beta1.Ingress) {
namespace := item.Namespace
object := ing.generateObject(item)
status.ResourceList.update(namespace, object)
}
func (ing *ingress) updateWithObjects(status *ResourceStatus, objects interface{}) {
if status.ResourceList == nil {
status.ResourceList = make(Resources)
}
items := objects.([]v1beta1.Ingress)
for _, item := range items {
ing.updateWithObject(status, item)
}
}
func (ing *ingress) updateWithEvent(status *ResourceStatus, event watch.Event) {
object := event.Object.(*v1beta1.Ingress)
namespace := object.Namespace
tmpObject := ing.generateObject(*object)
if event.Type == watch.Deleted {
status.ResourceList.del(namespace, tmpObject)
return
}
ing.updateWithObject(status, *object)
}

View File

@@ -0,0 +1,87 @@
/*
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 resources
import (
"k8s.io/api/core/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
)
type namespace struct {
k8sClient *kubernetes.Clientset
}
func (ns *namespace) list() (interface{}, error) {
nsList, err := ns.k8sClient.CoreV1().Namespaces().List(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return nsList.Items, nil
}
func (ns *namespace) getWatcher() (watch.Interface, error) {
watcher, err := ns.k8sClient.CoreV1().Namespaces().Watch(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return watcher, nil
}
func (ns *namespace) generateObject(item v1.Namespace) OtherResourceObject {
name := item.Name
nsp := item.Namespace
object := OtherResourceObject{Namespace: nsp, Name: name}
return object
}
func (ns *namespace) updateWithObject(status *ResourceStatus, item v1.Namespace) {
namespace := item.Namespace
object := ns.generateObject(item)
status.ResourceList.update(namespace, object)
}
func (ns *namespace) updateWithObjects(status *ResourceStatus, objects interface{}) {
if status.ResourceList == nil {
status.ResourceList = make(Resources)
}
items := objects.([]v1.Namespace)
for _, item := range items {
ns.updateWithObject(status, item)
}
}
func (ns *namespace) updateWithEvent(status *ResourceStatus, event watch.Event) {
object := event.Object.(*v1.Namespace)
namespace := object.Namespace
tmpObject := ns.generateObject(*object)
if event.Type == watch.Deleted {
status.ResourceList.del(namespace, tmpObject)
return
}
ns.updateWithObject(status, *object)
}

View File

@@ -0,0 +1,87 @@
/*
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 resources
import (
"k8s.io/api/core/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
)
type pod struct {
k8sClient *kubernetes.Clientset
}
func (po *pod) list() (interface{}, error) {
list, err := po.k8sClient.CoreV1().Pods("").List(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return list.Items, nil
}
func (po *pod) getWatcher() (watch.Interface, error) {
watcher, err := po.k8sClient.CoreV1().Pods("").Watch(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return watcher, nil
}
func (po *pod) generateObject(item v1.Pod) OtherResourceObject {
name := item.Name
ns := item.Namespace
Object := OtherResourceObject{Namespace: ns, Name: name}
return Object
}
func (po *pod) updateWithObject(status *ResourceStatus, item v1.Pod) {
namespace := item.Namespace
object := po.generateObject(item)
status.ResourceList.update(namespace, object)
}
func (po *pod) updateWithObjects(status *ResourceStatus, objects interface{}) {
if status.ResourceList == nil {
status.ResourceList = make(Resources)
}
items := objects.([]v1.Pod)
for _, item := range items {
po.updateWithObject(status, item)
}
}
func (po *pod) updateWithEvent(status *ResourceStatus, event watch.Event) {
object := event.Object.(*v1.Pod)
namespace := object.Namespace
tmpObject := po.generateObject(*object)
if event.Type == watch.Deleted {
status.ResourceList.del(namespace, tmpObject)
return
}
po.updateWithObject(status, *object)
}

View File

@@ -0,0 +1,87 @@
/*
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 resources
import (
"k8s.io/api/core/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
)
type persistmentVolume struct {
k8sClient *kubernetes.Clientset
}
func (pvc *persistmentVolume) list() (interface{}, error) {
list, err := pvc.k8sClient.CoreV1().PersistentVolumeClaims("").List(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return list.Items, nil
}
func (pvc *persistmentVolume) getWatcher() (watch.Interface, error) {
watcher, err := pvc.k8sClient.CoreV1().PersistentVolumeClaims("").Watch(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return watcher, nil
}
func (pvc *persistmentVolume) generateObject(item v1.PersistentVolumeClaim) OtherResourceObject {
name := item.Name
ns := item.Namespace
object := OtherResourceObject{Namespace: ns, Name: name}
return object
}
func (pvc *persistmentVolume) updateWithObject(status *ResourceStatus, item v1.PersistentVolumeClaim) {
namespace := item.Namespace
object := pvc.generateObject(item)
status.ResourceList.update(namespace, object)
}
func (pvc *persistmentVolume) updateWithObjects(status *ResourceStatus, objects interface{}) {
if status.ResourceList == nil {
status.ResourceList = make(Resources)
}
items := objects.([]v1.PersistentVolumeClaim)
for _, item := range items {
pvc.updateWithObject(status, item)
}
}
func (pvc *persistmentVolume) updateWithEvent(status *ResourceStatus, event watch.Event) {
object := event.Object.(*v1.PersistentVolumeClaim)
namespace := object.Namespace
tmpObject := pvc.generateObject(*object)
if event.Type == watch.Deleted {
status.ResourceList.del(namespace, tmpObject)
return
}
pvc.updateWithObject(status, *object)
}

View File

@@ -0,0 +1,87 @@
/*
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 resources
import (
"k8s.io/api/rbac/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
)
type role struct {
k8sClient *kubernetes.Clientset
}
func (r *role) list() (interface{}, error) {
list, err := r.k8sClient.RbacV1().Roles("").List(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return list.Items, nil
}
func (r *role) getWatcher() (watch.Interface, error) {
watcher, err := r.k8sClient.RbacV1().Roles("").Watch(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return watcher, nil
}
func (r *role) generateObject(item v1.Role) OtherResourceObject {
name := item.Name
ns := item.Namespace
object := OtherResourceObject{Namespace: ns, Name: name}
return object
}
func (r *role) updateWithObject(status *ResourceStatus, item v1.Role) {
namespace := item.Namespace
object := r.generateObject(item)
status.ResourceList.update(namespace, object)
}
func (r *role) updateWithObjects(status *ResourceStatus, objects interface{}) {
if status.ResourceList == nil {
status.ResourceList = make(Resources)
}
items := objects.([]v1.Role)
for _, item := range items {
r.updateWithObject(status, item)
}
}
func (r *role) updateWithEvent(status *ResourceStatus, event watch.Event) {
object := event.Object.(*v1.Role)
namespace := object.Namespace
tmpObject := r.generateObject(*object)
if event.Type == watch.Deleted {
status.ResourceList.del(namespace, tmpObject)
return
}
r.updateWithObject(status, *object)
}

View File

@@ -0,0 +1,178 @@
/*
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 resources
import (
"encoding/json"
"time"
"github.com/golang/glog"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/constants"
)
var etcdClient *client.EtcdClient
var stopChan = make(chan struct{})
const (
pods = "pods"
deployments = "deployments"
daemonsets = "daemonsets"
statefulsets = "statefulsets"
namespaces = "namespaces"
ingresses = "ingresses"
persistentVolumeClaim = "persistent-volume-claim"
roles = "roles"
services = "services"
)
func registerResource(resourceChans map[string]ResourceChan, resourceType string) {
resourceChan := ResourceChan{Type: resourceType, StatusChan: make(chan *ResourceStatus), StopChan: stopChan}
resourceChans[resourceType] = resourceChan
}
func updateStatus(resource Resource, resourceChan ResourceChan) {
defer func() {
if err := recover(); err != nil {
glog.Error(err)
close(resourceChan.StatusChan)
}
}()
var clusterStatus ResourceStatus
clusterStatus.UpdateTimeStamp = time.Now().Unix()
clusterStatus.ResourceType = resourceChan.Type
items, err := resource.list()
if err != nil {
glog.Errorln(err)
return
}
resource.updateWithObjects(&clusterStatus, items)
watcher, err := resource.getWatcher()
if err != nil {
glog.Error(err)
return
}
for {
select {
case <-resourceChan.StopChan:
return
case event := <-watcher.ResultChan():
resource.updateWithEvent(&clusterStatus, event)
break
default:
break
}
if time.Now().Unix()-clusterStatus.UpdateTimeStamp > constants.UpdateCircle {
clusterStatus.UpdateTimeStamp = time.Now().Unix()
resourceChan.StatusChan <- &clusterStatus
}
}
}
func updateResourceStatus(resourceChan ResourceChan) {
glog.Infof("updateResourceStatus:%s", resourceChan.Type)
client := client.NewK8sClient()
switch resourceChan.Type {
case deployments:
deploy := deployment{k8sClient: client}
go updateStatus(&deploy, resourceChan)
case daemonsets:
ds := daemonset{k8sClient: client}
go updateStatus(&ds, resourceChan)
case statefulsets:
ss := statefulset{k8sClient: client}
go updateStatus(&ss, resourceChan)
case namespaces:
ns := namespace{k8sClient: client}
go updateStatus(&ns, resourceChan)
case ingresses:
ing := ingress{k8sClient: client}
go updateStatus(&ing, resourceChan)
case persistentVolumeClaim:
pvc := persistmentVolume{k8sClient: client}
go updateStatus(&pvc, resourceChan)
case roles:
r := role{k8sClient: client}
go updateStatus(&r, resourceChan)
case services:
svc := service{k8sClient: client}
go updateStatus(&svc, resourceChan)
case pods:
po := pod{k8sClient: client}
go updateStatus(&po, resourceChan)
}
}
func updateAllResourceStatus(resourceChans map[string]ResourceChan) {
for _, resourceChan := range resourceChans {
updateResourceStatus(resourceChan)
}
}
func receiveResourceStatus(resourceChans map[string]ResourceChan) {
defer func() {
close(stopChan)
}()
for {
for _, resourceChan := range resourceChans {
select {
case res, ok := <-resourceChan.StatusChan:
if !ok {
glog.Errorf("job:calculate %s' status have stopped", resourceChan.Type)
registerResource(resourceChans, resourceChan.Type)
updateResourceStatus(resourceChans[resourceChan.Type])
} else {
value, _ := json.Marshal(res)
key := constants.Root + "/" + res.ResourceType
etcdClient.Put(key, string(value))
}
default:
continue
}
}
}
}
func Run() {
glog.Info("Begin to submit resource status")
var err error
etcdClient, err = client.NewEtcdClient()
defer etcdClient.Close()
if err != nil {
glog.Error(err)
}
resourceChans := make(map[string]ResourceChan)
resourceList := []string{statefulsets, deployments, daemonsets, namespaces, ingresses, services, roles, persistentVolumeClaim, pods}
for _, resource := range resourceList {
registerResource(resourceChans, resource)
}
updateAllResourceStatus(resourceChans)
receiveResourceStatus(resourceChans)
}

View File

@@ -0,0 +1,87 @@
/*
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 resources
import (
"k8s.io/api/core/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
)
type service struct {
k8sClient *kubernetes.Clientset
}
func (svc *service) list() (interface{}, error) {
list, err := svc.k8sClient.CoreV1().Services("").List(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return list.Items, nil
}
func (svc *service) getWatcher() (watch.Interface, error) {
watcher, err := svc.k8sClient.CoreV1().Services("").Watch(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return watcher, nil
}
func (svc *service) generateObject(item v1.Service) OtherResourceObject {
name := item.Name
ns := item.Namespace
object := OtherResourceObject{Namespace: ns, Name: name}
return object
}
func (svc *service) updateWithObject(status *ResourceStatus, item v1.Service) {
namespace := item.Namespace
object := svc.generateObject(item)
status.ResourceList.update(namespace, object)
}
func (svc *service) updateWithObjects(status *ResourceStatus, objects interface{}) {
if status.ResourceList == nil {
status.ResourceList = make(Resources)
}
items := objects.([]v1.Service)
for _, item := range items {
svc.updateWithObject(status, item)
}
}
func (svc *service) updateWithEvent(status *ResourceStatus, event watch.Event) {
object := event.Object.(*v1.Service)
namespace := object.Namespace
tmpObject := svc.generateObject(*object)
if event.Type == watch.Deleted {
status.ResourceList.del(namespace, tmpObject)
return
}
svc.updateWithObject(status, *object)
}

View File

@@ -0,0 +1,107 @@
/*
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 resources
import (
"k8s.io/api/apps/v1beta2"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
)
type statefulset struct {
k8sClient *kubernetes.Clientset
}
func (ss *statefulset) list() (interface{}, error) {
daemonsetList, err := ss.k8sClient.AppsV1beta2().StatefulSets("").List(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return daemonsetList.Items, nil
}
func (ss *statefulset) getWatcher() (watch.Interface, error) {
watcher, err := ss.k8sClient.AppsV1beta2().StatefulSets("").Watch(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
return watcher, nil
}
func (ss *statefulset) generateObject(item v1beta2.StatefulSet) WorkLoadObject {
var app string
var ready bool
name := item.Name
namespace := item.Namespace
availablePodNum := item.Status.ReadyReplicas
desirePodNum := *item.Spec.Replicas
createTime := item.CreationTimestamp
release := item.ObjectMeta.Labels["release"]
chart := item.ObjectMeta.Labels["chart"]
if len(release) > 0 && len(chart) > 0 {
app = release + "/" + chart
} else {
app = "-"
}
if availablePodNum >= desirePodNum {
ready = true
} else {
ready = false
}
statefulSetObject := WorkLoadObject{Namespace: namespace, Name: name, Available: availablePodNum, Desire: desirePodNum,
App: app, CreateTime: createTime, Ready: ready}
return statefulSetObject
}
func (ss *statefulset) updateWithObject(status *ResourceStatus, item v1beta2.StatefulSet) {
namespace := item.Namespace
ssObject := ss.generateObject(item)
status.ResourceList.update(namespace, ssObject)
}
func (ss *statefulset) updateWithObjects(status *ResourceStatus, objects interface{}) {
if status.ResourceList == nil {
status.ResourceList = make(Resources)
}
items := objects.([]v1beta2.StatefulSet)
for _, item := range items {
ss.updateWithObject(status, item)
}
}
func (ss *statefulset) updateWithEvent(status *ResourceStatus, event watch.Event) {
object := event.Object.(*v1beta2.StatefulSet)
namespace := object.Namespace
ssObject := ss.generateObject(*object)
if event.Type == watch.Deleted {
status.ResourceList.del(namespace, ssObject)
return
}
ss.updateWithObject(status, *object)
}

View File

@@ -0,0 +1,105 @@
/*
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 resources
import (
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
)
type resource interface {
equal(object resource) bool
}
type resourcetList interface {
update(key string, value resource)
del(key string, value resource)
}
type ResourceStatus struct {
ResourceType string `json:"type"`
ResourceList resourcetList `json:"lists"`
UpdateTimeStamp int64 `json:"updateTimestamp"`
}
type ResourceChan struct {
Type string
StatusChan chan *ResourceStatus
StopChan chan struct{}
}
type Resource interface {
list() (interface{}, error)
getWatcher() (watch.Interface, error)
updateWithObjects(workload *ResourceStatus, objects interface{})
updateWithEvent(workload *ResourceStatus, event watch.Event)
}
type WorkLoadObject struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
App string `json:"app"`
Available int32 `json:"available"`
Desire int32 `json:"desire"`
Ready bool `json:"ready"`
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
UpdateTime meta_v1.Time `json:"updateTime,omitempty"`
CreateTime meta_v1.Time `json:"createTime,omitempty"`
}
type OtherResourceObject struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
}
type Resources map[string][]resource
func (resources Resources) update(namespace string, object resource) {
for index, tmpObject := range resources[namespace] {
if tmpObject.equal(object) {
resources[namespace][index] = object
return
}
}
resources[namespace] = append(resources[namespace], object)
}
func (resources Resources) del(namespace string, object resource) {
for index, tmpObject := range resources[namespace] {
if tmpObject.equal(object) {
resources[namespace] = append(resources[namespace][:index], resources[namespace][index+1:]...)
return
}
}
}
func (workLoadObject WorkLoadObject) equal(object resource) bool {
tmp := object.(WorkLoadObject)
if workLoadObject.Name == tmp.Name && workLoadObject.Namespace == tmp.Namespace {
return true
}
return false
}
func (otherResourceObject OtherResourceObject) equal(object resource) bool {
tmp := object.(OtherResourceObject)
if otherResourceObject.Name == tmp.Name && otherResourceObject.Namespace == tmp.Namespace {
return true
}
return false
}