<commit_before>/*
Copyright 2019 Google LLC

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

    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 resources

import (
	"context"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/google/knative-gcp/pkg/apis/pubsub/v1alpha1"
	v1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestMakeMinimumReceiveAdapter(t *testing.T) {
	src := &v1alpha1.PullSubscription{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "source-name",
			Namespace: "source-namespace",
		},
		Spec: v1alpha1.PullSubscriptionSpec{
			Project: "eventing-name",
			Topic:   "topic",
			Secret: &corev1.SecretKeySelector{
				LocalObjectReference: corev1.LocalObjectReference{
					Name: "eventing-secret-name",
				},
				Key: "eventing-secret-key",
			},
		},
	}

	got := MakeReceiveAdapter(context.TODO(), &ReceiveAdapterArgs{
		Image:  "test-image",
		Source: src,
		Labels: map[string]string{
			"test-key1": "test-value1",
			"test-key2": "test-value2",
		},
		SubscriptionID: "sub-id",
		SinkURI:        "sink-uri",
		LoggingConfig:  "LoggingConfig-ABC123",
		MetricsConfig:  "MetricsConfig-ABC123",
	})

	one := int32(1)
	yes := true
	want := &v1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Namespace:   "source-namespace",
			Name:        "cre-pull-",
			Annotations: map[string]string{},
			Labels: map[string]string{
				"test-key1": "test-value1",
				"test-key2": "test-value2",
			},
			OwnerReferences: []metav1.OwnerReference{{
				APIVersion:         "pubsub.cloud.run/v1alpha1",
				Kind:               "PullSubscription",
				Name:               "source-name",
				Controller:         &yes,
				BlockOwnerDeletion: &yes,
			}},
		},
		Spec: v1.DeploymentSpec{
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{
					"test-key1": "test-value1",
					"test-key2": "test-value2",
				},
			},
			Replicas: &one,
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"test-key1": "test-value1",
						"test-key2": "test-value2",
					},
				},
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{{
						Name:  "receive-adapter",
						Image: "test-image",
						Env: []corev1.EnvVar{{
							Name:  "GOOGLE_APPLICATION_CREDENTIALS",
							Value: "/var/secrets/google/eventing-secret-key",
						}, {
							Name:  "PROJECT_ID",
							Value: "eventing-name",
						}, {
							Name:  "PUBSUB_TOPIC_ID",
							Value: "topic",
						}, {
							Name:  "PUBSUB_SUBSCRIPTION_ID",
							Value: "sub-id",
						}, {
							Name:  "SINK_URI",
							Value: "sink-uri",
						}, {
							Name: "TRANSFORMER_URI",
						}, {
							Name:  "SEND_MODE",
							Value: "binary",
						}, {
							Name: "K_CE_EXTENSIONS",
						}, {
							Name:  "K_METRICS_CONFIG",
							Value: "MetricsConfig-ABC123",
						}, {
							Name:  "K_LOGGING_CONFIG",
							Value: "LoggingConfig-ABC123",
						}},
						VolumeMounts: []corev1.VolumeMount{{
							Name:      credsVolume,
							MountPath: credsMountPath,
						}},
						Ports: []corev1.ContainerPort{{Name: "metrics", ContainerPort: 9090}},
					}},
					Volumes: []corev1.Volume{{
						Name: credsVolume,
						VolumeSource: corev1.VolumeSource{
							Secret: &corev1.SecretVolumeSource{
								SecretName: "eventing-secret-name",
							},
						},
					}},
				},
			},
		},
	}

	if diff := cmp.Diff(want, got); diff != "" {
		t.Errorf("unexpected deploy (-want, +got) = %v", diff)
	}
}

func TestMakeFullReceiveAdapter(t *testing.T) {
	src := &v1alpha1.PullSubscription{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "source-name",
			Namespace: "source-namespace",
		},
		Spec: v1alpha1.PullSubscriptionSpec{
			Project: "eventing-name",
			Topic:   "topic",
			CloudEventOverrides: &v1alpha1.CloudEventOverrides{
				Extensions: map[string]string{
					"foo": "bar", // base64 value is eyJmb28iOiJiYXIifQ==
				}},
			Secret: &corev1.SecretKeySelector{
				LocalObjectReference: corev1.LocalObjectReference{
					Name: "eventing-secret-name",
				},
				Key: "eventing-secret-key",
			},
		},
	}

	got := MakeReceiveAdapter(context.Background(), &ReceiveAdapterArgs{
		Image:  "test-image",
		Source: src,
		Labels: map[string]string{
			"test-key1": "test-value1",
			"test-key2": "test-value2",
		},
		SubscriptionID: "sub-id",
		SinkURI:        "sink-uri",
		TransformerURI: "transformer-uri",
		LoggingConfig:  "LoggingConfig-ABC123",
		MetricsConfig:  "MetricsConfig-ABC123",
	})

	one := int32(1)
	yes := true
	want := &v1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Namespace:   "source-namespace",
			Name:        "cre-pull-",
			Annotations: map[string]string{},
			Labels: map[string]string{
				"test-key1": "test-value1",
				"test-key2": "test-value2",
			},
			OwnerReferences: []metav1.OwnerReference{{
				APIVersion:         "pubsub.cloud.run/v1alpha1",
				Kind:               "PullSubscription",
				Name:               "source-name",
				Controller:         &yes,
				BlockOwnerDeletion: &yes,
			}},
		},
		Spec: v1.DeploymentSpec{
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{
					"test-key1": "test-value1",
					"test-key2": "test-value2",
				},
			},
			Replicas: &one,
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"test-key1": "test-value1",
						"test-key2": "test-value2",
					},
				},
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{{
						Name:  "receive-adapter",
						Image: "test-image",
						Env: []corev1.EnvVar{{
							Name:  "GOOGLE_APPLICATION_CREDENTIALS",
							Value: "/var/secrets/google/eventing-secret-key",
						}, {
							Name:  "PROJECT_ID",
							Value: "eventing-name",
						}, {
							Name:  "PUBSUB_TOPIC_ID",
							Value: "topic",
						}, {
							Name:  "PUBSUB_SUBSCRIPTION_ID",
							Value: "sub-id",
						}, {
							Name:  "SINK_URI",
							Value: "sink-uri",
						}, {
							Name:  "TRANSFORMER_URI",
							Value: "transformer-uri",
						}, {
							Name:  "SEND_MODE",
							Value: "binary",
						}, {
							Name:  "K_CE_EXTENSIONS",
							Value: "eyJmb28iOiJiYXIifQ==",
						}, {
							Name:  "K_METRICS_CONFIG",
							Value: "MetricsConfig-ABC123",
						}, {
							Name:  "K_LOGGING_CONFIG",
							Value: "LoggingConfig-ABC123",
						}},
						VolumeMounts: []corev1.VolumeMount{{
							Name:      credsVolume,
							MountPath: credsMountPath,
						}},
						Ports: []corev1.ContainerPort{{
							Name:          "metrics",
							ContainerPort: 9090,
						}},
					}},
					Volumes: []corev1.Volume{{
						Name: credsVolume,
						VolumeSource: corev1.VolumeSource{
							Secret: &corev1.SecretVolumeSource{
								SecretName: "eventing-secret-name",
							},
						},
					}},
				},
			},
		},
	}

	if diff := cmp.Diff(want, got); diff != "" {
		t.Errorf("unexpected deploy (-want, +got) = %v", diff)
	}
}
<commit_msg>Update pkg/reconciler/pullsubscription/resources/receive_adapter_test.go<commit_after>/*
Copyright 2019 Google LLC

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

    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 resources

import (
	"context"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/google/knative-gcp/pkg/apis/pubsub/v1alpha1"
	v1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestMakeMinimumReceiveAdapter(t *testing.T) {
	src := &v1alpha1.PullSubscription{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "source-name",
			Namespace: "source-namespace",
		},
		Spec: v1alpha1.PullSubscriptionSpec{
			Project: "eventing-name",
			Topic:   "topic",
			Secret: &corev1.SecretKeySelector{
				LocalObjectReference: corev1.LocalObjectReference{
					Name: "eventing-secret-name",
				},
				Key: "eventing-secret-key",
			},
		},
	}

	got := MakeReceiveAdapter(context.Background(), &ReceiveAdapterArgs{
		Image:  "test-image",
		Source: src,
		Labels: map[string]string{
			"test-key1": "test-value1",
			"test-key2": "test-value2",
		},
		SubscriptionID: "sub-id",
		SinkURI:        "sink-uri",
		LoggingConfig:  "LoggingConfig-ABC123",
		MetricsConfig:  "MetricsConfig-ABC123",
	})

	one := int32(1)
	yes := true
	want := &v1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Namespace:   "source-namespace",
			Name:        "cre-pull-",
			Annotations: map[string]string{},
			Labels: map[string]string{
				"test-key1": "test-value1",
				"test-key2": "test-value2",
			},
			OwnerReferences: []metav1.OwnerReference{{
				APIVersion:         "pubsub.cloud.run/v1alpha1",
				Kind:               "PullSubscription",
				Name:               "source-name",
				Controller:         &yes,
				BlockOwnerDeletion: &yes,
			}},
		},
		Spec: v1.DeploymentSpec{
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{
					"test-key1": "test-value1",
					"test-key2": "test-value2",
				},
			},
			Replicas: &one,
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"test-key1": "test-value1",
						"test-key2": "test-value2",
					},
				},
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{{
						Name:  "receive-adapter",
						Image: "test-image",
						Env: []corev1.EnvVar{{
							Name:  "GOOGLE_APPLICATION_CREDENTIALS",
							Value: "/var/secrets/google/eventing-secret-key",
						}, {
							Name:  "PROJECT_ID",
							Value: "eventing-name",
						}, {
							Name:  "PUBSUB_TOPIC_ID",
							Value: "topic",
						}, {
							Name:  "PUBSUB_SUBSCRIPTION_ID",
							Value: "sub-id",
						}, {
							Name:  "SINK_URI",
							Value: "sink-uri",
						}, {
							Name: "TRANSFORMER_URI",
						}, {
							Name:  "SEND_MODE",
							Value: "binary",
						}, {
							Name: "K_CE_EXTENSIONS",
						}, {
							Name:  "K_METRICS_CONFIG",
							Value: "MetricsConfig-ABC123",
						}, {
							Name:  "K_LOGGING_CONFIG",
							Value: "LoggingConfig-ABC123",
						}},
						VolumeMounts: []corev1.VolumeMount{{
							Name:      credsVolume,
							MountPath: credsMountPath,
						}},
						Ports: []corev1.ContainerPort{{Name: "metrics", ContainerPort: 9090}},
					}},
					Volumes: []corev1.Volume{{
						Name: credsVolume,
						VolumeSource: corev1.VolumeSource{
							Secret: &corev1.SecretVolumeSource{
								SecretName: "eventing-secret-name",
							},
						},
					}},
				},
			},
		},
	}

	if diff := cmp.Diff(want, got); diff != "" {
		t.Errorf("unexpected deploy (-want, +got) = %v", diff)
	}
}

func TestMakeFullReceiveAdapter(t *testing.T) {
	src := &v1alpha1.PullSubscription{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "source-name",
			Namespace: "source-namespace",
		},
		Spec: v1alpha1.PullSubscriptionSpec{
			Project: "eventing-name",
			Topic:   "topic",
			CloudEventOverrides: &v1alpha1.CloudEventOverrides{
				Extensions: map[string]string{
					"foo": "bar", // base64 value is eyJmb28iOiJiYXIifQ==
				}},
			Secret: &corev1.SecretKeySelector{
				LocalObjectReference: corev1.LocalObjectReference{
					Name: "eventing-secret-name",
				},
				Key: "eventing-secret-key",
			},
		},
	}

	got := MakeReceiveAdapter(context.Background(), &ReceiveAdapterArgs{
		Image:  "test-image",
		Source: src,
		Labels: map[string]string{
			"test-key1": "test-value1",
			"test-key2": "test-value2",
		},
		SubscriptionID: "sub-id",
		SinkURI:        "sink-uri",
		TransformerURI: "transformer-uri",
		LoggingConfig:  "LoggingConfig-ABC123",
		MetricsConfig:  "MetricsConfig-ABC123",
	})

	one := int32(1)
	yes := true
	want := &v1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Namespace:   "source-namespace",
			Name:        "cre-pull-",
			Annotations: map[string]string{},
			Labels: map[string]string{
				"test-key1": "test-value1",
				"test-key2": "test-value2",
			},
			OwnerReferences: []metav1.OwnerReference{{
				APIVersion:         "pubsub.cloud.run/v1alpha1",
				Kind:               "PullSubscription",
				Name:               "source-name",
				Controller:         &yes,
				BlockOwnerDeletion: &yes,
			}},
		},
		Spec: v1.DeploymentSpec{
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{
					"test-key1": "test-value1",
					"test-key2": "test-value2",
				},
			},
			Replicas: &one,
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"test-key1": "test-value1",
						"test-key2": "test-value2",
					},
				},
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{{
						Name:  "receive-adapter",
						Image: "test-image",
						Env: []corev1.EnvVar{{
							Name:  "GOOGLE_APPLICATION_CREDENTIALS",
							Value: "/var/secrets/google/eventing-secret-key",
						}, {
							Name:  "PROJECT_ID",
							Value: "eventing-name",
						}, {
							Name:  "PUBSUB_TOPIC_ID",
							Value: "topic",
						}, {
							Name:  "PUBSUB_SUBSCRIPTION_ID",
							Value: "sub-id",
						}, {
							Name:  "SINK_URI",
							Value: "sink-uri",
						}, {
							Name:  "TRANSFORMER_URI",
							Value: "transformer-uri",
						}, {
							Name:  "SEND_MODE",
							Value: "binary",
						}, {
							Name:  "K_CE_EXTENSIONS",
							Value: "eyJmb28iOiJiYXIifQ==",
						}, {
							Name:  "K_METRICS_CONFIG",
							Value: "MetricsConfig-ABC123",
						}, {
							Name:  "K_LOGGING_CONFIG",
							Value: "LoggingConfig-ABC123",
						}},
						VolumeMounts: []corev1.VolumeMount{{
							Name:      credsVolume,
							MountPath: credsMountPath,
						}},
						Ports: []corev1.ContainerPort{{
							Name:          "metrics",
							ContainerPort: 9090,
						}},
					}},
					Volumes: []corev1.Volume{{
						Name: credsVolume,
						VolumeSource: corev1.VolumeSource{
							Secret: &corev1.SecretVolumeSource{
								SecretName: "eventing-secret-name",
							},
						},
					}},
				},
			},
		},
	}

	if diff := cmp.Diff(want, got); diff != "" {
		t.Errorf("unexpected deploy (-want, +got) = %v", diff)
	}
}
<|endoftext|>


<commit_before>/*
 Copyright 2020 The GoPlus Authors (goplus.org)

 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 golang

import (
	"go/ast"
	"go/token"
	"reflect"
	"strconv"

	"github.com/goplus/gop/exec.spec"
	"github.com/goplus/gop/exec/golang/internal/go/printer"
	"github.com/qiniu/x/log"
)

// -----------------------------------------------------------------------------

const (
	nVariadicInvalid      = 0
	nVariadicFixedArgs    = 1
	nVariadicVariadicArgs = 2
)

// FuncInfo represents a Go+ function information.
type FuncInfo struct {
	isMethod int
	name     string
	closure  *printer.ReservedExpr // only when name="" (closure)
	recv     reflect.Type
	t        reflect.Type
	in       []reflect.Type
	out      []exec.Var
	scopeCtx
	nVariadic uint16
}

// NewFunc create a Go+ function.
func NewFunc(name string, nestDepth uint32, isMethod int) *FuncInfo {
	if name != "" {
		return &FuncInfo{name: name, isMethod: isMethod}
	}
	return &FuncInfo{closure: &printer.ReservedExpr{}}
}

func (p *FuncInfo) getFuncExpr(recv ast.Expr) ast.Expr {
	if p.isMethod == 1 {
		if p.name != "" {
			if unary, ok := recv.(*ast.UnaryExpr); ok {
				recv = unary.X
			}
			return &ast.SelectorExpr{
				X:   recv,
				Sel: Ident(p.name),
			}
		}
	}
	if p.name != "" {
		return Ident(p.name)
	}
	return p.closure
}

// Name returns the function name.
func (p *FuncInfo) Name() string {
	return p.name
}

// Type returns type of this function.
func (p *FuncInfo) Type() reflect.Type {
	if p.t == nil {
		out := make([]reflect.Type, len(p.out))
		for i, v := range p.out {
			out[i] = v.(*Var).typ
		}
		in := make([]reflect.Type, 0, p.NumIn())
		if p.isMethod == 1 {
			in = append(in, p.recv)
		}
		in = append(in, p.in...)
		p.t = reflect.FuncOf(in, out, p.IsVariadic())
	}
	return p.t
}

// NumIn returns a function's input parameter count.
func (p *FuncInfo) NumIn() int {
	return len(p.in) + p.isMethod
}

// NumOut returns a function's output parameter count.
func (p *FuncInfo) NumOut() int {
	return len(p.out)
}

// Out returns the type of a function type's i'th output parameter.
// It panics if i is not in the range [0, NumOut()).
func (p *FuncInfo) Out(i int) exec.Var {
	return p.out[i]
}

// Args sets argument types of a Go+ function.
func (p *FuncInfo) Args(in ...reflect.Type) exec.FuncInfo {
	if p.isMethod == 1 {
		p.recv = in[0]
		in = in[1:]
	}
	p.in = in
	p.setVariadic(nVariadicFixedArgs)
	return p
}

// Vargs sets argument types of a variadic Go+ function.
func (p *FuncInfo) Vargs(in ...reflect.Type) exec.FuncInfo {
	if in[len(in)-1].Kind() != reflect.Slice {
		log.Panicln("Vargs failed: last argument must be a slice.")
	}
	if p.isMethod == 1 {
		p.recv = in[0]
		in = in[1:]
	}
	p.in = in
	p.setVariadic(nVariadicVariadicArgs)
	return p
}

// Return sets return types of a Go+ function.
func (p *FuncInfo) Return(out ...exec.Var) exec.FuncInfo {
	p.out = out
	return p
}

// IsUnnamedOut returns if function results unnamed or not.
func (p *FuncInfo) IsUnnamedOut() bool {
	if len(p.out) > 0 {
		return p.out[0].IsUnnamedOut()
	}
	return false
}

// IsVariadic returns if this function is variadic or not.
func (p *FuncInfo) IsVariadic() bool {
	if p.nVariadic == 0 {
		log.Panicln("FuncInfo is unintialized.")
	}
	return p.nVariadic == nVariadicVariadicArgs
}

func (p *FuncInfo) setVariadic(nVariadic uint16) {
	if p.nVariadic == 0 {
		p.nVariadic = nVariadic
	} else if p.nVariadic != nVariadic {
		log.Panicln("setVariadic failed: unmatched -", p.name)
	}
}

// -----------------------------------------------------------------------------

// Closure instr
func (p *Builder) Closure(fun *FuncInfo) *Builder {
	p.rhs.Push(fun.getFuncExpr(nil))
	return p
}

// CallFunc instr
func (p *Builder) CallFunc(fun *FuncInfo, nexpr int) *Builder {
	if fun.isMethod == 1 {
		args := p.rhs.GetArgs(len(fun.in) + 1)
		recv := args[0]
		args = args[1:]
		p.rhs.Ret(len(fun.in)+1, args...)
		p.rhs.Push(fun.getFuncExpr(recv.(ast.Expr)))
	} else {
		p.rhs.Push(fun.getFuncExpr(nil))
	}
	return p.Call(nexpr, false)
}

// CallFuncv instr
func (p *Builder) CallFuncv(fun *FuncInfo, nexpr, arity int) *Builder {
	if fun.isMethod == 1 {
		args := p.rhs.GetArgs(arity)
		recv := args[0]
		args = args[1:]
		p.rhs.Ret(arity, args...)
		p.rhs.Push(fun.getFuncExpr(recv.(ast.Expr)))
	} else {
		p.rhs.Push(fun.getFuncExpr(nil))
	}
	return p.Call(nexpr, arity == -1)
}

// DefineFunc instr
func (p *Builder) DefineFunc(fun exec.FuncInfo) *Builder {
	f := fun.(*FuncInfo)
	f.initStmts()
	p.scopeCtx = &f.scopeCtx
	p.cfun = f
	return p
}

// Return instr
func (p *Builder) Return(n int32) *Builder {
	var results []ast.Expr
	var stmt ast.Stmt
	if n > 0 {
		arity := int(n)
		args := p.rhs.GetArgs(arity)
		results = make([]ast.Expr, n)
		for i, arg := range args {
			results[i] = arg.(ast.Expr)
		}
		p.rhs.PopN(arity)
	}
	stmt = &ast.ReturnStmt{Results: results}
	p.rhs.Push(stmt)
	return p
}

// EndFunc instr
func (p *Builder) EndFunc(fun *FuncInfo) *Builder {
	p.endBlockStmt(1)
	body := &ast.BlockStmt{List: fun.getStmts(p)}
	name := fun.name
	if name != "" {
		fn := &ast.FuncDecl{
			Name: Ident(name),
			Type: toFuncType(p, fun),
			Body: body,
		}
		if fun.isMethod == 1 {
			params := make([]*ast.Field, 1)
			params[0] = Field(p, "recv", fun.recv, "", false)
			fn.Recv = &ast.FieldList{Opening: 1, List: params, Closing: 1}
		}
		p.gblDecls = append(p.gblDecls, fn)
	} else {
		fun.closure.Expr = &ast.FuncLit{
			Type: toFuncType(p, fun),
			Body: body,
		}
	}
	p.cfun = nil
	p.scopeCtx = &p.gblScope
	return p
}

func toFuncType(p *Builder, typ *FuncInfo) *ast.FuncType {
	numIn, numOut := len(typ.in), typ.NumOut()
	variadic := typ.IsVariadic()
	var opening token.Pos
	var params, results []*ast.Field
	if numIn > 0 {
		params = make([]*ast.Field, numIn)
		if variadic {
			numIn--
		}
		for i := 0; i < numIn; i++ {
			params[i] = Field(p, toArg(i), typ.in[i], "", false)
		}
		if variadic {
			params[numIn] = Field(p, toArg(numIn), typ.in[numIn], "", true)
		}
	}
	if numOut > 0 {
		results = make([]*ast.Field, numOut)
		for i := 0; i < numOut; i++ {
			out := typ.Out(i).(*Var)
			results[i] = Field(p, out.name, out.typ, "", false)
		}
		opening++
	}
	return &ast.FuncType{
		Params:  &ast.FieldList{Opening: 1, List: params, Closing: 1},
		Results: &ast.FieldList{Opening: opening, List: results, Closing: opening},
	}
}

func toArg(i int) string {
	return "_arg_" + strconv.Itoa(i)
}
<commit_msg>resolve conflicts<commit_after>/*
 Copyright 2020 The GoPlus Authors (goplus.org)

 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 golang

import (
	"go/ast"
	"go/token"
	"reflect"
	"strconv"

	"github.com/goplus/gop/exec.spec"
	"github.com/goplus/gop/exec/golang/internal/go/printer"
	"github.com/qiniu/x/log"
)

// -----------------------------------------------------------------------------

const (
	nVariadicInvalid      = 0
	nVariadicFixedArgs    = 1
	nVariadicVariadicArgs = 2
)

// FuncInfo represents a Go+ function information.
type FuncInfo struct {
	isMethod int
	name     string
	closure  *printer.ReservedExpr // only when name="" (closure)
	recv     reflect.Type
	t        reflect.Type
	in       []reflect.Type
	out      []exec.Var
	scopeCtx
	nVariadic uint16
}

// NewFunc create a Go+ function.
func NewFunc(name string, nestDepth uint32, isMethod int) *FuncInfo {
	if name != "" {
		return &FuncInfo{name: name, isMethod: isMethod}
	}
	return &FuncInfo{closure: &printer.ReservedExpr{}}
}

func (p *FuncInfo) getFuncExpr(recv ast.Expr) ast.Expr {
	if p.isMethod == 1 {
		if p.name != "" {
			if unary, ok := recv.(*ast.UnaryExpr); ok {
				recv = unary.X
			}
			return &ast.SelectorExpr{
				X:   recv,
				Sel: Ident(p.name),
			}
		}
	}
	if p.name != "" {
		return Ident(p.name)
	}
	return p.closure
}

// Name returns the function name.
func (p *FuncInfo) Name() string {
	return p.name
}

// Type returns type of this function.
func (p *FuncInfo) Type() reflect.Type {
	if p.t == nil {
		out := make([]reflect.Type, len(p.out))
		for i, v := range p.out {
			out[i] = v.(*Var).typ
		}
		in := make([]reflect.Type, 0, p.NumIn())
		if p.isMethod == 1 {
			in = append(in, p.recv)
		}
		in = append(in, p.in...)
		p.t = reflect.FuncOf(in, out, p.IsVariadic())
	}
	return p.t
}

// NumIn returns a function's input parameter count.
func (p *FuncInfo) NumIn() int {
	return len(p.in) + p.isMethod
}

// NumOut returns a function's output parameter count.
func (p *FuncInfo) NumOut() int {
	return len(p.out)
}

// Out returns the type of a function type's i'th output parameter.
// It panics if i is not in the range [0, NumOut()).
func (p *FuncInfo) Out(i int) exec.Var {
	return p.out[i]
}

// Args sets argument types of a Go+ function.
func (p *FuncInfo) Args(in ...reflect.Type) exec.FuncInfo {
	if p.isMethod == 1 {
		p.recv = in[0]
		in = in[1:]
	}
	p.in = in
	p.setVariadic(nVariadicFixedArgs)
	return p
}

// Vargs sets argument types of a variadic Go+ function.
func (p *FuncInfo) Vargs(in ...reflect.Type) exec.FuncInfo {
	if in[len(in)-1].Kind() != reflect.Slice {
		log.Panicln("Vargs failed: last argument must be a slice.")
	}
	if p.isMethod == 1 {
		p.recv = in[0]
		in = in[1:]
	}
	p.in = in
	p.setVariadic(nVariadicVariadicArgs)
	return p
}

// Return sets return types of a Go+ function.
func (p *FuncInfo) Return(out ...exec.Var) exec.FuncInfo {
	p.out = out
	return p
}

// IsUnnamedOut returns if function results unnamed or not.
func (p *FuncInfo) IsUnnamedOut() bool {
	if len(p.out) > 0 {
		return p.out[0].IsUnnamedOut()
	}
	return false
}

// IsVariadic returns if this function is variadic or not.
func (p *FuncInfo) IsVariadic() bool {
	if p.nVariadic == 0 {
		log.Panicln("FuncInfo is unintialized.")
	}
	return p.nVariadic == nVariadicVariadicArgs
}

func (p *FuncInfo) setVariadic(nVariadic uint16) {
	if p.nVariadic == 0 {
		p.nVariadic = nVariadic
	} else if p.nVariadic != nVariadic {
		log.Panicln("setVariadic failed: unmatched -", p.name)
	}
}

// -----------------------------------------------------------------------------

// Closure instr
func (p *Builder) Closure(fun *FuncInfo) *Builder {
	p.rhs.Push(fun.getFuncExpr(nil))
	return p
}

// CallFunc instr
func (p *Builder) CallFunc(fun *FuncInfo, nexpr int) *Builder {
	if fun.isMethod == 1 {
		args := p.rhs.GetArgs(len(fun.in) + 1)
		recv := args[0]
		args = args[1:]
		p.rhs.Ret(len(fun.in)+1, args...)
		p.rhs.Push(fun.getFuncExpr(recv.(ast.Expr)))
	} else {
		p.rhs.Push(fun.getFuncExpr(nil))
	}
	return p.Call(nexpr, false)
}

// CallFuncv instr
func (p *Builder) CallFuncv(fun *FuncInfo, nexpr, arity int) *Builder {
	if fun.isMethod == 1 {
		args := p.rhs.GetArgs(arity)
		recv := args[0]
		args = args[1:]
		p.rhs.Ret(arity, args...)
		p.rhs.Push(fun.getFuncExpr(recv.(ast.Expr)))
	} else {
		p.rhs.Push(fun.getFuncExpr(nil))
	}
	return p.Call(nexpr, arity == -1)
}

// DefineFunc instr
func (p *Builder) DefineFunc(fun exec.FuncInfo) *Builder {
	f := fun.(*FuncInfo)
	f.initStmts()
	p.scopeCtx = &f.scopeCtx
	p.cfun = f
	return p
}

// Return instr
func (p *Builder) Return(n int32) *Builder {
	var results []ast.Expr
	var stmt ast.Stmt
	if n > 0 {
		arity := int(n)
		args := p.rhs.GetArgs(arity)
		for _, arg := range args {
			if v, ok := arg.(ast.Expr); ok {
				results = append(results, v)
			}
		}
		arity = len(results)
		p.rhs.PopN(arity)
	}
	stmt = &ast.ReturnStmt{Results: results}
	p.rhs.Push(stmt)
	return p
}

// EndFunc instr
func (p *Builder) EndFunc(fun *FuncInfo) *Builder {
	p.endBlockStmt(1)
	body := &ast.BlockStmt{List: fun.getStmts(p)}
	name := fun.name
	if name != "" {
		fn := &ast.FuncDecl{
			Name: Ident(name),
			Type: toFuncType(p, fun),
			Body: body,
		}
		if fun.isMethod == 1 {
			params := make([]*ast.Field, 1)
			params[0] = Field(p, "recv", fun.recv, "", false)
			fn.Recv = &ast.FieldList{Opening: 1, List: params, Closing: 1}
		}
		p.gblDecls = append(p.gblDecls, fn)
	} else {
		fun.closure.Expr = &ast.FuncLit{
			Type: toFuncType(p, fun),
			Body: body,
		}
	}
	p.cfun = nil
	p.scopeCtx = &p.gblScope
	return p
}

func toFuncType(p *Builder, typ *FuncInfo) *ast.FuncType {
	numIn, numOut := len(typ.in), typ.NumOut()
	variadic := typ.IsVariadic()
	var opening token.Pos
	var params, results []*ast.Field
	if numIn > 0 {
		params = make([]*ast.Field, numIn)
		if variadic {
			numIn--
		}
		for i := 0; i < numIn; i++ {
			params[i] = Field(p, toArg(i), typ.in[i], "", false)
		}
		if variadic {
			params[numIn] = Field(p, toArg(numIn), typ.in[numIn], "", true)
		}
	}
	if numOut > 0 {
		results = make([]*ast.Field, numOut)
		for i := 0; i < numOut; i++ {
			out := typ.Out(i).(*Var)
			results[i] = Field(p, out.name, out.typ, "", false)
		}
		opening++
	}
	return &ast.FuncType{
		Params:  &ast.FieldList{Opening: 1, List: params, Closing: 1},
		Results: &ast.FieldList{Opening: opening, List: results, Closing: opening},
	}
}

func toArg(i int) string {
	return "_arg_" + strconv.Itoa(i)
}
<|endoftext|>


<commit_before>package docker

import (
	"fmt"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/opentable/sous/lib"
	"github.com/opentable/sous/util/logging"
	"github.com/opentable/sous/util/logging/messages"
	"github.com/pkg/errors"
	"github.com/samsalisbury/semv"
)

// SourceIDFromLabels builds a SourceID from a map of labels, generally
// acquired from a Docker image.
func SourceIDFromLabels(labels map[string]string) (sous.SourceID, error) {
	missingLabels := make([]string, 0, 3)
	repo, present := labels[DockerRepoLabel]
	if !present {
		missingLabels = append(missingLabels, DockerRepoLabel)
	}

	versionStr, present := labels[DockerVersionLabel]
	if !present {
		missingLabels = append(missingLabels, DockerVersionLabel)
	}

	revision, present := labels[DockerRevisionLabel]
	if !present {
		missingLabels = append(missingLabels, DockerRevisionLabel)
	}

	path, present := labels[DockerPathLabel]
	if !present {
		missingLabels = append(missingLabels, DockerPathLabel)
	}

	if len(missingLabels) > 0 {
		err := errors.Errorf("Missing labels: %v", missingLabels)
		return sous.SourceID{}, err
	}

	id, err := sous.NewSourceID(repo, path, versionStr)
	id.Version.Meta = revision
	return id, err
}

// Labels computes a map of labels that should be applied to a container
// image that is built based on this SourceID.
func Labels(sid sous.SourceID, rev string) map[string]string {
	labels := make(map[string]string)
	labels[DockerVersionLabel] = sid.Version.Format(`M.m.p-?`)
	labels[DockerRevisionLabel] = rev
	labels[DockerPathLabel] = sid.Location.Dir
	labels[DockerRepoLabel] = sid.Location.Repo
	return labels
}

// XXX The idea is to make this a configuration value at some point.
var stripRE = regexp.MustCompile("^([[:alpha:]]+://)?(github.com(/opentable(/)?)?)?")

func imageRepoName(sl sous.SourceLocation, kind string) string {
	var name string
	if strings.HasPrefix(sl.Repo, "github.com") {
		name = imageRepoNameGitHub(sl.Repo, sl.Dir)
	} else {
		name = imageRepoNameGeneric(sl.Repo, sl.Dir)
	}

	if kind == "" {
		return name
	}

	return name + "-" + kind
}

func imageRepoNameGeneric(repo, offset string) string {
	return "blargle"
}

func imageRepoNameGitHub(repo, dir string) string {
	name := stripRE.ReplaceAllString(repo, "")
	if dir != "" {
		name = name + "/" + dir
	}
	return strings.ToLower(name)
}

func tagName(v semv.Version) string {
	return v.Format("M.m.p-?")
}

func versionName(sid sous.SourceID, kind string) string {
	return strings.Join([]string{imageRepoName(sid.Location, kind), tagName(sid.Version)}, ":")
}

func revisionName(sid sous.SourceID, rev string, kind string, time time.Time) string {
	//A tag name must be valid ASCII and may contain lowercase and uppercase
	//letters, digits, underscores, periods and dashes. A tag name may not start
	//with a period or a dash and may contain a maximum of 128 characters.
	//
	// revID = 40 bytes
	// RFC3339(ish) timestamp = 26 bytes
	// 40 + 26 + 2(separators) = 68 < 128

	// z prefix sorts "pinning" labels to the bottom
	// Format is the RFC3339 format, with . instead of : so that it's a legal docker tag
	labelStr := fmt.Sprintf("z%v-%v", rev, time.UTC().Format("2006-01-02T15.04.05"))
	return strings.Join([]string{imageRepoName(sid.Location, kind), labelStr}, ":")
}

func fullRepoName(registryHost string, sl sous.SourceLocation, kind string, ls logging.LogSink) string {
	frn := filepath.Join(registryHost, imageRepoName(sl, kind))
	messages.ReportLogFieldsMessage("Repo name", logging.DebugLevel, ls, sl, logging.KV("full-rep-name", frn))
	return frn
}

func versionTag(registryHost string, v sous.SourceID, kind string, ls logging.LogSink) string {
	verTag := filepath.Join(registryHost, versionName(v, kind))
	messages.ReportLogFieldsMessage("Docker Version Tag", logging.DebugLevel, ls, kind, logging.KV("version-tag", v))
	return verTag
}

func revisionTag(registryHost string, v sous.SourceID, rev string, kind string, time time.Time, ls logging.LogSink) string {
	revTag := filepath.Join(registryHost, revisionName(v, rev, kind, time))
	messages.ReportLogFieldsMessage("Docker RevisionTag", logging.DebugLevel, ls, kind, time, logging.KV("revision-tag", revTag), v)
	return revTag
}
<commit_msg>docker: unique image names (non-github)<commit_after>package docker

import (
	"crypto/sha1"
	"fmt"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/opentable/sous/lib"
	"github.com/opentable/sous/util/logging"
	"github.com/opentable/sous/util/logging/messages"
	"github.com/pkg/errors"
	"github.com/samsalisbury/semv"
)

// SourceIDFromLabels builds a SourceID from a map of labels, generally
// acquired from a Docker image.
func SourceIDFromLabels(labels map[string]string) (sous.SourceID, error) {
	missingLabels := make([]string, 0, 3)
	repo, present := labels[DockerRepoLabel]
	if !present {
		missingLabels = append(missingLabels, DockerRepoLabel)
	}

	versionStr, present := labels[DockerVersionLabel]
	if !present {
		missingLabels = append(missingLabels, DockerVersionLabel)
	}

	revision, present := labels[DockerRevisionLabel]
	if !present {
		missingLabels = append(missingLabels, DockerRevisionLabel)
	}

	path, present := labels[DockerPathLabel]
	if !present {
		missingLabels = append(missingLabels, DockerPathLabel)
	}

	if len(missingLabels) > 0 {
		err := errors.Errorf("Missing labels: %v", missingLabels)
		return sous.SourceID{}, err
	}

	id, err := sous.NewSourceID(repo, path, versionStr)
	id.Version.Meta = revision
	return id, err
}

// Labels computes a map of labels that should be applied to a container
// image that is built based on this SourceID.
func Labels(sid sous.SourceID, rev string) map[string]string {
	labels := make(map[string]string)
	labels[DockerVersionLabel] = sid.Version.Format(`M.m.p-?`)
	labels[DockerRevisionLabel] = rev
	labels[DockerPathLabel] = sid.Location.Dir
	labels[DockerRepoLabel] = sid.Location.Repo
	return labels
}

// XXX The idea is to make this a configuration value at some point.
var stripRE = regexp.MustCompile("^([[:alpha:]]+://)?(github.com(/opentable(/)?)?)?")

func imageRepoName(sl sous.SourceLocation, kind string) string {
	var name string
	if strings.HasPrefix(sl.Repo, "github.com") {
		name = imageRepoNameGitHub(sl.Repo, sl.Dir)
	} else {
		name = imageRepoNameGeneric(sl.Repo, sl.Dir)
	}

	if kind == "" {
		return name
	}

	return name + "-" + kind
}

func imageRepoNameGeneric(repo, offset string) string {
	name := repo
	if offset != "" {
		name += "/" + offset
	}
	return fmt.Sprintf("%x", sha1.Sum([]byte(name)))
}

func imageRepoNameGitHub(repo, dir string) string {
	name := stripRE.ReplaceAllString(repo, "")
	if dir != "" {
		name = name + "/" + dir
	}
	return strings.ToLower(name)
}

func tagName(v semv.Version) string {
	return v.Format("M.m.p-?")
}

func versionName(sid sous.SourceID, kind string) string {
	return strings.Join([]string{imageRepoName(sid.Location, kind), tagName(sid.Version)}, ":")
}

func revisionName(sid sous.SourceID, rev string, kind string, time time.Time) string {
	//A tag name must be valid ASCII and may contain lowercase and uppercase
	//letters, digits, underscores, periods and dashes. A tag name may not start
	//with a period or a dash and may contain a maximum of 128 characters.
	//
	// revID = 40 bytes
	// RFC3339(ish) timestamp = 26 bytes
	// 40 + 26 + 2(separators) = 68 < 128

	// z prefix sorts "pinning" labels to the bottom
	// Format is the RFC3339 format, with . instead of : so that it's a legal docker tag
	labelStr := fmt.Sprintf("z%v-%v", rev, time.UTC().Format("2006-01-02T15.04.05"))
	return strings.Join([]string{imageRepoName(sid.Location, kind), labelStr}, ":")
}

func fullRepoName(registryHost string, sl sous.SourceLocation, kind string, ls logging.LogSink) string {
	frn := filepath.Join(registryHost, imageRepoName(sl, kind))
	messages.ReportLogFieldsMessage("Repo name", logging.DebugLevel, ls, sl, logging.KV("full-rep-name", frn))
	return frn
}

func versionTag(registryHost string, v sous.SourceID, kind string, ls logging.LogSink) string {
	verTag := filepath.Join(registryHost, versionName(v, kind))
	messages.ReportLogFieldsMessage("Docker Version Tag", logging.DebugLevel, ls, kind, logging.KV("version-tag", v))
	return verTag
}

func revisionTag(registryHost string, v sous.SourceID, rev string, kind string, time time.Time, ls logging.LogSink) string {
	revTag := filepath.Join(registryHost, revisionName(v, rev, kind, time))
	messages.ReportLogFieldsMessage("Docker RevisionTag", logging.DebugLevel, ls, kind, time, logging.KV("revision-tag", revTag), v)
	return revTag
}
<|endoftext|>


<commit_before>package app

import (
	"html/template"
	"net/http"

	"github.com/prasannavl/go-gluons/http/httpservice"

	"github.com/prasannavl/mchain/hconv"

	"github.com/prasannavl/mchain"

	"github.com/prasannavl/go-gluons/http/chainutils"
	"github.com/prasannavl/go-gluons/http/gosock"
	"github.com/prasannavl/go-gluons/http/hostrouter"
	"github.com/prasannavl/go-gluons/http/utils"

	"github.com/prasannavl/go-gluons/http/fileserver"
	"github.com/prasannavl/go-gluons/http/middleware"
	"github.com/prasannavl/go-gluons/log"
	"github.com/prasannavl/mchain/builder"
)

func newAppHandler(c *AppContext, webRoot string) mchain.Handler {
	apiHandlers := apiHandlers(c)
	wss := gosock.NewWebSocketServer(apiHandlers)

	b := builder.Create()

	b.Add(
		middleware.CreateInitMiddleware(c.Logger),
		middleware.CreateLogMiddleware(log.InfoLevel),
		middleware.ErrorHandlerMiddleware,
		middleware.PanicRecoveryMiddleware,
		middleware.CreateRequestIDHandler(false),
		chainutils.OnPrefix("/api", wss),
		chainutils.OnPrefix("/assets/gotalk.js", gosock.CreateAssetHandler("/assets/gotalk.js", "/api", false)),
	)

	notFoundFilePath := webRoot + "/error/404.html"

	b.Handler(fileserver.NewEx(http.Dir(webRoot),
		utils.CreateFileHandler(notFoundFilePath, http.StatusNotFound).ServeHTTP))
	return b.Build()
}

func createAppContext(logger *log.Logger, addr string) *AppContext {
	services := Services{
		Logger:        logger,
		TemplateCache: make(map[string]*template.Template),
	}
	c := AppContext{
		Services:      services,
		ServerAddress: addr,
	}
	return &c
}

func NewApp(logger *log.Logger, addr string, webRoot string, hosts []string) http.Handler {
	context := createAppContext(logger, addr)
	appHandler := hconv.ToHttp(newAppHandler(context, webRoot), nil)
	if len(hosts) == 0 {
		return appHandler
	}
	r := hostrouter.New()
	log.Infof("host filters: %v", hosts)
	for _, h := range hosts {
		r.HandlePattern(h, appHandler)
	}

	notFoundFilePath := webRoot + "/error/404.html"

	return r.Build(hconv.FuncToHttp(
		utils.CreateFileHandler(notFoundFilePath, http.StatusNotFound).ServeHTTP,
		utils.InternalServerErrorAndLog))
}

func CreateService(logger *log.Logger, addr string, webRoot string, hosts []string,
	insecure bool, useSelfSigned bool) (httpservice.Service, error) {
	app := NewApp(logger, addr, webRoot, hosts)
	return httpservice.NewHandlerService(logger, addr, app,
		webRoot, hosts, insecure, useSelfSigned)
}
<commit_msg>add: todo comments<commit_after>package app

import (
	"html/template"
	"net/http"

	"github.com/prasannavl/go-gluons/http/httpservice"

	"github.com/prasannavl/mchain/hconv"

	"github.com/prasannavl/mchain"

	"github.com/prasannavl/go-gluons/http/chainutils"
	"github.com/prasannavl/go-gluons/http/gosock"
	"github.com/prasannavl/go-gluons/http/hostrouter"
	"github.com/prasannavl/go-gluons/http/utils"

	"github.com/prasannavl/go-gluons/http/fileserver"
	"github.com/prasannavl/go-gluons/http/middleware"
	"github.com/prasannavl/go-gluons/log"
	"github.com/prasannavl/mchain/builder"
)

// TODO: Router
// TODO: More elaborate request logging with user agent, remote ip

func newAppHandler(c *AppContext, webRoot string) mchain.Handler {
	apiHandlers := apiHandlers(c)
	wss := gosock.NewWebSocketServer(apiHandlers)

	b := builder.Create()

	b.Add(
		middleware.CreateInitMiddleware(c.Logger),
		middleware.CreateLogMiddleware(log.InfoLevel),
		middleware.ErrorHandlerMiddleware,
		middleware.PanicRecoveryMiddleware,
		middleware.CreateRequestIDHandler(false),
		chainutils.OnPrefix("/api", wss),
		chainutils.OnPrefix("/assets/gotalk.js", gosock.CreateAssetHandler("/assets/gotalk.js", "/api", false)),
	)

	notFoundFilePath := webRoot + "/error/404.html"

	b.Handler(fileserver.NewEx(http.Dir(webRoot),
		utils.CreateFileHandler(notFoundFilePath, http.StatusNotFound).ServeHTTP))
	return b.Build()
}

func createAppContext(logger *log.Logger, addr string) *AppContext {
	services := Services{
		Logger:        logger,
		TemplateCache: make(map[string]*template.Template),
	}
	c := AppContext{
		Services:      services,
		ServerAddress: addr,
	}
	return &c
}

func NewApp(logger *log.Logger, addr string, webRoot string, hosts []string) http.Handler {
	context := createAppContext(logger, addr)
	appHandler := hconv.ToHttp(newAppHandler(context, webRoot), nil)
	if len(hosts) == 0 {
		return appHandler
	}
	r := hostrouter.New()
	log.Infof("host filters: %v", hosts)
	for _, h := range hosts {
		r.HandlePattern(h, appHandler)
	}

	notFoundFilePath := webRoot + "/error/404.html"

	return r.Build(hconv.FuncToHttp(
		utils.CreateFileHandler(notFoundFilePath, http.StatusNotFound).ServeHTTP,
		utils.InternalServerErrorAndLog))
}

func CreateService(logger *log.Logger, addr string, webRoot string, hosts []string,
	insecure bool, useSelfSigned bool) (httpservice.Service, error) {
	app := NewApp(logger, addr, webRoot, hosts)
	return httpservice.NewHandlerService(logger, addr, app,
		webRoot, hosts, insecure, useSelfSigned)
}
<|endoftext|>


<commit_before>package skycmd

import (
	"encoding/json"
	"errors"

	"github.com/concourse/flag"
	"github.com/coreos/dex/connector/cf"
	"github.com/hashicorp/go-multierror"
)

func init() {
	RegisterConnector(&Connector{
		id:         "cf",
		config:     &CFFlags{},
		teamConfig: &CFTeamFlags{},
	})
}

type CFFlags struct {
	ClientID           string      `long:"client-id" description:"Client id"`
	ClientSecret       string      `long:"client-secret" description:"Client secret"`
	APIURL             string      `long:"api-url" description:"API URL"`
	CACerts            []flag.File `long:"ca-cert" description:"CA Certificate"`
	InsecureSkipVerify bool        `long:"skip-ssl-validation" description:"Skip SSL validation"`
}

func (self *CFFlags) Name() string {
	return "CloudFoundry"
}

func (self *CFFlags) Validate() error {
	var errs *multierror.Error

	if self.APIURL == "" {
		errs = multierror.Append(errs, errors.New("Missing api-url"))
	}

	if self.ClientID == "" {
		errs = multierror.Append(errs, errors.New("Missing client-id"))
	}

	if self.ClientSecret == "" {
		errs = multierror.Append(errs, errors.New("Missing client-secret"))
	}

	return errs.ErrorOrNil()
}

func (self *CFFlags) Serialize(redirectURI string) ([]byte, error) {
	if err := self.Validate(); err != nil {
		return nil, err
	}

	caCerts := []string{}
	for _, file := range self.CACerts {
		caCerts = append(caCerts, file.Path())
	}

	return json.Marshal(cf.Config{
		ClientID:           self.ClientID,
		ClientSecret:       self.ClientSecret,
		APIURL:             self.APIURL,
		RootCAs:            caCerts,
		InsecureSkipVerify: self.InsecureSkipVerify,
		RedirectURI:        redirectURI,
	})
}

type CFTeamFlags struct {
	Users  []string `long:"user" description:"List of whitelisted CloudFoundry users." value-name:"USERNAME"`
	Orgs   []string `long:"org" description:"List of whitelisted CloudFoundry orgs" value-name:"ORG_NAME"`
	Spaces []string `long:"space" description:"List of whitelisted CloudFoundry spaces" value-name:"ORG_NAME:SPACE_NAME"`
}

func (self *CFTeamFlags) IsValid() bool {
	return len(self.Users) > 0 || len(self.Orgs) > 0 || len(self.Spaces) > 0
}

func (self *CFTeamFlags) GetUsers() []string {
	return self.Users
}

func (self *CFTeamFlags) GetGroups() []string {
	return append(self.Orgs, self.Spaces...)
}
<commit_msg>Add deprecated flag to support cf-space-guids, for backwards compatibility<commit_after>package skycmd

import (
	"encoding/json"
	"errors"

	"github.com/concourse/flag"
	"github.com/coreos/dex/connector/cf"
	"github.com/hashicorp/go-multierror"
)

func init() {
	RegisterConnector(&Connector{
		id:         "cf",
		config:     &CFFlags{},
		teamConfig: &CFTeamFlags{},
	})
}

type CFFlags struct {
	ClientID           string      `long:"client-id" description:"Client id"`
	ClientSecret       string      `long:"client-secret" description:"Client secret"`
	APIURL             string      `long:"api-url" description:"API URL"`
	CACerts            []flag.File `long:"ca-cert" description:"CA Certificate"`
	InsecureSkipVerify bool        `long:"skip-ssl-validation" description:"Skip SSL validation"`
}

func (self *CFFlags) Name() string {
	return "CloudFoundry"
}

func (self *CFFlags) Validate() error {
	var errs *multierror.Error

	if self.APIURL == "" {
		errs = multierror.Append(errs, errors.New("Missing api-url"))
	}

	if self.ClientID == "" {
		errs = multierror.Append(errs, errors.New("Missing client-id"))
	}

	if self.ClientSecret == "" {
		errs = multierror.Append(errs, errors.New("Missing client-secret"))
	}

	return errs.ErrorOrNil()
}

func (self *CFFlags) Serialize(redirectURI string) ([]byte, error) {
	if err := self.Validate(); err != nil {
		return nil, err
	}

	caCerts := []string{}
	for _, file := range self.CACerts {
		caCerts = append(caCerts, file.Path())
	}

	return json.Marshal(cf.Config{
		ClientID:           self.ClientID,
		ClientSecret:       self.ClientSecret,
		APIURL:             self.APIURL,
		RootCAs:            caCerts,
		InsecureSkipVerify: self.InsecureSkipVerify,
		RedirectURI:        redirectURI,
	})
}

type CFTeamFlags struct {
	Users      []string `long:"user" description:"List of whitelisted CloudFoundry users." value-name:"USERNAME"`
	Orgs       []string `long:"org" description:"List of whitelisted CloudFoundry orgs" value-name:"ORG_NAME"`
	Spaces     []string `long:"space" description:"List of whitelisted CloudFoundry spaces" value-name:"ORG_NAME:SPACE_NAME"`
	SpaceGuids []string `long:"space-guid" description:"(Deprecated) List of whitelisted CloudFoundry space guids" value-name:"SPACE_GUID"`
}

func (self *CFTeamFlags) IsValid() bool {
	return len(self.Users) > 0 || len(self.Orgs) > 0 || len(self.Spaces) > 0 || len(self.SpaceGuids) > 0
}

func (self *CFTeamFlags) GetUsers() []string {
	return self.Users
}

func (self *CFTeamFlags) GetGroups() []string {
	var groups []string
	groups = append(groups, self.Orgs...)
	groups = append(groups, self.Spaces...)
	groups = append(groups, self.SpaceGuids...)
	return groups
}
<|endoftext|>


<commit_before>package sl

import (
	"testing"

	"github.ibm.com/riethm/gopherlayer.git/datatypes"
	"reflect"
)

func TestGet(t *testing.T) {
	// primitive test values
	testString := "Test"
	testInt := 123
	testFloat := 3.14159
	testBool := true

	// Pass by value
	if Get(testString) != "Test" {
		t.Errorf("Expected %s, got %s", testString, Get(testString))
	}

	if Get(testInt) != 123 {
		t.Errorf("Expected %d, got %d", testInt, Get(testInt))
	}

	if Get(testFloat) != 3.14159 {
		t.Errorf("Expected %f, got %f", testFloat, Get(testFloat))
	}

	if Get(testBool) != true {
		t.Errorf("Expected %t, got %t", testBool, Get(testBool))
	}

	// Pass by reference
	if Get(&testString) != "Test" {
		t.Errorf("Expected %s, got %s", testString, Get(&testString))
	}

	if Get(&testInt) != 123 {
		t.Errorf("Expected %d, got %d", testInt, Get(&testInt))
	}

	if Get(&testFloat) != 3.14159 {
		t.Errorf("Expected %f, got %f", testFloat, Get(&testFloat))
	}

	if Get(&testBool) != true {
		t.Errorf("Expected %t, got %t", testBool, Get(&testBool))
	}

	// zero value primitive
	var testZero int

	if Get(testZero) != 0 {
		t.Errorf("Expected %d, got %d", testZero, Get(testZero))
	}

	if Get(&testZero) != 0 {
		t.Errorf("Expected %d, got %d", testZero, Get(&testZero))
	}

	// nil pointer to primitive
	var testNil *int

	if Get(testNil) != 0 {
		t.Errorf("Expected %d, got %d", testNil, Get(testNil))
	}
}

func TestGetStruct(t *testing.T) {
	// Complex datatype test with
	// 1. non-nil primitive
	// 2. nil primitive (anything not set)
	// 3. non-nil ptr-to-struct type (Account)
	// 4. nil ptr-to-struct type (any unspecified ptr-to-struct field, e.g., Location)
	// 5. non-nil slice type (ActiveTickets)
	// 6. zero-val slice type (unspecified, e.g., ActiveTransactions)

	account := datatypes.Account{
		Id: Int(456),
	}

	tickets := []datatypes.Ticket{
		datatypes.Ticket{
			Id: Int(789),
		},
	}

	location := datatypes.Location{}

	var transactions []datatypes.Provisioning_Version1_Transaction

	d := datatypes.Virtual_Guest{
		Id:            Int(123),
		Account:       &account,
		ActiveTickets: tickets,
	}

	if Get(d.Id) != 123 {
		t.Errorf("Expected %d, got %d", *d.Id, Get(d.Id))
	}

	if !reflect.DeepEqual(Get(d.Account), account) {
		t.Errorf("Expected %v, got %v", account, Get(d.Account))
	}

	if !reflect.DeepEqual(Get(d.Location), location) {
		t.Errorf("Expected %v, got %v", location, Get(d.Location))
	}

	if !reflect.DeepEqual(Get(d.ActiveTickets), tickets) {
		t.Errorf("Expected %v, got %v", tickets, Get(d.ActiveTickets))
	}

	if !reflect.DeepEqual(Get(d.ActiveTransactions), transactions) {
		t.Errorf("Expected %#v, got %#v", transactions, Get(d.ActiveTransactions))
	}
}

func TestGetDefaults(t *testing.T) {
	var testptr *int

	if Get(testptr, 123) != 123 {
		t.Errorf("Expected 123, got %d", Get(testptr, 123))
	}
}
<commit_msg>Add copyright header to new file<commit_after>/**
 * Copyright 2016 IBM Corp.
 *
 * 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 sl

import (
	"testing"

	"github.ibm.com/riethm/gopherlayer.git/datatypes"
	"reflect"
)

func TestGet(t *testing.T) {
	// primitive test values
	testString := "Test"
	testInt := 123
	testFloat := 3.14159
	testBool := true

	// Pass by value
	if Get(testString) != "Test" {
		t.Errorf("Expected %s, got %s", testString, Get(testString))
	}

	if Get(testInt) != 123 {
		t.Errorf("Expected %d, got %d", testInt, Get(testInt))
	}

	if Get(testFloat) != 3.14159 {
		t.Errorf("Expected %f, got %f", testFloat, Get(testFloat))
	}

	if Get(testBool) != true {
		t.Errorf("Expected %t, got %t", testBool, Get(testBool))
	}

	// Pass by reference
	if Get(&testString) != "Test" {
		t.Errorf("Expected %s, got %s", testString, Get(&testString))
	}

	if Get(&testInt) != 123 {
		t.Errorf("Expected %d, got %d", testInt, Get(&testInt))
	}

	if Get(&testFloat) != 3.14159 {
		t.Errorf("Expected %f, got %f", testFloat, Get(&testFloat))
	}

	if Get(&testBool) != true {
		t.Errorf("Expected %t, got %t", testBool, Get(&testBool))
	}

	// zero value primitive
	var testZero int

	if Get(testZero) != 0 {
		t.Errorf("Expected %d, got %d", testZero, Get(testZero))
	}

	if Get(&testZero) != 0 {
		t.Errorf("Expected %d, got %d", testZero, Get(&testZero))
	}

	// nil pointer to primitive
	var testNil *int

	if Get(testNil) != 0 {
		t.Errorf("Expected %d, got %d", testNil, Get(testNil))
	}
}

func TestGetStruct(t *testing.T) {
	// Complex datatype test with
	// 1. non-nil primitive
	// 2. nil primitive (anything not set)
	// 3. non-nil ptr-to-struct type (Account)
	// 4. nil ptr-to-struct type (any unspecified ptr-to-struct field, e.g., Location)
	// 5. non-nil slice type (ActiveTickets)
	// 6. zero-val slice type (unspecified, e.g., ActiveTransactions)

	account := datatypes.Account{
		Id: Int(456),
	}

	tickets := []datatypes.Ticket{
		datatypes.Ticket{
			Id: Int(789),
		},
	}

	location := datatypes.Location{}

	var transactions []datatypes.Provisioning_Version1_Transaction

	d := datatypes.Virtual_Guest{
		Id:            Int(123),
		Account:       &account,
		ActiveTickets: tickets,
	}

	if Get(d.Id) != 123 {
		t.Errorf("Expected %d, got %d", *d.Id, Get(d.Id))
	}

	if !reflect.DeepEqual(Get(d.Account), account) {
		t.Errorf("Expected %v, got %v", account, Get(d.Account))
	}

	if !reflect.DeepEqual(Get(d.Location), location) {
		t.Errorf("Expected %v, got %v", location, Get(d.Location))
	}

	if !reflect.DeepEqual(Get(d.ActiveTickets), tickets) {
		t.Errorf("Expected %v, got %v", tickets, Get(d.ActiveTickets))
	}

	if !reflect.DeepEqual(Get(d.ActiveTransactions), transactions) {
		t.Errorf("Expected %#v, got %#v", transactions, Get(d.ActiveTransactions))
	}
}

func TestGetDefaults(t *testing.T) {
	var testptr *int

	if Get(testptr, 123) != 123 {
		t.Errorf("Expected 123, got %d", Get(testptr, 123))
	}
}
<|endoftext|>


<commit_before>package vtn

func triangleNumbers(nums []int) int {
    return 0
}
<commit_msg>brute force<commit_after>package vtn

import "sort"

func triangleNumbers(nums []int) int {
    return bruteForce(nums)
}

func bruteForce(nums []int) int {
    var res int
    n := len(nums)
    if n < 3 {
        return res
    }
    // sort nums O(nlgn)
    sort.Slice(nums, func(i, j int) bool {
        return nums[i] <= nums[j]
    })
    // time complexity O(n^3)
    for i := 0; i < n-2; i++ {
        for j := i + 1; j < n-1; j++ {
            for k := j + 1; k < n; k++ {
                if nums[i]+nums[j] > nums[k] {
                    res++
                } else {
                    break
                }
            }
        }
    }

    return res
}

// func helper(nums []int, res *int, pos int) {

// }
<|endoftext|>


<commit_before>package rule

import (
	"fmt"
	"math"
	"strconv"
	"time"
)

type Rule string

const (
	rule_start = iota
	vldt_start //validator start
	vldt_end   //validator end
	rule_end
)

func (r Rule) Parse() ([]Validator, error) {
	var vlds []Validator

	add := func(r Rule, pos int, f func(string, int) (Validator, int, error)) (int, error) {
		v, pos, err := f(string(r), pos)
		if err != nil {
			return pos, err
		}
		vlds = append(vlds, v)
		return pos, err
	}

	var err error
	st := rule_start
	for i := 0; i < len(r); i++ {
		c := r[i]
		switch c {
		case '(', '[':
			switch st {
			case rule_start, vldt_end:
				st = vldt_start
				i, err = add(r, i, validatorRange)
				if err != nil {
					return nil, err
				}
				st = vldt_end
			default:
				return nil, fmt.Errorf("parse error at position %d of rule %s", i, r)
			}
		case '<', '>':
			switch st {
			case rule_start, vldt_end:
				st = vldt_start
				i, err = add(r, i, validatorCompExp)
				if err != nil {
					return nil, err
				}
				st = vldt_end
			}
		case '/':
			switch st {
			case rule_start, vldt_end:
				st = vldt_start
				i, err = add(r, i, validatorRegex)
				if err != nil {
					return nil, err
				}
				st = vldt_end
			}
		case ' ', '\t':
			continue
		default:
			switch {
			case c >= '0' && c <= '9':
				fallthrough
			case c >= 'A' && c <= 'Z':
				fallthrough
			case c >= 'a' && c <= 'z':
				fallthrough
			case c == '_' || c == '-':
				switch st {
				case rule_start, vldt_end:
					st = vldt_start
					i, err = add(r, i, validatorNamed)
					if err != nil {
						return nil, err
					}
					st = vldt_end
				}

			}
		}
	}
	return vlds, nil
}

func parseInt(val string) (int64, error) {
	num, err := strconv.ParseInt(val, 0, 64)
	if err != nil {
		d, err := time.ParseDuration(val)
		if err != nil {
			return 0, err
		}
		num = int64(d)
	}
	return num, nil
}

func validatorRange(r string, pos int) (Validator, int, error) {
	return parseRange(r, pos)
}

func parseRange(r string, pos int) (*vrange, int, error) {
	i := pos
	v := &vrange{}
	vstart := 0
	vend := 0

	const (
		range_start = iota
		min_start
		min_val
		min_end
		max_start
		max_val
		max_end
		range_end
	)
	st := range_start

LOOP:
	for ; i < len(r); i++ {
		c := r[i]
		switch c {
		case '(', '[':
			if st == range_start {
				st = min_start
				vstart = i + 1
			} else {
				return nil, i, fmt.Errorf("range parse error at position %d of rule %s", i, r)
			}
			if c == '[' {
				v.left = true
			}
		case ',':
			switch st {
			case min_val:
				st = min_end
				vend = i
				fallthrough
			case min_end:
				val := r[vstart:vend]
				if val == "" {
					v.min = math.MinInt64
				} else {
					num, err := parseInt(val)
					if err != nil {
						return nil, i, err
					}
					v.min = num
				}

				st = max_start
				vstart = i + 1
			default:
				return nil, i, fmt.Errorf("range parse error at position %d of rule %s", i, r)
			}
		case ')', ']':
			switch st {
			case max_val:
				st = max_end
				vend = i
				fallthrough
			case max_end:
				val := r[vstart:vend]
				if val == "" {
					v.max = math.MaxInt64
				} else {
					num, err := parseInt(val)
					if err != nil {
						return nil, i, err
					}
					v.max = num
				}

				if c == ']' {
					v.right = true
				}

				st = range_end
				break LOOP
			default:
				return nil, i, fmt.Errorf("range parse error at position %d of rule %s", i, r)
			}
		case ' ', '\t':
			switch st {
			case min_start, max_start:
				vstart = i + 1
			case min_val:
				vend = i
				st = min_end
			case max_val:
				vend = i
				st = max_end
			}
		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.', //positive, negative, or float digital
			'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', 'X', 'x', //hex
			'h', 'm', 's', 'n', 'u', 'µ': //time.Duration
			switch st {
			case min_start:
				st = min_val
			case max_start:
				st = max_val
			}
		default:
			return nil, i, fmt.Errorf("unknown char at position %d of rule %s", i, r)
		}
	}

	if st != range_end {
		return nil, i, fmt.Errorf("rule is incompleted: %s", r)
	}

	return v, i, nil
}

func validatorCompExp(r string, pos int) (Validator, int, error) {
	return parseCompExp(r, pos)
}

//parse comparison expression
func parseCompExp(r string, pos int) (*vrange, int, error) {
	i := pos
	v := &vrange{}

	const (
		ce_start = iota
		val_start
		val_in
		val_end
		ce_end
	)
	st := ce_start
	vstart := 0
	vend := 0

	lt := true

LOOP:
	for ; i < len(r); i++ {
		c := r[i]
		switch c {
		case '<', '>':
			if st != ce_start {
				return nil, i, fmt.Errorf("rule parse error at position %d of rule %s", i, r)
			}
			if c == '<' {
				lt = true
			} else {
				lt = false
			}

			vstart = i + 1
			st = val_start
		case '=':
			if st != val_start {
				return nil, i, fmt.Errorf("rule parse error at position %d of rule %s, '=' should comes after '>' or '<'", i, r)
			}
			if lt {
				v.left = true
			} else {
				v.right = true
			}
		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.', //positive, negative, or float digital
			'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', 'X', 'x', //hex
			'h', 'm', 's', 'n', 'u', 'µ': //time.Duration
			if st == val_start {
				st = val_in
			}
		case ' ', '\t':
			switch st {
			case val_start:
				vstart = i + 1
			case val_in:
				st = val_end
				vend = i
				val := r[vstart:vend]
				num, err := parseInt(val)
				if err != nil {
					return nil, i, err
				}
				if lt {
					v.max = num
				} else {
					v.min = num
				}
				st = ce_end
				break LOOP
			default:
			}
		default:
			return nil, i, fmt.Errorf("unknown char at position %d of rule %s", i, r)
		}
	}
	if i == len(r) && st == val_in {
		st = val_end
		vend = i
		val := r[vstart:vend]
		num, err := parseInt(val)
		if err != nil {
			return nil, i, err
		}
		if lt {
			v.max = num
		} else {
			v.min = num
		}
		st = ce_end

	}
	if st != ce_end {
		return nil, i, fmt.Errorf("rule is incompleted %s", r)
	}

	if lt {
		v.min = math.MinInt64
	} else {
		v.max = math.MaxInt64
	}
	return v, i, nil
}

func validatorRegex(r string, pos int) (Validator, int, error) {
	return parseRegex(r, pos)
}
func parseRegex(r string, pos int) (*regex, int, error) {
	const (
		re_start = iota
		exp_start
		exp_val
		exp_end
		re_end
	)

	v := &regex{}

	vstart := 0
	vend := 0
	i := pos
	st := re_start
LOOP:
	for ; i < len(r); i++ {
		c := r[i]
		switch c {
		case '/':
			switch st {
			case re_start:
				vstart = i + 1
				st = exp_start
			case exp_start, exp_val:
				vend = i
				st = exp_end
				val := r[vstart:vend]
				v.exp = val
				st = re_end
				break LOOP
			}
		case ' ', '\t':
			if st == re_start {
				continue
			}
			fallthrough
		default:
			switch st {
			case re_start:
				return nil, i, fmt.Errorf("regex parse error at position %d, rule %q", i, r)
			case exp_start:
				st = exp_val
			}
		}
	}
	if st != re_end {
		return nil, i, fmt.Errorf("rule is incompleted: %s", r)
	}
	return v, i, nil
}

func validatorNamed(r string, pos int) (Validator, int, error) {
	return parseNamed(r, pos)
}
func parseNamed(r string, pos int) (*named, int, error) {
	const (
		name_start = iota
		name_val
		name_end
	)
	v := &named{}
	i := pos
	vstart := pos
	vend := pos
	st := name_start
LOOP:
	for ; i < len(r); i++ {
		c := r[i]
		switch c {
		case ' ', '\t':
			if st == name_val {
				vend = i
				st = name_end
				break LOOP
			}
			if st == name_start {
				vstart = i + 1
			}
		default:
			if st == name_start {
				st = name_val
			}
		}
	}
	if i == len(r) {
		st = name_end
		vend = i
	}

	val := r[vstart:vend]
	v.name = val
	return v, i, nil
}
<commit_msg>Fix bug: consume the "="<commit_after>package rule

import (
	"fmt"
	"math"
	"strconv"
	"time"
)

type Rule string

const (
	rule_start = iota
	vldt_start //validator start
	vldt_end   //validator end
	rule_end
)

func (r Rule) Parse() ([]Validator, error) {
	var vlds []Validator

	add := func(r Rule, pos int, f func(string, int) (Validator, int, error)) (int, error) {
		v, pos, err := f(string(r), pos)
		if err != nil {
			return pos, err
		}
		vlds = append(vlds, v)
		return pos, err
	}

	var err error
	st := rule_start
	for i := 0; i < len(r); i++ {
		c := r[i]
		switch c {
		case '(', '[':
			switch st {
			case rule_start, vldt_end:
				st = vldt_start
				i, err = add(r, i, validatorRange)
				if err != nil {
					return nil, err
				}
				st = vldt_end
			default:
				return nil, fmt.Errorf("parse error at position %d of rule %s", i, r)
			}
		case '<', '>':
			switch st {
			case rule_start, vldt_end:
				st = vldt_start
				i, err = add(r, i, validatorCompExp)
				if err != nil {
					return nil, err
				}
				st = vldt_end
			}
		case '/':
			switch st {
			case rule_start, vldt_end:
				st = vldt_start
				i, err = add(r, i, validatorRegex)
				if err != nil {
					return nil, err
				}
				st = vldt_end
			}
		case ' ', '\t':
			continue
		default:
			switch {
			case c >= '0' && c <= '9':
				fallthrough
			case c >= 'A' && c <= 'Z':
				fallthrough
			case c >= 'a' && c <= 'z':
				fallthrough
			case c == '_' || c == '-':
				switch st {
				case rule_start, vldt_end:
					st = vldt_start
					i, err = add(r, i, validatorNamed)
					if err != nil {
						return nil, err
					}
					st = vldt_end
				}

			}
		}
	}
	return vlds, nil
}

func parseInt(val string) (int64, error) {
	num, err := strconv.ParseInt(val, 0, 64)
	if err != nil {
		d, err := time.ParseDuration(val)
		if err != nil {
			return 0, err
		}
		num = int64(d)
	}
	return num, nil
}

func validatorRange(r string, pos int) (Validator, int, error) {
	return parseRange(r, pos)
}

func parseRange(r string, pos int) (*vrange, int, error) {
	i := pos
	v := &vrange{}
	vstart := 0
	vend := 0

	const (
		range_start = iota
		min_start
		min_val
		min_end
		max_start
		max_val
		max_end
		range_end
	)
	st := range_start

LOOP:
	for ; i < len(r); i++ {
		c := r[i]
		switch c {
		case '(', '[':
			if st == range_start {
				st = min_start
				vstart = i + 1
			} else {
				return nil, i, fmt.Errorf("range parse error at position %d of rule %s", i, r)
			}
			if c == '[' {
				v.left = true
			}
		case ',':
			switch st {
			case min_val:
				st = min_end
				vend = i
				fallthrough
			case min_end:
				val := r[vstart:vend]
				if val == "" {
					v.min = math.MinInt64
				} else {
					num, err := parseInt(val)
					if err != nil {
						return nil, i, err
					}
					v.min = num
				}

				st = max_start
				vstart = i + 1
			default:
				return nil, i, fmt.Errorf("range parse error at position %d of rule %s", i, r)
			}
		case ')', ']':
			switch st {
			case max_val:
				st = max_end
				vend = i
				fallthrough
			case max_end:
				val := r[vstart:vend]
				if val == "" {
					v.max = math.MaxInt64
				} else {
					num, err := parseInt(val)
					if err != nil {
						return nil, i, err
					}
					v.max = num
				}

				if c == ']' {
					v.right = true
				}

				st = range_end
				break LOOP
			default:
				return nil, i, fmt.Errorf("range parse error at position %d of rule %s", i, r)
			}
		case ' ', '\t':
			switch st {
			case min_start, max_start:
				vstart = i + 1
			case min_val:
				vend = i
				st = min_end
			case max_val:
				vend = i
				st = max_end
			}
		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.', //positive, negative, or float digital
			'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', 'X', 'x', //hex
			'h', 'm', 's', 'n', 'u', 'µ': //time.Duration
			switch st {
			case min_start:
				st = min_val
			case max_start:
				st = max_val
			}
		default:
			return nil, i, fmt.Errorf("unknown char at position %d of rule %s", i, r)
		}
	}

	if st != range_end {
		return nil, i, fmt.Errorf("rule is incompleted: %s", r)
	}

	return v, i, nil
}

func validatorCompExp(r string, pos int) (Validator, int, error) {
	return parseCompExp(r, pos)
}

//parse comparison expression
func parseCompExp(r string, pos int) (*vrange, int, error) {
	i := pos
	v := &vrange{}

	const (
		ce_start = iota
		val_start
		val_in
		val_end
		ce_end
	)
	st := ce_start
	vstart := 0
	vend := 0

	lt := true

LOOP:
	for ; i < len(r); i++ {
		c := r[i]
		switch c {
		case '<', '>':
			if st != ce_start {
				return nil, i, fmt.Errorf("rule parse error at position %d of rule %s", i, r)
			}
			if c == '<' {
				lt = true
			} else {
				lt = false
			}

			vstart = i + 1
			st = val_start
		case '=':
			if st != val_start {
				return nil, i, fmt.Errorf("rule parse error at position %d of rule %s, '=' should comes after '>' or '<'", i, r)
			}
			if lt {
				v.left = true
			} else {
				v.right = true
			}
			vstart += 1
		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.', //positive, negative, or float digital
			'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', 'X', 'x', //hex
			'h', 'm', 's', 'n', 'u', 'µ': //time.Duration
			if st == val_start {
				st = val_in
			}
		case ' ', '\t':
			switch st {
			case val_start:
				vstart = i + 1
			case val_in:
				st = val_end
				vend = i
				val := r[vstart:vend]
				num, err := parseInt(val)
				if err != nil {
					return nil, i, err
				}
				if lt {
					v.max = num
				} else {
					v.min = num
				}
				st = ce_end
				break LOOP
			default:
			}
		default:
			return nil, i, fmt.Errorf("unknown char at position %d of rule %s", i, r)
		}
	}
	if i == len(r) && st == val_in {
		st = val_end
		vend = i
		val := r[vstart:vend]
		num, err := parseInt(val)
		if err != nil {
			return nil, i, err
		}
		if lt {
			v.max = num
		} else {
			v.min = num
		}
		st = ce_end

	}
	if st != ce_end {
		return nil, i, fmt.Errorf("rule is incompleted %s", r)
	}

	if lt {
		v.min = math.MinInt64
	} else {
		v.max = math.MaxInt64
	}
	return v, i, nil
}

func validatorRegex(r string, pos int) (Validator, int, error) {
	return parseRegex(r, pos)
}
func parseRegex(r string, pos int) (*regex, int, error) {
	const (
		re_start = iota
		exp_start
		exp_val
		exp_end
		re_end
	)

	v := &regex{}

	vstart := 0
	vend := 0
	i := pos
	st := re_start
LOOP:
	for ; i < len(r); i++ {
		c := r[i]
		switch c {
		case '/':
			switch st {
			case re_start:
				vstart = i + 1
				st = exp_start
			case exp_start, exp_val:
				vend = i
				st = exp_end
				val := r[vstart:vend]
				v.exp = val
				st = re_end
				break LOOP
			}
		case ' ', '\t':
			if st == re_start {
				continue
			}
			fallthrough
		default:
			switch st {
			case re_start:
				return nil, i, fmt.Errorf("regex parse error at position %d, rule %q", i, r)
			case exp_start:
				st = exp_val
			}
		}
	}
	if st != re_end {
		return nil, i, fmt.Errorf("rule is incompleted: %s", r)
	}
	return v, i, nil
}

func validatorNamed(r string, pos int) (Validator, int, error) {
	return parseNamed(r, pos)
}
func parseNamed(r string, pos int) (*named, int, error) {
	const (
		name_start = iota
		name_val
		name_end
	)
	v := &named{}
	i := pos
	vstart := pos
	vend := pos
	st := name_start
LOOP:
	for ; i < len(r); i++ {
		c := r[i]
		switch c {
		case ' ', '\t':
			if st == name_val {
				vend = i
				st = name_end
				break LOOP
			}
			if st == name_start {
				vstart = i + 1
			}
		default:
			if st == name_start {
				st = name_val
			}
		}
	}
	if i == len(r) {
		st = name_end
		vend = i
	}

	val := r[vstart:vend]
	v.name = val
	return v, i, nil
}
<|endoftext|>


<commit_before>package admin

import (
	"bytes"
	"encoding/json"
	"errors"
	"html/template"
	"os"
	"path"
	"path/filepath"

	"github.com/qor/qor"
	"github.com/qor/qor/utils"
)

type Context struct {
	*qor.Context
	*Searcher
	Flashes  []Flash
	Resource *Resource
	Admin    *Admin
	Content  template.HTML
	Action   string
	Result   interface{}
}

func (context *Context) clone() *Context {
	return &Context{
		Context:  context.Context,
		Searcher: context.Searcher,
		Flashes:  context.Flashes,
		Resource: context.Resource,
		Admin:    context.Admin,
		Result:   context.Result,
		Content:  context.Content,
	}
}

// Resource
func (context *Context) resourcePath() string {
	if context.Resource == nil {
		return ""
	}
	return context.Resource.ToParam()
}

func (context *Context) setResource(res *Resource) *Context {
	context.Resource = res
	context.Searcher = &Searcher{Context: context}
	return context
}

func (context *Context) GetResource(name string) *Resource {
	return context.Admin.GetResource(name)
}

// Template
func (context *Context) getViewPaths() (paths []string) {
	var dirs = []string{context.resourcePath(), path.Join("themes", "default"), "."}
	var themes []string

	if context.Request != nil {
		if theme := context.Request.URL.Query().Get("theme"); theme != "" {
			themePath := path.Join("themes", theme)
			themes = append(themes, []string{path.Join(themePath, context.resourcePath()), themePath}...)
		}
	}

	if context.Resource != nil {
		for _, theme := range context.Resource.Config.Themes {
			themePath := path.Join("themes", theme)
			themes = append(themes, []string{path.Join(themePath, context.resourcePath()), themePath}...)
		}
	}

	for _, p := range append(themes, dirs...) {
		for _, d := range viewPaths {
			if context.Action != "" {
				if isExistingDir(path.Join(d, p, context.Action)) {
					paths = append(paths, path.Join(d, p, context.Action))
				}
			}

			if isExistingDir(path.Join(d, p)) {
				paths = append(paths, path.Join(d, p))
			}
		}
	}
	return paths
}

func (context *Context) findFile(layout string) (string, error) {
	for _, p := range context.getViewPaths() {
		if _, err := os.Stat(path.Join(p, layout)); !os.IsNotExist(err) {
			return path.Join(p, layout), nil
		}
	}
	return "", errors.New("file not found")
}

func (context *Context) FindTemplate(layouts ...string) (string, error) {
	for _, layout := range layouts {
		for _, p := range context.getViewPaths() {
			if _, err := os.Stat(filepath.Join(p, layout)); !os.IsNotExist(err) {
				return filepath.Join(p, layout), nil
			}
		}
	}
	return "", errors.New("template not found")
}

func (context *Context) Render(name string, results ...interface{}) template.HTML {
	if file, err := context.FindTemplate(name + ".tmpl"); err == nil {
		var clone = context.clone()
		var result = bytes.NewBufferString("")

		if len(results) > 0 {
			clone.Result = results[0]
		}

		if tmpl, err := template.New(filepath.Base(file)).Funcs(clone.FuncMap()).ParseFiles(file); err == nil {
			if err := tmpl.Execute(result, clone); err != nil {
				utils.ExitWithMsg(err)
			}
		} else {
			utils.ExitWithMsg(err)
		}
		return template.HTML(result.String())
	}

	return ""
}

func (context *Context) Execute(name string, result interface{}) {
	var tmpl *template.Template
	var cacheKey string

	if context.Action == "" {
		context.Action = name
	}

	if context.Resource != nil {
		cacheKey = path.Join(context.resourcePath(), name)
	} else {
		cacheKey = name
	}

	if t, ok := templates[cacheKey]; !ok || true {
		if file, err := context.FindTemplate("layout.tmpl"); err == nil {
			if tmpl, err = template.New(filepath.Base(file)).Funcs(context.FuncMap()).ParseFiles(file); err == nil {
				for _, name := range []string{"header", "footer"} {
					if tmpl.Lookup(name) == nil {
						if file, err := context.FindTemplate(name + ".tmpl"); err == nil {
							tmpl.ParseFiles(file)
						}
					} else {
						utils.ExitWithMsg(err)
					}
				}
			} else {
				utils.ExitWithMsg(err)
			}
		}
	} else {
		tmpl = t
	}

	context.Content = context.Render(name, result)
	if err := tmpl.Execute(context.Writer, context); err != nil {
		utils.ExitWithMsg(err)
	}
}

func (context *Context) JSON(name string, result interface{}) {
	js, _ := json.Marshal(context.Resource.convertObjectToJSONMap(context, result, name))
	context.Writer.Write(js)
}
<commit_msg>json marshal indent<commit_after>package admin

import (
	"bytes"
	"encoding/json"
	"errors"
	"html/template"
	"os"
	"path"
	"path/filepath"

	"github.com/qor/qor"
	"github.com/qor/qor/utils"
)

type Context struct {
	*qor.Context
	*Searcher
	Flashes  []Flash
	Resource *Resource
	Admin    *Admin
	Content  template.HTML
	Action   string
	Result   interface{}
}

func (context *Context) clone() *Context {
	return &Context{
		Context:  context.Context,
		Searcher: context.Searcher,
		Flashes:  context.Flashes,
		Resource: context.Resource,
		Admin:    context.Admin,
		Result:   context.Result,
		Content:  context.Content,
	}
}

// Resource
func (context *Context) resourcePath() string {
	if context.Resource == nil {
		return ""
	}
	return context.Resource.ToParam()
}

func (context *Context) setResource(res *Resource) *Context {
	context.Resource = res
	context.Searcher = &Searcher{Context: context}
	return context
}

func (context *Context) GetResource(name string) *Resource {
	return context.Admin.GetResource(name)
}

// Template
func (context *Context) getViewPaths() (paths []string) {
	var dirs = []string{context.resourcePath(), path.Join("themes", "default"), "."}
	var themes []string

	if context.Request != nil {
		if theme := context.Request.URL.Query().Get("theme"); theme != "" {
			themePath := path.Join("themes", theme)
			themes = append(themes, []string{path.Join(themePath, context.resourcePath()), themePath}...)
		}
	}

	if context.Resource != nil {
		for _, theme := range context.Resource.Config.Themes {
			themePath := path.Join("themes", theme)
			themes = append(themes, []string{path.Join(themePath, context.resourcePath()), themePath}...)
		}
	}

	for _, p := range append(themes, dirs...) {
		for _, d := range viewPaths {
			if context.Action != "" {
				if isExistingDir(path.Join(d, p, context.Action)) {
					paths = append(paths, path.Join(d, p, context.Action))
				}
			}

			if isExistingDir(path.Join(d, p)) {
				paths = append(paths, path.Join(d, p))
			}
		}
	}
	return paths
}

func (context *Context) findFile(layout string) (string, error) {
	for _, p := range context.getViewPaths() {
		if _, err := os.Stat(path.Join(p, layout)); !os.IsNotExist(err) {
			return path.Join(p, layout), nil
		}
	}
	return "", errors.New("file not found")
}

func (context *Context) FindTemplate(layouts ...string) (string, error) {
	for _, layout := range layouts {
		for _, p := range context.getViewPaths() {
			if _, err := os.Stat(filepath.Join(p, layout)); !os.IsNotExist(err) {
				return filepath.Join(p, layout), nil
			}
		}
	}
	return "", errors.New("template not found")
}

func (context *Context) Render(name string, results ...interface{}) template.HTML {
	if file, err := context.FindTemplate(name + ".tmpl"); err == nil {
		var clone = context.clone()
		var result = bytes.NewBufferString("")

		if len(results) > 0 {
			clone.Result = results[0]
		}

		if tmpl, err := template.New(filepath.Base(file)).Funcs(clone.FuncMap()).ParseFiles(file); err == nil {
			if err := tmpl.Execute(result, clone); err != nil {
				utils.ExitWithMsg(err)
			}
		} else {
			utils.ExitWithMsg(err)
		}
		return template.HTML(result.String())
	}

	return ""
}

func (context *Context) Execute(name string, result interface{}) {
	var tmpl *template.Template
	var cacheKey string

	if context.Action == "" {
		context.Action = name
	}

	if context.Resource != nil {
		cacheKey = path.Join(context.resourcePath(), name)
	} else {
		cacheKey = name
	}

	if t, ok := templates[cacheKey]; !ok || true {
		if file, err := context.FindTemplate("layout.tmpl"); err == nil {
			if tmpl, err = template.New(filepath.Base(file)).Funcs(context.FuncMap()).ParseFiles(file); err == nil {
				for _, name := range []string{"header", "footer"} {
					if tmpl.Lookup(name) == nil {
						if file, err := context.FindTemplate(name + ".tmpl"); err == nil {
							tmpl.ParseFiles(file)
						}
					} else {
						utils.ExitWithMsg(err)
					}
				}
			} else {
				utils.ExitWithMsg(err)
			}
		}
	} else {
		tmpl = t
	}

	context.Content = context.Render(name, result)
	if err := tmpl.Execute(context.Writer, context); err != nil {
		utils.ExitWithMsg(err)
	}
}

func (context *Context) JSON(name string, result interface{}) {
	js, _ := json.MarshalIndent(context.Resource.convertObjectToJSONMap(context, result, name), "", "\t")
	context.Writer.Write(js)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. 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 sources

import (
	"fmt"
	"net/url"
	"time"

	. "k8s.io/heapster/core"
	"k8s.io/heapster/sources/api"

	"github.com/golang/glog"
	kube_api "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/cache"
	kube_client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/fields"
)

const (
	infraContainerName = "POD"
	// TODO: following constants are copied from k8s, change to use them directly
	kubernetesPodNameLabel      = "io.kubernetes.pod.name"
	kubernetesPodNamespaceLabel = "io.kubernetes.pod.namespace"
	kubernetesPodUID            = "io.kubernetes.pod.uid"
	kubernetesContainerLabel    = "io.kubernetes.container.name"
)

type cpuVal struct {
	Val       int64
	Timestamp time.Time
}

// Kubelet-provided metrics for pod and system container.
type kubeletMetricsSource struct {
	host          Host
	kubeletClient *KubeletClient
	hostname      string
	hostId        string
	cpuLastVal    map[string]cpuVal
}

func (this *kubeletMetricsSource) String() string {
	return fmt.Sprintf("kubelet:%s:%d", this.host.IP, this.host.Port)
}

func (this *kubeletMetricsSource) decodeMetrics(c *api.Container) (string, *MetricSet) {
	var metricSetKey string
	cMetrics := &MetricSet{
		MetricValues: map[string]MetricValue{},
		Labels:       map[string]string{},
	}

	if isNode(c) {
		metricSetKey = NodeKey(this.hostname)
		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypeNode
	} else if isSysContainer(c) {
		cName := getSysContainerName(c)
		metricSetKey = NodeContainerKey(this.hostname, cName)
		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypeSystemContainer
		cMetrics.Labels[LabelContainerName.Key] = cName
	} else {
		cName := c.Spec.Labels[kubernetesContainerLabel]
		if cName == infraContainerName {
			return "", nil
		}
		ns := c.Spec.Labels[kubernetesPodNamespaceLabel]
		podName := c.Spec.Labels[kubernetesPodNameLabel]
		metricSetKey = PodContainerKey(ns, podName, cName)
		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypePodContainer
		cMetrics.Labels[LabelContainerName.Key] = cName
		cMetrics.Labels[LabelPodId.Key] = c.Spec.Labels[kubernetesPodUID]
		cMetrics.Labels[LabelPodName.Key] = podName
		cMetrics.Labels[LabelNamespaceName.Key] = ns
		// Needed for backward compatibility
		cMetrics.Labels[LabelPodNamespace.Key] = ns
		cMetrics.Labels[LabelContainerBaseImage.Key] = c.Spec.Image
	}

	for _, metric := range StandardMetrics {
		if metric.HasValue(&c.Spec) {
			cMetrics.MetricValues[metric.Name] = metric.GetValue(&c.Spec, c.Stats[0])
		}
	}

	// This is temporary workaround to support cpu/usege_rate metric.
	if currentVal, ok := cMetrics.MetricValues["cpu/usage"]; ok {
		if lastVal, ok := this.cpuLastVal[metricSetKey]; ok {
			// cpu/usage values are in nanoseconds; we want to have it in millicores (that's why constant 1000 is here).
			rateVal := 1000 * (currentVal.IntValue - lastVal.Val) / (c.Stats[0].Timestamp.UnixNano() - lastVal.Timestamp.UnixNano())
			cMetrics.MetricValues["cpu/usage_rate"] = MetricValue{
				ValueType:  ValueInt64,
				MetricType: MetricGauge,
				IntValue:   rateVal,
			}
		}
		this.cpuLastVal[metricSetKey] = cpuVal{
			Val:       currentVal.IntValue,
			Timestamp: c.Stats[0].Timestamp,
		}
	}

	// common labels
	cMetrics.Labels[LabelHostname.Key] = this.hostname
	cMetrics.Labels[LabelHostID.Key] = this.hostId

	// TODO: add labels: LabelPodNamespaceUID, LabelLabels, LabelResourceID

	return metricSetKey, cMetrics
}

func (this *kubeletMetricsSource) ScrapeMetrics(start, end time.Time) *DataBatch {
	containers, err := this.kubeletClient.GetAllRawContainers(this.host, start, end)
	if err != nil {
		glog.Errorf("error while getting containers from Kubelet: %v", err)
	}
	glog.Infof("successfully obtained stats for %v containers", len(containers))

	result := &DataBatch{
		Timestamp:  end,
		MetricSets: map[string]*MetricSet{},
	}
	for _, c := range containers {
		name, metrics := this.decodeMetrics(&c)
		if name == "" {
			continue
		}
		result.MetricSets[name] = metrics
	}
	return result
}

type kubeletProvider struct {
	nodeLister    *cache.StoreToNodeLister
	reflector     *cache.Reflector
	kubeletClient *KubeletClient
	cpuLastVals   map[string]map[string]cpuVal
}

func (this *kubeletProvider) GetMetricsSources() []MetricsSource {
	sources := []MetricsSource{}
	nodes, err := this.nodeLister.List()
	if err != nil {
		glog.Errorf("error while listing nodes: %v", err)
		return sources
	}

	nodeNames := make(map[string]bool)
	for _, node := range nodes.Items {
		nodeNames[node.Name] = true
		hostname, ip, err := getNodeHostnameAndIP(&node)
		if err != nil {
			glog.Errorf("%v", err)
			continue
		}
		if _, ok := this.cpuLastVals[node.Name]; !ok {
			this.cpuLastVals[node.Name] = make(map[string]cpuVal)
		}
		sources = append(sources, &kubeletMetricsSource{
			host:          Host{IP: ip, Port: this.kubeletClient.GetPort()},
			kubeletClient: this.kubeletClient,
			hostname:      hostname,
			hostId:        node.Spec.ExternalID,
			cpuLastVal:    this.cpuLastVals[node.Name],
		})
	}

	for key := range this.cpuLastVals {
		if _, ok := nodeNames[key]; !ok {
			delete(this.cpuLastVals, key)
		}
	}

	return sources
}

func getNodeHostnameAndIP(node *kube_api.Node) (string, string, error) {
	for _, c := range node.Status.Conditions {
		if c.Type == kube_api.NodeReady && c.Status != kube_api.ConditionTrue {
			return "", "", fmt.Errorf("Node %v is not ready", node.Name)
		}
	}
	hostname, ip := node.Name, ""
	for _, addr := range node.Status.Addresses {
		if addr.Type == kube_api.NodeHostName && addr.Address != "" {
			hostname = addr.Address
		}
		if addr.Type == kube_api.NodeInternalIP && addr.Address != "" {
			ip = addr.Address
		}
	}
	if ip != "" {
		return hostname, ip, nil
	}
	return "", "", fmt.Errorf("Node %v has no valid hostname and/or IP address: %v %v", node.Name, hostname, ip)
}

func NewKubeletProvider(uri *url.URL) (MetricsSourceProvider, error) {
	// create clients
	kubeConfig, kubeletConfig, err := getKubeConfigs(uri)
	if err != nil {
		return nil, err
	}
	kubeClient := kube_client.NewOrDie(kubeConfig)
	kubeletClient, err := NewKubeletClient(kubeletConfig)
	if err != nil {
		return nil, err
	}
	// watch nodes
	lw := cache.NewListWatchFromClient(kubeClient, "nodes", kube_api.NamespaceAll, fields.Everything())
	nodeLister := &cache.StoreToNodeLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}
	reflector := cache.NewReflector(lw, &kube_api.Node{}, nodeLister.Store, 0)
	reflector.Run()

	return &kubeletProvider{
		nodeLister:    nodeLister,
		reflector:     reflector,
		kubeletClient: kubeletClient,
		cpuLastVals:   make(map[string]map[string]cpuVal),
	}, nil
}
<commit_msg>Fixed memory leak in kubelet source<commit_after>// Copyright 2015 Google Inc. 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 sources

import (
	"fmt"
	"net/url"
	"time"

	. "k8s.io/heapster/core"
	"k8s.io/heapster/sources/api"

	"github.com/golang/glog"
	kube_api "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/cache"
	kube_client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/fields"
)

const (
	infraContainerName = "POD"
	// TODO: following constants are copied from k8s, change to use them directly
	kubernetesPodNameLabel      = "io.kubernetes.pod.name"
	kubernetesPodNamespaceLabel = "io.kubernetes.pod.namespace"
	kubernetesPodUID            = "io.kubernetes.pod.uid"
	kubernetesContainerLabel    = "io.kubernetes.container.name"
)

type cpuVal struct {
	Val       int64
	Timestamp time.Time
}

// Kubelet-provided metrics for pod and system container.
type kubeletMetricsSource struct {
	host          Host
	kubeletClient *KubeletClient
	hostname      string
	hostId        string
	cpuLastVal    map[string]cpuVal
}

func (this *kubeletMetricsSource) String() string {
	return fmt.Sprintf("kubelet:%s:%d", this.host.IP, this.host.Port)
}

func (this *kubeletMetricsSource) decodeMetrics(c *api.Container) (string, *MetricSet) {
	var metricSetKey string
	cMetrics := &MetricSet{
		MetricValues: map[string]MetricValue{},
		Labels:       map[string]string{},
	}

	if isNode(c) {
		metricSetKey = NodeKey(this.hostname)
		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypeNode
	} else if isSysContainer(c) {
		cName := getSysContainerName(c)
		metricSetKey = NodeContainerKey(this.hostname, cName)
		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypeSystemContainer
		cMetrics.Labels[LabelContainerName.Key] = cName
	} else {
		cName := c.Spec.Labels[kubernetesContainerLabel]
		if cName == infraContainerName {
			return "", nil
		}
		ns := c.Spec.Labels[kubernetesPodNamespaceLabel]
		podName := c.Spec.Labels[kubernetesPodNameLabel]
		metricSetKey = PodContainerKey(ns, podName, cName)
		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypePodContainer
		cMetrics.Labels[LabelContainerName.Key] = cName
		cMetrics.Labels[LabelPodId.Key] = c.Spec.Labels[kubernetesPodUID]
		cMetrics.Labels[LabelPodName.Key] = podName
		cMetrics.Labels[LabelNamespaceName.Key] = ns
		// Needed for backward compatibility
		cMetrics.Labels[LabelPodNamespace.Key] = ns
		cMetrics.Labels[LabelContainerBaseImage.Key] = c.Spec.Image
	}

	for _, metric := range StandardMetrics {
		if metric.HasValue(&c.Spec) {
			cMetrics.MetricValues[metric.Name] = metric.GetValue(&c.Spec, c.Stats[0])
		}
	}

	// This is temporary workaround to support cpu/usege_rate metric.
	if currentVal, ok := cMetrics.MetricValues["cpu/usage"]; ok {
		if lastVal, ok := this.cpuLastVal[metricSetKey]; ok {
			// cpu/usage values are in nanoseconds; we want to have it in millicores (that's why constant 1000 is here).
			rateVal := 1000 * (currentVal.IntValue - lastVal.Val) / (c.Stats[0].Timestamp.UnixNano() - lastVal.Timestamp.UnixNano())
			cMetrics.MetricValues["cpu/usage_rate"] = MetricValue{
				ValueType:  ValueInt64,
				MetricType: MetricGauge,
				IntValue:   rateVal,
			}
		}
		this.cpuLastVal[metricSetKey] = cpuVal{
			Val:       currentVal.IntValue,
			Timestamp: c.Stats[0].Timestamp,
		}
	}

	// common labels
	cMetrics.Labels[LabelHostname.Key] = this.hostname
	cMetrics.Labels[LabelHostID.Key] = this.hostId

	// TODO: add labels: LabelPodNamespaceUID, LabelLabels, LabelResourceID

	return metricSetKey, cMetrics
}

func (this *kubeletMetricsSource) ScrapeMetrics(start, end time.Time) *DataBatch {
	containers, err := this.kubeletClient.GetAllRawContainers(this.host, start, end)
	if err != nil {
		glog.Errorf("error while getting containers from Kubelet: %v", err)
	}
	glog.Infof("successfully obtained stats for %v containers", len(containers))

	result := &DataBatch{
		Timestamp:  end,
		MetricSets: map[string]*MetricSet{},
	}
	keys := make(map[string]bool)
	for _, c := range containers {
		name, metrics := this.decodeMetrics(&c)
		if name == "" {
			continue
		}
		result.MetricSets[name] = metrics
		keys[name] = true
	}
	// No remember data for pods that have been removed.
	for key := range this.cpuLastVal {
		if _, ok := keys[key]; !ok {
			delete(this.cpuLastVal, key)
		}
	}
	return result
}

type kubeletProvider struct {
	nodeLister    *cache.StoreToNodeLister
	reflector     *cache.Reflector
	kubeletClient *KubeletClient
	cpuLastVals   map[string]map[string]cpuVal
}

func (this *kubeletProvider) GetMetricsSources() []MetricsSource {
	sources := []MetricsSource{}
	nodes, err := this.nodeLister.List()
	if err != nil {
		glog.Errorf("error while listing nodes: %v", err)
		return sources
	}

	nodeNames := make(map[string]bool)
	for _, node := range nodes.Items {
		nodeNames[node.Name] = true
		hostname, ip, err := getNodeHostnameAndIP(&node)
		if err != nil {
			glog.Errorf("%v", err)
			continue
		}
		if _, ok := this.cpuLastVals[node.Name]; !ok {
			this.cpuLastVals[node.Name] = make(map[string]cpuVal)
		}
		sources = append(sources, &kubeletMetricsSource{
			host:          Host{IP: ip, Port: this.kubeletClient.GetPort()},
			kubeletClient: this.kubeletClient,
			hostname:      hostname,
			hostId:        node.Spec.ExternalID,
			cpuLastVal:    this.cpuLastVals[node.Name],
		})
	}

	for key := range this.cpuLastVals {
		if _, ok := nodeNames[key]; !ok {
			delete(this.cpuLastVals, key)
		}
	}

	return sources
}

func getNodeHostnameAndIP(node *kube_api.Node) (string, string, error) {
	for _, c := range node.Status.Conditions {
		if c.Type == kube_api.NodeReady && c.Status != kube_api.ConditionTrue {
			return "", "", fmt.Errorf("Node %v is not ready", node.Name)
		}
	}
	hostname, ip := node.Name, ""
	for _, addr := range node.Status.Addresses {
		if addr.Type == kube_api.NodeHostName && addr.Address != "" {
			hostname = addr.Address
		}
		if addr.Type == kube_api.NodeInternalIP && addr.Address != "" {
			ip = addr.Address
		}
	}
	if ip != "" {
		return hostname, ip, nil
	}
	return "", "", fmt.Errorf("Node %v has no valid hostname and/or IP address: %v %v", node.Name, hostname, ip)
}

func NewKubeletProvider(uri *url.URL) (MetricsSourceProvider, error) {
	// create clients
	kubeConfig, kubeletConfig, err := getKubeConfigs(uri)
	if err != nil {
		return nil, err
	}
	kubeClient := kube_client.NewOrDie(kubeConfig)
	kubeletClient, err := NewKubeletClient(kubeletConfig)
	if err != nil {
		return nil, err
	}
	// watch nodes
	lw := cache.NewListWatchFromClient(kubeClient, "nodes", kube_api.NamespaceAll, fields.Everything())
	nodeLister := &cache.StoreToNodeLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}
	reflector := cache.NewReflector(lw, &kube_api.Node{}, nodeLister.Store, 0)
	reflector.Run()

	return &kubeletProvider{
		nodeLister:    nodeLister,
		reflector:     reflector,
		kubeletClient: kubeletClient,
		cpuLastVals:   make(map[string]map[string]cpuVal),
	}, nil
}
<|endoftext|>