Support for placeholders in git and file URIs
The default git repository and also the native one (local files)
now support placeholders for {application}, {profile} and
{label} in the URI (searchLocation for the native repository).
Fixes gh-257
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.config.server.environment;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -23,6 +24,7 @@ import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
@@ -48,6 +50,8 @@ public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository
|
||||
|
||||
private Map<String, PatternMatchingJGitEnvironmentRepository> repos = new LinkedHashMap<String, PatternMatchingJGitEnvironmentRepository>();
|
||||
|
||||
private Map<String, JGitEnvironmentRepository> placeholders = new LinkedHashMap<String, JGitEnvironmentRepository>();
|
||||
|
||||
public MultipleJGitEnvironmentRepository(ConfigurableEnvironment environment) {
|
||||
super(environment);
|
||||
}
|
||||
@@ -82,24 +86,72 @@ public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository
|
||||
@Override
|
||||
public Locations getLocations(String application, String profile, String label) {
|
||||
for (PatternMatchingJGitEnvironmentRepository repository : this.repos.values()) {
|
||||
Environment source = repository.findOne(application, profile, label);
|
||||
if (source != null) {
|
||||
return repository.getLocations(application, profile, label);
|
||||
if (repository.matches(application, profile, label)) {
|
||||
JGitEnvironmentRepository candidate = getRepository(repository,
|
||||
application, profile, label);
|
||||
Environment source = candidate.findOne(application, profile, label);
|
||||
if (source != null) {
|
||||
return repository.getLocations(application, profile, label);
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.getLocations(application, profile, label);
|
||||
JGitEnvironmentRepository candidate = getRepository(this,
|
||||
application, profile, label);
|
||||
if (candidate==this) {
|
||||
return super.getLocations(application, profile, label);
|
||||
}
|
||||
return candidate.getLocations(application, profile, label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label) {
|
||||
for (PatternMatchingJGitEnvironmentRepository repository : this.repos.values()) {
|
||||
Environment source = repository.findOne(application, profile, label);
|
||||
if (source != null) {
|
||||
return source;
|
||||
if (repository.matches(application, profile, label)) {
|
||||
JGitEnvironmentRepository candidate = getRepository(repository,
|
||||
application, profile, label);
|
||||
Environment source = candidate.findOne(application, profile, label);
|
||||
if (source != null) {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
JGitEnvironmentRepository candidate = getRepository(this,
|
||||
application, profile, label);
|
||||
if (candidate==this) {
|
||||
return super.findOne(application, profile, label);
|
||||
}
|
||||
return candidate.findOne(application, profile, label);
|
||||
}
|
||||
|
||||
return super.findOne(application, profile, label);
|
||||
private JGitEnvironmentRepository getRepository(JGitEnvironmentRepository repository,
|
||||
String application, String profile, String label) {
|
||||
if (!repository.getUri().contains("{")) {
|
||||
return repository;
|
||||
}
|
||||
String key = repository.getUri();
|
||||
if (application!=null) {
|
||||
key = key.replace("{application}", application);
|
||||
}
|
||||
if (profile!=null) {
|
||||
key = key.replace("{profile}", profile);
|
||||
}
|
||||
if (label!=null) {
|
||||
key = key.replace("{label}", label);
|
||||
}
|
||||
if (!this.repos.containsKey(key)) {
|
||||
this.placeholders.put(key, getRepository(repository, key));
|
||||
}
|
||||
return this.placeholders.get(key);
|
||||
}
|
||||
|
||||
private JGitEnvironmentRepository getRepository(JGitEnvironmentRepository source,
|
||||
String uri) {
|
||||
JGitEnvironmentRepository repository = new JGitEnvironmentRepository(null);
|
||||
File basedir = repository.getBasedir();
|
||||
BeanUtils.copyProperties(source, repository);
|
||||
repository.setUri(uri);
|
||||
repository.setBasedir(basedir);
|
||||
return repository;
|
||||
}
|
||||
|
||||
public static class PatternMatchingJGitEnvironmentRepository
|
||||
@@ -117,6 +169,18 @@ public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository
|
||||
setUri(uri);
|
||||
}
|
||||
|
||||
public boolean matches(String application, String profile, String label) {
|
||||
if (this.pattern == null || this.pattern.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PatternMatchUtils.simpleMatch(this.pattern,
|
||||
application + "/" + profile)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label) {
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ public class NativeEnvironmentRepository
|
||||
ConfigurableEnvironment environment = getEnvironment(profile);
|
||||
builder.environment(environment);
|
||||
builder.web(false).bannerMode(Mode.OFF);
|
||||
String[] args = getArgs(config, label);
|
||||
String[] args = getArgs(config, profile, label);
|
||||
// Explicitly set the listeners (to exclude logging listener which would change
|
||||
// log levels in the caller)
|
||||
builder.application()
|
||||
@@ -122,7 +122,22 @@ public class NativeEnvironmentRepository
|
||||
}
|
||||
List<String> output = new ArrayList<String>();
|
||||
for (String location : locations) {
|
||||
output.add(location);
|
||||
String value = location;
|
||||
if (application != null) {
|
||||
value = value.replace("{application}", application);
|
||||
}
|
||||
if (profile != null) {
|
||||
value = value.replace("{profile}", profile);
|
||||
}
|
||||
if (label != null) {
|
||||
value = value.replace("{label}", label);
|
||||
}
|
||||
if (!value.endsWith("/")) {
|
||||
value = value + "/";
|
||||
}
|
||||
if (isDirectory(value)) {
|
||||
output.add(value);
|
||||
}
|
||||
}
|
||||
for (String location : locations) {
|
||||
if (isDirectory(location) && StringUtils.hasText(label)) {
|
||||
@@ -160,7 +175,8 @@ public class NativeEnvironmentRepository
|
||||
.cleanPath(new File(normal.substring("file:".length()))
|
||||
.getAbsolutePath());
|
||||
}
|
||||
for (String pattern : getLocations(null, null, result.getLabel())
|
||||
String profile = result.getProfiles() == null ? null : StringUtils.arrayToCommaDelimitedString(result.getProfiles());
|
||||
for (String pattern : getLocations(result.getName(), profile, result.getLabel())
|
||||
.getLocations()) {
|
||||
if (!pattern.contains(":")) {
|
||||
pattern = "file:" + pattern;
|
||||
@@ -195,8 +211,9 @@ public class NativeEnvironmentRepository
|
||||
return result;
|
||||
}
|
||||
|
||||
private String[] getArgs(String config, String label) {
|
||||
private String[] getArgs(String application, String profile, String label) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
String config = application;
|
||||
if (!config.startsWith("application")) {
|
||||
config = "application," + config;
|
||||
}
|
||||
@@ -204,7 +221,7 @@ public class NativeEnvironmentRepository
|
||||
list.add("--spring.cloud.bootstrap.enabled=false");
|
||||
list.add("--encrypt.failOnError=" + this.failOnError);
|
||||
list.add("--spring.config.location=" + StringUtils.arrayToCommaDelimitedString(
|
||||
getLocations(null, null, label).getLocations()));
|
||||
getLocations(application, profile, label).getLocations()));
|
||||
return list.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@@ -234,8 +251,8 @@ public class NativeEnvironmentRepository
|
||||
}
|
||||
|
||||
private boolean isDirectory(String location) {
|
||||
return !location.endsWith(".properties") && !location.endsWith(".yml")
|
||||
&& !location.endsWith(".yaml");
|
||||
return !location.contains("{") && !location.endsWith(".properties")
|
||||
&& !location.endsWith(".yml") && !location.endsWith(".yaml");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class JGitEnvironmentRepositoryIntegrationTests {
|
||||
if (this.basedir.exists()) {
|
||||
FileUtils.delete(this.basedir, FileUtils.RECURSIVE);
|
||||
}
|
||||
ConfigServerTestUtils.deleteLocalRepo("config-copy");
|
||||
ConfigServerTestUtils.deleteLocalRepo("");
|
||||
}
|
||||
|
||||
@After
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.config.server.environment;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentRepository.PatternMatchingJGitEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.test.ConfigServerTestUtils;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class MultipleJGitEnvironmentUriTemplateRepositoryTests {
|
||||
|
||||
private StandardEnvironment environment = new StandardEnvironment();
|
||||
private MultipleJGitEnvironmentRepository repository = new MultipleJGitEnvironmentRepository(
|
||||
this.environment);
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
String defaultUri = ConfigServerTestUtils.prepareLocalRepo("config-repo");
|
||||
this.repository.setUri(defaultUri);
|
||||
this.repository.setRepos(createRepositories());
|
||||
}
|
||||
|
||||
private Map<String, PatternMatchingJGitEnvironmentRepository> createRepositories()
|
||||
throws Exception {
|
||||
String test1Uri = ConfigServerTestUtils.prepareLocalRepo("test1-config-repo");
|
||||
ConfigServerTestUtils.prepareLocalRepo("test2-config-repo");
|
||||
|
||||
Map<String, PatternMatchingJGitEnvironmentRepository> repos = new HashMap<>();
|
||||
repos.put("templates", createRepository("test", "*-config-repo",
|
||||
test1Uri.replace("test1-config-repo", "{application}")));
|
||||
return repos;
|
||||
}
|
||||
|
||||
private PatternMatchingJGitEnvironmentRepository createRepository(String name,
|
||||
String pattern, String uri) {
|
||||
PatternMatchingJGitEnvironmentRepository repo = new PatternMatchingJGitEnvironmentRepository();
|
||||
repo.setEnvironment(this.environment);
|
||||
repo.setName(name);
|
||||
repo.setPattern(new String[] { pattern });
|
||||
repo.setUri(uri);
|
||||
return repo;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultRepo() {
|
||||
Environment environment = this.repository.findOne("bar", "staging", "master");
|
||||
assertEquals(2, environment.getPropertySources().size());
|
||||
assertEquals(this.repository.getUri() + "/bar.properties",
|
||||
environment.getPropertySources().get(0).getName());
|
||||
assertVersion(environment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mappingRepo() {
|
||||
Environment environment = this.repository.findOne("test1-config-repo", "staging",
|
||||
"master");
|
||||
assertEquals(1, environment.getPropertySources().size());
|
||||
assertEquals(
|
||||
getUri("*").replace("{application}", "test1-config-repo")
|
||||
+ "/application.yml",
|
||||
environment.getPropertySources().get(0).getName());
|
||||
assertVersion(environment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void otherMappingRepo() {
|
||||
Environment environment = this.repository.findOne("test2-config-repo", "staging",
|
||||
"master");
|
||||
assertEquals(1, environment.getPropertySources().size());
|
||||
assertEquals(
|
||||
getUri("*").replace("{application}", "test2-config-repo")
|
||||
+ "/application.properties",
|
||||
environment.getPropertySources().get(0).getName());
|
||||
assertVersion(environment);
|
||||
}
|
||||
|
||||
private void assertVersion(Environment environment) {
|
||||
String version = environment.getVersion();
|
||||
assertNotNull("version was null", version);
|
||||
assertTrue("version length was wrong",
|
||||
version.length() >= 40 && version.length() <= 64);
|
||||
}
|
||||
|
||||
private String getUri(String pattern) {
|
||||
String uri = null;
|
||||
|
||||
Map<String, PatternMatchingJGitEnvironmentRepository> repoMappings = this.repository
|
||||
.getRepos();
|
||||
|
||||
for (PatternMatchingJGitEnvironmentRepository repo : repoMappings.values()) {
|
||||
String[] mappingPattern = repo.getPattern();
|
||||
if (mappingPattern != null && mappingPattern.length != 0) {
|
||||
uri = repo.getUri();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.server.environment.SearchPathLocator.Locations;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
@@ -43,7 +44,7 @@ public class NativeEnvironmentRepositoryTests {
|
||||
|
||||
@Test
|
||||
public void emptySearchLocations() {
|
||||
this.repository.setSearchLocations((String[])null);
|
||||
this.repository.setSearchLocations((String[]) null);
|
||||
Environment environment = this.repository.findOne("foo", "development", "master");
|
||||
assertEquals(2, environment.getPropertySources().size());
|
||||
}
|
||||
@@ -91,4 +92,48 @@ public class NativeEnvironmentRepositoryTests {
|
||||
assertEquals("version was wrong", "myversion", environment.getVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void placeholdersLabel() {
|
||||
this.repository.setSearchLocations("classpath:/test/{label}/");
|
||||
Environment environment = this.repository.findOne("foo", "development", "dev");
|
||||
assertEquals(1, environment.getPropertySources().size());
|
||||
assertEquals("dev_bar",
|
||||
environment.getPropertySources().get(0).getSource().get("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void placeholdersProfile() {
|
||||
this.repository.setSearchLocations("classpath:/test/{profile}/");
|
||||
Environment environment = this.repository.findOne("foo", "dev", "master");
|
||||
assertEquals(1, environment.getPropertySources().size());
|
||||
assertEquals("dev_bar",
|
||||
environment.getPropertySources().get(0).getSource().get("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void placeholdersApplicationAndProfile() {
|
||||
this.repository.setSearchLocations("classpath:/test/{profile}/{application}/");
|
||||
Environment environment = this.repository.findOne("app", "dev", "master");
|
||||
assertEquals(1, environment.getPropertySources().size());
|
||||
assertEquals("app",
|
||||
environment.getPropertySources().get(0).getSource().get("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void locationPlaceholdersApplication() {
|
||||
this.repository.setSearchLocations("classpath:/test/{application}");
|
||||
Locations locations = this.repository.getLocations("foo", "dev", "master");
|
||||
assertEquals(1, locations.getLocations().length);
|
||||
assertEquals("classpath:/test/foo/", locations.getLocations()[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void placeholdersNoTrailingSlash() {
|
||||
this.repository.setSearchLocations("classpath:/test/{label}");
|
||||
Environment environment = this.repository.findOne("foo", "development", "dev");
|
||||
assertEquals(1, environment.getPropertySources().size());
|
||||
assertEquals("dev_bar",
|
||||
environment.getPropertySources().get(0).getSource().get("foo"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
foo: app
|
||||
@@ -0,0 +1 @@
|
||||
foo: foo
|
||||
@@ -0,0 +1 @@
|
||||
foo: bar
|
||||
@@ -0,0 +1,8 @@
|
||||
Add application.properties
|
||||
# Please enter the commit message for your changes. Lines starting
|
||||
# with '#' will be ignored, and an empty message aborts the commit.
|
||||
# On branch master
|
||||
# Changes to be committed:
|
||||
# new file: application.properties
|
||||
# deleted: application.yml
|
||||
#
|
||||
@@ -0,0 +1,2 @@
|
||||
7df4a26d5437d9d4090cd5809967f870444cde8f not-for-merge branch 'raw' of dsyer@localhost:/home/dsyer/dev/platform/config/spring-platform-config-server/target/test-classes/config-repo
|
||||
7df4a26d5437d9d4090cd5809967f870444cde8f not-for-merge tag 'foo' of dsyer@localhost:/home/dsyer/dev/platform/config/spring-platform-config-server/target/test-classes/config-repo
|
||||
@@ -0,0 +1 @@
|
||||
ref: refs/heads/master
|
||||
@@ -0,0 +1 @@
|
||||
dfd1f2f006c9ea71be688e3eeaf383ff640fd21a
|
||||
@@ -0,0 +1,5 @@
|
||||
[core]
|
||||
repositoryformatversion = 0
|
||||
filemode = true
|
||||
logallrefupdates = true
|
||||
[branch "raw"]
|
||||
@@ -0,0 +1,3 @@
|
||||
1 1
|
||||
7df4a26d5437d9d4090cd5809967f870444cde8f 9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0 9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0
|
||||
1
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
0000000000000000000000000000000000000000 7df4a26d5437d9d4090cd5809967f870444cde8f Dave Syer <dsyer@gopivotal.com> 1406860717 -0700
|
||||
7df4a26d5437d9d4090cd5809967f870444cde8f 9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0 Dave Syer <dsyer@pivotal.io> 1415902155 +0000 checkout: moving from raw to master
|
||||
9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0 c4bd92016dc14b9fe376d9c3f7af1c9d22d44ee4 Dave Syer <dsyer@pivotal.io> 1415902338 +0000 commit: Move application.yml to subdir
|
||||
c4bd92016dc14b9fe376d9c3f7af1c9d22d44ee4 dfd1f2f006c9ea71be688e3eeaf383ff640fd21a Dave Syer <dsyer@pivotal.io> 1449047214 +0000 commit: Fix
|
||||
dfd1f2f006c9ea71be688e3eeaf383ff640fd21a c4bd92016dc14b9fe376d9c3f7af1c9d22d44ee4 Dave Syer <dsyer@pivotal.io> 1449047236 +0000 rebase -i (start): checkout 9f01f
|
||||
c4bd92016dc14b9fe376d9c3f7af1c9d22d44ee4 dfe21d586fc45ba72db173421c5139e26e50a7af Dave Syer <dsyer@pivotal.io> 1449047236 +0000 rebase -i (squash): Move application.yml to subdir
|
||||
dfe21d586fc45ba72db173421c5139e26e50a7af dfe21d586fc45ba72db173421c5139e26e50a7af Dave Syer <dsyer@pivotal.io> 1449047243 +0000 rebase -i (finish): returning to refs/heads/master
|
||||
dfe21d586fc45ba72db173421c5139e26e50a7af 29f2b1020f4b87e1de6aab368ff496cc4e99a959 Dave Syer <dsyer@pivotal.io> 1449047248 +0000 commit (amend): Add application.properties
|
||||
@@ -0,0 +1,5 @@
|
||||
0000000000000000000000000000000000000000 9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0 Dave Syer <dsyer@gopivotal.com> 1406860776 -0700 branch: Created from 9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0
|
||||
9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0 c4bd92016dc14b9fe376d9c3f7af1c9d22d44ee4 Dave Syer <dsyer@pivotal.io> 1415902338 +0000 commit: Move application.yml to subdir
|
||||
c4bd92016dc14b9fe376d9c3f7af1c9d22d44ee4 dfd1f2f006c9ea71be688e3eeaf383ff640fd21a Dave Syer <dsyer@pivotal.io> 1449047214 +0000 commit: Fix
|
||||
dfd1f2f006c9ea71be688e3eeaf383ff640fd21a dfe21d586fc45ba72db173421c5139e26e50a7af Dave Syer <dsyer@pivotal.io> 1449047243 +0000 rebase -i (finish): refs/heads/master onto c4bd92016dc14b9fe376d9c3f7af1c9d22d44ee4
|
||||
dfe21d586fc45ba72db173421c5139e26e50a7af 29f2b1020f4b87e1de6aab368ff496cc4e99a959 Dave Syer <dsyer@pivotal.io> 1449047248 +0000 commit (amend): Add application.properties
|
||||
@@ -0,0 +1 @@
|
||||
0000000000000000000000000000000000000000 7df4a26d5437d9d4090cd5809967f870444cde8f Dave Syer <dsyer@gopivotal.com> 1406860717 -0700
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
x+)JMU01f040031QH,(<28><>LN,<2C><><EFBFBD>ӫ<EFBFBD><D3AB>ah<61>xH<78>S<EFBFBD>O<EFBFBD>}jI<6A><49><EFBFBD><EFBFBD><EFBFBD><EFBFBD><07><01>x<12>
|
||||
@@ -0,0 +1 @@
|
||||
x<01><>Kj1D<><44>)z0<><30>g&Đ<1B>=<3D>n,<2C>-!ˆ<>>"<22><>kSP<53><07><>z-<03><><EFBFBD><EFBFBD>E@jR<6A><52>#<23><>d<EFBFBD><64><13>I<EFBFBD><49><EFBFBD><EFBFBD>U<EFBFBD><55><EFBFBD>`u<>
|
||||
@@ -0,0 +1,2 @@
|
||||
x<01><>1
|
||||
1E<>s<EFBFBD><73><05>$<24><>
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
x<01><>K
|
||||
1D]<5D><14><0B>O;<3B><17><A<>Ӎ<01>1z{<7B>7<EFBFBD>6<0F>WPT<50>97<39≯i<CCB8>Y<EFBFBD>3<EFBFBD>bM<62><4D>"<22><><EFBFBD>{%<25>1<EFBFBD>@"&<26>cPK<50><4B>h<EFBFBD>`J<><4A>Y<>P؇!!y Q,ar.p<><70><EFBFBD><EFBFBD>J<EFBFBD>縲<EFBFBD>~<7E><>Czv<7A><76><EFBFBD><EFBFBD><16><>\<5C><><02><><EFBFBD>,<2C><><EFBFBD>Q<EFBFBD>{<><D7BA>N]<5D>[}<01><>A<EFBFBD>
|
||||
@@ -0,0 +1,2 @@
|
||||
x<01><><EFBFBD>j1<10>S<EFBFBD>)<29>7<EFBFBD><37><EFBFBD><EFBFBD> LH<4C>6U<36>`o<>Kw<>!<21><06>}DH<44>i<06><><EFBFBD><EFBFBD><EFBFBD>弄<EFBFBD><EFA583><EFBFBD>7P<><50><EFBFBD>b<EFBFBD><62>9%Y<><59>D#Ҹ<><<3C>*Rp<12>AMn<1D><><EFBFBD>5<EFBFBD>n<EFBFBD>4<EFBFBD>Y<EFBFBD>PLy<11>,k<1C><><EFBFBD><EFBFBD>У<D0A3>Wz
|
||||
|<7C><><EFBFBD>%߇}<1C>Y;m<>R<EFBFBD><52>:<3A>8<><38><EFBFBD><EFBFBD>w}<7D><><EFBFBD>B<EFBFBD>0;?<3F>q泎:<3A><>0<EFBFBD>Ro<52>A<D7BE>p<70><7F>4<EFBFBD>VLZ
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
29f2b1020f4b87e1de6aab368ff496cc4e99a959
|
||||
@@ -0,0 +1 @@
|
||||
7df4a26d5437d9d4090cd5809967f870444cde8f
|
||||
@@ -0,0 +1 @@
|
||||
7df4a26d5437d9d4090cd5809967f870444cde8f
|
||||
Reference in New Issue
Block a user