I'm in the middle of implementing a new EnvironmentRepository and so far it's been easy enough extending from the base class and looking at the examples of existing implementations.

One issue I'm having is finding a way to add/extend spring-cloud-config-monitor so that my new EnvironmentRepository can also use the /monitor endpoint to refresh and automatically notify the consumers about new configuration. I tried creating a new BasePropertyPathNotificationExtractor but so far I can't get things wired so that the PropertyPathEndpoint can be called.

Documentation on how this is done or a sample bit of code would be extremely helpful to those extending spring-cloud-config. I suppose I can just create my own /my-monitor endpoint and publish the event like so?

this.applicationEventPublisher.publishEvent(new RefreshRemoteApplicationEvent(this, this.busId, service));

Comment From: ryanjbaxter

So you are creating a Bean for your BasePropertyPathNotificationExctractor and its not present in the list here? https://github.com/spring-cloud/spring-cloud-config/blob/9c695648be6a88c232ed7792759fc9adfad82c2d/spring-cloud-config-monitor/src/main/java/org/springframework/cloud/config/monitor/EnvironmentMonitorAutoConfiguration.java#L54

Comment From: Alos

Yes. It looks like I was able to register my extractor effectively, the issue might be further down the path. Now debugging the notifyByPath and the bus configuration.

Comment From: ryanjbaxter

OK let me know.

Comment From: Alos

Ok I think I know what's happening, but I can't figure out the correct way of solving it.

My new NewDogPropertyPathNotificationExtractor is correctly added. The PropertyPathEndpoint's notifyByPath method correctly loops around and picks up my new extractor, the problem is that the new extractor uses BasePropertyPathNotificationExtractor's extract method that looks to see if the request contains a commits.

The update to the /monitor endpoint has no such information. Should I overwrite the extract method on my implementation or is there a way to trigger the refresh in another way (besides poking at the /monitor endpoint)?

Comment From: ryanjbaxter

I would just override the extract method in your implementation

Comment From: Alos

If I wanted to override the extractor to fire off events to a dog-service (so that it can perform its context refresh and ask for its configuration again) what would I need to return as a PropertyPathNotification? So far I've made sure that the PropertyPathNotification has "dog-service" in its path. This made the guessServiceName populate the services set with: ["dog:service", "dog-service"] this triggered the following logs:

MyCustomPropertyPathNotificationExtractor : paths [dog-service]
 o.s.c.c.monitor.PropertyPathEndpoint.    : Refresh for: dog:service
 o.s.c.s.m.DirectWithAttributesChannel.   : Channel 'unknown.channel.name' has 1 subscriber(s).
 o.s.cloud.bus.event.RefreshListener.     : Received remote refresh request.
 o.s.cloud.bus.event.RefreshListener      : Refresh not performed, the event was targeting dog:service:**
 o.s.c.c.monitor.PropertyPathEndpoint     : Refresh for: dog-service
 o.s.cloud.bus.event.RefreshListener      : Received remote refresh request.
 o.s.cloud.bus.event.RefreshListener      : Refresh not performed, the event was targeting dog-service:**
c.g.c.a.l.MyCustomEnvironmentRepository   : Fetching configuration for application: dog-service, profile: kubernetes,development, label: null

I don't see where the spring-cloud-monitor would take the refresh event and try to call the implementation to fire a refresh event into the service bus. Can you point me to the part of the code that does this? Do I need to craft the path in a specific way to make sure the Config Service fires the event?

Comment From: Alos

Actually I think its working!

Logs on the dog-service:

o.s.c.c.c.ConfigServerConfigDataLoader : Fetching config from server at : http://config-service:8080
o.s.c.c.c.ConfigServerConfigDataLoader : Located environment: name=dog-service, profiles=[kubernetes,development], label=null, version=null, state=null
o.s.cloud.bus.event.RefreshListener : Keys refreshed []
o.s.c.s.binder.DefaultBinderFactory : Retrieving cached binder: pubsub

My only concern is the double services caused by the guessServiceName and the fact that the Keys are not refreshed in the client it self :S

Comment From: ryanjbaxter

I would have to debug something to understand why there are two of the same service names in the list. If you can provide a sample that would help.

Comment From: Alos

I'm calling the /monitor endpoint like this:

curl -X POST http://localhost:8080/monitor -H "X-SOME-Event: push" -H "Content-Type: application/json" -d '{"updates":["dog-service"]}'

My extractor looks like this:

public class MyPropertyPathNotificationExtractor extends BasePropertyPathNotificationExtractor {

  @Override
  public PropertyPathNotification extract(
      MultiValueMap<String, String> headers, Map<String, Object> request) {
    if (Objects.nonNull(request) && request.get("updates") instanceof Collection) {
      Set<String> paths = new LinkedHashSet<>();
      List<String> updates = (List<String>) request.get("updates");
      if (!updates.isEmpty()) {
        paths.addAll(updates);
        log.info("paths {}", paths);
        return new PropertyPathNotification(paths.toArray(new String[0]));
      }
    }
    return null;
  }

  @Override
  protected boolean requestBelongsToMyRepoManager(MultiValueMap<String, String> headers) {
    log.info("Refreshing repo");
    return "push".equals(headers.getFirst("X-SOME-Event"));
  }
}

Basically the MyPropertyPathNotificationExtractor returns a PropertyPathNotification that has "dog-service" it its paths array.

From there the guessServiceName somehow adds ["dog:service", "dog-service"]. This is because in:

https://github.com/spring-cloud/spring-cloud-config/blob/8493efb8457b4107c4bfa63a0fc45b0ba6b98756/spring-cloud-config-monitor/src/main/java/org/springframework/cloud/config/monitor/PropertyPathEndpoint.java#L105

it goes into the while loop and then into the:

if ("application".equals(name)) {
    services.add("*:" + profile);
}

Since our applications are running in K8s we named them foo-service i.e cat-service, dog-service, duck-service, etc.

Comment From: ryanjbaxter

I really dont understand where "dog:service" is coming from.guessServiceNameshould produce a set[dog-service, dog]if path isdog-service`. Sorry I am just not understanding whats going on.

Comment From: spring-cloud-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: spring-cloud-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.