Skip to content
This repository has been archived by the owner on Jun 26, 2024. It is now read-only.

Fix crash when service annotation with sliceOfMaps elements has invalid sourceValue #1015

Conversation

sadlerap
Copy link
Contributor

@sadlerap sadlerap commented Sep 7, 2021

Motivation

When using the sliceOfMaps elementType in a service binding annotation, the operator panics when the specified sourceValue does not exist in the underlying maps, although the expected behavior would be to inform the user the binding has an error through conditions.

Changes

This PR changes the current behavior by verifying that the extracted value is present, and reports an error in the service binding in the case that the sourceValue key isn't present.

Replaces #993 and fixes #967.

Testing

See #967 for reproducibility details.

@sadlerap sadlerap force-pushed the service-binding-operator-967 branch 2 times, most recently from acd21dc to ba78d29 Compare September 7, 2021 18:14
@codecov
Copy link

codecov bot commented Sep 7, 2021

Codecov Report

Merging #1015 (ba78d29) into master (b21b536) will increase coverage by 0.25%.
The diff coverage is 100.00%.

❗ Current head ba78d29 differs from pull request most recent head 139a4b8. Consider uploading reports for the commit 139a4b8 to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1015      +/-   ##
==========================================
+ Coverage   56.62%   56.87%   +0.25%     
==========================================
  Files          28       28              
  Lines        1547     1556       +9     
==========================================
+ Hits          876      885       +9     
  Misses        555      555              
  Partials      116      116              
Impacted Files Coverage Δ
pkg/reconcile/pipeline/handler/collect/impl.go 82.50% <100.00%> (+1.04%) ⬆️
apis/binding/v1alpha1/servicebinding_types.go 35.29% <0.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b21b536...139a4b8. Read the comment docs.

@sadlerap sadlerap changed the title Service binding operator 967 Fix crash when sliceOfMaps has invalid value Sep 8, 2021
@sadlerap sadlerap changed the title Fix crash when sliceOfMaps has invalid value Fix crash when service annotation of sliceOfMaps has invalid sourceValue Sep 8, 2021
@sadlerap sadlerap changed the title Fix crash when service annotation of sliceOfMaps has invalid sourceValue Fix crash when service annotation with sliceOfMaps elements has invalid sourceValue Sep 8, 2021
serviceContent map[string]interface{}
}

It("should set an error condition",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be actually added around this line https://github.com/redhat-developer/service-binding-operator/blob/master/pkg/reconcile/pipeline/handler/collect/impl_test.go#L390

The test name should describe the fixture and the expected outcome - "should set an error condition" does not say anything about under what condition and kind of error.


It("should set an error condition",
func() {
tc := testCase{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is any particular reason why use a new struct here, instead of just defining serviceContent variable directly and using it throughout the test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not particularly; there used to be more in this struct, but most of it got removed as a result of refactoring, leaving only this. I'll take it out.

service := mocks.NewMockService(mockCtrl)
serviceResource := &unstructured.Unstructured{Object: tc.serviceContent}

ctx.EXPECT().Services().Return([]pipeline.Service{service}, nil).MinTimes(1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MinTimes(1) is default if omitted, hence I would remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need MinTimes(1) since we're asserting that we call Services() at least once, not exactly once. Default behavior appears to be asserting that it gets called exactly once.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, I do think that we assert that we call it only once, and we will do that when we remove collect.BindingDefinitions(ctx) from the test.

pkg/reconcile/pipeline/handler/collect/impl_test.go Outdated Show resolved Hide resolved
@@ -542,3 +542,49 @@ Feature: Bind an application to a service using annotations
"""
Then Service Binding "binding-app-via-gvk" is ready
And The application env var "BACKEND_HOST_INTERNAL_DB" has value "internal.db.stable.example.com"

Scenario: Bind application to service with invalid sourceValue annotations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the scenario description suggests that the binding is performed, but actually it is not. Hence, we should reflect that, e.g.

"Application cannot be bound to service containing annotation with an invalid sourceValue value"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6054ca0.

"""
Then Service Binding CollectionReady.status is "False"
And Service Binding CollectionReady.reason is "ValueNotFound"
And Service Binding CollectionReady.message is "Value for key webarrows_primary not found"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

acceptance tests are failing on CI on this assertion, please double check what is going on.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked into the artifacts of the failing without-olm job and found that this step is failing because the message is actually Value for key webarrows_secondary not found:

$ cat servicebindings.binding.operators.coreos.com.yaml| yq -r '.items[] | select(.metadata.name == "bindapptoserviceannotations-546").status.conditions[] | select(.type == "CollectionReady")'

{
  "lastTransitionTime": "2021-09-08T17:15:27Z",
  "message": "Value for key webarrows_secondary not found",
  "reason": "ValueNotFound",
  "status": "False",
  "type": "CollectionReady"
}

For comparison by checking the artifacts of the passed with-olm job the message is correct:

$ cat servicebindings.binding.operators.coreos.com.yaml| yq -r '.items[] | select(.metadata.name == "bindapptoserviceannotations-546").status.conditions[] | select(.type == "CollectionReady")'
{
  "lastTransitionTime": "2021-09-08T17:15:50Z",
  "message": "Value for key webarrows_primary not found",
  "reason": "ValueNotFound",
  "status": "False",
  "type": "CollectionReady"
}

Copy link
Contributor

@pmacik pmacik Sep 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like whatever is going through .spec.connections map keys does so in a different (perhaps random) order for each of the above cases

@pedjak pedjak removed the request for review from isutton September 9, 2021 07:44
@sadlerap sadlerap force-pushed the service-binding-operator-967 branch 2 times, most recently from 4d1d13f to 6054ca0 Compare September 9, 2021 20:19
@sadlerap
Copy link
Contributor Author

sadlerap commented Sep 9, 2021

/retest

1 similar comment
@pedjak
Copy link
Contributor

pedjak commented Sep 10, 2021

/retest

service := mocks.NewMockService(mockCtrl)
serviceResource := &unstructured.Unstructured{Object: tc.serviceContent}

ctx.EXPECT().Services().Return([]pipeline.Service{service}, nil).MinTimes(1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, I do think that we assert that we call it only once, and we will do that when we remove collect.BindingDefinitions(ctx) from the test.

Copy link
Contributor

@pedjak pedjak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, apart from some comments on the introduced unit test. A few simplifications/test name improvements should be made to improve readability and maintainability. If this can be done quickly, I would suggest to do it then within this PR, otherwise, we should perform in the followup PR.

service.EXPECT().BindingDefs().DoAndReturn(func() []binding.Definition { return bindingDefs })
service.EXPECT().Resource().Return(serviceResource)

definition.EXPECT().Apply(serviceResource).Return(value, nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given that we really mock this call, serviceResource value could be further simplified and reduce even to something like unstructured.Unstructured{}

ctx.EXPECT().Services().Return([]pipeline.Service{service}, nil)

bindingDefs := []binding.Definition{definition}
service.EXPECT().BindingDefs().DoAndReturn(func() []binding.Definition { return bindingDefs })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these two lines could be collapsed in something like:

service.EXPECT().BindingDefs().Return(bindingDefs)

Comment on lines 392 to 402
var (
mockCtrl *gomock.Controller
)

BeforeEach(func() {
mockCtrl = gomock.NewController(GinkgoT())
})

AfterEach(func() {
mockCtrl.Finish()
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not needed because they are already declared in the outer context. With that, we can even remove introduced Context, because it does not contain any additional customizations.

mockCtrl.Finish()
})

It("should set an error condition",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so, at the end, this test is not about the invalid sourceValue - it is certainly triggered by such annotation from user side, but here we have an unit test, and the trigger is nil value of collected binding. We should reflect that in the test name. Also, the outcome of this tests is not only to set the right error condition, we also indicate that processing should be stopped and not retried at all.

@sadlerap
Copy link
Contributor Author

/retest

)

A service can have a sliceOfMaps-based element annotation.  For example:

    service.binding/java-maven_port: path={.spec.ports},elementType=sliceOfMaps,sourceKey=name,sourceValue=asdf

When sourceValue is incorrect, the operator would panic, since it didn't
verify that the sourceValue was incorrect.  Now, it sets an error
condition in the reconciler (which propogates to the binding status).

Signed-off-by: Andy Sadler <ansadler@redhat.com>
@pedjak
Copy link
Contributor

pedjak commented Sep 10, 2021

/lgtm
/approve

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Sep 10, 2021

[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 /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sadlerap
Copy link
Contributor Author

/retest

@openshift-merge-robot openshift-merge-robot merged commit 252987b into redhat-developer:master Sep 10, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

panic on invalid sourceValue
4 participants