<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package e2e_federation

import (
	"fmt"
	"strings"
	"time"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	clientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset/typed/core/v1"
	"k8s.io/kubernetes/pkg/api/v1"
	api_v1 "k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/test/e2e/framework"
	fedframework "k8s.io/kubernetes/test/e2e_federation/framework"

	. "github.com/onsi/ginkgo"
)

const (
	namespacePrefix = "e2e-namespace-test-"
	eventNamePrefix = "e2e-namespace-test-event-"
)

// Create/delete ingress api objects
var _ = framework.KubeDescribe("Federation namespace [Feature:Federation]", func() {
	f := fedframework.NewDefaultFederatedFramework("federation-namespace")

	Describe("Namespace objects", func() {
		var clusters map[string]*cluster // All clusters, keyed by cluster name

		BeforeEach(func() {
			fedframework.SkipUnlessFederated(f.ClientSet)
			clusters, _ = getRegisteredClusters(UserAgentName, f)
		})

		AfterEach(func() {
			fedframework.SkipUnlessFederated(f.ClientSet)
			deleteAllTestNamespaces(nil,
				f.FederationClientset.Core().Namespaces().List,
				f.FederationClientset.Core().Namespaces().Delete)
			for _, cluster := range clusters {
				deleteAllTestNamespaces(nil,
					cluster.Core().Namespaces().List,
					cluster.Core().Namespaces().Delete)
			}
		})

		It("should be created and deleted successfully", func() {
			fedframework.SkipUnlessFederated(f.ClientSet)

			nsName := createNamespace(f.FederationClientset.Core().Namespaces())

			By(fmt.Sprintf("Deleting namespace %s", nsName))
			deleteAllTestNamespaces(nil,
				f.FederationClientset.Core().Namespaces().List,
				f.FederationClientset.Core().Namespaces().Delete)
			By(fmt.Sprintf("Verified that deletion succeeded"))
		})

		It("should be deleted from underlying clusters when OrphanDependents is false", func() {
			fedframework.SkipUnlessFederated(f.ClientSet)
			orphanDependents := false
			verifyNsCascadingDeletion(f.FederationClientset.Core().Namespaces(), clusters, &orphanDependents)
			By(fmt.Sprintf("Verified that namespaces were deleted from underlying clusters"))
		})

		It("should not be deleted from underlying clusters when OrphanDependents is true", func() {
			fedframework.SkipUnlessFederated(f.ClientSet)
			orphanDependents := true
			verifyNsCascadingDeletion(f.FederationClientset.Core().Namespaces(), clusters, &orphanDependents)
			By(fmt.Sprintf("Verified that namespaces were not deleted from underlying clusters"))
		})

		It("should not be deleted from underlying clusters when OrphanDependents is nil", func() {
			fedframework.SkipUnlessFederated(f.ClientSet)

			verifyNsCascadingDeletion(f.FederationClientset.Core().Namespaces(), clusters, nil)
			By(fmt.Sprintf("Verified that namespaces were not deleted from underlying clusters"))
		})

		It("all resources in the namespace should be deleted when namespace is deleted", func() {
			fedframework.SkipUnlessFederated(f.ClientSet)

			nsName := createNamespace(f.FederationClientset.Core().Namespaces())

			// Create resources in the namespace.
			event := api_v1.Event{
				ObjectMeta: metav1.ObjectMeta{
					Name:      v1.SimpleNameGenerator.GenerateName(eventNamePrefix),
					Namespace: nsName,
				},
				InvolvedObject: api_v1.ObjectReference{
					Kind:      "Pod",
					Namespace: nsName,
					Name:      "sample-pod",
				},
			}
			By(fmt.Sprintf("Creating event %s in namespace %s", event.Name, nsName))
			_, err := f.FederationClientset.Core().Events(nsName).Create(&event)
			if err != nil {
				framework.Failf("Failed to create event %v in namespace %s, err: %s", event, nsName, err)
			}

			By(fmt.Sprintf("Deleting namespace %s", nsName))
			deleteAllTestNamespaces(nil,
				f.FederationClientset.Core().Namespaces().List,
				f.FederationClientset.Core().Namespaces().Delete)

			By(fmt.Sprintf("Verify that event %s was deleted as well", event.Name))
			latestEvent, err := f.FederationClientset.Core().Events(nsName).Get(event.Name, metav1.GetOptions{})
			if !errors.IsNotFound(err) {
				framework.Failf("Event %s should have been deleted. Found: %v", event.Name, latestEvent)
			}
			By(fmt.Sprintf("Verified that deletion succeeded"))
		})
	})
})

// verifyNsCascadingDeletion verifies that namespaces are deleted from
// underlying clusters when orphan dependents is false and they are not
// deleted when orphan dependents is true.
func verifyNsCascadingDeletion(nsClient clientset.NamespaceInterface, clusters map[string]*cluster, orphanDependents *bool) {
	nsName := createNamespace(nsClient)
	// Check subclusters if the namespace was created there.
	By(fmt.Sprintf("Waiting for namespace %s to be created in all underlying clusters", nsName))
	err := wait.Poll(5*time.Second, 2*time.Minute, func() (bool, error) {
		for _, cluster := range clusters {
			_, err := cluster.Core().Namespaces().Get(nsName, metav1.GetOptions{})
			if err != nil && !errors.IsNotFound(err) {
				return false, err
			}
			if err != nil {
				return false, nil
			}
		}
		return true, nil
	})
	framework.ExpectNoError(err, "Not all namespaces created")

	By(fmt.Sprintf("Deleting namespace %s", nsName))
	deleteAllTestNamespaces(orphanDependents, nsClient.List, nsClient.Delete)

	By(fmt.Sprintf("Verifying namespaces %s in underlying clusters", nsName))
	errMessages := []string{}
	// namespace should be present in underlying clusters unless orphanDependents is false.
	shouldExist := orphanDependents == nil || *orphanDependents == true
	for clusterName, clusterClientset := range clusters {
		_, err := clusterClientset.Core().Namespaces().Get(nsName, metav1.GetOptions{})
		if shouldExist && errors.IsNotFound(err) {
			errMessages = append(errMessages, fmt.Sprintf("unexpected NotFound error for namespace %s in cluster %s, expected namespace to exist", nsName, clusterName))
		} else if !shouldExist && !errors.IsNotFound(err) {
			errMessages = append(errMessages, fmt.Sprintf("expected NotFound error for namespace %s in cluster %s, got error: %v", nsName, clusterName, err))
		}
	}
	if len(errMessages) != 0 {
		framework.Failf("%s", strings.Join(errMessages, "; "))
	}
}

func createNamespace(nsClient clientset.NamespaceInterface) string {
	ns := api_v1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name: v1.SimpleNameGenerator.GenerateName(namespacePrefix),
		},
	}
	By(fmt.Sprintf("Creating namespace %s", ns.Name))
	_, err := nsClient.Create(&ns)
	framework.ExpectNoError(err, "Failed to create namespace %s", ns.Name)
	By(fmt.Sprintf("Created namespace %s", ns.Name))
	return ns.Name
}

func deleteAllTestNamespaces(orphanDependents *bool, lister func(metav1.ListOptions) (*api_v1.NamespaceList, error), deleter func(string, *metav1.DeleteOptions) error) {
	list, err := lister(metav1.ListOptions{})
	if err != nil {
		framework.Failf("Failed to get all namespaes: %v", err)
		return
	}
	for _, namespace := range list.Items {
		if strings.HasPrefix(namespace.Name, namespacePrefix) {
			By(fmt.Sprintf("Deleting ns: %s, found by listing", namespace.Name))
			err := deleter(namespace.Name, &metav1.DeleteOptions{OrphanDependents: orphanDependents})
			if err != nil {
				framework.Failf("Failed to set %s for deletion: %v", namespace.Name, err)
			}
		}
	}
	waitForNoTestNamespaces(lister)
}

func waitForNoTestNamespaces(lister func(metav1.ListOptions) (*api_v1.NamespaceList, error)) {
	err := wait.Poll(5*time.Second, 2*time.Minute, func() (bool, error) {
		list, err := lister(metav1.ListOptions{})
		if err != nil {
			return false, err
		}
		for _, namespace := range list.Items {
			if strings.HasPrefix(namespace.Name, namespacePrefix) {
				return false, nil
			}
		}
		return true, nil
	})
	if err != nil {
		framework.Failf("Namespaces not deleted: %v", err)
	}
}
<commit_msg>[Federation] Make federation namespace e2e tests parallelizable.<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package e2e_federation

import (
	"fmt"
	"strings"
	"time"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	clientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset/typed/core/v1"
	"k8s.io/kubernetes/pkg/api/v1"
	api_v1 "k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/test/e2e/framework"
	fedframework "k8s.io/kubernetes/test/e2e_federation/framework"

	. "github.com/onsi/ginkgo"
)

const (
	namespacePrefix = "e2e-namespace-test-"
	eventNamePrefix = "e2e-namespace-test-event-"
)

// Create/delete ingress api objects
var _ = framework.KubeDescribe("Federation namespace [Feature:Federation]", func() {
	f := fedframework.NewDefaultFederatedFramework("federation-namespace")

	Describe("Namespace objects", func() {
		var clusters map[string]*cluster // All clusters, keyed by cluster name

		var nsName string

		BeforeEach(func() {
			fedframework.SkipUnlessFederated(f.ClientSet)
			clusters, _ = getRegisteredClusters(UserAgentName, f)
		})

		AfterEach(func() {
			fedframework.SkipUnlessFederated(f.ClientSet)
			deleteNamespace(nil, nsName,
				f.FederationClientset.Core().Namespaces().Get,
				f.FederationClientset.Core().Namespaces().Delete)
			for _, cluster := range clusters {
				deleteNamespace(nil, nsName,
					cluster.Core().Namespaces().Get,
					cluster.Core().Namespaces().Delete)
			}
		})

		It("should be created and deleted successfully", func() {
			fedframework.SkipUnlessFederated(f.ClientSet)

			nsName = createNamespace(f.FederationClientset.Core().Namespaces())

			By(fmt.Sprintf("Deleting namespace %s", nsName))
			deleteNamespace(nil, nsName,
				f.FederationClientset.Core().Namespaces().Get,
				f.FederationClientset.Core().Namespaces().Delete)
			By(fmt.Sprintf("Verified that deletion succeeded"))
		})

		It("should be deleted from underlying clusters when OrphanDependents is false", func() {
			fedframework.SkipUnlessFederated(f.ClientSet)
			orphanDependents := false
			nsName = verifyNsCascadingDeletion(f.FederationClientset.Core().Namespaces(), clusters, &orphanDependents)
			By(fmt.Sprintf("Verified that namespaces were deleted from underlying clusters"))
		})

		It("should not be deleted from underlying clusters when OrphanDependents is true", func() {
			fedframework.SkipUnlessFederated(f.ClientSet)
			orphanDependents := true
			nsName = verifyNsCascadingDeletion(f.FederationClientset.Core().Namespaces(), clusters, &orphanDependents)
			By(fmt.Sprintf("Verified that namespaces were not deleted from underlying clusters"))
		})

		It("should not be deleted from underlying clusters when OrphanDependents is nil", func() {
			fedframework.SkipUnlessFederated(f.ClientSet)

			nsName = verifyNsCascadingDeletion(f.FederationClientset.Core().Namespaces(), clusters, nil)
			By(fmt.Sprintf("Verified that namespaces were not deleted from underlying clusters"))
		})

		It("all resources in the namespace should be deleted when namespace is deleted", func() {
			fedframework.SkipUnlessFederated(f.ClientSet)

			nsName = createNamespace(f.FederationClientset.Core().Namespaces())

			// Create resources in the namespace.
			event := api_v1.Event{
				ObjectMeta: metav1.ObjectMeta{
					Name:      v1.SimpleNameGenerator.GenerateName(eventNamePrefix),
					Namespace: nsName,
				},
				InvolvedObject: api_v1.ObjectReference{
					Kind:      "Pod",
					Namespace: nsName,
					Name:      "sample-pod",
				},
			}
			By(fmt.Sprintf("Creating event %s in namespace %s", event.Name, nsName))
			_, err := f.FederationClientset.Core().Events(nsName).Create(&event)
			if err != nil {
				framework.Failf("Failed to create event %v in namespace %s, err: %s", event, nsName, err)
			}

			By(fmt.Sprintf("Deleting namespace %s", nsName))
			deleteNamespace(nil, nsName,
				f.FederationClientset.Core().Namespaces().Get,
				f.FederationClientset.Core().Namespaces().Delete)

			By(fmt.Sprintf("Verify that event %s was deleted as well", event.Name))
			latestEvent, err := f.FederationClientset.Core().Events(nsName).Get(event.Name, metav1.GetOptions{})
			if !errors.IsNotFound(err) {
				framework.Failf("Event %s should have been deleted. Found: %v", event.Name, latestEvent)
			}
			By(fmt.Sprintf("Verified that deletion succeeded"))
		})
	})
})

// verifyNsCascadingDeletion verifies that namespaces are deleted from
// underlying clusters when orphan dependents is false and they are not
// deleted when orphan dependents is true.
func verifyNsCascadingDeletion(nsClient clientset.NamespaceInterface, clusters map[string]*cluster, orphanDependents *bool) string {
	nsName := createNamespace(nsClient)
	// Check subclusters if the namespace was created there.
	By(fmt.Sprintf("Waiting for namespace %s to be created in all underlying clusters", nsName))
	err := wait.Poll(5*time.Second, 2*time.Minute, func() (bool, error) {
		for _, cluster := range clusters {
			_, err := cluster.Core().Namespaces().Get(nsName, metav1.GetOptions{})
			if err != nil && !errors.IsNotFound(err) {
				return false, err
			}
			if err != nil {
				return false, nil
			}
		}
		return true, nil
	})
	framework.ExpectNoError(err, "Not all namespaces created")

	By(fmt.Sprintf("Deleting namespace %s", nsName))
	deleteNamespace(orphanDependents, nsName, nsClient.Get, nsClient.Delete)

	By(fmt.Sprintf("Verifying namespaces %s in underlying clusters", nsName))
	errMessages := []string{}
	// namespace should be present in underlying clusters unless orphanDependents is false.
	shouldExist := orphanDependents == nil || *orphanDependents == true
	for clusterName, clusterClientset := range clusters {
		_, err := clusterClientset.Core().Namespaces().Get(nsName, metav1.GetOptions{})
		if shouldExist && errors.IsNotFound(err) {
			errMessages = append(errMessages, fmt.Sprintf("unexpected NotFound error for namespace %s in cluster %s, expected namespace to exist", nsName, clusterName))
		} else if !shouldExist && !errors.IsNotFound(err) {
			errMessages = append(errMessages, fmt.Sprintf("expected NotFound error for namespace %s in cluster %s, got error: %v", nsName, clusterName, err))
		}
	}
	if len(errMessages) != 0 {
		framework.Failf("%s", strings.Join(errMessages, "; "))
	}
	return nsName
}

func createNamespace(nsClient clientset.NamespaceInterface) string {
	ns := api_v1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name: v1.SimpleNameGenerator.GenerateName(namespacePrefix),
		},
	}
	By(fmt.Sprintf("Creating namespace %s", ns.Name))
	_, err := nsClient.Create(&ns)
	framework.ExpectNoError(err, "Failed to create namespace %s", ns.Name)
	By(fmt.Sprintf("Created namespace %s", ns.Name))
	return ns.Name
}

func deleteNamespace(orphanDependents *bool, namespace string, getter func(name string, options metav1.GetOptions) (*v1.Namespace, error), deleter func(string, *metav1.DeleteOptions) error) {
	By(fmt.Sprintf("Deleting namespace: %s", namespace))
	err := deleter(namespace, &metav1.DeleteOptions{OrphanDependents: orphanDependents})
	if errors.IsNotFound(err) {
		return
	} else if err != nil {
		framework.Failf("Failed to set %s for deletion: %v", namespace, err)
	}
	waitForNamespaceDeletion(namespace, getter)
}

func waitForNamespaceDeletion(namespace string, getter func(name string, options metav1.GetOptions) (*v1.Namespace, error)) {
	err := wait.Poll(5*time.Second, 2*time.Minute, func() (bool, error) {
		_, err := getter(namespace, metav1.GetOptions{})
		if err != nil && !errors.IsNotFound(err) {
			return false, err
		}
		return true, nil
	})
	if err != nil {
		framework.Failf("Namespaces not deleted: %v", err)
	}
}
<|endoftext|>


<commit_before>package builds

import (
	"context"
	"time"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"

	exutil "github.com/openshift/origin/test/extended/util"
)

var _ = g.Describe("[sig-builds][Feature:Builds] remove all builds when build configuration is removed", func() {
	defer g.GinkgoRecover()
	var (
		buildFixture = exutil.FixturePath("testdata", "builds", "test-build.yaml")
		oc           = exutil.NewCLI("cli-remove-build")
	)

	g.Context("", func() {
		g.BeforeEach(func() {
			exutil.PreTestDump()
		})

		g.JustBeforeEach(func() {
			oc.Run("create").Args("-f", buildFixture).Execute()
		})

		g.AfterEach(func() {
			if g.CurrentGinkgoTestDescription().Failed {
				exutil.DumpPodStates(oc)
				exutil.DumpConfigMapStates(oc)
				exutil.DumpPodLogsStartingWith("", oc)
			}
		})

		g.Describe("oc delete buildconfig", func() {
			g.It("should start builds and delete the buildconfig", func() {
				var (
					err    error
					builds [4]string
				)

				g.By("starting multiple builds")
				for i := range builds {
					stdout, _, err := exutil.StartBuild(oc, "sample-build", "-o=name")
					o.Expect(err).NotTo(o.HaveOccurred())
					builds[i] = stdout
				}

				g.By("deleting the buildconfig")
				err = oc.Run("delete").Args("bc/sample-build").Execute()
				o.Expect(err).NotTo(o.HaveOccurred())

				g.By("waiting for builds to clear")
				err = wait.Poll(3*time.Second, 3*time.Minute, func() (bool, error) {
					builds, err := oc.BuildClient().BuildV1().Builds(oc.Namespace()).List(context.Background(), metav1.ListOptions{})
					o.Expect(err).NotTo(o.HaveOccurred())
					if len(builds.Items) > 0 {
						return false, nil
					}
					configMaps, err := oc.KubeClient().CoreV1().ConfigMaps(oc.Namespace()).List(context.Background(), metav1.ListOptions{})
					o.Expect(err).NotTo(o.HaveOccurred())
					if len(configMaps.Items) > 0 {
						return false, nil
					}
					return true, nil
				})
				if err == wait.ErrWaitTimeout {
					g.Fail("timed out waiting for builds to clear")
				}
			})

		})
	})
})
<commit_msg>Fix build test CM count<commit_after>package builds

import (
	"context"
	"time"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"

	exutil "github.com/openshift/origin/test/extended/util"
)

var _ = g.Describe("[sig-builds][Feature:Builds] remove all builds when build configuration is removed", func() {
	defer g.GinkgoRecover()
	var (
		buildFixture = exutil.FixturePath("testdata", "builds", "test-build.yaml")
		oc           = exutil.NewCLI("cli-remove-build")
	)

	g.Context("", func() {
		g.BeforeEach(func() {
			exutil.PreTestDump()
		})

		g.JustBeforeEach(func() {
			oc.Run("create").Args("-f", buildFixture).Execute()
		})

		g.AfterEach(func() {
			if g.CurrentGinkgoTestDescription().Failed {
				exutil.DumpPodStates(oc)
				exutil.DumpConfigMapStates(oc)
				exutil.DumpPodLogsStartingWith("", oc)
			}
		})

		g.Describe("oc delete buildconfig", func() {
			g.It("should start builds and delete the buildconfig", func() {
				var (
					err    error
					builds [4]string
				)
				configMaps, err := oc.KubeClient().CoreV1().ConfigMaps(oc.Namespace()).List(context.Background(), metav1.ListOptions{})
				o.Expect(err).NotTo(o.HaveOccurred())
				initialConfigMapCount := len(configMaps.Items)

				g.By("starting multiple builds")
				for i := range builds {
					stdout, _, err := exutil.StartBuild(oc, "sample-build", "-o=name")
					o.Expect(err).NotTo(o.HaveOccurred())
					builds[i] = stdout
				}

				g.By("deleting the buildconfig")
				err = oc.Run("delete").Args("bc/sample-build").Execute()
				o.Expect(err).NotTo(o.HaveOccurred())

				g.By("waiting for builds to clear")
				err = wait.Poll(3*time.Second, 3*time.Minute, func() (bool, error) {
					builds, err := oc.BuildClient().BuildV1().Builds(oc.Namespace()).List(context.Background(), metav1.ListOptions{})
					o.Expect(err).NotTo(o.HaveOccurred())
					if len(builds.Items) > 0 {
						return false, nil
					}
					configMaps, err := oc.KubeClient().CoreV1().ConfigMaps(oc.Namespace()).List(context.Background(), metav1.ListOptions{})
					o.Expect(err).NotTo(o.HaveOccurred())
					if len(configMaps.Items) > initialConfigMapCount {
						return false, nil
					}
					return true, nil
				})
				if err == wait.ErrWaitTimeout {
					g.Fail("timed out waiting for builds to clear")
				}
			})

		})
	})
})
<|endoftext|>


<commit_before>/*
Copyright 2019 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 guestbook

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/spf13/cobra"

	utilnet "k8s.io/apimachinery/pkg/util/net"
)

// CmdGuestbook is used by agnhost Cobra.
var CmdGuestbook = &cobra.Command{
	Use:   "guestbook",
	Short: "Creates a HTTP server with various endpoints representing a guestbook app",
	Long: `Starts a HTTP server on the given --http-port (default: 80), serving various endpoints representing a guestbook app. The endpoints and their purpose are:

- /register: A guestbook slave will subscribe to a master, to its given --slaveof endpoint. The master will then push any updates it receives to its registered slaves through the --backend-port.
- /get: Returns '{"data": value}', where the value is the stored value for the given key if non-empty, or the entire store.
- /set: Will set the given key-value pair in its own store and propagate it to its slaves, if any. Will return '{"data": "Updated"}' to the caller on success.
- /guestbook: Will proxy the request to agnhost-master if the given cmd is 'set', or agnhost-slave if the given cmd is 'get'.`,
	Args: cobra.MaximumNArgs(0),
	Run:  main,
}

var (
	httpPort    string
	backendPort string
	slaveOf     string
	slaves      []string
	store       map[string]interface{}
)

const (
	timeout = time.Duration(15) * time.Second
	sleep   = time.Duration(1) * time.Second
)

func init() {
	CmdGuestbook.Flags().StringVar(&httpPort, "http-port", "80", "HTTP Listen Port")
	CmdGuestbook.Flags().StringVar(&backendPort, "backend-port", "6379", "Backend's HTTP Listen Port")
	CmdGuestbook.Flags().StringVar(&slaveOf, "slaveof", "", "The host's name to register to")
	store = make(map[string]interface{})
}

func main(cmd *cobra.Command, args []string) {
	go registerNode(slaveOf, backendPort)
	startHTTPServer(httpPort)
}

func registerNode(registerTo, port string) {
	if registerTo == "" {
		return
	}

	hostPort := net.JoinHostPort(registerTo, backendPort)

	request := fmt.Sprintf("register?host=%s", getIP(hostPort).String())
	log.Printf("Registering to master: %s/%s", hostPort, request)
	start := time.Now()
	for time.Since(start) < timeout {
		_, err := net.ResolveTCPAddr("tcp", hostPort)
		if err != nil {
			log.Printf("unable to resolve %s, --slaveof param and/or --backend-port param are invalid: %v. Retrying in %s.", hostPort, err, sleep)
			time.Sleep(sleep)
			continue
		}

		response, err := dialHTTP(request, hostPort)
		if err != nil {
			log.Printf("encountered error while registering to master: %v. Retrying in %s.", err, sleep)
			time.Sleep(sleep)
			continue
		}

		responseJSON := make(map[string]interface{})
		err = json.Unmarshal([]byte(response), &responseJSON)
		if err != nil {
			log.Fatalf("Error while unmarshaling master's response: %v", err)
		}

		var ok bool
		store, ok = responseJSON["data"].(map[string]interface{})
		if !ok {
			log.Fatalf("Could not cast responseJSON: %s", responseJSON["data"])
		}
		log.Printf("Registered to node: %s", registerTo)
		return
	}

	log.Fatal("Timed out while registering to master.")
}

func startHTTPServer(port string) {
	http.HandleFunc("/register", registerHandler)
	http.HandleFunc("/get", getHandler)
	http.HandleFunc("/set", setHandler)
	http.HandleFunc("/guestbook", guestbookHandler)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}

// registerHandler will register the caller in this server's list of slaves.
// /set requests will be propagated to slaves, if any.
func registerHandler(w http.ResponseWriter, r *http.Request) {
	values, err := url.Parse(r.URL.RequestURI())
	if err != nil {
		http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
		return
	}

	ip := values.Query().Get("host")
	log.Printf("GET /register?host=%s", ip)

	// send all the store to the slave as well.
	output := make(map[string]interface{})
	output["data"] = store
	bytes, err := json.Marshal(output)
	if err != nil {
		http.Error(w, fmt.Sprintf("response could not be serialized. %v", err), http.StatusExpectationFailed)
		return
	}
	fmt.Fprint(w, string(bytes))
	slaves = append(slaves, ip)
	log.Printf("Node '%s' registered.", ip)
}

// getHandler will return '{"data": value}', where value is the stored value for
// the given key if non-empty, or entire store.
func getHandler(w http.ResponseWriter, r *http.Request) {
	values, err := url.Parse(r.URL.RequestURI())
	if err != nil {
		http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
		return
	}

	key := values.Query().Get("key")

	log.Printf("GET /get?key=%s", key)

	output := make(map[string]interface{})
	if key == "" {
		output["data"] = store
	} else {
		value, found := store[key]
		if !found {
			value = ""
		}
		output["data"] = value
	}

	bytes, err := json.Marshal(output)
	if err == nil {
		fmt.Fprint(w, string(bytes))
	} else {
		http.Error(w, fmt.Sprintf("response could not be serialized. %v", err), http.StatusExpectationFailed)
	}
}

// setHandler will set the given key-value pair in its own store and propagate
// it to its slaves, if any. Will return '{"message": "Updated"}' to the caller on success.
func setHandler(w http.ResponseWriter, r *http.Request) {
	values, err := url.Parse(r.URL.RequestURI())
	if err != nil {
		http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
		return
	}

	key := values.Query().Get("key")
	value := values.Query().Get("value")

	log.Printf("GET /set?key=%s&value=%s", key, value)

	if key == "" {
		http.Error(w, "cannot set with empty key.", http.StatusBadRequest)
		return
	}

	store[key] = value
	request := fmt.Sprintf("set?key=%s&value=%s", key, value)
	for _, slave := range slaves {
		hostPort := net.JoinHostPort(slave, backendPort)
		_, err = dialHTTP(request, hostPort)
		if err != nil {
			http.Error(w, fmt.Sprintf("encountered error while propagating to slave '%s': %v", slave, err), http.StatusExpectationFailed)
			return
		}
	}

	output := map[string]string{}
	output["message"] = "Updated"
	bytes, err := json.Marshal(output)
	if err == nil {
		fmt.Fprint(w, string(bytes))
	} else {
		http.Error(w, fmt.Sprintf("response could not be serialized. %v", err), http.StatusExpectationFailed)
	}
}

// guestbookHandler will proxy the request to agnhost-master if the given cmd is
// 'set' or agnhost-slave if the given cmd is 'get'.
func guestbookHandler(w http.ResponseWriter, r *http.Request) {
	values, err := url.Parse(r.URL.RequestURI())
	if err != nil {
		http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
		return
	}

	cmd := strings.ToLower(values.Query().Get("cmd"))
	key := values.Query().Get("key")
	value := values.Query().Get("value")

	log.Printf("GET /guestbook?cmd=%s&key=%s&value=%s", cmd, key, value)

	if cmd != "get" && cmd != "set" {
		http.Error(w, fmt.Sprintf("unsupported cmd: '%s'", cmd), http.StatusBadRequest)
		return
	}
	if cmd == "set" && key == "" {
		http.Error(w, "cannot set with empty key.", http.StatusBadRequest)
		return
	}

	host := "agnhost-master"
	if cmd == "get" {
		host = "agnhost-slave"
	}

	hostPort := net.JoinHostPort(host, backendPort)
	_, err = net.ResolveTCPAddr("tcp", hostPort)
	if err != nil {
		http.Error(w, fmt.Sprintf("host and/or port param are invalid. %v", err), http.StatusBadRequest)
		return
	}

	request := fmt.Sprintf("%s?key=%s&value=%s", cmd, key, value)
	response, err := dialHTTP(request, hostPort)
	if err == nil {
		fmt.Fprint(w, response)
	} else {
		http.Error(w, fmt.Sprintf("encountered error: %v", err), http.StatusExpectationFailed)
	}
}

func dialHTTP(request, hostPort string) (string, error) {
	transport := utilnet.SetTransportDefaults(&http.Transport{})
	httpClient := createHTTPClient(transport)
	resp, err := httpClient.Get(fmt.Sprintf("http://%s/%s", hostPort, request))
	defer transport.CloseIdleConnections()
	if err == nil {
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err == nil {
			return string(body), nil
		}
	}
	return "", err
}

func createHTTPClient(transport *http.Transport) *http.Client {
	client := &http.Client{
		Transport: transport,
		Timeout:   5 * time.Second,
	}
	return client
}

func getIP(hostPort string) net.IP {
	conn, err := net.Dial("udp", hostPort)
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	localAddr := conn.LocalAddr().(*net.UDPAddr)
	return localAddr.IP
}
<commit_msg>Move getting IP address into retry loop for agnhost/guestbook<commit_after>/*
Copyright 2019 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 guestbook

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/spf13/cobra"

	utilnet "k8s.io/apimachinery/pkg/util/net"
)

// CmdGuestbook is used by agnhost Cobra.
var CmdGuestbook = &cobra.Command{
	Use:   "guestbook",
	Short: "Creates a HTTP server with various endpoints representing a guestbook app",
	Long: `Starts a HTTP server on the given --http-port (default: 80), serving various endpoints representing a guestbook app. The endpoints and their purpose are:

- /register: A guestbook slave will subscribe to a master, to its given --slaveof endpoint. The master will then push any updates it receives to its registered slaves through the --backend-port.
- /get: Returns '{"data": value}', where the value is the stored value for the given key if non-empty, or the entire store.
- /set: Will set the given key-value pair in its own store and propagate it to its slaves, if any. Will return '{"data": "Updated"}' to the caller on success.
- /guestbook: Will proxy the request to agnhost-master if the given cmd is 'set', or agnhost-slave if the given cmd is 'get'.`,
	Args: cobra.MaximumNArgs(0),
	Run:  main,
}

var (
	httpPort    string
	backendPort string
	slaveOf     string
	slaves      []string
	store       map[string]interface{}
)

const (
	timeout = time.Duration(15) * time.Second
	sleep   = time.Duration(1) * time.Second
)

func init() {
	CmdGuestbook.Flags().StringVar(&httpPort, "http-port", "80", "HTTP Listen Port")
	CmdGuestbook.Flags().StringVar(&backendPort, "backend-port", "6379", "Backend's HTTP Listen Port")
	CmdGuestbook.Flags().StringVar(&slaveOf, "slaveof", "", "The host's name to register to")
	store = make(map[string]interface{})
}

func main(cmd *cobra.Command, args []string) {
	go registerNode(slaveOf, backendPort)
	startHTTPServer(httpPort)
}

func registerNode(registerTo, port string) {
	if registerTo == "" {
		return
	}

	hostPort := net.JoinHostPort(registerTo, backendPort)

	start := time.Now()
	for time.Since(start) < timeout {
		host, err := getIP(hostPort)
		if err != nil {
			log.Printf("unable to get IP %s: %v. Retrying in %s.", hostPort, err, sleep)
			time.Sleep(sleep)
			continue
		}

		request := fmt.Sprintf("register?host=%s", host.String())
		log.Printf("Registering to master: %s/%s", hostPort, request)
		_, err = net.ResolveTCPAddr("tcp", hostPort)
		if err != nil {
			log.Printf("unable to resolve %s, --slaveof param and/or --backend-port param are invalid: %v. Retrying in %s.", hostPort, err, sleep)
			time.Sleep(sleep)
			continue
		}

		response, err := dialHTTP(request, hostPort)
		if err != nil {
			log.Printf("encountered error while registering to master: %v. Retrying in %s.", err, sleep)
			time.Sleep(sleep)
			continue
		}

		responseJSON := make(map[string]interface{})
		err = json.Unmarshal([]byte(response), &responseJSON)
		if err != nil {
			log.Fatalf("Error while unmarshaling master's response: %v", err)
		}

		var ok bool
		store, ok = responseJSON["data"].(map[string]interface{})
		if !ok {
			log.Fatalf("Could not cast responseJSON: %s", responseJSON["data"])
		}
		log.Printf("Registered to node: %s", registerTo)
		return
	}

	log.Fatal("Timed out while registering to master.")
}

func startHTTPServer(port string) {
	http.HandleFunc("/register", registerHandler)
	http.HandleFunc("/get", getHandler)
	http.HandleFunc("/set", setHandler)
	http.HandleFunc("/guestbook", guestbookHandler)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}

// registerHandler will register the caller in this server's list of slaves.
// /set requests will be propagated to slaves, if any.
func registerHandler(w http.ResponseWriter, r *http.Request) {
	values, err := url.Parse(r.URL.RequestURI())
	if err != nil {
		http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
		return
	}

	ip := values.Query().Get("host")
	log.Printf("GET /register?host=%s", ip)

	// send all the store to the slave as well.
	output := make(map[string]interface{})
	output["data"] = store
	bytes, err := json.Marshal(output)
	if err != nil {
		http.Error(w, fmt.Sprintf("response could not be serialized. %v", err), http.StatusExpectationFailed)
		return
	}
	fmt.Fprint(w, string(bytes))
	slaves = append(slaves, ip)
	log.Printf("Node '%s' registered.", ip)
}

// getHandler will return '{"data": value}', where value is the stored value for
// the given key if non-empty, or entire store.
func getHandler(w http.ResponseWriter, r *http.Request) {
	values, err := url.Parse(r.URL.RequestURI())
	if err != nil {
		http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
		return
	}

	key := values.Query().Get("key")

	log.Printf("GET /get?key=%s", key)

	output := make(map[string]interface{})
	if key == "" {
		output["data"] = store
	} else {
		value, found := store[key]
		if !found {
			value = ""
		}
		output["data"] = value
	}

	bytes, err := json.Marshal(output)
	if err == nil {
		fmt.Fprint(w, string(bytes))
	} else {
		http.Error(w, fmt.Sprintf("response could not be serialized. %v", err), http.StatusExpectationFailed)
	}
}

// setHandler will set the given key-value pair in its own store and propagate
// it to its slaves, if any. Will return '{"message": "Updated"}' to the caller on success.
func setHandler(w http.ResponseWriter, r *http.Request) {
	values, err := url.Parse(r.URL.RequestURI())
	if err != nil {
		http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
		return
	}

	key := values.Query().Get("key")
	value := values.Query().Get("value")

	log.Printf("GET /set?key=%s&value=%s", key, value)

	if key == "" {
		http.Error(w, "cannot set with empty key.", http.StatusBadRequest)
		return
	}

	store[key] = value
	request := fmt.Sprintf("set?key=%s&value=%s", key, value)
	for _, slave := range slaves {
		hostPort := net.JoinHostPort(slave, backendPort)
		_, err = dialHTTP(request, hostPort)
		if err != nil {
			http.Error(w, fmt.Sprintf("encountered error while propagating to slave '%s': %v", slave, err), http.StatusExpectationFailed)
			return
		}
	}

	output := map[string]string{}
	output["message"] = "Updated"
	bytes, err := json.Marshal(output)
	if err == nil {
		fmt.Fprint(w, string(bytes))
	} else {
		http.Error(w, fmt.Sprintf("response could not be serialized. %v", err), http.StatusExpectationFailed)
	}
}

// guestbookHandler will proxy the request to agnhost-master if the given cmd is
// 'set' or agnhost-slave if the given cmd is 'get'.
func guestbookHandler(w http.ResponseWriter, r *http.Request) {
	values, err := url.Parse(r.URL.RequestURI())
	if err != nil {
		http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
		return
	}

	cmd := strings.ToLower(values.Query().Get("cmd"))
	key := values.Query().Get("key")
	value := values.Query().Get("value")

	log.Printf("GET /guestbook?cmd=%s&key=%s&value=%s", cmd, key, value)

	if cmd != "get" && cmd != "set" {
		http.Error(w, fmt.Sprintf("unsupported cmd: '%s'", cmd), http.StatusBadRequest)
		return
	}
	if cmd == "set" && key == "" {
		http.Error(w, "cannot set with empty key.", http.StatusBadRequest)
		return
	}

	host := "agnhost-master"
	if cmd == "get" {
		host = "agnhost-slave"
	}

	hostPort := net.JoinHostPort(host, backendPort)
	_, err = net.ResolveTCPAddr("tcp", hostPort)
	if err != nil {
		http.Error(w, fmt.Sprintf("host and/or port param are invalid. %v", err), http.StatusBadRequest)
		return
	}

	request := fmt.Sprintf("%s?key=%s&value=%s", cmd, key, value)
	response, err := dialHTTP(request, hostPort)
	if err == nil {
		fmt.Fprint(w, response)
	} else {
		http.Error(w, fmt.Sprintf("encountered error: %v", err), http.StatusExpectationFailed)
	}
}

func dialHTTP(request, hostPort string) (string, error) {
	transport := utilnet.SetTransportDefaults(&http.Transport{})
	httpClient := createHTTPClient(transport)
	resp, err := httpClient.Get(fmt.Sprintf("http://%s/%s", hostPort, request))
	defer transport.CloseIdleConnections()
	if err == nil {
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err == nil {
			return string(body), nil
		}
	}
	return "", err
}

func createHTTPClient(transport *http.Transport) *http.Client {
	client := &http.Client{
		Transport: transport,
		Timeout:   5 * time.Second,
	}
	return client
}

func getIP(hostPort string) (net.IP, error) {
	conn, err := net.Dial("udp", hostPort)
	if err != nil {
		return []byte{}, err
	}
	defer conn.Close()

	localAddr := conn.LocalAddr().(*net.UDPAddr)
	return localAddr.IP, nil
}
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2013-2015 SRS(simple-rtmp-server)

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/*
SRS is industrial-strength live streaming cluster, for the best conceptual integrity and the simplest implementation.
*/
package main
<commit_msg>remove srs dir<commit_after><|endoftext|>


<commit_before>package main

import (
    "fmt"
    "gitee.com/johng/gf/g/os/glog"
    "gitee.com/johng/gf/g/encoding/gparser"
)

func getWithPattern1() {
    data :=
        `{
            "users" : {
                    "count" : 100,
                    "list"  : [
                        {"name" : "Ming", "score" : 60},
                        {"name" : "John", "score" : 99.5}
                    ]
            }
        }`

    if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
        glog.Error(e)
    } else {
        fmt.Println("John Score:", p.GetFloat32("users.list.1.score"))
    }
}

func getWithPattern2() {
    data :=
        `<?xml version="1.0" encoding="UTF-8"?>
         <note>
           <to>Tove</to>
           <from>Jani</from>
           <heading>Reminder</heading>
           Don't forget me this weekend!
         </note>`

    if p, e := gparser.LoadContent([]byte(data), "xml"); e != nil {
        glog.Error(e)
    } else {
        fmt.Println("Heading:", p.GetString("note.heading"))
    }
}

// 当键名存在"."号时，检索优先级：键名->层级，因此不会引起歧义
func multiDots() {
    data :=
        `{
            "users" : {
                "count" : 100
            },
            "users.count" : 101
        }`
    if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
        glog.Error(e)
    } else {
        fmt.Println("Users Count:", p.Get("users.count"))
    }
}

// 设置数据
func set1() {
    data :=
        `<?xml version="1.0" encoding="UTF-8"?>

           <count>10</count>
           <list><title>gf article1</title><content>gf content1</content></list>
           <list><title>gf article2</title><content>gf content2</content></list>
           <list><title>gf article3</title><content>gf content3</content></list>

`
    if p, e := gparser.LoadContent([]byte(data), "xml"); e != nil {
        glog.Error(e)
    } else {
        p.Set("article.list.0", nil)
        c, _ := p.ToJson()
        fmt.Println(string(c))
        // {"article":{"count":"10","list":[{"content":"gf content2","title":"gf article2"},{"content":"gf content3","title":"gf article3"}]}}
    }
}

func set2() {
    data :=
        `{
            "users" : {
                "count" : 100
            }
        }`
    if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
        glog.Error(e)
    } else {
        p.Set("users.count",  1)
        p.Set("users.list",  []string{"John", "小明"})
        c, _ := p.ToJson()
        fmt.Println(string(c))
    }
}

func makeXml1() {
    p := gparser.New()
    p.Set("name",   "john")
    p.Set("age",    18)
    p.Set("scores", map[string]int{
        "语文" : 100,
        "数学" : 100,
        "英语" : 100,
    })
    c, _ := p.ToXmlIndent("simple-xml")
    fmt.Println(string(c))
}

func makeXml2() {
    type Order struct {
        Id    int      `json:"id"`
        Price float32  `json:"price"`
    }
    p := gparser.New()
    p.Set("orders.list.0", Order{1, 100})
    p.Set("orders.list.1", Order{2, 666.66})
    p.Set("orders.list.2", Order{3, 999.99})
    fmt.Println("Order 1 Price:", p.Get("orders.list.1.price"))
    c, _ := p.ToJson()
    fmt.Println(string(c))
    // {"orders":{"list":{"0":{"id":1,"price":100},"1":{"id":2,"price":666.66},"2":{"id":3,"price":999.99}}}}
}

func convert() {
    p := gparser.New(map[string]string{
        "name" : "gf",
        "site" : "https://gitee.com/johng",
    })
    c, _ := p.ToJson()
    fmt.Println("JSON:")
    fmt.Println(string(c))
    fmt.Println("======================")

    fmt.Println("XML:")
    c, _ = p.ToXmlIndent()
    fmt.Println(string(c))
    fmt.Println("======================")

    fmt.Println("YAML:")
    c, _ = p.ToYaml()
    fmt.Println(string(c))
    fmt.Println("======================")

    fmt.Println("TOML:")
    c, _ = p.ToToml()
    fmt.Println(string(c))

}

func main() {
    convert()
}<commit_msg>新增gparser示例<commit_after>package main

import (
    "fmt"
    "gitee.com/johng/gf/g/os/glog"
    "gitee.com/johng/gf/g/encoding/gparser"
)

func getWithPattern1() {
    data :=
        `{
            "users" : {
                    "count" : 100,
                    "list"  : [
                        {"name" : "Ming", "score" : 60},
                        {"name" : "John", "score" : 99.5}
                    ]
            }
        }`

    if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
        glog.Error(e)
    } else {
        fmt.Println("John Score:", p.GetFloat32("users.list.1.score"))
    }
}

func getWithPattern2() {
    data :=
        `<?xml version="1.0" encoding="UTF-8"?>
         <note>
           <to>Tove</to>
           <from>Jani</from>
           <heading>Reminder</heading>
           Don't forget me this weekend!
         </note>`

    if p, e := gparser.LoadContent([]byte(data), "xml"); e != nil {
        glog.Error(e)
    } else {
        fmt.Println("Heading:", p.GetString("note.heading"))
    }
}

// 当键名存在"."号时，检索优先级：键名->层级，因此不会引起歧义
func multiDots1() {
    data :=
        `{
            "users" : {
                "count" : 100
            },
            "users.count" : 101
        }`
    if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
        glog.Error(e)
    } else {
        fmt.Println("Users Count:", p.Get("users.count"))
    }
}

func multiDots2() {
    data :=
        `{
            "users" : {
                "count" : {
                    "type1" : 1,
                    "type2" : 2
                },
                "count.type1" : 100
            }
        }`
    if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
        glog.Error(e)
    } else {
        fmt.Println("Users Count:", p.Get("users.count.type1"))
        fmt.Println("Users Count:", p.Get("users.count.type2"))
    }
}

// 设置数据
func set1() {
    data :=
        `<?xml version="1.0" encoding="UTF-8"?>

           <count>10</count>
           <list><title>gf article1</title><content>gf content1</content></list>
           <list><title>gf article2</title><content>gf content2</content></list>
           <list><title>gf article3</title><content>gf content3</content></list>

`
    if p, e := gparser.LoadContent([]byte(data), "xml"); e != nil {
        glog.Error(e)
    } else {
        p.Set("article.list.0", nil)
        c, _ := p.ToJson()
        fmt.Println(string(c))
        // {"article":{"count":"10","list":[{"content":"gf content2","title":"gf article2"},{"content":"gf content3","title":"gf article3"}]}}
    }
}

func set2() {
    data :=
        `{
            "users" : {
                "count" : 100
            }
        }`
    if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
        glog.Error(e)
    } else {
        p.Set("users.count",  1)
        p.Set("users.list",  []string{"John", "小明"})
        c, _ := p.ToJson()
        fmt.Println(string(c))
    }
}

func makeXml1() {
    p := gparser.New()
    p.Set("name",   "john")
    p.Set("age",    18)
    p.Set("scores", map[string]int{
        "语文" : 100,
        "数学" : 100,
        "英语" : 100,
    })
    c, _ := p.ToXmlIndent("simple-xml")
    fmt.Println(string(c))
}

func makeXml2() {
    type Order struct {
        Id    int      `json:"id"`
        Price float32  `json:"price"`
    }
    p := gparser.New()
    p.Set("orders.list.0", Order{1, 100})
    p.Set("orders.list.1", Order{2, 666.66})
    p.Set("orders.list.2", Order{3, 999.99})
    fmt.Println("Order 1 Price:", p.Get("orders.list.1.price"))
    c, _ := p.ToJson()
    fmt.Println(string(c))
    // {"orders":{"list":{"0":{"id":1,"price":100},"1":{"id":2,"price":666.66},"2":{"id":3,"price":999.99}}}}
}

func convert() {
    p := gparser.New(map[string]string{
        "name" : "gf",
        "site" : "https://gitee.com/johng",
    })
    c, _ := p.ToJson()
    fmt.Println("JSON:")
    fmt.Println(string(c))
    fmt.Println("======================")

    fmt.Println("XML:")
    c, _ = p.ToXmlIndent()
    fmt.Println(string(c))
    fmt.Println("======================")

    fmt.Println("YAML:")
    c, _ = p.ToYaml()
    fmt.Println(string(c))
    fmt.Println("======================")

    fmt.Println("TOML:")
    c, _ = p.ToToml()
    fmt.Println(string(c))

}

func main() {
    multiDots2()
}<|endoftext|>


<commit_before>package main

import (
	"appengine"
	"appengine/datastore"
	"http"
	"os"
	"regexp"
	"strconv"
	"template"
	"time"
)

type Page struct {
	Id   int64
	Time datastore.Time
	Body []byte
}

func (p *Page) save(c appengine.Context) (id int64, err os.Error) {
	if len(p.Body) > 4096 {
		err = os.NewError("Paste is too large to store")
		return
	}
	k, err := datastore.Put(c, datastore.NewIncompleteKey("Page"), p)
	if err != nil {
		return
	}
	return k.IntID(), nil
}

func loadPage(r *http.Request, id int64) (*Page, os.Error) {
	c := appengine.NewContext(r)
	p := new(Page)
	err := datastore.Get(c, datastore.NewKey("Page", "", id, nil), p)
	if err != nil {
		return nil, err
	}
	p.Id = id
	return p, nil
}

func pasteHandler(w http.ResponseWriter, r *http.Request) {
	p := &Page{Time: 0, Body: make([]byte, 0)}
	renderTemplate(w, "paste", p)
}

func makeHandler(fn func(http.ResponseWriter, *http.Request, int64, string), tmplt string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		id := r.URL.Path[len("/"+tmplt+"/"):]
		if !idValidator.MatchString(id) {
			http.NotFound(w, r)
			return
		}
		i, err := strconv.Atoi64(id)
		if err != nil {
			http.NotFound(w, r)
			return
		}
		fn(w, r, i, tmplt)
	}
}

func viewHandler(w http.ResponseWriter, r *http.Request, id int64, tmplt string) {
	p, err := loadPage(r, id)
	if err != nil {
		http.NotFound(w, r)
		return
	}
	renderTemplate(w, tmplt, p)
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	body := r.FormValue("body")
	p := &Page{
		Time: datastore.SecondsToTime(time.Seconds()),
		Body: []byte(body),
	}
	id, err := p.save(c)
	if err != nil {
		c.Logf("Error saving paste %s\n", id)
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
	c.Logf("Saving paste %v\n", id)
	http.Redirect(w, r, "/plain/"+strconv.Itoa64(id), http.StatusFound)
}

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
	t := "tmplt/" + tmpl + ".html"
	mt := mtime(t)
	if mt > templmtimes[tmpl] {
		templmtimes[tmpl] = mt
		templates[tmpl] = template.MustParseFile(t, nil)
	}
	err := templates[tmpl].Execute(w, p)
	if err != nil {
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
}

var templates = make(map[string]*template.Template)
var templmtimes = make(map[string]int64)

func init() {
	for _, tmpl := range []string{"paste", "plain", "fancy"} {
		t := "tmplt/" + tmpl + ".html"
		templmtimes[tmpl] = mtime(t)
		templates[tmpl] = template.MustParseFile(t, nil)
	}

	http.HandleFunc("/", pasteHandler)
	http.HandleFunc("/plain/", makeHandler(viewHandler, "plain"))
	http.HandleFunc("/fancy/", makeHandler(viewHandler, "fancy"))
	http.HandleFunc("/save/", saveHandler)
	http.Handle("/js/", http.FileServer("js/", "/js/"))
	http.Handle("/css/", http.FileServer("css/", "/css/"))
}

func mtime(f string) int64 {
	fi, err := os.Stat(f)
	if err != nil {
		return 0
	}
	return fi.Mtime_ns
}

var idValidator = regexp.MustCompile("^[0-9]+$")
<commit_msg>Formatting.<commit_after>package main

import (
	"appengine"
	"appengine/datastore"
	"http"
	"os"
	"regexp"
	"strconv"
	"template"
	"time"
)

type Page struct {
	Id   int64
	Time datastore.Time
	Body []byte
}

func (p *Page) save(c appengine.Context) (id int64, err os.Error) {
	if len(p.Body) > 4096 {
		err = os.NewError("Paste is too large to store")
		return
	}
	k, err := datastore.Put(c, datastore.NewIncompleteKey("Page"), p)
	if err != nil {
		return
	}
	return k.IntID(), nil
}

func loadPage(r *http.Request, id int64) (*Page, os.Error) {
	c := appengine.NewContext(r)
	p := new(Page)
	err := datastore.Get(c, datastore.NewKey("Page", "", id, nil), p)
	if err != nil {
		return nil, err
	}
	p.Id = id
	return p, nil
}

func pasteHandler(w http.ResponseWriter, r *http.Request) {
	p := &Page{Time: 0, Body: make([]byte, 0)}
	renderTemplate(w, "paste", p)
}

var idValidator = regexp.MustCompile("^[0-9]+$")

func makeHandler(fn func(http.ResponseWriter, *http.Request, int64, string), tmplt string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		id := r.URL.Path[len("/"+tmplt+"/"):]
		if !idValidator.MatchString(id) {
			http.NotFound(w, r)
			return
		}
		i, err := strconv.Atoi64(id)
		if err != nil {
			http.NotFound(w, r)
			return
		}
		fn(w, r, i, tmplt)
	}
}

func viewHandler(w http.ResponseWriter, r *http.Request, id int64, tmplt string) {
	p, err := loadPage(r, id)
	if err != nil {
		http.NotFound(w, r)
		return
	}
	renderTemplate(w, tmplt, p)
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	body := r.FormValue("body")
	p := &Page{
		Time: datastore.SecondsToTime(time.Seconds()),
		Body: []byte(body),
	}
	id, err := p.save(c)
	if err != nil {
		c.Logf("Error saving paste %s\n", id)
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
	c.Logf("Saving paste %v\n", id)
	http.Redirect(w, r, "/plain/"+strconv.Itoa64(id), http.StatusFound)
}

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
	t := "tmplt/" + tmpl + ".html"
	mt := mtime(t)
	if mt > templmtimes[tmpl] {
		templmtimes[tmpl] = mt
		templates[tmpl] = template.MustParseFile(t, nil)
	}
	err := templates[tmpl].Execute(w, p)
	if err != nil {
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
}

var templates = make(map[string]*template.Template)
var templmtimes = make(map[string]int64)

func init() {
	for _, tmpl := range []string{"paste", "plain", "fancy"} {
		t := "tmplt/" + tmpl + ".html"
		templmtimes[tmpl] = mtime(t)
		templates[tmpl] = template.MustParseFile(t, nil)
	}

	http.HandleFunc("/", pasteHandler)
	http.HandleFunc("/plain/", makeHandler(viewHandler, "plain"))
	http.HandleFunc("/fancy/", makeHandler(viewHandler, "fancy"))
	http.HandleFunc("/save/", saveHandler)
	http.Handle("/js/", http.FileServer("js/", "/js/"))
	http.Handle("/css/", http.FileServer("css/", "/css/"))
}

func mtime(f string) int64 {
	fi, err := os.Stat(f)
	if err != nil {
		return 0
	}
	return fi.Mtime_ns
}
<|endoftext|>


<commit_before>package core

import (
	"fmt"
)

func sprint(x interface{}) StringType {
	return StringType(fmt.Sprint(x))
}

type dumpable interface {
	dump() Value
}

// Dump dumps a value into a string type value.
var Dump = NewLazyFunction(
	NewSignature([]string{"x"}, nil, "", nil, nil, ""),
	func(ts ...*Thunk) Value {
		v := ts[0].Eval()

		switch x := v.(type) {
		case ErrorType:
			return x
		case dumpable:
			v = x.dump()
		default:
			v = PApp(ToString, Normal(v)).Eval()
		}

		if _, ok := v.(StringType); !ok {
			return NotStringError(v)
		}

		return v
	})

// internalDumpOrFail is the same as DumpOrFail.
func internalDumpOrFail(v Value) string {
	v = ensureWHNF(v)

	switch x := v.(type) {
	case ErrorType:
		panic(x)
	case dumpable:
		v = x.dump()
	case stringable:
		v = x.string()
	default:
		panic(fmt.Sprintf("Invalid value detected: %#v", v))
	}

	switch x := v.(type) {
	case StringType:
		return string(x)
	case ErrorType:
		panic(x)
	}

	panic(fmt.Sprintf("Invalid value detected: %#v", v))
}

// DumpOrFail dumps a value into a string value or fail exiting a process.
// This function should be used only to create strings of error information.
func DumpOrFail(v Value) string {
	v = PApp(Dump, Normal(v)).Eval()
	s, ok := v.(StringType)

	if !ok {
		panic(NotStringError(v).Eval().(ErrorType))
	}

	return string(s)
}

// Equal checks if 2 values are equal or not.
var Equal = NewStrictFunction(
	NewSignature(
		[]string{"x", "y"}, nil, "",
		nil, nil, "",
	),
	func(ts ...*Thunk) (v Value) {
		defer func() {
			if r := recover(); r != nil {
				v = r
			}
		}()

		return BoolType(compare(ts[0].Eval(), ts[1].Eval()) == 0)
	})

// ensureWHNF evaluates nested thunks into WHNF values.
// This function must be used with care because it prevents tail call
// elimination.
func ensureWHNF(v Value) Value {
	if t, ok := v.(*Thunk); ok {
		return t.Eval()
	}

	return v
}

var identity = NewLazyFunction(
	NewSignature([]string{"x"}, nil, "", nil, nil, ""),
	func(ts ...*Thunk) Value { return ts[0] })

// TypeOf returns a type name of an argument as a string.
var TypeOf = NewLazyFunction(
	NewSignature([]string{"x"}, nil, "", nil, nil, ""),
	func(ts ...*Thunk) Value {
		// No case of OutputType should be here.
		switch ts[0].Eval().(type) {
		case BoolType:
			return NewString("bool")
		case DictionaryType:
			return NewString("dict")
		case ListType:
			return NewString("list")
		case NilType:
			return NewString("nil")
		case NumberType:
			return NewString("number")
		case StringType:
			return NewString("string")

		case functionType:
			return NewString("function")
		case closureType:
			return NewString("function")

		case ErrorType:
			return NewString("error")
		}

		panic(fmt.Errorf("Invalid value: %#v", ts[0].Eval()))
	})
<commit_msg>Refactor internalDumpOrFail()<commit_after>package core

import (
	"fmt"
)

func sprint(x interface{}) StringType {
	return StringType(fmt.Sprint(x))
}

type dumpable interface {
	dump() Value
}

// Dump dumps a value into a string type value.
var Dump = NewLazyFunction(
	NewSignature([]string{"x"}, nil, "", nil, nil, ""),
	func(ts ...*Thunk) Value {
		v := ts[0].Eval()

		switch x := v.(type) {
		case ErrorType:
			return x
		case dumpable:
			v = x.dump()
		default:
			v = PApp(ToString, Normal(v)).Eval()
		}

		if _, ok := v.(StringType); !ok {
			return NotStringError(v)
		}

		return v
	})

// internalDumpOrFail is the same as DumpOrFail.
func internalDumpOrFail(v Value) string {
	v = ensureWHNF(v)

	switch x := v.(type) {
	case ErrorType:
		panic(x)
	case dumpable:
		v = x.dump()
	case stringable:
		v = x.string()
	default:
		panic(fmt.Sprintf("Invalid value detected: %#v", v))
	}

	if s, ok := v.(StringType); ok {
		return string(s)
	}

	panic(fmt.Sprintf("Invalid value detected: %#v", v))
}

// DumpOrFail dumps a value into a string value or fail exiting a process.
// This function should be used only to create strings of error information.
func DumpOrFail(v Value) string {
	v = PApp(Dump, Normal(v)).Eval()
	s, ok := v.(StringType)

	if !ok {
		panic(NotStringError(v).Eval().(ErrorType))
	}

	return string(s)
}

// Equal checks if 2 values are equal or not.
var Equal = NewStrictFunction(
	NewSignature(
		[]string{"x", "y"}, nil, "",
		nil, nil, "",
	),
	func(ts ...*Thunk) (v Value) {
		defer func() {
			if r := recover(); r != nil {
				v = r
			}
		}()

		return BoolType(compare(ts[0].Eval(), ts[1].Eval()) == 0)
	})

// ensureWHNF evaluates nested thunks into WHNF values.
// This function must be used with care because it prevents tail call
// elimination.
func ensureWHNF(v Value) Value {
	if t, ok := v.(*Thunk); ok {
		return t.Eval()
	}

	return v
}

var identity = NewLazyFunction(
	NewSignature([]string{"x"}, nil, "", nil, nil, ""),
	func(ts ...*Thunk) Value { return ts[0] })

// TypeOf returns a type name of an argument as a string.
var TypeOf = NewLazyFunction(
	NewSignature([]string{"x"}, nil, "", nil, nil, ""),
	func(ts ...*Thunk) Value {
		// No case of OutputType should be here.
		switch ts[0].Eval().(type) {
		case BoolType:
			return NewString("bool")
		case DictionaryType:
			return NewString("dict")
		case ListType:
			return NewString("list")
		case NilType:
			return NewString("nil")
		case NumberType:
			return NewString("number")
		case StringType:
			return NewString("string")

		case functionType:
			return NewString("function")
		case closureType:
			return NewString("function")

		case ErrorType:
			return NewString("error")
		}

		panic(fmt.Errorf("Invalid value: %#v", ts[0].Eval()))
	})
<|endoftext|>


<commit_before>/*
 *    Copyright (C) 2014 Christian Muehlhaeuser
 *                  2014 Michael Wendland
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors:
 *      Christian Muehlhaeuser <muesli@gmail.com>
 *      Michael Wendland <michiwend@michiwend.com>
 *      Johannes Fürmann <johannes@weltraumpflege.org>
 */

package main

import (
	"encoding/json"
	"io/ioutil"
	"log"

	"github.com/muesli/beehive/app"
	_ "github.com/muesli/beehive/filters"
	_ "github.com/muesli/beehive/filters/contains"
	_ "github.com/muesli/beehive/filters/equals"
	_ "github.com/muesli/beehive/filters/endswith"
	_ "github.com/muesli/beehive/filters/startswith"

	"github.com/muesli/beehive/modules"
	//_ "github.com/muesli/beehive/modules/hellobee"
	_ "github.com/muesli/beehive/modules/anelpowerctrlbee"
	_ "github.com/muesli/beehive/modules/ircbee"
	_ "github.com/muesli/beehive/modules/jabberbee"
	_ "github.com/muesli/beehive/modules/jenkinsbee"
	_ "github.com/muesli/beehive/modules/nagiosbee"
	_ "github.com/muesli/beehive/modules/notificationbee"
	_ "github.com/muesli/beehive/modules/rssbee"
	_ "github.com/muesli/beehive/modules/webbee"
	_ "github.com/muesli/beehive/modules/timebee"
	_ "github.com/muesli/beehive/modules/serialbee"
	_ "github.com/muesli/beehive/modules/spaceapibee"
	_ "github.com/muesli/beehive/modules/htmlextractbee"
	_ "github.com/muesli/beehive/modules/efabee"
    _ "github.com/waaaaargh/beehive/modules/twitterbee"
)

var (
	configFile string
)

type Config struct {
	Bees   []modules.Bee
	Chains []modules.Chain
}

// Loads chains from config
func loadConfig() Config {
	config := Config{}

	j, err := ioutil.ReadFile(configFile)
	if err == nil {
		err = json.Unmarshal(j, &config)
	}

	if err != nil {
		log.Fatal(err)
	}

	return config
}

// Saves chains to config
func saveConfig(c Config) {
	j, err := json.MarshalIndent(c, "", "  ")
	if err == nil {
		err = ioutil.WriteFile(configFile, j, 0644)
	}

	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	app.AddFlags([]app.CliFlag{
    	app.CliFlag{&configFile, "config", "./beehive.conf", "Config-file to use"},
	})

	// Parse command-line args for all registered modules
	app.Run()

	log.Println()
	log.Println("Beehive is buzzing...")

	config := loadConfig()

	// Initialize modules
	modules.StartModules(config.Bees)
	// Load chains from config
	modules.SetChains(config.Chains)

	// Keep app alive
	ch := make(chan bool)
	<-ch

	// Save chains to config
	config.Chains = modules.Chains()
	saveConfig(config)
}
<commit_msg>* Switch to upstream-repo.<commit_after>/*
 *    Copyright (C) 2014 Christian Muehlhaeuser
 *                  2014 Michael Wendland
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors:
 *      Christian Muehlhaeuser <muesli@gmail.com>
 *      Michael Wendland <michiwend@michiwend.com>
 *      Johannes Fürmann <johannes@weltraumpflege.org>
 */

package main

import (
	"encoding/json"
	"io/ioutil"
	"log"

	"github.com/muesli/beehive/app"
	_ "github.com/muesli/beehive/filters"
	_ "github.com/muesli/beehive/filters/contains"
	_ "github.com/muesli/beehive/filters/equals"
	_ "github.com/muesli/beehive/filters/endswith"
	_ "github.com/muesli/beehive/filters/startswith"

	"github.com/muesli/beehive/modules"
	//_ "github.com/muesli/beehive/modules/hellobee"
	_ "github.com/muesli/beehive/modules/anelpowerctrlbee"
	_ "github.com/muesli/beehive/modules/ircbee"
	_ "github.com/muesli/beehive/modules/jabberbee"
	_ "github.com/muesli/beehive/modules/jenkinsbee"
	_ "github.com/muesli/beehive/modules/nagiosbee"
	_ "github.com/muesli/beehive/modules/notificationbee"
	_ "github.com/muesli/beehive/modules/rssbee"
	_ "github.com/muesli/beehive/modules/webbee"
	_ "github.com/muesli/beehive/modules/timebee"
	_ "github.com/muesli/beehive/modules/serialbee"
	_ "github.com/muesli/beehive/modules/spaceapibee"
	_ "github.com/muesli/beehive/modules/htmlextractbee"
	_ "github.com/muesli/beehive/modules/efabee"
	_ "github.com/muesli/beehive/modules/twitterbee"
)

var (
	configFile string
)

type Config struct {
	Bees   []modules.Bee
	Chains []modules.Chain
}

// Loads chains from config
func loadConfig() Config {
	config := Config{}

	j, err := ioutil.ReadFile(configFile)
	if err == nil {
		err = json.Unmarshal(j, &config)
	}

	if err != nil {
		log.Fatal(err)
	}

	return config
}

// Saves chains to config
func saveConfig(c Config) {
	j, err := json.MarshalIndent(c, "", "  ")
	if err == nil {
		err = ioutil.WriteFile(configFile, j, 0644)
	}

	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	app.AddFlags([]app.CliFlag{
    	app.CliFlag{&configFile, "config", "./beehive.conf", "Config-file to use"},
	})

	// Parse command-line args for all registered modules
	app.Run()

	log.Println()
	log.Println("Beehive is buzzing...")

	config := loadConfig()

	// Initialize modules
	modules.StartModules(config.Bees)
	// Load chains from config
	modules.SetChains(config.Chains)

	// Keep app alive
	ch := make(chan bool)
	<-ch

	// Save chains to config
	config.Chains = modules.Chains()
	saveConfig(config)
}
<|endoftext|>


<commit_before>package exp

import "strings"

// Match

type expMatch struct {
	key, str string
}

func (e expMatch) Eval(p Params) bool {
	return p.Get(e.key) == e.str
}

func (e expMatch) String() string {
	return sprintf("[%s==%s]", e.key, e.str)
}

// Match is an expression that evaluates to true if str is equal to the value
// pointed to by key.
func Match(key, str string) Exp {
	return expMatch{key, str}
}

// MatchAny is an expression that evaluates to true if any of the strs are equal
// to the value pointed to by key.
func MatchAny(key string, strs ...string) Exp {
	exp := make([]Exp, len(strs))
	for i, str := range strs {
		exp[i] = Match(key, str)
	}
	return Or(exp...)
}

// Contains

type expContains struct {
	key, substr string
}

func (e expContains) Eval(p Params) bool {
	return strings.Contains(p.Get(e.key), e.substr)
}

func (e expContains) String() string {
	return sprintf("[%s∋%s]", e.key, e.substr)
}

// Contains is an expression that evaluates to true if substr is within the
// value pointed to by key.
func Contains(key, substr string) Exp {
	return expContains{key, substr}
}

// ContainsAny

type expContainsAny struct {
	key, chars string
}

func (e expContainsAny) Eval(p Params) bool {
	return strings.ContainsAny(p.Get(e.key), e.chars)
}

func (e expContainsAny) String() string {
	return sprintf("[%s∋%s]", e.key, e.chars)
}

// ContainsAny evaluates to true if any Unicode code points in chars are within
// the value pointed to by key.
func ContainsAny(key, chars string) Exp {
	return expContainsAny{key, chars}
}

// ContainsRune

type expContainsRune struct {
	key string
	r   rune
}

func (e expContainsRune) Eval(p Params) bool {
	return strings.ContainsRune(p.Get(e.key), e.r)
}

func (e expContainsRune) String() string {
	return sprintf("[%s∋%c]", e.key, e.r)
}

// ContainsRune evaluates to true if the Unicode code point r is within the
// value pointed to by key.
func ContainsRune(key string, r rune) Exp {
	return expContainsRune{key, r}
}

// Len

type expLen struct {
	key    string
	length int
}

func (e expLen) Eval(p Params) bool {
	return len(p.Get(e.key)) == e.length
}

func (e expLen) String() string {
	return sprintf("[len(%s)==%d]", e.key, e.length)
}

// Len evalates to true if the length of the string pointed to by key is equal
// to length.
func Len(key string, length int) Exp {
	return expLen{key, length}
}

// Count

type expCount struct {
	key, sep string
	count    int
}

func (e expCount) Eval(p Params) bool {
	return strings.Count(p.Get(e.key), e.sep) == e.count
}

func (e expCount) String() string {
	return sprintf("[count(%s,%s)==%d]", e.key, e.sep, e.count)
}

// Count evaluates to true if the number of non-overlapping instances of sep in
// the value pointed to by key is equal to count.
func Count(key, sep string, count int) Exp {
	return expCount{key, sep, count}
}

// EqualFold

type expEqualFold struct {
	key, s string
}

func (e expEqualFold) Eval(p Params) bool {
	return strings.EqualFold(p.Get(e.key), e.s)
}

func (e expEqualFold) String() string {
	return sprintf("[%s≈%s]", e.key, e.s)
}

// EqualFold evaluates to true if the value pointed to by key and s, interpreted
// as UTF-8 strings, are equal under Unicode case-folding.
func EqualFold(key, s string) Exp {
	return expEqualFold{key, s}
}
<commit_msg>Improving docs and examples<commit_after>package exp

import "strings"

// Match

type expMatch struct {
	key, str string
}

func (e expMatch) Eval(p Params) bool {
	return p.Get(e.key) == e.str
}

func (e expMatch) String() string {
	return sprintf("[%s==%s]", e.key, e.str)
}

// Match is an expression that evaluates to true if str is equal to the value
// pointed to by key.
//
// 	m := Map{"foo": "bar"}
// 	Match("foo", "bar").Eval(m) // true
// 	Match("foo", "baz").Eval(m) // false
func Match(key, str string) Exp {
	return expMatch{key, str}
}

// MatchAny is an expression that evaluates to true if any of the strs are equal
// to the value pointed to by key.
//
// 	m := Map{"foo": "bar"}
// 	MatchAny("foo", "bar", "baz", "cux").Eval(m) // true
// 	MatchAny("foo", "baf", "baz", "lux").Eval(m) // false
func MatchAny(key string, strs ...string) Exp {
	exp := make([]Exp, len(strs))
	for i, str := range strs {
		exp[i] = Match(key, str)
	}
	return Or(exp...)
}

// Contains

type expContains struct {
	key, substr string
}

func (e expContains) Eval(p Params) bool {
	return strings.Contains(p.Get(e.key), e.substr)
}

func (e expContains) String() string {
	return sprintf("[%s∋%s]", e.key, e.substr)
}

// Contains is an expression that evaluates to true if substr is within the
// value pointed to by key.
func Contains(key, substr string) Exp {
	return expContains{key, substr}
}

// ContainsAny

type expContainsAny struct {
	key, chars string
}

func (e expContainsAny) Eval(p Params) bool {
	return strings.ContainsAny(p.Get(e.key), e.chars)
}

func (e expContainsAny) String() string {
	return sprintf("[%s∋%s]", e.key, e.chars)
}

// ContainsAny evaluates to true if any Unicode code points in chars are within
// the value pointed to by key.
func ContainsAny(key, chars string) Exp {
	return expContainsAny{key, chars}
}

// ContainsRune

type expContainsRune struct {
	key string
	r   rune
}

func (e expContainsRune) Eval(p Params) bool {
	return strings.ContainsRune(p.Get(e.key), e.r)
}

func (e expContainsRune) String() string {
	return sprintf("[%s∋%c]", e.key, e.r)
}

// ContainsRune evaluates to true if the Unicode code point r is within the
// value pointed to by key.
func ContainsRune(key string, r rune) Exp {
	return expContainsRune{key, r}
}

// Len

type expLen struct {
	key    string
	length int
}

func (e expLen) Eval(p Params) bool {
	return len(p.Get(e.key)) == e.length
}

func (e expLen) String() string {
	return sprintf("[len(%s)==%d]", e.key, e.length)
}

// Len evalates to true if the length of the string pointed to by key is equal
// to length.
func Len(key string, length int) Exp {
	return expLen{key, length}
}

// Count

type expCount struct {
	key, sep string
	count    int
}

func (e expCount) Eval(p Params) bool {
	return strings.Count(p.Get(e.key), e.sep) == e.count
}

func (e expCount) String() string {
	return sprintf("[count(%s,%s)==%d]", e.key, e.sep, e.count)
}

// Count evaluates to true if the number of non-overlapping instances of sep in
// the value pointed to by key is equal to count.
//
// 	m := Map{
// 		"foo": "bar",
// 		"bar": "zoo"
// 	}
// 	Count("foo", "b", 1).Eval(m) // true
// 	Count("foo", "a", 1).Eval(m) // true
// 	Count("foo", "r", 1).Eval(m) // true
// 	Count("bar", "o", 2).Eval(m) // true
func Count(key, sep string, count int) Exp {
	return expCount{key, sep, count}
}

// EqualFold

type expEqualFold struct {
	key, s string
}

func (e expEqualFold) Eval(p Params) bool {
	return strings.EqualFold(p.Get(e.key), e.s)
}

func (e expEqualFold) String() string {
	return sprintf("[%s≈%s]", e.key, e.s)
}

// EqualFold evaluates to true if the value pointed to by key and s, interpreted
// as UTF-8 strings, are equal under Unicode case-folding.
//
// 	m := Map{
// 		"foo": "Bar",
// 		"bar": "BaZ"
// 	}
// 	EqualFold("foo", "bar").Eval(m) // true
// 	EqualFold("bar", "baz").Eval(m) // true
func EqualFold(key, s string) Exp {
	return expEqualFold{key, s}
}
<|endoftext|>


<commit_before>package sprig

import (
	"encoding/base32"
	"encoding/base64"
	"fmt"
	"reflect"
	"strconv"
	"strings"

	util "github.com/aokoli/goutils"
)

func base64encode(v string) string {
	return base64.StdEncoding.EncodeToString([]byte(v))
}

func base64decode(v string) string {
	data, err := base64.StdEncoding.DecodeString(v)
	if err != nil {
		return err.Error()
	}
	return string(data)
}

func base32encode(v string) string {
	return base32.StdEncoding.EncodeToString([]byte(v))
}

func base32decode(v string) string {
	data, err := base32.StdEncoding.DecodeString(v)
	if err != nil {
		return err.Error()
	}
	return string(data)
}

func abbrev(width int, s string) string {
	if width < 4 {
		return s
	}
	r, _ := util.Abbreviate(s, width)
	return r
}

func abbrevboth(left, right int, s string) string {
	if right < 4 || left > 0 && right < 7 {
		return s
	}
	r, _ := util.AbbreviateFull(s, left, right)
	return r
}
func initials(s string) string {
	// Wrap this just to eliminate the var args, which templates don't do well.
	return util.Initials(s)
}

func randAlphaNumeric(count int) string {
	// It is not possible, it appears, to actually generate an error here.
	r, _ := util.RandomAlphaNumeric(count)
	return r
}

func randAlpha(count int) string {
	r, _ := util.RandomAlphabetic(count)
	return r
}

func randAscii(count int) string {
	r, _ := util.RandomAscii(count)
	return r
}

func randNumeric(count int) string {
	r, _ := util.RandomNumeric(count)
	return r
}

func untitle(str string) string {
	return util.Uncapitalize(str)
}

func quote(str ...interface{}) string {
	out := make([]string, len(str))
	for i, s := range str {
		out[i] = fmt.Sprintf("%q", strval(s))
	}
	return strings.Join(out, " ")
}

func squote(str ...interface{}) string {
	out := make([]string, len(str))
	for i, s := range str {
		out[i] = fmt.Sprintf("'%v'", s)
	}
	return strings.Join(out, " ")
}

func cat(v ...interface{}) string {
	r := strings.TrimSpace(strings.Repeat("%v ", len(v)))
	return fmt.Sprintf(r, v...)
}

func indent(spaces int, v string) string {
	pad := strings.Repeat(" ", spaces)
	return pad + strings.Replace(v, "\n", "\n"+pad, -1)
}

func nindent(spaces int, v string) string {
	return "\n" + indent(spaces, v)
}

func replace(old, new, src string) string {
	return strings.Replace(src, old, new, -1)
}

func plural(one, many string, count int) string {
	if count == 1 {
		return one
	}
	return many
}

func strslice(v interface{}) []string {
	switch v := v.(type) {
	case []string:
		return v
	case []interface{}:
		l := len(v)
		b := make([]string, l)
		for i := 0; i < l; i++ {
			b[i] = strval(v[i])
		}
		return b
	default:
		val := reflect.ValueOf(v)
		switch val.Kind() {
		case reflect.Array, reflect.Slice:
			l := val.Len()
			b := make([]string, l)
			for i := 0; i < l; i++ {
				b[i] = strval(val.Index(i).Interface())
			}
			return b
		default:
			return []string{strval(v)}
		}
	}
}

func strval(v interface{}) string {
	switch v := v.(type) {
	case string:
		return v
	case []byte:
		return string(v)
	case error:
		return v.Error()
	case fmt.Stringer:
		return v.String()
	default:
		return fmt.Sprintf("%v", v)
	}
}

func trunc(c int, s string) string {
	if len(s) <= c {
		return s
	}
	return s[0:c]
}

func join(sep string, v interface{}) string {
	return strings.Join(strslice(v), sep)
}

func split(sep, orig string) map[string]string {
	parts := strings.Split(orig, sep)
	res := make(map[string]string, len(parts))
	for i, v := range parts {
		res["_"+strconv.Itoa(i)] = v
	}
	return res
}

func splitn(sep string, n int, orig string) map[string]string {
	parts := strings.SplitN(orig, sep, n)
	res := make(map[string]string, len(parts))
	for i, v := range parts {
		res["_"+strconv.Itoa(i)] = v
	}
	return res
}

// substring creates a substring of the given string.
//
// If start is < 0, this calls string[:length].
//
// If start is >= 0 and length < 0, this calls string[start:]
//
// Otherwise, this calls string[start, length].
func substring(start, length int, s string) string {
	if start < 0 {
		return s[:length]
	}
	if length < 0 {
		return s[start:]
	}
	return s[start:length]
}
<commit_msg>Fix substr var names and comments<commit_after>package sprig

import (
	"encoding/base32"
	"encoding/base64"
	"fmt"
	"reflect"
	"strconv"
	"strings"

	util "github.com/aokoli/goutils"
)

func base64encode(v string) string {
	return base64.StdEncoding.EncodeToString([]byte(v))
}

func base64decode(v string) string {
	data, err := base64.StdEncoding.DecodeString(v)
	if err != nil {
		return err.Error()
	}
	return string(data)
}

func base32encode(v string) string {
	return base32.StdEncoding.EncodeToString([]byte(v))
}

func base32decode(v string) string {
	data, err := base32.StdEncoding.DecodeString(v)
	if err != nil {
		return err.Error()
	}
	return string(data)
}

func abbrev(width int, s string) string {
	if width < 4 {
		return s
	}
	r, _ := util.Abbreviate(s, width)
	return r
}

func abbrevboth(left, right int, s string) string {
	if right < 4 || left > 0 && right < 7 {
		return s
	}
	r, _ := util.AbbreviateFull(s, left, right)
	return r
}
func initials(s string) string {
	// Wrap this just to eliminate the var args, which templates don't do well.
	return util.Initials(s)
}

func randAlphaNumeric(count int) string {
	// It is not possible, it appears, to actually generate an error here.
	r, _ := util.RandomAlphaNumeric(count)
	return r
}

func randAlpha(count int) string {
	r, _ := util.RandomAlphabetic(count)
	return r
}

func randAscii(count int) string {
	r, _ := util.RandomAscii(count)
	return r
}

func randNumeric(count int) string {
	r, _ := util.RandomNumeric(count)
	return r
}

func untitle(str string) string {
	return util.Uncapitalize(str)
}

func quote(str ...interface{}) string {
	out := make([]string, len(str))
	for i, s := range str {
		out[i] = fmt.Sprintf("%q", strval(s))
	}
	return strings.Join(out, " ")
}

func squote(str ...interface{}) string {
	out := make([]string, len(str))
	for i, s := range str {
		out[i] = fmt.Sprintf("'%v'", s)
	}
	return strings.Join(out, " ")
}

func cat(v ...interface{}) string {
	r := strings.TrimSpace(strings.Repeat("%v ", len(v)))
	return fmt.Sprintf(r, v...)
}

func indent(spaces int, v string) string {
	pad := strings.Repeat(" ", spaces)
	return pad + strings.Replace(v, "\n", "\n"+pad, -1)
}

func nindent(spaces int, v string) string {
	return "\n" + indent(spaces, v)
}

func replace(old, new, src string) string {
	return strings.Replace(src, old, new, -1)
}

func plural(one, many string, count int) string {
	if count == 1 {
		return one
	}
	return many
}

func strslice(v interface{}) []string {
	switch v := v.(type) {
	case []string:
		return v
	case []interface{}:
		l := len(v)
		b := make([]string, l)
		for i := 0; i < l; i++ {
			b[i] = strval(v[i])
		}
		return b
	default:
		val := reflect.ValueOf(v)
		switch val.Kind() {
		case reflect.Array, reflect.Slice:
			l := val.Len()
			b := make([]string, l)
			for i := 0; i < l; i++ {
				b[i] = strval(val.Index(i).Interface())
			}
			return b
		default:
			return []string{strval(v)}
		}
	}
}

func strval(v interface{}) string {
	switch v := v.(type) {
	case string:
		return v
	case []byte:
		return string(v)
	case error:
		return v.Error()
	case fmt.Stringer:
		return v.String()
	default:
		return fmt.Sprintf("%v", v)
	}
}

func trunc(c int, s string) string {
	if len(s) <= c {
		return s
	}
	return s[0:c]
}

func join(sep string, v interface{}) string {
	return strings.Join(strslice(v), sep)
}

func split(sep, orig string) map[string]string {
	parts := strings.Split(orig, sep)
	res := make(map[string]string, len(parts))
	for i, v := range parts {
		res["_"+strconv.Itoa(i)] = v
	}
	return res
}

func splitn(sep string, n int, orig string) map[string]string {
	parts := strings.SplitN(orig, sep, n)
	res := make(map[string]string, len(parts))
	for i, v := range parts {
		res["_"+strconv.Itoa(i)] = v
	}
	return res
}

// substring creates a substring of the given string.
//
// If start is < 0, this calls string[:end].
//
// If start is >= 0 and end < 0, this calls string[start:]
//
// Otherwise, this calls string[start, end].
func substring(start, end int, s string) string {
	if start < 0 {
		return s[:end]
	}
	if end < 0 {
		return s[start:]
	}
	return s[start:end]
}
<|endoftext|>