func base64URLDecode(s string) (string, error) {
	v, err := base64.URLEncoding.DecodeString(s)
	if err != nil {
		return "", errors.Wrap(err, "base64URLDecode")
	}
	return string(v), nil
}

func base64URLDecode ( s string ) ( string , error ) { v , err := base64 . URLEncoding . DecodeString ( s ) if err != nil { return " " , errors . Wrap ( err , " " ) } return string ( v ) , nil }

// base64URLDecode decodes the given string as a URL-safe base64 string.

base64URLDecode decodes the given string as a URL - safe base64 string .


func base64URLEncode(s string) (string, error) {
	return base64.URLEncoding.EncodeToString([]byte(s)), nil
}

func base64URLEncode ( s string ) ( string , error ) { return base64 . URLEncoding . EncodeToString ( [ ] byte ( s ) ) , nil }

// base64URLEncode encodes the given string to be URL-safe.

base64URLEncode encodes the given string to be URL - safe .


func byTag(in interface{}) (map[string][]interface{}, error) {
	m := make(map[string][]interface{})

	switch typed := in.(type) {
	case nil:
	case []*dep.CatalogSnippet:
		for _, s := range typed {
			for _, t := range s.Tags {
				m[t] = append(m[t], s)
			}
		}
	case []*dep.CatalogService:
		for _, s := range typed {
			for _, t := range s.ServiceTags {
				m[t] = append(m[t], s)
			}
		}
	case []*dep.HealthService:
		for _, s := range typed {
			for _, t := range s.Tags {
				m[t] = append(m[t], s)
			}
		}
	default:
		return nil, fmt.Errorf("byTag: wrong argument type %T", in)
	}

	return m, nil
}

func byTag ( in interface { } ) ( map [ string ] [ ] interface { } , error ) { m := make ( map [ string ] [ ] interface { } ) switch typed := in . ( type ) { case nil : case [ ] * dep . CatalogSnippet : for _ , s := range typed { for _ , t := range s . Tags { m [ t ] = append ( m [ t ] , s ) } } case [ ] * dep . CatalogService : for _ , s := range typed { for _ , t := range s . ServiceTags { m [ t ] = append ( m [ t ] , s ) } } case [ ] * dep . HealthService : for _ , s := range typed { for _ , t := range s . Tags { m [ t ] = append ( m [ t ] , s ) } } default : return nil , fmt . Errorf ( " " , in ) } return m , nil }

// byTag is a template func that takes the provided services and
// produces a map based on Service tags.
//
// The map key is a string representing the service tag. The map value is a
// slice of Services which have the tag assigned.

byTag is a template func that takes the provided services and produces a map based on Service tags . The map key is a string representing the service tag . The map value is a slice of Services which have the tag assigned .


func explode(pairs []*dep.KeyPair) (map[string]interface{}, error) {
	m := make(map[string]interface{})
	for _, pair := range pairs {
		if err := explodeHelper(m, pair.Key, pair.Value, pair.Key); err != nil {
			return nil, errors.Wrap(err, "explode")
		}
	}
	return m, nil
}

func explode ( pairs [ ] * dep . KeyPair ) ( map [ string ] interface { } , error ) { m := make ( map [ string ] interface { } ) for _ , pair := range pairs { if err := explodeHelper ( m , pair . Key , pair . Value , pair . Key ) ; err != nil { return nil , errors . Wrap ( err , " " ) } } return m , nil }

// explode is used to expand a list of keypairs into a deeply-nested hash.

explode is used to expand a list of keypairs into a deeply - nested hash .


func explodeHelper(m map[string]interface{}, k, v, p string) error {
	if strings.Contains(k, "/") {
		parts := strings.Split(k, "/")
		top := parts[0]
		key := strings.Join(parts[1:], "/")

		if _, ok := m[top]; !ok {
			m[top] = make(map[string]interface{})
		}
		nest, ok := m[top].(map[string]interface{})
		if !ok {
			return fmt.Errorf("not a map: %q: %q already has value %q", p, top, m[top])
		}
		return explodeHelper(nest, key, v, k)
	}

	if k != "" {
		m[k] = v
	}

	return nil
}

func explodeHelper ( m map [ string ] interface { } , k , v , p string ) error { if strings . Contains ( k , " " ) { parts := strings . Split ( k , " " ) top := parts [ 0 ] key := strings . Join ( parts [ 1 : ] , " " ) if _ , ok := m [ top ] ; ! ok { m [ top ] = make ( map [ string ] interface { } ) } nest , ok := m [ top ] . ( map [ string ] interface { } ) if ! ok { return fmt . Errorf ( " " , p , top , m [ top ] ) } return explodeHelper ( nest , key , v , k ) } if k != " " { m [ k ] = v } return nil }

// explodeHelper is a recursive helper for explode.

explodeHelper is a recursive helper for explode .


func in(l, v interface{}) (bool, error) {
	lv := reflect.ValueOf(l)
	vv := reflect.ValueOf(v)

	switch lv.Kind() {
	case reflect.Array, reflect.Slice:
		// if the slice contains 'interface' elements, then the element needs to be extracted directly to examine its type,
		// otherwise it will just resolve to 'interface'.
		var interfaceSlice []interface{}
		if reflect.TypeOf(l).Elem().Kind() == reflect.Interface {
			interfaceSlice = l.([]interface{})
		}

		for i := 0; i < lv.Len(); i++ {
			var lvv reflect.Value
			if interfaceSlice != nil {
				lvv = reflect.ValueOf(interfaceSlice[i])
			} else {
				lvv = lv.Index(i)
			}

			switch lvv.Kind() {
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				switch vv.Kind() {
				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
					if vv.Int() == lvv.Int() {
						return true, nil
					}
				}
			case reflect.Float32, reflect.Float64:
				switch vv.Kind() {
				case reflect.Float32, reflect.Float64:
					if vv.Float() == lvv.Float() {
						return true, nil
					}
				}
			case reflect.String:
				if vv.Type() == lvv.Type() && vv.String() == lvv.String() {
					return true, nil
				}
			}
		}
	case reflect.String:
		if vv.Type() == lv.Type() && strings.Contains(lv.String(), vv.String()) {
			return true, nil
		}
	}

	return false, nil
}

// in searches for a given value in a given interface.

in searches for a given value in a given interface .


func indent(spaces int, s string) (string, error) {
	if spaces < 0 {
		return "", fmt.Errorf("indent value must be a positive integer")
	}
	var output, prefix []byte
	var sp bool
	var size int
	prefix = []byte(strings.Repeat(" ", spaces))
	sp = true
	for _, c := range []byte(s) {
		if sp && c != '\n' {
			output = append(output, prefix...)
			size += spaces
		}
		output = append(output, c)
		sp = c == '\n'
		size++
	}
	return string(output[:size]), nil
}

func indent ( spaces int , s string ) ( string , error ) { if spaces < 0 { return " " , fmt . Errorf ( " " ) } var output , prefix [ ] byte var sp bool var size int prefix = [ ] byte ( strings . Repeat ( " " , spaces ) ) sp = true for _ , c := range [ ] byte ( s ) { if sp && c != '\n' { output = append ( output , prefix ... ) size += spaces } output = append ( output , c ) sp = c == '\n' size ++ } return string ( output [ : size ] ) , nil }

// Indent prefixes each line of a string with the specified number of spaces


func join(sep string, a []string) (string, error) {
	return strings.Join(a, sep), nil
}

func join ( sep string , a [ ] string ) ( string , error ) { return strings . Join ( a , sep ) , nil }

// join is a version of strings.Join that can be piped

join is a version of strings . Join that can be piped


func parseBool(s string) (bool, error) {
	if s == "" {
		return false, nil
	}

	result, err := strconv.ParseBool(s)
	if err != nil {
		return false, errors.Wrap(err, "parseBool")
	}
	return result, nil
}

func parseBool ( s string ) ( bool , error ) { if s == " " { return false , nil } result , err := strconv . ParseBool ( s ) if err != nil { return false , errors . Wrap ( err , " " ) } return result , nil }

// parseBool parses a string into a boolean


func parseFloat(s string) (float64, error) {
	if s == "" {
		return 0.0, nil
	}

	result, err := strconv.ParseFloat(s, 10)
	if err != nil {
		return 0, errors.Wrap(err, "parseFloat")
	}
	return result, nil
}

func parseFloat ( s string ) ( float64 , error ) { if s == " " { return 0.0 , nil } result , err := strconv . ParseFloat ( s , 10 ) if err != nil { return 0 , errors . Wrap ( err , " " ) } return result , nil }

// parseFloat parses a string into a base 10 float


func parseInt(s string) (int64, error) {
	if s == "" {
		return 0, nil
	}

	result, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		return 0, errors.Wrap(err, "parseInt")
	}
	return result, nil
}

func parseInt ( s string ) ( int64 , error ) { if s == " " { return 0 , nil } result , err := strconv . ParseInt ( s , 10 , 64 ) if err != nil { return 0 , errors . Wrap ( err , " " ) } return result , nil }

// parseInt parses a string into a base 10 int


func parseJSON(s string) (interface{}, error) {
	if s == "" {
		return map[string]interface{}{}, nil
	}

	var data interface{}
	if err := json.Unmarshal([]byte(s), &data); err != nil {
		return nil, err
	}
	return data, nil
}

func parseJSON ( s string ) ( interface { } , error ) { if s == " " { return map [ string ] interface { } { } , nil } var data interface { } if err := json . Unmarshal ( [ ] byte ( s ) , & data ) ; err != nil { return nil , err } return data , nil }

// parseJSON returns a structure for valid JSON


func parseUint(s string) (uint64, error) {
	if s == "" {
		return 0, nil
	}

	result, err := strconv.ParseUint(s, 10, 64)
	if err != nil {
		return 0, errors.Wrap(err, "parseUint")
	}
	return result, nil
}

func parseUint ( s string ) ( uint64 , error ) { if s == " " { return 0 , nil } result , err := strconv . ParseUint ( s , 10 , 64 ) if err != nil { return 0 , errors . Wrap ( err , " " ) } return result , nil }

// parseUint parses a string into a base 10 int


func plugin(name string, args ...string) (string, error) {
	if name == "" {
		return "", nil
	}

	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)

	// Strip and trim each arg or else some plugins get confused with the newline
	// characters
	jsons := make([]string, 0, len(args))
	for _, arg := range args {
		if v := strings.TrimSpace(arg); v != "" {
			jsons = append(jsons, v)
		}
	}

	cmd := exec.Command(name, jsons...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	if err := cmd.Start(); err != nil {
		return "", fmt.Errorf("exec %q: %s\n\nstdout:\n\n%s\n\nstderr:\n\n%s",
			name, err, stdout.Bytes(), stderr.Bytes())
	}

	done := make(chan error, 1)
	go func() {
		done <- cmd.Wait()
	}()

	select {
	case <-time.After(30 * time.Second):
		if cmd.Process != nil {
			if err := cmd.Process.Kill(); err != nil {
				return "", fmt.Errorf("exec %q: failed to kill", name)
			}
		}
		<-done // Allow the goroutine to exit
		return "", fmt.Errorf("exec %q: did not finish in 30s", name)
	case err := <-done:
		if err != nil {
			return "", fmt.Errorf("exec %q: %s\n\nstdout:\n\n%s\n\nstderr:\n\n%s",
				name, err, stdout.Bytes(), stderr.Bytes())
		}
	}

	return strings.TrimSpace(stdout.String()), nil
}

// plugin executes a subprocess as the given command string. It is assumed the
// resulting command returns JSON which is then parsed and returned as the
// value for use in the template.

plugin executes a subprocess as the given command string . It is assumed the resulting command returns JSON which is then parsed and returned as the value for use in the template .


func replaceAll(f, t, s string) (string, error) {
	return strings.Replace(s, f, t, -1), nil
}

func replaceAll ( f , t , s string ) ( string , error ) { return strings . Replace ( s , f , t , - 1 ) , nil }

// replaceAll replaces all occurrences of a value in a string with the given
// replacement value.

replaceAll replaces all occurrences of a value in a string with the given replacement value .


func regexReplaceAll(re, pl, s string) (string, error) {
	compiled, err := regexp.Compile(re)
	if err != nil {
		return "", err
	}
	return compiled.ReplaceAllString(s, pl), nil
}

func regexReplaceAll ( re , pl , s string ) ( string , error ) { compiled , err := regexp . Compile ( re ) if err != nil { return " " , err } return compiled . ReplaceAllString ( s , pl ) , nil }

// regexReplaceAll replaces all occurrences of a regular expression with
// the given replacement value.

regexReplaceAll replaces all occurrences of a regular expression with the given replacement value .


func regexMatch(re, s string) (bool, error) {
	compiled, err := regexp.Compile(re)
	if err != nil {
		return false, err
	}
	return compiled.MatchString(s), nil
}

func regexMatch ( re , s string ) ( bool , error ) { compiled , err := regexp . Compile ( re ) if err != nil { return false , err } return compiled . MatchString ( s ) , nil }

// regexMatch returns true or false if the string matches
// the given regular expression

regexMatch returns true or false if the string matches the given regular expression


