Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
2863fa9f
Commit
2863fa9f
authored
Jan 22, 2015
by
Vish Kannan
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #3726 from mrunalp/update/go-dockerclient
Update github.com/fsouza/go-dockerclient to pick up IpcMode support.
parents
5c3fd0c3
683fd13c
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
509 additions
and
66 deletions
+509
-66
Godeps.json
Godeps/Godeps.json
+9
-10
archive.go
...space/src/github.com/docker/docker/pkg/archive/archive.go
+32
-16
archive_test.go
.../src/github.com/docker/docker/pkg/archive/archive_test.go
+4
-4
changes_test.go
.../src/github.com/docker/docker/pkg/archive/changes_test.go
+1
-1
diff.go
...orkspace/src/github.com/docker/docker/pkg/archive/diff.go
+22
-18
utils_test.go
...ce/src/github.com/docker/docker/pkg/archive/utils_test.go
+2
-1
size.go
..._workspace/src/github.com/docker/docker/pkg/units/size.go
+1
-1
size_test.go
...space/src/github.com/docker/docker/pkg/units/size_test.go
+3
-3
.travis.yml
...rkspace/src/github.com/fsouza/go-dockerclient/.travis.yml
+1
-0
AUTHORS
.../_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS
+5
-0
README.markdown
...ace/src/github.com/fsouza/go-dockerclient/README.markdown
+1
-1
client.go
...workspace/src/github.com/fsouza/go-dockerclient/client.go
+14
-4
container.go
...kspace/src/github.com/fsouza/go-dockerclient/container.go
+1
-0
exec.go
.../_workspace/src/github.com/fsouza/go-dockerclient/exec.go
+48
-0
exec_test.go
...kspace/src/github.com/fsouza/go-dockerclient/exec_test.go
+131
-0
tar.go
...s/_workspace/src/github.com/fsouza/go-dockerclient/tar.go
+3
-3
fmtpolice
...c/github.com/fsouza/go-dockerclient/testing/bin/fmtpolice
+1
-1
server.go
...e/src/github.com/fsouza/go-dockerclient/testing/server.go
+50
-3
server_test.go
.../github.com/fsouza/go-dockerclient/testing/server_test.go
+80
-0
tls.go
...s/_workspace/src/github.com/fsouza/go-dockerclient/tls.go
+100
-0
No files found.
Godeps/Godeps.json
View file @
2863fa9f
...
...
@@ -46,42 +46,42 @@
{
"ImportPath"
:
"github.com/docker/docker/pkg/archive"
,
"Comment"
:
"v1.4.1-108-g364720b"
,
"Rev"
:
"
364720b5e7e725cdc466171de873eefdb8609a33
"
"Rev"
:
"
211513156dc1ace48e630b4bf4ea0fcfdc8d9abf
"
},
{
"ImportPath"
:
"github.com/docker/docker/pkg/fileutils"
,
"Comment"
:
"v1.4.1-108-g364720b"
,
"Rev"
:
"
364720b5e7e725cdc466171de873eefdb8609a33
"
"Rev"
:
"
211513156dc1ace48e630b4bf4ea0fcfdc8d9abf
"
},
{
"ImportPath"
:
"github.com/docker/docker/pkg/ioutils"
,
"Comment"
:
"v1.4.1-108-g364720b"
,
"Rev"
:
"
364720b5e7e725cdc466171de873eefdb8609a33
"
"Rev"
:
"
211513156dc1ace48e630b4bf4ea0fcfdc8d9abf
"
},
{
"ImportPath"
:
"github.com/docker/docker/pkg/pools"
,
"Comment"
:
"v1.4.1-108-g364720b"
,
"Rev"
:
"
364720b5e7e725cdc466171de873eefdb8609a33
"
"Rev"
:
"
211513156dc1ace48e630b4bf4ea0fcfdc8d9abf
"
},
{
"ImportPath"
:
"github.com/docker/docker/pkg/promise"
,
"Comment"
:
"v1.4.1-108-g364720b"
,
"Rev"
:
"
364720b5e7e725cdc466171de873eefdb8609a33
"
"Rev"
:
"
211513156dc1ace48e630b4bf4ea0fcfdc8d9abf
"
},
{
"ImportPath"
:
"github.com/docker/docker/pkg/system"
,
"Comment"
:
"v1.4.1-108-g364720b"
,
"Rev"
:
"
364720b5e7e725cdc466171de873eefdb8609a33
"
"Rev"
:
"
211513156dc1ace48e630b4bf4ea0fcfdc8d9abf
"
},
{
"ImportPath"
:
"github.com/docker/docker/pkg/units"
,
"Comment"
:
"v1.4.1-108-g364720b"
,
"Rev"
:
"
364720b5e7e725cdc466171de873eefdb8609a33
"
"Rev"
:
"
211513156dc1ace48e630b4bf4ea0fcfdc8d9abf
"
},
{
"ImportPath"
:
"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
,
"Comment"
:
"v1.4.1-108-g364720b"
,
"Rev"
:
"
364720b5e7e725cdc466171de873eefdb8609a33
"
"Rev"
:
"
211513156dc1ace48e630b4bf4ea0fcfdc8d9abf
"
},
{
"ImportPath"
:
"github.com/elazarl/go-bindata-assetfs"
,
...
...
@@ -94,8 +94,7 @@
},
{
"ImportPath"
:
"github.com/fsouza/go-dockerclient"
,
"Comment"
:
"0.2.1-334-g9c377ff"
,
"Rev"
:
"9c377ffd9aed48a012adf1c3fd517fe98394120b"
"Rev"
:
"d19717788084716e4adff0515be6289aa04bec46"
},
{
"ImportPath"
:
"github.com/ghodss/yaml"
,
...
...
Godeps/_workspace/src/github.com/docker/docker/pkg/archive/archive.go
View file @
2863fa9f
...
...
@@ -30,11 +30,11 @@ type (
ArchiveReader
io
.
Reader
Compression
int
TarOptions
struct
{
Includes
[]
string
Exclude
s
[]
string
Compression
Compression
NoLchown
bool
Name
string
Include
File
s
[]
string
Exclude
Patterns
[]
string
Compression
Compression
NoLchown
bool
Name
string
}
// Archiver allows the reuse of most utility functions of this package
...
...
@@ -378,7 +378,7 @@ func escapeName(name string) string {
}
// TarWithOptions creates an archive from the directory at `path`, only including files whose relative
// paths are included in `options.Include
s` (if non-nil) or not in `options.Exclude
s`.
// paths are included in `options.Include
Files` (if non-nil) or not in `options.ExcludePattern
s`.
func
TarWithOptions
(
srcPath
string
,
options
*
TarOptions
)
(
io
.
ReadCloser
,
error
)
{
pipeReader
,
pipeWriter
:=
io
.
Pipe
()
...
...
@@ -401,12 +401,14 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
// mutating the filesystem and we can see transient errors
// from this
if
options
.
Includes
==
nil
{
options
.
Includes
=
[]
string
{
"."
}
if
options
.
Include
File
s
==
nil
{
options
.
Include
File
s
=
[]
string
{
"."
}
}
seen
:=
make
(
map
[
string
]
bool
)
var
renamedRelFilePath
string
// For when tar.Options.Name is set
for
_
,
include
:=
range
options
.
Includes
{
for
_
,
include
:=
range
options
.
Include
File
s
{
filepath
.
Walk
(
filepath
.
Join
(
srcPath
,
include
),
func
(
filePath
string
,
f
os
.
FileInfo
,
err
error
)
error
{
if
err
!=
nil
{
log
.
Debugf
(
"Tar: Can't stat file %s to tar: %s"
,
srcPath
,
err
)
...
...
@@ -420,10 +422,19 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
return
nil
}
skip
,
err
:=
fileutils
.
Matches
(
relFilePath
,
options
.
Excludes
)
if
err
!=
nil
{
log
.
Debugf
(
"Error matching %s"
,
relFilePath
,
err
)
return
err
skip
:=
false
// If "include" is an exact match for the current file
// then even if there's an "excludePatterns" pattern that
// matches it, don't skip it. IOW, assume an explicit 'include'
// is asking for that file no matter what - which is true
// for some files, like .dockerignore and Dockerfile (sometimes)
if
include
!=
relFilePath
{
skip
,
err
=
fileutils
.
Matches
(
relFilePath
,
options
.
ExcludePatterns
)
if
err
!=
nil
{
log
.
Debugf
(
"Error matching %s"
,
relFilePath
,
err
)
return
err
}
}
if
skip
{
...
...
@@ -433,6 +444,11 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
return
nil
}
if
seen
[
relFilePath
]
{
return
nil
}
seen
[
relFilePath
]
=
true
// Rename the base resource
if
options
.
Name
!=
""
&&
filePath
==
srcPath
+
"/"
+
filepath
.
Base
(
relFilePath
)
{
renamedRelFilePath
=
relFilePath
...
...
@@ -487,7 +503,7 @@ loop:
// This keeps "../" as-is, but normalizes "/../" to "/"
hdr
.
Name
=
filepath
.
Clean
(
hdr
.
Name
)
for
_
,
exclude
:=
range
options
.
Excludes
{
for
_
,
exclude
:=
range
options
.
Exclude
Pattern
s
{
if
strings
.
HasPrefix
(
hdr
.
Name
,
exclude
)
{
continue
loop
}
...
...
@@ -563,8 +579,8 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
if
options
==
nil
{
options
=
&
TarOptions
{}
}
if
options
.
Excludes
==
nil
{
options
.
Excludes
=
[]
string
{}
if
options
.
Exclude
Pattern
s
==
nil
{
options
.
Exclude
Pattern
s
=
[]
string
{}
}
decompressedArchive
,
err
:=
DecompressStream
(
archive
)
if
err
!=
nil
{
...
...
Godeps/_workspace/src/github.com/docker/docker/pkg/archive/archive_test.go
View file @
2863fa9f
...
...
@@ -165,8 +165,8 @@ func TestTarUntar(t *testing.T) {
Gzip
,
}
{
changes
,
err
:=
tarUntar
(
t
,
origin
,
&
TarOptions
{
Compression
:
c
,
Exclude
s
:
[]
string
{
"3"
},
Compression
:
c
,
Exclude
Patterns
:
[]
string
{
"3"
},
})
if
err
!=
nil
{
...
...
@@ -196,8 +196,8 @@ func TestTarWithOptions(t *testing.T) {
opts
*
TarOptions
numChanges
int
}{
{
&
TarOptions
{
Includes
:
[]
string
{
"1"
}},
1
},
{
&
TarOptions
{
Excludes
:
[]
string
{
"2"
}},
1
},
{
&
TarOptions
{
Include
File
s
:
[]
string
{
"1"
}},
1
},
{
&
TarOptions
{
Exclude
Pattern
s
:
[]
string
{
"2"
}},
1
},
}
for
_
,
testCase
:=
range
cases
{
changes
,
err
:=
tarUntar
(
t
,
origin
,
testCase
.
opts
)
...
...
Godeps/_workspace/src/github.com/docker/docker/pkg/archive/changes_test.go
View file @
2863fa9f
...
...
@@ -286,7 +286,7 @@ func TestApplyLayer(t *testing.T) {
t
.
Fatal
(
err
)
}
if
err
:=
ApplyLayer
(
src
,
layerCopy
);
err
!=
nil
{
if
_
,
err
:=
ApplyLayer
(
src
,
layerCopy
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
...
...
Godeps/_workspace/src/github.com/docker/docker/pkg/archive/diff.go
View file @
2863fa9f
...
...
@@ -15,7 +15,7 @@ import (
"github.com/docker/docker/pkg/system"
)
func
UnpackLayer
(
dest
string
,
layer
ArchiveReader
)
error
{
func
UnpackLayer
(
dest
string
,
layer
ArchiveReader
)
(
size
int64
,
err
error
)
{
tr
:=
tar
.
NewReader
(
layer
)
trBuf
:=
pools
.
BufioReader32KPool
.
Get
(
tr
)
defer
pools
.
BufioReader32KPool
.
Put
(
trBuf
)
...
...
@@ -33,9 +33,11 @@ func UnpackLayer(dest string, layer ArchiveReader) error {
break
}
if
err
!=
nil
{
return
err
return
0
,
err
}
size
+=
hdr
.
Size
// Normalize name, for safety and for a simple is-root check
hdr
.
Name
=
filepath
.
Clean
(
hdr
.
Name
)
...
...
@@ -48,7 +50,7 @@ func UnpackLayer(dest string, layer ArchiveReader) error {
if
_
,
err
:=
os
.
Lstat
(
parentPath
);
err
!=
nil
&&
os
.
IsNotExist
(
err
)
{
err
=
os
.
MkdirAll
(
parentPath
,
0600
)
if
err
!=
nil
{
return
err
return
0
,
err
}
}
}
...
...
@@ -63,12 +65,12 @@ func UnpackLayer(dest string, layer ArchiveReader) error {
aufsHardlinks
[
basename
]
=
hdr
if
aufsTempdir
==
""
{
if
aufsTempdir
,
err
=
ioutil
.
TempDir
(
""
,
"dockerplnk"
);
err
!=
nil
{
return
err
return
0
,
err
}
defer
os
.
RemoveAll
(
aufsTempdir
)
}
if
err
:=
createTarFile
(
filepath
.
Join
(
aufsTempdir
,
basename
),
dest
,
hdr
,
tr
,
true
);
err
!=
nil
{
return
err
return
0
,
err
}
}
continue
...
...
@@ -77,10 +79,10 @@ func UnpackLayer(dest string, layer ArchiveReader) error {
path
:=
filepath
.
Join
(
dest
,
hdr
.
Name
)
rel
,
err
:=
filepath
.
Rel
(
dest
,
path
)
if
err
!=
nil
{
return
err
return
0
,
err
}
if
strings
.
HasPrefix
(
rel
,
".."
)
{
return
breakoutError
(
fmt
.
Errorf
(
"%q is outside of %q"
,
hdr
.
Name
,
dest
))
return
0
,
breakoutError
(
fmt
.
Errorf
(
"%q is outside of %q"
,
hdr
.
Name
,
dest
))
}
base
:=
filepath
.
Base
(
path
)
...
...
@@ -88,7 +90,7 @@ func UnpackLayer(dest string, layer ArchiveReader) error {
originalBase
:=
base
[
len
(
".wh."
)
:
]
originalPath
:=
filepath
.
Join
(
filepath
.
Dir
(
path
),
originalBase
)
if
err
:=
os
.
RemoveAll
(
originalPath
);
err
!=
nil
{
return
err
return
0
,
err
}
}
else
{
// If path exits we almost always just want to remove and replace it.
...
...
@@ -98,7 +100,7 @@ func UnpackLayer(dest string, layer ArchiveReader) error {
if
fi
,
err
:=
os
.
Lstat
(
path
);
err
==
nil
{
if
!
(
fi
.
IsDir
()
&&
hdr
.
Typeflag
==
tar
.
TypeDir
)
{
if
err
:=
os
.
RemoveAll
(
path
);
err
!=
nil
{
return
err
return
0
,
err
}
}
}
...
...
@@ -113,18 +115,18 @@ func UnpackLayer(dest string, layer ArchiveReader) error {
linkBasename
:=
filepath
.
Base
(
hdr
.
Linkname
)
srcHdr
=
aufsHardlinks
[
linkBasename
]
if
srcHdr
==
nil
{
return
fmt
.
Errorf
(
"Invalid aufs hardlink"
)
return
0
,
fmt
.
Errorf
(
"Invalid aufs hardlink"
)
}
tmpFile
,
err
:=
os
.
Open
(
filepath
.
Join
(
aufsTempdir
,
linkBasename
))
if
err
!=
nil
{
return
err
return
0
,
err
}
defer
tmpFile
.
Close
()
srcData
=
tmpFile
}
if
err
:=
createTarFile
(
path
,
dest
,
srcHdr
,
srcData
,
true
);
err
!=
nil
{
return
err
return
0
,
err
}
// Directory mtimes must be handled at the end to avoid further
...
...
@@ -139,27 +141,29 @@ func UnpackLayer(dest string, layer ArchiveReader) error {
path
:=
filepath
.
Join
(
dest
,
hdr
.
Name
)
ts
:=
[]
syscall
.
Timespec
{
timeToTimespec
(
hdr
.
AccessTime
),
timeToTimespec
(
hdr
.
ModTime
)}
if
err
:=
syscall
.
UtimesNano
(
path
,
ts
);
err
!=
nil
{
return
err
return
0
,
err
}
}
return
nil
return
size
,
nil
}
// ApplyLayer parses a diff in the standard layer format from `layer`, and
// applies it to the directory `dest`.
func
ApplyLayer
(
dest
string
,
layer
ArchiveReader
)
error
{
// applies it to the directory `dest`. Returns the size in bytes of the
// contents of the layer.
func
ApplyLayer
(
dest
string
,
layer
ArchiveReader
)
(
int64
,
error
)
{
dest
=
filepath
.
Clean
(
dest
)
// We need to be able to set any perms
oldmask
,
err
:=
system
.
Umask
(
0
)
if
err
!=
nil
{
return
err
return
0
,
err
}
defer
system
.
Umask
(
oldmask
)
// ignore err, ErrNotSupportedPlatform
layer
,
err
=
DecompressStream
(
layer
)
if
err
!=
nil
{
return
err
return
0
,
err
}
return
UnpackLayer
(
dest
,
layer
)
}
Godeps/_workspace/src/github.com/docker/docker/pkg/archive/utils_test.go
View file @
2863fa9f
...
...
@@ -17,7 +17,8 @@ var testUntarFns = map[string]func(string, io.Reader) error{
return
Untar
(
r
,
dest
,
nil
)
},
"applylayer"
:
func
(
dest
string
,
r
io
.
Reader
)
error
{
return
ApplyLayer
(
dest
,
ArchiveReader
(
r
))
_
,
err
:=
ApplyLayer
(
dest
,
ArchiveReader
(
r
))
return
err
},
}
...
...
Godeps/_workspace/src/github.com/docker/docker/pkg/units/size.go
View file @
2863fa9f
...
...
@@ -39,7 +39,7 @@ var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB",
// HumanSize returns a human-readable approximation of a size
// using SI standard (eg. "44kB", "17MB")
func
HumanSize
(
size
in
t64
)
string
{
func
HumanSize
(
size
floa
t64
)
string
{
return
intToString
(
float64
(
size
),
1000.0
,
decimapAbbrs
)
}
...
...
Godeps/_workspace/src/github.com/docker/docker/pkg/units/size_test.go
View file @
2863fa9f
...
...
@@ -23,9 +23,9 @@ func TestHumanSize(t *testing.T) {
assertEquals
(
t
,
"1 MB"
,
HumanSize
(
1000000
))
assertEquals
(
t
,
"1.049 MB"
,
HumanSize
(
1048576
))
assertEquals
(
t
,
"2 MB"
,
HumanSize
(
2
*
MB
))
assertEquals
(
t
,
"3.42 GB"
,
HumanSize
(
int64
(
float64
(
3.42
*
GB
)
)))
assertEquals
(
t
,
"5.372 TB"
,
HumanSize
(
int64
(
float64
(
5.372
*
TB
)
)))
assertEquals
(
t
,
"2.22 PB"
,
HumanSize
(
int64
(
float64
(
2.22
*
PB
)
)))
assertEquals
(
t
,
"3.42 GB"
,
HumanSize
(
float64
(
3.42
*
GB
)))
assertEquals
(
t
,
"5.372 TB"
,
HumanSize
(
float64
(
5.372
*
TB
)))
assertEquals
(
t
,
"2.22 PB"
,
HumanSize
(
float64
(
2.22
*
PB
)))
}
func
TestFromHumanSize
(
t
*
testing
.
T
)
{
...
...
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml
View file @
2863fa9f
...
...
@@ -2,6 +2,7 @@ language: go
go
:
-
1.2.2
-
1.3.1
-
1.4
-
tip
env
:
-
GOARCH=amd64
...
...
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS
View file @
2863fa9f
...
...
@@ -3,6 +3,7 @@
Aldrin Leal <aldrin@leal.eng.br>
Andreas Jaekle <andreas@jaekle.net>
Andrews Medina <andrewsmedina@gmail.com>
Artem Sidorenko <artem@2realities.com>
Andy Goldstein <andy.goldstein@redhat.com>
Ben McCann <benmccann.com>
Carlos Diaz-Padron <cpadron@mozilla.com>
...
...
@@ -16,6 +17,7 @@ Dawn Chen <dawnchen@google.com>
Ed <edrocksit@gmail.com>
Eric Anderson <anderson@copperegg.com>
Fabio Rehm <fgrehm@gmail.com>
Fatih Arslan <ftharsln@gmail.com>
Flavia Missi <flaviamissi@gmail.com>
Francisco Souza <f@souza.cc>
Jari Kolehmainen <jari.kolehmainen@digia.com>
...
...
@@ -25,11 +27,14 @@ Jean-Baptiste Dalido <jeanbaptiste@appgratis.com>
Jeff Mitchell <jeffrey.mitchell@gmail.com>
Jeffrey Hulten <jhulten@gmail.com>
Johan Euphrosine <proppy@google.com>
Kamil Domanski <kamil@domanski.co>
Karan Misra <kidoman@gmail.com>
Kim, Hirokuni <hirokuni.kim@kvh.co.jp>
Lucas Clemente <lucas@clemente.io>
Martin Sweeney <martin@sweeney.io>
Máximo Cuadros Ortiz <mcuadros@gmail.com>
Mike Dillon <mike.dillon@synctree.com>
Mrunal Patel <mrunalp@gmail.com>
Omeid Matten <public@omeid.me>
Paul Morie <pmorie@gmail.com>
Peter Jihoon Kim <raingrove@gmail.com>
...
...
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/README.markdown
View file @
2863fa9f
...
...
@@ -22,7 +22,7 @@ import (
func
main
()
{
endpoint
:=
"unix:///var/run/docker.sock"
client
,
_
:=
docker
.
NewClient
(
endpoint
)
imgs
,
_
:=
client
.
ListImages
(
true
)
imgs
,
_
:=
client
.
ListImages
(
docker
.
ListImagesOptions
{
All
:
false
}
)
for
_
,
img
:=
range
imgs
{
fmt
.
Println
(
"ID: "
,
img
.
ID
)
fmt
.
Println
(
"RepoTags: "
,
img
.
RepoTags
)
...
...
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go
View file @
2863fa9f
...
...
@@ -460,24 +460,34 @@ func (c *Client) hijack(method, path string, success chan struct{}, setRawTermin
protocol
=
"tcp"
address
=
c
.
endpointURL
.
Host
}
dial
,
err
:=
net
.
Dial
(
protocol
,
address
)
if
err
!=
nil
{
return
err
var
dial
net
.
Conn
if
c
.
TLSConfig
!=
nil
&&
protocol
!=
"unix"
{
dial
,
err
=
tlsDial
(
protocol
,
address
,
c
.
TLSConfig
)
if
err
!=
nil
{
return
err
}
}
else
{
dial
,
err
=
net
.
Dial
(
protocol
,
address
)
if
err
!=
nil
{
return
err
}
}
defer
dial
.
Close
()
clientconn
:=
httputil
.
NewClientConn
(
dial
,
nil
)
defer
clientconn
.
Close
()
clientconn
.
Do
(
req
)
if
success
!=
nil
{
success
<-
struct
{}{}
<-
success
}
rwc
,
br
:=
clientconn
.
Hijack
()
defer
rwc
.
Close
()
errs
:=
make
(
chan
error
,
2
)
exit
:=
make
(
chan
bool
)
go
func
()
{
defer
close
(
exit
)
var
err
error
if
setRawTerminal
{
// When TTY is ON, use regular copy
_
,
err
=
io
.
Copy
(
stdout
,
br
)
}
else
{
_
,
err
=
stdCopy
(
stdout
,
stderr
,
br
)
...
...
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go
View file @
2863fa9f
...
...
@@ -358,6 +358,7 @@ type HostConfig struct {
ExtraHosts
[]
string
`json:"ExtraHosts,omitempty" yaml:"ExtraHosts,omitempty"`
VolumesFrom
[]
string
`json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"`
NetworkMode
string
`json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"`
IpcMode
string
`json:"IpcMode,omitempty" yaml:"IpcMode,omitempty"`
RestartPolicy
RestartPolicy
`json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty"`
}
...
...
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec.go
View file @
2863fa9f
...
...
@@ -56,6 +56,34 @@ type Exec struct {
ID
string
`json:"Id,omitempty" yaml:"Id,omitempty"`
}
// ExecProcessConfig is a type describing the command associated to a Exec
// instance. It's used in the ExecInspect type.
//
// See http://goo.gl/ypQULN for more details
type
ExecProcessConfig
struct
{
Privileged
bool
`json:"privileged,omitempty" yaml:"privileged,omitempty"`
User
string
`json:"user,omitempty" yaml:"user,omitempty"`
Tty
bool
`json:"tty,omitempty" yaml:"tty,omitempty"`
EntryPoint
string
`json:"entrypoint,omitempty" yaml:"entrypoint,omitempty"`
Arguments
[]
string
`json:"arguments,omitempty" yaml:"arguments,omitempty"`
}
// ExecInspect is a type with details about a exec instance, including the
// exit code if the command has finished running. It's returned by a api
// call to /exec/(id)/json
//
// See http://goo.gl/ypQULN for more details
type
ExecInspect
struct
{
ID
string
`json:"ID,omitempty" yaml:"ID,omitempty"`
Running
bool
`json:"Running,omitempty" yaml:"Running,omitempty"`
ExitCode
int
`json:"ExitCode,omitempty" yaml:"ExitCode,omitempty"`
OpenStdin
bool
`json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty"`
OpenStderr
bool
`json:"OpenStderr,omitempty" yaml:"OpenStderr,omitempty"`
OpenStdout
bool
`json:"OpenStdout,omitempty" yaml:"OpenStdout,omitempty"`
ProcessConfig
ExecProcessConfig
`json:"ProcessConfig,omitempty" yaml:"ProcessConfig,omitempty"`
Container
Container
`json:"Container,omitempty" yaml:"Container,omitempty"`
}
// CreateExec sets up an exec instance in a running container `id`, returning the exec
// instance, or an error in case of failure.
//
...
...
@@ -119,6 +147,26 @@ func (c *Client) ResizeExecTTY(id string, height, width int) error {
return
err
}
// InspectExec returns low-level information about the exec command id.
//
// See http://goo.gl/ypQULN for more details
func
(
c
*
Client
)
InspectExec
(
id
string
)
(
*
ExecInspect
,
error
)
{
path
:=
fmt
.
Sprintf
(
"/exec/%s/json"
,
id
)
body
,
status
,
err
:=
c
.
do
(
"GET"
,
path
,
nil
)
if
status
==
http
.
StatusNotFound
{
return
nil
,
&
NoSuchExec
{
ID
:
id
}
}
if
err
!=
nil
{
return
nil
,
err
}
var
exec
ExecInspect
err
=
json
.
Unmarshal
(
body
,
&
exec
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
exec
,
nil
}
// NoSuchExec is the error returned when a given exec instance does not exist.
type
NoSuchExec
struct
{
ID
string
...
...
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec_test.go
View file @
2863fa9f
...
...
@@ -10,6 +10,7 @@ import (
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"strings"
"testing"
)
...
...
@@ -126,3 +127,133 @@ func TestExecResize(t *testing.T) {
t
.
Errorf
(
"ExecCreate: Wrong path in request. Want %q. Got %q."
,
expectedURL
.
Path
,
gotPath
)
}
}
func
TestExecInspect
(
t
*
testing
.
T
)
{
jsonExec
:=
`{
"ID": "32adfeeec34250f9530ce1dafd40c6233832315e065ea6b362d745e2f63cde0e",
"Running": true,
"ExitCode": 0,
"ProcessConfig": {
"privileged": false,
"user": "",
"tty": true,
"entrypoint": "bash",
"arguments": []
},
"OpenStdin": true,
"OpenStderr": true,
"OpenStdout": true,
"Container": {
"State": {
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Pid": 29392,
"ExitCode": 0,
"Error": "",
"StartedAt": "2015-01-21T17:08:59.634662178Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"ID": "922cd0568714763dc725b24b7c9801016b2a3de68e2a1dc989bf5abf07740521",
"Created": "2015-01-21T17:08:59.46407212Z",
"Path": "/bin/bash",
"Args": [
"-lc",
"tsuru_unit_agent http://192.168.50.4:8080 689b30e0ab3adce374346de2e72512138e0e8b75 gtest /var/lib/tsuru/start && tail -f /dev/null"
],
"Config": {
"Hostname": "922cd0568714",
"Domainname": "",
"User": "ubuntu",
"Memory": 0,
"MemorySwap": 0,
"CpuShares": 100,
"Cpuset": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"PortSpecs": null,
"ExposedPorts": {
"8888/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/bash",
"-lc",
"tsuru_unit_agent http://192.168.50.4:8080 689b30e0ab3adce374346de2e72512138e0e8b75 gtest /var/lib/tsuru/start && tail -f /dev/null"
],
"Image": "tsuru/app-gtest",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"NetworkDisabled": false,
"MacAddress": "",
"OnBuild": null
},
"Image": "a88060b8b54fde0f7168c86742d0ce83b80f3f10925d85c98fdad9ed00bef544",
"NetworkSettings": {
"IPAddress": "172.17.0.8",
"IPPrefixLen": 16,
"MacAddress": "02:42:ac:11:00:08",
"LinkLocalIPv6Address": "fe80::42:acff:fe11:8",
"LinkLocalIPv6PrefixLen": 64,
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"Gateway": "172.17.42.1",
"IPv6Gateway": "",
"Bridge": "docker0",
"PortMapping": null,
"Ports": {
"8888/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "49156"
}
]
}
},
"ResolvConfPath": "/var/lib/docker/containers/922cd0568714763dc725b24b7c9801016b2a3de68e2a1dc989bf5abf07740521/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/922cd0568714763dc725b24b7c9801016b2a3de68e2a1dc989bf5abf07740521/hostname",
"HostsPath": "/var/lib/docker/containers/922cd0568714763dc725b24b7c9801016b2a3de68e2a1dc989bf5abf07740521/hosts",
"Name": "/c7e43b72288ee9d0270a",
"Driver": "aufs",
"ExecDriver": "native-0.2",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"RestartCount": 0,
"UpdateDns": false,
"Volumes": {},
"VolumesRW": {}
}
}`
var
expected
ExecInspect
err
:=
json
.
Unmarshal
([]
byte
(
jsonExec
),
&
expected
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
fakeRT
:=
&
FakeRoundTripper
{
message
:
jsonExec
,
status
:
http
.
StatusOK
}
client
:=
newTestClient
(
fakeRT
)
expectedID
:=
"32adfeeec34250f9530ce1dafd40c6233832315e065ea6b362d745e2f63cde0e"
execObj
,
err
:=
client
.
InspectExec
(
expectedID
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
!
reflect
.
DeepEqual
(
*
execObj
,
expected
)
{
t
.
Errorf
(
"ExecInspect: Expected %#v. Got %#v."
,
expected
,
*
execObj
)
}
req
:=
fakeRT
.
requests
[
0
]
if
req
.
Method
!=
"GET"
{
t
.
Errorf
(
"ExecInspect: wrong HTTP method. Want %q. Got %q."
,
"GET"
,
req
.
Method
)
}
expectedURL
,
_
:=
url
.
Parse
(
client
.
getURL
(
"/exec/"
+
expectedID
+
"/json"
))
if
gotPath
:=
fakeRT
.
requests
[
0
]
.
URL
.
Path
;
gotPath
!=
expectedURL
.
Path
{
t
.
Errorf
(
"ExecInspect: Wrong path in request. Want %q. Got %q."
,
expectedURL
.
Path
,
gotPath
)
}
}
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/tar.go
View file @
2863fa9f
...
...
@@ -27,9 +27,9 @@ func createTarStream(srcPath string) (io.ReadCloser, error) {
return
nil
,
err
}
tarOpts
:=
&
archive
.
TarOptions
{
Exclude
s
:
excludes
,
Compression
:
archive
.
Uncompressed
,
NoLchown
:
true
,
Exclude
Patterns
:
excludes
,
Compression
:
archive
.
Uncompressed
,
NoLchown
:
true
,
}
return
archive
.
TarWithOptions
(
srcPath
,
tarOpts
)
}
...
...
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/bin/fmtpolice
View file @
2863fa9f
...
...
@@ -31,7 +31,7 @@ _lint_verbose() {
_install_linter
()
{
if
[[
!
-x
"
${
GOPATH
}
/bin/golint"
]]
;
then
go get
-u
github.com/golang/lint/golint
go get
-u
-f
github.com/golang/lint/golint
fi
}
...
...
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go
View file @
2863fa9f
...
...
@@ -34,7 +34,7 @@ import (
// For more details on the remote API, check http://goo.gl/G3plxW.
type
DockerServer
struct
{
containers
[]
*
docker
.
Container
execs
[]
*
docker
.
Exec
execs
[]
*
docker
.
Exec
Inspect
cMut
sync
.
RWMutex
images
[]
docker
.
Image
iMut
sync
.
RWMutex
...
...
@@ -99,7 +99,9 @@ func (s *DockerServer) buildMuxer() {
s
.
mux
.
Path
(
"/containers/{id:.*}/attach"
)
.
Methods
(
"POST"
)
.
HandlerFunc
(
s
.
handlerWrapper
(
s
.
attachContainer
))
s
.
mux
.
Path
(
"/containers/{id:.*}"
)
.
Methods
(
"DELETE"
)
.
HandlerFunc
(
s
.
handlerWrapper
(
s
.
removeContainer
))
s
.
mux
.
Path
(
"/containers/{id:.*}/exec"
)
.
Methods
(
"POST"
)
.
HandlerFunc
(
s
.
handlerWrapper
(
s
.
createExecContainer
))
s
.
mux
.
Path
(
"/exec/{id:.*}/resize"
)
.
Methods
(
"POST"
)
.
HandlerFunc
(
s
.
handlerWrapper
(
s
.
resizeExecContainer
))
s
.
mux
.
Path
(
"/exec/{id:.*}/start"
)
.
Methods
(
"POST"
)
.
HandlerFunc
(
s
.
handlerWrapper
(
s
.
startExecContainer
))
s
.
mux
.
Path
(
"/exec/{id:.*}/json"
)
.
Methods
(
"GET"
)
.
HandlerFunc
(
s
.
handlerWrapper
(
s
.
inspectExecContainer
))
s
.
mux
.
Path
(
"/images/create"
)
.
Methods
(
"POST"
)
.
HandlerFunc
(
s
.
handlerWrapper
(
s
.
pullImage
))
s
.
mux
.
Path
(
"/build"
)
.
Methods
(
"POST"
)
.
HandlerFunc
(
s
.
handlerWrapper
(
s
.
buildImage
))
s
.
mux
.
Path
(
"/images/json"
)
.
Methods
(
"GET"
)
.
HandlerFunc
(
s
.
handlerWrapper
(
s
.
listImages
))
...
...
@@ -724,10 +726,31 @@ func (s *DockerServer) getImage(w http.ResponseWriter, r *http.Request) {
}
func
(
s
*
DockerServer
)
createExecContainer
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
id
:=
mux
.
Vars
(
r
)[
"id"
]
container
,
_
,
err
:=
s
.
findContainer
(
id
)
if
err
!=
nil
{
http
.
Error
(
w
,
err
.
Error
(),
http
.
StatusNotFound
)
return
}
exec
:=
docker
.
ExecInspect
{
ID
:
"id-exec-created-by-test"
,
Container
:
*
container
,
}
var
params
docker
.
CreateExecOptions
err
=
json
.
NewDecoder
(
r
.
Body
)
.
Decode
(
&
params
)
if
err
!=
nil
{
http
.
Error
(
w
,
err
.
Error
(),
http
.
StatusInternalServerError
)
return
}
if
len
(
params
.
Cmd
)
>
0
{
exec
.
ProcessConfig
.
EntryPoint
=
params
.
Cmd
[
0
]
if
len
(
params
.
Cmd
)
>
1
{
exec
.
ProcessConfig
.
Arguments
=
params
.
Cmd
[
1
:
]
}
}
s
.
execs
=
append
(
s
.
execs
,
&
exec
)
w
.
WriteHeader
(
http
.
StatusOK
)
w
.
Header
()
.
Set
(
"Content-Type"
,
"application/json"
)
exec
:=
docker
.
Exec
{
ID
:
"id-exec-created-by-test"
}
s
.
execs
=
append
(
s
.
execs
,
&
exec
)
json
.
NewEncoder
(
w
)
.
Encode
(
map
[
string
]
string
{
"Id"
:
exec
.
ID
})
}
...
...
@@ -742,3 +765,27 @@ func (s *DockerServer) startExecContainer(w http.ResponseWriter, r *http.Request
}
w
.
WriteHeader
(
http
.
StatusNotFound
)
}
func
(
s
*
DockerServer
)
resizeExecContainer
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
id
:=
mux
.
Vars
(
r
)[
"id"
]
for
_
,
exec
:=
range
s
.
execs
{
if
exec
.
ID
==
id
{
w
.
WriteHeader
(
http
.
StatusOK
)
return
}
}
w
.
WriteHeader
(
http
.
StatusNotFound
)
}
func
(
s
*
DockerServer
)
inspectExecContainer
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
id
:=
mux
.
Vars
(
r
)[
"id"
]
for
_
,
exec
:=
range
s
.
execs
{
if
exec
.
ID
==
id
{
w
.
WriteHeader
(
http
.
StatusOK
)
w
.
Header
()
.
Set
(
"Content-Type"
,
"application/json"
)
json
.
NewEncoder
(
w
)
.
Encode
(
exec
)
return
}
}
w
.
WriteHeader
(
http
.
StatusNotFound
)
}
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go
View file @
2863fa9f
...
...
@@ -1089,3 +1089,83 @@ func TestDefaultHandler(t *testing.T) {
t
.
Fatalf
(
"DefaultHandler: Expected to return server.mux, got: %#v"
,
server
.
DefaultHandler
())
}
}
func
TestCreateExecContainer
(
t
*
testing
.
T
)
{
server
:=
DockerServer
{}
addContainers
(
&
server
,
2
)
server
.
buildMuxer
()
recorder
:=
httptest
.
NewRecorder
()
body
:=
`{"Cmd": ["bash", "-c", "ls"]}`
path
:=
fmt
.
Sprintf
(
"/containers/%s/exec"
,
server
.
containers
[
0
]
.
ID
)
request
,
_
:=
http
.
NewRequest
(
"POST"
,
path
,
strings
.
NewReader
(
body
))
server
.
ServeHTTP
(
recorder
,
request
)
if
recorder
.
Code
!=
http
.
StatusOK
{
t
.
Fatalf
(
"CreateExec: wrong status. Want %d. Got %d."
,
http
.
StatusOK
,
recorder
.
Code
)
}
serverExec
:=
server
.
execs
[
0
]
var
got
docker
.
Exec
err
:=
json
.
NewDecoder
(
recorder
.
Body
)
.
Decode
(
&
got
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
got
.
ID
!=
serverExec
.
ID
{
t
.
Errorf
(
"CreateExec: wrong value. Want %#v. Got %#v."
,
serverExec
.
ID
,
got
.
ID
)
}
expected
:=
docker
.
ExecInspect
{
ID
:
got
.
ID
,
ProcessConfig
:
docker
.
ExecProcessConfig
{
EntryPoint
:
"bash"
,
Arguments
:
[]
string
{
"-c"
,
"ls"
},
},
Container
:
*
server
.
containers
[
0
],
}
if
!
reflect
.
DeepEqual
(
*
serverExec
,
expected
)
{
t
.
Errorf
(
"InspectContainer: wrong value. Want:
\n
%#v
\n
Got:
\n
%#v
\n
"
,
expected
,
*
serverExec
)
}
}
func
TestInspectExecContainer
(
t
*
testing
.
T
)
{
server
:=
DockerServer
{}
addContainers
(
&
server
,
1
)
server
.
buildMuxer
()
recorder
:=
httptest
.
NewRecorder
()
body
:=
`{"Cmd": ["bash", "-c", "ls"]}`
path
:=
fmt
.
Sprintf
(
"/containers/%s/exec"
,
server
.
containers
[
0
]
.
ID
)
request
,
_
:=
http
.
NewRequest
(
"POST"
,
path
,
strings
.
NewReader
(
body
))
server
.
ServeHTTP
(
recorder
,
request
)
if
recorder
.
Code
!=
http
.
StatusOK
{
t
.
Fatalf
(
"CreateExec: wrong status. Want %d. Got %d."
,
http
.
StatusOK
,
recorder
.
Code
)
}
var
got
docker
.
Exec
err
:=
json
.
NewDecoder
(
recorder
.
Body
)
.
Decode
(
&
got
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
path
=
fmt
.
Sprintf
(
"/exec/%s/json"
,
got
.
ID
)
request
,
_
=
http
.
NewRequest
(
"GET"
,
path
,
nil
)
server
.
ServeHTTP
(
recorder
,
request
)
if
recorder
.
Code
!=
http
.
StatusOK
{
t
.
Fatalf
(
"CreateExec: wrong status. Want %d. Got %d."
,
http
.
StatusOK
,
recorder
.
Code
)
}
var
got2
docker
.
ExecInspect
err
=
json
.
NewDecoder
(
recorder
.
Body
)
.
Decode
(
&
got2
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
expected
:=
docker
.
ExecInspect
{
ID
:
got
.
ID
,
ProcessConfig
:
docker
.
ExecProcessConfig
{
EntryPoint
:
"bash"
,
Arguments
:
[]
string
{
"-c"
,
"ls"
},
},
Container
:
*
server
.
containers
[
0
],
}
got2
.
Container
.
State
.
StartedAt
=
expected
.
Container
.
State
.
StartedAt
got2
.
Container
.
State
.
FinishedAt
=
expected
.
Container
.
State
.
FinishedAt
got2
.
Container
.
Config
=
expected
.
Container
.
Config
got2
.
Container
.
Created
=
expected
.
Container
.
Created
got2
.
Container
.
NetworkSettings
=
expected
.
Container
.
NetworkSettings
if
!
reflect
.
DeepEqual
(
got2
,
expected
)
{
t
.
Errorf
(
"InspectContainer: wrong value. Want:
\n
%#v
\n
Got:
\n
%#v
\n
"
,
expected
,
got2
)
}
}
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/tls.go
0 → 100644
View file @
2863fa9f
// Copyright 2014 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// The content is borrowed from Docker's own source code to provide a simple
// tls based dialer
package
docker
import
(
"crypto/tls"
"errors"
"net"
"strings"
"time"
)
type
tlsClientCon
struct
{
*
tls
.
Conn
rawConn
net
.
Conn
}
func
(
c
*
tlsClientCon
)
CloseWrite
()
error
{
// Go standard tls.Conn doesn't provide the CloseWrite() method so we do it
// on its underlying connection.
if
cwc
,
ok
:=
c
.
rawConn
.
(
interface
{
CloseWrite
()
error
});
ok
{
return
cwc
.
CloseWrite
()
}
return
nil
}
func
tlsDialWithDialer
(
dialer
*
net
.
Dialer
,
network
,
addr
string
,
config
*
tls
.
Config
)
(
net
.
Conn
,
error
)
{
// We want the Timeout and Deadline values from dialer to cover the
// whole process: TCP connection and TLS handshake. This means that we
// also need to start our own timers now.
timeout
:=
dialer
.
Timeout
if
!
dialer
.
Deadline
.
IsZero
()
{
deadlineTimeout
:=
dialer
.
Deadline
.
Sub
(
time
.
Now
())
if
timeout
==
0
||
deadlineTimeout
<
timeout
{
timeout
=
deadlineTimeout
}
}
var
errChannel
chan
error
if
timeout
!=
0
{
errChannel
=
make
(
chan
error
,
2
)
time
.
AfterFunc
(
timeout
,
func
()
{
errChannel
<-
errors
.
New
(
""
)
})
}
rawConn
,
err
:=
dialer
.
Dial
(
network
,
addr
)
if
err
!=
nil
{
return
nil
,
err
}
colonPos
:=
strings
.
LastIndex
(
addr
,
":"
)
if
colonPos
==
-
1
{
colonPos
=
len
(
addr
)
}
hostname
:=
addr
[
:
colonPos
]
// If no ServerName is set, infer the ServerName
// from the hostname we're connecting to.
if
config
.
ServerName
==
""
{
// Make a copy to avoid polluting argument or default.
c
:=
*
config
c
.
ServerName
=
hostname
config
=
&
c
}
conn
:=
tls
.
Client
(
rawConn
,
config
)
if
timeout
==
0
{
err
=
conn
.
Handshake
()
}
else
{
go
func
()
{
errChannel
<-
conn
.
Handshake
()
}()
err
=
<-
errChannel
}
if
err
!=
nil
{
rawConn
.
Close
()
return
nil
,
err
}
// This is Docker difference with standard's crypto/tls package: returned a
// wrapper which holds both the TLS and raw connections.
return
&
tlsClientCon
{
conn
,
rawConn
},
nil
}
func
tlsDial
(
network
,
addr
string
,
config
*
tls
.
Config
)
(
net
.
Conn
,
error
)
{
return
tlsDialWithDialer
(
new
(
net
.
Dialer
),
network
,
addr
,
config
)
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment