LCOV - code coverage report
Current view: top level - pgm - SwitchSecureStore.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 0 93 0.0 %
Date: 2022-11-19 15:00:39 Functions: 0 12 0.0 %

          Line data    Source code
       1             : // Copyright (C) 2014 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.pgm;
      16             : 
      17             : import com.google.common.base.Joiner;
      18             : import com.google.common.base.Strings;
      19             : import com.google.common.collect.Iterables;
      20             : import com.google.common.flogger.FluentLogger;
      21             : import com.google.gerrit.common.IoUtil;
      22             : import com.google.gerrit.common.Nullable;
      23             : import com.google.gerrit.common.SiteLibraryLoaderUtil;
      24             : import com.google.gerrit.pgm.util.SiteProgram;
      25             : import com.google.gerrit.server.config.SitePaths;
      26             : import com.google.gerrit.server.plugins.JarScanner;
      27             : import com.google.gerrit.server.securestore.DefaultSecureStore;
      28             : import com.google.gerrit.server.securestore.SecureStore;
      29             : import com.google.gerrit.server.securestore.SecureStore.EntryKey;
      30             : import com.google.inject.Injector;
      31             : import java.io.IOException;
      32             : import java.nio.file.Files;
      33             : import java.nio.file.Path;
      34             : import java.nio.file.Paths;
      35             : import java.util.Arrays;
      36             : import java.util.List;
      37             : import java.util.jar.JarFile;
      38             : import java.util.zip.ZipEntry;
      39             : import org.eclipse.jgit.errors.ConfigInvalidException;
      40             : import org.eclipse.jgit.storage.file.FileBasedConfig;
      41             : import org.eclipse.jgit.util.FS;
      42             : import org.kohsuke.args4j.Option;
      43             : 
      44           0 : public class SwitchSecureStore extends SiteProgram {
      45           0 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      46             : 
      47             :   private static String getSecureStoreClassFromGerritConfig(SitePaths sitePaths) {
      48           0 :     FileBasedConfig cfg = new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.DETECTED);
      49             :     try {
      50           0 :       cfg.load();
      51           0 :     } catch (IOException | ConfigInvalidException e) {
      52           0 :       throw new RuntimeException("Cannot read gerrit.config file", e);
      53           0 :     }
      54           0 :     return cfg.getString("gerrit", null, "secureStoreClass");
      55             :   }
      56             : 
      57             :   @Option(
      58             :       name = "--new-secure-store-lib",
      59             :       usage = "Path to new SecureStore implementation",
      60             :       required = true)
      61             :   private String newSecureStoreLib;
      62             : 
      63             :   @Override
      64             :   public int run() throws Exception {
      65           0 :     SitePaths sitePaths = new SitePaths(getSitePath());
      66           0 :     Path newSecureStorePath = Paths.get(newSecureStoreLib);
      67           0 :     if (!Files.exists(newSecureStorePath)) {
      68           0 :       logger.atSevere().log("File %s doesn't exist", newSecureStorePath.toAbsolutePath());
      69           0 :       return -1;
      70             :     }
      71             : 
      72           0 :     String newSecureStore = getNewSecureStoreClassName(newSecureStorePath);
      73           0 :     String currentSecureStoreName = getCurrentSecureStoreClassName(sitePaths);
      74             : 
      75           0 :     if (currentSecureStoreName.equals(newSecureStore)) {
      76           0 :       logger.atSevere().log(
      77             :           "Old and new SecureStore implementation names "
      78             :               + "are the same. Migration will not work");
      79           0 :       return -1;
      80             :     }
      81             : 
      82           0 :     IoUtil.loadJARs(newSecureStorePath);
      83           0 :     SiteLibraryLoaderUtil.loadSiteLib(sitePaths.lib_dir);
      84             : 
      85           0 :     logger.atInfo().log(
      86             :         "Current secureStoreClass property (%s) will be replaced with %s",
      87             :         currentSecureStoreName, newSecureStore);
      88           0 :     Injector dbInjector = createDbInjector();
      89           0 :     SecureStore currentStore = getSecureStore(currentSecureStoreName, dbInjector);
      90           0 :     SecureStore newStore = getSecureStore(newSecureStore, dbInjector);
      91             : 
      92           0 :     migrateProperties(currentStore, newStore);
      93             : 
      94           0 :     removeOldLib(sitePaths, currentSecureStoreName);
      95           0 :     copyNewLib(sitePaths, newSecureStorePath);
      96             : 
      97           0 :     updateGerritConfig(sitePaths, newSecureStore);
      98             : 
      99           0 :     return 0;
     100             :   }
     101             : 
     102             :   private void migrateProperties(SecureStore currentStore, SecureStore newStore) {
     103           0 :     logger.atInfo().log("Migrate entries");
     104           0 :     for (EntryKey key : currentStore.list()) {
     105           0 :       String[] value = currentStore.getList(key.section, key.subsection, key.name);
     106           0 :       if (value != null) {
     107           0 :         newStore.setList(key.section, key.subsection, key.name, Arrays.asList(value));
     108             :       } else {
     109           0 :         String msg = String.format("Cannot migrate entry for %s", key.section);
     110           0 :         if (key.subsection != null) {
     111           0 :           msg = msg + String.format(".%s", key.subsection);
     112             :         }
     113           0 :         msg = msg + String.format(".%s", key.name);
     114           0 :         throw new RuntimeException(msg);
     115             :       }
     116           0 :     }
     117           0 :   }
     118             : 
     119             :   private void removeOldLib(SitePaths sitePaths, String currentSecureStoreName) throws IOException {
     120           0 :     Path oldSecureStore = findJarWithSecureStore(sitePaths, currentSecureStoreName);
     121           0 :     if (oldSecureStore != null) {
     122           0 :       logger.atInfo().log(
     123           0 :           "Removing old SecureStore (%s) from lib/ directory", oldSecureStore.getFileName());
     124             :       try {
     125           0 :         Files.delete(oldSecureStore);
     126           0 :       } catch (IOException e) {
     127           0 :         logger.atSevere().withCause(e).log("Cannot remove %s", oldSecureStore.toAbsolutePath());
     128           0 :       }
     129             :     } else {
     130           0 :       logger.atInfo().log(
     131             :           "Cannot find jar with old SecureStore (%s) in lib/ directory", currentSecureStoreName);
     132             :     }
     133           0 :   }
     134             : 
     135             :   private void copyNewLib(SitePaths sitePaths, Path newSecureStorePath) throws IOException {
     136           0 :     logger.atInfo().log(
     137           0 :         "Copy new SecureStore (%s) into lib/ directory", newSecureStorePath.getFileName());
     138           0 :     Files.copy(newSecureStorePath, sitePaths.lib_dir.resolve(newSecureStorePath.getFileName()));
     139           0 :   }
     140             : 
     141             :   private void updateGerritConfig(SitePaths sitePaths, String newSecureStore)
     142             :       throws IOException, ConfigInvalidException {
     143           0 :     logger.atInfo().log(
     144             :         "Set gerrit.secureStoreClass property of gerrit.config to %s", newSecureStore);
     145           0 :     FileBasedConfig config = new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.DETECTED);
     146           0 :     config.load();
     147           0 :     config.setString("gerrit", null, "secureStoreClass", newSecureStore);
     148           0 :     config.save();
     149           0 :   }
     150             : 
     151             :   private String getNewSecureStoreClassName(Path secureStore) throws IOException {
     152           0 :     try (JarScanner scanner = new JarScanner(secureStore)) {
     153           0 :       List<String> newSecureStores = scanner.findSubClassesOf(SecureStore.class);
     154           0 :       if (newSecureStores.isEmpty()) {
     155           0 :         throw new RuntimeException(
     156           0 :             String.format(
     157             :                 "Cannot find implementation of SecureStore interface in %s",
     158           0 :                 secureStore.toAbsolutePath()));
     159             :       }
     160           0 :       if (newSecureStores.size() > 1) {
     161           0 :         throw new RuntimeException(
     162           0 :             String.format(
     163             :                 "Found too many implementations of SecureStore:\n%s\nin %s",
     164           0 :                 Joiner.on("\n").join(newSecureStores), secureStore.toAbsolutePath()));
     165             :       }
     166           0 :       return Iterables.getOnlyElement(newSecureStores);
     167             :     }
     168             :   }
     169             : 
     170             :   private String getCurrentSecureStoreClassName(SitePaths sitePaths) {
     171           0 :     String current = getSecureStoreClassFromGerritConfig(sitePaths);
     172           0 :     if (!Strings.isNullOrEmpty(current)) {
     173           0 :       return current;
     174             :     }
     175           0 :     return DefaultSecureStore.class.getName();
     176             :   }
     177             : 
     178             :   private SecureStore getSecureStore(String className, Injector injector) {
     179             :     try {
     180             :       @SuppressWarnings("unchecked")
     181           0 :       Class<? extends SecureStore> clazz = (Class<? extends SecureStore>) Class.forName(className);
     182           0 :       return injector.getInstance(clazz);
     183           0 :     } catch (ClassNotFoundException e) {
     184           0 :       throw new RuntimeException(
     185           0 :           String.format("Cannot load SecureStore implementation: %s", className), e);
     186             :     }
     187             :   }
     188             : 
     189             :   @Nullable
     190             :   private Path findJarWithSecureStore(SitePaths sitePaths, String secureStoreClass)
     191             :       throws IOException {
     192           0 :     List<Path> jars = SiteLibraryLoaderUtil.listJars(sitePaths.lib_dir);
     193           0 :     String secureStoreClassPath = secureStoreClass.replace('.', '/') + ".class";
     194           0 :     for (Path jar : jars) {
     195           0 :       try (JarFile jarFile = new JarFile(jar.toFile())) {
     196           0 :         ZipEntry entry = jarFile.getEntry(secureStoreClassPath);
     197           0 :         if (entry != null) {
     198           0 :           return jar;
     199             :         }
     200           0 :       } catch (IOException e) {
     201           0 :         logger.atSevere().withCause(e).log("%s", e.getMessage());
     202           0 :       }
     203           0 :     }
     204           0 :     return null;
     205             :   }
     206             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750