-
Notifications
You must be signed in to change notification settings - Fork 881
*: fill resolv.conf from command line --dns #2040
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,6 +56,9 @@ End the image arguments with a lone "---" to resume argument parsing.`, | |
| flagInheritEnv bool | ||
| flagExplicitEnv envMap | ||
| flagInteractive bool | ||
| flagDNS flagStringList | ||
| flagDNSSearch flagStringList | ||
| flagDNSOpt flagStringList | ||
| flagNoOverlay bool | ||
| flagStoreOnly bool | ||
| flagNoStore bool | ||
|
|
@@ -76,6 +79,9 @@ func init() { | |
| cmdRun.Flags().BoolVar(&flagPrivateUsers, "private-users", false, "Run within user namespaces (experimental).") | ||
| cmdRun.Flags().Var(&flagExplicitEnv, "set-env", "an environment variable to set for apps in the form name=value") | ||
| cmdRun.Flags().BoolVar(&flagInteractive, "interactive", false, "run pod interactively. If true, only one image may be supplied.") | ||
| cmdRun.Flags().Var(&flagDNS, "dns", "name servers to write in /etc/resolv.conf") | ||
| cmdRun.Flags().Var(&flagDNSSearch, "dns-search", "DNS search domains to write in /etc/resolv.conf") | ||
| cmdRun.Flags().Var(&flagDNSOpt, "dns-opt", "DNS options to write in /etc/resolv.conf") | ||
| cmdRun.Flags().BoolVar(&flagStoreOnly, "store-only", false, "use only available images in the store (do not discover or download from remote URLs)") | ||
| cmdRun.Flags().BoolVar(&flagNoStore, "no-store", false, "fetch images ignoring the local store") | ||
| cmdRun.Flags().StringVar(&flagPodManifest, "pod-manifest", "", "the path to the pod manifest. If it's non-empty, then only '--net', '--no-overlay' and '--interactive' will have effects") | ||
|
|
@@ -91,6 +97,9 @@ func init() { | |
| cmdRun.Flags().Var((*appCPULimit)(&rktApps), "cpu", "cpu limit for the preceding image (example: '--cpu=500m')") | ||
|
|
||
| flagPorts = portList{} | ||
| flagDNS = flagStringList{} | ||
| flagDNSSearch = flagStringList{} | ||
| flagDNSOpt = flagStringList{} | ||
|
|
||
| // Disable interspersed flags to stop parsing after the first non flag | ||
| // argument. All the subsequent parsing will be done by parseApps. | ||
|
|
@@ -271,6 +280,9 @@ func runRun(cmd *cobra.Command, args []string) (exit int) { | |
| Net: flagNet, | ||
| LockFd: lfd, | ||
| Interactive: flagInteractive, | ||
| DNS: flagDNS, | ||
| DNSSearch: flagDNSSearch, | ||
| DNSOpt: flagDNSOpt, | ||
| MDSRegister: flagMDSRegister, | ||
| LocalConfig: globalFlags.LocalConfigDir, | ||
| RktGid: rktgid, | ||
|
|
@@ -328,6 +340,22 @@ func (pl *portList) Type() string { | |
| return "portList" | ||
| } | ||
|
|
||
| // flagStringList implements the flag.Value interface to contain a set of strings | ||
| type flagStringList []string | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @krnowak where is the preferred place to add this new type? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we have a good place to put new types used in several, but not all commands. But often There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, this is quite generic, so rkt.go it is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe I wrote a similar thing somewhere before... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's here - https://github.com/coreos/rkt/blob/master/tools/common/util.go#L24 Maybe we should move this thing elsewhere. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. However, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just keep it here then. We can later move this stuff in to a separate go file. |
||
|
|
||
| func (dns *flagStringList) Set(s string) error { | ||
| *dns = append(*dns, s) | ||
| return nil | ||
| } | ||
|
|
||
| func (dns *flagStringList) String() string { | ||
| return strings.Join(*dns, " ") | ||
| } | ||
|
|
||
| func (dns *flagStringList) Type() string { | ||
| return "flagStringList" | ||
| } | ||
|
|
||
| // envMap implements the flag.Value interface to contain a set of name=value mappings | ||
| type envMap struct { | ||
| mapping map[string]string | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,7 @@ import ( | |
| "fmt" | ||
| "io/ioutil" | ||
| "log" | ||
| "net" | ||
| "os" | ||
| "path" | ||
| "path/filepath" | ||
|
|
@@ -87,6 +88,9 @@ type RunConfig struct { | |
| Apps schema.AppList // applications (prepare gets them via Apps) | ||
| LocalConfig string // Path to local configuration | ||
| RktGid int // group id of the 'rkt' group, -1 if there's no rkt group. | ||
| DNS []string // DNS name servers to write in /etc/resolv.conf | ||
| DNSSearch []string // DNS search domains to write in /etc/resolv.conf | ||
| DNSOpt []string // DNS options to write in /etc/resolv.conf | ||
| } | ||
|
|
||
| // configuration shared by both Run and Prepare | ||
|
|
@@ -379,6 +383,32 @@ func preparedWithPrivateUsers(dir string) (string, error) { | |
| return string(bytes), nil | ||
| } | ||
|
|
||
| func addResolvConf(cfg RunConfig, rootfs string) { | ||
| content := "# Generated by rkt\n\n" | ||
| if len(cfg.DNSSearch) > 0 { | ||
| content += fmt.Sprintf("search %s\n", strings.Join(cfg.DNSSearch, " ")) | ||
| } | ||
| for _, server := range cfg.DNS { | ||
| // skip empty entries | ||
| if server == "" { | ||
| continue | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be a good idea to check if server is a valid IP address here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
| // comment invalid entries | ||
| if net.ParseIP(server) == nil { | ||
| content += "# " | ||
| } | ||
| content += "nameserver " + server + "\n" | ||
| } | ||
| if len(cfg.DNSOpt) > 0 { | ||
| content += fmt.Sprintf("options %s\n", strings.Join(cfg.DNSOpt, " ")) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate DNS options maybe? There are only a few of those, validating will give better UX with a constant overhead There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But rkt would need to keep the list of options in sync with the glibc options when new options are added. It does not happen that often but still. The last one to be added seems to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, I find this very restricting as it depends on the implementation. I would be very annoyed if I were to develop a new DNS option and couldn't test it in rkt. |
||
| } | ||
| content += "\n" | ||
|
|
||
| if err := ioutil.WriteFile(filepath.Join(rootfs, "etc/rkt-resolv.conf"), []byte(content), 0644); err != nil { | ||
| log.Fatalf("error writing /etc/rkt-resolv.conf: %v\n", err) | ||
| } | ||
| } | ||
|
|
||
| // Run mounts the right overlay filesystems and actually runs the prepared | ||
| // pod by exec()ing the stage1 init inside the pod filesystem. | ||
| func Run(cfg RunConfig, dir string, dataDir string) { | ||
|
|
@@ -406,6 +436,10 @@ func Run(cfg RunConfig, dir string, dataDir string) { | |
|
|
||
| destRootfs := common.Stage1RootfsPath(dir) | ||
|
|
||
| if len(cfg.DNS) > 0 || len(cfg.DNSSearch) > 0 || len(cfg.DNSOpt) > 0 { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. libc does not recognize more than 3 DNS servers. We should error out if more than 3 are supplied. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not our responsibility to do this nor can't we be certain that the application inside uses libc's resolver. |
||
| addResolvConf(cfg, destRootfs) | ||
| } | ||
|
|
||
| if err := os.Setenv(common.EnvLockFd, fmt.Sprintf("%v", cfg.LockFd)); err != nil { | ||
| log.Fatalf("setting lock fd environment: %v", err) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -147,12 +147,15 @@ int main(int argc, char *argv[]) | |
| "/dev/console", | ||
| NULL | ||
| }; | ||
| static const mount_point mount_table[] = { | ||
| static const mount_point dirs_mount_table[] = { | ||
| { "/proc", "/proc", "bind", NULL, MS_BIND|MS_REC }, | ||
| { "/sys", "/sys", "bind", NULL, MS_BIND|MS_REC }, | ||
| { "/dev/shm", "/dev/shm", "bind", NULL, MS_BIND }, | ||
| { "/dev/pts", "/dev/pts", "bind", NULL, MS_BIND }, | ||
| }; | ||
| static const mount_point files_mount_table[] = { | ||
| { "/etc/rkt-resolv.conf", "/etc/resolv.conf", "bind", NULL, MS_BIND }, | ||
| }; | ||
| const char *root; | ||
| int rootfd; | ||
| char to[4096]; | ||
|
|
@@ -243,8 +246,8 @@ int main(int argc, char *argv[]) | |
| } | ||
|
|
||
| /* Bind mount directories */ | ||
| for (i = 0; i < nelems(mount_table); i++) { | ||
| const mount_point *mnt = &mount_table[i]; | ||
| for (i = 0; i < nelems(dirs_mount_table); i++) { | ||
| const mount_point *mnt = &dirs_mount_table[i]; | ||
|
|
||
| exit_if(snprintf(to, sizeof(to), "%s/%s", root, mnt->target) >= sizeof(to), | ||
| "Path too long: \"%s\"", to); | ||
|
|
@@ -253,6 +256,26 @@ int main(int argc, char *argv[]) | |
| "Mounting \"%s\" on \"%s\" failed", mnt->source, to); | ||
| } | ||
|
|
||
| /* Bind mount files, if the source exists */ | ||
| for (i = 0; i < nelems(files_mount_table); i++) { | ||
| const mount_point *mnt = &files_mount_table[i]; | ||
| int fd; | ||
|
|
||
| exit_if(snprintf(to, sizeof(to), "%s/%s", root, mnt->target) >= sizeof(to), | ||
| "Path too long: \"%s\"", to); | ||
| if (access(mnt->source, F_OK) != 0) | ||
| continue; | ||
| if (access(to, F_OK) != 0) { | ||
| pexit_if((fd = creat(to, 0644)) == -1, | ||
| "Cannot create file: \"%s\"", to); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we get more information in case of an error? Is there an implicit ERRNO parser here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, the p of pexit_if means it will use |
||
| pexit_if(close(fd) == -1, | ||
| "Cannot close file: \"%s\"", to); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see above |
||
| } | ||
| pexit_if(mount(mnt->source, to, mnt->type, | ||
| mnt->flags, mnt->options) == -1, | ||
| "Mounting \"%s\" on \"%s\" failed", mnt->source, to); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see above |
||
| } | ||
|
|
||
| /* /dev/ptmx -> /dev/pts/ptmx */ | ||
| exit_if(snprintf(to, sizeof(to), "%s/dev/ptmx", root) >= sizeof(to), | ||
| "Path too long: \"%s\"", to); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| // Copyright 2016 The rkt Authors | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package main | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "testing" | ||
|
|
||
| "github.com/coreos/rkt/tests/testutils" | ||
| ) | ||
|
|
||
| // TestDNS is checking how rkt fills /etc/resolv.conf | ||
| func TestDNS(t *testing.T) { | ||
| imageFile := patchTestACI("rkt-inspect-exit.aci", "--exec=/inspect --print-msg=Hello --read-file") | ||
| defer os.Remove(imageFile) | ||
| ctx := testutils.NewRktRunCtx() | ||
| defer ctx.Cleanup() | ||
|
|
||
| for _, tt := range []struct { | ||
| paramDNS string | ||
| expectedLine string | ||
| }{ | ||
| { | ||
| paramDNS: "", | ||
| expectedLine: "Cannot read file", | ||
| }, | ||
| { | ||
| paramDNS: "--dns=8.8.4.4", | ||
| expectedLine: "nameserver 8.8.4.4", | ||
| }, | ||
| { | ||
| paramDNS: "--dns=8.8.8.8 --dns=8.8.4.4", | ||
| expectedLine: "nameserver 8.8.8.8", | ||
| }, | ||
| { | ||
| paramDNS: "--dns=8.8.8.8 --dns=8.8.4.4 --dns-search=search.com --dns-opt=debug", | ||
| expectedLine: "nameserver 8.8.4.4", | ||
| }, | ||
| { | ||
| paramDNS: "--dns-search=foo.com --dns-search=bar.com", | ||
| expectedLine: "search foo.com bar.com", | ||
| }, | ||
| { | ||
| paramDNS: "--dns-opt=debug --dns-opt=use-vc --dns-opt=rotate", | ||
| expectedLine: "options debug use-vc rotate", | ||
| }, | ||
| } { | ||
|
|
||
| rktCmd := fmt.Sprintf(`%s --insecure-options=image run --set-env=FILE=/etc/resolv.conf %s %s`, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just me being curious: any reason why you didn't put There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did that by mistake at first but it was not working: line 27 has the parameters to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Obviously I would've made the same mistake at first ;-) |
||
| ctx.Cmd(), tt.paramDNS, imageFile) | ||
| t.Logf("%s\n", rktCmd) | ||
| runRktAndCheckOutput(t, rktCmd, tt.expectedLine, false) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These tests should be improved in the future to match against the whole file instead of lines. That way we can ensure that the order is deterministic which might matter for some DNS implementations. |
||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should mention that only 3 nameservers will be considered
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.