1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.linecorp.centraldogma.server;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.linecorp.centraldogma.server.auth.AuthConfig.DEFAULT_SESSION_CACHE_SPEC;
21 import static com.linecorp.centraldogma.server.auth.AuthConfig.DEFAULT_SESSION_TIMEOUT_MILLIS;
22 import static com.linecorp.centraldogma.server.auth.AuthConfig.DEFAULT_SESSION_VALIDATION_SCHEDULE;
23 import static com.linecorp.centraldogma.server.internal.storage.repository.RepositoryCache.validateCacheSpec;
24 import static java.util.Objects.requireNonNull;
25
26 import java.io.File;
27 import java.net.InetSocketAddress;
28 import java.time.Duration;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.List;
32 import java.util.Set;
33
34 import javax.annotation.Nullable;
35
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import com.github.benmanes.caffeine.cache.CaffeineSpec;
40 import com.google.common.collect.ImmutableList;
41 import com.google.common.collect.ImmutableSet;
42 import com.google.common.collect.ImmutableSet.Builder;
43
44 import com.linecorp.armeria.common.Flags;
45 import com.linecorp.armeria.common.SessionProtocol;
46 import com.linecorp.armeria.server.ServerPort;
47 import com.linecorp.centraldogma.internal.Jackson;
48 import com.linecorp.centraldogma.server.auth.AuthConfig;
49 import com.linecorp.centraldogma.server.auth.AuthProvider;
50 import com.linecorp.centraldogma.server.auth.AuthProviderFactory;
51 import com.linecorp.centraldogma.server.auth.Session;
52 import com.linecorp.centraldogma.server.storage.repository.Repository;
53
54 import io.micrometer.core.instrument.MeterRegistry;
55
56
57
58
59
60
61
62
63
64
65
66
67 public final class CentralDogmaBuilder {
68 private static final Logger logger = LoggerFactory.getLogger(CentralDogmaBuilder.class);
69
70
71 private static final ServerPort DEFAULT_PORT = new ServerPort(36462, SessionProtocol.HTTP);
72
73 static final int DEFAULT_NUM_REPOSITORY_WORKERS = 16;
74 static final int DEFAULT_NUM_MIRRORING_THREADS = 16;
75 static final int DEFAULT_MAX_NUM_FILES_PER_MIRROR = 8192;
76 static final long DEFAULT_MAX_NUM_BYTES_PER_MIRROR = 32 * 1048576;
77 static final long DEFAULT_MAX_REMOVED_REPOSITORY_AGE_MILLIS = 604_800_000;
78
79 static final String DEFAULT_REPOSITORY_CACHE_SPEC =
80 "maximumWeight=134217728," +
81 "expireAfterAccess=5m";
82
83
84
85
86 private final List<ServerPort> ports = new ArrayList<>(2);
87 private TlsConfig tls;
88 private List<String> trustedProxyAddresses = new ArrayList<>();
89 private List<String> clientAddressSources = new ArrayList<>();
90 private Integer numWorkers;
91 private Integer maxNumConnections;
92 private Long requestTimeoutMillis;
93 private Long idleTimeoutMillis;
94 private Integer maxFrameLength;
95
96
97 private final File dataDir;
98 private int numRepositoryWorkers = DEFAULT_NUM_REPOSITORY_WORKERS;
99 private long maxRemovedRepositoryAgeMillis = DEFAULT_MAX_REMOVED_REPOSITORY_AGE_MILLIS;
100
101 @Nullable
102 private String repositoryCacheSpec = DEFAULT_REPOSITORY_CACHE_SPEC;
103 private boolean webAppEnabled = true;
104 @Nullable
105 private String webAppTitle;
106 private boolean mirroringEnabled = true;
107 private int numMirroringThreads = DEFAULT_NUM_MIRRORING_THREADS;
108 private int maxNumFilesPerMirror = DEFAULT_MAX_NUM_FILES_PER_MIRROR;
109 private long maxNumBytesPerMirror = DEFAULT_MAX_NUM_BYTES_PER_MIRROR;
110 @Nullable
111 private GracefulShutdownTimeout gracefulShutdownTimeout;
112 private ReplicationConfig replicationConfig = ReplicationConfig.NONE;
113 private String accessLogFormat;
114
115
116 @Nullable
117 private AuthProviderFactory authProviderFactory;
118 private final ImmutableSet.Builder<String> administrators = new Builder<>();
119 private boolean caseSensitiveLoginNames;
120 private String sessionCacheSpec = DEFAULT_SESSION_CACHE_SPEC;
121 private long sessionTimeoutMillis = DEFAULT_SESSION_TIMEOUT_MILLIS;
122 private String sessionValidationSchedule = DEFAULT_SESSION_VALIDATION_SCHEDULE;
123 @Nullable
124 private Object authProviderProperties;
125 private int writeQuota;
126 private int timeWindowSeconds;
127 private MeterRegistry meterRegistry = Flags.meterRegistry();
128
129 @Nullable
130 private CorsConfig corsConfig;
131
132
133
134
135 public CentralDogmaBuilder(File dataDir) {
136 this.dataDir = requireNonNull(dataDir, "dataDir");
137 if (dataDir.exists() && !dataDir.isDirectory()) {
138 throw new IllegalArgumentException("dataDir: " + dataDir + " (not a directory)");
139 }
140 }
141
142
143
144
145
146
147
148 public CentralDogmaBuilder port(int port, SessionProtocol protocol) {
149 return port(new ServerPort(port, protocol));
150 }
151
152
153
154
155
156
157
158 public CentralDogmaBuilder port(InetSocketAddress localAddress, SessionProtocol protocol) {
159 return port(new ServerPort(localAddress, protocol));
160 }
161
162
163
164
165 public CentralDogmaBuilder port(ServerPort port) {
166 ports.add(requireNonNull(port, "port"));
167 return this;
168 }
169
170
171
172
173 public CentralDogmaBuilder tls(TlsConfig tls) {
174 this.tls = requireNonNull(tls, "tls");
175 return this;
176 }
177
178
179
180
181
182
183
184
185 public CentralDogmaBuilder trustedProxyAddresses(String... exactOrCidrAddresses) {
186 requireNonNull(exactOrCidrAddresses, "exactOrCidrAddresses");
187 trustedProxyAddresses.addAll(ImmutableList.copyOf(exactOrCidrAddresses));
188 return this;
189 }
190
191
192
193
194
195
196
197
198 public CentralDogmaBuilder trustedProxyAddresses(Iterable<String> exactOrCidrAddresses) {
199 requireNonNull(exactOrCidrAddresses, "exactOrCidrAddresses");
200 trustedProxyAddresses.addAll(ImmutableList.copyOf(exactOrCidrAddresses));
201 return this;
202 }
203
204
205
206
207
208
209
210
211
212
213
214
215
216 public CentralDogmaBuilder clientAddressSources(String... clientAddressSources) {
217 requireNonNull(clientAddressSources, "clientAddressSources");
218 this.clientAddressSources.addAll(ImmutableList.copyOf(clientAddressSources));
219 return this;
220 }
221
222
223
224
225
226
227
228
229
230
231
232
233
234 public CentralDogmaBuilder clientAddressSources(Iterable<String> clientAddressSources) {
235 requireNonNull(clientAddressSources, "clientAddressSources");
236 this.clientAddressSources.addAll(ImmutableList.copyOf(clientAddressSources));
237 return this;
238 }
239
240
241
242
243
244 public CentralDogmaBuilder numWorkers(int numWorkers) {
245 this.numWorkers = numWorkers;
246 return this;
247 }
248
249
250
251
252 public CentralDogmaBuilder maxNumConnections(int maxNumConnections) {
253 this.maxNumConnections = maxNumConnections;
254 return this;
255 }
256
257
258
259
260
261
262 public CentralDogmaBuilder requestTimeout(Duration requestTimeout) {
263 return requestTimeoutMillis(requireNonNull(requestTimeout, "requestTimeout").toMillis());
264 }
265
266
267
268
269
270
271 public CentralDogmaBuilder requestTimeoutMillis(long requestTimeoutMillis) {
272 this.requestTimeoutMillis = requestTimeoutMillis;
273 return this;
274 }
275
276
277
278
279
280
281 public CentralDogmaBuilder idleTimeout(Duration idleTimeout) {
282 return idleTimeoutMillis(requireNonNull(idleTimeout, "idleTimeout").toMillis());
283 }
284
285
286
287
288
289 public CentralDogmaBuilder idleTimeoutMillis(long idleTimeoutMillis) {
290 this.idleTimeoutMillis = idleTimeoutMillis;
291 return this;
292 }
293
294
295
296
297 public CentralDogmaBuilder maxFrameLength(int maxFrameLength) {
298 this.maxFrameLength = maxFrameLength;
299 return this;
300 }
301
302
303
304
305
306 public CentralDogmaBuilder numRepositoryWorkers(int numRepositoryWorkers) {
307 this.numRepositoryWorkers = numRepositoryWorkers;
308 return this;
309 }
310
311
312
313
314
315
316 public CentralDogmaBuilder maxRemovedRepositoryAge(Duration maxRemovedRepositoryAge) {
317 maxRemovedRepositoryAgeMillis(
318 requireNonNull(maxRemovedRepositoryAge, "maxRemovedRepositoryAge").toMillis());
319 return this;
320 }
321
322
323
324
325
326
327
328 public CentralDogmaBuilder maxRemovedRepositoryAgeMillis(long maxRemovedRepositoryAgeMillis) {
329 this.maxRemovedRepositoryAgeMillis = maxRemovedRepositoryAgeMillis;
330 return this;
331 }
332
333
334
335
336
337
338
339
340 @Deprecated
341 public CentralDogmaBuilder cacheSpec(String cacheSpec) {
342 repositoryCacheSpec = validateCacheSpec(cacheSpec);
343 return this;
344 }
345
346
347
348
349
350
351 public CentralDogmaBuilder repositoryCacheSpec(String repositoryCacheSpec) {
352 this.repositoryCacheSpec = validateCacheSpec(repositoryCacheSpec);
353 return this;
354 }
355
356
357
358
359
360 public CentralDogmaBuilder webAppEnabled(boolean webAppEnabled) {
361 this.webAppEnabled = webAppEnabled;
362 return this;
363 }
364
365
366
367
368 public CentralDogmaBuilder webAppTitle(String webAppTitle) {
369 this.webAppTitle = requireNonNull(webAppTitle, "webAppTitle");
370 return this;
371 }
372
373
374
375
376
377 public CentralDogmaBuilder mirroringEnabled(boolean mirroringEnabled) {
378 this.mirroringEnabled = mirroringEnabled;
379 return this;
380 }
381
382
383
384
385
386 public CentralDogmaBuilder numMirroringThreads(int numMirroringThreads) {
387 this.numMirroringThreads = numMirroringThreads;
388 return this;
389 }
390
391
392
393
394
395 public CentralDogmaBuilder maxNumFilesPerMirror(int maxNumFilesPerMirror) {
396 this.maxNumFilesPerMirror = maxNumFilesPerMirror;
397 return this;
398 }
399
400
401
402
403
404 public CentralDogmaBuilder maxNumBytesPerMirror(long maxNumBytesPerMirror) {
405 this.maxNumBytesPerMirror = maxNumBytesPerMirror;
406 return this;
407 }
408
409
410
411
412 public CentralDogmaBuilder gracefulShutdownTimeout(GracefulShutdownTimeout gracefulShutdownTimeout) {
413 this.gracefulShutdownTimeout = gracefulShutdownTimeout;
414 return this;
415 }
416
417
418
419
420
421 public CentralDogmaBuilder replication(ReplicationConfig replicationConfig) {
422 this.replicationConfig = requireNonNull(replicationConfig, "replicationConfig");
423 return this;
424 }
425
426
427
428
429
430
431 public CentralDogmaBuilder accessLogFormat(String accessLogFormat) {
432 this.accessLogFormat = requireNonNull(accessLogFormat, "accessLogFormat");
433 return this;
434 }
435
436
437
438
439 public CentralDogmaBuilder authProviderFactory(AuthProviderFactory authProviderFactory) {
440 this.authProviderFactory = requireNonNull(authProviderFactory, "authProviderFactory");
441 return this;
442 }
443
444
445
446
447 public CentralDogmaBuilder administrators(String... administrators) {
448 requireNonNull(administrators, "administrators");
449 for (final String administrator : administrators) {
450 this.administrators.add(administrator);
451 }
452 return this;
453 }
454
455
456
457
458 public CentralDogmaBuilder administrators(Iterable<String> administrators) {
459 requireNonNull(administrators, "administrators");
460 this.administrators.addAll(administrators);
461 return this;
462 }
463
464
465
466
467 public CentralDogmaBuilder caseSensitiveLoginNames(boolean caseSensitiveLoginNames) {
468 this.caseSensitiveLoginNames = caseSensitiveLoginNames;
469 return this;
470 }
471
472
473
474
475
476
477
478 public CentralDogmaBuilder sessionCacheSpec(String sessionCacheSpec) {
479 this.sessionCacheSpec = validateCacheSpec(sessionCacheSpec);
480 return this;
481 }
482
483
484
485
486
487 public CentralDogmaBuilder sessionTimeoutMillis(long sessionTimeoutMillis) {
488 this.sessionTimeoutMillis = sessionTimeoutMillis;
489 return this;
490 }
491
492
493
494
495
496 public CentralDogmaBuilder sessionTimeout(Duration sessionTimeout) {
497 return sessionTimeoutMillis(
498 requireNonNull(sessionTimeout, "sessionTimeout").toMillis());
499 }
500
501
502
503
504
505 public CentralDogmaBuilder sessionValidationSchedule(String sessionValidationSchedule) {
506 this.sessionValidationSchedule =
507 requireNonNull(sessionValidationSchedule, "sessionValidationSchedule");
508 return this;
509 }
510
511
512
513
514 public CentralDogmaBuilder authProviderProperties(Object authProviderProperties) {
515 this.authProviderProperties = requireNonNull(authProviderProperties, "authProviderProperties");
516 return this;
517 }
518
519
520
521
522 public CentralDogmaBuilder writeQuotaPerRepository(int writeQuota, int timeWindowSeconds) {
523 checkArgument(writeQuota > 0, "writeQuota: %s (expected: > 0)", writeQuota);
524 checkArgument(timeWindowSeconds > 0, "timeWindowSeconds: %s (expected: > 0)", timeWindowSeconds);
525 this.writeQuota = writeQuota;
526 this.timeWindowSeconds = timeWindowSeconds;
527 return this;
528 }
529
530
531
532
533 public CentralDogmaBuilder meterRegistry(MeterRegistry meterRegistry) {
534 this.meterRegistry = requireNonNull(meterRegistry, "meterRegistry");
535 return this;
536 }
537
538
539
540
541 public CentralDogmaBuilder cors(CorsConfig corsConfig) {
542 this.corsConfig = requireNonNull(corsConfig, "corsConfig");
543 return this;
544 }
545
546
547
548
549 public CentralDogma build() {
550 return new CentralDogma(buildConfig(), meterRegistry);
551 }
552
553 private CentralDogmaConfig buildConfig() {
554 final List<ServerPort> ports = !this.ports.isEmpty() ? this.ports
555 : Collections.singletonList(DEFAULT_PORT);
556 final Set<String> adminSet = administrators.build();
557 final AuthConfig authCfg;
558 if (authProviderFactory != null) {
559 authCfg = new AuthConfig(
560 authProviderFactory, adminSet, caseSensitiveLoginNames,
561 sessionCacheSpec, sessionTimeoutMillis, sessionValidationSchedule,
562 authProviderProperties != null ? Jackson.valueToTree(authProviderProperties) : null);
563 } else {
564 authCfg = null;
565 logger.info("{} is not specified, so {} will not be configured.",
566 AuthProviderFactory.class.getSimpleName(),
567 AuthConfig.class.getSimpleName());
568 }
569
570 final QuotaConfig quotaConfig = writeQuota > 0 ? new QuotaConfig(writeQuota, timeWindowSeconds) : null;
571
572 return new CentralDogmaConfig(dataDir, ports, tls, trustedProxyAddresses, clientAddressSources,
573 numWorkers, maxNumConnections,
574 requestTimeoutMillis, idleTimeoutMillis, maxFrameLength,
575 numRepositoryWorkers, repositoryCacheSpec,
576 maxRemovedRepositoryAgeMillis, gracefulShutdownTimeout,
577 webAppEnabled, webAppTitle, mirroringEnabled, numMirroringThreads,
578 maxNumFilesPerMirror, maxNumBytesPerMirror, replicationConfig,
579 null, accessLogFormat, authCfg, quotaConfig,
580 corsConfig);
581 }
582 }