Repository: kubernetes-sigs/kwok
Pull request #277 (2023-02-11 14:39:06)

Fix default stages name
<!--  Thanks for sending a pull request!  Here are some tips for you:

1. If this is your first time, please read our contributor guidelines: https://git.k8s.io/community/contributors/guide/first-contribution.md#your-first-contribution and developer guide https://git.k8s.io/community/contributors/devel/development.md#development-guide
2. Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request. For reference on required PR/issue labels, read here:
https://git.k8s.io/community/contributors/devel/sig-release/release.md#issuepr-kind-label
3. Ensure you have added or ran the appropriate tests for your PR: https://git.k8s.io/community/contributors/devel/sig-testing/testing.md
4. If you want *faster* PR reviews, read how: https://git.k8s.io/community/contributors/guide/pull-requests.md#best-practices-for-faster-reviews
5. If the PR is unfinished, see how to mark it: https://git.k8s.io/community/contributors/guide/pull-requests.md#marking-unfinished-pull-requests
-->

#### What type of PR is this?

<!--
Add one of the following kinds:
/kind bug
/kind cleanup
/kind documentation
/kind feature

Optionally add one or more of the following kinds if applicable:
/kind api-change
/kind deprecation
/kind failing-test
/kind flake
/kind regression
-->

/kind bug

#### What this PR does / why we need it:

#### Which issue(s) this PR fixes:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
xref #271

#### Special notes for your reviewer:

#### Does this PR introduce a user-facing change?
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".

For more information on release notes see: https://git.k8s.io/community/contributors/guide/release-notes.md
-->
```release-note
NONE
```

#### Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:

<!--
This section can be blank if this pull request does not require a release note.

When adding links which point to resources within git repositories, like
KEPs or supporting documentation, please reference a specific commit and avoid
linking directly to the master branch. This ensures that links reference a
specific point in time, rather than a document that may change over time.

See here for guidance on getting permanent links to files: https://help.github.com/en/articles/getting-permanent-links-to-files

Please use the following format for linking documentation:
- [KEP]: <link>
- [Usage]: <link>
- [Other doc]: <link>
-->
```docs
NONE
```


k8s-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/kwok/pull/277#" title="Author self-approved">wzshiming</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fkwok).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/kubernetes-sigs/kwok/blob/main/OWNERS)~~ [wzshiming]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *k8s-kwok* processing.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | a6cf4ea68b41d5c1f312a5fb7f8bf8f3d67c34db |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/k8s-kwok/deploys/63e7a88cbb002a0008036217 |

Patch:
diff --git a/pkg/apis/internalversion/kwokctl_configuration_types.go b/pkg/apis/internalversion/kwokctl_configuration_types.go
index d738b1c5..8a46a85c 100644
--- a/pkg/apis/internalversion/kwokctl_configuration_types.go
+++ b/pkg/apis/internalversion/kwokctl_configuration_types.go
@@ -36,17 +36,13 @@ type KwokctlConfiguration struct {
 
 // KwokctlConfigurationOptions holds information about the options.
 type KwokctlConfigurationOptions struct {
+
 	// KubeApiserverPort is the port to expose apiserver.
 	KubeApiserverPort uint32
 
 	// Runtime is the runtime to use.
 	Runtime string
 
-	// Runtimes is a list of alternate runtimes. When Runtime is empty,
-	// the availability of the runtimes in the list is checked one by one
-	// and set to Runtime
-	Runtimes []string
-
 	// PrometheusPort is the port to expose Prometheus metrics.
 	PrometheusPort uint32
 
@@ -168,7 +164,7 @@ type KwokctlConfigurationOptions struct {
 	// KubeSchedulerPort is kube-scheduler port in the binary runtime
 	KubeSchedulerPort uint32
 
-	// KwokControllerPort is kwok-controller port that is exposed to the host.
+	// KwokControllerPort is kube-controller port in the binary runtime
 	KwokControllerPort uint32
 
 	// CacheDir is the directory of the cache.
diff --git a/pkg/apis/internalversion/zz_generated.conversion.go b/pkg/apis/internalversion/zz_generated.conversion.go
index 2cc34cca..16eb80b2 100644
--- a/pkg/apis/internalversion/zz_generated.conversion.go
+++ b/pkg/apis/internalversion/zz_generated.conversion.go
@@ -499,7 +499,6 @@ func Convert_v1alpha1_KwokctlConfiguration_To_internalversion_KwokctlConfigurati
 func autoConvert_internalversion_KwokctlConfigurationOptions_To_v1alpha1_KwokctlConfigurationOptions(in *KwokctlConfigurationOptions, out *v1alpha1.KwokctlConfigurationOptions, s conversion.Scope) error {
 	out.KubeApiserverPort = in.KubeApiserverPort
 	out.Runtime = in.Runtime
-	out.Runtimes = *(*[]string)(unsafe.Pointer(&in.Runtimes))
 	out.PrometheusPort = in.PrometheusPort
 	out.KwokVersion = in.KwokVersion
 	out.KubeVersion = in.KubeVersion
@@ -563,7 +562,6 @@ func Convert_internalversion_KwokctlConfigurationOptions_To_v1alpha1_KwokctlConf
 func autoConvert_v1alpha1_KwokctlConfigurationOptions_To_internalversion_KwokctlConfigurationOptions(in *v1alpha1.KwokctlConfigurationOptions, out *KwokctlConfigurationOptions, s conversion.Scope) error {
 	out.KubeApiserverPort = in.KubeApiserverPort
 	out.Runtime = in.Runtime
-	out.Runtimes = *(*[]string)(unsafe.Pointer(&in.Runtimes))
 	out.PrometheusPort = in.PrometheusPort
 	out.KwokVersion = in.KwokVersion
 	out.KubeVersion = in.KubeVersion
diff --git a/pkg/apis/internalversion/zz_generated.deepcopy.go b/pkg/apis/internalversion/zz_generated.deepcopy.go
index 344db24e..cbd99b35 100644
--- a/pkg/apis/internalversion/zz_generated.deepcopy.go
+++ b/pkg/apis/internalversion/zz_generated.deepcopy.go
@@ -167,7 +167,7 @@ func (in *KwokctlConfiguration) DeepCopyInto(out *KwokctlConfiguration) {
 	*out = *in
 	out.TypeMeta = in.TypeMeta
 	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
-	in.Options.DeepCopyInto(&out.Options)
+	out.Options = in.Options
 	if in.Components != nil {
 		in, out := &in.Components, &out.Components
 		*out = make([]Component, len(*in))
@@ -199,11 +199,6 @@ func (in *KwokctlConfiguration) DeepCopyObject() runtime.Object {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *KwokctlConfigurationOptions) DeepCopyInto(out *KwokctlConfigurationOptions) {
 	*out = *in
-	if in.Runtimes != nil {
-		in, out := &in.Runtimes, &out.Runtimes
-		*out = make([]string, len(*in))
-		copy(*out, *in)
-	}
 	return
 }
 
diff --git a/pkg/apis/v1alpha1/kwokctl_configuration_types.go b/pkg/apis/v1alpha1/kwokctl_configuration_types.go
index b209f494..c58a0235 100644
--- a/pkg/apis/v1alpha1/kwokctl_configuration_types.go
+++ b/pkg/apis/v1alpha1/kwokctl_configuration_types.go
@@ -51,13 +51,9 @@ type KwokctlConfigurationOptions struct {
 
 	// Runtime is the runtime to use.
 	// is the default value for flag --runtime and env KWOK_RUNTIME
+	// +default="docker"
 	Runtime string `json:"runtime,omitempty"`
 
-	// Runtimes is a list of alternate runtimes. When Runtime is empty,
-	// the availability of the runtimes in the list is checked one by one
-	// and set to Runtime
-	Runtimes []string `json:"runtimes,omitempty"`
-
 	// PrometheusPort is the port to expose Prometheus metrics.
 	// is the default value for flag --prometheus-port and env KWOK_PROMETHEUS_PORT
 	PrometheusPort uint32 `json:"prometheusPort,omitempty"`
@@ -263,8 +259,7 @@ type KwokctlConfigurationOptions struct {
 	// KubeSchedulerPort is kube-scheduler port in the binary runtime
 	KubeSchedulerPort uint32 `json:"kubeSchedulerPort,omitempty"`
 
-	// KwokControllerPort is kwok-controller port that is exposed to the host.
-	// is the default value for flag --controller-port and env KWOK_CONTROLLER_PORT
+	// KwokControllerPort is kube-controller port in the binary runtime
 	KwokControllerPort uint32 `json:"kwokControllerPort,omitempty"`
 
 	// CacheDir is the directory of the cache.
diff --git a/pkg/apis/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/v1alpha1/zz_generated.deepcopy.go
index febbef27..f5128764 100644
--- a/pkg/apis/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/v1alpha1/zz_generated.deepcopy.go
@@ -226,11 +226,6 @@ func (in *KwokctlConfiguration) DeepCopyObject() runtime.Object {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *KwokctlConfigurationOptions) DeepCopyInto(out *KwokctlConfigurationOptions) {
 	*out = *in
-	if in.Runtimes != nil {
-		in, out := &in.Runtimes, &out.Runtimes
-		*out = make([]string, len(*in))
-		copy(*out, *in)
-	}
 	if in.SecurePort != nil {
 		in, out := &in.SecurePort, &out.SecurePort
 		*out = new(bool)
diff --git a/pkg/apis/v1alpha1/zz_generated.defaults.go b/pkg/apis/v1alpha1/zz_generated.defaults.go
index 9719a17c..fa4f9589 100644
--- a/pkg/apis/v1alpha1/zz_generated.defaults.go
+++ b/pkg/apis/v1alpha1/zz_generated.defaults.go
@@ -62,6 +62,9 @@ func SetObjectDefaults_KwokConfiguration(in *KwokConfiguration) {
 }
 
 func SetObjectDefaults_KwokctlConfiguration(in *KwokctlConfiguration) {
+	if in.Options.Runtime == "" {
+		in.Options.Runtime = "docker"
+	}
 	if in.Options.SecurePort == nil {
 		var ptrVar1 bool = false
 		in.Options.SecurePort = &ptrVar1
diff --git a/pkg/config/vars.go b/pkg/config/vars.go
index 52de0a32..7d297a36 100644
--- a/pkg/config/vars.go
+++ b/pkg/config/vars.go
@@ -147,15 +147,6 @@ func setKwokctlConfigurationDefaults(config *v1alpha1.KwokctlConfiguration) *v1a
 	conf.QuietPull = format.Ptr(envs.GetEnvWithPrefix("QUIET_PULL", *conf.QuietPull))
 
 	conf.Runtime = envs.GetEnvWithPrefix("RUNTIME", conf.Runtime)
-	if len(conf.Runtimes) == 0 {
-		conf.Runtimes = []string{
-			consts.RuntimeTypeDocker,
-			consts.RuntimeTypeNerdctl,
-		}
-		if runtime.GOOS == "linux" {
-			conf.Runtimes = append(conf.Runtimes, consts.RuntimeTypeBinary)
-		}
-	}
 
 	conf.Mode = envs.GetEnvWithPrefix("MODE", conf.Mode)
 
@@ -286,7 +277,6 @@ func setKwokctlKwokConfig(conf *v1alpha1.KwokctlConfigurationOptions) {
 		conf.KwokControllerImage = joinImageURI(conf.KwokImagePrefix, "kwok", conf.KwokVersion)
 	}
 	conf.KwokControllerImage = envs.GetEnvWithPrefix("CONTROLLER_IMAGE", conf.KwokControllerImage)
-	conf.KwokControllerPort = envs.GetEnvWithPrefix("CONTROLLER_PORT", conf.KwokControllerPort)
 }
 
 func setKwokctlEtcdConfig(conf *v1alpha1.KwokctlConfigurationOptions) {
diff --git a/pkg/kwok/controllers/finalizers.go b/pkg/kwok/controllers/finalizers.go
index 1841b932..79eb0e92 100644
--- a/pkg/kwok/controllers/finalizers.go
+++ b/pkg/kwok/controllers/finalizers.go
@@ -100,10 +100,7 @@ func finalizersModify(metaFinalizers []string, finalizers *internalversion.Stage
 			ops = append(ops, finalizersAdd(metaFinalizers, finalizers.Add)...)
 		}
 	} else {
-		if len(metaFinalizers) != 0 {
-			ops = append(ops, finalizersEmpty)
-		}
-
+		ops = append(ops, finalizersEmpty)
 		if len(finalizers.Add) != 0 {
 			ops = append(ops, finalizersAdd(nil, finalizers.Add)...)
 		}
diff --git a/pkg/kwok/controllers/lifecycle.go b/pkg/kwok/controllers/lifecycle.go
index b3da3d1c..1a42a5a5 100644
--- a/pkg/kwok/controllers/lifecycle.go
+++ b/pkg/kwok/controllers/lifecycle.go
@@ -29,23 +29,28 @@ import (
 	"k8s.io/apimachinery/pkg/util/yaml"
 
 	"sigs.k8s.io/kwok/pkg/apis/internalversion"
+	"sigs.k8s.io/kwok/pkg/apis/v1alpha1"
 	"sigs.k8s.io/kwok/pkg/utils/expression"
 	"sigs.k8s.io/kwok/pkg/utils/format"
 )
 
 // NewStagesFromYaml returns stages from yaml data.
 func NewStagesFromYaml(data []byte) ([]*internalversion.Stage, error) {
-	var podStageStatus []*internalversion.Stage
+	var stages []*internalversion.Stage
 	decoder := yaml.NewYAMLToJSONDecoder(bytes.NewBuffer(data))
 	for {
-		var stage internalversion.Stage
+		var stage v1alpha1.Stage
 		err := decoder.Decode(&stage)
 		if errors.Is(err, io.EOF) {
 			break
 		}
-		podStageStatus = append(podStageStatus, &stage)
+		internalStage, err := internalversion.ConvertToInternalVersionStage(&stage)
+		if err != nil {
+			return nil, err
+		}
+		stages = append(stages, internalStage)
 	}
-	return podStageStatus, nil
+	return stages, nil
 }
 
 // NewLifecycle returns a new Lifecycle.
diff --git a/pkg/kwokctl/cmd/create/cluster/cluster.go b/pkg/kwokctl/cmd/create/cluster/cluster.go
index d95cbb40..f71d66c0 100644
--- a/pkg/kwokctl/cmd/create/cluster/cluster.go
+++ b/pkg/kwokctl/cmd/create/cluster/cluster.go
@@ -82,7 +82,6 @@ func NewCommand(ctx context.Context) *cobra.Command {
 	cmd.Flags().StringVar(&flags.Options.PrometheusImage, "prometheus-image", flags.Options.PrometheusImage, `Image of Prometheus, only for docker/nerdctl/kind runtime
 '${KWOK_PROMETHEUS_IMAGE_PREFIX}/prometheus:${KWOK_PROMETHEUS_VERSION}'
 `)
-	cmd.Flags().Uint32Var(&flags.Options.KwokControllerPort, "controller-port", flags.Options.KwokControllerPort, `Port of kwok-controller given to the host`)
 	cmd.Flags().StringVar(&flags.Options.KindNodeImage, "kind-node-image", flags.Options.KindNodeImage, `Image of kind node, only for kind runtime
 '${KWOK_KIND_NODE_IMAGE_PREFIX}/node:${KWOK_KUBE_VERSION}'
 `)
@@ -128,41 +127,14 @@ func runE(ctx context.Context, flags *flagpole) error {
 		defer cancel()
 	}
 
-	var rt runtime.Runtime
-	var err error
-	if flags.Options.Runtime == "" {
-		errs := make([]error, 0, len(flags.Options.Runtimes))
-		for _, r := range flags.Options.Runtimes {
-			buildRuntime, ok := runtime.DefaultRegistry.Get(r)
-			if !ok {
-				err = fmt.Errorf("runtime %q not found", flags.Options.Runtime)
-				errs = append(errs, err)
-				continue
-			}
-			if rt, err = buildRuntime(name, workdir); err != nil {
-				errs = append(errs, err)
-				continue
-			}
-			if err = rt.Available(ctx); err != nil {
-				errs = append(errs, err)
-				continue
-			}
-			flags.Options.Runtime = r
-			logger.Debug("Detected runtime available", "runtime", flags.Options.Runtime)
-			break
-		}
-		if flags.Options.Runtime == "" {
-			return fmt.Errorf("runtime %v not available: %v", flags.Options.Runtimes, errs)
-		}
-	} else {
-		buildRuntime, ok := runtime.DefaultRegistry.Get(flags.Options.Runtime)
-		if !ok {
-			return fmt.Errorf("runtime %q not found", flags.Options.Runtime)
-		}
-		rt, err = buildRuntime(name, workdir)
-		if err != nil {
-			return fmt.Errorf("runtime %v not available: %w", flags.Options.Runtime, err)
-		}
+	buildRuntime, ok := runtime.DefaultRegistry.Get(flags.Options.Runtime)
+	if !ok {
+		return fmt.Errorf("runtime %q not found", flags.Options.Runtime)
+	}
+
+	rt, err := buildRuntime(name, workdir)
+	if err != nil {
+		return err
 	}
 
 	_, err = rt.Config(ctx)
diff --git a/pkg/kwokctl/components/kwok_controller.go b/pkg/kwokctl/components/kwok_controller.go
index 12045de2..f0e2a306 100644
--- a/pkg/kwokctl/components/kwok_controller.go
+++ b/pkg/kwokctl/components/kwok_controller.go
@@ -44,7 +44,6 @@ func BuildKwokControllerComponent(conf BuildKwokControllerComponentConfig) (comp
 
 	inContainer := conf.Image != ""
 	var volumes []internalversion.Volume
-	var ports []internalversion.Port
 
 	if inContainer {
 		volumes = append(volumes,
@@ -69,14 +68,6 @@ func BuildKwokControllerComponent(conf BuildKwokControllerComponentConfig) (comp
 				ReadOnly:  true,
 			},
 		)
-		if conf.Port != 0 {
-			ports = append(ports,
-				internalversion.Port{
-					HostPort: conf.Port,
-					Port:     10247,
-				},
-			)
-		}
 		kwokControllerArgs = append(kwokControllerArgs,
 			"--kubeconfig=/root/.kube/config",
 			"--config=/root/.kwok/kwok.yaml",
@@ -102,7 +93,6 @@ func BuildKwokControllerComponent(conf BuildKwokControllerComponentConfig) (comp
 		Links: []string{
 			"kube-apiserver",
 		},
-		Ports:   ports,
 		Command: []string{"kwok"},
 		Volumes: volumes,
 		Args:    kwokControllerArgs,
diff --git a/pkg/kwokctl/runtime/binary/cluster.go b/pkg/kwokctl/runtime/binary/cluster.go
index c1102c45..1d251cae 100644
--- a/pkg/kwokctl/runtime/binary/cluster.go
+++ b/pkg/kwokctl/runtime/binary/cluster.go
@@ -55,11 +55,6 @@ func NewCluster(name, workdir string) (runtime.Runtime, error) {
 	}, nil
 }
 
-// Available  checks whether the runtime is available.
-func (c *Cluster) Available(ctx context.Context) error {
-	return nil
-}
-
 func (c *Cluster) download(ctx context.Context) error {
 	config, err := c.Config(ctx)
 	if err != nil {
diff --git a/pkg/kwokctl/runtime/compose/cluster.go b/pkg/kwokctl/runtime/compose/cluster.go
index 2e8804a6..f5d7f7da 100644
--- a/pkg/kwokctl/runtime/compose/cluster.go
+++ b/pkg/kwokctl/runtime/compose/cluster.go
@@ -46,31 +46,15 @@ import (
 // Cluster is a implementation of Runtime for docker-compose.
 type Cluster struct {
 	*runtime.Cluster
-
-	runtime string
 }
 
-// NewNerdctlCluster creates a new Runtime for nerdctl compose.
-func NewNerdctlCluster(name, workdir string) (runtime.Runtime, error) {
+// NewCluster creates a new Runtime for docker-compose.
+func NewCluster(name, workdir string) (runtime.Runtime, error) {
 	return &Cluster{
 		Cluster: runtime.NewCluster(name, workdir),
-		runtime: consts.RuntimeTypeNerdctl,
 	}, nil
 }
 
-// NewDockerCluster creates a new Runtime for docker-compose.
-func NewDockerCluster(name, workdir string) (runtime.Runtime, error) {
-	return &Cluster{
-		Cluster: runtime.NewCluster(name, workdir),
-		runtime: consts.RuntimeTypeDocker,
-	}, nil
-}
-
-// Available  checks whether the runtime is available.
-func (c *Cluster) Available(ctx context.Context) error {
-	return exec.Exec(ctx, "", exec.IOStreams{}, c.runtime, "version")
-}
-
 func (c *Cluster) setup(ctx context.Context) error {
 	config, err := c.Config(ctx)
 	if err != nil {
@@ -188,13 +172,13 @@ func (c *Cluster) Install(ctx context.Context) error {
 	if conf.PrometheusPort != 0 {
 		images = append(images, conf.PrometheusImage)
 	}
-	err = image.PullImages(ctx, c.runtime, images, conf.QuietPull)
+	err = image.PullImages(ctx, conf.Runtime, images, conf.QuietPull)
 	if err != nil {
 		return err
 	}
 
 	// Configure the etcd
-	etcdVersion, err := version.ParseFromImage(ctx, c.runtime, conf.EtcdImage, "etcd")
+	etcdVersion, err := version.ParseFromImage(ctx, conf.Runtime, conf.EtcdImage, "etcd")
 	if err != nil {
 		return err
 	}
@@ -211,7 +195,7 @@ func (c *Cluster) Install(ctx context.Context) error {
 	config.Components = append(config.Components, etcdComponent)
 
 	// Configure the kube-apiserver
-	kubeApiserverVersion, err := version.ParseFromImage(ctx, c.runtime, conf.KubeApiserverImage, "kube-apiserver")
+	kubeApiserverVersion, err := version.ParseFromImage(ctx, conf.Runtime, conf.KubeApiserverImage, "kube-apiserver")
 	if err != nil {
 		return err
 	}
@@ -239,7 +223,7 @@ func (c *Cluster) Install(ctx context.Context) error {
 
 	// Configure the kube-controller-manager
 	if !conf.DisableKubeControllerManager {
-		kubeControllerManagerVersion, err := version.ParseFromImage(ctx, c.runtime, conf.KubeControllerManagerImage, "kube-controller-manager")
+		kubeControllerManagerVersion, err := version.ParseFromImage(ctx, conf.Runtime, conf.KubeControllerManagerImage, "kube-controller-manager")
 		if err != nil {
 			return err
 		}
@@ -273,7 +257,7 @@ func (c *Cluster) Install(ctx context.Context) error {
 			}
 		}
 
-		kubeSchedulerVersion, err := version.ParseFromImage(ctx, c.runtime, conf.KubeSchedulerImage, "kube-scheduler")
+		kubeSchedulerVersion, err := version.ParseFromImage(ctx, conf.Runtime, conf.KubeSchedulerImage, "kube-scheduler")
 		if err != nil {
 			return err
 		}
@@ -297,7 +281,7 @@ func (c *Cluster) Install(ctx context.Context) error {
 	}
 
 	// Configure the kwok-controller
-	kwokControllerVersion, err := version.ParseFromImage(ctx, c.runtime, conf.KwokControllerImage, "kwok")
+	kwokControllerVersion, err := version.ParseFromImage(ctx, conf.Runtime, conf.KwokControllerImage, "kwok")
 	if err != nil {
 		return err
 	}
@@ -337,7 +321,7 @@ func (c *Cluster) Install(ctx context.Context) error {
 			return fmt.Errorf("failed to write prometheus yaml: %w", err)
 		}
 
-		prometheusVersion, err := version.ParseFromImage(ctx, c.runtime, conf.PrometheusImage, "")
+		prometheusVersion, err := version.ParseFromImage(ctx, conf.Runtime, conf.PrometheusImage, "")
 		if err != nil {
 			return err
 		}
@@ -558,10 +542,15 @@ func (c *Cluster) Down(ctx context.Context) error {
 
 // Start starts the cluster
 func (c *Cluster) Start(ctx context.Context) error {
+	conf, err := c.Config(ctx)
+	if err != nil {
+		return err
+	}
+
 	// TODO: nerdctl does not support 'compose start' in v1.1.0 or earlier
 	// Support in https://github.com/containerd/nerdctl/pull/1656 merge into the main branch, but there is no release
 	subcommand := []string{"start"}
-	if c.runtime == consts.RuntimeTypeNerdctl {
+	if conf.Options.Runtime == consts.RuntimeTypeNerdctl {
 		subcommand = []string{"up", "-d"}
 	}
 
@@ -578,7 +567,7 @@ func (c *Cluster) Start(ctx context.Context) error {
 		return fmt.Errorf("failed to start cluster: %w", err)
 	}
 
-	if c.runtime == consts.RuntimeTypeNerdctl {
+	if conf.Options.Runtime == consts.RuntimeTypeNerdctl {
 		backupFilename := c.GetWorkdirPath("restart.db")
 		fi, err := os.Stat(backupFilename)
 		if err == nil {
@@ -601,9 +590,14 @@ func (c *Cluster) Start(ctx context.Context) error {
 
 // Stop stops the cluster
 func (c *Cluster) Stop(ctx context.Context) error {
+	conf, err := c.Config(ctx)
+	if err != nil {
+		return err
+	}
+
 	// TODO: nerdctl does not support 'compose stop' in v1.0.0 or earlier
 	subcommand := "stop"
-	if c.runtime == consts.RuntimeTypeNerdctl {
+	if conf.Options.Runtime == consts.RuntimeTypeNerdctl {
 		subcommand = "down"
 		err := c.SnapshotSave(ctx, c.GetWorkdirPath("restart.db"))
 		if err != nil {
@@ -628,24 +622,50 @@ func (c *Cluster) Stop(ctx context.Context) error {
 
 // StartComponent starts a component in the cluster
 func (c *Cluster) StartComponent(ctx context.Context, name string) error {
-	return exec.Exec(ctx, c.Workdir(), exec.IOStreams{}, c.runtime, "start", c.Name()+"-"+name)
+	config, err := c.Config(ctx)
+	if err != nil {
+		return err
+	}
+	conf := &config.Options
+
+	err = exec.Exec(ctx, c.Workdir(), exec.IOStreams{}, conf.Runtime, "start", c.Name()+"-"+name)
+	if err != nil {
+		return err
+	}
+	return nil
 }
 
 // StopComponent stops a component in the cluster
 func (c *Cluster) StopComponent(ctx context.Context, name string) error {
-	return exec.Exec(ctx, c.Workdir(), exec.IOStreams{}, c.runtime, "stop", c.Name()+"-"+name)
+	config, err := c.Config(ctx)
+	if err != nil {
+		return err
+	}
+	conf := &config.Options
+
+	err = exec.Exec(ctx, c.Workdir(), exec.IOStreams{}, conf.Runtime, "stop", c.Name()+"-"+name)
+	if err != nil {
+		return err
+	}
+	return nil
 }
 
 func (c *Cluster) logs(ctx context.Context, name string, out io.Writer, follow bool) error {
+	config, err := c.Config(ctx)
+	if err != nil {
+		return err
+	}
+	conf := &config.Options
+
 	args := []string{"logs"}
 	if follow {
 		args = append(args, "-f")
 	}
 	args = append(args, c.Name()+"-"+name)
-	err := exec.Exec(ctx, c.Workdir(), exec.IOStreams{
+	err = exec.Exec(ctx, c.Workdir(), exec.IOStreams{
 		ErrOut: out,
 		Out:    out,
-	}, c.runtime, args...)
+	}, conf.Runtime, args...)
 	if err != nil {
 		return err
 	}
@@ -701,8 +721,8 @@ func (c *Cluster) buildComposeCommands(ctx context.Context, args ...string) ([]s
 	}
 	conf := &config.Options
 
-	if c.runtime == consts.RuntimeTypeDocker {
-		err := exec.Exec(ctx, "", exec.IOStreams{}, c.runtime, "compose", "version")
+	if conf.Runtime == consts.RuntimeTypeDocker {
+		err := exec.Exec(ctx, "", exec.IOStreams{}, conf.Runtime, "compose", "version")
 		if err != nil {
 			// docker compose subcommand does not exist, try to download it
 			dockerComposePath := c.GetBinPath("docker-compose" + conf.BinSuffix)
@@ -713,12 +733,18 @@ func (c *Cluster) buildComposeCommands(ctx context.Context, args ...string) ([]s
 			return append([]string{dockerComposePath}, args...), nil
 		}
 	}
-	return append([]string{c.runtime, "compose"}, args...), nil
+	return append([]string{conf.Runtime, "compose"}, args...), nil
 }
 
 // EtcdctlInCluster implements the ectdctl subcommand
 func (c *Cluster) EtcdctlInCluster(ctx context.Context, stm exec.IOStreams, args ...string) error {
+	config, err := c.Config(ctx)
+	if err != nil {
+		return err
+	}
+	conf := &config.Options
+
 	etcdContainerName := c.Name() + "-etcd"
-	return exec.Exec(ctx, "", stm, c.runtime,
-		append([]string{"exec", "-i", etcdContainerName, "etcdctl"}, args...)...)
+
+	return exec.Exec(ctx, "", stm, conf.Runtime, append([]string{"exec", "-i", etcdContainerName, "etcdctl"}, args...)...)
 }
diff --git a/pkg/kwokctl/runtime/compose/init.go b/pkg/kwokctl/runtime/compose/init.go
index 434485cc..e8142b3e 100644
--- a/pkg/kwokctl/runtime/compose/init.go
+++ b/pkg/kwokctl/runtime/compose/init.go
@@ -22,6 +22,6 @@ import (
 )
 
 func init() {
-	runtime.DefaultRegistry.Register(consts.RuntimeTypeDocker, NewDockerCluster)
-	runtime.DefaultRegistry.Register(consts.RuntimeTypeNerdctl, NewNerdctlCluster)
+	runtime.DefaultRegistry.Register(consts.RuntimeTypeDocker, NewCluster)
+	runtime.DefaultRegistry.Register(consts.RuntimeTypeNerdctl, NewCluster)
 }
diff --git a/pkg/kwokctl/runtime/config.go b/pkg/kwokctl/runtime/config.go
index 585deaa9..9c03e1e7 100644
--- a/pkg/kwokctl/runtime/config.go
+++ b/pkg/kwokctl/runtime/config.go
@@ -27,9 +27,6 @@ import (
 
 // Runtime is the interface for a runtime.
 type Runtime interface {
-	// Available checks whether the runtime is available.
-	Available(ctx context.Context) error
-
 	// SetConfig sets the config of cluster
 	SetConfig(ctx context.Context, conf *internalversion.KwokctlConfiguration) error
 
diff --git a/pkg/kwokctl/runtime/kind/cluster.go b/pkg/kwokctl/runtime/kind/cluster.go
index 75b97fdd..2ed7e83e 100644
--- a/pkg/kwokctl/runtime/kind/cluster.go
+++ b/pkg/kwokctl/runtime/kind/cluster.go
@@ -52,16 +52,6 @@ func NewCluster(name, workdir string) (runtime.Runtime, error) {
 	}, nil
 }
 
-// Available  checks whether the runtime is available.
-func (c *Cluster) Available(ctx context.Context) error {
-	// kind depends on docker or podman
-	// kwokctl will download kind binary if it is not available
-	// TODO: nerdctl kind provider support is discussing in
-	// https://github.com/kubernetes-sigs/kind/issues/2317 and
-	// https://github.com/containerd/nerdctl/issues/349
-	return nil
-}
-
 // Install installs the cluster
 func (c *Cluster) Install(ctx context.Context) error {
 	config, err := c.Config(ctx)
@@ -108,15 +98,14 @@ func (c *Cluster) Install(ctx context.Context) error {
 
 	configPath := c.GetWorkdirPath(runtime.ConfigName)
 	kindYaml, err := BuildKind(BuildKindConfig{
-		KubeApiserverPort:  conf.KubeApiserverPort,
-		PrometheusPort:     conf.PrometheusPort,
-		KwokControllerPort: conf.KwokControllerPort,
-		FeatureGates:       featureGates,
-		RuntimeConfig:      runtimeConfig,
-		AuditPolicy:        auditPolicyPath,
-		AuditLog:           auditLogPath,
-		SchedulerConfig:    schedulerConfigPath,
-		ConfigPath:         configPath,
+		KubeApiserverPort: conf.KubeApiserverPort,
+		PrometheusPort:    conf.PrometheusPort,
+		FeatureGates:      featureGates,
+		RuntimeConfig:     runtimeConfig,
+		AuditPolicy:       auditPolicyPath,
+		AuditLog:          auditLogPath,
+		SchedulerConfig:   schedulerConfigPath,
+		ConfigPath:        configPath,
 	})
 	if err != nil {
 		return err
diff --git a/pkg/kwokctl/runtime/kind/kind.go b/pkg/kwokctl/runtime/kind/kind.go
index e226e6b4..c22e0b8d 100644
--- a/pkg/kwokctl/runtime/kind/kind.go
+++ b/pkg/kwokctl/runtime/kind/kind.go
@@ -41,9 +41,8 @@ func BuildKind(conf BuildKindConfig) (string, error) {
 
 // BuildKindConfig is the configuration for building the kind config
 type BuildKindConfig struct {
-	KubeApiserverPort  uint32
-	PrometheusPort     uint32
-	KwokControllerPort uint32
+	KubeApiserverPort uint32
+	PrometheusPort    uint32
 
 	RuntimeConfig []string
 	FeatureGates  []string
diff --git a/pkg/kwokctl/runtime/kind/kind.yaml.tpl b/pkg/kwokctl/runtime/kind/kind.yaml.tpl
index 854b5e29..d91d87c1 100644
--- a/pkg/kwokctl/runtime/kind/kind.yaml.tpl
+++ b/pkg/kwokctl/runtime/kind/kind.yaml.tpl
@@ -9,20 +9,12 @@ networking:
 nodes:
 - role: control-plane
 
-{{ if or .PrometheusPort .KwokControllerPort }}
+{{ if .PrometheusPort }}
   extraPortMappings:
-  {{ if .PrometheusPort }}
   - containerPort: 9090
     hostPort: {{ .PrometheusPort }}
     listenAddress: "0.0.0.0"
     protocol: TCP
-  {{ end }}
-  {{ if .KwokControllerPort }}
-  - containerPort: 10247
-    hostPort: {{ .KwokControllerPort }}
-    listenAddress: "0.0.0.0"
-    protocol: TCP
-  {{ end }}
 {{ end }}
 
   kubeadmConfigPatches:


Test patch:
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index ce4a2fb4..71d0cee3 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -222,9 +222,3 @@ jobs:
         shell: bash
         run: |
           ./hack/e2e-test.sh kwokctl/kwokctl_${{ matrix.kwokctl-runtime }}_benchmark
-
-      - name: Test Auto Detect
-        if: ${{ matrix.kwokctl-runtime == 'binary' }}
-        shell: bash
-        run: |
-          ./hack/e2e-test.sh kwokctl/kwokctl_auto_detect
diff --git a/pkg/kwok/controllers/finalizers_test.go b/pkg/kwok/controllers/finalizers_test.go
index ad03c023..2c2d3c7d 100644
--- a/pkg/kwok/controllers/finalizers_test.go
+++ b/pkg/kwok/controllers/finalizers_test.go
@@ -121,76 +121,3 @@ func Test_finalizersRemove(t *testing.T) {
 		})
 	}
 }
-
-func Test_finalizersModify(t *testing.T) {
-	type args struct {
-		metaFinalizers []string
-		finalizers     *internalversion.StageFinalizers
-	}
-	tests := []struct {
-		name string
-		args args
-		want []jsonpathOperation
-	}{
-		{
-			args: args{
-				metaFinalizers: nil,
-				finalizers: &internalversion.StageFinalizers{
-					Empty: true,
-				},
-			},
-			want: nil,
-		},
-		{
-			args: args{
-				metaFinalizers: []string{"a"},
-				finalizers: &internalversion.StageFinalizers{
-					Empty: true,
-				},
-			},
-			want: []jsonpathOperation{
-				{Op: "remove", Path: "/metadata/finalizers", Value: nil},
-			},
-		},
-		{
-			args: args{
-				metaFinalizers: []string{"a"},
-				finalizers: &internalversion.StageFinalizers{
-					Empty: true,
-					Add: []internalversion.FinalizerItem{
-						{
-							Value: "b",
-						},
-					},
-				},
-			},
-			want: []jsonpathOperation{
-				{Op: "remove", Path: "/metadata/finalizers", Value: nil},
-				{Op: "add", Path: "/metadata/finalizers", Value: []string{"b"}},
-			},
-		},
-		{
-			args: args{
-				metaFinalizers: nil,
-				finalizers: &internalversion.StageFinalizers{
-					Empty: true,
-					Add: []internalversion.FinalizerItem{
-						{
-							Value: "b",
-						},
-					},
-				},
-			},
-			want: []jsonpathOperation{
-				{Op: "add", Path: "/metadata/finalizers", Value: []string{"b"}},
-			},
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if got := finalizersModify(tt.args.metaFinalizers, tt.args.finalizers); !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("finalizersModify() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
diff --git a/pkg/kwok/controllers/lifecycle_test.go b/pkg/kwok/controllers/lifecycle_test.go
new file mode 100644
index 00000000..e2a07c48
--- /dev/null
+++ b/pkg/kwok/controllers/lifecycle_test.go
@@ -0,0 +1,78 @@
+/*
+Copyright 2023 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 controllers
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	"sigs.k8s.io/kwok/pkg/apis/internalversion"
+)
+
+func TestNewStagesFromYaml(t *testing.T) {
+	type args struct {
+		data []byte
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    []*internalversion.Stage
+		wantErr bool
+	}{
+		{
+			name: "empty",
+			args: args{
+				data: []byte(`kind: Stage
+apiVersion: kwok.x-k8s.io/v1alpha1
+metadata:
+  name: node-test
+spec:
+  resourceRef:
+    apiGroup: v1
+    kind: Node`),
+			},
+			want: []*internalversion.Stage{
+				{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "node-test",
+					},
+					Spec: internalversion.StageSpec{
+						ResourceRef: internalversion.StageResourceRef{
+							APIGroup: "v1",
+							Kind:     "Node",
+						},
+					},
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := NewStagesFromYaml(tt.args.data)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("NewStagesFromYaml() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+
+			if diff := cmp.Diff(got, tt.want); diff != "" {
+				t.Errorf("NewStagesFromYaml() diff (-got +want): %s", diff)
+			}
+		})
+	}
+}
diff --git a/test/kwokctl/kwokctl-config-runtimes.yaml b/test/kwokctl/kwokctl-config-runtimes.yaml
deleted file mode 100644
index df1383cb..00000000
--- a/test/kwokctl/kwokctl-config-runtimes.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-kind: KwokctlConfiguration
-apiVersion: kwok.x-k8s.io/v1alpha1
-options:
-  runtimes:
-    - none
-    - binary
diff --git a/test/kwokctl/kwokctl_auto_detect.test.sh b/test/kwokctl/kwokctl_auto_detect.test.sh
deleted file mode 100755
index 450cfefc..00000000
--- a/test/kwokctl/kwokctl_auto_detect.test.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2023 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.
-
-DIR=$(realpath $(dirname "${BASH_SOURCE[0]}"))
-
-source "${DIR}/helper.sh"
-
-function main() {
-  local all_releases=("${@}")
-  for release in "${all_releases[@]}"; do
-    KWOK_KUBE_VERSION="${release}" kwokctl -v=-4 create cluster --timeout 10m --wait 10m --quiet-pull --config "${DIR}"/kwokctl-config-runtimes.yaml || exit 1
-    kwokctl delete cluster || exit 1
-  done
-}
-
-requirements_for_binary
-
-main $(supported_releases)
diff --git a/test/kwokctl/kwokctl_workable_test.sh b/test/kwokctl/kwokctl_workable_test.sh
index 7f2bd619..40a204ee 100755
--- a/test/kwokctl/kwokctl_workable_test.sh
+++ b/test/kwokctl/kwokctl_workable_test.sh
@@ -66,7 +66,7 @@ function test_create_cluster() {
   local targets
   local i
 
-  KWOK_KUBE_VERSION="${release}" kwokctl -v=-4 create cluster --name "${name}" --timeout 10m --wait 10m --quiet-pull --prometheus-port 9090 --controller-port 10247
+  KWOK_KUBE_VERSION="${release}" kwokctl -v=-4 create cluster --name "${name}" --timeout 10m --wait 10m --quiet-pull --prometheus-port 9090
   if [[ $? -ne 0 ]]; then
     echo "Error: Cluster ${name} creation failed"
     show_info "${name}"
@@ -121,17 +121,6 @@ function test_prometheus() {
   fi
 }
 
-function test_kwok_controller_port() {
-    local result
-    result="$(curl http://127.0.0.1:10247/healthz)"
-    if [[ ! $result == "ok" ]]; then
-        echo "Error: controller healthz is not ok"
-        echo curl -s http://127.0.0.1:10247/healthz
-        echo "${result}"
-        return 1
-    fi
-}
-
 function main() {
   local failed=()
   local name
@@ -141,7 +130,6 @@ function main() {
     name="cluster-${KWOK_RUNTIME}-${release//./-}"
     test_create_cluster "${release}" "${name}" || failed+=("create_cluster_${name}")
     test_prometheus || failed+=("prometheus_${name}")
-    test_kwok_controller_port || failed+=("kwok_controller_port_${name}")
     test_delete_cluster "${release}" "${name}" || failed+=("delete_cluster_${name}")
   done
   echo "------------------------------"