From aeb842c2b650ebf541acba23613e3b2ffa392ec3 Mon Sep 17 00:00:00 2001 From: wadecai Date: Thu, 17 Sep 2020 20:31:24 +0800 Subject: [PATCH] Implemetaion of coscheduling base on crd[Part 1]: controller and crd Change codes according to round 1 suggestions Update PR based on suggestions --- Makefile | 12 +- build/controller/Dockerfile | 25 ++ Dockerfile => build/scheduler/Dockerfile | 0 cmd/controller/app/options.go | 47 +++ cmd/controller/app/server.go | 120 ++++++++ cmd/controller/controller.go | 38 +++ cmd/{ => scheduler}/main.go | 0 cmd/{ => scheduler}/main_test.go | 0 go.mod | 2 + go.sum | 18 -- hack/update-codegen.sh | 7 + pkg/apis/podgroup/register.go | 22 ++ pkg/apis/podgroup/v1alpha1/doc.go | 21 ++ pkg/apis/podgroup/v1alpha1/register.go | 55 ++++ pkg/apis/podgroup/v1alpha1/types.go | 134 +++++++++ .../v1alpha1/zz_generated.deepcopy.go | 138 +++++++++ .../v1alpha1/zz_generated.defaults.go | 32 +++ pkg/controller/podgroup.go | 268 ++++++++++++++++++ pkg/controller/podgroup_test.go | 186 ++++++++++++ .../clientset/versioned/clientset.go | 97 +++++++ pkg/generated/clientset/versioned/doc.go | 20 ++ .../versioned/fake/clientset_generated.go | 82 ++++++ pkg/generated/clientset/versioned/fake/doc.go | 20 ++ .../clientset/versioned/fake/register.go | 56 ++++ .../clientset/versioned/scheme/doc.go | 20 ++ .../clientset/versioned/scheme/register.go | 56 ++++ .../versioned/typed/podgroup/v1alpha1/doc.go | 20 ++ .../typed/podgroup/v1alpha1/fake/doc.go | 20 ++ .../podgroup/v1alpha1/fake/fake_podgroup.go | 142 ++++++++++ .../v1alpha1/fake/fake_podgroup_client.go | 40 +++ .../podgroup/v1alpha1/generated_expansion.go | 21 ++ .../typed/podgroup/v1alpha1/podgroup.go | 195 +++++++++++++ .../podgroup/v1alpha1/podgroup_client.go | 89 ++++++ .../informers/externalversions/factory.go | 180 ++++++++++++ .../informers/externalversions/generic.go | 62 ++++ .../internalinterfaces/factory_interfaces.go | 40 +++ .../externalversions/podgroup/interface.go | 46 +++ .../podgroup/v1alpha1/interface.go | 45 +++ .../podgroup/v1alpha1/podgroup.go | 90 ++++++ .../podgroup/v1alpha1/expansion_generated.go | 27 ++ .../listers/podgroup/v1alpha1/podgroup.go | 99 +++++++ pkg/util/podgroup.go | 80 ++++++ pkg/util/podgroup_test.go | 78 +++++ pkg/util/types.go | 33 +++ 44 files changed, 2762 insertions(+), 21 deletions(-) create mode 100644 build/controller/Dockerfile rename Dockerfile => build/scheduler/Dockerfile (100%) create mode 100644 cmd/controller/app/options.go create mode 100644 cmd/controller/app/server.go create mode 100644 cmd/controller/controller.go rename cmd/{ => scheduler}/main.go (100%) rename cmd/{ => scheduler}/main_test.go (100%) create mode 100644 pkg/apis/podgroup/register.go create mode 100644 pkg/apis/podgroup/v1alpha1/doc.go create mode 100644 pkg/apis/podgroup/v1alpha1/register.go create mode 100644 pkg/apis/podgroup/v1alpha1/types.go create mode 100644 pkg/apis/podgroup/v1alpha1/zz_generated.deepcopy.go create mode 100644 pkg/apis/podgroup/v1alpha1/zz_generated.defaults.go create mode 100644 pkg/controller/podgroup.go create mode 100644 pkg/controller/podgroup_test.go create mode 100644 pkg/generated/clientset/versioned/clientset.go create mode 100644 pkg/generated/clientset/versioned/doc.go create mode 100644 pkg/generated/clientset/versioned/fake/clientset_generated.go create mode 100644 pkg/generated/clientset/versioned/fake/doc.go create mode 100644 pkg/generated/clientset/versioned/fake/register.go create mode 100644 pkg/generated/clientset/versioned/scheme/doc.go create mode 100644 pkg/generated/clientset/versioned/scheme/register.go create mode 100644 pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/doc.go create mode 100644 pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/fake/doc.go create mode 100644 pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/fake/fake_podgroup.go create mode 100644 pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/fake/fake_podgroup_client.go create mode 100644 pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/generated_expansion.go create mode 100644 pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/podgroup.go create mode 100644 pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/podgroup_client.go create mode 100644 pkg/generated/informers/externalversions/factory.go create mode 100644 pkg/generated/informers/externalversions/generic.go create mode 100644 pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go create mode 100644 pkg/generated/informers/externalversions/podgroup/interface.go create mode 100644 pkg/generated/informers/externalversions/podgroup/v1alpha1/interface.go create mode 100644 pkg/generated/informers/externalversions/podgroup/v1alpha1/podgroup.go create mode 100644 pkg/generated/listers/podgroup/v1alpha1/expansion_generated.go create mode 100644 pkg/generated/listers/podgroup/v1alpha1/podgroup.go create mode 100644 pkg/util/podgroup.go create mode 100644 pkg/util/podgroup_test.go create mode 100644 pkg/util/types.go diff --git a/Makefile b/Makefile index 30ecdc464..9e7b97d51 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ BUILDENVVAR=CGO_ENABLED=0 LOCAL_REGISTRY=localhost:5000/scheduler-plugins LOCAL_IMAGE=kube-scheduler:latest +LOCAL_CONTROLLER_IMAGE=controller:latest # RELEASE_REGISTRY is the container registry to push # into. The default is to push to the staging @@ -24,26 +25,31 @@ LOCAL_IMAGE=kube-scheduler:latest RELEASE_REGISTRY?=gcr.io/k8s-staging-scheduler-plugins RELEASE_VERSION?=$(shell git describe --tags --match "v*") RELEASE_IMAGE:=kube-scheduler:$(RELEASE_VERSION) +RELEASE_CONTROLLER_IMAGE:=controller:$(RELEASE_VERSION) .PHONY: all all: build .PHONY: build build: autogen - $(COMMONENVVAR) $(BUILDENVVAR) go build -ldflags '-w' -o bin/kube-scheduler cmd/main.go + $(COMMONENVVAR) $(BUILDENVVAR) go build -ldflags '-w' -o bin/kube-scheduler cmd/scheduler/main.go + $(COMMONENVVAR) $(BUILDENVVAR) go build -ldflags '-w' -o bin/controller cmd/controller/controller.go .PHONY: local-image local-image: clean - docker build -t $(LOCAL_REGISTRY)/$(LOCAL_IMAGE) . + docker build -f ./build/scheduler/Dockerfile -t $(LOCAL_REGISTRY)/$(LOCAL_IMAGE) . + docker build -f ./build/controller/Dockerfile -t $(LOCAL_REGISTRY)/$(LOCAL_CONTROLLER_IMAGE) . .PHONY: release-image release-image: clean - docker build -t $(RELEASE_REGISTRY)/$(RELEASE_IMAGE) . + docker build -f ./build/scheduler/Dockerfile -t $(RELEASE_REGISTRY)/$(RELEASE_IMAGE) . + docker build -f ./build/controller/Dockerfile -t $(RELEASE_REGISTRY)/$(RELEASE_CONTROLLER_IMAGE) . .PHONY: push-release-image push-release-image: release-image gcloud auth configure-docker docker push $(RELEASE_REGISTRY)/$(RELEASE_IMAGE) + docker push $(RELEASE_REGISTRY)/$(RELEASE_CONTROLLER_IMAGE) .PHONY: update-vendor update-vendor: diff --git a/build/controller/Dockerfile b/build/controller/Dockerfile new file mode 100644 index 000000000..6a580e26b --- /dev/null +++ b/build/controller/Dockerfile @@ -0,0 +1,25 @@ +# Copyright 2020 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +FROM golang:1.15.0 + +WORKDIR /go/src/sigs.k8s.io/scheduler-plugins +COPY . . +RUN make + +FROM alpine:3.12 + +COPY --from=0 /go/src/sigs.k8s.io/scheduler-plugins/bin/controller /bin/controller + +WORKDIR /bin +CMD ["controller"] diff --git a/Dockerfile b/build/scheduler/Dockerfile similarity index 100% rename from Dockerfile rename to build/scheduler/Dockerfile diff --git a/cmd/controller/app/options.go b/cmd/controller/app/options.go new file mode 100644 index 000000000..0fc1abb7f --- /dev/null +++ b/cmd/controller/app/options.go @@ -0,0 +1,47 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package app + +import ( + "github.com/spf13/pflag" +) + +type ServerRunOptions struct { + KubeConfig string + MasterUrl string + InCluster bool + ApiServerQPS int + ApiServerBurst int + Workers int + EnableLeaderElection bool +} + +func NewServerRunOptions() *ServerRunOptions { + options := &ServerRunOptions{} + options.addAllFlags() + return options +} + +func (s *ServerRunOptions) addAllFlags() { + pflag.BoolVar(&s.InCluster, "incluster", s.InCluster, "If controller run incluster.") + pflag.StringVar(&s.MasterUrl, "kubeConfig", s.MasterUrl, "Kube Config path if not run in cluster.") + pflag.StringVar(&s.MasterUrl, "masterUrl", s.MasterUrl, "Master Url if not run in cluster.") + pflag.IntVar(&s.ApiServerQPS, "qps", 5, "qps of query apiserver.") + pflag.IntVar(&s.ApiServerBurst, "burst", 10, "burst of query apiserver.") + pflag.IntVar(&s.Workers, "workers", 1, "workers of scheduler-plugin-controllers.") + pflag.BoolVar(&s.EnableLeaderElection, "enableLeaderElection", s.EnableLeaderElection, "If EnableLeaderElection for controller.") +} diff --git a/cmd/controller/app/server.go b/cmd/controller/app/server.go new file mode 100644 index 000000000..d2b37983f --- /dev/null +++ b/cmd/controller/app/server.go @@ -0,0 +1,120 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package app + +import ( + "context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/uuid" + "k8s.io/apiserver/pkg/server" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + restclient "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/leaderelection" + "k8s.io/client-go/tools/leaderelection/resourcelock" + "k8s.io/klog/v2" + "os" + + "sigs.k8s.io/scheduler-plugins/pkg/controller" + pgclientset "sigs.k8s.io/scheduler-plugins/pkg/generated/clientset/versioned" + pgformers "sigs.k8s.io/scheduler-plugins/pkg/generated/informers/externalversions" + "sigs.k8s.io/scheduler-plugins/pkg/util" +) + +func newConfig(kubeconfig, master string, inCluster bool) (*restclient.Config, error) { + var ( + config *rest.Config + err error + ) + if inCluster { + config, err = rest.InClusterConfig() + } else { + config, err = clientcmd.BuildConfigFromFlags(master, kubeconfig) + } + if err != nil { + return nil, err + } + return config, nil +} + +func Run(s *ServerRunOptions) error { + ctx := context.Background() + config, err := newConfig(s.KubeConfig, s.MasterUrl, s.InCluster) + if err != nil { + klog.Fatal(err) + } + config.QPS = float32(s.ApiServerQPS) + config.Burst = s.ApiServerBurst + stopCh := server.SetupSignalHandler() + + pgClient := pgclientset.NewForConfigOrDie(config) + kubeClient := kubernetes.NewForConfigOrDie(config) + + pgInformerFactory := pgformers.NewSharedInformerFactory(pgClient, 0) + pgInformer := pgInformerFactory.Scheduling().V1alpha1().PodGroups() + + informerFactory := informers.NewSharedInformerFactoryWithOptions(kubeClient, 0, informers.WithTweakListOptions(func(opt *metav1.ListOptions) { + opt.LabelSelector = util.PodGroupLabel + })) + podInformer := informerFactory.Core().V1().Pods() + ctrl := controller.NewPodGroupController(kubeClient, pgInformer, podInformer, pgClient) + pgInformerFactory.Start(stopCh) + informerFactory.Start(stopCh) + run := func(ctx context.Context) { + ctrl.Run(s.Workers, ctx.Done()) + } + + if !s.EnableLeaderElection { + run(ctx) + } else { + id, err := os.Hostname() + if err != nil { + return err + } + + // add a uniquifier so that two processes on the same host don't accidentally both become active + id = id + "_" + string(uuid.NewUUID()) + + rl, err := resourcelock.New("endpoints", + "kube-system", + "sched-plugins-controller", + kubeClient.CoreV1(), + kubeClient.CoordinationV1(), + resourcelock.ResourceLockConfig{ + Identity: id, + }) + if err != nil { + klog.Fatalf("error creating lock: %v", err) + } + + leaderelection.RunOrDie(context.TODO(), leaderelection.LeaderElectionConfig{ + Lock: rl, + Callbacks: leaderelection.LeaderCallbacks{ + OnStartedLeading: run, + OnStoppedLeading: func() { + klog.Fatalf("leaderelection lost") + }, + }, + Name: "scheduler-plugins controller", + }) + } + + <-stopCh + return nil +} diff --git a/cmd/controller/controller.go b/cmd/controller/controller.go new file mode 100644 index 000000000..c746f64eb --- /dev/null +++ b/cmd/controller/controller.go @@ -0,0 +1,38 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/spf13/pflag" + "sigs.k8s.io/scheduler-plugins/cmd/controller/app" +) + +func main() { + options := app.NewServerRunOptions() + + pflag.CommandLine.AddGoFlagSet(flag.CommandLine) + pflag.Parse() + + if err := app.Run(options); err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) + } +} diff --git a/cmd/main.go b/cmd/scheduler/main.go similarity index 100% rename from cmd/main.go rename to cmd/scheduler/main.go diff --git a/cmd/main_test.go b/cmd/scheduler/main_test.go similarity index 100% rename from cmd/main_test.go rename to cmd/scheduler/main_test.go diff --git a/go.mod b/go.mod index b8cb5ed17..260230ff1 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module sigs.k8s.io/scheduler-plugins go 1.15 require ( + github.com/evanphx/json-patch v4.9.0+incompatible github.com/google/go-cmp v0.4.0 github.com/spf13/pflag v1.0.5 k8s.io/api v0.19.0 @@ -35,6 +36,7 @@ replace ( k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.19.0 k8s.io/kubectl => k8s.io/kubectl v0.19.0 k8s.io/kubelet => k8s.io/kubelet v0.19.0 + k8s.io/kubernetes => k8s.io/kubernetes v1.19.0 k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.19.0 k8s.io/metrics => k8s.io/metrics v0.19.0 k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.19.0 diff --git a/go.sum b/go.sum index 365203891..f85165a79 100644 --- a/go.sum +++ b/go.sum @@ -472,7 +472,6 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -482,7 +481,6 @@ github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 h1:0XM1XL/OFF github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -493,14 +491,12 @@ github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2 github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -523,7 +519,6 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -560,7 +555,6 @@ github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG github.com/thecodeteam/goscaleio v0.1.0/go.mod h1:68sdkZAsK8bvEwBlbQnlLS+xU+hvLYM/iQ8KXej1AwM= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -581,7 +575,6 @@ github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSf github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -591,12 +584,10 @@ go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.2 h1:jxcFYjlkl8xaERsgLo+RNquI0epW6zuy/ZRQs6jnrFA= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -666,7 +657,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -676,7 +666,6 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgN golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -709,7 +698,6 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -729,13 +717,11 @@ golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -789,7 +775,6 @@ google.golang.org/api v0.15.1 h1:5mMS6mYvK5LVB8+ujVBC33Y8gltBo/kT6HBm6kU80G4= google.golang.org/api v0.15.1/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= @@ -800,7 +785,6 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= @@ -813,7 +797,6 @@ google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -829,7 +812,6 @@ google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEG google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index cf2ed2ca1..22fd299d0 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -28,3 +28,10 @@ bash "${CODEGEN_PKG}"/generate-internal-groups.sh \ sigs.k8s.io/scheduler-plugins/pkg/apis \ "config:v1beta1" \ --go-header-file "${SCRIPT_ROOT}"/hack/boilerplate/boilerplate.generatego.txt + +bash "${CODEGEN_PKG}"/generate-groups.sh \ + all \ + sigs.k8s.io/scheduler-plugins/pkg/generated \ + sigs.k8s.io/scheduler-plugins/pkg/apis \ + podgroup:v1alpha1 \ + --go-header-file "${SCRIPT_ROOT}"/hack/boilerplate/boilerplate.generatego.txt diff --git a/pkg/apis/podgroup/register.go b/pkg/apis/podgroup/register.go new file mode 100644 index 000000000..d80241659 --- /dev/null +++ b/pkg/apis/podgroup/register.go @@ -0,0 +1,22 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package podgroup + +// GroupName is the group name used in this package +const ( + GroupName = "scheduling.sigs.k8s.io" +) diff --git a/pkg/apis/podgroup/v1alpha1/doc.go b/pkg/apis/podgroup/v1alpha1/doc.go new file mode 100644 index 000000000..8f099f2cb --- /dev/null +++ b/pkg/apis/podgroup/v1alpha1/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +k8s:deepcopy-gen=package +// +groupName=scheduling.sigs.k8s.io + +// Package v1alpha1 is the v1alpha1 version of the API. +package v1alpha1 // import "sigs.k8s.io/scheduler-plugins/pkg/apis/podgroup/v1alpha1" diff --git a/pkg/apis/podgroup/v1alpha1/register.go b/pkg/apis/podgroup/v1alpha1/register.go new file mode 100644 index 000000000..eddfe78f1 --- /dev/null +++ b/pkg/apis/podgroup/v1alpha1/register.go @@ -0,0 +1,55 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + + "sigs.k8s.io/scheduler-plugins/pkg/apis/podgroup" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: podgroup.GroupName, Version: "v1alpha1"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + // SchemeBuilder initializes a scheme builder + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + // AddToScheme is a global function that registers this API group & version to a scheme + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &PodGroup{}, + &PodGroupList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/podgroup/v1alpha1/types.go b/pkg/apis/podgroup/v1alpha1/types.go new file mode 100644 index 000000000..7b2e33baf --- /dev/null +++ b/pkg/apis/podgroup/v1alpha1/types.go @@ -0,0 +1,134 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// PodGroupPhase is the phase of a pod group at the current time. +type PodGroupPhase string + +// These are the valid phase of podGroups. +const ( + // PodPending means the pod group has been accepted by the system, but scheduler can not allocate + // enough resources to it. + PodGroupPending PodGroupPhase = "Pending" + + // PodRunning means `spec.minMember` pods of PodGroups has been in running phase. + PodGroupRunning PodGroupPhase = "Running" + + // PreScheduling means all of pods has been are waiting to be scheduled, enqueue waitingPod + PodGroupPreScheduling PodGroupPhase = "PreScheduling" + + // PodRunning means some of pods has been scheduling in running phase but have not reach the `spec. + // minMember` pods of PodGroups. + PodGroupScheduling PodGroupPhase = "Scheduling" + + // PodScheduled means `spec.minMember` pods of PodGroups have been scheduled finished and pods have been in running + // phase. + PodGroupScheduled PodGroupPhase = "Scheduled" + + // PodGroupUnknown means part of `spec.minMember` pods are running but the other part can not + // be scheduled, e.g. not enough resource; scheduler will wait for related controller to recover it. + PodGroupUnknown PodGroupPhase = "Unknown" + + // PodGroupFinish means all of `spec.minMember` pods are successfully. + PodGroupFinished PodGroupPhase = "Finished" + + // PodGroupFailed means at least one of `spec.minMember` pods is failed. + PodGroupFailed PodGroupPhase = "Failed" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PodGroup is a collection of Pod; used for batch workload. +type PodGroup struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Specification of the desired behavior of the pod group. + // +optional + Spec PodGroupSpec `json:"spec,omitempty"` + + // Status represents the current information about a pod group. + // This data may not be up to date. + // +optional + Status PodGroupStatus `json:"status,omitempty"` +} + +// PodGroupSpec represents the template of a pod group. +type PodGroupSpec struct { + // MinMember defines the minimal number of members/tasks to run the pod group; + // if there's not enough resources to start all tasks, the scheduler + // will not start anyone. + MinMember uint32 `json:"minMember,omitempty"` + + // MinResources defines the minimal resource of members/tasks to run the pod group; + // if there's not enough resources to start all tasks, the scheduler + // will not start anyone. + MinResources *v1.ResourceList `json:"minResources,omitempty"` + + // MaxScheduleTime defines the maximal time of members/tasks to wait before run the pod group; + MaxScheduleTime *metav1.Duration `json:"maxScheduleTime,omitempty"` +} + +// PodGroupStatus represents the current state of a pod group. +type PodGroupStatus struct { + // Current phase of PodGroup. + Phase PodGroupPhase `json:"phase,omitempty"` + + // OccupiedBy marks the workload (e.g., deployment, statefulset) UID that occupy the podgroup. + // It is empty if not initialized. + OccupiedBy string `json:"occupiedBy,omitempty"` + + // The number of actively running pods. + // +optional + Scheduled uint32 `json:"scheduled,omitempty"` + + // The number of actively running pods. + // +optional + Running uint32 `json:"running,omitempty"` + + // The number of pods which reached phase Succeeded. + // +optional + Succeeded uint32 `json:"succeeded,omitempty"` + + // The number of pods which reached phase Failed. + // +optional + Failed uint32 `json:"failed,omitempty"` + + // ScheduleStartTime of the group + ScheduleStartTime metav1.Time `json:"scheduleStartTime,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PodGroupList is a collection of pod groups. +type PodGroupList struct { + metav1.TypeMeta `json:",inline"` + // Standard list metadata + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + + // Items is the list of PodGroup + Items []PodGroup `json:"items"` +} diff --git a/pkg/apis/podgroup/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/podgroup/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..1d210cada --- /dev/null +++ b/pkg/apis/podgroup/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,138 @@ +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/api/core/v1" + resource "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodGroup) DeepCopyInto(out *PodGroup) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodGroup. +func (in *PodGroup) DeepCopy() *PodGroup { + if in == nil { + return nil + } + out := new(PodGroup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PodGroup) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodGroupList) DeepCopyInto(out *PodGroupList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PodGroup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodGroupList. +func (in *PodGroupList) DeepCopy() *PodGroupList { + if in == nil { + return nil + } + out := new(PodGroupList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PodGroupList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodGroupSpec) DeepCopyInto(out *PodGroupSpec) { + *out = *in + if in.MinResources != nil { + in, out := &in.MinResources, &out.MinResources + *out = new(v1.ResourceList) + if **in != nil { + in, out := *in, *out + *out = make(map[v1.ResourceName]resource.Quantity, len(*in)) + for key, val := range *in { + (*out)[key] = val.DeepCopy() + } + } + } + if in.MaxScheduleTime != nil { + in, out := &in.MaxScheduleTime, &out.MaxScheduleTime + *out = new(metav1.Duration) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodGroupSpec. +func (in *PodGroupSpec) DeepCopy() *PodGroupSpec { + if in == nil { + return nil + } + out := new(PodGroupSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodGroupStatus) DeepCopyInto(out *PodGroupStatus) { + *out = *in + in.ScheduleStartTime.DeepCopyInto(&out.ScheduleStartTime) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodGroupStatus. +func (in *PodGroupStatus) DeepCopy() *PodGroupStatus { + if in == nil { + return nil + } + out := new(PodGroupStatus) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/podgroup/v1alpha1/zz_generated.defaults.go b/pkg/apis/podgroup/v1alpha1/zz_generated.defaults.go new file mode 100644 index 000000000..dd621a3ac --- /dev/null +++ b/pkg/apis/podgroup/v1alpha1/zz_generated.defaults.go @@ -0,0 +1,32 @@ +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + return nil +} diff --git a/pkg/controller/podgroup.go b/pkg/controller/podgroup.go new file mode 100644 index 000000000..93236d800 --- /dev/null +++ b/pkg/controller/podgroup.go @@ -0,0 +1,268 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "reflect" + "time" + + v1 "k8s.io/api/core/v1" + apierrs "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + coreinformer "k8s.io/client-go/informers/core/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + corelister "k8s.io/client-go/listers/core/v1" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog/v2" + + pgv1 "sigs.k8s.io/scheduler-plugins/pkg/apis/podgroup/v1alpha1" + pgclientset "sigs.k8s.io/scheduler-plugins/pkg/generated/clientset/versioned" + pginformer "sigs.k8s.io/scheduler-plugins/pkg/generated/informers/externalversions/podgroup/v1alpha1" + pglister "sigs.k8s.io/scheduler-plugins/pkg/generated/listers/podgroup/v1alpha1" + "sigs.k8s.io/scheduler-plugins/pkg/util" +) + +// PodGroupController is a controller that process pod groups using provided Handler interface +type PodGroupController struct { + eventRecorder record.EventRecorder + pgQueue workqueue.RateLimitingInterface + pgLister pglister.PodGroupLister + podLister corelister.PodLister + pgListerSynced cache.InformerSynced + podListerSynced cache.InformerSynced + pgClient pgclientset.Interface +} + +// NewPodGroupController returns a new *PodGroupController +func NewPodGroupController(client kubernetes.Interface, + pgInformer pginformer.PodGroupInformer, + podInformer coreinformer.PodInformer, + pgClient pgclientset.Interface) *PodGroupController { + broadcaster := record.NewBroadcaster() + broadcaster.StartRecordingToSink(&corev1.EventSinkImpl{Interface: client.CoreV1().Events(v1.NamespaceAll)}) + + ctrl := &PodGroupController{ + eventRecorder: broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "Coscheduling"}), + pgQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Coscheduling-queue"), + } + + pgInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: ctrl.pgAdded, + UpdateFunc: ctrl.pgUpdated, + }) + podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: ctrl.podAdded, + UpdateFunc: ctrl.podUpdated, + }) + + ctrl.pgLister = pgInformer.Lister() + ctrl.podLister = podInformer.Lister() + ctrl.pgListerSynced = pgInformer.Informer().HasSynced + ctrl.podListerSynced = podInformer.Informer().HasSynced + ctrl.pgClient = pgClient + return ctrl +} + +// Run starts listening on channel events +func (ctrl *PodGroupController) Run(workers int, stopCh <-chan struct{}) { + defer ctrl.pgQueue.ShutDown() + + klog.Info("Starting coscheduling") + defer klog.Info("Shutting coscheduling") + + if !cache.WaitForCacheSync(stopCh, ctrl.pgListerSynced, ctrl.pgListerSynced) { + klog.Error("Cannot sync caches") + return + } + klog.Info("Coscheduling sync finished") + for i := 0; i < workers; i++ { + go wait.Until(ctrl.sync, 0, stopCh) + } + + <-stopCh +} + +// pgAdded reacts to a PG creation +func (ctrl *PodGroupController) pgAdded(obj interface{}) { + key, err := cache.MetaNamespaceKeyFunc(obj) + if err != nil { + runtime.HandleError(err) + return + } + pg := obj.(*pgv1.PodGroup) + if pg.Status.Phase == pgv1.PodGroupFinished || pg.Status.Phase == pgv1.PodGroupFailed { + return + } + // If startScheduleTime - createTime > 2days, do not enqueue again because pod may have been GCed + if pg.Status.Scheduled == pg.Spec.MinMember && pg.Status.Running == 0 && + pg.Status.ScheduleStartTime.Sub(pg.CreationTimestamp.Time) > 48*time.Hour { + return + } + klog.Infof("Enqueue key %v", key) + ctrl.pgQueue.Add(key) +} + +// pgUpdated reacts to a PG update +func (ctrl *PodGroupController) pgUpdated(old, new interface{}) { + ctrl.pgAdded(new) +} + +// podAdded reacts to a PG creation +func (ctrl *PodGroupController) podAdded(obj interface{}) { + pod := obj.(*v1.Pod) + pgName, ok := util.VerifyPodLabelSatisfied(pod) + if !ok || len(pgName) == 0 { + return + } + pg, err := ctrl.pgLister.PodGroups(pod.Namespace).Get(pgName) + if err != nil { + klog.Error(err) + return + } + klog.V(5).Infof("Add pg %v when pod %v add", pg.Name, pod.Name) + ctrl.pgAdded(pg) +} + +// pgUpdated reacts to a PG update +func (ctrl *PodGroupController) podUpdated(old, new interface{}) { + ctrl.podAdded(new) +} + +// syncPG deals with one key off the queue. It returns false when it's time to quit. +func (ctrl *PodGroupController) sync() { + keyObj, quit := ctrl.pgQueue.Get() + if quit { + return + } + defer ctrl.pgQueue.Done(keyObj) + + key := keyObj.(string) + namespace, pgName, err := cache.SplitMetaNamespaceKey(key) + klog.V(4).Infof("Started PG processing %q", pgName) + + // get PG to process + pg, err := ctrl.pgLister.PodGroups(namespace).Get(pgName) + ctx := context.TODO() + if err != nil { + if apierrs.IsNotFound(err) { + pg, err = ctrl.pgClient.SchedulingV1alpha1().PodGroups(namespace).Get(ctx, pgName, metav1.GetOptions{}) + if err != nil && apierrs.IsNotFound(err) { + // PG was deleted in the meantime, ignore. + klog.V(3).Infof("PG %q deleted", pgName) + return + } + } + klog.Errorf("Error getting PodGroup %q: %v", pgName, err) + ctrl.pgQueue.AddRateLimited(keyObj) + return + } + ctrl.syncHandler(ctx, pg) +} + +// syncHandle syncs pod group and convert status +func (ctrl *PodGroupController) syncHandler(ctx context.Context, pg *pgv1.PodGroup) { + key, err := cache.MetaNamespaceKeyFunc(pg) + if err != nil { + runtime.HandleError(err) + return + } + + defer func() { + if err != nil { + ctrl.pgQueue.AddRateLimited(key) + return + } + }() + + pgCopy := pg.DeepCopy() + if string(pgCopy.Status.Phase) == "" { + pgCopy.Status.Phase = pgv1.PodGroupPending + } + + selector := labels.Set(map[string]string{util.PodGroupLabel: pgCopy.Name}).AsSelector() + pods, err := ctrl.podLister.List(selector) + if err != nil { + klog.Errorf("List pods for group %v failed: %v", pgCopy.Name, err) + return + } + var ( + running uint32 = 0 + succeeded uint32 = 0 + failed uint32 = 0 + ) + if len(pods) != 0 { + for _, pod := range pods { + switch pod.Status.Phase { + case v1.PodRunning: + running++ + case v1.PodSucceeded: + succeeded++ + case v1.PodFailed: + failed++ + } + } + } + pgCopy.Status.Failed = failed + pgCopy.Status.Succeeded = succeeded + pgCopy.Status.Running = running + + if pgCopy.Status.Scheduled >= pgCopy.Spec.MinMember && pgCopy.Status.Phase == pgv1.PodGroupScheduling { + pgCopy.Status.Phase = pgv1.PodGroupScheduled + } + + if pgCopy.Status.Succeeded+pgCopy.Status.Running >= pg.Spec.MinMember && pgCopy.Status.Phase == pgv1.PodGroupScheduled { + pgCopy.Status.Phase = pgv1.PodGroupRunning + } + // Final state of pod group + if pgCopy.Status.Failed != 0 && pgCopy.Status.Failed+pgCopy.Status.Running+pgCopy.Status.Succeeded >= pg.Spec. + MinMember { + pgCopy.Status.Phase = pgv1.PodGroupFailed + } + if pgCopy.Status.Succeeded >= pg.Spec.MinMember { + pgCopy.Status.Phase = pgv1.PodGroupFinished + } + + err = ctrl.patchPodGroup(pg, pgCopy) + if err == nil { + ctrl.pgQueue.Forget(pg) + } +} + +func (ctrl *PodGroupController) patchPodGroup(old, new *pgv1.PodGroup) error { + if !reflect.DeepEqual(old, new) { + patch, err := util.CreateMergePatch(old, new) + if err != nil { + return err + } + + _, err = ctrl.pgClient.SchedulingV1alpha1().PodGroups(old.Namespace).Patch(context.TODO(), old.Name, types.MergePatchType, + patch, metav1.PatchOptions{}) + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/controller/podgroup_test.go b/pkg/controller/podgroup_test.go new file mode 100644 index 000000000..2b7e61fd7 --- /dev/null +++ b/pkg/controller/podgroup_test.go @@ -0,0 +1,186 @@ +package controller + +import ( + "context" + "fmt" + "k8s.io/apimachinery/pkg/util/wait" + "testing" + "time" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "k8s.io/kubernetes/pkg/controller" + st "k8s.io/kubernetes/pkg/scheduler/testing" + + "sigs.k8s.io/scheduler-plugins/pkg/apis/podgroup/v1alpha1" + pgfake "sigs.k8s.io/scheduler-plugins/pkg/generated/clientset/versioned/fake" + pgformers "sigs.k8s.io/scheduler-plugins/pkg/generated/informers/externalversions" + "sigs.k8s.io/scheduler-plugins/pkg/util" +) + +func Test_Run(t *testing.T) { + ctx := context.TODO() + createTime := metav1.Time{Time: time.Now().Add(-72 * time.Hour)} + cases := []struct { + name string + pgName string + minMember uint32 + podNames []string + podNextPhase v1.PodPhase + podPhase v1.PodPhase + previousPhase v1alpha1.PodGroupPhase + desiredGroupPhase v1alpha1.PodGroupPhase + podGroupCreateTime *metav1.Time + }{ + { + name: "Group running", + pgName: "pg1", + minMember: 2, + podNames: []string{"pod1", "pod2"}, + podPhase: v1.PodRunning, + previousPhase: v1alpha1.PodGroupScheduled, + desiredGroupPhase: v1alpha1.PodGroupRunning, + }, + { + name: "Group running, more than min member", + pgName: "pg11", + minMember: 2, + podNames: []string{"pod11", "pod21"}, + podPhase: v1.PodRunning, + previousPhase: v1alpha1.PodGroupScheduled, + desiredGroupPhase: v1alpha1.PodGroupRunning, + }, + { + name: "Group failed", + pgName: "pg2", + minMember: 2, + podNames: []string{"pod1", "pod2"}, + podPhase: v1.PodFailed, + previousPhase: v1alpha1.PodGroupScheduled, + desiredGroupPhase: v1alpha1.PodGroupFailed, + }, + { + name: "Group finished", + pgName: "pg3", + minMember: 2, + podNames: []string{"pod1", "pod2"}, + podPhase: v1.PodSucceeded, + previousPhase: v1alpha1.PodGroupScheduled, + desiredGroupPhase: v1alpha1.PodGroupFinished, + }, + { + name: "Group status convert from scheduling to scheduled", + pgName: "pg4", + minMember: 2, + podNames: []string{"pod1", "pod2"}, + podPhase: v1.PodPending, + previousPhase: v1alpha1.PodGroupScheduling, + desiredGroupPhase: v1alpha1.PodGroupScheduled, + }, + { + name: "Group status convert from scheduling to succeed", + pgName: "pg5", + minMember: 2, + podNames: []string{"pod1", "pod2"}, + podPhase: v1.PodPending, + previousPhase: v1alpha1.PodGroupScheduling, + desiredGroupPhase: v1alpha1.PodGroupFinished, + podNextPhase: v1.PodSucceeded, + }, + { + name: "Group status convert from scheduling to succeed", + pgName: "pg6", + minMember: 2, + podNames: []string{"pod1", "pod2"}, + podPhase: v1.PodPending, + previousPhase: v1alpha1.PodGroupScheduling, + desiredGroupPhase: v1alpha1.PodGroupFailed, + podNextPhase: v1.PodSucceeded, + }, + { + name: "Group group should not enqueue, created too long", + pgName: "pg7", + minMember: 2, + podNames: []string{"pod1", "pod2"}, + podPhase: v1.PodRunning, + previousPhase: v1alpha1.PodGroupPending, + desiredGroupPhase: v1alpha1.PodGroupPending, + podGroupCreateTime: &createTime, + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + ps := makePods(c.podNames, c.pgName, c.podPhase) + kubeClient := fake.NewSimpleClientset(ps[0], ps[1]) + pg := makePG(c.pgName, 2, c.previousPhase, c.podGroupCreateTime) + pgClient := pgfake.NewSimpleClientset(pg) + + informerFactory := informers.NewSharedInformerFactory(kubeClient, controller.NoResyncPeriodFunc()) + pgInformerFactory := pgformers.NewSharedInformerFactory(pgClient, controller.NoResyncPeriodFunc()) + podInformer := informerFactory.Core().V1().Pods() + pgInformer := pgInformerFactory.Scheduling().V1alpha1().PodGroups() + ctrl := NewPodGroupController(kubeClient, pgInformer, podInformer, pgClient) + + pgInformerFactory.Start(ctx.Done()) + informerFactory.Start(ctx.Done()) + // 0 means not set + if len(c.podNextPhase) != 0 { + ps := makePods(c.podNames, c.pgName, c.podNextPhase) + for _, p := range ps { + kubeClient.CoreV1().Pods(p.Namespace).UpdateStatus(ctx, p, metav1.UpdateOptions{}) + } + } + go ctrl.Run(1, ctx.Done()) + err := wait.Poll(200*time.Millisecond, 1*time.Second, func() (done bool, err error) { + pg, err := pgClient.SchedulingV1alpha1().PodGroups("default").Get(ctx, c.pgName, metav1.GetOptions{}) + if err != nil { + return false, err + } + if pg.Status.Phase != c.desiredGroupPhase { + return false, fmt.Errorf("want %v, got %v", c.desiredGroupPhase, pg.Status.Phase) + } + return true, nil + }) + if err != nil { + + } + }) + } + +} + +func makePods(podNames []string, pgName string, phase v1.PodPhase) []*v1.Pod { + pds := make([]*v1.Pod, 0) + for _, name := range podNames { + pod := st.MakePod().Namespace("default").Name(name).Obj() + pod.Labels = map[string]string{util.PodGroupLabel: pgName} + pod.Status.Phase = phase + pds = append(pds, pod) + } + return pds +} + +func makePG(pgName string, minMember uint32, previousPhase v1alpha1.PodGroupPhase, createTime *metav1.Time) *v1alpha1.PodGroup { + pg := &v1alpha1.PodGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: pgName, + Namespace: "default", + CreationTimestamp: metav1.Time{Time: time.Now()}, + }, + Spec: v1alpha1.PodGroupSpec{ + MinMember: minMember, + }, + Status: v1alpha1.PodGroupStatus{ + OccupiedBy: "test", + Scheduled: minMember, + ScheduleStartTime: metav1.Time{Time: time.Now()}, + Phase: previousPhase, + }, + } + if createTime != nil { + pg.CreationTimestamp = *createTime + } + return pg +} diff --git a/pkg/generated/clientset/versioned/clientset.go b/pkg/generated/clientset/versioned/clientset.go new file mode 100644 index 000000000..46fa0236f --- /dev/null +++ b/pkg/generated/clientset/versioned/clientset.go @@ -0,0 +1,97 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + "fmt" + + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" + schedulingv1alpha1 "sigs.k8s.io/scheduler-plugins/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + SchedulingV1alpha1() schedulingv1alpha1.SchedulingV1alpha1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + schedulingV1alpha1 *schedulingv1alpha1.SchedulingV1alpha1Client +} + +// SchedulingV1alpha1 retrieves the SchedulingV1alpha1Client +func (c *Clientset) SchedulingV1alpha1() schedulingv1alpha1.SchedulingV1alpha1Interface { + return c.schedulingV1alpha1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfig will generate a rate-limiter in configShallowCopy. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + if configShallowCopy.Burst <= 0 { + return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") + } + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.schedulingV1alpha1, err = schedulingv1alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.schedulingV1alpha1 = schedulingv1alpha1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.schedulingV1alpha1 = schedulingv1alpha1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/pkg/generated/clientset/versioned/doc.go b/pkg/generated/clientset/versioned/doc.go new file mode 100644 index 000000000..41721ca52 --- /dev/null +++ b/pkg/generated/clientset/versioned/doc.go @@ -0,0 +1,20 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated clientset. +package versioned diff --git a/pkg/generated/clientset/versioned/fake/clientset_generated.go b/pkg/generated/clientset/versioned/fake/clientset_generated.go new file mode 100644 index 000000000..1d2f38eff --- /dev/null +++ b/pkg/generated/clientset/versioned/fake/clientset_generated.go @@ -0,0 +1,82 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/discovery" + fakediscovery "k8s.io/client-go/discovery/fake" + "k8s.io/client-go/testing" + clientset "sigs.k8s.io/scheduler-plugins/pkg/generated/clientset/versioned" + schedulingv1alpha1 "sigs.k8s.io/scheduler-plugins/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1" + fakeschedulingv1alpha1 "sigs.k8s.io/scheduler-plugins/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/fake" +) + +// NewSimpleClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +func NewSimpleClientset(objects ...runtime.Object) *Clientset { + o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{tracker: o} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +// Clientset implements clientset.Interface. Meant to be embedded into a +// struct to get a default implementation. This makes faking out just the method +// you want to test easier. +type Clientset struct { + testing.Fake + discovery *fakediscovery.FakeDiscovery + tracker testing.ObjectTracker +} + +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + return c.discovery +} + +func (c *Clientset) Tracker() testing.ObjectTracker { + return c.tracker +} + +var _ clientset.Interface = &Clientset{} + +// SchedulingV1alpha1 retrieves the SchedulingV1alpha1Client +func (c *Clientset) SchedulingV1alpha1() schedulingv1alpha1.SchedulingV1alpha1Interface { + return &fakeschedulingv1alpha1.FakeSchedulingV1alpha1{Fake: &c.Fake} +} diff --git a/pkg/generated/clientset/versioned/fake/doc.go b/pkg/generated/clientset/versioned/fake/doc.go new file mode 100644 index 000000000..9b99e7167 --- /dev/null +++ b/pkg/generated/clientset/versioned/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated fake clientset. +package fake diff --git a/pkg/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go new file mode 100644 index 000000000..4a16f96f0 --- /dev/null +++ b/pkg/generated/clientset/versioned/fake/register.go @@ -0,0 +1,56 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + schedulingv1alpha1 "sigs.k8s.io/scheduler-plugins/pkg/apis/podgroup/v1alpha1" +) + +var scheme = runtime.NewScheme() +var codecs = serializer.NewCodecFactory(scheme) + +var localSchemeBuilder = runtime.SchemeBuilder{ + schedulingv1alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(scheme)) +} diff --git a/pkg/generated/clientset/versioned/scheme/doc.go b/pkg/generated/clientset/versioned/scheme/doc.go new file mode 100644 index 000000000..7dc375616 --- /dev/null +++ b/pkg/generated/clientset/versioned/scheme/doc.go @@ -0,0 +1,20 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/pkg/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go new file mode 100644 index 000000000..f8286385f --- /dev/null +++ b/pkg/generated/clientset/versioned/scheme/register.go @@ -0,0 +1,56 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + schedulingv1alpha1 "sigs.k8s.io/scheduler-plugins/pkg/apis/podgroup/v1alpha1" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + schedulingv1alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(Scheme)) +} diff --git a/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/doc.go b/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/doc.go new file mode 100644 index 000000000..df51baa4d --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/fake/doc.go b/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/fake/doc.go new file mode 100644 index 000000000..16f443990 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/fake/fake_podgroup.go b/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/fake/fake_podgroup.go new file mode 100644 index 000000000..c59419d5e --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/fake/fake_podgroup.go @@ -0,0 +1,142 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha1 "sigs.k8s.io/scheduler-plugins/pkg/apis/podgroup/v1alpha1" +) + +// FakePodGroups implements PodGroupInterface +type FakePodGroups struct { + Fake *FakeSchedulingV1alpha1 + ns string +} + +var podgroupsResource = schema.GroupVersionResource{Group: "scheduling.sigs.k8s.io", Version: "v1alpha1", Resource: "podgroups"} + +var podgroupsKind = schema.GroupVersionKind{Group: "scheduling.sigs.k8s.io", Version: "v1alpha1", Kind: "PodGroup"} + +// Get takes name of the podGroup, and returns the corresponding podGroup object, and an error if there is any. +func (c *FakePodGroups) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.PodGroup, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(podgroupsResource, c.ns, name), &v1alpha1.PodGroup{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PodGroup), err +} + +// List takes label and field selectors, and returns the list of PodGroups that match those selectors. +func (c *FakePodGroups) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.PodGroupList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(podgroupsResource, podgroupsKind, c.ns, opts), &v1alpha1.PodGroupList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.PodGroupList{ListMeta: obj.(*v1alpha1.PodGroupList).ListMeta} + for _, item := range obj.(*v1alpha1.PodGroupList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested podGroups. +func (c *FakePodGroups) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(podgroupsResource, c.ns, opts)) + +} + +// Create takes the representation of a podGroup and creates it. Returns the server's representation of the podGroup, and an error, if there is any. +func (c *FakePodGroups) Create(ctx context.Context, podGroup *v1alpha1.PodGroup, opts v1.CreateOptions) (result *v1alpha1.PodGroup, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(podgroupsResource, c.ns, podGroup), &v1alpha1.PodGroup{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PodGroup), err +} + +// Update takes the representation of a podGroup and updates it. Returns the server's representation of the podGroup, and an error, if there is any. +func (c *FakePodGroups) Update(ctx context.Context, podGroup *v1alpha1.PodGroup, opts v1.UpdateOptions) (result *v1alpha1.PodGroup, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(podgroupsResource, c.ns, podGroup), &v1alpha1.PodGroup{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PodGroup), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakePodGroups) UpdateStatus(ctx context.Context, podGroup *v1alpha1.PodGroup, opts v1.UpdateOptions) (*v1alpha1.PodGroup, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(podgroupsResource, "status", c.ns, podGroup), &v1alpha1.PodGroup{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PodGroup), err +} + +// Delete takes name of the podGroup and deletes it. Returns an error if one occurs. +func (c *FakePodGroups) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(podgroupsResource, c.ns, name), &v1alpha1.PodGroup{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakePodGroups) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(podgroupsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.PodGroupList{}) + return err +} + +// Patch applies the patch and returns the patched podGroup. +func (c *FakePodGroups) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.PodGroup, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(podgroupsResource, c.ns, name, pt, data, subresources...), &v1alpha1.PodGroup{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PodGroup), err +} diff --git a/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/fake/fake_podgroup_client.go b/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/fake/fake_podgroup_client.go new file mode 100644 index 000000000..1bf14cf3a --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/fake/fake_podgroup_client.go @@ -0,0 +1,40 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" + v1alpha1 "sigs.k8s.io/scheduler-plugins/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1" +) + +type FakeSchedulingV1alpha1 struct { + *testing.Fake +} + +func (c *FakeSchedulingV1alpha1) PodGroups(namespace string) v1alpha1.PodGroupInterface { + return &FakePodGroups{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeSchedulingV1alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/generated_expansion.go new file mode 100644 index 000000000..999084df8 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/generated_expansion.go @@ -0,0 +1,21 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +type PodGroupExpansion interface{} diff --git a/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/podgroup.go b/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/podgroup.go new file mode 100644 index 000000000..13a0b05dd --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/podgroup.go @@ -0,0 +1,195 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1alpha1 "sigs.k8s.io/scheduler-plugins/pkg/apis/podgroup/v1alpha1" + scheme "sigs.k8s.io/scheduler-plugins/pkg/generated/clientset/versioned/scheme" +) + +// PodGroupsGetter has a method to return a PodGroupInterface. +// A group's client should implement this interface. +type PodGroupsGetter interface { + PodGroups(namespace string) PodGroupInterface +} + +// PodGroupInterface has methods to work with PodGroup resources. +type PodGroupInterface interface { + Create(ctx context.Context, podGroup *v1alpha1.PodGroup, opts v1.CreateOptions) (*v1alpha1.PodGroup, error) + Update(ctx context.Context, podGroup *v1alpha1.PodGroup, opts v1.UpdateOptions) (*v1alpha1.PodGroup, error) + UpdateStatus(ctx context.Context, podGroup *v1alpha1.PodGroup, opts v1.UpdateOptions) (*v1alpha1.PodGroup, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.PodGroup, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.PodGroupList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.PodGroup, err error) + PodGroupExpansion +} + +// podGroups implements PodGroupInterface +type podGroups struct { + client rest.Interface + ns string +} + +// newPodGroups returns a PodGroups +func newPodGroups(c *SchedulingV1alpha1Client, namespace string) *podGroups { + return &podGroups{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the podGroup, and returns the corresponding podGroup object, and an error if there is any. +func (c *podGroups) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.PodGroup, err error) { + result = &v1alpha1.PodGroup{} + err = c.client.Get(). + Namespace(c.ns). + Resource("podgroups"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of PodGroups that match those selectors. +func (c *podGroups) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.PodGroupList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.PodGroupList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("podgroups"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested podGroups. +func (c *podGroups) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("podgroups"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a podGroup and creates it. Returns the server's representation of the podGroup, and an error, if there is any. +func (c *podGroups) Create(ctx context.Context, podGroup *v1alpha1.PodGroup, opts v1.CreateOptions) (result *v1alpha1.PodGroup, err error) { + result = &v1alpha1.PodGroup{} + err = c.client.Post(). + Namespace(c.ns). + Resource("podgroups"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(podGroup). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a podGroup and updates it. Returns the server's representation of the podGroup, and an error, if there is any. +func (c *podGroups) Update(ctx context.Context, podGroup *v1alpha1.PodGroup, opts v1.UpdateOptions) (result *v1alpha1.PodGroup, err error) { + result = &v1alpha1.PodGroup{} + err = c.client.Put(). + Namespace(c.ns). + Resource("podgroups"). + Name(podGroup.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(podGroup). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *podGroups) UpdateStatus(ctx context.Context, podGroup *v1alpha1.PodGroup, opts v1.UpdateOptions) (result *v1alpha1.PodGroup, err error) { + result = &v1alpha1.PodGroup{} + err = c.client.Put(). + Namespace(c.ns). + Resource("podgroups"). + Name(podGroup.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(podGroup). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the podGroup and deletes it. Returns an error if one occurs. +func (c *podGroups) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("podgroups"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *podGroups) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("podgroups"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched podGroup. +func (c *podGroups) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.PodGroup, err error) { + result = &v1alpha1.PodGroup{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("podgroups"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/podgroup_client.go b/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/podgroup_client.go new file mode 100644 index 000000000..c507d39d7 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/podgroup/v1alpha1/podgroup_client.go @@ -0,0 +1,89 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + rest "k8s.io/client-go/rest" + v1alpha1 "sigs.k8s.io/scheduler-plugins/pkg/apis/podgroup/v1alpha1" + "sigs.k8s.io/scheduler-plugins/pkg/generated/clientset/versioned/scheme" +) + +type SchedulingV1alpha1Interface interface { + RESTClient() rest.Interface + PodGroupsGetter +} + +// SchedulingV1alpha1Client is used to interact with features provided by the scheduling.sigs.k8s.io group. +type SchedulingV1alpha1Client struct { + restClient rest.Interface +} + +func (c *SchedulingV1alpha1Client) PodGroups(namespace string) PodGroupInterface { + return newPodGroups(c, namespace) +} + +// NewForConfig creates a new SchedulingV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*SchedulingV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &SchedulingV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new SchedulingV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *SchedulingV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new SchedulingV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *SchedulingV1alpha1Client { + return &SchedulingV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *SchedulingV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/generated/informers/externalversions/factory.go b/pkg/generated/informers/externalversions/factory.go new file mode 100644 index 000000000..b49277e35 --- /dev/null +++ b/pkg/generated/informers/externalversions/factory.go @@ -0,0 +1,180 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + reflect "reflect" + sync "sync" + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" + versioned "sigs.k8s.io/scheduler-plugins/pkg/generated/clientset/versioned" + internalinterfaces "sigs.k8s.io/scheduler-plugins/pkg/generated/informers/externalversions/internalinterfaces" + podgroup "sigs.k8s.io/scheduler-plugins/pkg/generated/informers/externalversions/podgroup" +) + +// SharedInformerOption defines the functional option type for SharedInformerFactory. +type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory + +type sharedInformerFactory struct { + client versioned.Interface + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc + lock sync.Mutex + defaultResync time.Duration + customResync map[reflect.Type]time.Duration + + informers map[reflect.Type]cache.SharedIndexInformer + // startedInformers is used for tracking which informers have been started. + // This allows Start() to be called multiple times safely. + startedInformers map[reflect.Type]bool +} + +// WithCustomResyncConfig sets a custom resync period for the specified informer types. +func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + for k, v := range resyncConfig { + factory.customResync[reflect.TypeOf(k)] = v + } + return factory + } +} + +// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. +func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.tweakListOptions = tweakListOptions + return factory + } +} + +// WithNamespace limits the SharedInformerFactory to the specified namespace. +func WithNamespace(namespace string) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.namespace = namespace + return factory + } +} + +// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. +func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync) +} + +// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. +// Listers obtained via this SharedInformerFactory will be subject to the same filters +// as specified here. +// Deprecated: Please use NewSharedInformerFactoryWithOptions instead +func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) +} + +// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. +func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { + factory := &sharedInformerFactory{ + client: client, + namespace: v1.NamespaceAll, + defaultResync: defaultResync, + informers: make(map[reflect.Type]cache.SharedIndexInformer), + startedInformers: make(map[reflect.Type]bool), + customResync: make(map[reflect.Type]time.Duration), + } + + // Apply all options + for _, opt := range options { + factory = opt(factory) + } + + return factory +} + +// Start initializes all requested informers. +func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { + f.lock.Lock() + defer f.lock.Unlock() + + for informerType, informer := range f.informers { + if !f.startedInformers[informerType] { + go informer.Run(stopCh) + f.startedInformers[informerType] = true + } + } +} + +// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { + informers := func() map[reflect.Type]cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informers := map[reflect.Type]cache.SharedIndexInformer{} + for informerType, informer := range f.informers { + if f.startedInformers[informerType] { + informers[informerType] = informer + } + } + return informers + }() + + res := map[reflect.Type]bool{} + for informType, informer := range informers { + res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) + } + return res +} + +// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// client. +func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informerType := reflect.TypeOf(obj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + + resyncPeriod, exists := f.customResync[informerType] + if !exists { + resyncPeriod = f.defaultResync + } + + informer = newFunc(f.client, resyncPeriod) + f.informers[informerType] = informer + + return informer +} + +// SharedInformerFactory provides shared informers for resources in all known +// API group versions. +type SharedInformerFactory interface { + internalinterfaces.SharedInformerFactory + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + + Scheduling() podgroup.Interface +} + +func (f *sharedInformerFactory) Scheduling() podgroup.Interface { + return podgroup.New(f, f.namespace, f.tweakListOptions) +} diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go new file mode 100644 index 000000000..ce6a33e33 --- /dev/null +++ b/pkg/generated/informers/externalversions/generic.go @@ -0,0 +1,62 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + "fmt" + + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" + v1alpha1 "sigs.k8s.io/scheduler-plugins/pkg/apis/podgroup/v1alpha1" +) + +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() cache.SharedIndexInformer + Lister() cache.GenericLister +} + +type genericInformer struct { + informer cache.SharedIndexInformer + resource schema.GroupResource +} + +// Informer returns the SharedIndexInformer. +func (f *genericInformer) Informer() cache.SharedIndexInformer { + return f.informer +} + +// Lister returns the GenericLister. +func (f *genericInformer) Lister() cache.GenericLister { + return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) +} + +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { + switch resource { + // Group=scheduling.sigs.k8s.io, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithResource("podgroups"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Scheduling().V1alpha1().PodGroups().Informer()}, nil + + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} diff --git a/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go new file mode 100644 index 000000000..4be8496c9 --- /dev/null +++ b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -0,0 +1,40 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package internalinterfaces + +import ( + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + cache "k8s.io/client-go/tools/cache" + versioned "sigs.k8s.io/scheduler-plugins/pkg/generated/clientset/versioned" +) + +// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. +type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer + +// SharedInformerFactory a small interface to allow for adding an informer without an import cycle +type SharedInformerFactory interface { + Start(stopCh <-chan struct{}) + InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer +} + +// TweakListOptionsFunc is a function that transforms a v1.ListOptions. +type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/pkg/generated/informers/externalversions/podgroup/interface.go b/pkg/generated/informers/externalversions/podgroup/interface.go new file mode 100644 index 000000000..718c092fd --- /dev/null +++ b/pkg/generated/informers/externalversions/podgroup/interface.go @@ -0,0 +1,46 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package podgroup + +import ( + internalinterfaces "sigs.k8s.io/scheduler-plugins/pkg/generated/informers/externalversions/internalinterfaces" + v1alpha1 "sigs.k8s.io/scheduler-plugins/pkg/generated/informers/externalversions/podgroup/v1alpha1" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1alpha1 provides access to shared informers for resources in V1alpha1. + V1alpha1() v1alpha1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V1alpha1 returns a new v1alpha1.Interface. +func (g *group) V1alpha1() v1alpha1.Interface { + return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/generated/informers/externalversions/podgroup/v1alpha1/interface.go b/pkg/generated/informers/externalversions/podgroup/v1alpha1/interface.go new file mode 100644 index 000000000..b825827e6 --- /dev/null +++ b/pkg/generated/informers/externalversions/podgroup/v1alpha1/interface.go @@ -0,0 +1,45 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + internalinterfaces "sigs.k8s.io/scheduler-plugins/pkg/generated/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // PodGroups returns a PodGroupInformer. + PodGroups() PodGroupInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// PodGroups returns a PodGroupInformer. +func (v *version) PodGroups() PodGroupInformer { + return &podGroupInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/generated/informers/externalversions/podgroup/v1alpha1/podgroup.go b/pkg/generated/informers/externalversions/podgroup/v1alpha1/podgroup.go new file mode 100644 index 000000000..116017184 --- /dev/null +++ b/pkg/generated/informers/externalversions/podgroup/v1alpha1/podgroup.go @@ -0,0 +1,90 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" + podgroupv1alpha1 "sigs.k8s.io/scheduler-plugins/pkg/apis/podgroup/v1alpha1" + versioned "sigs.k8s.io/scheduler-plugins/pkg/generated/clientset/versioned" + internalinterfaces "sigs.k8s.io/scheduler-plugins/pkg/generated/informers/externalversions/internalinterfaces" + v1alpha1 "sigs.k8s.io/scheduler-plugins/pkg/generated/listers/podgroup/v1alpha1" +) + +// PodGroupInformer provides access to a shared informer and lister for +// PodGroups. +type PodGroupInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.PodGroupLister +} + +type podGroupInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewPodGroupInformer constructs a new informer for PodGroup type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewPodGroupInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredPodGroupInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredPodGroupInformer constructs a new informer for PodGroup type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredPodGroupInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.SchedulingV1alpha1().PodGroups(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.SchedulingV1alpha1().PodGroups(namespace).Watch(context.TODO(), options) + }, + }, + &podgroupv1alpha1.PodGroup{}, + resyncPeriod, + indexers, + ) +} + +func (f *podGroupInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredPodGroupInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *podGroupInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&podgroupv1alpha1.PodGroup{}, f.defaultInformer) +} + +func (f *podGroupInformer) Lister() v1alpha1.PodGroupLister { + return v1alpha1.NewPodGroupLister(f.Informer().GetIndexer()) +} diff --git a/pkg/generated/listers/podgroup/v1alpha1/expansion_generated.go b/pkg/generated/listers/podgroup/v1alpha1/expansion_generated.go new file mode 100644 index 000000000..4bee06cb8 --- /dev/null +++ b/pkg/generated/listers/podgroup/v1alpha1/expansion_generated.go @@ -0,0 +1,27 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +// PodGroupListerExpansion allows custom methods to be added to +// PodGroupLister. +type PodGroupListerExpansion interface{} + +// PodGroupNamespaceListerExpansion allows custom methods to be added to +// PodGroupNamespaceLister. +type PodGroupNamespaceListerExpansion interface{} diff --git a/pkg/generated/listers/podgroup/v1alpha1/podgroup.go b/pkg/generated/listers/podgroup/v1alpha1/podgroup.go new file mode 100644 index 000000000..6b856e464 --- /dev/null +++ b/pkg/generated/listers/podgroup/v1alpha1/podgroup.go @@ -0,0 +1,99 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha1 "sigs.k8s.io/scheduler-plugins/pkg/apis/podgroup/v1alpha1" +) + +// PodGroupLister helps list PodGroups. +// All objects returned here must be treated as read-only. +type PodGroupLister interface { + // List lists all PodGroups in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.PodGroup, err error) + // PodGroups returns an object that can list and get PodGroups. + PodGroups(namespace string) PodGroupNamespaceLister + PodGroupListerExpansion +} + +// podGroupLister implements the PodGroupLister interface. +type podGroupLister struct { + indexer cache.Indexer +} + +// NewPodGroupLister returns a new PodGroupLister. +func NewPodGroupLister(indexer cache.Indexer) PodGroupLister { + return &podGroupLister{indexer: indexer} +} + +// List lists all PodGroups in the indexer. +func (s *podGroupLister) List(selector labels.Selector) (ret []*v1alpha1.PodGroup, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.PodGroup)) + }) + return ret, err +} + +// PodGroups returns an object that can list and get PodGroups. +func (s *podGroupLister) PodGroups(namespace string) PodGroupNamespaceLister { + return podGroupNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// PodGroupNamespaceLister helps list and get PodGroups. +// All objects returned here must be treated as read-only. +type PodGroupNamespaceLister interface { + // List lists all PodGroups in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.PodGroup, err error) + // Get retrieves the PodGroup from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.PodGroup, error) + PodGroupNamespaceListerExpansion +} + +// podGroupNamespaceLister implements the PodGroupNamespaceLister +// interface. +type podGroupNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all PodGroups in the indexer for a given namespace. +func (s podGroupNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.PodGroup, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.PodGroup)) + }) + return ret, err +} + +// Get retrieves the PodGroup from the indexer for a given namespace and name. +func (s podGroupNamespaceLister) Get(name string) (*v1alpha1.PodGroup, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("podgroup"), name) + } + return obj.(*v1alpha1.PodGroup), nil +} diff --git a/pkg/util/podgroup.go b/pkg/util/podgroup.go new file mode 100644 index 000000000..66ea20930 --- /dev/null +++ b/pkg/util/podgroup.go @@ -0,0 +1,80 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "encoding/json" + "fmt" + "time" + + jsonpatch "github.com/evanphx/json-patch" + v1 "k8s.io/api/core/v1" + + "sigs.k8s.io/scheduler-plugins/pkg/apis/podgroup/v1alpha1" +) + +// DefaultWaitTime is 60s if MaxScheduleTime is not specified. +const DefaultWaitTime = 60 * time.Second + +// CreateMergePatch return patch generated from original and new interfaces +func CreateMergePatch(original, new interface{}) ([]byte, error) { + pvByte, err := json.Marshal(original) + if err != nil { + return nil, err + } + cloneByte, err := json.Marshal(new) + if err != nil { + return nil, err + } + patch, err := jsonpatch.CreateMergePatch(pvByte, cloneByte) + if err != nil { + return nil, err + } + return patch, nil +} + +// VerifyPodLabelSatisfied verifies if pod ann satisfies coscheduling +func VerifyPodLabelSatisfied(pod *v1.Pod) (string, bool) { + if pod.Labels == nil { + return "", false + } + if pod.Labels[PodGroupLabel] == "" { + return "", false + } + return pod.Labels[PodGroupLabel], true +} + +// GetPodGroupFullName verify if pod ann satisfies coscheduling +func GetPodGroupFullName(pg *v1alpha1.PodGroup) string { + if pg == nil { + return "" + } + + return fmt.Sprintf("%v/%v", pg.Namespace, pg.Name) +} + +// GetWaitTimeDuration verify if pod ann satisfies coscheduling +func GetWaitTimeDuration(pg *v1alpha1.PodGroup, defaultMaxScheTime *time.Duration) time.Duration { + waitTime := DefaultWaitTime + if defaultMaxScheTime != nil || *defaultMaxScheTime != 0 { + waitTime = *defaultMaxScheTime + } + if pg != nil && pg.Spec.MaxScheduleTime != nil { + return pg.Spec.MaxScheduleTime.Duration + } + return waitTime +} diff --git a/pkg/util/podgroup_test.go b/pkg/util/podgroup_test.go new file mode 100644 index 000000000..afe6c331e --- /dev/null +++ b/pkg/util/podgroup_test.go @@ -0,0 +1,78 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "testing" + + "k8s.io/kubernetes/pkg/apis/core" +) + +type test struct { + old interface{} + new interface{} + expectedError bool +} + +func TestCreateMergePatch(t *testing.T) { + tests := []struct { + old interface{} + new interface{} + expected string + }{ + { + old: &core.Pod{ + Spec: core.PodSpec{ + Hostname: "test", + }, + }, + new: &core.Pod{ + Status: core.PodStatus{ + Reason: "test", + }, + }, + expected: `{"Spec":{"Hostname":""},"Status":{"Reason":"test"}}`, + }, + + { + old: &core.Pod{ + Spec: core.PodSpec{ + Hostname: "test", + }, + Status: core.PodStatus{ + Reason: "test1", + }, + }, + new: &core.Pod{ + Status: core.PodStatus{ + Reason: "test", + }, + }, + expected: `{"Spec":{"Hostname":""},"Status":{"Reason":"test"}}`, + }, + } + + for _, tcase := range tests { + patch, err := CreateMergePatch(tcase.old, tcase.new) + if err != nil { + t.Error(err) + } + if string(patch) != tcase.expected { + t.Errorf("expected %v get %v", tcase.expected, string(patch)) + } + } +} diff --git a/pkg/util/types.go b/pkg/util/types.go new file mode 100644 index 000000000..982111a91 --- /dev/null +++ b/pkg/util/types.go @@ -0,0 +1,33 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import "fmt" + +const ( + // PodGroupLabel is the default label of coscheduling + PodGroupLabel = "pod-group.scheduling.sigs.k8s.io" +) + +var ( + // ErrorNotMatched means pod does not match coscheduling + ErrorNotMatched = fmt.Errorf("not match coscheduling") + // ErrorWaiting means pod number does not match the min pods required + ErrorWaiting = fmt.Errorf("waiting") + // ErrorResourceNotEnough means cluster resource is not enough, mainly used in Pre-Filter + ErrorResourceNotEnough = fmt.Errorf("resource not enough") +)