<commit_before>package terraform

import (
	"log"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/dag"
	"github.com/hashicorp/terraform/states"
	"github.com/zclconf/go-cty/cty"
)

// OrphanResourceCountTransformer is a GraphTransformer that adds orphans
// for an expanded count to the graph. The determination of this depends
// on the count argument given.
//
// Orphans are found by comparing the count to what is found in the state.
// This transform assumes that if an element in the state is within the count
// bounds given, that it is not an orphan.
type OrphanResourceCountTransformer struct {
	Concrete ConcreteResourceInstanceNodeFunc

	Count   int                  // Actual count of the resource, or -1 if count is not set at all
	ForEach map[string]cty.Value // The ForEach map on the resource
	Addr    addrs.AbsResource    // Addr of the resource to look for orphans
	State   *states.State        // Full global state
}

func (t *OrphanResourceCountTransformer) Transform(g *Graph) error {
	rs := t.State.Resource(t.Addr)
	if rs == nil {
		return nil // Resource doesn't exist in state, so nothing to do!
	}

	haveKeys := make(map[addrs.InstanceKey]struct{})
	for key := range rs.Instances {
		haveKeys[key] = struct{}{}
	}

	// if for_each is set, use that transformer
	if t.ForEach != nil {
		return t.transformForEach(haveKeys, g)
	}
	if t.Count < 0 {
		return t.transformNoCount(haveKeys, g)
	}
	if t.Count == 0 {
		return t.transformZeroCount(haveKeys, g)
	}
	return t.transformCount(haveKeys, g)
}

func (t *OrphanResourceCountTransformer) transformForEach(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
	// If there is a no-key node, add this to the graph first,
	// so that we can create edges to it in subsequent (StringKey) nodes.
	// This is because the last item determines the resource mode for the whole resource,
	// so if this (non-deterministically) happens to end up as the last one,
	// that will change the resource's EachMode and our addressing for our instances
	// will not work as expected
	noKeyNode, hasNoKeyNode := haveKeys[addrs.NoKey]
	if hasNoKeyNode {
		g.Add(noKeyNode)
	}

	for key := range haveKeys {
		s, _ := key.(addrs.StringKey)
		// If the key is present in our current for_each, carry on
		if _, ok := t.ForEach[string(s)]; ok {
			continue
		}

		// If the key is no-key, we have already added it, so skip
		if key == addrs.NoKey {
			continue
		}

		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
		var node dag.Vertex = abstract
		if f := t.Concrete; f != nil {
			node = f(abstract)
		}
		log.Printf("[TRACE] OrphanResourceCount(non-zero): adding %s as %T", t.Addr, node)
		g.Add(node)

		// Add edge to noKeyNode if it exists
		if hasNoKeyNode {
			g.Connect(dag.BasicEdge(node, noKeyNode))
		}
	}
	return nil
}

func (t *OrphanResourceCountTransformer) transformCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
	// Due to the logic in Transform, we only get in here if our count is
	// at least one.

	_, have0Key := haveKeys[addrs.IntKey(0)]

	for key := range haveKeys {
		if key == addrs.NoKey && !have0Key {
			// If we have no 0-key then we will accept a no-key instance
			// as an alias for it.
			continue
		}

		i, isInt := key.(addrs.IntKey)
		if isInt && int(i) < t.Count {
			continue
		}

		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
		var node dag.Vertex = abstract
		if f := t.Concrete; f != nil {
			node = f(abstract)
		}
		log.Printf("[TRACE] OrphanResourceCount(non-zero): adding %s as %T", t.Addr, node)
		g.Add(node)
	}

	return nil
}

func (t *OrphanResourceCountTransformer) transformZeroCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
	// This case is easy: we need to orphan any keys we have at all.

	for key := range haveKeys {
		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
		var node dag.Vertex = abstract
		if f := t.Concrete; f != nil {
			node = f(abstract)
		}
		log.Printf("[TRACE] OrphanResourceCount(zero): adding %s as %T", t.Addr, node)
		g.Add(node)
	}

	return nil
}

func (t *OrphanResourceCountTransformer) transformNoCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
	// Negative count indicates that count is not set at all, in which
	// case we expect to have a single instance with no key set at all.
	// However, we'll also accept an instance with key 0 set as an alias
	// for it, in case the user has just deleted the "count" argument and
	// so wants to keep the first instance in the set.

	_, haveNoKey := haveKeys[addrs.NoKey]
	_, have0Key := haveKeys[addrs.IntKey(0)]
	keepKey := addrs.NoKey
	if have0Key && !haveNoKey {
		// If we don't have a no-key instance then we can use the 0-key instance
		// instead.
		keepKey = addrs.IntKey(0)
	}

	for key := range haveKeys {
		if key == keepKey {
			continue
		}

		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
		var node dag.Vertex = abstract
		if f := t.Concrete; f != nil {
			node = f(abstract)
		}
		log.Printf("[TRACE] OrphanResourceCount(no-count): adding %s as %T", t.Addr, node)
		g.Add(node)
	}

	return nil
}
<commit_msg>Creating the node would be nice<commit_after>package terraform

import (
	"log"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/dag"
	"github.com/hashicorp/terraform/states"
	"github.com/zclconf/go-cty/cty"
)

// OrphanResourceCountTransformer is a GraphTransformer that adds orphans
// for an expanded count to the graph. The determination of this depends
// on the count argument given.
//
// Orphans are found by comparing the count to what is found in the state.
// This transform assumes that if an element in the state is within the count
// bounds given, that it is not an orphan.
type OrphanResourceCountTransformer struct {
	Concrete ConcreteResourceInstanceNodeFunc

	Count   int                  // Actual count of the resource, or -1 if count is not set at all
	ForEach map[string]cty.Value // The ForEach map on the resource
	Addr    addrs.AbsResource    // Addr of the resource to look for orphans
	State   *states.State        // Full global state
}

func (t *OrphanResourceCountTransformer) Transform(g *Graph) error {
	rs := t.State.Resource(t.Addr)
	if rs == nil {
		return nil // Resource doesn't exist in state, so nothing to do!
	}

	haveKeys := make(map[addrs.InstanceKey]struct{})
	for key := range rs.Instances {
		haveKeys[key] = struct{}{}
	}

	// if for_each is set, use that transformer
	if t.ForEach != nil {
		return t.transformForEach(haveKeys, g)
	}
	if t.Count < 0 {
		return t.transformNoCount(haveKeys, g)
	}
	if t.Count == 0 {
		return t.transformZeroCount(haveKeys, g)
	}
	return t.transformCount(haveKeys, g)
}

func (t *OrphanResourceCountTransformer) transformForEach(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
	// If there is a no-key node, add this to the graph first,
	// so that we can create edges to it in subsequent (StringKey) nodes.
	// This is because the last item determines the resource mode for the whole resource,
	// so if this (non-deterministically) happens to end up as the last one,
	// that will change the resource's EachMode and our addressing for our instances
	// will not work as expected
	_, hasNoKeyNode := haveKeys[addrs.NoKey]
	var noKeyNode dag.Vertex
	if hasNoKeyNode {
		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(addrs.NoKey))
		noKeyNode = abstract
		if f := t.Concrete; f != nil {
			noKeyNode = f(abstract)
		}
		g.Add(noKeyNode)
	}

	for key := range haveKeys {
		s, _ := key.(addrs.StringKey)
		// If the key is present in our current for_each, carry on
		if _, ok := t.ForEach[string(s)]; ok {
			continue
		}

		// If the key is no-key, we have already added it, so skip
		if key == addrs.NoKey {
			continue
		}

		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
		var node dag.Vertex = abstract
		if f := t.Concrete; f != nil {
			node = f(abstract)
		}
		log.Printf("[TRACE] OrphanResourceCount(non-zero): adding %s as %T", t.Addr, node)
		g.Add(node)

		// Add edge to noKeyNode if it exists
		if hasNoKeyNode {
			g.Connect(dag.BasicEdge(node, noKeyNode))
		}
	}
	return nil
}

func (t *OrphanResourceCountTransformer) transformCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
	// Due to the logic in Transform, we only get in here if our count is
	// at least one.

	_, have0Key := haveKeys[addrs.IntKey(0)]

	for key := range haveKeys {
		if key == addrs.NoKey && !have0Key {
			// If we have no 0-key then we will accept a no-key instance
			// as an alias for it.
			continue
		}

		i, isInt := key.(addrs.IntKey)
		if isInt && int(i) < t.Count {
			continue
		}

		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
		var node dag.Vertex = abstract
		if f := t.Concrete; f != nil {
			node = f(abstract)
		}
		log.Printf("[TRACE] OrphanResourceCount(non-zero): adding %s as %T", t.Addr, node)
		g.Add(node)
	}

	return nil
}

func (t *OrphanResourceCountTransformer) transformZeroCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
	// This case is easy: we need to orphan any keys we have at all.

	for key := range haveKeys {
		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
		var node dag.Vertex = abstract
		if f := t.Concrete; f != nil {
			node = f(abstract)
		}
		log.Printf("[TRACE] OrphanResourceCount(zero): adding %s as %T", t.Addr, node)
		g.Add(node)
	}

	return nil
}

func (t *OrphanResourceCountTransformer) transformNoCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error {
	// Negative count indicates that count is not set at all, in which
	// case we expect to have a single instance with no key set at all.
	// However, we'll also accept an instance with key 0 set as an alias
	// for it, in case the user has just deleted the "count" argument and
	// so wants to keep the first instance in the set.

	_, haveNoKey := haveKeys[addrs.NoKey]
	_, have0Key := haveKeys[addrs.IntKey(0)]
	keepKey := addrs.NoKey
	if have0Key && !haveNoKey {
		// If we don't have a no-key instance then we can use the 0-key instance
		// instead.
		keepKey = addrs.IntKey(0)
	}

	for key := range haveKeys {
		if key == keepKey {
			continue
		}

		abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key))
		var node dag.Vertex = abstract
		if f := t.Concrete; f != nil {
			node = f(abstract)
		}
		log.Printf("[TRACE] OrphanResourceCount(no-count): adding %s as %T", t.Addr, node)
		g.Add(node)
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Knative 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 shared

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"testing"
	"time"

	"github.com/google/go-containerregistry/pkg/name"
	appsv1 "k8s.io/api/apps/v1"
	"k8s.io/apimachinery/pkg/util/sets"

	"knative.dev/pkg/pool"
	pkgTest "knative.dev/pkg/test"
	"knative.dev/pkg/test/spoof"
	"knative.dev/serving/test"
)

const scaleToZeroGracePeriod = 30 * time.Second

// DigestResolutionExceptions holds the set of "registry" domains for which
// digest resolution is not required.  These "registry" domains are generally
// associated with images that aren't actually published to a registry, but
// side-loaded into the cluster's container daemon via an operation like
// `docker load` or `kind load`.
var DigestResolutionExceptions = sets.NewString("kind.local", "ko.local", "dev.local")

// WaitForScaleToZero will wait for the specified deployment to scale to 0 replicas.
// Will wait up to 6 times the scaleToZeroGracePeriod (30 seconds) before failing.
func WaitForScaleToZero(t pkgTest.TLegacy, deploymentName string, clients *test.Clients) error {
	t.Helper()
	t.Logf("Waiting for %q to scale to zero", deploymentName)

	return pkgTest.WaitForDeploymentState(
		context.Background(),
		clients.KubeClient,
		deploymentName,
		func(d *appsv1.Deployment) (bool, error) {
			return d.Status.ReadyReplicas == 0, nil
		},
		"DeploymentIsScaledDown",
		test.ServingNamespace,
		scaleToZeroGracePeriod*6,
	)
}

// ValidateImageDigest validates the image digest.
func ValidateImageDigest(t *testing.T, imageName string, imageDigest string) (bool, error) {
	ref, err := name.ParseReference(pkgTest.ImagePath(imageName))
	if err != nil {
		return false, err
	}
	if DigestResolutionExceptions.Has(ref.Context().RegistryStr()) {
		t.Run("digest validation", func(t *testing.T) {
			t.Skipf("Skipping digest verification due to use of registry domain %s (one of %v)",
				ref.Context().RegistryStr(), DigestResolutionExceptions)
		})
		return true, nil
	}

	if imageDigest == "" {
		return false, errors.New("imageDigest not present")
	}
	digest, err := name.NewDigest(imageDigest)
	if err != nil {
		return false, err
	}

	return ref.Context().String() == digest.Context().String(), nil
}

// sendRequests sends "num" requests to "url", returning a string for each spoof.Response.Body.
func sendRequests(client *spoof.SpoofingClient, url *url.URL, num int) ([]string, error) {
	responses := make([]string, num)

	// Launch "num" requests, recording the responses we get in "responses".
	g, _ := pool.NewWithContext(context.Background(), 5, num)
	for i := 0; i < num; i++ {
		// We don't index into "responses" inside the goroutine to avoid a race, see #1545.
		result := &responses[i]
		g.Go(func() error {
			req, err := http.NewRequest(http.MethodGet, url.String(), nil)
			if err != nil {
				return err
			}

			resp, err := client.Do(req)
			if err != nil {
				return err
			}

			*result = string(resp.Body)
			return nil
		})
	}
	return responses, g.Wait()
}

func substrInList(key string, targets []string) string {
	for _, t := range targets {
		if strings.Contains(key, t) {
			return t
		}
	}
	return ""
}

// checkResponses verifies that each "expectedResponse" is present in "actualResponses" at least "min" times.
func checkResponses(t pkgTest.TLegacy, num, min int, domain string, expectedResponses, actualResponses []string) error {
	// counts maps the expected response body to the number of matching requests we saw.
	counts := make(map[string]int, len(expectedResponses))
	// badCounts maps the unexpected response body to the number of matching requests we saw.
	badCounts := make(map[string]int)

	// counts := eval(
	//   SELECT body, count(*) AS total
	//   FROM $actualResponses
	//   WHERE body IN $expectedResponses
	//   GROUP BY body
	// )
	for i, ar := range actualResponses {
		if er := substrInList(ar, expectedResponses); er != "" {
			counts[er]++
		} else {
			badCounts[ar]++
			t.Logf("For domain %s: got unexpected response for request %d", domain, i)
		}
	}

	// Print unexpected responses for debugging purposes
	for badResponse, count := range badCounts {
		t.Logf("For domain %s: saw unexpected response %q %d times.", domain, badResponse, count)
	}

	// Verify that we saw each entry in "expectedResponses" at least "min" times.
	// check(SELECT body FROM $counts WHERE total < $min)
	totalMatches := 0
	errMsg := []string{}
	for _, er := range expectedResponses {
		count := counts[er]
		if count < min {
			errMsg = append(errMsg,
				fmt.Sprintf("domain %s failed: want at least %d, got %d for response %q",
					domain, min, count, er))
		}

		t.Logf("For domain %s: wanted at least %d, got %d requests.", domain, min, count)
		totalMatches += count
	}
	// Verify that the total expected responses match the number of requests made.
	if totalMatches < num {
		errMsg = append(errMsg,
			fmt.Sprintf("domain %s: saw expected responses %d times, wanted %d", domain, totalMatches, num))
	}
	if len(errMsg) == 0 {
		// If we made it here, the implementation conforms. Congratulations!
		return nil
	}
	return errors.New(strings.Join(errMsg, ","))
}

// CheckDistribution sends "num" requests to "domain", then validates that
// we see each body in "expectedResponses" at least "min" times.
func CheckDistribution(t pkgTest.TLegacy, clients *test.Clients, url *url.URL, num, min int, expectedResponses []string) error {
	ctx := context.Background()
	client, err := pkgTest.NewSpoofingClient(ctx, clients.KubeClient, t.Logf, url.Hostname(), test.ServingFlags.ResolvableDomain, test.AddRootCAtoTransport(ctx, t.Logf, clients, test.ServingFlags.Https))
	if err != nil {
		return err
	}

	t.Logf("Performing %d concurrent requests to %s", num, url)
	actualResponses, err := sendRequests(client, url, num)
	if err != nil {
		return err
	}

	return checkResponses(t, num, min, url.Hostname(), expectedResponses, actualResponses)
}
<commit_msg>Remove unnecessary 'WithContext' and align parallelism with networking. (#9425)<commit_after>/*
Copyright 2020 The Knative 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 shared

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"testing"
	"time"

	"github.com/google/go-containerregistry/pkg/name"
	appsv1 "k8s.io/api/apps/v1"
	"k8s.io/apimachinery/pkg/util/sets"

	"knative.dev/pkg/pool"
	pkgTest "knative.dev/pkg/test"
	"knative.dev/pkg/test/spoof"
	"knative.dev/serving/test"
)

const scaleToZeroGracePeriod = 30 * time.Second

// DigestResolutionExceptions holds the set of "registry" domains for which
// digest resolution is not required.  These "registry" domains are generally
// associated with images that aren't actually published to a registry, but
// side-loaded into the cluster's container daemon via an operation like
// `docker load` or `kind load`.
var DigestResolutionExceptions = sets.NewString("kind.local", "ko.local", "dev.local")

// WaitForScaleToZero will wait for the specified deployment to scale to 0 replicas.
// Will wait up to 6 times the scaleToZeroGracePeriod (30 seconds) before failing.
func WaitForScaleToZero(t pkgTest.TLegacy, deploymentName string, clients *test.Clients) error {
	t.Helper()
	t.Logf("Waiting for %q to scale to zero", deploymentName)

	return pkgTest.WaitForDeploymentState(
		context.Background(),
		clients.KubeClient,
		deploymentName,
		func(d *appsv1.Deployment) (bool, error) {
			return d.Status.ReadyReplicas == 0, nil
		},
		"DeploymentIsScaledDown",
		test.ServingNamespace,
		scaleToZeroGracePeriod*6,
	)
}

// ValidateImageDigest validates the image digest.
func ValidateImageDigest(t *testing.T, imageName string, imageDigest string) (bool, error) {
	ref, err := name.ParseReference(pkgTest.ImagePath(imageName))
	if err != nil {
		return false, err
	}
	if DigestResolutionExceptions.Has(ref.Context().RegistryStr()) {
		t.Run("digest validation", func(t *testing.T) {
			t.Skipf("Skipping digest verification due to use of registry domain %s (one of %v)",
				ref.Context().RegistryStr(), DigestResolutionExceptions)
		})
		return true, nil
	}

	if imageDigest == "" {
		return false, errors.New("imageDigest not present")
	}
	digest, err := name.NewDigest(imageDigest)
	if err != nil {
		return false, err
	}

	return ref.Context().String() == digest.Context().String(), nil
}

// sendRequests sends "num" requests to "url", returning a string for each spoof.Response.Body.
func sendRequests(client *spoof.SpoofingClient, url *url.URL, num int) ([]string, error) {
	responses := make([]string, num)

	// Launch "num" requests, recording the responses we get in "responses".
	g := pool.NewWithCapacity(8, num)
	for i := 0; i < num; i++ {
		// We don't index into "responses" inside the goroutine to avoid a race, see #1545.
		result := &responses[i]
		g.Go(func() error {
			req, err := http.NewRequest(http.MethodGet, url.String(), nil)
			if err != nil {
				return err
			}

			resp, err := client.Do(req)
			if err != nil {
				return err
			}

			*result = string(resp.Body)
			return nil
		})
	}
	return responses, g.Wait()
}

func substrInList(key string, targets []string) string {
	for _, t := range targets {
		if strings.Contains(key, t) {
			return t
		}
	}
	return ""
}

// checkResponses verifies that each "expectedResponse" is present in "actualResponses" at least "min" times.
func checkResponses(t pkgTest.TLegacy, num, min int, domain string, expectedResponses, actualResponses []string) error {
	// counts maps the expected response body to the number of matching requests we saw.
	counts := make(map[string]int, len(expectedResponses))
	// badCounts maps the unexpected response body to the number of matching requests we saw.
	badCounts := make(map[string]int)

	// counts := eval(
	//   SELECT body, count(*) AS total
	//   FROM $actualResponses
	//   WHERE body IN $expectedResponses
	//   GROUP BY body
	// )
	for i, ar := range actualResponses {
		if er := substrInList(ar, expectedResponses); er != "" {
			counts[er]++
		} else {
			badCounts[ar]++
			t.Logf("For domain %s: got unexpected response for request %d", domain, i)
		}
	}

	// Print unexpected responses for debugging purposes
	for badResponse, count := range badCounts {
		t.Logf("For domain %s: saw unexpected response %q %d times.", domain, badResponse, count)
	}

	// Verify that we saw each entry in "expectedResponses" at least "min" times.
	// check(SELECT body FROM $counts WHERE total < $min)
	totalMatches := 0
	errMsg := []string{}
	for _, er := range expectedResponses {
		count := counts[er]
		if count < min {
			errMsg = append(errMsg,
				fmt.Sprintf("domain %s failed: want at least %d, got %d for response %q",
					domain, min, count, er))
		}

		t.Logf("For domain %s: wanted at least %d, got %d requests.", domain, min, count)
		totalMatches += count
	}
	// Verify that the total expected responses match the number of requests made.
	if totalMatches < num {
		errMsg = append(errMsg,
			fmt.Sprintf("domain %s: saw expected responses %d times, wanted %d", domain, totalMatches, num))
	}
	if len(errMsg) == 0 {
		// If we made it here, the implementation conforms. Congratulations!
		return nil
	}
	return errors.New(strings.Join(errMsg, ","))
}

// CheckDistribution sends "num" requests to "domain", then validates that
// we see each body in "expectedResponses" at least "min" times.
func CheckDistribution(t pkgTest.TLegacy, clients *test.Clients, url *url.URL, num, min int, expectedResponses []string) error {
	ctx := context.Background()
	client, err := pkgTest.NewSpoofingClient(ctx, clients.KubeClient, t.Logf, url.Hostname(), test.ServingFlags.ResolvableDomain, test.AddRootCAtoTransport(ctx, t.Logf, clients, test.ServingFlags.Https))
	if err != nil {
		return err
	}

	t.Logf("Performing %d concurrent requests to %s", num, url)
	actualResponses, err := sendRequests(client, url, num)
	if err != nil {
		return err
	}

	return checkResponses(t, num, min, url.Hostname(), expectedResponses, actualResponses)
}
<|endoftext|>


<commit_before>package anonuuid

import (
	"fmt"
	"log"
	"math/rand"
	"regexp"
	"strings"
	"time"
)

var (
	// UUIDRegex is the regex used to find UUIDs in texts
	UUIDRegex = "[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}"
)

// AnonUUID is the main structure, it contains the cache map and helpers
type AnonUUID struct {
	cache map[string]string

	// Hexspeak flag will generate hexspeak style fake UUIDs
	Hexspeak bool

	// Random flag will generate random fake UUIDs
	Random bool
}

// Sanitize takes a string as input and return sanitized string
func (a *AnonUUID) Sanitize(input string) string {
	r := regexp.MustCompile(UUIDRegex)

	return r.ReplaceAllStringFunc(input, func(m string) string {
		parts := r.FindStringSubmatch(m)
		return a.FakeUUID(parts[0])
	})
}

// FakeUUID takes a realUUID and return its corresponding fakeUUID
func (a *AnonUUID) FakeUUID(realUUID string) string {
	if _, ok := a.cache[realUUID]; !ok {

		var fakeUUID string
		var err error
		if a.Hexspeak {
			fakeUUID, err = GenerateHexspeakUUID(len(a.cache))
		} else if a.Random {
			fakeUUID, err = GenerateRandomUUID(10)
		} else {
			fakeUUID, err = GenerateLenUUID(len(a.cache))
		}

		if err != nil {
			fmt.Println(err)
			log.Fatalf("Test")
		}

		// FIXME: check for duplicates and retry

		a.cache[realUUID] = fakeUUID
	}
	return a.cache[realUUID]
}

// New returns a prepared AnonUUID structure
func New() *AnonUUID {
	return &AnonUUID{
		cache:    make(map[string]string),
		Hexspeak: false,
		Random:   false,
	}
}

func init() {
	rand.Seed(time.Now().UTC().UnixNano())
}

// FormatUUID takes a string in input and return an UUID formatted string by repeating the string and placing dashes if necessary
func FormatUUID(part string) (string, error) {
	if len(part) < 32 {
		part = strings.Repeat(part, 32)
	}
	if len(part) > 32 {
		part = part[:32]
	}
	uuid := part[:8] + "-" + part[8:12] + "-" + part[12:16] + "-" + part[16:20] + "-" + part[20:32]

	matched, err := regexp.MatchString("^"+UUIDRegex+"$", uuid)
	if err != nil {
		return "", err
	}
	if !matched {
		return "", fmt.Errorf("String '%s' is not a valid UUID", uuid)
	}

	return uuid, nil
}

// GenerateRandomUUID returns an UUID based on random strings
func GenerateRandomUUID(length int) (string, error) {
	var letters = []rune("abcdef0123456789")

	b := make([]rune, length)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return FormatUUID(string(b))
}

// GenerateHexspeakUUID returns an UUID formatted string containing hexspeak words
func GenerateHexspeakUUID(i int) (string, error) {
	hexspeaks := []string{
		"0ff1ce",
		"31337",
		"4b1d",
		"badc0de",
		"badcafe",
		"badf00d",
		"deadbabe",
		"deadbeef",
		"deadc0de",
		"deadfeed",
		"fee1bad",
	}
	return FormatUUID(hexspeaks[i%len(hexspeaks)])
}

// GenerateLenUUID returns an UUID formatted string based on an index number
func GenerateLenUUID(i int) (string, error) {
	return FormatUUID(fmt.Sprintf("%x", i))
}
<commit_msg>Forced 13th character to be 1<commit_after>package anonuuid

import (
	"fmt"
	"log"
	"math/rand"
	"regexp"
	"strings"
	"time"
)

var (
	// UUIDRegex is the regex used to find UUIDs in texts
	UUIDRegex = "[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}"
)

// AnonUUID is the main structure, it contains the cache map and helpers
type AnonUUID struct {
	cache map[string]string

	// Hexspeak flag will generate hexspeak style fake UUIDs
	Hexspeak bool

	// Random flag will generate random fake UUIDs
	Random bool
}

// Sanitize takes a string as input and return sanitized string
func (a *AnonUUID) Sanitize(input string) string {
	r := regexp.MustCompile(UUIDRegex)

	return r.ReplaceAllStringFunc(input, func(m string) string {
		parts := r.FindStringSubmatch(m)
		return a.FakeUUID(parts[0])
	})
}

// FakeUUID takes a realUUID and return its corresponding fakeUUID
func (a *AnonUUID) FakeUUID(realUUID string) string {
	if _, ok := a.cache[realUUID]; !ok {

		var fakeUUID string
		var err error
		if a.Hexspeak {
			fakeUUID, err = GenerateHexspeakUUID(len(a.cache))
		} else if a.Random {
			fakeUUID, err = GenerateRandomUUID(10)
		} else {
			fakeUUID, err = GenerateLenUUID(len(a.cache))
		}

		if err != nil {
			fmt.Println(err)
			log.Fatalf("Test")
		}

		// FIXME: check for duplicates and retry

		a.cache[realUUID] = fakeUUID
	}
	return a.cache[realUUID]
}

// New returns a prepared AnonUUID structure
func New() *AnonUUID {
	return &AnonUUID{
		cache:    make(map[string]string),
		Hexspeak: false,
		Random:   false,
	}
}

func init() {
	rand.Seed(time.Now().UTC().UnixNano())
}

// FormatUUID takes a string in input and return an UUID formatted string by repeating the string and placing dashes if necessary
func FormatUUID(part string) (string, error) {
	if len(part) < 32 {
		part = strings.Repeat(part, 32)
	}
	if len(part) > 32 {
		part = part[:32]
	}
	uuid := part[:8] + "-" + part[8:12] + "-1" + part[13:16] + "-" + part[16:20] + "-" + part[20:32]

	matched, err := regexp.MatchString("^"+UUIDRegex+"$", uuid)
	if err != nil {
		return "", err
	}
	if !matched {
		return "", fmt.Errorf("String '%s' is not a valid UUID", uuid)
	}

	return uuid, nil
}

// GenerateRandomUUID returns an UUID based on random strings
func GenerateRandomUUID(length int) (string, error) {
	var letters = []rune("abcdef0123456789")

	b := make([]rune, length)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return FormatUUID(string(b))
}

// GenerateHexspeakUUID returns an UUID formatted string containing hexspeak words
func GenerateHexspeakUUID(i int) (string, error) {
	hexspeaks := []string{
		"0ff1ce",
		"31337",
		"4b1d",
		"badc0de",
		"badcafe",
		"badf00d",
		"deadbabe",
		"deadbeef",
		"deadc0de",
		"deadfeed",
		"fee1bad",
	}
	return FormatUUID(hexspeaks[i%len(hexspeaks)])
}

// GenerateLenUUID returns an UUID formatted string based on an index number
func GenerateLenUUID(i int) (string, error) {
	return FormatUUID(fmt.Sprintf("%x", i))
}
<|endoftext|>


<commit_before>package engine

import (
	"math"
	"math/rand"
)

type DotsMask struct {
	mask [][]uint8
}

var DotsMaskSquare2x2 = NewDotsMask([][]uint8{
	{1, 1},
	{1, 1},
})

var DotsMaskTank = NewDotsMask([][]uint8{
	{0, 1, 0},
	{1, 1, 1},
	{1, 0, 1},
})

var DotsMaskHome = NewDotsMask([][]uint8{
	{1, 1, 0, 1, 1},
	{1, 0, 0, 0, 1},
	{1, 0, 0, 0, 1},
	{1, 0, 0, 0, 1},
	{1, 0, 0, 0, 1},
	{1, 1, 1, 1, 1},
})

var DotsMaskCross = NewDotsMask([][]uint8{
	{0, 0, 0, 1, 0, 0, 0},
	{0, 0, 0, 1, 0, 0, 0},
	{0, 0, 0, 1, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 1, 0, 0, 0},
	{0, 0, 0, 1, 0, 0, 0},
	{0, 0, 0, 1, 0, 0, 0},
})

var DotsMaskDiagonal = NewDotsMask([][]uint8{
	{0, 0, 0, 0, 0, 0, 1},
	{0, 0, 0, 0, 0, 1, 0},
	{0, 0, 0, 0, 1, 0, 0},
	{0, 0, 0, 1, 0, 0, 0},
	{0, 0, 1, 0, 0, 0, 0},
	{0, 1, 0, 0, 0, 0, 0},
	{1, 0, 0, 0, 0, 0, 0},
})

func NewDotsMask(mask [][]uint8) *DotsMask {
	copyMask := make([][]uint8, len(mask))

	for i, row := range mask {
		if len(row) > math.MaxUint8 {
			copyMask[i] = make([]uint8, math.MaxUint8+1)
		} else {
			copyMask[i] = make([]uint8, len(row))
		}
		copy(copyMask[i], row)
	}

	return &DotsMask{
		mask: copyMask,
	}
}

func NewZeroDotsMask(width, height uint8) *DotsMask {
	mask := make([][]uint8, height)
	for i := range mask {
		mask[i] = make([]uint8, width)
	}
	return &DotsMask{
		mask: mask,
	}
}

func (dm *DotsMask) Copy() *DotsMask {
	copyMask := make([][]uint8, len(dm.mask))

	for i, row := range dm.mask {
		if len(row) > math.MaxUint8 {
			copyMask[i] = make([]uint8, math.MaxUint8+1)
		} else {
			copyMask[i] = make([]uint8, len(row))
		}
		copy(copyMask[i], row)
	}

	return &DotsMask{
		mask: copyMask,
	}
}

func (dm *DotsMask) Width() uint8 {
	width := 0
	for _, row := range dm.mask {
		if width < len(row) {
			width = len(row)
		}
	}
	return uint8(width)
}

func (dm *DotsMask) Height() uint8 {
	return uint8(len(dm.mask))
}

func (dm *DotsMask) TurnOver() *DotsMask {
	newMask := NewZeroDotsMask(dm.Width(), dm.Height())
	for i := range dm.mask {
		copy(newMask.mask[len(dm.mask)-1-i], dm.mask[i])
	}
	return newMask
}

func (dm *DotsMask) TurnRight() *DotsMask {
	newMask := NewZeroDotsMask(dm.Height(), dm.Width())
	for i := 0; i < len(dm.mask); i++ {
		for j := 0; j < len(dm.mask[i]); j++ {
			newMask.mask[j][len(dm.mask)-1-i] = dm.mask[i][j]
		}
	}
	return newMask
}

func (dm *DotsMask) TurnLeft() *DotsMask {
	newMask := NewZeroDotsMask(dm.Height(), dm.Width())
	for i := 0; i < len(dm.mask); i++ {
		for j := 0; j < len(dm.mask[i]); j++ {
			newMask.mask[len(newMask.mask)-1-j][i] = dm.mask[i][j]
		}
	}
	return newMask
}

func (dm *DotsMask) TurnRandom() *DotsMask {
	const (
		caseReturnCopy = iota
		caseReturnTurnRight
		caseReturnTurnLeft
		caseReturnTurnOver
		turnReturnCasesCount
	)

	switch rand.Intn(turnReturnCasesCount) {
	case caseReturnCopy:
		return dm.Copy()
	case caseReturnTurnRight:
		return dm.TurnRight()
	case caseReturnTurnLeft:
		return dm.TurnLeft()
	case caseReturnTurnOver:
		return dm.TurnOver()
	}

	return nil
}

func (dm *DotsMask) Location(x, y uint8) Location {
	location := make(Location, 0)
	for i := 0; i < len(dm.mask); i++ {
		for j := 0; j < len(dm.mask[i]); j++ {
			if dm.mask[i][j] > 0 {
				location = append(location, Dot{
					X: x + uint8(j),
					Y: y + uint8(i),
				})
			}
		}
	}
	return location
}

func (dm *DotsMask) Empty() bool {
	for i := range dm.mask {
		for j := range dm.mask[i] {
			if dm.mask[i][j] > 0 {
				return false
			}
		}
	}
	return true
}
<commit_msg>Create DotsMask masks: DotsMaskCrossSmall and DotsMaskDiagonalSmall<commit_after>package engine

import (
	"math"
	"math/rand"
)

type DotsMask struct {
	mask [][]uint8
}

var DotsMaskSquare2x2 = NewDotsMask([][]uint8{
	{1, 1},
	{1, 1},
})

var DotsMaskTank = NewDotsMask([][]uint8{
	{0, 1, 0},
	{1, 1, 1},
	{1, 0, 1},
})

var DotsMaskHome = NewDotsMask([][]uint8{
	{1, 1, 0, 1, 1},
	{1, 0, 0, 0, 1},
	{1, 0, 0, 0, 1},
	{1, 0, 0, 0, 1},
	{1, 0, 0, 0, 1},
	{1, 1, 1, 1, 1},
})

var DotsMaskCross = NewDotsMask([][]uint8{
	{0, 0, 0, 1, 0, 0, 0},
	{0, 0, 0, 1, 0, 0, 0},
	{0, 0, 0, 1, 0, 0, 0},
	{1, 1, 1, 1, 1, 1, 1},
	{0, 0, 0, 1, 0, 0, 0},
	{0, 0, 0, 1, 0, 0, 0},
	{0, 0, 0, 1, 0, 0, 0},
})

var DotsMaskDiagonal = NewDotsMask([][]uint8{
	{0, 0, 0, 0, 0, 0, 1},
	{0, 0, 0, 0, 0, 1, 0},
	{0, 0, 0, 0, 1, 0, 0},
	{0, 0, 0, 1, 0, 0, 0},
	{0, 0, 1, 0, 0, 0, 0},
	{0, 1, 0, 0, 0, 0, 0},
	{1, 0, 0, 0, 0, 0, 0},
})

var DotsMaskCrossSmall = NewDotsMask([][]uint8{
	{0, 1, 0},
	{1, 1, 1},
	{0, 1, 0},
})

var DotsMaskDiagonalSmall = NewDotsMask([][]uint8{
	{1, 0, 1},
	{0, 1, 0},
	{1, 0, 1},
})

func NewDotsMask(mask [][]uint8) *DotsMask {
	copyMask := make([][]uint8, len(mask))

	for i, row := range mask {
		if len(row) > math.MaxUint8 {
			copyMask[i] = make([]uint8, math.MaxUint8+1)
		} else {
			copyMask[i] = make([]uint8, len(row))
		}
		copy(copyMask[i], row)
	}

	return &DotsMask{
		mask: copyMask,
	}
}

func NewZeroDotsMask(width, height uint8) *DotsMask {
	mask := make([][]uint8, height)
	for i := range mask {
		mask[i] = make([]uint8, width)
	}
	return &DotsMask{
		mask: mask,
	}
}

func (dm *DotsMask) Copy() *DotsMask {
	copyMask := make([][]uint8, len(dm.mask))

	for i, row := range dm.mask {
		if len(row) > math.MaxUint8 {
			copyMask[i] = make([]uint8, math.MaxUint8+1)
		} else {
			copyMask[i] = make([]uint8, len(row))
		}
		copy(copyMask[i], row)
	}

	return &DotsMask{
		mask: copyMask,
	}
}

func (dm *DotsMask) Width() uint8 {
	width := 0
	for _, row := range dm.mask {
		if width < len(row) {
			width = len(row)
		}
	}
	return uint8(width)
}

func (dm *DotsMask) Height() uint8 {
	return uint8(len(dm.mask))
}

func (dm *DotsMask) TurnOver() *DotsMask {
	newMask := NewZeroDotsMask(dm.Width(), dm.Height())
	for i := range dm.mask {
		copy(newMask.mask[len(dm.mask)-1-i], dm.mask[i])
	}
	return newMask
}

