Skip to content

Commit

Permalink
Merge pull request #9 from qmhu/recommendation
Browse files Browse the repository at this point in the history
Update plugin for recommendation framework
  • Loading branch information
qmhu authored Dec 15, 2022
2 parents eb3a54d + 392352b commit fefd850
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 51 deletions.
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ module github.com/gocrane/kubectl-crane
go 1.17

require (
github.com/gocrane/api v0.8.1-0.20221213081415-64b9beb331c1
github.com/gocrane/crane v0.8.0
github.com/gocrane/api v0.8.1-0.20221214085502-ce0ce68164fc
github.com/jedib0t/go-pretty/v6 v6.3.2
github.com/kolide/kit v0.0.0-20210803163830-e689ca24537d
github.com/spf13/cobra v1.4.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,8 @@ github.com/gobwas/ws v1.1.0-rc.5/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIb
github.com/gocrane/api v0.7.1-0.20220906050113-0f331eb419b0/go.mod h1:GxI+t9AW8+NsHkz2JkPBIJN//9eLUjTZl1ScYAbXMbk=
github.com/gocrane/api v0.8.1-0.20221213081415-64b9beb331c1 h1:TQzdhwVvfOdD9RvH2IGWl9Z4ML0WnON+dXWYTu68ZtM=
github.com/gocrane/api v0.8.1-0.20221213081415-64b9beb331c1/go.mod h1:GxI+t9AW8+NsHkz2JkPBIJN//9eLUjTZl1ScYAbXMbk=
github.com/gocrane/api v0.8.1-0.20221214085502-ce0ce68164fc h1:kxmAbzMWqqYVPcWZaju03QVpaDEHIbdLvmxi0JP9P/I=
github.com/gocrane/api v0.8.1-0.20221214085502-ce0ce68164fc/go.mod h1:GxI+t9AW8+NsHkz2JkPBIJN//9eLUjTZl1ScYAbXMbk=
github.com/gocrane/crane v0.8.0 h1:DcqejWi2Lpsr4iNY+bDnM5d5NpCmZiOIE/oVfAXtaxA=
github.com/gocrane/crane v0.8.0/go.mod h1:qO9VA+qZhSToG8OZoTOH+z75ow3/ZdsX5RstYnyZ6rk=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
Expand Down
4 changes: 2 additions & 2 deletions pkg/cmd/crane.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ func NewCraneCommand() *cobra.Command {
Short: "Kubectl plugin for crane, including recommendation and cost estimate.",
}

cmd.AddCommand(NewCmdCranePod())
cmd.AddCommand(NewCmdCraneWorkload())
//cmd.AddCommand(NewCmdCranePod())
//cmd.AddCommand(NewCmdCraneWorkload())
cmd.AddCommand(NewCmdRecommendationRule())
cmd.AddCommand(NewCmdRecommend())
cmd.AddCommand(NewCmdViewRecommend())
Expand Down
10 changes: 5 additions & 5 deletions pkg/cmd/recommend/adopt.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ var (
%[1]s recommend adopt --name workloads-rule-resource-ntzns
# pre-commit
%[1]s recommend adopt --name workloads-rule-resource-ntzns --dry-run=All
%[1]s recommend adopt --name workloads-rule-resource-ntzns --dry-run
`
)

type RecommendAdoptOptions struct {
CommonOptions *options.CommonOptions

DryRun string
DryRun bool
Name string
}

Expand Down Expand Up @@ -107,7 +107,7 @@ func (o *RecommendAdoptOptions) Run() error {
}

patchOptions := metav1.PatchOptions{}
if len(o.DryRun) != 0 {
if o.DryRun {
patchOptions.DryRun = []string{"All"}
}

Expand All @@ -117,7 +117,7 @@ func (o *RecommendAdoptOptions) Run() error {
}

// when dry-run set, print the object
if len(o.DryRun) != 0 {
if o.DryRun {
printer := printers.NewTypeSetter(scheme.Scheme).ToPrinter(&printers.YAMLPrinter{})
if err = printer.PrintObj(patched, o.CommonOptions.Out); err != nil {
return err
Expand All @@ -133,5 +133,5 @@ func (o *RecommendAdoptOptions) Run() error {

func (o *RecommendAdoptOptions) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVarP(&o.Name, "name", "", "", "Specify the name for recommend")
cmd.Flags().StringVarP(&o.DryRun, "dry-run", "", "", "Pre-commit")
cmd.Flags().BoolVarP(&o.DryRun, "dry-run", "", false, "dry-run")
}
64 changes: 49 additions & 15 deletions pkg/cmd/recommend/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package recommend
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"strconv"
Expand All @@ -16,21 +15,30 @@ import (

analysisv1alpha1 "github.com/gocrane/api/analysis/v1alpha1"

"github.com/gocrane/crane/pkg/known"
"github.com/gocrane/kubectl-crane/pkg/cmd/options"
"github.com/gocrane/kubectl-crane/pkg/utils"
)

var (
recommendListExample = `
# view all recommend result with all namespace
%[1]s recommend list
# view all recommend result with kube-system namespace
%[1]s recommend list --namespace kube-system
# view Resource type recommend result with kube-system namespace
%[1]s recommend list --namespace kube-system --type Resource
`
)

recommenderMap = map[string]int{analysisv1alpha1.ReplicasRecommender: 1, analysisv1alpha1.ResourceRecommender: 2, analysisv1alpha1.IdleNodeRecommender: 3}
const (
RecommendationRuleNameLabel = "analysis.crane.io/recommendation-rule-name"
RecommendationRuleUidLabel = "analysis.crane.io/recommendation-rule-uid"
RecommendationRuleRecommenderLabel = "analysis.crane.io/recommendation-rule-recommender"
RecommendationRuleTargetKindLabel = "analysis.crane.io/recommendation-target-kind"
RecommendationRuleTargetVersionLabel = "analysis.crane.io/recommendation-target-version"
RecommendationRuleTargetNameLabel = "analysis.crane.io/recommendation-target-name"
)

type RecommendListOptions struct {
Expand All @@ -39,6 +47,8 @@ type RecommendListOptions struct {
Name string
Type string
TargetKind string
TargetName string
RuleName string
}

func NewRecommendListOptions() *RecommendListOptions {
Expand Down Expand Up @@ -82,8 +92,14 @@ func (o *RecommendListOptions) Validate() error {
}

if len(o.Type) > 0 {
if _, ok := recommenderMap[o.Type]; !ok {
return errors.New("the recommender only support Replicas,Resource and IdleNode")
typeExist := false
for _, recommenderType := range analysisv1alpha1.AllRecommenderType {
if recommenderType == o.Type {
typeExist = true
}
}
if !typeExist {
return fmt.Errorf("the recommender type not supported %s", o.Type)
}
}

Expand All @@ -105,11 +121,19 @@ func (o *RecommendListOptions) Run() error {
}

if len(o.Type) > 0 {
query.LabelSelector[known.RecommendationRuleRecommenderLabel] = o.Type
query.LabelSelector[RecommendationRuleRecommenderLabel] = o.Type
}

if len(o.TargetKind) > 0 {
query.LabelSelector[known.RecommendationRuleTargetKindLabel] = o.TargetKind
query.LabelSelector[RecommendationRuleTargetKindLabel] = o.TargetKind
}

if len(o.TargetName) > 0 {
query.LabelSelector[RecommendationRuleTargetNameLabel] = o.TargetName
}

if len(o.RuleName) > 0 {
query.LabelSelector[RecommendationRuleNameLabel] = o.RuleName
}

namespace := ""
Expand Down Expand Up @@ -158,7 +182,7 @@ func RenderTable(recommendations []analysisv1alpha1.Recommendation, out io.Write
t.SetStyle(table.StyleLight)
t.SetOutputMirror(out)
header := table.Row{}
header = append(header, table.Row{"NAME", "RECOMMEND SOURCE", "NAMESPACE", "TARGET", "CURRENT RESOURCE", "RECOMMEND RESOURCE", "CREATED TIME", "UPDATED TIME"}...)
header = append(header, table.Row{"NAME", "NAMESPACE", "TYPE", "TARGET NAME", "TARGET NAMESPACE", "TARGET KIND", "CURRENT RESOURCE", "RECOMMEND RESOURCE", "ACTION", "CREATED TIME", "UPDATED TIME"}...)
t.AppendHeader(header)
t.SetColumnConfigs([]table.ColumnConfig{
{
Expand All @@ -176,19 +200,23 @@ func RenderTable(recommendations []analysisv1alpha1.Recommendation, out io.Write
row := table.Row{}

row = append(row, recommendation.Name)
row = append(row, recommendation.Namespace)
row = append(row, recommendation.Spec.Type)

row = append(row, recommendation.Spec.TargetRef.Name)
row = append(row, recommendation.Namespace)
row = append(row, recommendation.Spec.TargetRef.Kind)

currentResource := ""
recommendResource := ""
if recommendation.Spec.Type == "Resource" {
switch recommendation.Spec.Type {
case "Resource":
var currentInfo analysisv1alpha1.PatchResource
if err := json.Unmarshal([]byte(recommendation.Status.RecommendationContent.CurrentInfo), &currentInfo); err != nil {
row = append(row, "")
} else {
for _, container := range currentInfo.Spec.Template.Spec.Containers {
currentResource += container.Name + "/" + container.Resources.Requests.Cpu().String() + "m/" + container.Resources.Requests.Memory().String() + "\n"
currentResource += container.Name + "/" + container.Resources.Requests.Cpu().String() + "/" + container.Resources.Requests.Memory().String() + "\n"
}
}

Expand All @@ -197,10 +225,10 @@ func RenderTable(recommendations []analysisv1alpha1.Recommendation, out io.Write
row = append(row, "")
} else {
for _, container := range recommendInfo.Spec.Template.Spec.Containers {
recommendResource += container.Name + "/" + container.Resources.Requests.Cpu().String() + "m/" + container.Resources.Requests.Memory().String() + "\n"
recommendResource += container.Name + "/" + container.Resources.Requests.Cpu().String() + "/" + container.Resources.Requests.Memory().String() + "\n"
}
}
} else if recommendation.Spec.Type == "Replicas" {
case "Replicas":
var currentInfo analysisv1alpha1.PatchReplicas
if err := json.Unmarshal([]byte(recommendation.Status.RecommendationContent.CurrentInfo), &currentInfo); err != nil {
row = append(row, "")
Expand All @@ -214,10 +242,14 @@ func RenderTable(recommendations []analysisv1alpha1.Recommendation, out io.Write
} else {
recommendResource += strconv.Itoa(int(*recommendInfo.Spec.Replicas))
}
default:
recommendResource = recommendation.Status.RecommendedInfo
currentResource = recommendation.Status.CurrentInfo
}

row = append(row, currentResource)
row = append(row, recommendResource)
row = append(row, recommendation.Status.Action)

row = append(row, recommendation.CreationTimestamp)
row = append(row, recommendation.Status.LastUpdateTime)
Expand All @@ -233,7 +265,9 @@ func RenderTable(recommendations []analysisv1alpha1.Recommendation, out io.Write
}

func (o *RecommendListOptions) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVarP(&o.Type, "type", "", "Resource", "Specify the type for recommend[Resource, Replicas, IdleNode]")
cmd.Flags().StringVarP(&o.Name, "name", "", "", "Specify the name for recommend")
cmd.Flags().StringVarP(&o.TargetKind, "targetKind", "", "", "Specify the target type for recommendationrules")
cmd.Flags().StringVarP(&o.Type, "type", "", "", "List recommendation with specify recommend type[Resource, Replicas, IdleNode]")
cmd.Flags().StringVarP(&o.Name, "name", "", "", "Specify the name for recommendation")
cmd.Flags().StringVarP(&o.TargetKind, "targetKind", "", "", "List recommendation with specify recommendation target kind")
cmd.Flags().StringVarP(&o.TargetName, "targetName", "", "", "List recommendation with specify recommendation target name")
cmd.Flags().StringVarP(&o.RuleName, "ruleName", "", "", "List recommendation with specify recommendationRule name")
}
46 changes: 24 additions & 22 deletions pkg/cmd/recommendationRule/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ import (
"encoding/json"
"errors"
"fmt"
"strings"
"time"

"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/klog/v2"
"strings"

"github.com/gocrane/api/analysis/v1alpha1"

Expand All @@ -28,13 +26,11 @@ var (
%[1]s rr create --target '[{"kind": "Deployment", "apiVersion": "apps/v1"}]' --run-interval 4h
# pre-commit
%[1]s rr create --target '[{"kind": "Deployment", "apiVersion": "apps/v1"}]' --run-interval 4h --dry-run=All
%[1]s rr create --target '[{"kind": "Deployment", "apiVersion": "apps/v1"}]' --run-interval 4h --dry-run
# create a simple recommendation rule for all namespace with Any and Resource\Replicas recommender
%[1]s rr create --namespace Any --recommender Resource,Replicas --target '[{"kind": "Deployment", "apiVersion": "apps/v1"}]' --run-interval 4h
`

recommenderMap = map[string]int{"Replicas": 1, "Resource": 2, "IdleNode": 3}
)

type RecommendationRuleCreateOptions struct {
Expand All @@ -43,7 +39,8 @@ type RecommendationRuleCreateOptions struct {
Recommender string
Target string
RunInterval string
DryRun string
DryRun bool
Name string

ResourceSelectors []v1alpha1.ResourceSelector
}
Expand Down Expand Up @@ -95,15 +92,25 @@ func (o *RecommendationRuleCreateOptions) Validate() error {

recommenders := strings.Split(o.Recommender, ",")
for _, recommender := range recommenders {
if _, ok := recommenderMap[recommender]; !ok {
return errors.New("the recommender only support Replicas,Resource and IdleNode")
typeExist := false
for _, recommenderType := range v1alpha1.AllRecommenderType {
if recommenderType == recommender {
typeExist = true
}
}
if !typeExist {
return fmt.Errorf("the recommender type not supported %s", recommender)
}
}

if len(o.RunInterval) == 0 {
return errors.New("please specify the runInterval with --runInterval")
}

if len(o.Name) == 0 {
return errors.New("please specify RecommendationRule name with --name")
}

return nil
}

Expand All @@ -124,34 +131,26 @@ func (o *RecommendationRuleCreateOptions) Run() error {
}
recommendationRule.Namespace = ""

name := "recommendation"

if len(*o.CommonOptions.ConfigFlags.Namespace) == 0 || strings.EqualFold(*o.CommonOptions.ConfigFlags.Namespace, "Any") {
recommendationRule.Spec.NamespaceSelector.Any = true
name += "-any"
} else {
recommendationRule.Spec.NamespaceSelector.MatchNames = strings.Split(*o.CommonOptions.ConfigFlags.Namespace, ",")
}

recommendationRule.Spec.ResourceSelectors = o.ResourceSelectors
name += "-" + strings.ToLower(o.ResourceSelectors[0].Kind)

recommenders := strings.Split(o.Recommender, ",")
for _, recommender := range recommenders {
name += "-" + strings.ToLower(recommender)
recommendationRule.Spec.Recommenders = append(recommendationRule.Spec.Recommenders, v1alpha1.Recommender{
Name: recommender,
})
}

recommendationRule.Spec.RunInterval = o.RunInterval
name += "-" + strings.ToLower(o.RunInterval)
name += "-" + time.Now().Format("2006-01-02")

recommendationRule.Name = name
recommendationRule.Name = o.Name

createOptions := metav1.CreateOptions{}
if len(o.DryRun) != 0 {
if o.DryRun {
createOptions.DryRun = []string{"All"}
}

Expand All @@ -161,22 +160,25 @@ func (o *RecommendationRuleCreateOptions) Run() error {
}

// when dry-run set, print the object
if len(o.DryRun) != 0 {
if o.DryRun {
created.Kind = "RecommendationRule"
created.APIVersion = "analysis.crane.io/v1alpha1"
printer := printers.NewTypeSetter(scheme.Scheme).ToPrinter(&printers.YAMLPrinter{})
if err = printer.PrintObj(created, o.CommonOptions.Out); err != nil {
return err
}

return nil
}

klog.Infof(fmt.Sprintf("the recommendation rule %s created successfully", name))
klog.Infof(fmt.Sprintf("the recommendation rule %s created successfully", o.Name))
return nil
}

func (o *RecommendationRuleCreateOptions) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVarP(&o.Recommender, "recommender", "", "Resource", "specify type for recommendationrules,separated with ',' if more than one, default is Resource")
cmd.Flags().StringVarP(&o.Target, "target", "", "", "specify recommend target for recommendationrules")
cmd.Flags().StringVarP(&o.RunInterval, "run-interval", "", "", "Specify runInterval for recommendationrules")
cmd.Flags().StringVarP(&o.DryRun, "dry-run", "", "", "pre-commit")
cmd.Flags().BoolVarP(&o.DryRun, "dry-run", "", false, "dry-run")
cmd.Flags().StringVarP(&o.Name, "name", "", "", "recommendationrule name")
}
3 changes: 2 additions & 1 deletion pkg/cmd/recommendationRule/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func (o *RecommendationRuleListOptions) renderTable(recommendationRules []analys
t.SetStyle(table.StyleLight)
t.SetOutputMirror(o.CommonOptions.Out)
header := table.Row{}
header = append(header, table.Row{"NAME", "RECOMMENDER", "TARGET", "NAMESPACE", "RUN INTERVAL", "CREATE TIME"}...)
header = append(header, table.Row{"NAME", "RECOMMENDER", "TARGET", "NAMESPACE", "RUN INTERVAL", "LAST UPDATE TIME", "CREATE TIME"}...)
t.AppendHeader(header)
t.SetColumnConfigs([]table.ColumnConfig{
{
Expand Down Expand Up @@ -158,6 +158,7 @@ func (o *RecommendationRuleListOptions) renderTable(recommendationRules []analys
row = append(row, strings.Join(namespaces, ","))

row = append(row, recommendRule.Spec.RunInterval)
row = append(row, recommendRule.Status.LastUpdateTime)
row = append(row, recommendRule.CreationTimestamp)

t.AppendRows([]table.Row{
Expand Down
Loading

0 comments on commit fefd850

Please sign in to comment.