<commit_before>/*
Copyright 2020 The Jetstack cert-manager contributors.

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 create

import (
	"context"
	"encoding/pem"
	"fmt"

	"github.com/spf13/cobra"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/resource"
	restclient "k8s.io/client-go/rest"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"

	cmapiv1alpha2 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
	cmclient "github.com/jetstack/cert-manager/pkg/client/clientset/versioned"
	"github.com/jetstack/cert-manager/pkg/util/pki"
	"github.com/jetstack/cert-manager/pkg/webhook"
)

var (
	long = templates.LongDesc(i18n.T(`
Create a cert-manager CertificateRequest resource for one-time Certificate issuing without auto renewal.`))

	example = templates.Examples(i18n.T(`
`))

	alias = []string{"cr"}
)

var (
	// Use the webhook's scheme as it already has the internal cert-manager types,
	// and their conversion functions registered.
	// In future we may we want to consider creating a dedicated scheme used by
	// the ctl tool.
	scheme = webhook.Scheme
)

// Options is a struct to support create certificaterequest command
type Options struct {
	CMClient   cmclient.Interface
	RESTConfig *restclient.Config

	resource.FilenameOptions
	genericclioptions.IOStreams
}

// NewOptions returns initialized Options
func NewOptions(ioStreams genericclioptions.IOStreams) *Options {
	return &Options{
		IOStreams: ioStreams,
	}
}

// NewCmdCreateCertficate returns a cobra command for create CertificateRequest
func NewCmdCreateCertficate(ioStreams genericclioptions.IOStreams, factory cmdutil.Factory) *cobra.Command {
	o := NewOptions(ioStreams)
	cmd := &cobra.Command{
		Use:     "certificaterequest",
		Aliases: alias,
		Short:   "Create a CertificateRequest resource",
		Long:    long,
		Example: example,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(factory))
			cmdutil.CheckErr(o.Run(factory, args))
		},
	}

	cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "Path to a the manifest of Certificate resource.")

	return cmd
}

// Complete takes the command arguments and factory and infers any remaining options.
func (o *Options) Complete(f cmdutil.Factory) error {
	var err error

	err = o.FilenameOptions.RequireFilenameOrKustomize()
	if err != nil {
		return err
	}

	o.RESTConfig, err = f.ToRESTConfig()
	if err != nil {
		return err
	}

	o.CMClient, err = cmclient.NewForConfig(o.RESTConfig)
	if err != nil {
		return err
	}

	return nil
}

// Run executes create certificaterequest command
func (o *Options) Run(f cmdutil.Factory, args []string) error {
	builder := new(resource.Builder)

	cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}

	// Read file as internal API version
	r := builder.
		WithScheme(scheme, schema.GroupVersion{Group: cmapiv1alpha2.SchemeGroupVersion.Group, Version: runtime.APIVersionInternal}).
		LocalParam(true).ContinueOnError().
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(enforceNamespace, &o.FilenameOptions).Flatten().Do()

	if err := r.Err(); err != nil {
		return fmt.Errorf("error when getting Result from Builder: %s", err)
	}

	singleItemImplied := false
	infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
	if err != nil {
		return fmt.Errorf("error when getting infos out of Result: %s", err)
	}

	// Ensure only one object per command
	if len(infos) == 0 {
		return fmt.Errorf("no object passed to create certificaterequest")
	}
	if len(infos) > 1 {
		return fmt.Errorf("multiple objects passed to create certificaterequest")
	}

	info := infos[0]
	// Convert to v1alpha2 because that version is needed for functions that follow
	crtObj, err := scheme.ConvertToVersion(info.Object, cmapiv1alpha2.SchemeGroupVersion)
	if err != nil {
		return fmt.Errorf("failed to convert object into version v1alpha2: %v", err)
	}

	// Cast Object into Certificate
	crt, ok := crtObj.(*cmapiv1alpha2.Certificate)
	if !ok {
		return fmt.Errorf("decoded object is not a v1alpha2 Certificate")
	}

	fmt.Println("Successfully decoded the object to a v1alpha2 Certificate")

	signer, err := pki.GeneratePrivateKeyForCertificate(crt)
	if err != nil {
		return fmt.Errorf("error when generating private key: %v", err)
	}

	keyData, err := pki.EncodePrivateKey(signer, crt.Spec.KeyEncoding)
	if err != nil {
		return fmt.Errorf("error when encoding private key: %v", err)
	}

	req, err := o.buildCertificateRequest(crt, keyData)
	if err != nil {
		return fmt.Errorf("error when building CertificateRequest: %v", err)
	}

	ns := crt.Namespace
	if ns == "" {
		ns = cmdNamespace
	}
	req, err = o.CMClient.CertmanagerV1alpha2().CertificateRequests(ns).Create(context.TODO(), req, metav1.CreateOptions{})
	if err != nil {
		return fmt.Errorf("error when creating CertificateRequest through client: %v", err)
	}

	fmt.Printf("CertificateRequest %v has been created in namespace %v\n", req.Name, req.Namespace)

	return nil
}

// Builds a CertificateRequest
func (o *Options) buildCertificateRequest(crt *cmapiv1alpha2.Certificate, pk []byte) (*cmapiv1alpha2.CertificateRequest, error) {
	csrPEM, err := generateCSR(crt, pk)
	if err != nil {
		return nil, err
	}

	annotations := make(map[string]string, len(crt.Annotations)+2)
	for k, v := range crt.Annotations {
		annotations[k] = v
	}
	annotations[cmapiv1alpha2.CRPrivateKeyAnnotationKey] = crt.Spec.SecretName
	annotations[cmapiv1alpha2.CertificateNameKey] = crt.Name

	cr := &cmapiv1alpha2.CertificateRequest{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: crt.Name + "-",
			Annotations:  annotations,
			Labels:       crt.Labels,
		},
		Spec: cmapiv1alpha2.CertificateRequestSpec{
			CSRPEM:    csrPEM,
			Duration:  crt.Spec.Duration,
			IssuerRef: crt.Spec.IssuerRef,
			IsCA:      crt.Spec.IsCA,
			Usages:    crt.Spec.Usages,
		},
	}

	return cr, nil
}

func generateCSR(crt *cmapiv1alpha2.Certificate, pk []byte) ([]byte, error) {
	csr, err := pki.GenerateCSR(crt)
	if err != nil {
		return nil, err
	}

	signer, err := pki.DecodePrivateKeyBytes(pk)
	if err != nil {
		return nil, err
	}

	csrDER, err := pki.EncodeCSR(csr, signer)
	if err != nil {
		return nil, err
	}

	csrPEM := pem.EncodeToMemory(&pem.Block{
		Type: "CERTIFICATE REQUEST", Bytes: csrDER,
	})

	return csrPEM, nil
}
<commit_msg>Use first arg as name of CR if supplied.<commit_after>/*
Copyright 2020 The Jetstack cert-manager contributors.

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 create

import (
	"context"
	"encoding/pem"
	"fmt"

	"github.com/spf13/cobra"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/resource"
	restclient "k8s.io/client-go/rest"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"

	cmapiv1alpha2 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
	cmclient "github.com/jetstack/cert-manager/pkg/client/clientset/versioned"
	"github.com/jetstack/cert-manager/pkg/util/pki"
	"github.com/jetstack/cert-manager/pkg/webhook"
)

var (
	long = templates.LongDesc(i18n.T(`
Create a cert-manager CertificateRequest resource for one-time Certificate issuing without auto renewal.`))

	example = templates.Examples(i18n.T(`
`))

	alias = []string{"cr"}
)

var (
	// Use the webhook's scheme as it already has the internal cert-manager types,
	// and their conversion functions registered.
	// In future we may we want to consider creating a dedicated scheme used by
	// the ctl tool.
	scheme = webhook.Scheme
)

// Options is a struct to support create certificaterequest command
type Options struct {
	CMClient   cmclient.Interface
	RESTConfig *restclient.Config

	resource.FilenameOptions
	genericclioptions.IOStreams
}

// NewOptions returns initialized Options
func NewOptions(ioStreams genericclioptions.IOStreams) *Options {
	return &Options{
		IOStreams: ioStreams,
	}
}

// NewCmdCreateCertficate returns a cobra command for create CertificateRequest
func NewCmdCreateCertficate(ioStreams genericclioptions.IOStreams, factory cmdutil.Factory) *cobra.Command {
	o := NewOptions(ioStreams)
	cmd := &cobra.Command{
		Use:     "certificaterequest",
		Aliases: alias,
		Short:   "Create a CertificateRequest resource",
		Long:    long,
		Example: example,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(factory))
			cmdutil.CheckErr(o.Run(factory, args))
		},
	}

	cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "Path to a the manifest of Certificate resource.")

	return cmd
}

// Complete takes the command arguments and factory and infers any remaining options.
func (o *Options) Complete(f cmdutil.Factory) error {
	var err error

	err = o.FilenameOptions.RequireFilenameOrKustomize()
	if err != nil {
		return err
	}

	o.RESTConfig, err = f.ToRESTConfig()
	if err != nil {
		return err
	}

	o.CMClient, err = cmclient.NewForConfig(o.RESTConfig)
	if err != nil {
		return err
	}

	return nil
}

// Run executes create certificaterequest command
func (o *Options) Run(f cmdutil.Factory, args []string) error {
	builder := new(resource.Builder)

	cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}

	// Read file as internal API version
	r := builder.
		WithScheme(scheme, schema.GroupVersion{Group: cmapiv1alpha2.SchemeGroupVersion.Group, Version: runtime.APIVersionInternal}).
		LocalParam(true).ContinueOnError().
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(enforceNamespace, &o.FilenameOptions).Flatten().Do()

	if err := r.Err(); err != nil {
		return fmt.Errorf("error when getting Result from Builder: %s", err)
	}

	singleItemImplied := false
	infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
	if err != nil {
		return fmt.Errorf("error when getting infos out of Result: %s", err)
	}

	// Ensure only one object per command
	if len(infos) == 0 {
		return fmt.Errorf("no object passed to create certificaterequest")
	}
	if len(infos) > 1 {
		return fmt.Errorf("multiple objects passed to create certificaterequest")
	}

	info := infos[0]
	// Convert to v1alpha2 because that version is needed for functions that follow
	crtObj, err := scheme.ConvertToVersion(info.Object, cmapiv1alpha2.SchemeGroupVersion)
	if err != nil {
		return fmt.Errorf("failed to convert object into version v1alpha2: %v", err)
	}

	// Cast Object into Certificate
	crt, ok := crtObj.(*cmapiv1alpha2.Certificate)
	if !ok {
		return fmt.Errorf("decoded object is not a v1alpha2 Certificate")
	}

	fmt.Println("Successfully decoded the object to a v1alpha2 Certificate")

	signer, err := pki.GeneratePrivateKeyForCertificate(crt)
	if err != nil {
		return fmt.Errorf("error when generating private key: %v", err)
	}

	keyData, err := pki.EncodePrivateKey(signer, crt.Spec.KeyEncoding)
	if err != nil {
		return fmt.Errorf("error when encoding private key: %v", err)
	}

	// Use name for CertificateRequest if specified as arg, else will use name of the Certificate as GenerateName
	crName := ""
	if len(args) > 0 {
		crName = args[0]
	}
	req, err := o.buildCertificateRequest(crt, keyData, crName)
	if err != nil {
		return fmt.Errorf("error when building CertificateRequest: %v", err)
	}

	ns := crt.Namespace
	if ns == "" {
		ns = cmdNamespace
	}
	req, err = o.CMClient.CertmanagerV1alpha2().CertificateRequests(ns).Create(context.TODO(), req, metav1.CreateOptions{})
	if err != nil {
		return fmt.Errorf("error when creating CertificateRequest through client: %v", err)
	}

	fmt.Printf("CertificateRequest %v has been created in namespace %v\n", req.Name, req.Namespace)

	return nil
}

// Builds a CertificateRequest
func (o *Options) buildCertificateRequest(crt *cmapiv1alpha2.Certificate, pk []byte, crName string) (*cmapiv1alpha2.CertificateRequest, error) {
	csrPEM, err := generateCSR(crt, pk)
	if err != nil {
		return nil, err
	}

	annotations := make(map[string]string, len(crt.Annotations)+2)
	for k, v := range crt.Annotations {
		annotations[k] = v
	}
	annotations[cmapiv1alpha2.CRPrivateKeyAnnotationKey] = crt.Spec.SecretName
	annotations[cmapiv1alpha2.CertificateNameKey] = crt.Name

	cr := &cmapiv1alpha2.CertificateRequest{
		ObjectMeta: metav1.ObjectMeta{
			Annotations: annotations,
			Labels:      crt.Labels,
		},
		Spec: cmapiv1alpha2.CertificateRequestSpec{
			CSRPEM:    csrPEM,
			Duration:  crt.Spec.Duration,
			IssuerRef: crt.Spec.IssuerRef,
			IsCA:      crt.Spec.IsCA,
			Usages:    crt.Spec.Usages,
		},
	}
	if crName != "" {
		cr.Name = crName
	} else {
		cr.GenerateName = crt.Name + "-"
	}

	return cr, nil
}

func generateCSR(crt *cmapiv1alpha2.Certificate, pk []byte) ([]byte, error) {
	csr, err := pki.GenerateCSR(crt)
	if err != nil {
		return nil, err
	}

	signer, err := pki.DecodePrivateKeyBytes(pk)
	if err != nil {
		return nil, err
	}

	csrDER, err := pki.EncodeCSR(csr, signer)
	if err != nil {
		return nil, err
	}

	csrPEM := pem.EncodeToMemory(&pem.Block{
		Type: "CERTIFICATE REQUEST", Bytes: csrDER,
	})

	return csrPEM, nil
}
<|endoftext|>


<commit_before>package main

import "fmt"

//Pcall2 acts as a protected call, returning wether the call went through successfully, and its return value.
func Pcall2(f func([]interface{}) []interface{}, params []interface{}) (success bool, result []interface{}) {
	defer func() {
		if r := recover(); r != nil {
			success = false
			result = make([]interface{}, 1)
			result[0] = r
			fmt.Printf("An error occoured: %v", r)
		}
	}()
	return true, f(params)
}

func main2() {
	success, response := Pcall2(func(arg1 []interface{}) []interface{} {
		var i int
		for i = 0; i < 4; i++ {
			arg1[i] = i
		}
		return arg1
	}, make([]interface{}, 3))
	fmt.Printf("Success: %t, Response: %v", success, response)
}
<commit_msg>pcall not used<commit_after><|endoftext|>


<commit_before>package main

import (
	"testing"

	"github.com/fiunchinho/dmz-controller/repository"
	"github.com/stretchr/testify/assert"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/extensions/v1beta1"
)

func TestIpsAreAdded(t *testing.T) {
	ingressRepository := repository.NewFakeIngressRepository()
	configMapRepository := repository.NewFakeConfigMapRepository()
	irrelevantNamespace := "namespace"
	ingressName := "my-ingress"
	ingress := BuildIngressObject().Named(ingressName).WithAnnotation(DMZProvidersAnnotation, "vpn").Build()

	ingressRepository.Save(ingress)

	configMap := &v1.ConfigMap{
		Data: map[string]string{
			"offices": "1.2.3.4/32",
			"vpn":     "4.4.4.4/32",
		},
	}
	configMap.Name = DMZConfigMapName
	configMapRepository.Save(configMap)

	NewIngressWhitelister(irrelevantNamespace, ingressRepository, configMapRepository).Whitelist(ingressName)

	assert := assert.New(t)
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "4.4.4.4/32", "IP is missing")
	assert.NotContains(ingress.Annotations[IngressWhitelistAnnotation], "1.2.3.4/32", "This IP should not be here")
}

func TestThatNothingChangesWhenTheDMZAnnotationIsNotPresent(t *testing.T) {
	ingressRepository := repository.NewFakeIngressRepository()
	configMapRepository := repository.NewFakeConfigMapRepository()
	irrelevantNamespace := "namespace"
	ingressName := "my-ingress"
	ingress := &v1beta1.Ingress{}
	ingress.Name = ingressName

	ingressRepository.Save(ingress)

	configMap := &v1.ConfigMap{
		Data: map[string]string{
			"offices": "1.2.3.4/32",
		},
	}
	configMap.Name = DMZConfigMapName
	configMapRepository.Save(configMap)

	NewIngressWhitelister(irrelevantNamespace, ingressRepository, configMapRepository).Whitelist(ingressName)

	assert := assert.New(t)
	assert.NotContains(ingress.Annotations, IngressWhitelistAnnotation, "It's not annotated to be whitelisted")
}

func TestThatAssignIpsWhenThereAreTwoIpSources(t *testing.T) {
	ingressRepository := repository.NewFakeIngressRepository()
	configMapRepository := repository.NewFakeConfigMapRepository()
	irrelevantNamespace := "namespace"
	ingressName := "my-ingress"
	ingress := BuildIngressObject().Named(ingressName).WithAnnotation(DMZProvidersAnnotation, "vpn,offices").Build()

	ingressRepository.Save(ingress)

	configMap := &v1.ConfigMap{
		Data: map[string]string{
			"offices": "1.2.3.4/32",
			"vpn":     "4.4.4.4/32",
		},
	}
	configMap.Name = DMZConfigMapName
	configMapRepository.Save(configMap)

	NewIngressWhitelister(irrelevantNamespace, ingressRepository, configMapRepository).Whitelist(ingressName)

	assert := assert.New(t)
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "4.4.4.4/32", "IP is missing")
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "1.2.3.4/32", "IP is missing")
}

func TestThatItKeepsExistingWhitelistedIpsNotManagedByTheController(t *testing.T) {
	ingressRepository := repository.NewFakeIngressRepository()
	configMapRepository := repository.NewFakeConfigMapRepository()
	irrelevantNamespace := "namespace"
	ingressName := "my-ingress"
	ingress := BuildIngressObject().Named(ingressName).WithAnnotation(IngressWhitelistAnnotation, "123.1.2.3/32").WithAnnotation(DMZProvidersAnnotation, "offices").Build()

	ingressRepository.Save(ingress)

	configMap := &v1.ConfigMap{
		Data: map[string]string{
			"offices": "1.2.3.4/32",
		},
	}
	configMap.Name = DMZConfigMapName
	configMapRepository.Save(configMap)

	NewIngressWhitelister(irrelevantNamespace, ingressRepository, configMapRepository).Whitelist(ingressName)

	assert := assert.New(t)
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "1.2.3.4/32", "IP is missing")
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "123.1.2.3/32", "IP that was whitelisted before is missing now")
}

func TestUpdatesIpsWhenProviderChanges(t *testing.T) {
	ingressRepository := repository.NewFakeIngressRepository()
	configMapRepository := repository.NewFakeConfigMapRepository()
	irrelevantNamespace := "namespace"
	ingressName := "my-ingress"
	ingress := BuildIngressObject().Named(ingressName).WithAnnotation(DMZProvidersAnnotation, "vpn,offices").Build()

	ingressRepository.Save(ingress)

	configMap := &v1.ConfigMap{
		Data: map[string]string{
			"offices": "1.2.3.4/32",
			"vpn":     "4.4.4.4/32",
		},
	}
	configMap.Name = DMZConfigMapName
	configMap.Namespace = irrelevantNamespace
	configMapRepository.Save(configMap)

	NewIngressWhitelister(irrelevantNamespace, ingressRepository, configMapRepository).Whitelist(ingressName)

	configMap.Data = map[string]string{
		"offices": "1.2.3.4/32",
		"vpn":     "8.8.8.8/32",
	}
	configMapRepository.Save(configMap)

	NewIngressWhitelister(irrelevantNamespace, ingressRepository, configMapRepository).Whitelist(ingressName)

	assert := assert.New(t)
	assert.NotContains(ingress.Annotations[IngressWhitelistAnnotation], "4.4.4.4/32", "Old IP was not removed")
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "8.8.8.8/32", "IP is missing")
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "1.2.3.4/32", "IP is missing")
}

func BuildIngressObject() *IngressBuilder {
	return &IngressBuilder{
		annotations: make(map[string]string),
	}
}

type IngressBuilder struct {
	ingressName string
	annotations map[string]string
}

func (builder *IngressBuilder) Named(name string) *IngressBuilder {
	builder.ingressName = name

	return builder
}

func (builder *IngressBuilder) WithAnnotation(key string, value string) *IngressBuilder {
	builder.annotations[key] = value

	return builder
}

func (builder *IngressBuilder) Build() *v1beta1.Ingress {
	ingress := &v1beta1.Ingress{}
	ingress.Name = builder.ingressName
	ingress.Annotations = builder.annotations

	return ingress
}

func NewIngressWhitelister(namespace string, ingressRepository repository.IngressRepository, configMapRepository repository.ConfigMapRepository) *IngressWhitelister {
	return &IngressWhitelister{
		namespace:           namespace,
		ingressRepository:   ingressRepository,
		configMapRepository: configMapRepository,
	}
}
<commit_msg>Added new test case<commit_after>package main

import (
	"testing"

	"github.com/fiunchinho/dmz-controller/repository"
	"github.com/stretchr/testify/assert"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/extensions/v1beta1"
)

func TestIpsAreAdded(t *testing.T) {
	ingressRepository := repository.NewFakeIngressRepository()
	configMapRepository := repository.NewFakeConfigMapRepository()
	irrelevantNamespace := "namespace"
	ingressName := "my-ingress"
	ingress := BuildIngressObject().Named(ingressName).WithAnnotation(DMZProvidersAnnotation, "vpn").Build()

	ingressRepository.Save(ingress)

	configMap := &v1.ConfigMap{
		Data: map[string]string{
			"offices": "1.2.3.4/32",
			"vpn":     "4.4.4.4/32",
		},
	}
	configMap.Name = DMZConfigMapName
	configMapRepository.Save(configMap)

	NewIngressWhitelister(irrelevantNamespace, ingressRepository, configMapRepository).Whitelist(ingressName)

	assert := assert.New(t)
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "4.4.4.4/32", "IP is missing")
	assert.NotContains(ingress.Annotations[IngressWhitelistAnnotation], "1.2.3.4/32", "This IP should not be here")
}

func TestThatNothingChangesWhenTheDMZAnnotationIsNotPresent(t *testing.T) {
	ingressRepository := repository.NewFakeIngressRepository()
	configMapRepository := repository.NewFakeConfigMapRepository()
	irrelevantNamespace := "namespace"
	ingressName := "my-ingress"
	ingress := &v1beta1.Ingress{}
	ingress.Name = ingressName

	ingressRepository.Save(ingress)

	configMap := &v1.ConfigMap{
		Data: map[string]string{
			"offices": "1.2.3.4/32",
		},
	}
	configMap.Name = DMZConfigMapName
	configMapRepository.Save(configMap)

	NewIngressWhitelister(irrelevantNamespace, ingressRepository, configMapRepository).Whitelist(ingressName)

	assert := assert.New(t)
	assert.NotContains(ingress.Annotations, IngressWhitelistAnnotation, "It's not annotated to be whitelisted")
}

func TestThatAssignIpsWhenThereAreTwoIpSources(t *testing.T) {
	ingressRepository := repository.NewFakeIngressRepository()
	configMapRepository := repository.NewFakeConfigMapRepository()
	irrelevantNamespace := "namespace"
	ingressName := "my-ingress"
	ingress := BuildIngressObject().Named(ingressName).WithAnnotation(DMZProvidersAnnotation, "vpn,offices").Build()

	ingressRepository.Save(ingress)

	configMap := &v1.ConfigMap{
		Data: map[string]string{
			"offices": "1.2.3.4/32",
			"vpn":     "4.4.4.4/32",
		},
	}
	configMap.Name = DMZConfigMapName
	configMapRepository.Save(configMap)

	NewIngressWhitelister(irrelevantNamespace, ingressRepository, configMapRepository).Whitelist(ingressName)

	assert := assert.New(t)
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "4.4.4.4/32", "IP is missing")
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "1.2.3.4/32", "IP is missing")
}

func TestThatItKeepsExistingWhitelistedIpsNotManagedByTheController(t *testing.T) {
	ingressRepository := repository.NewFakeIngressRepository()
	configMapRepository := repository.NewFakeConfigMapRepository()
	irrelevantNamespace := "namespace"
	ingressName := "my-ingress"
	ingress := BuildIngressObject().Named(ingressName).WithAnnotation(IngressWhitelistAnnotation, "123.1.2.3/32").WithAnnotation(DMZProvidersAnnotation, "offices").Build()

	ingressRepository.Save(ingress)

	configMap := &v1.ConfigMap{
		Data: map[string]string{
			"offices": "1.2.3.4/32",
		},
	}
	configMap.Name = DMZConfigMapName
	configMapRepository.Save(configMap)

	NewIngressWhitelister(irrelevantNamespace, ingressRepository, configMapRepository).Whitelist(ingressName)

	assert := assert.New(t)
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "1.2.3.4/32", "IP is missing")
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "123.1.2.3/32", "IP that was whitelisted before is missing now")
}

func TestUpdatesIpsWhenProviderChanges(t *testing.T) {
	ingressRepository := repository.NewFakeIngressRepository()
	configMapRepository := repository.NewFakeConfigMapRepository()
	irrelevantNamespace := "namespace"
	ingressName := "my-ingress"
	ingress := BuildIngressObject().Named(ingressName).WithAnnotation(DMZProvidersAnnotation, "vpn,offices").Build()

	ingressRepository.Save(ingress)

	configMap := &v1.ConfigMap{
		Data: map[string]string{
			"offices": "1.2.3.4/32",
			"vpn":     "4.4.4.4/32",
		},
	}
	configMap.Name = DMZConfigMapName
	configMap.Namespace = irrelevantNamespace
	configMapRepository.Save(configMap)

	NewIngressWhitelister(irrelevantNamespace, ingressRepository, configMapRepository).Whitelist(ingressName)

	configMap.Data = map[string]string{
		"offices": "1.2.3.4/32",
		"vpn":     "8.8.8.8/32",
	}
	configMapRepository.Save(configMap)

	NewIngressWhitelister(irrelevantNamespace, ingressRepository, configMapRepository).Whitelist(ingressName)

	assert := assert.New(t)
	assert.NotContains(ingress.Annotations[IngressWhitelistAnnotation], "4.4.4.4/32", "Old IP was not removed")
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "8.8.8.8/32", "IP is missing")
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "1.2.3.4/32", "IP is missing")
}

func TestThatItSkipsNonExistingProviders(t *testing.T) {
	ingressRepository := repository.NewFakeIngressRepository()
	configMapRepository := repository.NewFakeConfigMapRepository()
	irrelevantNamespace := "namespace"
	ingressName := "my-ingress"
	ingress := BuildIngressObject().Named(ingressName).WithAnnotation(DMZProvidersAnnotation, "vpn,non-existing,offices").Build()

	ingressRepository.Save(ingress)

	configMap := &v1.ConfigMap{
		Data: map[string]string{
			"offices": "1.2.3.4/32",
			"vpn":     "4.4.4.4/32",
		},
	}
	configMap.Name = DMZConfigMapName
	configMapRepository.Save(configMap)

	NewIngressWhitelister(irrelevantNamespace, ingressRepository, configMapRepository).Whitelist(ingressName)

	assert := assert.New(t)
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "4.4.4.4/32", "IP is missing")
	assert.Contains(ingress.Annotations[IngressWhitelistAnnotation], "1.2.3.4/32", "IP is missing")
}

func BuildIngressObject() *IngressBuilder {
	return &IngressBuilder{
		annotations: make(map[string]string),
	}
}

type IngressBuilder struct {
	ingressName string
	annotations map[string]string
}

func (builder *IngressBuilder) Named(name string) *IngressBuilder {
	builder.ingressName = name

	return builder
}

func (builder *IngressBuilder) WithAnnotation(key string, value string) *IngressBuilder {
	builder.annotations[key] = value

	return builder
}

func (builder *IngressBuilder) Build() *v1beta1.Ingress {
	ingress := &v1beta1.Ingress{}
	ingress.Name = builder.ingressName
	ingress.Annotations = builder.annotations

	return ingress
}

func NewIngressWhitelister(namespace string, ingressRepository repository.IngressRepository, configMapRepository repository.ConfigMapRepository) *IngressWhitelister {
	return &IngressWhitelister{
		namespace:           namespace,
		ingressRepository:   ingressRepository,
		configMapRepository: configMapRepository,
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 Google Inc.  All rights reserved.
// Copyright 2016 the gousb Authors.  All rights reserved.
//
// 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 usb

// #include <libusb.h>
import "C"

import (
	"fmt"
	"reflect"
	"sync"
	"time"
	"unsafe"
)

var DefaultReadTimeout = 1 * time.Second
var DefaultWriteTimeout = 1 * time.Second
var DefaultControlTimeout = 250 * time.Millisecond //5 * time.Second

type Device struct {
	handle *C.libusb_device_handle

	// Embed the device information for easy access
	*Descriptor

	// Timeouts
	ReadTimeout    time.Duration
	WriteTimeout   time.Duration
	ControlTimeout time.Duration

	// Claimed interfaces
	lock    *sync.Mutex
	claimed map[uint8]int
}

func newDevice(handle *C.libusb_device_handle, desc *Descriptor) *Device {
	ifaces := 0
	d := &Device{
		handle:         handle,
		Descriptor:     desc,
		ReadTimeout:    DefaultReadTimeout,
		WriteTimeout:   DefaultWriteTimeout,
		ControlTimeout: DefaultControlTimeout,
		lock:           new(sync.Mutex),
		claimed:        make(map[uint8]int, ifaces),
	}

	return d
}

func (d *Device) Reset() error {
	if errno := C.libusb_reset_device(d.handle); errno != 0 {
		return usbError(errno)
	}
	return nil
}

func (d *Device) Control(rType, request uint8, val, idx uint16, data []byte) (int, error) {
	//log.Printf("control xfer: %d:%d/%d:%d %x", idx, rType, request, val, string(data))
	dataSlice := (*reflect.SliceHeader)(unsafe.Pointer(&data))
	n := C.libusb_control_transfer(
		d.handle,
		C.uint8_t(rType),
		C.uint8_t(request),
		C.uint16_t(val),
		C.uint16_t(idx),
		(*C.uchar)(unsafe.Pointer(dataSlice.Data)),
		C.uint16_t(len(data)),
		C.uint(d.ControlTimeout/time.Millisecond))
	if n < 0 {
		return int(n), usbError(n)
	}
	return int(n), nil
}

// ActiveConfig returns the config id (not the index) of the active configuration.
// This corresponds to the ConfigInfo.Config field.
func (d *Device) ActiveConfig() (uint8, error) {
	var cfg C.int
	if errno := C.libusb_get_configuration(d.handle, &cfg); errno < 0 {
		return 0, usbError(errno)
	}
	return uint8(cfg), nil
}

// SetConfig attempts to change the active configuration.
// The cfg provided is the config id (not the index) of the configuration to set,
// which corresponds to the ConfigInfo.Config field.
func (d *Device) SetConfig(cfg uint8) error {
	if errno := C.libusb_set_configuration(d.handle, C.int(cfg)); errno < 0 {
		return usbError(errno)
	}
	return nil
}

// Close the device.
func (d *Device) Close() error {
	if d.handle == nil {
		return fmt.Errorf("usb: double close on device")
	}
	d.lock.Lock()
	defer d.lock.Unlock()
	for iface := range d.claimed {
		C.libusb_release_interface(d.handle, C.int(iface))
	}
	C.libusb_close(d.handle)
	d.handle = nil
	return nil
}

func (d *Device) OpenEndpoint(conf, iface, setup, epoint uint8) (Endpoint, error) {
	end := &endpoint{
		Device: d,
	}

	var setAlternate bool
	for _, c := range d.Configs {
		if c.Config != conf {
			continue
		}
		debug.Printf("found conf: %#v\n", c)
		for _, i := range c.Interfaces {
			if i.Number != iface {
				continue
			}
			debug.Printf("found iface: %#v\n", i)
			for i, s := range i.Setups {
				if s.Alternate != setup {
					continue
				}
				setAlternate = i != 0

				debug.Printf("found setup: %#v [default: %v]\n", s, !setAlternate)
				for _, e := range s.Endpoints {
					debug.Printf("ep %02x search: %#v\n", epoint, s)
					if e.Address != epoint {
						continue
					}
					end.InterfaceSetup = s
					end.EndpointInfo = e
					goto found
				}
				return nil, fmt.Errorf("usb: unknown endpoint %02x", epoint)
			}
			return nil, fmt.Errorf("usb: unknown setup %02x", setup)
		}
		return nil, fmt.Errorf("usb: unknown interface %02x", iface)
	}
	return nil, fmt.Errorf("usb: unknown configuration %02x", conf)

found:

	// Set the configuration
	var activeConf C.int
	if errno := C.libusb_get_configuration(d.handle, &activeConf); errno < 0 {
		return nil, fmt.Errorf("usb: getcfg: %s", usbError(errno))
	}
	if int(activeConf) != int(conf) {
		if errno := C.libusb_set_configuration(d.handle, C.int(conf)); errno < 0 {
			return nil, fmt.Errorf("usb: setcfg: %s", usbError(errno))
		}
	}

	// Claim the interface
	if errno := C.libusb_claim_interface(d.handle, C.int(iface)); errno < 0 {
		return nil, fmt.Errorf("usb: claim: %s", usbError(errno))
	}

	// Increment the claim count
	d.lock.Lock()
	d.claimed[iface]++
	d.lock.Unlock() // unlock immediately because the next calls may block

	// Choose the alternate
	if setAlternate {
		if errno := C.libusb_set_interface_alt_setting(d.handle, C.int(iface), C.int(setup)); errno < 0 {
			debug.Printf("altsetting error: %s", usbError(errno))
			return nil, fmt.Errorf("usb: setalt: %s", usbError(errno))
		}
	}

	return end, nil
}

func (d *Device) GetStringDescriptor(desc_index int) (string, error) {

	// allocate 200-byte array limited the length of string descriptor
	goBuffer := make([]byte, 200)

	// get string descriptor from libusb. if errno < 0 then there are any errors.
	// if errno >= 0; it is a length of result string descriptor
	errno := C.libusb_get_string_descriptor_ascii(
		d.handle,
		C.uint8_t(desc_index),
		(*C.uchar)(unsafe.Pointer(&goBuffer[0])),
		200)

	// if any errors occur
	if errno < 0 {
		return "", fmt.Errorf("usb: getstr: %s", usbError(errno))
	}
	// convert slice of byte to string with limited length from errno
	stringDescriptor := string(goBuffer[:errno])

	return stringDescriptor, nil
}
<commit_msg>detach before claim<commit_after>// Copyright 2013 Google Inc.  All rights reserved.
// Copyright 2016 the gousb Authors.  All rights reserved.
//
// 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 usb

// #include <libusb.h>
import "C"

import (
	"fmt"
	"reflect"
	"sync"
	"time"
	"unsafe"
)

var DefaultReadTimeout = 1 * time.Second
var DefaultWriteTimeout = 1 * time.Second
var DefaultControlTimeout = 250 * time.Millisecond //5 * time.Second

type Device struct {
	handle *C.libusb_device_handle

	// Embed the device information for easy access
	*Descriptor

	// Timeouts
	ReadTimeout    time.Duration
	WriteTimeout   time.Duration
	ControlTimeout time.Duration

	// Claimed interfaces
	lock    *sync.Mutex
	claimed map[uint8]int
}

func newDevice(handle *C.libusb_device_handle, desc *Descriptor) *Device {
	ifaces := 0
	d := &Device{
		handle:         handle,
		Descriptor:     desc,
		ReadTimeout:    DefaultReadTimeout,
		WriteTimeout:   DefaultWriteTimeout,
		ControlTimeout: DefaultControlTimeout,
		lock:           new(sync.Mutex),
		claimed:        make(map[uint8]int, ifaces),
	}

	return d
}

func (d *Device) Reset() error {
	if errno := C.libusb_reset_device(d.handle); errno != 0 {
		return usbError(errno)
	}
	return nil
}

func (d *Device) Control(rType, request uint8, val, idx uint16, data []byte) (int, error) {
	//log.Printf("control xfer: %d:%d/%d:%d %x", idx, rType, request, val, string(data))
	dataSlice := (*reflect.SliceHeader)(unsafe.Pointer(&data))
	n := C.libusb_control_transfer(
		d.handle,
		C.uint8_t(rType),
		C.uint8_t(request),
		C.uint16_t(val),
		C.uint16_t(idx),
		(*C.uchar)(unsafe.Pointer(dataSlice.Data)),
		C.uint16_t(len(data)),
		C.uint(d.ControlTimeout/time.Millisecond))
	if n < 0 {
		return int(n), usbError(n)
	}
	return int(n), nil
}

// ActiveConfig returns the config id (not the index) of the active configuration.
// This corresponds to the ConfigInfo.Config field.
func (d *Device) ActiveConfig() (uint8, error) {
	var cfg C.int
	if errno := C.libusb_get_configuration(d.handle, &cfg); errno < 0 {
		return 0, usbError(errno)
	}
	return uint8(cfg), nil
}

// SetConfig attempts to change the active configuration.
// The cfg provided is the config id (not the index) of the configuration to set,
// which corresponds to the ConfigInfo.Config field.
func (d *Device) SetConfig(cfg uint8) error {
	if errno := C.libusb_set_configuration(d.handle, C.int(cfg)); errno < 0 {
		return usbError(errno)
	}
	return nil
}

// Close the device.
func (d *Device) Close() error {
	if d.handle == nil {
		return fmt.Errorf("usb: double close on device")
	}
	d.lock.Lock()
	defer d.lock.Unlock()
	for iface := range d.claimed {
		C.libusb_release_interface(d.handle, C.int(iface))
	}
	C.libusb_close(d.handle)
	d.handle = nil
	return nil
}

func (d *Device) OpenEndpoint(conf, iface, setup, epoint uint8) (Endpoint, error) {
	end := &endpoint{
		Device: d,
	}

	var setAlternate bool
	for _, c := range d.Configs {
		if c.Config != conf {
			continue
		}
		debug.Printf("found conf: %#v\n", c)
		for _, i := range c.Interfaces {
			if i.Number != iface {
				continue
			}
			debug.Printf("found iface: %#v\n", i)
			for i, s := range i.Setups {
				if s.Alternate != setup {
					continue
				}
				setAlternate = i != 0

				debug.Printf("found setup: %#v [default: %v]\n", s, !setAlternate)
				for _, e := range s.Endpoints {
					debug.Printf("ep %02x search: %#v\n", epoint, s)
					if e.Address != epoint {
						continue
					}
					end.InterfaceSetup = s
					end.EndpointInfo = e
					goto found
				}
				return nil, fmt.Errorf("usb: unknown endpoint %02x", epoint)
			}
			return nil, fmt.Errorf("usb: unknown setup %02x", setup)
		}
		return nil, fmt.Errorf("usb: unknown interface %02x", iface)
	}
	return nil, fmt.Errorf("usb: unknown configuration %02x", conf)

found:

	// Set the configuration
	var activeConf C.int
	if errno := C.libusb_get_configuration(d.handle, &activeConf); errno < 0 {
		return nil, fmt.Errorf("usb: getcfg: %s", usbError(errno))
	}
	if int(activeConf) != int(conf) {
		if errno := C.libusb_set_configuration(d.handle, C.int(conf)); errno < 0 {
			return nil, fmt.Errorf("usb: setcfg: %s", usbError(errno))
		}
	}

	// Detach the interface
	if errno := C.libusb_detach_kernel_driver(d.handle, C.int(iface)); errno < 0 {
		fmt.Errorf("usb: detach: %s", usbError(errno))
	}

	// Claim the interface
	if errno := C.libusb_claim_interface(d.handle, C.int(iface)); errno < 0 {
		return nil, fmt.Errorf("usb: claim: %s", usbError(errno))
	}

	// Increment the claim count
	d.lock.Lock()
	d.claimed[iface]++
	d.lock.Unlock() // unlock immediately because the next calls may block

	// Choose the alternate
	if setAlternate {
		if errno := C.libusb_set_interface_alt_setting(d.handle, C.int(iface), C.int(setup)); errno < 0 {
			debug.Printf("altsetting error: %s", usbError(errno))
			return nil, fmt.Errorf("usb: setalt: %s", usbError(errno))
		}
	}

	return end, nil
}

func (d *Device) GetStringDescriptor(desc_index int) (string, error) {

	// allocate 200-byte array limited the length of string descriptor
	goBuffer := make([]byte, 200)

	// get string descriptor from libusb. if errno < 0 then there are any errors.
	// if errno >= 0; it is a length of result string descriptor
	errno := C.libusb_get_string_descriptor_ascii(
		d.handle,
		C.uint8_t(desc_index),
		(*C.uchar)(unsafe.Pointer(&goBuffer[0])),
		200)

	// if any errors occur
	if errno < 0 {
		return "", fmt.Errorf("usb: getstr: %s", usbError(errno))
	}
	// convert slice of byte to string with limited length from errno
	stringDescriptor := string(goBuffer[:errno])

	return stringDescriptor, nil
}
<|endoftext|>


<commit_before>package cgo

import (
	"fmt"
	"go/ast"
	"go/token"
	"go/types"
	"strings"
)

func CObjectStruct() ast.Decl {
	return &ast.GenDecl{
		Tok: token.TYPE,
		Specs: []ast.Spec{
			&ast.TypeSpec{
				Name: NewIdent("cobject"),
				Type: &ast.StructType{
					Fields: &ast.FieldList{
						List: []*ast.Field{
							{
								Names: []*ast.Ident{NewIdent("ptr")},
								Type: &ast.SelectorExpr{
									X:   NewIdent("unsafe"),
									Sel: NewIdent("Pointer"),
								},
							},
							{
								Names: []*ast.Ident{NewIdent("cnt")},
								Type:  NewIdent("int32"),
							},
						},
					},
				},
			},
		},
	}
}

func RefsStruct() ast.Decl {
	return &ast.GenDecl{
		Tok: token.TYPE,
		Specs: []ast.Spec{
			&ast.TypeSpec{
				Name: NewIdent("refs"),
				Type: &ast.StructType{
					Fields: &ast.FieldList{
						List: []*ast.Field{
							{
								Type: &ast.SelectorExpr{
									X:   NewIdent("sync"),
									Sel: NewIdent("Mutex"),
								},
							},
							{
								Names: []*ast.Ident{NewIdent("next")},
								Type:  NewIdent("int32"),
							},
							{
								Names: []*ast.Ident{NewIdent("refs")},
								Type: &ast.MapType{
									Key: &ast.SelectorExpr{
										X:   NewIdent("unsafe"),
										Sel: NewIdent("Pointer"),
									},
									Value: NewIdent("int32"),
								},
							},
							{
								Names: []*ast.Ident{NewIdent("ptrs")},
								Type: &ast.MapType{
									Key:   NewIdent("int32"),
									Value: NewIdent("cobject"),
								},
							},
						},
					},
				},
			},
		},
	}
}

// IncrementRef takes a target expression to increment it's cgo pointer ref and returns the expression
func IncrementRef(target ast.Expr) *ast.ExprStmt {
	return &ast.ExprStmt{
		X: &ast.CallExpr{
			Fun: NewIdent("cgo_incref"),
			Args: []ast.Expr{
				&ast.CallExpr{
					Fun: UnsafePtrSelector(),
					Args: []ast.Expr{
						target,
					},
				},
			},
		},
	}
}

// IncrementRef takes a target expression to decrement it's cgo pointer ref and returns the expression
func DecrementRef(target ast.Expr) *ast.ExprStmt {
	return &ast.ExprStmt{
		X: &ast.CallExpr{
			Fun: NewIdent("cgo_decref"),
			Args: []ast.Expr{
				&ast.CallExpr{
					Fun: UnsafePtrSelector(),
					Args: []ast.Expr{
						target,
					},
				},
			},
		},
	}
}

// NewIdent takes a name as string and returns an *ast.Ident in that name
func NewIdent(name string) *ast.Ident {
	return &ast.Ident{
		Name: name,
	}
}

// UnsafePtrSelector is a commonly used selector expression (unsafe.Pointer)
func UnsafePtrSelector() *ast.SelectorExpr {
	return &ast.SelectorExpr{
		X:   NewIdent("unsafe"),
		Sel: NewIdent("Pointer"),
	}
}

// DeclareVar declares a local variable
func DeclareVar(name *ast.Ident, t ast.Expr) *ast.DeclStmt {
	return &ast.DeclStmt{
		Decl: &ast.GenDecl{
			Tok: token.VAR,
			Specs: []ast.Spec{
				&ast.ValueSpec{
					Names: []*ast.Ident{name},
					Type:  t,
				},
			},
		},
	}
}

// CastReturn returns a return expression with a cast version of the target expression
func CastReturn(castType, target ast.Expr) *ast.ReturnStmt {
	return Return(CastUnsafePtr(castType, target))
}

// CastUnsafePtr take a cast type and target expression and returns a cast expression
func CastUnsafePtr(castType, target ast.Expr) *ast.CallExpr {
	return &ast.CallExpr{
		Fun: &ast.ParenExpr{
			X: castType,
		},
		Args: []ast.Expr{
			&ast.CallExpr{
				Fun: UnsafePtrSelector(),
				Args: []ast.Expr{
					target,
				},
			},
		},
	}
}

// DeRef takes an expression and prefaces the expression with a *
func DeRef(expr ast.Expr) *ast.StarExpr {
	return &ast.StarExpr{X: expr}
}

// Return takes an expression and returns a return statement containing the expression
func Return(expression ast.Expr) *ast.ReturnStmt {
	return &ast.ReturnStmt{
		Results: []ast.Expr{
			expression,
		},
	}
}

// FormatSprintf takes a format and a target expression and returns a fmt.Sprintf expression
func FormatSprintf(format string, target ast.Expr) *ast.CallExpr {
	fmtSprintf := &ast.CallExpr{
		Fun: &ast.SelectorExpr{
			X:   NewIdent("fmt"),
			Sel: NewIdent("Sprintf"),
		},
		Args: []ast.Expr{
			&ast.BasicLit{
				Kind:  token.STRING,
				Value: "\"" + format + "\"",
			},
			target,
		},
	}

	return fmtSprintf
}

// ExportComments takes a name to export as string and returns a comment group
func ExportComments(exportName string) []*ast.Comment {
	return []*ast.Comment{
		{
			Text:  "//export " + exportName,
			Slash: token.Pos(1),
		},
	}
}

// InstanceMethodParams returns a constructed field list for an instance method
func InstanceMethodParams(selfTypeIdent *ast.Ident, fields ...*ast.Field) *ast.FieldList {
	tmpFields := []*ast.Field{
		{
			Names: []*ast.Ident{NewIdent("self")},
			Type:  selfTypeIdent,
		},
	}
	tmpFields = append(tmpFields, fields...)
	params := &ast.FieldList{
		List: tmpFields,
	}
	return params
}

func FuncAst(f *Func) *ast.FuncDecl {
	fun := f.Func
	functionName := f.CGoName()
	sig := fun.Type().(*types.Signature)
	functionCall := &ast.CallExpr{
		Fun:  NewIdent(f.AliasedGoName()),
		Args: ParamIdents(sig.Params()),
	}

	funcDecl := &ast.FuncDecl{
		Doc: &ast.CommentGroup{
			List: ExportComments(functionName),
		},
		Name: NewIdent(functionName),
		Body: &ast.BlockStmt{List: []ast.Stmt{}},
	}

	if sig.Results().Len() > 0 {
		// signature will return
		funcDecl.Body.List = append(funcDecl.Body.List, Return(functionCall))

		funcDecl.Type = &ast.FuncType{
			Params:  FieldsAst(sig.Params()),
			Results: FieldsAst(sig.Results()),
		}
	} else {
		funcDecl.Body.List = append(funcDecl.Body.List, &ast.ExprStmt{
			X: functionCall,
		})

		funcDecl.Type = &ast.FuncType{
			Params: FieldsAst(sig.Params()),
		}
	}

	return funcDecl
}

func ParamIdents(funcParams *types.Tuple) []ast.Expr {
	if funcParams == nil || funcParams.Len() <= 0 {
		return []ast.Expr{}
	}

	args := make([]ast.Expr, funcParams.Len())
	for i := 0; i < funcParams.Len(); i++ {
		args[i] = NewIdent(funcParams.At(i).Name())
	}
	return args
}

func FieldsAst(funcParams *types.Tuple) *ast.FieldList {
	if funcParams == nil || funcParams.Len() <= 0 {
		return &ast.FieldList{}
	}

	fields := make([]*ast.Field, funcParams.Len())
	for i := 0; i < funcParams.Len(); i++ {
		p := funcParams.At(i)
		switch t := p.Type().(type) {
		case *types.Pointer:
			fields[i] = VarToField(p, t.Elem())
		default:
			fields[i] = VarToField(p, t)
		}
	}
	return &ast.FieldList{List: fields}
}

func VarToField(p *types.Var, t types.Type) *ast.Field {
	switch typ := t.(type) {
	case *types.Named:
		return NamedToField(p, typ)
	default:
		name := p.Name()
		typeName := p.Type().String()
		return &ast.Field{
			Type:  NewIdent(typeName),
			Names: []*ast.Ident{NewIdent(name)},
		}

	}
}

func NamedToField(p *types.Var, named *types.Named) *ast.Field {
	pkg := p.Pkg()
	if pkg == nil {
		pkg = named.Obj().Pkg()
	}

	if pkg != nil {
		path := pkg.Path()
		pkgAlias := PkgPathAliasFromString(path)
		typeName := pkgAlias + "." + named.Obj().Name()
		nameIdent := NewIdent(p.Name())
		return &ast.Field{
			Type:  NewIdent(typeName),
			Names: []*ast.Ident{nameIdent},
		}
	} else {
		fmt.Println(p, named)
		typeIdnet := NewIdent(p.Type().String())
		nameIdent := NewIdent(p.Name())
		return &ast.Field{
			Type:  typeIdnet,
			Names: []*ast.Ident{nameIdent},
		}
	}
}

func PkgPathAliasFromString(path string) string {
	splits := strings.FieldsFunc(path, splitPkgPath)
	return strings.Join(splits, "_")
}

func splitPkgPath(r rune) bool {
	return r == '.' || r == '/'
}
<commit_msg>add documentation and normalize naming<commit_after>package cgo

import (
	"fmt"
	"go/ast"
	"go/token"
	"go/types"
	"strings"
)

// CObjectStruct produces an AST struct which will represent a C exposed Object
func CObjectStruct() ast.Decl {
	return &ast.GenDecl{
		Tok: token.TYPE,
		Specs: []ast.Spec{
			&ast.TypeSpec{
				Name: NewIdent("cobject"),
				Type: &ast.StructType{
					Fields: &ast.FieldList{
						List: []*ast.Field{
							{
								Names: []*ast.Ident{NewIdent("ptr")},
								Type: &ast.SelectorExpr{
									X:   NewIdent("unsafe"),
									Sel: NewIdent("Pointer"),
								},
							},
							{
								Names: []*ast.Ident{NewIdent("cnt")},
								Type:  NewIdent("int32"),
							},
						},
					},
				},
			},
		},
	}
}

// RefsStruct produces an AST struct which will keep track of references to pointers used by the host CFFI
func RefsStruct() ast.Decl {
	return &ast.GenDecl{
		Tok: token.TYPE,
		Specs: []ast.Spec{
			&ast.TypeSpec{
				Name: NewIdent("refs"),
				Type: &ast.StructType{
					Fields: &ast.FieldList{
						List: []*ast.Field{
							{
								Type: &ast.SelectorExpr{
									X:   NewIdent("sync"),
									Sel: NewIdent("Mutex"),
								},
							},
							{
								Names: []*ast.Ident{NewIdent("next")},
								Type:  NewIdent("int32"),
							},
							{
								Names: []*ast.Ident{NewIdent("refs")},
								Type: &ast.MapType{
									Key: &ast.SelectorExpr{
										X:   NewIdent("unsafe"),
										Sel: NewIdent("Pointer"),
									},
									Value: NewIdent("int32"),
								},
							},
							{
								Names: []*ast.Ident{NewIdent("ptrs")},
								Type: &ast.MapType{
									Key:   NewIdent("int32"),
									Value: NewIdent("cobject"),
								},
							},
						},
					},
				},
			},
		},
	}
}

// IncrementRef takes a target expression to increment it's cgo pointer ref and returns the expression
func IncrementRef(target ast.Expr) *ast.ExprStmt {
	return &ast.ExprStmt{
		X: &ast.CallExpr{
			Fun: NewIdent("cgo_incref"),
			Args: []ast.Expr{
				&ast.CallExpr{
					Fun: UnsafePtrSelector(),
					Args: []ast.Expr{
						target,
					},
				},
			},
		},
	}
}

// IncrementRef takes a target expression to decrement it's cgo pointer ref and returns the expression
func DecrementRef(target ast.Expr) *ast.ExprStmt {
	return &ast.ExprStmt{
		X: &ast.CallExpr{
			Fun: NewIdent("cgo_decref"),
			Args: []ast.Expr{
				&ast.CallExpr{
					Fun: UnsafePtrSelector(),
					Args: []ast.Expr{
						target,
					},
				},
			},
		},
	}
}

// NewIdent takes a name as string and returns an *ast.Ident in that name
func NewIdent(name string) *ast.Ident {
	return &ast.Ident{
		Name: name,
	}
}

// UnsafePtrSelector is a commonly used selector expression (unsafe.Pointer)
func UnsafePtrSelector() *ast.SelectorExpr {
	return &ast.SelectorExpr{
		X:   NewIdent("unsafe"),
		Sel: NewIdent("Pointer"),
	}
}

// DeclareVar declares a local variable
func DeclareVar(name *ast.Ident, t ast.Expr) *ast.DeclStmt {
	return &ast.DeclStmt{
		Decl: &ast.GenDecl{
			Tok: token.VAR,
			Specs: []ast.Spec{
				&ast.ValueSpec{
					Names: []*ast.Ident{name},
					Type:  t,
				},
			},
		},
	}
}

// CastReturn returns a return expression with a cast version of the target expression
func CastReturn(castType, target ast.Expr) *ast.ReturnStmt {
	return Return(CastUnsafePtr(castType, target))
}

// CastUnsafePtr take a cast type and target expression and returns a cast expression
func CastUnsafePtr(castType, target ast.Expr) *ast.CallExpr {
	return &ast.CallExpr{
		Fun: &ast.ParenExpr{
			X: castType,
		},
		Args: []ast.Expr{
			&ast.CallExpr{
				Fun: UnsafePtrSelector(),
				Args: []ast.Expr{
					target,
				},
			},
		},
	}
}

// DeRef takes an expression and prefaces the expression with a *
func DeRef(expr ast.Expr) *ast.StarExpr {
	return &ast.StarExpr{X: expr}
}

// Return takes an expression and returns a return statement containing the expression
func Return(expression ast.Expr) *ast.ReturnStmt {
	return &ast.ReturnStmt{
		Results: []ast.Expr{
			expression,
		},
	}
}

// FormatSprintf takes a format and a target expression and returns a fmt.Sprintf expression
func FormatSprintf(format string, target ast.Expr) *ast.CallExpr {
	fmtSprintf := &ast.CallExpr{
		Fun: &ast.SelectorExpr{
			X:   NewIdent("fmt"),
			Sel: NewIdent("Sprintf"),
		},
		Args: []ast.Expr{
			&ast.BasicLit{
				Kind:  token.STRING,
				Value: "\"" + format + "\"",
			},
			target,
		},
	}

	return fmtSprintf
}

// ExportComments takes a name to export as string and returns a comment group
func ExportComments(exportName string) []*ast.Comment {
	return []*ast.Comment{
		{
			Text:  "//export " + exportName,
			Slash: token.Pos(1),
		},
	}
}

// InstanceMethodParams returns a constructed field list for an instance method
func InstanceMethodParams(selfTypeIdent *ast.Ident, fields ...*ast.Field) *ast.FieldList {
	tmpFields := []*ast.Field{
		{
			Names: []*ast.Ident{NewIdent("self")},
			Type:  selfTypeIdent,
		},
	}
	tmpFields = append(tmpFields, fields...)
	params := &ast.FieldList{
		List: tmpFields,
	}
	return params
}

// FuncAst returns an FuncDecl which wraps the func
func FuncAst(f *Func) *ast.FuncDecl {
	fun := f.Func
	functionName := f.CGoName()
	sig := fun.Type().(*types.Signature)
	functionCall := &ast.CallExpr{
		Fun:  NewIdent(f.AliasedGoName()),
		Args: ParamIdents(sig.Params()),
	}

	funcDecl := &ast.FuncDecl{
		Doc: &ast.CommentGroup{
			List: ExportComments(functionName),
		},
		Name: NewIdent(functionName),
		Body: &ast.BlockStmt{List: []ast.Stmt{}},
	}

	if sig.Results().Len() > 0 {
		// signature will return
		funcDecl.Body.List = append(funcDecl.Body.List, Return(functionCall))

		funcDecl.Type = &ast.FuncType{
			Params:  Fields(sig.Params()),
			Results: Fields(sig.Results()),
		}
	} else {
		funcDecl.Body.List = append(funcDecl.Body.List, &ast.ExprStmt{
			X: functionCall,
		})

		funcDecl.Type = &ast.FuncType{
			Params: Fields(sig.Params()),
		}
	}

	return funcDecl
}

// ParamIdents transforms parameter tuples into a slice of AST expressions
func ParamIdents(funcParams *types.Tuple) []ast.Expr {
	if funcParams == nil || funcParams.Len() <= 0 {
		return []ast.Expr{}
	}

	args := make([]ast.Expr, funcParams.Len())
	for i := 0; i < funcParams.Len(); i++ {
		args[i] = NewIdent(funcParams.At(i).Name())
	}
	return args
}

// Fields transforms parameters into a list of AST fields
func Fields(funcParams *types.Tuple) *ast.FieldList {
	if funcParams == nil || funcParams.Len() <= 0 {
		return &ast.FieldList{}
	}

	fields := make([]*ast.Field, funcParams.Len())
	for i := 0; i < funcParams.Len(); i++ {
		p := funcParams.At(i)
		switch t := p.Type().(type) {
		case *types.Pointer:
			fields[i] = VarToField(p, t.Elem())
		default:
			fields[i] = VarToField(p, t)
		}
	}
	return &ast.FieldList{List: fields}
}

// VarToField transforms a Var into an AST field
func VarToField(p *types.Var, t types.Type) *ast.Field {
	switch typ := t.(type) {
	case *types.Named:
		return NamedToField(p, typ)
	default:
		name := p.Name()
		typeName := p.Type().String()
		return &ast.Field{
			Type:  NewIdent(typeName),
			Names: []*ast.Ident{NewIdent(name)},
		}

	}
}

// NameToField transforms a Var that's a Named type into an AST Field
func NamedToField(p *types.Var, named *types.Named) *ast.Field {
	pkg := p.Pkg()
	if pkg == nil {
		pkg = named.Obj().Pkg()
	}

	if pkg != nil {
		path := pkg.Path()
		pkgAlias := PkgPathAliasFromString(path)
		typeName := pkgAlias + "." + named.Obj().Name()
		nameIdent := NewIdent(p.Name())
		return &ast.Field{
			Type:  NewIdent(typeName),
			Names: []*ast.Ident{nameIdent},
		}
	} else {
		fmt.Println(p, named)
		typeIdnet := NewIdent(p.Type().String())
		nameIdent := NewIdent(p.Name())
		return &ast.Field{
			Type:  typeIdnet,
			Names: []*ast.Ident{nameIdent},
		}
	}
}

// PkgPathAliasFromString takes a golang path as a string and returns an import alias for that path
func PkgPathAliasFromString(path string) string {
	splits := strings.FieldsFunc(path, splitPkgPath)
	return strings.Join(splits, "_")
}

func splitPkgPath(r rune) bool {
	return r == '.' || r == '/'
}
<|endoftext|>


<commit_before>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The buildlet is an HTTP server that untars content to disk and runs
// commands it has untarred, streaming their output back over HTTP.
// It is part of Go's continuous build system.
//
// This program intentionally allows remote code execution, and
// provides no security of its own. It is assumed that any user uses
// it with an appropriately-configured firewall between their VM
// instances.
package main // import "golang.org/x/tools/dashboard/buildlet"

/* Notes:

https://go.googlesource.com/go/+archive/3b76b017cabb.tar.gz
curl -X PUT --data-binary "@go-3b76b017cabb.tar.gz" http://127.0.0.1:5937/writetgz

curl -d "cmd=src/make.bash" http://127.0.0.1:5937/exec

*/

import (
	"archive/tar"
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

var (
	scratchDir = flag.String("scratchdir", "", "Temporary directory to use. The contents of this directory may be deleted at any time. If empty, TempDir is used to create one.")
	listenAddr = flag.String("listen", defaultListenAddr(), "address to listen on. Warning: this service is inherently insecure and offers no protection of its own. Do not expose this port to the world.")
)

func defaultListenAddr() string {
	if OnGCE() {
		// In production, default to
		return ":80"
	}
	return "localhost:5936"
}

func main() {
	flag.Parse()
	if !OnGCE() && !strings.HasPrefix(*listenAddr, "localhost:") {
		log.Printf("** WARNING ***  This server is unsafe and offers no security. Be careful.")
	}
	if *scratchDir == "" {
		dir, err := ioutil.TempDir("", "buildlet-scatch")
		if err != nil {
			log.Fatalf("error creating scratchdir with ioutil.TempDir: %v", err)
		}
		*scratchDir = dir
	}
	if _, err := os.Lstat(*scratchDir); err != nil {
		log.Fatalf("invalid --scratchdir %q: %v", *scratchDir, err)
	}
	http.HandleFunc("/writetgz", handleWriteTGZ)
	http.HandleFunc("/exec", handleExec)
	http.HandleFunc("/", handleRoot)
	// TODO: removeall
	log.Printf("Listening on %s ...", *listenAddr)
	log.Fatalf("ListenAndServe: %v", http.ListenAndServe(*listenAddr, nil))
}

func handleRoot(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "buildlet running on %s-%s", runtime.GOOS, runtime.GOARCH)
}

