Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■
    src/main/java/io/onedev/k8shelper/Action.java
    skipped 5 lines
    6 6   
    7 7   private static final long serialVersionUID = 1L;
    8 8   
     9 + private final String name;
     10 +
    9 11   private final ExecuteCondition condition;
    10 12  
    11 13   private final Executable executable;
    12 14  
    13  - public Action(Executable executable, ExecuteCondition condition) {
     15 + public Action(String name, Executable executable, ExecuteCondition condition) {
     16 + this.name = name;
    14 17   this.executable = executable;
    15 18   this.condition = condition;
     19 + }
     20 + 
     21 + public String getName() {
     22 + return name;
    16 23   }
    17 24   
    18 25   public Executable getExecutable() {
    skipped 9 lines
  • ■ ■ ■ ■ ■ ■
    src/main/java/io/onedev/k8shelper/CheckoutCode.java
     1 +package io.onedev.k8shelper;
     2 + 
     3 +import org.slf4j.Logger;
     4 +import org.slf4j.LoggerFactory;
     5 + 
     6 +import com.google.common.base.Preconditions;
     7 + 
     8 +public class CheckoutCode {
     9 + 
     10 + private static final Logger logger = LoggerFactory.getLogger(CheckoutCode.class);
     11 +
     12 + public static void main(String[] args) {
     13 + int exitCode = 0;
     14 + try {
     15 + String serverUrl = System.getenv(KubernetesHelper.ENV_SERVER_URL);
     16 + if (serverUrl == null)
     17 + throw new RuntimeException("Environment '" + KubernetesHelper.ENV_SERVER_URL + "' is not defined");
     18 + String jobToken = Preconditions.checkNotNull(System.getenv(KubernetesHelper.ENV_JOB_TOKEN));
     19 + if (jobToken == null)
     20 + throw new RuntimeException("Environment '" + KubernetesHelper.ENV_JOB_TOKEN + "' is not defined");
     21 + 
     22 + KubernetesHelper.checkoutCode(serverUrl, jobToken, args[0],
     23 + Integer.parseInt(args[1]), CloneInfo.fromString(args[2]));
     24 + } catch (Exception e) {
     25 + logger.error("Error executing step", e);
     26 + exitCode = 1;
     27 + } finally {
     28 + logger.info(KubernetesHelper.LOG_END_MESSAGE);
     29 + System.exit(exitCode);
     30 + }
     31 + }
     32 +
     33 +}
     34 + 
  • ■ ■ ■ ■ ■ ■
    src/main/java/io/onedev/k8shelper/CheckoutExecutable.java
     1 +package io.onedev.k8shelper;
     2 + 
     3 +public class CheckoutExecutable extends LeafExecutable {
     4 + 
     5 + private static final long serialVersionUID = 1L;
     6 + 
     7 + private final int cloneDepth;
     8 +
     9 + private final CloneInfo cloneInfo;
     10 +
     11 + public CheckoutExecutable(int cloneDepth, CloneInfo cloneInfo) {
     12 + this.cloneDepth = cloneDepth;
     13 + this.cloneInfo = cloneInfo;
     14 + }
     15 + 
     16 + public int getCloneDepth() {
     17 + return cloneDepth;
     18 + }
     19 + 
     20 + public CloneInfo getCloneInfo() {
     21 + return cloneInfo;
     22 + }
     23 + 
     24 +}
     25 + 
  • ■ ■ ■ ■ ■ ■
    src/main/java/io/onedev/k8shelper/CloneInfo.java
    skipped 1 lines
    2 2   
    3 3  import java.io.File;
    4 4  import java.io.Serializable;
     5 +import static java.nio.charset.StandardCharsets.UTF_8;
     6 +import java.util.Base64;
     7 +import java.util.List;
     8 + 
     9 +import com.google.common.base.Splitter;
    5 10   
    6 11  import io.onedev.commons.utils.command.Commandline;
    7 12  import io.onedev.commons.utils.command.LineConsumer;
    skipped 12 lines
    20 25   return cloneUrl;
    21 26   }
    22 27  
    23  - public abstract void writeAuthData(File homeDir, Commandline git, LineConsumer infoLogger, LineConsumer errorLogger);
     28 + public abstract void writeAuthData(File homeDir, Commandline git,
     29 + LineConsumer infoLogger, LineConsumer errorLogger);
     30 +
     31 + public static CloneInfo fromString(String string) {
     32 + List<String> fields = Splitter.on("-").splitToList(string);
     33 + String cloneUrl = new String(Base64.getDecoder().decode(fields.get(1).getBytes(UTF_8)), UTF_8);
     34 + if (fields.get(0).equals("default")) {
     35 + String jobToken = new String(Base64.getDecoder().decode(fields.get(2).getBytes(UTF_8)), UTF_8);
     36 + return new DefaultCloneInfo(cloneUrl, jobToken);
     37 + } else if (fields.get(0).equals("http")) {
     38 + String accessToken = new String(Base64.getDecoder().decode(fields.get(2).getBytes(UTF_8)), UTF_8);
     39 + return new HttpCloneInfo(cloneUrl, accessToken);
     40 + } else {
     41 + String privateKey = new String(Base64.getDecoder().decode(fields.get(2).getBytes(UTF_8)), UTF_8);
     42 + String knownHosts = new String(Base64.getDecoder().decode(fields.get(3).getBytes(UTF_8)), UTF_8);
     43 + return new SshCloneInfo(cloneUrl, privateKey, knownHosts);
     44 + }
     45 + }
    24 46  
    25 47  }
    26 48   
  • ■ ■ ■ ■ ■
    src/main/java/io/onedev/k8shelper/CompositeExecutable.java
    skipped 1 lines
    2 2   
    3 3  import java.util.ArrayList;
    4 4  import java.util.List;
     5 + 
     6 +import com.google.common.base.Joiner;
     7 +import com.google.common.base.Preconditions;
     8 + 
    5 9  import static io.onedev.k8shelper.ExecuteCondition.*;
    6 10   
    7 11  public class CompositeExecutable implements Executable {
    skipped 16 lines
    24 28   for (int i = 0; i<actions.size(); i++) {
    25 29   Action action = actions.get(i);
    26 30   List<Integer> newPosition = new ArrayList<>(position);
    27  - newPosition.add(i+1);
    28  - if (action.getCondition() == ALWAYS || action.getCondition() == ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL && !failed ) {
     31 + newPosition.add(i);
     32 + if (action.getCondition() == ALWAYS
     33 + || action.getCondition() == ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL && !failed ) {
    29 34   if (!action.getExecutable().execute(handler, newPosition))
    30 35   failed = true;
    31 36   } else {
    skipped 7 lines
    39 44   public void skip(LeafHandler handler, List<Integer> position) {
    40 45   for (int i=0; i<actions.size(); i++) {
    41 46   List<Integer> newPosition = new ArrayList<>(position);
    42  - newPosition.add(i+1);
     47 + newPosition.add(i);
    43 48   actions.get(i).getExecutable().skip(handler, newPosition);
    44 49   }
    45 50   }
    skipped 2 lines
    48 53   public <T> T traverse(LeafVisitor<T> visitor, List<Integer> position) {
    49 54   for (int i=0; i<actions.size(); i++) {
    50 55   List<Integer> newPosition = new ArrayList<>(position);
    51  - newPosition.add(i+1);
     56 + newPosition.add(i);
    52 57   T result = actions.get(i).getExecutable().traverse(visitor, newPosition);
    53 58   if (result != null)
    54 59   return result;
    55 60   }
    56 61   return null;
     62 + }
     63 +
     64 + public List<String> getNames(List<Integer> position) {
     65 + Preconditions.checkArgument(!position.isEmpty());
     66 +
     67 + List<String> names = new ArrayList<>();
     68 + List<Integer> positionCopy = new ArrayList<>(position);
     69 + Action action = actions.get(positionCopy.remove(0));
     70 + names.add(action.getName());
     71 + if (!positionCopy.isEmpty()) {
     72 + CompositeExecutable executable = (CompositeExecutable) action.getExecutable();
     73 + names.addAll(executable.getNames(positionCopy));
     74 + }
     75 + return names;
     76 + }
     77 +
     78 + public String getNamesAsString(List<Integer> position) {
     79 + return Joiner.on(" -> ").join(getNames(position));
    57 80   }
    58 81  
    59 82  }
    skipped 1 lines
  • ■ ■ ■ ■ ■ ■
    src/main/java/io/onedev/k8shelper/DefaultCloneInfo.java
    1 1  package io.onedev.k8shelper;
    2 2   
    3 3  import java.io.File;
     4 +import java.nio.charset.StandardCharsets;
     5 +import java.util.Base64;
    4 6   
    5 7  import javax.ws.rs.core.HttpHeaders;
    6 8   
    skipped 17 lines
    24 26   String extraHeader = HttpHeaders.AUTHORIZATION + ": " + KubernetesHelper.BEARER + " " + jobToken;
    25 27   git.addArgs("config", "--global", "http.extraHeader", extraHeader);
    26 28   git.execute(infoLogger, errorLogger).checkReturnCode();
     29 + }
     30 + 
     31 + @Override
     32 + public String toString() {
     33 + return "default-"
     34 + + Base64.getEncoder().encodeToString(getCloneUrl().getBytes(StandardCharsets.UTF_8))
     35 + + "-" + Base64.getEncoder().encodeToString(jobToken.getBytes(StandardCharsets.UTF_8));
    27 36   }
    28 37  
    29 38  }
    skipped 1 lines
  • ■ ■ ■ ■ ■ ■
    src/main/java/io/onedev/k8shelper/HttpCloneInfo.java
    1 1  package io.onedev.k8shelper;
    2 2   
    3 3  import java.io.File;
     4 +import java.nio.charset.StandardCharsets;
     5 +import java.util.Base64;
    4 6   
    5 7  import javax.ws.rs.core.HttpHeaders;
    6 8   
    skipped 18 lines
    25 27   String extraHeader = HttpHeaders.AUTHORIZATION + ": " + KubernetesHelper.BEARER + " " + accessToken;
    26 28   git.addArgs("config", "--global", "http.extraHeader", extraHeader);
    27 29   git.execute(infoLogger, errorLogger).checkReturnCode();
     30 + }
     31 + 
     32 + @Override
     33 + public String toString() {
     34 + return "http-"
     35 + + Base64.getEncoder().encodeToString(getCloneUrl().getBytes(StandardCharsets.UTF_8))
     36 + + "-" + Base64.getEncoder().encodeToString(accessToken.getBytes(StandardCharsets.UTF_8));
    28 37   }
    29 38  
    30 39  }
    skipped 1 lines
  • ■ ■ ■ ■ ■ ■
    src/main/java/io/onedev/k8shelper/KubernetesHelper.java
    skipped 6 lines
    7 7  import java.io.OutputStream;
    8 8  import java.nio.ByteBuffer;
    9 9  import java.nio.charset.Charset;
    10  -import java.nio.charset.StandardCharsets;
     10 +import static java.nio.charset.StandardCharsets.UTF_8;
    11 11  import java.nio.file.Files;
    12 12  import java.util.ArrayList;
    13 13  import java.util.Base64;
    14 14  import java.util.Collection;
    15 15  import java.util.Date;
    16 16  import java.util.HashMap;
     17 +import java.util.HashSet;
    17 18  import java.util.Iterator;
    18 19  import java.util.List;
    19 20  import java.util.Map;
    20 21  import java.util.function.Consumer;
     22 +import java.util.regex.Matcher;
     23 +import java.util.regex.Pattern;
    21 24  import java.util.stream.Collectors;
    22 25   
    23 26  import javax.ws.rs.client.Client;
    skipped 6 lines
    30 33  import javax.ws.rs.core.Response;
    31 34  import javax.ws.rs.core.StreamingOutput;
    32 35   
     36 +import org.apache.commons.compress.utils.IOUtils;
    33 37  import org.apache.commons.lang3.SerializationUtils;
    34 38  import org.apache.commons.lang3.StringUtils;
    35 39  import org.apache.commons.lang3.SystemUtils;
    skipped 2 lines
    38 42  import org.slf4j.LoggerFactory;
    39 43   
    40 44  import com.google.common.base.Splitter;
     45 +import com.google.common.base.Throwables;
    41 46  import com.google.common.collect.Lists;
    42 47   
     48 +import io.onedev.commons.utils.ExceptionUtils;
     49 +import io.onedev.commons.utils.ExplicitException;
    43 50  import io.onedev.commons.utils.FileUtils;
    44 51  import io.onedev.commons.utils.PathUtils;
    45 52  import io.onedev.commons.utils.TarUtils;
    skipped 10 lines
    56 63  
    57 64   public static final String LOG_END_MESSAGE = "===== End of OneDev K8s Helper Log =====";
    58 65  
     66 + public static final String BUILD_VERSION = "buildVersion";
     67 +
     68 + public static final String WORKSPACE = "workspace";
     69 +
     70 + public static final String PLACEHOLDER_PREFIX = "<&onedev#";
     71 +
     72 + public static final String PLACEHOLDER_SUFFIX = "#onedev&>";
     73 +
     74 + private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile(PLACEHOLDER_PREFIX + "(.*?)" + PLACEHOLDER_SUFFIX);
     75 +
    59 76   private static final Logger logger = LoggerFactory.getLogger(KubernetesHelper.class);
    60 77  
    61 78   private static File getBuildHome() {
    skipped 22 lines
    84 101   }
    85 102  
    86 103   private static File getWorkspace() {
    87  - return new File(getBuildHome(), "workspace");
     104 + return new File(getBuildHome(), WORKSPACE);
    88 105   }
    89 106  
    90 107   private static File getCommandHome() {
    skipped 55 lines
    146 163   }
    147 164   }
    148 165  
    149  - private static void generateCommandScript(String commandPosition, List<String> setupCommands, List<String> stepCommands) {
     166 + private static void generateCommandScript(List<Integer> position, String stepNames,
     167 + List<String> setupCommands, List<String> stepCommands) {
    150 168   try {
     169 + String positionStr = stringifyPosition(position);
    151 170   File commandHome = getCommandHome();
    152 171   if (isWindows()) {
    153  - File setupScriptFile = new File(commandHome, "setup-" + commandPosition + ".bat");
     172 + StringBuilder escapedStepNames = new StringBuilder();
     173 + for (int i=0; i<stepNames.length(); i++)
     174 + escapedStepNames.append('^').append(stepNames.charAt(i));
     175 +
     176 + File setupScriptFile = new File(commandHome, "setup-" + positionStr + ".bat");
    154 177   FileUtils.writeLines(setupScriptFile, setupCommands, "\r\n");
    155 178  
    156  - File stepScriptFile = new File(commandHome, "step-" + commandPosition + ".bat");
     179 + File stepScriptFile = new File(commandHome, "step-" + positionStr + ".bat");
    157 180   FileUtils.writeLines(stepScriptFile, stepCommands, "\r\n");
    158 181  
    159  - File scriptFile = new File(commandHome, commandPosition + ".bat");
    160  - String markPrefix = getMarkHome().getAbsolutePath() + "\\" + commandPosition;
     182 + File scriptFile = new File(commandHome, positionStr + ".bat");
     183 + String markPrefix = getMarkHome().getAbsolutePath() + "\\" + positionStr;
    161 184   List<String> scriptContent = Lists.newArrayList(
    162 185   "@echo off",
    163 186   ":wait",
    164 187   "if exist \"" + markPrefix + ".skip\" (",
    165  - " echo Skipping step #" + commandPosition + "...",
     188 + " echo Skipping step ^\"" + escapedStepNames + "^\"...",
    166 189   " echo " + LOG_END_MESSAGE,
    167 190   " goto :eof",
     191 + ")",
     192 + "if exist \"" + markPrefix + ".error\" (",
     193 + " echo Running step ^\"" + escapedStepNames + "^\"...",
     194 + " type " + markPrefix + ".error",
     195 + " copy /y nul " + markPrefix + ".failed > nul",
     196 + " echo " + LOG_END_MESSAGE,
     197 + " exit 1",
    168 198   ")",
    169 199   "if exist \"" + markPrefix + ".start\" goto start",
    170 200   "ping 127.0.0.1 -n 2 > nul",
    skipped 1 lines
    172 202   ":start",
    173 203   "cd " + getWorkspace().getAbsolutePath()
    174 204   + " && cmd /c " + setupScriptFile.getAbsolutePath()
    175  - + " && cmd /c echo Running step #" + commandPosition + "..."
     205 + + " && cmd /c echo Running step ^\"" + escapedStepNames + "^\"..."
    176 206   + " && cmd /c " + stepScriptFile.getAbsolutePath(),
    177 207   "set exit_code=%errorlevel%",
    178 208   "if \"%exit_code%\"==\"0\" (",
    skipped 5 lines
    184 214   "exit %exit_code%");
    185 215   FileUtils.writeLines(scriptFile, scriptContent, "\r\n");
    186 216   } else {
    187  - File setupScriptFile = new File(commandHome, "setup-" + commandPosition + ".sh");
     217 + String escapedStepNames = stepNames.replace("'", "'\\''");
     218 +
     219 + File setupScriptFile = new File(commandHome, "setup-" + positionStr + ".sh");
    188 220   FileUtils.writeLines(setupScriptFile, setupCommands, "\n");
    189 221  
    190  - File stepScriptFile = new File(commandHome, "step-" + commandPosition + ".sh");
     222 + File stepScriptFile = new File(commandHome, "step-" + positionStr + ".sh");
    191 223   FileUtils.writeLines(stepScriptFile, stepCommands, "\n");
    192 224  
    193  - File scriptFile = new File(commandHome, commandPosition + ".sh");
    194  - String markPrefix = getMarkHome().getAbsolutePath() + "/" + commandPosition;
     225 + File scriptFile = new File(commandHome, positionStr + ".sh");
     226 + String markPrefix = getMarkHome().getAbsolutePath() + "/" + positionStr;
    195 227   List<String> wrapperScriptContent = Lists.newArrayList(
    196  - "while [ ! -f " + markPrefix + ".start ] && [ ! -f " + markPrefix + ".skip ]",
     228 + "while [ ! -f " + markPrefix + ".start ] && [ ! -f " + markPrefix + ".skip ] && [ ! -f " + markPrefix + ".error ]",
    197 229   "do",
    198 230   " sleep 0.1",
    199 231   "done",
    200 232   "if [ -f " + markPrefix + ".skip ]",
    201 233   "then",
    202  - " echo \"Skipping step #" + commandPosition + "...\"",
     234 + " echo 'Skipping step \"" + escapedStepNames + "\"...'",
    203 235   " echo " + LOG_END_MESSAGE,
    204 236   " exit 0",
    205 237   "fi",
     238 + "if [ -f " + markPrefix + ".error ]",
     239 + "then",
     240 + " echo 'Running step \"" + escapedStepNames + "\"...'",
     241 + " cat " + markPrefix + ".error",
     242 + " touch " + markPrefix + ".failed",
     243 + " echo " + LOG_END_MESSAGE,
     244 + " exit 1",
     245 + "fi",
    206 246   "cd " + getWorkspace().getAbsolutePath()
    207 247   + " && sh " + setupScriptFile.getAbsolutePath()
    208  - + " && echo \"Running step #" + commandPosition + "...\""
     248 + + " && echo 'Running step \"" + escapedStepNames + "\"...'"
    209 249   + " && sh " + stepScriptFile.getAbsolutePath(),
    210 250   "exitCode=\"$?\"",
    211 251   "if [ $exitCode -eq 0 ]",
    skipped 57 lines
    269 309   };
    270 310   }
    271 311  
    272  - public static String encodeCommandArg(Collection<String> decoded) {
     312 + public static String encodeAsCommandArg(Collection<String> list) {
    273 313   Collection<String> base64 = new ArrayList<>();
    274  - for (String each: decoded)
    275  - base64.add(Base64.getEncoder().encodeToString(each.getBytes(StandardCharsets.UTF_8)));
    276  - String encoded = StringUtils.join(base64, "-");
    277  - if (encoded.length() == 0)
    278  - encoded = "-";
    279  - return encoded;
     314 + for (String each: list)
     315 + base64.add(Base64.getEncoder().encodeToString(each.getBytes(UTF_8)));
     316 + String commandArg = StringUtils.join(base64, "-");
     317 + if (commandArg.length() == 0)
     318 + commandArg = "-";
     319 + return commandArg;
    280 320   }
    281 321  
    282  - public static Collection<String> decodeCommandArg(String encoded) {
    283  - Collection<String> decoded = new ArrayList<>();
    284  - for (String each: Splitter.on('-').trimResults().omitEmptyStrings().split(encoded))
    285  - decoded.add(new String(Base64.getDecoder().decode(each), StandardCharsets.UTF_8));
     322 + public static Collection<String> decodeCommandArgAsCollection(String commandArg) {
     323 + Collection<String> decoded = new HashSet<>();
     324 + for (String each: Splitter.on('-').trimResults().omitEmptyStrings().split(commandArg))
     325 + decoded.add(new String(Base64.getDecoder().decode(each), UTF_8));
    286 326   return decoded;
    287 327   }
    288 328  
    skipped 36 lines
    325 365   tempFile.delete();
    326 366   }
    327 367   FileUtils.createDir(getWorkspace());
    328  - if (isWindows())
    329  - generateCommandScript("1", Lists.newArrayList(), Lists.newArrayList("@echo off", "echo hello from container"));
    330  - else
    331  - generateCommandScript("1", Lists.newArrayList(), Lists.newArrayList("echo hello from container"));
     368 + if (isWindows()) {
     369 + generateCommandScript(Lists.newArrayList(0), "test", Lists.newArrayList(),
     370 + Lists.newArrayList("@echo off", "echo hello from container"));
     371 + } else {
     372 + generateCommandScript(Lists.newArrayList(0), "test", Lists.newArrayList(),
     373 + Lists.newArrayList("echo hello from container"));
     374 + }
    332 375   } else {
    333 376   WebTarget target = client.target(serverUrl).path("rest/k8s/job-context");
    334 377   Invocation.Builder builder = target.request();
    skipped 50 lines
    385 428   } else {
    386 429   FileUtils.createDir(workspace);
    387 430   }
    388  - boolean retrieveSource = (boolean) jobContext.get("retrieveSource");
    389  - if (retrieveSource) {
    390  - String commitHash = (String) jobContext.get("commitHash");
    391  - CloneInfo cloneInfo = (CloneInfo) jobContext.get("cloneInfo");
    392  -
    393  - logger.info("Retrieving source code from {}...", cloneInfo.getCloneUrl());
    394  - 
    395  - LineConsumer infoLogger = newInfoLogger();
    396  - LineConsumer errorLogger = newErrorLogger();
    397  -
    398  - File userHome;
    399  - if (SystemUtils.IS_OS_WINDOWS)
    400  - userHome = new File(System.getProperty("user.home"));
    401  - else
    402  - userHome = new File("/root");
    403  -
    404  - Commandline git = new Commandline("git").workingDir(workspace);
    405  -
    406  - // Populate auth data here in order to clone source in init container
    407  - cloneInfo.writeAuthData(userHome, git, infoLogger, errorLogger);
    408  -
    409  - File mountedUserHome = new File(userHome, "onedev");
    410  - Commandline gitOfMountedUserHome = new Commandline("git");
    411  - gitOfMountedUserHome.environments().put("HOME", mountedUserHome.getAbsolutePath());
    412  -
    413  - // Populate auth data here in case user want to do additional pull/push in main container
    414  - cloneInfo.writeAuthData(mountedUserHome, gitOfMountedUserHome, infoLogger, errorLogger);
    415  -
    416  - File trustCertsHome = getTrustCertsHome();
    417  - if (trustCertsHome.exists()) {
    418  - List<String> trustCertContent = new ArrayList<>();
    419  - for (File file: trustCertsHome.listFiles()) {
    420  - if (file.isFile())
    421  - trustCertContent.addAll(FileUtils.readLines(file, Charset.defaultCharset()));
    422  - }
    423  - installGitCert(new File(getBuildHome(), "trust-cert.pem"), trustCertContent, git,
    424  - infoLogger, errorLogger);
    425  - }
    426  -
    427  - Integer cloneDepth = (Integer) jobContext.get("cloneDepth");
    428  - clone(workspace, cloneInfo.getCloneUrl(), commitHash, cloneDepth, git, infoLogger, errorLogger);
    429  -
    430  - git.clearArgs();
    431  - git.addArgs("remote", "add", "origin", cloneInfo.getCloneUrl());
    432  - git.execute(infoLogger, errorLogger).checkReturnCode();
    433  -
    434  - if (new File(workspace, ".gitmodules").exists()) {
    435  - logger.info("Retrieving submodules...");
    436  -
    437  - git.clearArgs();
    438  - git.addArgs("submodule", "update", "--init", "--recursive", "--force", "--quiet");
    439  - if (cloneDepth != null)
    440  - git.addArgs("--depth=" + cloneDepth);
    441  - git.execute(infoLogger, new LineConsumer() {
    442  - 
    443  - @Override
    444  - public void consume(String line) {
    445  - if (line.contains("Submodule") && line.contains("registered for path")
    446  - || line.startsWith("From ") || line.startsWith(" * branch")
    447  - || line.startsWith(" +") && line.contains("->")) {
    448  - infoLogger.consume(line);
    449  - } else {
    450  - errorLogger.consume(line);
    451  - }
    452  - }
    453  -
    454  - }).checkReturnCode();
    455  - }
    456  -
    457  - }
    458 431  
    459 432   logger.info("Generating command scripts...");
    460 433  
    461 434   List<Action> actions = (List<Action>) jobContext.get("actions");
    462  - new CompositeExecutable(actions).traverse(new LeafVisitor<Void>() {
     435 + CompositeExecutable entryExecutable = new CompositeExecutable(actions);
     436 + entryExecutable.traverse(new LeafVisitor<Void>() {
    463 437   
    464 438   @Override
    465 439   public Void visit(LeafExecutable executable, List<Integer> position) {
     440 + String stepNames = entryExecutable.getNamesAsString(position);
     441 +
    466 442   List<String> setupCommands = new ArrayList<>();
    467 443   if (isWindows()) {
    468 444   setupCommands.add("@echo off");
    skipped 8 lines
    477 453   File linkTarget = entry.getKey().getDirectory(cacheHome);
    478 454   // create possible missing parent directories
    479 455   if (isWindows()) {
     456 + setupCommands.add(String.format("echo Setting up cache \"%s\"...", link));
    480 457   setupCommands.add(String.format("if not exist \"%s\" mkdir \"%s\"", link, link));
    481 458   setupCommands.add(String.format("rmdir /q /s \"%s\"", link));
    482 459   setupCommands.add(String.format("mklink /D \"%s\" \"%s\"", link, linkTarget.getAbsolutePath()));
    483 460   } else {
     461 + setupCommands.add(String.format("echo Setting up cache \"%s\"...", link));
    484 462   setupCommands.add(String.format("mkdir -p \"%s\"", link));
    485 463   setupCommands.add(String.format("rm -rf \"%s\"", link));
    486 464   setupCommands.add(String.format("ln -s \"%s\" \"%s\"", linkTarget.getAbsolutePath(), link));
    skipped 8 lines
    495 473   if (executable instanceof CommandExecutable) {
    496 474   commands = ((CommandExecutable) executable).getCommands();
    497 475   } else {
    498  - ServerExecutable serverExecutable = (ServerExecutable) executable;
    499  -
     476 + String command;
    500 477   String classPath;
    501 478   if (SystemUtils.IS_OS_LINUX)
    502 479   classPath = "/k8s-helper/*";
    503 480   else
    504 481   classPath = "C:\\k8s-helper\\*";
     482 + if (executable instanceof CheckoutExecutable) {
     483 + CheckoutExecutable checkoutExecutable = (CheckoutExecutable) executable;
     484 + checkoutExecutable.getCloneInfo();
     485 + command = String.format("java -classpath \"%s\" io.onedev.k8shelper.CheckoutCode %s %d %s",
     486 + classPath, positionStr, checkoutExecutable.getCloneDepth(),
     487 + checkoutExecutable.getCloneInfo().toString());
     488 + } else {
     489 + ServerExecutable serverExecutable = (ServerExecutable) executable;
    505 490  
    506  - String includeFiles = encodeCommandArg(serverExecutable.getIncludeFiles());
    507  - String excludeFiles = encodeCommandArg(serverExecutable.getExcludeFiles());
    508  - String command = String.format("java -classpath \"%s\" io.onedev.k8shelper.RunServerStep %s %s %s",
    509  - classPath, positionStr, includeFiles, excludeFiles);
     491 + String includeFiles = encodeAsCommandArg(serverExecutable.getIncludeFiles());
     492 + String excludeFiles = encodeAsCommandArg(serverExecutable.getExcludeFiles());
     493 + String placeholders = encodeAsCommandArg(serverExecutable.getPlaceholders());
     494 + command = String.format("java -classpath \"%s\" io.onedev.k8shelper.RunServerStep %s %s %s %s",
     495 + classPath, positionStr, includeFiles, excludeFiles, placeholders);
     496 + }
    510 497   if (SystemUtils.IS_OS_LINUX)
    511 498   commands = Lists.newArrayList(command);
    512 499   else
    513 500   commands = Lists.newArrayList("@echo off", command);
    514 501   }
    515 502  
    516  - generateCommandScript(positionStr, setupCommands, commands);
     503 + generateCommandScript(position, stepNames, setupCommands, commands);
    517 504  
    518 505   return null;
    519 506   }
    skipped 22 lines
    542 529   }
    543 530  
    544 531   public static String stringifyPosition(List<Integer> position) {
    545  - return StringUtils.join(position, ".");
     532 + return StringUtils.join(position, "-");
    546 533   }
    547 534  
    548 535   public static List<Integer> parsePosition(String position) {
    549  - return Splitter.on('.').splitToList(position)
     536 + return Splitter.on('-').splitToList(position)
    550 537   .stream()
    551 538   .map(it->Integer.parseInt(it))
    552 539   .collect(Collectors.toList());
    skipped 13 lines
    566 553   }
    567 554   }
    568 555  
    569  - public static void clone(File workspace, String cloneUrl, String commitHash,
    570  - Integer cloneDepth, Commandline git, LineConsumer infoLogger, LineConsumer errorLogger) {
     556 + public static void cloneRepository(File workspace, String cloneUrl, String commitHash,
     557 + int cloneDepth, Commandline git, LineConsumer infoLogger, LineConsumer errorLogger) {
    571 558   git.clearArgs();
    572 559   if (!new File(workspace, ".git").exists()) {
    573 560   git.addArgs("init", ".");
    skipped 10 lines
    584 571  
    585 572   git.clearArgs();
    586 573   git.addArgs("fetch", cloneUrl, "--force", "--quiet");
    587  - if (cloneDepth != null)
     574 + if (cloneDepth != 0)
    588 575   git.addArgs("--depth=" + cloneDepth);
    589 576   git.addArgs(commitHash);
    590 577   git.execute(infoLogger, errorLogger).checkReturnCode();
    skipped 20 lines
    611 598   }
    612 599   }
    613 600  
     601 + private static Map<String, Object> readJobContext() {
     602 + byte[] jobContextBytes;
     603 + try {
     604 + jobContextBytes = FileUtils.readFileToByteArray(getJobContextFile());
     605 + } catch (IOException e) {
     606 + throw new RuntimeException(e);
     607 + }
     608 + return SerializationUtils.deserialize(jobContextBytes);
     609 + }
     610 + 
    614 611   @SuppressWarnings("unchecked")
    615 612   public static void sidecar(String serverUrl, String jobToken, boolean test) {
    616 613   LeafHandler commandHandler = new LeafHandler() {
    skipped 2 lines
    619 616   public boolean execute(LeafExecutable executable, List<Integer> position) {
    620 617   String positionStr = stringifyPosition(position);
    621 618  
    622  - File file = new File(getMarkHome(), positionStr + ".start");
     619 + File file;
     620 +
     621 + File stepScriptFile;
     622 + if (SystemUtils.IS_OS_WINDOWS)
     623 + stepScriptFile = new File(getCommandHome(), "step-" + positionStr + ".bat");
     624 + else
     625 + stepScriptFile = new File(getCommandHome(), "step-" + positionStr + ".sh");
     626 + 
    623 627   try {
     628 + String stepScript = FileUtils.readFileToString(stepScriptFile, UTF_8);
     629 + 
     630 + stepScript = replacePlaceholders(stepScript, getBuildHome());
     631 +
     632 + FileUtils.writeFile(stepScriptFile, stepScript, UTF_8.name());
     633 +
     634 + file = new File(getMarkHome(), positionStr + ".start");
    624 635   if (!file.createNewFile())
    625 636   throw new RuntimeException("Failed to create file: " + file.getAbsolutePath());
    626  - } catch (IOException e) {
    627  - throw new RuntimeException(e);
     637 + } catch (Exception e) {
     638 + file = new File(getMarkHome(), positionStr + ".error");
     639 + 
     640 + ExplicitException explicitException = ExceptionUtils.find(e, ExplicitException.class);
     641 + String errorMessage;
     642 + if (explicitException != null)
     643 + errorMessage = explicitException.getMessage().trim();
     644 + else
     645 + errorMessage = Throwables.getStackTraceAsString(e).trim();
     646 + errorMessage += "\n";
     647 + if (SystemUtils.IS_OS_WINDOWS)
     648 + errorMessage = errorMessage.replace("\n", "\r\n");
     649 +
     650 + FileUtils.writeFile(file, errorMessage, UTF_8.name());
    628 651   }
    629 652  
    630 653   File successfulFile = new File(getMarkHome(), positionStr + ".successful");
    skipped 26 lines
    657 680   "this does not matter", Lists.newArrayList("this does not matter"));
    658 681   executable.execute(commandHandler, Lists.newArrayList(1));
    659 682   } else {
    660  - byte[] jobContextBytes;
    661  - try {
    662  - jobContextBytes = FileUtils.readFileToByteArray(getJobContextFile());
    663  - } catch (IOException e) {
    664  - throw new RuntimeException(e);
    665  - }
    666  - Map<String, Object> jobContext = SerializationUtils.deserialize(jobContextBytes);
     683 + Map<String, Object> jobContext = readJobContext();
    667 684  
    668 685   List<Action> actions = (List<Action>) jobContext.get("actions");
    669 686  
    skipped 22 lines
    692 709   }
    693 710   }
    694 711  
     712 + public static void writeInt(OutputStream os, int value) {
     713 + try {
     714 + os.write(ByteBuffer.allocate(4).putInt(value).array());
     715 + } catch (IOException e) {
     716 + throw new RuntimeException(e);
     717 + }
     718 + }
     719 + 
     720 + public static void writeString(OutputStream os, String value) {
     721 + try {
     722 + byte[] valueBytes = value.getBytes(UTF_8);
     723 + os.write(ByteBuffer.allocate(4).putInt(valueBytes.length).array());
     724 + os.write(valueBytes);
     725 + } catch (IOException e) {
     726 + throw new RuntimeException(e);
     727 + }
     728 + }
     729 +
     730 + public static String readString(InputStream is) {
     731 + try {
     732 + byte[] lengthBytes = new byte[4];
     733 + if (IOUtils.readFully(is, lengthBytes) != lengthBytes.length)
     734 + throw new ExplicitException("Invalid input stream");
     735 + int length = ByteBuffer.wrap(lengthBytes).getInt();
     736 + byte[] stringBytes = new byte[length];
     737 + if (IOUtils.readFully(is, stringBytes) != stringBytes.length)
     738 + throw new ExplicitException("Invalid input stream");
     739 + return new String(stringBytes, UTF_8);
     740 + } catch (IOException e) {
     741 + throw new RuntimeException(e);
     742 + }
     743 + }
     744 +
     745 + public static int readInt(InputStream is) {
     746 + try {
     747 + byte[] intBytes = new byte[4];
     748 + if (IOUtils.readFully(is, intBytes) != intBytes.length)
     749 + throw new ExplicitException("Invalid input stream");
     750 + return ByteBuffer.wrap(intBytes).getInt();
     751 + } catch (IOException e) {
     752 + throw new RuntimeException(e);
     753 + }
     754 + }
     755 +
     756 + public static void checkoutCode(String serverUrl, String jobToken, String positionStr,
     757 + int cloneDepth, CloneInfo cloneInfo) throws IOException {
     758 + Map<String, Object> jobContext = readJobContext();
     759 + String commitHash = (String) jobContext.get("commitHash");
     760 +
     761 + logger.info("Checking out code from {}...", cloneInfo.getCloneUrl());
     762 + 
     763 + LineConsumer infoLogger = newInfoLogger();
     764 + LineConsumer errorLogger = newErrorLogger();
     765 +
     766 + File userHome;
     767 + if (SystemUtils.IS_OS_WINDOWS)
     768 + userHome = new File(System.getProperty("user.home"));
     769 + else
     770 + userHome = new File("/root");
     771 +
     772 + File workspace = getWorkspace();
     773 + Commandline git = new Commandline("git").workingDir(workspace);
     774 +
     775 + cloneInfo.writeAuthData(userHome, git, infoLogger, errorLogger);
     776 +
     777 + File mountedUserHome = new File(userHome, "onedev");
     778 + Commandline gitOfMountedUserHome = new Commandline("git");
     779 + gitOfMountedUserHome.environments().put("HOME", mountedUserHome.getAbsolutePath());
     780 +
     781 + // Populate auth data here in case user want to do additional pull/push in other step container
     782 + cloneInfo.writeAuthData(mountedUserHome, gitOfMountedUserHome, infoLogger, errorLogger);
     783 +
     784 + File trustCertsHome = getTrustCertsHome();
     785 + if (trustCertsHome.exists()) {
     786 + List<String> trustCertContent = new ArrayList<>();
     787 + for (File file: trustCertsHome.listFiles()) {
     788 + if (file.isFile())
     789 + trustCertContent.addAll(FileUtils.readLines(file, Charset.defaultCharset()));
     790 + }
     791 + installGitCert(new File(getBuildHome(), "trust-cert.pem"), trustCertContent, git,
     792 + infoLogger, errorLogger);
     793 + }
     794 +
     795 + cloneRepository(workspace, cloneInfo.getCloneUrl(), commitHash, cloneDepth, git, infoLogger, errorLogger);
     796 +
     797 + git.clearArgs();
     798 + git.addArgs("remote", "add", "origin", cloneInfo.getCloneUrl());
     799 + git.execute(infoLogger, errorLogger).checkReturnCode();
     800 +
     801 + if (new File(workspace, ".gitmodules").exists()) {
     802 + logger.info("Retrieving submodules...");
     803 +
     804 + git.clearArgs();
     805 + git.addArgs("submodule", "update", "--init", "--recursive", "--force", "--quiet");
     806 + if (cloneDepth != 0)
     807 + git.addArgs("--depth=" + cloneDepth);
     808 + git.execute(infoLogger, new LineConsumer() {
     809 + 
     810 + @Override
     811 + public void consume(String line) {
     812 + if (line.contains("Submodule") && line.contains("registered for path")
     813 + || line.startsWith("From ") || line.startsWith(" * branch")
     814 + || line.startsWith(" +") && line.contains("->")) {
     815 + infoLogger.consume(line);
     816 + } else {
     817 + errorLogger.consume(line);
     818 + }
     819 + }
     820 +
     821 + }).checkReturnCode();
     822 + }
     823 +
     824 + }
     825 +
    695 826   public static void runServerStep(String serverUrl, String jobToken, String positionStr,
    696  - String encodedIncludeFiles, String encodedExcludeFiles) {
     827 + String encodedIncludeFiles, String encodedExcludeFiles, String encodedPlaceholders) {
    697 828   installJVMCert();
    698 829  
    699 830   Client client = ClientBuilder.newClient();
    skipped 4 lines
    704 835   builder.header(HttpHeaders.AUTHORIZATION, BEARER + " " + jobToken);
    705 836   
    706 837   List<Integer> position = parsePosition(positionStr);
    707  - Collection<String> includeFiles = decodeCommandArg(encodedIncludeFiles);
    708  - Collection<String> excludeFiles = decodeCommandArg(encodedExcludeFiles);
     838 + Collection<String> includeFiles = decodeCommandArgAsCollection(encodedIncludeFiles);
     839 + Collection<String> excludeFiles = decodeCommandArgAsCollection(encodedExcludeFiles);
     840 + Collection<String> placeholders = decodeCommandArgAsCollection(encodedPlaceholders);
     841 +
     842 + Map<String, String> placeholderValues = readPlaceholderValues(getBuildHome(), placeholders);
    709 843   
    710 844   StreamingOutput os = new StreamingOutput() {
    711 845   
    712 846   @Override
    713 847   public void write(OutputStream os) throws IOException {
    714  - os.write(ByteBuffer.allocate(4).putInt(position.size()).array());
    715  - for (int each: position) {
    716  - os.write(ByteBuffer.allocate(4).putInt(each).array());
     848 + writeInt(os, position.size());
     849 + for (int each: position)
     850 + writeInt(os, each);
     851 +
     852 + writeInt(os, placeholderValues.size());
     853 + for (Map.Entry<String, String> entry: placeholderValues.entrySet()) {
     854 + writeString(os, entry.getKey());
     855 + writeString(os, entry.getValue());
    717 856   }
    718  - TarUtils.tar(getWorkspace(), includeFiles, excludeFiles, os);
     857 +
     858 + TarUtils.tar(
     859 + getWorkspace(),
     860 + replacePlaceholders(includeFiles, placeholderValues),
     861 + replacePlaceholders(excludeFiles, placeholderValues),
     862 + os);
    719 863   }
    720 864  
    721 865   };
    722 866  
    723 867   try (Response response = builder.post(Entity.entity(os, MediaType.APPLICATION_OCTET_STREAM))) {
    724 868   checkStatus(response);
    725  - byte[] logBytes = response.readEntity(byte[].class);
    726  - List<String> logMessages = SerializationUtils.deserialize(logBytes);
    727  - for (String logMessage: logMessages)
     869 + ServerExecutionResult result = SerializationUtils.deserialize(response.readEntity(byte[].class));
     870 + for (String logMessage: result.getLogMessages())
    728 871   logger.info(logMessage);
     872 + if (result.getOutputFiles() != null) {
     873 + for (Map.Entry<String, byte[]> entry: result.getOutputFiles().entrySet()) {
     874 + try {
     875 + FileUtils.writeByteArrayToFile(
     876 + new File(getBuildHome(), entry.getKey()),
     877 + entry.getValue());
     878 + } catch (IOException e) {
     879 + throw new RuntimeException(e);
     880 + }
     881 + }
     882 + }
    729 883   }
    730 884   } finally {
    731 885   client.close();
    732 886   }
     887 + }
     888 +
     889 + public static Collection<String> parsePlaceholders(String string) {
     890 + Collection<String> placeholderFiles = new HashSet<>();
     891 + Matcher matcher = PLACEHOLDER_PATTERN.matcher(string);
     892 + while (matcher.find())
     893 + placeholderFiles.add(matcher.group(1));
     894 + return placeholderFiles;
     895 + }
     896 + 
     897 + public static Map<String, String> readPlaceholderValues(File dir, Collection<String> placeholders) {
     898 + Map<String, String> placeholderValues = new HashMap<>();
     899 + for (String placeholder: placeholders) {
     900 + File file = new File(dir, placeholder);
     901 + if (file.exists()) {
     902 + try {
     903 + placeholderValues.put(placeholder, FileUtils.readFileToString(file, UTF_8).trim());
     904 + } catch (IOException e) {
     905 + throw new RuntimeException(e);
     906 + }
     907 + }
     908 + }
     909 + return placeholderValues;
     910 + }
     911 +
     912 + public static String replacePlaceholders(String string, Map<String, String> placeholderValues) {
     913 + Matcher matcher = PLACEHOLDER_PATTERN.matcher(string);
     914 + StringBuffer buffer = new StringBuffer();
     915 + while (matcher.find()) {
     916 + String placeholder = matcher.group(1);
     917 + String placeholderValue = placeholderValues.get(placeholder);
     918 + if (placeholderValue != null) {
     919 + matcher.appendReplacement(buffer, placeholderValue);
     920 + } else if (placeholder.startsWith(WORKSPACE + "/")) {
     921 + throw new ExplicitException("Error replacing placeholder: unable to find file '"
     922 + + placeholder.substring(WORKSPACE.length()+1) + "' in workspace");
     923 + } else if (placeholder.equals(BUILD_VERSION)){
     924 + throw new ExplicitException("Error replacing placeholder: build version not set yet");
     925 + }
     926 + }
     927 + matcher.appendTail(buffer);
     928 + return buffer.toString();
     929 + }
     930 +
     931 + public static String replacePlaceholders(String string, File buildHome) {
     932 + Collection<String> placeholders = parsePlaceholders(string);
     933 + Map<String, String> placeholderValues = readPlaceholderValues(buildHome, placeholders);
     934 + return replacePlaceholders(string, placeholderValues);
     935 + }
     936 +
     937 + public static Collection<String> replacePlaceholders(Collection<String> collection,
     938 + Map<String, String> placeholderValues) {
     939 + Collection<String> replacedCollection = new ArrayList<>();
     940 + for (String each: collection)
     941 + replacedCollection.add(replacePlaceholders(each, placeholderValues));
     942 + return replacedCollection;
     943 + }
     944 +
     945 + public static Collection<String> replacePlaceholders(Collection<String> collection, File buildHome) {
     946 + Collection<String> replacedCollection = new ArrayList<>();
     947 + for (String each: collection)
     948 + replacedCollection.add(replacePlaceholders(each, buildHome));
     949 + return replacedCollection;
    733 950   }
    734 951  
    735 952  }
    skipped 1 lines
  • ■ ■ ■ ■ ■
    src/main/java/io/onedev/k8shelper/RunServerStep.java
    skipped 4 lines
    5 5   
    6 6  import com.google.common.base.Preconditions;
    7 7   
    8  -import io.onedev.commons.utils.ExplicitException;
    9  - 
    10 8  public class RunServerStep {
    11 9   
    12 10   private static final Logger logger = LoggerFactory.getLogger(RunServerStep.class);
    skipped 8 lines
    21 19   if (jobToken == null)
    22 20   throw new RuntimeException("Environment '" + KubernetesHelper.ENV_JOB_TOKEN + "' is not defined");
    23 21   
    24  - if (args.length < 3)
    25  - throw new ExplicitException("Insufficient args to run server step");
    26  -
    27  - KubernetesHelper.runServerStep(serverUrl, jobToken, args[0], args[1], args[2]);
     22 + KubernetesHelper.runServerStep(serverUrl, jobToken, args[0], args[1], args[2], args[3]);
    28 23   } catch (Exception e) {
    29 24   logger.error("Error executing step", e);
    30 25   exitCode = 1;
    skipped 8 lines
  • ■ ■ ■ ■ ■
    src/main/java/io/onedev/k8shelper/ServerExecutable.java
    1 1  package io.onedev.k8shelper;
    2 2   
     3 +import java.util.Collection;
    3 4  import java.util.Set;
    4 5   
    5 6  public class ServerExecutable extends LeafExecutable {
    skipped 6 lines
    12 13  
    13 14   private final Set<String> excludeFiles;
    14 15  
    15  - public ServerExecutable(Object step, Set<String> includeFiles, Set<String> excludeFiles) {
     16 + private final Collection<String> placeholders;
     17 +
     18 + public ServerExecutable(Object step, Set<String> includeFiles, Set<String> excludeFiles, Collection<String> placeholders) {
    16 19   this.step = step;
    17 20   this.includeFiles = includeFiles;
    18 21   this.excludeFiles = excludeFiles;
     22 + this.placeholders = placeholders;
    19 23   }
    20 24   
    21 25   public Object getStep() {
    skipped 6 lines
    28 32   
    29 33   public Set<String> getExcludeFiles() {
    30 34   return excludeFiles;
     35 + }
     36 + 
     37 + public Collection<String> getPlaceholders() {
     38 + return placeholders;
    31 39   }
    32 40   
    33 41  }
    skipped 1 lines
  • ■ ■ ■ ■ ■ ■
    src/main/java/io/onedev/k8shelper/ServerExecutionResult.java
     1 +package io.onedev.k8shelper;
     2 + 
     3 +import java.io.Serializable;
     4 +import java.util.List;
     5 +import java.util.Map;
     6 + 
     7 +import javax.annotation.Nullable;
     8 + 
     9 +public class ServerExecutionResult implements Serializable {
     10 + 
     11 + private static final long serialVersionUID = 1L;
     12 + 
     13 + private final List<String> logMessages;
     14 +
     15 + private final Map<String, byte[]> outputFiles;
     16 +
     17 + public ServerExecutionResult(List<String> logMessages, @Nullable Map<String, byte[]> outputFiles) {
     18 + this.logMessages = logMessages;
     19 + this.outputFiles = outputFiles;
     20 + }
     21 + 
     22 + public List<String> getLogMessages() {
     23 + return logMessages;
     24 + }
     25 + 
     26 + @Nullable
     27 + public Map<String, byte[]> getOutputFiles() {
     28 + return outputFiles;
     29 + }
     30 +
     31 +}
     32 + 
  • ■ ■ ■ ■ ■ ■
    src/main/java/io/onedev/k8shelper/SshCloneInfo.java
    1 1  package io.onedev.k8shelper;
    2 2   
    3 3  import java.io.File;
     4 +import java.nio.charset.StandardCharsets;
     5 +import java.util.Base64;
    4 6   
    5 7  import org.apache.commons.lang3.SystemUtils;
    6 8   
    skipped 27 lines
    34 36   chmod.execute(infoLogger, errorLogger).checkReturnCode();
    35 37   }
    36 38   FileUtils.writeFile(new File(sshDir, "known_hosts"), knownHosts);
     39 + }
     40 + 
     41 + @Override
     42 + public String toString() {
     43 + return "ssh-"
     44 + + Base64.getEncoder().encodeToString(getCloneUrl().getBytes(StandardCharsets.UTF_8))
     45 + + "-" + Base64.getEncoder().encodeToString(privateKey.getBytes(StandardCharsets.UTF_8))
     46 + + "-" + Base64.getEncoder().encodeToString(knownHosts.getBytes(StandardCharsets.UTF_8));
    37 47   }
    38 48  
    39 49  }
    skipped 1 lines
Please wait...
Page is in error, reload to recover