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

Enable force refresh by forceRefresh query parameter #2402

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.server.support.AbstractScmAccessor;
import org.springframework.cloud.config.server.support.AbstractScmAccessorProperties;
import org.springframework.cloud.config.server.support.RequestContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;

Expand Down Expand Up @@ -57,11 +58,18 @@ public synchronized Environment findOne(String application, String profile, Stri

@Override
public synchronized Environment findOne(String application, String profile, String label, boolean includeOrigin) {
return findOne(application, profile, label, includeOrigin,
new RequestContext.Builder().forceRefresh(false).build());
}

@Override
public synchronized Environment findOne(String application, String profile, String label, boolean includeOrigin,
RequestContext ctx) {
NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(),
new NativeEnvironmentProperties(), this.observationRegistry);
Locations locations = getLocations(application, profile, label);
Locations locations = getLocations(application, profile, label, ctx);
delegate.setSearchLocations(locations.getLocations());
Environment result = delegate.findOne(application, profile, "", includeOrigin);
Environment result = delegate.findOne(application, profile, "", includeOrigin, ctx);
result.setVersion(locations.getVersion());
result.setLabel(label);
return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(), getUri());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.apache.commons.logging.LogFactory;

import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.server.support.RequestContext;
import org.springframework.core.OrderComparator;

/**
Expand Down Expand Up @@ -77,18 +78,25 @@ public Environment findOne(String application, String profile, String label) {

@Override
public Environment findOne(String application, String profile, String label, boolean includeOrigin) {
return findOne(application, profile, label, includeOrigin,
new RequestContext.Builder().forceRefresh(false).build());
}

@Override
public Environment findOne(String application, String profile, String label, boolean includeOrigin,
RequestContext ctx) {
Environment env = new Environment(application, new String[] { profile }, label, null, null);
if (this.environmentRepositories.size() == 1) {
Environment envRepo = this.environmentRepositories.get(0).findOne(application, profile, label,
includeOrigin);
includeOrigin, ctx);
env.addAll(envRepo.getPropertySources());
env.setVersion(envRepo.getVersion());
env.setState(envRepo.getState());
}
else {
for (EnvironmentRepository repo : environmentRepositories) {
try {
env.addAll(repo.findOne(application, profile, label, includeOrigin).getPropertySources());
env.addAll(repo.findOne(application, profile, label, includeOrigin, ctx).getPropertySources());
}
catch (Exception e) {
if (failOnError) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.springframework.cloud.config.environment.EnvironmentMediaType;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.cloud.config.server.support.PathUtils;
import org.springframework.cloud.config.server.support.RequestContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
Expand Down Expand Up @@ -106,32 +107,40 @@ public void setAcceptEmpty(boolean acceptEmpty) {

@GetMapping(path = "/{name}/{profiles:(?!.*\\b\\.(?:ya?ml|properties|json)\\b).*}",
produces = MediaType.APPLICATION_JSON_VALUE)
public Environment defaultLabel(@PathVariable String name, @PathVariable String profiles) {
return getEnvironment(name, profiles, null, false);
public Environment defaultLabel(@PathVariable String name, @PathVariable String profiles,
@RequestParam(defaultValue = "false") boolean forceRefresh) {
RequestContext ctx = new RequestContext.Builder().forceRefresh(forceRefresh).build();
return getEnvironment(name, profiles, null, false, ctx);
}

@GetMapping(path = "/{name}/{profiles:(?!.*\\b\\.(?:ya?ml|properties|json)\\b).*}",
produces = EnvironmentMediaType.V2_JSON)
public Environment defaultLabelIncludeOrigin(@PathVariable String name, @PathVariable String profiles) {
return getEnvironment(name, profiles, null, true);
public Environment defaultLabelIncludeOrigin(@PathVariable String name, @PathVariable String profiles,
@RequestParam(defaultValue = "false") boolean forceRefresh) {
RequestContext ctx = new RequestContext.Builder().forceRefresh(forceRefresh).build();
return getEnvironment(name, profiles, null, true, ctx);
}

@GetMapping(path = "/{name}/{profiles}/{label:.*}", produces = MediaType.APPLICATION_JSON_VALUE)
public Environment labelled(@PathVariable String name, @PathVariable String profiles, @PathVariable String label) {
return getEnvironment(name, profiles, label, false);
public Environment labelled(@PathVariable String name, @PathVariable String profiles, @PathVariable String label,
@RequestParam(defaultValue = "false") boolean forceRefresh) {
RequestContext ctx = new RequestContext.Builder().forceRefresh(forceRefresh).build();
return getEnvironment(name, profiles, label, false, ctx);
}

@GetMapping(path = "/{name}/{profiles}/{label:.*}", produces = EnvironmentMediaType.V2_JSON)
public Environment labelledIncludeOrigin(@PathVariable String name, @PathVariable String profiles,
@PathVariable String label) {
return getEnvironment(name, profiles, label, true);
@PathVariable String label, @RequestParam(defaultValue = "false") boolean forceRefresh) {
RequestContext ctx = new RequestContext.Builder().forceRefresh(forceRefresh).build();
return getEnvironment(name, profiles, label, true, ctx);
}

public Environment getEnvironment(String name, String profiles, String label, boolean includeOrigin) {
public Environment getEnvironment(String name, String profiles, String label, boolean includeOrigin,
RequestContext ctx) {
try {
name = normalize(name);
label = normalize(label);
Environment environment = this.repository.findOne(name, profiles, label, includeOrigin);
Environment environment = this.repository.findOne(name, profiles, label, includeOrigin, ctx);
if (!this.acceptEmpty && (environment == null || environment.getPropertySources().isEmpty())) {
throw new EnvironmentNotFoundException("Profile Not found");
}
Expand All @@ -153,16 +162,17 @@ private String normalize(String part) {

@GetMapping("/{name}-{profiles}.properties")
public ResponseEntity<String> properties(@PathVariable String name, @PathVariable String profiles,
@RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws IOException {
return labelledProperties(name, profiles, null, resolvePlaceholders);
@RequestParam(defaultValue = "true") boolean resolvePlaceholders,
@RequestParam(defaultValue = "false") boolean forceRefresh) throws IOException {
return labelledProperties(name, profiles, null, resolvePlaceholders, forceRefresh);
}

@GetMapping("/{label}/{name}-{profiles}.properties")
public ResponseEntity<String> labelledProperties(@PathVariable String name, @PathVariable String profiles,
@PathVariable String label, @RequestParam(defaultValue = "true") boolean resolvePlaceholders)
throws IOException {
@PathVariable String label, @RequestParam(defaultValue = "true") boolean resolvePlaceholders,
@RequestParam(defaultValue = "false") boolean forceRefresh) throws IOException {
validateProfiles(profiles);
Environment environment = labelled(name, profiles, label);
Environment environment = labelled(name, profiles, label, forceRefresh);
Map<String, Object> properties = convertToProperties(environment);
String propertiesString = getPropertiesString(properties);
if (resolvePlaceholders) {
Expand All @@ -173,16 +183,17 @@ public ResponseEntity<String> labelledProperties(@PathVariable String name, @Pat

@GetMapping("{name}-{profiles}.json")
public ResponseEntity<String> jsonProperties(@PathVariable String name, @PathVariable String profiles,
@RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws Exception {
return labelledJsonProperties(name, profiles, null, resolvePlaceholders);
@RequestParam(defaultValue = "true") boolean resolvePlaceholders,
@RequestParam(defaultValue = "false") boolean forceRefresh) throws Exception {
return labelledJsonProperties(name, profiles, null, resolvePlaceholders, forceRefresh);
}

@GetMapping("/{label}/{name}-{profiles}.json")
public ResponseEntity<String> labelledJsonProperties(@PathVariable String name, @PathVariable String profiles,
@PathVariable String label, @RequestParam(defaultValue = "true") boolean resolvePlaceholders)
throws Exception {
@PathVariable String label, @RequestParam(defaultValue = "true") boolean resolvePlaceholders,
@RequestParam(defaultValue = "false") boolean forceRefresh) throws Exception {
validateProfiles(profiles);
Environment environment = labelled(name, profiles, label);
Environment environment = labelled(name, profiles, label, forceRefresh);
Map<String, Object> properties = convertToMap(environment);
String json = this.objectMapper.writeValueAsString(properties);
if (resolvePlaceholders) {
Expand All @@ -204,16 +215,17 @@ private String getPropertiesString(Map<String, Object> properties) {

@GetMapping({ "/{name}-{profiles}.yml", "/{name}-{profiles}.yaml" })
public ResponseEntity<String> yaml(@PathVariable String name, @PathVariable String profiles,
@RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws Exception {
return labelledYaml(name, profiles, null, resolvePlaceholders);
@RequestParam(defaultValue = "true") boolean resolvePlaceholders,
@RequestParam(defaultValue = "false") boolean forceRefresh) throws Exception {
return labelledYaml(name, profiles, null, resolvePlaceholders, forceRefresh);
}

@GetMapping({ "/{label}/{name}-{profiles}.yml", "/{label}/{name}-{profiles}.yaml" })
public ResponseEntity<String> labelledYaml(@PathVariable String name, @PathVariable String profiles,
@PathVariable String label, @RequestParam(defaultValue = "true") boolean resolvePlaceholders)
throws Exception {
@PathVariable String label, @RequestParam(defaultValue = "true") boolean resolvePlaceholders,
@RequestParam(defaultValue = "false") boolean forceRefresh) throws Exception {
validateProfiles(profiles);
Environment environment = labelled(name, profiles, label);
Environment environment = labelled(name, profiles, label, forceRefresh);
Map<String, Object> result = convertToMap(environment);
if (this.stripDocument && result.size() == 1 && result.keySet().iterator().next().equals("document")) {
Object value = result.get("document");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.cloud.config.environment.PropertyValueDescriptor;
import org.springframework.cloud.config.server.encryption.EnvironmentEncryptor;
import org.springframework.cloud.config.server.support.RequestContext;

/**
* A delegating {@link EnvironmentRepository} that can decrypt the properties if an
Expand Down Expand Up @@ -61,7 +62,12 @@ public Environment findOne(String name, String profiles, String label) {

@Override
public Environment findOne(String name, String profiles, String label, boolean includeOrigin) {
Environment environment = this.delegate.findOne(name, profiles, label, includeOrigin);
return findOne(name, profiles, label, includeOrigin, new RequestContext.Builder().forceRefresh(false).build());
}

@Override
public Environment findOne(String name, String profiles, String label, boolean includeOrigin, RequestContext ctx) {
Environment environment = this.delegate.findOne(name, profiles, label, includeOrigin, ctx);
if (this.environmentEncryptors != null) {
for (EnvironmentEncryptor environmentEncryptor : environmentEncryptors) {
environment = environmentEncryptor.decrypt(environment);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.springframework.cloud.config.server.environment;

import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.server.support.RequestContext;

/**
* @author Dave Syer
Expand All @@ -30,4 +31,9 @@ default Environment findOne(String application, String profile, String label, bo
return findOne(application, profile, label);
}

default Environment findOne(String application, String profile, String label, boolean includeOrigin,
RequestContext ctx) {
return findOne(application, profile, label, includeOrigin);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ public class JGitEnvironmentProperties extends AbstractScmAccessorProperties
*/
private int refreshRate = 0;

/**
* Allow forceRefresh query parameter to force refresh the git repository regardless
* of refreshRate if true.
*/
private boolean allowForceRefresh = false;

/**
* Valid SSH private key. Must be set if ignoreLocalSshSettings is true and Git URI is
* SSH format.
Expand Down Expand Up @@ -189,6 +195,14 @@ public void setRefreshRate(int refreshRate) {
this.refreshRate = refreshRate;
}

public boolean getAllowForceRefresh() {
return this.allowForceRefresh;
}

public void setAllowForceRefresh(boolean allowForceRefresh) {
this.allowForceRefresh = allowForceRefresh;
}

public String getPrivateKey() {
return this.privateKey;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@

import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.config.server.support.GitCredentialsProviderFactory;
import org.springframework.cloud.config.server.support.RequestContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.UrlResource;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -96,6 +97,12 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
*/
private int timeout;

/**
* Allow forceRefresh query parameter to force refresh the git repository regardless
* of refreshRate if true.
*/
public boolean allowForceRefresh = false;

/**
* Time (in seconds) between refresh of the git repository.
*/
Expand Down Expand Up @@ -158,6 +165,7 @@ public JGitEnvironmentRepository(ConfigurableEnvironment environment, JGitEnviro
this.timeout = properties.getTimeout();
this.deleteUntrackedBranches = properties.isDeleteUntrackedBranches();
this.refreshRate = properties.getRefreshRate();
this.allowForceRefresh = properties.getAllowForceRefresh();
this.skipSslValidation = properties.isSkipSslValidation();
this.gitFactory = new JGitFactory(properties.isCloneSubmodules());
this.tryMasterBranch = properties.isTryMasterBranch();
Expand Down Expand Up @@ -196,6 +204,14 @@ public void setRefreshRate(int refreshRate) {
this.refreshRate = refreshRate;
}

public boolean getAllowForceRefresh() {
return this.allowForceRefresh;
}

public void setAllowForceRefresh(boolean allowForceRefresh) {
this.allowForceRefresh = allowForceRefresh;
}

public TransportConfigCallback getTransportConfigCallback() {
return this.transportConfigCallback;
}
Expand Down Expand Up @@ -254,19 +270,24 @@ public void setSkipSslValidation(boolean skipSslValidation) {

@Override
public synchronized Locations getLocations(String application, String profile, String label) {
return getLocations(application, profile, label, new RequestContext.Builder().forceRefresh(false).build());
}

@Override
public synchronized Locations getLocations(String application, String profile, String label, RequestContext ctx) {
if (label == null) {
label = this.defaultLabel;
}
String version;
try {
version = refresh(label);
version = refresh(label, ctx.getForceRefresh());
}
catch (Exception e) {
if (this.defaultLabel.equals(label) && JGitEnvironmentProperties.MAIN_LABEL.equals(this.defaultLabel)
&& tryMasterBranch) {
logger.info("Could not refresh default label " + label, e);
logger.info("Will try to refresh master label instead.");
version = refresh(JGitEnvironmentProperties.MASTER_LABEL);
version = refresh(JGitEnvironmentProperties.MASTER_LABEL, ctx.getForceRefresh());
}
else {
throw e;
Expand All @@ -289,11 +310,11 @@ public synchronized void afterPropertiesSet() throws Exception {
* @param label label to refresh
* @return head id
*/
public String refresh(String label) {
public String refresh(String label, boolean forceRefresh) {
Git git = null;
try {
git = createGitClient();
if (shouldPull(git)) {
if (shouldPull(git, forceRefresh)) {
FetchResult fetchStatus = fetch(git, label);
if (this.deleteUntrackedBranches && fetchStatus != null) {
deleteUntrackedLocalBranches(fetchStatus.getTrackingRefUpdates(), git);
Expand Down Expand Up @@ -467,11 +488,10 @@ private Ref checkout(Git git, String label) throws GitAPIException {
return checkout.call();
}

protected boolean shouldPull(Git git) throws GitAPIException {
protected boolean shouldPull(Git git, boolean forceRefresh) throws GitAPIException {
boolean shouldPull;

if (this.refreshRate < 0 || (this.refreshRate > 0
&& System.currentTimeMillis() - this.lastRefresh < (this.refreshRate * 1000))) {
if (!(this.allowForceRefresh && forceRefresh) && (this.refreshRate < 0 || (this.refreshRate > 0
&& System.currentTimeMillis() - this.lastRefresh < (this.refreshRate * 1000)))) {
return false;
}

Expand Down
Loading