From 3968676255b4d2fa378ce5955910603e5a29ffba Mon Sep 17 00:00:00 2001
From: Shiming Zhang
Date: Mon, 26 Aug 2024 22:26:31 +0800
Subject: [PATCH 1/2] Move parameters to expression
---
pkg/kwokctl/cmd/scale/scale.go | 3 +-
pkg/kwokctl/scale/scale.go | 27 ----------------
pkg/utils/expression/parameters.go | 49 ++++++++++++++++++++++++++++++
3 files changed, 51 insertions(+), 28 deletions(-)
create mode 100644 pkg/utils/expression/parameters.go
diff --git a/pkg/kwokctl/cmd/scale/scale.go b/pkg/kwokctl/cmd/scale/scale.go
index 9818a792a..d51547c27 100644
--- a/pkg/kwokctl/cmd/scale/scale.go
+++ b/pkg/kwokctl/cmd/scale/scale.go
@@ -34,6 +34,7 @@ import (
"sigs.k8s.io/kwok/pkg/kwokctl/scale"
"sigs.k8s.io/kwok/pkg/log"
"sigs.k8s.io/kwok/pkg/utils/client"
+ "sigs.k8s.io/kwok/pkg/utils/expression"
"sigs.k8s.io/kwok/pkg/utils/slices"
)
@@ -115,7 +116,7 @@ func runE(ctx context.Context, flags *flagpole, args []string) error {
}
}
- parameters, err := scale.NewParameters(ctx, krc.Parameters, flags.Params)
+ parameters, err := expression.NewParameters(ctx, krc.Parameters, flags.Params)
if err != nil {
return err
}
diff --git a/pkg/kwokctl/scale/scale.go b/pkg/kwokctl/scale/scale.go
index 609371b4d..ba9d8d692 100644
--- a/pkg/kwokctl/scale/scale.go
+++ b/pkg/kwokctl/scale/scale.go
@@ -36,7 +36,6 @@ import (
"sigs.k8s.io/kwok/pkg/kwokctl/snapshot"
"sigs.k8s.io/kwok/pkg/log"
"sigs.k8s.io/kwok/pkg/utils/client"
- "sigs.k8s.io/kwok/pkg/utils/expression"
"sigs.k8s.io/kwok/pkg/utils/gotpl"
utilsnet "sigs.k8s.io/kwok/pkg/utils/net"
"sigs.k8s.io/kwok/pkg/utils/yaml"
@@ -300,32 +299,6 @@ func Scale(ctx context.Context, clientset client.Clientset, conf Config) error {
return nil
}
-// NewParameters parses the parameters.
-func NewParameters(ctx context.Context, raw json.RawMessage, params []string) (any, error) {
- var param any
- err := json.Unmarshal(raw, ¶m)
- if err != nil {
- return nil, fmt.Errorf("unmarshal params error: %w", err)
- }
-
- for _, p := range params {
- q, err := expression.NewQuery(p)
- if err != nil {
- return nil, fmt.Errorf("parse param %s error: %w", p, err)
- }
- datas, err := q.Execute(ctx, param)
- if err != nil {
- return nil, fmt.Errorf("execute param %s with %v error: %w", p, param, err)
- }
- if len(datas) != 1 {
- return nil, fmt.Errorf("unexpected result: %v", datas)
- }
- param = datas[0]
- }
-
- return param, nil
-}
-
var (
labelNameKey = "kwok.x-k8s.io/kwokctl-scale"
)
diff --git a/pkg/utils/expression/parameters.go b/pkg/utils/expression/parameters.go
new file mode 100644
index 000000000..01fee06d2
--- /dev/null
+++ b/pkg/utils/expression/parameters.go
@@ -0,0 +1,49 @@
+/*
+Copyright 2024 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 expression
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+)
+
+// NewParameters parses the parameters.
+func NewParameters(ctx context.Context, raw json.RawMessage, params []string) (any, error) {
+ var param any
+ err := json.Unmarshal(raw, ¶m)
+ if err != nil {
+ return nil, fmt.Errorf("unmarshal params error: %w", err)
+ }
+
+ for _, p := range params {
+ q, err := NewQuery(p)
+ if err != nil {
+ return nil, fmt.Errorf("parse param %s error: %w", p, err)
+ }
+ datas, err := q.Execute(ctx, param)
+ if err != nil {
+ return nil, fmt.Errorf("execute param %s with %v error: %w", p, param, err)
+ }
+ if len(datas) != 1 {
+ return nil, fmt.Errorf("unexpected result: %v", datas)
+ }
+ param = datas[0]
+ }
+
+ return param, nil
+}
From 6496b32cafd38d0605550b5896fd23d0d1ff9dc2 Mon Sep 17 00:00:00 2001
From: Shiming Zhang
Date: Mon, 26 Aug 2024 22:26:31 +0800
Subject: [PATCH 2/2] Add kwokctl component api
---
kustomize/kwokctl/component/dashboard.yaml | 54 +++++++++
.../v1alpha1/kwokctl_component_types.go | 43 +++++++
.../v1alpha1/kwokctl_configuration_types.go | 4 +
.../config/v1alpha1/zz_generated.deepcopy.go | 31 +++++
pkg/apis/internalversion/conversion.go | 22 ++++
.../kwokctl_component_types.go | 34 ++++++
.../kwokctl_configuration_types.go | 3 +
.../zz_generated.conversion.go | 37 ++++++
.../internalversion/zz_generated.deepcopy.go | 22 ++++
pkg/config/config.go | 6 +
.../runtime/binary/cluster_component.go | 103 ++++++++++++++++
pkg/kwokctl/runtime/component.go | 47 ++++++++
.../runtime/compose/cluster_component.go | 113 ++++++++++++++++++
pkg/kwokctl/runtime/kind/cluster_component.go | 99 +++++++++++++++
pkg/utils/gotpl/funcs.go | 10 ++
pkg/utils/gotpl/renderer.go | 6 +-
site/content/en/docs/generated/apis.md | 88 ++++++++++++++
17 files changed, 719 insertions(+), 3 deletions(-)
create mode 100644 kustomize/kwokctl/component/dashboard.yaml
create mode 100644 pkg/apis/config/v1alpha1/kwokctl_component_types.go
create mode 100644 pkg/apis/internalversion/kwokctl_component_types.go
create mode 100644 pkg/kwokctl/runtime/binary/cluster_component.go
create mode 100644 pkg/kwokctl/runtime/component.go
create mode 100644 pkg/kwokctl/runtime/compose/cluster_component.go
create mode 100644 pkg/kwokctl/runtime/kind/cluster_component.go
diff --git a/kustomize/kwokctl/component/dashboard.yaml b/kustomize/kwokctl/component/dashboard.yaml
new file mode 100644
index 000000000..4be590a6a
--- /dev/null
+++ b/kustomize/kwokctl/component/dashboard.yaml
@@ -0,0 +1,54 @@
+apiVersion: config.kwok.x-k8s.io/v1alpha1
+kind: KwokctlComponent
+metadata:
+ name: dashboard
+parameters:
+ image: ""
+ binary: ""
+ version: "2.7.0"
+ bindAddress: "0.0.0.0"
+ port: 8000
+template: |-
+ {{ $version :=
+ ( or
+ ( env "KWOK_DASHBOARD_VERSION" )
+ .version
+ )
+ }}
+ {{ $image :=
+ ( or
+ .image
+ ( env "KWOK_DASHBOARD_IMAGE" )
+ ( join
+ ""
+ ( list
+ ( or
+ ( env "KWOK_DASHBOARD_IMAGE_PREFIX" )
+ "docker.io/kubernetesui"
+ )
+ "/dashboard:v"
+ $version
+ )
+ )
+ )
+ }}
+ image: {{ $image }}
+ binary: {{ .binary }}
+ links:
+ - kube-apiserver
+ ports:
+ - name: http
+ port: {{ .port }}
+ hostPort: {{ .port }}
+ protocol: TCP
+ args:
+ - --insecure-bind-address={{ .bindAddress }}
+ - --insecure-port={{ .port }}
+ - --bind-address=127.0.0.1
+ - --port=0
+ - --enable-insecure-login
+ - --enable-skip-login
+ - --disable-settings-authorizer
+ - --metrics-provider=none
+ - --system-banner=Welcome to {{ ClusterName }}
+ - --kubeconfig={{ Kubeconfig }}
diff --git a/pkg/apis/config/v1alpha1/kwokctl_component_types.go b/pkg/apis/config/v1alpha1/kwokctl_component_types.go
new file mode 100644
index 000000000..1597547ad
--- /dev/null
+++ b/pkg/apis/config/v1alpha1/kwokctl_component_types.go
@@ -0,0 +1,43 @@
+/*
+Copyright 2024 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 (
+ "encoding/json"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+const (
+ // KwokctlComponentKind is the kind of the kwokctl component.
+ KwokctlComponentKind = "KwokctlComponent"
+)
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// KwokctlComponent holds information about the kwokctl component.
+type KwokctlComponent struct {
+ //+k8s:conversion-gen=false
+ metav1.TypeMeta `json:",inline"`
+ // Standard list metadata.
+ // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
+ metav1.ObjectMeta `json:"metadata,omitempty"`
+ // Parameters is the parameters for the kwokctl component configuration.
+ Parameters json.RawMessage `json:"parameters,omitempty"`
+ // Template is the template for the kwokctl component configuration.
+ Template string `json:"template,omitempty"`
+}
diff --git a/pkg/apis/config/v1alpha1/kwokctl_configuration_types.go b/pkg/apis/config/v1alpha1/kwokctl_configuration_types.go
index 4dd7eb114..eeef6615a 100644
--- a/pkg/apis/config/v1alpha1/kwokctl_configuration_types.go
+++ b/pkg/apis/config/v1alpha1/kwokctl_configuration_types.go
@@ -523,6 +523,10 @@ type Component struct {
// MetricsDiscovery is the metrics discovery of the component.
MetricsDiscovery *ComponentMetric `json:"metricsDiscovery,omitempty"`
+ // Address is the address of the component.
+ // +optional
+ Address string `json:"address,omitempty"`
+
// Version is the version of the component.
// +optional
Version string `json:"version,omitempty"`
diff --git a/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go
index 80c889090..9fe9d315f 100644
--- a/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go
@@ -239,6 +239,37 @@ func (in *KwokConfigurationOptions) DeepCopy() *KwokConfigurationOptions {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *KwokctlComponent) DeepCopyInto(out *KwokctlComponent) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ if in.Parameters != nil {
+ in, out := &in.Parameters, &out.Parameters
+ *out = make(json.RawMessage, len(*in))
+ copy(*out, *in)
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KwokctlComponent.
+func (in *KwokctlComponent) DeepCopy() *KwokctlComponent {
+ if in == nil {
+ return nil
+ }
+ out := new(KwokctlComponent)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *KwokctlComponent) 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 *KwokctlConfiguration) DeepCopyInto(out *KwokctlConfiguration) {
*out = *in
diff --git a/pkg/apis/internalversion/conversion.go b/pkg/apis/internalversion/conversion.go
index 147c1ecd3..b2aeac28b 100644
--- a/pkg/apis/internalversion/conversion.go
+++ b/pkg/apis/internalversion/conversion.go
@@ -68,6 +68,28 @@ func ConvertToInternalKwokctlResource(in *configv1alpha1.KwokctlResource) (*Kwok
return &out, nil
}
+// ConvertToV1alpha1KwokctlComponent converts an internal version KwokctlComponent to a v1alpha1.KwokctlComponent.
+func ConvertToV1alpha1KwokctlComponent(in *KwokctlComponent) (*configv1alpha1.KwokctlComponent, error) {
+ var out configv1alpha1.KwokctlComponent
+ out.APIVersion = configv1alpha1.GroupVersion.String()
+ out.Kind = configv1alpha1.KwokctlComponentKind
+ err := Convert_internalversion_KwokctlComponent_To_v1alpha1_KwokctlComponent(in, &out, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &out, nil
+}
+
+// ConvertToInternalKwokctlComponent converts a v1alpha1.KwokctlComponent to an internal version.
+func ConvertToInternalKwokctlComponent(in *configv1alpha1.KwokctlComponent) (*KwokctlComponent, error) {
+ var out KwokctlComponent
+ err := Convert_v1alpha1_KwokctlComponent_To_internalversion_KwokctlComponent(in, &out, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &out, nil
+}
+
// ConvertToV1alpha1KwokConfiguration converts an internal version KwokConfiguration to a v1alpha1.KwokConfiguration.
func ConvertToV1alpha1KwokConfiguration(in *KwokConfiguration) (*configv1alpha1.KwokConfiguration, error) {
var out configv1alpha1.KwokConfiguration
diff --git a/pkg/apis/internalversion/kwokctl_component_types.go b/pkg/apis/internalversion/kwokctl_component_types.go
new file mode 100644
index 000000000..3bc6849a4
--- /dev/null
+++ b/pkg/apis/internalversion/kwokctl_component_types.go
@@ -0,0 +1,34 @@
+/*
+Copyright 2024 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 internalversion
+
+import (
+ "encoding/json"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// KwokctlComponent provides component definition for kwokctl.
+type KwokctlComponent struct {
+ // Standard list metadata.
+ // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
+ metav1.ObjectMeta
+ // Parameters is the parameters for the kwokctl component configuration.
+ Parameters json.RawMessage
+ // Template is the template for the kwokctl component configuration.
+ Template string
+}
diff --git a/pkg/apis/internalversion/kwokctl_configuration_types.go b/pkg/apis/internalversion/kwokctl_configuration_types.go
index 5d3a35454..953b6c4c8 100644
--- a/pkg/apis/internalversion/kwokctl_configuration_types.go
+++ b/pkg/apis/internalversion/kwokctl_configuration_types.go
@@ -355,6 +355,9 @@ type Component struct {
// MetricsDiscovery is the metrics discovery of the component.
MetricsDiscovery *ComponentMetric
+ // Address is the address of the component.
+ Address string
+
// Version is the version of the component.
Version string
}
diff --git a/pkg/apis/internalversion/zz_generated.conversion.go b/pkg/apis/internalversion/zz_generated.conversion.go
index f0a056fde..3cb788e60 100644
--- a/pkg/apis/internalversion/zz_generated.conversion.go
+++ b/pkg/apis/internalversion/zz_generated.conversion.go
@@ -340,6 +340,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
+ if err := s.AddGeneratedConversionFunc((*KwokctlComponent)(nil), (*configv1alpha1.KwokctlComponent)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_internalversion_KwokctlComponent_To_v1alpha1_KwokctlComponent(a.(*KwokctlComponent), b.(*configv1alpha1.KwokctlComponent), scope)
+ }); err != nil {
+ return err
+ }
+ if err := s.AddGeneratedConversionFunc((*configv1alpha1.KwokctlComponent)(nil), (*KwokctlComponent)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_v1alpha1_KwokctlComponent_To_internalversion_KwokctlComponent(a.(*configv1alpha1.KwokctlComponent), b.(*KwokctlComponent), scope)
+ }); err != nil {
+ return err
+ }
if err := s.AddGeneratedConversionFunc((*KwokctlConfiguration)(nil), (*configv1alpha1.KwokctlConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_internalversion_KwokctlConfiguration_To_v1alpha1_KwokctlConfiguration(a.(*KwokctlConfiguration), b.(*configv1alpha1.KwokctlConfiguration), scope)
}); err != nil {
@@ -1071,6 +1081,7 @@ func autoConvert_internalversion_Component_To_v1alpha1_Component(in *Component,
}
out.Metric = (*configv1alpha1.ComponentMetric)(unsafe.Pointer(in.Metric))
out.MetricsDiscovery = (*configv1alpha1.ComponentMetric)(unsafe.Pointer(in.MetricsDiscovery))
+ out.Address = in.Address
out.Version = in.Version
return nil
}
@@ -1104,6 +1115,7 @@ func autoConvert_v1alpha1_Component_To_internalversion_Component(in *configv1alp
}
out.Metric = (*ComponentMetric)(unsafe.Pointer(in.Metric))
out.MetricsDiscovery = (*ComponentMetric)(unsafe.Pointer(in.MetricsDiscovery))
+ out.Address = in.Address
out.Version = in.Version
return nil
}
@@ -1566,6 +1578,31 @@ func Convert_v1alpha1_KwokConfigurationOptions_To_internalversion_KwokConfigurat
return autoConvert_v1alpha1_KwokConfigurationOptions_To_internalversion_KwokConfigurationOptions(in, out, s)
}
+func autoConvert_internalversion_KwokctlComponent_To_v1alpha1_KwokctlComponent(in *KwokctlComponent, out *configv1alpha1.KwokctlComponent, s conversion.Scope) error {
+ out.ObjectMeta = in.ObjectMeta
+ out.Parameters = *(*json.RawMessage)(unsafe.Pointer(&in.Parameters))
+ out.Template = in.Template
+ return nil
+}
+
+// Convert_internalversion_KwokctlComponent_To_v1alpha1_KwokctlComponent is an autogenerated conversion function.
+func Convert_internalversion_KwokctlComponent_To_v1alpha1_KwokctlComponent(in *KwokctlComponent, out *configv1alpha1.KwokctlComponent, s conversion.Scope) error {
+ return autoConvert_internalversion_KwokctlComponent_To_v1alpha1_KwokctlComponent(in, out, s)
+}
+
+func autoConvert_v1alpha1_KwokctlComponent_To_internalversion_KwokctlComponent(in *configv1alpha1.KwokctlComponent, out *KwokctlComponent, s conversion.Scope) error {
+ // INFO: in.TypeMeta opted out of conversion generation
+ out.ObjectMeta = in.ObjectMeta
+ out.Parameters = *(*json.RawMessage)(unsafe.Pointer(&in.Parameters))
+ out.Template = in.Template
+ return nil
+}
+
+// Convert_v1alpha1_KwokctlComponent_To_internalversion_KwokctlComponent is an autogenerated conversion function.
+func Convert_v1alpha1_KwokctlComponent_To_internalversion_KwokctlComponent(in *configv1alpha1.KwokctlComponent, out *KwokctlComponent, s conversion.Scope) error {
+ return autoConvert_v1alpha1_KwokctlComponent_To_internalversion_KwokctlComponent(in, out, s)
+}
+
func autoConvert_internalversion_KwokctlConfiguration_To_v1alpha1_KwokctlConfiguration(in *KwokctlConfiguration, out *configv1alpha1.KwokctlConfiguration, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_internalversion_KwokctlConfigurationOptions_To_v1alpha1_KwokctlConfigurationOptions(&in.Options, &out.Options, s); err != nil {
diff --git a/pkg/apis/internalversion/zz_generated.deepcopy.go b/pkg/apis/internalversion/zz_generated.deepcopy.go
index 03a962e75..fff3774eb 100644
--- a/pkg/apis/internalversion/zz_generated.deepcopy.go
+++ b/pkg/apis/internalversion/zz_generated.deepcopy.go
@@ -695,6 +695,28 @@ func (in *KwokConfigurationOptions) DeepCopy() *KwokConfigurationOptions {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *KwokctlComponent) DeepCopyInto(out *KwokctlComponent) {
+ *out = *in
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ if in.Parameters != nil {
+ in, out := &in.Parameters, &out.Parameters
+ *out = make(json.RawMessage, len(*in))
+ copy(*out, *in)
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KwokctlComponent.
+func (in *KwokctlComponent) DeepCopy() *KwokctlComponent {
+ if in == nil {
+ return nil
+ }
+ out := new(KwokctlComponent)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KwokctlConfiguration) DeepCopyInto(out *KwokctlConfiguration) {
*out = *in
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 089702c5f..8bd513cd4 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -110,6 +110,12 @@ var configHandlers = map[string]configHandler{
MutateToInternal: mutateToInternalConfig(internalversion.ConvertToInternalKwokctlResource),
MutateToVersiond: mutateToVersiondConfig(internalversion.ConvertToV1alpha1KwokctlResource),
},
+ configv1alpha1.KwokctlComponentKind: {
+ Unmarshal: unmarshalConfig[*configv1alpha1.KwokctlComponent],
+ Marshal: marshalConfig,
+ MutateToInternal: mutateToInternalConfig(internalversion.ConvertToInternalKwokctlComponent),
+ MutateToVersiond: mutateToVersiondConfig(internalversion.ConvertToV1alpha1KwokctlComponent),
+ },
v1alpha1.StageKind: {
Unmarshal: unmarshalConfig[*v1alpha1.Stage],
Marshal: marshalConfig,
diff --git a/pkg/kwokctl/runtime/binary/cluster_component.go b/pkg/kwokctl/runtime/binary/cluster_component.go
new file mode 100644
index 000000000..093326369
--- /dev/null
+++ b/pkg/kwokctl/runtime/binary/cluster_component.go
@@ -0,0 +1,103 @@
+/*
+Copyright 2024 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 binary
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+
+ "sigs.k8s.io/kwok/pkg/apis/internalversion"
+ "sigs.k8s.io/kwok/pkg/config"
+ "sigs.k8s.io/kwok/pkg/kwokctl/runtime"
+ "sigs.k8s.io/kwok/pkg/utils/expression"
+ "sigs.k8s.io/kwok/pkg/utils/gotpl"
+ "sigs.k8s.io/kwok/pkg/utils/path"
+ "sigs.k8s.io/kwok/pkg/utils/slices"
+)
+
+// AddComponent adds the component in to cluster
+func (c *Cluster) AddComponent(ctx context.Context, name string, args ...string) error {
+ conf, err := c.Config(ctx)
+ if err != nil {
+ return err
+ }
+ _, ok := slices.Find(conf.Components, func(component internalversion.Component) bool {
+ return component.Name == name
+ })
+ if ok {
+ return fmt.Errorf("component %s is already exists", name)
+ }
+
+ kcp := config.FilterWithTypeFromContext[*internalversion.KwokctlComponent](ctx)
+ renderer := gotpl.NewRenderer(gotpl.FuncMap{
+ "ClusterName": c.Name,
+ "Workdir": c.Workdir,
+ "Runtime": func() string {
+ return c.Runtime(ctx)
+ },
+ "Mode": func() string {
+ return c.Mode(ctx)
+ },
+ "Address": func() string {
+ return c.ComponentAddress(ctx, name)
+ },
+ "PkiDir": func() string {
+ return path.Join(c.Workdir(), runtime.PkiName)
+ },
+ "Kubeconfig": func() string {
+ return c.GetWorkdirPath(runtime.InHostKubeconfigName)
+ },
+ "Config": func() *internalversion.KwokctlConfiguration {
+ return conf
+ },
+ })
+
+ krc, ok := slices.Find(kcp, func(krc *internalversion.KwokctlComponent) bool {
+ return krc.Name == name
+ })
+ if !ok {
+ return fmt.Errorf("component %s is not exists", name)
+ }
+
+ param, err := expression.NewParameters(ctx, krc.Parameters, args)
+ if err != nil {
+ return err
+ }
+
+ componentData, err := renderer.ToJSON(krc.Template, param)
+ if err != nil {
+ return err
+ }
+ var component internalversion.Component
+ err = json.Unmarshal(componentData, &component)
+ if err != nil {
+ return err
+ }
+ component.Name = name
+
+ binaryPath, err := c.EnsureBinary(ctx, component.Name, component.Binary)
+ if err != nil {
+ return err
+ }
+
+ component.Binary = binaryPath
+
+ conf.Components = append(conf.Components, component)
+
+ return c.SetConfig(ctx, conf)
+}
diff --git a/pkg/kwokctl/runtime/component.go b/pkg/kwokctl/runtime/component.go
new file mode 100644
index 000000000..912003ef2
--- /dev/null
+++ b/pkg/kwokctl/runtime/component.go
@@ -0,0 +1,47 @@
+/*
+Copyright 2024 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 runtime
+
+import (
+ "context"
+
+ "sigs.k8s.io/kwok/pkg/kwokctl/components"
+ "sigs.k8s.io/kwok/pkg/utils/net"
+)
+
+func (c *Cluster) Runtime(ctx context.Context) string {
+ config, err := c.Config(ctx)
+ if err != nil {
+ return ""
+ }
+ conf := &config.Options
+
+ return conf.Runtime
+}
+
+func (c *Cluster) Mode(ctx context.Context) string {
+ return components.GetRuntimeMode(c.Runtime(ctx))
+}
+
+func (c *Cluster) ComponentAddress(ctx context.Context, name string) string {
+ switch c.Mode(ctx) {
+ case components.RuntimeModeContainer:
+ return c.Name() + "-" + name
+ default:
+ return net.LocalAddress
+ }
+}
diff --git a/pkg/kwokctl/runtime/compose/cluster_component.go b/pkg/kwokctl/runtime/compose/cluster_component.go
new file mode 100644
index 000000000..25eb49a90
--- /dev/null
+++ b/pkg/kwokctl/runtime/compose/cluster_component.go
@@ -0,0 +1,113 @@
+/*
+Copyright 2024 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 compose
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+
+ "sigs.k8s.io/kwok/pkg/apis/internalversion"
+ "sigs.k8s.io/kwok/pkg/config"
+ "sigs.k8s.io/kwok/pkg/kwokctl/runtime"
+ "sigs.k8s.io/kwok/pkg/utils/expression"
+ "sigs.k8s.io/kwok/pkg/utils/gotpl"
+ "sigs.k8s.io/kwok/pkg/utils/slices"
+)
+
+// AddComponent adds the component in to cluster
+func (c *Cluster) AddComponent(ctx context.Context, name string, args ...string) error {
+ conf, err := c.Config(ctx)
+ if err != nil {
+ return err
+ }
+ _, ok := slices.Find(conf.Components, func(component internalversion.Component) bool {
+ return component.Name == name
+ })
+ if ok {
+ return fmt.Errorf("component %s is already exists", name)
+ }
+
+ kcp := config.FilterWithTypeFromContext[*internalversion.KwokctlComponent](ctx)
+ renderer := gotpl.NewRenderer(gotpl.FuncMap{
+ "ClusterName": c.Name,
+ "Workdir": c.Workdir,
+ "Runtime": func() string {
+ return c.Runtime(ctx)
+ },
+ "Mode": func() string {
+ return c.Mode(ctx)
+ },
+ "Address": func() string {
+ return c.ComponentAddress(ctx, name)
+ },
+ "PkiDir": func() string {
+ return "/etc/kubernetes/pki"
+ },
+ "Kubeconfig": func() string {
+ return "/root/.kube/config"
+ },
+ "Config": func() *internalversion.KwokctlConfiguration {
+ return conf
+ },
+ })
+
+ krc, ok := slices.Find(kcp, func(krc *internalversion.KwokctlComponent) bool {
+ return krc.Name == name
+ })
+ if !ok {
+ return fmt.Errorf("component %s is not exists", name)
+ }
+
+ param, err := expression.NewParameters(ctx, krc.Parameters, args)
+ if err != nil {
+ return err
+ }
+
+ componentData, err := renderer.ToJSON(krc.Template, param)
+ if err != nil {
+ return err
+ }
+ var component internalversion.Component
+ err = json.Unmarshal(componentData, &component)
+ if err != nil {
+ return err
+ }
+ component.Name = name
+
+ volumes := []internalversion.Volume{
+ {
+ HostPath: c.GetWorkdirPath(runtime.InClusterKubeconfigName),
+ MountPath: "/root/.kube/config",
+ },
+ {
+ HostPath: c.GetWorkdirPath(runtime.PkiName),
+ MountPath: "/etc/kubernetes/pki/",
+ },
+ }
+
+ component.Volumes = append(component.Volumes, volumes...)
+
+ err = c.EnsureImage(ctx, c.runtime, component.Image)
+ if err != nil {
+ return err
+ }
+
+ conf.Components = append(conf.Components, component)
+
+ return c.SetConfig(ctx, conf)
+}
diff --git a/pkg/kwokctl/runtime/kind/cluster_component.go b/pkg/kwokctl/runtime/kind/cluster_component.go
new file mode 100644
index 000000000..ae9046147
--- /dev/null
+++ b/pkg/kwokctl/runtime/kind/cluster_component.go
@@ -0,0 +1,99 @@
+/*
+Copyright 2024 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 kind
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+
+ "sigs.k8s.io/kwok/pkg/apis/internalversion"
+ "sigs.k8s.io/kwok/pkg/config"
+ "sigs.k8s.io/kwok/pkg/utils/expression"
+ "sigs.k8s.io/kwok/pkg/utils/gotpl"
+ "sigs.k8s.io/kwok/pkg/utils/slices"
+)
+
+// AddComponent adds the component in to cluster
+func (c *Cluster) AddComponent(ctx context.Context, name string, args ...string) error {
+ conf, err := c.Config(ctx)
+ if err != nil {
+ return err
+ }
+ _, ok := slices.Find(conf.Components, func(component internalversion.Component) bool {
+ return component.Name == name
+ })
+ if ok {
+ return fmt.Errorf("component %s is already exists", name)
+ }
+
+ kcp := config.FilterWithTypeFromContext[*internalversion.KwokctlComponent](ctx)
+ renderer := gotpl.NewRenderer(gotpl.FuncMap{
+ "ClusterName": c.Name,
+ "Workdir": c.Workdir,
+ "Runtime": func() string {
+ return c.Runtime(ctx)
+ },
+ "Mode": func() string {
+ return c.Mode(ctx)
+ },
+ "Address": func() string {
+ return c.ComponentAddress(ctx, name)
+ },
+ "PkiDir": func() string {
+ return "/etc/kubernetes/pki"
+ },
+ "Kubeconfig": func() string {
+ return "/root/.kube/config"
+ },
+ "Config": func() *internalversion.KwokctlConfiguration {
+ return conf
+ },
+ })
+
+ krc, ok := slices.Find(kcp, func(krc *internalversion.KwokctlComponent) bool {
+ return krc.Name == name
+ })
+ if !ok {
+ return fmt.Errorf("component %s is not exists", name)
+ }
+
+ param, err := expression.NewParameters(ctx, krc.Parameters, args)
+ if err != nil {
+ return err
+ }
+
+ componentData, err := renderer.ToJSON(krc.Template, param)
+ if err != nil {
+ return err
+ }
+ var component internalversion.Component
+ err = json.Unmarshal(componentData, &component)
+ if err != nil {
+ return err
+ }
+ component.Name = name
+
+ err = c.EnsureImage(ctx, c.runtime, component.Image)
+ if err != nil {
+ return err
+ }
+
+ conf.Components = append(conf.Components, component)
+
+ return c.SetConfig(ctx, conf)
+}
diff --git a/pkg/utils/gotpl/funcs.go b/pkg/utils/gotpl/funcs.go
index 40c2c0810..d4e3435e2 100644
--- a/pkg/utils/gotpl/funcs.go
+++ b/pkg/utils/gotpl/funcs.go
@@ -19,6 +19,7 @@ package gotpl
import (
"encoding/json"
"fmt"
+ "runtime"
"strconv"
"strings"
"time"
@@ -36,6 +37,15 @@ var (
genericFuncs = sprig.TxtFuncMap()
)
+func init() {
+ genericFuncs["GOOS"] = func() string {
+ return runtime.GOOS
+ }
+ genericFuncs["GOARCH"] = func() string {
+ return runtime.GOARCH
+ }
+}
+
var (
startTime = time.Now().Format(time.RFC3339Nano)
diff --git a/pkg/utils/gotpl/renderer.go b/pkg/utils/gotpl/renderer.go
index 26996a957..fe794dc86 100644
--- a/pkg/utils/gotpl/renderer.go
+++ b/pkg/utils/gotpl/renderer.go
@@ -101,7 +101,7 @@ func (r *renderer) ToText(text string, original interface{}) ([]byte, error) {
err := r.render(buf, text, original)
if err != nil {
- return nil, fmt.Errorf("%w: %s", err, buf.String())
+ return nil, fmt.Errorf("%w: %s", err, strings.TrimRight(buf.String(), "\x00"))
}
return slices.Clone(buf.Bytes()), nil
}
@@ -113,12 +113,12 @@ func (r *renderer) ToJSON(text string, original interface{}) ([]byte, error) {
err := r.render(buf, text, original)
if err != nil {
- return nil, fmt.Errorf("%w: %s", err, buf.String())
+ return nil, fmt.Errorf("%w: %s", err, strings.TrimRight(buf.String(), "\x00"))
}
out, err := yaml.YAMLToJSON(buf.Bytes())
if err != nil {
- return nil, fmt.Errorf("%w: %s", err, buf.String())
+ return nil, fmt.Errorf("%w: %s", err, strings.TrimRight(buf.String(), "\x00"))
}
return out, nil
}
diff --git a/site/content/en/docs/generated/apis.md b/site/content/en/docs/generated/apis.md
index bef9ae20f..3690ae3cb 100644
--- a/site/content/en/docs/generated/apis.md
+++ b/site/content/en/docs/generated/apis.md
@@ -137,6 +137,9 @@ Resource Types:
KwokConfiguration
+KwokctlComponent
+
+
KwokctlConfiguration
@@ -206,6 +209,79 @@ KwokConfigurationOptions
+
+KwokctlComponent
+ #
+
+
+
KwokctlComponent holds information about the kwokctl component.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+apiVersion
+string
+ |
+
+
+config.kwok.x-k8s.io/v1alpha1
+
+ |
+
+
+
+kind
+string
+ |
+KwokctlComponent |
+
+
+
+metadata
+
+
+Kubernetes meta/v1.ObjectMeta
+
+
+ |
+
+ Standard list metadata.
+More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
+Refer to the Kubernetes API documentation for the fields of the
+metadata field.
+ |
+
+
+
+parameters
+
+encoding/json.RawMessage
+
+ |
+
+ Parameters is the parameters for the kwokctl component configuration.
+ |
+
+
+
+template
+
+string
+
+ |
+
+ Template is the template for the kwokctl component configuration.
+ |
+
+
+
KwokctlConfiguration
#
@@ -2007,6 +2083,18 @@ ComponentMetric
+address
+
+string
+
+ |
+
+(Optional)
+ Address is the address of the component.
+ |
+
+
+
version
string
|