<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 options

import (
	utilfeature "k8s.io/apiserver/pkg/util/feature"
	"k8s.io/klog"
	"k8s.io/kubernetes/pkg/client/leaderelectionconfig"
	"k8s.io/kubernetes/pkg/util/flag"
	// add the kubernetes feature gates
	_ "k8s.io/kubernetes/pkg/features"

	"github.com/spf13/pflag"
	"k8s.io/cloud-provider-alibaba-cloud/cmd/cloudprovider/app"
)

// AddFlags adds flags for a specific ExternalCMServer to the specified FlagSet
func AddFlags(ccm *app.ServerCCM, fs *pflag.FlagSet) {
	fs.Int32Var(&ccm.Generic.Port, "port", ccm.Generic.Port, "The port that the cloud-controller-manager'ccm http service runs on.")
	fs.Var(flag.IPVar{Val: &ccm.Generic.Address}, "address", "The IP address to serve on (set to 0.0.0.0 for all interfaces).")
	fs.StringVar(&ccm.KubeCloudShared.CloudProvider.Name, "cloud-provider", ccm.KubeCloudShared.CloudProvider.Name, "The provider of cloud services. Cannot be empty.")
	fs.StringVar(&ccm.KubeCloudShared.CloudProvider.CloudConfigFile, "cloud-config", ccm.KubeCloudShared.CloudProvider.CloudConfigFile, "The path to the cloud provider configuration file. Empty string for no configuration file.")
	fs.BoolVar(&ccm.KubeCloudShared.AllowUntaggedCloud, "allow-untagged-cloud", false, "Allow the cluster to run without the cluster-id on cloud instances. This is a legacy mode of operation and a cluster-id will be required in the future.")
	fs.DurationVar(&ccm.Generic.MinResyncPeriod.Duration, "min-resync-period", ccm.Generic.MinResyncPeriod.Duration, "The resync period in reflectors will be random between MinResyncPeriod and 2*MinResyncPeriod.")
	fs.DurationVar(&ccm.KubeCloudShared.NodeMonitorPeriod.Duration, "node-monitor-period", ccm.KubeCloudShared.NodeMonitorPeriod.Duration,
		"The period for syncing NodeStatus in NodeController.")
	fs.DurationVar(&ccm.NodeStatusUpdateFrequency.Duration, "node-status-update-frequency", ccm.NodeStatusUpdateFrequency.Duration, "Specifies how often the controller updates nodes' status.")
	// TODO: remove --service-account-private-key-file 6 months after 1.8 is released (~1.10)
	//fs.StringVar(&ccm.ServiceAccountKeyFile, "service-account-private-key-file", ccm.ServiceAccountKeyFile, "Filename containing a PEM-encoded private RSA or ECDSA key used to sign service account tokens.")
	fs.BoolVar(&ccm.KubeCloudShared.UseServiceAccountCredentials, "use-service-account-credentials", ccm.KubeCloudShared.UseServiceAccountCredentials, "If true, use individual service account credentials for each controller.")
	fs.DurationVar(&ccm.KubeCloudShared.RouteReconciliationPeriod.Duration, "route-reconciliation-period", ccm.KubeCloudShared.RouteReconciliationPeriod.Duration, "The period for reconciling routes created for nodes by cloud provider.")
	fs.BoolVar(&ccm.KubeCloudShared.ConfigureCloudRoutes, "configure-cloud-routes", true, "Should CIDRs allocated by allocate-node-cidrs be configured on the cloud provider.")
	fs.BoolVar(&ccm.Generic.Debugging.EnableProfiling, "profiling", true, "Enable profiling via web interface host:port/debug/pprof/.")
	fs.BoolVar(&ccm.Generic.Debugging.EnableContentionProfiling, "contention-profiling", false, "Enable lock contention profiling, if profiling is enabled.")
	fs.StringVar(&ccm.KubeCloudShared.ClusterCIDR, "cluster-cidr", ccm.KubeCloudShared.ClusterCIDR, "CIDR Range for Pods in cluster.")
	fs.StringVar(&ccm.KubeCloudShared.ClusterName, "cluster-name", ccm.KubeCloudShared.ClusterName, "The instance prefix for the cluster.")
	fs.BoolVar(&ccm.KubeCloudShared.AllocateNodeCIDRs, "allocate-node-cidrs", false, "Should CIDRs for Pods be allocated and set on the cloud provider.")
	fs.StringVar(&ccm.Master, "master", ccm.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig).")
	fs.StringVar(&ccm.Kubeconfig, "kubeconfig", ccm.Kubeconfig, "Path to kubeconfig file with authorization and master location information.")
	fs.StringVar(&ccm.Generic.ClientConnection.ContentType, "kube-api-content-type", ccm.Generic.ClientConnection.ContentType, "Content type of requests sent to apiserver.")
	fs.Float32Var(&ccm.Generic.ClientConnection.QPS, "kube-api-qps", ccm.Generic.ClientConnection.QPS, "QPS to use while talking with kubernetes apiserver.")
	fs.Int32Var(&ccm.Generic.ClientConnection.Burst, "kube-api-burst", ccm.Generic.ClientConnection.Burst, "Burst to use while talking with kubernetes apiserver.")
	fs.DurationVar(&ccm.Generic.ControllerStartInterval.Duration, "controller-start-interval", ccm.Generic.ControllerStartInterval.Duration, "Interval between starting controller managers.")
	fs.Int32Var(&ccm.ServiceController.ConcurrentServiceSyncs, "concurrent-service-syncs", ccm.ServiceController.ConcurrentServiceSyncs, "The number of services that are allowed to sync concurrently. Larger number = more responsive service management, but more CPU (and network) load")
	err := fs.MarkDeprecated("allow-untagged-cloud", "This flag is deprecated and will be removed in a future release. A cluster-id will be required on cloud instances.")
	if err != nil {
		klog.Warningf("add flags error: %s", err.Error())
	}
	err = fs.MarkDeprecated("service-account-private-key-file", "This flag is currently no-op and will be deleted.")
	if err != nil {
		klog.Warningf("add flags error: %s", err.Error())
	}
	leaderelectionconfig.BindFlags(&ccm.Generic.LeaderElection, fs)

	utilfeature.DefaultMutableFeatureGate.AddFlag(fs)
}
<commit_msg>remove deprecated flag<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 options

import (
	utilfeature "k8s.io/apiserver/pkg/util/feature"
	"k8s.io/klog"
	"k8s.io/kubernetes/pkg/client/leaderelectionconfig"
	"k8s.io/kubernetes/pkg/util/flag"
	// add the kubernetes feature gates
	_ "k8s.io/kubernetes/pkg/features"

	"github.com/spf13/pflag"
	"k8s.io/cloud-provider-alibaba-cloud/cmd/cloudprovider/app"
)

// AddFlags adds flags for a specific ExternalCMServer to the specified FlagSet
func AddFlags(ccm *app.ServerCCM, fs *pflag.FlagSet) {
	fs.Int32Var(&ccm.Generic.Port, "port", ccm.Generic.Port, "The port that the cloud-controller-manager'ccm http service runs on.")
	fs.Var(flag.IPVar{Val: &ccm.Generic.Address}, "address", "The IP address to serve on (set to 0.0.0.0 for all interfaces).")
	fs.StringVar(&ccm.KubeCloudShared.CloudProvider.Name, "cloud-provider", ccm.KubeCloudShared.CloudProvider.Name, "The provider of cloud services. Cannot be empty.")
	fs.StringVar(&ccm.KubeCloudShared.CloudProvider.CloudConfigFile, "cloud-config", ccm.KubeCloudShared.CloudProvider.CloudConfigFile, "The path to the cloud provider configuration file. Empty string for no configuration file.")
	fs.BoolVar(&ccm.KubeCloudShared.AllowUntaggedCloud, "allow-untagged-cloud", false, "Allow the cluster to run without the cluster-id on cloud instances. This is a legacy mode of operation and a cluster-id will be required in the future.")
	fs.DurationVar(&ccm.Generic.MinResyncPeriod.Duration, "min-resync-period", ccm.Generic.MinResyncPeriod.Duration, "The resync period in reflectors will be random between MinResyncPeriod and 2*MinResyncPeriod.")
	fs.DurationVar(&ccm.KubeCloudShared.NodeMonitorPeriod.Duration, "node-monitor-period", ccm.KubeCloudShared.NodeMonitorPeriod.Duration,
		"The period for syncing NodeStatus in NodeController.")
	fs.DurationVar(&ccm.NodeStatusUpdateFrequency.Duration, "node-status-update-frequency", ccm.NodeStatusUpdateFrequency.Duration, "Specifies how often the controller updates nodes' status.")
	fs.BoolVar(&ccm.KubeCloudShared.UseServiceAccountCredentials, "use-service-account-credentials", ccm.KubeCloudShared.UseServiceAccountCredentials, "If true, use individual service account credentials for each controller.")
	fs.DurationVar(&ccm.KubeCloudShared.RouteReconciliationPeriod.Duration, "route-reconciliation-period", ccm.KubeCloudShared.RouteReconciliationPeriod.Duration, "The period for reconciling routes created for nodes by cloud provider.")
	fs.BoolVar(&ccm.KubeCloudShared.ConfigureCloudRoutes, "configure-cloud-routes", true, "Should CIDRs allocated by allocate-node-cidrs be configured on the cloud provider.")
	fs.BoolVar(&ccm.Generic.Debugging.EnableProfiling, "profiling", true, "Enable profiling via web interface host:port/debug/pprof/.")
	fs.BoolVar(&ccm.Generic.Debugging.EnableContentionProfiling, "contention-profiling", false, "Enable lock contention profiling, if profiling is enabled.")
	fs.StringVar(&ccm.KubeCloudShared.ClusterCIDR, "cluster-cidr", ccm.KubeCloudShared.ClusterCIDR, "CIDR Range for Pods in cluster.")
	fs.StringVar(&ccm.KubeCloudShared.ClusterName, "cluster-name", ccm.KubeCloudShared.ClusterName, "The instance prefix for the cluster.")
	fs.BoolVar(&ccm.KubeCloudShared.AllocateNodeCIDRs, "allocate-node-cidrs", false, "Should CIDRs for Pods be allocated and set on the cloud provider.")
	fs.StringVar(&ccm.Master, "master", ccm.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig).")
	fs.StringVar(&ccm.Kubeconfig, "kubeconfig", ccm.Kubeconfig, "Path to kubeconfig file with authorization and master location information.")
	fs.StringVar(&ccm.Generic.ClientConnection.ContentType, "kube-api-content-type", ccm.Generic.ClientConnection.ContentType, "Content type of requests sent to apiserver.")
	fs.Float32Var(&ccm.Generic.ClientConnection.QPS, "kube-api-qps", ccm.Generic.ClientConnection.QPS, "QPS to use while talking with kubernetes apiserver.")
	fs.Int32Var(&ccm.Generic.ClientConnection.Burst, "kube-api-burst", ccm.Generic.ClientConnection.Burst, "Burst to use while talking with kubernetes apiserver.")
	fs.DurationVar(&ccm.Generic.ControllerStartInterval.Duration, "controller-start-interval", ccm.Generic.ControllerStartInterval.Duration, "Interval between starting controller managers.")
	fs.Int32Var(&ccm.ServiceController.ConcurrentServiceSyncs, "concurrent-service-syncs", ccm.ServiceController.ConcurrentServiceSyncs, "The number of services that are allowed to sync concurrently. Larger number = more responsive service management, but more CPU (and network) load")
	err := fs.MarkDeprecated("allow-untagged-cloud", "This flag is deprecated and will be removed in a future release. A cluster-id will be required on cloud instances.")
	if err != nil {
		klog.Warningf("add flags error: %s", err.Error())
	}
	leaderelectionconfig.BindFlags(&ccm.Generic.LeaderElection, fs)

	utilfeature.DefaultMutableFeatureGate.AddFlag(fs)
}
<|endoftext|>


<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 app

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/json"
	"encoding/pem"
	"io/ioutil"
	"math/big"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"sync"
	"testing"
	"time"

	cfsslconfig "github.com/cloudflare/cfssl/config"
	cfsslsigner "github.com/cloudflare/cfssl/signer"
	cfssllocal "github.com/cloudflare/cfssl/signer/local"

	certapi "k8s.io/api/certificates/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	restclient "k8s.io/client-go/rest"
	certutil "k8s.io/client-go/util/cert"
)

// Test_buildClientCertificateManager validates that we can build a local client cert
// manager that will use the bootstrap client until we get a valid cert, then use our
// provided identity on subsequent requests.
func Test_buildClientCertificateManager(t *testing.T) {
	testDir, err := ioutil.TempDir("", "kubeletcert")
	if err != nil {
		t.Fatal(err)
	}
	defer func() { os.RemoveAll(testDir) }()

	serverPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	serverCA, err := certutil.NewSelfSignedCACert(certutil.Config{
		CommonName: "the-test-framework",
	}, serverPrivateKey)
	if err != nil {
		t.Fatal(err)
	}
	server := &csrSimulator{
		t:                t,
		serverPrivateKey: serverPrivateKey,
		serverCA:         serverCA,
	}
	s := httptest.NewServer(server)
	defer s.Close()

	config1 := &restclient.Config{
		UserAgent: "FirstClient",
		Host:      s.URL,
	}
	config2 := &restclient.Config{
		UserAgent: "SecondClient",
		Host:      s.URL,
	}

	nodeName := types.NodeName("test")
	m, err := buildClientCertificateManager(config1, config2, testDir, nodeName)
	if err != nil {
		t.Fatal(err)
	}
	defer m.Stop()
	r := m.(rotater)

	// get an expired CSR (simulating historical output)
	server.backdate = 2 * time.Hour
	server.expectUserAgent = "FirstClient"
	ok, err := r.RotateCerts()
	if !ok || err != nil {
		t.Fatalf("unexpected rotation err: %t %v", ok, err)
	}
	if cert := m.Current(); cert != nil {
		t.Fatalf("Unexpected cert, should be expired: %#v", cert)
	}
	fi := getFileInfo(testDir)
	if len(fi) != 2 {
		t.Fatalf("Unexpected directory contents: %#v", fi)
	}

	// if m.Current() == nil, then we try again and get a valid
	// client
	server.backdate = 0
	server.expectUserAgent = "FirstClient"
	if ok, err := r.RotateCerts(); !ok || err != nil {
		t.Fatalf("unexpected rotation err: %t %v", ok, err)
	}
	if cert := m.Current(); cert == nil {
		t.Fatalf("Unexpected cert, should be valid: %#v", cert)
	}
	fi = getFileInfo(testDir)
	if len(fi) != 2 {
		t.Fatalf("Unexpected directory contents: %#v", fi)
	}

	// if m.Current() != nil, then we should use the second client
	server.expectUserAgent = "SecondClient"
	if ok, err := r.RotateCerts(); !ok || err != nil {
		t.Fatalf("unexpected rotation err: %t %v", ok, err)
	}
	if cert := m.Current(); cert == nil {
		t.Fatalf("Unexpected cert, should be valid: %#v", cert)
	}
	fi = getFileInfo(testDir)
	if len(fi) != 2 {
		t.Fatalf("Unexpected directory contents: %#v", fi)
	}
}

func Test_buildClientCertificateManager_populateCertDir(t *testing.T) {
	testDir, err := ioutil.TempDir("", "kubeletcert")
	if err != nil {
		t.Fatal(err)
	}
	defer func() { os.RemoveAll(testDir) }()

	// when no cert is provided, write nothing to disk
	config1 := &restclient.Config{
		UserAgent: "FirstClient",
		Host:      "http://localhost",
	}
	config2 := &restclient.Config{
		UserAgent: "SecondClient",
		Host:      "http://localhost",
	}
	nodeName := types.NodeName("test")
	if _, err := buildClientCertificateManager(config1, config2, testDir, nodeName); err != nil {
		t.Fatal(err)
	}
	fi := getFileInfo(testDir)
	if len(fi) != 0 {
		t.Fatalf("Unexpected directory contents: %#v", fi)
	}

	// an invalid cert should be ignored
	config2.CertData = []byte("invalid contents")
	config2.KeyData = []byte("invalid contents")
	if _, err := buildClientCertificateManager(config1, config2, testDir, nodeName); err == nil {
		t.Fatal("unexpected non error")
	}
	fi = getFileInfo(testDir)
	if len(fi) != 0 {
		t.Fatalf("Unexpected directory contents: %#v", fi)
	}

	// an expired client certificate should be written to disk, because the cert manager can
	// use config1 to refresh it and the cert manager won't return it for clients.
	config2.CertData, config2.KeyData = genClientCert(t, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour))
	if _, err := buildClientCertificateManager(config1, config2, testDir, nodeName); err != nil {
		t.Fatal(err)
	}
	fi = getFileInfo(testDir)
	if len(fi) != 2 {
		t.Fatalf("Unexpected directory contents: %#v", fi)
	}

	// a valid, non-expired client certificate should be written to disk
	config2.CertData, config2.KeyData = genClientCert(t, time.Now().Add(-time.Hour), time.Now().Add(24*time.Hour))
	if _, err := buildClientCertificateManager(config1, config2, testDir, nodeName); err != nil {
		t.Fatal(err)
	}
	fi = getFileInfo(testDir)
	if len(fi) != 2 {
		t.Fatalf("Unexpected directory contents: %#v", fi)
	}

}

func getFileInfo(dir string) map[string]os.FileInfo {
	fi := make(map[string]os.FileInfo)
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if path == dir {
			return nil
		}
		fi[path] = info
		if !info.IsDir() {
			os.Remove(path)
		}
		return nil
	})
	return fi
}

type rotater interface {
	RotateCerts() (bool, error)
}

func getCSR(req *http.Request) (*certapi.CertificateSigningRequest, error) {
	if req.Body == nil {
		return nil, nil
	}
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return nil, err
	}
	csr := &certapi.CertificateSigningRequest{}
	if err := json.Unmarshal(body, csr); err != nil {
		return nil, err
	}
	return csr, nil
}

func mustMarshal(obj interface{}) []byte {
	data, err := json.Marshal(obj)
	if err != nil {
		panic(err)
	}
	return data
}

type csrSimulator struct {
	t *testing.T

	serverPrivateKey *ecdsa.PrivateKey
	serverCA         *x509.Certificate
	backdate         time.Duration

	expectUserAgent string

	lock sync.Mutex
	csr  *certapi.CertificateSigningRequest
}

func (s *csrSimulator) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	s.lock.Lock()
	defer s.lock.Unlock()
	t := s.t

	// filter out timeouts as csrSimulator don't support them
	q := req.URL.Query()
	q.Del("timeout")
	q.Del("timeoutSeconds")
	req.URL.RawQuery = q.Encode()

	t.Logf("Request %q %q %q", req.Method, req.URL, req.UserAgent())

	if len(s.expectUserAgent) > 0 && req.UserAgent() != s.expectUserAgent {
		t.Errorf("Unexpected user agent: %s", req.UserAgent())
	}

	switch {
	case req.Method == "POST" && req.URL.Path == "/apis/certificates.k8s.io/v1beta1/certificatesigningrequests":
		csr, err := getCSR(req)
		if err != nil {
			t.Fatal(err)
		}
		if csr.Name == "" {
			csr.Name = "test-csr"
		}

		csr.UID = types.UID("1")
		csr.ResourceVersion = "1"
		data := mustMarshal(csr)
		w.Header().Set("Content-Type", "application/json")
		w.Write(data)

		csr = csr.DeepCopy()
		csr.ResourceVersion = "2"
		var usages []string
		for _, usage := range csr.Spec.Usages {
			usages = append(usages, string(usage))
		}
		policy := &cfsslconfig.Signing{
			Default: &cfsslconfig.SigningProfile{
				Usage:        usages,
				Expiry:       time.Hour,
				ExpiryString: time.Hour.String(),
				Backdate:     s.backdate,
			},
		}
		cfs, err := cfssllocal.NewSigner(s.serverPrivateKey, s.serverCA, cfsslsigner.DefaultSigAlgo(s.serverPrivateKey), policy)
		if err != nil {
			t.Fatal(err)
		}
		csr.Status.Certificate, err = cfs.Sign(cfsslsigner.SignRequest{
			Request: string(csr.Spec.Request),
		})
		if err != nil {
			t.Fatal(err)
		}
		csr.Status.Conditions = []certapi.CertificateSigningRequestCondition{
			{Type: certapi.CertificateApproved},
		}
		s.csr = csr

	case req.Method == "GET" && req.URL.Path == "/apis/certificates.k8s.io/v1beta1/certificatesigningrequests" && req.URL.RawQuery == "fieldSelector=metadata.name%3Dtest-csr&limit=500&resourceVersion=0":
		if s.csr == nil {
			t.Fatalf("no csr")
		}
		csr := s.csr.DeepCopy()

		data := mustMarshal(&certapi.CertificateSigningRequestList{
			ListMeta: metav1.ListMeta{
				ResourceVersion: "2",
			},
			Items: []certapi.CertificateSigningRequest{
				*csr,
			},
		})
		w.Header().Set("Content-Type", "application/json")
		w.Write(data)

	case req.Method == "GET" && req.URL.Path == "/apis/certificates.k8s.io/v1beta1/certificatesigningrequests" && req.URL.RawQuery == "fieldSelector=metadata.name%3Dtest-csr&resourceVersion=2&watch=true":
		if s.csr == nil {
			t.Fatalf("no csr")
		}
		csr := s.csr.DeepCopy()

		data := mustMarshal(&metav1.WatchEvent{
			Type: "ADDED",
			Object: runtime.RawExtension{
				Raw: mustMarshal(csr),
			},
		})
		w.Header().Set("Content-Type", "application/json")
		w.Write(data)

	default:
		t.Fatalf("unexpected request: %s %s", req.Method, req.URL)
	}
}

// genClientCert generates an x509 certificate for testing. Certificate and key
// are returned in PEM encoding.
func genClientCert(t *testing.T, from, to time.Time) ([]byte, []byte) {
	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	keyRaw, err := x509.MarshalECPrivateKey(key)
	if err != nil {
		t.Fatal(err)
	}
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		t.Fatal(err)
	}
	cert := &x509.Certificate{
		SerialNumber: serialNumber,
		Subject:      pkix.Name{Organization: []string{"Acme Co"}},
		NotBefore:    from,
		NotAfter:     to,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
		BasicConstraintsValid: true,
	}
	certRaw, err := x509.CreateCertificate(rand.Reader, cert, cert, key.Public(), key)
	if err != nil {
		t.Fatal(err)
	}
	return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certRaw}),
		pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyRaw})
}
<commit_msg>Update unit test with expected query parameters<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 app

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/json"
	"encoding/pem"
	"io/ioutil"
	"math/big"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"sync"
	"testing"
	"time"

	cfsslconfig "github.com/cloudflare/cfssl/config"
	cfsslsigner "github.com/cloudflare/cfssl/signer"
	cfssllocal "github.com/cloudflare/cfssl/signer/local"

	certapi "k8s.io/api/certificates/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	restclient "k8s.io/client-go/rest"
	certutil "k8s.io/client-go/util/cert"
)

// Test_buildClientCertificateManager validates that we can build a local client cert
// manager that will use the bootstrap client until we get a valid cert, then use our
// provided identity on subsequent requests.
func Test_buildClientCertificateManager(t *testing.T) {
	testDir, err := ioutil.TempDir("", "kubeletcert")
	if err != nil {
		t.Fatal(err)
	}
	defer func() { os.RemoveAll(testDir) }()

	serverPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	serverCA, err := certutil.NewSelfSignedCACert(certutil.Config{
		CommonName: "the-test-framework",
	}, serverPrivateKey)
	if err != nil {
		t.Fatal(err)
	}
	server := &csrSimulator{
		t:                t,
		serverPrivateKey: serverPrivateKey,
		serverCA:         serverCA,
	}
	s := httptest.NewServer(server)
	defer s.Close()

	config1 := &restclient.Config{
		UserAgent: "FirstClient",
		Host:      s.URL,
	}
	config2 := &restclient.Config{
		UserAgent: "SecondClient",
		Host:      s.URL,
	}

	nodeName := types.NodeName("test")
	m, err := buildClientCertificateManager(config1, config2, testDir, nodeName)
	if err != nil {
		t.Fatal(err)
	}
	defer m.Stop()
	r := m.(rotater)

	// get an expired CSR (simulating historical output)
	server.backdate = 2 * time.Hour
	server.SetExpectUserAgent("FirstClient")
	ok, err := r.RotateCerts()
	if !ok || err != nil {
		t.Fatalf("unexpected rotation err: %t %v", ok, err)
	}
	if cert := m.Current(); cert != nil {
		t.Fatalf("Unexpected cert, should be expired: %#v", cert)
	}
	fi := getFileInfo(testDir)
	if len(fi) != 2 {
		t.Fatalf("Unexpected directory contents: %#v", fi)
	}

	// if m.Current() == nil, then we try again and get a valid
	// client
	server.backdate = 0
	server.SetExpectUserAgent("FirstClient")
	if ok, err := r.RotateCerts(); !ok || err != nil {
		t.Fatalf("unexpected rotation err: %t %v", ok, err)
	}
	if cert := m.Current(); cert == nil {
		t.Fatalf("Unexpected cert, should be valid: %#v", cert)
	}
	fi = getFileInfo(testDir)
	if len(fi) != 2 {
		t.Fatalf("Unexpected directory contents: %#v", fi)
	}

	// if m.Current() != nil, then we should use the second client
	server.SetExpectUserAgent("SecondClient")
	if ok, err := r.RotateCerts(); !ok || err != nil {
		t.Fatalf("unexpected rotation err: %t %v", ok, err)
	}
	if cert := m.Current(); cert == nil {
		t.Fatalf("Unexpected cert, should be valid: %#v", cert)
	}
	fi = getFileInfo(testDir)
	if len(fi) != 2 {
		t.Fatalf("Unexpected directory contents: %#v", fi)
	}
}

func Test_buildClientCertificateManager_populateCertDir(t *testing.T) {
	testDir, err := ioutil.TempDir("", "kubeletcert")
	if err != nil {
		t.Fatal(err)
	}
	defer func() { os.RemoveAll(testDir) }()

	// when no cert is provided, write nothing to disk
	config1 := &restclient.Config{
		UserAgent: "FirstClient",
		Host:      "http://localhost",
	}
	config2 := &restclient.Config{
		UserAgent: "SecondClient",
		Host:      "http://localhost",
	}
	nodeName := types.NodeName("test")
	if _, err := buildClientCertificateManager(config1, config2, testDir, nodeName); err != nil {
		t.Fatal(err)
	}
	fi := getFileInfo(testDir)
	if len(fi) != 0 {
		t.Fatalf("Unexpected directory contents: %#v", fi)
	}

	// an invalid cert should be ignored
	config2.CertData = []byte("invalid contents")
	config2.KeyData = []byte("invalid contents")
	if _, err := buildClientCertificateManager(config1, config2, testDir, nodeName); err == nil {
		t.Fatal("unexpected non error")
	}
	fi = getFileInfo(testDir)
	if len(fi) != 0 {
		t.Fatalf("Unexpected directory contents: %#v", fi)
	}

	// an expired client certificate should be written to disk, because the cert manager can
	// use config1 to refresh it and the cert manager won't return it for clients.
	config2.CertData, config2.KeyData = genClientCert(t, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour))
	if _, err := buildClientCertificateManager(config1, config2, testDir, nodeName); err != nil {
		t.Fatal(err)
	}
	fi = getFileInfo(testDir)
	if len(fi) != 2 {
		t.Fatalf("Unexpected directory contents: %#v", fi)
	}

	// a valid, non-expired client certificate should be written to disk
	config2.CertData, config2.KeyData = genClientCert(t, time.Now().Add(-time.Hour), time.Now().Add(24*time.Hour))
	if _, err := buildClientCertificateManager(config1, config2, testDir, nodeName); err != nil {
		t.Fatal(err)
	}
	fi = getFileInfo(testDir)
	if len(fi) != 2 {
		t.Fatalf("Unexpected directory contents: %#v", fi)
	}

}

func getFileInfo(dir string) map[string]os.FileInfo {
	fi := make(map[string]os.FileInfo)
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if path == dir {
			return nil
		}
		fi[path] = info
		if !info.IsDir() {
			os.Remove(path)
		}
		return nil
	})
	return fi
}

type rotater interface {
	RotateCerts() (bool, error)
}

func getCSR(req *http.Request) (*certapi.CertificateSigningRequest, error) {
	if req.Body == nil {
		return nil, nil
	}
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return nil, err
	}
	csr := &certapi.CertificateSigningRequest{}
	if err := json.Unmarshal(body, csr); err != nil {
		return nil, err
	}
	return csr, nil
}

func mustMarshal(obj interface{}) []byte {
	data, err := json.Marshal(obj)
	if err != nil {
		panic(err)
	}
	return data
}

type csrSimulator struct {
	t *testing.T

	serverPrivateKey *ecdsa.PrivateKey
	serverCA         *x509.Certificate
	backdate         time.Duration

	userAgentLock   sync.Mutex
	expectUserAgent string

	lock sync.Mutex
	csr  *certapi.CertificateSigningRequest
}

func (s *csrSimulator) SetExpectUserAgent(a string) {
	s.userAgentLock.Lock()
	defer s.userAgentLock.Unlock()
	s.expectUserAgent = a
}
func (s *csrSimulator) ExpectUserAgent() string {
	s.userAgentLock.Lock()
	defer s.userAgentLock.Unlock()
	return s.expectUserAgent
}

func (s *csrSimulator) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	s.lock.Lock()
	defer s.lock.Unlock()
	t := s.t

	// filter out timeouts as csrSimulator don't support them
	q := req.URL.Query()
	q.Del("timeout")
	q.Del("timeoutSeconds")
	q.Del("allowWatchBookmarks")
	req.URL.RawQuery = q.Encode()

	t.Logf("Request %q %q %q", req.Method, req.URL, req.UserAgent())

	if a := s.ExpectUserAgent(); len(a) > 0 && req.UserAgent() != a {
		t.Errorf("Unexpected user agent: %s", req.UserAgent())
	}

	switch {
	case req.Method == "POST" && req.URL.Path == "/apis/certificates.k8s.io/v1beta1/certificatesigningrequests":
		csr, err := getCSR(req)
		if err != nil {
			t.Fatal(err)
		}
		if csr.Name == "" {
			csr.Name = "test-csr"
		}

		csr.UID = types.UID("1")
		csr.ResourceVersion = "1"
		data := mustMarshal(csr)
		w.Header().Set("Content-Type", "application/json")
		w.Write(data)

		csr = csr.DeepCopy()
		csr.ResourceVersion = "2"
		var usages []string
		for _, usage := range csr.Spec.Usages {
			usages = append(usages, string(usage))
		}
		policy := &cfsslconfig.Signing{
			Default: &cfsslconfig.SigningProfile{
				Usage:        usages,
				Expiry:       time.Hour,
				ExpiryString: time.Hour.String(),
				Backdate:     s.backdate,
			},
		}
		cfs, err := cfssllocal.NewSigner(s.serverPrivateKey, s.serverCA, cfsslsigner.DefaultSigAlgo(s.serverPrivateKey), policy)
		if err != nil {
			t.Fatal(err)
		}
		csr.Status.Certificate, err = cfs.Sign(cfsslsigner.SignRequest{
			Request: string(csr.Spec.Request),
		})
		if err != nil {
			t.Fatal(err)
		}
		csr.Status.Conditions = []certapi.CertificateSigningRequestCondition{
			{Type: certapi.CertificateApproved},
		}
		s.csr = csr

	case req.Method == "GET" && req.URL.Path == "/apis/certificates.k8s.io/v1beta1/certificatesigningrequests" && req.URL.RawQuery == "fieldSelector=metadata.name%3Dtest-csr&limit=500&resourceVersion=0":
		if s.csr == nil {
			t.Fatalf("no csr")
		}
		csr := s.csr.DeepCopy()

		data := mustMarshal(&certapi.CertificateSigningRequestList{
			ListMeta: metav1.ListMeta{
				ResourceVersion: "2",
			},
			Items: []certapi.CertificateSigningRequest{
				*csr,
			},
		})
		w.Header().Set("Content-Type", "application/json")
		w.Write(data)

	case req.Method == "GET" && req.URL.Path == "/apis/certificates.k8s.io/v1beta1/certificatesigningrequests" && req.URL.RawQuery == "fieldSelector=metadata.name%3Dtest-csr&resourceVersion=2&watch=true":
		if s.csr == nil {
			t.Fatalf("no csr")
		}
		csr := s.csr.DeepCopy()

		data := mustMarshal(&metav1.WatchEvent{
			Type: "ADDED",
			Object: runtime.RawExtension{
				Raw: mustMarshal(csr),
			},
		})
		w.Header().Set("Content-Type", "application/json")
		w.Write(data)

	default:
		t.Fatalf("unexpected request: %s %s", req.Method, req.URL)
	}
}

// genClientCert generates an x509 certificate for testing. Certificate and key
// are returned in PEM encoding.
func genClientCert(t *testing.T, from, to time.Time) ([]byte, []byte) {
	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	keyRaw, err := x509.MarshalECPrivateKey(key)
	if err != nil {
		t.Fatal(err)
	}
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		t.Fatal(err)
	}
	cert := &x509.Certificate{
		SerialNumber: serialNumber,
		Subject:      pkix.Name{Organization: []string{"Acme Co"}},
		NotBefore:    from,
		NotAfter:     to,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
		BasicConstraintsValid: true,
	}
	certRaw, err := x509.CreateCertificate(rand.Reader, cert, cert, key.Public(), key)
	if err != nil {
		t.Fatal(err)
	}
	return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certRaw}),
		pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyRaw})
}
<|endoftext|>


<commit_before>// +build js,!windows

package syscall

import (
	"github.com/gopherjs/gopherjs/js"
	"unsafe"
)

func init() {
	process := js.Global.Get("process")
	if !process.IsUndefined() {
		jsEnv := process.Get("env")
		envkeys := js.Global.Get("Object").Call("keys", jsEnv)
		envs = make([]string, envkeys.Length())
		for i := 0; i < envkeys.Length(); i++ {
			key := envkeys.Index(i).Str()
			envs[i] = key + "=" + jsEnv.Get(key).Str()
		}
	}
}

var syscallModule js.Object
var alreadyTriedToLoad = false
var minusOne = -1

func syscall(name string) js.Object {
	defer recover() // return nil
	if syscallModule == nil {
		if alreadyTriedToLoad {
			return nil
		}
		alreadyTriedToLoad = true
		require := js.Global.Get("require")
		if require.IsUndefined() {
			syscallHandler := js.Global.Get("$syscall")
			if !syscallHandler.IsUndefined() {
				return syscallHandler
			}
			panic("")
		}
		syscallModule = require.Invoke("syscall")
	}
	return syscallModule.Get(name)
}

func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
	if f := syscall("Syscall"); f != nil {
		r := f.Invoke(trap, a1, a2, a3)
		return uintptr(r.Index(0).Int()), uintptr(r.Index(1).Int()), Errno(r.Index(2).Int())
	}
	if trap == SYS_WRITE && (a1 == 1 || a1 == 2) {
		b := js.Global.Call("go$sliceType", js.Global.Get("Go$Uint8")).New(js.InternalObject(a2)).Interface().([]byte)
		printToConsole(b)
		return uintptr(len(b)), 0, 0
	}
	printWarning()
	return uintptr(minusOne), 0, EACCES
}

func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
	if f := syscall("Syscall6"); f != nil {
		r := f.Invoke(trap, a1, a2, a3, a4, a5, a6)
		return uintptr(r.Index(0).Int()), uintptr(r.Index(1).Int()), Errno(r.Index(2).Int())
	}
	printWarning()
	return uintptr(minusOne), 0, EACCES
}

func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
	if f := syscall("Syscall"); f != nil {
		r := f.Invoke(trap, a1, a2, a3)
		return uintptr(r.Index(0).Int()), uintptr(r.Index(1).Int()), Errno(r.Index(2).Int())
	}
	printWarning()
	return uintptr(minusOne), 0, EACCES
}

func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
	if f := syscall("Syscall6"); f != nil {
		r := f.Invoke(trap, a1, a2, a3, a4, a5, a6)
		return uintptr(r.Index(0).Int()), uintptr(r.Index(1).Int()), Errno(r.Index(2).Int())
	}
	printWarning()
	return uintptr(minusOne), 0, EACCES
}

func BytePtrFromString(s string) (*byte, error) {
	return (*byte)(unsafe.Pointer(js.Global.Call("go$stringToBytes", s, true).Unsafe())), nil
}
<commit_msg>added syscall.setenv_c native<commit_after>// +build js,!windows

package syscall

import (
	"github.com/gopherjs/gopherjs/js"
	"unsafe"
)

func init() {
	process := js.Global.Get("process")
	if !process.IsUndefined() {
		jsEnv := process.Get("env")
		envkeys := js.Global.Get("Object").Call("keys", jsEnv)
		envs = make([]string, envkeys.Length())
		for i := 0; i < envkeys.Length(); i++ {
			key := envkeys.Index(i).Str()
			envs[i] = key + "=" + jsEnv.Get(key).Str()
		}
	}
}

func setenv_c(k, v string) {
	process := js.Global.Get("process")
	if !process.IsUndefined() {
		process.Get("env").Set(k, v)
	}
}

var syscallModule js.Object
var alreadyTriedToLoad = false
var minusOne = -1

func syscall(name string) js.Object {
	defer recover() // return nil
	if syscallModule == nil {
		if alreadyTriedToLoad {
			return nil
		}
		alreadyTriedToLoad = true
		require := js.Global.Get("require")
		if require.IsUndefined() {
			syscallHandler := js.Global.Get("$syscall")
			if !syscallHandler.IsUndefined() {
				return syscallHandler
			}
			panic("")
		}
		syscallModule = require.Invoke("syscall")
	}
	return syscallModule.Get(name)
}

func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
	if f := syscall("Syscall"); f != nil {
		r := f.Invoke(trap, a1, a2, a3)
		return uintptr(r.Index(0).Int()), uintptr(r.Index(1).Int()), Errno(r.Index(2).Int())
	}
	if trap == SYS_WRITE && (a1 == 1 || a1 == 2) {
		b := js.Global.Call("go$sliceType", js.Global.Get("Go$Uint8")).New(js.InternalObject(a2)).Interface().([]byte)
		printToConsole(b)
		return uintptr(len(b)), 0, 0
	}
	printWarning()
	return uintptr(minusOne), 0, EACCES
}

func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
	if f := syscall("Syscall6"); f != nil {
		r := f.Invoke(trap, a1, a2, a3, a4, a5, a6)
		return uintptr(r.Index(0).Int()), uintptr(r.Index(1).Int()), Errno(r.Index(2).Int())
	}
	printWarning()
	return uintptr(minusOne), 0, EACCES
}

func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
	if f := syscall("Syscall"); f != nil {
		r := f.Invoke(trap, a1, a2, a3)
		return uintptr(r.Index(0).Int()), uintptr(r.Index(1).Int()), Errno(r.Index(2).Int())
	}
	printWarning()
	return uintptr(minusOne), 0, EACCES
}

func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
	if f := syscall("Syscall6"); f != nil {
		r := f.Invoke(trap, a1, a2, a3, a4, a5, a6)
		return uintptr(r.Index(0).Int()), uintptr(r.Index(1).Int()), Errno(r.Index(2).Int())
	}
	printWarning()
	return uintptr(minusOne), 0, EACCES
}

func BytePtrFromString(s string) (*byte, error) {
	return (*byte)(unsafe.Pointer(js.Global.Call("go$stringToBytes", s, true).Unsafe())), nil
}
<|endoftext|>


<commit_before>package main

import (
    "bytes"
    "flag"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
    "runtime/debug"
    "time"
    "github.com/johnny-morrice/godelbrot/rest"
    lib "github.com/johnny-morrice/godelbrot/libgodelbrot"
)

func main() {
    args := readArgs()

    web := &webclient{}
    web.args = args
    web.client.Timeout = time.Millisecond * time.Duration(web.args.timeout)

    if args.cycle || args.newrq {
        info, ierr := lib.ReadInfo(os.Stdin)
        fatalguard(ierr)
        web.req = info.GenRequest()
    }

    zoomparts := map[string]bool {
        "xmin": true,
        "xmax": true,
        "ymin": true,
        "ymax": true,
    }

    flag.Visit(func (fl *flag.Flag) {
        _, ok := zoomparts[fl.Name]
        if ok {
            web.zoom = true
        }
    })

    // Ugly
    var r io.Reader
    if args.newrq {
        rqi, err := web.newrq()
        fatalguard(err)
        reqr, jerr := jsonr(rqi)
        fatalguard(jerr)
        r = reqr
    } else if args.getrq != "" {
        rqi, err := web.getrq()
        fatalguard(err)
        reqr, jerr := jsonr(rqi)
        fatalguard(jerr)
        r = reqr
    } else if args.getimag != "" {
        png, err := web.getimag()
        fatalguard(err)
        r = png
    } else if args.cycle {
        png, err := web.cycle()
        fatalguard(err)
        r = png
    }

    _, cpyerr := io.Copy(os.Stdout, r)
    fatalguard(cpyerr)
}

type webclient struct {
    args params
    client http.Client
    req lib.Request
    zoom bool
}

func (web *webclient) cycle() (io.Reader, error) {
    newresp, err := web.newrq()
    if err != nil {
        return nil, err
    }
    for {
        url := web.url(newresp.RQStatusURL)
        rqstat, err := web.getrqraw(url)
        if err != nil {
            return nil, err
        }
        switch rqstat.State {
        case "done":
            url := web.url(rqstat.ImageURL)
            return web.getimagraw(url)
        case "error":
            weberr := fmt.Errorf("RQGetResp error: %v", rqstat.Error)
            return nil, weberr
        case "wait":
            // NOP
        default:
            panic(fmt.Errorf("Unknown status: %v", rqstat.State))
        }
    }
}

func (web *webclient) newrq() (*rest.RQNewResp, error) {
    return web.newrqraw(web.url("renderqueue"))
}

func (web *webclient) newrqraw(url string) (*rest.RQNewResp, error) {
    renreq, rerr := web.renreq()
    if rerr != nil {
        return nil, addstack(rerr)
    }
    buff := &bytes.Buffer{}
    werr := rest.WriteReq(buff, renreq)
    if werr != nil {
        return nil, addstack(werr)
    }
    resp, err := web.client.Post(url, "application/json", buff)
    if err != nil {
        return nil, err
    }
    if resp.StatusCode != 200 {
        return nil, httpError(resp)
    }
    defer resp.Body.Close()
    rqi := &rest.RQNewResp{}
    derr := decode(resp.Body, rqi)
    return rqi, addstack(derr)
}

func (web *webclient) getrq() (*rest.RQGetResp, error) {
    url := web.url(fmt.Sprintf("renderqueue/%v", web.args.getrq))
    return web.getrqraw(url)
}

func (web *webclient) getrqraw(url string) (*rest.RQGetResp, error) {
    resp, err := web.client.Get(url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    rqi := &rest.RQGetResp{}
    derr := decode(resp.Body, rqi)
    return rqi, addstack(derr)
}

func (web *webclient) getimag() (io.Reader, error) {
    url := fmt.Sprintf("image/%v", web.args.getimag)
    return web.getimagraw(web.url(url))
}

func (web *webclient) getimagraw(url string) (io.Reader, error) {
    resp, err := web.client.Get(url)
    if err != nil {
        return nil, err
    }
    if resp.StatusCode != 200 {
        return nil, httpError(resp)
    }
    defer resp.Body.Close()
    buff := &bytes.Buffer{}
    _, cpyerr := io.Copy(buff, resp.Body)
    return buff, addstack(cpyerr)
}

func (web *webclient) renreq() (*rest.RenderRequest, error) {
    renreq := &rest.RenderRequest{}
    renreq.Req = web.req
    if web.zoom {
        renreq.WantZoom = true
        renreq.Target.Xmin = web.args.xmin
        renreq.Target.Xmax = web.args.xmax
        renreq.Target.Ymin = web.args.ymin
        renreq.Target.Ymax = web.args.ymax
    }

    return renreq, nil
}

func (web *webclient) url(last string) string {
    args := web.args
    if web.args.prefix == "" {
        return fmt.Sprintf("http://%v:%v/%v/",
            args.addr, args.port, last)
    } else {
        return fmt.Sprintf("http://%v:%v/%v/%v",
                args.addr, args.port, args.prefix, last)
    }
}

func httpError(resp *http.Response) error {
    buff := &bytes.Buffer{}
    err := resp.Write(buff)
    if err != nil {
        panic(err)
    }
    return fmt.Errorf("Response:\n%v", buff)
}

func decode(r io.Reader, any interface{}) error {
    dec := json.NewDecoder(r)
    return dec.Decode(any)
}

func jsonr(any interface{}) (io.Reader, error) {
    buff := &bytes.Buffer{}
    enc := json.NewEncoder(buff)
    err := enc.Encode(any)
    return buff, err
}

func fatalguard(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error: %v", err)
        os.Exit(1)
    }
}

func addstack(err error) error {
    if err == nil {
        return nil
    } else {
        return fmt.Errorf("%v\n%v", err, string(debug.Stack()))
    }

}

func readArgs() params {
    args := params{}
    flag.StringVar(&args.addr, "remote", "localhost", "Remote address of restfulbrot service")
    flag.UintVar(&args.port, "port", 9898, "Port of remote service")
    flag.StringVar(&args.prefix, "prefix", "", "Prefix of service URL")
    flag.UintVar(&args.timeout, "timeout", 1000, "Web request abort timeout (milliseconds)")
    flag.UintVar(&args.wait, "wait", 100, "Time between requests (spider delay)")
    flag.BoolVar(&args.newrq, "newrq", false, "Add new item to render queue (info from stdin)")
    flag.StringVar(&args.getrq, "getrq", "", "Get status of render queue item")
    flag.StringVar(&args.getimag, "getimag", "", "Download fractal render (png to stdout)")
    flag.BoolVar(&args.cycle, "cycle", true,
        "Wait for fractal to render (info from stdin, png to stdout")
    flag.Parse()
    return args
}

type params struct {
    addr string
    port uint
    prefix string
    timeout uint
    wait uint

    newrq bool
    getrq string
    getimag string
    cycle bool

    config string
    xmin uint
    xmax uint
    ymin uint
    ymax uint
}<commit_msg>Add debug mode to clientbrot<commit_after>package main

import (
    "bytes"
    "flag"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "os"
    "runtime/debug"
    "time"
    "github.com/johnny-morrice/godelbrot/rest"
    lib "github.com/johnny-morrice/godelbrot/libgodelbrot"
)

func main() {
    args := readArgs()

    web := newWebClient(args)

    if args.cycle || args.newrq {
        info, ierr := lib.ReadInfo(os.Stdin)
        fatalguard(ierr)
        web.req = info.GenRequest()
    }

    zoomparts := map[string]bool {
        "xmin": true,
        "xmax": true,
        "ymin": true,
        "ymax": true,
    }

    flag.Visit(func (fl *flag.Flag) {
        _, ok := zoomparts[fl.Name]
        if ok {
            web.zoom = true
        }
    })

    // Ugly
    var r io.Reader
    if args.newrq {
        rqi, err := web.newrq()
        fatalguard(err)
        reqr, jerr := jsonr(rqi)
        fatalguard(jerr)
        r = reqr
    } else if args.getrq != "" {
        rqi, err := web.getrq()
        fatalguard(err)
        reqr, jerr := jsonr(rqi)
        fatalguard(jerr)
        r = reqr
    } else if args.getimag != "" {
        png, err := web.getimag()
        fatalguard(err)
        r = png
    } else if args.cycle {
        png, err := web.cycle()
        fatalguard(err)
        r = png
    }

    _, cpyerr := io.Copy(os.Stdout, r)
    fatalguard(cpyerr)
}

type webclient struct {
    args params
    client http.Client
    req lib.Request
    zoom bool
    tick *time.Ticker

}

func newWebClient(args params) *webclient {
    web := &webclient{}
    web.args = args
    web.client.Timeout = time.Millisecond * time.Duration(web.args.timeout)
    return web
}

func (web *webclient) cycle() (io.Reader, error) {
    newresp, err := web.newrq()
    if err != nil {
        return nil, err
    }
    for {
        url := web.url(newresp.RQStatusURL)
        rqstat, err := web.getrqraw(url)
        if err != nil {
            return nil, err
        }
        switch rqstat.State {
        case "done":
            url := web.url(rqstat.ImageURL)
            return web.getimagraw(url)
        case "error":
            weberr := fmt.Errorf("RQGetResp error: %v", rqstat.Error)
            return nil, weberr
        case "wait":
            // NOP
        default:
            panic(fmt.Errorf("Unknown status: %v", rqstat.State))
        }
    }
}

func (web *webclient) newrq() (*rest.RQNewResp, error) {
    return web.newrqraw(web.url("renderqueue"))
}

func (web *webclient) newrqraw(url string) (*rest.RQNewResp, error) {
    renreq, rerr := web.renreq()
    if rerr != nil {
        return nil, addstack(rerr)
    }
    buff := &bytes.Buffer{}
    werr := rest.WriteReq(buff, renreq)
    if werr != nil {
        return nil, addstack(werr)
    }
    resp, err := web.post(url, "application/json", buff)
    if err != nil {
        return nil, err
    }
    if resp.StatusCode != 200 {
        return nil, httpError(resp)
    }
    defer resp.Body.Close()
    rqi := &rest.RQNewResp{}
    derr := web.decode(resp.Body, rqi)
    return rqi, addstack(derr)
}

func (web *webclient) getrq() (*rest.RQGetResp, error) {
    url := web.url(fmt.Sprintf("renderqueue/%v", web.args.getrq))
    return web.getrqraw(url)
}

func (web *webclient) getrqraw(url string) (*rest.RQGetResp, error) {
    resp, err := web.get(url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    rqi := &rest.RQGetResp{}
    derr := web.decode(resp.Body, rqi)
    return rqi, addstack(derr)
}

func (web *webclient) getimag() (io.Reader, error) {
    url := fmt.Sprintf("image/%v", web.args.getimag)
    return web.getimagraw(web.url(url))
}

func (web *webclient) getimagraw(url string) (io.Reader, error) {
    resp, err := web.get(url)
    if err != nil {
        return nil, err
    }
    if resp.StatusCode != 200 {
        return nil, httpError(resp)
    }
    defer resp.Body.Close()
    buff := &bytes.Buffer{}
    _, cpyerr := io.Copy(buff, resp.Body)
    return buff, addstack(cpyerr)
}

func (web *webclient) renreq() (*rest.RenderRequest, error) {
    renreq := &rest.RenderRequest{}
    renreq.Req = web.req
    if web.zoom {
        renreq.WantZoom = true
        renreq.Target.Xmin = web.args.xmin
        renreq.Target.Xmax = web.args.xmax
        renreq.Target.Ymin = web.args.ymin
        renreq.Target.Ymax = web.args.ymax
    }

    return renreq, nil
}

func (web *webclient) url(last string) string {
    args := web.args
    if web.args.prefix == "" {
        return fmt.Sprintf("http://%v:%v/%v/",
            args.addr, args.port, last)
    } else {
        return fmt.Sprintf("http://%v:%v/%v/%v",
                args.addr, args.port, args.prefix, last)
    }
}

func (web *webclient) get(url string) (r *http.Response, err error) {
    web.cautiously(func () {
        if web.args.debug {
            log.Printf("GET %v", url)
        }
        r, err = web.client.Get(url)
        if web.args.debug {
            web.reportResponse(r, err)
        }
    })
    return
}

func (web *webclient) post(url, ctype string, body io.Reader) (r *http.Response, err error) {
    web.cautiously(func () {
        if web.args.debug {
            log.Printf("POST %v", url)
        }
        r, err = web.client.Post(url, ctype, body)
        if web.args.debug {
            web.reportResponse(r, err)
        }
    })
    return
}

func (web *webclient) reportResponse(r *http.Response, err error) {
    if err != nil {
        log.Printf("Error: %v", err)
    }
    log.Printf("Status: %v", r.Status)
    ctypeHeads := r.Header["Content-Type"]
    if len(ctypeHeads) != 1 {
        log.Printf("Bad Content-Type header")
    } else {
        log.Printf("Content-Type: %v", ctypeHeads[0])
    }
}

func (web *webclient) cautiously(f func()) {
    if web.tick == nil {
        web.tick = time.NewTicker(time.Duration(web.args.ticktime) * time.Millisecond)
    } else {
        <-web.tick.C
    }
    f()
}

func (web *webclient) decode(r io.Reader, any interface{}) error {
    if web.args.debug {
        buff := &bytes.Buffer{}
        r = io.TeeReader(r, buff)
        derr := decode(r, any)
        log.Printf("Decoded: %v", buff.String())
        return derr
    }
    return decode(r, any)
}

func httpError(resp *http.Response) error {
    buff := &bytes.Buffer{}
    err := resp.Write(buff)
    if err != nil {
        panic(err)
    }
    return fmt.Errorf("Response:\n%v", buff)
}

func decode(r io.Reader, any interface{}) error {
    dec := json.NewDecoder(r)
    return dec.Decode(any)
}

func jsonr(any interface{}) (io.Reader, error) {
    buff := &bytes.Buffer{}
    enc := json.NewEncoder(buff)
    err := enc.Encode(any)
    return buff, err
}

func fatalguard(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error: %v", err)
        os.Exit(1)
    }
}

func addstack(err error) error {
    if err == nil {
        return nil
    } else {
        return fmt.Errorf("%v\n%v", err, string(debug.Stack()))
    }

}

func readArgs() params {
    args := params{}
    flag.StringVar(&args.addr, "remote", "localhost", "Remote address of restfulbrot service")
    flag.UintVar(&args.port, "port", 9898, "Port of remote service")
    flag.StringVar(&args.prefix, "prefix", "", "Prefix of service URL")
    flag.UintVar(&args.timeout, "timeout", 1000, "Web request abort timeout (milliseconds)")
    flag.UintVar(&args.ticktime, "ticktime", 100, "Max one request per tick (milliseconds)")
    flag.BoolVar(&args.newrq, "newrq", false, "Add new item to render queue (info from stdin)")
    flag.StringVar(&args.getrq, "getrq", "", "Get status of render queue item")
    flag.StringVar(&args.getimag, "getimag", "", "Download fractal render (png to stdout)")
    flag.BoolVar(&args.cycle, "cycle", true,
        "Wait for fractal to render (info from stdin, png to stdout")
    flag.BoolVar(&args.debug, "debug", false, "Verbose debug mode")
    flag.Parse()
    return args
}

type params struct {
    addr string
    port uint
    prefix string
    timeout uint
    ticktime uint
    debug bool

    newrq bool
    getrq string
    getimag string
    cycle bool

    config string
    xmin uint
    xmax uint
    ymin uint
    ymax uint
}<|endoftext|>


<commit_before>package bootfromvolume

import (
	"testing"

	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	th "github.com/rackspace/gophercloud/testhelper"
)

func TestCreateOpts(t *testing.T) {
	base := servers.CreateOpts{
		Name:      "createdserver",
		ImageRef:  "asdfasdfasdf",
		FlavorRef: "performance1-1",
	}

	ext := CreateOptsExt{
		CreateOptsBuilder: base,
		BlockDevice: []BlockDevice{
			BlockDevice{
				UUID:            "123456",
				SourceType:      Image,
				DestinationType: "volume",
				VolumeSize:      10,
			},
		},
	}

	expected := `
    {
      "server": {
        "name": "createdserver",
        "imageRef": "asdfasdfasdf",
        "flavorRef": "performance1-1",
				"flavorName": "",
				"imageName": "",
        "block_device_mapping_v2":[
          {
            "uuid":"123456",
            "source_type":"image",
            "destination_type":"volume",
            "boot_index": "0",
            "delete_on_termination": "false",
            "volume_size": "10"
          }
        ]
      }
    }
  `
	actual, err := ext.ToServerCreateMap()
	th.AssertNoErr(t, err)
	th.CheckJSONEquals(t, expected, actual)
}
<commit_msg>fix formatting in bfv<commit_after>package bootfromvolume

import (
	"testing"

	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	th "github.com/rackspace/gophercloud/testhelper"
)

func TestCreateOpts(t *testing.T) {
	base := servers.CreateOpts{
		Name:      "createdserver",
		ImageRef:  "asdfasdfasdf",
		FlavorRef: "performance1-1",
	}

	ext := CreateOptsExt{
		CreateOptsBuilder: base,
		BlockDevice: []BlockDevice{
			BlockDevice{
				UUID:            "123456",
				SourceType:      Image,
				DestinationType: "volume",
				VolumeSize:      10,
			},
		},
	}

	expected := `
    {
      "server": {
        "name": "createdserver",
        "imageRef": "asdfasdfasdf",
        "flavorRef": "performance1-1",
	"flavorName": "",
	"imageName": "",
        "block_device_mapping_v2":[
          {
            "uuid":"123456",
            "source_type":"image",
            "destination_type":"volume",
            "boot_index": "0",
            "delete_on_termination": "false",
            "volume_size": "10"
          }
        ]
      }
    }
  `
	actual, err := ext.ToServerCreateMap()
	th.AssertNoErr(t, err)
	th.CheckJSONEquals(t, expected, actual)
}
<|endoftext|>


<commit_before>package rules

// TODO
<commit_msg>Add unit tests for firewall rules<commit_after>package rules

import (
	"fmt"
	"net/http"
	"testing"

	fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
	"github.com/rackspace/gophercloud/pagination"
	th "github.com/rackspace/gophercloud/testhelper"
)

func TestURLs(t *testing.T) {
	th.SetupHTTP()
	defer th.TeardownHTTP()

	th.AssertEquals(t, th.Endpoint()+"v2.0/fw/firewall_rules", rootURL(fake.ServiceClient()))
}

func TestList(t *testing.T) {
	th.SetupHTTP()
	defer th.TeardownHTTP()

	th.Mux.HandleFunc("/v2.0/fw/firewall_rules", func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "GET")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, `
{
    "firewall_rules": [
        {
            "protocol": "tcp",
            "description": "ssh rule",
            "source_port": null,
            "source_ip_address": null,
            "destination_ip_address": "192.168.1.0/24",
            "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
            "position": 2,
            "destination_port": "22",
            "id": "f03bd950-6c56-4f5e-a307-45967078f507",
            "name": "ssh_form_any",
            "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
            "enabled": true,
            "action": "allow",
            "ip_version": 4,
            "shared": false
        },
        {
            "protocol": "udp",
            "description": "udp rule",
            "source_port": null,
            "source_ip_address": null,
            "destination_ip_address": null,
            "firewall_policy_id": "98d7fb51-698c-4123-87e8-f1eee6b5ab7e",
            "position": 1,
            "destination_port": null,
            "id": "ab7bd950-6c56-4f5e-a307-45967078f890",
            "name": "deny_all_udp",
            "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
            "enabled": true,
            "action": "deny",
            "ip_version": 4,
            "shared": false
        }
    ]
}
        `)
	})

	count := 0

	List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
		count++
		actual, err := ExtractRules(page)
		if err != nil {
			t.Errorf("Failed to extract members: %v", err)
			return false, err
		}

		expected := []Rule{
			Rule{
				Protocol:             "tcp",
				Description:          "ssh rule",
				SourcePort:           "",
				SourceIPAddress:      "",
				DestinationIPAddress: "192.168.1.0/24",
				PolicyID:             "e2a5fb51-698c-4898-87e8-f1eee6b50919",
				Position:             2,
				DestinationPort:      "22",
				ID:                   "f03bd950-6c56-4f5e-a307-45967078f507",
				Name:                 "ssh_form_any",
				TenantID:             "80cf934d6ffb4ef5b244f1c512ad1e61",
				Enabled:              true,
				Action:               "allow",
				IPVersion:            4,
				Shared:               false,
			},
			Rule{
				Protocol:             "udp",
				Description:          "udp rule",
				SourcePort:           "",
				SourceIPAddress:      "",
				DestinationIPAddress: "",
				PolicyID:             "98d7fb51-698c-4123-87e8-f1eee6b5ab7e",
				Position:             1,
				DestinationPort:      "",
				ID:                   "ab7bd950-6c56-4f5e-a307-45967078f890",
				Name:                 "deny_all_udp",
				TenantID:             "80cf934d6ffb4ef5b244f1c512ad1e61",
				Enabled:              true,
				Action:               "deny",
				IPVersion:            4,
				Shared:               false,
			},
		}

		th.CheckDeepEquals(t, expected, actual)

		return true, nil
	})

	if count != 1 {
		t.Errorf("Expected 1 page, got %d", count)
	}
}

func TestCreate(t *testing.T) {
	th.SetupHTTP()
	defer th.TeardownHTTP()

	th.Mux.HandleFunc("/v2.0/fw/firewall_rules", func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "POST")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
		th.TestHeader(t, r, "Content-Type", "application/json")
		th.TestHeader(t, r, "Accept", "application/json")
		th.TestJSONRequest(t, r, `
{
	"firewall_rule": {
		"protocol": "tcp",
		"description": "ssh rule",
		"destination_ip_address": "192.168.1.0/24",
		"destination_port": "22",
		"name": "ssh_form_any",
		"action": "allow",
		"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61"
	}
}
      `)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusCreated)

		fmt.Fprintf(w, `
{
	"firewall_rule":{
		"protocol": "tcp",
		"description": "ssh rule",
		"source_port": null,
		"source_ip_address": null,
		"destination_ip_address": "192.168.1.0/24",
		"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
		"position": 2,
		"destination_port": "22",
		"id": "f03bd950-6c56-4f5e-a307-45967078f507",
		"name": "ssh_form_any",
		"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
		"enabled": true,
		"action": "allow",
		"ip_version": 4,
		"shared": false
	}
}
        `)
	})

	options := CreateOpts{
		TenantID:             "80cf934d6ffb4ef5b244f1c512ad1e61",
		Protocol:             "tcp",
		Description:          "ssh rule",
		DestinationIPAddress: "192.168.1.0/24",
		DestinationPort:      "22",
		Name:                 "ssh_form_any",
		Action:               "allow",
	}

	_, err := Create(fake.ServiceClient(), options).Extract()
	th.AssertNoErr(t, err)
}

func TestGet(t *testing.T) {
	th.SetupHTTP()
	defer th.TeardownHTTP()

	th.Mux.HandleFunc("/v2.0/fw/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "GET")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, `
{
	"firewall_rule":{
		"protocol": "tcp",
		"description": "ssh rule",
		"source_port": null,
		"source_ip_address": null,
		"destination_ip_address": "192.168.1.0/24",
		"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
		"position": 2,
		"destination_port": "22",
		"id": "f03bd950-6c56-4f5e-a307-45967078f507",
		"name": "ssh_form_any",
		"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
		"enabled": true,
		"action": "allow",
		"ip_version": 4,
		"shared": false
	}
}
        `)
	})

	rule, err := Get(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507").Extract()
	th.AssertNoErr(t, err)

	th.AssertEquals(t, "tcp", rule.Protocol)
	th.AssertEquals(t, "ssh rule", rule.Description)
	th.AssertEquals(t, "192.168.1.0/24", rule.DestinationIPAddress)
	th.AssertEquals(t, "e2a5fb51-698c-4898-87e8-f1eee6b50919", rule.PolicyID)
	th.AssertEquals(t, 2, rule.Position)
	th.AssertEquals(t, "22", rule.DestinationPort)
	th.AssertEquals(t, "f03bd950-6c56-4f5e-a307-45967078f507", rule.ID)
	th.AssertEquals(t, "ssh_form_any", rule.Name)
	th.AssertEquals(t, "80cf934d6ffb4ef5b244f1c512ad1e61", rule.TenantID)
	th.AssertEquals(t, true, rule.Enabled)
	th.AssertEquals(t, "allow", rule.Action)
	th.AssertEquals(t, 4, rule.IPVersion)
	th.AssertEquals(t, false, rule.Shared)
}

func TestUpdate(t *testing.T) {
	th.SetupHTTP()
	defer th.TeardownHTTP()

	th.Mux.HandleFunc("/v2.0/fw/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "PUT")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
		th.TestHeader(t, r, "Content-Type", "application/json")
		th.TestHeader(t, r, "Accept", "application/json")
		th.TestJSONRequest(t, r, `
{
	"firewall_rule":{
		"protocol": "tcp",
		"description": "ssh rule",
		"destination_ip_address": "192.168.1.0/24",
		"destination_port": "22",
		"source_ip_address": null,
		"source_port": null,
		"name": "ssh_form_any",
		"action": "allow"
	}
}
	`)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, `
{
	"firewall_rule":{
		"protocol": "tcp",
		"description": "ssh rule",
		"source_port": null,
		"source_ip_address": null,
		"destination_ip_address": "192.168.1.0/24",
		"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
		"position": 2,
		"destination_port": "22",
		"id": "f03bd950-6c56-4f5e-a307-45967078f507",
		"name": "ssh_form_any",
		"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
		"enabled": true,
		"action": "allow",
		"ip_version": 4,
		"shared": false
	}
}
		`)
	})

	name := "ssh_form_any"
	description := "ssh rule"
	destinationIPAddress := "192.168.1.0/24"
	destinationPort := "22"
	empty := ""

	options := UpdateOpts{
		Protocol:             "tcp",
		Description:          &description,
		DestinationIPAddress: &destinationIPAddress,
		DestinationPort:      &destinationPort,
		Name:                 &name,
		SourceIPAddress:      &empty,
		SourcePort:           &empty,
		Action:               "allow",
	}

	_, err := Update(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507", options).Extract()
	th.AssertNoErr(t, err)
}

func TestDelete(t *testing.T) {
	th.SetupHTTP()
	defer th.TeardownHTTP()

	th.Mux.HandleFunc("/v2.0/fw/firewall_rules/4ec89077-d057-4a2b-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "DELETE")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
		w.WriteHeader(http.StatusNoContent)
	})

	res := Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304")
	th.AssertNoErr(t, res.Err)
}
<|endoftext|>


<commit_before>package app

import (
	"io"
	"os"

	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/cliutil"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/config"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/util"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
)

// BaseAppSetup sets up a cli.App for the given commands and config
func BaseAppSetup(flags []cli.Flag, commands []cli.Command) (app *cli.App, err error) {
	app = cli.NewApp()
	app.Version = lib.Version
	app.Flags = flags
	app.Commands = commands
	app.Writer = io.MultiWriter(
		log.LogFile,
		os.Stdout,
	)
	app.ErrWriter = io.MultiWriter(
		log.LogFile,
		os.Stderr,
	)

	app.Commands = cliutil.CreateMultiwordCommands(app.Commands)
	return

}

// SetClientAndConfig adds the client and config to the given app.
// it abstracts away setting the Metadata on the app. Mostly so that we get some type-checking.
// without it - it's just assigning to an interface{} which will always succeed,
// and which would near-inevitably result in hard-to-debug null pointer errors down the line.
func SetClientAndConfig(app *cli.App, client lib.Client, config config.Manager) {
	if app.Metadata == nil {
		app.Metadata = make(map[string]interface{})
	}
	app.Metadata["client"] = client
	app.Metadata["config"] = config
	app.Metadata["prompter"] = util.NewPrompter()
}
<commit_msg>Add Usage to the App so that it doesn't say 'a new cli application'<commit_after>package app

import (
	"io"
	"os"

	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/cliutil"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/config"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/util"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
)

// BaseAppSetup sets up a cli.App for the given commands and config
func BaseAppSetup(flags []cli.Flag, commands []cli.Command) (app *cli.App, err error) {
	app = cli.NewApp()
	app.Version = lib.Version
	app.Flags = flags
	app.Commands = commands
	app.Usage = "Command-line interface to Bytemark Cloud services"
	app.Writer = io.MultiWriter(
		log.LogFile,
		os.Stdout,
	)
	app.ErrWriter = io.MultiWriter(
		log.LogFile,
		os.Stderr,
	)

	app.Commands = cliutil.CreateMultiwordCommands(app.Commands)
	return

}

// SetClientAndConfig adds the client and config to the given app.
// it abstracts away setting the Metadata on the app. Mostly so that we get some type-checking.
// without it - it's just assigning to an interface{} which will always succeed,
// and which would near-inevitably result in hard-to-debug null pointer errors down the line.
func SetClientAndConfig(app *cli.App, client lib.Client, config config.Manager) {
	if app.Metadata == nil {
		app.Metadata = make(map[string]interface{})
	}
	app.Metadata["client"] = client
	app.Metadata["config"] = config
	app.Metadata["prompter"] = util.NewPrompter()
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"encoding/json"
	"sort"
	"strings"

	"github.com/restic/restic/internal/errors"
	"github.com/restic/restic/internal/restic"
	"github.com/spf13/cobra"
)

var cmdForget = &cobra.Command{
	Use:   "forget [flags] [snapshot ID] [...]",
	Short: "Remove snapshots from the repository",
	Long: `
The "forget" command removes snapshots according to a policy. Please note that
this command really only deletes the snapshot object in the repository, which
is a reference to data stored there. In order to remove this (now unreferenced)
data after 'forget' was run successfully, see the 'prune' command. `,
	DisableAutoGenTag: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runForget(forgetOptions, globalOptions, args)
	},
}

// ForgetOptions collects all options for the forget command.
type ForgetOptions struct {
	Last     int
	Hourly   int
	Daily    int
	Weekly   int
	Monthly  int
	Yearly   int
	KeepTags restic.TagLists

	Host    string
	Tags    restic.TagLists
	Paths   []string
	Compact bool

	// Grouping
	GroupBy string
	DryRun  bool
	Prune   bool
}

var forgetOptions ForgetOptions

func init() {
	cmdRoot.AddCommand(cmdForget)

	f := cmdForget.Flags()
	f.IntVarP(&forgetOptions.Last, "keep-last", "l", 0, "keep the last `n` snapshots")
	f.IntVarP(&forgetOptions.Hourly, "keep-hourly", "H", 0, "keep the last `n` hourly snapshots")
	f.IntVarP(&forgetOptions.Daily, "keep-daily", "d", 0, "keep the last `n` daily snapshots")
	f.IntVarP(&forgetOptions.Weekly, "keep-weekly", "w", 0, "keep the last `n` weekly snapshots")
	f.IntVarP(&forgetOptions.Monthly, "keep-monthly", "m", 0, "keep the last `n` monthly snapshots")
	f.IntVarP(&forgetOptions.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots")

	f.Var(&forgetOptions.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)")
	// Sadly the commonly used shortcut `H` is already used.
	f.StringVar(&forgetOptions.Host, "host", "", "only consider snapshots with the given `host`")
	// Deprecated since 2017-03-07.
	f.StringVar(&forgetOptions.Host, "hostname", "", "only consider snapshots with the given `hostname` (deprecated)")
	f.Var(&forgetOptions.Tags, "tag", "only consider snapshots which include this `taglist` in the format `tag[,tag,...]` (can be specified multiple times)")
	f.StringArrayVar(&forgetOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` (can be specified multiple times)")
	f.BoolVarP(&forgetOptions.Compact, "compact", "c", false, "use compact format")

	f.StringVarP(&forgetOptions.GroupBy, "group-by", "g", "host,paths", "string for grouping snapshots by host,paths,tags")
	f.BoolVarP(&forgetOptions.DryRun, "dry-run", "n", false, "do not delete anything, just print what would be done")
	f.BoolVar(&forgetOptions.Prune, "prune", false, "automatically run the 'prune' command if snapshots have been removed")

	f.SortFlags = false
}

func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	lock, err := lockRepoExclusive(repo)
	defer unlockRepo(lock)
	if err != nil {
		return err
	}

	// group by hostname and dirs
	type key struct {
		Hostname string
		Paths    []string
		Tags     []string
	}
	snapshotGroups := make(map[string]restic.Snapshots)

	var GroupByTag bool
	var GroupByHost bool
	var GroupByPath bool
	var GroupOptionList []string

	GroupOptionList = strings.Split(opts.GroupBy, ",")

	for _, option := range GroupOptionList {
		switch option {
		case "host":
			GroupByHost = true
		case "paths":
			GroupByPath = true
		case "tags":
			GroupByTag = true
		case "":
		default:
			return errors.Fatal("unknown grouping option: '" + option + "'")
		}
	}

	removeSnapshots := 0

	ctx, cancel := context.WithCancel(gopts.ctx)
	defer cancel()
	for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
		if len(args) > 0 {
			// When explicit snapshots args are given, remove them immediately.
			if !opts.DryRun {
				h := restic.Handle{Type: restic.SnapshotFile, Name: sn.ID().String()}
				if err = repo.Backend().Remove(gopts.ctx, h); err != nil {
					return err
				}
				Verbosef("removed snapshot %v\n", sn.ID().Str())
				removeSnapshots++
			} else {
				Verbosef("would have removed snapshot %v\n", sn.ID().Str())
			}
		} else {
			// Determining grouping-keys
			var tags []string
			var hostname string
			var paths []string

			if GroupByTag {
				tags = sn.Tags
				sort.StringSlice(tags).Sort()
			}
			if GroupByHost {
				hostname = sn.Hostname
			}
			if GroupByPath {
				paths = sn.Paths
			}

			sort.StringSlice(sn.Paths).Sort()
			var k []byte
			var err error

			k, err = json.Marshal(key{Tags: tags, Hostname: hostname, Paths: paths})

			if err != nil {
				return err
			}
			snapshotGroups[string(k)] = append(snapshotGroups[string(k)], sn)
		}
	}
	if len(args) > 0 {
		return nil
	}

	policy := restic.ExpirePolicy{
		Last:    opts.Last,
		Hourly:  opts.Hourly,
		Daily:   opts.Daily,
		Weekly:  opts.Weekly,
		Monthly: opts.Monthly,
		Yearly:  opts.Yearly,
		Tags:    opts.KeepTags,
	}

	if policy.Empty() {
		Verbosef("no policy was specified, no snapshots will be removed\n")
		return nil
	}

	for k, snapshotGroup := range snapshotGroups {
		var key key
		if json.Unmarshal([]byte(k), &key) != nil {
			return err
		}

		// Info
		Verbosef("snapshots")
		var infoStrings []string
		if GroupByTag {
			infoStrings = append(infoStrings, "tags ["+strings.Join(key.Tags, ", ")+"]")
		}
		if GroupByHost {
			infoStrings = append(infoStrings, "host ["+key.Hostname+"]")
		}
		if GroupByPath {
			infoStrings = append(infoStrings, "paths ["+strings.Join(key.Paths, ", ")+"]")
		}
		if infoStrings != nil {
			Verbosef(" for (" + strings.Join(infoStrings, ", ") + ")")
		}
		Verbosef(":\n\n")

		keep, remove := restic.ApplyPolicy(snapshotGroup, policy)

		if len(keep) != 0 && !gopts.Quiet {
			Printf("keep %d snapshots:\n", len(keep))
			PrintSnapshots(globalOptions.stdout, keep, opts.Compact)
			Printf("\n")
		}

		if len(remove) != 0 && !gopts.Quiet {
			Printf("remove %d snapshots:\n", len(remove))
			PrintSnapshots(globalOptions.stdout, remove, opts.Compact)
			Printf("\n")
		}

		removeSnapshots += len(remove)

		if !opts.DryRun {
			for _, sn := range remove {
				h := restic.Handle{Type: restic.SnapshotFile, Name: sn.ID().String()}
				err = repo.Backend().Remove(gopts.ctx, h)
				if err != nil {
					return err
				}
			}
		}
	}

	if removeSnapshots > 0 && opts.Prune {
		Verbosef("%d snapshots have been removed, running prune\n", removeSnapshots)
		if !opts.DryRun {
			return pruneRepository(gopts, repo)
		}
	}

	return nil
}
<commit_msg>forget: Also run prune when only IDs are forgotten<commit_after>package main

import (
	"context"
	"encoding/json"
	"sort"
	"strings"

	"github.com/restic/restic/internal/errors"
	"github.com/restic/restic/internal/restic"
	"github.com/spf13/cobra"
)

var cmdForget = &cobra.Command{
	Use:   "forget [flags] [snapshot ID] [...]",
	Short: "Remove snapshots from the repository",
	Long: `
The "forget" command removes snapshots according to a policy. Please note that
this command really only deletes the snapshot object in the repository, which
is a reference to data stored there. In order to remove this (now unreferenced)
data after 'forget' was run successfully, see the 'prune' command. `,
	DisableAutoGenTag: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runForget(forgetOptions, globalOptions, args)
	},
}

// ForgetOptions collects all options for the forget command.
type ForgetOptions struct {
	Last     int
	Hourly   int
	Daily    int
	Weekly   int
	Monthly  int
	Yearly   int
	KeepTags restic.TagLists

	Host    string
	Tags    restic.TagLists
	Paths   []string
	Compact bool

	// Grouping
	GroupBy string
	DryRun  bool
	Prune   bool
}

var forgetOptions ForgetOptions

func init() {
	cmdRoot.AddCommand(cmdForget)

	f := cmdForget.Flags()
	f.IntVarP(&forgetOptions.Last, "keep-last", "l", 0, "keep the last `n` snapshots")
	f.IntVarP(&forgetOptions.Hourly, "keep-hourly", "H", 0, "keep the last `n` hourly snapshots")
	f.IntVarP(&forgetOptions.Daily, "keep-daily", "d", 0, "keep the last `n` daily snapshots")
	f.IntVarP(&forgetOptions.Weekly, "keep-weekly", "w", 0, "keep the last `n` weekly snapshots")
	f.IntVarP(&forgetOptions.Monthly, "keep-monthly", "m", 0, "keep the last `n` monthly snapshots")
	f.IntVarP(&forgetOptions.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots")

	f.Var(&forgetOptions.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)")
	// Sadly the commonly used shortcut `H` is already used.
	f.StringVar(&forgetOptions.Host, "host", "", "only consider snapshots with the given `host`")
	// Deprecated since 2017-03-07.
	f.StringVar(&forgetOptions.Host, "hostname", "", "only consider snapshots with the given `hostname` (deprecated)")
	f.Var(&forgetOptions.Tags, "tag", "only consider snapshots which include this `taglist` in the format `tag[,tag,...]` (can be specified multiple times)")
	f.StringArrayVar(&forgetOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` (can be specified multiple times)")
	f.BoolVarP(&forgetOptions.Compact, "compact", "c", false, "use compact format")

	f.StringVarP(&forgetOptions.GroupBy, "group-by", "g", "host,paths", "string for grouping snapshots by host,paths,tags")
	f.BoolVarP(&forgetOptions.DryRun, "dry-run", "n", false, "do not delete anything, just print what would be done")
	f.BoolVar(&forgetOptions.Prune, "prune", false, "automatically run the 'prune' command if snapshots have been removed")

	f.SortFlags = false
}

func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error {
	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	lock, err := lockRepoExclusive(repo)
	defer unlockRepo(lock)
	if err != nil {
		return err
	}

	// group by hostname and dirs
	type key struct {
		Hostname string
		Paths    []string
		Tags     []string
	}
	snapshotGroups := make(map[string]restic.Snapshots)

	var GroupByTag bool
	var GroupByHost bool
	var GroupByPath bool
	var GroupOptionList []string

	GroupOptionList = strings.Split(opts.GroupBy, ",")

	for _, option := range GroupOptionList {
		switch option {
		case "host":
			GroupByHost = true
		case "paths":
			GroupByPath = true
		case "tags":
			GroupByTag = true
		case "":
		default:
			return errors.Fatal("unknown grouping option: '" + option + "'")
		}
	}

	removeSnapshots := 0

	ctx, cancel := context.WithCancel(gopts.ctx)
	defer cancel()
	for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
		if len(args) > 0 {
			// When explicit snapshots args are given, remove them immediately.
			if !opts.DryRun {
				h := restic.Handle{Type: restic.SnapshotFile, Name: sn.ID().String()}
				if err = repo.Backend().Remove(gopts.ctx, h); err != nil {
					return err
				}
				Verbosef("removed snapshot %v\n", sn.ID().Str())
				removeSnapshots++
			} else {
				Verbosef("would have removed snapshot %v\n", sn.ID().Str())
			}
		} else {
			// Determining grouping-keys
			var tags []string
			var hostname string
			var paths []string

			if GroupByTag {
				tags = sn.Tags
				sort.StringSlice(tags).Sort()
			}
			if GroupByHost {
				hostname = sn.Hostname
			}
			if GroupByPath {
				paths = sn.Paths
			}

			sort.StringSlice(sn.Paths).Sort()
			var k []byte
			var err error

			k, err = json.Marshal(key{Tags: tags, Hostname: hostname, Paths: paths})

			if err != nil {
				return err
			}
			snapshotGroups[string(k)] = append(snapshotGroups[string(k)], sn)
		}
	}

	policy := restic.ExpirePolicy{
		Last:    opts.Last,
		Hourly:  opts.Hourly,
		Daily:   opts.Daily,
		Weekly:  opts.Weekly,
		Monthly: opts.Monthly,
		Yearly:  opts.Yearly,
		Tags:    opts.KeepTags,
	}

	if policy.Empty() && len(args) == 0 {
		Verbosef("no policy was specified, no snapshots will be removed\n")
	}

	if !policy.Empty() {
		for k, snapshotGroup := range snapshotGroups {
			var key key
			if json.Unmarshal([]byte(k), &key) != nil {
				return err
			}

			// Info
			Verbosef("snapshots")
			var infoStrings []string
			if GroupByTag {
				infoStrings = append(infoStrings, "tags ["+strings.Join(key.Tags, ", ")+"]")
			}
			if GroupByHost {
				infoStrings = append(infoStrings, "host ["+key.Hostname+"]")
			}
			if GroupByPath {
				infoStrings = append(infoStrings, "paths ["+strings.Join(key.Paths, ", ")+"]")
			}
			if infoStrings != nil {
				Verbosef(" for (" + strings.Join(infoStrings, ", ") + ")")
			}
			Verbosef(":\n\n")

			keep, remove := restic.ApplyPolicy(snapshotGroup, policy)

			if len(keep) != 0 && !gopts.Quiet {
				Printf("keep %d snapshots:\n", len(keep))
				PrintSnapshots(globalOptions.stdout, keep, opts.Compact)
				Printf("\n")
			}

			if len(remove) != 0 && !gopts.Quiet {
				Printf("remove %d snapshots:\n", len(remove))
				PrintSnapshots(globalOptions.stdout, remove, opts.Compact)
				Printf("\n")
			}

			removeSnapshots += len(remove)

			if !opts.DryRun {
				for _, sn := range remove {
					h := restic.Handle{Type: restic.SnapshotFile, Name: sn.ID().String()}
					err = repo.Backend().Remove(gopts.ctx, h)
					if err != nil {
						return err
					}
				}
			}
		}
	}

	if removeSnapshots > 0 && opts.Prune {
		Verbosef("%d snapshots have been removed, running prune\n", removeSnapshots)
		if !opts.DryRun {
			return pruneRepository(gopts, repo)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package jwt_test

import (
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"io/ioutil"
	"net/http"
	"reflect"
	"testing"
	"time"
)

var (
	jwtTestDefaultKey []byte
	defaultKeyFunc    jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return jwtTestDefaultKey, nil }
	emptyKeyFunc      jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return nil, nil }
	errorKeyFunc      jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return nil, fmt.Errorf("error loading key") }
	nilKeyFunc        jwt.Keyfunc = nil
)

var jwtTestData = []struct {
	name        string
	tokenString string
	keyfunc     jwt.Keyfunc
	claims      map[string]interface{}
	valid       bool
	errors      uint32
}{
	{
		"basic",
		"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
		defaultKeyFunc,
		map[string]interface{}{"foo": "bar"},
		true,
		0,
	},
	{
		"basic expired",
		"", // autogen
		defaultKeyFunc,
		map[string]interface{}{"foo": "bar", "exp": float64(time.Now().Unix() - 100)},
		false,
		jwt.ValidationErrorExpired,
	},
	{
		"basic nbf",
		"", // autogen
		defaultKeyFunc,
		map[string]interface{}{"foo": "bar", "nbf": float64(time.Now().Unix() + 100)},
		false,
		jwt.ValidationErrorNotValidYet,
	},
	{
		"expired and nbf",
		"", // autogen
		defaultKeyFunc,
		map[string]interface{}{"foo": "bar", "nbf": float64(time.Now().Unix() + 100), "exp": float64(time.Now().Unix() - 100)},
		false,
		jwt.ValidationErrorNotValidYet | jwt.ValidationErrorExpired,
	},
	{
		"basic invalid",
		"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.EhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
		defaultKeyFunc,
		map[string]interface{}{"foo": "bar"},
		false,
		jwt.ValidationErrorSignatureInvalid,
	},
	{
		"basic nokeyfunc",
		"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
		nilKeyFunc,
		map[string]interface{}{"foo": "bar"},
		false,
		jwt.ValidationErrorUnverifiable,
	},
	{
		"basic nokey",
		"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
		emptyKeyFunc,
		map[string]interface{}{"foo": "bar"},
		false,
		jwt.ValidationErrorSignatureInvalid,
	},
	{
		"basic errorkey",
		"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
		errorKeyFunc,
		map[string]interface{}{"foo": "bar"},
		false,
		jwt.ValidationErrorUnverifiable,
	},
}

func init() {
	var e error
	if jwtTestDefaultKey, e = ioutil.ReadFile("test/sample_key.pub"); e != nil {
		panic(e)
	}
}

func makeSample(c map[string]interface{}) string {
	key, e := ioutil.ReadFile("test/sample_key")
	if e != nil {
		panic(e.Error())
	}

	token := jwt.New(jwt.SigningMethodRS256)
	token.Claims = c
	s, e := token.SignedString(key)

	if e != nil {
		panic(e.Error())
	}

	return s
}

func TestJWT(t *testing.T) {
	for _, data := range jwtTestData {
		if data.tokenString == "" {
			data.tokenString = makeSample(data.claims)
		}
		token, err := jwt.Parse(data.tokenString, data.keyfunc)

		if !reflect.DeepEqual(data.claims, token.Claims) {
			t.Errorf("[%v] Claims mismatch. Expecting: %v  Got: %v", data.name, data.claims, token.Claims)
		}
		if data.valid && err != nil {
			t.Errorf("[%v] Error while verifying token: %T:%v", data.name, err, err)
		}
		if !data.valid && err == nil {
			t.Errorf("[%v] Invalid token passed validation", data.name)
		}
		if data.errors != 0 {
			if err == nil {
				t.Errorf("[%v] Expecting error.  Didn't get one.", data.name)
			} else {
				// compare the bitfield part of the error
				if err.(*jwt.ValidationError).Errors != data.errors {
					t.Errorf("[%v] Errors don't match expectation", data.name)
				}

			}
		}
	}
}

func TestParseRequest(t *testing.T) {
	// Bearer token request
	for _, data := range jwtTestData {
		if data.tokenString == "" {
			data.tokenString = makeSample(data.claims)
		}

		r, _ := http.NewRequest("GET", "/", nil)
		r.Header.Set("Authorization", fmt.Sprintf("Bearer %v", data.tokenString))
		token, err := jwt.ParseFromRequest(r, data.keyfunc)

		if !reflect.DeepEqual(data.claims, token.Claims) {
			t.Errorf("[%v] Claims mismatch. Expecting: %v  Got: %v", data.name, data.claims, token.Claims)
		}
		if data.valid && err != nil {
			t.Errorf("[%v] Error while verifying token: %v", data.name, err)
		}
		if !data.valid && err == nil {
			t.Errorf("[%v] Invalid token passed validation", data.name)
		}
	}
}
<commit_msg>added a test condition<commit_after>package jwt_test

import (
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"io/ioutil"
	"net/http"
	"reflect"
	"testing"
	"time"
)

var (
	jwtTestDefaultKey []byte
	defaultKeyFunc    jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return jwtTestDefaultKey, nil }
	emptyKeyFunc      jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return nil, nil }
	errorKeyFunc      jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return nil, fmt.Errorf("error loading key") }
	nilKeyFunc        jwt.Keyfunc = nil
)

var jwtTestData = []struct {
	name        string
	tokenString string
	keyfunc     jwt.Keyfunc
	claims      map[string]interface{}
	valid       bool
	errors      uint32
}{
	{
		"basic",
		"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
		defaultKeyFunc,
		map[string]interface{}{"foo": "bar"},
		true,
		0,
	},
	{
		"basic expired",
		"", // autogen
		defaultKeyFunc,
		map[string]interface{}{"foo": "bar", "exp": float64(time.Now().Unix() - 100)},
		false,
		jwt.ValidationErrorExpired,
	},
	{
		"basic nbf",
		"", // autogen
		defaultKeyFunc,
		map[string]interface{}{"foo": "bar", "nbf": float64(time.Now().Unix() + 100)},
		false,
		jwt.ValidationErrorNotValidYet,
	},
	{
		"expired and nbf",
		"", // autogen
		defaultKeyFunc,
		map[string]interface{}{"foo": "bar", "nbf": float64(time.Now().Unix() + 100), "exp": float64(time.Now().Unix() - 100)},
		false,
		jwt.ValidationErrorNotValidYet | jwt.ValidationErrorExpired,
	},
	{
		"basic invalid",
		"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.EhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
		defaultKeyFunc,
		map[string]interface{}{"foo": "bar"},
		false,
		jwt.ValidationErrorSignatureInvalid,
	},
	{
		"basic nokeyfunc",
		"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
		nilKeyFunc,
		map[string]interface{}{"foo": "bar"},
		false,
		jwt.ValidationErrorUnverifiable,
	},
	{
		"basic nokey",
		"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
		emptyKeyFunc,
		map[string]interface{}{"foo": "bar"},
		false,
		jwt.ValidationErrorSignatureInvalid,
	},
	{
		"basic errorkey",
		"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
		errorKeyFunc,
		map[string]interface{}{"foo": "bar"},
		false,
		jwt.ValidationErrorUnverifiable,
	},
}

func init() {
	var e error
	if jwtTestDefaultKey, e = ioutil.ReadFile("test/sample_key.pub"); e != nil {
		panic(e)
	}
}

func makeSample(c map[string]interface{}) string {
	key, e := ioutil.ReadFile("test/sample_key")
	if e != nil {
		panic(e.Error())
	}

	token := jwt.New(jwt.SigningMethodRS256)
	token.Claims = c
	s, e := token.SignedString(key)

	if e != nil {
		panic(e.Error())
	}

	return s
}

func TestJWT(t *testing.T) {
	for _, data := range jwtTestData {
		if data.tokenString == "" {
			data.tokenString = makeSample(data.claims)
		}
		token, err := jwt.Parse(data.tokenString, data.keyfunc)

		if !reflect.DeepEqual(data.claims, token.Claims) {
			t.Errorf("[%v] Claims mismatch. Expecting: %v  Got: %v", data.name, data.claims, token.Claims)
		}
		if data.valid && err != nil {
			t.Errorf("[%v] Error while verifying token: %T:%v", data.name, err, err)
		}
		if !data.valid && err == nil {
			t.Errorf("[%v] Invalid token passed validation", data.name)
		}
		if data.errors != 0 {
			if err == nil {
				t.Errorf("[%v] Expecting error.  Didn't get one.", data.name)
			} else {
				// compare the bitfield part of the error
				if err.(*jwt.ValidationError).Errors != data.errors {
					t.Errorf("[%v] Errors don't match expectation", data.name)
				}

			}
		}
	}
}

func TestParseRequest(t *testing.T) {
	// Bearer token request
	for _, data := range jwtTestData {
		if data.tokenString == "" {
			data.tokenString = makeSample(data.claims)
		}

		r, _ := http.NewRequest("GET", "/", nil)
		r.Header.Set("Authorization", fmt.Sprintf("Bearer %v", data.tokenString))
		token, err := jwt.ParseFromRequest(r, data.keyfunc)

		if token == nil {
			t.Errorf("[%v] Token was not found: %v", data.name, err)
			continue
		}
		if !reflect.DeepEqual(data.claims, token.Claims) {
			t.Errorf("[%v] Claims mismatch. Expecting: %v  Got: %v", data.name, data.claims, token.Claims)
		}
		if data.valid && err != nil {
			t.Errorf("[%v] Error while verifying token: %v", data.name, err)
		}
		if !data.valid && err == nil {
			t.Errorf("[%v] Invalid token passed validation", data.name)
		}
	}
}
<|endoftext|>