|
| 1 | +# Scheduler Framework Plugins |
| 2 | + |
| 3 | +## Creating a new in-tree plugin |
| 4 | + |
| 5 | +Read [the docs](https://kubernetes.io/docs/concepts/scheduling-eviction/scheduling-framework/) |
| 6 | +to understand the different extension points within the scheduling framework. |
| 7 | + |
| 8 | +TODO([#5466](https://github.com/kubernetes/community/issues/5466)): finish this section |
| 9 | + |
| 10 | +## Adding plugin configuration parameters through `KubeSchedulerConfiguration` |
| 11 | + |
| 12 | +You can give users the ability to configure parameters in scheduler plugins using |
| 13 | +[`KubeSchedulerConfiguration`](https://kubernetes.io/docs/reference/scheduling/config/). |
| 14 | +This section covers how you can add arguments to existing in-tree plugins [(example PR)](https://github.com/kubernetes/kubernetes/pull/94814). |
| 15 | +Let's assume the plugin is called `FooPlugin` and we want to add an optional |
| 16 | +integer parameter named `barParam`. |
| 17 | + |
| 18 | +### Defining and registering the struct |
| 19 | + |
| 20 | +First, we need to define a struct type named `FooPluginArgs` in |
| 21 | +`pkg/scheduler/apis/config/types_pluginargs.go`, which is the representation of |
| 22 | +the configuration parameters that is internal to the scheduler. |
| 23 | + |
| 24 | +```go |
| 25 | +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object |
| 26 | + |
| 27 | +type FooPluginArgs struct { |
| 28 | + // metav1 is k8s.io/apimachinery/pkg/apis/meta/v1 (package is in staging/src) |
| 29 | + metav1.TypeMeta |
| 30 | + BarParam int32 |
| 31 | +} |
| 32 | +``` |
| 33 | + |
| 34 | +Note that we embed `k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta` to include |
| 35 | +API metadata for [versioning and persistence](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#api-conventions). |
| 36 | +We add the `+k8s:deepcopy-gen:interfaces` comment to [auto-generate a `DeepCopy` function](https://github.com/kubernetes/kubernetes/tree/master/staging/src/k8s.io/code-generator) |
| 37 | +for the struct. |
| 38 | + |
| 39 | +Similarly, define `FooPluginArgs` in `staging/src/k8s.io/kube-scheduler/config/{version}/types_pluginargs.go`, |
| 40 | +which is the versioned representation used in the `kube-scheduler` binary used |
| 41 | +for deserialization. This time, however, in order to allow implicit default |
| 42 | +values for arguments, the type of the struct's fields may be pointers; leaving |
| 43 | +a parameter unspecified will set the pointer field to its zero value (nil), |
| 44 | +which can be used to let the framework know that it must fill in the default |
| 45 | +value. `BarParam` is of type `int32` and let's say we want a non-zero default |
| 46 | +value for it: |
| 47 | + |
| 48 | +```go |
| 49 | +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object |
| 50 | + |
| 51 | +type FooPluginArgs struct { |
| 52 | + metav1.TypeMeta `json:",inline"` |
| 53 | + BarParam *int32 `json:"barParam,omitempty"` |
| 54 | +} |
| 55 | +``` |
| 56 | + |
| 57 | +For each `types_pluginargs.go` addition, remember to register the type in the |
| 58 | +corresponding `register.go`, which will allow the scheduler to recognize |
| 59 | +`KubeSchedulerConfiguration` values at parse-time. |
| 60 | + |
| 61 | +### Setting defaults |
| 62 | + |
| 63 | +When a `KubeSchedulerConfiguration` object is parsed (happens in |
| 64 | +`cmd/kube-scheduler/app/options/options.go`), the scheduler will convert from |
| 65 | +the versioned type to the internal type, filling in the unspecified fields with |
| 66 | +defaults. Speaking of defaults, define `SetDefaults_FooPluginArgs` in |
| 67 | +`pkg/scheduler/apis/config/v1beta1/defaults.go` as follows: |
| 68 | + |
| 69 | +```go |
| 70 | +// v1beta1 refers to k8s.io/kube-scheduler/config/v1beta1 (package is in staging/src) |
| 71 | +func SetDefaults_FooPluginArgs(obj *v1beta1.FooPluginArgs) { |
| 72 | + if obj.BarParam == nil { |
| 73 | + obj.BarParam = pointer.Int32Ptr(42) |
| 74 | + } |
| 75 | +} |
| 76 | +``` |
| 77 | + |
| 78 | +### Validating configuration at runtime |
| 79 | + |
| 80 | +Next, we need to define validators to make sure the user's configuration and |
| 81 | +your default values are valid. To do this, add something like this in |
| 82 | +`pkg/scheduler/apis/config/validation/validation_pluginargs.go`: |
| 83 | + |
| 84 | +```go |
| 85 | +// From here on, FooPluginArgs refers to the type defined in pkg/scheduler |
| 86 | +// definition, not the kube-scheduler definition. We're dealing with |
| 87 | +// post-default values. |
| 88 | +func ValidateFooPluginArgs(args config.FooPluginArgs) error { |
| 89 | + if args.BarParam < 0 && args.BarParam > 100 { |
| 90 | + return fmt.Errorf("must be in the range [0, 100]") |
| 91 | + } |
| 92 | + return nil |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +### Code generation |
| 97 | + |
| 98 | +We have defined everything necessary to run code generation now. Remember to |
| 99 | +commit all your changes (not sure why this is needed) and do a `make clean` |
| 100 | +first. Then: |
| 101 | + |
| 102 | +```sh |
| 103 | +$ cd $GOPATH/src/k8s.io/kubernetes |
| 104 | +$ git add -A && git commit |
| 105 | +$ make clean |
| 106 | +$ ./hack/update-codegen.sh |
| 107 | +$ make generated_files |
| 108 | +``` |
| 109 | + |
| 110 | +This should automatically generate code to deep copy objects, convert between |
| 111 | +different struct types, convert pointer types to raw types, and set defaults. |
| 112 | + |
| 113 | +### Testing |
| 114 | + |
| 115 | +After code generation, go back and write tests for all of the changes you made |
| 116 | +in the previous section: |
| 117 | + |
| 118 | +- `pkg/scheduler/apis/config/v1beta1/defaults_test.go` to unit test the |
| 119 | + defaults. |
| 120 | +- `pkg/scheduler/apis/config/validation/validation_pluginargs_test.go` to unit |
| 121 | + test the validator. |
| 122 | +- `pkg/scheduler/apis/config/scheme/scheme_test.go` to test the whole pipeline |
| 123 | + using a `KubeSchedulerConfiguration` definition. |
| 124 | + |
| 125 | +### Receiving the arguments in the plugin |
| 126 | + |
| 127 | +We can now finally receive `FooPluginArgs` in the plugin code. To do this, |
| 128 | +modify the plugin's `New` method signature like so: |
| 129 | + |
| 130 | +```go |
| 131 | +func New(fpArgs runtime.Object, fh framework.FrameworkHandle) (framework.Plugin, error) { |
| 132 | + // config.FooPluginArgs refers to the pkg/scheduler struct type definition. |
| 133 | + args, ok := fpArgs.(*config.FooPluginArgs) |
| 134 | + if !ok { |
| 135 | + return nil, fmt.Errorf("got args of type %T, want *FooPluginArgs", fpArgs) |
| 136 | + } |
| 137 | + if err := validation.ValidateFooPluginArgs(*args); err != nil { |
| 138 | + return nil, err |
| 139 | + } |
| 140 | + // Use args.BarParam as you like. |
| 141 | +} |
| 142 | +``` |
0 commit comments