mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-09 21:17:09 +08:00
Fix tolerations not parsing its options correctly, add tests
Signed-off-by: Zsolt <zsolt.szeberenyi@figured.com>
This commit is contained in:
@ -68,121 +68,9 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
|
||||
clientset: clientset,
|
||||
}
|
||||
|
||||
deploymentOpt := &manifest.DeploymentOpt{
|
||||
Name: deploymentName,
|
||||
Image: bkimage.DefaultImage,
|
||||
Replicas: 1,
|
||||
BuildkitFlags: cfg.BuildkitFlags,
|
||||
Rootless: false,
|
||||
Platforms: cfg.Platforms,
|
||||
ConfigFiles: cfg.Files,
|
||||
}
|
||||
|
||||
deploymentOpt.Qemu.Image = bkimage.QemuImage
|
||||
|
||||
loadbalance := LoadbalanceSticky
|
||||
|
||||
for k, v := range cfg.DriverOpts {
|
||||
switch k {
|
||||
case "image":
|
||||
if v != "" {
|
||||
deploymentOpt.Image = v
|
||||
}
|
||||
case "namespace":
|
||||
namespace = v
|
||||
case "replicas":
|
||||
deploymentOpt.Replicas, err = strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "requests.cpu":
|
||||
deploymentOpt.RequestsCPU = v
|
||||
case "requests.memory":
|
||||
deploymentOpt.RequestsMemory = v
|
||||
case "limits.cpu":
|
||||
deploymentOpt.LimitsCPU = v
|
||||
case "limits.memory":
|
||||
deploymentOpt.LimitsMemory = v
|
||||
case "rootless":
|
||||
deploymentOpt.Rootless, err = strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, isImage := cfg.DriverOpts["image"]; !isImage {
|
||||
deploymentOpt.Image = bkimage.DefaultRootlessImage
|
||||
}
|
||||
case "nodeselector":
|
||||
kvs := strings.Split(strings.Trim(v, `"`), ",")
|
||||
s := map[string]string{}
|
||||
for i := range kvs {
|
||||
kv := strings.Split(kvs[i], "=")
|
||||
if len(kv) == 2 {
|
||||
s[kv[0]] = kv[1]
|
||||
}
|
||||
}
|
||||
deploymentOpt.NodeSelector = s
|
||||
case "tolerations":
|
||||
u, err := strconv.Unquote(v)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
ts := strings.Split(u, ";")
|
||||
deploymentOpt.Tolerations = []corev1.Toleration{}
|
||||
for i := range ts {
|
||||
kvs := strings.Split(ts[i], ",")
|
||||
if len(kvs) == 0 {
|
||||
return nil, errors.Errorf("invalid tolaration %q", v)
|
||||
}
|
||||
|
||||
t := corev1.Toleration{}
|
||||
|
||||
for j := range kvs {
|
||||
kv := strings.Split(kvs[j], "=")
|
||||
if len(kv) == 2 {
|
||||
switch kv[0] {
|
||||
case "key":
|
||||
t.Key = kv[1]
|
||||
case "operator":
|
||||
t.Operator = corev1.TolerationOperator(kv[1])
|
||||
case "value":
|
||||
t.Value = kv[1]
|
||||
case "effect":
|
||||
t.Effect = corev1.TaintEffect(kv[1])
|
||||
case "tolerationSeconds":
|
||||
c, err := strconv.Atoi(kv[1])
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
c64 := int64(c)
|
||||
t.TolerationSeconds = &c64
|
||||
default:
|
||||
return nil, errors.Errorf("invalid tolaration %q", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deploymentOpt.Tolerations = append(deploymentOpt.Tolerations, t)
|
||||
}
|
||||
case "loadbalance":
|
||||
switch v {
|
||||
case LoadbalanceSticky:
|
||||
case LoadbalanceRandom:
|
||||
default:
|
||||
return nil, errors.Errorf("invalid loadbalance %q", v)
|
||||
}
|
||||
loadbalance = v
|
||||
case "qemu.install":
|
||||
deploymentOpt.Qemu.Install, err = strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "qemu.image":
|
||||
if v != "" {
|
||||
deploymentOpt.Qemu.Image = v
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf("invalid driver option %s for driver %s", k, DriverName)
|
||||
}
|
||||
deploymentOpt, loadbalance, namespace, err := f.processDriverOpts(deploymentName, namespace, cfg)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.deployment, d.configMaps, err = manifest.NewDeployment(deploymentOpt)
|
||||
@ -212,6 +100,121 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg driver.InitConfig) (*manifest.DeploymentOpt, string, string, error) {
|
||||
deploymentOpt := &manifest.DeploymentOpt{
|
||||
Name: deploymentName,
|
||||
Image: bkimage.DefaultImage,
|
||||
Replicas: 1,
|
||||
BuildkitFlags: cfg.BuildkitFlags,
|
||||
Rootless: false,
|
||||
Platforms: cfg.Platforms,
|
||||
ConfigFiles: cfg.Files,
|
||||
}
|
||||
|
||||
deploymentOpt.Qemu.Image = bkimage.QemuImage
|
||||
|
||||
loadbalance := LoadbalanceSticky
|
||||
var err error
|
||||
|
||||
for k, v := range cfg.DriverOpts {
|
||||
switch k {
|
||||
case "image":
|
||||
if v != "" {
|
||||
deploymentOpt.Image = v
|
||||
}
|
||||
case "namespace":
|
||||
namespace = v
|
||||
case "replicas":
|
||||
deploymentOpt.Replicas, err = strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
case "requests.cpu":
|
||||
deploymentOpt.RequestsCPU = v
|
||||
case "requests.memory":
|
||||
deploymentOpt.RequestsMemory = v
|
||||
case "limits.cpu":
|
||||
deploymentOpt.LimitsCPU = v
|
||||
case "limits.memory":
|
||||
deploymentOpt.LimitsMemory = v
|
||||
case "rootless":
|
||||
deploymentOpt.Rootless, err = strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
if _, isImage := cfg.DriverOpts["image"]; !isImage {
|
||||
deploymentOpt.Image = bkimage.DefaultRootlessImage
|
||||
}
|
||||
case "nodeselector":
|
||||
kvs := strings.Split(strings.Trim(v, `"`), ",")
|
||||
s := map[string]string{}
|
||||
for i := range kvs {
|
||||
kv := strings.Split(kvs[i], "=")
|
||||
if len(kv) == 2 {
|
||||
s[kv[0]] = kv[1]
|
||||
}
|
||||
}
|
||||
deploymentOpt.NodeSelector = s
|
||||
case "tolerations":
|
||||
ts := strings.Split(v, ";")
|
||||
deploymentOpt.Tolerations = []corev1.Toleration{}
|
||||
for i := range ts {
|
||||
kvs := strings.Split(ts[i], ",")
|
||||
|
||||
t := corev1.Toleration{}
|
||||
|
||||
for j := range kvs {
|
||||
kv := strings.Split(kvs[j], "=")
|
||||
if len(kv) == 2 {
|
||||
switch kv[0] {
|
||||
case "key":
|
||||
t.Key = kv[1]
|
||||
case "operator":
|
||||
t.Operator = corev1.TolerationOperator(kv[1])
|
||||
case "value":
|
||||
t.Value = kv[1]
|
||||
case "effect":
|
||||
t.Effect = corev1.TaintEffect(kv[1])
|
||||
case "tolerationSeconds":
|
||||
c, err := strconv.Atoi(kv[1])
|
||||
if nil != err {
|
||||
return nil, "", "", err
|
||||
}
|
||||
c64 := int64(c)
|
||||
t.TolerationSeconds = &c64
|
||||
default:
|
||||
return nil, "", "", errors.Errorf("invalid tolaration %q", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deploymentOpt.Tolerations = append(deploymentOpt.Tolerations, t)
|
||||
}
|
||||
case "loadbalance":
|
||||
switch v {
|
||||
case LoadbalanceSticky:
|
||||
case LoadbalanceRandom:
|
||||
default:
|
||||
return nil, "", "", errors.Errorf("invalid loadbalance %q", v)
|
||||
}
|
||||
loadbalance = v
|
||||
case "qemu.install":
|
||||
deploymentOpt.Qemu.Install, err = strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
case "qemu.image":
|
||||
if v != "" {
|
||||
deploymentOpt.Qemu.Image = v
|
||||
}
|
||||
default:
|
||||
return nil, "", "", errors.Errorf("invalid driver option %s for driver %s", k, DriverName)
|
||||
}
|
||||
}
|
||||
|
||||
return deploymentOpt, loadbalance, namespace, nil
|
||||
}
|
||||
|
||||
func (f *factory) AllowsInstances() bool {
|
||||
return true
|
||||
}
|
||||
|
230
driver/kubernetes/factory_test.go
Normal file
230
driver/kubernetes/factory_test.go
Normal file
@ -0,0 +1,230 @@
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/buildx/driver"
|
||||
"github.com/docker/buildx/driver/bkimage"
|
||||
"github.com/stretchr/testify/require"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
type mockKubeClientConfig struct {
|
||||
clientConfig *rest.Config
|
||||
namespace string
|
||||
}
|
||||
|
||||
func (r *mockKubeClientConfig) ClientConfig() (*rest.Config, error) {
|
||||
return r.clientConfig, nil
|
||||
}
|
||||
|
||||
func (r *mockKubeClientConfig) Namespace() (string, bool, error) {
|
||||
return r.namespace, true, nil
|
||||
}
|
||||
|
||||
func TestFactory_processDriverOpts(t *testing.T) {
|
||||
kcc := mockKubeClientConfig{
|
||||
clientConfig: &rest.Config{},
|
||||
}
|
||||
|
||||
cfg := driver.InitConfig{
|
||||
Name: "buildx_buildkit_test",
|
||||
KubeClientConfig: &kcc,
|
||||
}
|
||||
f := factory{}
|
||||
|
||||
t.Run(
|
||||
"ValidOptions", func(t *testing.T) {
|
||||
cfg.DriverOpts = map[string]string{
|
||||
"namespace": "test-ns",
|
||||
"image": "test:latest",
|
||||
"replicas": "2",
|
||||
"requests.cpu": "100m",
|
||||
"requests.memory": "32Mi",
|
||||
"limits.cpu": "200m",
|
||||
"limits.memory": "64Mi",
|
||||
"rootless": "true",
|
||||
"nodeselector": "selector1=value1,selector2=value2",
|
||||
"tolerations": "key=tolerationKey1,value=tolerationValue1,operator=Equal,effect=NoSchedule,tolerationSeconds=60;key=tolerationKey2,operator=Exists",
|
||||
"loadbalance": "random",
|
||||
"qemu.install": "true",
|
||||
"qemu.image": "qemu:latest",
|
||||
}
|
||||
ns := "test"
|
||||
|
||||
r, loadbalance, ns, err := f.processDriverOpts(cfg.Name, ns, cfg)
|
||||
|
||||
nodeSelectors := map[string]string{
|
||||
"selector1": "value1",
|
||||
"selector2": "value2",
|
||||
}
|
||||
|
||||
ts := int64(60)
|
||||
tolerations := []v1.Toleration{
|
||||
{
|
||||
Key: "tolerationKey1",
|
||||
Operator: v1.TolerationOpEqual,
|
||||
Value: "tolerationValue1",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
TolerationSeconds: &ts,
|
||||
},
|
||||
{
|
||||
Key: "tolerationKey2",
|
||||
Operator: v1.TolerationOpExists,
|
||||
},
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, "test-ns", ns)
|
||||
require.Equal(t, "test:latest", r.Image)
|
||||
require.Equal(t, 2, r.Replicas)
|
||||
require.Equal(t, "100m", r.RequestsCPU)
|
||||
require.Equal(t, "32Mi", r.RequestsMemory)
|
||||
require.Equal(t, "200m", r.LimitsCPU)
|
||||
require.Equal(t, "64Mi", r.LimitsMemory)
|
||||
require.True(t, r.Rootless)
|
||||
require.Equal(t, nodeSelectors, r.NodeSelector)
|
||||
require.Equal(t, tolerations, r.Tolerations)
|
||||
require.Equal(t, LoadbalanceRandom, loadbalance)
|
||||
require.True(t, r.Qemu.Install)
|
||||
require.Equal(t, "qemu:latest", r.Qemu.Image)
|
||||
},
|
||||
)
|
||||
|
||||
t.Run(
|
||||
"NoOptions", func(t *testing.T) {
|
||||
cfg.DriverOpts = map[string]string{}
|
||||
|
||||
r, loadbalance, ns, err := f.processDriverOpts(cfg.Name, "test", cfg)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, "test", ns)
|
||||
require.Equal(t, bkimage.DefaultImage, r.Image)
|
||||
require.Equal(t, 1, r.Replicas)
|
||||
require.Equal(t, "", r.RequestsCPU)
|
||||
require.Equal(t, "", r.RequestsMemory)
|
||||
require.Equal(t, "", r.LimitsCPU)
|
||||
require.Equal(t, "", r.LimitsMemory)
|
||||
require.False(t, r.Rootless)
|
||||
require.Empty(t, r.NodeSelector)
|
||||
require.Empty(t, r.Tolerations)
|
||||
require.Equal(t, LoadbalanceSticky, loadbalance)
|
||||
require.False(t, r.Qemu.Install)
|
||||
require.Equal(t, bkimage.QemuImage, r.Qemu.Image)
|
||||
},
|
||||
)
|
||||
|
||||
t.Run(
|
||||
"RootlessOverride", func(t *testing.T) {
|
||||
cfg.DriverOpts = map[string]string{
|
||||
"rootless": "true",
|
||||
"loadbalance": "sticky",
|
||||
}
|
||||
|
||||
r, loadbalance, ns, err := f.processDriverOpts(cfg.Name, "test", cfg)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, "test", ns)
|
||||
require.Equal(t, bkimage.DefaultRootlessImage, r.Image)
|
||||
require.Equal(t, 1, r.Replicas)
|
||||
require.Equal(t, "", r.RequestsCPU)
|
||||
require.Equal(t, "", r.RequestsMemory)
|
||||
require.Equal(t, "", r.LimitsCPU)
|
||||
require.Equal(t, "", r.LimitsMemory)
|
||||
require.True(t, r.Rootless)
|
||||
require.Empty(t, r.NodeSelector)
|
||||
require.Empty(t, r.Tolerations)
|
||||
require.Equal(t, LoadbalanceSticky, loadbalance)
|
||||
require.False(t, r.Qemu.Install)
|
||||
require.Equal(t, bkimage.QemuImage, r.Qemu.Image)
|
||||
},
|
||||
)
|
||||
|
||||
t.Run(
|
||||
"InvalidReplicas", func(t *testing.T) {
|
||||
cfg.DriverOpts = map[string]string{
|
||||
"replicas": "invalid",
|
||||
}
|
||||
|
||||
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
|
||||
|
||||
require.Error(t, err)
|
||||
},
|
||||
)
|
||||
|
||||
t.Run(
|
||||
"InvalidRootless", func(t *testing.T) {
|
||||
cfg.DriverOpts = map[string]string{
|
||||
"rootless": "invalid",
|
||||
}
|
||||
|
||||
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
|
||||
|
||||
require.Error(t, err)
|
||||
},
|
||||
)
|
||||
|
||||
t.Run(
|
||||
"InvalidTolerationKeyword", func(t *testing.T) {
|
||||
cfg.DriverOpts = map[string]string{
|
||||
"tolerations": "key=foo,value=bar,invalid=foo2",
|
||||
}
|
||||
|
||||
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
|
||||
|
||||
require.Error(t, err)
|
||||
},
|
||||
)
|
||||
|
||||
t.Run(
|
||||
"InvalidTolerationSeconds", func(t *testing.T) {
|
||||
cfg.DriverOpts = map[string]string{
|
||||
"tolerations": "key=foo,value=bar,tolerationSeconds=invalid",
|
||||
}
|
||||
|
||||
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
|
||||
|
||||
require.Error(t, err)
|
||||
},
|
||||
)
|
||||
|
||||
t.Run(
|
||||
"InvalidLoadBalance", func(t *testing.T) {
|
||||
cfg.DriverOpts = map[string]string{
|
||||
"loadbalance": "invalid",
|
||||
}
|
||||
|
||||
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
|
||||
|
||||
require.Error(t, err)
|
||||
},
|
||||
)
|
||||
|
||||
t.Run(
|
||||
"InvalidQemuInstall", func(t *testing.T) {
|
||||
cfg.DriverOpts = map[string]string{
|
||||
"qemu.install": "invalid",
|
||||
}
|
||||
|
||||
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
|
||||
|
||||
require.Error(t, err)
|
||||
},
|
||||
)
|
||||
|
||||
t.Run(
|
||||
"InvalidOption", func(t *testing.T) {
|
||||
cfg.DriverOpts = map[string]string{
|
||||
"invalid": "foo",
|
||||
}
|
||||
|
||||
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
|
||||
|
||||
require.Error(t, err)
|
||||
},
|
||||
)
|
||||
}
|
Reference in New Issue
Block a user