Package org.platformlayer.ops.packages

Source Code of org.platformlayer.ops.packages.AptPackageManager$AptInfoCache

package org.platformlayer.ops.packages;

import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;

import org.platformlayer.images.model.ConfigurePackage;
import org.platformlayer.ops.Command;
import org.platformlayer.ops.CommandEnvironment;
import org.platformlayer.ops.FileUpload;
import org.platformlayer.ops.OpsException;
import org.platformlayer.ops.OpsTarget;
import org.platformlayer.ops.process.ProcessExecution;
import org.platformlayer.ops.process.ProcessExecutionException;
import org.platformlayer.ops.proxy.HttpProxyHelper;
import org.platformlayer.ops.proxy.HttpProxyHelper.Usage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fathomdb.TimeSpan;
import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public class AptPackageManager {
  static final Logger log = LoggerFactory.getLogger(AptPackageManager.class);

  @Inject
  HttpProxyHelper httpProxies;

  class AptInfoCache {
    public List<String> installedPackages;
  }

  final Map<OpsTarget, AptInfoCache> cache = Maps.newHashMap();

  AptInfoCache getCache(OpsTarget target) {
    AptInfoCache cached = cache.get(target);
    if (cached == null) {
      cached = new AptInfoCache();
      cache.put(target, cached);
    }
    return cached;
  }

  void flushCache(OpsTarget target) {
    cache.remove(target);
  }

  public void install(OpsTarget target, String... packageNames) throws OpsException {
    install(target, Arrays.asList(packageNames));
  }

  public void upgrade(OpsTarget target) throws OpsException {
    CommandEnvironment commandEnvironment = buildEnvironmentWithProxy(target);

    Command command = Command.build("apt-get --yes upgrade");
    target.executeCommand(command.setEnvironment(commandEnvironment).setTimeout(TimeSpan.TEN_MINUTES));

    flushCache(target);
  }

  public void clean(OpsTarget target) throws OpsException {
    CommandEnvironment commandEnvironment = buildEnvironmentWithProxy(target);

    Command command = Command.build("apt-get clean");
    target.executeCommand(command.setEnvironment(commandEnvironment).setTimeout(TimeSpan.TEN_MINUTES));
  }

  private boolean haveCurl(OpsTarget target) throws OpsException {
    return target.getFilesystemInfoFile(new File("/usr/bin/curl")) != null;
  }

  public void addRepositoryKeyUrl(OpsTarget target, String url) throws OpsException {
    Command command = Command.build("wget -q -O - {0} | apt-key add -", url);
    target.executeCommand(command);

    flushCache(target);
  }

  public void addRepository(OpsTarget target, String id, List<String> sources) throws OpsException {
    File dir = new File("/etc/apt/sources.list.d");
    File file = new File(dir, id + ".list");

    StringBuilder sb = new StringBuilder();
    for (String source : sources) {
      sb.append(source);
      sb.append("\n");
    }

    log.info("Uploading to " + file + ": " + sb.toString());

    FileUpload.upload(target, file, sb.toString());

    flushCache(target);
  }

  public void preconfigurePackages(OpsTarget target, List<ConfigurePackage> settings) throws OpsException {
    File preseedTmpDir = target.createTempDir();

    StringBuilder sb = new StringBuilder();

    for (ConfigurePackage conf : settings) {
      String confType = conf.getType();
      if (confType == null) {
        confType = "string";
      }
      String line = conf.getPackageName() + "\t" + conf.getKey() + "\t" + conf.getType() + "\t" + conf.getValue()
          + "\n";
      sb.append(line);
    }

    File preseedFile = new File(preseedTmpDir, "misc.preseed");

    FileUpload.upload(target, preseedFile, sb.toString());
    target.executeCommand(Command.build("cat {0} | debconf-set-selections", preseedFile));
  }

  public List<String> getInstalledPackageInfo(OpsTarget target) throws OpsException {
    AptInfoCache cached = getCache(target);

    if (cached.installedPackages == null) {
      Command command = Command.build("/usr/bin/dpkg --get-selections");
      ProcessExecution execution = target.executeCommand(command);

      final List<String> packages = Lists.newArrayList();
      for (String line : Splitter.on("\n").split(execution.getStdOut())) {
        line = line.trim();
        if (line.isEmpty()) {
          continue;
        }

        List<String> tokens = Lists.newArrayList(Splitter.on(CharMatcher.WHITESPACE).omitEmptyStrings()
            .split(line));
        if (tokens.size() != 2) {
          throw new OpsException("Error parsing line; expected 2 items: " + line);
        }
        String state = tokens.get(1);
        if (state.equals("install")) {
          String packageName = tokens.get(0);
          int colonIndex = packageName.indexOf(':');
          if (colonIndex != -1) {
            // Architecture sometimes follows package name
            packageName = packageName.substring(0, colonIndex);
          }
          packages.add(packageName);
        } else if (state.equals("deinstall")) {
          // Not installed (?)
        } else {
          throw new OpsException("Unknown package state in line: " + line);
        }
      }
      cached.installedPackages = packages;
    } else {
      log.debug("Re-using cached package info");
    }

    return cached.installedPackages;
  }

  // // some packages might need longer if the network is slow
  // private static Map<String, TimeSpan> packageTimeMap = new HashMap<String, TimeSpan>() {
  // {
  // put("sun-java6-jdk", new TimeSpan("30m"));
  // }
  // };
  //

  // public static void update(OpsServer server) throws OpsException {
  // SimpleBashCommand command = new SimpleBashCommand("/usr/bin/apt-get");
  // command.addLiteralArg("-q");
  // command.addLiteralArg("-y");
  // command.addLiteralArg("update");
  //
  // server.simpleRun(command, new TimeSpan("5m"));
  // }
  //
  // public static void installUpdates(OpsServer server) throws OpsException {
  // SimpleBashCommand command = new SimpleBashCommand("/usr/bin/apt-get");
  // command.addLiteralArg("-q");
  // command.addLiteralArg("-y");
  // command.addLiteralArg("upgrade");
  //
  // server.simpleRun(command, new TimeSpan("15m"));
  // }

  public List<String> findOutOfDatePackages(OpsTarget target) throws OpsException {
    Command command = Command.build("apt-get -q -q --simulate dist-upgrade");
    ProcessExecution execution = target.executeCommand(command);

    final List<String> packages = Lists.newArrayList();
    for (String line : Splitter.on("\n").split(execution.getStdOut())) {
      line = line.trim();
      if (line.isEmpty()) {
        continue;
      }

      List<String> tokens = Lists
          .newArrayList(Splitter.on(CharMatcher.WHITESPACE).omitEmptyStrings().split(line));
      if (tokens.size() < 2) {
        continue;
      }

      String action = tokens.get(0);
      if (action.equals("Inst")) {
        // e.g. Inst coreutils [8.13-3.1] (8.13-3.2 Debian:testing [amd64])
        packages.add(tokens.get(1));

        // New version is in tokens[2]
        // Current version is in tokens[3], but that's a trick
      }

    }
    return packages;
  }

  private CommandEnvironment buildEnvironmentWithProxy(OpsTarget target) throws OpsException {
    CommandEnvironment commandEnvironment = null;

    if (!haveCurl(target)) {
      log.warn("We don't yet have curl; can't detect best proxy so won't use a proxy");
    } else {
      commandEnvironment = httpProxies.getHttpProxyEnvironment(target, Usage.SoftwarePackages, null);
    }

    if (commandEnvironment == null) {
      commandEnvironment = new CommandEnvironment();
    }

    return commandEnvironment;
  }

  public void install(OpsTarget target, Iterable<String> packageNames) throws OpsException {
    CommandEnvironment commandEnvironment = buildEnvironmentWithProxy(target);
    commandEnvironment.put("DEBIAN_FRONTEND", "noninteractive");

    log.info("Installing APT packages: " + Joiner.on(",").join(packageNames));

    Command command = Command.build("apt-get install --yes");
    for (String packageName : packageNames) {
      command.addQuoted(packageName);
    }
    target.executeCommand(command.setEnvironment(commandEnvironment).setTimeout(TimeSpan.TEN_MINUTES));

    flushCache(target);
  }

  public void update(OpsTarget target, boolean force) throws OpsException {
    if (!force && !isUpdateNeeded(target)) {
      log.info("apt-get update not needed; won't update");
      return;
    }

    log.info("Updating apt repositories");

    CommandEnvironment commandEnvironment = buildEnvironmentWithProxy(target);

    Command command = Command.build("apt-get --yes update");
    command = command.setEnvironment(commandEnvironment).setTimeout(TimeSpan.TEN_MINUTES);
    executeAptCommand(target, command);

    flushCache(target);
  }

  private boolean isUpdateNeeded(OpsTarget target) {
    log.info("isUpdateNeeded stub-implemented");
    return true;
  }

  private void executeAptCommand(OpsTarget target, Command command) throws OpsException {
    for (int attempt = 0; attempt < 2; attempt++) {
      try {
        target.executeCommand(command);
      } catch (ProcessExecutionException e) {
        if (attempt == 0) {
          ProcessExecution execution = e.getExecution();
          if (execution != null) {
            String stdErr = execution.getStdErr();
            // Stderr=E: dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the
            // problem.
            if (stdErr.contains("dpkg --configure -a")) {
              log.warn("Detected that dpkg --configure -a is needed");
              doDpkgConfigure(target);
              log.warn("Retrying apt command");
              continue;
            }
          }
        }
        throw new OpsException("Error executing apt command", e);
      }
      return;
    }
  }

  private void doDpkgConfigure(OpsTarget target) throws OpsException {
    CommandEnvironment commandEnvironment = buildEnvironmentWithProxy(target);
    Command command = Command.build("dpkg --configure -a");
    command = command.setEnvironment(commandEnvironment).setTimeout(TimeSpan.TEN_MINUTES);
    target.executeCommand(command);
  }

}
TOP

Related Classes of org.platformlayer.ops.packages.AptPackageManager$AptInfoCache

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.