<commit_before>// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package model

import (
	"crypto/md5"
	"encoding/binary"
	"net"
	"sort"
	"strings"

	udpa "github.com/cncf/udpa/go/udpa/type/v1"
	"k8s.io/client-go/tools/cache"

	networking "istio.io/api/networking/v1alpha3"
	"istio.io/istio/pkg/config"
	"istio.io/istio/pkg/config/host"
	"istio.io/istio/pkg/config/labels"
	"istio.io/istio/pkg/config/schema/collection"
	"istio.io/istio/pkg/config/schema/gvk"
)

// Statically link protobuf descriptors from UDPA
var _ = udpa.TypedStruct{}

// ConfigKey describe a specific config item.
// In most cases, the name is the config's name. However, for ServiceEntry it is service's FQDN.
type ConfigKey struct {
	Kind      config.GroupVersionKind
	Name      string
	Namespace string
}

func (key ConfigKey) HashCode() uint64 {
	hash := md5.New()
	for _, v := range []string{
		key.Name,
		key.Namespace,
		key.Kind.Kind,
		key.Kind.Group,
		key.Kind.Version,
	} {
		hash.Write([]byte(v))
	}
	var tmp [md5.Size]byte
	sum := hash.Sum(tmp[:0])
	return binary.BigEndian.Uint64(sum)
}

func (key ConfigKey) String() string {
	return key.Kind.String() + "/" + key.Namespace + "/" + key.Name
}

// ConfigsOfKind extracts configs of the specified kind.
func ConfigsOfKind(configs map[ConfigKey]struct{}, kind config.GroupVersionKind) map[ConfigKey]struct{} {
	ret := make(map[ConfigKey]struct{})

	for conf := range configs {
		if conf.Kind == kind {
			ret[conf] = struct{}{}
		}
	}

	return ret
}

// ConfigNamesOfKind extracts config names of the specified kind.
func ConfigNamesOfKind(configs map[ConfigKey]struct{}, kind config.GroupVersionKind) map[string]struct{} {
	ret := make(map[string]struct{})

	for conf := range configs {
		if conf.Kind == kind {
			ret[conf.Name] = struct{}{}
		}
	}

	return ret
}

// ConfigStore describes a set of platform agnostic APIs that must be supported
// by the underlying platform to store and retrieve Istio configuration.
//
// Configuration key is defined to be a combination of the type, name, and
// namespace of the configuration object. The configuration key is guaranteed
// to be unique in the store.
//
// The storage interface presented here assumes that the underlying storage
// layer supports _Get_ (list), _Update_ (update), _Create_ (create) and
// _Delete_ semantics but does not guarantee any transactional semantics.
//
// _Update_, _Create_, and _Delete_ are mutator operations. These operations
// are asynchronous, and you might not see the effect immediately (e.g. _Get_
// might not return the object by key immediately after you mutate the store.)
// Intermittent errors might occur even though the operation succeeds, so you
// should always check if the object store has been modified even if the
// mutating operation returns an error.  Objects should be created with
// _Create_ operation and updated with _Update_ operation.
//
// Resource versions record the last mutation operation on each object. If a
// mutation is applied to a different revision of an object than what the
// underlying storage expects as defined by pure equality, the operation is
// blocked.  The client of this interface should not make assumptions about the
// structure or ordering of the revision identifier.
//
// Object references supplied and returned from this interface should be
// treated as read-only. Modifying them violates thread-safety.
type ConfigStore interface {
	// Schemas exposes the configuration type schema known by the config store.
	// The type schema defines the bidirectional mapping between configuration
	// types and the protobuf encoding schema.
	Schemas() collection.Schemas

	// Get retrieves a configuration element by a type and a key
	Get(typ config.GroupVersionKind, name, namespace string) *config.Config

	// List returns objects by type and namespace.
	// Use "" for the namespace to list across namespaces.
	List(typ config.GroupVersionKind, namespace string) ([]config.Config, error)

	// Create adds a new configuration object to the store. If an object with the
	// same name and namespace for the type already exists, the operation fails
	// with no side effects.
	Create(config config.Config) (revision string, err error)

	// Update modifies an existing configuration object in the store.  Update
	// requires that the object has been created.  Resource version prevents
	// overriding a value that has been changed between prior _Get_ and _Put_
	// operation to achieve optimistic concurrency. This method returns a new
	// revision if the operation succeeds.
	Update(config config.Config) (newRevision string, err error)

	UpdateStatus(config config.Config) (newRevision string, err error)

	// Patch applies only the modifications made in the PatchFunc rather than doing a full replace. Useful to avoid
	// read-modify-write conflicts when there are many concurrent-writers to the same resource.
	Patch(orig config.Config, patchFn config.PatchFunc) (string, error)

	// Delete removes an object from the store by key
	// For k8s, resourceVersion must be fulfilled before a deletion is carried out.
	// If not possible, a 409 Conflict status will be returned.
	Delete(typ config.GroupVersionKind, name, namespace string, resourceVersion *string) error
}

// ConfigStoreCache is a local fully-replicated cache of the config store.  The
// cache actively synchronizes its local state with the remote store and
// provides a notification mechanism to receive update events. As such, the
// notification handlers must be registered prior to calling _Run_, and the
// cache requires initial synchronization grace period after calling  _Run_.
//
// Update notifications require the following consistency guarantee: the view
// in the cache must be AT LEAST as fresh as the moment notification arrives, but
// MAY BE more fresh (e.g. if _Delete_ cancels an _Add_ event).
//
// Handlers execute on the single worker queue in the order they are appended.
// Handlers receive the notification event and the associated object.  Note
// that all handlers must be registered before starting the cache controller.
type ConfigStoreCache interface {
	ConfigStore

	// RegisterEventHandler adds a handler to receive config update events for a
	// configuration type
	RegisterEventHandler(kind config.GroupVersionKind, handler func(config.Config, config.Config, Event))

	// Run until a signal is received
	Run(stop <-chan struct{})

	SetWatchErrorHandler(func(r *cache.Reflector, err error)) error

	// HasSynced returns true after initial cache synchronization is complete
	HasSynced() bool
}

// IstioConfigStore is a specialized interface to access config store using
// Istio configuration types
type IstioConfigStore interface {
	ConfigStore

	// ServiceEntries lists all service entries
	ServiceEntries() []config.Config

	// Gateways lists all gateways bound to the specified workload labels
	Gateways(workloadLabels labels.Collection) []config.Config

	// AuthorizationPolicies selects AuthorizationPolicies in the specified namespace.
	AuthorizationPolicies(namespace string) []config.Config
}

const (
	// NamespaceAll is a designated symbol for listing across all namespaces
	NamespaceAll = ""
)

// ResolveShortnameToFQDN uses metadata information to resolve a reference
// to shortname of the service to FQDN
func ResolveShortnameToFQDN(hostname string, meta config.Meta) host.Name {
	out := hostname
	// Treat the wildcard hostname as fully qualified. Any other variant of a wildcard hostname will contain a `.` too,
	// and skip the next if, so we only need to check for the literal wildcard itself.
	if hostname == "*" {
		return host.Name(out)
	}

	// if the hostname is a valid ipv4 or ipv6 address, do not append domain or namespace
	if net.ParseIP(hostname) != nil {
		return host.Name(out)
	}

	// if FQDN is specified, do not append domain or namespace to hostname
	if !strings.Contains(hostname, ".") {
		if meta.Namespace != "" {
			out = out + "." + meta.Namespace
		}

		// FIXME this is a gross hack to hardcode a service's domain name in kubernetes
		// BUG this will break non kubernetes environments if they use shortnames in the
		// rules.
		if meta.Domain != "" {
			out = out + ".svc." + meta.Domain
		}
	}

	return host.Name(out)
}

// resolveGatewayName uses metadata information to resolve a reference
// to shortname of the gateway to FQDN
func resolveGatewayName(gwname string, meta config.Meta) string {
	out := gwname

	// New way of binding to a gateway in remote namespace
	// is ns/name. Old way is either FQDN or short name
	if !strings.Contains(gwname, "/") {
		if !strings.Contains(gwname, ".") {
			// we have a short name. Resolve to a gateway in same namespace
			out = meta.Namespace + "/" + gwname
		} else {
			// parse namespace from FQDN. This is very hacky, but meant for backward compatibility only
			// This is a legacy FQDN format. Transform name.ns.svc.cluster.local -> ns/name
			i := strings.Index(gwname, ".")
			fqdn := strings.Index(gwname[i+1:], ".")
			if fqdn == -1 {
				out = gwname[i+1:] + "/" + gwname[:i]
			} else {
				out = gwname[i+1:i+1+fqdn] + "/" + gwname[:i]
			}
		}
	} else {
		// remove the . from ./gateway and substitute it with the namespace name
		i := strings.Index(gwname, "/")
		if gwname[:i] == "." {
			out = meta.Namespace + "/" + gwname[i+1:]
		}
	}
	return out
}

// MostSpecificHostMatch compares the elements of the stack to the needle, and returns the longest stack element
// matching the needle, or false if no element in the stack matches the needle.
func MostSpecificHostMatch(needle host.Name, m map[host.Name]struct{}, stack []host.Name) (host.Name, bool) {
	matches := []host.Name{}

	// exact match first
	if m != nil {
		if _, ok := m[needle]; ok {
			return needle, true
		}
	} else {
		for _, h := range stack {
			if h == needle {
				return needle, true
			}
		}
	}

	if needle.IsWildCarded() {
		// slice has better loop performance than map, so use stack to range
		// and stack is ordered before
		for _, h := range stack {
			// both needle and h are wildcards
			if h.IsWildCarded() {
				if len(needle) < len(h) {
					continue
				}
				if strings.HasSuffix(string(needle[1:]), string(h[1:])) {
					matches = append(matches, h)
				}
			}
		}
	} else {
		for _, h := range stack {
			// only n is wildcard
			if h.IsWildCarded() {
				if strings.HasSuffix(string(needle), string(h[1:])) {
					matches = append(matches, h)
				}
			}
		}
	}

	if len(matches) > 0 {
		// TODO: return closest match out of all non-exact matching hosts
		return matches[0], true
	}
	return "", false
}

// istioConfigStore provides a simple adapter for Istio configuration types
// from the generic config registry
type istioConfigStore struct {
	ConfigStore
}

// MakeIstioStore creates a wrapper around a store.
// In pilot it is initialized with a ConfigStoreCache, tests only use
// a regular ConfigStore.
func MakeIstioStore(store ConfigStore) IstioConfigStore {
	return &istioConfigStore{store}
}

func (store *istioConfigStore) ServiceEntries() []config.Config {
	serviceEntries, err := store.List(gvk.ServiceEntry, NamespaceAll)
	if err != nil {
		return nil
	}

	// To ensure the ip allocation logic deterministically
	// allocates the same IP to a service entry.
	sortConfigByCreationTime(serviceEntries)
	return serviceEntries
}

// sortConfigByCreationTime sorts the list of config objects in ascending order by their creation time (if available).
func sortConfigByCreationTime(configs []config.Config) {
	sort.Slice(configs, func(i, j int) bool {
		// If creation time is the same, then behavior is nondeterministic. In this case, we can
		// pick an arbitrary but consistent ordering based on name and namespace, which is unique.
		// CreationTimestamp is stored in seconds, so this is not uncommon.
		if configs[i].CreationTimestamp == configs[j].CreationTimestamp {
			in := configs[i].Name + "." + configs[i].Namespace
			jn := configs[j].Name + "." + configs[j].Namespace
			return in < jn
		}
		return configs[i].CreationTimestamp.Before(configs[j].CreationTimestamp)
	})
}

func (store *istioConfigStore) Gateways(workloadLabels labels.Collection) []config.Config {
	configs, err := store.List(gvk.Gateway, NamespaceAll)
	if err != nil {
		return nil
	}

	sortConfigByCreationTime(configs)
	out := make([]config.Config, 0)
	for _, cfg := range configs {
		gateway := cfg.Spec.(*networking.Gateway)
		if gateway.GetSelector() == nil {
			// no selector. Applies to all workloads asking for the gateway
			out = append(out, cfg)
		} else {
			gatewaySelector := labels.Instance(gateway.GetSelector())
			if workloadLabels.IsSupersetOf(gatewaySelector) {
				out = append(out, cfg)
			}
		}
	}
	return out
}

// key creates a key from a reference's name and namespace.
func key(name, namespace string) string {
	return name + "/" + namespace
}

func (store *istioConfigStore) AuthorizationPolicies(namespace string) []config.Config {
	authorizationPolicies, err := store.List(gvk.AuthorizationPolicy, namespace)
	if err != nil {
		log.Errorf("failed to get AuthorizationPolicy in namespace %s: %v", namespace, err)
		return nil
	}

	return authorizationPolicies
}
<commit_msg>Trim down long log lines: (#34332)<commit_after>// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package model

import (
	"crypto/md5"
	"encoding/binary"
	"net"
	"sort"
	"strings"

	udpa "github.com/cncf/udpa/go/udpa/type/v1"
	"k8s.io/client-go/tools/cache"

	networking "istio.io/api/networking/v1alpha3"
	"istio.io/istio/pkg/config"
	"istio.io/istio/pkg/config/host"
	"istio.io/istio/pkg/config/labels"
	"istio.io/istio/pkg/config/schema/collection"
	"istio.io/istio/pkg/config/schema/gvk"
)

// Statically link protobuf descriptors from UDPA
var _ = udpa.TypedStruct{}

// ConfigKey describe a specific config item.
// In most cases, the name is the config's name. However, for ServiceEntry it is service's FQDN.
type ConfigKey struct {
	Kind      config.GroupVersionKind
	Name      string
	Namespace string
}

func (key ConfigKey) HashCode() uint64 {
	hash := md5.New()
	for _, v := range []string{
		key.Name,
		key.Namespace,
		key.Kind.Kind,
		key.Kind.Group,
		key.Kind.Version,
	} {
		hash.Write([]byte(v))
	}
	var tmp [md5.Size]byte
	sum := hash.Sum(tmp[:0])
	return binary.BigEndian.Uint64(sum)
}

func (key ConfigKey) String() string {
	return key.Kind.Kind + "/" + key.Namespace + "/" + key.Name
}

// ConfigsOfKind extracts configs of the specified kind.
func ConfigsOfKind(configs map[ConfigKey]struct{}, kind config.GroupVersionKind) map[ConfigKey]struct{} {
	ret := make(map[ConfigKey]struct{})

	for conf := range configs {
		if conf.Kind == kind {
			ret[conf] = struct{}{}
		}
	}

	return ret
}

// ConfigNamesOfKind extracts config names of the specified kind.
func ConfigNamesOfKind(configs map[ConfigKey]struct{}, kind config.GroupVersionKind) map[string]struct{} {
	ret := make(map[string]struct{})

	for conf := range configs {
		if conf.Kind == kind {
			ret[conf.Name] = struct{}{}
		}
	}

	return ret
}

// ConfigStore describes a set of platform agnostic APIs that must be supported
// by the underlying platform to store and retrieve Istio configuration.
//
// Configuration key is defined to be a combination of the type, name, and
// namespace of the configuration object. The configuration key is guaranteed
// to be unique in the store.
//
// The storage interface presented here assumes that the underlying storage
// layer supports _Get_ (list), _Update_ (update), _Create_ (create) and
// _Delete_ semantics but does not guarantee any transactional semantics.
//
// _Update_, _Create_, and _Delete_ are mutator operations. These operations
// are asynchronous, and you might not see the effect immediately (e.g. _Get_
// might not return the object by key immediately after you mutate the store.)
// Intermittent errors might occur even though the operation succeeds, so you
// should always check if the object store has been modified even if the
// mutating operation returns an error.  Objects should be created with
// _Create_ operation and updated with _Update_ operation.
//
// Resource versions record the last mutation operation on each object. If a
// mutation is applied to a different revision of an object than what the
// underlying storage expects as defined by pure equality, the operation is
// blocked.  The client of this interface should not make assumptions about the
// structure or ordering of the revision identifier.
//
// Object references supplied and returned from this interface should be
// treated as read-only. Modifying them violates thread-safety.
type ConfigStore interface {
	// Schemas exposes the configuration type schema known by the config store.
	// The type schema defines the bidirectional mapping between configuration
	// types and the protobuf encoding schema.
	Schemas() collection.Schemas

	// Get retrieves a configuration element by a type and a key
	Get(typ config.GroupVersionKind, name, namespace string) *config.Config

	// List returns objects by type and namespace.
	// Use "" for the namespace to list across namespaces.
	List(typ config.GroupVersionKind, namespace string) ([]config.Config, error)

	// Create adds a new configuration object to the store. If an object with the
	// same name and namespace for the type already exists, the operation fails
	// with no side effects.
	Create(config config.Config) (revision string, err error)

	// Update modifies an existing configuration object in the store.  Update
	// requires that the object has been created.  Resource version prevents
	// overriding a value that has been changed between prior _Get_ and _Put_
	// operation to achieve optimistic concurrency. This method returns a new
	// revision if the operation succeeds.
	Update(config config.Config) (newRevision string, err error)

	UpdateStatus(config config.Config) (newRevision string, err error)

	// Patch applies only the modifications made in the PatchFunc rather than doing a full replace. Useful to avoid
	// read-modify-write conflicts when there are many concurrent-writers to the same resource.
	Patch(orig config.Config, patchFn config.PatchFunc) (string, error)

	// Delete removes an object from the store by key
	// For k8s, resourceVersion must be fulfilled before a deletion is carried out.
	// If not possible, a 409 Conflict status will be returned.
	Delete(typ config.GroupVersionKind, name, namespace string, resourceVersion *string) error
}

// ConfigStoreCache is a local fully-replicated cache of the config store.  The
// cache actively synchronizes its local state with the remote store and
// provides a notification mechanism to receive update events. As such, the
// notification handlers must be registered prior to calling _Run_, and the
// cache requires initial synchronization grace period after calling  _Run_.
//
// Update notifications require the following consistency guarantee: the view
// in the cache must be AT LEAST as fresh as the moment notification arrives, but
// MAY BE more fresh (e.g. if _Delete_ cancels an _Add_ event).
//
// Handlers execute on the single worker queue in the order they are appended.
// Handlers receive the notification event and the associated object.  Note
// that all handlers must be registered before starting the cache controller.
type ConfigStoreCache interface {
	ConfigStore

	// RegisterEventHandler adds a handler to receive config update events for a
	// configuration type
	RegisterEventHandler(kind config.GroupVersionKind, handler func(config.Config, config.Config, Event))

	// Run until a signal is received
	Run(stop <-chan struct{})

	SetWatchErrorHandler(func(r *cache.Reflector, err error)) error

	// HasSynced returns true after initial cache synchronization is complete
	HasSynced() bool
}

// IstioConfigStore is a specialized interface to access config store using
// Istio configuration types
type IstioConfigStore interface {
	ConfigStore

	// ServiceEntries lists all service entries
	ServiceEntries() []config.Config

	// Gateways lists all gateways bound to the specified workload labels
	Gateways(workloadLabels labels.Collection) []config.Config

	// AuthorizationPolicies selects AuthorizationPolicies in the specified namespace.
	AuthorizationPolicies(namespace string) []config.Config
}

const (
	// NamespaceAll is a designated symbol for listing across all namespaces
	NamespaceAll = ""
)

// ResolveShortnameToFQDN uses metadata information to resolve a reference
// to shortname of the service to FQDN
func ResolveShortnameToFQDN(hostname string, meta config.Meta) host.Name {
	out := hostname
	// Treat the wildcard hostname as fully qualified. Any other variant of a wildcard hostname will contain a `.` too,
	// and skip the next if, so we only need to check for the literal wildcard itself.
	if hostname == "*" {
		return host.Name(out)
	}

	// if the hostname is a valid ipv4 or ipv6 address, do not append domain or namespace
	if net.ParseIP(hostname) != nil {
		return host.Name(out)
	}

	// if FQDN is specified, do not append domain or namespace to hostname
	if !strings.Contains(hostname, ".") {
		if meta.Namespace != "" {
			out = out + "." + meta.Namespace
		}

		// FIXME this is a gross hack to hardcode a service's domain name in kubernetes
		// BUG this will break non kubernetes environments if they use shortnames in the
		// rules.
		if meta.Domain != "" {
			out = out + ".svc." + meta.Domain
		}
	}

	return host.Name(out)
}

// resolveGatewayName uses metadata information to resolve a reference
// to shortname of the gateway to FQDN
func resolveGatewayName(gwname string, meta config.Meta) string {
	out := gwname

	// New way of binding to a gateway in remote namespace
	// is ns/name. Old way is either FQDN or short name
	if !strings.Contains(gwname, "/") {
		if !strings.Contains(gwname, ".") {
			// we have a short name. Resolve to a gateway in same namespace
			out = meta.Namespace + "/" + gwname
		} else {
			// parse namespace from FQDN. This is very hacky, but meant for backward compatibility only
			// This is a legacy FQDN format. Transform name.ns.svc.cluster.local -> ns/name
			i := strings.Index(gwname, ".")
			fqdn := strings.Index(gwname[i+1:], ".")
			if fqdn == -1 {
				out = gwname[i+1:] + "/" + gwname[:i]
			} else {
				out = gwname[i+1:i+1+fqdn] + "/" + gwname[:i]
			}
		}
	} else {
		// remove the . from ./gateway and substitute it with the namespace name
		i := strings.Index(gwname, "/")
		if gwname[:i] == "." {
			out = meta.Namespace + "/" + gwname[i+1:]
		}
	}
	return out
}

// MostSpecificHostMatch compares the elements of the stack to the needle, and returns the longest stack element
// matching the needle, or false if no element in the stack matches the needle.
func MostSpecificHostMatch(needle host.Name, m map[host.Name]struct{}, stack []host.Name) (host.Name, bool) {
	matches := []host.Name{}

	// exact match first
	if m != nil {
		if _, ok := m[needle]; ok {
			return needle, true
		}
	} else {
		for _, h := range stack {
			if h == needle {
				return needle, true
			}
		}
	}

	if needle.IsWildCarded() {
		// slice has better loop performance than map, so use stack to range
		// and stack is ordered before
		for _, h := range stack {
			// both needle and h are wildcards
			if h.IsWildCarded() {
				if len(needle) < len(h) {
					continue
				}
				if strings.HasSuffix(string(needle[1:]), string(h[1:])) {
					matches = append(matches, h)
				}
			}
		}
	} else {
		for _, h := range stack {
			// only n is wildcard
			if h.IsWildCarded() {
				if strings.HasSuffix(string(needle), string(h[1:])) {
					matches = append(matches, h)
				}
			}
		}
	}

	if len(matches) > 0 {
		// TODO: return closest match out of all non-exact matching hosts
		return matches[0], true
	}
	return "", false
}

// istioConfigStore provides a simple adapter for Istio configuration types
// from the generic config registry
type istioConfigStore struct {
	ConfigStore
}

// MakeIstioStore creates a wrapper around a store.
// In pilot it is initialized with a ConfigStoreCache, tests only use
// a regular ConfigStore.
func MakeIstioStore(store ConfigStore) IstioConfigStore {
	return &istioConfigStore{store}
}

func (store *istioConfigStore) ServiceEntries() []config.Config {
	serviceEntries, err := store.List(gvk.ServiceEntry, NamespaceAll)
	if err != nil {
		return nil
	}

	// To ensure the ip allocation logic deterministically
	// allocates the same IP to a service entry.
	sortConfigByCreationTime(serviceEntries)
	return serviceEntries
}

// sortConfigByCreationTime sorts the list of config objects in ascending order by their creation time (if available).
func sortConfigByCreationTime(configs []config.Config) {
	sort.Slice(configs, func(i, j int) bool {
		// If creation time is the same, then behavior is nondeterministic. In this case, we can
		// pick an arbitrary but consistent ordering based on name and namespace, which is unique.
		// CreationTimestamp is stored in seconds, so this is not uncommon.
		if configs[i].CreationTimestamp == configs[j].CreationTimestamp {
			in := configs[i].Name + "." + configs[i].Namespace
			jn := configs[j].Name + "." + configs[j].Namespace
			return in < jn
		}
		return configs[i].CreationTimestamp.Before(configs[j].CreationTimestamp)
	})
}

func (store *istioConfigStore) Gateways(workloadLabels labels.Collection) []config.Config {
	configs, err := store.List(gvk.Gateway, NamespaceAll)
	if err != nil {
		return nil
	}

	sortConfigByCreationTime(configs)
	out := make([]config.Config, 0)
	for _, cfg := range configs {
		gateway := cfg.Spec.(*networking.Gateway)
		if gateway.GetSelector() == nil {
			// no selector. Applies to all workloads asking for the gateway
			out = append(out, cfg)
		} else {
			gatewaySelector := labels.Instance(gateway.GetSelector())
			if workloadLabels.IsSupersetOf(gatewaySelector) {
				out = append(out, cfg)
			}
		}
	}
	return out
}

// key creates a key from a reference's name and namespace.
func key(name, namespace string) string {
	return name + "/" + namespace
}

func (store *istioConfigStore) AuthorizationPolicies(namespace string) []config.Config {
	authorizationPolicies, err := store.List(gvk.AuthorizationPolicy, namespace)
	if err != nil {
		log.Errorf("failed to get AuthorizationPolicy in namespace %s: %v", namespace, err)
		return nil
	}

	return authorizationPolicies
}
<|endoftext|>


<commit_before>// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package status

import (
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/gogo/protobuf/types"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/utils/clock"

	"istio.io/api/meta/v1alpha1"
	"istio.io/istio/pilot/pkg/features"
	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pkg/config"
	"istio.io/istio/pkg/config/schema/collections"
	"istio.io/pkg/log"
)

var scope = log.RegisterScope("status",
	"CRD distribution status debugging", 0)

type Progress struct {
	AckedInstances int
	TotalInstances int
}

func (p *Progress) PlusEquals(p2 Progress) {
	p.TotalInstances += p2.TotalInstances
	p.AckedInstances += p2.AckedInstances
}

type DistributionController struct {
	configStore     model.ConfigStore
	mu              sync.RWMutex
	CurrentState    map[Resource]map[string]Progress
	ObservationTime map[string]time.Time
	UpdateInterval  time.Duration
	dynamicClient   dynamic.Interface
	clock           clock.Clock
	workers         WorkerQueue
	StaleInterval   time.Duration
	cmInformer      cache.SharedIndexInformer
}

func NewController(restConfig rest.Config, namespace string, cs model.ConfigStore) *DistributionController {
	c := &DistributionController{
		CurrentState:    make(map[Resource]map[string]Progress),
		ObservationTime: make(map[string]time.Time),
		UpdateInterval:  200 * time.Millisecond,
		StaleInterval:   time.Minute,
		clock:           clock.RealClock{},
		configStore:     cs,
	}

	// client-go defaults to 5 QPS, with 10 Boost, which is insufficient for updating status on all the config
	// in the mesh.  These values can be configured using environment variables for tuning (see pilot/pkg/features)
	restConfig.QPS = float32(features.StatusQPS)
	restConfig.Burst = features.StatusBurst
	var err error
	if c.dynamicClient, err = dynamic.NewForConfig(&restConfig); err != nil {
		scope.Fatalf("Could not connect to kubernetes: %s", err)
	}

	// configmap informer
	i := informers.NewSharedInformerFactoryWithOptions(kubernetes.NewForConfigOrDie(&restConfig), 1*time.Minute,
		informers.WithNamespace(namespace),
		informers.WithTweakListOptions(func(listOptions *metav1.ListOptions) {
			listOptions.LabelSelector = labels.Set(map[string]string{labelKey: "true"}).AsSelector().String()
		})).
		Core().V1().ConfigMaps()
	c.cmInformer = i.Informer()
	i.Informer().AddEventHandler(&DistroReportHandler{dc: c})

	return c
}

func (c *DistributionController) Start(stop <-chan struct{}) {
	scope.Info("Starting status leader controller")

	// this will list all existing configmaps, as well as updates, right?
	ctx := NewIstioContext(stop)
	go c.cmInformer.Run(ctx.Done())

	c.workers = NewWorkerPool(func(resource *Resource, progress *Progress) {
		c.writeStatus(*resource, *progress)
	}, uint(features.StatusMaxWorkers.Get()))
	c.workers.Run(ctx)

	//  create Status Writer
	t := c.clock.Tick(c.UpdateInterval)

	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			case <-t:
				staleReporters := c.writeAllStatus()
				if len(staleReporters) > 0 {
					c.removeStaleReporters(staleReporters)
				}
			}
		}
	}()
}