func split(sep, s string) ([]string, error) {
	s = strings.TrimSpace(s)
	if s == "" {
		return []string{}, nil
	}
	return strings.Split(s, sep), nil
}

func split ( sep , s string ) ( [ ] string , error ) { s = strings . TrimSpace ( s ) if s == " " { return [ ] string { } , nil } return strings . Split ( s , sep ) , nil }

// split is a version of strings.Split that can be piped

split is a version of strings . Split that can be piped


func timestamp(s ...string) (string, error) {
	switch len(s) {
	case 0:
		return now().Format(time.RFC3339), nil
	case 1:
		if s[0] == "unix" {
			return strconv.FormatInt(now().Unix(), 10), nil
		}
		return now().Format(s[0]), nil
	default:
		return "", fmt.Errorf("timestamp: wrong number of arguments, expected 0 or 1"+
			", but got %d", len(s))
	}
}

func timestamp ( s ... string ) ( string , error ) { switch len ( s ) { case 0 : return now ( ) . Format ( time . RFC3339 ) , nil case 1 : if s [ 0 ] == " " { return strconv . FormatInt ( now ( ) . Unix ( ) , 10 ) , nil } return now ( ) . Format ( s [ 0 ] ) , nil default : return " " , fmt . Errorf ( " " + " " , len ( s ) ) } }

// timestamp returns the current UNIX timestamp in UTC. If an argument is
// specified, it will be used to format the timestamp.

timestamp returns the current UNIX timestamp in UTC . If an argument is specified it will be used to format the timestamp .


func toJSON(i interface{}) (string, error) {
	result, err := json.Marshal(i)
	if err != nil {
		return "", errors.Wrap(err, "toJSON")
	}
	return string(bytes.TrimSpace(result)), err
}

func toJSON ( i interface { } ) ( string , error ) { result , err := json . Marshal ( i ) if err != nil { return " " , errors . Wrap ( err , " " ) } return string ( bytes . TrimSpace ( result ) ) , err }

// toJSON converts the given structure into a deeply nested JSON string.

toJSON converts the given structure into a deeply nested JSON string .


func toJSONPretty(m map[string]interface{}) (string, error) {
	result, err := json.MarshalIndent(m, "", "  ")
	if err != nil {
		return "", errors.Wrap(err, "toJSONPretty")
	}
	return string(bytes.TrimSpace(result)), err
}

func toJSONPretty ( m map [ string ] interface { } ) ( string , error ) { result , err := json . MarshalIndent ( m , " " , " " ) if err != nil { return " " , errors . Wrap ( err , " " ) } return string ( bytes . TrimSpace ( result ) ) , err }

// toJSONPretty converts the given structure into a deeply nested pretty JSON
// string.

toJSONPretty converts the given structure into a deeply nested pretty JSON string .


func toYAML(m map[string]interface{}) (string, error) {
	result, err := yaml.Marshal(m)
	if err != nil {
		return "", errors.Wrap(err, "toYAML")
	}
	return string(bytes.TrimSpace(result)), nil
}

func toYAML ( m map [ string ] interface { } ) ( string , error ) { result , err := yaml . Marshal ( m ) if err != nil { return " " , errors . Wrap ( err , " " ) } return string ( bytes . TrimSpace ( result ) ) , nil }

// toYAML converts the given structure into a deeply nested YAML string.

toYAML converts the given structure into a deeply nested YAML string .


func toTOML(m map[string]interface{}) (string, error) {
	buf := bytes.NewBuffer([]byte{})
	enc := toml.NewEncoder(buf)
	if err := enc.Encode(m); err != nil {
		return "", errors.Wrap(err, "toTOML")
	}
	result, err := ioutil.ReadAll(buf)
	if err != nil {
		return "", errors.Wrap(err, "toTOML")
	}
	return string(bytes.TrimSpace(result)), nil
}

func toTOML ( m map [ string ] interface { } ) ( string , error ) { buf := bytes . NewBuffer ( [ ] byte { } ) enc := toml . NewEncoder ( buf ) if err := enc . Encode ( m ) ; err != nil { return " " , errors . Wrap ( err , " " ) } result , err := ioutil . ReadAll ( buf ) if err != nil { return " " , errors . Wrap ( err , " " ) } return string ( bytes . TrimSpace ( result ) ) , nil }

// toTOML converts the given structure into a deeply nested TOML string.

toTOML converts the given structure into a deeply nested TOML string .


func add(b, a interface{}) (interface{}, error) {
	av := reflect.ValueOf(a)
	bv := reflect.ValueOf(b)

	switch av.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		switch bv.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			return av.Int() + bv.Int(), nil
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			return av.Int() + int64(bv.Uint()), nil
		case reflect.Float32, reflect.Float64:
			return float64(av.Int()) + bv.Float(), nil
		default:
			return nil, fmt.Errorf("add: unknown type for %q (%T)", bv, b)
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		switch bv.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			return int64(av.Uint()) + bv.Int(), nil
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			return av.Uint() + bv.Uint(), nil
		case reflect.Float32, reflect.Float64:
			return float64(av.Uint()) + bv.Float(), nil
		default:
			return nil, fmt.Errorf("add: unknown type for %q (%T)", bv, b)
		}
	case reflect.Float32, reflect.Float64:
		switch bv.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			return av.Float() + float64(bv.Int()), nil
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			return av.Float() + float64(bv.Uint()), nil
		case reflect.Float32, reflect.Float64:
			return av.Float() + bv.Float(), nil
		default:
			return nil, fmt.Errorf("add: unknown type for %q (%T)", bv, b)
		}
	default:
		return nil, fmt.Errorf("add: unknown type for %q (%T)", av, a)
	}
}

func add ( b , a interface { } ) ( interface { } , error ) { av := reflect . ValueOf ( a ) bv := reflect . ValueOf ( b ) switch av . Kind ( ) { case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 : switch bv . Kind ( ) { case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 : return av . Int ( ) + bv . Int ( ) , nil case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 : return av . Int ( ) + int64 ( bv . Uint ( ) ) , nil case reflect . Float32 , reflect . Float64 : return float64 ( av . Int ( ) ) + bv . Float ( ) , nil default : return nil , fmt . Errorf ( " " , bv , b ) } case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 : switch bv . Kind ( ) { case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 : return int64 ( av . Uint ( ) ) + bv . Int ( ) , nil case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 : return av . Uint ( ) + bv . Uint ( ) , nil case reflect . Float32 , reflect . Float64 : return float64 ( av . Uint ( ) ) + bv . Float ( ) , nil default : return nil , fmt . Errorf ( " " , bv , b ) } case reflect . Float32 , reflect . Float64 : switch bv . Kind ( ) { case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 : return av . Float ( ) + float64 ( bv . Int ( ) ) , nil case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 : return av . Float ( ) + float64 ( bv . Uint ( ) ) , nil case reflect . Float32 , reflect . Float64 : return av . Float ( ) + bv . Float ( ) , nil default : return nil , fmt . Errorf ( " " , bv , b ) } default : return nil , fmt . Errorf ( " " , av , a ) } }


func modulo(b, a interface{}) (interface{}, error) {
	av := reflect.ValueOf(a)
	bv := reflect.ValueOf(b)

	switch av.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		switch bv.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			return av.Int() % bv.Int(), nil
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			return av.Int() % int64(bv.Uint()), nil
		default:
			return nil, fmt.Errorf("modulo: unknown type for %q (%T)", bv, b)
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		switch bv.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			return int64(av.Uint()) % bv.Int(), nil
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			return av.Uint() % bv.Uint(), nil
		default:
			return nil, fmt.Errorf("modulo: unknown type for %q (%T)", bv, b)
		}
	default:
		return nil, fmt.Errorf("modulo: unknown type for %q (%T)", av, a)
	}
}

func modulo ( b , a interface { } ) ( interface { } , error ) { av := reflect . ValueOf ( a ) bv := reflect . ValueOf ( b ) switch av . Kind ( ) { case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 : switch bv . Kind ( ) { case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 : return av . Int ( ) % bv . Int ( ) , nil case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 : return av . Int ( ) % int64 ( bv . Uint ( ) ) , nil default : return nil , fmt . Errorf ( " " , bv , b ) } case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 : switch bv . Kind ( ) { case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 : return int64 ( av . Uint ( ) ) % bv . Int ( ) , nil case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 : return av . Uint ( ) % bv . Uint ( ) , nil default : return nil , fmt . Errorf ( " " , bv , b ) } default : return nil , fmt . Errorf ( " " , av , a ) } }

// modulo returns the modulo of b from a.


func (item Item) Expired() bool {
	if item.Expiration == 0 {
		return false
	}
	return time.Now().UnixNano() > item.Expiration
}

func ( item Item ) Expired ( ) bool { if item . Expiration == 0 { return false } return time . Now ( ) . UnixNano ( ) > item . Expiration }

// Returns true if the item has expired.


func (c *cache) SetDefault(k string, x interface{}) {
	c.Set(k, x, DefaultExpiration)
}

func ( c * cache ) SetDefault ( k string , x interface { } ) { c . Set ( k , x , DefaultExpiration ) }

// Add an item to the cache, replacing any existing item, using the default
// expiration.

Add an item to the cache replacing any existing item using the default expiration .


func (c *cache) Add(k string, x interface{}, d time.Duration) error {
	c.mu.Lock()
	_, found := c.get(k)
	if found {
		c.mu.Unlock()
		return fmt.Errorf("Item %s already exists", k)
	}
	c.set(k, x, d)
	c.mu.Unlock()
	return nil
}

func ( c * cache ) Add ( k string , x interface { } , d time . Duration ) error { c . mu . Lock ( ) _ , found := c . get ( k ) if found { c . mu . Unlock ( ) return fmt . Errorf ( " " , k ) } c . set ( k , x , d ) c . mu . Unlock ( ) return nil }

// Add an item to the cache only if an item doesn't already exist for the given
// key, or if the existing item has expired. Returns an error otherwise.

Add an item to the cache only if an item doesn t already exist for the given key or if the existing item has expired . Returns an error otherwise .


func (c *cache) Get(k string) (interface{}, bool) {
	c.mu.RLock()
	// "Inlining" of get and Expired
	item, found := c.items[k]
	if !found {
		c.mu.RUnlock()
		return nil, false
	}
	if item.Expiration > 0 {
		if time.Now().UnixNano() > item.Expiration {
			c.mu.RUnlock()
			return nil, false
		}
	}
	c.mu.RUnlock()
	return item.Object, true
}

func
(
c
*
cache
)
Get
(
k
string
)
(
interface
{
}
,
bool
)
{
c
.
mu
.
RLock
(
)
// "Inlining" of get and Expired
item
,
found
:=
c
.
items
[
k
]
if
!
found
{
c
.
mu
.
RUnlock
(
)
return
,
false
}
if
item
.
Expiration
>
0
{
if
time
.
Now
(
)
.
UnixNano
(
)
>
item
.
Expiration
{
c
.
mu
.
RUnlock
(
)
return
,
false
}
}
c
.
mu
.
RUnlock
(
)
return
item
.
Object
,
true
}

// Get an item from the cache. Returns the item or nil, and a bool indicating
// whether the key was found.

Get an item from the cache . Returns the item or nil and a bool indicating whether the key was found .


func (c *cache) IncrementInt(k string, n int) (int, error) {
	c.mu.Lock()
	v, found := c.items[k]
	if !found || v.Expired() {
		c.mu.Unlock()
		return 0, fmt.Errorf("Item %s not found", k)
	}
	rv, ok := v.Object.(int)
	if !ok {
		c.mu.Unlock()
		return 0, fmt.Errorf("The value for %s is not an int", k)
	}
	nv := rv + n
	v.Object = nv
	c.items[k] = v
	c.mu.Unlock()
	return nv, nil
}

func ( c * cache ) IncrementInt ( k string , n int ) ( int , error ) { c . mu . Lock ( ) v , found := c . items [ k ] if ! found || v . Expired ( ) { c . mu . Unlock ( ) return 0 , fmt . Errorf ( " " , k ) } rv , ok := v . Object . ( int ) if ! ok { c . mu . Unlock ( ) return 0 , fmt . Errorf ( " " , k ) } nv := rv + n v . Object = nv c . items [ k ] = v c . mu . Unlock ( ) return nv , nil }

// Increment an item of type int by n. Returns an error if the item's value is
// not an int, or if it was not found. If there is no error, the incremented
// value is returned.

Increment an item of type int by n . Returns an error if the item s value is not an int or if it was not found . If there is no error the incremented value is returned .


func (c *cache) Delete(k string) {
	c.mu.Lock()
	v, evicted := c.delete(k)
	c.mu.Unlock()
	if evicted {
		c.onEvicted(k, v)
	}
}

func ( c * cache ) Delete ( k string ) { c . mu . Lock ( ) v , evicted := c . delete ( k ) c . mu . Unlock ( ) if evicted { c . onEvicted ( k , v ) } }

// Delete an item from the cache. Does nothing if the key is not in the cache.

Delete an item from the cache . Does nothing if the key is not in the cache .


func (c *cache) DeleteExpired() {
	var evictedItems []keyAndValue
	now := time.Now().UnixNano()
	c.mu.Lock()
	for k, v := range c.items {
		// "Inlining" of expired
		if v.Expiration > 0 && now > v.Expiration {
			ov, evicted := c.delete(k)
			if evicted {
				evictedItems = append(evictedItems, keyAndValue{k, ov})
			}
		}
	}
	c.mu.Unlock()
	for _, v := range evictedItems {
		c.onEvicted(v.key, v.value)
	}
}

func
(
c
*
cache
)
DeleteExpired
(
)
{
var
evictedItems
[
]
keyAndValue
now
:=
time
.
Now
(
)
.
UnixNano
(
)
c
.
mu
.
Lock
(
)
for
k
,
v
:=
range
c
.
items
{
// "Inlining" of expired
if
v
.
Expiration
>
0
&&
now
>
v
.
Expiration
{
ov
,
evicted
:=
c
.
delete
(
k
)
if
evicted
{
evictedItems
=
append
(
evictedItems
,
keyAndValue
{
k
,
ov
}
)
}
}
}
c
.
mu
.
Unlock
(
)
for
_
,
v
:=
range
evictedItems
{
c
.
onEvicted
(
v
.
key
,
v
.
value
)
}
}

// Delete all expired items from the cache.

Delete all expired items from the cache .


func (c *cache) Items() map[string]Item {
	c.mu.RLock()
	defer c.mu.RUnlock()
	m := make(map[string]Item, len(c.items))
	now := time.Now().UnixNano()
	for k, v := range c.items {
		// "Inlining" of Expired
		if v.Expiration > 0 {
			if now > v.Expiration {
				continue
			}
		}
		m[k] = v
	}
	return m
}

func
(
c
*
cache
)
Items
(
)
map
[
string
]
Item
{
c
.
mu
.
RLock
(
)
defer
c
.
mu
.
RUnlock
(
)
m
:=
make
(
map
[
string
]
Item
,
len
(
c
.
items
)
)
now
:=
time
.
Now
(
)
.
UnixNano
(
)
for
k
,
v
:=
range
c
.
items
{
// "Inlining" of Expired
if
v
.
Expiration
>
0
{
if
now
>
v
.
Expiration
{
continue
}
}
m
[
k
]
=
v
}
return
m
}

// Copies all unexpired items in the cache into a new map and returns it.

Copies all unexpired items in the cache into a new map and returns it .


func (c *cache) ItemCount() int {
	c.mu.RLock()
	n := len(c.items)
	c.mu.RUnlock()
	return n
}

func ( c * cache ) ItemCount ( ) int { c . mu . RLock ( ) n := len ( c . items ) c . mu . RUnlock ( ) return n }

// Returns the number of items in the cache. This may include items that have
// expired, but have not yet been cleaned up.

Returns the number of items in the cache . This may include items that have expired but have not yet been cleaned up .


func (c *cache) Flush() {
	c.mu.Lock()
	c.items = map[string]Item{}
	c.mu.Unlock()
}

func ( c * cache ) Flush ( ) { c . mu . Lock ( ) c . items = map [ string ] Item { } c . mu . Unlock ( ) }


// Why is all this 5x faster than a for loop?

// djb2 with better shuffling. 5x faster than FNV with the hash.Hash overhead.

djb2 with better shuffling . 5x faster than FNV with the hash . Hash overhead .


func (NetLinkClient) LinkByName(name string) (netlink.Link, error) {
	return netlink.LinkByName(name)
}

func ( NetLinkClient ) LinkByName ( name string ) ( netlink . Link , error ) { return netlink . LinkByName ( name ) }

// LinkByName finds a link by name and returns a pointer to the object


func NewMockTaskEngine(ctrl *gomock.Controller) *MockTaskEngine {
	mock := &MockTaskEngine{ctrl: ctrl}
	mock.recorder = &MockTaskEngineMockRecorder{mock}
	return mock
}

func NewMockTaskEngine ( ctrl * gomock . Controller ) * MockTaskEngine { mock := & MockTaskEngine { ctrl : ctrl } mock . recorder = & MockTaskEngineMockRecorder { mock } return mock }

// NewMockTaskEngine creates a new mock instance


func (mr *MockTaskEngineMockRecorder) AddTask(arg0 interface{}) *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTask", reflect.TypeOf((*MockTaskEngine)(nil).AddTask), arg0)
}

func ( mr * MockTaskEngineMockRecorder ) AddTask ( arg0 interface { } ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockTaskEngine ) ( nil ) . AddTask ) , arg0 ) }

// AddTask indicates an expected call of AddTask


func (m *MockTaskEngine) GetTaskByArn(arg0 string) (*task.Task, bool) {
	ret := m.ctrl.Call(m, "GetTaskByArn", arg0)
	ret0, _ := ret[0].(*task.Task)
	ret1, _ := ret[1].(bool)
	return ret0, ret1
}

func ( m * MockTaskEngine ) GetTaskByArn ( arg0 string ) ( * task . Task , bool ) { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( * task . Task ) ret1 , _ := ret [ 1 ] . ( bool ) return ret0 , ret1 }


func (m *MockTaskEngine) ListTasks() ([]*task.Task, error) {
	ret := m.ctrl.Call(m, "ListTasks")
	ret0, _ := ret[0].([]*task.Task)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

func ( m * MockTaskEngine ) ListTasks ( ) ( [ ] * task . Task , error ) { ret := m . ctrl . Call ( m , " " ) ret0 , _ := ret [ 0 ] . ( [ ] * task . Task ) ret1 , _ := ret [ 1 ] . ( error ) return ret0 , ret1 }


func (m *MockTaskEngine) MarshalJSON() ([]byte, error) {
	ret := m.ctrl.Call(m, "MarshalJSON")
	ret0, _ := ret[0].([]byte)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

func ( m * MockTaskEngine ) MarshalJSON ( ) ( [ ] byte , error ) { ret := m . ctrl . Call ( m , " " ) ret0 , _ := ret [ 0 ] . ( [ ] byte ) ret1 , _ := ret [ 1 ] . ( error ) return ret0 , ret1 }


func (m *MockTaskEngine) MustInit(arg0 context.Context) {
	m.ctrl.Call(m, "MustInit", arg0)
}

func ( m * MockTaskEngine ) MustInit ( arg0 context . Context ) { m . ctrl . Call ( m , " " , arg0 ) }


func (m *MockTaskEngine) StateChangeEvents() chan statechange.Event {
	ret := m.ctrl.Call(m, "StateChangeEvents")
	ret0, _ := ret[0].(chan statechange.Event)
	return ret0
}

func ( m * MockTaskEngine ) StateChangeEvents ( ) chan statechange . Event { ret := m . ctrl . Call ( m , " " ) ret0 , _ := ret [ 0 ] . ( chan statechange . Event ) return ret0 }


func (mr *MockTaskEngineMockRecorder) Version() *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Version", reflect.TypeOf((*MockTaskEngine)(nil).Version))
}

func ( mr * MockTaskEngineMockRecorder ) Version ( ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockTaskEngine ) ( nil ) . Version ) ) }

// Version indicates an expected call of Version


func NewMockImageManager(ctrl *gomock.Controller) *MockImageManager {
	mock := &MockImageManager{ctrl: ctrl}
	mock.recorder = &MockImageManagerMockRecorder{mock}
	return mock
}

func NewMockImageManager ( ctrl * gomock . Controller ) * MockImageManager { mock := & MockImageManager { ctrl : ctrl } mock . recorder = & MockImageManagerMockRecorder { mock } return mock }

// NewMockImageManager creates a new mock instance


func (m *MockImageManager) AddAllImageStates(arg0 []*image.ImageState) {
	m.ctrl.Call(m, "AddAllImageStates", arg0)
}

func ( m * MockImageManager ) AddAllImageStates ( arg0 [ ] * image . ImageState ) { m . ctrl . Call ( m , " " , arg0 ) }


func (mr *MockImageManagerMockRecorder) AddAllImageStates(arg0 interface{}) *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddAllImageStates", reflect.TypeOf((*MockImageManager)(nil).AddAllImageStates), arg0)
}

func ( mr * MockImageManagerMockRecorder ) AddAllImageStates ( arg0 interface { } ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockImageManager ) ( nil ) . AddAllImageStates ) , arg0 ) }

// AddAllImageStates indicates an expected call of AddAllImageStates


func (m *MockImageManager) GetImageStateFromImageName(arg0 string) (*image.ImageState, bool) {
	ret := m.ctrl.Call(m, "GetImageStateFromImageName", arg0)
	ret0, _ := ret[0].(*image.ImageState)
	ret1, _ := ret[1].(bool)
	return ret0, ret1
}

func ( m * MockImageManager ) GetImageStateFromImageName ( arg0 string ) ( * image . ImageState , bool ) { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( * image . ImageState ) ret1 , _ := ret [ 1 ] . ( bool ) return ret0 , ret1 }

// GetImageStateFromImageName mocks base method


func (m *MockImageManager) RecordContainerReference(arg0 *container.Container) error {
	ret := m.ctrl.Call(m, "RecordContainerReference", arg0)
	ret0, _ := ret[0].(error)
	return ret0
}

func ( m * MockImageManager ) RecordContainerReference ( arg0 * container . Container ) error { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( error ) return ret0 }

// RecordContainerReference mocks base method


func (m *MockImageManager) StartImageCleanupProcess(arg0 context.Context) {
	m.ctrl.Call(m, "StartImageCleanupProcess", arg0)
}

func ( m * MockImageManager ) StartImageCleanupProcess ( arg0 context . Context ) { m . ctrl . Call ( m , " " , arg0 ) }

// StartImageCleanupProcess mocks base method


func NewEC2MetadataClient(client HttpClient) EC2MetadataClient {
	if client == nil {
		return &ec2MetadataClientImpl{
			client: ec2metadata.New(
				session.New(), aws.NewConfig().WithMaxRetries(metadataRetries)),
		}
	} else {
		return &ec2MetadataClientImpl{client: client}
	}
}

func NewEC2MetadataClient ( client HttpClient ) EC2MetadataClient { if client == nil { return & ec2MetadataClientImpl { client : ec2metadata . New ( session . New ( ) , aws . NewConfig ( ) . WithMaxRetries ( metadataRetries ) ) , } } else { return & ec2MetadataClientImpl { client : client } } }

// NewEC2MetadataClient creates an ec2metadata client to retrieve metadata


func (c *ec2MetadataClientImpl) DefaultCredentials() (*RoleCredentials, error) {
	securityCredential, err := c.client.GetMetadata(SecurityCrednetialsResource)
	if err != nil {
		return nil, err
	}

	securityCredentialList := strings.Split(strings.TrimSpace(securityCredential), "\n")
	if len(securityCredentialList) == 0 {
		return nil, errors.New("No security credentials in response")
	}

	defaultCredentialName := securityCredentialList[0]

	defaultCredentialStr, err := c.client.GetMetadata(SecurityCrednetialsResource + defaultCredentialName)
	if err != nil {
		return nil, err
	}
	var credential RoleCredentials
	err = json.Unmarshal([]byte(defaultCredentialStr), &credential)
	if err != nil {
		return nil, err
	}
	return &credential, nil
}

func ( c * ec2MetadataClientImpl ) DefaultCredentials ( ) ( * RoleCredentials , error ) { securityCredential , err := c . client . GetMetadata ( SecurityCrednetialsResource ) if err != nil { return nil , err } securityCredentialList := strings . Split ( strings . TrimSpace ( securityCredential ) , " \n " ) if len ( securityCredentialList ) == 0 { return nil , errors . New ( " " ) } defaultCredentialName := securityCredentialList [ 0 ] defaultCredentialStr , err := c . client . GetMetadata ( SecurityCrednetialsResource + defaultCredentialName ) if err != nil { return nil , err } var credential RoleCredentials err = json . Unmarshal ( [ ] byte ( defaultCredentialStr ) , & credential ) if err != nil { return nil , err } return & credential , nil }

// DefaultCredentials returns the credentials associated with the instance iam role


func (c *ec2MetadataClientImpl) GetDynamicData(path string) (string, error) {
	return c.client.GetDynamicData(path)
}

func ( c * ec2MetadataClientImpl ) GetDynamicData ( path string ) ( string , error ) { return c . client . GetDynamicData ( path ) }

// GetDynamicData returns the dynamic data with provided path from instance metadata


func (c *ec2MetadataClientImpl) GetMetadata(path string) (string, error) {
	return c.client.GetMetadata(path)
}

func ( c * ec2MetadataClientImpl ) GetMetadata ( path string ) ( string , error ) { return c . client . GetMetadata ( path ) }

// GetMetadata returns the metadata from instance metadata service specified by the path


func (c *ec2MetadataClientImpl) VPCID(mac string) (string, error) {
	return c.client.GetMetadata(fmt.Sprintf(VPCIDResourceFormat, mac))
}

func ( c * ec2MetadataClientImpl ) VPCID ( mac string ) ( string , error ) { return c . client . GetMetadata ( fmt . Sprintf ( VPCIDResourceFormat , mac ) ) }

// VPCID returns the VPC id for the network interface, given
// its mac address

VPCID returns the VPC id for the network interface given its mac address


func (c *ec2MetadataClientImpl) SubnetID(mac string) (string, error) {
	return c.client.GetMetadata(fmt.Sprintf(SubnetIDResourceFormat, mac))
}

func ( c * ec2MetadataClientImpl ) SubnetID ( mac string ) ( string , error ) { return c . client . GetMetadata ( fmt . Sprintf ( SubnetIDResourceFormat , mac ) ) }

// SubnetID returns the subnet id for the network interface,
// given its mac address

SubnetID returns the subnet id for the network interface given its mac address


func newRefreshCredentialsHandler(ctx context.Context, cluster string, containerInstanceArn string, acsClient wsclient.ClientServer, credentialsManager credentials.Manager, taskEngine engine.TaskEngine) refreshCredentialsHandler {
	// Create a cancelable context from the parent context
	derivedContext, cancel := context.WithCancel(ctx)
	return refreshCredentialsHandler{
		messageBuffer:      make(chan *ecsacs.IAMRoleCredentialsMessage),
		ackRequest:         make(chan *ecsacs.IAMRoleCredentialsAckRequest),
		ctx:                derivedContext,
		cancel:             cancel,
		cluster:            aws.String(cluster),
		containerInstance:  aws.String(containerInstanceArn),
		acsClient:          acsClient,
		credentialsManager: credentialsManager,
		taskEngine:         taskEngine,
	}
}

// newRefreshCredentialsHandler returns a new refreshCredentialsHandler object


func (refreshHandler *refreshCredentialsHandler) handlerFunc() func(message *ecsacs.IAMRoleCredentialsMessage) {
	// return a function that just enqueues IAMRoleCredentials messages into the message buffer
	return func(message *ecsacs.IAMRoleCredentialsMessage) {
		refreshHandler.messageBuffer <- message
	}
}

// handlerFunc returns the request handler function for the ecsacs.IAMRoleCredentialsMessage

handlerFunc returns the request handler function for the ecsacs . IAMRoleCredentialsMessage


func (refreshHandler *refreshCredentialsHandler) ackMessage(ack *ecsacs.IAMRoleCredentialsAckRequest) {
	err := refreshHandler.acsClient.MakeRequest(ack)
	if err != nil {
		seelog.Warnf("Error 'ack'ing request with messageID: %s, error: %v", aws.StringValue(ack.MessageId), err)
	}
	seelog.Debugf("Acking credentials message: %s", ack.String())
}

func ( refreshHandler * refreshCredentialsHandler ) ackMessage ( ack * ecsacs . IAMRoleCredentialsAckRequest ) { err := refreshHandler . acsClient . MakeRequest ( ack ) if err != nil { seelog . Warnf ( " " , aws . StringValue ( ack . MessageId ) , err ) } seelog . Debugf ( " " , ack . String ( ) ) }

// ackMessageId sends an IAMRoleCredentialsAckRequest to the backend


func (refreshHandler *refreshCredentialsHandler) handleMessages() {
	for {
		select {
		case message := <-refreshHandler.messageBuffer:
			refreshHandler.handleSingleMessage(message)
		case <-refreshHandler.ctx.Done():
			return
		}
	}
}

func ( refreshHandler * refreshCredentialsHandler ) handleMessages ( ) { for { select { case message := <- refreshHandler . messageBuffer : refreshHandler . handleSingleMessage ( message ) case <- refreshHandler . ctx . Done ( ) : return } } }

// handleMessages processes refresh credentials messages in the buffer in-order

handleMessages processes refresh credentials messages in the buffer in - order


func (refreshHandler *refreshCredentialsHandler) handleSingleMessage(message *ecsacs.IAMRoleCredentialsMessage) error {
	// Validate fields in the message
	err := validateIAMRoleCredentialsMessage(message)
	if err != nil {
		seelog.Errorf("Error validating credentials message: %v", err)
		return err
	}
	taskArn := aws.StringValue(message.TaskArn)
	messageId := aws.StringValue(message.MessageId)
	task, ok := refreshHandler.taskEngine.GetTaskByArn(taskArn)
	if !ok {
		seelog.Errorf("Task not found in the engine for the arn in credentials message, arn: %s, messageId: %s", taskArn, messageId)
		return fmt.Errorf("task not found in the engine for the arn in credentials message, arn: %s", taskArn)
	}

	roleType := aws.StringValue(message.RoleType)
	if !validRoleType(roleType) {
		seelog.Errorf("Unknown RoleType for task in credentials message, roleType: %s arn: %s, messageId: %s", roleType, taskArn, messageId)
	} else {
		err = refreshHandler.credentialsManager.SetTaskCredentials(
			&(credentials.TaskIAMRoleCredentials{
				ARN:                taskArn,
				IAMRoleCredentials: credentials.IAMRoleCredentialsFromACS(message.RoleCredentials, roleType),
			}))
		if err != nil {
			seelog.Errorf("Unable to update credentials for task, err: %v messageId: %s", err, messageId)
			return fmt.Errorf("unable to update credentials %v", err)
		}

		if roleType == credentials.ApplicationRoleType {
			task.SetCredentialsID(aws.StringValue(message.RoleCredentials.CredentialsId))
		}
		if roleType == credentials.ExecutionRoleType {
			task.SetExecutionRoleCredentialsID(aws.StringValue(message.RoleCredentials.CredentialsId))
		}
	}

	go func() {
		response := &ecsacs.IAMRoleCredentialsAckRequest{
			Expiration:    message.RoleCredentials.Expiration,
			MessageId:     message.MessageId,
			CredentialsId: message.RoleCredentials.CredentialsId,
		}
		refreshHandler.ackRequest <- response
	}()
	return nil
}

func
(
refreshHandler
*
refreshCredentialsHandler
)
handleSingleMessage
(
message
*
ecsacs
.
IAMRoleCredentialsMessage
)
error
{
// Validate fields in the message
err
:=
validateIAMRoleCredentialsMessage
(
message
)
if
err
!=
{
seelog
.
Errorf
(
"
"
,
err
)
return
err
}
taskArn
:=
aws
.
StringValue
(
message
.
TaskArn
)
messageId
:=
aws
.
StringValue
(
message
.
MessageId
)
task
,
ok
:=
refreshHandler
.
taskEngine
.
GetTaskByArn
(
taskArn
)
if
!
ok
{
seelog
.
Errorf
(
"
"
,
taskArn
,
messageId
)
return
fmt
.
Errorf
(
"
"
,
taskArn
)
}
roleType
:=
aws
.
StringValue
(
message
.
RoleType
)
if
!
validRoleType
(
roleType
)
{
seelog
.
Errorf
(
"
"
,
roleType
,
taskArn
,
messageId
)
}
else
{
err
=
refreshHandler
.
credentialsManager
.
SetTaskCredentials
(
&
(
credentials
.
TaskIAMRoleCredentials
{
ARN
:
taskArn
,
IAMRoleCredentials
:
credentials
.
IAMRoleCredentialsFromACS
(
message
.
RoleCredentials
,
roleType
)
,
}
)
)
if
err
!=
{
seelog
.
Errorf
(
"
"
,
err
,
messageId
)
return
fmt
.
Errorf
(
"
"
,
err
)
}
if
roleType
==
credentials
.
ApplicationRoleType
{
task
.
SetCredentialsID
(
aws
.
StringValue
(
message
.
RoleCredentials
.
CredentialsId
)
)
}
if
roleType
==
credentials
.
ExecutionRoleType
{
task
.
SetExecutionRoleCredentialsID
(
aws
.
StringValue
(
message
.
RoleCredentials
.
CredentialsId
)
)
}
}
go
func
(
)
{
response
:=
&
ecsacs
.
IAMRoleCredentialsAckRequest
{
Expiration
:
message
.
RoleCredentials
.
Expiration
,
MessageId
:
message
.
MessageId
,
CredentialsId
:
message
.
RoleCredentials
.
CredentialsId
,
}
refreshHandler
.
ackRequest
<-
response
}
(
)
return
}

// handleSingleMessage processes a single refresh credentials message.

handleSingleMessage processes a single refresh credentials message .


func validRoleType(roleType string) bool {
	switch roleType {
	case credentials.ApplicationRoleType:
		return true
	case credentials.ExecutionRoleType:
		return true
	default:
		return false
	}
}

func validRoleType ( roleType string ) bool { switch roleType { case credentials . ApplicationRoleType : return true case credentials . ExecutionRoleType : return true default : return false } }

// validRoleType returns false if the RoleType in the acs refresh payload is not
// one of the expected types. TaskApplication, TaskExecution

validRoleType returns false if the RoleType in the acs refresh payload is not one of the expected types . TaskApplication TaskExecution


func newPayloadRequestHandler(
	ctx context.Context,
	taskEngine engine.TaskEngine,
	ecsClient api.ECSClient,
	cluster string,
	containerInstanceArn string,
	acsClient wsclient.ClientServer,
	saver statemanager.Saver,
	refreshHandler refreshCredentialsHandler,
	credentialsManager credentials.Manager,
	taskHandler *eventhandler.TaskHandler) payloadRequestHandler {
	// Create a cancelable context from the parent context
	derivedContext, cancel := context.WithCancel(ctx)
	return payloadRequestHandler{
		messageBuffer:        make(chan *ecsacs.PayloadMessage, payloadMessageBufferSize),
		ackRequest:           make(chan string, payloadMessageBufferSize),
		taskEngine:           taskEngine,
		ecsClient:            ecsClient,
		saver:                saver,
		taskHandler:          taskHandler,
		ctx:                  derivedContext,
		cancel:               cancel,
		cluster:              cluster,
		containerInstanceArn: containerInstanceArn,
		acsClient:            acsClient,
		refreshHandler:       refreshHandler,
		credentialsManager:   credentialsManager,
	}
}

// newPayloadRequestHandler returns a new payloadRequestHandler object


func (payloadHandler *payloadRequestHandler) handlerFunc() func(payload *ecsacs.PayloadMessage) {
	// return a function that just enqueues PayloadMessages into the message buffer
	return func(payload *ecsacs.PayloadMessage) {
		payloadHandler.messageBuffer <- payload
	}
}

// handlerFunc returns the request handler function for the ecsacs.PayloadMessage type

handlerFunc returns the request handler function for the ecsacs . PayloadMessage type


func (payloadHandler *payloadRequestHandler) ackMessageId(messageID string) {
	seelog.Debugf("Acking payload message id: %s", messageID)
	err := payloadHandler.acsClient.MakeRequest(&ecsacs.AckRequest{
		Cluster:           aws.String(payloadHandler.cluster),
		ContainerInstance: aws.String(payloadHandler.containerInstanceArn),
		MessageId:         aws.String(messageID),
	})
	if err != nil {
		seelog.Warnf("Error 'ack'ing request with messageID: %s, error: %v", messageID, err)
	}
}

func ( payloadHandler * payloadRequestHandler ) ackMessageId ( messageID string ) { seelog . Debugf ( " " , messageID ) err := payloadHandler . acsClient . MakeRequest ( & ecsacs . AckRequest { Cluster : aws . String ( payloadHandler . cluster ) , ContainerInstance : aws . String ( payloadHandler . containerInstanceArn ) , MessageId : aws . String ( messageID ) , } ) if err != nil { seelog . Warnf ( " " , messageID , err ) } }

// ackMessageId sends an AckRequest for a message id


func (payloadHandler *payloadRequestHandler) handleMessages() {
	for {
		select {
		case payload := <-payloadHandler.messageBuffer:
			payloadHandler.handleSingleMessage(payload)
		case <-payloadHandler.ctx.Done():
			return
		}
	}
}

func ( payloadHandler * payloadRequestHandler ) handleMessages ( ) { for { select { case payload := <- payloadHandler . messageBuffer : payloadHandler . handleSingleMessage ( payload ) case <- payloadHandler . ctx . Done ( ) : return } } }

// handleMessages processes payload messages in the payload message buffer in-order

handleMessages processes payload messages in the payload message buffer in - order


func (payloadHandler *payloadRequestHandler) handleSingleMessage(payload *ecsacs.PayloadMessage) error {
	if aws.StringValue(payload.MessageId) == "" {
		seelog.Criticalf("Received a payload with no message id")
		return fmt.Errorf("received a payload with no message id")
	}
	seelog.Debugf("Received payload message, message id: %s", aws.StringValue(payload.MessageId))
	credentialsAcks, allTasksHandled := payloadHandler.addPayloadTasks(payload)
	// save the state of tasks we know about after passing them to the task engine
	err := payloadHandler.saver.Save()
	if err != nil {
		seelog.Errorf("Error saving state for payload message! err: %v, messageId: %s", err, aws.StringValue(payload.MessageId))
		// Don't ack; maybe we can save it in the future.
		return fmt.Errorf("error saving state for payload message, with messageId: %s", aws.StringValue(payload.MessageId))
	}
	if !allTasksHandled {
		return fmt.Errorf("did not handle all tasks")
	}

	go func() {
		// Throw the ack in async; it doesn't really matter all that much and this is blocking handling more tasks.
		for _, credentialsAck := range credentialsAcks {
			payloadHandler.refreshHandler.ackMessage(credentialsAck)
		}
		payloadHandler.ackRequest <- *payload.MessageId
	}()

	return nil
}

// handleSingleMessage processes a single payload message. It adds tasks in the message to the task engine
// An error is returned if the message was not handled correctly. The error is being used only for testing
// today. In the future, it could be used for doing more interesting things.

handleSingleMessage processes a single payload message . It adds tasks in the message to the task engine An error is returned if the message was not handled correctly . The error is being used only for testing today . In the future it could be used for doing more interesting things .


func (payloadHandler *payloadRequestHandler) addPayloadTasks(payload *ecsacs.PayloadMessage) ([]*ecsacs.IAMRoleCredentialsAckRequest, bool) {
	// verify that we were able to work with all tasks in this payload so we know whether to ack the whole thing or not
	allTasksOK := true

	validTasks := make([]*apitask.Task, 0, len(payload.Tasks))
	for _, task := range payload.Tasks {
		if task == nil {
			seelog.Criticalf("Received nil task for messageId: %s", aws.StringValue(payload.MessageId))
			allTasksOK = false
			continue
		}
		apiTask, err := apitask.TaskFromACS(task, payload)
		if err != nil {
			payloadHandler.handleUnrecognizedTask(task, err, payload)
			allTasksOK = false
			continue
		}
		if task.RoleCredentials != nil {
			// The payload from ACS for the task has credentials for the
			// task. Add those to the credentials manager and set the
			// credentials id for the task as well
			taskIAMRoleCredentials := credentials.IAMRoleCredentialsFromACS(task.RoleCredentials, credentials.ApplicationRoleType)
			err = payloadHandler.credentialsManager.SetTaskCredentials(
				&(credentials.TaskIAMRoleCredentials{
					ARN:                aws.StringValue(task.Arn),
					IAMRoleCredentials: taskIAMRoleCredentials,
				}))
			if err != nil {
				payloadHandler.handleUnrecognizedTask(task, err, payload)
				allTasksOK = false
				continue
			}
			apiTask.SetCredentialsID(taskIAMRoleCredentials.CredentialsID)
		}

		// Adding the eni information to the task struct
		if len(task.ElasticNetworkInterfaces) != 0 {
			eni, err := apieni.ENIFromACS(task.ElasticNetworkInterfaces)
			if err != nil {
				payloadHandler.handleUnrecognizedTask(task, err, payload)
				allTasksOK = false
				continue
			}

			apiTask.SetTaskENI(eni)
		}
		// Add the app mesh information to task struct
		if task.ProxyConfiguration != nil {
			appmesh, err := apiappmesh.AppMeshFromACS(task.ProxyConfiguration)
			if err != nil {
				payloadHandler.handleUnrecognizedTask(task, err, payload)
				allTasksOK = false
				continue
			}
			apiTask.SetAppMesh(appmesh)
		}
		if task.ExecutionRoleCredentials != nil {
			// The payload message contains execution credentials for the task.
			// Add the credentials to the credentials manager and set the
			// task executionCredentials id.
			taskExecutionIAMRoleCredentials := credentials.IAMRoleCredentialsFromACS(task.ExecutionRoleCredentials, credentials.ExecutionRoleType)
			err = payloadHandler.credentialsManager.SetTaskCredentials(
				&(credentials.TaskIAMRoleCredentials{
					ARN:                aws.StringValue(task.Arn),
					IAMRoleCredentials: taskExecutionIAMRoleCredentials,
				}))
			if err != nil {
				payloadHandler.handleUnrecognizedTask(task, err, payload)
				allTasksOK = false
				continue
			}
			apiTask.SetExecutionRoleCredentialsID(taskExecutionIAMRoleCredentials.CredentialsID)
		}

		validTasks = append(validTasks, apiTask)
	}
	// Add 'stop' transitions first to allow seqnum ordering to work out
	// Because a 'start' sequence number should only be proceeded if all 'stop's
	// of the same sequence number have completed, the 'start' events need to be
	// added after the 'stop' events are there to block them.
	stoppedTasksCredentialsAcks, stoppedTasksAddedOK := payloadHandler.addTasks(payload, validTasks, isTaskStatusNotStopped)
	newTasksCredentialsAcks, newTasksAddedOK := payloadHandler.addTasks(payload, validTasks, isTaskStatusStopped)
	if !stoppedTasksAddedOK || !newTasksAddedOK {
		allTasksOK = false
	}

	// Construct a slice with credentials acks from all tasks
	var credentialsAcks []*ecsacs.IAMRoleCredentialsAckRequest
	credentialsAcks = append(stoppedTasksCredentialsAcks, newTasksCredentialsAcks...)
	return credentialsAcks, allTasksOK
}

// addPayloadTasks does validation on each task and, for all valid ones, adds
// it to the task engine. It returns a bool indicating if it could add every
// task to the taskEngine and a slice of credential ack requests

addPayloadTasks does validation on each task and for all valid ones adds it to the task engine . It returns a bool indicating if it could add every task to the taskEngine and a slice of credential ack requests


func (payloadHandler *payloadRequestHandler) addTasks(payload *ecsacs.PayloadMessage, tasks []*apitask.Task, skipAddTask skipAddTaskComparatorFunc) ([]*ecsacs.IAMRoleCredentialsAckRequest, bool) {
	allTasksOK := true
	var credentialsAcks []*ecsacs.IAMRoleCredentialsAckRequest
	for _, task := range tasks {
		if skipAddTask(task.GetDesiredStatus()) {
			continue
		}
		payloadHandler.taskEngine.AddTask(task)

		ackCredentials := func(id string, description string) {
			ack, err := payloadHandler.ackCredentials(payload.MessageId, id)
			if err != nil {
				allTasksOK = false
				seelog.Errorf("Failed to acknowledge %s credentials for task: %s, err: %v", description, task.String(), err)
				return
			}
			credentialsAcks = append(credentialsAcks, ack)
		}

		// Generate an ack request for the credentials in the task, if the
		// task is associated with an IAM role or the execution role
		taskCredentialsID := task.GetCredentialsID()
		if taskCredentialsID != "" {
			ackCredentials(taskCredentialsID, "task iam role")
		}

		taskExecutionCredentialsID := task.GetExecutionCredentialsID()
		if taskExecutionCredentialsID != "" {
			ackCredentials(taskExecutionCredentialsID, "task execution role")
		}
	}
	return credentialsAcks, allTasksOK
}

// addTasks adds the tasks to the task engine based on the skipAddTask condition
// This is used to add non-stopped tasks before adding stopped tasks

addTasks adds the tasks to the task engine based on the skipAddTask condition This is used to add non - stopped tasks before adding stopped tasks


func (payloadHandler *payloadRequestHandler) handleUnrecognizedTask(task *ecsacs.Task, err error, payload *ecsacs.PayloadMessage) {
	seelog.Warnf("Received unexpected acs message, messageID: %s, task: %v, err: %v",
		aws.StringValue(payload.MessageId), aws.StringValue(task.Arn), err)

	if aws.StringValue(task.Arn) == "" {
		seelog.Criticalf("Received task with no arn, messageId: %s", aws.StringValue(payload.MessageId))
		return
	}

	// Only need to stop the task; it brings down the containers too.
	taskEvent := api.TaskStateChange{
		TaskARN: *task.Arn,
		Status:  apitaskstatus.TaskStopped,
		Reason:  UnrecognizedTaskError{err}.Error(),
		// The real task cannot be extracted from payload message, so we send an empty task.
		// This is necessary because the task handler will not send an event whose
		// Task is nil.
		Task: &apitask.Task{},
	}

	payloadHandler.taskHandler.AddStateChangeEvent(taskEvent, payloadHandler.ecsClient)
}

// handleUnrecognizedTask handles unrecognized tasks by sending 'stopped' with
// a suitable reason to the backend

handleUnrecognizedTask handles unrecognized tasks by sending stopped with a suitable reason to the backend


func v2HandlersSetup(muxRouter *mux.Router,
	state dockerstate.TaskEngineState,
	ecsClient api.ECSClient,
	statsEngine stats.Engine,
	cluster string,
	credentialsManager credentials.Manager,
	auditLogger audit.AuditLogger,
	availabilityZone string,
	containerInstanceArn string) {
	muxRouter.HandleFunc(v2.CredentialsPath, v2.CredentialsHandler(credentialsManager, auditLogger))
	muxRouter.HandleFunc(v2.ContainerMetadataPath, v2.TaskContainerMetadataHandler(state, ecsClient, cluster, availabilityZone, containerInstanceArn, false))
	muxRouter.HandleFunc(v2.TaskMetadataPath, v2.TaskContainerMetadataHandler(state, ecsClient, cluster, availabilityZone, containerInstanceArn, false))
	muxRouter.HandleFunc(v2.TaskWithTagsMetadataPath, v2.TaskContainerMetadataHandler(state, ecsClient, cluster, availabilityZone, containerInstanceArn, true))
	muxRouter.HandleFunc(v2.TaskMetadataPathWithSlash, v2.TaskContainerMetadataHandler(state, ecsClient, cluster, availabilityZone, containerInstanceArn, false))
	muxRouter.HandleFunc(v2.TaskWithTagsMetadataPathWithSlash, v2.TaskContainerMetadataHandler(state, ecsClient, cluster, availabilityZone, containerInstanceArn, true))
	muxRouter.HandleFunc(v2.ContainerStatsPath, v2.TaskContainerStatsHandler(state, statsEngine))
	muxRouter.HandleFunc(v2.TaskStatsPath, v2.TaskContainerStatsHandler(state, statsEngine))
	muxRouter.HandleFunc(v2.TaskStatsPathWithSlash, v2.TaskContainerStatsHandler(state, statsEngine))
}

func v2HandlersSetup ( muxRouter * mux . Router , state dockerstate . TaskEngineState , ecsClient api . ECSClient , statsEngine stats . Engine , cluster string , credentialsManager credentials . Manager , auditLogger audit . AuditLogger , availabilityZone string , containerInstanceArn string ) { muxRouter . HandleFunc ( v2 . CredentialsPath , v2 . CredentialsHandler ( credentialsManager , auditLogger ) ) muxRouter . HandleFunc ( v2 . ContainerMetadataPath , v2 . TaskContainerMetadataHandler ( state , ecsClient , cluster , availabilityZone , containerInstanceArn , false ) ) muxRouter . HandleFunc ( v2 . TaskMetadataPath , v2 . TaskContainerMetadataHandler ( state , ecsClient , cluster , availabilityZone , containerInstanceArn , false ) ) muxRouter . HandleFunc ( v2 . TaskWithTagsMetadataPath , v2 . TaskContainerMetadataHandler ( state , ecsClient , cluster , availabilityZone , containerInstanceArn , true ) ) muxRouter . HandleFunc ( v2 . TaskMetadataPathWithSlash , v2 . TaskContainerMetadataHandler ( state , ecsClient , cluster , availabilityZone , containerInstanceArn , false ) ) muxRouter . HandleFunc ( v2 . TaskWithTagsMetadataPathWithSlash , v2 . TaskContainerMetadataHandler ( state , ecsClient , cluster , availabilityZone , containerInstanceArn , true ) ) muxRouter . HandleFunc ( v2 . ContainerStatsPath , v2 . TaskContainerStatsHandler ( state , statsEngine ) ) muxRouter . HandleFunc ( v2 . TaskStatsPath , v2 . TaskContainerStatsHandler ( state , statsEngine ) ) muxRouter . HandleFunc ( v2 . TaskStatsPathWithSlash , v2 . TaskContainerStatsHandler ( state , statsEngine ) ) }

// v2HandlersSetup adds all handlers in v2 package to the mux router.

v2HandlersSetup adds all handlers in v2 package to the mux router .


func v3HandlersSetup(muxRouter *mux.Router,
	state dockerstate.TaskEngineState,
	ecsClient api.ECSClient,
	statsEngine stats.Engine,
	cluster string,
	availabilityZone string,
	containerInstanceArn string) {
	muxRouter.HandleFunc(v3.ContainerMetadataPath, v3.ContainerMetadataHandler(state))
	muxRouter.HandleFunc(v3.TaskMetadataPath, v3.TaskMetadataHandler(state, ecsClient, cluster, availabilityZone, containerInstanceArn, false))
	muxRouter.HandleFunc(v3.TaskWithTagsMetadataPath, v3.TaskMetadataHandler(state, ecsClient, cluster, availabilityZone, containerInstanceArn, true))
	muxRouter.HandleFunc(v3.ContainerStatsPath, v3.ContainerStatsHandler(state, statsEngine))
	muxRouter.HandleFunc(v3.TaskStatsPath, v3.TaskStatsHandler(state, statsEngine))
	muxRouter.HandleFunc(v3.ContainerAssociationsPath, v3.ContainerAssociationsHandler(state))
	muxRouter.HandleFunc(v3.ContainerAssociationPathWithSlash, v3.ContainerAssociationHandler(state))
	muxRouter.HandleFunc(v3.ContainerAssociationPath, v3.ContainerAssociationHandler(state))
}

func v3HandlersSetup ( muxRouter * mux . Router , state dockerstate . TaskEngineState , ecsClient api . ECSClient , statsEngine stats . Engine , cluster string , availabilityZone string , containerInstanceArn string ) { muxRouter . HandleFunc ( v3 . ContainerMetadataPath , v3 . ContainerMetadataHandler ( state ) ) muxRouter . HandleFunc ( v3 . TaskMetadataPath , v3 . TaskMetadataHandler ( state , ecsClient , cluster , availabilityZone , containerInstanceArn , false ) ) muxRouter . HandleFunc ( v3 . TaskWithTagsMetadataPath , v3 . TaskMetadataHandler ( state , ecsClient , cluster , availabilityZone , containerInstanceArn , true ) ) muxRouter . HandleFunc ( v3 . ContainerStatsPath , v3 . ContainerStatsHandler ( state , statsEngine ) ) muxRouter . HandleFunc ( v3 . TaskStatsPath , v3 . TaskStatsHandler ( state , statsEngine ) ) muxRouter . HandleFunc ( v3 . ContainerAssociationsPath , v3 . ContainerAssociationsHandler ( state ) ) muxRouter . HandleFunc ( v3 . ContainerAssociationPathWithSlash , v3 . ContainerAssociationHandler ( state ) ) muxRouter . HandleFunc ( v3 . ContainerAssociationPath , v3 . ContainerAssociationHandler ( state ) ) }

// v3HandlersSetup adds all handlers in v3 package to the mux router.

v3HandlersSetup adds all handlers in v3 package to the mux router .


func NewMockProvider(ctrl *gomock.Controller) *MockProvider {
	mock := &MockProvider{ctrl: ctrl}
	mock.recorder = &MockProviderMockRecorder{mock}
	return mock
}

func NewMockProvider ( ctrl * gomock . Controller ) * MockProvider { mock := & MockProvider { ctrl : ctrl } mock . recorder = & MockProviderMockRecorder { mock } return mock }

// NewMockProvider creates a new mock instance


func (m *MockProvider) IsExpired() bool {
	ret := m.ctrl.Call(m, "IsExpired")
	ret0, _ := ret[0].(bool)
	return ret0
}

func ( m * MockProvider ) IsExpired ( ) bool { ret := m . ctrl . Call ( m , " " ) ret0 , _ := ret [ 0 ] . ( bool ) return ret0 }


func (mr *MockProviderMockRecorder) IsExpired() *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsExpired", reflect.TypeOf((*MockProvider)(nil).IsExpired))
}

func ( mr * MockProviderMockRecorder ) IsExpired ( ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockProvider ) ( nil ) . IsExpired ) ) }

// IsExpired indicates an expected call of IsExpired


func (m *MockProvider) Retrieve() (credentials.Value, error) {
	ret := m.ctrl.Call(m, "Retrieve")
	ret0, _ := ret[0].(credentials.Value)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

func ( m * MockProvider ) Retrieve ( ) ( credentials . Value , error ) { ret := m . ctrl . Call ( m , " " ) ret0 , _ := ret [ 0 ] . ( credentials . Value ) ret1 , _ := ret [ 1 ] . ( error ) return ret0 , ret1 }


func NewMockContainerMetadataResolver(ctrl *gomock.Controller) *MockContainerMetadataResolver {
	mock := &MockContainerMetadataResolver{ctrl: ctrl}
	mock.recorder = &MockContainerMetadataResolverMockRecorder{mock}
	return mock
}

// NewMockContainerMetadataResolver creates a new mock instance


func (m *MockContainerMetadataResolver) ResolveContainer(arg0 string) (*container.DockerContainer, error) {
	ret := m.ctrl.Call(m, "ResolveContainer", arg0)
	ret0, _ := ret[0].(*container.DockerContainer)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

func ( m * MockContainerMetadataResolver ) ResolveContainer ( arg0 string ) ( * container . DockerContainer , error ) { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( * container . DockerContainer ) ret1 , _ := ret [ 1 ] . ( error ) return ret0 , ret1 }


func (mr *MockContainerMetadataResolverMockRecorder) ResolveContainer(arg0 interface{}) *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveContainer", reflect.TypeOf((*MockContainerMetadataResolver)(nil).ResolveContainer), arg0)
}

// ResolveContainer indicates an expected call of ResolveContainer


func (m *MockContainerMetadataResolver) ResolveTask(arg0 string) (*task.Task, error) {
	ret := m.ctrl.Call(m, "ResolveTask", arg0)
	ret0, _ := ret[0].(*task.Task)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

func ( m * MockContainerMetadataResolver ) ResolveTask ( arg0 string ) ( * task . Task , error ) { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( * task . Task ) ret1 , _ := ret [ 1 ] . ( error ) return ret0 , ret1 }


func NewTaskResponse(taskARN string,
	state dockerstate.TaskEngineState,
	ecsClient api.ECSClient,
	cluster string,
	az string,
	containerInstanceArn string,
	propagateTags bool) (*TaskResponse, error) {
	task, ok := state.TaskByArn(taskARN)
	if !ok {
		return nil, errors.Errorf("v2 task response: unable to find task '%s'", taskARN)
	}

	resp := &TaskResponse{
		Cluster:          cluster,
		TaskARN:          task.Arn,
		Family:           task.Family,
		Revision:         task.Version,
		DesiredStatus:    task.GetDesiredStatus().String(),
		KnownStatus:      task.GetKnownStatus().String(),
		AvailabilityZone: az,
	}

	taskCPU := task.CPU
	taskMemory := task.Memory
	if taskCPU != 0 || taskMemory != 0 {
		taskLimits := &LimitsResponse{}
		if taskCPU != 0 {
			taskLimits.CPU = &taskCPU
		}
		if taskMemory != 0 {
			taskLimits.Memory = &taskMemory
		}
		resp.Limits = taskLimits
	}

	if timestamp := task.GetPullStartedAt(); !timestamp.IsZero() {
		resp.PullStartedAt = aws.Time(timestamp.UTC())
	}
	if timestamp := task.GetPullStoppedAt(); !timestamp.IsZero() {
		resp.PullStoppedAt = aws.Time(timestamp.UTC())
	}
	if timestamp := task.GetExecutionStoppedAt(); !timestamp.IsZero() {
		resp.ExecutionStoppedAt = aws.Time(timestamp.UTC())
	}
	containerNameToDockerContainer, ok := state.ContainerMapByArn(task.Arn)
	if !ok {
		seelog.Warnf("V2 task response: unable to get container name mapping for task '%s'",
			task.Arn)
		return resp, nil
	}

	eni := task.GetTaskENI()
	for _, dockerContainer := range containerNameToDockerContainer {
		containerResponse := newContainerResponse(dockerContainer, eni, state)
		resp.Containers = append(resp.Containers, containerResponse)
	}

	if propagateTags {
		propagateTagsToMetadata(state, ecsClient, containerInstanceArn, taskARN, resp)
	}

	return resp, nil
}

func NewTaskResponse ( taskARN string , state dockerstate . TaskEngineState , ecsClient api . ECSClient , cluster string , az string , containerInstanceArn string , propagateTags bool ) ( * TaskResponse , error ) { task , ok := state . TaskByArn ( taskARN ) if ! ok { return nil , errors . Errorf ( " " , taskARN ) } resp := & TaskResponse { Cluster : cluster , TaskARN : task . Arn , Family : task . Family , Revision : task . Version , DesiredStatus : task . GetDesiredStatus ( ) . String ( ) , KnownStatus : task . GetKnownStatus ( ) . String ( ) , AvailabilityZone : az , } taskCPU := task . CPU taskMemory := task . Memory if taskCPU != 0 || taskMemory != 0 { taskLimits := & LimitsResponse { } if taskCPU != 0 { taskLimits . CPU = & taskCPU } if taskMemory != 0 { taskLimits . Memory = & taskMemory } resp . Limits = taskLimits } if timestamp := task . GetPullStartedAt ( ) ; ! timestamp . IsZero ( ) { resp . PullStartedAt = aws . Time ( timestamp . UTC ( ) ) } if timestamp := task . GetPullStoppedAt ( ) ; ! timestamp . IsZero ( ) { resp . PullStoppedAt = aws . Time ( timestamp . UTC ( ) ) } if timestamp := task . GetExecutionStoppedAt ( ) ; ! timestamp . IsZero ( ) { resp . ExecutionStoppedAt = aws . Time ( timestamp . UTC ( ) ) } containerNameToDockerContainer , ok := state . ContainerMapByArn ( task . Arn ) if ! ok { seelog . Warnf ( " " , task . Arn ) return resp , nil } eni := task . GetTaskENI ( ) for _ , dockerContainer := range containerNameToDockerContainer { containerResponse := newContainerResponse ( dockerContainer , eni , state ) resp . Containers = append ( resp . Containers , containerResponse ) } if propagateTags { propagateTagsToMetadata ( state , ecsClient , containerInstanceArn , taskARN , resp ) } return resp , nil }

// NewTaskResponse creates a new response object for the task


func NewContainerResponse(containerID string,
	state dockerstate.TaskEngineState) (*ContainerResponse, error) {
	dockerContainer, ok := state.ContainerByID(containerID)
	if !ok {
		return nil, errors.Errorf(
			"v2 container response: unable to find container '%s'", containerID)
	}
	task, ok := state.TaskByID(containerID)
	if !ok {
		return nil, errors.Errorf(
			"v2 container response: unable to find task for container '%s'", containerID)
	}

	resp := newContainerResponse(dockerContainer, task.GetTaskENI(), state)
	return &resp, nil
}

func NewContainerResponse ( containerID string , state dockerstate . TaskEngineState ) ( * ContainerResponse , error ) { dockerContainer , ok := state . ContainerByID ( containerID ) if ! ok { return nil , errors . Errorf ( " " , containerID ) } task , ok := state . TaskByID ( containerID ) if ! ok { return nil , errors . Errorf ( " " , containerID ) } resp := newContainerResponse ( dockerContainer , task . GetTaskENI ( ) , state ) return & resp , nil }

// NewContainerResponse creates a new container response based on container id


func New(arguments []string) (*Args, error) {
	flagset := flag.NewFlagSet("Amazon ECS Agent", flag.ContinueOnError)

	args := &Args{
		Version:              flagset.Bool(versionFlagName, false, versionUsage),
		LogLevel:             flagset.String(logLevelFlagName, "", logLevelUsage),
		AcceptInsecureCert:   flagset.Bool(acceptInsecureCertFlagName, false, acceptInsecureCertUsage),
		License:              flagset.Bool(licenseFlagName, false, licenseUsage),
		BlackholeEC2Metadata: flagset.Bool(blackholeEC2MetadataFlagName, false, blacholeEC2MetadataUsage),
		ECSAttributes:        flagset.Bool(ecsAttributesFlagName, false, ecsAttributesUsage),
		WindowsService:       flagset.Bool(windowsServiceFlagName, false, windowsServiceUsage),
	}

	err := flagset.Parse(arguments)
	if err != nil {
		return nil, err
	}

	return args, nil
}

func New ( arguments [ ] string ) ( * Args , error ) { flagset := flag . NewFlagSet ( " " , flag . ContinueOnError ) args := & Args { Version : flagset . Bool ( versionFlagName , false , versionUsage ) , LogLevel : flagset . String ( logLevelFlagName , " " , logLevelUsage ) , AcceptInsecureCert : flagset . Bool ( acceptInsecureCertFlagName , false , acceptInsecureCertUsage ) , License : flagset . Bool ( licenseFlagName , false , licenseUsage ) , BlackholeEC2Metadata : flagset . Bool ( blackholeEC2MetadataFlagName , false , blacholeEC2MetadataUsage ) , ECSAttributes : flagset . Bool ( ecsAttributesFlagName , false , ecsAttributesUsage ) , WindowsService : flagset . Bool ( windowsServiceFlagName , false , windowsServiceUsage ) , } err := flagset . Parse ( arguments ) if err != nil { return nil , err } return args , nil }

// New creates a new Args object from the argument list


func DefaultConfig() Config {
	programData := utils.DefaultIfBlank(os.Getenv("ProgramData"), `C:\ProgramData`)
	ecsRoot := filepath.Join(programData, "Amazon", "ECS")
	dataDir := filepath.Join(ecsRoot, "data")
	platformVariables := PlatformVariables{
		CPUUnbounded: false,
	}
	return Config{
		DockerEndpoint: "npipe:////./pipe/docker_engine",
		ReservedPorts: []uint16{
			DockerReservedPort,
			DockerReservedSSLPort,
			AgentIntrospectionPort,
			AgentCredentialsPort,
			rdpPort,
			rpcPort,
			smbPort,
			winRMPortHTTP,
			winRMPortHTTPS,
			dnsPort,
			netBIOSPort,
		},
		ReservedPortsUDP: []uint16{},
		DataDir:          dataDir,
		// DataDirOnHost is identical to DataDir for Windows because we do not
		// run as a container
		DataDirOnHost:                       dataDir,
		ReservedMemory:                      0,
		AvailableLoggingDrivers:             []dockerclient.LoggingDriver{dockerclient.JSONFileDriver, dockerclient.NoneDriver, dockerclient.AWSLogsDriver},
		TaskCleanupWaitDuration:             DefaultTaskCleanupWaitDuration,
		DockerStopTimeout:                   defaultDockerStopTimeout,
		ContainerStartTimeout:               defaultContainerStartTimeout,
		ImagePullInactivityTimeout:          defaultImagePullInactivityTimeout,
		CredentialsAuditLogFile:             filepath.Join(ecsRoot, defaultCredentialsAuditLogFile),
		CredentialsAuditLogDisabled:         false,
		ImageCleanupDisabled:                false,
		MinimumImageDeletionAge:             DefaultImageDeletionAge,
		ImageCleanupInterval:                DefaultImageCleanupTimeInterval,
		NumImagesToDeletePerCycle:           DefaultNumImagesToDeletePerCycle,
		NumNonECSContainersToDeletePerCycle: DefaultNumNonECSContainersToDeletePerCycle,
		ContainerMetadataEnabled:            false,
		TaskCPUMemLimit:                     ExplicitlyDisabled,
		PlatformVariables:                   platformVariables,
		TaskMetadataSteadyStateRate:         DefaultTaskMetadataSteadyStateRate,
		TaskMetadataBurstRate:               DefaultTaskMetadataBurstRate,
		SharedVolumeMatchFullConfig:         false, //only requiring shared volumes to match on name, which is default docker behavior
		PollMetrics:                         false,
		PollingMetricsWaitDuration:          DefaultPollingMetricsWaitDuration,
	}
}

// DefaultConfig returns the default configuration for Windows


func New(timeout time.Duration, insecureSkipVerify bool) *http.Client {
	// Transport is the transport requests will be made over
	// Note, these defaults are taken from the golang http library. We do not
	// explicitly do not use theirs to avoid changing their behavior.
	transport := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   defaultDialTimeout,
			KeepAlive: defaultDialKeepalive,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
	}

	transport.TLSClientConfig = &tls.Config{}
	cipher.WithSupportedCipherSuites(transport.TLSClientConfig)
	transport.TLSClientConfig.InsecureSkipVerify = insecureSkipVerify

	client := &http.Client{
		Transport: &ecsRoundTripper{insecureSkipVerify, transport},
		Timeout:   timeout,
	}

	return client
}

// New returns an ECS httpClient with a roundtrip timeout of the given duration


func RemoveFromTaskArray(arr []*Task, ndx int) []*Task {
	if ndx < 0 || ndx >= len(arr) {
		return arr
	}
	return append(arr[0:ndx], arr[ndx+1:]...)
}

func RemoveFromTaskArray ( arr [ ] * Task , ndx int ) [ ] * Task { if ndx < 0 || ndx >= len ( arr ) { return arr } return append ( arr [ 0 : ndx ] , arr [ ndx + 1 : ] ... ) }

// RemoveFromTaskArray removes the element at ndx from an array of task
// pointers, arr. If the ndx is out of bounds, it returns arr unchanged.

RemoveFromTaskArray removes the element at ndx from an array of task pointers arr . If the ndx is out of bounds it returns arr unchanged .


func (task *Task) adjustForPlatform(cfg *config.Config) {
	task.downcaseAllVolumePaths()
	platformFields := PlatformFields{
		CpuUnbounded: cfg.PlatformVariables.CPUUnbounded,
	}
	task.PlatformFields = platformFields
}

func ( task * Task ) adjustForPlatform ( cfg * config . Config ) { task . downcaseAllVolumePaths ( ) platformFields := PlatformFields { CpuUnbounded : cfg . PlatformVariables . CPUUnbounded , } task . PlatformFields = platformFields }

// adjustForPlatform makes Windows-specific changes to the task after unmarshal

adjustForPlatform makes Windows - specific changes to the task after unmarshal


func (task *Task) platformHostConfigOverride(hostConfig *dockercontainer.HostConfig) error {
	// Convert the CPUShares to CPUPercent
	hostConfig.CPUPercent = hostConfig.CPUShares * percentageFactor / int64(cpuShareScaleFactor)
	if hostConfig.CPUPercent == 0 && hostConfig.CPUShares != 0 {
		// if CPU percent is too low, we set it to the minimum(linux and some windows tasks).
		// if the CPU is explicitly set to zero or not set at all, and CPU unbounded
		// tasks are allowed for windows, let CPU percent be zero.
		// this is a workaround to allow CPU unbounded tasks(https://github.com/aws/amazon-ecs-agent/issues/1127)
		hostConfig.CPUPercent = minimumCPUPercent
	}
	hostConfig.CPUShares = 0
	return nil
}

// platformHostConfigOverride provides an entry point to set up default HostConfig options to be
// passed to Docker API.

platformHostConfigOverride provides an entry point to set up default HostConfig options to be passed to Docker API .


func (eni *ENIAttachmentStatus) String() string {
	for k, v := range eniAttachmentStatusMap {
		if v == *eni {
			return k
		}
	}
	return "NONE"
}

func ( eni * ENIAttachmentStatus ) String ( ) string { for k , v := range eniAttachmentStatusMap { if v == * eni { return k } } return " " }

// String return the string value of the eniattachment status


func _main() int {

	var typesOnly bool
	flag.BoolVar(&typesOnly, "typesOnly", false, "only generate types")
	flag.Parse()

	apiFile := "./api/api-2.json"
	var err error
	if typesOnly {
		err = genTypesOnlyAPI(apiFile)
	} else {
		err = genFull(apiFile)
	}
	if err != nil {
		fmt.Println(err)
		return 1
	}
	return 0
}

func _main ( ) int { var typesOnly bool flag . BoolVar ( & typesOnly , " " , false , " " ) flag . Parse ( ) apiFile := " " var err error if typesOnly { err = genTypesOnlyAPI ( apiFile ) } else { err = genFull ( apiFile ) } if err != nil { fmt . Println ( err ) return 1 } return 0 }

// return-based exit code so the 'defer' works

return - based exit code so the defer works


func (retrier *oneDayRetrier) RetryRules(r *request.Request) time.Duration {
	// This logic is the same as the default retrier, but duplicated here such
	// that upstream changes do not invalidate the math done above.
	if r.RetryCount <= submitStateChangeInitialRetries {
		delay := int(math.Pow(2, float64(r.RetryCount))) * (rand.Intn(30) + 30)
		return time.Duration(delay) * time.Millisecond
	}
	return 5 * time.Minute
}

// RetryRules returns the correct time to delay between retries for this
// instance of a retry. For the first 14 requests, it follows an exponential
// backoff between 30ms and 1 minute.
// See the const comments for math on how this gets us to around 24 hours
// total.

RetryRules returns the correct time to delay between retries for this instance of a retry . For the first 14 requests it follows an exponential backoff between 30ms and 1 minute . See the const comments for math on how this gets us to around 24 hours total .


func NewMockTime(ctrl *gomock.Controller) *MockTime {
	mock := &MockTime{ctrl: ctrl}
	mock.recorder = &MockTimeMockRecorder{mock}
	return mock
}

func NewMockTime ( ctrl * gomock . Controller ) * MockTime { mock := & MockTime { ctrl : ctrl } mock . recorder = & MockTimeMockRecorder { mock } return mock }

// NewMockTime creates a new mock instance


func (m *MockTime) After(arg0 time.Duration) <-chan time.Time {
	ret := m.ctrl.Call(m, "After", arg0)
	ret0, _ := ret[0].(<-chan time.Time)
	return ret0
}

func ( m * MockTime ) After ( arg0 time . Duration ) <- chan time . Time { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( <- chan time . Time ) return ret0 }


func (m *MockTime) AfterFunc(arg0 time.Duration, arg1 func()) ttime.Timer {
	ret := m.ctrl.Call(m, "AfterFunc", arg0, arg1)
	ret0, _ := ret[0].(ttime.Timer)
	return ret0
}

func ( m * MockTime ) AfterFunc ( arg0 time . Duration , arg1 func ( ) ) ttime . Timer { ret := m . ctrl . Call ( m , " " , arg0 , arg1 ) ret0 , _ := ret [ 0 ] . ( ttime . Timer ) return ret0 }


func (m *MockTime) Now() time.Time {
	ret := m.ctrl.Call(m, "Now")
	ret0, _ := ret[0].(time.Time)
	return ret0
}

func ( m * MockTime ) Now ( ) time . Time { ret := m . ctrl . Call ( m , " " ) ret0 , _ := ret [ 0 ] . ( time . Time ) return ret0 }


func (mr *MockTimeMockRecorder) Now() *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Now", reflect.TypeOf((*MockTime)(nil).Now))
}

func ( mr * MockTimeMockRecorder ) Now ( ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockTime ) ( nil ) . Now ) ) }

// Now indicates an expected call of Now


func (m *MockTime) Sleep(arg0 time.Duration) {
	m.ctrl.Call(m, "Sleep", arg0)
}

func ( m * MockTime ) Sleep ( arg0 time . Duration ) { m . ctrl . Call ( m , " " , arg0 ) }


func (mr *MockTimeMockRecorder) Sleep(arg0 interface{}) *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sleep", reflect.TypeOf((*MockTime)(nil).Sleep), arg0)
}

func ( mr * MockTimeMockRecorder ) Sleep ( arg0 interface { } ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockTime ) ( nil ) . Sleep ) , arg0 ) }

// Sleep indicates an expected call of Sleep


func NewMockTimer(ctrl *gomock.Controller) *MockTimer {
	mock := &MockTimer{ctrl: ctrl}
	mock.recorder = &MockTimerMockRecorder{mock}
	return mock
}

func NewMockTimer ( ctrl * gomock . Controller ) * MockTimer { mock := & MockTimer { ctrl : ctrl } mock . recorder = & MockTimerMockRecorder { mock } return mock }

// NewMockTimer creates a new mock instance


func (m *MockTimer) Reset(arg0 time.Duration) bool {
	ret := m.ctrl.Call(m, "Reset", arg0)
	ret0, _ := ret[0].(bool)
	return ret0
}

func ( m * MockTimer ) Reset ( arg0 time . Duration ) bool { ret := m . ctrl . Call ( m , " " , arg0 ) ret0 , _ := ret [ 0 ] . ( bool ) return ret0 }


func (mr *MockTimerMockRecorder) Reset(arg0 interface{}) *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reset", reflect.TypeOf((*MockTimer)(nil).Reset), arg0)
}

func ( mr * MockTimerMockRecorder ) Reset ( arg0 interface { } ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockTimer ) ( nil ) . Reset ) , arg0 ) }

// Reset indicates an expected call of Reset


func (m *MockTimer) Stop() bool {
	ret := m.ctrl.Call(m, "Stop")
	ret0, _ := ret[0].(bool)
	return ret0
}

func ( m * MockTimer ) Stop ( ) bool { ret := m . ctrl . Call ( m , " " ) ret0 , _ := ret [ 0 ] . ( bool ) return ret0 }


func (mr *MockTimerMockRecorder) Stop() *gomock.Call {
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockTimer)(nil).Stop))
}

func ( mr * MockTimerMockRecorder ) Stop ( ) * gomock . Call { return mr . mock . ctrl . RecordCallWithMethodType ( mr . mock , " " , reflect . TypeOf ( ( * MockTimer ) ( nil ) . Stop ) ) }

// Stop indicates an expected call of Stop


func AddAgentUpdateHandlers(cs wsclient.ClientServer, cfg *config.Config, saver statemanager.Saver, taskEngine engine.TaskEngine) {
	singleUpdater := &updater{
		acs:        cs,
		config:     cfg,
		fs:         os.Default,
		httpclient: httpclient.New(updateDownloadTimeout, false),
	}
	cs.AddRequestHandler(singleUpdater.stageUpdateHandler())
	cs.AddRequestHandler(singleUpdater.performUpdateHandler(saver, taskEngine))
}

func AddAgentUpdateHandlers ( cs wsclient . ClientServer , cfg * config . Config , saver statemanager . Saver , taskEngine engine . TaskEngine ) { singleUpdater := & updater { acs : cs , config : cfg , fs : os . Default , httpclient : httpclient . New ( updateDownloadTimeout , false ) , } cs . AddRequestHandler ( singleUpdater . stageUpdateHandler ( ) ) cs . AddRequestHandler ( singleUpdater . performUpdateHandler ( saver , taskEngine ) ) }

// AddAgentUpdateHandlers adds the needed update handlers to perform agent
// updates

AddAgentUpdateHandlers adds the needed update handlers to perform agent updates


func (dg *dockerGoClient) sdkDockerClient() (sdkclient.Client, error) {
	if dg.version == "" {
		return dg.sdkClientFactory.GetDefaultClient()
	}
	return dg.sdkClientFactory.GetClient(dg.version)
}

func ( dg * dockerGoClient ) sdkDockerClient ( ) ( sdkclient . Client , error ) { if dg . version == " " { return dg . sdkClientFactory . GetDefaultClient ( ) } return dg . sdkClientFactory . GetClient ( dg . version ) }


func DockerStateToState(state *types.ContainerState) apicontainerstatus.ContainerStatus {
	if state.Running {
		return apicontainerstatus.ContainerRunning
	}

	if state.Dead {
		return apicontainerstatus.ContainerStopped
	}

	// StartAt field in ContainerState is a string and need to convert to compare to zero time instant
	startTime, _ := time.Parse(time.RFC3339, state.StartedAt)
	if startTime.IsZero() && state.Error == "" {
		return apicontainerstatus.ContainerCreated
	}

	return apicontainerstatus.ContainerStopped
}

// DockerStateToState converts the container status from docker to status recognized by the agent


func MetadataFromContainer(dockerContainer *types.ContainerJSON) DockerContainerMetadata {
	var bindings []apicontainer.PortBinding
	var err apierrors.NamedError
	if dockerContainer.NetworkSettings != nil {
		// Convert port bindings into the format our container expects
		bindings, err = apicontainer.PortBindingFromDockerPortBinding(dockerContainer.NetworkSettings.Ports)
		if err != nil {
			seelog.Criticalf("DockerGoClient: Docker had network bindings we couldn't understand: %v", err)
			return DockerContainerMetadata{Error: apierrors.NamedError(err)}
		}
	}

	createdTime, _ := time.Parse(time.RFC3339, dockerContainer.Created)
	startedTime := time.Time{}
	finishedTime := time.Time{}
	// Need to check for nil to make sure we do not try to access fields of nil pointer
	if dockerContainer.State != nil {
		startedTime, _ = time.Parse(time.RFC3339, dockerContainer.State.StartedAt)
		finishedTime, _ = time.Parse(time.RFC3339, dockerContainer.State.FinishedAt)
	}

	metadata := DockerContainerMetadata{
		DockerID:     dockerContainer.ID,
		PortBindings: bindings,
		Volumes:      dockerContainer.Mounts,
		CreatedAt:    createdTime,
		StartedAt:    startedTime,
		FinishedAt:   finishedTime,
	}

	if dockerContainer.NetworkSettings != nil {
		metadata.NetworkSettings = dockerContainer.NetworkSettings
	}

	if dockerContainer.HostConfig != nil {
		metadata.NetworkMode = string(dockerContainer.HostConfig.NetworkMode)
	}

	if dockerContainer.Config != nil {
		metadata.Labels = dockerContainer.Config.Labels
	}

	if dockerContainer.State == nil {
		return metadata
	}
	if !dockerContainer.State.Running && !finishedTime.IsZero() {
		// Only record an exitcode if it has exited
		metadata.ExitCode = &dockerContainer.State.ExitCode
	}
	if dockerContainer.State.Error != "" {
		metadata.Error = NewDockerStateError(dockerContainer.State.Error)
	}
	if dockerContainer.State.OOMKilled {
		metadata.Error = OutOfMemoryError{}
	}
	// Health field in Docker SDK is a pointer, need to check before not nil before dereference.
	if dockerContainer.State.Health == nil || dockerContainer.State.Health.Status == "" || dockerContainer.State.Health.Status == healthCheckStarting {
		return metadata
	}
	// Record the health check information if exists
	metadata.Health = getMetadataHealthCheck(dockerContainer)
	return metadata
}

// MetadataFromContainer translates dockerContainer into DockerContainerMetadata


func (dg *dockerGoClient) ContainerEvents(ctx context.Context) (<-chan DockerContainerChangeEvent, error) {
	client, err := dg.sdkDockerClient()
	if err != nil {
		return nil, err
	}

	events := make(chan *events.Message)
	buffer := NewInfiniteBuffer()

	derivedCtx, cancel := context.WithCancel(ctx)
	dockerEvents, eventErr := client.Events(derivedCtx, types.EventsOptions{})

	// Cache the event from docker client. Channel closes when an error is passed to eventErr.
	go buffer.StartListening(derivedCtx, dockerEvents)
	// Receive errors from channels. If error thrown is not EOF, log and reopen channel.
	// TODO: move the error check into StartListening() to keep event streaming and error handling in one place.
	go func() {
		for {
			select {
			case err := <-eventErr:
				if err == io.EOF || err == io.ErrUnexpectedEOF {
					seelog.Info("DockerGoClient: All events have been received")
					cancel()
					return
				} else {
					// If an error is returned, we need to reopen channel to continue listening
					seelog.Errorf("DockerGoClient: error while listening to Docker Events : %v", err)
					nextCtx, nextCancel := context.WithCancel(ctx)
					dockerEvents, eventErr = client.Events(nextCtx, types.EventsOptions{})
					// Cache the event from docker client.
					go buffer.StartListening(nextCtx, dockerEvents)
					// Close previous stream after starting to listen on new one
					cancel()
					// Reassign cancel variable next Cancel function to setup next iteration of loop.
					cancel = nextCancel
				}
			case <-ctx.Done():
				return
			}
		}
	}()

	// Read the buffered events and send to task engine
	go buffer.Consume(events)
	changedContainers := make(chan DockerContainerChangeEvent)
	go dg.handleContainerEvents(ctx, events, changedContainers)

	return changedContainers, nil
}

// Listen to the docker event stream for container changes and pass them up


func (dg *dockerGoClient) ListContainers(ctx context.Context, all bool, timeout time.Duration) ListContainersResponse {
	ctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()

	// Buffered channel so in the case of timeout it takes one write, never gets
	// read, and can still be GC'd
	response := make(chan ListContainersResponse, 1)
	go func() { response <- dg.listContainers(ctx, all) }()
	select {
	case resp := <-response:
		return resp
	case <-ctx.Done():
		// Context has either expired or canceled. If it has timed out,
		// send back the DockerTimeoutError
		err := ctx.Err()
		if err == context.DeadlineExceeded {
			return ListContainersResponse{Error: &DockerTimeoutError{timeout, "listing"}}
		}
		return ListContainersResponse{Error: &CannotListContainersError{err}}
	}
}

// ListContainers returns a slice of container IDs.

ListContainers returns a slice of container IDs .


func (dg *dockerGoClient) ListPluginsWithFilters(ctx context.Context, enabled bool, capabilities []string, timeout time.Duration) ([]string, error) {
	// Create filter list
	filterList := filters.NewArgs(filters.Arg("enabled", strconv.FormatBool(enabled)))
	for _, capability := range capabilities {
		filterList.Add("capability", capability)
	}

	var filteredPluginNames []string
	response := dg.ListPlugins(ctx, timeout, filterList)

	if response.Error != nil {
		return nil, response.Error
	}
	// Create a list of the filtered plugin names
	for _, plugin := range response.Plugins {
		filteredPluginNames = append(filteredPluginNames, plugin.Name)
	}
	return filteredPluginNames, nil
}

// ListPluginsWithFilters takes in filter arguments and returns the string of filtered Plugin names


func (dg *dockerGoClient) APIVersion() (dockerclient.DockerVersion, error) {
	client, err := dg.sdkDockerClient()
	if err != nil {
		return "", err
	}
	return dg.sdkClientFactory.FindClientAPIVersion(client), nil
}

func ( dg * dockerGoClient ) APIVersion ( ) ( dockerclient . DockerVersion , error ) { client , err := dg . sdkDockerClient ( ) if err != nil { return " " , err } return dg . sdkClientFactory . FindClientAPIVersion ( client ) , nil }

// APIVersion returns the client api version


func (dg *dockerGoClient) LoadImage(ctx context.Context, inputStream io.Reader, timeout time.Duration) error {
	ctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()
	defer metrics.MetricsEngineGlobal.RecordDockerMetric("LOAD_IMAGE")()
	response := make(chan error, 1)
	go func() {
		response <- dg.loadImage(ctx, inputStream)
	}()
	select {
	case resp := <-response:
		return resp
	case <-ctx.Done():
		return &DockerTimeoutError{timeout, "loading image"}
	}
}

func ( dg * dockerGoClient ) LoadImage ( ctx context . Context , inputStream io . Reader , timeout time . Duration ) error { ctx , cancel := context . WithTimeout ( ctx , timeout ) defer cancel ( ) defer metrics . MetricsEngineGlobal . RecordDockerMetric ( " " ) ( ) response := make ( chan error , 1 ) go func ( ) { response <- dg . loadImage ( ctx , inputStream ) } ( ) select { case resp := <- response : return resp case <- ctx . Done ( ) : return & DockerTimeoutError { timeout , " " } } }

// LoadImage invokes loads an image from an input stream, with a specified timeout

LoadImage invokes loads an image from an input stream with a specified timeout


func (imageState *ImageState) UpdateContainerReference(container *apicontainer.Container) {
	imageState.lock.Lock()
	defer imageState.lock.Unlock()
	seelog.Infof("Updating container reference %v in Image State - %v", container.Name, imageState.Image.ImageID)
	imageState.Containers = append(imageState.Containers, container)
}

func ( imageState * ImageState ) UpdateContainerReference ( container * apicontainer . Container ) { imageState . lock . Lock ( ) defer imageState . lock . Unlock ( ) seelog . Infof ( " " , container . Name , imageState . Image . ImageID ) imageState . Containers = append ( imageState . Containers , container ) }

// UpdateContainerReference updates container reference in image state


func (imageState *ImageState) AddImageName(imageName string) {
	imageState.lock.Lock()
	defer imageState.lock.Unlock()
	if !imageState.HasImageName(imageName) {
		seelog.Infof("Adding image name- %v to Image state- %v", imageName, imageState.Image.ImageID)
		imageState.Image.Names = append(imageState.Image.Names, imageName)
	}
}

func ( imageState * ImageState ) AddImageName ( imageName string ) { imageState . lock . Lock ( ) defer imageState . lock . Unlock ( ) if ! imageState . HasImageName ( imageName ) { seelog . Infof ( " " , imageName , imageState . Image . ImageID ) imageState . Image . Names = append ( imageState . Image . Names , imageName ) } }

// AddImageName adds image name to image state


func (imageState *ImageState) GetImageNamesCount() int {
	imageState.lock.RLock()
	defer imageState.lock.RUnlock()
	return len(imageState.Image.Names)
}

func ( imageState * ImageState ) GetImageNamesCount ( ) int { imageState . lock . RLock ( ) defer imageState . lock . RUnlock ( ) return len ( imageState . Image . Names ) }

// GetImageNamesCount returns number of image names


func (imageState *ImageState) UpdateImageState(container *apicontainer.Container) {
	imageState.AddImageName(container.Image)
	imageState.UpdateContainerReference(container)
}

func ( imageState * ImageState ) UpdateImageState ( container * apicontainer . Container ) { imageState . AddImageName ( container . Image ) imageState . UpdateContainerReference ( container ) }

// UpdateImageState updates image name and container reference in image state


func (imageState *ImageState) RemoveImageName(containerImageName string) {
	imageState.lock.Lock()
	defer imageState.lock.Unlock()
	for i, imageName := range imageState.Image.Names {
		if imageName == containerImageName {
			imageState.Image.Names = append(imageState.Image.Names[:i], imageState.Image.Names[i+1:]...)
		}
	}
}

func ( imageState * ImageState ) RemoveImageName ( containerImageName string ) { imageState . lock . Lock ( ) defer imageState . lock . Unlock ( ) for i , imageName := range imageState . Image . Names { if imageName == containerImageName { imageState . Image . Names = append ( imageState . Image . Names [ : i ] , imageState . Image . Names [ i + 1 : ] ... ) } } }

// RemoveImageName removes image name from image state


func (imageState *ImageState) HasImageName(containerImageName string) bool {
	for _, imageName := range imageState.Image.Names {
		if imageName == containerImageName {
			return true
		}
	}
	return false
}

func ( imageState * ImageState ) HasImageName ( containerImageName string ) bool { for _ , imageName := range imageState . Image . Names { if imageName == containerImageName { return true } } return false }

// HasImageName returns true if image state contains the containerImageName


func (imageState *ImageState) RemoveContainerReference(container *apicontainer.Container) error {
	// Get the image state write lock for updating container reference
	imageState.lock.Lock()
	defer imageState.lock.Unlock()
	for i := range imageState.Containers {
		if imageState.Containers[i].Name == container.Name {
			// Container reference found; hence remove it
			seelog.Infof("Removing Container Reference: %v from Image State- %v", container.Name, imageState.Image.ImageID)
			imageState.Containers = append(imageState.Containers[:i], imageState.Containers[i+1:]...)
			// Update the last used time for the image
			imageState.LastUsedAt = time.Now()
			return nil
		}
	}
	return fmt.Errorf("Container reference is not found in the image state container: %s", container.String())
}

// RemoveContainerReference removes container reference from image state


func (imageState *ImageState) SetPullSucceeded(pullSucceeded bool) {
	imageState.lock.Lock()
	defer imageState.lock.Unlock()

	imageState.PullSucceeded = pullSucceeded
}

func ( imageState * ImageState ) SetPullSucceeded ( pullSucceeded bool ) { imageState . lock . Lock ( ) defer imageState . lock . Unlock ( ) imageState . PullSucceeded = pullSucceeded }

// SetPullSucceeded sets the PullSucceeded of the imageState


func (imageState *ImageState) GetPullSucceeded() bool {
	imageState.lock.RLock()
	defer imageState.lock.RUnlock()

	return imageState.PullSucceeded
}

func ( imageState * ImageState ) GetPullSucceeded ( ) bool { imageState . lock . RLock ( ) defer imageState . lock . RUnlock ( ) return imageState . PullSucceeded }

// GetPullSucceeded safely returns the PullSucceeded of the imageState