a877aed45f
Change-Id: I16cd7730c1e0732253ac52f51010f6b813295aa7
210 lines
6.4 KiB
Go
210 lines
6.4 KiB
Go
package apply
|
|
|
|
// Author: Weisen Pan
|
|
// Date: 2023-10-24
|
|
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
|
|
survey "github.com/AlecAivazis/survey/v2"
|
|
"github.com/olekukonko/tablewriter"
|
|
"github.com/pquerna/ffjson/ffjson"
|
|
log "github.com/sirupsen/logrus"
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/knets_pkg/api/resource"
|
|
resourcehelper "k8s.io/kubectl/knets_pkg/util/resource"
|
|
"sigs.k8s.io/yaml"
|
|
|
|
localcache "github.com/alibaba/open-local/knets_pkg/scheduler/algorithm/cache"
|
|
"github.com/hkust-adsl/kubernetes-scheduler-simulator/knets_pkg/api/v1alpha1"
|
|
"github.com/hkust-adsl/kubernetes-scheduler-simulator/knets_pkg/chart"
|
|
"github.com/hkust-adsl/kubernetes-scheduler-simulator/knets_pkg/simulator"
|
|
simontype "github.com/hkust-adsl/kubernetes-scheduler-simulator/knets_pkg/type"
|
|
gpushareutils "github.com/hkust-adsl/kubernetes-scheduler-simulator/knets_pkg/type/open-gpu-share/utils"
|
|
"github.com/hkust-adsl/kubernetes-scheduler-simulator/knets_pkg/utils"
|
|
)
|
|
|
|
type Options struct {
|
|
SimonConfig string
|
|
DefaultSchedulerConfigFile string
|
|
UseGreed bool
|
|
Interactive bool
|
|
ExtendedResources []string
|
|
}
|
|
|
|
type Applier struct {
|
|
cluster v1alpha1.Cluster
|
|
appList []v1alpha1.AppInfo
|
|
schedulerConfig string
|
|
useGreed bool
|
|
interactive bool
|
|
extendedResources []string
|
|
customConfig v1alpha1.CustomConfig
|
|
}
|
|
|
|
type Interface interface {
|
|
Run() error
|
|
}
|
|
|
|
// NewApplier returns a default applier that has passed the validity test
|
|
func NewApplier(opts Options) Interface {
|
|
simonCR := &v1alpha1.Simon{}
|
|
configFile, err := ioutil.ReadFile(opts.SimonConfig)
|
|
if err != nil {
|
|
log.Fatalf("failed to read config file(%s): %v", opts.SimonConfig, err)
|
|
}
|
|
configJSON, err := yaml.YAMLToJSON(configFile)
|
|
if err != nil {
|
|
log.Fatalf("failed to unmarshal config file(%s) to json: %v", opts.SimonConfig, err)
|
|
}
|
|
|
|
if err := json.Unmarshal(configJSON, simonCR); err != nil {
|
|
log.Fatalf("failed to unmarshal config json to object: %v", err)
|
|
}
|
|
|
|
applier := &Applier{
|
|
cluster: simonCR.Spec.Cluster,
|
|
appList: simonCR.Spec.AppList,
|
|
customConfig: simonCR.Spec.CustomConfig,
|
|
schedulerConfig: opts.DefaultSchedulerConfigFile,
|
|
useGreed: opts.UseGreed,
|
|
interactive: opts.Interactive,
|
|
extendedResources: opts.ExtendedResources,
|
|
}
|
|
|
|
if err := validate(applier); err != nil {
|
|
fmt.Printf("%v", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
return applier
|
|
}
|
|
|
|
func (applier *Applier) Run() (err error) {
|
|
var (
|
|
resourceMap map[string]simulator.ResourceTypes
|
|
resourceList []string
|
|
content []string
|
|
)
|
|
resourceMap = make(map[string]simulator.ResourceTypes)
|
|
|
|
// convert the application files into the kubernetes objects and generate a ResourceTypes struct, then make a resource list
|
|
var appResource simulator.ResourceTypes
|
|
for _, app := range applier.appList {
|
|
// process separately chart and other file
|
|
if app.Chart {
|
|
// parse and render chart as a yaml array
|
|
if content, err = chart.ProcessChart(app.Name, app.Path); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if content, err = utils.GetYamlContentFromDirectory(app.Path); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if appResource, err = simulator.GetObjectFromYamlContent(content); err != nil {
|
|
return err
|
|
}
|
|
|
|
resourceMap[app.Name] = appResource
|
|
resourceList = append(resourceList, app.Name)
|
|
}
|
|
|
|
// convert the cluster files into the kubernetes objects and generate a ResourceTypes struct
|
|
// cluster resource generated by two types of cluster, custom cluster and real cluster
|
|
var clusterResource simulator.ResourceTypes
|
|
if applier.cluster.KubeConfig != "" {
|
|
// generate kube-client
|
|
kubeclient, err := utils.CreateKubeClient(applier.cluster.KubeConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if clusterResource, err = simulator.CreateClusterResourceFromClient(kubeclient); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if clusterResource, err = simulator.CreateClusterResourceFromClusterConfig(applier.cluster.CustomCluster); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// confirm the list of applications that needed to be deployed in interactive mode
|
|
var selectedAppNameList []string
|
|
var selectedResourceList []simulator.AppResource
|
|
if applier.interactive {
|
|
var multiQs = []*survey.Question{
|
|
{
|
|
Name: "APPs",
|
|
Prompt: &survey.MultiSelect{
|
|
Message: "Confirm your apps :",
|
|
Options: resourceList,
|
|
},
|
|
},
|
|
}
|
|
err = survey.Ask(multiQs, &selectedAppNameList)
|
|
if err != nil {
|
|
log.Fatalf("%v", err)
|
|
}
|
|
} else {
|
|
selectedAppNameList = resourceList
|
|
}
|
|
for _, name := range selectedAppNameList {
|
|
selectedResourceList = append(selectedResourceList, simulator.AppResource{
|
|
Name: name,
|
|
Resource: resourceMap[name],
|
|
})
|
|
}
|
|
|
|
// Run the simulator
|
|
success := false
|
|
var result *simontype.SimulateResult
|
|
result, err = simulator.Simulate(clusterResource, selectedResourceList, simulator.WithSchedulerConfig(applier.schedulerConfig), simulator.WithKubeConfig(applier.cluster.KubeConfig), simulator.WithCustomConfig(applier.customConfig))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(result.UnscheduledPods) == 0 {
|
|
if ok, reason, err := satisfyResourceSetting(result.NodeStatus); err != nil {
|
|
return err
|
|
} else if !ok {
|
|
fmt.Printf(utils.ColorRed+"%s"+utils.ColorReset, reason)
|
|
} else {
|
|
success = true
|
|
}
|
|
} else {
|
|
fmt.Printf(utils.ColorRed+"there are %d unscheduled pods\n"+utils.ColorReset, len(result.UnscheduledPods))
|
|
log.Infof("there are %d unscheduled pods\n", len(result.UnscheduledPods))
|
|
allDaemonSets := clusterResource.DaemonSets
|
|
for _, app := range selectedResourceList {
|
|
allDaemonSets = append(allDaemonSets, app.Resource.DaemonSets...)
|
|
}
|
|
for _, unScheduledPod := range result.UnscheduledPods {
|
|
log.Debugf("failed to schedule pod %s/%s: %s", unScheduledPod.Pod.Namespace, unScheduledPod.Pod.Name, unScheduledPod.Reason)
|
|
}
|
|
}
|
|
|
|
if success {
|
|
fmt.Printf(utils.ColorGreen + "Success!\n" + utils.ColorReset)
|
|
} else {
|
|
fmt.Printf(utils.ColorRed + "Failed!\n" + utils.ColorReset)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validate(applier *Applier) error {
|
|
fmt.Printf("cluster.KubeConfig=%s\n", applier.cluster.KubeConfig)
|
|
fmt.Printf("cluster.CustomCluster=%s\n", applier.cluster.CustomCluster)
|
|
if len(applier.cluster.KubeConfig) == 0 && len(applier.cluster.CustomCluster) == 0 ||
|
|
len(applier.cluster.KubeConfig) != 0 && len(applier.cluster.CustomCluster) != 0 {
|
|
return fmt.Errorf("only one of values of both kubeConfig and customConfig must exist ")
|
|
}
|
|
|
|
if len(applier.cluster.KubeConfig) != 0 {
|
|
if _, err
|