func (dm *DotsMask) TurnRight() *DotsMask {
	newMask := NewZeroDotsMask(dm.Height(), dm.Width())
	for i := 0; i < len(dm.mask); i++ {
		for j := 0; j < len(dm.mask[i]); j++ {
			newMask.mask[j][len(dm.mask)-1-i] = dm.mask[i][j]
		}
	}
	return newMask
}

func (dm *DotsMask) TurnLeft() *DotsMask {
	newMask := NewZeroDotsMask(dm.Height(), dm.Width())
	for i := 0; i < len(dm.mask); i++ {
		for j := 0; j < len(dm.mask[i]); j++ {
			newMask.mask[len(newMask.mask)-1-j][i] = dm.mask[i][j]
		}
	}
	return newMask
}

func (dm *DotsMask) TurnRandom() *DotsMask {
	const (
		caseReturnCopy = iota
		caseReturnTurnRight
		caseReturnTurnLeft
		caseReturnTurnOver
		turnReturnCasesCount
	)

	switch rand.Intn(turnReturnCasesCount) {
	case caseReturnCopy:
		return dm.Copy()
	case caseReturnTurnRight:
		return dm.TurnRight()
	case caseReturnTurnLeft:
		return dm.TurnLeft()
	case caseReturnTurnOver:
		return dm.TurnOver()
	}

	return nil
}

func (dm *DotsMask) Location(x, y uint8) Location {
	location := make(Location, 0)
	for i := 0; i < len(dm.mask); i++ {
		for j := 0; j < len(dm.mask[i]); j++ {
			if dm.mask[i][j] > 0 {
				location = append(location, Dot{
					X: x + uint8(j),
					Y: y + uint8(i),
				})
			}
		}
	}
	return location
}

func (dm *DotsMask) Empty() bool {
	for i := range dm.mask {
		for j := range dm.mask[i] {
			if dm.mask[i][j] > 0 {
				return false
			}
		}
	}
	return true
}
<|endoftext|>


<commit_before>package vagrantutil

import (
	"fmt"
	"log"
	"os"
	"testing"

	"github.com/koding/klient/Godeps/_workspace/src/github.com/hashicorp/go-version"
)

const testVagrantFile = `# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.hostname = "vagrant"

  config.vm.provider "virtualbox" do |vb|
    # Use VBoxManage to customize the VM. For example to change memory:
    vb.customize ["modifyvm", :id, "--memory", "2048", "--cpus", "2"]
  end
end
`

var (
	vg          *Vagrant
	vagrantName = "vagrantTest"
)

func TestMain(m *testing.M) {
	var err error
	vg, err = NewVagrant(vagrantName)
	if err != nil {
		log.Fatalln(err)
	}

	os.Exit(m.Run())
}

func TestVersion(t *testing.T) {
	out, err := vg.Version()
	if err != nil {
		t.Fatal(err)
	}

	// check if the output is correct
	_, err = version.NewVersion(out)
	if err != nil {
		t.Error(err)
	}
}

func TestCreate(t *testing.T) {
	err := vg.Create(testVagrantFile)
	if err != nil {
		t.Fatal(err)
	}

	if err := vg.vagrantfileExists(); err != nil {
		t.Error(err)
	}
}

func TestUp(t *testing.T) {
	out, err := vg.Up()
	if err != nil {
		t.Fatal(err)
	}

	log.Printf("Starting to read the stream output of 'vagrant up':\n\n")
	for res := range out {
		if res.Error != nil {
			t.Error(err)
		}
		log.Println(res.Line)
	}

	log.Printf("\n\nStreaming is finished for 'vagrant up' command")

	status, err := vg.Status()
	if err != nil {
		t.Fatal(err)
	}

	if status != Running {
		t.Errorf("Vagrant status should be: %s. Got: %s", Running, status)
	}
}

func TestHalt(t *testing.T) {
	out, err := vg.Halt()
	if err != nil {
		t.Fatal(err)
	}

	log.Printf("Starting to read the stream output of 'vagrant halt':\n\n")
	for res := range out {
		if res.Error != nil {
			t.Error(err)
		}
		log.Println(res.Line)
	}

	log.Printf("\n\nStreaming is finished for 'vagrant halt' command")

	status, err := vg.Status()
	if err != nil {
		t.Fatal(err)
	}

	if status != PowerOff {
		t.Errorf("Vagrant status should be: %s. Got: %s", PowerOff, status)
	}
}

func TestDestroy(t *testing.T) {
	out, err := vg.Destroy()
	if err != nil {
		t.Fatal(err)
	}

	log.Printf("Starting to read the stream output of 'vagrant destroy':\n\n")
	for res := range out {
		if res.Error != nil {
			t.Error(err)
		}
		log.Println(res.Line)
	}
	log.Printf("\n\nStreaming is finished for 'vagrant destroy' command")

	status, err := vg.Status()
	if err != nil {
		t.Fatal(err)
	}

	if status != NotCreated {
		t.Errorf("Vagrant status should be: %s. Got: %s", NotCreated, status)
	}

}

func TestStatus(t *testing.T) {
	status, err := vg.Status()
	if err != nil {
		t.Fatal(err)
	}

	fmt.Printf("status = %+v\n", status)
}
<commit_msg>vagrant_test: fix import path<commit_after>package vagrantutil

import (
	"fmt"
	"log"
	"os"
	"testing"

	"github.com/hashicorp/go-version"
)

const testVagrantFile = `# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.hostname = "vagrant"

  config.vm.provider "virtualbox" do |vb|
    # Use VBoxManage to customize the VM. For example to change memory:
    vb.customize ["modifyvm", :id, "--memory", "2048", "--cpus", "2"]
  end
end
`

var (
	vg          *Vagrant
	vagrantName = "vagrantTest"
)

func TestMain(m *testing.M) {
	var err error
	vg, err = NewVagrant(vagrantName)
	if err != nil {
		log.Fatalln(err)
	}

	os.Exit(m.Run())
}

func TestVersion(t *testing.T) {
	out, err := vg.Version()
	if err != nil {
		t.Fatal(err)
	}

	// check if the output is correct
	_, err = version.NewVersion(out)
	if err != nil {
		t.Error(err)
	}
}

func TestCreate(t *testing.T) {
	err := vg.Create(testVagrantFile)
	if err != nil {
		t.Fatal(err)
	}

	if err := vg.vagrantfileExists(); err != nil {
		t.Error(err)
	}
}

func TestUp(t *testing.T) {
	out, err := vg.Up()
	if err != nil {
		t.Fatal(err)
	}

	log.Printf("Starting to read the stream output of 'vagrant up':\n\n")
	for res := range out {
		if res.Error != nil {
			t.Error(err)
		}
		log.Println(res.Line)
	}

	log.Printf("\n\nStreaming is finished for 'vagrant up' command")

	status, err := vg.Status()
	if err != nil {
		t.Fatal(err)
	}

	if status != Running {
		t.Errorf("Vagrant status should be: %s. Got: %s", Running, status)
	}
}

func TestHalt(t *testing.T) {
	out, err := vg.Halt()
	if err != nil {
		t.Fatal(err)
	}

	log.Printf("Starting to read the stream output of 'vagrant halt':\n\n")
	for res := range out {
		if res.Error != nil {
			t.Error(err)
		}
		log.Println(res.Line)
	}

	log.Printf("\n\nStreaming is finished for 'vagrant halt' command")

	status, err := vg.Status()
	if err != nil {
		t.Fatal(err)
	}

	if status != PowerOff {
		t.Errorf("Vagrant status should be: %s. Got: %s", PowerOff, status)
	}
}

func TestDestroy(t *testing.T) {
	out, err := vg.Destroy()
	if err != nil {
		t.Fatal(err)
	}

	log.Printf("Starting to read the stream output of 'vagrant destroy':\n\n")
	for res := range out {
		if res.Error != nil {
			t.Error(err)
		}
		log.Println(res.Line)
	}
	log.Printf("\n\nStreaming is finished for 'vagrant destroy' command")

	status, err := vg.Status()
	if err != nil {
		t.Fatal(err)
	}

	if status != NotCreated {
		t.Errorf("Vagrant status should be: %s. Got: %s", NotCreated, status)
	}

}

func TestStatus(t *testing.T) {
	status, err := vg.Status()
	if err != nil {
		t.Fatal(err)
	}

	fmt.Printf("status = %+v\n", status)
}
<|endoftext|>


<commit_before>/*                          _       _
 *__      _____  __ ___   ___  __ _| |_ ___
 *\ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
 * \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
 *  \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
 *
 * Copyright © 2016 - 2018 Weaviate. All rights reserved.
 * LICENSE: https://github.com/creativesoftwarefdn/weaviate/blob/develop/LICENSE.md
 * AUTHOR: Bob van Luijt (bob@kub.design)
 * See www.creativesoftwarefdn.org for details
 * Contact: @CreativeSofwFdn / bob@kub.design
 */

// Package network provides the network graphql endpoint for Weaviate
package network

import (
	"fmt"
	"github.com/creativesoftwarefdn/weaviate/graphqlapi/descriptions"
	"github.com/creativesoftwarefdn/weaviate/graphqlapi/local/common_filters"
	"github.com/creativesoftwarefdn/weaviate/graphqlapi/utils"
	"github.com/graphql-go/graphql"
)

func genNetworkWhereOperatorEnum() *graphql.Enum {
	graphql.EnumConfig{
		Name: "NetworkWhereOperatorEnum",
		Values: graphql.EnumValueConfigMap{
			"And":              &graphql.EnumValueConfig{},
			"Or":               &graphql.EnumValueConfig{},
			"Equal":            &graphql.EnumValueConfig{},
			"Not":              &graphql.EnumValueConfig{},
			"NotEqual":         &graphql.EnumValueConfig{},
			"GreaterThan":      &graphql.EnumValueConfig{},
			"GreaterThanEqual": &graphql.EnumValueConfig{},
			"LessThan":         &graphql.EnumValueConfig{},
			"LessThanEqual":    &graphql.EnumValueConfig{},
		},
		Description: descriptions.WhereOperatorEnumDesc,
	}
}

// generate these elements once
func genNetworkStaticWhereFilterElements() graphql.InputObjectConfigFieldMap {
	staticFilterElements := graphql.InputObjectConfigFieldMap{
		"operator": &graphql.InputObjectFieldConfig{
			Type:        genNetworkWhereOperatorEnum(),
			Description: descriptions.WhereOperatorDesc,
		},
		"valueInt": &graphql.InputObjectFieldConfig{
			Type:        graphql.Int,
			Description: descriptions.WhereValueIntDesc,
		},
		"valueNumber": &graphql.InputObjectFieldConfig{
			Type:        graphql.Float,
			Description: descriptions.WhereValueNumberDesc,
		},
		"valueBoolean": &graphql.InputObjectFieldConfig{
			Type:        graphql.Boolean,
			Description: descriptions.WhereValueBooleanDesc,
		},
		"valueString": &graphql.InputObjectFieldConfig{
			Type:        graphql.String,
			Description: descriptions.WhereValueStringDesc,
		},
	}

	return staticFilterElements
}

// This is an exact translation of the Prototype from JS to Go. In the prototype some filter elements are declared as global variables, this is recreated here.
func genGlobalNetworkFilterElements(filterContainer *utils.FilterContainer) {
	filterContainer.WeaviateNetworkWhereKeywordsInpObj = genWeaviateNetworkWhereNameKeywordsInpObj()
	filterContainer.WeaviateNetworkIntrospectPropertiesObjField = genWeaviateNetworkIntrospectPropertiesObjField()
}

func genWeaviateNetworkWhereNameKeywordsInpObj() *graphql.InputObject {
	weaviateNetworkWhereNameKeywordsInpObj := graphql.NewInputObject(
		graphql.InputObjectConfig{
			Name: "WeaviateNetworkWhereNameKeywordsInpObj",
			Fields: graphql.InputObjectConfigFieldMap{
				"value": &graphql.InputObjectFieldConfig{
					Type:        graphql.String,
					Description: descriptions.WhereKeywordsValueDesc,
				},
				"weight": &graphql.InputObjectFieldConfig{
					Type:        graphql.Float,
					Description: descriptions.WhereKeywordsWeightDesc,
				},
			},
			Description: descriptions.WhereKeywordsInpObjDesc,
		},
	)
	return weaviateNetworkWhereNameKeywordsInpObj
}

func genWeaviateNetworkIntrospectPropertiesObjField() *graphql.Field {
	weaviateNetworkIntrospectPropertiesObject := graphql.NewObject(
		graphql.ObjectConfig{
			Name: "WeaviateNetworkIntrospectPropertiesObj",
			Fields: graphql.Fields{
				"propertyName": &graphql.Field{
					Type:        graphql.String,
					Description: descriptions.WherePropertiesPropertyNameDesc,
				},
				"certainty": &graphql.Field{
					Type:        graphql.Float,
					Description: descriptions.WhereCertaintyDesc,
				},
			},
			Description: descriptions.WherePropertiesObjDesc,
		},
	)

	weaviateNetworkIntrospectPropertiesObjField := &graphql.Field{
		Name:        "WeaviateNetworkIntrospectPropertiesObj",
		Description: descriptions.WherePropertiesObjDesc,
		Type:        graphql.NewList(weaviateNetworkIntrospectPropertiesObject),
		Resolve: func(p graphql.ResolveParams) (interface{}, error) {
			return nil, fmt.Errorf("not supported")
		},
	}

	return weaviateNetworkIntrospectPropertiesObjField

}

func genNetworkFetchThingsAndActionsFilterFields(filterContainer *utils.FilterContainer) graphql.InputObjectConfigFieldMap {
	networkFetchWhereInpObjPropertiesObj := genNetworkFetchWhereInpObjPropertiesObj(filterContainer)
	networkFetchWhereInpObjClassInpObj := genNetworkFetchWhereInpObjClassInpObj(filterContainer)

	networkFetchThingsAndActionsFilterFields := graphql.InputObjectConfigFieldMap{
		"class": &graphql.InputObjectFieldConfig{
			Type:        graphql.NewList(networkFetchWhereInpObjClassInpObj),
			Description: descriptions.WhereClassDesc,
		},
		"properties": &graphql.InputObjectFieldConfig{
			Type:        graphql.NewList(networkFetchWhereInpObjPropertiesObj),
			Description: descriptions.WherePropertiesDesc,
		},
		"first": &graphql.InputObjectFieldConfig{
			Type:        graphql.Int,
			Description: descriptions.FirstDesc,
		},
	}

	return networkFetchThingsAndActionsFilterFields
}

func genNetworkFetchWhereInpObjPropertiesObj(filterContainer *utils.FilterContainer) *graphql.InputObject {
	filterPropertiesElements := genNetworkStaticWhereFilterElements()

	filterPropertiesElements["certainty"] = &graphql.InputObjectFieldConfig{
		Type:        graphql.Float,
		Description: descriptions.WhereCertaintyDesc,
	}
	filterPropertiesElements["valueDate"] = &graphql.InputObjectFieldConfig{
		Type:        graphql.String,
		Description: descriptions.WhereValueDateDesc,
	}
	filterPropertiesElements["valueText"] = &graphql.InputObjectFieldConfig{
		Type:        graphql.String,
		Description: descriptions.WhereValueTextDesc,
	}
	filterPropertiesElements["name"] = &graphql.InputObjectFieldConfig{
		Type:        graphql.String,
		Description: descriptions.WhereNameDesc,
	}
	filterPropertiesElements["keywords"] = &graphql.InputObjectFieldConfig{
		Type:        graphql.NewList(genNetworkFetchWherePropertyWhereKeywordsInpObj()),
		Description: descriptions.WhereKeywordsDesc,
	}

	networkFetchWhereInpObjPropertiesObj := graphql.NewInputObject(
		graphql.InputObjectConfig{
			Name:        "WeaviateNetworkFetchWhereInpObjProperties",
			Fields:      filterPropertiesElements,
			Description: descriptions.WherePropertiesDesc,
		},
	)

	return networkFetchWhereInpObjPropertiesObj
}

func genNetworkFetchWherePropertyWhereKeywordsInpObj() *graphql.InputObject {
	outputObject := graphql.NewInputObject(
		graphql.InputObjectConfig{
			Name: "NetworkFetchWherePropertyWhereKeywordsInpObj",
			Fields: graphql.InputObjectConfigFieldMap{
				"value": &graphql.InputObjectFieldConfig{
					Type:        graphql.String,
					Description: descriptions.WhereKeywordsValueDesc,
				},
				"weight": &graphql.InputObjectFieldConfig{
					Type:        graphql.Float,
					Description: descriptions.WhereKeywordsWeightDesc,
				},
			},
			Description: descriptions.WhereKeywordsInpObjDesc,
		},
	)
	return outputObject
}

func genNetworkFetchWhereInpObjClassInpObj(filterContainer *utils.FilterContainer) *graphql.InputObject {
	filterClassElements := graphql.InputObjectConfigFieldMap{
		"name": &graphql.InputObjectFieldConfig{
			Type:        graphql.String,
			Description: descriptions.WhereNameDesc,
		},
		"certainty": &graphql.InputObjectFieldConfig{
			Type:        graphql.Float,
			Description: descriptions.WhereCertaintyDesc,
		},
		"keywords": &graphql.InputObjectFieldConfig{
			Type:        graphql.NewList(filterContainer.WeaviateNetworkWhereKeywordsInpObj),
			Description: descriptions.WhereKeywordsDesc,
		},
		"first": &graphql.InputObjectFieldConfig{
			Type:        graphql.Int,
			Description: descriptions.FirstDesc,
		},
	}

	networkFetchWhereInpObjClassInpObj := graphql.NewInputObject(
		graphql.InputObjectConfig{
			Name:        "WeaviateNetworkFetchWhereInpObjClassInpObj",
			Fields:      filterClassElements,
			Description: descriptions.WhereClassDesc,
		},
	)
	return networkFetchWhereInpObjClassInpObj
}

func genNetworkIntrospectThingsAndActionsFilterFields(filterContainer *utils.FilterContainer) graphql.InputObjectConfigFieldMap {
	weaviateNetworkIntrospectWherePropertiesObj := genWeaviateNetworkIntrospectWherePropertiesObj(filterContainer)
	weaviateNetworkIntrospectWhereClassObj := genWeaviateNetworkIntrospectWhereClassObj(filterContainer)

	networkIntrospectThingsAndActionsFilterFields := graphql.InputObjectConfigFieldMap{
		"class": &graphql.InputObjectFieldConfig{
			Type:        graphql.NewList(weaviateNetworkIntrospectWhereClassObj),
			Description: descriptions.WhereClassDesc,
		},
		"properties": &graphql.InputObjectFieldConfig{
			Type:        graphql.NewList(weaviateNetworkIntrospectWherePropertiesObj),
			Description: descriptions.WherePropertiesDesc,
		},
	}

	return networkIntrospectThingsAndActionsFilterFields
}

func genWeaviateNetworkIntrospectWherePropertiesObj(filterContainer *utils.FilterContainer) *graphql.InputObject {
	filterPropertiesElements := graphql.InputObjectConfigFieldMap{
		"first": &graphql.InputObjectFieldConfig{
			Type:        graphql.Int,
			Description: descriptions.FirstDesc,
		},
		"certainty": &graphql.InputObjectFieldConfig{
			Type:        graphql.Float,
			Description: descriptions.WhereCertaintyDesc,
		},
		"name": &graphql.InputObjectFieldConfig{
			Type:        graphql.String,
			Description: descriptions.WhereNameDesc,
		},
		"keywords": &graphql.InputObjectFieldConfig{
			Type:        graphql.NewList(filterContainer.WeaviateNetworkWhereKeywordsInpObj),
			Description: descriptions.WhereKeywordsDesc,
		},
	}

	weaviateNetworkIntrospectWherePropertiesObj := graphql.NewInputObject(
		graphql.InputObjectConfig{
			Name:        "WeaviateNetworkIntrospectWherePropertiesObj",
			Fields:      filterPropertiesElements,
			Description: descriptions.WherePropertiesObjDesc,
		},
	)

	return weaviateNetworkIntrospectWherePropertiesObj
}

func genWeaviateNetworkIntrospectWhereClassObj(filterContainer *utils.FilterContainer) *graphql.InputObject {
	filterClassElements := graphql.InputObjectConfigFieldMap{
		"name": &graphql.InputObjectFieldConfig{
			Type:        graphql.String,
			Description: descriptions.WhereNameDesc,
		},
		"certainty": &graphql.InputObjectFieldConfig{
			Type:        graphql.Float,
			Description: descriptions.WhereCertaintyDesc,
		},
		"keywords": &graphql.InputObjectFieldConfig{
			Type:        graphql.NewList(filterContainer.WeaviateNetworkWhereKeywordsInpObj),
			Description: descriptions.WhereKeywordsDesc,
		},
		"first": &graphql.InputObjectFieldConfig{
			Type:        graphql.Int,
			Description: descriptions.FirstDesc,
		},
	}

	weaviateNetworkIntrospectWhereClassObj := graphql.NewInputObject(
		graphql.InputObjectConfig{
			Name:        "WeaviateNetworkIntrospectWhereClassObj",
			Fields:      filterClassElements,
			Description: descriptions.WherePropertiesObjDesc,
		},
	)
	return weaviateNetworkIntrospectWhereClassObj
}
<commit_msg>PR wip<commit_after>/*                          _       _
 *__      _____  __ ___   ___  __ _| |_ ___
 *\ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
 * \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
 *  \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
 *
 * Copyright © 2016 - 2018 Weaviate. All rights reserved.
 * LICENSE: https://github.com/creativesoftwarefdn/weaviate/blob/develop/LICENSE.md
 * AUTHOR: Bob van Luijt (bob@kub.design)
 * See www.creativesoftwarefdn.org for details
 * Contact: @CreativeSofwFdn / bob@kub.design
 */

// Package network provides the network graphql endpoint for Weaviate
package network

import (
	"fmt"
	"github.com/creativesoftwarefdn/weaviate/graphqlapi/descriptions"
	"github.com/creativesoftwarefdn/weaviate/graphqlapi/utils"
	"github.com/graphql-go/graphql"
)

func genNetworkWhereOperatorEnum() *graphql.Enum {
	enumConf := graphql.EnumConfig{
		Name: "NetworkWhereOperatorEnum",
		Values: graphql.EnumValueConfigMap{
			"And":              &graphql.EnumValueConfig{},
			"Or":               &graphql.EnumValueConfig{},
			"Equal":            &graphql.EnumValueConfig{},
			"Not":              &graphql.EnumValueConfig{},
			"NotEqual":         &graphql.EnumValueConfig{},
			"GreaterThan":      &graphql.EnumValueConfig{},
			"GreaterThanEqual": &graphql.EnumValueConfig{},
			"LessThan":         &graphql.EnumValueConfig{},
			"LessThanEqual":    &graphql.EnumValueConfig{},
		},
		Description: descriptions.WhereOperatorEnumDesc,
	}

	return graphql.NewEnum(enumConf)
}

// generate these elements once
func genNetworkStaticWhereFilterElements() graphql.InputObjectConfigFieldMap {
	staticFilterElements := graphql.InputObjectConfigFieldMap{
		"operator": &graphql.InputObjectFieldConfig{
			Type:        genNetworkWhereOperatorEnum(),
			Description: descriptions.WhereOperatorDesc,
		},
		"valueInt": &graphql.InputObjectFieldConfig{
			Type:        graphql.Int,
			Description: descriptions.WhereValueIntDesc,
		},
		"valueNumber": &graphql.InputObjectFieldConfig{
			Type:        graphql.Float,
			Description: descriptions.WhereValueNumberDesc,
		},
		"valueBoolean": &graphql.InputObjectFieldConfig{
			Type:        graphql.Boolean,
			Description: descriptions.WhereValueBooleanDesc,
		},
		"valueString": &graphql.InputObjectFieldConfig{
			Type:        graphql.String,
			Description: descriptions.WhereValueStringDesc,
		},
	}

	return staticFilterElements
}

// This is an exact translation of the Prototype from JS to Go. In the prototype some filter elements are declared as global variables, this is recreated here.
func genGlobalNetworkFilterElements(filterContainer *utils.FilterContainer) {
	filterContainer.WeaviateNetworkWhereKeywordsInpObj = genWeaviateNetworkWhereNameKeywordsInpObj()
	filterContainer.WeaviateNetworkIntrospectPropertiesObjField = genWeaviateNetworkIntrospectPropertiesObjField()
}

func genWeaviateNetworkWhereNameKeywordsInpObj() *graphql.InputObject {
	weaviateNetworkWhereNameKeywordsInpObj := graphql.NewInputObject(
		graphql.InputObjectConfig{
			Name: "WeaviateNetworkWhereNameKeywordsInpObj",
			Fields: graphql.InputObjectConfigFieldMap{
				"value": &graphql.InputObjectFieldConfig{
					Type:        graphql.String,
					Description: descriptions.WhereKeywordsValueDesc,
				},
				"weight": &graphql.InputObjectFieldConfig{
					Type:        graphql.Float,
					Description: descriptions.WhereKeywordsWeightDesc,
				},
			},
			Description: descriptions.WhereKeywordsInpObjDesc,
		},
	)
	return weaviateNetworkWhereNameKeywordsInpObj
}

func genWeaviateNetworkIntrospectPropertiesObjField() *graphql.Field {
	weaviateNetworkIntrospectPropertiesObject := graphql.NewObject(
		graphql.ObjectConfig{
			Name: "WeaviateNetworkIntrospectPropertiesObj",
			Fields: graphql.Fields{
				"propertyName": &graphql.Field{
					Type:        graphql.String,
					Description: descriptions.WherePropertiesPropertyNameDesc,
				},
				"certainty": &graphql.Field{
					Type:        graphql.Float,
					Description: descriptions.WhereCertaintyDesc,
				},
			},
			Description: descriptions.WherePropertiesObjDesc,
		},
	)

	weaviateNetworkIntrospectPropertiesObjField := &graphql.Field{
		Name:        "WeaviateNetworkIntrospectPropertiesObj",
		Description: descriptions.WherePropertiesObjDesc,
		Type:        graphql.NewList(weaviateNetworkIntrospectPropertiesObject),
		Resolve: func(p graphql.ResolveParams) (interface{}, error) {
			return nil, fmt.Errorf("not supported")
		},
	}

	return weaviateNetworkIntrospectPropertiesObjField

}

func genNetworkFetchThingsAndActionsFilterFields(filterContainer *utils.FilterContainer) graphql.InputObjectConfigFieldMap {
	networkFetchWhereInpObjPropertiesObj := genNetworkFetchWhereInpObjPropertiesObj(filterContainer)
	networkFetchWhereInpObjClassInpObj := genNetworkFetchWhereInpObjClassInpObj(filterContainer)

	networkFetchThingsAndActionsFilterFields := graphql.InputObjectConfigFieldMap{
		"class": &graphql.InputObjectFieldConfig{
			Type:        graphql.NewList(networkFetchWhereInpObjClassInpObj),
			Description: descriptions.WhereClassDesc,
		},
		"properties": &graphql.InputObjectFieldConfig{
			Type:        graphql.NewList(networkFetchWhereInpObjPropertiesObj),
			Description: descriptions.WherePropertiesDesc,
		},
		"first": &graphql.InputObjectFieldConfig{
			Type:        graphql.Int,
			Description: descriptions.FirstDesc,
		},
	}

	return networkFetchThingsAndActionsFilterFields
}

func genNetworkFetchWhereInpObjPropertiesObj(filterContainer *utils.FilterContainer) *graphql.InputObject {
	filterPropertiesElements := genNetworkStaticWhereFilterElements()

	filterPropertiesElements["certainty"] = &graphql.InputObjectFieldConfig{
		Type:        graphql.Float,
		Description: descriptions.WhereCertaintyDesc,
	}
	filterPropertiesElements["valueDate"] = &graphql.InputObjectFieldConfig{
		Type:        graphql.String,
		Description: descriptions.WhereValueDateDesc,
	}
	filterPropertiesElements["valueText"] = &graphql.InputObjectFieldConfig{
		Type:        graphql.String,
		Description: descriptions.WhereValueTextDesc,
	}
	filterPropertiesElements["name"] = &graphql.InputObjectFieldConfig{
		Type:        graphql.String,
		Description: descriptions.WhereNameDesc,
	}
	filterPropertiesElements["keywords"] = &graphql.InputObjectFieldConfig{
		Type:        graphql.NewList(genNetworkFetchWherePropertyWhereKeywordsInpObj()),
		Description: descriptions.WhereKeywordsDesc,
	}

	networkFetchWhereInpObjPropertiesObj := graphql.NewInputObject(
		graphql.InputObjectConfig{
			Name:        "WeaviateNetworkFetchWhereInpObjProperties",
			Fields:      filterPropertiesElements,
			Description: descriptions.WherePropertiesDesc,
		},
	)

	return networkFetchWhereInpObjPropertiesObj
}

func genNetworkFetchWherePropertyWhereKeywordsInpObj() *graphql.InputObject {
	outputObject := graphql.NewInputObject(
		graphql.InputObjectConfig{
			Name: "NetworkFetchWherePropertyWhereKeywordsInpObj",
			Fields: graphql.InputObjectConfigFieldMap{
				"value": &graphql.InputObjectFieldConfig{
					Type:        graphql.String,
					Description: descriptions.WhereKeywordsValueDesc,
				},
				"weight": &graphql.InputObjectFieldConfig{
					Type:        graphql.Float,
					Description: descriptions.WhereKeywordsWeightDesc,
				},
			},
			Description: descriptions.WhereKeywordsInpObjDesc,
		},
	)
	return outputObject
}

func genNetworkFetchWhereInpObjClassInpObj(filterContainer *utils.FilterContainer) *graphql.InputObject {
	filterClassElements := graphql.InputObjectConfigFieldMap{
		"name": &graphql.InputObjectFieldConfig{
			Type:        graphql.String,
			Description: descriptions.WhereNameDesc,
		},
		"certainty": &graphql.InputObjectFieldConfig{
			Type:        graphql.Float,
			Description: descriptions.WhereCertaintyDesc,
		},
		"keywords": &graphql.InputObjectFieldConfig{
			Type:        graphql.NewList(filterContainer.WeaviateNetworkWhereKeywordsInpObj),
			Description: descriptions.WhereKeywordsDesc,
		},
		"first": &graphql.InputObjectFieldConfig{
			Type:        graphql.Int,
			Description: descriptions.FirstDesc,
		},
	}

	networkFetchWhereInpObjClassInpObj := graphql.NewInputObject(
		graphql.InputObjectConfig{
			Name:        "WeaviateNetworkFetchWhereInpObjClassInpObj",
			Fields:      filterClassElements,
			Description: descriptions.WhereClassDesc,
		},
	)
	return networkFetchWhereInpObjClassInpObj
}

func genNetworkIntrospectThingsAndActionsFilterFields(filterContainer *utils.FilterContainer) graphql.InputObjectConfigFieldMap {
	weaviateNetworkIntrospectWherePropertiesObj := genWeaviateNetworkIntrospectWherePropertiesObj(filterContainer)
	weaviateNetworkIntrospectWhereClassObj := genWeaviateNetworkIntrospectWhereClassObj(filterContainer)

	networkIntrospectThingsAndActionsFilterFields := graphql.InputObjectConfigFieldMap{
		"class": &graphql.InputObjectFieldConfig{
			Type:        graphql.NewList(weaviateNetworkIntrospectWhereClassObj),
			Description: descriptions.WhereClassDesc,
		},
		"properties": &graphql.InputObjectFieldConfig{
			Type:        graphql.NewList(weaviateNetworkIntrospectWherePropertiesObj),
			Description: descriptions.WherePropertiesDesc,
		},
	}

	return networkIntrospectThingsAndActionsFilterFields
}

func genWeaviateNetworkIntrospectWherePropertiesObj(filterContainer *utils.FilterContainer) *graphql.InputObject {
	filterPropertiesElements := graphql.InputObjectConfigFieldMap{
		"first": &graphql.InputObjectFieldConfig{
			Type:        graphql.Int,
			Description: descriptions.FirstDesc,
		},
		"certainty": &graphql.InputObjectFieldConfig{
			Type:        graphql.Float,
			Description: descriptions.WhereCertaintyDesc,
		},
		"name": &graphql.InputObjectFieldConfig{
			Type:        graphql.String,
			Description: descriptions.WhereNameDesc,
		},
		"keywords": &graphql.InputObjectFieldConfig{
			Type:        graphql.NewList(filterContainer.WeaviateNetworkWhereKeywordsInpObj),
			Description: descriptions.WhereKeywordsDesc,
		},
	}

	weaviateNetworkIntrospectWherePropertiesObj := graphql.NewInputObject(
		graphql.InputObjectConfig{
			Name:        "WeaviateNetworkIntrospectWherePropertiesObj",
			Fields:      filterPropertiesElements,
			Description: descriptions.WherePropertiesObjDesc,
		},
	)

	return weaviateNetworkIntrospectWherePropertiesObj
}

func genWeaviateNetworkIntrospectWhereClassObj(filterContainer *utils.FilterContainer) *graphql.InputObject {
	filterClassElements := graphql.InputObjectConfigFieldMap{
		"name": &graphql.InputObjectFieldConfig{
			Type:        graphql.String,
			Description: descriptions.WhereNameDesc,
		},
		"certainty": &graphql.InputObjectFieldConfig{
			Type:        graphql.Float,
			Description: descriptions.WhereCertaintyDesc,
		},
		"keywords": &graphql.InputObjectFieldConfig{
			Type:        graphql.NewList(filterContainer.WeaviateNetworkWhereKeywordsInpObj),
			Description: descriptions.WhereKeywordsDesc,
		},
		"first": &graphql.InputObjectFieldConfig{
			Type:        graphql.Int,
			Description: descriptions.FirstDesc,
		},
	}

	weaviateNetworkIntrospectWhereClassObj := graphql.NewInputObject(
		graphql.InputObjectConfig{
			Name:        "WeaviateNetworkIntrospectWhereClassObj",
			Fields:      filterClassElements,
			Description: descriptions.WherePropertiesObjDesc,
		},
	)
	return weaviateNetworkIntrospectWhereClassObj
}
<|endoftext|>


<commit_before>// Copyright 2015 Gareth Watts
// Licensed under an MIT license
// See the LICENSE file for details

/*
Command gencerts converts root CA certificates from the Mozilla NSS project to a .go file.

The program parses a certdata.txt file and extracts only those certificates that have been
labeled as trusted for use as a certificate authority.  Other certificates in the certdata.txt
file are ignored.

Without arguments, gencert reads a certdata.txt file from stdin and emits a .go file
to stdout that contains the parsed certificates along with some helper methods to access them.

The program can also download the latest certdata.txt file from the Mozilla NSS Mercurial site
(or another url using the -url option) or read and write to a specified filename using -source
and -target.

NOTE: Using -download with an https url requires that the program have access to root certificates!
The certdata format used by the NSS project is also subject to intermittant change and may cause
this program to fail.
*/
package main

import (
	"crypto/sha1"
	"flag"
	"fmt"
	"hash"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"time"

	"github.com/gwatts/rootcerts/certparse"
)

const (
	defaultDownloadURL = "http://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt"
)

var (
	packageName = flag.String("package", "main", "Name of the package to use for generated file")
	download    = flag.Bool("download", false, "Set to true to download the latest certificate data from Mozilla. See -url")
	downloadURL = flag.String("url", defaultDownloadURL, "URL to download certificate data from if -download is true")
	sourceFile  = flag.String("source", "", "Source filename to read certificate data from if -download is false.  Defaults to stdin")
	outputFile  = flag.String("target", "", "Filename to write .go output file to.  Defaults to stdout")
)

const (
	indent     = 4
	indentWrap = 64
)

var tplText = `{{define "main"}}package {{.package}}

// Generated using github.com/gwatts/rootcerts/gencert
// Generated on {{ .time }}
// Input file SHA1: {{ .filesha1 }}

import (
    "crypto/tls"
    "crypto/x509"
    "errors"
    "fmt"
    "net/http"
    "sync"
)

// TrustLevel defines for which purposes the certificate is trusted to issue
// certificates (ie. to act as a CA)
type TrustLevel int

const (
    ServerTrustedDelegator TrustLevel = 1 << iota // // Trusted for issuing server certificates
    EmailTrustedDelegator // Trusted for issuing email certificates
    CodeTrustedDelegator // Trusted for issuing code signing certificates
)

// A Cert defines a single unparsed certificate.
type Cert struct {
    Label  string
    Serial string
    Trust  TrustLevel
    DER    []byte
}

// X509Cert parses the certificate into a *x509.Certificate.
func (c *Cert) X509Cert() *x509.Certificate {
    cert, err := x509.ParseCertificate(c.DER)
    if err != nil {
        panic(fmt.Sprintf("unexpected failure parsing certificate %q/%s: %s", c.Label, c.Serial, err))
    }
    return cert
}

var serverCertPool *x509.CertPool
var serverOnce sync.Once

// ServerCertPool returns a pool containing all root CA certificates that are trusted
// for issuing server certificates.
func ServerCertPool() *x509.CertPool {
    serverOnce.Do(func() {
        serverCertPool=  x509.NewCertPool()
        for _, c := range CertsByTrust(ServerTrustedDelegator) {
            serverCertPool.AddCert(c.X509Cert())
        }
    })
    return serverCertPool
}

// CertsByTrust returns only those certificates that match all bits of
// the specified TrustLevel.
func CertsByTrust(t TrustLevel) (result []Cert) {
    for _, c := range certs {
        if c.Trust &t == t {
            result = append(result, c)
        }
    }
    return result
}

// UpdateDefaultTransport updates the configuration for http.DefaultTransport
// to use the root CA certificates defined here when used as an HTTP client.
//
// It will return an error if the DefaultTransport is not actually an *http.Transport.
func UpdateDefaultTransport() error {
    if t, ok := http.DefaultTransport.(*http.Transport); ok {
        if t.TLSClientConfig == nil {
            t.TLSClientConfig = &tls.Config{RootCAs: ServerCertPool()}
        } else {
            t.TLSClientConfig.RootCAs = ServerCertPool()
        }
    } else {
        return errors.New("http.DefaultTransport is not an *http.Transport")
    }
    return nil
}

// Certs returns all trusted certificates extracted from certdata.txt.
func Certs() []Cert {
    return certs
}

// make this unexported to avoid generating a huge documentation page.
var certs = []Cert{
{{ range .certs -}}
	{{ if ge .Cert.SerialNumber.Sign 0 -}}
    {
        Label: "{{ .Label }}",
        Serial: "{{ .Cert.SerialNumber }}",
        Trust: {{ .Trust }},
        DER: {{ .Cert.Raw | indentbytes }},
    },
	{{- end }}
{{- end }}
}
{{end}}

{{define "go1.6"}}
// +build go1.6

package {{.package}}

func init() {
	certs = append(certs, negCerts...)
}

// Certificates with a negative serial number are only supported in Go 1.6+
var negCerts = []Cert{
{{ range .certs -}}
	{{ if lt .Cert.SerialNumber.Sign 0 -}}
    {
        Label: "{{ .Label }}",
        Serial: "{{ .Cert.SerialNumber }}",
        Trust: {{ .Trust }},
        DER: {{ .Cert.Raw | indentbytes }},
    },
	{{- end }}
{{- end }}
}
{{end}}

}
`
var funcMap = template.FuncMap{
	"indentbytes": indentBytes,
}

var tpl = template.Must(template.New("data").Funcs(funcMap).Parse(tplText))

func fail(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", a...)
	os.Exit(100)
}

func indentBytes(data []byte) string {
	var out []byte
	idt := strings.Repeat(" ", indent)

	s := fmt.Sprintf("%#v", data)
	for len(s) > indentWrap {
		if sp := strings.IndexByte(s[indentWrap:], ','); sp > -1 {
			out = append(out, idt...)
			out = append(out, s[:indentWrap+sp+1]...)
			out = append(out, '\n')
			s = s[indentWrap+sp+1:]
		} else {
			break
		}
	}
	out = append(out, idt...)
	out = append(out, s...)
	return string(out)
}

type hashReader struct {
	hash.Hash
	r io.Reader
}

func (hr *hashReader) Read(p []byte) (n int, err error) {
	n, err = hr.r.Read(p)
	if n > 0 {
		hr.Hash.Write(p[0:n])
	}
	return n, err
}

func newHashReader(r io.Reader, h hash.Hash) *hashReader {
	return &hashReader{h, r}
}

func fmt16name(name string) string {
	if ext := filepath.Ext(name); ext != "" {
		return name[0:len(name)-len(ext)] + "_16" + ext
	}
	return ""
}

func hasNeg(certs []certparse.Cert) bool {
	for _, cert := range certs {
		if cert.Cert.SerialNumber.Sign() < 0 {
			return true
		}
	}
	return false
}

func main() {
	flag.Parse()

	var (
		source           io.Reader
		target, target16 io.Writer
		err              error
	)

	if *download {
		resp, err := http.Get(*downloadURL)
		if err != nil {
			fail("Failed to download source: %s", err)
		}
		if resp.StatusCode < 200 || resp.StatusCode > 299 {
			fail("Non-200 status code when downloading source: %s", resp.Status)
		}
		source = resp.Body

	} else if *sourceFile == "" || *sourceFile == "-" {
		source = os.Stdin

	} else {
		source, err = os.Open(*sourceFile)
		if err != nil {
			fail("Failed to open source file: %s", err)
		}
	}

	if *outputFile == "" || *outputFile == "-" {
		target = os.Stdout

	} else {
		target, err = os.Create(*outputFile)
		if err != nil {
			fail("Failed to open target file: %s", err)
		}
		if fn16 := fmt16name(*outputFile); fn16 != "" {
			target16, err = os.Create(fn16)
			if err != nil {
				fail("Failed to open target file: %s", err)
			}
		}

	}

	hashSource := newHashReader(source, sha1.New())

	certs, err := certparse.ReadTrustedCerts(hashSource)
	if err != nil {
		fail("Failed to read certificates: %s", err)
	}

	tplParams := map[string]interface{}{
		"package":  *packageName,
		"certs":    certs,
		"time":     time.Now().Format(time.RFC1123Z),
		"filesha1": fmt.Sprintf("%0x", hashSource.Sum(nil)),
	}

	if err = tpl.ExecuteTemplate(target, "main", tplParams); err != nil {
		fail("Template execution failed: %s", err)
	}

	if hasNeg(certs) && target16 != nil {
		if err = tpl.ExecuteTemplate(target16, "go1.6", tplParams); err != nil {
			fail("Template execution failed: %s", err)
		}
	}
}
<commit_msg>Use HTTPS to download certdata.txt from Mozilla project.<commit_after>// Copyright 2015 Gareth Watts
// Licensed under an MIT license
// See the LICENSE file for details

/*
Command gencerts converts root CA certificates from the Mozilla NSS project to a .go file.

The program parses a certdata.txt file and extracts only those certificates that have been
labeled as trusted for use as a certificate authority.  Other certificates in the certdata.txt
file are ignored.

Without arguments, gencert reads a certdata.txt file from stdin and emits a .go file
to stdout that contains the parsed certificates along with some helper methods to access them.

The program can also download the latest certdata.txt file from the Mozilla NSS Mercurial site
(or another url using the -url option) or read and write to a specified filename using -source
and -target.

NOTE: Using -download with an https url requires that the program have access to root certificates!
The certdata format used by the NSS project is also subject to intermittant change and may cause
this program to fail.
*/
package main

import (
	"crypto/sha1"
	"flag"
	"fmt"
	"hash"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"time"

	"github.com/gwatts/rootcerts/certparse"
)

const (
	defaultDownloadURL = "https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt"
)

var (
	packageName = flag.String("package", "main", "Name of the package to use for generated file")
	download    = flag.Bool("download", false, "Set to true to download the latest certificate data from Mozilla. See -url")
	downloadURL = flag.String("url", defaultDownloadURL, "URL to download certificate data from if -download is true")
	sourceFile  = flag.String("source", "", "Source filename to read certificate data from if -download is false.  Defaults to stdin")
	outputFile  = flag.String("target", "", "Filename to write .go output file to.  Defaults to stdout")
)

const (
	indent     = 4
	indentWrap = 64
)

var tplText = `{{define "main"}}package {{.package}}

// Generated using github.com/gwatts/rootcerts/gencert
// Generated on {{ .time }}
// Input file SHA1: {{ .filesha1 }}

import (
    "crypto/tls"
    "crypto/x509"
    "errors"
    "fmt"
    "net/http"
    "sync"
)

// TrustLevel defines for which purposes the certificate is trusted to issue
// certificates (ie. to act as a CA)
type TrustLevel int

const (
    ServerTrustedDelegator TrustLevel = 1 << iota // // Trusted for issuing server certificates
    EmailTrustedDelegator // Trusted for issuing email certificates
    CodeTrustedDelegator // Trusted for issuing code signing certificates
)

// A Cert defines a single unparsed certificate.
type Cert struct {
    Label  string
    Serial string
    Trust  TrustLevel
    DER    []byte
}

// X509Cert parses the certificate into a *x509.Certificate.
func (c *Cert) X509Cert() *x509.Certificate {
    cert, err := x509.ParseCertificate(c.DER)
    if err != nil {
        panic(fmt.Sprintf("unexpected failure parsing certificate %q/%s: %s", c.Label, c.Serial, err))
    }
    return cert
}

var serverCertPool *x509.CertPool
var serverOnce sync.Once

// ServerCertPool returns a pool containing all root CA certificates that are trusted
// for issuing server certificates.
func ServerCertPool() *x509.CertPool {
    serverOnce.Do(func() {
        serverCertPool=  x509.NewCertPool()
        for _, c := range CertsByTrust(ServerTrustedDelegator) {
            serverCertPool.AddCert(c.X509Cert())
        }
    })
    return serverCertPool
}

// CertsByTrust returns only those certificates that match all bits of
// the specified TrustLevel.
func CertsByTrust(t TrustLevel) (result []Cert) {
    for _, c := range certs {
        if c.Trust &t == t {
            result = append(result, c)
        }
    }
    return result
}

// UpdateDefaultTransport updates the configuration for http.DefaultTransport
// to use the root CA certificates defined here when used as an HTTP client.
//
// It will return an error if the DefaultTransport is not actually an *http.Transport.
func UpdateDefaultTransport() error {
    if t, ok := http.DefaultTransport.(*http.Transport); ok {
        if t.TLSClientConfig == nil {
            t.TLSClientConfig = &tls.Config{RootCAs: ServerCertPool()}
        } else {
            t.TLSClientConfig.RootCAs = ServerCertPool()
        }
    } else {
        return errors.New("http.DefaultTransport is not an *http.Transport")
    }
    return nil
}

// Certs returns all trusted certificates extracted from certdata.txt.
func Certs() []Cert {
    return certs
}

// make this unexported to avoid generating a huge documentation page.
var certs = []Cert{
{{ range .certs -}}
	{{ if ge .Cert.SerialNumber.Sign 0 -}}
    {
        Label: "{{ .Label }}",
        Serial: "{{ .Cert.SerialNumber }}",
        Trust: {{ .Trust }},
        DER: {{ .Cert.Raw | indentbytes }},
    },
	{{- end }}
{{- end }}
}
{{end}}

{{define "go1.6"}}
// +build go1.6

package {{.package}}

func init() {
	certs = append(certs, negCerts...)
}

// Certificates with a negative serial number are only supported in Go 1.6+
var negCerts = []Cert{
{{ range .certs -}}
	{{ if lt .Cert.SerialNumber.Sign 0 -}}
    {
        Label: "{{ .Label }}",
        Serial: "{{ .Cert.SerialNumber }}",
        Trust: {{ .Trust }},
        DER: {{ .Cert.Raw | indentbytes }},
    },
	{{- end }}
{{- end }}
}
{{end}}

}
`
var funcMap = template.FuncMap{
	"indentbytes": indentBytes,
}

var tpl = template.Must(template.New("data").Funcs(funcMap).Parse(tplText))

func fail(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", a...)
	os.Exit(100)
}

func indentBytes(data []byte) string {
	var out []byte
	idt := strings.Repeat(" ", indent)

	s := fmt.Sprintf("%#v", data)
	for len(s) > indentWrap {
		if sp := strings.IndexByte(s[indentWrap:], ','); sp > -1 {
			out = append(out, idt...)
			out = append(out, s[:indentWrap+sp+1]...)
			out = append(out, '\n')
			s = s[indentWrap+sp+1:]
		} else {
			break
		}
	}
	out = append(out, idt...)
	out = append(out, s...)
	return string(out)
}

type hashReader struct {
	hash.Hash
	r io.Reader
}

func (hr *hashReader) Read(p []byte) (n int, err error) {
	n, err = hr.r.Read(p)
	if n > 0 {
		hr.Hash.Write(p[0:n])
	}
	return n, err
}

func newHashReader(r io.Reader, h hash.Hash) *hashReader {
	return &hashReader{h, r}
}

func fmt16name(name string) string {
	if ext := filepath.Ext(name); ext != "" {
		return name[0:len(name)-len(ext)] + "_16" + ext
	}
	return ""
}

func hasNeg(certs []certparse.Cert) bool {
	for _, cert := range certs {
		if cert.Cert.SerialNumber.Sign() < 0 {
			return true
		}
	}
	return false
}

func main() {
	flag.Parse()

	var (
		source           io.Reader
		target, target16 io.Writer
		err              error
	)

	if *download {
		resp, err := http.Get(*downloadURL)
		if err != nil {
			fail("Failed to download source: %s", err)
		}
		if resp.StatusCode < 200 || resp.StatusCode > 299 {
			fail("Non-200 status code when downloading source: %s", resp.Status)
		}
		source = resp.Body

	} else if *sourceFile == "" || *sourceFile == "-" {
		source = os.Stdin

	} else {
		source, err = os.Open(*sourceFile)
		if err != nil {
			fail("Failed to open source file: %s", err)
		}
	}

	if *outputFile == "" || *outputFile == "-" {
		target = os.Stdout

	} else {
		target, err = os.Create(*outputFile)
		if err != nil {
			fail("Failed to open target file: %s", err)
		}
		if fn16 := fmt16name(*outputFile); fn16 != "" {
			target16, err = os.Create(fn16)
			if err != nil {
				fail("Failed to open target file: %s", err)
			}
		}

	}

	hashSource := newHashReader(source, sha1.New())

	certs, err := certparse.ReadTrustedCerts(hashSource)
	if err != nil {
		fail("Failed to read certificates: %s", err)
	}

	tplParams := map[string]interface{}{
		"package":  *packageName,
		"certs":    certs,
		"time":     time.Now().Format(time.RFC1123Z),
		"filesha1": fmt.Sprintf("%0x", hashSource.Sum(nil)),
	}

	if err = tpl.ExecuteTemplate(target, "main", tplParams); err != nil {
		fail("Template execution failed: %s", err)
	}

	if hasNeg(certs) && target16 != nil {
		if err = tpl.ExecuteTemplate(target16, "go1.6", tplParams); err != nil {
			fail("Template execution failed: %s", err)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package network

// Tests network performance using iperf or other containers.
import (
	"fmt"
	"math"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"k8s.io/api/core/v1"
	"k8s.io/kubernetes/test/e2e/framework"
	imageutils "k8s.io/kubernetes/test/utils/image"
)

const (
	// empirically derived as a baseline for expectations from running this test using kube-up.sh.
	gceBandwidthBitsEstimate = int64(30000000000)
	// on 4 node clusters, we found this test passes very quickly, generally in less then 100 seconds.
	smallClusterTimeout = 200 * time.Second
)

// Declared as Flakey since it has not been proven to run in parallel on small nodes or slow networks in CI
// TODO jayunit100 : Retag this test according to semantics from #22401
var _ = SIGDescribe("Networking IPerf [Experimental] [Slow] [Feature:Networking-Performance]", func() {

	f := framework.NewDefaultFramework("network-perf")

	// A few simple bandwidth tests which are capped by nodes.
	// TODO replace the 1 with the scale option implementation
	// TODO: Make this a function parameter, once we distribute iperf endpoints, possibly via session affinity.
	numClient := 1
	numServer := 1
	maxBandwidthBits := gceBandwidthBitsEstimate

	It(fmt.Sprintf("should transfer ~ 1GB onto the service endpoint %v servers (maximum of %v clients)", numServer, numClient), func() {
		nodes := framework.GetReadySchedulableNodesOrDie(f.ClientSet)
		totalPods := len(nodes.Items)
		// for a single service, we expect to divide bandwidth between the network.  Very crude estimate.
		expectedBandwidth := int(float64(maxBandwidthBits) / float64(totalPods))
		Expect(totalPods).NotTo(Equal(0))
		appName := "iperf-e2e"
		err, _ := f.CreateServiceForSimpleAppWithPods(
			8001,
			8002,
			appName,
			func(n v1.Node) v1.PodSpec {
				return v1.PodSpec{
					Containers: []v1.Container{{
						Name:  "iperf-server",
						Image: imageutils.GetE2EImage(imageutils.Iperf),
						Args: []string{
							"/bin/sh",
							"-c",
							"/usr/local/bin/iperf -s -p 8001 ",
						},
						Ports: []v1.ContainerPort{{ContainerPort: 8001}},
					}},
					NodeName:      n.Name,
					RestartPolicy: v1.RestartPolicyOnFailure,
				}
			},
			// this will be used to generate the -service name which all iperf clients point at.
			numServer, // Generally should be 1 server unless we do affinity or use a version of iperf that supports LB
			true,      // Make sure we wait, otherwise all the clients will die and need to restart.
		)

		if err != nil {
			framework.Failf("Fatal error waiting for iperf server endpoint : %v", err)
		}

		iperfClientPodLabels := f.CreatePodsPerNodeForSimpleApp(
			"iperf-e2e-cli",
			func(n v1.Node) v1.PodSpec {
				return v1.PodSpec{
					Containers: []v1.Container{
						{
							Name:  "iperf-client",
							Image: imageutils.GetE2EImage(imageutils.Iperf),
							Args: []string{
								"/bin/sh",
								"-c",
								"/usr/local/bin/iperf -c service-for-" + appName + " -p 8002 --reportstyle C && sleep 5",
							},
						},
					},
					RestartPolicy: v1.RestartPolicyOnFailure, // let them successfully die.
				}
			},
			numClient,
		)

		framework.Logf("Reading all perf results to stdout.")
		framework.Logf("date,cli,cliPort,server,serverPort,id,interval,transferBits,bandwidthBits")

		// Calculate expected number of clients based on total nodes.
		expectedCli := func() int {
			nodes := framework.GetReadySchedulableNodesOrDie(f.ClientSet)
			return int(math.Min(float64(len(nodes.Items)), float64(numClient)))
		}()

		// Extra 1/10 second per client.
		iperfTimeout := smallClusterTimeout + (time.Duration(expectedCli/10) * time.Second)
		iperfResults := &IPerfResults{}

		iperfClusterVerification := f.NewClusterVerification(
			f.Namespace,
			framework.PodStateVerification{
				Selectors:   iperfClientPodLabels,
				ValidPhases: []v1.PodPhase{v1.PodSucceeded},
			},
		)

		pods, err2 := iperfClusterVerification.WaitFor(expectedCli, iperfTimeout)
		if err2 != nil {
			framework.Failf("Error in wait...")
		} else if len(pods) < expectedCli {
			framework.Failf("IPerf restuls : Only got %v out of %v, after waiting %v", len(pods), expectedCli, iperfTimeout)
		} else {
			// For each builds up a collection of IPerfRecords
			iperfClusterVerification.ForEach(
				func(p v1.Pod) {
					resultS, err := framework.LookForStringInLog(f.Namespace.Name, p.Name, "iperf-client", "0-", 1*time.Second)
					if err == nil {
						framework.Logf(resultS)
						iperfResults.Add(NewIPerf(resultS))
					} else {
						framework.Failf("Unexpected error, %v when running forEach on the pods.", err)
					}
				})
		}
		fmt.Println("[begin] Node,Bandwith CSV")
		fmt.Println(iperfResults.ToTSV())
		fmt.Println("[end] Node,Bandwith CSV")

		for ipClient, bandwidth := range iperfResults.BandwidthMap {
			framework.Logf("%v had bandwidth %v.  Ratio to expected (%v) was %f", ipClient, bandwidth, expectedBandwidth, float64(bandwidth)/float64(expectedBandwidth))
		}
	})
})
<commit_msg>Add IPv6 option for e2e iPerf test<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package network

// Tests network performance using iperf or other containers.
import (
	"fmt"
	"math"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"k8s.io/api/core/v1"
	"k8s.io/kubernetes/test/e2e/framework"
	imageutils "k8s.io/kubernetes/test/utils/image"
)

const (
	// empirically derived as a baseline for expectations from running this test using kube-up.sh.
	gceBandwidthBitsEstimate = int64(30000000000)
	// on 4 node clusters, we found this test passes very quickly, generally in less then 100 seconds.
	smallClusterTimeout = 200 * time.Second
)

// networkingIPerf test runs iperf on a container in either IPv4 or IPv6 mode.
func networkingIPerfTest(isIPv6 bool) {

	f := framework.NewDefaultFramework("network-perf")

	// A few simple bandwidth tests which are capped by nodes.
	// TODO replace the 1 with the scale option implementation
	// TODO: Make this a function parameter, once we distribute iperf endpoints, possibly via session affinity.
	numClient := 1
	numServer := 1
	maxBandwidthBits := gceBandwidthBitsEstimate

	familyStr := ""
	if isIPv6 {
		familyStr = "-V "
	}

	It(fmt.Sprintf("should transfer ~ 1GB onto the service endpoint %v servers (maximum of %v clients)", numServer, numClient), func() {
		nodes := framework.GetReadySchedulableNodesOrDie(f.ClientSet)
		totalPods := len(nodes.Items)
		// for a single service, we expect to divide bandwidth between the network.  Very crude estimate.
		expectedBandwidth := int(float64(maxBandwidthBits) / float64(totalPods))
		Expect(totalPods).NotTo(Equal(0))
		appName := "iperf-e2e"
		err, _ := f.CreateServiceForSimpleAppWithPods(
			8001,
			8002,
			appName,
			func(n v1.Node) v1.PodSpec {
				return v1.PodSpec{
					Containers: []v1.Container{{
						Name:  "iperf-server",
						Image: imageutils.GetE2EImage(imageutils.Iperf),
						Args: []string{
							"/bin/sh",
							"-c",
							"/usr/local/bin/iperf " + familyStr + "-s -p 8001 ",
						},
						Ports: []v1.ContainerPort{{ContainerPort: 8001}},
					}},
					NodeName:      n.Name,
					RestartPolicy: v1.RestartPolicyOnFailure,
				}
			},
			// this will be used to generate the -service name which all iperf clients point at.
			numServer, // Generally should be 1 server unless we do affinity or use a version of iperf that supports LB
			true,      // Make sure we wait, otherwise all the clients will die and need to restart.
		)

		if err != nil {
			framework.Failf("Fatal error waiting for iperf server endpoint : %v", err)
		}

		iperfClientPodLabels := f.CreatePodsPerNodeForSimpleApp(
			"iperf-e2e-cli",
			func(n v1.Node) v1.PodSpec {
				return v1.PodSpec{
					Containers: []v1.Container{
						{
							Name:  "iperf-client",
							Image: imageutils.GetE2EImage(imageutils.Iperf),
							Args: []string{
								"/bin/sh",
								"-c",
								"/usr/local/bin/iperf " + familyStr + "-c service-for-" + appName + " -p 8002 --reportstyle C && sleep 5",
							},
						},
					},
					RestartPolicy: v1.RestartPolicyOnFailure, // let them successfully die.
				}
			},
			numClient,
		)

		framework.Logf("Reading all perf results to stdout.")
		framework.Logf("date,cli,cliPort,server,serverPort,id,interval,transferBits,bandwidthBits")

		// Calculate expected number of clients based on total nodes.
		expectedCli := func() int {
			nodes := framework.GetReadySchedulableNodesOrDie(f.ClientSet)
			return int(math.Min(float64(len(nodes.Items)), float64(numClient)))
		}()

		// Extra 1/10 second per client.
		iperfTimeout := smallClusterTimeout + (time.Duration(expectedCli/10) * time.Second)
		iperfResults := &IPerfResults{}

		iperfClusterVerification := f.NewClusterVerification(
			f.Namespace,
			framework.PodStateVerification{
				Selectors:   iperfClientPodLabels,
				ValidPhases: []v1.PodPhase{v1.PodSucceeded},
			},
		)

		pods, err2 := iperfClusterVerification.WaitFor(expectedCli, iperfTimeout)
		if err2 != nil {
			framework.Failf("Error in wait...")
		} else if len(pods) < expectedCli {
			framework.Failf("IPerf restuls : Only got %v out of %v, after waiting %v", len(pods), expectedCli, iperfTimeout)
		} else {
			// For each builds up a collection of IPerfRecords
			iperfClusterVerification.ForEach(
				func(p v1.Pod) {
					resultS, err := framework.LookForStringInLog(f.Namespace.Name, p.Name, "iperf-client", "0-", 1*time.Second)
					if err == nil {
						framework.Logf(resultS)
						iperfResults.Add(NewIPerf(resultS))
					} else {
						framework.Failf("Unexpected error, %v when running forEach on the pods.", err)
					}
				})
		}
		fmt.Println("[begin] Node,Bandwith CSV")
		fmt.Println(iperfResults.ToTSV())
		fmt.Println("[end] Node,Bandwith CSV")

		for ipClient, bandwidth := range iperfResults.BandwidthMap {
			framework.Logf("%v had bandwidth %v.  Ratio to expected (%v) was %f", ipClient, bandwidth, expectedBandwidth, float64(bandwidth)/float64(expectedBandwidth))
		}
	})
}

// Declared as Flakey since it has not been proven to run in parallel on small nodes or slow networks in CI
// TODO jayunit100 : Retag this test according to semantics from #22401
var _ = SIGDescribe("Networking IPerf IPv4 [Experimental] [Feature:Networking-IPv4] [Slow] [Feature:Networking-Performance]", func() {
	networkingIPerfTest(false)
})

// Declared as Flakey since it has not been proven to run in parallel on small nodes or slow networks in CI
// TODO jayunit100 : Retag this test according to semantics from #22401
var _ = SIGDescribe("Networking IPerf IPv6 [Experimental] [Feature:Networking-IPv6] [Slow] [Feature:Networking-Performance]", func() {
	networkingIPerfTest(true)
})
<|endoftext|>