Enable ability to flip configuration of allownomutate

This commit is contained in:
Hyatt 2023-03-24 09:38:00 -05:00
parent 80ef925ff4
commit 8094816911
Signed by: nhyatt
GPG Key ID: C50D0BBB5BC40BEA
9 changed files with 50 additions and 57 deletions

View File

@ -37,7 +37,7 @@ func strictTransport(w http.ResponseWriter) {
w.Header().Add("Strict-Transport-Security", "max-age=63072000") w.Header().Add("Strict-Transport-Security", "max-age=63072000")
} }
func httpServer(cfg *config.Config) { func httpServer() {
var serverCertificate tls.Certificate var serverCertificate tls.Certificate
if config.DefaultConfig().WebServerCertificate == "" || cfg.WebServerKey == "" { if config.DefaultConfig().WebServerCertificate == "" || cfg.WebServerKey == "" {
log.Printf("[INFO] No webserver certificate configured, automatically generating self signed certificate.") log.Printf("[INFO] No webserver certificate configured, automatically generating self signed certificate.")
@ -73,11 +73,7 @@ func httpServer(cfg *config.Config) {
ah := &admissionHandler{ ah := &admissionHandler{
decoder: serializer.NewCodecFactory(runtime.NewScheme()).UniversalDeserializer(), decoder: serializer.NewCodecFactory(runtime.NewScheme()).UniversalDeserializer(),
config: cfg, config: &cfg,
}
wh := &webHandler{
config: cfg,
} }
// pod admission // pod admission
@ -87,18 +83,14 @@ func httpServer(cfg *config.Config) {
// pod mutation // pod mutation
path.HandleFunc("/api/v1/mutate/pod", ah.ahServe(operations.PodsMutation())) path.HandleFunc("/api/v1/mutate/pod", ah.ahServe(operations.PodsMutation()))
// web root // web root
path.HandleFunc("/", wh.webServe()) path.HandleFunc("/", webServe())
if err := connection.ListenAndServeTLS("", ""); err != nil { if err := connection.ListenAndServeTLS("", ""); err != nil {
log.Fatalf("[ERROR] %s\n", err) log.Fatalf("[ERROR] %s\n", err)
} }
} }
type webHandler struct { func webServe() http.HandlerFunc {
config *config.Config
}
func (h *webHandler) webServe() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
httpAccessLog(r) httpAccessLog(r)
crossSiteOrigin(w) crossSiteOrigin(w)
@ -112,7 +104,7 @@ func (h *webHandler) webServe() http.HandlerFunc {
case r.URL.Path == "/healthcheck": case r.URL.Path == "/healthcheck":
tmpltHealthCheck(w) tmpltHealthCheck(w)
case r.URL.Path == "/": case r.URL.Path == "/":
tmpltWebRoot(w, r.URL.Query(), *h.config) tmpltWebRoot(w, r.URL.Query())
default: default:
msg := fmt.Sprintf("Unable to locate requested path: '%s'", r.URL.Path) msg := fmt.Sprintf("Unable to locate requested path: '%s'", r.URL.Path)
log.Printf("[DEBUG] %s", msg) log.Printf("[DEBUG] %s", msg)
@ -170,7 +162,7 @@ func (h *admissionHandler) ahServe(hook operations.Hook) http.HandlerFunc {
return return
} }
result, err := hook.Execute(review.Request, h.config) result, err := hook.Execute(review.Request, &cfg)
if err != nil { if err != nil {
msg := err.Error() msg := err.Error()
log.Printf("[ERROR] Internal Server Error: %s", msg) log.Printf("[ERROR] Internal Server Error: %s", msg)

View File

@ -7,8 +7,6 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"net/url" "net/url"
"mutating-webhook/internal/config"
) )
const cT string = "Content-Type" const cT string = "Content-Type"
@ -56,7 +54,7 @@ func tmpltHealthCheck(w http.ResponseWriter) {
w.Write(output) //nolint:errcheck w.Write(output) //nolint:errcheck
} }
func tmpltWebRoot(w http.ResponseWriter, urlPrams url.Values, cfg config.Config) { func tmpltWebRoot(w http.ResponseWriter, urlPrams url.Values) {
o := struct { o := struct {
Application string `json:"application" yaml:"application"` Application string `json:"application" yaml:"application"`
Description string `json:"description" yaml:"description"` Description string `json:"description" yaml:"description"`

View File

@ -10,6 +10,9 @@ import (
"mutating-webhook/internal/config" "mutating-webhook/internal/config"
) )
// global configuration
var cfg config.Config
func forever() { func forever() {
c := make(chan os.Signal, 1) c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) signal.Notify(c, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
@ -24,9 +27,9 @@ func main() {
}() }()
// initialize application configuration // initialize application configuration
cfg := config.Init() cfg = config.Init()
go httpServer(cfg) go httpServer()
forever() forever()
} }

View File

@ -41,8 +41,8 @@ type Config struct {
} }
// DefaultConfig initializes the config variable for use with a prepared set of defaults. // DefaultConfig initializes the config variable for use with a prepared set of defaults.
func DefaultConfig() *Config { func DefaultConfig() Config {
return &Config{ return Config{
Log: &logutils.LevelFilter{ Log: &logutils.LevelFilter{
Levels: []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARNING", "ERROR"}, Levels: []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARNING", "ERROR"},
Writer: os.Stderr, Writer: os.Stderr,
@ -50,7 +50,7 @@ func DefaultConfig() *Config {
} }
} }
func (cfg *Config) setLogLevel() { func setLogLevel(cfg Config) {
switch { switch {
case cfg.LogLevel <= 20: case cfg.LogLevel <= 20:
cfg.Log.SetMinLevel(logutils.LogLevel("ERROR")) cfg.Log.SetMinLevel(logutils.LogLevel("ERROR"))
@ -66,7 +66,7 @@ func (cfg *Config) setLogLevel() {
log.SetOutput(cfg.Log) log.SetOutput(cfg.Log)
} }
func (cfg *Config) printRunningConfig(cfgInfo []StructInfo) { func printRunningConfig(cfg *Config, cfgInfo []StructInfo) {
log.Printf("[DEBUG] Current Running Configuration Values:") log.Printf("[DEBUG] Current Running Configuration Values:")
for _, info := range cfgInfo { for _, info := range cfgInfo {
switch info.Type.String() { switch info.Type.String() {

View File

@ -19,10 +19,10 @@ func getOSEnv(env, def string) string {
// Init initializes the application configuration by reading default values from the struct's tags // Init initializes the application configuration by reading default values from the struct's tags
// and environment variables. Tags processed by this process are as follows: // and environment variables. Tags processed by this process are as follows:
// `ignored:"true" env:"ENVIRONMENT_VARIABLE" default:"default value"` // `ignored:"true" env:"ENVIRONMENT_VARIABLE" default:"default value"`
func Init() *Config { func Init() Config {
cfg := DefaultConfig() cfg := DefaultConfig()
cfgInfo, err := getStructInfo(cfg) cfgInfo, err := getStructInfo(&cfg)
if err != nil { if err != nil {
log.Fatalf("[FATAL] %v", err) log.Fatalf("[FATAL] %v", err)
} }
@ -34,7 +34,7 @@ func Init() *Config {
if info.DefaultValue != nil { if info.DefaultValue != nil {
dv = info.DefaultValue.(string) dv = info.DefaultValue.(string)
} }
p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*string) p := reflect.ValueOf(&cfg).Elem().FieldByName(info.Name).Addr().Interface().(*string)
flag.StringVar(p, info.Name, dv, "("+info.Key+")") flag.StringVar(p, info.Name, dv, "("+info.Key+")")
case "bool": case "bool":
@ -42,7 +42,7 @@ func Init() *Config {
if info.DefaultValue != nil { if info.DefaultValue != nil {
dv = info.DefaultValue.(bool) dv = info.DefaultValue.(bool)
} }
p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*bool) p := reflect.ValueOf(&cfg).Elem().FieldByName(info.Name).Addr().Interface().(*bool)
flag.BoolVar(p, info.Name, dv, "("+info.Key+")") flag.BoolVar(p, info.Name, dv, "("+info.Key+")")
case "int": case "int":
@ -50,14 +50,14 @@ func Init() *Config {
if info.DefaultValue != nil { if info.DefaultValue != nil {
dv = int(info.DefaultValue.(int64)) dv = int(info.DefaultValue.(int64))
} }
p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*int) p := reflect.ValueOf(&cfg).Elem().FieldByName(info.Name).Addr().Interface().(*int)
flag.IntVar(p, info.Name, dv, "("+info.Key+")") flag.IntVar(p, info.Name, dv, "("+info.Key+")")
} }
} }
flag.Parse() flag.Parse()
// set logging level // set logging level
cfg.setLogLevel() setLogLevel(cfg)
// timezone & format configuration // timezone & format configuration
cfg.TZoneUTC, _ = time.LoadLocation("UTC") cfg.TZoneUTC, _ = time.LoadLocation("UTC")
@ -71,7 +71,7 @@ func Init() *Config {
time.Now().Format(cfg.TimeFormat) time.Now().Format(cfg.TimeFormat)
// print running config // print running config
cfg.printRunningConfig(cfgInfo) printRunningConfig(&cfg, cfgInfo)
// read config file // read config file
configFileData, err := getConfigFileData(cfg.ConfigFile) configFileData, err := getConfigFileData(cfg.ConfigFile)

View File

@ -1,24 +1,24 @@
package operations package operations
import ( import (
"mutating-webhook/internal/config"
admission "k8s.io/api/admission/v1" admission "k8s.io/api/admission/v1"
"mutating-webhook/internal/config"
) )
func DeploymentsValidation() Hook { func DeploymentsValidation() Hook {
return Hook{ return Hook{
// default allow // default allow
Create: func(r *admission.AdmissionRequest, cfg config.Config) (*Result, error) { Create: func(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
return &Result{Allowed: true}, nil return &Result{Allowed: true}, nil
}, },
Delete: func(r *admission.AdmissionRequest, cfg config.Config) (*Result, error) { Delete: func(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
return &Result{Allowed: true}, nil return &Result{Allowed: true}, nil
}, },
Update: func(r *admission.AdmissionRequest, cfg config.Config) (*Result, error) { Update: func(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
return &Result{Allowed: true}, nil return &Result{Allowed: true}, nil
}, },
Connect: func(r *admission.AdmissionRequest, cfg config.Config) (*Result, error) { Connect: func(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
return &Result{Allowed: true}, nil return &Result{Allowed: true}, nil
}, },
} }

View File

@ -5,9 +5,9 @@ package operations
import ( import (
"fmt" "fmt"
"mutating-webhook/internal/config"
admission "k8s.io/api/admission/v1" admission "k8s.io/api/admission/v1"
"mutating-webhook/internal/config"
) )
// Result contains the result of an admission request // Result contains the result of an admission request
@ -18,7 +18,7 @@ type Result struct {
} }
// AdmitFunc defines how to process an admission request // AdmitFunc defines how to process an admission request
type AdmitFunc func(request *admission.AdmissionRequest, cfg config.Config) (*Result, error) type AdmitFunc func(request *admission.AdmissionRequest, cfg *config.Config) (*Result, error)
// Hook represents the set of functions for each operation in an admission webhook. // Hook represents the set of functions for each operation in an admission webhook.
type Hook struct { type Hook struct {
@ -32,19 +32,19 @@ type Hook struct {
func (h *Hook) Execute(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) { func (h *Hook) Execute(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
switch r.Operation { switch r.Operation {
case admission.Create: case admission.Create:
return wrapperExecution(h.Create, r, *cfg) return wrapperExecution(h.Create, r, cfg)
case admission.Update: case admission.Update:
return wrapperExecution(h.Update, r, *cfg) return wrapperExecution(h.Update, r, cfg)
case admission.Delete: case admission.Delete:
return wrapperExecution(h.Delete, r, *cfg) return wrapperExecution(h.Delete, r, cfg)
case admission.Connect: case admission.Connect:
return wrapperExecution(h.Connect, r, *cfg) return wrapperExecution(h.Connect, r, cfg)
} }
return &Result{Msg: fmt.Sprintf("Invalid operation: %s", r.Operation)}, nil return &Result{Msg: fmt.Sprintf("Invalid operation: %s", r.Operation)}, nil
} }
func wrapperExecution(fn AdmitFunc, r *admission.AdmissionRequest, cfg config.Config) (*Result, error) { func wrapperExecution(fn AdmitFunc, r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
if fn == nil { if fn == nil {
return nil, fmt.Errorf("operation %s is not registered", r.Operation) return nil, fmt.Errorf("operation %s is not registered", r.Operation)
} }

View File

@ -3,30 +3,30 @@ package operations
import ( import (
"fmt" "fmt"
"mutating-webhook/internal/config"
admission "k8s.io/api/admission/v1" admission "k8s.io/api/admission/v1"
core "k8s.io/api/core/v1" core "k8s.io/api/core/v1"
"mutating-webhook/internal/config"
) )
func PodsMutation() Hook { func PodsMutation() Hook {
return Hook{ return Hook{
Create: podMutationCreate(), Create: podMutationCreate(),
// default allow // default allow
Delete: func(r *admission.AdmissionRequest, cfg config.Config) (*Result, error) { Delete: func(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
return &Result{Allowed: true}, nil return &Result{Allowed: true}, nil
}, },
Update: func(r *admission.AdmissionRequest, cfg config.Config) (*Result, error) { Update: func(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
return &Result{Allowed: true}, nil return &Result{Allowed: true}, nil
}, },
Connect: func(r *admission.AdmissionRequest, cfg config.Config) (*Result, error) { Connect: func(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
return &Result{Allowed: true}, nil return &Result{Allowed: true}, nil
}, },
} }
} }
func podMutationCreate() AdmitFunc { func podMutationCreate() AdmitFunc {
return func(r *admission.AdmissionRequest, cfg config.Config) (*Result, error) { return func(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
var operations []PatchOperation var operations []PatchOperation
pod, err := parsePod(r.Object.Raw) pod, err := parsePod(r.Object.Raw)
if err != nil { if err != nil {

View File

@ -4,31 +4,31 @@ import (
"log" "log"
"strings" "strings"
"mutating-webhook/internal/config"
admission "k8s.io/api/admission/v1" admission "k8s.io/api/admission/v1"
"mutating-webhook/internal/config"
) )
func PodsValidation() Hook { func PodsValidation() Hook {
return Hook{ return Hook{
// default allow // default allow
Create: func(r *admission.AdmissionRequest, cfg config.Config) (*Result, error) { Create: func(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
return &Result{Allowed: true}, nil return &Result{Allowed: true}, nil
}, },
Delete: func(r *admission.AdmissionRequest, cfg config.Config) (*Result, error) { Delete: func(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
return &Result{Allowed: true}, nil return &Result{Allowed: true}, nil
}, },
Update: func(r *admission.AdmissionRequest, cfg config.Config) (*Result, error) { Update: func(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
return &Result{Allowed: true}, nil return &Result{Allowed: true}, nil
}, },
Connect: func(r *admission.AdmissionRequest, cfg config.Config) (*Result, error) { Connect: func(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
return &Result{Allowed: true}, nil return &Result{Allowed: true}, nil
}, },
} }
} }
func podValidationCreate() AdmitFunc { func podValidationCreate() AdmitFunc {
return func(r *admission.AdmissionRequest, cfg config.Config) (*Result, error) { return func(r *admission.AdmissionRequest, cfg *config.Config) (*Result, error) {
pod, err := parsePod(r.Object.Raw) pod, err := parsePod(r.Object.Raw)
if err != nil { if err != nil {
return &Result{Msg: err.Error()}, nil return &Result{Msg: err.Error()}, nil