mirror of
https://github.com/docker/cli.git
synced 2026-11-12 15:53:52 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb310fca49 | ||
|
|
0e40b91921 | ||
|
|
a51e9e6397 | ||
|
|
3ff19c3755 | ||
|
|
e94a87225a | ||
|
|
7c803f7c34 | ||
|
|
4591b13318 | ||
|
|
f9a5c1f152 | ||
|
|
829843333b | ||
|
|
f0e74408cf | ||
|
|
a4bedce165 | ||
|
|
d4755cfbd5 | ||
|
|
6eef644faf | ||
|
|
f47f8e333b | ||
|
|
64838b6907 | ||
|
|
c8308ecf6c | ||
|
|
793c75015e | ||
|
|
776e181628 | ||
|
|
7fac7eb1af | ||
|
|
27f0612665 | ||
|
|
d5ce81172f | ||
|
|
3a1d0c67ff | ||
|
|
e1c31c8910 | ||
|
|
2d6231ad4d | ||
|
|
be60297514 | ||
|
|
528b959ebc | ||
|
|
b668f31022 | ||
|
|
7bfe3d2006 | ||
|
|
f6ec0a368e | ||
|
|
56324e6348 | ||
|
|
040920f428 | ||
|
|
5908cdf87c | ||
|
|
3179a3b1e5 | ||
|
|
4d630fe843 | ||
|
|
9b02509a69 | ||
|
|
696605c99b | ||
|
|
25d53feb65 | ||
|
|
1d540a50d0 | ||
|
|
16d5974584 | ||
|
|
3694e78a56 | ||
|
|
998d8235c3 | ||
|
|
a3e131b323 | ||
|
|
7a9a020688 | ||
|
|
39676cc33e | ||
|
|
4b4e4c2504 | ||
|
|
66905dcff8 | ||
|
|
05aa524a7b | ||
|
|
3f39587e3b | ||
|
|
e22f088abb | ||
|
|
ae7bb665e7 | ||
|
|
f0bf0d2c46 | ||
|
|
5761759e43 | ||
|
|
e669be6a1b | ||
|
|
8291738733 | ||
|
|
a42f8129fd | ||
|
|
41b01f1319 | ||
|
|
a1c4a0f9e8 | ||
|
|
ab4a5cc0f7 | ||
|
|
a7cad2fec2 | ||
|
|
e6e0fa0c8f | ||
|
|
813cc7ed9c | ||
|
|
2c7de2070e | ||
|
|
3d91ba436c | ||
|
|
b5e0dca08d | ||
|
|
7374c0a152 | ||
|
|
e70edc3576 | ||
|
|
b320feff9b | ||
|
|
73dad4356e | ||
|
|
201e44907d | ||
|
|
1b35214c11 | ||
|
|
fccc105d4d | ||
|
|
37ef1f56f7 | ||
|
|
65e0d7c562 | ||
|
|
09c94458c7 | ||
|
|
be5a3ccb7d | ||
|
|
b083f625e4 | ||
|
|
f7b23cf572 | ||
|
|
bda413e0ae | ||
|
|
0cab2689a5 | ||
|
|
42ad2446e0 | ||
|
|
ae7b1f737b | ||
|
|
a087595fb6 | ||
|
|
43b498829b | ||
|
|
bd778fc27b | ||
|
|
10fda078a0 | ||
|
|
49253c7b00 | ||
|
|
98150ae8ac | ||
|
|
fbe24f3e54 | ||
|
|
50a23edef3 | ||
|
|
3bfa0f36b2 | ||
|
|
acec6cb56f | ||
|
|
f969531205 | ||
|
|
c10bf24fb2 | ||
|
|
1a57296c26 | ||
|
|
7467a6a7de | ||
|
|
98d2661522 | ||
|
|
44c8cec274 | ||
|
|
b2f6b4902c | ||
|
|
2441946ce7 | ||
|
|
03dbef8931 | ||
|
|
315715e145 | ||
|
|
c749704b83 | ||
|
|
b49a07b63d | ||
|
|
c77f5255a4 | ||
|
|
67a7fe0b3b | ||
|
|
9ac043bc9d | ||
|
|
7db8c83e3a | ||
|
|
a1f67cc029 | ||
|
|
a3912a4713 | ||
|
|
20ae2a6c4e | ||
|
|
9c7b701de5 | ||
|
|
6f91e46b7b | ||
|
|
1f3468bb77 | ||
|
|
c8c14206aa | ||
|
|
7d9b95d7ec | ||
|
|
0f72909216 | ||
|
|
66b760d2b1 | ||
|
|
c83334119c | ||
|
|
cdc9d1095b | ||
|
|
fe1a85f1e9 | ||
|
|
60aa890a06 | ||
|
|
49ff9c82db | ||
|
|
1ed4e67b34 | ||
|
|
ad7bd8235f | ||
|
|
b71d5e32cd | ||
|
|
3bc713c157 | ||
|
|
d85a3b265c | ||
|
|
08b5f63b0e | ||
|
|
c2d4bd4ec2 | ||
|
|
2fa69baf8d | ||
|
|
e3657b0a8c | ||
|
|
99c5edceb4 | ||
|
|
ad6c667034 | ||
|
|
5cef8fc8e5 | ||
|
|
9597e681cb | ||
|
|
c050ae7fb1 | ||
|
|
aa45ea366c | ||
|
|
df5c7130df | ||
|
|
a90e5598a4 | ||
|
|
66f8d279c1 | ||
|
|
c4e5fd7e53 | ||
|
|
149f69ca06 | ||
|
|
79f5dbcd55 | ||
|
|
137b3cc89e | ||
|
|
57332bcea3 | ||
|
|
2fe281d170 | ||
|
|
69f0132397 | ||
|
|
9447d7431b | ||
|
|
c70ce79adc | ||
|
|
667bd9a1b8 | ||
|
|
6c66c799c7 | ||
|
|
8a9e86c728 | ||
|
|
e176053a3f | ||
|
|
f38bea4ac3 | ||
|
|
379470969b | ||
|
|
2355349d8b | ||
|
|
73d513e56a | ||
|
|
9fd5604d80 | ||
|
|
82ac89901f | ||
|
|
29e3a70b04 | ||
|
|
3fb239756d | ||
|
|
ff72c27a7a | ||
|
|
ed54e85144 | ||
|
|
0bc3d1fd2d | ||
|
|
50bb8c70f3 | ||
|
|
25168137bd | ||
|
|
3af5c3f8c6 | ||
|
|
3143ba5700 | ||
|
|
a89484e338 | ||
|
|
2ac9213069 | ||
|
|
578ab52ece | ||
|
|
c8e9c04d19 | ||
|
|
2fead2a50f | ||
|
|
df1fe15cf6 | ||
|
|
be9adbd5c1 | ||
|
|
2907276eca | ||
|
|
59b02c04bf | ||
|
|
6a3eb417d5 | ||
|
|
c30ccb308d | ||
|
|
1572845a2f | ||
|
|
caad34cf58 | ||
|
|
bf683dfe52 | ||
|
|
307befd7e2 | ||
|
|
b58270ba69 | ||
|
|
0ecfcb5997 | ||
|
|
0ea69840c6 | ||
|
|
208de55a17 | ||
|
|
1a8077b814 | ||
|
|
fa0e2597e6 | ||
|
|
f357def036 | ||
|
|
792ce891be | ||
|
|
d473c60571 | ||
|
|
b020a36d10 | ||
|
|
d2e8ff9e20 | ||
|
|
10a899b6bd | ||
|
|
41718b98f6 | ||
|
|
caf21526a0 | ||
|
|
5b38d82aa0 | ||
|
|
e303dfb6fd | ||
|
|
94b98bfa21 | ||
|
|
87e400e44e | ||
|
|
8cb2456248 | ||
|
|
11b15544c5 | ||
|
|
344adac7a6 | ||
|
|
2027d17a9d | ||
|
|
ff881608fb | ||
|
|
8947ee2709 | ||
|
|
2f1931f9eb | ||
|
|
8164090257 | ||
|
|
e803e487c3 | ||
|
|
5b636878fc | ||
|
|
3bc3f0390e | ||
|
|
296e10c0c5 | ||
|
|
a63faebcf1 | ||
|
|
49236a4391 | ||
|
|
17b3250f0f | ||
|
|
5d246f4998 | ||
|
|
f913afa98c | ||
|
|
90f256aeab | ||
|
|
ee10970b05 | ||
|
|
35c929ed5e | ||
|
|
60eb4ceaf7 | ||
|
|
1b15368c47 | ||
|
|
a720cf572f | ||
|
|
ec7a9ad6e4 | ||
|
|
5e413159e5 | ||
|
|
d4226d2f73 | ||
|
|
a7c10adf4e | ||
|
|
a4f41d94db | ||
|
|
71e1883ca0 | ||
|
|
06eb05570a | ||
|
|
a1b83ffd2c | ||
|
|
649097ffe0 | ||
|
|
57f1de13b3 | ||
|
|
c5431132d7 | ||
|
|
c66cebee7a | ||
|
|
c105a58f65 | ||
|
|
545fd2ad76 | ||
|
|
315f7d7d04 | ||
|
|
6aedc5e912 | ||
|
|
3ac398aa49 | ||
|
|
781c427788 | ||
|
|
47e66c5812 | ||
|
|
9933222452 | ||
|
|
3f5553548b | ||
|
|
c8273616ee | ||
|
|
8aebc31806 | ||
|
|
57ef4e32f4 | ||
|
|
c15fb3a8e5 | ||
|
|
cb07256868 | ||
|
|
5ec13f81a2 | ||
|
|
394c393998 | ||
|
|
a4ba5831a0 | ||
|
|
ac45214f7d | ||
|
|
12a1cf4783 | ||
|
|
7fd21aefd8 | ||
|
|
3f9063e775 | ||
|
|
8758cdca10 | ||
|
|
529b1e7ec7 | ||
|
|
b8bfba8dc6 | ||
|
|
d6ddcdfa6a | ||
|
|
7380aae601 | ||
|
|
6a6cd35985 | ||
|
|
941a493f49 | ||
|
|
1e275568f1 | ||
|
|
2a78b4e9a3 | ||
|
|
8cf8fc27fa | ||
|
|
68d67f2cbf | ||
|
|
c1754d9e5d | ||
|
|
af9b8c1be3 | ||
|
|
292fc5c580 | ||
|
|
11f5e33a90 |
+5
-1
@@ -1,2 +1,6 @@
|
|||||||
|
.dockerignore
|
||||||
.git
|
.git
|
||||||
build
|
.gitignore
|
||||||
|
appveyor.yml
|
||||||
|
build
|
||||||
|
circle.yml
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- bodyclose
|
||||||
|
- deadcode
|
||||||
|
- dogsled
|
||||||
|
- gocyclo
|
||||||
|
- goimports
|
||||||
|
- golint
|
||||||
|
- gosec
|
||||||
|
- gosimple
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- interfacer
|
||||||
|
- lll
|
||||||
|
- megacheck
|
||||||
|
- misspell
|
||||||
|
- nakedret
|
||||||
|
- staticcheck
|
||||||
|
- structcheck
|
||||||
|
- typecheck
|
||||||
|
- unconvert
|
||||||
|
- unparam
|
||||||
|
- unused
|
||||||
|
- varcheck
|
||||||
|
|
||||||
|
disable:
|
||||||
|
- errcheck
|
||||||
|
|
||||||
|
run:
|
||||||
|
timeout: 5m
|
||||||
|
skip-dirs:
|
||||||
|
- cli/command/stack/kubernetes/api/openapi
|
||||||
|
- cli/command/stack/kubernetes/api/client
|
||||||
|
skip-files:
|
||||||
|
- cli/compose/schema/bindata.go
|
||||||
|
- .*generated.*
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
gocyclo:
|
||||||
|
min-complexity: 16
|
||||||
|
govet:
|
||||||
|
check-shadowing: false
|
||||||
|
lll:
|
||||||
|
line-length: 200
|
||||||
|
nakedret:
|
||||||
|
command: nakedret
|
||||||
|
pattern: ^(?P<path>.*?\\.go):(?P<line>\\d+)\\s*(?P<message>.*)$
|
||||||
|
|
||||||
|
issues:
|
||||||
|
# The default exclusion rules are a bit too permissive, so copying the relevant ones below
|
||||||
|
exclude-use-default: false
|
||||||
|
|
||||||
|
exclude:
|
||||||
|
- parameter .* always receives
|
||||||
|
|
||||||
|
exclude-rules:
|
||||||
|
# These are copied from the default exclude rules, except for "ineffective break statement"
|
||||||
|
# and GoDoc checks.
|
||||||
|
# https://github.com/golangci/golangci-lint/blob/0cc87df732aaf1d5ad9ce9ca538d38d916918b36/pkg/config/config.go#L36
|
||||||
|
- text: "Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*printf?|os\\.(Un)?Setenv). is not checked"
|
||||||
|
linters:
|
||||||
|
- errcheck
|
||||||
|
- text: "func name will be used as test\\.Test.* by other packages, and that stutters; consider calling this"
|
||||||
|
linters:
|
||||||
|
- golint
|
||||||
|
- text: "G103: Use of unsafe calls should be audited"
|
||||||
|
linters:
|
||||||
|
- gosec
|
||||||
|
- text: "G104: Errors unhandled"
|
||||||
|
linters:
|
||||||
|
- gosec
|
||||||
|
- text: "G204: Subprocess launch(ed with (variable|function call)|ing should be audited)"
|
||||||
|
linters:
|
||||||
|
- gosec
|
||||||
|
- text: "(G301|G302): (Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less)"
|
||||||
|
linters:
|
||||||
|
- gosec
|
||||||
|
- text: "G304: Potential file inclusion via variable"
|
||||||
|
linters:
|
||||||
|
- gosec
|
||||||
|
- text: "(G201|G202): SQL string (formatting|concatenation)"
|
||||||
|
linters:
|
||||||
|
- gosec
|
||||||
@@ -44,6 +44,7 @@ Antonio Murdaca <antonio.murdaca@gmail.com> <runcom@users.noreply.github.com>
|
|||||||
Anuj Bahuguna <anujbahuguna.dev@gmail.com>
|
Anuj Bahuguna <anujbahuguna.dev@gmail.com>
|
||||||
Anuj Bahuguna <anujbahuguna.dev@gmail.com> <abahuguna@fiberlink.com>
|
Anuj Bahuguna <anujbahuguna.dev@gmail.com> <abahuguna@fiberlink.com>
|
||||||
Anusha Ragunathan <anusha.ragunathan@docker.com> <anusha@docker.com>
|
Anusha Ragunathan <anusha.ragunathan@docker.com> <anusha@docker.com>
|
||||||
|
Ao Li <la9249@163.com>
|
||||||
Arnaud Porterie <arnaud.porterie@docker.com>
|
Arnaud Porterie <arnaud.porterie@docker.com>
|
||||||
Arnaud Porterie <arnaud.porterie@docker.com> <icecrime@gmail.com>
|
Arnaud Porterie <arnaud.porterie@docker.com> <icecrime@gmail.com>
|
||||||
Arthur Gautier <baloo@gandi.net> <superbaloo+registrations.github@superbaloo.net>
|
Arthur Gautier <baloo@gandi.net> <superbaloo+registrations.github@superbaloo.net>
|
||||||
@@ -394,6 +395,8 @@ Stefan Berger <stefanb@linux.vnet.ibm.com>
|
|||||||
Stefan Berger <stefanb@linux.vnet.ibm.com> <stefanb@us.ibm.com>
|
Stefan Berger <stefanb@linux.vnet.ibm.com> <stefanb@us.ibm.com>
|
||||||
Stefan J. Wernli <swernli@microsoft.com> <swernli@ntdev.microsoft.com>
|
Stefan J. Wernli <swernli@microsoft.com> <swernli@ntdev.microsoft.com>
|
||||||
Stefan S. <tronicum@user.github.com>
|
Stefan S. <tronicum@user.github.com>
|
||||||
|
Stefan Scherer <stefan.scherer@docker.com>
|
||||||
|
Stefan Scherer <stefan.scherer@docker.com> <scherer_stefan@icloud.com>
|
||||||
Stephen Day <stevvooe@gmail.com>
|
Stephen Day <stevvooe@gmail.com>
|
||||||
Stephen Day <stevvooe@gmail.com> <stephen.day@docker.com>
|
Stephen Day <stevvooe@gmail.com> <stephen.day@docker.com>
|
||||||
Stephen Day <stevvooe@gmail.com> <stevvooe@users.noreply.github.com>
|
Stephen Day <stevvooe@gmail.com> <stevvooe@users.noreply.github.com>
|
||||||
@@ -402,6 +405,8 @@ Steve Richards <steve.richards@docker.com> stevejr <>
|
|||||||
Sun Gengze <690388648@qq.com>
|
Sun Gengze <690388648@qq.com>
|
||||||
Sun Jianbo <wonderflow.sun@gmail.com>
|
Sun Jianbo <wonderflow.sun@gmail.com>
|
||||||
Sun Jianbo <wonderflow.sun@gmail.com> <wonderflow@zju.edu.cn>
|
Sun Jianbo <wonderflow.sun@gmail.com> <wonderflow@zju.edu.cn>
|
||||||
|
Sunny Gogoi <indiasuny000@gmail.com>
|
||||||
|
Sunny Gogoi <indiasuny000@gmail.com> <me@darkowlzz.space>
|
||||||
Sven Dowideit <SvenDowideit@home.org.au>
|
Sven Dowideit <SvenDowideit@home.org.au>
|
||||||
Sven Dowideit <SvenDowideit@home.org.au> <sven@t440s.home.gateway>
|
Sven Dowideit <SvenDowideit@home.org.au> <sven@t440s.home.gateway>
|
||||||
Sven Dowideit <SvenDowideit@home.org.au> <SvenDowideit@docker.com>
|
Sven Dowideit <SvenDowideit@home.org.au> <SvenDowideit@docker.com>
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ Anton Polonskiy <anton.polonskiy@gmail.com>
|
|||||||
Antonio Murdaca <antonio.murdaca@gmail.com>
|
Antonio Murdaca <antonio.murdaca@gmail.com>
|
||||||
Antonis Kalipetis <akalipetis@gmail.com>
|
Antonis Kalipetis <akalipetis@gmail.com>
|
||||||
Anusha Ragunathan <anusha.ragunathan@docker.com>
|
Anusha Ragunathan <anusha.ragunathan@docker.com>
|
||||||
|
Ao Li <la9249@163.com>
|
||||||
Arash Deshmeh <adeshmeh@ca.ibm.com>
|
Arash Deshmeh <adeshmeh@ca.ibm.com>
|
||||||
Arnaud Porterie <arnaud.porterie@docker.com>
|
Arnaud Porterie <arnaud.porterie@docker.com>
|
||||||
Ashwini Oruganti <ashwini.oruganti@gmail.com>
|
Ashwini Oruganti <ashwini.oruganti@gmail.com>
|
||||||
@@ -158,6 +159,7 @@ David Cramer <davcrame@cisco.com>
|
|||||||
David Dooling <dooling@gmail.com>
|
David Dooling <dooling@gmail.com>
|
||||||
David Gageot <david@gageot.net>
|
David Gageot <david@gageot.net>
|
||||||
David Lechner <david@lechnology.com>
|
David Lechner <david@lechnology.com>
|
||||||
|
David Scott <dave@recoil.org>
|
||||||
David Sheets <dsheets@docker.com>
|
David Sheets <dsheets@docker.com>
|
||||||
David Williamson <david.williamson@docker.com>
|
David Williamson <david.williamson@docker.com>
|
||||||
David Xia <dxia@spotify.com>
|
David Xia <dxia@spotify.com>
|
||||||
@@ -300,6 +302,7 @@ Jim Galasyn <jim.galasyn@docker.com>
|
|||||||
Jimmy Leger <jimmy.leger@gmail.com>
|
Jimmy Leger <jimmy.leger@gmail.com>
|
||||||
Jimmy Song <rootsongjc@gmail.com>
|
Jimmy Song <rootsongjc@gmail.com>
|
||||||
jimmyxian <jimmyxian2004@yahoo.com.cn>
|
jimmyxian <jimmyxian2004@yahoo.com.cn>
|
||||||
|
Jintao Zhang <zhangjintao9020@gmail.com>
|
||||||
Joao Fernandes <joao.fernandes@docker.com>
|
Joao Fernandes <joao.fernandes@docker.com>
|
||||||
Joe Doliner <jdoliner@pachyderm.io>
|
Joe Doliner <jdoliner@pachyderm.io>
|
||||||
Joe Gordon <joe.gordon0@gmail.com>
|
Joe Gordon <joe.gordon0@gmail.com>
|
||||||
@@ -471,9 +474,11 @@ Mrunal Patel <mrunalp@gmail.com>
|
|||||||
muicoder <muicoder@gmail.com>
|
muicoder <muicoder@gmail.com>
|
||||||
Muthukumar R <muthur@gmail.com>
|
Muthukumar R <muthur@gmail.com>
|
||||||
Máximo Cuadros <mcuadros@gmail.com>
|
Máximo Cuadros <mcuadros@gmail.com>
|
||||||
|
Mårten Cassel <marten.cassel@gmail.com>
|
||||||
Nace Oroz <orkica@gmail.com>
|
Nace Oroz <orkica@gmail.com>
|
||||||
Nahum Shalman <nshalman@omniti.com>
|
Nahum Shalman <nshalman@omniti.com>
|
||||||
Nalin Dahyabhai <nalin@redhat.com>
|
Nalin Dahyabhai <nalin@redhat.com>
|
||||||
|
Nao YONASHIRO <owan.orisano@gmail.com>
|
||||||
Nassim 'Nass' Eddequiouaq <eddequiouaq.nassim@gmail.com>
|
Nassim 'Nass' Eddequiouaq <eddequiouaq.nassim@gmail.com>
|
||||||
Natalie Parker <nparker@omnifone.com>
|
Natalie Parker <nparker@omnifone.com>
|
||||||
Nate Brennand <nate.brennand@clever.com>
|
Nate Brennand <nate.brennand@clever.com>
|
||||||
@@ -595,7 +600,7 @@ Spencer Brown <spencer@spencerbrown.org>
|
|||||||
squeegels <1674195+squeegels@users.noreply.github.com>
|
squeegels <1674195+squeegels@users.noreply.github.com>
|
||||||
Srini Brahmaroutu <srbrahma@us.ibm.com>
|
Srini Brahmaroutu <srbrahma@us.ibm.com>
|
||||||
Stefan S. <tronicum@user.github.com>
|
Stefan S. <tronicum@user.github.com>
|
||||||
Stefan Scherer <scherer_stefan@icloud.com>
|
Stefan Scherer <stefan.scherer@docker.com>
|
||||||
Stefan Weil <sw@weilnetz.de>
|
Stefan Weil <sw@weilnetz.de>
|
||||||
Stephane Jeandeaux <stephane.jeandeaux@gmail.com>
|
Stephane Jeandeaux <stephane.jeandeaux@gmail.com>
|
||||||
Stephen Day <stevvooe@gmail.com>
|
Stephen Day <stevvooe@gmail.com>
|
||||||
@@ -605,7 +610,9 @@ Steve Richards <steve.richards@docker.com>
|
|||||||
Steven Burgess <steven.a.burgess@hotmail.com>
|
Steven Burgess <steven.a.burgess@hotmail.com>
|
||||||
Subhajit Ghosh <isubuz.g@gmail.com>
|
Subhajit Ghosh <isubuz.g@gmail.com>
|
||||||
Sun Jianbo <wonderflow.sun@gmail.com>
|
Sun Jianbo <wonderflow.sun@gmail.com>
|
||||||
|
Sune Keller <absukl@almbrand.dk>
|
||||||
Sungwon Han <sungwon.han@navercorp.com>
|
Sungwon Han <sungwon.han@navercorp.com>
|
||||||
|
Sunny Gogoi <indiasuny000@gmail.com>
|
||||||
Sven Dowideit <SvenDowideit@home.org.au>
|
Sven Dowideit <SvenDowideit@home.org.au>
|
||||||
Sylvain Baubeau <sbaubeau@redhat.com>
|
Sylvain Baubeau <sbaubeau@redhat.com>
|
||||||
Sébastien HOUZÉ <cto@verylastroom.com>
|
Sébastien HOUZÉ <cto@verylastroom.com>
|
||||||
|
|||||||
Vendored
+2
-1
@@ -5,8 +5,9 @@ wrappedNode(label: 'linux && x86_64', cleanWorkspace: true) {
|
|||||||
|
|
||||||
stage "Run end-to-end test suite"
|
stage "Run end-to-end test suite"
|
||||||
sh "docker version"
|
sh "docker version"
|
||||||
|
sh "docker info"
|
||||||
sh "E2E_UNIQUE_ID=clie2e${BUILD_NUMBER} \
|
sh "E2E_UNIQUE_ID=clie2e${BUILD_NUMBER} \
|
||||||
IMAGE_TAG=clie2e${BUILD_NUMBER} \
|
IMAGE_TAG=clie2e${BUILD_NUMBER} \
|
||||||
make -f docker.Makefile test-e2e"
|
DOCKER_BUILDKIT=1 make -f docker.Makefile test-e2e"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
[](https://circleci.com/gh/docker/cli/tree/master) [](https://jenkins.dockerproject.org/job/docker/job/cli/job/master/)
|
[](https://circleci.com/gh/docker/cli/tree/master)
|
||||||
|
[](https://ci.docker.com/public/job/cli/job/master)
|
||||||
|
|
||||||
docker/cli
|
docker/cli
|
||||||
==========
|
==========
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ clone_folder: c:\gopath\src\github.com\docker\cli
|
|||||||
|
|
||||||
environment:
|
environment:
|
||||||
GOPATH: c:\gopath
|
GOPATH: c:\gopath
|
||||||
GOVERSION: 1.12.4
|
GOVERSION: 1.12.17
|
||||||
DEPVERSION: v0.4.1
|
DEPVERSION: v0.4.1
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
|||||||
+76
-38
@@ -4,35 +4,64 @@ jobs:
|
|||||||
|
|
||||||
lint:
|
lint:
|
||||||
working_directory: /work
|
working_directory: /work
|
||||||
docker: [{image: 'docker:18.03-git'}]
|
docker: [{image: 'docker:18.09-git'}]
|
||||||
|
environment:
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- setup_remote_docker:
|
- setup_remote_docker:
|
||||||
version: 18.03.1-ce
|
version: 18.09.3
|
||||||
reusable: true
|
reusable: true
|
||||||
exclusive: false
|
exclusive: false
|
||||||
- run:
|
- run:
|
||||||
|
name: "Docker version"
|
||||||
command: docker version
|
command: docker version
|
||||||
|
- run:
|
||||||
|
name: "Docker info"
|
||||||
|
command: docker info
|
||||||
|
- run:
|
||||||
|
name: "Shellcheck - build image"
|
||||||
|
command: |
|
||||||
|
docker build --progress=plain -f dockerfiles/Dockerfile.shellcheck --tag cli-validator:$CIRCLE_BUILD_NUM .
|
||||||
|
- run:
|
||||||
|
name: "Shellcheck"
|
||||||
|
command: |
|
||||||
|
docker run --rm cli-validator:$CIRCLE_BUILD_NUM \
|
||||||
|
make shellcheck
|
||||||
|
- run:
|
||||||
|
name: "Lint - build image"
|
||||||
|
command: |
|
||||||
|
docker build --progress=plain -f dockerfiles/Dockerfile.lint --tag cli-linter:$CIRCLE_BUILD_NUM .
|
||||||
- run:
|
- run:
|
||||||
name: "Lint"
|
name: "Lint"
|
||||||
command: |
|
command: |
|
||||||
docker build -f dockerfiles/Dockerfile.lint --tag cli-linter:$CIRCLE_BUILD_NUM .
|
|
||||||
docker run --rm cli-linter:$CIRCLE_BUILD_NUM
|
docker run --rm cli-linter:$CIRCLE_BUILD_NUM
|
||||||
|
|
||||||
cross:
|
cross:
|
||||||
working_directory: /work
|
working_directory: /work
|
||||||
docker: [{image: 'docker:18.03-git'}]
|
docker: [{image: 'docker:18.09-git'}]
|
||||||
|
environment:
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
parallelism: 3
|
parallelism: 3
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- setup_remote_docker:
|
- setup_remote_docker:
|
||||||
version: 18.03.1-ce
|
version: 18.09.3
|
||||||
reusable: true
|
reusable: true
|
||||||
exclusive: false
|
exclusive: false
|
||||||
|
- run:
|
||||||
|
name: "Docker version"
|
||||||
|
command: docker version
|
||||||
|
- run:
|
||||||
|
name: "Docker info"
|
||||||
|
command: docker info
|
||||||
|
- run:
|
||||||
|
name: "Cross - build image"
|
||||||
|
command: |
|
||||||
|
docker build --progress=plain -f dockerfiles/Dockerfile.cross --tag cli-builder:$CIRCLE_BUILD_NUM .
|
||||||
- run:
|
- run:
|
||||||
name: "Cross"
|
name: "Cross"
|
||||||
command: |
|
command: |
|
||||||
docker build -f dockerfiles/Dockerfile.cross --tag cli-builder:$CIRCLE_BUILD_NUM .
|
|
||||||
name=cross-$CIRCLE_BUILD_NUM-$CIRCLE_NODE_INDEX
|
name=cross-$CIRCLE_BUILD_NUM-$CIRCLE_NODE_INDEX
|
||||||
docker run \
|
docker run \
|
||||||
-e CROSS_GROUP=$CIRCLE_NODE_INDEX \
|
-e CROSS_GROUP=$CIRCLE_NODE_INDEX \
|
||||||
@@ -46,18 +75,29 @@ jobs:
|
|||||||
|
|
||||||
test:
|
test:
|
||||||
working_directory: /work
|
working_directory: /work
|
||||||
docker: [{image: 'docker:18.03-git'}]
|
docker: [{image: 'docker:18.09-git'}]
|
||||||
|
environment:
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- setup_remote_docker:
|
- setup_remote_docker:
|
||||||
version: 18.03.1-ce
|
version: 18.09.3
|
||||||
reusable: true
|
reusable: true
|
||||||
exclusive: false
|
exclusive: false
|
||||||
|
- run:
|
||||||
|
name: "Docker version"
|
||||||
|
command: docker version
|
||||||
|
- run:
|
||||||
|
name: "Docker info"
|
||||||
|
command: docker info
|
||||||
|
- run:
|
||||||
|
name: "Unit Test with Coverage - build image"
|
||||||
|
command: |
|
||||||
|
mkdir -p test-results/unit-tests
|
||||||
|
docker build --progress=plain -f dockerfiles/Dockerfile.dev --tag cli-builder:$CIRCLE_BUILD_NUM .
|
||||||
- run:
|
- run:
|
||||||
name: "Unit Test with Coverage"
|
name: "Unit Test with Coverage"
|
||||||
command: |
|
command: |
|
||||||
mkdir -p test-results/unit-tests
|
|
||||||
docker build -f dockerfiles/Dockerfile.dev --tag cli-builder:$CIRCLE_BUILD_NUM .
|
|
||||||
docker run \
|
docker run \
|
||||||
-e GOTESTSUM_JUNITFILE=/tmp/junit.xml \
|
-e GOTESTSUM_JUNITFILE=/tmp/junit.xml \
|
||||||
--name \
|
--name \
|
||||||
@@ -77,39 +117,38 @@ jobs:
|
|||||||
echo 'Codecov failed to upload'
|
echo 'Codecov failed to upload'
|
||||||
- store_test_results:
|
- store_test_results:
|
||||||
path: test-results
|
path: test-results
|
||||||
|
- store_artifacts:
|
||||||
|
path: test-results
|
||||||
|
|
||||||
validate:
|
validate:
|
||||||
working_directory: /work
|
working_directory: /work
|
||||||
docker: [{image: 'docker:18.03-git'}]
|
docker: [{image: 'docker:18.09-git'}]
|
||||||
|
environment:
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- setup_remote_docker:
|
- setup_remote_docker:
|
||||||
version: 18.03.1-ce
|
version: 18.09.3
|
||||||
reusable: true
|
reusable: true
|
||||||
exclusive: false
|
exclusive: false
|
||||||
|
- run:
|
||||||
|
name: "Docker version"
|
||||||
|
command: docker version
|
||||||
|
- run:
|
||||||
|
name: "Docker info"
|
||||||
|
command: docker info
|
||||||
|
- run:
|
||||||
|
name: "Validate - build image"
|
||||||
|
command: |
|
||||||
|
rm -f .dockerignore # include .git
|
||||||
|
docker build --progress=plain -f dockerfiles/Dockerfile.dev --tag cli-builder-with-git:$CIRCLE_BUILD_NUM .
|
||||||
- run:
|
- run:
|
||||||
name: "Validate Vendor, Docs, and Code Generation"
|
name: "Validate Vendor, Docs, and Code Generation"
|
||||||
command: |
|
command: |
|
||||||
rm -f .dockerignore # include .git
|
|
||||||
docker build -f dockerfiles/Dockerfile.dev --tag cli-builder-with-git:$CIRCLE_BUILD_NUM .
|
|
||||||
docker run --rm cli-builder-with-git:$CIRCLE_BUILD_NUM \
|
docker run --rm cli-builder-with-git:$CIRCLE_BUILD_NUM \
|
||||||
make ci-validate
|
make ci-validate
|
||||||
no_output_timeout: 15m
|
no_output_timeout: 15m
|
||||||
shellcheck:
|
|
||||||
working_directory: /work
|
|
||||||
docker: [{image: 'docker:18.03-git'}]
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- setup_remote_docker:
|
|
||||||
version: 18.03.1-ce
|
|
||||||
reusable: true
|
|
||||||
exclusive: false
|
|
||||||
- run:
|
|
||||||
name: "Run shellcheck"
|
|
||||||
command: |
|
|
||||||
docker build -f dockerfiles/Dockerfile.shellcheck --tag cli-validator:$CIRCLE_BUILD_NUM .
|
|
||||||
docker run --rm cli-validator:$CIRCLE_BUILD_NUM \
|
|
||||||
make shellcheck
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
ci:
|
ci:
|
||||||
@@ -118,4 +157,3 @@ workflows:
|
|||||||
- cross
|
- cross
|
||||||
- test
|
- test
|
||||||
- validate
|
- validate
|
||||||
- shellcheck
|
|
||||||
|
|||||||
@@ -101,5 +101,6 @@ func main() {
|
|||||||
SchemaVersion: "0.1.0",
|
SchemaVersion: "0.1.0",
|
||||||
Vendor: "Docker Inc.",
|
Vendor: "Docker Inc.",
|
||||||
Version: "testing",
|
Version: "testing",
|
||||||
|
Experimental: os.Getenv("HELLO_EXPERIMENTAL") != "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type fakeCandidate struct {
|
type fakeCandidate struct {
|
||||||
path string
|
path string
|
||||||
exec bool
|
exec bool
|
||||||
meta string
|
meta string
|
||||||
|
allowExperimental bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeCandidate) Path() string {
|
func (c *fakeCandidate) Path() string {
|
||||||
@@ -35,9 +36,10 @@ func TestValidateCandidate(t *testing.T) {
|
|||||||
builtinName = NamePrefix + "builtin"
|
builtinName = NamePrefix + "builtin"
|
||||||
builtinAlias = NamePrefix + "alias"
|
builtinAlias = NamePrefix + "alias"
|
||||||
|
|
||||||
badPrefixPath = "/usr/local/libexec/cli-plugins/wobble"
|
badPrefixPath = "/usr/local/libexec/cli-plugins/wobble"
|
||||||
badNamePath = "/usr/local/libexec/cli-plugins/docker-123456"
|
badNamePath = "/usr/local/libexec/cli-plugins/docker-123456"
|
||||||
goodPluginPath = "/usr/local/libexec/cli-plugins/" + goodPluginName
|
goodPluginPath = "/usr/local/libexec/cli-plugins/" + goodPluginName
|
||||||
|
metaExperimental = `{"SchemaVersion": "0.1.0", "Vendor": "e2e-testing", "Experimental": true}`
|
||||||
)
|
)
|
||||||
|
|
||||||
fakeroot := &cobra.Command{Use: "docker"}
|
fakeroot := &cobra.Command{Use: "docker"}
|
||||||
@@ -49,40 +51,46 @@ func TestValidateCandidate(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
c *fakeCandidate
|
name string
|
||||||
|
c *fakeCandidate
|
||||||
|
|
||||||
// Either err or invalid may be non-empty, but not both (both can be empty for a good plugin).
|
// Either err or invalid may be non-empty, but not both (both can be empty for a good plugin).
|
||||||
err string
|
err string
|
||||||
invalid string
|
invalid string
|
||||||
}{
|
}{
|
||||||
/* Each failing one of the tests */
|
/* Each failing one of the tests */
|
||||||
{c: &fakeCandidate{path: ""}, err: "plugin candidate path cannot be empty"},
|
{name: "empty path", c: &fakeCandidate{path: ""}, err: "plugin candidate path cannot be empty"},
|
||||||
{c: &fakeCandidate{path: badPrefixPath}, err: fmt.Sprintf("does not have %q prefix", NamePrefix)},
|
{name: "bad prefix", c: &fakeCandidate{path: badPrefixPath}, err: fmt.Sprintf("does not have %q prefix", NamePrefix)},
|
||||||
{c: &fakeCandidate{path: badNamePath}, invalid: "did not match"},
|
{name: "bad path", c: &fakeCandidate{path: badNamePath}, invalid: "did not match"},
|
||||||
{c: &fakeCandidate{path: builtinName}, invalid: `plugin "builtin" duplicates builtin command`},
|
{name: "builtin command", c: &fakeCandidate{path: builtinName}, invalid: `plugin "builtin" duplicates builtin command`},
|
||||||
{c: &fakeCandidate{path: builtinAlias}, invalid: `plugin "alias" duplicates an alias of builtin command "builtin"`},
|
{name: "builtin alias", c: &fakeCandidate{path: builtinAlias}, invalid: `plugin "alias" duplicates an alias of builtin command "builtin"`},
|
||||||
{c: &fakeCandidate{path: goodPluginPath, exec: false}, invalid: fmt.Sprintf("failed to fetch metadata: faked a failure to exec %q", goodPluginPath)},
|
{name: "fetch failure", c: &fakeCandidate{path: goodPluginPath, exec: false}, invalid: fmt.Sprintf("failed to fetch metadata: faked a failure to exec %q", goodPluginPath)},
|
||||||
{c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `xyzzy`}, invalid: "invalid character"},
|
{name: "metadata not json", c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `xyzzy`}, invalid: "invalid character"},
|
||||||
{c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{}`}, invalid: `plugin SchemaVersion "" is not valid`},
|
{name: "empty schemaversion", c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{}`}, invalid: `plugin SchemaVersion "" is not valid`},
|
||||||
{c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{"SchemaVersion": "xyzzy"}`}, invalid: `plugin SchemaVersion "xyzzy" is not valid`},
|
{name: "invalid schemaversion", c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{"SchemaVersion": "xyzzy"}`}, invalid: `plugin SchemaVersion "xyzzy" is not valid`},
|
||||||
{c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{"SchemaVersion": "0.1.0"}`}, invalid: "plugin metadata does not define a vendor"},
|
{name: "no vendor", c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{"SchemaVersion": "0.1.0"}`}, invalid: "plugin metadata does not define a vendor"},
|
||||||
{c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{"SchemaVersion": "0.1.0", "Vendor": ""}`}, invalid: "plugin metadata does not define a vendor"},
|
{name: "empty vendor", c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{"SchemaVersion": "0.1.0", "Vendor": ""}`}, invalid: "plugin metadata does not define a vendor"},
|
||||||
|
{name: "experimental required", c: &fakeCandidate{path: goodPluginPath, exec: true, meta: metaExperimental}, invalid: "requires experimental CLI"},
|
||||||
// This one should work
|
// This one should work
|
||||||
{c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{"SchemaVersion": "0.1.0", "Vendor": "e2e-testing"}`}},
|
{name: "valid", c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{"SchemaVersion": "0.1.0", "Vendor": "e2e-testing"}`}},
|
||||||
|
{name: "valid + allowing experimental", c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{"SchemaVersion": "0.1.0", "Vendor": "e2e-testing"}`, allowExperimental: true}},
|
||||||
|
{name: "experimental + allowing experimental", c: &fakeCandidate{path: goodPluginPath, exec: true, meta: metaExperimental, allowExperimental: true}},
|
||||||
} {
|
} {
|
||||||
p, err := newPlugin(tc.c, fakeroot)
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
if tc.err != "" {
|
p, err := newPlugin(tc.c, fakeroot, tc.c.allowExperimental)
|
||||||
assert.ErrorContains(t, err, tc.err)
|
if tc.err != "" {
|
||||||
} else if tc.invalid != "" {
|
assert.ErrorContains(t, err, tc.err)
|
||||||
assert.NilError(t, err)
|
} else if tc.invalid != "" {
|
||||||
assert.Assert(t, cmp.ErrorType(p.Err, reflect.TypeOf(&pluginError{})))
|
assert.NilError(t, err)
|
||||||
assert.ErrorContains(t, p.Err, tc.invalid)
|
assert.Assert(t, cmp.ErrorType(p.Err, reflect.TypeOf(&pluginError{})))
|
||||||
} else {
|
assert.ErrorContains(t, p.Err, tc.invalid)
|
||||||
assert.NilError(t, err)
|
} else {
|
||||||
assert.Equal(t, NamePrefix+p.Name, goodPluginName)
|
assert.NilError(t, err)
|
||||||
assert.Equal(t, p.SchemaVersion, "0.1.0")
|
assert.Equal(t, NamePrefix+p.Name, goodPluginName)
|
||||||
assert.Equal(t, p.Vendor, "e2e-testing")
|
assert.Equal(t, p.SchemaVersion, "0.1.0")
|
||||||
}
|
assert.Equal(t, p.Vendor, "e2e-testing")
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package manager
|
package manager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -27,10 +28,23 @@ func (e errPluginNotFound) Error() string {
|
|||||||
return "Error: No such CLI plugin: " + string(e)
|
return "Error: No such CLI plugin: " + string(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type errPluginRequireExperimental string
|
||||||
|
|
||||||
|
// Note: errPluginRequireExperimental implements notFound so that the plugin
|
||||||
|
// is skipped when listing the plugins.
|
||||||
|
func (e errPluginRequireExperimental) NotFound() {}
|
||||||
|
|
||||||
|
func (e errPluginRequireExperimental) Error() string {
|
||||||
|
return fmt.Sprintf("plugin candidate %q: requires experimental CLI", string(e))
|
||||||
|
}
|
||||||
|
|
||||||
type notFound interface{ NotFound() }
|
type notFound interface{ NotFound() }
|
||||||
|
|
||||||
// IsNotFound is true if the given error is due to a plugin not being found.
|
// IsNotFound is true if the given error is due to a plugin not being found.
|
||||||
func IsNotFound(err error) bool {
|
func IsNotFound(err error) bool {
|
||||||
|
if e, ok := err.(*pluginError); ok {
|
||||||
|
err = e.Cause()
|
||||||
|
}
|
||||||
_, ok := err.(notFound)
|
_, ok := err.(notFound)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
@@ -117,12 +131,14 @@ func ListPlugins(dockerCli command.Cli, rootcmd *cobra.Command) ([]Plugin, error
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c := &candidate{paths[0]}
|
c := &candidate{paths[0]}
|
||||||
p, err := newPlugin(c, rootcmd)
|
p, err := newPlugin(c, rootcmd, dockerCli.ClientInfo().HasExperimental)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
p.ShadowedPaths = paths[1:]
|
if !IsNotFound(p.Err) {
|
||||||
plugins = append(plugins, p)
|
p.ShadowedPaths = paths[1:]
|
||||||
|
plugins = append(plugins, p)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return plugins, nil
|
return plugins, nil
|
||||||
@@ -159,12 +175,19 @@ func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
c := &candidate{path: path}
|
c := &candidate{path: path}
|
||||||
plugin, err := newPlugin(c, rootcmd)
|
plugin, err := newPlugin(c, rootcmd, dockerCli.ClientInfo().HasExperimental)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if plugin.Err != nil {
|
if plugin.Err != nil {
|
||||||
// TODO: why are we not returning plugin.Err?
|
// TODO: why are we not returning plugin.Err?
|
||||||
|
|
||||||
|
err := plugin.Err.(*pluginError).Cause()
|
||||||
|
// if an experimental plugin was invoked directly while experimental mode is off
|
||||||
|
// provide a more useful error message than "not found".
|
||||||
|
if err, ok := err.(errPluginRequireExperimental); ok {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return nil, errPluginNotFound(name)
|
return nil, errPluginNotFound(name)
|
||||||
}
|
}
|
||||||
cmd := exec.Command(plugin.Path, args...)
|
cmd := exec.Command(plugin.Path, args...)
|
||||||
|
|||||||
@@ -22,4 +22,7 @@ type Metadata struct {
|
|||||||
ShortDescription string `json:",omitempty"`
|
ShortDescription string `json:",omitempty"`
|
||||||
// URL is a pointer to the plugin's homepage.
|
// URL is a pointer to the plugin's homepage.
|
||||||
URL string `json:",omitempty"`
|
URL string `json:",omitempty"`
|
||||||
|
// Experimental specifies whether the plugin is experimental.
|
||||||
|
// Experimental plugins are not displayed on non-experimental CLIs.
|
||||||
|
Experimental bool `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ type Plugin struct {
|
|||||||
// is set, and is always a `pluginError`, but the `Plugin` is still
|
// is set, and is always a `pluginError`, but the `Plugin` is still
|
||||||
// returned with no error. An error is only returned due to a
|
// returned with no error. An error is only returned due to a
|
||||||
// non-recoverable error.
|
// non-recoverable error.
|
||||||
func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) {
|
//
|
||||||
|
// nolint: gocyclo
|
||||||
|
func newPlugin(c Candidate, rootcmd *cobra.Command, allowExperimental bool) (Plugin, error) {
|
||||||
path := c.Path()
|
path := c.Path()
|
||||||
if path == "" {
|
if path == "" {
|
||||||
return Plugin{}, errors.New("plugin candidate path cannot be empty")
|
return Plugin{}, errors.New("plugin candidate path cannot be empty")
|
||||||
@@ -94,7 +96,10 @@ func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) {
|
|||||||
p.Err = wrapAsPluginError(err, "invalid metadata")
|
p.Err = wrapAsPluginError(err, "invalid metadata")
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
if p.Experimental && !allowExperimental {
|
||||||
|
p.Err = &pluginError{errPluginRequireExperimental(p.Name)}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
if p.Metadata.SchemaVersion != "0.1.0" {
|
if p.Metadata.SchemaVersion != "0.1.0" {
|
||||||
p.Err = NewPluginError("plugin SchemaVersion %q is not valid, must be 0.1.0", p.Metadata.SchemaVersion)
|
p.Err = NewPluginError("plugin SchemaVersion %q is not valid, must be 0.1.0", p.Metadata.SchemaVersion)
|
||||||
return p, nil
|
return p, nil
|
||||||
|
|||||||
@@ -114,11 +114,14 @@ func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta
|
|||||||
fullname := manager.NamePrefix + name
|
fullname := manager.NamePrefix + name
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: fmt.Sprintf("docker [OPTIONS] %s [ARG...]", name),
|
Use: fmt.Sprintf("docker [OPTIONS] %s [ARG...]", name),
|
||||||
Short: fullname + " is a Docker CLI plugin",
|
Short: fullname + " is a Docker CLI plugin",
|
||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
SilenceErrors: true,
|
SilenceErrors: true,
|
||||||
PersistentPreRunE: PersistentPreRunE,
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// We can't use this as the hook directly since it is initialised later (in runPlugin)
|
||||||
|
return PersistentPreRunE(cmd, args)
|
||||||
|
},
|
||||||
TraverseChildren: true,
|
TraverseChildren: true,
|
||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
|||||||
|
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
|
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
|
||||||
flags.BoolVarP(&options.all, "all", "a", false, "Remove all unused images, not just dangling ones")
|
flags.BoolVarP(&options.all, "all", "a", false, "Remove all unused build cache, not just dangling ones")
|
||||||
flags.Var(&options.filter, "filter", "Provide filter values (e.g. 'unused-for=24h')")
|
flags.Var(&options.filter, "filter", "Provide filter values (e.g. 'until=24h')")
|
||||||
flags.Var(&options.keepStorage, "keep-storage", "Amount of disk space to keep for cache")
|
flags.Var(&options.keepStorage, "keep-storage", "Amount of disk space to keep for cache")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
+22
-10
@@ -14,7 +14,6 @@ import (
|
|||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
dcontext "github.com/docker/cli/cli/context"
|
dcontext "github.com/docker/cli/cli/context"
|
||||||
"github.com/docker/cli/cli/context/docker"
|
"github.com/docker/cli/cli/context/docker"
|
||||||
kubcontext "github.com/docker/cli/cli/context/kubernetes"
|
|
||||||
"github.com/docker/cli/cli/context/store"
|
"github.com/docker/cli/cli/context/store"
|
||||||
"github.com/docker/cli/cli/debug"
|
"github.com/docker/cli/cli/debug"
|
||||||
cliflags "github.com/docker/cli/cli/flags"
|
cliflags "github.com/docker/cli/cli/flags"
|
||||||
@@ -210,11 +209,11 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
|
|||||||
|
|
||||||
cli.configFile = cliconfig.LoadDefaultConfigFile(cli.err)
|
cli.configFile = cliconfig.LoadDefaultConfigFile(cli.err)
|
||||||
|
|
||||||
baseContextSore := store.New(cliconfig.ContextStoreDir(), cli.contextStoreConfig)
|
baseContextStore := store.New(cliconfig.ContextStoreDir(), cli.contextStoreConfig)
|
||||||
cli.contextStore = &ContextStoreWithDefault{
|
cli.contextStore = &ContextStoreWithDefault{
|
||||||
Store: baseContextSore,
|
Store: baseContextStore,
|
||||||
Resolver: func() (*DefaultContext, error) {
|
Resolver: func() (*DefaultContext, error) {
|
||||||
return resolveDefaultContext(opts.Common, cli.ConfigFile(), cli.Err())
|
return ResolveDefaultContext(opts.Common, cli.ConfigFile(), cli.contextStoreConfig, cli.Err())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cli.currentContext, err = resolveContextName(opts.Common, cli.configFile, cli.contextStore)
|
cli.currentContext, err = resolveContextName(opts.Common, cli.configFile, cli.contextStore)
|
||||||
@@ -259,10 +258,11 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
|
|||||||
|
|
||||||
// NewAPIClientFromFlags creates a new APIClient from command line flags
|
// NewAPIClientFromFlags creates a new APIClient from command line flags
|
||||||
func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
|
func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
|
||||||
|
storeConfig := DefaultContextStoreConfig()
|
||||||
store := &ContextStoreWithDefault{
|
store := &ContextStoreWithDefault{
|
||||||
Store: store.New(cliconfig.ContextStoreDir(), defaultContextStoreConfig()),
|
Store: store.New(cliconfig.ContextStoreDir(), storeConfig),
|
||||||
Resolver: func() (*DefaultContext, error) {
|
Resolver: func() (*DefaultContext, error) {
|
||||||
return resolveDefaultContext(opts, configFile, ioutil.Discard)
|
return ResolveDefaultContext(opts, configFile, storeConfig, ioutil.Discard)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
contextName, err := resolveContextName(opts, configFile, store)
|
contextName, err := resolveContextName(opts, configFile, store)
|
||||||
@@ -453,7 +453,7 @@ func NewDockerCli(ops ...DockerCliOption) (*DockerCli, error) {
|
|||||||
WithContentTrustFromEnv(),
|
WithContentTrustFromEnv(),
|
||||||
WithContainerizedClient(containerizedengine.NewClient),
|
WithContainerizedClient(containerizedengine.NewClient),
|
||||||
}
|
}
|
||||||
cli.contextStoreConfig = defaultContextStoreConfig()
|
cli.contextStoreConfig = DefaultContextStoreConfig()
|
||||||
ops = append(defaultOps, ops...)
|
ops = append(defaultOps, ops...)
|
||||||
if err := cli.Apply(ops...); err != nil {
|
if err := cli.Apply(ops...); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -526,10 +526,22 @@ func resolveContextName(opts *cliflags.CommonOptions, config *configfile.ConfigF
|
|||||||
return DefaultContextName, nil
|
return DefaultContextName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultContextStoreConfig() store.Config {
|
var defaultStoreEndpoints = []store.NamedTypeGetter{
|
||||||
|
store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }),
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterDefaultStoreEndpoints registers a new named endpoint
|
||||||
|
// metadata type with the default context store config, so that
|
||||||
|
// endpoint will be supported by stores using the config returned by
|
||||||
|
// DefaultContextStoreConfig.
|
||||||
|
func RegisterDefaultStoreEndpoints(ep ...store.NamedTypeGetter) {
|
||||||
|
defaultStoreEndpoints = append(defaultStoreEndpoints, ep...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultContextStoreConfig returns a new store.Config with the default set of endpoints configured.
|
||||||
|
func DefaultContextStoreConfig() store.Config {
|
||||||
return store.NewConfig(
|
return store.NewConfig(
|
||||||
func() interface{} { return &DockerContext{} },
|
func() interface{} { return &DockerContext{} },
|
||||||
store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }),
|
defaultStoreEndpoints...,
|
||||||
store.EndpointTypeGetter(kubcontext.KubernetesEndpoint, func() interface{} { return &kubcontext.EndpointMeta{} }),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/context/docker"
|
"github.com/docker/cli/cli/context/docker"
|
||||||
"github.com/docker/cli/cli/context/kubernetes"
|
|
||||||
"github.com/docker/cli/cli/context/store"
|
"github.com/docker/cli/cli/context/store"
|
||||||
"github.com/docker/cli/cli/streams"
|
"github.com/docker/cli/cli/streams"
|
||||||
clitypes "github.com/docker/cli/types"
|
clitypes "github.com/docker/cli/types"
|
||||||
@@ -97,7 +96,7 @@ func WithContainerizedClient(containerizedFn func(string) (clitypes.Containerize
|
|||||||
func WithContextEndpointType(endpointName string, endpointType store.TypeGetter) DockerCliOption {
|
func WithContextEndpointType(endpointName string, endpointType store.TypeGetter) DockerCliOption {
|
||||||
return func(cli *DockerCli) error {
|
return func(cli *DockerCli) error {
|
||||||
switch endpointName {
|
switch endpointName {
|
||||||
case docker.DockerEndpoint, kubernetes.KubernetesEndpoint:
|
case docker.DockerEndpoint:
|
||||||
return fmt.Errorf("cannot change %q endpoint type", endpointName)
|
return fmt.Errorf("cannot change %q endpoint type", endpointName)
|
||||||
}
|
}
|
||||||
cli.contextStoreConfig.SetEndpoint(endpointName, endpointType)
|
cli.contextStoreConfig.SetEndpoint(endpointName, endpointType)
|
||||||
|
|||||||
+24
-1
@@ -6,6 +6,7 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -79,6 +80,24 @@ func TestNewAPIClientFromFlagsWithAPIVersionFromEnv(t *testing.T) {
|
|||||||
assert.Check(t, is.Equal(customVersion, apiclient.ClientVersion()))
|
assert.Check(t, is.Equal(customVersion, apiclient.ClientVersion()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewAPIClientFromFlagsWithHttpProxyEnv(t *testing.T) {
|
||||||
|
defer env.Patch(t, "HTTP_PROXY", "http://proxy.acme.com:1234")()
|
||||||
|
defer env.Patch(t, "DOCKER_HOST", "tcp://docker.acme.com:2376")()
|
||||||
|
|
||||||
|
opts := &flags.CommonOptions{}
|
||||||
|
configFile := &configfile.ConfigFile{}
|
||||||
|
apiclient, err := NewAPIClientFromFlags(opts, configFile)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
transport, ok := apiclient.HTTPClient().Transport.(*http.Transport)
|
||||||
|
assert.Assert(t, ok)
|
||||||
|
assert.Assert(t, transport.Proxy != nil)
|
||||||
|
request, err := http.NewRequest(http.MethodGet, "tcp://docker.acme.com:2376", nil)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
url, err := transport.Proxy(request)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Check(t, is.Equal("http://proxy.acme.com:1234", url.String()))
|
||||||
|
}
|
||||||
|
|
||||||
type fakeClient struct {
|
type fakeClient struct {
|
||||||
client.Client
|
client.Client
|
||||||
pingFunc func() (types.Ping, error)
|
pingFunc func() (types.Ping, error)
|
||||||
@@ -133,6 +152,7 @@ func TestInitializeFromClient(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, testcase := range testcases {
|
for _, testcase := range testcases {
|
||||||
|
testcase := testcase
|
||||||
t.Run(testcase.doc, func(t *testing.T) {
|
t.Run(testcase.doc, func(t *testing.T) {
|
||||||
apiclient := &fakeClient{
|
apiclient := &fakeClient{
|
||||||
pingFunc: testcase.pingFunc,
|
pingFunc: testcase.pingFunc,
|
||||||
@@ -170,6 +190,7 @@ func TestExperimentalCLI(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, testcase := range testcases {
|
for _, testcase := range testcases {
|
||||||
|
testcase := testcase
|
||||||
t.Run(testcase.doc, func(t *testing.T) {
|
t.Run(testcase.doc, func(t *testing.T) {
|
||||||
dir := fs.NewDir(t, testcase.doc, fs.WithFile("config.json", testcase.configfile))
|
dir := fs.NewDir(t, testcase.doc, fs.WithFile("config.json", testcase.configfile))
|
||||||
defer dir.Remove()
|
defer dir.Remove()
|
||||||
@@ -223,6 +244,7 @@ func TestGetClientWithPassword(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, testcase := range testcases {
|
for _, testcase := range testcases {
|
||||||
|
testcase := testcase
|
||||||
t.Run(testcase.doc, func(t *testing.T) {
|
t.Run(testcase.doc, func(t *testing.T) {
|
||||||
passRetriever := func(_, _ string, _ bool, attempts int) (passphrase string, giveup bool, err error) {
|
passRetriever := func(_, _ string, _ bool, attempts int) (passphrase string, giveup bool, err error) {
|
||||||
// Always return an invalid pass first to test iteration
|
// Always return an invalid pass first to test iteration
|
||||||
@@ -275,11 +297,12 @@ func TestNewDockerCliAndOperators(t *testing.T) {
|
|||||||
inbuf := bytes.NewBuffer([]byte("input"))
|
inbuf := bytes.NewBuffer([]byte("input"))
|
||||||
outbuf := bytes.NewBuffer(nil)
|
outbuf := bytes.NewBuffer(nil)
|
||||||
errbuf := bytes.NewBuffer(nil)
|
errbuf := bytes.NewBuffer(nil)
|
||||||
cli.Apply(
|
err = cli.Apply(
|
||||||
WithInputStream(ioutil.NopCloser(inbuf)),
|
WithInputStream(ioutil.NopCloser(inbuf)),
|
||||||
WithOutputStream(outbuf),
|
WithOutputStream(outbuf),
|
||||||
WithErrorStream(errbuf),
|
WithErrorStream(errbuf),
|
||||||
)
|
)
|
||||||
|
assert.NilError(t, err)
|
||||||
// Check input stream
|
// Check input stream
|
||||||
inputStream, err := ioutil.ReadAll(cli.In())
|
inputStream, err := ioutil.ReadAll(cli.In())
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|||||||
@@ -7,10 +7,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
"gotest.tools/golden"
|
"gotest.tools/golden"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
is "gotest.tools/assert/cmp"
|
is "gotest.tools/assert/cmp"
|
||||||
"gotest.tools/golden"
|
"gotest.tools/golden"
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http/httputil"
|
|
||||||
|
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
@@ -103,10 +102,7 @@ func runAttach(dockerCli command.Cli, opts *attachOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, errAttach := client.ContainerAttach(ctx, opts.container, options)
|
resp, errAttach := client.ContainerAttach(ctx, opts.container, options)
|
||||||
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
if errAttach != nil {
|
||||||
// ContainerAttach returns an ErrPersistEOF (connection closed)
|
|
||||||
// means server met an error and put it in Hijacked connection
|
|
||||||
// keep the error and read detailed error message from hijacked connection later
|
|
||||||
return errAttach
|
return errAttach
|
||||||
}
|
}
|
||||||
defer resp.Close()
|
defer resp.Close()
|
||||||
@@ -142,10 +138,6 @@ func runAttach(dockerCli command.Cli, opts *attachOptions) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if errAttach != nil {
|
|
||||||
return errAttach
|
|
||||||
}
|
|
||||||
|
|
||||||
return getExitStatus(errC, resultC)
|
return getExitStatus(errC, resultC)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ func TestNewCreateCommandWithContentTrustErrors(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
cli := test.NewFakeCli(&fakeClient{
|
cli := test.NewFakeCli(&fakeClient{
|
||||||
createContainerFunc: func(config *container.Config,
|
createContainerFunc: func(config *container.Config,
|
||||||
hostConfig *container.HostConfig,
|
hostConfig *container.HostConfig,
|
||||||
@@ -209,6 +210,7 @@ func TestNewCreateCommandWithWarnings(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
cli := test.NewFakeCli(&fakeClient{
|
cli := test.NewFakeCli(&fakeClient{
|
||||||
createContainerFunc: func(config *container.Config,
|
createContainerFunc: func(config *container.Config,
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
"gotest.tools/golden"
|
"gotest.tools/golden"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path"
|
"path"
|
||||||
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -707,6 +708,15 @@ func parseNetworkOpts(copts *containerOptions) (map[string]*networktypes.Endpoin
|
|||||||
if _, ok := endpoints[n.Target]; ok {
|
if _, ok := endpoints[n.Target]; ok {
|
||||||
return nil, errdefs.InvalidParameter(errors.Errorf("network %q is specified multiple times", n.Target))
|
return nil, errdefs.InvalidParameter(errors.Errorf("network %q is specified multiple times", n.Target))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For backward compatibility: if no custom options are provided for the network,
|
||||||
|
// and only a single network is specified, omit the endpoint-configuration
|
||||||
|
// on the client (the daemon will still create it when creating the container)
|
||||||
|
if i == 0 && len(copts.netMode.Value()) == 1 {
|
||||||
|
if ep == nil || reflect.DeepEqual(*ep, networktypes.EndpointSettings{}) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
endpoints[n.Target] = ep
|
endpoints[n.Target] = ep
|
||||||
}
|
}
|
||||||
if hasUserDefined && hasNonUserDefined {
|
if hasUserDefined && hasNonUserDefined {
|
||||||
@@ -789,7 +799,7 @@ func parsePortOpts(publishOpts []string) ([]string, error) {
|
|||||||
|
|
||||||
params[opt[0]] = opt[1]
|
params[opt[0]] = opt[1]
|
||||||
}
|
}
|
||||||
optsList = append(optsList, fmt.Sprintf("%s:%s/%s", params["target"], params["published"], params["protocol"]))
|
optsList = append(optsList, fmt.Sprintf("%s:%s/%s", params["published"], params["target"], params["protocol"]))
|
||||||
}
|
}
|
||||||
return optsList, nil
|
return optsList, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func setupRunFlags() (*pflag.FlagSet, *containerOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseMustError(t *testing.T, args string) {
|
func parseMustError(t *testing.T, args string) {
|
||||||
_, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
|
_, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " ")) //nolint:dogsled
|
||||||
assert.ErrorContains(t, err, "", args)
|
assert.ErrorContains(t, err, "", args)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,13 +401,13 @@ func TestParseNetworkConfig(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "single-network-legacy",
|
name: "single-network-legacy",
|
||||||
flags: []string{"--network", "net1"},
|
flags: []string{"--network", "net1"},
|
||||||
expected: map[string]*networktypes.EndpointSettings{"net1": {}},
|
expected: map[string]*networktypes.EndpointSettings{},
|
||||||
expectedCfg: container.HostConfig{NetworkMode: "net1"},
|
expectedCfg: container.HostConfig{NetworkMode: "net1"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "single-network-advanced",
|
name: "single-network-advanced",
|
||||||
flags: []string{"--network", "name=net1"},
|
flags: []string{"--network", "name=net1"},
|
||||||
expected: map[string]*networktypes.EndpointSettings{"net1": {}},
|
expected: map[string]*networktypes.EndpointSettings{},
|
||||||
expectedCfg: container.HostConfig{NetworkMode: "net1"},
|
expectedCfg: container.HostConfig{NetworkMode: "net1"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -539,7 +539,7 @@ func TestParseModes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// uts ko
|
// uts ko
|
||||||
_, _, _, err = parseRun([]string{"--uts=container:", "img", "cmd"})
|
_, _, _, err = parseRun([]string{"--uts=container:", "img", "cmd"}) //nolint:dogsled
|
||||||
assert.ErrorContains(t, err, "--uts: invalid UTS mode")
|
assert.ErrorContains(t, err, "--uts: invalid UTS mode")
|
||||||
|
|
||||||
// uts ok
|
// uts ok
|
||||||
@@ -600,7 +600,7 @@ func TestParseRestartPolicy(t *testing.T) {
|
|||||||
|
|
||||||
func TestParseRestartPolicyAutoRemove(t *testing.T) {
|
func TestParseRestartPolicyAutoRemove(t *testing.T) {
|
||||||
expected := "Conflicting options: --restart and --rm"
|
expected := "Conflicting options: --restart and --rm"
|
||||||
_, _, _, err := parseRun([]string{"--rm", "--restart=always", "img", "cmd"})
|
_, _, _, err := parseRun([]string{"--rm", "--restart=always", "img", "cmd"}) //nolint:dogsled
|
||||||
if err == nil || err.Error() != expected {
|
if err == nil || err.Error() != expected {
|
||||||
t.Fatalf("Expected error %v, but got none", expected)
|
t.Fatalf("Expected error %v, but got none", expected)
|
||||||
}
|
}
|
||||||
@@ -854,3 +854,9 @@ func TestParseSystemPaths(t *testing.T) {
|
|||||||
assert.DeepEqual(t, readonlyPaths, tc.readonly)
|
assert.DeepEqual(t, readonlyPaths, tc.readonly)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParsePortOpts(t *testing.T) {
|
||||||
|
parsed, err := parsePortOpts([]string{"published=1500,target=200", "target=80,published=90"})
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, []string{"1500:200/tcp", "90:80/tcp"}, parsed)
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func NewRmCommand(dockerCli command.Cli) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
flags.BoolVarP(&opts.rmVolumes, "volumes", "v", false, "Remove the volumes associated with the container")
|
flags.BoolVarP(&opts.rmVolumes, "volumes", "v", false, "Remove anonymous volumes associated with the container")
|
||||||
flags.BoolVarP(&opts.rmLink, "link", "l", false, "Remove the specified link")
|
flags.BoolVarP(&opts.rmLink, "link", "l", false, "Remove the specified link")
|
||||||
flags.BoolVarP(&opts.force, "force", "f", false, "Force the removal of a running container (uses SIGKILL)")
|
flags.BoolVarP(&opts.force, "force", "f", false, "Force the removal of a running container (uses SIGKILL)")
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http/httputil"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -115,11 +114,6 @@ func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptio
|
|||||||
config.StdinOnce = false
|
config.StdinOnce = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable sigProxy when in TTY mode
|
|
||||||
if config.Tty {
|
|
||||||
opts.sigProxy = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Telling the Windows daemon the initial size of the tty during start makes
|
// Telling the Windows daemon the initial size of the tty during start makes
|
||||||
// a far better user experience rather than relying on subsequent resizes
|
// a far better user experience rather than relying on subsequent resizes
|
||||||
// to cause things to catch up.
|
// to cause things to catch up.
|
||||||
@@ -253,10 +247,7 @@ func attachContainer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, errAttach := dockerCli.Client().ContainerAttach(ctx, containerID, options)
|
resp, errAttach := dockerCli.Client().ContainerAttach(ctx, containerID, options)
|
||||||
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
if errAttach != nil {
|
||||||
// ContainerAttach returns an ErrPersistEOF (connection closed)
|
|
||||||
// means server met an error and put it in Hijacked connection
|
|
||||||
// keep the error and read detailed error message from hijacked connection later
|
|
||||||
return nil, errAttach
|
return nil, errAttach
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http/httputil"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
@@ -98,10 +97,7 @@ func runStart(dockerCli command.Cli, opts *startOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, errAttach := dockerCli.Client().ContainerAttach(ctx, c.ID, options)
|
resp, errAttach := dockerCli.Client().ContainerAttach(ctx, c.ID, options)
|
||||||
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
if errAttach != nil {
|
||||||
// ContainerAttach return an ErrPersistEOF (connection closed)
|
|
||||||
// means server met an error and already put it in Hijacked connection,
|
|
||||||
// we would keep the error and read the detailed error message from hijacked connection
|
|
||||||
return errAttach
|
return errAttach
|
||||||
}
|
}
|
||||||
defer resp.Close()
|
defer resp.Close()
|
||||||
@@ -154,7 +150,7 @@ func runStart(dockerCli command.Cli, opts *startOptions) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if attachErr := <-cErr; attachErr != nil {
|
if attachErr := <-cErr; attachErr != nil {
|
||||||
if _, ok := err.(term.EscapeError); ok {
|
if _, ok := attachErr.(term.EscapeError); ok {
|
||||||
// The user entered the detach escape sequence.
|
// The user entered the detach escape sequence.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,7 +208,9 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
for range time.Tick(500 * time.Millisecond) {
|
ticker := time.NewTicker(500 * time.Millisecond)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for range ticker.C {
|
||||||
cleanScreen()
|
cleanScreen()
|
||||||
ccstats := []StatsEntry{}
|
ccstats := []StatsEntry{}
|
||||||
cStats.mu.Lock()
|
cStats.mu.Lock()
|
||||||
|
|||||||
@@ -78,13 +78,19 @@ func RunCreate(cli command.Cli, o *CreateOptions) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "unable to parse default-stack-orchestrator")
|
return errors.Wrap(err, "unable to parse default-stack-orchestrator")
|
||||||
}
|
}
|
||||||
if o.From == "" && o.Docker == nil && o.Kubernetes == nil {
|
switch {
|
||||||
return createFromExistingContext(s, cli.CurrentContext(), stackOrchestrator, o)
|
case o.From == "" && o.Docker == nil && o.Kubernetes == nil:
|
||||||
|
err = createFromExistingContext(s, cli.CurrentContext(), stackOrchestrator, o)
|
||||||
|
case o.From != "":
|
||||||
|
err = createFromExistingContext(s, o.From, stackOrchestrator, o)
|
||||||
|
default:
|
||||||
|
err = createNewContext(o, stackOrchestrator, cli, s)
|
||||||
}
|
}
|
||||||
if o.From != "" {
|
if err == nil {
|
||||||
return createFromExistingContext(s, o.From, stackOrchestrator, o)
|
fmt.Fprintln(cli.Out(), o.Name)
|
||||||
|
fmt.Fprintf(cli.Err(), "Successfully created context %q\n", o.Name)
|
||||||
}
|
}
|
||||||
return createNewContext(o, stackOrchestrator, cli, s)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func createNewContext(o *CreateOptions, stackOrchestrator command.Orchestrator, cli command.Cli, s store.Writer) error {
|
func createNewContext(o *CreateOptions, stackOrchestrator command.Orchestrator, cli command.Cli, s store.Writer) error {
|
||||||
@@ -127,8 +133,6 @@ func createNewContext(o *CreateOptions, stackOrchestrator command.Orchestrator,
|
|||||||
if err := s.ResetTLSMaterial(o.Name, &contextTLSData); err != nil {
|
if err := s.ResetTLSMaterial(o.Name, &contextTLSData); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintln(cli.Out(), o.Name)
|
|
||||||
fmt.Fprintf(cli.Err(), "Successfully created context %q\n", o.Name)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -131,6 +132,11 @@ func TestCreateInvalids(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func assertContextCreateLogging(t *testing.T, cli *test.FakeCli, n string) {
|
||||||
|
assert.Equal(t, n+"\n", cli.OutBuffer().String())
|
||||||
|
assert.Equal(t, fmt.Sprintf("Successfully created context %q\n", n), cli.ErrBuffer().String())
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateOrchestratorSwarm(t *testing.T) {
|
func TestCreateOrchestratorSwarm(t *testing.T) {
|
||||||
cli, cleanup := makeFakeCli(t)
|
cli, cleanup := makeFakeCli(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
@@ -141,8 +147,7 @@ func TestCreateOrchestratorSwarm(t *testing.T) {
|
|||||||
Docker: map[string]string{},
|
Docker: map[string]string{},
|
||||||
})
|
})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Equal(t, "test\n", cli.OutBuffer().String())
|
assertContextCreateLogging(t, cli, "test")
|
||||||
assert.Equal(t, "Successfully created context \"test\"\n", cli.ErrBuffer().String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateOrchestratorEmpty(t *testing.T) {
|
func TestCreateOrchestratorEmpty(t *testing.T) {
|
||||||
@@ -154,6 +159,7 @@ func TestCreateOrchestratorEmpty(t *testing.T) {
|
|||||||
Docker: map[string]string{},
|
Docker: map[string]string{},
|
||||||
})
|
})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
assertContextCreateLogging(t, cli, "test")
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateTestKubeEndpoint(t *testing.T, s store.Reader, name string) {
|
func validateTestKubeEndpoint(t *testing.T, s store.Reader, name string) {
|
||||||
@@ -189,6 +195,7 @@ func TestCreateOrchestratorAllKubernetesEndpointFromCurrent(t *testing.T) {
|
|||||||
cli, cleanup := makeFakeCli(t)
|
cli, cleanup := makeFakeCli(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
createTestContextWithKube(t, cli)
|
createTestContextWithKube(t, cli)
|
||||||
|
assertContextCreateLogging(t, cli, "test")
|
||||||
validateTestKubeEndpoint(t, cli.ContextStore(), "test")
|
validateTestKubeEndpoint(t, cli.ContextStore(), "test")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,6 +232,7 @@ func TestCreateFromContext(t *testing.T) {
|
|||||||
defer cleanup()
|
defer cleanup()
|
||||||
revert := env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")
|
revert := env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")
|
||||||
defer revert()
|
defer revert()
|
||||||
|
cli.ResetOutputBuffers()
|
||||||
assert.NilError(t, RunCreate(cli, &CreateOptions{
|
assert.NilError(t, RunCreate(cli, &CreateOptions{
|
||||||
Name: "original",
|
Name: "original",
|
||||||
Description: "original description",
|
Description: "original description",
|
||||||
@@ -236,6 +244,9 @@ func TestCreateFromContext(t *testing.T) {
|
|||||||
},
|
},
|
||||||
DefaultStackOrchestrator: "swarm",
|
DefaultStackOrchestrator: "swarm",
|
||||||
}))
|
}))
|
||||||
|
assertContextCreateLogging(t, cli, "original")
|
||||||
|
|
||||||
|
cli.ResetOutputBuffers()
|
||||||
assert.NilError(t, RunCreate(cli, &CreateOptions{
|
assert.NilError(t, RunCreate(cli, &CreateOptions{
|
||||||
Name: "dummy",
|
Name: "dummy",
|
||||||
Description: "dummy description",
|
Description: "dummy description",
|
||||||
@@ -247,11 +258,14 @@ func TestCreateFromContext(t *testing.T) {
|
|||||||
},
|
},
|
||||||
DefaultStackOrchestrator: "swarm",
|
DefaultStackOrchestrator: "swarm",
|
||||||
}))
|
}))
|
||||||
|
assertContextCreateLogging(t, cli, "dummy")
|
||||||
|
|
||||||
cli.SetCurrentContext("dummy")
|
cli.SetCurrentContext("dummy")
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
c := c
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
cli.ResetOutputBuffers()
|
||||||
err := RunCreate(cli, &CreateOptions{
|
err := RunCreate(cli, &CreateOptions{
|
||||||
From: "original",
|
From: "original",
|
||||||
Name: c.name,
|
Name: c.name,
|
||||||
@@ -261,6 +275,7 @@ func TestCreateFromContext(t *testing.T) {
|
|||||||
Kubernetes: c.kubernetes,
|
Kubernetes: c.kubernetes,
|
||||||
})
|
})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
assertContextCreateLogging(t, cli, c.name)
|
||||||
newContext, err := cli.ContextStore().GetMetadata(c.name)
|
newContext, err := cli.ContextStore().GetMetadata(c.name)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
newContextTyped, err := command.GetDockerContext(newContext)
|
newContextTyped, err := command.GetDockerContext(newContext)
|
||||||
@@ -308,6 +323,7 @@ func TestCreateFromCurrent(t *testing.T) {
|
|||||||
defer cleanup()
|
defer cleanup()
|
||||||
revert := env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")
|
revert := env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")
|
||||||
defer revert()
|
defer revert()
|
||||||
|
cli.ResetOutputBuffers()
|
||||||
assert.NilError(t, RunCreate(cli, &CreateOptions{
|
assert.NilError(t, RunCreate(cli, &CreateOptions{
|
||||||
Name: "original",
|
Name: "original",
|
||||||
Description: "original description",
|
Description: "original description",
|
||||||
@@ -319,17 +335,21 @@ func TestCreateFromCurrent(t *testing.T) {
|
|||||||
},
|
},
|
||||||
DefaultStackOrchestrator: "swarm",
|
DefaultStackOrchestrator: "swarm",
|
||||||
}))
|
}))
|
||||||
|
assertContextCreateLogging(t, cli, "original")
|
||||||
|
|
||||||
cli.SetCurrentContext("original")
|
cli.SetCurrentContext("original")
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
c := c
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
cli.ResetOutputBuffers()
|
||||||
err := RunCreate(cli, &CreateOptions{
|
err := RunCreate(cli, &CreateOptions{
|
||||||
Name: c.name,
|
Name: c.name,
|
||||||
Description: c.description,
|
Description: c.description,
|
||||||
DefaultStackOrchestrator: c.orchestrator,
|
DefaultStackOrchestrator: c.orchestrator,
|
||||||
})
|
})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
assertContextCreateLogging(t, cli, c.name)
|
||||||
newContext, err := cli.ContextStore().GetMetadata(c.name)
|
newContext, err := cli.ContextStore().GetMetadata(c.name)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
newContextTyped, err := command.GetDockerContext(newContext)
|
newContextTyped, err := command.GetDockerContext(newContext)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
func newImportCommand(dockerCli command.Cli) *cobra.Command {
|
func newImportCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "import CONTEXT FILE|-",
|
Use: "import CONTEXT FILE|-",
|
||||||
Short: "Import a context from a tar file",
|
Short: "Import a context from a tar or zip file",
|
||||||
Args: cli.ExactArgs(2),
|
Args: cli.ExactArgs(2),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return RunImport(dockerCli, args[0], args[1])
|
return RunImport(dockerCli, args[0], args[1])
|
||||||
@@ -28,6 +28,7 @@ func RunImport(dockerCli command.Cli, name string, source string) error {
|
|||||||
if err := checkContextNameForCreation(dockerCli.ContextStore(), name); err != nil {
|
if err := checkContextNameForCreation(dockerCli.ContextStore(), name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var reader io.Reader
|
var reader io.Reader
|
||||||
if source == "-" {
|
if source == "-" {
|
||||||
reader = dockerCli.In()
|
reader = dockerCli.In()
|
||||||
@@ -43,6 +44,7 @@ func RunImport(dockerCli command.Cli, name string, source string) error {
|
|||||||
if err := store.Import(name, dockerCli.ContextStore(), reader); err != nil {
|
if err := store.Import(name, dockerCli.ContextStore(), reader); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintln(dockerCli.Out(), name)
|
fmt.Fprintln(dockerCli.Out(), name)
|
||||||
fmt.Fprintf(dockerCli.Err(), "Successfully imported context %q\n", name)
|
fmt.Fprintf(dockerCli.Err(), "Successfully imported context %q\n", name)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -3,15 +3,11 @@ package command
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
"github.com/docker/cli/cli/context/docker"
|
"github.com/docker/cli/cli/context/docker"
|
||||||
"github.com/docker/cli/cli/context/kubernetes"
|
|
||||||
"github.com/docker/cli/cli/context/store"
|
"github.com/docker/cli/cli/context/store"
|
||||||
cliflags "github.com/docker/cli/cli/flags"
|
cliflags "github.com/docker/cli/cli/flags"
|
||||||
"github.com/docker/docker/pkg/homedir"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,7 +16,7 @@ const (
|
|||||||
DefaultContextName = "default"
|
DefaultContextName = "default"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultContext contains the default context data for all enpoints
|
// DefaultContext contains the default context data for all endpoints
|
||||||
type DefaultContext struct {
|
type DefaultContext struct {
|
||||||
Meta store.Metadata
|
Meta store.Metadata
|
||||||
TLS store.ContextTLSData
|
TLS store.ContextTLSData
|
||||||
@@ -35,8 +31,21 @@ type ContextStoreWithDefault struct {
|
|||||||
Resolver DefaultContextResolver
|
Resolver DefaultContextResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveDefaultContext creates a Metadata for the current CLI invocation parameters
|
// EndpointDefaultResolver is implemented by any EndpointMeta object
|
||||||
func resolveDefaultContext(opts *cliflags.CommonOptions, config *configfile.ConfigFile, stderr io.Writer) (*DefaultContext, error) {
|
// which wants to be able to populate the store with whatever their default is.
|
||||||
|
type EndpointDefaultResolver interface {
|
||||||
|
// ResolveDefault returns values suitable for storing in store.Metadata.Endpoints
|
||||||
|
// and store.ContextTLSData.Endpoints.
|
||||||
|
//
|
||||||
|
// An error is only returned for something fatal, not simply
|
||||||
|
// the lack of a default (e.g. because the config file which
|
||||||
|
// would contain it is missing). If there is no default then
|
||||||
|
// returns nil, nil, nil.
|
||||||
|
ResolveDefault(Orchestrator) (interface{}, *store.EndpointTLSData, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveDefaultContext creates a Metadata for the current CLI invocation parameters
|
||||||
|
func ResolveDefaultContext(opts *cliflags.CommonOptions, config *configfile.ConfigFile, storeconfig store.Config, stderr io.Writer) (*DefaultContext, error) {
|
||||||
stackOrchestrator, err := GetStackOrchestrator("", "", config.StackOrchestrator, stderr)
|
stackOrchestrator, err := GetStackOrchestrator("", "", config.StackOrchestrator, stderr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -62,20 +71,28 @@ func resolveDefaultContext(opts *cliflags.CommonOptions, config *configfile.Conf
|
|||||||
contextTLSData.Endpoints[docker.DockerEndpoint] = *dockerEP.TLSData.ToStoreTLSData()
|
contextTLSData.Endpoints[docker.DockerEndpoint] = *dockerEP.TLSData.ToStoreTLSData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default context uses env-based kubeconfig for Kubernetes endpoint configuration
|
if err := storeconfig.ForeachEndpointType(func(n string, get store.TypeGetter) error {
|
||||||
kubeconfig := os.Getenv("KUBECONFIG")
|
if n == docker.DockerEndpoint { // handled above
|
||||||
if kubeconfig == "" {
|
return nil
|
||||||
kubeconfig = filepath.Join(homedir.Get(), ".kube/config")
|
|
||||||
}
|
|
||||||
kubeEP, err := kubernetes.FromKubeConfig(kubeconfig, "", "")
|
|
||||||
if (stackOrchestrator == OrchestratorKubernetes || stackOrchestrator == OrchestratorAll) && err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "default orchestrator is %s but kubernetes endpoint could not be found", stackOrchestrator)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
contextMetadata.Endpoints[kubernetes.KubernetesEndpoint] = kubeEP.EndpointMeta
|
|
||||||
if kubeEP.TLSData != nil {
|
|
||||||
contextTLSData.Endpoints[kubernetes.KubernetesEndpoint] = *kubeEP.TLSData.ToStoreTLSData()
|
|
||||||
}
|
}
|
||||||
|
ep := get()
|
||||||
|
if i, ok := ep.(EndpointDefaultResolver); ok {
|
||||||
|
meta, tls, err := i.ResolveDefault(stackOrchestrator)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if meta == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
contextMetadata.Endpoints[n] = meta
|
||||||
|
if tls != nil {
|
||||||
|
contextTLSData.Endpoints[n] = *tls
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Nothing to be done
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DefaultContext{Meta: contextMetadata, TLS: contextTLSData}, nil
|
return &DefaultContext{Meta: contextMetadata, TLS: contextTLSData}, nil
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
"github.com/docker/cli/cli/context/docker"
|
"github.com/docker/cli/cli/context/docker"
|
||||||
"github.com/docker/cli/cli/context/kubernetes"
|
|
||||||
"github.com/docker/cli/cli/context/store"
|
"github.com/docker/cli/cli/context/store"
|
||||||
cliflags "github.com/docker/cli/cli/flags"
|
cliflags "github.com/docker/cli/cli/flags"
|
||||||
"github.com/docker/go-connections/tlsconfig"
|
"github.com/docker/go-connections/tlsconfig"
|
||||||
@@ -63,22 +62,20 @@ func TestDefaultContextInitializer(t *testing.T) {
|
|||||||
cli, err := NewDockerCli()
|
cli, err := NewDockerCli()
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
defer env.Patch(t, "DOCKER_HOST", "ssh://someswarmserver")()
|
defer env.Patch(t, "DOCKER_HOST", "ssh://someswarmserver")()
|
||||||
defer env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")()
|
|
||||||
cli.configFile = &configfile.ConfigFile{
|
cli.configFile = &configfile.ConfigFile{
|
||||||
StackOrchestrator: "all",
|
StackOrchestrator: "swarm",
|
||||||
}
|
}
|
||||||
ctx, err := resolveDefaultContext(&cliflags.CommonOptions{
|
ctx, err := ResolveDefaultContext(&cliflags.CommonOptions{
|
||||||
TLS: true,
|
TLS: true,
|
||||||
TLSOptions: &tlsconfig.Options{
|
TLSOptions: &tlsconfig.Options{
|
||||||
CAFile: "./testdata/ca.pem",
|
CAFile: "./testdata/ca.pem",
|
||||||
},
|
},
|
||||||
}, cli.ConfigFile(), cli.Err())
|
}, cli.ConfigFile(), DefaultContextStoreConfig(), cli.Err())
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Equal(t, "default", ctx.Meta.Name)
|
assert.Equal(t, "default", ctx.Meta.Name)
|
||||||
assert.Equal(t, OrchestratorAll, ctx.Meta.Metadata.(DockerContext).StackOrchestrator)
|
assert.Equal(t, OrchestratorSwarm, ctx.Meta.Metadata.(DockerContext).StackOrchestrator)
|
||||||
assert.DeepEqual(t, "ssh://someswarmserver", ctx.Meta.Endpoints[docker.DockerEndpoint].(docker.EndpointMeta).Host)
|
assert.DeepEqual(t, "ssh://someswarmserver", ctx.Meta.Endpoints[docker.DockerEndpoint].(docker.EndpointMeta).Host)
|
||||||
golden.Assert(t, string(ctx.TLS.Endpoints[docker.DockerEndpoint].Files["ca.pem"]), "ca.pem")
|
golden.Assert(t, string(ctx.TLS.Endpoints[docker.DockerEndpoint].Files["ca.pem"]), "ca.pem")
|
||||||
assert.DeepEqual(t, "zoinx", ctx.Meta.Endpoints[kubernetes.KubernetesEndpoint].(kubernetes.EndpointMeta).DefaultNamespace)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExportDefaultImport(t *testing.T) {
|
func TestExportDefaultImport(t *testing.T) {
|
||||||
|
|||||||
@@ -304,7 +304,8 @@ func TestContainerContextWriteWithNoContainers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, context := range contexts {
|
for _, context := range contexts {
|
||||||
ContainerWrite(context.context, containers)
|
err := ContainerWrite(context.context, containers)
|
||||||
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.Equal(context.expected, out.String()))
|
assert.Check(t, is.Equal(context.expected, out.String()))
|
||||||
// Clean buffer
|
// Clean buffer
|
||||||
out.Reset()
|
out.Reset()
|
||||||
|
|||||||
@@ -348,7 +348,8 @@ func TestImageContextWriteWithNoImage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, context := range contexts {
|
for _, context := range contexts {
|
||||||
ImageWrite(context.context, images)
|
err := ImageWrite(context.context, images)
|
||||||
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.Equal(context.expected, out.String()))
|
assert.Check(t, is.Equal(context.expected, out.String()))
|
||||||
// Clean buffer
|
// Clean buffer
|
||||||
out.Reset()
|
out.Reset()
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
package idresolver
|
package idresolver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
is "gotest.tools/assert/cmp"
|
is "gotest.tools/assert/cmp"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
"context"
|
|
||||||
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestResolveError(t *testing.T) {
|
func TestResolveError(t *testing.T) {
|
||||||
|
|||||||
@@ -120,28 +120,40 @@ func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
|
|||||||
flags.VarP(&options.tags, "tag", "t", "Name and optionally a tag in the 'name:tag' format")
|
flags.VarP(&options.tags, "tag", "t", "Name and optionally a tag in the 'name:tag' format")
|
||||||
flags.Var(&options.buildArgs, "build-arg", "Set build-time variables")
|
flags.Var(&options.buildArgs, "build-arg", "Set build-time variables")
|
||||||
flags.Var(options.ulimits, "ulimit", "Ulimit options")
|
flags.Var(options.ulimits, "ulimit", "Ulimit options")
|
||||||
|
flags.SetAnnotation("ulimit", "no-buildkit", nil)
|
||||||
flags.StringVarP(&options.dockerfileName, "file", "f", "", "Name of the Dockerfile (Default is 'PATH/Dockerfile')")
|
flags.StringVarP(&options.dockerfileName, "file", "f", "", "Name of the Dockerfile (Default is 'PATH/Dockerfile')")
|
||||||
flags.VarP(&options.memory, "memory", "m", "Memory limit")
|
flags.VarP(&options.memory, "memory", "m", "Memory limit")
|
||||||
|
flags.SetAnnotation("memory", "no-buildkit", nil)
|
||||||
flags.Var(&options.memorySwap, "memory-swap", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
|
flags.Var(&options.memorySwap, "memory-swap", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
|
||||||
|
flags.SetAnnotation("memory-swap", "no-buildkit", nil)
|
||||||
flags.Var(&options.shmSize, "shm-size", "Size of /dev/shm")
|
flags.Var(&options.shmSize, "shm-size", "Size of /dev/shm")
|
||||||
|
flags.SetAnnotation("shm-size", "no-buildkit", nil)
|
||||||
flags.Int64VarP(&options.cpuShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
|
flags.Int64VarP(&options.cpuShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
|
||||||
|
flags.SetAnnotation("cpu-shares", "no-buildkit", nil)
|
||||||
flags.Int64Var(&options.cpuPeriod, "cpu-period", 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
|
flags.Int64Var(&options.cpuPeriod, "cpu-period", 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
|
||||||
|
flags.SetAnnotation("cpu-period", "no-buildkit", nil)
|
||||||
flags.Int64Var(&options.cpuQuota, "cpu-quota", 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
|
flags.Int64Var(&options.cpuQuota, "cpu-quota", 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
|
||||||
|
flags.SetAnnotation("cpu-quota", "no-buildkit", nil)
|
||||||
flags.StringVar(&options.cpuSetCpus, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
|
flags.StringVar(&options.cpuSetCpus, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
|
||||||
|
flags.SetAnnotation("cpuset-cpus", "no-buildkit", nil)
|
||||||
flags.StringVar(&options.cpuSetMems, "cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)")
|
flags.StringVar(&options.cpuSetMems, "cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)")
|
||||||
|
flags.SetAnnotation("cpuset-mems", "no-buildkit", nil)
|
||||||
flags.StringVar(&options.cgroupParent, "cgroup-parent", "", "Optional parent cgroup for the container")
|
flags.StringVar(&options.cgroupParent, "cgroup-parent", "", "Optional parent cgroup for the container")
|
||||||
|
flags.SetAnnotation("cgroup-parent", "no-buildkit", nil)
|
||||||
flags.StringVar(&options.isolation, "isolation", "", "Container isolation technology")
|
flags.StringVar(&options.isolation, "isolation", "", "Container isolation technology")
|
||||||
flags.Var(&options.labels, "label", "Set metadata for an image")
|
flags.Var(&options.labels, "label", "Set metadata for an image")
|
||||||
flags.BoolVar(&options.noCache, "no-cache", false, "Do not use cache when building the image")
|
flags.BoolVar(&options.noCache, "no-cache", false, "Do not use cache when building the image")
|
||||||
flags.BoolVar(&options.rm, "rm", true, "Remove intermediate containers after a successful build")
|
flags.BoolVar(&options.rm, "rm", true, "Remove intermediate containers after a successful build")
|
||||||
|
flags.SetAnnotation("rm", "no-buildkit", nil)
|
||||||
flags.BoolVar(&options.forceRm, "force-rm", false, "Always remove intermediate containers")
|
flags.BoolVar(&options.forceRm, "force-rm", false, "Always remove intermediate containers")
|
||||||
|
flags.SetAnnotation("force-rm", "no-buildkit", nil)
|
||||||
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the build output and print image ID on success")
|
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the build output and print image ID on success")
|
||||||
flags.BoolVar(&options.pull, "pull", false, "Always attempt to pull a newer version of the image")
|
flags.BoolVar(&options.pull, "pull", false, "Always attempt to pull a newer version of the image")
|
||||||
flags.StringSliceVar(&options.cacheFrom, "cache-from", []string{}, "Images to consider as cache sources")
|
flags.StringSliceVar(&options.cacheFrom, "cache-from", []string{}, "Images to consider as cache sources")
|
||||||
flags.BoolVar(&options.compress, "compress", false, "Compress the build context using gzip")
|
flags.BoolVar(&options.compress, "compress", false, "Compress the build context using gzip")
|
||||||
flags.SetAnnotation("compress", "no-buildkit", nil)
|
flags.SetAnnotation("compress", "no-buildkit", nil)
|
||||||
|
|
||||||
flags.StringSliceVar(&options.securityOpt, "security-opt", []string{}, "Security options")
|
flags.StringSliceVar(&options.securityOpt, "security-opt", []string{}, "Security options")
|
||||||
|
flags.SetAnnotation("security-opt", "no-buildkit", nil)
|
||||||
flags.StringVar(&options.networkMode, "network", "default", "Set the networking mode for the RUN instructions during build")
|
flags.StringVar(&options.networkMode, "network", "default", "Set the networking mode for the RUN instructions during build")
|
||||||
flags.SetAnnotation("network", "version", []string{"1.25"})
|
flags.SetAnnotation("network", "version", []string{"1.25"})
|
||||||
flags.Var(&options.extraHosts, "add-host", "Add a custom host-to-IP mapping (host:ip)")
|
flags.Var(&options.extraHosts, "add-host", "Add a custom host-to-IP mapping (host:ip)")
|
||||||
@@ -474,7 +486,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
|
|||||||
// should be just the image ID and we'll print that to stdout.
|
// should be just the image ID and we'll print that to stdout.
|
||||||
if options.quiet {
|
if options.quiet {
|
||||||
imageID = fmt.Sprintf("%s", buildBuff)
|
imageID = fmt.Sprintf("%s", buildBuff)
|
||||||
fmt.Fprintf(dockerCli.Out(), imageID)
|
_, _ = fmt.Fprint(dockerCli.Out(), imageID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.imageIDFile != "" {
|
if options.imageIDFile != "" {
|
||||||
|
|||||||
@@ -232,6 +232,7 @@ func GetContextFromURL(out io.Writer, remoteURL, dockerfileName string) (io.Read
|
|||||||
// getWithStatusError does an http.Get() and returns an error if the
|
// getWithStatusError does an http.Get() and returns an error if the
|
||||||
// status code is 4xx or 5xx.
|
// status code is 4xx or 5xx.
|
||||||
func getWithStatusError(url string) (resp *http.Response, err error) {
|
func getWithStatusError(url string) (resp *http.Response, err error) {
|
||||||
|
// #nosec G107
|
||||||
if resp, err = http.Get(url); err != nil {
|
if resp, err = http.Get(url); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -242,9 +243,9 @@ func getWithStatusError(url string) (resp *http.Response, err error) {
|
|||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, msg+": error reading body")
|
return nil, errors.Wrapf(err, "%s: error reading body", msg)
|
||||||
}
|
}
|
||||||
return nil, errors.Errorf(msg+": %s", bytes.TrimSpace(body))
|
return nil, errors.Errorf("%s: %s", msg, bytes.TrimSpace(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetContextFromLocalDir uses the given local directory as context for a
|
// GetContextFromLocalDir uses the given local directory as context for a
|
||||||
@@ -344,7 +345,6 @@ func getDockerfileRelPath(absContextDir, givenDockerfile string) (string, error)
|
|||||||
absDockerfile, err = filepath.EvalSymlinks(absDockerfile)
|
absDockerfile, err = filepath.EvalSymlinks(absDockerfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Errorf("unable to evaluate symlinks in Dockerfile path: %v", err)
|
return "", errors.Errorf("unable to evaluate symlinks in Dockerfile path: %v", err)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ var prepareEmpty = func(t *testing.T) (string, func()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var prepareNoFiles = func(t *testing.T) (string, func()) {
|
var prepareNoFiles = func(t *testing.T) (string, func()) {
|
||||||
return createTestTempDir(t, "", "builder-context-test")
|
return createTestTempDir(t, "builder-context-test")
|
||||||
}
|
}
|
||||||
|
|
||||||
var prepareOneFile = func(t *testing.T) (string, func()) {
|
var prepareOneFile = func(t *testing.T) (string, func()) {
|
||||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
|
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
|
||||||
return contextDir, cleanup
|
return contextDir, cleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ func testValidateContextDirectory(t *testing.T, prepare func(t *testing.T) (stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetContextFromLocalDirNoDockerfile(t *testing.T) {
|
func TestGetContextFromLocalDirNoDockerfile(t *testing.T) {
|
||||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
_, _, err := GetContextFromLocalDir(contextDir, "")
|
_, _, err := GetContextFromLocalDir(contextDir, "")
|
||||||
@@ -50,7 +50,7 @@ func TestGetContextFromLocalDirNoDockerfile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetContextFromLocalDirNotExistingDir(t *testing.T) {
|
func TestGetContextFromLocalDirNotExistingDir(t *testing.T) {
|
||||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
fakePath := filepath.Join(contextDir, "fake")
|
fakePath := filepath.Join(contextDir, "fake")
|
||||||
@@ -60,7 +60,7 @@ func TestGetContextFromLocalDirNotExistingDir(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetContextFromLocalDirNotExistingDockerfile(t *testing.T) {
|
func TestGetContextFromLocalDirNotExistingDockerfile(t *testing.T) {
|
||||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
fakePath := filepath.Join(contextDir, "fake")
|
fakePath := filepath.Join(contextDir, "fake")
|
||||||
@@ -70,10 +70,10 @@ func TestGetContextFromLocalDirNotExistingDockerfile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) {
|
func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) {
|
||||||
contextDir, dirCleanup := createTestTempDir(t, "", "builder-context-test")
|
contextDir, dirCleanup := createTestTempDir(t, "builder-context-test")
|
||||||
defer dirCleanup()
|
defer dirCleanup()
|
||||||
|
|
||||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
|
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
|
||||||
|
|
||||||
chdirCleanup := chdir(t, contextDir)
|
chdirCleanup := chdir(t, contextDir)
|
||||||
defer chdirCleanup()
|
defer chdirCleanup()
|
||||||
@@ -86,10 +86,10 @@ func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetContextFromLocalDirWithDockerfile(t *testing.T) {
|
func TestGetContextFromLocalDirWithDockerfile(t *testing.T) {
|
||||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
|
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
|
||||||
|
|
||||||
absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "")
|
absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
@@ -99,11 +99,11 @@ func TestGetContextFromLocalDirWithDockerfile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetContextFromLocalDirLocalFile(t *testing.T) {
|
func TestGetContextFromLocalDirLocalFile(t *testing.T) {
|
||||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
|
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
|
||||||
testFilename := createTestTempFile(t, contextDir, "tmpTest", "test", 0777)
|
testFilename := createTestTempFile(t, contextDir, "tmpTest", "test")
|
||||||
|
|
||||||
absContextDir, relDockerfile, err := GetContextFromLocalDir(testFilename, "")
|
absContextDir, relDockerfile, err := GetContextFromLocalDir(testFilename, "")
|
||||||
|
|
||||||
@@ -121,13 +121,13 @@ func TestGetContextFromLocalDirLocalFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetContextFromLocalDirWithCustomDockerfile(t *testing.T) {
|
func TestGetContextFromLocalDirWithCustomDockerfile(t *testing.T) {
|
||||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
chdirCleanup := chdir(t, contextDir)
|
chdirCleanup := chdir(t, contextDir)
|
||||||
defer chdirCleanup()
|
defer chdirCleanup()
|
||||||
|
|
||||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
|
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
|
||||||
|
|
||||||
absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, DefaultDockerfileName)
|
absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, DefaultDockerfileName)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
@@ -173,10 +173,10 @@ func TestGetContextFromReaderString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetContextFromReaderTar(t *testing.T) {
|
func TestGetContextFromReaderTar(t *testing.T) {
|
||||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
|
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
|
||||||
|
|
||||||
tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
|
tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
@@ -241,17 +241,18 @@ func TestValidateContextDirectoryWithOneFileExcludes(t *testing.T) {
|
|||||||
// createTestTempDir creates a temporary directory for testing.
|
// createTestTempDir creates a temporary directory for testing.
|
||||||
// It returns the created path and a cleanup function which is meant to be used as deferred call.
|
// It returns the created path and a cleanup function which is meant to be used as deferred call.
|
||||||
// When an error occurs, it terminates the test.
|
// When an error occurs, it terminates the test.
|
||||||
func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) {
|
//nolint: unparam
|
||||||
path, err := ioutil.TempDir(dir, prefix)
|
func createTestTempDir(t *testing.T, prefix string) (string, func()) {
|
||||||
|
path, err := ioutil.TempDir("", prefix)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
return path, func() { assert.NilError(t, os.RemoveAll(path)) }
|
return path, func() { assert.NilError(t, os.RemoveAll(path)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// createTestTempFile creates a temporary file within dir with specific contents and permissions.
|
// createTestTempFile creates a temporary file within dir with specific contents and permissions.
|
||||||
// When an error occurs, it terminates the test
|
// When an error occurs, it terminates the test
|
||||||
func createTestTempFile(t *testing.T, dir, filename, contents string, perm os.FileMode) string {
|
func createTestTempFile(t *testing.T, dir, filename, contents string) string {
|
||||||
filePath := filepath.Join(dir, filename)
|
filePath := filepath.Join(dir, filename)
|
||||||
err := ioutil.WriteFile(filePath, []byte(contents), perm)
|
err := ioutil.WriteFile(filePath, []byte(contents), 0777)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
return filePath
|
return filePath
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containerd/console"
|
"github.com/containerd/console"
|
||||||
|
"github.com/containerd/containerd/platforms"
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/command/image/build"
|
"github.com/docker/cli/cli/command/image/build"
|
||||||
@@ -155,7 +156,8 @@ func runBuildBuildKit(dockerCli command.Cli, options buildOptions) error {
|
|||||||
}
|
}
|
||||||
w = f
|
w = f
|
||||||
}
|
}
|
||||||
s.Allow(filesync.NewFSSyncTarget(w))
|
output := func(map[string]string) (io.WriteCloser, error) { return w, nil }
|
||||||
|
s.Allow(filesync.NewFSSyncTarget(output))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +175,7 @@ func runBuildBuildKit(dockerCli command.Cli, options buildOptions) error {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Allow(authprovider.NewDockerAuthProvider())
|
s.Allow(authprovider.NewDockerAuthProvider(os.Stderr))
|
||||||
if len(options.secrets) > 0 {
|
if len(options.secrets) > 0 {
|
||||||
sp, err := parseSecretSpecs(options.secrets)
|
sp, err := parseSecretSpecs(options.secrets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -215,6 +217,14 @@ func runBuildBuildKit(dockerCli command.Cli, options buildOptions) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v := os.Getenv("BUILDKIT_PROGRESS"); v != "" && options.progress == "auto" {
|
||||||
|
options.progress = v
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.EqualFold(options.platform, "local") {
|
||||||
|
options.platform = platforms.DefaultString()
|
||||||
|
}
|
||||||
|
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
defer func() { // make sure the Status ends cleanly on build errors
|
defer func() { // make sure the Status ends cleanly on build errors
|
||||||
s.Close()
|
s.Close()
|
||||||
@@ -466,16 +476,13 @@ func parseSecret(value string) (*secretsprovider.FileSource, error) {
|
|||||||
func parseSSHSpecs(sl []string) (session.Attachable, error) {
|
func parseSSHSpecs(sl []string) (session.Attachable, error) {
|
||||||
configs := make([]sshprovider.AgentConfig, 0, len(sl))
|
configs := make([]sshprovider.AgentConfig, 0, len(sl))
|
||||||
for _, v := range sl {
|
for _, v := range sl {
|
||||||
c, err := parseSSH(v)
|
c := parseSSH(v)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
configs = append(configs, *c)
|
configs = append(configs, *c)
|
||||||
}
|
}
|
||||||
return sshprovider.NewSSHAgentProvider(configs)
|
return sshprovider.NewSSHAgentProvider(configs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSSH(value string) (*sshprovider.AgentConfig, error) {
|
func parseSSH(value string) *sshprovider.AgentConfig {
|
||||||
parts := strings.SplitN(value, "=", 2)
|
parts := strings.SplitN(value, "=", 2)
|
||||||
cfg := sshprovider.AgentConfig{
|
cfg := sshprovider.AgentConfig{
|
||||||
ID: parts[0],
|
ID: parts[0],
|
||||||
@@ -483,5 +490,5 @@ func parseSSH(value string) (*sshprovider.AgentConfig, error) {
|
|||||||
if len(parts) > 1 {
|
if len(parts) > 1 {
|
||||||
cfg.Paths = strings.Split(parts[1], ",")
|
cfg.Paths = strings.Split(parts[1], ",")
|
||||||
}
|
}
|
||||||
return &cfg, nil
|
return &cfg
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,16 +35,13 @@ func isSessionSupported(dockerCli command.Cli, forStream bool) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func trySession(dockerCli command.Cli, contextDir string, forStream bool) (*session.Session, error) {
|
func trySession(dockerCli command.Cli, contextDir string, forStream bool) (*session.Session, error) {
|
||||||
var s *session.Session
|
if !isSessionSupported(dockerCli, forStream) {
|
||||||
if isSessionSupported(dockerCli, forStream) {
|
return nil, nil
|
||||||
sharedKey, err := getBuildSharedKey(contextDir)
|
}
|
||||||
if err != nil {
|
sharedKey := getBuildSharedKey(contextDir)
|
||||||
return nil, errors.Wrap(err, "failed to get build shared key")
|
s, err := session.NewSession(context.Background(), filepath.Base(contextDir), sharedKey)
|
||||||
}
|
if err != nil {
|
||||||
s, err = session.NewSession(context.Background(), filepath.Base(contextDir), sharedKey)
|
return nil, errors.Wrap(err, "failed to create session")
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to create session")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
@@ -130,10 +127,10 @@ func (bw *bufferedWriter) String() string {
|
|||||||
return fmt.Sprintf("%s", bw.Writer)
|
return fmt.Sprintf("%s", bw.Writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBuildSharedKey(dir string) (string, error) {
|
func getBuildSharedKey(dir string) string {
|
||||||
// build session is hash of build dir with node based randomness
|
// build session is hash of build dir with node based randomness
|
||||||
s := sha256.Sum256([]byte(fmt.Sprintf("%s:%s", tryNodeIdentifier(), dir)))
|
s := sha256.Sum256([]byte(fmt.Sprintf("%s:%s", tryNodeIdentifier(), dir)))
|
||||||
return hex.EncodeToString(s[:]), nil
|
return hex.EncodeToString(s[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func tryNodeIdentifier() string {
|
func tryNodeIdentifier() string {
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ func TestNewPullCommandSuccess(t *testing.T) {
|
|||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
args []string
|
args []string
|
||||||
flags map[string]string
|
|
||||||
expectedTag string
|
expectedTag string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@@ -64,11 +63,8 @@ func TestNewPullCommandSuccess(t *testing.T) {
|
|||||||
expectedTag: "image:latest",
|
expectedTag: "image:latest",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "simple-quiet",
|
name: "simple-quiet",
|
||||||
args: []string{"image"},
|
args: []string{"--quiet", "image"},
|
||||||
flags: map[string]string{
|
|
||||||
"quiet": "true",
|
|
||||||
},
|
|
||||||
expectedTag: "image:latest",
|
expectedTag: "image:latest",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
Using default tag: latest
|
|
||||||
docker.io/library/image:latest
|
docker.io/library/image:latest
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package functions
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNodeDemoteErrors(t *testing.T) {
|
func TestNodeDemoteErrors(t *testing.T) {
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package functions
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
"gotest.tools/golden"
|
"gotest.tools/golden"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
is "gotest.tools/assert/cmp"
|
is "gotest.tools/assert/cmp"
|
||||||
"gotest.tools/golden"
|
"gotest.tools/golden"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNodeListErrorOnAPIFailure(t *testing.T) {
|
func TestNodeListErrorOnAPIFailure(t *testing.T) {
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNodePromoteErrors(t *testing.T) {
|
func TestNodePromoteErrors(t *testing.T) {
|
||||||
|
|||||||
@@ -8,11 +8,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
"gotest.tools/golden"
|
"gotest.tools/golden"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNodeUpdateErrors(t *testing.T) {
|
func TestNodeUpdateErrors(t *testing.T) {
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func TestListErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestList(t *testing.T) {
|
func TestList(t *testing.T) {
|
||||||
singlePluginListFunc := func(filter filters.Args) (types.PluginsListResponse, error) {
|
singlePluginListFunc := func(_ filters.Args) (types.PluginsListResponse, error) {
|
||||||
return types.PluginsListResponse{
|
return types.PluginsListResponse{
|
||||||
{
|
{
|
||||||
ID: "id-foo",
|
ID: "id-foo",
|
||||||
@@ -113,7 +113,7 @@ func TestList(t *testing.T) {
|
|||||||
"format": "{{ .ID }}",
|
"format": "{{ .ID }}",
|
||||||
},
|
},
|
||||||
golden: "plugin-list-with-no-trunc-option.golden",
|
golden: "plugin-list-with-no-trunc-option.golden",
|
||||||
listFunc: func(filter filters.Args) (types.PluginsListResponse, error) {
|
listFunc: func(_ filters.Args) (types.PluginsListResponse, error) {
|
||||||
return types.PluginsListResponse{
|
return types.PluginsListResponse{
|
||||||
{
|
{
|
||||||
ID: "xyg4z2hiSLO5yTnBJfg4OYia9gKA6Qjd",
|
ID: "xyg4z2hiSLO5yTnBJfg4OYia9gKA6Qjd",
|
||||||
@@ -142,7 +142,7 @@ func TestList(t *testing.T) {
|
|||||||
"format": "{{ .Name }}",
|
"format": "{{ .Name }}",
|
||||||
},
|
},
|
||||||
golden: "plugin-list-sort.golden",
|
golden: "plugin-list-sort.golden",
|
||||||
listFunc: func(filter filters.Args) (types.PluginsListResponse, error) {
|
listFunc: func(_ filters.Args) (types.PluginsListResponse, error) {
|
||||||
return types.PluginsListResponse{
|
return types.PluginsListResponse{
|
||||||
{
|
{
|
||||||
ID: "id-1",
|
ID: "id-1",
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func TestLoginWithCredStoreCreds(t *testing.T) {
|
|||||||
}
|
}
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
cli := (*test.FakeCli)(test.NewFakeCli(&fakeClient{}))
|
cli := test.NewFakeCli(&fakeClient{})
|
||||||
errBuf := new(bytes.Buffer)
|
errBuf := new(bytes.Buffer)
|
||||||
cli.SetErr(errBuf)
|
cli.SetErr(errBuf)
|
||||||
loginWithCredStoreCreds(ctx, cli, &tc.inputAuthConfig)
|
loginWithCredStoreCreds(ctx, cli, &tc.inputAuthConfig)
|
||||||
@@ -166,7 +166,7 @@ func TestRunLogin(t *testing.T) {
|
|||||||
|
|
||||||
if tc.inputStoredCred != nil {
|
if tc.inputStoredCred != nil {
|
||||||
cred := *tc.inputStoredCred
|
cred := *tc.inputStoredCred
|
||||||
configfile.GetCredentialsStore(cred.ServerAddress).Store(cred)
|
assert.NilError(t, configfile.GetCredentialsStore(cred.ServerAddress).Store(cred))
|
||||||
}
|
}
|
||||||
loginErr := runLogin(cli, tc.inputLoginOption)
|
loginErr := runLogin(cli, tc.inputLoginOption)
|
||||||
if tc.expectedErr != "" {
|
if tc.expectedErr != "" {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultSecretTableFormat = "table {{.ID}}\t{{.Name}}\t{{.Driver}}\t{{.CreatedAt}}\t{{.UpdatedAt}}"
|
defaultSecretTableFormat = "table {{.ID}}\t{{.Name}}\t{{.Driver}}\t{{.CreatedAt}}\t{{.UpdatedAt}}" // #nosec G101
|
||||||
secretIDHeader = "ID"
|
secretIDHeader = "ID"
|
||||||
secretCreatedHeader = "CREATED"
|
secretCreatedHeader = "CREATED"
|
||||||
secretUpdatedHeader = "UPDATED"
|
secretUpdatedHeader = "UPDATED"
|
||||||
|
|||||||
@@ -7,10 +7,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
"gotest.tools/golden"
|
"gotest.tools/golden"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
is "gotest.tools/assert/cmp"
|
is "gotest.tools/assert/cmp"
|
||||||
"gotest.tools/golden"
|
"gotest.tools/golden"
|
||||||
|
|||||||
@@ -3,11 +3,10 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeClient struct {
|
type fakeClient struct {
|
||||||
|
|||||||
@@ -202,9 +202,9 @@ func TestToServiceNetwork(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nwo := opts.NetworkOpt{}
|
nwo := opts.NetworkOpt{}
|
||||||
nwo.Set("zzz-network")
|
assert.NilError(t, nwo.Set("zzz-network"))
|
||||||
nwo.Set("mmm-network")
|
assert.NilError(t, nwo.Set("mmm-network"))
|
||||||
nwo.Set("aaa-network")
|
assert.NilError(t, nwo.Set("aaa-network"))
|
||||||
|
|
||||||
o := newServiceOptions()
|
o := newServiceOptions()
|
||||||
o.mode = "replicated"
|
o.mode = "replicated"
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func TestUpdateServiceArgs(t *testing.T) {
|
|||||||
cspec := spec.TaskTemplate.ContainerSpec
|
cspec := spec.TaskTemplate.ContainerSpec
|
||||||
cspec.Args = []string{"old", "args"}
|
cspec.Args = []string{"old", "args"}
|
||||||
|
|
||||||
updateService(nil, nil, flags, spec)
|
updateService(context.TODO(), nil, flags, spec)
|
||||||
assert.Check(t, is.DeepEqual([]string{"the", "new args"}, cspec.Args))
|
assert.Check(t, is.DeepEqual([]string{"the", "new args"}, cspec.Args))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,18 +532,18 @@ func TestUpdateReadOnly(t *testing.T) {
|
|||||||
// Update with --read-only=true, changed to true
|
// Update with --read-only=true, changed to true
|
||||||
flags := newUpdateCommand(nil).Flags()
|
flags := newUpdateCommand(nil).Flags()
|
||||||
flags.Set("read-only", "true")
|
flags.Set("read-only", "true")
|
||||||
updateService(nil, nil, flags, spec)
|
updateService(context.TODO(), nil, flags, spec)
|
||||||
assert.Check(t, cspec.ReadOnly)
|
assert.Check(t, cspec.ReadOnly)
|
||||||
|
|
||||||
// Update without --read-only, no change
|
// Update without --read-only, no change
|
||||||
flags = newUpdateCommand(nil).Flags()
|
flags = newUpdateCommand(nil).Flags()
|
||||||
updateService(nil, nil, flags, spec)
|
updateService(context.TODO(), nil, flags, spec)
|
||||||
assert.Check(t, cspec.ReadOnly)
|
assert.Check(t, cspec.ReadOnly)
|
||||||
|
|
||||||
// Update with --read-only=false, changed to false
|
// Update with --read-only=false, changed to false
|
||||||
flags = newUpdateCommand(nil).Flags()
|
flags = newUpdateCommand(nil).Flags()
|
||||||
flags.Set("read-only", "false")
|
flags.Set("read-only", "false")
|
||||||
updateService(nil, nil, flags, spec)
|
updateService(context.TODO(), nil, flags, spec)
|
||||||
assert.Check(t, !cspec.ReadOnly)
|
assert.Check(t, !cspec.ReadOnly)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -558,18 +558,18 @@ func TestUpdateInit(t *testing.T) {
|
|||||||
// Update with --init=true
|
// Update with --init=true
|
||||||
flags := newUpdateCommand(nil).Flags()
|
flags := newUpdateCommand(nil).Flags()
|
||||||
flags.Set("init", "true")
|
flags.Set("init", "true")
|
||||||
updateService(nil, nil, flags, spec)
|
updateService(context.TODO(), nil, flags, spec)
|
||||||
assert.Check(t, is.Equal(true, *cspec.Init))
|
assert.Check(t, is.Equal(true, *cspec.Init))
|
||||||
|
|
||||||
// Update without --init, no change
|
// Update without --init, no change
|
||||||
flags = newUpdateCommand(nil).Flags()
|
flags = newUpdateCommand(nil).Flags()
|
||||||
updateService(nil, nil, flags, spec)
|
updateService(context.TODO(), nil, flags, spec)
|
||||||
assert.Check(t, is.Equal(true, *cspec.Init))
|
assert.Check(t, is.Equal(true, *cspec.Init))
|
||||||
|
|
||||||
// Update with --init=false
|
// Update with --init=false
|
||||||
flags = newUpdateCommand(nil).Flags()
|
flags = newUpdateCommand(nil).Flags()
|
||||||
flags.Set("init", "false")
|
flags.Set("init", "false")
|
||||||
updateService(nil, nil, flags, spec)
|
updateService(context.TODO(), nil, flags, spec)
|
||||||
assert.Check(t, is.Equal(false, *cspec.Init))
|
assert.Check(t, is.Equal(false, *cspec.Init))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,18 +584,18 @@ func TestUpdateStopSignal(t *testing.T) {
|
|||||||
// Update with --stop-signal=SIGUSR1
|
// Update with --stop-signal=SIGUSR1
|
||||||
flags := newUpdateCommand(nil).Flags()
|
flags := newUpdateCommand(nil).Flags()
|
||||||
flags.Set("stop-signal", "SIGUSR1")
|
flags.Set("stop-signal", "SIGUSR1")
|
||||||
updateService(nil, nil, flags, spec)
|
updateService(context.TODO(), nil, flags, spec)
|
||||||
assert.Check(t, is.Equal("SIGUSR1", cspec.StopSignal))
|
assert.Check(t, is.Equal("SIGUSR1", cspec.StopSignal))
|
||||||
|
|
||||||
// Update without --stop-signal, no change
|
// Update without --stop-signal, no change
|
||||||
flags = newUpdateCommand(nil).Flags()
|
flags = newUpdateCommand(nil).Flags()
|
||||||
updateService(nil, nil, flags, spec)
|
updateService(context.TODO(), nil, flags, spec)
|
||||||
assert.Check(t, is.Equal("SIGUSR1", cspec.StopSignal))
|
assert.Check(t, is.Equal("SIGUSR1", cspec.StopSignal))
|
||||||
|
|
||||||
// Update with --stop-signal=SIGWINCH
|
// Update with --stop-signal=SIGWINCH
|
||||||
flags = newUpdateCommand(nil).Flags()
|
flags = newUpdateCommand(nil).Flags()
|
||||||
flags.Set("stop-signal", "SIGWINCH")
|
flags.Set("stop-signal", "SIGWINCH")
|
||||||
updateService(nil, nil, flags, spec)
|
updateService(context.TODO(), nil, flags, spec)
|
||||||
assert.Check(t, is.Equal("SIGWINCH", cspec.StopSignal))
|
assert.Check(t, is.Equal("SIGWINCH", cspec.StopSignal))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ func TestHandlePullSecret(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
c := c
|
||||||
t.Run(c.version, func(t *testing.T) {
|
t.Run(c.version, func(t *testing.T) {
|
||||||
conv, err := NewStackConverter(c.version)
|
conv, err := NewStackConverter(c.version)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
@@ -222,6 +223,7 @@ func TestHandlePullPolicy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
c := c
|
||||||
t.Run(c.version, func(t *testing.T) {
|
t.Run(c.version, func(t *testing.T) {
|
||||||
conv, err := NewStackConverter(c.version)
|
conv, err := NewStackConverter(c.version)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
@@ -271,6 +273,7 @@ func TestHandleInternalServiceType(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
c := c
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
res, err := fromComposeServiceConfig(composetypes.ServiceConfig{
|
res, err := fromComposeServiceConfig(composetypes.ServiceConfig{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
|
|||||||
@@ -56,16 +56,16 @@ func TestCreateChildResourcesV1Beta1(t *testing.T) {
|
|||||||
secrets))
|
secrets))
|
||||||
c, err := configs.Get("test", metav1.GetOptions{})
|
c, err := configs.Get("test", metav1.GetOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
checkOwnerReferences(t, c.ObjectMeta, "test", v1beta1.SchemeGroupVersion.String())
|
checkOwnerReferences(t, c.ObjectMeta, v1beta1.SchemeGroupVersion.String())
|
||||||
s, err := secrets.Get("test", metav1.GetOptions{})
|
s, err := secrets.Get("test", metav1.GetOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
checkOwnerReferences(t, s.ObjectMeta, "test", v1beta1.SchemeGroupVersion.String())
|
checkOwnerReferences(t, s.ObjectMeta, v1beta1.SchemeGroupVersion.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkOwnerReferences(t *testing.T, objMeta metav1.ObjectMeta, stackName, stackVersion string) {
|
func checkOwnerReferences(t *testing.T, objMeta metav1.ObjectMeta, stackVersion string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
assert.Equal(t, len(objMeta.OwnerReferences), 1)
|
assert.Equal(t, len(objMeta.OwnerReferences), 1)
|
||||||
assert.Equal(t, objMeta.OwnerReferences[0].Name, stackName)
|
assert.Equal(t, objMeta.OwnerReferences[0].Name, "test")
|
||||||
assert.Equal(t, objMeta.OwnerReferences[0].Kind, "Stack")
|
assert.Equal(t, objMeta.OwnerReferences[0].Kind, "Stack")
|
||||||
assert.Equal(t, objMeta.OwnerReferences[0].APIVersion, stackVersion)
|
assert.Equal(t, objMeta.OwnerReferences[0].APIVersion, stackVersion)
|
||||||
}
|
}
|
||||||
@@ -82,10 +82,10 @@ func TestCreateChildResourcesV1Beta2(t *testing.T) {
|
|||||||
secrets))
|
secrets))
|
||||||
c, err := configs.Get("test", metav1.GetOptions{})
|
c, err := configs.Get("test", metav1.GetOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
checkOwnerReferences(t, c.ObjectMeta, "test", v1beta2.SchemeGroupVersion.String())
|
checkOwnerReferences(t, c.ObjectMeta, v1beta2.SchemeGroupVersion.String())
|
||||||
s, err := secrets.Get("test", metav1.GetOptions{})
|
s, err := secrets.Get("test", metav1.GetOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
checkOwnerReferences(t, s.ObjectMeta, "test", v1beta2.SchemeGroupVersion.String())
|
checkOwnerReferences(t, s.ObjectMeta, v1beta2.SchemeGroupVersion.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateChildResourcesV1Alpha3(t *testing.T) {
|
func TestCreateChildResourcesV1Alpha3(t *testing.T) {
|
||||||
@@ -100,10 +100,10 @@ func TestCreateChildResourcesV1Alpha3(t *testing.T) {
|
|||||||
secrets))
|
secrets))
|
||||||
c, err := configs.Get("test", metav1.GetOptions{})
|
c, err := configs.Get("test", metav1.GetOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
checkOwnerReferences(t, c.ObjectMeta, "test", v1alpha3.SchemeGroupVersion.String())
|
checkOwnerReferences(t, c.ObjectMeta, v1alpha3.SchemeGroupVersion.String())
|
||||||
s, err := secrets.Get("test", metav1.GetOptions{})
|
s, err := secrets.Get("test", metav1.GetOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
checkOwnerReferences(t, s.ObjectMeta, "test", v1alpha3.SchemeGroupVersion.String())
|
checkOwnerReferences(t, s.ObjectMeta, v1alpha3.SchemeGroupVersion.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateChildResourcesWithStackCreationErrorV1Beta1(t *testing.T) {
|
func TestCreateChildResourcesWithStackCreationErrorV1Beta1(t *testing.T) {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func GetStacks(kubeCli *KubeCli, opts options.List) ([]*formatter.Stack, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isAllNamespacesDisabled(kubeCliConfig *configfile.KubernetesConfig) bool {
|
func isAllNamespacesDisabled(kubeCliConfig *configfile.KubernetesConfig) bool {
|
||||||
return kubeCliConfig == nil || kubeCliConfig != nil && kubeCliConfig.AllNamespaces != "disabled"
|
return kubeCliConfig == nil || kubeCliConfig.AllNamespaces != "disabled"
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStacks(kubeCli *KubeCli, opts options.List) ([]*formatter.Stack, error) {
|
func getStacks(kubeCli *KubeCli, opts options.List) ([]*formatter.Stack, error) {
|
||||||
|
|||||||
@@ -41,11 +41,13 @@ func newTestPodAndStackRepository(initialPods []apiv1.Pod, initialStacks []apiv1
|
|||||||
|
|
||||||
o := k8stesting.NewObjectTracker(scheme, codecs.UniversalDecoder())
|
o := k8stesting.NewObjectTracker(scheme, codecs.UniversalDecoder())
|
||||||
for _, obj := range initialPods {
|
for _, obj := range initialPods {
|
||||||
|
obj := obj
|
||||||
if err := o.Add(&obj); err != nil {
|
if err := o.Add(&obj); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, obj := range initialStacks {
|
for _, obj := range initialStacks {
|
||||||
|
obj := obj
|
||||||
if err := o.Add(&obj); err != nil {
|
if err := o.Add(&obj); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
// Import builders to get the builder function as package function
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
// Import builders to get the builder function as package function
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
// Import builders to get the builder function as package function
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
"gotest.tools/golden"
|
"gotest.tools/golden"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
"gotest.tools/golden"
|
"gotest.tools/golden"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,12 +7,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
"gotest.tools/golden"
|
"gotest.tools/golden"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -88,9 +88,7 @@ func runInfo(cmd *cobra.Command, dockerCli command.Cli, opts *infoOptions) error
|
|||||||
func prettyPrintInfo(dockerCli command.Cli, info info) error {
|
func prettyPrintInfo(dockerCli command.Cli, info info) error {
|
||||||
fmt.Fprintln(dockerCli.Out(), "Client:")
|
fmt.Fprintln(dockerCli.Out(), "Client:")
|
||||||
if info.ClientInfo != nil {
|
if info.ClientInfo != nil {
|
||||||
if err := prettyPrintClientInfo(dockerCli, *info.ClientInfo); err != nil {
|
prettyPrintClientInfo(dockerCli, *info.ClientInfo)
|
||||||
info.ClientErrors = append(info.ClientErrors, err.Error())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for _, err := range info.ClientErrors {
|
for _, err := range info.ClientErrors {
|
||||||
fmt.Fprintln(dockerCli.Out(), "ERROR:", err)
|
fmt.Fprintln(dockerCli.Out(), "ERROR:", err)
|
||||||
@@ -113,7 +111,7 @@ func prettyPrintInfo(dockerCli command.Cli, info info) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func prettyPrintClientInfo(dockerCli command.Cli, info clientInfo) error {
|
func prettyPrintClientInfo(dockerCli command.Cli, info clientInfo) {
|
||||||
fmt.Fprintln(dockerCli.Out(), " Debug Mode:", info.Debug)
|
fmt.Fprintln(dockerCli.Out(), " Debug Mode:", info.Debug)
|
||||||
|
|
||||||
if len(info.Plugins) > 0 {
|
if len(info.Plugins) > 0 {
|
||||||
@@ -134,8 +132,6 @@ func prettyPrintClientInfo(dockerCli command.Cli, info clientInfo) error {
|
|||||||
if len(info.Warnings) > 0 {
|
if len(info.Warnings) > 0 {
|
||||||
fmt.Fprintln(dockerCli.Err(), strings.Join(info.Warnings, "\n"))
|
fmt.Fprintln(dockerCli.Err(), strings.Join(info.Warnings, "\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: gocyclo
|
// nolint: gocyclo
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/docker/cli/cli/version"
|
"github.com/docker/cli/cli/version"
|
||||||
"github.com/docker/cli/kubernetes"
|
"github.com/docker/cli/kubernetes"
|
||||||
"github.com/docker/cli/templates"
|
"github.com/docker/cli/templates"
|
||||||
|
kubeapi "github.com/docker/compose-on-kubernetes/api"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@@ -243,7 +244,7 @@ func getKubernetesVersion(dockerCli command.Cli, kubeConfig string) *kubernetesV
|
|||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if dockerCli.CurrentContext() == "" {
|
if dockerCli.CurrentContext() == "" {
|
||||||
clientConfig = kubernetes.NewKubernetesConfig(kubeConfig)
|
clientConfig = kubeapi.NewKubernetesConfig(kubeConfig)
|
||||||
} else {
|
} else {
|
||||||
clientConfig, err = kubecontext.ConfigFromContext(dockerCli.CurrentContext(), dockerCli.ContextStore())
|
clientConfig, err = kubecontext.ConfigFromContext(dockerCli.CurrentContext(), dockerCli.ContextStore())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import (
|
|||||||
"github.com/docker/cli/cli/command/formatter"
|
"github.com/docker/cli/cli/command/formatter"
|
||||||
"github.com/docker/cli/cli/command/idresolver"
|
"github.com/docker/cli/cli/command/idresolver"
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
// Import builders to get the builder function as package function
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
|
|||||||
@@ -393,11 +393,6 @@ func TestGetSignerRolesWithKeyIDs(t *testing.T) {
|
|||||||
"bob": {"key71", "key72"},
|
"bob": {"key71", "key72"},
|
||||||
}
|
}
|
||||||
|
|
||||||
var roleWithSigs []client.RoleWithSignatures
|
|
||||||
for _, role := range roles {
|
|
||||||
roleWithSig := client.RoleWithSignatures{Role: role, Signatures: nil}
|
|
||||||
roleWithSigs = append(roleWithSigs, roleWithSig)
|
|
||||||
}
|
|
||||||
signerRoleToKeyIDs := getDelegationRoleToKeyMap(roles)
|
signerRoleToKeyIDs := getDelegationRoleToKeyMap(roles)
|
||||||
assert.Check(t, is.DeepEqual(expectedSignerRoleToKeyIDs, signerRoleToKeyIDs))
|
assert.Check(t, is.DeepEqual(expectedSignerRoleToKeyIDs, signerRoleToKeyIDs))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ func validateAndGenerateKey(streams command.Streams, keyName string, workingDir
|
|||||||
|
|
||||||
pubPEM, err := generateKeyAndOutputPubPEM(keyName, privKeyFileStore)
|
pubPEM, err := generateKeyAndOutputPubPEM(keyName, privKeyFileStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(streams.Out(), err.Error())
|
fmt.Fprint(streams.Out(), err.Error())
|
||||||
return errors.Wrapf(err, "failed to generate key for %s", keyName)
|
return errors.Wrapf(err, "failed to generate key for %s", keyName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,8 +108,7 @@ func generateKeyAndOutputPubPEM(keyName string, privKeyStore trustmanager.KeySto
|
|||||||
return pem.Block{}, err
|
return pem.Block{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
privKeyStore.AddKey(trustmanager.KeyInfo{Role: data.RoleName(keyName)}, privKey)
|
if err := privKeyStore.AddKey(trustmanager.KeyInfo{Role: data.RoleName(keyName)}, privKey); err != nil {
|
||||||
if err != nil {
|
|
||||||
return pem.Block{}, err
|
return pem.Block{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
@@ -69,12 +70,14 @@ func loadPrivKey(streams command.Streams, keyPath string, options keyLoadOptions
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getPrivKeyBytesFromPath(keyPath string) ([]byte, error) {
|
func getPrivKeyBytesFromPath(keyPath string) ([]byte, error) {
|
||||||
fileInfo, err := os.Stat(keyPath)
|
if runtime.GOOS != "windows" {
|
||||||
if err != nil {
|
fileInfo, err := os.Stat(keyPath)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
if fileInfo.Mode()&nonOwnerReadWriteMask != 0 {
|
}
|
||||||
return nil, fmt.Errorf("private key file %s must not be readable or writable by others", keyPath)
|
if fileInfo.Mode()&nonOwnerReadWriteMask != 0 {
|
||||||
|
return nil, fmt.Errorf("private key file %s must not be readable or writable by others", keyPath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
from, err := os.OpenFile(keyPath, os.O_RDONLY, notary.PrivExecPerms)
|
from, err := os.OpenFile(keyPath, os.O_RDONLY, notary.PrivExecPerms)
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ var testKeys = map[string][]byte{
|
|||||||
func TestLoadKeyFromPath(t *testing.T) {
|
func TestLoadKeyFromPath(t *testing.T) {
|
||||||
skip.If(t, runtime.GOOS == "windows")
|
skip.If(t, runtime.GOOS == "windows")
|
||||||
for keyID, keyBytes := range testKeys {
|
for keyID, keyBytes := range testKeys {
|
||||||
|
keyID, keyBytes := keyID, keyBytes
|
||||||
t.Run(fmt.Sprintf("load-key-id-%s-from-path", keyID), func(t *testing.T) {
|
t.Run(fmt.Sprintf("load-key-id-%s-from-path", keyID), func(t *testing.T) {
|
||||||
testLoadKeyFromPath(t, keyID, keyBytes)
|
testLoadKeyFromPath(t, keyID, keyBytes)
|
||||||
})
|
})
|
||||||
@@ -172,6 +173,7 @@ func testLoadKeyFromPath(t *testing.T, privKeyID string, privKeyFixture []byte)
|
|||||||
func TestLoadKeyTooPermissive(t *testing.T) {
|
func TestLoadKeyTooPermissive(t *testing.T) {
|
||||||
skip.If(t, runtime.GOOS == "windows")
|
skip.If(t, runtime.GOOS == "windows")
|
||||||
for keyID, keyBytes := range testKeys {
|
for keyID, keyBytes := range testKeys {
|
||||||
|
keyID, keyBytes := keyID, keyBytes
|
||||||
t.Run(fmt.Sprintf("load-key-id-%s-too-permissive", keyID), func(t *testing.T) {
|
t.Run(fmt.Sprintf("load-key-id-%s-too-permissive", keyID), func(t *testing.T) {
|
||||||
testLoadKeyTooPermissive(t, keyBytes)
|
testLoadKeyTooPermissive(t, keyBytes)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ func TestGetOrGenerateNotaryKey(t *testing.T) {
|
|||||||
assert.Check(t, is.DeepEqual(rootKeyA.Public(), rootKeyB.Public()))
|
assert.Check(t, is.DeepEqual(rootKeyA.Public(), rootKeyB.Public()))
|
||||||
|
|
||||||
// Now also try with a delegation key
|
// Now also try with a delegation key
|
||||||
releasesKey, err := getOrGenerateNotaryKey(notaryRepo, data.RoleName(trust.ReleasesRole))
|
releasesKey, err := getOrGenerateNotaryKey(notaryRepo, trust.ReleasesRole)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, releasesKey != nil)
|
assert.Check(t, releasesKey != nil)
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func PromptForConfirmation(ins io.Reader, outs io.Writer, message string) bool {
|
|||||||
}
|
}
|
||||||
message += " [y/N] "
|
message += " [y/N] "
|
||||||
|
|
||||||
fmt.Fprintf(outs, message)
|
_, _ = fmt.Fprint(outs, message)
|
||||||
|
|
||||||
// On Windows, force the use of the regular OS stdin stream.
|
// On Windows, force the use of the regular OS stdin stream.
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
@@ -130,7 +130,7 @@ func AddPlatformFlag(flags *pflag.FlagSet, target *string) {
|
|||||||
|
|
||||||
// ValidateOutputPath validates the output paths of the `export` and `save` commands.
|
// ValidateOutputPath validates the output paths of the `export` and `save` commands.
|
||||||
func ValidateOutputPath(path string) error {
|
func ValidateOutputPath(path string) error {
|
||||||
dir := filepath.Dir(path)
|
dir := filepath.Dir(filepath.Clean(path))
|
||||||
if dir != "" && dir != "." {
|
if dir != "" && dir != "." {
|
||||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||||
return errors.Errorf("invalid output path: directory %q does not exist", dir)
|
return errors.Errorf("invalid output path: directory %q does not exist", dir)
|
||||||
@@ -139,6 +139,10 @@ func ValidateOutputPath(path string) error {
|
|||||||
// check whether `path` points to a regular file
|
// check whether `path` points to a regular file
|
||||||
// (if the path exists and doesn't point to a directory)
|
// (if the path exists and doesn't point to a directory)
|
||||||
if fileInfo, err := os.Stat(path); !os.IsNotExist(err) {
|
if fileInfo, err := os.Stat(path); !os.IsNotExist(err) {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if fileInfo.Mode().IsDir() || fileInfo.Mode().IsRegular() {
|
if fileInfo.Mode().IsDir() || fileInfo.Mode().IsRegular() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,3 +35,39 @@ func TestStringSliceReplaceAt(t *testing.T) {
|
|||||||
assert.Assert(t, !ok)
|
assert.Assert(t, !ok)
|
||||||
assert.DeepEqual(t, []string{"foo"}, out)
|
assert.DeepEqual(t, []string{"foo"}, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateOutputPath(t *testing.T) {
|
||||||
|
basedir, err := ioutil.TempDir("", "TestValidateOutputPath")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
defer os.RemoveAll(basedir)
|
||||||
|
dir := filepath.Join(basedir, "dir")
|
||||||
|
notexist := filepath.Join(basedir, "notexist")
|
||||||
|
err = os.MkdirAll(dir, 0755)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
file := filepath.Join(dir, "file")
|
||||||
|
err = ioutil.WriteFile(file, []byte("hi"), 0644)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
var testcases = []struct {
|
||||||
|
path string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{basedir, nil},
|
||||||
|
{file, nil},
|
||||||
|
{dir, nil},
|
||||||
|
{dir + string(os.PathSeparator), nil},
|
||||||
|
{notexist, nil},
|
||||||
|
{notexist + string(os.PathSeparator), nil},
|
||||||
|
{filepath.Join(notexist, "file"), errors.New("does not exist")},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testcase := range testcases {
|
||||||
|
t.Run(testcase.path, func(t *testing.T) {
|
||||||
|
err := ValidateOutputPath(testcase.path)
|
||||||
|
if testcase.err == nil {
|
||||||
|
assert.NilError(t, err)
|
||||||
|
} else {
|
||||||
|
assert.ErrorContains(t, err, testcase.err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
"gotest.tools/golden"
|
"gotest.tools/golden"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,12 +6,11 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
|
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
volumetypes "github.com/docker/docker/api/types/volume"
|
volumetypes "github.com/docker/docker/api/types/volume"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
// Import builders to get the builder function as package function
|
|
||||||
. "github.com/docker/cli/internal/test/builders"
|
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
"gotest.tools/golden"
|
"gotest.tools/golden"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ func Secrets(namespace Namespace, secrets map[string]composetypes.SecretConfig)
|
|||||||
var obj swarmFileObject
|
var obj swarmFileObject
|
||||||
var err error
|
var err error
|
||||||
if secret.Driver != "" {
|
if secret.Driver != "" {
|
||||||
obj, err = driverObjectConfig(namespace, name, composetypes.FileObjectConfig(secret))
|
obj = driverObjectConfig(namespace, name, composetypes.FileObjectConfig(secret))
|
||||||
} else {
|
} else {
|
||||||
obj, err = fileObjectConfig(namespace, name, composetypes.FileObjectConfig(secret))
|
obj, err = fileObjectConfig(namespace, name, composetypes.FileObjectConfig(secret))
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,7 @@ type swarmFileObject struct {
|
|||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func driverObjectConfig(namespace Namespace, name string, obj composetypes.FileObjectConfig) (swarmFileObject, error) {
|
func driverObjectConfig(namespace Namespace, name string, obj composetypes.FileObjectConfig) swarmFileObject {
|
||||||
if obj.Name != "" {
|
if obj.Name != "" {
|
||||||
name = obj.Name
|
name = obj.Name
|
||||||
} else {
|
} else {
|
||||||
@@ -174,7 +174,7 @@ func driverObjectConfig(namespace Namespace, name string, obj composetypes.FileO
|
|||||||
Labels: AddStackLabel(namespace, obj.Labels),
|
Labels: AddStackLabel(namespace, obj.Labels),
|
||||||
},
|
},
|
||||||
Data: []byte{},
|
Data: []byte{},
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileObjectConfig(namespace Namespace, name string, obj composetypes.FileObjectConfig) (swarmFileObject, error) {
|
func fileObjectConfig(namespace Namespace, name string, obj composetypes.FileObjectConfig) (swarmFileObject, error) {
|
||||||
|
|||||||
@@ -66,11 +66,7 @@ func Service(
|
|||||||
configs []*swarm.ConfigReference,
|
configs []*swarm.ConfigReference,
|
||||||
) (swarm.ServiceSpec, error) {
|
) (swarm.ServiceSpec, error) {
|
||||||
name := namespace.Scope(service.Name)
|
name := namespace.Scope(service.Name)
|
||||||
|
endpoint := convertEndpointSpec(service.Deploy.EndpointMode, service.Ports)
|
||||||
endpoint, err := convertEndpointSpec(service.Deploy.EndpointMode, service.Ports)
|
|
||||||
if err != nil {
|
|
||||||
return swarm.ServiceSpec{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
mode, err := convertDeployMode(service.Deploy.Mode, service.Deploy.Replicas)
|
mode, err := convertDeployMode(service.Deploy.Mode, service.Deploy.Replicas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -103,10 +99,7 @@ func Service(
|
|||||||
return swarm.ServiceSpec{}, err
|
return swarm.ServiceSpec{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsConfig, err := convertDNSConfig(service.DNS, service.DNSSearch)
|
dnsConfig := convertDNSConfig(service.DNS, service.DNSSearch)
|
||||||
if err != nil {
|
|
||||||
return swarm.ServiceSpec{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var privileges swarm.Privileges
|
var privileges swarm.Privileges
|
||||||
privileges.CredentialSpec, err = convertCredentialSpec(
|
privileges.CredentialSpec, err = convertCredentialSpec(
|
||||||
@@ -575,7 +568,7 @@ func convertResources(source composetypes.Resources) (*swarm.ResourceRequirement
|
|||||||
return resources, nil
|
return resources, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertEndpointSpec(endpointMode string, source []composetypes.ServicePortConfig) (*swarm.EndpointSpec, error) {
|
func convertEndpointSpec(endpointMode string, source []composetypes.ServicePortConfig) *swarm.EndpointSpec {
|
||||||
portConfigs := []swarm.PortConfig{}
|
portConfigs := []swarm.PortConfig{}
|
||||||
for _, port := range source {
|
for _, port := range source {
|
||||||
portConfig := swarm.PortConfig{
|
portConfig := swarm.PortConfig{
|
||||||
@@ -594,7 +587,7 @@ func convertEndpointSpec(endpointMode string, source []composetypes.ServicePortC
|
|||||||
return &swarm.EndpointSpec{
|
return &swarm.EndpointSpec{
|
||||||
Mode: swarm.ResolutionMode(strings.ToLower(endpointMode)),
|
Mode: swarm.ResolutionMode(strings.ToLower(endpointMode)),
|
||||||
Ports: portConfigs,
|
Ports: portConfigs,
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertEnvironment(source map[string]*string) []string {
|
func convertEnvironment(source map[string]*string) []string {
|
||||||
@@ -629,14 +622,14 @@ func convertDeployMode(mode string, replicas *uint64) (swarm.ServiceMode, error)
|
|||||||
return serviceMode, nil
|
return serviceMode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertDNSConfig(DNS []string, DNSSearch []string) (*swarm.DNSConfig, error) {
|
func convertDNSConfig(DNS []string, DNSSearch []string) *swarm.DNSConfig {
|
||||||
if DNS != nil || DNSSearch != nil {
|
if DNS != nil || DNSSearch != nil {
|
||||||
return &swarm.DNSConfig{
|
return &swarm.DNSConfig{
|
||||||
Nameservers: DNS,
|
Nameservers: DNS,
|
||||||
Search: DNSSearch,
|
Search: DNSSearch,
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertCredentialSpec(namespace Namespace, spec composetypes.CredentialSpecConfig, refs []*swarm.ConfigReference) (*swarm.CredentialSpec, error) {
|
func convertCredentialSpec(namespace Namespace, spec composetypes.CredentialSpecConfig, refs []*swarm.ConfigReference) (*swarm.CredentialSpec, error) {
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ func TestConvertEndpointSpec(t *testing.T) {
|
|||||||
Published: 80,
|
Published: 80,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
endpoint, err := convertEndpointSpec("vip", source)
|
endpoint := convertEndpointSpec("vip", source)
|
||||||
|
|
||||||
expected := swarm.EndpointSpec{
|
expected := swarm.EndpointSpec{
|
||||||
Mode: swarm.ResolutionMode(strings.ToLower("vip")),
|
Mode: swarm.ResolutionMode(strings.ToLower("vip")),
|
||||||
@@ -195,7 +195,6 @@ func TestConvertEndpointSpec(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NilError(t, err)
|
|
||||||
assert.Check(t, is.DeepEqual(expected, *endpoint))
|
assert.Check(t, is.DeepEqual(expected, *endpoint))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,13 +270,11 @@ func TestConvertServiceNetworksCustomDefault(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.DeepEqual(expected, []swarm.NetworkAttachmentConfig(configs)))
|
assert.Check(t, is.DeepEqual(expected, configs))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConvertDNSConfigEmpty(t *testing.T) {
|
func TestConvertDNSConfigEmpty(t *testing.T) {
|
||||||
dnsConfig, err := convertDNSConfig(nil, nil)
|
dnsConfig := convertDNSConfig(nil, nil)
|
||||||
|
|
||||||
assert.NilError(t, err)
|
|
||||||
assert.Check(t, is.DeepEqual((*swarm.DNSConfig)(nil), dnsConfig))
|
assert.Check(t, is.DeepEqual((*swarm.DNSConfig)(nil), dnsConfig))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,8 +284,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestConvertDNSConfigAll(t *testing.T) {
|
func TestConvertDNSConfigAll(t *testing.T) {
|
||||||
dnsConfig, err := convertDNSConfig(nameservers, search)
|
dnsConfig := convertDNSConfig(nameservers, search)
|
||||||
assert.NilError(t, err)
|
|
||||||
assert.Check(t, is.DeepEqual(&swarm.DNSConfig{
|
assert.Check(t, is.DeepEqual(&swarm.DNSConfig{
|
||||||
Nameservers: nameservers,
|
Nameservers: nameservers,
|
||||||
Search: search,
|
Search: search,
|
||||||
@@ -296,8 +292,7 @@ func TestConvertDNSConfigAll(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestConvertDNSConfigNameservers(t *testing.T) {
|
func TestConvertDNSConfigNameservers(t *testing.T) {
|
||||||
dnsConfig, err := convertDNSConfig(nameservers, nil)
|
dnsConfig := convertDNSConfig(nameservers, nil)
|
||||||
assert.NilError(t, err)
|
|
||||||
assert.Check(t, is.DeepEqual(&swarm.DNSConfig{
|
assert.Check(t, is.DeepEqual(&swarm.DNSConfig{
|
||||||
Nameservers: nameservers,
|
Nameservers: nameservers,
|
||||||
Search: nil,
|
Search: nil,
|
||||||
@@ -305,8 +300,7 @@ func TestConvertDNSConfigNameservers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestConvertDNSConfigSearch(t *testing.T) {
|
func TestConvertDNSConfigSearch(t *testing.T) {
|
||||||
dnsConfig, err := convertDNSConfig(nil, search)
|
dnsConfig := convertDNSConfig(nil, search)
|
||||||
assert.NilError(t, err)
|
|
||||||
assert.Check(t, is.DeepEqual(&swarm.DNSConfig{
|
assert.Check(t, is.DeepEqual(&swarm.DNSConfig{
|
||||||
Nameservers: nil,
|
Nameservers: nil,
|
||||||
Search: search,
|
Search: search,
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ func Interpolate(config map[string]interface{}, opts Options) (map[string]interf
|
|||||||
|
|
||||||
func recursiveInterpolate(value interface{}, path Path, opts Options) (interface{}, error) {
|
func recursiveInterpolate(value interface{}, path Path, opts Options) (interface{}, error) {
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
newValue, err := opts.Substitute(value, template.Mapping(opts.LookupValue))
|
newValue, err := opts.Substitute(value, template.Mapping(opts.LookupValue))
|
||||||
if err != nil || newValue == value {
|
if err != nil || newValue == value {
|
||||||
@@ -91,7 +90,6 @@ func recursiveInterpolate(value interface{}, path Path, opts Options) (interface
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
return value, nil
|
return value, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ var interpolateTypeCastMapping = map[interp.Path]interp.Cast{
|
|||||||
servicePath("deploy", "replicas"): toInt,
|
servicePath("deploy", "replicas"): toInt,
|
||||||
servicePath("deploy", "update_config", "parallelism"): toInt,
|
servicePath("deploy", "update_config", "parallelism"): toInt,
|
||||||
servicePath("deploy", "update_config", "max_failure_ratio"): toFloat,
|
servicePath("deploy", "update_config", "max_failure_ratio"): toFloat,
|
||||||
|
servicePath("deploy", "rollback_config", "parallelism"): toInt,
|
||||||
|
servicePath("deploy", "rollback_config", "max_failure_ratio"): toFloat,
|
||||||
servicePath("deploy", "restart_policy", "max_attempts"): toInt,
|
servicePath("deploy", "restart_policy", "max_attempts"): toInt,
|
||||||
|
servicePath("deploy", "placement", "max_replicas_per_node"): toInt,
|
||||||
servicePath("ports", interp.PathMatchList, "target"): toInt,
|
servicePath("ports", interp.PathMatchList, "target"): toInt,
|
||||||
servicePath("ports", interp.PathMatchList, "published"): toInt,
|
servicePath("ports", interp.PathMatchList, "published"): toInt,
|
||||||
servicePath("ulimits", interp.PathMatchAll): toInt,
|
servicePath("ulimits", interp.PathMatchAll): toInt,
|
||||||
|
|||||||
@@ -32,6 +32,14 @@ type Options struct {
|
|||||||
SkipInterpolation bool
|
SkipInterpolation bool
|
||||||
// Interpolation options
|
// Interpolation options
|
||||||
Interpolate *interp.Options
|
Interpolate *interp.Options
|
||||||
|
// Discard 'env_file' entries after resolving to 'environment' section
|
||||||
|
discardEnvFiles bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDiscardEnvFiles sets the Options to discard the `env_file` section after resolving to
|
||||||
|
// the `environment` section
|
||||||
|
func WithDiscardEnvFiles(opts *Options) {
|
||||||
|
opts.discardEnvFiles = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseYAML reads the bytes from a file, parses the bytes into a mapping
|
// ParseYAML reads the bytes from a file, parses the bytes into a mapping
|
||||||
@@ -105,6 +113,11 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cfg.Filename = file.Filename
|
cfg.Filename = file.Filename
|
||||||
|
if opts.discardEnvFiles {
|
||||||
|
for i := range cfg.Services {
|
||||||
|
cfg.Services[i].EnvFile = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
configs = append(configs, cfg)
|
configs = append(configs, cfg)
|
||||||
}
|
}
|
||||||
@@ -284,10 +297,13 @@ func Transform(source interface{}, target interface{}, additionalTransformers ..
|
|||||||
return decoder.Decode(source)
|
return decoder.Decode(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransformerFunc defines a function to perform the actual transformation
|
||||||
|
type TransformerFunc func(interface{}) (interface{}, error)
|
||||||
|
|
||||||
// Transformer defines a map to type transformer
|
// Transformer defines a map to type transformer
|
||||||
type Transformer struct {
|
type Transformer struct {
|
||||||
TypeOf reflect.Type
|
TypeOf reflect.Type
|
||||||
Func func(interface{}) (interface{}, error)
|
Func TransformerFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTransformHook(additionalTransformers ...Transformer) mapstructure.DecodeHookFuncType {
|
func createTransformHook(additionalTransformers ...Transformer) mapstructure.DecodeHookFuncType {
|
||||||
@@ -479,12 +495,13 @@ func resolveVolumePaths(volumes []types.ServiceVolumeConfig, workingDir string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
filePath := expandUser(volume.Source, lookupEnv)
|
filePath := expandUser(volume.Source, lookupEnv)
|
||||||
// Check for a Unix absolute path first, to handle a Windows client
|
// Check if source is an absolute path (either Unix or Windows), to
|
||||||
// with a Unix daemon. This handles a Windows client connecting to a
|
// handle a Windows client with a Unix daemon or vice-versa.
|
||||||
// Unix daemon. Note that this is not required for Docker for Windows
|
//
|
||||||
// when specifying a local Windows path, because Docker for Windows
|
// Note that this is not required for Docker for Windows when specifying
|
||||||
// translates the Windows path into a valid path within the VM.
|
// a local Windows path, because Docker for Windows translates the Windows
|
||||||
if !path.IsAbs(filePath) {
|
// path into a valid path within the VM.
|
||||||
|
if !path.IsAbs(filePath) && !isAbs(filePath) {
|
||||||
filePath = absPath(workingDir, filePath)
|
filePath = absPath(workingDir, filePath)
|
||||||
}
|
}
|
||||||
volume.Source = filePath
|
volume.Source = filePath
|
||||||
@@ -670,7 +687,7 @@ func absPath(workingDir string, filePath string) string {
|
|||||||
return filepath.Join(workingDir, filePath)
|
return filepath.Join(workingDir, filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformMapStringString(data interface{}) (interface{}, error) {
|
var transformMapStringString TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||||
switch value := data.(type) {
|
switch value := data.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
return toMapStringString(value, false), nil
|
return toMapStringString(value, false), nil
|
||||||
@@ -681,7 +698,7 @@ func transformMapStringString(data interface{}) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformExternal(data interface{}) (interface{}, error) {
|
var transformExternal TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||||
switch value := data.(type) {
|
switch value := data.(type) {
|
||||||
case bool:
|
case bool:
|
||||||
return map[string]interface{}{"external": value}, nil
|
return map[string]interface{}{"external": value}, nil
|
||||||
@@ -692,7 +709,7 @@ func transformExternal(data interface{}) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformServicePort(data interface{}) (interface{}, error) {
|
var transformServicePort TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||||
switch entries := data.(type) {
|
switch entries := data.(type) {
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
// We process the list instead of individual items here.
|
// We process the list instead of individual items here.
|
||||||
@@ -725,7 +742,7 @@ func transformServicePort(data interface{}) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformStringSourceMap(data interface{}) (interface{}, error) {
|
var transformStringSourceMap TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||||
switch value := data.(type) {
|
switch value := data.(type) {
|
||||||
case string:
|
case string:
|
||||||
return map[string]interface{}{"source": value}, nil
|
return map[string]interface{}{"source": value}, nil
|
||||||
@@ -736,7 +753,7 @@ func transformStringSourceMap(data interface{}) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformBuildConfig(data interface{}) (interface{}, error) {
|
var transformBuildConfig TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||||
switch value := data.(type) {
|
switch value := data.(type) {
|
||||||
case string:
|
case string:
|
||||||
return map[string]interface{}{"context": value}, nil
|
return map[string]interface{}{"context": value}, nil
|
||||||
@@ -747,7 +764,7 @@ func transformBuildConfig(data interface{}) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformServiceVolumeConfig(data interface{}) (interface{}, error) {
|
var transformServiceVolumeConfig TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||||
switch value := data.(type) {
|
switch value := data.(type) {
|
||||||
case string:
|
case string:
|
||||||
return ParseVolume(value)
|
return ParseVolume(value)
|
||||||
@@ -758,7 +775,7 @@ func transformServiceVolumeConfig(data interface{}) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformServiceNetworkMap(value interface{}) (interface{}, error) {
|
var transformServiceNetworkMap TransformerFunc = func(value interface{}) (interface{}, error) {
|
||||||
if list, ok := value.([]interface{}); ok {
|
if list, ok := value.([]interface{}); ok {
|
||||||
mapValue := map[interface{}]interface{}{}
|
mapValue := map[interface{}]interface{}{}
|
||||||
for _, name := range list {
|
for _, name := range list {
|
||||||
@@ -769,7 +786,7 @@ func transformServiceNetworkMap(value interface{}) (interface{}, error) {
|
|||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformStringOrNumberList(value interface{}) (interface{}, error) {
|
var transformStringOrNumberList TransformerFunc = func(value interface{}) (interface{}, error) {
|
||||||
list := value.([]interface{})
|
list := value.([]interface{})
|
||||||
result := make([]string, len(list))
|
result := make([]string, len(list))
|
||||||
for i, item := range list {
|
for i, item := range list {
|
||||||
@@ -778,7 +795,7 @@ func transformStringOrNumberList(value interface{}) (interface{}, error) {
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformStringList(data interface{}) (interface{}, error) {
|
var transformStringList TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||||
switch value := data.(type) {
|
switch value := data.(type) {
|
||||||
case string:
|
case string:
|
||||||
return []string{value}, nil
|
return []string{value}, nil
|
||||||
@@ -789,13 +806,13 @@ func transformStringList(data interface{}) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformMappingOrListFunc(sep string, allowNil bool) func(interface{}) (interface{}, error) {
|
func transformMappingOrListFunc(sep string, allowNil bool) TransformerFunc {
|
||||||
return func(data interface{}) (interface{}, error) {
|
return func(data interface{}) (interface{}, error) {
|
||||||
return transformMappingOrList(data, sep, allowNil), nil
|
return transformMappingOrList(data, sep, allowNil), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformListOrMappingFunc(sep string, allowNil bool) func(interface{}) (interface{}, error) {
|
func transformListOrMappingFunc(sep string, allowNil bool) TransformerFunc {
|
||||||
return func(data interface{}) (interface{}, error) {
|
return func(data interface{}) (interface{}, error) {
|
||||||
return transformListOrMapping(data, sep, allowNil), nil
|
return transformListOrMapping(data, sep, allowNil), nil
|
||||||
}
|
}
|
||||||
@@ -834,14 +851,14 @@ func transformMappingOrList(mappingOrList interface{}, sep string, allowNil bool
|
|||||||
panic(errors.Errorf("expected a map or a list, got %T: %#v", mappingOrList, mappingOrList))
|
panic(errors.Errorf("expected a map or a list, got %T: %#v", mappingOrList, mappingOrList))
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformShellCommand(value interface{}) (interface{}, error) {
|
var transformShellCommand TransformerFunc = func(value interface{}) (interface{}, error) {
|
||||||
if str, ok := value.(string); ok {
|
if str, ok := value.(string); ok {
|
||||||
return shellwords.Parse(str)
|
return shellwords.Parse(str)
|
||||||
}
|
}
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformHealthCheckTest(data interface{}) (interface{}, error) {
|
var transformHealthCheckTest TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||||
switch value := data.(type) {
|
switch value := data.(type) {
|
||||||
case string:
|
case string:
|
||||||
return append([]string{"CMD-SHELL"}, value), nil
|
return append([]string{"CMD-SHELL"}, value), nil
|
||||||
@@ -852,7 +869,7 @@ func transformHealthCheckTest(data interface{}) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformSize(value interface{}) (interface{}, error) {
|
var transformSize TransformerFunc = func(value interface{}) (interface{}, error) {
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case int:
|
case int:
|
||||||
return int64(value), nil
|
return int64(value), nil
|
||||||
@@ -862,7 +879,7 @@ func transformSize(value interface{}) (interface{}, error) {
|
|||||||
panic(errors.Errorf("invalid type for size %T", value))
|
panic(errors.Errorf("invalid type for size %T", value))
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformStringToDuration(value interface{}) (interface{}, error) {
|
var transformStringToDuration TransformerFunc = func(value interface{}) (interface{}, error) {
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
d, err := time.ParseDuration(value)
|
d, err := time.ParseDuration(value)
|
||||||
|
|||||||
@@ -582,7 +582,7 @@ volumes:
|
|||||||
|
|
||||||
func TestLoadWithInterpolationCastFull(t *testing.T) {
|
func TestLoadWithInterpolationCastFull(t *testing.T) {
|
||||||
dict, err := ParseYAML([]byte(`
|
dict, err := ParseYAML([]byte(`
|
||||||
version: "3.4"
|
version: "3.8"
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
configs:
|
configs:
|
||||||
@@ -599,8 +599,13 @@ services:
|
|||||||
update_config:
|
update_config:
|
||||||
parallelism: $theint
|
parallelism: $theint
|
||||||
max_failure_ratio: $thefloat
|
max_failure_ratio: $thefloat
|
||||||
|
rollback_config:
|
||||||
|
parallelism: $theint
|
||||||
|
max_failure_ratio: $thefloat
|
||||||
restart_policy:
|
restart_policy:
|
||||||
max_attempts: $theint
|
max_attempts: $theint
|
||||||
|
placement:
|
||||||
|
max_replicas_per_node: $theint
|
||||||
ports:
|
ports:
|
||||||
- $theint
|
- $theint
|
||||||
- "34567"
|
- "34567"
|
||||||
@@ -649,7 +654,7 @@ networks:
|
|||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
expected := &types.Config{
|
expected := &types.Config{
|
||||||
Filename: "filename.yml",
|
Filename: "filename.yml",
|
||||||
Version: "3.4",
|
Version: "3.8",
|
||||||
Services: []types.ServiceConfig{
|
Services: []types.ServiceConfig{
|
||||||
{
|
{
|
||||||
Name: "web",
|
Name: "web",
|
||||||
@@ -675,9 +680,16 @@ networks:
|
|||||||
Parallelism: uint64Ptr(555),
|
Parallelism: uint64Ptr(555),
|
||||||
MaxFailureRatio: 3.14,
|
MaxFailureRatio: 3.14,
|
||||||
},
|
},
|
||||||
|
RollbackConfig: &types.UpdateConfig{
|
||||||
|
Parallelism: uint64Ptr(555),
|
||||||
|
MaxFailureRatio: 3.14,
|
||||||
|
},
|
||||||
RestartPolicy: &types.RestartPolicy{
|
RestartPolicy: &types.RestartPolicy{
|
||||||
MaxAttempts: uint64Ptr(555),
|
MaxAttempts: uint64Ptr(555),
|
||||||
},
|
},
|
||||||
|
Placement: types.Placement{
|
||||||
|
MaxReplicas: 555,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Ports: []types.ServicePortConfig{
|
Ports: []types.ServicePortConfig{
|
||||||
{Target: 555, Mode: "ingress", Protocol: "tcp"},
|
{Target: 555, Mode: "ingress", Protocol: "tcp"},
|
||||||
@@ -752,6 +764,38 @@ services:
|
|||||||
assert.Check(t, is.DeepEqual([]string{"build", "links", "pid"}, unsupported))
|
assert.Check(t, is.DeepEqual([]string{"build", "links", "pid"}, unsupported))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDiscardEnvFileOption(t *testing.T) {
|
||||||
|
dict, err := ParseYAML([]byte(`version: "3"
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx
|
||||||
|
env_file:
|
||||||
|
- example1.env
|
||||||
|
- example2.env
|
||||||
|
`))
|
||||||
|
expectedEnvironmentMap := types.MappingWithEquals{
|
||||||
|
"FOO": strPtr("foo_from_env_file"),
|
||||||
|
"BAZ": strPtr("baz_from_env_file"),
|
||||||
|
"BAR": strPtr("bar_from_env_file_2"), // Original value is overwritten by example2.env
|
||||||
|
"QUX": strPtr("quz_from_env_file_2"),
|
||||||
|
}
|
||||||
|
assert.NilError(t, err)
|
||||||
|
configDetails := buildConfigDetails(dict, nil)
|
||||||
|
|
||||||
|
// Default behavior keeps the `env_file` entries
|
||||||
|
configWithEnvFiles, err := Load(configDetails)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, configWithEnvFiles.Services[0].EnvFile, types.StringList{"example1.env",
|
||||||
|
"example2.env"})
|
||||||
|
assert.DeepEqual(t, configWithEnvFiles.Services[0].Environment, expectedEnvironmentMap)
|
||||||
|
|
||||||
|
// Custom behavior removes the `env_file` entries
|
||||||
|
configWithoutEnvFiles, err := Load(configDetails, WithDiscardEnvFiles)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, configWithoutEnvFiles.Services[0].EnvFile, types.StringList(nil))
|
||||||
|
assert.DeepEqual(t, configWithoutEnvFiles.Services[0].Environment, expectedEnvironmentMap)
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuildProperties(t *testing.T) {
|
func TestBuildProperties(t *testing.T) {
|
||||||
dict, err := ParseYAML([]byte(`
|
dict, err := ParseYAML([]byte(`
|
||||||
version: "3"
|
version: "3"
|
||||||
@@ -978,6 +1022,84 @@ services:
|
|||||||
assert.Error(t, err, `invalid mount config for type "bind": field Source must not be empty`)
|
assert.Error(t, err, `invalid mount config for type "bind": field Source must not be empty`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadBindMountSourceIsWindowsAbsolute(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
doc string
|
||||||
|
yaml string
|
||||||
|
expected types.ServiceVolumeConfig
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
doc: "Z-drive lowercase",
|
||||||
|
yaml: `
|
||||||
|
version: '3.3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
windows:
|
||||||
|
image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: z:\
|
||||||
|
target: c:\data
|
||||||
|
`,
|
||||||
|
expected: types.ServiceVolumeConfig{Type: "bind", Source: `z:\`, Target: `c:\data`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "Z-drive uppercase",
|
||||||
|
yaml: `
|
||||||
|
version: '3.3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
windows:
|
||||||
|
image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: Z:\
|
||||||
|
target: C:\data
|
||||||
|
`,
|
||||||
|
expected: types.ServiceVolumeConfig{Type: "bind", Source: `Z:\`, Target: `C:\data`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "Z-drive subdirectory",
|
||||||
|
yaml: `
|
||||||
|
version: '3.3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
windows:
|
||||||
|
image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: Z:\some-dir
|
||||||
|
target: C:\data
|
||||||
|
`,
|
||||||
|
expected: types.ServiceVolumeConfig{Type: "bind", Source: `Z:\some-dir`, Target: `C:\data`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "forward-slashes",
|
||||||
|
yaml: `
|
||||||
|
version: '3.3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: app:latest
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: /z/some-dir
|
||||||
|
target: /c/data
|
||||||
|
`,
|
||||||
|
expected: types.ServiceVolumeConfig{Type: "bind", Source: `/z/some-dir`, Target: `/c/data`},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.doc, func(t *testing.T) {
|
||||||
|
config, err := loadYAML(tc.yaml)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Check(t, is.Len(config.Services[0].Volumes, 1))
|
||||||
|
assert.Check(t, is.DeepEqual(tc.expected, config.Services[0].Volumes[0]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoadBindMountWithSource(t *testing.T) {
|
func TestLoadBindMountWithSource(t *testing.T) {
|
||||||
config, err := loadYAML(`
|
config, err := loadYAML(`
|
||||||
version: "3.5"
|
version: "3.5"
|
||||||
@@ -1466,6 +1588,7 @@ services:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, testcase := range testcases {
|
for _, testcase := range testcases {
|
||||||
|
testcase := testcase
|
||||||
t.Run(testcase.doc, func(t *testing.T) {
|
t.Run(testcase.doc, func(t *testing.T) {
|
||||||
config, err := loadYAML(testcase.yaml)
|
config, err := loadYAML(testcase.yaml)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig,
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for name, overrideService := range overrideServices {
|
for name, overrideService := range overrideServices {
|
||||||
|
overrideService := overrideService
|
||||||
if baseService, ok := baseServices[name]; ok {
|
if baseService, ok := baseServices[name]; ok {
|
||||||
if err := mergo.Merge(&baseService, &overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(specials)); err != nil {
|
if err := mergo.Merge(&baseService, &overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(specials)); err != nil {
|
||||||
return base, errors.Wrapf(err, "cannot merge service %s", name)
|
return base, errors.Wrapf(err, "cannot merge service %s", name)
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package loader
|
||||||
|
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
// https://github.com/golang/go/blob/master/LICENSE
|
||||||
|
|
||||||
|
// This file contains utilities to check for Windows absolute paths on Linux.
|
||||||
|
// The code in this file was largely copied from the Golang filepath package
|
||||||
|
// https://github.com/golang/go/blob/1d0e94b1e13d5e8a323a63cd1cc1ef95290c9c36/src/path/filepath/path_windows.go#L12-L65
|
||||||
|
|
||||||
|
func isSlash(c uint8) bool {
|
||||||
|
return c == '\\' || c == '/'
|
||||||
|
}
|
||||||
|
|
||||||
|
// isAbs reports whether the path is a Windows absolute path.
|
||||||
|
func isAbs(path string) (b bool) {
|
||||||
|
l := volumeNameLen(path)
|
||||||
|
if l == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
path = path[l:]
|
||||||
|
if path == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return isSlash(path[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// volumeNameLen returns length of the leading volume name on Windows.
|
||||||
|
// It returns 0 elsewhere.
|
||||||
|
// nolint: gocyclo
|
||||||
|
func volumeNameLen(path string) int {
|
||||||
|
if len(path) < 2 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// with drive letter
|
||||||
|
c := path[0]
|
||||||
|
if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
// is it UNC? https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
||||||
|
if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
|
||||||
|
!isSlash(path[2]) && path[2] != '.' {
|
||||||
|
// first, leading `\\` and next shouldn't be `\`. its server name.
|
||||||
|
for n := 3; n < l-1; n++ {
|
||||||
|
// second, next '\' shouldn't be repeated.
|
||||||
|
if isSlash(path[n]) {
|
||||||
|
n++
|
||||||
|
// third, following something characters. its share name.
|
||||||
|
if !isSlash(path[n]) {
|
||||||
|
if path[n] == '.' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for ; n < l; n++ {
|
||||||
|
if isSlash(path[n]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package loader
|
||||||
|
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
// https://github.com/golang/go/blob/master/LICENSE
|
||||||
|
|
||||||
|
// The code in this file was copied from the Golang filepath package with some
|
||||||
|
// small modifications to run it on non-Windows platforms.
|
||||||
|
// https://github.com/golang/go/blob/1d0e94b1e13d5e8a323a63cd1cc1ef95290c9c36/src/path/filepath/path_test.go#L711-L763
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
type IsAbsTest struct {
|
||||||
|
path string
|
||||||
|
isAbs bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var isabstests = []IsAbsTest{
|
||||||
|
{"", false},
|
||||||
|
{"/", true},
|
||||||
|
{"/usr/bin/gcc", true},
|
||||||
|
{"..", false},
|
||||||
|
{"/a/../bb", true},
|
||||||
|
{".", false},
|
||||||
|
{"./", false},
|
||||||
|
{"lala", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
var winisabstests = []IsAbsTest{
|
||||||
|
{`C:\`, true},
|
||||||
|
{`c\`, false},
|
||||||
|
{`c::`, false},
|
||||||
|
{`c:`, false},
|
||||||
|
{`/`, false},
|
||||||
|
{`\`, false},
|
||||||
|
{`\Windows`, false},
|
||||||
|
{`c:a\b`, false},
|
||||||
|
{`c:\a\b`, true},
|
||||||
|
{`c:/a/b`, true},
|
||||||
|
{`\\host\share\foo`, true},
|
||||||
|
{`//host/share/foo/bar`, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsAbs(t *testing.T) {
|
||||||
|
tests := winisabstests
|
||||||
|
|
||||||
|
// All non-windows tests should fail, because they have no volume letter.
|
||||||
|
for _, test := range isabstests {
|
||||||
|
tests = append(tests, IsAbsTest{test.path, false})
|
||||||
|
}
|
||||||
|
// All non-windows test should work as intended if prefixed with volume letter.
|
||||||
|
for _, test := range isabstests {
|
||||||
|
tests = append(tests, IsAbsTest{"c:" + test.path, test.isAbs})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
if r := isAbs(test.path); r != test.isAbs {
|
||||||
|
t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -117,7 +117,6 @@ func TestValidateCredentialSpecs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateSecretConfigNames(t *testing.T) {
|
func TestValidateSecretConfigNames(t *testing.T) {
|
||||||
|
|||||||
@@ -275,6 +275,7 @@ func TestExtractVariables(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
actual := ExtractVariables(tc.dict, defaultPattern)
|
actual := ExtractVariables(tc.dict, defaultPattern)
|
||||||
assert.Check(t, is.DeepEqual(actual, tc.expected))
|
assert.Check(t, is.DeepEqual(actual, tc.expected))
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ type Config struct {
|
|||||||
Volumes map[string]VolumeConfig `yaml:",omitempty" json:"volumes,omitempty"`
|
Volumes map[string]VolumeConfig `yaml:",omitempty" json:"volumes,omitempty"`
|
||||||
Secrets map[string]SecretConfig `yaml:",omitempty" json:"secrets,omitempty"`
|
Secrets map[string]SecretConfig `yaml:",omitempty" json:"secrets,omitempty"`
|
||||||
Configs map[string]ConfigObjConfig `yaml:",omitempty" json:"configs,omitempty"`
|
Configs map[string]ConfigObjConfig `yaml:",omitempty" json:"configs,omitempty"`
|
||||||
Extras map[string]interface{} `yaml:",inline", json:"-"`
|
Extras map[string]interface{} `yaml:",inline" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON makes Config implement json.Marshaler
|
// MarshalJSON makes Config implement json.Marshaler
|
||||||
|
|||||||
@@ -454,7 +454,6 @@ func TestJSONWithPsFormatNoFile(t *testing.T) {
|
|||||||
if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` {
|
if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` {
|
||||||
t.Fatalf("Unknown ps format: %s\n", config.PsFormat)
|
t.Fatalf("Unknown ps format: %s\n", config.PsFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJSONSaveWithNoFile(t *testing.T) {
|
func TestJSONSaveWithNoFile(t *testing.T) {
|
||||||
@@ -578,6 +577,7 @@ func TestConfigPath(t *testing.T) {
|
|||||||
expectedErr: fmt.Sprintf("is outside of root config directory %q", "dummy"),
|
expectedErr: fmt.Sprintf("is outside of root config directory %q", "dummy"),
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
SetDir(tc.dir)
|
SetDir(tc.dir)
|
||||||
f, err := Path(tc.path...)
|
f, err := Path(tc.path...)
|
||||||
|
|||||||
@@ -123,9 +123,11 @@ func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
|
|||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
for addr, ac := range configFile.AuthConfigs {
|
for addr, ac := range configFile.AuthConfigs {
|
||||||
ac.Username, ac.Password, err = decodeAuth(ac.Auth)
|
if ac.Auth != "" {
|
||||||
if err != nil {
|
ac.Username, ac.Password, err = decodeAuth(ac.Auth)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ac.Auth = ""
|
ac.Auth = ""
|
||||||
ac.ServerAddress = addr
|
ac.ServerAddress = addr
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package configfile
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -380,6 +381,41 @@ func TestGetAllCredentialsCredHelperOverridesDefaultStore(t *testing.T) {
|
|||||||
assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount))
|
assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadFromReaderWithUsernamePassword(t *testing.T) {
|
||||||
|
configFile := New("test-load")
|
||||||
|
defer os.Remove("test-load")
|
||||||
|
|
||||||
|
want := types.AuthConfig{
|
||||||
|
Username: "user",
|
||||||
|
Password: "pass",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range []types.AuthConfig{
|
||||||
|
want,
|
||||||
|
{
|
||||||
|
Auth: encodeAuth(&want),
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
cf := ConfigFile{
|
||||||
|
AuthConfigs: map[string]types.AuthConfig{
|
||||||
|
"example.com/foo": tc,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(cf)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
err = configFile.LoadFromReader(bytes.NewReader(b))
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
got, err := configFile.GetAuthConfig("example.com/foo")
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
assert.Check(t, is.DeepEqual(want.Username, got.Username))
|
||||||
|
assert.Check(t, is.DeepEqual(want.Password, got.Password))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCheckKubernetesConfigurationRaiseAnErrorOnInvalidValue(t *testing.T) {
|
func TestCheckKubernetesConfigurationRaiseAnErrorOnInvalidValue(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
|
|||||||
@@ -104,8 +104,8 @@ func (c *Endpoint) ClientOpts() ([]client.Opt, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
result = append(result,
|
result = append(result,
|
||||||
client.WithHost(c.Host),
|
|
||||||
withHTTPClient(tlsConfig),
|
withHTTPClient(tlsConfig),
|
||||||
|
client.WithHost(c.Host),
|
||||||
)
|
)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
package kubernetes
|
package kubernetes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/context"
|
"github.com/docker/cli/cli/context"
|
||||||
"github.com/docker/cli/cli/context/store"
|
"github.com/docker/cli/cli/context/store"
|
||||||
"github.com/docker/cli/kubernetes"
|
api "github.com/docker/compose-on-kubernetes/api"
|
||||||
|
"github.com/docker/docker/pkg/homedir"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
)
|
)
|
||||||
@@ -17,6 +23,8 @@ type EndpointMeta struct {
|
|||||||
Exec *clientcmdapi.ExecConfig `json:",omitempty"`
|
Exec *clientcmdapi.ExecConfig `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ command.EndpointDefaultResolver = &EndpointMeta{}
|
||||||
|
|
||||||
// Endpoint is a typed wrapper around a context-store generic endpoint describing
|
// Endpoint is a typed wrapper around a context-store generic endpoint describing
|
||||||
// a Kubernetes endpoint, with TLS data
|
// a Kubernetes endpoint, with TLS data
|
||||||
type Endpoint struct {
|
type Endpoint struct {
|
||||||
@@ -24,6 +32,12 @@ type Endpoint struct {
|
|||||||
TLSData *context.TLSData
|
TLSData *context.TLSData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
command.RegisterDefaultStoreEndpoints(
|
||||||
|
store.EndpointTypeGetter(KubernetesEndpoint, func() interface{} { return &EndpointMeta{} }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// WithTLSData loads TLS materials for the endpoint
|
// WithTLSData loads TLS materials for the endpoint
|
||||||
func (c *EndpointMeta) WithTLSData(s store.Reader, contextName string) (Endpoint, error) {
|
func (c *EndpointMeta) WithTLSData(s store.Reader, contextName string) (Endpoint, error) {
|
||||||
tlsData, err := context.LoadTLSData(s, contextName, KubernetesEndpoint)
|
tlsData, err := context.LoadTLSData(s, contextName, KubernetesEndpoint)
|
||||||
@@ -61,6 +75,32 @@ func (c *Endpoint) KubernetesConfig() clientcmd.ClientConfig {
|
|||||||
return clientcmd.NewDefaultClientConfig(*cfg, &clientcmd.ConfigOverrides{})
|
return clientcmd.NewDefaultClientConfig(*cfg, &clientcmd.ConfigOverrides{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResolveDefault returns endpoint metadata for the default Kubernetes
|
||||||
|
// endpoint, which is derived from the env-based kubeconfig.
|
||||||
|
func (c *EndpointMeta) ResolveDefault(stackOrchestrator command.Orchestrator) (interface{}, *store.EndpointTLSData, error) {
|
||||||
|
kubeconfig := os.Getenv("KUBECONFIG")
|
||||||
|
if kubeconfig == "" {
|
||||||
|
kubeconfig = filepath.Join(homedir.Get(), ".kube/config")
|
||||||
|
}
|
||||||
|
kubeEP, err := FromKubeConfig(kubeconfig, "", "")
|
||||||
|
if err != nil {
|
||||||
|
if stackOrchestrator == command.OrchestratorKubernetes || stackOrchestrator == command.OrchestratorAll {
|
||||||
|
return nil, nil, errors.Wrapf(err, "default orchestrator is %s but unable to resolve kubernetes endpoint", stackOrchestrator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We deliberately quash the error here, returning nil
|
||||||
|
// for the first argument is sufficient to indicate we weren't able to
|
||||||
|
// provide a default
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var tls *store.EndpointTLSData
|
||||||
|
if kubeEP.TLSData != nil {
|
||||||
|
tls = kubeEP.TLSData.ToStoreTLSData()
|
||||||
|
}
|
||||||
|
return kubeEP.EndpointMeta, tls, nil
|
||||||
|
}
|
||||||
|
|
||||||
// EndpointFromContext extracts kubernetes endpoint info from current context
|
// EndpointFromContext extracts kubernetes endpoint info from current context
|
||||||
func EndpointFromContext(metadata store.Metadata) *EndpointMeta {
|
func EndpointFromContext(metadata store.Metadata) *EndpointMeta {
|
||||||
ep, ok := metadata.Endpoints[KubernetesEndpoint]
|
ep, ok := metadata.Endpoints[KubernetesEndpoint]
|
||||||
@@ -91,5 +131,5 @@ func ConfigFromContext(name string, s store.Reader) (clientcmd.ClientConfig, err
|
|||||||
return ep.KubernetesConfig(), nil
|
return ep.KubernetesConfig(), nil
|
||||||
}
|
}
|
||||||
// context has no kubernetes endpoint
|
// context has no kubernetes endpoint
|
||||||
return kubernetes.NewKubernetesConfig(""), nil
|
return api.NewKubernetesConfig(""), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package kubernetes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
|
cliflags "github.com/docker/cli/cli/flags"
|
||||||
|
"gotest.tools/assert"
|
||||||
|
"gotest.tools/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDefaultContextInitializer(t *testing.T) {
|
||||||
|
cli, err := command.NewDockerCli()
|
||||||
|
assert.NilError(t, err)
|
||||||
|
defer env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig")()
|
||||||
|
configFile := &configfile.ConfigFile{
|
||||||
|
StackOrchestrator: "all",
|
||||||
|
}
|
||||||
|
ctx, err := command.ResolveDefaultContext(&cliflags.CommonOptions{}, configFile, command.DefaultContextStoreConfig(), cli.Err())
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, "default", ctx.Meta.Name)
|
||||||
|
assert.Equal(t, command.OrchestratorAll, ctx.Meta.Metadata.(command.DockerContext).StackOrchestrator)
|
||||||
|
assert.DeepEqual(t, "zoinx", ctx.Meta.Endpoints[KubernetesEndpoint].(EndpointMeta).DefaultNamespace)
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user