From 47f2cb54221f24703895cbdb300956f533c91ef3 Mon Sep 17 00:00:00 2001 From: Pavel Okhlopkov Date: Tue, 3 Dec 2024 09:36:21 +0300 Subject: [PATCH 01/13] add json log handle Signed-off-by: Pavel Okhlopkov --- pkg/executor/executor.go | 7 +++++-- pkg/executor/executor_test.go | 22 ++++++++++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/pkg/executor/executor.go b/pkg/executor/executor.go index 277948de..a8a36485 100644 --- a/pkg/executor/executor.go +++ b/pkg/executor/executor.go @@ -158,7 +158,10 @@ func (pl *proxyLogger) Write(p []byte) (int, error) { return len(p), nil } - return len(p), err + pl.logger.Warn("output is not json", slog.String("error", err.Error())) + pl.writerScanner(p) + + return len(p), nil } logMap, ok := line.(map[string]interface{}) @@ -191,7 +194,7 @@ func (pl *proxyLogger) Write(p []byte) (int, error) { } // logEntry.Log(log.FatalLevel, string(logLine)) - logger.Log(context.Background(), log.LevelFatal.Level(), "hook result", slog.Any("hook", logMap)) + logger.Log(context.Background(), log.LevelFatal.Level(), "hook result", log.RawJSON("hook", logLine)) return len(p), nil } diff --git a/pkg/executor/executor_test.go b/pkg/executor/executor_test.go index 16c43c6e..37385ae7 100644 --- a/pkg/executor/executor_test.go +++ b/pkg/executor/executor_test.go @@ -108,8 +108,8 @@ func TestRunAndLogLines(t *testing.T) { assert.NoError(t, err) assert.Equal(t, buf.String(), `{"level":"debug","msg":"Executing command 'echo [\"a\",\"b\",\"c\"]' in '' dir","source":"executor/executor.go:101","a":"b","time":"2006-01-02T15:04:05Z"}`+"\n"+ - `{"level":"debug","msg":"json log line not map[string]interface{}","source":"executor/executor.go:170","a":"b","line":["a","b","c"],"output":"stdout","time":"2006-01-02T15:04:05Z"}`+"\n"+ - `{"level":"info","msg":"[\"a\",\"b\",\"c\"]\n","source":"executor/executor.go:173","a":"b","output":"stdout","time":"2006-01-02T15:04:05Z"}`+"\n") + `{"level":"debug","msg":"json log line not map[string]interface{}","source":"executor/executor.go:173","a":"b","line":["a","b","c"],"output":"stdout","time":"2006-01-02T15:04:05Z"}`+"\n"+ + `{"level":"info","msg":"[\"a\",\"b\",\"c\"]\n","source":"executor/executor.go:176","a":"b","output":"stdout","time":"2006-01-02T15:04:05Z"}`+"\n") buf.Reset() }) @@ -147,6 +147,24 @@ c d buf.Reset() }) + t.Run("multiline non json with json proxy on", func(t *testing.T) { + arg := ` +a b +c d +` + ex := NewExecutor("", "echo", []string{arg}, []string{}). + WithLogProxyHookJSON(true). + WithLogger(logger) + + _, err := ex.RunAndLogLines(map[string]string{"foor": "baar"}) + assert.NoError(t, err) + assert.Equal(t, buf.String(), `{"level":"warn","msg":"output is not json","error":"invalid character 'a' looking for beginning of value","foor":"baar","output":"stdout","time":"2006-01-02T15:04:05Z"}`+"\n"+ + `{"level":"info","msg":"a b","foor":"baar","output":"stdout","time":"2006-01-02T15:04:05Z"}`+"\n"+ + `{"level":"info","msg":"c d","foor":"baar","output":"stdout","time":"2006-01-02T15:04:05Z"}`+"\n") + + buf.Reset() + }) + t.Run("multiline json", func(t *testing.T) { arg := `{ "a":"b", From e293ce90a9a688c60250c1e05a973a3ba57c33bb Mon Sep 17 00:00:00 2001 From: Pavel Okhlopkov Date: Wed, 4 Dec 2024 17:03:49 +0300 Subject: [PATCH 02/13] log proxy hook json true Signed-off-by: Pavel Okhlopkov --- pkg/app/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/app/log.go b/pkg/app/log.go index 88bfe80e..a4ea6c6a 100644 --- a/pkg/app/log.go +++ b/pkg/app/log.go @@ -16,7 +16,7 @@ var ( LogLevel = "info" LogNoTime = false LogType = "text" - LogProxyHookJSON = false + LogProxyHookJSON = true ) // ForcedDurationForDebugLevel - force expiration for debug level. From a19873ad8128985ce48a63929f91e69598d75829 Mon Sep 17 00:00:00 2001 From: Pavel Okhlopkov Date: Wed, 4 Dec 2024 17:06:15 +0300 Subject: [PATCH 03/13] bump any Signed-off-by: Pavel Okhlopkov --- pkg/executor/executor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/executor/executor.go b/pkg/executor/executor.go index a8a36485..ca7fbc45 100644 --- a/pkg/executor/executor.go +++ b/pkg/executor/executor.go @@ -194,7 +194,7 @@ func (pl *proxyLogger) Write(p []byte) (int, error) { } // logEntry.Log(log.FatalLevel, string(logLine)) - logger.Log(context.Background(), log.LevelFatal.Level(), "hook result", log.RawJSON("hook", logLine)) + logger.Log(context.Background(), log.LevelFatal.Level(), "hook result", slog.Any("hook", logMap)) return len(p), nil } From a7578199ee92b52b94a1805787254c001ff1d957 Mon Sep 17 00:00:00 2001 From: Pavel Okhlopkov Date: Wed, 4 Dec 2024 17:09:16 +0300 Subject: [PATCH 04/13] rolback to false Signed-off-by: Pavel Okhlopkov --- pkg/app/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/app/log.go b/pkg/app/log.go index a4ea6c6a..88bfe80e 100644 --- a/pkg/app/log.go +++ b/pkg/app/log.go @@ -16,7 +16,7 @@ var ( LogLevel = "info" LogNoTime = false LogType = "text" - LogProxyHookJSON = true + LogProxyHookJSON = false ) // ForcedDurationForDebugLevel - force expiration for debug level. From 7b7bf67fc40690eb45b72013bc0eaa92d6cb32cf Mon Sep 17 00:00:00 2001 From: Pavel Okhlopkov Date: Wed, 4 Dec 2024 18:11:56 +0300 Subject: [PATCH 05/13] replace warn with debug Signed-off-by: Pavel Okhlopkov --- pkg/executor/executor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/executor/executor.go b/pkg/executor/executor.go index ca7fbc45..3bc18ea9 100644 --- a/pkg/executor/executor.go +++ b/pkg/executor/executor.go @@ -158,7 +158,7 @@ func (pl *proxyLogger) Write(p []byte) (int, error) { return len(p), nil } - pl.logger.Warn("output is not json", slog.String("error", err.Error())) + pl.logger.Debug("output is not json", slog.String("error", err.Error())) pl.writerScanner(p) return len(p), nil From 8a586cf953de7df36b677dcea253a1071763257b Mon Sep 17 00:00:00 2001 From: Pavel Okhlopkov Date: Wed, 4 Dec 2024 18:15:21 +0300 Subject: [PATCH 06/13] fix tests Signed-off-by: Pavel Okhlopkov --- pkg/executor/executor_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/executor/executor_test.go b/pkg/executor/executor_test.go index 37385ae7..4276e19d 100644 --- a/pkg/executor/executor_test.go +++ b/pkg/executor/executor_test.go @@ -158,8 +158,7 @@ c d _, err := ex.RunAndLogLines(map[string]string{"foor": "baar"}) assert.NoError(t, err) - assert.Equal(t, buf.String(), `{"level":"warn","msg":"output is not json","error":"invalid character 'a' looking for beginning of value","foor":"baar","output":"stdout","time":"2006-01-02T15:04:05Z"}`+"\n"+ - `{"level":"info","msg":"a b","foor":"baar","output":"stdout","time":"2006-01-02T15:04:05Z"}`+"\n"+ + assert.Equal(t, buf.String(), `{"level":"info","msg":"a b","foor":"baar","output":"stdout","time":"2006-01-02T15:04:05Z"}`+"\n"+ `{"level":"info","msg":"c d","foor":"baar","output":"stdout","time":"2006-01-02T15:04:05Z"}`+"\n") buf.Reset() From 0067d942f6c8fc0b0abcf113ee4cba79ef3f65d3 Mon Sep 17 00:00:00 2001 From: Pavel Okhlopkov Date: Thu, 5 Dec 2024 08:03:41 +0300 Subject: [PATCH 07/13] bump log logic Signed-off-by: Pavel Okhlopkov --- pkg/executor/executor.go | 59 ++++++++++++++++++++++++----------- pkg/executor/executor_test.go | 21 ++++++++++--- 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/pkg/executor/executor.go b/pkg/executor/executor.go index 3bc18ea9..3239aee8 100644 --- a/pkg/executor/executor.go +++ b/pkg/executor/executor.go @@ -158,7 +158,7 @@ func (pl *proxyLogger) Write(p []byte) (int, error) { return len(p), nil } - pl.logger.Debug("output is not json", slog.String("error", err.Error())) + pl.logger.Debug("output is not json", log.Err(err)) pl.writerScanner(p) return len(p), nil @@ -178,23 +178,8 @@ func (pl *proxyLogger) Write(p []byte) (int, error) { return len(p), err } - logger := pl.logger.With(pl.proxyJsonLogKey, true) - - logLineRaw, _ := json.Marshal(logMap) - logLine := string(logLineRaw) - - if len(logLine) > 10000 { - logLine = fmt.Sprintf("%s:truncated", logLine[:10000]) - - logger.Log(context.Background(), log.LevelFatal.Level(), "hook result", slog.Any("hook", map[string]any{ - "truncated": logLine, - })) - - return len(p), nil - } - // logEntry.Log(log.FatalLevel, string(logLine)) - logger.Log(context.Background(), log.LevelFatal.Level(), "hook result", slog.Any("hook", logMap)) + pl.mergeAndLogInputLog(context.TODO(), logMap, "hook") return len(p), nil } @@ -235,6 +220,44 @@ func (pl *proxyLogger) writerScanner(p []byte) { // If there was an error while scanning the input, log an error if err := scanner.Err(); err != nil { - pl.logger.Error("reading from scanner", slog.String("error", err.Error())) + pl.logger.Error("reading from scanner", log.Err(err)) } } + +func (pl *proxyLogger) mergeAndLogInputLog(ctx context.Context, inputLog map[string]interface{}, prefix string) { + var lvl log.Level + + lvlRaw, ok := inputLog[slog.LevelKey].(string) + if ok { + lvl = log.LogLevelFromStr(lvlRaw) + delete(inputLog, slog.LevelKey) + } + + msg, ok := inputLog[slog.MessageKey].(string) + if !ok { + msg = "hook result" + } + delete(inputLog, slog.MessageKey) + delete(inputLog, slog.TimeKey) + + logLineRaw, _ := json.Marshal(inputLog) + logLine := string(logLineRaw) + + logger := pl.logger.With(pl.proxyJsonLogKey, true) + + if len(logLine) > 10000 { + logLine = fmt.Sprintf("%s:truncated", logLine[:10000]) + + logger.Log(ctx, lvl.Level(), msg, slog.Any("hook", map[string]any{ + "truncated": logLine, + })) + + return + } + + for key, val := range inputLog { + logger = logger.With(slog.Any(prefix+"_"+key, val)) + } + + logger.Log(ctx, lvl.Level(), msg) +} diff --git a/pkg/executor/executor_test.go b/pkg/executor/executor_test.go index 4276e19d..3776f94f 100644 --- a/pkg/executor/executor_test.go +++ b/pkg/executor/executor_test.go @@ -39,7 +39,7 @@ func TestRunAndLogLines(t *testing.T) { _, err := ex.RunAndLogLines(map[string]string{"a": "b"}) assert.NoError(t, err) - assert.Equal(t, buf.String(), `{"level":"fatal","msg":"hook result","a":"b","hook":{"foo":"baz"},"output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"}`+"\n") + assert.Equal(t, buf.String(), `{"level":"info","msg":"hook result","a":"b","hook_foo":"baz","output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"}`+"\n") buf.Reset() }) @@ -71,7 +71,7 @@ func TestRunAndLogLines(t *testing.T) { _, err = ex.RunAndLogLines(map[string]string{"a": "b"}) assert.NoError(t, err) - reg := regexp.MustCompile(`{"level":"fatal","msg":"hook result","a":"b","hook":{"truncated":".*:truncated"},"output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"`) + reg := regexp.MustCompile(`{"level":"info","msg":"hook result","a":"b","hook":{"truncated":".*:truncated"},"output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"`) assert.Regexp(t, reg, buf.String()) buf.Reset() @@ -126,7 +126,7 @@ func TestRunAndLogLines(t *testing.T) { _, err := ex.RunAndLogLines(map[string]string{"foor": "baar"}) assert.NoError(t, err) - assert.Equal(t, buf.String(), `{"level":"fatal","msg":"hook result","foor":"baar","hook":{"a":"b","c":"d"},"output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"}`+"\n") + assert.Equal(t, buf.String(), `{"level":"info","msg":"hook result","foor":"baar","hook_a":"b","hook_c":"d","output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"}`+"\n") buf.Reset() }) @@ -175,7 +175,20 @@ c d _, err := ex.RunAndLogLines(map[string]string{"foor": "baar"}) assert.NoError(t, err) - assert.Equal(t, buf.String(), `{"level":"fatal","msg":"hook result","foor":"baar","hook":{"a":"b","c":"d"},"output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"}`+"\n") + assert.Equal(t, buf.String(), `{"level":"info","msg":"hook result","foor":"baar","hook_a":"b","hook_c":"d","output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"}`+"\n") + + buf.Reset() + }) + + t.Run("input json nest", func(t *testing.T) { + arg := `{"level":"info","msg":"hook result","foor":"baar","a":"b","c":"d","time":"2024-01-02T15:04:05Z"}` + ex := NewExecutor("", "echo", []string{arg}, []string{}). + WithLogProxyHookJSON(true). + WithLogger(logger) + + _, err := ex.RunAndLogLines(map[string]string{"foor": "baar"}) + assert.NoError(t, err) + assert.Equal(t, buf.String(), `{"level":"info","msg":"hook result","foor":"baar","hook_a":"b","hook_c":"d","hook_foor":"baar","output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"}`+"\n") buf.Reset() }) From a0c3ad80e7241bf702b38b4640395360a001a05e Mon Sep 17 00:00:00 2001 From: Pavel Okhlopkov Date: Thu, 5 Dec 2024 08:03:47 +0300 Subject: [PATCH 08/13] bump log lib Signed-off-by: Pavel Okhlopkov --- go.mod | 2 +- go.sum | 4 ++-- pkg/config/config.go | 2 +- pkg/shell-operator/bootstrap.go | 11 +++++------ pkg/webhook/admission/handler.go | 3 +-- pkg/webhook/conversion/handler.go | 3 +-- 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index d5739ada..fcd7cb4e 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/flant/shell-operator go 1.22.8 require ( - github.com/deckhouse/deckhouse/pkg/log v0.0.0-20241106140903-258b93b3334e + github.com/deckhouse/deckhouse/pkg/log v0.0.0-20241205040953-7b376bae249c github.com/flant/kube-client v1.2.2 github.com/flant/libjq-go v1.6.3-0.20201126171326-c46a40ff22ee // branch: master github.com/go-chi/chi/v5 v5.1.0 diff --git a/go.sum b/go.sum index d25d28f3..a1efe696 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckhouse/deckhouse/pkg/log v0.0.0-20241106140903-258b93b3334e h1:QUQy+5Bv7/UzhfrytiG3c5gfLGhPppepVbRpbMisVIw= -github.com/deckhouse/deckhouse/pkg/log v0.0.0-20241106140903-258b93b3334e/go.mod h1:Mk5HRzkc5pIcDIZ2JJ6DPuuqnwhXVkb3you8M8Mg+4w= +github.com/deckhouse/deckhouse/pkg/log v0.0.0-20241205040953-7b376bae249c h1:dK30IW9uGg0DvSy+IcdQ6zwEBRV55R7tEtaruEKYkSA= +github.com/deckhouse/deckhouse/pkg/log v0.0.0-20241205040953-7b376bae249c/go.mod h1:Mk5HRzkc5pIcDIZ2JJ6DPuuqnwhXVkb3you8M8Mg+4w= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= diff --git a/pkg/config/config.go b/pkg/config/config.go index bb5e493c..b39222c9 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -268,7 +268,7 @@ func (c *Config) callOnChange(name string, oldValue string, newValue string) { err := c.params[name].onChange(oldValue, newValue) if err != nil { c.logger.Error("OnChange handler failed for parameter during value change values", - slog.String("parameter", name), slog.String("old_value", oldValue), slog.String("new_value", newValue), slog.String("error", err.Error())) + slog.String("parameter", name), slog.String("old_value", oldValue), slog.String("new_value", newValue), log.Err(err)) } c.m.Lock() delete(c.errors, name) diff --git a/pkg/shell-operator/bootstrap.go b/pkg/shell-operator/bootstrap.go index 3f00f53f..24e929c2 100644 --- a/pkg/shell-operator/bootstrap.go +++ b/pkg/shell-operator/bootstrap.go @@ -3,7 +3,6 @@ package shell_operator import ( "context" "fmt" - "log/slog" "github.com/deckhouse/deckhouse/pkg/log" @@ -34,13 +33,13 @@ func Init(logger *log.Logger) (*ShellOperator, error) { hooksDir, err := utils.RequireExistingDirectory(app.HooksDir) if err != nil { - logger.Log(context.TODO(), log.LevelFatal.Level(), "hooks directory is required", slog.String("error", err.Error())) + logger.Log(context.TODO(), log.LevelFatal.Level(), "hooks directory is required", log.Err(err)) return nil, err } tempDir, err := utils.EnsureTempDirectory(app.TempDir) if err != nil { - logger.Log(context.TODO(), log.LevelFatal.Level(), "temp directory", slog.String("error", err.Error())) + logger.Log(context.TODO(), log.LevelFatal.Level(), "temp directory", log.Err(err)) return nil, err } @@ -49,7 +48,7 @@ func Init(logger *log.Logger) (*ShellOperator, error) { // Debug server. debugServer, err := RunDefaultDebugServer(app.DebugUnixSocket, app.DebugHttpServerAddr, op.logger.Named("debug-server")) if err != nil { - logger.Log(context.TODO(), log.LevelFatal.Level(), "start Debug server", slog.String("error", err.Error())) + logger.Log(context.TODO(), log.LevelFatal.Level(), "start Debug server", log.Err(err)) return nil, err } @@ -59,13 +58,13 @@ func Init(logger *log.Logger) (*ShellOperator, error) { "queue": "", }) if err != nil { - logger.Log(context.TODO(), log.LevelFatal.Level(), "essemble common operator", slog.String("error", err.Error())) + logger.Log(context.TODO(), log.LevelFatal.Level(), "essemble common operator", log.Err(err)) return nil, err } err = op.assembleShellOperator(hooksDir, tempDir, debugServer, runtimeConfig) if err != nil { - logger.Log(context.TODO(), log.LevelFatal.Level(), "essemble shell operator", slog.String("error", err.Error())) + logger.Log(context.TODO(), log.LevelFatal.Level(), "essemble shell operator", log.Err(err)) return nil, err } diff --git a/pkg/webhook/admission/handler.go b/pkg/webhook/admission/handler.go index e5a56482..e54b30b3 100644 --- a/pkg/webhook/admission/handler.go +++ b/pkg/webhook/admission/handler.go @@ -3,7 +3,6 @@ package admission import ( "encoding/json" "fmt" - "log/slog" "net/http" "strings" @@ -64,7 +63,7 @@ func (h *WebhookHandler) serveReviewRequest(w http.ResponseWriter, r *http.Reque admissionResponse, err := h.handleReviewRequest(r.URL.Path, admissionReview.Request) if err != nil { - log.Error("validation failed", "request", admissionReview.Request.UID, slog.String("error", err.Error())) + log.Error("validation failed", "request", admissionReview.Request.UID, log.Err(err)) admissionReview.Response = errored(err) } else { admissionReview.Response = admissionResponse diff --git a/pkg/webhook/conversion/handler.go b/pkg/webhook/conversion/handler.go index dcff8ae6..fe35a36d 100644 --- a/pkg/webhook/conversion/handler.go +++ b/pkg/webhook/conversion/handler.go @@ -4,7 +4,6 @@ import ( "encoding/json" "errors" "fmt" - "log/slog" "net/http" "strings" @@ -67,7 +66,7 @@ func (h *WebhookHandler) serveReviewRequest(w http.ResponseWriter, r *http.Reque conversionResponse, err := h.handleReviewRequest(crdName, convertReview.Request) if err != nil { - log.Error("failed to convert", "request", convertReview.Request.UID, slog.String("error", err.Error())) + log.Error("failed to convert", "request", convertReview.Request.UID, log.Err(err)) convertReview.Response = errored(err) } else { convertReview.Response = conversionResponse From 80919f9ca87064c766de835655fdc745d64c6d2a Mon Sep 17 00:00:00 2001 From: Pavel Okhlopkov Date: Thu, 5 Dec 2024 11:11:55 +0300 Subject: [PATCH 09/13] add comments Signed-off-by: Pavel Okhlopkov --- pkg/executor/executor.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/executor/executor.go b/pkg/executor/executor.go index 3239aee8..f40d6467 100644 --- a/pkg/executor/executor.go +++ b/pkg/executor/executor.go @@ -224,6 +224,11 @@ func (pl *proxyLogger) writerScanner(p []byte) { } } +// level = level +// msg = msg +// prefix for all fields hook_ +// source = hook_source +// stacktrace = hook_stacktrace func (pl *proxyLogger) mergeAndLogInputLog(ctx context.Context, inputLog map[string]interface{}, prefix string) { var lvl log.Level From 7cbd7861e71f0042def22efd9149bab6fcd294a0 Mon Sep 17 00:00:00 2001 From: Pavel Okhlopkov Date: Thu, 5 Dec 2024 16:05:36 +0300 Subject: [PATCH 10/13] add verbosity Signed-off-by: Pavel Okhlopkov --- pkg/executor/executor.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/executor/executor.go b/pkg/executor/executor.go index f40d6467..040c6f5a 100644 --- a/pkg/executor/executor.go +++ b/pkg/executor/executor.go @@ -108,10 +108,10 @@ func (e *Executor) RunAndLogLines(logLabels map[string]string) (*CmdUsage, error err := e.cmd.Run() if err != nil { if len(stdErr.Bytes()) > 0 { - return nil, fmt.Errorf("%s", stdErr.String()) + return nil, fmt.Errorf("stderr: %s", stdErr.String()) } - return nil, err + return nil, fmt.Errorf("cmd run: %w", err) } var usage *CmdUsage @@ -129,7 +129,7 @@ func (e *Executor) RunAndLogLines(logLabels map[string]string) (*CmdUsage, error } } - return usage, err + return usage, nil } type proxyLogger struct { @@ -175,7 +175,7 @@ func (pl *proxyLogger) Write(p []byte) (int, error) { // fall back to using the logger pl.logger.Info(string(p)) - return len(p), err + return len(p), nil } // logEntry.Log(log.FatalLevel, string(logLine)) From 44049043e3a475fef25e674a699276fa17a09e5e Mon Sep 17 00:00:00 2001 From: Pavel Okhlopkov Date: Fri, 6 Dec 2024 13:11:00 +0300 Subject: [PATCH 11/13] bump Signed-off-by: Pavel Okhlopkov --- go.mod | 5 +- go.sum | 2 + pkg/app/app_config.go | 266 ++++++++++++++++++++++++++++++++++++++++++ pkg/app/debug.go | 6 +- 4 files changed, 273 insertions(+), 6 deletions(-) create mode 100644 pkg/app/app_config.go diff --git a/go.mod b/go.mod index fcd7cb4e..2d446f2b 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,10 @@ require ( // Remove 'in body' from errors, fix for Go 1.16 (https://github.com/go-openapi/validate/pull/138). replace github.com/go-openapi/validate => github.com/flant/go-openapi-validate v0.19.12-flant.0 -require github.com/gojuno/minimock/v3 v3.4.1 +require ( + github.com/caarlos0/env/v11 v11.2.2 + github.com/gojuno/minimock/v3 v3.4.1 +) require ( github.com/DataDog/gostackparse v0.7.0 // indirect diff --git a/go.sum b/go.sum index a1efe696..70ea202d 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:o github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/caarlos0/env/v11 v11.2.2 h1:95fApNrUyueipoZN/EhA8mMxiNxrBwDa+oAZrMWl3Kg= +github.com/caarlos0/env/v11 v11.2.2/go.mod h1:JBfcdeQiBoI3Zh1QRAWfe+tpiNTmDtcCj/hHHHMx0vc= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= diff --git a/pkg/app/app_config.go b/pkg/app/app_config.go new file mode 100644 index 00000000..2bfe0e7c --- /dev/null +++ b/pkg/app/app_config.go @@ -0,0 +1,266 @@ +package app + +import ( + "fmt" + "time" + + env "github.com/caarlos0/env/v11" + + "github.com/deckhouse/deckhouse/pkg/log" +) + +type appConfig struct { + HooksDir string `env:"HOOKS_DIR"` + TmpDir string `env:"TMP_DIR"` + ListenAddress string `env:"LISTEN_ADDRESS"` + ListenPort string `env:"LISTEN_PORT"` + PrometheusMetricsPrefix string `env:"PROMETHEUS_METRICS_PREFIX"` + // unused? + HooksMetricsListenPort string `env:"HOOK_METRICS_LISTEN_PORT"` + Namespace string `env:"NAMESPACE"` +} + +func newAppConfig() *appConfig { + return &appConfig{} +} + +type debugConfig struct { + HTTPServerAddress string `env:"HTTP_SERVER_ADDR"` + KeepTemporaryFiles string `env:"KEEP_TMP_FILES"` + KubernetesAPI bool `env:"KUBERNETES_API"` + UnixSocket string `env:"UNIX_SOCKET"` +} + +func newDebugConfig() *debugConfig { + return &debugConfig{} +} + +type jqConfig struct { + LibraryPath string `env:"LIBRARY_PATH"` +} + +func newJQConfig() *jqConfig { + return &jqConfig{} +} + +type kubeConfig struct { + // Settings for Kubernetes connection. + ContextName string `env:"CONTEXT"` + ConfigPath string `env:"CONFIG"` + ServerAddress string `env:"SERVER"` + // Rate limit settings for 'main' kube client + ClientQPS float32 `env:"CLIENT_QPS"` + ClientBurst int `env:"CLIENT_BURST"` +} + +func newKubeConfig() *kubeConfig { + return &kubeConfig{} +} + +type objectPatcherConfig struct { + // Settings for 'object_patcher' kube client + KubeClientQPS float32 `env:"KUBE_CLIENT_QPS"` + KubeClisntBurst int `env:"KUBE_CLIENT_BURST"` + KubeClientTimeout time.Duration `env:"KUBE_CLIENT_TIMEOUT"` +} + +func newObjectPatcherConfig() *objectPatcherConfig { + return &objectPatcherConfig{} +} + +type validatingWebhookConfig struct { + ConfigurationName string `env:"CONFIGURATION_NAME"` + ServiceName string `env:"SERVICE_NAME"` + ServerCert string `env:"SERVER_CERT"` + ServerKey string `env:"SERVER_KEY"` + CA string `env:"CA"` + // check separator? + ClientCA []string `env:"CLIENT_CA" envSeparator:","` + // enum "Fail" || "Ignore" + FailurePolicy string `env:"FAILURE_POLICY"` + ListenPort string `env:"LISTEN_PORT"` + ListenAddress string `env:"LISTEN_ADDRESS"` +} + +func newValidatingWebhookConfig() *validatingWebhookConfig { + return &validatingWebhookConfig{} +} + +type conversionWebhookConfig struct { + ServiceName string `env:"SERVICE_NAME"` + ServerCert string `env:"SERVER_CERT"` + ServerKey string `env:"SERVER_KEY"` + CA string `env:"CA"` + // check separator? + ClientCA []string `env:"CLIENT_CA" envSeparator:","` + ListenPort string `env:"LISTEN_PORT"` + ListenAddress string `env:"LISTEN_ADDRESS"` +} + +func newConversionWebhookConfig() *conversionWebhookConfig { + return &conversionWebhookConfig{} +} + +type logConfig struct { + Level string `env:"LEVEL"` + Type string `env:"TYPE"` + NoTime bool `env:"NO_TIME"` + ProxyHookJson bool `env:"PROXY_HOOK_JSON"` +} + +func newLogConfig() *logConfig { + return &logConfig{} +} + +type Config struct { + AppConfig *appConfig `envPrefix:"SHELL_OPERATOR_"` + JQConfig *jqConfig `envPrefix:"JQ_"` + KubeConfig *kubeConfig `envPrefix:"KUBE_"` + ObjectPatcherConfig *objectPatcherConfig `envPrefix:"OBJECT_PATCHER_"` + ValidatingWebhookConfig *validatingWebhookConfig `envPrefix:"VALIDATING_WEBHOOK_"` + ConversionWebhookConfig *conversionWebhookConfig `envPrefix:"CONVERSION_WEBHOOK_"` + + DebugConfig *debugConfig `envPrefix:"DEBUG_"` + + LogConfig *logConfig `envPrefix:"LOG_"` + LogLevel log.Level `env:"-"` + + ready bool +} + +func NewConfig() *Config { + return &Config{ + AppConfig: newAppConfig(), + JQConfig: newJQConfig(), + KubeConfig: newKubeConfig(), + ObjectPatcherConfig: newObjectPatcherConfig(), + ValidatingWebhookConfig: newValidatingWebhookConfig(), + ConversionWebhookConfig: newConversionWebhookConfig(), + DebugConfig: newDebugConfig(), + LogConfig: newLogConfig(), + } +} + +func (cfg *Config) Parse() error { + if cfg.IsReady() { + return nil + } + + opts := env.Options{ + Prefix: "", + } + + err := env.ParseWithOptions(cfg, opts) + if err != nil { + return fmt.Errorf("failed to parse config: %w", err) + } + + cfg.LogLevel = log.LogLevelFromStr(cfg.LogConfig.Level) + + return nil +} + +func (cfg *Config) SetupGlobalVars() { + if cfg.IsReady() { + return + } + + setIfNotEmpty(&HooksDir, cfg.AppConfig.HooksDir) + setIfNotEmpty(&TempDir, cfg.AppConfig.TmpDir) + setIfNotEmpty(&ListenAddress, cfg.AppConfig.ListenAddress) + setIfNotEmpty(&ListenPort, cfg.AppConfig.ListenPort) + setIfNotEmpty(&PrometheusMetricsPrefix, cfg.AppConfig.PrometheusMetricsPrefix) + setIfNotEmpty(&Namespace, cfg.AppConfig.Namespace) + + setIfNotEmpty(&DebugHttpServerAddr, cfg.DebugConfig.HTTPServerAddress) + setIfNotEmpty(&DebugKeepTmpFilesVar, cfg.DebugConfig.KeepTemporaryFiles) + setIfNotEmpty(&DebugKeepTmpFiles, cfg.DebugConfig.KeepTemporaryFiles == "true" || cfg.DebugConfig.KeepTemporaryFiles == "yes") + setIfNotEmpty(&DebugKubernetesAPI, cfg.DebugConfig.KubernetesAPI) + setIfNotEmpty(&DebugUnixSocket, cfg.DebugConfig.UnixSocket) + + setIfNotEmpty(&JqLibraryPath, cfg.JQConfig.LibraryPath) + + setIfNotEmpty(&KubeContext, cfg.KubeConfig.ContextName) + setIfNotEmpty(&KubeConfig, cfg.KubeConfig.ConfigPath) + setIfNotEmpty(&KubeServer, cfg.KubeConfig.ServerAddress) + setIfNotEmpty(&KubeClientQps, cfg.KubeConfig.ClientQPS) + setIfNotEmpty(&KubeClientBurst, cfg.KubeConfig.ClientBurst) + + setIfNotEmpty(&ObjectPatcherKubeClientQps, cfg.ObjectPatcherConfig.KubeClientQPS) + setIfNotEmpty(&ObjectPatcherKubeClientBurst, cfg.ObjectPatcherConfig.KubeClisntBurst) + setIfNotEmpty(&ObjectPatcherKubeClientTimeout, cfg.ObjectPatcherConfig.KubeClientTimeout) + + setIfNotEmpty(&LogLevel, cfg.LogConfig.Level) + setIfNotEmpty(&LogNoTime, cfg.LogConfig.NoTime) + setIfNotEmpty(&LogType, cfg.LogConfig.Type) + setIfNotEmpty(&LogProxyHookJSON, cfg.LogConfig.ProxyHookJson) + + setIfNotEmpty(&ValidatingWebhookSettings.ConfigurationName, cfg.ValidatingWebhookConfig.ConfigurationName) + setIfNotEmpty(&ValidatingWebhookSettings.ServiceName, cfg.ValidatingWebhookConfig.ServiceName) + setIfNotEmpty(&ValidatingWebhookSettings.ServerCertPath, cfg.ValidatingWebhookConfig.ServerCert) + setIfNotEmpty(&ValidatingWebhookSettings.ServerKeyPath, cfg.ValidatingWebhookConfig.ServerKey) + setIfNotEmpty(&ValidatingWebhookSettings.CAPath, cfg.ValidatingWebhookConfig.CA) + setSliceIfNotEmpty(&ValidatingWebhookSettings.ClientCAPaths, cfg.ValidatingWebhookConfig.ClientCA) + setIfNotEmpty(&ValidatingWebhookSettings.DefaultFailurePolicy, cfg.ValidatingWebhookConfig.FailurePolicy) + setIfNotEmpty(&ValidatingWebhookSettings.ListenPort, cfg.ValidatingWebhookConfig.ListenPort) + setIfNotEmpty(&ValidatingWebhookSettings.ListenAddr, cfg.ValidatingWebhookConfig.ListenAddress) + + setIfNotEmpty(&ConversionWebhookSettings.ServiceName, cfg.ValidatingWebhookConfig.ServiceName) + setIfNotEmpty(&ConversionWebhookSettings.ServerCertPath, cfg.ValidatingWebhookConfig.ServerCert) + setIfNotEmpty(&ConversionWebhookSettings.ServerKeyPath, cfg.ValidatingWebhookConfig.ServerKey) + setIfNotEmpty(&ConversionWebhookSettings.CAPath, cfg.ValidatingWebhookConfig.CA) + setSliceIfNotEmpty(&ConversionWebhookSettings.ClientCAPaths, cfg.ValidatingWebhookConfig.ClientCA) + setIfNotEmpty(&ConversionWebhookSettings.ListenPort, cfg.ValidatingWebhookConfig.ListenPort) + setIfNotEmpty(&ConversionWebhookSettings.ListenAddr, cfg.ValidatingWebhookConfig.ListenAddress) +} + +func (cfg *Config) IsReady() bool { + return cfg.ready +} + +func (cfg *Config) SetReady() { + cfg.ready = true +} + +var configInstance *Config + +func MustGetConfig() *Config { + cfg, err := GetConfig() + if err != nil { + panic(err) + } + + return cfg +} + +func GetConfig() (*Config, error) { + if configInstance != nil { + return configInstance, nil + } + + cfg := NewConfig() + err := cfg.Parse() + if err != nil { + return nil, err + } + + configInstance = cfg + + return configInstance, nil +} + +func setIfNotEmpty[T comparable](v *T, env T) { + if !isZero(env) { + *v = env + } +} + +func setSliceIfNotEmpty[T any](v *[]T, env []T) { + if len(env) != 0 { + *v = env + } +} + +func isZero[T comparable](v T) bool { + return v == *new(T) +} diff --git a/pkg/app/debug.go b/pkg/app/debug.go index 19860a08..434300a3 100644 --- a/pkg/app/debug.go +++ b/pkg/app/debug.go @@ -30,11 +30,7 @@ func DefineDebugFlags(kpApp *kingpin.Application, cmd *kingpin.CmdClause) { cmd.Flag("debug-keep-tmp-files", "set to yes to disable cleanup of temporary files"). Envar("DEBUG_KEEP_TMP_FILES"). Hidden(). - Default(DebugKeepTmpFilesVar).Action(func(_ *kingpin.ParseContext) error { - DebugKeepTmpFiles = DebugKeepTmpFilesVar == "yes" - - return nil - }). + Default(DebugKeepTmpFilesVar). StringVar(&DebugKeepTmpFilesVar) cmd.Flag("debug-kubernetes-api", "enable client-go debug messages"). From bb385fb728b80ede2c8398ee9910ca6d4c661f92 Mon Sep 17 00:00:00 2001 From: Pavel Okhlopkov Date: Mon, 1 Sep 2025 12:29:13 +0300 Subject: [PATCH 12/13] bump Signed-off-by: Pavel Okhlopkov --- pkg/app/app_config.go | 12 ------------ pkg/webhook/admission/handler.go | 1 + pkg/webhook/conversion/handler.go | 1 + 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/pkg/app/app_config.go b/pkg/app/app_config.go index 2bfe0e7c..ab641088 100644 --- a/pkg/app/app_config.go +++ b/pkg/app/app_config.go @@ -35,14 +35,6 @@ func newDebugConfig() *debugConfig { return &debugConfig{} } -type jqConfig struct { - LibraryPath string `env:"LIBRARY_PATH"` -} - -func newJQConfig() *jqConfig { - return &jqConfig{} -} - type kubeConfig struct { // Settings for Kubernetes connection. ContextName string `env:"CONTEXT"` @@ -114,7 +106,6 @@ func newLogConfig() *logConfig { type Config struct { AppConfig *appConfig `envPrefix:"SHELL_OPERATOR_"` - JQConfig *jqConfig `envPrefix:"JQ_"` KubeConfig *kubeConfig `envPrefix:"KUBE_"` ObjectPatcherConfig *objectPatcherConfig `envPrefix:"OBJECT_PATCHER_"` ValidatingWebhookConfig *validatingWebhookConfig `envPrefix:"VALIDATING_WEBHOOK_"` @@ -131,7 +122,6 @@ type Config struct { func NewConfig() *Config { return &Config{ AppConfig: newAppConfig(), - JQConfig: newJQConfig(), KubeConfig: newKubeConfig(), ObjectPatcherConfig: newObjectPatcherConfig(), ValidatingWebhookConfig: newValidatingWebhookConfig(), @@ -178,8 +168,6 @@ func (cfg *Config) SetupGlobalVars() { setIfNotEmpty(&DebugKubernetesAPI, cfg.DebugConfig.KubernetesAPI) setIfNotEmpty(&DebugUnixSocket, cfg.DebugConfig.UnixSocket) - setIfNotEmpty(&JqLibraryPath, cfg.JQConfig.LibraryPath) - setIfNotEmpty(&KubeContext, cfg.KubeConfig.ContextName) setIfNotEmpty(&KubeConfig, cfg.KubeConfig.ConfigPath) setIfNotEmpty(&KubeServer, cfg.KubeConfig.ServerAddress) diff --git a/pkg/webhook/admission/handler.go b/pkg/webhook/admission/handler.go index 3ffdf7b0..64409889 100644 --- a/pkg/webhook/admission/handler.go +++ b/pkg/webhook/admission/handler.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "net/http" "strings" diff --git a/pkg/webhook/conversion/handler.go b/pkg/webhook/conversion/handler.go index 67393911..f44e9f68 100644 --- a/pkg/webhook/conversion/handler.go +++ b/pkg/webhook/conversion/handler.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "net/http" "strings" From 59a9657474086cf98d6d002a4023a10818e0edda Mon Sep 17 00:00:00 2001 From: Pavel Okhlopkov Date: Fri, 10 Apr 2026 11:19:37 +0300 Subject: [PATCH 13/13] fix Signed-off-by: Pavel Okhlopkov --- pkg/app/app_config.go | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/pkg/app/app_config.go b/pkg/app/app_config.go index ab641088..95ecacb6 100644 --- a/pkg/app/app_config.go +++ b/pkg/app/app_config.go @@ -163,7 +163,6 @@ func (cfg *Config) SetupGlobalVars() { setIfNotEmpty(&Namespace, cfg.AppConfig.Namespace) setIfNotEmpty(&DebugHttpServerAddr, cfg.DebugConfig.HTTPServerAddress) - setIfNotEmpty(&DebugKeepTmpFilesVar, cfg.DebugConfig.KeepTemporaryFiles) setIfNotEmpty(&DebugKeepTmpFiles, cfg.DebugConfig.KeepTemporaryFiles == "true" || cfg.DebugConfig.KeepTemporaryFiles == "yes") setIfNotEmpty(&DebugKubernetesAPI, cfg.DebugConfig.KubernetesAPI) setIfNotEmpty(&DebugUnixSocket, cfg.DebugConfig.UnixSocket) @@ -183,23 +182,23 @@ func (cfg *Config) SetupGlobalVars() { setIfNotEmpty(&LogType, cfg.LogConfig.Type) setIfNotEmpty(&LogProxyHookJSON, cfg.LogConfig.ProxyHookJson) - setIfNotEmpty(&ValidatingWebhookSettings.ConfigurationName, cfg.ValidatingWebhookConfig.ConfigurationName) - setIfNotEmpty(&ValidatingWebhookSettings.ServiceName, cfg.ValidatingWebhookConfig.ServiceName) - setIfNotEmpty(&ValidatingWebhookSettings.ServerCertPath, cfg.ValidatingWebhookConfig.ServerCert) - setIfNotEmpty(&ValidatingWebhookSettings.ServerKeyPath, cfg.ValidatingWebhookConfig.ServerKey) - setIfNotEmpty(&ValidatingWebhookSettings.CAPath, cfg.ValidatingWebhookConfig.CA) - setSliceIfNotEmpty(&ValidatingWebhookSettings.ClientCAPaths, cfg.ValidatingWebhookConfig.ClientCA) - setIfNotEmpty(&ValidatingWebhookSettings.DefaultFailurePolicy, cfg.ValidatingWebhookConfig.FailurePolicy) - setIfNotEmpty(&ValidatingWebhookSettings.ListenPort, cfg.ValidatingWebhookConfig.ListenPort) - setIfNotEmpty(&ValidatingWebhookSettings.ListenAddr, cfg.ValidatingWebhookConfig.ListenAddress) - - setIfNotEmpty(&ConversionWebhookSettings.ServiceName, cfg.ValidatingWebhookConfig.ServiceName) - setIfNotEmpty(&ConversionWebhookSettings.ServerCertPath, cfg.ValidatingWebhookConfig.ServerCert) - setIfNotEmpty(&ConversionWebhookSettings.ServerKeyPath, cfg.ValidatingWebhookConfig.ServerKey) - setIfNotEmpty(&ConversionWebhookSettings.CAPath, cfg.ValidatingWebhookConfig.CA) - setSliceIfNotEmpty(&ConversionWebhookSettings.ClientCAPaths, cfg.ValidatingWebhookConfig.ClientCA) - setIfNotEmpty(&ConversionWebhookSettings.ListenPort, cfg.ValidatingWebhookConfig.ListenPort) - setIfNotEmpty(&ConversionWebhookSettings.ListenAddr, cfg.ValidatingWebhookConfig.ListenAddress) + setIfNotEmpty(&ValidatingWebhookConfigurationName, cfg.ValidatingWebhookConfig.ConfigurationName) + setIfNotEmpty(&ValidatingWebhookServiceName, cfg.ValidatingWebhookConfig.ServiceName) + setIfNotEmpty(&ValidatingWebhookServerCert, cfg.ValidatingWebhookConfig.ServerCert) + setIfNotEmpty(&ValidatingWebhookServerKey, cfg.ValidatingWebhookConfig.ServerKey) + setIfNotEmpty(&ValidatingWebhookCA, cfg.ValidatingWebhookConfig.CA) + setSliceIfNotEmpty(&ValidatingWebhookClientCA, cfg.ValidatingWebhookConfig.ClientCA) + setIfNotEmpty(&ValidatingWebhookFailurePolicy, cfg.ValidatingWebhookConfig.FailurePolicy) + setIfNotEmpty(&ValidatingWebhookListenPort, cfg.ValidatingWebhookConfig.ListenPort) + setIfNotEmpty(&ValidatingWebhookListenAddress, cfg.ValidatingWebhookConfig.ListenAddress) + + setIfNotEmpty(&ConversionWebhookServiceName, cfg.ConversionWebhookConfig.ServiceName) + setIfNotEmpty(&ConversionWebhookServerCert, cfg.ConversionWebhookConfig.ServerCert) + setIfNotEmpty(&ConversionWebhookServerKey, cfg.ConversionWebhookConfig.ServerKey) + setIfNotEmpty(&ConversionWebhookCA, cfg.ConversionWebhookConfig.CA) + setSliceIfNotEmpty(&ConversionWebhookClientCA, cfg.ConversionWebhookConfig.ClientCA) + setIfNotEmpty(&ConversionWebhookListenPort, cfg.ConversionWebhookConfig.ListenPort) + setIfNotEmpty(&ConversionWebhookListenAddress, cfg.ConversionWebhookConfig.ListenAddress) } func (cfg *Config) IsReady() bool {