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

Add YAML configuration files for micro services #8

Merged
merged 9 commits into from
Apr 19, 2017

Conversation

jonathangreen
Copy link
Contributor

@jonathangreen jonathangreen commented Apr 17, 2017

GitHub Issue

Part of:
Islandora/documentation#603

What does this Pull Request do?

This PR does a few things:

  • It puts the service providers in their own provider subdirectory
  • It updates the IslandoraServiceProvider to be more idiomatic in how it handles configuration loading etc.
  • It adds a new service provider YamlConfigServiceProvider that loads configuration from YAML files and registers them as properties on the container.

How should this be tested?

Should be tested along with the forthcoming PR to Crayfish to use these new services.

Interested parties

@Islandora-CLAW/committers

@@ -0,0 +1,91 @@
<?php
Copy link
Member

Choose a reason for hiding this comment

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

@jonathangreen did you do a mv or git mv here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you look here:
fc681e9

It is a rename.

Copy link
Member

Choose a reason for hiding this comment

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

ah, cool. I guess it's showing up differently in the diff, or I should just look at the commits themselves 😃

@codecov
Copy link

codecov bot commented Apr 17, 2017

Codecov Report

Merging #8 into master will increase coverage by 13.55%.
The diff coverage is 94.11%.

Impacted file tree graph

@@              Coverage Diff              @@
##             master       #8       +/-   ##
=============================================
+ Coverage     62.79%   76.34%   +13.55%     
- Complexity      104      114       +10     
=============================================
  Files             8        9        +1     
  Lines           344      372       +28     
=============================================
+ Hits            216      284       +68     
+ Misses          128       88       -40
Impacted Files Coverage Δ Complexity Δ
src/Syn/SettingsParser.php 90.98% <100%> (+0.98%) 47 <0> (+2) ⬆️
src/Provider/IslandoraServiceProvider.php 92.85% <92.85%> (ø) 4 <4> (?)
src/Provider/YamlConfigServiceProvider.php 95.45% <95.45%> (ø) 10 <10> (?)
src/Syn/JwtAuthenticator.php 97.61% <0%> (+1.19%) 23% <0%> (ø) ⬇️
src/CmdExecuteService.php 4.25% <0%> (+4.25%) 12% <0%> (ø) ⬇️
src/FedoraResourceConverter.php 25% <0%> (+25%) 3% <0%> (ø) ⬇️

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 bdd33fb...a6b4b6e. Read the comment docs.

  * Update IslandoraServiceProvider to be more idiomatic in the way
    it uses configuration variables.
  * Add a new YamlConfigServiceProvider to load configuration from
    YAML files for Crayfish services.
Copy link
Member

@whikloj whikloj left a comment

Choose a reason for hiding this comment

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

Not sure I understand the YamlConfigServiceProvider. I don't know where the crayfish configuration parameters would come from, and I'm not sure if I like all the configuration stuff being placed into the container along side all the actual services/providers...

Questions/comments below

