1   /*
2    * Copyright 2023 LINE Corporation
3    *
4    * LINE Corporation licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
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 com.linecorp.centraldogma.server.mirror.MirrorSchemes.SCHEME_DOGMA;
22  import static java.util.Objects.requireNonNull;
23  
24  import java.net.URI;
25  
26  import javax.annotation.Nullable;
27  
28  import com.cronutils.model.Cron;
29  import com.cronutils.model.CronType;
30  import com.cronutils.model.definition.CronDefinitionBuilder;
31  import com.cronutils.parser.CronParser;
32  import com.fasterxml.jackson.annotation.JsonCreator;
33  import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
34  import com.fasterxml.jackson.annotation.JsonInclude;
35  import com.fasterxml.jackson.annotation.JsonInclude.Include;
36  import com.fasterxml.jackson.annotation.JsonProperty;
37  import com.google.common.base.MoreObjects;
38  import com.google.common.collect.Streams;
39  
40  import com.linecorp.centraldogma.server.mirror.MirrorDirection;
41  
42  // ignoreUnknown = true for backward compatibility since `type` field is removed.
43  @JsonIgnoreProperties(ignoreUnknown = true)
44  @JsonInclude(Include.NON_NULL)
45  public final class MirrorConfig {
46  
47      public static final String DEFAULT_SCHEDULE = "0 * * * * ?"; // Every minute
48  
49      public static final CronParser CRON_PARSER = new CronParser(
50              CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ));
51  
52      private final String id;
53      private final boolean enabled;
54      private final MirrorDirection direction;
55      private final String localRepo;
56      private final String localPath;
57      private final URI remoteUri;
58      @Nullable
59      private final String gitignore;
60      private final String credentialName;
61      @Nullable
62      private final Cron schedule;
63      @Nullable
64      private final String zone;
65  
66      @JsonCreator
67      public MirrorConfig(@JsonProperty("id") String id,
68                          @JsonProperty("enabled") @Nullable Boolean enabled,
69                          @JsonProperty("schedule") @Nullable String schedule,
70                          @JsonProperty(value = "direction", required = true) MirrorDirection direction,
71                          @JsonProperty(value = "localRepo", required = true) String localRepo,
72                          @JsonProperty("localPath") @Nullable String localPath,
73                          @JsonProperty(value = "remoteUri", required = true) URI remoteUri,
74                          @JsonProperty("gitignore") @Nullable Object gitignore,
75                          // TODO(minwoox): Remove this credentialId property after migration is done.
76                          @JsonProperty("credentialId") @Nullable String credentialId,
77                          @JsonProperty("credentialName") @Nullable String credentialName,
78                          @JsonProperty("zone") @Nullable String zone) {
79          this(id, enabled, schedule != null ? CRON_PARSER.parse(schedule) : null, direction, localRepo,
80               localPath, remoteUri, gitignore,
81               requireNonNull(firstNonNull(credentialName, credentialId), "credentialName"),
82               zone);
83      }
84  
85      private MirrorConfig(String id, @Nullable Boolean enabled, @Nullable Cron schedule,
86                           MirrorDirection direction, String localRepo, @Nullable String localPath,
87                           URI remoteUri, @Nullable Object gitignore, String credentialName,
88                           @Nullable String zone) {
89          this.id = requireNonNull(id, "id");
90          this.enabled = firstNonNull(enabled, true);
91          this.schedule = schedule;
92          this.direction = requireNonNull(direction, "direction");
93          this.localRepo = requireNonNull(localRepo, "localRepo");
94          this.localPath = firstNonNull(localPath, "/");
95          this.remoteUri = requireNonNull(remoteUri, "remoteUri");
96  
97          // Validate the remote URI.
98          final String suffix = remoteUri.getScheme().equals(SCHEME_DOGMA) ? "dogma" : "git";
99          RepositoryUri.parse(remoteUri, suffix);
100 
101         if (gitignore != null) {
102             if (gitignore instanceof Iterable &&
103                 Streams.stream((Iterable<?>) gitignore).allMatch(String.class::isInstance)) {
104                 this.gitignore = String.join("\n", (Iterable<String>) gitignore);
105             } else if (gitignore instanceof String) {
106                 this.gitignore = (String) gitignore;
107             } else {
108                 throw new IllegalArgumentException(
109                         "gitignore: " + gitignore + " (expected: either a string or an array of strings)");
110             }
111         } else {
112             this.gitignore = null;
113         }
114         this.credentialName = requireNonNull(credentialName, "credentialName");
115         this.zone = zone;
116     }
117 
118     public MirrorConfig withCredentialName(String credentialName) {
119         return new MirrorConfig(id, enabled, schedule, direction, localRepo, localPath, remoteUri,
120                                 gitignore, credentialName, zone);
121     }
122 
123     @JsonProperty("id")
124     public String id() {
125         return id;
126     }
127 
128     @JsonProperty("enabled")
129     public boolean enabled() {
130         return enabled;
131     }
132 
133     @JsonProperty("direction")
134     public MirrorDirection direction() {
135         return direction;
136     }
137 
138     @JsonProperty("localRepo")
139     public String localRepo() {
140         return localRepo;
141     }
142 
143     @JsonProperty("localPath")
144     public String localPath() {
145         return localPath;
146     }
147 
148     @JsonProperty("remoteUri")
149     public String remoteUri() {
150         return remoteUri.toString();
151     }
152 
153     URI rawRemoteUri() {
154         return remoteUri;
155     }
156 
157     @JsonProperty("gitignore")
158     @Nullable
159     public String gitignore() {
160         return gitignore;
161     }
162 
163     @JsonProperty("credentialName")
164     public String credentialName() {
165         return credentialName;
166     }
167 
168     @Nullable
169     @JsonProperty("schedule")
170     public String schedule() {
171         if (schedule != null) {
172             return schedule.asString();
173         } else {
174             return null;
175         }
176     }
177 
178     @Nullable
179     Cron cronSchedule() {
180         return schedule;
181     }
182 
183     @Nullable
184     @JsonProperty("zone")
185     public String zone() {
186         return zone;
187     }
188 
189     @Override
190     public String toString() {
191         return MoreObjects.toStringHelper(this).omitNullValues()
192                           .add("enabled", enabled)
193                           .add("direction", direction)
194                           .add("localRepo", localRepo)
195                           .add("localPath", localPath)
196                           .add("remoteUri", remoteUri)
197                           .add("gitignore", gitignore)
198                           .add("credentialName", credentialName)
199                           .add("schedule", schedule)
200                           .add("zone", zone)
201                           .toString();
202     }
203 }