|
|
@@ -19,8 +19,6 @@ import (
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
|
"k8s.io/client-go/kubernetes"
|
|
|
- "k8s.io/client-go/tools/cache"
|
|
|
- "k8s.io/client-go/util/workqueue"
|
|
|
"k8s.io/klog"
|
|
|
)
|
|
|
|
|
|
@@ -44,52 +42,20 @@ const (
|
|
|
)
|
|
|
|
|
|
type CostModel struct {
|
|
|
- Controller *Controller
|
|
|
-}
|
|
|
+ Cache ClusterCache
|
|
|
|
|
|
-func NewCostModel(podListWatcher cache.ListerWatcher) *CostModel {
|
|
|
- cm := &CostModel{}
|
|
|
- cm.ContainerWatcher(podListWatcher)
|
|
|
- return cm
|
|
|
+ stop chan struct{}
|
|
|
}
|
|
|
|
|
|
-func (cm *CostModel) ContainerWatcher(podListWatcher cache.ListerWatcher) {
|
|
|
-
|
|
|
- // create the workqueue
|
|
|
- queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
|
|
|
+func NewCostModel(client kubernetes.Interface) *CostModel {
|
|
|
+ stopCh := make(chan struct{})
|
|
|
+ cache := NewKubernetesClusterCache(client)
|
|
|
+ cache.Run(stopCh)
|
|
|
|
|
|
- // Bind the workqueue to a cache with the help of an informer. This way we make sure that
|
|
|
- // whenever the cache is updated, the pod key is added to the workqueue.
|
|
|
- // Note that when we finally process the item from the workqueue, we might see a newer version
|
|
|
- // of the Pod than the version which was responsible for triggering the update.
|
|
|
- indexer, informer := cache.NewIndexerInformer(podListWatcher, &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{
|
|
|
- AddFunc: func(obj interface{}) {
|
|
|
- key, err := cache.MetaNamespaceKeyFunc(obj)
|
|
|
- if err == nil {
|
|
|
- queue.Add(key)
|
|
|
- }
|
|
|
- },
|
|
|
- UpdateFunc: func(old interface{}, new interface{}) {
|
|
|
- key, err := cache.MetaNamespaceKeyFunc(new)
|
|
|
- if err == nil {
|
|
|
- queue.Add(key)
|
|
|
- }
|
|
|
- },
|
|
|
- DeleteFunc: func(obj interface{}) {
|
|
|
- // IndexerInformer uses a delta queue, therefore for deletes we have to use this
|
|
|
- // key function.
|
|
|
- key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
|
|
|
- if err == nil {
|
|
|
- queue.Add(key)
|
|
|
- }
|
|
|
- },
|
|
|
- }, cache.Indexers{})
|
|
|
-
|
|
|
- cm.Controller = NewController(queue, indexer, informer)
|
|
|
- // Now let's start the controller
|
|
|
- stop := make(chan struct{})
|
|
|
- //defer close(stop)
|
|
|
- go cm.Controller.Run(1, stop)
|
|
|
+ return &CostModel{
|
|
|
+ Cache: cache,
|
|
|
+ stop: stopCh,
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
type CostData struct {
|
|
|
@@ -334,21 +300,21 @@ func (cm *CostModel) ComputeCostData(cli prometheusClient.Client, clientset kube
|
|
|
podDeploymentsMapping := make(map[string]map[string][]string)
|
|
|
podServicesMapping := make(map[string]map[string][]string)
|
|
|
namespaceLabelsMapping := make(map[string]map[string]string)
|
|
|
- podlist := cm.Controller.GetAll()
|
|
|
+ podlist := cm.Cache.GetAllPods()
|
|
|
var k8sErr error
|
|
|
go func() {
|
|
|
defer wg.Done()
|
|
|
|
|
|
- podDeploymentsMapping, k8sErr = getPodDeployments(clientset, podlist)
|
|
|
+ podDeploymentsMapping, k8sErr = getPodDeployments(cm.Cache, podlist)
|
|
|
if k8sErr != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- podServicesMapping, k8sErr = getPodServices(clientset, podlist)
|
|
|
+ podServicesMapping, k8sErr = getPodServices(cm.Cache, podlist)
|
|
|
if k8sErr != nil {
|
|
|
return
|
|
|
}
|
|
|
- namespaceLabelsMapping, k8sErr = getNamespaceLabels(clientset)
|
|
|
+ namespaceLabelsMapping, k8sErr = getNamespaceLabels(cm.Cache)
|
|
|
if k8sErr != nil {
|
|
|
return
|
|
|
}
|
|
|
@@ -369,7 +335,7 @@ func (cm *CostModel) ComputeCostData(cli prometheusClient.Client, clientset kube
|
|
|
return nil, fmt.Errorf("Error parsing normalization values: " + err.Error())
|
|
|
}
|
|
|
|
|
|
- nodes, err := getNodeCost(clientset, cloud)
|
|
|
+ nodes, err := getNodeCost(cm.Cache, cloud)
|
|
|
if err != nil {
|
|
|
klog.V(1).Infof("Warning, no Node cost model available: " + err.Error())
|
|
|
return nil, err
|
|
|
@@ -380,7 +346,7 @@ func (cm *CostModel) ComputeCostData(cli prometheusClient.Client, clientset kube
|
|
|
klog.Infof("Unable to get PV Data: %s", err.Error())
|
|
|
}
|
|
|
if pvClaimMapping != nil {
|
|
|
- err = addPVData(clientset, pvClaimMapping, cloud)
|
|
|
+ err = addPVData(cm.Cache, pvClaimMapping, cloud)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
@@ -841,17 +807,14 @@ func getContainerAllocation(req []*Vector, used []*Vector) []*Vector {
|
|
|
|
|
|
return allocation
|
|
|
}
|
|
|
-func addPVData(clientset kubernetes.Interface, pvClaimMapping map[string]*PersistentVolumeClaimData, cloud costAnalyzerCloud.Provider) error {
|
|
|
+func addPVData(cache ClusterCache, pvClaimMapping map[string]*PersistentVolumeClaimData, cloud costAnalyzerCloud.Provider) error {
|
|
|
cfg, err := cloud.GetConfig()
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- storageClasses, err := clientset.StorageV1().StorageClasses().List(metav1.ListOptions{})
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
+ storageClasses := cache.GetAllStorageClasses()
|
|
|
storageClassMap := make(map[string]map[string]string)
|
|
|
- for _, storageClass := range storageClasses.Items {
|
|
|
+ for _, storageClass := range storageClasses {
|
|
|
params := storageClass.Parameters
|
|
|
storageClassMap[storageClass.ObjectMeta.Name] = params
|
|
|
if storageClass.GetAnnotations()["storageclass.kubernetes.io/is-default-class"] == "true" || storageClass.GetAnnotations()["storageclass.beta.kubernetes.io/is-default-class"] == "true" {
|
|
|
@@ -860,12 +823,9 @@ func addPVData(clientset kubernetes.Interface, pvClaimMapping map[string]*Persis
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- pvs, err := clientset.CoreV1().PersistentVolumes().List(metav1.ListOptions{})
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
+ pvs := cache.GetAllPersistentVolumes()
|
|
|
pvMap := make(map[string]*costAnalyzerCloud.PV)
|
|
|
- for _, pv := range pvs.Items {
|
|
|
+ for _, pv := range pvs {
|
|
|
parameters, ok := storageClassMap[pv.Spec.StorageClassName]
|
|
|
if !ok {
|
|
|
klog.V(4).Infof("Unable to find parameters for storage class \"%s\". Does pv \"%s\" have a storageClassName?", pv.Spec.StorageClassName, pv.Name)
|
|
|
@@ -875,7 +835,7 @@ func addPVData(clientset kubernetes.Interface, pvClaimMapping map[string]*Persis
|
|
|
Region: pv.Labels[v1.LabelZoneRegion],
|
|
|
Parameters: parameters,
|
|
|
}
|
|
|
- err := GetPVCost(cacPv, &pv, cloud)
|
|
|
+ err := GetPVCost(cacPv, pv, cloud)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
@@ -914,17 +874,14 @@ func GetPVCost(pv *costAnalyzerCloud.PV, kpv *v1.PersistentVolume, cloud costAna
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func getNodeCost(clientset kubernetes.Interface, cloud costAnalyzerCloud.Provider) (map[string]*costAnalyzerCloud.Node, error) {
|
|
|
+func getNodeCost(cache ClusterCache, cloud costAnalyzerCloud.Provider) (map[string]*costAnalyzerCloud.Node, error) {
|
|
|
cfg, err := cloud.GetConfig()
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
- nodeList, err := clientset.CoreV1().Nodes().List(metav1.ListOptions{})
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
+ nodeList := cache.GetAllNodes()
|
|
|
nodes := make(map[string]*costAnalyzerCloud.Node)
|
|
|
- for _, n := range nodeList.Items {
|
|
|
+ for _, n := range nodeList {
|
|
|
name := n.GetObjectMeta().GetName()
|
|
|
nodeLabels := n.GetObjectMeta().GetLabels()
|
|
|
nodeLabels["providerID"] = n.Spec.ProviderID
|
|
|
@@ -1044,13 +1001,10 @@ func getNodeCost(clientset kubernetes.Interface, cloud costAnalyzerCloud.Provide
|
|
|
return nodes, nil
|
|
|
}
|
|
|
|
|
|
-func getPodServices(clientset kubernetes.Interface, podList []*v1.Pod) (map[string]map[string][]string, error) {
|
|
|
- servicesList, err := clientset.CoreV1().Services("").List(metav1.ListOptions{})
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
+func getPodServices(cache ClusterCache, podList []*v1.Pod) (map[string]map[string][]string, error) {
|
|
|
+ servicesList := cache.GetAllServices()
|
|
|
podServicesMapping := make(map[string]map[string][]string)
|
|
|
- for _, service := range servicesList.Items {
|
|
|
+ for _, service := range servicesList {
|
|
|
namespace := service.GetObjectMeta().GetNamespace()
|
|
|
name := service.GetObjectMeta().GetName()
|
|
|
|
|
|
@@ -1073,13 +1027,10 @@ func getPodServices(clientset kubernetes.Interface, podList []*v1.Pod) (map[stri
|
|
|
return podServicesMapping, nil
|
|
|
}
|
|
|
|
|
|
-func getPodDeployments(clientset kubernetes.Interface, podList []*v1.Pod) (map[string]map[string][]string, error) {
|
|
|
- deploymentsList, err := clientset.AppsV1().Deployments("").List(metav1.ListOptions{})
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
+func getPodDeployments(cache ClusterCache, podList []*v1.Pod) (map[string]map[string][]string, error) {
|
|
|
+ deploymentsList := cache.GetAllDeployments()
|
|
|
podDeploymentsMapping := make(map[string]map[string][]string) // namespace: podName: [deploymentNames]
|
|
|
- for _, deployment := range deploymentsList.Items {
|
|
|
+ for _, deployment := range deploymentsList {
|
|
|
namespace := deployment.GetObjectMeta().GetNamespace()
|
|
|
name := deployment.GetObjectMeta().GetName()
|
|
|
if _, ok := podDeploymentsMapping[namespace]; !ok {
|
|
|
@@ -1183,20 +1134,20 @@ func (cm *CostModel) ComputeCostDataRange(cli prometheusClient.Client, clientset
|
|
|
podDeploymentsMapping := make(map[string]map[string][]string)
|
|
|
podServicesMapping := make(map[string]map[string][]string)
|
|
|
namespaceLabelsMapping := make(map[string]map[string]string)
|
|
|
- podlist := cm.Controller.GetAll()
|
|
|
+ podlist := cm.Cache.GetAllPods()
|
|
|
var k8sErr error
|
|
|
go func() {
|
|
|
|
|
|
- podDeploymentsMapping, k8sErr = getPodDeployments(clientset, podlist)
|
|
|
+ podDeploymentsMapping, k8sErr = getPodDeployments(cm.Cache, podlist)
|
|
|
if k8sErr != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- podServicesMapping, k8sErr = getPodServices(clientset, podlist)
|
|
|
+ podServicesMapping, k8sErr = getPodServices(cm.Cache, podlist)
|
|
|
if k8sErr != nil {
|
|
|
return
|
|
|
}
|
|
|
- namespaceLabelsMapping, k8sErr = getNamespaceLabels(clientset)
|
|
|
+ namespaceLabelsMapping, k8sErr = getNamespaceLabels(cm.Cache)
|
|
|
if k8sErr != nil {
|
|
|
return
|
|
|
}
|
|
|
@@ -1218,7 +1169,7 @@ func (cm *CostModel) ComputeCostDataRange(cli prometheusClient.Client, clientset
|
|
|
return nil, fmt.Errorf("Error parsing normalization values: " + err.Error())
|
|
|
}
|
|
|
|
|
|
- nodes, err := getNodeCost(clientset, cloud)
|
|
|
+ nodes, err := getNodeCost(cm.Cache, cloud)
|
|
|
if err != nil {
|
|
|
klog.V(1).Infof("Warning, no cost model available: " + err.Error())
|
|
|
return nil, err
|
|
|
@@ -1230,7 +1181,7 @@ func (cm *CostModel) ComputeCostDataRange(cli prometheusClient.Client, clientset
|
|
|
klog.Infof("Unable to get PV Data: %s", err.Error())
|
|
|
}
|
|
|
if pvClaimMapping != nil {
|
|
|
- err = addPVData(clientset, pvClaimMapping, cloud)
|
|
|
+ err = addPVData(cm.Cache, pvClaimMapping, cloud)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
@@ -1498,13 +1449,10 @@ func (cm *CostModel) ComputeCostDataRange(cli prometheusClient.Client, clientset
|
|
|
return containerNameCost, err
|
|
|
}
|
|
|
|
|
|
-func getNamespaceLabels(clientset kubernetes.Interface) (map[string]map[string]string, error) {
|
|
|
+func getNamespaceLabels(cache ClusterCache) (map[string]map[string]string, error) {
|
|
|
nsToLabels := make(map[string]map[string]string)
|
|
|
- nss, err := clientset.CoreV1().Namespaces().List(metav1.ListOptions{})
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
- for _, ns := range nss.Items {
|
|
|
+ nss := cache.GetAllNamespaces()
|
|
|
+ for _, ns := range nss {
|
|
|
nsToLabels[ns.Name] = ns.Labels
|
|
|
}
|
|
|
return nsToLabels, nil
|