aboutsummaryrefslogtreecommitdiffstats
path: root/node-admin/src
diff options
context:
space:
mode:
authorHåkon Hallingstad <hakon@oath.com>2018-08-28 11:40:21 +0200
committerHåkon Hallingstad <hakon@oath.com>2018-08-28 11:40:21 +0200
commit6591a9f52683829f296c9b0eb2610b9278b5f5ba (patch)
treeea7b9e4cddac1851cb4681ea7188af5098e68e37 /node-admin/src
parentd8beea0be39ed0e3a9686a9f555d7d279e08dde1 (diff)
Extract YumPackageName
Diffstat (limited to 'node-admin/src')
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/Yum.java140
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumPackageName.java148
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumPackageNameTest.java62
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumTest.java14
4 files changed, 218 insertions, 146 deletions
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/Yum.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/Yum.java
index 5a95c90d216..226135a1617 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/Yum.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/Yum.java
@@ -94,10 +94,10 @@ public class Yum {
*
* <p>WARNING: In order to simplify the user interface of {@link #lockVersion()},
* the package name specified in the command, e.g. {@link #install(String...)}, MUST be of
- * a simple format, see {@link PackageName#fromString(String)}.
+ * a simple format, see {@link YumPackageName#fromString(String)}.
*/
public GenericYumCommand lockVersion() {
- packages.forEach(PackageName::fromString); // to throw any parse error here instead of later
+ packages.forEach(YumPackageName::fromString); // to throw any parse error here instead of later
lockVersion = true;
return this;
}
@@ -110,19 +110,17 @@ public class Yum {
// Remove all locks for other version
packages.stream()
- .map(PackageName::fromString)
- .map(PackageName.Builder::new)
- .map(builder -> builder.setArchitecture("*").build())
+ .map(YumPackageName::fromString)
.forEach(packageName -> {
packageNamesToLock.add(packageName.getName());
- fullPackageNamesToLock.add(packageName.toFullName());
+ fullPackageNamesToLock.add(packageName.toVersionLock());
});
terminal.newCommandLine(context)
.add("yum", "--quiet", "versionlock", "list")
.executeSilently()
.getOutputLinesStream()
- .map(PackageName::parseString)
+ .map(YumPackageName::parseString)
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(packageName -> {
@@ -130,10 +128,10 @@ public class Yum {
if (packageNamesToLock.contains(packageName.getName())) {
// If existing lock doesn't exactly match the full package name,
// it means it's locked to another version and we must remove that lock.
- String fullName = packageName.toFullName();
- if (!fullPackageNamesToLock.remove(fullName)) {
+ String versionLockName = packageName.toVersionLock();
+ if (!fullPackageNamesToLock.remove(versionLockName)) {
terminal.newCommandLine(context)
- .add("yum", "versionlock", "delete", fullName)
+ .add("yum", "versionlock", "delete", versionLockName)
.execute();
}
}
@@ -174,126 +172,4 @@ public class Yum {
}
}
- /** YUM package name. */
- static class PackageName {
- private static final Pattern ARCHITECTURE_PATTERN = Pattern.compile("\\.(noarch|x86_64|i686|i386|\\*)$");
- private static final Pattern NAME_VER_REL_PATTERN = Pattern.compile("^(.+)-([^-]*[0-9][^-]*)-([^-]*[0-9][^-]*)$");
-
- public final Optional<String> epoch;
- public final String name;
- public final Optional<String> version;
- public final Optional<String> release;
- public final Optional<String> architecture;
-
- private PackageName(Optional<String> epoch,
- String name,
- Optional<String> version,
- Optional<String> release,
- Optional<String> architecture) {
- this.epoch = epoch;
- this.name = name;
- this.version = version;
- this.release = release;
- this.architecture = architecture;
- }
-
- /**
- * Parse the string specification of a YUM package.
- *
- * <p>According to yum(8) a package can be specified using a variety of different
- * and ambiguous formats. We'll use a subset:
- *
- * <ul>
- * <li>spec MUST be of the form name-ver-rel, name-ver-rel.arch, or epoch:name-ver-rel.arch.
- * <li>If specified, arch MUST be one of "noarch", "i686", "x86_64", or "*". The wildcard
- * is equivalent to not specifying arch.
- * <li>rel cannot end in something that would be mistaken for the '.arch' suffix.
- * <li>ver and rel are assumed to not contain any '-' to uniquely identify name,
- * and must contain a digit.
- * </ul>
- *
- * @param spec A package name of the form epoch:name-ver-rel.arch, name-ver-rel.arch, or name-ver-rel.
- * @return The package with that name.
- * @throws IllegalArgumentException if spec does not specify a package name.
- */
- public static PackageName fromString(String spec) {
- return parseString(spec).orElseThrow(() -> new IllegalArgumentException("Failed to decode the YUM package spec '" + spec + "'"));
- }
-
- /** See {@link #fromString(String)}. */
- public static Optional<PackageName> parseString(String spec) {
- Optional<String> epoch = Optional.empty();
- int epochColon = spec.indexOf(':');
- if (epochColon >= 0) {
- epoch = Optional.of(spec.substring(0, epochColon));
- spec = spec.substring(epochColon + 1);
- }
-
- Optional<String> architecture = Optional.empty();
- Matcher architectureMatcher = ARCHITECTURE_PATTERN.matcher(spec);
- if (architectureMatcher.find()) {
- architecture = Optional.of(architectureMatcher.group(1));
- spec = spec.substring(0, architectureMatcher.start());
- }
-
-
- Matcher matcher = NAME_VER_REL_PATTERN.matcher(spec);
- if (matcher.find()) {
- return Optional.of(new PackageName(
- epoch,
- matcher.group(1),
- Optional.of(matcher.group(2)),
- Optional.of(matcher.group(3)),
- architecture));
- }
-
- return Optional.empty();
- }
-
- public Optional<String> getEpoch() { return epoch; }
- public String getName() { return name; }
- public Optional<String> getVersion() { return version; }
- public Optional<String> getRelease() { return release; }
- public Optional<String> getArchitecture() { return architecture; }
-
- /**
- * Return the full name of the package in the format epoch:name-ver-rel.arch, which can
- * be used with e.g. the YUM install and versionlock commands.
- *
- * <p>The package MUST have both version and release. Absent epoch defaults to "0".
- * Absent arch defaults to "*".
- */
- public String toFullName() {
- return String.format("%s:%s-%s-%s.%s",
- epoch.orElse("0"),
- name,
- version.orElseThrow(() -> new IllegalStateException("Version is missing for YUM package " + name)),
- release.orElseThrow(() -> new IllegalStateException("Release is missing for YUM package " + name)),
- architecture.orElse("*"));
- }
-
- public static class Builder {
- private Optional<String> epoch;
- private String name;
- private Optional<String> version;
- private Optional<String> release;
- private Optional<String> architecture;
-
- public Builder(PackageName aPackage) {
- epoch = aPackage.epoch;
- name = aPackage.name;
- version = aPackage.version;
- release = aPackage.release;
- architecture = aPackage.architecture;
- }
-
- public Builder setEpoch(String epoch) { this.epoch = Optional.of(epoch); return this; }
- public Builder setName(String name) { this.name = name; return this; }
- public Builder setRelease(String version) { this.version = Optional.of(version); return this; }
- public Builder setVersion(String release) { this.release = Optional.of(release); return this; }
- public Builder setArchitecture(String architecture) { this.architecture = Optional.of(architecture); return this; }
-
- public PackageName build() { return new PackageName(epoch, name, version, release, architecture); }
- }
- }
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumPackageName.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumPackageName.java
new file mode 100644
index 00000000000..d6565a67979
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumPackageName.java
@@ -0,0 +1,148 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.task.util.yum;
+
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * YUM package name.
+ *
+ * @author hakonhall
+ */
+public class YumPackageName {
+ private static final Pattern ARCHITECTURE_PATTERN = Pattern.compile("\\.(noarch|x86_64|i686|i386|\\*)$");
+ private static final Pattern NAME_VER_REL_PATTERN = Pattern.compile("^(.+)-([^-]*[0-9][^-]*)-([^-]*[0-9][^-]*)$");
+
+ public final Optional<String> epoch;
+ public final String name;
+ public final Optional<String> version;
+ public final Optional<String> release;
+ public final Optional<String> architecture;
+
+ /** @see Builder */
+ private YumPackageName(Optional<String> epoch,
+ String name,
+ Optional<String> version,
+ Optional<String> release,
+ Optional<String> architecture) {
+ this.epoch = epoch;
+ this.name = name;
+ this.version = version;
+ this.release = release;
+ this.architecture = architecture;
+ }
+
+ /**
+ * Parse the string specification of a YUM package.
+ *
+ * <p>According to yum(8) a package can be specified using a variety of different
+ * and ambiguous formats. We'll use a subset:
+ *
+ * <ul>
+ * <li>spec MUST be of the form name-ver-rel, name-ver-rel.arch, or epoch:name-ver-rel.arch.
+ * <li>If specified, arch should be one of "noarch", "i686", "x86_64", or "*". The wildcard
+ * is equivalent to not specifying arch.
+ * <li>rel cannot end in something that would be mistaken for the '.arch' suffix.
+ * <li>ver and rel are assumed to not contain any '-' to uniquely identify name,
+ * and must contain a digit.
+ * </ul>
+ *
+ * @param spec A package name of the form epoch:name-ver-rel.arch, name-ver-rel.arch, or name-ver-rel.
+ * @return The package with that name.
+ * @throws IllegalArgumentException if spec does not specify a package name.
+ */
+ public static YumPackageName fromString(String spec) {
+ return parseString(spec).orElseThrow(() -> new IllegalArgumentException("Failed to decode the YUM package spec '" + spec + "'"));
+ }
+
+ /** See {@link #fromString(String)}. */
+ public static Optional<YumPackageName> parseString(String spec) {
+ Optional<String> epoch = Optional.empty();
+ int epochColon = spec.indexOf(':');
+ if (epochColon >= 0) {
+ epoch = Optional.of(spec.substring(0, epochColon));
+ if (!epoch.get().chars().allMatch(Character::isDigit)) {
+ throw new IllegalArgumentException("Epoch is not a number: " + epoch.get());
+ }
+
+ spec = spec.substring(epochColon + 1);
+ }
+
+ Optional<String> architecture = Optional.empty();
+ Matcher architectureMatcher = ARCHITECTURE_PATTERN.matcher(spec);
+ if (architectureMatcher.find()) {
+ architecture = Optional.of(architectureMatcher.group(1));
+ spec = spec.substring(0, architectureMatcher.start());
+ }
+
+
+ Matcher matcher = NAME_VER_REL_PATTERN.matcher(spec);
+ if (matcher.find()) {
+ return Optional.of(new YumPackageName(
+ epoch,
+ matcher.group(1),
+ Optional.of(matcher.group(2)),
+ Optional.of(matcher.group(3)),
+ architecture));
+ }
+
+ return Optional.empty();
+ }
+
+ public Optional<String> getEpoch() { return epoch; }
+ public String getName() { return name; }
+ public Optional<String> getVersion() { return version; }
+ public Optional<String> getRelease() { return release; }
+ public Optional<String> getArchitecture() { return architecture; }
+
+ /**
+ * Return the full name of the package in the format epoch:name-ver-rel.arch, which can
+ * be used with e.g. the YUM install and versionlock commands.
+ *
+ * <p>The package MUST have both version and release. Absent epoch defaults to "0".
+ * Absent arch defaults to "*".
+ */
+ public String toFullName() {
+ return String.format("%s:%s-%s-%s.%s",
+ epoch.orElse("0"),
+ name,
+ version.orElseThrow(() -> new IllegalStateException("Version is missing for YUM package " + name)),
+ release.orElseThrow(() -> new IllegalStateException("Release is missing for YUM package " + name)),
+ architecture.orElse("*"));
+ }
+
+ /** The package name output by 'yum versionlock list'. Can also be used with 'add' and 'delete'. */
+ public String toVersionLock() {
+ return String.format("%s:%s-%s-%s.%s",
+ epoch.orElse("0"),
+ name,
+ version.orElseThrow(() -> new IllegalStateException("Version is missing for YUM package " + name)),
+ release.orElseThrow(() -> new IllegalStateException("Release is missing for YUM package " + name)),
+ "*");
+ }
+
+ public static class Builder {
+ private Optional<String> epoch;
+ private String name;
+ private Optional<String> version;
+ private Optional<String> release;
+ private Optional<String> architecture;
+
+ public Builder(YumPackageName aPackage) {
+ epoch = aPackage.epoch;
+ name = aPackage.name;
+ version = aPackage.version;
+ release = aPackage.release;
+ architecture = aPackage.architecture;
+ }
+
+ public Builder setEpoch(String epoch) { this.epoch = Optional.of(epoch); return this; }
+ public Builder setName(String name) { this.name = name; return this; }
+ public Builder setRelease(String version) { this.version = Optional.of(version); return this; }
+ public Builder setVersion(String release) { this.release = Optional.of(release); return this; }
+ public Builder setArchitecture(String architecture) { this.architecture = Optional.of(architecture); return this; }
+
+ public YumPackageName build() { return new YumPackageName(epoch, name, version, release, architecture); }
+ }
+}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumPackageNameTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumPackageNameTest.java
new file mode 100644
index 00000000000..911a5b64b33
--- /dev/null
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumPackageNameTest.java
@@ -0,0 +1,62 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.task.util.yum;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+public class YumPackageNameTest {
+
+ @Test
+ public void parsePackageName() {
+ YumPackageName packageName = YumPackageName.fromString("docker-engine-selinux-1.12.6-1.el7");
+ assertFalse(packageName.getEpoch().isPresent());
+ assertEquals("docker-engine-selinux", packageName.getName());
+ assertEquals("1.12.6", packageName.getVersion().get());
+ assertEquals("1.el7", packageName.getRelease().get());
+ assertFalse(packageName.getArchitecture().isPresent());
+ assertEquals("0:docker-engine-selinux-1.12.6-1.el7.*", packageName.toFullName());
+ }
+
+ @Test
+ public void parsePackageNameWithArchitecture() {
+ YumPackageName packageName = YumPackageName.fromString("docker-engine-selinux-1.12.6-1.el7.x86_64");
+ assertFalse(packageName.getEpoch().isPresent());
+ assertEquals("docker-engine-selinux", packageName.getName());
+ assertEquals("1.12.6", packageName.getVersion().get());
+ assertEquals("1.el7", packageName.getRelease().get());
+ assertEquals("x86_64", packageName.getArchitecture().get());
+ assertEquals("0:docker-engine-selinux-1.12.6-1.el7.x86_64", packageName.toFullName());
+ assertEquals("0:docker-engine-selinux-1.12.6-1.el7.*", packageName.toVersionLock());
+ }
+
+ @Test
+ public void parsePackageNameWithEpochAndArchitecture() {
+ YumPackageName packageName = YumPackageName.fromString("1:docker-engine-selinux-1.12.6-1.el7.x86_64");
+ assertEquals("1", packageName.getEpoch().get());
+ assertEquals("docker-engine-selinux", packageName.getName());
+ assertEquals("1.12.6", packageName.getVersion().get());
+ assertEquals("1.el7", packageName.getRelease().get());
+ assertEquals("x86_64", packageName.getArchitecture().get());
+ assertEquals("1:docker-engine-selinux-1.12.6-1.el7.x86_64", packageName.toFullName());
+ assertEquals("1:docker-engine-selinux-1.12.6-1.el7.*", packageName.toVersionLock());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void failParsingOfPackageName() {
+ YumPackageName.fromString("docker-engine-selinux");
+ }
+
+ @Test
+ public void unrecognizedArchitectureGetsGobbledUp() {
+ YumPackageName packageName = YumPackageName.fromString("docker-engine-selinux-1.12.6-1.el7.i486");
+ // This is not a great feature - please use YumPackageName.Builder instead.
+ assertEquals("1.el7.i486", packageName.getRelease().get());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void failParsingOfPackageNameWithEpochAndArchitecture() {
+ YumPackageName.fromString("epoch:docker-engine-selinux-1.12.6-1.el7.x86_64");
+ }
+} \ No newline at end of file
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumTest.java
index 896914b7967..f2a2306263a 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/yum/YumTest.java
@@ -191,18 +191,4 @@ public class YumTest {
yum.upgrade().converge(taskContext);
}
-
- @Test
- public void parsePackageName() {
- Yum.PackageName packageName = Yum.PackageName.fromString("docker-engine-selinux-1.12.6-1.el7");
- assertEquals("docker-engine-selinux", packageName.getName());
- assertEquals("1.12.6", packageName.getVersion().get());
- assertEquals("1.el7", packageName.getRelease().get());
- assertEquals("0:docker-engine-selinux-1.12.6-1.el7.*", packageName.toFullName());
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void failParsingOfPackageName() {
- Yum.PackageName.fromString("docker-engine-selinux");
- }
} \ No newline at end of file