Signed-off-by: Daniel Nephin <dnephin@docker.com>
| ... | ... |
@@ -12,7 +12,6 @@ import ( |
| 12 | 12 |
flag "github.com/docker/docker/pkg/mflag" |
| 13 | 13 |
"github.com/docker/docker/pkg/mount" |
| 14 | 14 |
"github.com/docker/docker/pkg/signal" |
| 15 |
- "github.com/docker/docker/volume" |
|
| 16 | 15 |
"github.com/docker/go-connections/nat" |
| 17 | 16 |
"github.com/docker/go-units" |
| 18 | 17 |
) |
| ... | ... |
@@ -199,7 +198,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 199 | 199 |
var binds []string |
| 200 | 200 |
// add any bind targets to the list of container volumes |
| 201 | 201 |
for bind := range flVolumes.GetMap() {
|
| 202 |
- if arr := volume.SplitN(bind, 2); len(arr) > 1 {
|
|
| 202 |
+ if arr := volumeSplitN(bind, 2); len(arr) > 1 {
|
|
| 203 | 203 |
// after creating the bind mount we want to delete it from the flVolumes values because |
| 204 | 204 |
// we do not want bind mounts being committed to image configs |
| 205 | 205 |
binds = append(binds, bind) |
| ... | ... |
@@ -621,3 +620,59 @@ func validatePath(val string, validator func(string) bool) (string, error) {
|
| 621 | 621 |
} |
| 622 | 622 |
return val, nil |
| 623 | 623 |
} |
| 624 |
+ |
|
| 625 |
+// SplitN splits raw into a maximum of n parts, separated by a separator colon. |
|
| 626 |
+// A separator colon is the last `:` character in the regex `[/:\\]?[a-zA-Z]:` (note `\\` is `\` escaped). |
|
| 627 |
+// This allows to correctly split strings such as `C:\foo:D:\:rw`. |
|
| 628 |
+func volumeSplitN(raw string, n int) []string {
|
|
| 629 |
+ var array []string |
|
| 630 |
+ if len(raw) == 0 || raw[0] == ':' {
|
|
| 631 |
+ // invalid |
|
| 632 |
+ return nil |
|
| 633 |
+ } |
|
| 634 |
+ // numberOfParts counts the number of parts separated by a separator colon |
|
| 635 |
+ numberOfParts := 0 |
|
| 636 |
+ // left represents the left-most cursor in raw, updated at every `:` character considered as a separator. |
|
| 637 |
+ left := 0 |
|
| 638 |
+ // right represents the right-most cursor in raw incremented with the loop. Note this |
|
| 639 |
+ // starts at index 1 as index 0 is already handle above as a special case. |
|
| 640 |
+ for right := 1; right < len(raw); right++ {
|
|
| 641 |
+ // stop parsing if reached maximum number of parts |
|
| 642 |
+ if n >= 0 && numberOfParts >= n {
|
|
| 643 |
+ break |
|
| 644 |
+ } |
|
| 645 |
+ if raw[right] != ':' {
|
|
| 646 |
+ continue |
|
| 647 |
+ } |
|
| 648 |
+ potentialDriveLetter := raw[right-1] |
|
| 649 |
+ if (potentialDriveLetter >= 'A' && potentialDriveLetter <= 'Z') || (potentialDriveLetter >= 'a' && potentialDriveLetter <= 'z') {
|
|
| 650 |
+ if right > 1 {
|
|
| 651 |
+ beforePotentialDriveLetter := raw[right-2] |
|
| 652 |
+ if beforePotentialDriveLetter != ':' && beforePotentialDriveLetter != '/' && beforePotentialDriveLetter != '\\' {
|
|
| 653 |
+ // e.g. `C:` is not preceded by any delimiter, therefore it was not a drive letter but a path ending with `C:`. |
|
| 654 |
+ array = append(array, raw[left:right]) |
|
| 655 |
+ left = right + 1 |
|
| 656 |
+ numberOfParts++ |
|
| 657 |
+ } |
|
| 658 |
+ // else, `C:` is considered as a drive letter and not as a delimiter, so we continue parsing. |
|
| 659 |
+ } |
|
| 660 |
+ // if right == 1, then `C:` is the beginning of the raw string, therefore `:` is again not considered a delimiter and we continue parsing. |
|
| 661 |
+ } else {
|
|
| 662 |
+ // if `:` is not preceded by a potential drive letter, then consider it as a delimiter. |
|
| 663 |
+ array = append(array, raw[left:right]) |
|
| 664 |
+ left = right + 1 |
|
| 665 |
+ numberOfParts++ |
|
| 666 |
+ } |
|
| 667 |
+ } |
|
| 668 |
+ // need to take care of the last part |
|
| 669 |
+ if left < len(raw) {
|
|
| 670 |
+ if n >= 0 && numberOfParts >= n {
|
|
| 671 |
+ // if the maximum number of parts is reached, just append the rest to the last part |
|
| 672 |
+ // left-1 is at the last `:` that needs to be included since not considered a separator. |
|
| 673 |
+ array[n-1] += raw[left-1:] |
|
| 674 |
+ } else {
|
|
| 675 |
+ array = append(array, raw[left:]) |
|
| 676 |
+ } |
|
| 677 |
+ } |
|
| 678 |
+ return array |
|
| 679 |
+} |
| ... | ... |
@@ -763,3 +763,52 @@ func TestValidateDevice(t *testing.T) {
|
| 763 | 763 |
} |
| 764 | 764 |
} |
| 765 | 765 |
} |
| 766 |
+ |
|
| 767 |
+func TestVolumeSplitN(t *testing.T) {
|
|
| 768 |
+ for _, x := range []struct {
|
|
| 769 |
+ input string |
|
| 770 |
+ n int |
|
| 771 |
+ expected []string |
|
| 772 |
+ }{
|
|
| 773 |
+ {`C:\foo:d:`, -1, []string{`C:\foo`, `d:`}},
|
|
| 774 |
+ {`:C:\foo:d:`, -1, nil},
|
|
| 775 |
+ {`/foo:/bar:ro`, 3, []string{`/foo`, `/bar`, `ro`}},
|
|
| 776 |
+ {`/foo:/bar:ro`, 2, []string{`/foo`, `/bar:ro`}},
|
|
| 777 |
+ {`C:\foo\:/foo`, -1, []string{`C:\foo\`, `/foo`}},
|
|
| 778 |
+ |
|
| 779 |
+ {`d:\`, -1, []string{`d:\`}},
|
|
| 780 |
+ {`d:`, -1, []string{`d:`}},
|
|
| 781 |
+ {`d:\path`, -1, []string{`d:\path`}},
|
|
| 782 |
+ {`d:\path with space`, -1, []string{`d:\path with space`}},
|
|
| 783 |
+ {`d:\pathandmode:rw`, -1, []string{`d:\pathandmode`, `rw`}},
|
|
| 784 |
+ {`c:\:d:\`, -1, []string{`c:\`, `d:\`}},
|
|
| 785 |
+ {`c:\windows\:d:`, -1, []string{`c:\windows\`, `d:`}},
|
|
| 786 |
+ {`c:\windows:d:\s p a c e`, -1, []string{`c:\windows`, `d:\s p a c e`}},
|
|
| 787 |
+ {`c:\windows:d:\s p a c e:RW`, -1, []string{`c:\windows`, `d:\s p a c e`, `RW`}},
|
|
| 788 |
+ {`c:\program files:d:\s p a c e i n h o s t d i r`, -1, []string{`c:\program files`, `d:\s p a c e i n h o s t d i r`}},
|
|
| 789 |
+ {`0123456789name:d:`, -1, []string{`0123456789name`, `d:`}},
|
|
| 790 |
+ {`MiXeDcAsEnAmE:d:`, -1, []string{`MiXeDcAsEnAmE`, `d:`}},
|
|
| 791 |
+ {`name:D:`, -1, []string{`name`, `D:`}},
|
|
| 792 |
+ {`name:D::rW`, -1, []string{`name`, `D:`, `rW`}},
|
|
| 793 |
+ {`name:D::RW`, -1, []string{`name`, `D:`, `RW`}},
|
|
| 794 |
+ {`c:/:d:/forward/slashes/are/good/too`, -1, []string{`c:/`, `d:/forward/slashes/are/good/too`}},
|
|
| 795 |
+ {`c:\Windows`, -1, []string{`c:\Windows`}},
|
|
| 796 |
+ {`c:\Program Files (x86)`, -1, []string{`c:\Program Files (x86)`}},
|
|
| 797 |
+ |
|
| 798 |
+ {``, -1, nil},
|
|
| 799 |
+ {`.`, -1, []string{`.`}},
|
|
| 800 |
+ {`..\`, -1, []string{`..\`}},
|
|
| 801 |
+ {`c:\:..\`, -1, []string{`c:\`, `..\`}},
|
|
| 802 |
+ {`c:\:d:\:xyzzy`, -1, []string{`c:\`, `d:\`, `xyzzy`}},
|
|
| 803 |
+ } {
|
|
| 804 |
+ res := volumeSplitN(x.input, x.n) |
|
| 805 |
+ if len(res) < len(x.expected) {
|
|
| 806 |
+ t.Fatalf("input: %v, expected: %v, got: %v", x.input, x.expected, res)
|
|
| 807 |
+ } |
|
| 808 |
+ for i, e := range res {
|
|
| 809 |
+ if e != x.expected[i] {
|
|
| 810 |
+ t.Fatalf("input: %v, expected: %v, got: %v", x.input, x.expected, res)
|
|
| 811 |
+ } |
|
| 812 |
+ } |
|
| 813 |
+ } |
|
| 814 |
+} |
| ... | ... |
@@ -113,59 +113,3 @@ func ParseVolumesFrom(spec string) (string, string, error) {
|
| 113 | 113 |
} |
| 114 | 114 |
return id, mode, nil |
| 115 | 115 |
} |
| 116 |
- |
|
| 117 |
-// SplitN splits raw into a maximum of n parts, separated by a separator colon. |
|
| 118 |
-// A separator colon is the last `:` character in the regex `[/:\\]?[a-zA-Z]:` (note `\\` is `\` escaped). |
|
| 119 |
-// This allows to correctly split strings such as `C:\foo:D:\:rw`. |
|
| 120 |
-func SplitN(raw string, n int) []string {
|
|
| 121 |
- var array []string |
|
| 122 |
- if len(raw) == 0 || raw[0] == ':' {
|
|
| 123 |
- // invalid |
|
| 124 |
- return nil |
|
| 125 |
- } |
|
| 126 |
- // numberOfParts counts the number of parts separated by a separator colon |
|
| 127 |
- numberOfParts := 0 |
|
| 128 |
- // left represents the left-most cursor in raw, updated at every `:` character considered as a separator. |
|
| 129 |
- left := 0 |
|
| 130 |
- // right represents the right-most cursor in raw incremented with the loop. Note this |
|
| 131 |
- // starts at index 1 as index 0 is already handle above as a special case. |
|
| 132 |
- for right := 1; right < len(raw); right++ {
|
|
| 133 |
- // stop parsing if reached maximum number of parts |
|
| 134 |
- if n >= 0 && numberOfParts >= n {
|
|
| 135 |
- break |
|
| 136 |
- } |
|
| 137 |
- if raw[right] != ':' {
|
|
| 138 |
- continue |
|
| 139 |
- } |
|
| 140 |
- potentialDriveLetter := raw[right-1] |
|
| 141 |
- if (potentialDriveLetter >= 'A' && potentialDriveLetter <= 'Z') || (potentialDriveLetter >= 'a' && potentialDriveLetter <= 'z') {
|
|
| 142 |
- if right > 1 {
|
|
| 143 |
- beforePotentialDriveLetter := raw[right-2] |
|
| 144 |
- if beforePotentialDriveLetter != ':' && beforePotentialDriveLetter != '/' && beforePotentialDriveLetter != '\\' {
|
|
| 145 |
- // e.g. `C:` is not preceded by any delimiter, therefore it was not a drive letter but a path ending with `C:`. |
|
| 146 |
- array = append(array, raw[left:right]) |
|
| 147 |
- left = right + 1 |
|
| 148 |
- numberOfParts++ |
|
| 149 |
- } |
|
| 150 |
- // else, `C:` is considered as a drive letter and not as a delimiter, so we continue parsing. |
|
| 151 |
- } |
|
| 152 |
- // if right == 1, then `C:` is the beginning of the raw string, therefore `:` is again not considered a delimiter and we continue parsing. |
|
| 153 |
- } else {
|
|
| 154 |
- // if `:` is not preceded by a potential drive letter, then consider it as a delimiter. |
|
| 155 |
- array = append(array, raw[left:right]) |
|
| 156 |
- left = right + 1 |
|
| 157 |
- numberOfParts++ |
|
| 158 |
- } |
|
| 159 |
- } |
|
| 160 |
- // need to take care of the last part |
|
| 161 |
- if left < len(raw) {
|
|
| 162 |
- if n >= 0 && numberOfParts >= n {
|
|
| 163 |
- // if the maximum number of parts is reached, just append the rest to the last part |
|
| 164 |
- // left-1 is at the last `:` that needs to be included since not considered a separator. |
|
| 165 |
- array[n-1] += raw[left-1:] |
|
| 166 |
- } else {
|
|
| 167 |
- array = append(array, raw[left:]) |
|
| 168 |
- } |
|
| 169 |
- } |
|
| 170 |
- return array |
|
| 171 |
-} |
| ... | ... |
@@ -133,55 +133,6 @@ func TestParseMountSpec(t *testing.T) {
|
| 133 | 133 |
} |
| 134 | 134 |
} |
| 135 | 135 |
|
| 136 |
-func TestSplitN(t *testing.T) {
|
|
| 137 |
- for _, x := range []struct {
|
|
| 138 |
- input string |
|
| 139 |
- n int |
|
| 140 |
- expected []string |
|
| 141 |
- }{
|
|
| 142 |
- {`C:\foo:d:`, -1, []string{`C:\foo`, `d:`}},
|
|
| 143 |
- {`:C:\foo:d:`, -1, nil},
|
|
| 144 |
- {`/foo:/bar:ro`, 3, []string{`/foo`, `/bar`, `ro`}},
|
|
| 145 |
- {`/foo:/bar:ro`, 2, []string{`/foo`, `/bar:ro`}},
|
|
| 146 |
- {`C:\foo\:/foo`, -1, []string{`C:\foo\`, `/foo`}},
|
|
| 147 |
- |
|
| 148 |
- {`d:\`, -1, []string{`d:\`}},
|
|
| 149 |
- {`d:`, -1, []string{`d:`}},
|
|
| 150 |
- {`d:\path`, -1, []string{`d:\path`}},
|
|
| 151 |
- {`d:\path with space`, -1, []string{`d:\path with space`}},
|
|
| 152 |
- {`d:\pathandmode:rw`, -1, []string{`d:\pathandmode`, `rw`}},
|
|
| 153 |
- {`c:\:d:\`, -1, []string{`c:\`, `d:\`}},
|
|
| 154 |
- {`c:\windows\:d:`, -1, []string{`c:\windows\`, `d:`}},
|
|
| 155 |
- {`c:\windows:d:\s p a c e`, -1, []string{`c:\windows`, `d:\s p a c e`}},
|
|
| 156 |
- {`c:\windows:d:\s p a c e:RW`, -1, []string{`c:\windows`, `d:\s p a c e`, `RW`}},
|
|
| 157 |
- {`c:\program files:d:\s p a c e i n h o s t d i r`, -1, []string{`c:\program files`, `d:\s p a c e i n h o s t d i r`}},
|
|
| 158 |
- {`0123456789name:d:`, -1, []string{`0123456789name`, `d:`}},
|
|
| 159 |
- {`MiXeDcAsEnAmE:d:`, -1, []string{`MiXeDcAsEnAmE`, `d:`}},
|
|
| 160 |
- {`name:D:`, -1, []string{`name`, `D:`}},
|
|
| 161 |
- {`name:D::rW`, -1, []string{`name`, `D:`, `rW`}},
|
|
| 162 |
- {`name:D::RW`, -1, []string{`name`, `D:`, `RW`}},
|
|
| 163 |
- {`c:/:d:/forward/slashes/are/good/too`, -1, []string{`c:/`, `d:/forward/slashes/are/good/too`}},
|
|
| 164 |
- {`c:\Windows`, -1, []string{`c:\Windows`}},
|
|
| 165 |
- {`c:\Program Files (x86)`, -1, []string{`c:\Program Files (x86)`}},
|
|
| 166 |
- |
|
| 167 |
- {``, -1, nil},
|
|
| 168 |
- {`.`, -1, []string{`.`}},
|
|
| 169 |
- {`..\`, -1, []string{`..\`}},
|
|
| 170 |
- {`c:\:..\`, -1, []string{`c:\`, `..\`}},
|
|
| 171 |
- {`c:\:d:\:xyzzy`, -1, []string{`c:\`, `d:\`, `xyzzy`}},
|
|
| 172 |
- } {
|
|
| 173 |
- res := SplitN(x.input, x.n) |
|
| 174 |
- if len(res) < len(x.expected) {
|
|
| 175 |
- t.Fatalf("input: %v, expected: %v, got: %v", x.input, x.expected, res)
|
|
| 176 |
- } |
|
| 177 |
- for i, e := range res {
|
|
| 178 |
- if e != x.expected[i] {
|
|
| 179 |
- t.Fatalf("input: %v, expected: %v, got: %v", x.input, x.expected, res)
|
|
| 180 |
- } |
|
| 181 |
- } |
|
| 182 |
- } |
|
| 183 |
-} |
|
| 184 |
- |
|
| 185 | 136 |
// testParseMountSpec is a structure used by TestParseMountSpecSplit for |
| 186 | 137 |
// specifying test cases for the ParseMountSpec() function. |
| 187 | 138 |
type testParseMountSpec struct {
|