Skip to content

Commit

Permalink
Merge pull request #917 from kubecost/bolt/cluster-info-local
Browse files Browse the repository at this point in the history
ClusterMapInfo Injects Local Cluster Info on Empty
  • Loading branch information
mbolt35 committed Sep 3, 2021
2 parents f90ee32 + 156604e commit ca90b93
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 11 deletions.
22 changes: 22 additions & 0 deletions pkg/costmodel/clusterinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

cloudProvider "github.com/kubecost/cost-model/pkg/cloud"
"github.com/kubecost/cost-model/pkg/costmodel/clusters"
"github.com/kubecost/cost-model/pkg/env"
"github.com/kubecost/cost-model/pkg/thanos"

Expand Down Expand Up @@ -40,6 +41,27 @@ func writeThanosFlags(clusterInfo map[string]string) {
}
}

// default local cluster info provider implementation which provides an instanced object for
// getting the local cluster info
type defaultLocalClusterInfoProvider struct {
k8s kubernetes.Interface
provider cloudProvider.Provider
}

// GetClusterInfo returns a string map containing the local cluster info
func (dlcip *defaultLocalClusterInfoProvider) GetClusterInfo() map[string]string {
return GetClusterInfo(dlcip.k8s, dlcip.provider)
}

// NewLocalClusterInfoProvider creates a new clusters.LocalClusterInfoProvider implementation for providing local
// cluster information
func NewLocalClusterInfoProvider(k8s kubernetes.Interface, cloud cloudProvider.Provider) clusters.LocalClusterInfoProvider {
return &defaultLocalClusterInfoProvider{
k8s: k8s,
provider: cloud,
}
}

// GetClusterInfo provides specific information about the cluster cloud provider as well as
// generic configuration values.
func GetClusterInfo(kubeClient kubernetes.Interface, cloud cloudProvider.Provider) map[string]string {
Expand Down
79 changes: 70 additions & 9 deletions pkg/costmodel/clusters/clustermap.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"sync"
"time"

"github.com/kubecost/cost-model/pkg/env"
"github.com/kubecost/cost-model/pkg/log"
"github.com/kubecost/cost-model/pkg/prom"
"github.com/kubecost/cost-model/pkg/thanos"
Expand Down Expand Up @@ -68,23 +69,31 @@ type ClusterMap interface {
StopRefresh()
}

// LocalClusterInfoProvider is a contract which is capable of performing local cluster info lookups.
type LocalClusterInfoProvider interface {
// GetClusterInfo returns a string map containing the local cluster info
GetClusterInfo() map[string]string
}

// ClusterMap keeps records of all known cost-model clusters.
type PrometheusClusterMap struct {
lock *sync.RWMutex
client prometheus.Client
clusters map[string]*ClusterInfo
stop chan struct{}
lock *sync.RWMutex
client prometheus.Client
clusters map[string]*ClusterInfo
localCluster LocalClusterInfoProvider
stop chan struct{}
}

// NewClusterMap creates a new ClusterMap implementation using a prometheus or thanos client
func NewClusterMap(client prometheus.Client, refresh time.Duration) ClusterMap {
func NewClusterMap(client prometheus.Client, lcip LocalClusterInfoProvider, refresh time.Duration) ClusterMap {
stop := make(chan struct{})

cm := &PrometheusClusterMap{
lock: new(sync.RWMutex),
client: client,
clusters: make(map[string]*ClusterInfo),
stop: stop,
lock: new(sync.RWMutex),
client: client,
clusters: make(map[string]*ClusterInfo),
localCluster: lcip,
stop: stop,
}

// Run an updater to ensure cluster data stays relevant over time
Expand Down Expand Up @@ -175,9 +184,61 @@ func (pcm *PrometheusClusterMap) loadClusters() (map[string]*ClusterInfo, error)
}
}

// populate the local cluster if it doesn't exist
localID := env.GetClusterID()
if _, ok := clusters[localID]; !ok {
localInfo, err := pcm.getLocalClusterInfo()
if err != nil {
log.Warningf("Failed to load local cluster info: %s", err)
} else {
clusters[localInfo.ID] = localInfo
}
}

return clusters, nil
}

// getLocalClusterInfo returns the local cluster info in the event there does not exist a metric available.
func (pcm *PrometheusClusterMap) getLocalClusterInfo() (*ClusterInfo, error) {
info := pcm.localCluster.GetClusterInfo()

var id string
var name string

if i, ok := info["id"]; ok {
id = i
} else {
return nil, fmt.Errorf("Local Cluster Info Missing ID")
}
if n, ok := info["name"]; ok {
name = n
} else {
return nil, fmt.Errorf("Local Cluster Info Missing Name")
}

var clusterProfile string
var provider string
var provisioner string

if cp, ok := info["clusterProfile"]; ok {
clusterProfile = cp
}
if pvdr, ok := info["provider"]; ok {
provider = pvdr
}
if pvsr, ok := info["provisioner"]; ok {
provisioner = pvsr
}

return &ClusterInfo{
ID: id,
Name: name,
Profile: clusterProfile,
Provider: provider,
Provisioner: provisioner,
}, nil
}

// refreshClusters loads the clusters and updates the internal map
func (pcm *PrometheusClusterMap) refreshClusters() {
updated, err := pcm.loadClusters()
Expand Down
5 changes: 3 additions & 2 deletions pkg/costmodel/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -1167,10 +1167,11 @@ func Initialize(additionalConfigWatchers ...ConfigWatchers) *Accesses {

// Initialize ClusterMap for maintaining ClusterInfo by ClusterID
var clusterMap clusters.ClusterMap
localCIProvider := NewLocalClusterInfoProvider(kubeClientset, cloudProvider)
if thanosClient != nil {
clusterMap = clusters.NewClusterMap(thanosClient, 10*time.Minute)
clusterMap = clusters.NewClusterMap(thanosClient, localCIProvider, 10*time.Minute)
} else {
clusterMap = clusters.NewClusterMap(promCli, 5*time.Minute)
clusterMap = clusters.NewClusterMap(promCli, localCIProvider, 5*time.Minute)
}

// cache responses from model and aggregation for a default of 10 minutes;
Expand Down

0 comments on commit ca90b93

Please sign in to comment.