From 6d166a41dde9239c98d293f88ca98c1f57d176a3 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Fri, 15 Dec 2017 00:57:41 -0500 Subject: [PATCH] Refactor protokube hosts file into its own package So we can bring this in without our gossip layer This supports an imminent implementation that uses DNS-SD --- hack/.packages | 1 + protokube/pkg/gossip/dns/BUILD.bazel | 1 + protokube/pkg/gossip/dns/hosts.go | 117 +---------------- protokube/pkg/gossip/dns/hosts/BUILD.bazel | 9 ++ protokube/pkg/gossip/dns/hosts/hosts.go | 139 +++++++++++++++++++++ 5 files changed, 152 insertions(+), 115 deletions(-) create mode 100644 protokube/pkg/gossip/dns/hosts/BUILD.bazel create mode 100644 protokube/pkg/gossip/dns/hosts/hosts.go diff --git a/hack/.packages b/hack/.packages index 19e23bd01b..f985c6655c 100644 --- a/hack/.packages +++ b/hack/.packages @@ -102,6 +102,7 @@ k8s.io/kops/protokube/pkg/etcd k8s.io/kops/protokube/pkg/gossip k8s.io/kops/protokube/pkg/gossip/aws k8s.io/kops/protokube/pkg/gossip/dns +k8s.io/kops/protokube/pkg/gossip/dns/hosts k8s.io/kops/protokube/pkg/gossip/dns/provider k8s.io/kops/protokube/pkg/gossip/gce k8s.io/kops/protokube/pkg/gossip/mesh diff --git a/protokube/pkg/gossip/dns/BUILD.bazel b/protokube/pkg/gossip/dns/BUILD.bazel index cb31c09725..6d18537655 100644 --- a/protokube/pkg/gossip/dns/BUILD.bazel +++ b/protokube/pkg/gossip/dns/BUILD.bazel @@ -10,6 +10,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//protokube/pkg/gossip:go_default_library", + "//protokube/pkg/gossip/dns/hosts:go_default_library", "//vendor/github.com/golang/glog:go_default_library", ], ) diff --git a/protokube/pkg/gossip/dns/hosts.go b/protokube/pkg/gossip/dns/hosts.go index f70749f689..eef5d13c61 100644 --- a/protokube/pkg/gossip/dns/hosts.go +++ b/protokube/pkg/gossip/dns/hosts.go @@ -17,19 +17,10 @@ limitations under the License. package dns import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "sort" - "strings" - "github.com/golang/glog" + "k8s.io/kops/protokube/pkg/gossip/dns/hosts" ) -const GUARD_BEGIN = "# Begin host entries managed by kops - do not edit" -const GUARD_END = "# End host entries managed by kops" - // HostsFile stores DNS records into /etc/hosts type HostsFile struct { Path string @@ -58,109 +49,5 @@ func (h *HostsFile) Update(snapshot *DNSViewSnapshot) error { } } - stat, err := os.Stat(h.Path) - if err != nil { - return fmt.Errorf("error getting file status of %q: %v", h.Path, err) - } - - data, err := ioutil.ReadFile(h.Path) - if err != nil { - return fmt.Errorf("error reading file %q: %v", h.Path, err) - } - - var out []string - depth := 0 - for _, line := range strings.Split(string(data), "\n") { - k := strings.TrimSpace(line) - if k == GUARD_BEGIN { - depth++ - } - - if depth <= 0 { - out = append(out, line) - } - - if k == GUARD_END { - depth-- - } - } - - // Ensure a single blank line - for { - if len(out) == 0 { - break - } - - if out[len(out)-1] != "" { - break - } - - out = out[:len(out)-1] - } - out = append(out, "") - - out = append(out, GUARD_BEGIN) - for addr, hosts := range addrToHosts { - sort.Strings(hosts) - out = append(out, addr+"\t"+strings.Join(hosts, " ")) - } - out = append(out, GUARD_END) - out = append(out, "") - - // Note that because we are bind mounting /etc/hosts, we can't do a normal atomic file write - // (where we write a temp file and rename it) - // TODO: We should just hold the file open while we read & write it - err = ioutil.WriteFile(h.Path, []byte(strings.Join(out, "\n")), stat.Mode().Perm()) - if err != nil { - return fmt.Errorf("error writing file %q: %v", h.Path, err) - } - - return nil -} - -func atomicWriteFile(filename string, data []byte, perm os.FileMode) error { - dir := filepath.Dir(filename) - - tempFile, err := ioutil.TempFile(dir, ".tmp"+filepath.Base(filename)) - if err != nil { - return fmt.Errorf("error creating temp file in %q: %v", dir, err) - } - - mustClose := true - mustRemove := true - - defer func() { - if mustClose { - if err := tempFile.Close(); err != nil { - glog.Warningf("error closing temp file: %v", err) - } - } - - if mustRemove { - if err := os.Remove(tempFile.Name()); err != nil { - glog.Warningf("error removing temp file %q: %v", tempFile.Name(), err) - } - } - }() - - if _, err := tempFile.Write(data); err != nil { - return fmt.Errorf("error writing temp file: %v", err) - } - - if err := tempFile.Close(); err != nil { - return fmt.Errorf("error closing temp file: %v", err) - } - - mustClose = false - - if err := os.Chmod(tempFile.Name(), perm); err != nil { - return fmt.Errorf("error changing mode of temp file: %v", err) - } - - if err := os.Rename(tempFile.Name(), filename); err != nil { - return fmt.Errorf("error moving temp file %q to %q: %v", tempFile.Name(), filename, err) - } - - mustRemove = false - return nil + return hosts.UpdateHostsFileWithRecords(h.Path, addrToHosts) } diff --git a/protokube/pkg/gossip/dns/hosts/BUILD.bazel b/protokube/pkg/gossip/dns/hosts/BUILD.bazel new file mode 100644 index 0000000000..b74a901807 --- /dev/null +++ b/protokube/pkg/gossip/dns/hosts/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["hosts.go"], + importpath = "k8s.io/kops/protokube/pkg/gossip/dns/hosts", + visibility = ["//visibility:public"], + deps = ["//vendor/github.com/golang/glog:go_default_library"], +) diff --git a/protokube/pkg/gossip/dns/hosts/hosts.go b/protokube/pkg/gossip/dns/hosts/hosts.go new file mode 100644 index 0000000000..8b1a530d68 --- /dev/null +++ b/protokube/pkg/gossip/dns/hosts/hosts.go @@ -0,0 +1,139 @@ +/* +Copyright 2017 The Kubernetes 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 hosts + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + + "github.com/golang/glog" +) + +const GUARD_BEGIN = "# Begin host entries managed by kops - do not edit" +const GUARD_END = "# End host entries managed by kops" + +func UpdateHostsFileWithRecords(p string, addrToHosts map[string][]string) error { + stat, err := os.Stat(p) + if err != nil { + return fmt.Errorf("error getting file status of %q: %v", p, err) + } + + data, err := ioutil.ReadFile(p) + if err != nil { + return fmt.Errorf("error reading file %q: %v", p, err) + } + + var out []string + depth := 0 + for _, line := range strings.Split(string(data), "\n") { + k := strings.TrimSpace(line) + if k == GUARD_BEGIN { + depth++ + } + + if depth <= 0 { + out = append(out, line) + } + + if k == GUARD_END { + depth-- + } + } + + // Ensure a single blank line + for { + if len(out) == 0 { + break + } + + if out[len(out)-1] != "" { + break + } + + out = out[:len(out)-1] + } + out = append(out, "") + + out = append(out, GUARD_BEGIN) + for addr, hosts := range addrToHosts { + sort.Strings(hosts) + out = append(out, addr+"\t"+strings.Join(hosts, " ")) + } + out = append(out, GUARD_END) + out = append(out, "") + + // Note that because we are bind mounting /etc/hosts, we can't do a normal atomic file write + // (where we write a temp file and rename it) + // TODO: We should just hold the file open while we read & write it + err = ioutil.WriteFile(p, []byte(strings.Join(out, "\n")), stat.Mode().Perm()) + if err != nil { + return fmt.Errorf("error writing file %q: %v", p, err) + } + + return nil +} + +func atomicWriteFile(filename string, data []byte, perm os.FileMode) error { + dir := filepath.Dir(filename) + + tempFile, err := ioutil.TempFile(dir, ".tmp"+filepath.Base(filename)) + if err != nil { + return fmt.Errorf("error creating temp file in %q: %v", dir, err) + } + + mustClose := true + mustRemove := true + + defer func() { + if mustClose { + if err := tempFile.Close(); err != nil { + glog.Warningf("error closing temp file: %v", err) + } + } + + if mustRemove { + if err := os.Remove(tempFile.Name()); err != nil { + glog.Warningf("error removing temp file %q: %v", tempFile.Name(), err) + } + } + }() + + if _, err := tempFile.Write(data); err != nil { + return fmt.Errorf("error writing temp file: %v", err) + } + + if err := tempFile.Close(); err != nil { + return fmt.Errorf("error closing temp file: %v", err) + } + + mustClose = false + + if err := os.Chmod(tempFile.Name(), perm); err != nil { + return fmt.Errorf("error changing mode of temp file: %v", err) + } + + if err := os.Rename(tempFile.Name(), filename); err != nil { + return fmt.Errorf("error moving temp file %q to %q: %v", tempFile.Name(), filename, err) + } + + mustRemove = false + return nil +}