func (c *DistributionController) handleReport(d DistributionReport) {
	defer c.mu.Unlock()
	c.mu.Lock()
	for resstr := range d.InProgressResources {
		res := *ResourceFromString(resstr)
		if _, ok := c.CurrentState[res]; !ok {
			c.CurrentState[res] = make(map[string]Progress)
		}
		c.CurrentState[res][d.Reporter] = Progress{d.InProgressResources[resstr], d.DataPlaneCount}
	}
	c.ObservationTime[d.Reporter] = c.clock.Now()
}

func (c *DistributionController) writeAllStatus() (staleReporters []string) {
	defer c.mu.RUnlock()
	c.mu.RLock()
	for config, fractions := range c.CurrentState {
		var distributionState Progress
		for reporter, w := range fractions {
			// check for stale data here
			if c.clock.Since(c.ObservationTime[reporter]) > c.StaleInterval {
				scope.Warnf("Status reporter %s has not been heard from since %v, deleting report.",
					reporter, c.ObservationTime[reporter])
				staleReporters = append(staleReporters, reporter)
			} else {
				distributionState.PlusEquals(w)
			}
		}
		if distributionState.TotalInstances > 0 { // this is necessary when all reports are stale.
			c.queueWriteStatus(config, distributionState)
		}
	}
	return
}

func (c *DistributionController) writeStatus(config Resource, distributionState Progress) {
	schema, _ := collections.All.FindByGroupVersionResource(config.GroupVersionResource)
	if schema == nil {
		scope.Warnf("schema %v could not be identified", schema)
		c.pruneOldVersion(config)
		return
	}
	if !strings.HasSuffix(schema.Resource().Group(), "istio.io") {
		// we don't write status for objects we don't own
		return
	}
	current := c.configStore.Get(schema.Resource().GroupVersionKind(), config.Name, config.Namespace)
	if current == nil {
		scope.Warnf("config store missing entry %v, status will not update", config)
		// this happens when resources are rapidly deleted, such as the validation-readiness checker
		c.pruneOldVersion(config)
		return
	}
	if config.Generation != strconv.FormatInt(current.Generation, 10) {
		// this distribution report is for an old version of the object.  Prune and continue.
		c.pruneOldVersion(config)
		return
	}

	// check if status needs updating
	if needsReconcile, desiredStatus := ReconcileStatuses(current, distributionState, current.Generation); needsReconcile {
		// technically, we should be updating probe time even when reconciling isn't needed, but
		// I'm skipping that for efficiency.
		current.Status = desiredStatus
		_, err := c.configStore.UpdateStatus(*current)
		if err != nil {
			scope.Errorf("Encountered unexpected error updating status for %v, will try again later: %s", config, err)
			return
		}
	}
}

func (c *DistributionController) pruneOldVersion(config Resource) {
	defer c.mu.Unlock()
	c.mu.Lock()
	delete(c.CurrentState, config)
}

func (c *DistributionController) removeStaleReporters(staleReporters []string) {
	defer c.mu.Unlock()
	c.mu.Lock()
	for key, fractions := range c.CurrentState {
		for _, staleReporter := range staleReporters {
			delete(fractions, staleReporter)
		}
		c.CurrentState[key] = fractions
	}
}

func (c *DistributionController) queueWriteStatus(config Resource, state Progress) {
	c.workers.Push(config, state)
}

func (c *DistributionController) configDeleted(res config.Config) {
	r := ResourceFromModelConfig(res)
	c.workers.Delete(*r)
}

func GetTypedStatus(in interface{}) (out *v1alpha1.IstioStatus, err error) {
	if ret, ok := in.(*v1alpha1.IstioStatus); ok {
		return ret, nil
	}
	return nil, fmt.Errorf("cannot cast %t: %v to IstioStatus", in, in)
}

func boolToConditionStatus(b bool) string {
	if b {
		return "True"
	}
	return "False"
}

func ReconcileStatuses(current *config.Config, desired Progress, generation int64) (bool, *v1alpha1.IstioStatus) {
	needsReconcile := false
	currentStatus, err := GetTypedStatus(current.Status)
	desiredCondition := v1alpha1.IstioCondition{
		Type:               "Reconciled",
		Status:             boolToConditionStatus(desired.AckedInstances == desired.TotalInstances),
		LastProbeTime:      types.TimestampNow(),
		LastTransitionTime: types.TimestampNow(),
		Message:            fmt.Sprintf("%d/%d proxies up to date.", desired.AckedInstances, desired.TotalInstances),
	}
	if err != nil {
		// the status field is in an unexpected state.
		if scope.DebugEnabled() {
			scope.Debugf("Encountered unexpected status content.  Overwriting status: %v", current.Status)
		} else {
			scope.Warn("Encountered unexpected status content.  Overwriting status.")
		}
		currentStatus = &v1alpha1.IstioStatus{
			Conditions: []*v1alpha1.IstioCondition{&desiredCondition},
		}
		currentStatus.ObservedGeneration = generation
		return true, currentStatus
	}
	var currentCondition *v1alpha1.IstioCondition
	conditionIndex := -1
	for i, c := range currentStatus.Conditions {
		if c.Type == "Reconciled" {
			currentCondition = currentStatus.Conditions[i]
			conditionIndex = i
		}
	}
	if currentCondition == nil ||
		currentCondition.Message != desiredCondition.Message ||
		currentCondition.Status != desiredCondition.Status {
		needsReconcile = true
	}
	if conditionIndex > -1 {
		currentStatus.Conditions[conditionIndex] = &desiredCondition
	} else {
		currentStatus.Conditions = append(currentStatus.Conditions, &desiredCondition)
	}
	currentStatus.ObservedGeneration = generation
	return needsReconcile, currentStatus
}

type DistroReportHandler struct {
	dc *DistributionController
}

func (drh *DistroReportHandler) OnAdd(obj interface{}) {
	drh.HandleNew(obj)
}

func (drh *DistroReportHandler) OnUpdate(oldObj, newObj interface{}) {
	drh.HandleNew(newObj)
}

func (drh *DistroReportHandler) HandleNew(obj interface{}) {
	cm, ok := obj.(*v1.ConfigMap)
	if !ok {
		scope.Warnf("expected configmap, but received %v, discarding", obj)
		return
	}
	rptStr := cm.Data[dataField]
	scope.Debugf("using report: %s", rptStr)
	dr, err := ReportFromYaml([]byte(cm.Data[dataField]))
	if err != nil {
		scope.Warnf("received malformed distributionReport %s, discarding: %v", cm.Name, err)
		return
	}
	drh.dc.handleReport(dr)
}

func (drh *DistroReportHandler) OnDelete(obj interface{}) {
	// TODO: what do we do here?  will these ever be deleted?
}
<commit_msg>Fix data race in analysis status update (#31120)<commit_after>// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package status

import (
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/gogo/protobuf/types"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/utils/clock"

	"istio.io/api/meta/v1alpha1"
	"istio.io/istio/pilot/pkg/features"
	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pkg/config"
	"istio.io/istio/pkg/config/schema/collections"
	"istio.io/pkg/log"
)

var scope = log.RegisterScope("status",
	"CRD distribution status debugging", 0)

type Progress struct {
	AckedInstances int
	TotalInstances int
}

func (p *Progress) PlusEquals(p2 Progress) {
	p.TotalInstances += p2.TotalInstances
	p.AckedInstances += p2.AckedInstances
}

type DistributionController struct {
	configStore     model.ConfigStore
	mu              sync.RWMutex
	CurrentState    map[Resource]map[string]Progress
	ObservationTime map[string]time.Time
	UpdateInterval  time.Duration
	dynamicClient   dynamic.Interface
	clock           clock.Clock
	workers         WorkerQueue
	StaleInterval   time.Duration
	cmInformer      cache.SharedIndexInformer
}

func NewController(restConfig rest.Config, namespace string, cs model.ConfigStore) *DistributionController {
	c := &DistributionController{
		CurrentState:    make(map[Resource]map[string]Progress),
		ObservationTime: make(map[string]time.Time),
		UpdateInterval:  200 * time.Millisecond,
		StaleInterval:   time.Minute,
		clock:           clock.RealClock{},
		configStore:     cs,
	}

	// client-go defaults to 5 QPS, with 10 Boost, which is insufficient for updating status on all the config
	// in the mesh.  These values can be configured using environment variables for tuning (see pilot/pkg/features)
	restConfig.QPS = float32(features.StatusQPS)
	restConfig.Burst = features.StatusBurst
	var err error
	if c.dynamicClient, err = dynamic.NewForConfig(&restConfig); err != nil {
		scope.Fatalf("Could not connect to kubernetes: %s", err)
	}

	// configmap informer
	i := informers.NewSharedInformerFactoryWithOptions(kubernetes.NewForConfigOrDie(&restConfig), 1*time.Minute,
		informers.WithNamespace(namespace),
		informers.WithTweakListOptions(func(listOptions *metav1.ListOptions) {
			listOptions.LabelSelector = labels.Set(map[string]string{labelKey: "true"}).AsSelector().String()
		})).
		Core().V1().ConfigMaps()
	c.cmInformer = i.Informer()
	i.Informer().AddEventHandler(&DistroReportHandler{dc: c})

	return c
}

func (c *DistributionController) Start(stop <-chan struct{}) {
	scope.Info("Starting status leader controller")

	// this will list all existing configmaps, as well as updates, right?
	ctx := NewIstioContext(stop)
	go c.cmInformer.Run(ctx.Done())

	c.workers = NewWorkerPool(func(resource *Resource, progress *Progress) {
		c.writeStatus(*resource, *progress)
	}, uint(features.StatusMaxWorkers.Get()))
	c.workers.Run(ctx)

	//  create Status Writer
	t := c.clock.Tick(c.UpdateInterval)

	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			case <-t:
				staleReporters := c.writeAllStatus()
				if len(staleReporters) > 0 {
					c.removeStaleReporters(staleReporters)
				}
			}
		}
	}()
}

func (c *DistributionController) handleReport(d DistributionReport) {
	defer c.mu.Unlock()
	c.mu.Lock()
	for resstr := range d.InProgressResources {
		res := *ResourceFromString(resstr)
		if _, ok := c.CurrentState[res]; !ok {
			c.CurrentState[res] = make(map[string]Progress)
		}
		c.CurrentState[res][d.Reporter] = Progress{d.InProgressResources[resstr], d.DataPlaneCount}
	}
	c.ObservationTime[d.Reporter] = c.clock.Now()
}

func (c *DistributionController) writeAllStatus() (staleReporters []string) {
	defer c.mu.RUnlock()
	c.mu.RLock()
	for config, fractions := range c.CurrentState {
		var distributionState Progress
		for reporter, w := range fractions {
			// check for stale data here
			if c.clock.Since(c.ObservationTime[reporter]) > c.StaleInterval {
				scope.Warnf("Status reporter %s has not been heard from since %v, deleting report.",
					reporter, c.ObservationTime[reporter])
				staleReporters = append(staleReporters, reporter)
			} else {
				distributionState.PlusEquals(w)
			}
		}
		if distributionState.TotalInstances > 0 { // this is necessary when all reports are stale.
			c.queueWriteStatus(config, distributionState)
		}
	}
	return
}

func (c *DistributionController) writeStatus(config Resource, distributionState Progress) {
	schema, _ := collections.All.FindByGroupVersionResource(config.GroupVersionResource)
	if schema == nil {
		scope.Warnf("schema %v could not be identified", schema)
		c.pruneOldVersion(config)
		return
	}
	if !strings.HasSuffix(schema.Resource().Group(), "istio.io") {
		// we don't write status for objects we don't own
		return
	}
	current := c.configStore.Get(schema.Resource().GroupVersionKind(), config.Name, config.Namespace)
	if current == nil {
		scope.Warnf("config store missing entry %v, status will not update", config)
		// this happens when resources are rapidly deleted, such as the validation-readiness checker
		c.pruneOldVersion(config)
		return
	}
	if config.Generation != strconv.FormatInt(current.Generation, 10) {
		// this distribution report is for an old version of the object.  Prune and continue.
		c.pruneOldVersion(config)
		return
	}

	// check if status needs updating
	if needsReconcile, desiredStatus := ReconcileStatuses(current, distributionState, current.Generation); needsReconcile {
		// technically, we should be updating probe time even when reconciling isn't needed, but
		// I'm skipping that for efficiency.
		current.Status = desiredStatus
		_, err := c.configStore.UpdateStatus(*current)
		if err != nil {
			scope.Errorf("Encountered unexpected error updating status for %v, will try again later: %s", config, err)
			return
		}
	}
}

func (c *DistributionController) pruneOldVersion(config Resource) {
	defer c.mu.Unlock()
	c.mu.Lock()
	delete(c.CurrentState, config)
}

func (c *DistributionController) removeStaleReporters(staleReporters []string) {
	defer c.mu.Unlock()
	c.mu.Lock()
	for key, fractions := range c.CurrentState {
		for _, staleReporter := range staleReporters {
			delete(fractions, staleReporter)
		}
		c.CurrentState[key] = fractions
	}
}

func (c *DistributionController) queueWriteStatus(config Resource, state Progress) {
	c.workers.Push(config, state)
}

func (c *DistributionController) configDeleted(res config.Config) {
	r := ResourceFromModelConfig(res)
	c.workers.Delete(*r)
}

func GetTypedStatus(in interface{}) (out *v1alpha1.IstioStatus, err error) {
	if ret, ok := in.(*v1alpha1.IstioStatus); ok {
		return ret, nil
	}
	return nil, fmt.Errorf("cannot cast %t: %v to IstioStatus", in, in)
}

func boolToConditionStatus(b bool) string {
	if b {
		return "True"
	}
	return "False"
}

func ReconcileStatuses(current *config.Config, desired Progress, generation int64) (bool, *v1alpha1.IstioStatus) {
	needsReconcile := false
	currentStatus, err := GetTypedStatus(current.Status)
	desiredCondition := v1alpha1.IstioCondition{
		Type:               "Reconciled",
		Status:             boolToConditionStatus(desired.AckedInstances == desired.TotalInstances),
		LastProbeTime:      types.TimestampNow(),
		LastTransitionTime: types.TimestampNow(),
		Message:            fmt.Sprintf("%d/%d proxies up to date.", desired.AckedInstances, desired.TotalInstances),
	}
	if err != nil {
		// the status field is in an unexpected state.
		if scope.DebugEnabled() {
			scope.Debugf("Encountered unexpected status content.  Overwriting status: %v", current.Status)
		} else {
			scope.Warn("Encountered unexpected status content.  Overwriting status.")
		}
		currentStatus = &v1alpha1.IstioStatus{
			Conditions: []*v1alpha1.IstioCondition{&desiredCondition},
		}
		currentStatus.ObservedGeneration = generation
		return true, currentStatus
	}
	currentStatus = currentStatus.DeepCopy()
	var currentCondition *v1alpha1.IstioCondition
	conditionIndex := -1
	for i, c := range currentStatus.Conditions {
		if c.Type == "Reconciled" {
			currentCondition = currentStatus.Conditions[i]
			conditionIndex = i
		}
	}
	if currentCondition == nil ||
		currentCondition.Message != desiredCondition.Message ||
		currentCondition.Status != desiredCondition.Status {
		needsReconcile = true
	}
	if conditionIndex > -1 {
		currentStatus.Conditions[conditionIndex] = &desiredCondition
	} else {
		currentStatus.Conditions = append(currentStatus.Conditions, &desiredCondition)
	}
	currentStatus.ObservedGeneration = generation
	return needsReconcile, currentStatus
}

type DistroReportHandler struct {
	dc *DistributionController
}

func (drh *DistroReportHandler) OnAdd(obj interface{}) {
	drh.HandleNew(obj)
}

func (drh *DistroReportHandler) OnUpdate(oldObj, newObj interface{}) {
	drh.HandleNew(newObj)
}

func (drh *DistroReportHandler) HandleNew(obj interface{}) {
	cm, ok := obj.(*v1.ConfigMap)
	if !ok {
		scope.Warnf("expected configmap, but received %v, discarding", obj)
		return
	}
	rptStr := cm.Data[dataField]
	scope.Debugf("using report: %s", rptStr)
	dr, err := ReportFromYaml([]byte(cm.Data[dataField]))
	if err != nil {
		scope.Warnf("received malformed distributionReport %s, discarding: %v", cm.Name, err)
		return
	}
	drh.dc.handleReport(dr)
}

func (drh *DistroReportHandler) OnDelete(obj interface{}) {
	// TODO: what do we do here?  will these ever be deleted?
}
<|endoftext|>


<commit_before>package ping

import (
	"bytes"
	"time"

	"github.com/hsheth2/gonet/icmp"

	"github.com/hsheth2/gonet/ipv4"

	"github.com/hsheth2/logs"
)

const DATA_56_BYTES = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd"

func (pm *Ping_Manager) ping_response_dealer() {
	for {
		ping := <-pm.reply
		identNum := uint16(ping.Header.Opt >> 16)
		if _, ok := pm.identifiers[identNum]; !ok {
			/*logs*/ logs.Info.Println("Dropped something from response dealer, identnum=", identNum, "options=", ping.Header.Opt)
			continue
		}
		pm.identifiers[identNum] <- ping
	}
}

func sendSinglePing(writer ipv4.Writer, id, seq uint16, timeout time.Duration, reply chan *icmp.Packet) {
	// prepare packet
	packet := &icmp.Header{
		Tp:   icmp.EchoRequest,
		Code: PING_ICMP_CODE,
		Opt:  uint32(id)<<16 | uint32(seq),
		Data: []byte(DATA_56_BYTES), // TODO make legit by putting the timestamp in the data
	}

	// make data
	icmp.SendPacket(writer, packet)

	time1 := time.Now()
	timeoutTimer := time.NewTimer(timeout)
	go func(seqChan chan *icmp.Packet, header *icmp.Header, time1 *time.Time, timer *time.Timer) {
		defer close(seqChan)
		for {
			select {
			case pingResonse := <-seqChan:
				if !bytes.Equal(pingResonse.Header.Data, header.Data) {
					/*logs*/ logs.Info.Println("Dropped packet because header data not equal to ping sent")
					continue
				}
				time2 := time.Now()
				logs.Info.Printf("%d bytes from %v: icmp_seq=%d time=%f ms",
					len(header.Data)+icmp.HeaderMinSize,
					pingResonse.RIP.IP,
					uint16(header.Opt),
					float32(time2.Sub(*time1).Nanoseconds())/1000000) // put ttl
				return
			case <-timer.C:
				logs.Info.Println("Seq num of", uint16(header.Opt), "timed out")
				return
			}
		}
	}(reply, packet, &time1, timeoutTimer)
}

func (pm *Ping_Manager) initIdentifier(terminate chan bool) (id uint16, seqChannel map[uint16](chan *icmp.Packet), err error) {
	// get identifier
	pm.currentIdentifier++
	id = pm.currentIdentifier

	// setup sequence number dealer
	pm.identifiers[id] = make(chan *icmp.Packet)
	seqChannel = make(map[uint16](chan *icmp.Packet))

	// create go routine function to deal packets
	go sequenceDealer(pm.identifiers[id], seqChannel, terminate)

	return id, seqChannel, nil
}

func sequenceDealer(idInput chan *icmp.Packet, seqChan map[uint16](chan *icmp.Packet), terminate chan bool) {
	// TODO verify IPs
	for {
		select {
		case <-terminate:
			//			/*logs*/logs.Info.Println("Terminating seq dealer")
			return
		case packet := <-idInput:
			// /*logs*/logs.Info.Println("icmp in =", packet.Header.Opt)
			seqNum := uint16(packet.Header.Opt)
			if _, ok := seqChan[seqNum]; ok { // FIXME data race
				seqChan[seqNum] <- packet
			} else {
				/*logs*/ logs.Info.Println("Dropping bad seq num packet with existing identifier")
			}
		}
	}
}

const FLOOD_INTERVAL = 0

func (pm *Ping_Manager) SendPing(ip *ipv4.Address, interval, timeout time.Duration, numPings uint16) error {
	terminate := make(chan bool)
	id, seqChannel, err := pm.initIdentifier(terminate)
	if err != nil {
		logs.Error.Println(err)
		return err
	}
	defer func() { terminate <- true }()

	// get ip writer
	writer, err := pm.getIP_Writer(ip)
	if err != nil {
		logs.Error.Println(err)
		return err
	}

	for i := uint16(1); i <= numPings; i++ {
		seqChannel[i] = make(chan *icmp.Packet) // FIXME data race

		sendSinglePing(writer, id, i, timeout, seqChannel[i]) // function is non-blocking

		// not last
		if i != numPings {
			time.Sleep(interval)
		}
	}

	time.Sleep(timeout)
	return nil
}
<commit_msg>Fixed a spelling error<commit_after>package ping

import (
	"bytes"
	"time"

	"github.com/hsheth2/gonet/icmp"

	"github.com/hsheth2/gonet/ipv4"

	"github.com/hsheth2/logs"
)

const DATA_56_BYTES = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd"

func (pm *Ping_Manager) ping_response_dealer() {
	for {
		ping := <-pm.reply
		identNum := uint16(ping.Header.Opt >> 16)
		if _, ok := pm.identifiers[identNum]; !ok {
			/*logs*/ logs.Info.Println("Dropped something from response dealer, identnum=", identNum, "options=", ping.Header.Opt)
			continue
		}
		pm.identifiers[identNum] <- ping
	}
}

func sendSinglePing(writer ipv4.Writer, id, seq uint16, timeout time.Duration, reply chan *icmp.Packet) {
	// prepare packet
	packet := &icmp.Header{
		Tp:   icmp.EchoRequest,
		Code: PING_ICMP_CODE,
		Opt:  uint32(id)<<16 | uint32(seq),
		Data: []byte(DATA_56_BYTES), // TODO make legit by putting the timestamp in the data
	}

	// make data
	icmp.SendPacket(writer, packet)

	time1 := time.Now()
	timeoutTimer := time.NewTimer(timeout)
	go func(seqChan chan *icmp.Packet, header *icmp.Header, time1 *time.Time, timer *time.Timer) {
		defer close(seqChan)
		for {
			select {
			case pingResponse := <-seqChan:
				if !bytes.Equal(pingResponse.Header.Data, header.Data) {
					/*logs*/ logs.Info.Println("Dropped packet because header data not equal to ping sent")
					continue
				}
				time2 := time.Now()
				logs.Info.Printf("%d bytes from %v: icmp_seq=%d time=%f ms",
					len(header.Data)+icmp.HeaderMinSize,
					pingResponse.RIP.IP,
					uint16(header.Opt),
					float32(time2.Sub(*time1).Nanoseconds())/1000000) // put ttl
				return
			case <-timer.C:
				logs.Info.Println("Seq num of", uint16(header.Opt), "timed out")
				return
			}
		}
	}(reply, packet, &time1, timeoutTimer)
}

func (pm *Ping_Manager) initIdentifier(terminate chan bool) (id uint16, seqChannel map[uint16](chan *icmp.Packet), err error) {
	// get identifier
	pm.currentIdentifier++
	id = pm.currentIdentifier

	// setup sequence number dealer
	pm.identifiers[id] = make(chan *icmp.Packet)
	seqChannel = make(map[uint16](chan *icmp.Packet))

	// create go routine function to deal packets
	go sequenceDealer(pm.identifiers[id], seqChannel, terminate)

	return id, seqChannel, nil
}

func sequenceDealer(idInput chan *icmp.Packet, seqChan map[uint16](chan *icmp.Packet), terminate chan bool) {
	// TODO verify IPs
	for {
		select {
		case <-terminate:
			//			/*logs*/logs.Info.Println("Terminating seq dealer")
			return
		case packet := <-idInput:
			// /*logs*/logs.Info.Println("icmp in =", packet.Header.Opt)
			seqNum := uint16(packet.Header.Opt)
			if _, ok := seqChan[seqNum]; ok { // FIXME data race
				seqChan[seqNum] <- packet
			} else {
				/*logs*/ logs.Info.Println("Dropping bad seq num packet with existing identifier")
			}
		}
	}
}

const FLOOD_INTERVAL = 0

func (pm *Ping_Manager) SendPing(ip *ipv4.Address, interval, timeout time.Duration, numPings uint16) error {
	terminate := make(chan bool)
	id, seqChannel, err := pm.initIdentifier(terminate)
	if err != nil {
		logs.Error.Println(err)
		return err
	}
	defer func() { terminate <- true }()

	// get ip writer
	writer, err := pm.getIP_Writer(ip)
	if err != nil {
		logs.Error.Println(err)
		return err
	}

	for i := uint16(1); i <= numPings; i++ {
		seqChannel[i] = make(chan *icmp.Packet) // FIXME data race

		sendSinglePing(writer, id, i, timeout, seqChannel[i]) // function is non-blocking

		// not last
		if i != numPings {
			time.Sleep(interval)
		}
	}

	time.Sleep(timeout)
	return nil
}
<|endoftext|>


<commit_before>package git_pipeline_test

import (
	"github.com/concourse/testflight/gitserver"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("We shouldn't reclone cached resources", func() {
	var originGitServer *gitserver.Server
	var cachedGitServer *gitserver.Server

	BeforeEach(func() {
		originGitServer = gitserver.Start(gitServerRootfs, gardenClient)
		cachedGitServer = gitserver.Start(gitServerRootfs, gardenClient)

		configurePipeline(
			"-c", "fixtures/caching.yml",
			"-v", "origin-git-server="+originGitServer.URI(),
			"-v", "cached-git-server="+cachedGitServer.URI(),
		)
		originGitServer.Commit()
		cachedGitServer.Commit()
	})

	AfterEach(func() {
		originGitServer.Stop()
		cachedGitServer.Stop()
	})

	FIt("does not reclone on new commits", func() {
		// We expect to see cloning twice - once for each resource
		watch := flyWatch("some-passing-job", "1")
		Ω(watch).Should(gbytes.Say("Cloning into"))
		Ω(watch).Should(gbytes.Say("Cloning into"))
		Ω(watch).Should(gbytes.Say("succeeded"))
		Ω(watch).Should(gexec.Exit(0))

		originGitServer.Commit()

		// The second time we only expect to clone one resource
		watch = flyWatch("some-passing-job", "2")
		Ω(watch).Should(gbytes.Say("Cloning into"))
		Ω(watch).ShouldNot(gbytes.Say("Cloning into"))
		Ω(watch).Should(gbytes.Say("using version of resource found in cache"))
		Ω(watch).Should(gbytes.Say("succeeded"))
		Ω(watch).Should(gexec.Exit(0))
	})
})
<commit_msg>un-F<commit_after>package git_pipeline_test

import (
	"github.com/concourse/testflight/gitserver"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("We shouldn't reclone cached resources", func() {
	var originGitServer *gitserver.Server
	var cachedGitServer *gitserver.Server

	BeforeEach(func() {
		originGitServer = gitserver.Start(gitServerRootfs, gardenClient)
		cachedGitServer = gitserver.Start(gitServerRootfs, gardenClient)

		configurePipeline(
			"-c", "fixtures/caching.yml",
			"-v", "origin-git-server="+originGitServer.URI(),
			"-v", "cached-git-server="+cachedGitServer.URI(),
		)
		originGitServer.Commit()
		cachedGitServer.Commit()
	})

	AfterEach(func() {
		originGitServer.Stop()
		cachedGitServer.Stop()
	})

	It("does not reclone on new commits", func() {
		// We expect to see cloning twice - once for each resource
		watch := flyWatch("some-passing-job", "1")
		Ω(watch).Should(gbytes.Say("Cloning into"))
		Ω(watch).Should(gbytes.Say("Cloning into"))
		Ω(watch).Should(gbytes.Say("succeeded"))
		Ω(watch).Should(gexec.Exit(0))

		originGitServer.Commit()

		// The second time we only expect to clone one resource
		watch = flyWatch("some-passing-job", "2")
		Ω(watch).Should(gbytes.Say("Cloning into"))
		Ω(watch).ShouldNot(gbytes.Say("Cloning into"))
		Ω(watch).Should(gbytes.Say("using version of resource found in cache"))
		Ω(watch).Should(gbytes.Say("succeeded"))
		Ω(watch).Should(gexec.Exit(0))
	})
})
<|endoftext|>


<commit_before>/*
Copyright 2020 Google LLC

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

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

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

package automation

import (
	"context"

	"golang.org/x/oauth2"
	"google.golang.org/api/cloudresourcemanager/v1"
	"google.golang.org/api/compute/v1"
	"google.golang.org/api/option"
	"google.golang.org/api/recommender/v1"
	"google.golang.org/api/serviceusage/v1"
)

// GoogleService is the inferface that prodives methods required to list recommendations and apply them
type GoogleService interface {
	// changes the machine type of an instance
	ChangeMachineType(project, zone, instance, machineType string) error

	// creates a snapshot of a disk
	CreateSnapshot(project, zone, disk, name string) error

	// deletes persistent disk
	DeleteDisk(project, zone, disk string) error

	// gets the specified instance resource
	GetInstance(project string, zone string, instance string) (*compute.Instance, error)

	// lists whether the requirements have been met for all APIs (APIs enabled).
	ListAPIRequirements(project string, apis []string) ([]*Requirement, error)

	// lists whether the requirements have been met for all required permissions.
	ListPermissionRequirements(project string, permissions [][]string) ([]*Requirement, error)

	// lists projects
	ListProjects() ([]string, error)

	// listing recommendations for specified project, zone and recommender
	ListRecommendations(project, location, recommenderID string) ([]*gcloudRecommendation, error)

	// listing every zone available for the project methods
	ListZonesNames(project string) ([]string, error)

	// listing every region available for the project methods
	ListRegionsNames(project string) ([]string, error)

	// stops the specified instance
	StopInstance(project, zone, instance string) error
}

// googleService implements GoogleService interface for Recommender and Compute APIs.
type googleService struct {
	ctx                    context.Context
	computeService         *compute.Service
	recommenderService     *recommender.Service
	resourceManagerService *cloudresourcemanager.Service
	serviceUsageService    *serviceusage.Service
}

// NewGoogleService creates new googleServices.
// If creation failed the error will be non-nil.
func NewGoogleService(ctx context.Context, conf *oauth2.Config, tok *oauth2.Token) (GoogleService, error) {
	client := conf.Client(ctx, tok)
	computeService, err := compute.NewService(ctx, option.WithHTTPClient(client))
	if err != nil {
		return nil, err
	}

	recommenderService, err := recommender.NewService(ctx)
	if err != nil {
		return nil, err
	}

	resourceManagerService, err := cloudresourcemanager.NewService(ctx)
	if err != nil {
		return nil, err
	}

	serviceUsageService, err := serviceusage.NewService(ctx)
	if err != nil {
		return nil, err
	}

	return &googleService{
		ctx:                    ctx,
		computeService:         computeService,
		recommenderService:     recommenderService,
		resourceManagerService: resourceManagerService,
		serviceUsageService:    serviceUsageService,
	}, nil
}
<commit_msg>fix authorization<commit_after>/*
Copyright 2020 Google LLC

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

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

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

package automation

import (
	"context"

	"golang.org/x/oauth2"
	"google.golang.org/api/cloudresourcemanager/v1"
	"google.golang.org/api/compute/v1"
	"google.golang.org/api/option"
	"google.golang.org/api/recommender/v1"
	"google.golang.org/api/serviceusage/v1"
)

// GoogleService is the inferface that prodives methods required to list recommendations and apply them
type GoogleService interface {
	// changes the machine type of an instance
	ChangeMachineType(project, zone, instance, machineType string) error

	// creates a snapshot of a disk
	CreateSnapshot(project, zone, disk, name string) error

	// deletes persistent disk
	DeleteDisk(project, zone, disk string) error

	// gets the specified instance resource
	GetInstance(project string, zone string, instance string) (*compute.Instance, error)

	// lists whether the requirements have been met for all APIs (APIs enabled).
	ListAPIRequirements(project string, apis []string) ([]*Requirement, error)

	// lists whether the requirements have been met for all required permissions.
	ListPermissionRequirements(project string, permissions [][]string) ([]*Requirement, error)

	// lists projects
	ListProjects() ([]string, error)

	// listing recommendations for specified project, zone and recommender
	ListRecommendations(project, location, recommenderID string) ([]*gcloudRecommendation, error)

	// listing every zone available for the project methods
	ListZonesNames(project string) ([]string, error)

	// listing every region available for the project methods
	ListRegionsNames(project string) ([]string, error)

	// stops the specified instance
	StopInstance(project, zone, instance string) error
}

// googleService implements GoogleService interface for Recommender and Compute APIs.
type googleService struct {
	ctx                    context.Context
	computeService         *compute.Service
	recommenderService     *recommender.Service
	resourceManagerService *cloudresourcemanager.Service
	serviceUsageService    *serviceusage.Service
}

// NewGoogleService creates new googleServices.
// If creation failed the error will be non-nil.
func NewGoogleService(ctx context.Context, conf *oauth2.Config, tok *oauth2.Token) (GoogleService, error) {
	client := conf.Client(ctx, tok)
	clientOption := option.WithHTTPClient(client)
	computeService, err := compute.NewService(ctx, clientOption)
	if err != nil {
		return nil, err
	}

	recommenderService, err := recommender.NewService(ctx, clientOption)
	if err != nil {
		return nil, err
	}

	resourceManagerService, err := cloudresourcemanager.NewService(ctx, clientOption)
	if err != nil {
		return nil, err
	}

	serviceUsageService, err := serviceusage.NewService(ctx, clientOption)
	if err != nil {
		return nil, err
	}

	return &googleService{
		ctx:                    ctx,
		computeService:         computeService,
		recommenderService:     recommenderService,
		resourceManagerService: resourceManagerService,
		serviceUsageService:    serviceUsageService,
	}, nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"io"
	"os"

	"github.com/spf13/cobra"

	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)

var (
	internalTYPELong = templates.LongDesc(`
		Single line title

		Description body`)

	internalTYPEExample = templates.Examples(`%s`)
)

type TYPEOptions struct {
	In          io.Reader
	Out, ErrOut io.Writer
}

// NewCmdTYPE implements a TYPE command
// This is an example type for templating.
func NewCmdTYPE(fullName string, f kcmdutil.Factory, in io.Reader, out, errout io.Writer) *cobra.Command {
	options := &TYPEOptions{
		In:     in,
		Out:    out,
		ErrOut: errout,
	}
	cmd := &cobra.Command{
		Use:     "NAME [...]",
		Short:   "A short description",
		Long:    internalTYPELong,
		Example: fmt.Sprintf(internalTYPEExample, fullName),
		Run: func(cmd *cobra.Command, args []string) {
			kcmdutil.CheckErr(options.Complete(f, cmd, args))
			kcmdutil.CheckErr(options.Validate())
			if err := options.Run(); err != nil {
				// TODO: move me to kcmdutil
				if err == kcmdutil.ErrExit {
					os.Exit(1)
				}
				kcmdutil.CheckErr(err)
			}
		},
	}
	return cmd
}

func (o *TYPEOptions) Complete(f kcmdutil.Factory, c *cobra.Command, args []string) error {
	return nil
}

func (o *TYPEOptions) Validate() error { return nil }
func (o *TYPEOptions) Run() error      { return nil }
<commit_msg>Update example<commit_after>package cmd

import (
	"fmt"

	"github.com/spf13/cobra"

	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"

	"github.com/openshift/origin/pkg/oc/util/ocscheme"
)

var (
	internalTYPELong = templates.LongDesc(`
		Single line title

		Description body`)

	internalTYPEExample = templates.Examples(`%s`)
)

type TYPEOptions struct {
	PrintFlags *genericclioptions.PrintFlags

	Printer printers.ResourcePrinter
	DryRun  bool

	genericclioptions.IOStreams
}

// NewTYPEOptions returns a TYPEOptions with proper defaults.
// This is an example type for templating.
func NewTYPEOptions(streams genericclioptions.IOStreams) *TYPEOptions {
	return &TYPEOptions{
		PrintFlags: genericclioptions.NewPrintFlags("action performed").WithTypeSetter(ocscheme.PrintingInternalScheme),
		IOStreams:  streams,
	}
}

// NewCmdTYPE implements a TYPE command
// This is an example type for templating.
func NewCmdTYPE(fullName string, f kcmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
	o := NewTYPEOptions(streams)
	cmd := &cobra.Command{
		Use:     "NAME [...]",
		Short:   "A short description",
		Long:    internalTYPELong,
		Example: fmt.Sprintf(internalTYPEExample, fullName),
		Run: func(cmd *cobra.Command, args []string) {
			kcmdutil.CheckErr(o.Complete(f, cmd, args))
			kcmdutil.CheckErr(o.Validate())
			kcmdutil.CheckErr(o.Run())
		},
	}

	o.PrintFlags.AddFlags(cmd)
	kcmdutil.AddDryRunFlag(cmd)

	return cmd
}

func (o *TYPEOptions) Complete(f kcmdutil.Factory, cmd *cobra.Command, args []string) error {
	var err error
	o.DryRun = kcmdutil.GetDryRunFlag(cmd)
	if o.DryRun {
		o.PrintFlags.Complete("%s (dry run)")
	}
	o.Printer, err = o.PrintFlags.ToPrinter()
	if err != nil {
		return err
	}

	return nil
}

func (o *TYPEOptions) Validate() error { return nil }
func (o *TYPEOptions) Run() error      { return nil }
<|endoftext|>


<commit_before>package provisioning

import (
	log "github.com/Sirupsen/logrus"
	"os/exec"
	"github.com/intelsdi-x/swan/pkg/isolation"
	"time"
	"syscall"
	"bytes"
)

// LocalTask implements Task interface.
type LocalTask struct{
	pid isolation.TaskPID
	statusCh chan Status
	status Status
	terminated bool
}

// NewLocalTask returns a LocalTask instance.
func NewLocalTask(pid isolation.TaskPID, statusCh chan Status) *LocalTask {
	t := &LocalTask{
		pid,
		statusCh,
		Status{},
		false,
	}
	return t
}

func (task *LocalTask) completeTask(status Status) {
	task.terminated = true
	task.status = status
	task.statusCh = nil
}

// Stop terminates the local task.
func (task *LocalTask) Stop() {
	if (task.terminated) {
		panic("Task is not running.")
	}

	log.Debug("Sending SIGTERM to PID ", -task.pid)
	err := syscall.Kill(-int(task.pid), syscall.SIGTERM)
	if (err != nil) {
		panic(err)
	}

	s := <-task.statusCh
	task.completeTask(s)
}

// Status gets status of the local task.
func (task LocalTask) Status() Status {
	if (!task.terminated) {
		return Status{code: RunningCode}
	}

	return task.status
}

// Wait blocks until process is terminated or timeout appeared.
// Returns true after timeout exceeds.
func (task *LocalTask) Wait(timeoutMs int) bool {
	if (task.terminated) {
		return false
	}

	if (timeoutMs == 0) {
		s := <-task.statusCh
		task.completeTask(s)

	} else {
		timeoutDuration := time.Duration(timeoutMs) * time.Millisecond

		select {
		case s := <-task.statusCh:
			task.completeTask(s)
		case <-time.After(timeoutDuration):
			return true
		}
	}

	return false
}

// Local provisioning is responsible for providing the execution environment
// on local machine via exec.Command. It also needed to setup given isolation
// using Isolation Manager.
// It runs command as current user.
type Local struct{
	isolations []isolation.ProcessIsolation

	// It is important to set additional PGID for parent process and his children
	// to have ability to kill all the children processes.
	setPGID bool
}

// NewLocal returns a Local instance.
func NewLocal(isolations []isolation.ProcessIsolation) Local {
	l := Local{
		isolations: isolations,
		setPGID: true,
	}
	return l
}

// Run runs the command given as input.
// Returned Task pointer is able to stop & monitor the provisioned process.
func (l Local) Run(command string) (Task) {
	statusCh := make(chan Status)

	log.Debug("Starting ", command)

	cmd := exec.Command("sh", "-c", command)

	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: l.setPGID}

	// Setting Buffer as io.Writer for Command output.
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	err := cmd.Start()

	if (err != nil) {
		panic(err)
	}

	log.Debug("Started with pid ", cmd.Process.Pid)

	// Wait for local task in goroutine.
	go func() {
		// Wait for task completion.
		cmd.Wait()

		log.Debug(
		  "Ended ", command,
		  " with output: ", stdout.String(),
		  " with err output: ", stderr.String(),
		  " with status code: ",
		  (cmd.ProcessState.Sys().(syscall.WaitStatus)).ExitStatus())

		statusCh <- Status{
			(cmd.ProcessState.Sys().(syscall.WaitStatus)).ExitStatus(),
			stdout.String(),
			stderr.String(),
		}
	}()

	taskPid := isolation.TaskPID(cmd.Process.Pid)

	// Perform rest of the isolation synchronously.
	for _, isolation := range l.isolations {
		isolation.Isolate(taskPid)
	}

	t := NewLocalTask(taskPid, statusCh)

	return t
}
<commit_msg>Refactored comment.<commit_after>package provisioning

import (
	log "github.com/Sirupsen/logrus"
	"os/exec"
	"github.com/intelsdi-x/swan/pkg/isolation"
	"time"
	"syscall"
	"bytes"
)

// LocalTask implements Task interface.
type LocalTask struct{
	pid isolation.TaskPID
	statusCh chan Status
	status Status
	terminated bool
}

// NewLocalTask returns a LocalTask instance.
func NewLocalTask(pid isolation.TaskPID, statusCh chan Status) *LocalTask {
	t := &LocalTask{
		pid,
		statusCh,
		Status{},
		false,
	}
	return t
}

func (task *LocalTask) completeTask(status Status) {
	task.terminated = true
	task.status = status
	task.statusCh = nil
}

// Stop terminates the local task.
func (task *LocalTask) Stop() {
	if (task.terminated) {
		panic("Task is not running.")
	}

	log.Debug("Sending SIGTERM to PID ", -task.pid)
	err := syscall.Kill(-int(task.pid), syscall.SIGTERM)
	if (err != nil) {
		panic(err)
	}

	s := <-task.statusCh
	task.completeTask(s)
}

// Status gets status of the local task.
func (task LocalTask) Status() Status {
	if (!task.terminated) {
		return Status{code: RunningCode}
	}

	return task.status
}

// Wait blocks until process is terminated or timeout appeared.
// Returns true after timeout exceeds.
func (task *LocalTask) Wait(timeoutMs int) bool {
	if (task.terminated) {
		return false
	}

	if (timeoutMs == 0) {
		s := <-task.statusCh
		task.completeTask(s)

	} else {
		timeoutDuration := time.Duration(timeoutMs) * time.Millisecond

		select {
		case s := <-task.statusCh:
			task.completeTask(s)
		case <-time.After(timeoutDuration):
			return true
		}
	}

	return false
}

// Local provisioning is responsible for providing the execution environment
// on local machine via exec.Command. It also needed to setup given isolation
// using Isolation Manager.
// It runs command as current user.
type Local struct{
	isolations []isolation.ProcessIsolation

	// It is important to set additional PGID for parent process and his children
	// to have ability to kill all the children processes.
	setPGID bool
}

// NewLocal returns a Local instance.
func NewLocal(isolations []isolation.ProcessIsolation) Local {
	l := Local{
		isolations: isolations,
		setPGID: true,
	}
	return l
}

// Run runs the command given as input.
// Returned Task is able to stop & monitor the provisioned process.
func (l Local) Run(command string) (Task) {
	statusCh := make(chan Status)

	log.Debug("Starting ", command)

	cmd := exec.Command("sh", "-c", command)

	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: l.setPGID}

	// Setting Buffer as io.Writer for Command output.
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	err := cmd.Start()

	if (err != nil) {
		panic(err)
	}

	log.Debug("Started with pid ", cmd.Process.Pid)

	// Wait for local task in goroutine.
	go func() {
		// Wait for task completion.
		cmd.Wait()

		log.Debug(
		  "Ended ", command,
		  " with output: ", stdout.String(),
		  " with err output: ", stderr.String(),
		  " with status code: ",
		  (cmd.ProcessState.Sys().(syscall.WaitStatus)).ExitStatus())

		statusCh <- Status{
			(cmd.ProcessState.Sys().(syscall.WaitStatus)).ExitStatus(),
			stdout.String(),
			stderr.String(),
		}
	}()

	taskPid := isolation.TaskPID(cmd.Process.Pid)

	// Perform rest of the isolation synchronously.
	for _, isolation := range l.isolations {
		isolation.Isolate(taskPid)
	}

	t := NewLocalTask(taskPid, statusCh)

	return t
}
<|endoftext|>


<commit_before>/*-
 * Copyright (c) 2017, F5 Networks, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"appmanager"

	log "f5/vlogger"

	"k8s.io/client-go/pkg/api/v1"
)

// Implementation for the cache.ResourceEventHandler interface for namespaces
type namespaceEventHandler struct {
	appMgr *appmanager.Manager
}

func NewNamespaceEventHandler(
	appMgr *appmanager.Manager,
) *namespaceEventHandler {
	return &namespaceEventHandler{appMgr: appMgr}
}

func (eh *namespaceEventHandler) OnAdd(obj interface{}) {
	var ns *v1.Namespace
	label := "f5type in (virtual-server)"
	ns = obj.(*v1.Namespace)
	namespace := ns.ObjectMeta.Name
	handlers := appmanager.NewEventHandler(eh.appMgr)
	st, err := watchManager.Add(namespace, "configmaps", label, &v1.ConfigMap{}, handlers)
	if nil != err {
		log.Warningf("Failed to add configmaps watch for namespace %v: %v", namespace, err)
		return
	}
	if len(st.ListKeys()) > 0 {
		items := st.List()
		for _, item := range items {
			obj := appmanager.ChangedObject{Old: nil, New: item}
			eh.appMgr.ProcessConfigMapUpdate(0, obj)
		}
	}
}

func (eh *namespaceEventHandler) OnUpdate(oldObj, newObj interface{}) {
	// Unimplemented function
}

func (eh *namespaceEventHandler) OnDelete(obj interface{}) {
	var ns *v1.Namespace
	ns = obj.(*v1.Namespace)
	watchManager.Remove(ns.ObjectMeta.Name, "namespaces")
	eh.appMgr.RemoveNamespace(ns.ObjectMeta.Name)
}
<commit_msg>Namespace watch code crashes due to bad memory access<commit_after>/*-
 * Copyright (c) 2017, F5 Networks, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"appmanager"

	log "f5/vlogger"

	"k8s.io/client-go/pkg/api/v1"
)

// Implementation for the cache.ResourceEventHandler interface for namespaces
type namespaceEventHandler struct {
	appMgr *appmanager.Manager
}

func NewNamespaceEventHandler(
	appMgr *appmanager.Manager,
) *namespaceEventHandler {
	return &namespaceEventHandler{appMgr: appMgr}
}

func (eh *namespaceEventHandler) OnAdd(obj interface{}) {
	var ns *v1.Namespace
	label := "f5type in (virtual-server)"
	ns = obj.(*v1.Namespace)
	namespace := ns.ObjectMeta.Name
	handlers := appmanager.NewEventHandler(eh.appMgr)
	st, err := eh.appMgr.WatchManager().Add(namespace, "configmaps", label, &v1.ConfigMap{}, handlers)
	if nil != err {
		log.Warningf("Failed to add configmaps watch for namespace %v: %v", namespace, err)
		return
	}
	if len(st.ListKeys()) > 0 {
		items := st.List()
		for _, item := range items {
			obj := appmanager.ChangedObject{Old: nil, New: item}
			eh.appMgr.ProcessConfigMapUpdate(0, obj)
		}
	}
}

func (eh *namespaceEventHandler) OnUpdate(oldObj, newObj interface{}) {
	// Unimplemented function
}

func (eh *namespaceEventHandler) OnDelete(obj interface{}) {
	var ns *v1.Namespace
	ns = obj.(*v1.Namespace)
	eh.appMgr.WatchManager().Remove(ns.ObjectMeta.Name, "namespaces")
	eh.appMgr.RemoveNamespace(ns.ObjectMeta.Name)
}
<|endoftext|>


<commit_before>package ntemplate

import (
	"path/filepath"
	"strings"
)

type PathAliases map[string]string

func (p PathAliases) Add(alias, absPath string) PathAliases {
	var err error
	absPath, err = filepath.Abs(absPath)
	if err != nil {
		panic(err)
	}
	p[alias] = absPath
	return p
}

func (p PathAliases) ParsePrefix(withAliasPrefixPath string) string {
	rpath, _ := p.ParsePrefixOk(withAliasPrefixPath)
	return rpath
}

func (p PathAliases) ParsePrefixOk(withAliasPrefixPath string) (string, bool) {
	if len(withAliasPrefixPath) < 3 || withAliasPrefixPath[0] == '/' || withAliasPrefixPath[0] == '.' {
		return withAliasPrefixPath, false
	}
	parts := strings.SplitN(withAliasPrefixPath, `/`, 2)
	if len(parts) != 2 {
		return withAliasPrefixPath, false
	}
	alias := parts[0]
	if opath, ok := p[alias]; ok {
		return filepath.Join(opath, withAliasPrefixPath), true
	}
	return withAliasPrefixPath, false
}

func (p PathAliases) RestorePrefix(fullpath string) string {
	rpath, _ := p.RestorePrefixOk(fullpath)
	return rpath
}

func (p PathAliases) RestorePrefixOk(fullpath string) (string, bool) {
	for _, absPath := range p {
		if strings.HasPrefix(fullpath, absPath) {
			return fullpath[len(absPath):], true
		}
	}
	return fullpath, false
}

func (p PathAliases) Parse(withAliasTagPath string) string {
	rpath, _ := p.ParseOk(withAliasTagPath)
	return rpath
}

func (p PathAliases) ParseOk(withAliasTagPath string) (string, bool) {
	if len(withAliasTagPath) < 3 || withAliasTagPath[0] != '[' {
		return withAliasTagPath, false
	}
	withAliasTagPath = withAliasTagPath[1:]
	parts := strings.SplitN(withAliasTagPath, `]`, 2)
	if len(parts) != 2 {
		return withAliasTagPath, false
	}
	alias := parts[0]
	rpath := parts[1]
	if opath, ok := p[alias]; ok {
		rpath = filepath.Join(opath, rpath)
		return rpath, true
	}
	return rpath, false
}

func (p PathAliases) Restore(fullpath string) string {
	rpath, _ := p.RestoreOk(fullpath)
	return rpath
}

func (p PathAliases) RestoreOk(fullpath string) (string, bool) {
	for alias, absPath := range p {
		if strings.HasPrefix(fullpath, absPath) {
			return `[` + alias + `]` + fullpath[len(absPath):], true
		}
	}
	return fullpath, false
}
<commit_msg>update<commit_after>package ntemplate

import (
	"path/filepath"
	"strings"
)

type PathAliases map[string]string

func (p PathAliases) Add(alias, absPath string) PathAliases {
	var err error
	absPath, err = filepath.Abs(absPath)
	if err != nil {
		panic(err)
	}
	p[alias] = absPath
	return p
}

func (p PathAliases) ParsePrefix(withAliasPrefixPath string) string {
	rpath, _ := p.ParsePrefixOk(withAliasPrefixPath)
	return rpath
}

func (p PathAliases) ParsePrefixOk(withAliasPrefixPath string) (string, bool) {
	if len(withAliasPrefixPath) < 3 || withAliasPrefixPath[0] == '/' || withAliasPrefixPath[0] == '.' {
		return withAliasPrefixPath, false
	}
	parts := strings.SplitN(withAliasPrefixPath, `/`, 2)
	if len(parts) != 2 {
		return withAliasPrefixPath, false
	}
	alias := parts[0]
	if opath, ok := p[alias]; ok {
		return filepath.Join(opath, withAliasPrefixPath), true
	}
	return withAliasPrefixPath, false
}

func (p PathAliases) RestorePrefix(fullpath string) string {
	rpath, _ := p.RestorePrefixOk(fullpath)
	return rpath
}

func (p PathAliases) RestorePrefixOk(fullpath string) (string, bool) {
	for _, absPath := range p {
		if strings.HasPrefix(fullpath, absPath) {
			return filepath.ToSlash(fullpath[len(absPath):]), true
		}
	}
	return fullpath, false
}

func (p PathAliases) Parse(withAliasTagPath string) string {
	rpath, _ := p.ParseOk(withAliasTagPath)
	return rpath
}

func (p PathAliases) ParseOk(withAliasTagPath string) (string, bool) {
	if len(withAliasTagPath) < 3 || withAliasTagPath[0] != '[' {
		return withAliasTagPath, false
	}
	withAliasTagPath = withAliasTagPath[1:]
	parts := strings.SplitN(withAliasTagPath, `]`, 2)
	if len(parts) != 2 {
		return withAliasTagPath, false
	}
	alias := parts[0]
	rpath := parts[1]
	if opath, ok := p[alias]; ok {
		rpath = filepath.Join(opath, rpath)
		return rpath, true
	}
	return rpath, false
}

func (p PathAliases) Restore(fullpath string) string {
	rpath, _ := p.RestoreOk(fullpath)
	return rpath
}

func (p PathAliases) RestoreOk(fullpath string) (string, bool) {
	for alias, absPath := range p {
		if strings.HasPrefix(fullpath, absPath) {
			return `[` + alias + `]` + filepath.ToSlash(fullpath[len(absPath):]), true
		}
	}
	return fullpath, false
}
<|endoftext|>


<commit_before>package build

import (
	"log"
	"os"
	"path/filepath"

	"github.com/davecheney/gogo"
)

// future implements a gogo.Future
type future struct {
	err chan error
}

func (f *future) Result() error {
	result := <-f.err
	f.err <- result
	return result
}

// Build returns a Future representing the result of compiling the
// package pkg, and its dependencies.
// If pkg is a a command, then the results of build include linking
// the final binary into pkg.Context.Bindir().
func Build(pkg *gogo.Package) gogo.Future {
	if pkg.Name() == "main" {
		return buildCommand(pkg)
	}
	return buildPackage(pkg)
}

// buildPackage returns a Future repesenting the results of compiling
// pkg and its dependencies.
func buildPackage(pkg *gogo.Package) gogo.Future {
	var deps []gogo.Future
	for _, dep := range pkg.Imports {
		deps = append(deps, buildPackage(dep))
	}
	if _, ok := pkg.Context.Targets[pkg]; !ok {
		compile := compile(pkg, deps, false)
		pkg.Context.Targets[pkg] = compile
	}
	return pkg.Context.Targets[pkg]
}

// buildCommand returns a Future repesenting the results of compiling
// pkg as a command and linking the result into pkg.Context.Bindir().
func buildCommand(pkg *gogo.Package) gogo.Future {
	var deps []gogo.Future
	for _, dep := range pkg.Imports {
		deps = append(deps, buildPackage(dep))
	}
	compile := compile(pkg, deps, false)
	ld := Ld(pkg, compile)
	return ld
}

// compile is a helper which combines all the steps required
// to build a go package
func compile(pkg *gogo.Package, deps []gogo.Future, includeTests bool) gogo.Future {
	var gofiles []string
	gofiles = append(gofiles, pkg.GoFiles...)
	var objs []objFuture
	if len(pkg.CgoFiles) > 0 {
		cgo, cgofiles := cgo(pkg, deps)
		deps = append(deps, cgo[0])
		objs = append(objs, cgo...)
		gofiles = append(gofiles, cgofiles...)
	}
	if includeTests {
		gofiles = append(gofiles, pkg.TestGoFiles...)
	}
	objs = append(objs, Gc(pkg, deps, gofiles))
	for _, sfile := range pkg.SFiles {
		objs = append(objs, Asm(pkg, sfile))
	}
	pack := Pack(pkg, objs)
	return pack
}

// objFuture represents a Future that produces an object file.
type objFuture interface {
	gogo.Future

	// objfile returns the name of the file that is
	// produced by the Future if successful.
	objfile() string
}

// pkgFuture represents a Future that produces a pkg (.a) file.
type pkgFuture interface {
	gogo.Future

	// pkgfile returns the name of the file that is
	// produced by the Future if successful.
	pkgfile() string
}

type packTarget struct {
	future
	deps     []objFuture
	objfiles []string
	*gogo.Package
}

// Pack returns a Future representing the result of packing a
// set of Context specific object files into an archive.
func Pack(pkg *gogo.Package, deps []objFuture) pkgFuture {
	t := &packTarget{
		future: future{
			err: make(chan error, 1),
		},
		deps:    deps,
		Package: pkg,
	}
	go t.execute()
	return t
}

func (t *packTarget) execute() {
	for _, dep := range t.deps {
		if err := dep.Result(); err != nil {
			t.future.err <- err
			return
		}
		// collect successful objfiles for packing
		t.objfiles = append(t.objfiles, dep.objfile())
	}
	log.Printf("pack %q: %s", t.Package.ImportPath(), t.objfiles)
	t.future.err <- t.build()
}

func (t *packTarget) pkgfile() string { return t.Package.ImportPath() + ".a" }

func (t *packTarget) build() error {
	ofile := t.pkgfile()
	pkgdir := filepath.Dir(filepath.Join(t.Pkgdir(), ofile))
	if err := os.MkdirAll(pkgdir, 0777); err != nil {
		return err
	}
	return t.Pack(ofile, t.Pkgdir(), t.objfiles...)
}

type gcTarget struct {
	future
	deps    []gogo.Future
	gofiles []string
	*gogo.Package
}

func (t *gcTarget) execute() {
	for _, dep := range t.deps {
		if err := dep.Result(); err != nil {
			t.future.err <- err
			return
		}
	}
	log.Printf("gc %q: %s", t.Package.ImportPath(), t.gofiles)
	t.future.err <- t.build()
}

// Gc returns a Future representing the result of compiling a
// set of gofiles with the Context specified gc compiler.
func Gc(pkg *gogo.Package, deps []gogo.Future, gofiles []string) objFuture {
	t := &gcTarget{
		future: future{
			err: make(chan error, 1),
		},
		deps:    deps,
		gofiles: gofiles,
		Package: pkg,
	}
	go t.execute()
	return t
}

func (t *gcTarget) objfile() string { return filepath.Join(t.Objdir(), "_go_.6") }

func (t *gcTarget) build() error {
	if err := os.MkdirAll(t.Objdir(), 0777); err != nil {
		return err
	}
	return t.Gc(t.ImportPath(), t.Srcdir(), t.objfile(), t.gofiles)
}

type asmTarget struct {
	future
	sfile string
	*gogo.Package
}

func (t *asmTarget) execute() {
	log.Printf("as %q: %s", t.Package.ImportPath(), t.sfile)
	t.future.err <- t.build()
}

// Asm returns a Future representing the result of assembling
// sfile with the Context specified asssembler.
func Asm(pkg *gogo.Package, sfile string) objFuture {
	t := &asmTarget{
		future: future{
			err: make(chan error, 1),
		},
		sfile:   sfile,
		Package: pkg,
	}
	go t.execute()
	return t
}

func (t *asmTarget) objfile() string {
	return filepath.Join(t.Objdir(), t.sfile[:len(t.sfile)-len(".s")]+".6")
}

func (t *asmTarget) build() error {
	if err := os.MkdirAll(t.Objdir(), 0777); err != nil {
		return err
	}
	return t.Asm(t.Srcdir(), t.objfile(), t.sfile)
}

type ldTarget struct {
	future
	deps []gogo.Future
	*gogo.Package
}

func (t *ldTarget) execute() {
	for _, dep := range t.deps {
		if err := dep.Result(); err != nil {
			t.future.err <- err
			return
		}
	}
	log.Printf("ld %q", t.Package.ImportPath())
	t.future.err <- t.build()
}

// Ld returns a Future representing the result of linking a
// Package into a command with the Context provided linker.
func Ld(pkg *gogo.Package, deps ...gogo.Future) gogo.Future {
	t := &ldTarget{
		future: future{
			err: make(chan error, 1),
		},
		deps:    deps,
		Package: pkg,
	}
	go t.execute()
	return &t.future
}

func (t *ldTarget) pkgfile() string { return filepath.Join(t.Workdir(), t.Package.ImportPath()+".a") }

func (t *ldTarget) build() error {
	bindir := t.Package.Context.Bindir()
	if err := os.MkdirAll(bindir, 0777); err != nil {
		return err
	}
	return t.Ld(filepath.Join(bindir, filepath.Base(t.Package.ImportPath())), t.pkgfile())
}
<commit_msg>added more documentation<commit_after>package build

import (
	"log"
	"os"
	"path/filepath"

	"github.com/davecheney/gogo"
)

// future implements a gogo.Future
type future struct {
	err chan error
}

func (f *future) Result() error {
	result := <-f.err
	f.err <- result
	return result
}

// Build returns a Future representing the result of compiling the
// package pkg, and its dependencies.
// If pkg is a command, then the results of build include linking
// the final binary into pkg.Context.Bindir().
func Build(pkg *gogo.Package) gogo.Future {
	if pkg.Name() == "main" {
		return buildCommand(pkg)
	}
	return buildPackage(pkg)
}

// buildPackage returns a Future repesenting the results of compiling
// pkg and its dependencies.
func buildPackage(pkg *gogo.Package) gogo.Future {
	var deps []gogo.Future
	for _, dep := range pkg.Imports {
		deps = append(deps, buildPackage(dep))
	}
	if _, ok := pkg.Context.Targets[pkg]; !ok {
		compile := compile(pkg, deps, false)
		pkg.Context.Targets[pkg] = compile
	}
	return pkg.Context.Targets[pkg]
}

// buildCommand returns a Future repesenting the results of compiling
// pkg as a command and linking the result into pkg.Context.Bindir().
func buildCommand(pkg *gogo.Package) gogo.Future {
	var deps []gogo.Future
	for _, dep := range pkg.Imports {
		deps = append(deps, buildPackage(dep))
	}
	compile := compile(pkg, deps, false)
	ld := Ld(pkg, compile)
	return ld
}

// compile is a helper which combines all the steps required
// to build a go package
func compile(pkg *gogo.Package, deps []gogo.Future, includeTests bool) gogo.Future {
	var gofiles []string
	gofiles = append(gofiles, pkg.GoFiles...)
	var objs []objFuture
	if len(pkg.CgoFiles) > 0 {
		cgo, cgofiles := cgo(pkg, deps)
		deps = append(deps, cgo[0])
		objs = append(objs, cgo...)
		gofiles = append(gofiles, cgofiles...)
	}
	if includeTests {
		gofiles = append(gofiles, pkg.TestGoFiles...)
	}
	objs = append(objs, Gc(pkg, deps, gofiles))
	for _, sfile := range pkg.SFiles {
		objs = append(objs, Asm(pkg, sfile))
	}
	pack := Pack(pkg, objs)
	return pack
}

// objFuture represents a Future that produces an object file.
type objFuture interface {
	gogo.Future

	// objfile returns the name of the file that is
	// produced by the Future if successful.
	objfile() string
}

// pkgFuture represents a Future that produces a pkg (.a) file.
type pkgFuture interface {
	gogo.Future

	// pkgfile returns the name of the file that is
	// produced by the Future if successful.
	pkgfile() string
}

type packTarget struct {
	future
	deps     []objFuture
	objfiles []string
	*gogo.Package
}

// Pack returns a Future representing the result of packing a
// set of Context specific object files into an archive.
func Pack(pkg *gogo.Package, deps []objFuture) pkgFuture {
	t := &packTarget{
		future: future{
			err: make(chan error, 1),
		},
		deps:    deps,
		Package: pkg,
	}
	go t.execute()
	return t
}

func (t *packTarget) execute() {
	for _, dep := range t.deps {
		if err := dep.Result(); err != nil {
			t.future.err <- err
			return
		}
		// collect successful objfiles for packing
		t.objfiles = append(t.objfiles, dep.objfile())
	}
	log.Printf("pack %q: %s", t.Package.ImportPath(), t.objfiles)
	t.future.err <- t.build()
}

func (t *packTarget) pkgfile() string { return t.Package.ImportPath() + ".a" }

func (t *packTarget) build() error {
	ofile := t.pkgfile()
	pkgdir := filepath.Dir(filepath.Join(t.Pkgdir(), ofile))
	if err := os.MkdirAll(pkgdir, 0777); err != nil {
		return err
	}
	return t.Pack(ofile, t.Pkgdir(), t.objfiles...)
}

type gcTarget struct {
	future
	deps    []gogo.Future
	gofiles []string
	*gogo.Package
}

func (t *gcTarget) execute() {
	for _, dep := range t.deps {
		if err := dep.Result(); err != nil {
			t.future.err <- err
			return
		}
	}
	log.Printf("gc %q: %s", t.Package.ImportPath(), t.gofiles)
	t.future.err <- t.build()
}

// Gc returns a Future representing the result of compiling a
// set of gofiles with the Context specified gc compiler.
func Gc(pkg *gogo.Package, deps []gogo.Future, gofiles []string) objFuture {
	t := &gcTarget{
		future: future{
			err: make(chan error, 1),
		},
		deps:    deps,
		gofiles: gofiles,
		Package: pkg,
	}
	go t.execute()
	return t
}

func (t *gcTarget) objfile() string { return filepath.Join(t.Objdir(), "_go_.6") }

func (t *gcTarget) build() error {
	if err := os.MkdirAll(t.Objdir(), 0777); err != nil {
		return err
	}
	return t.Gc(t.ImportPath(), t.Srcdir(), t.objfile(), t.gofiles)
}

type asmTarget struct {
	future
	sfile string
	*gogo.Package
}

func (t *asmTarget) execute() {
	log.Printf("as %q: %s", t.Package.ImportPath(), t.sfile)
	t.future.err <- t.build()
}

// Asm returns a Future representing the result of assembling
// sfile with the Context specified asssembler.
func Asm(pkg *gogo.Package, sfile string) objFuture {
	t := &asmTarget{
		future: future{
			err: make(chan error, 1),
		},
		sfile:   sfile,
		Package: pkg,
	}
	go t.execute()
	return t
}

func (t *asmTarget) objfile() string {
	return filepath.Join(t.Objdir(), t.sfile[:len(t.sfile)-len(".s")]+".6")
}

func (t *asmTarget) build() error {
	if err := os.MkdirAll(t.Objdir(), 0777); err != nil {
		return err
	}
	return t.Asm(t.Srcdir(), t.objfile(), t.sfile)
}

type ldTarget struct {
	future
	deps []gogo.Future
	*gogo.Package
}

func (t *ldTarget) execute() {
	for _, dep := range t.deps {
		if err := dep.Result(); err != nil {
			t.future.err <- err
			return
		}
	}
	log.Printf("ld %q", t.Package.ImportPath())
	t.future.err <- t.build()
}

// Ld returns a Future representing the result of linking a
// Package into a command with the Context provided linker.
func Ld(pkg *gogo.Package, deps ...gogo.Future) gogo.Future {
	t := &ldTarget{
		future: future{
			err: make(chan error, 1),
		},
		deps:    deps,
		Package: pkg,
	}
	go t.execute()
	return &t.future
}

func (t *ldTarget) pkgfile() string { return filepath.Join(t.Workdir(), t.Package.ImportPath()+".a") }

func (t *ldTarget) build() error {
	bindir := t.Package.Context.Bindir()
	if err := os.MkdirAll(bindir, 0777); err != nil {
		return err
	}
	return t.Ld(filepath.Join(bindir, filepath.Base(t.Package.ImportPath())), t.pkgfile())
}
<|endoftext|>


<commit_before>package gamqp

import (
	"bytes"
	"strings"
	"testing"
)

func TestBoolean(t *testing.T) {
	cases := []struct {
		in    []byte
		value bool
	}{
		{[]byte{0x00}, false},
		{[]byte{0x01}, true},
	}
	for _, c := range cases {
		value, _ := boolean(bytes.NewReader(c.in))
		if value != c.value {
			t.Errorf("boolean(%v) == %t, want %t", c.in, value, c.value)
		}
	}
}

func TestShortStr(t *testing.T) {
	cases := []struct {
		in, value string
	}{
		{"\n0123456789", "0123456789"},
		{"\014abc123def456", "abc123def456"},
	}
	for _, c := range cases {
		value, _ := shortStr(strings.NewReader(c.in))
		if value != c.value {
			t.Errorf("shortStr(%s) == %s, want %s", c.in, value, c.value)
		}
	}

}
<commit_msg>Add additional coverage<commit_after>package gamqp

import (
	"bytes"
	"io"
	"strings"
	"testing"
)

func TestBoolean(t *testing.T) {
	cases := []struct {
		in    []byte
		value bool
		err   error
	}{
		{[]byte{0x00}, false, nil},
		{[]byte{0x01}, true, nil},
		{[]byte{}, false, io.EOF},
	}
	for _, c := range cases {
		value, err := boolean(bytes.NewReader(c.in))
		if err != nil {
			if err != c.err {
				t.Errorf("shortStr(%s) == %#v, want %#v", c.in, err, c.err)
			}
		} else if value != c.value {
			t.Errorf("boolean(%v) == %t, want %t", c.in, value, c.value)
		}
	}
}

func TestShortStr(t *testing.T) {
	cases := []struct {
		in, value string
		err       error
	}{
		{"\n0123456789", "0123456789", nil},
		{"\014abc123def456", "abc123def456", nil},
		{"aabc123", "", io.ErrUnexpectedEOF},
		{"", "", io.EOF},
	}
	for _, c := range cases {
		value, err := shortStr(strings.NewReader(c.in))
		if err != nil {
			if err != c.err {
				t.Errorf("shortStr(%s) == %#v, want %#v", c.in, err, c.err)
			}
		} else {
			if value != c.value {
				t.Errorf("shortStr(%s) == %s, want %s", c.in, value, c.value)
			}
		}
	}

}
<|endoftext|>