-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Implemented Device parser #593
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,8 @@ | ||
| package com.github.dockerjava.api.model; | ||
|
|
||
| import static com.google.common.base.Preconditions.checkNotNull; | ||
| import static org.apache.commons.lang.BooleanUtils.isNotTrue; | ||
| import static org.apache.commons.lang.StringUtils.isEmpty; | ||
|
|
||
| import org.apache.commons.lang.builder.EqualsBuilder; | ||
| import org.apache.commons.lang.builder.HashCodeBuilder; | ||
|
|
@@ -9,6 +11,11 @@ | |
| import com.fasterxml.jackson.annotation.JsonInclude.Include; | ||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||
|
|
||
| import javax.annotation.Nonnull; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import java.util.StringTokenizer; | ||
|
|
||
| @JsonInclude(Include.NON_NULL) | ||
| public class Device { | ||
|
|
||
|
|
@@ -45,6 +52,75 @@ public String getPathOnHost() { | |
| return pathOnHost; | ||
| } | ||
|
|
||
| /** | ||
| * @link https://github.com/docker/docker/blob/6b4a46f28266031ce1a1315f17fb69113a06efe1/runconfig/opts/parse_test.go#L468 | ||
| */ | ||
| @Nonnull | ||
| public static Device parse(@Nonnull String deviceStr) { | ||
| String src = ""; | ||
| String dst = ""; | ||
| String permissions = "rwm"; | ||
| final String[] arr = deviceStr.trim().split(":"); | ||
| // java String.split() returns wrong length, use tokenizer instead | ||
| switch (new StringTokenizer(deviceStr, ":").countTokens()) { | ||
| case 3: { | ||
| // Mismatches docker code logic. While there is no validations after parsing, checking heregit | ||
| if (validDeviceMode(arr[2])) { | ||
| permissions = arr[2]; | ||
| } else { | ||
| throw new IllegalArgumentException("Invalid device specification: " + deviceStr); | ||
| } | ||
| } | ||
| case 2: { | ||
| if (validDeviceMode(arr[1])) { | ||
| permissions = arr[1]; | ||
| } else { | ||
| dst = arr[1]; | ||
| } | ||
| } | ||
| case 1: { | ||
| src = arr[0]; | ||
| break; | ||
| } | ||
| default: { | ||
| throw new IllegalArgumentException("Invalid device specification: " + deviceStr); | ||
| } | ||
| } | ||
|
|
||
| if (isEmpty(dst)) { | ||
| dst = src; | ||
| } | ||
|
|
||
| return new Device(permissions, dst, src); | ||
| } | ||
|
|
||
| /** | ||
| * ValidDeviceMode checks if the mode for device is valid or not. | ||
| * Valid mode is a composition of r (read), w (write), and m (mknod). | ||
| * | ||
| * @link https://github.com/docker/docker/blob/6b4a46f28266031ce1a1315f17fb69113a06efe1/runconfig/opts/parse.go#L796 | ||
| */ | ||
| private static boolean validDeviceMode(String deviceMode) { | ||
| Map<String, Boolean> validModes = new HashMap<>(3); | ||
| validModes.put("r", true); | ||
| validModes.put("w", true); | ||
| validModes.put("m", true); | ||
|
|
||
| if (isEmpty(deviceMode)) { | ||
| return false; | ||
| } | ||
|
|
||
| for (char ch : deviceMode.toCharArray()) { | ||
| final String mode = String.valueOf(ch); | ||
| if (isNotTrue(validModes.get(mode))) { | ||
| return false; // wrong mode | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if char repeats -> wrong mode |
||
| } | ||
| validModes.put(mode, false); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is it for? Some kind of optimization?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. error duplicated modes i.e.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK. Now I got it. So it must be declared within the method then. Sorry for bothering.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah it tricky logic :) |
||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object obj) { | ||
| if (obj instanceof Device) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| package com.github.dockerjava.api.model; | ||
|
|
||
| import org.testng.annotations.Test; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.Collections; | ||
| import java.util.HashMap; | ||
| import java.util.LinkedHashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| import static junit.framework.Assert.fail; | ||
| import static org.hamcrest.MatcherAssert.assertThat; | ||
| import static org.hamcrest.Matchers.contains; | ||
| import static org.hamcrest.Matchers.containsString; | ||
| import static org.hamcrest.Matchers.equalTo; | ||
| import static org.hamcrest.Matchers.is; | ||
|
|
||
| /** | ||
| * @author Kanstantsin Shautsou | ||
| */ | ||
| public class DeviceTest { | ||
|
|
||
| public static List<String> validPaths = Arrays.asList( | ||
| "/home", | ||
| "/home:/home", | ||
| "/home:/something/else", | ||
| "/with space", | ||
| "/home:/with space", | ||
| "relative:/absolute-path", | ||
| "hostPath:/containerPath:r", | ||
| "/hostPath:/containerPath:rw", | ||
| "/hostPath:/containerPath:mrw" | ||
| ); | ||
|
|
||
| public static HashMap<String, String> badPaths = new LinkedHashMap<String, String>() {{ | ||
| put("", "bad format for path: "); | ||
| // TODO implement ValidatePath | ||
| // put("./", "./ is not an absolute path"); | ||
| // put("../", "../ is not an absolute path"); | ||
| // put("/:../", "../ is not an absolute path"); | ||
| // put("/:path", "path is not an absolute path"); | ||
| // put(":", "bad format for path: :"); | ||
| // put("/tmp:", " is not an absolute path"); | ||
| // put(":test", "bad format for path: :test"); | ||
| // put(":/test", "bad format for path: :/test"); | ||
| // put("tmp:", " is not an absolute path"); | ||
| // put(":test:", "bad format for path: :test:"); | ||
| // put("::", "bad format for path: ::"); | ||
| // put(":::", "bad format for path: :::"); | ||
| // put("/tmp:::", "bad format for path: /tmp:::"); | ||
| // put(":/tmp::", "bad format for path: :/tmp::"); | ||
| // put("path:ro", "ro is not an absolute path"); | ||
| // put("path:rr", "rr is not an absolute path"); | ||
| put("a:/b:ro", "bad mode specified: ro"); | ||
| put("a:/b:rr", "bad mode specified: rr"); | ||
| }}; | ||
|
|
||
| @Test | ||
| public void testParse() throws Exception { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test is missing the invalid mode case. |
||
| assertThat(Device.parse("/dev/sda:/dev/xvdc:r"), | ||
| equalTo(new Device("r", "/dev/xvdc", "/dev/sda"))); | ||
|
|
||
| assertThat(Device.parse("/dev/snd:rw"), | ||
| equalTo(new Device("rw", "/dev/snd", "/dev/snd"))); | ||
|
|
||
| assertThat(Device.parse("/dev/snd:/something"), | ||
| equalTo(new Device("rwm", "/something", "/dev/snd"))); | ||
|
|
||
| assertThat(Device.parse("/dev/snd:/something:rw"), | ||
| equalTo(new Device("rw", "/something", "/dev/snd"))); | ||
|
|
||
| } | ||
|
|
||
| @Test | ||
| public void testParseBadPaths() { | ||
| for (Map.Entry<String, String> entry : badPaths.entrySet()) { | ||
| final String deviceStr = entry.getKey(); | ||
| try { | ||
| Device.parse(deviceStr); | ||
| fail("Should fail because: " + entry.getValue() + " '" + deviceStr + "'"); | ||
| } catch (IllegalArgumentException ex) { | ||
| assertThat(ex.getMessage(), containsString("Invalid device specification:")); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| public void testParseValidPaths() { | ||
| for (String path : validPaths) { | ||
| Device.parse(path); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The `validModes' map should be declared as static member and not declared inline.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you mean under
not declared inline.?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean it should be declared at the class level not in the method itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It must be mutable, it changes true to false to exclude double letters.