@@ -20,17 +20,28 @@ import (
2020
2121var (
2222 // procCGroupPattern is a regular expression that parses the entries in /proc/self/cgroup
23- procCGroupPattern = regexp .MustCompile (`\d+:([a-z_,]+):/.*/(docker-|)([a-z0-9]+).*` )
23+ procCGroupPattern = regexp .MustCompile (`\d+:([a-z_,]+):/.*/(\w+-|)([a-z0-9]+).*` )
24+
25+ // pidPattern is a regexp to match the pid provided in /proc/1/sched inside a container
26+ pidPattern = regexp .MustCompile (`.*?\((\d+)` )
27+
28+ // resolvConfigPattern is a regexp to match the /etc/resolv.conf mount info in /proc/1/mountinfo
29+ resolvConfPattern = regexp .MustCompile (`.*?(/.*?) /etc/resolv\.conf ` )
2430)
2531
2632// readNetClsCGroup parses /proc/self/cgroup in order to determine the container id that can be used
27- // the network namespace that this process is running on.
28- func readNetClsCGroup (reader io.Reader ) string {
29- cgroups := make (map [string ]string )
33+ // the network namespace that this process is running on, it returns the cgroup and container type
34+ // (docker vs crio).
35+ func readNetClsCGroup (reader io.Reader ) (string , string ) {
36+
37+ containerType := "docker"
3038
39+ cgroups := make (map [string ]string )
3140 scanner := bufio .NewScanner (reader )
3241 for scanner .Scan () {
3342 if match := procCGroupPattern .FindStringSubmatch (scanner .Text ()); match != nil {
43+ containerType = match [2 ]
44+
3445 list := strings .Split (match [1 ], "," )
3546 containerId := match [3 ]
3647 if len (list ) > 0 {
@@ -46,26 +57,89 @@ func readNetClsCGroup(reader io.Reader) string {
4657 names := []string {"net_cls" , "cpu" }
4758 for _ , group := range names {
4859 if value , ok := cgroups [group ]; ok {
49- return value
60+ return value , containerType
5061 }
5162 }
5263
53- return ""
64+ return "" , containerType
65+ }
66+
67+ // readResolveConfHostPath determines the path to the resolv.conf file for this
68+ // container, as it exists on the host machine. (/etc/resolv.conf is mounted
69+ // into the container from the host path).
70+ func readResolvConfHostPath () (string , error ) {
71+ // find the /etc/resolv.conf host path based on what is mounted into this
72+ // container.
73+ resolvConf , err := os .Open ("/proc/1/mountinfo" )
74+ if err != nil {
75+ return "" , err
76+ }
77+ defer resolvConf .Close ()
78+
79+ scanner := bufio .NewScanner (resolvConf )
80+ for scanner .Scan () {
81+ if match := resolvConfPattern .FindStringSubmatch (scanner .Text ()); match != nil {
82+ // don't allow the path to contain quote, comma, or colon as these could be used
83+ // to escape the bindmount argument we are passing to docker and mount other
84+ // filepaths from the host.
85+ if strings .ContainsAny (match [1 ], "\" ,:" ) {
86+ return "" , fmt .Errorf ("/etc/resolv.conf path from host contains invalid characters (',', '\" ', or ':')" )
87+ }
88+ return match [1 ], nil
89+ }
90+ }
91+ return "" , fmt .Errorf ("Unable to determine /etc/resolv.conf hostpath" )
5492}
5593
56- // getDockerNetworkMode determines whether the builder is running as a container
94+ // readPid determines the actual host pid of the pid 1 process in this container
95+ func readPid () (string , error ) {
96+ // get pid from /proc/1/sched , e.g.: "java (8151, #threads: 53)"
97+ pidFile , err := os .Open ("/proc/1/sched" )
98+ if err != nil {
99+ return "" , err
100+ }
101+ defer pidFile .Close ()
102+
103+ pidLine , err := bufio .NewReader (pidFile ).ReadString ('\n' )
104+ if err != nil {
105+ return "" , err
106+ }
107+ match := pidPattern .FindStringSubmatch (pidLine )
108+ if match == nil {
109+ return "" , fmt .Errorf ("Unable to determine pid from %s" , pidLine )
110+ }
111+ return match [1 ], nil
112+ }
113+
114+ // getContainerNetworkMode determines whether the builder is running as a container
57115// by examining /proc/self/cgroup. This context is then passed to source-to-image.
58- func getDockerNetworkMode () s2iapi.DockerNetworkMode {
116+ // It returns a suitable argument for NetworkMode. If the container platform is
117+ // CRI-O, it also returns a path for /etc/resolv.conf, suitable for bindmounting.
118+ func getContainerNetworkMode () (string , string , error ) {
59119 file , err := os .Open ("/proc/self/cgroup" )
60120 if err != nil {
61- return ""
121+ return "" , "" , err
62122 }
63123 defer file .Close ()
64124
65- if id := readNetClsCGroup (file ); id != "" {
66- return s2iapi .NewDockerNetworkModeContainer (id )
125+ resolvConfHostPath , err := readResolvConfHostPath ()
126+ if err != nil {
127+ return "" , "" , err
128+ }
129+
130+ if id , containerType := readNetClsCGroup (file ); id != "" {
131+ glog .V (5 ).Infof ("container type=%s" , containerType )
132+ if containerType != "crio-" {
133+ return s2iapi .DockerNetworkModeContainerPrefix + id , resolvConfHostPath , nil
134+ }
135+
136+ pid , err := readPid ()
137+ if err != nil {
138+ return "" , "" , err
139+ }
140+ return fmt .Sprintf ("netns:/proc/%s/ns/net" , pid ), fmt .Sprintf ("/var/run" + resolvConfHostPath ), nil
67141 }
68- return ""
142+ return "" , "" , nil
69143}
70144
71145// GetCGroupLimits returns a struct populated with cgroup limit values gathered
0 commit comments