-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove KubeConfig Dependency for Store Tests #2789
Remove KubeConfig Dependency for Store Tests #2789
Conversation
a125e2a
to
b5f32b4
Compare
@diazjf to be sure this is ok, please also remove https://github.com/kubernetes/ingress-nginx/blob/master/.travis.yml#L38 (these tests are the only reason why we start a cluster in tests) |
b5f32b4
to
786b80b
Compare
Codecov Report
@@ Coverage Diff @@
## master #2789 +/- ##
==========================================
- Coverage 47.58% 47.33% -0.26%
==========================================
Files 75 75
Lines 5413 5413
==========================================
- Hits 2576 2562 -14
- Misses 2509 2527 +18
+ Partials 328 324 -4
Continue to review full report at Codecov.
|
@aledbf main_test.go still fails since I haven't edited that file. store_test.go should pass. I will update main_test.go in this PR and let you know. |
@diazjf thank you for removing this dependency |
Excellent 👏 , I also wanted to tackle this, the dependency on a real API was annoying. |
@@ -605,37 +619,89 @@ func TestStore(t *testing.T) { | |||
// check invalid secret (missing ca) | |||
} | |||
|
|||
func createNamespace(clientSet *kubernetes.Clientset, t *testing.T) string { | |||
func createNamespace(clientSet *fake.Clientset, t *testing.T) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still using a specific type, you can replace any Clientset by kubernetes.Interface
to make it portable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we can cast fake.ClientSet to kubernetes.Clientset.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not ClientSet
, Interface
. All REST clients implement it, including fake
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you provide me an example of how it should look. Having some issues trying to properly type-cast.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All you have to do is replace clientSet *fake.Clientset
with clientSet kubernetes.Interface
, where kubernetes
is k8s.io/client-go/kubernetes
.
There is nothing to cast since you're just calling methods provided by the interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome Thanks, I see it now:
fakeclient:
clientset "k8s.io/client-go/kubernetes"
clientset.Interface = &Clientset{}
# &Clientset{} is:
# type Clientset struct {
# testing.Fake
# discovery *fakediscovery.FakeDiscovery
#}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason why it works is not the contents of the struct, just the methods the struct implements (in other words, Admissionregistration()
... StorageV1beta1()
)
"sync" | ||
"sync/atomic" | ||
"testing" | ||
"time" | ||
|
||
"github.com/eapache/channels" | ||
"k8s.io/api/extensions/v1beta1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
if err != nil { | ||
if k8sErrors.IsNotFound(err) { | ||
return clientSet.ExtensionsV1beta1().Ingresses(ingress.Namespace).Create(ingress) | ||
t.Logf("ingress %v not found, creating", ingress) | ||
return clientSet.Extensions().Ingresses(ingress.Namespace).Create(ingress) | ||
} | ||
return nil, err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should avoid returning errors in tests and simply call t.Fatal
in case an error occurs, like you did above. Call t.Helper()
at the beginning of the function in that case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
b19883f
to
f890769
Compare
func ensureIngress(ingress *extensions.Ingress, clientSet *kubernetes.Clientset) (*extensions.Ingress, error) { | ||
s, err := clientSet.ExtensionsV1beta1().Ingresses(ingress.Namespace).Update(ingress) | ||
func createConfigMap(clientSet *fake.Clientset, ns string, t *testing.T) string { | ||
t.Log("creating temporal config map") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a helper function, adding t.Helper()
here would allow logs to report line numbers accurately.
return ing | ||
} | ||
|
||
func deleteIngress(ingress *extensions.Ingress, clientSet *fake.Clientset, t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a helper function, adding t.Helper()
here would allow logs to report line numbers accurately.
return cm.Name | ||
} | ||
|
||
func deleteConfigMap(cm, ns string, clientSet *fake.Clientset, t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a helper function, adding t.Helper()
here would allow logs to report line numbers accurately.
t.Logf("temporal configmap %v deleted", cm) | ||
} | ||
|
||
func ensureIngress(ingress *extensions.Ingress, clientSet *fake.Clientset, t *testing.T) *extensions.Ingress { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a helper function, adding t.Helper()
here would allow logs to report line numbers accurately.
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "dummy", | ||
Namespace: ns, | ||
SelfLink: fmt.Sprintf("/apis/extensions/v1beta1/namespaces/%s/ingresses/dummy", ns), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this field generated by the API server, and not the user?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the fakeClient mocks generating this field, when it is not provided in the ingress the following is obtained:
Could not construct reference to: '<ingress-resource>' due to: 'selfLink was empty, can't make reference'
if err != nil { | ||
t.Errorf("unexpected error creating ingress: %v", err) | ||
t.Errorf("unexpected error waiting for secret: %v", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(my favorite review comment, sorry for repeating myself) Log messages should start with a capital letter to differentiate them from error messages returned by functions.
Besides errors are never expected, so removing the word "unexpected" from all these log messages makes them shorter and to the point. But that's obviously a nit pick, these messages are not user facing so feel free to skip that comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
} | ||
time.Sleep(1 * time.Second) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need that sleep with the fake client?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I noticed that if we don't wait a second, the event wont get generated by the time we need it to.
f890769
to
8fd8984
Compare
@aledbf @antoineco So I can get store_test.go working without KubeClient, but main_test.go will require some more effort, are you guys opposed to just merging this once all the issues have been resolved, and I can submit another PR in the future for main_test.go? By main_test.go I mean https://github.com/kubernetes/ingress-nginx/blob/master/cmd/nginx/main_test.go I'm thinking it looks more like a functional test, also I'm wondering where this is used. |
cb5e1bc
to
753cdc7
Compare
So tests are passing!! Give me 👍 and I'll revert .travis and main_test.go back to normal. |
Awesome work, thanks @diazjf! What issue are you facing exactly with |
753cdc7
to
4e71431
Compare
@antoineco thanks for all the help. It seems like |
cmd/nginx/main_test.go
Outdated
if err == nil { | ||
t.Fatal("Expected an error creating REST client without an API server URL or kubeconfig file.") | ||
t.Fatal("expected an error creating REST client without an API server URL or kubeconfig file.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Capital letter was fine (log message, cf previous comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will change back.
} | ||
|
||
_, err = createApiserverClient("", "") | ||
_, err := createApiserverClient("", "") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This calls kubernetes.NewForConfig()
, which will always fail without API. Not sure if it's really interesting to test createApiserverClient()
at all, unless we make it testable (eg. create a fake
client if called by tests).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it will be a bigger effort to make it more testable, but maybe we can set that for a future release.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can look into this in the future if its ok.
|
||
ns := "test" | ||
|
||
cm := createConfigMap(clientSet, ns, t) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure to understand why this is here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since controller.NewNGINXController(conf, nil, fs)
make a call to the store. The store sets a configuration and requires a configmap. If it is not created then there will be a segmentation fault.
Removes the KubeConfig Dependency for the Store Test by using the FakeClient Instead. Unit Tests should not rely on a real KubeConfig. Fixes kubernetes#2789
4e71431
to
c783c08
Compare
I'm fine refactoring the edit: sorry, didn't see the "hold" label had been removed. |
/lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: antoineco, diazjf The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
👍 |
I removed the label after the 0.17.1 release |
Removes the KubeConfig Dependency for the Store Test by using the FakeClient Instead. Unit Tests should not rely on a real KubeConfig.
See #2788