diff --git a/go.mod b/go.mod index bf7df799..d94c4f27 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,9 @@ module github.com/rancher/lasso go 1.19 require ( + github.com/golang/mock v1.6.0 github.com/prometheus/client_golang v1.12.1 + github.com/stretchr/testify v1.8.1 k8s.io/apimachinery v0.25.4 k8s.io/client-go v0.25.4 k8s.io/klog v1.0.0 @@ -32,6 +34,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect diff --git a/go.sum b/go.sum index f5118177..27ae5f77 100644 --- a/go.sum +++ b/go.sum @@ -103,6 +103,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -234,16 +236,22 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -285,6 +293,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -315,6 +324,7 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -336,6 +346,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -370,7 +381,9 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= @@ -433,6 +446,7 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go new file mode 100644 index 00000000..25e9cad3 --- /dev/null +++ b/pkg/client/client_test.go @@ -0,0 +1,56 @@ +package client + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/client-go/rest" +) + +type userAgentRoundTripper struct { + expectedUserAgent string + t *testing.T +} + +func TestWithAgent(t *testing.T) { + testUserAgent := "testagent" + testConfig := rest.Config{ + UserAgent: "defaultuseragent", + ContentConfig: rest.ContentConfig{ + NegotiatedSerializer: serializer.WithoutConversionCodecFactory{}, + }, + Transport: userAgentRoundTripper{t: t, expectedUserAgent: testUserAgent}, + } + + testRestClient, err := rest.UnversionedRESTClientFor(&testConfig) + assert.Nil(t, err) + + testClient := &Client{ + RESTClient: testRestClient, + Config: testConfig, + } + + testClientWithAgent, err := testClient.WithAgent("testagent") + assert.Nil(t, err) + + testRestClientWithAgent := testClientWithAgent.RESTClient.(*rest.RESTClient) + + _, err = testRestClientWithAgent.Client.Transport.RoundTrip(&http.Request{}) + assert.Nil(t, err) + + // with invalid config + testClient = &Client{ + Config: rest.Config{}, + } + + testClientWithAgent, err = testClient.WithAgent("testagent") + assert.NotNil(t, err) + assert.Nil(t, testClientWithAgent) +} + +func (u userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + assert.Equal(u.t, u.expectedUserAgent, req.Header.Get("User-Agent")) + return &http.Response{}, nil +} diff --git a/pkg/client/mocks_test.go b/pkg/client/mocks_test.go new file mode 100644 index 00000000..b297ebe2 --- /dev/null +++ b/pkg/client/mocks_test.go @@ -0,0 +1,172 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/rancher/lasso/pkg/client (interfaces: SharedClientFactory) + +// Package client is a generated GoMock package. +package client + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" +) + +// MockSharedClientFactory is a mock of SharedClientFactory interface. +type MockSharedClientFactory struct { + ctrl *gomock.Controller + recorder *MockSharedClientFactoryMockRecorder +} + +// MockSharedClientFactoryMockRecorder is the mock recorder for MockSharedClientFactory. +type MockSharedClientFactoryMockRecorder struct { + mock *MockSharedClientFactory +} + +// NewMockSharedClientFactory creates a new mock instance. +func NewMockSharedClientFactory(ctrl *gomock.Controller) *MockSharedClientFactory { + mock := &MockSharedClientFactory{ctrl: ctrl} + mock.recorder = &MockSharedClientFactoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSharedClientFactory) EXPECT() *MockSharedClientFactoryMockRecorder { + return m.recorder +} + +// ForKind mocks base method. +func (m *MockSharedClientFactory) ForKind(arg0 schema.GroupVersionKind) (*Client, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ForKind", arg0) + ret0, _ := ret[0].(*Client) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ForKind indicates an expected call of ForKind. +func (mr *MockSharedClientFactoryMockRecorder) ForKind(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ForKind", reflect.TypeOf((*MockSharedClientFactory)(nil).ForKind), arg0) +} + +// ForResource mocks base method. +func (m *MockSharedClientFactory) ForResource(arg0 schema.GroupVersionResource, arg1 bool) (*Client, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ForResource", arg0, arg1) + ret0, _ := ret[0].(*Client) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ForResource indicates an expected call of ForResource. +func (mr *MockSharedClientFactoryMockRecorder) ForResource(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ForResource", reflect.TypeOf((*MockSharedClientFactory)(nil).ForResource), arg0, arg1) +} + +// ForResourceKind mocks base method. +func (m *MockSharedClientFactory) ForResourceKind(arg0 schema.GroupVersionResource, arg1 string, arg2 bool) *Client { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ForResourceKind", arg0, arg1, arg2) + ret0, _ := ret[0].(*Client) + return ret0 +} + +// ForResourceKind indicates an expected call of ForResourceKind. +func (mr *MockSharedClientFactoryMockRecorder) ForResourceKind(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ForResourceKind", reflect.TypeOf((*MockSharedClientFactory)(nil).ForResourceKind), arg0, arg1, arg2) +} + +// GVKForObject mocks base method. +func (m *MockSharedClientFactory) GVKForObject(arg0 runtime.Object) (schema.GroupVersionKind, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GVKForObject", arg0) + ret0, _ := ret[0].(schema.GroupVersionKind) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GVKForObject indicates an expected call of GVKForObject. +func (mr *MockSharedClientFactoryMockRecorder) GVKForObject(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GVKForObject", reflect.TypeOf((*MockSharedClientFactory)(nil).GVKForObject), arg0) +} + +// GVKForResource mocks base method. +func (m *MockSharedClientFactory) GVKForResource(arg0 schema.GroupVersionResource) (schema.GroupVersionKind, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GVKForResource", arg0) + ret0, _ := ret[0].(schema.GroupVersionKind) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GVKForResource indicates an expected call of GVKForResource. +func (mr *MockSharedClientFactoryMockRecorder) GVKForResource(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GVKForResource", reflect.TypeOf((*MockSharedClientFactory)(nil).GVKForResource), arg0) +} + +// IsHealthy mocks base method. +func (m *MockSharedClientFactory) IsHealthy(arg0 context.Context) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsHealthy", arg0) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsHealthy indicates an expected call of IsHealthy. +func (mr *MockSharedClientFactoryMockRecorder) IsHealthy(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsHealthy", reflect.TypeOf((*MockSharedClientFactory)(nil).IsHealthy), arg0) +} + +// IsNamespaced mocks base method. +func (m *MockSharedClientFactory) IsNamespaced(arg0 schema.GroupVersionKind) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsNamespaced", arg0) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsNamespaced indicates an expected call of IsNamespaced. +func (mr *MockSharedClientFactoryMockRecorder) IsNamespaced(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNamespaced", reflect.TypeOf((*MockSharedClientFactory)(nil).IsNamespaced), arg0) +} + +// NewObjects mocks base method. +func (m *MockSharedClientFactory) NewObjects(arg0 schema.GroupVersionKind) (runtime.Object, runtime.Object, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewObjects", arg0) + ret0, _ := ret[0].(runtime.Object) + ret1, _ := ret[1].(runtime.Object) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// NewObjects indicates an expected call of NewObjects. +func (mr *MockSharedClientFactoryMockRecorder) NewObjects(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewObjects", reflect.TypeOf((*MockSharedClientFactory)(nil).NewObjects), arg0) +} + +// ResourceForGVK mocks base method. +func (m *MockSharedClientFactory) ResourceForGVK(arg0 schema.GroupVersionKind) (schema.GroupVersionResource, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResourceForGVK", arg0) + ret0, _ := ret[0].(schema.GroupVersionResource) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ResourceForGVK indicates an expected call of ResourceForGVK. +func (mr *MockSharedClientFactoryMockRecorder) ResourceForGVK(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResourceForGVK", reflect.TypeOf((*MockSharedClientFactory)(nil).ResourceForGVK), arg0) +} diff --git a/pkg/client/useragent_test.go b/pkg/client/useragent_test.go new file mode 100644 index 00000000..6258d99d --- /dev/null +++ b/pkg/client/useragent_test.go @@ -0,0 +1,90 @@ +package client + +import ( + "fmt" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/client-go/rest" + "net/http" + "testing" +) + +func TestNewSharedClientFactoryWithAgent(t *testing.T) { + mockSharedClientFactory := NewMockSharedClientFactory(gomock.NewController(t)) + + testUserAgent := "testagent" + testConfig := rest.Config{ + UserAgent: "defaultuseragent", + ContentConfig: rest.ContentConfig{ + NegotiatedSerializer: serializer.WithoutConversionCodecFactory{}, + }, + Transport: userAgentRoundTripper{t: t, expectedUserAgent: testUserAgent}, + } + + testRestClient, err := rest.UnversionedRESTClientFor(&testConfig) + assert.Nil(t, err) + testClient := &Client{ + RESTClient: testRestClient, + Config: testConfig, + } + mockSharedClientFactory.EXPECT().ForKind(gomock.Any()).DoAndReturn(func(gvk schema.GroupVersionKind) (*Client, error) { + return testClient, nil + }).AnyTimes() + mockSharedClientFactory.EXPECT().ForResource(gomock.Any(), gomock.Any()).DoAndReturn(func(gvr schema.GroupVersionResource, namespaced bool) (*Client, error) { + return testClient, nil + }).AnyTimes() + mockSharedClientFactory.EXPECT().ForResourceKind(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(gvr schema.GroupVersionResource, kind string, namespaced bool) *Client { + return testClient + }).AnyTimes() + testSharedClientFactoryWithAgent := NewSharedClientFactoryWithAgent("testagent", mockSharedClientFactory) + + // for kind + clientWithAgent, err := testSharedClientFactoryWithAgent.ForKind(schema.GroupVersionKind{}) + assert.Nil(t, err) + + restClient := clientWithAgent.RESTClient.(*rest.RESTClient) + restClient.Client.Transport.RoundTrip(&http.Request{}) + + // for resource + clientWithAgent, err = testSharedClientFactoryWithAgent.ForResource(schema.GroupVersionResource{}, false) + assert.Nil(t, err) + + restClient = clientWithAgent.RESTClient.(*rest.RESTClient) + restClient.Client.Transport.RoundTrip(&http.Request{}) + + // for resource kind + clientWithAgent = testSharedClientFactoryWithAgent.ForResourceKind(schema.GroupVersionResource{}, "", false) + assert.Nil(t, err) + + restClient = clientWithAgent.RESTClient.(*rest.RESTClient) + restClient.Client.Transport.RoundTrip(&http.Request{}) + + // test with errors + mockSharedClientErrFactory := NewMockSharedClientFactory(gomock.NewController(t)) + mockSharedClientErrFactory.EXPECT().ForKind(gomock.Any()).DoAndReturn(func(gvk schema.GroupVersionKind) (*Client, error) { + return nil, fmt.Errorf("some error") + }).AnyTimes() + mockSharedClientErrFactory.EXPECT().ForResource(gomock.Any(), gomock.Any()).DoAndReturn(func(gvr schema.GroupVersionResource, namespaced bool) (*Client, error) { + return nil, fmt.Errorf("some error") + }).AnyTimes() + mockSharedClientErrFactory.EXPECT().ForResourceKind(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(gvr schema.GroupVersionResource, kind string, namespaced bool) *Client { + return nil + }).AnyTimes() + testSharedClientFactoryWithAgent = NewSharedClientFactoryWithAgent("", mockSharedClientErrFactory) + + // for kind + clientWithAgent, err = testSharedClientFactoryWithAgent.ForKind(schema.GroupVersionKind{}) + assert.NotNil(t, err) + assert.Nil(t, clientWithAgent) + + // for resource + clientWithAgent, err = testSharedClientFactoryWithAgent.ForResource(schema.GroupVersionResource{}, false) + assert.NotNil(t, err) + assert.Nil(t, clientWithAgent) + + // for resource kind + clientWithAgent = testSharedClientFactoryWithAgent.ForResourceKind(schema.GroupVersionResource{}, "", false) + assert.Nil(t, clientWithAgent) +} diff --git a/pkg/controller/mocks_test.go b/pkg/controller/mocks_test.go new file mode 100644 index 00000000..7a53d4bb --- /dev/null +++ b/pkg/controller/mocks_test.go @@ -0,0 +1,128 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/rancher/lasso/pkg/controller (interfaces: SharedController) + +// Package controller is a generated GoMock package. +package controller + +import ( + context "context" + reflect "reflect" + time "time" + + gomock "github.com/golang/mock/gomock" + client "github.com/rancher/lasso/pkg/client" + cache "k8s.io/client-go/tools/cache" +) + +// MockSharedController is a mock of SharedController interface. +type MockSharedController struct { + ctrl *gomock.Controller + recorder *MockSharedControllerMockRecorder +} + +// MockSharedControllerMockRecorder is the mock recorder for MockSharedController. +type MockSharedControllerMockRecorder struct { + mock *MockSharedController +} + +// NewMockSharedController creates a new mock instance. +func NewMockSharedController(ctrl *gomock.Controller) *MockSharedController { + mock := &MockSharedController{ctrl: ctrl} + mock.recorder = &MockSharedControllerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSharedController) EXPECT() *MockSharedControllerMockRecorder { + return m.recorder +} + +// Client mocks base method. +func (m *MockSharedController) Client() *client.Client { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Client") + ret0, _ := ret[0].(*client.Client) + return ret0 +} + +// Client indicates an expected call of Client. +func (mr *MockSharedControllerMockRecorder) Client() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Client", reflect.TypeOf((*MockSharedController)(nil).Client)) +} + +// Enqueue mocks base method. +func (m *MockSharedController) Enqueue(arg0, arg1 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Enqueue", arg0, arg1) +} + +// Enqueue indicates an expected call of Enqueue. +func (mr *MockSharedControllerMockRecorder) Enqueue(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Enqueue", reflect.TypeOf((*MockSharedController)(nil).Enqueue), arg0, arg1) +} + +// EnqueueAfter mocks base method. +func (m *MockSharedController) EnqueueAfter(arg0, arg1 string, arg2 time.Duration) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "EnqueueAfter", arg0, arg1, arg2) +} + +// EnqueueAfter indicates an expected call of EnqueueAfter. +func (mr *MockSharedControllerMockRecorder) EnqueueAfter(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnqueueAfter", reflect.TypeOf((*MockSharedController)(nil).EnqueueAfter), arg0, arg1, arg2) +} + +// EnqueueKey mocks base method. +func (m *MockSharedController) EnqueueKey(arg0 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "EnqueueKey", arg0) +} + +// EnqueueKey indicates an expected call of EnqueueKey. +func (mr *MockSharedControllerMockRecorder) EnqueueKey(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnqueueKey", reflect.TypeOf((*MockSharedController)(nil).EnqueueKey), arg0) +} + +// Informer mocks base method. +func (m *MockSharedController) Informer() cache.SharedIndexInformer { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Informer") + ret0, _ := ret[0].(cache.SharedIndexInformer) + return ret0 +} + +// Informer indicates an expected call of Informer. +func (mr *MockSharedControllerMockRecorder) Informer() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Informer", reflect.TypeOf((*MockSharedController)(nil).Informer)) +} + +// RegisterHandler mocks base method. +func (m *MockSharedController) RegisterHandler(arg0 context.Context, arg1 string, arg2 SharedControllerHandler) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterHandler", arg0, arg1, arg2) +} + +// RegisterHandler indicates an expected call of RegisterHandler. +func (mr *MockSharedControllerMockRecorder) RegisterHandler(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterHandler", reflect.TypeOf((*MockSharedController)(nil).RegisterHandler), arg0, arg1, arg2) +} + +// Start mocks base method. +func (m *MockSharedController) Start(arg0 context.Context, arg1 int) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Start", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Start indicates an expected call of Start. +func (mr *MockSharedControllerMockRecorder) Start(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockSharedController)(nil).Start), arg0, arg1) +} diff --git a/pkg/controller/useragent.go b/pkg/controller/useragent.go new file mode 100644 index 00000000..571729ad --- /dev/null +++ b/pkg/controller/useragent.go @@ -0,0 +1,68 @@ +package controller + +import ( + "github.com/rancher/lasso/pkg/client" + "github.com/rancher/lasso/pkg/log" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +type sharedControllerFactoryWithAgent struct { + SharedControllerFactory + + userAgent string +} + +type sharedControllerWithAgent struct { + SharedController + + userAgent string +} + +// NewSharedControllerFactoryWithAgent returns a controller factory that is equivalent to the passed controller factory +// with the addition that it will wrap the returned controllers from ForKind, ForResource, and ForResourceKind with the +// sharedController struct using the passed user agent. +func NewSharedControllerFactoryWithAgent(userAgent string, clientFactory SharedControllerFactory) SharedControllerFactory { + return &sharedControllerFactoryWithAgent{ + SharedControllerFactory: clientFactory, + userAgent: userAgent, + } +} + +func NewSharedControllerWithAgent(userAgent string, controller SharedController) SharedController { + return &sharedControllerWithAgent{ + SharedController: controller, + userAgent: userAgent, + } +} + +func (s *sharedControllerFactoryWithAgent) ForKind(gvk schema.GroupVersionKind) (SharedController, error) { + resourceController, err := s.SharedControllerFactory.ForKind(gvk) + if err != nil { + return resourceController, err + } + + return NewSharedControllerWithAgent(s.userAgent, resourceController), err +} + +func (s *sharedControllerFactoryWithAgent) ForResource(gvr schema.GroupVersionResource, namespaced bool) SharedController { + resourceController := s.SharedControllerFactory.ForResource(gvr, namespaced) + return NewSharedControllerWithAgent(s.userAgent, resourceController) +} + +func (s *sharedControllerFactoryWithAgent) ForResourceKind(gvr schema.GroupVersionResource, kind string, namespaced bool) SharedController { + resourceController := s.SharedControllerFactory.ForResourceKind(gvr, kind, namespaced) + return NewSharedControllerWithAgent(s.userAgent, resourceController) +} + +func (s *sharedControllerWithAgent) Client() *client.Client { + client := s.SharedController.Client() + if client == nil { + return client + } + clientWithAgent, err := client.WithAgent(s.userAgent) + if err != nil { + log.Debugf("failed to get client with agent [%s]", s.userAgent) + return client + } + return clientWithAgent +} diff --git a/pkg/controller/useragent_test.go b/pkg/controller/useragent_test.go new file mode 100644 index 00000000..cbc056c1 --- /dev/null +++ b/pkg/controller/useragent_test.go @@ -0,0 +1,55 @@ +package controller + +import ( + "github.com/golang/mock/gomock" + "github.com/rancher/lasso/pkg/client" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/client-go/rest" + "net/http" + "testing" +) + +type userAgentRoundTripper struct { + expectedUserAgent string + t *testing.T +} + +func TestNewSharedControllerFactoryWithAgent(t *testing.T) { + testUserAgent := "testagent" + testConfig := rest.Config{ + UserAgent: "defaultuseragent", + ContentConfig: rest.ContentConfig{ + NegotiatedSerializer: serializer.WithoutConversionCodecFactory{}, + }, + Transport: userAgentRoundTripper{t: t, expectedUserAgent: testUserAgent}, + } + + testRestClient, err := rest.UnversionedRESTClientFor(&testConfig) + assert.Nil(t, err) + + testSharedController := NewMockSharedController(gomock.NewController(t)) + testClient := &client.Client{ + RESTClient: testRestClient, + Config: testConfig, + } + testSharedController.EXPECT().Client().DoAndReturn(func() *client.Client { + return testClient + }).AnyTimes() + + testSharedControllerWithAgent := NewSharedControllerWithAgent(testUserAgent, testSharedController) + testClientWithAgent := testSharedControllerWithAgent.Client().RESTClient.(*rest.RESTClient) + testClientWithAgent.Client.Transport.RoundTrip(&http.Request{}) + + testErrSharedController := NewMockSharedController(gomock.NewController(t)) + testErrSharedController.EXPECT().Client().DoAndReturn(func() *client.Client { + return nil + }).AnyTimes() + testSharedControllerWithAgent = NewSharedControllerWithAgent(testUserAgent, testErrSharedController) + assert.Nil(t, testSharedControllerWithAgent.Client()) +} + +func (u userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + assert.Equal(u.t, u.expectedUserAgent, req.Header.Get("User-Agent")) + return &http.Response{}, nil +}