Skip to content

Commit

Permalink
Merge pull request #6933 from mikesplain/automated-cherry-pick-of-#6890-
Browse files Browse the repository at this point in the history
#6893-#6898-origin-release-1.13

Automated cherry pick of #6890: Fix machine types with klog #6893: /etc/hosts (gossip): Stronger logic #6898: Add i3en
  • Loading branch information
k8s-ci-robot committed May 15, 2019
2 parents e908733 + 5d5c43b commit 4596d04
Show file tree
Hide file tree
Showing 6 changed files with 327 additions and 11 deletions.
1 change: 0 additions & 1 deletion hack/machine_types/machine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ func main() {
flag.StringVar(&outputPath, "out", outputPath, "file to write")

flag.Parse()
flag.Lookup("logtostderr").Value.Set("true")

if err := run(); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
Expand Down
14 changes: 14 additions & 0 deletions hack/machine_types/vpc_ip_resource_limit.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ var InstanceENIsAvailable = map[string]int{
"i3.8xlarge": 8,
"i3.16xlarge": 15,
"i3.metal": 15,
"i3en.large": 3,
"i3en.xlarge": 4,
"i3en.2xlarge": 4,
"i3en.3xlarge": 4,
"i3en.6xlarge": 8,
"i3en.12xlarge": 8,
"i3en.24xlarge": 15,
"m1.small": 2,
"m1.medium": 2,
"m1.large": 3,
Expand Down Expand Up @@ -296,6 +303,13 @@ var InstanceIPsAvailable = map[string]int64{
"i3.8xlarge": 30,
"i3.16xlarge": 50,
"i3.metal": 50,
"i3en.large": 10,
"i3en.xlarge": 15,
"i3en.2xlarge": 15,
"i3en.3xlarge": 15,
"i3en.6xlarge": 30,
"i3en.12xlarge": 30,
"i3en.24xlarge": 50,
"m1.small": 4,
"m1.medium": 6,
"m1.large": 10,
Expand Down
9 changes: 8 additions & 1 deletion protokube/pkg/gossip/dns/hosts/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
Expand All @@ -7,3 +7,10 @@ go_library(
visibility = ["//visibility:public"],
deps = ["//vendor/k8s.io/klog:go_default_library"],
)

go_test(
name = "go_default_test",
srcs = ["hosts_test.go"],
embed = [":go_default_library"],
deps = ["//pkg/diff:go_default_library"],
)
84 changes: 75 additions & 9 deletions protokube/pkg/gossip/dns/hosts/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,30 @@ limitations under the License.
package hosts

import (
"bytes"
"fmt"
"io/ioutil"
math_rand "math/rand"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"time"

"k8s.io/klog"
)

const GUARD_BEGIN = "# Begin host entries managed by kops - do not edit"
const GUARD_END = "# End host entries managed by kops"

var hostsFileMutex sync.Mutex

func UpdateHostsFileWithRecords(p string, addrToHosts map[string][]string) error {
// For safety / sanity, we avoid concurrent updates from one process
hostsFileMutex.Lock()
defer hostsFileMutex.Unlock()

stat, err := os.Stat(p)
if err != nil {
return fmt.Errorf("error getting file status of %q: %v", p, err)
Expand All @@ -42,19 +52,28 @@ func UpdateHostsFileWithRecords(p string, addrToHosts map[string][]string) error
}

var out []string
depth := 0
inGuardBlock := false
for _, line := range strings.Split(string(data), "\n") {
k := strings.TrimSpace(line)
if k == GUARD_BEGIN {
depth++
if inGuardBlock {
klog.Warningf("/etc/hosts guard-block begin seen while in guard block; will ignore")
}
inGuardBlock = true
}

if depth <= 0 {
if !inGuardBlock {
out = append(out, line)
}

if k == GUARD_END {
depth--
if !inGuardBlock {
klog.Warningf("/etc/hosts guard-block end seen before guard-block start; will ignore end")
// Don't output the line
out = out[:len(out)-1]
}

inGuardBlock = false
}
}

Expand All @@ -72,25 +91,72 @@ func UpdateHostsFileWithRecords(p string, addrToHosts map[string][]string) error
}
out = append(out, "")

out = append(out, GUARD_BEGIN)
var block []string
for addr, hosts := range addrToHosts {
sort.Strings(hosts)
out = append(out, addr+"\t"+strings.Join(hosts, " "))
block = append(block, addr+"\t"+strings.Join(hosts, " "))
}
// Sort into a consistent order to minimize updates
sort.Strings(block)

out = append(out, GUARD_BEGIN)
out = append(out, block...)
out = append(out, GUARD_END)
out = append(out, "")

updated := []byte(strings.Join(out, "\n"))

if bytes.Equal(updated, data) {
klog.V(2).Infof("skipping update of unchanged /etc/hosts")
return nil
}

// 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 {
if err := pseudoAtomicWrite(p, updated, stat.Mode()); err != nil {
return fmt.Errorf("error writing file %q: %v", p, err)
}

return nil
}

// 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);
// instead we write the file, pause, re-read and see if anyone else
// wrote in the meantime; if so we rewrite again. By pausing for a
// random amount of time, eventually we'll win the write race and
// exit. This doesn't guarantee fairness, but it should mean that the
// end-result is not malformed (i.e. partial writes).
func pseudoAtomicWrite(p string, b []byte, mode os.FileMode) error {
attempt := 0
for {
attempt++
if attempt > 10 {
return fmt.Errorf("failed to consistently write file %q - too many retries", p)
}

if err := ioutil.WriteFile(p, b, mode); err != nil {
klog.Warningf("error writing file %q: %v", p, err)
continue
}

n := 1 + math_rand.Intn(20)
time.Sleep(time.Duration(n) * time.Millisecond)

contents, err := ioutil.ReadFile(p)
if err != nil {
klog.Warningf("error re-reading file %q: %v", p, err)
continue
}

if bytes.Equal(contents, b) {
return nil
}

klog.Warningf("detected concurrent write to file %q, will retry", p)
}
}

func atomicWriteFile(filename string, data []byte, perm os.FileMode) error {
dir := filepath.Dir(filename)

Expand Down
159 changes: 159 additions & 0 deletions protokube/pkg/gossip/dns/hosts/hosts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
Copyright 2019 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 (
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"

"k8s.io/kops/pkg/diff"
)

func TestRemovesDuplicateGuardedBlocks(t *testing.T) {
in := `
foo 10.2.3.4
# Begin host entries managed by etcd-manager[etcd] - do not edit
# End host entries managed by etcd-manager[etcd]
# Begin host entries managed by etcd-manager[etcd] - do not edit
# End host entries managed by etcd-manager[etcd]
# Begin host entries managed by kops - do not edit
# End host entries managed by kops
# Begin host entries managed by kops - do not edit
# End host entries managed by kops
# Begin host entries managed by kops - do not edit
# End host entries managed by kops
# Begin host entries managed by kops - do not edit
# End host entries managed by kops
# Begin host entries managed by kops - do not edit
# End host entries managed by kops
# Begin host entries managed by kops - do not edit
# End host entries managed by kops
# Begin host entries managed by kops - do not edit
# End host entries managed by kops
# Begin host entries managed by kops - do not edit
# End host entries managed by kops
`

expected := `
foo 10.2.3.4
# Begin host entries managed by etcd-manager[etcd] - do not edit
# End host entries managed by etcd-manager[etcd]
# Begin host entries managed by etcd-manager[etcd] - do not edit
# End host entries managed by etcd-manager[etcd]
# Begin host entries managed by kops - do not edit
a\t10.0.1.1 10.0.1.2
b\t10.0.2.1
c\t
# End host entries managed by kops
`

runTest(t, in, expected)
}

func TestRecoversFromBadNesting(t *testing.T) {
in := `
foo 10.2.3.4
# End host entries managed by kops
# Begin host entries managed by kops - do not edit
# Begin host entries managed by kops - do not edit
# End host entries managed by kops
# End host entries managed by kops
# End host entries managed by kops
# Begin host entries managed by kops - do not edit
# End host entries managed by kops
# Begin host entries managed by kops - do not edit
# End host entries managed by kops
# Begin host entries managed by kops - do not edit
# End host entries managed by kops
# Begin host entries managed by kops - do not edit
# Begin host entries managed by kops - do not edit
# Begin host entries managed by kops - do not edit
# Begin host entries managed by kops - do not edit
# End host entries managed by kops
# Begin host entries managed by kops - do not edit
# End host entries managed by kops
bar 10.1.2.3
`

expected := `
foo 10.2.3.4
bar 10.1.2.3
# Begin host entries managed by kops - do not edit
a\t10.0.1.1 10.0.1.2
b\t10.0.2.1
c\t
# End host entries managed by kops
`

runTest(t, in, expected)
}

func runTest(t *testing.T, in string, expected string) {
expected = strings.Replace(expected, "\\t", "\t", -1)

dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("error creating temp dir: %v", err)
}
defer func() {
err := os.RemoveAll(dir)
if err != nil {
t.Errorf("failed to remove temp dir %q: %v", dir, err)
}
}()

p := filepath.Join(dir, "hosts")
addrToHosts := map[string][]string{
"a": {"10.0.1.2", "10.0.1.1"},
"b": {"10.0.2.1"},
"c": {},
}

if err := ioutil.WriteFile(p, []byte(in), 0755); err != nil {
t.Fatalf("error writing hosts file: %v", err)
}

// We run it repeatedly to make sure we don't change it accidentally
for i := 0; i < 100; i++ {
if err := UpdateHostsFileWithRecords(p, addrToHosts); err != nil {
t.Fatalf("error updating hosts file: %v", err)
}

b, err := ioutil.ReadFile(p)
if err != nil {
t.Fatalf("error reading output file: %v", err)
}

actual := string(b)
if actual != expected {
diffString := diff.FormatDiff(expected, actual)
t.Logf("diff:\n%s\n", diffString)
t.Errorf("unexpected output. expected=%q, actual=%q", expected, actual)
}
}
}
Loading

0 comments on commit 4596d04

Please sign in to comment.