Skip to content

Commit

Permalink
feat: allow multiple inject configurations
Browse files Browse the repository at this point in the history
  • Loading branch information
0x416e746f6e committed Apr 25, 2024
1 parent ac29c51 commit 29bb2fb
Show file tree
Hide file tree
Showing 17 changed files with 490 additions and 69 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# dev config

.kube-sidecar-injector.yaml

# builds

/bin/*
Expand Down
28 changes: 28 additions & 0 deletions cmd/dump_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package main

import (
"fmt"

"github.com/flashbots/kube-sidecar-injector/config"
"github.com/urfave/cli/v2"
"gopkg.in/yaml.v2"
)

func CommandDumpConfig(cfg *config.Config, globalFlags []cli.Flag) *cli.Command {
cmd := CommandServe(cfg, globalFlags)

cmd.Name = "dump-config"
cmd.Usage = "dump the effective configuration"

cmd.Action = func(_ *cli.Context) error {
bytes, err := yaml.Marshal(cfg)
if err != nil {
return err
}

fmt.Printf("---\n\n%s\n", string(bytes))
return nil
}

return cmd
}
32 changes: 30 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/flashbots/kube-sidecar-injector/global"
"github.com/flashbots/kube-sidecar-injector/logutils"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v2/altsrc"
"go.uber.org/zap"
)

Expand All @@ -20,11 +21,20 @@ var (
)

func main() {
defaultConfigFile := "/etc/" + global.AppName + "/config.yaml"

cfg := &config.Config{
Version: version,
}

flags := []cli.Flag{
&cli.StringFlag{
EnvVars: []string{envPrefix + "CONFIG_FILE"},
Name: "config-file",
Usage: "config file",
Value: defaultConfigFile,
},

&cli.StringFlag{
Destination: &cfg.Log.Level,
EnvVars: []string{envPrefix + "LOG_LEVEL"},
Expand All @@ -43,7 +53,8 @@ func main() {
}

commands := []*cli.Command{
CommandServe(cfg),
CommandServe(cfg, flags),
CommandDumpConfig(cfg, flags),
}

app := &cli.App{
Expand All @@ -55,7 +66,24 @@ func main() {
Commands: commands,
DefaultCommand: commands[0].Name,

Before: func(_ *cli.Context) error {
Before: func(clictx *cli.Context) error {
f := clictx.String("config-file")
if _, err := os.Stat(f); err == nil {
// read non-CLI config from the file
_cfg, err := config.ReadFrom(f)
if err != nil {
return err
}
cfg.Inject = _cfg.Inject
// read the rest of config
if err := altsrc.InitInputSourceWithContext(
flags,
altsrc.NewYamlSourceFromFlagFunc("config-file"),
)(clictx); err != nil {
return err
}
}

// setup logger
l, err := logutils.NewLogger(&cfg.Log)
if err != nil {
Expand Down
19 changes: 17 additions & 2 deletions cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const (
categoryServer = "SERVER:"
)

func CommandServe(cfg *config.Config) *cli.Command {
func CommandServe(cfg *config.Config, globalFlags []cli.Flag) *cli.Command {
var rawServicePortNumber int64

debugFlags := []cli.Flag{}
Expand Down Expand Up @@ -99,7 +99,22 @@ func CommandServe(cfg *config.Config) *cli.Command {
Usage: "run the monitor server",
Flags: flags,

Before: func(ctx *cli.Context) error {
Before: func(clictx *cli.Context) error {
for _, i := range cfg.Inject {
if i.LabelSelector != nil {
if _, err := i.LabelSelector.LabelSelector(); err != nil {
return err
}
}
for _, c := range i.Containers {
if _, err := c.Container(); err != nil {
return fmt.Errorf("invalid config for container '%s': %w",
c.Name, err,
)
}
}
}

if rawServicePortNumber > 65535 {
return fmt.Errorf("invalid port service port number: %d", rawServicePortNumber)
}
Expand Down
42 changes: 39 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,45 @@
package config

import (
"errors"
"fmt"
"os"

"gopkg.in/yaml.v3"
)

type Config struct {
K8S K8S `yaml:"k8s"`
Log Log `yaml:"log"`
Server Server `yaml:"server"`
Inject []Inject `yaml:"inject,omitempty"`
K8S K8S `yaml:"k8s"`
Log Log `yaml:"log"`
Server Server `yaml:"server"`

Version string
}

var (
ErrConfigFailedToReadFromFile = errors.New("failed to read configuration from file")
ErrConfigurationFailedToDecode = errors.New("failed to decode configuration")
)

func ReadFrom(file string) (
*Config, error,
) {
f, err := os.OpenFile(file, os.O_RDONLY, 0)
if err != nil {
return nil, fmt.Errorf("%w: %s: %w",
ErrConfigFailedToReadFromFile, file, err,
)
}
d := yaml.NewDecoder(f)
d.KnownFields(true)
var _cfg Config
if err := d.Decode(&_cfg); err != nil {
return nil, fmt.Errorf("%w: %s: %w",
ErrConfigurationFailedToDecode, file, err,
)
}
return &Config{
Inject: _cfg.Inject,
}, nil
}
150 changes: 150 additions & 0 deletions config/container.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package config

import (
"fmt"
"hash"
"unsafe"

core_v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
)

type Container struct {
Name string `yaml:"name,omitempty"`
Image string `yaml:"image,omitempty"`
Command []string `yaml:"command,omitempty"`
Args []string `yaml:"args,omitempty"`
Ports []ContainerPort `yaml:"ports,omitempty"`
Resources *ResourceRequirements `yaml:"resources,omitempty"`
}

type ContainerPort struct {
Name string `yaml:"name,omitempty"`
HostPort int32 `yaml:"hostPort,omitempty"`
ContainerPort int32 `yaml:"containerPort,omitempty"`
Protocol string `yaml:"protocol,omitempty"`
HostIP string `yaml:"hostIP,omitempty"`
}

type ResourceRequirements struct {
Limits map[string]string `yaml:"limits,omitempty"`
Requests map[string]string `yaml:"requests,omitempty"`
}

func (c Container) hash(sum hash.Hash64) {
sum.Write([]byte("name:"))
sum.Write([]byte(c.Name))
sum.Write([]byte{255})

sum.Write([]byte("image:"))
sum.Write([]byte(c.Image))
sum.Write([]byte{255})

sum.Write([]byte("command:"))
for _, cmd := range c.Command {
sum.Write([]byte(cmd))
sum.Write([]byte{255})
}

sum.Write([]byte("args:"))
for _, arg := range c.Args {
sum.Write([]byte(arg))
sum.Write([]byte{255})
}

sum.Write([]byte("ports:"))
for _, p := range c.Ports {
sum.Write([]byte("name:"))
sum.Write([]byte(p.Name))
sum.Write([]byte{255})

sum.Write([]byte("hostPort:"))
sum.Write(unsafe.Slice(
(*byte)(unsafe.Pointer(&p.ContainerPort)),
unsafe.Sizeof(p.ContainerPort),
))
sum.Write([]byte{255})

sum.Write([]byte("protocol:"))
sum.Write([]byte(p.Protocol))
sum.Write([]byte{255})

sum.Write([]byte("hostIP:"))
sum.Write([]byte(p.HostIP))
sum.Write([]byte{255})
}

if c.Resources != nil {
sum.Write([]byte("resources:"))

sum.Write([]byte("limits:"))
for k, v := range c.Resources.Limits {
sum.Write([]byte("key:"))
sum.Write([]byte(k))
sum.Write([]byte{255})

sum.Write([]byte("value:"))
sum.Write([]byte(v))
sum.Write([]byte{255})
}

sum.Write([]byte("requests:"))
for k, v := range c.Resources.Requests {
sum.Write([]byte("key:"))
sum.Write([]byte(k))
sum.Write([]byte{255})

sum.Write([]byte("value:"))
sum.Write([]byte(v))
sum.Write([]byte{255})
}
}
}

func (c Container) Container() (*core_v1.Container, error) {
ports := make([]core_v1.ContainerPort, 0, len(c.Ports))
for _, p := range c.Ports {
ports = append(ports, core_v1.ContainerPort{
Name: p.Name,
HostPort: p.HostPort,
ContainerPort: p.ContainerPort,
Protocol: core_v1.Protocol(p.Protocol),
HostIP: p.HostIP,
})
}

limits := make(map[core_v1.ResourceName]resource.Quantity)
if c.Resources != nil {
for k, v := range c.Resources.Limits {
q, err := resource.ParseQuantity(v)
if err != nil {
return nil, fmt.Errorf("%w: %s", err, v)
}
limits[core_v1.ResourceName(k)] = q
}
}

requests := make(map[core_v1.ResourceName]resource.Quantity)
if c.Resources != nil {
for k, v := range c.Resources.Requests {
q, err := resource.ParseQuantity(v)
if err != nil {
return nil, fmt.Errorf("%w: %s", err, v)
}
requests[core_v1.ResourceName(k)] = q
}
}

return &core_v1.Container{
Name: c.Name,
Image: c.Image,
Command: c.Command,
Args: c.Args,

Ports: ports,
Resources: core_v1.ResourceRequirements{
Limits: limits,
Requests: requests,
},
}, nil
}
25 changes: 25 additions & 0 deletions config/inject.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package config

import (
"fmt"
"hash/fnv"
)

type Inject struct {
LabelSelector *LabelSelector `yaml:"labelSelector,omitempty"`
Containers []Container `yaml:"containers,omitempty"`
}

func (i Inject) Fingerprint() string {
sum := fnv.New64a()

sum.Write([]byte("labelSelector:"))
i.LabelSelector.hash(sum)

sum.Write([]byte("containers:"))
for _, c := range i.Containers {
c.hash(sum)
}

return fmt.Sprintf("%016x", sum.Sum64())
}
8 changes: 4 additions & 4 deletions config/k8s.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package config

type K8S struct {
Namespace string `yaml:"namespace"`
ServiceName string `yaml:"service_name"`
ServicePortNumber int32 `yaml:"service_port_number"`
MutatingWebhookConfigurationName string `yaml:"mutating_webhook_configuration_name"`
Namespace string `yaml:"namespace,omitempty"`
ServiceName string `yaml:"serviceName,omitempty"`
ServicePortNumber int32 `yaml:"servicePortNumber,omitempty"`
MutatingWebhookConfigurationName string `yaml:"mutatingWebhookConfigurationName,omitempty"`
}
Loading

0 comments on commit 29bb2fb

Please sign in to comment.