Unverified Commit f18b3252 authored by Chris Kim's avatar Chris Kim Committed by GitHub

[master] Add etcd extra args support for K3s (#4463)

* Add etcd extra args support for K3s Signed-off-by: 's avatarChris Kim <oats87g@gmail.com> * Add etcd custom argument integration test Signed-off-by: 's avatarChris Kim <oats87g@gmail.com> * go generate Signed-off-by: 's avatarChris Kim <oats87g@gmail.com>
parent 41ff19de
//go:build !ignore_autogenerated
// +build !ignore_autogenerated // +build !ignore_autogenerated
/* /*
......
...@@ -48,6 +48,7 @@ type Server struct { ...@@ -48,6 +48,7 @@ type Server struct {
TLSSan cli.StringSlice TLSSan cli.StringSlice
BindAddress string BindAddress string
ExtraAPIArgs cli.StringSlice ExtraAPIArgs cli.StringSlice
ExtraEtcdArgs cli.StringSlice
ExtraSchedulerArgs cli.StringSlice ExtraSchedulerArgs cli.StringSlice
ExtraControllerArgs cli.StringSlice ExtraControllerArgs cli.StringSlice
ExtraCloudControllerArgs cli.StringSlice ExtraCloudControllerArgs cli.StringSlice
...@@ -128,6 +129,11 @@ var ( ...@@ -128,6 +129,11 @@ var (
Usage: "(flags) Customized flag for kube-apiserver process", Usage: "(flags) Customized flag for kube-apiserver process",
Value: &ServerConfig.ExtraAPIArgs, Value: &ServerConfig.ExtraAPIArgs,
} }
ExtraEtcdArgs = cli.StringSliceFlag{
Name: "etcd-arg",
Usage: "(flags) Customized flag for etcd process",
Value: &ServerConfig.ExtraEtcdArgs,
}
ExtraSchedulerArgs = cli.StringSliceFlag{ ExtraSchedulerArgs = cli.StringSliceFlag{
Name: "kube-scheduler-arg", Name: "kube-scheduler-arg",
Usage: "(flags) Customized flag for kube-scheduler process", Usage: "(flags) Customized flag for kube-scheduler process",
...@@ -214,6 +220,7 @@ var ServerFlags = []cli.Flag{ ...@@ -214,6 +220,7 @@ var ServerFlags = []cli.Flag{
EnvVar: version.ProgramUpper + "_KUBECONFIG_MODE", EnvVar: version.ProgramUpper + "_KUBECONFIG_MODE",
}, },
ExtraAPIArgs, ExtraAPIArgs,
ExtraEtcdArgs,
ExtraControllerArgs, ExtraControllerArgs,
ExtraSchedulerArgs, ExtraSchedulerArgs,
cli.StringSliceFlag{ cli.StringSliceFlag{
......
...@@ -117,6 +117,7 @@ func run(app *cli.Context, cfg *cmds.Server, leaderControllers server.CustomCont ...@@ -117,6 +117,7 @@ func run(app *cli.Context, cfg *cmds.Server, leaderControllers server.CustomCont
serverConfig.ControlConfig.APIServerBindAddress = cfg.APIServerBindAddress serverConfig.ControlConfig.APIServerBindAddress = cfg.APIServerBindAddress
serverConfig.ControlConfig.ExtraAPIArgs = cfg.ExtraAPIArgs serverConfig.ControlConfig.ExtraAPIArgs = cfg.ExtraAPIArgs
serverConfig.ControlConfig.ExtraControllerArgs = cfg.ExtraControllerArgs serverConfig.ControlConfig.ExtraControllerArgs = cfg.ExtraControllerArgs
serverConfig.ControlConfig.ExtraEtcdArgs = cfg.ExtraEtcdArgs
serverConfig.ControlConfig.ExtraSchedulerAPIArgs = cfg.ExtraSchedulerArgs serverConfig.ControlConfig.ExtraSchedulerAPIArgs = cfg.ExtraSchedulerArgs
serverConfig.ControlConfig.ClusterDomain = cfg.ClusterDomain serverConfig.ControlConfig.ClusterDomain = cfg.ClusterDomain
serverConfig.ControlConfig.Datastore.Endpoint = cfg.DatastoreEndpoint serverConfig.ControlConfig.Datastore.Endpoint = cfg.DatastoreEndpoint
......
...@@ -74,7 +74,7 @@ func (c *Cluster) Bootstrap(ctx context.Context, snapshot bool) error { ...@@ -74,7 +74,7 @@ func (c *Cluster) Bootstrap(ctx context.Context, snapshot bool) error {
ElectionTimeout: 5000, ElectionTimeout: 5000,
LogOutputs: []string{"stderr"}, LogOutputs: []string{"stderr"},
} }
configFile, err := args.ToConfigFile() configFile, err := args.ToConfigFile(c.config.ExtraEtcdArgs)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -135,6 +135,7 @@ type Control struct { ...@@ -135,6 +135,7 @@ type Control struct {
ExtraAPIArgs []string ExtraAPIArgs []string
ExtraControllerArgs []string ExtraControllerArgs []string
ExtraCloudControllerArgs []string ExtraCloudControllerArgs []string
ExtraEtcdArgs []string
ExtraSchedulerAPIArgs []string ExtraSchedulerAPIArgs []string
NoLeaderElect bool NoLeaderElect bool
JoinURL string JoinURL string
......
...@@ -19,8 +19,8 @@ func (e Embedded) CurrentETCDOptions() (InitialOptions, error) { ...@@ -19,8 +19,8 @@ func (e Embedded) CurrentETCDOptions() (InitialOptions, error) {
return InitialOptions{}, nil return InitialOptions{}, nil
} }
func (e Embedded) ETCD(ctx context.Context, args ETCDConfig) error { func (e Embedded) ETCD(ctx context.Context, args ETCDConfig, extraArgs []string) error {
configFile, err := args.ToConfigFile() configFile, err := args.ToConfigFile(extraArgs)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -6,12 +6,15 @@ import ( ...@@ -6,12 +6,15 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"sigs.k8s.io/yaml" "strings"
"time"
"github.com/rancher/k3s/pkg/cli/cmds" "github.com/rancher/k3s/pkg/cli/cmds"
daemonconfig "github.com/rancher/k3s/pkg/daemons/config" daemonconfig "github.com/rancher/k3s/pkg/daemons/config"
yaml2 "gopkg.in/yaml.v2"
"k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/authenticator"
"sigs.k8s.io/yaml"
) )
var ( var (
...@@ -27,7 +30,7 @@ type Executor interface { ...@@ -27,7 +30,7 @@ type Executor interface {
Scheduler(ctx context.Context, apiReady <-chan struct{}, args []string) error Scheduler(ctx context.Context, apiReady <-chan struct{}, args []string) error
ControllerManager(ctx context.Context, apiReady <-chan struct{}, args []string) error ControllerManager(ctx context.Context, apiReady <-chan struct{}, args []string) error
CurrentETCDOptions() (InitialOptions, error) CurrentETCDOptions() (InitialOptions, error)
ETCD(ctx context.Context, args ETCDConfig) error ETCD(ctx context.Context, args ETCDConfig, extraArgs []string) error
CloudControllerManager(ctx context.Context, ccmRBACReady <-chan struct{}, args []string) error CloudControllerManager(ctx context.Context, ccmRBACReady <-chan struct{}, args []string) error
} }
...@@ -69,13 +72,53 @@ type InitialOptions struct { ...@@ -69,13 +72,53 @@ type InitialOptions struct {
State string `json:"initial-cluster-state,omitempty"` State string `json:"initial-cluster-state,omitempty"`
} }
func (e ETCDConfig) ToConfigFile() (string, error) { func (e ETCDConfig) ToConfigFile(extraArgs []string) (string, error) {
confFile := filepath.Join(e.DataDir, "config") confFile := filepath.Join(e.DataDir, "config")
bytes, err := yaml.Marshal(&e) bytes, err := yaml.Marshal(&e)
if err != nil { if err != nil {
return "", err return "", err
} }
if len(extraArgs) > 0 {
var s map[string]interface{}
if err := yaml2.Unmarshal(bytes, &s); err != nil {
return "", err
}
for _, v := range extraArgs {
extraArg := strings.SplitN(v, "=", 2)
// Depending on the argV, we have different types to handle.
// Source: https://github.com/etcd-io/etcd/blob/44b8ae145b505811775f5af915dd19198d556d55/server/config/config.go#L36-L190 and https://etcd.io/docs/v3.5/op-guide/configuration/#configuration-file
if len(extraArg) == 2 {
key := strings.TrimLeft(extraArg[0], "-")
lowerKey := strings.ToLower(key)
var stringArr []string
if i, err := strconv.Atoi(extraArg[1]); err == nil {
s[key] = i
} else if time, err := time.ParseDuration(extraArg[1]); err == nil && (strings.Contains(lowerKey, "time") || strings.Contains(lowerKey, "duration") || strings.Contains(lowerKey, "interval") || strings.Contains(lowerKey, "retention")) {
// auto-compaction-retention is either a time.Duration or int, depending on version. If it is an int, it will be caught above.
s[key] = time
} else if err := yaml.Unmarshal([]byte(extraArg[1]), &stringArr); err == nil {
s[key] = stringArr
} else {
switch strings.ToLower(extraArg[1]) {
case "true":
s[key] = true
case "false":
s[key] = false
default:
s[key] = extraArg[1]
}
}
}
}
bytes, err = yaml2.Marshal(&s)
if err != nil {
return "", err
}
}
if err := os.MkdirAll(e.DataDir, 0700); err != nil { if err := os.MkdirAll(e.DataDir, 0700); err != nil {
return "", err return "", err
} }
...@@ -118,8 +161,8 @@ func CurrentETCDOptions() (InitialOptions, error) { ...@@ -118,8 +161,8 @@ func CurrentETCDOptions() (InitialOptions, error) {
return executor.CurrentETCDOptions() return executor.CurrentETCDOptions()
} }
func ETCD(ctx context.Context, args ETCDConfig) error { func ETCD(ctx context.Context, args ETCDConfig, extraArgs []string) error {
return executor.ETCD(ctx, args) return executor.ETCD(ctx, args, extraArgs)
} }
func CloudControllerManager(ctx context.Context, ccmRBACReady <-chan struct{}, args []string) error { func CloudControllerManager(ctx context.Context, ccmRBACReady <-chan struct{}, args []string) error {
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
// manifests/metrics-server/resource-reader.yaml // manifests/metrics-server/resource-reader.yaml
// manifests/rolebindings.yaml // manifests/rolebindings.yaml
// manifests/traefik.yaml // manifests/traefik.yaml
//go:build !no_stage
// +build !no_stage // +build !no_stage
package deploy package deploy
......
...@@ -669,7 +669,7 @@ func (e *ETCD) cluster(ctx context.Context, forceNew bool, options executor.Init ...@@ -669,7 +669,7 @@ func (e *ETCD) cluster(ctx context.Context, forceNew bool, options executor.Init
HeartbeatInterval: 500, HeartbeatInterval: 500,
Logger: "zap", Logger: "zap",
LogOutputs: []string{"stderr"}, LogOutputs: []string{"stderr"},
}) }, e.config.ExtraEtcdArgs)
} }
// RemovePeer removes a peer from the cluster. The peer name and IP address must both match. // RemovePeer removes a peer from the cluster. The peer name and IP address must both match.
......
This source diff could not be displayed because it is too large. You can view the blob instead.
package integration
import (
"os/exec"
"strings"
"testing"
. "github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/reporters"
. "github.com/onsi/gomega"
testutil "github.com/rancher/k3s/tests/util"
)
var customEtcdArgsServer *testutil.K3sServer
var customEtcdArgsServerArgs = []string{
"--cluster-init",
"--etcd-arg quota-backend-bytes=858993459",
}
var _ = BeforeSuite(func() {
if !testutil.IsExistingServer() {
var err error
customEtcdArgsServer, err = testutil.K3sStartServer(customEtcdArgsServerArgs...)
Expect(err).ToNot(HaveOccurred())
}
})
var _ = Describe("custom etcd args", func() {
BeforeEach(func() {
if testutil.IsExistingServer() && !testutil.ServerArgsPresent(customEtcdArgsServerArgs) {
Skip("Test needs k3s server with: " + strings.Join(customEtcdArgsServerArgs, " "))
}
})
When("a custom quota backend bytes is specified", func() {
It("renders a config file with the correct entry", func() {
Eventually(func() (string, error) {
var cmd *exec.Cmd
grepCmd := "grep"
grepCmdArgs := []string{"quota-backend-bytes", "/var/lib/rancher/k3s/server/db/etcd/config"}
if testutil.IsRoot() {
cmd = exec.Command(grepCmd, grepCmdArgs...)
} else {
fullGrepCmd := append([]string{grepCmd}, grepCmdArgs...)
cmd = exec.Command("sudo", fullGrepCmd...)
}
byteOut, err := cmd.CombinedOutput()
return string(byteOut), err
}, "45s", "5s").Should(MatchRegexp(".*quota-backend-bytes: 858993459.*"))
})
})
})
var _ = AfterSuite(func() {
if !testutil.IsExistingServer() {
Expect(testutil.K3sKillServer(customEtcdArgsServer)).To(Succeed())
}
})
func Test_IntegrationCustomEtcdArgs(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t, "Custom etcd Arguments", []Reporter{
reporters.NewJUnitReporter("/tmp/results/junit-ls.xml"),
})
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment