<commit_before>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package types declares the data types and implements
// the algorithms for type-checking of Go packages. Use
// Config.Check to invoke the type checker for a package.
// Alternatively, create a new type checked with NewChecker
// and invoke it incrementally by calling Checker.Files.
//
// Type-checking consists of several interdependent phases:
//
// Name resolution maps each identifier (ast.Ident) in the program to the
// language object (Object) it denotes.
// Use Info.{Defs,Uses,Implicits} for the results of name resolution.
//
// Constant folding computes the exact constant value (constant.Value)
// for every expression (ast.Expr) that is a compile-time constant.
// Use Info.Types[expr].Value for the results of constant folding.
//
// Type inference computes the type (Type) of every expression (ast.Expr)
// and checks for compliance with the language specification.
// Use Info.Types[expr].Type for the results of type inference.
//
// For a tutorial, see https://golang.org/s/types-tutorial.
//
package types // import "go/types"

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/constant"
	"go/token"
)

// An Error describes a type-checking error; it implements the error interface.
// A "soft" error is an error that still permits a valid interpretation of a
// package (such as "unused variable"); "hard" errors may lead to unpredictable
// behavior if ignored.
type Error struct {
	Fset *token.FileSet // file set for interpretation of Pos
	Pos  token.Pos      // error position
	Msg  string         // error message
	Soft bool           // if set, error is "soft"
}

// Error returns an error string formatted as follows:
// filename:line:column: message
func (err Error) Error() string {
	return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg)
}

// An Importer resolves import paths to Packages.
//
// CAUTION: This interface does not support the import of locally
// vendored packages. See also https://golang.org/s/go15vendor.
// If possible, external implementations should implement Importer2.
type Importer interface {
	// Import returns the imported package for the given import
	// path, or an error if the package couldn't be imported.
	// Two calls to Import with the same path and srcDir return
	// the same package.
	Import(path string) (*Package, error)
}

// ImportMode is reserved for future use.
type ImportMode int

// An Importer2 resolves import paths to packages; it
// supports vendoring per https://golang.org/s/go15vendor.
// Use go/importer to obtain an Importer2 implementation.
type Importer2 interface {
	// Importer is present for backward-compatibility. Calling
	// Import(path) is the same as calling Import(path, "", 0);
	// i.e., locally vendored packages may not be found.
	// The types package does not call Import if an Importer2
	// is present.
	Importer

	// Import2 returns the imported package for the given import
	// path when imported by the package in srcDir, or an error
	// if the package couldn't be imported. The mode value must
	// be 0; it is reserved for future use.
	// Two calls to Import2 with the same path and srcDir return
	// the same package.
	Import2(path, srcDir string, mode ImportMode) (*Package, error)
}

// A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
	// If IgnoreFuncBodies is set, function bodies are not
	// type-checked.
	IgnoreFuncBodies bool

	// If FakeImportC is set, `import "C"` (for packages requiring Cgo)
	// declares an empty "C" package and errors are omitted for qualified
	// identifiers referring to package C (which won't find an object).
	// This feature is intended for the standard library cmd/api tool.
	//
	// Caution: Effects may be unpredictable due to follow-up errors.
	//          Do not use casually!
	FakeImportC bool

	// If Error != nil, it is called with each error found
	// during type checking; err has dynamic type Error.
	// Secondary errors (for instance, to enumerate all types
	// involved in an invalid recursive type declaration) have
	// error strings that start with a '\t' character.
	// If Error == nil, type-checking stops with the first
	// error found.
	Error func(err error)

	// An importer is used to import packages referred to from
	// import declarations.
	// If the installed importer implements Importer2, Import2
	// is called instead of Import.
	// An error is reported if an importer is needed but none
	// was installed.
	Importer Importer

	// If Sizes != nil, it provides the sizing functions for package unsafe.
	// Otherwise &StdSizes{WordSize: 8, MaxAlign: 8} is used instead.
	Sizes Sizes

	// If DisableUnusedImportCheck is set, packages are not checked
	// for unused imports.
	DisableUnusedImportCheck bool
}

// Info holds result type information for a type-checked package.
// Only the information for which a map is provided is collected.
// If the package has type errors, the collected information may
// be incomplete.
type Info struct {
	// Types maps expressions to their types, and for constant
	// expressions, their values. Invalid expressions are omitted.
	//
	// For (possibly parenthesized) identifiers denoting built-in
	// functions, the recorded signatures are call-site specific:
	// if the call result is not a constant, the recorded type is
	// an argument-specific signature. Otherwise, the recorded type
	// is invalid.
	//
	// Identifiers on the lhs of declarations (i.e., the identifiers
	// which are being declared) are collected in the Defs map.
	// Identifiers denoting packages are collected in the Uses maps.
	Types map[ast.Expr]TypeAndValue

	// Defs maps identifiers to the objects they define (including
	// package names, dots "." of dot-imports, and blank "_" identifiers).
	// For identifiers that do not denote objects (e.g., the package name
	// in package clauses, or symbolic variables t in t := x.(type) of
	// type switch headers), the corresponding objects are nil.
	//
	// For an anonymous field, Defs returns the field *Var it defines.
	//
	// Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
	Defs map[*ast.Ident]Object

	// Uses maps identifiers to the objects they denote.
	//
	// For an anonymous field, Uses returns the *TypeName it denotes.
	//
	// Invariant: Uses[id].Pos() != id.Pos()
	Uses map[*ast.Ident]Object

	// Implicits maps nodes to their implicitly declared objects, if any.
	// The following node and object types may appear:
	//
	//	node               declared object
	//
	//	*ast.ImportSpec    *PkgName for dot-imports and imports without renames
	//	*ast.CaseClause    type-specific *Var for each type switch case clause (incl. default)
	//      *ast.Field         anonymous parameter *Var
	//
	Implicits map[ast.Node]Object

	// Selections maps selector expressions (excluding qualified identifiers)
	// to their corresponding selections.
	Selections map[*ast.SelectorExpr]*Selection

	// Scopes maps ast.Nodes to the scopes they define. Package scopes are not
	// associated with a specific node but with all files belonging to a package.
	// Thus, the package scope can be found in the type-checked Package object.
	// Scopes nest, with the Universe scope being the outermost scope, enclosing
	// the package scope, which contains (one or more) files scopes, which enclose
	// function scopes which in turn enclose statement and function literal scopes.
	// Note that even though package-level functions are declared in the package
	// scope, the function scopes are embedded in the file scope of the file
	// containing the function declaration.
	//
	// The following node types may appear in Scopes:
	//
	//	*ast.File
	//	*ast.FuncType
	//	*ast.BlockStmt
	//	*ast.IfStmt
	//	*ast.SwitchStmt
	//	*ast.TypeSwitchStmt
	//	*ast.CaseClause
	//	*ast.CommClause
	//	*ast.ForStmt
	//	*ast.RangeStmt
	//
	Scopes map[ast.Node]*Scope

	// InitOrder is the list of package-level initializers in the order in which
	// they must be executed. Initializers referring to variables related by an
	// initialization dependency appear in topological order, the others appear
	// in source order. Variables without an initialization expression do not
	// appear in this list.
	InitOrder []*Initializer
}

// TypeOf returns the type of expression e, or nil if not found.
// Precondition: the Types, Uses and Defs maps are populated.
//
func (info *Info) TypeOf(e ast.Expr) Type {
	if t, ok := info.Types[e]; ok {
		return t.Type
	}
	if id, _ := e.(*ast.Ident); id != nil {
		if obj := info.ObjectOf(id); obj != nil {
			return obj.Type()
		}
	}
	return nil
}

// ObjectOf returns the object denoted by the specified id,
// or nil if not found.
//
// If id is an anonymous struct field, ObjectOf returns the field (*Var)
// it uses, not the type (*TypeName) it defines.
//
// Precondition: the Uses and Defs maps are populated.
//
func (info *Info) ObjectOf(id *ast.Ident) Object {
	if obj, _ := info.Defs[id]; obj != nil {
		return obj
	}
	return info.Uses[id]
}

// TypeAndValue reports the type and value (for constants)
// of the corresponding expression.
type TypeAndValue struct {
	mode  operandMode
	Type  Type
	Value constant.Value
}

// TODO(gri) Consider eliminating the IsVoid predicate. Instead, report
// "void" values as regular values but with the empty tuple type.

// IsVoid reports whether the corresponding expression
// is a function call without results.
func (tv TypeAndValue) IsVoid() bool {
	return tv.mode == novalue
}

// IsType reports whether the corresponding expression specifies a type.
func (tv TypeAndValue) IsType() bool {
	return tv.mode == typexpr
}

// IsBuiltin reports whether the corresponding expression denotes
// a (possibly parenthesized) built-in function.
func (tv TypeAndValue) IsBuiltin() bool {
	return tv.mode == builtin
}

// IsValue reports whether the corresponding expression is a value.
// Builtins are not considered values. Constant values have a non-
// nil Value.
func (tv TypeAndValue) IsValue() bool {
	switch tv.mode {
	case constant_, variable, mapindex, value, commaok:
		return true
	}
	return false
}

// IsNil reports whether the corresponding expression denotes the
// predeclared value nil.
func (tv TypeAndValue) IsNil() bool {
	return tv.mode == value && tv.Type == Typ[UntypedNil]
}

// Addressable reports whether the corresponding expression
// is addressable (https://golang.org/ref/spec#Address_operators).
func (tv TypeAndValue) Addressable() bool {
	return tv.mode == variable
}

// Assignable reports whether the corresponding expression
// is assignable to (provided a value of the right type).
func (tv TypeAndValue) Assignable() bool {
	return tv.mode == variable || tv.mode == mapindex
}

// HasOk reports whether the corresponding expression may be
// used on the lhs of a comma-ok assignment.
func (tv TypeAndValue) HasOk() bool {
	return tv.mode == commaok || tv.mode == mapindex
}

// An Initializer describes a package-level variable, or a list of variables in case
// of a multi-valued initialization expression, and the corresponding initialization
// expression.
type Initializer struct {
	Lhs []*Var // var Lhs = Rhs
	Rhs ast.Expr
}

func (init *Initializer) String() string {
	var buf bytes.Buffer
	for i, lhs := range init.Lhs {
		if i > 0 {
			buf.WriteString(", ")
		}
		buf.WriteString(lhs.Name())
	}
	buf.WriteString(" = ")
	WriteExpr(&buf, init.Rhs)
	return buf.String()
}

// Check type-checks a package and returns the resulting package object and
// the first error if any. Additionally, if info != nil, Check populates each
// of the non-nil maps in the Info struct.
//
// The package is marked as complete if no errors occurred, otherwise it is
// incomplete. See Config.Error for controlling behavior in the presence of
// errors.
//
// The package is specified by a list of *ast.Files and corresponding
// file set, and the package path the package is identified with.
// The clean path must not be empty or dot (".").
func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) {
	pkg := NewPackage(path, "")
	return pkg, NewChecker(conf, fset, pkg, info).Files(files)
}

// AssertableTo reports whether a value of type V can be asserted to have type T.
func AssertableTo(V *Interface, T Type) bool {
	m, _ := assertableTo(V, T)
	return m == nil
}

// AssignableTo reports whether a value of type V is assignable to a variable of type T.
func AssignableTo(V, T Type) bool {
	x := operand{mode: value, typ: V}
	return x.assignableTo(nil, T, nil) // config not needed for non-constant x
}

// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
func ConvertibleTo(V, T Type) bool {
	x := operand{mode: value, typ: V}
	return x.convertibleTo(nil, T) // config not needed for non-constant x
}

// Implements reports whether type V implements interface T.
func Implements(V Type, T *Interface) bool {
	f, _ := MissingMethod(V, T, true)
	return f == nil
}
<commit_msg>go/types: slightly better doc strings<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package types declares the data types and implements
// the algorithms for type-checking of Go packages. Use
// Config.Check to invoke the type checker for a package.
// Alternatively, create a new type checked with NewChecker
// and invoke it incrementally by calling Checker.Files.
//
// Type-checking consists of several interdependent phases:
//
// Name resolution maps each identifier (ast.Ident) in the program to the
// language object (Object) it denotes.
// Use Info.{Defs,Uses,Implicits} for the results of name resolution.
//
// Constant folding computes the exact constant value (constant.Value)
// for every expression (ast.Expr) that is a compile-time constant.
// Use Info.Types[expr].Value for the results of constant folding.
//
// Type inference computes the type (Type) of every expression (ast.Expr)
// and checks for compliance with the language specification.
// Use Info.Types[expr].Type for the results of type inference.
//
// For a tutorial, see https://golang.org/s/types-tutorial.
//
package types // import "go/types"

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/constant"
	"go/token"
)

// An Error describes a type-checking error; it implements the error interface.
// A "soft" error is an error that still permits a valid interpretation of a
// package (such as "unused variable"); "hard" errors may lead to unpredictable
// behavior if ignored.
type Error struct {
	Fset *token.FileSet // file set for interpretation of Pos
	Pos  token.Pos      // error position
	Msg  string         // error message
	Soft bool           // if set, error is "soft"
}

// Error returns an error string formatted as follows:
// filename:line:column: message
func (err Error) Error() string {
	return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg)
}

// An Importer resolves import paths to Packages.
//
// CAUTION: This interface does not support the import of locally
// vendored packages. See also https://golang.org/s/go15vendor.
// If possible, external implementations should implement Importer2.
type Importer interface {
	// Import returns the imported package for the given import
	// path, or an error if the package couldn't be imported.
	// Two calls to Import with the same path and srcDir return
	// the same package.
	Import(path string) (*Package, error)
}

// ImportMode is reserved for future use.
type ImportMode int

// An Importer2 resolves import paths to packages; it
// supports vendoring per https://golang.org/s/go15vendor.
// Use go/importer to obtain an Importer2 implementation.
type Importer2 interface {
	// Importer is present for backward-compatibility. Calling
	// Import(path) is the same as calling Import(path, "", 0);
	// i.e., locally vendored packages may not be found.
	// The types package does not call Import if an Importer2
	// is present.
	Importer

	// Import2 returns the imported package for the given import
	// path when imported by the package in srcDir, or an error
	// if the package couldn't be imported. The mode value must
	// be 0; it is reserved for future use.
	// Two calls to Import2 with the same path and srcDir return
	// the same package.
	Import2(path, srcDir string, mode ImportMode) (*Package, error)
}

// A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
	// If IgnoreFuncBodies is set, function bodies are not
	// type-checked.
	IgnoreFuncBodies bool

	// If FakeImportC is set, `import "C"` (for packages requiring Cgo)
	// declares an empty "C" package and errors are omitted for qualified
	// identifiers referring to package C (which won't find an object).
	// This feature is intended for the standard library cmd/api tool.
	//
	// Caution: Effects may be unpredictable due to follow-up errors.
	//          Do not use casually!
	FakeImportC bool

	// If Error != nil, it is called with each error found
	// during type checking; err has dynamic type Error.
	// Secondary errors (for instance, to enumerate all types
	// involved in an invalid recursive type declaration) have
	// error strings that start with a '\t' character.
	// If Error == nil, type-checking stops with the first
	// error found.
	Error func(err error)

	// An importer is used to import packages referred to from
	// import declarations.
	// If the installed importer implements Importer2, the type
	// checker calls Import2 instead of Import.
	// The type checker reports an error if an importer is needed
	// but none was installed.
	Importer Importer

	// If Sizes != nil, it provides the sizing functions for package unsafe.
	// Otherwise &StdSizes{WordSize: 8, MaxAlign: 8} is used instead.
	Sizes Sizes

	// If DisableUnusedImportCheck is set, packages are not checked
	// for unused imports.
	DisableUnusedImportCheck bool
}

// Info holds result type information for a type-checked package.
// Only the information for which a map is provided is collected.
// If the package has type errors, the collected information may
// be incomplete.
type Info struct {
	// Types maps expressions to their types, and for constant
	// expressions, their values. Invalid expressions are omitted.
	//
	// For (possibly parenthesized) identifiers denoting built-in
	// functions, the recorded signatures are call-site specific:
	// if the call result is not a constant, the recorded type is
	// an argument-specific signature. Otherwise, the recorded type
	// is invalid.
	//
	// Identifiers on the lhs of declarations (i.e., the identifiers
	// which are being declared) are collected in the Defs map.
	// Identifiers denoting packages are collected in the Uses maps.
	Types map[ast.Expr]TypeAndValue

	// Defs maps identifiers to the objects they define (including
	// package names, dots "." of dot-imports, and blank "_" identifiers).
	// For identifiers that do not denote objects (e.g., the package name
	// in package clauses, or symbolic variables t in t := x.(type) of
	// type switch headers), the corresponding objects are nil.
	//
	// For an anonymous field, Defs returns the field *Var it defines.
	//
	// Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
	Defs map[*ast.Ident]Object

	// Uses maps identifiers to the objects they denote.
	//
	// For an anonymous field, Uses returns the *TypeName it denotes.
	//
	// Invariant: Uses[id].Pos() != id.Pos()
	Uses map[*ast.Ident]Object

	// Implicits maps nodes to their implicitly declared objects, if any.
	// The following node and object types may appear:
	//
	//	node               declared object
	//
	//	*ast.ImportSpec    *PkgName for dot-imports and imports without renames
	//	*ast.CaseClause    type-specific *Var for each type switch case clause (incl. default)
	//      *ast.Field         anonymous parameter *Var
	//
	Implicits map[ast.Node]Object

	// Selections maps selector expressions (excluding qualified identifiers)
	// to their corresponding selections.
	Selections map[*ast.SelectorExpr]*Selection

	// Scopes maps ast.Nodes to the scopes they define. Package scopes are not
	// associated with a specific node but with all files belonging to a package.
	// Thus, the package scope can be found in the type-checked Package object.
	// Scopes nest, with the Universe scope being the outermost scope, enclosing
	// the package scope, which contains (one or more) files scopes, which enclose
	// function scopes which in turn enclose statement and function literal scopes.
	// Note that even though package-level functions are declared in the package
	// scope, the function scopes are embedded in the file scope of the file
	// containing the function declaration.
	//
	// The following node types may appear in Scopes:
	//
	//	*ast.File
	//	*ast.FuncType
	//	*ast.BlockStmt
	//	*ast.IfStmt
	//	*ast.SwitchStmt
	//	*ast.TypeSwitchStmt
	//	*ast.CaseClause
	//	*ast.CommClause
	//	*ast.ForStmt
	//	*ast.RangeStmt
	//
	Scopes map[ast.Node]*Scope

	// InitOrder is the list of package-level initializers in the order in which
	// they must be executed. Initializers referring to variables related by an
	// initialization dependency appear in topological order, the others appear
	// in source order. Variables without an initialization expression do not
	// appear in this list.
	InitOrder []*Initializer
}

// TypeOf returns the type of expression e, or nil if not found.
// Precondition: the Types, Uses and Defs maps are populated.
//
func (info *Info) TypeOf(e ast.Expr) Type {
	if t, ok := info.Types[e]; ok {
		return t.Type
	}
	if id, _ := e.(*ast.Ident); id != nil {
		if obj := info.ObjectOf(id); obj != nil {
			return obj.Type()
		}
	}
	return nil
}

// ObjectOf returns the object denoted by the specified id,
// or nil if not found.
//
// If id is an anonymous struct field, ObjectOf returns the field (*Var)
// it uses, not the type (*TypeName) it defines.
//
// Precondition: the Uses and Defs maps are populated.
//
func (info *Info) ObjectOf(id *ast.Ident) Object {
	if obj, _ := info.Defs[id]; obj != nil {
		return obj
	}
	return info.Uses[id]
}

// TypeAndValue reports the type and value (for constants)
// of the corresponding expression.
type TypeAndValue struct {
	mode  operandMode
	Type  Type
	Value constant.Value
}

// TODO(gri) Consider eliminating the IsVoid predicate. Instead, report
// "void" values as regular values but with the empty tuple type.

// IsVoid reports whether the corresponding expression
// is a function call without results.
func (tv TypeAndValue) IsVoid() bool {
	return tv.mode == novalue
}

// IsType reports whether the corresponding expression specifies a type.
func (tv TypeAndValue) IsType() bool {
	return tv.mode == typexpr
}

// IsBuiltin reports whether the corresponding expression denotes
// a (possibly parenthesized) built-in function.
func (tv TypeAndValue) IsBuiltin() bool {
	return tv.mode == builtin
}

// IsValue reports whether the corresponding expression is a value.
// Builtins are not considered values. Constant values have a non-
// nil Value.
func (tv TypeAndValue) IsValue() bool {
	switch tv.mode {
	case constant_, variable, mapindex, value, commaok:
		return true
	}
	return false
}

// IsNil reports whether the corresponding expression denotes the
// predeclared value nil.
func (tv TypeAndValue) IsNil() bool {
	return tv.mode == value && tv.Type == Typ[UntypedNil]
}

// Addressable reports whether the corresponding expression
// is addressable (https://golang.org/ref/spec#Address_operators).
func (tv TypeAndValue) Addressable() bool {
	return tv.mode == variable
}

// Assignable reports whether the corresponding expression
// is assignable to (provided a value of the right type).
func (tv TypeAndValue) Assignable() bool {
	return tv.mode == variable || tv.mode == mapindex
}

// HasOk reports whether the corresponding expression may be
// used on the lhs of a comma-ok assignment.
func (tv TypeAndValue) HasOk() bool {
	return tv.mode == commaok || tv.mode == mapindex
}

// An Initializer describes a package-level variable, or a list of variables in case
// of a multi-valued initialization expression, and the corresponding initialization
// expression.
type Initializer struct {
	Lhs []*Var // var Lhs = Rhs
	Rhs ast.Expr
}

func (init *Initializer) String() string {
	var buf bytes.Buffer
	for i, lhs := range init.Lhs {
		if i > 0 {
			buf.WriteString(", ")
		}
		buf.WriteString(lhs.Name())
	}
	buf.WriteString(" = ")
	WriteExpr(&buf, init.Rhs)
	return buf.String()
}

// Check type-checks a package and returns the resulting package object and
// the first error if any. Additionally, if info != nil, Check populates each
// of the non-nil maps in the Info struct.
//
// The package is marked as complete if no errors occurred, otherwise it is
// incomplete. See Config.Error for controlling behavior in the presence of
// errors.
//
// The package is specified by a list of *ast.Files and corresponding
// file set, and the package path the package is identified with.
// The clean path must not be empty or dot (".").
func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) {
	pkg := NewPackage(path, "")
	return pkg, NewChecker(conf, fset, pkg, info).Files(files)
}

// AssertableTo reports whether a value of type V can be asserted to have type T.
func AssertableTo(V *Interface, T Type) bool {
	m, _ := assertableTo(V, T)
	return m == nil
}

// AssignableTo reports whether a value of type V is assignable to a variable of type T.
func AssignableTo(V, T Type) bool {
	x := operand{mode: value, typ: V}
	return x.assignableTo(nil, T, nil) // config not needed for non-constant x
}

// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
func ConvertibleTo(V, T Type) bool {
	x := operand{mode: value, typ: V}
	return x.convertibleTo(nil, T) // config not needed for non-constant x
}

// Implements reports whether type V implements interface T.
func Implements(V Type, T *Interface) bool {
	f, _ := MissingMethod(V, T, true)
	return f == nil
}
<|endoftext|>


<commit_before>// Copyright 2017 冯立强 fenglq@tingyun.com.  All rights reserved.

//配置信息缓存的性能优化版本,无锁访问, array存储
package cache_config

type Configuration struct {
	CStrings  Strings
	CBools    Bools
	CIntegers Integers
}

func (c *Configuration) Init(stringMax, boolMax, integerMax int) {
	c.CStrings.Init(stringMax)
	c.CBools.Init(boolMax)
	c.CIntegers.Init(integerMax)
}

func (c *Configuration) Update(strings, bools, integers map[string]int, key string, value interface{}) bool {
	if index, found := strings[key]; found {
		if v, ok := value.(string); !ok {
			return false
		} else {
			return c.CStrings.Update(index, v)
		}
	}
	if index, found := bools[key]; found {
		if v, ok := value.(bool); !ok {
			return false
		} else {
			return c.CBools.Update(index, v)
		}
	}
	if index, found := integers[key]; found {
		if v, ok := value.(int); !ok {
			return false
		} else {
			return c.CIntegers.Update(index, int64(v))
		}
	}
	return false
}
func (c *Configuration) Commit() {
	c.CStrings.Commit()
	c.CIntegers.Commit()
	c.CBools.Commit()
}
<commit_msg>Fix read int config value bug<commit_after>// Copyright 2017 冯立强 fenglq@tingyun.com.  All rights reserved.

//配置信息缓存的性能优化版本,无锁访问, array存储
package cache_config

import (
	"errors"
	"fmt"
)

type Configuration struct {
	CStrings  Strings
	CBools    Bools
	CIntegers Integers
}

func (c *Configuration) Init(stringMax, boolMax, integerMax int) {
	c.CStrings.Init(stringMax)
	c.CBools.Init(boolMax)
	c.CIntegers.Init(integerMax)
}

func readInt(v interface{}) (error, int) {
	switch r := v.(type) {
	case float64:
		return nil, int(r)
	case float32:
		return nil, int(r)
	case int:
		return nil, r
	case int32:
		return nil, int(r)
	case int64:
		return nil, int(r)
	case uint32:
		return nil, int(r)
	case uint64:
		return nil, int(r)
	default:
		return errors.New(fmt.Sprint(v, ":  not int value.")), 0
	}
}

func (c *Configuration) Update(strings, bools, integers map[string]int, key string, value interface{}) bool {
	if index, found := strings[key]; found {
		if v, ok := value.(string); !ok {
			return false
		} else {
			return c.CStrings.Update(index, v)
		}
	}
	if index, found := bools[key]; found {
		if v, ok := value.(bool); !ok {
			return false
		} else {
			return c.CBools.Update(index, v)
		}
	}
	if index, found := integers[key]; found {
		if err, vl := readInt(value); err == nil {
			return c.CIntegers.Update(index, int64(vl))
		}
		return false
	}
	return false
}
func (c *Configuration) Commit() {
	c.CStrings.Commit()
	c.CIntegers.Commit()
	c.CBools.Commit()
}
<|endoftext|>


<commit_before>package gotype

import "errors"

var (
	errNotInitialized           = errors.New("Unfolder is not initialized")
	errInvalidState             = errors.New("invalid state")
	errUnsupported              = errors.New("unsupported")
	errMapRequiresStringKey     = errors.New("map requires string key")
	errSquashNeedObject         = errors.New("require map or struct when using squash/inline")
	errNilInput                 = errors.New("nil input")
	errRequiresPointer          = errors.New("requires pointer")
	errKeyIntoNonStruct         = errors.New("key for non-structure target")
	errUnexpectedObjectKey      = errors.New("unexpected object key")
	errRequiresPrimitive        = errors.New("requires primitive target to set a boolean value")
	errRequiresBoolReceiver     = errors.New("requires bool receiver")
	errIncompatibleTypes        = errors.New("can not assign to incompatible go type")
	errStartArrayWaitingForKey  = errors.New("start array while waiting for object field name")
	errStartObjectWaitingForKey = errors.New("start object while waiting for object field name")
	errExpectedArrayNotObject   = errors.New("expected array but received object")
	errExpectedObjectNotArray   = errors.New("expected object but received array")
	errUnexpectedArrayStart     = errors.New("unexpected array start")
	errUnexpectedObjectStart    = errors.New("unexpected object start")
	errExpectedObjectKey        = errors.New("waiting for object key or object end marker")
	errExpectedArray            = errors.New("expected array")
	errExpectedObject           = errors.New("expected object")
	errExpectedObjectValue      = errors.New("expected object value")
)

func errTODO() error {
	panic(errors.New("TODO"))
}

func visitErrTODO(V visitor, v interface{}) error {
	return errTODO()
}
<commit_msg>add missing error types<commit_after>package gotype

import "errors"

var (
	errNotInitialized           = errors.New("Unfolder is not initialized")
	errInvalidState             = errors.New("invalid state")
	errUnsupported              = errors.New("unsupported")
	errMapRequiresStringKey     = errors.New("map requires string key")
	errSquashNeedObject         = errors.New("require map or struct when using squash/inline")
	errNilInput                 = errors.New("nil input")
	errRequiresPointer          = errors.New("requires pointer")
	errKeyIntoNonStruct         = errors.New("key for non-structure target")
	errUnexpectedObjectKey      = errors.New("unexpected object key")
	errRequiresPrimitive        = errors.New("requires primitive target to set a boolean value")
	errRequiresBoolReceiver     = errors.New("requires bool receiver")
	errIncompatibleTypes        = errors.New("can not assign to incompatible go type")
	errStartArrayWaitingForKey  = errors.New("start array while waiting for object field name")
	errStartObjectWaitingForKey = errors.New("start object while waiting for object field name")
	errExpectedArrayNotObject   = errors.New("expected array but received object")
	errExpectedObjectNotArray   = errors.New("expected object but received array")
	errUnexpectedArrayStart     = errors.New("unexpected array start")
	errUnexpectedObjectStart    = errors.New("unexpected object start")
	errExpectedObjectKey        = errors.New("waiting for object key or object end marker")
	errExpectedArray            = errors.New("expected array")
	errExpectedObject           = errors.New("expected object")
	errExpectedObjectValue      = errors.New("expected object value")
	errExpectedObjectClose      = errors.New("missing object close")
)

func errTODO() error {
	panic(errors.New("TODO"))
}

func visitErrTODO(V visitor, v interface{}) error {
	return errTODO()
}
<|endoftext|>


<commit_before>package poloniex

import (
	"encoding/json"
	"fmt"
	"net/url"
	"time"

	"github.com/franela/goreq"
	"github.com/k0kubun/pp"
)

type (
	Ticker      map[string]TickerEntry
	TickerEntry struct {
		Last        float64 `json:",string"`
		Ask         float64 `json:"lowestAsk,string"`
		Bid         float64 `json:"highestBid,string"`
		Change      float64 `json:"percentChange,string"`
		BaseVolume  float64 `json:"baseVolume,string"`
		QuoteVolume float64 `json:"quoteVolume,string"`
		IsFrozen    int64   `json:"isFrozen,string"`
		ID          int64   `json:"id"`
	}

	DailyVolume          map[string]DailyVolumeEntry
	DailyVolumeEntry     map[string]float64
	DailyVolumeTemp      map[string]interface{}
	DailyVolumeEntryTemp map[string]interface{}

	OrderBook struct {
		Asks     []Order
		Bids     []Order
		IsFrozen bool
	}
	Order struct {
		Rate   float64
		Amount float64
	}

	OrderBookTemp struct {
		Asks     []OrderTemp
		Bids     []OrderTemp
		IsFrozen interface{}
	}
	OrderTemp        []interface{}
	OrderBookAll     map[string]OrderBook
	OrderBookAllTemp map[string]OrderBookTemp

	TradeHistory      []TradeHistoryEntry
	TradeHistoryEntry struct {
		ID     int64 `json:"globalTradeID"`
		Date   string
		Type   string
		Rate   float64 `json:",string"`
		Amount float64 `json:",string"`
		Total  float64 `json:",string"`
	}

	ChartData      []ChartDataEntry
	ChartDataEntry struct {
		Date            int64
		High            float64
		Low             float64
		Open            float64
		Close           float64
		Volume          float64
		QuoteVolume     float64
		WeightedAverage float64
	}

	Currencies map[string]Currency
	Currency   struct {
		Name           string
		TxFee          float64 `json:",string"`
		MinConf        float64
		DepositAddress string
		Disabled       int64
		Delisted       int64
		Frozen         int64
	}

	LoanOrders struct {
		Offers  []LoanOrder
		Demands []LoanOrder
	}
	LoanOrder struct {
		Rate     float64 `json:",string"`
		Amount   float64 `json:",string"`
		RangeMin float64
		RangeMax float64
	}
)

func (p *Poloniex) Ticker() (ticker Ticker, err error) {
	err = p.public("returnTicker", nil, &ticker)
	return
}

func (p *Poloniex) DailyVolume() (dailyVolume DailyVolume, err error) {
	dvt := DailyVolumeTemp{}
	err = p.public("return24hVolume", nil, &dvt)
	if err != nil {
		return
	}
	dailyVolume = DailyVolume{}
	for k := range dvt {
		v := dvt[k]
		dve := DailyVolumeEntry{}
		switch i := v.(type) {
		default:
			v := i.(map[string]interface{})
			for kk, vv := range v {
				dve[kk] = toFloat(vv)
			}
			dailyVolume[k] = dve
		case string:
			//ignore anything that isn't a map
		}
	}
	return
}

func (p *Poloniex) OrderBook(pair string) (orderBook OrderBook, err error) {
	params := url.Values{}
	params.Add("currencyPair", pair)
	params.Add("depth", "40")
	obt := OrderBookTemp{}
	err = p.public("returnOrderBook", params, &obt)
	if err != nil {
		return
	}
	orderBook = tempToOrderBook(obt)
	return
}

func (p *Poloniex) OrderBookAll() (orderBook OrderBookAll, err error) {
	params := url.Values{}
	params.Add("depth", "5")
	params.Add("currencyPair", "all")
	obt := OrderBookAllTemp{}
	err = p.public("returnOrderBook", params, &obt)
	if err != nil {
		return
	}
	orderBook = OrderBookAll{}
	for k, v := range obt {
		orderBook[k] = tempToOrderBook(v)
	}
	return
}

func (p *Poloniex) TradeHistory(pair string, dates ...int64) (tradeHistory TradeHistory, err error) {
	params := url.Values{}
	params.Add("currencyPair", pair)
	if len(dates) > 0 {
		// we have a start date
		params.Add("start", fmt.Sprintf("%d", dates[0]))
	}
	if len(dates) > 1 {
		// we have an end date
		params.Add("end", fmt.Sprintf("%d", dates[2]))
	}
	err = p.public("returnTradeHistory", params, &tradeHistory)
	return
}

func (p *Poloniex) ChartData(pair string) (chartData ChartData, err error) {
	params := url.Values{}
	params.Add("currencyPair", pair)
	params.Add("start", fmt.Sprintf("%d", time.Now().Add(-24*time.Hour).Unix()))
	params.Add("end", "9999999999")
	params.Add("period", "300")
	err = p.public("returnChartData", params, &chartData)
	return
}

func (p *Poloniex) ChartDataPeriod(pair string, start, end time.Time, period ...int) (chartData ChartData, err error) {
	params := url.Values{}
	params.Add("currencyPair", pair)
	params.Add("start", fmt.Sprintf("%d", start.Unix()))
	params.Add("end", fmt.Sprintf("%d", end.Unix()))
	pi := 300
	if len(period) > 0 {
		pi = period[0]
	}
	ps := fmt.Sprintf("%d", pi)
	params.Add("period", ps)
	err = p.public("returnChartData", params, &chartData)
	return
}

func (p *Poloniex) ChartDataCurrent(pair string) (chartData ChartData, err error) {
	params := url.Values{}
	params.Add("currencyPair", pair)
	params.Add("start", fmt.Sprintf("%d", time.Now().Add(-5*time.Minute).Unix()))
	params.Add("end", "9999999999")
	params.Add("period", "300")
	err = p.public("returnChartData", params, &chartData)
	return
}

func (p *Poloniex) Currencies() (currencies Currencies, err error) {
	err = p.public("returnCurrencies", nil, &currencies)
	return
}

func (p *Poloniex) LoanOrders(currency string) (loanOrders LoanOrders, err error) {
	params := url.Values{}
	params.Add("currency", currency)
	err = p.public("returnLoanOrders", params, &loanOrders)
	return
}

func tempToOrderBook(obt OrderBookTemp) (ob OrderBook) {
	asks := obt.Asks
	bids := obt.Bids
	ob.IsFrozen = obt.IsFrozen.(string) != "0"
	ob.Asks = []Order{}
	ob.Bids = []Order{}
	for k := range asks {
		v := asks[k]
		price := toFloat(v[0])
		amount := toFloat(v[1])
		o := Order{Rate: price, Amount: amount}
		ob.Asks = append(ob.Asks, o)
	}
	for k := range bids {
		v := bids[k]
		price := toFloat(v[0])
		amount := toFloat(v[1])
		o := Order{Rate: price, Amount: amount}
		ob.Bids = append(ob.Bids, o)
	}
	return
}

func (p *Poloniex) public(command string, params url.Values, retval interface{}) (err error) {
	if p.debug {
		defer un(trace("public: " + command))
	}
	p.mutex.Lock()
	defer p.mutex.Unlock()
	if params == nil {
		params = url.Values{}
	}
	params.Add("command", command)
	req := goreq.Request{Uri: PUBLICURI, QueryString: params, Timeout: 130 * time.Second}
	res, err := req.Do()
	if err != nil {
		return
	}
	if p.debug {
		pp.Println(res.Request.URL.String())
	}

	defer res.Body.Close()

	s, err := res.Body.ToString()
	if err != nil {
		return
	}
	if p.debug {
		pp.Println(s)
	}
	err = json.Unmarshal([]byte(s), retval)
	return
}
<commit_msg>oops, typo<commit_after>package poloniex

import (
	"encoding/json"
	"fmt"
	"net/url"
	"time"

	"github.com/franela/goreq"
	"github.com/k0kubun/pp"
)

type (
	Ticker      map[string]TickerEntry
	TickerEntry struct {
		Last        float64 `json:",string"`
		Ask         float64 `json:"lowestAsk,string"`
		Bid         float64 `json:"highestBid,string"`
		Change      float64 `json:"percentChange,string"`
		BaseVolume  float64 `json:"baseVolume,string"`
		QuoteVolume float64 `json:"quoteVolume,string"`
		IsFrozen    int64   `json:"isFrozen,string"`
		ID          int64   `json:"id"`
	}

	DailyVolume          map[string]DailyVolumeEntry
	DailyVolumeEntry     map[string]float64
	DailyVolumeTemp      map[string]interface{}
	DailyVolumeEntryTemp map[string]interface{}

	OrderBook struct {
		Asks     []Order
		Bids     []Order
		IsFrozen bool
	}
	Order struct {
		Rate   float64
		Amount float64
	}

	OrderBookTemp struct {
		Asks     []OrderTemp
		Bids     []OrderTemp
		IsFrozen interface{}
	}
	OrderTemp        []interface{}
	OrderBookAll     map[string]OrderBook
	OrderBookAllTemp map[string]OrderBookTemp

	TradeHistory      []TradeHistoryEntry
	TradeHistoryEntry struct {
		ID     int64 `json:"globalTradeID"`
		Date   string
		Type   string
		Rate   float64 `json:",string"`
		Amount float64 `json:",string"`
		Total  float64 `json:",string"`
	}

	ChartData      []ChartDataEntry
	ChartDataEntry struct {
		Date            int64
		High            float64
		Low             float64
		Open            float64
		Close           float64
		Volume          float64
		QuoteVolume     float64
		WeightedAverage float64
	}

	Currencies map[string]Currency
	Currency   struct {
		Name           string
		TxFee          float64 `json:",string"`
		MinConf        float64
		DepositAddress string
		Disabled       int64
		Delisted       int64
		Frozen         int64
	}

	LoanOrders struct {
		Offers  []LoanOrder
		Demands []LoanOrder
	}
	LoanOrder struct {
		Rate     float64 `json:",string"`
		Amount   float64 `json:",string"`
		RangeMin float64
		RangeMax float64
	}
)

func (p *Poloniex) Ticker() (ticker Ticker, err error) {
	err = p.public("returnTicker", nil, &ticker)
	return
}

func (p *Poloniex) DailyVolume() (dailyVolume DailyVolume, err error) {
	dvt := DailyVolumeTemp{}
	err = p.public("return24hVolume", nil, &dvt)
	if err != nil {
		return
	}
	dailyVolume = DailyVolume{}
	for k := range dvt {
		v := dvt[k]
		dve := DailyVolumeEntry{}
		switch i := v.(type) {
		default:
			v := i.(map[string]interface{})
			for kk, vv := range v {
				dve[kk] = toFloat(vv)
			}
			dailyVolume[k] = dve
		case string:
			//ignore anything that isn't a map
		}
	}
	return
}

func (p *Poloniex) OrderBook(pair string) (orderBook OrderBook, err error) {
	params := url.Values{}
	params.Add("currencyPair", pair)
	params.Add("depth", "40")
	obt := OrderBookTemp{}
	err = p.public("returnOrderBook", params, &obt)
	if err != nil {
		return
	}
	orderBook = tempToOrderBook(obt)
	return
}

func (p *Poloniex) OrderBookAll() (orderBook OrderBookAll, err error) {
	params := url.Values{}
	params.Add("depth", "5")
	params.Add("currencyPair", "all")
	obt := OrderBookAllTemp{}
	err = p.public("returnOrderBook", params, &obt)
	if err != nil {
		return
	}
	orderBook = OrderBookAll{}
	for k, v := range obt {
		orderBook[k] = tempToOrderBook(v)
	}
	return
}

func (p *Poloniex) TradeHistory(pair string, dates ...int64) (tradeHistory TradeHistory, err error) {
	params := url.Values{}
	params.Add("currencyPair", pair)
	if len(dates) > 0 {
		// we have a start date
		params.Add("start", fmt.Sprintf("%d", dates[0]))
	}
	if len(dates) > 1 {
		// we have an end date
		params.Add("end", fmt.Sprintf("%d", dates[1]))
	}
	err = p.public("returnTradeHistory", params, &tradeHistory)
	return
}

func (p *Poloniex) ChartData(pair string) (chartData ChartData, err error) {
	params := url.Values{}
	params.Add("currencyPair", pair)
	params.Add("start", fmt.Sprintf("%d", time.Now().Add(-24*time.Hour).Unix()))
	params.Add("end", "9999999999")
	params.Add("period", "300")
	err = p.public("returnChartData", params, &chartData)
	return
}

func (p *Poloniex) ChartDataPeriod(pair string, start, end time.Time, period ...int) (chartData ChartData, err error) {
	params := url.Values{}
	params.Add("currencyPair", pair)
	params.Add("start", fmt.Sprintf("%d", start.Unix()))
	params.Add("end", fmt.Sprintf("%d", end.Unix()))
	pi := 300
	if len(period) > 0 {
		pi = period[0]
	}
	ps := fmt.Sprintf("%d", pi)
	params.Add("period", ps)
	err = p.public("returnChartData", params, &chartData)
	return
}

func (p *Poloniex) ChartDataCurrent(pair string) (chartData ChartData, err error) {
	params := url.Values{}
	params.Add("currencyPair", pair)
	params.Add("start", fmt.Sprintf("%d", time.Now().Add(-5*time.Minute).Unix()))
	params.Add("end", "9999999999")
	params.Add("period", "300")
	err = p.public("returnChartData", params, &chartData)
	return
}

func (p *Poloniex) Currencies() (currencies Currencies, err error) {
	err = p.public("returnCurrencies", nil, &currencies)
	return
}

func (p *Poloniex) LoanOrders(currency string) (loanOrders LoanOrders, err error) {
	params := url.Values{}
	params.Add("currency", currency)
	err = p.public("returnLoanOrders", params, &loanOrders)
	return
}

func tempToOrderBook(obt OrderBookTemp) (ob OrderBook) {
	asks := obt.Asks
	bids := obt.Bids
	ob.IsFrozen = obt.IsFrozen.(string) != "0"
	ob.Asks = []Order{}
	ob.Bids = []Order{}
	for k := range asks {
		v := asks[k]
		price := toFloat(v[0])
		amount := toFloat(v[1])
		o := Order{Rate: price, Amount: amount}
		ob.Asks = append(ob.Asks, o)
	}
	for k := range bids {
		v := bids[k]
		price := toFloat(v[0])
		amount := toFloat(v[1])
		o := Order{Rate: price, Amount: amount}
		ob.Bids = append(ob.Bids, o)
	}
	return
}

func (p *Poloniex) public(command string, params url.Values, retval interface{}) (err error) {
	if p.debug {
		defer un(trace("public: " + command))
	}
	p.mutex.Lock()
	defer p.mutex.Unlock()
	if params == nil {
		params = url.Values{}
	}
	params.Add("command", command)
	req := goreq.Request{Uri: PUBLICURI, QueryString: params, Timeout: 130 * time.Second}
	res, err := req.Do()
	if err != nil {
		return
	}
	if p.debug {
		pp.Println(res.Request.URL.String())
	}

	defer res.Body.Close()

	s, err := res.Body.ToString()
	if err != nil {
		return
	}
	if p.debug {
		pp.Println(s)
	}
	err = json.Unmarshal([]byte(s), retval)
	return
}
<|endoftext|>


<commit_before>package containerd

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/containerd/containerd"
	"github.com/containerd/containerd/namespaces"
	"github.com/natefinch/lumberjack"
	util2 "github.com/rancher/k3s/pkg/agent/util"
	"github.com/rancher/k3s/pkg/daemons/config"
	"github.com/sirupsen/logrus"
	"google.golang.org/grpc"
	runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
	"k8s.io/kubernetes/pkg/kubelet/util"
)

const (
	maxMsgSize = 1024 * 1024 * 16
	configToml = `
[plugins.opt]
	path = "%OPT%"
[plugins.cri]
  stream_server_address = "%NODE%"
  stream_server_port = "10010"
`
	configCNIToml = `
  [plugins.cri.cni]
    bin_dir = "%CNIBIN%"
    conf_dir = "%CNICFG%"
`
)

func Run(ctx context.Context, cfg *config.Node) error {
	args := []string{
		"containerd",
		"-c", cfg.Containerd.Config,
		"-a", cfg.Containerd.Address,
		"--state", cfg.Containerd.State,
		"--root", cfg.Containerd.Root,
	}

	template := configToml
	if !cfg.NoFlannel {
		template += configCNIToml
	}

	template = strings.Replace(template, "%OPT%", cfg.Containerd.Opt, -1)
	template = strings.Replace(template, "%CNIBIN%", cfg.AgentConfig.CNIBinDir, -1)
	template = strings.Replace(template, "%CNICFG%", cfg.AgentConfig.CNIConfDir, -1)
	template = strings.Replace(template, "%NODE%", cfg.AgentConfig.NodeName, -1)

	if err := util2.WriteFile(cfg.Containerd.Config, template); err != nil {
		return err
	}

	if os.Getenv("CONTAINERD_LOG_LEVEL") != "" {
		args = append(args, "-l", "CONTAINERD_LOG_LEVEL")
	}

	stdOut := io.Writer(os.Stdout)
	stdErr := io.Writer(os.Stderr)

	if cfg.Containerd.Log != "" {
		logrus.Infof("Logging containerd to %s", cfg.Containerd.Log)
		stdOut = &lumberjack.Logger{
			Filename:   cfg.Containerd.Log,
			MaxSize:    50,
			MaxBackups: 3,
			MaxAge:     28,
			Compress:   true,
		}
		stdErr = stdOut
	}

	go func() {
		logrus.Infof("Running containerd %s", config.ArgString(args[1:]))
		cmd := exec.Command(args[0], args[1:]...)
		cmd.Stdout = stdOut
		cmd.Stderr = stdErr
		cmd.SysProcAttr = &syscall.SysProcAttr{
			Pdeathsig: syscall.SIGKILL,
		}
		if err := cmd.Run(); err != nil {
			fmt.Fprintf(os.Stderr, "containerd: %s\n", err)
		}
		os.Exit(1)
	}()

	for {
		addr, dailer, err := util.GetAddressAndDialer("unix://" + cfg.Containerd.Address)
		if err != nil {
			time.Sleep(1 * time.Second)
			continue
		}

		conn, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithTimeout(3*time.Second), grpc.WithDialer(dailer), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)))
		if err != nil {
			time.Sleep(1 * time.Second)
			continue
		}

		c := runtimeapi.NewRuntimeServiceClient(conn)

		_, err = c.Version(ctx, &runtimeapi.VersionRequest{
			Version: "0.1.0",
		})
		if err == nil {
			conn.Close()
			break
		}
		conn.Close()
		logrus.Infof("Waiting for containerd startup: %v", err)
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-time.After(time.Second):
		}
	}

	return preloadImages(cfg)
}

func preloadImages(cfg *config.Node) error {
	fileInfo, err := os.Stat(cfg.Images)
	if os.IsNotExist(err) {
		return nil
	} else if err != nil {
		logrus.Errorf("Unable to find images in %s: %v", cfg.Images, err)
		return nil
	}

	if !fileInfo.IsDir() {
		return nil
	}

	fileInfos, err := ioutil.ReadDir(cfg.Images)
	if err != nil {
		logrus.Errorf("Unable to read images in %s: %v", cfg.Images, err)
		return nil
	}

	client, err := containerd.New(cfg.Containerd.Address)
	if err != nil {
		return err
	}
	defer client.Close()

	ctxContainerD := namespaces.WithNamespace(context.Background(), "k8s.io")

	for _, fileInfo := range fileInfos {
		if fileInfo.IsDir() {
			continue
		}

		filePath := filepath.Join(cfg.Images, fileInfo.Name())

		file, err := os.Open(filePath)
		if err != nil {
			logrus.Errorf("Unable to read %s: %v", filePath, err)
			continue
		}

		logrus.Debugf("Import %s", filePath)
		_, err = client.Import(ctxContainerD, file)
		if err != nil {
			logrus.Errorf("Unable to import %s: %v", filePath, err)
		}
	}
	return nil
}
<commit_msg>Fix containerd debug log env var<commit_after>package containerd

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/containerd/containerd"
	"github.com/containerd/containerd/namespaces"
	"github.com/natefinch/lumberjack"
	util2 "github.com/rancher/k3s/pkg/agent/util"
	"github.com/rancher/k3s/pkg/daemons/config"
	"github.com/sirupsen/logrus"
	"google.golang.org/grpc"
	runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
	"k8s.io/kubernetes/pkg/kubelet/util"
)

const (
	maxMsgSize = 1024 * 1024 * 16
	configToml = `
[plugins.opt]
	path = "%OPT%"
[plugins.cri]
  stream_server_address = "%NODE%"
  stream_server_port = "10010"
`
	configCNIToml = `
  [plugins.cri.cni]
    bin_dir = "%CNIBIN%"
    conf_dir = "%CNICFG%"
`
)

func Run(ctx context.Context, cfg *config.Node) error {
	args := []string{
		"containerd",
		"-c", cfg.Containerd.Config,
		"-a", cfg.Containerd.Address,
		"--state", cfg.Containerd.State,
		"--root", cfg.Containerd.Root,
	}

	template := configToml
	if !cfg.NoFlannel {
		template += configCNIToml
	}

	template = strings.Replace(template, "%OPT%", cfg.Containerd.Opt, -1)
	template = strings.Replace(template, "%CNIBIN%", cfg.AgentConfig.CNIBinDir, -1)
	template = strings.Replace(template, "%CNICFG%", cfg.AgentConfig.CNIConfDir, -1)
	template = strings.Replace(template, "%NODE%", cfg.AgentConfig.NodeName, -1)

	if err := util2.WriteFile(cfg.Containerd.Config, template); err != nil {
		return err
	}

	if os.Getenv("CONTAINERD_LOG_LEVEL") != "" {
		args = append(args, "-l", os.Getenv("CONTAINERD_LOG_LEVEL"))
	}

	stdOut := io.Writer(os.Stdout)
	stdErr := io.Writer(os.Stderr)

	if cfg.Containerd.Log != "" {
		logrus.Infof("Logging containerd to %s", cfg.Containerd.Log)
		stdOut = &lumberjack.Logger{
			Filename:   cfg.Containerd.Log,
			MaxSize:    50,
			MaxBackups: 3,
			MaxAge:     28,
			Compress:   true,
		}
		stdErr = stdOut
	}

	go func() {
		logrus.Infof("Running containerd %s", config.ArgString(args[1:]))
		cmd := exec.Command(args[0], args[1:]...)
		cmd.Stdout = stdOut
		cmd.Stderr = stdErr
		cmd.SysProcAttr = &syscall.SysProcAttr{
			Pdeathsig: syscall.SIGKILL,
		}
		if err := cmd.Run(); err != nil {
			fmt.Fprintf(os.Stderr, "containerd: %s\n", err)
		}
		os.Exit(1)
	}()

	for {
		addr, dailer, err := util.GetAddressAndDialer("unix://" + cfg.Containerd.Address)
		if err != nil {
			time.Sleep(1 * time.Second)
			continue
		}

		conn, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithTimeout(3*time.Second), grpc.WithDialer(dailer), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)))
		if err != nil {
			time.Sleep(1 * time.Second)
			continue
		}

		c := runtimeapi.NewRuntimeServiceClient(conn)

		_, err = c.Version(ctx, &runtimeapi.VersionRequest{
			Version: "0.1.0",
		})
		if err == nil {
			conn.Close()
			break
		}
		conn.Close()
		logrus.Infof("Waiting for containerd startup: %v", err)
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-time.After(time.Second):
		}
	}

	return preloadImages(cfg)
}

func preloadImages(cfg *config.Node) error {
	fileInfo, err := os.Stat(cfg.Images)
	if os.IsNotExist(err) {
		return nil
	} else if err != nil {
		logrus.Errorf("Unable to find images in %s: %v", cfg.Images, err)
		return nil
	}

	if !fileInfo.IsDir() {
		return nil
	}

	fileInfos, err := ioutil.ReadDir(cfg.Images)
	if err != nil {
		logrus.Errorf("Unable to read images in %s: %v", cfg.Images, err)
		return nil
	}

	client, err := containerd.New(cfg.Containerd.Address)
	if err != nil {
		return err
	}
	defer client.Close()

	ctxContainerD := namespaces.WithNamespace(context.Background(), "k8s.io")

	for _, fileInfo := range fileInfos {
		if fileInfo.IsDir() {
			continue
		}

		filePath := filepath.Join(cfg.Images, fileInfo.Name())

		file, err := os.Open(filePath)
		if err != nil {
			logrus.Errorf("Unable to read %s: %v", filePath, err)
			continue
		}

		logrus.Debugf("Import %s", filePath)
		_, err = client.Import(ctxContainerD, file)
		if err != nil {
			logrus.Errorf("Unable to import %s: %v", filePath, err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2018 Istio 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 v2_test

import (
	"fmt"
	"io/ioutil"
	"testing"
	"time"

	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pilot/pkg/proxy/envoy/v2"
	"istio.io/istio/pkg/test/env"
	"istio.io/istio/tests/util"
)

// Regression for envoy restart and overlapping connections
func TestAdsReconnectWithNonce(t *testing.T) {
	_, tearDown := initLocalPilotTestEnv(t)
	defer tearDown()
	edsstr, cancel, err := connectADS(util.MockPilotGrpcAddr)
	if err != nil {
		t.Fatal(err)
	}
	err = sendEDSReq([]string{"outbound|1080||service3.default.svc.cluster.local"}, sidecarId(app3Ip, "app3"), edsstr)
	if err != nil {
		t.Fatal(err)
	}
	res, _ := adsReceive(edsstr, 5*time.Second)

	// closes old process
	cancel()

	edsstr, cancel, err = connectADS(util.MockPilotGrpcAddr)
	if err != nil {
		t.Fatal(err)
	}
	defer cancel()

	err = sendEDSReqReconnect([]string{"service3.default.svc.cluster.local|http"}, edsstr, res)
	if err != nil {
		t.Fatal(err)
	}
	err = sendEDSReq([]string{"outbound|1080||service3.default.svc.cluster.local"}, sidecarId(app3Ip, "app3"), edsstr)
	if err != nil {
		t.Fatal(err)
	}
	res, _ = adsReceive(edsstr, 5*time.Second)

	t.Log("Received ", res)
}

// Regression for envoy restart and overlapping connections
func TestAdsReconnect(t *testing.T) {
	s, tearDown := initLocalPilotTestEnv(t)
	defer tearDown()

	edsstr, cancel, err := connectADS(util.MockPilotGrpcAddr)
	if err != nil {
		t.Fatal(err)
	}
	err = sendCDSReq(sidecarId(app3Ip, "app3"), edsstr)
	if err != nil {
		t.Fatal(err)
	}

	_, _ = adsReceive(edsstr, 5*time.Second)

	// envoy restarts and reconnects
	edsstr2, cancel2, err := connectADS(util.MockPilotGrpcAddr)
	if err != nil {
		t.Fatal(err)
	}
	defer cancel2()
	err = sendCDSReq(sidecarId(app3Ip, "app3"), edsstr2)
	if err != nil {
		t.Fatal(err)
	}
	_, _ = adsReceive(edsstr2, 5*time.Second)

	// closes old process
	cancel()

	time.Sleep(1 * time.Second)

	// event happens
	v2.AdsPushAll(s.EnvoyXdsServer)
	// will trigger recompute and push (we may need to make a change once diff is implemented

	m, err := adsReceive(edsstr2, 3*time.Second)
	if err != nil {
		t.Fatal("Recv failed", err)
	}
	t.Log("Received ", m)
}

func TestTLS(t *testing.T) {
	_, tearDown := initLocalPilotTestEnv(t)
	defer tearDown()

	edsstr, cancel, err := connectADSS(util.MockPilotSecureAddr)
	if err != nil {
		t.Fatal(err)
	}
	defer cancel()
	err = sendCDSReq(sidecarId(app3Ip, "app3"), edsstr)
	if err != nil {
		t.Fatal(err)
	}
	_, err = adsReceive(edsstr, 3*time.Second)
	if err != nil {
		t.Error("Failed to receive with TLS connection ", err)
	}
}

func TestAdsClusterUpdate(t *testing.T) {
	server, tearDown := initLocalPilotTestEnv(t)
	defer tearDown()

	edsstr, cancel, err := connectADS(util.MockPilotGrpcAddr)
	if err != nil {
		t.Fatal(err)
	}
	defer cancel()

	var sendEDSReqAndVerify = func(clusterName string) {
		err = sendEDSReq([]string{clusterName}, sidecarId("1.1.1.1", "app3"), edsstr)
		if err != nil {
			t.Fatal(err)
		}
		res, err := adsReceive(edsstr, 5*time.Second)
		if err != nil {
			t.Fatal("Recv failed", err)
		}

		if res.TypeUrl != "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment" {
			t.Error("Expecting type.googleapis.com/envoy.api.v2.ClusterLoadAssignment got ", res.TypeUrl)
		}
		if res.Resources[0].TypeUrl != "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment" {
			t.Error("Expecting type.googleapis.com/envoy.api.v2.ClusterLoadAssignment got ", res.Resources[0].TypeUrl)
		}

		cla, err := getLoadAssignment(res)
		if err != nil {
			t.Fatal("Invalid EDS response ", err)
		}
		if cla.ClusterName != clusterName {
			t.Error(fmt.Sprintf("Expecting %s got ", clusterName), cla.ClusterName)
		}
	}

	_ = server.EnvoyXdsServer.MemRegistry.AddEndpoint("adsupdate.default.svc.cluster.local",
		"http-main", 2080, "10.2.0.1", 1080)

	cluster1 := "outbound|80||adsupdate.default.svc.cluster.local"
	sendEDSReqAndVerify(cluster1)

	// register a second endpoint
	_ = server.EnvoyXdsServer.MemRegistry.AddEndpoint("adsupdate2.default.svc.cluster.local",
		"http-status", 2080, "10.2.0.2", 1081)

	cluster2 := "outbound|80||adsupdate2.default.svc.cluster.local"
	sendEDSReqAndVerify(cluster2)
}

func TestAdsUpdate(t *testing.T) {
	server, tearDown := initLocalPilotTestEnv(t)
	defer tearDown()

	edsstr, cancel, err := connectADS(util.MockPilotGrpcAddr)
	if err != nil {
		t.Fatal(err)
	}
	defer cancel()

	// Old style cluster.
	// TODO: convert tests (except eds) to new style.
	server.EnvoyXdsServer.MemRegistry.AddService("adsupdate.default.svc.cluster.local", &model.Service{
		Hostname: "adsupdate.default.svc.cluster.local",
		Address:  "10.11.0.1",
		Ports:    testPorts(0),
	})
	_ = server.EnvoyXdsServer.MemRegistry.AddEndpoint("adsupdate.default.svc.cluster.local",
		"http-main", 2080, "10.2.0.1", 1080)

	err = sendEDSReq([]string{"outbound|2080||adsupdate.default.svc.cluster.local"}, sidecarId("1.1.1.1", "app3"), edsstr)
	if err != nil {
		t.Fatal(err)
	}

	res1, err := adsReceive(edsstr, 5*time.Second)
	if err != nil {
		t.Fatal("Recv failed", err)
	}

	if res1.TypeUrl != "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment" {
		t.Error("Expecting type.googleapis.com/envoy.api.v2.ClusterLoadAssignment got ", res1.TypeUrl)
	}
	if res1.Resources[0].TypeUrl != "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment" {
		t.Error("Expecting type.googleapis.com/envoy.api.v2.ClusterLoadAssignment got ", res1.Resources[0].TypeUrl)
	}
	cla, err := getLoadAssignment(res1)
	if err != nil {
		t.Fatal("Invalid EDS response ", err)
	}
	// TODO: validate VersionInfo and nonce once we settle on a scheme

	ep := cla.Endpoints
	if len(ep) == 0 {
		t.Fatal("No endpoints")
	}
	lbe := ep[0].LbEndpoints
	if len(lbe) == 0 {
		t.Fatal("No lb endpoints")
	}
	if "10.2.0.1" != lbe[0].Endpoint.Address.GetSocketAddress().Address {
		t.Error("Expecting 10.2.0.1 got ", lbe[0].Endpoint.Address.GetSocketAddress().Address)
	}
	strResponse, _ := model.ToJSONWithIndent(res1, " ")
	_ = ioutil.WriteFile(env.IstioOut+"/edsv2_sidecar.json", []byte(strResponse), 0644)

	_ = server.EnvoyXdsServer.MemRegistry.AddEndpoint("adsupdate.default.svc.cluster.local",
		"http-main", 2080, "10.1.7.1", 1080)

	// will trigger recompute and push for all clients - including some that may be closing
	// This reproduced the 'push on closed connection' bug.
	v2.AdsPushAll(server.EnvoyXdsServer)

	res1, err = adsReceive(edsstr, 5*time.Second)
	if err != nil {
		t.Fatal("Recv2 failed", err)
	}
	strResponse, _ = model.ToJSONWithIndent(res1, " ")
	_ = ioutil.WriteFile(env.IstioOut+"/edsv2_update.json", []byte(strResponse), 0644)
}
<commit_msg>remove unused code and fix flaky test (#10204)<commit_after>// Copyright 2018 Istio 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 v2_test

import (
	"fmt"
	"io/ioutil"
	"testing"
	"time"

	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pilot/pkg/proxy/envoy/v2"
	"istio.io/istio/pkg/test/env"
	"istio.io/istio/tests/util"
)

// Regression for envoy restart and overlapping connections
func TestAdsReconnectWithNonce(t *testing.T) {
	_, tearDown := initLocalPilotTestEnv(t)
	defer tearDown()
	edsstr, cancel, err := connectADS(util.MockPilotGrpcAddr)
	if err != nil {
		t.Fatal(err)
	}
	err = sendEDSReq([]string{"outbound|1080||service3.default.svc.cluster.local"}, sidecarId(app3Ip, "app3"), edsstr)
	if err != nil {
		t.Fatal(err)
	}
	res, _ := adsReceive(edsstr, 5*time.Second)

	// closes old process
	cancel()

	edsstr, cancel, err = connectADS(util.MockPilotGrpcAddr)
	if err != nil {
		t.Fatal(err)
	}
	defer cancel()

	err = sendEDSReqReconnect([]string{"service3.default.svc.cluster.local|http"}, edsstr, res)
	if err != nil {
		t.Fatal(err)
	}
	err = sendEDSReq([]string{"outbound|1080||service3.default.svc.cluster.local"}, sidecarId(app3Ip, "app3"), edsstr)
	if err != nil {
		t.Fatal(err)
	}
	res, _ = adsReceive(edsstr, 5*time.Second)

	t.Log("Received ", res)
}

// Regression for envoy restart and overlapping connections
func TestAdsReconnect(t *testing.T) {
	s, tearDown := initLocalPilotTestEnv(t)
	defer tearDown()

	edsstr, cancel, err := connectADS(util.MockPilotGrpcAddr)
	if err != nil {
		t.Fatal(err)
	}
	err = sendCDSReq(sidecarId(app3Ip, "app3"), edsstr)
	if err != nil {
		t.Fatal(err)
	}

	_, _ = adsReceive(edsstr, 5*time.Second)

	// envoy restarts and reconnects
	edsstr2, cancel2, err := connectADS(util.MockPilotGrpcAddr)
	if err != nil {
		t.Fatal(err)
	}
	defer cancel2()
	err = sendCDSReq(sidecarId(app3Ip, "app3"), edsstr2)
	if err != nil {
		t.Fatal(err)
	}
	_, _ = adsReceive(edsstr2, 5*time.Second)

	// closes old process
	cancel()

	time.Sleep(1 * time.Second)

	// event happens
	v2.AdsPushAll(s.EnvoyXdsServer)
	// will trigger recompute and push (we may need to make a change once diff is implemented

	m, err := adsReceive(edsstr2, 3*time.Second)
	if err != nil {
		t.Fatal("Recv failed", err)
	}
	t.Log("Received ", m)
}

func TestTLS(t *testing.T) {
	_, tearDown := initLocalPilotTestEnv(t)
	defer tearDown()

	edsstr, cancel, err := connectADSS(util.MockPilotSecureAddr)
	if err != nil {
		t.Fatal(err)
	}
	defer cancel()
	err = sendCDSReq(sidecarId(app3Ip, "app3"), edsstr)
	if err != nil {
		t.Fatal(err)
	}
	_, err = adsReceive(edsstr, 3*time.Second)
	if err != nil {
		t.Error("Failed to receive with TLS connection ", err)
	}
}

func TestAdsClusterUpdate(t *testing.T) {
	_, tearDown := initLocalPilotTestEnv(t)
	defer tearDown()

	edsstr, cancel, err := connectADS(util.MockPilotGrpcAddr)
	if err != nil {
		t.Fatal(err)
	}
	defer cancel()

	var sendEDSReqAndVerify = func(clusterName string) {
		err = sendEDSReq([]string{clusterName}, sidecarId("1.1.1.1", "app3"), edsstr)
		if err != nil {
			t.Fatal(err)
		}
		res, err := adsReceive(edsstr, 5*time.Second)
		if err != nil {
			t.Fatal("Recv failed", err)
		}

		if res.TypeUrl != "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment" {
			t.Error("Expecting type.googleapis.com/envoy.api.v2.ClusterLoadAssignment got ", res.TypeUrl)
		}
		if res.Resources[0].TypeUrl != "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment" {
			t.Error("Expecting type.googleapis.com/envoy.api.v2.ClusterLoadAssignment got ", res.Resources[0].TypeUrl)
		}

		cla, err := getLoadAssignment(res)
		if err != nil {
			t.Fatal("Invalid EDS response ", err)
		}
		if cla.ClusterName != clusterName {
			t.Error(fmt.Sprintf("Expecting %s got ", clusterName), cla.ClusterName)
		}
	}

	cluster1 := "outbound|80||adsclusterupdate.default.svc.cluster.local"
	sendEDSReqAndVerify(cluster1)

	cluster2 := "outbound|80||adsclusterupdate2.default.svc.cluster.local"
	sendEDSReqAndVerify(cluster2)
}

func TestAdsUpdate(t *testing.T) {
	server, tearDown := initLocalPilotTestEnv(t)
	defer tearDown()

	edsstr, cancel, err := connectADS(util.MockPilotGrpcAddr)
	if err != nil {
		t.Fatal(err)
	}
	defer cancel()

	// Old style cluster.
	// TODO: convert tests (except eds) to new style.
	server.EnvoyXdsServer.MemRegistry.AddService("adsupdate.default.svc.cluster.local", &model.Service{
		Hostname: "adsupdate.default.svc.cluster.local",
		Address:  "10.11.0.1",
		Ports:    testPorts(0),
	})
	_ = server.EnvoyXdsServer.MemRegistry.AddEndpoint("adsupdate.default.svc.cluster.local",
		"http-main", 2080, "10.2.0.1", 1080)

	err = sendEDSReq([]string{"outbound|2080||adsupdate.default.svc.cluster.local"}, sidecarId("1.1.1.1", "app3"), edsstr)
	if err != nil {
		t.Fatal(err)
	}

	res1, err := adsReceive(edsstr, 5*time.Second)
	if err != nil {
		t.Fatal("Recv failed", err)
	}

	if res1.TypeUrl != "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment" {
		t.Error("Expecting type.googleapis.com/envoy.api.v2.ClusterLoadAssignment got ", res1.TypeUrl)
	}
	if res1.Resources[0].TypeUrl != "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment" {
		t.Error("Expecting type.googleapis.com/envoy.api.v2.ClusterLoadAssignment got ", res1.Resources[0].TypeUrl)
	}
	cla, err := getLoadAssignment(res1)
	if err != nil {
		t.Fatal("Invalid EDS response ", err)
	}
	// TODO: validate VersionInfo and nonce once we settle on a scheme

	ep := cla.Endpoints
	if len(ep) == 0 {
		t.Fatal("No endpoints")
	}
	lbe := ep[0].LbEndpoints
	if len(lbe) == 0 {
		t.Fatal("No lb endpoints")
	}
	if "10.2.0.1" != lbe[0].Endpoint.Address.GetSocketAddress().Address {
		t.Error("Expecting 10.2.0.1 got ", lbe[0].Endpoint.Address.GetSocketAddress().Address)
	}
	strResponse, _ := model.ToJSONWithIndent(res1, " ")
	_ = ioutil.WriteFile(env.IstioOut+"/edsv2_sidecar.json", []byte(strResponse), 0644)

	_ = server.EnvoyXdsServer.MemRegistry.AddEndpoint("adsupdate.default.svc.cluster.local",
		"http-main", 2080, "10.1.7.1", 1080)

	// will trigger recompute and push for all clients - including some that may be closing
	// This reproduced the 'push on closed connection' bug.
	v2.AdsPushAll(server.EnvoyXdsServer)

	res1, err = adsReceive(edsstr, 5*time.Second)
	if err != nil {
		t.Fatal("Recv2 failed", err)
	}
	strResponse, _ = model.ToJSONWithIndent(res1, " ")
	_ = ioutil.WriteFile(env.IstioOut+"/edsv2_update.json", []byte(strResponse), 0644)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package resource

import (
	"errors"

	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/discovery"
)

type CategoryExpander interface {
	Expand(category string) ([]schema.GroupResource, bool)
}

type SimpleCategoryExpander struct {
	Expansions map[string][]schema.GroupResource
}

func (e SimpleCategoryExpander) Expand(category string) ([]schema.GroupResource, bool) {
	ret, ok := e.Expansions[category]
	return ret, ok
}

type discoveryFilteredExpander struct {
	delegate CategoryExpander

	discoveryClient discovery.DiscoveryInterface
}

// NewDiscoveryFilteredExpander returns a category expander that filters the returned groupresources by
// what the server has available
func NewDiscoveryFilteredExpander(delegate CategoryExpander, client discovery.DiscoveryInterface) (discoveryFilteredExpander, error) {
	if client == nil {
		return discoveryFilteredExpander{}, errors.New("Please provide discovery client to shortcut expander")
	}
	return discoveryFilteredExpander{delegate: delegate, discoveryClient: client}, nil
}

func (e discoveryFilteredExpander) Expand(category string) ([]schema.GroupResource, bool) {
	delegateExpansion, ok := e.delegate.Expand(category)

	// Check if we have access to server resources
	apiResources, err := e.discoveryClient.ServerResources()
	if err != nil {
		return delegateExpansion, ok
	}

	availableResources, err := discovery.GroupVersionResources(apiResources)
	if err != nil {
		return delegateExpansion, ok
	}

	available := []schema.GroupResource{}
	for _, requestedResource := range delegateExpansion {
		for availableResource := range availableResources {
			if requestedResource.Group == availableResource.Group &&
				requestedResource.Resource == availableResource.Resource {
				available = append(available, requestedResource)
				break
			}
		}
	}

	return available, ok
}

// legacyUserResources are the resource names that apply to the primary, user facing resources used by
// client tools. They are in deletion-first order - dependent resources should be last.
// Should remain exported in order to expose a current list of resources to downstream
// composition that wants to build on the concept of 'all' for their CLIs.
var legacyUserResources = []schema.GroupResource{
	{Group: "", Resource: "pods"},
	{Group: "", Resource: "replicationcontrollers"},
	{Group: "", Resource: "services"},
	{Group: "apps", Resource: "statefulsets"},
	{Group: "autoscaling", Resource: "horizontalpodautoscalers"},
	{Group: "batch", Resource: "jobs"},
	{Group: "extensions", Resource: "deployments"},
	{Group: "extensions", Resource: "replicasets"},
}

// LegacyCategoryExpander is the old hardcoded expansion
var LegacyCategoryExpander CategoryExpander = SimpleCategoryExpander{
	Expansions: map[string][]schema.GroupResource{
		"all": legacyUserResources,
	},
}

// LegacyFederationCategoryExpander is the old hardcoded expansion for federation
var LegacyFederationCategoryExpander CategoryExpander = SimpleCategoryExpander{
	Expansions: map[string][]schema.GroupResource{
		"all": {{Group: "", Resource: "services"}},
	},
}
<commit_msg>add a union category expander<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package resource

import (
	"errors"

	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/discovery"
)

type CategoryExpander interface {
	Expand(category string) ([]schema.GroupResource, bool)
}

type SimpleCategoryExpander struct {
	Expansions map[string][]schema.GroupResource
}

func (e SimpleCategoryExpander) Expand(category string) ([]schema.GroupResource, bool) {
	ret, ok := e.Expansions[category]
	return ret, ok
}

type discoveryFilteredExpander struct {
	delegate CategoryExpander

	discoveryClient discovery.DiscoveryInterface
}

// NewDiscoveryFilteredExpander returns a category expander that filters the returned groupresources by
// what the server has available
func NewDiscoveryFilteredExpander(delegate CategoryExpander, client discovery.DiscoveryInterface) (discoveryFilteredExpander, error) {
	if client == nil {
		return discoveryFilteredExpander{}, errors.New("Please provide discovery client to shortcut expander")
	}
	return discoveryFilteredExpander{delegate: delegate, discoveryClient: client}, nil
}

func (e discoveryFilteredExpander) Expand(category string) ([]schema.GroupResource, bool) {
	delegateExpansion, ok := e.delegate.Expand(category)

	// Check if we have access to server resources
	apiResources, err := e.discoveryClient.ServerResources()
	if err != nil {
		return delegateExpansion, ok
	}

	availableResources, err := discovery.GroupVersionResources(apiResources)
	if err != nil {
		return delegateExpansion, ok
	}

	available := []schema.GroupResource{}
	for _, requestedResource := range delegateExpansion {
		for availableResource := range availableResources {
			if requestedResource.Group == availableResource.Group &&
				requestedResource.Resource == availableResource.Resource {
				available = append(available, requestedResource)
				break
			}
		}
	}

	return available, ok
}

type UnionCategoryExpander []CategoryExpander

func (u UnionCategoryExpander) Expand(category string) ([]schema.GroupResource, bool) {
	ret := []schema.GroupResource{}
	ok := false

	for _, expansion := range u {
		curr, currOk := expansion.Expand(category)

		for _, currGR := range curr {
			found := false
			for _, existing := range ret {
				if existing == currGR {
					found = true
					break
				}
			}
			if !found {
				ret = append(ret, currGR)
			}
		}
		ok = ok || currOk
	}

	return ret, ok
}

// legacyUserResources are the resource names that apply to the primary, user facing resources used by
// client tools. They are in deletion-first order - dependent resources should be last.
// Should remain exported in order to expose a current list of resources to downstream
// composition that wants to build on the concept of 'all' for their CLIs.
var legacyUserResources = []schema.GroupResource{
	{Group: "", Resource: "pods"},
	{Group: "", Resource: "replicationcontrollers"},
	{Group: "", Resource: "services"},
	{Group: "apps", Resource: "statefulsets"},
	{Group: "autoscaling", Resource: "horizontalpodautoscalers"},
	{Group: "batch", Resource: "jobs"},
	{Group: "extensions", Resource: "deployments"},
	{Group: "extensions", Resource: "replicasets"},
}

// LegacyCategoryExpander is the old hardcoded expansion
var LegacyCategoryExpander CategoryExpander = SimpleCategoryExpander{
	Expansions: map[string][]schema.GroupResource{
		"all": legacyUserResources,
	},
}

// LegacyFederationCategoryExpander is the old hardcoded expansion for federation
var LegacyFederationCategoryExpander CategoryExpander = SimpleCategoryExpander{
	Expansions: map[string][]schema.GroupResource{
		"all": {{Group: "", Resource: "services"}},
	},
}
<|endoftext|>


<commit_before>/*
Copyright (C) 2017 Red Hat, 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 openshift

import (
	"fmt"
	"path/filepath"
	"strconv"
	"strings"

	"encoding/json"

	"github.com/minishift/minishift/pkg/minikube/constants"
	instanceState "github.com/minishift/minishift/pkg/minishift/config"
	"github.com/minishift/minishift/pkg/util"
	"github.com/pkg/errors"
)

var (
	systemKubeConfigPath string
	runner               util.Runner = &util.RealRunner{}
)

func init() {
	systemKubeConfigPath = filepath.Join(constants.Minipath, "machines", constants.MachineName+"_kubeconfig")
}

type Service struct {
	Items []struct {
		Metadata struct {
			Name string `json:"name"`
		} `json:"metadata"`
		Spec struct {
			Ports []struct {
				NodePort int `json:"nodePort"`
			} `json:"ports"`
		} `json:"spec"`
	} `json:"items"`
}

type Route struct {
	Items []struct {
		Spec struct {
			AlternateBackends []struct {
				Name   string `json:"name"`
				Weight int    `json:"weight"`
			} `json:"alternateBackends"`
			Host string `json:"host"`
			To   struct {
				Name   string `json:"name"`
				Weight int    `json:"weight"`
			} `json:"to"`
		} `json:"spec"`
	} `json:"items"`
}

type ServiceWeight struct {
	Name   string
	Weight string
}

type ServiceSpec struct {
	Namespace string
	Name      string
	URL       []string
	NodePort  string
	Weight    []string
}

const (
	ProjectsCustomCol = "-o=custom-columns=NAME:.metadata.name"
)

// GetServiceSpecs takes Namespace string and return route/nodeport/name/weight in a ServiceSpec structure
func GetServiceSpecs(serviceNamespace string) ([]ServiceSpec, error) {
	var serviceSpecs []ServiceSpec

	if serviceNamespace != "" && !isProjectExists(serviceNamespace) {
		return serviceSpecs, errors.New(fmt.Sprintf("Namespace %s doesn't exits", serviceNamespace))
	}

	namespaces, err := getValidNamespaces(serviceNamespace)
	if err != nil {
		return serviceSpecs, err
	}

	// iterate over namespaces, get command output, format route and nodePort
	for _, namespace := range namespaces {
		serviceNodePort, err := getService(namespace)
		if err != nil {
			return serviceSpecs, err
		}
		routeSpec, err := getRouteSpec(namespace)
		if err != nil {
			return serviceSpecs, err
		}
		if serviceNodePort != nil {
			serviceSpecs = append(serviceSpecs, filterAndUpdateServiceSpecs(routeSpec, namespace, serviceNodePort)...)
		}
	}
	if len(serviceSpecs) == 0 {
		return serviceSpecs, errors.New(fmt.Sprintf("No services defined in namespace '%s'", serviceNamespace))
	}

	return serviceSpecs, nil
}

// Check whether project exists or not
func isProjectExists(projectName string) bool {
	projects, err := getProjects()
	if err != nil {
		return false
	}

	for _, name := range projects {
		if name == projectName {
			return true
		}
	}

	return false
}

func getValidNamespaces(serviceListNamespace string) ([]string, error) {
	var (
		namespaces []string
		err        error
	)

	// If namespace is default then consider all namespaces user belongs to
	if serviceListNamespace == "" {
		namespaces, err = getProjects()
		if err != nil {
			return namespaces, errors.Wrap(err, "Error getting valid namespaces user belongs to.")
		}
	} else {
		namespaces = append(namespaces, serviceListNamespace)
	}

	return namespaces, nil
}

// getRouteSpec take valid namespace and return map which have route as key and
// value be ServiceWeight struct
func getRouteSpec(namespace string) (map[string][]ServiceWeight, error) {
	routeSpec := make(map[string][]ServiceWeight)
	cmdArgText := fmt.Sprintf("get route -o json --config=%s -n %s", systemKubeConfigPath, namespace)
	tokens := strings.Split(cmdArgText, " ")
	cmdName := instanceState.InstanceConfig.OcPath
	cmdOut, err := runner.Output(cmdName, tokens...)
	if err != nil {
		return nil, err
	}

	if strings.Contains(string(cmdOut), "No resources found") {
		return nil, nil
	}

	var data Route
	err = json.Unmarshal(cmdOut, &data)
	if err != nil {
		return nil, err
	}

	for _, item := range data.Items {
		totalWeight := item.Spec.To.Weight
		for _, alternateBackend := range item.Spec.AlternateBackends {
			totalWeight += alternateBackend.Weight
		}
		routeSpec[item.Spec.Host] = []ServiceWeight{{Name: item.Spec.To.Name, Weight: calculateWeight(float64(item.Spec.To.Weight), float64(totalWeight))}}
		for _, alternateBackend := range item.Spec.AlternateBackends {
			routeSpec[item.Spec.Host] = append(routeSpec[item.Spec.Host], ServiceWeight{Name: alternateBackend.Name,
				Weight: calculateWeight(float64(alternateBackend.Weight), float64(totalWeight))})
		}
	}
	return routeSpec, nil
}

func calculateWeight(a float64, b float64) string {
	weightPercentage := int(a / b * 100)
	if weightPercentage != 100 {
		return strconv.Itoa(weightPercentage) + "%"
	}
	return ""
}

func filterAndUpdateServiceSpecs(routeSpec map[string][]ServiceWeight, namespace string, serviceNodePort map[string]string) []ServiceSpec {
	var serviceSpecs []ServiceSpec
	serviceSpecMap := make(map[string]*ServiceSpec)

	for routeURL, v := range routeSpec {
		for _, service := range v {
			// split content on colon to separate NAME, HOST and Weight
			if _, ok := serviceNodePort[service.Name]; ok {
				if _, ok := serviceSpecMap[service.Name]; !ok {
					serviceSpecMap[service.Name] = &ServiceSpec{Namespace: namespace,
						Name:     service.Name,
						URL:      []string{fmt.Sprintf("http://%s", routeURL)},
						NodePort: serviceNodePort[service.Name],
						Weight:   []string{service.Weight},
					}
				} else {
					serviceSpecMap[service.Name].URL = append(serviceSpecMap[service.Name].URL, fmt.Sprintf("http://%s", routeURL))
					serviceSpecMap[service.Name].Weight = append(serviceSpecMap[service.Name].Weight, service.Weight)
				}
				delete(serviceNodePort, service.Name)
			} else {
				if _, ok := serviceSpecMap[service.Name]; !ok {
					serviceSpecMap[service.Name] = &ServiceSpec{Namespace: namespace,
						Name:     service.Name,
						URL:      []string{fmt.Sprintf("http://%s", routeURL)},
						NodePort: "",
						Weight:   []string{service.Weight},
					}
				} else {
					serviceSpecMap[service.Name].URL = append(serviceSpecMap[service.Name].URL, fmt.Sprintf("http://%s", routeURL))
					serviceSpecMap[service.Name].Weight = append(serviceSpecMap[service.Name].Weight, service.Weight)
				}
			}
		}
	}

	for k, v := range serviceNodePort {
		serviceSpecs = append(serviceSpecs, ServiceSpec{Namespace: namespace, Name: k, URL: nil, NodePort: v, Weight: nil})
	}
	for _, v := range serviceSpecMap {
		serviceSpecs = append(serviceSpecs, *v)
	}

	return serviceSpecs
}

// Discard empty elements
func emptyFilter(data []string) []string {
	var res []string

	for _, ele := range data {
		if ele != "" {
			res = append(res, ele)
		}
	}

	return res
}

// Get service provide service name and node-port as map data structure
func getService(namespace string) (map[string]string, error) {
	serviceNodePort := make(map[string]string)
	cmdArgText := fmt.Sprintf("get svc -o json --config=%s -n %s", systemKubeConfigPath, namespace)
	tokens := strings.Split(cmdArgText, " ")
	cmdName := instanceState.InstanceConfig.OcPath
	cmdOut, err := runner.Output(cmdName, tokens...)
	if err != nil {
		return nil, err
	}

	if strings.Contains(string(cmdOut), "No resources found") {
		return nil, nil
	}

	var data Service
	err = json.Unmarshal(cmdOut, &data)
	if err != nil {
		return nil, err
	}

	for _, item := range data.Items {
		for _, port := range item.Spec.Ports {
			nodePort := ""
			if port.NodePort != 0 {
				nodePort = fmt.Sprintf("%d", port.NodePort)
			}
			serviceNodePort[item.Metadata.Name] = nodePort
		}
	}
	return serviceNodePort, nil
}

// Get all projects a user belongs to
func getProjects() ([]string, error) {
	cmdArgText := fmt.Sprintf("get projects --config=%s %s", systemKubeConfigPath, ProjectsCustomCol)
	tokens := strings.Split(cmdArgText, " ")
	cmdName := instanceState.InstanceConfig.OcPath
	cmdOut, err := runner.Output(cmdName, tokens...)
	if err != nil {
		return []string{}, err
	}

	contents := strings.Split(string(cmdOut), "\n")
	return emptyFilter(contents[1:]), nil
}
<commit_msg>Issue #1215 Typo in minishift openshift service<commit_after>/*
Copyright (C) 2017 Red Hat, 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 openshift

import (
	"fmt"
	"path/filepath"
	"strconv"
	"strings"

	"encoding/json"

	"github.com/minishift/minishift/pkg/minikube/constants"
	instanceState "github.com/minishift/minishift/pkg/minishift/config"
	"github.com/minishift/minishift/pkg/util"
	"github.com/pkg/errors"
)

var (
	systemKubeConfigPath string
	runner               util.Runner = &util.RealRunner{}
)

func init() {
	systemKubeConfigPath = filepath.Join(constants.Minipath, "machines", constants.MachineName+"_kubeconfig")
}

type Service struct {
	Items []struct {
		Metadata struct {
			Name string `json:"name"`
		} `json:"metadata"`
		Spec struct {
			Ports []struct {
				NodePort int `json:"nodePort"`
			} `json:"ports"`
		} `json:"spec"`
	} `json:"items"`
}

type Route struct {
	Items []struct {
		Spec struct {
			AlternateBackends []struct {
				Name   string `json:"name"`
				Weight int    `json:"weight"`
			} `json:"alternateBackends"`
			Host string `json:"host"`
			To   struct {
				Name   string `json:"name"`
				Weight int    `json:"weight"`
			} `json:"to"`
		} `json:"spec"`
	} `json:"items"`
}

type ServiceWeight struct {
	Name   string
	Weight string
}

type ServiceSpec struct {
	Namespace string
	Name      string
	URL       []string
	NodePort  string
	Weight    []string
}

const (
	ProjectsCustomCol = "-o=custom-columns=NAME:.metadata.name"
)

// GetServiceSpecs takes Namespace string and return route/nodeport/name/weight in a ServiceSpec structure
func GetServiceSpecs(serviceNamespace string) ([]ServiceSpec, error) {
	var serviceSpecs []ServiceSpec

	if serviceNamespace != "" && !isProjectExists(serviceNamespace) {
		return serviceSpecs, errors.New(fmt.Sprintf("Namespace %s doesn't exist", serviceNamespace))
	}

	namespaces, err := getValidNamespaces(serviceNamespace)
	if err != nil {
		return serviceSpecs, err
	}

	// iterate over namespaces, get command output, format route and nodePort
	for _, namespace := range namespaces {
		serviceNodePort, err := getService(namespace)
		if err != nil {
			return serviceSpecs, err
		}
		routeSpec, err := getRouteSpec(namespace)
		if err != nil {
			return serviceSpecs, err
		}
		if serviceNodePort != nil {
			serviceSpecs = append(serviceSpecs, filterAndUpdateServiceSpecs(routeSpec, namespace, serviceNodePort)...)
		}
	}
	if len(serviceSpecs) == 0 {
		return serviceSpecs, errors.New(fmt.Sprintf("No services defined in namespace '%s'", serviceNamespace))
	}

	return serviceSpecs, nil
}

// Check whether project exists or not
func isProjectExists(projectName string) bool {
	projects, err := getProjects()
	if err != nil {
		return false
	}

	for _, name := range projects {
		if name == projectName {
			return true
		}
	}

	return false
}

func getValidNamespaces(serviceListNamespace string) ([]string, error) {
	var (
		namespaces []string
		err        error
	)

	// If namespace is default then consider all namespaces user belongs to
	if serviceListNamespace == "" {
		namespaces, err = getProjects()
		if err != nil {
			return namespaces, errors.Wrap(err, "Error getting valid namespaces user belongs to.")
		}
	} else {
		namespaces = append(namespaces, serviceListNamespace)
	}

	return namespaces, nil
}

// getRouteSpec take valid namespace and return map which have route as key and
// value be ServiceWeight struct
func getRouteSpec(namespace string) (map[string][]ServiceWeight, error) {
	routeSpec := make(map[string][]ServiceWeight)
	cmdArgText := fmt.Sprintf("get route -o json --config=%s -n %s", systemKubeConfigPath, namespace)
	tokens := strings.Split(cmdArgText, " ")
	cmdName := instanceState.InstanceConfig.OcPath
	cmdOut, err := runner.Output(cmdName, tokens...)
	if err != nil {
		return nil, err
	}

	if strings.Contains(string(cmdOut), "No resources found") {
		return nil, nil
	}

	var data Route
	err = json.Unmarshal(cmdOut, &data)
	if err != nil {
		return nil, err
	}

	for _, item := range data.Items {
		totalWeight := item.Spec.To.Weight
		for _, alternateBackend := range item.Spec.AlternateBackends {
			totalWeight += alternateBackend.Weight
		}
		routeSpec[item.Spec.Host] = []ServiceWeight{{Name: item.Spec.To.Name, Weight: calculateWeight(float64(item.Spec.To.Weight), float64(totalWeight))}}
		for _, alternateBackend := range item.Spec.AlternateBackends {
			routeSpec[item.Spec.Host] = append(routeSpec[item.Spec.Host], ServiceWeight{Name: alternateBackend.Name,
				Weight: calculateWeight(float64(alternateBackend.Weight), float64(totalWeight))})
		}
	}
	return routeSpec, nil
}

func calculateWeight(a float64, b float64) string {
	weightPercentage := int(a / b * 100)
	if weightPercentage != 100 {
		return strconv.Itoa(weightPercentage) + "%"
	}
	return ""
}

func filterAndUpdateServiceSpecs(routeSpec map[string][]ServiceWeight, namespace string, serviceNodePort map[string]string) []ServiceSpec {
	var serviceSpecs []ServiceSpec
	serviceSpecMap := make(map[string]*ServiceSpec)

	for routeURL, v := range routeSpec {
		for _, service := range v {
			// split content on colon to separate NAME, HOST and Weight
			if _, ok := serviceNodePort[service.Name]; ok {
				if _, ok := serviceSpecMap[service.Name]; !ok {
					serviceSpecMap[service.Name] = &ServiceSpec{Namespace: namespace,
						Name:     service.Name,
						URL:      []string{fmt.Sprintf("http://%s", routeURL)},
						NodePort: serviceNodePort[service.Name],
						Weight:   []string{service.Weight},
					}
				} else {
					serviceSpecMap[service.Name].URL = append(serviceSpecMap[service.Name].URL, fmt.Sprintf("http://%s", routeURL))
					serviceSpecMap[service.Name].Weight = append(serviceSpecMap[service.Name].Weight, service.Weight)
				}
				delete(serviceNodePort, service.Name)
			} else {
				if _, ok := serviceSpecMap[service.Name]; !ok {
					serviceSpecMap[service.Name] = &ServiceSpec{Namespace: namespace,
						Name:     service.Name,
						URL:      []string{fmt.Sprintf("http://%s", routeURL)},
						NodePort: "",
						Weight:   []string{service.Weight},
					}
				} else {
					serviceSpecMap[service.Name].URL = append(serviceSpecMap[service.Name].URL, fmt.Sprintf("http://%s", routeURL))
					serviceSpecMap[service.Name].Weight = append(serviceSpecMap[service.Name].Weight, service.Weight)
				}
			}
		}
	}

	for k, v := range serviceNodePort {
		serviceSpecs = append(serviceSpecs, ServiceSpec{Namespace: namespace, Name: k, URL: nil, NodePort: v, Weight: nil})
	}
	for _, v := range serviceSpecMap {
		serviceSpecs = append(serviceSpecs, *v)
	}

	return serviceSpecs
}

// Discard empty elements
func emptyFilter(data []string) []string {
	var res []string

	for _, ele := range data {
		if ele != "" {
			res = append(res, ele)
		}
	}

	return res
}

// Get service provide service name and node-port as map data structure
func getService(namespace string) (map[string]string, error) {
	serviceNodePort := make(map[string]string)
	cmdArgText := fmt.Sprintf("get svc -o json --config=%s -n %s", systemKubeConfigPath, namespace)
	tokens := strings.Split(cmdArgText, " ")
	cmdName := instanceState.InstanceConfig.OcPath
	cmdOut, err := runner.Output(cmdName, tokens...)
	if err != nil {
		return nil, err
	}

	if strings.Contains(string(cmdOut), "No resources found") {
		return nil, nil
	}

	var data Service
	err = json.Unmarshal(cmdOut, &data)
	if err != nil {
		return nil, err
	}

	for _, item := range data.Items {
		for _, port := range item.Spec.Ports {
			nodePort := ""
			if port.NodePort != 0 {
				nodePort = fmt.Sprintf("%d", port.NodePort)
			}
			serviceNodePort[item.Metadata.Name] = nodePort
		}
	}
	return serviceNodePort, nil
}

// Get all projects a user belongs to
func getProjects() ([]string, error) {
	cmdArgText := fmt.Sprintf("get projects --config=%s %s", systemKubeConfigPath, ProjectsCustomCol)
	tokens := strings.Split(cmdArgText, " ")
	cmdName := instanceState.InstanceConfig.OcPath
	cmdOut, err := runner.Output(cmdName, tokens...)
	if err != nil {
		return []string{}, err
	}

	contents := strings.Split(string(cmdOut), "\n")
	return emptyFilter(contents[1:]), nil
}
<|endoftext|>