Skip to content
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

Service key support #292

Conversation

gberche-orange
Copy link
Contributor

@gberche-orange gberche-orange commented Nov 8, 2019

This PR add ability for ServiceInstanceAppBindingWorkflow impls to use service keys to expose some backing services credentials (see #285 for more background about related use-case). It includes CT and AT.

Other improvements were added to the AT:

  • documentation enhancements
  • logging deployed broker recents logs

@gberche-orange
Copy link
Contributor Author

Started rebasing and fixing new PMD and checkstyle rules from #291 Still some checkstyle fixes to be brought

Original PR before is available at https://github.com/orange-cloudfoundry/osb-cmdb-spike/tree/service-key-support-before-spring-style-rebase

@Albertoimpl
Copy link
Member

Thanks for the contribution, would you mind rebasing this PR @gberche-orange? Thanks!

@gberche-orange gberche-orange force-pushed the service-key-support branch 2 times, most recently from c8c8787 to aba42e7 Compare December 16, 2019 22:14
@gberche-orange
Copy link
Contributor Author

@Albertoimpl the PR was rebased against master and adapted to new checkstyles and pmd rules. I'm looking forward to your review.

@@ -28,4 +29,15 @@

Flux<String> deleteServiceInstance(List<BackingService> backingServices);

/**
* Create service keys
* @return A flux of credentials associated with each created service key
Copy link
Member

Choose a reason for hiding this comment

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

Please, delete comment to be consistent with the other methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As the content of the returned value is assigned deep in CloudFoundryAppDeployer, and felt not that intuitive obvious to newcomers, I rather added javadoc on all methods of the class as to still preserve class consistency. Please let me know if you have a better suggestion on how to remove ambiguity to code readers on the intent and content of the returned type.


/**
* Delete service keys
* @return a Flux of service key names that were deleted
Copy link
Member

Choose a reason for hiding this comment

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

Please, delete comment to be consistent with the other methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Albertoimpl
Copy link
Member

Thanks a lot for the contribution! I don't see any problem with the implementation nor with the way it is implemented, apart from the lack of ATs and CTs that should be included and the picky notes about the comments I left.

There are a couple of things that do not feel right but I am happy to re-evaluate them in a future release when we start implementing the k8s support.

  • You can achieve this behaviour by just implementing a CreateServiceInstanceWorkflow.
  • In a k8s world, there is not a direct mapping to a Service Key, so leaking that name, that is only CF-related, as part of the AppDeployer interface, which is a generic one, doesn't feel right.

As said, if you add the ATs and CTs, I am happy with including it. Thanks a lot for the great work!

@gberche-orange
Copy link
Contributor Author

Thanks @Albertoimpl for your review. I'll try working on providing the CT and AT now that I see there is interest.

You can achieve this behaviour by just implementing a CreateServiceInstanceWorkflow.

Actually, I'm currently using the service key support in a custom CreateServiceInstanceAppBindingWorkflow, see osbcmdb/AppDeploymentCreateServiceBindingWorkflow.java. I first considered implementing this without SCAB support, but this required to duplicate some of the existing beans such as DefaultBackingServicesProvisionService and CloudFoundryAppDeployer, so I'm happy if this can make it into SCAB and be useful to others.

In a k8s world, there is not a direct mapping to a Service Key, so leaking that name, that is only CF-related, as part of the AppDeployer interface, which is a generic one, doesn't feel right.

Yes. I understand that the service key could map to a K8S service cat ServiceBinding and fetching credentials from the associated Secret, see https://kubernetes.io/docs/concepts/extend-kubernetes/service-catalog/#mapping-the-connection-credentials These pieces of information are stored in secrets that the application in the cluster can access and use to connect directly with the managed service.

So potentially, a K8S-deployer implementation of the service-key methods could be brought in the future. I do agree that then the name "ServiceKey" could be more generic such as "ServiceCredentials".

@Albertoimpl
Copy link
Member

Great to hear!

Thanks @Albertoimpl for your review. I'll try working on providing the CT and AT now that I see there is interest.

Looking forward to reviewing them. I typically start working always with an AT, then a CT and then I start coding the rest.

DefaultBackingServicesProvisionService and CloudFoundryAppDeployer, so I'm happy if this can make it into SCAB and be useful to others.

Perfect! Thanks for contributing. I am sure this will benefit others.

So potentially, a K8S-deployer implementation of the service-key methods could be brought in the future. I do agree that then the name "ServiceKey" could be more generic such as "ServiceCredentials".

It would be ideal if you made this rename.

Again, thanks a lot for your contributions!

@royclarkson royclarkson added the blocked Waiting for further information or feedback label Jan 27, 2020
gberche-orange added a commit to orange-cloudfoundry/osb-cmdb that referenced this pull request Mar 13, 2020
@gberche-orange gberche-orange force-pushed the service-key-support branch 4 times, most recently from 630dae0 to 7b412e5 Compare March 13, 2020 21:32
@gberche-orange
Copy link
Contributor Author

@Albertoimpl Sorry for long silence on this PR. I've rebased the PR and added component test covering service key usage, and addressed suggested changes in the review. Can you please have a new look ?

Note that I'm struggling with checkstyle import rule on https://github.com/orange-cloudfoundry/osb-cmdb-spike/blob/5016f4f5b1bd28d7ba9066bdfe3bc766b8ce397f/spring-cloud-app-broker-integration-tests/src/test/java/org.springframework.cloud.appbroker/integration/CreateBindingWithServiceKeyComponentTest.java#L54

  • Besides, gradle checkstyle fails on my development box with symptom
    Unable to create Root Module: config {.../src/checkstyle/checkstyle.xml}, which looks similar to https://stackoverflow.com/questions/49645147/checkstyle-unable-to-create-root-module which I need to pursue. Any hint on fixing this would help, as waiting for circleci builds feedback implies a 15 min feedback time.
  • I tried to follow the CONTRIBUTING.adoc which recommends usage of the eclipse code formatter, and applied the intellij "optimize import" action. Is there other automated import formatting that the app broker team uses within intellij ?

I'll now try to provide an acceptance test. Is there a way I would be able see test results associated with the PR for acceptance tests (such as making the associated concourse pipeline public or adding 3rd party contributors as viewers) ?

@gberche-orange
Copy link
Contributor Author

gberche-orange commented Mar 16, 2020

@Albertoimpl

The AT asks for a service key on the brokered service instance (i.e. an OSB service binding call with an empty appId in the bindResource), hence requires a service binding workflow. I have therefore made this workflow available in core (for integration-tests and acceptance-tests) to use, and leave it up to app broker users to in their spring configuration classes. This avoids having service key workflow fixtures in both integration and acceptance tests.

@gberche-orange
Copy link
Contributor Author

gberche-orange commented Mar 17, 2020

Identified steps for contributing the AT

  • set up the local environment to run the test
    • Refine the documentation which is missing the required authorities for the UAA client
  • execute existing test cases
  • develop new test
    • handle clean up of provisioned element on test failures
      • reverse engineer and document element provisionned
        • add debugging traces
      • document clean up script manually invoked
      • improve the CloudFoundryAcceptanceTest to be resilient to failed/interrupted tests that did not execute cleanup
    • troubleshoot failed test
      • access broker logs
      • configure broker logs verbosity
    • Add support for service keys in assertions
    • configure broker used in acceptance test with service binding workflow
      • understand how/when NoOp*Workflow in acceptance-tests are used by the app broker: accept() clause filters the service plan Id
    • Use a backing service which support service keys and returns credentials
      • replace NoOpServiceInstanceBindingService with service binding workflows
  • execute the new test to get test status
    • import concourse ci pipeline

@Albertoimpl
Copy link
Member

Thanks a lot for the contributions, it is looking great!
We added a different style guide than the documented in CONTRIBUTING, sorry about that, we are going to fix it: #345 and you can find all the check styles over here: https://github.com/spring-cloud/spring-cloud-app-broker/tree/master/src

Regarding the ATs, we have them ready to be public, we need to move the pipeline to a public concourse instance: #346
However, you should be able to run them in your own environments. Let me know if you find any issues with that.

Thanks!

from dot-separated package names to slash-separated package names
…loaded core classes

CreateBindingWithServiceKeyComponentTest Test OK
…loaded core classes

CreateBindingWithServiceKeyComponentTest Test OK
…pDeploymentCreateServiceInstanceWorkflow regarding brokered services without backing apps
See related #344
- service instance unprovisionning now deletes backing apps and service instances that are described in current configuration
- previous behavior of looking up & deleting backing apps and associated service bindings
- cleanup comments
- disable deprecated test case
- update component tests stubs

See related #344
- service instance unprovisionning now deletes backing apps and service instances that are described in current configuration
- previous behavior of looking up & deleting backing apps and associated service bindings
- Added CreateInstanceWithOnlyServicesAcceptanceTest to support #287 and #344
- Added CreateInstanceWithBackingServiceKeysAcceptanceTest to support #292. Still failing (ServiceKeyCreateServiceBindingWorkflow bean is not shadowed by NoOpServiceInstanceBindingService)
   - Added ServiceKeyCreateServiceBindingWorkflow and ServiceKeyDeleteServiceBindingWorkflow to AppBrokerApplication
   - Add service key assertion support in CloudFoundryService

- Refined CloudFoundryAcceptanceTest to now log the app broker recent logs when debug mode is enable
- Refined default logging levels
…anceAppBindingWorkflow which only accepts the backing service plan.

This enables backing services to have controlled service binding behavior on which acceptance tests can rely on.
This is consistent with the approach used for service instances.
…ng :-)

Adjusted log level to support debugging of the test in a CI/CD environment without interactive debugging capabilities
…small nulls

Smart null provide friendly exception on unstubbed methods such as

org.mockito.exceptions.verification.SmartNullPointerException:
You have a NullPointerException here:
-> at reactor.core.publisher.Mono.subscribe(Mono.java:4105)
because this method call was *not* stubbed correctly:
-> at org.springframework.cloud.appbroker.workflow.instance.AppDeploymentDeleteServiceInstanceWorkflow.lambda$deleteBackingServices$0(AppDeploymentDeleteServiceInstanceWorkflow.java:72)
targetService.addToBackingServices(
    [BackingService{serviceInstanceName='my-service-instance', name='my-service', plan='a-plan', parameters={}, properties={}, parametersTransformers=[], rebindOnUpdate=false}],
    org.springframework.cloud.appbroker.deployer.TargetSpec@e67e89f4,
    "service-instance-id"
);

	at reactor.core.publisher.Mono.subscribe(Mono.java:4105)
	at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:188)
	at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2199)
	at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribe(MonoFlatMapMany.java:134)
	at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54)
	at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
	at reactor.core.publisher.Flux.subscribe(Flux.java:8174)
	at reactor.core.publisher.FluxConcatArray$ConcatArraySubscriber.onComplete(FluxConcatArray.java:207)
	at reactor.core.publisher.FluxConcatArray.subscribe(FluxConcatArray.java:80)
	at reactor.core.publisher.Mono.subscribe(Mono.java:4105)
	at reactor.test.DefaultStepVerifierBuilder$DefaultStepVerifier.toVerifierAndSubscribe(DefaultStepVerifierBuilder.java:867)
	at reactor.test.DefaultStepVerifierBuilder$DefaultStepVerifier.verify(DefaultStepVerifierBuilder.java:823)
	at reactor.test.DefaultStepVerifierBuilder$DefaultStepVerifier.verify(DefaultStepVerifierBuilder.java:815)
	at reactor.test.DefaultStepVerifierBuilder.verifyComplete(DefaultStepVerifierBuilder.java:682)
	at org.springframework.cloud.appbroker.workflow.instance.AppDeploymentDeleteServiceInstanceWorkflowTest.deleteServiceInstanceSucceeds(AppDeploymentDeleteServiceInstanceWorkflowTest.java:165)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:132)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:124)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:74)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:104)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:202)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at java.util.ArrayList.forEach(ArrayList.java:1257)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at java.util.ArrayList.forEach(ArrayList.java:1257)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
	at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
	at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)
Removed NoOpServiceInstanceBindingService replaced by NoBinding worflows instead
@gberche-orange
Copy link
Contributor Author

This osb-cmdb related feature seems to not make much sense into app-broker use-cases. Closing it.

Thanks however @Albertoimpl for your past review and support on this PR !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked Waiting for further information or feedback
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants