use-case-and-architecture/ai_computing_force_scheduling/knets_pkg/apply/apply.go
Weisen Pan a877aed45f AI-based CFN Traffic Control and Computer Force Scheduling
Change-Id: I16cd7730c1e0732253ac52f51010f6b813295aa7
2023-11-03 00:09:19 -07:00

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