diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6f48169bb..b01448af0 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,11 +3,11 @@ PLEASE READ THIS MESSAGE. Documentation fixes or enhancements: - for Traefik v2: use branch v2.10 -- for Traefik v3: use branch master +- for Traefik v3: use branch v3.0 Bug fixes: - for Traefik v2: use branch v2.10 -- for Traefik v3: use branch master +- for Traefik v3: use branch v3.0 Enhancements: - for Traefik v2: we only accept bug fixes diff --git a/go.mod b/go.mod index 5e19c8abf..518004282 100644 --- a/go.mod +++ b/go.mod @@ -71,7 +71,7 @@ require ( github.com/unrolled/render v1.0.2 github.com/unrolled/secure v1.0.9 github.com/vdemeester/shakers v0.1.0 - github.com/vulcand/oxy/v2 v2.0.0-20230227135449-a0e9f7ff1040 + github.com/vulcand/oxy/v2 v2.0.0-20230417082832-03de175b3822 github.com/vulcand/predicate v1.2.0 go.elastic.co/apm v1.13.1 go.elastic.co/apm/module/apmot v1.13.1 diff --git a/go.sum b/go.sum index 3170383cd..3941cfe17 100644 --- a/go.sum +++ b/go.sum @@ -1889,8 +1889,8 @@ github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6Ac github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= -github.com/vulcand/oxy/v2 v2.0.0-20230227135449-a0e9f7ff1040 h1:L+nLher4530BUkyOpxxBsl2SLbrD4fSlDH5rGZ8DRBM= -github.com/vulcand/oxy/v2 v2.0.0-20230227135449-a0e9f7ff1040/go.mod h1:A2voDnpONyqdplUDK0lt5y4XHLiBXPBw7iQES8+ZWRw= +github.com/vulcand/oxy/v2 v2.0.0-20230417082832-03de175b3822 h1:DXLWOIMPcQV+bxCFhBYSY5AIGP4DGvXH6qkwsg82YYY= +github.com/vulcand/oxy/v2 v2.0.0-20230417082832-03de175b3822/go.mod h1:A2voDnpONyqdplUDK0lt5y4XHLiBXPBw7iQES8+ZWRw= github.com/vulcand/predicate v1.2.0 h1:uFsW1gcnnR7R+QTID+FVcs0sSYlIGntoGOTb3rQJt50= github.com/vulcand/predicate v1.2.0/go.mod h1:VipoNYXny6c8N381zGUWkjuuNHiRbeAZhE7Qm9c+2GA= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= diff --git a/pkg/config/dynamic/plugins.go b/pkg/config/dynamic/plugins.go index a44236695..6df68b242 100644 --- a/pkg/config/dynamic/plugins.go +++ b/pkg/config/dynamic/plugins.go @@ -1,22 +1,26 @@ package dynamic -import "k8s.io/apimachinery/pkg/runtime" +import ( + "encoding/json" + "fmt" + "reflect" +) // +k8s:deepcopy-gen=false // PluginConf holds the plugin configuration. -type PluginConf map[string]interface{} +type PluginConf map[string]any -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +// DeepCopyInto is a deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PluginConf) DeepCopyInto(out *PluginConf) { if in == nil { *out = nil } else { - *out = runtime.DeepCopyJSON(*in) + *out = deepCopyJSON(*in) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PluginConf. +// DeepCopy is a deepcopy function, copying the receiver, creating a new PluginConf. func (in *PluginConf) DeepCopy() *PluginConf { if in == nil { return nil @@ -25,3 +29,49 @@ func (in *PluginConf) DeepCopy() *PluginConf { in.DeepCopyInto(out) return out } + +// inspired by https://github.com/kubernetes/apimachinery/blob/53ecdf01b997ca93c7db7615dfe7b27ad8391983/pkg/runtime/converter.go#L607 +func deepCopyJSON(x map[string]any) map[string]any { + return deepCopyJSONValue(x).(map[string]any) +} + +func deepCopyJSONValue(x any) any { + switch x := x.(type) { + case map[string]any: + if x == nil { + // Typed nil - an any that contains a type map[string]any with a value of nil + return x + } + clone := make(map[string]any, len(x)) + for k, v := range x { + clone[k] = deepCopyJSONValue(v) + } + return clone + case []any: + if x == nil { + // Typed nil - an any that contains a type []any with a value of nil + return x + } + clone := make([]any, len(x)) + for i, v := range x { + clone[i] = deepCopyJSONValue(v) + } + return clone + case string, int64, bool, float64, nil, json.Number: + return x + default: + v := reflect.ValueOf(x) + + if v.NumMethod() == 0 { + panic(fmt.Errorf("cannot deep copy %T", x)) + } + + method := v.MethodByName("DeepCopy") + if method.Kind() == reflect.Invalid { + panic(fmt.Errorf("cannot deep copy %T", x)) + } + + call := method.Call(nil) + return call[0].Interface() + } +} diff --git a/pkg/config/dynamic/plugins_test.go b/pkg/config/dynamic/plugins_test.go new file mode 100644 index 000000000..6021362dd --- /dev/null +++ b/pkg/config/dynamic/plugins_test.go @@ -0,0 +1,75 @@ +package dynamic + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +type FakeConfig struct { + Name string `json:"name"` +} + +// DeepCopyInto is a deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FakeConfig) DeepCopyInto(out *FakeConfig) { + *out = *in +} + +// DeepCopy is a deepcopy function, copying the receiver, creating a new AddPrefix. +func (in *FakeConfig) DeepCopy() *FakeConfig { + if in == nil { + return nil + } + out := new(FakeConfig) + in.DeepCopyInto(out) + return out +} + +type Foo struct { + Name string +} + +func TestPluginConf_DeepCopy_mapOfStruct(t *testing.T) { + f := &FakeConfig{Name: "bir"} + p := PluginConf{ + "fii": f, + } + + clone := p.DeepCopy() + assert.Equal(t, &p, clone) + + f.Name = "bur" + + assert.NotEqual(t, &p, clone) +} + +func TestPluginConf_DeepCopy_map(t *testing.T) { + m := map[string]interface{}{ + "name": "bar", + } + p := PluginConf{ + "config": map[string]interface{}{ + "foo": m, + }, + } + + clone := p.DeepCopy() + assert.Equal(t, &p, clone) + + p["one"] = "a" + m["two"] = "b" + + assert.NotEqual(t, &p, clone) +} + +func TestPluginConf_DeepCopy_panic(t *testing.T) { + p := &PluginConf{ + "config": map[string]interface{}{ + "foo": &Foo{Name: "gigi"}, + }, + } + + assert.Panics(t, func() { + p.DeepCopy() + }) +}