mirror of
https://github.com/GoogleContainerTools/kaniko
synced 2026-10-19 11:10:45 +02:00
fix deps
This commit is contained in:
+8
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -72,6 +73,13 @@ type Descriptor struct {
|
||||
// URLs contains the source URLs of this content.
|
||||
URLs []string `json:"urls,omitempty"`
|
||||
|
||||
// Annotations contains arbitrary metadata relating to the targeted content.
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
|
||||
// Platform describes the platform which the image in the manifest runs on.
|
||||
// This should only be used when referring to a manifest.
|
||||
Platform *v1.Platform `json:"platform,omitempty"`
|
||||
|
||||
// NOTE: Before adding a field here, please ensure that all
|
||||
// other options have been exhausted. Much of the type relationships
|
||||
// depend on the simplicity of this type.
|
||||
|
||||
+34
-2
@@ -39,7 +39,7 @@ Ahmed Kamal <email.ahmedkamal@googlemail.com>
|
||||
Ahmet Alp Balkan <ahmetb@microsoft.com>
|
||||
Aidan Feldman <aidan.feldman@gmail.com>
|
||||
Aidan Hobson Sayers <aidanhs@cantab.net>
|
||||
AJ Bowen <aj@gandi.net>
|
||||
AJ Bowen <aj@soulshake.net>
|
||||
Ajey Charantimath <ajey.charantimath@gmail.com>
|
||||
ajneu <ajneu@users.noreply.github.com>
|
||||
Akash Gupta <akagup@microsoft.com>
|
||||
@@ -54,6 +54,7 @@ Alan Scherger <flyinprogrammer@gmail.com>
|
||||
Alan Thompson <cloojure@gmail.com>
|
||||
Albert Callarisa <shark234@gmail.com>
|
||||
Albert Zhang <zhgwenming@gmail.com>
|
||||
Alejandro González Hevia <alejandrgh11@gmail.com>
|
||||
Aleksa Sarai <asarai@suse.de>
|
||||
Aleksandrs Fadins <aleks@s-ko.net>
|
||||
Alena Prokharchyk <alena@rancher.com>
|
||||
@@ -65,6 +66,7 @@ Alex Coventry <alx@empirical.com>
|
||||
Alex Crawford <alex.crawford@coreos.com>
|
||||
Alex Ellis <alexellis2@gmail.com>
|
||||
Alex Gaynor <alex.gaynor@gmail.com>
|
||||
Alex Goodman <wagoodman@gmail.com>
|
||||
Alex Olshansky <i@creagenics.com>
|
||||
Alex Samorukov <samm@os2.kiev.ua>
|
||||
Alex Warhawk <ax.warhawk@gmail.com>
|
||||
@@ -77,6 +79,7 @@ Alexander Shopov <ash@kambanaria.org>
|
||||
Alexandre Beslic <alexandre.beslic@gmail.com>
|
||||
Alexandre Garnier <zigarn@gmail.com>
|
||||
Alexandre González <agonzalezro@gmail.com>
|
||||
Alexandre Jomin <alexandrejomin@gmail.com>
|
||||
Alexandru Sfirlogea <alexandru.sfirlogea@gmail.com>
|
||||
Alexey Guskov <lexag@mail.ru>
|
||||
Alexey Kotlyarov <alexey@infoxchange.net.au>
|
||||
@@ -98,11 +101,13 @@ Amir Goldstein <amir73il@aquasec.com>
|
||||
Amit Bakshi <ambakshi@gmail.com>
|
||||
Amit Krishnan <amit.krishnan@oracle.com>
|
||||
Amit Shukla <amit.shukla@docker.com>
|
||||
Amr Gawish <amr.gawish@gmail.com>
|
||||
Amy Lindburg <amy.lindburg@docker.com>
|
||||
Anand Patil <anand.prabhakar.patil@gmail.com>
|
||||
AnandkumarPatel <anandkumarpatel@gmail.com>
|
||||
Anatoly Borodin <anatoly.borodin@gmail.com>
|
||||
Anchal Agrawal <aagrawa4@illinois.edu>
|
||||
Anda Xu <anda.xu@docker.com>
|
||||
Anders Janmyr <anders@janmyr.com>
|
||||
Andre Dublin <81dublin@gmail.com>
|
||||
Andre Granovsky <robotciti@live.com>
|
||||
@@ -197,6 +202,7 @@ Ben Toews <mastahyeti@gmail.com>
|
||||
Ben Wiklund <ben@daisyowl.com>
|
||||
Benjamin Atkin <ben@benatkin.com>
|
||||
Benjamin Boudreau <boudreau.benjamin@gmail.com>
|
||||
Benjamin Yolken <yolken@stripe.com>
|
||||
Benoit Chesneau <bchesneau@gmail.com>
|
||||
Bernerd Schaefer <bj.schaefer@gmail.com>
|
||||
Bernhard M. Wiedemann <bwiedemann@suse.de>
|
||||
@@ -267,6 +273,7 @@ Carlos Sanchez <carlos@apache.org>
|
||||
Carol Fager-Higgins <carol.fager-higgins@docker.com>
|
||||
Cary <caryhartline@users.noreply.github.com>
|
||||
Casey Bisson <casey.bisson@joyent.com>
|
||||
Catalin Pirvu <pirvu.catalin94@gmail.com>
|
||||
Ce Gao <ce.gao@outlook.com>
|
||||
Cedric Davies <cedricda@microsoft.com>
|
||||
Cezar Sa Espinola <cezarsa@gmail.com>
|
||||
@@ -293,6 +300,8 @@ Chen Min <chenmin46@huawei.com>
|
||||
Chen Mingjie <chenmingjie0828@163.com>
|
||||
Chen Qiu <cheney-90@hotmail.com>
|
||||
Cheng-mean Liu <soccerl@microsoft.com>
|
||||
Chengguang Xu <cgxu519@gmx.com>
|
||||
chenyuzhu <chenyuzhi@oschina.cn>
|
||||
Chetan Birajdar <birajdar.chetan@gmail.com>
|
||||
Chewey <prosto-chewey@users.noreply.github.com>
|
||||
Chia-liang Kao <clkao@clkao.org>
|
||||
@@ -313,6 +322,7 @@ Chris Snow <chsnow123@gmail.com>
|
||||
Chris St. Pierre <chris.a.st.pierre@gmail.com>
|
||||
Chris Stivers <chris@stivers.us>
|
||||
Chris Swan <chris.swan@iee.org>
|
||||
Chris Telfer <ctelfer@docker.com>
|
||||
Chris Wahl <github@wahlnetwork.com>
|
||||
Chris Weyl <cweyl@alumni.drew.edu>
|
||||
Christian Berendt <berendt@b1-systems.de>
|
||||
@@ -336,6 +346,7 @@ Chun Chen <ramichen@tencent.com>
|
||||
Ciro S. Costa <ciro.costa@usp.br>
|
||||
Clayton Coleman <ccoleman@redhat.com>
|
||||
Clinton Kitson <clintonskitson@gmail.com>
|
||||
Cody Roseborough <crrosebo@amazon.com>
|
||||
Coenraad Loubser <coenraad@wish.org.za>
|
||||
Colin Dunklau <colin.dunklau@gmail.com>
|
||||
Colin Hebert <hebert.colin@gmail.com>
|
||||
@@ -411,6 +422,7 @@ Dave MacDonald <mindlapse@gmail.com>
|
||||
Dave Tucker <dt@docker.com>
|
||||
David Anderson <dave@natulte.net>
|
||||
David Calavera <david.calavera@gmail.com>
|
||||
David Chung <david.chung@docker.com>
|
||||
David Corking <dmc-source@dcorking.com>
|
||||
David Cramer <davcrame@cisco.com>
|
||||
David Currie <david_currie@uk.ibm.com>
|
||||
@@ -510,6 +522,7 @@ Eike Herzbach <eike@herzbach.net>
|
||||
Eivin Giske Skaaren <eivinsn@axis.com>
|
||||
Eivind Uggedal <eivind@uggedal.com>
|
||||
Elan Ruusamäe <glen@pld-linux.org>
|
||||
Elango Sivanandam <elango.siva@docker.com>
|
||||
Elena Morozova <lelenanam@gmail.com>
|
||||
Eli Uriegas <eli.uriegas@docker.com>
|
||||
Elias Faxö <elias.faxo@tre.se>
|
||||
@@ -548,6 +561,7 @@ Erik St. Martin <alakriti@gmail.com>
|
||||
Erik Weathers <erikdw@gmail.com>
|
||||
Erno Hopearuoho <erno.hopearuoho@gmail.com>
|
||||
Erwin van der Koogh <info@erronis.nl>
|
||||
Ethan Bell <ebgamer29@gmail.com>
|
||||
Euan Kemp <euan.kemp@coreos.com>
|
||||
Eugen Krizo <eugen.krizo@gmail.com>
|
||||
Eugene Yakubovich <eugene.yakubovich@coreos.com>
|
||||
@@ -575,6 +589,7 @@ Fabrizio Regini <freegenie@gmail.com>
|
||||
Fabrizio Soppelsa <fsoppelsa@mirantis.com>
|
||||
Faiz Khan <faizkhan00@gmail.com>
|
||||
falmp <chico.lopes@gmail.com>
|
||||
Fangming Fang <fangming.fang@arm.com>
|
||||
Fangyuan Gao <21551127@zju.edu.cn>
|
||||
Fareed Dudhia <fareeddudhia@googlemail.com>
|
||||
Fathi Boudra <fathi.boudra@linaro.org>
|
||||
@@ -673,6 +688,7 @@ Guilherme Salgado <gsalgado@gmail.com>
|
||||
Guillaume Dufour <gdufour.prestataire@voyages-sncf.com>
|
||||
Guillaume J. Charmes <guillaume.charmes@docker.com>
|
||||
guoxiuyan <guoxiuyan@huawei.com>
|
||||
Guri <odg0318@gmail.com>
|
||||
Gurjeet Singh <gurjeet@singh.im>
|
||||
Guruprasad <lgp171188@gmail.com>
|
||||
Gustav Sinder <gustav.sinder@gmail.com>
|
||||
@@ -696,6 +712,7 @@ heartlock <21521209@zju.edu.cn>
|
||||
Hector Castro <hectcastro@gmail.com>
|
||||
Helen Xie <chenjg@harmonycloud.cn>
|
||||
Henning Sprang <henning.sprang@gmail.com>
|
||||
Hiroshi Hatake <hatake@clear-code.com>
|
||||
Hobofan <goisser94@gmail.com>
|
||||
Hollie Teal <hollie@docker.com>
|
||||
Hong Xu <hong@topbug.net>
|
||||
@@ -808,6 +825,7 @@ Jean-Pierre Huynh <jean-pierre.huynh@ounet.fr>
|
||||
Jean-Tiare Le Bigot <jt@yadutaf.fr>
|
||||
Jeeva S. Chelladhurai <sjeeva@gmail.com>
|
||||
Jeff Anderson <jeff@docker.com>
|
||||
Jeff Hajewski <jeff.hajewski@gmail.com>
|
||||
Jeff Johnston <jeff.johnston.mn@gmail.com>
|
||||
Jeff Lindsay <progrium@gmail.com>
|
||||
Jeff Mickey <j@codemac.net>
|
||||
@@ -893,6 +911,7 @@ Jonas Pfenniger <jonas@pfenniger.name>
|
||||
Jonathan A. Sternberg <jonathansternberg@gmail.com>
|
||||
Jonathan Boulle <jonathanboulle@gmail.com>
|
||||
Jonathan Camp <jonathan@irondojo.com>
|
||||
Jonathan Choy <jonathan.j.choy@gmail.com>
|
||||
Jonathan Dowland <jon+github@alcopop.org>
|
||||
Jonathan Lebon <jlebon@redhat.com>
|
||||
Jonathan Lomas <jonathan@floatinglomas.ca>
|
||||
@@ -962,6 +981,7 @@ Kareem Khazem <karkhaz@karkhaz.com>
|
||||
kargakis <kargakis@users.noreply.github.com>
|
||||
Karl Grzeszczak <karlgrz@gmail.com>
|
||||
Karol Duleba <mr.fuxi@gmail.com>
|
||||
Karthik Karanth <karanth.karthik@gmail.com>
|
||||
Karthik Nayak <Karthik.188@gmail.com>
|
||||
Kate Heddleston <kate.heddleston@gmail.com>
|
||||
Katie McLaughlin <katie@glasnt.com>
|
||||
@@ -1108,6 +1128,7 @@ Manfred Zabarauskas <manfredas@zabarauskas.com>
|
||||
Manjunath A Kumatagi <mkumatag@in.ibm.com>
|
||||
Mansi Nahar <mmn4185@rit.edu>
|
||||
Manuel Meurer <manuel@krautcomputing.com>
|
||||
Manuel Rüger <manuel@rueg.eu>
|
||||
Manuel Woelker <github@manuel.woelker.org>
|
||||
mapk0y <mapk0y@gmail.com>
|
||||
Marc Abramowitz <marc@marc-abramowitz.com>
|
||||
@@ -1149,10 +1170,12 @@ Martin Mosegaard Amdisen <martin.amdisen@praqma.com>
|
||||
Martin Redmond <redmond.martin@gmail.com>
|
||||
Mary Anthony <mary.anthony@docker.com>
|
||||
Masahito Zembutsu <zembutsu@users.noreply.github.com>
|
||||
Masato Ohba <over.rye@gmail.com>
|
||||
Masayuki Morita <minamijoyo@gmail.com>
|
||||
Mason Malone <mason.malone@gmail.com>
|
||||
Mateusz Sulima <sulima.mateusz@gmail.com>
|
||||
Mathias Monnerville <mathias@monnerville.com>
|
||||
Mathieu Champlon <mathieu.champlon@docker.com>
|
||||
Mathieu Le Marec - Pasquet <kiorky@cryptelium.net>
|
||||
Mathieu Parent <math.parent@gmail.com>
|
||||
Matt Apperson <me@mattapperson.com>
|
||||
@@ -1209,6 +1232,7 @@ Michael Huettermann <michael@huettermann.net>
|
||||
Michael Irwin <mikesir87@gmail.com>
|
||||
Michael Käufl <docker@c.michael-kaeufl.de>
|
||||
Michael Neale <michael.neale@gmail.com>
|
||||
Michael Nussbaum <michael.nussbaum@getbraintree.com>
|
||||
Michael Prokop <github@michael-prokop.at>
|
||||
Michael Scharf <github@scharf.gr>
|
||||
Michael Spetsiotis <michael_spets@hotmail.com>
|
||||
@@ -1223,6 +1247,7 @@ Michal Minář <miminar@redhat.com>
|
||||
Michal Wieczorek <wieczorek-michal@wp.pl>
|
||||
Michaël Pailloncy <mpapo.dev@gmail.com>
|
||||
Michał Czeraszkiewicz <czerasz@gmail.com>
|
||||
Michał Gryko <github@odkurzacz.org>
|
||||
Michiel@unhosted <michiel@unhosted.org>
|
||||
Mickaël FORTUNATO <morsi.morsicus@gmail.com>
|
||||
Miguel Angel Fernández <elmendalerenda@gmail.com>
|
||||
@@ -1239,6 +1264,7 @@ Mike Estes <mike.estes@logos.com>
|
||||
Mike Gaffney <mike@uberu.com>
|
||||
Mike Goelzer <mike.goelzer@docker.com>
|
||||
Mike Leone <mleone896@gmail.com>
|
||||
Mike Lundy <mike@fluffypenguin.org>
|
||||
Mike MacCana <mike.maccana@gmail.com>
|
||||
Mike Naberezny <mike@naberezny.com>
|
||||
Mike Snitzer <snitzer@redhat.com>
|
||||
@@ -1297,6 +1323,7 @@ Niall O'Higgins <niallo@unworkable.org>
|
||||
Nicholas E. Rabenau <nerab@gmx.at>
|
||||
Nick DeCoursin <n.decoursin@foodpanda.com>
|
||||
Nick Irvine <nfirvine@nfirvine.com>
|
||||
Nick Neisen <nwneisen@gmail.com>
|
||||
Nick Parker <nikaios@gmail.com>
|
||||
Nick Payne <nick@kurai.co.uk>
|
||||
Nick Russo <nicholasjamesrusso@gmail.com>
|
||||
@@ -1322,6 +1349,7 @@ Nishant Totla <nishanttotla@gmail.com>
|
||||
NIWA Hideyuki <niwa.niwa@nifty.ne.jp>
|
||||
Noah Meyerhans <nmeyerha@amazon.com>
|
||||
Noah Treuhaft <noah.treuhaft@docker.com>
|
||||
NobodyOnSE <ich@sektor.selfip.com>
|
||||
noducks <onemannoducks@gmail.com>
|
||||
Nolan Darilek <nolan@thewordnerd.info>
|
||||
nponeccop <andy.melnikov@gmail.com>
|
||||
@@ -1329,7 +1357,6 @@ Nuutti Kotivuori <naked@iki.fi>
|
||||
nzwsch <hi@nzwsch.com>
|
||||
O.S. Tezer <ostezer@gmail.com>
|
||||
objectified <objectified@gmail.com>
|
||||
odk- <github@odkurzacz.org>
|
||||
Oguz Bilgic <fisyonet@gmail.com>
|
||||
Oh Jinkyun <tintypemolly@gmail.com>
|
||||
Ohad Schneider <ohadschn@users.noreply.github.com>
|
||||
@@ -1352,6 +1379,7 @@ Patrick Böänziger <patrick.baenziger@bsi-software.com>
|
||||
Patrick Devine <patrick.devine@docker.com>
|
||||
Patrick Hemmer <patrick.hemmer@gmail.com>
|
||||
Patrick Stapleton <github@gdi2290.com>
|
||||
Patrik Cyvoct <patrik@ptrk.io>
|
||||
pattichen <craftsbear@gmail.com>
|
||||
Paul <paul9869@gmail.com>
|
||||
paul <paul@inkling.com>
|
||||
@@ -1423,6 +1451,7 @@ Pradip Dhara <pradipd@microsoft.com>
|
||||
Prasanna Gautam <prasannagautam@gmail.com>
|
||||
Pratik Karki <prertik@outlook.com>
|
||||
Prayag Verma <prayag.verma@gmail.com>
|
||||
Priya Wadhwa <priyawadhwa@google.com>
|
||||
Przemek Hejman <przemyslaw.hejman@gmail.com>
|
||||
Pure White <daniel48@126.com>
|
||||
pysqz <randomq@126.com>
|
||||
@@ -1838,11 +1867,13 @@ Wang Xing <hzwangxing@corp.netease.com>
|
||||
Wang Yuexiao <wang.yuexiao@zte.com.cn>
|
||||
Ward Vandewege <ward@jhvc.com>
|
||||
WarheadsSE <max@warheads.net>
|
||||
Wassim Dhif <wassimdhif@gmail.com>
|
||||
Wayne Chang <wayne@neverfear.org>
|
||||
Wayne Song <wsong@docker.com>
|
||||
Weerasak Chongnguluam <singpor@gmail.com>
|
||||
Wei Wu <wuwei4455@gmail.com>
|
||||
Wei-Ting Kuo <waitingkuo0527@gmail.com>
|
||||
weipeng <weipeng@tuscloud.io>
|
||||
weiyan <weiyan3@huawei.com>
|
||||
Weiyang Zhu <cnresonant@gmail.com>
|
||||
Wen Cheng Ma <wenchma@cn.ibm.com>
|
||||
@@ -1948,5 +1979,6 @@ Zunayed Ali <zunayed@gmail.com>
|
||||
Átila Camurça Alves <camurca.home@gmail.com>
|
||||
尹吉峰 <jifeng.yin@gmail.com>
|
||||
徐俊杰 <paco.xu@daocloud.io>
|
||||
慕陶 <jihui.xjh@alibaba-inc.com>
|
||||
搏通 <yufeng.pyf@alibaba-inc.com>
|
||||
黄艳红00139573 <huang.yanhong@zte.com.cn>
|
||||
|
||||
+2
-2
@@ -3,9 +3,9 @@ package api // import "github.com/docker/docker/api"
|
||||
// Common constants for daemon and client.
|
||||
const (
|
||||
// DefaultVersion of Current REST API
|
||||
DefaultVersion string = "1.37"
|
||||
DefaultVersion = "1.38"
|
||||
|
||||
// NoBaseImageSpecifier is the symbol used by the FROM
|
||||
// command to specify that no base image is to be used.
|
||||
NoBaseImageSpecifier string = "scratch"
|
||||
NoBaseImageSpecifier = "scratch"
|
||||
)
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
package api // import "github.com/docker/docker/api"
|
||||
|
||||
// MinVersion represents Minimum REST API version supported
|
||||
const MinVersion string = "1.12"
|
||||
const MinVersion = "1.12"
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
// CheckpointCreateOptions holds parameters to create a checkpoint from a container
|
||||
|
||||
+3
@@ -121,6 +121,9 @@ type PluginConfigArgs struct {
|
||||
// swagger:model PluginConfigInterface
|
||||
type PluginConfigInterface struct {
|
||||
|
||||
// Protocol to use for clients connecting to the plugin.
|
||||
ProtocolScheme string `json:"ProtocolScheme,omitempty"`
|
||||
|
||||
// socket
|
||||
// Required: true
|
||||
Socket string `json:"Socket"`
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ package types
|
||||
// swagger:model Port
|
||||
type Port struct {
|
||||
|
||||
// IP
|
||||
// Host IP address that the container's port is mapped to
|
||||
IP string `json:"IP,omitempty"`
|
||||
|
||||
// Port on the container
|
||||
|
||||
+8
@@ -11,9 +11,17 @@ const (
|
||||
RuntimeContainer RuntimeType = "container"
|
||||
// RuntimePlugin is the plugin based runtime
|
||||
RuntimePlugin RuntimeType = "plugin"
|
||||
// RuntimeNetworkAttachment is the network attachment runtime
|
||||
RuntimeNetworkAttachment RuntimeType = "attachment"
|
||||
|
||||
// RuntimeURLContainer is the proto url for the container type
|
||||
RuntimeURLContainer RuntimeURL = "types.docker.com/RuntimeContainer"
|
||||
// RuntimeURLPlugin is the proto url for the plugin type
|
||||
RuntimeURLPlugin RuntimeURL = "types.docker.com/RuntimePlugin"
|
||||
)
|
||||
|
||||
// NetworkAttachmentSpec represents the runtime spec type for network
|
||||
// attachment tasks
|
||||
type NetworkAttachmentSpec struct {
|
||||
ContainerID string
|
||||
}
|
||||
|
||||
+7
-4
@@ -60,10 +60,13 @@ type Task struct {
|
||||
|
||||
// TaskSpec represents the spec of a task.
|
||||
type TaskSpec struct {
|
||||
// ContainerSpec and PluginSpec are mutually exclusive.
|
||||
// PluginSpec will only be used when the `Runtime` field is set to `plugin`
|
||||
ContainerSpec *ContainerSpec `json:",omitempty"`
|
||||
PluginSpec *runtime.PluginSpec `json:",omitempty"`
|
||||
// ContainerSpec, NetworkAttachmentSpec, and PluginSpec are mutually exclusive.
|
||||
// PluginSpec is only used when the `Runtime` field is set to `plugin`
|
||||
// NetworkAttachmentSpec is used if the `Runtime` field is set to
|
||||
// `attachment`.
|
||||
ContainerSpec *ContainerSpec `json:",omitempty"`
|
||||
PluginSpec *runtime.PluginSpec `json:",omitempty"`
|
||||
NetworkAttachmentSpec *NetworkAttachmentSpec `json:",omitempty"`
|
||||
|
||||
Resources *ResourceRequirements `json:",omitempty"`
|
||||
RestartPolicy *RestartPolicy `json:",omitempty"`
|
||||
|
||||
+9
-2
@@ -82,11 +82,14 @@ func GetTimestamp(value string, reference time.Time) (string, error) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// if there is a `-` then it's an RFC3339 like timestamp otherwise assume unixtimestamp
|
||||
// if there is a `-` then it's an RFC3339 like timestamp
|
||||
if strings.Contains(value, "-") {
|
||||
return "", err // was probably an RFC3339 like timestamp but the parser failed with an error
|
||||
}
|
||||
return value, nil // unixtimestamp in and out case (meaning: the value passed at the command line is already in the right format for passing to the server)
|
||||
if _, _, err := parseTimestamp(value); err != nil {
|
||||
return "", fmt.Errorf("failed to parse value as time or duration: %q", value)
|
||||
}
|
||||
return value, nil // unix timestamp in and out case (meaning: the value passed at the command line is already in the right format for passing to the server)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())), nil
|
||||
@@ -104,6 +107,10 @@ func ParseTimestamps(value string, def int64) (int64, int64, error) {
|
||||
if value == "" {
|
||||
return def, 0, nil
|
||||
}
|
||||
return parseTimestamp(value)
|
||||
}
|
||||
|
||||
func parseTimestamp(value string) (int64, int64, error) {
|
||||
sa := strings.SplitN(value, ".", 2)
|
||||
s, err := strconv.ParseInt(sa[0], 10, 64)
|
||||
if err != nil {
|
||||
|
||||
Generated
Vendored
+4
-4
@@ -1,4 +1,4 @@
|
||||
package volume // import "github.com/docker/docker/api/types/volume"
|
||||
package volume
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
@@ -7,9 +7,9 @@ package volume // import "github.com/docker/docker/api/types/volume"
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// VolumesCreateBody volumes create body
|
||||
// swagger:model VolumesCreateBody
|
||||
type VolumesCreateBody struct {
|
||||
// VolumeCreateBody
|
||||
// swagger:model VolumeCreateBody
|
||||
type VolumeCreateBody struct {
|
||||
|
||||
// Name of the volume driver to use.
|
||||
// Required: true
|
||||
Generated
Vendored
+4
-4
@@ -1,4 +1,4 @@
|
||||
package volume // import "github.com/docker/docker/api/types/volume"
|
||||
package volume
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
@@ -9,9 +9,9 @@ package volume // import "github.com/docker/docker/api/types/volume"
|
||||
|
||||
import "github.com/docker/docker/api/types"
|
||||
|
||||
// VolumesListOKBody volumes list o k body
|
||||
// swagger:model VolumesListOKBody
|
||||
type VolumesListOKBody struct {
|
||||
// VolumeListOKBody
|
||||
// swagger:model VolumeListOKBody
|
||||
type VolumeListOKBody struct {
|
||||
|
||||
// List of volumes
|
||||
// Required: true
|
||||
+1
-1
@@ -19,7 +19,7 @@ import (
|
||||
|
||||
const (
|
||||
// DefaultDockerfileName is the Default filename with Docker commands, read by docker build
|
||||
DefaultDockerfileName string = "Dockerfile"
|
||||
DefaultDockerfileName = "Dockerfile"
|
||||
)
|
||||
|
||||
// Source defines a location that can be used as a source for the ADD/COPY
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ func (b *BuildArgs) MergeReferencedArgs(other *BuildArgs) {
|
||||
// WarnOnUnusedBuildArgs checks if there are any leftover build-args that were
|
||||
// passed but not consumed during build. Print a warning, if there are any.
|
||||
func (b *BuildArgs) WarnOnUnusedBuildArgs(out io.Writer) {
|
||||
leftoverArgs := []string{}
|
||||
var leftoverArgs []string
|
||||
for arg := range b.argsFromOptions {
|
||||
_, isReferenced := b.referencedArgs[arg]
|
||||
_, isBuiltin := builtinAllowedBuildArgs[arg]
|
||||
|
||||
+20
-12
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -208,13 +209,26 @@ func newBuilder(clientCtx context.Context, options builderOptions) *Builder {
|
||||
return b
|
||||
}
|
||||
|
||||
// Build 'LABEL' command(s) from '--label' options and add to the last stage
|
||||
func buildLabelOptions(labels map[string]string, stages []instructions.Stage) {
|
||||
keys := []string{}
|
||||
for key := range labels {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
// Sort the label to have a repeatable order
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
value := labels[key]
|
||||
stages[len(stages)-1].AddCommand(instructions.NewLabelCommand(key, value, true))
|
||||
}
|
||||
}
|
||||
|
||||
// Build runs the Dockerfile builder by parsing the Dockerfile and executing
|
||||
// the instructions from the file.
|
||||
func (b *Builder) build(source builder.Source, dockerfile *parser.Result) (*builder.Result, error) {
|
||||
defer b.imageSources.Unmount()
|
||||
|
||||
addNodesForLabelOption(dockerfile.AST, b.options.Labels)
|
||||
|
||||
stages, metaArgs, err := instructions.Parse(dockerfile.AST)
|
||||
if err != nil {
|
||||
if instructions.IsUnknownInstruction(err) {
|
||||
@@ -231,6 +245,9 @@ func (b *Builder) build(source builder.Source, dockerfile *parser.Result) (*buil
|
||||
stages = stages[:targetIx+1]
|
||||
}
|
||||
|
||||
// Add 'LABEL' command specified by '--label' option to the last stage
|
||||
buildLabelOptions(b.options.Labels, stages)
|
||||
|
||||
dockerfile.PrintWarnings(b.Stderr)
|
||||
dispatchState, err := b.dispatchDockerfileWithCancellation(stages, metaArgs, dockerfile.EscapeToken, source)
|
||||
if err != nil {
|
||||
@@ -333,15 +350,6 @@ func (b *Builder) dispatchDockerfileWithCancellation(parseResult []instructions.
|
||||
return dispatchRequest.state, nil
|
||||
}
|
||||
|
||||
func addNodesForLabelOption(dockerfile *parser.Node, labels map[string]string) {
|
||||
if len(labels) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
node := parser.NodeFromLabels(labels)
|
||||
dockerfile.Children = append(dockerfile.Children, node)
|
||||
}
|
||||
|
||||
// BuildFromConfig builds directly from `changes`, treating it as if it were the contents of a Dockerfile
|
||||
// It will:
|
||||
// - Call parse.Parse() to get an AST root for the concatenated Dockerfile entries.
|
||||
@@ -379,7 +387,7 @@ func BuildFromConfig(config *container.Config, changes []string, os string) (*co
|
||||
b.Stderr = ioutil.Discard
|
||||
b.disableCommit = true
|
||||
|
||||
commands := []instructions.Command{}
|
||||
var commands []instructions.Command
|
||||
for _, n := range dockerfile.AST.Children {
|
||||
cmd, err := instructions.ParseCommand(n)
|
||||
if err != nil {
|
||||
|
||||
+3
-1
@@ -172,7 +172,9 @@ func (o *copier) calcCopyInfo(origPath string, allowWildcards bool) ([]copyInfo,
|
||||
// TODO: do this when creating copier. Requires validateCopySourcePath
|
||||
// (and other below) to be aware of the difference sources. Why is it only
|
||||
// done on image Source?
|
||||
if imageSource != nil {
|
||||
if imageSource != nil && o.activeLayer == nil {
|
||||
// this needs to be protected against repeated calls as wildcard copy
|
||||
// will call it multiple times for a single COPY
|
||||
var err error
|
||||
rwLayer, err := imageSource.NewRWLayer()
|
||||
if err != nil {
|
||||
|
||||
+21
-2
@@ -2,7 +2,6 @@ package instructions // import "github.com/docker/docker/builder/dockerfile/inst
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
@@ -110,17 +109,37 @@ type MaintainerCommand struct {
|
||||
Maintainer string
|
||||
}
|
||||
|
||||
// NewLabelCommand creates a new 'LABEL' command
|
||||
func NewLabelCommand(k string, v string, NoExp bool) *LabelCommand {
|
||||
kvp := KeyValuePair{Key: k, Value: v}
|
||||
c := "LABEL "
|
||||
c += kvp.String()
|
||||
nc := withNameAndCode{code: c, name: "label"}
|
||||
cmd := &LabelCommand{
|
||||
withNameAndCode: nc,
|
||||
Labels: KeyValuePairs{
|
||||
kvp,
|
||||
},
|
||||
noExpand: NoExp,
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// LabelCommand : LABEL some json data describing the image
|
||||
//
|
||||
// Sets the Label variable foo to bar,
|
||||
//
|
||||
type LabelCommand struct {
|
||||
withNameAndCode
|
||||
Labels KeyValuePairs // kvp slice instead of map to preserve ordering
|
||||
Labels KeyValuePairs // kvp slice instead of map to preserve ordering
|
||||
noExpand bool
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *LabelCommand) Expand(expander SingleWordExpander) error {
|
||||
if c.noExpand {
|
||||
return nil
|
||||
}
|
||||
return expandKvpsInPlace(c.Labels, expander)
|
||||
}
|
||||
|
||||
|
||||
-31
@@ -10,12 +10,9 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/docker/docker/builder/dockerfile/command"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -205,34 +202,6 @@ func parseLabel(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
return node, nil, err
|
||||
}
|
||||
|
||||
// NodeFromLabels returns a Node for the injected labels
|
||||
func NodeFromLabels(labels map[string]string) *Node {
|
||||
keys := []string{}
|
||||
for key := range labels {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
// Sort the label to have a repeatable order
|
||||
sort.Strings(keys)
|
||||
|
||||
labelPairs := []string{}
|
||||
var rootNode *Node
|
||||
var prevNode *Node
|
||||
for _, key := range keys {
|
||||
value := labels[key]
|
||||
labelPairs = append(labelPairs, fmt.Sprintf("%q='%s'", key, value))
|
||||
// Value must be single quoted to prevent env variable expansion
|
||||
// See https://github.com/docker/docker/issues/26027
|
||||
node := newKeyValueNode(key, "'"+value+"'")
|
||||
rootNode, prevNode = appendKeyValueNode(node, rootNode, prevNode)
|
||||
}
|
||||
|
||||
return &Node{
|
||||
Value: command.Label,
|
||||
Original: commandLabel + " " + strings.Join(labelPairs, " "),
|
||||
Next: rootNode,
|
||||
}
|
||||
}
|
||||
|
||||
// parses a statement containing one or more keyword definition(s) and/or
|
||||
// value assignments, like `name1 name2= name3="" name4=value`.
|
||||
// Note that this is a stricter format than the old format of assignment,
|
||||
|
||||
+1
-2
@@ -262,8 +262,7 @@ func Parse(rwc io.Reader) (*Result, error) {
|
||||
}
|
||||
|
||||
if hasEmptyContinuationLine {
|
||||
warning := "[WARNING]: Empty continuation line found in:\n " + line
|
||||
warnings = append(warnings, warning)
|
||||
warnings = append(warnings, "[WARNING]: Empty continuation line found in:\n "+line)
|
||||
}
|
||||
|
||||
child, err := newNodeFromLine(line, d)
|
||||
|
||||
+40
-11
@@ -131,7 +131,7 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
||||
|
||||
if stopChar != scanner.EOF && ch == stopChar {
|
||||
sw.scanner.Next()
|
||||
break
|
||||
return result.String(), words.getWords(), nil
|
||||
}
|
||||
if fn, ok := charFuncMapping[ch]; ok {
|
||||
// Call special processing func for certain chars
|
||||
@@ -166,7 +166,9 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
||||
result.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
|
||||
if stopChar != scanner.EOF {
|
||||
return "", []string{}, errors.Errorf("unexpected end of statement while looking for matching %s", string(stopChar))
|
||||
}
|
||||
return result.String(), words.getWords(), nil
|
||||
}
|
||||
|
||||
@@ -259,22 +261,29 @@ func (sw *shellWord) processDollar() (string, error) {
|
||||
}
|
||||
|
||||
sw.scanner.Next()
|
||||
name := sw.processName()
|
||||
ch := sw.scanner.Peek()
|
||||
if ch == '}' {
|
||||
// Normal ${xx} case
|
||||
sw.scanner.Next()
|
||||
return sw.getEnv(name), nil
|
||||
switch sw.scanner.Peek() {
|
||||
case scanner.EOF:
|
||||
return "", errors.New("syntax error: missing '}'")
|
||||
case '{', '}', ':':
|
||||
// Invalid ${{xx}, ${:xx}, ${:}. ${} case
|
||||
return "", errors.New("syntax error: bad substitution")
|
||||
}
|
||||
if ch == ':' {
|
||||
name := sw.processName()
|
||||
ch := sw.scanner.Next()
|
||||
switch ch {
|
||||
case '}':
|
||||
// Normal ${xx} case
|
||||
return sw.getEnv(name), nil
|
||||
case ':':
|
||||
// Special ${xx:...} format processing
|
||||
// Yes it allows for recursive $'s in the ... spot
|
||||
|
||||
sw.scanner.Next() // skip over :
|
||||
modifier := sw.scanner.Next()
|
||||
|
||||
word, _, err := sw.processStopOn('}')
|
||||
if err != nil {
|
||||
if sw.scanner.Peek() == scanner.EOF {
|
||||
return "", errors.New("syntax error: missing '}'")
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -310,6 +319,14 @@ func (sw *shellWord) processName() string {
|
||||
for sw.scanner.Peek() != scanner.EOF {
|
||||
ch := sw.scanner.Peek()
|
||||
if name.Len() == 0 && unicode.IsDigit(ch) {
|
||||
for sw.scanner.Peek() != scanner.EOF && unicode.IsDigit(sw.scanner.Peek()) {
|
||||
// Keep reading until the first non-digit character, or EOF
|
||||
ch = sw.scanner.Next()
|
||||
name.WriteRune(ch)
|
||||
}
|
||||
return name.String()
|
||||
}
|
||||
if name.Len() == 0 && isSpecialParam(ch) {
|
||||
ch = sw.scanner.Next()
|
||||
return string(ch)
|
||||
}
|
||||
@@ -323,6 +340,18 @@ func (sw *shellWord) processName() string {
|
||||
return name.String()
|
||||
}
|
||||
|
||||
// isSpecialParam checks if the provided character is a special parameters,
|
||||
// as defined in http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_05_02
|
||||
func isSpecialParam(char rune) bool {
|
||||
switch char {
|
||||
case '@', '*', '#', '?', '-', '$', '!', '0':
|
||||
// Special parameters
|
||||
// http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_05_02
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (sw *shellWord) getEnv(name string) string {
|
||||
for _, env := range sw.envs {
|
||||
i := strings.Index(env, "=")
|
||||
|
||||
+1
-1
@@ -615,7 +615,7 @@ func (s sortableCacheSources) Swap(i, j int) {
|
||||
}
|
||||
|
||||
func newTarsumHash(stat *fsutil.Stat) (hash.Hash, error) {
|
||||
fi := &fsutil.StatInfo{stat}
|
||||
fi := &fsutil.StatInfo{Stat: stat}
|
||||
p := stat.Path
|
||||
if fi.IsDir() {
|
||||
p += string(os.PathSeparator)
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
iradix "github.com/hashicorp/go-immutable-radix"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tonistiigi/fsutil"
|
||||
)
|
||||
|
||||
+5
@@ -356,6 +356,11 @@ func (cli *Client) DaemonHost() string {
|
||||
return cli.host
|
||||
}
|
||||
|
||||
// HTTPClient returns a copy of the HTTP client bound to the server
|
||||
func (cli *Client) HTTPClient() *http.Client {
|
||||
return &*cli.client
|
||||
}
|
||||
|
||||
// ParseHostURL parses a url string, validates the string is a host url, and
|
||||
// returns the parsed URL
|
||||
func ParseHostURL(host string) (*url.URL, error) {
|
||||
|
||||
+3
-2
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
timetypes "github.com/docker/docker/api/types/time"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ContainerLogs returns the logs generated by a container in an io.ReadCloser.
|
||||
@@ -45,7 +46,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, container string, options
|
||||
if options.Since != "" {
|
||||
ts, err := timetypes.GetTimestamp(options.Since, time.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, `invalid value for "since"`)
|
||||
}
|
||||
query.Set("since", ts)
|
||||
}
|
||||
@@ -53,7 +54,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, container string, options
|
||||
if options.Until != "" {
|
||||
ts, err := timetypes.GetTimestamp(options.Until, time.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, `invalid value for "until"`)
|
||||
}
|
||||
query.Set("until", ts)
|
||||
}
|
||||
|
||||
+7
-2
@@ -8,8 +8,13 @@ import (
|
||||
timetypes "github.com/docker/docker/api/types/time"
|
||||
)
|
||||
|
||||
// ContainerStop stops a container without terminating the process.
|
||||
// The process is blocked until the container stops or the timeout expires.
|
||||
// ContainerStop stops a container. In case the container fails to stop
|
||||
// gracefully within a time frame specified by the timeout argument,
|
||||
// it is forcefully terminated (killed).
|
||||
//
|
||||
// If the timeout is nil, the container's StopTimeout value is used, if set,
|
||||
// otherwise the engine default. A negative timeout value can be specified,
|
||||
// meaning no timeout, i.e. no forceful termination is performed.
|
||||
func (cli *Client) ContainerStop(ctx context.Context, containerID string, timeout *time.Duration) error {
|
||||
query := url.Values{}
|
||||
if timeout != nil {
|
||||
|
||||
-1
@@ -2,7 +2,6 @@ package client // import "github.com/docker/docker/client"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
|
||||
+4
-2
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
@@ -33,6 +34,7 @@ type CommonAPIClient interface {
|
||||
VolumeAPIClient
|
||||
ClientVersion() string
|
||||
DaemonHost() string
|
||||
HTTPClient() *http.Client
|
||||
ServerVersion(ctx context.Context) (types.Version, error)
|
||||
NegotiateAPIVersion(ctx context.Context)
|
||||
NegotiateAPIVersionPing(types.Ping)
|
||||
@@ -168,10 +170,10 @@ type SystemAPIClient interface {
|
||||
|
||||
// VolumeAPIClient defines API client methods for the volumes
|
||||
type VolumeAPIClient interface {
|
||||
VolumeCreate(ctx context.Context, options volumetypes.VolumesCreateBody) (types.Volume, error)
|
||||
VolumeCreate(ctx context.Context, options volumetypes.VolumeCreateBody) (types.Volume, error)
|
||||
VolumeInspect(ctx context.Context, volumeID string) (types.Volume, error)
|
||||
VolumeInspectWithRaw(ctx context.Context, volumeID string) (types.Volume, []byte, error)
|
||||
VolumeList(ctx context.Context, filter filters.Args) (volumetypes.VolumesListOKBody, error)
|
||||
VolumeList(ctx context.Context, filter filters.Args) (volumetypes.VolumeListOKBody, error)
|
||||
VolumeRemove(ctx context.Context, volumeID string, force bool) error
|
||||
VolumesPrune(ctx context.Context, pruneFilter filters.Args) (types.VolumesPruneReport, error)
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,9 +1,8 @@
|
||||
package client // import "github.com/docker/docker/client"
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"context"
|
||||
"net/url"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -136,7 +136,7 @@ func imageWithDigestString(image string, dgst digest.Digest) string {
|
||||
|
||||
// imageWithTagString takes an image string, and returns a tagged image
|
||||
// string, adding a 'latest' tag if one was not provided. It returns an
|
||||
// emptry string if a canonical reference was provided
|
||||
// empty string if a canonical reference was provided
|
||||
func imageWithTagString(image string) string {
|
||||
namedRef, err := reference.ParseNormalizedNamed(image)
|
||||
if err == nil {
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
timetypes "github.com/docker/docker/api/types/time"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ServiceLogs returns the logs generated by a service in an io.ReadCloser.
|
||||
@@ -25,7 +26,7 @@ func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options ty
|
||||
if options.Since != "" {
|
||||
ts, err := timetypes.GetTimestamp(options.Since, time.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, `invalid value for "since"`)
|
||||
}
|
||||
query.Set("since", ts)
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
// VolumeCreate creates a volume in the docker host.
|
||||
func (cli *Client) VolumeCreate(ctx context.Context, options volumetypes.VolumesCreateBody) (types.Volume, error) {
|
||||
func (cli *Client) VolumeCreate(ctx context.Context, options volumetypes.VolumeCreateBody) (types.Volume, error) {
|
||||
var volume types.Volume
|
||||
resp, err := cli.post(ctx, "/volumes/create", nil, options, nil)
|
||||
if err != nil {
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ import (
|
||||
)
|
||||
|
||||
// VolumeList returns the volumes configured in the docker host.
|
||||
func (cli *Client) VolumeList(ctx context.Context, filter filters.Args) (volumetypes.VolumesListOKBody, error) {
|
||||
var volumes volumetypes.VolumesListOKBody
|
||||
func (cli *Client) VolumeList(ctx context.Context, filter filters.Args) (volumetypes.VolumeListOKBody, error) {
|
||||
var volumes volumetypes.VolumeListOKBody
|
||||
query := url.Values{}
|
||||
|
||||
if filter.Len() > 0 {
|
||||
|
||||
+1
-376
@@ -6,11 +6,9 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
@@ -19,7 +17,6 @@ import (
|
||||
"github.com/containerd/containerd/cio"
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
mounttypes "github.com/docker/docker/api/types/mount"
|
||||
networktypes "github.com/docker/docker/api/types/network"
|
||||
swarmtypes "github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/container/stream"
|
||||
"github.com/docker/docker/daemon/exec"
|
||||
@@ -28,7 +25,6 @@ import (
|
||||
"github.com/docker/docker/daemon/network"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
@@ -36,15 +32,9 @@ import (
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/restartmanager"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/volume"
|
||||
volumemounts "github.com/docker/docker/volume/mounts"
|
||||
"github.com/docker/go-connections/nat"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/options"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/docker/go-units"
|
||||
agentexec "github.com/docker/swarmkit/agent/exec"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -52,11 +42,6 @@ import (
|
||||
|
||||
const configFileName = "config.v2.json"
|
||||
|
||||
var (
|
||||
errInvalidEndpoint = errors.New("invalid endpoint while building port map info")
|
||||
errInvalidNetwork = errors.New("invalid network settings while building port map info")
|
||||
)
|
||||
|
||||
// ExitStatus provides exit reasons for a container.
|
||||
type ExitStatus struct {
|
||||
// The exit code with which the container exited.
|
||||
@@ -538,366 +523,6 @@ func (container *Container) InitDNSHostConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetEndpointInNetwork returns the container's endpoint to the provided network.
|
||||
func (container *Container) GetEndpointInNetwork(n libnetwork.Network) (libnetwork.Endpoint, error) {
|
||||
endpointName := strings.TrimPrefix(container.Name, "/")
|
||||
return n.EndpointByName(endpointName)
|
||||
}
|
||||
|
||||
func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint) error {
|
||||
if ep == nil {
|
||||
return errInvalidEndpoint
|
||||
}
|
||||
|
||||
networkSettings := container.NetworkSettings
|
||||
if networkSettings == nil {
|
||||
return errInvalidNetwork
|
||||
}
|
||||
|
||||
if len(networkSettings.Ports) == 0 {
|
||||
pm, err := getEndpointPortMapInfo(ep)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
networkSettings.Ports = pm
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEndpointPortMapInfo(ep libnetwork.Endpoint) (nat.PortMap, error) {
|
||||
pm := nat.PortMap{}
|
||||
driverInfo, err := ep.DriverInfo()
|
||||
if err != nil {
|
||||
return pm, err
|
||||
}
|
||||
|
||||
if driverInfo == nil {
|
||||
// It is not an error for epInfo to be nil
|
||||
return pm, nil
|
||||
}
|
||||
|
||||
if expData, ok := driverInfo[netlabel.ExposedPorts]; ok {
|
||||
if exposedPorts, ok := expData.([]types.TransportPort); ok {
|
||||
for _, tp := range exposedPorts {
|
||||
natPort, err := nat.NewPort(tp.Proto.String(), strconv.Itoa(int(tp.Port)))
|
||||
if err != nil {
|
||||
return pm, fmt.Errorf("Error parsing Port value(%v):%v", tp.Port, err)
|
||||
}
|
||||
pm[natPort] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mapData, ok := driverInfo[netlabel.PortMap]
|
||||
if !ok {
|
||||
return pm, nil
|
||||
}
|
||||
|
||||
if portMapping, ok := mapData.([]types.PortBinding); ok {
|
||||
for _, pp := range portMapping {
|
||||
natPort, err := nat.NewPort(pp.Proto.String(), strconv.Itoa(int(pp.Port)))
|
||||
if err != nil {
|
||||
return pm, err
|
||||
}
|
||||
natBndg := nat.PortBinding{HostIP: pp.HostIP.String(), HostPort: strconv.Itoa(int(pp.HostPort))}
|
||||
pm[natPort] = append(pm[natPort], natBndg)
|
||||
}
|
||||
}
|
||||
|
||||
return pm, nil
|
||||
}
|
||||
|
||||
// GetSandboxPortMapInfo retrieves the current port-mapping programmed for the given sandbox
|
||||
func GetSandboxPortMapInfo(sb libnetwork.Sandbox) nat.PortMap {
|
||||
pm := nat.PortMap{}
|
||||
if sb == nil {
|
||||
return pm
|
||||
}
|
||||
|
||||
for _, ep := range sb.Endpoints() {
|
||||
pm, _ = getEndpointPortMapInfo(ep)
|
||||
if len(pm) > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return pm
|
||||
}
|
||||
|
||||
// BuildEndpointInfo sets endpoint-related fields on container.NetworkSettings based on the provided network and endpoint.
|
||||
func (container *Container) BuildEndpointInfo(n libnetwork.Network, ep libnetwork.Endpoint) error {
|
||||
if ep == nil {
|
||||
return errInvalidEndpoint
|
||||
}
|
||||
|
||||
networkSettings := container.NetworkSettings
|
||||
if networkSettings == nil {
|
||||
return errInvalidNetwork
|
||||
}
|
||||
|
||||
epInfo := ep.Info()
|
||||
if epInfo == nil {
|
||||
// It is not an error to get an empty endpoint info
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, ok := networkSettings.Networks[n.Name()]; !ok {
|
||||
networkSettings.Networks[n.Name()] = &network.EndpointSettings{
|
||||
EndpointSettings: &networktypes.EndpointSettings{},
|
||||
}
|
||||
}
|
||||
networkSettings.Networks[n.Name()].NetworkID = n.ID()
|
||||
networkSettings.Networks[n.Name()].EndpointID = ep.ID()
|
||||
|
||||
iface := epInfo.Iface()
|
||||
if iface == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if iface.MacAddress() != nil {
|
||||
networkSettings.Networks[n.Name()].MacAddress = iface.MacAddress().String()
|
||||
}
|
||||
|
||||
if iface.Address() != nil {
|
||||
ones, _ := iface.Address().Mask.Size()
|
||||
networkSettings.Networks[n.Name()].IPAddress = iface.Address().IP.String()
|
||||
networkSettings.Networks[n.Name()].IPPrefixLen = ones
|
||||
}
|
||||
|
||||
if iface.AddressIPv6() != nil && iface.AddressIPv6().IP.To16() != nil {
|
||||
onesv6, _ := iface.AddressIPv6().Mask.Size()
|
||||
networkSettings.Networks[n.Name()].GlobalIPv6Address = iface.AddressIPv6().IP.String()
|
||||
networkSettings.Networks[n.Name()].GlobalIPv6PrefixLen = onesv6
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type named interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
// UpdateJoinInfo updates network settings when container joins network n with endpoint ep.
|
||||
func (container *Container) UpdateJoinInfo(n named, ep libnetwork.Endpoint) error {
|
||||
if err := container.buildPortMapInfo(ep); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
epInfo := ep.Info()
|
||||
if epInfo == nil {
|
||||
// It is not an error to get an empty endpoint info
|
||||
return nil
|
||||
}
|
||||
if epInfo.Gateway() != nil {
|
||||
container.NetworkSettings.Networks[n.Name()].Gateway = epInfo.Gateway().String()
|
||||
}
|
||||
if epInfo.GatewayIPv6().To16() != nil {
|
||||
container.NetworkSettings.Networks[n.Name()].IPv6Gateway = epInfo.GatewayIPv6().String()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateSandboxNetworkSettings updates the sandbox ID and Key.
|
||||
func (container *Container) UpdateSandboxNetworkSettings(sb libnetwork.Sandbox) error {
|
||||
container.NetworkSettings.SandboxID = sb.ID()
|
||||
container.NetworkSettings.SandboxKey = sb.Key()
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuildJoinOptions builds endpoint Join options from a given network.
|
||||
func (container *Container) BuildJoinOptions(n named) ([]libnetwork.EndpointOption, error) {
|
||||
var joinOptions []libnetwork.EndpointOption
|
||||
if epConfig, ok := container.NetworkSettings.Networks[n.Name()]; ok {
|
||||
for _, str := range epConfig.Links {
|
||||
name, alias, err := opts.ParseLink(str)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
joinOptions = append(joinOptions, libnetwork.CreateOptionAlias(name, alias))
|
||||
}
|
||||
for k, v := range epConfig.DriverOpts {
|
||||
joinOptions = append(joinOptions, libnetwork.EndpointOptionGeneric(options.Generic{k: v}))
|
||||
}
|
||||
}
|
||||
|
||||
return joinOptions, nil
|
||||
}
|
||||
|
||||
// BuildCreateEndpointOptions builds endpoint options from a given network.
|
||||
func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epConfig *networktypes.EndpointSettings, sb libnetwork.Sandbox, daemonDNS []string) ([]libnetwork.EndpointOption, error) {
|
||||
var (
|
||||
bindings = make(nat.PortMap)
|
||||
pbList []types.PortBinding
|
||||
exposeList []types.TransportPort
|
||||
createOptions []libnetwork.EndpointOption
|
||||
)
|
||||
|
||||
defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName()
|
||||
|
||||
if (!container.EnableServiceDiscoveryOnDefaultNetwork() && n.Name() == defaultNetName) ||
|
||||
container.NetworkSettings.IsAnonymousEndpoint {
|
||||
createOptions = append(createOptions, libnetwork.CreateOptionAnonymous())
|
||||
}
|
||||
|
||||
if epConfig != nil {
|
||||
ipam := epConfig.IPAMConfig
|
||||
|
||||
if ipam != nil {
|
||||
var (
|
||||
ipList []net.IP
|
||||
ip, ip6, linkip net.IP
|
||||
)
|
||||
|
||||
for _, ips := range ipam.LinkLocalIPs {
|
||||
if linkip = net.ParseIP(ips); linkip == nil && ips != "" {
|
||||
return nil, errors.Errorf("Invalid link-local IP address: %s", ipam.LinkLocalIPs)
|
||||
}
|
||||
ipList = append(ipList, linkip)
|
||||
|
||||
}
|
||||
|
||||
if ip = net.ParseIP(ipam.IPv4Address); ip == nil && ipam.IPv4Address != "" {
|
||||
return nil, errors.Errorf("Invalid IPv4 address: %s)", ipam.IPv4Address)
|
||||
}
|
||||
|
||||
if ip6 = net.ParseIP(ipam.IPv6Address); ip6 == nil && ipam.IPv6Address != "" {
|
||||
return nil, errors.Errorf("Invalid IPv6 address: %s)", ipam.IPv6Address)
|
||||
}
|
||||
|
||||
createOptions = append(createOptions,
|
||||
libnetwork.CreateOptionIpam(ip, ip6, ipList, nil))
|
||||
|
||||
}
|
||||
|
||||
for _, alias := range epConfig.Aliases {
|
||||
createOptions = append(createOptions, libnetwork.CreateOptionMyAlias(alias))
|
||||
}
|
||||
for k, v := range epConfig.DriverOpts {
|
||||
createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(options.Generic{k: v}))
|
||||
}
|
||||
}
|
||||
|
||||
if container.NetworkSettings.Service != nil {
|
||||
svcCfg := container.NetworkSettings.Service
|
||||
|
||||
var vip string
|
||||
if svcCfg.VirtualAddresses[n.ID()] != nil {
|
||||
vip = svcCfg.VirtualAddresses[n.ID()].IPv4
|
||||
}
|
||||
|
||||
var portConfigs []*libnetwork.PortConfig
|
||||
for _, portConfig := range svcCfg.ExposedPorts {
|
||||
portConfigs = append(portConfigs, &libnetwork.PortConfig{
|
||||
Name: portConfig.Name,
|
||||
Protocol: libnetwork.PortConfig_Protocol(portConfig.Protocol),
|
||||
TargetPort: portConfig.TargetPort,
|
||||
PublishedPort: portConfig.PublishedPort,
|
||||
})
|
||||
}
|
||||
|
||||
createOptions = append(createOptions, libnetwork.CreateOptionService(svcCfg.Name, svcCfg.ID, net.ParseIP(vip), portConfigs, svcCfg.Aliases[n.ID()]))
|
||||
}
|
||||
|
||||
if !containertypes.NetworkMode(n.Name()).IsUserDefined() {
|
||||
createOptions = append(createOptions, libnetwork.CreateOptionDisableResolution())
|
||||
}
|
||||
|
||||
// configs that are applicable only for the endpoint in the network
|
||||
// to which container was connected to on docker run.
|
||||
// Ideally all these network-specific endpoint configurations must be moved under
|
||||
// container.NetworkSettings.Networks[n.Name()]
|
||||
if n.Name() == container.HostConfig.NetworkMode.NetworkName() ||
|
||||
(n.Name() == defaultNetName && container.HostConfig.NetworkMode.IsDefault()) {
|
||||
if container.Config.MacAddress != "" {
|
||||
mac, err := net.ParseMAC(container.Config.MacAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
genericOption := options.Generic{
|
||||
netlabel.MacAddress: mac,
|
||||
}
|
||||
|
||||
createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Port-mapping rules belong to the container & applicable only to non-internal networks
|
||||
portmaps := GetSandboxPortMapInfo(sb)
|
||||
if n.Info().Internal() || len(portmaps) > 0 {
|
||||
return createOptions, nil
|
||||
}
|
||||
|
||||
if container.HostConfig.PortBindings != nil {
|
||||
for p, b := range container.HostConfig.PortBindings {
|
||||
bindings[p] = []nat.PortBinding{}
|
||||
for _, bb := range b {
|
||||
bindings[p] = append(bindings[p], nat.PortBinding{
|
||||
HostIP: bb.HostIP,
|
||||
HostPort: bb.HostPort,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
portSpecs := container.Config.ExposedPorts
|
||||
ports := make([]nat.Port, len(portSpecs))
|
||||
var i int
|
||||
for p := range portSpecs {
|
||||
ports[i] = p
|
||||
i++
|
||||
}
|
||||
nat.SortPortMap(ports, bindings)
|
||||
for _, port := range ports {
|
||||
expose := types.TransportPort{}
|
||||
expose.Proto = types.ParseProtocol(port.Proto())
|
||||
expose.Port = uint16(port.Int())
|
||||
exposeList = append(exposeList, expose)
|
||||
|
||||
pb := types.PortBinding{Port: expose.Port, Proto: expose.Proto}
|
||||
binding := bindings[port]
|
||||
for i := 0; i < len(binding); i++ {
|
||||
pbCopy := pb.GetCopy()
|
||||
newP, err := nat.NewPort(nat.SplitProtoPort(binding[i].HostPort))
|
||||
var portStart, portEnd int
|
||||
if err == nil {
|
||||
portStart, portEnd, err = newP.Range()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Error parsing HostPort value (%s)", binding[i].HostPort)
|
||||
}
|
||||
pbCopy.HostPort = uint16(portStart)
|
||||
pbCopy.HostPortEnd = uint16(portEnd)
|
||||
pbCopy.HostIP = net.ParseIP(binding[i].HostIP)
|
||||
pbList = append(pbList, pbCopy)
|
||||
}
|
||||
|
||||
if container.HostConfig.PublishAllPorts && len(binding) == 0 {
|
||||
pbList = append(pbList, pb)
|
||||
}
|
||||
}
|
||||
|
||||
var dns []string
|
||||
|
||||
if len(container.HostConfig.DNS) > 0 {
|
||||
dns = container.HostConfig.DNS
|
||||
} else if len(daemonDNS) > 0 {
|
||||
dns = daemonDNS
|
||||
}
|
||||
|
||||
if len(dns) > 0 {
|
||||
createOptions = append(createOptions,
|
||||
libnetwork.CreateOptionDNS(dns))
|
||||
}
|
||||
|
||||
createOptions = append(createOptions,
|
||||
libnetwork.CreateOptionPortMapping(pbList),
|
||||
libnetwork.CreateOptionExposedPorts(exposeList))
|
||||
|
||||
return createOptions, nil
|
||||
}
|
||||
|
||||
// UpdateMonitor updates monitor configure for running container
|
||||
func (container *Container) UpdateMonitor(restartPolicy containertypes.RestartPolicy) {
|
||||
type policySetter interface {
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
package container // import "github.com/docker/docker/container"
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func detachMounted(path string) error {
|
||||
return unix.Unmount(path, unix.MNT_DETACH)
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
// +build freebsd
|
||||
|
||||
package container // import "github.com/docker/docker/container"
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func detachMounted(path string) error {
|
||||
// FreeBSD do not support the lazy unmount or MNT_DETACH feature.
|
||||
// Therefore there are separate definitions for this.
|
||||
return unix.Unmount(path, 0)
|
||||
}
|
||||
|
||||
// SecretMounts returns the mounts for the secret path
|
||||
func (container *Container) SecretMounts() []Mount {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmountSecrets unmounts the fs for secrets
|
||||
func (container *Container) UnmountSecrets() error {
|
||||
return nil
|
||||
}
|
||||
+4
-3
@@ -1,4 +1,4 @@
|
||||
// +build linux freebsd
|
||||
// +build !windows
|
||||
|
||||
package container // import "github.com/docker/docker/container"
|
||||
|
||||
@@ -23,7 +23,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultStopTimeout is the timeout (in seconds) for the syscall signal used to stop a container.
|
||||
// DefaultStopTimeout sets the default time, in seconds, to wait
|
||||
// for the graceful container stop before forcefully terminating it.
|
||||
DefaultStopTimeout = 10
|
||||
|
||||
containerSecretMountPath = "/run/secrets"
|
||||
@@ -380,7 +381,7 @@ func (container *Container) DetachAndUnmount(volumeEventLog func(name, action st
|
||||
}
|
||||
|
||||
for _, mountPath := range mountPaths {
|
||||
if err := detachMounted(mountPath); err != nil {
|
||||
if err := mount.Unmount(mountPath); err != nil {
|
||||
logrus.Warnf("%s unmountVolumes: Failed to do lazy umount fo volume '%s': %v", container.ID, mountPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -340,7 +340,7 @@ func (v *memdbView) transform(container *Container) *Snapshot {
|
||||
}
|
||||
|
||||
if len(container.Args) > 0 {
|
||||
args := []string{}
|
||||
var args []string
|
||||
for _, arg := range container.Args {
|
||||
if strings.Contains(arg, " ") {
|
||||
args = append(args, fmt.Sprintf("'%s'", arg))
|
||||
|
||||
+23
-1
@@ -4,8 +4,11 @@ import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/plugingetter"
|
||||
"github.com/docker/docker/pkg/plugins"
|
||||
"github.com/docker/docker/plugin/v2"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func lookupPlugin(name string, pg plugingetter.PluginGetter, config Options) (Driver, error) {
|
||||
@@ -28,6 +31,25 @@ func newPluginDriver(name string, pl plugingetter.CompatPlugin, config Options)
|
||||
}
|
||||
}
|
||||
}
|
||||
proxy := &graphDriverProxy{name, pl, Capabilities{}}
|
||||
|
||||
var proxy *graphDriverProxy
|
||||
|
||||
switch pt := pl.(type) {
|
||||
case plugingetter.PluginWithV1Client:
|
||||
proxy = &graphDriverProxy{name, pl, Capabilities{}, pt.Client()}
|
||||
case plugingetter.PluginAddr:
|
||||
if pt.Protocol() != plugins.ProtocolSchemeHTTPV1 {
|
||||
return nil, errors.Errorf("plugin protocol not supported: %s", pt.Protocol())
|
||||
}
|
||||
addr := pt.Addr()
|
||||
client, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pt.Timeout())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error creating plugin client")
|
||||
}
|
||||
proxy = &graphDriverProxy{name, pl, Capabilities{}, client}
|
||||
default:
|
||||
return nil, errdefs.System(errors.Errorf("got unknown plugin type %T", pt))
|
||||
}
|
||||
|
||||
return proxy, proxy.Init(filepath.Join(home, name), config.DriverOptions, config.UIDMaps, config.GIDMaps)
|
||||
}
|
||||
|
||||
+18
-17
@@ -13,9 +13,10 @@ import (
|
||||
)
|
||||
|
||||
type graphDriverProxy struct {
|
||||
name string
|
||||
p plugingetter.CompatPlugin
|
||||
caps Capabilities
|
||||
name string
|
||||
p plugingetter.CompatPlugin
|
||||
caps Capabilities
|
||||
client *plugins.Client
|
||||
}
|
||||
|
||||
type graphDriverRequest struct {
|
||||
@@ -57,7 +58,7 @@ func (d *graphDriverProxy) Init(home string, opts []string, uidMaps, gidMaps []i
|
||||
GIDMaps: gidMaps,
|
||||
}
|
||||
var ret graphDriverResponse
|
||||
if err := d.p.Client().Call("GraphDriver.Init", args, &ret); err != nil {
|
||||
if err := d.client.Call("GraphDriver.Init", args, &ret); err != nil {
|
||||
return err
|
||||
}
|
||||
if ret.Err != "" {
|
||||
@@ -74,7 +75,7 @@ func (d *graphDriverProxy) Init(home string, opts []string, uidMaps, gidMaps []i
|
||||
func (d *graphDriverProxy) fetchCaps() (Capabilities, error) {
|
||||
args := &graphDriverRequest{}
|
||||
var ret graphDriverResponse
|
||||
if err := d.p.Client().Call("GraphDriver.Capabilities", args, &ret); err != nil {
|
||||
if err := d.client.Call("GraphDriver.Capabilities", args, &ret); err != nil {
|
||||
if !plugins.IsNotFound(err) {
|
||||
return Capabilities{}, err
|
||||
}
|
||||
@@ -108,7 +109,7 @@ func (d *graphDriverProxy) create(method, id, parent string, opts *CreateOpts) e
|
||||
args.StorageOpt = opts.StorageOpt
|
||||
}
|
||||
var ret graphDriverResponse
|
||||
if err := d.p.Client().Call(method, args, &ret); err != nil {
|
||||
if err := d.client.Call(method, args, &ret); err != nil {
|
||||
return err
|
||||
}
|
||||
if ret.Err != "" {
|
||||
@@ -120,7 +121,7 @@ func (d *graphDriverProxy) create(method, id, parent string, opts *CreateOpts) e
|
||||
func (d *graphDriverProxy) Remove(id string) error {
|
||||
args := &graphDriverRequest{ID: id}
|
||||
var ret graphDriverResponse
|
||||
if err := d.p.Client().Call("GraphDriver.Remove", args, &ret); err != nil {
|
||||
if err := d.client.Call("GraphDriver.Remove", args, &ret); err != nil {
|
||||
return err
|
||||
}
|
||||
if ret.Err != "" {
|
||||
@@ -135,7 +136,7 @@ func (d *graphDriverProxy) Get(id, mountLabel string) (containerfs.ContainerFS,
|
||||
MountLabel: mountLabel,
|
||||
}
|
||||
var ret graphDriverResponse
|
||||
if err := d.p.Client().Call("GraphDriver.Get", args, &ret); err != nil {
|
||||
if err := d.client.Call("GraphDriver.Get", args, &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var err error
|
||||
@@ -148,7 +149,7 @@ func (d *graphDriverProxy) Get(id, mountLabel string) (containerfs.ContainerFS,
|
||||
func (d *graphDriverProxy) Put(id string) error {
|
||||
args := &graphDriverRequest{ID: id}
|
||||
var ret graphDriverResponse
|
||||
if err := d.p.Client().Call("GraphDriver.Put", args, &ret); err != nil {
|
||||
if err := d.client.Call("GraphDriver.Put", args, &ret); err != nil {
|
||||
return err
|
||||
}
|
||||
if ret.Err != "" {
|
||||
@@ -160,7 +161,7 @@ func (d *graphDriverProxy) Put(id string) error {
|
||||
func (d *graphDriverProxy) Exists(id string) bool {
|
||||
args := &graphDriverRequest{ID: id}
|
||||
var ret graphDriverResponse
|
||||
if err := d.p.Client().Call("GraphDriver.Exists", args, &ret); err != nil {
|
||||
if err := d.client.Call("GraphDriver.Exists", args, &ret); err != nil {
|
||||
return false
|
||||
}
|
||||
return ret.Exists
|
||||
@@ -169,7 +170,7 @@ func (d *graphDriverProxy) Exists(id string) bool {
|
||||
func (d *graphDriverProxy) Status() [][2]string {
|
||||
args := &graphDriverRequest{}
|
||||
var ret graphDriverResponse
|
||||
if err := d.p.Client().Call("GraphDriver.Status", args, &ret); err != nil {
|
||||
if err := d.client.Call("GraphDriver.Status", args, &ret); err != nil {
|
||||
return nil
|
||||
}
|
||||
return ret.Status
|
||||
@@ -180,7 +181,7 @@ func (d *graphDriverProxy) GetMetadata(id string) (map[string]string, error) {
|
||||
ID: id,
|
||||
}
|
||||
var ret graphDriverResponse
|
||||
if err := d.p.Client().Call("GraphDriver.GetMetadata", args, &ret); err != nil {
|
||||
if err := d.client.Call("GraphDriver.GetMetadata", args, &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ret.Err != "" {
|
||||
@@ -199,7 +200,7 @@ func (d *graphDriverProxy) Cleanup() error {
|
||||
|
||||
args := &graphDriverRequest{}
|
||||
var ret graphDriverResponse
|
||||
if err := d.p.Client().Call("GraphDriver.Cleanup", args, &ret); err != nil {
|
||||
if err := d.client.Call("GraphDriver.Cleanup", args, &ret); err != nil {
|
||||
return nil
|
||||
}
|
||||
if ret.Err != "" {
|
||||
@@ -213,7 +214,7 @@ func (d *graphDriverProxy) Diff(id, parent string) (io.ReadCloser, error) {
|
||||
ID: id,
|
||||
Parent: parent,
|
||||
}
|
||||
body, err := d.p.Client().Stream("GraphDriver.Diff", args)
|
||||
body, err := d.client.Stream("GraphDriver.Diff", args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -226,7 +227,7 @@ func (d *graphDriverProxy) Changes(id, parent string) ([]archive.Change, error)
|
||||
Parent: parent,
|
||||
}
|
||||
var ret graphDriverResponse
|
||||
if err := d.p.Client().Call("GraphDriver.Changes", args, &ret); err != nil {
|
||||
if err := d.client.Call("GraphDriver.Changes", args, &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ret.Err != "" {
|
||||
@@ -238,7 +239,7 @@ func (d *graphDriverProxy) Changes(id, parent string) ([]archive.Change, error)
|
||||
|
||||
func (d *graphDriverProxy) ApplyDiff(id, parent string, diff io.Reader) (int64, error) {
|
||||
var ret graphDriverResponse
|
||||
if err := d.p.Client().SendFile(fmt.Sprintf("GraphDriver.ApplyDiff?id=%s&parent=%s", id, parent), diff, &ret); err != nil {
|
||||
if err := d.client.SendFile(fmt.Sprintf("GraphDriver.ApplyDiff?id=%s&parent=%s", id, parent), diff, &ret); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if ret.Err != "" {
|
||||
@@ -253,7 +254,7 @@ func (d *graphDriverProxy) DiffSize(id, parent string) (int64, error) {
|
||||
Parent: parent,
|
||||
}
|
||||
var ret graphDriverResponse
|
||||
if err := d.p.Client().Call("GraphDriver.DiffSize", args, &ret); err != nil {
|
||||
if err := d.client.Call("GraphDriver.DiffSize", args, &ret); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if ret.Err != "" {
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ func (a *pluginAdapter) Log(msg *Message) error {
|
||||
|
||||
a.buf.Line = msg.Line
|
||||
a.buf.TimeNano = msg.Timestamp.UnixNano()
|
||||
a.buf.Partial = (msg.PLogMetaData != nil)
|
||||
a.buf.Partial = msg.PLogMetaData != nil
|
||||
a.buf.Source = msg.Source
|
||||
|
||||
err := a.enc.Encode(&a.buf)
|
||||
|
||||
+4
@@ -81,6 +81,7 @@ func (c *Copier) copySrc(name string, src io.Reader) {
|
||||
read, err := src.Read(buf[n:upto])
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
logReadsFailedCount.Inc(1)
|
||||
logrus.Errorf("Error scanning log stream: %s", err)
|
||||
return
|
||||
}
|
||||
@@ -120,6 +121,7 @@ func (c *Copier) copySrc(name string, src io.Reader) {
|
||||
}
|
||||
|
||||
if logErr := c.dst.Log(msg); logErr != nil {
|
||||
logWritesFailedCount.Inc(1)
|
||||
logrus.Errorf("Failed to log msg %q for logger %s: %s", msg.Line, c.dst.Name(), logErr)
|
||||
}
|
||||
}
|
||||
@@ -143,6 +145,7 @@ func (c *Copier) copySrc(name string, src io.Reader) {
|
||||
partialid = stringid.GenerateRandomID()
|
||||
ordinal = 1
|
||||
firstPartial = false
|
||||
totalPartialLogs.Inc(1)
|
||||
} else {
|
||||
msg.Timestamp = partialTS
|
||||
}
|
||||
@@ -151,6 +154,7 @@ func (c *Copier) copySrc(name string, src io.Reader) {
|
||||
hasMorePartial = true
|
||||
|
||||
if logErr := c.dst.Log(msg); logErr != nil {
|
||||
logWritesFailedCount.Inc(1)
|
||||
logrus.Errorf("Failed to log msg %q for logger %s: %s", msg.Line, c.dst.Name(), logErr)
|
||||
}
|
||||
p = 0
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/plugingetter"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/docker/docker/daemon/logger"
|
||||
"github.com/docker/docker/daemon/logger/jsonfilelog/jsonlog"
|
||||
"github.com/docker/docker/daemon/logger/loggerutils"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
+23
-13
@@ -97,7 +97,7 @@ type LogFile struct {
|
||||
|
||||
type makeDecoderFunc func(rdr io.Reader) func() (*logger.Message, error)
|
||||
|
||||
//NewLogFile creates new LogFile
|
||||
// NewLogFile creates new LogFile
|
||||
func NewLogFile(logPath string, capacity int64, maxFiles int, compress bool, marshaller logger.MarshalFunc, decodeFunc makeDecoderFunc, perms os.FileMode) (*LogFile, error) {
|
||||
log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, perms)
|
||||
if err != nil {
|
||||
@@ -201,6 +201,13 @@ func rotate(name string, maxFiles int, compress bool) error {
|
||||
if compress {
|
||||
extension = ".gz"
|
||||
}
|
||||
|
||||
lastFile := fmt.Sprintf("%s.%d%s", name, maxFiles-1, extension)
|
||||
err := os.Remove(lastFile)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return errors.Wrap(err, "error removing oldest log file")
|
||||
}
|
||||
|
||||
for i := maxFiles - 1; i > 1; i-- {
|
||||
toPath := name + "." + strconv.Itoa(i) + extension
|
||||
fromPath := name + "." + strconv.Itoa(i-1) + extension
|
||||
@@ -230,7 +237,7 @@ func compressFile(fileName string, lastTimestamp time.Time) {
|
||||
}
|
||||
}()
|
||||
|
||||
outFile, err := os.OpenFile(fileName+".gz", os.O_CREATE|os.O_RDWR, 0640)
|
||||
outFile, err := os.OpenFile(fileName+".gz", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0640)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to open or create gzip log file: %v", err)
|
||||
return
|
||||
@@ -251,7 +258,7 @@ func compressFile(fileName string, lastTimestamp time.Time) {
|
||||
compressWriter.Header.Extra, err = json.Marshal(&extra)
|
||||
if err != nil {
|
||||
// Here log the error only and don't return since this is just an optimization.
|
||||
logrus.Warningf("Failed to marshal JSON: %v", err)
|
||||
logrus.Warningf("Failed to marshal gzip header as JSON: %v", err)
|
||||
}
|
||||
|
||||
_, err = pools.Copy(compressWriter, file)
|
||||
@@ -281,6 +288,9 @@ func (w *LogFile) Close() error {
|
||||
}
|
||||
|
||||
// ReadLogs decodes entries from log files and sends them the passed in watcher
|
||||
//
|
||||
// Note: Using the follow option can become inconsistent in cases with very frequent rotations and max log files is 1.
|
||||
// TODO: Consider a different implementation which can effectively follow logs under frequent rotations.
|
||||
func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher) {
|
||||
w.mu.RLock()
|
||||
currentFile, err := os.Open(w.f.Name())
|
||||
@@ -364,7 +374,7 @@ func (w *LogFile) openRotatedFiles(config logger.ReadConfig) (files []*os.File,
|
||||
f, err := os.Open(fmt.Sprintf("%s.%d", w.f.Name(), i-1))
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "error opening rotated log file")
|
||||
}
|
||||
|
||||
fileName := fmt.Sprintf("%s.%d.gz", w.f.Name(), i-1)
|
||||
@@ -377,8 +387,8 @@ func (w *LogFile) openRotatedFiles(config logger.ReadConfig) (files []*os.File,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
if !os.IsNotExist(errors.Cause(err)) {
|
||||
return nil, errors.Wrap(err, "error getting reference to decompressed log file")
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -399,13 +409,13 @@ func (w *LogFile) openRotatedFiles(config logger.ReadConfig) (files []*os.File,
|
||||
func decompressfile(fileName, destFileName string, since time.Time) (*os.File, error) {
|
||||
cf, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "error opening file for decompression")
|
||||
}
|
||||
defer cf.Close()
|
||||
|
||||
rc, err := gzip.NewReader(cf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "error making gzip reader for compressed log file")
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
@@ -418,17 +428,17 @@ func decompressfile(fileName, destFileName string, since time.Time) (*os.File, e
|
||||
|
||||
rs, err := os.OpenFile(destFileName, os.O_CREATE|os.O_RDWR, 0640)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "error creating file for copying decompressed log stream")
|
||||
}
|
||||
|
||||
_, err = pools.Copy(rs, rc)
|
||||
if err != nil {
|
||||
rs.Close()
|
||||
rErr := os.Remove(rs.Name())
|
||||
if rErr != nil && os.IsNotExist(rErr) {
|
||||
if rErr != nil && !os.IsNotExist(rErr) {
|
||||
logrus.Errorf("Failed to remove the logfile %q: %v", rs.Name(), rErr)
|
||||
}
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "error while copying decompressed log stream to file")
|
||||
}
|
||||
|
||||
return rs, nil
|
||||
@@ -461,7 +471,7 @@ func tailFile(f io.ReadSeeker, watcher *logger.LogWatcher, createDecoder makeDec
|
||||
for {
|
||||
msg, err := decodeLogLine()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
if errors.Cause(err) != io.EOF {
|
||||
watcher.Err <- err
|
||||
}
|
||||
return
|
||||
@@ -569,7 +579,7 @@ func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan int
|
||||
}
|
||||
|
||||
handleDecodeErr := func(err error) error {
|
||||
if err != io.EOF {
|
||||
if errors.Cause(err) != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package logger // import "github.com/docker/docker/daemon/logger"
|
||||
|
||||
import (
|
||||
"github.com/docker/go-metrics"
|
||||
)
|
||||
|
||||
var (
|
||||
logWritesFailedCount metrics.Counter
|
||||
logReadsFailedCount metrics.Counter
|
||||
totalPartialLogs metrics.Counter
|
||||
)
|
||||
|
||||
func init() {
|
||||
loggerMetrics := metrics.NewNamespace("logger", "", nil)
|
||||
|
||||
logWritesFailedCount = loggerMetrics.NewCounter("log_write_operations_failed", "Number of log write operations that failed")
|
||||
logReadsFailedCount = loggerMetrics.NewCounter("log_read_operations_failed", "Number of log reads from container stdio that failed")
|
||||
totalPartialLogs = loggerMetrics.NewCounter("log_entries_size_greater_than_buffer", "Number of log entries which are larger than the log buffer")
|
||||
|
||||
metrics.Register(loggerMetrics)
|
||||
}
|
||||
+29
-3
@@ -7,7 +7,9 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/api/types/plugins/logdriver"
|
||||
"github.com/docker/docker/errdefs"
|
||||
getter "github.com/docker/docker/pkg/plugingetter"
|
||||
"github.com/docker/docker/pkg/plugins"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -37,11 +39,35 @@ func getPlugin(name string, mode int) (Creator, error) {
|
||||
return nil, fmt.Errorf("error looking up logging plugin %s: %v", name, err)
|
||||
}
|
||||
|
||||
d := &logPluginProxy{p.Client()}
|
||||
return makePluginCreator(name, d, p.ScopedPath), nil
|
||||
client, err := makePluginClient(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return makePluginCreator(name, client, p.ScopedPath), nil
|
||||
}
|
||||
|
||||
func makePluginCreator(name string, l *logPluginProxy, scopePath func(s string) string) Creator {
|
||||
func makePluginClient(p getter.CompatPlugin) (logPlugin, error) {
|
||||
if pc, ok := p.(getter.PluginWithV1Client); ok {
|
||||
return &logPluginProxy{pc.Client()}, nil
|
||||
}
|
||||
pa, ok := p.(getter.PluginAddr)
|
||||
if !ok {
|
||||
return nil, errdefs.System(errors.Errorf("got unknown plugin type %T", p))
|
||||
}
|
||||
|
||||
if pa.Protocol() != plugins.ProtocolSchemeHTTPV1 {
|
||||
return nil, errors.Errorf("plugin protocol not supported: %s", p)
|
||||
}
|
||||
|
||||
addr := pa.Addr()
|
||||
c, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pa.Timeout())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error making plugin client")
|
||||
}
|
||||
return &logPluginProxy{c}, nil
|
||||
}
|
||||
|
||||
func makePluginCreator(name string, l logPlugin, scopePath func(s string) string) Creator {
|
||||
return func(logCtx Info) (logger Logger, err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
|
||||
+4
-1
@@ -12,7 +12,10 @@ import (
|
||||
)
|
||||
|
||||
func openPluginStream(a *pluginAdapter) (io.WriteCloser, error) {
|
||||
f, err := fifo.OpenFifo(context.Background(), a.fifoPath, unix.O_WRONLY|unix.O_CREAT|unix.O_NONBLOCK, 0700)
|
||||
// Make sure to also open with read (in addition to write) to avoid borken pipe errors on plugin failure.
|
||||
// It is up to the plugin to keep track of pipes that it should re-attach to, however.
|
||||
// If the plugin doesn't open for reads, then the container will block once the pipe is full.
|
||||
f, err := fifo.OpenFifo(context.Background(), a.fifoPath, unix.O_RDWR|unix.O_CREAT|unix.O_NONBLOCK, 0700)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error creating i/o pipe for log plugin: %s", a.Name())
|
||||
}
|
||||
|
||||
+1
-1
@@ -150,7 +150,7 @@ func newRing(maxBytes int64) *messageRing {
|
||||
}
|
||||
|
||||
// Enqueue adds a message to the buffer queue
|
||||
// If the message is too big for the buffer it drops the oldest messages to make room
|
||||
// If the message is too big for the buffer it drops the new message.
|
||||
// If there are no messages in the queue and the message is still too big, it adds the message anyway.
|
||||
func (r *messageRing) Enqueue(m *Message) error {
|
||||
mSize := int64(len(m.Line))
|
||||
|
||||
+8
-8
@@ -6,12 +6,12 @@ package dockerversion // import "github.com/docker/docker/dockerversion"
|
||||
// Default build-time variable for library-import.
|
||||
// This file is overridden on build with build-time informations.
|
||||
const (
|
||||
GitCommit string = "library-import"
|
||||
Version string = "library-import"
|
||||
BuildTime string = "library-import"
|
||||
IAmStatic string = "library-import"
|
||||
ContainerdCommitID string = "library-import"
|
||||
RuncCommitID string = "library-import"
|
||||
InitCommitID string = "library-import"
|
||||
PlatformName string = ""
|
||||
GitCommit = "library-import"
|
||||
Version = "library-import"
|
||||
BuildTime = "library-import"
|
||||
IAmStatic = "library-import"
|
||||
ContainerdCommitID = "library-import"
|
||||
RuncCommitID = "library-import"
|
||||
InitCommitID = "library-import"
|
||||
PlatformName = ""
|
||||
)
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ func IsConflict(err error) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsUnauthorized returns if the the passed in error is an ErrUnauthorized
|
||||
// IsUnauthorized returns if the passed in error is an ErrUnauthorized
|
||||
func IsUnauthorized(err error) bool {
|
||||
_, ok := getImplementer(err).(ErrUnauthorized)
|
||||
return ok
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"github.com/opencontainers/runc/libcontainer/devices"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// Device transforms a libcontainer configs.Device to a specs.LinuxDevice object.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package oci // import "github.com/docker/docker/oci"
|
||||
|
||||
import specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
import "github.com/opencontainers/runtime-spec/specs-go"
|
||||
|
||||
// RemoveNamespace removes the `nsType` namespace from OCI spec `s`
|
||||
func RemoveNamespace(s *specs.Spec, nsType specs.LinuxNamespaceType) {
|
||||
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
package opts
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
types "github.com/docker/libnetwork/ipamutils"
|
||||
)
|
||||
|
||||
// PoolsOpt is a Value type for parsing the default address pools definitions
|
||||
type PoolsOpt struct {
|
||||
values []*types.NetworkToSplit
|
||||
}
|
||||
|
||||
// UnmarshalJSON fills values structure info from JSON input
|
||||
func (p *PoolsOpt) UnmarshalJSON(raw []byte) error {
|
||||
return json.Unmarshal(raw, &(p.values))
|
||||
}
|
||||
|
||||
// Set predefined pools
|
||||
func (p *PoolsOpt) Set(value string) error {
|
||||
csvReader := csv.NewReader(strings.NewReader(value))
|
||||
fields, err := csvReader.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
poolsDef := types.NetworkToSplit{}
|
||||
|
||||
for _, field := range fields {
|
||||
parts := strings.SplitN(field, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
|
||||
}
|
||||
|
||||
key := strings.ToLower(parts[0])
|
||||
value := strings.ToLower(parts[1])
|
||||
|
||||
switch key {
|
||||
case "base":
|
||||
poolsDef.Base = value
|
||||
case "size":
|
||||
size, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid size value: %q (must be integer): %v", value, err)
|
||||
}
|
||||
poolsDef.Size = size
|
||||
default:
|
||||
return fmt.Errorf("unexpected key '%s' in '%s'", key, field)
|
||||
}
|
||||
}
|
||||
|
||||
p.values = append(p.values, &poolsDef)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns the type of this option
|
||||
func (p *PoolsOpt) Type() string {
|
||||
return "pool-options"
|
||||
}
|
||||
|
||||
// String returns a string repr of this option
|
||||
func (p *PoolsOpt) String() string {
|
||||
pools := []string{}
|
||||
for _, pool := range p.values {
|
||||
repr := fmt.Sprintf("%s %d", pool.Base, pool.Size)
|
||||
pools = append(pools, repr)
|
||||
}
|
||||
return strings.Join(pools, ", ")
|
||||
}
|
||||
|
||||
// Value returns the mounts
|
||||
func (p *PoolsOpt) Value() []*types.NetworkToSplit {
|
||||
return p.values
|
||||
}
|
||||
|
||||
// Name returns the flag name of this option
|
||||
func (p *PoolsOpt) Name() string {
|
||||
return "default-address-pools"
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ValidateEnv validates an environment variable and returns it.
|
||||
// If no value is specified, it returns the current value using os.Getenv.
|
||||
//
|
||||
// As on ParseEnvFile and related to #16585, environment variable names
|
||||
// are not validate what so ever, it's up to application inside docker
|
||||
// to validate them or not.
|
||||
//
|
||||
// The only validation here is to check if name is empty, per #25099
|
||||
func ValidateEnv(val string) (string, error) {
|
||||
arr := strings.Split(val, "=")
|
||||
if arr[0] == "" {
|
||||
return "", errors.Errorf("invalid environment variable: %s", val)
|
||||
}
|
||||
if len(arr) > 1 {
|
||||
return val, nil
|
||||
}
|
||||
if !doesEnvExist(val) {
|
||||
return val, nil
|
||||
}
|
||||
return fmt.Sprintf("%s=%s", val, os.Getenv(val)), nil
|
||||
}
|
||||
|
||||
func doesEnvExist(name string) bool {
|
||||
for _, entry := range os.Environ() {
|
||||
parts := strings.SplitN(entry, "=", 2)
|
||||
if runtime.GOOS == "windows" {
|
||||
// Environment variable are case-insensitive on Windows. PaTh, path and PATH are equivalent.
|
||||
if strings.EqualFold(parts[0], name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if parts[0] == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
-165
@@ -1,165 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultHTTPPort Default HTTP Port used if only the protocol is provided to -H flag e.g. dockerd -H tcp://
|
||||
// These are the IANA registered port numbers for use with Docker
|
||||
// see http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=docker
|
||||
DefaultHTTPPort = 2375 // Default HTTP Port
|
||||
// DefaultTLSHTTPPort Default HTTP Port used when TLS enabled
|
||||
DefaultTLSHTTPPort = 2376 // Default TLS encrypted HTTP Port
|
||||
// DefaultUnixSocket Path for the unix socket.
|
||||
// Docker daemon by default always listens on the default unix socket
|
||||
DefaultUnixSocket = "/var/run/docker.sock"
|
||||
// DefaultTCPHost constant defines the default host string used by docker on Windows
|
||||
DefaultTCPHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultHTTPPort)
|
||||
// DefaultTLSHost constant defines the default host string used by docker for TLS sockets
|
||||
DefaultTLSHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultTLSHTTPPort)
|
||||
// DefaultNamedPipe defines the default named pipe used by docker on Windows
|
||||
DefaultNamedPipe = `//./pipe/docker_engine`
|
||||
)
|
||||
|
||||
// ValidateHost validates that the specified string is a valid host and returns it.
|
||||
func ValidateHost(val string) (string, error) {
|
||||
host := strings.TrimSpace(val)
|
||||
// The empty string means default and is not handled by parseDaemonHost
|
||||
if host != "" {
|
||||
_, err := parseDaemonHost(host)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
}
|
||||
// Note: unlike most flag validators, we don't return the mutated value here
|
||||
// we need to know what the user entered later (using ParseHost) to adjust for TLS
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// ParseHost and set defaults for a Daemon host string
|
||||
func ParseHost(defaultToTLS bool, val string) (string, error) {
|
||||
host := strings.TrimSpace(val)
|
||||
if host == "" {
|
||||
if defaultToTLS {
|
||||
host = DefaultTLSHost
|
||||
} else {
|
||||
host = DefaultHost
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
host, err = parseDaemonHost(host)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
}
|
||||
return host, nil
|
||||
}
|
||||
|
||||
// parseDaemonHost parses the specified address and returns an address that will be used as the host.
|
||||
// Depending of the address specified, this may return one of the global Default* strings defined in hosts.go.
|
||||
func parseDaemonHost(addr string) (string, error) {
|
||||
addrParts := strings.SplitN(addr, "://", 2)
|
||||
if len(addrParts) == 1 && addrParts[0] != "" {
|
||||
addrParts = []string{"tcp", addrParts[0]}
|
||||
}
|
||||
|
||||
switch addrParts[0] {
|
||||
case "tcp":
|
||||
return ParseTCPAddr(addrParts[1], DefaultTCPHost)
|
||||
case "unix":
|
||||
return parseSimpleProtoAddr("unix", addrParts[1], DefaultUnixSocket)
|
||||
case "npipe":
|
||||
return parseSimpleProtoAddr("npipe", addrParts[1], DefaultNamedPipe)
|
||||
case "fd":
|
||||
return addr, nil
|
||||
default:
|
||||
return "", fmt.Errorf("Invalid bind address format: %s", addr)
|
||||
}
|
||||
}
|
||||
|
||||
// parseSimpleProtoAddr parses and validates that the specified address is a valid
|
||||
// socket address for simple protocols like unix and npipe. It returns a formatted
|
||||
// socket address, either using the address parsed from addr, or the contents of
|
||||
// defaultAddr if addr is a blank string.
|
||||
func parseSimpleProtoAddr(proto, addr, defaultAddr string) (string, error) {
|
||||
addr = strings.TrimPrefix(addr, proto+"://")
|
||||
if strings.Contains(addr, "://") {
|
||||
return "", fmt.Errorf("Invalid proto, expected %s: %s", proto, addr)
|
||||
}
|
||||
if addr == "" {
|
||||
addr = defaultAddr
|
||||
}
|
||||
return fmt.Sprintf("%s://%s", proto, addr), nil
|
||||
}
|
||||
|
||||
// ParseTCPAddr parses and validates that the specified address is a valid TCP
|
||||
// address. It returns a formatted TCP address, either using the address parsed
|
||||
// from tryAddr, or the contents of defaultAddr if tryAddr is a blank string.
|
||||
// tryAddr is expected to have already been Trim()'d
|
||||
// defaultAddr must be in the full `tcp://host:port` form
|
||||
func ParseTCPAddr(tryAddr string, defaultAddr string) (string, error) {
|
||||
if tryAddr == "" || tryAddr == "tcp://" {
|
||||
return defaultAddr, nil
|
||||
}
|
||||
addr := strings.TrimPrefix(tryAddr, "tcp://")
|
||||
if strings.Contains(addr, "://") || addr == "" {
|
||||
return "", fmt.Errorf("Invalid proto, expected tcp: %s", tryAddr)
|
||||
}
|
||||
|
||||
defaultAddr = strings.TrimPrefix(defaultAddr, "tcp://")
|
||||
defaultHost, defaultPort, err := net.SplitHostPort(defaultAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// url.Parse fails for trailing colon on IPv6 brackets on Go 1.5, but
|
||||
// not 1.4. See https://github.com/golang/go/issues/12200 and
|
||||
// https://github.com/golang/go/issues/6530.
|
||||
if strings.HasSuffix(addr, "]:") {
|
||||
addr += defaultPort
|
||||
}
|
||||
|
||||
u, err := url.Parse("tcp://" + addr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
host, port, err := net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
// try port addition once
|
||||
host, port, err = net.SplitHostPort(net.JoinHostPort(u.Host, defaultPort))
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Invalid bind address format: %s", tryAddr)
|
||||
}
|
||||
|
||||
if host == "" {
|
||||
host = defaultHost
|
||||
}
|
||||
if port == "" {
|
||||
port = defaultPort
|
||||
}
|
||||
p, err := strconv.Atoi(port)
|
||||
if err != nil && p == 0 {
|
||||
return "", fmt.Errorf("Invalid bind address format: %s", tryAddr)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("tcp://%s%s", net.JoinHostPort(host, port), u.Path), nil
|
||||
}
|
||||
|
||||
// ValidateExtraHost validates that the specified string is a valid extrahost and returns it.
|
||||
// ExtraHost is in the form of name:ip where the ip has to be a valid ip (IPv4 or IPv6).
|
||||
func ValidateExtraHost(val string) (string, error) {
|
||||
// allow for IPv6 addresses in extra hosts by only splitting on first ":"
|
||||
arr := strings.SplitN(val, ":", 2)
|
||||
if len(arr) != 2 || len(arr[0]) == 0 {
|
||||
return "", fmt.Errorf("bad format for add-host: %q", val)
|
||||
}
|
||||
if _, err := ValidateIPAddress(arr[1]); err != nil {
|
||||
return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1])
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
import "fmt"
|
||||
|
||||
// DefaultHost constant defines the default host string used by docker on other hosts than Windows
|
||||
var DefaultHost = fmt.Sprintf("unix://%s", DefaultUnixSocket)
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
// DefaultHost constant defines the default host string used by docker on Windows
|
||||
var DefaultHost = "npipe://" + DefaultNamedPipe
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
// IPOpt holds an IP. It is used to store values from CLI flags.
|
||||
type IPOpt struct {
|
||||
*net.IP
|
||||
}
|
||||
|
||||
// NewIPOpt creates a new IPOpt from a reference net.IP and a
|
||||
// string representation of an IP. If the string is not a valid
|
||||
// IP it will fallback to the specified reference.
|
||||
func NewIPOpt(ref *net.IP, defaultVal string) *IPOpt {
|
||||
o := &IPOpt{
|
||||
IP: ref,
|
||||
}
|
||||
o.Set(defaultVal)
|
||||
return o
|
||||
}
|
||||
|
||||
// Set sets an IPv4 or IPv6 address from a given string. If the given
|
||||
// string is not parsable as an IP address it returns an error.
|
||||
func (o *IPOpt) Set(val string) error {
|
||||
ip := net.ParseIP(val)
|
||||
if ip == nil {
|
||||
return fmt.Errorf("%s is not an ip address", val)
|
||||
}
|
||||
*o.IP = ip
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the IP address stored in the IPOpt. If stored IP is a
|
||||
// nil pointer, it returns an empty string.
|
||||
func (o *IPOpt) String() string {
|
||||
if *o.IP == nil {
|
||||
return ""
|
||||
}
|
||||
return o.IP.String()
|
||||
}
|
||||
|
||||
// Type returns the type of the option
|
||||
func (o *IPOpt) Type() string {
|
||||
return "ip"
|
||||
}
|
||||
-337
@@ -1,337 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
var (
|
||||
alphaRegexp = regexp.MustCompile(`[a-zA-Z]`)
|
||||
domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`)
|
||||
)
|
||||
|
||||
// ListOpts holds a list of values and a validation function.
|
||||
type ListOpts struct {
|
||||
values *[]string
|
||||
validator ValidatorFctType
|
||||
}
|
||||
|
||||
// NewListOpts creates a new ListOpts with the specified validator.
|
||||
func NewListOpts(validator ValidatorFctType) ListOpts {
|
||||
var values []string
|
||||
return *NewListOptsRef(&values, validator)
|
||||
}
|
||||
|
||||
// NewListOptsRef creates a new ListOpts with the specified values and validator.
|
||||
func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
|
||||
return &ListOpts{
|
||||
values: values,
|
||||
validator: validator,
|
||||
}
|
||||
}
|
||||
|
||||
func (opts *ListOpts) String() string {
|
||||
if len(*opts.values) == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%v", *opts.values)
|
||||
}
|
||||
|
||||
// Set validates if needed the input value and adds it to the
|
||||
// internal slice.
|
||||
func (opts *ListOpts) Set(value string) error {
|
||||
if opts.validator != nil {
|
||||
v, err := opts.validator(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value = v
|
||||
}
|
||||
(*opts.values) = append((*opts.values), value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete removes the specified element from the slice.
|
||||
func (opts *ListOpts) Delete(key string) {
|
||||
for i, k := range *opts.values {
|
||||
if k == key {
|
||||
(*opts.values) = append((*opts.values)[:i], (*opts.values)[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetMap returns the content of values in a map in order to avoid
|
||||
// duplicates.
|
||||
func (opts *ListOpts) GetMap() map[string]struct{} {
|
||||
ret := make(map[string]struct{})
|
||||
for _, k := range *opts.values {
|
||||
ret[k] = struct{}{}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetAll returns the values of slice.
|
||||
func (opts *ListOpts) GetAll() []string {
|
||||
return (*opts.values)
|
||||
}
|
||||
|
||||
// GetAllOrEmpty returns the values of the slice
|
||||
// or an empty slice when there are no values.
|
||||
func (opts *ListOpts) GetAllOrEmpty() []string {
|
||||
v := *opts.values
|
||||
if v == nil {
|
||||
return make([]string, 0)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Get checks the existence of the specified key.
|
||||
func (opts *ListOpts) Get(key string) bool {
|
||||
for _, k := range *opts.values {
|
||||
if k == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Len returns the amount of element in the slice.
|
||||
func (opts *ListOpts) Len() int {
|
||||
return len((*opts.values))
|
||||
}
|
||||
|
||||
// Type returns a string name for this Option type
|
||||
func (opts *ListOpts) Type() string {
|
||||
return "list"
|
||||
}
|
||||
|
||||
// WithValidator returns the ListOpts with validator set.
|
||||
func (opts *ListOpts) WithValidator(validator ValidatorFctType) *ListOpts {
|
||||
opts.validator = validator
|
||||
return opts
|
||||
}
|
||||
|
||||
// NamedOption is an interface that list and map options
|
||||
// with names implement.
|
||||
type NamedOption interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
// NamedListOpts is a ListOpts with a configuration name.
|
||||
// This struct is useful to keep reference to the assigned
|
||||
// field name in the internal configuration struct.
|
||||
type NamedListOpts struct {
|
||||
name string
|
||||
ListOpts
|
||||
}
|
||||
|
||||
var _ NamedOption = &NamedListOpts{}
|
||||
|
||||
// NewNamedListOptsRef creates a reference to a new NamedListOpts struct.
|
||||
func NewNamedListOptsRef(name string, values *[]string, validator ValidatorFctType) *NamedListOpts {
|
||||
return &NamedListOpts{
|
||||
name: name,
|
||||
ListOpts: *NewListOptsRef(values, validator),
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the name of the NamedListOpts in the configuration.
|
||||
func (o *NamedListOpts) Name() string {
|
||||
return o.name
|
||||
}
|
||||
|
||||
// MapOpts holds a map of values and a validation function.
|
||||
type MapOpts struct {
|
||||
values map[string]string
|
||||
validator ValidatorFctType
|
||||
}
|
||||
|
||||
// Set validates if needed the input value and add it to the
|
||||
// internal map, by splitting on '='.
|
||||
func (opts *MapOpts) Set(value string) error {
|
||||
if opts.validator != nil {
|
||||
v, err := opts.validator(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value = v
|
||||
}
|
||||
vals := strings.SplitN(value, "=", 2)
|
||||
if len(vals) == 1 {
|
||||
(opts.values)[vals[0]] = ""
|
||||
} else {
|
||||
(opts.values)[vals[0]] = vals[1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAll returns the values of MapOpts as a map.
|
||||
func (opts *MapOpts) GetAll() map[string]string {
|
||||
return opts.values
|
||||
}
|
||||
|
||||
func (opts *MapOpts) String() string {
|
||||
return fmt.Sprintf("%v", opts.values)
|
||||
}
|
||||
|
||||
// Type returns a string name for this Option type
|
||||
func (opts *MapOpts) Type() string {
|
||||
return "map"
|
||||
}
|
||||
|
||||
// NewMapOpts creates a new MapOpts with the specified map of values and a validator.
|
||||
func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts {
|
||||
if values == nil {
|
||||
values = make(map[string]string)
|
||||
}
|
||||
return &MapOpts{
|
||||
values: values,
|
||||
validator: validator,
|
||||
}
|
||||
}
|
||||
|
||||
// NamedMapOpts is a MapOpts struct with a configuration name.
|
||||
// This struct is useful to keep reference to the assigned
|
||||
// field name in the internal configuration struct.
|
||||
type NamedMapOpts struct {
|
||||
name string
|
||||
MapOpts
|
||||
}
|
||||
|
||||
var _ NamedOption = &NamedMapOpts{}
|
||||
|
||||
// NewNamedMapOpts creates a reference to a new NamedMapOpts struct.
|
||||
func NewNamedMapOpts(name string, values map[string]string, validator ValidatorFctType) *NamedMapOpts {
|
||||
return &NamedMapOpts{
|
||||
name: name,
|
||||
MapOpts: *NewMapOpts(values, validator),
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the name of the NamedMapOpts in the configuration.
|
||||
func (o *NamedMapOpts) Name() string {
|
||||
return o.name
|
||||
}
|
||||
|
||||
// ValidatorFctType defines a validator function that returns a validated string and/or an error.
|
||||
type ValidatorFctType func(val string) (string, error)
|
||||
|
||||
// ValidatorFctListType defines a validator function that returns a validated list of string and/or an error
|
||||
type ValidatorFctListType func(val string) ([]string, error)
|
||||
|
||||
// ValidateIPAddress validates an Ip address.
|
||||
func ValidateIPAddress(val string) (string, error) {
|
||||
var ip = net.ParseIP(strings.TrimSpace(val))
|
||||
if ip != nil {
|
||||
return ip.String(), nil
|
||||
}
|
||||
return "", fmt.Errorf("%s is not an ip address", val)
|
||||
}
|
||||
|
||||
// ValidateDNSSearch validates domain for resolvconf search configuration.
|
||||
// A zero length domain is represented by a dot (.).
|
||||
func ValidateDNSSearch(val string) (string, error) {
|
||||
if val = strings.Trim(val, " "); val == "." {
|
||||
return val, nil
|
||||
}
|
||||
return validateDomain(val)
|
||||
}
|
||||
|
||||
func validateDomain(val string) (string, error) {
|
||||
if alphaRegexp.FindString(val) == "" {
|
||||
return "", fmt.Errorf("%s is not a valid domain", val)
|
||||
}
|
||||
ns := domainRegexp.FindSubmatch([]byte(val))
|
||||
if len(ns) > 0 && len(ns[1]) < 255 {
|
||||
return string(ns[1]), nil
|
||||
}
|
||||
return "", fmt.Errorf("%s is not a valid domain", val)
|
||||
}
|
||||
|
||||
// ValidateLabel validates that the specified string is a valid label, and returns it.
|
||||
// Labels are in the form on key=value.
|
||||
func ValidateLabel(val string) (string, error) {
|
||||
if strings.Count(val, "=") < 1 {
|
||||
return "", fmt.Errorf("bad attribute format: %s", val)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// ValidateSingleGenericResource validates that a single entry in the
|
||||
// generic resource list is valid.
|
||||
// i.e 'GPU=UID1' is valid however 'GPU:UID1' or 'UID1' isn't
|
||||
func ValidateSingleGenericResource(val string) (string, error) {
|
||||
if strings.Count(val, "=") < 1 {
|
||||
return "", fmt.Errorf("invalid node-generic-resource format `%s` expected `name=value`", val)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// ParseLink parses and validates the specified string as a link format (name:alias)
|
||||
func ParseLink(val string) (string, string, error) {
|
||||
if val == "" {
|
||||
return "", "", fmt.Errorf("empty string specified for links")
|
||||
}
|
||||
arr := strings.Split(val, ":")
|
||||
if len(arr) > 2 {
|
||||
return "", "", fmt.Errorf("bad format for links: %s", val)
|
||||
}
|
||||
if len(arr) == 1 {
|
||||
return val, val, nil
|
||||
}
|
||||
// This is kept because we can actually get a HostConfig with links
|
||||
// from an already created container and the format is not `foo:bar`
|
||||
// but `/foo:/c1/bar`
|
||||
if strings.HasPrefix(arr[0], "/") {
|
||||
_, alias := path.Split(arr[1])
|
||||
return arr[0][1:], alias, nil
|
||||
}
|
||||
return arr[0], arr[1], nil
|
||||
}
|
||||
|
||||
// MemBytes is a type for human readable memory bytes (like 128M, 2g, etc)
|
||||
type MemBytes int64
|
||||
|
||||
// String returns the string format of the human readable memory bytes
|
||||
func (m *MemBytes) String() string {
|
||||
// NOTE: In spf13/pflag/flag.go, "0" is considered as "zero value" while "0 B" is not.
|
||||
// We return "0" in case value is 0 here so that the default value is hidden.
|
||||
// (Sometimes "default 0 B" is actually misleading)
|
||||
if m.Value() != 0 {
|
||||
return units.BytesSize(float64(m.Value()))
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
|
||||
// Set sets the value of the MemBytes by passing a string
|
||||
func (m *MemBytes) Set(value string) error {
|
||||
val, err := units.RAMInBytes(value)
|
||||
*m = MemBytes(val)
|
||||
return err
|
||||
}
|
||||
|
||||
// Type returns the type
|
||||
func (m *MemBytes) Type() string {
|
||||
return "bytes"
|
||||
}
|
||||
|
||||
// Value returns the value in int64
|
||||
func (m *MemBytes) Value() int64 {
|
||||
return int64(*m)
|
||||
}
|
||||
|
||||
// UnmarshalJSON is the customized unmarshaler for MemBytes
|
||||
func (m *MemBytes) UnmarshalJSON(s []byte) error {
|
||||
if len(s) <= 2 || s[0] != '"' || s[len(s)-1] != '"' {
|
||||
return fmt.Errorf("invalid size: %q", s)
|
||||
}
|
||||
val, err := units.RAMInBytes(string(s[1 : len(s)-1]))
|
||||
*m = MemBytes(val)
|
||||
return err
|
||||
}
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
// DefaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. dockerd -H tcp://:8080
|
||||
const DefaultHTTPHost = "localhost"
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
// TODO Windows. Identify bug in GOLang 1.5.1+ and/or Windows Server 2016 TP5.
|
||||
// @jhowardmsft, @swernli.
|
||||
//
|
||||
// On Windows, this mitigates a problem with the default options of running
|
||||
// a docker client against a local docker daemon on TP5.
|
||||
//
|
||||
// What was found that if the default host is "localhost", even if the client
|
||||
// (and daemon as this is local) is not physically on a network, and the DNS
|
||||
// cache is flushed (ipconfig /flushdns), then the client will pause for
|
||||
// exactly one second when connecting to the daemon for calls. For example
|
||||
// using docker run windowsservercore cmd, the CLI will send a create followed
|
||||
// by an attach. You see the delay between the attach finishing and the attach
|
||||
// being seen by the daemon.
|
||||
//
|
||||
// Here's some daemon debug logs with additional debug spew put in. The
|
||||
// AfterWriteJSON log is the very last thing the daemon does as part of the
|
||||
// create call. The POST /attach is the second CLI call. Notice the second
|
||||
// time gap.
|
||||
//
|
||||
// time="2015-11-06T13:38:37.259627400-08:00" level=debug msg="After createRootfs"
|
||||
// time="2015-11-06T13:38:37.263626300-08:00" level=debug msg="After setHostConfig"
|
||||
// time="2015-11-06T13:38:37.267631200-08:00" level=debug msg="before createContainerPl...."
|
||||
// time="2015-11-06T13:38:37.271629500-08:00" level=debug msg=ToDiskLocking....
|
||||
// time="2015-11-06T13:38:37.275643200-08:00" level=debug msg="loggin event...."
|
||||
// time="2015-11-06T13:38:37.277627600-08:00" level=debug msg="logged event...."
|
||||
// time="2015-11-06T13:38:37.279631800-08:00" level=debug msg="In defer func"
|
||||
// time="2015-11-06T13:38:37.282628100-08:00" level=debug msg="After daemon.create"
|
||||
// time="2015-11-06T13:38:37.286651700-08:00" level=debug msg="return 2"
|
||||
// time="2015-11-06T13:38:37.289629500-08:00" level=debug msg="Returned from daemon.ContainerCreate"
|
||||
// time="2015-11-06T13:38:37.311629100-08:00" level=debug msg="After WriteJSON"
|
||||
// ... 1 second gap here....
|
||||
// time="2015-11-06T13:38:38.317866200-08:00" level=debug msg="Calling POST /v1.22/containers/984758282b842f779e805664b2c95d563adc9a979c8a3973e68c807843ee4757/attach"
|
||||
// time="2015-11-06T13:38:38.326882500-08:00" level=info msg="POST /v1.22/containers/984758282b842f779e805664b2c95d563adc9a979c8a3973e68c807843ee4757/attach?stderr=1&stdin=1&stdout=1&stream=1"
|
||||
//
|
||||
// We suspect this is either a bug introduced in GOLang 1.5.1, or that a change
|
||||
// in GOLang 1.5.1 (from 1.4.3) is exposing a bug in Windows. In theory,
|
||||
// the Windows networking stack is supposed to resolve "localhost" internally,
|
||||
// without hitting DNS, or even reading the hosts file (which is why localhost
|
||||
// is commented out in the hosts file on Windows).
|
||||
//
|
||||
// We have validated that working around this using the actual IPv4 localhost
|
||||
// address does not cause the delay.
|
||||
//
|
||||
// This does not occur with the docker client built with 1.4.3 on the same
|
||||
// Windows build, regardless of whether the daemon is built using 1.5.1
|
||||
// or 1.4.3. It does not occur on Linux. We also verified we see the same thing
|
||||
// on a cross-compiled Windows binary (from Linux).
|
||||
//
|
||||
// Final note: This is a mitigation, not a 'real' fix. It is still susceptible
|
||||
// to the delay if a user were to do 'docker run -H=tcp://localhost:2375...'
|
||||
// explicitly.
|
||||
|
||||
// DefaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. dockerd -H tcp://:8080
|
||||
const DefaultHTTPHost = "127.0.0.1"
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
// QuotedString is a string that may have extra quotes around the value. The
|
||||
// quotes are stripped from the value.
|
||||
type QuotedString struct {
|
||||
value *string
|
||||
}
|
||||
|
||||
// Set sets a new value
|
||||
func (s *QuotedString) Set(val string) error {
|
||||
*s.value = trimQuotes(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns the type of the value
|
||||
func (s *QuotedString) Type() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
func (s *QuotedString) String() string {
|
||||
return *s.value
|
||||
}
|
||||
|
||||
func trimQuotes(value string) string {
|
||||
lastIndex := len(value) - 1
|
||||
for _, char := range []byte{'\'', '"'} {
|
||||
if value[0] == char && value[lastIndex] == char {
|
||||
return value[1:lastIndex]
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// NewQuotedString returns a new quoted string option
|
||||
func NewQuotedString(value *string) *QuotedString {
|
||||
return &QuotedString{value: value}
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
// RuntimeOpt defines a map of Runtimes
|
||||
type RuntimeOpt struct {
|
||||
name string
|
||||
stockRuntimeName string
|
||||
values *map[string]types.Runtime
|
||||
}
|
||||
|
||||
// NewNamedRuntimeOpt creates a new RuntimeOpt
|
||||
func NewNamedRuntimeOpt(name string, ref *map[string]types.Runtime, stockRuntime string) *RuntimeOpt {
|
||||
if ref == nil {
|
||||
ref = &map[string]types.Runtime{}
|
||||
}
|
||||
return &RuntimeOpt{name: name, values: ref, stockRuntimeName: stockRuntime}
|
||||
}
|
||||
|
||||
// Name returns the name of the NamedListOpts in the configuration.
|
||||
func (o *RuntimeOpt) Name() string {
|
||||
return o.name
|
||||
}
|
||||
|
||||
// Set validates and updates the list of Runtimes
|
||||
func (o *RuntimeOpt) Set(val string) error {
|
||||
parts := strings.SplitN(val, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid runtime argument: %s", val)
|
||||
}
|
||||
|
||||
parts[0] = strings.TrimSpace(parts[0])
|
||||
parts[1] = strings.TrimSpace(parts[1])
|
||||
if parts[0] == "" || parts[1] == "" {
|
||||
return fmt.Errorf("invalid runtime argument: %s", val)
|
||||
}
|
||||
|
||||
parts[0] = strings.ToLower(parts[0])
|
||||
if parts[0] == o.stockRuntimeName {
|
||||
return fmt.Errorf("runtime name '%s' is reserved", o.stockRuntimeName)
|
||||
}
|
||||
|
||||
if _, ok := (*o.values)[parts[0]]; ok {
|
||||
return fmt.Errorf("runtime '%s' was already defined", parts[0])
|
||||
}
|
||||
|
||||
(*o.values)[parts[0]] = types.Runtime{Path: parts[1]}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns Runtime values as a string.
|
||||
func (o *RuntimeOpt) String() string {
|
||||
var out []string
|
||||
for k := range *o.values {
|
||||
out = append(out, k)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", out)
|
||||
}
|
||||
|
||||
// GetMap returns a map of Runtimes (name: path)
|
||||
func (o *RuntimeOpt) GetMap() map[string]types.Runtime {
|
||||
if o.values != nil {
|
||||
return *o.values
|
||||
}
|
||||
|
||||
return map[string]types.Runtime{}
|
||||
}
|
||||
|
||||
// Type returns the type of the option
|
||||
func (o *RuntimeOpt) Type() string {
|
||||
return "runtime"
|
||||
}
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
// UlimitOpt defines a map of Ulimits
|
||||
type UlimitOpt struct {
|
||||
values *map[string]*units.Ulimit
|
||||
}
|
||||
|
||||
// NewUlimitOpt creates a new UlimitOpt
|
||||
func NewUlimitOpt(ref *map[string]*units.Ulimit) *UlimitOpt {
|
||||
if ref == nil {
|
||||
ref = &map[string]*units.Ulimit{}
|
||||
}
|
||||
return &UlimitOpt{ref}
|
||||
}
|
||||
|
||||
// Set validates a Ulimit and sets its name as a key in UlimitOpt
|
||||
func (o *UlimitOpt) Set(val string) error {
|
||||
l, err := units.ParseUlimit(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
(*o.values)[l.Name] = l
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns Ulimit values as a string.
|
||||
func (o *UlimitOpt) String() string {
|
||||
var out []string
|
||||
for _, v := range *o.values {
|
||||
out = append(out, v.String())
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", out)
|
||||
}
|
||||
|
||||
// GetList returns a slice of pointers to Ulimits.
|
||||
func (o *UlimitOpt) GetList() []*units.Ulimit {
|
||||
var ulimits []*units.Ulimit
|
||||
for _, v := range *o.values {
|
||||
ulimits = append(ulimits, v)
|
||||
}
|
||||
|
||||
return ulimits
|
||||
}
|
||||
|
||||
// Type returns the option type
|
||||
func (o *UlimitOpt) Type() string {
|
||||
return "ulimit"
|
||||
}
|
||||
|
||||
// NamedUlimitOpt defines a named map of Ulimits
|
||||
type NamedUlimitOpt struct {
|
||||
name string
|
||||
UlimitOpt
|
||||
}
|
||||
|
||||
var _ NamedOption = &NamedUlimitOpt{}
|
||||
|
||||
// NewNamedUlimitOpt creates a new NamedUlimitOpt
|
||||
func NewNamedUlimitOpt(name string, ref *map[string]*units.Ulimit) *NamedUlimitOpt {
|
||||
if ref == nil {
|
||||
ref = &map[string]*units.Ulimit{}
|
||||
}
|
||||
return &NamedUlimitOpt{
|
||||
name: name,
|
||||
UlimitOpt: *NewUlimitOpt(ref),
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the option name
|
||||
func (o *NamedUlimitOpt) Name() string {
|
||||
return o.name
|
||||
}
|
||||
+1
-1
@@ -73,5 +73,5 @@ func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
|
||||
|
||||
func getFileUIDGID(stat interface{}) (idtools.IDPair, error) {
|
||||
// no notion of file ownership mapping yet on Windows
|
||||
return idtools.IDPair{0, 0}, nil
|
||||
return idtools.IDPair{UID: 0, GID: 0}, nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ func timeToTimespec(time time.Time) (ts syscall.Timespec) {
|
||||
if time.IsZero() {
|
||||
// Return UTIME_OMIT special value
|
||||
ts.Sec = 0
|
||||
ts.Nsec = ((1 << 30) - 2)
|
||||
ts.Nsec = (1 << 30) - 2
|
||||
return
|
||||
}
|
||||
return syscall.NsecToTimespec(time.UnixNano())
|
||||
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
package discovery // import "github.com/docker/docker/pkg/discovery"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
// Backends is a global map of discovery backends indexed by their
|
||||
// associated scheme.
|
||||
backends = make(map[string]Backend)
|
||||
)
|
||||
|
||||
// Register makes a discovery backend available by the provided scheme.
|
||||
// If Register is called twice with the same scheme an error is returned.
|
||||
func Register(scheme string, d Backend) error {
|
||||
if _, exists := backends[scheme]; exists {
|
||||
return fmt.Errorf("scheme already registered %s", scheme)
|
||||
}
|
||||
logrus.WithField("name", scheme).Debugf("Registering discovery service")
|
||||
backends[scheme] = d
|
||||
return nil
|
||||
}
|
||||
|
||||
func parse(rawurl string) (string, string) {
|
||||
parts := strings.SplitN(rawurl, "://", 2)
|
||||
|
||||
// nodes:port,node2:port => nodes://node1:port,node2:port
|
||||
if len(parts) == 1 {
|
||||
return "nodes", parts[0]
|
||||
}
|
||||
return parts[0], parts[1]
|
||||
}
|
||||
|
||||
// ParseAdvertise parses the --cluster-advertise daemon config which accepts
|
||||
// <ip-address>:<port> or <interface-name>:<port>
|
||||
func ParseAdvertise(advertise string) (string, error) {
|
||||
var (
|
||||
iface *net.Interface
|
||||
addrs []net.Addr
|
||||
err error
|
||||
)
|
||||
|
||||
addr, port, err := net.SplitHostPort(advertise)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid --cluster-advertise configuration: %s: %v", advertise, err)
|
||||
}
|
||||
|
||||
ip := net.ParseIP(addr)
|
||||
// If it is a valid ip-address, use it as is
|
||||
if ip != nil {
|
||||
return advertise, nil
|
||||
}
|
||||
|
||||
// If advertise is a valid interface name, get the valid IPv4 address and use it to advertise
|
||||
ifaceName := addr
|
||||
iface, err = net.InterfaceByName(ifaceName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid cluster advertise IP address or interface name (%s) : %v", advertise, err)
|
||||
}
|
||||
|
||||
addrs, err = iface.Addrs()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to get advertise IP address from interface (%s) : %v", advertise, err)
|
||||
}
|
||||
|
||||
if len(addrs) == 0 {
|
||||
return "", fmt.Errorf("no available advertise IP address in interface (%s)", advertise)
|
||||
}
|
||||
|
||||
addr = ""
|
||||
for _, a := range addrs {
|
||||
ip, _, err := net.ParseCIDR(a.String())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error deriving advertise ip-address in interface (%s) : %v", advertise, err)
|
||||
}
|
||||
if ip.To4() == nil || ip.IsLoopback() {
|
||||
continue
|
||||
}
|
||||
addr = ip.String()
|
||||
break
|
||||
}
|
||||
if addr == "" {
|
||||
return "", fmt.Errorf("could not find a valid ip-address in interface %s", advertise)
|
||||
}
|
||||
|
||||
addr = net.JoinHostPort(addr, port)
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// New returns a new Discovery given a URL, heartbeat and ttl settings.
|
||||
// Returns an error if the URL scheme is not supported.
|
||||
func New(rawurl string, heartbeat time.Duration, ttl time.Duration, clusterOpts map[string]string) (Backend, error) {
|
||||
scheme, uri := parse(rawurl)
|
||||
if backend, exists := backends[scheme]; exists {
|
||||
logrus.WithFields(logrus.Fields{"name": scheme, "uri": uri}).Debugf("Initializing discovery service")
|
||||
err := backend.Initialize(uri, heartbeat, ttl, clusterOpts)
|
||||
return backend, err
|
||||
}
|
||||
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
package discovery // import "github.com/docker/docker/pkg/discovery"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotSupported is returned when a discovery service is not supported.
|
||||
ErrNotSupported = errors.New("discovery service not supported")
|
||||
|
||||
// ErrNotImplemented is returned when discovery feature is not implemented
|
||||
// by discovery backend.
|
||||
ErrNotImplemented = errors.New("not implemented in this discovery service")
|
||||
)
|
||||
|
||||
// Watcher provides watching over a cluster for nodes joining and leaving.
|
||||
type Watcher interface {
|
||||
// Watch the discovery for entry changes.
|
||||
// Returns a channel that will receive changes or an error.
|
||||
// Providing a non-nil stopCh can be used to stop watching.
|
||||
Watch(stopCh <-chan struct{}) (<-chan Entries, <-chan error)
|
||||
}
|
||||
|
||||
// Backend is implemented by discovery backends which manage cluster entries.
|
||||
type Backend interface {
|
||||
// Watcher must be provided by every backend.
|
||||
Watcher
|
||||
|
||||
// Initialize the discovery with URIs, a heartbeat, a ttl and optional settings.
|
||||
Initialize(string, time.Duration, time.Duration, map[string]string) error
|
||||
|
||||
// Register to the discovery.
|
||||
Register(string) error
|
||||
}
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
package discovery // import "github.com/docker/docker/pkg/discovery"
|
||||
|
||||
import "net"
|
||||
|
||||
// NewEntry creates a new entry.
|
||||
func NewEntry(url string) (*Entry, error) {
|
||||
host, port, err := net.SplitHostPort(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Entry{host, port}, nil
|
||||
}
|
||||
|
||||
// An Entry represents a host.
|
||||
type Entry struct {
|
||||
Host string
|
||||
Port string
|
||||
}
|
||||
|
||||
// Equals returns true if cmp contains the same data.
|
||||
func (e *Entry) Equals(cmp *Entry) bool {
|
||||
return e.Host == cmp.Host && e.Port == cmp.Port
|
||||
}
|
||||
|
||||
// String returns the string form of an entry.
|
||||
func (e *Entry) String() string {
|
||||
return net.JoinHostPort(e.Host, e.Port)
|
||||
}
|
||||
|
||||
// Entries is a list of *Entry with some helpers.
|
||||
type Entries []*Entry
|
||||
|
||||
// Equals returns true if cmp contains the same data.
|
||||
func (e Entries) Equals(cmp Entries) bool {
|
||||
// Check if the file has really changed.
|
||||
if len(e) != len(cmp) {
|
||||
return false
|
||||
}
|
||||
for i := range e {
|
||||
if !e[i].Equals(cmp[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Contains returns true if the Entries contain a given Entry.
|
||||
func (e Entries) Contains(entry *Entry) bool {
|
||||
for _, curr := range e {
|
||||
if curr.Equals(entry) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Diff compares two entries and returns the added and removed entries.
|
||||
func (e Entries) Diff(cmp Entries) (Entries, Entries) {
|
||||
added := Entries{}
|
||||
for _, entry := range cmp {
|
||||
if !e.Contains(entry) {
|
||||
added = append(added, entry)
|
||||
}
|
||||
}
|
||||
|
||||
removed := Entries{}
|
||||
for _, entry := range e {
|
||||
if !cmp.Contains(entry) {
|
||||
removed = append(removed, entry)
|
||||
}
|
||||
}
|
||||
|
||||
return added, removed
|
||||
}
|
||||
|
||||
// CreateEntries returns an array of entries based on the given addresses.
|
||||
func CreateEntries(addrs []string) (Entries, error) {
|
||||
entries := Entries{}
|
||||
if addrs == nil {
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
if len(addr) == 0 {
|
||||
continue
|
||||
}
|
||||
entry, err := NewEntry(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
package discovery // import "github.com/docker/docker/pkg/discovery"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Generate takes care of IP generation
|
||||
func Generate(pattern string) []string {
|
||||
re, _ := regexp.Compile(`\[(.+):(.+)\]`)
|
||||
submatch := re.FindStringSubmatch(pattern)
|
||||
if submatch == nil {
|
||||
return []string{pattern}
|
||||
}
|
||||
|
||||
from, err := strconv.Atoi(submatch[1])
|
||||
if err != nil {
|
||||
return []string{pattern}
|
||||
}
|
||||
to, err := strconv.Atoi(submatch[2])
|
||||
if err != nil {
|
||||
return []string{pattern}
|
||||
}
|
||||
|
||||
template := re.ReplaceAllString(pattern, "%d")
|
||||
|
||||
var result []string
|
||||
for val := from; val <= to; val++ {
|
||||
entry := fmt.Sprintf(template, val)
|
||||
result = append(result, entry)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
-192
@@ -1,192 +0,0 @@
|
||||
package kv // import "github.com/docker/docker/pkg/discovery/kv"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/discovery"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/docker/libkv"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libkv/store/consul"
|
||||
"github.com/docker/libkv/store/etcd"
|
||||
"github.com/docker/libkv/store/zookeeper"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultDiscoveryPath = "docker/nodes"
|
||||
)
|
||||
|
||||
// Discovery is exported
|
||||
type Discovery struct {
|
||||
backend store.Backend
|
||||
store store.Store
|
||||
heartbeat time.Duration
|
||||
ttl time.Duration
|
||||
prefix string
|
||||
path string
|
||||
}
|
||||
|
||||
func init() {
|
||||
Init()
|
||||
}
|
||||
|
||||
// Init is exported
|
||||
func Init() {
|
||||
// Register to libkv
|
||||
zookeeper.Register()
|
||||
consul.Register()
|
||||
etcd.Register()
|
||||
|
||||
// Register to internal discovery service
|
||||
discovery.Register("zk", &Discovery{backend: store.ZK})
|
||||
discovery.Register("consul", &Discovery{backend: store.CONSUL})
|
||||
discovery.Register("etcd", &Discovery{backend: store.ETCD})
|
||||
}
|
||||
|
||||
// Initialize is exported
|
||||
func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Duration, clusterOpts map[string]string) error {
|
||||
var (
|
||||
parts = strings.SplitN(uris, "/", 2)
|
||||
addrs = strings.Split(parts[0], ",")
|
||||
err error
|
||||
)
|
||||
|
||||
// A custom prefix to the path can be optionally used.
|
||||
if len(parts) == 2 {
|
||||
s.prefix = parts[1]
|
||||
}
|
||||
|
||||
s.heartbeat = heartbeat
|
||||
s.ttl = ttl
|
||||
|
||||
// Use a custom path if specified in discovery options
|
||||
dpath := defaultDiscoveryPath
|
||||
if clusterOpts["kv.path"] != "" {
|
||||
dpath = clusterOpts["kv.path"]
|
||||
}
|
||||
|
||||
s.path = path.Join(s.prefix, dpath)
|
||||
|
||||
var config *store.Config
|
||||
if clusterOpts["kv.cacertfile"] != "" && clusterOpts["kv.certfile"] != "" && clusterOpts["kv.keyfile"] != "" {
|
||||
logrus.Info("Initializing discovery with TLS")
|
||||
tlsConfig, err := tlsconfig.Client(tlsconfig.Options{
|
||||
CAFile: clusterOpts["kv.cacertfile"],
|
||||
CertFile: clusterOpts["kv.certfile"],
|
||||
KeyFile: clusterOpts["kv.keyfile"],
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config = &store.Config{
|
||||
// Set ClientTLS to trigger https (bug in libkv/etcd)
|
||||
ClientTLS: &store.ClientTLSConfig{
|
||||
CACertFile: clusterOpts["kv.cacertfile"],
|
||||
CertFile: clusterOpts["kv.certfile"],
|
||||
KeyFile: clusterOpts["kv.keyfile"],
|
||||
},
|
||||
// The actual TLS config that will be used
|
||||
TLS: tlsConfig,
|
||||
}
|
||||
} else {
|
||||
logrus.Info("Initializing discovery without TLS")
|
||||
}
|
||||
|
||||
// Creates a new store, will ignore options given
|
||||
// if not supported by the chosen store
|
||||
s.store, err = libkv.NewStore(s.backend, addrs, config)
|
||||
return err
|
||||
}
|
||||
|
||||
// Watch the store until either there's a store error or we receive a stop request.
|
||||
// Returns false if we shouldn't attempt watching the store anymore (stop request received).
|
||||
func (s *Discovery) watchOnce(stopCh <-chan struct{}, watchCh <-chan []*store.KVPair, discoveryCh chan discovery.Entries, errCh chan error) bool {
|
||||
for {
|
||||
select {
|
||||
case pairs := <-watchCh:
|
||||
if pairs == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
logrus.WithField("discovery", s.backend).Debugf("Watch triggered with %d nodes", len(pairs))
|
||||
|
||||
// Convert `KVPair` into `discovery.Entry`.
|
||||
addrs := make([]string, len(pairs))
|
||||
for _, pair := range pairs {
|
||||
addrs = append(addrs, string(pair.Value))
|
||||
}
|
||||
|
||||
entries, err := discovery.CreateEntries(addrs)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
} else {
|
||||
discoveryCh <- entries
|
||||
}
|
||||
case <-stopCh:
|
||||
// We were requested to stop watching.
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Watch is exported
|
||||
func (s *Discovery) Watch(stopCh <-chan struct{}) (<-chan discovery.Entries, <-chan error) {
|
||||
ch := make(chan discovery.Entries)
|
||||
errCh := make(chan error)
|
||||
|
||||
go func() {
|
||||
defer close(ch)
|
||||
defer close(errCh)
|
||||
|
||||
// Forever: Create a store watch, watch until we get an error and then try again.
|
||||
// Will only stop if we receive a stopCh request.
|
||||
for {
|
||||
// Create the path to watch if it does not exist yet
|
||||
exists, err := s.store.Exists(s.path)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
if !exists {
|
||||
if err := s.store.Put(s.path, []byte(""), &store.WriteOptions{IsDir: true}); err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
}
|
||||
|
||||
// Set up a watch.
|
||||
watchCh, err := s.store.WatchTree(s.path, stopCh)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
} else {
|
||||
if !s.watchOnce(stopCh, watchCh, ch, errCh) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here it means the store watch channel was closed. This
|
||||
// is unexpected so let's retry later.
|
||||
errCh <- fmt.Errorf("Unexpected watch error")
|
||||
time.Sleep(s.heartbeat)
|
||||
}
|
||||
}()
|
||||
return ch, errCh
|
||||
}
|
||||
|
||||
// Register is exported
|
||||
func (s *Discovery) Register(addr string) error {
|
||||
opts := &store.WriteOptions{TTL: s.ttl}
|
||||
return s.store.Put(path.Join(s.path, addr), []byte(addr), opts)
|
||||
}
|
||||
|
||||
// Store returns the underlying store used by KV discovery.
|
||||
func (s *Discovery) Store() store.Store {
|
||||
return s.store
|
||||
}
|
||||
|
||||
// Prefix returns the store prefix
|
||||
func (s *Discovery) Prefix() string {
|
||||
return s.prefix
|
||||
}
|
||||
+1
-1
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// PatternMatcher allows checking paths agaist a list of patterns
|
||||
// PatternMatcher allows checking paths against a list of patterns
|
||||
type PatternMatcher struct {
|
||||
patterns []*Pattern
|
||||
exclusions bool
|
||||
|
||||
+2
-2
@@ -30,8 +30,8 @@ func (e ranges) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
|
||||
func (e ranges) Less(i, j int) bool { return e[i].Start < e[j].Start }
|
||||
|
||||
const (
|
||||
subuidFileName string = "/etc/subuid"
|
||||
subgidFileName string = "/etc/subgid"
|
||||
subuidFileName = "/etc/subuid"
|
||||
subgidFileName = "/etc/subgid"
|
||||
)
|
||||
|
||||
// MkdirAllAndChown creates a directory (include any along the path) and then modifies
|
||||
|
||||
+5
-5
@@ -8,9 +8,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
gotty "github.com/Nvveen/Gotty"
|
||||
"github.com/Nvveen/Gotty"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to
|
||||
@@ -245,7 +245,7 @@ func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error {
|
||||
// DisplayJSONMessagesStream displays a json message stream from `in` to `out`, `isTerminal`
|
||||
// describes if `out` is a terminal. If this is the case, it will print `\n` at the end of
|
||||
// each line and move the cursor while displaying.
|
||||
func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(*json.RawMessage)) error {
|
||||
func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(JSONMessage)) error {
|
||||
var (
|
||||
dec = json.NewDecoder(in)
|
||||
ids = make(map[string]int)
|
||||
@@ -277,7 +277,7 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr,
|
||||
|
||||
if jm.Aux != nil {
|
||||
if auxCallback != nil {
|
||||
auxCallback(jm.Aux)
|
||||
auxCallback(jm)
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -330,6 +330,6 @@ type stream interface {
|
||||
}
|
||||
|
||||
// DisplayJSONMessagesToStream prints json messages to the output stream
|
||||
func DisplayJSONMessagesToStream(in io.Reader, stream stream, auxCallback func(*json.RawMessage)) error {
|
||||
func DisplayJSONMessagesToStream(in io.Reader, stream stream, auxCallback func(JSONMessage)) error {
|
||||
return DisplayJSONMessagesStream(in, stream, stream.FD(), stream.IsTerminal(), auxCallback)
|
||||
}
|
||||
|
||||
-112
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
Package locker provides a mechanism for creating finer-grained locking to help
|
||||
free up more global locks to handle other tasks.
|
||||
|
||||
The implementation looks close to a sync.Mutex, however the user must provide a
|
||||
reference to use to refer to the underlying lock when locking and unlocking,
|
||||
and unlock may generate an error.
|
||||
|
||||
If a lock with a given name does not exist when `Lock` is called, one is
|
||||
created.
|
||||
Lock references are automatically cleaned up on `Unlock` if nothing else is
|
||||
waiting for the lock.
|
||||
*/
|
||||
package locker // import "github.com/docker/docker/pkg/locker"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// ErrNoSuchLock is returned when the requested lock does not exist
|
||||
var ErrNoSuchLock = errors.New("no such lock")
|
||||
|
||||
// Locker provides a locking mechanism based on the passed in reference name
|
||||
type Locker struct {
|
||||
mu sync.Mutex
|
||||
locks map[string]*lockCtr
|
||||
}
|
||||
|
||||
// lockCtr is used by Locker to represent a lock with a given name.
|
||||
type lockCtr struct {
|
||||
mu sync.Mutex
|
||||
// waiters is the number of waiters waiting to acquire the lock
|
||||
// this is int32 instead of uint32 so we can add `-1` in `dec()`
|
||||
waiters int32
|
||||
}
|
||||
|
||||
// inc increments the number of waiters waiting for the lock
|
||||
func (l *lockCtr) inc() {
|
||||
atomic.AddInt32(&l.waiters, 1)
|
||||
}
|
||||
|
||||
// dec decrements the number of waiters waiting on the lock
|
||||
func (l *lockCtr) dec() {
|
||||
atomic.AddInt32(&l.waiters, -1)
|
||||
}
|
||||
|
||||
// count gets the current number of waiters
|
||||
func (l *lockCtr) count() int32 {
|
||||
return atomic.LoadInt32(&l.waiters)
|
||||
}
|
||||
|
||||
// Lock locks the mutex
|
||||
func (l *lockCtr) Lock() {
|
||||
l.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks the mutex
|
||||
func (l *lockCtr) Unlock() {
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// New creates a new Locker
|
||||
func New() *Locker {
|
||||
return &Locker{
|
||||
locks: make(map[string]*lockCtr),
|
||||
}
|
||||
}
|
||||
|
||||
// Lock locks a mutex with the given name. If it doesn't exist, one is created
|
||||
func (l *Locker) Lock(name string) {
|
||||
l.mu.Lock()
|
||||
if l.locks == nil {
|
||||
l.locks = make(map[string]*lockCtr)
|
||||
}
|
||||
|
||||
nameLock, exists := l.locks[name]
|
||||
if !exists {
|
||||
nameLock = &lockCtr{}
|
||||
l.locks[name] = nameLock
|
||||
}
|
||||
|
||||
// increment the nameLock waiters while inside the main mutex
|
||||
// this makes sure that the lock isn't deleted if `Lock` and `Unlock` are called concurrently
|
||||
nameLock.inc()
|
||||
l.mu.Unlock()
|
||||
|
||||
// Lock the nameLock outside the main mutex so we don't block other operations
|
||||
// once locked then we can decrement the number of waiters for this lock
|
||||
nameLock.Lock()
|
||||
nameLock.dec()
|
||||
}
|
||||
|
||||
// Unlock unlocks the mutex with the given name
|
||||
// If the given lock is not being waited on by any other callers, it is deleted
|
||||
func (l *Locker) Unlock(name string) error {
|
||||
l.mu.Lock()
|
||||
nameLock, exists := l.locks[name]
|
||||
if !exists {
|
||||
l.mu.Unlock()
|
||||
return ErrNoSuchLock
|
||||
}
|
||||
|
||||
if nameLock.count() == 0 {
|
||||
delete(l.locks, name)
|
||||
}
|
||||
nameLock.Unlock()
|
||||
|
||||
l.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
// Package parsers provides helper functions to parse and validate different type
|
||||
// of string. It can be hosts, unix addresses, tcp addresses, filters, kernel
|
||||
// operating system versions.
|
||||
package parsers // import "github.com/docker/docker/pkg/parsers"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseKeyValueOpt parses and validates the specified string as a key/value pair (key=value)
|
||||
func ParseKeyValueOpt(opt string) (string, string, error) {
|
||||
parts := strings.SplitN(opt, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return "", "", fmt.Errorf("Unable to parse key/value option: %s", opt)
|
||||
}
|
||||
return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
|
||||
}
|
||||
|
||||
// ParseUintList parses and validates the specified string as the value
|
||||
// found in some cgroup file (e.g. `cpuset.cpus`, `cpuset.mems`), which could be
|
||||
// one of the formats below. Note that duplicates are actually allowed in the
|
||||
// input string. It returns a `map[int]bool` with available elements from `val`
|
||||
// set to `true`.
|
||||
// Supported formats:
|
||||
// 7
|
||||
// 1-6
|
||||
// 0,3-4,7,8-10
|
||||
// 0-0,0,1-7
|
||||
// 03,1-3 <- this is gonna get parsed as [1,2,3]
|
||||
// 3,2,1
|
||||
// 0-2,3,1
|
||||
func ParseUintList(val string) (map[int]bool, error) {
|
||||
if val == "" {
|
||||
return map[int]bool{}, nil
|
||||
}
|
||||
|
||||
availableInts := make(map[int]bool)
|
||||
split := strings.Split(val, ",")
|
||||
errInvalidFormat := fmt.Errorf("invalid format: %s", val)
|
||||
|
||||
for _, r := range split {
|
||||
if !strings.Contains(r, "-") {
|
||||
v, err := strconv.Atoi(r)
|
||||
if err != nil {
|
||||
return nil, errInvalidFormat
|
||||
}
|
||||
availableInts[v] = true
|
||||
} else {
|
||||
split := strings.SplitN(r, "-", 2)
|
||||
min, err := strconv.Atoi(split[0])
|
||||
if err != nil {
|
||||
return nil, errInvalidFormat
|
||||
}
|
||||
max, err := strconv.Atoi(split[1])
|
||||
if err != nil {
|
||||
return nil, errInvalidFormat
|
||||
}
|
||||
if max < min {
|
||||
return nil, errInvalidFormat
|
||||
}
|
||||
for i := min; i <= max; i++ {
|
||||
availableInts[i] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return availableInts, nil
|
||||
}
|
||||
+16
-1
@@ -1,6 +1,9 @@
|
||||
package plugingetter // import "github.com/docker/docker/pkg/plugingetter"
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/plugins"
|
||||
)
|
||||
|
||||
@@ -15,10 +18,22 @@ const (
|
||||
|
||||
// CompatPlugin is an abstraction to handle both v2(new) and v1(legacy) plugins.
|
||||
type CompatPlugin interface {
|
||||
Client() *plugins.Client
|
||||
Name() string
|
||||
ScopedPath(string) string
|
||||
IsV1() bool
|
||||
PluginWithV1Client
|
||||
}
|
||||
|
||||
// PluginWithV1Client is a plugin that directly utilizes the v1/http plugin client
|
||||
type PluginWithV1Client interface {
|
||||
Client() *plugins.Client
|
||||
}
|
||||
|
||||
// PluginAddr is a plugin that exposes the socket address for creating custom clients rather than the built-in `*plugins.Client`
|
||||
type PluginAddr interface {
|
||||
Addr() net.Addr
|
||||
Timeout() time.Duration
|
||||
Protocol() string
|
||||
}
|
||||
|
||||
// CountedPlugin is a plugin which is reference counted.
|
||||
|
||||
+8
@@ -31,6 +31,9 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ProtocolSchemeHTTPV1 is the name of the protocol used for interacting with plugins using this package.
|
||||
const ProtocolSchemeHTTPV1 = "moby.plugins.http/v1"
|
||||
|
||||
var (
|
||||
// ErrNotImplements is returned if the plugin does not implement the requested driver.
|
||||
ErrNotImplements = errors.New("Plugin does not implement the requested driver")
|
||||
@@ -88,6 +91,11 @@ func (p *Plugin) Client() *Client {
|
||||
return p.client
|
||||
}
|
||||
|
||||
// Protocol returns the protocol name/version used for plugins in this package.
|
||||
func (p *Plugin) Protocol() string {
|
||||
return ProtocolSchemeHTTPV1
|
||||
}
|
||||
|
||||
// IsV1 returns true for V1 plugins and false otherwise.
|
||||
func (p *Plugin) IsV1() bool {
|
||||
return true
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
// CatchAll catches all signals and relays them to the specified channel.
|
||||
func CatchAll(sigc chan os.Signal) {
|
||||
handledSigs := []os.Signal{}
|
||||
var handledSigs []os.Signal
|
||||
for _, s := range SignalMap {
|
||||
handledSigs = append(handledSigs, s)
|
||||
}
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
// +build !linux,!windows
|
||||
|
||||
package sysinfo // import "github.com/docker/docker/pkg/sysinfo"
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// NumCPU returns the number of CPUs
|
||||
func NumCPU() int {
|
||||
return runtime.NumCPU()
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
package sysinfo // import "github.com/docker/docker/pkg/sysinfo"
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// numCPU queries the system for the count of threads available
|
||||
// for use to this process.
|
||||
//
|
||||
// Issues two syscalls.
|
||||
// Returns 0 on errors. Use |runtime.NumCPU| in that case.
|
||||
func numCPU() int {
|
||||
// Gets the affinity mask for a process: The very one invoking this function.
|
||||
pid, _, _ := unix.RawSyscall(unix.SYS_GETPID, 0, 0, 0)
|
||||
|
||||
var mask [1024 / 64]uintptr
|
||||
_, _, err := unix.RawSyscall(unix.SYS_SCHED_GETAFFINITY, pid, uintptr(len(mask)*8), uintptr(unsafe.Pointer(&mask[0])))
|
||||
if err != 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// For every available thread a bit is set in the mask.
|
||||
ncpu := 0
|
||||
for _, e := range mask {
|
||||
if e == 0 {
|
||||
continue
|
||||
}
|
||||
ncpu += int(popcnt(uint64(e)))
|
||||
}
|
||||
return ncpu
|
||||
}
|
||||
|
||||
// NumCPU returns the number of CPUs which are currently online
|
||||
func NumCPU() int {
|
||||
if ncpu := numCPU(); ncpu > 0 {
|
||||
return ncpu
|
||||
}
|
||||
return runtime.NumCPU()
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
package sysinfo // import "github.com/docker/docker/pkg/sysinfo"
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
kernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
getCurrentProcess = kernel32.NewProc("GetCurrentProcess")
|
||||
getProcessAffinityMask = kernel32.NewProc("GetProcessAffinityMask")
|
||||
)
|
||||
|
||||
func numCPU() int {
|
||||
// Gets the affinity mask for a process
|
||||
var mask, sysmask uintptr
|
||||
currentProcess, _, _ := getCurrentProcess.Call()
|
||||
ret, _, _ := getProcessAffinityMask.Call(currentProcess, uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
|
||||
if ret == 0 {
|
||||
return 0
|
||||
}
|
||||
// For every available thread a bit is set in the mask.
|
||||
ncpu := int(popcnt(uint64(mask)))
|
||||
return ncpu
|
||||
}
|
||||
|
||||
// NumCPU returns the number of CPUs which are currently online
|
||||
func NumCPU() int {
|
||||
if ncpu := numCPU(); ncpu > 0 {
|
||||
return ncpu
|
||||
}
|
||||
return runtime.NumCPU()
|
||||
}
|
||||
-144
@@ -1,144 +0,0 @@
|
||||
package sysinfo // import "github.com/docker/docker/pkg/sysinfo"
|
||||
|
||||
import "github.com/docker/docker/pkg/parsers"
|
||||
|
||||
// SysInfo stores information about which features a kernel supports.
|
||||
// TODO Windows: Factor out platform specific capabilities.
|
||||
type SysInfo struct {
|
||||
// Whether the kernel supports AppArmor or not
|
||||
AppArmor bool
|
||||
// Whether the kernel supports Seccomp or not
|
||||
Seccomp bool
|
||||
|
||||
cgroupMemInfo
|
||||
cgroupCPUInfo
|
||||
cgroupBlkioInfo
|
||||
cgroupCpusetInfo
|
||||
cgroupPids
|
||||
|
||||
// Whether IPv4 forwarding is supported or not, if this was disabled, networking will not work
|
||||
IPv4ForwardingDisabled bool
|
||||
|
||||
// Whether bridge-nf-call-iptables is supported or not
|
||||
BridgeNFCallIPTablesDisabled bool
|
||||
|
||||
// Whether bridge-nf-call-ip6tables is supported or not
|
||||
BridgeNFCallIP6TablesDisabled bool
|
||||
|
||||
// Whether the cgroup has the mountpoint of "devices" or not
|
||||
CgroupDevicesEnabled bool
|
||||
}
|
||||
|
||||
type cgroupMemInfo struct {
|
||||
// Whether memory limit is supported or not
|
||||
MemoryLimit bool
|
||||
|
||||
// Whether swap limit is supported or not
|
||||
SwapLimit bool
|
||||
|
||||
// Whether soft limit is supported or not
|
||||
MemoryReservation bool
|
||||
|
||||
// Whether OOM killer disable is supported or not
|
||||
OomKillDisable bool
|
||||
|
||||
// Whether memory swappiness is supported or not
|
||||
MemorySwappiness bool
|
||||
|
||||
// Whether kernel memory limit is supported or not
|
||||
KernelMemory bool
|
||||
}
|
||||
|
||||
type cgroupCPUInfo struct {
|
||||
// Whether CPU shares is supported or not
|
||||
CPUShares bool
|
||||
|
||||
// Whether CPU CFS(Completely Fair Scheduler) period is supported or not
|
||||
CPUCfsPeriod bool
|
||||
|
||||
// Whether CPU CFS(Completely Fair Scheduler) quota is supported or not
|
||||
CPUCfsQuota bool
|
||||
|
||||
// Whether CPU real-time period is supported or not
|
||||
CPURealtimePeriod bool
|
||||
|
||||
// Whether CPU real-time runtime is supported or not
|
||||
CPURealtimeRuntime bool
|
||||
}
|
||||
|
||||
type cgroupBlkioInfo struct {
|
||||
// Whether Block IO weight is supported or not
|
||||
BlkioWeight bool
|
||||
|
||||
// Whether Block IO weight_device is supported or not
|
||||
BlkioWeightDevice bool
|
||||
|
||||
// Whether Block IO read limit in bytes per second is supported or not
|
||||
BlkioReadBpsDevice bool
|
||||
|
||||
// Whether Block IO write limit in bytes per second is supported or not
|
||||
BlkioWriteBpsDevice bool
|
||||
|
||||
// Whether Block IO read limit in IO per second is supported or not
|
||||
BlkioReadIOpsDevice bool
|
||||
|
||||
// Whether Block IO write limit in IO per second is supported or not
|
||||
BlkioWriteIOpsDevice bool
|
||||
}
|
||||
|
||||
type cgroupCpusetInfo struct {
|
||||
// Whether Cpuset is supported or not
|
||||
Cpuset bool
|
||||
|
||||
// Available Cpuset's cpus
|
||||
Cpus string
|
||||
|
||||
// Available Cpuset's memory nodes
|
||||
Mems string
|
||||
}
|
||||
|
||||
type cgroupPids struct {
|
||||
// Whether Pids Limit is supported or not
|
||||
PidsLimit bool
|
||||
}
|
||||
|
||||
// IsCpusetCpusAvailable returns `true` if the provided string set is contained
|
||||
// in cgroup's cpuset.cpus set, `false` otherwise.
|
||||
// If error is not nil a parsing error occurred.
|
||||
func (c cgroupCpusetInfo) IsCpusetCpusAvailable(provided string) (bool, error) {
|
||||
return isCpusetListAvailable(provided, c.Cpus)
|
||||
}
|
||||
|
||||
// IsCpusetMemsAvailable returns `true` if the provided string set is contained
|
||||
// in cgroup's cpuset.mems set, `false` otherwise.
|
||||
// If error is not nil a parsing error occurred.
|
||||
func (c cgroupCpusetInfo) IsCpusetMemsAvailable(provided string) (bool, error) {
|
||||
return isCpusetListAvailable(provided, c.Mems)
|
||||
}
|
||||
|
||||
func isCpusetListAvailable(provided, available string) (bool, error) {
|
||||
parsedProvided, err := parsers.ParseUintList(provided)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
parsedAvailable, err := parsers.ParseUintList(available)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for k := range parsedProvided {
|
||||
if !parsedAvailable[k] {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Returns bit count of 1, used by NumCPU
|
||||
func popcnt(x uint64) (n byte) {
|
||||
x -= (x >> 1) & 0x5555555555555555
|
||||
x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
|
||||
x += x >> 4
|
||||
x &= 0x0f0f0f0f0f0f0f0f
|
||||
x *= 0x0101010101010101
|
||||
return byte(x >> 56)
|
||||
}
|
||||
-254
@@ -1,254 +0,0 @@
|
||||
package sysinfo // import "github.com/docker/docker/pkg/sysinfo"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func findCgroupMountpoints() (map[string]string, error) {
|
||||
cgMounts, err := cgroups.GetCgroupMounts(false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to parse cgroup information: %v", err)
|
||||
}
|
||||
mps := make(map[string]string)
|
||||
for _, m := range cgMounts {
|
||||
for _, ss := range m.Subsystems {
|
||||
mps[ss] = m.Mountpoint
|
||||
}
|
||||
}
|
||||
return mps, nil
|
||||
}
|
||||
|
||||
// New returns a new SysInfo, using the filesystem to detect which features
|
||||
// the kernel supports. If `quiet` is `false` warnings are printed in logs
|
||||
// whenever an error occurs or misconfigurations are present.
|
||||
func New(quiet bool) *SysInfo {
|
||||
sysInfo := &SysInfo{}
|
||||
cgMounts, err := findCgroupMountpoints()
|
||||
if err != nil {
|
||||
logrus.Warnf("Failed to parse cgroup information: %v", err)
|
||||
} else {
|
||||
sysInfo.cgroupMemInfo = checkCgroupMem(cgMounts, quiet)
|
||||
sysInfo.cgroupCPUInfo = checkCgroupCPU(cgMounts, quiet)
|
||||
sysInfo.cgroupBlkioInfo = checkCgroupBlkioInfo(cgMounts, quiet)
|
||||
sysInfo.cgroupCpusetInfo = checkCgroupCpusetInfo(cgMounts, quiet)
|
||||
sysInfo.cgroupPids = checkCgroupPids(quiet)
|
||||
}
|
||||
|
||||
_, ok := cgMounts["devices"]
|
||||
sysInfo.CgroupDevicesEnabled = ok
|
||||
|
||||
sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
|
||||
sysInfo.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
|
||||
sysInfo.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
|
||||
|
||||
// Check if AppArmor is supported.
|
||||
if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
|
||||
sysInfo.AppArmor = true
|
||||
}
|
||||
|
||||
// Check if Seccomp is supported, via CONFIG_SECCOMP.
|
||||
if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
|
||||
// Make sure the kernel has CONFIG_SECCOMP_FILTER.
|
||||
if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
|
||||
sysInfo.Seccomp = true
|
||||
}
|
||||
}
|
||||
|
||||
return sysInfo
|
||||
}
|
||||
|
||||
// checkCgroupMem reads the memory information from the memory cgroup mount point.
|
||||
func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo {
|
||||
mountPoint, ok := cgMounts["memory"]
|
||||
if !ok {
|
||||
if !quiet {
|
||||
logrus.Warn("Your kernel does not support cgroup memory limit")
|
||||
}
|
||||
return cgroupMemInfo{}
|
||||
}
|
||||
|
||||
swapLimit := cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
|
||||
if !quiet && !swapLimit {
|
||||
logrus.Warn("Your kernel does not support swap memory limit")
|
||||
}
|
||||
memoryReservation := cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
|
||||
if !quiet && !memoryReservation {
|
||||
logrus.Warn("Your kernel does not support memory reservation")
|
||||
}
|
||||
oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control")
|
||||
if !quiet && !oomKillDisable {
|
||||
logrus.Warn("Your kernel does not support oom control")
|
||||
}
|
||||
memorySwappiness := cgroupEnabled(mountPoint, "memory.swappiness")
|
||||
if !quiet && !memorySwappiness {
|
||||
logrus.Warn("Your kernel does not support memory swappiness")
|
||||
}
|
||||
kernelMemory := cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
|
||||
if !quiet && !kernelMemory {
|
||||
logrus.Warn("Your kernel does not support kernel memory limit")
|
||||
}
|
||||
|
||||
return cgroupMemInfo{
|
||||
MemoryLimit: true,
|
||||
SwapLimit: swapLimit,
|
||||
MemoryReservation: memoryReservation,
|
||||
OomKillDisable: oomKillDisable,
|
||||
MemorySwappiness: memorySwappiness,
|
||||
KernelMemory: kernelMemory,
|
||||
}
|
||||
}
|
||||
|
||||
// checkCgroupCPU reads the cpu information from the cpu cgroup mount point.
|
||||
func checkCgroupCPU(cgMounts map[string]string, quiet bool) cgroupCPUInfo {
|
||||
mountPoint, ok := cgMounts["cpu"]
|
||||
if !ok {
|
||||
if !quiet {
|
||||
logrus.Warn("Unable to find cpu cgroup in mounts")
|
||||
}
|
||||
return cgroupCPUInfo{}
|
||||
}
|
||||
|
||||
cpuShares := cgroupEnabled(mountPoint, "cpu.shares")
|
||||
if !quiet && !cpuShares {
|
||||
logrus.Warn("Your kernel does not support cgroup cpu shares")
|
||||
}
|
||||
|
||||
cpuCfsPeriod := cgroupEnabled(mountPoint, "cpu.cfs_period_us")
|
||||
if !quiet && !cpuCfsPeriod {
|
||||
logrus.Warn("Your kernel does not support cgroup cfs period")
|
||||
}
|
||||
|
||||
cpuCfsQuota := cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
|
||||
if !quiet && !cpuCfsQuota {
|
||||
logrus.Warn("Your kernel does not support cgroup cfs quotas")
|
||||
}
|
||||
|
||||
cpuRealtimePeriod := cgroupEnabled(mountPoint, "cpu.rt_period_us")
|
||||
if !quiet && !cpuRealtimePeriod {
|
||||
logrus.Warn("Your kernel does not support cgroup rt period")
|
||||
}
|
||||
|
||||
cpuRealtimeRuntime := cgroupEnabled(mountPoint, "cpu.rt_runtime_us")
|
||||
if !quiet && !cpuRealtimeRuntime {
|
||||
logrus.Warn("Your kernel does not support cgroup rt runtime")
|
||||
}
|
||||
|
||||
return cgroupCPUInfo{
|
||||
CPUShares: cpuShares,
|
||||
CPUCfsPeriod: cpuCfsPeriod,
|
||||
CPUCfsQuota: cpuCfsQuota,
|
||||
CPURealtimePeriod: cpuRealtimePeriod,
|
||||
CPURealtimeRuntime: cpuRealtimeRuntime,
|
||||
}
|
||||
}
|
||||
|
||||
// checkCgroupBlkioInfo reads the blkio information from the blkio cgroup mount point.
|
||||
func checkCgroupBlkioInfo(cgMounts map[string]string, quiet bool) cgroupBlkioInfo {
|
||||
mountPoint, ok := cgMounts["blkio"]
|
||||
if !ok {
|
||||
if !quiet {
|
||||
logrus.Warn("Unable to find blkio cgroup in mounts")
|
||||
}
|
||||
return cgroupBlkioInfo{}
|
||||
}
|
||||
|
||||
weight := cgroupEnabled(mountPoint, "blkio.weight")
|
||||
if !quiet && !weight {
|
||||
logrus.Warn("Your kernel does not support cgroup blkio weight")
|
||||
}
|
||||
|
||||
weightDevice := cgroupEnabled(mountPoint, "blkio.weight_device")
|
||||
if !quiet && !weightDevice {
|
||||
logrus.Warn("Your kernel does not support cgroup blkio weight_device")
|
||||
}
|
||||
|
||||
readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
|
||||
if !quiet && !readBpsDevice {
|
||||
logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device")
|
||||
}
|
||||
|
||||
writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
|
||||
if !quiet && !writeBpsDevice {
|
||||
logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device")
|
||||
}
|
||||
readIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
|
||||
if !quiet && !readIOpsDevice {
|
||||
logrus.Warn("Your kernel does not support cgroup blkio throttle.read_iops_device")
|
||||
}
|
||||
|
||||
writeIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
|
||||
if !quiet && !writeIOpsDevice {
|
||||
logrus.Warn("Your kernel does not support cgroup blkio throttle.write_iops_device")
|
||||
}
|
||||
return cgroupBlkioInfo{
|
||||
BlkioWeight: weight,
|
||||
BlkioWeightDevice: weightDevice,
|
||||
BlkioReadBpsDevice: readBpsDevice,
|
||||
BlkioWriteBpsDevice: writeBpsDevice,
|
||||
BlkioReadIOpsDevice: readIOpsDevice,
|
||||
BlkioWriteIOpsDevice: writeIOpsDevice,
|
||||
}
|
||||
}
|
||||
|
||||
// checkCgroupCpusetInfo reads the cpuset information from the cpuset cgroup mount point.
|
||||
func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetInfo {
|
||||
mountPoint, ok := cgMounts["cpuset"]
|
||||
if !ok {
|
||||
if !quiet {
|
||||
logrus.Warn("Unable to find cpuset cgroup in mounts")
|
||||
}
|
||||
return cgroupCpusetInfo{}
|
||||
}
|
||||
|
||||
cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
|
||||
if err != nil {
|
||||
return cgroupCpusetInfo{}
|
||||
}
|
||||
|
||||
mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems"))
|
||||
if err != nil {
|
||||
return cgroupCpusetInfo{}
|
||||
}
|
||||
|
||||
return cgroupCpusetInfo{
|
||||
Cpuset: true,
|
||||
Cpus: strings.TrimSpace(string(cpus)),
|
||||
Mems: strings.TrimSpace(string(mems)),
|
||||
}
|
||||
}
|
||||
|
||||
// checkCgroupPids reads the pids information from the pids cgroup mount point.
|
||||
func checkCgroupPids(quiet bool) cgroupPids {
|
||||
_, err := cgroups.FindCgroupMountpoint("pids")
|
||||
if err != nil {
|
||||
if !quiet {
|
||||
logrus.Warn(err)
|
||||
}
|
||||
return cgroupPids{}
|
||||
}
|
||||
|
||||
return cgroupPids{
|
||||
PidsLimit: true,
|
||||
}
|
||||
}
|
||||
|
||||
func cgroupEnabled(mountPoint, name string) bool {
|
||||
_, err := os.Stat(path.Join(mountPoint, name))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func readProcBool(path string) bool {
|
||||
val, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return strings.TrimSpace(string(val)) == "1"
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
// +build !linux,!windows
|
||||
|
||||
package sysinfo // import "github.com/docker/docker/pkg/sysinfo"
|
||||
|
||||
// New returns an empty SysInfo for non linux for now.
|
||||
func New(quiet bool) *SysInfo {
|
||||
sysInfo := &SysInfo{}
|
||||
return sysInfo
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package sysinfo // import "github.com/docker/docker/pkg/sysinfo"
|
||||
|
||||
// New returns an empty SysInfo for windows for now.
|
||||
func New(quiet bool) *SysInfo {
|
||||
sysInfo := &SysInfo{}
|
||||
return sysInfo
|
||||
}
|
||||
+1
-1
@@ -9,7 +9,7 @@ import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
ansiterm "github.com/Azure/go-ansiterm"
|
||||
"github.com/Azure/go-ansiterm"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
||||
+9
-1
@@ -9,7 +9,15 @@ import (
|
||||
|
||||
var (
|
||||
validPrefixes = map[string][]string{
|
||||
"url": {"http://", "https://"},
|
||||
"url": {"http://", "https://"},
|
||||
|
||||
// The github.com/ prefix is a special case used to treat context-paths
|
||||
// starting with `github.com` as a git URL if the given path does not
|
||||
// exist locally. The "github.com/" prefix is kept for backward compatibility,
|
||||
// and is a legacy feature.
|
||||
//
|
||||
// Going forward, no additional prefixes should be added, and users should
|
||||
// be encouraged to use explicit URLs (https://github.com/user/repo.git) instead.
|
||||
"git": {"git://", "github.com/", "git@"},
|
||||
"transport": {"tcp://", "tcp+tls://", "udp://", "unix://", "unixgram://"},
|
||||
}
|
||||
|
||||
+47
-2
@@ -2,15 +2,17 @@ package v2 // import "github.com/docker/docker/plugin/v2"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/plugingetter"
|
||||
"github.com/docker/docker/pkg/plugins"
|
||||
"github.com/opencontainers/go-digest"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// Plugin represents an individual plugin.
|
||||
@@ -27,6 +29,8 @@ type Plugin struct {
|
||||
modifyRuntimeSpec func(*specs.Spec)
|
||||
|
||||
SwarmServiceID string
|
||||
timeout time.Duration
|
||||
addr net.Addr
|
||||
}
|
||||
|
||||
const defaultPluginRuntimeDestination = "/run/docker/plugins"
|
||||
@@ -50,6 +54,7 @@ func (p *Plugin) ScopedPath(s string) string {
|
||||
}
|
||||
|
||||
// Client returns the plugin client.
|
||||
// Deprecated: use p.Addr() and manually create the client
|
||||
func (p *Plugin) Client() *plugins.Client {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
@@ -58,6 +63,7 @@ func (p *Plugin) Client() *plugins.Client {
|
||||
}
|
||||
|
||||
// SetPClient set the plugin client.
|
||||
// Deprecated: Hardcoded plugin client is deprecated
|
||||
func (p *Plugin) SetPClient(client *plugins.Client) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
@@ -257,10 +263,49 @@ func (p *Plugin) Release() {
|
||||
p.AddRefCount(plugingetter.Release)
|
||||
}
|
||||
|
||||
// SetSpecOptModifier sets the function to use to modify the the generated
|
||||
// SetSpecOptModifier sets the function to use to modify the generated
|
||||
// runtime spec.
|
||||
func (p *Plugin) SetSpecOptModifier(f func(*specs.Spec)) {
|
||||
p.mu.Lock()
|
||||
p.modifyRuntimeSpec = f
|
||||
p.mu.Unlock()
|
||||
}
|
||||
|
||||
// Timeout gets the currently configured connection timeout.
|
||||
// This should be used when dialing the plugin.
|
||||
func (p *Plugin) Timeout() time.Duration {
|
||||
p.mu.RLock()
|
||||
t := p.timeout
|
||||
p.mu.RUnlock()
|
||||
return t
|
||||
}
|
||||
|
||||
// SetTimeout sets the timeout to use for dialing.
|
||||
func (p *Plugin) SetTimeout(t time.Duration) {
|
||||
p.mu.Lock()
|
||||
p.timeout = t
|
||||
p.mu.Unlock()
|
||||
}
|
||||
|
||||
// Addr returns the net.Addr to use to connect to the plugin socket
|
||||
func (p *Plugin) Addr() net.Addr {
|
||||
p.mu.RLock()
|
||||
addr := p.addr
|
||||
p.mu.RUnlock()
|
||||
return addr
|
||||
}
|
||||
|
||||
// SetAddr sets the plugin address which can be used for dialing the plugin.
|
||||
func (p *Plugin) SetAddr(addr net.Addr) {
|
||||
p.mu.Lock()
|
||||
p.addr = addr
|
||||
p.mu.Unlock()
|
||||
}
|
||||
|
||||
// Protocol is the protocol that should be used for interacting with the plugin.
|
||||
func (p *Plugin) Protocol() string {
|
||||
if p.PluginObj.Config.Interface.ProtocolScheme != "" {
|
||||
return p.PluginObj.Config.Interface.ProtocolScheme
|
||||
}
|
||||
return plugins.ProtocolSchemeHTTPV1
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/oci"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ package v2 // import "github.com/docker/docker/plugin/v2"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// InitSpec creates an OCI spec from the plugin's config.
|
||||
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
package runconfig // import "github.com/docker/docker/runconfig"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
networktypes "github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/pkg/sysinfo"
|
||||
)
|
||||
|
||||
// ContainerDecoder implements httputils.ContainerDecoder
|
||||
// calling DecodeContainerConfig.
|
||||
type ContainerDecoder struct{}
|
||||
|
||||
// DecodeConfig makes ContainerDecoder to implement httputils.ContainerDecoder
|
||||
func (r ContainerDecoder) DecodeConfig(src io.Reader) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
|
||||
return decodeContainerConfig(src)
|
||||
}
|
||||
|
||||
// DecodeHostConfig makes ContainerDecoder to implement httputils.ContainerDecoder
|
||||
func (r ContainerDecoder) DecodeHostConfig(src io.Reader) (*container.HostConfig, error) {
|
||||
return decodeHostConfig(src)
|
||||
}
|
||||
|
||||
// decodeContainerConfig decodes a json encoded config into a ContainerConfigWrapper
|
||||
// struct and returns both a Config and a HostConfig struct
|
||||
// Be aware this function is not checking whether the resulted structs are nil,
|
||||
// it's your business to do so
|
||||
func decodeContainerConfig(src io.Reader) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
|
||||
var w ContainerConfigWrapper
|
||||
|
||||
decoder := json.NewDecoder(src)
|
||||
if err := decoder.Decode(&w); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
hc := w.getHostConfig()
|
||||
|
||||
// Perform platform-specific processing of Volumes and Binds.
|
||||
if w.Config != nil && hc != nil {
|
||||
|
||||
// Initialize the volumes map if currently nil
|
||||
if w.Config.Volumes == nil {
|
||||
w.Config.Volumes = make(map[string]struct{})
|
||||
}
|
||||
}
|
||||
|
||||
// Certain parameters need daemon-side validation that cannot be done
|
||||
// on the client, as only the daemon knows what is valid for the platform.
|
||||
if err := validateNetMode(w.Config, hc); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Validate isolation
|
||||
if err := validateIsolation(hc); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Validate QoS
|
||||
if err := validateQoS(hc); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Validate Resources
|
||||
if err := validateResources(hc, sysinfo.New(true)); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Validate Privileged
|
||||
if err := validatePrivileged(hc); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Validate ReadonlyRootfs
|
||||
if err := validateReadonlyRootfs(hc); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return w.Config, hc, w.NetworkingConfig, nil
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package runconfig // import "github.com/docker/docker/runconfig"
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
networktypes "github.com/docker/docker/api/types/network"
|
||||
)
|
||||
|
||||
// ContainerConfigWrapper is a Config wrapper that holds the container Config (portable)
|
||||
// and the corresponding HostConfig (non-portable).
|
||||
type ContainerConfigWrapper struct {
|
||||
*container.Config
|
||||
InnerHostConfig *container.HostConfig `json:"HostConfig,omitempty"`
|
||||
Cpuset string `json:",omitempty"` // Deprecated. Exported for backwards compatibility.
|
||||
NetworkingConfig *networktypes.NetworkingConfig `json:"NetworkingConfig,omitempty"`
|
||||
*container.HostConfig // Deprecated. Exported to read attributes from json that are not in the inner host config structure.
|
||||
}
|
||||
|
||||
// getHostConfig gets the HostConfig of the Config.
|
||||
// It's mostly there to handle Deprecated fields of the ContainerConfigWrapper
|
||||
func (w *ContainerConfigWrapper) getHostConfig() *container.HostConfig {
|
||||
hc := w.HostConfig
|
||||
|
||||
if hc == nil && w.InnerHostConfig != nil {
|
||||
hc = w.InnerHostConfig
|
||||
} else if w.InnerHostConfig != nil {
|
||||
if hc.Memory != 0 && w.InnerHostConfig.Memory == 0 {
|
||||
w.InnerHostConfig.Memory = hc.Memory
|
||||
}
|
||||
if hc.MemorySwap != 0 && w.InnerHostConfig.MemorySwap == 0 {
|
||||
w.InnerHostConfig.MemorySwap = hc.MemorySwap
|
||||
}
|
||||
if hc.CPUShares != 0 && w.InnerHostConfig.CPUShares == 0 {
|
||||
w.InnerHostConfig.CPUShares = hc.CPUShares
|
||||
}
|
||||
if hc.CpusetCpus != "" && w.InnerHostConfig.CpusetCpus == "" {
|
||||
w.InnerHostConfig.CpusetCpus = hc.CpusetCpus
|
||||
}
|
||||
|
||||
if hc.VolumeDriver != "" && w.InnerHostConfig.VolumeDriver == "" {
|
||||
w.InnerHostConfig.VolumeDriver = hc.VolumeDriver
|
||||
}
|
||||
|
||||
hc = w.InnerHostConfig
|
||||
}
|
||||
|
||||
if hc != nil {
|
||||
if w.Cpuset != "" && hc.CpusetCpus == "" {
|
||||
hc.CpusetCpus = w.Cpuset
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure NetworkMode has an acceptable value. We do this to ensure
|
||||
// backwards compatible API behavior.
|
||||
SetDefaultNetModeIfBlank(hc)
|
||||
|
||||
return hc
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package runconfig // import "github.com/docker/docker/runconfig"
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
networktypes "github.com/docker/docker/api/types/network"
|
||||
)
|
||||
|
||||
// ContainerConfigWrapper is a Config wrapper that holds the container Config (portable)
|
||||
// and the corresponding HostConfig (non-portable).
|
||||
type ContainerConfigWrapper struct {
|
||||
*container.Config
|
||||
HostConfig *container.HostConfig `json:"HostConfig,omitempty"`
|
||||
NetworkingConfig *networktypes.NetworkingConfig `json:"NetworkingConfig,omitempty"`
|
||||
}
|
||||
|
||||
// getHostConfig gets the HostConfig of the Config.
|
||||
func (w *ContainerConfigWrapper) getHostConfig() *container.HostConfig {
|
||||
return w.HostConfig
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
package runconfig // import "github.com/docker/docker/runconfig"
|
||||
|
||||
const (
|
||||
// ErrConflictContainerNetworkAndLinks conflict between --net=container and links
|
||||
ErrConflictContainerNetworkAndLinks validationError = "conflicting options: container type network can't be used with links. This would result in undefined behavior"
|
||||
// ErrConflictSharedNetwork conflict between private and other networks
|
||||
ErrConflictSharedNetwork validationError = "container sharing network namespace with another container or host cannot be connected to any other network"
|
||||
// ErrConflictHostNetwork conflict from being disconnected from host network or connected to host network.
|
||||
ErrConflictHostNetwork validationError = "container cannot be disconnected from host network or connected to host network"
|
||||
// ErrConflictNoNetwork conflict between private and other networks
|
||||
ErrConflictNoNetwork validationError = "container cannot be connected to multiple networks with one of the networks in private (none) mode"
|
||||
// ErrConflictNetworkAndDNS conflict between --dns and the network mode
|
||||
ErrConflictNetworkAndDNS validationError = "conflicting options: dns and the network mode"
|
||||
// ErrConflictNetworkHostname conflict between the hostname and the network mode
|
||||
ErrConflictNetworkHostname validationError = "conflicting options: hostname and the network mode"
|
||||
// ErrConflictHostNetworkAndLinks conflict between --net=host and links
|
||||
ErrConflictHostNetworkAndLinks validationError = "conflicting options: host type networking can't be used with links. This would result in undefined behavior"
|
||||
// ErrConflictContainerNetworkAndMac conflict between the mac address and the network mode
|
||||
ErrConflictContainerNetworkAndMac validationError = "conflicting options: mac-address and the network mode"
|
||||
// ErrConflictNetworkHosts conflict between add-host and the network mode
|
||||
ErrConflictNetworkHosts validationError = "conflicting options: custom host-to-IP mapping and the network mode"
|
||||
// ErrConflictNetworkPublishPorts conflict between the publish options and the network mode
|
||||
ErrConflictNetworkPublishPorts validationError = "conflicting options: port publishing and the container type network mode"
|
||||
// ErrConflictNetworkExposePorts conflict between the expose option and the network mode
|
||||
ErrConflictNetworkExposePorts validationError = "conflicting options: port exposing and the container type network mode"
|
||||
// ErrUnsupportedNetworkAndIP conflict between network mode and requested ip address
|
||||
ErrUnsupportedNetworkAndIP validationError = "user specified IP address is supported on user defined networks only"
|
||||
// ErrUnsupportedNetworkNoSubnetAndIP conflict between network with no configured subnet and requested ip address
|
||||
ErrUnsupportedNetworkNoSubnetAndIP validationError = "user specified IP address is supported only when connecting to networks with user configured subnets"
|
||||
// ErrUnsupportedNetworkAndAlias conflict between network mode and alias
|
||||
ErrUnsupportedNetworkAndAlias validationError = "network-scoped alias is supported only for containers in user defined networks"
|
||||
// ErrConflictUTSHostname conflict between the hostname and the UTS mode
|
||||
ErrConflictUTSHostname validationError = "conflicting options: hostname and the UTS mode"
|
||||
)
|
||||
|
||||
type validationError string
|
||||
|
||||
func (e validationError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func (e validationError) InvalidParameter() {}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
package runconfig // import "github.com/docker/docker/runconfig"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
)
|
||||
|
||||
// DecodeHostConfig creates a HostConfig based on the specified Reader.
|
||||
// It assumes the content of the reader will be JSON, and decodes it.
|
||||
func decodeHostConfig(src io.Reader) (*container.HostConfig, error) {
|
||||
decoder := json.NewDecoder(src)
|
||||
|
||||
var w ContainerConfigWrapper
|
||||
if err := decoder.Decode(&w); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hc := w.getHostConfig()
|
||||
return hc, nil
|
||||
}
|
||||
|
||||
// SetDefaultNetModeIfBlank changes the NetworkMode in a HostConfig structure
|
||||
// to default if it is not populated. This ensures backwards compatibility after
|
||||
// the validation of the network mode was moved from the docker CLI to the
|
||||
// docker daemon.
|
||||
func SetDefaultNetModeIfBlank(hc *container.HostConfig) {
|
||||
if hc != nil {
|
||||
if hc.NetworkMode == container.NetworkMode("") {
|
||||
hc.NetworkMode = container.NetworkMode("default")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validateNetContainerMode ensures that the various combinations of requested
|
||||
// network settings wrt container mode are valid.
|
||||
func validateNetContainerMode(c *container.Config, hc *container.HostConfig) error {
|
||||
// We may not be passed a host config, such as in the case of docker commit
|
||||
if hc == nil {
|
||||
return nil
|
||||
}
|
||||
parts := strings.Split(string(hc.NetworkMode), ":")
|
||||
if parts[0] == "container" {
|
||||
if len(parts) < 2 || parts[1] == "" {
|
||||
return validationError("Invalid network mode: invalid container format container:<name|id>")
|
||||
}
|
||||
}
|
||||
|
||||
if hc.NetworkMode.IsContainer() && c.Hostname != "" {
|
||||
return ErrConflictNetworkHostname
|
||||
}
|
||||
|
||||
if hc.NetworkMode.IsContainer() && len(hc.Links) > 0 {
|
||||
return ErrConflictContainerNetworkAndLinks
|
||||
}
|
||||
|
||||
if hc.NetworkMode.IsContainer() && len(hc.DNS) > 0 {
|
||||
return ErrConflictNetworkAndDNS
|
||||
}
|
||||
|
||||
if hc.NetworkMode.IsContainer() && len(hc.ExtraHosts) > 0 {
|
||||
return ErrConflictNetworkHosts
|
||||
}
|
||||
|
||||
if (hc.NetworkMode.IsContainer() || hc.NetworkMode.IsHost()) && c.MacAddress != "" {
|
||||
return ErrConflictContainerNetworkAndMac
|
||||
}
|
||||
|
||||
if hc.NetworkMode.IsContainer() && (len(hc.PortBindings) > 0 || hc.PublishAllPorts) {
|
||||
return ErrConflictNetworkPublishPorts
|
||||
}
|
||||
|
||||
if hc.NetworkMode.IsContainer() && len(c.ExposedPorts) > 0 {
|
||||
return ErrConflictNetworkExposePorts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
-110
@@ -1,110 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package runconfig // import "github.com/docker/docker/runconfig"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/sysinfo"
|
||||
)
|
||||
|
||||
// DefaultDaemonNetworkMode returns the default network stack the daemon should
|
||||
// use.
|
||||
func DefaultDaemonNetworkMode() container.NetworkMode {
|
||||
return container.NetworkMode("bridge")
|
||||
}
|
||||
|
||||
// IsPreDefinedNetwork indicates if a network is predefined by the daemon
|
||||
func IsPreDefinedNetwork(network string) bool {
|
||||
n := container.NetworkMode(network)
|
||||
return n.IsBridge() || n.IsHost() || n.IsNone() || n.IsDefault()
|
||||
}
|
||||
|
||||
// validateNetMode ensures that the various combinations of requested
|
||||
// network settings are valid.
|
||||
func validateNetMode(c *container.Config, hc *container.HostConfig) error {
|
||||
// We may not be passed a host config, such as in the case of docker commit
|
||||
if hc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := validateNetContainerMode(c, hc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hc.UTSMode.IsHost() && c.Hostname != "" {
|
||||
return ErrConflictUTSHostname
|
||||
}
|
||||
|
||||
if hc.NetworkMode.IsHost() && len(hc.Links) > 0 {
|
||||
return ErrConflictHostNetworkAndLinks
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateIsolation performs platform specific validation of
|
||||
// isolation in the hostconfig structure. Linux only supports "default"
|
||||
// which is LXC container isolation
|
||||
func validateIsolation(hc *container.HostConfig) error {
|
||||
// We may not be passed a host config, such as in the case of docker commit
|
||||
if hc == nil {
|
||||
return nil
|
||||
}
|
||||
if !hc.Isolation.IsValid() {
|
||||
return fmt.Errorf("Invalid isolation: %q - %s only supports 'default'", hc.Isolation, runtime.GOOS)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateQoS performs platform specific validation of the QoS settings
|
||||
func validateQoS(hc *container.HostConfig) error {
|
||||
// We may not be passed a host config, such as in the case of docker commit
|
||||
if hc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if hc.IOMaximumBandwidth != 0 {
|
||||
return fmt.Errorf("Invalid QoS settings: %s does not support configuration of maximum bandwidth", runtime.GOOS)
|
||||
}
|
||||
|
||||
if hc.IOMaximumIOps != 0 {
|
||||
return fmt.Errorf("Invalid QoS settings: %s does not support configuration of maximum IOPs", runtime.GOOS)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateResources performs platform specific validation of the resource settings
|
||||
// cpu-rt-runtime and cpu-rt-period can not be greater than their parent, cpu-rt-runtime requires sys_nice
|
||||
func validateResources(hc *container.HostConfig, si *sysinfo.SysInfo) error {
|
||||
// We may not be passed a host config, such as in the case of docker commit
|
||||
if hc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if hc.Resources.CPURealtimePeriod > 0 && !si.CPURealtimePeriod {
|
||||
return fmt.Errorf("Your kernel does not support cgroup cpu real-time period")
|
||||
}
|
||||
|
||||
if hc.Resources.CPURealtimeRuntime > 0 && !si.CPURealtimeRuntime {
|
||||
return fmt.Errorf("Your kernel does not support cgroup cpu real-time runtime")
|
||||
}
|
||||
|
||||
if hc.Resources.CPURealtimePeriod != 0 && hc.Resources.CPURealtimeRuntime != 0 && hc.Resources.CPURealtimeRuntime > hc.Resources.CPURealtimePeriod {
|
||||
return fmt.Errorf("cpu real-time runtime cannot be higher than cpu real-time period")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validatePrivileged performs platform specific validation of the Privileged setting
|
||||
func validatePrivileged(hc *container.HostConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateReadonlyRootfs performs platform specific validation of the ReadonlyRootfs setting
|
||||
func validateReadonlyRootfs(hc *container.HostConfig) error {
|
||||
return nil
|
||||
}
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
package runconfig // import "github.com/docker/docker/runconfig"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/sysinfo"
|
||||
)
|
||||
|
||||
// DefaultDaemonNetworkMode returns the default network stack the daemon should
|
||||
// use.
|
||||
func DefaultDaemonNetworkMode() container.NetworkMode {
|
||||
return container.NetworkMode("nat")
|
||||
}
|
||||
|
||||
// IsPreDefinedNetwork indicates if a network is predefined by the daemon
|
||||
func IsPreDefinedNetwork(network string) bool {
|
||||
return !container.NetworkMode(network).IsUserDefined()
|
||||
}
|
||||
|
||||
// validateNetMode ensures that the various combinations of requested
|
||||
// network settings are valid.
|
||||
func validateNetMode(c *container.Config, hc *container.HostConfig) error {
|
||||
if hc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := validateNetContainerMode(c, hc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hc.NetworkMode.IsContainer() && hc.Isolation.IsHyperV() {
|
||||
return fmt.Errorf("Using the network stack of another container is not supported while using Hyper-V Containers")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateIsolation performs platform specific validation of the
|
||||
// isolation in the hostconfig structure. Windows supports 'default' (or
|
||||
// blank), 'process', or 'hyperv'.
|
||||
func validateIsolation(hc *container.HostConfig) error {
|
||||
// We may not be passed a host config, such as in the case of docker commit
|
||||
if hc == nil {
|
||||
return nil
|
||||
}
|
||||
if !hc.Isolation.IsValid() {
|
||||
return fmt.Errorf("Invalid isolation: %q. Windows supports 'default', 'process', or 'hyperv'", hc.Isolation)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateQoS performs platform specific validation of the Qos settings
|
||||
func validateQoS(hc *container.HostConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateResources performs platform specific validation of the resource settings
|
||||
func validateResources(hc *container.HostConfig, si *sysinfo.SysInfo) error {
|
||||
// We may not be passed a host config, such as in the case of docker commit
|
||||
if hc == nil {
|
||||
return nil
|
||||
}
|
||||
if hc.Resources.CPURealtimePeriod != 0 {
|
||||
return fmt.Errorf("Windows does not support CPU real-time period")
|
||||
}
|
||||
if hc.Resources.CPURealtimeRuntime != 0 {
|
||||
return fmt.Errorf("Windows does not support CPU real-time runtime")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validatePrivileged performs platform specific validation of the Privileged setting
|
||||
func validatePrivileged(hc *container.HostConfig) error {
|
||||
// We may not be passed a host config, such as in the case of docker commit
|
||||
if hc == nil {
|
||||
return nil
|
||||
}
|
||||
if hc.Privileged {
|
||||
return fmt.Errorf("Windows does not support privileged mode")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateReadonlyRootfs performs platform specific validation of the ReadonlyRootfs setting
|
||||
func validateReadonlyRootfs(hc *container.HostConfig) error {
|
||||
// We may not be passed a host config, such as in the case of docker commit
|
||||
if hc == nil {
|
||||
return nil
|
||||
}
|
||||
if hc.ReadonlyRootfs {
|
||||
return fmt.Errorf("Windows does not support root filesystem in read-only mode")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+1
-1
@@ -113,7 +113,7 @@ func SplitProtoPort(rawPort string) (string, string) {
|
||||
}
|
||||
|
||||
func validateProto(proto string) bool {
|
||||
for _, availableProto := range []string{"tcp", "udp"} {
|
||||
for _, availableProto := range []string{"tcp", "udp", "sctp"} {
|
||||
if availableProto == proto {
|
||||
return true
|
||||
}
|
||||
|
||||
-1
@@ -4,7 +4,6 @@ package tlsconfig
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
|
||||
)
|
||||
|
||||
// SystemCertPool returns an new empty cert pool,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user