func handleWriteTGZ(w http.ResponseWriter, r *http.Request) {
	if r.Method != "PUT" {
		http.Error(w, "requires PUT method", http.StatusBadRequest)
		return
	}
	err := untar(r.Body, *scratchDir)
	if err != nil {
		status := http.StatusInternalServerError
		if he, ok := err.(httpStatuser); ok {
			status = he.httpStatus()
		}
		http.Error(w, err.Error(), status)
		return
	}
	io.WriteString(w, "OK")
}

// untar reads the gzip-compressed tar file from r and writes it into dir.
func untar(r io.Reader, dir string) error {
	zr, err := gzip.NewReader(r)
	if err != nil {
		return badRequest("requires gzip-compressed body: " + err.Error())
	}
	tr := tar.NewReader(zr)
	for {
		f, err := tr.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Printf("tar reading error: %v", err)
			return badRequest("tar error: " + err.Error())
		}
		if !validRelPath(f.Name) {
			return badRequest(fmt.Sprintf("tar file contained invalid name %q", f.Name))
		}
		rel := filepath.FromSlash(f.Name)
		abs := filepath.Join(dir, rel)

		fi := f.FileInfo()
		mode := fi.Mode()
		switch {
		case mode.IsRegular():
			// Make the directory. This is redundant because it should
			// already be made by a directory entry in the tar
			// beforehand. Thus, don't check for errors; the next
			// write will fail with the same error.
			os.MkdirAll(filepath.Dir(abs), 0755)
			wf, err := os.OpenFile(abs, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode.Perm())
			if err != nil {
				return err
			}
			n, err := io.Copy(wf, tr)
			if closeErr := wf.Close(); closeErr != nil && err == nil {
				err = closeErr
			}
			if err != nil {
				return fmt.Errorf("error writing to %s: %v", abs, err)
			}
			if n != f.Size {
				return fmt.Errorf("only wrote %d bytes to %s; expected %d", n, abs, f.Size)
			}
			log.Printf("wrote %s", abs)
		case mode.IsDir():
			if err := os.MkdirAll(abs, 0755); err != nil {
				return err
			}
		default:
			return badRequest(fmt.Sprintf("tar file entry %s contained unsupported file type %v", f.Name, mode))
		}
	}
	return nil
}

