Commit e59a17f2 authored by qmhu's avatar qmhu
Browse files

add recommendation apis

parent cf40b769
Showing with 219 additions and 841 deletions
+219 -841
......@@ -46,7 +46,6 @@ import (
_ "github.com/gocrane/crane/pkg/querybuilder-providers/prometheus"
"github.com/gocrane/crane/pkg/server"
serverconfig "github.com/gocrane/crane/pkg/server/config"
"github.com/gocrane/crane/pkg/server/handler/prediction"
"github.com/gocrane/crane/pkg/utils/target"
"github.com/gocrane/crane/pkg/webhooks"
)
......@@ -346,24 +345,16 @@ func runAll(ctx context.Context, mgr ctrl.Manager, predictorMgr predictor.Manage
if err := opts.ApplyTo(serverConfig); err != nil {
klog.Exit(err)
}
// use controller runtime rest config, we can not refer kubeconfig option directly because it is unexported variable in vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go
serverConfig.KubeRestConfig = mgr.GetConfig()
serverConfig.KubeConfig = mgr.GetConfig()
serverConfig.Client = mgr.GetClient()
serverConfig.Scheme = mgr.GetScheme()
serverConfig.RestMapper = mgr.GetRESTMapper()
serverConfig.PredictorMgr = predictorMgr
craneServer, err := server.NewServer(serverConfig)
if err != nil {
klog.Exit(err)
}
discoveryClientSet, err := discovery.NewDiscoveryClientForConfig(mgr.GetConfig())
if err != nil {
klog.Exit(err, "Unable to create discover client")
}
scaleKindResolver := scale.NewDiscoveryScaleKindResolver(discoveryClientSet)
scaleClient := scale.New(discoveryClientSet.RESTClient(), mgr.GetRESTMapper(), dynamic.LegacyAPIPathResolverFunc, scaleKindResolver)
selectorFetcher := target.NewSelectorFetcher(mgr.GetScheme(), mgr.GetRESTMapper(), scaleClient, mgr.GetClient())
ctx = context.WithValue(ctx, prediction.PredictorManagerKey, predictorMgr)
ctx = context.WithValue(ctx, prediction.SelectorFetcherKey, selectorFetcher)
craneServer.Run(ctx)
return nil
})
......
......@@ -4,7 +4,7 @@ go 1.17
require (
github.com/go-echarts/go-echarts/v2 v2.2.4
github.com/gocrane/api v0.6.0
github.com/gocrane/api v0.6.1-0.20220719082537-b7bdeaf2fb17
github.com/google/cadvisor v0.39.2
github.com/mjibson/go-dsp v0.0.0-20180508042940-11479a337f12
github.com/prometheus/client_golang v1.11.0
......
This diff is collapsed.
......@@ -17,6 +17,7 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/version"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
......@@ -77,6 +78,13 @@ func (c *Controller) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
return ctrl.Result{}, nil
}
// convert Analytics to RecommendationRule
recommendationRule := ConvertToRecommendationRule(analytics)
if err := UpsertRecommendationRule(recommendationRule, c.Client); err != nil {
klog.Infof(fmt.Sprintf("Upsert recommendation rule failed: %v", err))
return ctrl.Result{}, err
}
lastUpdateTime := analytics.Status.LastUpdateTime
if analytics.Spec.CompletionStrategy.CompletionStrategyType == analysisv1alph1.CompletionStrategyOnce {
if lastUpdateTime != nil {
......@@ -552,3 +560,40 @@ func setReadyCondition(status *analysisv1alph1.AnalyticsStatus, conditionStatus
LastTransitionTime: metav1.Now(),
})
}
func ConvertToRecommendationRule(analytics *analysisv1alph1.Analytics) *analysisv1alph1.RecommendationRule {
recommendationRule := &analysisv1alph1.RecommendationRule{}
recommendationRule.Name = analytics.Name
recommendationRule.Spec.ResourceSelectors = analytics.Spec.ResourceSelectors
// todo: make sure the conversion is right after recommendation refactor
recommendationRule.Spec.Recommenders = []analysisv1alph1.Recommender{{Name: string(analytics.Spec.Type)}}
if analytics.Namespace == known.CraneSystemNamespace {
recommendationRule.Spec.NamespaceSelector.Any = true
} else {
recommendationRule.Spec.NamespaceSelector.MatchNames = []string{analytics.Namespace}
}
if analytics.Spec.CompletionStrategy.CompletionStrategyType == analysisv1alph1.CompletionStrategyOnce {
recommendationRule.Spec.RunInterval = ""
} else {
recommendationRule.Spec.RunInterval = (time.Duration(*analytics.Spec.CompletionStrategy.PeriodSeconds) * time.Second).String()
}
return recommendationRule
}
func UpsertRecommendationRule(recommendationRule *analysisv1alph1.RecommendationRule, client client.Client) error {
recommendationRuleExist := &analysisv1alph1.RecommendationRule{}
if err := client.Get(context.TODO(), types.NamespacedName{Name: recommendationRule.Name}, recommendationRuleExist); err != nil {
if errors.IsNotFound(err) {
return client.Create(context.TODO(), recommendationRule)
}
return err
}
if !reflect.DeepEqual(recommendationRule.Spec, recommendationRuleExist.Spec) {
recommendationRuleExist.Spec = recommendationRule.Spec
return client.Update(context.TODO(), recommendationRuleExist)
}
return nil
}
package config
import (
predictormgr "github.com/gocrane/crane/pkg/predictor"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
predictormgr "github.com/gocrane/crane/pkg/predictor"
"github.com/gocrane/crane/pkg/server/service/dashboard"
)
......@@ -18,8 +21,12 @@ type Config struct {
EnableGrafana bool `json:"enableGrafana"`
GrafanaConfig *dashboard.GrafanaConfig `json:"grafanaConfig"`
KubeRestConfig *rest.Config `json:"KubeRestConfig"`
StoreType string `json:"storeType"`
KubeConfig *rest.Config `json:"kubeConfig"`
Scheme *runtime.Scheme `json:"scheme"`
Client client.Client `json:"client"`
RestMapper meta.RESTMapper `json:"restMapper"`
StoreType string `json:"storeType"`
PredictorMgr predictormgr.Manager
}
......
......@@ -29,7 +29,6 @@ func NewClusterHandler(srv cluster.Service) *ClusterHandler {
// ListClusters list the clusters which has deployed crane server.
func (ch *ClusterHandler) ListClusters(c *gin.Context) {
clusterList, err := ch.clusterSrv.ListClusters(context.TODO())
if err != nil {
ginwrapper.WriteResponse(c, err, nil)
......@@ -138,7 +137,6 @@ func (ch *ClusterHandler) UpdateCluster(c *gin.Context) {
// DeleteCluster del the clusters
func (ch *ClusterHandler) DeleteCluster(c *gin.Context) {
err := ch.clusterSrv.DeleteCluster(context.TODO(), c.Param("clusterid"))
if err != nil {
ginwrapper.WriteResponse(c, err, nil)
......
......@@ -10,16 +10,19 @@ import (
"github.com/go-echarts/go-echarts/v2/components"
"github.com/go-echarts/go-echarts/v2/opts"
"github.com/go-echarts/go-echarts/v2/types"
"github.com/gocrane/crane/pkg/prediction/dsp"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/scale"
"k8s.io/klog/v2"
craneclientset "github.com/gocrane/api/pkg/generated/clientset/versioned"
"github.com/gocrane/api/prediction/v1alpha1"
"github.com/gocrane/crane/pkg/controller/timeseriesprediction"
"github.com/gocrane/crane/pkg/prediction/dsp"
predictormgr "github.com/gocrane/crane/pkg/predictor"
"github.com/gocrane/crane/pkg/server/config"
"github.com/gocrane/crane/pkg/server/ginwrapper"
"github.com/gocrane/crane/pkg/utils/target"
)
......@@ -37,27 +40,19 @@ var (
SelectorFetcherKey ContextKey = "selectorFetcher"
)
func NewDebugHandler(ctx context.Context) *DebugHandler {
config, err := rest.InClusterConfig()
func NewDebugHandler(config *config.Config) *DebugHandler {
discoveryClientSet, err := discovery.NewDiscoveryClientForConfig(config.KubeConfig)
if err != nil {
klog.Fatalf("Failed to get InClusterConfig, %v.", err)
klog.Exit(err, "Unable to create discover client")
}
val := ctx.Value(PredictorManagerKey)
if val == nil {
klog.Fatalf("predictorManager not found")
}
predictorManager := val.(predictormgr.Manager)
val = ctx.Value(SelectorFetcherKey)
if val == nil {
klog.Fatalf("selectorFetcher not found")
}
selectorFetcher := val.(target.SelectorFetcher)
scaleKindResolver := scale.NewDiscoveryScaleKindResolver(discoveryClientSet)
scaleClient := scale.New(discoveryClientSet.RESTClient(), config.RestMapper, dynamic.LegacyAPIPathResolverFunc, scaleKindResolver)
selectorFetcher := target.NewSelectorFetcher(config.Scheme, config.RestMapper, scaleClient, config.Client)
return &DebugHandler{
craneClient: craneclientset.NewForConfigOrDie(config),
predictorManager: predictorManager,
craneClient: craneclientset.NewForConfigOrDie(config.KubeConfig),
predictorManager: config.PredictorMgr,
selectorFetcher: selectorFetcher,
}
}
......
package recommendation
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
analysisapi "github.com/gocrane/api/analysis/v1alpha1"
"github.com/gocrane/crane/pkg/server/config"
"github.com/gocrane/crane/pkg/server/ginwrapper"
)
type Handler struct {
client client.Client
}
func NewRecommendationHandler(config *config.Config) *Handler {
return &Handler{
client: config.Client,
}
}
// ListRecommendations list the recommendations in cluster.
func (h *Handler) ListRecommendations(c *gin.Context) {
recommendList := &analysisapi.RecommendationList{}
err := h.client.List(context.TODO(), recommendList)
if err != nil {
ginwrapper.WriteResponse(c, err, nil)
return
}
ginwrapper.WriteResponse(c, nil, recommendList)
}
// ListRecommendationRules list the recommendationRules in cluster.
func (h *Handler) ListRecommendationRules(c *gin.Context) {
recommendationRuleList := &analysisapi.RecommendationRuleList{}
err := h.client.List(context.TODO(), recommendationRuleList)
if err != nil {
ginwrapper.WriteResponse(c, err, nil)
return
}
ginwrapper.WriteResponse(c, nil, recommendationRuleList)
}
// CreateRecommendationRule create a recommendationRules from request.
func (h *Handler) CreateRecommendationRule(c *gin.Context) {
recommendationRule := &analysisapi.RecommendationRule{}
if err := c.ShouldBindJSON(&recommendationRule); err != nil {
ginwrapper.WriteResponse(c, err, nil)
return
}
err := h.client.Create(context.TODO(), recommendationRule)
if err != nil {
ginwrapper.WriteResponse(c, err, nil)
return
}
ginwrapper.WriteResponse(c, nil, nil)
}
// UpdateRecommendationRule update a recommendationRules from request.
func (h *Handler) UpdateRecommendationRule(c *gin.Context) {
recommendationRule := &analysisapi.RecommendationRule{}
if err := c.ShouldBindJSON(&recommendationRule); err != nil {
ginwrapper.WriteResponse(c, err, nil)
return
}
if c.Param("recommendationRuleName") != recommendationRule.Name {
ginwrapper.WriteResponse(c, fmt.Errorf("RecommendationRule name is unexpect"), nil)
return
}
recommendationRuleExist := &analysisapi.RecommendationRule{}
if err := h.client.Get(context.TODO(), types.NamespacedName{Name: recommendationRule.Name}, recommendationRuleExist); err != nil {
ginwrapper.WriteResponse(c, err, nil)
return
}
recommendationRuleExist.Spec = recommendationRule.Spec
err := h.client.Update(context.TODO(), recommendationRuleExist)
if err != nil {
ginwrapper.WriteResponse(c, err, nil)
return
}
ginwrapper.WriteResponse(c, nil, nil)
}
// DeleteRecommendationRule delete a recommendationRules from request.
func (h *Handler) DeleteRecommendationRule(c *gin.Context) {
recommendationRuleExist := &analysisapi.RecommendationRule{}
if err := h.client.Get(context.TODO(), types.NamespacedName{Name: c.Param("recommendationRuleName")}, recommendationRuleExist); err != nil {
ginwrapper.WriteResponse(c, err, nil)
return
}
ginwrapper.WriteResponse(c, nil, nil)
err := h.client.Delete(context.TODO(), recommendationRuleExist)
if err != nil {
ginwrapper.WriteResponse(c, err, nil)
return
}
ginwrapper.WriteResponse(c, nil, nil)
}
package server
import (
"context"
"github.com/gocrane/crane/pkg/server/handler/clusters"
"github.com/gocrane/crane/pkg/server/handler/dashboards"
"github.com/gocrane/crane/pkg/server/handler/prediction"
"github.com/gocrane/crane/pkg/server/handler/recommendation"
)
func (s *apiServer) initRouter() {
s.installHandler()
}
func (s *apiServer) installHandler() {
func (s *apiServer) initRouter(ctx context.Context) {
clusterHandler := clusters.NewClusterHandler(s.clusterSrv)
recommendationHandler := recommendation.NewRecommendationHandler(s.config)
v1 := s.Group("/api/v1")
{
......@@ -42,6 +42,27 @@ func (s *apiServer) installHandler() {
{
nsv1.GET(":clusterid", clusterHandler.ListNamespaces)
}
// recommendations
recommendv1 := v1.Group("/recommendation")
{
recommendv1.GET("", recommendationHandler.ListRecommendations)
}
// recommendationRules
recommendrulev1 := v1.Group("/recommendationRule")
{
recommendrulev1.GET("", recommendationHandler.ListRecommendationRules)
recommendrulev1.POST("", recommendationHandler.CreateRecommendationRule)
recommendrulev1.PUT("recommendationRuleName", recommendationHandler.UpdateRecommendationRule)
recommendrulev1.DELETE("recommendationRuleName", recommendationHandler.DeleteRecommendationRule)
}
}
debugHandler := prediction.NewDebugHandler(s.config)
debug := s.Group("/api/prediction/debug")
{
debug.GET(":namespace/:tsp", debugHandler.Display)
}
}
......@@ -17,7 +17,6 @@ import (
"github.com/gocrane/crane/pkg/server/config"
"github.com/gocrane/crane/pkg/server/ginwrapper"
"github.com/gocrane/crane/pkg/server/handler/prediction"
"github.com/gocrane/crane/pkg/server/middleware"
clustersrv "github.com/gocrane/crane/pkg/server/service/cluster"
dashboardsrv "github.com/gocrane/crane/pkg/server/service/dashboard"
......@@ -55,14 +54,6 @@ func NewServer(cfg *config.Config) (*apiServer, error) {
return server, nil
}
func (s *apiServer) installPredictionDebugAPIs(ctx context.Context) {
debugHandler := prediction.NewDebugHandler(ctx)
debug := s.Group("/api/prediction/debug")
{
debug.GET(":namespace/:tsp", debugHandler.Display)
}
}
func (s *apiServer) installGenericAPIs() {
// install metric handler
if s.config.EnableMetrics {
......@@ -116,10 +107,10 @@ func (s *apiServer) initServices() {
// Kubernetes API setup
if s.config.KubeRestConfig == nil {
if s.config.KubeConfig == nil {
klog.Fatal(fmt.Errorf("kubernetes rest config is null"))
}
kubeClientset, err := kubernetes.NewForConfig(s.config.KubeRestConfig)
kubeClientset, err := kubernetes.NewForConfig(s.config.KubeConfig)
if err != nil {
klog.Fatal(err.Error())
}
......@@ -144,8 +135,7 @@ func (s *apiServer) Run(ctx context.Context) {
s.installDefaultMiddlewares()
s.installGenericAPIs()
s.initRouter()
s.installPredictionDebugAPIs(ctx)
s.initRouter(ctx)
s.startGracefulShutDownManager(ctx)
go func() {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment