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
Fix support for label selectors #971
Fix support for label selectors #971
Conversation
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.
a few more unit tests are missing:
- when both application name and selector are set
- when label selector is used but no application is annotated so the result is empty
- there is an error at performing
List
call
and acceptance tests demonstrating the usage of the label selector and the situation when both name and label selector are set.
if i.serviceBinding.Spec.Application.Name != "" && | ||
i.serviceBinding.Spec.Application.LabelSelector != nil && | ||
i.serviceBinding.Spec.Application.LabelSelector.MatchLabels != nil { | ||
return nil, errs.New("Both label selector and name for application should be used") |
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 mean - "should not be used"?
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. 5326c6f
|
||
} | ||
if i.serviceBinding.Spec.Application.LabelSelector != nil && i.serviceBinding.Spec.Application.LabelSelector.MatchLabels != nil { | ||
gvr, err := i.typeLookup.ResourceForReferable(ref) |
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 lines repeats also in line 130 - so it could be moved before back to the original place.
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. 5326c6f
} | ||
|
||
if len(objList.Items) == 0 { | ||
return nil, errs.New("application not found") |
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 contract of this method is to return empty array, not array if not applications are not found, see the doc:
Applications() ([]Application, error) |
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.
Returning nil
returns an empty slice of Application
objects.
See this example: https://play.golang.org/p/QvQLT9CH3Fg
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, nice. However, according to the contract, we should not return error in this place, just empty slice.
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.
Fixed and added a test case for the same: 588535c
@@ -91,6 +92,51 @@ var _ = Describe("Context", func() { | |||
Entry("no binding path specified", nil, defaultContainerPath), | |||
Entry("binding path specified", &v1alpha1.BindingPath{ContainersPath: "foo.bar"}, "foo.bar"), | |||
) | |||
DescribeTable("should return slice of size 1 if application is specifiedt through label seclector", func(bindingPath *v1alpha1.BindingPath, expectedContainerPath 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.
typo "specifiedt"
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.
it would be also more interesting to cover the case when there at least two applications with the same label - with that we check if the loop in https://github.com/redhat-developer/service-binding-operator/pull/971/files#diff-0168dc7ac8aa540873af78cf38f886f6a5ace89fb75dce9880490cedbed1d46eR157 really works.
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. 5326c6f
Codecov Report
@@ Coverage Diff @@
## master #971 +/- ##
==========================================
+ Coverage 54.27% 54.72% +0.44%
==========================================
Files 23 23
Lines 1227 1239 +12
==========================================
+ Hits 666 678 +12
Misses 473 473
Partials 88 88
Continue to review full report at Codecov.
|
@baijum can you think about some better commit message/PR summary describing what was done and how? |
@baijum commented on this pull request.
Returning `nil` returns an empty slice of `Application` objects.
See this example: https://play.golang.org/p/QvQLT9CH3Fg
In this particular case, 'nil' is an un-initialized slice of a
Application items, not an empty initialized slice of Application
items.
***@***.***/golang-nil-vs-empty-slice-87fd51c0a4d
I've found a good summary on why this matters.
|
I have updated the PR title and description. It can be used for the commit message after squashing the commits. |
Done. 538772c |
I missed this. I will work on this. |
Done. 5e472be |
Done. 7debb12 |
I couldn't figure out how to produce an error in the |
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.
it would be good to have an additonal acceptance test demonstrating how to bind 2 applications having the same label.
if err != nil { | ||
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.
IMHO this check does not have to be guarded by i.serviceBinding.Spec.Application.Name != ""
condition because in line 129 we get just GroupVersionResource struct, Name
does play any role in that call.
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. af6e990
} | ||
|
||
if len(objList.Items) == 0 { | ||
return nil, nil |
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.
should we return here also emptyResult, nill
, similar to what we have in line 150?
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. af6e990
@@ -91,7 +92,175 @@ var _ = Describe("Context", func() { | |||
Entry("no binding path specified", nil, defaultContainerPath), | |||
Entry("binding path specified", &v1alpha1.BindingPath{ContainersPath: "foo.bar"}, "foo.bar"), | |||
) | |||
DescribeTable("should return slice of size 1 if application is specified through label seclector", func(bindingPath *v1alpha1.BindingPath, expectedContainerPath 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.
test "should return slice of size 2 if 2 applications are specified through label seclector" covers the logic tested here, hence we could drop this test, because it does not introduce any new testing path.
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. af6e990
ref := v1alpha1.Application{ | ||
Ref: v1alpha1.Ref{ | ||
Group: "app", | ||
Version: "v1", | ||
Kind: "Foo", | ||
Name: "app1", | ||
}, | ||
LabelSelector: ls, | ||
} |
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.
it could be dropped from the test setup, because it is not used anywhere in the test later on.
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 test is to check the effect of two kinds of application references: label & name.
Even though it is not directly used, this setup is required to validate the functionality.
ctx := &impl{client: client, serviceBinding: &sb, typeLookup: typeLookup} | ||
|
||
_, err := ctx.Applications() | ||
Expect(err).To(HaveOccurred()) |
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, but which error has occurred? we need to assert that as well.
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. af6e990
@@ -0,0 +1,111 @@ | |||
Feature: Bind an application with label selector to a service |
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.
should we really consider this as a separate feature? this is all about referring application resources using attached labels
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 a separate feature file for the label selector is fine.
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 backup your statement with some reason?
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 am fine with moving to another file. Please suggest the file.
Given Namespace [TEST_NAMESPACE] is used | ||
* Service Binding Operator is running | ||
|
||
Scenario: Bind an application with label selector to backend service |
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.
application resource has labels attached, but label selector is used inside service binding to define what application should be bound. Could we be a bit more verbose about the setup and the expected outcome? For example:
Bind successfully an application owning a given label to a service
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. af6e990
And The application env var "BACKEND_USERNAME" has value "AzureDiamond" | ||
And The application env var "BACKEND_PASSWORD" has value "hunter2" | ||
|
||
Scenario: Attempt to bind an application with label selector and name to a service |
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.
please rephrase to include the outcome as well.. in this case service binding will be unsuccessful, right?
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. af6e990
|
||
Then jq ".status.conditions[] | select(.type=="CollectionReady").status" of Service Binding "service-binding-a-s-e" should be changed to "True" | ||
And jq ".status.conditions[] | select(.type=="InjectionReady").status" of Service Binding "service-binding-a-s-e" should be changed to "False" | ||
And jq ".status.conditions[] | select(.type=="InjectionReady").reason" of Service Binding "service-binding-a-s-e" should be changed to "ApplicationNotFound" |
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.
IMHO - ApplicationNotFound
is not correct reason - simply we do not allow name and label to cooexist - Hence, something like InvalidApplicationReference
should be more appropriate.
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 need some more time to figure out how to introduce a new failure reason.
@baijum use |
Done. ee27a52 |
I will work on this. |
} | ||
|
||
for index, _ := range objList.Items { | ||
i.applications = append(i.applications, &application{gvr: gvr, persistedResource: &(objList.Items[index]), bindingPath: i.serviceBinding.Spec.Application.BindingPath}) |
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 persistedResource
value (address of the item) is taken using the index.
Ref. https://play.golang.org/p/ePs7st6f91g
Expect(applications[0].Resource()).To(Equal(u1)) | ||
Expect(applications[0].ContainersPath()).To(Equal(expectedContainerPath)) | ||
Expect(applications[1].Resource()).To(Equal(u2)) | ||
Expect(applications[1].ContainersPath()).To(Equal(expectedContainerPath)) |
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 am not sure if we can/should really assert on the order of returned application instances. What is only interesting for us that the number of returned instances is 2 and one of them is u2
and another one is u2
, it order in the slice is not important. If the order changes suddenly for some reason (say fake client implementation changes), the test would fail.
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.
Order is not reliable and not part of the API.
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.
Check Omega assertion library.
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. 52aca7f
if i.serviceBinding.Spec.Application.Name != "" && | ||
i.serviceBinding.Spec.Application.LabelSelector != nil && | ||
i.serviceBinding.Spec.Application.LabelSelector.MatchLabels != nil { | ||
return nil, errs.New("Both label selector and name for application should not be used") |
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.
we can extract error into a var that can be used later in test assertion - instead of string copying.
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.
Created separate issue: #974
u := &unstructured.Unstructured{} | ||
u.SetName("app") | ||
u.SetNamespace(sb.Namespace) | ||
u.SetGroupVersionKind(schema.GroupVersionKind{Group: "app", Version: "v1", Kind: "Foo"}) | ||
u.SetLabels(map[string]string{"env": "stage"}) |
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.
we do not need to create any app object at all, since we are going to fail earlier. Omitting this will also implicitly check that we do not try to access the cluster for this scenario.
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 code has been removed. Ref. #974
_, err := ctx.Applications() | ||
Expect(err).To(HaveOccurred()) | ||
// FIXME: This is not the expected error message | ||
Expect(err.Error()).To(ContainSubstring("no kind is registered for the type v1.DeploymentList in scheme")) |
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 error should be the same as the one you put in line 263: "Error listing foo"
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.
For some reason, that error message is not coming. I have used client.PrependReactor
(line 261-264) as per earlier suggestion, so the listing failed as expected.
I tried to use unstructured.Unstructured{}
instead of v1.DeploymentList{}
, but that also seems to be not working. I will come back to this issue once everything else finished.
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. 8229485
Done. 53bb65a |
client := fake.NewSimpleDynamicClient(runtime.NewScheme()) | ||
client.PrependReactor("list", "foos", | ||
func(action testing.Action) (handled bool, ret runtime.Object, err error) { | ||
return true, nil, e.New("Error listing foo") |
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.
DRY: please extract error in a variable and use it later in the assertion
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. 5e91f6d
|
||
_, err := ctx.Applications() | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err.Error()).To(ContainSubstring("Error listing foo")) |
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 error we get must be the one we got from the client, and the message should be the identical. Such assertion assures that the error is propagated to the caller:
Expect(err).To(Equal(expectedError))
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. 5e91f6d
@@ -122,6 +262,7 @@ var _ = Describe("Context", func() { | |||
Expect(err).To(HaveOccurred()) | |||
Expect(errors.IsNotFound(err)).To(BeTrue()) | |||
}) | |||
|
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.
please revert unrelated whitespace change.
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. 5e91f6d
@then(u'The application env var "{name}" has value "{value}" in both apps') | ||
def check_env_var_value_in_both_apps(context, name, value): | ||
found = polling2.poll(lambda: context.application1.get_env_var_value(name) == value, step=5, timeout=400) | ||
assert found, f'Env var "{name}" should contain value "{value}" in {context.application1}' |
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.
if we reach this line, then found
is true
always - otherwise Timeout exception occurs. Hence, this and the similar line at line 108 can be removed.
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. 5e91f6d
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.
+1 after having required CI jobs passing. Please also squash the commits.
6023281
to
0676cc0
Compare
- Fix `.spec.application.name` field becoming mandatory - If more than one application is selected through label selectors, all will be considered for binding Fixes redhat-developer#965 Signed-off-by: Baiju Muthukadan <baiju.m.mail@gmail.com>
/lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: pedjak 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 |
/test 4.8-acceptance |
.spec.application.name
field becoming mandatoryFixes #965
InvalidApplicationReference
reason and corresponding code and testsshould return an error if application list returns error