Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ClusterMapInfo Injects Local Cluster Info on Empty #917

Merged
merged 2 commits into from
Sep 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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