return strtolower($container['crayfish.log.level']) == 'none' ? null : $container['crayfish.log.file'];
};
$container['monolog.level'] = function ($container) {
return $container['crayfish.log.level'];
Copy link
Member

Choose a reason for hiding this comment

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

We don't have a "crayfish" application that runs, so I think this should probably be $container['gemini.log.level'] or $container['houdini.log.level'] or perhaps I misunderstand the YamlConfigServiceProvider. But I am getting..

[Tue Apr 18 02:57:48 2017] PHP Fatal error:  Uncaught InvalidArgumentException: Identifier "crayfish.log.level" is not defined. in /home/ubuntu/all_claw/Crayfish/Gemini/vendor/pimple/pimple/src/Pimple/Container.php:96
Stack trace:
#0 /home/ubuntu/all_claw/Crayfish/Gemini/vendor/islandora/crayfish-commons/src/Provider/IslandoraServiceProvider.php(33): Pimple\Container->offsetGet('crayfish.log.le...')
#1 /home/ubuntu/all_claw/Crayfish/Gemini/vendor/pimple/pimple/src/Pimple/Container.php(113): Islandora\Crayfish\Commons\Provider\IslandoraServiceProvider->Islandora\Crayfish\Commons\Provider\{closure}(Object(Silex\Application))
#2 /home/ubuntu/all_claw/Crayfish/Gemini/vendor/silex/silex/src/Silex/Provider/MonologServiceProvider.php(94): Pimple\Container->offsetGet('monolog.logfile')
#3 /home/ubuntu/all_claw/Crayfish/Gemini/vendor/pimple/pimple/src/Pimple/Container.php(113): Silex\Provider\MonologServiceProvider->Silex\Provider\{closure}(Object(Silex\Application))
#4 /home/ubuntu/all_claw/Crayfish/Gemini/vendor/silex/silex/src/Silex/Provider/MonologServicePro in /home/ubuntu/all_claw/Crayfish/Gemini/vendor/pimple/pimple/src/Pimple/Container.php on line 96

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So YamlConfigServiceProvider puts all the configuration from a particular YAML file into a "namespace". The default namespace is crayfish. It then loads every key in the YAML file concatenating them together with dots.

So for example:

---
test:
  for: jared

Would make a new entry in the service container: crayfish.entry.for. This would have a value of jared. That is why all the configuration is loading from the crayfish namespace.

So for the issue you are having here is that the crayfish.log.level variable isn't defined on the container. It should be defined here. Do you have it defined in your config file?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I have that exact same file. Other than changing the db username/password. So there is clearly something else going on.

Copy link
Member

Choose a reason for hiding this comment

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

Ok so it seemed that the issue was that IslandoraServiceProvider is trying to use the crayfish.log.level when it is instantiated. Which is before the YamlConfigServiceProvider is registered and so there are no configuration options? But that seems like it would never have worked for you.

However, if I switch the order then I get errors around the missing definiton of $app['db'].

I'll try cleaning this out and start again.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was cleaning some things up last night at 10pm when I did the rebase and maybe I broke something. I'll give it another try now and make sure it still works for me. Sorry about that.

Copy link
Member

Choose a reason for hiding this comment

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

No worries, we should figure out how to set up a memory based backend and then we could write some functional tests to ensure these two services operate together consistently.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Did you test Houdini or Hypercube, or just Gemini? I just tried Houdini and Hypercube and they seem to be working for me. Having some DB issues with Gemini right now that I think are just VM issues, but I'm going to keep trying it.

Copy link
Member

Choose a reason for hiding this comment

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

I didn't, but I will.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just pushed a commit that should fix the DB settings for gemini. I did break them at the last moment last night.


// Configure external services
$container['monolog.logfile'] = function ($container) {
return strtolower($container['crayfish.log.level']) == 'none' ? null : $container['crayfish.log.file'];
Copy link
Member

Choose a reason for hiding this comment

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

I think this is the same issue as below.

use Symfony\Component\Yaml\Yaml;
use InvalidArgumentException;

class YamlConfigServiceProvider implements ServiceProviderInterface
Copy link
Member

Choose a reason for hiding this comment

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

Wondering if using the Symfony ConfigurationInterface might not be preferable? Allows us to easily set defaults and do configuration validation?

http://symfony.com/doc/current/components/config/definition.html

See also @acoburn's use in static-ldp.
https://github.com/trellis-ldp/static-ldp/blob/master/src/TrellisConfiguration.php

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See reply below.

@jonathangreen
Copy link
Contributor Author

jonathangreen commented Apr 18, 2017

Not sure I understand the YamlConfigServiceProvider. I don't know where the crayfish configuration parameters would come from, and I'm not sure if I like all the configuration stuff being placed into the container along side all the actual services/providers...

Silex ServiceProviders all load their configuration from the container in the way that we do in this pull request. I believe this is the "Silex way" to load configuration.

Pimple (the service container) also has a section on storing parameters in the service container, and gives documentation about it, so its not like its an unsupported use case.

Wondering if using the Symfony ConfigurationInterface might not be preferable? Allows us to easily set defaults and do configuration validation?

In the other ServiceProviders from Silex linked above, they load their default config into the container, and then the application using the ServiceProvider is free to override the settings by adding parameters to the container, this is the pattern I followed in this pull request.

I took a look at the Symfony ConfigurationInterface when starting this pull request, and it didn't seem like a good for for how Silex does things. We chose to use Silex because it is lighter weight then a full Symfony application, and I think we should follow the patterns they use. That said I have not used ConfigurationInterface extensively so maybe I am wrong.

@jonathangreen
Copy link
Contributor Author

jonathangreen commented Apr 18, 2017

Any other @Islandora-CLAW/committers have any opinions about this one? If we should use the Symfony ConfigurationInterface here or not?

public function register(Container $container)
{
// Register services we rely on
$container->register(new MonologServiceProvider());

Choose a reason for hiding this comment

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

I think in-service registering is discouraged in Silex. It runs, so not a bug, and yes, container implements register method, but this introduces hidden dependencies for IslandoraServiceProvider breaking dependency injection possibilities (There is a lot about the topic in google). The implementer of IslandoraServiceProvider, i guess crayfish? should be responsible for registering into Pimple those services we rely on. IslandoraServiceProvider should just issue alerts/throw exceptions in case the named containers are not present/not initialized plus provide inline documentation stating that it requires those.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The alternative here is that we register each component separately in each and every microservice within crayfish, which is much harder and less testable then registering a service within a service.

The point of this provider is the provide a way to bootstrap a container for a crayfish service, so we can keep each service relatively lean and share code. I could see this being discouraged in a service meant for a general purpose, but I think it is okay here, when we know we are going to be using the service from crayfish.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm generally of the opinion that micro services should be configured separately. That is, while sharing configuration makes some things easier, it is usually the wrong thing to do.

Choose a reason for hiding this comment

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

I disagree on those points Jonathan but again, I won't oppose, it is just not the silex way. Testing services that don't register their own dependencies is in fact easier. I get your point that this may not a general use service, ok, but imagine the case that another service (islandora one) requires access to database under different means or another related services uses monolog under a different configuration. By obscuring the dependencies in the code, an implementer could override (in a bad for us, but in an silex expected way) what you already build here at service provider (settings etc) and since the whole silex/pimple idea is based on lazy loading, means nothing is initialized, set, called until it is used it is complicated to define upfront which of the registrations will in fact override the other one.

use Pimple\ServiceProviderInterface;
use Silex\Provider\DoctrineServiceProvider;
use Silex\Provider\MonologServiceProvider;
use Silex\Provider\ServiceControllerServiceProvider;

Choose a reason for hiding this comment

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

Silex\Provider\ServiceControllerServiceProvider i not used here. ServiceProviderInterface comes from pimple

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason for hiding this comment

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

Jonathan, sorry those are new silex patterns for me. Why do you create new instance of a class that is made to be extended and does not much by itself? No critics, but I don't get 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.

I am following the documentation for the class.
https://silex.sensiolabs.org/doc/2.0/providers/service_controller.html

Choose a reason for hiding this comment

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

@jonathangreen that is only needed (at app level) to allow Controllers (route controllers, like the code that reacts to GET or POST) to be defined as services, its a way of overriding the normal controller resolver to allow classes to be used , stuff like this
$app->get('/ldp', "posts.controller:givememyJson");

I don't see that here. The documentation there is a bit misleading but if you look further down it expands to the controller notion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The point is that this service is bootstrapping the container for other crayfish apps, so it needs to load this service so that the apps don't. Thats the whole purpose of this.

@acoburn
Copy link
Contributor

acoburn commented Apr 18, 2017

I am in favor of using the Symfony ConfigurationInterface

@whikloj
Copy link
Member

whikloj commented Apr 19, 2017

Ok, so this works for me now.

There are two issues that I think are still unresolved.

  1. The dependency injection question of registering the services inside services.
  2. The YamlConfigServiceProvider versus the Symfony ConfigurationInterface.

I also think the YamlConfigServiceProvider could probably be merged with IslandoraServiceProvider...but that is just my opinion.

We have a short timeline before IslandoraCon, so I'm going to merge this (and Islandora/Crayfish#27) but leave issue Islandora/documentation#603 open and add my opinions there.

I'll also open a new ticket around the "not registering services inside services" discussion.

Does that sound good to all the @Islandora-CLAW/committers?

@dannylamb
Copy link
Contributor

Sounds great @whikloj

@dannylamb
Copy link
Contributor

dannylamb commented Apr 19, 2017

I'm also adding to the CLAW call agenda if it's not already there, so we can discuss and move forward in a way that keeps everyone happy since I don't think all concerns have really been addressed.

@jonathangreen
Copy link
Contributor Author

Sounds great to me as well @whikloj. Thanks again for testing.

@whikloj
Copy link
Member

whikloj commented Apr 19, 2017

@acoburn haven't heard from you, so hopefully you are okay continuing this conversation on Islandora/documentation#603 and I'll merge this in the interim.

@whikloj whikloj merged commit fc3c832 into Islandora:master Apr 19, 2017
@acoburn
Copy link
Contributor

acoburn commented Apr 19, 2017

@whikloj yes, merge away!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants