<commit_before>// Copyright (C) 2017 Google Inc.
//
// 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 (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"reflect"
	"time"

	"os"

	"github.com/google/gapid/test/robot/web/client/widgets/grid"
)

type trackInfo struct {
	track                 Item
	packageDisplayToOrder map[string]int
	packageList           []string
	headPackage           string
}

var (
	traceKind  = item{id: "trace"}
	reportKind = item{id: "report"}
	replayKind = item{id: "replay"}

	packageDisplayTemplate = "{{if .information.tag}}{{.information.tag}}" +
		"{{else if and (isUserType .information.type) (.information.cl)}}{{.information.cl}}" +
		"{{else if  .information.uploader}}{{.information.uploader}} - {{.id}}" +
		"{{else}}unknown - {{.id}}" +
		"{{end}}"

	machineDisplayTemplate = "{{if .information.Name}}{{.information.Name}}" +
		"{{else if .information.Configuration.Hardware.Name}}{{.information.Configuration.Hardware.Name}}" +
		"{{else}}{{.information.Configuration.OS}} - {{.information.id.data}}" +
		"{{end}}"

	kindDimension = &dimension{
		name:     "kind",
		enumData: enum{traceKind, replayKind, reportKind},
		valueOf: func(t *task) Item {
			return t.kind
		},
	}
	subjectDimension = &dimension{
		name: "subject",
		valueOf: func(t *task) Item {
			return t.trace.subject
		},
		enumSrc: func() enum {
			return itemGetter("{{.id}}", "{{.Information.APK.package}}", template.FuncMap{})(queryArray("/subjects/"))
		},
	}
	targetDimension = &dimension{
		name: "traceTarget",
		valueOf: func(t *task) Item {
			return t.trace.target
		},
		enumSrc: func() enum {
			return itemGetter("{{.id}}", machineDisplayTemplate, template.FuncMap{})(queryArray("/devices/"))
		},
	}
	hostDimension = &dimension{
		name: "host",
		valueOf: func(t *task) Item {
			return t.host
		},
		enumSrc: func() enum {
			return itemGetter("{{.id}}", machineDisplayTemplate, template.FuncMap{})(queryArray("/devices/"))
		},
	}

	tracks = map[string]*trackInfo{"auto": &trackInfo{
		track: item{
			id:         "",
			display:    "auto",
			underlying: map[string]string{"id": "", "name": "auto", "head": ""},
		},
		packageList: []string{},
		headPackage: "",
	}}
	packageDisplayToOrder = map[string]int{}
	packageToTrack        = map[string]Item{}

	trackDimension = &dimension{
		name: "track",
		valueOf: func(t *task) Item {
			it, ok := packageToTrack[t.pkg.Id()]
			if !ok {
				return tracks["auto"].track
			}
			return it
		},
		enumSrc: func() enum {
			result := itemGetter("{{.id}}", "{{.name}}", template.FuncMap{})(queryArray("/tracks/"))
			for _, it := range result {
				track := it.Underlying().(map[string]interface{})
				trackName, ok := track["name"].(string)
				if !ok {
					continue
				}
				trackHead, ok := track["head"].(string)
				if !ok {
					continue
				}
				tracks[trackName] = &trackInfo{
					track: it,
					packageDisplayToOrder: map[string]int{},
					headPackage:           trackHead,
				}
			}
			result = append(result, tracks["auto"].track)
			return result
		},
	}
	packageDimension = &dimension{
		name: "package",
		valueOf: func(t *task) Item {
			return t.pkg
		},
		enumSrc: func() enum {
			result := itemGetter("{{.id}}", packageDisplayTemplate, template.FuncMap{"isUserType": isUserType})(queryArray("/packages/"))
			itemMap := map[string]Item{}
			childMap := map[string]string{}
			rootPkgs := []string{}
			for _, it := range result {
				pkgRoot := it.Underlying().(map[string]interface{})
				pkgId, ok := pkgRoot["id"].(string)
				itemMap[pkgId] = it
				if !ok {
					continue
				}
				if parentMem, ok := pkgRoot["parent"]; ok {
					parentId, ok := parentMem.(string)
					if !ok {
						continue
					}
					childMap[parentId] = pkgId
				} else {
					rootPkgs = append(rootPkgs, pkgId)
				}
			}
			for _, root := range rootPkgs {
				packageList := []string{}
				packageDisplayToOrder[itemMap[root].Display()] = len(packageDisplayToOrder)
				packageList = append(packageList, root)
				for childId, ok := childMap[root]; ok; childId, ok = childMap[root] {
					packageDisplayToOrder[itemMap[childId].Display()] = len(packageDisplayToOrder)
					// want tracks stored from Root -> Head
					packageList = append(packageList, childId)
					root = childId
				}
				head := root
				foundTrack := false
				// force update the trackDimension to populate tracks
				trackDimension.getEnum()
				for _, destTrack := range tracks {
					if destTrack.headPackage == head {
						destTrack.packageList = packageList
						for _, p := range destTrack.packageList {
							packageToTrack[p] = destTrack.track
						}
						foundTrack = true
						break
					}
				}
				if !foundTrack {
					// We just append all tracks to the "auto" track that didn't match an existing track
					tracks["auto"].packageList = append(tracks["auto"].packageList, packageList...)
				}
			}
			return result
		},
		enumSort: func(a, b string) bool {
			if ao, ok := packageDisplayToOrder[a]; ok {
				if bo, ok := packageDisplayToOrder[b]; ok {
					return ao < bo
				}
			}
			return a < b
		},
	}

	dimensions = []*dimension{kindDimension, subjectDimension, targetDimension, hostDimension, trackDimension, packageDimension}
)

func isUserType(t reflect.Value) bool {
	// cannot currently use build.Type_UserType to check the type need to fix that.
	return t.Kind() == reflect.Float64 && t.Float() == float64(2)
}

func itemGetter(idPattern string, displayPattern string, functions template.FuncMap) func([]interface{}) enum {
	mustTemplate := func(s string) *template.Template {
		return template.Must(template.New(fmt.Sprintf("t%d", time.Now().Unix())).Funcs(functions).Parse(s))
	}
	exec := func(t *template.Template, item interface{}) string {
		var b bytes.Buffer
		t.Execute(&b, item)
		return b.String()
	}
	idt := mustTemplate(idPattern)
	dispt := mustTemplate(displayPattern)
	return func(entries []interface{}) enum {
		result := enum{}
		for _, it := range entries {
			result = append(result, item{id: exec(idt, it), display: exec(dispt, it), underlying: it})
		}
		return result
	}
}

func queryRestEndpoint(path string) ([]byte, error) {
	resp, err := http.Get(path)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

func queryArray(path string) []interface{} {
	// TODO: Cache this, as we're using the same path for multiple dimensions.
	body, err := queryRestEndpoint(path)
	if err != nil {
		panic(err)
	}

	arr := []interface{}{}
	if err := json.Unmarshal(body, &arr); err != nil {
		panic(err)
	}
	return arr
}

func queryObject(path string) map[string]interface{} {
	resp, err := http.Get(path)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}

	arr := map[string]interface{}{}
	if err := json.Unmarshal(body, &arr); err != nil {
		panic(err)
	}
	return arr
}

func clearDimensionData() {
	for _, d := range dimensions {
		if d.enumSrc != nil {
			d.enumData = nil
		}
		d.itemMap = nil
	}
}

func newTask(entry map[string]interface{}, kind Item) *task {
	t := &task{
		underlying: entry,
		trace:      traceInfo{target: nilItem, subject: nilItem},
		kind:       kind,
		host:       nilItem,
		pkg:        nilItem,
		parent:     nil,
	}

	if st, ok := entry["status"].(float64); ok {
		switch int(st) {
		case 1:
			t.status = grid.InProgress
			t.result = grid.Unknown
		case 2:
			t.status = grid.Current
			t.result = grid.Succeeded
		case 3:
			t.status = grid.Current
			t.result = grid.Failed
		}
	} else {
		t.status = grid.Stale
		t.result = grid.Failed
	}
	return t
}

func compareTasksSimilar(t1 *task, t2 *task) bool {
	// similar tasks can have different packages, but have the same target, subject, and host.
	if t1.trace.target.Id() == t2.trace.target.Id() && t1.trace.subject.Id() == t2.trace.subject.Id() && t1.host.Id() == t2.host.Id() {
		return true
	}
	return false
}

func connectTaskParentChild(childListMap map[string][]*task, parentListMap map[string][]*task, t *task) {
	findParentPkgIdInList := func(idList []string, childId string) string {
		// it is the index of id's parent.
		for it, id := range idList[1:] {
			if childId == id {
				return idList[it]
			}
		}
		return ""
	}
	pkgId := t.pkg.Id()
	parentListMap[pkgId] = append(parentListMap[pkgId], t)

	if parPkgId := findParentPkgIdInList(tracks["auto"].packageList, pkgId); parPkgId != "" {
		childListMap[parPkgId] = append(childListMap[parPkgId], t)

		if parentList, ok := parentListMap[parPkgId]; ok {
			for _, parent := range parentList {
				if compareTasksSimilar(t, parent) {
					t.parent = parent
				}
			}
		}
	}

	if childList, ok := childListMap[pkgId]; ok {
		for _, child := range childList {
			if compareTasksSimilar(t, child) {
				if child.parent != nil {
					fmt.Fprintf(os.Stderr, "A task's parent was found twice? parent package id: %v; child package id: %v", pkgId, child.pkg.Id())
				} else {
					child.parent = t
				}
			}
		}
	}
}

func robotTasksPerKind(kind Item, path string, fun func(map[string]interface{}, *task)) []*task {
	tasks := []*task{}
	notCurrentTasks := []*task{}
	currentTasks := []*task{}
	childMap := map[string][]*task{}
	parentMap := map[string][]*task{}

	for _, e := range queryArray(path) {
		e := e.(map[string]interface{})
		t := newTask(e, kind)
		fun(e, t)
		connectTaskParentChild(childMap, parentMap, t)
		tasks = append(tasks, t)
		if t.status != grid.Current {
			notCurrentTasks = append(notCurrentTasks, t)
		} else {
			currentTasks = append(currentTasks, t)
		}
	}
	for _, t := range notCurrentTasks {
		if t.parent != nil {
			t.result = t.parent.result
		}
	}
	for _, t := range currentTasks {
		if t.parent != nil && t.parent.result != t.result {
			t.status = grid.Changed
		}
	}
	return tasks
}

func getRobotTasks() []*task {
	traceMap := map[string]*task{}
	tasks := []*task{}

	traceProc := func(e map[string]interface{}, t *task) {
		ei := e["input"].(map[string]interface{})
		t.trace = traceInfo{
			target:  targetDimension.GetItem(e["target"]),
			subject: subjectDimension.GetItem(ei["subject"]),
		}
		t.host = hostDimension.GetItem(e["host"])
		t.pkg = packageDimension.GetItem(ei["package"])
		if eo := e["output"]; eo != nil {
			if traceOutput := eo.(map[string]interface{})["trace"]; traceOutput != nil {
				traceMap[traceOutput.(string)] = t
			}
		}
	}
	tasks = append(tasks, robotTasksPerKind(traceKind, "/traces/", traceProc)...)

	subTaskProc := func(e map[string]interface{}, t *task) {
		ei := (e["input"].(map[string]interface{}))
		id := ei["trace"].(string)
		if traceTask, found := traceMap[id]; found {
			t.trace = traceTask.trace
		} else {
			fmt.Fprintf(os.Stderr, "Trace %s not found when processing action\n", id)
		}
		t.host = hostDimension.GetItem(e["host"])
		t.pkg = packageDimension.GetItem(ei["package"])
	}
	tasks = append(tasks, robotTasksPerKind(replayKind, "/replays/", subTaskProc)...)
	tasks = append(tasks, robotTasksPerKind(reportKind, "/reports/", subTaskProc)...)

	return tasks
}
<commit_msg>Address nit<commit_after>// Copyright (C) 2017 Google Inc.
//
// 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 (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"reflect"
	"time"

	"os"

	"github.com/google/gapid/test/robot/web/client/widgets/grid"
)

type trackInfo struct {
	track                 Item
	packageDisplayToOrder map[string]int
	packageList           []string
	headPackage           string
}

var (
	traceKind  = item{id: "trace"}
	reportKind = item{id: "report"}
	replayKind = item{id: "replay"}

	packageDisplayTemplate = "{{if .information.tag}}{{.information.tag}}" +
		"{{else if and (isUserType .information.type) (.information.cl)}}{{.information.cl}}" +
		"{{else if  .information.uploader}}{{.information.uploader}} - {{.id}}" +
		"{{else}}unknown - {{.id}}" +
		"{{end}}"

	machineDisplayTemplate = "{{if .information.Name}}{{.information.Name}}" +
		"{{else if .information.Configuration.Hardware.Name}}{{.information.Configuration.Hardware.Name}}" +
		"{{else}}{{.information.Configuration.OS}} - {{.information.id.data}}" +
		"{{end}}"

	kindDimension = &dimension{
		name:     "kind",
		enumData: enum{traceKind, replayKind, reportKind},
		valueOf: func(t *task) Item {
			return t.kind
		},
	}
	subjectDimension = &dimension{
		name: "subject",
		valueOf: func(t *task) Item {
			return t.trace.subject
		},
		enumSrc: func() enum {
			return itemGetter("{{.id}}", "{{.Information.APK.package}}", template.FuncMap{})(queryArray("/subjects/"))
		},
	}
	targetDimension = &dimension{
		name: "traceTarget",
		valueOf: func(t *task) Item {
			return t.trace.target
		},
		enumSrc: func() enum {
			return itemGetter("{{.id}}", machineDisplayTemplate, template.FuncMap{})(queryArray("/devices/"))
		},
	}
	hostDimension = &dimension{
		name: "host",
		valueOf: func(t *task) Item {
			return t.host
		},
		enumSrc: func() enum {
			return itemGetter("{{.id}}", machineDisplayTemplate, template.FuncMap{})(queryArray("/devices/"))
		},
	}

	tracks = map[string]*trackInfo{"auto": &trackInfo{
		track: item{
			id:         "",
			display:    "auto",
			underlying: map[string]string{"id": "", "name": "auto", "head": ""},
		},
		packageList: []string{},
		headPackage: "",
	}}
	packageDisplayToOrder = map[string]int{}
	packageToTrack        = map[string]Item{}

	trackDimension = &dimension{
		name: "track",
		valueOf: func(t *task) Item {
			it, ok := packageToTrack[t.pkg.Id()]
			if !ok {
				return tracks["auto"].track
			}
			return it
		},
		enumSrc: func() enum {
			result := itemGetter("{{.id}}", "{{.name}}", template.FuncMap{})(queryArray("/tracks/"))
			for _, it := range result {
				track := it.Underlying().(map[string]interface{})
				trackName, ok := track["name"].(string)
				if !ok {
					continue
				}
				trackHead, ok := track["head"].(string)
				if !ok {
					continue
				}
				tracks[trackName] = &trackInfo{
					track: it,
					packageDisplayToOrder: map[string]int{},
					headPackage:           trackHead,
				}
			}
			result = append(result, tracks["auto"].track)
			return result
		},
	}
	packageDimension = &dimension{
		name: "package",
		valueOf: func(t *task) Item {
			return t.pkg
		},
		enumSrc: func() enum {
			result := itemGetter("{{.id}}", packageDisplayTemplate, template.FuncMap{"isUserType": isUserType})(queryArray("/packages/"))
			itemMap := map[string]Item{}
			childMap := map[string]string{}
			rootPkgs := []string{}
			for _, it := range result {
				pkgRoot := it.Underlying().(map[string]interface{})
				pkgId, ok := pkgRoot["id"].(string)
				itemMap[pkgId] = it
				if !ok {
					continue
				}
				if parentMem, ok := pkgRoot["parent"]; ok {
					parentId, ok := parentMem.(string)
					if !ok {
						continue
					}
					childMap[parentId] = pkgId
				} else {
					rootPkgs = append(rootPkgs, pkgId)
				}
			}
			for _, root := range rootPkgs {
				packageList := []string{}
				packageDisplayToOrder[itemMap[root].Display()] = len(packageDisplayToOrder)
				packageList = append(packageList, root)
				for childId, ok := childMap[root]; ok; childId, ok = childMap[root] {
					packageDisplayToOrder[itemMap[childId].Display()] = len(packageDisplayToOrder)
					// want tracks stored from Root -> Head
					packageList = append(packageList, childId)
					root = childId
				}
				head := root
				foundTrack := false
				// force update the trackDimension to populate tracks
				trackDimension.getEnum()
				for _, destTrack := range tracks {
					if destTrack.headPackage == head {
						destTrack.packageList = packageList
						for _, p := range destTrack.packageList {
							packageToTrack[p] = destTrack.track
						}
						foundTrack = true
						break
					}
				}
				if !foundTrack {
					// We just append all packages to the "auto" track that didn't match an existing track
					tracks["auto"].packageList = append(tracks["auto"].packageList, packageList...)
				}
			}
			return result
		},
		enumSort: func(a, b string) bool {
			if ao, ok := packageDisplayToOrder[a]; ok {
				if bo, ok := packageDisplayToOrder[b]; ok {
					return ao < bo
				}
			}
			return a < b
		},
	}

	dimensions = []*dimension{kindDimension, subjectDimension, targetDimension, hostDimension, trackDimension, packageDimension}
)

func isUserType(t reflect.Value) bool {
	// cannot currently use build.Type_UserType to check the type need to fix that.
	return t.Kind() == reflect.Float64 && t.Float() == float64(2)
}

func itemGetter(idPattern string, displayPattern string, functions template.FuncMap) func([]interface{}) enum {
	mustTemplate := func(s string) *template.Template {
		return template.Must(template.New(fmt.Sprintf("t%d", time.Now().Unix())).Funcs(functions).Parse(s))
	}
	exec := func(t *template.Template, item interface{}) string {
		var b bytes.Buffer
		t.Execute(&b, item)
		return b.String()
	}
	idt := mustTemplate(idPattern)
	dispt := mustTemplate(displayPattern)
	return func(entries []interface{}) enum {
		result := enum{}
		for _, it := range entries {
			result = append(result, item{id: exec(idt, it), display: exec(dispt, it), underlying: it})
		}
		return result
	}
}

func queryRestEndpoint(path string) ([]byte, error) {
	resp, err := http.Get(path)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

func queryArray(path string) []interface{} {
	// TODO: Cache this, as we're using the same path for multiple dimensions.
	body, err := queryRestEndpoint(path)
	if err != nil {
		panic(err)
	}

	arr := []interface{}{}
	if err := json.Unmarshal(body, &arr); err != nil {
		panic(err)
	}
	return arr
}

func queryObject(path string) map[string]interface{} {
	resp, err := http.Get(path)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}

	arr := map[string]interface{}{}
	if err := json.Unmarshal(body, &arr); err != nil {
		panic(err)
	}
	return arr
}

func clearDimensionData() {
	for _, d := range dimensions {
		if d.enumSrc != nil {
			d.enumData = nil
		}
		d.itemMap = nil
	}
}

func newTask(entry map[string]interface{}, kind Item) *task {
	t := &task{
		underlying: entry,
		trace:      traceInfo{target: nilItem, subject: nilItem},
		kind:       kind,
		host:       nilItem,
		pkg:        nilItem,
		parent:     nil,
	}

	if st, ok := entry["status"].(float64); ok {
		switch int(st) {
		case 1:
			t.status = grid.InProgress
			t.result = grid.Unknown
		case 2:
			t.status = grid.Current
			t.result = grid.Succeeded
		case 3:
			t.status = grid.Current
			t.result = grid.Failed
		}
	} else {
		t.status = grid.Stale
		t.result = grid.Failed
	}
	return t
}

func compareTasksSimilar(t1 *task, t2 *task) bool {
	// similar tasks can have different packages, but have the same target, subject, and host.
	if t1.trace.target.Id() == t2.trace.target.Id() && t1.trace.subject.Id() == t2.trace.subject.Id() && t1.host.Id() == t2.host.Id() {
		return true
	}
	return false
}

func connectTaskParentChild(childListMap map[string][]*task, parentListMap map[string][]*task, t *task) {
	findParentPkgIdInList := func(idList []string, childId string) string {
		// it is the index of id's parent.
		for it, id := range idList[1:] {
			if childId == id {
				return idList[it]
			}
		}
		return ""
	}
	pkgId := t.pkg.Id()
	parentListMap[pkgId] = append(parentListMap[pkgId], t)

	if parPkgId := findParentPkgIdInList(tracks["auto"].packageList, pkgId); parPkgId != "" {
		childListMap[parPkgId] = append(childListMap[parPkgId], t)

		if parentList, ok := parentListMap[parPkgId]; ok {
			for _, parent := range parentList {
				if compareTasksSimilar(t, parent) {
					t.parent = parent
				}
			}
		}
	}

	if childList, ok := childListMap[pkgId]; ok {
		for _, child := range childList {
			if compareTasksSimilar(t, child) {
				if child.parent != nil {
					fmt.Fprintf(os.Stderr, "A task's parent was found twice? parent package id: %v; child package id: %v", pkgId, child.pkg.Id())
				} else {
					child.parent = t
				}
			}
		}
	}
}

func robotTasksPerKind(kind Item, path string, fun func(map[string]interface{}, *task)) []*task {
	tasks := []*task{}
	notCurrentTasks := []*task{}
	currentTasks := []*task{}
	childMap := map[string][]*task{}
	parentMap := map[string][]*task{}

	for _, e := range queryArray(path) {
		e := e.(map[string]interface{})
		t := newTask(e, kind)
		fun(e, t)
		connectTaskParentChild(childMap, parentMap, t)
		tasks = append(tasks, t)
		if t.status != grid.Current {
			notCurrentTasks = append(notCurrentTasks, t)
		} else {
			currentTasks = append(currentTasks, t)
		}
	}
	for _, t := range notCurrentTasks {
		if t.parent != nil {
			t.result = t.parent.result
		}
	}
	for _, t := range currentTasks {
		if t.parent != nil && t.parent.result != t.result {
			t.status = grid.Changed
		}
	}
	return tasks
}

func getRobotTasks() []*task {
	traceMap := map[string]*task{}
	tasks := []*task{}

	traceProc := func(e map[string]interface{}, t *task) {
		ei := e["input"].(map[string]interface{})
		t.trace = traceInfo{
			target:  targetDimension.GetItem(e["target"]),
			subject: subjectDimension.GetItem(ei["subject"]),
		}
		t.host = hostDimension.GetItem(e["host"])
		t.pkg = packageDimension.GetItem(ei["package"])
		if eo := e["output"]; eo != nil {
			if traceOutput := eo.(map[string]interface{})["trace"]; traceOutput != nil {
				traceMap[traceOutput.(string)] = t
			}
		}
	}
	tasks = append(tasks, robotTasksPerKind(traceKind, "/traces/", traceProc)...)

	subTaskProc := func(e map[string]interface{}, t *task) {
		ei := (e["input"].(map[string]interface{}))
		id := ei["trace"].(string)
		if traceTask, found := traceMap[id]; found {
			t.trace = traceTask.trace
		} else {
			fmt.Fprintf(os.Stderr, "Trace %s not found when processing action\n", id)
		}
		t.host = hostDimension.GetItem(e["host"])
		t.pkg = packageDimension.GetItem(ei["package"])
	}
	tasks = append(tasks, robotTasksPerKind(replayKind, "/replays/", subTaskProc)...)
	tasks = append(tasks, robotTasksPerKind(reportKind, "/reports/", subTaskProc)...)

	return tasks
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path"
)

const BUFFER_SIZE = 1024

// fatal crashes the program if the given error is non-nil
// This isn't a good way to perform production error-handling,
// but it will serve for this demo.
func fatal(err error) {
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func main() {
	xdgConfigPath := os.Getenv("XDG_CONFIG_HOME")
	if xdgConfigPath == "" {
		home := os.Getenv("HOME")
		if home == "" {
			fmt.Println("$HOME undefined, aborting...")
			os.Exit(1)
		}
		xdgConfigPath = path.Join(home, ".config")
	}
	fmt.Println("Config Dir:", xdgConfigPath)
	configFile, err := os.Open(path.Join(xdgConfigPath, "matterleast.conf"))
	fatal(err)

	defer configFile.Close()
	data := make([]byte, BUFFER_SIZE)
	bytesRead, err := configFile.Read(data)
	fatal(err)

}
<commit_msg>Implement conversion of json file to golang map<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"path"
)

const BUFFER_SIZE = 1024

// fatal crashes the program if the given error is non-nil
// This isn't a good way to perform production error-handling,
// but it will serve for this demo.
func fatal(err error) {
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func main() {
	xdgConfigPath := os.Getenv("XDG_CONFIG_HOME")
	if xdgConfigPath == "" {
		home := os.Getenv("HOME")
		if home == "" {
			fmt.Println("$HOME undefined, aborting...")
			os.Exit(1)
		}
		xdgConfigPath = path.Join(home, ".config")
	}
	fmt.Println("Config Dir:", xdgConfigPath)
	configFile, err := os.Open(path.Join(xdgConfigPath, "matterleast.conf"))
	fatal(err)

	defer configFile.Close()
	data := make([]byte, BUFFER_SIZE)
	bytesRead, err := configFile.Read(data)
	fatal(err)

	config := make(map[string]string)
	err = json.Unmarshal(data[:bytesRead], &config)
	fatal(err)

	fmt.Println(config)
}
<|endoftext|>


<commit_before>package sdk

import (
	"compress/gzip"
	"encoding/json"
	"github.com/astaxie/beego/httplib"
	"io/ioutil"
)

func (rc *RongCloud) do(b *httplib.BeegoHTTPRequest) (body []byte, err error) {
	return rc.httpRequest(b)
}

func (rc *RongCloud) httpRequest(b *httplib.BeegoHTTPRequest) (body []byte, err error) {
	resp, err := b.DoRequest()
	if err != nil {
		return nil, err
	}
	if resp.Body == nil {
		return nil, nil
	}
	defer resp.Body.Close()
	rc.checkStatusCode(resp)
	if resp.Header.Get("Content-Encoding") == "gzip" {
		reader, err := gzip.NewReader(resp.Body)
		if err != nil {
			return nil, err
		}
		body, err = ioutil.ReadAll(reader)
	} else {
		body, err = ioutil.ReadAll(resp.Body)
	}
	if err = checkHTTPResponseCode(body); err != nil {
		return nil, err
	}
	return body, err
}

func checkHTTPResponseCode(rep []byte) error {
	code := codePool.Get().(CodeResult)
	if err := json.Unmarshal(rep, &code); err != nil {
		return err
	}
	if code.Code != 200 {
		return code
	}
	return nil
}
<commit_msg>refactor: 优化http response 解析函数 checkHTTPResponseCode<commit_after>package sdk

import (
	"compress/gzip"
	"encoding/json"
	"github.com/astaxie/beego/httplib"
	"io/ioutil"
)

func (rc *RongCloud) do(b *httplib.BeegoHTTPRequest) (body []byte, err error) {
	return rc.httpRequest(b)
}

func (rc *RongCloud) httpRequest(b *httplib.BeegoHTTPRequest) (body []byte, err error) {
	resp, err := b.DoRequest()
	if err != nil {
		return nil, err
	}
	if resp.Body == nil {
		return nil, nil
	}
	defer resp.Body.Close()
	rc.checkStatusCode(resp)
	if resp.Header.Get("Content-Encoding") == "gzip" {
		reader, err := gzip.NewReader(resp.Body)
		if err != nil {
			return nil, err
		}
		body, err = ioutil.ReadAll(reader)
	} else {
		body, err = ioutil.ReadAll(resp.Body)
	}
	if err = checkHTTPResponseCode(body); err != nil {
		return nil, err
	}
	return body, err
}

func checkHTTPResponseCode(rep []byte) error {
	code := codePool.Get().(CodeResult)
	defer codePool.Put(code)
	if err := json.Unmarshal(rep, &code); err != nil {
		return err
	}
	if code.Code != 200 {
		return code
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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 handlers

import (
	"bytes"
	"fmt"
	"net/http"
	"reflect"
	"time"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
	"k8s.io/apiserver/pkg/endpoints/metrics"
	"k8s.io/apiserver/pkg/server/httplog"
	"k8s.io/apiserver/pkg/util/wsstream"

	"golang.org/x/net/websocket"
)

// nothing will ever be sent down this channel
var neverExitWatch <-chan time.Time = make(chan time.Time)

// timeoutFactory abstracts watch timeout logic for testing
type TimeoutFactory interface {
	TimeoutCh() (<-chan time.Time, func() bool)
}

// realTimeoutFactory implements timeoutFactory
type realTimeoutFactory struct {
	timeout time.Duration
}

// TimeoutCh returns a channel which will receive something when the watch times out,
// and a cleanup function to call when this happens.
func (w *realTimeoutFactory) TimeoutCh() (<-chan time.Time, func() bool) {
	if w.timeout == 0 {
		return neverExitWatch, func() bool { return false }
	}
	t := time.NewTimer(w.timeout)
	return t.C, t.Stop
}

// serveWatch will serve a watch response.
// TODO: the functionality in this method and in WatchServer.Serve is not cleanly decoupled.
func serveWatch(watcher watch.Interface, scope RequestScope, mediaTypeOptions negotiation.MediaTypeOptions, req *http.Request, w http.ResponseWriter, timeout time.Duration) {
	options, err := optionsForTransform(mediaTypeOptions, req)
	if err != nil {
		scope.err(err, w, req)
		return
	}

	// negotiate for the stream serializer from the scope's serializer
	serializer, err := negotiation.NegotiateOutputMediaTypeStream(req, scope.Serializer, &scope)
	if err != nil {
		scope.err(err, w, req)
		return
	}
	framer := serializer.StreamSerializer.Framer
	streamSerializer := serializer.StreamSerializer.Serializer
	encoder := scope.Serializer.EncoderForVersion(streamSerializer, scope.Kind.GroupVersion())
	useTextFraming := serializer.EncodesAsText
	if framer == nil {
		scope.err(fmt.Errorf("no framer defined for %q available for embedded encoding", serializer.MediaType), w, req)
		return
	}
	// TODO: next step, get back mediaTypeOptions from negotiate and return the exact value here
	mediaType := serializer.MediaType
	if mediaType != runtime.ContentTypeJSON {
		mediaType += ";stream=watch"
	}

	// locate the appropriate embedded encoder based on the transform
	var embeddedEncoder runtime.Encoder
	contentKind, contentSerializer, transform := targetEncodingForTransform(&scope, mediaTypeOptions, req)
	if transform {
		var embedded runtime.Serializer
		for _, supported := range contentSerializer.SupportedMediaTypes() {
			if supported.MediaType == serializer.MediaType {
				embedded = supported.Serializer
			}
		}
		if embedded == nil {
			scope.err(fmt.Errorf("no encoder for %q exists in the requested target %#v", serializer.MediaType, contentSerializer), w, req)
			return
		}
		embeddedEncoder = contentSerializer.EncoderForVersion(embedded, contentKind.GroupVersion())
	} else {
		embeddedEncoder = scope.Serializer.EncoderForVersion(serializer.Serializer, contentKind.GroupVersion())
	}

	ctx := req.Context()

	server := &WatchServer{
		Watching: watcher,
		Scope:    scope,

		UseTextFraming:  useTextFraming,
		MediaType:       mediaType,
		Framer:          framer,
		Encoder:         encoder,
		EmbeddedEncoder: embeddedEncoder,

		Fixup: func(obj runtime.Object) runtime.Object {
			result, err := transformObject(ctx, obj, options, mediaTypeOptions, scope, req)
			if err != nil {
				utilruntime.HandleError(fmt.Errorf("failed to transform object %v: %v", reflect.TypeOf(obj), err))
				return obj
			}
			// When we are transformed to a table, use the table options as the state for whether we
			// should print headers - on watch, we only want to print table headers on the first object
			// and omit them on subsequent events.
			if tableOptions, ok := options.(*metav1beta1.TableOptions); ok {
				tableOptions.NoHeaders = true
			}
			return result
		},

		TimeoutFactory: &realTimeoutFactory{timeout},
	}

	server.ServeHTTP(w, req)
}

// WatchServer serves a watch.Interface over a websocket or vanilla HTTP.
type WatchServer struct {
	Watching watch.Interface
	Scope    RequestScope

	// true if websocket messages should use text framing (as opposed to binary framing)
	UseTextFraming bool
	// the media type this watch is being served with
	MediaType string
	// used to frame the watch stream
	Framer runtime.Framer
	// used to encode the watch stream event itself
	Encoder runtime.Encoder
	// used to encode the nested object in the watch stream
	EmbeddedEncoder runtime.Encoder
	// used to correct the object before we send it to the serializer
	Fixup func(runtime.Object) runtime.Object

	TimeoutFactory TimeoutFactory
}

// ServeHTTP serves a series of encoded events via HTTP with Transfer-Encoding: chunked
// or over a websocket connection.
func (s *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	kind := s.Scope.Kind
	metrics.RegisteredWatchers.WithLabelValues(kind.Group, kind.Version, kind.Kind).Inc()
	defer metrics.RegisteredWatchers.WithLabelValues(kind.Group, kind.Version, kind.Kind).Dec()

	w = httplog.Unlogged(w)

	if wsstream.IsWebSocketRequest(req) {
		w.Header().Set("Content-Type", s.MediaType)
		websocket.Handler(s.HandleWS).ServeHTTP(w, req)
		return
	}

	cn, ok := w.(http.CloseNotifier)
	if !ok {
		err := fmt.Errorf("unable to start watch - can't get http.CloseNotifier: %#v", w)
		utilruntime.HandleError(err)
		s.Scope.err(errors.NewInternalError(err), w, req)
		return
	}
	flusher, ok := w.(http.Flusher)
	if !ok {
		err := fmt.Errorf("unable to start watch - can't get http.Flusher: %#v", w)
		utilruntime.HandleError(err)
		s.Scope.err(errors.NewInternalError(err), w, req)
		return
	}

	framer := s.Framer.NewFrameWriter(w)
	if framer == nil {
		// programmer error
		err := fmt.Errorf("no stream framing support is available for media type %q", s.MediaType)
		utilruntime.HandleError(err)
		s.Scope.err(errors.NewBadRequest(err.Error()), w, req)
		return
	}
	e := streaming.NewEncoder(framer, s.Encoder)

	// ensure the connection times out
	timeoutCh, cleanup := s.TimeoutFactory.TimeoutCh()
	defer cleanup()
	defer s.Watching.Stop()

	// begin the stream
	w.Header().Set("Content-Type", s.MediaType)
	w.Header().Set("Transfer-Encoding", "chunked")
	w.WriteHeader(http.StatusOK)
	flusher.Flush()

	var unknown runtime.Unknown
	internalEvent := &metav1.InternalEvent{}
	buf := &bytes.Buffer{}
	ch := s.Watching.ResultChan()
	for {
		select {
		case <-cn.CloseNotify():
			return
		case <-timeoutCh:
			return
		case event, ok := <-ch:
			if !ok {
				// End of results.
				return
			}

			obj := s.Fixup(event.Object)
			if err := s.EmbeddedEncoder.Encode(obj, buf); err != nil {
				// unexpected error
				utilruntime.HandleError(fmt.Errorf("unable to encode watch object %T: %v", obj, err))
				return
			}

			// ContentType is not required here because we are defaulting to the serializer
			// type
			unknown.Raw = buf.Bytes()
			event.Object = &unknown

			// create the external type directly and encode it.  Clients will only recognize the serialization we provide.
			// The internal event is being reused, not reallocated so its just a few extra assignments to do it this way
			// and we get the benefit of using conversion functions which already have to stay in sync
			outEvent := &metav1.WatchEvent{}
			*internalEvent = metav1.InternalEvent(event)
			err := metav1.Convert_v1_InternalEvent_To_v1_WatchEvent(internalEvent, outEvent, nil)
			if err != nil {
				utilruntime.HandleError(fmt.Errorf("unable to convert watch object: %v", err))
				// client disconnect.
				return
			}
			if err := e.Encode(outEvent); err != nil {
				utilruntime.HandleError(fmt.Errorf("unable to encode watch object %T: %v (%#v)", outEvent, err, e))
				// client disconnect.
				return
			}
			if len(ch) == 0 {
				flusher.Flush()
			}

			buf.Reset()
		}
	}
}

// HandleWS implements a websocket handler.
func (s *WatchServer) HandleWS(ws *websocket.Conn) {
	defer ws.Close()
	done := make(chan struct{})

	go func() {
		defer utilruntime.HandleCrash()
		// This blocks until the connection is closed.
		// Client should not send anything.
		wsstream.IgnoreReceives(ws, 0)
		// Once the client closes, we should also close
		close(done)
	}()

	var unknown runtime.Unknown
	internalEvent := &metav1.InternalEvent{}
	buf := &bytes.Buffer{}
	streamBuf := &bytes.Buffer{}
	ch := s.Watching.ResultChan()
	for {
		select {
		case <-done:
			s.Watching.Stop()
			return
		case event, ok := <-ch:
			if !ok {
				// End of results.
				return
			}
			obj := s.Fixup(event.Object)
			if err := s.EmbeddedEncoder.Encode(obj, buf); err != nil {
				// unexpected error
				utilruntime.HandleError(fmt.Errorf("unable to encode watch object %T: %v", obj, err))
				return
			}

			// ContentType is not required here because we are defaulting to the serializer
			// type
			unknown.Raw = buf.Bytes()
			event.Object = &unknown

			// the internal event will be versioned by the encoder
			// create the external type directly and encode it.  Clients will only recognize the serialization we provide.
			// The internal event is being reused, not reallocated so its just a few extra assignments to do it this way
			// and we get the benefit of using conversion functions which already have to stay in sync
			outEvent := &metav1.WatchEvent{}
			*internalEvent = metav1.InternalEvent(event)
			err := metav1.Convert_v1_InternalEvent_To_v1_WatchEvent(internalEvent, outEvent, nil)
			if err != nil {
				utilruntime.HandleError(fmt.Errorf("unable to convert watch object: %v", err))
				// client disconnect.
				s.Watching.Stop()
				return
			}
			if err := s.Encoder.Encode(outEvent, streamBuf); err != nil {
				// encoding error
				utilruntime.HandleError(fmt.Errorf("unable to encode event: %v", err))
				s.Watching.Stop()
				return
			}
			if s.UseTextFraming {
				if err := websocket.Message.Send(ws, streamBuf.String()); err != nil {
					// Client disconnect.
					s.Watching.Stop()
					return
				}
			} else {
				if err := websocket.Message.Send(ws, streamBuf.Bytes()); err != nil {
					// Client disconnect.
					s.Watching.Stop()
					return
				}
			}
			buf.Reset()
			streamBuf.Reset()
		}
	}
}
<commit_msg>Avoid allocating the watch shim object more than once<commit_after>/*
Copyright 2014 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 handlers

import (
	"bytes"
	"fmt"
	"net/http"
	"reflect"
	"time"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
	"k8s.io/apiserver/pkg/endpoints/metrics"
	"k8s.io/apiserver/pkg/server/httplog"
	"k8s.io/apiserver/pkg/util/wsstream"

	"golang.org/x/net/websocket"
)

// nothing will ever be sent down this channel
var neverExitWatch <-chan time.Time = make(chan time.Time)

// timeoutFactory abstracts watch timeout logic for testing
type TimeoutFactory interface {
	TimeoutCh() (<-chan time.Time, func() bool)
}

// realTimeoutFactory implements timeoutFactory
type realTimeoutFactory struct {
	timeout time.Duration
}

// TimeoutCh returns a channel which will receive something when the watch times out,
// and a cleanup function to call when this happens.
func (w *realTimeoutFactory) TimeoutCh() (<-chan time.Time, func() bool) {
	if w.timeout == 0 {
		return neverExitWatch, func() bool { return false }
	}
	t := time.NewTimer(w.timeout)
	return t.C, t.Stop
}

// serveWatch will serve a watch response.
// TODO: the functionality in this method and in WatchServer.Serve is not cleanly decoupled.
func serveWatch(watcher watch.Interface, scope RequestScope, mediaTypeOptions negotiation.MediaTypeOptions, req *http.Request, w http.ResponseWriter, timeout time.Duration) {
	options, err := optionsForTransform(mediaTypeOptions, req)
	if err != nil {
		scope.err(err, w, req)
		return
	}

	// negotiate for the stream serializer from the scope's serializer
	serializer, err := negotiation.NegotiateOutputMediaTypeStream(req, scope.Serializer, &scope)
	if err != nil {
		scope.err(err, w, req)
		return
	}
	framer := serializer.StreamSerializer.Framer
	streamSerializer := serializer.StreamSerializer.Serializer
	encoder := scope.Serializer.EncoderForVersion(streamSerializer, scope.Kind.GroupVersion())
	useTextFraming := serializer.EncodesAsText
	if framer == nil {
		scope.err(fmt.Errorf("no framer defined for %q available for embedded encoding", serializer.MediaType), w, req)
		return
	}
	// TODO: next step, get back mediaTypeOptions from negotiate and return the exact value here
	mediaType := serializer.MediaType
	if mediaType != runtime.ContentTypeJSON {
		mediaType += ";stream=watch"
	}

	// locate the appropriate embedded encoder based on the transform
	var embeddedEncoder runtime.Encoder
	contentKind, contentSerializer, transform := targetEncodingForTransform(&scope, mediaTypeOptions, req)
	if transform {
		var embedded runtime.Serializer
		for _, supported := range contentSerializer.SupportedMediaTypes() {
			if supported.MediaType == serializer.MediaType {
				embedded = supported.Serializer
			}
		}
		if embedded == nil {
			scope.err(fmt.Errorf("no encoder for %q exists in the requested target %#v", serializer.MediaType, contentSerializer), w, req)
			return
		}
		embeddedEncoder = contentSerializer.EncoderForVersion(embedded, contentKind.GroupVersion())
	} else {
		embeddedEncoder = scope.Serializer.EncoderForVersion(serializer.Serializer, contentKind.GroupVersion())
	}

	ctx := req.Context()

	server := &WatchServer{
		Watching: watcher,
		Scope:    scope,

		UseTextFraming:  useTextFraming,
		MediaType:       mediaType,
		Framer:          framer,
		Encoder:         encoder,
		EmbeddedEncoder: embeddedEncoder,

		Fixup: func(obj runtime.Object) runtime.Object {
			result, err := transformObject(ctx, obj, options, mediaTypeOptions, scope, req)
			if err != nil {
				utilruntime.HandleError(fmt.Errorf("failed to transform object %v: %v", reflect.TypeOf(obj), err))
				return obj
			}
			// When we are transformed to a table, use the table options as the state for whether we
			// should print headers - on watch, we only want to print table headers on the first object
			// and omit them on subsequent events.
			if tableOptions, ok := options.(*metav1beta1.TableOptions); ok {
				tableOptions.NoHeaders = true
			}
			return result
		},

		TimeoutFactory: &realTimeoutFactory{timeout},
	}

	server.ServeHTTP(w, req)
}

// WatchServer serves a watch.Interface over a websocket or vanilla HTTP.
type WatchServer struct {
	Watching watch.Interface
	Scope    RequestScope

	// true if websocket messages should use text framing (as opposed to binary framing)
	UseTextFraming bool
	// the media type this watch is being served with
	MediaType string
	// used to frame the watch stream
	Framer runtime.Framer
	// used to encode the watch stream event itself
	Encoder runtime.Encoder
	// used to encode the nested object in the watch stream
	EmbeddedEncoder runtime.Encoder
	// used to correct the object before we send it to the serializer
	Fixup func(runtime.Object) runtime.Object

	TimeoutFactory TimeoutFactory
}

// ServeHTTP serves a series of encoded events via HTTP with Transfer-Encoding: chunked
// or over a websocket connection.
func (s *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	kind := s.Scope.Kind
	metrics.RegisteredWatchers.WithLabelValues(kind.Group, kind.Version, kind.Kind).Inc()
	defer metrics.RegisteredWatchers.WithLabelValues(kind.Group, kind.Version, kind.Kind).Dec()

	w = httplog.Unlogged(w)

	if wsstream.IsWebSocketRequest(req) {
		w.Header().Set("Content-Type", s.MediaType)
		websocket.Handler(s.HandleWS).ServeHTTP(w, req)
		return
	}

	cn, ok := w.(http.CloseNotifier)
	if !ok {
		err := fmt.Errorf("unable to start watch - can't get http.CloseNotifier: %#v", w)
		utilruntime.HandleError(err)
		s.Scope.err(errors.NewInternalError(err), w, req)
		return
	}
	flusher, ok := w.(http.Flusher)
	if !ok {
		err := fmt.Errorf("unable to start watch - can't get http.Flusher: %#v", w)
		utilruntime.HandleError(err)
		s.Scope.err(errors.NewInternalError(err), w, req)
		return
	}

	framer := s.Framer.NewFrameWriter(w)
	if framer == nil {
		// programmer error
		err := fmt.Errorf("no stream framing support is available for media type %q", s.MediaType)
		utilruntime.HandleError(err)
		s.Scope.err(errors.NewBadRequest(err.Error()), w, req)
		return
	}
	e := streaming.NewEncoder(framer, s.Encoder)

	// ensure the connection times out
	timeoutCh, cleanup := s.TimeoutFactory.TimeoutCh()
	defer cleanup()
	defer s.Watching.Stop()

	// begin the stream
	w.Header().Set("Content-Type", s.MediaType)
	w.Header().Set("Transfer-Encoding", "chunked")
	w.WriteHeader(http.StatusOK)
	flusher.Flush()

	var unknown runtime.Unknown
	internalEvent := &metav1.InternalEvent{}
	outEvent := &metav1.WatchEvent{}
	buf := &bytes.Buffer{}
	ch := s.Watching.ResultChan()
	for {
		select {
		case <-cn.CloseNotify():
			return
		case <-timeoutCh:
			return
		case event, ok := <-ch:
			if !ok {
				// End of results.
				return
			}

			obj := s.Fixup(event.Object)
			if err := s.EmbeddedEncoder.Encode(obj, buf); err != nil {
				// unexpected error
				utilruntime.HandleError(fmt.Errorf("unable to encode watch object %T: %v", obj, err))
				return
			}

			// ContentType is not required here because we are defaulting to the serializer
			// type
			unknown.Raw = buf.Bytes()
			event.Object = &unknown

			*outEvent = metav1.WatchEvent{}

			// create the external type directly and encode it.  Clients will only recognize the serialization we provide.
			// The internal event is being reused, not reallocated so its just a few extra assignments to do it this way
			// and we get the benefit of using conversion functions which already have to stay in sync
			*internalEvent = metav1.InternalEvent(event)
			err := metav1.Convert_v1_InternalEvent_To_v1_WatchEvent(internalEvent, outEvent, nil)
			if err != nil {
				utilruntime.HandleError(fmt.Errorf("unable to convert watch object: %v", err))
				// client disconnect.
				return
			}
			if err := e.Encode(outEvent); err != nil {
				utilruntime.HandleError(fmt.Errorf("unable to encode watch object %T: %v (%#v)", outEvent, err, e))
				// client disconnect.
				return
			}
			if len(ch) == 0 {
				flusher.Flush()
			}

			buf.Reset()
		}
	}
}

// HandleWS implements a websocket handler.
func (s *WatchServer) HandleWS(ws *websocket.Conn) {
	defer ws.Close()
	done := make(chan struct{})

	go func() {
		defer utilruntime.HandleCrash()
		// This blocks until the connection is closed.
		// Client should not send anything.
		wsstream.IgnoreReceives(ws, 0)
		// Once the client closes, we should also close
		close(done)
	}()

	var unknown runtime.Unknown
	internalEvent := &metav1.InternalEvent{}
	buf := &bytes.Buffer{}
	streamBuf := &bytes.Buffer{}
	ch := s.Watching.ResultChan()
	for {
		select {
		case <-done:
			s.Watching.Stop()
			return
		case event, ok := <-ch:
			if !ok {
				// End of results.
				return
			}
			obj := s.Fixup(event.Object)
			if err := s.EmbeddedEncoder.Encode(obj, buf); err != nil {
				// unexpected error
				utilruntime.HandleError(fmt.Errorf("unable to encode watch object %T: %v", obj, err))
				return
			}

			// ContentType is not required here because we are defaulting to the serializer
			// type
			unknown.Raw = buf.Bytes()
			event.Object = &unknown

			// the internal event will be versioned by the encoder
			// create the external type directly and encode it.  Clients will only recognize the serialization we provide.
			// The internal event is being reused, not reallocated so its just a few extra assignments to do it this way
			// and we get the benefit of using conversion functions which already have to stay in sync
			outEvent := &metav1.WatchEvent{}
			*internalEvent = metav1.InternalEvent(event)
			err := metav1.Convert_v1_InternalEvent_To_v1_WatchEvent(internalEvent, outEvent, nil)
			if err != nil {
				utilruntime.HandleError(fmt.Errorf("unable to convert watch object: %v", err))
				// client disconnect.
				s.Watching.Stop()
				return
			}
			if err := s.Encoder.Encode(outEvent, streamBuf); err != nil {
				// encoding error
				utilruntime.HandleError(fmt.Errorf("unable to encode event: %v", err))
				s.Watching.Stop()
				return
			}
			if s.UseTextFraming {
				if err := websocket.Message.Send(ws, streamBuf.String()); err != nil {
					// Client disconnect.
					s.Watching.Stop()
					return
				}
			} else {
				if err := websocket.Message.Send(ws, streamBuf.Bytes()); err != nil {
					// Client disconnect.
					s.Watching.Stop()
					return
				}
			}
			buf.Reset()
			streamBuf.Reset()
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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 resource

import (
	"fmt"
	"io"
	"net/url"
	"os"
	"strings"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
)

// Builder provides convenience functions for taking arguments and parameters
// from the command line and converting them to a list of resources to iterate
// over using the Visitor interface.
type Builder struct {
	mapper *Mapper

	errs []error

	paths  []Visitor
	stream bool
	dir    bool

	selector labels.Selector

	resources []string

	namespace string
	name      string

	defaultNamespace bool
	requireNamespace bool

	flatten bool
	latest  bool

	singleResourceType bool
	continueOnError    bool
}

// NewBuilder creates a builder that operates on generic objects.
func NewBuilder(mapper meta.RESTMapper, typer runtime.ObjectTyper, clientMapper ClientMapper) *Builder {
	return &Builder{
		mapper: &Mapper{typer, mapper, clientMapper},
	}
}

// Filename is parameters passed via a filename argument which may be URLs, the "-" argument indicating
// STDIN, or paths to files or directories. If ContinueOnError() is set prior to this method being called,
// objects on the path that are unrecognized will be ignored (but logged at V(2)).
func (b *Builder) FilenameParam(paths ...string) *Builder {
	for _, s := range paths {
		switch {
		case s == "-":
			b.Stdin()
		case strings.Index(s, "http://") == 0 || strings.Index(s, "https://") == 0:
			url, err := url.Parse(s)
			if err != nil {
				b.errs = append(b.errs, fmt.Errorf("the URL passed to filename %q is not valid: %v", s, err))
				continue
			}
			b.URL(url)
		default:
			b.Path(s)
		}
	}
	return b
}

// URL accepts a number of URLs directly.
func (b *Builder) URL(urls ...*url.URL) *Builder {
	for _, u := range urls {
		b.paths = append(b.paths, &URLVisitor{
			Mapper: b.mapper,
			URL:    u,
		})
	}
	return b
}

// Stdin will read objects from the standard input. If ContinueOnError() is set
// prior to this method being called, objects in the stream that are unrecognized
// will be ignored (but logged at V(2)).
func (b *Builder) Stdin() *Builder {
	return b.Stream(os.Stdin, "STDIN")
}

// Stream will read objects from the provided reader, and if an error occurs will
// include the name string in the error message. If ContinueOnError() is set
// prior to this method being called, objects in the stream that are unrecognized
// will be ignored (but logged at V(2)).
func (b *Builder) Stream(r io.Reader, name string) *Builder {
	b.stream = true
	b.paths = append(b.paths, NewStreamVisitor(r, b.mapper, name, b.continueOnError))
	return b
}

// Path is a set of filesystem paths that may be files containing one or more
// resources. If ContinueOnError() is set prior to this method being called,
// objects on the path that are unrecognized will be ignored (but logged at V(2)).
func (b *Builder) Path(paths ...string) *Builder {
	for _, p := range paths {
		i, err := os.Stat(p)
		if os.IsNotExist(err) {
			b.errs = append(b.errs, fmt.Errorf("the path %q does not exist", p))
			continue
		}
		if err != nil {
			b.errs = append(b.errs, fmt.Errorf("the path %q cannot be accessed: %v", p, err))
			continue
		}
		var visitor Visitor
		if i.IsDir() {
			b.dir = true
			visitor = &DirectoryVisitor{
				Mapper:       b.mapper,
				Path:         p,
				Extensions:   []string{".json", ".yaml"},
				Recursive:    false,
				IgnoreErrors: b.continueOnError,
			}
		} else {
			visitor = &PathVisitor{
				Mapper:       b.mapper,
				Path:         p,
				IgnoreErrors: b.continueOnError,
			}
		}
		b.paths = append(b.paths, visitor)
	}
	return b
}

// ResourceTypes is a list of types of resources to operate on, when listing objects on
// the server or retrieving objects that match a selector.
func (b *Builder) ResourceTypes(types ...string) *Builder {
	b.resources = append(b.resources, types...)
	return b
}

// SelectorParam defines a selector that should be applied to the object types to load.
// This will not affect files loaded from disk or URL. If the parameter is empty it is
// a no-op - to select all resources invoke `b.Selector(labels.Everything)`.
func (b *Builder) SelectorParam(s string) *Builder {
	selector, err := labels.ParseSelector(s)
	if err != nil {
		b.errs = append(b.errs, fmt.Errorf("the provided selector %q is not valid: %v", s, err))
		return b
	}
	if selector.Empty() {
		return b
	}
	return b.Selector(selector)
}

// Selector accepts a selector directly, and if non nil will trigger a list action.
func (b *Builder) Selector(selector labels.Selector) *Builder {
	b.selector = selector
	return b
}

// The namespace that these resources should be assumed to under - used by DefaultNamespace()
// and RequireNamespace()
func (b *Builder) NamespaceParam(namespace string) *Builder {
	b.namespace = namespace
	return b
}

// DefaultNamespace instructs the builder to set the namespace value for any object found
// to NamespaceParam() if empty.
func (b *Builder) DefaultNamespace() *Builder {
	b.defaultNamespace = true
	return b
}

// RequireNamespace instructs the builder to set the namespace value for any object found
// to NamespaceParam() if empty, and if the value on the resource does not match
// NamespaceParam() an error will be returned.
func (b *Builder) RequireNamespace() *Builder {
	b.requireNamespace = true
	return b
}

// ResourceTypeOrNameArgs indicates that the builder should accept one or two arguments
// of the form `(<type1>[,<type2>,...]|<type> <name>)`. When one argument is received, the types
// provided will be retrieved from the server (and be comma delimited).  When two arguments are
// received, they must be a single type and name. If more than two arguments are provided an
// error is set.
func (b *Builder) ResourceTypeOrNameArgs(args ...string) *Builder {
	switch len(args) {
	case 2:
		b.name = args[1]
		b.ResourceTypes(SplitResourceArgument(args[0])...)
	case 1:
		b.ResourceTypes(SplitResourceArgument(args[0])...)
		if b.selector == nil {
			b.selector = labels.Everything()
		}
	case 0:
	default:
		b.errs = append(b.errs, fmt.Errorf("when passing arguments, must be resource or resource and name"))
	}
	return b
}

// ResourceTypeAndNameArgs expects two arguments, a resource type, and a resource name. The resource
// matching that type and and name will be retrieved from the server.
func (b *Builder) ResourceTypeAndNameArgs(args ...string) *Builder {
	switch len(args) {
	case 2:
		b.name = args[1]
		b.ResourceTypes(SplitResourceArgument(args[0])...)
	case 0:
	default:
		b.errs = append(b.errs, fmt.Errorf("when passing arguments, must be resource and name"))
	}
	return b
}

// Flatten will convert any objects with a field named "Items" that is an array of runtime.Object
// compatible types into individual entries and give them their own items. The original object
// is not passed to any visitors.
func (b *Builder) Flatten() *Builder {
	b.flatten = true
	return b
}

// Latest will fetch the latest copy of any objects loaded from URLs or files from the server.
func (b *Builder) Latest() *Builder {
	b.latest = true
	return b
}

// ContinueOnError will attempt to load and visit as many objects as possible, even if some visits
// return errors or some objects cannot be loaded. The default behavior is to terminate after
// the first error is returned from a VisitorFunc.
func (b *Builder) ContinueOnError() *Builder {
	b.continueOnError = true
	return b
}

// SingleResourceType will cause the builder to error if the user specifies more than a single type
// of resource.
func (b *Builder) SingleResourceType() *Builder {
	b.singleResourceType = true
	return b
}

func (b *Builder) resourceMappings() ([]*meta.RESTMapping, error) {
	if len(b.resources) > 1 && b.singleResourceType {
		return nil, fmt.Errorf("you may only specify a single resource type")
	}
	mappings := []*meta.RESTMapping{}
	for _, r := range b.resources {
		version, kind, err := b.mapper.VersionAndKindForResource(r)
		if err != nil {
			return nil, err
		}
		mapping, err := b.mapper.RESTMapping(kind, version)
		if err != nil {
			return nil, err
		}
		mappings = append(mappings, mapping)
	}
	return mappings, nil
}

func (b *Builder) visitorResult() *Result {
	if len(b.errs) > 0 {
		return &Result{err: errors.NewAggregate(b.errs)}
	}

	// visit selectors
	if b.selector != nil {
		if len(b.name) != 0 {
			return &Result{err: fmt.Errorf("name cannot be provided when a selector is specified")}
		}
		if len(b.resources) == 0 {
			return &Result{err: fmt.Errorf("at least one resource must be specified to use a selector")}
		}
		// empty selector has different error message for paths being provided
		if len(b.paths) != 0 {
			if b.selector.Empty() {
				return &Result{err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
			} else {
				return &Result{err: fmt.Errorf("a selector may not be specified when path, URL, or stdin is provided as input")}
			}
		}
		mappings, err := b.resourceMappings()
		if err != nil {
			return &Result{err: err}
		}

		visitors := []Visitor{}
		for _, mapping := range mappings {
			client, err := b.mapper.ClientForMapping(mapping)
			if err != nil {
				return &Result{err: err}
			}
			visitors = append(visitors, NewSelector(client, mapping, b.namespace, b.selector))
		}
		if b.continueOnError {
			return &Result{visitor: EagerVisitorList(visitors), sources: visitors}
		}
		return &Result{visitor: VisitorList(visitors), sources: visitors}
	}

	// visit single item specified by name
	if len(b.name) != 0 {
		if len(b.paths) != 0 {
			return &Result{singular: true, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
		}
		if len(b.resources) == 0 {
			return &Result{singular: true, err: fmt.Errorf("you must provide a resource and a resource name together")}
		}
		if len(b.resources) > 1 {
			return &Result{singular: true, err: fmt.Errorf("you must specify only one resource")}
		}
		if len(b.namespace) == 0 {
			return &Result{singular: true, err: fmt.Errorf("namespace may not be empty when retrieving a resource by name")}
		}
		mappings, err := b.resourceMappings()
		if err != nil {
			return &Result{singular: true, err: err}
		}
		client, err := b.mapper.ClientForMapping(mappings[0])
		if err != nil {
			return &Result{singular: true, err: err}
		}
		info := NewInfo(client, mappings[0], b.namespace, b.name)
		if err := info.Get(); err != nil {
			return &Result{singular: true, err: err}
		}
		return &Result{singular: true, visitor: info, sources: []Visitor{info}}
	}

	// visit items specified by paths
	if len(b.paths) != 0 {
		singular := !b.dir && !b.stream && len(b.paths) == 1
		if len(b.resources) != 0 {
			return &Result{singular: singular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify resource arguments as well")}
		}

		var visitors Visitor
		if b.continueOnError {
			visitors = EagerVisitorList(b.paths)
		} else {
			visitors = VisitorList(b.paths)
		}

		// only items from disk can be refetched
		if b.latest {
			// must flatten lists prior to fetching
			if b.flatten {
				visitors = NewFlattenListVisitor(visitors, b.mapper)
			}
			visitors = NewDecoratedVisitor(visitors, RetrieveLatest)
		}
		return &Result{singular: singular, visitor: visitors, sources: b.paths}
	}

	return &Result{err: fmt.Errorf("you must provide one or more resources by argument or filename")}
}

// Do returns a Result object with a Visitor for the resources identified by the Builder.
// The visitor will respect the error behavior specified by ContinueOnError. Note that stream
// inputs are consumed by the first execution - use Infos() or Object() on the Result to capture a list
// for further iteration.
func (b *Builder) Do() *Result {
	r := b.visitorResult()
	if r.err != nil {
		return r
	}
	if b.flatten {
		r.visitor = NewFlattenListVisitor(r.visitor, b.mapper)
	}
	helpers := []VisitorFunc{}
	if b.defaultNamespace {
		helpers = append(helpers, SetNamespace(b.namespace))
	}
	if b.requireNamespace {
		helpers = append(helpers, RequireNamespace(b.namespace))
	}
	helpers = append(helpers, FilterNamespace())
	r.visitor = NewDecoratedVisitor(r.visitor, helpers...)
	return r
}

func SplitResourceArgument(arg string) []string {
	set := util.NewStringSet()
	set.Insert(strings.Split(arg, ",")...)
	return set.List()
}
<commit_msg>Do not provide a namespace on request url if resource does not require it<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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 resource

import (
	"fmt"
	"io"
	"net/url"
	"os"
	"strings"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
)

// Builder provides convenience functions for taking arguments and parameters
// from the command line and converting them to a list of resources to iterate
// over using the Visitor interface.
type Builder struct {
	mapper *Mapper

	errs []error

	paths  []Visitor
	stream bool
	dir    bool

	selector labels.Selector

	resources []string

	namespace string
	name      string

	defaultNamespace bool
	requireNamespace bool

	flatten bool
	latest  bool

	singleResourceType bool
	continueOnError    bool
}

// NewBuilder creates a builder that operates on generic objects.
func NewBuilder(mapper meta.RESTMapper, typer runtime.ObjectTyper, clientMapper ClientMapper) *Builder {
	return &Builder{
		mapper: &Mapper{typer, mapper, clientMapper},
	}
}

// Filename is parameters passed via a filename argument which may be URLs, the "-" argument indicating
// STDIN, or paths to files or directories. If ContinueOnError() is set prior to this method being called,
// objects on the path that are unrecognized will be ignored (but logged at V(2)).
func (b *Builder) FilenameParam(paths ...string) *Builder {
	for _, s := range paths {
		switch {
		case s == "-":
			b.Stdin()
		case strings.Index(s, "http://") == 0 || strings.Index(s, "https://") == 0:
			url, err := url.Parse(s)
			if err != nil {
				b.errs = append(b.errs, fmt.Errorf("the URL passed to filename %q is not valid: %v", s, err))
				continue
			}
			b.URL(url)
		default:
			b.Path(s)
		}
	}
	return b
}

// URL accepts a number of URLs directly.
func (b *Builder) URL(urls ...*url.URL) *Builder {
	for _, u := range urls {
		b.paths = append(b.paths, &URLVisitor{
			Mapper: b.mapper,
			URL:    u,
		})
	}
	return b
}

// Stdin will read objects from the standard input. If ContinueOnError() is set
// prior to this method being called, objects in the stream that are unrecognized
// will be ignored (but logged at V(2)).
func (b *Builder) Stdin() *Builder {
	return b.Stream(os.Stdin, "STDIN")
}

// Stream will read objects from the provided reader, and if an error occurs will
// include the name string in the error message. If ContinueOnError() is set
// prior to this method being called, objects in the stream that are unrecognized
// will be ignored (but logged at V(2)).
func (b *Builder) Stream(r io.Reader, name string) *Builder {
	b.stream = true
	b.paths = append(b.paths, NewStreamVisitor(r, b.mapper, name, b.continueOnError))
	return b
}

// Path is a set of filesystem paths that may be files containing one or more
// resources. If ContinueOnError() is set prior to this method being called,
// objects on the path that are unrecognized will be ignored (but logged at V(2)).
func (b *Builder) Path(paths ...string) *Builder {
	for _, p := range paths {
		i, err := os.Stat(p)
		if os.IsNotExist(err) {
			b.errs = append(b.errs, fmt.Errorf("the path %q does not exist", p))
			continue
		}
		if err != nil {
			b.errs = append(b.errs, fmt.Errorf("the path %q cannot be accessed: %v", p, err))
			continue
		}
		var visitor Visitor
		if i.IsDir() {
			b.dir = true
			visitor = &DirectoryVisitor{
				Mapper:       b.mapper,
				Path:         p,
				Extensions:   []string{".json", ".yaml"},
				Recursive:    false,
				IgnoreErrors: b.continueOnError,
			}
		} else {
			visitor = &PathVisitor{
				Mapper:       b.mapper,
				Path:         p,
				IgnoreErrors: b.continueOnError,
			}
		}
		b.paths = append(b.paths, visitor)
	}
	return b
}

// ResourceTypes is a list of types of resources to operate on, when listing objects on
// the server or retrieving objects that match a selector.
func (b *Builder) ResourceTypes(types ...string) *Builder {
	b.resources = append(b.resources, types...)
	return b
}

// SelectorParam defines a selector that should be applied to the object types to load.
// This will not affect files loaded from disk or URL. If the parameter is empty it is
// a no-op - to select all resources invoke `b.Selector(labels.Everything)`.
func (b *Builder) SelectorParam(s string) *Builder {
	selector, err := labels.ParseSelector(s)
	if err != nil {
		b.errs = append(b.errs, fmt.Errorf("the provided selector %q is not valid: %v", s, err))
		return b
	}
	if selector.Empty() {
		return b
	}
	return b.Selector(selector)
}

// Selector accepts a selector directly, and if non nil will trigger a list action.
func (b *Builder) Selector(selector labels.Selector) *Builder {
	b.selector = selector
	return b
}

// The namespace that these resources should be assumed to under - used by DefaultNamespace()
// and RequireNamespace()
func (b *Builder) NamespaceParam(namespace string) *Builder {
	b.namespace = namespace
	return b
}

// DefaultNamespace instructs the builder to set the namespace value for any object found
// to NamespaceParam() if empty.
func (b *Builder) DefaultNamespace() *Builder {
	b.defaultNamespace = true
	return b
}

// RequireNamespace instructs the builder to set the namespace value for any object found
// to NamespaceParam() if empty, and if the value on the resource does not match
// NamespaceParam() an error will be returned.
func (b *Builder) RequireNamespace() *Builder {
	b.requireNamespace = true
	return b
}

// ResourceTypeOrNameArgs indicates that the builder should accept one or two arguments
// of the form `(<type1>[,<type2>,...]|<type> <name>)`. When one argument is received, the types
// provided will be retrieved from the server (and be comma delimited).  When two arguments are
// received, they must be a single type and name. If more than two arguments are provided an
// error is set.
func (b *Builder) ResourceTypeOrNameArgs(args ...string) *Builder {
	switch len(args) {
	case 2:
		b.name = args[1]
		b.ResourceTypes(SplitResourceArgument(args[0])...)
	case 1:
		b.ResourceTypes(SplitResourceArgument(args[0])...)
		if b.selector == nil {
			b.selector = labels.Everything()
		}
	case 0:
	default:
		b.errs = append(b.errs, fmt.Errorf("when passing arguments, must be resource or resource and name"))
	}
	return b
}

// ResourceTypeAndNameArgs expects two arguments, a resource type, and a resource name. The resource
// matching that type and and name will be retrieved from the server.
func (b *Builder) ResourceTypeAndNameArgs(args ...string) *Builder {
	switch len(args) {
	case 2:
		b.name = args[1]
		b.ResourceTypes(SplitResourceArgument(args[0])...)
	case 0:
	default:
		b.errs = append(b.errs, fmt.Errorf("when passing arguments, must be resource and name"))
	}
	return b
}

// Flatten will convert any objects with a field named "Items" that is an array of runtime.Object
// compatible types into individual entries and give them their own items. The original object
// is not passed to any visitors.
func (b *Builder) Flatten() *Builder {
	b.flatten = true
	return b
}

// Latest will fetch the latest copy of any objects loaded from URLs or files from the server.
func (b *Builder) Latest() *Builder {
	b.latest = true
	return b
}

// ContinueOnError will attempt to load and visit as many objects as possible, even if some visits
// return errors or some objects cannot be loaded. The default behavior is to terminate after
// the first error is returned from a VisitorFunc.
func (b *Builder) ContinueOnError() *Builder {
	b.continueOnError = true
	return b
}

// SingleResourceType will cause the builder to error if the user specifies more than a single type
// of resource.
func (b *Builder) SingleResourceType() *Builder {
	b.singleResourceType = true
	return b
}

func (b *Builder) resourceMappings() ([]*meta.RESTMapping, error) {
	if len(b.resources) > 1 && b.singleResourceType {
		return nil, fmt.Errorf("you may only specify a single resource type")
	}
	mappings := []*meta.RESTMapping{}
	for _, r := range b.resources {
		version, kind, err := b.mapper.VersionAndKindForResource(r)
		if err != nil {
			return nil, err
		}
		mapping, err := b.mapper.RESTMapping(kind, version)
		if err != nil {
			return nil, err
		}
		mappings = append(mappings, mapping)
	}
	return mappings, nil
}

func (b *Builder) visitorResult() *Result {
	if len(b.errs) > 0 {
		return &Result{err: errors.NewAggregate(b.errs)}
	}

	// visit selectors
	if b.selector != nil {
		if len(b.name) != 0 {
			return &Result{err: fmt.Errorf("name cannot be provided when a selector is specified")}
		}
		if len(b.resources) == 0 {
			return &Result{err: fmt.Errorf("at least one resource must be specified to use a selector")}
		}
		// empty selector has different error message for paths being provided
		if len(b.paths) != 0 {
			if b.selector.Empty() {
				return &Result{err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
			} else {
				return &Result{err: fmt.Errorf("a selector may not be specified when path, URL, or stdin is provided as input")}
			}
		}
		mappings, err := b.resourceMappings()
		if err != nil {
			return &Result{err: err}
		}

		visitors := []Visitor{}
		for _, mapping := range mappings {
			client, err := b.mapper.ClientForMapping(mapping)
			if err != nil {
				return &Result{err: err}
			}
			selectorNamespace := b.namespace
			if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
				selectorNamespace = ""
			}
			visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, b.selector))
		}
		if b.continueOnError {
			return &Result{visitor: EagerVisitorList(visitors), sources: visitors}
		}
		return &Result{visitor: VisitorList(visitors), sources: visitors}
	}

	// visit single item specified by name
	if len(b.name) != 0 {
		if len(b.paths) != 0 {
			return &Result{singular: true, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
		}
		if len(b.resources) == 0 {
			return &Result{singular: true, err: fmt.Errorf("you must provide a resource and a resource name together")}
		}
		if len(b.resources) > 1 {
			return &Result{singular: true, err: fmt.Errorf("you must specify only one resource")}
		}
		mappings, err := b.resourceMappings()
		if err != nil {
			return &Result{singular: true, err: err}
		}
		mapping := mappings[0]
		if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
			b.namespace = ""
		} else {
			if len(b.namespace) == 0 {
				return &Result{singular: true, err: fmt.Errorf("namespace may not be empty when retrieving a resource by name")}
			}
		}
		client, err := b.mapper.ClientForMapping(mapping)
		if err != nil {
			return &Result{singular: true, err: err}
		}
		info := NewInfo(client, mappings[0], b.namespace, b.name)
		if err := info.Get(); err != nil {
			return &Result{singular: true, err: err}
		}
		return &Result{singular: true, visitor: info, sources: []Visitor{info}}
	}

	// visit items specified by paths
	if len(b.paths) != 0 {
		singular := !b.dir && !b.stream && len(b.paths) == 1
		if len(b.resources) != 0 {
			return &Result{singular: singular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify resource arguments as well")}
		}

		var visitors Visitor
		if b.continueOnError {
			visitors = EagerVisitorList(b.paths)
		} else {
			visitors = VisitorList(b.paths)
		}

		// only items from disk can be refetched
		if b.latest {
			// must flatten lists prior to fetching
			if b.flatten {
				visitors = NewFlattenListVisitor(visitors, b.mapper)
			}
			visitors = NewDecoratedVisitor(visitors, RetrieveLatest)
		}
		return &Result{singular: singular, visitor: visitors, sources: b.paths}
	}

	return &Result{err: fmt.Errorf("you must provide one or more resources by argument or filename")}
}

// Do returns a Result object with a Visitor for the resources identified by the Builder.
// The visitor will respect the error behavior specified by ContinueOnError. Note that stream
// inputs are consumed by the first execution - use Infos() or Object() on the Result to capture a list
// for further iteration.
func (b *Builder) Do() *Result {
	r := b.visitorResult()
	if r.err != nil {
		return r
	}
	if b.flatten {
		r.visitor = NewFlattenListVisitor(r.visitor, b.mapper)
	}
	helpers := []VisitorFunc{}
	if b.defaultNamespace {
		helpers = append(helpers, SetNamespace(b.namespace))
	}
	if b.requireNamespace {
		helpers = append(helpers, RequireNamespace(b.namespace))
	}
	helpers = append(helpers, FilterNamespace())
	r.visitor = NewDecoratedVisitor(r.visitor, helpers...)
	return r
}

func SplitResourceArgument(arg string) []string {
	set := util.NewStringSet()
	set.Insert(strings.Split(arg, ",")...)
	return set.List()
}
<|endoftext|>


<commit_before>package client

import (
	"os"
)

func (k Kubectl) Delete(namespace, kind, name string, opts DeleteOpts) error {
	argv := []string{
		"-n", namespace,
		kind, name,
	}
	if opts.Force {
		argv = append(argv, "--force")
	}

	cmd := k.ctl("delete", argv...)
	cmd.Stdout = os.Stderr
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin

	if err := cmd.Run(); err != nil {
		return err
	}

	return nil
}
<commit_msg>Bugfix/allow removal with altered state (#539)<commit_after>package client

import (
	"bytes"
	"os"
	"strings"
)

func (k Kubectl) Delete(namespace, kind, name string, opts DeleteOpts) error {
	argv := []string{
		"-n", namespace,
		kind, name,
	}
	if opts.Force {
		argv = append(argv, "--force")
	}

	cmd := k.ctl("delete", argv...)

	var stdout bytes.Buffer
	var stderr bytes.Buffer

	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	cmd.Stdin = os.Stdin

	if err := cmd.Run(); err != nil {
		if strings.Contains(stderr.String(), "Error from server (NotFound):") {
			print("Delete failed: " + stderr.String())
			return nil
		}
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 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 components

import (
	"strings"

	"github.com/golang/glog"
	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/loader"
)

// KubeletOptionsBuilder adds options for kubelets
type KubeletOptionsBuilder struct {
	Context *OptionsContext
}

var _ loader.OptionsBuilder = &KubeletOptionsBuilder{}

func (b *KubeletOptionsBuilder) BuildOptions(o interface{}) error {
	clusterSpec := o.(*kops.ClusterSpec)

	kubernetesVersion, err := KubernetesVersion(clusterSpec)
	if err != nil {
		return err
	}

	if clusterSpec.Kubelet == nil {
		clusterSpec.Kubelet = &kops.KubeletConfigSpec{}
	}
	if clusterSpec.MasterKubelet == nil {
		clusterSpec.MasterKubelet = &kops.KubeletConfigSpec{}
	}

	ip, err := WellKnownServiceIP(clusterSpec, 10)
	if err != nil {
		return err
	}

	// Standard options
	clusterSpec.Kubelet.EnableDebuggingHandlers = fi.Bool(true)
	clusterSpec.Kubelet.PodManifestPath = "/etc/kubernetes/manifests"
	clusterSpec.Kubelet.AllowPrivileged = fi.Bool(true)
	clusterSpec.Kubelet.LogLevel = fi.Int32(2)
	clusterSpec.Kubelet.ClusterDNS = ip.String()
	clusterSpec.Kubelet.ClusterDomain = clusterSpec.ClusterDNSDomain
	clusterSpec.Kubelet.NonMasqueradeCIDR = clusterSpec.NonMasqueradeCIDR

	if b.Context.IsKubernetesLT("1.7") {
		// babysit-daemons removed in 1.7
		clusterSpec.Kubelet.BabysitDaemons = fi.Bool(true)
	}

	clusterSpec.MasterKubelet.RegisterSchedulable = fi.Bool(false)
	// Replace the CIDR with a CIDR allocated by KCM (the default, but included for clarity)
	// We _do_ allow debugging handlers, so we can do logs
	// This does allow more access than we would like though
	clusterSpec.MasterKubelet.EnableDebuggingHandlers = fi.Bool(true)

	// In 1.5 we fixed this, but in 1.4 we need to set the PodCIDR on the master
	// so that hostNetwork pods can come up
	if kubernetesVersion.Major == 1 && kubernetesVersion.Minor <= 4 {
		// We bootstrap with a fake CIDR, but then this will be replaced (unless we're running with _isolated_master)
		clusterSpec.MasterKubelet.PodCIDR = "10.123.45.0/28"
	}

	// 1.5 deprecates the reconcile cidr option (and 1.6 removes it)
	if kubernetesVersion.Major == 1 && kubernetesVersion.Minor <= 4 {
		clusterSpec.MasterKubelet.ReconcileCIDR = fi.Bool(true)

		if fi.BoolValue(clusterSpec.IsolateMasters) {
			clusterSpec.MasterKubelet.ReconcileCIDR = fi.Bool(false)
		}

		usesKubenet, err := UsesKubenet(clusterSpec)
		if err != nil {
			return err
		}
		if usesKubenet {
			clusterSpec.Kubelet.ReconcileCIDR = fi.Bool(true)
		}
	}

	if kubernetesVersion.Major == 1 && kubernetesVersion.Minor >= 4 {
		// For pod eviction in low memory or empty disk situations
		if clusterSpec.Kubelet.EvictionHard == nil {
			evictionHard := []string{
				// TODO: Some people recommend 250Mi, but this would hurt small machines
				"memory.available<100Mi",

				// Disk eviction (evict old images)
				// We don't need to specify both, but it seems harmless / safer
				"nodefs.available<10%",
				"nodefs.inodesFree<5%",
				"imagefs.available<10%",
				"imagefs.inodesFree<5%",
			}
			clusterSpec.Kubelet.EvictionHard = fi.String(strings.Join(evictionHard, ","))
		}
	}

	if b.Context.IsKubernetesGTE("1.6") {
		// for 1.6+ use kubeconfig instead of api-servers
		const kubeconfigPath = "/var/lib/kubelet/kubeconfig"
		clusterSpec.Kubelet.KubeconfigPath = kubeconfigPath
		clusterSpec.MasterKubelet.KubeconfigPath = kubeconfigPath

		// Only pass require-kubeconfig to versions prior to 1.10; deprecated & being removed
		if b.Context.IsKubernetesLT("1.10") {
			clusterSpec.Kubelet.RequireKubeconfig = fi.Bool(true)
			clusterSpec.MasterKubelet.RequireKubeconfig = fi.Bool(true)
		}
	} else {
		// Legacy behaviour for <= 1.5
		clusterSpec.Kubelet.APIServers = "https://" + clusterSpec.MasterInternalName
		clusterSpec.MasterKubelet.APIServers = "http://127.0.0.1:8080"
	}

	// IsolateMasters enables the legacy behaviour, where master pods on a separate network
	// In newer versions of kubernetes, most of that functionality has been removed though
	if fi.BoolValue(clusterSpec.IsolateMasters) {
		clusterSpec.MasterKubelet.EnableDebuggingHandlers = fi.Bool(false)
		clusterSpec.MasterKubelet.HairpinMode = "none"
	}

	cloudProvider := kops.CloudProviderID(clusterSpec.CloudProvider)

	clusterSpec.Kubelet.CgroupRoot = "/"

	glog.V(1).Infof("Cloud Provider: %s", cloudProvider)
	if cloudProvider == kops.CloudProviderAWS {
		clusterSpec.Kubelet.CloudProvider = "aws"

		// For 1.6 we're using much cleaner cgroup hierarchies
		// but we keep the settings we've tested for k8s 1.5 and lower
		// (see https://github.com/kubernetes/kubernetes/pull/41349)
		if kubernetesVersion.Major == 1 && kubernetesVersion.Minor <= 5 {
			clusterSpec.Kubelet.CgroupRoot = "docker"
		}

		// Use the hostname from the AWS metadata service
		// if hostnameOverride is not set.
		if clusterSpec.Kubelet.HostnameOverride == "" {
			clusterSpec.Kubelet.HostnameOverride = "@aws"
		}
	}

	if cloudProvider == kops.CloudProviderDO {
		clusterSpec.Kubelet.CloudProvider = "external"
	}

	if cloudProvider == kops.CloudProviderGCE {
		clusterSpec.Kubelet.CloudProvider = "gce"
		clusterSpec.Kubelet.HairpinMode = "promiscuous-bridge"

		if clusterSpec.CloudConfig == nil {
			clusterSpec.CloudConfig = &kops.CloudConfiguration{}
		}
		clusterSpec.CloudConfig.Multizone = fi.Bool(true)
		clusterSpec.CloudConfig.NodeTags = fi.String(GCETagForRole(b.Context.ClusterName, kops.InstanceGroupRoleNode))
	}

	if cloudProvider == kops.CloudProviderVSphere {
		clusterSpec.Kubelet.CloudProvider = "vsphere"
		clusterSpec.Kubelet.HairpinMode = "promiscuous-bridge"
	}

	if cloudProvider == kops.CloudProviderOpenstack {
		clusterSpec.Kubelet.CloudProvider = "openstack"
	}

	if clusterSpec.ExternalCloudControllerManager != nil {
		clusterSpec.Kubelet.CloudProvider = "external"
	}

	usesKubenet, err := UsesKubenet(clusterSpec)
	if err != nil {
		return err
	}
	if usesKubenet {
		clusterSpec.Kubelet.NetworkPluginName = "kubenet"

		if kubernetesVersion.Major == 1 && kubernetesVersion.Minor >= 4 {
			// AWS MTU is 9001
			clusterSpec.Kubelet.NetworkPluginMTU = fi.Int32(9001)
		}
	}

	// Specify our pause image
	image := "gcr.io/google_containers/pause-amd64:3.0"
	if image, err = b.Context.AssetBuilder.RemapImage(image); err != nil {
		return err
	}
	clusterSpec.Kubelet.PodInfraContainerImage = image

	if clusterSpec.Kubelet.FeatureGates == nil {
		clusterSpec.Kubelet.FeatureGates = make(map[string]string)
	}
	if _, found := clusterSpec.Kubelet.FeatureGates["ExperimentalCriticalPodAnnotation"]; !found {
		if b.Context.IsKubernetesGTE("1.5.2") {
			clusterSpec.Kubelet.FeatureGates["ExperimentalCriticalPodAnnotation"] = "true"
		}
	}

	return nil
}
<commit_msg>modify check require-kubeconfig<commit_after>/*
Copyright 2016 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 components

import (
	"strings"

	"github.com/golang/glog"
	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/loader"
)

// KubeletOptionsBuilder adds options for kubelets
type KubeletOptionsBuilder struct {
	Context *OptionsContext
}

var _ loader.OptionsBuilder = &KubeletOptionsBuilder{}

func (b *KubeletOptionsBuilder) BuildOptions(o interface{}) error {
	clusterSpec := o.(*kops.ClusterSpec)

	kubernetesVersion, err := KubernetesVersion(clusterSpec)
	if err != nil {
		return err
	}

	if clusterSpec.Kubelet == nil {
		clusterSpec.Kubelet = &kops.KubeletConfigSpec{}
	}
	if clusterSpec.MasterKubelet == nil {
		clusterSpec.MasterKubelet = &kops.KubeletConfigSpec{}
	}

	ip, err := WellKnownServiceIP(clusterSpec, 10)
	if err != nil {
		return err
	}

	// Standard options
	clusterSpec.Kubelet.EnableDebuggingHandlers = fi.Bool(true)
	clusterSpec.Kubelet.PodManifestPath = "/etc/kubernetes/manifests"
	clusterSpec.Kubelet.AllowPrivileged = fi.Bool(true)
	clusterSpec.Kubelet.LogLevel = fi.Int32(2)
	clusterSpec.Kubelet.ClusterDNS = ip.String()
	clusterSpec.Kubelet.ClusterDomain = clusterSpec.ClusterDNSDomain
	clusterSpec.Kubelet.NonMasqueradeCIDR = clusterSpec.NonMasqueradeCIDR

	if b.Context.IsKubernetesLT("1.7") {
		// babysit-daemons removed in 1.7
		clusterSpec.Kubelet.BabysitDaemons = fi.Bool(true)
	}

	clusterSpec.MasterKubelet.RegisterSchedulable = fi.Bool(false)
	// Replace the CIDR with a CIDR allocated by KCM (the default, but included for clarity)
	// We _do_ allow debugging handlers, so we can do logs
	// This does allow more access than we would like though
	clusterSpec.MasterKubelet.EnableDebuggingHandlers = fi.Bool(true)

	// In 1.5 we fixed this, but in 1.4 we need to set the PodCIDR on the master
	// so that hostNetwork pods can come up
	if kubernetesVersion.Major == 1 && kubernetesVersion.Minor <= 4 {
		// We bootstrap with a fake CIDR, but then this will be replaced (unless we're running with _isolated_master)
		clusterSpec.MasterKubelet.PodCIDR = "10.123.45.0/28"
	}

	// 1.5 deprecates the reconcile cidr option (and 1.6 removes it)
	if kubernetesVersion.Major == 1 && kubernetesVersion.Minor <= 4 {
		clusterSpec.MasterKubelet.ReconcileCIDR = fi.Bool(true)

		if fi.BoolValue(clusterSpec.IsolateMasters) {
			clusterSpec.MasterKubelet.ReconcileCIDR = fi.Bool(false)
		}

		usesKubenet, err := UsesKubenet(clusterSpec)
		if err != nil {
			return err
		}
		if usesKubenet {
			clusterSpec.Kubelet.ReconcileCIDR = fi.Bool(true)
		}
	}

	if kubernetesVersion.Major == 1 && kubernetesVersion.Minor >= 4 {
		// For pod eviction in low memory or empty disk situations
		if clusterSpec.Kubelet.EvictionHard == nil {
			evictionHard := []string{
				// TODO: Some people recommend 250Mi, but this would hurt small machines
				"memory.available<100Mi",

				// Disk eviction (evict old images)
				// We don't need to specify both, but it seems harmless / safer
				"nodefs.available<10%",
				"nodefs.inodesFree<5%",
				"imagefs.available<10%",
				"imagefs.inodesFree<5%",
			}
			clusterSpec.Kubelet.EvictionHard = fi.String(strings.Join(evictionHard, ","))
		}
	}

	if b.Context.IsKubernetesGTE("1.6") {
		// for 1.6+ use kubeconfig instead of api-servers
		const kubeconfigPath = "/var/lib/kubelet/kubeconfig"
		clusterSpec.Kubelet.KubeconfigPath = kubeconfigPath
		clusterSpec.MasterKubelet.KubeconfigPath = kubeconfigPath

		// Only pass require-kubeconfig to versions prior to 1.9; deprecated & being removed
		if b.Context.IsKubernetesLT("1.9") {
			clusterSpec.Kubelet.RequireKubeconfig = fi.Bool(true)
			clusterSpec.MasterKubelet.RequireKubeconfig = fi.Bool(true)
		}
	} else {
		// Legacy behaviour for <= 1.5
		clusterSpec.Kubelet.APIServers = "https://" + clusterSpec.MasterInternalName
		clusterSpec.MasterKubelet.APIServers = "http://127.0.0.1:8080"
	}

	// IsolateMasters enables the legacy behaviour, where master pods on a separate network
	// In newer versions of kubernetes, most of that functionality has been removed though
	if fi.BoolValue(clusterSpec.IsolateMasters) {
		clusterSpec.MasterKubelet.EnableDebuggingHandlers = fi.Bool(false)
		clusterSpec.MasterKubelet.HairpinMode = "none"
	}

	cloudProvider := kops.CloudProviderID(clusterSpec.CloudProvider)

	clusterSpec.Kubelet.CgroupRoot = "/"

	glog.V(1).Infof("Cloud Provider: %s", cloudProvider)
	if cloudProvider == kops.CloudProviderAWS {
		clusterSpec.Kubelet.CloudProvider = "aws"

		// For 1.6 we're using much cleaner cgroup hierarchies
		// but we keep the settings we've tested for k8s 1.5 and lower
		// (see https://github.com/kubernetes/kubernetes/pull/41349)
		if kubernetesVersion.Major == 1 && kubernetesVersion.Minor <= 5 {
			clusterSpec.Kubelet.CgroupRoot = "docker"
		}

		// Use the hostname from the AWS metadata service
		// if hostnameOverride is not set.
		if clusterSpec.Kubelet.HostnameOverride == "" {
			clusterSpec.Kubelet.HostnameOverride = "@aws"
		}
	}

	if cloudProvider == kops.CloudProviderDO {
		clusterSpec.Kubelet.CloudProvider = "external"
	}

	if cloudProvider == kops.CloudProviderGCE {
		clusterSpec.Kubelet.CloudProvider = "gce"
		clusterSpec.Kubelet.HairpinMode = "promiscuous-bridge"

		if clusterSpec.CloudConfig == nil {
			clusterSpec.CloudConfig = &kops.CloudConfiguration{}
		}
		clusterSpec.CloudConfig.Multizone = fi.Bool(true)
		clusterSpec.CloudConfig.NodeTags = fi.String(GCETagForRole(b.Context.ClusterName, kops.InstanceGroupRoleNode))
	}

	if cloudProvider == kops.CloudProviderVSphere {
		clusterSpec.Kubelet.CloudProvider = "vsphere"
		clusterSpec.Kubelet.HairpinMode = "promiscuous-bridge"
	}

	if cloudProvider == kops.CloudProviderOpenstack {
		clusterSpec.Kubelet.CloudProvider = "openstack"
	}

	if clusterSpec.ExternalCloudControllerManager != nil {
		clusterSpec.Kubelet.CloudProvider = "external"
	}

	usesKubenet, err := UsesKubenet(clusterSpec)
	if err != nil {
		return err
	}
	if usesKubenet {
		clusterSpec.Kubelet.NetworkPluginName = "kubenet"

		if kubernetesVersion.Major == 1 && kubernetesVersion.Minor >= 4 {
			// AWS MTU is 9001
			clusterSpec.Kubelet.NetworkPluginMTU = fi.Int32(9001)
		}
	}

	// Specify our pause image
	image := "gcr.io/google_containers/pause-amd64:3.0"
	if image, err = b.Context.AssetBuilder.RemapImage(image); err != nil {
		return err
	}
	clusterSpec.Kubelet.PodInfraContainerImage = image

	if clusterSpec.Kubelet.FeatureGates == nil {
		clusterSpec.Kubelet.FeatureGates = make(map[string]string)
	}
	if _, found := clusterSpec.Kubelet.FeatureGates["ExperimentalCriticalPodAnnotation"]; !found {
		if b.Context.IsKubernetesGTE("1.5.2") {
			clusterSpec.Kubelet.FeatureGates["ExperimentalCriticalPodAnnotation"] = "true"
		}
	}

	return nil
}
<|endoftext|>