func handleExec(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "requires POST method", http.StatusBadRequest)
		return
	}
	cmdPath := r.FormValue("cmd") // required
	if !validRelPath(cmdPath) {
		http.Error(w, "requires 'cmd' parameter", http.StatusBadRequest)
		return
	}
	absCmd := filepath.Join(*scratchDir, filepath.FromSlash(cmdPath))
	cmd := exec.Command(absCmd, r.PostForm["cmdArg"]...)
	cmd.Dir = filepath.Dir(absCmd)
	cmdOutput := &flushWriter{w: w}
	cmd.Stdout = cmdOutput
	cmd.Stderr = cmdOutput
	err := cmd.Run()
	log.Printf("Run = %v", err)
	// TODO: put the exit status in the HTTP trailer,
	// once https://golang.org/issue/7759 is fixed.
}

// flushWriter is an io.Writer wrapper that writes to w and
// Flushes the output immediately, if w is an http.Flusher.
type flushWriter struct {
	mu sync.Mutex
	w  http.ResponseWriter
}

func (hw *flushWriter) Write(p []byte) (n int, err error) {
	hw.mu.Lock()
	defer hw.mu.Unlock()
	n, err = hw.w.Write(p)
	if f, ok := hw.w.(http.Flusher); ok {
		f.Flush()
	}
	return
}

