1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package com.linecorp.centraldogma.server.internal.storage.repository;
19
20 import static com.google.common.base.MoreObjects.firstNonNull;
21 import static java.util.Objects.requireNonNull;
22
23 import java.net.URI;
24 import java.util.List;
25 import java.util.Optional;
26 import java.util.ServiceLoader;
27
28 import javax.annotation.Nullable;
29
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 import com.cronutils.model.Cron;
34 import com.cronutils.model.CronType;
35 import com.cronutils.model.definition.CronDefinitionBuilder;
36 import com.cronutils.parser.CronParser;
37 import com.fasterxml.jackson.annotation.JsonCreator;
38 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
39 import com.fasterxml.jackson.annotation.JsonInclude;
40 import com.fasterxml.jackson.annotation.JsonInclude.Include;
41 import com.fasterxml.jackson.annotation.JsonProperty;
42 import com.google.common.base.MoreObjects;
43 import com.google.common.collect.ImmutableList;
44 import com.google.common.collect.Streams;
45
46 import com.linecorp.centraldogma.server.mirror.Mirror;
47 import com.linecorp.centraldogma.server.mirror.MirrorContext;
48 import com.linecorp.centraldogma.server.mirror.MirrorCredential;
49 import com.linecorp.centraldogma.server.mirror.MirrorDirection;
50 import com.linecorp.centraldogma.server.mirror.MirrorProvider;
51 import com.linecorp.centraldogma.server.storage.project.Project;
52
53
54 @JsonIgnoreProperties(ignoreUnknown = true)
55 @JsonInclude(Include.NON_NULL)
56 public final class MirrorConfig {
57
58 private static final Logger logger = LoggerFactory.getLogger(MirrorConfig.class);
59
60 private static final String DEFAULT_SCHEDULE = "0 * * * * ?";
61
62 private static final CronParser CRON_PARSER = new CronParser(
63 CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ));
64
65 private static final List<MirrorProvider> MIRROR_PROVIDERS;
66
67 static {
68 MIRROR_PROVIDERS = ImmutableList.copyOf(ServiceLoader.load(MirrorProvider.class));
69 logger.debug("Available {}s: {}", MirrorProvider.class.getSimpleName(), MIRROR_PROVIDERS);
70 }
71
72 private final boolean enabled;
73 private final MirrorDirection direction;
74 @Nullable
75 private final String localRepo;
76 private final String localPath;
77 private final URI remoteUri;
78 @Nullable
79 private final String gitignore;
80 @Nullable
81 private final String credentialId;
82 private final Cron schedule;
83
84 @JsonCreator
85 public MirrorConfig(@JsonProperty("enabled") @Nullable Boolean enabled,
86 @JsonProperty("schedule") @Nullable String schedule,
87 @JsonProperty(value = "direction", required = true) MirrorDirection direction,
88 @JsonProperty(value = "localRepo", required = true) String localRepo,
89 @JsonProperty("localPath") @Nullable String localPath,
90 @JsonProperty(value = "remoteUri", required = true) URI remoteUri,
91 @JsonProperty("gitignore") @Nullable Object gitignore,
92 @JsonProperty("credentialId") @Nullable String credentialId) {
93 this.enabled = firstNonNull(enabled, true);
94 this.schedule = CRON_PARSER.parse(firstNonNull(schedule, DEFAULT_SCHEDULE));
95 this.direction = requireNonNull(direction, "direction");
96 this.localRepo = requireNonNull(localRepo, "localRepo");
97 this.localPath = firstNonNull(localPath, "/");
98 this.remoteUri = requireNonNull(remoteUri, "remoteUri");
99 if (gitignore != null) {
100 if (gitignore instanceof Iterable &&
101 Streams.stream((Iterable<?>) gitignore).allMatch(String.class::isInstance)) {
102 this.gitignore = String.join("\n", (Iterable<String>) gitignore);
103 } else if (gitignore instanceof String) {
104 this.gitignore = (String) gitignore;
105 } else {
106 throw new IllegalArgumentException(
107 "gitignore: " + gitignore + " (expected: either a string or an array of strings)");
108 }
109 } else {
110 this.gitignore = null;
111 }
112 this.credentialId = credentialId;
113 }
114
115 @Nullable
116 Mirror toMirror(Project parent, Iterable<MirrorCredential> credentials) {
117 if (!enabled || localRepo == null || !parent.repos().exists(localRepo)) {
118 return null;
119 }
120
121 final MirrorContext mirrorContext = new MirrorContext(
122 schedule, direction, findCredential(credentials, remoteUri, credentialId),
123 parent.repos().get(localRepo), localPath, remoteUri, gitignore);
124 for (MirrorProvider mirrorProvider : MIRROR_PROVIDERS) {
125 final Mirror mirror = mirrorProvider.newMirror(mirrorContext);
126 if (mirror != null) {
127 return mirror;
128 }
129 }
130
131 throw new IllegalArgumentException("could not find a mirror provider for " + mirrorContext);
132 }
133
134 private static MirrorCredential findCredential(Iterable<MirrorCredential> credentials, URI remoteUri,
135 @Nullable String credentialId) {
136 if (credentialId != null) {
137
138 for (MirrorCredential c : credentials) {
139 final Optional<String> id = c.id();
140 if (id.isPresent() && credentialId.equals(id.get())) {
141 return c;
142 }
143 }
144 } else {
145
146 for (MirrorCredential c : credentials) {
147 if (c.matches(remoteUri)) {
148 return c;
149 }
150 }
151 }
152
153 return MirrorCredential.FALLBACK;
154 }
155
156 @JsonProperty("enabled")
157 public boolean enabled() {
158 return enabled;
159 }
160
161 @JsonProperty("direction")
162 public MirrorDirection direction() {
163 return direction;
164 }
165
166 @Nullable
167 @JsonProperty("localRepo")
168 public String localRepo() {
169 return localRepo;
170 }
171
172 @JsonProperty("localPath")
173 public String localPath() {
174 return localPath;
175 }
176
177 @JsonProperty("remoteUri")
178 public String remoteUri() {
179 return remoteUri.toString();
180 }
181
182 @JsonProperty("gitignore")
183 @Nullable
184 public String gitignore() {
185 return gitignore;
186 }
187
188 @Nullable
189 @JsonProperty("credentialId")
190 public String credentialId() {
191 return credentialId;
192 }
193
194 @JsonProperty("schedule")
195 public String schedule() {
196 return schedule.asString();
197 }
198
199 @Override
200 public String toString() {
201 return MoreObjects.toStringHelper(this).omitNullValues()
202 .add("enabled", enabled)
203 .add("direction", direction)
204 .add("localRepo", localRepo)
205 .add("localPath", localPath)
206 .add("remoteUri", remoteUri)
207 .add("gitignore", gitignore)
208 .add("credentialId", credentialId)
209 .add("schedule", schedule)
210 .toString();
211 }
212 }