func validRelPath(p string) bool {
	if p == "" || strings.Contains(p, `\`) || strings.HasPrefix(p, "/") || strings.Contains(p, "../") {
		return false
	}
	return true
}

type httpStatuser interface {
	error
	httpStatus() int
}

type httpError struct {
	statusCode int
	msg        string
}

func (he httpError) Error() string   { return he.msg }
func (he httpError) httpStatus() int { return he.statusCode }

func badRequest(msg string) error {
	return httpError{http.StatusBadRequest, msg}
}

// metaClient to fetch GCE metadata values.
var metaClient = &http.Client{
	Transport: &http.Transport{
		Dial: (&net.Dialer{
			Timeout:   750 * time.Millisecond,
			KeepAlive: 30 * time.Second,
		}).Dial,
		ResponseHeaderTimeout: 750 * time.Millisecond,
	},
}

var onGCE struct {
	sync.Mutex
	set bool
	v   bool
}

// OnGCE reports whether this process is running on Google Compute Engine.
func OnGCE() bool {
	defer onGCE.Unlock()
	onGCE.Lock()
	if onGCE.set {
		return onGCE.v
	}
	onGCE.set = true

	res, err := metaClient.Get("http://metadata.google.internal")
	if err != nil {
		return false
	}
	onGCE.v = res.Header.Get("Metadata-Flavor") == "Google"
	return onGCE.v
}
<commit_msg>dashboard/buildlet: make exec handler return process state in HTTP trailer<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The buildlet is an HTTP server that untars content to disk and runs
// commands it has untarred, streaming their output back over HTTP.
// It is part of Go's continuous build system.
//
// This program intentionally allows remote code execution, and
// provides no security of its own. It is assumed that any user uses
// it with an appropriately-configured firewall between their VM
// instances.
package main // import "golang.org/x/tools/dashboard/buildlet"

/* Notes:

https://go.googlesource.com/go/+archive/3b76b017cabb.tar.gz
curl -X PUT --data-binary "@go-3b76b017cabb.tar.gz" http://127.0.0.1:5937/writetgz

curl -d "cmd=src/make.bash" http://127.0.0.1:5937/exec

*/

import (
	"archive/tar"
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

var (
	scratchDir = flag.String("scratchdir", "", "Temporary directory to use. The contents of this directory may be deleted at any time. If empty, TempDir is used to create one.")
	listenAddr = flag.String("listen", defaultListenAddr(), "address to listen on. Warning: this service is inherently insecure and offers no protection of its own. Do not expose this port to the world.")
)

func defaultListenAddr() string {
	if OnGCE() {
		// In production, default to
		return ":80"
	}
	return "localhost:5936"
}

func main() {
	flag.Parse()
	if !OnGCE() && !strings.HasPrefix(*listenAddr, "localhost:") {
		log.Printf("** WARNING ***  This server is unsafe and offers no security. Be careful.")
	}
	if *scratchDir == "" {
		dir, err := ioutil.TempDir("", "buildlet-scatch")
		if err != nil {
			log.Fatalf("error creating scratchdir with ioutil.TempDir: %v", err)
		}
		*scratchDir = dir
	}
	if _, err := os.Lstat(*scratchDir); err != nil {
		log.Fatalf("invalid --scratchdir %q: %v", *scratchDir, err)
	}
	http.HandleFunc("/writetgz", handleWriteTGZ)
	http.HandleFunc("/exec", handleExec)
	http.HandleFunc("/", handleRoot)
	// TODO: removeall
	log.Printf("Listening on %s ...", *listenAddr)
	log.Fatalf("ListenAndServe: %v", http.ListenAndServe(*listenAddr, nil))
}

func handleRoot(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "buildlet running on %s-%s", runtime.GOOS, runtime.GOARCH)
}

func handleWriteTGZ(w http.ResponseWriter, r *http.Request) {
	if r.Method != "PUT" {
		http.Error(w, "requires PUT method", http.StatusBadRequest)
		return
	}
	err := untar(r.Body, *scratchDir)
	if err != nil {
		status := http.StatusInternalServerError
		if he, ok := err.(httpStatuser); ok {
			status = he.httpStatus()
		}
		http.Error(w, err.Error(), status)
		return
	}
	io.WriteString(w, "OK")
}

// untar reads the gzip-compressed tar file from r and writes it into dir.
func untar(r io.Reader, dir string) error {
	zr, err := gzip.NewReader(r)
	if err != nil {
		return badRequest("requires gzip-compressed body: " + err.Error())
	}
	tr := tar.NewReader(zr)
	for {
		f, err := tr.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Printf("tar reading error: %v", err)
			return badRequest("tar error: " + err.Error())
		}
		if !validRelPath(f.Name) {
			return badRequest(fmt.Sprintf("tar file contained invalid name %q", f.Name))
		}
		rel := filepath.FromSlash(f.Name)
		abs := filepath.Join(dir, rel)

		fi := f.FileInfo()
		mode := fi.Mode()
		switch {
		case mode.IsRegular():
			// Make the directory. This is redundant because it should
			// already be made by a directory entry in the tar
			// beforehand. Thus, don't check for errors; the next
			// write will fail with the same error.
			os.MkdirAll(filepath.Dir(abs), 0755)
			wf, err := os.OpenFile(abs, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode.Perm())
			if err != nil {
				return err
			}
			n, err := io.Copy(wf, tr)
			if closeErr := wf.Close(); closeErr != nil && err == nil {
				err = closeErr
			}
			if err != nil {
				return fmt.Errorf("error writing to %s: %v", abs, err)
			}
			if n != f.Size {
				return fmt.Errorf("only wrote %d bytes to %s; expected %d", n, abs, f.Size)
			}
			log.Printf("wrote %s", abs)
		case mode.IsDir():
			if err := os.MkdirAll(abs, 0755); err != nil {
				return err
			}
		default:
			return badRequest(fmt.Sprintf("tar file entry %s contained unsupported file type %v", f.Name, mode))
		}
	}
	return nil
}

// Process-State is an HTTP Trailer set in the /exec handler to "ok"
// on success, or os.ProcessState.String() on failure.
const hdrProcessState = "Process-State"

func handleExec(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "requires POST method", http.StatusBadRequest)
		return
	}
	if r.ProtoMajor*10+r.ProtoMinor < 11 {
		// We need trailers, only available in HTTP/1.1 or HTTP/2.
		http.Error(w, "HTTP/1.1 or higher required", http.StatusBadRequest)
		return
	}

	w.Header().Set("Trailer", hdrProcessState) // declare it so we can set it

	cmdPath := r.FormValue("cmd") // required
	if !validRelPath(cmdPath) {
		http.Error(w, "requires 'cmd' parameter", http.StatusBadRequest)
		return
	}
	if f, ok := w.(http.Flusher); ok {
		f.Flush()
	}

	absCmd := filepath.Join(*scratchDir, filepath.FromSlash(cmdPath))
	cmd := exec.Command(absCmd, r.PostForm["cmdArg"]...)
	cmd.Dir = filepath.Dir(absCmd)
	cmdOutput := &flushWriter{w: w}
	cmd.Stdout = cmdOutput
	cmd.Stderr = cmdOutput
	err := cmd.Run()
	state := "ok"
	if err != nil {
		if ps := cmd.ProcessState; ps != nil {
			state = ps.String()
		} else {
			state = err.Error()
		}
	}
	w.Header().Set(hdrProcessState, state)
	log.Printf("Run = %s", state)
}

// flushWriter is an io.Writer wrapper that writes to w and
// Flushes the output immediately, if w is an http.Flusher.
type flushWriter struct {
	mu sync.Mutex
	w  http.ResponseWriter
}

func (hw *flushWriter) Write(p []byte) (n int, err error) {
	hw.mu.Lock()
	defer hw.mu.Unlock()
	n, err = hw.w.Write(p)
	if f, ok := hw.w.(http.Flusher); ok {
		f.Flush()
	}
	return
}

func validRelPath(p string) bool {
	if p == "" || strings.Contains(p, `\`) || strings.HasPrefix(p, "/") || strings.Contains(p, "../") {
		return false
	}
	return true
}

type httpStatuser interface {
	error
	httpStatus() int
}

type httpError struct {
	statusCode int
	msg        string
}

func (he httpError) Error() string   { return he.msg }
func (he httpError) httpStatus() int { return he.statusCode }

func badRequest(msg string) error {
	return httpError{http.StatusBadRequest, msg}
}

// metaClient to fetch GCE metadata values.
var metaClient = &http.Client{
	Transport: &http.Transport{
		Dial: (&net.Dialer{
			Timeout:   750 * time.Millisecond,
			KeepAlive: 30 * time.Second,
		}).Dial,
		ResponseHeaderTimeout: 750 * time.Millisecond,
	},
}

var onGCE struct {
	sync.Mutex
	set bool
	v   bool
}

// OnGCE reports whether this process is running on Google Compute Engine.
func OnGCE() bool {
	defer onGCE.Unlock()
	onGCE.Lock()
	if onGCE.set {
		return onGCE.v
	}
	onGCE.set = true

	res, err := metaClient.Get("http://metadata.google.internal")
	if err != nil {
		return false
	}
	onGCE.v = res.Header.Get("Metadata-Flavor") == "Google"
	return onGCE.v
}
<|endoftext|>


<commit_before>package templates

const (
	/* Provider template */
	providerTemplate = `
provider "aws" {
	region  = "{{ .Region }}"
	profile = "{{ .Account }}"
}
`

	/* Terraform Backend */
	backendTemplate = `
terraform {
	required_version = ">= v0.11.7"
	backend "s3" {
		region     = "{{ .Region }}"
		profile    = "{{ .Account }}"
		bucket     = "{{ .Region }}-{{ .Account }}-ilm-state"
		key     = "terraform-states/s3backends/{{ .Region }}-{{ .Account }}-ilm-state.tfstate"
		encrypt    = "true"
		dynamodb_table = "{{ .Region }}-{{ .Account }}-dynamodb-table-tf-lock"
		acl        = "bucket-owner-full-control"
	}
}
`
)

/*
 * Return the provider template
 */
func GetProviderTemplate() string {
	return providerTemplate
}

/* Return Backend template */
func GetBackendTemplate() string {
	return backendTemplate
}
<commit_msg>Add S3 backend - dynamodb for lock<commit_after>package templates

const (
	/* Provider template */
	providerTemplate = `
provider "aws" {
	region  = "{{ .Region }}"
	profile = "{{ .Account }}"
}
`

	/* Terraform Backend */
	backendTemplate = `
terraform {
	required_version = ">= v0.11.7"
	backend "s3" {
		region     = "{{ .Region }}"
		profile    = "{{ .Account }}"
		bucket     = "{{ .Region }}-{{ .Account }}-tfstate-state"
		key     = "terraform-states/s3backends/{{ .Region }}-{{ .Account }}-tfstate-state.tfstate"
		encrypt    = "true"
		dynamodb_table = "{{ .Region }}-{{ .Account }}-dynamodb-table-tf-lock"
		acl        = "bucket-owner-full-control"
	}
}
`
/ * TF State S3 Backend Definition */
	tfstateBackendTemplate = `
	resource "aws_dynamodb_table" "terraform_statelock" {
		name           = "{{ .Region }}-{{ .Account }}-dynamodb-table-tf-lock"
		read_capacity  = 1
		write_capacity = 1
		hash_key       = "LockID"

		attribute {
			name = "LockID"
			type = "S"
		}

		lifecycle {
			prevent_destroy = false
		}
	}

	resource "aws_s3_bucket" "tfstate_state_bucket" {
	  bucket = "{{ .Region }}-{{ .Account }}-tfstate-state"

	}

	`
)

/*
 * Return the provider template
 */
func GetProviderTemplate() string {
	return providerTemplate
}

/* Return Backend template */
func GetBackendTemplate() string {
	return backendTemplate
}
<|endoftext|>


<commit_before>package client_handler

import (
	"crypto/rc4"
	"fmt"
	"io"
	"math/big"

	log "github.com/gonet2/libs/nsq-logger"
)

import (
	sp "github.com/gonet2/libs/services/proto"
	"misc/crypto/dh"
	"misc/packet"
	. "types"
)

// 心跳包
func P_heart_beat_req(sess *Session, reader *packet.Packet) []byte {
	return packet.Pack(Code["heart_beat_ack"], nil, nil)
}

// 密钥交换
func P_get_seed_req(sess *Session, reader *packet.Packet) []byte {
	tbl, _ := PKT_seed_info(reader)
	// KEY1
	X1, E1 := dh.DHExchange()
	KEY1 := dh.DHKey(X1, big.NewInt(int64(tbl.F_client_send_seed)))

	// KEY2
	X2, E2 := dh.DHExchange()
	KEY2 := dh.DHKey(X2, big.NewInt(int64(tbl.F_client_receive_seed)))

	ret := seed_info{int32(E1.Int64()), int32(E2.Int64())}
	// 服务器加密种子是客户端解密种子
	encoder, err := rc4.NewCipher([]byte(fmt.Sprintf("%v%v", SALT, KEY2)))
	if err != nil {
		log.Critical(err)
		return nil
	}
	decoder, err := rc4.NewCipher([]byte(fmt.Sprintf("%v%v", SALT, KEY1)))
	if err != nil {
		log.Critical(err)
		return nil
	}
	sess.Encoder = encoder
	sess.Decoder = decoder
	sess.Flag |= SESS_KEYEXCG
	return packet.Pack(Code["get_seed_ack"], ret, nil)
}

// 玩家登陆过程
func P_user_login_req(sess *Session, reader *packet.Packet) []byte {
	// fetch messages for current session
	fetcher_task := func(sess *Session) {
		for {
			in, err := sess.Stream.Recv()
			// close signal
			if err == io.EOF {
				log.Trace(err)
				return
			}
			if err != nil {
				log.Error(err)
				return
			}

			switch in.Type {
			case sp.Game_Message:
				sess.MQ <- in.Message
			case sp.Game_Kick:
				sess.Flag |= SESS_KICKED_OUT
			}
		}
	}
	go fetcher_task(sess)
	return nil
}

func checkErr(err error) {
	if err != nil {
		log.Error(err)
		panic("error occured in protocol module")
	}
}
<commit_msg>update<commit_after>package client_handler

import (
	"crypto/rc4"
	"fmt"
	"io"
	"math/big"

	log "github.com/gonet2/libs/nsq-logger"
)

import (
	sp "github.com/gonet2/libs/services/proto"
	"misc/crypto/dh"
	"misc/packet"
	. "types"
)

// 心跳包
func P_heart_beat_req(sess *Session, reader *packet.Packet) []byte {
	return packet.Pack(Code["heart_beat_ack"], nil, nil)
}

// 密钥交换
func P_get_seed_req(sess *Session, reader *packet.Packet) []byte {
	tbl, _ := PKT_seed_info(reader)
	// KEY1
	X1, E1 := dh.DHExchange()
	KEY1 := dh.DHKey(X1, big.NewInt(int64(tbl.F_client_send_seed)))

	// KEY2
	X2, E2 := dh.DHExchange()
	KEY2 := dh.DHKey(X2, big.NewInt(int64(tbl.F_client_receive_seed)))

	ret := seed_info{int32(E1.Int64()), int32(E2.Int64())}
	// 服务器加密种子是客户端解密种子
	encoder, err := rc4.NewCipher([]byte(fmt.Sprintf("%v%v", SALT, KEY2)))
	if err != nil {
		log.Critical(err)
		return nil
	}
	decoder, err := rc4.NewCipher([]byte(fmt.Sprintf("%v%v", SALT, KEY1)))
	if err != nil {
		log.Critical(err)
		return nil
	}
	sess.Encoder = encoder
	sess.Decoder = decoder
	sess.Flag |= SESS_KEYEXCG
	return packet.Pack(Code["get_seed_ack"], ret, nil)
}

// 玩家登陆过程
func P_user_login_req(sess *Session, reader *packet.Packet) []byte {
	// TODO : 登陆鉴权过程

	// 读取GAME返回消息
	fetcher_task := func(sess *Session) {
		for {
			in, err := sess.Stream.Recv()
			// close signal
			if err == io.EOF {
				log.Trace(err)
				return
			}
			if err != nil {
				log.Error(err)
				return
			}

			switch in.Type {
			case sp.Game_Message:
				sess.MQ <- in.Message
			case sp.Game_Kick:
				sess.Flag |= SESS_KICKED_OUT
			}
		}
	}
	go fetcher_task(sess)
	return nil
}

func checkErr(err error) {
	if err != nil {
		log.Error(err)
		panic("error occured in protocol module")
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Knative Authors

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

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

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

package handler

import (
	"context"
	"net/http"
	"net/http/httputil"
	"net/url"

	"go.opencensus.io/plugin/ochttp"
	"go.opencensus.io/trace"
	"go.uber.org/zap"

	"knative.dev/pkg/logging"
	pkgnet "knative.dev/pkg/network"
	tracingconfig "knative.dev/pkg/tracing/config"
	"knative.dev/pkg/tracing/propagation/tracecontextb3"
	"knative.dev/serving/pkg/activator"
	activatorconfig "knative.dev/serving/pkg/activator/config"
	"knative.dev/serving/pkg/activator/util"
	"knative.dev/serving/pkg/network"
	"knative.dev/serving/pkg/queue"
)

// Throttler is the interface that Handler calls to Try to proxy the user request.
type Throttler interface {
	Try(context.Context, func(string) error) error
}

// activationHandler will wait for an active endpoint for a revision
// to be available before proxying the request
type activationHandler struct {
	transport        http.RoundTripper
	tracingTransport http.RoundTripper
	throttler        Throttler
	bufferPool       httputil.BufferPool
}

// New constructs a new http.Handler that deals with revision activation.
func New(ctx context.Context, t Throttler) http.Handler {
	defaultTransport := pkgnet.AutoTransport
	return &activationHandler{
		transport: defaultTransport,
		tracingTransport: &ochttp.Transport{
			Base:        defaultTransport,
			Propagation: tracecontextb3.B3Egress,
		},
		throttler:  t,
		bufferPool: network.NewBufferPool(),
	}
}

func (a *activationHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	logger := logging.FromContext(r.Context())
	tracingEnabled := activatorconfig.FromContext(r.Context()).Tracing.Backend != tracingconfig.None

	tryContext, trySpan := r.Context(), (*trace.Span)(nil)
	if tracingEnabled {
		tryContext, trySpan = trace.StartSpan(r.Context(), "throttler_try")
	}

	if err := a.throttler.Try(tryContext, func(dest string) error {
		trySpan.End()

		proxyCtx, proxySpan := r.Context(), (*trace.Span)(nil)
		if tracingEnabled {
			proxyCtx, proxySpan = trace.StartSpan(r.Context(), "activator_proxy")
		}
		a.proxyRequest(logger, w, r.WithContext(proxyCtx), &url.URL{
			Scheme: "http",
			Host:   dest,
		}, tracingEnabled)
		proxySpan.End()

		return nil
	}); err != nil {
		// Set error on our capacity waiting span and end it
		trySpan.Annotate([]trace.Attribute{trace.StringAttribute("activator.throttler.error", err.Error())}, "ThrottlerTry")
		trySpan.End()

		logger.Errorw("Throttler try error", zap.Error(err))

		switch err {
		case context.DeadlineExceeded, queue.ErrRequestQueueFull:
			http.Error(w, err.Error(), http.StatusServiceUnavailable)
		default:
			w.WriteHeader(http.StatusInternalServerError)
		}
	}
}

func (a *activationHandler) proxyRequest(logger *zap.SugaredLogger, w http.ResponseWriter, r *http.Request, target *url.URL, tracingEnabled bool) {
	network.RewriteHostIn(r)
	r.Header.Set(network.ProxyHeaderName, activator.Name)

	// Setup the reverse proxy.
	proxy := httputil.NewSingleHostReverseProxy(target)
	proxy.BufferPool = a.bufferPool
	proxy.Transport = a.transport
	if tracingEnabled {
		proxy.Transport = a.tracingTransport
	}
	proxy.FlushInterval = network.FlushInterval
	proxy.ErrorHandler = pkgnet.ErrorHandler(logger)
	util.SetupHeaderPruning(proxy)

	proxy.ServeHTTP(w, r)
}
<commit_msg>Thread the new transport method which permits setting idle conns (#8810)<commit_after>/*
Copyright 2018 The Knative Authors

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

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

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

package handler

import (
	"context"
	"net/http"
	"net/http/httputil"
	"net/url"

	"go.opencensus.io/plugin/ochttp"
	"go.opencensus.io/trace"
	"go.uber.org/zap"

	"knative.dev/pkg/logging"
	pkgnet "knative.dev/pkg/network"
	tracingconfig "knative.dev/pkg/tracing/config"
	"knative.dev/pkg/tracing/propagation/tracecontextb3"
	"knative.dev/serving/pkg/activator"
	activatorconfig "knative.dev/serving/pkg/activator/config"
	"knative.dev/serving/pkg/activator/util"
	"knative.dev/serving/pkg/network"
	"knative.dev/serving/pkg/queue"
)

// Throttler is the interface that Handler calls to Try to proxy the user request.
type Throttler interface {
	Try(context.Context, func(string) error) error
}

// activationHandler will wait for an active endpoint for a revision
// to be available before proxying the request
type activationHandler struct {
	transport        http.RoundTripper
	tracingTransport http.RoundTripper
	throttler        Throttler
	bufferPool       httputil.BufferPool
}

// New constructs a new http.Handler that deals with revision activation.
func New(ctx context.Context, t Throttler) http.Handler {
	// TODO: run loadtests to determine the optimal values here.
	defaultTransport := pkgnet.NewAutoTransport(1000, /*maxidleconnes*/
		100 /*maxidleperhos*/)
	return &activationHandler{
		transport: defaultTransport,
		tracingTransport: &ochttp.Transport{
			Base:        defaultTransport,
			Propagation: tracecontextb3.B3Egress,
		},
		throttler:  t,
		bufferPool: network.NewBufferPool(),
	}
}

func (a *activationHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	logger := logging.FromContext(r.Context())
	tracingEnabled := activatorconfig.FromContext(r.Context()).Tracing.Backend != tracingconfig.None

	tryContext, trySpan := r.Context(), (*trace.Span)(nil)
	if tracingEnabled {
		tryContext, trySpan = trace.StartSpan(r.Context(), "throttler_try")
	}

	if err := a.throttler.Try(tryContext, func(dest string) error {
		trySpan.End()

		proxyCtx, proxySpan := r.Context(), (*trace.Span)(nil)
		if tracingEnabled {
			proxyCtx, proxySpan = trace.StartSpan(r.Context(), "activator_proxy")
		}
		a.proxyRequest(logger, w, r.WithContext(proxyCtx), &url.URL{
			Scheme: "http",
			Host:   dest,
		}, tracingEnabled)
		proxySpan.End()

		return nil
	}); err != nil {
		// Set error on our capacity waiting span and end it
		trySpan.Annotate([]trace.Attribute{trace.StringAttribute("activator.throttler.error", err.Error())}, "ThrottlerTry")
		trySpan.End()

		logger.Errorw("Throttler try error", zap.Error(err))

		switch err {
		case context.DeadlineExceeded, queue.ErrRequestQueueFull:
			http.Error(w, err.Error(), http.StatusServiceUnavailable)
		default:
			w.WriteHeader(http.StatusInternalServerError)
		}
	}
}

func (a *activationHandler) proxyRequest(logger *zap.SugaredLogger, w http.ResponseWriter, r *http.Request, target *url.URL, tracingEnabled bool) {
	network.RewriteHostIn(r)
	r.Header.Set(network.ProxyHeaderName, activator.Name)

	// Setup the reverse proxy.
	proxy := httputil.NewSingleHostReverseProxy(target)
	proxy.BufferPool = a.bufferPool
	proxy.Transport = a.transport
	if tracingEnabled {
		proxy.Transport = a.tracingTransport
	}
	proxy.FlushInterval = network.FlushInterval
	proxy.ErrorHandler = pkgnet.ErrorHandler(logger)
	util.SetupHeaderPruning(proxy)

	proxy.ServeHTTP(w, r)
}
<|endoftext|>


<commit_before>package cmd

const Version = "3.24.0"
<commit_msg>version bump<commit_after>package cmd

const Version = "3.25.0"
<|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 authentication

import (
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
)

const (
	// ImpersonateUserHeader is used to impersonate a particular user during an API server request
	ImpersonateUserHeader = "Impersonate-User"

	// ImpersonateGroupHeader is used to impersonate a particular group during an API server request.
	// It can be repeated multipled times for multiple groups.
	ImpersonateGroupHeader = "Impersonate-Group"
)

// +genclient=true
// +nonNamespaced=true
// +noMethods=true

// TokenReview attempts to authenticate a token to a known user.
type TokenReview struct {
	unversioned.TypeMeta
	// ObjectMeta fulfills the meta.ObjectMetaAccessor interface so that the stock
	// REST handler paths work
	api.ObjectMeta

	// Spec holds information about the request being evaluated
	Spec TokenReviewSpec

	// Status is filled in by the server and indicates whether the request can be authenticated.
	Status TokenReviewStatus
}

// TokenReviewSpec is a description of the token authentication request.
type TokenReviewSpec struct {
	// Token is the opaque bearer token.
	Token string
}

// TokenReviewStatus is the result of the token authentication request.
// This type mirrors the authentication.Token interface
type TokenReviewStatus struct {
	// Authenticated indicates that the token was associated with a known user.
	Authenticated bool
	// User is the UserInfo associated with the provided token.
	User UserInfo
	// Error indicates that the token couldn't be checked
	Error string
}

// UserInfo holds the information about the user needed to implement the
// user.Info interface.
type UserInfo struct {
	// The name that uniquely identifies this user among all active users.
	Username string
	// A unique value that identifies this user across time. If this user is
	// deleted and another user by the same name is added, they will have
	// different UIDs.
	UID string
	// The names of groups this user is a part of.
	Groups []string
	// Any additional information provided by the authenticator.
	Extra map[string]ExtraValue
}

// ExtraValue masks the value so protobuf can generate
type ExtraValue []string
<commit_msg>allow impersonating user.Info.Extra<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 authentication

import (
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
)

const (
	// ImpersonateUserHeader is used to impersonate a particular user during an API server request
	ImpersonateUserHeader = "Impersonate-User"

	// ImpersonateGroupHeader is used to impersonate a particular group during an API server request.
	// It can be repeated multipled times for multiple groups.
	ImpersonateGroupHeader = "Impersonate-Group"

	// ImpersonateUserExtraHeaderPrefix is a prefix for any header used to impersonate an entry in the
	// extra map[string][]string for user.Info.  The key will be every after the prefix.
	// It can be repeated multipled times for multiple map keys and the same key can be repeated multiple
	// times to have multiple elements in the slice under a single key
	ImpersonateUserExtraHeaderPrefix = "Impersonate-Extra-"
)

// +genclient=true
// +nonNamespaced=true
// +noMethods=true

// TokenReview attempts to authenticate a token to a known user.
type TokenReview struct {
	unversioned.TypeMeta
	// ObjectMeta fulfills the meta.ObjectMetaAccessor interface so that the stock
	// REST handler paths work
	api.ObjectMeta

	// Spec holds information about the request being evaluated
	Spec TokenReviewSpec

	// Status is filled in by the server and indicates whether the request can be authenticated.
	Status TokenReviewStatus
}

// TokenReviewSpec is a description of the token authentication request.
type TokenReviewSpec struct {
	// Token is the opaque bearer token.
	Token string
}

// TokenReviewStatus is the result of the token authentication request.
// This type mirrors the authentication.Token interface
type TokenReviewStatus struct {
	// Authenticated indicates that the token was associated with a known user.
	Authenticated bool
	// User is the UserInfo associated with the provided token.
	User UserInfo
	// Error indicates that the token couldn't be checked
	Error string
}

// UserInfo holds the information about the user needed to implement the
// user.Info interface.
type UserInfo struct {
	// The name that uniquely identifies this user among all active users.
	Username string
	// A unique value that identifies this user across time. If this user is
	// deleted and another user by the same name is added, they will have
	// different UIDs.
	UID string
	// The names of groups this user is a part of.
	Groups []string
	// Any additional information provided by the authenticator.
	Extra map[string]ExtraValue
}

// ExtraValue masks the value so protobuf can generate
type ExtraValue []string
<|endoftext|>